OSDN Git Service

Updated copyright year.
[x264-launcher/x264-launcher.git] / src / thread_vapoursynth.cpp
index 4c758cf..d98947b 100644 (file)
@@ -1,6 +1,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // Simple x264 Launcher
-// Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2022 LoRd_MuldeR <MuldeR2@GMX.de>
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
 
 #include "thread_vapoursynth.h"
 
-#include <QLibrary>
+//Mutils
+#include <MUtils/OSSupport.h>
+#include <MUtils/Registry.h>
+
+//Qt
 #include <QEventLoop>
 #include <QTimer>
-#include <QMutexLocker>
 #include <QApplication>
 #include <QDir>
-#include <QProcess>
+#include <QHash>
+#include <QAbstractFileEngine.h>
 
+//Internal
 #include "global.h"
+#include "model_sysinfo.h"
 
-static const unsigned int VAPOURSYNTH_VERSION_MIN = 20;
+//CRT
+#include <cassert>
 
+//Static
 QMutex VapourSynthCheckThread::m_vpsLock;
-QFile *VapourSynthCheckThread::m_vpsExePath = NULL;
-QFile *VapourSynthCheckThread::m_vpsDllPath = NULL;
-QLibrary *VapourSynthCheckThread::m_vpsLib = NULL;
-
-#define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
-
-static inline QString &cleanDir(QString &path)
-{
-       if(!path.isEmpty())
-       {
-               path = QDir::fromNativeSeparators(path);
-               while(path.endsWith('/'))
-               {
-                       path.chop(1);
-               }
-       }
-       return path;
-}
+QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
+QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
+
+//Const
+static const char* const VPS_DLL_NAME = "vapoursynth.dll";
+static const char* const VPS_EXE_NAME = "vspipe.exe";
+static const char* const VPS_REG_KEY1 = "SOFTWARE\\VapourSynth";
+static const char* const VPS_REG_KEY2 = "SOFTWARE\\VapourSynth-32";
+static const char* const VPS_REG_NAME = "VapourSynthDLL";
+
+//Default VapurSynth architecture
+#if _WIN64 || __x86_64__
+#define VAPOURSYNTH_DEF VAPOURSYNTH_X64
+#else
+#define VAPOURSYNTH_DEF VAPOURSYNTH_X86;
+#endif
+
+//Enable detection of "portabel" edition?
+#define ENABLE_PORTABLE_VPS true
+
+//EOL flags
+#define REG_ROOT_EOL  (MUtils::Registry::reg_root_t (-1))
+#define REG_SCOPE_EOL (MUtils::Registry::reg_scope_t(-1))
+
+//Auxilary functions
+#define BOOLIFY(X) ((X) ? '1' : '0')
+#define VPS_BITNESS(X) (((X) + 1U) * 32U)
 
 //-------------------------------------
 // External API
 //-------------------------------------
 
