OSDN Git Service

Updated RHash and UPX to the latest versions.
[x264-launcher/x264-launcher.git] / src / thread_vapoursynth.cpp
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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "thread_vapoursynth.h"
23
24 //Mutils
25 #include <MUtils/OSSupport.h>
26 #include <MUtils/Registry.h>
27
28 //Qt
29 #include <QLibrary>
30 #include <QEventLoop>
31 #include <QTimer>
32 #include <QMutexLocker>
33 #include <QApplication>
34 #include <QDir>
35 #include <QProcess>
36
37 //Internal
38 #include "global.h"
39 #include "model_sysinfo.h"
40
41 //CRT
42 #include <cassert>
43
44 //Const
45 static const bool ENABLE_PORTABLE_VPS = true;
46
47 //Static
48 QMutex VapourSynthCheckThread::m_vpsLock;
49 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
50 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
51
52 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
53 #define BOOLIFY(X) ((X) ? '1' : '0')
54 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
55
56 static inline QString &cleanDir(QString &path)
57 {
58         if(!path.isEmpty())
59         {
60                 path = QDir::fromNativeSeparators(path);
61                 while(path.endsWith('/'))
62                 {
63                         path.chop(1);
64                 }
65         }
66         return path;
67 }
68
69 //-------------------------------------
70 // External API
71 //-------------------------------------
72
73 bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
74 {
75         sysinfo->clearVapourSynth();
76         sysinfo->clearVPSPath();
77         
78         QMutexLocker lock(&m_vpsLock);
79
80         QEventLoop loop;
81         VapourSynthCheckThread thread;
82
83         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
84
85         connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
86         connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
87         
88         thread.start();
89         QTimer::singleShot(15000, &loop, SLOT(quit()));
90         
91         qDebug("VapourSynth thread has been created, please wait...");
92         loop.exec(QEventLoop::ExcludeUserInputEvents);
93         qDebug("VapourSynth thread finished.");
94
95         QApplication::restoreOverrideCursor();
96
97         if(!thread.wait(1000))
98         {
99                 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
100                 thread.terminate();
101                 thread.wait();
102                 return false;
103         }
104
105         if(thread.getException())
106         {
107                 qWarning("VapourSynth thread encountered an exception !!!");
108                 return false;
109         }
110
111         if(thread.getSuccess())
112         {
113                 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, thread.getSuccess() & VAPOURSYNTH_X86);
114                 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, thread.getSuccess() & VAPOURSYNTH_X64);
115                 sysinfo->setVPSPath(thread.getPath());
116                 qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
117         }
118         else
119         {
120                 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
121         }
122
123         return true;
124 }
125
126 //-------------------------------------
127 // Thread class
128 //-------------------------------------
129
130 VapourSynthCheckThread::VapourSynthCheckThread(void)
131 {
132         m_success &= 0;
133         m_exception = false;
134         m_vpsPath.clear();
135 }
136
137 VapourSynthCheckThread::~VapourSynthCheckThread(void)
138 {
139 }
140
141 void VapourSynthCheckThread::run(void)
142 {
143         m_success &= 0;
144         m_exception = false;
145         m_vpsPath.clear();
146
147         detectVapourSynthPath1(m_success, m_vpsPath, &m_exception);
148 }
149
150 void VapourSynthCheckThread::detectVapourSynthPath1(int &success, QString &path, volatile bool *exception)
151 {
152         __try
153         {
154                 return detectVapourSynthPath2(success, path, exception);
155         }
156         __except(1)
157         {
158                 *exception = true;
159                 qWarning("Unhandled exception error in VapourSynth thread !!!");
160         }
161 }
162
163 void VapourSynthCheckThread::detectVapourSynthPath2(int &success, QString &path, volatile bool *exception)
164 {
165         try
166         {
167                 return detectVapourSynthPath3(success, path);
168         }
169         catch(...)
170         {
171                 *exception = true;
172                 qWarning("VapourSynth initializdation raised an C++ exception!");
173         }
174 }
175
176 void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
177 {
178         success &= 0;
179         path.clear();
180
181         static const char *VPS_CORE_DIR[] =
182         {
183                 "core32",
184                 "core64",
185                 NULL
186         };
187         static const int VPS_BIT_FLAG[] =
188         {
189                 VAPOURSYNTH_X86,
190                 VAPOURSYNTH_X64,
191                 NULL
192         };
193         static const char *VPS_REG_KEYS[] = 
194         {
195                 "SOFTWARE\\VapourSynth",
196                 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
197                 NULL
198         };
199         static const char *VPS_REG_NAME[] =
200         {
201                 "Path",
202                 "InstallLocation",
203                 "Inno Setup: App Path",
204                 NULL
205         };
206         static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
207         {
208                 MUtils::Registry::scope_default,
209                 MUtils::Registry::scope_wow_x32,
210                 MUtils::Registry::scope_wow_x64
211         };
212
213         QString vapoursynthPath;
214
215         //Look for "portable" VapourSynth version
216         if (ENABLE_PORTABLE_VPS)
217         {
218                 const QString vpsPortableDir = QString("%1/extra/VapourSynth").arg(QCoreApplication::applicationDirPath());
219                 if (VALID_DIR(vpsPortableDir))
220                 {
221                         for (size_t i = 0; VPS_CORE_DIR[i]; i++)
222                         {
223                                 const QFileInfo vpsPortableDll = QFileInfo(QString("%1/%2/VapourSynth.dll").arg(vpsPortableDir, QString::fromLatin1(VPS_CORE_DIR[i])));
224                                 if (vpsPortableDll.exists() && vpsPortableDll.isFile())
225                                 {
226                                         vapoursynthPath = vpsPortableDir;
227                                         break;
228                                 }
229                         }
230                 }
231         }
232
233         //Read VapourSynth path from registry
234         if (vapoursynthPath.isEmpty())
235         {
236                 for (size_t i = 0; VPS_REG_KEYS[i]; i++)
237                 {
238                         for (size_t j = 0; VPS_REG_NAME[j]; j++)
239                         {
240                                 for (size_t k = 0; k < 3; k++)
241                                 {
242                                         if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), REG_SCOPE[k]))
243                                         {
244                                                 QString temp;
245                                                 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]))
246                                                 {
247                                                         temp = cleanDir(temp);
248                                                         if (VALID_DIR(temp))
249                                                         {
250                                                                 vapoursynthPath = temp;
251                                                                 break;
252                                                         }
253                                                 }
254                                         }
255                                 }
256                                 if (!vapoursynthPath.isEmpty())
257                                 {
258                                         break;
259                                 }
260                         }
261                         if (!vapoursynthPath.isEmpty())
262                         {
263                                 break;
264                         }
265                 }
266         }
267
268         //Make sure VapourSynth directory does exist
269         if(vapoursynthPath.isEmpty())
270         {
271                 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
272                 return;
273         }
274
275         //Validate the VapourSynth installation now!
276         qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
277         for (size_t i = 0; VPS_CORE_DIR[i]; i++)
278         {
279                 QFile *vpsExeFile, *vpsDllFile;
280                 if (isVapourSynthComplete(QString("%1/%2").arg(vapoursynthPath, QString::fromLatin1(VPS_CORE_DIR[i])), vpsExeFile, vpsDllFile))
281                 {
282                         if (vpsExeFile && checkVapourSynth(vpsExeFile->fileName()))
283                         {
284                                 success |= VPS_BIT_FLAG[i];
285                                 qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
286                                 m_vpsExePath[i].reset(vpsExeFile);
287                                 m_vpsDllPath[i].reset(vpsDllFile);
288                         }
289                         else
290                         {
291                                 qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
292                         }
293                 }
294                 else
295                 {
296                         qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
297                 }
298         }
299
300         //Return VapourSynth path
301         if(success)
302         {
303                 path = vapoursynthPath;
304         }
305 }
306
307 bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
308 {
309         bool complete = false;
310         vpsExeFile = vpsDllFile = NULL;
311
312         QFileInfo vpsExeInfo(QString("%1/vspipe.exe"     ).arg(vsCorePath));
313         QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
314         
315         qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
316         qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
317
318         if(vpsExeInfo.exists() && vpsDllInfo.exists())
319         {
320                 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
321                 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
322                 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
323                 {
324                         complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
325                 }
326         }
327
328         if(!complete)
329         {
330                 MUTILS_DELETE(vpsExeFile);
331                 MUTILS_DELETE(vpsDllFile);
332         }
333
334         return complete;
335 }
336
337 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
338 {
339         QProcess process;
340         QStringList output;
341
342         //Setup process object
343         process.setWorkingDirectory(QDir::tempPath());
344         process.setProcessChannelMode(QProcess::MergedChannels);
345         process.setReadChannel(QProcess::StandardOutput);
346
347         //Try to start VSPIPE.EXE
348         process.start(vspipePath, QStringList() << "--version");
349         if(!process.waitForStarted())
350         {
351                 qWarning("Failed to launch VSPIPE.EXE -> %s", process.errorString().toUtf8().constData());
352                 return false;
353         }
354
355         //Wait for process to finish
356         while(process.state() != QProcess::NotRunning)
357         {
358                 if(process.waitForReadyRead(12000))
359                 {
360                         while(process.canReadLine())
361                         {
362                                 output << QString::fromUtf8(process.readLine()).simplified();
363                         }
364                         continue;
365                 }
366                 if(process.state() != QProcess::NotRunning)
367                 {
368                         qWarning("VSPIPE.EXE process encountered a deadlock -> aborting now!");
369                         break;
370                 }
371         }
372
373         //Make sure VSPIPE.EXE has terminated!
374         process.waitForFinished(2500);
375         if(process.state() != QProcess::NotRunning)
376         {
377                 qWarning("VSPIPE.EXE process still running, going to kill it!");
378                 process.kill();
379                 process.waitForFinished(-1);
380         }
381
382         //Read pending lines
383         while(process.canReadLine())
384         {
385                 output << QString::fromUtf8(process.readLine()).simplified();
386         }
387
388         //Check exit code
389         if(process.exitCode() != 0)
390         {
391                 qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
392                 return false;
393         }
394
395         //Init regular expressions
396         QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
397
398         //Check for version info
399         bool vapoursynthLogo = false;
400         for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
401         {
402                 if(vpsLogo.lastIndexIn(*iter) >= 0)
403                 {
404                         vapoursynthLogo = true;
405                         break;
406                 }
407         }
408
409         //Minimum required version found?
410         if(vapoursynthLogo)
411         {
412                 qDebug("VapourSynth version was detected successfully.");
413                 return true;
414         }
415
416         //Failed to determine version
417         qWarning("Failed to determine VapourSynth version!");
418         return false;
419 }