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 /**
31  * @page GameLauncher Stratagus Game Launcher
32  *
33  * Stratagus Game Launcher is C code for generating launcher for any Stratagus game.
34  * Game launcher for concrete game check if game data exists in default Stratagus
35  * location and spawn Stratagus process with correct game data location. If does not
36  * exist it show GUI or console error message.
37  *
38  * Before including this header, you need to define:
39  *
40  * ::GAME_NAME
41  *
42  * ::GAME_CD
43  *
44  * ::GAME
45  *
46  * On Non Windows system you need to specify also paths:
47  *
48  * ::DATA_PATH
49  *
50  * ::SCRIPTS_PATH
51  *
52  * ::STRATAGUS_BIN
53  *
54  * On Windows paths are reading from InstallLocation key in Uninstall section:
55  * Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Stratagus
56  *
57  * Example use of code:
58  *
59  * @code
60  *
61  * #define GAME_NAME "My Game Name"
62  * #define GAME_CD "Original Game CD Name"
63  * #define GAME "my_game"
64  *
65  * #ifndef WIN32
66  * #define DATA_PATH "/usr/share/games/stratagus/my_game"
67  * #define SCRIPTS_PATH "/usr/share/games/stratagus/my_game"
68  * #define STRATAGUS_BIN "/usr/games/stratagus"
69  * #endif
70  *
71  * #include <stratagus-game-launcher.h>
72  *
73  * @endcode
74  **/
75 
76 /**
77  * \def GAME_NAME
78  * Full name of your Game
79  **/
80 
81 /**
82  * \def GAME_CD
83  * Full name of data CD
84  **/
85 
86 /**
87  * \def GAME
88  * Short name of game (lower ascii chars without space)
89  **/
90 
91 /**
92  * \def DATA_PATH
93  * Path to game data directory
94  **/
95 
96 /**
97  * \def SCRIPTS_PATH
98  * Path to game scripts directory
99  **/
100 
101 /**
102  * \def STRATAGUS_BIN
103  * Path to stratagus executable binary
104  **/
105 
106 /* Fake definitions for Doxygen */
107 #ifdef DOXYGEN
108 #define GAME_NAME
109 #define GAME_CD
110 #define GAME
111 #define DATA_PATH
112 #define SCRIPTS_PATH
113 #define STRATAGUS_BIN
114 #endif
115 
116 #if ! defined (GAME_NAME) || ! defined (GAME_CD) || ! defined (GAME)
117 #error You need to define all Game macros, see stratagus-game-launcher.h
118 #endif
119 
120 #if ( defined (_MSC_VER) || defined (_WIN32) || defined (_WIN64) ) && ! defined (WIN32)
121 #define WIN32
122 #endif
123 
124 /**
125  * \def TITLE_PNG
126  * OPTIONAL: Path to title screen (for testing if data was extracted)
127  **/
128 #ifndef TITLE_PNG
129 #ifdef WIN32
130 #define TITLE_PNG "%s\\graphics\\ui\\title.png"
131 #else
132 #define TITLE_PNG "%s/graphics/ui/title.png"
133 #endif
134 #endif
135 
136 #ifndef WIN32
137 #if ! defined (DATA_PATH) || ! defined (SCRIPTS_PATH) || ! defined (STRATAGUS_BIN)
138 #error You need to define paths, see stratagus-game-launcher.h
139 #endif
140 #endif
141 
142 #ifdef WIN32
143 #define WINVER 0x0501
144 #include <windows.h>
145 #include <wincon.h>
146 #include <process.h>
147 #include <errno.h>
148 #endif
149 
150 #include <stdio.h>
151 #include <stdlib.h>
152 #include <string.h>
153 
154 #include <sys/stat.h>
155 #include <sys/types.h>
156 
157 #if defined(_MSC_VER) || defined(__MINGW32__)
158 #include <direct.h>
159 #define inline __inline
160 #define chdir _chdir
161 #define getcwd _getcwd
162 #define spawnvp _spawnvp
163 #define stat _stat
164 #endif
165 
166 #ifdef _MSC_VER
167 #pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
168 #endif
169 
170 #ifndef WIN32
171 #include <unistd.h>
172 #include <X11/Xlib.h>
173 #ifndef NOGTK
174 #include <gtk/gtk.h>
175 #endif
176 #endif
177 
178 #ifdef _WIN64
179 #define REGKEY "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Stratagus (64 bit)"
180 #elif defined (WIN32)
181 #define REGKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Stratagus"
182 #endif
183 
184 #define TITLE GAME_NAME
185 #define STRATAGUS_NOT_FOUND "Stratagus is not installed.\nYou need Stratagus to run " GAME_NAME "!\nFirst install Stratagus from https://launchpad.net/stratagus"
186 #define DATA_NOT_EXTRACTED GAME_NAME " data was not extracted yet.\nYou need extract data from original " GAME_CD " first!"
187 #define NO_X_DISPLAY "Cannot open X Display"
188 #define CONSOLE_MODE_NOT_ROOT "You must be root to run " GAME_NAME " in console framebuffer mode"
189 
190 #define BUFF_SIZE 1024
191 
192 #ifndef WIN32
193 int ConsoleMode = 0;
194 #endif
195 
error(char * title,char * text)196 static void error(char * title, char * text) {
197 
198 #ifdef WIN32
199 	MessageBox(NULL, text, title, MB_OK | MB_ICONERROR);
200 #else
201 #ifdef NOGTK
202 	{
203 #else
204 	if ( ! ConsoleMode ) {
205 		GtkWidget * window = NULL;
206 		GtkWidget * dialog = NULL;
207 
208 		dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", text);
209 		gtk_window_set_title(GTK_WINDOW(dialog), title);
210 		gtk_window_set_skip_pager_hint(GTK_WINDOW(dialog), 0);
211 		gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), 0);
212 		gtk_label_set_selectable(GTK_LABEL(GTK_MESSAGE_DIALOG(dialog)->label), 0);
213 		gtk_dialog_run(GTK_DIALOG(dialog));
214 		gtk_widget_destroy(dialog);
215 
216 	} else {
217 #endif
218 		fprintf(stderr, "%s -- Error: %s\n", title, text);
219 	}
220 #endif
221 	exit(1);
222 }
223 
224 int main(int argc, char * argv[]) {
225 
226 #ifndef WIN32
227 	if ( ! XOpenDisplay(NULL) ) {
228 		ConsoleMode = 1;
229 	}
230 	if ( ConsoleMode ) {
231 		if ( getuid() != 0 ) {
232 			error(TITLE, CONSOLE_MODE_NOT_ROOT);
233 		}
234 	} else {
235 #ifndef NOGTK
236 		gtk_init(&argc, &argv);
237 #endif
238 	}
239 #endif
240 
241 	struct stat st;
242 	char data_path[BUFF_SIZE];
243 	char scripts_path[BUFF_SIZE];
244 	char stratagus_bin[BUFF_SIZE];
245 	char title_path[BUFF_SIZE];
246 
247 #ifdef WIN32
248 	char executable_path[BUFF_SIZE];
249 	memset(executable_path, 0, sizeof(executable_path));
250 	GetModuleFileName(NULL, executable_path, sizeof(executable_path)-1);
251 
252 	char executable_drive[_MAX_DRIVE];
253 	char executable_dir[_MAX_DIR];
254 	memset(executable_drive, 0, sizeof(executable_drive));
255 	memset(executable_dir, 0, sizeof(executable_dir));
256 	_splitpath(executable_path, executable_drive, executable_dir, NULL, NULL);
257 
258 	size_t data_path_size = sizeof(data_path);
259 	memset(data_path, 0, data_path_size);
260 
261 	if (executable_path[0] && executable_drive[0] && executable_dir[0]) {
262 		strcpy(data_path, executable_drive);
263 		strcpy(data_path+strlen(executable_drive), executable_dir);
264 	} else {
265 		getcwd(data_path, data_path_size);
266 	}
267 	const size_t data_path_length = strlen(data_path);
268 	if (data_path_length != 0 && data_path[data_path_length - 1] == '\\') {
269 		data_path[data_path_length - 1] = '\0';
270 	}
271 	sprintf(scripts_path, "\"%s\"", data_path);
272 
273 	char stratagus_path[BUFF_SIZE];
274 
275 	// Try to use stratagus.exe from data (install) directory first
276 	sprintf(stratagus_bin, "%s\\stratagus.exe", data_path);
277 	if (stat(stratagus_bin, &st) != 0) {
278 		// If no local stratagus.exe is present, look for a globally installed version
279 		DWORD stratagus_path_size = sizeof(stratagus_path);
280 		memset(stratagus_path, 0, stratagus_path_size);
281 		HKEY key;
282 
283 		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
284 			if (RegQueryValueEx(key, "InstallLocation", NULL, NULL, (LPBYTE)stratagus_path, &stratagus_path_size) == ERROR_SUCCESS) {
285 				if (stratagus_path_size == 0 || strlen(stratagus_path) == 0) {
286 					error(TITLE, STRATAGUS_NOT_FOUND);
287 				}
288 			}
289 			RegCloseKey(key);
290 		}
291 
292 		if (chdir(stratagus_path) != 0) {
293 			error(TITLE, STRATAGUS_NOT_FOUND);
294 		}
295 		sprintf(stratagus_bin, "%s\\stratagus.exe", stratagus_path);
296 	}
297 #else
298 	strcpy(data_path, DATA_PATH);
299 	strcpy(scripts_path, SCRIPTS_PATH);
300 	strcpy(stratagus_bin, STRATAGUS_BIN);
301 #endif
302 
303 	if ( stat(stratagus_bin, &st) != 0 ) {
304 		error(TITLE, STRATAGUS_NOT_FOUND);
305 	}
306 	if ( stat(data_path, &st) != 0 ) {
307 		error(TITLE, DATA_NOT_EXTRACTED);
308 	}
309 	sprintf(title_path, TITLE_PNG, data_path);
310 #ifdef WIN32
311 	int data_path_len = strlen(data_path);
312 
313 	for (int i = data_path_len - 1; i >= 0; --i) {
314 		data_path[i + 1] = data_path[i];
315 	}
316 	data_path[0] = '"';
317 	data_path[data_path_len + 1] = '"';
318 	data_path[data_path_len + 2] = 0;
319 #endif
320 
321 	if ( stat(title_path, &st) != 0 ) {
322 		error(TITLE, DATA_NOT_EXTRACTED);
323 	}
324 #ifndef WIN32
325 	if ( strcmp(data_path, scripts_path) != 0 ) {
326 		if ( chdir(data_path) != 0 ) {
327 			error(TITLE, DATA_NOT_EXTRACTED);
328 		}
329 	}
330 #endif
331 
332 #ifdef _MSC_VER
333 	char** stratagus_argv;
334 	stratagus_argv = (char**) malloc((argc + 3) * sizeof (*stratagus_argv));
335 #else
336 	char * stratagus_argv[argc + 3];
337 #endif
338 
339 #ifdef WIN32
340 	char stratagus_argv0_esc[BUFF_SIZE];
341 	memset(stratagus_argv0_esc, 0, sizeof(stratagus_argv0_esc));
342 	strcpy(stratagus_argv0_esc + 1, argv[0]);
343 	stratagus_argv0_esc[0] = '"';
344 	stratagus_argv0_esc[strlen(argv[0]) + 1] = '"';
345 	stratagus_argv0_esc[strlen(argv[0]) + 2] = 0;
346 	stratagus_argv[0] = stratagus_argv0_esc;
347 #else
348 	stratagus_argv[0] = argv[0];
349 #endif
350 
351 	stratagus_argv[1] = "-d";
352 	stratagus_argv[2] = scripts_path;
353 
354 	for (int i = 3; i < argc + 2; ++i ) {
355 		stratagus_argv[i] = argv[i - 2];
356 	}
357 	stratagus_argv[argc + 2] = NULL;
358 
359 #ifdef WIN32
360 	AttachConsole(ATTACH_PARENT_PROCESS);
361 
362 	errno = 0;
363 	int ret = spawnvp(_P_WAIT, stratagus_bin, stratagus_argv);
364 #ifdef _MSC_VER
365 	free (stratagus_argv);
366 #endif
367 	if ( errno == 0 ) {
368 		return ret;
369 	}
370 #else
371 	execvp(stratagus_bin, stratagus_argv);
372 #endif
373 
374 	error(TITLE, STRATAGUS_NOT_FOUND);
375 	return 1;
376 }
377