-int VapourSynthCheckThread::detect(QString &path)
+bool VapourSynthCheckThread::detect(SysinfoModel* sysinfo)
 {
-       path.clear();
        QMutexLocker lock(&m_vpsLock);
 
+       sysinfo->clearVapourSynth();
+       sysinfo->clearVPS32Path();
+       sysinfo->clearVPS64Path();
+
        QEventLoop loop;
        VapourSynthCheckThread thread;
 
@@ -69,83 +89,61 @@ int VapourSynthCheckThread::detect(QString &path)
 
        connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
        connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
-       
+
        thread.start();
-       QTimer::singleShot(15000, &loop, SLOT(quit()));
-       
+       QTimer::singleShot(30000, &loop, SLOT(quit()));
+
        qDebug("VapourSynth thread has been created, please wait...");
        loop.exec(QEventLoop::ExcludeUserInputEvents);
        qDebug("VapourSynth thread finished.");
 
        QApplication::restoreOverrideCursor();
 
-       if(!thread.wait(1000))
+       if (!thread.wait(1000))
        {
                qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
                thread.terminate();
                thread.wait();
-               return -1;
+               return false;
        }
 
-       if(thread.getException())
+       if (thread.getException())
        {
                qWarning("VapourSynth thread encountered an exception !!!");
-               return -1;
-       }
-       
-       if(thread.getSuccess())
-       {
-               path = thread.getPath();
-               qDebug("VapourSynth check completed successfully.");
-               return 1;
+               return false;
        }
 
-       qWarning("VapourSynth thread failed to detect installation!");
-       return 0;
-}
-
-void VapourSynthCheckThread::unload(void)
-{
-       QMutexLocker lock(&m_vpsLock);
-
-       if(m_vpsLib)
+       const int success = thread.getSuccess();
+       if (!success)
        {
-               if(m_vpsLib->isLoaded())
-               {
-                       m_vpsLib->unload();
-               }
+               qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
+               return true;
        }
 
-       if(m_vpsExePath)
+       if (success & VAPOURSYNTH_X86)
        {
-               if (m_vpsExePath->isOpen())
-               {
-                       m_vpsExePath->close();
-               }
+               sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
+               sysinfo->setVPS32Path(thread.getPath32());
        }
 
-       if(m_vpsDllPath)
+       if (success & VAPOURSYNTH_X64)
        {
-               if(m_vpsDllPath->isOpen())
-               {
-                       m_vpsDllPath->close();
-               }
+               sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
+               sysinfo->setVPS64Path(thread.getPath64());
        }
 
-       X264_DELETE(m_vpsExePath);
-       X264_DELETE(m_vpsDllPath);
-       X264_DELETE(m_vpsLib);
+       qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
+       return true;
 }
 
 //-------------------------------------
-// Thread class
+// Thread functions
 //-------------------------------------
 
 VapourSynthCheckThread::VapourSynthCheckThread(void)
 {
-       m_success = false;
-       m_exception = false;
-       m_vpsPath.clear();
+       m_vpsPath[0U].clear();
+       m_vpsPath[1U].clear();
 }
 
 VapourSynthCheckThread::~VapourSynthCheckThread(void)
@@ -154,222 +152,222 @@ VapourSynthCheckThread::~VapourSynthCheckThread(void)
 
 void VapourSynthCheckThread::run(void)
 {
-       m_exception = m_success = false;
-       m_success = detectVapourSynthPath1(m_vpsPath, &m_exception);
-}
-
-bool VapourSynthCheckThread::detectVapourSynthPath1(QString &path, volatile bool *exception)
-{
-       __try
-       {
-               return detectVapourSynthPath2(path, exception);
-       }
-       __except(1)
-       {
-               *exception = true;
-               qWarning("Unhandled exception error in VapourSynth thread !!!");
-               return false;
-       }
+       m_vpsPath[0U].clear();
+       m_vpsPath[1U].clear();
+       StarupThread::run();
 }
 
-bool VapourSynthCheckThread::detectVapourSynthPath2(QString &path, volatile bool *exception)
+int VapourSynthCheckThread::threadMain(void)
 {
-       try
+       static const int VPS_BIT_FLAG[] =
        {
-               return detectVapourSynthPath3(path);
-       }
-       catch(...)
+               VAPOURSYNTH_X86,
+               VAPOURSYNTH_X64,
+               NULL
+       };
+       static const MUtils::Registry::reg_root_t REG_ROOTS[] =
        {
-               *exception = true;
-               qWarning("VapourSynth initializdation raised an C++ exception!");
-               return false;
-       }
-}
-
-bool VapourSynthCheckThread::detectVapourSynthPath3(QString &path)
-{
-       bool success = false;
-       X264_DELETE(m_vpsExePath);
-       X264_DELETE(m_vpsDllPath);
-       path.clear();
-
-       static const char *VPS_REG_KEYS[] = 
+               MUtils::Registry::root_machine,
+               MUtils::Registry::root_user,
+               REG_ROOT_EOL
+       };
+       static const char* const REG_PATHS_HKLM[] =
        {
-               "SOFTWARE\\VapourSynth",
-               "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
+               VPS_REG_KEY1,
                NULL
        };
