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