Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Settings rework #808

Merged
merged 12 commits into from
Aug 3, 2019
21 changes: 21 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ SET(organizer_SRCS
spawn.cpp
singleinstance.cpp
settingsdialog.cpp
settingsdialogdiagnostics.cpp
settingsdialoggeneral.cpp
settingsdialognexus.cpp
settingsdialogpaths.cpp
settingsdialogplugins.cpp
settingsdialogsteam.cpp
settingsdialogworkarounds.cpp
settings.cpp
selfupdater.cpp
selectiondialog.cpp
Expand Down Expand Up @@ -151,6 +158,13 @@ SET(organizer_HDRS
spawn.h
singleinstance.h
settingsdialog.h
settingsdialogdiagnostics.h
settingsdialoggeneral.h
settingsdialognexus.h
settingsdialogpaths.h
settingsdialogplugins.h
settingsdialogsteam.h
settingsdialogworkarounds.h
settings.h
selfupdater.h
selectiondialog.h
Expand Down Expand Up @@ -431,6 +445,13 @@ set(profiles
set(settings
settings
settingsdialog
settingsdialogdiagnostics
settingsdialoggeneral
settingsdialognexus
settingsdialogpaths
settingsdialogplugins
settingsdialogsteam
settingsdialogworkarounds
)

set(utilities
Expand Down
274 changes: 4 additions & 270 deletions src/loadmechanism.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,286 +32,20 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QCryptographicHash>
#include <QCoreApplication>


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(qApp->applicationDirPath().toUtf8().constData());
hintFile.close();
}


void LoadMechanism::removeHintFile(QDir targetDirectory)
{
targetDirectory.remove("mo_path.txt");
}


bool LoadMechanism::isDirectLoadingSupported()
{
//FIXME: Seriously? isn't there a 'do i need steam' thing?
IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
if (game->gameName().compare("oblivion", Qt::CaseInsensitive) == 0) {
// oblivion can be loaded directly if it's not the steam variant
return !game->gameDirectory().exists("steam_api.dll");
} else {
// all other games work afaik
return true;
}
}

bool LoadMechanism::isScriptExtenderSupported()
{
IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
ScriptExtender *extender = game->feature<ScriptExtender>();

// test if there even is an extender for the managed game and if so whether it's installed
return extender != nullptr && extender->isInstalled();
}

bool LoadMechanism::isProxyDLLSupported()
{
// using steam_api.dll as the proxy is way too game specific as many games will have different
// versions of that dll.
// plus: the proxy dll hasn't been working for at least the whole 1.12.x versions of MO and
// noone reported it so why maintain an unused feature?
return false;
/* IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
return game->gameDirectory().exists(QString::fromStdWString(AppConfig::proxyDLLTarget()));*/
}


bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fileNameRHS)
bool LoadMechanism::isDirectLoadingSupported() const
{
QFile fileLHS(fileNameLHS);
if (!fileLHS.open(QIODevice::ReadOnly)) {
throw MyException(QObject::tr("file not found: %1").arg(qUtf8Printable(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("file not found: %1").arg(qUtf8Printable(fileNameRHS)));
}
QByteArray dataRHS = fileRHS.readAll();
QByteArray hashRHS = QCryptographicHash::hash(dataRHS, QCryptographicHash::Md5);

fileRHS.close();

return hashLHS == hashRHS;
return true;
}


void LoadMechanism::deactivateScriptExtender()
void LoadMechanism::activate(EMechanism)
{
try {
IPluginGame const *game = qApp->property("managed_game").value<IPluginGame*>();
ScriptExtender *extender = game->feature<ScriptExtender>();
if (extender == nullptr) {
return;
}

QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->PluginPath());

#pragma message("implement this for usvfs")

QString vfsDLLName = "";
if (extender->getArch() == IMAGE_FILE_MACHINE_I386) {
vfsDLLName = ToQString(AppConfig::vfs32DLLName());
}
else if (extender->getArch() == IMAGE_FILE_MACHINE_AMD64)
{
vfsDLLName = ToQString(AppConfig::vfs64DLLName());
}
log::debug("USVFS DLL Name: {}", vfsDLLName);
if (vfsDLLName != "") {
if (QFile(pluginsDir.absoluteFilePath(vfsDLLName)).exists()) {
// remove dll from SE plugins directory
if (!pluginsDir.remove(vfsDLLName)) {
throw MyException(QObject::tr("Failed to delete %1").arg(pluginsDir.absoluteFilePath(vfsDLLName)));
}
}
}

removeHintFile(pluginsDir);
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate script extender loading"), e.what());
}
// no-op
}


void LoadMechanism::deactivateProxyDLL()
{
try {
IPluginGame const *game = qApp->property("managed_game").value<IPluginGame *>();

QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget()));

QFile targetDLL(targetPath);
if (targetDLL.exists()) {
QString origFile = game->gameDirectory().absoluteFilePath(QString::fromStdWString(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));
}
}
}

removeHintFile(game->gameDirectory());
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to deactivate proxy-dll loading"), e.what());
}
}


