OSDN Git Service

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