1 /**************************************************************************
2  *  PipeWalker game (http://pipewalker.sourceforge.net)                   *
3  *  Copyright (C) 2007-2012 by Artem Senichev <artemsen@gmail.com>        *
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 3 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     *
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. *
17  **************************************************************************/
18 
19 #include "common.h"
20 #include "game.h"
21 #include "image.h"
22 #include "settings.h"
23 #include "../extra/pipewalker.xpm"
24 
25 #ifdef WIN32
26 #include <SDL/SDL_syswm.h>
27 
28 //! SDL window procedure
29 WNDPROC sdl_wnd_proc = NULL;
30 
31 //! Own WNDPROC
32 LRESULT pw_win32_wnd_proc(HWND wnd, UINT msg, WPARAM w_param, LPARAM l_param);
33 #endif // WIN32
34 
35 
36 /**
37  * Parse command line parameters
38  * \param argc number of parameters
39  * \param argv parameters array
40  * \param lvl_id start level id
41  * \param lvl_sz start level map size
42  * \param lvl_wrap start level wrap mode
43  * \return true if we need to close application
44  */
45 bool parse_cmd_params(int argc, char* argv[], unsigned long& lvl_id, level::size& lvl_sz, bool& lvl_wrap);
46 
47 
48 /**
49  * SDL timer callback
50  */
51 Uint32 timer_callback(Uint32 interval);
52 
53 
54 /**
55  * ANSI main entry point
56  */
main(int argc,char * argv[])57 int main(int argc, char* argv[])
58 {
59 	unsigned long lvl_id;
60 	level::size lvl_sz;
61 	bool lvl_wrap;
62 	if (parse_cmd_params(argc, argv, lvl_id, lvl_sz, lvl_wrap))
63 		return 0;	//Help or version was shown
64 
65 	//Initialization
66 	if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) {
67 		fprintf(stderr, "Critical error\nSDL_Init failed: %s\n", SDL_GetError());
68 		return 1;
69 	}
70 
71 	//Let's get some video information
72 	const SDL_VideoInfo* vinfo = SDL_GetVideoInfo();
73 	if (!vinfo) {
74 		fprintf(stderr, "Critical error\nUnable to get video information: %s\n", SDL_GetError());
75 		return 1;
76 	}
77 
78 	//Save desktop size
79 	const int desktop_width = vinfo->current_w;
80 	const int desktop_height = vinfo->current_h;
81 
82 	//Calculate minimum window sizes
83 	const int wnd_min_width =  PW_SCREEN_WIDTH  / 3;
84 	const int wnd_min_height = PW_SCREEN_HEIGHT / 3;
85 
86 	//Create window
87 	if (!SDL_SetVideoMode(PW_SCREEN_WIDTH, PW_SCREEN_HEIGHT, 0, SDL_OPENGL | SDL_RESIZABLE)) {
88 		fprintf(stderr, "Critical error\nUnable to set video mode: %s\n", SDL_GetError());
89 		return 1;
90 	}
91 	SDL_WM_SetCaption(PACKAGE_NAME, PACKAGE_NAME);
92 	image wnd_icon;
93 	if (wnd_icon.load_XPM(pipewalker_xpm, sizeof(pipewalker_xpm) / sizeof(pipewalker_xpm[0])))
94 		SDL_WM_SetIcon(wnd_icon.get_surface(), NULL);
95 
96 	game& game_instance = game::instance();
97 	if (!game_instance.initialize(lvl_id, lvl_sz, lvl_wrap))
98 		return 1;
99 
100 	//Timer - about 25 fps
101 	SDL_SetTimer(40, &timer_callback);
102 
103 #ifdef WIN32
104 	SDL_SysWMinfo wmi;
105 	SDL_VERSION(&wmi.version);
106 	if (SDL_GetWMInfo(&wmi)) {
107 		//Set own window procedure
108 		sdl_wnd_proc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(wmi.window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&pw_win32_wnd_proc)));
109 		//Set normal icon
110 		static HICON icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(0));
111 		if (icon)
112 			SetClassLongPtr(wmi.window, GCL_HICON, reinterpret_cast<LONG>(icon));
113 	}
114 #endif // WIN32
115 
116 	bool done = false;
117 	Uint8 last_mouse_state = 0;
118 
119 	while (!done) {
120 		SDL_Event event;
121 		if (SDL_WaitEvent(&event) == 0) {
122 			fprintf(stderr, "Critical error\nSDL_WaitEvent failed: %s\n", SDL_GetError());
123 			return 1;
124 		}
125 		switch (event.type) {
126 			case SDL_MOUSEMOTION:
127 				{
128 					Sint32 x, y;
129 					SDL_GetMouseState(&x, &y);
130 					game_instance.on_mouse_move(x, y);
131 				}
132 				break;
133 			case SDL_MOUSEBUTTONDOWN:
134 				//We need to save buttons state - in the SDL_MOUSEBUTTONUP event doesn't have this information
135 				last_mouse_state = SDL_GetMouseState(NULL, NULL);
136 				break;
137 			case SDL_MOUSEBUTTONUP:
138 				if (last_mouse_state) {
139 					game_instance.on_mouse_click(last_mouse_state);
140 					last_mouse_state = 0;
141 				}
142 				break;
143 			case SDL_KEYDOWN:
144 				if (event.key.keysym.sym == SDLK_F4 && (SDL_GetModState() == KMOD_LALT || SDL_GetModState() == KMOD_RALT))
145 					done = true;	//Alt+F4 pressed
146 				else
147 					done = game_instance.on_key_press(event.key.keysym.sym);
148 				break;
149 			case SDL_QUIT:
150 				done = true;
151 				break;
152 			case SDL_VIDEOEXPOSE:
153 				game_instance.draw_scene();
154 				break;
155 			case SDL_VIDEORESIZE:
156 				if (event.resize.w && event.resize.h) {
157 					int wnd_width = event.resize.w;
158 					int wnd_height = event.resize.h;
159 
160 					//Set correct aspect ratio
161 					if (wnd_width != desktop_width && wnd_height != desktop_height) {
162 						if (wnd_height != vinfo->current_h)
163 							wnd_width = static_cast<int>(static_cast<float>(wnd_height) / PW_ASPECT_RATIO);
164 						else if (wnd_width != vinfo->current_w)
165 							wnd_height = static_cast<int>(static_cast<float>(wnd_width) * PW_ASPECT_RATIO);
166 						if (wnd_width < wnd_min_width || wnd_height < wnd_min_height) {
167 							//Set minimum window size
168 							wnd_width = wnd_min_width;
169 							wnd_height = wnd_min_height;
170 						}
171 					}
172 
173 					SDL_SetVideoMode(wnd_width, wnd_height, 0, SDL_OPENGL | SDL_RESIZABLE);
174 					game_instance.on_window_resize(wnd_width, wnd_height);
175 					SDL_Event expose_event;
176 					expose_event.type = SDL_VIDEOEXPOSE;
177 					SDL_PushEvent(&expose_event);
178 				}
179 				break;
180 		}
181 	}
182 
183 	game_instance.finalize();
184 
185 	SDL_Quit();
186 	return 0;
187 }
188 
189 
190 #ifdef WIN32
191 /**
192  * MS Windows entry point (See MSDN for more information)
193  */
WinMain(HINSTANCE,HINSTANCE,LPSTR,int)194 int APIENTRY WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
195 {
196 #ifndef NDEBUG
197 	//Create console for debug session
198 	if (!AttachConsole(ATTACH_PARENT_PROCESS))
199 		AllocConsole();
200 	freopen("CONOUT$", "wb", stdout);
201 	freopen("CONOUT$", "wb", stderr);
202 #endif	//NDEBUG
203 
204 	const int rc = main(__argc, __argv);
205 
206 #ifndef NDEBUG
207 	FreeConsole();
208 #endif	//NDEBUG
209 
210 	return rc;
211 }
212 #endif // WIN32
213 
214 
parse_cmd_params(int argc,char * argv[],unsigned long & lvl_id,level::size & lvl_sz,bool & lvl_wrap)215 bool parse_cmd_params(int argc, char* argv[], unsigned long& lvl_id, level::size& lvl_sz, bool& lvl_wrap)
216 {
217 	bool app_exit = false;
218 
219 	lvl_id = 0;
220 	lvl_sz = level::sz_normal;
221 	lvl_wrap = true;
222 
223 	for (int i = 1 /* skip executable file name */; !app_exit && i < argc; ++i) {
224 		const char* param_val = argv[i];
225 		while (*param_val && (*param_val == '-' || *param_val == '/'))
226 			++param_val;
227 		if (strcmp(param_val, "help") == 0 || strcmp(param_val, "?") == 0) {
228 			const char* msg =
229 				"Command line parameters:\n"
230 				" -help:    this help\n"
231 				" -version: show information about version\n"
232 				" -id:      start level with specified id (number from 1 to 99999999)\n"
233 				" -wrap:    set wrap mode (0: disable, 1: enable (default))\n"
234 				" -size:    set level map size (small, normal (default), big or extra)\n"
235 				"\nUsage example:\n"
236 				" pipewalker -id=1234 -wrap=0 -size=big\n";
237 #ifdef WIN32
238 			MessageBoxA(NULL, msg, "PipeWalker " PACKAGE_VERSION, MB_OK | MB_ICONINFORMATION);
239 #else
240 			printf("PipeWalker version " PACKAGE_VERSION "\n");
241 			printf(msg);
242 #endif // WIN32
243 			app_exit = true;
244 		}
245 		else if (strcmp(param_val, "version") == 0) {
246 #ifdef WIN32
247 			MessageBoxA(NULL, "PipeWalker version " PACKAGE_VERSION, "PipeWalker " PACKAGE_VERSION, MB_OK | MB_ICONINFORMATION);
248 #else
249 			printf("PipeWalker version " PACKAGE_VERSION "\n");
250 #endif // WIN32
251 			app_exit = true;
252 		}
253 		else if (strcmp(param_val, "debug") == 0)
254 			settings::debug_mode(true);
255 		else if (strncmp(param_val, "id=", sizeof("id=") - 1) == 0) {
256 			lvl_id = static_cast<unsigned long>(atoi(param_val + sizeof("id=") - 1));
257 			if (lvl_id > PW_MAX_LEVEL_NUMBER)
258 				lvl_id = 0;
259 		}
260 		else if (strncmp(param_val, "wrap=", sizeof("wrap=") - 1) == 0)
261 			lvl_wrap = *(param_val + sizeof("id=") - 1) && atoi(param_val + sizeof("id=") - 1) != 0;
262 		else if (strncmp(param_val, "size=", sizeof("size=") - 1) == 0) {
263 			const char* level_size = param_val + sizeof("size=") - 1;
264 			if (strcmp(level_size, "small") == 0)
265 				lvl_sz = level::sz_small;
266 			else if (strcmp(level_size, "normal") == 0)
267 				lvl_sz = level::sz_normal;
268 			else if (strcmp(level_size, "big") == 0)
269 				lvl_sz = level::sz_big;
270 			else if (strcmp(level_size, "extra") == 0)
271 				lvl_sz = level::sz_extra;
272 		}
273 	}
274 
275 	return app_exit;
276 }
277 
278 
timer_callback(Uint32 interval)279 Uint32 timer_callback(Uint32 interval)
280 {
281 	if (game::instance().need_redisplay()) {
282 		//draw_scene();
283 		SDL_Event expose_event;
284 		expose_event.type = SDL_VIDEOEXPOSE;
285 		SDL_PushEvent(&expose_event);
286 	}
287 	return interval;
288 }
289 
290 
291 #ifdef WIN32
pw_win32_wnd_proc(HWND wnd,UINT msg,WPARAM w_param,LPARAM l_param)292 LRESULT pw_win32_wnd_proc(HWND wnd, UINT msg, WPARAM w_param, LPARAM l_param)
293 {
294 	assert(sdl_wnd_proc);
295 
296 	if (msg == WM_GETMINMAXINFO) {
297 		static LONG max_width = 0;
298 		static LONG max_height = 0;
299 		if (!max_height) {
300 			max_height = GetSystemMetrics(SM_CYMAXIMIZED);
301 			//Calculate correct aspect ratio
302 			RECT aspect;
303 			aspect.top = aspect.left = 0;
304 			aspect.right = PW_SCREEN_WIDTH;
305 			aspect.bottom = PW_SCREEN_HEIGHT;
306 			AdjustWindowRectEx(&aspect, GetWindowLongPtr(wnd, GWL_STYLE), FALSE, GetWindowLongPtr(wnd, GWL_EXSTYLE));
307 			const float need_aspect = static_cast<float>(aspect.bottom - aspect.top) / static_cast<float>(aspect.right - aspect.left);
308 			max_width = static_cast<LONG>(static_cast<float>(max_height) / need_aspect);
309 		}
310 
311 		LPMINMAXINFO mmi = reinterpret_cast<LPMINMAXINFO>(l_param);
312 		mmi->ptMaxSize.x = mmi->ptMaxTrackSize.x = max_width;
313 		mmi->ptMaxSize.y = mmi->ptMaxTrackSize.y = max_height;
314 		mmi->ptMinTrackSize.x = PW_SCREEN_WIDTH / 3;
315 		mmi->ptMinTrackSize.y = PW_SCREEN_HEIGHT / 3;
316 		return 0;
317 	}
318 
319 	return CallWindowProc(sdl_wnd_proc, wnd, msg, w_param, l_param);
320 }
321 #endif // WIN32
322