1 // WL_MAIN.C
2
3 #ifdef _WIN32
4 // #include <io.h>
5 #else
6 #include <unistd.h>
7 #endif
8
9 #include "wl_def.h"
10 #include "wl_menu.h"
11 #include "id_ca.h"
12 #include "id_sd.h"
13 #include "id_vl.h"
14 #include "id_vh.h"
15 #include "id_us.h"
16 #include "wl_atmos.h"
17 #include "m_classes.h"
18 #include "m_random.h"
19 #include "config.h"
20 #include "w_wad.h"
21 #include "language.h"
22 #include "textures/textures.h"
23 #include "c_cvars.h"
24 #include "thingdef/thingdef.h"
25 #include "v_font.h"
26 #include "v_palette.h"
27 #include "v_video.h"
28 #include "r_data/colormaps.h"
29 #include "wl_agent.h"
30 #include "doomerrors.h"
31 #include "lumpremap.h"
32 #include "scanner.h"
33 #include "g_shared/a_keys.h"
34 #include "g_mapinfo.h"
35 #include "wl_draw.h"
36 #include "wl_inter.h"
37 #include "wl_iwad.h"
38 #include "wl_play.h"
39 #include "wl_game.h"
40 #include "wl_loadsave.h"
41 #include "dobject.h"
42 #include "colormatcher.h"
43 #include "version.h"
44 #include "r_2d/r_main.h"
45 #include "filesys.h"
46 #include "g_conversation.h"
47 #include "g_intermission.h"
48
49 #include <clocale>
50
51 /*
52 =============================================================================
53
54 WOLFENSTEIN 3-D
55
56 An Id Software production
57
58 by John Carmack
59
60 =============================================================================
61 */
62
63 /*
64 =============================================================================
65
66 LOCAL CONSTANTS
67
68 =============================================================================
69 */
70
71
72 #define FOCALLENGTH (0x5700l) // in global coordinates
73
74 #define VIEWWIDTH 256 // size of view window
75 #define VIEWHEIGHT 144
76
77 /*
78 =============================================================================
79
80 GLOBAL VARIABLES
81
82 =============================================================================
83 */
84
85 //
86 // proejection variables
87 //
88 fixed focallength;
89 fixed focallengthy;
90 fixed r_depthvisibility;
91 unsigned screenofs;
92 int viewscreenx, viewscreeny;
93 int viewwidth;
94 int viewheight;
95 int statusbarx;
96 int statusbary1, statusbary2;
97 short centerx;
98 short centerxwide;
99 int shootdelta; // pixels away from centerx a target can be
100 fixed scale;
101 fixed pspritexscale;
102 fixed pspriteyscale;
103 fixed yaspect;
104 int32_t heightnumerator;
105
106
107 void Quit (const char *error,...);
108
109 bool startgame;
110 bool loadedgame;
111 int mousexadjustment;
112 int mouseyadjustment;
113 int panxadjustment;
114 int panyadjustment;
115
116 //
117 // Command line parameter variables
118 //
119 bool param_nowait = false;
120 int param_difficulty = 1; // default is "normal"
121 const char* param_tedlevel = NULL; // default is not to start a level
122 int param_joystickindex = 0;
123
124 int param_joystickhat = -1;
125 int param_samplerate = 44100;
126 int param_audiobuffer = 2048 / (44100 / param_samplerate);
127
128 //===========================================================================
129
130 /*
131 =====================
132 =
133 = NewGame
134 =
135 = Set up new game to start from the beginning
136 =
137 =====================
138 */
139
NewGame(int difficulty,const FString & map,bool displayBriefing,const ClassDef * playerClass)140 void NewGame (int difficulty, const FString &map, bool displayBriefing, const ClassDef *playerClass)
141 {
142 if(!playerClass)
143 playerClass = ClassDef::FindClass(gameinfo.PlayerClasses[0]);
144
145 memset (&gamestate,0,sizeof(gamestate));
146 gamestate.difficulty = &SkillInfo::GetSkill(difficulty);
147 strncpy(gamestate.mapname, map, 8);
148 gamestate.mapname[8] = 0;
149 gamestate.playerClass = playerClass;
150 levelInfo = &LevelInfo::Find(map);
151
152 if(displayBriefing)
153 EnterText(levelInfo->Cluster);
154
155 // Clear LevelRatios
156 LevelRatios.killratio = LevelRatios.secretsratio = LevelRatios.treasureratio =
157 LevelRatios.numLevels = LevelRatios.time = 0;
158
159 players[0].state = player_t::PST_ENTER;
160
161 Dialog::ClearConversations();
162
163 startgame = true;
164 }
165
166 //===========================================================================
167
168 /*
169 ==========================
170 =
171 = ShutdownId
172 =
173 = Shuts down all ID_?? managers
174 =
175 ==========================
176 */
177
ShutdownId(void)178 void ShutdownId (void)
179 {
180 US_Shutdown (); // This line is completely useless...
181 SD_Shutdown ();
182 IN_Shutdown ();
183 }
184
185
186 //===========================================================================
187
188 /*
189 ==================
190 =
191 = BuildTables
192 =
193 = Calculates:
194 =
195 = scale projection constant
196 = sintable/costable overlapping fractional tables
197 =
198 ==================
199 */
200
201 const float radtoint = (float)(FINEANGLES/2/PI);
202
BuildTables(void)203 void BuildTables (void)
204 {
205 //
206 // calculate fine tangents
207 //
208
209 int i;
210 for(i=0;i<FINEANGLES/8;i++)
211 {
212 double tang=tan((i+0.5)/radtoint);
213 finetangent[i + FINEANGLES/2] = finetangent[i]=(fixed)(tang*FRACUNIT);
214 finetangent[FINEANGLES/4-1-i]=(fixed)((1/tang)*FRACUNIT);
215 finetangent[FINEANGLES/4+i]=-finetangent[FINEANGLES/4-1-i];
216 finetangent[FINEANGLES/2-1-i]=-finetangent[i];
217 }
218 memcpy(finetangent + FINEANGLES/2, finetangent, sizeof(fixed)*ANG180);
219
220 //
221 // costable overlays sintable with a quarter phase shift
222 // ANGLES is assumed to be divisable by four
223 //
224
225 float angle = 0;
226 float anglestep = (float)(PI/2/ANG90);
227 for(i=0; i<FINEANGLES; i++)
228 {
229 finesine[i]=fixed(FRACUNIT*sin(angle));
230 angle+=anglestep;
231 }
232 memcpy(&finesine[FINEANGLES], finesine, FINEANGLES*sizeof(fixed)/4);
233
234 #if defined(USE_STARSKY) || defined(USE_RAIN) || defined(USE_SNOW)
235 Init3DPoints();
236 #endif
237 }
238
239 //===========================================================================
240
CalcVisibility(fixed vis)241 void CalcVisibility(fixed vis)
242 {
243 r_depthvisibility = FixedDiv(FixedMul((160*FRACUNIT),vis),focallengthy<<16);
244 }
245
246 /*
247 ====================
248 =
249 = CalcProjection
250 =
251 = Uses focallength
252 =
253 ====================
254 */
255
CalcProjection(int32_t focal)256 void CalcProjection (int32_t focal)
257 {
258 int i;
259 int intang;
260 float angle;
261 double tang;
262 int halfview;
263 double facedist;
264
265 const fixed projectionFOV = static_cast<fixed>((players[0].FOV / 90.0f)*AspectCorrection[r_ratio].viewGlobal);
266
267 // 0xFD17 is a magic number to convert the player's radius 0x5800 to FOCALLENGTH (0x5700)
268 focallength = FixedMul(focal, 0xFD17);
269 facedist = 2*FOCALLENGTH+0x100; // Used to be MINDIST (0x5800) which was 0x100 then the FOCALLENGTH (0x5700)
270 halfview = viewwidth/2; // half view in pixels
271 focallengthy = centerx*yaspect/finetangent[FINEANGLES/2+(ANGLE_45>>ANGLETOFINESHIFT)];
272
273 //
274 // calculate scale value for vertical height calculations
275 // and sprite x calculations
276 //
277 scale = (fixed) (viewwidth*facedist/projectionFOV);
278
279 //
280 // divide heightnumerator by a posts distance to get the posts height for
281 // the heightbuffer. The pixel height is height>>2
282 //
283 heightnumerator = FixedMul(((TILEGLOBAL*scale)>>6), yaspect);
284
285 //
286 // calculate the angle offset from view angle of each pixel's ray
287 //
288
289 for (i=0;i<halfview;i++)
290 {
291 // start 1/2 pixel over, so viewangle bisects two middle pixels
292 tang = (int32_t)i*projectionFOV/viewwidth/facedist;
293 angle = (float) atan(tang);
294 intang = (int) (angle*radtoint);
295 pixelangle[halfview-1-i] = intang;
296 pixelangle[halfview+i] = -intang;
297 }
298 }
299
300 //===========================================================================
301
302 Menu musicMenu(CTL_X, CTL_Y-6, 280, 32);
303 static TArray<FString> songList;
304
MENU_LISTENER(ChangeMusic)305 MENU_LISTENER(ChangeMusic)
306 {
307 StartCPMusic(songList[which]);
308 for(unsigned int i = 0;i < songList.Size();++i)
309 musicMenu[i]->setHighlighted(i == (unsigned)which);
310 musicMenu.draw();
311 return true;
312 }
313
DoJukebox(void)314 void DoJukebox(void)
315 {
316 IN_ClearKeysDown();
317 if (!AdLibPresent && !SoundBlasterPresent)
318 return;
319
320 VW_FadeOut ();
321
322 ClearMScreen ();
323 musicMenu.setHeadText(language["ROBSJUKEBOX"], true);
324 for(unsigned int i = 0;i < (unsigned)Wads.GetNumLumps();++i)
325 {
326 if(Wads.GetLumpNamespace(i) != ns_music)
327 continue;
328
329 FString langString;
330 langString.Format("MUS_%s", Wads.GetLumpFullName(i));
331 const char* trackName = language[langString];
332 if(trackName == langString.GetChars())
333 musicMenu.addItem(new MenuItem(Wads.GetLumpFullName(i), ChangeMusic));
334 else
335 musicMenu.addItem(new MenuItem(language[langString], ChangeMusic));
336 songList.Push(Wads.GetLumpFullName(i));
337
338 }
339 musicMenu.show();
340 return;
341 }
342
343 /*
344 ==========================
345 =
346 = InitGame
347 =
348 = Load a few things right away
349 =
350 ==========================
351 */
352
CollectGC()353 static void CollectGC()
354 {
355 GC::FullGC();
356 GC::DelSoftRootHead();
357 }
358
DrawStartupConsole()359 static bool DrawStartupConsole()
360 {
361 static const char* const tempString = " " GAMENAME " " DOTVERSIONSTR_NOREV "\n\n\nTo be replaced with console...\n\n The memory thing was just\n for show anyways.";
362
363 if(gameinfo.SignonLump.IsEmpty())
364 return false;
365
366 CA_CacheScreen(TexMan(gameinfo.SignonLump));
367
368 word width, height;
369 VW_MeasurePropString(ConFont, tempString, width, height);
370 px = 160-width/2;
371 py = 76+62-height/2;
372 VWB_DrawPropString(ConFont, tempString, CR_GRAY);
373
374 return true;
375 }
376
377 void I_ShutdownGraphics();
InitGame()378 static void InitGame()
379 {
380 // initialize SDL
381 #if SDL_VERSION_ATLEAST(2,0,0)
382 if(SDL_Init(0) < 0)
383 #else
384 if(SDL_Init(SDL_INIT_VIDEO) < 0)
385 #endif
386 {
387 printf("Unable to init SDL: %s\n", SDL_GetError());
388 exit(1);
389 }
390 atterm(SDL_Quit);
391
392 SDL_ShowCursor(SDL_DISABLE);
393
394 //
395 // Mapinfo
396 //
397
398 V_InitFontColors();
399 G_ParseMapInfo(true);
400
401 //
402 // Init texture manager
403 //
404
405 TexMan.Init();
406 printf("VL_ReadPalette: Setting up the Palette...\n");
407 VL_ReadPalette(gameinfo.GamePalette);
408 atterm(R_DeinitColormaps);
409 GenerateLookupTables();
410
411 //
412 // Fonts
413 //
414 V_InitFonts();
415 atterm(V_ClearFonts);
416
417 //
418 // load in and lock down some basic chunks
419 //
420
421 BuildTables (); // trig tables
422
423 // Setup a temporary window so if we have to terminate we don't do extra mode sets
424 VL_SetVGAPlaneMode (true);
425 DrawStartupConsole();
426
427 VW_UpdateScreen();
428
429 //
430 // Load Actors
431 //
432
433 ClassDef::LoadActors();
434 atterm(CollectGC);
435
436 // I_ShutdownGraphics needs to be run before the class definitions are unloaded.
437 atterm (I_ShutdownGraphics);
438
439 // Parse non-gameinfo sections in MAPINFO
440 G_ParseMapInfo(false);
441
442 //
443 // Fonts
444 //
445 VH_Startup ();
446 IN_Startup ();
447 SD_Startup ();
448 printf("US_Startup: Starting the User Manager.\n");
449 US_Startup ();
450
451 //
452 // Load Keys
453 //
454
455 P_InitKeyMessages();
456 atterm(P_DeinitKeyMessages);
457
458 //
459 // Finish with setting up through the config file.
460 //
461 FinalReadConfig();
462
463 //
464 // Load the status bar
465 //
466 CreateStatusBar();
467
468 //
469 // initialize the menusalcProjection
470 printf("CreateMenus: Preparing the menu system...\n");
471 CreateMenus();
472
473 //
474 // Load Noah's Ark quiz
475 //
476 Dialog::LoadGlobalModule("NOAHQUIZ");
477
478 //
479 // Finish signon screen
480 //
481 VL_SetVGAPlaneMode();
482 if(DrawStartupConsole())
483 {
484 VH_UpdateScreen();
485
486 if (!param_nowait)
487 IN_UserInput(70*4);
488 }
489 else // Delay for a moment to allow the user to enter the jukebox if desired
490 IN_UserInput(16);
491
492 //
493 // HOLDING DOWN 'M' KEY?
494 //
495 IN_ProcessEvents();
496
497 if (Keyboard[sc_M])
498 DoJukebox();
499
500 #ifdef NOTYET
501 vdisp = (byte *) (0xa0000+PAGE1START);
502 vbuf = (byte *) (0xa0000+PAGE2START);
503 #endif
504 }
505
506 //===========================================================================
507
508 /*
509 ==========================
510 =
511 = SetViewSize
512 =
513 ==========================
514 */
515
SetViewSize(unsigned int screenWidth,unsigned int screenHeight)516 static void SetViewSize (unsigned int screenWidth, unsigned int screenHeight)
517 {
518 statusbarx = 0;
519 if(AspectCorrection[r_ratio].isWide)
520 statusbarx = screenWidth*(48-AspectCorrection[r_ratio].multiplier)/(48*2);
521
522 if(StatusBar)
523 {
524 statusbary1 = StatusBar->GetHeight(true);
525 statusbary2 = 200 - StatusBar->GetHeight(false);
526 }
527 else
528 {
529 statusbary1 = 0;
530 statusbary2 = 200;
531 }
532
533 statusbary1 = statusbary1*screenHeight/200;
534 if(AspectCorrection[r_ratio].tallscreen)
535 statusbary2 = ((statusbary2 - 100)*screenHeight*3)/AspectCorrection[r_ratio].baseHeight + screenHeight/2
536 + (screenHeight - screenHeight*AspectCorrection[r_ratio].multiplier/48)/2;
537 else
538 statusbary2 = statusbary2*screenHeight/200;
539
540 unsigned int width;
541 unsigned int height;
542 if(viewsize == 21)
543 {
544 width = screenWidth;
545 height = screenHeight;
546 }
547 else if(viewsize == 20)
548 {
549 width = screenWidth;
550 height = statusbary2-statusbary1;
551 }
552 else
553 {
554 width = screenWidth - (20-viewsize)*16*screenWidth/320;
555 height = (statusbary2-statusbary1+1) - (20-viewsize)*8*screenHeight/200;
556 }
557
558 // Some code assumes these are even.
559 viewwidth = width&~1;
560 viewheight = height&~1;
561 centerx = viewwidth/2-1;
562 centerxwide = AspectCorrection[r_ratio].isWide ? CorrectWidthFactor(centerx) : centerx;
563 // This should allow shooting within 9 degrees, but it's not perfect.
564 shootdelta = ((viewwidth<<FRACBITS)/AspectCorrection[r_ratio].viewGlobal)/10;
565 if((unsigned) viewheight == screenHeight)
566 viewscreenx = viewscreeny = screenofs = 0;
567 else
568 {
569 viewscreenx = (screenWidth-viewwidth) / 2;
570 viewscreeny = (statusbary2+statusbary1-viewheight)/2;
571 screenofs = viewscreeny*SCREENPITCH+viewscreenx;
572 }
573
574 int virtheight = screenHeight;
575 int virtwidth = screenWidth;
576 if(AspectCorrection[r_ratio].isWide)
577 virtwidth = CorrectWidthFactor(virtwidth);
578 else
579 virtheight = CorrectWidthFactor(virtheight);
580 yaspect = FixedMul((320<<FRACBITS)/200,(virtheight<<FRACBITS)/virtwidth);
581
582 pspritexscale = (centerxwide<<FRACBITS)/160;
583 pspriteyscale = FixedMul(pspritexscale, yaspect);
584
585 //
586 // calculate trace angles and projection constants
587 //
588 if(players[0].mo)
589 CalcProjection(players[0].mo->radius);
590 else
591 CalcProjection (FOCALLENGTH);
592 }
593
NewViewSize(int width,unsigned int scrWidth,unsigned int scrHeight)594 void NewViewSize (int width, unsigned int scrWidth, unsigned int scrHeight)
595 {
596 if(width < 4 || width > 21)
597 return;
598
599 viewsize = width;
600 SetViewSize(scrWidth, scrHeight);
601 }
602
603
604
605 //===========================================================================
606
607 /*
608 ==========================
609 =
610 = Quit
611 =
612 ==========================
613 */
614
Quit(const char * errorStr,...)615 void Quit (const char *errorStr, ...)
616 {
617 #ifdef NOTYET
618 byte *screen;
619 #endif
620 char error[256];
621 if(errorStr != NULL)
622 {
623 va_list vlist;
624 va_start(vlist, errorStr);
625 vsprintf(error, errorStr, vlist);
626 va_end(vlist);
627 }
628 else error[0] = 0;
629
630 ShutdownId ();
631
632 if (error[0] == 0)
633 {
634 #ifdef NOTYET
635 #ifndef JAPAN
636 CA_CacheGrChunk (ORDERSCREEN);
637 screen = grsegs[ORDERSCREEN];
638 #endif
639 #endif
640
641 WriteConfig ();
642 }
643 #ifdef NOTYET
644 else
645 {
646 CA_CacheGrChunk (ERRORSCREEN);
647 screen = grsegs[ERRORSCREEN];
648 }
649 #endif
650
651 if (error[0] != 0)
652 {
653 #ifdef NOTYET
654 memcpy((byte *)0xb8000,screen+7,7*160);
655 SetTextCursor(9,3);
656 #endif
657 puts(error);
658 #ifdef NOTYET
659 SetTextCursor(0,7);
660 #endif
661 VW_WaitVBL(200);
662 exit(1);
663 }
664 else
665 if (error[0] == 0)
666 {
667 #ifdef NOTYET
668 #ifndef JAPAN
669 memcpy((byte *)0xb8000,screen+7,24*160); // 24 for SPEAR/UPLOAD compatibility
670 #endif
671 SetTextCursor(0,23);
672 #endif
673 }
674
675 exit(0);
676 }
677
I_Error(const char * format,...)678 void I_Error(const char* format, ...)
679 {
680 va_list vlist;
681 va_start(vlist, format);
682 FString error;
683 error.VFormat(format, vlist);
684 va_end(vlist);
685
686 throw CRecoverableError(error);
687 }
688
689 //==========================================================================
690
691 /*
692 ==================
693 =
694 = PG13
695 =
696 ==================
697 */
698
PG13(void)699 static void PG13 (void)
700 {
701 VW_FadeOut ();
702
703 if(gameinfo.AdvisoryPic.IsEmpty())
704 return;
705
706 BYTE color = ColorMatcher.Pick(RPART(gameinfo.AdvisoryColor), GPART(gameinfo.AdvisoryColor), BPART(gameinfo.AdvisoryColor));
707
708 VWB_Clear(color, 0, 0, screenWidth, screenHeight);
709 FTexture *tex = TexMan(gameinfo.AdvisoryPic);
710 VWB_DrawGraphic(tex, 304-tex->GetScaledWidth(), 174-tex->GetScaledHeight());
711 VW_UpdateScreen ();
712
713 VW_FadeIn ();
714 IN_UserInput (TICRATE * 7);
715
716 VW_FadeOut ();
717 }
718
719 //===========================================================================
720
721 ////////////////////////////////////////////////////////
722 //
723 // NON-SHAREWARE NOTICE
724 //
725 ////////////////////////////////////////////////////////
NonShareware(void)726 static void NonShareware (void)
727 {
728 if(strlen(language["REGNOTICE_TITLE"]) == 0)
729 return;
730
731 VW_FadeOut ();
732
733 ClearMScreen ();
734 DrawStripes (10);
735
736 PrintX = 110;
737 PrintY = 15;
738
739 pa = MENU_TOP;
740 US_Print (BigFont, language["REGNOTICE_TITLE"], gameinfo.FontColors[GameInfo::MENU_HIGHLIGHTSELECTION]);
741 pa = MENU_CENTER;
742
743 WindowX = PrintX = 40;
744 PrintY = 60;
745 US_Print (BigFont, language["REGNOTICE_MESSAGE"], gameinfo.FontColors[GameInfo::MENU_SELECTION]);
746
747 VW_UpdateScreen ();
748 VW_FadeIn ();
749 IN_Ack ();
750 }
751
752 //===========================================================================
753
754
755 /*
756 =====================
757 =
758 = DemoLoop
759 =
760 =====================
761 */
762
DemoLoop()763 static void DemoLoop()
764 {
765 //
766 // check for launch from ted
767 //
768 if (param_tedlevel)
769 {
770 param_nowait = true;
771 NewGame(param_difficulty,param_tedlevel,false);
772 }
773
774
775 //
776 // main game cycle
777 //
778
779 if (!param_nowait && (IWad::GetGame().Flags & IWad::REGISTERED))
780 NonShareware();
781
782 StartCPMusic(gameinfo.TitleMusic);
783
784 if (!param_nowait)
785 PG13 ();
786
787 IntermissionInfo *demoLoop = IntermissionInfo::Find("DemoLoop");
788 bool gotoMenu = false;
789 while (1)
790 {
791 while(!param_nowait && ShowIntermission(demoLoop, true))
792 {
793 }
794
795 if(!param_tedlevel)
796 {
797 gotoMenu = false;
798
799 if (Keyboard[sc_Tab])
800 RecordDemo ();
801 else
802 US_ControlPanel (0);
803 }
804
805 if (param_tedlevel || startgame || loadedgame)
806 {
807 param_tedlevel = NULL;
808 if(GameLoop ())
809 gotoMenu = true;
810
811 if(!param_nowait && !gotoMenu)
812 {
813 StartCPMusic(gameinfo.TitleMusic);
814 }
815 }
816 }
817 }
818
819
820 //===========================================================================
821
822 // CheckRatio -- From ZDoom
823 //
824 // Tries to guess the physical dimensions of the screen based on the
825 // screen's pixel dimensions.
CheckRatio(int width,int height,int * trueratio)826 int CheckRatio (int width, int height, int *trueratio)
827 {
828 int fakeratio = -1;
829 Aspect ratio;
830
831 if (vid_aspect != ASPECT_NONE)
832 {
833 // [SP] User wants to force aspect ratio; let them.
834 fakeratio = vid_aspect;
835 }
836 /*if (vid_nowidescreen)
837 {
838 if (!vid_tft)
839 {
840 fakeratio = 0;
841 }
842 else
843 {
844 fakeratio = (height * 5/4 == width) ? 4 : 0;
845 }
846 }*/
847 // If the size is approximately 16:9, consider it so.
848 if (abs (height * 16/9 - width) < 10)
849 {
850 ratio = ASPECT_16_9;
851 }
852 // Consider 17:10 as well.
853 else if (abs (height * 17/10 - width) < 10)
854 {
855 ratio = ASPECT_17_10;
856 }
857 // 16:10 has more variance in the pixel dimensions. Grr.
858 else if (abs (height * 16/10 - width) < 60)
859 {
860 // 320x200 and 640x400 are always 4:3, not 16:10
861 if ((width == 320 && height == 200) || (width == 640 && height == 400))
862 {
863 ratio = ASPECT_NONE;
864 }
865 else
866 {
867 ratio = ASPECT_16_10;
868 }
869 }
870 // Unless vid_tft is set, 1280x1024 is 4:3, not 5:4.
871 else if (height * 5/4 == width)// && vid_tft)
872 {
873 ratio = ASPECT_5_4;
874 }
875 // Assume anything else is 4:3.
876 else
877 {
878 ratio = ASPECT_4_3;
879 }
880
881 if (trueratio != NULL)
882 {
883 *trueratio = ratio;
884 }
885 return (fakeratio >= 0) ? fakeratio : ratio;
886 }
887
888 #define IFARG(str) if(!strcmp(arg, (str)))
889
CheckParameters(int argc,char * argv[],TArray<FString> & files)890 static const char* CheckParameters(int argc, char *argv[], TArray<FString> &files)
891 {
892 const char* extension = NULL;
893 bool hasError = false, showHelp = false;
894 bool sampleRateGiven = false, audioBufferGiven = false;
895 int defaultSampleRate = param_samplerate;
896
897 fullscreen = vid_fullscreen;
898
899 for(int i = 1; i < argc; i++)
900 {
901 char *arg = argv[i];
902 IFARG("--baby")
903 param_difficulty = 0;
904 else IFARG("--easy")
905 param_difficulty = 1;
906 else IFARG("--normal")
907 param_difficulty = 2;
908 else IFARG("--hard")
909 param_difficulty = 3;
910 else IFARG("--nowait")
911 param_nowait = true;
912 else IFARG("--tedlevel")
913 {
914 if(++i >= argc)
915 {
916 printf("The tedlevel option is missing the level argument!\n");
917 hasError = true;
918 }
919 else param_tedlevel = argv[i];
920 }
921 else IFARG("--fullscreen")
922 fullscreen = true;
923 else IFARG("--res")
924 {
925 if(i + 2 >= argc)
926 {
927 printf("The res option needs the width and/or the height argument!\n");
928 hasError = true;
929 }
930 else
931 {
932 screenWidth = atoi(argv[++i]);
933 screenHeight = atoi(argv[++i]);
934 if(screenWidth < 320)
935 printf("Screen width must be at least 320!\n"), hasError = true;
936 if(screenHeight < 200)
937 printf("Screen height must be at least 200!\n"), hasError = true;
938 }
939 }
940 else IFARG("--aspect")
941 {
942 const char* ratio = argv[++i];
943 if(strcmp(ratio, "4:3") == 0)
944 vid_aspect = ASPECT_4_3;
945 else if(strcmp(ratio, "16:10") == 0)
946 vid_aspect = ASPECT_16_10;
947 else if(strcmp(ratio, "17:10") == 0)
948 vid_aspect = ASPECT_17_10;
949 else if(strcmp(ratio, "16:9") == 0)
950 vid_aspect = ASPECT_16_9;
951 else if(strcmp(ratio, "5:4") == 0)
952 vid_aspect = ASPECT_5_4;
953 else
954 {
955 printf("Unknown aspect ratio %s!\n", ratio);
956 hasError = true;
957 }
958 }
959 else IFARG("--bits")
960 {
961 if(++i >= argc)
962 {
963 printf("The bits option is missing the color depth argument!\n");
964 hasError = true;
965 }
966 else
967 {
968 screenBits = atoi(argv[i]);
969 switch(screenBits)
970 {
971 case 8:
972 case 16:
973 case 24:
974 case 32:
975 break;
976
977 default:
978 printf("Screen color depth must be 8, 16, 24, or 32!\n");
979 hasError = true;
980 break;
981 }
982 }
983 }
984 else IFARG("--noadaptive")
985 noadaptive = true;
986 else IFARG("--nodblbuf")
987 usedoublebuffering = false;
988 else IFARG("--extravbls")
989 {
990 if(++i >= argc)
991 {
992 printf("The extravbls option is missing the vbls argument!\n");
993 hasError = true;
994 }
995 else
996 {
997 extravbls = atoi(argv[i]);
998 if((signed)extravbls < 0)
999 {
1000 printf("Extravbls must be positive!\n");
1001 hasError = true;
1002 }
1003 }
1004 }
1005 else IFARG("--joystick")
1006 {
1007 if(++i >= argc)
1008 {
1009 printf("The joystick option is missing the index argument!\n");
1010 hasError = true;
1011 }
1012 else param_joystickindex = atoi(argv[i]); // index is checked in InitGame
1013 }
1014 else IFARG("--joystickhat")
1015 {
1016 if(++i >= argc)
1017 {
1018 printf("The joystickhat option is missing the index argument!\n");
1019 hasError = true;
1020 }
1021 else param_joystickhat = atoi(argv[i]);
1022 }
1023 else IFARG("--samplerate")
1024 {
1025 if(++i >= argc)
1026 {
1027 printf("The samplerate option is missing the rate argument!\n");
1028 hasError = true;
1029 }
1030 else param_samplerate = atoi(argv[i]);
1031 sampleRateGiven = true;
1032 }
1033 else IFARG("--audiobuffer")
1034 {
1035 if(++i >= argc)
1036 {
1037 printf("The audiobuffer option is missing the size argument!\n");
1038 hasError = true;
1039 }
1040 else param_audiobuffer = atoi(argv[i]);
1041 audioBufferGiven = true;
1042 }
1043 else IFARG("--help")
1044 showHelp = true;
1045 else IFARG("--data")
1046 if(++i >= argc)
1047 {
1048 printf("Expected main data extension!\n");
1049 hasError = true;
1050 }
1051 else
1052 extension = argv[i];
1053 else IFARG("--file")
1054 {
1055 if(++i < argc)
1056 files.Push(argv[i]);
1057 }
1058 else IFARG("--config")
1059 {
1060 // The config code will handle this itself, so ignore it here.
1061 ++i;
1062 }
1063 else IFARG("--console") {} // Windows always create console parameter
1064 else IFARG("--savedir")
1065 {
1066 if(++i < argc)
1067 FileSys::SetDirectoryPath(FileSys::DIR_Saves, argv[i]);
1068 }
1069 else
1070 files.Push(argv[i]);
1071 }
1072 if(hasError || showHelp)
1073 {
1074 if(hasError) printf("\n");
1075 printf(
1076 GAMENAME " v" DOTVERSIONSTR "\n"
1077 "http://maniacsvault.net/ecwolf/\n"
1078 "Based on Wolf4SDL v1.7\n"
1079 "Ported by Chaos-Software (http://www.chaos-software.de.vu)\n"
1080 "Original Wolfenstein 3D by id Software\n\n"
1081 "Usage: " BINNAME " [options]\n"
1082 "Options:\n"
1083 " --help This help page\n"
1084 #ifdef _WIN32
1085 " --console Display a console window\n"
1086 #endif
1087 " --config <file> Use an explicit location for the config file\n"
1088 " --savedir <dir> Use an explicit location for save games\n"
1089 " --file <file> Loads an extra data file\n"
1090 " --tedlevel <level> Starts the game in the given level\n"
1091 " --baby Sets the difficulty to baby for tedlevel\n"
1092 " --easy Sets the difficulty to easy for tedlevel\n"
1093 " --normal Sets the difficulty to normal for tedlevel\n"
1094 " --hard Sets the difficulty to hard for tedlevel\n"
1095 " --nowait Skips intro screens\n"
1096 " --fullscreen Starts the game in fullscreen mode\n"
1097 " --res <width> <height> Sets the screen resolution\n"
1098 " --aspect <aspect> Sets the aspect ratio.\n"
1099 " --noadaptive Disables adaptive tics.\n"
1100 " --bits <b> Sets the screen color depth\n"
1101 " (use this when you have palette/fading problems\n"
1102 " allowed: 8, 16, 24, 32, default: \"best\" depth)\n"
1103 " --nodblbuf Don't use SDL's double buffering\n"
1104 " --extravbls <vbls> Sets a delay after each frame, which may help to\n"
1105 " reduce flickering (unit is currently 8 ms, default: 0)\n"
1106 " --joystick <index> Use the index-th joystick if available\n"
1107 " (-1 to disable joystick, default: 0)\n"
1108 " --joystickhat <index> Enables movement with the given coolie hat\n"
1109 " --samplerate <rate> Sets the sound sample rate (given in Hz, default: %i)\n"
1110 " --audiobuffer <size> Sets the size of the audio buffer (-> sound latency)\n"
1111 " (given in bytes, default: 2048 / (44100 / samplerate))\n"
1112 , defaultSampleRate
1113 );
1114 exit(1);
1115 }
1116
1117 r_ratio = static_cast<Aspect>(CheckRatio(screenWidth, screenHeight));
1118
1119 if(sampleRateGiven && !audioBufferGiven)
1120 param_audiobuffer = 2048 / (44100 / param_samplerate);
1121
1122 #ifdef __ANDROID__
1123 param_audiobuffer = (2048*2) / (44100 / param_samplerate);
1124 #endif
1125
1126 return extension;
1127 }
1128
1129 #ifndef _WIN32
1130 // I_MakeRNGSeed is from ZDoom
1131 #include <time.h>
1132
1133 // Return a random seed, preferably one with lots of entropy.
I_MakeRNGSeed()1134 unsigned int I_MakeRNGSeed()
1135 {
1136 unsigned int seed;
1137 int file;
1138
1139 // Try reading from /dev/urandom first, then /dev/random, then
1140 // if all else fails, use a crappy seed from time().
1141 seed = time(NULL);
1142 file = open("/dev/urandom", O_RDONLY);
1143 if (file < 0)
1144 {
1145 file = open("/dev/random", O_RDONLY);
1146 }
1147 if (file >= 0)
1148 {
1149 read(file, &seed, sizeof(seed));
1150 close(file);
1151 }
1152 return seed;
1153 }
1154 #else
1155 unsigned int I_MakeRNGSeed();
1156 #endif
1157
1158 /*
1159 ==========================
1160 =
1161 = main
1162 =
1163 ==========================
1164 */
1165
1166 void InitThinkerList();
ScannerMessageHandler(Scanner::MessageLevel level,const char * error,va_list list)1167 void ScannerMessageHandler(Scanner::MessageLevel level, const char *error, va_list list)
1168 {
1169 FString errorMessage;
1170 errorMessage.VFormat(error, list);
1171
1172 if(level == Scanner::ERROR)
1173 throw CRecoverableError(errorMessage);
1174 else
1175 printf("%s", errorMessage.GetChars());
1176 }
1177
1178 // Basically from ZDoom
1179 // We are definting an atterm function so that we can control the exit behavior.
1180 static const unsigned int MAX_TERMS = 32;
1181 static void (*TermFuncs[MAX_TERMS])(void);
1182 static unsigned int NumTerms;
atterm(void (* func)(void))1183 void atterm(void (*func)(void))
1184 {
1185 for(unsigned int i = 0;i < NumTerms;++i)
1186 {
1187 if(TermFuncs[i] == func)
1188 return;
1189 }
1190
1191 if(NumTerms < MAX_TERMS)
1192 TermFuncs[NumTerms++] = func;
1193 else
1194 fprintf(stderr, "Failed to register atterm function!\n");
1195 }
CallTerminateFunctions()1196 void CallTerminateFunctions()
1197 {
1198 while(NumTerms > 0)
1199 TermFuncs[--NumTerms]();
1200 }
1201
1202 #ifdef _WIN32
1203 void I_AcknowledgeError();
1204 #endif
1205
WL_Main(int argc,char * argv[])1206 int WL_Main (int argc, char *argv[])
1207 {
1208 // Stop the C library from screwing around with its functions according
1209 // to the system locale.
1210 setlocale(LC_ALL, "C");
1211
1212 FileSys::SetupPaths(argc, argv);
1213
1214 // Find the program directory.
1215 FString progdir(FileSys::GetDirectoryPath(FileSys::DIR_Program));
1216
1217 Scanner::SetMessageHandler(ScannerMessageHandler);
1218 atexit(CallTerminateFunctions);
1219
1220 try
1221 {
1222 printf("ReadConfig: Reading the Configuration.\n");
1223 config.LocateConfigFile(argc, argv);
1224 ReadConfig();
1225
1226 {
1227 TArray<FString> wadfiles, files;
1228
1229 Printf("IWad: Selecting base game data.\n");
1230 const char* extension = CheckParameters(argc, argv, wadfiles);
1231 IWad::SelectGame(files, extension, MAIN_PK3, progdir);
1232
1233 for(unsigned int i = 0;i < wadfiles.Size();++i)
1234 files.Push(wadfiles[i]);
1235
1236 printf("W_Init: Init WADfiles.\n");
1237 Wads.InitMultipleFiles(files);
1238 language.SetupStrings();
1239 LumpRemapper::RemapAll();
1240 }
1241
1242 InitThinkerList();
1243
1244 R_InitRenderer();
1245
1246 printf("InitGame: Setting up the game...\n");
1247 InitGame();
1248
1249 rngseed = I_MakeRNGSeed();
1250 FRandom::StaticClearRandom();
1251
1252 printf("DemoLoop: Starting the game loop...\n");
1253 DemoLoop();
1254
1255 Quit("Demo loop exited???");
1256 }
1257 catch(class CDoomError &error)
1258 {
1259 SDL_Quit();
1260
1261 #ifdef __ANDROID__
1262 if(error.GetMessage())
1263 Printf("%s\n", error.GetMessage());
1264 #else
1265 if(error.GetMessage())
1266 fprintf(stderr, "%s\n", error.GetMessage());
1267 #endif
1268
1269 #ifdef _WIN32
1270 I_AcknowledgeError();
1271 #endif
1272
1273 exit(-1);
1274 }
1275 return 1;
1276 }
1277
1278 // TODO: Move this to a system dependent file?
1279 #if defined(main) && !defined(__APPLE__)
1280 #undef main
1281 #endif
1282
1283 #ifndef NO_GTK
1284 #include <gtk/gtk.h>
1285 bool GtkAvailable;
1286 #endif
1287
1288 #ifndef _WIN32
1289 #ifdef __ANDROID__
main_android(int argc,char * argv[])1290 extern "C" int main_android(int argc, char *argv[])
1291 #else
1292 int main(int argc, char *argv[])
1293 #endif
1294 {
1295 // Set LC_NUMERIC environment variable in case some library decides to
1296 // clear the setlocale call at least this will be correct.
1297 // Note that the LANG environment variable is overridden by LC_*
1298 setenv("LC_NUMERIC", "C", 1);
1299
1300 #ifndef NO_GTK
1301 GtkAvailable = gtk_init_check(&argc, &argv);
1302 #endif
1303
1304 return WL_Main(argc, argv);
1305 }
1306 #endif
1307