Compare commits

..
10 Commits
34 changed files with 628 additions and 478 deletions
+13
View File
@@ -83,6 +83,7 @@ SET(organizer_SRCS
viewmarkingscrollbar.cpp
plugincontainer.cpp
organizercore.cpp
usvfsconnector.cpp
shared/inject.cpp
shared/windows_error.cpp
@@ -91,6 +92,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
@@ -173,6 +175,7 @@ SET(organizer_HDRS
plugincontainer.h
organizercore.h
iuserinterface.h
usvfsconnector.h
shared/inject.h
shared/windows_error.h
@@ -181,6 +184,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
@@ -282,19 +286,28 @@ 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_features/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)
+1
View File
@@ -36,6 +36,7 @@ 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()));
+4 -1
View File
@@ -694,7 +694,10 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
bool archiveOpen = m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(fileName)).c_str(),
new MethodCallback<InstallationManager, void, LPSTR>(this, &InstallationManager::queryPassword));
if (!archiveOpen) {
qDebug("integrated archiver can't open %s. errorcode %d", qPrintable(fileName), m_CurrentArchive->getLastError());
qDebug("integrated archiver can't open %s: %s (%d)",
qPrintable(fileName),
qPrintable(getErrorString(m_CurrentArchive->getLastError())),
m_CurrentArchive->getLastError());
}
ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this));
-4
View File
@@ -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;
+7
View File
@@ -132,6 +132,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
@@ -141,6 +144,7 @@ void LoadMechanism::deactivateScriptExtender()
}
removeHintFile(pluginsDir);
*/
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate script extender loading"), e.what());
}
@@ -191,6 +195,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());
@@ -210,6 +216,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());
}
+1 -19
View File
@@ -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);
-4
View File
@@ -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();
+1 -15
View File
@@ -123,7 +123,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());
@@ -131,19 +131,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;
}
@@ -516,7 +503,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(gamePath)));
+5 -237
View File
@@ -162,7 +162,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);
@@ -187,6 +186,7 @@ MainWindow::MainWindow(const QString &exeName
m_RefreshProgress->setVisible(false);
statusBar()->addWidget(m_RefreshProgress, 1000);
statusBar()->clearMessage();
statusBar()->hide();
ui->actionEndorseMO->setVisible(false);
@@ -234,8 +234,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);
@@ -265,8 +263,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()));
@@ -305,9 +301,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()));
@@ -1320,164 +1313,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;
IPluginGame *gamePlugin = qApp->property("managed_game").value<IPluginGame*>();
BSAInvalidation *invalidation = gamePlugin->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) {
@@ -1486,7 +1321,6 @@ void MainWindow::saveModMetas()
}
}
void MainWindow::fixCategories()
{
for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) {
@@ -1557,10 +1391,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);
}
@@ -1578,7 +1408,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());
}
@@ -1871,8 +1700,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);
@@ -1900,7 +1731,6 @@ void MainWindow::modorder_changed()
}
m_OrganizerCore.refreshBSAList();
m_OrganizerCore.currentProfile()->modlistWriter().write();
m_ArchiveListWriter.write();
m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins();
{ // refresh selection
@@ -2809,30 +2639,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()
{
@@ -3855,8 +3661,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) {
@@ -4062,32 +3867,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);
@@ -4663,17 +4442,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
+2 -18
View File
@@ -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
*/
@@ -319,14 +314,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,
@@ -447,8 +438,6 @@ private slots:
void startExeAction();
void checkBSAList();
void updateProblemsButton();
void saveModMetas();
@@ -502,8 +491,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);
@@ -522,7 +509,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();
@@ -532,8 +518,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);
};
-111
View File
@@ -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"/>
+5 -3
View File
@@ -83,7 +83,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();
@@ -536,8 +536,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());
}
+5
View File
@@ -58,6 +58,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()
+41 -27
View File
@@ -990,6 +990,7 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString &
// TODO: should also pass arguments
if (m_AboutToRun(binary.absoluteFilePath())) {
m_USVFS.updateMapping(fileMapping());
return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true);
} else {
qDebug("start of \"%s\" canceled by plugin", qPrintable(binary.absoluteFilePath()));
@@ -1185,10 +1186,6 @@ void OrganizerCore::refreshBSAList()
m_ActiveArchives = m_DefaultArchives;
}
if (m_UserInterface != nullptr) {
m_UserInterface->updateBSAList(m_DefaultArchives, m_ActiveArchives);
}
m_ArchivesInit = true;
}
}
@@ -1247,9 +1244,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()));
@@ -1394,9 +1389,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();
@@ -1535,9 +1527,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()));
}
@@ -1571,21 +1560,30 @@ void OrganizerCore::prepareStart() {
storeSettings();
}
std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping()
std::vector<Mapping> OrganizerCore::fileMapping()
{
IPluginGame *game = qApp->property("managed_game").value<IPluginGame*>();
return fileMapping(game->dataDirectory().absolutePath(),
directoryStructure(),
directoryStructure());
IPluginGame *game = qApp->property("managed_game").value<IPluginGame *>();
MappingType result = fileMapping(
QDir::toNativeSeparators(game->dataDirectory().absolutePath()),
"\\",
directoryStructure(), directoryStructure());
for (MOBase::IPluginFileMapper *mapper : m_PluginContainer->plugins<MOBase::IPluginFileMapper>()) {
MappingType pluginMap = mapper->mappings();
result.reserve(result.size() + pluginMap.size());
result.insert(result.end(), pluginMap.begin(), pluginMap.end());
}
return result;
}
std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping(
std::vector<Mapping> OrganizerCore::fileMapping(
const QString &dataPath,
const QString &relPath,
const DirectoryEntry *base,
const DirectoryEntry *directoryEntry)
{
std::vector<std::pair<QString, QString>> result;
std::vector<Mapping> result;
for (FileEntry::Ptr current : directoryEntry->getFiles()) {
bool isArchive = false;
@@ -1594,20 +1592,36 @@ 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});
}
}
// 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();
if (origin == 0) {
continue;
}
QString originPath
= QString::fromStdWString(base->getOriginByID(origin).getPath());
QString dirName = QString::fromStdWString((*current)->getName());
QString source = originPath + relPath + dirName;
QString target = dataPath + relPath + dirName;
result.push_back({source, target, true});
std::vector<Mapping> subRes
= fileMapping(dataPath, relPath + dirName + "\\", base, *current);
result.insert(result.end(), subRes.begin(), subRes.end());
}
return result;
}
+10 -4
View File
@@ -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>
@@ -157,7 +158,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
@@ -210,9 +214,10 @@ 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);
private slots:
@@ -270,6 +275,7 @@ private:
bool m_ArchivesInit;
MOBase::DelayedFileWriter m_PluginListsWriter;
UsvfsConnector m_USVFS;
};
+7 -1
View File
@@ -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)) {
+3 -1
View File
@@ -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;
+3 -1
View File
@@ -585,7 +585,9 @@ bool Profile::invalidationActive(bool *supported) const
}
return false;
} else {
*supported = false;
if (supported != nullptr) {
*supported = false;
}
}
return false;
}
+56 -3
View File
@@ -117,9 +117,6 @@ SaveGameGamebryo::~SaveGameGamebryo()
}
void SaveGameGamebryo::readSkyrimFile(QFile &saveFile)
{
char fileID[14];
@@ -172,6 +169,58 @@ void SaveGameGamebryo::readSkyrimFile(QFile &saveFile)
}
}
void SaveGameGamebryo::readFO4File(QFile &saveFile)
{
char fileID[13];
saveFile.read(fileID, 12);
fileID[12] = '\0';
if (strncmp(fileID, "FO4_SAVEGAME", 12) != 0) {
throw std::runtime_error(QObject::tr("wrong file format").toUtf8().constData());
}
FileSkip<unsigned long>(saveFile); // header size
FileSkip<unsigned long>(saveFile); // header version, -> 11
FileRead(saveFile, m_SaveNumber);
m_PCName = ReadFOSString(saveFile, false);
unsigned long temp;
FileRead(saveFile, temp); // player level
m_PCLevel = static_cast<unsigned short>(temp);
m_PCLocation = ReadFOSString(saveFile, false);
ReadFOSString(saveFile, false); // playtime as ascii hhh.mm.ss
ReadFOSString(saveFile, false); // race name (HumanRace)
FileSkip<unsigned short>(saveFile); // ???
FileSkip<float>(saveFile, 2); // ???
FileSkip<unsigned char>(saveFile, 8); // filetime
// FileSkip<unsigned char>(saveFile, 18); // ??? 18 bytes of data. not completely random, maybe a time stamp? maybe
unsigned long width, height;
FileRead(saveFile, width); // 640
FileRead(saveFile, height); // 384
QScopedArrayPointer<unsigned char> buffer(new unsigned char[width * height * 4]);
saveFile.read(reinterpret_cast<char*>(buffer.data()), width * height * 4);
// 640x384 is a bit large
m_Screenshot = QImage(buffer.data(), width, height, QImage::Format_RGBA8888).scaledToWidth(320);
FileSkip<unsigned char>(saveFile); // form version (?)
ReadFOSString(saveFile, false); // game version
FileSkip<unsigned long>(saveFile); // plugin info size (?)
unsigned char pluginCount;
FileRead(saveFile, pluginCount);
for (int i = 0; i < pluginCount; ++i) {
m_Plugins.push_back(ReadFOSString(saveFile, false));
}
}
void SaveGameGamebryo::readESSFile(QFile &saveFile)
{
@@ -332,6 +381,10 @@ void SaveGameGamebryo::readFile(const QString &fileName)
setCreationTime(fileName);
readSkyrimFile(saveFile);
} break;
case GameInfo::TYPE_FALLOUT4: {
setCreationTime(fileName);
readFO4File(saveFile);
} break;
}
saveFile.close();
+1
View File
@@ -85,6 +85,7 @@ private:
void readESSFile(QFile &saveFile);
void readFOSFile(QFile &saveFile, bool newVegas);
void readFO4File(QFile &saveFile);
void readSkyrimFile(QFile &saveFile);
void setCreationTime(const QString &fileName);

Some files were not shown because too many files have changed in this diff Show More