#include "unshieldworker.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include Wizard::UnshieldWorker::UnshieldWorker(QObject *parent) : QObject(parent), mIniSettings() { unshield_set_log_level(0); mMorrowindPath = QString(); mTribunalPath = QString(); mBloodmoonPath = QString(); mPath = QString(); mIniPath = QString(); // Default to Latin encoding mIniCodec = QTextCodec::codecForName("windows-1252"); mInstallMorrowind = false; mInstallTribunal = false; mInstallBloodmoon = false; mMorrowindDone = false; mTribunalDone = false; mBloodmoonDone = false; } Wizard::UnshieldWorker::~UnshieldWorker() { } void Wizard::UnshieldWorker::setInstallMorrowind(bool install) { QWriteLocker writeLock(&mLock); mInstallMorrowind = install; } void Wizard::UnshieldWorker::setInstallTribunal(bool install) { QWriteLocker writeLock(&mLock); mInstallTribunal = install; } void Wizard::UnshieldWorker::setInstallBloodmoon(bool install) { QWriteLocker writeLock(&mLock); mInstallBloodmoon = install; } bool Wizard::UnshieldWorker::getInstallMorrowind() { QReadLocker readLock(&mLock); return mInstallMorrowind; } bool Wizard::UnshieldWorker::getInstallTribunal() { QReadLocker readLock(&mLock); return mInstallTribunal; } bool Wizard::UnshieldWorker::getInstallBloodmoon() { QReadLocker readLock(&mLock); return mInstallBloodmoon; } void Wizard::UnshieldWorker::setMorrowindPath(const QString &path) { QWriteLocker writeLock(&mLock); mMorrowindPath = path; mWait.wakeAll(); } void Wizard::UnshieldWorker::setTribunalPath(const QString &path) { QWriteLocker writeLock(&mLock); mTribunalPath = path; mWait.wakeAll(); } void Wizard::UnshieldWorker::setBloodmoonPath(const QString &path) { QWriteLocker writeLock(&mLock); mBloodmoonPath = path; mWait.wakeAll(); } QString Wizard::UnshieldWorker::getMorrowindPath() { QReadLocker readLock(&mLock); return mMorrowindPath; } QString Wizard::UnshieldWorker::getTribunalPath() { QReadLocker readLock(&mLock); return mTribunalPath; } QString Wizard::UnshieldWorker::getBloodmoonPath() { QReadLocker readLock(&mLock); return mBloodmoonPath; } void Wizard::UnshieldWorker::setPath(const QString &path) { QWriteLocker writeLock(&mLock); mPath = path; } void Wizard::UnshieldWorker::setIniPath(const QString &path) { QWriteLocker writeLock(&mLock); mIniPath = path; } QString Wizard::UnshieldWorker::getPath() { QReadLocker readLock(&mLock); return mPath; } QString Wizard::UnshieldWorker::getIniPath() { QReadLocker readLock(&mLock); return mIniPath; } void Wizard::UnshieldWorker::setIniCodec(QTextCodec *codec) { QWriteLocker writeLock(&mLock); mIniCodec = codec; } void Wizard::UnshieldWorker::setMorrowindDone(bool done) { QWriteLocker writeLock(&mLock); mMorrowindDone = done; } void Wizard::UnshieldWorker::setTribunalDone(bool done) { QWriteLocker writeLock(&mLock); mTribunalDone = done; } void Wizard::UnshieldWorker::setBloodmoonDone(bool done) { QWriteLocker writeLock(&mLock); mBloodmoonDone = done; } bool Wizard::UnshieldWorker::getMorrowindDone() { QReadLocker readLock(&mLock); return mMorrowindDone; } bool Wizard::UnshieldWorker::getTribunalDone() { QReadLocker readLock(&mLock); return mTribunalDone; } bool Wizard::UnshieldWorker::getBloodmoonDone() { QReadLocker readLock(&mLock); return mBloodmoonDone; } void Wizard::UnshieldWorker::setupSettings() { // Create Morrowind.ini settings map if (getIniPath().isEmpty()) return; QFile file(getIniPath()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { // TODO: Emit error signal qDebug() << "Error opening .ini file!"; return; } QTextStream stream(&file); stream.setCodec(mIniCodec); mIniSettings.readFile(stream); } bool Wizard::UnshieldWorker::removeDirectory(const QString &dirName) { bool result = true; QDir dir(dirName); if (dir.exists(dirName)) { QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)); foreach(QFileInfo info, list) { if (info.isDir()) { result = removeDirectory(info.absoluteFilePath()); } else { result = QFile::remove(info.absoluteFilePath()); } if (!result) return result; } result = dir.rmdir(dirName); } return result; } bool Wizard::UnshieldWorker::copyFile(const QString &source, const QString &destination, bool keepSource) { QDir dir; QFile file; QFileInfo info(destination); if (info.exists()) dir.remove(info.absoluteFilePath()); if (file.copy(source, destination)) { if (!keepSource) { return file.remove(source); } else { return true; } } else { qDebug() << "copy failed! " << file.errorString(); } return false; } bool Wizard::UnshieldWorker::copyDirectory(const QString &source, const QString &destination, bool keepSource) { QDir sourceDir(source); QDir destDir(destination); if (!destDir.exists()) { sourceDir.mkpath(destination); destDir.refresh(); } if (!destDir.exists()) return false; bool result = true; QFileInfoList list(sourceDir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)); foreach (const QFileInfo &info, list) { QString relativePath(info.absoluteFilePath()); relativePath.remove(source); if (info.isSymLink()) continue; if (info.isDir()) { result = moveDirectory(info.absoluteFilePath(), destDir.absolutePath() + relativePath); } else { qDebug() << "moving: " << info.absoluteFilePath() << " to: " << destDir.absolutePath() + relativePath; result = moveFile(info.absoluteFilePath(), destDir.absolutePath() + relativePath); } } if (!keepSource) return result && removeDirectory(sourceDir.absolutePath()); return result; } bool Wizard::UnshieldWorker::moveFile(const QString &source, const QString &destination) { return copyFile(source, destination, false); } bool Wizard::UnshieldWorker::moveDirectory(const QString &source, const QString &destination) { return copyDirectory(source, destination, false); } void Wizard::UnshieldWorker::installDirectories(const QString &source) { QDir dir(source); if (!dir.exists()) return; QStringList directories; directories << QLatin1String("Fonts") << QLatin1String("Music") << QLatin1String("Sound") << QLatin1String("Splash") << QLatin1String("Video"); QFileInfoList list(dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs)); foreach(QFileInfo info, list) { if (info.isSymLink()) continue; if (directories.contains(info.fileName())) { qDebug() << "found " << info.fileName(); emit textChanged(tr("Extracting: %1 directory").arg(info.fileName())); copyDirectory(info.absoluteFilePath(), getPath() + QDir::separator() + info.fileName()); } } // Copy the Data Files dir too, but only the subdirectories QFileInfo info(dir.absoluteFilePath("Data Files")); if (info.exists()) { emit textChanged(tr("Extracting: Data Files directory")); copyDirectory(info.absoluteFilePath(), getPath()); } } void Wizard::UnshieldWorker::extract() { qDebug() << "extract!"; QDir disk; if (getInstallMorrowind()) { while (!getMorrowindDone()) { if (getMorrowindPath().isEmpty()) { qDebug() << "request file dialog"; QReadLocker readLock(&mLock); emit requestFileDialog(QLatin1String("Morrowind")); mWait.wait(&mLock); } if (!getMorrowindPath().isEmpty()) { disk.setPath(getMorrowindPath()); if (!findFile(disk.absoluteFilePath(QLatin1String("data1.hdr")), QLatin1String("Morrowind.bsa")) | findFile(disk.absoluteFilePath(QLatin1String("data1.hdr")), QLatin1String("Tribunal.bsa")) | findFile(disk.absoluteFilePath(QLatin1String("data1.hdr")), QLatin1String("Bloodmoon.bsa"))) { QReadLocker readLock(&mLock); emit requestFileDialog(QLatin1String("Morrowind")); mWait.wait(&mLock); } else { if (installMorrowind()) { setMorrowindDone(true); } else { qDebug() << "Erorr installing Morrowind"; return; } } } } } if (getInstallTribunal()) { while (!getTribunalDone()) { QDir tribunal(disk); if (!tribunal.cd(QLatin1String("Tribunal"))) { qDebug() << "not found on cd!"; QReadLocker locker(&mLock); emit requestFileDialog(QLatin1String("Tribunal")); mWait.wait(&mLock); } else if (tribunal.exists(QLatin1String("data1.hdr"))) { qDebug() << "Exists! " << tribunal.absolutePath(); setTribunalPath(tribunal.absolutePath()); } if (getTribunalPath().isEmpty()) { qDebug() << "request file dialog"; QReadLocker locker(&mLock); emit requestFileDialog(QLatin1String("Tribunal")); mWait.wait(&mLock); } // Make sure the dir is up-to-date tribunal.setPath(getTribunalPath()); if (!getTribunalPath().isEmpty()) { if (!findFile(tribunal.absoluteFilePath(QLatin1String("data1.hdr")), QLatin1String("Tribunal.bsa"))) { qDebug() << "found"; QReadLocker locker(&mLock); emit requestFileDialog(QLatin1String("Tribunal")); mWait.wait(&mLock); } else { if (installTribunal()) { setTribunalDone(true); } else { qDebug() << "Erorr installing Tribunal"; return; } } } } } if (getInstallBloodmoon()) { while (!getBloodmoonDone()) { QDir bloodmoon(disk); qDebug() << "Test!: " << bloodmoon.absolutePath(); if (!bloodmoon.cd(QLatin1String("Bloodmoon"))) { qDebug() << "not found on cd!"; QReadLocker locker(&mLock); emit requestFileDialog(QLatin1String("Bloodmoon")); mWait.wait(&mLock); } else if (bloodmoon.exists(QLatin1String("data1.hdr"))) { qDebug() << "Exists! " << bloodmoon.absolutePath(); setBloodmoonPath(bloodmoon.absolutePath()); } if (getBloodmoonPath().isEmpty()) { qDebug() << "request file dialog"; QReadLocker locker(&mLock); emit requestFileDialog(QLatin1String("Bloodmoon")); mWait.wait(&mLock); } // Make sure the dir is up-to-date bloodmoon.setPath(getBloodmoonPath()); if (!findFile(bloodmoon.absoluteFilePath(QLatin1String("data1.hdr")), QLatin1String("Bloodmoon.bsa"))) { QReadLocker locker(&mLock); emit requestFileDialog(QLatin1String("Bloodmoon")); mWait.wait(&mLock); } else { if (installBloodmoon()) { setBloodmoonDone(true); } else { qDebug() << "Erorr installing Bloodmoon"; return; } } } } // Remove the temporary directory removeDirectory(mPath + QDir::separator() + QLatin1String("extract-temp")); // Fill the progress bar int total = 0; if (getInstallMorrowind()) total = 100; if (getInstallTribunal()) total = total + 100; if (getInstallBloodmoon()) total = total + 100; emit textChanged(tr("Installation finished!")); emit progressChanged(total); emit finished(); qDebug() << "installation finished!"; } bool Wizard::UnshieldWorker::installMorrowind() { qDebug() << "install morrowind!"; emit textChanged(QLatin1String("Installing Morrowind")); QDir disk(getMorrowindPath()); if (!disk.exists()) { qDebug() << "getMorrowindPath: " << getMorrowindPath(); return false; } // Create temporary extract directory // TODO: Use QTemporaryDir in Qt 5.0 QString tempPath(getPath() + QDir::separator() + QLatin1String("extract-temp")); QDir temp; // Make sure the temporary folder is empty removeDirectory(tempPath); if (!temp.mkpath(tempPath)) { qDebug() << "Can't make path"; return false; } temp.setPath(tempPath); if (!temp.mkdir(QLatin1String("morrowind"))) { qDebug() << "Can't make dir"; return false; } if (!temp.cd(QLatin1String("morrowind"))) { qDebug() << "Can't cd to dir"; return false; } // Extract the installation files extractCab(disk.absoluteFilePath(QLatin1String("data1.hdr")), temp.absolutePath()); // TODO: Throw error; // Move the files from the temporary path to the destination folder emit textChanged(tr("Moving installation files")); if (!moveDirectory(temp.absoluteFilePath(QLatin1String("Data Files")), getPath())) { qDebug() << "failed to move files!"; return false; } // Install files outside of cab archives installDirectories(disk.absolutePath()); // Copy Morrowind configuration file QString iniPath(temp.absoluteFilePath(QLatin1String("App Executables"))); iniPath.append(QDir::separator() + QLatin1String("Morrowind.ini")); QFileInfo info(iniPath); if (info.exists()) { emit textChanged(tr("Extracting: Morrowind.ini")); moveFile(info.absoluteFilePath(), getPath() + QDir::separator() + QLatin1String("Morrowind.ini")); } else { qDebug() << "Could not find ini file!"; return false; } emit textChanged(tr("Morrowind installation finished!")); return true; } bool Wizard::UnshieldWorker::installTribunal() { emit textChanged(QLatin1String("Installing Tribunal")); QDir disk(getTribunalPath()); if (!disk.exists()) { qDebug() << "disk does not exist! " << disk.absolutePath() << getTribunalPath(); return false; } // Create temporary extract directory // TODO: Use QTemporaryDir in Qt 5.0 QString tempPath(getPath() + QDir::separator() + QLatin1String("extract-temp")); QDir temp; // Make sure the temporary folder is empty removeDirectory(tempPath); if (!temp.mkpath(tempPath)) { qDebug() << "Can't make path"; return false; } temp.setPath(tempPath); if (!temp.mkdir(QLatin1String("tribunal"))) { qDebug() << "Can't make dir"; return false; } if (!temp.cd(QLatin1String("tribunal"))) { qDebug() << "Can't cd to dir"; return false; } // Extract the installation files extractCab(disk.absoluteFilePath(QLatin1String("data1.hdr")), temp.absolutePath()); // TODO: Throw error; // Move the files from the temporary path to the destination folder emit textChanged(tr("Moving installation files")); if (!moveDirectory(temp.absoluteFilePath(QLatin1String("Data Files")), getPath())) { qDebug() << "failed to move files!"; return false; } // Install files outside of cab archives installDirectories(disk.absolutePath()); emit textChanged(tr("Tribunal installation finished!")); return true; } bool Wizard::UnshieldWorker::installBloodmoon() { emit textChanged(QLatin1String("Installing Bloodmoon")); QDir disk(getBloodmoonPath()); if (!disk.exists()) { return false; } // Create temporary extract directory // TODO: Use QTemporaryDir in Qt 5.0 QString tempPath(getPath() + QDir::separator() + QLatin1String("extract-temp")); QDir temp; // Make sure the temporary folder is empty removeDirectory(tempPath); if (!temp.mkpath(tempPath)) { qDebug() << "Can't make path"; return false; } temp.setPath(tempPath); if (!temp.mkdir(QLatin1String("bloodmoon"))) { qDebug() << "Can't make dir"; return false; } if (!temp.cd(QLatin1String("bloodmoon"))) { qDebug() << "Can't cd to dir"; return false; } // Extract the installation files extractCab(disk.absoluteFilePath(QLatin1String("data1.hdr")), temp.absolutePath()); // TODO: Throw error; // Move the files from the temporary path to the destination folder emit textChanged(tr("Moving installation files")); if (!moveDirectory(temp.absoluteFilePath(QLatin1String("Data Files")), getPath())) { qDebug() << "failed to move files!"; return false; } // Install files outside of cab archives installDirectories(disk.absolutePath()); QFileInfo patch(temp.absoluteFilePath(QLatin1String("Tribunal Patch") + QDir::separator() + QLatin1String("Tribunal.esm"))); QFileInfo original(getPath() + QDir::separator() + QLatin1String("Tribunal.esm")); if (original.exists() && patch.exists()) { emit textChanged(tr("Extracting: Tribunal patch")); copyFile(patch.absoluteFilePath(), original.absoluteFilePath()); } emit textChanged(tr("Bloodmoon installation finished!")); return true; } bool Wizard::UnshieldWorker::extractFile(Unshield *unshield, const QString &outputDir, const QString &prefix, int index, int counter) { bool success; QString path(outputDir); path.append(QDir::separator()); int directory = unshield_file_directory(unshield, index); if (!prefix.isEmpty()) path.append(prefix + QDir::separator()); if (directory >= 0) path.append(QString::fromLatin1(unshield_directory_name(unshield, directory)) + QDir::separator()); // Ensure the path has the right separators path.replace(QLatin1Char('\\'), QDir::separator()); path = QDir::toNativeSeparators(path); // Ensure the target path exists QDir dir; dir.mkpath(path); QString fileName(path); fileName.append(QString::fromLatin1(unshield_file_name(unshield, index))); // Calculate the percentage done int progress = (((float) counter / (float) unshield_file_count(unshield)) * 100); if (getMorrowindDone()) progress = progress + 100; if (getTribunalDone()) progress = progress + 100; emit textChanged(tr("Extracting: %1").arg(QString::fromLatin1(unshield_file_name(unshield, index)))); emit progressChanged(progress); success = unshield_file_save(unshield, index, fileName.toLatin1().constData()); if (!success) { emit error(tr("Failed to extract %1").arg(fileName)); dir.remove(fileName); } return success; } bool Wizard::UnshieldWorker::findFile(const QString &cabFile, const QString &fileName) { Unshield *unshield; unshield = unshield_open(cabFile.toLatin1().constData()); // TODO: Proper error if (!unshield) { emit error(tr("Failed to open %1").arg(cabFile)); return false; } for (int i=0; ifirst_file; j<=group->last_file; ++j) { QString current(QString::fromLatin1(unshield_file_name(unshield, j))); qDebug() << "File is: " << unshield_file_name(unshield, j); if (current == fileName) return true; // File is found! } } unshield_close(unshield); return false; } void Wizard::UnshieldWorker::extractCab(const QString &cabFile, const QString &outputDir) { Unshield *unshield; unshield = unshield_open(cabFile.toLatin1().constData()); // TODO: Proper error if (!unshield) { emit error(tr("Failed to open %1").arg(cabFile)); return; } int counter = 0; for (int i=0; ifirst_file; j<=group->last_file; ++j) { if (unshield_file_is_valid(unshield, j)) { extractFile(unshield, outputDir, group->name, j, counter); ++counter; } } } unshield_close(unshield); }