/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer 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.
Mod Organizer 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 Mod Organizer. If not, see .
*/
#include "loadmechanism.h"
#include "utility.h"
#include "util.h"
#include
#include
#include
#include
#include
#include
#include
#include
using namespace MOBase;
using namespace MOShared;
LoadMechanism::LoadMechanism()
: m_SelectedMechanism(LOAD_MODORGANIZER)
{
}
void LoadMechanism::writeHintFile(const QDir &targetDirectory)
{
QString hintFilePath = targetDirectory.absoluteFilePath("mo_path.txt");
QFile hintFile(hintFilePath);
if (hintFile.exists()) {
hintFile.remove();
}
if (!hintFile.open(QIODevice::WriteOnly)) {
throw MyException(QObject::tr("failed to open %1: %2").arg(hintFilePath).arg(hintFile.errorString()));
}
hintFile.write(ToString(GameInfo::instance().getOrganizerDirectory(), true).c_str());
hintFile.close();
}
void LoadMechanism::removeHintFile(QDir &targetDirectory)
{
targetDirectory.remove("mo_path.txt");
}
bool LoadMechanism::isDirectLoadingSupported()
{
if (GameInfo::instance().getType() == GameInfo::TYPE_OBLIVION) {
// oblivion can be loaded directly if it's not the steam variant
QString gamePath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
QDir gameDir(gamePath);
// test if this is the steam version of oblivion
if (QFile(gameDir.absoluteFilePath("steam_api.dll")).exists()) {
return false;
}
}
// all other games work afaik
return true;
}
bool LoadMechanism::isScriptExtenderSupported()
{
QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
if ((QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_loader.exe")).exists()) ||
(QFile(targetPath.mid(0).append("/").append(ToQString(GameInfo::instance().getSEName())).append("_steam_loader.dll")).exists())) {
return true;
} else {
return false;
}
}
bool LoadMechanism::isProxyDLLSupported()
{
QString targetPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
return QFile(targetPath).exists();
}
bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fileNameRHS)
{
QFile fileLHS(fileNameLHS);
if (!fileLHS.open(QIODevice::ReadOnly)) {
throw MyException(QObject::tr("%1 not found").arg(fileNameLHS));
}
QByteArray dataLHS = fileLHS.readAll();
QByteArray hashLHS = QCryptographicHash::hash(dataLHS, QCryptographicHash::Md5);
fileLHS.close();
QFile fileRHS(fileNameRHS);
if (!fileRHS.open(QIODevice::ReadOnly)) {
throw MyException(QObject::tr("%1 not found").arg(fileNameRHS));
}
QByteArray dataRHS = fileRHS.readAll();
QByteArray hashRHS = QCryptographicHash::hash(dataRHS, QCryptographicHash::Md5);
fileRHS.close();
return hashLHS == hashRHS;
}
void LoadMechanism::deactivateScriptExtender()
{
try {
QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
QString pluginsDirPath = gameDirectory;
pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins");
QDir pluginsDir(pluginsDirPath);
QString hookDLLName = ToQString(AppConfig::hookDLLName());
if (QFile(pluginsDir.absoluteFilePath(hookDLLName)).exists()) {
// remove dll from SE plugins directory
if (!pluginsDir.remove(hookDLLName)) {
throw MyException(QObject::tr("Failed to delete %1").arg(pluginsDir.absoluteFilePath(hookDLLName)));
}
}
removeHintFile(pluginsDir);
} catch (const std::exception &e) {
QMessageBox::critical(NULL, QObject::tr("Failed to deactivate script extender loading"), e.what());
}
}
void LoadMechanism::deactivateProxyDLL()
{
try {
QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
QString targetPath = gameDirectory;
targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
QFile targetDLL(targetPath);
if (targetDLL.exists()) {
QString origFile = gameDirectory.mid(0).append(ToQString(AppConfig::proxyDLLOrig()));
// determine if a proxy-dll is installed
// this is a very crude way of making this decision but it should be good enough
if ((targetDLL.size() < 24576) && (QFile(origFile).exists())) {
// remove proxy-dll
if (!targetDLL.remove()) {
throw MyException(QObject::tr("Failed to remove %1: %2").arg(targetPath).arg(targetDLL.errorString()));
} else if (!QFile::rename(origFile, targetPath)) {
throw MyException(QObject::tr("Failed to rename %1 to %2").arg(origFile, targetPath));
}
}
}
QDir dir(gameDirectory);
removeHintFile(dir);
} catch (const std::exception &e) {
QMessageBox::critical(NULL, QObject::tr("Failed to deactivate proxy-dll loading"), e.what());
}
}
void LoadMechanism::activateScriptExtender()
{
try {
QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
QString pluginsDirPath = gameDirectory;
pluginsDirPath.append("/data/").append(ToQString(GameInfo::instance().getSEName())).append("/plugins");
QDir pluginsDir(pluginsDirPath);
if (!pluginsDir.exists()) {
pluginsDir.mkpath(".");
}
QString targetPath = pluginsDir.absoluteFilePath(ToQString(AppConfig::hookDLLName()));
QString hookDLLPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::hookDLLName()));
QFile dllFile(targetPath);
if (dllFile.exists()) {
// may be outdated
if (!hashIdentical(targetPath, hookDLLPath)) {
dllFile.remove();
}
}
if (!dllFile.exists()) {
// install dll to SE plugins
if (!QFile::copy(hookDLLPath, targetPath)) {
throw MyException(QObject::tr("Failed to copy %1 to %2").arg(hookDLLPath, targetPath));
}
}
writeHintFile(pluginsDir);
} catch (const std::exception &e) {
QMessageBox::critical(NULL, QObject::tr("Failed to set up script extender loading"), e.what());
}
}
void LoadMechanism::activateProxyDLL()
{
try {
QString gameDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory()));
QString targetPath = gameDirectory;
targetPath.append("/").append(ToQString(AppConfig::proxyDLLTarget()));
QFile targetDLL(targetPath);
if (!targetDLL.exists()) {
return;
}
QString sourcePath = QDir::fromNativeSeparators(
ToQString(GameInfo::instance().getOrganizerDirectory())).append("/").append(ToQString(AppConfig::proxyDLLSource()));
// this is a very crude way of making this decision but it should be good enough
if (targetDLL.size() < 24576) {
// determine if a proxy-dll is already installed and if so, if it's the right one
if (!hashIdentical(targetPath, sourcePath)) {
// wrong proxy dll, probably outdated. delete and install the new one
if (!QFile::remove(targetPath)) {
throw MyException(QObject::tr("Failed to delete old proxy-dll %1").arg(targetPath));
}
if (!QFile::copy(sourcePath, targetPath)) {
throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
}
} // otherwise the proxy-dll is already the right one
} else {
// no proxy dll installed yet. move the original and insert proxy-dll
QString origFile = gameDirectory;
origFile.append("/").append(ToQString(AppConfig::proxyDLLOrig()));
if (QFile(origFile).exists()) {
// orig-file exists. this may happen if the steam-api was updated or the user messed with the
// dlls.
if (!QFile::remove(origFile)) {
throw MyException(QObject::tr("Failed to overwrite %1").arg(origFile));
}
}
if (!QFile::rename(targetPath, origFile)) {
throw MyException(QObject::tr("Failed to rename %1 to %2").arg(targetPath).arg(origFile));
}
if (!QFile::copy(sourcePath, targetPath)) {
throw MyException(QObject::tr("Failed to copy %1 to %2").arg(sourcePath).arg(targetPath));
}
}
writeHintFile(QDir(gameDirectory));
} catch (const std::exception &e) {
QMessageBox::critical(NULL, QObject::tr("Failed to set up proxy-dll loading"), e.what());
}
}
void LoadMechanism::activate(EMechanism mechanism)
{
switch (mechanism) {
case LOAD_MODORGANIZER: {
deactivateProxyDLL();
deactivateScriptExtender();
} break;
case LOAD_SCRIPTEXTENDER: {
deactivateProxyDLL();
activateScriptExtender();
} break;
case LOAD_PROXYDLL: {
deactivateScriptExtender();
activateProxyDLL();
} break;
}
}