OSDN Git Service

Updated project/solution files.
[lamexp/LameXP.git] / src / Thread_Initialization.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2017 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, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Thread_Initialization.h"
24
25 //Internal
26 #define LAMEXP_INC_TOOLS 1
27 #include "Tools.h"
28 #include "LockedFile.h"
29 #include "FileHash.h"
30 #include "Tool_Abstract.h"
31
32 //MUtils
33 #include <MUtils/Global.h>
34 #include <MUtils/OSSupport.h>
35 #include <MUtils/Translation.h>
36 #include <MUtils/Exception.h>
37
38 //Qt
39 #include <QFileInfo>
40 #include <QCoreApplication>
41 #include <QProcess>
42 #include <QMap>
43 #include <QDir>
44 #include <QResource>
45 #include <QTextStream>
46 #include <QRunnable>
47 #include <QThreadPool>
48 #include <QMutex>
49 #include <QQueue>
50 #include <QElapsedTimer>
51 #include <QVector>
52
53 /* enable custom tools? */
54 static const bool ENABLE_CUSTOM_TOOLS = true;
55
56 /* helper macros */
57 #define PRINT_CPU_TYPE(X) case X: qDebug("Selected CPU is: " #X)
58 #define MAKE_REGEXP(STR) (((STR) && ((STR)[0])) ? QRegExp((STR)) : QRegExp())
59
60 /* constants */
61 static const double g_allowedExtractDelay = 12.0;
62 static const size_t BUFF_SIZE = 512;
63
64 /* number of CPU cores -> number of threads */
65 static unsigned int cores2threads(const unsigned int cores)
66 {
67         static const size_t LUT_LEN = 4;
68         
69         static const struct
70         {
71                 const unsigned int upperBound;
72                 const double coeffs[4];
73         }
74         LUT[LUT_LEN] =
75         {
76                 {  4, { -0.052695810565,  0.158087431694, 4.982841530055, -1.088233151184 } },
77                 {  8, {  0.042431693989, -0.983442622951, 9.548961748634, -7.176393442623 } },
78                 { 12, { -0.006277322404,  0.185573770492, 0.196830601093, 17.762622950820 } },
79                 { 32, {  0.000673497268, -0.064655737705, 3.199584699454,  5.751606557377 } }
80         };
81
82         size_t index = 0;
83         while((cores > LUT[index].upperBound) && (index < (LUT_LEN-1))) index++;
84
85         const double x = qBound(1.0, double(cores), double(LUT[LUT_LEN-1].upperBound));
86         const double y = (LUT[index].coeffs[0] * pow(x, 3.0)) + (LUT[index].coeffs[1] * pow(x, 2.0)) + (LUT[index].coeffs[2] * x) + LUT[index].coeffs[3];
87
88         return qRound(abs(y));
89 }
90
91 /* create regular expression list*/
92 static QList<QRegExp> createRegExpList(const char *const *const regExpList)
93 {
94         QList<QRegExp> result;
95         for (size_t i = 0; regExpList[i]; ++i)
96         {
97                 result << MAKE_REGEXP(regExpList[i]);
98         }
99         return result;
100 }
101
102 ////////////////////////////////////////////////////////////
103 // BaseTask class
104 ////////////////////////////////////////////////////////////
105
106 class BaseTask : public QRunnable
107 {
108 public:
109         BaseTask(void)
110         {
111         }
112
113         ~BaseTask(void)
114         {
115         }
116
117         static void clearFlags(void)
118         {
119                 s_exception.fetchAndStoreOrdered(0);
120                 s_errMsg[0] = char(0);
121         }
122
123         static bool getExcept(void)
124         {
125                 return MUTILS_BOOLIFY(s_exception);
126         }
127
128         static bool getErrMsg(char *buffer, const size_t buffSize)
129         {
130                 if(s_errMsg[0])
131                 {
132                         strncpy_s(buffer, BUFF_SIZE, s_errMsg, _TRUNCATE);
133                         return true;
134                 }
135                 return false;
136         }
137
138 protected:
139         virtual void taskMain(void) = 0;
140
141         void run(void)
142         {
143                 try
144                 {
145                         if(!getExcept()) taskMain();
146                 }
147                 catch(const std::exception &e)
148                 {
149                         if(!s_exception.fetchAndAddOrdered(1))
150                         {
151                                 strncpy_s(s_errMsg, BUFF_SIZE, e.what(), _TRUNCATE);
152                         }
153                         qWarning("OptionalInitTask exception error:\n%s\n\n", e.what());
154                 }
155                 catch(...)
156                 {
157                         if (!s_exception.fetchAndAddOrdered(1))
158                         {
159                                 strncpy_s(s_errMsg, BUFF_SIZE, "Unknown exception error!", _TRUNCATE);
160                         }
161                         qWarning("OptionalInitTask encountered an unknown exception!");
162                 }
163         }
164
165         static QAtomicInt s_exception;
166         static char s_errMsg[BUFF_SIZE];
167 };
168
169 char BaseTask::s_errMsg[BUFF_SIZE] = {'\0'};
170 QAtomicInt BaseTask::s_exception(0);
171
172 ////////////////////////////////////////////////////////////
173 // ExtractorTask class
174 ////////////////////////////////////////////////////////////
175
176 class ExtractorTask : public BaseTask
177 {
178 public:
179         ExtractorTask(QResource *const toolResource, const QDir &appDir, const QString &toolName, const QByteArray &toolHash, const unsigned int toolVersion, const QString &toolTag)
180         :
181                 m_appDir(appDir),
182                 m_tempPath(MUtils::temp_folder()),
183                 m_toolName(toolName),
184                 m_toolHash(toolHash),
185                 m_toolVersion(toolVersion),
186                 m_toolTag(toolTag),
187                 m_toolResource(toolResource)
188         {
189                 /* Nothing to do */
190         }
191
192         ~ExtractorTask(void)
193         {
194         }
195
196         static bool getCustom(void)
197         {
198                 return MUTILS_BOOLIFY(s_custom);
199         }
200
201         static void clearFlags(void)
202         {
203                 BaseTask::clearFlags();
204                 s_custom.fetchAndStoreOrdered(0);
205         }
206
207 protected:
208         void taskMain(void)
209         {
210                 QScopedPointer<LockedFile> lockedFile;
211                 unsigned int version = m_toolVersion;
212
213                 const QFileInfo toolFileInfo(m_toolName);
214                 const QString   toolShrtName = QString("%1.%2").arg(toolFileInfo.baseName().toLower(), toolFileInfo.suffix().toLower());
215
216                 //Try to load a "custom" tool first
217                 if(ENABLE_CUSTOM_TOOLS)
218                 {
219                         const QFileInfo customTool(QString("%1/tools/%2/%3").arg(m_appDir.canonicalPath(), QString::number(lamexp_version_build()), toolShrtName));
220                         if(customTool.exists() && customTool.isFile())
221                         {
222                                 qDebug("Setting up file: %s <- %s", toolShrtName.toLatin1().constData(), m_appDir.relativeFilePath(customTool.canonicalFilePath()).toLatin1().constData());
223                                 try
224                                 {
225                                         lockedFile.reset(new LockedFile(customTool.canonicalFilePath()));
226                                         version = UINT_MAX; s_custom.ref();
227                                 }
228                                 catch(std::runtime_error&)
229                                 {
230                                         lockedFile.reset();
231                                 }
232                         }
233                 }
234
235                 //Try to load the tool from the "cache" next
236                 if(lockedFile.isNull())
237                 {
238                         const QFileInfo chachedTool(QString("%1/cache/%2").arg(m_appDir.canonicalPath(), toolFileInfo.fileName()));
239                         if(chachedTool.exists() && chachedTool.isFile())
240                         {
241                                 qDebug("Validating file: %s <- %s", toolShrtName.toLatin1().constData(), m_appDir.relativeFilePath(chachedTool.canonicalFilePath()).toLatin1().constData());
242                                 try
243                                 {
244                                         lockedFile.reset(new LockedFile(chachedTool.canonicalFilePath(), m_toolHash));
245                                 }
246                                 catch(std::runtime_error&)
247                                 {
248                                         lockedFile.reset();
249                                 }
250                         }
251                 }
252
253                 //If still not initialized, extract tool now!
254                 if(lockedFile.isNull())
255                 {
256                         qDebug("Extracting file: %s -> %s", m_toolName.toLatin1().constData(), toolShrtName.toLatin1().constData());
257                         lockedFile.reset(new LockedFile(m_toolResource.data(), QString("%1/lxp_%2").arg(m_tempPath, toolShrtName), m_toolHash));
258                 }
259
260                 //Register tool
261                 lamexp_tools_register(toolShrtName, lockedFile.take(), version, m_toolTag);
262         }
263
264 private:
265         static QAtomicInt         s_custom;
266         QScopedPointer<QResource> m_toolResource;
267         const QDir                m_appDir;
268         const QString             m_tempPath;
269         const QString             m_toolName;
270         const QByteArray          m_toolHash;
271         const unsigned int        m_toolVersion;
272         const QString             m_toolTag;
273 };
274
275 QAtomicInt ExtractorTask::s_custom = false;
276
277 ////////////////////////////////////////////////////////////
278 // InitAacEncTask class
279 ////////////////////////////////////////////////////////////
280
281 class InitAacEncTask : public BaseTask
282 {
283 public:
284         InitAacEncTask(const aac_encoder_t *const encoder_info)
285         :
286                 m_encoder_info(encoder_info)
287         {
288         }
289
290         ~InitAacEncTask(void)
291         {
292         }
293
294 protected:
295         void taskMain(void)
296         {
297                 initAacEncImpl
298                 (
299                         m_encoder_info->toolName,
300                         m_encoder_info->fileNames,
301                         m_encoder_info->checkArgs ? (QStringList() << QString::fromLatin1(m_encoder_info->checkArgs)) : QStringList(),
302                         m_encoder_info->toolMinVersion,
303                         m_encoder_info->verDigits,
304                         m_encoder_info->verShift,
305                         m_encoder_info->verStr,
306                         MAKE_REGEXP(m_encoder_info->regExpVer),
307                         MAKE_REGEXP(m_encoder_info->regExpSig),
308                         m_encoder_info->regExpLib[0] ? createRegExpList(m_encoder_info->regExpLib) : QList<QRegExp>()
309                 );
310         }
311
312         static void initAacEncImpl(const char *const toolName, const char *const fileNames[], const QStringList &checkArgs, const quint32 &toolMinVersion, const quint32 &verDigits, const quint32 &verShift, const char *const verStr, QRegExp &regExpVer, QRegExp &regExpSig = QRegExp(), const QList<QRegExp> &regExpLib = QList<QRegExp>());
313
314 private:
315         const aac_encoder_t *const m_encoder_info;
316 };
317
318 ////////////////////////////////////////////////////////////
319 // Constructor
320 ////////////////////////////////////////////////////////////
321
322 InitializationThread::InitializationThread(const MUtils::CPUFetaures::cpu_info_t &cpuFeatures)
323 :
324         m_bSuccess(false),
325         m_slowIndicator(false)
326 {
327
328         memcpy(&m_cpuFeatures, &cpuFeatures, sizeof(MUtils::CPUFetaures::cpu_info_t));
329 }
330
331 ////////////////////////////////////////////////////////////
332 // Thread Main
333 ////////////////////////////////////////////////////////////
334
335 void InitializationThread::run(void)
336 {
337         try
338         {
339                 doInit();
340         }
341         catch(const std::exception &error)
342         {
343                 MUTILS_PRINT_ERROR("\nGURU MEDITATION !!!\n\nException error:\n%s\n", error.what());
344                 MUtils::OS::fatal_exit(L"Unhandeled C++ exception error, application will exit!");
345         }
346         catch(...)
347         {
348                 MUTILS_PRINT_ERROR("\nGURU MEDITATION !!!\n\nUnknown exception error!\n");
349                 MUtils::OS::fatal_exit(L"Unhandeled C++ exception error, application will exit!");
350         }
351 }
352
353 double InitializationThread::doInit(const size_t threadCount)
354 {
355         m_bSuccess = false;
356         delay();
357
358         //CPU type selection
359         unsigned int cpuSupport = 0;
360         const bool haveSSE2 = (m_cpuFeatures.features & MUtils::CPUFetaures::FLAG_SSE) && (m_cpuFeatures.features & MUtils::CPUFetaures::FLAG_SSE2);
361         if(haveSSE2 && (m_cpuFeatures.vendor & MUtils::CPUFetaures::VENDOR_INTEL))
362         {
363                 if (m_cpuFeatures.features & MUtils::CPUFetaures::FLAG_AVX)
364                 {
365                         cpuSupport = m_cpuFeatures.x64 ? CPU_TYPE_X64_AVX : CPU_TYPE_X86_AVX;
366                 }
367                 else
368                 {
369                         cpuSupport = m_cpuFeatures.x64 ? CPU_TYPE_X64_SSE : CPU_TYPE_X86_SSE;
370                 }
371         }
372         else
373         {
374                 cpuSupport = m_cpuFeatures.x64 ? CPU_TYPE_X64_GEN : CPU_TYPE_X86_GEN;
375         }
376
377         //Hack to disable x64 on Wine, as x64 binaries won't run under Wine (tested with Wine 1.4 under Ubuntu 12.04 x64)
378         if(cpuSupport & CPU_TYPE_X64_ALL)
379         {
380                 if(MUtils::OS::running_on_wine())
381                 {
382                         qWarning("Running under Wine on a 64-Bit system. Going to disable all x64 support!\n");
383                         cpuSupport = (cpuSupport == CPU_TYPE_X64_SSE) ? CPU_TYPE_X86_SSE : CPU_TYPE_X86_GEN;
384                 }
385         }
386
387         //Print selected CPU type
388         switch(cpuSupport)
389         {
390                 PRINT_CPU_TYPE(CPU_TYPE_X86_GEN); break;
391                 PRINT_CPU_TYPE(CPU_TYPE_X86_SSE); break;
392                 PRINT_CPU_TYPE(CPU_TYPE_X86_AVX); break;
393                 PRINT_CPU_TYPE(CPU_TYPE_X64_GEN); break;
394                 PRINT_CPU_TYPE(CPU_TYPE_X64_SSE); break;
395                 PRINT_CPU_TYPE(CPU_TYPE_X64_AVX); break;
396                 default: MUTILS_THROW("CPU support undefined!");
397         }
398
399         //Allocate queues
400         QQueue<QString> queueToolName;
401         QQueue<QString> queueChecksum;
402         QQueue<QString> queueVersInfo;
403         QQueue<unsigned int> queueVersions;
404         QQueue<unsigned int> queueCpuTypes;
405
406         //Init properties
407         for(int i = 0; true; i++)
408         {
409                 if(!(g_lamexp_tools[i].pcName || g_lamexp_tools[i].pcHash  || g_lamexp_tools[i].uiVersion))
410                 {
411                         break;
412                 }
413                 else if(g_lamexp_tools[i].pcName && g_lamexp_tools[i].pcHash && g_lamexp_tools[i].uiVersion)
414                 {
415                         queueToolName.enqueue(QString::fromLatin1(g_lamexp_tools[i].pcName));
416                         queueChecksum.enqueue(QString::fromLatin1(g_lamexp_tools[i].pcHash));
417                         queueVersInfo.enqueue(QString::fromLatin1(g_lamexp_tools[i].pcVersTag));
418                         queueCpuTypes.enqueue(g_lamexp_tools[i].uiCpuType);
419                         queueVersions.enqueue(g_lamexp_tools[i].uiVersion);
420                 }
421                 else
422                 {
423                         qFatal("Inconsistent checksum data detected. Take care!");
424                 }
425         }
426
427         QDir appDir = QDir(QCoreApplication::applicationDirPath()).canonicalPath();
428
429         QScopedPointer<QThreadPool> pool(new QThreadPool());
430         pool->setMaxThreadCount((threadCount > 0) ? threadCount : qBound(2U, cores2threads(m_cpuFeatures.count), 16U));
431         ExtractorTask::clearFlags();
432
433         //Start the timer
434         QElapsedTimer timeExtractStart;
435         timeExtractStart.start();
436         
437         //Extract all files
438         while(!(queueToolName.isEmpty() || queueChecksum.isEmpty() || queueVersInfo.isEmpty() || queueCpuTypes.isEmpty() || queueVersions.isEmpty()))
439         {
440                 const QString toolName = queueToolName.dequeue();
441                 const QString checksum = queueChecksum.dequeue();
442                 const QString versInfo = queueVersInfo.dequeue();
443                 const unsigned int cpuType = queueCpuTypes.dequeue();
444                 const unsigned int version = queueVersions.dequeue();
445                         
446                 const QByteArray toolHash(checksum.toLatin1());
447                 if(toolHash.size() != 96)
448                 {
449                         qFatal("The checksum for \"%s\" has an invalid size!", MUTILS_UTF8(toolName));
450                         return -1.0;
451                 }
452                         
453                 QScopedPointer<QResource> resource(new QResource(QString(":/tools/%1").arg(toolName)));
454                 if(!(resource->isValid() && resource->data()))
455                 {
456                         qFatal("The resource for \"%s\" could not be found!", MUTILS_UTF8(toolName));
457                         return -1.0;
458                 }
459                         
460                 if(cpuType & cpuSupport)
461                 {
462                         pool->start(new ExtractorTask(resource.take(), appDir, toolName, toolHash, version, versInfo));
463                         continue;
464                 }
465         }
466
467         //Sanity Check
468         if(!(queueToolName.isEmpty() && queueChecksum.isEmpty() && queueVersInfo.isEmpty() && queueCpuTypes.isEmpty() && queueVersions.isEmpty()))
469         {
470                 qFatal("Checksum queues *not* empty fater verification completed. Take care!");
471         }
472
473         //Wait for extrator threads to finish
474         pool->waitForDone();
475
476         //Performance measure
477         const double delayExtract = double(timeExtractStart.elapsed()) / 1000.0;
478         timeExtractStart.invalidate();
479
480         //Make sure all files were extracted correctly
481         if(ExtractorTask::getExcept())
482         {
483                 char errorMsg[BUFF_SIZE];
484                 if(ExtractorTask::getErrMsg(errorMsg, BUFF_SIZE))
485                 {
486                         qFatal("At least one of the required tools could not be initialized:\n%s", errorMsg);
487                         return -1.0;
488                 }
489                 qFatal("At least one of the required tools could not be initialized!");
490                 return -1.0;
491         }
492
493         qDebug("All extracted.\n");
494
495         //Using any custom tools?
496         if(ExtractorTask::getCustom())
497         {
498                 qWarning("Warning: Using custom tools, you might encounter unexpected problems!\n");
499         }
500
501         //Check delay
502         if(delayExtract > g_allowedExtractDelay)
503         {
504                 m_slowIndicator = true;
505                 qWarning("Extracting tools took %.3f seconds -> probably slow realtime virus scanner.", delayExtract);
506                 qWarning("Please report performance problems to your anti-virus developer !!!\n");
507         }
508         else
509         {
510                 qDebug("Extracting the tools took %.3f seconds (OK).\n", delayExtract);
511         }
512
513         //Register all translations
514         initTranslations();
515
516         //Look for AAC encoders
517         InitAacEncTask::clearFlags();
518         for(size_t i = 0; g_lamexp_aacenc[i].toolName; i++)
519         {
520                 pool->start(new InitAacEncTask(&(g_lamexp_aacenc[i])));
521         }
522         pool->waitForDone();
523
524         //Make sure initialization finished correctly
525         if(InitAacEncTask::getExcept())
526         {
527                 char errorMsg[BUFF_SIZE];
528                 if(InitAacEncTask::getErrMsg(errorMsg, BUFF_SIZE))
529                 {
530                         qFatal("At least one optional component failed to initialize:\n%s", errorMsg);
531                         return -1.0;
532                 }
533                 qFatal("At least one optional component failed to initialize!");
534                 return -1.0;
535         }
536
537         m_bSuccess = true;
538         delay();
539
540         return delayExtract;
541 }
542
543 ////////////////////////////////////////////////////////////
544 // INTERNAL FUNCTIONS
545 ////////////////////////////////////////////////////////////
546
547 void InitializationThread::delay(void)
548 {
549         MUtils::OS::sleep_ms(333);
550 }
551
552 ////////////////////////////////////////////////////////////
553 // Translation Support
554 ////////////////////////////////////////////////////////////
555
556 void InitializationThread::initTranslations(void)
557 {
558         //Search for language files
559         const QDir qmDirectory(":/localization");
560         const QStringList qmFiles = qmDirectory.entryList(QStringList() << "LameXP_??.qm", QDir::Files, QDir::Name);
561
562         //Make sure we found at least one translation
563         if(qmFiles.count() < 1)
564         {
565                 qFatal("Could not find any translation files!");
566                 return;
567         }
568
569         //Initialize variables
570         const QString langResTemplate(":/localization/%1.txt");
571         QRegExp langIdExp("^LameXP_(\\w\\w)\\.qm$", Qt::CaseInsensitive);
572
573         //Add all available translations
574         for(QStringList::ConstIterator iter = qmFiles.constBegin(); iter != qmFiles.constEnd(); iter++)
575         {
576                 const QString langFile = qmDirectory.absoluteFilePath(*iter);
577                 QString langId, langName;
578                 unsigned int systemId = 0, country = 0;
579                 
580                 if(QFileInfo(langFile).isFile() && (langIdExp.indexIn(*iter) >= 0))
581                 {
582                         langId = langIdExp.cap(1).toLower();
583                         QScopedPointer<QResource> langRes(new QResource(langResTemplate.arg(*iter)));
584                         if(langRes->isValid() && langRes->size() > 0)
585                         {
586                                 QByteArray data = QByteArray::fromRawData(reinterpret_cast<const char*>(langRes->data()), langRes->size());
587                                 QTextStream stream(&data, QIODevice::ReadOnly);
588                                 stream.setAutoDetectUnicode(false); stream.setCodec("UTF-8");
589
590                                 while(!(stream.atEnd() || (stream.status() != QTextStream::Ok)))
591                                 {
592                                         QStringList langInfo = stream.readLine().simplified().split(",", QString::SkipEmptyParts);
593                                         if(langInfo.count() >= 3)
594                                         {
595                                                 systemId = langInfo.at(0).trimmed().toUInt();
596                                                 country  = langInfo.at(1).trimmed().toUInt();
597                                                 langName = langInfo.at(2).trimmed();
598                                                 break;
599                                         }
600                                 }
601                         }
602                 }
603
604                 if(!(langId.isEmpty() || langName.isEmpty() || (systemId == 0)))
605                 {
606                         if(MUtils::Translation::insert(langId, langFile, langName, systemId, country))
607                         {
608                                 qDebug("Registering translation: %s = %s (%u) [%u]", MUTILS_UTF8(*iter), MUTILS_UTF8(langName), systemId, country);
609                         }
610                         else
611                         {
612                                 qWarning("Failed to register: %s", langFile.toLatin1().constData());
613                         }
614                 }
615         }
616
617         qDebug("All registered.\n");
618 }
619
620 ////////////////////////////////////////////////////////////
621 // AAC Encoder Detection
622 ////////////////////////////////////////////////////////////
623
624 void InitAacEncTask::initAacEncImpl(const char *const toolName, const char *const fileNames[], const QStringList &checkArgs, const quint32 &toolMinVersion, const quint32 &verDigits, const quint32 &verShift, const char *const verStr, QRegExp &regExpVer, QRegExp &regExpSig, const QList<QRegExp> &regExpLib)
625 {
626         static const size_t MAX_FILES = 8;
627         const QString appPath = QDir(QCoreApplication::applicationDirPath()).canonicalPath();
628         
629         QFileInfoList fileInfo;
630         for(size_t i = 0; fileNames[i] && (fileInfo.count() < MAX_FILES); i++)
631         {
632                 fileInfo.append(QFileInfo(QString("%1/%2").arg(appPath, QString::fromLatin1(fileNames[i]))));
633         }
634         
635         for(QFileInfoList::ConstIterator iter = fileInfo.constBegin(); iter != fileInfo.constEnd(); iter++)
636         {
637                 if(!(iter->exists() && iter->isFile()))
638                 {
639                         qDebug("%s encoder binaries not found -> Encoding support will be disabled!\n", toolName);
640                         return;
641                 }
642                 if((iter->suffix().compare("exe", Qt::CaseInsensitive) == 0) && (!MUtils::OS::is_executable_file(iter->canonicalFilePath())))
643                 {
644                         qDebug("%s executable is invalid -> %s support will be disabled!\n", MUTILS_UTF8(iter->fileName()), toolName);
645                         return;
646                 }
647         }
648
649         qDebug("Found %s encoder binary:\n%s\n", toolName, MUTILS_UTF8(fileInfo.first().canonicalFilePath()));
650
651         //Lock the encoder binaries
652         QScopedPointer<LockedFile> binaries[MAX_FILES];
653         try
654         {
655                 size_t index = 0;
656                 for(QFileInfoList::ConstIterator iter = fileInfo.constBegin(); iter != fileInfo.constEnd(); iter++)
657                 {
658                         binaries[index++].reset(new LockedFile(iter->canonicalFilePath()));
659                 }
660         }
661         catch(...)
662         {
663                 qWarning("Failed to get excluive lock to encoder binary -> %s support will be disabled!", toolName);
664                 return;
665         }
666
667         QProcess process;
668         MUtils::init_process(process, fileInfo.first().absolutePath());
669         process.start(fileInfo.first().canonicalFilePath(), checkArgs);
670
671         if(!process.waitForStarted())
672         {
673                 qWarning("%s process failed to create!", toolName);
674                 qWarning("Error message: \"%s\"\n", process.errorString().toLatin1().constData());
675                 process.kill();
676                 process.waitForFinished(-1);
677                 return;
678         }
679
680         quint32 toolVersion = quint32(-1);
681         bool sigFound = regExpSig.isEmpty() ? true : false;
682         QVector<bool> extraLib(regExpLib.count(), false);
683
684         while(process.state() != QProcess::NotRunning)
685         {
686                 if(!process.waitForReadyRead())
687                 {
688                         if(process.state() == QProcess::Running)
689                         {
690                                 qWarning("%s process time out -> killing!", toolName);
691                                 process.kill();
692                                 process.waitForFinished(-1);
693                                 return;
694                         }
695                 }
696                 while(process.canReadLine())
697                 {
698                         const QString line = QString::fromUtf8(process.readLine().constData()).simplified();
699                         if (!sigFound)
700                         {
701                                 sigFound = (regExpSig.lastIndexIn(line) >= 0);
702                         }
703                         if (sigFound)
704                         {
705                                 if (regExpVer.lastIndexIn(line) >= 0)
706                                 {
707                                         quint32 tmp[8];
708                                         if (MUtils::regexp_parse_uint32(regExpVer, tmp, qMin(verDigits, 8U)))
709                                         {
710                                                 toolVersion = 0;
711                                                 for (quint32 i = 0; i < verDigits; i++)
712                                                 {
713                                                         toolVersion = (verShift > 0) ? ((toolVersion * verShift) + qBound(0U, tmp[i], (verShift - 1))) : tmp[i];
714                                                 }
715                                         }
716                                 }
717                                 for (int i = 0; i < regExpLib.count(); ++i)
718                                 {
719                                         extraLib[i] = extraLib[i] || (regExpLib[i].lastIndexIn(line) >= 0);
720                                 }
721                         }
722                 }
723         }
724
725         if((toolVersion == 0) || (toolVersion == quint32(-1)))
726         {
727                 qWarning("%s version could not be determined -> Encoding support will be disabled!", toolName);
728                 return;
729         }
730
731         if(toolVersion < toolMinVersion)
732         {
733                 qWarning("%s version is too much outdated (%s) -> Encoding support will be disabled!", toolName, MUTILS_UTF8(lamexp_version2string(verStr, toolVersion,    "N/A")));
734                 qWarning("Minimum required %s version currently is: %s\n",                             toolName, MUTILS_UTF8(lamexp_version2string(verStr, toolMinVersion, "N/A")));
735                 return;
736         }
737
738         for (int i = 0; i < extraLib.count(); ++i)
739         {
740                 if (!extraLib[i])
741                 {
742                         qWarning("%s lacks companion library (%s) -> Encoding support will be disabled!\n", toolName, MUTILS_UTF8(regExpLib[i].pattern()));
743                         return;
744                 }
745         }
746         
747         qDebug("Enabled %s encoder %s.\n", toolName, MUTILS_UTF8(lamexp_version2string(verStr, toolVersion, "N/A")));
748
749         size_t index = 0;
750         for(QFileInfoList::ConstIterator iter = fileInfo.constBegin(); iter != fileInfo.constEnd(); iter++)
751         {
752                 lamexp_tools_register(iter->fileName(), binaries[index++].take(), toolVersion);
753         }
754 }
755
756 ////////////////////////////////////////////////////////////
757 // Self-Test Function
758 ////////////////////////////////////////////////////////////
759
760 void InitializationThread::selfTest(void)
761 {
762         const unsigned int cpu[7] = {CPU_TYPE_X86_GEN, CPU_TYPE_X86_SSE, CPU_TYPE_X86_AVX, CPU_TYPE_X64_GEN, CPU_TYPE_X64_SSE, CPU_TYPE_X64_AVX, 0 };
763
764         unsigned int expectedCount = UINT_MAX;
765         for(size_t k = 0; cpu[k]; k++)
766         {
767                 qDebug("[TEST]");
768                 switch(cpu[k])
769                 {
770                         PRINT_CPU_TYPE(CPU_TYPE_X86_GEN); break;
771                         PRINT_CPU_TYPE(CPU_TYPE_X86_SSE); break;
772                         PRINT_CPU_TYPE(CPU_TYPE_X86_AVX); break;
773                         PRINT_CPU_TYPE(CPU_TYPE_X64_GEN); break;
774                         PRINT_CPU_TYPE(CPU_TYPE_X64_SSE); break;
775                         PRINT_CPU_TYPE(CPU_TYPE_X64_AVX); break;
776                 default:
777                         MUTILS_THROW("CPU support undefined!");
778                 }
779                 unsigned int n = 0;
780                 for(int i = 0; true; i++)
781                 {
782                         if(!(g_lamexp_tools[i].pcName || g_lamexp_tools[i].pcHash  || g_lamexp_tools[i].uiVersion))
783                         {
784                                 break;
785                         }
786                         else if(g_lamexp_tools[i].pcName && g_lamexp_tools[i].pcHash && g_lamexp_tools[i].uiVersion)
787                         {
788                                 const QString toolName = QString::fromLatin1(g_lamexp_tools[i].pcName);
789                                 const QByteArray expectedHash = QByteArray(g_lamexp_tools[i].pcHash);
790                                 if(g_lamexp_tools[i].uiCpuType & cpu[k])
791                                 {
792                                         qDebug("%02i -> %s", ++n, MUTILS_UTF8(toolName));
793                                         QFile resource(QString(":/tools/%1").arg(toolName));
794                                         if(!resource.open(QIODevice::ReadOnly))
795                                         {
796                                                 qFatal("The resource for \"%s\" could not be opened!", MUTILS_UTF8(toolName));
797                                                 break;
798                                         }
799                                         QByteArray hash = FileHash::computeHash(resource);
800                                         if(hash.isNull() || _stricmp(hash.constData(), expectedHash.constData()))
801                                         {
802                                                 qFatal("Hash check for tool \"%s\" has failed!", MUTILS_UTF8(toolName));
803                                                 break;
804                                         }
805                                         resource.close();
806                                 }
807                         }
808                         else
809                         {
810                                 qFatal("Inconsistent checksum data detected. Take care!");
811                         }
812                 }
813                 if (expectedCount != UINT_MAX)
814                 {
815                         if (n != expectedCount)
816                         {
817                                 qFatal("Tool count mismatch for CPU type %u. Should be %u, but got %u !!!", cpu[k], expectedCount, n);
818                         }
819                 }
820                 else
821                 {
822                         expectedCount = n; /*remember count*/
823                 }
824                 qDebug("Done.\n");
825         }
826 }
827
828 ////////////////////////////////////////////////////////////
829 // EVENTS
830 ////////////////////////////////////////////////////////////
831
832 /*NONE*/