summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt20
-rw-r--r--src/categories.cpp4
-rw-r--r--src/editexecutablesdialog.cpp13
-rw-r--r--src/editexecutablesdialog.ui17
-rw-r--r--src/executableslist.cpp7
-rw-r--r--src/executableslist.h8
-rw-r--r--src/gameinfoimpl.cpp55
-rw-r--r--src/installationmanager.cpp6
-rw-r--r--src/iuserinterface.h4
-rw-r--r--src/loadmechanism.cpp7
-rw-r--r--src/logbuffer.cpp20
-rw-r--r--src/logbuffer.h4
-rw-r--r--src/main.cpp16
-rw-r--r--src/mainwindow.cpp255
-rw-r--r--src/mainwindow.h22
-rw-r--r--src/mainwindow.ui111
-rw-r--r--src/nexusinterface.cpp8
-rw-r--r--src/nxmaccessmanager.cpp5
-rw-r--r--src/organizercore.cpp189
-rw-r--r--src/organizercore.h21
-rw-r--r--src/organizerproxy.cpp5
-rw-r--r--src/organizerproxy.h2
-rw-r--r--src/plugincontainer.cpp8
-rw-r--r--src/plugincontainer.h4
-rw-r--r--src/profile.cpp4
-rw-r--r--src/profile.h2
-rw-r--r--src/savegame.h8
-rw-r--r--src/savegamegamebryo.cpp26
-rw-r--r--src/savegamegamebyro.h4
-rw-r--r--src/selfupdater.cpp3
-rw-r--r--src/selfupdater.h2
-rw-r--r--src/shared/appconfig.inc1
-rw-r--r--src/shared/directoryentry.cpp4
-rw-r--r--src/shared/directoryentry.h9
-rw-r--r--src/shared/fallout3info.cpp17
-rw-r--r--src/shared/fallout3info.h4
-rw-r--r--src/shared/fallout4info.cpp100
-rw-r--r--src/shared/fallout4info.h75
-rw-r--r--src/shared/falloutnvinfo.cpp17
-rw-r--r--src/shared/falloutnvinfo.h6
-rw-r--r--src/shared/gameinfo.cpp3
-rw-r--r--src/shared/gameinfo.h6
-rw-r--r--src/shared/oblivioninfo.cpp17
-rw-r--r--src/shared/oblivioninfo.h4
-rw-r--r--src/shared/skyriminfo.cpp23
-rw-r--r--src/shared/skyriminfo.h4
-rw-r--r--src/spawn.cpp49
-rw-r--r--src/spawn.h2
-rw-r--r--src/splash.pngbin230138 -> 273418 bytes
-rw-r--r--src/transfersavesdialog.cpp27
-rw-r--r--src/tutorials/tutorial_firststeps_main.js36
-rw-r--r--src/usvfsconnector.cpp151
-rw-r--r--src/usvfsconnector.h80
-rw-r--r--src/version.rc4
54 files changed, 749 insertions, 750 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3c786868..c55f9efe 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -87,6 +87,7 @@ SET(organizer_SRCS
viewmarkingscrollbar.cpp
plugincontainer.cpp
organizercore.cpp
+ usvfsconnector.cpp
shared/inject.cpp
shared/windows_error.cpp
@@ -95,6 +96,7 @@ SET(organizer_SRCS
shared/gameinfo.cpp
shared/oblivioninfo.cpp
shared/fallout3info.cpp
+ shared/fallout4info.cpp
shared/falloutnvinfo.cpp
shared/util.cpp
shared/skyriminfo.cpp
@@ -181,6 +183,7 @@ SET(organizer_HDRS
plugincontainer.h
organizercore.h
iuserinterface.h
+ usvfsconnector.h
shared/inject.h
shared/windows_error.h
@@ -189,6 +192,7 @@ SET(organizer_HDRS
shared/gameinfo.h
shared/oblivioninfo.h
shared/fallout3info.h
+ shared/fallout4info.h
shared/falloutnvinfo.h
shared/util.h
shared/skyriminfo.h
@@ -283,26 +287,36 @@ SET(project_path "${default_project_path}" CACHE PATH "path to the other mo proj
SET(lib_path "${project_path}/../../install/libs")
-MESSAGE(STATUS ${lib_path})
-MESSAGE(STATUS ${project_path})
-
INCLUDE_DIRECTORIES(${project_path}/uibase/src
${project_path}/bsatk/src
${project_path}/esptk/src
${project_path}/archive/src
+ ${project_path}/../usvfs/usvfs
+ ${project_path}/game_gamebryo/src
${project_path}/game_features/src)
+
+MESSAGE(STATUS ${project_path}/gameGamebryo/src)
+
+
INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS})
LINK_DIRECTORIES(${lib_path}
${project_path}/../zlib/lib)
ADD_DEFINITIONS(-D_UNICODE -DUNICODE -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
+IF("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
+ SET(usvfs_name usvfs_x64)
+ELSE()
+ SET(usvfs_name usvfs_x86)
+ENDIF()
+
ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS})
TARGET_LINK_LIBRARIES(ModOrganizer
Qt5::Widgets Qt5::WinExtras Qt5::WebKitWidgets
${Boost_LIBRARIES}
zlibstatic
uibase esptk bsatk
+ ${usvfs_name}
Dbghelp advapi32 Version Shlwapi)
SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS /GL)
diff --git a/src/categories.cpp b/src/categories.cpp
index 59291a49..7539d3fd 100644
--- a/src/categories.cpp
+++ b/src/categories.cpp
@@ -317,9 +317,11 @@ int CategoryFactory::getCategoryIndex(int ID) const
int CategoryFactory::getCategoryID(const QString &name) const
{
- auto iter = std::find_if(m_Categories.begin(), m_Categories.end(), [name] (const Category &cat) -> bool {
+ auto iter = std::find_if(m_Categories.begin(), m_Categories.end(),
+ [name] (const Category &cat) -> bool {
return cat.m_Name == name;
});
+
if (iter != m_Categories.end()) {
return iter->m_ID;
} else {
diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp
index b9d548a6..1b2d5a48 100644
--- a/src/editexecutablesdialog.cpp
+++ b/src/editexecutablesdialog.cpp
@@ -85,7 +85,6 @@ void EditExecutablesDialog::resetInput()
ui->argumentsEdit->setText("");
ui->appIDOverwriteEdit->clear();
ui->overwriteAppIDBox->setChecked(false);
- ui->closeCheckBox->setChecked(false);
ui->useAppIconCheckBox->setChecked(false);
m_CurrentItem = nullptr;
}
@@ -97,9 +96,6 @@ void EditExecutablesDialog::saveExecutable()
QDir::fromNativeSeparators(ui->binaryEdit->text()),
ui->argumentsEdit->text(),
QDir::fromNativeSeparators(ui->workingDirEdit->text()),
- (ui->closeCheckBox->checkState() == Qt::Checked) ?
- ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE
- : ExecutableInfo::CloseMOStyle::DEFAULT_STAY,
ui->overwriteAppIDBox->isChecked() ?
ui->appIDOverwriteEdit->text() : "",
Executable::UseApplicationIcon | Executable::CustomExecutable,
@@ -218,7 +214,6 @@ bool EditExecutablesDialog::executableChanged()
|| selectedExecutable.m_SteamAppID != ui->appIDOverwriteEdit->text()
|| selectedExecutable.m_WorkingDirectory != QDir::fromNativeSeparators(ui->workingDirEdit->text())
|| selectedExecutable.m_BinaryInfo.absoluteFilePath() != QDir::fromNativeSeparators(ui->binaryEdit->text())
- || (selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE) != ui->closeCheckBox->isChecked()
|| selectedExecutable.usesOwnIcon() != ui->useAppIconCheckBox->isChecked();
} else {
QFileInfo fileInfo(ui->binaryEdit->text());
@@ -291,14 +286,6 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur
ui->binaryEdit->setText(QDir::toNativeSeparators(selectedExecutable.m_BinaryInfo.absoluteFilePath()));
ui->argumentsEdit->setText(selectedExecutable.m_Arguments);
ui->workingDirEdit->setText(QDir::toNativeSeparators(selectedExecutable.m_WorkingDirectory));
- ui->closeCheckBox->setChecked(selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE);
- if (selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::NEVER_CLOSE) {
- ui->closeCheckBox->setEnabled(false);
- ui->closeCheckBox->setToolTip(tr("MO must be kept running or this application will not work correctly."));
- } else {
- ui->closeCheckBox->setEnabled(true);
- ui->closeCheckBox->setToolTip(tr("If checked, MO will be closed once the specified executable is run."));
- }
ui->removeButton->setEnabled(selectedExecutable.isCustom());
ui->overwriteAppIDBox->setChecked(!selectedExecutable.m_SteamAppID.isEmpty());
if (!selectedExecutable.m_SteamAppID.isEmpty()) {
diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui
index abb6fe68..a2aad19a 100644
--- a/src/editexecutablesdialog.ui
+++ b/src/editexecutablesdialog.ui
@@ -165,19 +165,6 @@ Right now the only case I know of where this needs to be overwritten is for the
</layout>
</item>
<item>
- <widget class="QCheckBox" name="closeCheckBox">
- <property name="toolTip">
- <string>If checked, MO will be closed once the specified executable is run.</string>
- </property>
- <property name="whatsThis">
- <string>If checked, MO will be closed once the specified executable is run.</string>
- </property>
- <property name="text">
- <string>Close MO when started</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QCheckBox" name="useAppIconCheckBox">
<property name="text">
<string>Use Application's Icon for shortcuts</string>
@@ -247,10 +234,6 @@ Right now the only case I know of where this needs to be overwritten is for the
</layout>
</item>
</layout>
- <zorder>executablesListBox</zorder>
- <zorder>closeButton</zorder>
- <zorder>closeCheckBox</zorder>
- <zorder>useAppIconCheckBox</zorder>
</widget>
<resources/>
<connections/>
diff --git a/src/executableslist.cpp b/src/executableslist.cpp
index a4511ade..9fe4596c 100644
--- a/src/executableslist.cpp
+++ b/src/executableslist.cpp
@@ -50,7 +50,6 @@ void ExecutablesList::init(IPluginGame const *game)
info.binary().absoluteFilePath(),
info.arguments().join(" "),
info.workingDirectory().absolutePath(),
- info.closeMO(),
info.steamAppID());
}
}
@@ -135,7 +134,6 @@ void ExecutablesList::updateExecutable(const QString &title,
const QString &executableName,
const QString &arguments,
const QString &workingDirectory,
- ExecutableInfo::CloseMOStyle closeMO,
const QString &steamAppID,
Executable::Flags mask,
Executable::Flags flags)
@@ -146,7 +144,6 @@ void ExecutablesList::updateExecutable(const QString &title,
if (existingExe != m_Executables.end()) {
existingExe->m_Title = title;
- existingExe->m_CloseMO = closeMO;
existingExe->m_Flags &= ~mask;
existingExe->m_Flags |= flags;
// for pre-configured executables don't overwrite settings we didn't store
@@ -162,7 +159,6 @@ void ExecutablesList::updateExecutable(const QString &title,
} else {
Executable newExe;
newExe.m_Title = title;
- newExe.m_CloseMO = closeMO;
newExe.m_BinaryInfo = file;
newExe.m_Arguments = arguments;
newExe.m_WorkingDirectory = workingDirectory;
@@ -186,12 +182,11 @@ void ExecutablesList::remove(const QString &title)
void ExecutablesList::addExecutableInternal(const QString &title, const QString &executableName,
const QString &arguments, const QString &workingDirectory,
- ExecutableInfo::CloseMOStyle closeMO, const QString &steamAppID)
+ const QString &steamAppID)
{
QFileInfo file(executableName);
if (file.exists()) {
Executable newExe;
- newExe.m_CloseMO = closeMO;
newExe.m_BinaryInfo = file;
newExe.m_Title = title;
newExe.m_Arguments = arguments;
diff --git a/src/executableslist.h b/src/executableslist.h
index 3d5ba0ed..eb7d2d6f 100644
--- a/src/executableslist.h
+++ b/src/executableslist.h
@@ -36,7 +36,6 @@ struct Executable {
QString m_Title;
QFileInfo m_BinaryInfo;
QString m_Arguments;
- MOBase::ExecutableInfo::CloseMOStyle m_CloseMO;
QString m_SteamAppID;
QString m_WorkingDirectory;
@@ -126,17 +125,15 @@ public:
* @param title name displayed in the UI
* @param executableName the actual filename to execute
* @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,
- MOBase::ExecutableInfo::CloseMOStyle closeMO,
const QString &steamAppID,
Executable::Flags flags)
{
- updateExecutable(title, executableName, arguments, workingDirectory, closeMO, steamAppID, Executable::AllFlags, flags);
+ updateExecutable(title, executableName, arguments, workingDirectory, steamAppID, Executable::AllFlags, flags);
}
/**
@@ -151,7 +148,6 @@ public:
const QString &executableName,
const QString &arguments,
const QString &workingDirectory,
- MOBase::ExecutableInfo::CloseMOStyle closeMO,
const QString &steamAppID,
Executable::Flags mask,
Executable::Flags flags);
@@ -192,7 +188,7 @@ private:
std::vector<Executable>::iterator findExe(const QString &title);
void addExecutableInternal(const QString &title, const QString &executableName, const QString &arguments,
- const QString &workingDirectory, MOBase::ExecutableInfo::CloseMOStyle closeMO,
+ const QString &workingDirectory,
const QString &steamAppID);
private:
diff --git a/src/gameinfoimpl.cpp b/src/gameinfoimpl.cpp
new file mode 100644
index 00000000..333fd029
--- /dev/null
+++ b/src/gameinfoimpl.cpp
@@ -0,0 +1,55 @@
+/*
+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 "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_FALLOUT4: return IGameInfo::TYPE_FALLOUT4;
+ 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/installationmanager.cpp b/src/installationmanager.cpp
index 7b1f3f37..739948cd 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -47,6 +47,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QApplication>
#include <QDateTime>
#include <QDirIterator>
+#include <QDebug>
#include <Shellapi.h>
@@ -703,7 +704,10 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
bool archiveOpen = m_ArchiveHandler->open(fileName,
new MethodCallback<InstallationManager, void, QString *>(this, &InstallationManager::queryPassword));
if (!archiveOpen) {
- qDebug("integrated archiver can't open %s. errorcode %d", qPrintable(fileName), m_ArchiveHandler->getLastError());
+ qDebug("integrated archiver can't open %s: %s (%d)",
+ qPrintable(fileName),
+ qPrintable(getErrorString(m_ArchiveHandler->getLastError())),
+ m_ArchiveHandler->getLastError());
}
ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this));
diff --git a/src/iuserinterface.h b/src/iuserinterface.h
index bcf800b7..e6b39ee1 100644
--- a/src/iuserinterface.h
+++ b/src/iuserinterface.h
@@ -26,10 +26,6 @@ public:
virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0;
- virtual void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) = 0;
-
- virtual MOBase::DelayedFileWriterBase &archivesWriter() = 0;
-
virtual void lock() = 0;
virtual void unlock() = 0;
virtual bool unlockClicked() = 0;
diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp
index 47587467..8ca8464e 100644
--- a/src/loadmechanism.cpp
+++ b/src/loadmechanism.cpp
@@ -131,6 +131,9 @@ void LoadMechanism::deactivateScriptExtender()
QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->name() + "/plugins");
+#pragma "implement this for usvfs"
+
+ /*
QString hookDLLName = ToQString(AppConfig::hookDLLName());
if (QFile(pluginsDir.absoluteFilePath(hookDLLName)).exists()) {
// remove dll from SE plugins directory
@@ -140,6 +143,7 @@ void LoadMechanism::deactivateScriptExtender()
}
removeHintFile(pluginsDir);
+ */
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate script extender loading"), e.what());
}
@@ -190,6 +194,8 @@ void LoadMechanism::activateScriptExtender()
pluginsDir.mkpath(".");
}
+#pragma message("implement this for usvfs")
+/*
QString targetPath = pluginsDir.absoluteFilePath(ToQString(AppConfig::hookDLLName()));
QString hookDLLPath = qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::hookDLLName());
@@ -209,6 +215,7 @@ void LoadMechanism::activateScriptExtender()
}
}
writeHintFile(pluginsDir);
+*/
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to set up script extender loading"), e.what());
}
diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp
index a07ae264..2c3fc101 100644
--- a/src/logbuffer.cpp
+++ b/src/logbuffer.cpp
@@ -124,8 +124,6 @@ char LogBuffer::msgTypeID(QtMsgType type)
}
}
-#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
-
void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
// QMutexLocker doesn't support timeout...
@@ -141,6 +139,7 @@ void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QSt
s_Instance->logMessage(type, message);
}
// fprintf(stdout, "(%s:%u) %s\n", context.file, context.line, qPrintable(message));
+
if (type == QtDebugMsg) {
fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), qPrintable(message));
} else {
@@ -154,21 +153,6 @@ void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QSt
fflush(stdout);
}
-#else
-
-void LogBuffer::log(QtMsgType type, const char *message)
-{
- QMutexLocker guard(&s_Mutex);
- if (!s_Instance.isNull()) {
- s_Instance->logMessage(type, message);
- }
- fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), message);
- fflush(stdout);
-}
-
-#endif
-
-
QModelIndex LogBuffer::index(int row, int column, const QModelIndex&) const
{
return createIndex(row, column, row);
@@ -269,8 +253,6 @@ void log(const char *format, ...)
va_end(argList);
}
-
-
QString LogBuffer::Message::toString() const
{
return QString("%1 [%2] %3").arg(time.toString()).arg(msgTypeID(type)).arg(message);
diff --git a/src/logbuffer.h b/src/logbuffer.h
index 748aaf31..0cfecfa2 100644
--- a/src/logbuffer.h
+++ b/src/logbuffer.h
@@ -35,11 +35,7 @@ class LogBuffer : public QAbstractItemModel
public:
static void init(int messageCount, QtMsgType minMsgType, const QString &outputFileName);
-#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
static void log(QtMsgType type, const QMessageLogContext &context, const QString &message);
-#else
- static void log(QtMsgType type, const char *message);
-#endif
static void writeNow();
static void cleanQuit();
diff --git a/src/main.cpp b/src/main.cpp
index 3469e9c3..30938485 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -124,7 +124,7 @@ bool bootstrap()
// cycle logfile
removeOldFiles(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()),
- "ModOrganizer*.log", 5, QDir::Name);
+ "usvfs*.log", 5, QDir::Name);
createAndMakeWritable(AppConfig::profilesPath());
createAndMakeWritable(AppConfig::modsPath());
@@ -132,19 +132,6 @@ bool bootstrap()
createAndMakeWritable(AppConfig::overwritePath());
createAndMakeWritable(AppConfig::logPath());
- // verify the hook-dll exists
- QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName());
-
- if (::GetModuleHandleW(ToWString(dllName).c_str()) != nullptr) {
- throw std::runtime_error("hook.dll already loaded! You can't start Mod Organizer from within itself (not even indirectly)");
- }
-
- HMODULE dllMod = ::LoadLibraryW(ToWString(dllName).c_str());
- if (dllMod == nullptr) {
- throw windows_error("hook.dll is missing or invalid");
- }
- ::FreeLibrary(dllMod);
-
return true;
}
@@ -588,7 +575,6 @@ int main(int argc, char *argv[])
}
game->setGameVariant(settings.value("game_edition").toString());
-
#pragma message("edition isn't used?")
qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(game->gameDirectory().absolutePath())));
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f30e46f1..1a7d1564 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -167,7 +167,6 @@ MainWindow::MainWindow(const QString &exeName
, m_OrganizerCore(organizerCore)
, m_PluginContainer(pluginContainer)
, m_DidUpdateMasterList(false)
- , m_ArchiveListWriter(std::bind(&MainWindow::saveArchiveList, this))
{
ui->setupUi(this);
updateWindowTitle(QString(), false);
@@ -192,6 +191,7 @@ MainWindow::MainWindow(const QString &exeName
m_RefreshProgress->setVisible(false);
statusBar()->addWidget(m_RefreshProgress, 1000);
statusBar()->clearMessage();
+ statusBar()->hide();
ui->actionEndorseMO->setVisible(false);
@@ -239,8 +239,6 @@ MainWindow::MainWindow(const QString &exeName
}
ui->espList->installEventFilter(m_OrganizerCore.pluginList());
- ui->bsaList->setLocalMoveOnly(true);
-
ui->splitter->setStretchFactor(0, 3);
ui->splitter->setStretchFactor(1, 2);
@@ -270,8 +268,6 @@ MainWindow::MainWindow(const QString &exeName
connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString)));
connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString)));
- connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved()));
-
connect(ui->dataTree, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(expandDataTreeItem(QTreeWidgetItem*)));
connect(m_OrganizerCore.directoryRefresher(), SIGNAL(refreshed()), this, SLOT(directory_refreshed()));
@@ -310,9 +306,6 @@ MainWindow::MainWindow(const QString &exeName
connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString)));
- m_CheckBSATimer.setSingleShot(true);
- connect(&m_CheckBSATimer, SIGNAL(timeout()), this, SLOT(checkBSAList()));
-
m_UpdateProblemsTimer.setSingleShot(true);
connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton()));
@@ -367,9 +360,7 @@ void MainWindow::updateWindowTitle(const QString &accountName, bool premium)
m_OrganizerCore.managedGame()->gameName(),
m_OrganizerCore.getVersion().displayString());
- if (accountName.isEmpty()) {
- title.append(" (not logged in)");
- } else {
+ if (!accountName.isEmpty()) {
title.append(QString(" (%1%2)").arg(accountName, premium ? "*" : ""));
}
@@ -592,9 +583,9 @@ bool MainWindow::errorReported(QString &logFile)
}
-int MainWindow::checkForProblems()
+size_t MainWindow::checkForProblems()
{
- int numProblems = 0;
+ size_t numProblems = 0;
for (IPluginDiagnose *diagnose : m_PluginContainer.plugins<IPluginDiagnose>()) {
numProblems += diagnose->activeProblems().size();
}
@@ -989,7 +980,6 @@ void MainWindow::startExeAction()
selectedExecutable.m_Arguments,
selectedExecutable.m_WorkingDirectory.length() != 0 ? selectedExecutable.m_WorkingDirectory
: selectedExecutable.m_BinaryInfo.absolutePath(),
- selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE,
selectedExecutable.m_SteamAppID);
} else {
qCritical("not an action?");
@@ -1328,163 +1318,6 @@ static QStringList toStringList(InputIterator current, InputIterator end)
return result;
}
-void MainWindow::updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives)
-{
- m_DefaultArchives = defaultArchives;
- ui->bsaList->clear();
-#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
- ui->bsaList->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
-#else
- ui->bsaList->header()->setResizeMode(QHeaderView::ResizeToContents);
-#endif
-
- std::vector<std::pair<UINT32, QTreeWidgetItem*>> items;
-
- BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
- std::vector<FileEntry::Ptr> files = m_OrganizerCore.directoryStructure()->getFiles();
-
- QStringList plugins = m_OrganizerCore.findFiles("", [] (const QString &fileName) -> bool {
- return fileName.endsWith(".esp", Qt::CaseInsensitive)
- || fileName.endsWith(".esm", Qt::CaseInsensitive);
- });
-
- auto hasAssociatedPlugin = [&](const QString &bsaName) -> bool {
- for (const QString &pluginName : plugins) {
- QFileInfo pluginInfo(pluginName);
- if (bsaName.startsWith(QFileInfo(pluginName).baseName(), Qt::CaseInsensitive)
- && (m_OrganizerCore.pluginList()->state(pluginInfo.fileName()) == IPluginList::STATE_ACTIVE)) {
- return true;
- }
- }
- return false;
- };
-
- for (FileEntry::Ptr current : files) {
- QFileInfo fileInfo(ToQString(current->getName().c_str()));
-
- if (fileInfo.suffix().toLower() == "bsa") {
- int index = activeArchives.indexOf(fileInfo.fileName());
- if (index == -1) {
- index = 0xFFFF;
- } else {
- index += 2;
- }
-
- if ((invalidation != nullptr) && invalidation->isInvalidationBSA(fileInfo.fileName())) {
- index = 1;
- }
-
- int originId = current->getOrigin();
- FilesOrigin &origin = m_OrganizerCore.directoryStructure()->getOriginByID(originId);
-
- QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList()
- << fileInfo.fileName()
- << ToQString(origin.getName()));
- newItem->setData(0, Qt::UserRole, index);
- newItem->setData(1, Qt::UserRole, originId);
- newItem->setFlags(newItem->flags() & ~Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable);
- newItem->setCheckState(0, (index != -1) ? Qt::Checked : Qt::Unchecked);
- newItem->setData(0, Qt::UserRole, false);
- if (m_OrganizerCore.settings().forceEnableCoreFiles()
- && defaultArchives.contains(fileInfo.fileName())) {
- newItem->setCheckState(0, Qt::Checked);
- newItem->setDisabled(true);
- newItem->setData(0, Qt::UserRole, true);
- } else if (fileInfo.fileName().compare("update.bsa", Qt::CaseInsensitive) == 0) {
- newItem->setCheckState(0, Qt::Checked);
- newItem->setDisabled(true);
- } else if (hasAssociatedPlugin(fileInfo.fileName())) {
- newItem->setCheckState(0, Qt::Checked);
- newItem->setDisabled(true);
- } else {
- if (ui->manageArchivesBox->isChecked()) {
- newItem->setCheckState(0, (index != 0xFFFF) ? Qt::Checked : Qt::Unchecked);
- } else {
- newItem->setCheckState(0, Qt::Unchecked);
- newItem->setDisabled(true);
- }
- }
-
- if (index < 0) index = 0;
-
- UINT32 sortValue = ((origin.getPriority() & 0xFFFF) << 16) | (index & 0xFFFF);
- items.push_back(std::make_pair(sortValue, newItem));
- }
- }
-
- std::sort(items.begin(), items.end(), BySortValue);
-
- for (auto iter = items.begin(); iter != items.end(); ++iter) {
- int originID = iter->second->data(1, Qt::UserRole).toInt();
-
- FilesOrigin origin = m_OrganizerCore.directoryStructure()->getOriginByID(originID);
- QString modName("data");
- unsigned int modIndex = ModInfo::getIndex(ToQString(origin.getName()));
- if (modIndex != UINT_MAX) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- modName = modInfo->name();
- }
- QList<QTreeWidgetItem*> items = ui->bsaList->findItems(modName, Qt::MatchFixedString);
- QTreeWidgetItem *subItem = nullptr;
- if (items.length() > 0) {
- subItem = items.at(0);
- } else {
- subItem = new QTreeWidgetItem(QStringList(modName));
- subItem->setFlags(subItem->flags() & ~Qt::ItemIsDragEnabled);
- ui->bsaList->addTopLevelItem(subItem);
- }
- subItem->addChild(iter->second);
- subItem->setExpanded(true);
- }
-
- checkBSAList();
-}
-
-
-void MainWindow::checkBSAList()
-{
- DataArchives *archives = m_OrganizerCore.managedGame()->feature<DataArchives>();
-
- if (archives != nullptr) {
- ui->bsaList->blockSignals(true);
- ON_BLOCK_EXIT([&] () { ui->bsaList->blockSignals(false); });
-
- QStringList defaultArchives = archives->archives(m_OrganizerCore.currentProfile());
-
- bool warning = false;
-
- for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) {
- bool modWarning = false;
- QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i);
- for (int j = 0; j < tlItem->childCount(); ++j) {
- QTreeWidgetItem *item = tlItem->child(j);
- QString filename = item->text(0);
- item->setIcon(0, QIcon());
- item->setToolTip(0, QString());
-
- if (item->checkState(0) == Qt::Unchecked) {
- if (defaultArchives.contains(filename)) {
- item->setIcon(0, QIcon(":/MO/gui/warning"));
- item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!"));
- modWarning = true;
- }
- }
- }
- if (modWarning) {
- ui->bsaList->expandItem(ui->bsaList->topLevelItem(i));
- warning = true;
- }
- }
-
- if (warning) {
- ui->tabWidget->setTabIcon(1, QIcon(":/MO/gui/warning"));
- } else {
- ui->tabWidget->setTabIcon(1, QIcon());
- }
- }
-}
-
-
void MainWindow::saveModMetas()
{
for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) {
@@ -1493,7 +1326,6 @@ void MainWindow::saveModMetas()
}
}
-
void MainWindow::fixCategories()
{
for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) {
@@ -1564,10 +1396,6 @@ void MainWindow::readSettings()
if (settings.value("Settings/use_proxy", false).toBool()) {
activateProxy(true);
}
-
- ui->manageArchivesBox->blockSignals(true);
- ui->manageArchivesBox->setChecked(settings.value("manage_bsas", true).toBool());
- ui->manageArchivesBox->blockSignals(false);
}
@@ -1585,7 +1413,6 @@ void MainWindow::storeSettings(QSettings &settings)
settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry());
settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
- settings.setValue("manage_bsas", ui->manageArchivesBox->isChecked());
settings.setValue("selected_executable", ui->executablesListBox->currentIndex());
}
@@ -1669,7 +1496,6 @@ void MainWindow::on_startButton_clicked()
selectedExecutable.m_Arguments,
selectedExecutable.m_WorkingDirectory.length() != 0 ? selectedExecutable.m_WorkingDirectory
: selectedExecutable.m_BinaryInfo.absolutePath(),
- selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE,
selectedExecutable.m_SteamAppID);
}
@@ -1880,8 +1706,10 @@ void MainWindow::setESPListSorting(int index)
void MainWindow::refresher_progress(int percent)
{
if (percent == 100) {
-// m_RefreshProgress->setVisible(false);
+ m_RefreshProgress->setVisible(false);
+ statusBar()->hide();
} else if (!m_RefreshProgress->isVisible()) {
+ statusBar()->show();
m_RefreshProgress->setVisible(true);
m_RefreshProgress->setRange(0, 100);
m_RefreshProgress->setValue(percent);
@@ -1909,7 +1737,6 @@ void MainWindow::modorder_changed()
}
m_OrganizerCore.refreshBSAList();
m_OrganizerCore.currentProfile()->modlistWriter().write();
- m_ArchiveListWriter.write();
m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins();
{ // refresh selection
@@ -2828,30 +2655,6 @@ void MainWindow::savePrimaryCategory()
}
}
-void MainWindow::saveArchiveList()
-{
- if (m_OrganizerCore.isArchivesInit()) {
- SafeWriteFile archiveFile(m_OrganizerCore.currentProfile()->getArchivesFileName());
- for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) {
- QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i);
- for (int j = 0; j < tlItem->childCount(); ++j) {
- QTreeWidgetItem *item = tlItem->child(j);
- if (item->checkState(0) == Qt::Checked) {
- // in managed mode, "register" all enabled archives, otherwise register only the files registered in the ini
- if (ui->manageArchivesBox->isChecked()
- || item->data(0, Qt::UserRole).toBool()) {
- archiveFile->write(item->text(0).toUtf8().append("\r\n"));
- }
- }
- }
- }
- if (archiveFile.commitIfDifferent(m_ArchiveListHash)) {
- qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_OrganizerCore.currentProfile()->getArchivesFileName())));
- }
- } else {
- qWarning("archive list not initialised");
- }
-}
void MainWindow::checkModsForUpdates()
{
@@ -3618,7 +3421,6 @@ void MainWindow::addAsExecutable()
binaryInfo.absoluteFilePath(),
arguments,
targetInfo.absolutePath(),
- ExecutableInfo::CloseMOStyle::DEFAULT_STAY,
QString(),
Executable::CustomExecutable);
refreshExecutablesList();
@@ -3888,8 +3690,7 @@ void MainWindow::updateDownloadListDelegate()
void MainWindow::modDetailsUpdated(bool)
{
- --m_ModsToUpdate;
- if (m_ModsToUpdate == 0) {
+ if (--m_ModsToUpdate == 0) {
statusBar()->hide();
m_ModListSortProxy->setCategoryFilter(boost::assign::list_of(CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE));
for (int i = 0; i < ui->categoriesList->topLevelItemCount(); ++i) {
@@ -4095,32 +3896,6 @@ void MainWindow::displayColumnSelection(const QPoint &pos)
}
}
-
-void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos)
-{
- m_ContextItem = ui->bsaList->itemAt(pos);
-
-// m_ContextRow = ui->bsaList->indexOfTopLevelItem(ui->bsaList->itemAt(pos));
-
- QMenu menu;
- menu.addAction(tr("Extract..."), this, SLOT(extractBSATriggered()));
-
- menu.exec(ui->bsaList->mapToGlobal(pos));
-}
-
-void MainWindow::bsaList_itemMoved()
-{
- m_ArchiveListWriter.write();
- m_CheckBSATimer.start(500);
-}
-
-
-void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int)
-{
- m_ArchiveListWriter.write();
- m_CheckBSATimer.start(500);
-}
-
void MainWindow::on_actionProblems_triggered()
{
ProblemsDialog problems(m_PluginContainer.plugins<IPluginDiagnose>(), this);
@@ -4438,10 +4213,9 @@ void MainWindow::on_bossButton_clicked()
HANDLE stdOutWrite = INVALID_HANDLE_VALUE;
HANDLE stdOutRead = INVALID_HANDLE_VALUE;
createStdoutPipe(&stdOutRead, &stdOutWrite);
+ m_OrganizerCore.prepareVFS();
HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"),
parameters.join(" "),
- m_OrganizerCore.currentProfile()->name(),
- m_OrganizerCore.settings().logLevel(),
qApp->applicationDirPath() + "/loot",
true,
stdOutWrite);
@@ -4696,17 +4470,6 @@ void MainWindow::on_categoriesOrBtn_toggled(bool checked)
}
}
-void MainWindow::on_managedArchiveLabel_linkHovered(const QString&)
-{
- QToolTip::showText(QCursor::pos(),
- ui->managedArchiveLabel->toolTip());
-}
-
-void MainWindow::on_manageArchivesBox_toggled(bool)
-{
- m_OrganizerCore.refreshBSAList();
-}
-
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
//Accept copy or move drags to the download window. Link drags are not
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 0cdea807..49476bf3 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -73,8 +73,8 @@ class MainWindow : public QMainWindow, public IUserInterface
friend class OrganizerProxy;
-
public:
+
explicit MainWindow(const QString &exeName, QSettings &initSettings,
OrganizerCore &organizerCore, PluginContainer &pluginContainer,
QWidget *parent = 0);
@@ -88,15 +88,12 @@ public:
virtual bool unlockClicked() override;
bool addProfile();
- void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives);
void refreshDataTree();
void refreshSaveList();
void setModListSorting(int index);
void setESPListSorting(int index);
- void saveArchiveList();
-
void registerPluginTool(MOBase::IPluginTool *tool);
void registerModPage(MOBase::IPluginModPage *modPage);
@@ -119,9 +116,8 @@ public:
virtual bool closeWindow();
virtual void setWindowEnabled(bool enabled);
- virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; }
-
void updateWindowTitle(const QString &accountName, bool premium);
+
public slots:
void displayColumnSelection(const QPoint &pos);
@@ -135,7 +131,6 @@ public slots:
signals:
-
/**
* @brief emitted after the information dialog has been closed
*/
@@ -214,7 +209,7 @@ private:
bool extractProgress(QProgressDialog &extractProgress, int percentage, std::string fileName);
- int checkForProblems();
+ size_t checkForProblems();
int getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments);
QTreeWidgetItem *addFilterItem(QTreeWidgetItem *root, const QString &name, int categoryID, ModListSortProxy::FilterType type);
@@ -315,14 +310,10 @@ private:
std::vector<QTreeWidgetItem*> m_RemoveWidget;
- QByteArray m_ArchiveListHash;
-
bool m_DidUpdateMasterList;
LockedDialog *m_LockDialog { nullptr };
- MOBase::DelayedFileWriter m_ArchiveListWriter;
-
enum class ShortcutType {
Toolbar,
Desktop,
@@ -444,8 +435,6 @@ private slots:
void startExeAction();
- void checkBSAList();
-
void updateProblemsButton();
void saveModMetas();
@@ -499,8 +488,6 @@ private slots: // ui slots
void on_actionUpdate_triggered();
void on_actionEndorseMO_triggered();
- void on_bsaList_customContextMenuRequested(const QPoint &pos);
- void bsaList_itemMoved();
void on_btnRefreshData_clicked();
void on_categoriesList_customContextMenuRequested(const QPoint &pos);
void on_conflictsCheckBox_toggled(bool checked);
@@ -519,7 +506,6 @@ private slots: // ui slots
void on_categoriesList_itemSelectionChanged();
void on_linkButton_pressed();
void on_showHiddenBox_toggled(bool checked);
- void on_bsaList_itemChanged(QTreeWidgetItem *item, int column);
void on_bossButton_clicked();
void on_saveButton_clicked();
@@ -529,8 +515,6 @@ private slots: // ui slots
void on_actionCopy_Log_to_Clipboard_triggered();
void on_categoriesAndBtn_toggled(bool checked);
void on_categoriesOrBtn_toggled(bool checked);
- void on_managedArchiveLabel_linkHovered(const QString &link);
- void on_manageArchivesBox_toggled(bool checked);
};
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index ca93ea54..02319bee 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -884,112 +884,6 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
- <widget class="QWidget" name="bsaTab">
- <attribute name="title">
- <string>Archives</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_9">
- <property name="leftMargin">
- <number>6</number>
- </property>
- <property name="topMargin">
- <number>6</number>
- </property>
- <property name="rightMargin">
- <number>6</number>
- </property>
- <property name="bottomMargin">
- <number>6</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_9" stretch="0,1">
- <item>
- <widget class="QCheckBox" name="manageArchivesBox">
- <property name="text">
- <string/>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="managedArchiveLabel">
- <property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;BSAs are bundles of game assets (textures, scripts, ...). By default, the engine loads these bundles in a separate step from loose files. MO can manage those archives to align their load order with that of loose files:&lt;/p&gt;&lt;p&gt;If archives are &lt;span style=&quot; font-weight:600;&quot;&gt;managed&lt;/span&gt;, their load order is specified by the priority of the corresponding mod (left pane), the same as the loose files. You can manually enable any BSA that has no corresponding plugin active.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If archives are &lt;span style=&quot; font-weight:600;&quot;&gt;not managed&lt;/span&gt; their load order is specified by the priority of the corresponding plugin (right pane, plugins tab). You can then not manually enable BSAs where the plugin isn't active.&lt;/p&gt;&lt;p&gt;In either case you can not disable archives if there is a matching plugin, the game will load them no matter what.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Have MO manage archives (&lt;a href=&quot;#&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;read more&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="MOBase::SortableTreeWidget" name="bsaList">
- <property name="contextMenuPolicy">
- <enum>Qt::CustomContextMenu</enum>
- </property>
- <property name="toolTip">
- <string>List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order.</string>
- </property>
- <property name="whatsThis">
- <string>BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they &quot;compete&quot; with loose files in your data directory over which is loaded.
-By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored!
-
-BSAs checked here are loaded in such a way that your installation order is obeyed properly.</string>
- </property>
- <property name="editTriggers">
- <set>QAbstractItemView::NoEditTriggers</set>
- </property>
- <property name="showDropIndicator" stdset="0">
- <bool>true</bool>
- </property>
- <property name="dragEnabled">
- <bool>false</bool>
- </property>
- <property name="dragDropOverwriteMode">
- <bool>false</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::DragDrop</enum>
- </property>
- <property name="defaultDropAction">
- <enum>Qt::MoveAction</enum>
- </property>
- <property name="selectionMode">
- <enum>QAbstractItemView::SingleSelection</enum>
- </property>
- <property name="selectionBehavior">
- <enum>QAbstractItemView::SelectRows</enum>
- </property>
- <property name="indentation">
- <number>20</number>
- </property>
- <property name="itemsExpandable">
- <bool>true</bool>
- </property>
- <property name="columnCount">
- <number>1</number>
- </property>
- <attribute name="headerVisible">
- <bool>false</bool>
- </attribute>
- <attribute name="headerDefaultSectionSize">
- <number>200</number>
- </attribute>
- <column>
- <property name="text">
- <string>File</string>
- </property>
- </column>
- </widget>
- </item>
- </layout>
- </widget>
<widget class="QWidget" name="dataTab">
<attribute name="title">
<string>Data</string>
@@ -1487,11 +1381,6 @@ Right now this has very limited functionality</string>
<extends>QTreeView</extends>
<header>modlistview.h</header>
</customwidget>
- <customwidget>
- <class>MOBase::SortableTreeWidget</class>
- <extends>QTreeWidget</extends>
- <header>sortabletreewidget.h</header>
- </customwidget>
</customwidgets>
<resources>
<include location="resources.qrc"/>
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp
index 26a74ef6..79749523 100644
--- a/src/nexusinterface.cpp
+++ b/src/nexusinterface.cpp
@@ -86,7 +86,7 @@ void NexusBridge::nxmFilesAvailable(int modID, QVariant userData, QVariant resul
QVariantList resultList = resultData.toList();
- foreach(QVariant file, resultList) {
+ for (const QVariant &file : resultList) {
ModRepositoryFileInfo temp;
QVariantMap fileInfo = file.toMap();
temp.uri = fileInfo["uri"].toString();
@@ -564,8 +564,10 @@ void NexusInterface::requestError(QNetworkReply::NetworkError)
return;
}
- qCritical("request (%s) error: %s",
- qPrintable(reply->url().toString()), qPrintable(reply->errorString()));
+ qCritical("request (%s) error: %s (%d)",
+ qPrintable(reply->url().toString()),
+ qPrintable(reply->errorString()),
+ reply->error());
}
diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp
index 616b3806..a7dd6eef 100644
--- a/src/nxmaccessmanager.cpp
+++ b/src/nxmaccessmanager.cpp
@@ -59,6 +59,11 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion)
m_LoginTimeout.setInterval(30000);
setCookieJar(new PersistentCookieJar(
QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat")));
+
+ if (networkAccessible() == QNetworkAccessManager::UnknownAccessibility) {
+ // why is this necessary all of a sudden?
+ setNetworkAccessible(QNetworkAccessManager::Accessible);
+ }
}
NXMAccessManager::~NXMAccessManager()
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 06a6dba4..6a8e148f 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -14,6 +14,7 @@
#include "nxmaccessmanager.h"
#include <ipluginmodpage.h>
#include <dataarchives.h>
+#include <localsavegames.h>
#include <directoryentry.h>
#include <scopeguard.h>
#include <utility.h>
@@ -235,7 +236,6 @@ QSettings::Status OrganizerCore::storeSettings(const QString &fileName)
settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath());
settings.setValue("arguments", item.m_Arguments);
settings.setValue("workingDirectory", item.m_WorkingDirectory);
- settings.setValue("closeOnStart", item.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE);
settings.setValue("steamAppID", item.m_SteamAppID);
}
}
@@ -285,7 +285,9 @@ bool OrganizerCore::testForSteam()
bool success = false;
while (!success) {
processIDs.reset(new DWORD[currentSize]);
- if (!::EnumProcesses(processIDs.get(), currentSize * sizeof(DWORD), &bytesReturned)) {
+ if (!::EnumProcesses(processIDs.get(),
+ static_cast<DWORD>(currentSize) * sizeof(DWORD),
+ &bytesReturned)) {
qWarning("failed to determine if steam is running");
return true;
}
@@ -335,9 +337,6 @@ void OrganizerCore::updateExecutablesList(QSettings &settings)
int numCustomExecutables = settings.beginReadArray("customExecutables");
for (int i = 0; i < numCustomExecutables; ++i) {
settings.setArrayIndex(i);
- ExecutableInfo::CloseMOStyle closeMO =
- settings.value("closeOnStart").toBool() ? ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE
- : ExecutableInfo::CloseMOStyle::DEFAULT_STAY;
Executable::Flags flags;
if (settings.value("custom", true).toBool()) flags |= Executable::CustomExecutable;
@@ -348,7 +347,6 @@ void OrganizerCore::updateExecutablesList(QSettings &settings)
settings.value("binary").toString(),
settings.value("arguments").toString(),
settings.value("workingDirectory", "").toString(),
- closeMO,
settings.value("steamAppID", "").toString(),
flags);
}
@@ -543,6 +541,11 @@ void OrganizerCore::createDefaultProfile()
}
}
+void OrganizerCore::prepareVFS()
+{
+ m_USVFS.updateMapping(fileMapping());
+}
+
void OrganizerCore::setCurrentProfile(const QString &profileName)
{
if ((m_CurrentProfile != nullptr) &&
@@ -867,7 +870,7 @@ ModList *OrganizerCore::modList()
return &m_ModList;
}
-void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir &currentDirectory, bool closeAfterStart, const QString &steamAppID)
+void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir &currentDirectory, const QString &steamAppID)
{
LockedDialog *dialog = new LockedDialog(qApp->activeWindow());
dialog->show();
@@ -875,55 +878,52 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument
HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID);
if (processHandle != INVALID_HANDLE_VALUE) {
- if (closeAfterStart && (m_UserInterface != nullptr)) {
- m_UserInterface->closeWindow();
- } else {
- if (m_UserInterface != nullptr) {
- m_UserInterface->setWindowEnabled(false);
- }
- // re-enable the locked dialog because what'd be the point otherwise?
- dialog->setEnabled(true);
+ if (m_UserInterface != nullptr) {
+ m_UserInterface->setWindowEnabled(false);
+ }
+ // re-enable the locked dialog because what'd be the point otherwise?
+ dialog->setEnabled(true);
- QCoreApplication::processEvents();
+ QCoreApplication::processEvents();
- DWORD processExitCode;
- DWORD retLen;
- JOBOBJECT_BASIC_PROCESS_ID_LIST info;
+ DWORD processExitCode;
+ DWORD retLen;
+ JOBOBJECT_BASIC_PROCESS_ID_LIST info;
- {
- DWORD currentProcess = 0UL;
- bool isJobHandle = true;
+ {
+ DWORD currentProcess = 0UL;
+ bool isJobHandle = true;
- DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE);
- while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) {
- if (isJobHandle) {
- if (::QueryInformationJobObject(processHandle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) {
- if (info.NumberOfProcessIdsInList == 0) {
- break;
- } else {
- if (info.ProcessIdList[0] != currentProcess) {
- currentProcess = info.ProcessIdList[0];
- dialog->setProcessName(ToQString(getProcessName(currentProcess)));
- }
- }
+ DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE);
+ while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) {
+ if (isJobHandle) {
+ if (::QueryInformationJobObject(processHandle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) {
+ if (info.NumberOfProcessIdsInList == 0) {
+ break;
} else {
- // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there
- // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running.
- // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without
- // the right to break out.
- if (::GetLastError() != ERROR_MORE_DATA) {
- isJobHandle = false;
+ if (info.ProcessIdList[0] != currentProcess) {
+ currentProcess = info.ProcessIdList[0];
+ dialog->setProcessName(ToQString(getProcessName(currentProcess)));
}
}
+ } else {
+ // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there
+ // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running.
+ // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without
+ // the right to break out.
+ if (::GetLastError() != ERROR_MORE_DATA) {
+ isJobHandle = false;
+ }
}
+ }
- // keep processing events so the app doesn't appear dead
- QCoreApplication::processEvents();
+ // keep processing events so the app doesn't appear dead
+ QCoreApplication::processEvents();
- res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE);
- }
- ::GetExitCodeProcess(processHandle, &processExitCode);
+ res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE);
}
+ ::GetExitCodeProcess(processHandle, &processExitCode);
+
::CloseHandle(processHandle);
if (m_UserInterface != nullptr) {
@@ -966,7 +966,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString &
//This could possibly be extracted somewhere else but it's probably for when
//we have more than one provider of game registration.
- if (QFileInfo(managedGame()->gameDirectory().absoluteFilePath("steam_api.dll")).exists()
+ if ((QFileInfo(managedGame()->gameDirectory().absoluteFilePath("steam_api.dll")).exists()
+ || QFileInfo(managedGame()->gameDirectory().absoluteFilePath("steam_api64.dll")).exists())
&& (m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) {
if (!testForSteam()) {
QWidget *window = qApp->activeWindow();
@@ -995,7 +996,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString &
// TODO: should also pass arguments
if (m_AboutToRun(binary.absoluteFilePath())) {
- return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true);
+ m_USVFS.updateMapping(fileMapping());
+ return startBinary(binary, arguments, currentDirectory, true);
} else {
qDebug("start of \"%s\" canceled by plugin", qPrintable(binary.absoluteFilePath()));
return INVALID_HANDLE_VALUE;
@@ -1190,10 +1192,6 @@ void OrganizerCore::refreshBSAList()
m_ActiveArchives = m_DefaultArchives;
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives);
- }
-
m_ArchivesInit = true;
}
}
@@ -1252,9 +1250,7 @@ void OrganizerCore::updateModInDirectoryStructure(unsigned int index, ModInfo::P
updateModActiveState(index, true);
// now we need to refresh the bsa list and save it so there is no confusion about what archives are avaiable and active
refreshBSAList();
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
- }
+
std::vector<QString> archives = enabledArchives();
m_DirectoryRefresher.setMods(m_CurrentProfile->getActiveMods(),
std::set<QString>(archives.begin(), archives.end()));
@@ -1399,9 +1395,6 @@ void OrganizerCore::modStatusChanged(unsigned int index)
FilesOrigin &origin = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()));
origin.enable(false);
}
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
- }
}
modInfo->clearCaches();
@@ -1540,9 +1533,6 @@ bool OrganizerCore::saveCurrentLists()
try {
savePluginList();
- if (m_UserInterface != nullptr) {
- m_UserInterface->archivesWriter().write();
- }
} catch (const std::exception &e) {
reportError(tr("failed to save load order: %1").arg(e.what()));
}
@@ -1576,21 +1566,53 @@ void OrganizerCore::prepareStart() {
storeSettings();
}
-/*
-std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping()
+std::vector<Mapping> OrganizerCore::fileMapping()
{
- return fileMapping(managedGame()->dataDirectory().absolutePath(),
- directoryStructure(),
- directoryStructure());
-}
+ // need to wait until directory structure
+ while (m_DirectoryUpdate) {
+ ::Sleep(100);
+ QCoreApplication::processEvents();
+ }
+
+ int overwriteId = m_DirectoryStructure->getOriginByName(L"Overwrite").getID();
+
+ IPluginGame *game = qApp->property("managed_game").value<IPluginGame *>();
+ MappingType result = fileMapping(
+ QDir::toNativeSeparators(game->dataDirectory().absolutePath()),
+ "\\",
+ directoryStructure(), directoryStructure(),
+ overwriteId);
+
+ if (m_CurrentProfile->localSavesEnabled()) {
+ LocalSavegames *localSaves = game->feature<LocalSavegames>();
+
+ if (localSaves != nullptr) {
+ MappingType saveMap = localSaves->mappings(currentProfile()->absolutePath() + "/saves");
+ result.reserve(result.size() + saveMap.size());
+ result.insert(result.end(), saveMap.begin(), saveMap.end());
+ }
+ }
+ for (MOBase::IPluginFileMapper *mapper : m_PluginContainer->plugins<MOBase::IPluginFileMapper>()) {
+ IPlugin *plugin = dynamic_cast<IPlugin*>(mapper);
+ if (plugin->isActive()) {
+ MappingType pluginMap = mapper->mappings();
+ result.reserve(result.size() + pluginMap.size());
+ result.insert(result.end(), pluginMap.begin(), pluginMap.end());
+ }
+ }
-std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping(
+ return result;
+}
+
+std::vector<Mapping> OrganizerCore::fileMapping(
const QString &dataPath,
+ const QString &relPath,
const DirectoryEntry *base,
- const DirectoryEntry *directoryEntry)
+ const DirectoryEntry *directoryEntry,
+ int createDestination)
{
- std::vector<std::pair<QString, QString>> result;
+ std::vector<Mapping> result;
for (FileEntry::Ptr current : directoryEntry->getFiles()) {
bool isArchive = false;
@@ -1599,20 +1621,37 @@ std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping(
continue;
}
- QString fileName = ToQString(current->getRelativePath());
- QString source = ToQString(base->getOriginByID(origin).getPath()) + fileName;
- QString target = QDir::toNativeSeparators(dataPath) + fileName;
- result.push_back(std::make_pair(source, target));
+ QString originPath
+ = QString::fromStdWString(base->getOriginByID(origin).getPath());
+ QString fileName = QString::fromStdWString(current->getName());
+ QString source = originPath + relPath + fileName;
+ QString target = dataPath + relPath + fileName;
+ if (source != target) {
+ result.push_back({source, target, false, false});
+ }
}
// recurse into subdirectories
std::vector<DirectoryEntry*>::const_iterator current, end;
directoryEntry->getSubDirectories(current, end);
for (; current != end; ++current) {
- std::vector<std::pair<QString, QString>> subRes = fileMapping(dataPath, base, *current);
+ int origin = (*current)->anyOrigin();
+
+ QString originPath
+ = QString::fromStdWString(base->getOriginByID(origin).getPath());
+ QString dirName = QString::fromStdWString((*current)->getName());
+ QString source = originPath + relPath + dirName;
+ QString target = dataPath + relPath + dirName;
+
+ bool writeDestination = (base == directoryEntry)
+ && (origin == createDestination);
+
+ result.push_back({source, target, true, writeDestination});
+ std::vector<Mapping> subRes
+ = fileMapping(dataPath, relPath + dirName + "\\", base, *current,
+ createDestination);
result.insert(result.end(), subRes.begin(), subRes.end());
}
return result;
}
-*/
diff --git a/src/organizercore.h b/src/organizercore.h
index 5cfbaca4..ff730c2b 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -14,6 +14,7 @@
#include "modlistsortproxy.h"
#include "pluginlistsortproxy.h"
#include "executableslist.h"
+#include "usvfsconnector.h"
#include <directoryentry.h>
#include <imoinfo.h>
#include <iplugindiagnose.h>
@@ -115,7 +116,7 @@ public:
void doAfterLogin(const std::function<void()> &function) { m_PostLoginTasks.append(function); }
- void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir &currentDirectory = QDir(), bool closeAfterStart = true, const QString &steamAppID = "");
+ void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir &currentDirectory = QDir(), const QString &steamAppID = "");
HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir &currentDirectory, const QString &steamAppID);
void loginSuccessfulUpdate(bool necessary);
@@ -125,6 +126,8 @@ public:
MOBase::DelayedFileWriter &pluginsWriter() { return m_PluginListsWriter; }
+ void prepareVFS();
+
public:
MOBase::IModRepositoryBridge *createNexusBridge() const;
QString profileName() const;
@@ -156,7 +159,10 @@ public:
bool onFinishedRun(const std::function<void (const QString &, unsigned int)> &func);
void refreshModList(bool saveChanges = true);
- //std::vector<std::pair<QString, QString> > fileMapping();
+ /**
+ * @brief return a descriptor of the mappings real file->virtual file
+ */
+ std::vector<Mapping> fileMapping();
public: // IPluginDiagnose interface
@@ -209,11 +215,11 @@ private:
bool testForSteam();
- /*
- * std::vector<std::pair<QString, QString>> fileMapping(const QString &dataPath,
- const MOShared::DirectoryEntry *base,
- const MOShared::DirectoryEntry *directoryEntry);
-*/
+ std::vector<Mapping>
+ fileMapping(const QString &dataPath, const QString &relPath,
+ const MOShared::DirectoryEntry *base,
+ const MOShared::DirectoryEntry *directoryEntry,
+ int createDestination);
private slots:
@@ -269,6 +275,7 @@ private:
bool m_ArchivesInit;
MOBase::DelayedFileWriter m_PluginListsWriter;
+ UsvfsConnector m_USVFS;
};
diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp
index ba07c154..79ff50e5 100644
--- a/src/organizerproxy.cpp
+++ b/src/organizerproxy.cpp
@@ -151,6 +151,11 @@ QList<MOBase::IOrganizer::FileInfo> OrganizerProxy::findFileInfos(const QString
return m_Proxied->findFileInfos(path, filter);
}
+MOBase::IProfile *OrganizerProxy::profile()
+{
+ return m_Proxied->currentProfile();
+}
+
MOBase::IDownloadManager *OrganizerProxy::downloadManager() const
{
return m_Proxied->downloadManager();
diff --git a/src/organizerproxy.h b/src/organizerproxy.h
index 62a35498..02507dd7 100644
--- a/src/organizerproxy.h
+++ b/src/organizerproxy.h
@@ -33,6 +33,7 @@ public:
virtual QStringList findFiles(const QString &path, const std::function<bool(const QString &)> &filter) const;
virtual QStringList getFileOrigins(const QString &fileName) const;
virtual QList<FileInfo> findFileInfos(const QString &path, const std::function<bool(const FileInfo&)> &filter) const;
+ virtual MOBase::IProfile *profile();
virtual MOBase::IDownloadManager *downloadManager() const;
virtual MOBase::IPluginList *pluginList() const;
@@ -55,5 +56,4 @@ private:
};
-
#endif // ORGANIZERPROXY_H
diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp
index 7a059cd1..a594f703 100644
--- a/src/plugincontainer.cpp
+++ b/src/plugincontainer.cpp
@@ -87,7 +87,7 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName)
m_Organizer->settings().registerPlugin(pluginObj);
}
- { // diagnosis plugins
+ { // diagnosis plugin
IPluginDiagnose *diagnose = qobject_cast<IPluginDiagnose*>(plugin);
if (diagnose != nullptr) {
bf::at_key<IPluginDiagnose>(m_Plugins).push_back(diagnose);
@@ -96,6 +96,12 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName)
);
}
}
+ { // file mapper plugin
+ IPluginFileMapper *mapper = qobject_cast<IPluginFileMapper*>(plugin);
+ if (mapper != nullptr) {
+ bf::at_key<IPluginFileMapper>(m_Plugins).push_back(mapper);
+ }
+ }
{ // mod page plugin
IPluginModPage *modPage = qobject_cast<IPluginModPage*>(plugin);
if (verifyPlugin(modPage)) {
diff --git a/src/plugincontainer.h b/src/plugincontainer.h
index 02978e3e..70392d0b 100644
--- a/src/plugincontainer.h
+++ b/src/plugincontainer.h
@@ -10,6 +10,7 @@
#include <iplugintool.h>
#include <ipluginproxy.h>
#include <iplugininstaller.h>
+#include <ipluginfilemapper.h>
#include <QtPlugin>
#include <QPluginLoader>
#include <QFile>
@@ -36,7 +37,8 @@ private:
boost::fusion::pair<MOBase::IPluginModPage, std::vector<MOBase::IPluginModPage*>>,
boost::fusion::pair<MOBase::IPluginPreview, std::vector<MOBase::IPluginPreview*>>,
boost::fusion::pair<MOBase::IPluginTool, std::vector<MOBase::IPluginTool*>>,
- boost::fusion::pair<MOBase::IPluginProxy, std::vector<MOBase::IPluginProxy*>>
+ boost::fusion::pair<MOBase::IPluginProxy, std::vector<MOBase::IPluginProxy*>>,
+ boost::fusion::pair<MOBase::IPluginFileMapper, std::vector<MOBase::IPluginFileMapper*>>
> PluginMap;
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
diff --git a/src/profile.cpp b/src/profile.cpp
index 77f6ebd1..24f094f3 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -582,7 +582,9 @@ bool Profile::invalidationActive(bool *supported) const
}
return false;
} else {
- *supported = false;
+ if (supported != nullptr) {
+ *supported = false;
+ }
}
return false;
}
diff --git a/src/profile.h b/src/profile.h
index e33ac674..a7d71251 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -200,7 +200,7 @@ public:
*
* @return number of mods for which the profile has status information
**/
- unsigned int numMods() const { return m_ModStatus.size(); }
+ size_t numMods() const { return m_ModStatus.size(); }
/**
* @return the number of mods that can be enabled and where the priority can be modified
diff --git a/src/savegame.h b/src/savegame.h
index d1bf4691..e7220fcc 100644
--- a/src/savegame.h
+++ b/src/savegame.h
@@ -106,10 +106,10 @@ public:
protected:
QString m_FileName;
- QString m_PCName;
- unsigned short m_PCLevel;
- QString m_PCLocation;
- unsigned long m_SaveNumber;
+ QString m_PCName { "N/A" };
+ unsigned short m_PCLevel { 0 };
+ QString m_PCLocation { "N/A" };
+ unsigned long m_SaveNumber { 0 };
SYSTEMTIME m_CreationTime;
QImage m_Screenshot;
diff --git a/src/savegamegamebryo.cpp b/src/savegamegamebryo.cpp
index 68ed30af..3fc96bef 100644
--- a/src/savegamegamebryo.cpp
+++ b/src/savegamegamebryo.cpp
@@ -18,12 +18,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "savegamegamebyro.h"
-
#include "isavegame.h"
#include "savegameinfo.h"
#include "iplugingame.h"
-#include "gamebryosavegame.h"
-
+#include <gamebryosavegame.h>
#include <limits>
#include <set>
@@ -34,13 +32,29 @@ SaveGameGamebryo::SaveGameGamebryo(QObject *parent, const QString &fileName, IPl
: SaveGame(parent, fileName, game)
, m_Plugins()
{
- SaveGameInfo const *info = game->feature<SaveGameInfo>();
+ // ensure creation time is set to a valid, preferrably even a reasonable value
+ memset(&m_CreationTime, 0, sizeof(SYSTEMTIME));
+ HANDLE fileHandle = CreateFileW(fileName.toStdWString().c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ if (fileHandle != INVALID_HANDLE_VALUE) {
+ FILETIME creationTime;
+ if (GetFileTime(fileHandle, &creationTime, nullptr, nullptr)) {
+ FileTimeToSystemTime(&creationTime, &m_CreationTime);
+ }
+ }
+
+ const SaveGameInfo *info = game->feature<SaveGameInfo>();
if (info != nullptr) {
- ISaveGame const *save = info->getSaveGameInfo(fileName);
+ const ISaveGame *save = info->getSaveGameInfo(fileName);
m_Save = save;
//Kludgery
- GamebryoSaveGame const *s = dynamic_cast<GamebryoSaveGame const *>(save);
+ const GamebryoSaveGame *s = dynamic_cast<const GamebryoSaveGame *>(save);
m_PCName = s->getPCName();
m_PCLevel = s->getPCLevel();
m_PCLocation = s->getPCLocation();
diff --git a/src/savegamegamebyro.h b/src/savegamegamebyro.h
index bce08018..5ac62848 100644
--- a/src/savegamegamebyro.h
+++ b/src/savegamegamebyro.h
@@ -56,7 +56,7 @@ public:
/**
* @return number of plugins that were enabled when the save game was created
**/
- int numPlugins() const { return m_Plugins.size(); }
+ size_t numPlugins() const { return m_Plugins.size(); }
/**
* retrieve the name of one of the plugins that were enabled when the save game
@@ -70,7 +70,7 @@ private:
QStringList m_Plugins;
//Note: This isn't owned by us so safe to copy
- MOBase::ISaveGame const *m_Save;
+ const MOBase::ISaveGame *m_Save;
};
diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp
index 11478fbc..af1eb5b2 100644
--- a/src/selfupdater.cpp
+++ b/src/selfupdater.cpp
@@ -77,8 +77,6 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface)
throw MyException(InstallationManager::getErrorString(m_ArchiveHandler->getLastError()));
}
- connect(m_Progress, SIGNAL(canceled()), this, SLOT(downloadCancel()));
-
VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath()));
m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16,
@@ -135,6 +133,7 @@ void SelfUpdater::showProgress()
{
if (m_Progress == nullptr) {
m_Progress = new QProgressDialog(m_Parent, Qt::Dialog);
+ connect(m_Progress, SIGNAL(canceled()), this, SLOT(downloadCancel()));
}
m_Progress->setModal(true);
m_Progress->show();
diff --git a/src/selfupdater.h b/src/selfupdater.h
index 446778fb..0a31b0f3 100644
--- a/src/selfupdater.h
+++ b/src/selfupdater.h
@@ -29,6 +29,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QFile>
#include <QProgressDialog>
+#include <windows.h>
+
namespace MOBase { class IPluginGame; }
class NexusInterface;
diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc
index e2dbfe20..ac6c210f 100644
--- a/src/shared/appconfig.inc
+++ b/src/shared/appconfig.inc
@@ -14,7 +14,6 @@ APPPARAM(std::wstring, iniFileName, L"ModOrganizer.ini")
APPPARAM(std::wstring, proxyDLLTarget, L"steam_api.dll")
APPPARAM(std::wstring, proxyDLLOrig, L"steam_api_orig.dll") // needs to be identical to the value used in proxydll-project
APPPARAM(std::wstring, proxyDLLSource, L"proxy.dll")
-APPPARAM(std::wstring, hookDLLName, L"hook.dll") // needs to be identical to the value used in proxydll-project
APPPARAM(const wchar_t*, localSavePlaceholder, L"__MOProfileSave__\\")
APPPARAM(std::wstring, firstStepsTutorial, L"tutorial_firststeps_main.js")
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 54b6efff..e081c006 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -704,7 +704,7 @@ 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 = nullptr;
@@ -718,7 +718,7 @@ int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive)
return -1;
}
}
-}
+}*/
std::vector<FileEntry::Ptr> DirectoryEntry::getFiles() const
{
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 2d32450a..04b8782b 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -207,7 +207,8 @@ public:
DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID);
DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID,
- boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection);
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection);
~DirectoryEntry();
@@ -232,7 +233,9 @@ public:
FilesOrigin &getOriginByID(int ID) const;
FilesOrigin &getOriginByName(const std::wstring &name) const;
- int getOrigin(const std::wstring &path, bool &archive);
+ int anyOrigin() const;
+
+ //int getOrigin(const std::wstring &path, bool &archive);
std::vector<FileEntry::Ptr> getFiles() const;
@@ -316,8 +319,6 @@ private:
DirectoryEntry *getSubDirectoryRecursive(const std::wstring &path, bool create, int originID = -1);
- int anyOrigin() const;
-
void removeDirRecursive();
private:
diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp
index 797d68ec..446d4afe 100644
--- a/src/shared/fallout3info.cpp
+++ b/src/shared/fallout3info.cpp
@@ -68,21 +68,4 @@ std::vector<std::wstring> Fallout3Info::getIniFileNames() const
return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
}
-std::wstring Fallout3Info::getReferenceDataFile() const
-{
- return L"Fallout - Meshes.bsa";
-}
-
-bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) const
-{
- static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr };
-
- for (int i = 0; profileFiles[i] != nullptr; ++i) {
- if (_wcsicmp(fileName, profileFiles[i]) == 0) {
- return true;
- }
- }
- return false;
-}
-
} // namespace MOShared
diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h
index 5cf98e3b..601fc346 100644
--- a/src/shared/fallout3info.h
+++ b/src/shared/fallout3info.h
@@ -40,10 +40,6 @@ public:
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames() const;
- virtual std::wstring getReferenceDataFile() const;
-
- virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const;
-
virtual std::wstring archiveListKey() const { return L"SArchiveList"; }
private:
diff --git a/src/shared/fallout4info.cpp b/src/shared/fallout4info.cpp
new file mode 100644
index 00000000..aab431ad
--- /dev/null
+++ b/src/shared/fallout4info.cpp
@@ -0,0 +1,100 @@
+/*
+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 "fallout4info.h"
+#include "util.h"
+#include <tchar.h>
+#include <ShlObj.h>
+#include <sstream>
+#include "windows_error.h"
+#include "error_report.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <boost/assign.hpp>
+
+namespace MOShared {
+
+Fallout4Info::Fallout4Info(const std::wstring &gameDirectory)
+ : GameInfo(gameDirectory)
+{
+ identifyMyGamesDirectory(L"fallout4");
+}
+
+bool Fallout4Info::identifyGame(const std::wstring &searchPath)
+{
+ return FileExists(searchPath, L"Fallout4.exe") &&
+ FileExists(searchPath, L"Fallout4Launcher.exe");
+}
+
+std::wstring Fallout4Info::getRegPathStatic()
+{
+ HKEY key;
+ LONG errorcode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Bethesda Softworks\\Fallout4",
+ 0, KEY_QUERY_VALUE, &key);
+
+ if (errorcode != ERROR_SUCCESS) {
+ return std::wstring();
+ }
+
+ WCHAR temp[MAX_PATH];
+ DWORD bufferSize = MAX_PATH;
+
+ if (::RegQueryValueExW(key, L"Installed Path", nullptr, nullptr, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) {
+ return std::wstring(temp);
+ } else {
+ return std::wstring();
+ }
+}
+
+
+std::vector<std::wstring> Fallout4Info::getDLCPlugins()
+{
+ return std::vector<std::wstring>();
+}
+
+std::vector<std::wstring> Fallout4Info::getSavegameAttachmentExtensions()
+{
+ return std::vector<std::wstring>();
+}
+
+std::vector<std::wstring> Fallout4Info::getIniFileNames() const
+{
+ return boost::assign::list_of(L"fallout4.ini")(L"fallout4prefs.ini");
+}
+
+std::wstring Fallout4Info::getNexusPage(bool nmmScheme)
+{
+ if (nmmScheme) {
+ return L"http://nmm.nexusmods.com/fallout4";
+ } else {
+ return L"http://www.nexusmods.com/fallout4";
+ }
+}
+
+std::wstring Fallout4Info::getNexusInfoUrlStatic()
+{
+ return L"http://nmm.nexusmods.com/fallout4";
+}
+
+int Fallout4Info::getNexusModIDStatic()
+{
+ return 377160;
+}
+
+} // namespace MOShared
diff --git a/src/shared/fallout4info.h b/src/shared/fallout4info.h
new file mode 100644
index 00000000..70c9b7d3
--- /dev/null
+++ b/src/shared/fallout4info.h
@@ -0,0 +1,75 @@
+/*
+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 FALLOUT4INFO_H
+#define FALLOUT4INFO_H
+
+
+#include "gameinfo.h"
+
+namespace MOShared {
+
+
+class Fallout4Info : public GameInfo
+{
+
+ friend class GameInfo;
+
+public:
+
+ virtual ~Fallout4Info() {}
+
+ static std::wstring getRegPathStatic();
+ virtual std::wstring getRegPath() const { return getRegPathStatic(); }
+ virtual std::wstring getBinaryName() { return L"Fallout4.exe"; }
+
+ virtual std::wstring getGameName() const { return L"Fallout 4"; }
+ virtual std::wstring getGameShortName() const { return L"Fallout4"; }
+
+ virtual std::vector<std::wstring> getDLCPlugins();
+ virtual std::vector<std::wstring> getSavegameAttachmentExtensions();
+
+ // file name of this games ini (no path)
+ virtual std::vector<std::wstring> getIniFileNames() const;
+
+ virtual std::wstring getNexusPage(bool nmmScheme = true);
+ static std::wstring getNexusInfoUrlStatic();
+ virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); }
+ static int getNexusModIDStatic();
+ virtual int getNexusModID() { return getNexusModIDStatic(); }
+ virtual int getNexusGameID() { return 1151; }
+
+ // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to
+ // the game directory
+ //virtual std::vector<ExecutableInfo> getExecutables();
+
+ virtual std::wstring archiveListKey() { return L"SArchiveList"; }
+
+private:
+
+ Fallout4Info(const std::wstring &gameDirectory);
+
+ static bool identifyGame(const std::wstring &searchPath);
+
+
+};
+
+} // namespace MOShared
+
+#endif // FALLOUT3INFO_H
diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp
index 6347224d..510dfa01 100644
--- a/src/shared/falloutnvinfo.cpp
+++ b/src/shared/falloutnvinfo.cpp
@@ -68,21 +68,4 @@ std::vector<std::wstring> FalloutNVInfo::getIniFileNames() const
return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
}
-std::wstring FalloutNVInfo::getReferenceDataFile() const
-{
- return L"Fallout - Meshes.bsa";
-}
-
-bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) const
-{
- static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr };
-
- for (int i = 0; profileFiles[i] != nullptr; ++i) {
- if (_wcsicmp(fileName, profileFiles[i]) == 0) {
- return true;
- }
- }
- return false;
-}
-
} // namespace MOShared
diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h
index 04c13c7d..a9b3b9ec 100644
--- a/src/shared/falloutnvinfo.h
+++ b/src/shared/falloutnvinfo.h
@@ -41,12 +41,6 @@ public:
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames() const;
- virtual std::wstring getReferenceDataFile() const;
-
- virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const;
-
- virtual std::wstring archiveListKey() const { return L"SArchiveList"; }
-
private:
FalloutNVInfo(const std::wstring &gameDirectory);
diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp
index 5c13a520..9b097053 100644
--- a/src/shared/gameinfo.cpp
+++ b/src/shared/gameinfo.cpp
@@ -23,6 +23,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "oblivioninfo.h"
#include "fallout3info.h"
+#include "fallout4info.h"
#include "falloutnvinfo.h"
#include "skyriminfo.h"
#include "util.h"
@@ -96,6 +97,8 @@ bool GameInfo::identifyGame(const std::wstring &searchPath)
s_Instance = new FalloutNVInfo(searchPath);
} else if (SkyrimInfo::identifyGame(searchPath)) {
s_Instance = new SkyrimInfo(searchPath);
+ } else if (Fallout4Info::identifyGame(searchPath)) {
+ s_Instance = new Fallout4Info(searchPath);
}
return s_Instance != nullptr;
diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h
index 7bc86d3a..c99ebef7 100644
--- a/src/shared/gameinfo.h
+++ b/src/shared/gameinfo.h
@@ -60,12 +60,6 @@ public:
// file name of this games ini file(s)
virtual std::vector<std::wstring> getIniFileNames() const = 0;
- //**USED ONLY IN HOOKDLL
- virtual std::wstring getReferenceDataFile() const = 0;
-
- //**USED ONLY IN HOOKDLL
- virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const = 0;
-
protected:
GameInfo(const std::wstring &gameDirectory);
diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp
index b50f1daa..b290fb36 100644
--- a/src/shared/oblivioninfo.cpp
+++ b/src/shared/oblivioninfo.cpp
@@ -68,21 +68,4 @@ std::vector<std::wstring> OblivionInfo::getIniFileNames() const
return boost::assign::list_of(L"oblivion.ini")(L"oblivionprefs.ini");
}
-bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) const
-{
- static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", nullptr };
-
- for (int i = 0; profileFiles[i] != nullptr; ++i) {
- if (_wcsicmp(fileName, profileFiles[i]) == 0) {
- return true;
- }
- }
- return false;
-}
-
-std::wstring OblivionInfo::getReferenceDataFile() const
-{
- return L"Oblivion - Meshes.bsa";
-}
-
} // namespace MOShared
diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h
index bf0b2707..6609921a 100644
--- a/src/shared/oblivioninfo.h
+++ b/src/shared/oblivioninfo.h
@@ -39,10 +39,6 @@ public:
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames() const;
- virtual std::wstring getReferenceDataFile() const;
-
- virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const;
-
virtual std::wstring archiveListKey() const { return L"SArchiveList"; }
private:
diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp
index 9662e66d..fbe13ae0 100644
--- a/src/shared/skyriminfo.cpp
+++ b/src/shared/skyriminfo.cpp
@@ -78,27 +78,4 @@ std::vector<std::wstring> SkyrimInfo::getIniFileNames() const
return boost::assign::list_of(L"skyrim.ini")(L"skyrimprefs.ini");
}
-std::wstring SkyrimInfo::getReferenceDataFile() const
-{
- return L"Skyrim - Meshes.bsa";
-}
-
-bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const
-{
- static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", nullptr };
-
- for (int i = 0; profileFiles[i] != nullptr; ++i) {
- if (_wcsicmp(fileName, profileFiles[i]) == 0) {
- return true;
- }
- }
-
- if ((_wcsicmp(fileName, L"plugins.txt") == 0) &&
- (m_AppData.empty() || (StrStrIW(fullPath, m_AppData.c_str()) != nullptr))) {
- return true;
- }
-
- return false;
-}
-
} // namespace MOShared
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h
index bd329403..a13b834e 100644
--- a/src/shared/skyriminfo.h
+++ b/src/shared/skyriminfo.h
@@ -41,10 +41,6 @@ public:
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames() const;
- virtual std::wstring getReferenceDataFile() const;
-
- virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) const;
-
private:
SkyrimInfo(const std::wstring &gameDirectory);
diff --git a/src/spawn.cpp b/src/spawn.cpp
index 49e89a8b..19cf304c 100644
--- a/src/spawn.cpp
+++ b/src/spawn.cpp
@@ -23,6 +23,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "utility.h"
#include <report.h>
#include <inject.h>
+#include <usvfs.h>
+#include <Shellapi.h>
#include <appconfig.h>
#include <windows_error.h>
@@ -39,7 +41,8 @@ using namespace MOShared;
static const int BUFSIZE = 4096;
-static bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, bool suspended,
+static bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory,
+ bool suspended, bool hooked,
HANDLE stdOut, HANDLE stdErr,
HANDLE& processHandle, HANDLE& threadHandle)
{
@@ -87,15 +90,28 @@ static bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, b
}
PROCESS_INFORMATION pi;
- BOOL success = ::CreateProcess(nullptr,
- commandLine,
- nullptr, nullptr, // no special process or thread attributes
- inheritHandles, // inherit handles if we plan to use stdout or stderr reroute
- CREATE_BREAKAWAY_FROM_JOB | (suspended ? CREATE_SUSPENDED : 0), // create suspended so I have time to inject the DLL
- nullptr, // same environment as parent
- currentDirectory, // current directory
- &si, &pi // startup and process information
- );
+ BOOL success = FALSE;
+ if (hooked) {
+ success = ::CreateProcessHooked(nullptr,
+ commandLine,
+ nullptr, nullptr, // no special process or thread attributes
+ inheritHandles, // inherit handles if we plan to use stdout or stderr reroute
+ CREATE_BREAKAWAY_FROM_JOB,
+ nullptr, // same environment as parent
+ currentDirectory, // current directory
+ &si, &pi // startup and process information
+ );
+ } else {
+ success = ::CreateProcess(nullptr,
+ commandLine,
+ nullptr, nullptr, // no special process or thread attributes
+ inheritHandles, // inherit handles if we plan to use stdout or stderr reroute
+ CREATE_BREAKAWAY_FROM_JOB,
+ nullptr, // same environment as parent
+ currentDirectory, // current directory
+ &si, &pi // startup and process information
+ );
+ }
::SetEnvironmentVariable(TEXT("PATH"), oldPath.get());
@@ -113,8 +129,6 @@ static bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, b
HANDLE startBinary(const QFileInfo &binary,
const QString &arguments,
- const QString& profileName,
- int logLevel,
const QDir &currentDirectory,
bool hooked,
HANDLE stdOut,
@@ -138,8 +152,8 @@ HANDLE startBinary(const QFileInfo &binary,
std::wstring currentDirectoryName = ToWString(QDir::toNativeSeparators(currentDirectory.absolutePath()));
try {
- if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(), true,
- stdOut, stdErr, processHandle, threadHandle)) {
+ if (!spawn(binaryName.c_str(), ToWString(arguments).c_str(), currentDirectoryName.c_str(),
+ true, hooked, stdOut, stdErr, processHandle, threadHandle)) {
reportError(QObject::tr("failed to spawn \"%1\"").arg(binary.fileName()));
return INVALID_HANDLE_VALUE;
}
@@ -167,7 +181,7 @@ HANDLE startBinary(const QFileInfo &binary,
return INVALID_HANDLE_VALUE;
}
}
-
+/*
if (hooked) {
try {
QFileInfo dllInfo(QApplication::applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName()));
@@ -187,6 +201,7 @@ HANDLE startBinary(const QFileInfo &binary,
reportError("ready?");
#endif // DEBUG
}
+ */
if (::AssignProcessToJobObject(jobObject, processHandle) == 0) {
qWarning("failed to assign to job object: %lu", ::GetLastError());
@@ -196,10 +211,6 @@ HANDLE startBinary(const QFileInfo &binary,
::CloseHandle(processHandle);
}
- if (::ResumeThread(threadHandle) == (DWORD)-1) {
- reportError(QObject::tr("failed to run \"%1\"").arg(binary.fileName()));
- return INVALID_HANDLE_VALUE;
- }
::CloseHandle(threadHandle);
return jobObject;
}
diff --git a/src/spawn.h b/src/spawn.h
index 63eda18d..c2d99bdb 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -60,7 +60,7 @@ private:
* @todo is the profile name even used any more?
* @todo is the hooked parameter used?
**/
-HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString &profileName, int logLevel,
+HANDLE startBinary(const QFileInfo &binary, const QString &arguments,
const QDir &currentDirectory, bool hooked,
HANDLE stdOut = INVALID_HANDLE_VALUE, HANDLE stdErr = INVALID_HANDLE_VALUE);
diff --git a/src/splash.png b/src/splash.png
index 3eec8bed..4f1f1a3d 100644
--- a/src/splash.png
+++ b/src/splash.png
Binary files differ
diff --git a/src/transfersavesdialog.cpp b/src/transfersavesdialog.cpp
index 48fc4548..f6ca27cd 100644
--- a/src/transfersavesdialog.cpp
+++ b/src/transfersavesdialog.cpp
@@ -80,8 +80,9 @@ void TransferSavesDialog::refreshLocalSaves()
QStringList files = savesDir.entryList(QDir::Files, QDir::Time);
- foreach (const QString &filename, files) {
+ for (const QString &filename : files) {
SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename), m_GamePlugin);
+
save->setParent(this);
m_LocalSaves.push_back(save);
}
@@ -165,7 +166,7 @@ void TransferSavesDialog::on_moveToLocalBtn_clicked()
iter != m_GlobalSaves.end(); ++iter) {
if ((*iter)->pcName() == selectedCharacter) {
QStringList files = (*iter)->saveFiles();
- foreach (const QString &file, files) {
+ for (const QString &file : files) {
QFileInfo fileInfo(file);
QString destinationFile = destination + "/" + fileInfo.fileName();
if (QFile::exists(destinationFile)) {
@@ -202,22 +203,12 @@ void TransferSavesDialog::on_copyToLocalBtn_clicked()
iter != m_GlobalSaves.end(); ++iter) {
if ((*iter)->pcName() == selectedCharacter) {
QStringList files = (*iter)->saveFiles();
- foreach (const QString &file, files) {
+ QStringList destinations;
+ for (const QString &file : files) {
QFileInfo fileInfo(file);
- QString destinationFile = destination + "/" + 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());
- }
+ destinations.append(destination + "/" + fileInfo.fileName());
}
+ shellCopy(files, destinations, this);
}
}
}
@@ -239,7 +230,7 @@ void TransferSavesDialog::on_moveToGlobalBtn_clicked()
iter != m_LocalSaves.end(); ++iter) {
if ((*iter)->pcName() == selectedCharacter) {
QStringList files = (*iter)->saveFiles();
- foreach (const QString &file, files) {
+ for (const QString &file : files) {
QFileInfo fileInfo(file);
QString destinationFile = destination.filePath(fileInfo.fileName());
if (QFile::exists(destinationFile)) {
@@ -278,7 +269,7 @@ void TransferSavesDialog::on_copyToGlobalBtn_clicked()
iter != m_LocalSaves.end(); ++iter) {
if ((*iter)->pcName() == selectedCharacter) {
QStringList files = (*iter)->saveFiles();
- foreach (const QString &file, files) {
+ for (const QString &file : files) {
QFileInfo fileInfo(file);
QString destinationFile = destination.filePath(fileInfo.fileName());
if (QFile::exists(destinationFile)) {
diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js
index 2bc270d7..c47b5c0d 100644
--- a/src/tutorials/tutorial_firststeps_main.js
+++ b/src/tutorials/tutorial_firststeps_main.js
@@ -145,42 +145,6 @@ function getTutorialSteps()
},
function() {
- tutorial.text = qsTr("Another special type of files are BSAs. These are bundles of game resources. "
- + "Please open the \"Archives\"-tab.")
- if (tutorialControl.waitForTabOpen("tabWidget", 1)) {
- highlightItem("tabWidget", true)
- } else {
- waitForClick()
- }
- },
-
- function() {
- tutorial.text = qsTr("These archives can be a real headache because the way bsas interact "
- + "with non-bundled resources is complicated. The game can even crash if required "
- + "archives are not loaded or ordered incorrectly.")
- waitForClick()
- },
-
- function() {
- tutorial.text = qsTr("MO applies some \"magic\" to make all BSAs that are checked in this list load in "
- + "the correct order interleaved with the non-bundled resources.")
- waitForClick()
- },
-
- function() {
- tutorial.text = qsTr("You can disable this magic to make MO behave more like other tools. In this case "
- + "their load order follows that of the corresponding plugin (.esp).")
- highlightItem("managedArchiveLabel", false)
- waitForClick()
- },
-
- function() {
- tutorial.text = qsTr("Many BSAs will appear grayed out and enabled. These mods are loaded by the game engine "
- + "automatically so they can't be disabled here.")
- waitForClick()
- },
-
- function() {
tutorial.text = qsTr("Now you know how to download, install and enable mods.\n"
+ "It's important you always start the game from inside MO, otherwise "
+ "the mods you installed here won't work.")
diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp
new file mode 100644
index 00000000..380abe01
--- /dev/null
+++ b/src/usvfsconnector.cpp
@@ -0,0 +1,151 @@
+/*
+Copyright (C) 2015 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 "usvfsconnector.h"
+#include "settings.h"
+#include <memory>
+#include <QTemporaryFile>
+#include <QProgressDialog>
+#include <QDateTime>
+#include <QCoreApplication>
+
+
+static const char SHMID[] = "mod_organizer_instance";
+
+
+/*
+extern "C" DLLEXPORT BOOL WINAPI VirtualLinkFile(LPCWSTR source, LPCWSTR destination, BOOL failIfExists);
+extern "C" DLLEXPORT BOOL WINAPI VirtualLinkDirectoryStatic(LPCWSTR source, LPCWSTR destination, unsigned int flags);
+extern "C" DLLEXPORT BOOL WINAPI ConnectVFS(const usvfs::Parameters *parameters);
+extern "C" DLLEXPORT void WINAPI DisconnectVFS();
+extern "C" DLLEXPORT void WINAPI GetCurrentVFSName(char *buffer, size_t size);
+extern "C" DLLEXPORT BOOL WINAPI CreateProcessHooked(LPCWSTR lpApplicationName
+ , LPWSTR lpCommandLine
+ , LPSECURITY_ATTRIBUTES lpProcessAttributes
+ , LPSECURITY_ATTRIBUTES lpThreadAttributes
+ , BOOL bInheritHandles
+ , DWORD dwCreationFlags
+ , LPVOID lpEnvironment
+ , LPCWSTR lpCurrentDirectory
+ , LPSTARTUPINFOW lpStartupInfo
+ , LPPROCESS_INFORMATION lpProcessInformation
+ );
+extern "C" DLLEXPORT void __cdecl InitLogging(bool toLocal = false);
+extern "C" DLLEXPORT void __cdecl InitHooks(LPVOID userData, size_t userDataSize);
+*/
+
+
+LogWorker::LogWorker()
+ : m_Buffer(1024, '\0')
+ , m_QuitRequested(false)
+ , m_LogFile(qApp->property("dataPath").toString()
+ + QString("/logs/usvfs-%1.log").arg(
+ QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd_hh-mm-ss")))
+{
+ m_LogFile.open(QIODevice::WriteOnly);
+ qDebug("usvfs log messages are written to %s", qPrintable(m_LogFile.fileName()));
+}
+
+LogWorker::~LogWorker()
+{
+}
+
+void LogWorker::process()
+{
+ while (!m_QuitRequested) {
+ if (GetLogMessages(&m_Buffer[0], m_Buffer.size(), false)) {
+ m_LogFile.write(m_Buffer.c_str());
+ m_LogFile.write("\n");
+ m_LogFile.flush();
+ } else {
+ QThread::sleep(1);
+ }
+ }
+ emit finished();
+}
+
+void LogWorker::exit()
+{
+ m_QuitRequested = true;
+}
+
+LogLevel logLevel(int level)
+{
+ switch (level) {
+ case LogLevel::Info: return LogLevel::Info;
+ case LogLevel::Warning: return LogLevel::Warning;
+ case LogLevel::Error: return LogLevel::Error;
+ default: return LogLevel::Debug;
+ }
+}
+
+UsvfsConnector::UsvfsConnector()
+{
+ usvfs::Parameters params(SHMID, false, logLevel(Settings::instance().logLevel()));
+ InitLogging(false);
+ ConnectVFS(&params);
+
+ m_LogWorker.moveToThread(&m_WorkerThread);
+
+ connect(&m_WorkerThread, SIGNAL(started()), &m_LogWorker, SLOT(process()));
+ connect(&m_LogWorker, SIGNAL(finished()), &m_WorkerThread, SLOT(quit()));
+
+ m_WorkerThread.start(QThread::LowestPriority);
+}
+
+UsvfsConnector::~UsvfsConnector()
+{
+ DisconnectVFS();
+ m_LogWorker.exit();
+ m_WorkerThread.quit();
+ //m_WorkerThread.wait();
+}
+
+void UsvfsConnector::updateMapping(const MappingType &mapping)
+{
+ QProgressDialog progress;
+ progress.setLabelText(tr("Preparing vfs"));
+ progress.setMaximum(mapping.size());
+ progress.show();
+ int value = 0;
+ for (auto map : mapping) {
+ progress.setValue(value++);
+ if (value % 10 == 0) {
+ QCoreApplication::processEvents();
+ }
+ if (map.isDirectory) {
+ VirtualLinkDirectoryStatic(map.source.toStdWString().c_str()
+ , map.destination.toStdWString().c_str()
+ , (map.createTarget ? LINKFLAG_CREATETARGET : 0) | LINKFLAG_RECURSIVE);
+ } else {
+ VirtualLinkFile(map.source.toStdWString().c_str()
+ , map.destination.toStdWString().c_str()
+ , 0);
+ }
+ }
+/*
+ size_t dumpSize = 0;
+ CreateVFSDump(nullptr, &dumpSize);
+ std::unique_ptr<char[]> buffer(new char[dumpSize]);
+ CreateVFSDump(buffer.get(), &dumpSize);
+ qDebug(buffer.get());
+*/
+}
+
diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h
new file mode 100644
index 00000000..78bb7a49
--- /dev/null
+++ b/src/usvfsconnector.h
@@ -0,0 +1,80 @@
+/*
+Copyright (C) 2015 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 USVFSCONNECTOR_H
+#define USVFSCONNECTOR_H
+
+
+#include <usvfs.h>
+#include <filemapping.h>
+#include <QString>
+#include <QThread>
+#include <QFile>
+#include <QDebug>
+
+
+class LogWorker : public QThread {
+
+ Q_OBJECT
+
+public:
+
+ LogWorker();
+ ~LogWorker();
+
+public slots:
+
+ void process();
+ void exit();
+
+signals:
+
+ void outputLog(const QString &message);
+ void finished();
+
+private:
+
+ std::string m_Buffer;
+ bool m_QuitRequested;
+ QFile m_LogFile;
+
+};
+
+
+class UsvfsConnector : public QObject {
+
+ Q_OBJECT
+
+public:
+
+ UsvfsConnector();
+ ~UsvfsConnector();
+
+ void updateMapping(const MappingType &mapping);
+
+private:
+
+ LogWorker m_LogWorker;
+ QThread m_WorkerThread;
+
+};
+
+
+#endif // USVFSCONNECTOR_H
diff --git a/src/version.rc b/src/version.rc
index 50615bdc..d33afcbf 100644
--- a/src/version.rc
+++ b/src/version.rc
@@ -1,7 +1,7 @@
#include "Winver.h"
-#define VER_FILEVERSION 1,3,11,0
-#define VER_FILEVERSION_STR "1,3,11,0\0"
+#define VER_FILEVERSION 2,0,0
+#define VER_FILEVERSION_STR "2,0,0a1\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION