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!");