Fixed detection of 64-Bit VapourSynth when 32-Bit VapourSynth is *not* installed...
[simple-x264-launcher.git] / src / thread_vapoursynth.cpp
blobe3991dea303f9926fbc43df14dd6b4f8de402214
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2016 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "thread_vapoursynth.h"
24 //Mutils
25 #include <MUtils/OSSupport.h>
26 #include <MUtils/Registry.h>
28 //Qt
29 #include <QLibrary>
30 #include <QEventLoop>
31 #include <QTimer>
32 #include <QMutexLocker>
33 #include <QApplication>
34 #include <QDir>
35 #include <QProcess>
37 //Internal
38 #include "global.h"
39 #include "model_sysinfo.h"
41 //CRT
42 #include <cassert>
44 //Static
45 QMutex VapourSynthCheckThread::m_vpsLock;
46 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
47 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
49 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
50 #define BOOLIFY(X) ((X) ? '1' : '0')
52 static inline QString &cleanDir(QString &path)
54 if(!path.isEmpty())
56 path = QDir::fromNativeSeparators(path);
57 while(path.endsWith('/'))
59 path.chop(1);
62 return path;
65 //-------------------------------------
66 // External API
67 //-------------------------------------
69 bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
71 sysinfo->clearVapourSynth();
72 sysinfo->clearVPSPath();
74 QMutexLocker lock(&m_vpsLock);
76 QEventLoop loop;
77 VapourSynthCheckThread thread;
79 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
81 connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
82 connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
84 thread.start();
85 QTimer::singleShot(15000, &loop, SLOT(quit()));
87 qDebug("VapourSynth thread has been created, please wait...");
88 loop.exec(QEventLoop::ExcludeUserInputEvents);
89 qDebug("VapourSynth thread finished.");
91 QApplication::restoreOverrideCursor();
93 if(!thread.wait(1000))
95 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
96 thread.terminate();
97 thread.wait();
98 return false;
101 if(thread.getException())
103 qWarning("VapourSynth thread encountered an exception !!!");
104 return false;
107 if(thread.getSuccess())
109 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, thread.getSuccess() & VAPOURSYNTH_X86);
110 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, thread.getSuccess() & VAPOURSYNTH_X64);
111 sysinfo->setVPSPath(thread.getPath());
112 qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
114 else
116 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
119 return true;
122 //-------------------------------------
123 // Thread class
124 //-------------------------------------
126 VapourSynthCheckThread::VapourSynthCheckThread(void)
128 m_success &= 0;
129 m_exception = false;
130 m_vpsPath.clear();
133 VapourSynthCheckThread::~VapourSynthCheckThread(void)
137 void VapourSynthCheckThread::run(void)
139 m_success &= 0;
140 m_exception = false;
141 m_vpsPath.clear();
143 detectVapourSynthPath1(m_success, m_vpsPath, &m_exception);
146 void VapourSynthCheckThread::detectVapourSynthPath1(int &success, QString &path, volatile bool *exception)
148 __try
150 return detectVapourSynthPath2(success, path, exception);
152 __except(1)
154 *exception = true;
155 qWarning("Unhandled exception error in VapourSynth thread !!!");
159 void VapourSynthCheckThread::detectVapourSynthPath2(int &success, QString &path, volatile bool *exception)
163 return detectVapourSynthPath3(success, path);
165 catch(...)
167 *exception = true;
168 qWarning("VapourSynth initializdation raised an C++ exception!");
172 void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
174 success &= 0;
175 path.clear();
177 static const char *VPS_REG_KEYS[] =
179 "SOFTWARE\\VapourSynth",
180 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
181 NULL
183 static const char *VPS_REG_NAME[] =
185 "Path",
186 "InstallLocation",
187 "Inno Setup: App Path",
188 NULL
190 static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
192 MUtils::Registry::scope_default,
193 MUtils::Registry::scope_wow_x32,
194 MUtils::Registry::scope_wow_x64
197 //Read VapourSynth path from registry
198 QString vapoursynthPath;
199 for(size_t i = 0; VPS_REG_KEYS[i]; i++)
201 for(size_t j = 0; VPS_REG_NAME[j]; j++)
203 for (size_t k = 0; k < 3; k++)
205 QString temp;
206 if (MUtils::Registry::reg_value_read(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), QString::fromLatin1(VPS_REG_NAME[j]), temp, REG_SCOPE[k]))
208 temp = cleanDir(temp);
209 if (VALID_DIR(temp))
211 vapoursynthPath = temp;
212 break;
216 if (!vapoursynthPath.isEmpty())
218 break;
221 if (!vapoursynthPath.isEmpty())
223 break;
227 //Make sure VapourSynth does exist
228 if(!VALID_DIR(vapoursynthPath))
230 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
231 return;
234 qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
236 //Look for 32-Bit edition of VapourSynth first
237 QFile *vpsExeFile32, *vpsDllFile32;
238 if(isVapourSynthComplete(QString("%1/core32").arg(vapoursynthPath), vpsExeFile32, vpsDllFile32))
240 if(vpsExeFile32 && checkVapourSynth(vpsExeFile32->fileName()))
242 success |= VAPOURSYNTH_X86;
243 qDebug("VapourSynth 32-Bit edition found!");
244 m_vpsExePath[0].reset(vpsExeFile32);
245 m_vpsDllPath[0].reset(vpsDllFile32);
247 else
249 qWarning("VapourSynth 32-Bit edition was found, but version check has failed!");
252 else
254 qDebug("VapourSynth 32-Bit edition *not* found!");
257 //Look for 64-Bit edition of VapourSynth next
258 QFile *vpsExeFile64, *vpsDllFile64;
259 if(isVapourSynthComplete(QString("%1/core64").arg(vapoursynthPath), vpsExeFile64, vpsDllFile64))
261 if(vpsExeFile64 && checkVapourSynth(vpsExeFile64->fileName()))
263 success |= VAPOURSYNTH_X64;
264 qDebug("VapourSynth 64-Bit edition found!");
265 m_vpsExePath[1].reset(vpsExeFile64);
266 m_vpsDllPath[1].reset(vpsDllFile64);
268 else
270 qWarning("VapourSynth 64-Bit edition was found, but version check has failed!");
273 else
275 qDebug("VapourSynth 64-Bit edition *not* found!");
278 //Return VapourSynth path
279 if(success)
281 path = vapoursynthPath;
285 bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
287 bool complete = false;
288 vpsExeFile = vpsDllFile = NULL;
290 QFileInfo vpsExeInfo(QString("%1/vspipe.exe" ).arg(vsCorePath));
291 QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
293 qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
294 qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
296 if(vpsExeInfo.exists() && vpsDllInfo.exists())
298 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
299 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
300 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
302 complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
306 if(!complete)
308 MUTILS_DELETE(vpsExeFile);
309 MUTILS_DELETE(vpsDllFile);
312 return complete;
315 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
317 QProcess process;
318 QStringList output;
320 //Setup process object
321 process.setWorkingDirectory(QDir::tempPath());
322 process.setProcessChannelMode(QProcess::MergedChannels);
323 process.setReadChannel(QProcess::StandardOutput);
325 //Try to start VSPIPE.EXE
326 process.start(vspipePath, QStringList() << "--version");
327 if(!process.waitForStarted())
329 qWarning("Failed to launch VSPIPE.EXE -> %s", process.errorString().toUtf8().constData());
330 return false;
333 //Wait for process to finish
334 while(process.state() != QProcess::NotRunning)
336 if(process.waitForReadyRead(12000))
338 while(process.canReadLine())
340 output << QString::fromUtf8(process.readLine()).simplified();
342 continue;
344 if(process.state() != QProcess::NotRunning)
346 qWarning("VSPIPE.EXE process encountered a deadlock -> aborting now!");
347 break;
351 //Make sure VSPIPE.EXE has terminated!
352 process.waitForFinished(2500);
353 if(process.state() != QProcess::NotRunning)
355 qWarning("VSPIPE.EXE process still running, going to kill it!");
356 process.kill();
357 process.waitForFinished(-1);
360 //Read pending lines
361 while(process.canReadLine())
363 output << QString::fromUtf8(process.readLine()).simplified();
366 //Check exit code
367 if(process.exitCode() != 0)
369 qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
370 return false;
373 //Init regular expressions
374 QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
376 //Check for version info
377 bool vapoursynthLogo = false;
378 for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
380 if(vpsLogo.lastIndexIn(*iter) >= 0)
382 vapoursynthLogo = true;
383 break;
387 //Minimum required version found?
388 if(vapoursynthLogo)
390 qDebug("VapourSynth version was detected successfully.");
391 return true;
394 //Failed to determine version
395 qWarning("Failed to determine VapourSynth version!");
396 return false;