1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <signal.h>
5 #include <time.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 
9 #ifdef __MSW__
10 # include <windows.h>
11 #else
12 # include <sys/time.h>
13 # include <unistd.h>
14 #endif
15 
16 #include <GL/gl.h>
17 #include "../include/disk.h"
18 #include "../include/strexp.h"
19 #include "../include/string.h"
20 #include "gw.h"
21 
22 #include "menu.h"
23 #include "messages.h"
24 #include "textinput.h"
25 #include "sarreality.h"
26 #include "obj.h"
27 #include "simmanage.h"
28 #include "weather.h"
29 #include "gctl.h"
30 #include "sound.h"
31 #include "mission.h"
32 #include "sar.h"
33 #include "sarsplash.h"
34 #include "sarinstall.h"
35 #include "sardraw.h"
36 #include "sardrawselect.h"
37 #include "sarkey.h"
38 #include "sarscreenshot.h"
39 #include "sartime.h"
40 #include "sarmusic.h"
41 #include "optionio.h"
42 #include "playerstatio.h"
43 #include "texturelistio.h"
44 #include "sceneio.h"
45 #include "sarmenuop.h"
46 #include "sarmenucb.h"
47 #include "sarmenubuild.h"
48 #include "sarmenumanage.h"
49 #include "sarmenucodes.h"
50 #include "sarsimend.h"
51 #include "config.h"
52 
53 #include "fonts/6x10.fnt"
54 #include "fonts/7x14.fnt"
55 #include "fonts/banner.fnt"
56 #include "fonts/menu.fnt"
57 
58 
59 void SARHandleSignal(int s);
60 
61 int SARInitGCTL(sar_core_struct *core_ptr);
62 
63 void SARFullScreen(sar_core_struct *core_ptr);
64 void SARResolution(gw_display_struct *display, int width, int height);
65 void SARResolutionIncrease(gw_display_struct *display);
66 void SARResolutionDecrease(gw_display_struct *display);
67 
68 int SARLoadProgressCB(void *ptr, long pos, long size);
69 
70 void SARTextInputCBSendMessage(const char *value, void *data);
71 void SARTextInputCBQuitSimulation(const char *value, void *data);
72 
73 void SARDrawCB(int ctx_num, void *ptr);
74 void SARKeyBoardCB(void *ptr, int c, Boolean state, unsigned long t);
75 void SARPointerCB(
76 	int ctx_num, void *ptr,
77 	int x, int y, gw_event_type type, int btn_num, unsigned long t
78 );
79 void SARReshapeCB(int ctx_num, void *ptr, int x, int y, int width, int height);
80 void SARVisibilityCB(int ctx_num, void *ptr, gw_visibility v);
81 void SARSaveYourselfCB(int ctx_num, void *ptr);
82 void SARCloseCB(int ctx_num, void *ptr, void *data);
83 void SARResetTimmersCB(sar_core_struct *core_ptr, time_t t_new);
84 
85 sar_core_struct *SARInit(int argc, char **argv);
86 void SARManage(void *ptr);
87 void SARShutdown(sar_core_struct *core_ptr);
88 
89 
90 #define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
91 #define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
92 #define ATOF(s)		(((s) != NULL) ? (float)atof(s) : 0.0f)
93 #define STRDUP(s)	(((s) != NULL) ? strdup(s) : NULL)
94 
95 #define MAX(a,b)	(((a) > (b)) ? (a) : (b))
96 #define MIN(a,b)	(((a) < (b)) ? (a) : (b))
97 #define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
98 #define STRLEN(s)	(((s) != NULL) ? (int)strlen(s) : 0)
99 #define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : 1)
100 
101 #define RADTODEG(r)	((r) * 180.0 / PI)
102 #define DEGTORAD(d)	((d) * PI / 180.0)
103 
104 
105 static int segfault_count;
106 
107 
108 sar_dname_struct dname;
109 sar_fname_struct fname;
110 sar_next_struct next;
111 
112 float debug_value;
113 
114 int runlevel;
115 
116 time_t	cur_millitime,
117 	cur_systime,
118 	lapsed_millitime;
119 
120 float	time_compensation,
121 	time_compression;
122 
123 
124 /*
125  *	Records visibility of window.
126  */
127 static int is_visible = 0;
128 
129 
130 
131 
132 /*
133  *	Signal handler.
134  */
SARHandleSignal(int s)135 void SARHandleSignal(int s)
136 {
137 #if defined(__MSW__)
138 
139 #else
140 	switch(s)
141 	{
142 #ifdef SIGINT
143 	  case SIGINT:
144 	    runlevel = 1;
145 	    break;
146 #endif
147 #ifdef SIGTERM
148 	  case SIGTERM:
149 	    exit(0);
150 	    break;
151 #endif
152 #ifdef SIGQUIT
153 	  case SIGQUIT:
154 	    exit(0);
155 	    break;
156 #endif
157 #ifdef SIGSEGV
158 	  case SIGSEGV:
159 	    fprintf(
160 		stderr,
161 		PROG_NAME_FULL " triggered a segmentation fault.\n"
162 	    );
163 	    exit(1);
164 	    break;
165 #endif
166 	}
167 #endif
168 }
169 
170 /*
171  *	(Re)initializes the Game Controller based on values from the
172  *	current options.
173  */
SARInitGCTL(sar_core_struct * core_ptr)174 int SARInitGCTL(sar_core_struct *core_ptr)
175 {
176 	int i, total_joysticks;
177 	void *w, *rc;
178 	gctl_js_connection js_connection;
179 	gctl_struct *gctl;
180 	gctl_values_struct *v;
181 	gw_display_struct *display = core_ptr->display;
182 	const sar_option_struct *opt = &core_ptr->option;
183 
184 	/* Shutdown Game Controller as needed */
185 	GCtlDelete(core_ptr->gctl);
186 	core_ptr->gctl = NULL;
187 
188 	/* Get pointers to the Window and Rendering Context of the
189 	 * first GL Context, this is needed by the Game Controller
190 	 */
191 	if(GWContextGet(
192 	    display, GWContextCurrent(display),
193 	    &w, &rc,
194 	    NULL, NULL, NULL, NULL
195 	))
196 	    return(-1);
197 
198 	/* Check how many joysticks are specified by the options by
199 	 * checking if axis roles defined for each joystick in question
200 	 *
201 	 * Any joystick with one or more axis roles set means the stick
202 	 * is enabled
203 	 */
204 	total_joysticks = 0;
205 	if(opt->gctl_js0_axis_roles != 0)
206 	    total_joysticks++;
207 	if(opt->gctl_js1_axis_roles != 0)
208 	    total_joysticks++;
209 
210 
211 	/* Set up Game Controller Values */
212 	v = GCTL_VALUES(calloc(1, sizeof(gctl_values_struct)));
213 
214 	/* Controllers; Keyboard, Joystick, and/or Pointer */
215 	v->controllers = opt->gctl_controllers;
216 
217 	/* Options (none at this time) */
218 	v->options = opt->gctl_options;
219 
220 	/* Set up Game Controller Joystick Values for each joystick
221 	 * that is enabled
222 	 */
223 	v->total_joysticks = total_joysticks;
224 	if(total_joysticks > 0)
225 	    v->joystick = (gctl_values_js_struct *)calloc(
226 		total_joysticks, sizeof(gctl_values_js_struct)
227 	    );
228 	else
229 	    v->joystick = NULL;
230 	/* Joystick #1 */
231 	i = 0;
232 	js_connection = opt->js0_connection;
233 	if((total_joysticks > i) && (opt->gctl_js0_axis_roles != 0))
234 	{
235 	    gctl_values_js_struct *js_v = &v->joystick[i];
236 
237 	    js_v->priority = opt->js_priority;
238 	    switch(js_connection)
239 	    {
240 	      case GCTL_JS_CONNECTION_USB:
241 		js_v->device = STRDUP("/dev/input/js0");
242 		break;
243 	      case GCTL_JS_CONNECTION_STANDARD:
244 		js_v->device = STRDUP("/dev/js0");
245 		break;
246 	    }
247 	    js_v->connection = js_connection;
248 	    js_v->window = w;
249 
250 	    js_v->axis_role = opt->gctl_js0_axis_roles;
251 
252 	    js_v->button_rotate = opt->js0_btn_rotate;
253 	    js_v->button_air_brakes = opt->js0_btn_air_brakes;
254 	    js_v->button_wheel_brakes = opt->js0_btn_wheel_brakes;
255 	    js_v->button_zoom_in = opt->js0_btn_zoom_in;
256 	    js_v->button_zoom_out = opt->js0_btn_zoom_out;
257 	    js_v->button_hoist_up = opt->js0_btn_hoist_up;
258 	    js_v->button_hoist_down = opt->js0_btn_hoist_down;
259 	}
260 	/* Joystick #2 */
261 	i = 1;
262 	js_connection = opt->js1_connection;
263 	if((total_joysticks > i) && (opt->gctl_js1_axis_roles != 0))
264 	{
265 	    gctl_values_js_struct *js_v = &v->joystick[i];
266 
267 	    js_v->priority = opt->js_priority;
268 	    switch(js_connection)
269 	    {
270 	      case GCTL_JS_CONNECTION_USB:
271 		js_v->device = STRDUP("/dev/input/js1");
272 		break;
273 	      case GCTL_JS_CONNECTION_STANDARD:
274 		js_v->device = STRDUP("/dev/js1");
275 		break;
276 	    }
277 	    js_v->connection = js_connection;
278 	    js_v->window = w;
279 
280 	    js_v->axis_role = opt->gctl_js1_axis_roles;
281 
282 	    js_v->button_rotate = opt->js1_btn_rotate;
283 	    js_v->button_air_brakes = opt->js1_btn_air_brakes;
284 	    js_v->button_wheel_brakes = opt->js1_btn_wheel_brakes;
285 	    js_v->button_zoom_in = opt->js1_btn_zoom_in;
286 	    js_v->button_zoom_out = opt->js1_btn_zoom_out;
287 	    js_v->button_hoist_up = opt->js1_btn_hoist_up;
288 	    js_v->button_hoist_down = opt->js1_btn_hoist_down;
289 	}
290 
291 
292 	/* Initialize the Game Controller */
293 	core_ptr->gctl = gctl = GCtlNew(v);
294 
295 	/* Delete Game Controller Values */
296 	for(i = 0; i < v->total_joysticks; i++)
297 	{
298 	    gctl_values_js_struct *js_v = &v->joystick[i];
299 	    free(js_v->device);
300 	}
301 	free(v->joystick);
302 	free(v);
303 
304 
305 	/* Error initializing the Game Controller? */
306 	if(gctl == NULL)
307 	{
308 	    GWOutputMessage(
309 		core_ptr->display, GWOutputMessageTypeError,
310 		"Error Initializing Controller",
311 		GCtlGetError(),
312 "The above error was encountered when attempting\n\
313 to initialize the game controller. Please check\n\
314 if your controllers are set up properly or try\n\
315 a different game controller setting."
316 	    );
317 	    return(-1);
318 	}
319 	else
320 	{
321 	    return(0);
322 	}
323 }
324 
325 
326 /*
327  *	Toggles the switching between full screen and GUI modes.
328  */
SARFullScreen(sar_core_struct * core_ptr)329 void SARFullScreen(sar_core_struct *core_ptr)
330 {
331 	gw_display_struct *display = core_ptr->display;
332 	sar_option_struct *opt = &core_ptr->option;
333 
334 	/* Currenty in full screen mode? */
335 	if(GWContextIsFullScreen(display))
336 	{
337 	    /* Currently in full screen mode, so switch back to GUI
338 	     * mode.
339 	     */
340 	    GWContextFullScreen(display, False);
341 	    opt->last_fullscreen = False;
342 	}
343 	else
344 	{
345 	    /* Switch to full screen mode, first trying with the
346 	     * current size of the GL context.
347 	     */
348 	    int status = GWContextFullScreen(display, True);
349 	    if(status == 0)
350 	    {
351 		/* Successfully switched to full screen mode */
352 		opt->last_fullscreen = True;
353 	    }
354 	    else if(status == -2)
355 	    {
356 		/* No full screen mode that matches the current size
357 		 * of the GL context, so use the next closest size.
358 		 *
359 		 * Here we will get a list of valid vidmode sizes (if
360 		 * any) and set the GL context to the size of the
361 		 * closest (equal or smaller vidmode size) and then
362 		 * switch to full screen.
363 		 */
364 		int i, n, width, height;
365 		gw_vidmode_struct	*vm = GWVidModesGet(display, &n),
366 					*vm_ptr;
367 		int nwidth = 0, nheight = 0;
368 
369 		GWContextGet(
370 		    display, GWContextCurrent(display),
371 		    NULL, NULL,
372 		    NULL, NULL,
373 		    &width, &height
374 		);
375 
376 		/* Iterate through vidmodes, finding one that is closest
377 		 * (equal or smaller) than the size of the GL context
378 		 * and store its size as nwidth and nheight.
379 		 */
380 		for(i = 0; i < n; i++)
381 		{
382 		    vm_ptr = &vm[i];
383 		    if((vm_ptr->width < width) &&
384 		       (vm_ptr->height < height)
385 		    )
386 		    {
387 			nwidth = MAX(vm_ptr->width, nwidth);
388 			nheight = MAX(vm_ptr->height, nheight);
389 		    }
390 		}
391 		GWVidModesFree(vm, n);
392 
393 		/* If still unable to find useable video mode then
394 		 * use most conservitive resolution 320x240.
395 		 */
396 		if((nwidth == 0) || (nheight == 0))
397 		{
398 		    nwidth = 320;
399 		    nheight = 240;
400 		}
401 		/* Adjust context to new size that is hopefully suitable
402 		 * for full screen mode.
403 		 */
404 		GWContextSize(display, nwidth, nheight);
405 
406 		/* Attempt to switch to full screen mode with the new
407 		 * context size.
408 		 */
409 		status = GWContextFullScreen(display, True);
410 		if(status == 0)
411 		{
412 		    /* Successfully switched to full screen mode */
413 		    opt->last_fullscreen = True;
414 		}
415 		else if(status == -2)
416 		{
417 		    /* Still no full screen mode that matches the
418 		     * current size, so print message.
419 		     */
420 		    int width, height;
421 		    char *buf = (char *)malloc(4 * 80 * sizeof(char));
422 		    GWContextGet(
423 			display, GWContextCurrent(display),
424 			NULL, NULL,
425 			NULL, NULL,
426 			&width, &height
427 		    );
428 		    sprintf(buf,
429 "Unable to find a suitable video mode to fit a full screen\n\
430 geometry of %ix%i.  Try adjusting the size of the window and\n\
431 verify that both your hardware and software can support that\n\
432 geometry.",
433 			width, height
434 		    );
435 		    GWOutputMessage(
436 			display,
437 			GWOutputMessageTypeWarning,
438 			"Full Screen Failed", buf, NULL
439 		    );
440 		    free(buf);
441 		}
442 	    }
443 	    else if(status == -5)
444 	    {
445 		/* Full screen is not supported */
446 		GWOutputMessage(
447 		    display,
448 		    GWOutputMessageTypeWarning,
449 		    "Full Screen Failed",
450 		    "Full screen is not supported.",
451 		    NULL
452 		);
453 	    }
454 	}
455 }
456 
457 /*
458  *	Sets the resolution/size of the window and GL frame buffer.
459  *
460  *	If there is no change in size from the current size then
461  *	nothing will be done.
462  */
SARResolution(gw_display_struct * display,int width,int height)463 void SARResolution(gw_display_struct *display, int width, int height)
464 {
465 	int x, y, owidth, oheight;
466 
467 	/* Get current size and position */
468 	GWContextGet(
469 	    display, GWContextCurrent(display),
470 	    NULL, NULL,
471 	    &x, &y,
472 	    &owidth, &oheight
473 	);
474 
475 	/* Is there a change in window size? */
476 	if((width != owidth) || (height != oheight))
477 	{
478 	    /* Adjust the window position coordinates so that the window
479 	     * stays in the center.
480 	     */
481 	    x += (owidth / 2) - (width / 2);
482 	    y += (oheight / 2) - (height / 2);
483 
484 	    /* Make sure that the adjusted window position coordinates
485 	     * do not place the window completely off the root window.
486 	     */
487 
488 	    /* Sanitize width */
489 	    if((display->root_width > 0) &&
490 	       ((x + width) > display->root_width)
491 	    )
492 		x = display->root_width - width;
493 	    if(x < 0)
494 		x = 0;
495 	    /* Sanitize height */
496 	    if((display->root_height > 0) &&
497 	       ((y + height) > display->root_height)
498 	    )
499 		y = display->root_height - height;
500 	    if(y < 0)
501 		y = 0;
502 
503 	    /* Check if already in full screen mode */
504 	    if(GWContextIsFullScreen(display))
505 	    {
506 		/* Already in full screen mode, so just adjust the
507 		 * size and update the full screen mode.
508 		 */
509 		GWContextSize(display, width, height);
510 		GWContextFullScreen(display, True);
511 	    }
512 	    else
513 	    {
514 		/* In windowed mode, update both the size and the
515 		 * position.
516 		 */
517 		GWContextPosition(display, x, y);
518 		GWContextSize(display, width, height);
519 	    }
520 	}
521 }
522 
523 /*
524  *	Increases the resolution/size of the window and GL frame buffer
525  *	to the "next" resolution/size.
526  */
SARResolutionIncrease(gw_display_struct * display)527 void SARResolutionIncrease(gw_display_struct *display)
528 {
529 	int width, height, new_width, new_height;
530 
531 	GWContextGet(
532 	    display, GWContextCurrent(display),
533 	    NULL, NULL,
534 	    NULL, NULL,
535 	    &width, &height
536 	);
537 
538 	/* 320x240 */
539 	if(width < 320)
540 	{
541 	    new_width = 320;
542 	    new_height = 240;
543 	}
544 	/* 640x480 */
545 	else if(width < 640)
546 	{
547 	    new_width = 640;
548 	    new_height = 480;
549 	}
550 	/* 800x600 */
551 	else if(width < 800)
552 	{
553 	    new_width = 800;
554 	    new_height = 600;
555 	}
556 	/* 1024x768 */
557 	else if(width < 1024)
558 	{
559 	    new_width = 1024;
560 	    new_height = 768;
561 	}
562         else if (width < 1280)
563         {
564             new_width = 1280;
565             new_height = 768;
566         }
567         else if (width < 1366)
568         {
569             new_width = 1366;
570             new_height = 768;
571         }
572 	/* 100x70 */
573 	else
574 	{
575 	    new_width = 100;
576 	    new_height = 70;
577 	}
578 
579 	/* Set new resolution */
580 	SARResolution(display, new_width, new_height);
581 }
582 
583 /*
584  *	Decreases the resolution/size of the window and GL frame buffer
585  *      to the "previous" resolution/size.
586  */
SARResolutionDecrease(gw_display_struct * display)587 void SARResolutionDecrease(gw_display_struct *display)
588 {
589 	int width, height, new_width, new_height;
590 
591 	GWContextGet(
592 	    display, GWContextCurrent(display),
593 	    NULL, NULL,
594 	    NULL, NULL,
595 	    &width, &height
596 	);
597 
598         if (width > 1366)
599         {
600             new_width = 1280;
601             new_height = 768;
602         }
603         else if (width > 1280)
604         {
605             new_width = 1366;
606             new_height = 768;
607         }
608 	/* 1024x768 */
609 	else if(width > 1024)
610 	{
611 	    new_width = 1024;
612 	    new_height = 768;
613 	}
614 	/* 800x600 */
615 	else if(width > 800)
616 	{
617 	    new_width = 800;
618 	    new_height = 600;
619 	}
620 	/* 640x480 */
621 	else if(width > 640)
622 	{
623 	    new_width = 640;
624 	    new_height = 480;
625 	}
626 	/* 320x240 */
627 	else if(width > 320)
628 	{
629 	    new_width = 320;
630 	    new_height = 240;
631 	}
632 	/* 100x70 */
633 	else
634 	{
635 	    new_width = 100;
636 	    new_height = 70;
637 	}
638 
639 	/* Set new resolution */
640 	SARResolution(display, new_width, new_height);
641 }
642 
643 
644 /*
645  *	Loading Simulation progress callback, this updates the progress
646  *	bar in the Loading Simulation menu and redraws.
647  *
648  *	Returns 0 to indicate continue loading or non-zero to indicate
649  *	abort load.
650  *
651  *	Given input ptr must be of type sar_progress_cb_struct.
652  */
SARLoadProgressCB(void * ptr,long pos,long size)653 int SARLoadProgressCB(void *ptr, long pos, long size)
654 {
655 	int i;
656 	sar_core_struct *core_ptr;
657 	sar_menu_struct *m;
658 	void *o;
659 	gw_display_struct *display;
660 	sar_progress_cb_struct *cb_data = SAR_PROGRESS_CB(ptr);
661 	if(cb_data == NULL)
662 	    return(0);
663 
664 	core_ptr = SAR_CORE(cb_data->core_ptr);
665 	if(core_ptr == NULL)
666 	    return(0);
667 
668 	display = core_ptr->display;
669 	if(display == NULL)
670 	    return(0);
671 
672 	/* Get pointer to the current menu */
673 	i = core_ptr->cur_menu;
674 	if((i >= 0) && (i < core_ptr->total_menus))
675 	    m = core_ptr->menu[i];
676 	else
677 	    m = NULL;
678 	if(m == NULL)
679 	    return(0);
680 
681 	/* Make sure that we have a positive range to indicate a
682 	 * progress
683 	 */
684 	if((size > 0) && (cb_data->coeff_range > 0.0f))
685 	{
686 	    /* Update progress bar */
687 	    for(i = 0; i < m->total_objects; i++)
688 	    {
689 		o = m->object[i];
690 		if(SAR_MENU_IS_PROGRESS(o))
691 		{
692 		    GWManage(display);
693 		    SARMenuProgressSet(
694 			display, m, i,
695 			cb_data->coeff_offset +
696 			    ((float)pos / (float)size *
697 			    cb_data->coeff_range),
698 			True		/* Redraw */
699 		    );
700 		    break;
701 		}
702 	    }
703 	}
704 
705 	return(0);
706 }
707 
708 /*
709  *	Tempory function to handle send message callback.
710  */
SARTextInputCBSendMessage(const char * value,void * data)711 void SARTextInputCBSendMessage(const char *value, void *data)
712 {
713 	sar_core_struct *core_ptr = SAR_CORE(data);
714 	if((value == NULL) || (core_ptr == NULL))
715 	    return;
716 
717 	SARMessageAdd(core_ptr->scene, value);
718 }
719 
720 /*
721  *	Quick simulation text input callback.
722  */
SARTextInputCBQuitSimulation(const char * value,void * data)723 void SARTextInputCBQuitSimulation(const char *value, void *data)
724 {
725 	sar_core_struct *core_ptr = SAR_CORE(data);
726 	if((value == NULL) || (core_ptr == NULL))
727 	    return;
728 
729 	if(toupper(*value) == 'Y')
730 	    SARSimEnd(core_ptr);
731 }
732 
733 /*
734  *	Redraw callback.
735  */
SARDrawCB(int ctx_num,void * ptr)736 void SARDrawCB(int ctx_num, void *ptr)
737 {
738 	int cur_menu;
739 	sar_core_struct *core_ptr = SAR_CORE(ptr);
740 	if(core_ptr == NULL)
741 	    return;
742 
743 	/* Get current menu number */
744 	cur_menu = core_ptr->cur_menu;
745 
746 	/* Check if current menu is valid, if it is then that
747 	 * implies we are in the menus.
748 	 */
749 	if(SARIsMenuAllocated(core_ptr, cur_menu))
750 	{
751 	    /* Draw menus */
752 	    SARMenuDrawAll(
753 		core_ptr->display,
754 		core_ptr->menu[cur_menu]
755 	    );
756 	    SARTextInputDraw(core_ptr->text_input);
757 	    GWSwapBuffer(core_ptr->display);
758 	}
759 	else
760 	{
761 	    /* Draw scene */
762 	    SARDraw(core_ptr);
763 	}
764 }
765 
766 /*
767  *	Keyboard callback.
768  */
SARKeyBoardCB(void * ptr,int c,Boolean state,unsigned long t)769 void SARKeyBoardCB(void *ptr, int c, Boolean state, unsigned long t)
770 {
771 	sar_core_struct *core_ptr = SAR_CORE(ptr);
772 	gw_display_struct *display;
773 	sar_menu_struct *menu_ptr;
774 	Boolean alt_key_state, ctrl_key_state, shift_key_state;
775 	if(core_ptr == NULL)
776 	    return;
777 
778 	display = core_ptr->display;
779 	if(display == NULL)
780 	    return;
781 
782 	alt_key_state = display->alt_key_state;
783 	ctrl_key_state = display->ctrl_key_state;
784 	shift_key_state = display->shift_key_state;
785 
786 	/* First check to see if the text input prompt is mapped by
787 	 * checking if the text input callback function is set (not
788 	 * NULL).
789 	 */
790 	if(SARTextInputIsMapped(core_ptr->text_input))
791 	{
792 	    text_input_struct *text_input = core_ptr->text_input;
793 
794 	    /* Pass key event to text input prompt handler */
795 	    SARTextInputHandleKey(text_input, c, state);
796 
797 	    /* If we are in the menus then we need to redraw */
798 	    if(SARGetCurrentMenuPtr(core_ptr) != NULL)
799 	    {
800 		SARTextInputDraw(text_input);
801 		GWSwapBuffer(display);
802 	    }
803 
804 	    /* Return, do not continue. This is so that the key event
805 	     * is not passed to any other key event handlers.
806 	     */
807 	    return;
808 	}
809 
810 
811 	/* Intercept special key combinations here before going on to
812 	 * to regular key handling, if any keys are handled here then
813 	 * they will not be passed to the regular key handler
814 	 */
815 	/* Toggle full screen */
816 	if((c == GWKeyF11) && ctrl_key_state)
817 	{
818 	    if(!state)
819 		SARFullScreen(core_ptr);
820 	    return;
821 	}
822 	/* Increase resolution */
823 	if((c == ']') && ctrl_key_state)
824 	{
825 	    if(!state)
826 		SARResolutionIncrease(display);
827 	    return;
828 	}
829 	/* Decrease resolution */
830 	if((c == '[') && ctrl_key_state)
831 	{
832 	    if(!state)
833 		SARResolutionDecrease(display);
834 	    return;
835 	}
836 	/* Screen shot */
837 #ifdef __MSW__
838 	if((c == 'c') && ctrl_key_state)
839 #else
840 	if(((c == 'c') && ctrl_key_state) || (c == GWKeySysReq))
841 #endif
842 	{
843 	    if(!state)
844 		SARScreenShot(core_ptr, NULL, 2);
845 	    return;
846 	}
847 
848 	/* Get current selected menu (if any).  If there is no menu
849 	 * currently selected then call simulation key handler,
850 	 * otherwise call menu key handler.
851 	 */
852 	menu_ptr = SARGetCurrentMenuPtr(core_ptr);
853 	if(menu_ptr == NULL)
854 	{
855 	    /* In simulation ,so pass key event to simulation key
856 	     * handler.  Note that this function will not post a
857 	     * redraw.
858 	     */
859 	    SARKey(core_ptr, c, state, t);
860 	}
861 	else
862 	{
863 	    /* A menu is selected, call menu key handler */
864 	    SARMenuManageKey(core_ptr->display, menu_ptr, c, state);
865 	}
866 }
867 
868 /*
869  *	Pointer callback.
870  */
SARPointerCB(int ctx_num,void * ptr,int x,int y,gw_event_type type,int btn_num,unsigned long t)871 void SARPointerCB(
872 	int ctx_num, void *ptr,
873 	int x, int y, gw_event_type type, int btn_num, unsigned long t
874 )
875 {
876 	sar_core_struct *core_ptr = SAR_CORE(ptr);
877 	gw_display_struct *display;
878 	sar_menu_struct *menu_ptr;
879 	if(core_ptr == NULL)
880 	    return;
881 
882 	display = core_ptr->display;
883 	if(display == NULL)
884 	    return;
885 
886 	/* First check to see if the text input prompt is mapped, if
887 	 * it is then the event should be passed to it
888 	 */
889 	if(SARTextInputIsMapped(core_ptr->text_input))
890 	{
891 	    text_input_struct *text_input = core_ptr->text_input;
892 
893 	    /* Pass poionter event to text input prompt handler */
894 	    SARTextInputHandlePointer(
895 		text_input, x, y, type, btn_num
896 	    );
897 
898 	    /* If we are in the menus then we need to redraw */
899 	    if(SARGetCurrentMenuPtr(core_ptr) != NULL)
900 	    {
901 		SARTextInputDraw(text_input);
902 		GWSwapBuffer(display);
903 	    }
904 
905 	    /* Done handling the event */
906 	    return;
907 	}
908 
909 	/* Get current menu (if any) */
910 	menu_ptr = SARGetCurrentMenuPtr(core_ptr);
911 	if(menu_ptr == NULL)
912 	{
913 	    /* There is no current menu so it implies we are in
914 	     * simulation, so forward the event to game controller
915 	     */
916 	    GCtlHandlePointer(
917 		display, core_ptr->gctl,
918 		type, btn_num,
919 		x, y, t, lapsed_millitime
920 	    );
921 	}
922 	else
923 	{
924 	    /* There is a current menu so Forward the event to the
925 	     * menu pointer event handler
926 	     */
927 	    SARMenuManagePointer(
928 		display, menu_ptr,
929 		x, y, type, btn_num
930 	    );
931 	}
932 }
933 
934 /*
935  *	Resize callback, updates the last size of the toplevel window.
936  *
937  *	If width or height are less than 1 the last glViewport() setting
938  *	for width or height will be called again.
939  */
SARReshapeCB(int ctx_num,void * ptr,int x,int y,int width,int height)940 void SARReshapeCB(int ctx_num, void *ptr, int x, int y, int width, int height)
941 {
942 	sar_core_struct *core_ptr = SAR_CORE(ptr);
943 	gw_display_struct *display;
944 	sar_option_struct *opt;
945 
946 	if(core_ptr == NULL)
947 	    return;
948 
949 	display = core_ptr->display;
950 	if(display == NULL)
951 	    return;
952 
953 	opt = &core_ptr->option;
954 
955 	/* Change width and height if specified */
956 	if((width > 0) && (height > 0))
957 	{
958 	    /* Update last positions of toplevel window on global
959 	     * options
960 	     */
961 	    opt->last_x = x;
962 	    opt->last_y = y;
963 	    opt->last_width = width;
964 	    opt->last_height = height;
965 	}
966 	else
967 	{
968 	    /* If this function is called with width and height not
969 	     * positive, then it means it was called synthetically and
970 	     * that we should use the current size of the context on the
971 	     * display structure
972 	     */
973 	    GWContextGet(
974 		display, ctx_num,
975 		NULL, NULL,
976 		NULL, NULL,
977 		&width, &height
978 	    );
979 	}
980 
981 	/* Set new view port dimensions */
982 	if((width > 0) && (height > 0))
983 	    glViewport(0, 0, width, height);
984 }
985 
986 
987 /*
988  *	Visibility change callback.
989  */
SARVisibilityCB(int ctx_num,void * ptr,gw_visibility v)990 void SARVisibilityCB(int ctx_num, void *ptr, gw_visibility v)
991 {
992 	switch(v)
993 	{
994 	  case GWVisibilityFullyObscured:
995 	    is_visible = 0;
996 	    break;
997 	  default:
998 	    is_visible = 1;
999 	    break;
1000 	}
1001 }
1002 
1003 /*
1004  *	Save yourself callback.
1005  */
SARSaveYourselfCB(int ctx_num,void * ptr)1006 void SARSaveYourselfCB(int ctx_num, void *ptr)
1007 {
1008 	sar_core_struct *core_ptr = SAR_CORE(ptr);
1009 	if(core_ptr == NULL)
1010 	    return;
1011 
1012 
1013 
1014 }
1015 
1016 /*
1017  *	Close window callback.
1018  */
SARCloseCB(int ctx_num,void * ptr,void * data)1019 void SARCloseCB(int ctx_num, void *ptr, void *data)
1020 {
1021 	sar_core_struct *core_ptr = SAR_CORE(ptr);
1022 	if(core_ptr == NULL)
1023 	    return;
1024 
1025 	/* Check if currently In a menu, implying that we are currently
1026 	 * in the menu system and not in simulation.
1027 	 */
1028 	if(core_ptr->cur_menu > -1)
1029 	{
1030 	    /* Switch global runlevel to 1, causing the program to begin
1031 	     * shutting down.
1032 	     */
1033 	    runlevel = 1;
1034 	}
1035 	else
1036 	{
1037 	    /* Currently in simulation, so end simulation instead of
1038 	     * switching to runlevel 1.
1039 	     *
1040 	     * First check if the user is already being prompted for,
1041 	     * exit.  If the user is then end simulation, otherwise
1042 	     * prompt for exit.
1043 	     */
1044 	    if(SARTextInputIsMapped(core_ptr->text_input))
1045 	    {
1046 		SARTextInputUnmap(core_ptr->text_input);
1047 		SARSimEnd(core_ptr);
1048 	    }
1049 	    else
1050 	    {
1051 		if(core_ptr->mission != NULL)
1052 		{
1053 		    /* Same as end simulation, just prompt text different */
1054 		    SARTextInputMap(
1055 			core_ptr->text_input,
1056 			"Are you sure you want to abort the mission?",
1057 			NULL,
1058 			SARTextInputCBQuitSimulation,
1059 			core_ptr
1060 		    );
1061 		}
1062 		else
1063 		{
1064 		    SARTextInputMap(
1065 			core_ptr->text_input,
1066 			"Are you sure you want to quit simulation?",
1067 			NULL,
1068 			SARTextInputCBQuitSimulation,
1069 			core_ptr
1070 		    );
1071 		}
1072 	    }
1073 	}
1074 }
1075 
1076 /*
1077  *	Resets global timmers to zero and updates the global
1078  *	variable cur_millitime to the value of t_new.
1079  *
1080  *	Global variable lapsed_millitime will be reset to 0 and
1081  *	time_compensation will be set to 1.0.
1082  *
1083  *	Timings on core structure, scene, objects, and other related
1084  *	resources will also be reset.
1085  */
SARResetTimmersCB(sar_core_struct * core_ptr,time_t t_new)1086 void SARResetTimmersCB(sar_core_struct *core_ptr, time_t t_new)
1087 {
1088 	int i;
1089 	sar_scene_struct *scene;
1090 	sar_object_struct *obj_ptr;
1091 	sar_object_aircraft_struct *obj_aircraft_ptr;
1092 	sar_object_ground_struct *obj_ground_ptr;
1093 	sar_object_human_struct *obj_human_ptr;
1094 	sar_object_smoke_struct *obj_smoke_ptr;
1095 	sar_object_fire_struct *obj_fire_ptr;
1096 	sar_object_explosion_struct *obj_explosion_ptr;
1097 	sar_mission_struct *mission = NULL;
1098 
1099 
1100 	/* Set global variable cur_millitime to t_new */
1101 	cur_millitime = t_new;
1102 
1103 	/* Reset lapsed and time compensation */
1104 	lapsed_millitime = 0l;
1105 	time_compensation = 1.0f;
1106 
1107 
1108 	/* Update random seed */
1109 	srand((unsigned int)cur_millitime);
1110 
1111 
1112 	/* Reset global next timmers */
1113 	memset(&next, 0x00, sizeof(sar_next_struct));
1114 
1115 
1116 	/* Reset game controller timmers */
1117 	GCtlResetTimmers(core_ptr->gctl);
1118 
1119 	/* Reset timings on scene */
1120 	scene = core_ptr->scene;
1121 	if(scene != NULL)
1122 	{
1123 	    sar_cloud_bb_struct *cloud_bb;
1124 
1125 	    scene->message_display_until = 0l;
1126 	    scene->camera_ref_title_display_until = 0l;
1127 
1128 	    /* Cloud `billboard' objects lightening timmers */
1129 	    for(i = 0; i < scene->total_cloud_bbs; i++)
1130 	    {
1131 		cloud_bb = scene->cloud_bb[i];
1132 		if(cloud_bb == NULL)
1133 		    continue;
1134 
1135 		cloud_bb->lightening_next_on = 0l;
1136 		cloud_bb->lightening_next_off = 0l;
1137 		cloud_bb->lightening_started_on = 0l;
1138 	    }
1139 	}
1140 
1141 	/* Reset timings on mission */
1142 	mission = core_ptr->mission;
1143 	if(mission != NULL)
1144 	{
1145 	    mission->next_check = 0l;
1146 	    mission->next_log_position = 0l;
1147 	}
1148 
1149 
1150 	/* Reset timmers on objects */
1151 	for(i = 0; i < core_ptr->total_objects; i++)
1152 	{
1153 	    obj_ptr = core_ptr->object[i];
1154 	    if(obj_ptr == NULL)
1155 		continue;
1156 
1157 	    /* Any object with a defined life span needs to die whenever
1158 	     * timmers are reset
1159 	     *
1160 	     * Note that we need to reset the value to 1 instead of 0
1161 	     * since 0 means that the object has an infinate life span
1162 	     * (never dies)
1163 	     */
1164 	    if(obj_ptr->life_span > 0l)
1165 		obj_ptr->life_span = 1l;
1166 
1167 	    switch(obj_ptr->type)
1168 	    {
1169 	      case SAR_OBJ_TYPE_GARBAGE:
1170 	      case SAR_OBJ_TYPE_STATIC:
1171 	      case SAR_OBJ_TYPE_AUTOMOBILE:
1172 	      case SAR_OBJ_TYPE_WATERCRAFT:
1173 		break;
1174 	      case SAR_OBJ_TYPE_AIRCRAFT:
1175 		obj_aircraft_ptr = SAR_OBJ_GET_AIRCRAFT(obj_ptr);
1176 		if(obj_aircraft_ptr != NULL)
1177 		{
1178 		    obj_aircraft_ptr->next_engine_on = 0l;
1179 		}
1180 		break;
1181 	      case SAR_OBJ_TYPE_GROUND:
1182 		obj_ground_ptr = SAR_OBJ_GET_GROUND(obj_ptr);
1183 		break;
1184 	      case SAR_OBJ_TYPE_RUNWAY:
1185 	      case SAR_OBJ_TYPE_HELIPAD:
1186 		break;
1187 	      case SAR_OBJ_TYPE_HUMAN:
1188 		obj_human_ptr = SAR_OBJ_GET_HUMAN(obj_ptr);
1189 		break;
1190 	      case SAR_OBJ_TYPE_SMOKE:
1191 		obj_smoke_ptr = SAR_OBJ_GET_SMOKE(obj_ptr);
1192 		if(obj_smoke_ptr != NULL)
1193 		{
1194 		    obj_smoke_ptr->respawn_next = 0l;
1195 		}
1196 		break;
1197 	      case SAR_OBJ_TYPE_FIRE:
1198 		obj_fire_ptr = SAR_OBJ_GET_FIRE(obj_ptr);
1199 		if(obj_fire_ptr != NULL)
1200 		{
1201 		    obj_fire_ptr->next_frame_inc = 0l;
1202 		}
1203 		break;
1204 	      case SAR_OBJ_TYPE_EXPLOSION:
1205 		obj_explosion_ptr = SAR_OBJ_GET_EXPLOSION(obj_ptr);
1206 		if(obj_explosion_ptr != NULL)
1207 		{
1208 		    obj_explosion_ptr->next_frame_inc = 0l;
1209 		}
1210 		break;
1211 	      case SAR_OBJ_TYPE_CHEMICAL_SPRAY:
1212 	      case SAR_OBJ_TYPE_FUELTANK:
1213 	      case SAR_OBJ_TYPE_PREMODELED:
1214 		break;
1215 	    }
1216 
1217 	    /* Lights */
1218 	    if(obj_ptr->light != NULL)
1219 	    {
1220 		int n;
1221 		sar_light_struct *light;
1222 
1223 		for(n = 0; n < obj_ptr->total_lights; n++)
1224 		{
1225 		    light = obj_ptr->light[n];
1226 		    if(light == NULL)
1227 			continue;
1228 
1229 		    light->next_off = 0l;
1230 		    light->next_on = light->int_delay_on;
1231 		}
1232 	    }
1233 
1234 /* Add other object common timing values that need to be reset here */
1235 
1236 	}
1237 }
1238 
1239 
1240 /*
1241  *	SAR initialize.
1242  */
SARInit(int argc,char ** argv)1243 sar_core_struct *SARInit(int argc, char **argv)
1244 {
1245 	int i, strc;
1246 	const char *s, *arg;
1247 	char **strv;
1248 #ifndef __MSW__
1249 	const char *s2;
1250 #endif
1251 #ifdef __MSW__
1252 	HINSTANCE hInst = GetModuleHandle(NULL);
1253 #endif
1254 	sar_color_struct *color;
1255 
1256 	sar_core_struct *core_ptr;
1257 	gw_display_struct *dpy;
1258 	sar_option_struct *opt;
1259 
1260 	const char	*display_address = NULL;
1261 	const char	*font_name = NULL;
1262 	Boolean		cl_direct_rendering = True;
1263 	int		cl_fullscreen = 0;
1264         int             allow_key_repeat = True;
1265 	float		aspect_offset = 0.0f;
1266 	Boolean		startup_no_sound = False;
1267 	const char	*sound_server_connect_arg = NULL;
1268 
1269 	Boolean notify_install_local_error = False,
1270 		notify_install_local_success = False;
1271 
1272 	char cwd[PATH_MAX];
1273 	char tmp_path[PATH_MAX + NAME_MAX];
1274 	char geometry_string[GW_GEOMETRY_STRING_MAX];
1275 
1276 
1277 	/* Set signal callbacks */
1278 	signal(SIGINT, SARHandleSignal);
1279 	signal(SIGTERM, SARHandleSignal);
1280 	signal(SIGSEGV, SARHandleSignal);
1281 
1282 	/* Seed randon number generator */
1283 	srand((unsigned int)time(NULL));
1284 
1285 	/* Get current working directory */
1286 	if(getcwd(cwd, PATH_MAX) != NULL)
1287 	    cwd[PATH_MAX - 1] = '\0';
1288 	else
1289 	    strcpy(cwd, "/");
1290 
1291 	*geometry_string = '\0';
1292 
1293 
1294 	/* Reset globals */
1295 	cur_millitime = 0;
1296 	lapsed_millitime = 0;
1297 	time_compensation = 1.0f;
1298 	time_compression = 1.0f;
1299 
1300 
1301 	/* Allocate core structure */
1302 	core_ptr = SAR_CORE(calloc(1, sizeof(sar_core_struct)));
1303 	if(core_ptr == NULL)
1304 	    return(NULL);
1305 
1306 	opt = &core_ptr->option;
1307 
1308 	/* Reset core structure values */
1309 	core_ptr->prog_file = NULL;
1310 	core_ptr->prog_file_full_path = NULL;
1311 
1312 	core_ptr->stop_count = 0;
1313 
1314 	core_ptr->display = NULL;
1315 	core_ptr->recorder_address = NULL;
1316 	core_ptr->recorder = NULL;
1317 	core_ptr->audio_mode_name = NULL;
1318 	core_ptr->gctl = NULL;
1319 
1320 	core_ptr->cur_music_id = -1;
1321 	core_ptr->music_ref = NULL;
1322 	core_ptr->total_music_refs = 0;
1323 	core_ptr->human_data = NULL;
1324 	core_ptr->weather_data = NULL;
1325 
1326 	core_ptr->scene = NULL;
1327 	core_ptr->mission = NULL;
1328 	core_ptr->object = NULL;
1329 	core_ptr->total_objects = 0;
1330 
1331 	core_ptr->menu = NULL;
1332 	core_ptr->total_menus = 0;
1333 	core_ptr->cur_menu = 0;
1334 /* Bunch of menu images that we arn't resetting, but we can get away
1335  * with not resetting them.
1336  */
1337 
1338 	core_ptr->texture_list = NULL;
1339 	core_ptr->total_texture_list = 0;
1340 
1341 	core_ptr->player_stat = NULL;
1342 	core_ptr->total_player_stats = 0;
1343 
1344 	core_ptr->cur_mission_file = NULL;
1345 	core_ptr->cur_player_model_file = NULL;
1346 	core_ptr->cur_scene_file = NULL;
1347 
1348 	core_ptr->display_help = 0;
1349 
1350 	core_ptr->flir = False;
1351 
1352 	core_ptr->drawmap_objname = NULL;
1353 	core_ptr->total_drawmap_objnames = 0;
1354 	core_ptr->drawmap_ghc_result = 0.0f;
1355 
1356 	core_ptr->text_input = NULL;
1357 
1358 	memset(opt, 0x00, sizeof(sar_option_struct));
1359 	memset(&core_ptr->fps, 0x00, sizeof(sar_fps_struct));
1360 
1361 
1362 	/* Reset options to defaults */
1363 	opt->menu_backgrounds = True;
1364 	opt->menu_change_affects = True;
1365 	opt->menu_always_full_redraw = False;
1366 	opt->console_quiet = False;
1367 	opt->internal_debug = False;
1368 	opt->runtime_debug = False;
1369 	opt->prioritize_memory = False;
1370 	opt->units = SAR_UNITS_ENGLISH;
1371 
1372 	opt->textured_ground = True;
1373 	opt->textured_clouds = True;
1374 	opt->textured_objects = True;
1375 	opt->atmosphere = True;
1376 	opt->dual_pass_depth = True;
1377 	opt->prop_wash = True;
1378 	opt->smoke_trails = True;
1379 	opt->celestial_objects = True;
1380 	opt->gl_polygon_offset_factor = -1.9f;
1381 	opt->gl_shade_model = GL_SMOOTH;
1382 	opt->visibility_max = 4;
1383 	opt->rotor_blur_style = SAR_ROTOR_BLUR_CLOCK_RADIAL;
1384 	opt->graphics_acceleration = 0.0f;
1385 
1386 	opt->engine_sounds = False;
1387 	opt->event_sounds = False;
1388 	opt->voice_sounds = False;
1389 	opt->music = False;
1390 
1391 	opt->sound_priority = SND_PRIORITY_BACKGROUND;
1392 
1393 	opt->system_time_free_flight = False;
1394 
1395 	color = &opt->hud_color;
1396 	color->a = 1.0f;
1397 	color->r = 1.0f;
1398 	color->g = 1.0f;
1399 	color->b = 1.0f;
1400 
1401 	color = &opt->message_color;
1402 	color->a = 1.0f;
1403 	color->r = 1.0f;
1404 	color->g = 1.0f;
1405 	color->b = 1.0f;
1406 
1407 	opt->show_hud_text = True;
1408 	opt->show_outside_text = True;
1409 
1410 	opt->explosion_frame_int = SAR_DEF_EXPLOSION_FRAME_INT;
1411 	opt->splash_frame_int = SAR_DEF_SPLASH_FRAME_INT;
1412 	opt->crash_explosion_life_span = SAR_DEF_CRASH_EXPLOSION_LIFE_SPAN;
1413 	opt->fuel_tank_life_span = SAR_DEF_FUEL_TANK_LIFE_SPAN;
1414 
1415 	opt->rotor_wash_vis_coeff = (float)SAR_DEF_ROTOR_WASH_VIS_COEFF;
1416 
1417 	opt->gctl_controllers = GCTL_CONTROLLER_KEYBOARD |
1418 	    GCTL_CONTROLLER_POINTER;
1419 	opt->gctl_options = 0;
1420 	opt->js_priority = GCTL_JS_PRIORITY_BACKGROUND;
1421 
1422 	opt->js0_connection = GCTL_JS_CONNECTION_STANDARD;
1423 	opt->js1_connection = GCTL_JS_CONNECTION_STANDARD;
1424 
1425 	opt->js0_btn_rotate = 3;
1426 	opt->js0_btn_air_brakes = 6;
1427 	opt->js0_btn_wheel_brakes = 0;
1428 	opt->js0_btn_zoom_in = 2;
1429 	opt->js0_btn_zoom_out = 1;
1430 	opt->js0_btn_hoist_up = 5;
1431 	opt->js0_btn_hoist_down = 4;
1432 
1433 	opt->js1_btn_rotate = 3;
1434 	opt->js1_btn_air_brakes = 6;
1435 	opt->js1_btn_wheel_brakes = 0;
1436 	opt->js1_btn_zoom_in = 2;
1437 	opt->js1_btn_zoom_out = 1;
1438 	opt->js1_btn_hoist_up = 5;
1439 	opt->js1_btn_hoist_down = 4;
1440 /*
1441 	opt->gctl_js0_axis_roles = GCTL_JS_AXIS_ROLE_PITCH |
1442 	    GCTL_JS_AXIS_ROLE_BANK;
1443  */
1444 	opt->gctl_js0_axis_roles = 0;
1445 	opt->gctl_js1_axis_roles = 0;
1446 /*
1447 	opt->gctl_js1_axis_roles =
1448 GCTL_JS_AXIS_ROLE_AS_THROTTLE_AND_RUDDER;
1449  */
1450 
1451 	opt->hoist_contact_expansion_coeff = 1.0f;
1452 	opt->damage_resistance_coeff = 1.0f;
1453 	opt->flight_physics_level = FLIGHT_PHYSICS_REALISTIC;	/* Make it hard */
1454 
1455 	opt->last_selected_player = 0;
1456 	opt->last_selected_mission = 0;
1457 	opt->last_selected_ffscene = 0;
1458 	opt->last_selected_ffaircraft = 0;
1459 	opt->last_selected_ffweather = 0;
1460 	opt->last_x = 0;
1461 	opt->last_y = 0;
1462 	opt->last_width = SAR_DEF_WINDOW_WIDTH;
1463 	opt->last_height = SAR_DEF_WINDOW_HEIGHT;
1464 	opt->last_fullscreen = False;
1465 
1466 
1467 	/* Set local game directory */
1468 #ifdef __MSW__
1469 	strncpy(dname.local_data, cwd, PATH_MAX);
1470 #else
1471 	s = getenv("HOME");
1472 	if(s == NULL)
1473 	    s = cwd;
1474 	s2 = PrefixPaths(s, SAR_DEF_LOCAL_DATA_DIR);
1475 	strncpy(
1476 	    dname.local_data,
1477 	    (s2 != NULL) ? s2 : cwd,
1478 	    PATH_MAX
1479 	);
1480 #endif
1481 	dname.local_data[PATH_MAX - 1] = '\0';
1482 
1483 	/* Set global game directory */
1484 #if defined(__MSW__)
1485 	strncpy(dname.global_data, cwd, PATH_MAX);
1486 #else
1487 	s = getenv(SAR_DEF_ENV_GLOBAL_DIR);
1488 	if(s != NULL)
1489 	{
1490 	    struct stat stat_buf;
1491 
1492 	    if(!ISPATHABSOLUTE(s))
1493 		fprintf(
1494 		    stderr,
1495 "Warning: Environment variable \"%s\" value \"%s\" must be an absolute path.\n",
1496 		    SAR_DEF_ENV_GLOBAL_DIR, s
1497 		);
1498 	    if(stat(s, &stat_buf))
1499 		fprintf(
1500 		    stderr,
1501 "Warning: Environment variable \"%s\" value \"%s\" reffers to a non-existant object.\n",
1502 		    SAR_DEF_ENV_GLOBAL_DIR, s
1503 		);
1504 	}
1505 	strncpy(
1506 	    dname.global_data,
1507 	    (s != NULL) ? s : SAR_DEF_GLOBAL_DATA_DIR,
1508 	    PATH_MAX
1509 	);
1510 #endif
1511 	dname.global_data[PATH_MAX - 1] = '\0';
1512 
1513 	/* Set default configuration file */
1514 	s = PrefixPaths(dname.local_data, SAR_DEF_OPTIONS_FILE);
1515 	strncpy(
1516 	    fname.options,
1517 	    (s != NULL) ? s : SAR_DEF_OPTIONS_FILE,
1518 	    PATH_MAX + NAME_MAX
1519 	);
1520 	fname.options[PATH_MAX + NAME_MAX - 1] = '\0';
1521 
1522 	/* Set default global human data presets file */
1523 	s = PrefixPaths(dname.global_data, SAR_DEF_HUMAN_FILE);
1524 	strncpy(
1525 	    fname.human,
1526 	    (s != NULL) ? s : SAR_DEF_HUMAN_FILE,
1527 	    PATH_MAX + NAME_MAX
1528 	);
1529 	fname.human[PATH_MAX + NAME_MAX - 1] = '\0';
1530 
1531 	/* Set default global music data presets file */
1532 	s = PrefixPaths(dname.global_data, SAR_DEF_MUSIC_FILE);
1533 	strncpy(
1534 	    fname.music,
1535 	    (s != NULL) ? s : SAR_DEF_MUSIC_FILE,
1536 	    PATH_MAX + NAME_MAX
1537 	);
1538 	fname.music[PATH_MAX + NAME_MAX - 1] = '\0';
1539 
1540 	/* Set default global textures list file */
1541 	s = PrefixPaths(dname.global_data, SAR_DEF_TEXTURES_FILE);
1542 	strncpy(
1543 	    fname.textures,
1544 	    (s != NULL) ? s : SAR_DEF_TEXTURES_FILE,
1545 	    PATH_MAX + NAME_MAX
1546 	);
1547 	fname.textures[PATH_MAX + NAME_MAX - 1] = '\0';
1548 
1549 	/* Set default players list file */
1550 	s = PrefixPaths(dname.local_data, SAR_DEF_PLAYERS_FILE);
1551 	strncpy(
1552 	    fname.players,
1553 	    (s != NULL) ? s : SAR_DEF_PLAYERS_FILE,
1554 	    PATH_MAX + NAME_MAX
1555 	);
1556 	fname.players[PATH_MAX + NAME_MAX - 1] = '\0';
1557 
1558 	/* Set default global weather data presets file */
1559 	s = PrefixPaths(dname.global_data, SAR_DEF_WEATHER_FILE);
1560 	strncpy(
1561 	    fname.weather,
1562 	    (s != NULL) ? s : SAR_DEF_WEATHER_FILE,
1563 	    PATH_MAX + NAME_MAX
1564 	);
1565 	fname.weather[PATH_MAX + NAME_MAX - 1] = '\0';
1566 
1567 	/* Set default path to mission log file */
1568 	s = PrefixPaths(dname.local_data, SAR_DEF_MISSION_LOG_FILE);
1569 	strncpy(
1570 	    fname.mission_log,
1571 	    (s != NULL) ? s : SAR_DEF_MISSION_LOG_FILE,
1572 	    PATH_MAX + NAME_MAX
1573 	);
1574 	fname.mission_log[PATH_MAX + NAME_MAX - 1] = '\0';
1575 
1576 
1577 	/* Parse arguments */
1578 #ifdef __MSW__
1579 	/* Win32 parse arguments starting from index 0 */
1580 	for(i = 0; i < argc; i++)
1581 #else
1582 	for(i = 1; i < argc; i++)
1583 #endif
1584 	{
1585 	    arg = argv[i];
1586 	    if(arg == NULL)
1587 		 continue;
1588 
1589 	    /* Run time debug */
1590 	    if(!strcasecmp(arg, "--runtime-debug") ||
1591 	       !strcasecmp(arg, "-runtime-debug") ||
1592 	       !strcasecmp(arg, "--runtime_debug") ||
1593 	       !strcasecmp(arg, "-runtime_debug")
1594 	    )
1595 	    {
1596 		opt->runtime_debug = True;
1597 	    }
1598 	    /* Help */
1599 	    else if(!strcasecmp(arg, "--help") ||
1600 		    !strcasecmp(arg, "-help") ||
1601                     !strcasecmp(arg, "-h") ||
1602 		    !strcasecmp(arg, "-?") ||
1603 		    !strcasecmp(arg, "/h") ||
1604 		    !strcasecmp(arg, "/?") ||
1605 		    !strcasecmp(arg, "?")
1606 	    )
1607 	    {
1608 		/* Print help message */
1609 		printf(PROG_USAGE_MESG);
1610 		return(NULL);
1611 	    }
1612 	    /* Version */
1613 	    else if(!strcasecmp(arg, "--version") ||
1614 		    !strcasecmp(arg, "-version")
1615 	    )
1616 	    {
1617 		/* Print program version and copyright */
1618 		printf(
1619 		    PROG_NAME_FULL " Version " PROG_VERSION "\n"
1620 		    PROG_COPYRIGHT "\n"
1621 		);
1622 		return(NULL);
1623 	    }
1624 	    /* Configuration File */
1625 	    else if(!strcasecmp(arg, "--config") ||
1626 		    !strcasecmp(arg, "-config") ||
1627 		    !strcasecmp(arg, "--rcfile") ||
1628 		    !strcasecmp(arg, "-rcfile") ||
1629 		    !strcasecmp(arg, "-f")
1630 	    )
1631 	    {
1632 		i++;
1633 		arg = (i < argc) ? argv[i] : NULL;
1634 		if(arg != NULL)
1635 		{
1636 		    const char *path = PathSubHome(arg);
1637 		    if(path == NULL)
1638 			path = arg;
1639 		    strncpy(tmp_path, path, PATH_MAX + NAME_MAX);
1640 		    tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';
1641 
1642 		    if(!ISPATHABSOLUTE(tmp_path))
1643 		    {
1644 			path = PrefixPaths(cwd, tmp_path);
1645 			if(path != NULL)
1646 			{
1647 			    strncpy(tmp_path, path, PATH_MAX + NAME_MAX);
1648 			    tmp_path[PATH_MAX + NAME_MAX - 1] = '\0';
1649 			}
1650 		    }
1651 
1652 		    strncpy(fname.options, tmp_path, PATH_MAX + NAME_MAX);
1653 		    fname.options[PATH_MAX + NAME_MAX - 1] = '\0';
1654 		}
1655 		else
1656 		{
1657 		    fprintf(
1658 			stderr,
1659 			"%s: Requires argument.\n",
1660 			argv[i - 1]
1661 		    );
1662 		}
1663 	    }
1664 	    /* Control */
1665 	    else if(!strcasecmp(arg, "--control") ||
1666 		    !strcasecmp(arg, "-control") ||
1667 		    !strcasecmp(arg, "-c")
1668 	    )
1669 	    {
1670 		i++;
1671 		arg = (i < argc) ? argv[i] : NULL;
1672 		if(arg != NULL)
1673 		{
1674 		    const char *controller = arg;
1675 
1676 		    /* Joystick */
1677 		    if(strcasepfx(controller, "j"))
1678 			opt->gctl_controllers = GCTL_CONTROLLER_KEYBOARD |
1679 			    GCTL_CONTROLLER_POINTER | GCTL_CONTROLLER_JOYSTICK;
1680 		    /* Keyboard */
1681 		    else if(strcasepfx(controller, "k"))
1682 			opt->gctl_controllers = GCTL_CONTROLLER_KEYBOARD |
1683 			    GCTL_CONTROLLER_POINTER;
1684 		    else
1685 			fprintf(
1686 			    stderr,
1687 			    "%s: Unsupported argument `%s'\n",
1688 			    argv[i - 1], controller
1689 			);
1690 		}
1691 		else
1692 		{
1693 		    fprintf(
1694 			stderr,
1695 			"%s: Requires argument.\n",
1696 			argv[i - 1]
1697 		    );
1698 		}
1699 	    }
1700 	    /* Software rendering? */
1701 	    else if(!strcasecmp(arg, "--software_rendering") ||
1702 		    !strcasecmp(arg, "-software_rendering") ||
1703 		    !strcasecmp(arg, "--software-rendering") ||
1704 		    !strcasecmp(arg, "-software-rendering") ||
1705 		    !strcasecmp(arg, "--softwarerendering") ||
1706 		    !strcasecmp(arg, "-softwarerendering") ||
1707 		    !strcasecmp(arg, "--software") ||
1708 		    !strcasecmp(arg, "-software")
1709 	    )
1710 	    {
1711 		cl_direct_rendering = False;
1712 	    }
1713 	    /* Always Full Menu Redraws */
1714 	    else if(!strcasecmp(arg, "--full_menu_redraw") ||
1715 		    !strcasecmp(arg, "--full-menu-redraw") ||
1716 		    !strcasecmp(arg, "-full_menu_redraw") ||
1717 		    !strcasecmp(arg, "-full-menu-redraw")
1718 	    )
1719 	    {
1720 		opt->menu_always_full_redraw = True;
1721 	    }
1722 	    /* Display (connection to X server) */
1723 	    else if(!strcasecmp(arg, "--display") ||
1724 		    !strcasecmp(arg, "-display") ||
1725 		    !strcasecmp(arg, "--dpy") ||
1726 		    !strcasecmp(arg, "-dpy")
1727 	    )
1728 	    {
1729 		i++;
1730 		arg = (i < argc) ? argv[i] : NULL;
1731 		if(arg != NULL)
1732 		{
1733 		    display_address = arg;
1734 		}
1735 		else
1736 		{
1737 		    fprintf(
1738 			stderr,
1739 			"%s: Requires argument.\n",
1740 			argv[i - 1]
1741 		    );
1742 		}
1743 	    }
1744 	    /* Full screen */
1745 	    else if(!strcasecmp(arg, "--full_screen") ||
1746 		    !strcasecmp(arg, "-full_screen") ||
1747 		    !strcasecmp(arg, "--fullscreen") ||
1748 		    !strcasecmp(arg, "-fullscreen")
1749 	    )
1750 	    {
1751 		cl_fullscreen = 1;
1752 	    }
1753             else if ( (!strcasecmp(arg, "--window") ) ||
1754                       (!strcasecmp(arg, "-window") ) )
1755             {
1756                 cl_fullscreen = 2;
1757             }
1758             else if ( (!strcasecmp(arg, "--no-keyrepeat") ) ||
1759                       (!strcasecmp(arg, "--no-autorepeat") ) )
1760             {
1761                  allow_key_repeat = False;
1762             }
1763 
1764 	    /* Font (font name for XFonts, X only) */
1765 	    else if(!strcasecmp(arg, "--font") ||
1766 		    !strcasecmp(arg, "-font") ||
1767 		    !strcasecmp(arg, "--fn") ||
1768 		    !strcasecmp(arg, "-fn")
1769 	    )
1770 	    {
1771 		i++;
1772 		arg = (i < argc) ? argv[i] : NULL;
1773 		if(arg != NULL)
1774 		{
1775 		    font_name = arg;
1776 		}
1777 		else
1778 		{
1779 		    fprintf(
1780 			stderr,
1781 			"%s: Requires argument.\n",
1782 			argv[i - 1]
1783 		    );
1784 		}
1785 	    }
1786 	    /* Geometry (position and size of toplevel window) */
1787 	    else if(!strcasecmp(arg, "--geometry") ||
1788 		    !strcasecmp(arg, "-geometry")
1789 	    )
1790 	    {
1791 		i++;
1792 		arg = (i < argc) ? argv[i] : NULL;
1793 		if(arg != NULL)
1794 		{
1795 		    strncpy(geometry_string, arg, GW_GEOMETRY_STRING_MAX);
1796 		    geometry_string[GW_GEOMETRY_STRING_MAX - 1] = '\0';
1797 		}
1798 		else
1799 		{
1800 		    fprintf(
1801 			stderr,
1802 			"%s: Requires argument.\n",
1803 			argv[i - 1]
1804 		    );
1805 		}
1806 	    }
1807 	    /* Aspect offset */
1808 	    else if(!strcasecmp(arg, "--aspect_offset") ||
1809 		    !strcasecmp(arg, "-aspect_offset") ||
1810 		    !strcasecmp(arg, "--aspect-offset") ||
1811 		    !strcasecmp(arg, "-aspect-offset") ||
1812 		    !strcasecmp(arg, "--aspectoffset") ||
1813 		    !strcasecmp(arg, "-aspectoffset")
1814 	    )
1815 	    {
1816 		i++;
1817 		arg = (i < argc) ? argv[i] : NULL;
1818 		if(arg != NULL)
1819 		{
1820 		    aspect_offset = ATOF(arg);
1821 		}
1822 		else
1823 		{
1824 		    fprintf(
1825 			stderr,
1826 			"%s: Requires argument.\n",
1827 			argv[i - 1]
1828 		    );
1829 		}
1830 	    }
1831 	    /* Recorder */
1832 	    else if(!strcasecmp(arg, "--recorder") ||
1833 		    !strcasecmp(arg, "-recorder")
1834 	    )
1835 	    {
1836 		i++;
1837 		arg = (i < argc) ? argv[i] : NULL;
1838 		if(arg != NULL)
1839 		{
1840 		    sound_server_connect_arg = arg;
1841 		}
1842 		else
1843 		{
1844 		    fprintf(
1845 			stderr,
1846 			"%s: Requires argument.\n",
1847 			argv[i - 1]
1848 		    );
1849 		}
1850 	    }
1851 	    /* No sound */
1852 	    else if(!strcasecmp(arg, "--no_sound") ||
1853 		    !strcasecmp(arg, "--nosound") ||
1854 		    !strcasecmp(arg, "-no_sound") ||
1855 		    !strcasecmp(arg, "-nosound")
1856 	    )
1857 	    {
1858 		startup_no_sound = True;
1859 	    }
1860 	    /* No menu backgrounds */
1861 	    else if(!strcasecmp(arg, "--nomenubackgrounds") ||
1862 		    !strcasecmp(arg, "--nomenubackground") ||
1863 		    !strcasecmp(arg, "--nomenubkg") ||
1864 		    !strcasecmp(arg, "--nomenubg") ||
1865 		    !strcasecmp(arg, "-nomenubackgrounds") ||
1866 		    !strcasecmp(arg, "-nomenubackground") ||
1867 		    !strcasecmp(arg, "-nomenubkg") ||
1868 		    !strcasecmp(arg, "-nomenubg")
1869 	    )
1870 	    {
1871 		opt->menu_backgrounds = False;
1872 	    }
1873 
1874 	}
1875 
1876 
1877 	/* Check if program has been installed globally.
1878 	 * Win32 note this will be the same as installed locally.
1879 	 */
1880 	if(!SARIsInstalledGlobal(&dname, &fname))
1881 	{
1882 	    /* Not globally installed so it is a critical error */
1883 	    fprintf(
1884 		stderr,
1885 PROG_NAME_FULL " was unable to find the data files in:\n\
1886 \n\
1887     %s\n",
1888 		dname.global_data
1889 	    );
1890 	    fprintf(
1891 		stderr,
1892 "Please verify that you have installed the program properly. If you \
1893 have installed the global data files in a non-standard location, \
1894 then you should set the environment variable \"%s\" to refer to that \
1895 non-standard location.\n",
1896 		SAR_DEF_ENV_GLOBAL_DIR
1897 	    );
1898 	    free(core_ptr);
1899 	    return(NULL);
1900 	}
1901 	/* Check if program is installed locally */
1902 	if(!SARIsInstalledLocal(&dname, &fname))
1903 	{
1904 	    /* Not locally installed, so install from global */
1905 	    if(SARDoInstallLocal(&dname, &fname, opt))
1906 	    {
1907 		fprintf(
1908 		    stderr,
1909 PROG_NAME_FULL " was unable to complete the installation of local data files in:\n\
1910 \n\
1911     %s\n",
1912 		    dname.local_data
1913 		);
1914 		notify_install_local_error = True;
1915 	    }
1916 	    else
1917 	    {
1918 		notify_install_local_success = True;
1919 	    }
1920 	}
1921 
1922 	/* Load values from configuration file */
1923 	if(SAROptionsLoadFromFile(opt, fname.options))
1924 	{
1925 	    fprintf(
1926 		stderr,
1927  "%s: Warning: Error occured while loading configuration.\n",
1928 		fname.options
1929 	    );
1930 	}
1931 
1932 	/* Record program file name */
1933 #ifdef __MSW__
1934 	if(hInst != NULL)
1935 	{
1936 	    char prog[PATH_MAX];
1937 	    GetModuleFileName(hInst, prog, sizeof(prog));
1938 
1939 	    s = strrchr(prog, DIR_DELIMINATOR);
1940 	    if(s != NULL)
1941 		s++;
1942 	    else
1943 		s = prog;
1944 
1945 	    free(core_ptr->prog_file_full_path);
1946 	    core_ptr->prog_file_full_path = STRDUP(prog);
1947 
1948 	    free(core_ptr->prog_file);
1949 	    core_ptr->prog_file = STRDUP(s);
1950 	}
1951 #else
1952 	arg = (argc > 0) ? argv[0] : NULL;
1953 	if(arg != NULL)
1954 	{
1955 	    s = strrchr(arg, DIR_DELIMINATOR);
1956 	    if(s != NULL)
1957 		s++;
1958 	    else
1959 		s = arg;
1960 
1961 	    free(core_ptr->prog_file_full_path);
1962 	    core_ptr->prog_file_full_path = STRDUP(arg);
1963 
1964 	    free(core_ptr->prog_file);
1965 	    core_ptr->prog_file = STRDUP(s);
1966 	}
1967 #endif
1968 
1969 	/* Set up arguments for initializing the graphics wrapper */
1970 	strc = 11;
1971 	strv = (char **)calloc(strc, sizeof(char *));
1972 	strv[0] = STRDUP(cl_direct_rendering ?
1973 	    "--hardware_rendering" : "--software_rendering"
1974 	);
1975 	strv[1] = STRDUP("--geometry");
1976 	if(*geometry_string == '\0')
1977 	    sprintf(
1978 		geometry_string,
1979 		"%ix%i%s%i%s%i",
1980 		opt->last_width,
1981 		opt->last_height,
1982 		((opt->last_x < 0) ? "" : "+"),
1983 		opt->last_x,
1984 		((opt->last_y < 0) ? "" : "+"),
1985 		opt->last_y
1986 	    );
1987 	strv[2] = STRDUP(geometry_string);
1988 	strv[3] = STRDUP("--title");
1989 	strv[4] = STRDUP(PROG_NAME_FULL);
1990 	strv[5] = STRDUP("--icon_path");
1991 	strv[6] = STRDUP(SAR_DEF_SAR_ICON_FILE);
1992 	strv[7] = STRDUP("--icon_name");
1993 	strv[8] = STRDUP(PROG_NAME_FULL);
1994 	if(cl_fullscreen == 1)
1995 	    strv[9] = STRDUP("--full_screen");
1996         else if (cl_fullscreen == 2)
1997         {
1998             strv[9] = STRDUP("--window");
1999             opt->last_fullscreen = False;
2000         }
2001 	else
2002 	    strv[9] = STRDUP(opt->last_fullscreen ?
2003 		"--fullscreen" : "--windowed"
2004 	    );
2005         if (! allow_key_repeat)
2006            strv[10] = STRDUP("--no-keyrepeat");
2007 
2008 	if(!STRISEMPTY(display_address))
2009 	{
2010 	    int n = strc;
2011 	    strv = (char **)realloc(strv, strc * sizeof(char *));
2012 	    strv[n + 0] = STRDUP("--display");
2013 	    strv[n + 1] = STRDUP(display_address);
2014 	}
2015 	if(aspect_offset != 0.0)
2016 	{
2017 	    int n = strc;
2018 	    char num_str[80];
2019 	    sprintf(num_str, "%f", aspect_offset);
2020 	    strc = n + 2;
2021 	    strv = (char **)realloc(strv, strc * sizeof(char *));
2022 	    strv[n + 0] = STRDUP("--aspect_offset");
2023 	    strv[n + 1] = STRDUP(num_str);
2024 	}
2025 	if(opt->runtime_debug)
2026 	{
2027 	    int n = strc;
2028 	    strc = n + 1;
2029 	    strv = (char **)realloc(strv, strc * sizeof(char *));
2030 	    strv[n + 0] = STRDUP("--gw_debug");
2031 	}
2032 	if(font_name != NULL)
2033 	{
2034 	    int n = strc;
2035 	    strc = n + 2;
2036 	    strv = (char **)realloc(strv, strc * sizeof(char *));
2037 	    strv[n + 0] = STRDUP("--font");
2038 	    strv[n + 1] = STRDUP(font_name);
2039 	}
2040 
2041 	/* Initialize graphics erapper */
2042 	core_ptr->display = GWInit(strc, strv);
2043 
2044 	/* Delete arguments used in initializing graphics wrapper */
2045 	strlistfree(strv, strc);
2046 	strv = NULL;
2047 	strc = 0;
2048 
2049 	/* Failed to initialize graphics wrapper? */
2050 	if(core_ptr->display == NULL)
2051 	{
2052 	    SARShutdown(core_ptr);
2053 	    return(NULL);
2054 	}
2055 	dpy = core_ptr->display;
2056 
2057 	/* Check if we have OpenGL 1.1 or newer */
2058 	if((dpy->gl_version_major < 1) ?
2059 	    True : ((dpy->gl_version_major == 1) ?
2060 		(dpy->gl_version_minor < 1) : False)
2061 	)
2062 	{
2063 	    fprintf(
2064 		stderr,
2065 PROG_NAME_FULL " requires OpenGL version 1.1 or newer, the current \
2066 version that was detected is OpenGL version %i.%i.\n",
2067 		dpy->gl_version_major,
2068 		dpy->gl_version_minor
2069 	    );
2070 	}
2071 
2072 	/* Check if we have alpha bits */
2073 	if(dpy->alpha_channel_bits <= 0)
2074 	{
2075 	    fprintf(
2076 		stderr,
2077 PROG_NAME_FULL " requires an alpha channel, the current alpha channel \
2078 that was detected is 0 bits in size.\n"
2079 	    );
2080 	}
2081 
2082 	/* Set font data references */
2083 	opt->hud_font = font_6x10;		/* For HUD text */
2084 	opt->message_font = font_7x14;	/* For standard/misc messages */
2085 	opt->menu_font = font_menu;		/* For menus (except for menu values */
2086 	opt->banner_font = font_banner;	/* For the sticky banner messages */
2087 
2088 	/* Set graphics wrapper options */
2089 	GWSetDrawCB(dpy, SARDrawCB, core_ptr);
2090 	GWSetKeyboardCB(dpy, SARKeyBoardCB, core_ptr);
2091 	GWSetPointerCB(dpy, SARPointerCB, core_ptr);
2092 	GWSetResizeCB(dpy, SARReshapeCB, core_ptr);
2093 	GWSetVisibilityCB(dpy, SARVisibilityCB, core_ptr);
2094 	GWSetSaveYourselfCB(dpy, SARSaveYourselfCB, core_ptr);
2095 	GWSetCloseCB(dpy, SARCloseCB, core_ptr);
2096 	GWSetTimeoutCB(dpy, SARManage, core_ptr);
2097 
2098 
2099 	/* Do splash */
2100 	SARSplash(core_ptr);
2101 
2102 
2103 	/* Set audio mode name for changing of sound server audio mode */
2104 	free(core_ptr->audio_mode_name);
2105 	core_ptr->audio_mode_name = STRDUP("PlayStereo11025");
2106 
2107 	/* Initialize sound wrapper */
2108 	if (startup_no_sound) /* ||
2109 	   (!opt->engine_sounds &&
2110 	    !opt->event_sounds &&
2111 	    !opt->voice_sounds &&
2112 	    !opt->music
2113 	   )
2114 	) */
2115 	{
2116 	    /* Do not initialize sound and turn all sound options off */
2117 	    core_ptr->recorder = NULL;
2118 	    opt->engine_sounds = False;
2119 	    opt->event_sounds = False;
2120 	    opt->voice_sounds = False;
2121 	    opt->music = False;
2122 	}
2123 	else
2124 	{
2125 	    void *window;
2126             int type = SNDSERV_TYPE_NONE; // use this as default
2127 
2128             #ifdef Y_H
2129             type = SNDSERV_TYPE_Y;
2130             #endif
2131             #ifdef SDL_H
2132             type = SNDSERV_TYPE_SDL;    // hopefully we use this
2133             #endif
2134 
2135 	    GWContextGet(
2136 		dpy, GWContextCurrent(dpy),
2137 		&window, NULL,
2138 		NULL, NULL,
2139 		NULL, NULL
2140 	    );
2141 	    core_ptr->recorder = SoundInit(
2142 		core_ptr,
2143 	        type,
2144 	        sound_server_connect_arg,	/* Connect argument */
2145 		NULL,				/* Start argument */
2146 		window				/* Toplevel window */
2147 	    );
2148 
2149 	    if(core_ptr->recorder == NULL)
2150 	    {
2151 	        fprintf(
2152 		    stderr,
2153 PROG_NAME_FULL " could not connect to the Sound Server, please check \
2154 if the Sound Server is currently running.\n"
2155 		);
2156 
2157 		opt->engine_sounds = False;
2158 		opt->event_sounds = False;
2159 		opt->voice_sounds = False;
2160 		opt->music = False;
2161 	    }
2162             #ifdef Y_H
2163 	    else
2164 	    {
2165 		/* Change Y audio mode */
2166 	        SoundChangeMode(
2167 		    core_ptr->recorder, core_ptr->audio_mode_name
2168 		);
2169 
2170 		/* Update recorder address */
2171 		if(sound_server_connect_arg != NULL)
2172 		{
2173 		    free(core_ptr->recorder_address);
2174 		    core_ptr->recorder_address = STRDUP(sound_server_connect_arg);
2175 		}
2176 	    }
2177             #endif
2178 	}
2179 
2180 
2181 	/* Initialize game controller
2182 	 *
2183 	 * Game controller options need to be set up properly prior to
2184 	 * this call
2185 	 */
2186 	SARInitGCTL(core_ptr);
2187 
2188 
2189 	/* Load global texture reference names list (these contain only
2190 	 * names and file names of textures to be loaded when a scene
2191 	 * is loaded)
2192 	 */
2193 	SARTextureListLoadFromFile(
2194 	    fname.textures,
2195 	    &core_ptr->texture_list,
2196 	    &core_ptr->total_texture_list
2197 	);
2198 
2199 	/* Initialize predefined humans list */
2200 	core_ptr->human_data = SARHumanPresetsInit(core_ptr);
2201 	if(core_ptr->human_data == NULL)
2202 	{
2203 	    fprintf(
2204 		stderr,
2205 		"Error initializing human presets data.\n"
2206 	    );
2207 	}
2208 	/* Load predefined humans list from file */
2209 	SARHumanLoadFromFile(
2210 	    core_ptr->human_data,
2211 	    fname.human
2212 	);
2213 
2214 	/* Reset current music ID so that the music will restart after
2215 	 * the music list is loaded from file
2216 	 */
2217 	core_ptr->cur_music_id = -1;
2218 	/* Load music list from file */
2219 	SARMusicListLoadFromFile(
2220 	    fname.music,
2221 	    &core_ptr->music_ref, &core_ptr->total_music_refs
2222 	);
2223 
2224 	/* Initialize predefined weather conditions */
2225 	core_ptr->weather_data = SARWeatherPresetsInit(core_ptr);
2226 	if(core_ptr->weather_data == NULL)
2227 	{
2228 	    fprintf(
2229 		stderr,
2230 		"Error initializing weather presets data.\n"
2231 	    );
2232 	}
2233 	/* Load predefined weather conditions from file */
2234 	SARWeatherLoadFromFile(
2235 	    core_ptr->weather_data,
2236 	    fname.weather
2237 	);
2238 
2239 	/* Load players list */
2240 	SARPlayerStatsLoadFromFile(
2241 	    &core_ptr->player_stat,
2242 	    &core_ptr->total_player_stats,
2243 	    fname.players
2244 	);
2245 
2246 
2247 
2248 	/* Reset GL states for menu system */
2249 	SARMenuGLStateReset(core_ptr->display);
2250 
2251 	/* Create all menu system resources and each menu */
2252 	SARBuildMenus(core_ptr);
2253 
2254 	/* Force select the main menu */
2255 	core_ptr->cur_menu = -1;
2256 	SARMenuSwitchToMenu(core_ptr, SAR_MENU_NAME_MAIN);
2257 
2258 	/* Text input prompt */
2259 	core_ptr->text_input = SARTextInputNew(
2260 	    core_ptr->display, opt->message_font
2261 	);
2262 
2263 	/* Print messages as needed */
2264 	if(notify_install_local_error)
2265 	{
2266 	    char mesg[1024];
2267 
2268 	    sprintf(mesg,
2269 "Cannot install local data files to:\n%s",
2270 		dname.local_data
2271 	    );
2272 
2273 	    GWOutputMessage(
2274 		dpy, GWOutputMessageTypeError,
2275 		"Installation Error",
2276 		mesg,
2277 "There was a problem installing local data files, make sure that\n\
2278 global data for this program has been installed properly and that\n\
2279 your home directory exists and has has write permission for this program"
2280 	    );
2281 	}
2282 	else if(notify_install_local_success)
2283 	{
2284 	    char mesg[1024];
2285 
2286 	    sprintf(mesg,
2287 "Local data files for this program have been\ninstalled in:\n\n%s",
2288 		dname.local_data
2289 	    );
2290 
2291 	    GWOutputMessage(
2292 		dpy, GWOutputMessageTypeGeneral,
2293 		"Local Data Files Installed",
2294 		mesg,
2295 "The data files needed by this program have been installed, this\n\
2296 occured because the program needs them and they were not detected\n\
2297 to exist before."
2298 	    );
2299 	}
2300 
2301 	return(core_ptr);
2302 }
2303 
2304 /*
2305  *	SAR management, this is called once per loop as the timeout
2306  *	function.
2307  *
2308  *	The graphics wrapper normally calls this function on timeouts.
2309  */
SARManage(void * ptr)2310 void SARManage(void *ptr)
2311 {
2312 	int status;
2313 	int cur_menu;
2314 	time_t t_new;
2315 	sar_scene_struct *scene;
2316 	sar_core_struct *core_ptr = SAR_CORE(ptr);
2317 	const sar_option_struct *opt;
2318 	if(core_ptr == NULL)
2319 	    return;
2320 
2321 	opt = &core_ptr->option;
2322 
2323 	/* Get current time in milliseconds */
2324 	t_new = SARGetCurMilliTime();
2325 
2326 	/* Check if the new current time has "warped" to a smaller value
2327 	 * than the previous current time.
2328 	 */
2329 	if(t_new < cur_millitime)
2330 	{
2331 	    /* Timing has cycled, so we need to reset all timmers */
2332 	    SARResetTimmersCB(core_ptr, t_new);
2333 	}
2334 	else
2335 	{
2336 	    /* Calculate lapsed ms from last loop */
2337 	    lapsed_millitime = t_new - cur_millitime;
2338 
2339 	    /* Calculate time compensation coeff */
2340 	    time_compensation = (float)CLIP(
2341 		(float)lapsed_millitime / (float)CYCLE_LAPSE_MS,
2342 		0.0, 1000.0
2343 	    );
2344 
2345 	    /* Set new current time in ms */
2346 	    cur_millitime = t_new;
2347 	}
2348 
2349 	/* Get current systime seconds */
2350 	cur_systime = time(NULL);
2351 
2352 
2353 	/* Get new game controller positions */
2354 	GCtlUpdate(
2355 	    core_ptr->gctl,
2356 	    (Boolean)((opt->flight_physics_level != FLIGHT_PHYSICS_REALISTIC) ? True : False),	/* Heading nullzone? */
2357 	    (Boolean)((opt->flight_physics_level == FLIGHT_PHYSICS_EASY) ? True : False),	/* Pitch nullzone? */
2358 	    (Boolean)((opt->flight_physics_level == FLIGHT_PHYSICS_EASY) ? True : False),	/* Bank nullzone? */
2359 	    cur_millitime, lapsed_millitime, time_compensation
2360 	);
2361 
2362 
2363 	/* Check if a current menu is allocated (hence selected) */
2364 	cur_menu = core_ptr->cur_menu;
2365 	if(SARIsMenuAllocated(core_ptr, cur_menu))
2366 	{
2367 	    /* A menu is selected which implies that we are in the
2368 	     * menus.  Get current menu to see which menu we're
2369 	     * currently on.
2370 	     */
2371 	    SARMenuManage(core_ptr, core_ptr->menu[cur_menu]);
2372 	}
2373 	else
2374 	{
2375 	    /* No menu selected, this implies we are in game and
2376 	     * as such we need to handle simulation updates and
2377 	     * redraw.
2378 	     */
2379 	    scene = core_ptr->scene;
2380 
2381 	    SARSimUpdateScene(core_ptr, scene);
2382 	    SARSimUpdateSceneObjects(core_ptr, scene);
2383 
2384 	    if(is_visible)
2385 		SARDraw(core_ptr);
2386 
2387 	    /* Manage mission */
2388 	    status = SARMissionManage(core_ptr);
2389 	    /* Check mission manage result */
2390 	    switch(status)
2391 	    {
2392 	      case 1:
2393 		/* Mission ended with success, end simulation and
2394 		 * tabulate mission results.
2395 		 */
2396 		SARSimEnd(core_ptr);
2397 		break;
2398 
2399 	      case 2:
2400 		/* Mission over and failed. Do not call SARSimEnd(),
2401 		 * instead let user respond to failed message and
2402 		 * manually end mission.
2403 		 */
2404 		break;
2405 
2406 	      case -1:
2407 		/* Mission management error */
2408 		break;
2409 
2410 	      default:
2411 		/* Nothing eventful */
2412 		break;
2413 	    }
2414 	}
2415 
2416 	/* Manage sound events if connected to recorder */
2417 	if(core_ptr->recorder != NULL)
2418 	{
2419 	    /* Manage sound events.  If return is negative then that
2420 	     * means the recorder pointer is no longer valid.
2421 	     */
2422 	    status = SoundManageEvents(core_ptr->recorder);
2423 	    if(status < 0)
2424 		core_ptr->recorder = NULL;
2425 	}
2426 
2427 	/* Update music, checks the current run time situation and
2428 	 * changes the music as needed.
2429 	 */
2430 	SARMusicUpdate(core_ptr);
2431 
2432 }
2433 
2434 /*
2435  *	Deletes the Core.
2436  */
SARShutdown(sar_core_struct * core_ptr)2437 void SARShutdown(sar_core_struct *core_ptr)
2438 {
2439 	const char *s;
2440 	sar_option_struct *opt;
2441 
2442 	if(core_ptr == NULL)
2443 	    return;
2444 
2445 	opt = &core_ptr->option;
2446 
2447 	/* Save options */
2448 	s = fname.options;
2449 	if(!STRISEMPTY(s))
2450 	{
2451 	    if(SAROptionsSaveToFile(opt, s))
2452 		fprintf(
2453 		    stderr,
2454 "%s: Warning: Error occured while saving options.\n",
2455 		    s
2456 		);
2457 	}
2458 	/* Save players list */
2459 	s = fname.players;
2460 	if(!STRISEMPTY(s))
2461 	{
2462 	    if(SARPlayerStatsSaveToFile(
2463 		core_ptr->player_stat, core_ptr->total_player_stats,
2464 		s
2465 	    ))
2466 		fprintf(
2467 		    stderr,
2468 "%s: Warning: Error occured while saving pilots list.\n",
2469 		    s
2470 		);
2471 	}
2472 
2473 
2474 	/* Text input */
2475 	SARTextInputDelete(core_ptr->text_input);
2476 	core_ptr->text_input = NULL;
2477 
2478 	/* Global texture file reference list */
2479 	SARTextureListDeleteAll(
2480 	    &core_ptr->texture_list,
2481 	    &core_ptr->total_texture_list
2482 	);
2483 
2484 	/* File names */
2485 	free(core_ptr->cur_mission_file);
2486 	core_ptr->cur_mission_file = NULL;
2487 
2488 	free(core_ptr->cur_player_model_file);
2489 	core_ptr->cur_player_model_file = NULL;
2490 
2491 	free(core_ptr->cur_scene_file);
2492 	core_ptr->cur_scene_file = NULL;
2493 
2494 
2495 	/* Menus */
2496 	if(core_ptr->total_menus > 0)
2497 	{
2498 	    int i, n;
2499 	    for(i = 0; i < core_ptr->total_menus; i++)
2500 	    {
2501 		sar_menu_struct *m = core_ptr->menu[i];
2502 		if(m == NULL)
2503 		    continue;
2504 
2505 		/* Iterate through each object on the menu */
2506 		for(n = 0; n < m->total_objects; n++)
2507 		{
2508 		    void *o = m->object[n];
2509 		    if(o == NULL)
2510 			continue;
2511 
2512 		    /* Handle by the menu object's type */
2513 		    switch(SAR_MENU_OBJECT_TYPE(o))
2514 		    {
2515 		      case SAR_MENU_OBJECT_TYPE_LABEL:
2516 		      case SAR_MENU_OBJECT_TYPE_BUTTON:
2517 		      case SAR_MENU_OBJECT_TYPE_PROGRESS:
2518 		      case SAR_MENU_OBJECT_TYPE_MESSAGE_BOX:
2519 		        break;
2520 		      case SAR_MENU_OBJECT_TYPE_LIST:
2521 		        {
2522 			    int j;
2523 			    sar_menu_list_struct *list = SAR_MENU_LIST(o);
2524 			    /* Delete list item data */
2525 			    for(j = 0; j < list->total_items; j++)
2526 			        SARDeleteListItemData(list->item[j]);
2527 			}
2528 			break;
2529 		      case SAR_MENU_OBJECT_TYPE_MDISPLAY:
2530 		      case SAR_MENU_OBJECT_TYPE_SWITCH:
2531 		      case SAR_MENU_OBJECT_TYPE_SPIN:
2532 		      case SAR_MENU_OBJECT_TYPE_SLIDER:
2533 		      case SAR_MENU_OBJECT_TYPE_MAP:
2534 		      case SAR_MENU_OBJECT_TYPE_OBJVIEW:
2535 		        break;
2536 		    }
2537 		}
2538 
2539 		/* Delete this menu and all its objects */
2540 		SARMenuDelete(m);
2541 	    }
2542 	    free(core_ptr->menu);
2543 	    core_ptr->menu = NULL;
2544 	    core_ptr->cur_menu = 0;
2545 	    core_ptr->total_menus = 0;
2546 	}
2547 
2548 	/* Begin deleted resources used for the menus, these resources
2549 	 * may be shared but since all menus are deleted at this
2550 	 * point it is safe to delete these resources
2551 	 */
2552 
2553 	/* Images used by menus */
2554 #define DELETE_IMAGE(_i_)	\
2555 { if(*(_i_) != NULL) { SARImageDelete(*(_i_)); *(_i_) = NULL; } }
2556 	if(core_ptr->menu_list_bg_img != NULL)
2557 	{
2558 	    int i;
2559 	    for(i = 0; i < 9; i++)
2560 		DELETE_IMAGE(&core_ptr->menu_list_bg_img[i]);
2561 	    free(core_ptr->menu_list_bg_img);
2562 	    core_ptr->menu_list_bg_img = NULL;
2563 	}
2564 
2565 	DELETE_IMAGE(&core_ptr->menu_button_armed_img);
2566 	DELETE_IMAGE(&core_ptr->menu_button_unarmed_img);
2567 	DELETE_IMAGE(&core_ptr->menu_button_highlighted_img);
2568 	DELETE_IMAGE(&core_ptr->menu_button_label_img);
2569 
2570 	DELETE_IMAGE(&core_ptr->menu_label_bg_img);
2571 
2572 	DELETE_IMAGE(&core_ptr->menu_switch_bg_img);
2573 	DELETE_IMAGE(&core_ptr->menu_switch_off_img);
2574 	DELETE_IMAGE(&core_ptr->menu_switch_on_img);
2575 
2576 	DELETE_IMAGE(&core_ptr->menu_spin_label_img);
2577 	DELETE_IMAGE(&core_ptr->menu_spin_value_img);
2578 	DELETE_IMAGE(&core_ptr->menu_spin_dec_armed_img);
2579 	DELETE_IMAGE(&core_ptr->menu_spin_dec_unarmed_img);
2580 	DELETE_IMAGE(&core_ptr->menu_spin_inc_armed_img);
2581 	DELETE_IMAGE(&core_ptr->menu_spin_inc_unarmed_img);
2582 
2583 	DELETE_IMAGE(&core_ptr->menu_slider_label_img);
2584 	DELETE_IMAGE(&core_ptr->menu_slider_trough_img);
2585 	DELETE_IMAGE(&core_ptr->menu_slider_handle_img);
2586 
2587 	DELETE_IMAGE(&core_ptr->menu_progress_bg_img);
2588 	DELETE_IMAGE(&core_ptr->menu_progress_fg_img);
2589 
2590 	DELETE_IMAGE(&core_ptr->menu_button_pan_up_armed_img);
2591 	DELETE_IMAGE(&core_ptr->menu_button_pan_up_unarmed_img);
2592 	DELETE_IMAGE(&core_ptr->menu_button_pan_down_armed_img);
2593 	DELETE_IMAGE(&core_ptr->menu_button_pan_down_unarmed_img);
2594 	DELETE_IMAGE(&core_ptr->menu_button_pan_left_armed_img);
2595 	DELETE_IMAGE(&core_ptr->menu_button_pan_left_unarmed_img);
2596 	DELETE_IMAGE(&core_ptr->menu_button_pan_right_armed_img);
2597 	DELETE_IMAGE(&core_ptr->menu_button_pan_right_unarmed_img);
2598 
2599 	DELETE_IMAGE(&core_ptr->menu_button_zoom_in_armed_img);
2600 	DELETE_IMAGE(&core_ptr->menu_button_zoom_in_unarmed_img);
2601 	DELETE_IMAGE(&core_ptr->menu_button_zoom_out_armed_img);
2602 	DELETE_IMAGE(&core_ptr->menu_button_zoom_out_unarmed_img);
2603 
2604 	DELETE_IMAGE(&core_ptr->menumap_helipad_img);
2605 	DELETE_IMAGE(&core_ptr->menumap_intercept_img);
2606 	DELETE_IMAGE(&core_ptr->menumap_helicopter_img);
2607 	DELETE_IMAGE(&core_ptr->menumap_victim_img);
2608 	DELETE_IMAGE(&core_ptr->menumap_vessel_img);
2609 	DELETE_IMAGE(&core_ptr->menumap_crash_img);
2610 
2611 #undef DELETE_IMAGE
2612 
2613 
2614 	/* Delete mission structure (if any). Note that mission should
2615 	 * have been properly ended and deleted before shutting down
2616 	 */
2617 	SARMissionDelete(core_ptr->mission);
2618 	core_ptr->mission = NULL;
2619 
2620 	/* Delete scene */
2621 	SARSceneDestroy(
2622 	    core_ptr,
2623 	    core_ptr->scene,
2624 	    &core_ptr->object,
2625 	    &core_ptr->total_objects
2626 	);
2627 	free(core_ptr->scene);
2628 	core_ptr->scene = NULL;
2629 
2630 	/* Draw Map Object Names List */
2631 	SARDrawMapObjNameListDelete(
2632 	    &core_ptr->drawmap_objname,
2633 	    &core_ptr->total_drawmap_objnames
2634 	);
2635 
2636 	/* Player Stats */
2637 	if(core_ptr->total_player_stats > 0)
2638 	{
2639 	    int i;
2640 	    for(i = 0; i < core_ptr->total_player_stats; i++)
2641 		SARPlayerStatDelete(core_ptr->player_stat[i]);
2642 	    free(core_ptr->player_stat);
2643 	    core_ptr->player_stat = NULL;
2644 	    core_ptr->total_player_stats = 0;
2645 	}
2646 
2647 	/* Human Data */
2648 	SARHumanPresetsShutdown(core_ptr->human_data);
2649 	core_ptr->human_data = NULL;
2650 
2651 	/* Weather Data */
2652 	SARWeatherPresetsShutdown(core_ptr->weather_data);
2653 	core_ptr->weather_data = NULL;
2654 
2655 
2656 	/* Game Controller */
2657 	GCtlDelete(core_ptr->gctl);
2658 	core_ptr->gctl = NULL;
2659 
2660 	/* Music List */
2661 	SARMusicListDeleteAll(&core_ptr->music_ref, &core_ptr->total_music_refs);
2662 
2663 	/* Sound wrapper */
2664 	SoundShutdown(core_ptr->recorder);
2665 	core_ptr->recorder = NULL;
2666 
2667 	free(core_ptr->audio_mode_name);
2668 	core_ptr->audio_mode_name = NULL;
2669 
2670 	free(core_ptr->recorder_address);
2671 	core_ptr->recorder_address = NULL;
2672 
2673 	/* Graphics wrapper */
2674 	GWShutdown(core_ptr->display);
2675 	core_ptr->display = NULL;
2676 
2677 	/* Program name */
2678 	free(core_ptr->prog_file_full_path);
2679 	core_ptr->prog_file_full_path = NULL;
2680 	free(core_ptr->prog_file);
2681 	core_ptr->prog_file = NULL;
2682 
2683 	free(core_ptr);
2684 }
2685 
2686 #ifdef __MSW__
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)2687 int WINAPI WinMain(
2688 	HINSTANCE hInstance,		// Instance
2689 	HINSTANCE hPrevInstance,	// Previous Instance
2690 	LPSTR lpCmdLine,		// Command Line Parameters
2691 	int nCmdShow			// Window Show State
2692 )
2693 #else
2694 int main(int argc, char **argv)
2695 #endif
2696 {
2697 #ifdef __MSW__
2698 	int argc = 0;
2699 	char **argv = strexp(lpCmdLine, &argc);
2700 #endif
2701 	sar_core_struct *core_ptr;
2702 	const sar_option_struct *opt;
2703 
2704 
2705 	/* Reset globals */
2706 	debug_value = 0.0f;
2707 	segfault_count = 0;
2708 
2709 #ifdef __MSW__
2710 	/* Initialize COM loaders (needed for DirectX) */
2711 	CoInitialize(NULL);
2712 #endif
2713 
2714 	/* Initialize program core */
2715 	core_ptr = SARInit(argc, argv);
2716 	if(core_ptr == NULL)
2717 	    return(1);
2718 	opt = &core_ptr->option;
2719 
2720 	/* Main loop */
2721 	runlevel = 2;
2722 	while(runlevel >= 2)
2723 	{
2724 #ifdef __MSW__
2725 	    /* No sleeping for Windows */
2726 #else
2727 	    usleep(
2728 		(unsigned long)CLIP(
2729 		    (8000 * (1.0 - opt->graphics_acceleration)), 0, 1000
2730 		)
2731 	    );
2732 #endif
2733 	    GWManage(core_ptr->display);
2734 	}
2735 
2736 	/* Shutdown program core */
2737 	SARShutdown(core_ptr);
2738 	core_ptr = NULL;
2739 
2740 #ifdef __MSW__
2741 	/* Free command line arguments */
2742 	strlistfree(argv, argc);
2743 
2744 	/* Shutdown COM loader (needed for DirectX) */
2745 	CoUninitialize();
2746 #endif
2747 
2748 	return(0);
2749 }
2750