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