summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/activatemodsdialog.h104
-rw-r--r--src/baincomplexinstallerdialog.cpp4
-rw-r--r--src/baincomplexinstallerdialog.h152
-rw-r--r--src/categories.cpp506
-rw-r--r--src/categoriesdialog.cpp446
-rw-r--r--src/categoriesdialog.h104
-rw-r--r--src/directoryrefresher.cpp172
-rw-r--r--src/directoryrefresher.h174
-rw-r--r--src/downloadmanager.cpp1
-rw-r--r--src/dummybsa.cpp450
-rw-r--r--src/editexecutablesdialog.cpp3
-rw-r--r--src/editexecutablesdialog.h134
-rw-r--r--src/executableslist.cpp4
-rw-r--r--src/executableslist.h6
-rw-r--r--src/fomodinstallerdialog.cpp3
-rw-r--r--src/fomodinstallerdialog.h12
-rw-r--r--src/gameinfoimpl.cpp66
-rw-r--r--src/gameinfoimpl.h40
-rw-r--r--src/installationmanager.cpp4
-rw-r--r--src/installationmanager.h28
-rw-r--r--src/installdialog.cpp575
-rw-r--r--src/installdialog.h216
-rw-r--r--src/loadmechanism.cpp536
-rw-r--r--src/main.cpp4
-rw-r--r--src/mainwindow.cpp5
-rw-r--r--src/mainwindow.h30
-rw-r--r--src/mainwindow.ui8
-rw-r--r--src/moapplication.cpp122
-rw-r--r--src/modinfo.cpp4
-rw-r--r--src/modinfo.h34
-rw-r--r--src/modinfodialog.cpp3
-rw-r--r--src/modinfodialog.h8
-rw-r--r--src/modlist.cpp3
-rw-r--r--src/motddialog.cpp12
-rw-r--r--src/nexusdialog.cpp3
-rw-r--r--src/nexusdialog.h2
-rw-r--r--src/nexusinterface.cpp3
-rw-r--r--src/nexusinterface.h616
-rw-r--r--src/nexusview.cpp4
-rw-r--r--src/nxmaccessmanager.cpp272
-rw-r--r--src/nxmurl.cpp30
-rw-r--r--src/overwriteinfodialog.cpp465
-rw-r--r--src/pluginlist.cpp1667
-rw-r--r--src/pluginlist.h4
-rw-r--r--src/profile.cpp1350
-rw-r--r--src/profilesdialog.cpp514
-rw-r--r--src/profilesdialog.h150
-rw-r--r--src/report.cpp64
-rw-r--r--src/savegamegamebryo.cpp653
-rw-r--r--src/savegameinfowidget.cpp86
-rw-r--r--src/savetextasdialog.cpp2
-rw-r--r--src/selfupdater.cpp876
-rw-r--r--src/selfupdater.h252
-rw-r--r--src/settings.cpp986
-rw-r--r--src/settings.h408
-rw-r--r--src/settingsdialog.cpp264
-rw-r--r--src/settingsdialog.h122
-rw-r--r--src/shared/appconfig.cpp41
-rw-r--r--src/shared/appconfig.h42
-rw-r--r--src/shared/directoryentry.cpp1601
-rw-r--r--src/shared/directoryentry.h42
-rw-r--r--src/shared/error_report.cpp41
-rw-r--r--src/shared/error_report.h46
-rw-r--r--src/shared/fallout3info.cpp43
-rw-r--r--src/shared/fallout3info.h42
-rw-r--r--src/shared/falloutnvinfo.cpp43
-rw-r--r--src/shared/falloutnvinfo.h42
-rw-r--r--src/shared/gameinfo.cpp41
-rw-r--r--src/shared/gameinfo.h42
-rw-r--r--src/shared/inject.cpp42
-rw-r--r--src/shared/inject.h20
-rw-r--r--src/shared/oblivioninfo.cpp43
-rw-r--r--src/shared/oblivioninfo.h42
-rw-r--r--src/shared/skyriminfo.cpp43
-rw-r--r--src/shared/skyriminfo.h42
-rw-r--r--src/shared/util.cpp41
-rw-r--r--src/shared/util.h42
-rw-r--r--src/shared/windows_error.cpp42
-rw-r--r--src/shared/windows_error.h42
-rw-r--r--src/singleinstance.cpp176
-rw-r--r--src/spawn.cpp184
-rw-r--r--src/syncoverwritedialog.cpp258
-rw-r--r--src/syncoverwritedialog.h72
-rw-r--r--src/transfersavesdialog.cpp628
-rw-r--r--src/transfersavesdialog.h128
85 files changed, 8434 insertions, 8243 deletions
diff --git a/src/activatemodsdialog.h b/src/activatemodsdialog.h
index acb01f4e..db0bf7fa 100644
--- a/src/activatemodsdialog.h
+++ b/src/activatemodsdialog.h
@@ -17,55 +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 ACTIVATEMODSDIALOG_H
-#define ACTIVATEMODSDIALOG_H
-
-#include "tutorabledialog.h"
-#include <map>
-#include <set>
-
-namespace Ui {
- class ActivateModsDialog;
-}
-
-/**
- * @brief Dialog that is used to batch activate/deactivate mods and plugins
- **/
-class ActivateModsDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
- /**
- * @brief constructor
- *
- * @param missingPlugins a map containing missing plugins that need to be activated
- * @param parent ... Defaults to 0.
- **/
- explicit ActivateModsDialog(const std::map<QString, std::vector<QString> > &missingPlugins, QWidget *parent = 0);
- ~ActivateModsDialog();
-
- /**
- * @brief get a list of mods that the user chose to activate
- *
- * @note This can of ocurse only be called after the dialog has been displayed
- *
- * @return set< QString > the mods to activate
- **/
- std::set<QString> getModsToActivate();
-
- /**
- * @brief get a list of plugins that should be activated
- *
- * @return set< QString > the plugins to activate. This contains only plugins that become available after enabling the mods retrieved with getModsToActivate
- **/
- std::set<QString> getESPsToActivate();
-
-private slots:
- void on_buttonBox_accepted();
-
-private:
- Ui::ActivateModsDialog *ui;
-};
-
-#endif // ACTIVATEMODSDIALOG_H
+#ifndef ACTIVATEMODSDIALOG_H
+#define ACTIVATEMODSDIALOG_H
+
+#include "tutorabledialog.h"
+#include <map>
+#include <set>
+
+namespace Ui {
+ class ActivateModsDialog;
+}
+
+/**
+ * @brief Dialog that is used to batch activate/deactivate mods and plugins
+ **/
+class ActivateModsDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief constructor
+ *
+ * @param missingPlugins a map containing missing plugins that need to be activated
+ * @param parent ... Defaults to 0.
+ **/
+ explicit ActivateModsDialog(const std::map<QString, std::vector<QString> > &missingPlugins, QWidget *parent = 0);
+ ~ActivateModsDialog();
+
+ /**
+ * @brief get a list of mods that the user chose to activate
+ *
+ * @note This can of ocurse only be called after the dialog has been displayed
+ *
+ * @return set< QString > the mods to activate
+ **/
+ std::set<QString> getModsToActivate();
+
+ /**
+ * @brief get a list of plugins that should be activated
+ *
+ * @return set< QString > the plugins to activate. This contains only plugins that become available after enabling the mods retrieved with getModsToActivate
+ **/
+ std::set<QString> getESPsToActivate();
+
+private slots:
+ void on_buttonBox_accepted();
+
+private:
+ Ui::ActivateModsDialog *ui;
+};
+
+#endif // ACTIVATEMODSDIALOG_H
diff --git a/src/baincomplexinstallerdialog.cpp b/src/baincomplexinstallerdialog.cpp
index 9b282c82..f1440ff3 100644
--- a/src/baincomplexinstallerdialog.cpp
+++ b/src/baincomplexinstallerdialog.cpp
@@ -23,6 +23,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDir>
+
+using namespace MOBase;
+
+
BainComplexInstallerDialog::BainComplexInstallerDialog(DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent)
: TutorableDialog("BainInstaller", parent), ui(new Ui::BainComplexInstallerDialog), m_Manual(false)
{
diff --git a/src/baincomplexinstallerdialog.h b/src/baincomplexinstallerdialog.h
index c2f74745..136a4e54 100644
--- a/src/baincomplexinstallerdialog.h
+++ b/src/baincomplexinstallerdialog.h
@@ -17,79 +17,79 @@ 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 BAINCOMPLEXINSTALLERDIALOG_H
-#define BAINCOMPLEXINSTALLERDIALOG_H
-
-
-#include "mytree.h"
-#include "installdialog.h"
-#include "tutorabledialog.h"
-
-
-namespace Ui {
-class BainComplexInstallerDialog;
-}
-
-
-/**
- * @brief Dialog to choose from options offered by a (complex) bain package
- **/
-class BainComplexInstallerDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
- /**
- * @brief Constructor
- *
- * @param tree the directory tree of the archive. The caller is resonsible to verify this actually qualifies as a bain installer
- * @param modName proposed name for the mod. The dialog allows the user to change this
- * @param hasPackageTXT set to true if a package.txt is available for this archive. The file has to be unpacked to QDir::tempPath before displaying the dialog!
- * @param parent parent widget
- **/
- explicit BainComplexInstallerDialog(DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent);
- ~BainComplexInstallerDialog();
-
- /**
- * @return bool true if the user requested the manual dialog
- **/
- bool manualRequested() const { return m_Manual; }
-
- /**
- * @return QString the (user-modified) name to be used for the mod
- **/
- QString getName() const;
-
- /**
- * @brief retrieve the updated archive tree from the dialog. The caller is responsible to delete the returned tree.
- *
- * @note This call is destructive on the input tree!
- *
- * @param tree input tree. (TODO isn't this the same as the tree passed in the constructor?)
- * @return DataTree* a new tree with only the selected options and directories arranged correctly. The caller takes custody of this pointer!
- **/
- DirectoryTree *updateTree(DirectoryTree *tree);
-
-private slots:
-
- void on_manualBtn_clicked();
-
- void on_okBtn_clicked();
-
- void on_cancelBtn_clicked();
-
- void on_packageBtn_clicked();
-
-private:
-
- void moveTreeUp(DirectoryTree *target, DirectoryTree::Node *child);
-
-private:
-
- Ui::BainComplexInstallerDialog *ui;
-
- bool m_Manual;
-
-};
-
-#endif // BAINCOMPLEXINSTALLERDIALOG_H
+#ifndef BAINCOMPLEXINSTALLERDIALOG_H
+#define BAINCOMPLEXINSTALLERDIALOG_H
+
+
+#include "mytree.h"
+#include "installdialog.h"
+#include "tutorabledialog.h"
+
+
+namespace Ui {
+class BainComplexInstallerDialog;
+}
+
+
+/**
+ * @brief Dialog to choose from options offered by a (complex) bain package
+ **/
+class BainComplexInstallerDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor
+ *
+ * @param tree the directory tree of the archive. The caller is resonsible to verify this actually qualifies as a bain installer
+ * @param modName proposed name for the mod. The dialog allows the user to change this
+ * @param hasPackageTXT set to true if a package.txt is available for this archive. The file has to be unpacked to QDir::tempPath before displaying the dialog!
+ * @param parent parent widget
+ **/
+ explicit BainComplexInstallerDialog(MOBase::DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent);
+ ~BainComplexInstallerDialog();
+
+ /**
+ * @return bool true if the user requested the manual dialog
+ **/
+ bool manualRequested() const { return m_Manual; }
+
+ /**
+ * @return QString the (user-modified) name to be used for the mod
+ **/
+ QString getName() const;
+
+ /**
+ * @brief retrieve the updated archive tree from the dialog. The caller is responsible to delete the returned tree.
+ *
+ * @note This call is destructive on the input tree!
+ *
+ * @param tree input tree. (TODO isn't this the same as the tree passed in the constructor?)
+ * @return DataTree* a new tree with only the selected options and directories arranged correctly. The caller takes custody of this pointer!
+ **/
+ MOBase::DirectoryTree *updateTree(MOBase::DirectoryTree *tree);
+
+private slots:
+
+ void on_manualBtn_clicked();
+
+ void on_okBtn_clicked();
+
+ void on_cancelBtn_clicked();
+
+ void on_packageBtn_clicked();
+
+private:
+
+ void moveTreeUp(MOBase::DirectoryTree *target, MOBase::DirectoryTree::Node *child);
+
+private:
+
+ Ui::BainComplexInstallerDialog *ui;
+
+ bool m_Manual;
+
+};
+
+#endif // BAINCOMPLEXINSTALLERDIALOG_H
diff --git a/src/categories.cpp b/src/categories.cpp
index 133778c4..f8bc3530 100644
--- a/src/categories.cpp
+++ b/src/categories.cpp
@@ -17,254 +17,258 @@ 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 "categories.h"
-#include <utility.h>
-#include "report.h"
-#include <gameinfo.h>
-#include <QObject>
-#include <QFile>
-#include <QDir>
-#include <QList>
-
-
-CategoryFactory* CategoryFactory::s_Instance = NULL;
-
-
-CategoryFactory::CategoryFactory()
-{
- reset();
-
- QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
-
- if (!categoryFile.open(QIODevice::ReadOnly)) {
- loadDefaultCategories();
- } else {
- int lineNum = 0;
- while (!categoryFile.atEnd()) {
- QByteArray line = categoryFile.readLine();
- ++lineNum;
- QList<QByteArray> cells = line.split('|');
- if (cells.count() != 4) {
- qCritical("invalid category line %d: %s (%d cells)",
- lineNum, line.constData(), cells.count());
- } else {
- std::vector<int> nexusIDs;
- if (cells[2].length() > 0) {
- QList<QByteArray> nexusIDStrings = cells[2].split(',');
- for (QList<QByteArray>::iterator iter = nexusIDStrings.begin();
- iter != nexusIDStrings.end(); ++iter) {
- bool ok = false;
- int temp = iter->toInt(&ok);
- if (!ok) {
- qCritical("invalid id %s", iter->constData());
- }
- nexusIDs.push_back(temp);
- }
- }
- bool cell0Ok = true;
- bool cell3Ok = true;
- int id = cells[0].toInt(&cell0Ok);
- int parentID = cells[3].trimmed().toInt(&cell3Ok);
- if (!cell0Ok || !cell3Ok) {
- qCritical("invalid category line %d: %s",
- lineNum, line.constData());
- }
- addCategory(id, QString::fromUtf8(cells[1].constData()), nexusIDs, parentID);
- }
- }
- categoryFile.close();
- }
- std::sort(m_Categories.begin(), m_Categories.end());
- setParents();
-}
-
-
-CategoryFactory &CategoryFactory::instance()
-{
- if (s_Instance == NULL) {
- s_Instance = new CategoryFactory;
- }
- return *s_Instance;
-}
-
-
-void CategoryFactory::reset()
-{
- m_Categories.clear();
- addCategory(0, "None", MakeVector<int>(2, 28, 87), 0);
-}
-
-
-void CategoryFactory::setParents()
-{
- for (std::vector<Category>::iterator iter = m_Categories.begin();
- iter != m_Categories.end(); ++iter) {
- iter->m_HasChildren = false;
- }
-
- for (std::vector<Category>::const_iterator categoryIter = m_Categories.begin();
- categoryIter != m_Categories.end(); ++categoryIter) {
- if (categoryIter->m_ParentID != 0) {
- std::map<int, unsigned int>::const_iterator iter = m_IDMap.find(categoryIter->m_ParentID);
- if (iter != m_IDMap.end()) {
- m_Categories[iter->second].m_HasChildren = true;
- }
- }
- }
-}
-
-
-void CategoryFactory::saveCategories()
-{
- QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
-
- if (!categoryFile.open(QIODevice::WriteOnly)) {
- reportError(QObject::tr("Failed to save custom categories"));
- return;
- }
-
- categoryFile.resize(0);
- for (std::vector<Category>::const_iterator iter = m_Categories.begin();
- iter != m_Categories.end(); ++iter) {
- if (iter->m_ID == 0) {
- continue;
- }
- QByteArray line;
- line.append(QByteArray::number(iter->m_ID)).append("|")
- .append(iter->m_Name.toUtf8()).append("|")
- .append(VectorJoin(iter->m_NexusIDs, ",")).append("|")
- .append(QByteArray::number(iter->m_ParentID)).append("\n");
- categoryFile.write(line);
- }
- categoryFile.close();
-}
-
-
-void CategoryFactory::addCategory(int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
-{
- int index = m_Categories.size();
- m_Categories.push_back(Category(index, id, name, nexusIDs, parentID));
- for (std::vector<int>::const_iterator iter = nexusIDs.begin();
- iter != nexusIDs.end(); ++iter) {
- m_NexusMap[*iter] = index;
- }
- m_IDMap[id] = index;
-}
-
-
-void CategoryFactory::loadDefaultCategories()
-{
- // the order here is relevant as it defines the order in which the
- // mods appear in the combo box
- addCategory(1, "Animations", MakeVector<int>(1, 51), 0);
- addCategory(2, "Armour", MakeVector<int>(1, 54), 0);
- addCategory(3, "Audio", MakeVector<int>(1, 61), 0);
- addCategory(5, "Clothing", MakeVector<int>(1, 60), 0);
- addCategory(6, "Collectables", MakeVector<int>(1, 92), 0);
- addCategory(7, "Creatures", MakeVector<int>(2, 83, 65), 0);
- addCategory(8, "Factions", MakeVector<int>(1, 25), 0);
- addCategory(9, "Gameplay", MakeVector<int>(1, 24), 0);
- addCategory(10, "Hair", MakeVector<int>(1, 26), 0);
- addCategory(11, "Items", MakeVector<int>(2, 27, 85), 0);
- addCategory(19, "Weapons", MakeVector<int>(2, 36, 55), 11);
- addCategory(12, "Locations", MakeVector<int>(7, 22, 30, 70, 88, 89, 90, 91), 0);
- addCategory(4, "Cities", MakeVector<int>(1, 53), 12);
- addCategory(20, "Magic", MakeVector<int>(3, 75, 93, 94), 0);
- addCategory(21, "Models & Textures", MakeVector<int>(1, 29), 0);
- addCategory(13, "NPCs", MakeVector<int>(1, 33), 0);
- addCategory(14, "Patches", MakeVector<int>(2, 79, 84), 0);
- addCategory(15, "Quests", MakeVector<int>(1, 35), 0);
- addCategory(16, "Races & Classes", MakeVector<int>(1, 34), 0);
- addCategory(22, "Skills", MakeVector<int>(1, 73), 0);
- addCategory(17, "UI", MakeVector<int>(1, 42), 0);
- addCategory(18, "Visuals", MakeVector<int>(1, 62), 0);
-}
-
-
-int CategoryFactory::getParentID(unsigned int index) const
-{
- if ((index < 0) || (index >= m_Categories.size())) {
- throw MyException(QObject::tr("invalid index %1").arg(index));
- }
-
- return m_Categories[index].m_ParentID;
-}
-
-
-bool CategoryFactory::categoryExists(int id) const
-{
- return m_IDMap.find(id) != m_IDMap.end();
-}
-
-
-bool CategoryFactory::isDecendantOf(int id, int parentID) const
-{
- std::map<int, unsigned int>::const_iterator iter = m_IDMap.find(id);
- if (iter != m_IDMap.end()) {
- unsigned int index = iter->second;
- if (m_Categories[index].m_ParentID == 0) {
- return false;
- } else if (m_Categories[index].m_ParentID == parentID) {
- return true;
- } else {
- return isDecendantOf(m_Categories[index].m_ParentID, parentID);
- }
- } else {
- qWarning("%d is no valid category id", id);
- return false;
- }
-}
-
-
-bool CategoryFactory::hasChildren(unsigned int index) const
-{
- if ((index < 0) || (index >= m_Categories.size())) {
- throw MyException(QObject::tr("invalid index %1").arg(index));
- }
-
- return m_Categories[index].m_HasChildren;
-}
-
-
-QString CategoryFactory::getCategoryName(unsigned int index) const
-{
- if ((index < 0) || (index >= m_Categories.size())) {
- throw MyException(QObject::tr("invalid index %1").arg(index));
- }
-
- return m_Categories[index].m_Name;
-}
-
-
-int CategoryFactory::getCategoryID(unsigned int index) const
-{
- if ((index < 0) || (index >= m_Categories.size())) {
- throw MyException(QObject::tr("invalid index %1").arg(index));
- }
-
- return m_Categories[index].m_ID;
-}
-
-
-int CategoryFactory::getCategoryIndex(int ID) const
-{
- std::map<int, unsigned int>::const_iterator iter = m_IDMap.find(ID);
- if (iter == m_IDMap.end()) {
- throw MyException(QObject::tr("invalid category id %1").arg(ID));
- }
- return iter->second;
-}
-
-
-
-unsigned int CategoryFactory::resolveNexusID(int nexusID) const
-{
- std::map<int, unsigned int>::const_iterator iter = m_NexusMap.find(nexusID);
- if (iter != m_NexusMap.end()) {
- qDebug("nexus category id %d maps to internal %d", nexusID, iter->second);
- return iter->second;
- } else {
- qDebug("nexus category id %d not mapped", nexusID);
- return 0U;
- }
-}
+#include "categories.h"
+#include <utility.h>
+#include "report.h"
+#include <gameinfo.h>
+#include <QObject>
+#include <QFile>
+#include <QDir>
+#include <QList>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+CategoryFactory* CategoryFactory::s_Instance = NULL;
+
+
+CategoryFactory::CategoryFactory()
+{
+ reset();
+
+ QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
+
+ if (!categoryFile.open(QIODevice::ReadOnly)) {
+ loadDefaultCategories();
+ } else {
+ int lineNum = 0;
+ while (!categoryFile.atEnd()) {
+ QByteArray line = categoryFile.readLine();
+ ++lineNum;
+ QList<QByteArray> cells = line.split('|');
+ if (cells.count() != 4) {
+ qCritical("invalid category line %d: %s (%d cells)",
+ lineNum, line.constData(), cells.count());
+ } else {
+ std::vector<int> nexusIDs;
+ if (cells[2].length() > 0) {
+ QList<QByteArray> nexusIDStrings = cells[2].split(',');
+ for (QList<QByteArray>::iterator iter = nexusIDStrings.begin();
+ iter != nexusIDStrings.end(); ++iter) {
+ bool ok = false;
+ int temp = iter->toInt(&ok);
+ if (!ok) {
+ qCritical("invalid id %s", iter->constData());
+ }
+ nexusIDs.push_back(temp);
+ }
+ }
+ bool cell0Ok = true;
+ bool cell3Ok = true;
+ int id = cells[0].toInt(&cell0Ok);
+ int parentID = cells[3].trimmed().toInt(&cell3Ok);
+ if (!cell0Ok || !cell3Ok) {
+ qCritical("invalid category line %d: %s",
+ lineNum, line.constData());
+ }
+ addCategory(id, QString::fromUtf8(cells[1].constData()), nexusIDs, parentID);
+ }
+ }
+ categoryFile.close();
+ }
+ std::sort(m_Categories.begin(), m_Categories.end());
+ setParents();
+}
+
+
+CategoryFactory &CategoryFactory::instance()
+{
+ if (s_Instance == NULL) {
+ s_Instance = new CategoryFactory;
+ }
+ return *s_Instance;
+}
+
+
+void CategoryFactory::reset()
+{
+ m_Categories.clear();
+ addCategory(0, "None", MakeVector<int>(2, 28, 87), 0);
+}
+
+
+void CategoryFactory::setParents()
+{
+ for (std::vector<Category>::iterator iter = m_Categories.begin();
+ iter != m_Categories.end(); ++iter) {
+ iter->m_HasChildren = false;
+ }
+
+ for (std::vector<Category>::const_iterator categoryIter = m_Categories.begin();
+ categoryIter != m_Categories.end(); ++categoryIter) {
+ if (categoryIter->m_ParentID != 0) {
+ std::map<int, unsigned int>::const_iterator iter = m_IDMap.find(categoryIter->m_ParentID);
+ if (iter != m_IDMap.end()) {
+ m_Categories[iter->second].m_HasChildren = true;
+ }
+ }
+ }
+}
+
+
+void CategoryFactory::saveCategories()
+{
+ QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
+
+ if (!categoryFile.open(QIODevice::WriteOnly)) {
+ reportError(QObject::tr("Failed to save custom categories"));
+ return;
+ }
+
+ categoryFile.resize(0);
+ for (std::vector<Category>::const_iterator iter = m_Categories.begin();
+ iter != m_Categories.end(); ++iter) {
+ if (iter->m_ID == 0) {
+ continue;
+ }
+ QByteArray line;
+ line.append(QByteArray::number(iter->m_ID)).append("|")
+ .append(iter->m_Name.toUtf8()).append("|")
+ .append(VectorJoin(iter->m_NexusIDs, ",")).append("|")
+ .append(QByteArray::number(iter->m_ParentID)).append("\n");
+ categoryFile.write(line);
+ }
+ categoryFile.close();
+}
+
+
+void CategoryFactory::addCategory(int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
+{
+ int index = m_Categories.size();
+ m_Categories.push_back(Category(index, id, name, nexusIDs, parentID));
+ for (std::vector<int>::const_iterator iter = nexusIDs.begin();
+ iter != nexusIDs.end(); ++iter) {
+ m_NexusMap[*iter] = index;
+ }
+ m_IDMap[id] = index;
+}
+
+
+void CategoryFactory::loadDefaultCategories()
+{
+ // the order here is relevant as it defines the order in which the
+ // mods appear in the combo box
+ addCategory(1, "Animations", MakeVector<int>(1, 51), 0);
+ addCategory(2, "Armour", MakeVector<int>(1, 54), 0);
+ addCategory(3, "Audio", MakeVector<int>(1, 61), 0);
+ addCategory(5, "Clothing", MakeVector<int>(1, 60), 0);
+ addCategory(6, "Collectables", MakeVector<int>(1, 92), 0);
+ addCategory(7, "Creatures", MakeVector<int>(2, 83, 65), 0);
+ addCategory(8, "Factions", MakeVector<int>(1, 25), 0);
+ addCategory(9, "Gameplay", MakeVector<int>(1, 24), 0);
+ addCategory(10, "Hair", MakeVector<int>(1, 26), 0);
+ addCategory(11, "Items", MakeVector<int>(2, 27, 85), 0);
+ addCategory(19, "Weapons", MakeVector<int>(2, 36, 55), 11);
+ addCategory(12, "Locations", MakeVector<int>(7, 22, 30, 70, 88, 89, 90, 91), 0);
+ addCategory(4, "Cities", MakeVector<int>(1, 53), 12);
+ addCategory(20, "Magic", MakeVector<int>(3, 75, 93, 94), 0);
+ addCategory(21, "Models & Textures", MakeVector<int>(1, 29), 0);
+ addCategory(13, "NPCs", MakeVector<int>(1, 33), 0);
+ addCategory(14, "Patches", MakeVector<int>(2, 79, 84), 0);
+ addCategory(15, "Quests", MakeVector<int>(1, 35), 0);
+ addCategory(16, "Races & Classes", MakeVector<int>(1, 34), 0);
+ addCategory(22, "Skills", MakeVector<int>(1, 73), 0);
+ addCategory(17, "UI", MakeVector<int>(1, 42), 0);
+ addCategory(18, "Visuals", MakeVector<int>(1, 62), 0);
+}
+
+
+int CategoryFactory::getParentID(unsigned int index) const
+{
+ if ((index < 0) || (index >= m_Categories.size())) {
+ throw MyException(QObject::tr("invalid index %1").arg(index));
+ }
+
+ return m_Categories[index].m_ParentID;
+}
+
+
+bool CategoryFactory::categoryExists(int id) const
+{
+ return m_IDMap.find(id) != m_IDMap.end();
+}
+
+
+bool CategoryFactory::isDecendantOf(int id, int parentID) const
+{
+ std::map<int, unsigned int>::const_iterator iter = m_IDMap.find(id);
+ if (iter != m_IDMap.end()) {
+ unsigned int index = iter->second;
+ if (m_Categories[index].m_ParentID == 0) {
+ return false;
+ } else if (m_Categories[index].m_ParentID == parentID) {
+ return true;
+ } else {
+ return isDecendantOf(m_Categories[index].m_ParentID, parentID);
+ }
+ } else {
+ qWarning("%d is no valid category id", id);
+ return false;
+ }
+}
+
+
+bool CategoryFactory::hasChildren(unsigned int index) const
+{
+ if ((index < 0) || (index >= m_Categories.size())) {
+ throw MyException(QObject::tr("invalid index %1").arg(index));
+ }
+
+ return m_Categories[index].m_HasChildren;
+}
+
+
+QString CategoryFactory::getCategoryName(unsigned int index) const
+{
+ if ((index < 0) || (index >= m_Categories.size())) {
+ throw MyException(QObject::tr("invalid index %1").arg(index));
+ }
+
+ return m_Categories[index].m_Name;
+}
+
+
+int CategoryFactory::getCategoryID(unsigned int index) const
+{
+ if ((index < 0) || (index >= m_Categories.size())) {
+ throw MyException(QObject::tr("invalid index %1").arg(index));
+ }
+
+ return m_Categories[index].m_ID;
+}
+
+
+int CategoryFactory::getCategoryIndex(int ID) const
+{
+ std::map<int, unsigned int>::const_iterator iter = m_IDMap.find(ID);
+ if (iter == m_IDMap.end()) {
+ throw MyException(QObject::tr("invalid category id %1").arg(ID));
+ }
+ return iter->second;
+}
+
+
+
+unsigned int CategoryFactory::resolveNexusID(int nexusID) const
+{
+ std::map<int, unsigned int>::const_iterator iter = m_NexusMap.find(nexusID);
+ if (iter != m_NexusMap.end()) {
+ qDebug("nexus category id %d maps to internal %d", nexusID, iter->second);
+ return iter->second;
+ } else {
+ qDebug("nexus category id %d not mapped", nexusID);
+ return 0U;
+ }
+}
diff --git a/src/categoriesdialog.cpp b/src/categoriesdialog.cpp
index 8a634dd8..21e1a5aa 100644
--- a/src/categoriesdialog.cpp
+++ b/src/categoriesdialog.cpp
@@ -17,226 +17,226 @@ 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 "categoriesdialog.h"
-#include "ui_categoriesdialog.h"
-#include "categories.h"
-#include "utility.h"
-#include <QItemDelegate>
-#include <QRegExpValidator>
-#include <QLineEdit>
-#include <QMenu>
-
-
-class NewIDValidator : public QIntValidator {
-public:
- NewIDValidator(const std::set<int> &ids)
- : m_UsedIDs(ids) {}
- virtual State validate(QString &input, int &pos) const {
- State intRes = QIntValidator::validate(input, pos);
- if (intRes == Acceptable) {
- bool ok = false;
- int id = input.toInt(&ok);
- if (m_UsedIDs.find(id) != m_UsedIDs.end()) {
- return QValidator::Intermediate;
- }
- }
- return intRes;
- }
-private:
- const std::set<int> &m_UsedIDs;
-};
-
-
-class ExistingIDValidator : public QIntValidator {
-public:
- ExistingIDValidator(const std::set<int> &ids)
- : m_UsedIDs(ids) {}
- virtual State validate(QString &input, int &pos) const {
- State intRes = QIntValidator::validate(input, pos);
- if (intRes == Acceptable) {
- bool ok = false;
- int id = input.toInt(&ok);
- if ((id == 0) || (m_UsedIDs.find(id) != m_UsedIDs.end())) {
- return QValidator::Acceptable;
- } else {
- return QValidator::Intermediate;
- }
- } else {
- return intRes;
- }
- }
-private:
- const std::set<int> &m_UsedIDs;
-};
-
-
-class ValidatingDelegate : public QItemDelegate {
-
-public:
- ValidatingDelegate(QObject *parent, QValidator *validator)
- : QItemDelegate(parent), m_Validator(validator) {}
-
- QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const
- {
- QLineEdit *edit = new QLineEdit(parent);
- edit->setValidator(m_Validator);
- return edit;
- }
- virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
- {
- QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
- int pos = 0;
- if (m_Validator->validate(edit->text(), pos) == QValidator::Acceptable) {
- QItemDelegate::setModelData(editor, model, index);
- }
- }
-private:
- QValidator *m_Validator;
-};
-
-
-CategoriesDialog::CategoriesDialog(QWidget *parent)
- : TutorableDialog("Categories", parent), ui(new Ui::CategoriesDialog)
-{
- ui->setupUi(this);
- fillTable();
- connect(ui->categoriesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int,int)));
-}
-
-CategoriesDialog::~CategoriesDialog()
-{
- delete ui;
-}
-
-
-void CategoriesDialog::cellChanged(int row, int)
-{
- int currentID = ui->categoriesTable->item(row, 0)->text().toInt();
- if (currentID > m_HighestID) {
- m_HighestID = currentID;
- }
-}
-
-
-void CategoriesDialog::commitChanges()
-{
- CategoryFactory &categories = CategoryFactory::instance();
- categories.reset();
-
- for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) {
- int index = ui->categoriesTable->verticalHeader()->logicalIndex(i);
- QString nexusIDString = ui->categoriesTable->item(index, 2)->text();
- QStringList nexusIDStringList = nexusIDString.split(',', QString::SkipEmptyParts);
- std::vector<int> nexusIDs;
- for (QStringList::iterator iter = nexusIDStringList.begin();
- iter != nexusIDStringList.end(); ++iter) {
- nexusIDs.push_back(iter->toInt());
- }
-
- categories.addCategory(
- ui->categoriesTable->item(index, 0)->text().toInt(),
- ui->categoriesTable->item(index, 1)->text(),
- nexusIDs,
- ui->categoriesTable->item(index, 3)->text().toInt());
- }
- categories.setParents();
-
- categories.saveCategories();
-}
-
-
-void CategoriesDialog::refreshIDs()
-{
- m_HighestID = 0;
- for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) {
- int id = ui->categoriesTable->item(i, 0)->text().toInt();
- if (id > m_HighestID) {
- m_HighestID = id;
- }
- m_IDs.insert(id);
- }
-}
-
-
-void CategoriesDialog::fillTable()
-{
- CategoryFactory &categories = CategoryFactory::instance();
- QTableWidget *table = ui->categoriesTable;
-
-#if QT_VERSION >= 0x050000
- table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
- table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
- table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
- table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
- table->verticalHeader()->setSectionsMovable(true);
- table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
-#else
- table->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed);
- table->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
- table->horizontalHeader()->setResizeMode(2, QHeaderView::Fixed);
- table->horizontalHeader()->setResizeMode(3, QHeaderView::Fixed);
- table->verticalHeader()->setMovable(true);
- table->verticalHeader()->setResizeMode(QHeaderView::Fixed);
-#endif
-
-
- table->setItemDelegateForColumn(0, new ValidatingDelegate(this, new NewIDValidator(m_IDs)));
- table->setItemDelegateForColumn(2, new ValidatingDelegate(this, new QRegExpValidator(QRegExp("([0-9]+)?(,[0-9]+)*"), this)));
- table->setItemDelegateForColumn(3, new ValidatingDelegate(this, new ExistingIDValidator(m_IDs)));
-
- int row = 0;
- for (std::vector<CategoryFactory::Category>::const_iterator iter = categories.m_Categories.begin();
- iter != categories.m_Categories.end(); ++iter, ++row) {
- const CategoryFactory::Category &category = *iter;
- if (category.m_ID == 0) {
- --row;
- continue;
- }
- table->insertRow(row);
-// table->setVerticalHeaderItem(row, new QTableWidgetItem(" "));
-
- QScopedPointer<QTableWidgetItem> idItem(new QTableWidgetItem());
- idItem->setData(Qt::DisplayRole, category.m_ID);
-
- QScopedPointer<QTableWidgetItem> nameItem(new QTableWidgetItem(category.m_Name));
- QScopedPointer<QTableWidgetItem> nexusIDItem(new QTableWidgetItem(VectorJoin(category.m_NexusIDs, ",")));
- QScopedPointer<QTableWidgetItem> parentIDItem(new QTableWidgetItem());
- parentIDItem->setData(Qt::DisplayRole, category.m_ParentID);
-
- table->setItem(row, 0, idItem.take());
- table->setItem(row, 1, nameItem.take());
- table->setItem(row, 2, nexusIDItem.take());
- table->setItem(row, 3, parentIDItem.take());
- }
-
- refreshIDs();
-}
-
-
-void CategoriesDialog::addCategory_clicked()
-{
- int row = m_ContextRow >= 0 ? m_ContextRow : 0;
- ui->categoriesTable->insertRow(row);
- ui->categoriesTable->setVerticalHeaderItem(row, new QTableWidgetItem(" "));
- ui->categoriesTable->setItem(row, 0, new QTableWidgetItem(QString::number(++m_HighestID)));
- ui->categoriesTable->setItem(row, 1, new QTableWidgetItem("new"));
- ui->categoriesTable->setItem(row, 2, new QTableWidgetItem(""));
- ui->categoriesTable->setItem(row, 3, new QTableWidgetItem("0"));
-}
-
-
-void CategoriesDialog::removeCategory_clicked()
-{
- ui->categoriesTable->removeRow(m_ContextRow);
-}
-
-
-void CategoriesDialog::on_categoriesTable_customContextMenuRequested(const QPoint &pos)
-{
- m_ContextRow = ui->categoriesTable->rowAt(pos.y());
- QMenu menu;
- menu.addAction(tr("Add"), this, SLOT(addCategory_clicked()));
- menu.addAction(tr("Remove"), this, SLOT(removeCategory_clicked()));
-
- menu.exec(ui->categoriesTable->mapToGlobal(pos));
-}
+#include "categoriesdialog.h"
+#include "ui_categoriesdialog.h"
+#include "categories.h"
+#include "utility.h"
+#include <QItemDelegate>
+#include <QRegExpValidator>
+#include <QLineEdit>
+#include <QMenu>
+
+
+class NewIDValidator : public QIntValidator {
+public:
+ NewIDValidator(const std::set<int> &ids)
+ : m_UsedIDs(ids) {}
+ virtual State validate(QString &input, int &pos) const {
+ State intRes = QIntValidator::validate(input, pos);
+ if (intRes == Acceptable) {
+ bool ok = false;
+ int id = input.toInt(&ok);
+ if (m_UsedIDs.find(id) != m_UsedIDs.end()) {
+ return QValidator::Intermediate;
+ }
+ }
+ return intRes;
+ }
+private:
+ const std::set<int> &m_UsedIDs;
+};
+
+
+class ExistingIDValidator : public QIntValidator {
+public:
+ ExistingIDValidator(const std::set<int> &ids)
+ : m_UsedIDs(ids) {}
+ virtual State validate(QString &input, int &pos) const {
+ State intRes = QIntValidator::validate(input, pos);
+ if (intRes == Acceptable) {
+ bool ok = false;
+ int id = input.toInt(&ok);
+ if ((id == 0) || (m_UsedIDs.find(id) != m_UsedIDs.end())) {
+ return QValidator::Acceptable;
+ } else {
+ return QValidator::Intermediate;
+ }
+ } else {
+ return intRes;
+ }
+ }
+private:
+ const std::set<int> &m_UsedIDs;
+};
+
+
+class ValidatingDelegate : public QItemDelegate {
+
+public:
+ ValidatingDelegate(QObject *parent, QValidator *validator)
+ : QItemDelegate(parent), m_Validator(validator) {}
+
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const
+ {
+ QLineEdit *edit = new QLineEdit(parent);
+ edit->setValidator(m_Validator);
+ return edit;
+ }
+ virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
+ {
+ QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
+ int pos = 0;
+ if (m_Validator->validate(edit->text(), pos) == QValidator::Acceptable) {
+ QItemDelegate::setModelData(editor, model, index);
+ }
+ }
+private:
+ QValidator *m_Validator;
+};
+
+
+CategoriesDialog::CategoriesDialog(QWidget *parent)
+ : TutorableDialog("Categories", parent), ui(new Ui::CategoriesDialog)
+{
+ ui->setupUi(this);
+ fillTable();
+ connect(ui->categoriesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int,int)));
+}
+
+CategoriesDialog::~CategoriesDialog()
+{
+ delete ui;
+}
+
+
+void CategoriesDialog::cellChanged(int row, int)
+{
+ int currentID = ui->categoriesTable->item(row, 0)->text().toInt();
+ if (currentID > m_HighestID) {
+ m_HighestID = currentID;
+ }
+}
+
+
+void CategoriesDialog::commitChanges()
+{
+ CategoryFactory &categories = CategoryFactory::instance();
+ categories.reset();
+
+ for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) {
+ int index = ui->categoriesTable->verticalHeader()->logicalIndex(i);
+ QString nexusIDString = ui->categoriesTable->item(index, 2)->text();
+ QStringList nexusIDStringList = nexusIDString.split(',', QString::SkipEmptyParts);
+ std::vector<int> nexusIDs;
+ for (QStringList::iterator iter = nexusIDStringList.begin();
+ iter != nexusIDStringList.end(); ++iter) {
+ nexusIDs.push_back(iter->toInt());
+ }
+
+ categories.addCategory(
+ ui->categoriesTable->item(index, 0)->text().toInt(),
+ ui->categoriesTable->item(index, 1)->text(),
+ nexusIDs,
+ ui->categoriesTable->item(index, 3)->text().toInt());
+ }
+ categories.setParents();
+
+ categories.saveCategories();
+}
+
+
+void CategoriesDialog::refreshIDs()
+{
+ m_HighestID = 0;
+ for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) {
+ int id = ui->categoriesTable->item(i, 0)->text().toInt();
+ if (id > m_HighestID) {
+ m_HighestID = id;
+ }
+ m_IDs.insert(id);
+ }
+}
+
+
+void CategoriesDialog::fillTable()
+{
+ CategoryFactory &categories = CategoryFactory::instance();
+ QTableWidget *table = ui->categoriesTable;
+
+#if QT_VERSION >= 0x050000
+ table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
+ table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
+ table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
+ table->verticalHeader()->setSectionsMovable(true);
+ table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+#else
+ table->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed);
+ table->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
+ table->horizontalHeader()->setResizeMode(2, QHeaderView::Fixed);
+ table->horizontalHeader()->setResizeMode(3, QHeaderView::Fixed);
+ table->verticalHeader()->setMovable(true);
+ table->verticalHeader()->setResizeMode(QHeaderView::Fixed);
+#endif
+
+
+ table->setItemDelegateForColumn(0, new ValidatingDelegate(this, new NewIDValidator(m_IDs)));
+ table->setItemDelegateForColumn(2, new ValidatingDelegate(this, new QRegExpValidator(QRegExp("([0-9]+)?(,[0-9]+)*"), this)));
+ table->setItemDelegateForColumn(3, new ValidatingDelegate(this, new ExistingIDValidator(m_IDs)));
+
+ int row = 0;
+ for (std::vector<CategoryFactory::Category>::const_iterator iter = categories.m_Categories.begin();
+ iter != categories.m_Categories.end(); ++iter, ++row) {
+ const CategoryFactory::Category &category = *iter;
+ if (category.m_ID == 0) {
+ --row;
+ continue;
+ }
+ table->insertRow(row);
+// table->setVerticalHeaderItem(row, new QTableWidgetItem(" "));
+
+ QScopedPointer<QTableWidgetItem> idItem(new QTableWidgetItem());
+ idItem->setData(Qt::DisplayRole, category.m_ID);
+
+ QScopedPointer<QTableWidgetItem> nameItem(new QTableWidgetItem(category.m_Name));
+ QScopedPointer<QTableWidgetItem> nexusIDItem(new QTableWidgetItem(MOBase::VectorJoin(category.m_NexusIDs, ",")));
+ QScopedPointer<QTableWidgetItem> parentIDItem(new QTableWidgetItem());
+ parentIDItem->setData(Qt::DisplayRole, category.m_ParentID);
+
+ table->setItem(row, 0, idItem.take());
+ table->setItem(row, 1, nameItem.take());
+ table->setItem(row, 2, nexusIDItem.take());
+ table->setItem(row, 3, parentIDItem.take());
+ }
+
+ refreshIDs();
+}
+
+
+void CategoriesDialog::addCategory_clicked()
+{
+ int row = m_ContextRow >= 0 ? m_ContextRow : 0;
+ ui->categoriesTable->insertRow(row);
+ ui->categoriesTable->setVerticalHeaderItem(row, new QTableWidgetItem(" "));
+ ui->categoriesTable->setItem(row, 0, new QTableWidgetItem(QString::number(++m_HighestID)));
+ ui->categoriesTable->setItem(row, 1, new QTableWidgetItem("new"));
+ ui->categoriesTable->setItem(row, 2, new QTableWidgetItem(""));
+ ui->categoriesTable->setItem(row, 3, new QTableWidgetItem("0"));
+}
+
+
+void CategoriesDialog::removeCategory_clicked()
+{
+ ui->categoriesTable->removeRow(m_ContextRow);
+}
+
+
+void CategoriesDialog::on_categoriesTable_customContextMenuRequested(const QPoint &pos)
+{
+ m_ContextRow = ui->categoriesTable->rowAt(pos.y());
+ QMenu menu;
+ menu.addAction(tr("Add"), this, SLOT(addCategory_clicked()));
+ menu.addAction(tr("Remove"), this, SLOT(removeCategory_clicked()));
+
+ menu.exec(ui->categoriesTable->mapToGlobal(pos));
+}
diff --git a/src/categoriesdialog.h b/src/categoriesdialog.h
index 34007e77..67851618 100644
--- a/src/categoriesdialog.h
+++ b/src/categoriesdialog.h
@@ -17,55 +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 CATEGORIESDIALOG_H
-#define CATEGORIESDIALOG_H
-
-#include "tutorabledialog.h"
-#include <set>
-
-namespace Ui {
-class CategoriesDialog;
-}
-
-/**
- * @brief Dialog that allows users to configure mod categories
- **/
-class CategoriesDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
-
- explicit CategoriesDialog(QWidget *parent = 0);
- ~CategoriesDialog();
-
- /**
- * @brief store changes here to the global categories store (categories.h)
- *
- **/
- void commitChanges();
-
-private slots:
-
- void on_categoriesTable_customContextMenuRequested(const QPoint &pos);
- void addCategory_clicked();
- void removeCategory_clicked();
- void cellChanged(int row, int column);
-
-private:
-
- void refreshIDs();
- void fillTable();
-
-private:
-
- Ui::CategoriesDialog *ui;
- int m_ContextRow;
-
- int m_HighestID;
- std::set<int> m_IDs;
-
-};
-
-#endif // CATEGORIESDIALOG_H
-
+#ifndef CATEGORIESDIALOG_H
+#define CATEGORIESDIALOG_H
+
+#include "tutorabledialog.h"
+#include <set>
+
+namespace Ui {
+class CategoriesDialog;
+}
+
+/**
+ * @brief Dialog that allows users to configure mod categories
+ **/
+class CategoriesDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+
+ explicit CategoriesDialog(QWidget *parent = 0);
+ ~CategoriesDialog();
+
+ /**
+ * @brief store changes here to the global categories store (categories.h)
+ *
+ **/
+ void commitChanges();
+
+private slots:
+
+ void on_categoriesTable_customContextMenuRequested(const QPoint &pos);
+ void addCategory_clicked();
+ void removeCategory_clicked();
+ void cellChanged(int row, int column);
+
+private:
+
+ void refreshIDs();
+ void fillTable();
+
+private:
+
+ Ui::CategoriesDialog *ui;
+ int m_ContextRow;
+
+ int m_HighestID;
+ std::set<int> m_IDs;
+
+};
+
+#endif // CATEGORIESDIALOG_H
+
diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp
index 30ea46ae..c6d458de 100644
--- a/src/directoryrefresher.cpp
+++ b/src/directoryrefresher.cpp
@@ -17,87 +17,91 @@ 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 "directoryrefresher.h"
-#include "utility.h"
-#include "report.h"
-#include <gameinfo.h>
-#include <QDir>
-
-
-DirectoryRefresher::DirectoryRefresher()
- : m_DirectoryStructure(NULL)
-{
-}
-
-DirectoryEntry *DirectoryRefresher::getDirectoryStructure()
-{
- QMutexLocker locker(&m_RefreshLock);
- DirectoryEntry *result = m_DirectoryStructure;
- m_DirectoryStructure = NULL;
- return result;
-}
-
-void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods)
-{
- QMutexLocker locker(&m_RefreshLock);
- m_Mods = mods;
-}
-
-
-void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
-{
- static wchar_t *files[] = { L"meta.ini", L"readme.txt" };
- for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
- structure->removeFile(files[i]);
- }
-
- static wchar_t *dirs[] = { L"fomod" };
- for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
- structure->removeDir(dirs[i]);
- }
-}
-
-void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory)
-{
- std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
-
- directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
- QDir dir(directory);
- QFileInfoList bsaFiles = dir.entryInfoList(QStringList("*.bsa"), QDir::Files);
- foreach (QFileInfo file, bsaFiles) {
- directoryStructure->addFromBSA(ToWString(modName), directoryW,
- ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority);
- }
-}
-
-void DirectoryRefresher::refresh()
-{
- QMutexLocker locker(&m_RefreshLock);
-
- delete m_DirectoryStructure;
-
- m_DirectoryStructure = new DirectoryEntry(L"data", NULL, 0);
-
- // TODO what was the point of having the priority in this tuple? the list is already sorted by priority
- std::vector<std::tuple<QString, QString, int> >::const_iterator iter = m_Mods.begin();
-
- //TODO i is the priority here, where higher = more important. the input vector is also sorted by priority but inverted!
- for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
- QString modName = std::get<0>(*iter);
- try {
- addModToStructure(m_DirectoryStructure, modName, i, std::get<1>(*iter));
- } catch (const std::exception &e) {
- emit error(tr("failed to read bsa: %1").arg(e.what()));
- }
- emit progress((i * 100) / m_Mods.size() + 1);
- }
-
- std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
- m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
-
- emit progress(100);
-
- cleanStructure(m_DirectoryStructure);
-
- emit refreshed();
-}
+#include "directoryrefresher.h"
+#include "utility.h"
+#include "report.h"
+#include <gameinfo.h>
+#include <QDir>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+DirectoryRefresher::DirectoryRefresher()
+ : m_DirectoryStructure(NULL)
+{
+}
+
+DirectoryEntry *DirectoryRefresher::getDirectoryStructure()
+{
+ QMutexLocker locker(&m_RefreshLock);
+ DirectoryEntry *result = m_DirectoryStructure;
+ m_DirectoryStructure = NULL;
+ return result;
+}
+
+void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods)
+{
+ QMutexLocker locker(&m_RefreshLock);
+ m_Mods = mods;
+}
+
+
+void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
+{
+ static wchar_t *files[] = { L"meta.ini", L"readme.txt" };
+ for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
+ structure->removeFile(files[i]);
+ }
+
+ static wchar_t *dirs[] = { L"fomod" };
+ for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
+ structure->removeDir(dirs[i]);
+ }
+}
+
+void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory)
+{
+ std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
+
+ directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
+ QDir dir(directory);
+ QFileInfoList bsaFiles = dir.entryInfoList(QStringList("*.bsa"), QDir::Files);
+ foreach (QFileInfo file, bsaFiles) {
+ directoryStructure->addFromBSA(ToWString(modName), directoryW,
+ ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority);
+ }
+}
+
+void DirectoryRefresher::refresh()
+{
+ QMutexLocker locker(&m_RefreshLock);
+
+ delete m_DirectoryStructure;
+
+ m_DirectoryStructure = new DirectoryEntry(L"data", NULL, 0);
+
+ // TODO what was the point of having the priority in this tuple? the list is already sorted by priority
+ std::vector<std::tuple<QString, QString, int> >::const_iterator iter = m_Mods.begin();
+
+ //TODO i is the priority here, where higher = more important. the input vector is also sorted by priority but inverted!
+ for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
+ QString modName = std::get<0>(*iter);
+ try {
+ addModToStructure(m_DirectoryStructure, modName, i, std::get<1>(*iter));
+ } catch (const std::exception &e) {
+ emit error(tr("failed to read bsa: %1").arg(e.what()));
+ }
+ emit progress((i * 100) / m_Mods.size() + 1);
+ }
+
+ std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
+ m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
+
+ emit progress(100);
+
+ cleanStructure(m_DirectoryStructure);
+
+ emit refreshed();
+}
diff --git a/src/directoryrefresher.h b/src/directoryrefresher.h
index 47259fdd..5b785b2f 100644
--- a/src/directoryrefresher.h
+++ b/src/directoryrefresher.h
@@ -17,90 +17,90 @@ 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 DIRECTORYREFRESHER_H
-#define DIRECTORYREFRESHER_H
-
-#include <QObject>
-#include <directoryentry.h>
-#include <vector>
-#include <QMutex>
-#include <tuple>
-
-
-/**
- * @brief used to asynchronously generate the virtual view of the combined data directory
- **/
-class DirectoryRefresher : public QObject
-{
-
- Q_OBJECT
-
-public:
-
- /**
- * @brief constructor
- *
- **/
- DirectoryRefresher();
-
- /**
- * @brief retrieve the updated directory structure
- *
- * returns a pointer to the updated directory structure. DirectoryRefresher
- * deletes its own pointer and the caller takes custody of the pointer
- *
- * @return updated directory structure
- **/
- DirectoryEntry *getDirectoryStructure();
-
- /**
- * @brief sets up the mods to be included in the directory structure
- *
- * @param mods list of the mods to include
- **/
- void setMods(const std::vector<std::tuple<QString, QString, int> > &mods);
-
- /**
- * @brief sets up the directory where mods are stored
- * @param modDirectory the mod directory
- * @note this function could be obsoleted easily by storing absolute paths in the parameter to setMods. This is legacy
- */
- void setModDirectory(const QString &modDirectory);
-
- /**
- * @brief remove files from the directory structure that are known to be irrelevant to the game
- * @param the structure to clean
- */
- static void cleanStructure(DirectoryEntry *structure);
-
- /**
- * @brief add files for a mod to the directory structure, including bsas
- * @param modName
- * @param priorityBSA
- * @param directory
- * @param priorityDir
- */
- static void addModToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory);
-
-public slots:
-
- /**
- * @brief generate a directory structure from the mods set earlier
- **/
- void refresh();
-
-signals:
-
- void progress(int progress);
- void error(const QString &error);
- void refreshed();
-
-private:
-
- std::vector<std::tuple<QString, QString, int> > m_Mods;
- DirectoryEntry *m_DirectoryStructure;
- QMutex m_RefreshLock;
-
-};
-
-#endif // DIRECTORYREFRESHER_H
+#ifndef DIRECTORYREFRESHER_H
+#define DIRECTORYREFRESHER_H
+
+#include <QObject>
+#include <directoryentry.h>
+#include <vector>
+#include <QMutex>
+#include <tuple>
+
+
+/**
+ * @brief used to asynchronously generate the virtual view of the combined data directory
+ **/
+class DirectoryRefresher : public QObject
+{
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief constructor
+ *
+ **/
+ DirectoryRefresher();
+
+ /**
+ * @brief retrieve the updated directory structure
+ *
+ * returns a pointer to the updated directory structure. DirectoryRefresher
+ * deletes its own pointer and the caller takes custody of the pointer
+ *
+ * @return updated directory structure
+ **/
+ MOShared::DirectoryEntry *getDirectoryStructure();
+
+ /**
+ * @brief sets up the mods to be included in the directory structure
+ *
+ * @param mods list of the mods to include
+ **/
+ void setMods(const std::vector<std::tuple<QString, QString, int> > &mods);
+
+ /**
+ * @brief sets up the directory where mods are stored
+ * @param modDirectory the mod directory
+ * @note this function could be obsoleted easily by storing absolute paths in the parameter to setMods. This is legacy
+ */
+ void setModDirectory(const QString &modDirectory);
+
+ /**
+ * @brief remove files from the directory structure that are known to be irrelevant to the game
+ * @param the structure to clean
+ */
+ static void cleanStructure(MOShared::DirectoryEntry *structure);
+
+ /**
+ * @brief add files for a mod to the directory structure, including bsas
+ * @param modName
+ * @param priorityBSA
+ * @param directory
+ * @param priorityDir
+ */
+ static void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory);
+
+public slots:
+
+ /**
+ * @brief generate a directory structure from the mods set earlier
+ **/
+ void refresh();
+
+signals:
+
+ void progress(int progress);
+ void error(const QString &error);
+ void refreshed();
+
+private:
+
+ std::vector<std::tuple<QString, QString, int> > m_Mods;
+ MOShared::DirectoryEntry *m_DirectoryStructure;
+ QMutex m_RefreshLock;
+
+};
+
+#endif // DIRECTORYREFRESHER_H
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index 2e756e5d..6f29bbf1 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -35,6 +35,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using QtJson::Json;
+using namespace MOBase;
// TODO limit number of downloads, also display download during nxm requests, store modid/fileid with downloads
diff --git a/src/dummybsa.cpp b/src/dummybsa.cpp
index 8383a6ad..3357dea6 100644
--- a/src/dummybsa.cpp
+++ b/src/dummybsa.cpp
@@ -17,228 +17,228 @@ 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 "dummybsa.h"
-#include <QFile>
-#include <gameinfo.h>
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-
-
-static void writeUlong(unsigned char* buffer, int offset, unsigned long value)
-{
- union {
- unsigned long ulValue;
- unsigned char cValue[4];
- };
- ulValue = value;
- memcpy(buffer + offset, cValue, 4);
-}
-
-static void writeUlonglong(unsigned char* buffer, int offset, unsigned long long value)
-{
- union {
- unsigned long long ullValue;
- unsigned char cValue[8];
- };
- ullValue = value;
- memcpy(buffer + offset, cValue, 8);
-}
-/*
-static unsigned long long genHashInt(const char* pos, const char* end)
-{
- unsigned long long hash2 = 0;
- for (; pos < end; ++pos) {
- hash2 *= 0x1003f;
- hash2 += *pos;
- }
- return hash2;
-}
-
-
-static unsigned long long genHash(const char* fileName)
-{
- char fileNameLower[MAX_PATH + 1];
- int i = 0;
- for (; i < MAX_PATH && fileName[i] != '\0'; ++i) {
- fileNameLower[i] = tolower(fileName[i]);
- }
- fileNameLower[i] = '\0';
-
- char* ext = strrchr(fileNameLower, '.');
- if (ext == NULL) {
- ext = fileNameLower + strlen(fileNameLower);
- }
-
- int length = ext - fileNameLower;
-
- unsigned long long hash = 0ULL;
-
- if (length > 0) {
- hash = *(ext - 1) |
- ((length > 2 ? *(ext - 2) : 0) << 8) |
- (length << 16) |
- (fileNameLower[0] << 24);
- }
-
- if (strcmp(ext + 1, "dds") == 0) {
- hash |= 0x8080;
- } // the rest is not supported currently
-
- hash |= genHashInt(fileNameLower + 1, ext - 2) << 0x32;
- hash |= genHashInt(ext, ext + strlen(ext)) << 0x32;
-
- return hash;
-}*/
-
-
-static unsigned long genHashInt(const unsigned char *pos, const unsigned char *end)
-{
- unsigned long hash = 0;
- for (; pos < end; ++pos) {
- hash *= 0x1003f;
- hash += *pos;
- }
- return hash;
-}
-
-
-static unsigned long long genHash(const char* fileName)
-{
- char fileNameLower[MAX_PATH + 1];
- int i = 0;
- for (; i < MAX_PATH && fileName[i] != '\0'; ++i) {
- fileNameLower[i] = static_cast<char>(tolower(fileName[i]));
- if (fileNameLower[i] == '\\') {
- fileNameLower[i] = '/';
- }
- }
- fileNameLower[i] = '\0';
-
- unsigned char *fileNameLowerU = reinterpret_cast<unsigned char*>(fileNameLower);
-
- char* ext = strrchr(fileNameLower, '.');
- if (ext == NULL) {
- ext = fileNameLower + strlen(fileNameLower);
- }
- unsigned char *extU = reinterpret_cast<unsigned char*>(ext);
-
- int length = ext - fileNameLower;
-
- unsigned long long hash = 0ULL;
-
- if (length > 0) {
- hash = *(extU - 1) |
- ((length > 2 ? *(ext - 2) : 0) << 8) |
- (length << 16) |
- (fileNameLowerU[0] << 24);
- }
-
- if (strlen(ext) > 0) {
- if (strcmp(ext + 1, "kf") == 0) {
- hash |= 0x80;
- } else if (strcmp(ext + 1, "nif") == 0) {
- hash |= 0x8000;
- } else if (strcmp(ext + 1, "dds") == 0) {
- hash |= 0x8080;
- } else if (strcmp(ext + 1, "wav") == 0) {
- hash |= 0x80000000;
- }
-
- unsigned long long temp = static_cast<unsigned long long>(genHashInt(
- fileNameLowerU + 1, extU - 2));
- temp += static_cast<unsigned long long>(genHashInt(
- extU, extU + strlen(ext)));
-
- hash |= (temp & 0xFFFFFFFF) << 32;
- }
- return hash;
-}
-
-
-DummyBSA::DummyBSA()
- : m_Version(GameInfo::instance().getBSAVersion()), m_FolderName(""), m_FileName("dummy.dds"),
- m_TotalFileNameLength(0)
-{
-}
-
-
-void DummyBSA::writeHeader(QFile& file)
-{
- unsigned char header[] = {
- 'B', 'S', 'A', '\0', // magic string
- 0xDE, 0xAD, 0xBE, 0xEF, // version - insert later
- 0x24, 0x00, 0x00, 0x00, // offset to folder recors. header size is static
- 0xDE, 0xAD, 0xBE, 0xEF, // archive flags - insert later
- 0x01, 0x00, 0x00, 0x00, // folder count
- 0x01, 0x00, 0x00, 0x00, // file count
- 0xDE, 0xAD, 0xBE, 0xEF, // total folder names length - insert later
- 0xDE, 0xAD, 0xBE, 0xEF, // total file names length - insert later
- 0xDE, 0xAD, 0xBE, 0xEF // file flags - insert later
- };
-
- writeUlong(header, 4, GameInfo::instance().getBSAVersion());
- writeUlong(header, 12, 0x01 | 0x02); // has directories and has files.
- writeUlong(header, 24, m_FolderName.length() + 1); // empty folder name
- writeUlong(header, 28, m_TotalFileNameLength); // single character file name
-
- writeUlong(header, 32, 2); // has dds
-
- file.write(reinterpret_cast<char*>(header), sizeof(header));
-}
-
-
-void DummyBSA::writeFolderRecord(QFile& file, const std::string& folderName)
-{
- unsigned char folderRecord[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // folder hash
- 0x01, 0x00, 0x00, 0x00, // file count
- 0xDE, 0xAD, 0xBE, 0xEF, // offset to folder name
- };
- // we'd usually have to sort folders be the hash value generated here
- writeUlonglong(folderRecord, 0, genHash(folderName.c_str()));
- writeUlong( folderRecord, 12, 0x34 + m_TotalFileNameLength); // TODO: this should be calculated properly
-
- file.write(reinterpret_cast<char*>(folderRecord), sizeof(folderRecord));
-}
-
-
-void DummyBSA::writeFileRecord(QFile& file, const std::string& fileName)
-{
- unsigned char fileRecord[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // file name hash
- 0xDE, 0xAD, 0xBE, 0xEF, // size
- 0xDE, 0xAD, 0xBE, 0xEF, // offset to file data
- };
-
- // we'd usually have to sort files by the value generated here
- writeUlonglong(fileRecord, 0, genHash(fileName.c_str()));
- writeUlong( fileRecord, 8, 0);
- writeUlong( fileRecord, 12, 0x44 + (fileName.length() + 1) + 4); // after this record we expect the filename and 4 bytes of file size
-
- file.write(reinterpret_cast<char*>(fileRecord), sizeof(fileRecord));
-}
-
-
-void DummyBSA::writeFileRecordBlocks(QFile& file, const std::string& folderName)
-{
- file.write(folderName.c_str(), folderName.length() + 1);
-
- writeFileRecord(file, m_FileName);
-}
-
-
-void DummyBSA::write(const QString& fileName)
-{
- QFile file(fileName);
- file.open(QIODevice::WriteOnly);
-
- m_TotalFileNameLength = m_FileName.length() + 1;
-
- writeHeader(file);
- writeFolderRecord(file, m_FolderName);
- writeFileRecordBlocks(file, m_FolderName);
- file.write(m_FileName.c_str() , m_FileName.length() + 1);
- char fileSize[] = { 0x00, 0x00, 0x00, 0x00 };
- file.write(fileSize, sizeof(fileSize));
- file.close();
-}
+#include "dummybsa.h"
+#include <QFile>
+#include <gameinfo.h>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+
+static void writeUlong(unsigned char* buffer, int offset, unsigned long value)
+{
+ union {
+ unsigned long ulValue;
+ unsigned char cValue[4];
+ };
+ ulValue = value;
+ memcpy(buffer + offset, cValue, 4);
+}
+
+static void writeUlonglong(unsigned char* buffer, int offset, unsigned long long value)
+{
+ union {
+ unsigned long long ullValue;
+ unsigned char cValue[8];
+ };
+ ullValue = value;
+ memcpy(buffer + offset, cValue, 8);
+}
+/*
+static unsigned long long genHashInt(const char* pos, const char* end)
+{
+ unsigned long long hash2 = 0;
+ for (; pos < end; ++pos) {
+ hash2 *= 0x1003f;
+ hash2 += *pos;
+ }
+ return hash2;
+}
+
+
+static unsigned long long genHash(const char* fileName)
+{
+ char fileNameLower[MAX_PATH + 1];
+ int i = 0;
+ for (; i < MAX_PATH && fileName[i] != '\0'; ++i) {
+ fileNameLower[i] = tolower(fileName[i]);
+ }
+ fileNameLower[i] = '\0';
+
+ char* ext = strrchr(fileNameLower, '.');
+ if (ext == NULL) {
+ ext = fileNameLower + strlen(fileNameLower);
+ }
+
+ int length = ext - fileNameLower;
+
+ unsigned long long hash = 0ULL;
+
+ if (length > 0) {
+ hash = *(ext - 1) |
+ ((length > 2 ? *(ext - 2) : 0) << 8) |
+ (length << 16) |
+ (fileNameLower[0] << 24);
+ }
+
+ if (strcmp(ext + 1, "dds") == 0) {
+ hash |= 0x8080;
+ } // the rest is not supported currently
+
+ hash |= genHashInt(fileNameLower + 1, ext - 2) << 0x32;
+ hash |= genHashInt(ext, ext + strlen(ext)) << 0x32;
+
+ return hash;
+}*/
+
+
+static unsigned long genHashInt(const unsigned char *pos, const unsigned char *end)
+{
+ unsigned long hash = 0;
+ for (; pos < end; ++pos) {
+ hash *= 0x1003f;
+ hash += *pos;
+ }
+ return hash;
+}
+
+
+static unsigned long long genHash(const char* fileName)
+{
+ char fileNameLower[MAX_PATH + 1];
+ int i = 0;
+ for (; i < MAX_PATH && fileName[i] != '\0'; ++i) {
+ fileNameLower[i] = static_cast<char>(tolower(fileName[i]));
+ if (fileNameLower[i] == '\\') {
+ fileNameLower[i] = '/';
+ }
+ }
+ fileNameLower[i] = '\0';
+
+ unsigned char *fileNameLowerU = reinterpret_cast<unsigned char*>(fileNameLower);
+
+ char* ext = strrchr(fileNameLower, '.');
+ if (ext == NULL) {
+ ext = fileNameLower + strlen(fileNameLower);
+ }
+ unsigned char *extU = reinterpret_cast<unsigned char*>(ext);
+
+ int length = ext - fileNameLower;
+
+ unsigned long long hash = 0ULL;
+
+ if (length > 0) {
+ hash = *(extU - 1) |
+ ((length > 2 ? *(ext - 2) : 0) << 8) |
+ (length << 16) |
+ (fileNameLowerU[0] << 24);
+ }
+
+ if (strlen(ext) > 0) {
+ if (strcmp(ext + 1, "kf") == 0) {
+ hash |= 0x80;
+ } else if (strcmp(ext + 1, "nif") == 0) {
+ hash |= 0x8000;
+ } else if (strcmp(ext + 1, "dds") == 0) {
+ hash |= 0x8080;
+ } else if (strcmp(ext + 1, "wav") == 0) {
+ hash |= 0x80000000;
+ }
+
+ unsigned long long temp = static_cast<unsigned long long>(genHashInt(
+ fileNameLowerU + 1, extU - 2));
+ temp += static_cast<unsigned long long>(genHashInt(
+ extU, extU + strlen(ext)));
+
+ hash |= (temp & 0xFFFFFFFF) << 32;
+ }
+ return hash;
+}
+
+
+DummyBSA::DummyBSA()
+ : m_Version(MOShared::GameInfo::instance().getBSAVersion()), m_FolderName(""), m_FileName("dummy.dds"),
+ m_TotalFileNameLength(0)
+{
+}
+
+
+void DummyBSA::writeHeader(QFile& file)
+{
+ unsigned char header[] = {
+ 'B', 'S', 'A', '\0', // magic string
+ 0xDE, 0xAD, 0xBE, 0xEF, // version - insert later
+ 0x24, 0x00, 0x00, 0x00, // offset to folder recors. header size is static
+ 0xDE, 0xAD, 0xBE, 0xEF, // archive flags - insert later
+ 0x01, 0x00, 0x00, 0x00, // folder count
+ 0x01, 0x00, 0x00, 0x00, // file count
+ 0xDE, 0xAD, 0xBE, 0xEF, // total folder names length - insert later
+ 0xDE, 0xAD, 0xBE, 0xEF, // total file names length - insert later
+ 0xDE, 0xAD, 0xBE, 0xEF // file flags - insert later
+ };
+
+ writeUlong(header, 4, MOShared::GameInfo::instance().getBSAVersion());
+ writeUlong(header, 12, 0x01 | 0x02); // has directories and has files.
+ writeUlong(header, 24, m_FolderName.length() + 1); // empty folder name
+ writeUlong(header, 28, m_TotalFileNameLength); // single character file name
+
+ writeUlong(header, 32, 2); // has dds
+
+ file.write(reinterpret_cast<char*>(header), sizeof(header));
+}
+
+
+void DummyBSA::writeFolderRecord(QFile& file, const std::string& folderName)
+{
+ unsigned char folderRecord[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // folder hash
+ 0x01, 0x00, 0x00, 0x00, // file count
+ 0xDE, 0xAD, 0xBE, 0xEF, // offset to folder name
+ };
+ // we'd usually have to sort folders be the hash value generated here
+ writeUlonglong(folderRecord, 0, genHash(folderName.c_str()));
+ writeUlong( folderRecord, 12, 0x34 + m_TotalFileNameLength); // TODO: this should be calculated properly
+
+ file.write(reinterpret_cast<char*>(folderRecord), sizeof(folderRecord));
+}
+
+
+void DummyBSA::writeFileRecord(QFile& file, const std::string& fileName)
+{
+ unsigned char fileRecord[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, // file name hash
+ 0xDE, 0xAD, 0xBE, 0xEF, // size
+ 0xDE, 0xAD, 0xBE, 0xEF, // offset to file data
+ };
+
+ // we'd usually have to sort files by the value generated here
+ writeUlonglong(fileRecord, 0, genHash(fileName.c_str()));
+ writeUlong( fileRecord, 8, 0);
+ writeUlong( fileRecord, 12, 0x44 + (fileName.length() + 1) + 4); // after this record we expect the filename and 4 bytes of file size
+
+ file.write(reinterpret_cast<char*>(fileRecord), sizeof(fileRecord));
+}
+
+
+void DummyBSA::writeFileRecordBlocks(QFile& file, const std::string& folderName)
+{
+ file.write(folderName.c_str(), folderName.length() + 1);
+
+ writeFileRecord(file, m_FileName);
+}
+
+
+void DummyBSA::write(const QString& fileName)
+{
+ QFile file(fileName);
+ file.open(QIODevice::WriteOnly);
+
+ m_TotalFileNameLength = m_FileName.length() + 1;
+
+ writeHeader(file);
+ writeFolderRecord(file, m_FolderName);
+ writeFileRecordBlocks(file, m_FolderName);
+ file.write(m_FileName.c_str() , m_FileName.length() + 1);
+ char fileSize[] = { 0x00, 0x00, 0x00, 0x00 };
+ file.write(fileSize, sizeof(fileSize));
+ file.close();
+}
diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp
index 419d1395..42f29d83 100644
--- a/src/editexecutablesdialog.cpp
+++ b/src/editexecutablesdialog.cpp
@@ -23,6 +23,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QMessageBox>
+using namespace MOBase;
+using namespace MOShared;
+
EditExecutablesDialog::EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent)
: TutorableDialog("EditExecutables", parent),
ui(new Ui::EditExecutablesDialog), m_ExecutablesList(executablesList)
diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h
index fad72dfd..da26b177 100644
--- a/src/editexecutablesdialog.h
+++ b/src/editexecutablesdialog.h
@@ -17,70 +17,70 @@ 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 EDITEXECUTABLESDIALOG_H
-#define EDITEXECUTABLESDIALOG_H
-
-#include "tutorabledialog.h"
-#include <QListWidgetItem>
-#include "executableslist.h"
-
-namespace Ui {
- class EditExecutablesDialog;
-}
-
-/**
- * @brief Dialog to manage the list of executables
- **/
-class EditExecutablesDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
-
- /**
- * @brief constructor
- *
- * @param executablesList current list of executables
- * @param parent parent widget
- **/
- explicit EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent = 0);
-
- ~EditExecutablesDialog();
-
- /**
- * @brief retrieve the updated list of executables
- *
- * @return updated list of executables
- **/
- ExecutablesList getExecutablesList() const;
-
-private slots:
-
- void on_binaryEdit_textChanged(const QString &arg1);
-
- void on_addButton_clicked();
-
- void on_browseButton_clicked();
-
- void on_removeButton_clicked();
-
- void on_titleEdit_textChanged(const QString &arg1);
-
- void on_executablesListBox_itemClicked(QListWidgetItem *item);
-
- void on_overwriteAppIDBox_toggled(bool checked);
-
- void on_browseDirButton_clicked();
-
-private:
-
- void resetInput();
-
- void refreshExecutablesWidget();
-
-private:
- Ui::EditExecutablesDialog *ui;
- ExecutablesList m_ExecutablesList;
-};
-
-#endif // EDITEXECUTABLESDIALOG_H
+#ifndef EDITEXECUTABLESDIALOG_H
+#define EDITEXECUTABLESDIALOG_H
+
+#include "tutorabledialog.h"
+#include <QListWidgetItem>
+#include "executableslist.h"
+
+namespace Ui {
+ class EditExecutablesDialog;
+}
+
+/**
+ * @brief Dialog to manage the list of executables
+ **/
+class EditExecutablesDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief constructor
+ *
+ * @param executablesList current list of executables
+ * @param parent parent widget
+ **/
+ explicit EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent = 0);
+
+ ~EditExecutablesDialog();
+
+ /**
+ * @brief retrieve the updated list of executables
+ *
+ * @return updated list of executables
+ **/
+ ExecutablesList getExecutablesList() const;
+
+private slots:
+
+ void on_binaryEdit_textChanged(const QString &arg1);
+
+ void on_addButton_clicked();
+
+ void on_browseButton_clicked();
+
+ void on_removeButton_clicked();
+
+ void on_titleEdit_textChanged(const QString &arg1);
+
+ void on_executablesListBox_itemClicked(QListWidgetItem *item);
+
+ void on_overwriteAppIDBox_toggled(bool checked);
+
+ void on_browseDirButton_clicked();
+
+private:
+
+ void resetInput();
+
+ void refreshExecutablesWidget();
+
+private:
+ Ui::EditExecutablesDialog *ui;
+ ExecutablesList m_ExecutablesList;
+};
+
+#endif // EDITEXECUTABLESDIALOG_H
diff --git a/src/executableslist.cpp b/src/executableslist.cpp
index 036671f0..4e39c1a3 100644
--- a/src/executableslist.cpp
+++ b/src/executableslist.cpp
@@ -25,6 +25,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <algorithm>
+using namespace MOBase;
+using namespace MOShared;
+
+
QDataStream &operator<<(QDataStream &out, const Executable &obj)
{
out << obj.m_Title << obj.m_BinaryInfo.absoluteFilePath() << obj.m_Arguments << obj.m_CloseMO
diff --git a/src/executableslist.h b/src/executableslist.h
index 7016c560..4c9e662a 100644
--- a/src/executableslist.h
+++ b/src/executableslist.h
@@ -34,7 +34,7 @@ struct Executable {
QString m_Title;
QFileInfo m_BinaryInfo;
QString m_Arguments;
- CloseMOStyle m_CloseMO;
+ MOShared::CloseMOStyle m_CloseMO;
QString m_SteamAppID;
QString m_WorkingDirectory;
@@ -114,7 +114,7 @@ public:
* @param arguments arguments to pass to the executable
* @param closeMO if true, MO will be closed when the binary is started
**/
- void addExecutable(const QString &title, const QString &executableName, const QString &arguments, const QString &workingDirectory, CloseMOStyle closeMO, const QString &steamAppID, bool custom, bool toolbar);
+ void addExecutable(const QString &title, const QString &executableName, const QString &arguments, const QString &workingDirectory, MOShared::CloseMOStyle closeMO, const QString &steamAppID, bool custom, bool toolbar);
/**
* @brief remove the executable with the specified file name. This needs to be an absolute file path
@@ -144,7 +144,7 @@ private:
Executable *findExe(const QString &title);
- void addExecutableInternal(const QString &title, const QString &executableName, const QString &arguments, const QString &workingDirectory, CloseMOStyle closeMO, const QString &steamAppID);
+ void addExecutableInternal(const QString &title, const QString &executableName, const QString &arguments, const QString &workingDirectory, MOShared::CloseMOStyle closeMO, const QString &steamAppID);
private:
diff --git a/src/fomodinstallerdialog.cpp b/src/fomodinstallerdialog.cpp
index bca05567..97476092 100644
--- a/src/fomodinstallerdialog.cpp
+++ b/src/fomodinstallerdialog.cpp
@@ -31,6 +31,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Shellapi.h>
+using namespace MOBase;
+
+
bool ControlsAscending(QAbstractButton *LHS, QAbstractButton *RHS)
{
return LHS->text() < RHS->text();
diff --git a/src/fomodinstallerdialog.h b/src/fomodinstallerdialog.h
index 875146a6..be4e34b2 100644
--- a/src/fomodinstallerdialog.h
+++ b/src/fomodinstallerdialog.h
@@ -98,7 +98,7 @@ public:
* @param tree input tree. (TODO isn't this the same as the tree passed in the constructor?)
* @return DataTree* a new tree with only the selected options and directories arranged correctly. The caller takes custody of this pointer!
**/
- DirectoryTree *updateTree(DirectoryTree *tree);
+ MOBase::DirectoryTree *updateTree(MOBase::DirectoryTree *tree);
protected:
@@ -170,7 +170,7 @@ private:
static PluginType getPluginType(const QString &typeString);
static bool byPriority(const FileDescriptor *LHS, const FileDescriptor *RHS);
- bool copyFileIterator(DirectoryTree *sourceTree, DirectoryTree *destinationTree, FileDescriptor *descriptor);
+ bool copyFileIterator(MOBase::DirectoryTree *sourceTree, MOBase::DirectoryTree *destinationTree, FileDescriptor *descriptor);
void readFileList(QXmlStreamReader &reader, std::vector<FileDescriptor*> &fileList);
void readPluginType(QXmlStreamReader &reader, Plugin &plugin);
void readConditionFlags(QXmlStreamReader &reader, Plugin &plugin);
@@ -190,10 +190,10 @@ private:
bool testVisible(int pageIndex);
bool nextPage();
void activateCurrentPage();
- void moveTree(DirectoryTree::Node *target, DirectoryTree::Node *source);
- DirectoryTree::Node *findNode(DirectoryTree::Node *node, const QString &path, bool create);
- void copyLeaf(DirectoryTree::Node *sourceTree, const QString &sourcePath,
- DirectoryTree::Node *destinationTree, const QString &destinationPath);
+ void moveTree(MOBase::DirectoryTree::Node *target, MOBase::DirectoryTree::Node *source);
+ MOBase::DirectoryTree::Node *findNode(MOBase::DirectoryTree::Node *node, const QString &path, bool create);
+ void copyLeaf(MOBase::DirectoryTree::Node *sourceTree, const QString &sourcePath,
+ MOBase::DirectoryTree::Node *destinationTree, const QString &destinationPath);
private:
diff --git a/src/gameinfoimpl.cpp b/src/gameinfoimpl.cpp
index bb46ee3f..80c46bc8 100644
--- a/src/gameinfoimpl.cpp
+++ b/src/gameinfoimpl.cpp
@@ -17,34 +17,38 @@ 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 "gameinfoimpl.h"
-#include <gameinfo.h>
-#include <utility.h>
-#include <QDir>
-
-
-GameInfoImpl::GameInfoImpl()
-{
-}
-
-IGameInfo::Type GameInfoImpl::type() const
-{
- switch (GameInfo::instance().getType()) {
- case GameInfo::TYPE_OBLIVION: return IGameInfo::TYPE_OBLIVION;
- case GameInfo::TYPE_FALLOUT3: return IGameInfo::TYPE_FALLOUT3;
- case GameInfo::TYPE_FALLOUTNV: return IGameInfo::TYPE_FALLOUTNV;
- case GameInfo::TYPE_SKYRIM: return IGameInfo::TYPE_SKYRIM;
- default: throw MyException(QObject::tr("invalid game type %1").arg(GameInfo::instance().getType()));
- }
-}
-
-
-QString GameInfoImpl::path() const
-{
- return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
-}
-
-QString GameInfoImpl::binaryName() const
-{
- return ToQString(GameInfo::instance().getBinaryName());
-}
+#include "gameinfoimpl.h"
+#include <gameinfo.h>
+#include <utility.h>
+#include <QDir>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+GameInfoImpl::GameInfoImpl()
+{
+}
+
+IGameInfo::Type GameInfoImpl::type() const
+{
+ switch (GameInfo::instance().getType()) {
+ case GameInfo::TYPE_OBLIVION: return IGameInfo::TYPE_OBLIVION;
+ case GameInfo::TYPE_FALLOUT3: return IGameInfo::TYPE_FALLOUT3;
+ case GameInfo::TYPE_FALLOUTNV: return IGameInfo::TYPE_FALLOUTNV;
+ case GameInfo::TYPE_SKYRIM: return IGameInfo::TYPE_SKYRIM;
+ default: throw MyException(QObject::tr("invalid game type %1").arg(GameInfo::instance().getType()));
+ }
+}
+
+
+QString GameInfoImpl::path() const
+{
+ return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+}
+
+QString GameInfoImpl::binaryName() const
+{
+ return ToQString(GameInfo::instance().getBinaryName());
+}
diff --git a/src/gameinfoimpl.h b/src/gameinfoimpl.h
index abde6cbd..372c4c20 100644
--- a/src/gameinfoimpl.h
+++ b/src/gameinfoimpl.h
@@ -17,23 +17,23 @@ 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 GAMEINFOIMPL_H
-#define GAMEINFOIMPL_H
-
-
-#include <igameinfo.h>
-#include <QString>
-
-
-class GameInfoImpl : public IGameInfo
-{
-public:
- GameInfoImpl();
-
- virtual Type type() const;
- virtual QString path() const;
- virtual QString binaryName() const;
-
-};
-
-#endif // GAMEINFOIMPL_H
+#ifndef GAMEINFOIMPL_H
+#define GAMEINFOIMPL_H
+
+
+#include <igameinfo.h>
+#include <QString>
+
+
+class GameInfoImpl : public MOBase::IGameInfo
+{
+public:
+ GameInfoImpl();
+
+ virtual Type type() const;
+ virtual QString path() const;
+ virtual QString binaryName() const;
+
+};
+
+#endif // GAMEINFOIMPL_H
diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp
index a6f062de..b1b7ff6c 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -51,6 +51,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <boost/assign.hpp>
+using namespace MOBase;
+using namespace MOShared;
+
+
typedef Archive* (*CreateArchiveType)();
diff --git a/src/installationmanager.h b/src/installationmanager.h
index 054f584a..7d864647 100644
--- a/src/installationmanager.h
+++ b/src/installationmanager.h
@@ -42,7 +42,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
* All other archives are managed through the manual "InstallDialog"
* @todo this may be a good place to support plugins
**/
-class InstallationManager : public QObject, public IInstallationManager
+class InstallationManager : public QObject, public MOBase::IInstallationManager
{
Q_OBJECT
@@ -82,13 +82,13 @@ public:
**/
static QString getErrorString(Archive::Error errorCode);
- void installManual(DirectoryTree::Node *baseNode, DirectoryTree *filesTree, bool &hasIniTweaks, QString &modName, int categoryID, const QString &modsDirectory, QString newestVersion, QString version, int modID, bool success, bool manualRequest);
+ void installManual(MOBase::DirectoryTree::Node *baseNode, MOBase::DirectoryTree *filesTree, bool &hasIniTweaks, QString &modName, int categoryID, const QString &modsDirectory, QString newestVersion, QString version, int modID, bool success, bool manualRequest);
/**
* @brief register an installer-plugin
* @param the installer to register
*/
- void registerInstaller(IPluginInstaller *installer);
+ void registerInstaller(MOBase::IPluginInstaller *installer);
/**
* @return list of file extensions we can install
@@ -123,7 +123,7 @@ public:
* @param archiveFile path to the archive to install
* @return the installation result
*/
- virtual IPluginInstaller::EInstallResult installArchive(const QString &modName, const QString &archiveName);
+ virtual MOBase::IPluginInstaller::EInstallResult installArchive(const QString &modName, const QString &archiveName);
private:
@@ -134,22 +134,22 @@ private:
void dummyProgressFile(LPCWSTR) {}
- DirectoryTree *createFilesTree();
+ MOBase::DirectoryTree *createFilesTree();
// remap all files in the archive to the directory structure represented by baseNode
// files not present in baseNode are disabled
- void mapToArchive(const DirectoryTree::Node *baseNode);
+ void mapToArchive(const MOBase::DirectoryTree::Node *baseNode);
// recursive worker function for mapToArchive
- void mapToArchive(const DirectoryTree::Node *node, std::wstring path, FileData * const *data);
+ void mapToArchive(const MOBase::DirectoryTree::Node *node, std::wstring path, FileData * const *data);
bool unpackPackageTXT();
bool unpackSingleFile(const QString &fileName);
- bool isSimpleArchiveTopLayer(const DirectoryTree::Node *node, bool bainStyle);
- DirectoryTree::Node *getSimpleArchiveBase(DirectoryTree *dataTree);
- bool checkBainPackage(DirectoryTree *dataTree);
- bool checkFomodPackage(DirectoryTree *dataTree, QString &offset, bool &xmlInstaller);
+ bool isSimpleArchiveTopLayer(const MOBase::DirectoryTree::Node *node, bool bainStyle);
+ MOBase::DirectoryTree::Node *getSimpleArchiveBase(MOBase::DirectoryTree *dataTree);
+ bool checkBainPackage(MOBase::DirectoryTree *dataTree);
+ bool checkFomodPackage(MOBase::DirectoryTree *dataTree, QString &offset, bool &xmlInstaller);
bool checkNMMInstaller();
void fixModName(QString &name);
@@ -160,7 +160,7 @@ private:
int modID, const QString &version, const QString &newestVersion, int categoryID);
bool installFomodExternal(const QString &fileName, const QString &pluginsFileName, const QString &modDirectory);
- bool installFomodInternal(DirectoryTree *&baseNode, const QString &fomodPath, const QString &modsDirectory,
+ bool installFomodInternal(MOBase::DirectoryTree *&baseNode, const QString &fomodPath, const QString &modsDirectory,
int modID, const QString &version, const QString &newestVersion,
int categoryID, QString &modName, bool nameGuessed, bool &manualRequest);
QString generateBackupName(const QString &directoryName);
@@ -174,7 +174,7 @@ private slots:
private:
struct ByPriority {
- bool operator()(IPluginInstaller *LHS, IPluginInstaller *RHS) const
+ bool operator()(MOBase::IPluginInstaller *LHS, MOBase::IPluginInstaller *RHS) const
{
return LHS->priority() > RHS->priority();
}
@@ -191,7 +191,7 @@ private:
QWidget *m_ParentWidget;
- std::vector<IPluginInstaller*> m_Installers;
+ std::vector<MOBase::IPluginInstaller*> m_Installers;
std::set<QString, CaseInsensitive> m_SupportedExtensions;
QString m_NCCPath;
diff --git a/src/installdialog.cpp b/src/installdialog.cpp
index 0ad7dc60..ac508516 100644
--- a/src/installdialog.cpp
+++ b/src/installdialog.cpp
@@ -17,289 +17,292 @@ 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 "installdialog.h"
-#include "ui_installdialog.h"
-
-#include "report.h"
-#include "utility.h"
-#include "installationtester.h"
-
-#include <QMenu>
-#include <QInputDialog>
-#include <QMessageBox>
-
-
-InstallDialog::InstallDialog(DirectoryTree *tree, const QString &modName, QWidget *parent)
- : TutorableDialog("InstallDialog", parent), ui(new Ui::InstallDialog),
- m_DataTree(tree), m_TreeRoot(NULL), m_DataRoot(NULL), m_TreeSelection(NULL),
- m_Updating(false)
-{
- ui->setupUi(this);
-
- QLineEdit *editName = findChild<QLineEdit*>("editName");
- editName->setText(modName);
-
- m_Tree = findChild<ArchiveTree*>("treeContent");
-
- m_ProblemLabel = findChild<QLabel*>("problemLabel");
-
- connect(m_Tree, SIGNAL(changed()), this, SLOT(treeChanged()));
-
- updatePreview();
-}
-
-InstallDialog::~InstallDialog()
-{
- delete ui;
-}
-
-
-QString InstallDialog::getModName() const
-{
- QLineEdit *editName = findChild<QLineEdit*>("editName");
- return editName->text();
-}
-
-
-void InstallDialog::mapDataNode(DirectoryTree::Node *node, QTreeWidgetItem *baseItem) const
-{
- for (int i = 0; i < baseItem->childCount(); ++i) {
- QTreeWidgetItem *currentItem = baseItem->child(i);
-
- if (currentItem->checkState(0) != Qt::Unchecked) {
- if (currentItem->data(0, Qt::UserRole).isNull()) {
- DirectoryTree::Node *newNode = new DirectoryTree::Node;
- newNode->setData(currentItem->text(0));
- mapDataNode(newNode, currentItem);
- node->addNode(newNode, true);
- } else {
- node->addLeaf(FileTreeInformation(currentItem->text(0), currentItem->data(0, Qt::UserRole).toInt()));
- }
- }
- }
-}
-
-
-DirectoryTree *InstallDialog::getDataTree() const
-{
- DirectoryTree *base = new DirectoryTree;
-
- mapDataNode(base, m_Tree->topLevelItem(0));
- return base;
-}
-
-
-QString InstallDialog::getFullPath(const DirectoryTree::Node *node)
-{
- QString result(node->getData().name);
- const DirectoryTree::Node *parent = node->getParent();
- while (parent != NULL) {
- if (parent->getParent() != NULL) {
- result.prepend("\\");
- }
- result.prepend(parent->getData().name);
- parent = parent->getParent();
- }
- return result;
-}
-
-
-void InstallDialog::addDataToTree(DirectoryTree::Node *node, QTreeWidgetItem *treeItem)
-{
- QString path = getFullPath(node);
-
- // add directory elements
- for (DirectoryTree::node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
- QStringList fields((*iter)->getData().name);
- QTreeWidgetItem *newNodeItem = new QTreeWidgetItem(treeItem, fields);
- newNodeItem->setFlags(newNodeItem->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsTristate);
- newNodeItem->setCheckState(0, Qt::Checked);
- addDataToTree(*iter, newNodeItem);
- treeItem->addChild(newNodeItem);
- }
-
- // add file elements
- for (DirectoryTree::leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) {
- QStringList fields(iter->getName());
-
- QTreeWidgetItem *newLeafItem = new QTreeWidgetItem(treeItem, fields);
- newLeafItem->setFlags(newLeafItem->flags() | Qt::ItemIsUserCheckable);
- newLeafItem->setCheckState(0, Qt::Checked);
- if (path.size() != 0) {
- newLeafItem->setToolTip(0, path.mid(0).append("\\").append(iter->getName()));
- } else {
- newLeafItem->setToolTip(0, iter->getName());
- }
- newLeafItem->setData(0, Qt::UserRole, iter->getIndex());
-
- treeItem->addChild(newLeafItem);
- }
-}
-
-
-void InstallDialog::updatePreview()
-{
- m_Updating = true;
- m_Tree->clear();
- delete m_TreeRoot;
-
- m_TreeRoot = new QTreeWidgetItem(m_Tree, QStringList("<data>"));
-
- addDataToTree(m_DataTree, m_TreeRoot);
-
- setDataRoot(m_TreeRoot);
- m_Updating = false;
- updateProblems();
-}
-
-
-bool InstallDialog::testForProblem()
-{
- bool ok = false;
- QTreeWidgetItem *tlWidget = m_Tree->topLevelItem(0);
- for (int i = 0; i < tlWidget->childCount(); ++i) {
- QTreeWidgetItem *widget = tlWidget->child(i);
- if (widget->checkState(0) == Qt::Unchecked) {
- continue;
- }
-
- if (widget->data(0, Qt::UserRole).isValid()) {
- // file
- if (InstallationTester::isTopLevelSuffix(widget->text(0))) {
- ok = true;
- break;
- }
- } else {
- // directory
- if (InstallationTester::isTopLevelDirectory(widget->text(0))) {
- ok = true;
- break;
- }
- }
- }
- return ok;
-}
-
-
-void InstallDialog::updateProblems()
-{
-
- if (testForProblem()) {
- m_ProblemLabel->setText(tr("Looks good"));
- m_ProblemLabel->setToolTip(tr("No problem detected"));
- m_ProblemLabel->setStyleSheet("color: darkGreen;");
- } else {
- m_ProblemLabel->setText(tr("No game data on top level"));
- m_ProblemLabel->setToolTip(tr("There is no esp/esm file or asset directory (textures, meshes, interface, ...) "
- "on the top level."));
- m_ProblemLabel->setStyleSheet("color: red;");
- }
-}
-
-
-void InstallDialog::setDataRoot(QTreeWidgetItem* root)
-{
- if (root != NULL) {
- m_DataRoot = root;
-
- m_Tree->takeTopLevelItem(0);
- QTreeWidgetItem *temp = root->clone();
-// temp->setCheckState(0, Qt::Checked);
- temp->setFlags(temp->flags() & ~(Qt::ItemIsUserCheckable | Qt::ItemIsTristate));
- temp->setText(0, "<data>");
- temp->setData(0, Qt::CheckStateRole, QVariant());
- m_Tree->addTopLevelItem(temp);
- temp->setExpanded(true);
- } else {
- m_Tree->takeTopLevelItem(0);
- }
- updateProblems();
-}
-
-
-void InstallDialog::use_as_data()
-{
- if (m_TreeSelection != NULL) {
- setDataRoot(m_TreeSelection);
- m_TreeSelection = NULL;
- }
- updateProblems();
-}
-
-
-void InstallDialog::unset_data()
-{
- m_TreeSelection = NULL;
-
- setDataRoot(m_TreeRoot);
- updateProblems();
-}
-
-
-void InstallDialog::create_directory()
-{
- bool ok = false;
- QString result = QInputDialog::getText(this, tr("Enter a directory name"), tr("Name"),
- QLineEdit::Normal, QString(), &ok);
- if (ok && !result.isEmpty()) {
- for (int i = 0; i < m_TreeSelection->childCount(); ++i) {
- if (m_TreeSelection->child(i)->text(0) == result) {
- reportError(tr("A directory with that name exists"));
- return;
- }
- }
- QStringList fields(result);
- QTreeWidgetItem *newItem = new QTreeWidgetItem(m_TreeSelection, fields);
- newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
- newItem->setCheckState(0, Qt::Checked);
- m_TreeSelection->addChild(newItem);
- updateProblems();
- }
-}
-
-void InstallDialog::open_file()
-{
- emit openFile(m_TreeSelection->toolTip(0));
-}
-
-
-void InstallDialog::on_treeContent_customContextMenuRequested(QPoint pos)
-{
- m_TreeSelection = m_Tree->itemAt(pos);
- if (m_TreeSelection == 0) {
- return;
- }
-
- QMenu menu;
- menu.addAction(tr("Set data directory"), this, SLOT(use_as_data()));
- menu.addAction(tr("Unset data directory"), this, SLOT(unset_data()));
- if (m_TreeSelection->data(0, Qt::UserRole).isNull()) {
- menu.addAction(tr("Create directory..."), this, SLOT(create_directory()));
- } else {
- menu.addAction(tr("&Open"), this, SLOT(open_file()));
- }
- menu.exec(m_Tree->mapToGlobal(pos));
-}
-
-
-void InstallDialog::treeChanged()
-{
- updateProblems();
-}
-
-
-void InstallDialog::on_okButton_clicked()
-{
- if (!testForProblem()) {
- if (QMessageBox::question(this, tr("Continue?"), tr("This mod was probably NOT set up correctly, most likely it will NOT work. Really continue?"),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
- return;
- }
- }
- this->accept();
-}
-
-void InstallDialog::on_cancelButton_clicked()
-{
- this->reject();
-}
+#include "installdialog.h"
+#include "ui_installdialog.h"
+
+#include "report.h"
+#include "utility.h"
+#include "installationtester.h"
+
+#include <QMenu>
+#include <QInputDialog>
+#include <QMessageBox>
+
+
+using namespace MOBase;
+
+
+InstallDialog::InstallDialog(DirectoryTree *tree, const QString &modName, QWidget *parent)
+ : TutorableDialog("InstallDialog", parent), ui(new Ui::InstallDialog),
+ m_DataTree(tree), m_TreeRoot(NULL), m_DataRoot(NULL), m_TreeSelection(NULL),
+ m_Updating(false)
+{
+ ui->setupUi(this);
+
+ QLineEdit *editName = findChild<QLineEdit*>("editName");
+ editName->setText(modName);
+
+ m_Tree = findChild<ArchiveTree*>("treeContent");
+
+ m_ProblemLabel = findChild<QLabel*>("problemLabel");
+
+ connect(m_Tree, SIGNAL(changed()), this, SLOT(treeChanged()));
+
+ updatePreview();
+}
+
+InstallDialog::~InstallDialog()
+{
+ delete ui;
+}
+
+
+QString InstallDialog::getModName() const
+{
+ QLineEdit *editName = findChild<QLineEdit*>("editName");
+ return editName->text();
+}
+
+
+void InstallDialog::mapDataNode(DirectoryTree::Node *node, QTreeWidgetItem *baseItem) const
+{
+ for (int i = 0; i < baseItem->childCount(); ++i) {
+ QTreeWidgetItem *currentItem = baseItem->child(i);
+
+ if (currentItem->checkState(0) != Qt::Unchecked) {
+ if (currentItem->data(0, Qt::UserRole).isNull()) {
+ DirectoryTree::Node *newNode = new DirectoryTree::Node;
+ newNode->setData(currentItem->text(0));
+ mapDataNode(newNode, currentItem);
+ node->addNode(newNode, true);
+ } else {
+ node->addLeaf(FileTreeInformation(currentItem->text(0), currentItem->data(0, Qt::UserRole).toInt()));
+ }
+ }
+ }
+}
+
+
+DirectoryTree *InstallDialog::getDataTree() const
+{
+ DirectoryTree *base = new DirectoryTree;
+
+ mapDataNode(base, m_Tree->topLevelItem(0));
+ return base;
+}
+
+
+QString InstallDialog::getFullPath(const DirectoryTree::Node *node)
+{
+ QString result(node->getData().name);
+ const DirectoryTree::Node *parent = node->getParent();
+ while (parent != NULL) {
+ if (parent->getParent() != NULL) {
+ result.prepend("\\");
+ }
+ result.prepend(parent->getData().name);
+ parent = parent->getParent();
+ }
+ return result;
+}
+
+
+void InstallDialog::addDataToTree(DirectoryTree::Node *node, QTreeWidgetItem *treeItem)
+{
+ QString path = getFullPath(node);
+
+ // add directory elements
+ for (DirectoryTree::node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
+ QStringList fields((*iter)->getData().name);
+ QTreeWidgetItem *newNodeItem = new QTreeWidgetItem(treeItem, fields);
+ newNodeItem->setFlags(newNodeItem->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsTristate);
+ newNodeItem->setCheckState(0, Qt::Checked);
+ addDataToTree(*iter, newNodeItem);
+ treeItem->addChild(newNodeItem);
+ }
+
+ // add file elements
+ for (DirectoryTree::leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) {
+ QStringList fields(iter->getName());
+
+ QTreeWidgetItem *newLeafItem = new QTreeWidgetItem(treeItem, fields);
+ newLeafItem->setFlags(newLeafItem->flags() | Qt::ItemIsUserCheckable);
+ newLeafItem->setCheckState(0, Qt::Checked);
+ if (path.size() != 0) {
+ newLeafItem->setToolTip(0, path.mid(0).append("\\").append(iter->getName()));
+ } else {
+ newLeafItem->setToolTip(0, iter->getName());
+ }
+ newLeafItem->setData(0, Qt::UserRole, iter->getIndex());
+
+ treeItem->addChild(newLeafItem);
+ }
+}
+
+
+void InstallDialog::updatePreview()
+{
+ m_Updating = true;
+ m_Tree->clear();
+ delete m_TreeRoot;
+
+ m_TreeRoot = new QTreeWidgetItem(m_Tree, QStringList("<data>"));
+
+ addDataToTree(m_DataTree, m_TreeRoot);
+
+ setDataRoot(m_TreeRoot);
+ m_Updating = false;
+ updateProblems();
+}
+
+
+bool InstallDialog::testForProblem()
+{
+ bool ok = false;
+ QTreeWidgetItem *tlWidget = m_Tree->topLevelItem(0);
+ for (int i = 0; i < tlWidget->childCount(); ++i) {
+ QTreeWidgetItem *widget = tlWidget->child(i);
+ if (widget->checkState(0) == Qt::Unchecked) {
+ continue;
+ }
+
+ if (widget->data(0, Qt::UserRole).isValid()) {
+ // file
+ if (InstallationTester::isTopLevelSuffix(widget->text(0))) {
+ ok = true;
+ break;
+ }
+ } else {
+ // directory
+ if (InstallationTester::isTopLevelDirectory(widget->text(0))) {
+ ok = true;
+ break;
+ }
+ }
+ }
+ return ok;
+}
+
+
+void InstallDialog::updateProblems()
+{
+
+ if (testForProblem()) {
+ m_ProblemLabel->setText(tr("Looks good"));
+ m_ProblemLabel->setToolTip(tr("No problem detected"));
+ m_ProblemLabel->setStyleSheet("color: darkGreen;");
+ } else {
+ m_ProblemLabel->setText(tr("No game data on top level"));
+ m_ProblemLabel->setToolTip(tr("There is no esp/esm file or asset directory (textures, meshes, interface, ...) "
+ "on the top level."));
+ m_ProblemLabel->setStyleSheet("color: red;");
+ }
+}
+
+
+void InstallDialog::setDataRoot(QTreeWidgetItem* root)
+{
+ if (root != NULL) {
+ m_DataRoot = root;
+
+ m_Tree->takeTopLevelItem(0);
+ QTreeWidgetItem *temp = root->clone();
+// temp->setCheckState(0, Qt::Checked);
+ temp->setFlags(temp->flags() & ~(Qt::ItemIsUserCheckable | Qt::ItemIsTristate));
+ temp->setText(0, "<data>");
+ temp->setData(0, Qt::CheckStateRole, QVariant());
+ m_Tree->addTopLevelItem(temp);
+ temp->setExpanded(true);
+ } else {
+ m_Tree->takeTopLevelItem(0);
+ }
+ updateProblems();
+}
+
+
+void InstallDialog::use_as_data()
+{
+ if (m_TreeSelection != NULL) {
+ setDataRoot(m_TreeSelection);
+ m_TreeSelection = NULL;
+ }
+ updateProblems();
+}
+
+
+void InstallDialog::unset_data()
+{
+ m_TreeSelection = NULL;
+
+ setDataRoot(m_TreeRoot);
+ updateProblems();
+}
+
+
+void InstallDialog::create_directory()
+{
+ bool ok = false;
+ QString result = QInputDialog::getText(this, tr("Enter a directory name"), tr("Name"),
+ QLineEdit::Normal, QString(), &ok);
+ if (ok && !result.isEmpty()) {
+ for (int i = 0; i < m_TreeSelection->childCount(); ++i) {
+ if (m_TreeSelection->child(i)->text(0) == result) {
+ reportError(tr("A directory with that name exists"));
+ return;
+ }
+ }
+ QStringList fields(result);
+ QTreeWidgetItem *newItem = new QTreeWidgetItem(m_TreeSelection, fields);
+ newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
+ newItem->setCheckState(0, Qt::Checked);
+ m_TreeSelection->addChild(newItem);
+ updateProblems();
+ }
+}
+
+void InstallDialog::open_file()
+{
+ emit openFile(m_TreeSelection->toolTip(0));
+}
+
+
+void InstallDialog::on_treeContent_customContextMenuRequested(QPoint pos)
+{
+ m_TreeSelection = m_Tree->itemAt(pos);
+ if (m_TreeSelection == 0) {
+ return;
+ }
+
+ QMenu menu;
+ menu.addAction(tr("Set data directory"), this, SLOT(use_as_data()));
+ menu.addAction(tr("Unset data directory"), this, SLOT(unset_data()));
+ if (m_TreeSelection->data(0, Qt::UserRole).isNull()) {
+ menu.addAction(tr("Create directory..."), this, SLOT(create_directory()));
+ } else {
+ menu.addAction(tr("&Open"), this, SLOT(open_file()));
+ }
+ menu.exec(m_Tree->mapToGlobal(pos));
+}
+
+
+void InstallDialog::treeChanged()
+{
+ updateProblems();
+}
+
+
+void InstallDialog::on_okButton_clicked()
+{
+ if (!testForProblem()) {
+ if (QMessageBox::question(this, tr("Continue?"), tr("This mod was probably NOT set up correctly, most likely it will NOT work. Really continue?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
+ return;
+ }
+ }
+ this->accept();
+}
+
+void InstallDialog::on_cancelButton_clicked()
+{
+ this->reject();
+}
diff --git a/src/installdialog.h b/src/installdialog.h
index 14c9d733..e44d7eee 100644
--- a/src/installdialog.h
+++ b/src/installdialog.h
@@ -17,111 +17,111 @@ 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 INSTALLDIALOG_H
-#define INSTALLDIALOG_H
-
-#include "mytree.h"
-#include "archivetree.h"
-#include "tutorabledialog.h"
-#include <QDialog>
-#include <QUuid>
-#include <QTreeWidgetItem>
-#include <QProgressDialog>
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-#include <archive.h>
-#include <directorytree.h>
-
-
-namespace Ui {
- class InstallDialog;
-}
-
-
-/**
- * a dialog presented to manually define how a mod is to be installed. It provides
- * a tree view of the file contents that can modified directly
- **/
-class InstallDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
- /**
- * @brief constructor
- *
- * @param tree tree structure describing the vanilla archive structure. The InstallDialog does NOT take custody of this pointer!
- * @param modName name of the mod. The name can be modified through the dialog
- * @param parent parent widget
- **/
- explicit InstallDialog(DirectoryTree *tree, const QString &modName, QWidget *parent = 0);
- ~InstallDialog();
-
- /**
- * @brief retrieve the (modified) mod name
- *
- * @return updated mod name
- **/
- QString getModName() const;
-
- /**
- * @brief retrieve the user-modified directory structure
- *
- * @return modified data structure. This is a NEW datatree object for which the caller takes custody
- **/
- DirectoryTree *getDataTree() const;
-
-signals:
-
- void openFile(const QString fileName);
-
-private:
-
- void updatePreview();
- bool testForProblem();
- void updateProblems();
-
- void setDataRoot(QTreeWidgetItem* root);
-
- void updateFileList(QTreeWidgetItem *item, QString targetName, FileData* const *fileData, size_t size) const;
-
- void updateCheckState(QTreeWidgetItem *item);
-// void recursiveCheck(QTreeWidgetItem *item);
-// void recursiveUncheck(QTreeWidgetItem *item);
-
- void addDataToTree(DirectoryTree::Node *node, QTreeWidgetItem *treeItem);
-
- void mapDataNode(DirectoryTree::Node *node, QTreeWidgetItem *baseItem) const;
-
- static QString getFullPath(const DirectoryTree::Node *node);
-
-private slots:
-
- void on_treeContent_customContextMenuRequested(QPoint pos);
-
- void unset_data();
- void use_as_data();
- void create_directory();
- void open_file();
-
- void treeChanged();
-
- void on_cancelButton_clicked();
-
- void on_okButton_clicked();
-
-private:
- Ui::InstallDialog *ui;
-
- DirectoryTree *m_DataTree;
-
- ArchiveTree *m_Tree;
- QLabel *m_ProblemLabel;
- QTreeWidgetItem *m_TreeRoot;
- QTreeWidgetItem *m_DataRoot;
- QTreeWidgetItem *m_TreeSelection;
- bool m_Updating;
-
-};
-
-#endif // INSTALLDIALOG_H
+#ifndef INSTALLDIALOG_H
+#define INSTALLDIALOG_H
+
+#include "mytree.h"
+#include "archivetree.h"
+#include "tutorabledialog.h"
+#include <QDialog>
+#include <QUuid>
+#include <QTreeWidgetItem>
+#include <QProgressDialog>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <archive.h>
+#include <directorytree.h>
+
+
+namespace Ui {
+ class InstallDialog;
+}
+
+
+/**
+ * a dialog presented to manually define how a mod is to be installed. It provides
+ * a tree view of the file contents that can modified directly
+ **/
+class InstallDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @brief constructor
+ *
+ * @param tree tree structure describing the vanilla archive structure. The InstallDialog does NOT take custody of this pointer!
+ * @param modName name of the mod. The name can be modified through the dialog
+ * @param parent parent widget
+ **/
+ explicit InstallDialog(MOBase::DirectoryTree *tree, const QString &modName, QWidget *parent = 0);
+ ~InstallDialog();
+
+ /**
+ * @brief retrieve the (modified) mod name
+ *
+ * @return updated mod name
+ **/
+ QString getModName() const;
+
+ /**
+ * @brief retrieve the user-modified directory structure
+ *
+ * @return modified data structure. This is a NEW datatree object for which the caller takes custody
+ **/
+ MOBase::DirectoryTree *getDataTree() const;
+
+signals:
+
+ void openFile(const QString fileName);
+
+private:
+
+ void updatePreview();
+ bool testForProblem();
+ void updateProblems();
+
+ void setDataRoot(QTreeWidgetItem* root);
+
+ void updateFileList(QTreeWidgetItem *item, QString targetName, FileData* const *fileData, size_t size) const;
+
+ void updateCheckState(QTreeWidgetItem *item);
+// void recursiveCheck(QTreeWidgetItem *item);
+// void recursiveUncheck(QTreeWidgetItem *item);
+
+ void addDataToTree(MOBase::DirectoryTree::Node *node, QTreeWidgetItem *treeItem);
+
+ void mapDataNode(MOBase::DirectoryTree::Node *node, QTreeWidgetItem *baseItem) const;
+
+ static QString getFullPath(const MOBase::DirectoryTree::Node *node);
+
+private slots:
+
+ void on_treeContent_customContextMenuRequested(QPoint pos);
+
+ void unset_data();
+ void use_as_data();
+ void create_directory();
+ void open_file();
+
+ void treeChanged();
+
+ void on_cancelButton_clicked();
+
+ void on_okButton_clicked();
+
+private:
+ Ui::InstallDialog *ui;
+
+ MOBase::DirectoryTree *m_DataTree;
+
+ ArchiveTree *m_Tree;
+ QLabel *m_ProblemLabel;
+ QTreeWidgetItem *m_TreeRoot;
+ QTreeWidgetItem *m_DataRoot;
+ QTreeWidgetItem *m_TreeSelection;
+ bool m_Updating;
+
+};
+
+#endif // INSTALLDIALOG_H
diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp
index d10eca47..8eed460f 100644
--- a/src/loadmechanism.cpp
+++ b/src/loadmechanism.cpp
@@ -17,269 +17,273 @@ 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 "loadmechanism.h"
-#include "utility.h"
-#include "util.h"
-#include <gameinfo.h>
-#include <appconfig.h>
-#include <QFile>
-#include <QFileInfo>
-#include <QDir>
-#include <QString>
-#include <QMessageBox>
-#include <QCryptographicHash>
-
-
-LoadMechanism::LoadMechanism()
- : m_SelectedMechanism(LOAD_MODORGANIZER)
-{
-}
-
-void LoadMechanism::writeHintFile(const QDir &targetDirectory)
-{
- QString hintFilePath = targetDirectory.absoluteFilePath("mo_path.txt");
- QFile hintFile(hintFilePath);
- if (hintFile.exists()) {
- hintFile.remove();
- }
- if (!hintFile.open(QIODevice::WriteOnly)) {
- throw MyException(QObject::tr("failed to open %1: %2").arg(hintFilePath).arg(hintFile.errorString()));
- }
- hintFile.write(ToString(GameInfo::instance().getOrganizerDirectory(), true).c_str());
- hintFile.close();
-}
-
-
-void LoadMechanism::removeHintFile(QDir &targetDirectory)
-{
- targetDirectory.remove("mo_path.txt");
-}
-
-
-bool LoadMechanism::isDirectLoadingSupported()
-{
- if (GameInfo::instance().getType() == GameInfo::TYPE_OBLIVION) {
- // oblivion can be loaded directly if it's not the steam variant
- QString gamePath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
- QDir gameDir(gamePath);
- // test if this is the steam version of oblivion
- if (QFile(gameDir.absoluteFilePath("steam_api.dll")).exists()) {
- return false;
- }
- }
-
- // all other games work afaik
- return true;
-}
-
-
-bool LoadMechanism::isScriptExtenderSupported()
-{
- QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
- if ((QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_loader.exe")).exists()) ||
- (QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_steam_loader.dll")).exists())) {
- return true;
- } else {
- return false;
- }
-}
-
-
-bool LoadMechanism::isProxyDLLSupported()
-{
- QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
- targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
- return QFile(targetPath).exists();
-}
-
-
-bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fileNameRHS)
-{
- QFile fileLHS(fileNameLHS);
- if (!fileLHS.open(QIODevice::ReadOnly)) {
- throw MyException(QObject::tr("%1 not found").arg(fileNameLHS));
- }
- QByteArray dataLHS = fileLHS.readAll();
- QByteArray hashLHS = QCryptographicHash::hash(dataLHS, QCryptographicHash::Md5);
-
- fileLHS.close();
-
- QFile fileRHS(fileNameRHS);
- if (!fileRHS.open(QIODevice::ReadOnly)) {
- throw MyException(QObject::tr("%1 not found").arg(fileNameRHS));
- }
- QByteArray dataRHS = fileRHS.readAll();
- QByteArray hashRHS = QCryptographicHash::hash(dataRHS, QCryptographicHash::Md5);
-
- fileRHS.close();
-
- return hashLHS == hashRHS;
-}
-
-
-void LoadMechanism::deactivateScriptExtender()
-{
- try {
- QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
-
- QString pluginsDirPath = gameDirectory;
- pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins");
-
- QDir pluginsDir(pluginsDirPath);
-
- QString hookDLLName = ToQString(AppConfig::hookDLLName());
- if (QFile(pluginsDir.absoluteFilePath(hookDLLName)).exists()) {
- // remove dll from SE plugins directory
- if (!pluginsDir.remove(hookDLLName)) {
- throw MyException(QObject::tr("Failed to delete %1").arg(pluginsDir.absoluteFilePath(hookDLLName)));
- }
- }
-
- removeHintFile(pluginsDir);
- } catch (const std::exception &e) {
- QMessageBox::critical(NULL, QObject::tr("Failed to deactivate script extender loading"), e.what());
- }
-}
-
-
-void LoadMechanism::deactivateProxyDLL()
-{
- try {
- QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
-
- QString targetPath = gameDirectory;
- targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
-
- QFile targetDLL(targetPath);
- if (targetDLL.exists()) {
- QString origFile = gameDirectory.mid(0).append(ToQString(AppConfig::proxyDLLOrig()));
- // determine if a proxy-dll is installed
- // this is a very crude way of making this decision but it should be good enough
- if ((targetDLL.size() < 24576) && (QFile(origFile).exists())) {
- // remove proxy-dll
- if (!targetDLL.remove()) {
- throw MyException(QObject::tr("Failed to remove %1: %2").arg(targetPath).arg(targetDLL.errorString()));
- } else if (!QFile::rename(origFile, targetPath)) {
- throw MyException(QObject::tr("Failed to rename %1 to %2").arg(origFile, targetPath));
- }
- }
- }
-
- removeHintFile(QDir(gameDirectory));
- } catch (const std::exception &e) {
- QMessageBox::critical(NULL, QObject::tr("Failed to deactivate proxy-dll loading"), e.what());
- }
-}
-
-
-void LoadMechanism::activateScriptExtender()
-{
- try {
- QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
-
- QString pluginsDirPath = gameDirectory;
- pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins");
-
- QDir pluginsDir(pluginsDirPath);
-
- if (!pluginsDir.exists()) {
- pluginsDir.mkpath(".");
- }
-
- QString targetPath = pluginsDir.absoluteFilePath(ToQString(AppConfig::hookDLLName()));
- QString hookDLLPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::hookDLLName()));
-
- QFile dllFile(targetPath);
-
- if (dllFile.exists()) {
- // may be outdated
- if (!hashIdentical(targetPath, hookDLLPath)) {
- dllFile.remove();
- }
- }
-
- if (!dllFile.exists()) {
- // install dll to SE plugins
- if (!QFile::copy(hookDLLPath, targetPath)) {
- throw MyException(QObject::tr("Failed to copy %1 to %2").arg(hookDLLPath, targetPath));
- }
- }
- writeHintFile(pluginsDir);
- } catch (const std::exception &e) {
- QMessageBox::critical(NULL, QObject::tr("Failed to set up script extender loading"), e.what());
- }
-}
-
-
-void LoadMechanism::activateProxyDLL()
-{
- try {
- QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
-
- QString targetPath = gameDirectory;
- targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
- QFile targetDLL(targetPath);
- if (!targetDLL.exists()) {
- return;
- }
-
- QString sourcePath = QDir::fromNativeSeparators(
- ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::proxyDLLSource()));
-
- // this is a very crude way of making this decision but it should be good enough
- if (targetDLL.size() < 24576) {
- // determine if a proxy-dll is already installed and if so, if it's the right one
- if (!hashIdentical(targetPath, sourcePath)) {
- // wrong proxy dll, probably outdated. delete and install the new one
- if (!QFile::remove(targetPath)) {
- throw MyException(QObject::tr("Failed to delete old proxy-dll %1").arg(targetPath));
- }
- if (!QFile::copy(sourcePath, targetPath)) {
- throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
- }
- } // otherwise the proxy-dll is already the right one
- } else {
- // no proxy dll installed yet. move the original and insert proxy-dll
-
- QString origFile = gameDirectory;
- origFile.append("/").append(ToQString(AppConfig::proxyDLLOrig()));
-
- if (QFile(origFile).exists()) {
- // orig-file exists. this may happen if the steam-api was updated or the user messed with the
- // dlls.
- if (!QFile::remove(origFile)) {
- throw MyException(QObject::tr("Failed to overwrite %1").arg(origFile));
- }
- }
- if (!QFile::rename(targetPath, origFile)) {
- throw MyException(QObject::tr("Failed to rename %1 to %2").arg(targetPath).arg(origFile));
- }
- if (!QFile::copy(sourcePath, targetPath)) {
- throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
- }
- }
- writeHintFile(QDir(gameDirectory));
- } catch (const std::exception &e) {
- QMessageBox::critical(NULL, QObject::tr("Failed to set up proxy-dll loading"), e.what());
- }
-}
-
-
-void LoadMechanism::activate(EMechanism mechanism)
-{
- switch (mechanism) {
- case LOAD_MODORGANIZER: {
- deactivateProxyDLL();
- deactivateScriptExtender();
- } break;
- case LOAD_SCRIPTEXTENDER: {
- deactivateProxyDLL();
- activateScriptExtender();
- } break;
- case LOAD_PROXYDLL: {
- deactivateScriptExtender();
- activateProxyDLL();
- } break;
- }
-}
-
+#include "loadmechanism.h"
+#include "utility.h"
+#include "util.h"
+#include <gameinfo.h>
+#include <appconfig.h>
+#include <QFile>
+#include <QFileInfo>
+#include <QDir>
+#include <QString>
+#include <QMessageBox>
+#include <QCryptographicHash>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+LoadMechanism::LoadMechanism()
+ : m_SelectedMechanism(LOAD_MODORGANIZER)
+{
+}
+
+void LoadMechanism::writeHintFile(const QDir &targetDirectory)
+{
+ QString hintFilePath = targetDirectory.absoluteFilePath("mo_path.txt");
+ QFile hintFile(hintFilePath);
+ if (hintFile.exists()) {
+ hintFile.remove();
+ }
+ if (!hintFile.open(QIODevice::WriteOnly)) {
+ throw MyException(QObject::tr("failed to open %1: %2").arg(hintFilePath).arg(hintFile.errorString()));
+ }
+ hintFile.write(ToString(GameInfo::instance().getOrganizerDirectory(), true).c_str());
+ hintFile.close();
+}
+
+
+void LoadMechanism::removeHintFile(QDir &targetDirectory)
+{
+ targetDirectory.remove("mo_path.txt");
+}
+
+
+bool LoadMechanism::isDirectLoadingSupported()
+{
+ if (GameInfo::instance().getType() == GameInfo::TYPE_OBLIVION) {
+ // oblivion can be loaded directly if it's not the steam variant
+ QString gamePath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+ QDir gameDir(gamePath);
+ // test if this is the steam version of oblivion
+ if (QFile(gameDir.absoluteFilePath("steam_api.dll")).exists()) {
+ return false;
+ }
+ }
+
+ // all other games work afaik
+ return true;
+}
+
+
+bool LoadMechanism::isScriptExtenderSupported()
+{
+ QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+ if ((QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_loader.exe")).exists()) ||
+ (QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_steam_loader.dll")).exists())) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+bool LoadMechanism::isProxyDLLSupported()
+{
+ QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+ targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
+ return QFile(targetPath).exists();
+}
+
+
+bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fileNameRHS)
+{
+ QFile fileLHS(fileNameLHS);
+ if (!fileLHS.open(QIODevice::ReadOnly)) {
+ throw MyException(QObject::tr("%1 not found").arg(fileNameLHS));
+ }
+ QByteArray dataLHS = fileLHS.readAll();
+ QByteArray hashLHS = QCryptographicHash::hash(dataLHS, QCryptographicHash::Md5);
+
+ fileLHS.close();
+
+ QFile fileRHS(fileNameRHS);
+ if (!fileRHS.open(QIODevice::ReadOnly)) {
+ throw MyException(QObject::tr("%1 not found").arg(fileNameRHS));
+ }
+ QByteArray dataRHS = fileRHS.readAll();
+ QByteArray hashRHS = QCryptographicHash::hash(dataRHS, QCryptographicHash::Md5);
+
+ fileRHS.close();
+
+ return hashLHS == hashRHS;
+}
+
+
+void LoadMechanism::deactivateScriptExtender()
+{
+ try {
+ QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+
+ QString pluginsDirPath = gameDirectory;
+ pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins");
+
+ QDir pluginsDir(pluginsDirPath);
+
+ QString hookDLLName = ToQString(AppConfig::hookDLLName());
+ if (QFile(pluginsDir.absoluteFilePath(hookDLLName)).exists()) {
+ // remove dll from SE plugins directory
+ if (!pluginsDir.remove(hookDLLName)) {
+ throw MyException(QObject::tr("Failed to delete %1").arg(pluginsDir.absoluteFilePath(hookDLLName)));
+ }
+ }
+
+ removeHintFile(pluginsDir);
+ } catch (const std::exception &e) {
+ QMessageBox::critical(NULL, QObject::tr("Failed to deactivate script extender loading"), e.what());
+ }
+}
+
+
+void LoadMechanism::deactivateProxyDLL()
+{
+ try {
+ QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+
+ QString targetPath = gameDirectory;
+ targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
+
+ QFile targetDLL(targetPath);
+ if (targetDLL.exists()) {
+ QString origFile = gameDirectory.mid(0).append(ToQString(AppConfig::proxyDLLOrig()));
+ // determine if a proxy-dll is installed
+ // this is a very crude way of making this decision but it should be good enough
+ if ((targetDLL.size() < 24576) && (QFile(origFile).exists())) {
+ // remove proxy-dll
+ if (!targetDLL.remove()) {
+ throw MyException(QObject::tr("Failed to remove %1: %2").arg(targetPath).arg(targetDLL.errorString()));
+ } else if (!QFile::rename(origFile, targetPath)) {
+ throw MyException(QObject::tr("Failed to rename %1 to %2").arg(origFile, targetPath));
+ }
+ }
+ }
+
+ removeHintFile(QDir(gameDirectory));
+ } catch (const std::exception &e) {
+ QMessageBox::critical(NULL, QObject::tr("Failed to deactivate proxy-dll loading"), e.what());
+ }
+}
+
+
+void LoadMechanism::activateScriptExtender()
+{
+ try {
+ QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+
+ QString pluginsDirPath = gameDirectory;
+ pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins");
+
+ QDir pluginsDir(pluginsDirPath);
+
+ if (!pluginsDir.exists()) {
+ pluginsDir.mkpath(".");
+ }
+
+ QString targetPath = pluginsDir.absoluteFilePath(ToQString(AppConfig::hookDLLName()));
+ QString hookDLLPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::hookDLLName()));
+
+ QFile dllFile(targetPath);
+
+ if (dllFile.exists()) {
+ // may be outdated
+ if (!hashIdentical(targetPath, hookDLLPath)) {
+ dllFile.remove();
+ }
+ }
+
+ if (!dllFile.exists()) {
+ // install dll to SE plugins
+ if (!QFile::copy(hookDLLPath, targetPath)) {
+ throw MyException(QObject::tr("Failed to copy %1 to %2").arg(hookDLLPath, targetPath));
+ }
+ }
+ writeHintFile(pluginsDir);
+ } catch (const std::exception &e) {
+ QMessageBox::critical(NULL, QObject::tr("Failed to set up script extender loading"), e.what());
+ }
+}
+
+
+void LoadMechanism::activateProxyDLL()
+{
+ try {
+ QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
+
+ QString targetPath = gameDirectory;
+ targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
+ QFile targetDLL(targetPath);
+ if (!targetDLL.exists()) {
+ return;
+ }
+
+ QString sourcePath = QDir::fromNativeSeparators(
+ ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::proxyDLLSource()));
+
+ // this is a very crude way of making this decision but it should be good enough
+ if (targetDLL.size() < 24576) {
+ // determine if a proxy-dll is already installed and if so, if it's the right one
+ if (!hashIdentical(targetPath, sourcePath)) {
+ // wrong proxy dll, probably outdated. delete and install the new one
+ if (!QFile::remove(targetPath)) {
+ throw MyException(QObject::tr("Failed to delete old proxy-dll %1").arg(targetPath));
+ }
+ if (!QFile::copy(sourcePath, targetPath)) {
+ throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
+ }
+ } // otherwise the proxy-dll is already the right one
+ } else {
+ // no proxy dll installed yet. move the original and insert proxy-dll
+
+ QString origFile = gameDirectory;
+ origFile.append("/").append(ToQString(AppConfig::proxyDLLOrig()));
+
+ if (QFile(origFile).exists()) {
+ // orig-file exists. this may happen if the steam-api was updated or the user messed with the
+ // dlls.
+ if (!QFile::remove(origFile)) {
+ throw MyException(QObject::tr("Failed to overwrite %1").arg(origFile));
+ }
+ }
+ if (!QFile::rename(targetPath, origFile)) {
+ throw MyException(QObject::tr("Failed to rename %1 to %2").arg(targetPath).arg(origFile));
+ }
+ if (!QFile::copy(sourcePath, targetPath)) {
+ throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
+ }
+ }
+ writeHintFile(QDir(gameDirectory));
+ } catch (const std::exception &e) {
+ QMessageBox::critical(NULL, QObject::tr("Failed to set up proxy-dll loading"), e.what());
+ }
+}
+
+
+void LoadMechanism::activate(EMechanism mechanism)
+{
+ switch (mechanism) {
+ case LOAD_MODORGANIZER: {
+ deactivateProxyDLL();
+ deactivateScriptExtender();
+ } break;
+ case LOAD_SCRIPTEXTENDER: {
+ deactivateProxyDLL();
+ activateScriptExtender();
+ } break;
+ case LOAD_PROXYDLL: {
+ deactivateScriptExtender();
+ activateProxyDLL();
+ } break;
+ }
+}
+
diff --git a/src/main.cpp b/src/main.cpp
index d452c3b8..af6649b2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -66,6 +66,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#pragma comment(linker, "/manifestDependency:\"name='dlls' processorArchitecture='x86' version='1.0.0.0' type='win32' \"")
+using namespace MOBase;
+using namespace MOShared;
+
+
void removeOldLogfiles()
{
QFileInfoList files = QDir(ToQString(GameInfo::instance().getLogDir())).entryInfoList(QStringList("ModOrganizer*.log"),
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index dd217447..5a8211c8 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -91,6 +91,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <TlHelp32.h>
+using namespace MOBase;
+using namespace MOShared;
+
MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow), m_Tutorial(this, "MainWindow"),
@@ -2883,7 +2886,7 @@ void MainWindow::fixMods_clicked()
selectedItem->setData(Qt::UserRole, temp);
}
- const SaveGameGamebryo *save = qvariant_cast<SaveGameGamebryo*>(selectedItem->data(Qt::UserRole));
+ const SaveGameGamebryo *save = getSaveGame(selectedItem);
// collect the list of missing plugins
std::map<QString, std::vector<QString> > missingPlugins;
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 1d6508be..26e4c9dc 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -57,7 +57,7 @@ namespace Ui {
class QToolButton;
-class MainWindow : public QMainWindow, public IOrganizer
+class MainWindow : public QMainWindow, public MOBase::IOrganizer
{
Q_OBJECT
@@ -91,14 +91,14 @@ public:
void loadPlugins();
- virtual IGameInfo &gameInfo() const;
+ virtual MOBase::IGameInfo &gameInfo() const;
virtual QString profileName() const;
virtual QString profilePath() const;
- virtual VersionInfo appVersion() const;
- virtual IModInterface *getMod(const QString &name);
- virtual IModInterface *createMod(const QString &name);
- virtual bool removeMod(IModInterface *mod);
- virtual void modDataChanged(IModInterface *mod);
+ virtual MOBase::VersionInfo appVersion() const;
+ virtual MOBase::IModInterface *getMod(const QString &name);
+ virtual MOBase::IModInterface *createMod(const QString &name);
+ virtual bool removeMod(MOBase::IModInterface *mod);
+ virtual void modDataChanged(MOBase::IModInterface *mod);
virtual QVariant pluginSetting(const QString &pluginName, const QString &key) const;
void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info);
@@ -143,8 +143,8 @@ protected:
private:
void actionToToolButton(QAction *&sourceAction);
- bool verifyPlugin(IPlugin *plugin);
- void registerPluginTool(IPluginTool *tool);
+ bool verifyPlugin(MOBase::IPlugin *plugin);
+ void registerPluginTool(MOBase::IPluginTool *tool);
bool registerPlugin(QObject *pluginObj);
void updateToolBar();
@@ -163,7 +163,7 @@ private:
HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir &currentDirectory, const QString &steamAppID);
void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir &currentDirectory = QDir(), bool closeAfterStart = true, const QString &steamAppID = "");
- void updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly);
+ void updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly);
void refreshDirectoryStructure();
bool refreshProfiles(bool selectProfile = true);
void refreshExecutablesList();
@@ -174,7 +174,7 @@ private:
void displayModInformation(int row, int tab = 0);
void testExtractBSA(int modIndex);
- void writeDataToFile(QFile &file, const QString &directory, const DirectoryEntry &directoryEntry);
+ void writeDataToFile(QFile &file, const QString &directory, const MOShared::DirectoryEntry &directoryEntry);
void renameModInList(QFile &modList, const QString &oldName, const QString &newName);
@@ -224,7 +224,7 @@ private:
Ui::MainWindow *ui;
- TutorialControl m_Tutorial;
+ MOBase::TutorialControl m_Tutorial;
QString m_ExeName;
@@ -232,7 +232,7 @@ private:
QThread m_RefresherThread;
DirectoryRefresher m_DirectoryRefresher;
- DirectoryEntry *m_DirectoryStructure;
+ MOShared::DirectoryEntry *m_DirectoryStructure;
std::vector<QString> m_ModNameList; // the mod-list to go with the directory structure
QProgressBar *m_RefreshProgress;
bool m_Refreshing;
@@ -281,9 +281,9 @@ private:
QTime m_StartTime;
SaveGameInfoWidget *m_CurrentSaveView;
- IGameInfo *m_GameInfo;
+ MOBase::IGameInfo *m_GameInfo;
- std::vector<IPluginDiagnose*> m_DiagnosisPlugins;
+ std::vector<MOBase::IPluginDiagnose*> m_DiagnosisPlugins;
private slots:
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 517434a7..3493b710 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -378,9 +378,9 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
- <widget class="LineEditClear" name="modFilterEdit">
+ <widget class="MOBase::LineEditClear" name="modFilterEdit">
<property name="placeholderText">
- <string>Name filter</string>
+ <string>Namefilter</string>
</property>
</widget>
</item>
@@ -908,7 +908,7 @@ p, li { white-space: pre-wrap; }
<item>
<layout class="QVBoxLayout" name="downloadLayout">
<item>
- <widget class="LineEditClear" name="downloadFilterEdit">
+ <widget class="MOBase::LineEditClear" name="downloadFilterEdit">
<property name="placeholderText">
<string>Filter</string>
</property>
@@ -1205,7 +1205,7 @@ Right now this has very limited functionality</string>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
- <class>LineEditClear</class>
+ <class>MOBase::LineEditClear</class>
<extends>QLineEdit</extends>
<header>lineeditclear.h</header>
</customwidget>
diff --git a/src/moapplication.cpp b/src/moapplication.cpp
index aaae77c0..efb0b615 100644
--- a/src/moapplication.cpp
+++ b/src/moapplication.cpp
@@ -17,64 +17,64 @@ 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 "moapplication.h"
-#include "report.h"
-#include "utility.h"
-#include <appconfig.h>
-#include <QFile>
-#include <QStringList>
-
-
-MOApplication::MOApplication(int argc, char **argv)
- : QApplication(argc, argv)
-{
- connect(&m_StyleWatcher, SIGNAL(fileChanged(QString)), SLOT(updateStyle(QString)));
-}
-
-
-void MOApplication::setStyleFile(const QString &styleName)
-{
- // remove all files from watch
- QStringList currentWatch = m_StyleWatcher.files();
- if (currentWatch.count() != 0) {
- m_StyleWatcher.removePaths(currentWatch);
- }
- // set new stylesheet or clear it
- if (styleName.length() != 0) {
- QString styleSheetName = applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()) + "/" + styleName;
- m_StyleWatcher.addPath(styleSheetName);
- updateStyle(styleSheetName);
- } else {
- setStyleSheet("");
- }
-}
-
-
-bool MOApplication::notify(QObject *receiver, QEvent *event)
-{
- try {
- return QApplication::notify(receiver, event);
- } catch (const std::exception &e) {
- qCritical("uncaught exception in handler (object %s, eventtype %d): %s",
- receiver->objectName().toUtf8().constData(), event->type(), e.what());
- reportError(tr("an error occured: %1").arg(e.what()));
- return false;
- } catch (...) {
- qCritical("uncaught non-std exception in handler (object %s, eventtype %d)",
- receiver->objectName().toUtf8().constData(), event->type());
- reportError(tr("an error occured"));
- return false;
- }
-}
-
-
-void MOApplication::updateStyle(const QString &fileName)
-{
- QFile file(fileName);
- if (file.open(QFile::ReadOnly)) {
- setStyleSheet(file.readAll());
- } else {
- qDebug("no stylesheet");
- }
- file.close();
-}
+#include "moapplication.h"
+#include "report.h"
+#include "utility.h"
+#include <appconfig.h>
+#include <QFile>
+#include <QStringList>
+
+
+MOApplication::MOApplication(int argc, char **argv)
+ : QApplication(argc, argv)
+{
+ connect(&m_StyleWatcher, SIGNAL(fileChanged(QString)), SLOT(updateStyle(QString)));
+}
+
+
+void MOApplication::setStyleFile(const QString &styleName)
+{
+ // remove all files from watch
+ QStringList currentWatch = m_StyleWatcher.files();
+ if (currentWatch.count() != 0) {
+ m_StyleWatcher.removePaths(currentWatch);
+ }
+ // set new stylesheet or clear it
+ if (styleName.length() != 0) {
+ QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName;
+ m_StyleWatcher.addPath(styleSheetName);
+ updateStyle(styleSheetName);
+ } else {
+ setStyleSheet("");
+ }
+}
+
+
+bool MOApplication::notify(QObject *receiver, QEvent *event)
+{
+ try {
+ return QApplication::notify(receiver, event);
+ } catch (const std::exception &e) {
+ qCritical("uncaught exception in handler (object %s, eventtype %d): %s",
+ receiver->objectName().toUtf8().constData(), event->type(), e.what());
+ reportError(tr("an error occured: %1").arg(e.what()));
+ return false;
+ } catch (...) {
+ qCritical("uncaught non-std exception in handler (object %s, eventtype %d)",
+ receiver->objectName().toUtf8().constData(), event->type());
+ reportError(tr("an error occured"));
+ return false;
+ }
+}
+
+
+void MOApplication::updateStyle(const QString &fileName)
+{
+ QFile file(fileName);
+ if (file.open(QFile::ReadOnly)) {
+ setStyleSheet(file.readAll());
+ } else {
+ qDebug("no stylesheet");
+ }
+ file.close();
+}
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index 0a0cde68..b8ef834f 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -38,6 +38,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <sstream>
+using namespace MOBase;
+using namespace MOShared;
+
+
std::vector<ModInfo::Ptr> ModInfo::s_Collection;
std::map<QString, unsigned int> ModInfo::s_ModsByName;
std::map<int, unsigned int> ModInfo::s_ModsByModID;
diff --git a/src/modinfo.h b/src/modinfo.h
index 12ba89fd..a944a6f6 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -43,7 +43,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
* to manage the mod collection
*
**/
-class ModInfo : public QObject, public IModInterface
+class ModInfo : public QObject, public MOBase::IModInterface
{
Q_OBJECT
@@ -101,7 +101,7 @@ public:
/**
* @brief read the mod directory and Mod ModInfo objects for all subdirectories
**/
- static void updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure);
+ static void updateFromDisc(const QString &modDirectory, MOShared::DirectoryEntry **directoryStructure);
static void clear() { s_Collection.clear(); s_ModsByName.clear(); s_ModsByModID.clear(); }
@@ -174,7 +174,7 @@ public:
* @param dir directory to create from
* @return pointer to the info-structure of the newly created/added mod
*/
- static ModInfo::Ptr createFrom(const QDir &dir, DirectoryEntry **directoryStructure);
+ static ModInfo::Ptr createFrom(const QDir &dir, MOShared::DirectoryEntry **directoryStructure);
virtual bool isRegular() const { return false; }
@@ -240,7 +240,7 @@ public:
* @brief set/change the version of this mod
* @param version new version of the mod
*/
- virtual void setVersion(const VersionInfo &version);
+ virtual void setVersion(const MOBase::VersionInfo &version);
/**
* @brief set the newest version of this mod on the nexus
@@ -252,7 +252,7 @@ public:
* @todo this function should be made obsolete. All queries for mod information should go through
* this class so no public function for this change is required
**/
- virtual void setNewestVersion(const VersionInfo &version) = 0;
+ virtual void setNewestVersion(const MOBase::VersionInfo &version) = 0;
/**
* @brief changes/updates the nexus description text
@@ -304,14 +304,14 @@ public:
/**
* @return version object for machine based comparisons
**/
- virtual VersionInfo getVersion() const { return m_Version; }
+ virtual MOBase::VersionInfo getVersion() const { return m_Version; }
/**
* @brief getter for the newest version number of this mod
*
* @return newest version of the mod
**/
- virtual VersionInfo getNewestVersion() const = 0;
+ virtual MOBase::VersionInfo getNewestVersion() const = 0;
/**
* @brief getter for the nexus mod id
@@ -454,7 +454,7 @@ protected:
int m_PrimaryCategory;
std::set<int> m_Categories;
- VersionInfo m_Version;
+ MOBase::VersionInfo m_Version;
private:
@@ -553,7 +553,7 @@ public:
*
* @param version the new version to use
**/
- void setVersion(const VersionInfo &version);
+ void setVersion(const MOBase::VersionInfo &version);
/**
* @brief set the newest version of this mod on the nexus
@@ -565,7 +565,7 @@ public:
* @todo this function should be made obsolete. All queries for mod information should go through
* this class so no public function for this change is required
**/
- void setNewestVersion(const VersionInfo &version) { m_NewestVersion = version; }
+ void setNewestVersion(const MOBase::VersionInfo &version) { m_NewestVersion = version; }
/**
* @brief changes/updates the nexus description text
@@ -612,7 +612,7 @@ public:
*
* @return newest version of the mod
**/
- VersionInfo getNewestVersion() const { return m_NewestVersion; }
+ MOBase::VersionInfo getNewestVersion() const { return m_NewestVersion; }
/**
* @brief getter for the installation file
@@ -727,7 +727,7 @@ private:
protected:
- ModInfoRegular(const QDir &path, DirectoryEntry **directoryStructure);
+ ModInfoRegular(const QDir &path, MOShared::DirectoryEntry **directoryStructure);
private:
@@ -743,13 +743,13 @@ private:
int m_NexusID;
bool m_MetaInfoChanged;
- VersionInfo m_NewestVersion;
+ MOBase::VersionInfo m_NewestVersion;
EEndorsedState m_EndorsedState;
NexusBridge m_NexusBridge;
- DirectoryEntry **m_DirectoryStructure;
+ MOShared::DirectoryEntry **m_DirectoryStructure;
};
@@ -777,7 +777,7 @@ public:
private:
- ModInfoBackup(const QDir &path, DirectoryEntry **directoryStructure);
+ ModInfoBackup(const QDir &path, MOShared::DirectoryEntry **directoryStructure);
};
@@ -797,7 +797,7 @@ public:
virtual bool setName(const QString&) { return false; }
virtual void setNotes(const QString&) {}
virtual void setNexusID(int) {}
- virtual void setNewestVersion(const VersionInfo&) {}
+ virtual void setNewestVersion(const MOBase::VersionInfo&) {}
virtual void setNexusDescription(const QString&) {}
virtual void setIsEndorsed(bool) {}
virtual bool remove() { return false; }
@@ -805,7 +805,7 @@ public:
virtual QString name() const { return tr("Overwrite"); }
virtual QString notes() const { return ""; }
virtual QString absolutePath() const;
- virtual VersionInfo getNewestVersion() const { return ""; }
+ virtual MOBase::VersionInfo getNewestVersion() const { return ""; }
virtual QString getInstallationFile() const { return ""; }
virtual int getFixedPriority() const { return INT_MAX; }
virtual int getNexusID() const { return -1; }
diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp
index 14b40c91..df42cc11 100644
--- a/src/modinfodialog.cpp
+++ b/src/modinfodialog.cpp
@@ -42,6 +42,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using QtJson::Json;
+using namespace MOBase;
+using namespace MOShared;
+
class ModFileListWidget : public QListWidgetItem {
friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS);
diff --git a/src/modinfodialog.h b/src/modinfodialog.h
index 07aa8e84..03cf44ba 100644
--- a/src/modinfodialog.h
+++ b/src/modinfodialog.h
@@ -51,7 +51,7 @@ class QTreeView;
* this is a larger dialog used to visualise information abount the mod.
* @todo this would probably a good place for a plugin-system
**/
-class ModInfoDialog : public TutorableDialog
+class ModInfoDialog : public MOBase::TutorableDialog
{
Q_OBJECT
@@ -76,7 +76,7 @@ public:
* @param modInfo info structure about the mod to display
* @param parent parend widget
**/
- explicit ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, QWidget *parent = 0);
+ explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, QWidget *parent = 0);
~ModInfoDialog();
/**
@@ -209,8 +209,8 @@ private:
QTreeWidgetItem *m_ConflictsContextItem;
- const DirectoryEntry *m_Directory;
- FilesOrigin *m_Origin;
+ const MOShared::DirectoryEntry *m_Directory;
+ MOShared::FilesOrigin *m_Origin;
QTextCodec *m_UTF8Codec;
};
diff --git a/src/modlist.cpp b/src/modlist.cpp
index aac913c5..680e0af5 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -44,6 +44,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <algorithm>
+using namespace MOBase;
+
+
ModList::ModList(QObject *parent)
: QAbstractItemModel(parent), m_Profile(NULL), m_Modified(false),
m_FontMetrics(QFont())
diff --git a/src/motddialog.cpp b/src/motddialog.cpp
index fbadf5fd..1bf11a7f 100644
--- a/src/motddialog.cpp
+++ b/src/motddialog.cpp
@@ -33,16 +33,16 @@ MotDDialog::MotDDialog(const QString &message, QWidget *parent)
}
MotDDialog::~MotDDialog()
-{
- delete ui;
-}
-
-void MotDDialog::on_okButton_clicked()
+{
+ delete ui;
+}
+
+void MotDDialog::on_okButton_clicked()
{
this->close();
}
void MotDDialog::linkClicked(const QUrl &url)
{
- ::ShellExecuteW(NULL, L"open", ToWString(url.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL);
+ ::ShellExecuteW(NULL, L"open", MOBase::ToWString(url.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL);
}
diff --git a/src/nexusdialog.cpp b/src/nexusdialog.cpp
index 7046cdd7..91c0caf8 100644
--- a/src/nexusdialog.cpp
+++ b/src/nexusdialog.cpp
@@ -33,6 +33,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QWebHistory>
+using namespace MOBase;
+using namespace MOShared;
+
NexusDialog::NexusDialog(NXMAccessManager *accessManager, QWidget *parent)
: QDialog(parent), ui(new Ui::NexusDialog),
m_ModUrlExp(QString("http://([a-zA-Z0-9]*).nexusmods.com/mods/(\\d+)"), Qt::CaseInsensitive),
diff --git a/src/nexusdialog.h b/src/nexusdialog.h
index fbe99f3d..d7e8ff50 100644
--- a/src/nexusdialog.h
+++ b/src/nexusdialog.h
@@ -146,7 +146,7 @@ private:
Ui::NexusDialog *ui;
- TutorialControl m_Tutorial;
+ MOBase::TutorialControl m_Tutorial;
QString m_Url;
QRegExp m_ModUrlExp;
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp
index ad8c529d..016cdeef 100644
--- a/src/nexusinterface.cpp
+++ b/src/nexusinterface.cpp
@@ -27,6 +27,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using QtJson::Json;
+using namespace MOBase;
+using namespace MOShared;
+
NexusBridge::NexusBridge()
{
diff --git a/src/nexusinterface.h b/src/nexusinterface.h
index 0e6c0eeb..cca84e06 100644
--- a/src/nexusinterface.h
+++ b/src/nexusinterface.h
@@ -17,311 +17,311 @@ 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 NEXUSINTERFACE_H
-#define NEXUSINTERFACE_H
-
-
-#include "nxmaccessmanager.h"
-
-#include "utility.h"
-#include <gameinfo.h>
-#include <versioninfo.h>
-#include <QNetworkReply>
-#include <QNetworkDiskCache>
-#include <QQueue>
-#include <QVariant>
-#include <QTimer>
-#include <list>
-#include <set>
-
-
-class NexusInterface;
-
-
-/**
- * @brief convenience class to make nxm requests easier
- * usually, all objects that started a nxm request will be signaled if one finished.
- * Therefore, the objects need to store the id of the requests they started and then filter
- * the result.
- * NexusBridge does this automatically. Users connect to the signals of NexusBridge they intend
- * to handle and only receive the signals the caused
- **/
-class NexusBridge : public QObject
-{
-
- Q_OBJECT
-
-public:
-
- NexusBridge();
-
- /**
- * @brief request description for a mod
- *
- * @param modID id of the mod caller is interested in
- * @param userData user data to be returned with the result
- * @param url the url to request from
- **/
- void requestDescription(int modID, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request a list of the files belonging to a mod
- *
- * @param modID id of the mod caller is interested in
- * @param userData user data to be returned with the result
- * @param url the url to request from
- **/
- void requestFiles(int modID, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request info about a single file of a mod
- *
- * @param modID id of the mod caller is interested in
- * @param fileID id of the file the caller is interested in
- * @param userData user data to be returned with the result
- * @param url the url to request from
- **/
- void requestFileInfo(int modID, int fileID, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request the download url of a file
- *
- * @param modID id of the mod caller is interested in
- * @param fileID id of the file the caller is interested in
- * @param userData user data to be returned with the result
- * @param url the url to request from
- **/
- void requestDownloadURL(int modID, int fileID, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief requestToggleEndorsement
- * @param modID
- * @param userData
- * @param url
- */
- void requestToggleEndorsement(int modID, bool endorse, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
-signals:
-
- void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData);
- void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData);
- void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData);
- void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData);
- void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData);
- void nxmRequestFailed(int modID, QVariant userData, const QString &errorMessage);
-
-public slots:
-
- void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
- void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
- void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage);
-
-private:
-
- NexusInterface *m_Interface;
- std::set<int> m_RequestIDs;
-
-};
-
-
-/**
- * @brief Makes asynchronous requests to the nexus API
- *
- * This class can be used to make asynchronous requests to the Nexus API.
- * Currently, responses are sent to all receivers that have sent a request of the relevant type, so the
- * recipient has to filter the response by the id returned when making the request
- **/
-class NexusInterface : public QObject
-{
- Q_OBJECT
-
-public:
-
- static NexusInterface *instance();
-
- /**
- * @return the access manager object used to connect to nexus
- **/
- NXMAccessManager *getAccessManager();
-
- /**
- * @brief request description for a mod
- *
- * @param modID id of the mod caller is interested in
- * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable)
- * @param userData user data to be returned with the result
- * @param url the url to request from
- * @return int an id to identify the request
- **/
- int requestDescription(int modID, QObject *receiver, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request nexus descriptions for multiple mods at once
- * @param modIDs a list of ids of mods the caller is interested in
- * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable)
- * @param userData user data to be returned with the result
- * @param url the url to request from
- * @return int an id to identify the request
- */
- int requestUpdates(const std::vector<int> &modIDs, QObject *receiver, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request a list of the files belonging to a mod
- *
- * @param modID id of the mod caller is interested in
- * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
- * @param userData user data to be returned with the result
- * @param url the url to request from
- * @return int an id to identify the request
- **/
- int requestFiles(int modID, QObject *receiver, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request info about a single file of a mod
- *
- * @param modID id of the mod caller is interested in
- * @param fileID id of the file the caller is interested in
- * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
- * @param userData user data to be returned with the result
- * @param url the url to request from
- * @return int an id to identify the request
- **/
- int requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief request the download url of a file
- *
- * @param modID id of the mod caller is interested in
- * @param fileID id of the file the caller is interested in
- * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
- * @param userData user data to be returned with the result
- * @param url the url to request from
- * @return int an id to identify the request
- **/
- int requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @brief toggle endorsement state of the mod
- * @param modID id of the mod
- * @param endorse true if the mod should be endorsed, false for un-endorse
- * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
- * @param userData user data to be returned with the result
- * @param url the url to request from
- * @return int an id to identify the request
- */
- int requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData,
- const QString &url = ToQString(GameInfo::instance().getNexusInfoUrl()));
-
- /**
- * @param directory the directory to store cache files
- **/
- void setCacheDirectory(const QString &directory);
-
- /**
- * MO has to send a "Nexus Client Vx.y.z" as part of the user agent to be allowed to use the API
- * @param nmmVersion the version of nmm to impersonate
- **/
- void setNMMVersion(const QString &nmmVersion);
-
-public:
-
- /**
- * @brief guess the mod id from a filename as delivered by Nexus
- * @param fileName name of the file
- * @return the guessed mod id
- * @note this currently doesn't fit well with the remaining interface but this is the best place for the function
- */
- static void interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query);
-
-signals:
-
- void requestNXMDownload(const QString &url);
-
- void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID);
- void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
- void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
- void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString);
-
-private slots:
-
- void requestFinished();
- void requestError(QNetworkReply::NetworkError error);
- void requestTimeout();
-
- void downloadRequestedNXM(const QString &url);
-
-private:
-
- struct NXMRequestInfo {
- int m_ModID;
- std::vector<int> m_ModIDList;
- int m_FileID;
- QNetworkReply *m_Reply;
- enum Type {
- TYPE_DESCRIPTION,
- TYPE_FILES,
- TYPE_FILEINFO,
- TYPE_DOWNLOADURL,
- TYPE_TOGGLEENDORSEMENT,
- TYPE_GETUPDATES
- } m_Type;
- QVariant m_UserData;
- QTimer *m_Timeout;
- QString m_URL;
- 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) {}
- NXMRequestInfo(std::vector<int> 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) {}
- 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) {}
-
- private:
- static QAtomicInt s_NextID;
- };
-
- static const int MAX_ACTIVE_DOWNLOADS = 2;
-
-private:
-
- NexusInterface();
- void nextRequest();
- void requestFinished(std::list<NXMRequestInfo>::iterator iter);
-
-private:
-
- static NexusInterface *s_Instance;
-
- QNetworkDiskCache *m_DiskCache;
-
- NXMAccessManager *m_AccessManager;
-
- std::list<NXMRequestInfo> m_ActiveRequest;
- QQueue<NXMRequestInfo> m_RequestQueue;
-
- QString m_NMMVersion;
-
-};
-
-#endif // NEXUSINTERFACE_H
+#ifndef NEXUSINTERFACE_H
+#define NEXUSINTERFACE_H
+
+
+#include "nxmaccessmanager.h"
+
+#include "utility.h"
+#include <gameinfo.h>
+#include <versioninfo.h>
+#include <QNetworkReply>
+#include <QNetworkDiskCache>
+#include <QQueue>
+#include <QVariant>
+#include <QTimer>
+#include <list>
+#include <set>
+
+
+class NexusInterface;
+
+
+/**
+ * @brief convenience class to make nxm requests easier
+ * usually, all objects that started a nxm request will be signaled if one finished.
+ * Therefore, the objects need to store the id of the requests they started and then filter
+ * the result.
+ * NexusBridge does this automatically. Users connect to the signals of NexusBridge they intend
+ * to handle and only receive the signals the caused
+ **/
+class NexusBridge : public QObject
+{
+
+ Q_OBJECT
+
+public:
+
+ NexusBridge();
+
+ /**
+ * @brief request description for a mod
+ *
+ * @param modID id of the mod caller is interested in
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ **/
+ void requestDescription(int modID, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request a list of the files belonging to a mod
+ *
+ * @param modID id of the mod caller is interested in
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ **/
+ void requestFiles(int modID, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request info about a single file of a mod
+ *
+ * @param modID id of the mod caller is interested in
+ * @param fileID id of the file the caller is interested in
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ **/
+ void requestFileInfo(int modID, int fileID, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request the download url of a file
+ *
+ * @param modID id of the mod caller is interested in
+ * @param fileID id of the file the caller is interested in
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ **/
+ void requestDownloadURL(int modID, int fileID, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief requestToggleEndorsement
+ * @param modID
+ * @param userData
+ * @param url
+ */
+ void requestToggleEndorsement(int modID, bool endorse, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+signals:
+
+ void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData);
+ void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData);
+ void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData);
+ void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData);
+ void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData);
+ void nxmRequestFailed(int modID, QVariant userData, const QString &errorMessage);
+
+public slots:
+
+ void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
+ void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
+ void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage);
+
+private:
+
+ NexusInterface *m_Interface;
+ std::set<int> m_RequestIDs;
+
+};
+
+
+/**
+ * @brief Makes asynchronous requests to the nexus API
+ *
+ * This class can be used to make asynchronous requests to the Nexus API.
+ * Currently, responses are sent to all receivers that have sent a request of the relevant type, so the
+ * recipient has to filter the response by the id returned when making the request
+ **/
+class NexusInterface : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ static NexusInterface *instance();
+
+ /**
+ * @return the access manager object used to connect to nexus
+ **/
+ NXMAccessManager *getAccessManager();
+
+ /**
+ * @brief request description for a mod
+ *
+ * @param modID id of the mod caller is interested in
+ * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable)
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ * @return int an id to identify the request
+ **/
+ int requestDescription(int modID, QObject *receiver, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request nexus descriptions for multiple mods at once
+ * @param modIDs a list of ids of mods the caller is interested in
+ * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable)
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ * @return int an id to identify the request
+ */
+ int requestUpdates(const std::vector<int> &modIDs, QObject *receiver, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request a list of the files belonging to a mod
+ *
+ * @param modID id of the mod caller is interested in
+ * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ * @return int an id to identify the request
+ **/
+ int requestFiles(int modID, QObject *receiver, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request info about a single file of a mod
+ *
+ * @param modID id of the mod caller is interested in
+ * @param fileID id of the file the caller is interested in
+ * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ * @return int an id to identify the request
+ **/
+ int requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief request the download url of a file
+ *
+ * @param modID id of the mod caller is interested in
+ * @param fileID id of the file the caller is interested in
+ * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ * @return int an id to identify the request
+ **/
+ int requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @brief toggle endorsement state of the mod
+ * @param modID id of the mod
+ * @param endorse true if the mod should be endorsed, false for un-endorse
+ * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable)
+ * @param userData user data to be returned with the result
+ * @param url the url to request from
+ * @return int an id to identify the request
+ */
+ int requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData,
+ const QString &url = MOBase::ToQString(MOShared::GameInfo::instance().getNexusInfoUrl()));
+
+ /**
+ * @param directory the directory to store cache files
+ **/
+ void setCacheDirectory(const QString &directory);
+
+ /**
+ * MO has to send a "Nexus Client Vx.y.z" as part of the user agent to be allowed to use the API
+ * @param nmmVersion the version of nmm to impersonate
+ **/
+ void setNMMVersion(const QString &nmmVersion);
+
+public:
+
+ /**
+ * @brief guess the mod id from a filename as delivered by Nexus
+ * @param fileName name of the file
+ * @return the guessed mod id
+ * @note this currently doesn't fit well with the remaining interface but this is the best place for the function
+ */
+ static void interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query);
+
+signals:
+
+ void requestNXMDownload(const QString &url);
+
+ void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID);
+ void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
+ void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
+ void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorString);
+
+private slots:
+
+ void requestFinished();
+ void requestError(QNetworkReply::NetworkError error);
+ void requestTimeout();
+
+ void downloadRequestedNXM(const QString &url);
+
+private:
+
+ struct NXMRequestInfo {
+ int m_ModID;
+ std::vector<int> m_ModIDList;
+ int m_FileID;
+ QNetworkReply *m_Reply;
+ enum Type {
+ TYPE_DESCRIPTION,
+ TYPE_FILES,
+ TYPE_FILEINFO,
+ TYPE_DOWNLOADURL,
+ TYPE_TOGGLEENDORSEMENT,
+ TYPE_GETUPDATES
+ } m_Type;
+ QVariant m_UserData;
+ QTimer *m_Timeout;
+ QString m_URL;
+ 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) {}
+ NXMRequestInfo(std::vector<int> 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) {}
+ 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) {}
+
+ private:
+ static QAtomicInt s_NextID;
+ };
+
+ static const int MAX_ACTIVE_DOWNLOADS = 2;
+
+private:
+
+ NexusInterface();
+ void nextRequest();
+ void requestFinished(std::list<NXMRequestInfo>::iterator iter);
+
+private:
+
+ static NexusInterface *s_Instance;
+
+ QNetworkDiskCache *m_DiskCache;
+
+ NXMAccessManager *m_AccessManager;
+
+ std::list<NXMRequestInfo> m_ActiveRequest;
+ QQueue<NXMRequestInfo> m_RequestQueue;
+
+ QString m_NMMVersion;
+
+};
+
+#endif // NEXUSINTERFACE_H
diff --git a/src/nexusview.cpp b/src/nexusview.cpp
index c8c7f09d..269894c2 100644
--- a/src/nexusview.cpp
+++ b/src/nexusview.cpp
@@ -28,6 +28,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Shlwapi.h>
#include "utility.h"
+
+using namespace MOBase;
+
+
NexusView::NexusView(QWidget *parent)
: QWebView(parent), m_LastSeenModID(0)
{
diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp
index 55fcf0f7..0f9f0209 100644
--- a/src/nxmaccessmanager.cpp
+++ b/src/nxmaccessmanager.cpp
@@ -17,137 +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 "nxmaccessmanager.h"
-#include "nxmurl.h"
-#include "report.h"
-#include "utility.h"
-#include "selfupdater.h"
-#include <QMessageBox>
-#include <QNetworkProxy>
-#include <QNetworkRequest>
-#include <QNetworkCookie>
-#include <QNetworkCookieJar>
-#include <gameinfo.h>
-
-
-NXMAccessManager::NXMAccessManager(QObject *parent)
- : QNetworkAccessManager(parent), m_LoginReply(NULL)
-{
-}
-
-
-QNetworkReply *NXMAccessManager::createRequest(
- QNetworkAccessManager::Operation operation, const QNetworkRequest &request,
- QIODevice *device)
-{
- if (request.url().scheme() != "nxm") {
- return QNetworkAccessManager::createRequest(operation, request, device);
- }
- if (operation == GetOperation) {
- emit requestNXMDownload(request.url().toString());
-
- // eat the request, everything else will be done by the download manager
- return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation,
- QNetworkRequest(QUrl()));
- } else if (operation == PostOperation) {
- return QNetworkAccessManager::createRequest(operation, request, device);;
- } else {
- return QNetworkAccessManager::createRequest(operation, request, device);
- }
-}
-
-
-void NXMAccessManager::showCookies()
-{
- QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage())));
- foreach (QNetworkCookie cookie, cookies) {
- qDebug("%s - %s", cookie.name().constData(), cookie.value().constData());
- }
-}
-
-
-bool NXMAccessManager::loggedIn() const
-{
- return hasLoginCookies();
-}
-
-
-void NXMAccessManager::login(const QString &username, const QString &password)
-{
- if (m_LoginReply != NULL) {
- return;
- }
-
- if (hasLoginCookies()) {
- emit loginSuccessful(false);
- return;
- }
-
- m_Username = username;
- m_Password = password;
- pageLogin();
-}
-
-
-void NXMAccessManager::pageLogin()
-{
- QString requestString = QString("http://gatekeeper.nexusmods.com/Sessions/?Login&username=%2&password=%3").arg(m_Username).arg(m_Password);
-
- QNetworkRequest request(requestString);
- m_LoginReply = get(request);
-
- m_LoginTimeout.start();
- connect(m_LoginReply, SIGNAL(finished()), this, SLOT(loginFinished()));
- connect(m_LoginReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(loginError(QNetworkReply::NetworkError)));
-}
-
-
-void NXMAccessManager::loginTimeout()
-{
- emit loginFailed(tr("timeout"));
- m_LoginReply->deleteLater();
- m_LoginReply = NULL;
- m_LoginTimeout.stop();
- m_Username.clear();
- m_Password.clear();
-}
-
-
-void NXMAccessManager::loginError(QNetworkReply::NetworkError)
-{
- emit loginFailed(m_LoginReply->errorString());
- m_LoginTimeout.stop();
- m_LoginReply->deleteLater();
- m_LoginReply = NULL;
- m_Username.clear();
- m_Password.clear();
-}
-
-
-bool NXMAccessManager::hasLoginCookies() const
-{
- bool sidCookie = false;
- QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage())));
- foreach (QNetworkCookie cookie, cookies) {
- if (cookie.name() == "sid") {
- sidCookie = true;
- }
- }
- return sidCookie;
-}
-
-
-void NXMAccessManager::loginFinished()
-{
- if (hasLoginCookies()) {
- emit loginSuccessful(true);
- } else {
- emit loginFailed(tr("Please check your password"));
- }
-
- m_LoginTimeout.stop();
- m_LoginReply->deleteLater();
- m_LoginReply = NULL;
- m_Username.clear();
- m_Password.clear();
-}
+#include "nxmaccessmanager.h"
+#include "nxmurl.h"
+#include "report.h"
+#include "utility.h"
+#include "selfupdater.h"
+#include <QMessageBox>
+#include <QNetworkProxy>
+#include <QNetworkRequest>
+#include <QNetworkCookie>
+#include <QNetworkCookieJar>
+#include <gameinfo.h>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+NXMAccessManager::NXMAccessManager(QObject *parent)
+ : QNetworkAccessManager(parent), m_LoginReply(NULL)
+{
+}
+
+
+QNetworkReply *NXMAccessManager::createRequest(
+ QNetworkAccessManager::Operation operation, const QNetworkRequest &request,
+ QIODevice *device)
+{
+ if (request.url().scheme() != "nxm") {
+ return QNetworkAccessManager::createRequest(operation, request, device);
+ }
+ if (operation == GetOperation) {
+ emit requestNXMDownload(request.url().toString());
+
+ // eat the request, everything else will be done by the download manager
+ return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation,
+ QNetworkRequest(QUrl()));
+ } else if (operation == PostOperation) {
+ return QNetworkAccessManager::createRequest(operation, request, device);;
+ } else {
+ return QNetworkAccessManager::createRequest(operation, request, device);
+ }
+}
+
+
+void NXMAccessManager::showCookies()
+{
+ QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage())));
+ foreach (QNetworkCookie cookie, cookies) {
+ qDebug("%s - %s", cookie.name().constData(), cookie.value().constData());
+ }
+}
+
+
+bool NXMAccessManager::loggedIn() const
+{
+ return hasLoginCookies();
+}
+
+
+void NXMAccessManager::login(const QString &username, const QString &password)
+{
+ if (m_LoginReply != NULL) {
+ return;
+ }
+
+ if (hasLoginCookies()) {
+ emit loginSuccessful(false);
+ return;
+ }
+
+ m_Username = username;
+ m_Password = password;
+ pageLogin();
+}
+
+
+void NXMAccessManager::pageLogin()
+{
+ QString requestString = QString("http://gatekeeper.nexusmods.com/Sessions/?Login&username=%2&password=%3").arg(m_Username).arg(m_Password);
+
+ QNetworkRequest request(requestString);
+ m_LoginReply = get(request);
+
+ m_LoginTimeout.start();
+ connect(m_LoginReply, SIGNAL(finished()), this, SLOT(loginFinished()));
+ connect(m_LoginReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(loginError(QNetworkReply::NetworkError)));
+}
+
+
+void NXMAccessManager::loginTimeout()
+{
+ emit loginFailed(tr("timeout"));
+ m_LoginReply->deleteLater();
+ m_LoginReply = NULL;
+ m_LoginTimeout.stop();
+ m_Username.clear();
+ m_Password.clear();
+}
+
+
+void NXMAccessManager::loginError(QNetworkReply::NetworkError)
+{
+ emit loginFailed(m_LoginReply->errorString());
+ m_LoginTimeout.stop();
+ m_LoginReply->deleteLater();
+ m_LoginReply = NULL;
+ m_Username.clear();
+ m_Password.clear();
+}
+
+
+bool NXMAccessManager::hasLoginCookies() const
+{
+ bool sidCookie = false;
+ QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage())));
+ foreach (QNetworkCookie cookie, cookies) {
+ if (cookie.name() == "sid") {
+ sidCookie = true;
+ }
+ }
+ return sidCookie;
+}
+
+
+void NXMAccessManager::loginFinished()
+{
+ if (hasLoginCookies()) {
+ emit loginSuccessful(true);
+ } else {
+ emit loginFailed(tr("Please check your password"));
+ }
+
+ m_LoginTimeout.stop();
+ m_LoginReply->deleteLater();
+ m_LoginReply = NULL;
+ m_Username.clear();
+ m_Password.clear();
+}
diff --git a/src/nxmurl.cpp b/src/nxmurl.cpp
index 53eb369c..003b3c58 100644
--- a/src/nxmurl.cpp
+++ b/src/nxmurl.cpp
@@ -17,18 +17,18 @@ 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 "nxmurl.h"
-#include <utility.h>
-#include <QRegExp>
-#include <QStringList>
-
-NXMUrl::NXMUrl(const QString &url)
-{
- QRegExp exp("nxm://([a-z]+)/mods/(\\d+)/files/(\\d+)", Qt::CaseInsensitive);
- exp.indexIn(url);
- if (exp.captureCount() != 3) {
- throw MyException(tr("invalid nxm-link: %1").arg(url));
- }
- m_ModId = exp.cap(2).toInt();
- m_FileId = exp.cap(3).toInt();
-}
+#include "nxmurl.h"
+#include <utility.h>
+#include <QRegExp>
+#include <QStringList>
+
+NXMUrl::NXMUrl(const QString &url)
+{
+ QRegExp exp("nxm://([a-z]+)/mods/(\\d+)/files/(\\d+)", Qt::CaseInsensitive);
+ exp.indexIn(url);
+ if (exp.captureCount() != 3) {
+ throw MOBase::MyException(tr("invalid nxm-link: %1").arg(url));
+ }
+ m_ModId = exp.cap(2).toInt();
+ m_FileId = exp.cap(3).toInt();
+}
diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp
index 1af73b7b..99699b52 100644
--- a/src/overwriteinfodialog.cpp
+++ b/src/overwriteinfodialog.cpp
@@ -17,234 +17,237 @@ 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 "overwriteinfodialog.h"
-#include "ui_overwriteinfodialog.h"
-#include "report.h"
-#include "utility.h"
-#include <QMessageBox>
-#include <QMenu>
-#include <Shlwapi.h>
-
-
-class MyFileSystemModel : public QFileSystemModel {
-
-public:
- MyFileSystemModel(QObject *parent)
- : QFileSystemModel(parent), m_RegularColumnCount(0) {}
-
- virtual int columnCount(const QModelIndex &parent) const {
- m_RegularColumnCount = QFileSystemModel::columnCount(parent);
-// return m_RegularColumnCount + 1;
- return m_RegularColumnCount;
- }
-
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const {
- if ((orientation == Qt::Horizontal) &&
- (section >= m_RegularColumnCount)) {
- if (role == Qt::DisplayRole) {
- return tr("Overwrites");
- } else {
- return QVariant();
- }
- } else {
- return QFileSystemModel::headerData(section, orientation, role);
- }
- }
-
- virtual QVariant data(const QModelIndex &index, int role) const {
- if (index.column() == m_RegularColumnCount + 0) {
- if (role == Qt::DisplayRole) {
- return tr("not implemented");
- } else {
- return QVariant();
- }
- } else {
- return QFileSystemModel::data(index, role);
- }
- }
-
-private:
- mutable int m_RegularColumnCount;
-};
-
-
-OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget *parent)
- : QDialog(parent), ui(new Ui::OverwriteInfoDialog), m_FileSystemModel(NULL),
- m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL)
-{
- ui->setupUi(this);
-
- QString path = modInfo->absolutePath();
- m_FileSystemModel = new MyFileSystemModel(this);
- m_FileSystemModel->setReadOnly(false);
- m_FileSystemModel->setRootPath(path);
- ui->filesView->setModel(m_FileSystemModel);
- ui->filesView->setRootIndex(m_FileSystemModel->index(path));
- ui->filesView->setColumnWidth(0, 250);
-// ui->filesView->header()->hideSection(3);
-
- m_DeleteAction = new QAction(tr("&Delete"), ui->filesView);
- m_RenameAction = new QAction(tr("&Rename"), ui->filesView);
- m_OpenAction = new QAction(tr("&Open"), ui->filesView);
- m_NewFolderAction = new QAction(tr("&New Folder"), ui->filesView);
- QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered()));
- QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered()));
- QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered()));
- QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered()));
-}
-
-OverwriteInfoDialog::~OverwriteInfoDialog()
-{
- delete ui;
-}
-
-
-bool OverwriteInfoDialog::recursiveDelete(const QModelIndex &index)
-{
- for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) {
- QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index);
- if (m_FileSystemModel->isDir(childIndex)) {
- if (!recursiveDelete(childIndex)) {
- qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData());
- return false;
- }
- } else {
- if (!m_FileSystemModel->remove(childIndex)) {
- qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData());
- return false;
- }
- }
- }
- if (!m_FileSystemModel->remove(index)) {
- qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData());
- return false;
- }
- return true;
-}
-
-
-void OverwriteInfoDialog::deleteFile(const QModelIndex &index)
-{
-
- bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index)
- : m_FileSystemModel->remove(index);
- if (!res) {
- QString fileName = m_FileSystemModel->fileName(index);
- reportError(tr("Failed to delete \"%1\"").arg(fileName));
- }
-}
-
-
-void OverwriteInfoDialog::deleteTriggered()
-{
- if (m_FileSelection.count() == 0) {
- return;
- } else if (m_FileSelection.count() == 1) {
- QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0));
- if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName),
- QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
- return;
- }
- } else {
- if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"),
- QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
- return;
- }
- }
-
- foreach(QModelIndex index, m_FileSelection) {
- deleteFile(index);
- }
-}
-
-
-void OverwriteInfoDialog::renameTriggered()
-{
- QModelIndex selection = m_FileSelection.at(0);
- QModelIndex index = selection.sibling(selection.row(), 0);
- if (!index.isValid() || m_FileSystemModel->isReadOnly()) {
- return;
- }
-
- ui->filesView->edit(index);
-}
-
-
-void OverwriteInfoDialog::openFile(const QModelIndex &index)
-{
- QString fileName = m_FileSystemModel->filePath(index);
-
- HINSTANCE res = ::ShellExecuteW(NULL, L"open", ToWString(fileName).c_str(), NULL, NULL, SW_SHOW);
- if ((int)res <= 32) {
- qCritical("failed to invoke %s: %d", fileName.toUtf8().constData(), res);
- }
-}
-
-
-void OverwriteInfoDialog::openTriggered()
-{
- foreach(QModelIndex idx, m_FileSelection) {
- openFile(idx);
- }
-}
-
-void OverwriteInfoDialog::createDirectoryTriggered()
-{
- QModelIndex selection = m_FileSelection.at(0);
-
- QModelIndex index = m_FileSystemModel->isDir(selection) ? selection
- : selection.parent();
- index = index.sibling(index.row(), 0);
-
- QString name = tr("New Folder");
- QString path = m_FileSystemModel->filePath(index).append("/");
-
- QModelIndex existingIndex = m_FileSystemModel->index(path + name);
- int suffix = 1;
- while (existingIndex.isValid()) {
- name = tr("New Folder") + QString::number(suffix++);
- existingIndex = m_FileSystemModel->index(path + name);
- }
-
- QModelIndex newIndex = m_FileSystemModel->mkdir(index, name);
- if (!newIndex.isValid()) {
- reportError(tr("Failed to create \"%1\"").arg(name));
- return;
- }
-
- ui->filesView->setCurrentIndex(newIndex);
- ui->filesView->edit(newIndex);
-}
-
-
-void OverwriteInfoDialog::on_filesView_customContextMenuRequested(const QPoint &pos)
-{
- QItemSelectionModel *selectionModel = ui->filesView->selectionModel();
- m_FileSelection = selectionModel->selectedRows(0);
-
-// m_FileSelection = m_FileTree->indexAt(pos);
- QMenu menu(ui->filesView);
-
- menu.addAction(m_NewFolderAction);
-
- bool hasFiles = false;
-
- foreach(QModelIndex idx, m_FileSelection) {
- if (m_FileSystemModel->fileInfo(idx).isFile()) {
- hasFiles = true;
- break;
- }
- }
-
- if (selectionModel->hasSelection()) {
- if (hasFiles) {
- menu.addAction(m_OpenAction);
- }
- menu.addAction(m_RenameAction);
- menu.addAction(m_DeleteAction);
- } else {
- m_FileSelection.clear();
- m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0));
- }
- menu.exec(ui->filesView->mapToGlobal(pos));
-}
+#include "overwriteinfodialog.h"
+#include "ui_overwriteinfodialog.h"
+#include "report.h"
+#include "utility.h"
+#include <QMessageBox>
+#include <QMenu>
+#include <Shlwapi.h>
+
+
+using namespace MOBase;
+
+
+class MyFileSystemModel : public QFileSystemModel {
+
+public:
+ MyFileSystemModel(QObject *parent)
+ : QFileSystemModel(parent), m_RegularColumnCount(0) {}
+
+ virtual int columnCount(const QModelIndex &parent) const {
+ m_RegularColumnCount = QFileSystemModel::columnCount(parent);
+// return m_RegularColumnCount + 1;
+ return m_RegularColumnCount;
+ }
+
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const {
+ if ((orientation == Qt::Horizontal) &&
+ (section >= m_RegularColumnCount)) {
+ if (role == Qt::DisplayRole) {
+ return tr("Overwrites");
+ } else {
+ return QVariant();
+ }
+ } else {
+ return QFileSystemModel::headerData(section, orientation, role);
+ }
+ }
+
+ virtual QVariant data(const QModelIndex &index, int role) const {
+ if (index.column() == m_RegularColumnCount + 0) {
+ if (role == Qt::DisplayRole) {
+ return tr("not implemented");
+ } else {
+ return QVariant();
+ }
+ } else {
+ return QFileSystemModel::data(index, role);
+ }
+ }
+
+private:
+ mutable int m_RegularColumnCount;
+};
+
+
+OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget *parent)
+ : QDialog(parent), ui(new Ui::OverwriteInfoDialog), m_FileSystemModel(NULL),
+ m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL)
+{
+ ui->setupUi(this);
+
+ QString path = modInfo->absolutePath();
+ m_FileSystemModel = new MyFileSystemModel(this);
+ m_FileSystemModel->setReadOnly(false);
+ m_FileSystemModel->setRootPath(path);
+ ui->filesView->setModel(m_FileSystemModel);
+ ui->filesView->setRootIndex(m_FileSystemModel->index(path));
+ ui->filesView->setColumnWidth(0, 250);
+// ui->filesView->header()->hideSection(3);
+
+ m_DeleteAction = new QAction(tr("&Delete"), ui->filesView);
+ m_RenameAction = new QAction(tr("&Rename"), ui->filesView);
+ m_OpenAction = new QAction(tr("&Open"), ui->filesView);
+ m_NewFolderAction = new QAction(tr("&New Folder"), ui->filesView);
+ QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered()));
+ QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered()));
+ QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered()));
+ QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered()));
+}
+
+OverwriteInfoDialog::~OverwriteInfoDialog()
+{
+ delete ui;
+}
+
+
+bool OverwriteInfoDialog::recursiveDelete(const QModelIndex &index)
+{
+ for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) {
+ QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index);
+ if (m_FileSystemModel->isDir(childIndex)) {
+ if (!recursiveDelete(childIndex)) {
+ qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData());
+ return false;
+ }
+ } else {
+ if (!m_FileSystemModel->remove(childIndex)) {
+ qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData());
+ return false;
+ }
+ }
+ }
+ if (!m_FileSystemModel->remove(index)) {
+ qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData());
+ return false;
+ }
+ return true;
+}
+
+
+void OverwriteInfoDialog::deleteFile(const QModelIndex &index)
+{
+
+ bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index)
+ : m_FileSystemModel->remove(index);
+ if (!res) {
+ QString fileName = m_FileSystemModel->fileName(index);
+ reportError(tr("Failed to delete \"%1\"").arg(fileName));
+ }
+}
+
+
+void OverwriteInfoDialog::deleteTriggered()
+{
+ if (m_FileSelection.count() == 0) {
+ return;
+ } else if (m_FileSelection.count() == 1) {
+ QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0));
+ if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName),
+ QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
+ return;
+ }
+ } else {
+ if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"),
+ QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
+ return;
+ }
+ }
+
+ foreach(QModelIndex index, m_FileSelection) {
+ deleteFile(index);
+ }
+}
+
+
+void OverwriteInfoDialog::renameTriggered()
+{
+ QModelIndex selection = m_FileSelection.at(0);
+ QModelIndex index = selection.sibling(selection.row(), 0);
+ if (!index.isValid() || m_FileSystemModel->isReadOnly()) {
+ return;
+ }
+
+ ui->filesView->edit(index);
+}
+
+
+void OverwriteInfoDialog::openFile(const QModelIndex &index)
+{
+ QString fileName = m_FileSystemModel->filePath(index);
+
+ HINSTANCE res = ::ShellExecuteW(NULL, L"open", ToWString(fileName).c_str(), NULL, NULL, SW_SHOW);
+ if ((int)res <= 32) {
+ qCritical("failed to invoke %s: %d", fileName.toUtf8().constData(), res);
+ }
+}
+
+
+void OverwriteInfoDialog::openTriggered()
+{
+ foreach(QModelIndex idx, m_FileSelection) {
+ openFile(idx);
+ }
+}
+
+void OverwriteInfoDialog::createDirectoryTriggered()
+{
+ QModelIndex selection = m_FileSelection.at(0);
+
+ QModelIndex index = m_FileSystemModel->isDir(selection) ? selection
+ : selection.parent();
+ index = index.sibling(index.row(), 0);
+
+ QString name = tr("New Folder");
+ QString path = m_FileSystemModel->filePath(index).append("/");
+
+ QModelIndex existingIndex = m_FileSystemModel->index(path + name);
+ int suffix = 1;
+ while (existingIndex.isValid()) {
+ name = tr("New Folder") + QString::number(suffix++);
+ existingIndex = m_FileSystemModel->index(path + name);
+ }
+
+ QModelIndex newIndex = m_FileSystemModel->mkdir(index, name);
+ if (!newIndex.isValid()) {
+ reportError(tr("Failed to create \"%1\"").arg(name));
+ return;
+ }
+
+ ui->filesView->setCurrentIndex(newIndex);
+ ui->filesView->edit(newIndex);
+}
+
+
+void OverwriteInfoDialog::on_filesView_customContextMenuRequested(const QPoint &pos)
+{
+ QItemSelectionModel *selectionModel = ui->filesView->selectionModel();
+ m_FileSelection = selectionModel->selectedRows(0);
+
+// m_FileSelection = m_FileTree->indexAt(pos);
+ QMenu menu(ui->filesView);
+
+ menu.addAction(m_NewFolderAction);
+
+ bool hasFiles = false;
+
+ foreach(QModelIndex idx, m_FileSelection) {
+ if (m_FileSystemModel->fileInfo(idx).isFile()) {
+ hasFiles = true;
+ break;
+ }
+ }
+
+ if (selectionModel->hasSelection()) {
+ if (hasFiles) {
+ menu.addAction(m_OpenAction);
+ }
+ menu.addAction(m_RenameAction);
+ menu.addAction(m_DeleteAction);
+ } else {
+ m_FileSelection.clear();
+ m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0));
+ }
+ menu.exec(ui->filesView->mapToGlobal(pos));
+}
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index 72a8563b..486d6f42 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -17,835 +17,838 @@ 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 "pluginlist.h"
-#include "report.h"
-#include "inject.h"
-#include <utility.h>
-#include "settings.h"
-#include <gameinfo.h>
-#include <windows_error.h>
-
-#include <QtDebug>
-#include <QMessageBox>
-#include <QMimeData>
-#include <QCoreApplication>
-#include <QDir>
-#include <QTextCodec>
-#include <QFileInfo>
-#include <QListWidgetItem>
-#include <QString>
-#include <QApplication>
-#include <QKeyEvent>
-#include <QSortFilterProxyModel>
-
-#include <tchar.h>
-#include <ctime>
-#include <algorithm>
-#include <stdexcept>
-
-
-
-bool ByName(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
- return LHS.m_Name.toUpper() < RHS.m_Name.toUpper();
-}
-
-bool ByPriority(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
- if (LHS.m_IsMaster && !RHS.m_IsMaster) {
- return true;
- } else if (!LHS.m_IsMaster && RHS.m_IsMaster) {
- return false;
- } else {
- return LHS.m_Priority < RHS.m_Priority;
- }
-}
-
-bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
- QString lhsExtension = LHS.m_Name.right(3).toLower();
- QString rhsExtension = RHS.m_Name.right(3).toLower();
- if (lhsExtension != rhsExtension) {
- return lhsExtension == "esm";
- }
-
- return ::CompareFileTime(&LHS.m_Time, &RHS.m_Time) < 0;
-}
-
-PluginList::PluginList(QObject *parent)
- : QAbstractTableModel(parent), m_Modified(false),
- m_FontMetrics(QFont())
-{
-}
-
-
-QString PluginList::getColumnName(int column)
-{
- switch (column) {
- case COL_NAME: return tr("Name");
- case COL_PRIORITY: return tr("Priority");
- case COL_MODINDEX: return tr("Mod Index");
- default: return tr("unknown");
- }
-}
-
-
-QString PluginList::getColumnToolTip(int column)
-{
- switch (column) {
- case COL_NAME: return tr("Name of your mods");
- case COL_PRIORITY: return tr("Load priority of your mod. The higher, the more \"important\" it is and thus "
- "overwrites data from plugins with lower priority.");
- case COL_MODINDEX: return tr("The modindex determins the formids of objects originating from this mods.");
- default: return tr("unknown");
- }
-}
-
-
-void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseDirectory,
- const QString &pluginsFile, const QString &loadOrderFile,
- const QString &lockedOrderFile)
-{
- emit layoutAboutToBeChanged();
- m_ESPsByName.clear();
- m_ESPsByPriority.clear();
- m_ESPs.clear();
- std::vector<std::wstring> primaryPlugins = GameInfo::instance().getPrimaryPlugins();
-
- m_CurrentProfile = profileName;
-
- std::vector<FileEntry*> files = baseDirectory.getFiles();
- for (auto iter = files.begin(); iter != files.end(); ++iter) {
- FileEntry *current = *iter;
- if (current == NULL) {
- continue;
- }
- QString filename = QString::fromUtf16(current->getName().c_str());
- QString extension = filename.right(3).toLower();
-
- if ((extension == "esp") || (extension == "esm")) {
- bool forceEnabled = Settings::instance().forceEnableCoreFiles() &&
- std::find(primaryPlugins.begin(), primaryPlugins.end(), ToWString(filename.toLower())) != primaryPlugins.end();
-
- bool archive = false;
- FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive));
- m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName())));
- }
- }
-
- if (readLoadOrder(loadOrderFile)) {
- int maxPriority = 0;
- // assign known load orders
- for (std::vector<ESPInfo>::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) {
- std::map<QString, int>::const_iterator priorityIter = m_ESPLoadOrder.find(espIter->m_Name.toLower());
- if (priorityIter != m_ESPLoadOrder.end()) {
- if (priorityIter->second > maxPriority) {
- maxPriority = priorityIter->second;
- }
- espIter->m_Priority = priorityIter->second;
- } else {
- espIter->m_Priority = -1;
- }
- }
-
- ++maxPriority;
-
- // assign maximum priorities for plugins with unknown priority
- for (std::vector<ESPInfo>::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) {
- if (espIter->m_Priority == -1) {
- espIter->m_Priority = maxPriority++;
- }
- }
- } else {
- // no load order stored, determine by date
- std::sort(m_ESPs.begin(), m_ESPs.end(), ByDate);
-
- for (size_t i = 0; i < m_ESPs.size(); ++i) {
- m_ESPs[i].m_Priority = i;
- }
- }
-
- std::sort(m_ESPs.begin(), m_ESPs.end(), ByPriority); // first, sort by priority
- // remove gaps from the priorities so we can use them as array indices without overflow
- for (int i = 0; i < static_cast<int>(m_ESPs.size()); ++i) {
- m_ESPs[i].m_Priority = i;
- }
-
- std::sort(m_ESPs.begin(), m_ESPs.end(), ByName); // sort by name so alphabetical sorting works
-
- updateIndices();
-
- readEnabledFrom(pluginsFile);
-
- readLockedOrderFrom(lockedOrderFile);
-
- refreshLoadOrder();
-
- emit layoutChanged();
- emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), 1));
-}
-
-
-void PluginList::enableESP(const QString &name)
-{
- std::map<QString, int>::iterator iter = m_ESPsByName.find(name.toLower());
-
- if (iter != m_ESPsByName.end()) {
- m_ESPs[iter->second].m_Enabled = true;
- m_Modified = true;
- } else {
- reportError(tr("esp not found: %1").arg(name));
- }
-}
-
-
-void PluginList::enableAll()
-{
- for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
- iter->m_Enabled = true;
- }
- m_Modified = true;
- emit esplist_changed();
-}
-
-
-void PluginList::disableAll()
-{
- for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
- if (!iter->m_ForceEnabled) {
- iter->m_Enabled = false;
- }
- }
- m_Modified = true;
- emit esplist_changed();
-}
-
-
-bool PluginList::isEnabled(const QString &name)
-{
- std::map<QString, int>::iterator iter = m_ESPsByName.find(name.toLower());
-
- if (iter != m_ESPsByName.end()) {
- return m_ESPs[iter->second].m_Enabled;
- } else {
- return false;
- }
-}
-
-
-bool PluginList::isEnabled(int index)
-{
- return m_ESPs.at(index).m_Enabled;
-}
-
-
-bool PluginList::readLoadOrder(const QString &fileName)
-{
- std::set<QString> availableESPs;
- for (std::vector<ESPInfo>::const_iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
- availableESPs.insert(iter->m_Name.toLower());
- }
-
- m_ESPLoadOrder.clear();
-
- int priority = 0;
-
- std::vector<std::wstring> primaryPlugins = GameInfo::instance().getPrimaryPlugins();
- for (std::vector<std::wstring>::iterator iter = primaryPlugins.begin();
- iter != primaryPlugins.end(); ++iter) {
- if (availableESPs.find(ToQString(*iter)) != availableESPs.end()) {
- m_ESPLoadOrder[ToQString(*iter)] = priority++;
- }
- }
-
- QFile file(fileName);
- if (!file.open(QIODevice::ReadOnly)) {
- return false;
- }
- while (!file.atEnd()) {
- QByteArray line = file.readLine().trimmed();
- QString modName;
- if ((line.size() > 0) && (line.at(0) != '#')) {
- modName = QString::fromUtf8(line.constData()).toLower();
- }
-
- if ((modName.size() > 0) &&
- (m_ESPLoadOrder.find(modName) == m_ESPLoadOrder.end()) &&
- (availableESPs.find(modName) != availableESPs.end())) {
- m_ESPLoadOrder[modName] = priority++;
- }
- }
-
- file.close();
- return true;
-}
-
-
-void PluginList::readEnabledFrom(const QString &fileName)
-{
- for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
- if (!iter->m_ForceEnabled) {
- iter->m_Enabled = false;
- }
- iter->m_LoadOrder = -1;
- }
-
- QFile file(fileName);
- if (!file.exists()) {
- throw std::runtime_error(QObject::tr("failed to find \"%1\"").arg(fileName).toUtf8().constData());
- }
-
- file.open(QIODevice::ReadOnly);
- while (!file.atEnd()) {
- QByteArray line = file.readLine();
- QString modName;
- if ((line.size() > 0) && (line.at(0) != '#')) {
- modName = QString::fromUtf8(line.trimmed().constData());
- }
- if (modName.size() > 0) {
- std::map<QString, int>::iterator iter = m_ESPsByName.find(modName.toLower());
- if (iter != m_ESPsByName.end()) {
- m_ESPs[iter->second].m_Enabled = true;
- } else {
- qWarning("plugin %s not found", modName.toUtf8().constData());
- m_Modified = true;
- }
- }
- }
-
- file.close();
-}
-
-
-void PluginList::readLockedOrderFrom(const QString &fileName)
-{
- m_LockedOrder.clear();
-
- QFile file(fileName);
- if (!file.exists()) {
- // no locked load order, that's ok
- return;
- }
-
- file.open(QIODevice::ReadOnly);
- while (!file.atEnd()) {
- QByteArray line = file.readLine();
- if ((line.size() > 0) && (line.at(0) != '#')) {
- QList<QByteArray> fields = line.split('|');
- if (fields.count() == 2) {
- m_LockedOrder[QString::fromUtf8(fields.at(0))] = fields.at(1).trimmed().toInt();
- } else {
- reportError(tr("The file containing locked plugin indices is broken"));
- break;
- }
- }
- }
-
- file.close();
-}
-
-
-void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) const
-{
- QFile file(fileName);
- if (!file.open(QIODevice::WriteOnly)) {
- throw MyException(tr("failed to open output file: %1").arg(fileName));
- }
-
- QTextCodec *textCodec = writeUnchecked ? QTextCodec::codecForName("utf-8")
- : QTextCodec::codecForName("Windows-1252");
-
- if (textCodec == NULL) {
- QList<QByteArray> encodingList = QTextCodec::availableCodecs();
- QString encodings;
- QTextStream temp(&encodings);
- foreach (QByteArray encoding, encodingList) {
- temp << encoding << ", ";
- }
- qCritical("required string-encoding not supported. Available codecs: %s",
- encodings.toUtf8().constData());
-
- throw std::runtime_error(QObject::tr("encoding error, please report this as a bug and include the file "
- "mo_interface.log!").toUtf8().constData());
- }
-
- file.resize(0);
-
- file.write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n"));
-
- bool invalidFileNames = false;
-
- for (size_t i = 0; i < m_ESPs.size(); ++i) {
- int priority = m_ESPsByPriority[i];
- if ((m_ESPs[priority].m_Enabled || writeUnchecked) && !m_ESPs[priority].m_Removed) {
- //file.write(m_ESPs[priority].m_Name.toUtf8());
- if (!textCodec->canEncode(m_ESPs[priority].m_Name)) {
- invalidFileNames = true;
- qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData());
- } else {
- file.write(textCodec->fromUnicode(m_ESPs[priority].m_Name));
- }
- file.write("\r\n");
- }
- }
- file.close();
-
- if (invalidFileNames) {
- reportError(tr("Some of your plugins have invalid names! These plugins can not be loaded by the game. "
- "Please see mo_interface.log for a list of affected plugins and rename them."));
- }
-
- qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
-}
-
-
-void PluginList::writeLockedOrder(const QString &fileName) const
-{
- QFile file(fileName);
- if (!file.open(QIODevice::WriteOnly)) {
- throw MyException(tr("failed to open output file: %1").arg(fileName));
- }
-
- file.resize(0);
- file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
- for (auto iter = m_LockedOrder.begin(); iter != m_LockedOrder.end(); ++iter) {
- file.write(QString("%1|%2\r\n").arg(iter->first).arg(iter->second).toUtf8());
- }
- file.close();
-}
-
-
-void PluginList::saveTo(const QString &pluginFileName, const QString &loadOrderFileName, const QString &lockedOrderFileName, const QString& deleterFileName, bool hideUnchecked) const
-{
- if (!m_Modified) {
- return;
- }
-
- writePlugins(pluginFileName, false);
- writePlugins(loadOrderFileName, true);
- writeLockedOrder(lockedOrderFileName);
-
- if (hideUnchecked) {
- QFile deleterFile(deleterFileName);
- deleterFile.open(QIODevice::WriteOnly);
- deleterFile.resize(0);
- deleterFile.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
-
- for (size_t i = 0; i < m_ESPs.size(); ++i) {
- int priority = m_ESPsByPriority[i];
- if (m_ESPs[priority].m_Removed) {
- deleterFile.write(m_ESPs[priority].m_Name.toUtf8());
- deleterFile.write("\r\n");
- }
- }
- deleterFile.close();
- qDebug("%s saved", QDir::toNativeSeparators(deleterFileName).toUtf8().constData());
- }
-
- m_Modified = false;
-}
-
-
-bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure)
-{
- if (GameInfo::instance().getLoadOrderMechanism() != GameInfo::TYPE_FILETIME) {
- // nothing to do
- return true;
- }
-
- for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
- std::wstring espName = ToWString(iter->m_Name);
- const FileEntry *fileEntry = directoryStructure.findFile(espName);
- if (fileEntry != NULL) {
- QString fileName;
- bool archive = false;
- int originid = fileEntry->getOrigin(archive);
- fileName = QString("%1\\%2").arg(QDir::toNativeSeparators(ToQString(directoryStructure.getOriginByID(originid).getPath()))).arg(iter->m_Name);
-
- HANDLE file = ::CreateFile(ToWString(fileName).c_str(), GENERIC_READ | GENERIC_WRITE,
- 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (file == INVALID_HANDLE_VALUE) {
- if (::GetLastError() == ERROR_SHARING_VIOLATION) {
- // file is locked, probably the game is running
- return false;
- } else {
- throw windows_error(QObject::tr("failed to access %1").arg(fileName).toUtf8().constData());
- }
- }
-
- ULONGLONG temp = 0;
- temp = (145731ULL + iter->m_Priority) * 24 * 60 * 60 * 10000000ULL;
-
- FILETIME newWriteTime;
-
- newWriteTime.dwLowDateTime = (DWORD)(temp & 0xFFFFFFFF);
- newWriteTime.dwHighDateTime = (DWORD)(temp >> 32);
- iter->m_Time = newWriteTime;
- fileEntry->setFileTime(newWriteTime);
- if (!::SetFileTime(file, NULL, NULL, &newWriteTime)) {
- throw windows_error(QObject::tr("failed to set file time %1").arg(fileName).toUtf8().constData());
- }
-
- CloseHandle(file);
- }
- }
- return true;
-}
-
-bool PluginList::isESPLocked(int index) const
-{
- return m_LockedOrder.find(m_ESPs.at(index).m_Name.toLower()) != m_LockedOrder.end();
-}
-
-
-void PluginList::lockESPIndex(int index, bool lock)
-{
- if (lock) {
- m_LockedOrder[getName(index).toLower()] = m_ESPs.at(index).m_LoadOrder;
- } else {
- auto iter = m_LockedOrder.find(getName(index).toLower());
- if (iter != m_LockedOrder.end()) {
- m_LockedOrder.erase(iter);
- }
- }
- m_Modified = true;
- emit esplist_changed();
-}
-
-
-void PluginList::syncLoadOrder()
-{
- int loadOrder = 0;
- for (unsigned int i = 0; i < m_ESPs.size(); ++i) {
- int index = m_ESPsByPriority[i];
-
- if (m_ESPs[index].m_Enabled) {
- m_ESPs[index].m_LoadOrder = loadOrder++;
- } else {
- m_ESPs[index].m_LoadOrder = -1;
- }
- }
-}
-
-void PluginList::refreshLoadOrder()
-{
- syncLoadOrder();
-
- // set priorities according to locked load order
- std::map<int, QString> lockedLoadOrder;
- std::for_each(m_LockedOrder.begin(), m_LockedOrder.end(),
- [&lockedLoadOrder] (const std::pair<QString, int> &ele) { lockedLoadOrder[ele.second] = ele.first; });
-
- int targetPrio = 0;
- // this is guaranteed to iterate from lowest key (load order) to highest
- for (auto iter = lockedLoadOrder.begin(); iter != lockedLoadOrder.end(); ++iter) {
- auto nameIter = m_ESPsByName.find(iter->second);
- if (nameIter != m_ESPsByName.end()) {
- // locked esp exists
-
- // find the location to insert at
- while ((targetPrio < static_cast<int>(m_ESPs.size())) &&
- (m_ESPs[m_ESPsByPriority[targetPrio]].m_LoadOrder < iter->first)) {
- if (QString::compare(m_ESPs[m_ESPsByPriority[targetPrio]].m_Name, iter->second) != 0) {
- ++targetPrio;
- }
- }
- int temp = targetPrio;
- if (m_ESPs[nameIter->second].m_Priority != temp) {
- setPluginPriority(nameIter->second, temp);
- m_ESPs[nameIter->second].m_LoadOrder = iter->first;
- syncLoadOrder();
- m_Modified = true;
- }
- }
- }
-}
-
-
-void PluginList::updateIndices()
-{
- m_ESPsByName.clear();
- m_ESPsByPriority.clear();
- m_ESPsByPriority.resize(m_ESPs.size());
-
- for (unsigned int i = 0; i < m_ESPs.size(); ++i) {
- m_ESPsByName[m_ESPs[i].m_Name.toLower()] = i;
- m_ESPsByPriority[m_ESPs[i].m_Priority] = i;
- }
-}
-
-
-int PluginList::rowCount(const QModelIndex &parent) const
-{
- if (!parent.isValid()) {
- return m_ESPs.size();
- } else {
- return 0;
- }
-}
-
-int PluginList::columnCount(const QModelIndex &) const
-{
- return 3;
-}
-
-
-QVariant PluginList::data(const QModelIndex &modelIndex, int role) const
-{
- int index = modelIndex.row();
-
- if (role == Qt::DisplayRole) {
- switch (modelIndex.column()) {
- case COL_NAME: {
- return m_ESPs[index].m_Name;
- } break;
- case COL_PRIORITY: {
- if (m_ESPs[index].m_Priority == 0) {
- return tr("min");
- } else if (m_ESPs[index].m_Priority == m_ESPs.size() - 1) {
- return tr("max");
- } else {
- return QString::number(m_ESPs[index].m_Priority);
- }
- } break;
- case COL_MODINDEX: {
- if (m_ESPs[index].m_LoadOrder == -1) {
- return QString();
- } else {
- return QString("%1").arg(m_ESPs[index].m_LoadOrder, 2, 16, QChar('0')).toUpper();
- }
- } break;
- default: {
- return QVariant();
- } break;
- }
- } else if ((role == Qt::DecorationRole) && (modelIndex.column() == 0)) {
- if (m_LockedOrder.find(m_ESPs[index].m_Name.toLower()) != m_LockedOrder.end()) {
- return QIcon(":/MO/gui/locked");
- } else {
- return QVariant();
- }
- } else if ((role == Qt::CheckStateRole) && (modelIndex.column() == 0)) {
- return m_ESPs[index].m_Enabled ? Qt::Checked : Qt::Unchecked;
- } else if (role == Qt::TextAlignmentRole) {
- if (modelIndex.column() == 0) {
- return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
- } else {
- return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
- }
- } else if (role == Qt::ToolTipRole) {
- if (m_ESPs[index].m_ForceEnabled) {
- return tr("This plugin can't be disabled (enforced by the game)");
- } else {
- return tr("Origin: %1").arg(m_ESPs[index].m_OriginName);
- }
- } else {
- return QVariant();
- }
-}
-
-
-bool PluginList::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (role == Qt::CheckStateRole) {
- m_ESPs[index.row()].m_Enabled = value.toInt() == Qt::Checked;
-
- emit layoutAboutToBeChanged();
-
- refreshLoadOrder();
- m_Modified = true;
- emit esplist_changed();
-
- emit layoutChanged();
- return true;
- } else {
- return false;
- }
-}
-
-
-QVariant PluginList::headerData(int section, Qt::Orientation orientation,
- int role) const
-{
- if (orientation == Qt::Horizontal) {
- if (role == Qt::DisplayRole) {
- return getColumnName(section);
- } else if (role == Qt::ToolTipRole) {
- return getColumnToolTip(section);
- } else if (role == Qt::SizeHintRole) {
- QSize temp = m_FontMetrics.size(Qt::TextSingleLine, getColumnName(section));
- temp.rwidth() += 25;
- temp.rheight() += 12;
- return temp;
- }
- }
- return QAbstractItemModel::headerData(section, orientation, role);
-}
-
-
-Qt::ItemFlags PluginList::flags(const QModelIndex &modelIndex) const
-{
- int index = modelIndex.row();
- Qt::ItemFlags result = QAbstractTableModel::flags(modelIndex);
-
- if (modelIndex.isValid()) {
- if ((m_ESPs[index].m_ForceEnabled)) {
- result &= ~Qt::ItemIsEnabled;
- }
- result |= Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
- } else {
- result |= Qt::ItemIsDropEnabled;
- }
-
- return result;
-}
-
-
-void PluginList::setPluginPriority(int row, int &newPriority)
-{
- int newPriorityTemp = newPriority;
-
- QString sourceExtension = m_ESPs[row].m_Name.right(3).toLower();
- if (sourceExtension == "esp") {
- // don't allow esps to be moved above esms
- while ((newPriorityTemp < static_cast<int>(m_ESPsByPriority.size() - 1)) &&
- (m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_Name.right(3).toLower() == "esm")) {
- ++newPriorityTemp;
- }
- } else {
- // don't allow esms to be moved below esps
- while ((newPriorityTemp > 0) &&
- (m_ESPs[m_ESPsByPriority[newPriorityTemp - 1]].m_Name.right(3).toLower() == "esp")) {
- --newPriorityTemp;
- }
- // also don't allow "regular" esms to be moved above primary plugins
- while ((newPriorityTemp < static_cast<int>(m_ESPsByPriority.size() - 1)) &&
- (m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_ForceEnabled)) {
- ++newPriorityTemp;
- }
- }
-
- int oldPriority = m_ESPs[row].m_Priority;
- if (newPriorityTemp > oldPriority) {
- // priority is higher than the old, so the gap we left is in lower priorities
- for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) {
- --m_ESPs[m_ESPsByPriority[i]].m_Priority;
- }
- } else {
- for (int i = newPriorityTemp; i < oldPriority; ++i) {
- ++m_ESPs[m_ESPsByPriority[i]].m_Priority;
- }
- ++newPriority;
- }
-
- m_ESPs[row].m_Priority = newPriorityTemp;
- updateIndices();
-}
-
-
-void PluginList::changePluginPriority(std::vector<int> rows, int newPriority)
-{
- emit layoutAboutToBeChanged();
- // sort rows to insert by their old priority (ascending) and insert them move them in that order
- const std::vector<ESPInfo> &esp = m_ESPs;
- std::sort(rows.begin(), rows.end(),
- [&esp](const int &LHS, const int &RHS) {
- return esp[LHS].m_Priority < esp[RHS].m_Priority;
- });
-
- // odd stuff: if any of the dragged sources has priority lower than the destination then the
- // target idx is that of the row BELOW the dropped location, otherwise it's the one above. why?
- for (std::vector<int>::const_iterator iter = rows.begin();
- iter != rows.end(); ++iter) {
- if (m_ESPs[*iter].m_Priority < newPriority) {
- --newPriority;
- break;
- }
- }
-
- for (std::vector<int>::const_iterator iter = rows.begin(); iter != rows.end(); ++iter) {
- setPluginPriority(*iter, newPriority);
- }
- refreshLoadOrder();
-
- emit esplist_changed();
-
- m_Modified = true;
-
- emit layoutChanged();
-}
-
-
-bool PluginList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int, const QModelIndex &parent)
-{
- if (action == Qt::IgnoreAction) {
- return true;
- }
-
- QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist");
- QDataStream stream(&encoded, QIODevice::ReadOnly);
-
- std::vector<int> sourceRows;
-
- while (!stream.atEnd()) {
- int sourceRow, col;
- QMap<int, QVariant> roleDataMap;
- stream >> sourceRow >> col >> roleDataMap;
- if (col == 0) { // only add each row once
- sourceRows.push_back(sourceRow);
- }
- }
-
- if (row == -1) {
- row = parent.row();
- }
-
- int newPriority = 0;
-
- if ((row < 0) ||
- (row >= static_cast<int>(m_ESPs.size()))) {
- newPriority = m_ESPs.size();
- } else {
- newPriority = m_ESPs[row].m_Priority;
- }
- changePluginPriority(sourceRows, newPriority);
-
-
- return false;
-}
-
-
-bool PluginList::eventFilter(QObject *obj, QEvent *event)
-{
- if (event->type() == QEvent::KeyPress) {
- QAbstractItemView *itemView = qobject_cast<QAbstractItemView*>(obj);
- QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
- if ((itemView != NULL) &&
- (keyEvent->modifiers() == Qt::ControlModifier) &&
- ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) {
- QItemSelectionModel *selectionModel = itemView->selectionModel();
- const QSortFilterProxyModel *proxyModel = qobject_cast<const QSortFilterProxyModel*>(selectionModel->model());
- int diff = -1;
- if (((keyEvent->key() == Qt::Key_Up) && (proxyModel->sortOrder() == Qt::DescendingOrder)) ||
- ((keyEvent->key() == Qt::Key_Down) && (proxyModel->sortOrder() == Qt::AscendingOrder))) {
- diff = 1;
- }
- QModelIndexList rows = selectionModel->selectedRows();
- if (keyEvent->key() == Qt::Key_Down) {
- for (int i = 0; i < rows.size() / 2; ++i) {
- rows.swap(i, rows.size() - i - 1);
- }
- }
- foreach (QModelIndex idx, rows) {
- if (proxyModel != NULL) {
- idx = proxyModel->mapToSource(idx);
- }
- int newPriority = m_ESPs[idx.row()].m_Priority + diff;
- if ((newPriority >= 0) && (newPriority < rowCount())) {
- setPluginPriority(idx.row(), newPriority);
- emit dataChanged(this->index(idx.row(), 0), this->index(idx.row(), this->columnCount() - 1));
- }
- }
- refreshLoadOrder();
- return true;
- }
- }
- return QObject::eventFilter(obj, event);
-}
+#include "pluginlist.h"
+#include "report.h"
+#include "inject.h"
+#include <utility.h>
+#include "settings.h"
+#include <gameinfo.h>
+#include <windows_error.h>
+
+#include <QtDebug>
+#include <QMessageBox>
+#include <QMimeData>
+#include <QCoreApplication>
+#include <QDir>
+#include <QTextCodec>
+#include <QFileInfo>
+#include <QListWidgetItem>
+#include <QString>
+#include <QApplication>
+#include <QKeyEvent>
+#include <QSortFilterProxyModel>
+
+#include <tchar.h>
+#include <ctime>
+#include <algorithm>
+#include <stdexcept>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+bool ByName(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
+ return LHS.m_Name.toUpper() < RHS.m_Name.toUpper();
+}
+
+bool ByPriority(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
+ if (LHS.m_IsMaster && !RHS.m_IsMaster) {
+ return true;
+ } else if (!LHS.m_IsMaster && RHS.m_IsMaster) {
+ return false;
+ } else {
+ return LHS.m_Priority < RHS.m_Priority;
+ }
+}
+
+bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
+ QString lhsExtension = LHS.m_Name.right(3).toLower();
+ QString rhsExtension = RHS.m_Name.right(3).toLower();
+ if (lhsExtension != rhsExtension) {
+ return lhsExtension == "esm";
+ }
+
+ return ::CompareFileTime(&LHS.m_Time, &RHS.m_Time) < 0;
+}
+
+PluginList::PluginList(QObject *parent)
+ : QAbstractTableModel(parent), m_Modified(false),
+ m_FontMetrics(QFont())
+{
+}
+
+
+QString PluginList::getColumnName(int column)
+{
+ switch (column) {
+ case COL_NAME: return tr("Name");
+ case COL_PRIORITY: return tr("Priority");
+ case COL_MODINDEX: return tr("Mod Index");
+ default: return tr("unknown");
+ }
+}
+
+
+QString PluginList::getColumnToolTip(int column)
+{
+ switch (column) {
+ case COL_NAME: return tr("Name of your mods");
+ case COL_PRIORITY: return tr("Load priority of your mod. The higher, the more \"important\" it is and thus "
+ "overwrites data from plugins with lower priority.");
+ case COL_MODINDEX: return tr("The modindex determins the formids of objects originating from this mods.");
+ default: return tr("unknown");
+ }
+}
+
+
+void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseDirectory,
+ const QString &pluginsFile, const QString &loadOrderFile,
+ const QString &lockedOrderFile)
+{
+ emit layoutAboutToBeChanged();
+ m_ESPsByName.clear();
+ m_ESPsByPriority.clear();
+ m_ESPs.clear();
+ std::vector<std::wstring> primaryPlugins = GameInfo::instance().getPrimaryPlugins();
+
+ m_CurrentProfile = profileName;
+
+ std::vector<FileEntry*> files = baseDirectory.getFiles();
+ for (auto iter = files.begin(); iter != files.end(); ++iter) {
+ FileEntry *current = *iter;
+ if (current == NULL) {
+ continue;
+ }
+ QString filename = QString::fromUtf16(current->getName().c_str());
+ QString extension = filename.right(3).toLower();
+
+ if ((extension == "esp") || (extension == "esm")) {
+ bool forceEnabled = Settings::instance().forceEnableCoreFiles() &&
+ std::find(primaryPlugins.begin(), primaryPlugins.end(), ToWString(filename.toLower())) != primaryPlugins.end();
+
+ bool archive = false;
+ FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive));
+ m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName())));
+ }
+ }
+
+ if (readLoadOrder(loadOrderFile)) {
+ int maxPriority = 0;
+ // assign known load orders
+ for (std::vector<ESPInfo>::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) {
+ std::map<QString, int>::const_iterator priorityIter = m_ESPLoadOrder.find(espIter->m_Name.toLower());
+ if (priorityIter != m_ESPLoadOrder.end()) {
+ if (priorityIter->second > maxPriority) {
+ maxPriority = priorityIter->second;
+ }
+ espIter->m_Priority = priorityIter->second;
+ } else {
+ espIter->m_Priority = -1;
+ }
+ }
+
+ ++maxPriority;
+
+ // assign maximum priorities for plugins with unknown priority
+ for (std::vector<ESPInfo>::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) {
+ if (espIter->m_Priority == -1) {
+ espIter->m_Priority = maxPriority++;
+ }
+ }
+ } else {
+ // no load order stored, determine by date
+ std::sort(m_ESPs.begin(), m_ESPs.end(), ByDate);
+
+ for (size_t i = 0; i < m_ESPs.size(); ++i) {
+ m_ESPs[i].m_Priority = i;
+ }
+ }
+
+ std::sort(m_ESPs.begin(), m_ESPs.end(), ByPriority); // first, sort by priority
+ // remove gaps from the priorities so we can use them as array indices without overflow
+ for (int i = 0; i < static_cast<int>(m_ESPs.size()); ++i) {
+ m_ESPs[i].m_Priority = i;
+ }
+
+ std::sort(m_ESPs.begin(), m_ESPs.end(), ByName); // sort by name so alphabetical sorting works
+
+ updateIndices();
+
+ readEnabledFrom(pluginsFile);
+
+ readLockedOrderFrom(lockedOrderFile);
+
+ refreshLoadOrder();
+
+ emit layoutChanged();
+ emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), 1));
+}
+
+
+void PluginList::enableESP(const QString &name)
+{
+ std::map<QString, int>::iterator iter = m_ESPsByName.find(name.toLower());
+
+ if (iter != m_ESPsByName.end()) {
+ m_ESPs[iter->second].m_Enabled = true;
+ m_Modified = true;
+ } else {
+ reportError(tr("esp not found: %1").arg(name));
+ }
+}
+
+
+void PluginList::enableAll()
+{
+ for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
+ iter->m_Enabled = true;
+ }
+ m_Modified = true;
+ emit esplist_changed();
+}
+
+
+void PluginList::disableAll()
+{
+ for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
+ if (!iter->m_ForceEnabled) {
+ iter->m_Enabled = false;
+ }
+ }
+ m_Modified = true;
+ emit esplist_changed();
+}
+
+
+bool PluginList::isEnabled(const QString &name)
+{
+ std::map<QString, int>::iterator iter = m_ESPsByName.find(name.toLower());
+
+ if (iter != m_ESPsByName.end()) {
+ return m_ESPs[iter->second].m_Enabled;
+ } else {
+ return false;
+ }
+}
+
+
+bool PluginList::isEnabled(int index)
+{
+ return m_ESPs.at(index).m_Enabled;
+}
+
+
+bool PluginList::readLoadOrder(const QString &fileName)
+{
+ std::set<QString> availableESPs;
+ for (std::vector<ESPInfo>::const_iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
+ availableESPs.insert(iter->m_Name.toLower());
+ }
+
+ m_ESPLoadOrder.clear();
+
+ int priority = 0;
+
+ std::vector<std::wstring> primaryPlugins = GameInfo::instance().getPrimaryPlugins();
+ for (std::vector<std::wstring>::iterator iter = primaryPlugins.begin();
+ iter != primaryPlugins.end(); ++iter) {
+ if (availableESPs.find(ToQString(*iter)) != availableESPs.end()) {
+ m_ESPLoadOrder[ToQString(*iter)] = priority++;
+ }
+ }
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ return false;
+ }
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine().trimmed();
+ QString modName;
+ if ((line.size() > 0) && (line.at(0) != '#')) {
+ modName = QString::fromUtf8(line.constData()).toLower();
+ }
+
+ if ((modName.size() > 0) &&
+ (m_ESPLoadOrder.find(modName) == m_ESPLoadOrder.end()) &&
+ (availableESPs.find(modName) != availableESPs.end())) {
+ m_ESPLoadOrder[modName] = priority++;
+ }
+ }
+
+ file.close();
+ return true;
+}
+
+
+void PluginList::readEnabledFrom(const QString &fileName)
+{
+ for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
+ if (!iter->m_ForceEnabled) {
+ iter->m_Enabled = false;
+ }
+ iter->m_LoadOrder = -1;
+ }
+
+ QFile file(fileName);
+ if (!file.exists()) {
+ throw std::runtime_error(QObject::tr("failed to find \"%1\"").arg(fileName).toUtf8().constData());
+ }
+
+ file.open(QIODevice::ReadOnly);
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine();
+ QString modName;
+ if ((line.size() > 0) && (line.at(0) != '#')) {
+ modName = QString::fromUtf8(line.trimmed().constData());
+ }
+ if (modName.size() > 0) {
+ std::map<QString, int>::iterator iter = m_ESPsByName.find(modName.toLower());
+ if (iter != m_ESPsByName.end()) {
+ m_ESPs[iter->second].m_Enabled = true;
+ } else {
+ qWarning("plugin %s not found", modName.toUtf8().constData());
+ m_Modified = true;
+ }
+ }
+ }
+
+ file.close();
+}
+
+
+void PluginList::readLockedOrderFrom(const QString &fileName)
+{
+ m_LockedOrder.clear();
+
+ QFile file(fileName);
+ if (!file.exists()) {
+ // no locked load order, that's ok
+ return;
+ }
+
+ file.open(QIODevice::ReadOnly);
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine();
+ if ((line.size() > 0) && (line.at(0) != '#')) {
+ QList<QByteArray> fields = line.split('|');
+ if (fields.count() == 2) {
+ m_LockedOrder[QString::fromUtf8(fields.at(0))] = fields.at(1).trimmed().toInt();
+ } else {
+ reportError(tr("The file containing locked plugin indices is broken"));
+ break;
+ }
+ }
+ }
+
+ file.close();
+}
+
+
+void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) const
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ throw MyException(tr("failed to open output file: %1").arg(fileName));
+ }
+
+ QTextCodec *textCodec = writeUnchecked ? QTextCodec::codecForName("utf-8")
+ : QTextCodec::codecForName("Windows-1252");
+
+ if (textCodec == NULL) {
+ QList<QByteArray> encodingList = QTextCodec::availableCodecs();
+ QString encodings;
+ QTextStream temp(&encodings);
+ foreach (QByteArray encoding, encodingList) {
+ temp << encoding << ", ";
+ }
+ qCritical("required string-encoding not supported. Available codecs: %s",
+ encodings.toUtf8().constData());
+
+ throw std::runtime_error(QObject::tr("encoding error, please report this as a bug and include the file "
+ "mo_interface.log!").toUtf8().constData());
+ }
+
+ file.resize(0);
+
+ file.write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n"));
+
+ bool invalidFileNames = false;
+
+ for (size_t i = 0; i < m_ESPs.size(); ++i) {
+ int priority = m_ESPsByPriority[i];
+ if ((m_ESPs[priority].m_Enabled || writeUnchecked) && !m_ESPs[priority].m_Removed) {
+ //file.write(m_ESPs[priority].m_Name.toUtf8());
+ if (!textCodec->canEncode(m_ESPs[priority].m_Name)) {
+ invalidFileNames = true;
+ qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData());
+ } else {
+ file.write(textCodec->fromUnicode(m_ESPs[priority].m_Name));
+ }
+ file.write("\r\n");
+ }
+ }
+ file.close();
+
+ if (invalidFileNames) {
+ reportError(tr("Some of your plugins have invalid names! These plugins can not be loaded by the game. "
+ "Please see mo_interface.log for a list of affected plugins and rename them."));
+ }
+
+ qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
+}
+
+
+void PluginList::writeLockedOrder(const QString &fileName) const
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ throw MyException(tr("failed to open output file: %1").arg(fileName));
+ }
+
+ file.resize(0);
+ file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
+ for (auto iter = m_LockedOrder.begin(); iter != m_LockedOrder.end(); ++iter) {
+ file.write(QString("%1|%2\r\n").arg(iter->first).arg(iter->second).toUtf8());
+ }
+ file.close();
+}
+
+
+void PluginList::saveTo(const QString &pluginFileName, const QString &loadOrderFileName, const QString &lockedOrderFileName, const QString& deleterFileName, bool hideUnchecked) const
+{
+ if (!m_Modified) {
+ return;
+ }
+
+ writePlugins(pluginFileName, false);
+ writePlugins(loadOrderFileName, true);
+ writeLockedOrder(lockedOrderFileName);
+
+ if (hideUnchecked) {
+ QFile deleterFile(deleterFileName);
+ deleterFile.open(QIODevice::WriteOnly);
+ deleterFile.resize(0);
+ deleterFile.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
+
+ for (size_t i = 0; i < m_ESPs.size(); ++i) {
+ int priority = m_ESPsByPriority[i];
+ if (m_ESPs[priority].m_Removed) {
+ deleterFile.write(m_ESPs[priority].m_Name.toUtf8());
+ deleterFile.write("\r\n");
+ }
+ }
+ deleterFile.close();
+ qDebug("%s saved", QDir::toNativeSeparators(deleterFileName).toUtf8().constData());
+ }
+
+ m_Modified = false;
+}
+
+
+bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure)
+{
+ if (GameInfo::instance().getLoadOrderMechanism() != GameInfo::TYPE_FILETIME) {
+ // nothing to do
+ return true;
+ }
+
+ for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
+ std::wstring espName = ToWString(iter->m_Name);
+ const FileEntry *fileEntry = directoryStructure.findFile(espName);
+ if (fileEntry != NULL) {
+ QString fileName;
+ bool archive = false;
+ int originid = fileEntry->getOrigin(archive);
+ fileName = QString("%1\\%2").arg(QDir::toNativeSeparators(ToQString(directoryStructure.getOriginByID(originid).getPath()))).arg(iter->m_Name);
+
+ HANDLE file = ::CreateFile(ToWString(fileName).c_str(), GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ if (::GetLastError() == ERROR_SHARING_VIOLATION) {
+ // file is locked, probably the game is running
+ return false;
+ } else {
+ throw windows_error(QObject::tr("failed to access %1").arg(fileName).toUtf8().constData());
+ }
+ }
+
+ ULONGLONG temp = 0;
+ temp = (145731ULL + iter->m_Priority) * 24 * 60 * 60 * 10000000ULL;
+
+ FILETIME newWriteTime;
+
+ newWriteTime.dwLowDateTime = (DWORD)(temp & 0xFFFFFFFF);
+ newWriteTime.dwHighDateTime = (DWORD)(temp >> 32);
+ iter->m_Time = newWriteTime;
+ fileEntry->setFileTime(newWriteTime);
+ if (!::SetFileTime(file, NULL, NULL, &newWriteTime)) {
+ throw windows_error(QObject::tr("failed to set file time %1").arg(fileName).toUtf8().constData());
+ }
+
+ CloseHandle(file);
+ }
+ }
+ return true;
+}
+
+bool PluginList::isESPLocked(int index) const
+{
+ return m_LockedOrder.find(m_ESPs.at(index).m_Name.toLower()) != m_LockedOrder.end();
+}
+
+
+void PluginList::lockESPIndex(int index, bool lock)
+{
+ if (lock) {
+ m_LockedOrder[getName(index).toLower()] = m_ESPs.at(index).m_LoadOrder;
+ } else {
+ auto iter = m_LockedOrder.find(getName(index).toLower());
+ if (iter != m_LockedOrder.end()) {
+ m_LockedOrder.erase(iter);
+ }
+ }
+ m_Modified = true;
+ emit esplist_changed();
+}
+
+
+void PluginList::syncLoadOrder()
+{
+ int loadOrder = 0;
+ for (unsigned int i = 0; i < m_ESPs.size(); ++i) {
+ int index = m_ESPsByPriority[i];
+
+ if (m_ESPs[index].m_Enabled) {
+ m_ESPs[index].m_LoadOrder = loadOrder++;
+ } else {
+ m_ESPs[index].m_LoadOrder = -1;
+ }
+ }
+}
+
+void PluginList::refreshLoadOrder()
+{
+ syncLoadOrder();
+
+ // set priorities according to locked load order
+ std::map<int, QString> lockedLoadOrder;
+ std::for_each(m_LockedOrder.begin(), m_LockedOrder.end(),
+ [&lockedLoadOrder] (const std::pair<QString, int> &ele) { lockedLoadOrder[ele.second] = ele.first; });
+
+ int targetPrio = 0;
+ // this is guaranteed to iterate from lowest key (load order) to highest
+ for (auto iter = lockedLoadOrder.begin(); iter != lockedLoadOrder.end(); ++iter) {
+ auto nameIter = m_ESPsByName.find(iter->second);
+ if (nameIter != m_ESPsByName.end()) {
+ // locked esp exists
+
+ // find the location to insert at
+ while ((targetPrio < static_cast<int>(m_ESPs.size())) &&
+ (m_ESPs[m_ESPsByPriority[targetPrio]].m_LoadOrder < iter->first)) {
+ if (QString::compare(m_ESPs[m_ESPsByPriority[targetPrio]].m_Name, iter->second) != 0) {
+ ++targetPrio;
+ }
+ }
+ int temp = targetPrio;
+ if (m_ESPs[nameIter->second].m_Priority != temp) {
+ setPluginPriority(nameIter->second, temp);
+ m_ESPs[nameIter->second].m_LoadOrder = iter->first;
+ syncLoadOrder();
+ m_Modified = true;
+ }
+ }
+ }
+}
+
+
+void PluginList::updateIndices()
+{
+ m_ESPsByName.clear();
+ m_ESPsByPriority.clear();
+ m_ESPsByPriority.resize(m_ESPs.size());
+
+ for (unsigned int i = 0; i < m_ESPs.size(); ++i) {
+ m_ESPsByName[m_ESPs[i].m_Name.toLower()] = i;
+ m_ESPsByPriority[m_ESPs[i].m_Priority] = i;
+ }
+}
+
+
+int PluginList::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid()) {
+ return m_ESPs.size();
+ } else {
+ return 0;
+ }
+}
+
+int PluginList::columnCount(const QModelIndex &) const
+{
+ return 3;
+}
+
+
+QVariant PluginList::data(const QModelIndex &modelIndex, int role) const
+{
+ int index = modelIndex.row();
+
+ if (role == Qt::DisplayRole) {
+ switch (modelIndex.column()) {
+ case COL_NAME: {
+ return m_ESPs[index].m_Name;
+ } break;
+ case COL_PRIORITY: {
+ if (m_ESPs[index].m_Priority == 0) {
+ return tr("min");
+ } else if (m_ESPs[index].m_Priority == m_ESPs.size() - 1) {
+ return tr("max");
+ } else {
+ return QString::number(m_ESPs[index].m_Priority);
+ }
+ } break;
+ case COL_MODINDEX: {
+ if (m_ESPs[index].m_LoadOrder == -1) {
+ return QString();
+ } else {
+ return QString("%1").arg(m_ESPs[index].m_LoadOrder, 2, 16, QChar('0')).toUpper();
+ }
+ } break;
+ default: {
+ return QVariant();
+ } break;
+ }
+ } else if ((role == Qt::DecorationRole) && (modelIndex.column() == 0)) {
+ if (m_LockedOrder.find(m_ESPs[index].m_Name.toLower()) != m_LockedOrder.end()) {
+ return QIcon(":/MO/gui/locked");
+ } else {
+ return QVariant();
+ }
+ } else if ((role == Qt::CheckStateRole) && (modelIndex.column() == 0)) {
+ return m_ESPs[index].m_Enabled ? Qt::Checked : Qt::Unchecked;
+ } else if (role == Qt::TextAlignmentRole) {
+ if (modelIndex.column() == 0) {
+ return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
+ } else {
+ return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
+ }
+ } else if (role == Qt::ToolTipRole) {
+ if (m_ESPs[index].m_ForceEnabled) {
+ return tr("This plugin can't be disabled (enforced by the game)");
+ } else {
+ return tr("Origin: %1").arg(m_ESPs[index].m_OriginName);
+ }
+ } else {
+ return QVariant();
+ }
+}
+
+
+bool PluginList::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (role == Qt::CheckStateRole) {
+ m_ESPs[index.row()].m_Enabled = value.toInt() == Qt::Checked;
+
+ emit layoutAboutToBeChanged();
+
+ refreshLoadOrder();
+ m_Modified = true;
+ emit esplist_changed();
+
+ emit layoutChanged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+QVariant PluginList::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal) {
+ if (role == Qt::DisplayRole) {
+ return getColumnName(section);
+ } else if (role == Qt::ToolTipRole) {
+ return getColumnToolTip(section);
+ } else if (role == Qt::SizeHintRole) {
+ QSize temp = m_FontMetrics.size(Qt::TextSingleLine, getColumnName(section));
+ temp.rwidth() += 25;
+ temp.rheight() += 12;
+ return temp;
+ }
+ }
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+
+Qt::ItemFlags PluginList::flags(const QModelIndex &modelIndex) const
+{
+ int index = modelIndex.row();
+ Qt::ItemFlags result = QAbstractTableModel::flags(modelIndex);
+
+ if (modelIndex.isValid()) {
+ if ((m_ESPs[index].m_ForceEnabled)) {
+ result &= ~Qt::ItemIsEnabled;
+ }
+ result |= Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
+ } else {
+ result |= Qt::ItemIsDropEnabled;
+ }
+
+ return result;
+}
+
+
+void PluginList::setPluginPriority(int row, int &newPriority)
+{
+ int newPriorityTemp = newPriority;
+
+ QString sourceExtension = m_ESPs[row].m_Name.right(3).toLower();
+ if (sourceExtension == "esp") {
+ // don't allow esps to be moved above esms
+ while ((newPriorityTemp < static_cast<int>(m_ESPsByPriority.size() - 1)) &&
+ (m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_Name.right(3).toLower() == "esm")) {
+ ++newPriorityTemp;
+ }
+ } else {
+ // don't allow esms to be moved below esps
+ while ((newPriorityTemp > 0) &&
+ (m_ESPs[m_ESPsByPriority[newPriorityTemp - 1]].m_Name.right(3).toLower() == "esp")) {
+ --newPriorityTemp;
+ }
+ // also don't allow "regular" esms to be moved above primary plugins
+ while ((newPriorityTemp < static_cast<int>(m_ESPsByPriority.size() - 1)) &&
+ (m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_ForceEnabled)) {
+ ++newPriorityTemp;
+ }
+ }
+
+ int oldPriority = m_ESPs[row].m_Priority;
+ if (newPriorityTemp > oldPriority) {
+ // priority is higher than the old, so the gap we left is in lower priorities
+ for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) {
+ --m_ESPs[m_ESPsByPriority[i]].m_Priority;
+ }
+ } else {
+ for (int i = newPriorityTemp; i < oldPriority; ++i) {
+ ++m_ESPs[m_ESPsByPriority[i]].m_Priority;
+ }
+ ++newPriority;
+ }
+
+ m_ESPs[row].m_Priority = newPriorityTemp;
+ updateIndices();
+}
+
+
+void PluginList::changePluginPriority(std::vector<int> rows, int newPriority)
+{
+ emit layoutAboutToBeChanged();
+ // sort rows to insert by their old priority (ascending) and insert them move them in that order
+ const std::vector<ESPInfo> &esp = m_ESPs;
+ std::sort(rows.begin(), rows.end(),
+ [&esp](const int &LHS, const int &RHS) {
+ return esp[LHS].m_Priority < esp[RHS].m_Priority;
+ });
+
+ // odd stuff: if any of the dragged sources has priority lower than the destination then the
+ // target idx is that of the row BELOW the dropped location, otherwise it's the one above. why?
+ for (std::vector<int>::const_iterator iter = rows.begin();
+ iter != rows.end(); ++iter) {
+ if (m_ESPs[*iter].m_Priority < newPriority) {
+ --newPriority;
+ break;
+ }
+ }
+
+ for (std::vector<int>::const_iterator iter = rows.begin(); iter != rows.end(); ++iter) {
+ setPluginPriority(*iter, newPriority);
+ }
+ refreshLoadOrder();
+
+ emit esplist_changed();
+
+ m_Modified = true;
+
+ emit layoutChanged();
+}
+
+
+bool PluginList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int, const QModelIndex &parent)
+{
+ if (action == Qt::IgnoreAction) {
+ return true;
+ }
+
+ QByteArray encoded = mimeData->data("application/x-qabstractitemmodeldatalist");
+ QDataStream stream(&encoded, QIODevice::ReadOnly);
+
+ std::vector<int> sourceRows;
+
+ while (!stream.atEnd()) {
+ int sourceRow, col;
+ QMap<int, QVariant> roleDataMap;
+ stream >> sourceRow >> col >> roleDataMap;
+ if (col == 0) { // only add each row once
+ sourceRows.push_back(sourceRow);
+ }
+ }
+
+ if (row == -1) {
+ row = parent.row();
+ }
+
+ int newPriority = 0;
+
+ if ((row < 0) ||
+ (row >= static_cast<int>(m_ESPs.size()))) {
+ newPriority = m_ESPs.size();
+ } else {
+ newPriority = m_ESPs[row].m_Priority;
+ }
+ changePluginPriority(sourceRows, newPriority);
+
+
+ return false;
+}
+
+
+bool PluginList::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress) {
+ QAbstractItemView *itemView = qobject_cast<QAbstractItemView*>(obj);
+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
+ if ((itemView != NULL) &&
+ (keyEvent->modifiers() == Qt::ControlModifier) &&
+ ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) {
+ QItemSelectionModel *selectionModel = itemView->selectionModel();
+ const QSortFilterProxyModel *proxyModel = qobject_cast<const QSortFilterProxyModel*>(selectionModel->model());
+ int diff = -1;
+ if (((keyEvent->key() == Qt::Key_Up) && (proxyModel->sortOrder() == Qt::DescendingOrder)) ||
+ ((keyEvent->key() == Qt::Key_Down) && (proxyModel->sortOrder() == Qt::AscendingOrder))) {
+ diff = 1;
+ }
+ QModelIndexList rows = selectionModel->selectedRows();
+ if (keyEvent->key() == Qt::Key_Down) {
+ for (int i = 0; i < rows.size() / 2; ++i) {
+ rows.swap(i, rows.size() - i - 1);
+ }
+ }
+ foreach (QModelIndex idx, rows) {
+ if (proxyModel != NULL) {
+ idx = proxyModel->mapToSource(idx);
+ }
+ int newPriority = m_ESPs[idx.row()].m_Priority + diff;
+ if ((newPriority >= 0) && (newPriority < rowCount())) {
+ setPluginPriority(idx.row(), newPriority);
+ emit dataChanged(this->index(idx.row(), 0), this->index(idx.row(), this->columnCount() - 1));
+ }
+ }
+ refreshLoadOrder();
+ return true;
+ }
+ }
+ return QObject::eventFilter(obj, event);
+}
diff --git a/src/pluginlist.h b/src/pluginlist.h
index aa6d1426..a46fd4d5 100644
--- a/src/pluginlist.h
+++ b/src/pluginlist.h
@@ -59,7 +59,7 @@ public:
* @param baseDirectory the root directory structure representing the virtual data directory
* @todo the profile is not used? If it was, we should pass the Profile-object instead
**/
- void refresh(const QString &profileName, const DirectoryEntry &baseDirectory, const QString &pluginsFile, const QString &loadOrderFile, const QString &lockedOrderFile);
+ void refresh(const QString &profileName, const MOShared::DirectoryEntry &baseDirectory, const QString &pluginsFile, const QString &loadOrderFile, const QString &lockedOrderFile);
/**
* @brief enable a plugin based on its name
@@ -117,7 +117,7 @@ public:
* in different mods can also have different load orders which makes this very intransparent
* @note also stores to disk the list of locked esps
**/
- bool saveLoadOrder(DirectoryEntry &directoryStructure);
+ bool saveLoadOrder(MOShared::DirectoryEntry &directoryStructure);
QString getName(int index) const { return m_ESPs.at(index).m_Name; }
int getPriority(int index) const { return m_ESPs.at(index).m_Priority; }
diff --git a/src/profile.cpp b/src/profile.cpp
index 21cd1216..4892fd78 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -17,677 +17,679 @@ 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 "profile.h"
-#include "report.h"
-#include "gameinfo.h"
-#include "windows_error.h"
-#include "dummybsa.h"
-#include "modinfo.h"
-#include <utility.h>
-#include <util.h>
-#include <appconfig.h>
-#include <QMessageBox>
-#include <QApplication>
-#include <QSettings>
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <shlobj.h>
-#include <stdexcept>
-
-
-Profile::Profile()
-{
- initTimer();
-}
-
-Profile::Profile(const QString &name, bool useDefaultSettings)
-{
- QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()));
- QDir profileBase(profilesDir);
-
- if (!profileBase.exists() || !profileBase.mkdir(name)) {
- throw std::runtime_error(QObject::tr("failed to create %1").arg(name).toUtf8().constData());
- }
- QString fullPath = profilesDir + "/" + name;
- m_Directory = QDir(fullPath);
- QFile modList(m_Directory.filePath("modlist.txt"));
- if (!modList.open(QIODevice::ReadWrite)) {
- profileBase.rmdir(name);
- throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath("modlist.txt")).toUtf8().constData());
- }
- modList.close();
-
- try {
- GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings);
- } catch (...) {
- // clean up in case of an error
- removeDir(profileBase.absoluteFilePath(name));
- throw;
- }
- initTimer();
- refreshModStatus();
-}
-
-
-Profile::Profile(const QDir& directory)
- : m_Directory(directory)
-{
- if (!QFile::exists(m_Directory.filePath("modlist.txt"))) {
- throw std::runtime_error(QObject::tr("modlist.txt missing").toUtf8().constData());
- }
-
- GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath()));
-
- if (!QFile::exists(getIniFileName())) {
- reportError(QObject::tr("\"%1\" is missing").arg(getIniFileName()));
- }
- initTimer();
- refreshModStatus();
-}
-
-
-Profile::Profile(const Profile& reference)
- : m_Directory(reference.m_Directory)
-{
- initTimer();
- refreshModStatus();
-}
-
-
-Profile::~Profile()
-{
- writeModlistNow();
-}
-
-
-void Profile::initTimer()
-{
- m_SaveTimer = new QTimer(this);
- m_SaveTimer->setSingleShot(true);
- connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow()));
-}
-
-
-bool Profile::exists() const
-{
- return m_Directory.exists();
-}
-
-
-void Profile::writeModlist() const
-{
- if (!m_SaveTimer->isActive()) {
- m_SaveTimer->start(2000);
- }
-}
-
-
-void Profile::cancelWriteModlist() const
-{
- m_SaveTimer->stop();
-}
-
-
-void Profile::writeModlistNow() const
-{
- m_SaveTimer->stop();
-#pragma message("right now, we're doing unnecessary saves. We need a flag that says that mod priority, enabled-state or name of a mod has changed")
-
- QString fileName = getModlistFileName();
- QFile file(fileName);
- file.open(QIODevice::WriteOnly);
- file.resize(0);
- file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
- if (m_ModStatus.empty()) {
- return;
- }
-
- for (int i = m_ModStatus.size() - 1; i >= 0; --i) {
- // the priority order was inverted on load so it has to be inverted again
- unsigned int index = m_ModIndexByPriority[i];
- if (index != UINT_MAX) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
- if (modInfo->getFixedPriority() == INT_MIN) {
- if (m_ModStatus[index].m_Enabled) {
- file.write("+");
- } else {
- file.write("-");
- }
- file.write(modInfo->name().toUtf8());
- file.write("\r\n");
- }
- }
- }
-
- file.close();
- qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
-}
-
-
-void Profile::createTweakedIniFile()
-{
- QFileInfo iniInfo(getIniFileName());
-
- QString tweakedIni = iniInfo.absolutePath() + "/initweaks.ini";
-// QFile iniFile(tweakedIni);
-
- // workaround: the fallout nv launcher seems to mark the file read-only. crazy...
-/* ::SetFileAttributesW(ToWString(tweakedIni).c_str(), FILE_ATTRIBUTE_NORMAL);
- QFile(tweakedIni).remove();
- if (!iniFile.copy(tweakedIni)) {
- reportError(tr("failed to apply ini tweaks").append(": ").append(iniFile.errorString()));
- return;
- }*/
-
- QFile::remove(tweakedIni); // remove the old ini tweaks
-
- for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
- if (m_ModStatus[i].m_Enabled) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
- mergeTweaks(modInfo, tweakedIni);
- }
- }
-}
-
-
-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()));
- }
-
- bool modStatusModified = false;
- m_ModStatus.clear();
- m_ModStatus.resize(ModInfo::getNumMods());
-
- std::set<QString> namesRead;
-
- // load mods from file and update enabled state and priority for them
- file.open(QIODevice::ReadOnly);
- int index = 0;
- while (!file.atEnd()) {
- QByteArray line = file.readLine();
- bool enabled = true;
- QString modName;
- if (line.length() == 0) {
- // empty line
- continue;
- } else if (line.at(0) == '#') {
- // comment line
- continue;
- } else if (line.at(0) == '-') {
- enabled = false;
- modName = QString::fromUtf8(line.mid(1).trimmed().constData());
- } else if (line.at(0) == '+') {
- modName = QString::fromUtf8(line.mid(1).trimmed().constData());
- } else {
- modName = QString::fromUtf8(line.trimmed().constData());
- }
- if (modName.size() > 0) {
- if (namesRead.find(modName) != namesRead.end()) {
- continue;
- } else {
- namesRead.insert(modName);
- }
- unsigned int modindex = ModInfo::getIndex(modName);
- if (modindex != UINT_MAX) {
- ModInfo::Ptr info = ModInfo::getByIndex(modindex);
- if ((modindex < m_ModStatus.size()) && (info->getFixedPriority() == INT_MIN)) {
- m_ModStatus[modindex].m_Enabled = enabled;
- if (m_ModStatus[modindex].m_Priority == -1) {
- if (static_cast<size_t>(index) >= m_ModStatus.size()) {
- throw MyException(tr("invalid index %1").arg(index));
- }
- m_ModStatus[modindex].m_Priority = index++;
- }
- } else {
- qDebug("mod \"%s\" (profile \"%s\") not found",
- modName.toUtf8().constData(), m_Directory.path().toUtf8().constData());
- // need to rewrite the modlist to fix this
- modStatusModified = true;
- }
- }
- } else {
- // line was empty after trimming
- }
- }
-
- int numKnownMods = index;
-
- // invert priority order to match that of the pluginlist. Also
- // give priorities to mods not referenced in the profile
- for (size_t i = 0; i < m_ModStatus.size(); ++i) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
- if (!modInfo->canBeEnabled()) {
- continue;
- }
- if (m_ModStatus[i].m_Priority != -1) {
- m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1;
- } else {
- if (static_cast<size_t>(index) >= m_ModStatus.size()) {
- throw MyException(tr("invalid index %1").arg(index));
- }
- m_ModStatus[i].m_Priority = index++;
- // also, mark the mod-list as changed
- modStatusModified = true;
- }
- }
-
- file.close();
- updateIndices();
- if (modStatusModified) {
- writeModlist();
- }
-}
-
-
-void Profile::dumpModStatus() const
-{
- for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
- ModInfo::Ptr info = ModInfo::getByIndex(i);
- qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority,
- m_ModStatus[i].m_Enabled ? "enabled" : "disabled");
- }
-}
-
-
-void Profile::updateIndices()
-{
- m_NumRegularMods = 0;
- m_ModIndexByPriority.clear();
- m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX);
- for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
- int priority = m_ModStatus[i].m_Priority;
- if (priority < 0) {
- continue;
- } else if (priority >= static_cast<int>(m_ModIndexByPriority.size())) {
- qCritical("invalid priority %d for mod", priority);
- continue;
- } else {
- ++m_NumRegularMods;
- m_ModIndexByPriority.at(priority) = i;
- }
- }
-}
-
-
-std::vector<std::tuple<QString, QString, int> > Profile::getActiveMods()
-{
- std::vector<std::tuple<QString, QString, int> > result;
- for (std::vector<unsigned int>::const_iterator iter = m_ModIndexByPriority.begin();
- iter != m_ModIndexByPriority.end(); ++iter) {
- if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter);
- result.push_back(std::make_tuple(modInfo->name(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority));
- }
- }
-
- unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool {
- std::vector<ModInfo::EFlag> flags = mod->getFlags();
- return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); });
-
- if (overwriteIndex != UINT_MAX) {
- ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex);
- result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX));
- } else {
- reportError(tr("Overwrite directory couldn't be parsed"));
- }
- return result;
-}
-
-
-unsigned int Profile::modIndexByPriority(unsigned int priority) const
-{
- if (priority >= m_ModStatus.size()) {
- throw MyException(tr("invalid priority %1").arg(priority));
- }
-
- return m_ModIndexByPriority[priority];
-}
-
-
-void Profile::setModEnabled(unsigned int index, bool enabled)
-{
- if (index >= m_ModStatus.size()) {
- throw MyException(tr("invalid index %1").arg(index));
- }
-
- if (m_ModStatus[index].m_Overwrite) {
- // overwrite is always enabled
- return;
- }
-
- if (enabled != m_ModStatus[index].m_Enabled) {
- m_ModStatus[index].m_Enabled = enabled;
- emit modStatusChanged(index);
- }
-}
-
-
-bool Profile::modEnabled(unsigned int index) const
-{
- if (index >= m_ModStatus.size()) {
- throw MyException(tr("invalid index %1").arg(index));
- }
-
- return m_ModStatus[index].m_Enabled;
-}
-
-
-int Profile::getModPriority(unsigned int index) const
-{
- if (index >= m_ModStatus.size()) {
- throw MyException(tr("invalid index %1").arg(index));
- }
-
- return m_ModStatus[index].m_Priority;
-}
-
-
-void Profile::setModPriority(unsigned int index, int &newPriority)
-{
- int newPriorityTemp = (std::max)(0, (std::min<int>)(m_ModStatus.size() - 1, newPriority));
- if (m_ModStatus[index].m_Overwrite) {
- // can't change priority of the overwrite
- return;
- }
- int oldPriority = m_ModStatus[index].m_Priority;
- if (newPriorityTemp > oldPriority) {
- // priority is higher than the old, so the gap we left is in lower priorities
- for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) {
- --m_ModStatus[m_ModIndexByPriority[i]].m_Priority;
- }
- } else {
- for (int i = newPriorityTemp; i < oldPriority; ++i) {
- ++m_ModStatus[m_ModIndexByPriority[i]].m_Priority;
- }
- ++newPriority;
- }
-
- m_ModStatus[index].m_Priority = newPriorityTemp;
-
- updateIndices();
- writeModlist();
-}
-
-
-Profile Profile::createFrom(const QString &name, const Profile &reference)
-{
- QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name);
- reference.copyFilesTo(profileDirectory);
- return Profile(QDir(profileDirectory));
-}
-
-
-void Profile::copyFilesTo(QString &target) const
-{
- copyDir(m_Directory.absolutePath(), target, false);
-}
-
-
-std::vector<std::wstring> Profile::splitDZString(const wchar_t *buffer) const
-{
- std::vector<std::wstring> result;
- const wchar_t *pos = buffer;
- size_t length = wcslen(pos);
- while (length != 0U) {
- result.push_back(pos);
- pos += length + 1;
- length = wcslen(pos);
- }
- return result;
-}
-
-
-void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const
-{
- static const int bufferSize = 32768;
-
- std::wstring tweakNameW = ToWString(tweakName);
- std::wstring tweakedIniW = ToWString(tweakedIni);
- QScopedArrayPointer<wchar_t> buffer(new wchar_t[bufferSize]);
-
- // retrieve a list of sections
- DWORD size = ::GetPrivateProfileSectionNamesW(
- buffer.data(), bufferSize, tweakNameW.c_str());
-
- if (size == bufferSize - 2) {
- // unfortunately there is no good way to find the required size
- // of the buffer
- throw MyException(QString("Buffer too small. Please report this as a bug. "
- "For now you might want to split up %1").arg(tweakName));
- }
-
- std::vector<std::wstring> sections = splitDZString(buffer.data());
-
- // now iterate over all sections and retrieve a list of keys in each
- for (std::vector<std::wstring>::iterator iter = sections.begin();
- iter != sections.end(); ++iter) {
- // retrieve the names of all keys
- size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(),
- bufferSize, tweakNameW.c_str());
- if (size == bufferSize - 2) {
- throw MyException(QString("Buffer too small. Please report this as a bug. "
- "For now you might want to split up %1").arg(tweakName));
- }
-
- std::vector<std::wstring> keys = splitDZString(buffer.data());
-
- for (std::vector<std::wstring>::iterator keyIter = keys.begin();
- keyIter != keys.end(); ++keyIter) {
- //TODO this treats everything as strings but how could I differentiate the type?
- ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(),
- NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str());
- ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(),
- buffer.data(), tweakedIniW.c_str());
- }
- }
-}
-
-void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const
-{
- std::vector<QString> iniTweaks = modInfo->getIniTweaks();
- for (std::vector<QString>::iterator iter = iniTweaks.begin();
- iter != iniTweaks.end(); ++iter) {
- mergeTweak(*iter, tweakedIni);
- }
-
- ::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str());
-
- if (localSavesEnabled()) {
- ::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str());
-
- ::WritePrivateProfileStringW(L"General", L"SLocalSavePath",
- AppConfig::localSavePlaceholder(),
- ToWString(tweakedIni).c_str());
- }
-}
-
-
-bool Profile::invalidationActive(bool *supported) const
-{
- if (GameInfo::instance().requiresBSAInvalidation()) {
- *supported = true;
- wchar_t buffer[1024];
- std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
- if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
- L"", buffer, 1024, iniFileName.c_str()) == 0) {
- if (errno != 0x02) {
- if (supported != NULL) {
- *supported = false;
- }
- return false;
- } else {
- qCritical("failed to parse \"%ls\"", iniFileName.c_str());
- QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError());
- throw windows_error(errorMessage.toUtf8().constData());
- }
- }
- QStringList archives = ToQString(buffer).split(',');
-
- for (int i = 0; i < archives.count(); ++i) {
- QString bsaName = archives.at(i).trimmed();
- if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) {
- return true;
- }
- }
- } else {
- *supported = false;
- }
- return false;
-}
-
-
-void Profile::deactivateInvalidation() const
-{
- if (GameInfo::instance().requiresBSAInvalidation()) {
- wchar_t buffer[1024];
- std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
- if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
- L"", buffer, 1024, iniFileName.c_str()) == 0) {
- if (errno == 0x02) {
- QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError());
- throw windows_error(errorMessage.toUtf8().constData());
- } else {
- return;
- }
- }
- QStringList archives = ToQString(buffer).split(", ");
-
- for (int i = 0; i < archives.count();) {
- QString bsaName = archives.at(i).trimmed();
- if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) {
- archives.removeAt(i);
- } else {
- ++i;
- }
- }
-
- 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");
- }
- }
-}
-
-
-void Profile::activateInvalidation(const QString& dataDirectory) const
-{
- if (GameInfo::instance().requiresBSAInvalidation()) {
- wchar_t buffer[1024];
- std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
- if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
- L"", buffer, 1024, iniFileName.c_str()) == 0) {
- throw windows_error("failed to parse ini file");
- }
- QStringList archives = ToQString(buffer).split(", ");
-
- QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA());
-
- if (!archives.contains(invalidationBSA)) {
- archives.insert(0, invalidationBSA);
- }
-
- 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 bsaFile = dataDirectory + "/" + invalidationBSA;
- if (!QFile::exists(bsaFile)) {
- DummyBSA bsa;
- bsa.write(bsaFile);
- }
- }
-}
-
-
-bool Profile::localSavesEnabled() const
-{
- return m_Directory.exists("saves");
-}
-
-
-bool Profile::enableLocalSaves(bool enable)
-{
- if (enable) {
- if (m_Directory.exists("_saves")) {
- m_Directory.rename("_saves", "saves");
- } else {
- m_Directory.mkdir("saves");
- }
- } else {
-
- QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"),
- tr("Do you want to delete local savegames? (If you select \"No\", the save games "
- "will show up again if you re-enable local savegames)"),
- QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
- QMessageBox::Cancel);
- if (res == QMessageBox::Yes) {
- removeDir(m_Directory.absoluteFilePath("_saves"));
- } else if (res == QMessageBox::No) {
- m_Directory.rename("saves", "_saves");
- } else {
- return false;
- }
- }
-
- // default: assume success
- return true;
-}
-
-
-QString Profile::getModlistFileName() const
-{
- return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt"));
-}
-
-QString Profile::getPluginsFileName() const
-{
- return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt"));
-}
-
-
-QString Profile::getLoadOrderFileName() const
-{
- return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt"));
-}
-
-
-QString Profile::getLockedOrderFileName() const
-{
- return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt"));
-}
-
-
-QString Profile::getArchivesFileName() const
-{
- return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt"));
-}
-
-QString Profile::getDeleterFileName() const
-{
- return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt"));
-}
-
-
-QString Profile::getIniFileName() const
-{
- std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin());
- return m_Directory.absoluteFilePath(ToQString(primaryIniFile));
-}
-
-QString Profile::getPath() const
-{
- return QDir::cleanPath(m_Directory.absolutePath());
-}
+#include "profile.h"
+#include "report.h"
+#include "gameinfo.h"
+#include "windows_error.h"
+#include "dummybsa.h"
+#include "modinfo.h"
+#include <utility.h>
+#include <util.h>
+#include <appconfig.h>
+#include <QMessageBox>
+#include <QApplication>
+#include <QSettings>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shlobj.h>
+#include <stdexcept>
+
+using namespace MOBase;
+using namespace MOShared;
+
+Profile::Profile()
+{
+ initTimer();
+}
+
+Profile::Profile(const QString &name, bool useDefaultSettings)
+{
+ QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()));
+ QDir profileBase(profilesDir);
+
+ if (!profileBase.exists() || !profileBase.mkdir(name)) {
+ throw std::runtime_error(QObject::tr("failed to create %1").arg(name).toUtf8().constData());
+ }
+ QString fullPath = profilesDir + "/" + name;
+ m_Directory = QDir(fullPath);
+ QFile modList(m_Directory.filePath("modlist.txt"));
+ if (!modList.open(QIODevice::ReadWrite)) {
+ profileBase.rmdir(name);
+ throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath("modlist.txt")).toUtf8().constData());
+ }
+ modList.close();
+
+ try {
+ GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings);
+ } catch (...) {
+ // clean up in case of an error
+ removeDir(profileBase.absoluteFilePath(name));
+ throw;
+ }
+ initTimer();
+ refreshModStatus();
+}
+
+
+Profile::Profile(const QDir& directory)
+ : m_Directory(directory)
+{
+ if (!QFile::exists(m_Directory.filePath("modlist.txt"))) {
+ throw std::runtime_error(QObject::tr("modlist.txt missing").toUtf8().constData());
+ }
+
+ GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath()));
+
+ if (!QFile::exists(getIniFileName())) {
+ reportError(QObject::tr("\"%1\" is missing").arg(getIniFileName()));
+ }
+ initTimer();
+ refreshModStatus();
+}
+
+
+Profile::Profile(const Profile& reference)
+ : m_Directory(reference.m_Directory)
+{
+ initTimer();
+ refreshModStatus();
+}
+
+
+Profile::~Profile()
+{
+ writeModlistNow();
+}
+
+
+void Profile::initTimer()
+{
+ m_SaveTimer = new QTimer(this);
+ m_SaveTimer->setSingleShot(true);
+ connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow()));
+}
+
+
+bool Profile::exists() const
+{
+ return m_Directory.exists();
+}
+
+
+void Profile::writeModlist() const
+{
+ if (!m_SaveTimer->isActive()) {
+ m_SaveTimer->start(2000);
+ }
+}
+
+
+void Profile::cancelWriteModlist() const
+{
+ m_SaveTimer->stop();
+}
+
+
+void Profile::writeModlistNow() const
+{
+ m_SaveTimer->stop();
+#pragma message("right now, we're doing unnecessary saves. We need a flag that says that mod priority, enabled-state or name of a mod has changed")
+
+ QString fileName = getModlistFileName();
+ QFile file(fileName);
+ file.open(QIODevice::WriteOnly);
+ file.resize(0);
+ file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
+ if (m_ModStatus.empty()) {
+ return;
+ }
+
+ for (int i = m_ModStatus.size() - 1; i >= 0; --i) {
+ // the priority order was inverted on load so it has to be inverted again
+ unsigned int index = m_ModIndexByPriority[i];
+ if (index != UINT_MAX) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+ if (modInfo->getFixedPriority() == INT_MIN) {
+ if (m_ModStatus[index].m_Enabled) {
+ file.write("+");
+ } else {
+ file.write("-");
+ }
+ file.write(modInfo->name().toUtf8());
+ file.write("\r\n");
+ }
+ }
+ }
+
+ file.close();
+ qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
+}
+
+
+void Profile::createTweakedIniFile()
+{
+ QFileInfo iniInfo(getIniFileName());
+
+ QString tweakedIni = iniInfo.absolutePath() + "/initweaks.ini";
+// QFile iniFile(tweakedIni);
+
+ // workaround: the fallout nv launcher seems to mark the file read-only. crazy...
+/* ::SetFileAttributesW(ToWString(tweakedIni).c_str(), FILE_ATTRIBUTE_NORMAL);
+ QFile(tweakedIni).remove();
+ if (!iniFile.copy(tweakedIni)) {
+ reportError(tr("failed to apply ini tweaks").append(": ").append(iniFile.errorString()));
+ return;
+ }*/
+
+ QFile::remove(tweakedIni); // remove the old ini tweaks
+
+ for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
+ if (m_ModStatus[i].m_Enabled) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
+ mergeTweaks(modInfo, tweakedIni);
+ }
+ }
+}
+
+
+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()));
+ }
+
+ bool modStatusModified = false;
+ m_ModStatus.clear();
+ m_ModStatus.resize(ModInfo::getNumMods());
+
+ std::set<QString> namesRead;
+
+ // load mods from file and update enabled state and priority for them
+ file.open(QIODevice::ReadOnly);
+ int index = 0;
+ while (!file.atEnd()) {
+ QByteArray line = file.readLine();
+ bool enabled = true;
+ QString modName;
+ if (line.length() == 0) {
+ // empty line
+ continue;
+ } else if (line.at(0) == '#') {
+ // comment line
+ continue;
+ } else if (line.at(0) == '-') {
+ enabled = false;
+ modName = QString::fromUtf8(line.mid(1).trimmed().constData());
+ } else if (line.at(0) == '+') {
+ modName = QString::fromUtf8(line.mid(1).trimmed().constData());
+ } else {
+ modName = QString::fromUtf8(line.trimmed().constData());
+ }
+ if (modName.size() > 0) {
+ if (namesRead.find(modName) != namesRead.end()) {
+ continue;
+ } else {
+ namesRead.insert(modName);
+ }
+ unsigned int modindex = ModInfo::getIndex(modName);
+ if (modindex != UINT_MAX) {
+ ModInfo::Ptr info = ModInfo::getByIndex(modindex);
+ if ((modindex < m_ModStatus.size()) && (info->getFixedPriority() == INT_MIN)) {
+ m_ModStatus[modindex].m_Enabled = enabled;
+ if (m_ModStatus[modindex].m_Priority == -1) {
+ if (static_cast<size_t>(index) >= m_ModStatus.size()) {
+ throw MyException(tr("invalid index %1").arg(index));
+ }
+ m_ModStatus[modindex].m_Priority = index++;
+ }
+ } else {
+ qDebug("mod \"%s\" (profile \"%s\") not found",
+ modName.toUtf8().constData(), m_Directory.path().toUtf8().constData());
+ // need to rewrite the modlist to fix this
+ modStatusModified = true;
+ }
+ }
+ } else {
+ // line was empty after trimming
+ }
+ }
+
+ int numKnownMods = index;
+
+ // invert priority order to match that of the pluginlist. Also
+ // give priorities to mods not referenced in the profile
+ for (size_t i = 0; i < m_ModStatus.size(); ++i) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
+ if (!modInfo->canBeEnabled()) {
+ continue;
+ }
+ if (m_ModStatus[i].m_Priority != -1) {
+ m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1;
+ } else {
+ if (static_cast<size_t>(index) >= m_ModStatus.size()) {
+ throw MyException(tr("invalid index %1").arg(index));
+ }
+ m_ModStatus[i].m_Priority = index++;
+ // also, mark the mod-list as changed
+ modStatusModified = true;
+ }
+ }
+
+ file.close();
+ updateIndices();
+ if (modStatusModified) {
+ writeModlist();
+ }
+}
+
+
+void Profile::dumpModStatus() const
+{
+ for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
+ ModInfo::Ptr info = ModInfo::getByIndex(i);
+ qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority,
+ m_ModStatus[i].m_Enabled ? "enabled" : "disabled");
+ }
+}
+
+
+void Profile::updateIndices()
+{
+ m_NumRegularMods = 0;
+ m_ModIndexByPriority.clear();
+ m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX);
+ for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
+ int priority = m_ModStatus[i].m_Priority;
+ if (priority < 0) {
+ continue;
+ } else if (priority >= static_cast<int>(m_ModIndexByPriority.size())) {
+ qCritical("invalid priority %d for mod", priority);
+ continue;
+ } else {
+ ++m_NumRegularMods;
+ m_ModIndexByPriority.at(priority) = i;
+ }
+ }
+}
+
+
+std::vector<std::tuple<QString, QString, int> > Profile::getActiveMods()
+{
+ std::vector<std::tuple<QString, QString, int> > result;
+ for (std::vector<unsigned int>::const_iterator iter = m_ModIndexByPriority.begin();
+ iter != m_ModIndexByPriority.end(); ++iter) {
+ if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter);
+ result.push_back(std::make_tuple(modInfo->name(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority));
+ }
+ }
+
+ unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool {
+ std::vector<ModInfo::EFlag> flags = mod->getFlags();
+ return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); });
+
+ if (overwriteIndex != UINT_MAX) {
+ ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex);
+ result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX));
+ } else {
+ reportError(tr("Overwrite directory couldn't be parsed"));
+ }
+ return result;
+}
+
+
+unsigned int Profile::modIndexByPriority(unsigned int priority) const
+{
+ if (priority >= m_ModStatus.size()) {
+ throw MyException(tr("invalid priority %1").arg(priority));
+ }
+
+ return m_ModIndexByPriority[priority];
+}
+
+
+void Profile::setModEnabled(unsigned int index, bool enabled)
+{
+ if (index >= m_ModStatus.size()) {
+ throw MyException(tr("invalid index %1").arg(index));
+ }
+
+ if (m_ModStatus[index].m_Overwrite) {
+ // overwrite is always enabled
+ return;
+ }
+
+ if (enabled != m_ModStatus[index].m_Enabled) {
+ m_ModStatus[index].m_Enabled = enabled;
+ emit modStatusChanged(index);
+ }
+}
+
+
+bool Profile::modEnabled(unsigned int index) const
+{
+ if (index >= m_ModStatus.size()) {
+ throw MyException(tr("invalid index %1").arg(index));
+ }
+
+ return m_ModStatus[index].m_Enabled;
+}
+
+
+int Profile::getModPriority(unsigned int index) const
+{
+ if (index >= m_ModStatus.size()) {
+ throw MyException(tr("invalid index %1").arg(index));
+ }
+
+ return m_ModStatus[index].m_Priority;
+}
+
+
+void Profile::setModPriority(unsigned int index, int &newPriority)
+{
+ int newPriorityTemp = (std::max)(0, (std::min<int>)(m_ModStatus.size() - 1, newPriority));
+ if (m_ModStatus[index].m_Overwrite) {
+ // can't change priority of the overwrite
+ return;
+ }
+ int oldPriority = m_ModStatus[index].m_Priority;
+ if (newPriorityTemp > oldPriority) {
+ // priority is higher than the old, so the gap we left is in lower priorities
+ for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) {
+ --m_ModStatus[m_ModIndexByPriority[i]].m_Priority;
+ }
+ } else {
+ for (int i = newPriorityTemp; i < oldPriority; ++i) {
+ ++m_ModStatus[m_ModIndexByPriority[i]].m_Priority;
+ }
+ ++newPriority;
+ }
+
+ m_ModStatus[index].m_Priority = newPriorityTemp;
+
+ updateIndices();
+ writeModlist();
+}
+
+
+Profile Profile::createFrom(const QString &name, const Profile &reference)
+{
+ QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name);
+ reference.copyFilesTo(profileDirectory);
+ return Profile(QDir(profileDirectory));
+}
+
+
+void Profile::copyFilesTo(QString &target) const
+{
+ copyDir(m_Directory.absolutePath(), target, false);
+}
+
+
+std::vector<std::wstring> Profile::splitDZString(const wchar_t *buffer) const
+{
+ std::vector<std::wstring> result;
+ const wchar_t *pos = buffer;
+ size_t length = wcslen(pos);
+ while (length != 0U) {
+ result.push_back(pos);
+ pos += length + 1;
+ length = wcslen(pos);
+ }
+ return result;
+}
+
+
+void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const
+{
+ static const int bufferSize = 32768;
+
+ std::wstring tweakNameW = ToWString(tweakName);
+ std::wstring tweakedIniW = ToWString(tweakedIni);
+ QScopedArrayPointer<wchar_t> buffer(new wchar_t[bufferSize]);
+
+ // retrieve a list of sections
+ DWORD size = ::GetPrivateProfileSectionNamesW(
+ buffer.data(), bufferSize, tweakNameW.c_str());
+
+ if (size == bufferSize - 2) {
+ // unfortunately there is no good way to find the required size
+ // of the buffer
+ throw MyException(QString("Buffer too small. Please report this as a bug. "
+ "For now you might want to split up %1").arg(tweakName));
+ }
+
+ std::vector<std::wstring> sections = splitDZString(buffer.data());
+
+ // now iterate over all sections and retrieve a list of keys in each
+ for (std::vector<std::wstring>::iterator iter = sections.begin();
+ iter != sections.end(); ++iter) {
+ // retrieve the names of all keys
+ size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(),
+ bufferSize, tweakNameW.c_str());
+ if (size == bufferSize - 2) {
+ throw MyException(QString("Buffer too small. Please report this as a bug. "
+ "For now you might want to split up %1").arg(tweakName));
+ }
+
+ std::vector<std::wstring> keys = splitDZString(buffer.data());
+
+ for (std::vector<std::wstring>::iterator keyIter = keys.begin();
+ keyIter != keys.end(); ++keyIter) {
+ //TODO this treats everything as strings but how could I differentiate the type?
+ ::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(),
+ NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str());
+ ::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(),
+ buffer.data(), tweakedIniW.c_str());
+ }
+ }
+}
+
+void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const
+{
+ std::vector<QString> iniTweaks = modInfo->getIniTweaks();
+ for (std::vector<QString>::iterator iter = iniTweaks.begin();
+ iter != iniTweaks.end(); ++iter) {
+ mergeTweak(*iter, tweakedIni);
+ }
+
+ ::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str());
+
+ if (localSavesEnabled()) {
+ ::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str());
+
+ ::WritePrivateProfileStringW(L"General", L"SLocalSavePath",
+ AppConfig::localSavePlaceholder(),
+ ToWString(tweakedIni).c_str());
+ }
+}
+
+
+bool Profile::invalidationActive(bool *supported) const
+{
+ if (GameInfo::instance().requiresBSAInvalidation()) {
+ *supported = true;
+ wchar_t buffer[1024];
+ std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
+ if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
+ L"", buffer, 1024, iniFileName.c_str()) == 0) {
+ if (errno != 0x02) {
+ if (supported != NULL) {
+ *supported = false;
+ }
+ return false;
+ } else {
+ qCritical("failed to parse \"%ls\"", iniFileName.c_str());
+ QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError());
+ throw windows_error(errorMessage.toUtf8().constData());
+ }
+ }
+ QStringList archives = ToQString(buffer).split(',');
+
+ for (int i = 0; i < archives.count(); ++i) {
+ QString bsaName = archives.at(i).trimmed();
+ if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) {
+ return true;
+ }
+ }
+ } else {
+ *supported = false;
+ }
+ return false;
+}
+
+
+void Profile::deactivateInvalidation() const
+{
+ if (GameInfo::instance().requiresBSAInvalidation()) {
+ wchar_t buffer[1024];
+ std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
+ if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
+ L"", buffer, 1024, iniFileName.c_str()) == 0) {
+ if (errno == 0x02) {
+ QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError());
+ throw windows_error(errorMessage.toUtf8().constData());
+ } else {
+ return;
+ }
+ }
+ QStringList archives = ToQString(buffer).split(", ");
+
+ for (int i = 0; i < archives.count();) {
+ QString bsaName = archives.at(i).trimmed();
+ if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) {
+ archives.removeAt(i);
+ } else {
+ ++i;
+ }
+ }
+
+ 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");
+ }
+ }
+}
+
+
+void Profile::activateInvalidation(const QString& dataDirectory) const
+{
+ if (GameInfo::instance().requiresBSAInvalidation()) {
+ wchar_t buffer[1024];
+ std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
+ if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
+ L"", buffer, 1024, iniFileName.c_str()) == 0) {
+ throw windows_error("failed to parse ini file");
+ }
+ QStringList archives = ToQString(buffer).split(", ");
+
+ QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA());
+
+ if (!archives.contains(invalidationBSA)) {
+ archives.insert(0, invalidationBSA);
+ }
+
+ 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 bsaFile = dataDirectory + "/" + invalidationBSA;
+ if (!QFile::exists(bsaFile)) {
+ DummyBSA bsa;
+ bsa.write(bsaFile);
+ }
+ }
+}
+
+
+bool Profile::localSavesEnabled() const
+{
+ return m_Directory.exists("saves");
+}
+
+
+bool Profile::enableLocalSaves(bool enable)
+{
+ if (enable) {
+ if (m_Directory.exists("_saves")) {
+ m_Directory.rename("_saves", "saves");
+ } else {
+ m_Directory.mkdir("saves");
+ }
+ } else {
+
+ QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"),
+ tr("Do you want to delete local savegames? (If you select \"No\", the save games "
+ "will show up again if you re-enable local savegames)"),
+ QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+ QMessageBox::Cancel);
+ if (res == QMessageBox::Yes) {
+ removeDir(m_Directory.absoluteFilePath("_saves"));
+ } else if (res == QMessageBox::No) {
+ m_Directory.rename("saves", "_saves");
+ } else {
+ return false;
+ }
+ }
+
+ // default: assume success
+ return true;
+}
+
+
+QString Profile::getModlistFileName() const
+{
+ return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt"));
+}
+
+QString Profile::getPluginsFileName() const
+{
+ return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt"));
+}
+
+
+QString Profile::getLoadOrderFileName() const
+{
+ return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt"));
+}
+
+
+QString Profile::getLockedOrderFileName() const
+{
+ return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt"));
+}
+
+
+QString Profile::getArchivesFileName() const
+{
+ return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt"));
+}
+
+QString Profile::getDeleterFileName() const
+{
+ return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt"));
+}
+
+
+QString Profile::getIniFileName() const
+{
+ std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin());
+ return m_Directory.absoluteFilePath(ToQString(primaryIniFile));
+}
+
+QString Profile::getPath() const
+{
+ return QDir::cleanPath(m_Directory.absolutePath());
+}
diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp
index 3aceeb01..cbf50dfc 100644
--- a/src/profilesdialog.cpp
+++ b/src/profilesdialog.cpp
@@ -17,258 +17,262 @@ 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 "profilesdialog.h"
-#include "ui_profilesdialog.h"
-#include "profile.h"
-#include "report.h"
-#include "utility.h"
-#include "transfersavesdialog.h"
-#include "profileinputdialog.h"
-#include "mainwindow.h"
-#include <gameinfo.h>
-#include <QListWidgetItem>
-#include <QInputDialog>
-#include <QLineEdit>
-#include <QDirIterator>
-#include <QMessageBox>
-#include <QWhatsThis>
-
-
-ProfilesDialog::ProfilesDialog(const QString &gamePath, QWidget *parent)
- : TutorableDialog("Profiles", parent), ui(new Ui::ProfilesDialog), m_GamePath(gamePath), m_FailState(false)
-{
- ui->setupUi(this);
-
- QDir profilesDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())));
- profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
- m_ProfilesList = findChild<QListWidget*>("profilesList");
-
- QDirIterator profileIter(profilesDir);
-
- while (profileIter.hasNext()) {
- profileIter.next();
- addItem(profileIter.filePath());
- }
-
- QCheckBox *invalidationBox = findChild<QCheckBox*>("invalidationBox");
- if (!GameInfo::instance().requiresBSAInvalidation()) {
- invalidationBox->setToolTip(tr("Archive invalidation isn't required for this game."));
- invalidationBox->setEnabled(false);
- }
-}
-
-ProfilesDialog::~ProfilesDialog()
-{
- delete ui;
-}
-
-void ProfilesDialog::showEvent(QShowEvent *event)
-{
- TutorableDialog::showEvent(event);
-
- if (m_ProfilesList->count() == 0) {
- QPoint pos = m_ProfilesList->mapToGlobal(QPoint(0, 0));
- pos.rx() += m_ProfilesList->width() / 2;
- pos.ry() += (m_ProfilesList->height() / 2) - 20;
- QWhatsThis::showText(pos,
- QObject::tr("Before you can use ModOrganizer, you need to create at least one profile. "
- "ATTENTION: Run the game at least once before creating a profile!"), m_ProfilesList);
- }
-}
-
-void ProfilesDialog::on_closeButton_clicked()
-{
- close();
-}
-
-
-void ProfilesDialog::addItem(const QString &name)
-{
- try {
- QVariant temp;
- QDir profileDir(name);
- temp.setValue(Profile(profileDir));
- QListWidgetItem *newItem = new QListWidgetItem(profileDir.dirName(), m_ProfilesList);
- newItem->setData(Qt::UserRole, temp);
- m_FailState = false;
- } catch (const std::exception& e) {
- reportError(tr("failed to create profile: %1").arg(e.what()));
- }
-}
-
-
-void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings)
-{
- try {
- QVariant temp;
- temp.setValue(Profile(name, useDefaultSettings));
- QListWidget *profilesList = findChild<QListWidget*>("profilesList");
- QListWidgetItem *newItem = new QListWidgetItem(name, profilesList);
- newItem->setData(Qt::UserRole, temp);
- profilesList->addItem(newItem);
- m_FailState = false;
- } catch (const std::exception&) {
- m_FailState = true;
- throw;
- }
-}
-
-
-void ProfilesDialog::createProfile(const QString &name, const Profile &reference)
-{
- try {
- Profile newProfile = Profile::createFrom(name, reference);
-
- QVariant temp;
- temp.setValue(newProfile);
- QListWidget *profilesList = findChild<QListWidget*>("profilesList");
- QListWidgetItem *newItem = new QListWidgetItem(name, profilesList);
- newItem->setData(Qt::UserRole, temp);
- profilesList->addItem(newItem);
- m_FailState = false;
- } catch (const std::exception&) {
- m_FailState = true;
- throw;
- }
-}
-
-
-void ProfilesDialog::on_addProfileButton_clicked()
-{
- ProfileInputDialog dialog(this);
- bool okClicked = dialog.exec();
- QString name = dialog.getName();
-
- if (okClicked && (name.size() > 0)) {
- try {
- createProfile(name, dialog.getPreferDefaultSettings());
- } catch (const std::exception &e) {
- reportError(tr("failed to create profile: %1").arg(e.what()));
- }
- }
-}
-
-void ProfilesDialog::on_copyProfileButton_clicked()
-{
- bool okClicked;
- QString name = QInputDialog::getText(this, tr("Name"), tr("Please enter a name for the new profile"), QLineEdit::Normal, QString(), &okClicked);
- if (okClicked && (name.size() > 0)) {
- QListWidget *profilesList = findChild<QListWidget*>("profilesList");
-
- try {
- const Profile &currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile>();
- createProfile(name, currentProfile);
- } catch (const std::exception &e) {
- reportError(tr("failed to copy profile: %1").arg(e.what()));
- }
- }
-}
-
-void ProfilesDialog::on_removeProfileButton_clicked()
-{
- QMessageBox confirmBox(QMessageBox::Question, tr("Confirm"), tr("Are you sure you want to remove this profile?"),
- QMessageBox::Yes | QMessageBox::No);
-
- if (confirmBox.exec() == QMessageBox::Yes) {
- QListWidget *profilesList = findChild<QListWidget*>("profilesList");
-
- const Profile &currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile>();
-
- // on destruction, the profile object would write the profile.ini file again, so
- // we have to get rid of the it before deleting the directory
- QString profilePath = currentProfile.getPath();
- QListWidgetItem* item = profilesList->takeItem(profilesList->currentRow());
- if (item != NULL) {
- delete item;
- }
- removeDir(profilePath);
- }
-}
-
-
-void ProfilesDialog::on_invalidationBox_stateChanged(int state)
-{
- QListWidget *profilesList = findChild<QListWidget*>("profilesList");
-
- QListWidgetItem *currentItem = profilesList->currentItem();
- if (currentItem == NULL) {
- return;
- }
- if (!ui->invalidationBox->isEnabled()) {
- return;
- }
- try {
- QVariant currentProfileVariant = currentItem->data(Qt::UserRole);
- if (!currentProfileVariant.isValid() || currentProfileVariant.isNull()) {
- return;
- }
- const Profile &currentProfile = currentItem->data(Qt::UserRole).value<Profile>();
- if (state == Qt::Unchecked) {
- currentProfile.deactivateInvalidation();
- } else {
- currentProfile.activateInvalidation(m_GamePath + "/data");
- }
- } catch (const std::exception &e) {
- reportError(tr("failed to change archive invalidation state: %1").arg(e.what()));
- }
-}
-
-
-void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current, QListWidgetItem*)
-{
- QCheckBox *invalidationBox = findChild<QCheckBox*>("invalidationBox");
- QCheckBox *localSavesBox = findChild<QCheckBox*>("localSavesBox");
- QPushButton *copyButton = findChild<QPushButton*>("copyProfileButton");
- QPushButton *removeButton = findChild<QPushButton*>("removeProfileButton");
- QPushButton *transferButton = findChild<QPushButton*>("transferButton");
-
- if (current != NULL) {
- const Profile &currentProfile = current->data(Qt::UserRole).value<Profile>();
-
- try {
- bool invalidationSupported = false;
- invalidationBox->blockSignals(true);
- invalidationBox->setChecked(currentProfile.invalidationActive(&invalidationSupported));
- invalidationBox->setEnabled(invalidationSupported);
- invalidationBox->blockSignals(false);
-
- bool localSaves = currentProfile.localSavesEnabled();
- transferButton->setEnabled(localSaves);
- // prevent the stateChanged-event for the saves-box from triggering, otherwise it may think local saves
- // were disabled and delete the files/rename the dir
- localSavesBox->blockSignals(true);
- localSavesBox->setChecked(localSaves);
- localSavesBox->blockSignals(false);
-
- copyButton->setEnabled(true);
- removeButton->setEnabled(true);
- } catch (const std::exception& E) {
- reportError(tr("failed to determine if invalidation is active: %1").arg(E.what()));
- copyButton->setEnabled(false);
- removeButton->setEnabled(false);
- invalidationBox->setChecked(false);
- }
- } else {
- invalidationBox->setChecked(false);
- copyButton->setEnabled(false);
- removeButton->setEnabled(false);
- }
-}
-
-void ProfilesDialog::on_localSavesBox_stateChanged(int state)
-{
- Profile &currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
- if (currentProfile.enableLocalSaves(state == Qt::Checked)) {
- ui->transferButton->setEnabled(state == Qt::Checked);
- } else {
- // revert checkbox-state
- ui->localSavesBox->setChecked(state != Qt::Checked);
- }
-}
-
-void ProfilesDialog::on_transferButton_clicked()
-{
- const Profile &currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
- TransferSavesDialog transferDialog(currentProfile, this);
- transferDialog.exec();
-}
+#include "profilesdialog.h"
+#include "ui_profilesdialog.h"
+#include "profile.h"
+#include "report.h"
+#include "utility.h"
+#include "transfersavesdialog.h"
+#include "profileinputdialog.h"
+#include "mainwindow.h"
+#include <gameinfo.h>
+#include <QListWidgetItem>
+#include <QInputDialog>
+#include <QLineEdit>
+#include <QDirIterator>
+#include <QMessageBox>
+#include <QWhatsThis>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+ProfilesDialog::ProfilesDialog(const QString &gamePath, QWidget *parent)
+ : TutorableDialog("Profiles", parent), ui(new Ui::ProfilesDialog), m_GamePath(gamePath), m_FailState(false)
+{
+ ui->setupUi(this);
+
+ QDir profilesDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())));
+ profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
+ m_ProfilesList = findChild<QListWidget*>("profilesList");
+
+ QDirIterator profileIter(profilesDir);
+
+ while (profileIter.hasNext()) {
+ profileIter.next();
+ addItem(profileIter.filePath());
+ }
+
+ QCheckBox *invalidationBox = findChild<QCheckBox*>("invalidationBox");
+ if (!GameInfo::instance().requiresBSAInvalidation()) {
+ invalidationBox->setToolTip(tr("Archive invalidation isn't required for this game."));
+ invalidationBox->setEnabled(false);
+ }
+}
+
+ProfilesDialog::~ProfilesDialog()
+{
+ delete ui;
+}
+
+void ProfilesDialog::showEvent(QShowEvent *event)
+{
+ TutorableDialog::showEvent(event);
+
+ if (m_ProfilesList->count() == 0) {
+ QPoint pos = m_ProfilesList->mapToGlobal(QPoint(0, 0));
+ pos.rx() += m_ProfilesList->width() / 2;
+ pos.ry() += (m_ProfilesList->height() / 2) - 20;
+ QWhatsThis::showText(pos,
+ QObject::tr("Before you can use ModOrganizer, you need to create at least one profile. "
+ "ATTENTION: Run the game at least once before creating a profile!"), m_ProfilesList);
+ }
+}
+
+void ProfilesDialog::on_closeButton_clicked()
+{
+ close();
+}
+
+
+void ProfilesDialog::addItem(const QString &name)
+{
+ try {
+ QVariant temp;
+ QDir profileDir(name);
+ temp.setValue(Profile(profileDir));
+ QListWidgetItem *newItem = new QListWidgetItem(profileDir.dirName(), m_ProfilesList);
+ newItem->setData(Qt::UserRole, temp);
+ m_FailState = false;
+ } catch (const std::exception& e) {
+ reportError(tr("failed to create profile: %1").arg(e.what()));
+ }
+}
+
+
+void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings)
+{
+ try {
+ QVariant temp;
+ temp.setValue(Profile(name, useDefaultSettings));
+ QListWidget *profilesList = findChild<QListWidget*>("profilesList");
+ QListWidgetItem *newItem = new QListWidgetItem(name, profilesList);
+ newItem->setData(Qt::UserRole, temp);
+ profilesList->addItem(newItem);
+ m_FailState = false;
+ } catch (const std::exception&) {
+ m_FailState = true;
+ throw;
+ }
+}
+
+
+void ProfilesDialog::createProfile(const QString &name, const Profile &reference)
+{
+ try {
+ Profile newProfile = Profile::createFrom(name, reference);
+
+ QVariant temp;
+ temp.setValue(newProfile);
+ QListWidget *profilesList = findChild<QListWidget*>("profilesList");
+ QListWidgetItem *newItem = new QListWidgetItem(name, profilesList);
+ newItem->setData(Qt::UserRole, temp);
+ profilesList->addItem(newItem);
+ m_FailState = false;
+ } catch (const std::exception&) {
+ m_FailState = true;
+ throw;
+ }
+}
+
+
+void ProfilesDialog::on_addProfileButton_clicked()
+{
+ ProfileInputDialog dialog(this);
+ bool okClicked = dialog.exec();
+ QString name = dialog.getName();
+
+ if (okClicked && (name.size() > 0)) {
+ try {
+ createProfile(name, dialog.getPreferDefaultSettings());
+ } catch (const std::exception &e) {
+ reportError(tr("failed to create profile: %1").arg(e.what()));
+ }
+ }
+}
+
+void ProfilesDialog::on_copyProfileButton_clicked()
+{
+ bool okClicked;
+ QString name = QInputDialog::getText(this, tr("Name"), tr("Please enter a name for the new profile"), QLineEdit::Normal, QString(), &okClicked);
+ if (okClicked && (name.size() > 0)) {
+ QListWidget *profilesList = findChild<QListWidget*>("profilesList");
+
+ try {
+ const Profile &currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile>();
+ createProfile(name, currentProfile);
+ } catch (const std::exception &e) {
+ reportError(tr("failed to copy profile: %1").arg(e.what()));
+ }
+ }
+}
+
+void ProfilesDialog::on_removeProfileButton_clicked()
+{
+ QMessageBox confirmBox(QMessageBox::Question, tr("Confirm"), tr("Are you sure you want to remove this profile?"),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (confirmBox.exec() == QMessageBox::Yes) {
+ QListWidget *profilesList = findChild<QListWidget*>("profilesList");
+
+ const Profile &currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile>();
+
+ // on destruction, the profile object would write the profile.ini file again, so
+ // we have to get rid of the it before deleting the directory
+ QString profilePath = currentProfile.getPath();
+ QListWidgetItem* item = profilesList->takeItem(profilesList->currentRow());
+ if (item != NULL) {
+ delete item;
+ }
+ removeDir(profilePath);
+ }
+}
+
+
+void ProfilesDialog::on_invalidationBox_stateChanged(int state)
+{
+ QListWidget *profilesList = findChild<QListWidget*>("profilesList");
+
+ QListWidgetItem *currentItem = profilesList->currentItem();
+ if (currentItem == NULL) {
+ return;
+ }
+ if (!ui->invalidationBox->isEnabled()) {
+ return;
+ }
+ try {
+ QVariant currentProfileVariant = currentItem->data(Qt::UserRole);
+ if (!currentProfileVariant.isValid() || currentProfileVariant.isNull()) {
+ return;
+ }
+ const Profile &currentProfile = currentItem->data(Qt::UserRole).value<Profile>();
+ if (state == Qt::Unchecked) {
+ currentProfile.deactivateInvalidation();
+ } else {
+ currentProfile.activateInvalidation(m_GamePath + "/data");
+ }
+ } catch (const std::exception &e) {
+ reportError(tr("failed to change archive invalidation state: %1").arg(e.what()));
+ }
+}
+
+
+void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current, QListWidgetItem*)
+{
+ QCheckBox *invalidationBox = findChild<QCheckBox*>("invalidationBox");
+ QCheckBox *localSavesBox = findChild<QCheckBox*>("localSavesBox");
+ QPushButton *copyButton = findChild<QPushButton*>("copyProfileButton");
+ QPushButton *removeButton = findChild<QPushButton*>("removeProfileButton");
+ QPushButton *transferButton = findChild<QPushButton*>("transferButton");
+
+ if (current != NULL) {
+ const Profile &currentProfile = current->data(Qt::UserRole).value<Profile>();
+
+ try {
+ bool invalidationSupported = false;
+ invalidationBox->blockSignals(true);
+ invalidationBox->setChecked(currentProfile.invalidationActive(&invalidationSupported));
+ invalidationBox->setEnabled(invalidationSupported);
+ invalidationBox->blockSignals(false);
+
+ bool localSaves = currentProfile.localSavesEnabled();
+ transferButton->setEnabled(localSaves);
+ // prevent the stateChanged-event for the saves-box from triggering, otherwise it may think local saves
+ // were disabled and delete the files/rename the dir
+ localSavesBox->blockSignals(true);
+ localSavesBox->setChecked(localSaves);
+ localSavesBox->blockSignals(false);
+
+ copyButton->setEnabled(true);
+ removeButton->setEnabled(true);
+ } catch (const std::exception& E) {
+ reportError(tr("failed to determine if invalidation is active: %1").arg(E.what()));
+ copyButton->setEnabled(false);
+ removeButton->setEnabled(false);
+ invalidationBox->setChecked(false);
+ }
+ } else {
+ invalidationBox->setChecked(false);
+ copyButton->setEnabled(false);
+ removeButton->setEnabled(false);
+ }
+}
+
+void ProfilesDialog::on_localSavesBox_stateChanged(int state)
+{
+ Profile &currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
+ if (currentProfile.enableLocalSaves(state == Qt::Checked)) {
+ ui->transferButton->setEnabled(state == Qt::Checked);
+ } else {
+ // revert checkbox-state
+ ui->localSavesBox->setChecked(state != Qt::Checked);
+ }
+}
+
+void ProfilesDialog::on_transferButton_clicked()
+{
+ const Profile &currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
+ TransferSavesDialog transferDialog(currentProfile, this);
+ transferDialog.exec();
+}
diff --git a/src/profilesdialog.h b/src/profilesdialog.h
index c05340c9..df08aa45 100644
--- a/src/profilesdialog.h
+++ b/src/profilesdialog.h
@@ -17,78 +17,78 @@ 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 PROFILESDIALOG_H
-#define PROFILESDIALOG_H
-
-#include "tutorabledialog.h"
-#include <QListWidgetItem>
-#include "profile.h"
-
-
-namespace Ui {
- class ProfilesDialog;
-}
-
-
-/**
- * @brief Dialog that can be used to create/delete/modify profiles
- **/
-class ProfilesDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
-
- /**
- * @brief constructor
- *
- * @param gamePath the path to the game directory
- * @param parent parent widget
- * @todo the game path could be retrieved from GameInfo just as easily
- **/
- explicit ProfilesDialog(const QString &gamePath, QWidget *parent = 0);
- ~ProfilesDialog();
-
- /**
- * @return true if creation of a new profile failed
- * @todo the notion of a fail state makes little sense in the current dialog
- **/
- bool failed() const { return m_FailState; }
-
-protected:
-
- virtual void showEvent(QShowEvent *event);
-
-private:
-
- void addItem(const QString &name);
- void createProfile(const QString &name, bool useDefaultSettings);
- void createProfile(const QString &name, const Profile &reference);
-
-private slots:
-
- void on_closeButton_clicked();
-
- void on_addProfileButton_clicked();
-
- void on_invalidationBox_stateChanged(int arg1);
-
- void on_copyProfileButton_clicked();
-
- void on_profilesList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
-
- void on_removeProfileButton_clicked();
-
- void on_localSavesBox_stateChanged(int arg1);
-
- void on_transferButton_clicked();
-
-private:
- Ui::ProfilesDialog *ui;
- QString m_GamePath;
- QListWidget *m_ProfilesList;
- bool m_FailState;
-
-};
-
-#endif // PROFILESDIALOG_H
+#ifndef PROFILESDIALOG_H
+#define PROFILESDIALOG_H
+
+#include "tutorabledialog.h"
+#include <QListWidgetItem>
+#include "profile.h"
+
+
+namespace Ui {
+ class ProfilesDialog;
+}
+
+
+/**
+ * @brief Dialog that can be used to create/delete/modify profiles
+ **/
+class ProfilesDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief constructor
+ *
+ * @param gamePath the path to the game directory
+ * @param parent parent widget
+ * @todo the game path could be retrieved from GameInfo just as easily
+ **/
+ explicit ProfilesDialog(const QString &gamePath, QWidget *parent = 0);
+ ~ProfilesDialog();
+
+ /**
+ * @return true if creation of a new profile failed
+ * @todo the notion of a fail state makes little sense in the current dialog
+ **/
+ bool failed() const { return m_FailState; }
+
+protected:
+
+ virtual void showEvent(QShowEvent *event);
+
+private:
+
+ void addItem(const QString &name);
+ void createProfile(const QString &name, bool useDefaultSettings);
+ void createProfile(const QString &name, const Profile &reference);
+
+private slots:
+
+ void on_closeButton_clicked();
+
+ void on_addProfileButton_clicked();
+
+ void on_invalidationBox_stateChanged(int arg1);
+
+ void on_copyProfileButton_clicked();
+
+ void on_profilesList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
+
+ void on_removeProfileButton_clicked();
+
+ void on_localSavesBox_stateChanged(int arg1);
+
+ void on_transferButton_clicked();
+
+private:
+ Ui::ProfilesDialog *ui;
+ QString m_GamePath;
+ QListWidget *m_ProfilesList;
+ bool m_FailState;
+
+};
+
+#endif // PROFILESDIALOG_H
diff --git a/src/report.cpp b/src/report.cpp
index 6b379a1f..ca7e1dc9 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -17,33 +17,37 @@ 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 "report.h"
-#include "utility.h"
-#include <QMessageBox>
-#include <QApplication>
-#include <Windows.h>
-
-void reportError(QString message)
-{
- if (QApplication::topLevelWidgets().count() != 0) {
- QMessageBox messageBox(QMessageBox::Warning, QObject::tr("Error"), message, QMessageBox::Ok);
- messageBox.exec();
- } else {
- ::MessageBoxW(NULL, ToWString(message).c_str(), ToWString(QObject::tr("Error")).c_str(), MB_ICONERROR | MB_OK);
- }
-}
-
-
-std::tstring toTString(const QString& source)
-{
-#ifdef UNICODE
- wchar_t* temp = new wchar_t[source.size() + 1];
- source.toWCharArray(temp);
- temp[source.size()] = '\0';
- std::tstring result(temp);
- delete[] temp;
- return result;
-#else // UNICODE
- return source.toAscii();
-#endif // UNICODE
-}
+#include "report.h"
+#include "utility.h"
+#include <QMessageBox>
+#include <QApplication>
+#include <Windows.h>
+
+
+using namespace MOBase;
+
+
+void reportError(QString message)
+{
+ if (QApplication::topLevelWidgets().count() != 0) {
+ QMessageBox messageBox(QMessageBox::Warning, QObject::tr("Error"), message, QMessageBox::Ok);
+ messageBox.exec();
+ } else {
+ ::MessageBoxW(NULL, ToWString(message).c_str(), ToWString(QObject::tr("Error")).c_str(), MB_ICONERROR | MB_OK);
+ }
+}
+
+
+std::tstring toTString(const QString& source)
+{
+#ifdef UNICODE
+ wchar_t* temp = new wchar_t[source.size() + 1];
+ source.toWCharArray(temp);
+ temp[source.size()] = '\0';
+ std::tstring result(temp);
+ delete[] temp;
+ return result;
+#else // UNICODE
+ return source.toAscii();
+#endif // UNICODE
+}
diff --git a/src/savegamegamebryo.cpp b/src/savegamegamebryo.cpp
index 3b7ef017..f3794347 100644
--- a/src/savegamegamebryo.cpp
+++ b/src/savegamegamebryo.cpp
@@ -17,328 +17,331 @@ 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 "savegamegamebyro.h"
-#include <QFile>
-#include <QBuffer>
-#include <set>
-#include "gameinfo.h"
-#include <QFileInfo>
-#include <QDateTime>
-#include <limits>
-
-
-template <typename T>
-void FileRead(QFile &file, T &value)
-{
- int read = file.read(reinterpret_cast<char*>(&value), sizeof(T));
- if (read != sizeof(T)) {
- throw std::runtime_error("unexpected end of file");
- }
-}
-
-
-template <typename T>
-void FileSkip(QFile &file, int count = 1)
-{
- char ignore[sizeof(T)];
- for (int i = 0; i < count; ++i) {
- if (file.read(ignore, sizeof(T)) != sizeof(T)) {
- throw std::runtime_error("unexpected end of file");
- }
- }
-}
-
-
-QString ReadBString(QFile &file)
-{
- char buffer[256];
- file.read(buffer, 1); // size including zero termination
- unsigned char size = buffer[0];
- file.read(buffer, size);
- return QString::fromLatin1(buffer, size);
-}
-
-
-QString ReadFOSString(QFile &file, bool delimiter)
-{
- union {
- char lengthBuffer[2];
- unsigned short length;
- };
-
- file.read(lengthBuffer, 2);
- if (delimiter) {
- FileSkip<char>(file); // 0x7c
- }
- char *buffer = new char[length];
- file.read(buffer, length);
-
- QString result = QString::fromLatin1(buffer, length);
- delete [] buffer;
-
- return result;
-}
-
-
-SaveGameGamebryo::SaveGameGamebryo(QObject *parent)
- : SaveGame(parent), m_Plugins()
-{
-}
-
-
-SaveGameGamebryo::SaveGameGamebryo(QObject *parent, const QString &fileName)
- : SaveGame(parent, fileName), m_Plugins()
-{
- readFile(fileName);
-}
-
-
-SaveGameGamebryo::SaveGameGamebryo(const SaveGameGamebryo& reference)
- : SaveGame(reference), m_Plugins(reference.m_Plugins)
-{
-}
-
-
-SaveGameGamebryo& SaveGameGamebryo::operator=(const SaveGameGamebryo &reference)
-{
- if (&reference != this) {
- SaveGame::operator =(reference);
- m_Plugins = reference.m_Plugins;
- }
- return *this;
-}
-
-
-SaveGameGamebryo::~SaveGameGamebryo()
-{
-}
-
-
-QStringList SaveGameGamebryo::attachedFiles() const
-{
- QStringList result;
- QString seFileFile = fileName().mid(0).replace(".ess", ".skse");
- QFileInfo seFile(seFileFile);
- if (seFile.exists()) {
- result.append(seFile.absoluteFilePath());
- }
- return result;
-}
-
-
-void SaveGameGamebryo::readSkyrimFile(QFile &saveFile)
-{
- char fileID[14];
-
- saveFile.read(fileID, 13);
- fileID[13] = '\0';
- if (strncmp(fileID, "TESV_SAVEGAME", 13) != 0) {
- throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
- }
-
- FileSkip<unsigned long>(saveFile); // header size
- FileSkip<unsigned long>(saveFile); // header version, -> 8
- FileRead(saveFile, m_SaveNumber);
-
- m_PCName = ReadFOSString(saveFile, false);
-
- unsigned long temp;
- FileRead(saveFile, temp); // player level
- m_PCLevel = static_cast<unsigned short>(temp);
-
- m_PCLocation = ReadFOSString(saveFile, false);
- ReadFOSString(saveFile, false); // playtime as ascii hhh.mm.ss
- ReadFOSString(saveFile, false); // race name (i.e. BretonRace)
-
-
- FileSkip<unsigned short>(saveFile); // ???
- FileSkip<float>(saveFile, 2); // ???
- FileSkip<unsigned char>(saveFile, 8); // filetime
-
-// FileSkip<unsigned char>(saveFile, 18); // ??? 18 bytes of data. not completely random, maybe a time stamp? maybe
-
- unsigned long width, height;
- FileRead(saveFile, width); // 320
- FileRead(saveFile, height); // 192
-
- QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 3]);
- saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 3);
- // why do I have to copy here? without the copy, the buffer seems to get deleted after the
- // temporary vanishes, but Qts implicit sharing should handle that?
- m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGB888).copy();
-
- FileSkip<unsigned char>(saveFile); // form version
- FileSkip<unsigned long>(saveFile); // plugin info size
-
- unsigned char pluginCount;
- FileRead(saveFile, pluginCount);
-
- for (int i = 0; i < pluginCount; ++i) {
- m_Plugins.push_back(ReadFOSString(saveFile, false));
- }
-}
-
-
-void SaveGameGamebryo::readESSFile(QFile &saveFile)
-{
- char fileID[13];
- unsigned char versionMinor;
- unsigned long headerVersion, saveHeaderSize;
-// *** format is different for fallout!
- saveFile.read(fileID, 12);
- fileID[12] = '\0';
- FileSkip<unsigned char>(saveFile); FileRead(saveFile, versionMinor);
- FileSkip<SYSTEMTIME>(saveFile); // modified time
- FileRead(saveFile, headerVersion); FileRead(saveFile, saveHeaderSize);
-
- if (strncmp(fileID, "TES4SAVEGAME", 12) != 0) {
- throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
- }
-
- FileRead(saveFile, m_SaveNumber);
-
- m_PCName = ReadBString(saveFile);
-
- FileRead(saveFile, m_PCLevel);
- m_PCLocation = ReadBString(saveFile);
- FileSkip<float>(saveFile); // game days
- FileSkip<unsigned long>(saveFile); // game ticks
- FileRead(saveFile, m_CreationTime);
-
- unsigned long size;
- FileRead(saveFile, size); // screenshot size
-
- unsigned long width, height;
- FileRead(saveFile, width); FileRead(saveFile, height);
- QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 3]);
- saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 3);
- // why do I have to copy here? without the copy, the buffer seems to get deleted after the
- // temporary vanishes, but Qts implicit sharing should handle that?
- m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGB888).copy();
-
- unsigned char pluginCount;
- FileRead(saveFile, pluginCount);
-
- for (int i = 0; i < pluginCount; ++i) {
- QString name = ReadBString(saveFile);
- m_Plugins.push_back(name);
- }
-}
-
-
-void SaveGameGamebryo::readFOSFile(QFile &saveFile, bool newVegas)
-{
- char fileID[13];
- saveFile.read(fileID, 12);
- // the signature is only 11 characters, the 12th is random?
- fileID[11] = '\0';
-
- if (strncmp(fileID, "FO3SAVEGAME", 11) != 0) {
- throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
- }
-
- char ignore = 0x00;
- while (ignore != 0x7c) {
- FileRead<char>(saveFile, ignore); // unknown
- }
- if (newVegas) {
- ignore = 0x00;
- // in new vegas there is another block of uninteresting (?) information
- FileSkip<char>(saveFile); // 0x7c
- while (ignore != 0x7c) {
- FileRead<char>(saveFile, ignore); // unknown
- }
- }
-
- unsigned long width, height;
- FileRead(saveFile, width);
- FileSkip<char>(saveFile); // 0x7c
- FileRead(saveFile, height);
- FileSkip<char>(saveFile); // 0x7c
-
- FileRead(saveFile, m_SaveNumber);
- FileSkip<char>(saveFile); // 0x7c
-
- m_PCName = ReadFOSString(saveFile, true);
- FileSkip<char>(saveFile); // 0x7c
-
- ReadFOSString(saveFile, true);
- FileSkip<char>(saveFile); // 0x7c
-
- long Level;
- FileRead(saveFile, Level);
- m_PCLevel = Level;
- FileSkip<char>(saveFile); // 0x7c
-
- m_PCLocation = ReadFOSString(saveFile, true);
- FileSkip<char>(saveFile); // 0x7c
-
- ReadFOSString(saveFile, true); // playtime
-
- FileSkip<char>(saveFile);
-
- QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 3]);
- saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 3);
- // why do I have to copy here? without the copy, the buffer seems to get deleted after the
- // temporary vanishes, but Qts implicit sharing should handle that?
- m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGB888).scaledToWidth(256);
-
- FileSkip<char>(saveFile, 5); // unknown
-
- unsigned char pluginCount = 0;
- FileRead(saveFile, pluginCount);
- FileSkip<char>(saveFile); // 0x7c
-
- for (int i = 0; i < pluginCount; ++i) {
- QString name = ReadFOSString(saveFile, true);
- m_Plugins.push_back(name);
- FileSkip<char>(saveFile); // 0x7c
- }
-}
-
-
-void SaveGameGamebryo::setCreationTime(const QString &fileName)
-{
- QFileInfo creationTime(fileName);
- QDateTime modified = creationTime.lastModified();
- memset(&m_CreationTime, 0, sizeof(SYSTEMTIME));
-
- m_CreationTime.wDay = static_cast<WORD>(modified.date().day());
- m_CreationTime.wDayOfWeek = static_cast<WORD>(modified.date().dayOfWeek());
- m_CreationTime.wMonth = static_cast<WORD>(modified.date().month());
- m_CreationTime.wYear =static_cast<WORD>( modified.date().year());
-
- m_CreationTime.wHour = static_cast<WORD>(modified.time().hour());
- m_CreationTime.wMinute = static_cast<WORD>(modified.time().minute());
- m_CreationTime.wSecond = static_cast<WORD>(modified.time().second());
- m_CreationTime.wMilliseconds = static_cast<WORD>(modified.time().msec());
-}
-
-
-void SaveGameGamebryo::readFile(const QString &fileName)
-{
- m_FileName = fileName;
- QFile saveFile(fileName);
- if (!saveFile.open(QIODevice::ReadOnly)) {
- throw std::runtime_error(QObject::tr("failed to open %1").arg(fileName).toUtf8().constData());
- }
- switch (GameInfo::instance().getType()) {
- case GameInfo::TYPE_FALLOUT3: {
- setCreationTime(fileName);
- readFOSFile(saveFile, false);
- } break;
- case GameInfo::TYPE_FALLOUTNV: {
- setCreationTime(fileName);
- readFOSFile(saveFile, true);
- } break;
- case GameInfo::TYPE_OBLIVION: {
- readESSFile(saveFile);
- } break;
- case GameInfo::TYPE_SKYRIM: {
- setCreationTime(fileName);
- readSkyrimFile(saveFile);
- } break;
- }
-
- saveFile.close();
-}
+#include "savegamegamebyro.h"
+#include <QFile>
+#include <QBuffer>
+#include <set>
+#include "gameinfo.h"
+#include <QFileInfo>
+#include <QDateTime>
+#include <limits>
+
+
+using namespace MOShared;
+
+
+template <typename T>
+void FileRead(QFile &file, T &value)
+{
+ int read = file.read(reinterpret_cast<char*>(&value), sizeof(T));
+ if (read != sizeof(T)) {
+ throw std::runtime_error("unexpected end of file");
+ }
+}
+
+
+template <typename T>
+void FileSkip(QFile &file, int count = 1)
+{
+ char ignore[sizeof(T)];
+ for (int i = 0; i < count; ++i) {
+ if (file.read(ignore, sizeof(T)) != sizeof(T)) {
+ throw std::runtime_error("unexpected end of file");
+ }
+ }
+}
+
+
+QString ReadBString(QFile &file)
+{
+ char buffer[256];
+ file.read(buffer, 1); // size including zero termination
+ unsigned char size = buffer[0];
+ file.read(buffer, size);
+ return QString::fromLatin1(buffer, size);
+}
+
+
+QString ReadFOSString(QFile &file, bool delimiter)
+{
+ union {
+ char lengthBuffer[2];
+ unsigned short length;
+ };
+
+ file.read(lengthBuffer, 2);
+ if (delimiter) {
+ FileSkip<char>(file); // 0x7c
+ }
+ char *buffer = new char[length];
+ file.read(buffer, length);
+
+ QString result = QString::fromLatin1(buffer, length);
+ delete [] buffer;
+
+ return result;
+}
+
+
+SaveGameGamebryo::SaveGameGamebryo(QObject *parent)
+ : SaveGame(parent), m_Plugins()
+{
+}
+
+
+SaveGameGamebryo::SaveGameGamebryo(QObject *parent, const QString &fileName)
+ : SaveGame(parent, fileName), m_Plugins()
+{
+ readFile(fileName);
+}
+
+
+SaveGameGamebryo::SaveGameGamebryo(const SaveGameGamebryo& reference)
+ : SaveGame(reference), m_Plugins(reference.m_Plugins)
+{
+}
+
+
+SaveGameGamebryo& SaveGameGamebryo::operator=(const SaveGameGamebryo &reference)
+{
+ if (&reference != this) {
+ SaveGame::operator =(reference);
+ m_Plugins = reference.m_Plugins;
+ }
+ return *this;
+}
+
+
+SaveGameGamebryo::~SaveGameGamebryo()
+{
+}
+
+
+QStringList SaveGameGamebryo::attachedFiles() const
+{
+ QStringList result;
+ QString seFileFile = fileName().mid(0).replace(".ess", ".skse");
+ QFileInfo seFile(seFileFile);
+ if (seFile.exists()) {
+ result.append(seFile.absoluteFilePath());
+ }
+ return result;
+}
+
+
+void SaveGameGamebryo::readSkyrimFile(QFile &saveFile)
+{
+ char fileID[14];
+
+ saveFile.read(fileID, 13);
+ fileID[13] = '\0';
+ if (strncmp(fileID, "TESV_SAVEGAME", 13) != 0) {
+ throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
+ }
+
+ FileSkip<unsigned long>(saveFile); // header size
+ FileSkip<unsigned long>(saveFile); // header version, -> 8
+ FileRead(saveFile, m_SaveNumber);
+
+ m_PCName = ReadFOSString(saveFile, false);
+
+ unsigned long temp;
+ FileRead(saveFile, temp); // player level
+ m_PCLevel = static_cast<unsigned short>(temp);
+
+ m_PCLocation = ReadFOSString(saveFile, false);
+ ReadFOSString(saveFile, false); // playtime as ascii hhh.mm.ss
+ ReadFOSString(saveFile, false); // race name (i.e. BretonRace)
+
+
+ FileSkip<unsigned short>(saveFile); // ???
+ FileSkip<float>(saveFile, 2); // ???
+ FileSkip<unsigned char>(saveFile, 8); // filetime
+
+// FileSkip<unsigned char>(saveFile, 18); // ??? 18 bytes of data. not completely random, maybe a time stamp? maybe
+
+ unsigned long width, height;
+ FileRead(saveFile, width); // 320
+ FileRead(saveFile, height); // 192
+
+ QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 3]);
+ saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 3);
+ // why do I have to copy here? without the copy, the buffer seems to get deleted after the
+ // temporary vanishes, but Qts implicit sharing should handle that?
+ m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGB888).copy();
+
+ FileSkip<unsigned char>(saveFile); // form version
+ FileSkip<unsigned long>(saveFile); // plugin info size
+
+ unsigned char pluginCount;
+ FileRead(saveFile, pluginCount);
+
+ for (int i = 0; i < pluginCount; ++i) {
+ m_Plugins.push_back(ReadFOSString(saveFile, false));
+ }
+}
+
+
+void SaveGameGamebryo::readESSFile(QFile &saveFile)
+{
+ char fileID[13];
+ unsigned char versionMinor;
+ unsigned long headerVersion, saveHeaderSize;
+// *** format is different for fallout!
+ saveFile.read(fileID, 12);
+ fileID[12] = '\0';
+ FileSkip<unsigned char>(saveFile); FileRead(saveFile, versionMinor);
+ FileSkip<SYSTEMTIME>(saveFile); // modified time
+ FileRead(saveFile, headerVersion); FileRead(saveFile, saveHeaderSize);
+
+ if (strncmp(fileID, "TES4SAVEGAME", 12) != 0) {
+ throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
+ }
+
+ FileRead(saveFile, m_SaveNumber);
+
+ m_PCName = ReadBString(saveFile);
+
+ FileRead(saveFile, m_PCLevel);
+ m_PCLocation = ReadBString(saveFile);
+ FileSkip<float>(saveFile); // game days
+ FileSkip<unsigned long>(saveFile); // game ticks
+ FileRead(saveFile, m_CreationTime);
+
+ unsigned long size;
+ FileRead(saveFile, size); // screenshot size
+
+ unsigned long width, height;
+ FileRead(saveFile, width); FileRead(saveFile, height);
+ QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 3]);
+ saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 3);
+ // why do I have to copy here? without the copy, the buffer seems to get deleted after the
+ // temporary vanishes, but Qts implicit sharing should handle that?
+ m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGB888).copy();
+
+ unsigned char pluginCount;
+ FileRead(saveFile, pluginCount);
+
+ for (int i = 0; i < pluginCount; ++i) {
+ QString name = ReadBString(saveFile);
+ m_Plugins.push_back(name);
+ }
+}
+
+
+void SaveGameGamebryo::readFOSFile(QFile &saveFile, bool newVegas)
+{
+ char fileID[13];
+ saveFile.read(fileID, 12);
+ // the signature is only 11 characters, the 12th is random?
+ fileID[11] = '\0';
+
+ if (strncmp(fileID, "FO3SAVEGAME", 11) != 0) {
+ throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
+ }
+
+ char ignore = 0x00;
+ while (ignore != 0x7c) {
+ FileRead<char>(saveFile, ignore); // unknown
+ }
+ if (newVegas) {
+ ignore = 0x00;
+ // in new vegas there is another block of uninteresting (?) information
+ FileSkip<char>(saveFile); // 0x7c
+ while (ignore != 0x7c) {
+ FileRead<char>(saveFile, ignore); // unknown
+ }
+ }
+
+ unsigned long width, height;
+ FileRead(saveFile, width);
+ FileSkip<char>(saveFile); // 0x7c
+ FileRead(saveFile, height);
+ FileSkip<char>(saveFile); // 0x7c
+
+ FileRead(saveFile, m_SaveNumber);
+ FileSkip<char>(saveFile); // 0x7c
+
+ m_PCName = ReadFOSString(saveFile, true);
+ FileSkip<char>(saveFile); // 0x7c
+
+ ReadFOSString(saveFile, true);
+ FileSkip<char>(saveFile); // 0x7c
+
+ long Level;
+ FileRead(saveFile, Level);
+ m_PCLevel = Level;
+ FileSkip<char>(saveFile); // 0x7c
+
+ m_PCLocation = ReadFOSString(saveFile, true);
+ FileSkip<char>(saveFile); // 0x7c
+
+ ReadFOSString(saveFile, true); // playtime
+
+ FileSkip<char>(saveFile);
+
+ QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 3]);
+ saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 3);
+ // why do I have to copy here? without the copy, the buffer seems to get deleted after the
+ // temporary vanishes, but Qts implicit sharing should handle that?
+ m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGB888).scaledToWidth(256);
+
+ FileSkip<char>(saveFile, 5); // unknown
+
+ unsigned char pluginCount = 0;
+ FileRead(saveFile, pluginCount);
+ FileSkip<char>(saveFile); // 0x7c
+
+ for (int i = 0; i < pluginCount; ++i) {
+ QString name = ReadFOSString(saveFile, true);
+ m_Plugins.push_back(name);
+ FileSkip<char>(saveFile); // 0x7c
+ }
+}
+
+
+void SaveGameGamebryo::setCreationTime(const QString &fileName)
+{
+ QFileInfo creationTime(fileName);
+ QDateTime modified = creationTime.lastModified();
+ memset(&m_CreationTime, 0, sizeof(SYSTEMTIME));
+
+ m_CreationTime.wDay = static_cast<WORD>(modified.date().day());
+ m_CreationTime.wDayOfWeek = static_cast<WORD>(modified.date().dayOfWeek());
+ m_CreationTime.wMonth = static_cast<WORD>(modified.date().month());
+ m_CreationTime.wYear =static_cast<WORD>( modified.date().year());
+
+ m_CreationTime.wHour = static_cast<WORD>(modified.time().hour());
+ m_CreationTime.wMinute = static_cast<WORD>(modified.time().minute());
+ m_CreationTime.wSecond = static_cast<WORD>(modified.time().second());
+ m_CreationTime.wMilliseconds = static_cast<WORD>(modified.time().msec());
+}
+
+
+void SaveGameGamebryo::readFile(const QString &fileName)
+{
+ m_FileName = fileName;
+ QFile saveFile(fileName);
+ if (!saveFile.open(QIODevice::ReadOnly)) {
+ throw std::runtime_error(QObject::tr("failed to open %1").arg(fileName).toUtf8().constData());
+ }
+ switch (GameInfo::instance().getType()) {
+ case GameInfo::TYPE_FALLOUT3: {
+ setCreationTime(fileName);
+ readFOSFile(saveFile, false);
+ } break;
+ case GameInfo::TYPE_FALLOUTNV: {
+ setCreationTime(fileName);
+ readFOSFile(saveFile, true);
+ } break;
+ case GameInfo::TYPE_OBLIVION: {
+ readESSFile(saveFile);
+ } break;
+ case GameInfo::TYPE_SKYRIM: {
+ setCreationTime(fileName);
+ readSkyrimFile(saveFile);
+ } break;
+ }
+
+ saveFile.close();
+}
diff --git a/src/savegameinfowidget.cpp b/src/savegameinfowidget.cpp
index 34a0f358..f07a9694 100644
--- a/src/savegameinfowidget.cpp
+++ b/src/savegameinfowidget.cpp
@@ -17,46 +17,46 @@ 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 "savegameinfowidget.h"
-#include "ui_savegameinfowidget.h"
-#include "utility.h"
-#include <QGraphicsDropShadowEffect>
-
-
-SaveGameInfoWidget::SaveGameInfoWidget(QWidget *parent)
- : QWidget(parent), ui(new Ui::SaveGameInfoWidget)
-{
- ui->setupUi(this);
- this->setWindowFlags(Qt::ToolTip | Qt::BypassGraphicsProxyWidget);
- setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / qreal(255.0));
-// installEventFilter(this);
-}
-
-SaveGameInfoWidget::~SaveGameInfoWidget()
-{
- delete ui;
-}
-
-
-void SaveGameInfoWidget::setSave(const SaveGame *saveGame)
-{
- ui->saveNumLabel->setText(QString("%1").arg(saveGame->saveNumber()));
- ui->characterLabel->setText(saveGame->pcName());
- ui->locationLabel->setText(saveGame->pcLocation());
- ui->levelLabel->setText(QString("%1").arg(saveGame->pcLevel()));
- ui->dateLabel->setText(ToString(saveGame->creationTime()));
- ui->screenshotLabel->setPixmap(QPixmap::fromImage(saveGame->screenshot()));
- if (ui->gameFrame->layout() != NULL) {
- QLayoutItem *item = NULL;
- while ((item = ui->gameFrame->layout()->takeAt(0)) != NULL) {
- delete item->widget();
- delete item;
- }
- ui->gameFrame->layout()->setSizeConstraint(QLayout::SetFixedSize);
- }
-}
-
-QFrame *SaveGameInfoWidget::getGameFrame()
-{
- return ui->gameFrame;
-}
+#include "savegameinfowidget.h"
+#include "ui_savegameinfowidget.h"
+#include "utility.h"
+#include <QGraphicsDropShadowEffect>
+
+
+SaveGameInfoWidget::SaveGameInfoWidget(QWidget *parent)
+ : QWidget(parent), ui(new Ui::SaveGameInfoWidget)
+{
+ ui->setupUi(this);
+ this->setWindowFlags(Qt::ToolTip | Qt::BypassGraphicsProxyWidget);
+ setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / qreal(255.0));
+// installEventFilter(this);
+}
+
+SaveGameInfoWidget::~SaveGameInfoWidget()
+{
+ delete ui;
+}
+
+
+void SaveGameInfoWidget::setSave(const SaveGame *saveGame)
+{
+ ui->saveNumLabel->setText(QString("%1").arg(saveGame->saveNumber()));
+ ui->characterLabel->setText(saveGame->pcName());
+ ui->locationLabel->setText(saveGame->pcLocation());
+ ui->levelLabel->setText(QString("%1").arg(saveGame->pcLevel()));
+ ui->dateLabel->setText(MOBase::ToString(saveGame->creationTime()));
+ ui->screenshotLabel->setPixmap(QPixmap::fromImage(saveGame->screenshot()));
+ if (ui->gameFrame->layout() != NULL) {
+ QLayoutItem *item = NULL;
+ while ((item = ui->gameFrame->layout()->takeAt(0)) != NULL) {
+ delete item->widget();
+ delete item;
+ }
+ ui->gameFrame->layout()->setSizeConstraint(QLayout::SetFixedSize);
+ }
+}
+
+QFrame *SaveGameInfoWidget::getGameFrame()
+{
+ return ui->gameFrame;
+}
diff --git a/src/savetextasdialog.cpp b/src/savetextasdialog.cpp
index 465e3d65..1f23356f 100644
--- a/src/savetextasdialog.cpp
+++ b/src/savetextasdialog.cpp
@@ -37,7 +37,7 @@ void SaveTextAsDialog::on_saveAsBtn_clicked()
if (!fileName.isEmpty()) {
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
- reportError(tr("failed to open \"%1\" for writing").arg(fileName));
+ MOBase::reportError(tr("failed to open \"%1\" for writing").arg(fileName));
return;
}
diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp
index 884f9647..503c2596 100644
--- a/src/selfupdater.cpp
+++ b/src/selfupdater.cpp
@@ -17,439 +17,443 @@ 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 "selfupdater.h"
-#include "utility.h"
-#include "installationmanager.h"
-#include "report.h"
-#include "messagedialog.h"
-#include "downloadmanager.h"
-
-#include <versioninfo.h>
-#include <gameinfo.h>
-#include <skyriminfo.h>
-#include <QMessageBox>
-#include <QNetworkAccessManager>
-#include <QNetworkRequest>
-#include <QDir>
-#include <QLibrary>
-#include <QProcess>
-#include <QApplication>
-#include <util.h>
-
-
-typedef Archive* (*CreateArchiveType)();
-
-
-template <typename T> T resolveFunction(QLibrary &lib, const char *name)
-{
- T temp = reinterpret_cast<T>(lib.resolve(name));
- if (temp == NULL) {
- throw std::runtime_error(QObject::tr("invalid 7-zip32.dll: %1").arg(lib.errorString()).toLatin1().constData());
- }
- return temp;
-}
-
-
-SelfUpdater::SelfUpdater(NexusInterface *nexusInterface, QWidget *parent)
- : QObject(parent), m_Parent(parent), m_Interface(nexusInterface), m_UpdateRequestID(-1),
- m_Reply(NULL), m_Progress(parent), m_Attempts(3)
-{
- m_Progress.setMaximum(100);
-
- QLibrary archiveLib("dlls\\archive.dll");
- if (!archiveLib.load()) {
- throw MyException(tr("archive.dll not loaded: \"%1\"").arg(archiveLib.errorString()));
- }
-
- CreateArchiveType CreateArchiveFunc = resolveFunction<CreateArchiveType>(archiveLib, "CreateArchive");
-
- m_CurrentArchive = CreateArchiveFunc();
- if (!m_CurrentArchive->isValid()) {
- throw MyException(InstallationManager::getErrorString(m_CurrentArchive->getLastError()));
- }
-
- connect(&m_Progress, SIGNAL(canceled()), this, SLOT(downloadCancel()));
-
- VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath()));
-
- m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16,
- version.dwFileVersionMS & 0xFFFF,
- version.dwFileVersionLS >> 16);
-}
-
-
-SelfUpdater::~SelfUpdater()
-{
- delete m_CurrentArchive;
-}
-
-
-void SelfUpdater::testForUpdate()
-{
-/* if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
- emit updateAvailable();
- return;
- }*/
-
- if (m_UpdateRequestID == -1) {
- m_UpdateRequestID = m_Interface->requestDescription(
- SkyrimInfo::getNexusModIDStatic(), this, QVariant(),
- ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
- }
-}
-
-void SelfUpdater::startUpdate()
-{
-/* if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
- m_UpdateFile.setFileName(QCoreApplication::applicationDirPath() + "/mo_test_update.7z");
- installUpdate();
- return;
- }*/
-
- if ((m_UpdateRequestID == -1) &&
- (!m_NewestVersion.isEmpty())) {
- if (QMessageBox::question(m_Parent, tr("Update"),
- tr("An update is available (newest version: %1), do you want to install it?").arg(m_NewestVersion),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- m_UpdateRequestID = m_Interface->requestFiles(SkyrimInfo::getNexusModIDStatic(),
- this, m_NewestVersion,
- ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
- }
- }
-}
-
-
-void SelfUpdater::download(const QString &downloadLink, const QString &fileName)
-{
- qDebug("download: %s", downloadLink.toUtf8().constData());
- QNetworkAccessManager *accessManager = m_Interface->getAccessManager();
- QUrl dlUrl(downloadLink);
- QNetworkRequest request(dlUrl);
- m_Canceled = false;
- m_Reply = accessManager->get(request);
- m_UpdateFile.setFileName(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory()).append("\\").append(fileName)));
- m_UpdateFile.open(QIODevice::WriteOnly);
- m_Progress.setModal(true);
- m_Progress.show();
- m_Progress.setValue(0);
- m_Progress.setWindowTitle(tr("Update"));
- m_Progress.setLabelText(tr("Download in progress"));
-
- connect(m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64)));
- connect(m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
- connect(m_Reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
-}
-
-
-void SelfUpdater::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
-{
- if (m_Reply != NULL) {
- if (m_Canceled) {
- m_Reply->abort();
- } else {
- if (bytesTotal != 0) {
- m_Progress.setValue((bytesReceived * 100) / bytesTotal);
- }
- }
- }
-}
-
-
-void SelfUpdater::downloadReadyRead()
-{
- if (m_Reply != NULL) {
- m_UpdateFile.write(m_Reply->readAll());
- }
-}
-
-
-void SelfUpdater::downloadFinished()
-{
- int error = QNetworkReply::NoError;
-
- if (m_Reply != NULL) {
- m_UpdateFile.write(m_Reply->readAll());
-
- error = m_Reply->error();
-
- if (m_Reply->header(QNetworkRequest::ContentTypeHeader).toString().startsWith("text", Qt::CaseInsensitive)) {
- m_Canceled = true;
- }
-
- m_Progress.hide();
- m_Reply->close();
- m_Reply->deleteLater();
- m_Reply = NULL;
- }
-
- m_UpdateFile.close();
-
- if ((m_UpdateFile.size() == 0) ||
- (error != QNetworkReply::NoError) ||
- m_Canceled) {
- if (!m_Canceled) {
- reportError(tr("Download failed: %1").arg(error));
- }
- m_UpdateFile.remove();
- return;
- }
-
- qDebug("download: %s", m_UpdateFile.fileName().toUtf8().constData());
-
- try {
- installUpdate();
- } catch (const std::exception &e) {
- reportError(tr("Failed to install update: %1").arg(e.what()));
- }
-}
-
-
-void SelfUpdater::downloadCancel()
-{
- m_Canceled = true;
-}
-
-
-void SelfUpdater::installUpdate()
-{
- const QString mopath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory()));
-
- QString backupPath = mopath.mid(0).append("/update_backup");
- QDir().mkdir(backupPath);
-
- // rename files that are currently open so we can unpack the update
- if (!m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(m_UpdateFile.fileName())).c_str(),
- new MethodCallback<SelfUpdater, void, LPSTR>(this, &SelfUpdater::queryPassword))) {
- throw MyException(tr("failed to open archive \"%1\": %2")
- .arg(m_UpdateFile.fileName())
- .arg(InstallationManager::getErrorString(m_CurrentArchive->getLastError())));
- }
-
- // move all files contained in the archive out of the way,
- // otherwise we can't overwrite everything
- FileData* const *data;
- size_t size;
- m_CurrentArchive->getFileList(data, size);
-
- for (size_t i = 0; i < size; ++i) {
- QString outputName = ToQString(data[i]->getFileName());
- if (outputName.startsWith("ModOrganizer\\", Qt::CaseInsensitive)) {
- outputName = outputName.mid(13);
- data[i]->setOutputFileName(ToWString(outputName).c_str());
- } else if (outputName == "ModOrganizer") {
- data[i]->setSkip(true);
- }
- QFile file(mopath.mid(0).append("/").append(outputName));
- if (file.exists()) {
- file.rename(backupPath.mid(0).append("/").append(outputName));
- }
- }
-
- // now unpack the archive into the mo directory
- if (!m_CurrentArchive->extract(GameInfo::instance().getOrganizerDirectory().c_str(),
- new MethodCallback<SelfUpdater, void, float>(this, &SelfUpdater::updateProgress),
- new MethodCallback<SelfUpdater, void, LPCWSTR>(this, &SelfUpdater::updateProgressFile),
- new MethodCallback<SelfUpdater, void, LPCWSTR>(this, &SelfUpdater::report7ZipError))) {
- throw std::runtime_error("extracting failed");
- }
-
- m_CurrentArchive->close();
-
- m_UpdateFile.remove();
-
- QMessageBox::information(m_Parent, tr("Update"), tr("Update installed, Mod Organizer will now be restarted."));
-
- QProcess newProcess;
- if (QFile::exists(mopath.mid(0).append("/ModOrganizer.exe"))) {
- newProcess.startDetached(mopath.mid(0).append("/ModOrganizer.exe"), QStringList("update"));
- } else {
- newProcess.startDetached(mopath.mid(0).append("/ModOrganiser.exe"), QStringList("update"));
- }
- emit restart();
-}
-
-void SelfUpdater::queryPassword(LPSTR)
-{
- // nop
-}
-
-void SelfUpdater::updateProgress(float)
-{
- // nop
-}
-
-void SelfUpdater::updateProgressFile(LPCWSTR)
-{
- // nop
-}
-
-void SelfUpdater::report7ZipError(LPCWSTR errorMessage)
-{
- QMessageBox::critical(m_Parent, tr("Error"), ToQString(errorMessage));
-}
-
-
-QString SelfUpdater::retrieveNews(const QString &description)
-{
- QStringList temp = description.split("[s][/s]");
- if (temp.length() < 2) {
- return QString();
- } else {
- return temp.at(1);
- }
-}
-
-
-void SelfUpdater::nxmDescriptionAvailable(int, QVariant, QVariant resultData, int requestID)
-{
- if (requestID == m_UpdateRequestID) {
- m_UpdateRequestID = -1;
-
- QVariantMap result = resultData.toMap();
- QString motd = retrieveNews(result["description"].toString()).trimmed();
- if (motd.length() != 0) {
- emit motdAvailable(motd);
- }
-
- m_NewestVersion = result["version"].toString();
- if (m_NewestVersion.isEmpty()) {
- QTimer::singleShot(5000, this, SLOT(testForUpdate()));
- }
-
- VersionInfo currentVersion(m_MOVersion);
- VersionInfo newestVersion(m_NewestVersion);
-
- if (!m_NewestVersion.isEmpty() && (currentVersion < newestVersion)) {
- emit updateAvailable();
- } else if (newestVersion < currentVersion) {
- qDebug("this version is newer than the current version on nexus (%s vs %s)",
- currentVersion.canonicalString().toUtf8().constData(),
- newestVersion.canonicalString().toUtf8().constData());
- }
- }
-}
-
-
-void SelfUpdater::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID)
-{
- if (requestID != m_UpdateRequestID) {
- return;
- }
- QString version = userData.toString();
-
- m_UpdateRequestID = -1;
-
- if (!resultData.canConvert<QVariantList>()) {
- qCritical("invalid files result: %s", resultData.toString().toUtf8().constData());
- reportError(tr("Failed to parse response. Please report this as a bug and include the file mo_interface.log."));
- return;
- }
-
- QVariantList result = resultData.toList();
-
- QRegExp updateExpList(QString("updates version ([0-9., ]*) to %1").arg(version));
- QRegExp updateExpRange(QString("updates version ([0-9.]*) - ([0-9.]*) to %1").arg(version));
- int updateFileID = -1;
- QString updateFileName;
- int mainFileID = -1;
- QString mainFileName;
- int mainFileSize = 0;
-
- foreach(QVariant file, result) {
- QVariantMap fileInfo = file.toMap();
- if (!fileInfo["uri"].toString().endsWith(".7z")) {
- continue;
- }
-
- if (fileInfo["version"].toString() == version) {
- if (fileInfo["category_id"].toInt() == 2) {
- QString description = fileInfo["description"].toString();
- // update
- if (updateExpList.indexIn(description) != -1) {
- // there is an update for the newest version of MO, but does
- // it apply to the current version?
- QStringList supportedVersions = updateExpList.cap(1).split(QRegExp(",[ ]*"), QString::SkipEmptyParts);
- if (supportedVersions.contains(m_MOVersion.canonicalString())) {
- updateFileID = fileInfo["id"].toInt();
- updateFileName = fileInfo["uri"].toString();
- } else {
- qDebug("update not supported from %s", m_MOVersion.canonicalString().toUtf8().constData());
- }
- } else if (updateExpRange.indexIn(description) != -1) {
- VersionInfo rangeLowEnd(updateExpRange.cap(1));
- VersionInfo rangeHighEnd(updateExpRange.cap(2));
- if ((rangeLowEnd <= m_MOVersion) &&
- (m_MOVersion <= rangeHighEnd)) {
- updateFileID = fileInfo["id"].toInt();
- updateFileName = fileInfo["uri"].toString();
- break;
- } else {
- qDebug("update not supported from %s", m_MOVersion.canonicalString().toUtf8().constData());
- }
- } else {
- qWarning("invalid update description: %s",
- description.toUtf8().constData());
- }
- } else if (fileInfo["category_id"].toInt() == 1) {
- mainFileID = fileInfo["id"].toInt();
- mainFileName = fileInfo["uri"].toString();
- mainFileSize = fileInfo["size"].toInt();
- }
- }
- }
-
- if (updateFileID != -1) {
- qDebug("update available: %d", updateFileID);
- m_UpdateRequestID = m_Interface->requestDownloadURL(SkyrimInfo::getNexusModIDStatic(),
- updateFileID, this, updateFileName,
- ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
- } else if (mainFileID != -1) {
- qDebug("full download required: %d", mainFileID);
- if (QMessageBox::question(m_Parent, tr("Update"),
- tr("No incremental update available for this version, "
- "the complete package needs to be downloaded (%1 kB)").arg(mainFileSize),
- QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
- m_UpdateRequestID = m_Interface->requestDownloadURL(SkyrimInfo::getNexusModIDStatic(),
- mainFileID, this, mainFileName,
- ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
- }
- } else {
- qCritical("no file for update found");
- MessageDialog::showMessage(tr("no file for update found. Please update manually."), m_Parent);
- m_Progress.hide();
- }
-}
-
-
-void SelfUpdater::nxmRequestFailed(int, QVariant, int requestID, const QString &errorMessage)
-{
- if (requestID == m_UpdateRequestID) {
- m_UpdateRequestID = -1;
- if (m_Attempts > 0) {
- QTimer::singleShot(60000, this, SLOT(testForUpdate()));
- --m_Attempts;
- } else {
- MessageDialog::showMessage(tr("Failed to retrieve update information: %1").arg(errorMessage), m_Parent);
- }
- }
-}
-
-
-void SelfUpdater::nxmDownloadURLsAvailable(int, int, QVariant userData, QVariant resultData, int requestID)
-{
- if (requestID == m_UpdateRequestID) {
- m_UpdateRequestID = -1;
- QVariantList serverList = resultData.toList();
- if (serverList.count() != 0) {
- qSort(serverList.begin(), serverList.end(), DownloadManager::ServerByPreference);
-
- QVariantMap dlServer = serverList.first().toMap();
-
- download(dlServer["URI"].toString(), userData.toString());
- } else {
- MessageDialog::showMessage(tr("No download server available. Please try again later."), m_Parent);
- m_Progress.hide();
- }
- }
-}
-
+#include "selfupdater.h"
+#include "utility.h"
+#include "installationmanager.h"
+#include "report.h"
+#include "messagedialog.h"
+#include "downloadmanager.h"
+
+#include <versioninfo.h>
+#include <gameinfo.h>
+#include <skyriminfo.h>
+#include <QMessageBox>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QDir>
+#include <QLibrary>
+#include <QProcess>
+#include <QApplication>
+#include <util.h>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+typedef Archive* (*CreateArchiveType)();
+
+
+template <typename T> T resolveFunction(QLibrary &lib, const char *name)
+{
+ T temp = reinterpret_cast<T>(lib.resolve(name));
+ if (temp == NULL) {
+ throw std::runtime_error(QObject::tr("invalid 7-zip32.dll: %1").arg(lib.errorString()).toLatin1().constData());
+ }
+ return temp;
+}
+
+
+SelfUpdater::SelfUpdater(NexusInterface *nexusInterface, QWidget *parent)
+ : QObject(parent), m_Parent(parent), m_Interface(nexusInterface), m_UpdateRequestID(-1),
+ m_Reply(NULL), m_Progress(parent), m_Attempts(3)
+{
+ m_Progress.setMaximum(100);
+
+ QLibrary archiveLib("dlls\\archive.dll");
+ if (!archiveLib.load()) {
+ throw MyException(tr("archive.dll not loaded: \"%1\"").arg(archiveLib.errorString()));
+ }
+
+ CreateArchiveType CreateArchiveFunc = resolveFunction<CreateArchiveType>(archiveLib, "CreateArchive");
+
+ m_CurrentArchive = CreateArchiveFunc();
+ if (!m_CurrentArchive->isValid()) {
+ throw MyException(InstallationManager::getErrorString(m_CurrentArchive->getLastError()));
+ }
+
+ connect(&m_Progress, SIGNAL(canceled()), this, SLOT(downloadCancel()));
+
+ VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath()));
+
+ m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16,
+ version.dwFileVersionMS & 0xFFFF,
+ version.dwFileVersionLS >> 16);
+}
+
+
+SelfUpdater::~SelfUpdater()
+{
+ delete m_CurrentArchive;
+}
+
+
+void SelfUpdater::testForUpdate()
+{
+/* if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
+ emit updateAvailable();
+ return;
+ }*/
+
+ if (m_UpdateRequestID == -1) {
+ m_UpdateRequestID = m_Interface->requestDescription(
+ SkyrimInfo::getNexusModIDStatic(), this, QVariant(),
+ ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
+ }
+}
+
+void SelfUpdater::startUpdate()
+{
+/* if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
+ m_UpdateFile.setFileName(QCoreApplication::applicationDirPath() + "/mo_test_update.7z");
+ installUpdate();
+ return;
+ }*/
+
+ if ((m_UpdateRequestID == -1) &&
+ (!m_NewestVersion.isEmpty())) {
+ if (QMessageBox::question(m_Parent, tr("Update"),
+ tr("An update is available (newest version: %1), do you want to install it?").arg(m_NewestVersion),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ m_UpdateRequestID = m_Interface->requestFiles(SkyrimInfo::getNexusModIDStatic(),
+ this, m_NewestVersion,
+ ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
+ }
+ }
+}
+
+
+void SelfUpdater::download(const QString &downloadLink, const QString &fileName)
+{
+ qDebug("download: %s", downloadLink.toUtf8().constData());
+ QNetworkAccessManager *accessManager = m_Interface->getAccessManager();
+ QUrl dlUrl(downloadLink);
+ QNetworkRequest request(dlUrl);
+ m_Canceled = false;
+ m_Reply = accessManager->get(request);
+ m_UpdateFile.setFileName(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory()).append("\\").append(fileName)));
+ m_UpdateFile.open(QIODevice::WriteOnly);
+ m_Progress.setModal(true);
+ m_Progress.show();
+ m_Progress.setValue(0);
+ m_Progress.setWindowTitle(tr("Update"));
+ m_Progress.setLabelText(tr("Download in progress"));
+
+ connect(m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64)));
+ connect(m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
+ connect(m_Reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
+}
+
+
+void SelfUpdater::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ if (m_Reply != NULL) {
+ if (m_Canceled) {
+ m_Reply->abort();
+ } else {
+ if (bytesTotal != 0) {
+ m_Progress.setValue((bytesReceived * 100) / bytesTotal);
+ }
+ }
+ }
+}
+
+
+void SelfUpdater::downloadReadyRead()
+{
+ if (m_Reply != NULL) {
+ m_UpdateFile.write(m_Reply->readAll());
+ }
+}
+
+
+void SelfUpdater::downloadFinished()
+{
+ int error = QNetworkReply::NoError;
+
+ if (m_Reply != NULL) {
+ m_UpdateFile.write(m_Reply->readAll());
+
+ error = m_Reply->error();
+
+ if (m_Reply->header(QNetworkRequest::ContentTypeHeader).toString().startsWith("text", Qt::CaseInsensitive)) {
+ m_Canceled = true;
+ }
+
+ m_Progress.hide();
+ m_Reply->close();
+ m_Reply->deleteLater();
+ m_Reply = NULL;
+ }
+
+ m_UpdateFile.close();
+
+ if ((m_UpdateFile.size() == 0) ||
+ (error != QNetworkReply::NoError) ||
+ m_Canceled) {
+ if (!m_Canceled) {
+ reportError(tr("Download failed: %1").arg(error));
+ }
+ m_UpdateFile.remove();
+ return;
+ }
+
+ qDebug("download: %s", m_UpdateFile.fileName().toUtf8().constData());
+
+ try {
+ installUpdate();
+ } catch (const std::exception &e) {
+ reportError(tr("Failed to install update: %1").arg(e.what()));
+ }
+}
+
+
+void SelfUpdater::downloadCancel()
+{
+ m_Canceled = true;
+}
+
+
+void SelfUpdater::installUpdate()
+{
+ const QString mopath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory()));
+
+ QString backupPath = mopath.mid(0).append("/update_backup");
+ QDir().mkdir(backupPath);
+
+ // rename files that are currently open so we can unpack the update
+ if (!m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(m_UpdateFile.fileName())).c_str(),
+ new MethodCallback<SelfUpdater, void, LPSTR>(this, &SelfUpdater::queryPassword))) {
+ throw MyException(tr("failed to open archive \"%1\": %2")
+ .arg(m_UpdateFile.fileName())
+ .arg(InstallationManager::getErrorString(m_CurrentArchive->getLastError())));
+ }
+
+ // move all files contained in the archive out of the way,
+ // otherwise we can't overwrite everything
+ FileData* const *data;
+ size_t size;
+ m_CurrentArchive->getFileList(data, size);
+
+ for (size_t i = 0; i < size; ++i) {
+ QString outputName = ToQString(data[i]->getFileName());
+ if (outputName.startsWith("ModOrganizer\\", Qt::CaseInsensitive)) {
+ outputName = outputName.mid(13);
+ data[i]->setOutputFileName(ToWString(outputName).c_str());
+ } else if (outputName == "ModOrganizer") {
+ data[i]->setSkip(true);
+ }
+ QFile file(mopath.mid(0).append("/").append(outputName));
+ if (file.exists()) {
+ file.rename(backupPath.mid(0).append("/").append(outputName));
+ }
+ }
+
+ // now unpack the archive into the mo directory
+ if (!m_CurrentArchive->extract(GameInfo::instance().getOrganizerDirectory().c_str(),
+ new MethodCallback<SelfUpdater, void, float>(this, &SelfUpdater::updateProgress),
+ new MethodCallback<SelfUpdater, void, LPCWSTR>(this, &SelfUpdater::updateProgressFile),
+ new MethodCallback<SelfUpdater, void, LPCWSTR>(this, &SelfUpdater::report7ZipError))) {
+ throw std::runtime_error("extracting failed");
+ }
+
+ m_CurrentArchive->close();
+
+ m_UpdateFile.remove();
+
+ QMessageBox::information(m_Parent, tr("Update"), tr("Update installed, Mod Organizer will now be restarted."));
+
+ QProcess newProcess;
+ if (QFile::exists(mopath.mid(0).append("/ModOrganizer.exe"))) {
+ newProcess.startDetached(mopath.mid(0).append("/ModOrganizer.exe"), QStringList("update"));
+ } else {
+ newProcess.startDetached(mopath.mid(0).append("/ModOrganiser.exe"), QStringList("update"));
+ }
+ emit restart();
+}
+
+void SelfUpdater::queryPassword(LPSTR)
+{
+ // nop
+}
+
+void SelfUpdater::updateProgress(float)
+{
+ // nop
+}
+
+void SelfUpdater::updateProgressFile(LPCWSTR)
+{
+ // nop
+}
+
+void SelfUpdater::report7ZipError(LPCWSTR errorMessage)
+{
+ QMessageBox::critical(m_Parent, tr("Error"), ToQString(errorMessage));
+}
+
+
+QString SelfUpdater::retrieveNews(const QString &description)
+{
+ QStringList temp = description.split("[s][/s]");
+ if (temp.length() < 2) {
+ return QString();
+ } else {
+ return temp.at(1);
+ }
+}
+
+
+void SelfUpdater::nxmDescriptionAvailable(int, QVariant, QVariant resultData, int requestID)
+{
+ if (requestID == m_UpdateRequestID) {
+ m_UpdateRequestID = -1;
+
+ QVariantMap result = resultData.toMap();
+ QString motd = retrieveNews(result["description"].toString()).trimmed();
+ if (motd.length() != 0) {
+ emit motdAvailable(motd);
+ }
+
+ m_NewestVersion = result["version"].toString();
+ if (m_NewestVersion.isEmpty()) {
+ QTimer::singleShot(5000, this, SLOT(testForUpdate()));
+ }
+
+ VersionInfo currentVersion(m_MOVersion);
+ VersionInfo newestVersion(m_NewestVersion);
+
+ if (!m_NewestVersion.isEmpty() && (currentVersion < newestVersion)) {
+ emit updateAvailable();
+ } else if (newestVersion < currentVersion) {
+ qDebug("this version is newer than the current version on nexus (%s vs %s)",
+ currentVersion.canonicalString().toUtf8().constData(),
+ newestVersion.canonicalString().toUtf8().constData());
+ }
+ }
+}
+
+
+void SelfUpdater::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID)
+{
+ if (requestID != m_UpdateRequestID) {
+ return;
+ }
+ QString version = userData.toString();
+
+ m_UpdateRequestID = -1;
+
+ if (!resultData.canConvert<QVariantList>()) {
+ qCritical("invalid files result: %s", resultData.toString().toUtf8().constData());
+ reportError(tr("Failed to parse response. Please report this as a bug and include the file mo_interface.log."));
+ return;
+ }
+
+ QVariantList result = resultData.toList();
+
+ QRegExp updateExpList(QString("updates version ([0-9., ]*) to %1").arg(version));
+ QRegExp updateExpRange(QString("updates version ([0-9.]*) - ([0-9.]*) to %1").arg(version));
+ int updateFileID = -1;
+ QString updateFileName;
+ int mainFileID = -1;
+ QString mainFileName;
+ int mainFileSize = 0;
+
+ foreach(QVariant file, result) {
+ QVariantMap fileInfo = file.toMap();
+ if (!fileInfo["uri"].toString().endsWith(".7z")) {
+ continue;
+ }
+
+ if (fileInfo["version"].toString() == version) {
+ if (fileInfo["category_id"].toInt() == 2) {
+ QString description = fileInfo["description"].toString();
+ // update
+ if (updateExpList.indexIn(description) != -1) {
+ // there is an update for the newest version of MO, but does
+ // it apply to the current version?
+ QStringList supportedVersions = updateExpList.cap(1).split(QRegExp(",[ ]*"), QString::SkipEmptyParts);
+ if (supportedVersions.contains(m_MOVersion.canonicalString())) {
+ updateFileID = fileInfo["id"].toInt();
+ updateFileName = fileInfo["uri"].toString();
+ } else {
+ qDebug("update not supported from %s", m_MOVersion.canonicalString().toUtf8().constData());
+ }
+ } else if (updateExpRange.indexIn(description) != -1) {
+ VersionInfo rangeLowEnd(updateExpRange.cap(1));
+ VersionInfo rangeHighEnd(updateExpRange.cap(2));
+ if ((rangeLowEnd <= m_MOVersion) &&
+ (m_MOVersion <= rangeHighEnd)) {
+ updateFileID = fileInfo["id"].toInt();
+ updateFileName = fileInfo["uri"].toString();
+ break;
+ } else {
+ qDebug("update not supported from %s", m_MOVersion.canonicalString().toUtf8().constData());
+ }
+ } else {
+ qWarning("invalid update description: %s",
+ description.toUtf8().constData());
+ }
+ } else if (fileInfo["category_id"].toInt() == 1) {
+ mainFileID = fileInfo["id"].toInt();
+ mainFileName = fileInfo["uri"].toString();
+ mainFileSize = fileInfo["size"].toInt();
+ }
+ }
+ }
+
+ if (updateFileID != -1) {
+ qDebug("update available: %d", updateFileID);
+ m_UpdateRequestID = m_Interface->requestDownloadURL(SkyrimInfo::getNexusModIDStatic(),
+ updateFileID, this, updateFileName,
+ ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
+ } else if (mainFileID != -1) {
+ qDebug("full download required: %d", mainFileID);
+ if (QMessageBox::question(m_Parent, tr("Update"),
+ tr("No incremental update available for this version, "
+ "the complete package needs to be downloaded (%1 kB)").arg(mainFileSize),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
+ m_UpdateRequestID = m_Interface->requestDownloadURL(SkyrimInfo::getNexusModIDStatic(),
+ mainFileID, this, mainFileName,
+ ToQString(SkyrimInfo::getNexusInfoUrlStatic()));
+ }
+ } else {
+ qCritical("no file for update found");
+ MessageDialog::showMessage(tr("no file for update found. Please update manually."), m_Parent);
+ m_Progress.hide();
+ }
+}
+
+
+void SelfUpdater::nxmRequestFailed(int, QVariant, int requestID, const QString &errorMessage)
+{
+ if (requestID == m_UpdateRequestID) {
+ m_UpdateRequestID = -1;
+ if (m_Attempts > 0) {
+ QTimer::singleShot(60000, this, SLOT(testForUpdate()));
+ --m_Attempts;
+ } else {
+ MessageDialog::showMessage(tr("Failed to retrieve update information: %1").arg(errorMessage), m_Parent);
+ }
+ }
+}
+
+
+void SelfUpdater::nxmDownloadURLsAvailable(int, int, QVariant userData, QVariant resultData, int requestID)
+{
+ if (requestID == m_UpdateRequestID) {
+ m_UpdateRequestID = -1;
+ QVariantList serverList = resultData.toList();
+ if (serverList.count() != 0) {
+ qSort(serverList.begin(), serverList.end(), DownloadManager::ServerByPreference);
+
+ QVariantMap dlServer = serverList.first().toMap();
+
+ download(dlServer["URI"].toString(), userData.toString());
+ } else {
+ MessageDialog::showMessage(tr("No download server available. Please try again later."), m_Parent);
+ m_Progress.hide();
+ }
+ }
+}
+
diff --git a/src/selfupdater.h b/src/selfupdater.h
index 3a92b769..2c4c1ecd 100644
--- a/src/selfupdater.h
+++ b/src/selfupdater.h
@@ -17,129 +17,129 @@ 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 SELFUPDATER_H
-#define SELFUPDATER_H
-
-
-#include "nexusinterface.h"
-#include <archive.h>
-#include <versioninfo.h>
-
-#include <QObject>
-#include <QNetworkReply>
-#include <QFile>
-#include <QProgressDialog>
-
-
-/**
- * @brief manages updates for Mod Organizer itself
- * This class is used to update the Mod Organizer
- * The process looks like this:
- * 1. call testForUpdate() to determine is available
- * 2. if the updateAvailable() signal is received, allow the user to start the update
- * 3. if the user start the update, call startUpdate()
- * 4. startUpdate() will first query a list of files, try to determine if there is an
- * incremental update. If not, the user will have to confirm the download of a full download.
- * Once the correct file is selected, it is downloaded.
- * 5. before the downloaded file is extracted, existing files that are going to be replaced are
- * moved to "update_backup" on because files that are currently open can't be replaced.
- * 6. the update is extracted and then deleted
- * 7. finally, a restart is requested via signal.
- * 8. at restart, Mod Organizer will remove the update_backup directory since none of the files
- * should now be open
- *
- * @todo use NexusBridge
- **/
-class SelfUpdater : public QObject
-{
-
- Q_OBJECT
-
-public:
-
- /**
- * @brief constructor
- *
- * @param nexusInterface interface to query information from nexus
- * @param parent parent widget
- * @todo passing the nexus interface is unneccessary
- **/
- SelfUpdater(NexusInterface *nexusInterface, QWidget *parent);
- ~SelfUpdater();
-
- /**
- * @brief start the update process
- * @note this should not be called if there is no update available
- **/
- void startUpdate();
-
- /**
- * @return current version of Mod Organizer
- **/
- VersionInfo getVersion() const { return m_MOVersion; }
-
-public slots:
-
- /**
- * @brief request information about the current version
- **/
- void testForUpdate();
-
- void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
- void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage);
- void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
-
-signals:
-
- /**
- * @brief emitted if a restart of the client is necessary to complete the update
- **/
- void restart();
-
- /**
- * @brief emitted if an update is available
- **/
- void updateAvailable();
-
- /**
- * @brief emitted if a message of the day was received
- **/
- void motdAvailable(const QString &motd);
-
-private:
-
- void download(const QString &downloadLink, const QString &fileName);
- void installUpdate();
- void queryPassword(LPSTR password);
- void updateProgress(float percentage);
- void updateProgressFile(LPCWSTR fileName);
- void report7ZipError(LPCWSTR errorMessage);
- QString retrieveNews(const QString &description);
-
-private slots:
-
- void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
- void downloadReadyRead();
- void downloadFinished();
- void downloadCancel();
-
-private:
-
- QWidget *m_Parent;
- VersionInfo m_MOVersion;
- NexusInterface *m_Interface;
- int m_UpdateRequestID;
- QString m_NewestVersion;
- QFile m_UpdateFile;
- QNetworkReply *m_Reply;
- QProgressDialog m_Progress;
- bool m_Canceled;
- int m_Attempts;
-
- Archive *m_CurrentArchive;
-
-};
-
-
-#endif // SELFUPDATER_H
+#ifndef SELFUPDATER_H
+#define SELFUPDATER_H
+
+
+#include "nexusinterface.h"
+#include <archive.h>
+#include <versioninfo.h>
+
+#include <QObject>
+#include <QNetworkReply>
+#include <QFile>
+#include <QProgressDialog>
+
+
+/**
+ * @brief manages updates for Mod Organizer itself
+ * This class is used to update the Mod Organizer
+ * The process looks like this:
+ * 1. call testForUpdate() to determine is available
+ * 2. if the updateAvailable() signal is received, allow the user to start the update
+ * 3. if the user start the update, call startUpdate()
+ * 4. startUpdate() will first query a list of files, try to determine if there is an
+ * incremental update. If not, the user will have to confirm the download of a full download.
+ * Once the correct file is selected, it is downloaded.
+ * 5. before the downloaded file is extracted, existing files that are going to be replaced are
+ * moved to "update_backup" on because files that are currently open can't be replaced.
+ * 6. the update is extracted and then deleted
+ * 7. finally, a restart is requested via signal.
+ * 8. at restart, Mod Organizer will remove the update_backup directory since none of the files
+ * should now be open
+ *
+ * @todo use NexusBridge
+ **/
+class SelfUpdater : public QObject
+{
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief constructor
+ *
+ * @param nexusInterface interface to query information from nexus
+ * @param parent parent widget
+ * @todo passing the nexus interface is unneccessary
+ **/
+ SelfUpdater(NexusInterface *nexusInterface, QWidget *parent);
+ ~SelfUpdater();
+
+ /**
+ * @brief start the update process
+ * @note this should not be called if there is no update available
+ **/
+ void startUpdate();
+
+ /**
+ * @return current version of Mod Organizer
+ **/
+ MOBase::VersionInfo getVersion() const { return m_MOVersion; }
+
+public slots:
+
+ /**
+ * @brief request information about the current version
+ **/
+ void testForUpdate();
+
+ void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID);
+ void nxmRequestFailed(int modID, QVariant userData, int requestID, const QString &errorMessage);
+ void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
+
+signals:
+
+ /**
+ * @brief emitted if a restart of the client is necessary to complete the update
+ **/
+ void restart();
+
+ /**
+ * @brief emitted if an update is available
+ **/
+ void updateAvailable();
+
+ /**
+ * @brief emitted if a message of the day was received
+ **/
+ void motdAvailable(const QString &motd);
+
+private:
+
+ void download(const QString &downloadLink, const QString &fileName);
+ void installUpdate();
+ void queryPassword(LPSTR password);
+ void updateProgress(float percentage);
+ void updateProgressFile(LPCWSTR fileName);
+ void report7ZipError(LPCWSTR errorMessage);
+ QString retrieveNews(const QString &description);
+
+private slots:
+
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ void downloadReadyRead();
+ void downloadFinished();
+ void downloadCancel();
+
+private:
+
+ QWidget *m_Parent;
+ MOBase::VersionInfo m_MOVersion;
+ NexusInterface *m_Interface;
+ int m_UpdateRequestID;
+ QString m_NewestVersion;
+ QFile m_UpdateFile;
+ QNetworkReply *m_Reply;
+ QProgressDialog m_Progress;
+ bool m_Canceled;
+ int m_Attempts;
+
+ Archive *m_CurrentArchive;
+
+};
+
+
+#endif // SELFUPDATER_H
diff --git a/src/settings.cpp b/src/settings.cpp
index 7c12e815..e7553676 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -17,494 +17,498 @@ 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 "settings.h"
-
-#include "settingsdialog.h"
-#include "utility.h"
-#include "helper.h"
-#include <gameinfo.h>
-#include <appconfig.h>
-#include <utility.h>
-
-#include <QCheckBox>
-#include <QLineEdit>
-#include <QDirIterator>
-#include <QRegExp>
-#include <QCoreApplication>
-#include <QMessageBox>
-
-
-static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60, 0xa4, 0x5b, 0x01,
- 0xdb, 0xf8, 0x43, 0x3a, 0xb7, 0xb6, 0x98, 0xd4, 0x7d, 0xa2 };
-
-Settings *Settings::s_Instance = NULL;
-
-
-Settings::Settings()
- : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat)
-{
- if (s_Instance != NULL) {
- throw std::runtime_error("second instance of \"Settings\" created");
- } else {
- s_Instance = this;
- }
-}
-
-
-Settings::~Settings()
-{
- s_Instance = NULL;
-}
-
-
-Settings &Settings::instance()
-{
- if (s_Instance == NULL) {
- throw std::runtime_error("no instance of \"Settings\"");
- }
- return *s_Instance;
-}
-
-void Settings::clearPlugins()
-{
- m_Plugins.clear();
- m_PluginSettings.clear();
-}
-
-void Settings::registerPlugin(IPlugin *plugin)
-{
- m_Plugins.push_back(plugin);
- foreach (const PluginSetting &setting, plugin->settings()) {
- QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue);
- if (!temp.convert(setting.defaultValue.type())) {
- qWarning("failed to interpret \"%s\" as correct type for \"%s\" in plugin \"%s\", using default",
- qPrintable(temp.toString()), qPrintable(setting.key), qPrintable(plugin->name()));
- temp = setting.defaultValue;
- }
- m_PluginSettings[plugin->name()][setting.key] = temp;
- }
-}
-
-
-QString Settings::obfuscate(const QString &password) const
-{
- QByteArray temp = password.toUtf8();
-
- QByteArray buffer;
- for (int i = 0; i < temp.length(); ++i) {
- buffer.append(temp.at(i) ^ Key2[i % 20]);
- }
- return buffer.toBase64();
-}
-
-
-QString Settings::deObfuscate(const QString &password) const
-{
- QByteArray temp(QByteArray::fromBase64(password.toUtf8()));
-
- QByteArray buffer;
- for (int i = 0; i < temp.length(); ++i) {
- buffer.append(temp.at(i) ^ Key2[i % 20]);
- }
- return QString::fromUtf8(buffer.constData());
-}
-
-
-bool Settings::hideUncheckedPlugins() const
-{
- return m_Settings.value("Settings/hide_unchecked_plugins", false).toBool();
-}
-
-bool Settings::forceEnableCoreFiles() const
-{
- return m_Settings.value("Settings/force_enable_core_files", true).toBool();
-}
-
-bool Settings::automaticLoginEnabled() const
-{
- return m_Settings.value("Settings/nexus_login", false).toBool();
-}
-
-QString Settings::getSteamAppID() const
-{
- return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId())).toString();
-}
-
-QString Settings::getDownloadDirectory() const
-{
- return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString());
-}
-
-QString Settings::getCacheDirectory() const
-{
- return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString());
-}
-
-QString Settings::getModDirectory() const
-{
- return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString());
-}
-
-QString Settings::getNMMVersion() const
-{
- return m_Settings.value("Settings/nmm_version", "0.34.0").toString();
-}
-
-bool Settings::getNexusLogin(QString &username, QString &password) const
-{
- if (m_Settings.value("Settings/nexus_login", false).toBool()) {
- username = m_Settings.value("Settings/nexus_username", "").toString();
- password = deObfuscate(m_Settings.value("Settings/nexus_password", "").toString());
- return true;
- } else {
- return false;
- }
-}
-
-int Settings::logLevel() const
-{
- return m_Settings.value("Settings/log_level", 0).toInt();
-}
-
-
-void Settings::setNexusLogin(QString username, QString password)
-{
- m_Settings.setValue("Settings/nexus_login", true);
- m_Settings.setValue("Settings/nexus_username", username);
- m_Settings.setValue("Settings/nexus_password", obfuscate(password));
-}
-
-
-LoadMechanism::EMechanism Settings::getLoadMechanism() const
-{
- switch (m_Settings.value("Settings/load_mechanism").toInt()) {
- case LoadMechanism::LOAD_MODORGANIZER: return LoadMechanism::LOAD_MODORGANIZER;
- case LoadMechanism::LOAD_SCRIPTEXTENDER: return LoadMechanism::LOAD_SCRIPTEXTENDER;
- case LoadMechanism::LOAD_PROXYDLL: return LoadMechanism::LOAD_PROXYDLL;
- }
- throw std::runtime_error("invalid load mechanism");
-}
-
-
-void Settings::setupLoadMechanism()
-{
- m_LoadMechanism.activate(getLoadMechanism());
-}
-
-
-bool Settings::preferIntegratedInstallers()
-{
- return m_Settings.value("Settings/prefer_integrated_installer").toBool();
-}
-
-
-bool Settings::enableQuickInstaller()
-{
- return m_Settings.value("Settings/enable_quick_installer").toBool();
-}
-
-
-bool Settings::preferExternalBrowser()
-{
- return m_Settings.value("Settings/prefer_external_browser").toBool();
-}
-
-
-void Settings::setMotDHash(uint hash)
-{
- m_Settings.setValue("motd_hash", hash);
-}
-
-uint Settings::getMotDHash() const
-{
- return m_Settings.value("motd_hash", 0).toUInt();
-}
-
-QVariant Settings::pluginSetting(const QString &pluginName, const QString &key) const
-{
- auto iterPlugin = m_PluginSettings.find(pluginName);
- if (iterPlugin == m_PluginSettings.end()) {
- throw MyException(tr("setting for invalid plugin \"%1\" requested").arg(key));
- }
- auto iterSetting = iterPlugin->find(key);
- if (iterSetting == iterPlugin->end()) {
- throw MyException(tr("invalid setting \"%1\" requested for plugin \"%2\"").arg(key).arg(pluginName));
- }
-
- return *iterSetting;
-}
-
-
-void Settings::addLanguages(QComboBox *languageBox)
-{
- languageBox->addItem("English", "en_US");
-
- QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files);
- QString pattern = ToQString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm";
- QRegExp exp(pattern);
- while (langIter.hasNext()) {
- langIter.next();
- QString file = langIter.fileName();
- if (exp.exactMatch(file)) {
- QString languageCode = exp.cap(1);
- QLocale locale(languageCode);
- QString languageString = QLocale::languageToString(locale.language());
- if (locale.language() == QLocale::Chinese) {
- if (languageCode == "zh_TW") {
- languageString = "Chinese (traditional)";
- } else {
- languageString = "Chinese (simplified)";
- }
- }
-
- languageBox->addItem(QString("%1").arg(languageString), exp.cap(1));
- }
- }
-}
-
-void Settings::addStyles(QComboBox *styleBox)
-{
- styleBox->addItem("None", "");
- QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files);
- while (langIter.hasNext()) {
- langIter.next();
- QString style = langIter.fileName();
- styleBox->addItem(style, style);
- }
-}
-
-bool Settings::isNXMHandler(bool *modifyable)
-{
- QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\shell\\open\\command",
- QSettings::NativeFormat);
-
- QString currentExe = handlerReg.value("Default", "").toString().toUtf8().constData();
- QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\"");
- if (modifyable != NULL) {
- handlerReg.setValue("Default", currentExe);
- handlerReg.sync();
-
- *modifyable = handlerReg.status() == QSettings::NoError;
- // QSettings::isWritable returns wrong results...
- }
- return currentExe == myExe;
-}
-
-
-void Settings::setNXMHandlerActive(bool active, bool writable)
-{
- QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\",
- QSettings::NativeFormat);
-
- if (writable) {
- QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\"");
- handlerReg.setValue("Default", "URL:NXM Protocol");
- handlerReg.setValue("URL Protocol", "");
- handlerReg.setValue("shell/open/command/Default", active ? myExe : "");
- handlerReg.sync();
- } else {
- Helper::setNXMHandler(GameInfo::instance().getOrganizerDirectory(), active);
- }
-}
-
-
-void Settings::resetDialogs()
-{
- m_Settings.beginGroup("DialogChoices");
- QStringList keys = m_Settings.childKeys();
- foreach (QString key, keys) {
- m_Settings.remove(key);
- }
-
- m_Settings.endGroup();
-}
-
-
-void Settings::query(QWidget *parent)
-{
- SettingsDialog dialog(parent);
-
- connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs()));
-
- QCheckBox *hideUncheckedBox = dialog.findChild<QCheckBox*>("hideUncheckedBox");
- QCheckBox *forceEnableBox = dialog.findChild<QCheckBox*>("forceEnableBox");
- QComboBox *mechanismBox = dialog.findChild<QComboBox*>("mechanismBox");
-
- QCheckBox *loginCheckBox = dialog.findChild<QCheckBox*>("loginCheckBox");
- QLineEdit *usernameEdit = dialog.findChild<QLineEdit*>("usernameEdit");
- QLineEdit *passwordEdit = dialog.findChild<QLineEdit*>("passwordEdit");
-
- QLineEdit *appIDEdit = dialog.findChild<QLineEdit*>("appIDEdit");
-
- QComboBox *languageBox = dialog.findChild<QComboBox*>("languageBox");
- QComboBox *styleBox = dialog.findChild<QComboBox*>("styleBox");
- QComboBox *logLevelBox = dialog.findChild<QComboBox*>("logLevelBox");
- QCheckBox *handleNXMBox = dialog.findChild<QCheckBox*>("handleNXMBox");
-
- QLineEdit *downloadDirEdit = dialog.findChild<QLineEdit*>("downloadDirEdit");
- QLineEdit *modDirEdit = dialog.findChild<QLineEdit*>("modDirEdit");
- QLineEdit *cacheDirEdit = dialog.findChild<QLineEdit*>("cacheDirEdit");
-
- QCheckBox *preferIntegratedCheckBox = dialog.findChild<QCheckBox*>("preferIntegratedBox");
- QCheckBox *preferExternalBox = dialog.findChild<QCheckBox*>("preferExternalBox");
- QCheckBox *quickInstallerBox = dialog.findChild<QCheckBox*>("quickInstallerBox");
- QLineEdit *nmmVersionEdit = dialog.findChild<QLineEdit*>("nmmVersionEdit");
-
- QListWidget *pluginsList = dialog.findChild<QListWidget*>("pluginsList");
-
- //
- // set up current settings
- //
- LoadMechanism::EMechanism mechanismID = getLoadMechanism();
- int index = 0;
-
- if (m_LoadMechanism.isDirectLoadingSupported()) {
- mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER);
- if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) {
- index = mechanismBox->count() - 1;
- }
- }
-
- if (m_LoadMechanism.isScriptExtenderSupported()) {
- mechanismBox->addItem(QObject::tr("Script Extender"), LoadMechanism::LOAD_SCRIPTEXTENDER);
- if (mechanismID == LoadMechanism::LOAD_SCRIPTEXTENDER) {
- index = mechanismBox->count() - 1;
- }
- }
-
- if (m_LoadMechanism.isProxyDLLSupported()) {
- mechanismBox->addItem(QObject::tr("Proxy DLL"), LoadMechanism::LOAD_PROXYDLL);
- if (mechanismID == LoadMechanism::LOAD_PROXYDLL) {
- index = mechanismBox->count() - 1;
- }
- }
-
- mechanismBox->setCurrentIndex(index);
-
- {
- addLanguages(languageBox);
- int currentID = languageBox->findData(m_Settings.value("Settings/language", QLocale::system().name()).toString());
- if (currentID != -1) {
- languageBox->setCurrentIndex(currentID);
- }
- }
-
- {
- addStyles(styleBox);
- int currentID = styleBox->findData(m_Settings.value("Settings/style", "").toString());
- if (currentID != -1) {
- styleBox->setCurrentIndex(currentID);
- }
- }
-
- hideUncheckedBox->setChecked(hideUncheckedPlugins());
- forceEnableBox->setChecked(forceEnableCoreFiles());
-
- appIDEdit->setText(getSteamAppID());
-
- if (automaticLoginEnabled()) {
- loginCheckBox->setChecked(true);
- usernameEdit->setText(m_Settings.value("Settings/nexus_username", "").toString());
- passwordEdit->setText(deObfuscate(m_Settings.value("Settings/nexus_password", "").toString()));
- }
-
- bool registryWritable = false;
- bool nxmHandler = isNXMHandler(&registryWritable);
- handleNXMBox->setChecked(nxmHandler);
- if (!registryWritable) {
- handleNXMBox->setIcon(QIcon(":/MO/gui/locked"));
- handleNXMBox->setToolTip(tr("Administrative rights required to change this."));
- }
-
- downloadDirEdit->setText(getDownloadDirectory());
- modDirEdit->setText(getModDirectory());
- cacheDirEdit->setText(getCacheDirectory());
- preferIntegratedCheckBox->setChecked(m_Settings.value("Settings/prefer_integrated_installer", false).toBool());
- preferExternalBox->setChecked(m_Settings.value("Settings/prefer_external_browser", false).toBool());
- quickInstallerBox->setChecked(m_Settings.value("Settings/enable_quick_installer", true).toBool());
- nmmVersionEdit->setText(m_Settings.value("Settings/nmm_version", "0.33.1").toString());
- logLevelBox->setCurrentIndex(logLevel());
-
- foreach (IPlugin *plugin, m_Plugins) {
- QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), pluginsList);
- listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin));
- listItem->setData(Qt::UserRole + 1, m_PluginSettings[plugin->name()]);
- pluginsList->addItem(listItem);
- }
-
- if (dialog.exec() == QDialog::Accepted) {
- //
- // transfer modified settings to configuration file
- //
-
- m_Settings.setValue("Settings/hide_unchecked_plugins", hideUncheckedBox->checkState() ? true : false);
- m_Settings.setValue("Settings/force_enable_core_files", forceEnableBox->checkState() ? true : false);
- m_Settings.setValue("Settings/load_mechanism", mechanismBox->itemData(mechanismBox->currentIndex()).toInt());
- if (QDir(downloadDirEdit->text()).exists()) {
- m_Settings.setValue("Settings/download_directory", QDir::toNativeSeparators(downloadDirEdit->text()));
- }
- if (!QDir(cacheDirEdit->text()).exists()) {
- QDir().mkpath(cacheDirEdit->text());
- }
- m_Settings.setValue("Settings/cache_directory", QDir::toNativeSeparators(cacheDirEdit->text()));
- if (QDir(modDirEdit->text()).exists()) {
- if ((QDir::fromNativeSeparators(modDirEdit->text()) != QDir::fromNativeSeparators(getModDirectory())) &&
- (QMessageBox::question(NULL, tr("Confirm"), tr("Changing the mod directory affects all your profiles! "
- "Mods not present (or named differently) in the new location will be disabled in all profiles. "
- "There is no way to undo this unless you backed up your profiles manually. Proceed?"),
- QMessageBox::Yes | QMessageBox::No))) {
- m_Settings.setValue("Settings/mod_directory", QDir::toNativeSeparators(modDirEdit->text()));
- }
- }
- QString oldLanguage = m_Settings.value("Settings/language", "en_US").toString();
- QString newLanguage = languageBox->itemData(languageBox->currentIndex()).toString();
- if (newLanguage != oldLanguage) {
- m_Settings.setValue("Settings/language", newLanguage);
- emit languageChanged(newLanguage);
- }
-
- QString oldStyle = m_Settings.value("Settings/style", "").toString();
- QString newStyle = styleBox->itemData(styleBox->currentIndex()).toString();
- if (oldStyle != newStyle) {
- m_Settings.setValue("Settings/style", newStyle);
- emit styleChanged(newStyle);
- }
-
- m_Settings.setValue("Settings/log_level", logLevelBox->currentIndex());
-
- if (appIDEdit->text() != ToQString(GameInfo::instance().getSteamAPPId())) {
- m_Settings.setValue("Settings/app_id", appIDEdit->text());
- } else {
- m_Settings.remove("Settings/app_id");
- }
- if (loginCheckBox->isChecked()) {
- m_Settings.setValue("Settings/nexus_login", true);
- m_Settings.setValue("Settings/nexus_username", usernameEdit->text());
- m_Settings.setValue("Settings/nexus_password", obfuscate(passwordEdit->text()));
- } else {
- m_Settings.setValue("Settings/nexus_login", false);
- m_Settings.remove("Settings/nexus_username");
- m_Settings.remove("Settings/nexus_password");
- }
- if (nxmHandler != handleNXMBox->isChecked()) {
- setNXMHandlerActive(handleNXMBox->isChecked(), registryWritable);
- }
- m_Settings.setValue("Settings/prefer_integrated_installer", preferIntegratedCheckBox->isChecked());
- m_Settings.setValue("Settings/prefer_external_browser", preferExternalBox->isChecked());
- m_Settings.setValue("Settings/enable_quick_installer", quickInstallerBox->isChecked());
-
- m_Settings.setValue("Settings/nmm_version", nmmVersionEdit->text());
-
- // transfer plugin settings to in-memory structure
- for (int i = 0; i < pluginsList->count(); ++i) {
- QListWidgetItem *item = pluginsList->item(i);
- m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap();
- }
- // store plugin settings on disc
- for (auto iterPlugins = m_PluginSettings.begin(); iterPlugins != m_PluginSettings.end(); ++iterPlugins) {
- for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) {
- m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value());
- }
- }
- }
-}
+#include "settings.h"
+
+#include "settingsdialog.h"
+#include "utility.h"
+#include "helper.h"
+#include <gameinfo.h>
+#include <appconfig.h>
+#include <utility.h>
+
+#include <QCheckBox>
+#include <QLineEdit>
+#include <QDirIterator>
+#include <QRegExp>
+#include <QCoreApplication>
+#include <QMessageBox>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+static const unsigned char Key2[20] = { 0x99, 0xb8, 0x76, 0x42, 0x3e, 0xc1, 0x60, 0xa4, 0x5b, 0x01,
+ 0xdb, 0xf8, 0x43, 0x3a, 0xb7, 0xb6, 0x98, 0xd4, 0x7d, 0xa2 };
+
+Settings *Settings::s_Instance = NULL;
+
+
+Settings::Settings()
+ : m_Settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat)
+{
+ if (s_Instance != NULL) {
+ throw std::runtime_error("second instance of \"Settings\" created");
+ } else {
+ s_Instance = this;
+ }
+}
+
+
+Settings::~Settings()
+{
+ s_Instance = NULL;
+}
+
+
+Settings &Settings::instance()
+{
+ if (s_Instance == NULL) {
+ throw std::runtime_error("no instance of \"Settings\"");
+ }
+ return *s_Instance;
+}
+
+void Settings::clearPlugins()
+{
+ m_Plugins.clear();
+ m_PluginSettings.clear();
+}
+
+void Settings::registerPlugin(IPlugin *plugin)
+{
+ m_Plugins.push_back(plugin);
+ foreach (const PluginSetting &setting, plugin->settings()) {
+ QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue);
+ if (!temp.convert(setting.defaultValue.type())) {
+ qWarning("failed to interpret \"%s\" as correct type for \"%s\" in plugin \"%s\", using default",
+ qPrintable(temp.toString()), qPrintable(setting.key), qPrintable(plugin->name()));
+ temp = setting.defaultValue;
+ }
+ m_PluginSettings[plugin->name()][setting.key] = temp;
+ }
+}
+
+
+QString Settings::obfuscate(const QString &password) const
+{
+ QByteArray temp = password.toUtf8();
+
+ QByteArray buffer;
+ for (int i = 0; i < temp.length(); ++i) {
+ buffer.append(temp.at(i) ^ Key2[i % 20]);
+ }
+ return buffer.toBase64();
+}
+
+
+QString Settings::deObfuscate(const QString &password) const
+{
+ QByteArray temp(QByteArray::fromBase64(password.toUtf8()));
+
+ QByteArray buffer;
+ for (int i = 0; i < temp.length(); ++i) {
+ buffer.append(temp.at(i) ^ Key2[i % 20]);
+ }
+ return QString::fromUtf8(buffer.constData());
+}
+
+
+bool Settings::hideUncheckedPlugins() const
+{
+ return m_Settings.value("Settings/hide_unchecked_plugins", false).toBool();
+}
+
+bool Settings::forceEnableCoreFiles() const
+{
+ return m_Settings.value("Settings/force_enable_core_files", true).toBool();
+}
+
+bool Settings::automaticLoginEnabled() const
+{
+ return m_Settings.value("Settings/nexus_login", false).toBool();
+}
+
+QString Settings::getSteamAppID() const
+{
+ return m_Settings.value("Settings/app_id", ToQString(GameInfo::instance().getSteamAPPId())).toString();
+}
+
+QString Settings::getDownloadDirectory() const
+{
+ return QDir::toNativeSeparators(m_Settings.value("Settings/download_directory", ToQString(GameInfo::instance().getDownloadDir())).toString());
+}
+
+QString Settings::getCacheDirectory() const
+{
+ return QDir::toNativeSeparators(m_Settings.value("Settings/cache_directory", ToQString(GameInfo::instance().getCacheDir())).toString());
+}
+
+QString Settings::getModDirectory() const
+{
+ return QDir::toNativeSeparators(m_Settings.value("Settings/mod_directory", ToQString(GameInfo::instance().getModsDir())).toString());
+}
+
+QString Settings::getNMMVersion() const
+{
+ return m_Settings.value("Settings/nmm_version", "0.34.0").toString();
+}
+
+bool Settings::getNexusLogin(QString &username, QString &password) const
+{
+ if (m_Settings.value("Settings/nexus_login", false).toBool()) {
+ username = m_Settings.value("Settings/nexus_username", "").toString();
+ password = deObfuscate(m_Settings.value("Settings/nexus_password", "").toString());
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int Settings::logLevel() const
+{
+ return m_Settings.value("Settings/log_level", 0).toInt();
+}
+
+
+void Settings::setNexusLogin(QString username, QString password)
+{
+ m_Settings.setValue("Settings/nexus_login", true);
+ m_Settings.setValue("Settings/nexus_username", username);
+ m_Settings.setValue("Settings/nexus_password", obfuscate(password));
+}
+
+
+LoadMechanism::EMechanism Settings::getLoadMechanism() const
+{
+ switch (m_Settings.value("Settings/load_mechanism").toInt()) {
+ case LoadMechanism::LOAD_MODORGANIZER: return LoadMechanism::LOAD_MODORGANIZER;
+ case LoadMechanism::LOAD_SCRIPTEXTENDER: return LoadMechanism::LOAD_SCRIPTEXTENDER;
+ case LoadMechanism::LOAD_PROXYDLL: return LoadMechanism::LOAD_PROXYDLL;
+ }
+ throw std::runtime_error("invalid load mechanism");
+}
+
+
+void Settings::setupLoadMechanism()
+{
+ m_LoadMechanism.activate(getLoadMechanism());
+}
+
+
+bool Settings::preferIntegratedInstallers()
+{
+ return m_Settings.value("Settings/prefer_integrated_installer").toBool();
+}
+
+
+bool Settings::enableQuickInstaller()
+{
+ return m_Settings.value("Settings/enable_quick_installer").toBool();
+}
+
+
+bool Settings::preferExternalBrowser()
+{
+ return m_Settings.value("Settings/prefer_external_browser").toBool();
+}
+
+
+void Settings::setMotDHash(uint hash)
+{
+ m_Settings.setValue("motd_hash", hash);
+}
+
+uint Settings::getMotDHash() const
+{
+ return m_Settings.value("motd_hash", 0).toUInt();
+}
+
+QVariant Settings::pluginSetting(const QString &pluginName, const QString &key) const
+{
+ auto iterPlugin = m_PluginSettings.find(pluginName);
+ if (iterPlugin == m_PluginSettings.end()) {
+ throw MyException(tr("setting for invalid plugin \"%1\" requested").arg(key));
+ }
+ auto iterSetting = iterPlugin->find(key);
+ if (iterSetting == iterPlugin->end()) {
+ throw MyException(tr("invalid setting \"%1\" requested for plugin \"%2\"").arg(key).arg(pluginName));
+ }
+
+ return *iterSetting;
+}
+
+
+void Settings::addLanguages(QComboBox *languageBox)
+{
+ languageBox->addItem("English", "en_US");
+
+ QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files);
+ QString pattern = ToQString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm";
+ QRegExp exp(pattern);
+ while (langIter.hasNext()) {
+ langIter.next();
+ QString file = langIter.fileName();
+ if (exp.exactMatch(file)) {
+ QString languageCode = exp.cap(1);
+ QLocale locale(languageCode);
+ QString languageString = QLocale::languageToString(locale.language());
+ if (locale.language() == QLocale::Chinese) {
+ if (languageCode == "zh_TW") {
+ languageString = "Chinese (traditional)";
+ } else {
+ languageString = "Chinese (simplified)";
+ }
+ }
+
+ languageBox->addItem(QString("%1").arg(languageString), exp.cap(1));
+ }
+ }
+}
+
+void Settings::addStyles(QComboBox *styleBox)
+{
+ styleBox->addItem("None", "");
+ QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files);
+ while (langIter.hasNext()) {
+ langIter.next();
+ QString style = langIter.fileName();
+ styleBox->addItem(style, style);
+ }
+}
+
+bool Settings::isNXMHandler(bool *modifyable)
+{
+ QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\shell\\open\\command",
+ QSettings::NativeFormat);
+
+ QString currentExe = handlerReg.value("Default", "").toString().toUtf8().constData();
+ QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\"");
+ if (modifyable != NULL) {
+ handlerReg.setValue("Default", currentExe);
+ handlerReg.sync();
+
+ *modifyable = handlerReg.status() == QSettings::NoError;
+ // QSettings::isWritable returns wrong results...
+ }
+ return currentExe == myExe;
+}
+
+
+void Settings::setNXMHandlerActive(bool active, bool writable)
+{
+ QSettings handlerReg("HKEY_CLASSES_ROOT\\nxm\\",
+ QSettings::NativeFormat);
+
+ if (writable) {
+ QString myExe = QString("\"%1\" ").arg(QDir::toNativeSeparators(QCoreApplication::applicationFilePath())).append("\"%1\"");
+ handlerReg.setValue("Default", "URL:NXM Protocol");
+ handlerReg.setValue("URL Protocol", "");
+ handlerReg.setValue("shell/open/command/Default", active ? myExe : "");
+ handlerReg.sync();
+ } else {
+ Helper::setNXMHandler(GameInfo::instance().getOrganizerDirectory(), active);
+ }
+}
+
+
+void Settings::resetDialogs()
+{
+ m_Settings.beginGroup("DialogChoices");
+ QStringList keys = m_Settings.childKeys();
+ foreach (QString key, keys) {
+ m_Settings.remove(key);
+ }
+
+ m_Settings.endGroup();
+}
+
+
+void Settings::query(QWidget *parent)
+{
+ SettingsDialog dialog(parent);
+
+ connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs()));
+
+ QCheckBox *hideUncheckedBox = dialog.findChild<QCheckBox*>("hideUncheckedBox");
+ QCheckBox *forceEnableBox = dialog.findChild<QCheckBox*>("forceEnableBox");
+ QComboBox *mechanismBox = dialog.findChild<QComboBox*>("mechanismBox");
+
+ QCheckBox *loginCheckBox = dialog.findChild<QCheckBox*>("loginCheckBox");
+ QLineEdit *usernameEdit = dialog.findChild<QLineEdit*>("usernameEdit");
+ QLineEdit *passwordEdit = dialog.findChild<QLineEdit*>("passwordEdit");
+
+ QLineEdit *appIDEdit = dialog.findChild<QLineEdit*>("appIDEdit");
+
+ QComboBox *languageBox = dialog.findChild<QComboBox*>("languageBox");
+ QComboBox *styleBox = dialog.findChild<QComboBox*>("styleBox");
+ QComboBox *logLevelBox = dialog.findChild<QComboBox*>("logLevelBox");
+ QCheckBox *handleNXMBox = dialog.findChild<QCheckBox*>("handleNXMBox");
+
+ QLineEdit *downloadDirEdit = dialog.findChild<QLineEdit*>("downloadDirEdit");
+ QLineEdit *modDirEdit = dialog.findChild<QLineEdit*>("modDirEdit");
+ QLineEdit *cacheDirEdit = dialog.findChild<QLineEdit*>("cacheDirEdit");
+
+ QCheckBox *preferIntegratedCheckBox = dialog.findChild<QCheckBox*>("preferIntegratedBox");
+ QCheckBox *preferExternalBox = dialog.findChild<QCheckBox*>("preferExternalBox");
+ QCheckBox *quickInstallerBox = dialog.findChild<QCheckBox*>("quickInstallerBox");
+ QLineEdit *nmmVersionEdit = dialog.findChild<QLineEdit*>("nmmVersionEdit");
+
+ QListWidget *pluginsList = dialog.findChild<QListWidget*>("pluginsList");
+
+ //
+ // set up current settings
+ //
+ LoadMechanism::EMechanism mechanismID = getLoadMechanism();
+ int index = 0;
+
+ if (m_LoadMechanism.isDirectLoadingSupported()) {
+ mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER);
+ if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) {
+ index = mechanismBox->count() - 1;
+ }
+ }
+
+ if (m_LoadMechanism.isScriptExtenderSupported()) {
+ mechanismBox->addItem(QObject::tr("Script Extender"), LoadMechanism::LOAD_SCRIPTEXTENDER);
+ if (mechanismID == LoadMechanism::LOAD_SCRIPTEXTENDER) {
+ index = mechanismBox->count() - 1;
+ }
+ }
+
+ if (m_LoadMechanism.isProxyDLLSupported()) {
+ mechanismBox->addItem(QObject::tr("Proxy DLL"), LoadMechanism::LOAD_PROXYDLL);
+ if (mechanismID == LoadMechanism::LOAD_PROXYDLL) {
+ index = mechanismBox->count() - 1;
+ }
+ }
+
+ mechanismBox->setCurrentIndex(index);
+
+ {
+ addLanguages(languageBox);
+ int currentID = languageBox->findData(m_Settings.value("Settings/language", QLocale::system().name()).toString());
+ if (currentID != -1) {
+ languageBox->setCurrentIndex(currentID);
+ }
+ }
+
+ {
+ addStyles(styleBox);
+ int currentID = styleBox->findData(m_Settings.value("Settings/style", "").toString());
+ if (currentID != -1) {
+ styleBox->setCurrentIndex(currentID);
+ }
+ }
+
+ hideUncheckedBox->setChecked(hideUncheckedPlugins());
+ forceEnableBox->setChecked(forceEnableCoreFiles());
+
+ appIDEdit->setText(getSteamAppID());
+
+ if (automaticLoginEnabled()) {
+ loginCheckBox->setChecked(true);
+ usernameEdit->setText(m_Settings.value("Settings/nexus_username", "").toString());
+ passwordEdit->setText(deObfuscate(m_Settings.value("Settings/nexus_password", "").toString()));
+ }
+
+ bool registryWritable = false;
+ bool nxmHandler = isNXMHandler(&registryWritable);
+ handleNXMBox->setChecked(nxmHandler);
+ if (!registryWritable) {
+ handleNXMBox->setIcon(QIcon(":/MO/gui/locked"));
+ handleNXMBox->setToolTip(tr("Administrative rights required to change this."));
+ }
+
+ downloadDirEdit->setText(getDownloadDirectory());
+ modDirEdit->setText(getModDirectory());
+ cacheDirEdit->setText(getCacheDirectory());
+ preferIntegratedCheckBox->setChecked(m_Settings.value("Settings/prefer_integrated_installer", false).toBool());
+ preferExternalBox->setChecked(m_Settings.value("Settings/prefer_external_browser", false).toBool());
+ quickInstallerBox->setChecked(m_Settings.value("Settings/enable_quick_installer", true).toBool());
+ nmmVersionEdit->setText(m_Settings.value("Settings/nmm_version", "0.33.1").toString());
+ logLevelBox->setCurrentIndex(logLevel());
+
+ foreach (IPlugin *plugin, m_Plugins) {
+ QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), pluginsList);
+ listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin));
+ listItem->setData(Qt::UserRole + 1, m_PluginSettings[plugin->name()]);
+ pluginsList->addItem(listItem);
+ }
+
+ if (dialog.exec() == QDialog::Accepted) {
+ //
+ // transfer modified settings to configuration file
+ //
+
+ m_Settings.setValue("Settings/hide_unchecked_plugins", hideUncheckedBox->checkState() ? true : false);
+ m_Settings.setValue("Settings/force_enable_core_files", forceEnableBox->checkState() ? true : false);
+ m_Settings.setValue("Settings/load_mechanism", mechanismBox->itemData(mechanismBox->currentIndex()).toInt());
+ if (QDir(downloadDirEdit->text()).exists()) {
+ m_Settings.setValue("Settings/download_directory", QDir::toNativeSeparators(downloadDirEdit->text()));
+ }
+ if (!QDir(cacheDirEdit->text()).exists()) {
+ QDir().mkpath(cacheDirEdit->text());
+ }
+ m_Settings.setValue("Settings/cache_directory", QDir::toNativeSeparators(cacheDirEdit->text()));
+ if (QDir(modDirEdit->text()).exists()) {
+ if ((QDir::fromNativeSeparators(modDirEdit->text()) != QDir::fromNativeSeparators(getModDirectory())) &&
+ (QMessageBox::question(NULL, tr("Confirm"), tr("Changing the mod directory affects all your profiles! "
+ "Mods not present (or named differently) in the new location will be disabled in all profiles. "
+ "There is no way to undo this unless you backed up your profiles manually. Proceed?"),
+ QMessageBox::Yes | QMessageBox::No))) {
+ m_Settings.setValue("Settings/mod_directory", QDir::toNativeSeparators(modDirEdit->text()));
+ }
+ }
+ QString oldLanguage = m_Settings.value("Settings/language", "en_US").toString();
+ QString newLanguage = languageBox->itemData(languageBox->currentIndex()).toString();
+ if (newLanguage != oldLanguage) {
+ m_Settings.setValue("Settings/language", newLanguage);
+ emit languageChanged(newLanguage);
+ }
+
+ QString oldStyle = m_Settings.value("Settings/style", "").toString();
+ QString newStyle = styleBox->itemData(styleBox->currentIndex()).toString();
+ if (oldStyle != newStyle) {
+ m_Settings.setValue("Settings/style", newStyle);
+ emit styleChanged(newStyle);
+ }
+
+ m_Settings.setValue("Settings/log_level", logLevelBox->currentIndex());
+
+ if (appIDEdit->text() != ToQString(GameInfo::instance().getSteamAPPId())) {
+ m_Settings.setValue("Settings/app_id", appIDEdit->text());
+ } else {
+ m_Settings.remove("Settings/app_id");
+ }
+ if (loginCheckBox->isChecked()) {
+ m_Settings.setValue("Settings/nexus_login", true);
+ m_Settings.setValue("Settings/nexus_username", usernameEdit->text());
+ m_Settings.setValue("Settings/nexus_password", obfuscate(passwordEdit->text()));
+ } else {
+ m_Settings.setValue("Settings/nexus_login", false);
+ m_Settings.remove("Settings/nexus_username");
+ m_Settings.remove("Settings/nexus_password");
+ }
+ if (nxmHandler != handleNXMBox->isChecked()) {
+ setNXMHandlerActive(handleNXMBox->isChecked(), registryWritable);
+ }
+ m_Settings.setValue("Settings/prefer_integrated_installer", preferIntegratedCheckBox->isChecked());
+ m_Settings.setValue("Settings/prefer_external_browser", preferExternalBox->isChecked());
+ m_Settings.setValue("Settings/enable_quick_installer", quickInstallerBox->isChecked());
+
+ m_Settings.setValue("Settings/nmm_version", nmmVersionEdit->text());
+
+ // transfer plugin settings to in-memory structure
+ for (int i = 0; i < pluginsList->count(); ++i) {
+ QListWidgetItem *item = pluginsList->item(i);
+ m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap();
+ }
+ // store plugin settings on disc
+ for (auto iterPlugins = m_PluginSettings.begin(); iterPlugins != m_PluginSettings.end(); ++iterPlugins) {
+ for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) {
+ m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value());
+ }
+ }
+ }
+}
diff --git a/src/settings.h b/src/settings.h
index d02de1a6..04781611 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -17,207 +17,207 @@ 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 WORKAROUNDS_H
-#define WORKAROUNDS_H
-
-#include "loadmechanism.h"
-
-#include <iplugin.h>
-
-#include <QSettings>
-#include <QListWidget>
-#include <QComboBox>
-
-
-/**
- * manages the settings for Mod Organizer. The settings are not cached
- * inside the class but read/written directly from/to disc
- **/
-class Settings : public QObject
-{
-
- Q_OBJECT
-
-public:
-
- /**
- * @brief constructor
- **/
- Settings();
-
- virtual ~Settings();
-
- static Settings &instance();
-
- /**
- * unregister all plugins from settings
- */
- void clearPlugins();
-
- /**
- * @brief register plugin to be configurable
- * @param plugin the plugin to register
- */
- void registerPlugin(IPlugin *plugin);
-
- /**
- * displays a SettingsDialog that allows the user to change settings. If the
- * user accepts the changes, the settings are immediately written
- **/
- void query(QWidget *parent);
-
- /**
- * set up the settings for the specified plugins
- **/
- void addPluginSettings(const std::vector<IPlugin*> &plugins);
-
- /**
- * @return true if the user wants unchecked plugins (esp, esm) should be hidden from
- * the virtual dat adirectory
- **/
- bool hideUncheckedPlugins() const;
-
- /**
- * @return true if files of the core game are forced-enabled so the user can't accidentally disable them
- */
- bool forceEnableCoreFiles() const;
-
- /**
- * the steam appid is assigned by the steam platform to each product sold there.
- * The appid may differ between different versions of a game so it may be impossible
- * for Mod Organizer to automatically recognize it, though usually it does
- * @return the steam appid for the game
- **/
- QString getSteamAppID() const;
-
- /**
- * retrieve the directory where downloads are stored (with native separators)
- **/
- QString getDownloadDirectory() const;
-
- /**
- * retrieve the directory where mods are stored (with native separators)
- **/
- QString getModDirectory() const;
-
- /**
- * returns the version of nmm to impersonate when connecting to nexus
- **/
- QString getNMMVersion() const;
-
- /**
- * retrieve the directory where the web cache is stored (with native separators)
- **/
- QString getCacheDirectory() const;
-
- /**
- * @return true if the user has set up automatic login to nexus
- **/
- bool automaticLoginEnabled() const;
-
- /**
- * @brief retrieve the login information for nexus
- *
- * @param username (out) receives the user name for nexus
- * @param password (out) received the password for nexus
- * @return true if automatic login is active, false otherwise
- **/
- bool getNexusLogin(QString &username, QString &password) const;
-
- /**
- * @return the configured log level
- */
- int logLevel() const;
-
- /**
- * @brief set the nexus login information
- *
- * @param username username
- * @param password password
- */
- void setNexusLogin(QString username, QString password);
-
- /**
- * @return the load mechanism to be used
- **/
- LoadMechanism::EMechanism getLoadMechanism() const;
-
- /**
- * @brief activate the load mechanism selected by the user
- **/
- void setupLoadMechanism();
-
- /**
- * @return true if the user prefers the integrated installer over external variants
- **/
- bool preferIntegratedInstallers();
-
- /**
- * @return true if the user prefers to use an external browser over the integrated one
- **/
- bool preferExternalBrowser();
-
- /**
- * @return true if the user has enabled the quick installer (default true)
- **/
- bool enableQuickInstaller();
-
- /**
- * @brief sets the new motd hash
- **/
- void setMotDHash(uint hash);
-
- /**
- * @return hash of the last displayed message of the day
- **/
- uint getMotDHash() const;
-
- /**
- * @brief allows direct access to the wrapped QSettings object
- * @return the wrapped QSettings object
- */
- QSettings &directInterface() { return m_Settings; }
-
- /**
- * @brief retrieve a setting for one of the installed plugins
- * @param pluginName name of the plugin
- * @param key name of the setting to retrieve
- * @return the requested value as a QVariant
- * @throws an exception is thrown if this setting doesn't exist
- */
- QVariant pluginSetting(const QString &pluginName, const QString &key) const;
-
-private:
-
- QString obfuscate(const QString &password) const;
- QString deObfuscate(const QString &password) const;
-
- void addLanguages(QComboBox *languageBox);
- void addStyles(QComboBox *styleBox);
- bool isNXMHandler(bool *modifyable);
- void setNXMHandlerActive(bool active, bool writable);
-
-private slots:
-
- void resetDialogs();
-
-signals:
-
- void languageChanged(const QString &newLanguage);
- void styleChanged(const QString &newStyle);
-
-private:
-
- static Settings *s_Instance;
-
- QSettings m_Settings;
-
- LoadMechanism m_LoadMechanism;
-
- std::vector<IPlugin*> m_Plugins;
-
- QMap<QString, QMap<QString, QVariant> > m_PluginSettings;
-
-};
-
-#endif // WORKAROUNDS_H
+#ifndef WORKAROUNDS_H
+#define WORKAROUNDS_H
+
+#include "loadmechanism.h"
+
+#include <iplugin.h>
+
+#include <QSettings>
+#include <QListWidget>
+#include <QComboBox>
+
+
+/**
+ * manages the settings for Mod Organizer. The settings are not cached
+ * inside the class but read/written directly from/to disc
+ **/
+class Settings : public QObject
+{
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * @brief constructor
+ **/
+ Settings();
+
+ virtual ~Settings();
+
+ static Settings &instance();
+
+ /**
+ * unregister all plugins from settings
+ */
+ void clearPlugins();
+
+ /**
+ * @brief register plugin to be configurable
+ * @param plugin the plugin to register
+ */
+ void registerPlugin(MOBase::IPlugin *plugin);
+
+ /**
+ * displays a SettingsDialog that allows the user to change settings. If the
+ * user accepts the changes, the settings are immediately written
+ **/
+ void query(QWidget *parent);
+
+ /**
+ * set up the settings for the specified plugins
+ **/
+ void addPluginSettings(const std::vector<MOBase::IPlugin*> &plugins);
+
+ /**
+ * @return true if the user wants unchecked plugins (esp, esm) should be hidden from
+ * the virtual dat adirectory
+ **/
+ bool hideUncheckedPlugins() const;
+
+ /**
+ * @return true if files of the core game are forced-enabled so the user can't accidentally disable them
+ */
+ bool forceEnableCoreFiles() const;
+
+ /**
+ * the steam appid is assigned by the steam platform to each product sold there.
+ * The appid may differ between different versions of a game so it may be impossible
+ * for Mod Organizer to automatically recognize it, though usually it does
+ * @return the steam appid for the game
+ **/
+ QString getSteamAppID() const;
+
+ /**
+ * retrieve the directory where downloads are stored (with native separators)
+ **/
+ QString getDownloadDirectory() const;
+
+ /**
+ * retrieve the directory where mods are stored (with native separators)
+ **/
+ QString getModDirectory() const;
+
+ /**
+ * returns the version of nmm to impersonate when connecting to nexus
+ **/
+ QString getNMMVersion() const;
+
+ /**
+ * retrieve the directory where the web cache is stored (with native separators)
+ **/
+ QString getCacheDirectory() const;
+
+ /**
+ * @return true if the user has set up automatic login to nexus
+ **/
+ bool automaticLoginEnabled() const;
+
+ /**
+ * @brief retrieve the login information for nexus
+ *
+ * @param username (out) receives the user name for nexus
+ * @param password (out) received the password for nexus
+ * @return true if automatic login is active, false otherwise
+ **/
+ bool getNexusLogin(QString &username, QString &password) const;
+
+ /**
+ * @return the configured log level
+ */
+ int logLevel() const;
+
+ /**
+ * @brief set the nexus login information
+ *
+ * @param username username
+ * @param password password
+ */
+ void setNexusLogin(QString username, QString password);
+
+ /**
+ * @return the load mechanism to be used
+ **/
+ LoadMechanism::EMechanism getLoadMechanism() const;
+
+ /**
+ * @brief activate the load mechanism selected by the user
+ **/
+ void setupLoadMechanism();
+
+ /**
+ * @return true if the user prefers the integrated installer over external variants
+ **/
+ bool preferIntegratedInstallers();
+
+ /**
+ * @return true if the user prefers to use an external browser over the integrated one
+ **/
+ bool preferExternalBrowser();
+
+ /**
+ * @return true if the user has enabled the quick installer (default true)
+ **/
+ bool enableQuickInstaller();
+
+ /**
+ * @brief sets the new motd hash
+ **/
+ void setMotDHash(uint hash);
+
+ /**
+ * @return hash of the last displayed message of the day
+ **/
+ uint getMotDHash() const;
+
+ /**
+ * @brief allows direct access to the wrapped QSettings object
+ * @return the wrapped QSettings object
+ */
+ QSettings &directInterface() { return m_Settings; }
+
+ /**
+ * @brief retrieve a setting for one of the installed plugins
+ * @param pluginName name of the plugin
+ * @param key name of the setting to retrieve
+ * @return the requested value as a QVariant
+ * @throws an exception is thrown if this setting doesn't exist
+ */
+ QVariant pluginSetting(const QString &pluginName, const QString &key) const;
+
+private:
+
+ QString obfuscate(const QString &password) const;
+ QString deObfuscate(const QString &password) const;
+
+ void addLanguages(QComboBox *languageBox);
+ void addStyles(QComboBox *styleBox);
+ bool isNXMHandler(bool *modifyable);
+ void setNXMHandlerActive(bool active, bool writable);
+
+private slots:
+
+ void resetDialogs();
+
+signals:
+
+ void languageChanged(const QString &newLanguage);
+ void styleChanged(const QString &newStyle);
+
+private:
+
+ static Settings *s_Instance;
+
+ QSettings m_Settings;
+
+ LoadMechanism m_LoadMechanism;
+
+ std::vector<MOBase::IPlugin*> m_Plugins;
+
+ QMap<QString, QMap<QString, QVariant> > m_PluginSettings;
+
+};
+
+#endif // WORKAROUNDS_H
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index 855acdf1..8fb990c1 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -17,133 +17,137 @@ 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 "settingsdialog.h"
-#include "ui_settingsdialog.h"
-#include "categoriesdialog.h"
-#include "helper.h"
-#include <gameinfo.h>
-#include <QDirIterator>
-#include <QFileDialog>
-#include <QMessageBox>
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-
-
-SettingsDialog::SettingsDialog(QWidget *parent)
- : TutorableDialog("SettingsDialog", parent), ui(new Ui::SettingsDialog)
-{
- ui->setupUi(this);
-}
-
-SettingsDialog::~SettingsDialog()
-{
- delete ui;
-}
-
-void SettingsDialog::addPlugins(const std::vector<IPlugin*> &plugins)
-{
- foreach (IPlugin *plugin, plugins) {
- ui->pluginsList->addItem(plugin->name());
- }
-}
-
-void SettingsDialog::accept()
-{
- storeSettings(ui->pluginsList->currentItem());
- TutorableDialog::accept();
-}
-
-
-void SettingsDialog::on_loginCheckBox_toggled(bool checked)
-{
- QLineEdit *usernameEdit = findChild<QLineEdit*>("usernameEdit");
- QLineEdit *passwordEdit = findChild<QLineEdit*>("passwordEdit");
- if (checked) {
- passwordEdit->setEnabled(true);
- usernameEdit->setEnabled(true);
- } else {
- passwordEdit->setEnabled(false);
- usernameEdit->setEnabled(false);
- }
-}
-
-void SettingsDialog::on_categoriesBtn_clicked()
-{
- CategoriesDialog dialog(this);
- if (dialog.exec() == QDialog::Accepted) {
- dialog.commitChanges();
- }
-}
-
-void SettingsDialog::on_bsaDateBtn_clicked()
-{
- Helper::backdateBSAs(GameInfo::instance().getOrganizerDirectory(), GameInfo::instance().getGameDirectory().append(L"\\data"));
-}
-
-void SettingsDialog::on_browseDownloadDirBtn_clicked()
-{
- QString temp = QFileDialog::getExistingDirectory(this, tr("Select download directory"), ui->downloadDirEdit->text());
- if (!temp.isEmpty()) {
- ui->downloadDirEdit->setText(temp);
- }
-}
-
-void SettingsDialog::on_browseModDirBtn_clicked()
-{
- QString temp = QFileDialog::getExistingDirectory(this, tr("Select mod directory"), ui->downloadDirEdit->text());
- if (!temp.isEmpty()) {
- ui->modDirEdit->setText(temp);
- }
-}
-
-void SettingsDialog::on_browseCacheDirBtn_clicked()
-{
- QString temp = QFileDialog::getExistingDirectory(this, tr("Select cache directory"), ui->cacheDirEdit->text());
- if (!temp.isEmpty()) {
- ui->cacheDirEdit->setText(temp);
- }
-}
-
-void SettingsDialog::on_resetDialogsButton_clicked()
-{
- if (QMessageBox::question(this, tr("Confirm?"),
- tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- emit resetDialogs();
- }
-}
-
-void SettingsDialog::storeSettings(QListWidgetItem *pluginItem)
-{
- if (pluginItem != NULL) {
- QMap<QString, QVariant> settings = pluginItem->data(Qt::UserRole + 1).toMap();
-
- for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) {
- const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i);
- settings[item->text(0)] = item->data(1, Qt::DisplayRole);
- }
-
- pluginItem->setData(Qt::UserRole + 1, settings);
- }
-}
-
-void SettingsDialog::on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
-{
- storeSettings(previous);
-
- ui->pluginSettingsList->clear();
- IPlugin *plugin = static_cast<IPlugin*>(current->data(Qt::UserRole).value<void*>());
- ui->authorLabel->setText(plugin->author());
- ui->versionLabel->setText(plugin->version().canonicalString());
- ui->descriptionLabel->setText(plugin->description());
-
- QMap<QString, QVariant> settings = current->data(Qt::UserRole + 1).toMap();
- ui->pluginSettingsList->setEnabled(settings.count() != 0);
- for (auto iter = settings.begin(); iter != settings.end(); ++iter) {
- QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key()));
- newItem->setData(1, Qt::DisplayRole, *iter);
- newItem->setData(1, Qt::EditRole, *iter);
- newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
- ui->pluginSettingsList->addTopLevelItem(newItem);
- }
-}
+#include "settingsdialog.h"
+#include "ui_settingsdialog.h"
+#include "categoriesdialog.h"
+#include "helper.h"
+#include <gameinfo.h>
+#include <QDirIterator>
+#include <QFileDialog>
+#include <QMessageBox>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+SettingsDialog::SettingsDialog(QWidget *parent)
+ : TutorableDialog("SettingsDialog", parent), ui(new Ui::SettingsDialog)
+{
+ ui->setupUi(this);
+}
+
+SettingsDialog::~SettingsDialog()
+{
+ delete ui;
+}
+
+void SettingsDialog::addPlugins(const std::vector<IPlugin*> &plugins)
+{
+ foreach (IPlugin *plugin, plugins) {
+ ui->pluginsList->addItem(plugin->name());
+ }
+}
+
+void SettingsDialog::accept()
+{
+ storeSettings(ui->pluginsList->currentItem());
+ TutorableDialog::accept();
+}
+
+
+void SettingsDialog::on_loginCheckBox_toggled(bool checked)
+{
+ QLineEdit *usernameEdit = findChild<QLineEdit*>("usernameEdit");
+ QLineEdit *passwordEdit = findChild<QLineEdit*>("passwordEdit");
+ if (checked) {
+ passwordEdit->setEnabled(true);
+ usernameEdit->setEnabled(true);
+ } else {
+ passwordEdit->setEnabled(false);
+ usernameEdit->setEnabled(false);
+ }
+}
+
+void SettingsDialog::on_categoriesBtn_clicked()
+{
+ CategoriesDialog dialog(this);
+ if (dialog.exec() == QDialog::Accepted) {
+ dialog.commitChanges();
+ }
+}
+
+void SettingsDialog::on_bsaDateBtn_clicked()
+{
+ Helper::backdateBSAs(GameInfo::instance().getOrganizerDirectory(), GameInfo::instance().getGameDirectory().append(L"\\data"));
+}
+
+void SettingsDialog::on_browseDownloadDirBtn_clicked()
+{
+ QString temp = QFileDialog::getExistingDirectory(this, tr("Select download directory"), ui->downloadDirEdit->text());
+ if (!temp.isEmpty()) {
+ ui->downloadDirEdit->setText(temp);
+ }
+}
+
+void SettingsDialog::on_browseModDirBtn_clicked()
+{
+ QString temp = QFileDialog::getExistingDirectory(this, tr("Select mod directory"), ui->downloadDirEdit->text());
+ if (!temp.isEmpty()) {
+ ui->modDirEdit->setText(temp);
+ }
+}
+
+void SettingsDialog::on_browseCacheDirBtn_clicked()
+{
+ QString temp = QFileDialog::getExistingDirectory(this, tr("Select cache directory"), ui->cacheDirEdit->text());
+ if (!temp.isEmpty()) {
+ ui->cacheDirEdit->setText(temp);
+ }
+}
+
+void SettingsDialog::on_resetDialogsButton_clicked()
+{
+ if (QMessageBox::question(this, tr("Confirm?"),
+ tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ emit resetDialogs();
+ }
+}
+
+void SettingsDialog::storeSettings(QListWidgetItem *pluginItem)
+{
+ if (pluginItem != NULL) {
+ QMap<QString, QVariant> settings = pluginItem->data(Qt::UserRole + 1).toMap();
+
+ for (int i = 0; i < ui->pluginSettingsList->topLevelItemCount(); ++i) {
+ const QTreeWidgetItem *item = ui->pluginSettingsList->topLevelItem(i);
+ settings[item->text(0)] = item->data(1, Qt::DisplayRole);
+ }
+
+ pluginItem->setData(Qt::UserRole + 1, settings);
+ }
+}
+
+void SettingsDialog::on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
+{
+ storeSettings(previous);
+
+ ui->pluginSettingsList->clear();
+ IPlugin *plugin = static_cast<IPlugin*>(current->data(Qt::UserRole).value<void*>());
+ ui->authorLabel->setText(plugin->author());
+ ui->versionLabel->setText(plugin->version().canonicalString());
+ ui->descriptionLabel->setText(plugin->description());
+
+ QMap<QString, QVariant> settings = current->data(Qt::UserRole + 1).toMap();
+ ui->pluginSettingsList->setEnabled(settings.count() != 0);
+ for (auto iter = settings.begin(); iter != settings.end(); ++iter) {
+ QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(iter.key()));
+ newItem->setData(1, Qt::DisplayRole, *iter);
+ newItem->setData(1, Qt::EditRole, *iter);
+ newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
+ ui->pluginSettingsList->addTopLevelItem(newItem);
+ }
+}
diff --git a/src/settingsdialog.h b/src/settingsdialog.h
index 5cb3470a..3bfc1706 100644
--- a/src/settingsdialog.h
+++ b/src/settingsdialog.h
@@ -17,64 +17,64 @@ 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 WORKAROUNDDIALOG_H
-#define WORKAROUNDDIALOG_H
-
-#include "tutorabledialog.h"
-#include <iplugin.h>
-#include <QDialog>
-#include <QListWidgetItem>
-
-namespace Ui {
- class SettingsDialog;
-}
-
-/**
- * dialog used to change settings for Mod Organizer. On top of the
- * settings managed by the "Settings" class, this offers a button to open the
- * CategoriesDialog
- **/
-class SettingsDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
- explicit SettingsDialog(QWidget *parent = 0);
- ~SettingsDialog();
-
- void addPlugins(const std::vector<IPlugin*> &plugins);
-
-public slots:
-
- virtual void accept();
-
-signals:
-
- void resetDialogs();
-
-private:
-
- void storeSettings(QListWidgetItem *pluginItem);
-
-private slots:
- void on_loginCheckBox_toggled(bool checked);
-
- void on_categoriesBtn_clicked();
-
- void on_bsaDateBtn_clicked();
-
- void on_browseDownloadDirBtn_clicked();
-
- void on_browseModDirBtn_clicked();
-
- void on_browseCacheDirBtn_clicked();
-
- void on_resetDialogsButton_clicked();
-
- void on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
-
-private:
- Ui::SettingsDialog *ui;
-};
-
-#endif // WORKAROUNDDIALOG_H
+#ifndef WORKAROUNDDIALOG_H
+#define WORKAROUNDDIALOG_H
+
+#include "tutorabledialog.h"
+#include <iplugin.h>
+#include <QDialog>
+#include <QListWidgetItem>
+
+namespace Ui {
+ class SettingsDialog;
+}
+
+/**
+ * dialog used to change settings for Mod Organizer. On top of the
+ * settings managed by the "Settings" class, this offers a button to open the
+ * CategoriesDialog
+ **/
+class SettingsDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsDialog(QWidget *parent = 0);
+ ~SettingsDialog();
+
+ void addPlugins(const std::vector<MOBase::IPlugin*> &plugins);
+
+public slots:
+
+ virtual void accept();
+
+signals:
+
+ void resetDialogs();
+
+private:
+
+ void storeSettings(QListWidgetItem *pluginItem);
+
+private slots:
+ void on_loginCheckBox_toggled(bool checked);
+
+ void on_categoriesBtn_clicked();
+
+ void on_bsaDateBtn_clicked();
+
+ void on_browseDownloadDirBtn_clicked();
+
+ void on_browseModDirBtn_clicked();
+
+ void on_browseCacheDirBtn_clicked();
+
+ void on_resetDialogsButton_clicked();
+
+ void on_pluginsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
+
+private:
+ Ui::SettingsDialog *ui;
+};
+
+#endif // WORKAROUNDDIALOG_H
diff --git a/src/shared/appconfig.cpp b/src/shared/appconfig.cpp
index 0d933d0a..3f96bff1 100644
--- a/src/shared/appconfig.cpp
+++ b/src/shared/appconfig.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "appconfig.h"
namespace AppConfig {
@@ -24,7 +24,10 @@ namespace AppConfig {
#define PARWSTRING wstring
#define APPPARAM(partype, parid, value) partype parid ## () { return value; }
#include "appconfig.inc"
+
+namespace MOShared {
#undef PARWSTRING
#undef APPPARAM
}
+} // namespace MOShared
diff --git a/src/shared/appconfig.h b/src/shared/appconfig.h
index b10ecc3c..54544255 100644
--- a/src/shared/appconfig.h
+++ b/src/shared/appconfig.h
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef APPCONFIG_H
#define APPCONFIG_H
@@ -28,9 +28,13 @@ namespace AppConfig {
#define PARWSTRING wstring
#define APPPARAM(partype, parid, value) partype parid ## ();
#include "appconfig.inc"
+
+namespace MOShared {
#undef PARWSTRING
#undef APPPARAM
}
+} // namespace MOShared
+
#endif // APPCONFIG_H
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index aa3bce5f..eaba91a6 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -1,799 +1,802 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "directoryentry.h"
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-#include <sstream>
-#include <algorithm>
-#include <bsatk.h>
-#include "error_report.h"
-#include "util.h"
-#include "windows_error.h"
-#include <boost/bind.hpp>
-
-
-
-class OriginConnection {
-
-public:
-
- typedef int Index;
- static const int INVALID_INDEX = INT_MIN;
-
-public:
-
- OriginConnection()
- : m_NextID(0)
- {}
-
- FilesOrigin& createOrigin(const std::wstring &originName, const std::wstring &directory, int priority,
- boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection) {
- int newID = createID();
- m_Origins[newID] = FilesOrigin(newID, originName, directory, priority, fileRegister, originConnection);
- m_OriginsNameMap[originName] = newID;
- m_OriginsPriorityMap[priority] = newID;
- return m_Origins[newID];
- }
-
- bool exists(const std::wstring &name) {
- std::map<std::wstring, int>::iterator iter = m_OriginsNameMap.find(name);
- return iter != m_OriginsNameMap.end();
- }
-
- FilesOrigin &getByID(Index ID) {
- return m_Origins[ID];
- }
-
- FilesOrigin &getByName(const std::wstring &name) {
- std::map<std::wstring, int>::iterator iter = m_OriginsNameMap.find(name);
- if (iter != m_OriginsNameMap.end()) {
- return m_Origins[iter->second];
- } else {
- std::ostringstream stream;
- stream << "invalid origin name: " << ToString(name, false);
- throw std::runtime_error(stream.str());
- }
- }
-
- void changePriorityLookup(int oldPriority, int newPriority)
- {
- auto iter = m_OriginsPriorityMap.find(oldPriority);
- if (iter != m_OriginsPriorityMap.end()) {
- Index idx = iter->second;
- m_OriginsPriorityMap.erase(iter);
- m_OriginsPriorityMap[newPriority] = idx;
- }
- }
-
- void changeNameLookup(const std::wstring &oldName, const std::wstring &newName)
- {
- auto iter = m_OriginsNameMap.find(oldName);
- if (iter != m_OriginsNameMap.end()) {
- Index idx = iter->second;
- m_OriginsNameMap.erase(iter);
- m_OriginsNameMap[newName] = idx;
- } else {
- log("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str());
- }
- }
-
-private:
-
- Index createID() {
- return m_NextID++;
- }
-
-private:
-
- Index m_NextID;
-
- std::map<Index, FilesOrigin> m_Origins;
- std::map<std::wstring, Index> m_OriginsNameMap;
- std::map<int, Index> m_OriginsPriorityMap;
-
-};
-
-
-//
-// FilesOrigin
-//
-
-
-void FilesOrigin::enable(bool enabled)
-{
- if (!enabled) {
- std::set<FileEntry::Index> copy = m_Files;
- for (auto iter = copy.begin(); iter != copy.end(); ++iter) {
- m_FileRegister->removeOrigin(*iter, m_ID);
- }
- m_Files.clear();
- }
- m_Disabled = !enabled;
-}
-
-
-void FilesOrigin::removeFile(FileEntry::Index index)
-{
- auto iter = m_Files.find(index);
- if (iter != m_Files.end()) {
- m_Files.erase(iter);
- }
-}
-
-
-
-std::wstring tail(const std::wstring &source, const size_t count)
-{
- if (count >= source.length()) {
- return source;
- }
-
- return source.substr(source.length() - count);
-}
-
-
-void FilesOrigin::setPriority(int priority)
-{
- m_OriginConnection->changePriorityLookup(m_Priority, priority);
-
- m_Priority = priority;
-}
-
-
-void FilesOrigin::setName(const std::wstring &name)
-{
- m_OriginConnection->changeNameLookup(m_Name, name);
- // change path too
- if (tail(m_Path, m_Name.length()) == m_Name) {
- m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name);
- }
- m_Name = name;
-}
-
-std::vector<FileEntry*> FilesOrigin::getFiles() const
-{
- std::vector<FileEntry*> result;
-
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- result.push_back(m_FileRegister->getFile(*iter));
- }
-
- return result;
-}
-
-
-
-//
-// FileEntry
-//
-
-void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive)
-{
- if (m_Origin == -1) {
- m_Origin = origin;
- m_FileTime = fileTime;
- m_Archive = archive;
-// } else if (FilesOrigin::getByID(origin).getPriority() > FilesOrigin::getByID(m_Origin).getPriority()) {
- } else if (m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority()) {
- if (std::find(m_Alternatives.begin(), m_Alternatives.end(), m_Origin) == m_Alternatives.end()) {
- m_Alternatives.push_back(m_Origin);
- }
- m_Origin = origin;
- m_FileTime = fileTime;
- m_Archive = archive;
- } else {
- bool found = false;
- if (m_Origin == origin) {
- // already an origin
- return;
- }
- for (std::vector<int>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
- if (*iter == origin) {
- // already an origin
- return;
- }
- if (m_Parent->getOriginByID(*iter).getPriority() < m_Parent->getOriginByID(origin).getPriority()) {
- m_Alternatives.insert(iter, origin);
- found = true;
- break;
- }
- }
- if (!found) {
- m_Alternatives.push_back(origin);
- }
- }
-}
-
-bool FileEntry::removeOrigin(int origin)
-{
- if (m_Origin == origin) {
- if (!m_Alternatives.empty()) {
- // find alternative with the highest priority
- std::vector<int>::iterator currentIter = m_Alternatives.begin();
- for (std::vector<int>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
- if ((m_Parent->getOriginByID(*iter).getPriority() > m_Parent->getOriginByID(*currentIter).getPriority()) &&
- (*iter != origin)) {
- currentIter = iter;
- }
- }
- int currentID = *currentIter;
- m_Alternatives.erase(currentIter);
-
- m_Origin = currentID;
-
- // now we need to update the file time...
- std::wstring filePath = getFullPath();
- HANDLE file = ::CreateFile(filePath.c_str(), GENERIC_READ | GENERIC_WRITE,
- 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (!::GetFileTime(file, NULL, NULL, &m_FileTime)) {
- // maybe this file is in a bsa, but there is no easy way to find out which. User should refresh
- // the view to find out
- m_Archive = L"bsa?";
- } else {
- m_Archive = L"";
- }
-
- ::CloseHandle(file);
-
- } else {
- m_Origin = -1;
- return true;
- }
- } else {
- std::vector<int>::iterator newEnd = std::remove(m_Alternatives.begin(), m_Alternatives.end(), origin);
- m_Alternatives.erase(newEnd, m_Alternatives.end());
- }
- return false;
-}
-
-
-// sorted by priority descending
-static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS)
-{
- return entry->getOriginByID(LHS).getPriority() < entry->getOriginByID(RHS).getPriority();
-}
-
-
-FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent)
- : m_Index(index), m_Name(name), m_Parent(parent), m_Origin(-1), m_Archive(L"")
-{
-}
-
-
-void FileEntry::sortOrigins()
-{
- m_Alternatives.push_back(m_Origin);
- std::sort(m_Alternatives.begin(), m_Alternatives.end(), boost::bind(ByOriginPriority, m_Parent, _1, _2));
- m_Origin = m_Alternatives[m_Alternatives.size() - 1];
- m_Alternatives.pop_back();
-}
-
-
-bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const
-{
- if (parent == NULL) {
- return false;
- } else {
- // don't append the topmost parent because it is the virtual data-root
- if (recurseParents(path, parent->getParent())) {
- path.append(L"\\").append(parent->getName());
- }
- return true;
- }
-}
-
-std::wstring FileEntry::getFullPath() const
-{
- std::wstring result;
- bool ignore = false;
- result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); //base directory for origin
- recurseParents(result, m_Parent); // all intermediate directories
- result.append(L"\\").append(m_Name); // the actual filename
- return result;
-}
-
-std::wstring FileEntry::getRelativePath() const
-{
- std::wstring result;
- recurseParents(result, m_Parent); // all intermediate directories
- result.append(L"\\").append(m_Name); // the actual filename
- return result;
-}
-
-
-
-
-
-//
-// DirectoryEntry
-//
-DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID)
- : m_OriginConnection(new OriginConnection),
- m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID)
-{
- m_FileRegister.reset(new FileRegister(m_OriginConnection));
-}
-
-DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID,
- boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection)
- : m_FileRegister(fileRegister), m_OriginConnection(originConnection),
- m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID)
-{}
-
-
-DirectoryEntry::~DirectoryEntry()
-{
- clear();
-}
-
-
-const std::wstring &DirectoryEntry::getName() const
-{
- return m_Name;
-}
-
-
-void DirectoryEntry::clear()
-{
- m_Files.clear();
- for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- delete *iter;
- }
- m_SubDirectories.clear();
-}
-
-
-FilesOrigin &DirectoryEntry::createOrigin(const std::wstring &originName, const std::wstring &directory, int priority)
-{
- if (m_OriginConnection->exists(originName)) {
- FilesOrigin &origin = m_OriginConnection->getByName(originName);
- origin.enable(true);
- return origin;
- } else {
- return m_OriginConnection->createOrigin(originName, directory, priority, m_FileRegister, m_OriginConnection);
- }
-}
-
-
-void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority)
-{
- FilesOrigin &origin = createOrigin(originName, directory, priority);
- wchar_t *buffer = new wchar_t[MAXPATH_UNICODE + 1];
- memset(buffer, L'\0', MAXPATH_UNICODE + 1);
- try {
- int offset = _snwprintf(buffer, MAXPATH_UNICODE, L"%ls", directory.c_str());
- addFiles(origin, buffer, offset);
- } catch (...) {
- delete [] buffer;
- buffer = NULL;
- }
- delete [] buffer;
- m_Populated = true;
-}
-
-
-void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority)
-{
- FilesOrigin &origin = createOrigin(originName, directory, priority);
-
- WIN32_FILE_ATTRIBUTE_DATA fileData;
- if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) {
- throw windows_error("failed to determine file time");
- }
-
- BSA::Archive archive;
- BSA::EErrorCode res = archive.read(ToString(fileName, false).c_str());
- if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
- std::ostringstream stream;
- stream << "invalid bsa file: " << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError();
- throw std::runtime_error(stream.str());
- }
- size_t namePos = fileName.find_last_of(L"\\/");
- if (namePos == std::wstring::npos) {
- namePos = 0;
- } else {
- ++namePos;
- }
-
- addFiles(origin, archive.getRoot(), fileData.ftLastWriteTime, fileName.substr(namePos));
- m_Populated = true;
-}
-
-
-void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
-{
- WIN32_FIND_DATAW findData;
-
- _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, L"\\*");
- HANDLE searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
- if (searchHandle != INVALID_HANDLE_VALUE) {
- BOOL result = true;
- while (result) {
- if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- if ((wcscmp(findData.cFileName, L".") != 0) &&
- (wcscmp(findData.cFileName, L"..") != 0)) {
- int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName);
- // recurse into subdirectories
- getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset);
- }
- } else {
- insert(findData.cFileName, origin, findData.ftLastWriteTime, L"");
- }
- result = ::FindNextFileW(searchHandle, &findData);
- }
- }
- ::FindClose(searchHandle);
-}
-
-
-void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName)
-{
- // add files
- for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) {
- BSA::File::Ptr file = archiveFolder->getFile(fileIdx);
- insert(ToWString(file->getName(), true), origin, fileTime, archiveName);
- }
-
- // recurse into subdirectories
- for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) {
- BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx);
- DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID());
-
- folderEntry->addFiles(origin, folder, fileTime, archiveName);
- }
-}
-
-
-void DirectoryEntry::removeFile(const std::wstring &filePath, int *origin)
-{
- size_t pos = filePath.find_first_of(L"\\/");
- if (pos == std::string::npos) {
- this->remove(filePath, origin);
- } else {
- std::wstring dirName = filePath.substr(0, pos);
- std::wstring rest = filePath.substr(pos + 1);
- DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
- if (entry != NULL) {
- entry->removeFile(rest, origin);
- }
- }
-}
-
-void DirectoryEntry::removeDirRecursive()
-{
- while (!m_Files.empty()) {
- m_FileRegister->removeFile(m_Files.begin()->second);
- }
-
- for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- (*iter)->removeDirRecursive();
- }
- m_SubDirectories.clear();
-}
-
-void DirectoryEntry::removeDir(const std::wstring &path)
-{
- size_t pos = path.find_first_of(L"\\/");
- if (pos == std::string::npos) {
- for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- if (_wcsicmp((*iter)->getName().c_str(), path.c_str()) == 0) {
- (*iter)->removeDirRecursive();
- m_SubDirectories.erase(iter);
- break;
- }
- }
- } else {
- std::wstring dirName = path.substr(0, pos);
- std::wstring rest = path.substr(pos + 1);
- DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
- if (entry != NULL) {
- entry->removeDir(rest);
- }
- }
-}
-
-
-void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime)
-{
- size_t pos = filePath.find_first_of(L"\\/");
- if (pos == std::string::npos) {
- this->insert(filePath, origin, fileTime, L"");
- } else {
- std::wstring dirName = filePath.substr(0, pos);
- std::wstring rest = filePath.substr(pos + 1);
- getSubDirectoryRecursive(dirName, true, origin.getID())->insertFile(rest, origin, fileTime);
- }
-}
-
-
-void DirectoryEntry::removeFile(FileEntry::Index index)
-{
- if (m_Files.size() != 0) {
- auto iter = std::find_if(m_Files.begin(), m_Files.end(),
- [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
- return iter.second == index; } );
- if (iter != m_Files.end()) {
- m_Files.erase(iter);
- } else {
- log("file \"%ls\" not in directory \"%ls\"",
- m_FileRegister->getFile(index)->getName().c_str(),
- this->getName().c_str());
- }
- } else {
- log("file \"%ls\" not in directory \"%ls\", directory empty",
- m_FileRegister->getFile(index)->getName().c_str(),
- this->getName().c_str());
- }
-}
-
-
-int DirectoryEntry::anyOrigin() const
-{
- bool ignore;
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- FileEntry *entry = m_FileRegister->getFile(iter->second);
- if (!entry->isFromArchive()) {
- return entry->getOrigin(ignore);
- }
- }
-
- // if we got here, no file directly within this directory is a valid indicator for a mod, thus
- // we continue looking in subdirectories
- for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- int res = (*iter)->anyOrigin();
- if (res != -1){
- return res;
- }
- }
- return m_Origin;
-}
-
-
-bool DirectoryEntry::originExists(const std::wstring &name) const
-{
- return m_OriginConnection->exists(name);
-}
-
-
-FilesOrigin &DirectoryEntry::getOriginByID(int ID) const
-{
- return m_OriginConnection->getByID(ID);
-}
-
-
-FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const
-{
- return m_OriginConnection->getByName(name);
-}
-
-
-int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive)
-{
- const DirectoryEntry *directory = NULL;
- const FileEntry *file = searchFile(path, &directory);
- if (file != NULL) {
- return file->getOrigin(archive);
- } else {
- if (directory != NULL) {
- return directory->anyOrigin();
- } else {
- return -1;
- }
- }
-}
-
-std::vector<FileEntry*> DirectoryEntry::getFiles() const
-{
- std::vector<FileEntry*> result;
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- result.push_back(m_FileRegister->getFile(iter->second));
- }
- return result;
-}
-
-
-const FileEntry *DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const
-{
- if (directory != NULL) {
- *directory = NULL;
- }
-
- if ((path.length() == 0) ||
- (path == L"*")) {
- // no file name -> the path ended on a (back-)slash
- *directory = this;
-
- return NULL;
- }
-
- size_t len = path.find_first_of(L"\\/");
-
- if (len == std::string::npos) {
- // no more path components
- auto iter = m_Files.find(path);
- if (iter != m_Files.end()) {
- return m_FileRegister->getFile(iter->second);
- } else if (directory != NULL) {
- DirectoryEntry *temp = findSubDirectory(path);
- if (temp != NULL) {
- *directory = temp;
- }
- }
- } else {
- // file is in in a subdirectory, recurse into the matching subdirectory
- std::wstring pathComponent = path.substr(0, len);
- DirectoryEntry *temp = findSubDirectory(pathComponent);
- if (temp != NULL) {
- return temp->searchFile(path.substr(len + 1), directory);
- }
- }
- return NULL;
-}
-
-/*
-void DirectoryEntry::sortOrigins()
-{
- for (std::set<FileEntry>::iterator iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- const_cast<FileEntry&>(*iter).sortOrigins();
- }
- for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- (*iter)->sortOrigins();
- }
-}
-*/
-
-DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const
-{
- for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- if (_wcsicmp((*iter)->getName().c_str(), name.c_str()) == 0) {
- return *iter;
- }
- }
- return NULL;
-}
-
-
-
-const FileEntry *DirectoryEntry::findFile(const std::wstring &name)
-{
- auto iter = m_Files.find(name);
- if (iter != m_Files.end()) {
- return m_FileRegister->getFile(iter->second);
- } else {
- return NULL;
- }
-}
-
-DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID)
-{
- for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- if (_wcsicmp((*iter)->getName().c_str(), name.c_str()) == 0) {
- return *iter;
- }
- }
- if (create) {
- std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.insert(m_SubDirectories.end(),
- new DirectoryEntry(name, this, originID, m_FileRegister, m_OriginConnection));
- return *iter;
- } else {
- return NULL;
- }
-}
-
-
-DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &path, bool create, int originID)
-{
- if (path.length() == 0) {
- // path ended with a backslash?
- return this;
- }
-
- size_t pos = path.find_first_of(L"\\/");
- if (pos == std::wstring::npos) {
- return getSubDirectory(path, create);
- } else {
- DirectoryEntry *nextChild = getSubDirectory(path.substr(0, pos), create, originID);
- if (nextChild == NULL) {
- return NULL;
- } else {
- return nextChild->getSubDirectoryRecursive(path.substr(pos + 1), create, originID);
- }
- }
-}
-
-
-
-FileRegister::FileRegister(boost::shared_ptr<OriginConnection> originConnection)
- : m_OriginConnection(originConnection)
-{
-}
-
-FileEntry::Index FileRegister::generateIndex()
-{
- static FileEntry::Index sIndex = 0;
- return sIndex++;
-}
-
-bool FileRegister::indexValid(FileEntry::Index index) const
-{
- return m_Files.find(index) != m_Files.end();
-}
-
-FileEntry &FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent)
-{
- FileEntry::Index index = generateIndex();
- m_Files[index] = FileEntry(index, name, parent);
- return m_Files[index];
-}
-
-
-FileEntry *FileRegister::getFile(FileEntry::Index index)
-{
- auto iter = m_Files.find(index);
- if (iter != m_Files.end()) {
- return &iter->second;
- }
- return NULL;
-}
-
-
-void FileRegister::unregisterFile(FileEntry &file)
-{
- bool ignore;
- // unregister from origin
- int originID = file.getOrigin(ignore);
- m_OriginConnection->getByID(originID).removeFile(file.getIndex());
- const std::vector<int> &alternatives = file.getAlternatives();
- for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
- m_OriginConnection->getByID(*iter).removeFile(file.getIndex());
- }
-
- // unregister from directory
- if (file.getParent() != NULL) {
- file.getParent()->removeFile(file.getIndex());
- }
-}
-
-
-void FileRegister::removeFile(FileEntry::Index index)
-{
- auto iter = m_Files.find(index);
- if (iter != m_Files.end()) {
- unregisterFile(iter->second);
- m_Files.erase(index);
- }
-}
-
-void FileRegister::removeOrigin(FileEntry::Index index, int originID)
-{
- auto iter = m_Files.find(index);
- if (iter != m_Files.end()) {
- if (iter->second.removeOrigin(originID)) {
- unregisterFile(iter->second);
- }
- }
-}
-
-void FileRegister::sortOrigins()
-{
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- iter->second.sortOrigins();
- }
-}
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "directoryentry.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <sstream>
+#include <algorithm>
+#include <bsatk.h>
+#include "error_report.h"
+#include "util.h"
+#include "windows_error.h"
+#include <boost/bind.hpp>
+
+namespace MOShared {
+
+
+
+class OriginConnection {
+
+public:
+
+ typedef int Index;
+ static const int INVALID_INDEX = INT_MIN;
+
+public:
+
+ OriginConnection()
+ : m_NextID(0)
+ {}
+
+ FilesOrigin& createOrigin(const std::wstring &originName, const std::wstring &directory, int priority,
+ boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection) {
+ int newID = createID();
+ m_Origins[newID] = FilesOrigin(newID, originName, directory, priority, fileRegister, originConnection);
+ m_OriginsNameMap[originName] = newID;
+ m_OriginsPriorityMap[priority] = newID;
+ return m_Origins[newID];
+ }
+
+ bool exists(const std::wstring &name) {
+ std::map<std::wstring, int>::iterator iter = m_OriginsNameMap.find(name);
+ return iter != m_OriginsNameMap.end();
+ }
+
+ FilesOrigin &getByID(Index ID) {
+ return m_Origins[ID];
+ }
+
+ FilesOrigin &getByName(const std::wstring &name) {
+ std::map<std::wstring, int>::iterator iter = m_OriginsNameMap.find(name);
+ if (iter != m_OriginsNameMap.end()) {
+ return m_Origins[iter->second];
+ } else {
+ std::ostringstream stream;
+ stream << "invalid origin name: " << ToString(name, false);
+ throw std::runtime_error(stream.str());
+ }
+ }
+
+ void changePriorityLookup(int oldPriority, int newPriority)
+ {
+ auto iter = m_OriginsPriorityMap.find(oldPriority);
+ if (iter != m_OriginsPriorityMap.end()) {
+ Index idx = iter->second;
+ m_OriginsPriorityMap.erase(iter);
+ m_OriginsPriorityMap[newPriority] = idx;
+ }
+ }
+
+ void changeNameLookup(const std::wstring &oldName, const std::wstring &newName)
+ {
+ auto iter = m_OriginsNameMap.find(oldName);
+ if (iter != m_OriginsNameMap.end()) {
+ Index idx = iter->second;
+ m_OriginsNameMap.erase(iter);
+ m_OriginsNameMap[newName] = idx;
+ } else {
+ log("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str());
+ }
+ }
+
+private:
+
+ Index createID() {
+ return m_NextID++;
+ }
+
+private:
+
+ Index m_NextID;
+
+ std::map<Index, FilesOrigin> m_Origins;
+ std::map<std::wstring, Index> m_OriginsNameMap;
+ std::map<int, Index> m_OriginsPriorityMap;
+
+};
+
+
+//
+// FilesOrigin
+//
+
+
+void FilesOrigin::enable(bool enabled)
+{
+ if (!enabled) {
+ std::set<FileEntry::Index> copy = m_Files;
+ for (auto iter = copy.begin(); iter != copy.end(); ++iter) {
+ m_FileRegister->removeOrigin(*iter, m_ID);
+ }
+ m_Files.clear();
+ }
+ m_Disabled = !enabled;
+}
+
+
+void FilesOrigin::removeFile(FileEntry::Index index)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ m_Files.erase(iter);
+ }
+}
+
+
+
+std::wstring tail(const std::wstring &source, const size_t count)
+{
+ if (count >= source.length()) {
+ return source;
+ }
+
+ return source.substr(source.length() - count);
+}
+
+
+void FilesOrigin::setPriority(int priority)
+{
+ m_OriginConnection->changePriorityLookup(m_Priority, priority);
+
+ m_Priority = priority;
+}
+
+
+void FilesOrigin::setName(const std::wstring &name)
+{
+ m_OriginConnection->changeNameLookup(m_Name, name);
+ // change path too
+ if (tail(m_Path, m_Name.length()) == m_Name) {
+ m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name);
+ }
+ m_Name = name;
+}
+
+std::vector<FileEntry*> FilesOrigin::getFiles() const
+{
+ std::vector<FileEntry*> result;
+
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ result.push_back(m_FileRegister->getFile(*iter));
+ }
+
+ return result;
+}
+
+
+
+//
+// FileEntry
+//
+
+void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive)
+{
+ if (m_Origin == -1) {
+ m_Origin = origin;
+ m_FileTime = fileTime;
+ m_Archive = archive;
+// } else if (FilesOrigin::getByID(origin).getPriority() > FilesOrigin::getByID(m_Origin).getPriority()) {
+ } else if (m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority()) {
+ if (std::find(m_Alternatives.begin(), m_Alternatives.end(), m_Origin) == m_Alternatives.end()) {
+ m_Alternatives.push_back(m_Origin);
+ }
+ m_Origin = origin;
+ m_FileTime = fileTime;
+ m_Archive = archive;
+ } else {
+ bool found = false;
+ if (m_Origin == origin) {
+ // already an origin
+ return;
+ }
+ for (std::vector<int>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
+ if (*iter == origin) {
+ // already an origin
+ return;
+ }
+ if (m_Parent->getOriginByID(*iter).getPriority() < m_Parent->getOriginByID(origin).getPriority()) {
+ m_Alternatives.insert(iter, origin);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ m_Alternatives.push_back(origin);
+ }
+ }
+}
+
+bool FileEntry::removeOrigin(int origin)
+{
+ if (m_Origin == origin) {
+ if (!m_Alternatives.empty()) {
+ // find alternative with the highest priority
+ std::vector<int>::iterator currentIter = m_Alternatives.begin();
+ for (std::vector<int>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
+ if ((m_Parent->getOriginByID(*iter).getPriority() > m_Parent->getOriginByID(*currentIter).getPriority()) &&
+ (*iter != origin)) {
+ currentIter = iter;
+ }
+ }
+ int currentID = *currentIter;
+ m_Alternatives.erase(currentIter);
+
+ m_Origin = currentID;
+
+ // now we need to update the file time...
+ std::wstring filePath = getFullPath();
+ HANDLE file = ::CreateFile(filePath.c_str(), GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (!::GetFileTime(file, NULL, NULL, &m_FileTime)) {
+ // maybe this file is in a bsa, but there is no easy way to find out which. User should refresh
+ // the view to find out
+ m_Archive = L"bsa?";
+ } else {
+ m_Archive = L"";
+ }
+
+ ::CloseHandle(file);
+
+ } else {
+ m_Origin = -1;
+ return true;
+ }
+ } else {
+ std::vector<int>::iterator newEnd = std::remove(m_Alternatives.begin(), m_Alternatives.end(), origin);
+ m_Alternatives.erase(newEnd, m_Alternatives.end());
+ }
+ return false;
+}
+
+
+// sorted by priority descending
+static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS)
+{
+ return entry->getOriginByID(LHS).getPriority() < entry->getOriginByID(RHS).getPriority();
+}
+
+
+FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent)
+ : m_Index(index), m_Name(name), m_Parent(parent), m_Origin(-1), m_Archive(L"")
+{
+}
+
+
+void FileEntry::sortOrigins()
+{
+ m_Alternatives.push_back(m_Origin);
+ std::sort(m_Alternatives.begin(), m_Alternatives.end(), boost::bind(ByOriginPriority, m_Parent, _1, _2));
+ m_Origin = m_Alternatives[m_Alternatives.size() - 1];
+ m_Alternatives.pop_back();
+}
+
+
+bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const
+{
+ if (parent == NULL) {
+ return false;
+ } else {
+ // don't append the topmost parent because it is the virtual data-root
+ if (recurseParents(path, parent->getParent())) {
+ path.append(L"\\").append(parent->getName());
+ }
+ return true;
+ }
+}
+
+std::wstring FileEntry::getFullPath() const
+{
+ std::wstring result;
+ bool ignore = false;
+ result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); //base directory for origin
+ recurseParents(result, m_Parent); // all intermediate directories
+ result.append(L"\\").append(m_Name); // the actual filename
+ return result;
+}
+
+std::wstring FileEntry::getRelativePath() const
+{
+ std::wstring result;
+ recurseParents(result, m_Parent); // all intermediate directories
+ result.append(L"\\").append(m_Name); // the actual filename
+ return result;
+}
+
+
+
+
+
+//
+// DirectoryEntry
+//
+DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID)
+ : m_OriginConnection(new OriginConnection),
+ m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID)
+{
+ m_FileRegister.reset(new FileRegister(m_OriginConnection));
+}
+
+DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID,
+ boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection)
+ : m_FileRegister(fileRegister), m_OriginConnection(originConnection),
+ m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID)
+{}
+
+
+DirectoryEntry::~DirectoryEntry()
+{
+ clear();
+}
+
+
+const std::wstring &DirectoryEntry::getName() const
+{
+ return m_Name;
+}
+
+
+void DirectoryEntry::clear()
+{
+ m_Files.clear();
+ for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ delete *iter;
+ }
+ m_SubDirectories.clear();
+}
+
+
+FilesOrigin &DirectoryEntry::createOrigin(const std::wstring &originName, const std::wstring &directory, int priority)
+{
+ if (m_OriginConnection->exists(originName)) {
+ FilesOrigin &origin = m_OriginConnection->getByName(originName);
+ origin.enable(true);
+ return origin;
+ } else {
+ return m_OriginConnection->createOrigin(originName, directory, priority, m_FileRegister, m_OriginConnection);
+ }
+}
+
+
+void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority)
+{
+ FilesOrigin &origin = createOrigin(originName, directory, priority);
+ wchar_t *buffer = new wchar_t[MAXPATH_UNICODE + 1];
+ memset(buffer, L'\0', MAXPATH_UNICODE + 1);
+ try {
+ int offset = _snwprintf(buffer, MAXPATH_UNICODE, L"%ls", directory.c_str());
+ addFiles(origin, buffer, offset);
+ } catch (...) {
+ delete [] buffer;
+ buffer = NULL;
+ }
+ delete [] buffer;
+ m_Populated = true;
+}
+
+
+void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority)
+{
+ FilesOrigin &origin = createOrigin(originName, directory, priority);
+
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+ if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) {
+ throw windows_error("failed to determine file time");
+ }
+
+ BSA::Archive archive;
+ BSA::EErrorCode res = archive.read(ToString(fileName, false).c_str());
+ if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
+ std::ostringstream stream;
+ stream << "invalid bsa file: " << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError();
+ throw std::runtime_error(stream.str());
+ }
+ size_t namePos = fileName.find_last_of(L"\\/");
+ if (namePos == std::wstring::npos) {
+ namePos = 0;
+ } else {
+ ++namePos;
+ }
+
+ addFiles(origin, archive.getRoot(), fileData.ftLastWriteTime, fileName.substr(namePos));
+ m_Populated = true;
+}
+
+
+void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
+{
+ WIN32_FIND_DATAW findData;
+
+ _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, L"\\*");
+ HANDLE searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
+ if (searchHandle != INVALID_HANDLE_VALUE) {
+ BOOL result = true;
+ while (result) {
+ if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if ((wcscmp(findData.cFileName, L".") != 0) &&
+ (wcscmp(findData.cFileName, L"..") != 0)) {
+ int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName);
+ // recurse into subdirectories
+ getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset);
+ }
+ } else {
+ insert(findData.cFileName, origin, findData.ftLastWriteTime, L"");
+ }
+ result = ::FindNextFileW(searchHandle, &findData);
+ }
+ }
+ ::FindClose(searchHandle);
+}
+
+
+void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName)
+{
+ // add files
+ for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) {
+ BSA::File::Ptr file = archiveFolder->getFile(fileIdx);
+ insert(ToWString(file->getName(), true), origin, fileTime, archiveName);
+ }
+
+ // recurse into subdirectories
+ for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) {
+ BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx);
+ DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID());
+
+ folderEntry->addFiles(origin, folder, fileTime, archiveName);
+ }
+}
+
+
+void DirectoryEntry::removeFile(const std::wstring &filePath, int *origin)
+{
+ size_t pos = filePath.find_first_of(L"\\/");
+ if (pos == std::string::npos) {
+ this->remove(filePath, origin);
+ } else {
+ std::wstring dirName = filePath.substr(0, pos);
+ std::wstring rest = filePath.substr(pos + 1);
+ DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
+ if (entry != NULL) {
+ entry->removeFile(rest, origin);
+ }
+ }
+}
+
+void DirectoryEntry::removeDirRecursive()
+{
+ while (!m_Files.empty()) {
+ m_FileRegister->removeFile(m_Files.begin()->second);
+ }
+
+ for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ (*iter)->removeDirRecursive();
+ }
+ m_SubDirectories.clear();
+}
+
+void DirectoryEntry::removeDir(const std::wstring &path)
+{
+ size_t pos = path.find_first_of(L"\\/");
+ if (pos == std::string::npos) {
+ for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ if (_wcsicmp((*iter)->getName().c_str(), path.c_str()) == 0) {
+ (*iter)->removeDirRecursive();
+ m_SubDirectories.erase(iter);
+ break;
+ }
+ }
+ } else {
+ std::wstring dirName = path.substr(0, pos);
+ std::wstring rest = path.substr(pos + 1);
+ DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
+ if (entry != NULL) {
+ entry->removeDir(rest);
+ }
+ }
+}
+
+
+void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime)
+{
+ size_t pos = filePath.find_first_of(L"\\/");
+ if (pos == std::string::npos) {
+ this->insert(filePath, origin, fileTime, L"");
+ } else {
+ std::wstring dirName = filePath.substr(0, pos);
+ std::wstring rest = filePath.substr(pos + 1);
+ getSubDirectoryRecursive(dirName, true, origin.getID())->insertFile(rest, origin, fileTime);
+ }
+}
+
+
+void DirectoryEntry::removeFile(FileEntry::Index index)
+{
+ if (m_Files.size() != 0) {
+ auto iter = std::find_if(m_Files.begin(), m_Files.end(),
+ [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
+ return iter.second == index; } );
+ if (iter != m_Files.end()) {
+ m_Files.erase(iter);
+ } else {
+ log("file \"%ls\" not in directory \"%ls\"",
+ m_FileRegister->getFile(index)->getName().c_str(),
+ this->getName().c_str());
+ }
+ } else {
+ log("file \"%ls\" not in directory \"%ls\", directory empty",
+ m_FileRegister->getFile(index)->getName().c_str(),
+ this->getName().c_str());
+ }
+}
+
+
+int DirectoryEntry::anyOrigin() const
+{
+ bool ignore;
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ FileEntry *entry = m_FileRegister->getFile(iter->second);
+ if (!entry->isFromArchive()) {
+ return entry->getOrigin(ignore);
+ }
+ }
+
+ // if we got here, no file directly within this directory is a valid indicator for a mod, thus
+ // we continue looking in subdirectories
+ for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ int res = (*iter)->anyOrigin();
+ if (res != -1){
+ return res;
+ }
+ }
+ return m_Origin;
+}
+
+
+bool DirectoryEntry::originExists(const std::wstring &name) const
+{
+ return m_OriginConnection->exists(name);
+}
+
+
+FilesOrigin &DirectoryEntry::getOriginByID(int ID) const
+{
+ return m_OriginConnection->getByID(ID);
+}
+
+
+FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const
+{
+ return m_OriginConnection->getByName(name);
+}
+
+
+int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive)
+{
+ const DirectoryEntry *directory = NULL;
+ const FileEntry *file = searchFile(path, &directory);
+ if (file != NULL) {
+ return file->getOrigin(archive);
+ } else {
+ if (directory != NULL) {
+ return directory->anyOrigin();
+ } else {
+ return -1;
+ }
+ }
+}
+
+std::vector<FileEntry*> DirectoryEntry::getFiles() const
+{
+ std::vector<FileEntry*> result;
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ result.push_back(m_FileRegister->getFile(iter->second));
+ }
+ return result;
+}
+
+
+const FileEntry *DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const
+{
+ if (directory != NULL) {
+ *directory = NULL;
+ }
+
+ if ((path.length() == 0) ||
+ (path == L"*")) {
+ // no file name -> the path ended on a (back-)slash
+ *directory = this;
+
+ return NULL;
+ }
+
+ size_t len = path.find_first_of(L"\\/");
+
+ if (len == std::string::npos) {
+ // no more path components
+ auto iter = m_Files.find(path);
+ if (iter != m_Files.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else if (directory != NULL) {
+ DirectoryEntry *temp = findSubDirectory(path);
+ if (temp != NULL) {
+ *directory = temp;
+ }
+ }
+ } else {
+ // file is in in a subdirectory, recurse into the matching subdirectory
+ std::wstring pathComponent = path.substr(0, len);
+ DirectoryEntry *temp = findSubDirectory(pathComponent);
+ if (temp != NULL) {
+ return temp->searchFile(path.substr(len + 1), directory);
+ }
+ }
+ return NULL;
+}
+
+/*
+void DirectoryEntry::sortOrigins()
+{
+ for (std::set<FileEntry>::iterator iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ const_cast<FileEntry&>(*iter).sortOrigins();
+ }
+ for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ (*iter)->sortOrigins();
+ }
+}
+*/
+
+DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const
+{
+ for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ if (_wcsicmp((*iter)->getName().c_str(), name.c_str()) == 0) {
+ return *iter;
+ }
+ }
+ return NULL;
+}
+
+
+
+const FileEntry *DirectoryEntry::findFile(const std::wstring &name)
+{
+ auto iter = m_Files.find(name);
+ if (iter != m_Files.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return NULL;
+ }
+}
+
+DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID)
+{
+ for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ if (_wcsicmp((*iter)->getName().c_str(), name.c_str()) == 0) {
+ return *iter;
+ }
+ }
+ if (create) {
+ std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.insert(m_SubDirectories.end(),
+ new DirectoryEntry(name, this, originID, m_FileRegister, m_OriginConnection));
+ return *iter;
+ } else {
+ return NULL;
+ }
+}
+
+
+DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &path, bool create, int originID)
+{
+ if (path.length() == 0) {
+ // path ended with a backslash?
+ return this;
+ }
+
+ size_t pos = path.find_first_of(L"\\/");
+ if (pos == std::wstring::npos) {
+ return getSubDirectory(path, create);
+ } else {
+ DirectoryEntry *nextChild = getSubDirectory(path.substr(0, pos), create, originID);
+ if (nextChild == NULL) {
+ return NULL;
+ } else {
+ return nextChild->getSubDirectoryRecursive(path.substr(pos + 1), create, originID);
+ }
+ }
+}
+
+
+
+FileRegister::FileRegister(boost::shared_ptr<OriginConnection> originConnection)
+ : m_OriginConnection(originConnection)
+{
+}
+
+FileEntry::Index FileRegister::generateIndex()
+{
+ static FileEntry::Index sIndex = 0;
+ return sIndex++;
+}
+
+bool FileRegister::indexValid(FileEntry::Index index) const
+{
+ return m_Files.find(index) != m_Files.end();
+}
+
+FileEntry &FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent)
+{
+ FileEntry::Index index = generateIndex();
+ m_Files[index] = FileEntry(index, name, parent);
+ return m_Files[index];
+}
+
+
+FileEntry *FileRegister::getFile(FileEntry::Index index)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ return &iter->second;
+ }
+ return NULL;
+}
+
+
+void FileRegister::unregisterFile(FileEntry &file)
+{
+ bool ignore;
+ // unregister from origin
+ int originID = file.getOrigin(ignore);
+ m_OriginConnection->getByID(originID).removeFile(file.getIndex());
+ const std::vector<int> &alternatives = file.getAlternatives();
+ for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
+ m_OriginConnection->getByID(*iter).removeFile(file.getIndex());
+ }
+
+ // unregister from directory
+ if (file.getParent() != NULL) {
+ file.getParent()->removeFile(file.getIndex());
+ }
+}
+
+
+void FileRegister::removeFile(FileEntry::Index index)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ unregisterFile(iter->second);
+ m_Files.erase(index);
+ }
+}
+
+void FileRegister::removeOrigin(FileEntry::Index index, int originID)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ if (iter->second.removeOrigin(originID)) {
+ unregisterFile(iter->second);
+ }
+ }
+}
+
+void FileRegister::sortOrigins()
+{
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ iter->second.sortOrigins();
+ }
+}
+} // namespace MOShared
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 72e96357..e292861d 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef DIRECTORYENTRY_H
#define DIRECTORYENTRY_H
@@ -31,6 +31,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <boost/shared_ptr.hpp>
#include "util.h"
+namespace MOShared {
+
class DirectoryEntry;
class OriginConnection;
@@ -310,4 +312,6 @@ private:
};
+} // namespace MOShared
+
#endif // DIRECTORYENTRY_H
diff --git a/src/shared/error_report.cpp b/src/shared/error_report.cpp
index 49fdba85..c1b25229 100644
--- a/src/shared/error_report.cpp
+++ b/src/shared/error_report.cpp
@@ -1,26 +1,28 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "error_report.h"
#include <sstream>
#include <stdio.h>
+namespace MOShared {
+
void reportError(LPCSTR format, ...)
{
@@ -97,3 +99,4 @@ std::wstring getCurrentErrorStringW()
return result;
}
}
+} // namespace MOShared
diff --git a/src/shared/error_report.h b/src/shared/error_report.h
index 6ee296df..c09ad75b 100644
--- a/src/shared/error_report.h
+++ b/src/shared/error_report.h
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#pragma once
#include <tchar.h>
@@ -32,11 +32,13 @@ typedef string tstring;
#endif
}
+extern void log(const char* format, ...);
+
+namespace MOShared {
+
void reportError(LPCSTR format, ...);
void reportError(LPCWSTR format, ...);
-extern void log(const char* format, ...);
-
std::string getCurrentErrorStringA();
std::wstring getCurrentErrorStringW();
@@ -45,3 +47,5 @@ std::wstring getCurrentErrorStringW();
#else
#define getCurrentErrorString getCurrentErrorStringA
#endif
+
+} // namespace MOShared
diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp
index f9699145..a37e1119 100644
--- a/src/shared/fallout3info.cpp
+++ b/src/shared/fallout3info.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "fallout3info.h"
#include "util.h"
#include <tchar.h>
@@ -28,6 +28,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Windows.h>
#include <boost/assign.hpp>
+namespace MOShared {
+
Fallout3Info::Fallout3Info(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
: GameInfo(omoDirectory, gameDirectory)
{
@@ -178,7 +180,7 @@ void Fallout3Info::createProfile(const std::wstring &directory, bool useDefaults
source << getGameDirectory() << L"\\fallout_default.ini";
} else {
source << getMyGamesDirectory() << L"\\Fallout3";
- if (::FileExists(source.str(), L"fallout.ini")) {
+ if (FileExists(source.str(), L"fallout.ini")) {
source << L"\\fallout.ini";
} else {
source.str(L"");
@@ -242,3 +244,4 @@ std::vector<ExecutableInfo> Fallout3Info::getExecutables()
return result;
}
+} // namespace MOShared
diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h
index bc9ab82d..9ec32027 100644
--- a/src/shared/fallout3info.h
+++ b/src/shared/fallout3info.h
@@ -1,28 +1,30 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef FALLOUT3INFO_H
#define FALLOUT3INFO_H
#include "gameinfo.h"
+namespace MOShared {
+
class Fallout3Info : public GameInfo
{
@@ -91,4 +93,6 @@ private:
};
+} // namespace MOShared
+
#endif // FALLOUT3INFO_H
diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp
index 380940b7..cd5586f3 100644
--- a/src/shared/falloutnvinfo.cpp
+++ b/src/shared/falloutnvinfo.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "falloutnvinfo.h"
#include "util.h"
#include <tchar.h>
@@ -28,6 +28,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Windows.h>
#include <boost/assign.hpp>
+namespace MOShared {
+
FalloutNVInfo::FalloutNVInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
: GameInfo(omoDirectory, gameDirectory)
@@ -156,7 +158,7 @@ void FalloutNVInfo::createProfile(const std::wstring &directory, bool useDefault
source << getGameDirectory() << L"\\fallout_default.ini";
} else {
source << getMyGamesDirectory() << L"\\FalloutNV";
- if (::FileExists(source.str(), L"fallout.ini")) {
+ if (FileExists(source.str(), L"fallout.ini")) {
source << L"\\fallout.ini";
} else {
source.str(L"");
@@ -244,3 +246,4 @@ std::vector<ExecutableInfo> FalloutNVInfo::getExecutables()
return result;
}
+} // namespace MOShared
diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h
index aa02ac30..53784177 100644
--- a/src/shared/falloutnvinfo.h
+++ b/src/shared/falloutnvinfo.h
@@ -1,28 +1,30 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef FALLOUTNVINFO_H
#define FALLOUTNVINFO_H
#include "gameinfo.h"
+namespace MOShared {
+
class FalloutNVInfo : public GameInfo
{
@@ -92,4 +94,6 @@ private:
static bool identifyGame(const std::wstring &searchPath);
};
+} // namespace MOShared
+
#endif // FALLOUTNVINFO_H
diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp
index 6054df8b..fd5072bf 100644
--- a/src/shared/gameinfo.cpp
+++ b/src/shared/gameinfo.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "gameinfo.h"
#include "windows_error.h"
@@ -31,6 +31,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <sstream>
#include <cassert>
+namespace MOShared {
+
GameInfo* GameInfo::s_Instance = NULL;
@@ -202,3 +204,4 @@ std::wstring GameInfo::getSpecialPath(LPCWSTR name) const
return temp;
}
+} // namespace MOShared
diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h
index 8828a2fc..fd8ba998 100644
--- a/src/shared/gameinfo.h
+++ b/src/shared/gameinfo.h
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef GAMEINFO_H
#define GAMEINFO_H
@@ -27,6 +27,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
+namespace MOShared {
+
enum CloseMOStyle {
DEFAULT_CLOSE,
DEFAULT_STAY,
@@ -184,4 +186,6 @@ private:
};
+} // namespace MOShared
+
#endif // GAMEINFO_H
diff --git a/src/shared/inject.cpp b/src/shared/inject.cpp
index 8cedd247..9fea7fc8 100644
--- a/src/shared/inject.cpp
+++ b/src/shared/inject.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "inject.h"
/*#if defined UNICODE && !defined _UNICODE
#define _UNICODE 1
@@ -31,6 +31,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "windows_error.h"
#include "error_report.h"
+namespace MOShared {
+
struct TParameters {
char dllname[MAX_PATH];
@@ -140,3 +142,5 @@ void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dll
throw windows_error("failed to overwrite thread context");
}
}
+
+} // namespace MOShared
diff --git a/src/shared/inject.h b/src/shared/inject.h
index 69c56cc6..d5030363 100644
--- a/src/shared/inject.h
+++ b/src/shared/inject.h
@@ -17,11 +17,15 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#pragma once
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <string>
-
-void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dllname, const std::wstring &profileName, int logLevel);
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <string>
+
+namespace MOShared {
+
+void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dllname, const std::wstring &profileName, int logLevel);
+
+}
diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp
index c34b2202..1c714072 100644
--- a/src/shared/oblivioninfo.cpp
+++ b/src/shared/oblivioninfo.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "oblivioninfo.h"
#include <tchar.h>
#include <ShlObj.h>
@@ -28,6 +28,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Windows.h>
#include <boost/assign.hpp>
+namespace MOShared {
+
OblivionInfo::OblivionInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
: GameInfo(omoDirectory, gameDirectory)
@@ -137,7 +139,7 @@ void OblivionInfo::createProfile(const std::wstring &directory, bool useDefaults
source << getGameDirectory() << L"\\oblivion_default.ini";
} else {
source << getMyGamesDirectory() << L"Oblivion";
- if (::FileExists(source.str(), L"oblivion.ini")) {
+ if (FileExists(source.str(), L"oblivion.ini")) {
source << L"\\oblivion.ini";
} else {
source.str(L"");
@@ -250,3 +252,4 @@ std::vector<ExecutableInfo> OblivionInfo::getExecutables()
return result;
}
+} // namespace MOShared
diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h
index 0d6bba65..51fd46e4 100644
--- a/src/shared/oblivioninfo.h
+++ b/src/shared/oblivioninfo.h
@@ -1,27 +1,29 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef OBLIVIONINFO_H
#define OBLIVIONINFO_H
#include "gameinfo.h"
+namespace MOShared {
+
class OblivionInfo : public GameInfo
{
@@ -89,4 +91,6 @@ private:
};
+} // namespace MOShared
+
#endif // OBLIVIONINFO_H
diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp
index f3043c28..6302cef3 100644
--- a/src/shared/skyriminfo.cpp
+++ b/src/shared/skyriminfo.cpp
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "skyriminfo.h"
#include "util.h"
@@ -29,6 +29,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <Windows.h>
#include <boost/assign.hpp>
+namespace MOShared {
+
SkyrimInfo::SkyrimInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
: GameInfo(omoDirectory, gameDirectory)
@@ -206,7 +208,7 @@ void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults)
source << getGameDirectory() << L"\\skyrim_default.ini";
} else {
source << getMyGamesDirectory() << L"\\Skyrim";
- if (::FileExists(source.str(), L"skyrim.ini")) {
+ if (FileExists(source.str(), L"skyrim.ini")) {
source << L"\\skyrim.ini";
} else {
source.str(L"");
@@ -276,3 +278,4 @@ std::vector<ExecutableInfo> SkyrimInfo::getExecutables()
return result;
}
+} // namespace MOShared
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h
index 25de4029..b12a5d58 100644
--- a/src/shared/skyriminfo.h
+++ b/src/shared/skyriminfo.h
@@ -1,28 +1,30 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef SKYRIMINFO_H
#define SKYRIMINFO_H
#include "gameinfo.h"
+namespace MOShared {
+
class SkyrimInfo : public GameInfo
{
@@ -97,4 +99,6 @@ private:
};
+} // namespace MOShared
+
#endif // SKYRIMINFO_H
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index ec52dd38..c256e959 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1,27 +1,29 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "util.h"
#include "windows_error.h"
#include <sstream>
#include <algorithm>
+namespace MOShared {
+
bool FileExists(const std::wstring &filename)
{
@@ -117,3 +119,4 @@ VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName)
}
}
+} // namespace MOShared
diff --git a/src/shared/util.h b/src/shared/util.h
index 7f44b55d..4faf0a30 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef UTIL_H
#define UTIL_H
@@ -25,6 +25,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
+namespace MOShared {
+
static const int MAXPATH_UNICODE = 32767;
@@ -43,4 +45,6 @@ std::wstring ToLower(const std::wstring &text);
VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName);
+} // namespace MOShared
+
#endif // UTIL_H
diff --git a/src/shared/windows_error.cpp b/src/shared/windows_error.cpp
index ad91db44..647a5bfb 100644
--- a/src/shared/windows_error.cpp
+++ b/src/shared/windows_error.cpp
@@ -1,25 +1,27 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "windows_error.h"
#include <sstream>
+namespace MOShared {
+
std::string windows_error::constructMessage(const std::string& input, int inErrorCode)
{
std::ostringstream finalMessage;
@@ -43,3 +45,5 @@ std::string windows_error::constructMessage(const std::string& input, int inErro
::SetLastError(errorCode); // restore error code because FormatMessage might have modified it
return finalMessage.str();
}
+
+} // namespace MOShared
diff --git a/src/shared/windows_error.h b/src/shared/windows_error.h
index 879ee1d2..20b19027 100644
--- a/src/shared/windows_error.h
+++ b/src/shared/windows_error.h
@@ -1,22 +1,22 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#ifndef WINDOWS_ERROR_H
#define WINDOWS_ERROR_H
@@ -25,6 +25,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+namespace MOShared {
+
class windows_error : public std::runtime_error {
public:
windows_error(const std::string& message, int errorcode = -1)
@@ -37,4 +39,6 @@ private:
int m_ErrorCode;
};
+} // namespace MOShared
+
#endif // WINDOWS_ERROR_H
diff --git a/src/singleinstance.cpp b/src/singleinstance.cpp
index 69de45e2..029e8366 100644
--- a/src/singleinstance.cpp
+++ b/src/singleinstance.cpp
@@ -17,91 +17,91 @@ 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 "singleinstance.h"
-#include "report.h"
-#include "utility.h"
-#include <QLocalSocket>
-
-static const char s_Key[] = "mo-43d1a3ad-eeb0-4818-97c9-eda5216c29b5";
-static const int s_Timeout = 5000;
-
-
-SingleInstance::SingleInstance(bool forcePrimary, QObject *parent) :
- QObject(parent), m_PrimaryInstance(false)
-{
- m_SharedMem.setKey(s_Key);
- if (!m_SharedMem.create(1)) {
- if (forcePrimary) {
- while (m_SharedMem.error() == QSharedMemory::AlreadyExists) {
- Sleep(500);
- if (m_SharedMem.create(1)) {
- m_PrimaryInstance = true;
- break;
- }
- }
- }
-
- if (m_SharedMem.error() == QSharedMemory::AlreadyExists) {
- m_SharedMem.attach();
- m_PrimaryInstance = false;
- }
- if ((m_SharedMem.error() != QSharedMemory::NoError) &&
- (m_SharedMem.error() != QSharedMemory::AlreadyExists)) {
- throw MyException(tr("SHM error: %1").arg(m_SharedMem.errorString()));
- }
- } else {
- m_PrimaryInstance = true;
- }
- if (m_PrimaryInstance) {
- connect(&m_Server, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
- m_Server.listen(s_Key);
- }
-}
-
-
-void SingleInstance::sendMessage(const QString &message)
-{
- if (m_PrimaryInstance) {
- // nobody there to receive the message
- return;
- }
- QLocalSocket socket(this);
-
- bool connected = false;
- for(int i = 0; i < 2 && !connected; ++i) {
- if (i > 0) {
- Sleep(250);
- }
-
- // other instance may be just starting up
- socket.connectToServer(s_Key, QIODevice::WriteOnly);
- connected = socket.waitForConnected(s_Timeout);
- }
-
- if (!connected) {
- reportError(tr("failed to connect to running instance: %1").arg(socket.errorString()));
- return;
- }
-
- socket.write(message.toUtf8());
- if (!socket.waitForBytesWritten(s_Timeout)) {
- reportError(tr("failed to connect to running instance: %1").arg(socket.errorString()));
- return;
- }
-
- socket.disconnectFromServer();
-}
-
-
-void SingleInstance::receiveMessage()
-{
- QLocalSocket *socket = m_Server.nextPendingConnection();
- if (!socket->waitForReadyRead(s_Timeout)) {
- reportError(tr("failed to receive data from secondary instance: %1").arg(socket->errorString()));
- return;
- }
-
- QString message = QString::fromUtf8(socket->readAll().constData());
- emit messageSent(message);
- socket->disconnectFromServer();
-}
+#include "singleinstance.h"
+#include "report.h"
+#include "utility.h"
+#include <QLocalSocket>
+
+static const char s_Key[] = "mo-43d1a3ad-eeb0-4818-97c9-eda5216c29b5";
+static const int s_Timeout = 5000;
+
+
+SingleInstance::SingleInstance(bool forcePrimary, QObject *parent) :
+ QObject(parent), m_PrimaryInstance(false)
+{
+ m_SharedMem.setKey(s_Key);
+ if (!m_SharedMem.create(1)) {
+ if (forcePrimary) {
+ while (m_SharedMem.error() == QSharedMemory::AlreadyExists) {
+ Sleep(500);
+ if (m_SharedMem.create(1)) {
+ m_PrimaryInstance = true;
+ break;
+ }
+ }
+ }
+
+ if (m_SharedMem.error() == QSharedMemory::AlreadyExists) {
+ m_SharedMem.attach();
+ m_PrimaryInstance = false;
+ }
+ if ((m_SharedMem.error() != QSharedMemory::NoError) &&
+ (m_SharedMem.error() != QSharedMemory::AlreadyExists)) {
+ throw MOBase::MyException(tr("SHM error: %1").arg(m_SharedMem.errorString()));
+ }
+ } else {
+ m_PrimaryInstance = true;
+ }
+ if (m_PrimaryInstance) {
+ connect(&m_Server, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
+ m_Server.listen(s_Key);
+ }
+}
+
+
+void SingleInstance::sendMessage(const QString &message)
+{
+ if (m_PrimaryInstance) {
+ // nobody there to receive the message
+ return;
+ }
+ QLocalSocket socket(this);
+
+ bool connected = false;
+ for(int i = 0; i < 2 && !connected; ++i) {
+ if (i > 0) {
+ Sleep(250);
+ }
+
+ // other instance may be just starting up
+ socket.connectToServer(s_Key, QIODevice::WriteOnly);
+ connected = socket.waitForConnected(s_Timeout);
+ }
+
+ if (!connected) {
+ reportError(tr("failed to connect to running instance: %1").arg(socket.errorString()));
+ return;
+ }
+
+ socket.write(message.toUtf8());
+ if (!socket.waitForBytesWritten(s_Timeout)) {
+ reportError(tr("failed to connect to running instance: %1").arg(socket.errorString()));
+ return;
+ }
+
+ socket.disconnectFromServer();
+}
+
+
+void SingleInstance::receiveMessage()
+{
+ QLocalSocket *socket = m_Server.nextPendingConnection();
+ if (!socket->waitForReadyRead(s_Timeout)) {
+ reportError(tr("failed to receive data from secondary instance: %1").arg(socket->errorString()));
+ return;
+ }
+
+ QString message = QString::fromUtf8(socket->readAll().constData());
+ emit messageSent(message);
+ socket->disconnectFromServer();
+}
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 13e74f93..39bd2af9 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -17,93 +17,97 @@ 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 "spawn.h"
-#include "report.h"
-#include "utility.h"
-#include <gameinfo.h>
-#include <inject.h>
-#include <appconfig.h>
-#include <windows_error.h>
-#include <QApplication>
-
-
-bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool suspended, HANDLE& processHandle, HANDLE& threadHandle)
-{
- STARTUPINFO si;
- ::ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
- int length = wcslen(binary) + wcslen(arguments) + 4;
- wchar_t *commandLine = NULL;
- if (arguments[0] != L'\0') {
- commandLine = new wchar_t[length];
- _snwprintf(commandLine, length, L"\"%ls\" %ls", binary, arguments);
- } else {
- commandLine = new wchar_t[length];
- _snwprintf(commandLine, length, L"\"%ls\"", binary);
-
- }
-
- PROCESS_INFORMATION pi;
- BOOL success = ::CreateProcess(NULL,
- commandLine,
- NULL, NULL, // no special process or thread attributes
- FALSE, // don't inherit handle
- suspended ? CREATE_SUSPENDED : 0, // create suspended so I have time to inject the DLL
- NULL, // same environment as parent
- currentDirectory, // current directory
- &si, &pi // startup and process information
- );
-
- delete [] commandLine;
-
- if (!success) {
- throw windows_error("failed to start process");
- }
-
- processHandle = pi.hProcess;
- threadHandle = pi.hThread;
- return true;
-}
-
-
-HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString& profileName, int logLevel, const QDir &currentDirectory, bool hooked)
-{
- HANDLE processHandle, threadHandle;
- std::wstring binaryName = ToWString(QDir::toNativeSeparators(binary.absoluteFilePath()));
- std::wstring currentDirectoryName = ToWString(QDir::toNativeSeparators(currentDirectory.absolutePath()));
-
- try {
- if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(), hooked, processHandle, threadHandle)) {
- reportError(QObject::tr("failed to spawn \"%1\"").arg(binary.fileName()));
- return INVALID_HANDLE_VALUE;
- }
- } catch (const windows_error &e) {
- reportError(QObject::tr("failed to spawn \"%1\": %2").arg(binary.fileName()).arg(e.what()));
- return INVALID_HANDLE_VALUE;
- }
-
- if (hooked) {
- try {
- QFileInfo dllInfo(QApplication::applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()));
- if (!dllInfo.exists()) {
- reportError(QObject::tr("\"%1\" doesn't exist").arg(dllInfo.fileName()));
- return INVALID_HANDLE_VALUE;
- }
- injectDLL(processHandle, threadHandle,
- QDir::toNativeSeparators(dllInfo.canonicalFilePath()).toLocal8Bit().constData(),
- ToWString(profileName).c_str(), logLevel);
- } catch (const windows_error& e) {
- reportError(QObject::tr("failed to inject dll into \"%1\": %2").arg(binary.fileName()).arg(e.what()));
- ::TerminateProcess(processHandle, 1);
- return INVALID_HANDLE_VALUE;
- }
-#ifdef _DEBUG
- reportError("ready?");
-#endif // DEBUG
- if (::ResumeThread(threadHandle) == (DWORD)-1) {
- reportError(QObject::tr("failed to run \"%1\"").arg(binary.fileName()));
- return INVALID_HANDLE_VALUE;
- }
- }
- return processHandle;
-}
+#include "spawn.h"
+#include "report.h"
+#include "utility.h"
+#include <gameinfo.h>
+#include <inject.h>
+#include <appconfig.h>
+#include <windows_error.h>
+#include <QApplication>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool suspended, HANDLE& processHandle, HANDLE& threadHandle)
+{
+ STARTUPINFO si;
+ ::ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ int length = wcslen(binary) + wcslen(arguments) + 4;
+ wchar_t *commandLine = NULL;
+ if (arguments[0] != L'\0') {
+ commandLine = new wchar_t[length];
+ _snwprintf(commandLine, length, L"\"%ls\" %ls", binary, arguments);
+ } else {
+ commandLine = new wchar_t[length];
+ _snwprintf(commandLine, length, L"\"%ls\"", binary);
+
+ }
+
+ PROCESS_INFORMATION pi;
+ BOOL success = ::CreateProcess(NULL,
+ commandLine,
+ NULL, NULL, // no special process or thread attributes
+ FALSE, // don't inherit handle
+ suspended ? CREATE_SUSPENDED : 0, // create suspended so I have time to inject the DLL
+ NULL, // same environment as parent
+ currentDirectory, // current directory
+ &si, &pi // startup and process information
+ );
+
+ delete [] commandLine;
+
+ if (!success) {
+ throw windows_error("failed to start process");
+ }
+
+ processHandle = pi.hProcess;
+ threadHandle = pi.hThread;
+ return true;
+}
+
+
+HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString& profileName, int logLevel, const QDir &currentDirectory, bool hooked)
+{
+ HANDLE processHandle, threadHandle;
+ std::wstring binaryName = ToWString(QDir::toNativeSeparators(binary.absoluteFilePath()));
+ std::wstring currentDirectoryName = ToWString(QDir::toNativeSeparators(currentDirectory.absolutePath()));
+
+ try {
+ if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(), hooked, processHandle, threadHandle)) {
+ reportError(QObject::tr("failed to spawn \"%1\"").arg(binary.fileName()));
+ return INVALID_HANDLE_VALUE;
+ }
+ } catch (const windows_error &e) {
+ reportError(QObject::tr("failed to spawn \"%1\": %2").arg(binary.fileName()).arg(e.what()));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (hooked) {
+ try {
+ QFileInfo dllInfo(QApplication::applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()));
+ if (!dllInfo.exists()) {
+ reportError(QObject::tr("\"%1\" doesn't exist").arg(dllInfo.fileName()));
+ return INVALID_HANDLE_VALUE;
+ }
+ injectDLL(processHandle, threadHandle,
+ QDir::toNativeSeparators(dllInfo.canonicalFilePath()).toLocal8Bit().constData(),
+ ToWString(profileName).c_str(), logLevel);
+ } catch (const windows_error& e) {
+ reportError(QObject::tr("failed to inject dll into \"%1\": %2").arg(binary.fileName()).arg(e.what()));
+ ::TerminateProcess(processHandle, 1);
+ return INVALID_HANDLE_VALUE;
+ }
+#ifdef _DEBUG
+ reportError("ready?");
+#endif // DEBUG
+ if (::ResumeThread(threadHandle) == (DWORD)-1) {
+ reportError(QObject::tr("failed to run \"%1\"").arg(binary.fileName()));
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ return processHandle;
+}
diff --git a/src/syncoverwritedialog.cpp b/src/syncoverwritedialog.cpp
index 0fac8a7d..aae0fd7c 100644
--- a/src/syncoverwritedialog.cpp
+++ b/src/syncoverwritedialog.cpp
@@ -17,130 +17,134 @@ 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 "syncoverwritedialog.h"
-#include "ui_syncoverwritedialog.h"
-#include <utility.h>
-#include <report.h>
-#include <gameinfo.h>
-#include <QDir>
-#include <QDirIterator>
-#include <QComboBox>
-#include <QStringList>
-
-
-SyncOverwriteDialog::SyncOverwriteDialog(const QString &path, DirectoryEntry *directoryStructure, QWidget *parent)
- : TutorableDialog("SyncOverwrite", parent),
- ui(new Ui::SyncOverwriteDialog), m_SourcePath(path), m_DirectoryStructure(directoryStructure)
-{
- ui->setupUi(this);
- refresh(path, directoryStructure);
-
- QHeaderView *headerView = ui->syncTree->header();
-#if QT_VERSION >= 0x050000
- headerView->setSectionResizeMode(0, QHeaderView::Stretch);
- headerView->setSectionResizeMode(1, QHeaderView::Interactive);
-#else
- headerView->setResizeMode(0, QHeaderView::Stretch);
- headerView->setResizeMode(1, QHeaderView::Interactive);
-#endif
-}
-
-
-SyncOverwriteDialog::~SyncOverwriteDialog()
-{
- delete ui;
-}
-
-
-void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *directoryStructure, QTreeWidgetItem *subTree)
-{
- QDir overwrite(path);
- overwrite.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
- QDirIterator dirIter(overwrite);
- while (dirIter.hasNext()) {
- dirIter.next();
- QFileInfo fileInfo = dirIter.fileInfo();
-
- QString file = fileInfo.fileName();
- if (file == "meta.ini") {
- continue;
- }
-
- QTreeWidgetItem *newItem = new QTreeWidgetItem(subTree, QStringList(file));
-
- if (fileInfo.isDir()) {
- DirectoryEntry *subDir = directoryStructure->findSubDirectory(ToWString(file));
- if (subDir != NULL) {
- readTree(fileInfo.absoluteFilePath(), subDir, newItem);
- } else {
- qCritical("no directory structure for %s?", file.toUtf8().constData());
- delete newItem;
- newItem = NULL;
- }
- } else {
- const FileEntry *entry = directoryStructure->findFile(ToWString(file));
- QComboBox* combo = new QComboBox(ui->syncTree);
- combo->addItem(tr("<don't sync>"), -1);
- if (entry != NULL) {
- const std::vector<int> &alternatives = entry->getAlternatives();
- for (std::vector<int>::const_iterator iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
- combo->addItem(ToQString(m_DirectoryStructure->getOriginByID(*iter).getName()), *iter);
- }
- combo->setCurrentIndex(combo->count() - 1);
- } else {
- combo->setCurrentIndex(0);
- }
- ui->syncTree->setItemWidget(newItem, 1, combo);
- }
- if (newItem != NULL) {
- subTree->addChild(newItem);
- }
- }
-}
-
-
-void SyncOverwriteDialog::refresh(const QString &path, DirectoryEntry *directoryStructure)
-{
- QTreeWidgetItem *rootItem = new QTreeWidgetItem(ui->syncTree, QStringList("<data>"));
- readTree(path, directoryStructure, rootItem);
- ui->syncTree->addTopLevelItem(rootItem);
- ui->syncTree->expandAll();
-}
-
-
-void SyncOverwriteDialog::applyTo(QTreeWidgetItem *item, const QString &path, const QString &modDirectory)
-{
- for (int i = 0; i < item->childCount(); ++i) {
- QTreeWidgetItem *child = item->child(i);
- QString filePath;
- if (path.length() != 0) {
- filePath = path.mid(0).append("/").append(child->text(0));
- } else {
- filePath = child->text(0);
- }
- if (child->childCount() != 0) {
- applyTo(child, filePath, modDirectory);
- } else {
- QComboBox *comboBox = qobject_cast<QComboBox*>(ui->syncTree->itemWidget(child, 1));
- if (comboBox != NULL) {
- int originID = comboBox->itemData(comboBox->currentIndex(), Qt::UserRole).toInt();
- if (originID != -1) {
- FilesOrigin &origin = m_DirectoryStructure->getOriginByID(originID);
- QString source = m_SourcePath.mid(0).append("/").append(filePath);
- QString destination = modDirectory.mid(0).append("/").append(ToQString(origin.getName())).append("/").append(filePath);
- if (!QFile::remove(destination)) {
- reportError(tr("failed to remove %1").arg(destination));
- } else if (!QFile::rename(source, destination)) {
- reportError(tr("failed to move %1 to %2").arg(source).arg(destination));
- }
- }
- }
- }
- }
-}
-
-
-void SyncOverwriteDialog::apply(const QString &modDirectory)
-{
- applyTo(ui->syncTree->topLevelItem(0), "", modDirectory);
-}
+#include "syncoverwritedialog.h"
+#include "ui_syncoverwritedialog.h"
+#include <utility.h>
+#include <report.h>
+#include <gameinfo.h>
+#include <QDir>
+#include <QDirIterator>
+#include <QComboBox>
+#include <QStringList>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+SyncOverwriteDialog::SyncOverwriteDialog(const QString &path, DirectoryEntry *directoryStructure, QWidget *parent)
+ : TutorableDialog("SyncOverwrite", parent),
+ ui(new Ui::SyncOverwriteDialog), m_SourcePath(path), m_DirectoryStructure(directoryStructure)
+{
+ ui->setupUi(this);
+ refresh(path, directoryStructure);
+
+ QHeaderView *headerView = ui->syncTree->header();
+#if QT_VERSION >= 0x050000
+ headerView->setSectionResizeMode(0, QHeaderView::Stretch);
+ headerView->setSectionResizeMode(1, QHeaderView::Interactive);
+#else
+ headerView->setResizeMode(0, QHeaderView::Stretch);
+ headerView->setResizeMode(1, QHeaderView::Interactive);
+#endif
+}
+
+
+SyncOverwriteDialog::~SyncOverwriteDialog()
+{
+ delete ui;
+}
+
+
+void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *directoryStructure, QTreeWidgetItem *subTree)
+{
+ QDir overwrite(path);
+ overwrite.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
+ QDirIterator dirIter(overwrite);
+ while (dirIter.hasNext()) {
+ dirIter.next();
+ QFileInfo fileInfo = dirIter.fileInfo();
+
+ QString file = fileInfo.fileName();
+ if (file == "meta.ini") {
+ continue;
+ }
+
+ QTreeWidgetItem *newItem = new QTreeWidgetItem(subTree, QStringList(file));
+
+ if (fileInfo.isDir()) {
+ DirectoryEntry *subDir = directoryStructure->findSubDirectory(ToWString(file));
+ if (subDir != NULL) {
+ readTree(fileInfo.absoluteFilePath(), subDir, newItem);
+ } else {
+ qCritical("no directory structure for %s?", file.toUtf8().constData());
+ delete newItem;
+ newItem = NULL;
+ }
+ } else {
+ const FileEntry *entry = directoryStructure->findFile(ToWString(file));
+ QComboBox* combo = new QComboBox(ui->syncTree);
+ combo->addItem(tr("<don't sync>"), -1);
+ if (entry != NULL) {
+ const std::vector<int> &alternatives = entry->getAlternatives();
+ for (std::vector<int>::const_iterator iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
+ combo->addItem(ToQString(m_DirectoryStructure->getOriginByID(*iter).getName()), *iter);
+ }
+ combo->setCurrentIndex(combo->count() - 1);
+ } else {
+ combo->setCurrentIndex(0);
+ }
+ ui->syncTree->setItemWidget(newItem, 1, combo);
+ }
+ if (newItem != NULL) {
+ subTree->addChild(newItem);
+ }
+ }
+}
+
+
+void SyncOverwriteDialog::refresh(const QString &path, DirectoryEntry *directoryStructure)
+{
+ QTreeWidgetItem *rootItem = new QTreeWidgetItem(ui->syncTree, QStringList("<data>"));
+ readTree(path, directoryStructure, rootItem);
+ ui->syncTree->addTopLevelItem(rootItem);
+ ui->syncTree->expandAll();
+}
+
+
+void SyncOverwriteDialog::applyTo(QTreeWidgetItem *item, const QString &path, const QString &modDirectory)
+{
+ for (int i = 0; i < item->childCount(); ++i) {
+ QTreeWidgetItem *child = item->child(i);
+ QString filePath;
+ if (path.length() != 0) {
+ filePath = path.mid(0).append("/").append(child->text(0));
+ } else {
+ filePath = child->text(0);
+ }
+ if (child->childCount() != 0) {
+ applyTo(child, filePath, modDirectory);
+ } else {
+ QComboBox *comboBox = qobject_cast<QComboBox*>(ui->syncTree->itemWidget(child, 1));
+ if (comboBox != NULL) {
+ int originID = comboBox->itemData(comboBox->currentIndex(), Qt::UserRole).toInt();
+ if (originID != -1) {
+ FilesOrigin &origin = m_DirectoryStructure->getOriginByID(originID);
+ QString source = m_SourcePath.mid(0).append("/").append(filePath);
+ QString destination = modDirectory.mid(0).append("/").append(ToQString(origin.getName())).append("/").append(filePath);
+ if (!QFile::remove(destination)) {
+ reportError(tr("failed to remove %1").arg(destination));
+ } else if (!QFile::rename(source, destination)) {
+ reportError(tr("failed to move %1 to %2").arg(source).arg(destination));
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void SyncOverwriteDialog::apply(const QString &modDirectory)
+{
+ applyTo(ui->syncTree->topLevelItem(0), "", modDirectory);
+}
diff --git a/src/syncoverwritedialog.h b/src/syncoverwritedialog.h
index ca4abb8e..da7c8cf5 100644
--- a/src/syncoverwritedialog.h
+++ b/src/syncoverwritedialog.h
@@ -17,39 +17,39 @@ 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 SYNCOVERWRITEDIALOG_H
-#define SYNCOVERWRITEDIALOG_H
-
-
-#include <directoryentry.h>
-#include "tutorabledialog.h"
-#include <QTreeWidgetItem>
-
-
-namespace Ui {
-class SyncOverwriteDialog;
-}
-
-class SyncOverwriteDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
- explicit SyncOverwriteDialog(const QString &path, DirectoryEntry *directoryStructure, QWidget *parent = 0);
- ~SyncOverwriteDialog();
-
- void apply(const QString &modDirectory);
-private:
- void refresh(const QString &path, DirectoryEntry *directoryStructure);
- void readTree(const QString &path, DirectoryEntry *directoryStructure, QTreeWidgetItem *subTree);
- void applyTo(QTreeWidgetItem *item, const QString &path, const QString &modDirectory);
-
-private:
-
- Ui::SyncOverwriteDialog *ui;
- QString m_SourcePath;
- DirectoryEntry *m_DirectoryStructure;
-
-};
-
-#endif // SYNCOVERWRITEDIALOG_H
+#ifndef SYNCOVERWRITEDIALOG_H
+#define SYNCOVERWRITEDIALOG_H
+
+
+#include <directoryentry.h>
+#include "tutorabledialog.h"
+#include <QTreeWidgetItem>
+
+
+namespace Ui {
+class SyncOverwriteDialog;
+}
+
+class SyncOverwriteDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+ explicit SyncOverwriteDialog(const QString &path, MOShared::DirectoryEntry *directoryStructure, QWidget *parent = 0);
+ ~SyncOverwriteDialog();
+
+ void apply(const QString &modDirectory);
+private:
+ void refresh(const QString &path, MOShared::DirectoryEntry *directoryStructure);
+ void readTree(const QString &path, MOShared::DirectoryEntry *directoryStructure, QTreeWidgetItem *subTree);
+ void applyTo(QTreeWidgetItem *item, const QString &path, const QString &modDirectory);
+
+private:
+
+ Ui::SyncOverwriteDialog *ui;
+ QString m_SourcePath;
+ MOShared::DirectoryEntry *m_DirectoryStructure;
+
+};
+
+#endif // SYNCOVERWRITEDIALOG_H
diff --git a/src/transfersavesdialog.cpp b/src/transfersavesdialog.cpp
index c6ddfd78..e69e4ab2 100644
--- a/src/transfersavesdialog.cpp
+++ b/src/transfersavesdialog.cpp
@@ -17,315 +17,319 @@ 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 "transfersavesdialog.h"
-#include "ui_transfersavesdialog.h"
-#include "utility.h"
-#include <gameinfo.h>
-#include <QDir>
-#include <QMessageBox>
-#include <Shlwapi.h>
-#include <shlobj.h>
-
-
-TransferSavesDialog::TransferSavesDialog(const Profile &profile, QWidget *parent)
- : TutorableDialog("TransferSaves", parent), ui(new Ui::TransferSavesDialog), m_Profile(profile)
-{
- ui->setupUi(this);
- refreshGlobalSaves();
- refreshLocalSaves();
- refreshGlobalCharacters();
- refreshLocalCharacters();
-}
-
-TransferSavesDialog::~TransferSavesDialog()
-{
- delete ui;
-}
-
-
-void TransferSavesDialog::refreshGlobalSaves()
-{
- m_GlobalSaves.clear();
-
- QDir savesDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir())));
-
- QStringList filters;
- filters << ToQString(GameInfo::instance().getSaveGameExtension());
- savesDir.setNameFilters(filters);
-
- QStringList files = savesDir.entryList(QDir::Files, QDir::Time);
-
- foreach (const QString &filename, files) {
- SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename));
- save->setParent(this);
- m_GlobalSaves.push_back(save);
- }
-}
-
-
-void TransferSavesDialog::refreshLocalSaves()
-{
- m_LocalSaves.clear();
-
- QDir savesDir(m_Profile.getPath().append("/saves"));
-
- QStringList filters;
- filters << ToQString(GameInfo::instance().getSaveGameExtension());
- savesDir.setNameFilters(filters);
-
- QStringList files = savesDir.entryList(QDir::Files, QDir::Time);
-
- foreach (const QString &filename, files) {
- SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename));
- save->setParent(this);
- m_LocalSaves.push_back(save);
- }
-}
-
-
-void TransferSavesDialog::refreshGlobalCharacters()
-{
- std::set<QString> characters;
- for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
- iter != m_GlobalSaves.end(); ++iter) {
- characters.insert((*iter)->pcName());
- }
- ui->globalCharacterList->clear();
- for (std::set<QString>::const_iterator iter = characters.begin();
- iter != characters.end(); ++iter) {
- ui->globalCharacterList->addItem(*iter);
- }
- if (ui->globalCharacterList->count() > 0) {
- ui->globalCharacterList->setCurrentRow(0);
- ui->copyToLocalBtn->setEnabled(true);
- ui->moveToLocalBtn->setEnabled(true);
- } else {
- ui->copyToLocalBtn->setEnabled(false);
- ui->moveToLocalBtn->setEnabled(false);
- }
-}
-
-
-void TransferSavesDialog::refreshLocalCharacters()
-{
- std::set<QString> characters;
- for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
- iter != m_LocalSaves.end(); ++iter) {
- characters.insert((*iter)->pcName());
- }
- ui->localCharacterList->clear();
- for (std::set<QString>::const_iterator iter = characters.begin();
- iter != characters.end(); ++iter) {
- ui->localCharacterList->addItem(*iter);
- }
- if (ui->localCharacterList->count() > 0) {
- ui->localCharacterList->setCurrentRow(0);
- ui->copyToGlobalBtn->setEnabled(true);
- ui->moveToGlobalBtn->setEnabled(true);
- } else {
- ui->copyToGlobalBtn->setEnabled(false);
- ui->moveToGlobalBtn->setEnabled(false);
- }
-}
-
-
-bool TransferSavesDialog::testOverwrite(OverwriteMode &overwriteMode, const QString &destinationFile)
-{
- QMessageBox::StandardButton res = overwriteMode == OVERWRITE_YES ? QMessageBox::Yes : QMessageBox::No;
- if (overwriteMode == OVERWRITE_ASK) {
- res = QMessageBox::question(this, tr("Overwrite"),
- tr("Overwrite the file \"%1\"").arg(destinationFile),
- QMessageBox::Yes | QMessageBox::No | QMessageBox::YesToAll | QMessageBox::NoToAll);
- if (res == QMessageBox::YesToAll) {
- overwriteMode = OVERWRITE_YES;
- res = QMessageBox::Yes;
- } else if (res == QMessageBox::NoToAll) {
- overwriteMode = OVERWRITE_NO;
- res = QMessageBox::No;
- }
- }
- return res == QMessageBox::Yes;
-}
-
-QStringList TransferSavesDialog::getFilesToProcess(const SaveGame *save)
-{
- QStringList result = save->attachedFiles();
- result.append(save->fileName());
- return result;
-}
-
-void TransferSavesDialog::on_moveToLocalBtn_clicked()
-{
- QString selectedCharacter = ui->globalCharacterList->currentItem()->text();
- if (QMessageBox::question(this, tr("Confirm"),
- tr("Copy all save games of character \"%1\" to the profile?").arg(selectedCharacter),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- QString destination = m_Profile.getPath().append("/saves");
- OverwriteMode overwriteMode = OVERWRITE_ASK;
-
- for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
- iter != m_GlobalSaves.end(); ++iter) {
- if ((*iter)->pcName() == selectedCharacter) {
- QStringList files = getFilesToProcess(*iter);
- foreach (const QString &file, files) {
- QFileInfo fileInfo(file);
- QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
- if (QFile::exists(destinationFile)) {
- if (testOverwrite(overwriteMode, destinationFile)) {
- QFile::remove(destinationFile);
- } else {
- continue;
- }
- }
- if (!QFile::rename(fileInfo.absoluteFilePath(), destinationFile)) {
- qCritical("failed to move %s to %s",
- fileInfo.absoluteFilePath().toUtf8().constData(),
- destinationFile.toUtf8().constData());
- }
- }
- }
- }
- }
- refreshGlobalSaves();
- refreshGlobalCharacters();
- refreshLocalSaves();
- refreshLocalCharacters();
-}
-
-void TransferSavesDialog::on_copyToLocalBtn_clicked()
-{
- QString selectedCharacter = ui->globalCharacterList->currentItem()->text();
- if (QMessageBox::question(this, tr("Confirm"),
- tr("Copy all save games of character \"%1\" to the profile?").arg(selectedCharacter),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- QString destination = m_Profile.getPath().append("/saves");
- OverwriteMode overwriteMode = OVERWRITE_ASK;
- for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
- iter != m_GlobalSaves.end(); ++iter) {
- if ((*iter)->pcName() == selectedCharacter) {
- QStringList files = getFilesToProcess(*iter);
- foreach (const QString &file, files) {
- QFileInfo fileInfo(file);
- QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
- if (QFile::exists(destinationFile)) {
- if (testOverwrite(overwriteMode, destinationFile)) {
- QFile::remove(destinationFile);
- } else {
- continue;
- }
- }
- if (!QFile::copy(fileInfo.absoluteFilePath(), destinationFile)) {
- qCritical("failed to copy %s to %s",
- fileInfo.absoluteFilePath().toUtf8().constData(),
- destinationFile.toUtf8().constData());
- }
- }
- }
- }
- }
- refreshLocalSaves();
- refreshLocalCharacters();
-}
-
-void TransferSavesDialog::on_moveToGlobalBtn_clicked()
-{
- QString selectedCharacter = ui->localCharacterList->currentItem()->text();
- if (QMessageBox::question(this, tr("Confirm"),
- tr("Move all save games of character \"%1\" to the global location? Please be aware "
- "that this will mess up the running number of save games.").arg(selectedCharacter),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
-
- QString destination = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir()));
- OverwriteMode overwriteMode = OVERWRITE_ASK;
- for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
- iter != m_LocalSaves.end(); ++iter) {
- if ((*iter)->pcName() == selectedCharacter) {
- QStringList files = getFilesToProcess(*iter);
- foreach (const QString &file, files) {
- QFileInfo fileInfo(file);
- QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
- if (QFile::exists(destinationFile)) {
- if (testOverwrite(overwriteMode, destinationFile)) {
- QFile::remove(destinationFile);
- } else {
- continue;
- }
- }
- if (!QFile::rename(fileInfo.absoluteFilePath(), destinationFile)) {
- qCritical("failed to move %s to %s",
- fileInfo.absoluteFilePath().toUtf8().constData(),
- destinationFile.toUtf8().constData());
- }
- }
- }
- }
- }
- refreshGlobalSaves();
- refreshGlobalCharacters();
- refreshLocalSaves();
- refreshLocalCharacters();
-}
-
-void TransferSavesDialog::on_copyToGlobalBtn_clicked()
-{
- QString selectedCharacter = ui->localCharacterList->currentItem()->text();
- if (QMessageBox::question(this, tr("Confirm"),
- tr("Copy all save games of character \"%1\" to the global location? Please be aware "
- "that this will mess up the running number of save games.").arg(selectedCharacter),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
-
- QString destination = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir()));
- OverwriteMode overwriteMode = OVERWRITE_ASK;
- for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
- iter != m_LocalSaves.end(); ++iter) {
- if ((*iter)->pcName() == selectedCharacter) {
- QStringList files = getFilesToProcess(*iter);
- foreach (const QString &file, files) {
- QFileInfo fileInfo(file);
- QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
- if (QFile::exists(destinationFile)) {
- if (testOverwrite(overwriteMode, destinationFile)) {
- QFile::remove(destinationFile);
- } else {
- continue;
- }
- }
- if (!QFile::copy(fileInfo.absoluteFilePath(), destinationFile)) {
- qCritical("failed to copy %s to %s",
- fileInfo.absoluteFilePath().toUtf8().constData(),
- destinationFile.toUtf8().constData());
- }
- }
- }
- }
- }
- refreshGlobalSaves();
- refreshGlobalCharacters();
-}
-
-void TransferSavesDialog::on_doneButton_clicked()
-{
- close();
-}
-
-void TransferSavesDialog::on_globalCharacterList_currentTextChanged(const QString &currentText)
-{
- ui->globalSavesList->clear();
- for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
- iter != m_GlobalSaves.end(); ++iter) {
- if ((*iter)->pcName() == currentText) {
- ui->globalSavesList->addItem(QFileInfo((*iter)->fileName()).fileName());
- }
- }
-}
-
-void TransferSavesDialog::on_localCharacterList_currentTextChanged(const QString &currentText)
-{
- ui->localSavesList->clear();
- for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
- iter != m_LocalSaves.end(); ++iter) {
- if ((*iter)->pcName() == currentText) {
- ui->localSavesList->addItem(QFileInfo((*iter)->fileName()).fileName());
- }
- }
-}
+#include "transfersavesdialog.h"
+#include "ui_transfersavesdialog.h"
+#include "utility.h"
+#include <gameinfo.h>
+#include <QDir>
+#include <QMessageBox>
+#include <Shlwapi.h>
+#include <shlobj.h>
+
+
+using namespace MOBase;
+using namespace MOShared;
+
+
+TransferSavesDialog::TransferSavesDialog(const Profile &profile, QWidget *parent)
+ : TutorableDialog("TransferSaves", parent), ui(new Ui::TransferSavesDialog), m_Profile(profile)
+{
+ ui->setupUi(this);
+ refreshGlobalSaves();
+ refreshLocalSaves();
+ refreshGlobalCharacters();
+ refreshLocalCharacters();
+}
+
+TransferSavesDialog::~TransferSavesDialog()
+{
+ delete ui;
+}
+
+
+void TransferSavesDialog::refreshGlobalSaves()
+{
+ m_GlobalSaves.clear();
+
+ QDir savesDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir())));
+
+ QStringList filters;
+ filters << ToQString(GameInfo::instance().getSaveGameExtension());
+ savesDir.setNameFilters(filters);
+
+ QStringList files = savesDir.entryList(QDir::Files, QDir::Time);
+
+ foreach (const QString &filename, files) {
+ SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename));
+ save->setParent(this);
+ m_GlobalSaves.push_back(save);
+ }
+}
+
+
+void TransferSavesDialog::refreshLocalSaves()
+{
+ m_LocalSaves.clear();
+
+ QDir savesDir(m_Profile.getPath().append("/saves"));
+
+ QStringList filters;
+ filters << ToQString(GameInfo::instance().getSaveGameExtension());
+ savesDir.setNameFilters(filters);
+
+ QStringList files = savesDir.entryList(QDir::Files, QDir::Time);
+
+ foreach (const QString &filename, files) {
+ SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename));
+ save->setParent(this);
+ m_LocalSaves.push_back(save);
+ }
+}
+
+
+void TransferSavesDialog::refreshGlobalCharacters()
+{
+ std::set<QString> characters;
+ for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
+ iter != m_GlobalSaves.end(); ++iter) {
+ characters.insert((*iter)->pcName());
+ }
+ ui->globalCharacterList->clear();
+ for (std::set<QString>::const_iterator iter = characters.begin();
+ iter != characters.end(); ++iter) {
+ ui->globalCharacterList->addItem(*iter);
+ }
+ if (ui->globalCharacterList->count() > 0) {
+ ui->globalCharacterList->setCurrentRow(0);
+ ui->copyToLocalBtn->setEnabled(true);
+ ui->moveToLocalBtn->setEnabled(true);
+ } else {
+ ui->copyToLocalBtn->setEnabled(false);
+ ui->moveToLocalBtn->setEnabled(false);
+ }
+}
+
+
+void TransferSavesDialog::refreshLocalCharacters()
+{
+ std::set<QString> characters;
+ for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
+ iter != m_LocalSaves.end(); ++iter) {
+ characters.insert((*iter)->pcName());
+ }
+ ui->localCharacterList->clear();
+ for (std::set<QString>::const_iterator iter = characters.begin();
+ iter != characters.end(); ++iter) {
+ ui->localCharacterList->addItem(*iter);
+ }
+ if (ui->localCharacterList->count() > 0) {
+ ui->localCharacterList->setCurrentRow(0);
+ ui->copyToGlobalBtn->setEnabled(true);
+ ui->moveToGlobalBtn->setEnabled(true);
+ } else {
+ ui->copyToGlobalBtn->setEnabled(false);
+ ui->moveToGlobalBtn->setEnabled(false);
+ }
+}
+
+
+bool TransferSavesDialog::testOverwrite(OverwriteMode &overwriteMode, const QString &destinationFile)
+{
+ QMessageBox::StandardButton res = overwriteMode == OVERWRITE_YES ? QMessageBox::Yes : QMessageBox::No;
+ if (overwriteMode == OVERWRITE_ASK) {
+ res = QMessageBox::question(this, tr("Overwrite"),
+ tr("Overwrite the file \"%1\"").arg(destinationFile),
+ QMessageBox::Yes | QMessageBox::No | QMessageBox::YesToAll | QMessageBox::NoToAll);
+ if (res == QMessageBox::YesToAll) {
+ overwriteMode = OVERWRITE_YES;
+ res = QMessageBox::Yes;
+ } else if (res == QMessageBox::NoToAll) {
+ overwriteMode = OVERWRITE_NO;
+ res = QMessageBox::No;
+ }
+ }
+ return res == QMessageBox::Yes;
+}
+
+QStringList TransferSavesDialog::getFilesToProcess(const SaveGame *save)
+{
+ QStringList result = save->attachedFiles();
+ result.append(save->fileName());
+ return result;
+}
+
+void TransferSavesDialog::on_moveToLocalBtn_clicked()
+{
+ QString selectedCharacter = ui->globalCharacterList->currentItem()->text();
+ if (QMessageBox::question(this, tr("Confirm"),
+ tr("Copy all save games of character \"%1\" to the profile?").arg(selectedCharacter),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ QString destination = m_Profile.getPath().append("/saves");
+ OverwriteMode overwriteMode = OVERWRITE_ASK;
+
+ for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
+ iter != m_GlobalSaves.end(); ++iter) {
+ if ((*iter)->pcName() == selectedCharacter) {
+ QStringList files = getFilesToProcess(*iter);
+ foreach (const QString &file, files) {
+ QFileInfo fileInfo(file);
+ QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
+ if (QFile::exists(destinationFile)) {
+ if (testOverwrite(overwriteMode, destinationFile)) {
+ QFile::remove(destinationFile);
+ } else {
+ continue;
+ }
+ }
+ if (!QFile::rename(fileInfo.absoluteFilePath(), destinationFile)) {
+ qCritical("failed to move %s to %s",
+ fileInfo.absoluteFilePath().toUtf8().constData(),
+ destinationFile.toUtf8().constData());
+ }
+ }
+ }
+ }
+ }
+ refreshGlobalSaves();
+ refreshGlobalCharacters();
+ refreshLocalSaves();
+ refreshLocalCharacters();
+}
+
+void TransferSavesDialog::on_copyToLocalBtn_clicked()
+{
+ QString selectedCharacter = ui->globalCharacterList->currentItem()->text();
+ if (QMessageBox::question(this, tr("Confirm"),
+ tr("Copy all save games of character \"%1\" to the profile?").arg(selectedCharacter),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ QString destination = m_Profile.getPath().append("/saves");
+ OverwriteMode overwriteMode = OVERWRITE_ASK;
+ for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
+ iter != m_GlobalSaves.end(); ++iter) {
+ if ((*iter)->pcName() == selectedCharacter) {
+ QStringList files = getFilesToProcess(*iter);
+ foreach (const QString &file, files) {
+ QFileInfo fileInfo(file);
+ QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
+ if (QFile::exists(destinationFile)) {
+ if (testOverwrite(overwriteMode, destinationFile)) {
+ QFile::remove(destinationFile);
+ } else {
+ continue;
+ }
+ }
+ if (!QFile::copy(fileInfo.absoluteFilePath(), destinationFile)) {
+ qCritical("failed to copy %s to %s",
+ fileInfo.absoluteFilePath().toUtf8().constData(),
+ destinationFile.toUtf8().constData());
+ }
+ }
+ }
+ }
+ }
+ refreshLocalSaves();
+ refreshLocalCharacters();
+}
+
+void TransferSavesDialog::on_moveToGlobalBtn_clicked()
+{
+ QString selectedCharacter = ui->localCharacterList->currentItem()->text();
+ if (QMessageBox::question(this, tr("Confirm"),
+ tr("Move all save games of character \"%1\" to the global location? Please be aware "
+ "that this will mess up the running number of save games.").arg(selectedCharacter),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+
+ QString destination = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir()));
+ OverwriteMode overwriteMode = OVERWRITE_ASK;
+ for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
+ iter != m_LocalSaves.end(); ++iter) {
+ if ((*iter)->pcName() == selectedCharacter) {
+ QStringList files = getFilesToProcess(*iter);
+ foreach (const QString &file, files) {
+ QFileInfo fileInfo(file);
+ QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
+ if (QFile::exists(destinationFile)) {
+ if (testOverwrite(overwriteMode, destinationFile)) {
+ QFile::remove(destinationFile);
+ } else {
+ continue;
+ }
+ }
+ if (!QFile::rename(fileInfo.absoluteFilePath(), destinationFile)) {
+ qCritical("failed to move %s to %s",
+ fileInfo.absoluteFilePath().toUtf8().constData(),
+ destinationFile.toUtf8().constData());
+ }
+ }
+ }
+ }
+ }
+ refreshGlobalSaves();
+ refreshGlobalCharacters();
+ refreshLocalSaves();
+ refreshLocalCharacters();
+}
+
+void TransferSavesDialog::on_copyToGlobalBtn_clicked()
+{
+ QString selectedCharacter = ui->localCharacterList->currentItem()->text();
+ if (QMessageBox::question(this, tr("Confirm"),
+ tr("Copy all save games of character \"%1\" to the global location? Please be aware "
+ "that this will mess up the running number of save games.").arg(selectedCharacter),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+
+ QString destination = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getSaveGameDir()));
+ OverwriteMode overwriteMode = OVERWRITE_ASK;
+ for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
+ iter != m_LocalSaves.end(); ++iter) {
+ if ((*iter)->pcName() == selectedCharacter) {
+ QStringList files = getFilesToProcess(*iter);
+ foreach (const QString &file, files) {
+ QFileInfo fileInfo(file);
+ QString destinationFile = destination.mid(0).append("/").append(fileInfo.fileName());
+ if (QFile::exists(destinationFile)) {
+ if (testOverwrite(overwriteMode, destinationFile)) {
+ QFile::remove(destinationFile);
+ } else {
+ continue;
+ }
+ }
+ if (!QFile::copy(fileInfo.absoluteFilePath(), destinationFile)) {
+ qCritical("failed to copy %s to %s",
+ fileInfo.absoluteFilePath().toUtf8().constData(),
+ destinationFile.toUtf8().constData());
+ }
+ }
+ }
+ }
+ }
+ refreshGlobalSaves();
+ refreshGlobalCharacters();
+}
+
+void TransferSavesDialog::on_doneButton_clicked()
+{
+ close();
+}
+
+void TransferSavesDialog::on_globalCharacterList_currentTextChanged(const QString &currentText)
+{
+ ui->globalSavesList->clear();
+ for (std::vector<SaveGame*>::const_iterator iter = m_GlobalSaves.begin();
+ iter != m_GlobalSaves.end(); ++iter) {
+ if ((*iter)->pcName() == currentText) {
+ ui->globalSavesList->addItem(QFileInfo((*iter)->fileName()).fileName());
+ }
+ }
+}
+
+void TransferSavesDialog::on_localCharacterList_currentTextChanged(const QString &currentText)
+{
+ ui->localSavesList->clear();
+ for (std::vector<SaveGame*>::const_iterator iter = m_LocalSaves.begin();
+ iter != m_LocalSaves.end(); ++iter) {
+ if ((*iter)->pcName() == currentText) {
+ ui->localSavesList->addItem(QFileInfo((*iter)->fileName()).fileName());
+ }
+ }
+}
diff --git a/src/transfersavesdialog.h b/src/transfersavesdialog.h
index e8d08a13..923259f6 100644
--- a/src/transfersavesdialog.h
+++ b/src/transfersavesdialog.h
@@ -17,67 +17,67 @@ 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 TRANSFERSAVESDIALOG_H
-#define TRANSFERSAVESDIALOG_H
-
-#include "tutorabledialog.h"
-#include "savegamegamebyro.h"
-#include "profile.h"
-
-namespace Ui {
-class TransferSavesDialog;
-}
-
-class TransferSavesDialog : public TutorableDialog
-{
- Q_OBJECT
-
-public:
- explicit TransferSavesDialog(const Profile &profile, QWidget *parent = 0);
- ~TransferSavesDialog();
-
-private slots:
-
- void on_moveToLocalBtn_clicked();
-
- void on_doneButton_clicked();
-
- void on_globalCharacterList_currentTextChanged(const QString &currentText);
-
- void on_localCharacterList_currentTextChanged(const QString &currentText);
-
- void on_copyToLocalBtn_clicked();
-
- void on_moveToGlobalBtn_clicked();
-
- void on_copyToGlobalBtn_clicked();
-
-private:
-
- enum OverwriteMode {
- OVERWRITE_ASK,
- OVERWRITE_YES,
- OVERWRITE_NO
- };
-
-private:
-
- void refreshGlobalCharacters();
- void refreshLocalCharacters();
- void refreshGlobalSaves();
- void refreshLocalSaves();
- bool testOverwrite(OverwriteMode &overwriteMode, const QString &destinationFile);
- QStringList getFilesToProcess(const SaveGame *save);
-
-private:
-
- Ui::TransferSavesDialog *ui;
-
- Profile m_Profile;
-
- std::vector<SaveGame*> m_GlobalSaves;
- std::vector<SaveGame*> m_LocalSaves;
-
-};
-
-#endif // TRANSFERSAVESDIALOG_H
+#ifndef TRANSFERSAVESDIALOG_H
+#define TRANSFERSAVESDIALOG_H
+
+#include "tutorabledialog.h"
+#include "savegamegamebyro.h"
+#include "profile.h"
+
+namespace Ui {
+class TransferSavesDialog;
+}
+
+class TransferSavesDialog : public MOBase::TutorableDialog
+{
+ Q_OBJECT
+
+public:
+ explicit TransferSavesDialog(const Profile &profile, QWidget *parent = 0);
+ ~TransferSavesDialog();
+
+private slots:
+
+ void on_moveToLocalBtn_clicked();
+
+ void on_doneButton_clicked();
+
+ void on_globalCharacterList_currentTextChanged(const QString &currentText);
+
+ void on_localCharacterList_currentTextChanged(const QString &currentText);
+
+ void on_copyToLocalBtn_clicked();
+
+ void on_moveToGlobalBtn_clicked();
+
+ void on_copyToGlobalBtn_clicked();
+
+private:
+
+ enum OverwriteMode {
+ OVERWRITE_ASK,
+ OVERWRITE_YES,
+ OVERWRITE_NO
+ };
+
+private:
+
+ void refreshGlobalCharacters();
+ void refreshLocalCharacters();
+ void refreshGlobalSaves();
+ void refreshLocalSaves();
+ bool testOverwrite(OverwriteMode &overwriteMode, const QString &destinationFile);
+ QStringList getFilesToProcess(const SaveGame *save);
+
+private:
+
+ Ui::TransferSavesDialog *ui;
+
+ Profile m_Profile;
+
+ std::vector<SaveGame*> m_GlobalSaves;
+ std::vector<SaveGame*> m_LocalSaves;
+
+};
+
+#endif // TRANSFERSAVESDIALOG_H