2013-11-01 14:59:25 +01:00
/*
2014-05-04 14:50:01 +02:00
* Copyright (C) 2013 Sebastian Herbord. All rights reserved.
*
* This file is part of the basic diagnosis plugin for Mod Organizer
*
* This plugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This plugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this plugin. If not, see <http://www.gnu.org/licenses/>.
*/
2013-11-01 14:59:25 +01:00
2013-04-13 19:23:18 +02:00
#include "diagnosebasic.h"
#include <report.h>
#include <utility.h>
#include <QtPlugin>
#include <QFile>
#include <QDir>
#include <QCoreApplication>
2013-11-18 20:17:14 +01:00
#include <QMessageBox>
#include <QDateTime>
2013-07-14 14:59:01 +02:00
#include <regex>
2013-11-06 18:35:27 +01:00
#include <functional>
2014-05-04 14:50:01 +02:00
#include <vector>
#include <algorithm>
#pragma warning( push, 2 )
#include <boost/assign.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/connected_components.hpp>
#pragma warning( pop )
2013-04-13 19:23:18 +02:00
using namespace MOBase ;
DiagnoseBasic :: DiagnoseBasic ()
{
}
bool DiagnoseBasic :: init ( IOrganizer * moInfo )
{
m_MOInfo = moInfo ;
2013-11-06 18:35:27 +01:00
m_MOInfo -> modList () -> onModStateChanged ([ & ] ( const QString & modName , IModList :: ModStates ) {
2014-05-04 14:50:01 +02:00
if ( modName == "Overwrite" ) invalidate ();
});
2013-11-06 18:35:27 +01:00
m_MOInfo -> pluginList () -> onRefreshed ([ & ] () { this -> invalidate (); });
2013-04-13 19:23:18 +02:00
return true ;
}
QString DiagnoseBasic :: name () const
{
return tr ( "Basic diagnosis plugin" );
}
QString DiagnoseBasic :: author () const
{
return "Tannin" ;
}
QString DiagnoseBasic :: description () const
{
2013-05-10 10:57:35 +02:00
return tr ( "Checks for problems unrelated to other plugins" );
2013-04-13 19:23:18 +02:00
}
VersionInfo DiagnoseBasic :: version () const
{
2013-11-06 18:35:27 +01:00
return VersionInfo ( 1 , 1 , 0 , VersionInfo :: RELEASE_FINAL );
2013-04-13 19:23:18 +02:00
}
bool DiagnoseBasic :: isActive () const
{
return true ;
}
QList < PluginSetting > DiagnoseBasic :: settings () const
{
return QList < PluginSetting > ();
}
bool DiagnoseBasic :: errorReported () const
{
QDir dir ( QCoreApplication :: applicationDirPath () + "/logs" );
QFileInfoList files = dir . entryInfoList ( QStringList ( "ModOrganizer_??_??_??_??_??.log" ),
QDir :: Files , QDir :: Name | QDir :: Reversed );
if ( files . count () > 0 ) {
QString logFile = files . at ( 0 ). absoluteFilePath ();
QFile file ( logFile );
if ( file . open ( QIODevice :: ReadOnly | QIODevice :: Text )) {
char buffer [ 1024 ];
int line = 0 ;
qint64 lineLengths [ NUM_CONTEXT_ROWS ];
for ( int i = 0 ; i < NUM_CONTEXT_ROWS ; ++ i ) {
lineLengths [ i ] = 0 ;
}
while ( ! file . atEnd ()) {
lineLengths [ line % NUM_CONTEXT_ROWS ] = file . readLine ( buffer , 1024 ) + 1 ;
if ( strncmp ( buffer , "ERROR" , 5 ) == 0 ) {
qint64 sumChars = 0 ;
for ( int i = 0 ; i < NUM_CONTEXT_ROWS ; ++ i ) {
sumChars += lineLengths [ i ];
}
file . seek ( file . pos () - sumChars );
m_ErrorMessage = "" ;
for ( int i = 0 ; i < 2 * NUM_CONTEXT_ROWS ; ++ i ) {
file . readLine ( buffer , 1024 );
QString lineString = QString :: fromUtf8 ( buffer );
if ( lineString . startsWith ( "ERROR" )) {
m_ErrorMessage += "<b>" + lineString + "</b>" ;
} else {
m_ErrorMessage += lineString ;
}
}
return true ;
}
// prevent this function from taking forever
if ( line ++ >= 50000 ) {
break ;
}
}
}
}
return false ;
}
2013-05-22 20:43:08 +02:00
bool DiagnoseBasic :: overwriteFiles () const
{
QDir dir ( QCoreApplication :: applicationDirPath () + "/overwrite" );
2014-05-04 14:50:01 +02:00
2013-05-22 20:43:08 +02:00
return dir . count () != 2 ; // account for . and ..
}
2013-09-30 18:33:45 +02:00
bool DiagnoseBasic :: nitpickInstalled () const
{
QString path = m_MOInfo -> resolvePath ( "skse/plugins/nitpick.dll" );
2014-05-04 14:50:01 +02:00
2013-09-30 18:33:45 +02:00
return ! path . isEmpty ();
}
2013-11-01 14:59:25 +01:00
2013-11-06 18:35:27 +01:00
/// unused code to remove duplicates from a vector
2013-11-01 14:59:25 +01:00
template < typename T >
void makeUnique ( std :: vector < T > & vector )
{
std :: set < T > done ;
auto read = vector . begin ();
auto write = vector . begin ();
2014-05-04 14:50:01 +02:00
2013-11-01 14:59:25 +01:00
for (; read != vector . end (); ++ read ) {
if ( done . insert ( * read ). second ) {
* write = * read ;
++ write ;
}
}
vector . erase ( write , vector . end ());
}
2014-05-04 14:50:01 +02:00
bool operator < ( const DiagnoseBasic :: Move & lhs , const DiagnoseBasic :: Move & rhs )
{
2013-11-01 14:59:25 +01:00
if ( lhs . item . modName != rhs . item . modName ) return lhs . item . modName < rhs . item . modName ;
else return lhs . reference . modName < rhs . reference . modName ;
}
2014-05-04 14:50:01 +02:00
void DiagnoseBasic :: topoSort ( std :: vector < DiagnoseBasic :: ListElement > & list ) const
2013-11-01 14:59:25 +01:00
{
2014-05-04 14:50:01 +02:00
typedef std :: pair < int , int > Edge ;
std :: vector < Edge > before ;
2013-11-01 14:59:25 +01:00
2014-05-04 14:50:01 +02:00
// create a graph with edges where each edge tells us that mod a has to come before mod b
// this takes into account only pairs of mods that actually have conflicting scripts
for ( unsigned i = 0 ; i < list . size (); ++ i ) {
for ( unsigned j = i + 1 ; j < list . size (); ++ j ) {
if ( ! ( list [ i ]. relevantScripts & list [ j ]. relevantScripts ). empty ()) {
before . push_back ( Edge ( i , j ));
2013-11-01 14:59:25 +01:00
}
}
2014-05-04 14:50:01 +02:00
}
2013-11-01 14:59:25 +01:00
2014-05-04 14:50:01 +02:00
{
typedef boost :: adjacency_list < boost :: vecS , boost :: vecS , boost :: bidirectionalS , boost :: property < boost :: vertex_color_t , boost :: default_color_type >> Graph ;
using namespace boost ;
Graph graph ( before . begin (), before . end (), list . size ());
typedef graph_traits < Graph >:: vertex_descriptor Vertex ;
typedef std :: list < Vertex > Order ;
// figure out unconnected components of the graph.
std :: vector < int > component ( num_vertices ( graph ));
connected_components ( graph , & component [ 0 ]);
for ( int i = 0 ; i != component . size (); ++ i ) {
list [ i ]. sortGroup = component [ i ];
}
Order order ;
// do the actual sorting. This sorts the graph in full though the order between unconnected components doesn't
// really matter to us
boost :: topological_sort ( graph , std :: front_inserter ( order ));
}
}
void DiagnoseBasic :: Sorter :: sortGroup ( std :: vector < ListElement > modList )
{
std :: vector < ListElement > sorted ;
{
auto maxSeqBegin = modList . end ();
auto maxSeqEnd = modList . end ();
// first, determine the longest sequence of correctly sorted mods
auto curSeqBegin = modList . begin ();
auto curSeqEnd = modList . begin ();
auto iter = modList . begin () + 1 ;
for (; iter != modList . end (); ++ iter ) {
if ( iter -> modPriority < curSeqEnd -> modPriority ) {
// sequence ends
if (( maxSeqBegin == modList . end ()) || (( curSeqEnd - curSeqBegin ) > ( maxSeqEnd - maxSeqBegin ))) {
maxSeqBegin = curSeqBegin ;
maxSeqEnd = iter ;
}
curSeqBegin = curSeqEnd = iter ;
} else {
curSeqEnd = iter ;
}
}
if (( maxSeqBegin == modList . end ()) || (( curSeqEnd - curSeqBegin ) > ( maxSeqEnd - maxSeqBegin ))) {
maxSeqBegin = curSeqBegin ;
maxSeqEnd = modList . end ();
}
sorted = std :: vector < ListElement > ( maxSeqBegin , maxSeqEnd );
modList . erase ( maxSeqBegin , maxSeqEnd );
}
// now move the elements NOT in this sequence to the correct location within
while ( modList . begin () != modList . end ()) {
auto iter = modList . begin ();
bool found = false ;
auto targetIter = sorted . begin ();
for (; targetIter != sorted . end (); ++ targetIter ) {
if ( targetIter -> pluginPriority > iter -> pluginPriority ) {
moves . push_back ( Move ( * iter , * targetIter , Move :: BEFORE ));
found = true ;
break ;
}
}
if ( ! found ) {
// add to end!
moves . push_back ( Move ( * iter , * sorted . rbegin (), Move :: AFTER ));
}
sorted . insert ( targetIter , * iter );
modList . erase ( iter );
}
}
void DiagnoseBasic :: Sorter :: operator ()( std :: vector < ListElement > modList )
{
if ( modList . size () == 0 ) {
return ;
}
int currentGroup = 0 ;
while ( true ) {
std :: vector < ListElement > filtered ;
std :: copy_if ( modList . begin (), modList . end (), std :: back_inserter ( filtered ),
[ currentGroup ] ( const ListElement & ele ) -> bool { return ele . sortGroup == currentGroup ; });
2014-05-04 16:13:35 +02:00
++ currentGroup ;
2014-05-04 14:50:01 +02:00
if ( filtered . size () == 0 ) {
break ;
} else if ( filtered . size () == 1 ) {
// skip if there is only one element, there can't be a necessary move
continue ;
}
sortGroup ( filtered );
}
}
bool DiagnoseBasic :: assetOrder () const
{
Sorter minSorter ;
std :: vector < ListElement > modList ;
2013-11-01 14:59:25 +01:00
2013-11-18 20:17:14 +01:00
// list of mods containing conflicted scripts. We care only for those
2014-05-04 14:50:01 +02:00
std :: map < QString , QSet < QString >> scriptMods ;
foreach ( const IOrganizer :: FileInfo & pex , m_MOInfo -> findFileInfos ( "scripts" ,
[] ( const IOrganizer :: FileInfo & file ) -> bool { return file . filePath . endsWith ( ".pex" , Qt :: CaseInsensitive ); })) {
QStringList origins = pex . origins ;
origins . removeAll ( "data" ); // ignore files in base directory
if ( origins . size () > 1 ) {
foreach ( const QString & origin , origins ) {
scriptMods [ origin ]. insert ( pex . filePath );
2013-11-18 20:17:14 +01:00
}
}
}
2013-11-01 14:59:25 +01:00
// produce a list with the information we need: plugin, mod and the priority for each
QStringList esps = m_MOInfo -> findFiles ( "" , [] ( const QString & fileName ) -> bool { return fileName . endsWith ( ".esp" , Qt :: CaseInsensitive ); });
foreach ( const QString & esp , esps ) {
ListElement ele ;
2014-05-04 14:50:01 +02:00
2013-11-01 14:59:25 +01:00
ele . espName = QFileInfo ( esp ). fileName ();
ele . modName = m_MOInfo -> pluginList () -> origin ( ele . espName );
ele . pluginPriority = m_MOInfo -> pluginList () -> priority ( ele . espName );
ele . modPriority = m_MOInfo -> modList () -> priority ( ele . modName );
2013-11-06 18:35:27 +01:00
IModList :: ModStates state = m_MOInfo -> modList () -> state ( ele . modName );
2014-05-04 14:50:01 +02:00
auto iter = scriptMods . find ( ele . modName );
2013-11-18 20:17:14 +01:00
if ( state . testFlag ( IModList :: STATE_EXISTS ) && ! state . testFlag ( IModList :: STATE_ESSENTIAL ) &&
2014-05-04 14:50:01 +02:00
( iter != scriptMods . end ())) {
ele . relevantScripts = iter -> second ;
modList . push_back ( ele );
}
}
// generate a copy of list that contains each mod only once, otherwise
// strange things happen if a mod contains multiple esps that are mixed
// with esps from other mods
std :: vector < ListElement > distinctModList ;
{
std :: set < QString > includedMods ;
foreach ( const ListElement & ele , modList ) {
if ( includedMods . find ( ele . modName ) == includedMods . end ()) {
distinctModList . push_back ( ele );
includedMods . insert ( ele . modName );
}
2013-11-01 14:59:25 +01:00
}
}
// sort the list by plugin priority
2014-05-04 14:50:01 +02:00
std :: sort ( distinctModList . begin (), distinctModList . end (),
[] ( const ListElement & lhs , const ListElement & rhs ) -> bool { return lhs . pluginPriority < rhs . pluginPriority ; });
topoSort ( distinctModList );
2013-11-01 14:59:25 +01:00
// now determine the moves necessary to bring the mod list into this order
2014-05-04 14:50:01 +02:00
minSorter ( distinctModList );
2013-11-01 14:59:25 +01:00
m_SuggestedMoves = minSorter . moves ;
return m_SuggestedMoves . size () > 0 ;
}
2013-07-14 14:59:01 +02:00
bool DiagnoseBasic :: invalidFontConfig () const
{
if ( m_MOInfo -> gameInfo (). type () != IGameInfo :: TYPE_SKYRIM ) {
// this check is only for skyrim
return false ;
}
// files from skyrim_interface.bsa
static std :: vector < QString > defaultFonts = boost :: assign :: list_of ( "interface \\ fonts_console.swf" )
2014-05-04 14:50:01 +02:00
( "interface \\ fonts_en.swf" );
2013-07-14 14:59:01 +02:00
QString configPath = m_MOInfo -> resolvePath ( "interface/fontconfig.txt" );
if ( configPath . isEmpty ()) {
return false ;
}
QFile config ( configPath );
if ( ! config . open ( QIODevice :: ReadOnly | QIODevice :: Text )) {
qDebug ( "failed to open %s" , qPrintable ( configPath ));
return false ;
}
std :: tr1 :: regex exp ( "^fontlib \" ([^ \" ]*) \" $" );
while ( ! config . atEnd ()) {
QByteArray row = config . readLine ();
std :: tr1 :: cmatch match ;
if ( std :: tr1 :: regex_search ( row . constData (), match , exp )) {
std :: string temp = match [ 1 ];
QString path ( temp . c_str ());
bool isDefault = false ;
2014-05-04 14:50:01 +02:00
foreach ( const QString & def , defaultFonts ) {
2013-07-14 14:59:01 +02:00
if ( QString :: compare ( def , path , Qt :: CaseInsensitive ) == 0 ) {
isDefault = true ;
break ;
}
}
if ( ! isDefault && m_MOInfo -> resolvePath ( path ). isEmpty ()) {
return true ;
}
}
}
return false ;
}
2013-05-22 20:43:08 +02:00
2013-04-13 19:23:18 +02:00
std :: vector < unsigned int > DiagnoseBasic :: activeProblems () const
{
std :: vector < unsigned int > result ;
if ( errorReported ()) {
result . push_back ( PROBLEM_ERRORLOG );
}
2013-05-22 20:43:08 +02:00
if ( overwriteFiles ()) {
result . push_back ( PROBLEM_OVERWRITE );
}
2013-07-14 14:59:01 +02:00
if ( invalidFontConfig ()) {
result . push_back ( PROBLEM_INVALIDFONT );
}
2013-09-30 18:33:45 +02:00
if ( nitpickInstalled ()) {
result . push_back ( PROBLEM_NITPICKINSTALLED );
}
2013-11-01 14:59:25 +01:00
if ( assetOrder ()) {
result . push_back ( PROBLEM_ASSETORDER );
}
2013-11-18 20:17:14 +01:00
QStringList backups = QDir ( m_MOInfo -> profilePath ()). entryList ( QStringList () << "modlist.txt_backup_*" );
if ( backups . size () > 0 ) {
m_NewestModlistBackup = backups . last ();
result . push_back ( PROBLEM_MODLISTBACKUP );
}
2013-04-13 19:23:18 +02:00
2014-02-17 21:15:20 +01:00
if ( QFile :: exists ( m_MOInfo -> profilePath () + "/profile_tweaks.ini" )) {
result . push_back ( PROBLEM_PROFILETWEAKS );
}
2013-04-13 19:23:18 +02:00
return result ;
}
QString DiagnoseBasic :: shortDescription ( unsigned int key ) const
{
switch ( key ) {
case PROBLEM_ERRORLOG :
return tr ( "There was an error reported recently" );
2013-05-22 20:43:08 +02:00
case PROBLEM_OVERWRITE :
return tr ( "There are files in your overwrite mod" );
2013-07-14 14:59:01 +02:00
case PROBLEM_INVALIDFONT :
return tr ( "Your font configuration may be broken" );
2013-09-30 18:33:45 +02:00
case PROBLEM_NITPICKINSTALLED :
return tr ( "Nitpick installed" );
2013-11-01 14:59:25 +01:00
case PROBLEM_ASSETORDER :
return tr ( "Potential Mod order problem" );
2013-11-18 20:17:14 +01:00
case PROBLEM_MODLISTBACKUP :
return tr ( "Modlist backup exists" );
2014-02-17 21:15:20 +01:00
case PROBLEM_PROFILETWEAKS :
return tr ( "Ini Tweaks overwritten" );
2013-04-13 19:23:18 +02:00
default :
throw MyException ( tr ( "invalid problem key %1" ). arg ( key ));
}
}
QString DiagnoseBasic :: fullDescription ( unsigned int key ) const
{
switch ( key ) {
case PROBLEM_ERRORLOG :
return "<code>" + m_ErrorMessage . replace ( " \n " , "<br>" ) + "</code>" ;
2013-05-22 20:43:08 +02:00
case PROBLEM_OVERWRITE :
2013-09-21 19:25:33 +02:00
return tr ( "Files in the <font color= \" red \" ><i>Overwrite</i></font> mod are are usually files created by an external tool (i.e. Wrye Bash, Automatic Variants, ...).<br>"
"It is advisable you empty Overwrite directory by moving those files to an existing mod. You can do this by double-clicking the <font color= \" red \" ><i>Overwrite</i></font> mod and use drag&drop to move the files to a mod.<br>"
"Alternatively, right-click on <font color= \" red \" ><i>Overwrite</i></font> and create a new regular mod from the files there.<br>"
"<br>"
"Why is this necessary? Generated files may depend on the other mods active in a profile and may thus be incompatible with a different profile (i.e. bashed patches from Wrye Bash). "
"On the other hand the file may be necessary in all profiles (i.e. dlc esms after cleaning with TESVEdit)<br>"
"This can NOT be automated you HAVE to read up on the tools you use and make an educated decision." );
2013-07-14 14:59:01 +02:00
case PROBLEM_INVALIDFONT :
return tr ( "Your current configuration seems to reference a font that is not installed. You may see only boxes instead of letters.<br>"
"The font configuration is in Data \\ interface \\ fontconfig.txt. Most likely you have a broken installation of a font replacer mod." );
2013-09-30 18:33:45 +02:00
case PROBLEM_NITPICKINSTALLED :
return tr ( "You have the nitpick skse plugin installed. This plugin is not needed with Mod Organizer because MO already offers the same functionality. "
"Worse: The two solutions may conflict so it's strongly suggested you remove this plugin." );
2013-11-01 14:59:25 +01:00
case PROBLEM_ASSETORDER : {
2014-05-04 14:50:01 +02:00
QString res = tr ( "The conflict resolution order for some mods containing scripts differs from that of the corresponding esp.<br>"
"This may lead to subtle, hard to locate bugs. You should re-order the affected mods (left list!).<br>"
"There is no way to reliably know if these changes are necessary but its definitively safer.<br>"
"If someone suggested you ignore this message, please give them a proper slapping from me. <b>Do not ignore this warning</b><br>"
"The following changes should fix the issue:" ) + "<ul>" ;
foreach ( const Move & op , m_SuggestedMoves ) {
2013-11-01 14:59:25 +01:00
if ( op . type == Move :: BEFORE ) {
2013-12-07 16:07:28 +01:00
res += "<li>" + tr ( "Move %1 before %2" ). arg ( op . item . modName ). arg ( op . reference . modName ) + "</li>" ;
2013-11-01 14:59:25 +01:00
} else {
2013-12-07 16:07:28 +01:00
res += "<li>" + tr ( "Move %1 after %2" ). arg ( op . item . modName ). arg ( op . reference . modName ) + "</li>" ;
2013-11-01 14:59:25 +01:00
}
}
res += "</ul>" ;
return res ;
} break ;
2013-11-18 20:17:14 +01:00
case PROBLEM_MODLISTBACKUP : {
uint timestamp = m_NewestModlistBackup . right ( 10 ). toULong ();
QDateTime time ;
time . setTime_t ( timestamp );
return tr ( "A previous operation created a backup of your mod list on %1.<br>"
"This backup contains both the info which mods are enabled and the ordering.<br>"
"You can restore that backup here." ). arg ( time . toString ());
} break ;
2014-02-17 21:15:20 +01:00
case PROBLEM_PROFILETWEAKS : {
QString fileContent = readFileText ( m_MOInfo -> profilePath () + "/profile_tweaks.ini" );
return tr ( "Settings provided in ini tweaks have been overwritten in-game or in an applications.<br>"
"These overwrites are stored in a separate file (<i>profile_tweaks.ini</i> within the profile directory) < br > "
"to keep ini-tweaks in their original state but you should really get rid of this file as there is<br>"
"no tool support in MO to work on it. <br>"
"Advice: Copy settings you want to keep to an appropriate ini tweak, then delete <i>profile_tweaks.ini</i>.<br>"
"Hitting the <i>Fix</i> button will delete that file" )
2014-05-04 14:50:01 +02:00
+ "<hr><i>profile_tweaks.ini:</i><pre>" + fileContent + "</pre>" ;
2014-02-17 21:15:20 +01:00
} break ;
2013-04-13 19:23:18 +02:00
default :
throw MyException ( tr ( "invalid problem key %1" ). arg ( key ));
}
}
2013-11-01 14:59:25 +01:00
bool DiagnoseBasic :: hasGuidedFix ( unsigned int key ) const
2013-04-13 19:23:18 +02:00
{
2014-05-04 14:50:01 +02:00
return /*(key == PROBLEM_ASSETORDER) || */ ( key == PROBLEM_MODLISTBACKUP ) || ( key == PROBLEM_PROFILETWEAKS );
2013-04-13 19:23:18 +02:00
}
void DiagnoseBasic :: startGuidedFix ( unsigned int key ) const
{
2013-11-01 14:59:25 +01:00
switch ( key ) {
2013-12-07 16:07:28 +01:00
/* case PROBLEM_ASSETORDER: {
2014-05-04 14:50:01 +02:00
* if (QMessageBox::warning(NULL, tr("Continue?"), tr("This <b>BETA</b> feature will rearrange your mods to eliminate all "
* "possible ordering conflicts. A backup of your mod list will be created. Proceed?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
* shellCopy(QStringList(m_MOInfo->profilePath() + "/modlist.txt"),
* QStringList(m_MOInfo->profilePath() + "/modlist.txt_backup_" + QString("%1").arg(QDateTime::currentDateTime().toTime_t())));
* foreach (const Move &op, m_SuggestedMoves) {
* int oldPriority = m_MOInfo->modList()->priority(op.item.modName);
* int targetPriority = -1;
* if (op.type == Move::BEFORE) {
* targetPriority = m_MOInfo->modList()->priority(op.reference.modName);
* } else {
* targetPriority = m_MOInfo->modList()->priority(op.reference.modName) + 1;
* }
* if (oldPriority < targetPriority) {
* --targetPriority;
* }
* m_MOInfo->modList()->setPriority(op.item.modName, targetPriority);
* }
* }
* } break;*/
2013-11-18 20:17:14 +01:00
case PROBLEM_MODLISTBACKUP : {
QMessageBox question ( QMessageBox :: Question , tr ( "Restore backup?" ),
2014-05-04 14:50:01 +02:00
tr ( "Do you want to restore this backup or delete it?" ),
QMessageBox :: Yes | QMessageBox :: No | QMessageBox :: Cancel );
2013-11-18 20:17:14 +01:00
question . setButtonText ( QMessageBox :: Yes , tr ( "Restore" ));
question . setButtonText ( QMessageBox :: No , tr ( "Delete" ));
question . exec ();
if ( question . result () == QMessageBox :: Yes ) {
shellMove ( QStringList ( m_MOInfo -> profilePath () + "/" + m_NewestModlistBackup ), QStringList ( m_MOInfo -> profilePath () + "/modlist.txt" ));
m_MOInfo -> refreshModList ( false );
} else if ( question . result () == QMessageBox :: No ) {
shellDelete ( QStringList ( m_MOInfo -> profilePath () + "/" + m_NewestModlistBackup ));
2013-11-01 14:59:25 +01:00
}
} break ;
2014-02-17 21:15:20 +01:00
case PROBLEM_PROFILETWEAKS : {
shellDeleteQuiet ( m_MOInfo -> profilePath () + "/profile_tweaks.ini" );
} break ;
2013-11-01 14:59:25 +01:00
default : throw MyException ( tr ( "invalid problem key %1" ). arg ( key ));
}
2013-04-13 19:23:18 +02:00
}
2014-05-04 14:50:01 +02:00
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
2013-04-13 19:23:18 +02:00
Q_EXPORT_PLUGIN2 ( diagnosebasic , DiagnoseBasic )
2013-06-15 14:42:20 +02:00
#endif