Wizard now writes valid Morrowind.ini files

loadfix
pvdk 11 years ago
parent e8170adde5
commit 6916c0bc94

@ -38,8 +38,7 @@ void Wizard::ExistingInstallationPage::on_browseButton_clicked()
QString path(QDir::toNativeSeparators(info.absolutePath())); QString path(QDir::toNativeSeparators(info.absolutePath()));
QList<QListWidgetItem*> items = installationsList->findItems(path, Qt::MatchExactly); QList<QListWidgetItem*> items = installationsList->findItems(path, Qt::MatchExactly);
if (items.isEmpty()) if (items.isEmpty()) {
{
// Path is not yet in the list, add it // Path is not yet in the list, add it
mWizard->addInstallation(path); mWizard->addInstallation(path);

@ -1,8 +1,10 @@
#include "inisettings.hpp" #include "inisettings.hpp"
#include <QDir>
#include <QTextStream> #include <QTextStream>
#include <QFile> #include <QFile>
#include <QStringList>
#include <QString> #include <QString>
#include <QRegExp> #include <QRegExp>
#include <QDebug> #include <QDebug>
@ -15,25 +17,40 @@ Wizard::IniSettings::~IniSettings()
{ {
} }
QStringList Wizard::IniSettings::findKeys(const QString &text)
{
QStringList result;
foreach (const QString &key, mSettings.keys()) {
if (key.startsWith(text))
result << key;
}
return result;
}
bool Wizard::IniSettings::readFile(QTextStream &stream) bool Wizard::IniSettings::readFile(QTextStream &stream)
{ {
qDebug() << "readFile called!";
// Look for a square bracket, "'\\[" // Look for a square bracket, "'\\["
// that has one or more "not nothing" in it, "([^]]+)" // that has one or more "not nothing" in it, "([^]]+)"
// and is closed with a square bracket, "\\]" // and is closed with a square bracket, "\\]"
QRegExp sectionRe("^\\[([^]]+)\\]"); QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]"));
// Find any character(s) that is/are not equal sign(s), "[^=]+" // Find any character(s) that is/are not equal sign(s), "[^=]+"
// followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*" // followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
// and one or more periods, "(.+)" // and one or more periods, "(.+)"
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$"));
QString currentSection; QString currentSection;
while (!stream.atEnd()) while (!stream.atEnd())
{ {
QString line(stream.readLine()); const QString line(stream.readLine());
if (line.isEmpty() || line.startsWith(";")) if (line.isEmpty() || line.startsWith(QLatin1Char(';')))
continue; continue;
if (sectionRe.exactMatch(line)) if (sectionRe.exactMatch(line))
@ -49,6 +66,7 @@ bool Wizard::IniSettings::readFile(QTextStream &stream)
if (!currentSection.isEmpty()) if (!currentSection.isEmpty())
key = currentSection + QLatin1Char('/') + key; key = currentSection + QLatin1Char('/') + key;
qDebug() << "adding: " << key << value;
mSettings[key] = QVariant(value); mSettings[key] = QVariant(value);
} }
} }
@ -56,13 +74,161 @@ bool Wizard::IniSettings::readFile(QTextStream &stream)
return true; return true;
} }
bool Wizard::IniSettings::writeFile(QTextStream &stream) bool Wizard::IniSettings::writeFile(const QString &path, QTextStream &stream)
{ {
qDebug() << "test! " << stream.readAll(); // Look for a square bracket, "'\\["
// that has one or more "not nothing" in it, "([^]]+)"
// and is closed with a square bracket, "\\]"
QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]"));
// Find any character(s) that is/are not equal sign(s), "[^=]+"
// followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
// and one or more periods, "(.+)"
QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$"));
const QStringList keys(mSettings.keys());
QString currentSection;
QString buffer;
qDebug() << "Keys! " << keys;
while (!stream.atEnd()) { while (!stream.atEnd()) {
qDebug() << "test! " << stream.readLine();
const QString line(stream.readLine());
if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) {
buffer.append(line + QLatin1String("\n"));
continue;
}
if (sectionRe.exactMatch(line)) {
buffer.append(line + QLatin1String("\n"));
currentSection = sectionRe.cap(1);
} else if (keyRe.indexIn(line) != -1) {
QString key(keyRe.cap(1).trimmed());
QString lookupKey(key);
// Append the section, but only if there is one
if (!currentSection.isEmpty())
lookupKey = currentSection + QLatin1Char('/') + key;
buffer.append(key + QLatin1Char('=') + mSettings[lookupKey].toString() + QLatin1String("\n"));
mSettings.remove(lookupKey);
}
}
// Add the new settings to the buffer
QHashIterator<QString, QVariant> i(mSettings);
while (i.hasNext()) {
i.next();
QStringList fullKey(i.key().split(QLatin1Char('/')));
QString section(fullKey.at(0));
section.prepend(QLatin1Char('['));
section.append(QLatin1Char(']'));
QString key(fullKey.at(1));
int index = buffer.lastIndexOf(section);
if (index != -1) {
// Append the new keys to the bottom of the section
index = buffer.indexOf(QLatin1Char('['), index + 1);
if (index == -1 )
{
// Beginning of next section not found, we are at the last section
if (buffer.lastIndexOf(QLatin1String("\n")) > (buffer.lastIndexOf(section) + section.length())) {
// There is a newline after the section
index = buffer.lastIndexOf(QLatin1String("\n")) - 1;
buffer.insert(index - 2, QString("\n%1=%2").arg(key, i.value().toString()));
mSettings.remove(i.key());
continue;
} else {
// No newline found, or the last newline is before the last section
// Append the key to the bottom of the file
buffer.append(QString("\n%1=%2").arg(key, i.value().toString()));
mSettings.remove(i.key());
continue;
}
}
// Add the key at the index
buffer.insert(index - 1, QString("\n%1=%2").arg(key, i.value().toString()));
mSettings.remove(i.key());
} else {
// Add the section to the end of the file, because it's not found
buffer.append(QString("\n%1\n").arg(section));
i.previous();
}
} }
// Now we reopen the file, this time we write
QFile file(path);
if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
QTextStream in(&file);
in.setCodec(stream.codec());
// Write the updated buffer to an empty file
in << buffer;
file.flush();
file.close();
} else {
return false;
}
return true;
}
bool Wizard::IniSettings::parseInx(const QString &path)
{
QFile file(path);
if (file.open(QIODevice::ReadOnly))
{
const QByteArray data(file.readAll());
const QByteArray pattern("\x21\x00\x1A\x01\x04\x00\x04\x97\xFF\x06", 10);
int i = 0;
while ((i = data.indexOf(pattern, i)) != -1) {
int next = data.indexOf(pattern, i + 1);
if (next == -1)
break;
QByteArray array(data.mid(i, (next - i)));
// Skip some invalid entries
if (array.contains("\x04\x96\xFF")) {
++i;
continue;
}
// Remove the pattern from the beginning
array.remove(0, 12);
int index = array.indexOf("\x06");
const QString section(array.left(index));
// Figure how many characters to read for the key
int lenght = array.indexOf("\x06", section.length() + 3) - (section.length() + 3);
const QString key(array.mid(section.length() + 3, lenght));
QString value(array.mid(section.length() + key.length() + 6));
//qDebug() << section << key << value;
// Add the value
setValue(section + QLatin1Char('/') + key, QVariant(value));
++i;
}
file.close();
} else {
qDebug() << "Failed to open INX file: " << path;
return false;
}
return true; return true;
} }

