1 #include "mainwizard.hpp"
2 
3 #include <QDebug>
4 
5 #include <QTime>
6 #include <QCloseEvent>
7 #include <QMessageBox>
8 #include <QTextCodec>
9 #include <QDir>
10 
11 #include "intropage.hpp"
12 #include "methodselectionpage.hpp"
13 #include "languageselectionpage.hpp"
14 #include "existinginstallationpage.hpp"
15 #include "installationtargetpage.hpp"
16 #include "componentselectionpage.hpp"
17 #include "importpage.hpp"
18 #include "conclusionpage.hpp"
19 
20 #ifdef OPENMW_USE_UNSHIELD
21 #include "installationpage.hpp"
22 #endif
23 
24 using namespace Process;
25 
MainWizard(QWidget * parent)26 Wizard::MainWizard::MainWizard(QWidget *parent) :
27     QWizard(parent),
28     mInstallations(),
29     mError(false),
30     mGameSettings(mCfgMgr)
31 {
32 #ifndef Q_OS_MAC
33     setWizardStyle(QWizard::ModernStyle);
34 #else
35     setWizardStyle(QWizard::ClassicStyle);
36 #endif
37 
38     setWindowTitle(tr("OpenMW Wizard"));
39     setWindowIcon(QIcon(QLatin1String(":/images/openmw-wizard.png")));
40     setMinimumWidth(550);
41 
42     // Set the property for comboboxes to the text instead of index
43     setDefaultProperty("QComboBox", "currentText", "currentIndexChanged");
44 
45     setDefaultProperty("ComponentListWidget", "mCheckedItems", "checkedItemsChanged");
46 
47     mImporterInvoker = new ProcessInvoker();
48 
49     connect(mImporterInvoker->getProcess(), SIGNAL(started()),
50             this, SLOT(importerStarted()));
51 
52     connect(mImporterInvoker->getProcess(), SIGNAL(finished(int,QProcess::ExitStatus)),
53             this, SLOT(importerFinished(int,QProcess::ExitStatus)));
54 
55     mLogError = tr("<html><head/><body><p><b>Could not open %1 for writing</b></p> \
56                    <p>Please make sure you have the right permissions \
57                    and try again.</p></body></html>");
58 
59     setupLog();
60     setupGameSettings();
61     setupLauncherSettings();
62     setupInstallations();
63     setupPages();
64 
65     const boost::filesystem::path& installationPath = mCfgMgr.getInstallPath();
66     if (!installationPath.empty())
67     {
68         const boost::filesystem::path& dataPath = installationPath / "Data Files";
69         addInstallation(toQString(dataPath));
70     }
71 }
72 
~MainWizard()73 Wizard::MainWizard::~MainWizard()
74 {
75     delete mImporterInvoker;
76 }
77 
setupLog()78 void Wizard::MainWizard::setupLog()
79 {
80     QString logPath(toQString(mCfgMgr.getLogPath()));
81     logPath.append(QLatin1String("wizard.log"));
82 
83     QFile file(logPath);
84 
85     if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
86         QMessageBox msgBox;
87         msgBox.setWindowTitle(tr("Error opening Wizard log file"));
88         msgBox.setIcon(QMessageBox::Critical);
89         msgBox.setStandardButtons(QMessageBox::Ok);
90         msgBox.setText(mLogError.arg(file.fileName()));
91         msgBox.exec();
92         return qApp->quit();
93     }
94 
95     addLogText(QString("Started OpenMW Wizard on %1").arg(QDateTime::currentDateTime().toString()));
96 
97     qDebug() << logPath;
98 }
99 
addLogText(const QString & text)100 void Wizard::MainWizard::addLogText(const QString &text)
101 {
102     QString logPath(toQString(mCfgMgr.getLogPath()));
103     logPath.append(QLatin1String("wizard.log"));
104 
105     QFile file(logPath);
106 
107     if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
108         QMessageBox msgBox;
109         msgBox.setWindowTitle(tr("Error opening Wizard log file"));
110         msgBox.setIcon(QMessageBox::Critical);
111         msgBox.setStandardButtons(QMessageBox::Ok);
112         msgBox.setText(mLogError.arg(file.fileName()));
113         msgBox.exec();
114         return qApp->quit();
115     }
116 
117     if (!file.isSequential())
118         file.seek(file.size());
119 
120     QTextStream out(&file);
121 
122     if (!text.isEmpty())
123     {
124         out << text << "\n";
125         out.flush();
126     }
127 }
128 
setupGameSettings()129 void Wizard::MainWizard::setupGameSettings()
130 {
131     QString userPath(toQString(mCfgMgr.getUserConfigPath()));
132     QString globalPath(toQString(mCfgMgr.getGlobalPath()));
133     QString message(tr("<html><head/><body><p><b>Could not open %1 for reading</b></p> \
134                     <p>Please make sure you have the right permissions \
135                     and try again.</p></body></html>"));
136 
137     // Load the user config file first, separately
138     // So we can write it properly, uncontaminated
139     QString path(userPath + QLatin1String("openmw.cfg"));
140     QFile file(path);
141 
142     qDebug() << "Loading config file:" << path.toUtf8().constData();
143 
144     if (file.exists()) {
145         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
146             QMessageBox msgBox;
147             msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
148             msgBox.setIcon(QMessageBox::Critical);
149             msgBox.setStandardButtons(QMessageBox::Ok);
150             msgBox.setText(message.arg(file.fileName()));
151             msgBox.exec();
152             return qApp->quit();
153         }
154         QTextStream stream(&file);
155         stream.setCodec(QTextCodec::codecForName("UTF-8"));
156 
157         mGameSettings.readUserFile(stream);
158     }
159 
160     file.close();
161 
162     // Now the rest
163     QStringList paths;
164     paths.append(userPath + QLatin1String("openmw.cfg"));
165     paths.append(QLatin1String("openmw.cfg"));
166     paths.append(globalPath + QLatin1String("openmw.cfg"));
167 
168     for (const QString &path2 : paths)
169     {
170         qDebug() << "Loading config file:" << path2.toUtf8().constData();
171 
172         file.setFileName(path2);
173         if (file.exists()) {
174             if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
175                 QMessageBox msgBox;
176                 msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
177                 msgBox.setIcon(QMessageBox::Critical);
178                 msgBox.setStandardButtons(QMessageBox::Ok);
179                 msgBox.setText(message.arg(file.fileName()));
180 
181                 return qApp->quit();
182             }
183             QTextStream stream(&file);
184             stream.setCodec(QTextCodec::codecForName("UTF-8"));
185 
186             mGameSettings.readFile(stream);
187         }
188         file.close();
189     }
190 }
191 
setupLauncherSettings()192 void Wizard::MainWizard::setupLauncherSettings()
193 {
194     QString path(toQString(mCfgMgr.getUserConfigPath()));
195     path.append(QLatin1String(Config::LauncherSettings::sLauncherConfigFileName));
196 
197     QString message(tr("<html><head/><body><p><b>Could not open %1 for reading</b></p> \
198                     <p>Please make sure you have the right permissions \
199                     and try again.</p></body></html>"));
200 
201 
202     QFile file(path);
203 
204     qDebug() << "Loading config file:" << path.toUtf8().constData();
205 
206     if (file.exists()) {
207         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
208             QMessageBox msgBox;
209             msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
210             msgBox.setIcon(QMessageBox::Critical);
211             msgBox.setStandardButtons(QMessageBox::Ok);
212             msgBox.setText(message.arg(file.fileName()));
213             msgBox.exec();
214             return qApp->quit();
215         }
216         QTextStream stream(&file);
217         stream.setCodec(QTextCodec::codecForName("UTF-8"));
218 
219         mLauncherSettings.readFile(stream);
220     }
221 
222     file.close();
223 
224 }
225 
setupInstallations()226 void Wizard::MainWizard::setupInstallations()
227 {
228     // Check if the paths actually contain a Morrowind installation
229     for (const QString& path : mGameSettings.getDataDirs())
230     {
231 
232         if (findFiles(QLatin1String("Morrowind"), path))
233             addInstallation(path);
234     }
235 }
236 
runSettingsImporter()237 void Wizard::MainWizard::runSettingsImporter()
238 {
239     writeSettings();
240 
241     QString path(field(QLatin1String("installation.path")).toString());
242 
243     QString userPath(toQString(mCfgMgr.getUserConfigPath()));
244     QFile file(userPath + QLatin1String("openmw.cfg"));
245 
246     // Construct the arguments to run the importer
247     QStringList arguments;
248 
249     // Import plugin selection?
250     if (field(QLatin1String("installation.retailDisc")).toBool() == true
251             || field(QLatin1String("installation.import-addons")).toBool() == true)
252         arguments.append(QLatin1String("--game-files"));
253 
254     arguments.append(QLatin1String("--encoding"));
255 
256     // Set encoding
257     QString language(field(QLatin1String("installation.language")).toString());
258 
259     if (language == QLatin1String("Polish")) {
260         arguments.append(QLatin1String("win1250"));
261     } else if (language == QLatin1String("Russian")) {
262         arguments.append(QLatin1String("win1251"));
263     }  else {
264         arguments.append(QLatin1String("win1252"));
265     }
266 
267     // Now the paths
268     arguments.append(QLatin1String("--ini"));
269 
270     if (field(QLatin1String("installation.retailDisc")).toBool() == true) {
271         arguments.append(path + QDir::separator() + QLatin1String("Morrowind.ini"));
272     } else {
273         arguments.append(mInstallations[path].iniPath);
274     }
275 
276     arguments.append(QLatin1String("--cfg"));
277     arguments.append(userPath + QLatin1String("openmw.cfg"));
278 
279     if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false))
280         return qApp->quit();
281 }
282 
addInstallation(const QString & path)283 void Wizard::MainWizard::addInstallation(const QString &path)
284 {
285     qDebug() << "add installation in: " << path;
286     Installation install;// = new Installation();
287 
288     install.hasMorrowind = findFiles(QLatin1String("Morrowind"), path);
289     install.hasTribunal = findFiles(QLatin1String("Tribunal"), path);
290     install.hasBloodmoon = findFiles(QLatin1String("Bloodmoon"), path);
291 
292     // Try to autodetect the Morrowind.ini location
293     QDir dir(path);
294     QFile file(dir.filePath("Morrowind.ini"));
295 
296     // Try the parent directory
297     // In normal Morrowind installations that's where Morrowind.ini is
298     if (!file.exists()) {
299         dir.cdUp();
300         file.setFileName(dir.filePath(QLatin1String("Morrowind.ini")));
301     }
302 
303     if (file.exists())
304         install.iniPath = file.fileName();
305 
306     mInstallations.insert(QDir::toNativeSeparators(path), install);
307 
308     // Add it to the openmw.cfg too
309     if (!mGameSettings.getDataDirs().contains(path)) {
310         mGameSettings.setMultiValue(QLatin1String("data"), path);
311         mGameSettings.addDataDir(path);
312     }
313 }
314 
setupPages()315 void Wizard::MainWizard::setupPages()
316 {
317     setPage(Page_Intro, new IntroPage(this));
318     setPage(Page_MethodSelection, new MethodSelectionPage(this));
319     setPage(Page_LanguageSelection, new LanguageSelectionPage(this));
320     setPage(Page_ExistingInstallation, new ExistingInstallationPage(this));
321     setPage(Page_InstallationTarget, new InstallationTargetPage(this, mCfgMgr));
322     setPage(Page_ComponentSelection, new ComponentSelectionPage(this));
323 #ifdef OPENMW_USE_UNSHIELD
324     setPage(Page_Installation, new InstallationPage(this));
325 #endif
326     setPage(Page_Import, new ImportPage(this));
327     setPage(Page_Conclusion, new ConclusionPage(this));
328     setStartId(Page_Intro);
329 
330 }
331 
importerStarted()332 void Wizard::MainWizard::importerStarted()
333 {
334 }
335 
importerFinished(int exitCode,QProcess::ExitStatus exitStatus)336 void Wizard::MainWizard::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
337 {
338     if (exitCode != 0 || exitStatus == QProcess::CrashExit)
339         return;
340 
341     // Re-read the settings
342     setupGameSettings();
343 }
344 
accept()345 void Wizard::MainWizard::accept()
346 {
347     writeSettings();
348     QWizard::accept();
349 }
350 
reject()351 void Wizard::MainWizard::reject()
352 {
353     QMessageBox msgBox;
354     msgBox.setWindowTitle(tr("Quit Wizard"));
355     msgBox.setIcon(QMessageBox::Question);
356     msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
357     msgBox.setText(tr("Are you sure you want to exit the Wizard?"));
358 
359     if (msgBox.exec() == QMessageBox::Yes) {
360         QWizard::reject();
361     }
362 }
363 
writeSettings()364 void Wizard::MainWizard::writeSettings()
365 {
366     // Write the encoding and language settings
367     QString language(field(QLatin1String("installation.language")).toString());
368     mLauncherSettings.setValue(QLatin1String("Settings/language"), language);
369 
370     if (language == QLatin1String("Polish")) {
371         mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250"));
372     } else if (language == QLatin1String("Russian")) {
373         mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251"));
374     }  else {
375         mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252"));
376     }
377 
378     // Write the installation path so that openmw can find them
379     QString path(field(QLatin1String("installation.path")).toString());
380 
381     // Make sure the installation path is the last data= entry
382     mGameSettings.removeDataDir(path);
383     mGameSettings.addDataDir(path);
384 
385     QString userPath(toQString(mCfgMgr.getUserConfigPath()));
386     QDir dir(userPath);
387 
388     if (!dir.exists()) {
389         if (!dir.mkpath(userPath)) {
390             QMessageBox msgBox;
391             msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory"));
392             msgBox.setIcon(QMessageBox::Critical);
393             msgBox.setStandardButtons(QMessageBox::Ok);
394             msgBox.setText(tr("<html><head/><body><p><b>Could not create %1</b></p> \
395                               <p>Please make sure you have the right permissions \
396                               and try again.</p></body></html>").arg(userPath));
397             msgBox.exec();
398             return qApp->quit();
399         }
400     }
401 
402     // Game settings
403     QFile file(userPath + QLatin1String("openmw.cfg"));
404 
405     if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
406         // File cannot be opened or created
407         QMessageBox msgBox;
408         msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
409         msgBox.setIcon(QMessageBox::Critical);
410         msgBox.setStandardButtons(QMessageBox::Ok);
411         msgBox.setText(tr("<html><head/><body><p><b>Could not open %1 for writing</b></p> \
412                           <p>Please make sure you have the right permissions \
413                           and try again.</p></body></html>").arg(file.fileName()));
414         msgBox.exec();
415         return qApp->quit();
416     }
417 
418     QTextStream stream(&file);
419     stream.setCodec(QTextCodec::codecForName("UTF-8"));
420 
421     mGameSettings.writeFile(stream);
422     file.close();
423 
424     // Launcher settings
425     file.setFileName(userPath + QLatin1String(Config::LauncherSettings::sLauncherConfigFileName));
426 
427     if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
428         // File cannot be opened or created
429         QMessageBox msgBox;
430         msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
431         msgBox.setIcon(QMessageBox::Critical);
432         msgBox.setStandardButtons(QMessageBox::Ok);
433         msgBox.setText(tr("<html><head/><body><p><b>Could not open %1 for writing</b></p> \
434                           <p>Please make sure you have the right permissions \
435                           and try again.</p></body></html>").arg(file.fileName()));
436         msgBox.exec();
437         return qApp->quit();
438     }
439 
440     stream.setDevice(&file);
441     stream.setCodec(QTextCodec::codecForName("UTF-8"));
442 
443     mLauncherSettings.writeFile(stream);
444     file.close();
445 }
446 
findFiles(const QString & name,const QString & path)447 bool Wizard::MainWizard::findFiles(const QString &name, const QString &path)
448 {
449     QDir dir(path);
450 
451     if (!dir.exists())
452         return false;
453 
454     // TODO: add MIME handling to make sure the files are real
455     return (dir.entryList().contains(name + QLatin1String(".esm"), Qt::CaseInsensitive)
456             && dir.entryList().contains(name + QLatin1String(".bsa"), Qt::CaseInsensitive));
457 }
458 
toQString(const boost::filesystem::path & path)459 QString Wizard::MainWizard::toQString(const boost::filesystem::path& path)
460 {
461     return QString::fromUtf8(path.string().c_str());
462 }
463