Bump version + updated changelog.
[simple-x264-launcher.git] / src / thread_avisynth.cpp
blob491b30cc55c224b72555978395d0462cdcb1db9e
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2018 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_avisynth.h"
24 //Qt
25 #include <QLibrary>
26 #include <QEventLoop>
27 #include <QTimer>
28 #include <QMutexLocker>
29 #include <QApplication>
30 #include <QProcess>
31 #include <QDir>
33 //Internal
34 #include "global.h"
35 #include "model_sysinfo.h"
37 //MUtils
38 #include <MUtils/Global.h>
39 #include <MUtils/OSSupport.h>
41 //Const
42 static const bool ENABLE_PORTABLE_AVS = true;
44 //Static
45 QMutex AvisynthCheckThread::m_avsLock;
46 QScopedPointer<QFile> AvisynthCheckThread::m_avsDllPath[2];
48 //Helper
49 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
50 #define BOOLIFY(X) ((X) ? '1' : '0')
52 //Utility function
53 QString AVS_CHECK_BINARY(const SysinfoModel *sysinfo, const bool& x64)
55 return QString("%1/toolset/%2/avs_check_%2.exe").arg(sysinfo->getAppPath(), (x64 ? "x64": "x86"));
58 class Wow64RedirectionDisabler
60 public:
61 Wow64RedirectionDisabler(void)
63 m_oldValue = NULL;
64 m_disabled = MUtils::OS::wow64fsredir_disable(m_oldValue);
66 ~Wow64RedirectionDisabler(void)
68 if(m_disabled)
70 if(!MUtils::OS::wow64fsredir_revert(m_oldValue))
72 qWarning("Failed to renable WOW64 filesystem redirection!");
76 private:
77 bool m_disabled;
78 void* m_oldValue;
81 //-------------------------------------
82 // External API
83 //-------------------------------------
85 bool AvisynthCheckThread::detect(SysinfoModel *sysinfo)
87 sysinfo->clearAvisynth();
88 double version = 0.0;
89 QMutexLocker lock(&m_avsLock);
91 QEventLoop loop;
92 AvisynthCheckThread thread(sysinfo);
94 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
96 connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
97 connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
99 thread.start();
100 QTimer::singleShot(15000, &loop, SLOT(quit()));
102 qDebug("Avisynth thread has been created, please wait...");
103 loop.exec(QEventLoop::ExcludeUserInputEvents);
104 qDebug("Avisynth thread finished.");
106 QApplication::restoreOverrideCursor();
108 if(!thread.wait(1000))
110 qWarning("Avisynth thread encountered timeout -> probably deadlock!");
111 thread.terminate();
112 thread.wait();
113 return false;
116 if(thread.getException())
118 qWarning("Avisynth thread encountered an exception !!!");
119 return false;
122 if(thread.getSuccess())
124 sysinfo->setAvisynth(SysinfoModel::Avisynth_X86, thread.getSuccess() & AVISYNTH_X86);
125 sysinfo->setAvisynth(SysinfoModel::Avisynth_X64, thread.getSuccess() & AVISYNTH_X64);
126 sysinfo->setAVSPath(thread.getPath());
127 qDebug("Avisynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getAvisynth(SysinfoModel::Avisynth_X86)), BOOLIFY(sysinfo->getAvisynth(SysinfoModel::Avisynth_X64)));
129 else
131 qWarning("Avisynth could not be found -> Avisynth support disabled!");
134 return true;
137 //-------------------------------------
138 // Thread class
139 //-------------------------------------
141 AvisynthCheckThread::AvisynthCheckThread(const SysinfoModel *const sysinfo)
143 m_sysinfo(sysinfo)
145 m_success = false;
146 m_exception = false;
149 AvisynthCheckThread::~AvisynthCheckThread(void)
153 void AvisynthCheckThread::run(void)
155 m_exception = false;
156 m_success &= 0;
157 m_basePath.clear();
159 detectAvisynthVersion1(m_success, m_basePath, m_sysinfo, &m_exception);
162 void AvisynthCheckThread::detectAvisynthVersion1(int &success, QString &basePath, const SysinfoModel *const sysinfo, volatile bool *exception)
164 __try
166 detectAvisynthVersion2(success, basePath, sysinfo, exception);
168 __except(1)
170 *exception = true;
171 qWarning("Unhandled exception error in Avisynth thread !!!");
175 void AvisynthCheckThread::detectAvisynthVersion2(int &success, QString &basePath, const SysinfoModel *const sysinfo, volatile bool *exception)
179 return detectAvisynthVersion3(success, basePath, sysinfo);
181 catch(...)
183 *exception = true;
184 qWarning("Avisynth initializdation raised an C++ exception!");
188 void AvisynthCheckThread::detectAvisynthVersion3(int &success, QString &basePath, const SysinfoModel *const sysinfo)
190 success &= 0;
192 QFile *avsPath32;
193 if(checkAvisynth(basePath, sysinfo, avsPath32, false))
195 m_avsDllPath[0].reset(avsPath32);
196 success |= AVISYNTH_X86;
197 qDebug("Avisynth 32-Bit edition found!");
199 else
201 qDebug("Avisynth 32-Bit edition *not* found!");
204 if(sysinfo->getCPUFeatures(SysinfoModel::CPUFeatures_X64))
206 QFile *avsPath64;
207 if(checkAvisynth(basePath, sysinfo, avsPath64, true))
209 m_avsDllPath[1].reset(avsPath64);
210 success |= AVISYNTH_X64;
211 qDebug("Avisynth 64-Bit edition found!");
213 else
215 qDebug("Avisynth 64-Bit edition *not* found!");
218 else
220 qWarning("Skipping 64-Bit Avisynth check on non-x64 system!");
224 bool AvisynthCheckThread::checkAvisynth(QString &basePath, const SysinfoModel *const sysinfo, QFile *&path, const bool &x64)
226 qDebug("Avisynth %s-Bit support is being tested.", x64 ? "64" : "32");
228 QProcess process;
229 QStringList output;
231 //Look for "portable" Avisynth version
232 static const char *const ARCH_DIR[] = { "x64", "x86" };
233 const QLatin1String archSuffix = QLatin1String(ARCH_DIR[x64 ? 1 : 0]);
234 if (ENABLE_PORTABLE_AVS)
236 const QString avsPortableDir = QString("%1/extra/Avisynth").arg(QCoreApplication::applicationDirPath());
237 if (VALID_DIR(avsPortableDir))
239 QFileInfo avsDllFile(QString("%1/%2/avisynth.dll").arg(avsPortableDir, archSuffix)), devilDllFile(QString("%1/%2/devil.dll").arg(avsPortableDir, archSuffix));
240 if (avsDllFile.exists() && devilDllFile.exists() && avsDllFile.isFile() && devilDllFile.isFile())
242 qWarning("Adding portable Avisynth to PATH environment variable: %s", MUTILS_UTF8(avsPortableDir));
243 basePath = avsPortableDir;
248 //Setup process object
249 MUtils::init_process(process, QDir::tempPath(), true, basePath.isEmpty() ? NULL : &(QStringList() << QString("%1/%2").arg(basePath, archSuffix)));
251 //Try to start VSPIPE.EXE
252 process.start(AVS_CHECK_BINARY(sysinfo, x64), QStringList());
253 if(!process.waitForStarted())
255 qWarning("Failed to launch AVS_CHECK.EXE -> %s", process.errorString().toUtf8().constData());
256 return false;
259 //Wait for process to finish
260 while(process.state() != QProcess::NotRunning)
262 if(process.waitForReadyRead(12000))
264 while(process.canReadLine())
266 output << QString::fromUtf8(process.readLine()).simplified();
268 continue;
270 if(process.state() != QProcess::NotRunning)
272 qWarning("AVS_CHECK.EXE process encountered a deadlock -> aborting now!");
273 break;
277 //Make sure VSPIPE.EXE has terminated!
278 process.waitForFinished(2500);
279 if(process.state() != QProcess::NotRunning)
281 qWarning("AVS_CHECK.EXE process still running, going to kill it!");
282 process.kill();
283 process.waitForFinished(-1);
286 //Read pending lines
287 while(process.canReadLine())
289 output << QString::fromUtf8(process.readLine()).simplified();
292 //Check exit code
293 if(process.exitCode() != 0)
295 qWarning("AVS_CHECK.EXE failed with code 0x%08X -> disable Avisynth support!", process.exitCode());
296 return false;
299 //Init regular expressions
300 QRegExp avsLogo("Avisynth\\s+Checker\\s+(x86|x64)");
301 QRegExp avsPath("Avisynth_DLLPath=(.+)");
302 QRegExp avsVers("Avisynth_Version=(\\d+)\\.(\\d+)");
304 //Check for version info
305 bool avisynthLogo = false;
306 quint32 avisynthVersion[2] = { 0, 0 };
307 QString avisynthPath;
308 for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
310 if(avisynthLogo)
312 if(avsPath.indexIn(*iter) >= 0)
314 avisynthPath = avsPath.cap(1).trimmed();
316 else if(avsVers.indexIn(*iter) >= 0)
318 quint32 temp[2];
319 if(MUtils::regexp_parse_uint32(avsVers, temp, 2))
321 avisynthVersion[0] = temp[0];
322 avisynthVersion[1] = temp[1];
326 else
328 if(avsLogo.lastIndexIn(*iter) >= 0)
330 avisynthLogo = true;
335 //Minimum required version found?
336 if((avisynthVersion[0] >= 2) && (avisynthVersion[1] >= 50) && (!avisynthPath.isEmpty()))
338 Wow64RedirectionDisabler disableWow64Redir;
339 path = new QFile(avisynthPath);
340 if(!path->open(QIODevice::ReadOnly))
342 MUTILS_DELETE(path);
344 qDebug("Avisynth was detected successfully (current version: %u.%02u).", avisynthVersion[0], avisynthVersion[1]);
345 qDebug("Avisynth DLL path: %s", MUTILS_UTF8(avisynthPath));
346 return true;
349 //Failed to determine version
350 qWarning("Failed to determine Avisynth version!");
351 return false;