-       static const char *VPS_REG_NAME[] =
+       static const char* const REG_PATHS_HKCU[] =
        {
-               "Path",
-               "InstallLocation",
-               "Inno Setup: App Path",
+               VPS_REG_KEY1,
+               VPS_REG_KEY2,
                NULL
        };
+       static const MUtils::Registry::reg_scope_t REG_SCOPE_X86[] =
+       {
+               MUtils::Registry::scope_default,
+               REG_SCOPE_EOL
+       };
+       static const MUtils::Registry::reg_scope_t REG_SCOPE_X64[] =
+       {
+               MUtils::Registry::scope_wow_x32,
+               MUtils::Registry::scope_wow_x64,
+               REG_SCOPE_EOL
+       };
+
+       QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
+       int flags = 0;
+
+       //Look for "portable" VapourSynth version
+       for (size_t i = 0; i < 2U; i++)
+       {
+               const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
+               if (vpsPortableDir.exists())
+               {
+                       const QFileInfo vpsPortableDll(vpsPortableDir.absoluteFilePath(VPS_DLL_NAME));
+                       const QFileInfo vpsPortableExe(vpsPortableDir.absoluteFilePath(VPS_EXE_NAME));
+                       if ((vpsPortableDll.exists() && vpsPortableDll.isFile()) || (vpsPortableExe.exists() && vpsPortableExe.isFile()))
+                       {
+                               vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
+                               vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
+                       }
+               }
+       }
 
        //Read VapourSynth path from registry
