Compare commits

...
Author SHA1 Message Date
Tannin af89733121 - bugfix: update.bsa is again treated like a regular bsa because hiding it from the archive-list made it impossible (in managed bsa mode) to overwrite files from update.bsa
- bugfix: disabled-state for mo management wasn't saved
- bugfix: when starting an application, settings were saved twice
- bugfix: origin ids and handles for directory updates were out of sync
2014-06-30 22:37:21 +02:00
Tannin baf1240be7 Added tag release v1.2.6 for changeset c40e9a70e99e 2014-06-29 15:09:55 +02:00
Tannin 6e4626852c - re-inserted code for bsa hiding (this was necessary to enforce expected bsa order after all)
- bsas from foreign bsas are now loaded as expected by hook.dll as well
- state of log-window-split is now saved
- auto completion in installer name boxes is now case-sensitive so that users can change casing of the mod names
- bugfix: self updater did enforce the skyrim url for MO updates but not the game_id
- bugfix: modinfo dialog didn't correctly read mod meta information on opening
- bugfix: MO didn't report on endorsement (for MO)
2014-06-29 15:09:45 +02:00
Tannin e8f56d65d2 Added tag release v1.2.5 for changeset 70663ff89a38 2014-06-25 23:51:39 +02:00
Tannin 5e7c875b29 - test-plugins accidentally distributed in the 1.2.5 release are now automatically deleted
- some fixes to the tutorial (mostly wording but also some actual problems)
- bugfix: could disable mods that aren't supposed to be disable-able
- bugfix: mod list wasn't saved on some changes
- bugfix: when trying to start directly to application, MO didn't start up correctly
- bugfix: hook.dll didn't load mod bsas into its directory structure and thus didn't allow bsas to overwrite loose files
- bugfix: in some constellations the tutorial files couldn't be found despite being in the right location
2014-06-25 23:51:29 +02:00
Tannin b2a7c7c223 Merge 2014-06-23 21:43:40 +02:00
Tannin 7b4582c1b1 - adjusted mod load order in hook.dll to match the one from the UI
- bugfix: fnis checker took files in archives into consideration for hashing
2014-06-23 20:23:59 +02:00
Tannin 5d7b23acef - bugfix: networkaccessmanager of browser dialog wasn't destroyed correctly. As a result the cookie jar doesn't get saved 2014-06-21 12:16:57 +02:00
Tannin 3628ec930b - changed the url used to login on nexus (the previous page was disabled)
- bugfix: download manager might have tried to read from a closed connection
2014-06-21 10:39:28 +02:00
Tannin e22aeca3ff - download read buffer is now limited to 1MB. Might prevent out-of-memory errors during download
- bugfix: plugins are now disabled when deactivating a mod  before the files are removed from the vfs
2014-06-17 18:36:28 +02:00
Tannin cbd6b288eb Merge 2014-06-16 21:50:53 +02:00
Tannin 484749e612 - added pseudo-categories to filter for mo-managed vs. unmanaged mods
- deleted mods are now moved to the recycle bin instead of being deleted permanently
- reduced modinfo dialog for foreign mods
- foreign mods are now displayed with a pseudo-category
- added a signal when a plugin is moved
- bugfix: refreshing of directory structure for mods with bsas didn't work correctly when enabling a mod and on changing display of foreign mods
- bugfix: one mod in the list was assigned the same priority on the directory structure as the data directory
- bugfix: conflicts tab in the mod info dialog offered the hide option for files in bsas
2014-06-16 21:49:57 +02:00
Tannin 5b3cdaa83b bugfix: the warning dialog upon changing the mod directory didn't have an effect, the process couldn't be canceled 2014-06-14 09:17:57 +02:00
Tannin 6d3f88b330 Added tag release v1.2.3 for changeset c503e48f97aa 2014-06-08 15:10:21 +02:00
Tannin 0feb4b702d - loot client now only updates the masterlist once per MO session
- new event to notify plugins of changed mod priority
- overwrite now shows up in the "checked" category instead of "unchecked"
- display of "foreign" mods can now be limited to only official content
- bugfix: bsa extraction dialog showed up even if the plugin was disabled
- bugfix: after detection of foreign mods the priority of the overwrite folder could get messed up
- bugfix: when displaying only the context menu for the mod list as a whole, the menu didn't disappear
- bugfix: MO crashed when trying to download via the integrated browser
2014-06-08 15:10:08 +02:00
Tannin 5a4b6e70fd - plugin-list now displays loot messages 2014-06-02 19:15:23 +02:00
Tannin 18574c2ba8 - removed needless modlist-rewrite 2014-05-31 13:44:26 +02:00
Tannin 1eb783aae3 - added a new mod type that represents files handled externally (i.e. DLCs) as mods in MO
- hashes of file names in bsa files are no longer checked all the time
- author and description is now read from esp files
- rewrote the code that fixes modlists after a rename, should be a bit more robust
- fixes to qt 5 and msvc 2013 compatibility
- started to update the tutorial (not done yet!)
- bugfix: counter for the problems badge wasn't calculated correctly
2014-05-31 13:43:48 +02:00
Tannin 93a19799b8 Merge 2014-05-28 14:23:11 +02:00
Tannin 2a31eb40fb small bugfixes 2014-05-28 14:14:28 +02:00
Tannin cf0a1bc2be - files in bsas are now only displayed in the data tab if they are managed by mo
- number of problems detected by MO is now displayed as a badge on the icon
- rephrased the explanation text on the Archives tab. unchecked plugin-loaded bsas no longer prompt a warning
- bsa extraction is now handled in a plugin
- added a way for plugins to react to mod installation
- re-enabled the automatic fix for asset order problems
- bugfix: In some cases when a download wasn't started successfully the download urls weren't stored in the meta file so no resume was possible
- bugfix: MO tried to resume downloads when it didn't have and download urls
- bugfix: downloads couldn't be paused if the download was already broken on the network layer
- bugfix: download managear did not recognize a file as downloaded if the download completed before signals were hooked up
- bugfix: in-place file replacement was re-broken
2014-05-25 15:39:45 +02:00
Tannin 48c8cca578 - NCC will now report an error if a script tries to extract a non-existent file instead of creating an empty output file
- ncc installer plugin now uses a more reliable method to force the installer window to the foreground
- missing version on TESV.exe will no longer be reported as an error
- bugfix: loot client didn't read list of active mods
- bugfix: invalid free call in error reporting function
2014-05-15 19:11:19 +02:00
Tannin 977b407525 - mod list context menu split into two menus (one for whole list, one for selected mods)
- added option to combine category filters using "or"
- added context menu option for deselecting category filters
- slightly changed ui on the category filters
- added a sample plugin for cpp that can be built without building the rest of MO
- simple installer can now be configured to run without any user interaction
- extended interface for python plugins
- iorganizer implementation moved out of the main window
- nexus requests from plugins will now be identified in the user agent
- bugfix: shortcuts created from MO used the wrong working directory
- bugfix: deactivation of bsas didn't stick
- bugfix: file hiding mechanism wasn't active
- bugfix: executables linked on the toolbar couldn't be removed if the executable was removed first
- bugfix: the endorsement-filter couldn't be combined with other filters
- bugfix: python interface to repository bridge was broken
2014-05-13 21:20:44 +02:00
Tannin f4b1aba61a Added tag release v1.2.1 for changeset 366d0a6ac409 2014-05-06 21:07:34 +02:00
Tannin 0eb1662a0e - very effective optimization to findfirstfile-calls
- several configuration files are now only saved to disk if the content actually changed.
  This should also get rid of a problem where plugins.txt was re-written immediately after starting the game
  (causing a conflict with the game)
