OSDN Git Service

Added yet another configuration to build LameXP with *static* Qt5 libraries. Still...
[lamexp/LameXP.git] / src / Global.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 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 "Global.h"
23
24 //Qt includes
25 #include <QApplication>
26 #include <QMessageBox>
27 #include <QDir>
28 #include <QUuid>
29 #include <QMap>
30 #include <QDate>
31 #include <QIcon>
32 #include <QPlastiqueStyle>
33 #include <QImageReader>
34 #include <QSharedMemory>
35 #include <QSysInfo>
36 #include <QStringList>
37 #include <QSystemSemaphore>
38 #include <QMutex>
39 #include <QTextCodec>
40 #include <QLibrary>
41 #include <QRegExp>
42 #include <QResource>
43 #include <QTranslator>
44 #include <QEventLoop>
45 #include <QTimer>
46 #include <QLibraryInfo>
47 #include <QEvent>
48
49 //LameXP includes
50 #include "Resource.h"
51 #include "LockedFile.h"
52
53 //CRT includes
54 #include <iostream>
55 #include <fstream>
56 #include <io.h>
57 #include <fcntl.h>
58 #include <intrin.h>
59 #include <math.h>
60 #include <time.h>
61 #include <process.h>
62
63 //COM includes
64 #include <Objbase.h>
65 #include <PowrProf.h>
66
67 //Debug only includes
68 #if LAMEXP_DEBUG
69 #include <Psapi.h>
70 #endif
71
72 //Initialize static Qt plugins
73 #ifdef QT_NODLL
74 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
75 Q_IMPORT_PLUGIN(qico)
76 Q_IMPORT_PLUGIN(qsvg)
77 #else
78 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
79 Q_IMPORT_PLUGIN(QICOPlugin)
80 #endif
81 #endif
82
83 ///////////////////////////////////////////////////////////////////////////////
84 // TYPES
85 ///////////////////////////////////////////////////////////////////////////////
86
87 static const size_t g_lamexp_ipc_slots = 128;
88
89 typedef struct
90 {
91         unsigned int command;
92         unsigned int reserved_1;
93         unsigned int reserved_2;
94         char parameter[4096];
95 }
96 lamexp_ipc_data_t;
97
98 typedef struct
99 {
100         unsigned int pos_write;
101         unsigned int pos_read;
102         lamexp_ipc_data_t data[g_lamexp_ipc_slots];
103 }
104 lamexp_ipc_t;
105
106 ///////////////////////////////////////////////////////////////////////////////
107 // GLOBAL VARS
108 ///////////////////////////////////////////////////////////////////////////////
109
110 //Build version
111 static const struct
112 {
113         unsigned int ver_major;
114         unsigned int ver_minor;
115         unsigned int ver_build;
116         char *ver_release_name;
117 }
118 g_lamexp_version =
119 {
120         VER_LAMEXP_MAJOR,
121         VER_LAMEXP_MINOR,
122         VER_LAMEXP_BUILD,
123         VER_LAMEXP_RNAME
124 };
125
126 //Build date
127 static QDate g_lamexp_version_date;
128 static const char *g_lamexp_months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
129 static const char *g_lamexp_version_raw_date = __DATE__;
130 static const char *g_lamexp_version_raw_time = __TIME__;
131
132 //Console attached flag
133 static bool g_lamexp_console_attached = false;
134
135 //Compiler detection
136 //The following code was borrowed from MPC-HC project: http://mpc-hc.sf.net/
137 #if defined(__INTEL_COMPILER)
138         #if (__INTEL_COMPILER >= 1200)
139                 static const char *g_lamexp_version_compiler = "ICL 12.x";
140         #elif (__INTEL_COMPILER >= 1100)
141                 static const char *g_lamexp_version_compiler = "ICL 11.x";
142         #elif (__INTEL_COMPILER >= 1000)
143                 static const char *g_lamexp_version_compiler = "ICL 10.x";
144         #else
145                 #error Compiler is not supported!
146         #endif
147 #elif defined(_MSC_VER)
148         #if (_MSC_VER == 1600)
149                 #if (_MSC_FULL_VER >= 160040219)
150                         static const char *g_lamexp_version_compiler = "MSVC 2010-SP1";
151                 #else
152                         static const char *g_lamexp_version_compiler = "MSVC 2010";
153                 #endif
154         #elif (_MSC_VER == 1500)
155                 #if (_MSC_FULL_VER >= 150030729)
156                         static const char *g_lamexp_version_compiler = "MSVC 2008-SP1";
157                 #else
158                         static const char *g_lamexp_version_compiler = "MSVC 2008";
159                 #endif
160         #else
161                 #error Compiler is not supported!
162         #endif
163
164         // Note: /arch:SSE and /arch:SSE2 are only available for the x86 platform
165         #if !defined(_M_X64) && defined(_M_IX86_FP)
166                 #if (_M_IX86_FP == 1)
167                         LAMEXP_COMPILER_WARNING("SSE instruction set is enabled!")
168                 #elif (_M_IX86_FP == 2)
169                         LAMEXP_COMPILER_WARNING("SSE2 instruction set is enabled!")
170                 #endif
171         #endif
172 #else
173         #error Compiler is not supported!
174 #endif
175
176 //Architecture detection
177 #if defined(_M_X64)
178         static const char *g_lamexp_version_arch = "x64";
179 #elif defined(_M_IX86)
180         static const char *g_lamexp_version_arch = "x86";
181 #else
182         #error Architecture is not supported!
183 #endif
184
185 //Official web-site URL
186 static const char *g_lamexp_website_url = "http://lamexp.sourceforge.net/";
187 static const char *g_lamexp_support_url = "http://forum.doom9.org/showthread.php?t=157726";
188
189 //Tool versions (expected versions!)
190 static const unsigned int g_lamexp_toolver_neroaac = VER_LAMEXP_TOOL_NEROAAC;
191 static const unsigned int g_lamexp_toolver_fhgaacenc = VER_LAMEXP_TOOL_FHGAACENC;
192 static const unsigned int g_lamexp_toolver_qaacenc = VER_LAMEXP_TOOL_QAAC;
193 static const unsigned int g_lamexp_toolver_coreaudio = VER_LAMEXP_TOOL_COREAUDIO;
194
195 //Special folders
196 static QString g_lamexp_temp_folder;
197
198 //Tools
199 static QMap<QString, LockedFile*> g_lamexp_tool_registry;
200 static QMap<QString, unsigned int> g_lamexp_tool_versions;
201
202 //Languages
203 static struct
204 {
205         QMap<QString, QString> files;
206         QMap<QString, QString> names;
207         QMap<QString, unsigned int> sysid;
208         QMap<QString, unsigned int> cntry;
209 }
210 g_lamexp_translation;
211
212 //Translator
213 static QTranslator *g_lamexp_currentTranslator = NULL;
214
215 //Shared memory
216 static const struct
217 {
218         char *sharedmem;
219         char *semaphore_read;
220         char *semaphore_read_mutex;
221         char *semaphore_write;
222         char *semaphore_write_mutex;
223 }
224 g_lamexp_ipc_uuid =
225 {
226         "{21A68A42-6923-43bb-9CF6-64BF151942EE}",
227         "{7A605549-F58C-4d78-B4E5-06EFC34F405B}",
228         "{60AA8D04-F6B8-497d-81EB-0F600F4A65B5}",
229         "{726061D5-1615-4B82-871C-75FD93458E46}",
230         "{1A616023-AA6A-4519-8AF3-F7736E899977}"
231 };
232 static struct
233 {
234         QSharedMemory *sharedmem;
235         QSystemSemaphore *semaphore_read;
236         QSystemSemaphore *semaphore_read_mutex;
237         QSystemSemaphore *semaphore_write;
238         QSystemSemaphore *semaphore_write_mutex;
239 }
240 g_lamexp_ipc_ptr =
241 {
242         NULL, NULL, NULL
243 };
244
245 //Image formats
246 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
247 static const char *g_lamexp_imageformats[] = {"png", "jpg", "gif", "ico", "svg", NULL};
248 #else
249 static const char *g_lamexp_imageformats[] = {"png", "jpg", "gif", "ico", NULL};
250 #endif
251
252 //Global locks
253 static QMutex g_lamexp_message_mutex;
254
255 //Main thread ID
256 static const DWORD g_main_thread_id = GetCurrentThreadId();
257
258 //Log file
259 static FILE *g_lamexp_log_file = NULL;
260
261 ///////////////////////////////////////////////////////////////////////////////
262 // GLOBAL FUNCTIONS
263 ///////////////////////////////////////////////////////////////////////////////
264
265 /*
266  * Version getters
267  */
268 unsigned int lamexp_version_major(void) { return g_lamexp_version.ver_major; }
269 unsigned int lamexp_version_minor(void) { return g_lamexp_version.ver_minor; }
270 unsigned int lamexp_version_build(void) { return g_lamexp_version.ver_build; }
271 const char *lamexp_version_release(void) { return g_lamexp_version.ver_release_name; }
272 const char *lamexp_version_time(void) { return g_lamexp_version_raw_time; }
273 const char *lamexp_version_compiler(void) { return g_lamexp_version_compiler; }
274 const char *lamexp_version_arch(void) { return g_lamexp_version_arch; }
275 unsigned int lamexp_toolver_neroaac(void) { return g_lamexp_toolver_neroaac; }
276 unsigned int lamexp_toolver_fhgaacenc(void) { return g_lamexp_toolver_fhgaacenc; }
277 unsigned int lamexp_toolver_qaacenc(void) { return g_lamexp_toolver_qaacenc; }
278 unsigned int lamexp_toolver_coreaudio(void) { return g_lamexp_toolver_coreaudio; }
279
280 /*
281  * URL getters
282  */
283 const char *lamexp_website_url(void) { return g_lamexp_website_url; }
284 const char *lamexp_support_url(void) { return g_lamexp_support_url; }
285
286 /*
287  * Check for Demo (pre-release) version
288  */
289 bool lamexp_version_demo(void)
290 {
291         char buffer[128];
292         bool releaseVersion = false;
293         if(!strncpy_s(buffer, 128, g_lamexp_version.ver_release_name, _TRUNCATE))
294         {
295                 char *context, *prefix = strtok_s(buffer, "-,; ", &context);
296                 if(prefix)
297                 {
298                         releaseVersion = (!_stricmp(prefix, "Final")) || (!_stricmp(prefix, "Hotfix"));
299                 }
300         }
301         return LAMEXP_DEBUG || (!releaseVersion);
302 }
303
304 /*
305  * Calculate expiration date
306  */
307 QDate lamexp_version_expires(void)
308 {
309         return lamexp_version_date().addDays(LAMEXP_DEBUG ? 7 : 30);
310 }
311
312 /*
313  * Get build date date
314  */
315 const QDate &lamexp_version_date(void)
316 {
317         if(!g_lamexp_version_date.isValid())
318         {
319                 int date[3] = {0, 0, 0}; char temp[12] = {'\0'};
320                 strncpy_s(temp, 12, g_lamexp_version_raw_date, _TRUNCATE);
321
322                 if(strlen(temp) == 11)
323                 {
324                         temp[3] = temp[6] = '\0';
325                         date[2] = atoi(&temp[4]);
326                         date[0] = atoi(&temp[7]);
327                         
328                         for(int j = 0; j < 12; j++)
329                         {
330                                 if(!_strcmpi(&temp[0], g_lamexp_months[j]))
331                                 {
332                                         date[1] = j+1;
333                                         break;
334                                 }
335                         }
336
337                         g_lamexp_version_date = QDate(date[0], date[1], date[2]);
338                 }
339
340                 if(!g_lamexp_version_date.isValid())
341                 {
342                         qFatal("Internal error: Date format could not be recognized!");
343                 }
344         }
345
346         return g_lamexp_version_date;
347 }
348
349 /*
350  * Get the native operating system version
351  */
352 DWORD lamexp_get_os_version(void)
353 {
354         static DWORD osVersion = 0;
355         
356         if(!osVersion)
357         {
358                 OSVERSIONINFO osVerInfo;
359                 memset(&osVerInfo, 0, sizeof(OSVERSIONINFO));
360                 osVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
361         
362                 if(GetVersionEx(&osVerInfo) == TRUE)
363                 {
364                         if(osVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT)
365                         {
366                                 throw "Ouuups: Not running under Windows NT. This is not supposed to happen!";
367                         }
368                         osVersion = (DWORD)((osVerInfo.dwMajorVersion << 16) | (osVerInfo.dwMinorVersion & 0xffff));
369                 }
370         }
371
372         return osVersion;
373 }
374
375 /*
376  * Check if we are running under wine
377  */
378 bool lamexp_detect_wine(void)
379 {
380         static bool isWine = false;
381         static bool isWine_initialized = false;
382
383         if(!isWine_initialized)
384         {
385                 QLibrary ntdll("ntdll.dll");
386                 if(ntdll.load())
387                 {
388                         if(ntdll.resolve("wine_nt_to_unix_file_name") != NULL) isWine = true;
389                         if(ntdll.resolve("wine_get_version") != NULL) isWine = true;
390                         ntdll.unload();
391                 }
392                 isWine_initialized = true;
393         }
394
395         return isWine;
396 }
397
398 /*
399  * Global exception handler
400  */
401 LONG WINAPI lamexp_exception_handler(__in struct _EXCEPTION_POINTERS *ExceptionInfo)
402 {
403         if(GetCurrentThreadId() != g_main_thread_id)
404         {
405                 HANDLE mainThread = OpenThread(THREAD_TERMINATE, FALSE, g_main_thread_id);
406                 if(mainThread) TerminateThread(mainThread, ULONG_MAX);
407         }
408         
409         FatalAppExit(0, L"Unhandeled exception handler invoked, application will exit!");
410         TerminateProcess(GetCurrentProcess(), -1);
411         return LONG_MAX;
412 }
413
414 /*
415  * Invalid parameters handler
416  */
417 void lamexp_invalid_param_handler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t)
418 {
419         if(GetCurrentThreadId() != g_main_thread_id)
420         {
421                 HANDLE mainThread = OpenThread(THREAD_TERMINATE, FALSE, g_main_thread_id);
422                 if(mainThread) TerminateThread(mainThread, ULONG_MAX);
423                 
424         }
425         
426         FatalAppExit(0, L"Invalid parameter handler invoked, application will exit!");
427         TerminateProcess(GetCurrentProcess(), -1);
428 }
429
430 /*
431  * Change console text color
432  */
433 static void lamexp_console_color(FILE* file, WORD attributes)
434 {
435         const HANDLE hConsole = (HANDLE)(_get_osfhandle(_fileno(file)));
436         if((hConsole != NULL) && (hConsole != INVALID_HANDLE_VALUE))
437         {
438                 SetConsoleTextAttribute(hConsole, attributes);
439         }
440 }
441
442 /*
443  * Qt message handler
444  */
445 void lamexp_message_handler(QtMsgType type, const char *msg)
446 {
447         static const char *GURU_MEDITATION = "\n\nGURU MEDITATION !!!\n\n";
448         
449         QMutexLocker lock(&g_lamexp_message_mutex);
450
451         if(g_lamexp_log_file)
452         {
453                 static char prefix[] = "DWCF";
454                 int index = qBound(0, static_cast<int>(type), 3);
455                 unsigned int timestamp = static_cast<unsigned int>(_time64(NULL) % 3600I64);
456                 QString str = QString::fromUtf8(msg).trimmed().replace('\n', '\t');
457                 fprintf(g_lamexp_log_file, "[%c][%04u] %s\r\n", prefix[index], timestamp, str.toUtf8().constData());
458                 fflush(g_lamexp_log_file);
459         }
460
461         if(g_lamexp_console_attached)
462         {
463                 UINT oldOutputCP = GetConsoleOutputCP();
464                 if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
465
466                 switch(type)
467                 {
468                 case QtCriticalMsg:
469                 case QtFatalMsg:
470                         fflush(stdout);
471                         fflush(stderr);
472                         lamexp_console_color(stderr, FOREGROUND_RED | FOREGROUND_INTENSITY);
473                         fprintf(stderr, GURU_MEDITATION);
474                         fprintf(stderr, "%s\n", msg);
475                         fflush(stderr);
476                         break;
477                 case QtWarningMsg:
478                         lamexp_console_color(stderr, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
479                         fprintf(stderr, "%s\n", msg);
480                         fflush(stderr);
481                         break;
482                 default:
483                         lamexp_console_color(stderr, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
484                         fprintf(stderr, "%s\n", msg);
485                         fflush(stderr);
486                         break;
487                 }
488         
489                 lamexp_console_color(stderr, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
490                 if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(oldOutputCP);
491         }
492         else
493         {
494                 QString temp("[LameXP][%1] %2");
495                 
496                 switch(type)
497                 {
498                 case QtCriticalMsg:
499                 case QtFatalMsg:
500                         temp = temp.arg("C", QString::fromUtf8(msg));
501                         break;
502                 case QtWarningMsg:
503                         temp = temp.arg("W", QString::fromUtf8(msg));
504                         break;
505                 default:
506                         temp = temp.arg("I", QString::fromUtf8(msg));
507                         break;
508                 }
509
510                 temp.replace("\n", "\t").append("\n");
511                 OutputDebugStringA(temp.toLatin1().constData());
512         }
513
514         if(type == QtCriticalMsg || type == QtFatalMsg)
515         {
516                 lock.unlock();
517
518                 if(GetCurrentThreadId() != g_main_thread_id)
519                 {
520                         HANDLE mainThread = OpenThread(THREAD_TERMINATE, FALSE, g_main_thread_id);
521                         if(mainThread) TerminateThread(mainThread, ULONG_MAX);
522                 }
523
524                 MessageBoxW(NULL, QWCHAR(QString::fromUtf8(msg)), L"LameXP - GURU MEDITATION", MB_ICONERROR | MB_TOPMOST | MB_TASKMODAL);
525                 FatalAppExit(0, L"The application has encountered a critical error and will exit now!");
526                 TerminateProcess(GetCurrentProcess(), -1);
527         }
528 }
529
530 /*
531  * Initialize the console
532  */
533 void lamexp_init_console(int argc, char* argv[])
534 {
535         bool enableConsole = lamexp_version_demo();
536
537         if(_environ)
538         {
539                 wchar_t *logfile = NULL;
540                 size_t logfile_len = 0;
541                 if(!_wdupenv_s(&logfile, &logfile_len, L"LAMEXP_LOGFILE"))
542                 {
543                         if(logfile && (logfile_len > 0))
544                         {
545                                 FILE *temp = NULL;
546                                 if(!_wfopen_s(&temp, logfile, L"wb"))
547                                 {
548                                         fprintf(temp, "%c%c%c", 0xEF, 0xBB, 0xBF);
549                                         g_lamexp_log_file = temp;
550                                 }
551                                 free(logfile);
552                         }
553                 }
554         }
555
556         if(!LAMEXP_DEBUG)
557         {
558                 for(int i = 0; i < argc; i++)
559                 {
560                         if(!_stricmp(argv[i], "--console"))
561                         {
562                                 enableConsole = true;
563                         }
564                         else if(!_stricmp(argv[i], "--no-console"))
565                         {
566                                 enableConsole = false;
567                         }
568                 }
569         }
570
571         if(enableConsole)
572         {
573                 if(!g_lamexp_console_attached)
574                 {
575                         if(AllocConsole() != FALSE)
576                         {
577                                 SetConsoleCtrlHandler(NULL, TRUE);
578                                 SetConsoleTitle(L"LameXP - Audio Encoder Front-End | Debug Console");
579                                 SetConsoleOutputCP(CP_UTF8);
580                                 g_lamexp_console_attached = true;
581                         }
582                 }
583                 
584                 if(g_lamexp_console_attached)
585                 {
586                         //-------------------------------------------------------------------
587                         //See: http://support.microsoft.com/default.aspx?scid=kb;en-us;105305
588                         //-------------------------------------------------------------------
589                         const int flags = _O_WRONLY | _O_U8TEXT;
590                         int hCrtStdOut = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), flags);
591                         int hCrtStdErr = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), flags);
592                         FILE *hfStdOut = (hCrtStdOut >= 0) ? _fdopen(hCrtStdOut, "wb") : NULL;
593                         FILE *hfStdErr = (hCrtStdErr >= 0) ? _fdopen(hCrtStdErr, "wb") : NULL;
594                         if(hfStdOut) { *stdout = *hfStdOut; std::cout.rdbuf(new std::filebuf(hfStdOut)); }
595                         if(hfStdErr) { *stderr = *hfStdErr; std::cerr.rdbuf(new std::filebuf(hfStdErr)); }
596                 }
597
598                 HWND hwndConsole = GetConsoleWindow();
599
600                 if((hwndConsole != NULL) && (hwndConsole != INVALID_HANDLE_VALUE))
601                 {
602                         HMENU hMenu = GetSystemMenu(hwndConsole, 0);
603                         EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
604                         RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
605
606                         SetWindowPos(hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
607                         SetWindowLong(hwndConsole, GWL_STYLE, GetWindowLong(hwndConsole, GWL_STYLE) & (~WS_MAXIMIZEBOX) & (~WS_MINIMIZEBOX));
608                         SetWindowPos(hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
609                 }
610         }
611 }
612
613 /*
614  * Detect CPU features
615  */
616 lamexp_cpu_t lamexp_detect_cpu_features(int argc, char **argv)
617 {
618         typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
619         typedef VOID (WINAPI *GetNativeSystemInfoFun)(__out LPSYSTEM_INFO lpSystemInfo);
620         
621         static IsWow64ProcessFun IsWow64ProcessPtr = NULL;
622         static GetNativeSystemInfoFun GetNativeSystemInfoPtr = NULL;
623
624         lamexp_cpu_t features;
625         SYSTEM_INFO systemInfo;
626         int CPUInfo[4] = {-1};
627         char CPUIdentificationString[0x40];
628         char CPUBrandString[0x40];
629
630         memset(&features, 0, sizeof(lamexp_cpu_t));
631         memset(&systemInfo, 0, sizeof(SYSTEM_INFO));
632         memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString));
633         memset(CPUBrandString, 0, sizeof(CPUBrandString));
634         
635         __cpuid(CPUInfo, 0);
636         memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int));
637         memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int));
638         memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int));
639         features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0);
640         strncpy_s(features.vendor, 0x40, CPUIdentificationString, _TRUNCATE);
641
642         if(CPUInfo[0] >= 1)
643         {
644                 __cpuid(CPUInfo, 1);
645                 features.mmx = (CPUInfo[3] & 0x800000) || false;
646                 features.sse = (CPUInfo[3] & 0x2000000) || false;
647                 features.sse2 = (CPUInfo[3] & 0x4000000) || false;
648                 features.ssse3 = (CPUInfo[2] & 0x200) || false;
649                 features.sse3 = (CPUInfo[2] & 0x1) || false;
650                 features.ssse3 = (CPUInfo[2] & 0x200) || false;
651                 features.stepping = CPUInfo[0] & 0xf;
652                 features.model = ((CPUInfo[0] >> 4) & 0xf) + (((CPUInfo[0] >> 16) & 0xf) << 4);
653                 features.family = ((CPUInfo[0] >> 8) & 0xf) + ((CPUInfo[0] >> 20) & 0xff);
654         }
655
656         __cpuid(CPUInfo, 0x80000000);
657         int nExIds = qMax<int>(qMin<int>(CPUInfo[0], 0x80000004), 0x80000000);
658
659         for(int i = 0x80000002; i <= nExIds; ++i)
660         {
661                 __cpuid(CPUInfo, i);
662                 switch(i)
663                 {
664                 case 0x80000002:
665                         memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
666                         break;
667                 case 0x80000003:
668                         memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
669                         break;
670                 case 0x80000004:
671                         memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
672                         break;
673                 }
674         }
675
676         strncpy_s(features.brand, 0x40, CPUBrandString, _TRUNCATE);
677
678         if(strlen(features.brand) < 1) strncpy_s(features.brand, 0x40, "Unknown", _TRUNCATE);
679         if(strlen(features.vendor) < 1) strncpy_s(features.vendor, 0x40, "Unknown", _TRUNCATE);
680
681 #if !defined(_M_X64 ) && !defined(_M_IA64)
682         if(!IsWow64ProcessPtr || !GetNativeSystemInfoPtr)
683         {
684                 QLibrary Kernel32Lib("kernel32.dll");
685                 IsWow64ProcessPtr = (IsWow64ProcessFun) Kernel32Lib.resolve("IsWow64Process");
686                 GetNativeSystemInfoPtr = (GetNativeSystemInfoFun) Kernel32Lib.resolve("GetNativeSystemInfo");
687         }
688         if(IsWow64ProcessPtr)
689         {
690                 BOOL x64 = FALSE;
691                 if(IsWow64ProcessPtr(GetCurrentProcess(), &x64))
692                 {
693                         features.x64 = x64;
694                 }
695         }
696         if(GetNativeSystemInfoPtr)
697         {
698                 GetNativeSystemInfoPtr(&systemInfo);
699         }
700         else
701         {
702                 GetSystemInfo(&systemInfo);
703         }
704         features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL);
705 #else
706         GetNativeSystemInfo(&systemInfo);
707         features.count = systemInfo.dwNumberOfProcessors;
708         features.x64 = true;
709 #endif
710
711         if((argv != NULL) && (argc > 0))
712         {
713                 bool flag = false;
714                 for(int i = 0; i < argc; i++)
715                 {
716                         if(!_stricmp("--force-cpu-no-64bit", argv[i])) { flag = true; features.x64 = false; }
717                         if(!_stricmp("--force-cpu-no-sse", argv[i])) { flag = true; features.sse = features.sse2 = features.sse3 = features.ssse3 = false; }
718                         if(!_stricmp("--force-cpu-no-intel", argv[i])) { flag = true; features.intel = false; }
719                 }
720                 if(flag) qWarning("CPU flags overwritten by user-defined parameters. Take care!\n");
721         }
722
723         return features;
724 }
725
726 /*
727  * Check for debugger (detect routine)
728  */
729 static __forceinline bool lamexp_check_for_debugger(void)
730 {
731         if(IsDebuggerPresent())
732         {
733                 return true;
734         }
735         
736         __try
737         {
738                 CloseHandle((HANDLE) 0x7FFFFFFF);
739         }
740         __except(EXCEPTION_EXECUTE_HANDLER)
741         {
742                 return true;
743         }
744
745         __try 
746         {
747                 DebugBreak();
748         }
749         __except(EXCEPTION_EXECUTE_HANDLER) 
750         {
751                 return false;
752         }
753         
754         return true;
755 }
756
757 /*
758  * Check for debugger (thread proc)
759  */
760 static unsigned int __stdcall lamexp_debug_thread_proc(LPVOID lpParameter)
761 {
762         while(!lamexp_check_for_debugger())
763         {
764                 Sleep(32);
765         }
766         if(HANDLE thrd = OpenThread(THREAD_TERMINATE, FALSE, g_main_thread_id))
767         {
768                 if(TerminateThread(thrd, -1))
769                 {
770                         FatalAppExit(0, L"Not a debug build. Please unload debugger and try again!");
771                 }
772                 CloseHandle(thrd);
773         }
774         TerminateProcess(GetCurrentProcess(), -1);
775         return 666;
776 }
777
778 /*
779  * Check for debugger (startup routine)
780  */
781 static HANDLE lamexp_debug_thread_init(void)
782 {
783         if(lamexp_check_for_debugger())
784         {
785                 FatalAppExit(0, L"Not a debug build. Please unload debugger and try again!");
786                 TerminateProcess(GetCurrentProcess(), -1);
787         }
788
789         return (HANDLE) _beginthreadex(NULL, 0, lamexp_debug_thread_proc, NULL, 0, NULL);
790 }
791
792 /*
793  * Check for compatibility mode
794  */
795 static bool lamexp_check_compatibility_mode(const char *exportName, const char *executableName)
796 {
797         QLibrary kernel32("kernel32.dll");
798
799         if(exportName != NULL)
800         {
801                 if(kernel32.resolve(exportName) != NULL)
802                 {
803                         qWarning("Function '%s' exported from 'kernel32.dll' -> Windows compatibility mode!", exportName);
804                         qFatal("%s", QApplication::tr("Executable '%1' doesn't support Windows compatibility mode.").arg(QString::fromLatin1(executableName)).toLatin1().constData());
805                         return false;
806                 }
807         }
808
809         return true;
810 }
811
812 /*
813  * Computus according to H. Lichtenberg
814  */
815 static bool lamexp_computus(const QDate &date)
816 {
817         int X = date.year();
818         int A = X % 19;
819         int K = X / 100;
820         int M = 15 + (3*K + 3) / 4 - (8*K + 13) / 25;
821         int D = (19*A + M) % 30;
822         int S = 2 - (3*K + 3) / 4;
823         int R = D / 29 + (D / 28 - D / 29) * (A / 11);
824         int OG = 21 + D - R;
825         int SZ = 7 - (X + X / 4 + S) % 7;
826         int OE = 7 - (OG - SZ) % 7;
827         int OS = (OG + OE);
828
829         if(OS > 31)
830         {
831                 return (date.month() == 4) && (date.day() == (OS - 31));
832         }
833         else
834         {
835                 return (date.month() == 3) && (date.day() == OS);
836         }
837 }
838
839 /*
840  * Check for Thanksgiving
841  */
842 static bool lamexp_thanksgiving(const QDate &date)
843 {
844         int day = 0;
845
846         switch(QDate(date.year(), 11, 1).dayOfWeek())
847         {
848                 case 1: day = 25; break; 
849                 case 2: day = 24; break; 
850                 case 3: day = 23; break; 
851                 case 4: day = 22; break; 
852                 case 5: day = 28; break; 
853                 case 6: day = 27; break; 
854                 case 7: day = 26; break;
855         }
856
857         return (date.month() == 11) && (date.day() == day);
858 }
859
860 /*
861  * Initialize app icon
862  */
863 QIcon lamexp_app_icon(const QDate *date, const QTime *time)
864 {
865         QDate currentDate = (date) ? QDate(*date) : QDate::currentDate();
866         QTime currentTime = (time) ? QTime(*time) : QTime::currentTime();
867         
868         if(lamexp_thanksgiving(currentDate))
869         {
870                 return QIcon(":/MainIcon6.png");
871         }
872         else if(((currentDate.month() == 12) && (currentDate.day() == 31) && (currentTime.hour() >= 20)) || ((currentDate.month() == 1) && (currentDate.day() == 1)  && (currentTime.hour() <= 19)))
873         {
874                 return QIcon(":/MainIcon5.png");
875         }
876         else if(((currentDate.month() == 10) && (currentDate.day() == 31) && (currentTime.hour() >= 12)) || ((currentDate.month() == 11) && (currentDate.day() == 1)  && (currentTime.hour() <= 11)))
877         {
878                 return QIcon(":/MainIcon4.png");
879         }
880         else if((currentDate.month() == 12) && (currentDate.day() >= 24) && (currentDate.day() <= 26))
881         {
882                 return QIcon(":/MainIcon3.png");
883         }
884         else if(lamexp_computus(currentDate))
885         {
886                 return QIcon(":/MainIcon2.png");
887         }
888         else
889         {
890                 return QIcon(":/MainIcon1.png");
891         }
892 }
893
894 /*
895  * Broadcast event to all windows
896  */
897 static bool lamexp_broadcast(int eventType, bool onlyToVisible)
898 {
899         if(QApplication *app = dynamic_cast<QApplication*>(QApplication::instance()))
900         {
901                 qDebug("Broadcasting %d", eventType);
902                 
903                 bool allOk = true;
904                 QEvent poEvent(static_cast<QEvent::Type>(eventType));
905                 QWidgetList list = app->topLevelWidgets();
906
907                 while(!list.isEmpty())
908                 {
909                         QWidget *widget = list.takeFirst();
910                         if(!onlyToVisible || widget->isVisible())
911                         {
912                                 if(!app->sendEvent(widget, &poEvent))
913                                 {
914                                         allOk = false;
915                                 }
916                         }
917                 }
918
919                 qDebug("Broadcast %d done (%s)", eventType, (allOk ? "OK" : "Stopped"));
920                 return allOk;
921         }
922         else
923         {
924                 qWarning("Broadcast failed, could not get QApplication instance!");
925                 return false;
926         }
927 }
928
929 /*
930  * Qt event filter
931  */
932 static bool lamexp_event_filter(void *message, long *result)
933 {
934         if((!(LAMEXP_DEBUG)) && lamexp_check_for_debugger())
935         {
936                 FatalAppExit(0, L"Not a debug build. Please unload debugger and try again!");
937                 TerminateProcess(GetCurrentProcess(), -1);
938         }
939         
940         switch(reinterpret_cast<MSG*>(message)->message)
941         {
942         case WM_QUERYENDSESSION:
943                 qWarning("WM_QUERYENDSESSION message received!");
944                 *result = lamexp_broadcast(lamexp_event_queryendsession, false) ? TRUE : FALSE;
945                 return true;
946         case WM_ENDSESSION:
947                 qWarning("WM_ENDSESSION message received!");
948                 if(reinterpret_cast<MSG*>(message)->wParam == TRUE)
949                 {
950                         lamexp_broadcast(lamexp_event_endsession, false);
951                         if(QApplication *app = reinterpret_cast<QApplication*>(QApplication::instance()))
952                         {
953                                 app->closeAllWindows();
954                                 app->quit();
955                         }
956                         lamexp_finalization();
957                         exit(1);
958                 }
959                 *result = 0;
960                 return true;
961         default:
962                 /*ignore this message and let Qt handle it*/
963                 return false;
964         }
965 }
966
967 /*
968  * Check for process elevation
969  */
970 static bool lamexp_check_elevation(void)
971 {
972         typedef enum { lamexp_token_elevationType_class = 18, lamexp_token_elevation_class = 20 } LAMEXP_TOKEN_INFORMATION_CLASS;
973         typedef enum { lamexp_elevationType_default = 1, lamexp_elevationType_full, lamexp_elevationType_limited } LAMEXP_TOKEN_ELEVATION_TYPE;
974
975         HANDLE hToken = NULL;
976         bool bIsProcessElevated = false;
977         
978         if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
979         {
980                 LAMEXP_TOKEN_ELEVATION_TYPE tokenElevationType;
981                 DWORD returnLength;
982                 if(GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS) lamexp_token_elevationType_class, &tokenElevationType, sizeof(LAMEXP_TOKEN_ELEVATION_TYPE), &returnLength))
983                 {
984                         if(returnLength == sizeof(LAMEXP_TOKEN_ELEVATION_TYPE))
985                         {
986                                 switch(tokenElevationType)
987                                 {
988                                 case lamexp_elevationType_default:
989                                         qDebug("Process token elevation type: Default -> UAC is disabled.\n");
990                                         break;
991                                 case lamexp_elevationType_full:
992                                         qWarning("Process token elevation type: Full -> potential security risk!\n");
993                                         bIsProcessElevated = true;
994                                         break;
995                                 case lamexp_elevationType_limited:
996                                         qDebug("Process token elevation type: Limited -> not elevated.\n");
997                                         break;
998                                 }
999                         }
1000                 }
1001                 CloseHandle(hToken);
1002         }
1003         else
1004         {
1005                 qWarning("Failed to open process token!");
1006         }
1007
1008         return !bIsProcessElevated;
1009 }
1010
1011 /*
1012  * Initialize Qt framework
1013  */
1014 bool lamexp_init_qt(int argc, char* argv[])
1015 {
1016         static bool qt_initialized = false;
1017         typedef BOOL (WINAPI *SetDllDirectoryProc)(WCHAR *lpPathName);
1018
1019         //Don't initialized again, if done already
1020         if(qt_initialized)
1021         {
1022                 return true;
1023         }
1024         
1025         //Secure DLL loading
1026         QLibrary kernel32("kernel32.dll");
1027         if(kernel32.load())
1028         {
1029                 SetDllDirectoryProc pSetDllDirectory = (SetDllDirectoryProc) kernel32.resolve("SetDllDirectoryW");
1030                 if(pSetDllDirectory != NULL) pSetDllDirectory(L"");
1031                 kernel32.unload();
1032         }
1033
1034         //Extract executable name from argv[] array
1035         char *executableName = argv[0];
1036         while(char *temp = strpbrk(executableName, "\\/:?"))
1037         {
1038                 executableName = temp + 1;
1039         }
1040
1041         //Check Qt version
1042 #ifdef QT_BUILD_KEY
1043         qDebug("Using Qt v%s [%s], %s, %s", qVersion(), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), (qSharedBuild() ? "DLL" : "Static"), QLibraryInfo::buildKey().toLatin1().constData());
1044         qDebug("Compiled with Qt v%s [%s], %s\n", QT_VERSION_STR, QT_PACKAGEDATE_STR, QT_BUILD_KEY);
1045         if(_stricmp(qVersion(), QT_VERSION_STR))
1046         {
1047                 qFatal("%s", QApplication::tr("Executable '%1' requires Qt v%2, but found Qt v%3.").arg(QString::fromLatin1(executableName), QString::fromLatin1(QT_VERSION_STR), QString::fromLatin1(qVersion())).toLatin1().constData());
1048                 return false;
1049         }
1050         if(QLibraryInfo::buildKey().compare(QString::fromLatin1(QT_BUILD_KEY), Qt::CaseInsensitive))
1051         {
1052                 qFatal("%s", QApplication::tr("Executable '%1' was built for Qt '%2', but found Qt '%3'.").arg(QString::fromLatin1(executableName), QString::fromLatin1(QT_BUILD_KEY), QLibraryInfo::buildKey()).toLatin1().constData());
1053                 return false;
1054         }
1055 #else
1056         qDebug("Using Qt v%s [%s], %s", qVersion(), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), (qSharedBuild() ? "DLL" : "Static"));
1057         qDebug("Compiled with Qt v%s [%s]\n", QT_VERSION_STR, QT_PACKAGEDATE_STR);
1058 #endif
1059
1060         //Check the Windows version
1061         switch(QSysInfo::windowsVersion() & QSysInfo::WV_NT_based)
1062         {
1063         case 0:
1064         case QSysInfo::WV_NT:
1065                 qFatal("%s", QApplication::tr("Executable '%1' requires Windows 2000 or later.").arg(QString::fromLatin1(executableName)).toLatin1().constData());
1066                 break;
1067         case QSysInfo::WV_2000:
1068                 qDebug("Running on Windows 2000 (not officially supported!).\n");
1069                 lamexp_check_compatibility_mode("GetNativeSystemInfo", executableName);
1070                 break;
1071         case QSysInfo::WV_XP:
1072                 qDebug("Running on Windows XP.\n");
1073                 lamexp_check_compatibility_mode("GetLargePageMinimum", executableName);
1074                 break;
1075         case QSysInfo::WV_2003:
1076                 qDebug("Running on Windows Server 2003 or Windows XP x64-Edition.\n");
1077                 lamexp_check_compatibility_mode("GetLocaleInfoEx", executableName);
1078                 break;
1079         case QSysInfo::WV_VISTA:
1080                 qDebug("Running on Windows Vista or Windows Server 2008.\n");
1081                 lamexp_check_compatibility_mode("CreateRemoteThreadEx", executableName);
1082                 break;
1083         case QSysInfo::WV_WINDOWS7:
1084                 qDebug("Running on Windows 7 or Windows Server 2008 R2.\n");
1085                 lamexp_check_compatibility_mode(NULL, executableName);
1086                 break;
1087         default:
1088                 {
1089                         DWORD osVersionNo = lamexp_get_os_version();
1090                         qWarning("Running on an unknown/untested WinNT-based OS (v%u.%u).\n", HIWORD(osVersionNo), LOWORD(osVersionNo));
1091                 }
1092                 break;
1093         }
1094
1095         //Check for Wine
1096         if(lamexp_detect_wine())
1097         {
1098                 qWarning("It appears we are running under Wine, unexpected things might happen!\n");
1099         }
1100
1101         //Set text Codec for locale
1102         QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
1103
1104         //Create Qt application instance
1105         QApplication *application = new QApplication(argc, argv);
1106
1107         //Load plugins from application directory
1108         QCoreApplication::setLibraryPaths(QStringList() << QApplication::applicationDirPath());
1109         qDebug("Library Path:\n%s\n", QApplication::libraryPaths().first().toUtf8().constData());
1110
1111         //Set application properties
1112         application->setApplicationName("LameXP - Audio Encoder Front-End");
1113         application->setApplicationVersion(QString().sprintf("%d.%02d.%04d", lamexp_version_major(), lamexp_version_minor(), lamexp_version_build())); 
1114         application->setOrganizationName("LoRd_MuldeR");
1115         application->setOrganizationDomain("mulder.at.gg");
1116         application->setWindowIcon(lamexp_app_icon());
1117         application->setEventFilter(lamexp_event_filter);
1118
1119         //Set text Codec for locale
1120         // QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
1121
1122         //Load plugins from application directory
1123         // QCoreApplication::setLibraryPaths(QStringList() << QApplication::applicationDirPath());
1124         // qDebug("Library Path:\n%s\n", QApplication::libraryPaths().first().toUtf8().constData());
1125
1126         //Check for supported image formats
1127         QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
1128         for(int i = 0; g_lamexp_imageformats[i]; i++)
1129         {
1130                 if(!supportedFormats.contains(g_lamexp_imageformats[i]))
1131                 {
1132                         qFatal("Qt initialization error: QImageIOHandler for '%s' missing!", g_lamexp_imageformats[i]);
1133                         return false;
1134                 }
1135         }
1136
1137         //Add default translations
1138         g_lamexp_translation.files.insert(LAMEXP_DEFAULT_LANGID, "");
1139         g_lamexp_translation.names.insert(LAMEXP_DEFAULT_LANGID, "English");
1140
1141         //Check for process elevation
1142         if((!lamexp_check_elevation()) && (!lamexp_detect_wine()))
1143         {
1144                 QMessageBox messageBox(QMessageBox::Warning, "LameXP", "<nobr>LameXP was started with 'elevated' rights, altough LameXP does not need these rights.<br>Running an applications with unnecessary rights is a potential security risk!</nobr>", QMessageBox::NoButton, NULL, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint);
1145                 messageBox.addButton("Quit Program (Recommended)", QMessageBox::NoRole);
1146                 messageBox.addButton("Ignore", QMessageBox::NoRole);
1147                 if(messageBox.exec() == 0)
1148                 {
1149                         return false;
1150                 }
1151         }
1152
1153         //Update console icon, if a console is attached
1154 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
1155         if(g_lamexp_console_attached && (!lamexp_detect_wine()))
1156         {
1157                 typedef DWORD (__stdcall *SetConsoleIconFun)(HICON);
1158                 QLibrary kernel32("kernel32.dll");
1159                 if(kernel32.load())
1160                 {
1161                         SetConsoleIconFun SetConsoleIconPtr = (SetConsoleIconFun) kernel32.resolve("SetConsoleIcon");
1162                         if(SetConsoleIconPtr != NULL) SetConsoleIconPtr(QIcon(":/icons/sound.png").pixmap(16, 16).toWinHICON());
1163                         kernel32.unload();
1164                 }
1165         }
1166 #endif
1167
1168         //Done
1169         qt_initialized = true;
1170         return true;
1171 }
1172
1173 /*
1174  * Initialize IPC
1175  */
1176 int lamexp_init_ipc(void)
1177 {
1178         if(g_lamexp_ipc_ptr.sharedmem && g_lamexp_ipc_ptr.semaphore_read && g_lamexp_ipc_ptr.semaphore_write && g_lamexp_ipc_ptr.semaphore_read_mutex && g_lamexp_ipc_ptr.semaphore_write_mutex)
1179         {
1180                 return 0;
1181         }
1182
1183         g_lamexp_ipc_ptr.semaphore_read = new QSystemSemaphore(QString(g_lamexp_ipc_uuid.semaphore_read), 0);
1184         g_lamexp_ipc_ptr.semaphore_write = new QSystemSemaphore(QString(g_lamexp_ipc_uuid.semaphore_write), 0);
1185         g_lamexp_ipc_ptr.semaphore_read_mutex = new QSystemSemaphore(QString(g_lamexp_ipc_uuid.semaphore_read_mutex), 0);
1186         g_lamexp_ipc_ptr.semaphore_write_mutex = new QSystemSemaphore(QString(g_lamexp_ipc_uuid.semaphore_write_mutex), 0);
1187
1188         if(g_lamexp_ipc_ptr.semaphore_read->error() != QSystemSemaphore::NoError)
1189         {
1190                 QString errorMessage = g_lamexp_ipc_ptr.semaphore_read->errorString();
1191                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read);
1192                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write);
1193                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read_mutex);
1194                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write_mutex);
1195                 qFatal("Failed to create system smaphore: %s", errorMessage.toUtf8().constData());
1196                 return -1;
1197         }
1198         if(g_lamexp_ipc_ptr.semaphore_write->error() != QSystemSemaphore::NoError)
1199         {
1200                 QString errorMessage = g_lamexp_ipc_ptr.semaphore_write->errorString();
1201                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read);
1202                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write);
1203                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read_mutex);
1204                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write_mutex);
1205                 qFatal("Failed to create system smaphore: %s", errorMessage.toUtf8().constData());
1206                 return -1;
1207         }
1208         if(g_lamexp_ipc_ptr.semaphore_read_mutex->error() != QSystemSemaphore::NoError)
1209         {
1210                 QString errorMessage = g_lamexp_ipc_ptr.semaphore_read_mutex->errorString();
1211                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read);
1212                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write);
1213                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read_mutex);
1214                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write_mutex);
1215                 qFatal("Failed to create system smaphore: %s", errorMessage.toUtf8().constData());
1216                 return -1;
1217         }
1218         if(g_lamexp_ipc_ptr.semaphore_write_mutex->error() != QSystemSemaphore::NoError)
1219         {
1220                 QString errorMessage = g_lamexp_ipc_ptr.semaphore_write_mutex->errorString();
1221                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read);
1222                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write);
1223                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read_mutex);
1224                 LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write_mutex);
1225                 qFatal("Failed to create system smaphore: %s", errorMessage.toUtf8().constData());
1226                 return -1;
1227         }
1228
1229         g_lamexp_ipc_ptr.sharedmem = new QSharedMemory(QString(g_lamexp_ipc_uuid.sharedmem), NULL);
1230         
1231         if(!g_lamexp_ipc_ptr.sharedmem->create(sizeof(lamexp_ipc_t)))
1232         {
1233                 if(g_lamexp_ipc_ptr.sharedmem->error() == QSharedMemory::AlreadyExists)
1234                 {
1235                         g_lamexp_ipc_ptr.sharedmem->attach();
1236                         if(g_lamexp_ipc_ptr.sharedmem->error() == QSharedMemory::NoError)
1237                         {
1238                                 return 1;
1239                         }
1240                         else
1241                         {
1242                                 QString errorMessage = g_lamexp_ipc_ptr.sharedmem->errorString();
1243                                 LAMEXP_DELETE(g_lamexp_ipc_ptr.sharedmem);
1244                                 qFatal("Failed to attach to shared memory: %s", errorMessage.toUtf8().constData());
1245                                 return -1;
1246                         }
1247                 }
1248                 else
1249                 {
1250                         QString errorMessage = g_lamexp_ipc_ptr.sharedmem->errorString();
1251                         LAMEXP_DELETE(g_lamexp_ipc_ptr.sharedmem);
1252                         qFatal("Failed to create shared memory: %s", errorMessage.toUtf8().constData());
1253                         return -1;
1254                 }
1255         }
1256
1257         memset(g_lamexp_ipc_ptr.sharedmem->data(), 0, sizeof(lamexp_ipc_t));
1258         g_lamexp_ipc_ptr.semaphore_write->release(g_lamexp_ipc_slots);
1259         g_lamexp_ipc_ptr.semaphore_read_mutex->release();
1260         g_lamexp_ipc_ptr.semaphore_write_mutex->release();
1261
1262         return 0;
1263 }
1264
1265 /*
1266  * IPC send message
1267  */
1268 void lamexp_ipc_send(unsigned int command, const char* message)
1269 {
1270         if(!g_lamexp_ipc_ptr.sharedmem || !g_lamexp_ipc_ptr.semaphore_read || !g_lamexp_ipc_ptr.semaphore_write || !g_lamexp_ipc_ptr.semaphore_read_mutex || !g_lamexp_ipc_ptr.semaphore_write_mutex)
1271         {
1272                 throw "Shared memory for IPC not initialized yet.";
1273         }
1274
1275         lamexp_ipc_data_t ipc_data;
1276         memset(&ipc_data, 0, sizeof(lamexp_ipc_data_t));
1277         ipc_data.command = command;
1278         
1279         if(message)
1280         {
1281                 strncpy_s(ipc_data.parameter, 4096, message, _TRUNCATE);
1282         }
1283
1284         if(g_lamexp_ipc_ptr.semaphore_write->acquire())
1285         {
1286                 if(g_lamexp_ipc_ptr.semaphore_write_mutex->acquire())
1287                 {
1288                         lamexp_ipc_t *ptr = reinterpret_cast<lamexp_ipc_t*>(g_lamexp_ipc_ptr.sharedmem->data());
1289                         memcpy(&ptr->data[ptr->pos_write], &ipc_data, sizeof(lamexp_ipc_data_t));
1290                         ptr->pos_write = (ptr->pos_write + 1) % g_lamexp_ipc_slots;
1291                         g_lamexp_ipc_ptr.semaphore_read->release();
1292                         g_lamexp_ipc_ptr.semaphore_write_mutex->release();
1293                 }
1294         }
1295 }
1296
1297 /*
1298  * IPC read message
1299  */
1300 void lamexp_ipc_read(unsigned int *command, char* message, size_t buffSize)
1301 {
1302         *command = 0;
1303         message[0] = '\0';
1304         
1305         if(!g_lamexp_ipc_ptr.sharedmem || !g_lamexp_ipc_ptr.semaphore_read || !g_lamexp_ipc_ptr.semaphore_write || !g_lamexp_ipc_ptr.semaphore_read_mutex || !g_lamexp_ipc_ptr.semaphore_write_mutex)
1306         {
1307                 throw "Shared memory for IPC not initialized yet.";
1308         }
1309
1310         lamexp_ipc_data_t ipc_data;
1311         memset(&ipc_data, 0, sizeof(lamexp_ipc_data_t));
1312
1313         if(g_lamexp_ipc_ptr.semaphore_read->acquire())
1314         {
1315                 if(g_lamexp_ipc_ptr.semaphore_read_mutex->acquire())
1316                 {
1317                         lamexp_ipc_t *ptr = reinterpret_cast<lamexp_ipc_t*>(g_lamexp_ipc_ptr.sharedmem->data());
1318                         memcpy(&ipc_data, &ptr->data[ptr->pos_read], sizeof(lamexp_ipc_data_t));
1319                         ptr->pos_read = (ptr->pos_read + 1) % g_lamexp_ipc_slots;
1320                         g_lamexp_ipc_ptr.semaphore_write->release();
1321                         g_lamexp_ipc_ptr.semaphore_read_mutex->release();
1322
1323                         if(!(ipc_data.reserved_1 || ipc_data.reserved_2))
1324                         {
1325                                 *command = ipc_data.command;
1326                                 strncpy_s(message, buffSize, ipc_data.parameter, _TRUNCATE);
1327                         }
1328                         else
1329                         {
1330                                 qWarning("Malformed IPC message, will be ignored");
1331                         }
1332                 }
1333         }
1334 }
1335
1336 /*
1337  * Check for LameXP "portable" mode
1338  */
1339 bool lamexp_portable_mode(void)
1340 {
1341         QString baseName = QFileInfo(QApplication::applicationFilePath()).completeBaseName();
1342         int idx1 = baseName.indexOf("lamexp", 0, Qt::CaseInsensitive);
1343         int idx2 = baseName.lastIndexOf("portable", -1, Qt::CaseInsensitive);
1344         return (idx1 >= 0) && (idx2 >= 0) && (idx1 < idx2);
1345 }
1346
1347 /*
1348  * Get a random string
1349  */
1350 QString lamexp_rand_str(void)
1351 {
1352         QRegExp regExp("\\{(\\w+)-(\\w+)-(\\w+)-(\\w+)-(\\w+)\\}");
1353         QString uuid = QUuid::createUuid().toString();
1354
1355         if(regExp.indexIn(uuid) >= 0)
1356         {
1357                 return QString().append(regExp.cap(1)).append(regExp.cap(2)).append(regExp.cap(3)).append(regExp.cap(4)).append(regExp.cap(5));
1358         }
1359
1360         throw "The RegExp didn't match on the UUID string. This shouldn't happen ;-)";
1361 }
1362
1363 /*
1364  * Get LameXP temp folder
1365  */
1366 const QString &lamexp_temp_folder2(void)
1367 {
1368         static const char *TEMP_STR = "Temp";
1369         const QString WRITE_TEST_DATA = lamexp_rand_str();
1370         const QString SUB_FOLDER = lamexp_rand_str();
1371
1372         //Already initialized?
1373         if(!g_lamexp_temp_folder.isEmpty())
1374         {
1375                 if(QDir(g_lamexp_temp_folder).exists())
1376                 {
1377                         return g_lamexp_temp_folder;
1378                 }
1379                 else
1380                 {
1381                         g_lamexp_temp_folder.clear();
1382                 }
1383         }
1384         
1385         //Try the %TMP% or %TEMP% directory first
1386         QDir temp = QDir::temp();
1387         if(temp.exists())
1388         {
1389                 temp.mkdir(SUB_FOLDER);
1390                 if(temp.cd(SUB_FOLDER) && temp.exists())
1391                 {
1392                         QFile testFile(QString("%1/~%2.tmp").arg(temp.canonicalPath(), lamexp_rand_str()));
1393                         if(testFile.open(QIODevice::ReadWrite))
1394                         {
1395                                 if(testFile.write(WRITE_TEST_DATA.toLatin1().constData()) >= strlen(WRITE_TEST_DATA.toLatin1().constData()))
1396                                 {
1397                                         g_lamexp_temp_folder = temp.canonicalPath();
1398                                 }
1399                                 testFile.remove();
1400                         }
1401                 }
1402                 if(!g_lamexp_temp_folder.isEmpty())
1403                 {
1404                         return g_lamexp_temp_folder;
1405                 }
1406         }
1407
1408         //Create TEMP folder in %LOCALAPPDATA%
1409         QDir localAppData = QDir(lamexp_known_folder(lamexp_folder_localappdata));
1410         if(!localAppData.path().isEmpty())
1411         {
1412                 if(!localAppData.exists())
1413                 {
1414                         localAppData.mkpath(".");
1415                 }
1416                 if(localAppData.exists())
1417                 {
1418                         if(!localAppData.entryList(QDir::AllDirs).contains(TEMP_STR, Qt::CaseInsensitive))
1419                         {
1420                                 localAppData.mkdir(TEMP_STR);
1421                         }
1422                         if(localAppData.cd(TEMP_STR) && localAppData.exists())
1423                         {
1424                                 localAppData.mkdir(SUB_FOLDER);
1425                                 if(localAppData.cd(SUB_FOLDER) && localAppData.exists())
1426                                 {
1427                                         QFile testFile(QString("%1/~%2.tmp").arg(localAppData.canonicalPath(), lamexp_rand_str()));
1428                                         if(testFile.open(QIODevice::ReadWrite))
1429                                         {
1430                                                 if(testFile.write(WRITE_TEST_DATA.toLatin1().constData()) >= strlen(WRITE_TEST_DATA.toLatin1().constData()))
1431                                                 {
1432                                                         g_lamexp_temp_folder = localAppData.canonicalPath();
1433                                                 }
1434                                                 testFile.remove();
1435                                         }
1436                                 }
1437                         }
1438                 }
1439                 if(!g_lamexp_temp_folder.isEmpty())
1440                 {
1441                         return g_lamexp_temp_folder;
1442                 }
1443         }
1444
1445         //Failed to create TEMP folder!
1446         qFatal("Temporary directory could not be initialized!\n\nFirst attempt:\n%s\n\nSecond attempt:\n%s", temp.canonicalPath().toUtf8().constData(), localAppData.canonicalPath().toUtf8().constData());
1447         return g_lamexp_temp_folder;
1448 }
1449
1450 /*
1451  * Clean folder
1452  */
1453 bool lamexp_clean_folder(const QString &folderPath)
1454 {
1455         QDir tempFolder(folderPath);
1456         QFileInfoList entryList = tempFolder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
1457
1458         for(int i = 0; i < entryList.count(); i++)
1459         {
1460                 if(entryList.at(i).isDir())
1461                 {
1462                         lamexp_clean_folder(entryList.at(i).canonicalFilePath());
1463                 }
1464                 else
1465                 {
1466                         for(int j = 0; j < 3; j++)
1467                         {
1468                                 if(lamexp_remove_file(entryList.at(i).canonicalFilePath()))
1469                                 {
1470                                         break;
1471                                 }
1472                         }
1473                 }
1474         }
1475         
1476         tempFolder.rmdir(".");
1477         return !tempFolder.exists();
1478 }
1479
1480 /*
1481  * Register tool
1482  */
1483 void lamexp_register_tool(const QString &toolName, LockedFile *file, unsigned int version)
1484 {
1485         if(g_lamexp_tool_registry.contains(toolName.toLower()))
1486         {
1487                 throw "lamexp_register_tool: Tool is already registered!";
1488         }
1489
1490         g_lamexp_tool_registry.insert(toolName.toLower(), file);
1491         g_lamexp_tool_versions.insert(toolName.toLower(), version);
1492 }
1493
1494 /*
1495  * Check for tool
1496  */
1497 bool lamexp_check_tool(const QString &toolName)
1498 {
1499         return g_lamexp_tool_registry.contains(toolName.toLower());
1500 }
1501
1502 /*
1503  * Lookup tool path
1504  */
1505 const QString lamexp_lookup_tool(const QString &toolName)
1506 {
1507         if(g_lamexp_tool_registry.contains(toolName.toLower()))
1508         {
1509                 return g_lamexp_tool_registry.value(toolName.toLower())->filePath();
1510         }
1511         else
1512         {
1513                 return QString();
1514         }
1515 }
1516
1517 /*
1518  * Lookup tool version
1519  */
1520 unsigned int lamexp_tool_version(const QString &toolName)
1521 {
1522         if(g_lamexp_tool_versions.contains(toolName.toLower()))
1523         {
1524                 return g_lamexp_tool_versions.value(toolName.toLower());
1525         }
1526         else
1527         {
1528                 return UINT_MAX;
1529         }
1530 }
1531
1532 /*
1533  * Version number to human-readable string
1534  */
1535 const QString lamexp_version2string(const QString &pattern, unsigned int version, const QString &defaultText)
1536 {
1537         if(version == UINT_MAX)
1538         {
1539                 return defaultText;
1540         }
1541         
1542         QString result = pattern;
1543         int digits = result.count("?", Qt::CaseInsensitive);
1544         
1545         if(digits < 1)
1546         {
1547                 return result;
1548         }
1549         
1550         int pos = 0;
1551         QString versionStr = QString().sprintf(QString().sprintf("%%0%du", digits).toLatin1().constData(), version);
1552         int index = result.indexOf("?", Qt::CaseInsensitive);
1553         
1554         while(index >= 0 && pos < versionStr.length())
1555         {
1556                 result[index] = versionStr[pos++];
1557                 index = result.indexOf("?", Qt::CaseInsensitive);
1558         }
1559
1560         return result;
1561 }
1562
1563 /*
1564  * Register a new translation
1565  */
1566 bool lamexp_translation_register(const QString &langId, const QString &qmFile, const QString &langName, unsigned int &systemId, unsigned int &country)
1567 {
1568         if(qmFile.isEmpty() || langName.isEmpty() || systemId < 1)
1569         {
1570                 return false;
1571         }
1572
1573         g_lamexp_translation.files.insert(langId, qmFile);
1574         g_lamexp_translation.names.insert(langId, langName);
1575         g_lamexp_translation.sysid.insert(langId, systemId);
1576         g_lamexp_translation.cntry.insert(langId, country);
1577
1578         return true;
1579 }
1580
1581 /*
1582  * Get list of all translations
1583  */
1584 QStringList lamexp_query_translations(void)
1585 {
1586         return g_lamexp_translation.files.keys();
1587 }
1588
1589 /*
1590  * Get translation name
1591  */
1592 QString lamexp_translation_name(const QString &langId)
1593 {
1594         return g_lamexp_translation.names.value(langId.toLower(), QString());
1595 }
1596
1597 /*
1598  * Get translation system id
1599  */
1600 unsigned int lamexp_translation_sysid(const QString &langId)
1601 {
1602         return g_lamexp_translation.sysid.value(langId.toLower(), 0);
1603 }
1604
1605 /*
1606  * Get translation script id
1607  */
1608 unsigned int lamexp_translation_country(const QString &langId)
1609 {
1610         return g_lamexp_translation.cntry.value(langId.toLower(), 0);
1611 }
1612
1613 /*
1614  * Install a new translator
1615  */
1616 bool lamexp_install_translator(const QString &langId)
1617 {
1618         bool success = false;
1619
1620         if(langId.isEmpty() || langId.toLower().compare(LAMEXP_DEFAULT_LANGID) == 0)
1621         {
1622                 success = lamexp_install_translator_from_file(QString());
1623         }
1624         else
1625         {
1626                 QString qmFile = g_lamexp_translation.files.value(langId.toLower(), QString());
1627                 if(!qmFile.isEmpty())
1628                 {
1629                         success = lamexp_install_translator_from_file(QString(":/localization/%1").arg(qmFile));
1630                 }
1631                 else
1632                 {
1633                         qWarning("Translation '%s' not available!", langId.toLatin1().constData());
1634                 }
1635         }
1636
1637         return success;
1638 }
1639
1640 /*
1641  * Install a new translator from file
1642  */
1643 bool lamexp_install_translator_from_file(const QString &qmFile)
1644 {
1645         bool success = false;
1646
1647         if(!g_lamexp_currentTranslator)
1648         {
1649                 g_lamexp_currentTranslator = new QTranslator();
1650         }
1651
1652         if(!qmFile.isEmpty())
1653         {
1654                 QString qmPath = QFileInfo(qmFile).canonicalFilePath();
1655                 QApplication::removeTranslator(g_lamexp_currentTranslator);
1656                 success = g_lamexp_currentTranslator->load(qmPath);
1657                 QApplication::installTranslator(g_lamexp_currentTranslator);
1658                 if(!success)
1659                 {
1660                         qWarning("Failed to load translation:\n\"%s\"", qmPath.toLatin1().constData());
1661                 }
1662         }
1663         else
1664         {
1665                 QApplication::removeTranslator(g_lamexp_currentTranslator);
1666                 success = true;
1667         }
1668
1669         return success;
1670 }
1671
1672 /*
1673  * Locate known folder on local system
1674  */
1675 QString lamexp_known_folder(lamexp_known_folder_t folder_id)
1676 {
1677         typedef HRESULT (WINAPI *SHGetKnownFolderPathFun)(__in const GUID &rfid, __in DWORD dwFlags, __in HANDLE hToken, __out PWSTR *ppszPath);
1678         typedef HRESULT (WINAPI *SHGetFolderPathFun)(__in HWND hwndOwner, __in int nFolder, __in HANDLE hToken, __in DWORD dwFlags, __out LPWSTR pszPath);
1679
1680         static const int CSIDL_LOCAL_APPDATA = 0x001c;
1681         static const int CSIDL_PROGRAM_FILES = 0x0026;
1682         static const int CSIDL_SYSTEM_FOLDER = 0x0025;
1683         static const GUID GUID_LOCAL_APPDATA = {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}};
1684         static const GUID GUID_LOCAL_APPDATA_LOW = {0xA520A1A4,0x1780,0x4FF6,{0xBD,0x18,0x16,0x73,0x43,0xC5,0xAF,0x16}};
1685         static const GUID GUID_PROGRAM_FILES = {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}};
1686         static const GUID GUID_SYSTEM_FOLDER = {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}};
1687
1688         static SHGetKnownFolderPathFun SHGetKnownFolderPathPtr = NULL;
1689         static SHGetFolderPathFun SHGetFolderPathPtr = NULL;
1690
1691         if((!SHGetKnownFolderPathPtr) && (!SHGetFolderPathPtr))
1692         {
1693                 QLibrary kernel32Lib("shell32.dll");
1694                 if(kernel32Lib.load())
1695                 {
1696                         SHGetKnownFolderPathPtr = (SHGetKnownFolderPathFun) kernel32Lib.resolve("SHGetKnownFolderPath");
1697                         SHGetFolderPathPtr = (SHGetFolderPathFun) kernel32Lib.resolve("SHGetFolderPathW");
1698                 }
1699         }
1700
1701         int folderCSIDL = -1;
1702         GUID folderGUID = {0x0000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}};
1703
1704         switch(folder_id)
1705         {
1706         case lamexp_folder_localappdata:
1707                 folderCSIDL = CSIDL_LOCAL_APPDATA;
1708                 folderGUID = GUID_LOCAL_APPDATA;
1709                 break;
1710         case lamexp_folder_programfiles:
1711                 folderCSIDL = CSIDL_PROGRAM_FILES;
1712                 folderGUID = GUID_PROGRAM_FILES;
1713                 break;
1714         case lamexp_folder_systemfolder:
1715                 folderCSIDL = CSIDL_SYSTEM_FOLDER;
1716                 folderGUID = GUID_SYSTEM_FOLDER;
1717                 break;
1718         default:
1719                 return QString();
1720                 break;
1721         }
1722
1723         QString folder;
1724
1725         if(SHGetKnownFolderPathPtr)
1726         {
1727                 WCHAR *path = NULL;
1728                 if(SHGetKnownFolderPathPtr(folderGUID, 0x00008000, NULL, &path) == S_OK)
1729                 {
1730                         //MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST);
1731                         QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(path))));
1732                         if(!folderTemp.exists())
1733                         {
1734                                 folderTemp.mkpath(".");
1735                         }
1736                         if(folderTemp.exists())
1737                         {
1738                                 folder = folderTemp.canonicalPath();
1739                         }
1740                         CoTaskMemFree(path);
1741                 }
1742         }
1743         else if(SHGetFolderPathPtr)
1744         {
1745                 WCHAR *path = new WCHAR[4096];
1746                 if(SHGetFolderPathPtr(NULL, folderCSIDL, NULL, NULL, path) == S_OK)
1747                 {
1748                         //MessageBoxW(0, path, L"SHGetFolderPathW", MB_TOPMOST);
1749                         QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(path))));
1750                         if(!folderTemp.exists())
1751                         {
1752                                 folderTemp.mkpath(".");
1753                         }
1754                         if(folderTemp.exists())
1755                         {
1756                                 folder = folderTemp.canonicalPath();
1757                         }
1758                 }
1759                 delete [] path;
1760         }
1761
1762         return folder;
1763 }
1764
1765 /*
1766  * Safely remove a file
1767  */
1768 bool lamexp_remove_file(const QString &filename)
1769 {
1770         if(!QFileInfo(filename).exists() || !QFileInfo(filename).isFile())
1771         {
1772                 return true;
1773         }
1774         else
1775         {
1776                 if(!QFile::remove(filename))
1777                 {
1778                         DWORD attributes = GetFileAttributesW(QWCHAR(filename));
1779                         SetFileAttributesW(QWCHAR(filename), (attributes & (~FILE_ATTRIBUTE_READONLY)));
1780                         if(!QFile::remove(filename))
1781                         {
1782                                 qWarning("Could not delete \"%s\"", filename.toLatin1().constData());
1783                                 return false;
1784                         }
1785                         else
1786                         {
1787                                 return true;
1788                         }
1789                 }
1790                 else
1791                 {
1792                         return true;
1793                 }
1794         }
1795 }
1796
1797 /*
1798  * Check if visual themes are enabled (WinXP and later)
1799  */
1800 bool lamexp_themes_enabled(void)
1801 {
1802         typedef int (WINAPI *IsAppThemedFun)(void);
1803         
1804         static bool isAppThemed = false;
1805         static bool isAppThemed_initialized = false;
1806
1807         if(!isAppThemed_initialized)
1808         {
1809                 IsAppThemedFun IsAppThemedPtr = NULL;
1810                 QLibrary uxTheme(QString("%1/UxTheme.dll").arg(lamexp_known_folder(lamexp_folder_systemfolder)));
1811                 if(uxTheme.load())
1812                 {
1813                         IsAppThemedPtr = (IsAppThemedFun) uxTheme.resolve("IsAppThemed");
1814                 }
1815                 if(IsAppThemedPtr)
1816                 {
1817                         isAppThemed = IsAppThemedPtr();
1818                         if(!isAppThemed)
1819                         {
1820                                 qWarning("Theme support is disabled for this process!");
1821                         }
1822                 }
1823                 isAppThemed_initialized = true;
1824         }
1825
1826         return isAppThemed;
1827 }
1828
1829 /*
1830  * Get number of free bytes on disk
1831  */
1832 unsigned __int64 lamexp_free_diskspace(const QString &path, bool *ok)
1833 {
1834         ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
1835         if(GetDiskFreeSpaceExW(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(path).utf16()), &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes))
1836         {
1837                 if(ok) *ok = true;
1838                 return freeBytesAvailable.QuadPart;
1839         }
1840         else
1841         {
1842                 if(ok) *ok = false;
1843                 return 0;
1844         }
1845 }
1846
1847 /*
1848  * Check if computer does support hibernation
1849  */
1850 bool lamexp_is_hibernation_supported(void)
1851 {
1852         bool hibernationSupported = false;
1853
1854         SYSTEM_POWER_CAPABILITIES pwrCaps;
1855         SecureZeroMemory(&pwrCaps, sizeof(SYSTEM_POWER_CAPABILITIES));
1856         
1857         if(GetPwrCapabilities(&pwrCaps))
1858         {
1859                 hibernationSupported = pwrCaps.SystemS4 && pwrCaps.HiberFilePresent;
1860         }
1861
1862         return hibernationSupported;
1863 }
1864
1865 /*
1866  * Shutdown the computer
1867  */
1868 bool lamexp_shutdown_computer(const QString &message, const unsigned long timeout, const bool forceShutdown, const bool hibernate)
1869 {
1870         HANDLE hToken = NULL;
1871
1872         if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
1873         {
1874                 TOKEN_PRIVILEGES privileges;
1875                 memset(&privileges, 0, sizeof(TOKEN_PRIVILEGES));
1876                 privileges.PrivilegeCount = 1;
1877                 privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1878                 
1879                 if(LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &privileges.Privileges[0].Luid))
1880                 {
1881                         if(AdjustTokenPrivileges(hToken, FALSE, &privileges, NULL, NULL, NULL))
1882                         {
1883                                 if(hibernate)
1884                                 {
1885                                         if(SetSuspendState(TRUE, TRUE, TRUE))
1886                                         {
1887                                                 return true;
1888                                         }
1889                                 }
1890                                 const DWORD reason = SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_FLAG_PLANNED;
1891                                 return InitiateSystemShutdownEx(NULL, const_cast<wchar_t*>(QWCHAR(message)), timeout, forceShutdown ? TRUE : FALSE, FALSE, reason);
1892                         }
1893                 }
1894         }
1895         
1896         return false;
1897 }
1898
1899 /*
1900  * Make a window blink (to draw user's attention)
1901  */
1902 void lamexp_blink_window(QWidget *poWindow, unsigned int count, unsigned int delay)
1903 {
1904         static QMutex blinkMutex;
1905
1906         const double maxOpac = 1.0;
1907         const double minOpac = 0.3;
1908         const double delOpac = 0.1;
1909
1910         if(!blinkMutex.tryLock())
1911         {
1912                 qWarning("Blinking is already in progress, skipping!");
1913                 return;
1914         }
1915         
1916         try
1917         {
1918                 const int steps = static_cast<int>(ceil(maxOpac - minOpac) / delOpac);
1919                 const int sleep = static_cast<int>(floor(static_cast<double>(delay) / static_cast<double>(steps)));
1920                 const double opacity = poWindow->windowOpacity();
1921         
1922                 for(unsigned int i = 0; i < count; i++)
1923                 {
1924                         for(double x = maxOpac; x >= minOpac; x -= delOpac)
1925                         {
1926                                 poWindow->setWindowOpacity(x);
1927                                 QApplication::processEvents();
1928                                 Sleep(sleep);
1929                         }
1930
1931                         for(double x = minOpac; x <= maxOpac; x += delOpac)
1932                         {
1933                                 poWindow->setWindowOpacity(x);
1934                                 QApplication::processEvents();
1935                                 Sleep(sleep);
1936                         }
1937                 }
1938
1939                 poWindow->setWindowOpacity(opacity);
1940                 QApplication::processEvents();
1941                 blinkMutex.unlock();
1942         }
1943         catch (...)
1944         {
1945                 blinkMutex.unlock();
1946                 qWarning("Exception error while blinking!");
1947         }
1948 }
1949
1950 /*
1951  * Remove forbidden characters from a filename
1952  */
1953 const QString lamexp_clean_filename(const QString &str)
1954 {
1955         QString newStr(str);
1956         
1957         newStr.replace("\\", "-");
1958         newStr.replace(" / ", ", ");
1959         newStr.replace("/", ",");
1960         newStr.replace(":", "-");
1961         newStr.replace("*", "x");
1962         newStr.replace("?", "");
1963         newStr.replace("<", "[");
1964         newStr.replace(">", "]");
1965         newStr.replace("|", "!");
1966         
1967         return newStr.simplified();
1968 }
1969
1970 /*
1971  * Remove forbidden characters from a file path
1972  */
1973 const QString lamexp_clean_filepath(const QString &str)
1974 {
1975         QStringList parts = QString(str).replace("\\", "/").split("/");
1976
1977         for(int i = 0; i < parts.count(); i++)
1978         {
1979                 parts[i] = lamexp_clean_filename(parts[i]);
1980         }
1981
1982         return parts.join("/");
1983 }
1984
1985 /*
1986  * Get a list of all available Qt Text Codecs
1987  */
1988 QStringList lamexp_available_codepages(bool noAliases)
1989 {
1990         QStringList codecList;
1991         
1992         QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
1993         while(!availableCodecs.isEmpty())
1994         {
1995                 QByteArray current = availableCodecs.takeFirst();
1996                 if(!(current.startsWith("system") || current.startsWith("System")))
1997                 {
1998                         codecList << QString::fromLatin1(current.constData(), current.size());
1999                         if(noAliases)
2000                         {
2001                                 if(QTextCodec *currentCodec = QTextCodec::codecForName(current.constData()))
2002                                 {
2003                                         
2004                                         QList<QByteArray> aliases = currentCodec->aliases();
2005                                         while(!aliases.isEmpty()) availableCodecs.removeAll(aliases.takeFirst());
2006                                 }
2007                         }
2008                 }
2009         }
2010
2011         return codecList;
2012 }
2013
2014 /*
2015  * Finalization function (final clean-up)
2016  */
2017 void lamexp_finalization(void)
2018 {
2019         qDebug("lamexp_finalization()");
2020         
2021         //Free all tools
2022         if(!g_lamexp_tool_registry.isEmpty())
2023         {
2024                 QStringList keys = g_lamexp_tool_registry.keys();
2025                 for(int i = 0; i < keys.count(); i++)
2026                 {
2027                         LAMEXP_DELETE(g_lamexp_tool_registry[keys.at(i)]);
2028                 }
2029                 g_lamexp_tool_registry.clear();
2030                 g_lamexp_tool_versions.clear();
2031         }
2032         
2033         //Delete temporary files
2034         if(!g_lamexp_temp_folder.isEmpty())
2035         {
2036                 for(int i = 0; i < 100; i++)
2037                 {
2038                         if(lamexp_clean_folder(g_lamexp_temp_folder))
2039                         {
2040                                 break;
2041                         }
2042                         Sleep(125);
2043                 }
2044                 g_lamexp_temp_folder.clear();
2045         }
2046
2047         //Clear languages
2048         if(g_lamexp_currentTranslator)
2049         {
2050                 QApplication::removeTranslator(g_lamexp_currentTranslator);
2051                 LAMEXP_DELETE(g_lamexp_currentTranslator);
2052         }
2053         g_lamexp_translation.files.clear();
2054         g_lamexp_translation.names.clear();
2055
2056         //Destroy Qt application object
2057         QApplication *application = dynamic_cast<QApplication*>(QApplication::instance());
2058         LAMEXP_DELETE(application);
2059
2060         //Detach from shared memory
2061         if(g_lamexp_ipc_ptr.sharedmem) g_lamexp_ipc_ptr.sharedmem->detach();
2062         LAMEXP_DELETE(g_lamexp_ipc_ptr.sharedmem);
2063         LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read);
2064         LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write);
2065         LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_read_mutex);
2066         LAMEXP_DELETE(g_lamexp_ipc_ptr.semaphore_write_mutex);
2067
2068         //Free STDOUT and STDERR buffers
2069         if(g_lamexp_console_attached)
2070         {
2071                 if(std::filebuf *tmp = dynamic_cast<std::filebuf*>(std::cout.rdbuf()))
2072                 {
2073                         std::cout.rdbuf(NULL);
2074                         LAMEXP_DELETE(tmp);
2075                 }
2076                 if(std::filebuf *tmp = dynamic_cast<std::filebuf*>(std::cerr.rdbuf()))
2077                 {
2078                         std::cerr.rdbuf(NULL);
2079                         LAMEXP_DELETE(tmp);
2080                 }
2081         }
2082
2083         //Close log file
2084         if(g_lamexp_log_file)
2085         {
2086                 fclose(g_lamexp_log_file);
2087                 g_lamexp_log_file = NULL;
2088         }
2089 }
2090
2091 /*
2092  * Initialize debug thread
2093  */
2094 static const HANDLE g_debug_thread = LAMEXP_DEBUG ? NULL : lamexp_debug_thread_init();
2095
2096 /*
2097  * Get number private bytes [debug only]
2098  */
2099 SIZE_T lamexp_dbg_private_bytes(void)
2100 {
2101 #if LAMEXP_DEBUG
2102         PROCESS_MEMORY_COUNTERS_EX memoryCounters;
2103         memoryCounters.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
2104         GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS) &memoryCounters, sizeof(PROCESS_MEMORY_COUNTERS_EX));
2105         return memoryCounters.PrivateUsage;
2106 #else
2107         throw "Cannot call this function in a non-debug build!";
2108 #endif //LAMEXP_DEBUG
2109 }