1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * Startup and quit functions. Handles signals, inits the
31 * memory management, then calls D_DoomMain. Also contains
32 * I_Init which does other system-related startup stuff.
33 *
34 *-----------------------------------------------------------------------------
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 #ifdef _WIN32
46 #define WIN32_LEAN_AND_MEAN
47 #include <windows.h>
48 typedef BOOL (WINAPI *SetAffinityFunc)(HANDLE hProcess, DWORD mask);
49 #else
50 #include <sched.h>
51 #endif
52
53 #include <errno.h>
54
55 #include "TEXTSCREEN/txt_main.h"
56
57 #include "doomdef.h"
58 #include "m_argv.h"
59 #include "d_main.h"
60 #include "m_fixed.h"
61 #include "i_system.h"
62 #include "i_video.h"
63 #include "z_zone.h"
64 #include "lprintf.h"
65 #include "m_random.h"
66 #include "doomstat.h"
67 #include "g_game.h"
68 #include "m_misc.h"
69 #include "i_sound.h"
70 #include "i_main.h"
71 #include "r_fps.h"
72 #include "lprintf.h"
73
74 #include <signal.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77
78 #include "i_simd.h"
79 #include "e6y.h"
80
81 /* Most of the following has been rewritten by Lee Killough
82 *
83 * I_GetTime
84 * killough 4/13/98: Make clock rate adjustable by scale factor
85 * cphipps - much made static
86 */
87
88 int realtic_clock_rate = 100;
89 static int_64_t I_GetTime_Scale = 1<<24;
90
I_GetTime_Scaled(void)91 static int I_GetTime_Scaled(void)
92 {
93 return (int)( (int_64_t) I_GetTime_RealTime() * I_GetTime_Scale >> 24);
94 }
95
96
97
I_GetTime_FastDemo(void)98 static int I_GetTime_FastDemo(void)
99 {
100 static int fasttic;
101 return fasttic++;
102 }
103
104
105
I_GetTime_Error(void)106 static int I_GetTime_Error(void)
107 {
108 I_Error("I_GetTime_Error: GetTime() used before initialization");
109 return 0;
110 }
111
112
113
114 int (*I_GetTime)(void) = I_GetTime_Error;
115
I_Init(void)116 void I_Init(void)
117 {
118 /* killough 4/14/98: Adjustable speedup based on realtic_clock_rate */
119 if (fastdemo)
120 I_GetTime = I_GetTime_FastDemo;
121 else
122 if (realtic_clock_rate != 100)
123 {
124 I_GetTime_Scale = ((int_64_t) realtic_clock_rate << 24) / 100;
125 I_GetTime = I_GetTime_Scaled;
126 }
127 else
128 I_GetTime = I_GetTime_RealTime;
129
130 {
131 /* killough 2/21/98: avoid sound initialization if no sound & no music */
132 if (!(nomusicparm && nosfxparm))
133 I_InitSound();
134 }
135
136 R_InitInterpolation();
137
138 //e6y
139 #ifdef SIMD_INSTRUCTIONS
140 I_InitSIMD();
141 #endif
142 }
143
144 //e6y
I_Init2(void)145 void I_Init2(void)
146 {
147 if (fastdemo)
148 I_GetTime = I_GetTime_FastDemo;
149 else
150 {
151 if (realtic_clock_rate != 100)
152 {
153 I_GetTime_Scale = ((int_64_t) realtic_clock_rate << 24) / 100;
154 I_GetTime = I_GetTime_Scaled;
155 }
156 else
157 I_GetTime = I_GetTime_RealTime;
158 }
159 R_InitInterpolation();
160 force_singletics_to = gametic + BACKUPTICS;
161 }
162
163 /* cleanup handling -- killough:
164 */
I_SignalHandler(int s)165 static void I_SignalHandler(int s)
166 {
167 char buf[2048];
168
169 signal(s,SIG_IGN); /* Ignore future instances of this signal.*/
170
171 I_ExeptionProcess(); //e6y
172
173 strcpy(buf,"Exiting on signal: ");
174 I_SigString(buf+strlen(buf),2000-strlen(buf),s);
175
176 /* If corrupted memory could cause crash, dump memory
177 * allocation history, which points out probable causes
178 */
179 if (s==SIGSEGV || s==SIGILL || s==SIGFPE)
180 Z_DumpHistory(buf);
181
182 I_Error("I_SignalHandler: %s", buf);
183 }
184
185 //
186 // e6y: exeptions handling
187 //
188
189 static ExeptionsList_t current_exception_index;
190
191 ExeptionParam_t ExeptionsParams[EXEPTION_MAX + 1] =
192 {
193 {NULL},
194 {"gld_CreateScreenSizeFBO: Access violation in glFramebufferTexture2DEXT.\n\n"
195 "Are you using ATI graphics? Try to update your drivers "
196 "or change gl_compatibility variable in cfg to 1.\n"},
197 {NULL}
198 };
199
I_ExeptionBegin(ExeptionsList_t exception_index)200 void I_ExeptionBegin(ExeptionsList_t exception_index)
201 {
202 if (current_exception_index == EXEPTION_NONE)
203 {
204 current_exception_index = exception_index;
205 }
206 else
207 {
208 I_Error("I_SignalStateSet: signal_state set!");
209 }
210 }
211
I_ExeptionEnd(void)212 void I_ExeptionEnd(void)
213 {
214 current_exception_index = EXEPTION_NONE;
215 }
216
I_ExeptionProcess(void)217 void I_ExeptionProcess(void)
218 {
219 if (current_exception_index > EXEPTION_NONE && current_exception_index < EXEPTION_MAX)
220 {
221 I_Error("%s", ExeptionsParams[current_exception_index].error_message);
222 }
223 }
224
225
226 /* killough 2/22/98: Add support for ENDBOOM, which is PC-specific
227 *
228 * this converts BIOS color codes to ANSI codes.
229 * Its not pretty, but it does the job - rain
230 * CPhipps - made static
231 */
232
convert(int color,int * bold)233 inline static int convert(int color, int *bold)
234 {
235 if (color > 7) {
236 color -= 8;
237 *bold = 1;
238 }
239 switch (color) {
240 case 0:
241 return 0;
242 case 1:
243 return 4;
244 case 2:
245 return 2;
246 case 3:
247 return 6;
248 case 4:
249 return 1;
250 case 5:
251 return 5;
252 case 6:
253 return 3;
254 case 7:
255 return 7;
256 }
257 return 0;
258 }
259
260 /* CPhipps - flags controlling ENDOOM behaviour */
261 enum {
262 endoom_colours = 1,
263 endoom_nonasciichars = 2,
264 endoom_droplastline = 4
265 };
266
267 int endoom_mode;
268
PrintVer(void)269 static void PrintVer(void)
270 {
271 char vbuf[200];
272 lprintf(LO_INFO,"%s\n",I_GetVersionString(vbuf,200));
273 }
274
275 //
276 // ENDOOM support using text mode emulation
277 //
I_EndDoom(void)278 static void I_EndDoom(void)
279 {
280 int lump_eb, lump_ed, lump = -1;
281
282 const unsigned char *endoom_data;
283 unsigned char *screendata;
284
285 #ifndef _WIN32
286 PrintVer();
287 #endif
288
289 if (!showendoom || demorecording)
290 {
291 return;
292 }
293
294 /* CPhipps - ENDOOM/ENDBOOM selection */
295 lump_eb = W_CheckNumForName("ENDBOOM");/* jff 4/1/98 sign our work */
296 lump_ed = W_CheckNumForName("ENDOOM"); /* CPhipps - also maybe ENDOOM */
297
298 if (lump_eb == -1)
299 lump = lump_ed;
300 else if (lump_ed == -1)
301 lump = lump_eb;
302 else
303 { /* Both ENDOOM and ENDBOOM are present */
304 #define LUMP_IS_NEW(num) (!((lumpinfo[num].source == source_iwad) || (lumpinfo[num].source == source_auto_load)))
305 switch ((LUMP_IS_NEW(lump_ed) ? 1 : 0 ) |
306 (LUMP_IS_NEW(lump_eb) ? 2 : 0)) {
307 case 1:
308 lump = lump_ed;
309 break;
310 case 2:
311 lump = lump_eb;
312 break;
313 default:
314 /* Both lumps have equal priority, both present */
315 lump = (P_Random(pr_misc) & 1) ? lump_ed : lump_eb;
316 break;
317 }
318 }
319
320 if (lump != -1)
321 {
322 endoom_data = W_CacheLumpNum(lump);
323
324 // Set up text mode screen
325 TXT_Init();
326
327 // Make sure the new window has the right title and icon
328 I_SetWindowCaption();
329 I_SetWindowIcon();
330
331 // Write the data to the screen memory
332 screendata = TXT_GetScreenData();
333 memcpy(screendata, endoom_data, 4000);
334
335 // Wait for a keypress
336 while (true)
337 {
338 TXT_UpdateScreen();
339
340 if (TXT_GetChar() > 0)
341 {
342 break;
343 }
344
345 TXT_Sleep(0);
346 }
347
348 // Shut down text mode screen
349 TXT_Shutdown();
350 }
351 }
352
353 /* I_EndDoom
354 * Prints out ENDOOM or ENDBOOM, using some common sense to decide which.
355 * cphipps - moved to l_main.c, made static
356 */
357 #if 0
358 static void I_EndDoom2(void)
359 {
360 int lump_eb, lump_ed, lump = -1;
361
362 //e6y
363 if (!showendoom)
364 {
365 lprintf(LO_INFO,"I_EndDoom: All following output is skipped because of \"Fast Exit\" option\n");
366 return;
367 }
368
369 /* CPhipps - ENDOOM/ENDBOOM selection */
370 lump_eb = W_CheckNumForName("ENDBOOM");/* jff 4/1/98 sign our work */
371 lump_ed = W_CheckNumForName("ENDOOM"); /* CPhipps - also maybe ENDOOM */
372
373 if (lump_eb == -1)
374 lump = lump_ed;
375 else if (lump_ed == -1)
376 lump = lump_eb;
377 else
378 { /* Both ENDOOM and ENDBOOM are present */
379 #define LUMP_IS_NEW(num) (!((lumpinfo[num].source == source_iwad) || (lumpinfo[num].source == source_auto_load)))
380 switch ((LUMP_IS_NEW(lump_ed) ? 1 : 0 ) |
381 (LUMP_IS_NEW(lump_eb) ? 2 : 0)) {
382 case 1:
383 lump = lump_ed;
384 break;
385 case 2:
386 lump = lump_eb;
387 break;
388 default:
389 /* Both lumps have equal priority, both present */
390 lump = (P_Random(pr_misc) & 1) ? lump_ed : lump_eb;
391 break;
392 }
393 }
394
395 if (lump != -1)
396 {
397 int i, l = W_LumpLength(lump) / 2;
398 const char (*endoom)[2];
399 endoom = W_CacheLumpNum(lump);
400
401 /* cph - colour ENDOOM by rain */
402 #ifndef _WIN32
403 if (endoom_mode & endoom_nonasciichars)
404 /* switch to secondary charset, and set to cp437 (IBM charset) */
405 printf("\e)K\016");
406 #endif /* _WIN32 */
407
408 /* cph - optionally drop the last line, so everything fits on one screen */
409 if (endoom_mode & endoom_droplastline)
410 l -= 80;
411 lprintf(LO_INFO,"\n");
412 for (i=0; i<l; i++)
413 {
414 #ifdef _WIN32
415 I_ConTextAttr(endoom[i][1]);
416 #elif defined (DJGPP)
417 textattr(endoom[i][1]);
418 #else
419 if (endoom_mode & endoom_colours)
420 {
421 int oldbg = -1, oldcolor = -1, bold = 0, oldbold = -1, color = 0;
422
423 if (!(i % 80))
424 {
425 /* reset color but not bold when we start a new line */
426 oldbg = -1;
427 oldcolor = -1;
428 printf("\e[39m\e[49m\n");
429 }
430 /* foreground color */
431 bold = 0;
432 color = endoom[i][1] % 16;
433 if (color != oldcolor)
434 {
435 oldcolor = color;
436 color = convert(color, &bold);
437 if (oldbold != bold)
438 {
439 oldbold = bold;
440 printf("\e[%cm", bold + '0');
441 if (!bold) oldbg = -1;
442 }
443 /* we buffer everything or output is horrendously slow */
444 printf("\e[%dm", color + 30);
445 bold = 0;
446 }
447 /* background color */
448 color = endoom[i][1] / 16;
449 if (color != oldbg)
450 {
451 oldbg = color;
452 color = convert(color, &bold);
453 printf("\e[%dm", color + 40);
454 }
455 }
456 #endif
457 /* cph - portable ascii printout if requested */
458 if (isascii(endoom[i][0]) || (endoom_mode & endoom_nonasciichars))
459 lprintf(LO_INFO,"%c",endoom[i][0]);
460 else /* Probably a box character, so do #'s */
461 lprintf(LO_INFO,"#");
462 }
463 #ifndef _WIN32
464 lprintf(LO_INFO,"\b"); /* hack workaround for extra newline at bottom of screen */
465 lprintf(LO_INFO,"\r");
466 if (endoom_mode & endoom_nonasciichars)
467 printf("%c",'\017'); /* restore primary charset */
468 #endif /* _WIN32 */
469 W_UnlockLumpNum(lump);
470 }
471 #ifndef _WIN32
472 if (endoom_mode & endoom_colours)
473 puts("\e[0m"); /* cph - reset colours */
474 PrintVer();
475 #endif /* _WIN32 */
476 }
477 #endif
478
479 static int has_exited;
480
481 /* I_SafeExit
482 * This function is called instead of exit() by functions that might be called
483 * during the exit process (i.e. after exit() has already been called)
484 * Prevent infinitely recursive exits -- killough
485 */
486
I_SafeExit(int rc)487 void I_SafeExit(int rc)
488 {
489 if (!has_exited) /* If it hasn't exited yet, exit now -- killough */
490 {
491 has_exited=rc ? 2 : 1;
492 exit(rc);
493 }
494 }
495
I_Quit(void)496 static void I_Quit (void)
497 {
498 if (!has_exited)
499 has_exited=1; /* Prevent infinitely recursive exits -- killough */
500
501 if (has_exited == 1) {
502 if (!demorecording)
503 I_EndDoom();
504 if (demorecording)
505 G_CheckDemoStatus();
506 M_SaveDefaults ();
507 I_DemoExShutdown();
508 }
509 }
510
511 #ifdef SECURE_UID
512 uid_t stored_euid = -1;
513 #endif
514
515 //
516 // Ability to use only the allowed CPUs
517 //
518
I_SetAffinityMask(void)519 static void I_SetAffinityMask(void)
520 {
521 // Set the process affinity mask so that all threads
522 // run on the same processor. This is a workaround for a bug in
523 // SDL_mixer that causes occasional crashes.
524 if (process_affinity_mask)
525 {
526 const char *errbuf = NULL;
527 #ifdef _WIN32
528 HMODULE kernel32_dll;
529 SetAffinityFunc SetAffinity = NULL;
530 int ok = false;
531
532 // Find the kernel interface DLL.
533 kernel32_dll = LoadLibrary("kernel32.dll");
534
535 if (kernel32_dll)
536 {
537 // Find the SetProcessAffinityMask function.
538 SetAffinity = (SetAffinityFunc)GetProcAddress(kernel32_dll, "SetProcessAffinityMask");
539
540 // If the function was not found, we are on an old (Win9x) system
541 // that doesn't have this function. That's no problem, because
542 // those systems don't support SMP anyway.
543
544 if (SetAffinity)
545 {
546 ok = SetAffinity(GetCurrentProcess(), process_affinity_mask);
547 }
548 }
549
550 if (!ok)
551 {
552 errbuf = WINError();
553 }
554 #elif defined(HAVE_SCHED_SETAFFINITY)
555 // POSIX version:
556 int i;
557 {
558 cpu_set_t set;
559
560 CPU_ZERO(&set);
561
562 for(i = 0; i < 16; i++)
563 {
564 CPU_SET((process_affinity_mask>>i)&1, &set);
565 }
566
567 if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
568 {
569 errbuf = strerror(errno);
570 }
571 }
572 #else
573 return;
574 #endif
575
576 if (errbuf == NULL)
577 {
578 lprintf(LO_INFO, "I_SetAffinityMask: manual affinity mask is %d\n", process_affinity_mask);
579 }
580 else
581 {
582 lprintf(LO_ERROR, "I_SetAffinityMask: failed to set process affinity mask (%s)\n", errbuf);
583 }
584 }
585 }
586
587 //
588 // Sets the priority class for the prboom-plus process
589 //
590
I_SetProcessPriority(void)591 void I_SetProcessPriority(void)
592 {
593 if (process_priority)
594 {
595 const char *errbuf = NULL;
596
597 #ifdef _WIN32
598 {
599 DWORD dwPriorityClass = NORMAL_PRIORITY_CLASS;
600
601 if (process_priority == 1)
602 dwPriorityClass = HIGH_PRIORITY_CLASS;
603 else if (process_priority == 2)
604 dwPriorityClass = REALTIME_PRIORITY_CLASS;
605
606 if (SetPriorityClass(GetCurrentProcess(), dwPriorityClass) == 0)
607 {
608 errbuf = WINError();
609 }
610 }
611 #else
612 return;
613 #endif
614
615 if (errbuf == NULL)
616 {
617 lprintf(LO_INFO, "I_SetProcessPriority: priority for the process is %d\n", process_priority);
618 }
619 else
620 {
621 lprintf(LO_ERROR, "I_SetProcessPriority: failed to set priority for the process (%s)\n", errbuf);
622 }
623 }
624 }
625
626 //int main(int argc, const char * const * argv)
main(int argc,char ** argv)627 int main(int argc, char **argv)
628 {
629 #ifdef SECURE_UID
630 /* First thing, revoke setuid status (if any) */
631 stored_euid = geteuid();
632 if (getuid() != stored_euid)
633 if (seteuid(getuid()) < 0)
634 fprintf(stderr, "Failed to revoke setuid\n");
635 else
636 fprintf(stderr, "Revoked uid %d\n",stored_euid);
637 #endif
638
639 myargc = argc;
640 myargv = malloc(sizeof(myargv[0]) * myargc);
641 memcpy(myargv, argv, sizeof(myargv[0]) * myargc);
642
643 // e6y: Check for conflicts.
644 // Conflicting command-line parameters could cause the engine to be confused
645 // in some cases. Added checks to prevent this.
646 // Example: glboom.exe -record mydemo -playdemo demoname
647 ParamsMatchingCheck();
648
649 // e6y: was moved from D_DoomMainSetup
650 // init subsystems
651 //jff 9/3/98 use logical output routine
652 lprintf(LO_INFO,"M_LoadDefaults: Load system defaults.\n");
653 M_LoadDefaults(); // load before initing other systems
654
655 #ifdef _WIN32
656 if (!M_CheckParm("-nodraw")) {
657 /* initialize the console window */
658 //Init_ConsoleWin();
659 //atexit(Done_ConsoleWin);
660 }
661 #endif
662
663 /* Version info */
664 lprintf(LO_INFO,"\n");
665 PrintVer();
666
667 /* cph - Z_Close must be done after I_Quit, so we register it first. */
668 atexit(Z_Close);
669 /*
670 killough 1/98:
671
672 This fixes some problems with exit handling
673 during abnormal situations.
674
675 The old code called I_Quit() to end program,
676 while now I_Quit() is installed as an exit
677 handler and exit() is called to exit, either
678 normally or abnormally. Seg faults are caught
679 and the error handler is used, to prevent
680 being left in graphics mode or having very
681 loud SFX noise because the sound card is
682 left in an unstable state.
683 */
684
685 Z_Init(); /* 1/18/98 killough: start up memory stuff first */
686
687 atexit(I_Quit);
688 #ifndef PRBOOM_DEBUG
689 if (!M_CheckParm("-devparm"))
690 {
691 signal(SIGSEGV, I_SignalHandler);
692 }
693 signal(SIGTERM, I_SignalHandler);
694 signal(SIGFPE, I_SignalHandler);
695 signal(SIGILL, I_SignalHandler);
696 signal(SIGINT, I_SignalHandler); /* killough 3/6/98: allow CTRL-BRK during init */
697 signal(SIGABRT, I_SignalHandler);
698 #endif
699
700 // Ability to use only the allowed CPUs
701 I_SetAffinityMask();
702
703 // Priority class for the prboom-plus process
704 I_SetProcessPriority();
705
706 /* cphipps - call to video specific startup code */
707 I_PreInitGraphics();
708
709 D_DoomMain ();
710 return 0;
711 }
712