1 /*
2        _________ __                 __
3       /   _____//  |_____________ _/  |______     ____  __ __  ______
4       \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
5       /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
6      /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
7              \/                  \/          \//_____/            \/
8   ______________________                           ______________________
9                         T H E   W A R   B E G I N S
10          Stratagus - A free fantasy real time strategy game engine
11 
12 stratagus-game-launcher.h - Stratagus Game Launcher
13     Copyright (C) 2010-2011  Pali Rohár <pali.rohar@gmail.com>
14 
15     This program is free software: you can redistribute it and/or modify
16     it under the terms of the GNU General Public License as published by
17     the Free Software Foundation, either version 2 of the License, or
18     (at your option) any later version.
19 
20     This program is distributed in the hope that it will be useful,
21     but WITHOUT ANY WARRANTY; without even the implied warranty of
22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     GNU General Public License for more details.
24 
25     You should have received a copy of the GNU General Public License
26     along with this program.  If not, see <http://www.gnu.org/licenses/>.
27 */
28 
29 /**
30  * @page GameLauncher Stratagus Game Launcher
31  *
32  * Stratagus Game Launcher is C code for generating launcher for any Stratagus game.
33  * Game launcher for concrete game check if game data exists in default Stratagus
34  * location and spawn Stratagus process with correct game data location. If does not
35  * exist it show GUI or console error message.
36  *
37  * Before including this header, you need to define:
38  *
39  * ::GAME_NAME
40  *
41  * ::GAME_CD
42  *
43  * ::GAME_CD_FILE_PATTERNS
44  *
45  * ::GAME
46  *
47  * ::EXTRACTOR_TOOL
48  *
49  * ::EXTRACTOR_ARGS
50  *
51  * On Non Windows system you need to specify also paths:
52  *
53  * ::DATA_PATH
54  *
55  * ::SCRIPTS_PATH
56  *
57  * ::STRATAGUS_BIN
58  *
59  * On Windows paths are reading from InstallLocation key in Uninstall section:
60  * Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Stratagus
61  *
62  * Example use of code:
63  *
64  * @code
65  *
66  * #define GAME_NAME "My Game Name"
67  * #define GAME_CD "Original Game CD Name"
68  * #define GAME "my_game"
69  * #define GAME_CD_FILE_PATTERNS "*.WAR", "*.war"
70  * #define EXTRACTOR_TOOL "gametool"
71  * #define EXTRACTOR_ARGS {"-v", NULL}
72  *
73  * #ifndef WIN32
74  * #define DATA_PATH "/usr/share/games/stratagus/my_game"
75  * #define SCRIPTS_PATH "/usr/share/games/stratagus/my_game"
76  * #define STRATAGUS_BIN "/usr/games/stratagus"
77  * #endif
78  *
79  * #include <stratagus-game-launcher.h>
80  *
81  * @endcode
82  **/
83 
84 /**
85  * \def GAME_NAME
86  * Full name of your Game
87  **/
88 
89 /**
90  * \def GAME_CD
91  * Full name of data CD
92  **/
93 
94 /**
95  * \def GAME_CD_FILE_PATTERNS
96  * Comma-separated file patterns for the extraction wizard to help users select
97  * the right folder.
98  **/
99 
100 /**
101  * \def GAME
102  * Short name of game (lower ascii chars without space)
103  **/
104 
105 /**
106  * \def EXTRACTOR_TOOL
107  * The name of the game data extractor tool. This code will append the
108  * arguments, src, and destionation directories.
109  **/
110 
111 /**
112  * \def EXTRACTOR_ARGS
113  * The default arguments of the game data extractor tool.
114  **/
115 
116 /**
117  * \def DATA_PATH
118  * Path to game data directory
119  **/
120 
121 /**
122  * \def SCRIPTS_PATH
123  * Path to game scripts directory
124  **/
125 
126 /**
127  * \def STRATAGUS_BIN
128  * Path to stratagus executable binary
129  **/
130 
131 #ifndef STRATAGUS_GAME_LAUNCHER_H
132 #define STRATAGUS_GAME_LAUNCHER_H
133 
134 /* Fake definitions for Doxygen */
135 #include <sys/types.h>
136 #ifdef DOXYGEN
137 #define GAME_NAME
138 #define GAME_CD
139 #define GAME
140 #define DATA_PATH
141 #define SCRIPTS_PATH
142 #define STRATAGUS_BIN
143 #endif
144 
145 #if ! defined (GAME_NAME) || ! defined (GAME_CD) || ! defined (GAME) || ! defined(EXTRACTOR_TOOL)
146 #error You need to define all Game macros, see stratagus-game-launcher.h
147 #endif
148 
149 #ifndef GAME_SHOULD_EXTRACT_AGAIN
150 #define GAME_SHOULD_EXTRACT_AGAIN false
151 #endif
152 
153 /**
154  * \def TITLE_PNG
155  * OPTIONAL: Path to title screen (for testing if data was extracted)
156  **/
157 #ifndef TITLE_PNG
158 #ifdef WIN32
159 #define TITLE_PNG "%s\\graphics\\ui\\title.png"
160 #else
161 #define TITLE_PNG "%s/graphics/ui/title.png"
162 #endif
163 #endif
164 
165 #ifndef WIN32
166 #if ! defined (DATA_PATH) || ! defined (SCRIPTS_PATH) || ! defined (STRATAGUS_BIN)
167 #error You need to define paths, see stratagus-game-launcher.h
168 #endif
169 #pragma GCC diagnostic ignored "-Wwrite-strings"
170 #endif
171 
172 #ifdef _MSC_VER
173 #pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
174 #endif
175 
176 #ifdef _WIN64
177 #define REGKEY "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Stratagus (64 bit)"
178 #elif defined (WIN32)
179 #define REGKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Stratagus"
180 #endif
181 
182 #define TITLE GAME_NAME
183 #define EXTRACTOR_NOT_FOUND GAME_NAME " could not find its extraction tool.\n" EXTRACTOR_TOOL "!\n"
184 #define STRATAGUS_NOT_FOUND "Stratagus is not installed.\nYou need Stratagus to run " GAME_NAME "!\n"
185 #define DATA_NOT_EXTRACTED GAME_NAME " data was not extracted, is corrupted, or outdated.\nYou need to extract it from original " GAME_CD "."
186 
187 #include "stratagus-gameutils.h"
188 
SetUserDataPath(char * data_path)189 static void SetUserDataPath(char* data_path) {
190 #if defined(WIN32)
191 	char marker[MAX_PATH] = {'\0'};
192 	if (PathCombine(marker, data_path, "portable-install")) {
193 		if (PathFileExists(marker)) {
194 			return;
195 		}
196 	}
197 	SHGetFolderPathA(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, 0, data_path);
198 	// strcpy(data_path, getenv("APPDATA"));
199 #else
200 	strcpy(data_path, getenv("HOME"));
201 #endif
202 	int datalen = strlen(data_path);
203 #if defined(WIN32)
204 	strcat(data_path, "\\Stratagus\\");
205 #elif defined(USE_MAC)
206 	strcat(data_path, "/Library/Stratagus/");
207 #else
208 	strcat(data_path, "/.stratagus/");
209 #endif
210 	strcat(data_path, "data." GAME_NAME);
211 }
212 
check_version(char * tool_path,char * data_path)213 int check_version(char* tool_path, char* data_path) {
214     char buf[4096] = {'\0'};
215     sprintf(buf, "%s/extracted" , data_path);
216     FILE *f = fopen(buf, "r");
217     char dataversion[20] = {'\0'};
218     char toolversion[20] = {'\0'};
219     if (f) {
220 		fgets(dataversion, 20, f);
221 		fclose(f);
222     } else {
223 #ifdef CHECK_EXTRACTED_VERSION
224 		return 0; // No file means we have a problem
225 #else
226 		return 1; // No file means we don't care
227 #endif
228 	}
229 #ifndef WIN32
230 	sprintf(buf, "%s -V", tool_path);
231     FILE *pipe = popen(buf, "r");
232     if (f) {
233 		fgets(toolversion, 20, pipe);
234 		pclose(pipe);
235     }
236 #else
237 	sprintf(buf, "%s -V", tool_path); // tool_path is already quoted
238 	HANDLE g_hChildStd_OUT_Rd = NULL;
239 	HANDLE g_hChildStd_OUT_Wr = NULL;
240 	DWORD nbByteRead;
241 	SECURITY_ATTRIBUTES saAttr;
242 	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
243 	saAttr.bInheritHandle = TRUE;
244 	saAttr.lpSecurityDescriptor = NULL;
245 	if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
246 		return 1;
247 	if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
248 		return 1;
249 	PROCESS_INFORMATION piProcInfo;
250 	STARTUPINFO siStartInfo;
251 	ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
252 	ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
253 	siStartInfo.cb = sizeof(STARTUPINFO);
254 	siStartInfo.hStdError = g_hChildStd_OUT_Wr;
255 	siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
256 	siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
257 	if (!CreateProcess(NULL, buf, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
258 		return 1;
259 	CloseHandle(piProcInfo.hProcess);
260 	CloseHandle(piProcInfo.hThread);
261 	ReadFile(g_hChildStd_OUT_Rd, toolversion, 20, &nbByteRead, NULL);
262 #endif
263     // strip whitespace
264     for (size_t i=0, j=0; toolversion[j]=toolversion[i]; j+=!isspace(toolversion[i++]));
265     for (size_t i=0, j=0; dataversion[j]=dataversion[i]; j+=!isspace(dataversion[i++]));
266 	if (strcmp(dataversion, toolversion) == 0) {
267 		return 1;
268 	}
269     return 0;
270 }
271 
272 static void ExtractData(char* extractor_tool, char *const extractor_args[], char* destination, char* scripts_path, int force=0, char* datafileCstr=NULL) {
273 	int canJustReextract;
274 #ifdef EXTRACTION_FILES
275 	if (force == 0) {
276 		canJustReextract = 1;
277 		char* extraction_files[] = { EXTRACTION_FILES, NULL };
278 		char* efile = extraction_files[0];
279 		for (int i = 0; efile != NULL; i++) {
280 			fs::path efile_path = fs::path(destination) / efile;
281 			if (!fs::exists(efile_path)) {
282 				// file to extract not found
283 				canJustReextract = 0;
284 			}
285 			efile = extraction_files[i + 1];
286 		}
287 	} else {
288 		canJustReextract = 0;
289 	}
290 #else
291 	canJustReextract = 0;
292 #endif
293 	if (canJustReextract) {
294 		tinyfd_messageBox("", GAME " game data format changed, we can migrate in-place. Please be patient.", "ok", "info", 1);
295 	} else if (force == 0) {
296 		tinyfd_messageBox("Missing data",
297 						  DATA_NOT_EXTRACTED " Please select the " GAME_CD, "ok", "question", 1);
298 	} else if (force == 1) {
299 		tinyfd_messageBox("", "Please select the " GAME_CD, "ok", "question", 1);
300 	} else if (force == 2) {
301 		// pass
302 	}
303 #ifdef USE_MAC
304 	int patterncount = 0;
305 	char* filepatterns[] = { NULL };
306 	// file types as names not working at least on macOS sierra
307 #else
308 	char* filepatterns[] = { GAME_CD_FILE_PATTERNS, NULL };
309 	int patterncount = 0;
310 	while (filepatterns[patterncount++] != NULL);
311 #endif
312 	fs::path srcfolder;
313 	if (!canJustReextract || datafileCstr != NULL) {
314 		if (datafileCstr == NULL) {
315 			datafileCstr = tinyfd_openFileDialog(GAME_CD " location", "",
316 													 patterncount - 1, filepatterns, NULL, 0);
317 		}
318 		if (datafileCstr == NULL) {
319 			exit(-1);
320 		}
321 		std::string datafile = datafileCstr;
322 		if (datafile.compare(datafile.length() - 4, 4, ".exe") == 0) {
323 			// test if this is an innoextract installer and if so, extract it to a tempdir and pass that
324 #ifdef WIN32
325 			char moduleFileName[BUFF_SIZE];
326 			memset(moduleFileName, 0, sizeof(moduleFileName));
327 			GetModuleFileName(NULL, moduleFileName, sizeof(moduleFileName)-1);
328 			fs::path innoextractPath = fs::path(moduleFileName).parent_path() / "innoextract.exe";
329 			std::wstring file = innoextractPath.wstring();
330 			std::vector<std::wstring> argv = {L"-i", fs::path(datafile).wstring()};
331 #else
332 			const char *file = "innoextract";
333 			char *argv[] = {"-i", (char*)datafile.c_str(), NULL};
334 #endif
335 			if (runCommand(file, argv) == 0) {
336 				// innoextract exists and this exe file is an innosetup file
337 				bool success = false;
338 				fs::path tmpp = fs::temp_directory_path() / GAME;
339 				fs::create_directories(tmpp);
340 #ifdef WIN32
341 				wchar_t *curdir = _wgetcwd(NULL, 0);
342 #else
343 				char *curdir = getcwd(NULL, 0);
344 #endif
345 				if (curdir != NULL) {
346 #ifdef WIN32
347 					if (_wchdir(tmpp.wstring().c_str()) == 0) {
348 #else
349 					if (chdir(tmpp.string().c_str()) == 0) {
350 #endif
351 #ifdef WIN32
352 						argv[0] = L"-m";
353 #else
354 						argv[0] = "-m";
355 #endif
356 						success = runCommand(file, argv) == 0;
357 #ifdef WIN32
358 						_wchdir(curdir);
359 #else
360 						chdir(curdir);
361 #endif
362 					}
363 					free(curdir);
364 				}
365 				if (!success) {
366 					error("Problem with installer",
367 							"You selected an innosetup installer, and we could not extract it. "
368 							"Please extract it manually and point the extraction tool there.");
369 				} else {
370 					srcfolder = tmpp;
371 				}
372 			} else {
373 				// we cannot test if this is an innoextract installer, assume not but maybe warn
374 				if (datafile.compare("INSTALL.EXE") == 0 ||
375 					datafile.compare("install.exe") == 0 ||
376 					datafile.compare("INSTALL.exe") == 0 ||
377 					datafile.compare("SETUP.EXE") == 0 ||
378 					datafile.compare("setup.exe") == 0 ||
379 					datafile.compare("SETUP.exe") == 0) {
380 					// probably not a packaged installer
381 				} else {
382 					// warn
383 					tinyfd_messageBox("", "You selected an exe file, but I cannot run innoextract "
384 							"to check if its a single-file installer. If it is, please extract/install "
385 							"manually first and then run " GAME " again.", "ok", "question", 1);
386 				}
387 				srcfolder = fs::path(datafile).parent_path();
388 			}
389 		} else {
390 			srcfolder = fs::path(datafile).parent_path();
391 		}
392 	} else {
393 		srcfolder = fs::path(destination);
394 	}
395 
396 #ifdef WIN32
397 	char* sourcepath = _strdup(scripts_path);
398 	if (sourcepath[0] == '"') {
399 		// if scripts_path is quoted, remove the quotes, i.e.,
400 		// copy all but the first until all but the last char.
401 		// sourcepath is already large enough because it used to contain the
402 		// entire scripts_path
403 		strncpy(sourcepath, scripts_path + 1, strlen(scripts_path) - 2);
404 		sourcepath[strlen(scripts_path) - 2] = '\0';
405 	}
406 #else
407 	char* sourcepath = strdup(scripts_path);
408 #endif
409 
410 	fs::create_directories(fs::path(destination));
411 
412 	if (!fs::exists(sourcepath)) {
413 		// deployment time path not found, try compile time path
414 		strcpy(sourcepath, fs::path(SRC_PATH()).parent_path().string().c_str());
415 	}
416 
417 #ifndef WIN32
418 	if (!fs::exists(sourcepath)) {
419 		// deployment time path might be same as extractor
420 		strcpy(sourcepath, fs::path(extractor_tool).parent_path().string().c_str());
421 	}
422 #endif
423 
424 	if (!fs::exists(sourcepath)) {
425 		// scripts not found, abort!
426 		std::string msg("There was an error copying the data, could not discover scripts path: ");
427 		msg += sourcepath;
428 		tinyfd_messageBox("Error", msg.c_str(), "ok", "error", 1);
429 		return;
430 	}
431 
432 	if (force != 2) {
433 		fs::path contrib_src_path;
434 		fs::path contrib_dest_path(destination);
435 		int i = 0;
436 		int optional = 0;
437 		char* contrib_directories[] = CONTRIB_DIRECTORIES;
438 		while (contrib_directories[i] != NULL && contrib_directories[i + 1] != NULL) {
439 			if (!strcmp(contrib_directories[i], ":optional:")) {
440 				i += 1;
441 				optional = 1;
442 			} else {
443 				if (contrib_directories[i][0] != '/') {
444 					// absolute Unix paths are not appended to the source path
445 					contrib_src_path = fs::path(sourcepath);
446 					contrib_src_path /= contrib_directories[i];
447 				} else {
448 					contrib_src_path = fs::path(contrib_directories[i]);
449 				}
450 
451 				if (!fs::exists(contrib_src_path)) {
452 					// contrib dir not found, abort!
453 					if (!optional) {
454 						std::string msg("There was an error copying the data, could not discover contributed directory path: ");
455 						msg += contrib_src_path.string();
456 						error("Error", msg.c_str());
457 						return;
458 					}
459 				} else {
460 					copy_dir(contrib_src_path, contrib_dest_path / contrib_directories[i + 1]);
461 				}
462 				i += 2;
463 			}
464 		}
465 	}
466 
467 	int exitcode = 0;
468 
469  	char cmdbuf[4096] = {'\0'};
470 #ifdef WIN32
471 	std::wstring file;
472 	std::vector<std::wstring> args;
473 
474 	file = fs::path(extractor_tool).wstring();
475 	for (int i = 0; ; i++) {
476 		const char *earg = extractor_args[i];
477 		if (earg == NULL) {
478 			break;
479 		} else {
480 			const size_t WCHARBUF = 100;
481 			wchar_t  wszDest[WCHARBUF];
482 			MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, earg, -1, wszDest, WCHARBUF);
483 			args.push_back(wszDest);
484 		}
485 	}
486 	args.push_back(srcfolder.wstring());
487 	args.push_back(fs::path(destination).wstring());
488 	std::wstring combinedCommandline;
489 	exitcode = runCommand(file, args, true, &combinedCommandline);
490 #else
491 
492 #ifdef USE_MAC
493 	strcat(cmdbuf, "osascript -e \"tell application \\\"Terminal\\\"\n"
494                        "    set w to do script \\\"");
495 #else
496 	if (!isatty(1)) {
497 		strcat(cmdbuf, "xterm -e bash -c ");
498 		strcat(cmdbuf, " \"");
499 	}
500 #endif
501 
502 	strcat(cmdbuf, extractor_tool);
503 	for (int i = 0; ; i++) {
504 		const char *earg = extractor_args[i];
505 		if (earg == NULL) {
506 			break;
507 		} else {
508 			strcat(cmdbuf, " ");
509 			strcat(cmdbuf, earg);
510 		}
511 	}
512 	strcat(cmdbuf, " " QUOTE);
513 	strcat(cmdbuf, srcfolder.string().c_str());
514 	strcat(cmdbuf, QUOTE " " QUOTE);
515 	strcat(cmdbuf, destination);
516 	strcat(cmdbuf, QUOTE);
517 
518 #ifdef USE_MAC
519 	strcat(cmdbuf, "; exit\\\"\n"
520                        "    repeat\n"
521                        "        delay 1\n"
522                        "        if not busy of w then exit repeat\n"
523                        "    end repeat\n"
524                        "end tell\"");
525 #else
526 	if (!isatty(1)) {
527 	    strcat(cmdbuf, "; echo 'Press RETURN to continue...'; read\"");
528 	}
529 #endif
530 
531 	printf("Running extractor as %s\n", cmdbuf);
532 	exitcode = system(cmdbuf);
533 #endif
534 
535 	if (exitcode != 0) {
536 #ifdef WIN32
537 		WideCharToMultiByte(CP_ACP, 0, combinedCommandline.c_str(), combinedCommandline.size(), cmdbuf, sizeof(cmdbuf) - 1, NULL, NULL);
538 #endif
539 		char* extractortext = (char*)calloc(sizeof(char), strlen(cmdbuf) + 1024);
540 		sprintf(extractortext, "The following command was used to extract the data (you can run it manually in a console to find out more):\n%s", cmdbuf);
541 		tinyfd_messageBox("Extraction failed!", extractortext, "ok", "error", 1);
542 	} else if (!canJustReextract && GAME_SHOULD_EXTRACT_AGAIN) {
543 		ExtractData(extractor_tool, extractor_args, destination, scripts_path, 2);
544 	}
545 }
546 
main(int argc,char * argv[])547 int main(int argc, char * argv[]) {
548 	struct stat st;
549 	int argccpy = argc;
550 	char data_path[BUFF_SIZE];
551 	char scripts_path[BUFF_SIZE];
552 	char stratagus_bin[BUFF_SIZE];
553 	char title_path[BUFF_SIZE];
554 	char extractor_path[BUFF_SIZE] = {'\0'};
555 
556 	// Try the extractor from the same dir as we are
557 	if (strchr(argv[0], SLASH[0])) {
558 		strcpy(extractor_path, argv[0]);
559 		parentdir(extractor_path);
560 		strcat(extractor_path, SLASH EXTRACTOR_TOOL);
561 #ifdef WIN32
562 		if (!strstr(extractor_path, ".exe")) {
563 			strcat(extractor_path, ".exe");
564 		}
565 #endif
566 		if (stat(extractor_path, &st) == 0) {
567 #ifndef WIN32
568 			// Once we have the path, we quote it by moving the memory one byte to the
569 			// right, and surrounding it with the quote character and finishing null
570 			// bytes. Then we add the arguments.
571 			extractor_path[strlen(extractor_path) + 1] = '\0';
572 			memmove(extractor_path + 1, extractor_path, strlen(extractor_path));
573 			extractor_path[0] = QUOTE[0];
574 			extractor_path[strlen(extractor_path) + 1] = '\0';
575 			extractor_path[strlen(extractor_path)] = QUOTE[0];
576 #endif
577 		} else {
578 			extractor_path[0] = '\0';
579 		}
580 	}
581 	if (extractor_path[0] == '\0') {
582 		// Use extractor from PATH
583 		strcpy(extractor_path, EXTRACTOR_TOOL);
584 		if (!detectPresence(extractor_path)) {
585 			char msg[BUFF_SIZE * 2] = {'\0'};;
586 			strcpy(msg, EXTRACTOR_NOT_FOUND);
587 			strcat(msg, " (expected at ");
588 			strcat(msg, extractor_path);
589 			strcat(msg, ")");
590 			error(TITLE, msg);
591 		}
592 	}
593 
594 #ifdef WIN32
595 	char executable_path[BUFF_SIZE];
596 	memset(executable_path, 0, sizeof(executable_path));
597 	GetModuleFileName(NULL, executable_path, sizeof(executable_path)-1);
598 
599 	char executable_drive[_MAX_DRIVE];
600 	char executable_dir[_MAX_DIR];
601 	memset(executable_drive, 0, sizeof(executable_drive));
602 	memset(executable_dir, 0, sizeof(executable_dir));
603 	_splitpath(executable_path, executable_drive, executable_dir, NULL, NULL);
604 
605 	size_t data_path_size = sizeof(data_path);
606 	memset(data_path, 0, data_path_size);
607 
608 	if (executable_path[0] && executable_drive[0] && executable_dir[0]) {
609 		PathCombine(data_path, executable_drive, executable_dir);
610 	} else {
611 		_getcwd(data_path, data_path_size);
612 	}
613 	PathRemoveBackslash(data_path);
614 	sprintf(scripts_path, "\"%s\"", data_path);
615 
616 	char stratagus_path[BUFF_SIZE];
617 
618 	// Try to use stratagus.exe from data (install) directory first
619 	sprintf(stratagus_bin, "%s\\stratagus.exe", data_path);
620 	if (stat(stratagus_bin, &st) != 0) {
621 		// If no local stratagus.exe is present, search PATH
622 		if (!SearchPath(NULL, "stratagus", ".exe", MAX_PATH, stratagus_bin, NULL) &&
623 			!SearchPath(NULL, "stratagus-dbg", ".exe", MAX_PATH, stratagus_bin, NULL)) {
624 			// If no local or PATH stratagus.exe is present, look for a globally installed version
625 			DWORD stratagus_path_size = sizeof(stratagus_path);
626 			memset(stratagus_path, 0, stratagus_path_size);
627 			HKEY key;
628 
629 			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
630 				if (RegQueryValueEx(key, "InstallLocation", NULL, NULL, (LPBYTE)stratagus_path, &stratagus_path_size) == ERROR_SUCCESS) {
631 					if (stratagus_path_size == 0 || strlen(stratagus_path) == 0) {
632 						char msg[BUFF_SIZE * 2] = {'\0'};
633 						strcat(msg, STRATAGUS_NOT_FOUND);
634 						strcat(msg, " (expected globally installed or in ");
635 						strcat(msg, stratagus_bin);
636 						strcat(msg, ")");
637 						error(TITLE, msg);
638 					}
639 				}
640 				RegCloseKey(key);
641 			}
642 
643 			if (_chdir(stratagus_path) != 0) {
644 				char msg[BUFF_SIZE * 2] = {'\0'};
645 				strcat(msg, STRATAGUS_NOT_FOUND);
646 				strcat(msg, " (registry key found, but directory ");
647 				strcat(msg, stratagus_path);
648 				strcat(msg, " cannot be opened)");
649 				error(TITLE, msg);
650 			}
651 			sprintf(stratagus_bin, "%s\\stratagus.exe", stratagus_path);
652 		}
653 	}
654 
655 #ifdef DATA_PATH
656 	// usually this isn't defined for windows builds. if it is, use it
657 	strcpy(data_path, DATA_PATH);
658 #endif
659 #else
660 	strcpy(data_path, DATA_PATH);
661 	strcpy(scripts_path, SCRIPTS_PATH);
662 	strcpy(stratagus_bin, STRATAGUS_BIN);
663 #endif
664 
665 	char *const extractor_args[] = EXTRACTOR_ARGS;
666 
667 	if (argc == 2) {
668 		if (stat(argv[1], &st) == 0) {
669 			// extraction file given as argument and it is accessible => force extraction and exit
670 			tinyfd_forceConsole = 1;
671 			SetUserDataPath(data_path);
672 			ExtractData(extractor_path, extractor_args, data_path, scripts_path, 2, argv[1]);
673 			return 0;
674 		} else if (!strcmp(argv[1], "--extract")) {
675 			// Force extraction and exit
676 			SetUserDataPath(data_path);
677 			ExtractData(extractor_path, extractor_args, data_path, scripts_path, 1);
678 			return 0;
679 		} else if (!strcmp(argv[1], "--extract-no-gui")) {
680 			// Force extraction without ui and exit
681 			tinyfd_forceConsole = 1;
682 			SetUserDataPath(data_path);
683 			ExtractData(extractor_path, extractor_args, data_path, scripts_path, 1);
684 			return 0;
685 		} else if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
686                     printf("Usage: %s [path to extraction file|--extract|--extract-no-gui]\n"
687                            "\tpath to extraction file - will be used as file to start the extraction process on\n"
688                            "\t--extract - force extraction even if data is already extracted\n"
689                            "\t--extract-no-gui - force extraction even if data is already extracted, using the console only for prompts\n\n",
690                            argv[0]);
691                 }
692 	}
693 
694 	if ( stat(stratagus_bin, &st) != 0 ) {
695 #ifdef WIN32
696 		_fullpath(stratagus_bin, argv[0], BUFF_SIZE);
697 		PathRemoveFileSpec(stratagus_bin);
698 		strcat(extractor_path, "\\stratagus.exe");
699 		if (stat(stratagus_bin, &st) != 0) {
700 			char msg[BUFF_SIZE * 2] = {'\0'};
701 			strcat(msg, STRATAGUS_NOT_FOUND);
702 			strcat(msg, " (expected in ");
703 			strcat(msg, stratagus_bin);
704 			strcat(msg, ")");
705 			error(TITLE, msg);
706 		}
707 #else
708 		if (!detectPresence(stratagus_bin)) {
709 			realpath(argv[0], stratagus_bin);
710 			parentdir(stratagus_bin);
711 			if (strlen(stratagus_bin) > 0) {
712 				strcat(stratagus_bin, "/stratagus");
713 			} else {
714 				strcat(stratagus_bin, "./stratagus");
715 			}
716 			if ( stat(stratagus_bin, &st) != 0 ) {
717 				char msg[BUFF_SIZE * 2] = {'\0'};
718 				strcat(msg, STRATAGUS_NOT_FOUND);
719 				strcat(msg, " (expected in ");
720 				strcat(msg, stratagus_bin);
721 				strcat(msg, ")");
722 				error(TITLE, msg);
723 			}
724 		}
725 #endif
726 	}
727 
728 	sprintf(title_path, TITLE_PNG, data_path);
729 	if ( stat(title_path, &st) != 0 ) {
730 		SetUserDataPath(data_path);
731 		sprintf(title_path, TITLE_PNG, data_path);
732 		if ( stat(title_path, &st) != 0 ) {
733 			ExtractData(extractor_path, extractor_args, data_path, scripts_path);
734 		}
735 		if ( stat(title_path, &st) != 0 ) {
736 			std::string msg(DATA_NOT_EXTRACTED);
737 			msg += " (extraction was attempted, but it seems an error occurred)";
738 			error(TITLE, msg.c_str());
739 		}
740 	}
741 
742 	if (!check_version(extractor_path, data_path)) {
743 		ExtractData(extractor_path, extractor_args, data_path, scripts_path);
744 	}
745 
746 #ifdef WIN32
747 	int data_path_len = strlen(data_path);
748 	_chdir(data_path);
749 
750 	for (int i = data_path_len - 1; i >= 0; --i) {
751 		data_path[i + 1] = data_path[i];
752 	}
753 	data_path[0] = '"';
754 	data_path[data_path_len + 1] = '"';
755 	data_path[data_path_len + 2] = 0;
756 #endif
757 
758 #ifdef _MSC_VER
759 	char** stratagus_argv;
760 	stratagus_argv = (char**) malloc((argc + 3) * sizeof (*stratagus_argv));
761 #else
762 	char * stratagus_argv[argc + 3];
763 #endif
764 
765 #ifdef WIN32
766 	char stratagus_argv0_esc[BUFF_SIZE];
767 	memset(stratagus_argv0_esc, 0, sizeof(stratagus_argv0_esc));
768 	strcpy(stratagus_argv0_esc + 1, argv[0]);
769 	stratagus_argv0_esc[0] = '"';
770 	stratagus_argv0_esc[strlen(argv[0]) + 1] = '"';
771 	stratagus_argv0_esc[strlen(argv[0]) + 2] = 0;
772 	stratagus_argv[0] = stratagus_argv0_esc;
773 #else
774 	stratagus_argv[0] = argv[0];
775 #endif
776 
777 	stratagus_argv[1] = "-d";
778 	stratagus_argv[2] = data_path;
779 
780 	for (int i = 3; i < argc + 2; ++i ) {
781 		stratagus_argv[i] = argv[i - 2];
782 	}
783 	stratagus_argv[argc + 2] = NULL;
784 
785 	// Needed to reduce CPU load while idle threads are wating for havn't finished yet ones
786 	extern char** environ;
787     int i = 0;
788     while(environ[i]) { i++; }
789     environ[i] = (char*)"OMP_WAIT_POLICY=passive";
790     environ[i + 1] = NULL;
791 #ifdef WIN32
792 	int ret = _spawnvpe(_P_WAIT, stratagus_bin, stratagus_argv, environ);
793 #else
794 	int ret = 0;
795 	int childpid = fork();
796 	if (childpid == 0) {
797 		execvp(stratagus_bin, stratagus_argv);
798 		if (strcmp(stratagus_bin, "stratagus") == 0) {
799 			realpath(argv[0], stratagus_bin);
800 			parentdir(stratagus_bin);
801 			strcat(stratagus_bin, "/stratagus");
802 		}
803 		execvp(stratagus_bin, stratagus_argv);
804 		exit(ENOENT);
805 	} else if (childpid > 0) {
806 		waitpid(childpid, &ret, 0);
807 	} else {
808 		ret = ENOENT;
809 	}
810 #endif
811 	if (ret == ENOENT) {
812 		char msg[BUFF_SIZE * 8];
813 		strcpy(msg, "Execution failed for: ");
814 		strcat(msg, stratagus_bin);
815 		strcat(msg, " ");
816 		for (int i = 1; stratagus_argv[i] != NULL; i++) {
817 			if (strlen(msg) + strlen(stratagus_argv[i]) > BUFF_SIZE * 8) {
818 				break;
819 			}
820 			strcat(msg, stratagus_argv[i]);
821 			strcat(msg, " ");
822 		}
823 		error(TITLE, msg);
824 	} else if (ret != 0) {
825 		char message[8096 * 2] = {'\0'};
826 		snprintf(message, 8096 * 2,
827 				 "Stratagus failed to load game data. "
828 				 "If you just launched the game without any arguments, this may indicate a bug with the extraction process. "
829 				 "Please report this on https://github.com/Wargus/stratagus/issues/new, "
830 				 "and please give details, including: operating system, installation path, username, kind of source CD. "
831 				 "If you got an error message about the extraction command failing, please try to run it in a console "
832 				 "and post the output to the issue. A common problem is symbols in the path for the installation, the game data path, "
833 				 "or the username (like an ampersand or exclamation mark). Try changing these. "
834 #ifndef WIN32
835 #ifdef WIN32
836 				 "Also check if the file '%s' exists and check for errors or post it to the issue. "
837 #endif
838 				 "Try also to remove the folder %s and try the extraction again.",
839 #ifdef WIN32
840 				 GetExtractionLogPath(GAME_NAME, data_path),
841 #endif
842 				 data_path);
843 #else
844 				 "If not already done, please try using the portable version and check for stdout.txt, stderr.txt, and an extraction.log in the folder."
845 				 );
846 #endif
847 		error(TITLE, message);
848 #ifdef WIN32
849 		_unlink(title_path);
850 		_unlink(data_path);
851 #else
852 		unlink(title_path);
853 		unlink(data_path);
854 #endif
855 	}
856 	exit(ret);
857 }
858 
859 #endif
860