openmw-tes3coop/apps/wizard/unshield/unshieldworker.cpp

791 lines
21 KiB
C++

#include "unshieldworker.hpp"
#include <QDebug>
#include <QReadLocker>
#include <QWriteLocker>
#include <QFileDialog>
#include <QFileInfo>
#include <QFileInfoListIterator>
#include <QStringList>
#include <QTextStream>
#include <QTextCodec>
#include <QFile>
#include <QDir>
#include <QDirIterator>
#include <qmath.h>
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; i<unshield_file_group_count(unshield); ++i)
{
UnshieldFileGroup *group = unshield_file_group_get(unshield, i);
for (size_t j=group->first_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; i<unshield_file_group_count(unshield); ++i)
{
UnshieldFileGroup *group = unshield_file_group_get(unshield, i);
for (size_t j=group->first_file; j<=group->last_file; ++j)
{
if (unshield_file_is_valid(unshield, j)) {
extractFile(unshield, outputDir, group->name, j, counter);
++counter;
}
}
}
unshield_close(unshield);
}