- reduced "noise" from hook.dll
- removed some debugging messages
2014-05-05 18:24:15 +02:00
Tannin 164ec25a75 - bugfix: endless loop in detection of mod order problems 2014-05-04 16:13:35 +02:00
Tannin 6fb36d6c02 - main window now has a small view displaying log messages
- mod list will now be highlighted when grouping is active is active
- download tooltip now supports bbcode markup in the description
- bbcode translator will now translate some named colors
- algorithm for detection of mod order problems is now more sophisticated
- exposed more functionality to python plugins
- updated to qt 4.8.6 dlls
- bugfix: plugin list wasn't
- bugfix: state changes in mod list wasn't always reported
- bugfix: loot client will now create necessary directory
- bugfix: NCC sometimes used wrong source path for extracting
- bugfix: removed noisy debug message
2014-05-04 14:50:01 +02:00
Tannin ea1f959ad5 - download tooltip now also includes the file description
- will now display an error message when the ini file can't be updated (in addition to what windows says)
2014-05-01 10:03:40 +02:00
Tannin 7cf3b3455b Added tag release v1.2.0 for changeset cc9f6dd8ee3f 2014-04-25 20:02:53 +02:00
83 changed files with 16601 additions and 25153 deletions
+13
View File
@@ -18,5 +18,18 @@ staging_prepare/*
staging_trans/*
tools/python_zip/*
Makefile
html
*.vcxproj
*.pdb
*.dll
*.exp
*.tlog
*.user
*.obj
*.suo
*.sln
*.log
*.filters
*.lib
syntax: regexp
Makefile\.(Debug|Release)
+33 -10
View File
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QRegExp>
#include <map>
#include <algorithm>
#include <boost/assign.hpp>
namespace BBCode {
@@ -43,7 +44,6 @@ public:
// extract the tag name
m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset);
QString tagName = m_TagNameExp.cap(0).toLower();
//qDebug("tag name %s", tagName.toUtf8().constData());
TagMap::iterator tagIter = m_TagMap.find(tagName);
if (tagIter != m_TagMap.end()) {
// recognized tag
@@ -60,7 +60,21 @@ public:
length = closeTagPos + closeTag.length();
QString temp = input.mid(0, length);
if (tagIter->second.first.indexIn(temp) == 0) {
return temp.replace(tagIter->second.first, tagIter->second.second);
if (tagIter->second.second.isEmpty()) {
if (tagName == "color") {
QString color = tagIter->second.first.cap(1);
QString content = tagIter->second.first.cap(2);
auto colIter = m_ColorMap.find(color.toLower());
if (colIter != m_ColorMap.end()) {
color = colIter->second;
}
return temp.replace(tagIter->second.first, QString("<font style=\"color: #%1;\">%2</font>").arg(color, content));
} else {
qWarning("don't know how to deal with tag %s", qPrintable(tagName));
}
} else {
return temp.replace(tagIter->second.first, tagIter->second.second);
}
} else {
// expression doesn't match. either the input string is invalid
// or the expression is
@@ -96,7 +110,7 @@ private:
m_TagMap["size="] = std::make_pair(QRegExp("\\[size=([^\\]]*)\\](.*)\\[/size\\]"),
"<font size=\"\\1\">\\2</font>");
m_TagMap["color="] = std::make_pair(QRegExp("\\[color=([^\\]]*)\\](.*)\\[/color\\]"),
"<font style=\"color: #\\1;\">\\2</font>");
"");
m_TagMap["font="] = std::make_pair(QRegExp("\\[font=([^\\]]*)\\](.*)\\[/font\\]"),
"<font face=\\1>\\2</font>");
m_TagMap["center"] = std::make_pair(QRegExp("\\[center\\](.*)\\[/center\\]"),
@@ -139,17 +153,25 @@ private:
"<a href=\"\\1\">\\1</a>");
m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"),
"<a href=\"\\1\">\\2</a>");
/* m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"),
"<img src=\"\\1\"/>");
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"),
"<img src=\"\\2\" align=\"\\1\" />");*/
m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), "");
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), "");
m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), " ");
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), " ");
m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"),
"<a href=\"mailto:\\1\">\\2</a>");
m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"),
"<a href=\"http://www.youtube.com/v/\\1\">http://www.youtube.com/v/\\1</a>");
m_ColorMap.insert(std::make_pair<QString, QString>("red", "FF0000"));
m_ColorMap.insert(std::make_pair<QString, QString>("green", "00FF00"));
m_ColorMap.insert(std::make_pair<QString, QString>("blue", "0000FF"));
m_ColorMap.insert(std::make_pair<QString, QString>("black", "000000"));
m_ColorMap.insert(std::make_pair<QString, QString>("gray", "7F7F7F"));
m_ColorMap.insert(std::make_pair<QString, QString>("white", "FFFFFF"));
m_ColorMap.insert(std::make_pair<QString, QString>("yellow", "FFFF00"));
m_ColorMap.insert(std::make_pair<QString, QString>("cyan", "00FFFF"));
m_ColorMap.insert(std::make_pair<QString, QString>("magenta", "FF00FF"));
m_ColorMap.insert(std::make_pair<QString, QString>("brown", "A52A2A"));
m_ColorMap.insert(std::make_pair<QString, QString>("orange", "FFCC00"));
// make all patterns non-greedy and case-insensitive
for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) {
iter->second.first.setCaseSensitivity(Qt::CaseInsensitive);
@@ -157,10 +179,11 @@ private:
}
}
private:
QRegExp m_TagNameExp;
TagMap m_TagMap;
std::map<QString, QString> m_ColorMap;
};
+8 -8
View File
@@ -43,7 +43,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
BrowserDialog::BrowserDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::BrowserDialog)
, m_AccessManager(new QNetworkAccessManager)
, m_AccessManager(new QNetworkAccessManager(this))
{
ui->setupUi(this);
@@ -255,16 +255,16 @@ void BrowserDialog::on_searchEdit_returnPressed()
}
}
void BrowserDialog::on_browserTabWidget_currentChanged(QWidget *current)
void BrowserDialog::on_refreshBtn_clicked()
{
BrowserView *currentView = qobject_cast<BrowserView*>(current);
getCurrentView()->reload();
}
void BrowserDialog::on_browserTabWidget_currentChanged(int index)
{
BrowserView *currentView = qobject_cast<BrowserView*>(ui->browserTabWidget->widget(index));
if (currentView != NULL) {
ui->backBtn->setEnabled(currentView->history()->canGoBack());
ui->fwdBtn->setEnabled(currentView->history()->canGoForward());
}
}
void BrowserDialog::on_refreshBtn_clicked()
{
getCurrentView()->reload();
}
+2 -2
View File
@@ -99,10 +99,10 @@ private slots:
void startSearch();
void on_browserTabWidget_currentChanged(QWidget *arg1);
void on_refreshBtn_clicked();
void on_browserTabWidget_currentChanged(int index);
private:
QString guessFileName(const QString &url);
+2
View File
@@ -47,6 +47,8 @@ public:
static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
static const int CATEGORY_SPECIAL_CONFLICT = 10004;
static const int CATEGORY_SPECIAL_NOTENDORSED = 10005;
static const int CATEGORY_SPECIAL_MANAGED = 10006;
static const int CATEGORY_SPECIAL_UNMANAGED = 10007;
public:
+62 -18
View File
@@ -20,8 +20,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "directoryrefresher.h"
#include "utility.h"
#include "report.h"
#include "modinfo.h"
#include <gameinfo.h>
#include <QDir>
#include <QString>
using namespace MOBase;
@@ -46,12 +48,20 @@ DirectoryEntry *DirectoryRefresher::getDirectoryStructure()
return result;
}
void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods)
void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods
, const std::set<QString> &managedArchives)
{
QMutexLocker locker(&m_RefreshLock);
m_Mods = mods;
}
m_Mods.clear();
for (auto mod = mods.begin(); mod != mods.end(); ++mod) {
QString name = std::get<0>(*mod);
ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(name));
m_Mods.push_back(EntryInfo(name, std::get<1>(*mod), info->stealFiles(), info->archives(), std::get<2>(*mod)));
}
m_EnabledArchives = managedArchives;
}
void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
{
@@ -61,24 +71,59 @@ void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
}
static wchar_t *dirs[] = { L"fomod" };
for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
structure->removeDir(dirs[i]);
for (int i = 0; i < sizeof(dirs) / sizeof(wchar_t*); ++i) {
structure->removeDir(std::wstring(dirs[i]));
}
}
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory)
void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives)
{
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
QDir dir(directory);
QFileInfoList bsaFiles = dir.entryInfoList(QStringList("*.bsa"), QDir::Files);
foreach (QFileInfo file, bsaFiles) {
directoryStructure->addFromBSA(ToWString(modName), directoryW,
ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority);
foreach (const QString &archive, archives) {
QFileInfo fileInfo(archive);
if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) {
directoryStructure->addFromBSA(ToWString(modName), directoryW,
ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority);
}
}
}
void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles)
{
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
if (stealFiles.length() > 0) {
// instead of adding all the files of the target directory, we just change the root of the specified
// files to this mod
FilesOrigin origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
foreach (const QString &filename, stealFiles) {
QFileInfo fileInfo(filename);
FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName()));
if (file.get() != NULL) {
if (file->getOrigin() == 0) {
// replace data as the origin on this bsa
file->removeOrigin(0);
file->addOrigin(origin.getID(), file->getFileTime(), L"");
}
}
}
} else {
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
}
}
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure
, const QString &modName
, int priority
, const QString &directory
, const QStringList &stealFiles
, const QStringList &archives)
{
addModFilesToStructure(directoryStructure, modName, priority, directory, stealFiles);
addModBSAToStructure(directoryStructure, modName, priority, directory, archives);
}
void DirectoryRefresher::refresh()
{
QMutexLocker locker(&m_RefreshLock);
@@ -87,23 +132,22 @@ void DirectoryRefresher::refresh()
m_DirectoryStructure = new DirectoryEntry(L"data", NULL, 0);
std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
// TODO what was the point of having the priority in this tuple? the list is already sorted by priority
std::vector<std::tuple<QString, QString, int> >::const_iterator iter = m_Mods.begin();
auto iter = m_Mods.begin();
//TODO i is the priority here, where higher = more important. the input vector is also sorted by priority but inverted!
for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
QString modName = std::get<0>(*iter);
try {
addModToStructure(m_DirectoryStructure, modName, i, std::get<1>(*iter));
addModToStructure(m_DirectoryStructure, iter->modName, i, iter->absolutePath, iter->stealFiles, iter->archives);
} catch (const std::exception &e) {
emit error(tr("failed to read bsa: %1").arg(e.what()));
}
emit progress((i * 100) / m_Mods.size() + 1);
}
std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
emit progress(100);
cleanStructure(m_DirectoryStructure);
+48 -9
View File
@@ -20,11 +20,13 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#ifndef DIRECTORYREFRESHER_H
#define DIRECTORYREFRESHER_H
#include <QObject>
#include <vector>
#include <QMutex>
#include <tuple>
#include <directoryentry.h>
#include <QObject>
#include <QMutex>
#include <QStringList>
#include <vector>
#include <set>
#include <tuple>
/**
@@ -60,7 +62,7 @@ public:
*
* @param mods list of the mods to include
**/
void setMods(const std::vector<std::tuple<QString, QString, int> > &mods);
void setMods(const std::vector<std::tuple<QString, QString, int> > &mods, const std::set<QString> &managedArchives);
/**
* @brief sets up the directory where mods are stored
@@ -77,12 +79,34 @@ public:
/**
* @brief add files for a mod to the directory structure, including bsas
* @param directoryStructure
* @param modName
* @param priorityBSA
* @param priority
* @param directory
* @param priorityDir
* @param stealFiles
* @param archives
*/
static void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory);
void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles, const QStringList &archives);
/**
* @brief add only the bsas of a mod to the directory structure
* @param directoryStructure
* @param modName
* @param priority
* @param directory
* @param archives
*/
void addModBSAToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives);
/**
* @brief add only regular files ofr a mod to the directory structure
* @param directoryStructure
* @param modName
* @param priority
* @param directory
* @param stealFiles
*/
void addModFilesToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles);
public slots:
@@ -99,7 +123,22 @@ signals:
private:
std::vector<std::tuple<QString, QString, int> > m_Mods;
struct EntryInfo {
EntryInfo(const QString &modName, const QString &absolutePath,
const QStringList &stealFiles, const QStringList &archives, int priority)
: modName(modName), absolutePath(absolutePath), stealFiles(stealFiles)
, archives(archives), priority(priority) {}
QString modName;
QString absolutePath;
QStringList stealFiles;
QStringList archives;
int priority;
};
private:
std::vector<EntryInfo> m_Mods;
std::set<QString> m_EnabledArchives;
MOShared::DirectoryEntry *m_DirectoryStructure;
QMutex m_RefreshLock;
+4 -4
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="icuin49.dll"/>
<file name="icuuc49.dll"/>
<file name="icudt49.dll"/>
<file name="icuin52.dll"/>
<file name="icuuc52.dll"/>
<file name="icudt52.dll"/>
<file name="Qt5Core.dll"/>
<file name="Qt5Declarative.dll"/>
<file name="Qt5Gui.dll"/>
@@ -14,4 +14,4 @@
<file name="Qt5Widgets.dll"/>
<file name="Qt5Xml.dll"/>
<file name="Qt5XmlPatterns.dll"/>
</assembly>
</assembly>
+1 -1
View File
@@ -82,7 +82,7 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const
text += tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
} else {
const MOBase::ModRepositoryFileInfo *info = m_Manager->getFileInfo(index.row());
return QString("%1 (ID %2) %3").arg(info->modName).arg(m_Manager->getModID(index.row())).arg(info->version.canonicalString());
return QString("%1 (ID %2) %3<br><span>%4</span>").arg(info->modName).arg(m_Manager->getModID(index.row())).arg(info->version.canonicalString()).arg(info->description);
}
return text;
} else {
+123 -84
View File
@@ -26,6 +26,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "utility.h"
#include "json.h"
#include "selectiondialog.h"
#include "bbcode.h"
#include <utility.h>
#include <QTimer>
#include <QFileInfo>
@@ -147,7 +148,10 @@ void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile)
metaFile.rename(newName.mid(0).append(".meta"));
}
}
m_Output.setFileName(newName);
if (!m_Output.isOpen()) {
// can't set file name if it's open
m_Output.setFileName(newName);
}
}
bool DownloadManager::DownloadInfo::isPausedState()
@@ -252,67 +256,70 @@ void DownloadManager::setShowHidden(bool showHidden)
void DownloadManager::refreshList()
{
int downloadsBefore = m_ActiveDownloads.size();
try {
int downloadsBefore = m_ActiveDownloads.size();
// remove finished downloads
for (QVector<DownloadInfo*>::iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end();) {
if (((*Iter)->m_State == STATE_READY) || ((*Iter)->m_State == STATE_INSTALLED) || ((*Iter)->m_State == STATE_UNINSTALLED)) {
delete *Iter;
Iter = m_ActiveDownloads.erase(Iter);
} else {
++Iter;
}
}
QStringList nameFilters(m_SupportedExtensions);
foreach (const QString &extension, m_SupportedExtensions) {
nameFilters.append("*." + extension);
}
nameFilters.append(QString("*").append(UNFINISHED));
QDir dir(QDir::fromNativeSeparators(m_OutputDirectory));
// find orphaned meta files and delete them (sounds cruel but it's better for everyone)
QStringList orphans;
QStringList metaFiles = dir.entryList(QStringList() << "*.meta");
foreach (const QString &metaFile, metaFiles) {
QString baseFile = metaFile.left(metaFile.length() - 5);
if (!QFile::exists(dir.absoluteFilePath(baseFile))) {
orphans.append(dir.absoluteFilePath(metaFile));
}
}
if (orphans.size() > 0) {
qDebug("%d orphaned meta files will be deleted", orphans.size());
shellDelete(orphans, true);
}
// add existing downloads to list
foreach (QString file, dir.entryList(nameFilters, QDir::Files, QDir::Time)) {
bool Exists = false;
for (QVector<DownloadInfo*>::const_iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end() && !Exists; ++Iter) {
if (QString::compare((*Iter)->m_FileName, file, Qt::CaseInsensitive) == 0) {
Exists = true;
} else if (QString::compare(QFileInfo((*Iter)->m_Output.fileName()).fileName(), file, Qt::CaseInsensitive) == 0) {
Exists = true;
// remove finished downloads
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end();) {
if (((*iter)->m_State == STATE_READY) || ((*iter)->m_State == STATE_INSTALLED) || ((*iter)->m_State == STATE_UNINSTALLED)) {
delete *iter;
iter = m_ActiveDownloads.erase(iter);
} else {
++iter;
}
}
if (Exists) {
qDebug("%s exists", qPrintable(file));
continue;
QStringList nameFilters(m_SupportedExtensions);
foreach (const QString &extension, m_SupportedExtensions) {
nameFilters.append("*." + extension);
}
QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file;
nameFilters.append(QString("*").append(UNFINISHED));
QDir dir(QDir::fromNativeSeparators(m_OutputDirectory));
DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden);
if (info != NULL) {
m_ActiveDownloads.push_front(info);
// find orphaned meta files and delete them (sounds cruel but it's better for everyone)
QStringList orphans;
QStringList metaFiles = dir.entryList(QStringList() << "*.meta");
foreach (const QString &metaFile, metaFiles) {
QString baseFile = metaFile.left(metaFile.length() - 5);
if (!QFile::exists(dir.absoluteFilePath(baseFile))) {
orphans.append(dir.absoluteFilePath(metaFile));
}
}
if (orphans.size() > 0) {
qDebug("%d orphaned meta files will be deleted", orphans.size());
shellDelete(orphans, true);
}
}
if (m_ActiveDownloads.size() != downloadsBefore) {
qDebug("downloads after refresh: %d", m_ActiveDownloads.size());
// add existing downloads to list
foreach (QString file, dir.entryList(nameFilters, QDir::Files, QDir::Time)) {
bool Exists = false;
for (QVector<DownloadInfo*>::const_iterator Iter = m_ActiveDownloads.begin(); Iter != m_ActiveDownloads.end() && !Exists; ++Iter) {
if (QString::compare((*Iter)->m_FileName, file, Qt::CaseInsensitive) == 0) {
Exists = true;
} else if (QString::compare(QFileInfo((*Iter)->m_Output.fileName()).fileName(), file, Qt::CaseInsensitive) == 0) {
Exists = true;
}
}
if (Exists) {
continue;
}
QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file;
DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden);
if (info != NULL) {
m_ActiveDownloads.push_front(info);
}
}
if (m_ActiveDownloads.size() != downloadsBefore) {
qDebug("downloads after refresh: %d", m_ActiveDownloads.size());
}
emit update(-1);
} catch (const std::bad_alloc&) {
reportError(tr("Memory allocation error (in refreshing directory)."));
}
emit update(-1);
}
@@ -391,6 +398,7 @@ void DownloadManager::removePending(int modID, int fileID)
void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume)
{
reply->setReadBufferSize(1024 * 1024); // don't read more than 1MB at once to avoid memory troubles
newDownload->m_Reply = reply;
setState(newDownload, STATE_DOWNLOADING);
if (newDownload->m_Urls.count() == 0) {
@@ -403,6 +411,7 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
}
newDownload->m_StartTime.start();
createMetaFile(newDownload);
if (!newDownload->m_Output.open(mode)) {
reportError(tr("failed to download %1: could not open output file: %2")
@@ -412,6 +421,7 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
connect(newDownload->m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64)));
connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
connect(newDownload->m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
connect(newDownload->m_Reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
connect(newDownload->m_Reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
@@ -420,11 +430,15 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
removePending(newDownload->m_FileInfo->modID, newDownload->m_FileInfo->fileID);
emit aboutToUpdate();
m_ActiveDownloads.append(newDownload);
emit update(-1);
emit downloadAdded();
if (reply->isFinished()) {
// it's possible the download has already finished before this function ran
downloadFinished();
}
}
}
@@ -447,7 +461,7 @@ void DownloadManager::addNXMDownload(const QString &url)
emit update(-1);
emit downloadAdded();
m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId()));
m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId(), ""));
}
@@ -592,8 +606,11 @@ void DownloadManager::pauseDownload(int index)
DownloadInfo *info = m_ActiveDownloads.at(index);
if (info->m_State == STATE_DOWNLOADING) {
setState(info, STATE_PAUSING);
qDebug("pausing %d - %s", index, info->m_FileName.toUtf8().constData());
if (info->m_Reply->isRunning()) {
setState(info, STATE_PAUSING);
} else {
setState(info, STATE_PAUSED);
}
} else if ((info->m_State == STATE_FETCHINGMODINFO) || (info->m_State == STATE_FETCHINGFILEINFO)) {
setState(info, STATE_READY);
}
@@ -618,6 +635,11 @@ void DownloadManager::resumeDownloadInt(int index)
}
DownloadInfo *info = m_ActiveDownloads[index];
if (info->isPausedState()) {
if ((info->m_Urls.size() == 0)
|| ((info->m_Urls.size() == 1) && (info->m_Urls[0].size() == 0))) {
emit showMessage(tr("No known download urls. Sorry, this download can't be resumed."));
return;
}
if (info->m_State == STATE_ERROR) {
info->m_CurrentUrl = (info->m_CurrentUrl + 1) % info->m_Urls.count();
}
@@ -918,10 +940,10 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana
info->m_Reply->abort();
} break;
case STATE_FETCHINGMODINFO: {
m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->modID, this, info->m_DownloadID));
m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->modID, this, info->m_DownloadID, QString()));
} break;
case STATE_FETCHINGFILEINFO: {
m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->modID, this, info->m_DownloadID));
m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->modID, this, info->m_DownloadID, QString()));
} break;
case STATE_READY: {
createMetaFile(info);
@@ -954,32 +976,40 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
return;
}
int index = 0;
DownloadInfo *info = findDownload(this->sender(), &index);
if (info != NULL) {
if (info->m_State == STATE_CANCELING) {
setState(info, STATE_CANCELED);
} else if (info->m_State == STATE_PAUSING) {
setState(info, STATE_PAUSED);
} else {
if (bytesTotal > info->m_TotalSize) {
info->m_TotalSize = bytesTotal;
}
int oldProgress = info->m_Progress;
info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal);
TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal);
if (oldProgress != info->m_Progress) {
emit update(index);
try {
DownloadInfo *info = findDownload(this->sender(), &index);
if (info != NULL) {
if (info->m_State == STATE_CANCELING) {
setState(info, STATE_CANCELED);
} else if (info->m_State == STATE_PAUSING) {
setState(info, STATE_PAUSED);
} else {
if (bytesTotal > info->m_TotalSize) {
info->m_TotalSize = bytesTotal;
}
int oldProgress = info->m_Progress;
info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal);
TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal);
if (oldProgress != info->m_Progress) {
emit update(index);
}
}
}
} catch (const std::bad_alloc&) {
reportError(tr("Memory allocation error (in processing progress event)."));
}
}
void DownloadManager::downloadReadyRead()
{
DownloadInfo *info = findDownload(this->sender());
if (info != NULL) {
info->m_Output.write(info->m_Reply->readAll());
try {
DownloadInfo *info = findDownload(this->sender());
if (info != NULL) {
info->m_Output.write(info->m_Reply->readAll());
}
} catch (const std::bad_alloc&) {
reportError(tr("Memory allocation error (in processing downloaded data)."));
}
}
@@ -1159,14 +1189,14 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD
info->fileName = result["uri"].toString();
info->fileCategory = result["category_id"].toInt();
info->fileTime = matchDate(result["date"].toString());
info->description = result["description"].toString();
info->description = BBCode::convertToHTML(result["description"].toString());
info->repository = "Nexus";
info->modID = modID;
info->fileID = fileID;
QObject *test = info;
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test)));
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test), QString()));
}
@@ -1266,7 +1296,6 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
foreach (const QVariant &server, resultList) {
URLs.append(server.toMap()["URI"].toString());
}
addDownload(URLs, modID, fileID, info);
}
@@ -1308,8 +1337,11 @@ void DownloadManager::downloadFinished()
DownloadInfo *info = findDownload(this->sender(), &index);
if (info != NULL) {
QNetworkReply *reply = info->m_Reply;
QByteArray data = info->m_Reply->readAll();
info->m_Output.write(data);
QByteArray data;
if (reply->isOpen()) {
data = reply->readAll();
info->m_Output.write(data);
}
info->m_Output.close();
TaskProgressManager::instance().forgetMe(info->m_TaskProgressId);
@@ -1355,7 +1387,6 @@ void DownloadManager::downloadFinished()
createMetaFile(info);
emit update(index);
} else {
QString url = info->m_Urls[info->m_CurrentUrl];
if (info->m_FileInfo->userData.contains("downloadMap")) {
foreach (const QVariant &server, info->m_FileInfo->userData["downloadMap"].toList()) {
@@ -1405,6 +1436,14 @@ void DownloadManager::downloadFinished()
}
void DownloadManager::downloadError(QNetworkReply::NetworkError error)
{
if (error != QNetworkReply::OperationCanceledError) {
qWarning("Download error occured: %d", error);
}
}
void DownloadManager::metaDataChanged()
{
int index = 0;
@@ -1415,7 +1454,7 @@ void DownloadManager::metaDataChanged()
if (!newName.isEmpty() && (newName != info->m_FileName)) {
info->setName(getDownloadFileName(newName), true);
refreshAlphabeticalTranslation();
if (!info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) {
if (!info->m_Output.isOpen() && !info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) {
reportError(tr("failed to re-open %1").arg(info->m_FileName));
setState(info, STATE_CANCELING);
}
+1
View File
@@ -417,6 +417,7 @@ private slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void downloadReadyRead();
void downloadFinished();
void downloadError(QNetworkReply::NetworkError error);
void metaDataChanged();
void directoryChanged(const QString &dirctory);
+3 -2
View File
@@ -522,9 +522,10 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
return false;
}
QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory.mid(0).append("\\").append(modName));
QString targetDirectoryNative = m_ModsDirectory.mid(0).append("\\").append(modName);
QString targetDirectory = QDir::fromNativeSeparators(targetDirectoryNative);
qDebug("installing to \"%s\"", targetDirectory.toUtf8().constData());
qDebug("installing to \"%s\"", targetDirectoryNative.toUtf8().constData());
m_InstallationProgress.setWindowTitle(tr("Extracting files"));
m_InstallationProgress.setLabelText(QString());
+106 -18
View File
@@ -21,6 +21,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "report.h"
#include <QMutexLocker>
#include <QFile>
#include <QIcon>
#include <QDateTime>
#include <Windows.h>
@@ -29,7 +30,7 @@ QMutex LogBuffer::s_Mutex;
LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName)
: QObject(NULL), m_OutFileName(outputFileName), m_ShutDown(false),
: QAbstractItemModel(NULL), m_OutFileName(outputFileName), m_ShutDown(false),
m_MinMsgType(minMsgType), m_NumMessages(0)
{
m_Messages.resize(messageCount);
@@ -51,7 +52,16 @@ LogBuffer::~LogBuffer()
void LogBuffer::logMessage(QtMsgType type, const QString &message)
{
if (type >= m_MinMsgType) {
m_Messages.at(m_NumMessages % m_Messages.size()) = message;
Message msg = { type, QTime::currentTime(), message };
if (m_NumMessages < m_Messages.size()) {
beginInsertRows(QModelIndex(), m_NumMessages, m_NumMessages + 1);
}
m_Messages.at(m_NumMessages % m_Messages.size()) = msg;
if (m_NumMessages < m_Messages.size()) {
endInsertRows();
} else {
emit dataChanged(createIndex(0, 0), createIndex(m_Messages.size(), 0));
}
++m_NumMessages;
if (type >= QtCriticalMsg) {
write();
@@ -77,7 +87,7 @@ void LogBuffer::write() const
unsigned int i = (m_NumMessages > m_Messages.size()) ? m_NumMessages - m_Messages.size()
: 0U;
for (; i < m_NumMessages; ++i) {
file.write(m_Messages.at(i % m_Messages.size()).toUtf8());
file.write(m_Messages.at(i % m_Messages.size()).toString().toUtf8());
file.write("\r\n");
}
::SetLastError(lastError);
@@ -99,21 +109,6 @@ void LogBuffer::init(int messageCount, QtMsgType minMsgType, const QString &outp
#endif
}
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
QMutexLocker guard(&s_Mutex);
if (!s_Instance.isNull()) {
s_Instance->logMessage(type, message);
}
fprintf(stdout, "(%s:%u) %s\n", context.file, context.line, qPrintable(message));
fflush(stdout);
}
#else
char LogBuffer::msgTypeID(QtMsgType type)
{
switch (type) {
@@ -125,6 +120,26 @@ 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 guard(&s_Mutex);
if (!s_Instance.isNull()) {
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 {
fprintf(stdout, "%s [%c] (%s:%u) %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type),
context.file, context.line, qPrintable(message));
}
fflush(stdout);
}
#else
void LogBuffer::log(QtMsgType type, const char *message)
{
QMutexLocker guard(&s_Mutex);
@@ -137,6 +152,73 @@ void LogBuffer::log(QtMsgType type, const char *message)
#endif
QModelIndex LogBuffer::index(int row, int column, const QModelIndex&) const
{
return createIndex(row, column, row);
}
QModelIndex LogBuffer::parent(const QModelIndex&) const
{
return QModelIndex();
}
int LogBuffer::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
else
return std::min(m_NumMessages, m_Messages.size());
}
int LogBuffer::columnCount(const QModelIndex&) const
{
return 2;
}
QVariant LogBuffer::data(const QModelIndex &index, int role) const
{
unsigned offset = m_NumMessages < m_Messages.size() ? 0
: m_NumMessages - m_Messages.size();
unsigned int msgIndex = (offset + index.row() + 1) % m_Messages.size();
switch (role) {
case Qt::DisplayRole: {
if (index.column() == 0) {
return m_Messages.at(msgIndex).time;
} else if (index.column() == 1) {
const QString &msg = m_Messages.at(msgIndex).message;
if (msg.length() < 200) {
return msg;
} else {
return msg.mid(0, 200) + "...";
}
}
} break;
case Qt::DecorationRole: {
if (index.column() == 1) {
switch (m_Messages.at(msgIndex).type) {
case QtDebugMsg: return QIcon(":/MO/gui/information");
case QtWarningMsg: return QIcon(":/MO/gui/warning");
case QtCriticalMsg: return QIcon(":/MO/gui/important");
case QtFatalMsg: return QIcon(":/MO/gui/problem");
}
}
} break;
case Qt::UserRole: {
if (index.column() == 1) {
switch (m_Messages.at(msgIndex).type) {
case QtDebugMsg: return "D";
case QtWarningMsg: return "W";
case QtCriticalMsg: return "C";
case QtFatalMsg: return "F";
}
}
} break;
}
return QVariant();
}
void LogBuffer::writeNow()
{
QMutexLocker guard(&s_Mutex);
@@ -171,3 +253,9 @@ 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);
}
+23 -2
View File
@@ -23,10 +23,12 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QObject>
#include <QMutex>
#include <QScopedPointer>
#include <QStringListModel>
#include <QTime>
#include <vector>
class LogBuffer : public QObject
class LogBuffer : public QAbstractItemModel
{
Q_OBJECT
@@ -42,12 +44,22 @@ public:
static void writeNow();
static void cleanQuit();
static LogBuffer *instance() { return s_Instance.data(); }
public:
virtual ~LogBuffer();
void logMessage(QtMsgType type, const QString &message);
// QAbstractItemModel interface
public:
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
signals:
public slots:
@@ -62,6 +74,15 @@ private:
static char msgTypeID(QtMsgType type);
private:
struct Message {
QtMsgType type;
QTime time;
QString message;
QString toString() const;
};
private:
static QScopedPointer<LogBuffer> s_Instance;
@@ -71,7 +92,7 @@ private:
bool m_ShutDown;
QtMsgType m_MinMsgType;
unsigned int m_NumMessages;
std::vector<QString> m_Messages;
std::vector<Message> m_Messages;
};
+4 -6
View File
@@ -174,7 +174,9 @@ void cleanupDir()
"QtXml4.dll",
"QtWebKit4.dll",
"qjpeg4.dll",
"NCC/GamebryoBase.dll"
"NCC/GamebryoBase.dll",
"plugins/helloWorld.dll",
"plugins/testnexus.py"
};
static const int NUM_FILES = sizeof(fileNames) / sizeof(QString);
@@ -195,7 +197,6 @@ bool isNxmLink(const QString &link)
return link.left(6).toLower() == "nxm://";
}
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs)
{
typedef BOOL (WINAPI *FuncMiniDumpWriteDump)(HANDLE process, DWORD pid, HANDLE file, MINIDUMP_TYPE dumpType,
@@ -257,14 +258,11 @@ LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs
return result;
}
void registerMetaTypes()
{
registerExecutable();
}
bool HaveWriteAccess(const std::wstring &path)
{
bool writable = false;
@@ -333,7 +331,7 @@ int main(int argc, char *argv[])
, ToWString(QDir::currentPath()).c_str(), SW_SHOWNORMAL);
return 1;
}
LogBuffer::init(200, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log");
LogBuffer::init(100, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log");
qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath())));
qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(application.applicationDirPath())));
+629 -627
View File
File diff suppressed because it is too large Load Diff
+52 -39
View File
@@ -64,11 +64,13 @@ class ModListSortProxy;
class ModListGroupCategoriesProxy;
class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase::IPluginDiagnose
class MainWindow : public QMainWindow, public MOBase::IPluginDiagnose
{
Q_OBJECT
Q_INTERFACES(MOBase::IPluginDiagnose)
friend class OrganizerProxy;
private:
struct SignalCombinerAnd
@@ -88,6 +90,7 @@ private:
};
typedef boost::signals2::signal<bool (const QString&), SignalCombinerAnd> SignalAboutToRunApplication;
typedef boost::signals2::signal<void (const QString&)> SignalModInstalled;
public:
explicit MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent = 0);
@@ -96,8 +99,6 @@ public:
void readSettings();
bool addProfile();
void refreshLists();
void refreshESPList();
void refreshBSAList();
void refreshDataTree();
void refreshSaveList();
@@ -117,34 +118,6 @@ public:
void loadPlugins();
virtual MOBase::IGameInfo &gameInfo() const;
virtual MOBase::IModRepositoryBridge *createNexusBridge() const;
virtual QString profileName() const;
virtual QString profilePath() const;
virtual QString downloadsPath() const;
virtual MOBase::VersionInfo appVersion() const;
virtual MOBase::IModInterface *getMod(const QString &name);
virtual MOBase::IModInterface *createMod(MOBase::GuessedValue<QString> &name);
virtual bool removeMod(MOBase::IModInterface *mod);
virtual void modDataChanged(MOBase::IModInterface *mod);
virtual QVariant pluginSetting(const QString &pluginName, const QString &key) const;
virtual void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value);
virtual QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def = QVariant()) const;
virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync = true);
virtual QString pluginDataPath() const;
virtual void installMod(const QString &fileName);
virtual QString resolvePath(const QString &fileName) const;
virtual QStringList listDirectories(const QString &directoryName) const;
virtual QStringList findFiles(const QString &path, const std::function<bool(const QString &)> &filter) const;
virtual QList<FileInfo> findFileInfos(const QString &path, const std::function<bool(const FileInfo&)> &filter) const;
virtual MOBase::IDownloadManager *downloadManager();
virtual MOBase::IPluginList *pluginList();
virtual MOBase::IModList *modList();
virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "");
virtual bool onAboutToRun(const std::function<bool(const QString&)> &func);
virtual void refreshModList(bool saveChanges = true);
virtual std::vector<unsigned int> activeProblems() const;
virtual QString shortDescription(unsigned int key) const;
virtual QString fullDescription(unsigned int key) const;
@@ -153,13 +126,20 @@ public:
void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info);
void saveArchiveList();
bool saveArchiveList();
void createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite);
std::string readFromPipe(HANDLE stdOutRead);
void processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog);
HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "");
void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo);
public slots:
void refreshLists();
void displayColumnSelection(const QPoint &pos);
void externalMessage(const QString &message);
@@ -200,6 +180,9 @@ protected:
private:
void refreshESPList();
void refreshModList(bool saveChanges = true);
void actionToToolButton(QAction *&sourceAction);
bool verifyPlugin(MOBase::IPlugin *plugin);
void registerPluginTool(MOBase::IPluginTool *tool);
@@ -211,8 +194,6 @@ private:
void setExecutableIndex(int index);
bool nexusLogin();
bool testForSteam();
void startSteam();
@@ -224,6 +205,13 @@ private:
bool refreshProfiles(bool selectProfile = true);
void refreshExecutablesList();
void installMod();
MOBase::IModInterface *installMod(const QString &fileName);
MOBase::IModInterface *getMod(const QString &name);
MOBase::IModInterface *createMod(MOBase::GuessedValue<QString> &name);
bool removeMod(MOBase::IModInterface *mod);
QList<MOBase::IOrganizer::FileInfo> findFileInfos(const QString &path, const std::function<bool (const MOBase::IOrganizer::FileInfo &)> &filter) const;
bool modifyExecutablesDialog();
void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab);
void displayModInformation(int row, int tab = 0);
@@ -267,7 +255,7 @@ private:
bool extractProgress(QProgressDialog &extractProgress, int percentage, std::string fileName);
bool checkForProblems();
int checkForProblems();
int getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments);
QTreeWidgetItem *addFilterItem(QTreeWidgetItem *root, const QString &name, int categoryID);
@@ -275,8 +263,6 @@ private:
void setCategoryListVisible(bool visible);
void updateProblemsButton();
SaveGameGamebryo *getSaveGame(const QString &name);
SaveGameGamebryo *getSaveGame(QListWidgetItem *item);
@@ -298,6 +284,14 @@ private:
bool createBackup(const QString &filePath, const QDateTime &time);
QString queryRestore(const QString &filePath);
QMenu *modListContextMenu();
std::set<QString> enabledArchives();
void scheduleUpdateButton();
void updateModActiveState(int index, bool active);
private:
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
@@ -367,6 +361,7 @@ private:
bool m_ArchivesInit;
QTimer m_CheckBSATimer;
QTimer m_SaveMetaTimer;
QTimer m_UpdateProblemsTimer;
QTime m_StartTime;
SaveGameInfoWidget *m_CurrentSaveView;
@@ -380,6 +375,7 @@ private:
QFile m_PluginsCheck;
SignalAboutToRunApplication m_AboutToRun;
SignalModInstalled m_ModInstalled;
QString m_CurrentLanguage;
std::vector<QTranslator*> m_Translators;
@@ -391,6 +387,10 @@ private:
std::vector<QTreeWidgetItem*> m_RemoveWidget;
QByteArray m_ArchiveListHash;
bool m_DidUpdateMasterList;
private slots:
void showMessage(const QString &message);
@@ -456,6 +456,8 @@ private slots:
void linkClicked(const QString &url);
bool nexusLogin();
void loginSuccessful(bool necessary);
void loginSuccessfulUpdate(bool necessary);
void loginFailed(const QString &message);
@@ -482,11 +484,12 @@ private slots:
void modlistChanged(int row);
void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID);
// void nxmEndorsementToggled(int, QVariant, QVariant resultData, int);
void nxmEndorsementToggled(int, QVariant, QVariant resultData, int);
void nxmDownloadURLs(int modID, int fileID, QVariant userData, QVariant resultData, int requestID);
void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString);
void editCategories();
void deselectFilters();
void displayModInformation(const QString &modName, int tab);
void modOpenNext();
@@ -513,6 +516,9 @@ private slots:
void startExeAction();
void checkBSAList();
void updateProblemsButton();
void saveModMetas();
void updateStyle(const QString &style);
@@ -556,6 +562,7 @@ private slots:
void requestDownload(const QUrl &url, QNetworkReply *reply);
private slots: // ui slots
void profileRefresh();
// actions
void on_actionAdd_Profile_triggered();
void on_actionInstallMod_triggered();
@@ -576,7 +583,6 @@ private slots: // ui slots
void on_modList_customContextMenuRequested(const QPoint &pos);
void on_modList_doubleClicked(const QModelIndex &index);
void on_profileBox_currentIndexChanged(int index);
void on_profileRefreshBtn_clicked();
void on_savegameList_customContextMenuRequested(const QPoint &pos);
void on_startButton_clicked();
void on_tabWidget_currentChanged(int index);
@@ -594,6 +600,13 @@ private slots: // ui slots
void on_restoreButton_clicked();
void on_restoreModsButton_clicked();
void on_saveModsButton_clicked();
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);
};
#endif // MAINWINDOW_H
+1046 -954
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -81,6 +81,7 @@ void MessageDialog::resizeEvent(QResizeEvent *event)
void MessageDialog::showMessage(const QString &text, QWidget *reference, bool bringToFront)
{
qDebug("%s", qPrintable(text));
if (reference != NULL) {
if (bringToFront || (qApp->activeWindow() != NULL)) {
MessageDialog *dialog = new MessageDialog(text, reference);
+233 -117
View File
@@ -73,6 +73,16 @@ ModInfo::Ptr ModInfo::createFrom(const QDir &dir, DirectoryEntry **directoryStru
}
ModInfo::Ptr ModInfo::createFromPlugin(const QString &espName, const QStringList &bsaNames
, DirectoryEntry ** directoryStructure)
{
QMutexLocker locker(&s_Mutex);
ModInfo::Ptr result = ModInfo::Ptr(new ModInfoForeign(espName, bsaNames, directoryStructure));
s_Collection.push_back(result);
return result;
}
void ModInfo::createFromOverwrite()
{
QMutexLocker locker(&s_Mutex);
@@ -174,17 +184,37 @@ unsigned int ModInfo::findMod(const boost::function<bool (ModInfo::Ptr)> &filter
}
void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure)
void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure, bool displayForeign)
{
QMutexLocker lock(&s_Mutex);
s_Collection.clear();
s_NextID = 0;
// list all directories in the mod directory and make a mod out of each
QDir mods(QDir::fromNativeSeparators(modDirectory));
mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
QDirIterator modIter(mods);
while (modIter.hasNext()) {
createFrom(QDir(modIter.next()), directoryStructure);
{ // list all directories in the mod directory and make a mod out of each
QDir mods(QDir::fromNativeSeparators(modDirectory));
mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
QDirIterator modIter(mods);
while (modIter.hasNext()) {
createFrom(QDir(modIter.next()), directoryStructure);
}
}
{ // list plugins in the data directory and make a foreign-managed mod out of each
std::vector<std::wstring> dlcPlugins = GameInfo::instance().getDLCPlugins();
QDir dataDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data");
foreach (const QFileInfo &file, dataDir.entryInfoList(QStringList() << "*.esp" << "*.esm")) {
if ((file.baseName() != "Update") // hide update
&& (file.baseName() != ToQString(GameInfo::instance().getGameName())) // hide the game esp
&& (displayForeign // show non-dlc bundles only if the user wants them
|| std::find(dlcPlugins.begin(), dlcPlugins.end(), ToWString(file.fileName())) != dlcPlugins.end())) {
QStringList archives;
foreach (const QString archiveName, dataDir.entryList(QStringList() << file.baseName() + "*.bsa")) {
archives.append(dataDir.absoluteFilePath(archiveName));
}
createFromPlugin(file.fileName(), archives, directoryStructure);
}
}
}
createFromOverwrite();
@@ -219,7 +249,7 @@ ModInfo::ModInfo()
void ModInfo::checkChunkForUpdate(const std::vector<int> &modIDs, QObject *receiver)
{
if (modIDs.size() != 0) {
NexusInterface::instance()->requestUpdates(modIDs, receiver, QVariant());
NexusInterface::instance()->requestUpdates(modIDs, receiver, QVariant(), QString());
}
}
@@ -252,6 +282,12 @@ void ModInfo::setVersion(const VersionInfo &version)
m_Version = version;
}
bool ModInfo::hasFlag(ModInfo::EFlag flag) const
{
std::vector<EFlag> flags = getFlags();
return std::find(flags.begin(), flags.end(), flag) != flags.end();
}
bool ModInfo::categorySet(int categoryID) const
{
@@ -293,18 +329,136 @@ void ModInfo::testValid()
}
ModInfoWithConflictInfo::ModInfoWithConflictInfo(DirectoryEntry **directoryStructure)
: m_DirectoryStructure(directoryStructure) {}
void ModInfoWithConflictInfo::clearCaches()
{
m_LastConflictCheck = QTime();
}
std::vector<ModInfo::EFlag> ModInfoWithConflictInfo::getFlags() const
{
std::vector<ModInfo::EFlag> result;
switch (isConflicted()) {
case CONFLICT_MIXED: {
result.push_back(ModInfo::FLAG_CONFLICT_MIXED);
} break;
case CONFLICT_OVERWRITE: {
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITE);
} break;
case CONFLICT_OVERWRITTEN: {
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITTEN);
} break;
case CONFLICT_REDUNDANT: {
result.push_back(ModInfo::FLAG_CONFLICT_REDUNDANT);
} break;
default: { /* NOP */ }
}
return result;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const
{
// this is costy so cache the result
QTime now = QTime::currentTime();
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
bool overwrite = false;
bool overwritten = false;
bool regular = false;
int dataID = 0;
if ((*m_DirectoryStructure)->originExists(L"data")) {
dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID();
}
std::wstring name = ToWString(this->name());
if ((*m_DirectoryStructure)->originExists(name)) {
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
std::vector<FileEntry::Ptr> files = origin.getFiles();
for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) {
const std::vector<int> &alternatives = (*iter)->getAlternatives();
if (alternatives.size() == 0) {
// no alternatives -> no conflict
regular = true;
} else {
for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) {
// don't treat files overwritten in data as "conflict"
if (*altIter != dataID) {
bool ignore = false;
if ((*iter)->getOrigin(ignore) == origin.getID()) {
overwrite = true;
break;
} else {
overwritten = true;
break;
}
} else if (alternatives.size() == 1) {
// only alternative is data -> no conflict
regular = true;
}
}
}
}
}
m_LastConflictCheck = QTime::currentTime();
if (overwrite && overwritten) m_CurrentConflictState = CONFLICT_MIXED;
else if (overwrite) m_CurrentConflictState = CONFLICT_OVERWRITE;
else if (overwritten) {
if (!regular) {
m_CurrentConflictState = CONFLICT_REDUNDANT;
} else {
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
}
}
else m_CurrentConflictState = CONFLICT_NONE;
}
return m_CurrentConflictState;
}
bool ModInfoWithConflictInfo::isRedundant() const
{
std::wstring name = ToWString(this->name());
if ((*m_DirectoryStructure)->originExists(name)) {
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
std::vector<FileEntry::Ptr> files = origin.getFiles();
bool ignore = false;
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if ((*iter)->getOrigin(ignore) == origin.getID()) {
return false;
}
}
return true;
} else {
return false;
}
}
ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStructure)
: ModInfo(), m_Name(path.dirName()), m_Path(path.absolutePath()), m_MetaInfoChanged(false),
m_EndorsedState(ENDORSED_UNKNOWN), m_DirectoryStructure(directoryStructure)
: ModInfoWithConflictInfo(directoryStructure)
, m_Name(path.dirName())
, m_Path(path.absolutePath())
, m_MetaInfoChanged(false)
, m_EndorsedState(ENDORSED_UNKNOWN)
{
testValid();
m_CreationTime = QFileInfo(path.absolutePath()).created();
// read out the meta-file for information
readMeta();
connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant)), this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant)));
connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant)), this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant)));
connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString)), this, SLOT(nxmRequestFailed(int,int,QVariant,QString)));
connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant))
, this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant)));
connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant))
, this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant)));
connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString))
, this, SLOT(nxmRequestFailed(int,int,QVariant,QString)));
}
@@ -602,7 +756,7 @@ void ModInfoRegular::setNeverEndorse()
bool ModInfoRegular::remove()
{
m_MetaInfoChanged = false;
return shellDelete(QStringList(absolutePath()));
return shellDelete(QStringList(absolutePath()), true);
}
void ModInfoRegular::endorse(bool doEndorse)
@@ -612,11 +766,6 @@ void ModInfoRegular::endorse(bool doEndorse)
}
}
void ModInfoRegular::clearCaches()
{
m_LastConflictCheck = QTime();
}
QString ModInfoRegular::absolutePath() const
{
@@ -636,22 +785,7 @@ void ModInfoRegular::ignoreUpdate(bool ignore)
std::vector<ModInfo::EFlag> ModInfoRegular::getFlags() const
{
std::vector<ModInfo::EFlag> result;
switch (isConflicted()) {
case CONFLICT_MIXED: {
result.push_back(ModInfo::FLAG_CONFLICT_MIXED);
} break;
case CONFLICT_OVERWRITE: {
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITE);
} break;
case CONFLICT_OVERWRITTEN: {
result.push_back(ModInfo::FLAG_CONFLICT_OVERWRITTEN);
} break;
case CONFLICT_REDUNDANT: {
result.push_back(ModInfo::FLAG_CONFLICT_REDUNDANT);
} break;
default: { /* NOP */ }
}
std::vector<ModInfo::EFlag> result = ModInfoWithConflictInfo::getFlags();
if ((m_NexusID != -1) && (endorsedState() == ENDORSED_FALSE)) {
result.push_back(ModInfo::FLAG_NOTENDORSED);
}
@@ -712,92 +846,22 @@ ModInfoRegular::EEndorsedState ModInfoRegular::endorsedState() const
return m_EndorsedState;
}
ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const
{
// this is costy so cache the result
QTime now = QTime::currentTime();
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
bool overwrite = false;
bool overwritten = false;
bool regular = false;
int dataID = 0;
if ((*m_DirectoryStructure)->originExists(L"data")) {
dataID = (*m_DirectoryStructure)->getOriginByName(L"data").getID();
}
std::wstring name = ToWString(m_Name);
if ((*m_DirectoryStructure)->originExists(name)) {
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
std::vector<FileEntry::Ptr> files = origin.getFiles();
for (auto iter = files.begin(); iter != files.end() && (!overwrite || !overwritten || !regular); ++iter) {
const std::vector<int> &alternatives = (*iter)->getAlternatives();
if (alternatives.size() == 0) {
// no alternatives -> no conflict
regular = true;
} else {
for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) {
// don't treat files overwritten in data as "conflict"
if (*altIter != dataID) {
bool ignore = false;
if ((*iter)->getOrigin(ignore) == origin.getID()) {
overwrite = true;
break;
} else {
overwritten = true;
break;
}
} else if (alternatives.size() == 1) {
// only alternative is data -> no conflict
regular = true;
}
}
}
}
}
m_LastConflictCheck = QTime::currentTime();
if (overwrite && overwritten) m_CurrentConflictState = CONFLICT_MIXED;
else if (overwrite) m_CurrentConflictState = CONFLICT_OVERWRITE;
else if (overwritten) {
if (!regular) {
m_CurrentConflictState = CONFLICT_REDUNDANT;
} else {
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
}
}
else m_CurrentConflictState = CONFLICT_NONE;
}
return m_CurrentConflictState;
}
bool ModInfoRegular::isRedundant() const
{
std::wstring name = ToWString(m_Name);
if ((*m_DirectoryStructure)->originExists(name)) {
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
std::vector<FileEntry::Ptr> files = origin.getFiles();
bool ignore = false;
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if ((*iter)->getOrigin(ignore) == origin.getID()) {
return false;
}
}
return true;
} else {
return false;
}
}
QDateTime ModInfoRegular::getLastNexusQuery() const
{
return m_LastNexusQuery;
}
QStringList ModInfoRegular::archives() const
{
QStringList result;
QDir dir(this->absolutePath());
foreach (const QString &archive, dir.entryList(QStringList("*.bsa"))) {
result.append(this->absolutePath() + "/" + archive);
}
return result;
}
std::vector<QString> ModInfoRegular::getIniTweaks() const
{
QString metaFileName = absolutePath().append("/meta.ini");
@@ -874,9 +938,61 @@ int ModInfoOverwrite::getHighlight() const
return (isValid() ? HIGHLIGHT_IMPORTANT : HIGHLIGHT_INVALID) | HIGHLIGHT_CENTER;
}
QString ModInfoOverwrite::getDescription() const
{
return tr("This pseudo mod contains files from the virtual data tree that got "
"modified (i.e. by the construction kit)");
}
QStringList ModInfoOverwrite::archives() const
{
QStringList result;
QDir dir(this->absolutePath());
foreach (const QString &archive, dir.entryList(QStringList("*.bsa"))) {
result.append(this->absolutePath() + "/" + archive);
}
return result;
}
QString ModInfoForeign::name() const
{
return m_Name;
}
QDateTime ModInfoForeign::creationTime() const
{
return m_CreationTime;
}
QString ModInfoForeign::absolutePath() const
{
return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/data";
}
std::vector<ModInfo::EFlag> ModInfoForeign::getFlags() const
{
std::vector<ModInfo::EFlag> result = ModInfoWithConflictInfo::getFlags();
result.push_back(FLAG_FOREIGN);
return result;
}
int ModInfoForeign::getHighlight() const
{
return 0;
}
QString ModInfoForeign::getDescription() const
{
return tr("This pseudo mod represents content managed outside MO. It isn't modified by MO.");
}
ModInfoForeign::ModInfoForeign(const QString &referenceFile, const QStringList &archives,
DirectoryEntry **directoryStructure)
: ModInfoWithConflictInfo(directoryStructure)
, m_ReferenceFile(referenceFile)
, m_Archives(archives)
{
m_CreationTime = QFileInfo(referenceFile).created();
m_Name = QFileInfo(m_ReferenceFile).baseName();
}

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