void LoadMechanism::activateScriptExtender()
{
try {
IPluginGame const *game = qApp->property("managed_game").value<IPluginGame *>();
ScriptExtender *extender = game->feature<ScriptExtender>();
if (extender == nullptr) {
return;
}

QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->PluginPath());

if (!pluginsDir.exists()) {
pluginsDir.mkpath(".");
}

#pragma message("implement this for usvfs")
std::wstring vfsDLL = L"";
if (extender->getArch() == IMAGE_FILE_MACHINE_I386) {
vfsDLL = AppConfig::vfs32DLLName();
}
else if (extender->getArch() == IMAGE_FILE_MACHINE_AMD64)
{
vfsDLL = AppConfig::vfs64DLLName();
}
if (vfsDLL != L"") {
QString targetPath = pluginsDir.absoluteFilePath(ToQString(vfsDLL));
QString vfsDLLPath = qApp->applicationDirPath() + "/" + QString::fromStdWString(vfsDLL);

log::debug("DLL USVFS Target Path: {}", targetPath);
log::debug("DLL USVFS VFS DLL Path: {}", vfsDLLPath);

QFile dllFile(targetPath);

if (dllFile.exists()) {
// may be outdated
if (!hashIdentical(targetPath, vfsDLLPath)) {
dllFile.remove();
}
}

if (!dllFile.exists()) {
// install dll to SE plugins
if (!QFile::copy(vfsDLLPath, targetPath)) {
throw MyException(QObject::tr("Failed to copy %1 to %2").arg(vfsDLLPath, targetPath));
}
}
}
writeHintFile(pluginsDir);
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to set up script extender loading"), e.what());
}
}


void LoadMechanism::activateProxyDLL()
{
try {
IPluginGame const *game = qApp->property("managed_game").value<IPluginGame *>();

QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget()));

QFile targetDLL(targetPath);
if (!targetDLL.exists()) {
return;
}

QString sourcePath = qApp->applicationDirPath() + "/" + 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 = game->gameDirectory().absoluteFilePath(QString::fromStdWString(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(game->gameDirectory());
} catch (const std::exception &e) {
QMessageBox::critical(nullptr, QObject::tr("Failed to set up proxy-dll loading"), e.what());
}
}


void LoadMechanism::activate(EMechanism mechanism)
{
switch (mechanism) {
case LOAD_MODORGANIZER: {
log::debug("Load Mechanism: Mod Organizer");
deactivateProxyDLL();
deactivateScriptExtender();
} break;
case LOAD_SCRIPTEXTENDER: {
log::debug("Load Mechanism: ScriptExtender");
deactivateProxyDLL();
activateScriptExtender();
} break;
case LOAD_PROXYDLL: {
log::debug("Load Mechanism: Proxy DLL");
deactivateScriptExtender();
activateProxyDLL();
} break;
}
}

Loading