1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * inferno.c: Entry point of program (main procedure)
23  *
24  * After main initializes everything, most of the time is spent in the loop
25  * while (window_get_front())
26  * In this loop, the main menu is brought up first.
27  *
28  * main() for Inferno
29  *
30  */
31 
32 extern const char copyright[];
33 
34 const
35 #if defined(DXX_BUILD_DESCENT_I)
36 char copyright[] = "DESCENT   COPYRIGHT (C) 1994,1995 PARALLAX SOFTWARE CORPORATION";
37 #elif defined(DXX_BUILD_DESCENT_II)
38 char copyright[] = "DESCENT II  COPYRIGHT (C) 1994-1996 PARALLAX SOFTWARE CORPORATION";
39 #endif
40 
41 #include "dxxsconf.h"
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <limits.h>
46 #include <SDL.h>
47 #if DXX_USE_SCREENSHOT_FORMAT_PNG
48 #include <png.h>
49 #endif
50 
51 #ifdef __unix__
52 #include <unistd.h>
53 #include <sys/stat.h>
54 #include <sys/types.h>
55 #endif
56 
57 #include <cctype>
58 #include <locale>
59 #include "pstypes.h"
60 #include "strutil.h"
61 #include "console.h"
62 #include "gr.h"
63 #include "key.h"
64 #include "bm.h"
65 #include "inferno.h"
66 #include "dxxerror.h"
67 #include "player.h"
68 #include "game.h"
69 #include "u_mem.h"
70 #include "screens.h"
71 #include "texmerge.h"
72 #include "menu.h"
73 #include "digi.h"
74 #include "palette.h"
75 #include "args.h"
76 #include "titles.h"
77 #include "text.h"
78 #include "gamefont.h"
79 #include "kconfig.h"
80 #include "newmenu.h"
81 #include "config.h"
82 #include "multi.h"
83 #include "gameseq.h"
84 #if defined(DXX_BUILD_DESCENT_II)
85 #include "gamepal.h"
86 #include "movie.h"
87 #endif
88 #include "playsave.h"
89 #include "newdemo.h"
90 #include "joy.h"
91 #if !DXX_USE_OGL
92 #include "../texmap/scanline.h" //for select_tmap -MM
93 #include "texmap.h"
94 #endif
95 #include "event.h"
96 #include "rbaudio.h"
97 #if DXX_WORDS_NEED_ALIGNMENT
98 #include <sys/prctl.h>
99 #endif
100 #if DXX_USE_EDITOR
101 #include "messagebox.h"
102 #include "editor/editor.h"
103 #include "ui.h"
104 #endif
105 #include "vers_id.h"
106 #if DXX_USE_UDP
107 #include "net_udp.h"
108 #endif
109 #include "dsx-ns.h"
110 
111 #if DXX_USE_SDLIMAGE
112 #include <SDL_image.h>
113 #endif
114 #if DXX_USE_SDLMIXER
115 #include <SDL_mixer.h>
116 #endif
117 
118 namespace dsx {
119 
120 int Screen_mode=-1;					//game screen or editor screen?
121 
122 #if defined(DXX_BUILD_DESCENT_I)
123 uint8_t HiresGFXAvailable;
124 int MacHog = 0;	// using a Mac hogfile?
125 #endif
126 
127 //read help from a file & print to screen
print_commandline_help()128 static void print_commandline_help()
129 {
130 #define DXX_COMMAND_LINE_HELP_FMT(FMT,...)	FMT
131 #define DXX_COMMAND_LINE_HELP_ARG(FMT,...)	, ## __VA_ARGS__
132 
133 #define DXX_if_defined_placeholder1	,
134 #define DXX_if_defined_unwrap(A,...)	A, ## __VA_ARGS__
135 	/* If the parameter V, after macro expansion, evaluates to `1`, then
136 	 * expand to the parameter F.  Otherwise, expand to nothing.
137 	 */
138 #define DXX_if_defined(V,F)	DXX_if_defined2(V,F)
139 	/* If the parameter V, after macro expansion, evaluates to anything
140 	 * other than `1`, then expand to the parameter F.  Otherwise,
141 	 * expand to nothing.
142 	 */
143 #define DXX_if_not_defined_to_1(V,F)	DXX_if_not_defined_to_1_2(V,F)
144 #define DXX_if_defined2(V,F)	DXX_if_defined3(DXX_if_defined_placeholder##V, F)
145 #define DXX_if_not_defined_to_1_2(V,F)	DXX_if_not_defined_to_1_3(DXX_if_defined_placeholder##V, F)
146 #define DXX_if_defined3(V,F)	DXX_if_defined4(F, V 1, 0)
147 #define DXX_if_not_defined_to_1_3(V,F)	DXX_if_defined4(F, V 0, 1)
148 #define DXX_if_defined4(F,_,V,...)	DXX_if_defined5_##V(F)
149 #define DXX_if_defined5_0(F)
150 #define DXX_if_defined5_1(F)	DXX_if_defined_unwrap F
151 #define DXX_if_defined_01(V,F)	DXX_if_defined4(F,,V)
152 
153 #define DXX_COMMAND_LINE_HELP_unix(V)	DXX_if_defined(__unix__, (V))
154 #define DXX_COMMAND_LINE_HELP_D1(V)	DXX_if_defined(DXX_BUILD_DESCENT_I, (V))
155 #define DXX_COMMAND_LINE_HELP_D2(V)	DXX_if_defined(DXX_BUILD_DESCENT_II, (V))
156 #define DXX_STRINGIZE2(X)	#X
157 #define DXX_STRINGIZE(X)	DXX_STRINGIZE2(X)
158 
159 #if DXX_USE_OGL
160 #define DXX_COMMAND_LINE_HELP_OGL(V)	V
161 #define DXX_COMMAND_LINE_HELP_SDL(V)
162 #else
163 #define DXX_COMMAND_LINE_HELP_OGL(V)
164 #define DXX_COMMAND_LINE_HELP_SDL(V)	V
165 #endif
166 
167 #define DXX_COMMAND_LINE_HELP(VERB)	\
168 	VERB("\n System Options:\n\n")	\
169 	VERB("  -nonicefps                    Don't free CPU-cycles\n")	\
170 	VERB("  -maxfps <n>                   Set maximum framerate to <n>\n\t\t\t\t(default: " DXX_STRINGIZE(MAXIMUM_FPS) ", available: " DXX_STRINGIZE(MINIMUM_FPS) "-" DXX_STRINGIZE(MAXIMUM_FPS) ")\n")	\
171 	VERB("  -hogdir <s>                   set shared data directory to <s>\n")	\
172 	DXX_COMMAND_LINE_HELP_unix(	\
173 		VERB("  -nohogdir                     don't try to use shared data directory\n")	\
174 	)	\
175 	VERB("  -add-missions-dir <s>         Add contents of location <s> to the missions directory\n")	\
176 	VERB("  -use_players_dir              Put player files and saved games in Players subdirectory\n")	\
177 	VERB("  -lowmem                       Lowers animation detail for better performance with\n\t\t\t\tlow memory\n")	\
178 	VERB("  -pilot <s>                    Select pilot <s> automatically\n")	\
179 	VERB("  -auto-record-demo             Start recording on level entry\n")	\
180 	VERB("  -record-demo-format           Set demo name automatically\n")	\
181 	VERB("  -autodemo                     Start in demo mode\n")	\
182 	VERB("  -window                       Run the game in a window\n")	\
183 	VERB("  -noborders                    Don't show borders in window mode\n")	\
184 	DXX_COMMAND_LINE_HELP_D1(	\
185 		VERB("  -notitles                     Skip title screens\n")	\
186 	)	\
187 	DXX_COMMAND_LINE_HELP_D2(	\
188 		VERB("  -nomovies                     Don't play movies\n")	\
189 	)	\
190 	VERB("\n Controls:\n\n")	\
191 	VERB("  -nocursor                     Hide mouse cursor\n")	\
192 	VERB("  -nomouse                      Deactivate mouse\n")	\
193 	VERB("  -nojoystick                   Deactivate joystick\n")	\
194 	VERB("  -nostickykeys                 Make CapsLock and NumLock non-sticky\n")	\
195 	VERB("\n Sound:\n\n")	\
196 	VERB("  -nosound                      Disables sound output\n")	\
197 	VERB("  -nomusic                      Disables music output\n")	\
198 	DXX_COMMAND_LINE_HELP_D2(	\
199 		VERB("  -sound11k                     Use 11KHz sounds\n")	\
200 	)	\
201 	DXX_if_defined_01(DXX_USE_SDLMIXER, (	\
202 		VERB("  -nosdlmixer                   Disable Sound output via SDL_mixer\n")	\
203 	))	\
204 	VERB("\n Graphics:\n\n")	\
205 	VERB("  -lowresfont                   Force use of low resolution fonts\n")	\
206 	DXX_COMMAND_LINE_HELP_D2(	\
207 		VERB("  -lowresgraphics               Force use of low resolution graphics\n")	\
208 		VERB("  -lowresmovies                 Play low resolution movies if available (for slow machines)\n")	\
209 	)	\
210 	DXX_COMMAND_LINE_HELP_OGL(	\
211 		VERB("  -gl_fixedfont                 Don't scale fonts to current resolution\n")	\
212 		VERB("  -gl_syncmethod <n>            OpenGL sync method (default: %i)\n", OGL_SYNC_METHOD_DEFAULT)	\
213 		VERB("                                    0: Disabled\n")	\
214 		VERB("                                    1: Fence syncs, limit GPU latency to at most one frame\n")	\
215 		VERB("                                    2: Like 1, but sleep during sync to reduce CPU load\n")	\
216 		VERB("                                    3: Immediately sync after buffer swap\n")	\
217 		VERB("                                    4: Immediately sync after buffer swap\n")	\
218 		VERB("                                    5: Auto: if VSync is enabled and ARB_sync is supported, use mode 2, otherwise mode 0\n")	\
219 		VERB("  -gl_syncwait <n>              Wait interval (ms) for sync mode 2 (default: " DXX_STRINGIZE(OGL_SYNC_WAIT_DEFAULT) ")\n")	\
220 		VERB("  -gl_darkedges                 Re-enable dark edges around filtered textures (as present in earlier versions of the engine)\n")	\
221 		DXX_if_not_defined_to_1(RELEASE, (	\
222 		VERB("  -gl_stereo                    Enable OpenGL stereo quad buffering, if available\n")	\
223 		VERB("  -gl_stereoview <n>            Select OpenGL stereo viewport mode (experimental; incomplete)\n")	\
224 		VERB("                                    1: above/below half-height format\n")	\
225 		VERB("                                    2: side/by/side half-width format\n")	\
226 		VERB("                                    3: side/by/side half-size format, normal aspect ratio\n") \
227 		VERB("                                    4: above/below format with external sync blank interval\n")	\
228 		))	\
229 	)	\
230 	DXX_if_defined_01(DXX_USE_UDP, (	\
231 		VERB("\n Multiplayer:\n\n")	\
232 		VERB("  -udp_hostaddr <s>             Use IP address/Hostname <s> for manual game joining\n\t\t\t\t(default: %s)\n", UDP_MANUAL_ADDR_DEFAULT)	\
233 		VERB("  -udp_hostport <n>             Use UDP port <n> for manual game joining (default: %hu)\n", UDP_PORT_DEFAULT)	\
234 		VERB("  -udp_myport <n>               Set my own UDP port to <n> (default: %hu)\n", UDP_PORT_DEFAULT)	\
235 		DXX_if_defined_01(DXX_USE_TRACKER, (	\
236 			VERB("  -no-tracker                   Disable tracker (unless overridden by later -tracker_hostaddr)\n")	\
237 			VERB("  -tracker_hostaddr <n>         Address of tracker server to register/query games to/from\n\t\t\t\t(default: %s)\n", TRACKER_ADDR_DEFAULT)	\
238 			VERB("  -tracker_hostport <n>         Port of tracker server to register/query games to/from\n\t\t\t\t(default: %hu)\n", TRACKER_PORT_DEFAULT)	\
239 		))	\
240 	))	\
241 	DXX_if_defined(EDITOR, (	\
242 		VERB("\n Editor:\n\n")	\
243 		DXX_COMMAND_LINE_HELP_D1(	\
244 			VERB("  -nobm                         Don't load BITMAPS.TBL and BITMAPS.BIN - use internal data\n")	\
245 		)	\
246 		DXX_COMMAND_LINE_HELP_D2(	\
247 			VERB("  -autoload <s>                 Autoload level <s> in the editor\n")	\
248 			VERB("  -macdata                      Read and write Mac data files in editor (swap colors)\n")	\
249 			VERB("  -hoarddata                    Make the Hoard ham file from some files, then exit\n")	\
250 		)	\
251 	))	\
252 	VERB("\n Debug (use only if you know what you're doing):\n\n")	\
253 	VERB("  -debug                        Enable debugging output.\n")	\
254 	VERB("  -verbose                      Enable verbose output.\n")	\
255 	VERB("  -safelog                      Write gamelog.txt unbuffered.\n\t\t\t\tUse to keep helpful output to trace program crashes.\n")	\
256 	VERB("  -norun                        Bail out after initialization\n")	\
257 	VERB("  -no-grab                      Never grab keyboard/mouse\n")	\
258 	VERB("  -renderstats                  Enable renderstats info by default\n")	\
259 	VERB("  -text <s>                     Specify alternate .tex file\n")	\
260 	VERB("  -showmeminfo                  Show memory statistics\n")	\
261 	VERB("  -nodoublebuffer               Disable Doublebuffering\n")	\
262 	VERB("  -bigpig                       Use uncompressed RLE bitmaps\n")	\
263 	VERB("  -16bpp                        Use 16Bpp instead of 32Bpp\n")	\
264 	DXX_COMMAND_LINE_HELP_OGL(	\
265 		VERB("  -gl_oldtexmerge               Use old texmerge, uses more ram, but might be faster\n")	\
266 		VERB("  -gl_intensity4_ok <n>         Override DbgGlIntensity4Ok (default: 1)\n")	\
267 		VERB("  -gl_luminance4_alpha4_ok <n>  Override DbgGlLuminance4Alpha4Ok (default: 1)\n")	\
268 		VERB("  -gl_rgba2_ok <n>              Override DbgGlRGBA2Ok (default: 1)\n")	\
269 		VERB("  -gl_readpixels_ok <n>         Override DbgGlReadPixelsOk (default: 1)\n")	\
270 		VERB("  -gl_gettexlevelparam_ok <n>   Override DbgGlGetTexLevelParamOk (default: 1)\n")	\
271 	)	\
272 	DXX_COMMAND_LINE_HELP_SDL(	\
273 		VERB("  -tmap <s>                     Select texmapper <s> to use\n\t\t\t\t(default: c, available: c, fp, quad)\n")	\
274 		VERB("  -hwsurface                    Use SDL HW Surface\n")	\
275 		VERB("  -asyncblit                    Use queued blits over SDL. Can speed up rendering\n")	\
276 	)	\
277 	VERB("\n Help:\n\n")	\
278 	VERB("  -help, -h, -?, ?             View this help screen\n")	\
279 	VERB("\n\n")	\
280 
281 	printf(DXX_COMMAND_LINE_HELP(DXX_COMMAND_LINE_HELP_FMT) DXX_COMMAND_LINE_HELP(DXX_COMMAND_LINE_HELP_ARG));
282 }
283 
284 int Quitting = 0;
285 
286 }
287 
288 namespace dcx {
289 
290 // Default event handler for everything except the editor
standard_handler(const d_event & event)291 window_event_result standard_handler(const d_event &event)
292 {
293 	int key;
294 
295 	if (Quitting)
296 	{
297 		window *wind = window_get_front();
298 		if (!wind)
299 			return window_event_result::ignored;	// finished quitting
300 
301 		if (wind == Game_wind)
302 		{
303 			Quitting = 0;
304 			const auto choice = nm_messagebox_str(menu_title{nullptr}, nm_messagebox_tie(TXT_YES, TXT_NO), menu_subtitle{TXT_ABORT_GAME});
305 			if (choice != 0)
306 				return window_event_result::handled;	// aborted quitting
307 			else
308 			{
309 				CGameArg.SysAutoDemo = false;
310 				Quitting = 1;
311 			}
312 		}
313 
314 		// Close front window, let the code flow continue until all windows closed or quit cancelled
315 		if (!window_close(wind))
316 		{
317 			Quitting = 0;
318 			return window_event_result::handled;
319 		}
320 
321 		return window_event_result::deleted;	// tell the event system we deleted some window
322 	}
323 
324 	switch (event.type)
325 	{
326 		case EVENT_MOUSE_BUTTON_DOWN:
327 		case EVENT_MOUSE_BUTTON_UP:
328 			// No window selecting
329 			// We stay with the current one until it's closed/hidden or another one is made
330 			// Not the case for the editor
331 			break;
332 
333 		case EVENT_KEY_COMMAND:
334 			key = event_key_get(event);
335 
336 			switch (key)
337 			{
338 #if DXX_USE_SCREENSHOT
339 #ifdef macintosh
340 				case KEY_COMMAND + KEY_SHIFTED + KEY_3:
341 #endif
342 				case KEY_PRINT_SCREEN:
343 				{
344 					gr_set_default_canvas();
345 					save_screen_shot(0);
346 					return window_event_result::handled;
347 				}
348 #endif
349 
350 				case KEY_ALTED+KEY_ENTER:
351 				case KEY_ALTED+KEY_PADENTER:
352 					if (Game_wind)
353 						if (window_get_front() == Game_wind)
354 							return window_event_result::ignored;
355 					gr_toggle_fullscreen();
356 #if SDL_MAJOR_VERSION == 2
357 					{
358 						/* Hack to force the canvas to adjust to the new
359 						 * dimensions.  Without this, the canvas
360 						 * continues to use the old window size until
361 						 * the hack of calling `init_cockpit` from
362 						 * `game_handler` fixes the dimensions.  If the
363 						 * window became bigger, the game fails to draw
364 						 * in the full new area.  If the window became
365 						 * smaller, part of the game is outside the
366 						 * cropped area.
367 						 *
368 						 * If the automap is open, the view is still
369 						 * wrong, since the automap uses its own private
370 						 * canvas.  That will need to be fixed
371 						 * separately.  Ideally, the whole window
372 						 * system would be reworked to provide a general
373 						 * notification to every interested canvas when
374 						 * the top level window resizes.
375 						 */
376 						auto sm = Screen_mode;
377 						Screen_mode = SCREEN_GAME;
378 						init_cockpit();
379 						Screen_mode = sm;
380 					}
381 #endif
382 					return window_event_result::handled;
383 
384 #if defined(__APPLE__) || defined(macintosh)
385 				case KEY_COMMAND+KEY_Q:
386 					// Alt-F4 already taken, too bad
387 					Quitting = 1;
388 					return window_event_result::handled;
389 #endif
390 				case KEY_SHIFTED + KEY_ESC:
391 					con_showup(Controls);
392 					return window_event_result::handled;
393 			}
394 			break;
395 
396 		case EVENT_WINDOW_DRAW:
397 		case EVENT_IDLE:
398 			//see if redbook song needs to be restarted
399 #if DXX_USE_SDL_REDBOOK_AUDIO
400 			RBACheckFinishedHook();
401 #endif
402 			return window_event_result::handled;
403 
404 		case EVENT_QUIT:
405 #if DXX_USE_EDITOR
406 			if (SafetyCheck())
407 #endif
408 				Quitting = 1;
409 			return window_event_result::handled;
410 
411 		default:
412 			break;
413 	}
414 
415 	return window_event_result::ignored;
416 }
417 
418 #if DXX_HAVE_POISON
d_interface_unique_state()419 d_interface_unique_state::d_interface_unique_state()
420 {
421 	DXX_MAKE_VAR_UNDEFINED(PilotName);
422 }
423 #endif
424 
update_window_title()425 void d_interface_unique_state::update_window_title()
426 {
427 #if SDL_MAJOR_VERSION == 1
428 	if (!PilotName[0u])
429 		SDL_WM_SetCaption(DESCENT_VERSION, DXX_SDL_WINDOW_CAPTION);
430 	else
431 	{
432 		const char *const pilot = PilotName;
433 		std::array<char, 80> wm_caption_name, wm_caption_iconname;
434 		snprintf(wm_caption_name.data(), wm_caption_name.size(), "%s: %s", DESCENT_VERSION, pilot);
435 		snprintf(wm_caption_iconname.data(), wm_caption_iconname.size(), "%s: %s", DXX_SDL_WINDOW_CAPTION, pilot);
436 		SDL_WM_SetCaption(wm_caption_name.data(), wm_caption_iconname.data());
437 	}
438 #endif
439 }
440 
441 }
442 
443 namespace dsx {
444 
445 #define PROGNAME argv[0]
446 #define DXX_RENAME_IDENTIFIER2(I,N)	I##$##N
447 #define DXX_RENAME_IDENTIFIER(I,N)	DXX_RENAME_IDENTIFIER2(I,N)
448 #define argc	DXX_RENAME_IDENTIFIER(argc_gc, DXX_git_commit)
449 #define argv	DXX_RENAME_IDENTIFIER(argv_gd$b32, DXX_git_describe)
450 
451 //	DESCENT by Parallax Software
452 //	DESCENT II by Parallax Software
453 //	(varies based on preprocessor options)
454 //		Descent Main
455 
main(int argc,char * argv[])456 static int main(int argc, char *argv[])
457 {
458 	if (!PHYSFSX_init(argc, argv))
459 		return 1;
460 	con_init();  // Initialise the console
461 
462 	setbuf(stdout, NULL); // unbuffered output via printf
463 #ifdef _WIN32
464 	freopen( "CON", "w", stdout );
465 	freopen( "CON", "w", stderr );
466 #endif
467 
468 	if (CGameArg.SysShowCmdHelp) {
469 		print_commandline_help();
470 
471 		return(0);
472 	}
473 
474 	printf("\nType '%s -help' for a list of command-line options.\n\n", PROGNAME);
475 
476 	PHYSFSX_listSearchPathContent();
477 
478 	if (!PHYSFSX_checkSupportedArchiveTypes())
479 		return(0);
480 
481 #if defined(DXX_BUILD_DESCENT_I)
482 	const auto descent_hog = make_PHYSFSX_ComputedPathMount("descent.hog", physfs_search_path::append);
483 #define DXX_NAME_NUMBER	"1"
484 #define DXX_HOGFILE_NAMES	"descent.hog"
485 #elif defined(DXX_BUILD_DESCENT_II)
486 	const auto descent_hog = make_PHYSFSX_ComputedPathMount("descent2.hog", "d2demo.hog", physfs_search_path::append);
487 #define DXX_NAME_NUMBER	"2"
488 #define DXX_HOGFILE_NAMES	"descent2.hog or d2demo.hog"
489 #endif
490 	if (!descent_hog)
491 	{
492 #if defined(__unix__) && !defined(__APPLE__)
493 #define DXX_HOGFILE_PROGRAM_DATA_DIRECTORY	\
494 			      "\t$HOME/.d" DXX_NAME_NUMBER "x-rebirth\n"	\
495 					DXX_HOGFILE_SHAREPATH_INDENTED
496 #if DXX_USE_SHAREPATH
497 #define DXX_HOGFILE_SHAREPATH_INDENTED	\
498 			      "\t" DXX_SHAREPATH "\n"
499 #else
500 #define DXX_HOGFILE_SHAREPATH_INDENTED
501 #endif
502 #elif (defined(__APPLE__) && defined(__MACH__))
503 #define DXX_HOGFILE_PROGRAM_DATA_DIRECTORY	\
504 			      "\t~/Library/Preferences/D" DXX_NAME_NUMBER "X Rebirth\n"
505 #else
506 #define DXX_HOGFILE_PROGRAM_DATA_DIRECTORY	\
507 				  "\tDirectory containing D" DXX_NAME_NUMBER "X\n"
508 #endif
509 #if (defined(__APPLE__) && defined(__MACH__)) || defined(macintosh)
510 #define DXX_HOGFILE_APPLICATION_BUNDLE	\
511 				  "\tIn 'Resources' inside the application bundle\n"
512 #else
513 #define DXX_HOGFILE_APPLICATION_BUNDLE	""
514 #endif
515 #define DXX_MISSING_HOGFILE_ERROR_TEXT	\
516 		"Could not find a valid hog file (" DXX_HOGFILE_NAMES ")\nPossible locations are:\n"	\
517 		DXX_HOGFILE_PROGRAM_DATA_DIRECTORY	\
518 		"\tIn a subdirectory called 'data'\n"	\
519 		DXX_HOGFILE_APPLICATION_BUNDLE	\
520 		"Or use the -hogdir option to specify an alternate location."
521 		UserError(DXX_MISSING_HOGFILE_ERROR_TEXT);
522 	}
523 
524 #if defined(DXX_BUILD_DESCENT_I)
525 	switch (PHYSFSX_fsize("descent.hog"))
526 	{
527 		case D1_MAC_SHARE_MISSION_HOGSIZE:
528 		case D1_MAC_MISSION_HOGSIZE:
529 			MacHog = 1;	// used for fonts and the Automap
530 			break;
531 	}
532 #endif
533 
534 	load_text();
535 
536 	//print out the banner title
537 #if defined(DXX_BUILD_DESCENT_I)
538 	con_printf(CON_NORMAL, "%s  %s", DESCENT_VERSION, g_descent_build_datetime); // D1X version
539 	con_puts(CON_NORMAL, "This is a MODIFIED version of Descent, based on " BASED_VERSION ".");
540 	con_puts(CON_NORMAL, TXT_COPYRIGHT);
541 	con_puts(CON_NORMAL, TXT_TRADEMARK);
542 	con_puts(CON_NORMAL, "Copyright (C) 2005-2013 Christian Beckhaeuser, 2013-2017 Kp");
543 #elif defined(DXX_BUILD_DESCENT_II)
544 	con_printf(CON_NORMAL, "%s%s  %s", DESCENT_VERSION, PHYSFSX_exists(MISSION_DIR "d2x.hog",1) ? "  Vertigo Enhanced" : "", g_descent_build_datetime); // D2X version
545 	con_puts(CON_NORMAL, "This is a MODIFIED version of Descent 2, based on " BASED_VERSION ".");
546 	con_puts(CON_NORMAL, TXT_COPYRIGHT);
547 	con_puts(CON_NORMAL, TXT_TRADEMARK);
548 	con_puts(CON_NORMAL, "Copyright (C) 1999 Peter Hawkins, 2002 Bradley Bell, 2005-2013 Christian Beckhaeuser, 2013-2017 Kp");
549 #endif
550 
551 	if (CGameArg.DbgVerbose)
552 	{
553 		{
554 			PHYSFS_Version vc, vl;
555 			PHYSFS_VERSION(&vc);
556 			PHYSFS_getLinkedVersion(&vl);
557 			con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with PhysFS %u.%u.%u; loaded with PhysFS %u.%u.%u", vc.major, vc.minor, vc.patch, vl.major, vl.minor, vl.patch);
558 		}
559 		{
560 			SDL_version vc;
561 			SDL_VERSION(&vc);
562 #if SDL_MAJOR_VERSION == 1
563 			const auto vl = SDL_Linked_Version();
564 #else
565 			SDL_version vlv;
566 			const auto vl = &vlv;
567 			SDL_GetVersion(vl);
568 #endif
569 			con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with libSDL %u.%u.%u; loaded with libSDL %u.%u.%u", vc.major, vc.minor, vc.patch, vl->major, vl->minor, vl->patch);
570 		}
571 #if DXX_USE_SDLIMAGE
572 		{
573 			SDL_version vc;
574 			SDL_IMAGE_VERSION(&vc);
575 			const auto vl = IMG_Linked_Version();
576 			con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with SDL_image %u.%u.%u; loaded with SDL_image %u.%u.%u", vc.major, vc.minor, vc.patch, vl->major, vl->minor, vl->patch);
577 		}
578 #endif
579 #if DXX_USE_SDLMIXER
580 		{
581 			SDL_version vc;
582 			SDL_MIXER_VERSION(&vc);
583 			const auto vl = Mix_Linked_Version();
584 			con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with SDL_mixer %u.%u.%u; loaded with SDL_mixer %u.%u.%u", vc.major, vc.minor, vc.patch, vl->major, vl->minor, vl->patch);
585 		}
586 #endif
587 #if DXX_USE_SCREENSHOT_FORMAT_PNG
588 		con_printf(CON_VERBOSE, "D" DXX_NAME_NUMBER "X-Rebirth built with libpng version " PNG_LIBPNG_VER_STRING "; loaded with libpng version %s", png_get_libpng_ver(nullptr));
589 #endif
590 		con_puts(CON_VERBOSE, TXT_VERBOSE_1);
591 	}
592 
593 	ReadConfigFile();
594 
595 	PHYSFSX_addArchiveContent();
596 
597 	const auto &&arch_atexit_result = arch_init();
598 	/* This variable exists for the side effects that occur when it is
599 	 * destroyed.  clang-9 fails to recognize those side effects as a
600 	 * "use" and warns that the variable is unused.  Cast it to void to
601 	 * create a "use" to suppress the warning.
602 	 */
603 	(void)arch_atexit_result;
604 
605 #if !DXX_USE_OGL
606 	select_tmap(CGameArg.DbgTexMap);
607 
608 #if defined(DXX_BUILD_DESCENT_II)
609 	Lighting_on = 1;
610 #endif
611 #endif
612 
613 	con_puts(CON_VERBOSE, "Going into graphics mode...");
614 #if DXX_USE_OGL
615 	gr_set_mode_from_window_size();
616 #else
617 	gr_set_mode(Game_screen_mode);
618 #endif
619 
620 	// Load the palette stuff. Returns non-zero if error.
621 	con_puts(CON_DEBUG, "Initializing palette system...");
622 #if defined(DXX_BUILD_DESCENT_I)
623 	gr_use_palette_table( "PALETTE.256" );
624 #elif defined(DXX_BUILD_DESCENT_II)
625 	gr_use_palette_table(D2_DEFAULT_PALETTE );
626 #endif
627 
628 	con_puts(CON_DEBUG, "Initializing font system...");
629 	gamefont_init();	// must load after palette data loaded.
630 
631 #if defined(DXX_BUILD_DESCENT_II)
632 	con_puts(CON_DEBUG, "Initializing movie libraries...");
633 	auto &&loaded_builtin_movies = init_movies();		//init movie libraries
634 	/* clang does not recognize that creating the variable for the
635 	 * purpose of extending the returned value's lifetime is a use.  Add
636 	 * an explicit dummy use to silence its bogus -Wunused-variable
637 	 * warning.
638 	 */
639 	(void)loaded_builtin_movies;
640 #endif
641 
642 	show_titles();
643 
644 	set_screen_mode(SCREEN_MENU);
645 #ifdef DEBUG_MEMORY_ALLOCATIONS
646 	/* Memdebug runs before global destructors, so it incorrectly
647 	 * reports as leaked any allocations that would be freed by a global
648 	 * destructor.  This local will force the newmenu globals to be
649 	 * reset before memdebug scans, which prevents memdebug falsely
650 	 * reporting them as leaked.
651 	 *
652 	 * External tools, such as Valgrind, know to run global destructors
653 	 * before checking for leaks, so this hack is only necessary when
654 	 * memdebug is used.
655 	 */
656 	struct hack_free_global_backgrounds
657 	{
658 		~hack_free_global_backgrounds()
659 		{
660 			newmenu_free_background();
661 		}
662 	};
663 	hack_free_global_backgrounds hack_free_global_background;
664 #endif
665 
666 	con_puts(CON_DEBUG, "Doing gamedata_init...");
667 	gamedata_init();
668 
669 #if defined(DXX_BUILD_DESCENT_II)
670 #if DXX_USE_EDITOR
671 	if (GameArg.EdiSaveHoardData) {
672 		save_hoard_data();
673 		exit(1);
674 	}
675 	#endif
676 #endif
677 
678 	if (CGameArg.DbgNoRun)
679 		return(0);
680 
681 	con_puts(CON_DEBUG, "Initializing texture caching system...");
682 	texmerge_init();		// 10 cache bitmaps
683 
684 #if defined(DXX_BUILD_DESCENT_II)
685 	piggy_init_pigfile("groupa.pig");	//get correct pigfile
686 #endif
687 
688 	con_puts(CON_DEBUG, "Running game...");
689 	init_game();
690 
691 #if defined(DXX_BUILD_DESCENT_I)
692 	key_flush();
693 #endif
694 	{
695 		InterfaceUniqueState.PilotName.fill(0);
696 		if (!CGameArg.SysPilot.empty())
697 		{
698 			char filename[sizeof(PLAYER_DIRECTORY_TEXT) + CALLSIGN_LEN + 4];
699 
700 			/* Step over the literal PLAYER_DIRECTORY_TEXT when it is
701 			 * present.  Point at &filename[0] when
702 			 * PLAYER_DIRECTORY_TEXT is absent.
703 			 */
704 			const auto b = &filename[-CGameArg.SysUsePlayersDir];
705 			snprintf(filename, sizeof(filename), PLAYER_DIRECTORY_STRING("%.12s"), CGameArg.SysPilot.c_str());
706 			/* The pilot name is never used after this.  Clear it to
707 			 * free the allocated memory, if any.
708 			 */
709 			CGameArg.SysPilot.clear();
710 			auto p = b;
711 			for (const auto &facet = std::use_facet<std::ctype<char>>(std::locale::classic()); char &c = *p; ++p)
712 			{
713 				c = facet.tolower(static_cast<uint8_t>(c));
714 			}
715 			auto j = p - filename;
716 			if (j < sizeof(filename) - 4 && (j <= 4 || strcmp(&filename[j - 4], ".plr"))) // if player hasn't specified .plr extension in argument, add it
717 			{
718 				strcpy(&filename[j], ".plr");
719 				j += 4;
720 			}
721 			if(PHYSFSX_exists(filename,0))
722 			{
723 				InterfaceUniqueState.PilotName.copy(b, std::distance(b, &filename[j - 4]));
724 				InterfaceUniqueState.update_window_title();
725 				read_player_file();
726 				WriteConfigFile();
727 			}
728 		}
729 	}
730 
731 #if defined(DXX_BUILD_DESCENT_II)
732 #if DXX_USE_EDITOR
733 	if (!GameArg.EdiAutoLoad.empty()) {
734 		/* Any number >= FILENAME_LEN works */
735 		Current_mission->level_names[0].copy_if(GameArg.EdiAutoLoad.c_str(), GameArg.EdiAutoLoad.size());
736 		LoadLevel(1, 1);
737 	}
738 	else
739 #endif
740 #endif
741 	{
742 		Game_mode = {};
743 		DoMenu();
744 	}
745 
746 	while (window_get_front())
747 		// Send events to windows and the default handler
748 		event_process();
749 
750 	// Tidy up - avoids a crash on exit
751 	{
752 		window *wind;
753 
754 		show_menus();
755 		while ((wind = window_get_front()))
756 			window_close(wind);
757 	}
758 
759 	WriteConfigFile();
760 
761 	con_puts(CON_DEBUG, "Cleanup...");
762 	close_game();
763 	texmerge_close();
764 	gamedata_close();
765 	gamefont_close();
766 	Current_mission.reset();
767 	PHYSFSX_removeArchiveContent();
768 
769 	return(0);		//presumably successful exit
770 }
771 
772 }
773 
main(int argc,char * argv[])774 int main(int argc, char *argv[])
775 {
776 	mem_init();
777 #if DXX_WORDS_NEED_ALIGNMENT
778 	prctl(PR_SET_UNALIGN, PR_UNALIGN_NOPRINT, 0, 0, 0);
779 #endif
780 #ifdef WIN32
781 	void d_set_exception_handler();
782 	d_set_exception_handler();
783 #endif
784 	return dsx::main(argc, argv);
785 }
786 
787 #undef argv
788 #undef argc
789