-       QString vapoursynthPath;
-       for(size_t i = 0; VPS_REG_KEYS[i]; i++)
+       if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
        {
-               for(size_t j = 0; VPS_REG_NAME[j]; j++)
+               for (size_t i = 0; REG_ROOTS[i] != REG_ROOT_EOL; i++)
                {
-                       vapoursynthPath = cleanDir(x264_query_reg_string(false, VPS_REG_KEYS[i], VPS_REG_NAME[j]));
-                       if(VALID_DIR(vapoursynthPath)) break;
+                       const char *const *const paths = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? REG_PATHS_HKLM : REG_PATHS_HKCU;
+                       const MUtils::Registry::reg_scope_t* const scopes = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? ((MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64) ? REG_SCOPE_X64 : REG_SCOPE_X86) : REG_SCOPE_X86;
+                       for (size_t j = 0; paths[j]; j++)
+                       {
+                               for (size_t k = 0; scopes[k] != REG_SCOPE_EOL; k++)
+                               {
+                                       if (MUtils::Registry::reg_key_exists(REG_ROOTS[i], QString::fromLatin1(paths[j]), scopes[k]))
+                                       {
+                                               QString vpsRegDllPath;
+                                               if (MUtils::Registry::reg_value_read(REG_ROOTS[i], QString::fromLatin1(paths[j]), QString::fromLatin1(VPS_REG_NAME), vpsRegDllPath, scopes[k]))
+                                               {
+                                                       QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
+                                                       vpsRegDllInfo.makeAbsolute();
+                                                       if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
+                                                       {
+                                                               const int vpsArch = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? getVapourSynthType(scopes[k]) : ((j > 0U) ? VAPOURSYNTH_X86 : VAPOURSYNTH_X64);
+                                                               if ((!vpsDllInfo.contains(vpsArch)) || (!vpsExeInfo.contains(vpsArch)))
+                                                               {
+                                                                       vpsDllInfo.insert(vpsArch, vpsRegDllInfo);
+                                                                       vpsExeInfo.insert(vpsArch, vpsRegDllInfo.absoluteDir().absoluteFilePath(VPS_EXE_NAME)); /*derive VSPipe.EXE path from VapourSynth.DLL path!*/
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
                }
-               if(VALID_DIR(vapoursynthPath)) break;
        }
 
-       //Make sure VapourSynth does exist
-       if(!VALID_DIR(vapoursynthPath))
+       //Abort, if VapourSynth was *not* found
+       if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
        {
                qWarning("VapourSynth install path not found -> disable VapouSynth support!");
-               vapoursynthPath.clear();
+               return 0;
        }
 
-       //Make sure that 'vapoursynth.dll' and 'vspipe.exe' are available
-       bool vapoursynthComplete = false;
-       if(!vapoursynthPath.isEmpty())
+       //Validate the VapourSynth installation now!
+       for (size_t i = 0; i < 2U; i++)
        {
-               static const char *CORE_PATH[3] = { "core32", "core64", "core" };
-               qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
-               for(int i = 0; (i < 3) && (!vapoursynthComplete); i++)
+               qDebug("VapourSynth %u-Bit support is being tested.", VPS_BITNESS(i));
+               if (vpsDllInfo.contains(VPS_BIT_FLAG[i]) && vpsExeInfo.contains(VPS_BIT_FLAG[i]))
                {
-                       QFileInfo vpsExeInfo(QString("%1/%2/vspipe.exe"     ).arg(vapoursynthPath, CORE_PATH[i]));
-                       QFileInfo vpsDllInfo(QString("%1/%2/vapoursynth.dll").arg(vapoursynthPath, CORE_PATH[i]));
-                       qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
-                       qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
-                       if(vpsExeInfo.exists() && vpsDllInfo.exists())
+                       QFile *vpsExeFile, *vpsDllFile;
+                       if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
                        {
-                               m_vpsExePath = new QFile(vpsExeInfo.canonicalFilePath());
-                               m_vpsDllPath = new QFile(vpsDllInfo.canonicalFilePath());
-                               if(m_vpsExePath->open(QIODevice::ReadOnly) && m_vpsDllPath->open(QIODevice::ReadOnly))
+                               m_vpsExePath[i].reset(vpsExeFile);
+                               m_vpsDllPath[i].reset(vpsDllFile);
+                               if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
                                {
-                                       if(vapoursynthComplete = x264_is_executable(m_vpsExePath->fileName()))
-                                       {
-                                               vapoursynthPath.append("/").append(CORE_PATH[i]);
-                                       }
-                                       break;
+                                       qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
+                                       m_vpsPath[i] = m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalPathName);
+                                       flags |= VPS_BIT_FLAG[i];
+                               }
+                               else
+                               {
+                                       qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
                                }
-                               X264_DELETE(m_vpsExePath);
-                               X264_DELETE(m_vpsDllPath);
+                       }
+                       else
+                       {
+                               qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
                        }
                }
-               if(!vapoursynthComplete)
+               else
                {
-                       qWarning("VapourSynth installation incomplete -> disable VapouSynth support!");
+                       qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
                }
        }
 
-       //Make sure 'vsscript.dll' can be loaded successfully
-       if(vapoursynthComplete && m_vpsExePath)
+       return flags;
+}
+
+//-------------------------------------
+// Internal functions
+//-------------------------------------
+
+VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
+{
+       if (MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64)
        {
-               qDebug("VapourSynth detection is running, please stand by...");
-               success = checkVapourSynthVersion(m_vpsExePath->fileName());
+               switch (scope)
+               {
+               case MUtils::Registry::scope_wow_x32:
+                       return VAPOURSYNTH_X86;
+               case MUtils::Registry::scope_wow_x64:
+                       return VAPOURSYNTH_X64;
+               default:
+                       return VAPOURSYNTH_DEF;
+               }
        }
-
-       //Return VapourSynth path
-       if(success)
+       else
        {
-               path = vapoursynthPath;
+               return VAPOURSYNTH_X86; /*ignore scope on 32-Bit OS*/
        }
-
-       return success;
 }
 
-bool VapourSynthCheckThread::checkVapourSynthVersion(const QString vspipePath)
+bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
 {
-       QProcess process;
-       QStringList output;
-
-       //Setup process object
-       process.setWorkingDirectory(QDir::tempPath());
-       process.setProcessChannelMode(QProcess::MergedChannels);
-       process.setReadChannel(QProcess::StandardOutput);
-
-       //Try to start VSPIPE.EXE
-       process.start(vspipePath, QStringList() << "-version");
-       if(!process.waitForStarted())
-       {
-               qWarning("Failed to launch VSPIPE.EXE -> %s", process.errorString().toUtf8().constData());
-               return false;
-       }
+       bool complete = false;
+       vpsExeFile = vpsDllFile = NULL;
+       
+       qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
+       qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
 
-       //Wait for process to finish
-       while(process.state() != QProcess::NotRunning)
+       if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
        {
-               if(process.waitForReadyRead(12000))
+               vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
+               vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
+               if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
                {
-                       while(process.canReadLine())
-                       {
-                               output << QString::fromUtf8(process.readLine()).simplified();
-                       }
-                       continue;
-               }
-               if(process.state() != QProcess::NotRunning)
-               {
-                       qWarning("VSPIPE.EXE process encountered a deadlock -> aborting now!");
-                       break;
+                       complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
                }
        }
 
-       //Make sure VSPIPE.EXE has terminated!
-       process.waitForFinished(2500);
-       if(process.state() != QProcess::NotRunning)
+       if(!complete)
        {
-               qWarning("VSPIPE.EXE process still running, going to kill it!");
-               process.kill();
-               process.waitForFinished(-1);
+               MUTILS_DELETE(vpsExeFile);
+               MUTILS_DELETE(vpsDllFile);
        }
 
-       //Read pending lines
-       while(process.canReadLine())
-       {
-               output << QString::fromUtf8(process.readLine()).simplified();
-       }
+       return complete;
+}
 
-       //Check exit code
-       if(process.exitCode() != 0)
-       {
-               qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
-               return false;
-       }
+bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
+{
+       //Try to run VSPIPE.EXE
+       const QStringList output = runProcess(vspipePath, QStringList() << "--version");
 
        //Init regular expressions
-       unsigned int vapursynthVersion = 0;
-       bool vapoursynthLogo = false;
        QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
-       QRegExp vpsCore("Core\\s+r(\\d+)");
 
        //Check for version info
+       bool vapoursynthLogo = false;
        for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
        {
                if(vpsLogo.lastIndexIn(*iter) >= 0)
                {
                        vapoursynthLogo = true;
-                       continue;
-               }
-               if(vapoursynthLogo && (vpsCore.lastIndexIn(*iter) >= 0))
-               {
-                       bool ok = false;
-                       const unsigned int temp = vpsCore.cap(1).toUInt(&ok);
-                       if(ok) vapursynthVersion = temp;
+                       break;
                }
        }
 
        //Minimum required version found?
-       if(vapoursynthLogo && (vapursynthVersion > 0))
+       if(vapoursynthLogo)
        {
-               qDebug("VapourSynth version \"Core r%u\" detected.", vapursynthVersion);
-               if(vapursynthVersion < VAPOURSYNTH_VERSION_MIN)
-               {
-                       qWarning("VapourSynth version is too old -> disable Vapousynth support!");
-                       return false;
-               }
+               qDebug("VapourSynth version was detected successfully.");
                return true;
        }
 
        //Failed to determine version
-       qWarning("Failed to determine VapourSynth version -> disable Vapousynth support!");
-       qWarning("VapourSynth version is unsupported or VapourSynth installation is corrupted.");
+       qWarning("Failed to determine VapourSynth version!");
        return false;
 }