131 #ifndef STRATAGUS_GAME_LAUNCHER_H
132 #define STRATAGUS_GAME_LAUNCHER_H
135 #include <sys/types.h>
142 #define STRATAGUS_BIN
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
149 #ifndef GAME_SHOULD_EXTRACT_AGAIN
150 #define GAME_SHOULD_EXTRACT_AGAIN false
159 #define TITLE_PNG "%s\\graphics\\ui\\title.png"
161 #define TITLE_PNG "%s/graphics/ui/title.png"
166 #if ! defined (DATA_PATH) || ! defined (SCRIPTS_PATH) || ! defined (STRATAGUS_BIN)
167 #error You need to define paths, see stratagus-game-launcher.h
169 #pragma GCC diagnostic ignored "-Wwrite-strings"
173 #pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
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"
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 "."
191 char marker[MAX_PATH] = {
'\0'};
192 if (PathCombine(marker, data_path,
"portable-install")) {
193 if (PathFileExists(marker)) {
197 SHGetFolderPathA(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, 0, data_path);
200 strcpy(data_path, getenv(
"HOME"));
202 int datalen = strlen(data_path);
204 strcat(data_path,
"\\Stratagus\\");
205 #elif defined(USE_MAC)
206 strcat(data_path,
"/Library/Stratagus/");
208 strcat(data_path,
"/.stratagus/");
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'};
220 fgets(dataversion, 20, f);
223 #ifdef CHECK_EXTRACTED_VERSION
230 sprintf(buf,
"%s -V", tool_path);
231 FILE *pipe = popen(buf,
"r");
233 fgets(toolversion, 20, pipe);
237 sprintf(buf,
"%s -V", tool_path);
238 HANDLE g_hChildStd_OUT_Rd = NULL;
239 HANDLE g_hChildStd_OUT_Wr = NULL;
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))
247 if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
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))
259 CloseHandle(piProcInfo.hProcess);
260 CloseHandle(piProcInfo.hThread);
261 ReadFile(g_hChildStd_OUT_Rd, toolversion, 20, &nbByteRead, NULL);
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) {
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
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)) {
283 canJustReextract = 0;
285 efile = extraction_files[i + 1];
288 canJustReextract = 0;
291 canJustReextract = 0;
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) {
298 }
else if (force == 1) {
300 }
else if (force == 2) {
304 int patterncount = 0;
305 char* filepatterns[] = { NULL };
308 char* filepatterns[] = { GAME_CD_FILE_PATTERNS, NULL };
309 int patterncount = 0;
310 while (filepatterns[patterncount++] != NULL);
313 if (!canJustReextract || datafileCstr != NULL) {
314 if (datafileCstr == NULL) {
316 patterncount - 1, filepatterns, NULL, 0);
318 if (datafileCstr == NULL) {
321 std::string datafile = datafileCstr;
322 if (datafile.compare(datafile.length() - 4, 4,
".exe") == 0) {
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()};
332 const char *file =
"innoextract";
333 char *argv[] = {
"-i", (
char*)datafile.c_str(), NULL};
337 bool success =
false;
338 fs::path tmpp = fs::temp_directory_path() /
GAME;
339 fs::create_directories(tmpp);
341 wchar_t *curdir = _wgetcwd(NULL, 0);
343 char *curdir = getcwd(NULL, 0);
345 if (curdir != NULL) {
347 if (_wchdir(tmpp.wstring().c_str()) == 0) {
349 if (chdir(tmpp.string().c_str()) == 0) {
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.");
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) {
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);
387 srcfolder = fs::path(datafile).parent_path();
390 srcfolder = fs::path(datafile).parent_path();
393 srcfolder = fs::path(destination);
397 char* sourcepath = _strdup(scripts_path);
398 if (sourcepath[0] ==
'"') {
403 strncpy(sourcepath, scripts_path + 1, strlen(scripts_path) - 2);
404 sourcepath[strlen(scripts_path) - 2] =
'\0';
407 char* sourcepath = strdup(scripts_path);
410 fs::create_directories(fs::path(destination));
412 if (!fs::exists(sourcepath)) {
414 strcpy(sourcepath, fs::path(SRC_PATH()).parent_path().
string().c_str());
418 if (!fs::exists(sourcepath)) {
420 strcpy(sourcepath, fs::path(extractor_tool).parent_path().
string().c_str());
424 if (!fs::exists(sourcepath)) {
426 std::string msg(
"There was an error copying the data, could not discover scripts path: ");
433 fs::path contrib_src_path;
434 fs::path contrib_dest_path(destination);
437 char* contrib_directories[] = CONTRIB_DIRECTORIES;
438 while (contrib_directories[i] != NULL && contrib_directories[i + 1] != NULL) {
439 if (!strcmp(contrib_directories[i],
":optional:")) {
443 if (contrib_directories[i][0] !=
'/') {
445 contrib_src_path = fs::path(sourcepath);
446 contrib_src_path /= contrib_directories[i];
448 contrib_src_path = fs::path(contrib_directories[i]);
451 if (!fs::exists(contrib_src_path)) {
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());
460 copy_dir(contrib_src_path, contrib_dest_path / contrib_directories[i + 1]);
469 char cmdbuf[4096] = {
'\0'};
472 std::vector<std::wstring> args;
474 file = fs::path(extractor_tool).wstring();
475 for (
int i = 0; ; i++) {
476 const char *earg = extractor_args[i];
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);
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);
493 strcat(cmdbuf,
"osascript -e \"tell application \\\"Terminal\\\"\n"
494 " set w to do script \\\"");
497 strcat(cmdbuf,
"xterm -e bash -c ");
498 strcat(cmdbuf,
" \"");
502 strcat(cmdbuf, extractor_tool);
503 for (
int i = 0; ; i++) {
504 const char *earg = extractor_args[i];
509 strcat(cmdbuf, earg);
512 strcat(cmdbuf,
" " QUOTE);
513 strcat(cmdbuf, srcfolder.string().c_str());
515 strcat(cmdbuf, destination);
516 strcat(cmdbuf,
QUOTE);
519 strcat(cmdbuf,
"; exit\\\"\n"
522 " if not busy of w then exit repeat\n"
527 strcat(cmdbuf,
"; echo 'Press RETURN to continue...'; read\"");
531 printf(
"Running extractor as %s\n", cmdbuf);
532 exitcode = system(cmdbuf);
537 WideCharToMultiByte(CP_ACP, 0, combinedCommandline.c_str(), combinedCommandline.size(), cmdbuf,
sizeof(cmdbuf) - 1, NULL, NULL);
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);
543 ExtractData(extractor_tool, extractor_args, destination, scripts_path, 2);
547 int main(
int argc,
char * argv[]) {
557 if (strchr(argv[0],
SLASH[0])) {
558 strcpy(extractor_path, argv[0]);
560 strcat(extractor_path,
SLASH EXTRACTOR_TOOL);
562 if (!strstr(extractor_path,
".exe")) {
563 strcat(extractor_path,
".exe");
566 if (stat(extractor_path, &st) == 0) {
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];
578 extractor_path[0] =
'\0';
581 if (extractor_path[0] ==
'\0') {
583 strcpy(extractor_path, EXTRACTOR_TOOL);
587 strcat(msg,
" (expected at ");
588 strcat(msg, extractor_path);
596 memset(executable_path, 0,
sizeof(executable_path));
597 GetModuleFileName(NULL, executable_path,
sizeof(executable_path)-1);
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);
605 size_t data_path_size =
sizeof(data_path);
606 memset(data_path, 0, data_path_size);
608 if (executable_path[0] && executable_drive[0] && executable_dir[0]) {
609 PathCombine(data_path, executable_drive, executable_dir);
611 _getcwd(data_path, data_path_size);
613 PathRemoveBackslash(data_path);
614 sprintf(scripts_path,
"\"%s\"", data_path);
619 sprintf(stratagus_bin,
"%s\\stratagus.exe", data_path);
620 if (stat(stratagus_bin, &st) != 0) {
622 if (!SearchPath(NULL,
"stratagus",
".exe", MAX_PATH, stratagus_bin, NULL) &&
623 !SearchPath(NULL,
"stratagus-dbg",
".exe", MAX_PATH, stratagus_bin, NULL)) {
625 DWORD stratagus_path_size =
sizeof(stratagus_path);
626 memset(stratagus_path, 0, stratagus_path_size);
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) {
634 strcat(msg,
" (expected globally installed or in ");
635 strcat(msg, stratagus_bin);
643 if (_chdir(stratagus_path) != 0) {
646 strcat(msg,
" (registry key found, but directory ");
647 strcat(msg, stratagus_path);
648 strcat(msg,
" cannot be opened)");
651 sprintf(stratagus_bin,
"%s\\stratagus.exe", stratagus_path);
665 char *
const extractor_args[] = EXTRACTOR_ARGS;
668 if (stat(argv[1], &st) == 0) {
672 ExtractData(extractor_path, extractor_args, data_path, scripts_path, 2, argv[1]);
674 }
else if (!strcmp(argv[1],
"--extract")) {
677 ExtractData(extractor_path, extractor_args, data_path, scripts_path, 1);
679 }
else if (!strcmp(argv[1],
"--extract-no-gui")) {
683 ExtractData(extractor_path, extractor_args, data_path, scripts_path, 1);
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",
694 if ( stat(stratagus_bin, &st) != 0 ) {
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) {
702 strcat(msg,
" (expected in ");
703 strcat(msg, stratagus_bin);
709 realpath(argv[0], stratagus_bin);
711 if (strlen(stratagus_bin) > 0) {
712 strcat(stratagus_bin,
"/stratagus");
714 strcat(stratagus_bin,
"./stratagus");
716 if ( stat(stratagus_bin, &st) != 0 ) {
719 strcat(msg,
" (expected in ");
720 strcat(msg, stratagus_bin);
728 sprintf(title_path,
TITLE_PNG, data_path);
729 if ( stat(title_path, &st) != 0 ) {
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);
735 if ( stat(title_path, &st) != 0 ) {
737 msg +=
" (extraction was attempted, but it seems an error occurred)";
743 ExtractData(extractor_path, extractor_args, data_path, scripts_path);
747 int data_path_len = strlen(data_path);
750 for (
int i = data_path_len - 1; i >= 0; --i) {
751 data_path[i + 1] = data_path[i];
754 data_path[data_path_len + 1] =
'"';
755 data_path[data_path_len + 2] = 0;
759 char** stratagus_argv;
760 stratagus_argv = (
char**) malloc((argc + 3) *
sizeof (*stratagus_argv));
762 char * stratagus_argv[argc + 3];
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;
774 stratagus_argv[0] = argv[0];
777 stratagus_argv[1] =
"-d";
778 stratagus_argv[2] = data_path;
780 for (
int i = 3; i < argc + 2; ++i ) {
781 stratagus_argv[i] = argv[i - 2];
783 stratagus_argv[argc + 2] = NULL;
786 extern char** environ;
788 while(environ[i]) { i++; }
789 environ[i] = (
char*)
"OMP_WAIT_POLICY=passive";
790 environ[i + 1] = NULL;
792 int ret = _spawnvpe(_P_WAIT, stratagus_bin, stratagus_argv, environ);
795 int childpid = fork();
797 execvp(stratagus_bin, stratagus_argv);
798 if (strcmp(stratagus_bin,
"stratagus") == 0) {
799 realpath(argv[0], stratagus_bin);
801 strcat(stratagus_bin,
"/stratagus");
803 execvp(stratagus_bin, stratagus_argv);
805 }
else if (childpid > 0) {
806 waitpid(childpid, &ret, 0);
813 strcpy(msg,
"Execution failed for: ");
814 strcat(msg, stratagus_bin);
816 for (
int i = 1; stratagus_argv[i] != NULL; i++) {
817 if (strlen(msg) + strlen(stratagus_argv[i]) >
BUFF_SIZE * 8) {
820 strcat(msg, stratagus_argv[i]);
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. "
836 "Also check if the file '%s' exists and check for errors or post it to the issue. "
838 "Try also to remove the folder %s and try the extraction again.",
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."