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