@ -22,6 +22,11 @@ namespace Wizard
return mSettings.value(key, defaultValue); return mSettings.value(key, defaultValue);
} }
inline QList<QVariant> values() const
{
return mSettings.values();
}
inline void setValue(const QString &key, const QVariant &value) inline void setValue(const QString &key, const QVariant &value)
{ {
mSettings.insert(key, value); mSettings.insert(key, value);
@ -32,11 +37,17 @@ namespace Wizard
mSettings.remove(key); mSettings.remove(key);
} }
QStringList findKeys(const QString &text);
bool readFile(QTextStream &stream); bool readFile(QTextStream &stream);
bool writeFile(QTextStream &stream); bool writeFile(const QString &path, QTextStream &stream);
bool parseInx(const QString &path);
private: private:
int getLastNewline(const QString &buffer, int from);
SettingsMap mSettings; SettingsMap mSettings;
}; };

@ -110,6 +110,7 @@ void Wizard::InstallationPage::startInstallation()
// Set the location of the Morrowind.ini to update // Set the location of the Morrowind.ini to update
mUnshield->setIniPath(mWizard->mInstallations[path]->iniPath); mUnshield->setIniPath(mWizard->mInstallations[path]->iniPath);
mUnshield->setupSettings();
} }
// Set the installation target path // Set the installation target path

