Files
modorganizer-plugin_python/src/proxy/proxypython.cpp
T

299 lines
9.3 KiB
C++
Raw Normal View History

/*
Copyright (C) 2013 Sebastian Herbord. All rights reserved.
This file is part of python proxy plugin for MO
python proxy plugin 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.
Python proxy plugin 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 python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
*/
#include "proxypython.h"
#include <filesystem>
2022-04-25 21:21:06 +02:00
#include <QCoreApplication>
#include <QDirIterator>
#include <QMessageBox>
#include <QWidget>
#include <QtPlugin>
#include "log.h"
#include <utility.h>
#include <versioninfo.h>
namespace fs = std::filesystem;
using namespace MOBase;
// retrieve the path to the folder containing the proxy DLL
fs::path getPluginFolder()
{
wchar_t path[MAX_PATH];
HMODULE hm = NULL;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)&getPluginFolder, &hm) == 0) {
return {};
}
if (GetModuleFileName(hm, path, sizeof(path)) == 0) {
return {};
}
return fs::path(path).parent_path();
}
ProxyPython::ProxyPython()
2022-04-25 21:21:06 +02:00
: m_MOInfo{nullptr}, m_RunnerLib{nullptr}, m_Runner{nullptr},
m_LoadFailure(FailureType::NONE)
2013-07-13 10:10:46 +02:00
{
}
2022-04-25 21:21:06 +02:00
bool ProxyPython::init(IOrganizer* moInfo)
{
2022-04-25 21:21:06 +02:00
using CreatePythonRunner_func = IPythonRunner* (*)();
2022-02-13 17:33:53 +01:00
2022-04-25 21:21:06 +02:00
m_MOInfo = moInfo;
2022-04-25 21:21:06 +02:00
if (m_MOInfo && !m_MOInfo->isPluginEnabled(this)) {
return false;
2022-02-13 17:33:53 +01:00
}
2022-04-25 21:21:06 +02:00
if (QCoreApplication::applicationDirPath().contains(';')) {
m_LoadFailure = FailureType::SEMICOLON;
return true;
2022-02-13 17:33:53 +01:00
}
2022-04-25 21:21:06 +02:00
const auto pluginFolder = getPluginFolder();
if (pluginFolder.empty()) {
DWORD error = ::GetLastError();
m_LoadFailure = FailureType::DLL_NOT_FOUND;
log::error("failed to resolve Python proxy directory ({}): {}", error,
qUtf8Printable(windowsErrorString(::GetLastError())));
return false;
}
2022-04-25 21:21:06 +02:00
// load the pythonrunner library
const auto dllPaths = pluginFolder / "dlls";
if (SetDllDirectoryW(dllPaths.c_str()) == 0) {
DWORD error = ::GetLastError();
m_LoadFailure = FailureType::DLL_NOT_FOUND;
log::error("failed to add python DLL directory ({}): {}", error,
qUtf8Printable(windowsErrorString(::GetLastError())));
return false;
}
const auto runnerPath = pluginFolder / "pythonrunner.dll";
m_RunnerLib = ::LoadLibraryW(runnerPath.c_str());
2022-04-25 21:21:06 +02:00
if (!m_RunnerLib) {
DWORD error = ::GetLastError();
log::error("failed to load python runner ({}): {}", error,
2022-04-25 21:21:06 +02:00
qUtf8Printable(windowsErrorString(error)));
if (error == ERROR_MOD_NOT_FOUND) {
m_LoadFailure = FailureType::DLL_NOT_FOUND;
}
else {
m_LoadFailure = FailureType::INVALID_DLL;
}
return true;
}
const CreatePythonRunner_func createPythonRunner =
(CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner");
if (!createPythonRunner) {
m_LoadFailure = FailureType::INVALID_DLL;
return true;
}
if (m_MOInfo && m_MOInfo->persistent(name(), "tryInit", false).toBool()) {
2022-04-28 22:47:49 +02:00
m_LoadFailure = FailureType::INITIALIZATION;
if (QMessageBox::question(
parentWidget(), tr("Python Initialization failed"),
tr("On a previous start the Python Plugin failed to initialize.\n"
"Do you want to try initializing python again (at the risk of "
"another crash)?\n "
"Suggestion: Select \"no\", and click the warning sign for further "
"help.Afterwards you have to re-enable the python plugin."),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) == QMessageBox::No) {
// we force enabled here (note: this is a persistent settings since MO2 2.4
// or something), plugin
// usually should not handle enabled/disabled themselves but this is a base
// plugin so...
m_MOInfo->setPersistent(name(), "enabled", false, true);
return true;
}
2022-04-25 21:21:06 +02:00
}
if (m_MOInfo) {
m_MOInfo->setPersistent(name(), "tryInit", true);
}
m_Runner = std::unique_ptr<IPythonRunner>{createPythonRunner()};
if (m_Runner) {
m_Runner->initialize(pluginFolder / "libs");
}
2022-04-25 21:21:06 +02:00
if (m_MOInfo) {
m_MOInfo->setPersistent(name(), "tryInit", false);
}
if (!m_Runner || !m_Runner->isInitialized()) {
m_LoadFailure = FailureType::INITIALIZATION;
}
// reset DLL directory
SetDllDirectoryW(NULL);
2022-04-25 21:21:06 +02:00
2022-02-13 17:33:53 +01:00
return true;
}
QString ProxyPython::name() const
{
2022-04-25 21:21:06 +02:00
return "Python Proxy";
}
QString ProxyPython::localizedName() const
{
2022-04-25 21:21:06 +02:00
return tr("Python Proxy");
}
QString ProxyPython::author() const
{
2022-04-25 21:21:06 +02:00
return "AnyOldName3, Holt59, Silarn, Tannin";
}
QString ProxyPython::description() const
{
2022-04-25 21:21:06 +02:00
return tr("Proxy Plugin to allow plugins written in python to be loaded");
}
VersionInfo ProxyPython::version() const
{
2022-04-25 21:21:06 +02:00
return VersionInfo(2, 3, 0, VersionInfo::RELEASE_FINAL);
}
QList<PluginSetting> ProxyPython::settings() const
{
2022-04-25 21:21:06 +02:00
return {};
}
QStringList ProxyPython::pluginList(const QDir& pluginPath) const
{
2022-04-25 21:21:06 +02:00
QDir dir(pluginPath);
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
QDirIterator iter(dir);
2022-04-25 21:21:06 +02:00
// Note: We put python script (.py) and directory names, not the __init__.py
// files in those since it is easier for the runner to import them.
QStringList result;
while (iter.hasNext()) {
QString name = iter.next();
QFileInfo info = iter.fileInfo();
if (info.isFile() && name.endsWith(".py")) {
2022-04-25 21:21:06 +02:00
result.append(name);
}
else if (info.isDir() && QDir(info.absoluteFilePath()).exists("__init__.py")) {
result.append(name);
2022-04-25 21:21:06 +02:00
}
}
2022-04-21 23:57:05 +02:00
2022-04-25 21:21:06 +02:00
return result;
}
QList<QObject*> ProxyPython::load(const QString& identifier)
{
2022-04-25 21:21:06 +02:00
if (!m_Runner) {
return {};
}
2022-04-28 13:44:29 +02:00
return m_Runner->load(identifier);
2013-07-13 10:10:46 +02:00
}
void ProxyPython::unload(const QString& identifier)
{
2022-04-25 21:21:06 +02:00
if (m_Runner) {
return m_Runner->unload(identifier);
}
}
2013-07-13 10:10:46 +02:00
std::vector<unsigned int> ProxyPython::activeProblems() const
{
2022-04-25 21:21:06 +02:00
auto failure = m_LoadFailure;
2022-02-13 17:33:53 +01:00
2022-04-25 21:21:06 +02:00
// don't know how this could happen but wth
if (m_Runner && !m_Runner->isInitialized()) {
2022-04-25 21:21:06 +02:00
failure = FailureType::INITIALIZATION;
}
2013-07-13 10:10:46 +02:00
2022-04-25 21:21:06 +02:00
if (failure != FailureType::NONE) {
return {static_cast<std::underlying_type_t<FailureType>>(failure)};
}
2022-02-13 17:33:53 +01:00
2022-04-25 21:21:06 +02:00
return {};
2013-07-13 10:10:46 +02:00
}
QString ProxyPython::shortDescription(unsigned int key) const
{
2022-04-25 21:21:06 +02:00
switch (static_cast<FailureType>(key)) {
2022-02-13 17:33:53 +01:00
case FailureType::SEMICOLON:
2022-04-25 21:21:06 +02:00
return tr("ModOrganizer path contains a semicolon");
2022-02-13 17:33:53 +01:00
case FailureType::DLL_NOT_FOUND:
2022-04-25 21:21:06 +02:00
return tr("Python DLL not found");
2022-02-13 17:33:53 +01:00
case FailureType::INVALID_DLL:
2022-04-25 21:21:06 +02:00
return tr("Invalid Python DLL");
2022-02-13 17:33:53 +01:00
case FailureType::INITIALIZATION:
2022-04-25 21:21:06 +02:00
return tr("Initializing Python failed");
2013-07-13 10:10:46 +02:00
default:
2022-04-25 21:21:06 +02:00
return tr("invalid problem key %1").arg(key);
}
2013-07-13 10:10:46 +02:00
}
QString ProxyPython::fullDescription(unsigned int key) const
{
2022-04-25 21:21:06 +02:00
switch (static_cast<FailureType>(key)) {
case FailureType::SEMICOLON:
return tr("The path to Mod Organizer (%1) contains a semicolon. <br>"
"While this is legal on NTFS drives, many softwares do not handle it "
"correctly.<br>"
"Unfortunately MO depends on libraries that seem to fall into that "
"group.<br>"
"As a result the python plugin cannot be loaded, and the only "
"solution we can"
"offer is to remove the semicolon or move MO to a path without a "
"semicolon.")
.arg(QCoreApplication::applicationDirPath());
case FailureType::DLL_NOT_FOUND:
return tr("The Python plugin DLL was not found, maybe your antivirus deleted "
"it. Re-installing MO2 might fix the problem.");
case FailureType::INVALID_DLL:
return tr(
"The Python plugin DLL is invalid, maybe your antivirus is blocking it. "
"Re-installing MO2 and adding exclusions for it to your AV might fix the "
"problem.");
case FailureType::INITIALIZATION:
return tr("The initialization of the Python plugin DLL failed, unfortunately "
"without any details.");
default:
return tr("invalid problem key %1").arg(key);
}
2013-07-13 10:10:46 +02:00
}
bool ProxyPython::hasGuidedFix(unsigned int key) const
2013-07-13 10:10:46 +02:00
{
2022-04-25 21:21:06 +02:00
return false;
2013-07-13 10:10:46 +02:00
}
2022-04-25 21:21:06 +02:00
void ProxyPython::startGuidedFix(unsigned int key) const {}