From 637ce6bcc1b10198eb2a21570b6a97e122814f55 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 19 Sep 2014 22:01:37 +0200 Subject: - removed a use of wostringstream which seems to have been involved in a bug. Still not sure how - rewrote widechar <-> multibyte conversion functions to handle arbitrary string lengths --- src/shared/util.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'src/shared/util.cpp') diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 61ca9cdc..4bc5a8a4 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include namespace MOShared { @@ -64,24 +65,28 @@ bool FileExists(const std::wstring &searchPath, const std::wstring &filename) std::string ToString(const std::wstring &source, bool utf8) { - char buffer[MAX_PATH]; - if (utf8) { - ::WideCharToMultiByte(CP_UTF8, 0, source.c_str(), -1, buffer, MAX_PATH, NULL, NULL); - } else { - ::WideCharToMultiByte(GetACP(), 0, source.c_str(), -1, buffer, MAX_PATH, NULL, NULL); + std::string result; + UINT codepage = utf8 ? CP_UTF8 : GetACP(); + int sizeRequired = ::WideCharToMultiByte(codepage, 0, &source[0], -1, NULL, 0, NULL, NULL); + if (sizeRequired == 0) { + throw windows_error("failed to convert string to multibyte"); } - return std::string(buffer); + result.resize(sizeRequired, '\0'); + ::WideCharToMultiByte(codepage, 0, &source[0], (int)source.size(), &result[0], sizeRequired, NULL, NULL); + return result; } std::wstring ToWString(const std::string &source, bool utf8) { - wchar_t buffer[MAX_PATH]; - if (utf8) { - ::MultiByteToWideChar(CP_UTF8, 0, source.c_str(), -1, buffer, MAX_PATH); - } else { - ::MultiByteToWideChar(GetACP(), 0, source.c_str(), -1, buffer, MAX_PATH); + std::wstring result; + UINT codepage = utf8 ? CP_UTF8 : GetACP(); + int sizeRequired = ::MultiByteToWideChar(codepage, 0, &source[0], (int)source.size(), NULL, 0); + if (sizeRequired == 0) { + throw windows_error("failed to convert string to wide character"); } - return std::wstring(buffer); + result.resize(sizeRequired, L'\0'); + ::MultiByteToWideChar(codepage, 0, &source[0], (int)source.size(), &result[0], sizeRequired); + return result; } std::string &ToLower(std::string &text) -- cgit v1.3.1 From df0bd3331a4b2174f99117c5a6f21ff6bddca1ba Mon Sep 17 00:00:00 2001 From: Tannin Date: Wed, 5 Nov 2014 23:48:06 +0100 Subject: - archive library can now query for password during extraction (seems to be necessary for rars) - process blacklist is now taken from a file if there is one, not hardcoded - removed workaround for the papyrus compiler - updated loot client to work with the actual api - loot client now links with loot32.dll at runtime - loot client now produces its output in a (json-)file which includes all plugin messages and dirty flags - fomod installer now tries to parse the xml with several encodings - fomod installer will now display a diagnostics warning if the jpg imageformat isn't supported - base preview plugin now tries to be a bit smarter about resizing images to fit the screen - bugfix: fomod installer no longer tries to open an image even after detecting its invalid - bugfix: potential null-pointer dereferentiation in getprivateprofile... hooks - bugfix: potential null-pointer dereferentiation in download manager - bugfix: internal origin name showed up in one more place - bugfix: ToString function produced strings that were one (zero-termination-)character too long --- src/aboutdialog.cpp | 1 - src/dlls.manifest.debug.qt5 | 3 +++ src/downloadmanager.cpp | 2 +- src/main.cpp | 2 +- src/mainwindow.cpp | 48 ++++++++++++++++++++++++++++++++------------- src/mainwindow.h | 2 +- src/organizer.pro | 2 +- src/pluginlist.cpp | 12 +++++++++++- src/settings.cpp | 2 +- src/shared/util.cpp | 9 ++++++--- 10 files changed, 59 insertions(+), 24 deletions(-) (limited to 'src/shared/util.cpp') diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index b5bfeb04..90dcd073 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -41,7 +41,6 @@ AboutDialog::AboutDialog(const QString &version, QWidget *parent) addLicense("Boost Library", LICENSE_BOOST); addLicense("7-zip", LICENSE_LGPL3); addLicense("ZLib", LICENSE_ZLIB); - addLicense("NIF File Format Library", LICENSE_BSD3); addLicense("Tango Icon Theme", LICENSE_NONE); addLicense("RRZE Icon Set", LICENSE_CCBY3); addLicense("Icons by Lorc, Delapouite and sbed available on http://game-icons.net", LICENSE_CCBY3); diff --git a/src/dlls.manifest.debug.qt5 b/src/dlls.manifest.debug.qt5 index 6cc0a83d..1bbdf691 100644 --- a/src/dlls.manifest.debug.qt5 +++ b/src/dlls.manifest.debug.qt5 @@ -8,7 +8,10 @@ + + + diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index bc31adf4..82701a05 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -606,7 +606,7 @@ void DownloadManager::pauseDownload(int index) DownloadInfo *info = m_ActiveDownloads.at(index); if (info->m_State == STATE_DOWNLOADING) { - if (info->m_Reply->isRunning()) { + if ((info->m_Reply != NULL) && (info->m_Reply->isRunning())) { setState(info, STATE_PAUSING); } else { setState(info, STATE_PAUSED); diff --git a/src/main.cpp b/src/main.cpp index 642bfa1a..b4139f09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -332,7 +332,7 @@ int main(int argc, char *argv[]) } } - application.addLibraryPath(application.applicationDirPath() + "/dlls"); + application.setLibraryPaths(QStringList() << (application.applicationDirPath() + "/dlls")); SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 19be758e..d9db84ae 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -103,6 +103,10 @@ along with Mod Organizer. If not, see . #include #include #include +#include +#include +#include +#include #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include #else @@ -1268,14 +1272,14 @@ void MainWindow::unloadPlugins() ui->actionTool->menu()->clear(); } - foreach (QPluginLoader *loader, m_PluginLoaders) { - qDebug("unloading %s", qPrintable(loader->fileName())); + while (!m_PluginLoaders.empty()) { + QPluginLoader *loader = m_PluginLoaders.back(); + m_PluginLoaders.pop_back(); if (!loader->unload()) { qDebug("failed to unload %s: %s", qPrintable(loader->fileName()), qPrintable(loader->errorString())); } delete loader; } - m_PluginLoaders.clear(); } void MainWindow::loadPlugins() @@ -1322,7 +1326,7 @@ void MainWindow::loadPlugins() if (QLibrary::isLibrary(pluginName)) { QPluginLoader *pluginLoader = new QPluginLoader(pluginName, this); if (pluginLoader->instance() == NULL) { - m_UnloadedPlugins.push_back(pluginName); + m_FailedPlugins.push_back(pluginName); qCritical("failed to load plugin %s: %s", qPrintable(pluginName), qPrintable(pluginLoader->errorString())); } else { @@ -1330,7 +1334,7 @@ void MainWindow::loadPlugins() qDebug("loaded plugin \"%s\"", qPrintable(pluginName)); m_PluginLoaders.push_back(pluginLoader); } else { - m_UnloadedPlugins.push_back(pluginName); + m_FailedPlugins.push_back(pluginName); qWarning("plugin \"%s\" failed to load", qPrintable(pluginName)); } } @@ -2285,7 +2289,7 @@ void MainWindow::on_tabWidget_currentChanged(int index) std::vector MainWindow::activeProblems() const { std::vector problems; - if (m_UnloadedPlugins.size() != 0) { + if (m_FailedPlugins.size() != 0) { problems.push_back(PROBLEM_PLUGINSNOTLOADED); } if (m_PluginList.enabledCount() > 255) { @@ -2314,7 +2318,7 @@ QString MainWindow::fullDescription(unsigned int key) const switch (key) { case PROBLEM_PLUGINSNOTLOADED: { QString result = tr("The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version:") + "
    "; - foreach (const QString &plugin, m_UnloadedPlugins) { + foreach (const QString &plugin, m_FailedPlugins) { result += "
  • " + plugin + "
  • "; } result += "
      "; @@ -5375,13 +5379,10 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportU foreach (const std::string &line, lines) { if (line.length() > 0) { - size_t progidx = line.find("[progress]"); - size_t reportidx = line.find("[Report]"); - size_t erroridx = line.find("[error]"); + size_t progidx = line.find("[progress]"); + size_t erroridx = line.find("[error]"); if (progidx != std::string::npos) { dialog.setLabelText(line.substr(progidx + 11).c_str()); - } else if (reportidx != std::string::npos) { - reportURL = line.substr(reportidx + 9); } else if (erroridx != std::string::npos) { qWarning("%s", line.c_str()); errorMessages.append(boost::algorithm::trim_copy(line.substr(erroridx + 8)) + "\n"); @@ -5525,12 +5526,15 @@ void MainWindow::on_bossButton_clicked() dialog.setMaximum(0); dialog.show(); + QString outPath = QDir::temp().absoluteFilePath("lootreport.json"); + QStringList parameters; parameters << "--unattended" << "--stdout" << "--noreport" << "--game" << ToQString(GameInfo::instance().getGameShortName()) - << "--gamePath" << QString("\"%1\"").arg(ToQString(GameInfo::instance().getGameDirectory())); + << "--gamePath" << QString("\"%1\"").arg(ToQString(GameInfo::instance().getGameDirectory())) + << "--out" << outPath; if (m_DidUpdateMasterList) { parameters << "--skipUpdateMasterlist"; @@ -5540,7 +5544,7 @@ void MainWindow::on_bossButton_clicked() HANDLE stdOutWrite = INVALID_HANDLE_VALUE; HANDLE stdOutRead = INVALID_HANDLE_VALUE; createStdoutPipe(&stdOutRead, &stdOutWrite); - HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/LOOT.exe"), + HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"), parameters.join(" "), m_CurrentProfile->getName(), m_Settings.logLevel(), @@ -5615,6 +5619,22 @@ void MainWindow::on_bossButton_clicked() return; } else { success = true; + QFile outFile(outPath); + outFile.open(QIODevice::ReadOnly); + QJsonDocument doc = QJsonDocument::fromJson(outFile.readAll()); + QJsonArray array = doc.array(); + for (auto iter = array.begin(); iter != array.end(); ++iter) { + QJsonObject pluginObj = (*iter).toObject(); + QJsonArray pluginMessages = pluginObj["messages"].toArray(); + for (auto msgIter = pluginMessages.begin(); msgIter != pluginMessages.end(); ++msgIter) { + QJsonObject msg = (*msgIter).toObject(); + m_PluginList.addInformation(pluginObj["name"].toString(), + QString("%1: %2").arg(msg["type"].toString(), msg["message"].toString())); + } + if (pluginObj["dirty"].toString() == "yes") + m_PluginList.addInformation(pluginObj["name"].toString(), "dirty"); + } + } } else { reportError(tr("failed to start loot")); diff --git a/src/mainwindow.h b/src/mainwindow.h index 8bd663ac..ea79fc37 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -381,7 +381,7 @@ private: std::vector m_DiagnosisPlugins; std::vector m_DiagnosisConnections; std::vector m_ModPages; - std::vector m_UnloadedPlugins; + std::vector m_FailedPlugins; std::vector m_PluginLoaders; QFile m_PluginsCheck; diff --git a/src/organizer.pro b/src/organizer.pro index b24586e6..fd1e6aad 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -341,7 +341,7 @@ SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g DSTDIR ~= s,/,$$QMAKE_DIR_SEP,g QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 973e3cfc..b2d02cd2 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "safewritefile.h" #include "scopeguard.h" +#include "modinfo.h" #include #include #include @@ -158,7 +159,14 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD QString iniPath = QFileInfo(filename).baseName() + ".ini"; bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != NULL; - m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName()), ToQString(current->getFullPath()), hasIni)); + QString originName = ToQString(origin.getName()); + unsigned int modIndex = ModInfo::getIndex(originName); + if (modIndex != UINT_MAX) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + originName = modInfo->name(); + } + + m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), originName, ToQString(current->getFullPath()), hasIni)); } catch (const std::exception &e) { reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); } @@ -290,6 +298,8 @@ void PluginList::addInformation(const QString &name, const QString &message) if (iter != m_ESPsByName.end()) { m_AdditionalInfo[name.toLower()].m_Messages.append(message); + } else { + qWarning("failed to associate message for \"%s\"", qPrintable(name)); } } diff --git a/src/settings.cpp b/src/settings.cpp index 0ca811a3..274e5979 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -231,7 +231,7 @@ QString Settings::getModDirectory() const QString Settings::getNMMVersion() const { - static const QString MIN_NMM_VERSION = "0.47.0"; + static const QString MIN_NMM_VERSION = "0.52.3"; QString result = m_Settings.value("Settings/nmm_version", MIN_NMM_VERSION).toString(); if (VersionInfo(result) < VersionInfo(MIN_NMM_VERSION)) { result = MIN_NMM_VERSION; diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 4bc5a8a4..d4a77929 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -71,7 +71,9 @@ std::string ToString(const std::wstring &source, bool utf8) if (sizeRequired == 0) { throw windows_error("failed to convert string to multibyte"); } - result.resize(sizeRequired, '\0'); + // the size returned by WideCharToMultiByte contains zero termination IF -1 is specified for the length. + // we don't want that \0 in the string because then the length field would be wrong. Because madness + result.resize(sizeRequired - 1, '\0'); ::WideCharToMultiByte(codepage, 0, &source[0], (int)source.size(), &result[0], sizeRequired, NULL, NULL); return result; } @@ -117,14 +119,15 @@ std::wstring ToLower(const std::wstring &text) VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName) { - DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), NULL); + DWORD handle; + DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), &handle); if (size == 0) { throw windows_error("failed to determine file version info size"); } void *buffer = new char[size]; try { - if (!::GetFileVersionInfoW(fileName.c_str(), 0UL, size, buffer)) { + if (!::GetFileVersionInfoW(fileName.c_str(), handle, size, buffer)) { throw windows_error("failed to determine file version info"); } -- cgit v1.3.1