@ -21,11 +21,6 @@ void Wizard::InstallationTargetPage::initializePage()
QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str())); QString path(QFile::decodeName(mCfgMgr.getUserDataPath().string().c_str()));
path.append(QDir::separator() + QLatin1String("data")); path.append(QDir::separator() + QLatin1String("data"));
if (!QFile::exists(path)) {
QDir dir;
dir.mkpath(path);
}
QDir dir(path); QDir dir(path);
targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath())); targetLineEdit->setText(QDir::toNativeSeparators(dir.absolutePath()));
} }
@ -36,6 +31,13 @@ bool Wizard::InstallationTargetPage::validatePage()
qDebug() << "Validating path: " << path; qDebug() << "Validating path: " << path;
// TODO: Check writeability
if (!QFile::exists(path)) {
QDir dir;
dir.mkpath(path);
return true;
}
if (mWizard->findFiles(QLatin1String("Morrowind"), path)) { if (mWizard->findFiles(QLatin1String("Morrowind"), path)) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle(tr("Destination not empty")); msgBox.setWindowTitle(tr("Destination not empty"));

@ -127,6 +127,7 @@ void Wizard::MainWizard::setupPages()
setPage(Page_Conclusion, new ConclusionPage(this)); setPage(Page_Conclusion, new ConclusionPage(this));
setStartId(Page_Intro); setStartId(Page_Intro);
} }
void Wizard::MainWizard::accept() void Wizard::MainWizard::accept()
{ {
writeSettings(); writeSettings();

@ -43,7 +43,6 @@ Wizard::UnshieldWorker::UnshieldWorker(QObject *parent) :
Wizard::UnshieldWorker::~UnshieldWorker() Wizard::UnshieldWorker::~UnshieldWorker()
{ {
} }
void Wizard::UnshieldWorker::setInstallComponent(Wizard::Component component, bool install) void Wizard::UnshieldWorker::setInstallComponent(Wizard::Component component, bool install)
@ -198,6 +197,29 @@ void Wizard::UnshieldWorker::setupSettings()
mIniSettings.readFile(stream); mIniSettings.readFile(stream);
} }
void Wizard::UnshieldWorker::writeSettings()
{
if (getIniPath().isEmpty())
return;
QFile file(getIniPath());
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
qDebug() << "Error opening .ini file!";
emit error(tr("Failed to open Morrowind configuration file!"),
tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString()));
return;
}
QTextStream stream(&file);
stream.setCodec(mIniCodec);
if (!mIniSettings.writeFile(getIniPath(), stream)) {
emit error(tr("Failed to write Morrowind configuration file!"),
tr("Writing to %1 failed: %2.").arg(getIniPath(), file.errorString()));
}
}
bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName) bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName)
{ {
bool result = true; bool result = true;
@ -383,6 +405,32 @@ void Wizard::UnshieldWorker::extract()
setupAddon(Wizard::Component_Bloodmoon); setupAddon(Wizard::Component_Bloodmoon);
} }
// Update Morrowind configuration
if (getInstallComponent(Wizard::Component_Tribunal))
{
mIniSettings.setValue(QLatin1String("Archives/Archive0"), QVariant(QString("Tribunal.bsa")));
mIniSettings.setValue(QLatin1String("Game Files/Game File1"), QVariant(QString("Tribunal.esm")));
}
if (getInstallComponent(Wizard::Component_Bloodmoon))
{
mIniSettings.setValue(QLatin1String("Archives/Archive0"), QVariant(QString("Bloodmoon.bsa")));
mIniSettings.setValue(QLatin1String("Game Files/Game File1"), QVariant(QString("Bloodmoon.esm")));
}
if (getInstallComponent(Wizard::Component_Tribunal) &&
getInstallComponent(Wizard::Component_Bloodmoon))
{
mIniSettings.setValue(QLatin1String("Archives/Archive0"), QVariant(QString("Tribunal.bsa")));
mIniSettings.setValue(QLatin1String("Archives/Archive1"), QVariant(QString("Bloodmoon.bsa")));
mIniSettings.setValue(QLatin1String("Game Files/Game File1"), QVariant(QString("Tribunal.esm")));
mIniSettings.setValue(QLatin1String("Game Files/Game File2"), QVariant(QString("Bloodmoon.esm")));
}
// Write the settings to the Morrowind config file
writeSettings();
// Remove the temporary directory // Remove the temporary directory
removeDirectory(getPath() + QDir::separator() + QLatin1String("extract-temp")); removeDirectory(getPath() + QDir::separator() + QLatin1String("extract-temp"));
@ -554,6 +602,10 @@ bool Wizard::UnshieldWorker::installComponent(Component component)
emit error(tr("Could not find Morrowind configuration file!"), tr("Failed to find %0.").arg(iniPath)); emit error(tr("Could not find Morrowind configuration file!"), tr("Failed to find %0.").arg(iniPath));
return false; return false;
} }
// Setup Morrowind configuration
setIniPath(getPath() + QDir::separator() + QLatin1String("Morrowind.ini"));
setupSettings();
} }
if (component == Wizard::Component_Tribunal) if (component == Wizard::Component_Tribunal)
@ -564,7 +616,6 @@ bool Wizard::UnshieldWorker::installComponent(Component component)
emit textChanged(tr("Extracting: Sound directory")); emit textChanged(tr("Extracting: Sound directory"));
copyDirectory(sounds.absoluteFilePath(), getPath() + QDir::separator() + QLatin1String("Sound")); copyDirectory(sounds.absoluteFilePath(), getPath() + QDir::separator() + QLatin1String("Sound"));
} }
} }
if (component == Wizard::Component_Bloodmoon) if (component == Wizard::Component_Bloodmoon)
@ -577,6 +628,15 @@ bool Wizard::UnshieldWorker::installComponent(Component component)
copyFile(patch.absoluteFilePath(), original.absoluteFilePath()); copyFile(patch.absoluteFilePath(), original.absoluteFilePath());
} }
// Load Morrowind configuration settings from the setup script
QFileInfo inx(disk.absoluteFilePath(QLatin1String("setup.inx")));
if (inx.exists()) {
emit textChanged(tr("Updating Morrowind configuration file"));
mIniSettings.parseInx(inx.absoluteFilePath());
} else {
qDebug() << "setup.inx not found!";
}
} }
emit textChanged(tr("%0 installation finished!").arg(name)); emit textChanged(tr("%0 installation finished!").arg(name));
@ -649,7 +709,7 @@ bool Wizard::UnshieldWorker::findFile(const QString &cabFile, const QString &fil
{ {
QString current(QString::fromLatin1(unshield_file_name(unshield, j))); QString current(QString::fromLatin1(unshield_file_name(unshield, j)));
qDebug() << "File is: " << unshield_file_name(unshield, j); qDebug() << "File is: " << current;
if (current == fileName) if (current == fileName)
return true; // File is found! return true; // File is found!
} }

@ -41,8 +41,12 @@ namespace Wizard
void setIniCodec(QTextCodec *codec); void setIniCodec(QTextCodec *codec);
void setupSettings();
private: private:
void writeSettings();
bool getInstallComponent(Component component); bool getInstallComponent(Component component);
QString getComponentPath(Component component); QString getComponentPath(Component component);
@ -58,8 +62,6 @@ namespace Wizard
bool moveFile(const QString &source, const QString &destination); bool moveFile(const QString &source, const QString &destination);
bool moveDirectory(const QString &source, const QString &destination); bool moveDirectory(const QString &source, const QString &destination);
void setupSettings();
bool extractCab(const QString &cabFile, const QString &outputDir); bool extractCab(const QString &cabFile, const QString &outputDir);
bool extractFile(Unshield *unshield, const QString &outputDir, const QString &prefix, int index, int counter); bool extractFile(Unshield *unshield, const QString &outputDir, const QString &prefix, int index, int counter);
bool findFile(const QString &cabFile, const QString &fileName); bool findFile(const QString &cabFile, const QString &fileName);

Loading…
Cancel
Save