1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2018 LoRd_MuldeR <MuldeR2@GMX.de>
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.
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"
28 #include <QMutexLocker>
29 #include <QApplication>
35 #include "model_sysinfo.h"
38 #include <MUtils/Global.h>
39 #include <MUtils/OSSupport.h>
42 static const bool ENABLE_PORTABLE_AVS = true;
45 QMutex AvisynthCheckThread::m_avsLock;
46 QScopedPointer<QFile> AvisynthCheckThread::m_avsDllPath[2];
49 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
50 #define BOOLIFY(X) ((X) ? '1' : '0')
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
61 Wow64RedirectionDisabler(void)
64 m_disabled = MUtils::OS::wow64fsredir_disable(m_oldValue);
66 ~Wow64RedirectionDisabler(void)
70 if(!MUtils::OS::wow64fsredir_revert(m_oldValue))
72 qWarning("Failed to renable WOW64 filesystem redirection!");
81 //-------------------------------------
83 //-------------------------------------
85 bool AvisynthCheckThread::detect(SysinfoModel *sysinfo)
87 sysinfo->clearAvisynth();
89 QMutexLocker lock(&m_avsLock);
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()));
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!");
116 if(thread.getException())
118 qWarning("Avisynth thread encountered an exception !!!");
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)));
131 qWarning("Avisynth could not be found -> Avisynth support disabled!");
137 //-------------------------------------
139 //-------------------------------------
141 AvisynthCheckThread::AvisynthCheckThread(const SysinfoModel *const sysinfo)
149 AvisynthCheckThread::~AvisynthCheckThread(void)
153 void AvisynthCheckThread::run(void)
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)
166 detectAvisynthVersion2(success, basePath, sysinfo, exception);
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);
184 qWarning("Avisynth initializdation raised an C++ exception!");
188 void AvisynthCheckThread::detectAvisynthVersion3(int &success, QString &basePath, const SysinfoModel *const sysinfo)
193 if(checkAvisynth(basePath, sysinfo, avsPath32, false))
195 m_avsDllPath[0].reset(avsPath32);
196 success |= AVISYNTH_X86;
197 qDebug("Avisynth 32-Bit edition found!");
201 qDebug("Avisynth 32-Bit edition *not* found!");
204 if(sysinfo->getCPUFeatures(SysinfoModel::CPUFeatures_X64))
207 if(checkAvisynth(basePath, sysinfo, avsPath64, true))
209 m_avsDllPath[1].reset(avsPath64);
210 success |= AVISYNTH_X64;
211 qDebug("Avisynth 64-Bit edition found!");
215 qDebug("Avisynth 64-Bit edition *not* found!");
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");
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());
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();
270 if(process.state() != QProcess::NotRunning)
272 qWarning("AVS_CHECK.EXE process encountered a deadlock -> aborting now!");
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!");
283 process.waitForFinished(-1);
287 while(process.canReadLine())
289 output << QString::fromUtf8(process.readLine()).simplified();
293 if(process.exitCode() != 0)
295 qWarning("AVS_CHECK.EXE failed with code 0x%08X -> disable Avisynth support!", process.exitCode());
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++)
312 if(avsPath.indexIn(*iter) >= 0)
314 avisynthPath = avsPath.cap(1).trimmed();
316 else if(avsVers.indexIn(*iter) >= 0)
319 if(MUtils::regexp_parse_uint32(avsVers, temp, 2))
321 avisynthVersion[0] = temp[0];
322 avisynthVersion[1] = temp[1];
328 if(avsLogo.lastIndexIn(*iter) >= 0)
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))
344 qDebug("Avisynth was detected successfully (current version: %u.%02u).", avisynthVersion[0], avisynthVersion[1]);
345 qDebug("Avisynth DLL path: %s", MUTILS_UTF8(avisynthPath));
349 //Failed to determine version
350 qWarning("Failed to determine Avisynth version!");