mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1963 lines
		
	
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1963 lines
		
	
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SONIC ROBO BLAST 2
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// Copyright (C) 1993-1996 by id Software, Inc.
 | 
						|
// Copyright (C) 1998-2000 by DooM Legacy Team.
 | 
						|
// Copyright (C) 1999-2020 by Sonic Team Junior.
 | 
						|
//
 | 
						|
// This program is free software distributed under the
 | 
						|
// terms of the GNU General Public License, version 2.
 | 
						|
// See the 'LICENSE' file for more details.
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
/// \file  d_main.c
 | 
						|
/// \brief SRB2 main program
 | 
						|
///
 | 
						|
///        SRB2 main program (D_SRB2Main) and game loop (D_SRB2Loop),
 | 
						|
///        plus functions to parse command line parameters, configure game
 | 
						|
///        parameters, and call the startup functions.
 | 
						|
 | 
						|
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __GNUC__
 | 
						|
#include <unistd.h> // for getcwd
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef _WIN32
 | 
						|
#include <direct.h>
 | 
						|
#include <malloc.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <time.h>
 | 
						|
 | 
						|
#include "doomdef.h"
 | 
						|
#include "am_map.h"
 | 
						|
#include "console.h"
 | 
						|
#include "d_net.h"
 | 
						|
#include "f_finale.h"
 | 
						|
#include "g_game.h"
 | 
						|
#include "hu_stuff.h"
 | 
						|
#include "i_sound.h"
 | 
						|
#include "i_system.h"
 | 
						|
#include "i_time.h"
 | 
						|
#include "i_threads.h"
 | 
						|
#include "i_video.h"
 | 
						|
#include "m_argv.h"
 | 
						|
#include "k_menu.h"
 | 
						|
#include "m_misc.h"
 | 
						|
#include "p_setup.h"
 | 
						|
#include "p_saveg.h"
 | 
						|
#include "r_main.h"
 | 
						|
#include "r_local.h"
 | 
						|
#include "s_sound.h"
 | 
						|
#include "st_stuff.h"
 | 
						|
#include "v_video.h"
 | 
						|
#include "w_wad.h"
 | 
						|
#include "z_zone.h"
 | 
						|
#include "d_main.h"
 | 
						|
#include "d_netfil.h"
 | 
						|
#include "m_cheat.h"
 | 
						|
#include "y_inter.h"
 | 
						|
#include "p_local.h" // chasecam
 | 
						|
#include "m_misc.h" // screenshot functionality
 | 
						|
#include "deh_tables.h" // Dehacked list test
 | 
						|
#include "m_cond.h" // condition initialization
 | 
						|
#include "fastcmp.h"
 | 
						|
#include "r_fps.h" // Frame interpolation/uncapped
 | 
						|
#include "keys.h"
 | 
						|
#include "filesrch.h" // refreshdirmenu
 | 
						|
#include "g_input.h" // tutorial mode control scheming
 | 
						|
#include "m_perfstats.h"
 | 
						|
 | 
						|
// SRB2Kart
 | 
						|
#include "k_grandprix.h"
 | 
						|
#include "k_boss.h"
 | 
						|
#include "doomstat.h"
 | 
						|
 | 
						|
#ifdef CMAKECONFIG
 | 
						|
#include "config.h"
 | 
						|
#else
 | 
						|
#include "config.h.in"
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HWRENDER
 | 
						|
#include "hardware/hw_main.h" // 3D View Rendering
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef _WINDOWS
 | 
						|
#include "win32/win_main.h" // I_DoStartupMouse
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HW3SOUND
 | 
						|
#include "hardware/hw3sound.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include "lua_script.h"
 | 
						|
 | 
						|
// Version numbers for netplay :upside_down_face:
 | 
						|
int    VERSION;
 | 
						|
int SUBVERSION;
 | 
						|
 | 
						|
#ifdef HAVE_DISCORDRPC
 | 
						|
#include "discord.h"
 | 
						|
#endif
 | 
						|
 | 
						|
// platform independant focus loss
 | 
						|
UINT8 window_notinfocus = false;
 | 
						|
INT32 window_x;
 | 
						|
INT32 window_y;
 | 
						|
 | 
						|
//
 | 
						|
// DEMO LOOP
 | 
						|
//
 | 
						|
static char *startupiwads[MAX_WADFILES];
 | 
						|
static char *startuppwads[MAX_WADFILES];
 | 
						|
 | 
						|
boolean devparm = false; // started game with -devparm
 | 
						|
 | 
						|
boolean singletics = false; // timedemo
 | 
						|
boolean lastdraw = false;
 | 
						|
 | 
						|
postimg_t postimgtype[MAXSPLITSCREENPLAYERS];
 | 
						|
INT32 postimgparam[MAXSPLITSCREENPLAYERS];
 | 
						|
 | 
						|
// These variables are in effect
 | 
						|
// whether the respective sound system is disabled
 | 
						|
// or they're init'ed, but the player just toggled them
 | 
						|
 | 
						|
boolean sound_disabled = false;
 | 
						|
boolean digital_disabled = false;
 | 
						|
 | 
						|
boolean advancedemo;
 | 
						|
#ifdef DEBUGFILE
 | 
						|
INT32 debugload = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
UINT16 numskincolors;
 | 
						|
menucolor_t *menucolorhead, *menucolortail;
 | 
						|
 | 
						|
char savegamename[256];
 | 
						|
char liveeventbackup[256];
 | 
						|
 | 
						|
char srb2home[256] = ".";
 | 
						|
char srb2path[256] = ".";
 | 
						|
boolean usehome = true;
 | 
						|
const char *pandf = "%s" PATHSEP "%s";
 | 
						|
static char addonsdir[MAX_WADPATH];
 | 
						|
 | 
						|
//
 | 
						|
// EVENT HANDLING
 | 
						|
//
 | 
						|
// Events are asynchronous inputs generally generated by the game user.
 | 
						|
// Events can be discarded if no responder claims them
 | 
						|
// referenced from i_system.c for I_GetKey()
 | 
						|
 | 
						|
event_t events[MAXEVENTS];
 | 
						|
INT32 eventhead, eventtail;
 | 
						|
 | 
						|
boolean dedicated = false;
 | 
						|
 | 
						|
//
 | 
						|
// D_PostEvent
 | 
						|
// Called by the I/O functions when input is detected
 | 
						|
//
 | 
						|
void D_PostEvent(const event_t *ev)
 | 
						|
{
 | 
						|
	events[eventhead] = *ev;
 | 
						|
	eventhead = (eventhead+1) & (MAXEVENTS-1);
 | 
						|
}
 | 
						|
 | 
						|
// modifier keys
 | 
						|
// Now handled in I_OsPolling
 | 
						|
UINT8 shiftdown = 0; // 0x1 left, 0x2 right
 | 
						|
UINT8 ctrldown = 0; // 0x1 left, 0x2 right
 | 
						|
UINT8 altdown = 0; // 0x1 left, 0x2 right
 | 
						|
boolean capslock = 0;	// gee i wonder what this does.
 | 
						|
 | 
						|
//
 | 
						|
// D_ProcessEvents
 | 
						|
// Send all the events of the given timestamp down the responder chain
 | 
						|
//
 | 
						|
void D_ProcessEvents(void)
 | 
						|
{
 | 
						|
	event_t *ev;
 | 
						|
 | 
						|
	boolean eaten;
 | 
						|
	boolean menuresponse = false;
 | 
						|
 | 
						|
	memset(deviceResponding, false, sizeof (deviceResponding));
 | 
						|
	for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
 | 
						|
	{
 | 
						|
		ev = &events[eventtail];
 | 
						|
 | 
						|
		// Screenshots over everything so that they can be taken anywhere.
 | 
						|
		if (M_ScreenshotResponder(ev))
 | 
						|
			continue; // ate the event
 | 
						|
 | 
						|
		if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN)
 | 
						|
		{
 | 
						|
			if (cht_Responder(ev))
 | 
						|
				continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (demo.savemode == DSM_TITLEENTRY)
 | 
						|
		{
 | 
						|
			if (G_DemoTitleResponder(ev))
 | 
						|
				continue;
 | 
						|
		}
 | 
						|
 | 
						|
		// console input
 | 
						|
#ifdef HAVE_THREADS
 | 
						|
		I_lock_mutex(&con_mutex);
 | 
						|
#endif
 | 
						|
		{
 | 
						|
			eaten = CON_Responder(ev);
 | 
						|
		}
 | 
						|
#ifdef HAVE_THREADS
 | 
						|
		I_unlock_mutex(con_mutex);
 | 
						|
#endif
 | 
						|
 | 
						|
		if (eaten)
 | 
						|
		{
 | 
						|
			hu_keystrokes = true;
 | 
						|
			continue; // ate the event
 | 
						|
		}
 | 
						|
 | 
						|
		// Menu input
 | 
						|
		menuresponse = true;
 | 
						|
#ifdef HAVE_THREADS
 | 
						|
		I_lock_mutex(&k_menu_mutex);
 | 
						|
#endif
 | 
						|
		{
 | 
						|
			eaten = M_Responder(ev);
 | 
						|
		}
 | 
						|
#ifdef HAVE_THREADS
 | 
						|
		I_unlock_mutex(k_menu_mutex);
 | 
						|
#endif
 | 
						|
 | 
						|
		if (eaten)
 | 
						|
			continue; // menu ate the event
 | 
						|
 | 
						|
		// Demo input:
 | 
						|
		/*
 | 
						|
		if (demo.playback)
 | 
						|
			if (M_DemoResponder(ev))
 | 
						|
				continue;	// demo ate the event
 | 
						|
		*/
 | 
						|
 | 
						|
 | 
						|
		G_Responder(ev);
 | 
						|
	}
 | 
						|
 | 
						|
	// Reset menu controls when no event is processed
 | 
						|
	if (!menuresponse)
 | 
						|
	{
 | 
						|
		M_MapMenuControls(NULL);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// D_Display
 | 
						|
// draw current display, possibly wiping it from the previous
 | 
						|
//
 | 
						|
 | 
						|
// wipegamestate can be set to -1 to force a wipe on the next draw
 | 
						|
// added comment : there is a wipe eatch change of the gamestate
 | 
						|
gamestate_t wipegamestate = GS_LEVEL;
 | 
						|
// -1: Default; 0-n: Wipe index; INT16_MAX: do not wipe
 | 
						|
INT16 wipetypepre = -1;
 | 
						|
INT16 wipetypepost = -1;
 | 
						|
 | 
						|
static void D_Display(void)
 | 
						|
{
 | 
						|
	boolean forcerefresh = false;
 | 
						|
	static boolean wipe = false;
 | 
						|
	INT32 wipedefindex = 0;
 | 
						|
	UINT8 i;
 | 
						|
 | 
						|
	if (!dedicated)
 | 
						|
	{
 | 
						|
		if (nodrawers)
 | 
						|
			return; // for comparative timing/profiling
 | 
						|
 | 
						|
		// Lactozilla: Switching renderers works by checking
 | 
						|
		// if the game has to do it right when the frame
 | 
						|
		// needs to render. If so, five things will happen:
 | 
						|
		// 1. Interface functions will be called so
 | 
						|
		//    that switching to OpenGL creates a
 | 
						|
		//    GL context, and switching to Software
 | 
						|
		//    allocates screen buffers.
 | 
						|
		// 2. Software will set drawer functions,
 | 
						|
		//    and OpenGL will load textures and
 | 
						|
		//    create plane polygons, if necessary.
 | 
						|
		// 3. Functions related to switching video
 | 
						|
		//    modes (resolution) are called.
 | 
						|
		// 4. The frame is ready to be drawn!
 | 
						|
 | 
						|
		// Check for change of renderer or screen size (video mode)
 | 
						|
		if ((setrenderneeded || setmodeneeded) && !wipe)
 | 
						|
			SCR_SetMode(); // change video mode
 | 
						|
 | 
						|
		// Recalc the screen
 | 
						|
		if (vid.recalc)
 | 
						|
			SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc()
 | 
						|
 | 
						|
		if (rendermode == render_soft)
 | 
						|
		{
 | 
						|
			for (i = 0; i <= r_splitscreen; ++i)
 | 
						|
			{
 | 
						|
				R_SetViewContext(VIEWCONTEXT_PLAYER1 + i);
 | 
						|
				R_InterpolateViewRollAngle(rendertimefrac);
 | 
						|
				R_CheckViewMorph(i);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Change the view size if needed
 | 
						|
		// Set by changing video mode or renderer
 | 
						|
		if (setsizeneeded)
 | 
						|
		{
 | 
						|
			R_ExecuteSetViewSize();
 | 
						|
			forcerefresh = true; // force background redraw
 | 
						|
		}
 | 
						|
 | 
						|
		// draw buffered stuff to screen
 | 
						|
		// Used only by linux GGI version
 | 
						|
		I_UpdateNoBlit();
 | 
						|
	}
 | 
						|
 | 
						|
	// save the current screen if about to wipe
 | 
						|
	wipe = (gamestate != wipegamestate);
 | 
						|
	if (wipe && wipetypepre != INT16_MAX)
 | 
						|
	{
 | 
						|
		// set for all later
 | 
						|
		wipedefindex = gamestate; // wipe_xxx_toblack
 | 
						|
		if (gamestate == GS_TITLESCREEN && wipegamestate != GS_INTRO)
 | 
						|
			wipedefindex = wipe_titlescreen_toblack;
 | 
						|
 | 
						|
		if (wipetypepre < 0 || !F_WipeExists(wipetypepre))
 | 
						|
			wipetypepre = wipedefs[wipedefindex];
 | 
						|
 | 
						|
		if (rendermode != render_none)
 | 
						|
		{
 | 
						|
			// Fade to black first
 | 
						|
			if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always
 | 
						|
			 && wipetypepre != UINT8_MAX)
 | 
						|
			{
 | 
						|
				F_WipeStartScreen();
 | 
						|
				F_WipeColorFill(31);
 | 
						|
				F_WipeEndScreen();
 | 
						|
				F_RunWipe(wipetypepre, gamestate != GS_MENU, "FADEMAP0", false, false);
 | 
						|
			}
 | 
						|
 | 
						|
			if (gamestate != GS_LEVEL && rendermode != render_none)
 | 
						|
			{
 | 
						|
				V_SetPaletteLump("PLAYPAL"); // Reset the palette
 | 
						|
				R_ReInitColormaps(0, LUMPERROR);
 | 
						|
			}
 | 
						|
 | 
						|
			F_WipeStartScreen();
 | 
						|
		}
 | 
						|
		else //dedicated servers
 | 
						|
		{
 | 
						|
			F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU, "FADEMAP0", false, false);
 | 
						|
			wipegamestate = gamestate;
 | 
						|
		}
 | 
						|
 | 
						|
		wipetypepre = -1;
 | 
						|
	}
 | 
						|
	else
 | 
						|
		wipetypepre = -1;
 | 
						|
 | 
						|
	if (dedicated) //bail out after wipe logic
 | 
						|
		return;
 | 
						|
 | 
						|
	// do buffered drawing
 | 
						|
	switch (gamestate)
 | 
						|
	{
 | 
						|
		case GS_TITLESCREEN:
 | 
						|
			if (!titlemapinaction || !curbghide) {
 | 
						|
				F_TitleScreenDrawer();
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			/* FALLTHRU */
 | 
						|
		case GS_LEVEL:
 | 
						|
			if (!gametic)
 | 
						|
				break;
 | 
						|
			HU_Erase();
 | 
						|
			AM_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_INTERMISSION:
 | 
						|
			Y_IntermissionDrawer();
 | 
						|
			HU_Erase();
 | 
						|
			HU_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_VOTING:
 | 
						|
			Y_VoteDrawer();
 | 
						|
			HU_Erase();
 | 
						|
			HU_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_MENU:
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_INTRO:
 | 
						|
			F_IntroDrawer();
 | 
						|
			if (wipegamestate == (gamestate_t)-1)
 | 
						|
			{
 | 
						|
				wipe = true;
 | 
						|
				wipedefindex = gamestate; // wipe_xxx_toblack
 | 
						|
			}
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_ENDING:
 | 
						|
			F_EndingDrawer();
 | 
						|
			HU_Erase();
 | 
						|
			HU_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_CUTSCENE:
 | 
						|
			F_CutsceneDrawer();
 | 
						|
			HU_Erase();
 | 
						|
			HU_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_GAMEEND:
 | 
						|
			F_GameEndDrawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_EVALUATION:
 | 
						|
			F_GameEvaluationDrawer();
 | 
						|
			HU_Erase();
 | 
						|
			HU_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_CONTINUING:
 | 
						|
			//F_ContinueDrawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_CREDITS:
 | 
						|
			F_CreditDrawer();
 | 
						|
			HU_Erase();
 | 
						|
			HU_Drawer();
 | 
						|
			break;
 | 
						|
 | 
						|
		case GS_WAITINGPLAYERS:
 | 
						|
			// The clientconnect drawer is independent...
 | 
						|
			if (netgame)
 | 
						|
			{
 | 
						|
				// I don't think HOM from nothing drawing is independent...
 | 
						|
				F_WaitingPlayersDrawer();
 | 
						|
				HU_Erase();
 | 
						|
				HU_Drawer();
 | 
						|
			}
 | 
						|
		case GS_DEDICATEDSERVER:
 | 
						|
		case GS_NULL:
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	// STUPID race condition...
 | 
						|
	{
 | 
						|
		wipegamestate = gamestate;
 | 
						|
 | 
						|
		// clean up border stuff
 | 
						|
		// see if the border needs to be initially drawn
 | 
						|
		if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap)))
 | 
						|
		{
 | 
						|
			if (!automapactive && !dedicated && cv_renderview.value)
 | 
						|
			{
 | 
						|
				R_ApplyLevelInterpolators(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);
 | 
						|
 | 
						|
				viewwindowy = 0;
 | 
						|
				viewwindowx = 0;
 | 
						|
 | 
						|
				topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
 | 
						|
				objectsdrawn = 0;
 | 
						|
 | 
						|
				ps_rendercalltime = I_GetPreciseTime();
 | 
						|
 | 
						|
				for (i = 0; i <= r_splitscreen; i++)
 | 
						|
				{
 | 
						|
					if (players[displayplayers[i]].mo || players[displayplayers[i]].playerstate == PST_DEAD)
 | 
						|
					{
 | 
						|
						viewssnum = i;
 | 
						|
 | 
						|
#ifdef HWRENDER
 | 
						|
						if (rendermode != render_soft)
 | 
						|
							HWR_RenderPlayerView();
 | 
						|
						else
 | 
						|
#endif
 | 
						|
						if (rendermode != render_none)
 | 
						|
						{
 | 
						|
							if (i > 0) // Splitscreen-specific
 | 
						|
							{
 | 
						|
								switch (i)
 | 
						|
								{
 | 
						|
									case 1:
 | 
						|
										if (r_splitscreen > 1)
 | 
						|
										{
 | 
						|
											viewwindowx = viewwidth;
 | 
						|
											viewwindowy = 0;
 | 
						|
										}
 | 
						|
										else
 | 
						|
										{
 | 
						|
											viewwindowx = 0;
 | 
						|
											viewwindowy = viewheight;
 | 
						|
										}
 | 
						|
										M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
 | 
						|
										break;
 | 
						|
									case 2:
 | 
						|
										viewwindowx = 0;
 | 
						|
										viewwindowy = viewheight;
 | 
						|
										M_Memcpy(ylookup, ylookup3, viewheight*sizeof (ylookup[0]));
 | 
						|
										break;
 | 
						|
									case 3:
 | 
						|
										viewwindowx = viewwidth;
 | 
						|
										viewwindowy = viewheight;
 | 
						|
										M_Memcpy(ylookup, ylookup4, viewheight*sizeof (ylookup[0]));
 | 
						|
									default:
 | 
						|
										break;
 | 
						|
								}
 | 
						|
 | 
						|
 | 
						|
								topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
 | 
						|
							}
 | 
						|
 | 
						|
							R_RenderPlayerView();
 | 
						|
 | 
						|
							if (i > 0)
 | 
						|
								M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (rendermode == render_soft)
 | 
						|
				{
 | 
						|
					for (i = 0; i <= r_splitscreen; i++)
 | 
						|
					{
 | 
						|
						R_ApplyViewMorph(i);
 | 
						|
 | 
						|
						if (postimgtype[i])
 | 
						|
							V_DoPostProcessor(i, postimgtype[i], postimgparam[i]);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				ps_rendercalltime = I_GetPreciseTime() - ps_rendercalltime;
 | 
						|
				R_RestoreLevelInterpolators();
 | 
						|
			}
 | 
						|
 | 
						|
			if (lastdraw)
 | 
						|
			{
 | 
						|
				if (rendermode == render_soft)
 | 
						|
				{
 | 
						|
					VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
 | 
						|
				}
 | 
						|
 | 
						|
				lastdraw = false;
 | 
						|
			}
 | 
						|
 | 
						|
			ps_uitime = I_GetPreciseTime();
 | 
						|
 | 
						|
			if (gamestate == GS_LEVEL)
 | 
						|
			{
 | 
						|
				ST_Drawer();
 | 
						|
				F_TextPromptDrawer();
 | 
						|
				HU_Drawer();
 | 
						|
			}
 | 
						|
			else
 | 
						|
				F_TitleScreenDrawer();
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			ps_uitime = I_GetPreciseTime();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// change gamma if needed
 | 
						|
	// (GS_LEVEL handles this already due to level-specific palettes)
 | 
						|
	if (forcerefresh && !(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)))
 | 
						|
		V_SetPalette(0);
 | 
						|
 | 
						|
	// draw pause pic
 | 
						|
	if (paused && cv_showhud.value && !demo.playback)
 | 
						|
	{
 | 
						|
#if 0
 | 
						|
		INT32 py;
 | 
						|
		patch_t *patch;
 | 
						|
		if (automapactive)
 | 
						|
			py = 4;
 | 
						|
		else
 | 
						|
			py = viewwindowy + 4;
 | 
						|
		patch = W_CachePatchName("M_PAUSE", PU_PATCH);
 | 
						|
		V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - patch->width)/2, py, 0, patch);
 | 
						|
#else
 | 
						|
		INT32 y = ((automapactive) ? (32) : (BASEVIDHEIGHT/2));
 | 
						|
		M_DrawTextBox((BASEVIDWIDTH/2) - (60), y - (16), 13, 2);
 | 
						|
		V_DrawCenteredString(BASEVIDWIDTH/2, y - (4), V_YELLOWMAP, "Game Paused");
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	if (demo.rewinding)
 | 
						|
		V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSET);
 | 
						|
 | 
						|
	// vid size change is now finished if it was on...
 | 
						|
	vid.recalc = 0;
 | 
						|
 | 
						|
#ifdef HAVE_THREADS
 | 
						|
	I_lock_mutex(&k_menu_mutex);
 | 
						|
#endif
 | 
						|
	M_Drawer(); // menu is drawn even on top of everything
 | 
						|
#ifdef HAVE_THREADS
 | 
						|
	I_unlock_mutex(k_menu_mutex);
 | 
						|
#endif
 | 
						|
	// focus lost moved to M_Drawer
 | 
						|
 | 
						|
	CON_Drawer();
 | 
						|
 | 
						|
	ps_uitime = I_GetPreciseTime() - ps_uitime;
 | 
						|
 | 
						|
	//
 | 
						|
	// wipe update
 | 
						|
	//
 | 
						|
	if (wipe && wipetypepost != INT16_MAX)
 | 
						|
	{
 | 
						|
		// note: moved up here because NetUpdate does input changes
 | 
						|
		// and input during wipe tends to mess things up
 | 
						|
		wipedefindex += WIPEFINALSHIFT;
 | 
						|
 | 
						|
		if (wipetypepost < 0 || !F_WipeExists(wipetypepost))
 | 
						|
			wipetypepost = wipedefs[wipedefindex];
 | 
						|
 | 
						|
		if (rendermode != render_none)
 | 
						|
		{
 | 
						|
			F_WipeEndScreen();
 | 
						|
 | 
						|
			F_RunWipe(wipedefs[wipedefindex], gamestate != GS_MENU && gamestate != GS_TITLESCREEN, "FADEMAP0", true, false);
 | 
						|
		}
 | 
						|
 | 
						|
		// reset counters so timedemo doesn't count the wipe duration
 | 
						|
		if (demo.timing)
 | 
						|
		{
 | 
						|
			framecount = 0;
 | 
						|
			demostarttime = I_GetTime();
 | 
						|
		}
 | 
						|
 | 
						|
		wipetypepost = -1;
 | 
						|
	}
 | 
						|
	else
 | 
						|
		wipetypepost = -1;
 | 
						|
 | 
						|
	NetUpdate(); // send out any new accumulation
 | 
						|
 | 
						|
	// It's safe to end the game now.
 | 
						|
	if (G_GetExitGameFlag())
 | 
						|
	{
 | 
						|
		Command_ExitGame_f();
 | 
						|
		G_ClearExitGameFlag();
 | 
						|
	}
 | 
						|
 | 
						|
	//
 | 
						|
	// normal update
 | 
						|
	//
 | 
						|
	if (!wipe)
 | 
						|
	{
 | 
						|
		if (cv_shittyscreen.value)
 | 
						|
			V_DrawVhsEffect(cv_shittyscreen.value == 2);
 | 
						|
 | 
						|
		if (cv_netstat.value)
 | 
						|
		{
 | 
						|
			char s[50];
 | 
						|
			Net_GetNetStat();
 | 
						|
 | 
						|
			s[sizeof s - 1] = '\0';
 | 
						|
 | 
						|
			snprintf(s, sizeof s - 1, "get %d b/s", getbps);
 | 
						|
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-40, V_YELLOWMAP, s);
 | 
						|
			snprintf(s, sizeof s - 1, "send %d b/s", sendbps);
 | 
						|
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-30, V_YELLOWMAP, s);
 | 
						|
			snprintf(s, sizeof s - 1, "GameMiss %.2f%%", gamelostpercent);
 | 
						|
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-20, V_YELLOWMAP, s);
 | 
						|
			snprintf(s, sizeof s - 1, "SysMiss %.2f%%", lostpercent);
 | 
						|
			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
 | 
						|
		}
 | 
						|
 | 
						|
		if (cv_perfstats.value)
 | 
						|
		{
 | 
						|
			M_DrawPerfStats();
 | 
						|
		}
 | 
						|
 | 
						|
		ps_swaptime = I_GetPreciseTime();
 | 
						|
		I_FinishUpdate(); // page flip or blit buffer
 | 
						|
		ps_swaptime = I_GetPreciseTime() - ps_swaptime;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// =========================================================================
 | 
						|
// D_SRB2Loop
 | 
						|
// =========================================================================
 | 
						|
 | 
						|
tic_t rendergametic;
 | 
						|
 | 
						|
void D_SRB2Loop(void)
 | 
						|
{
 | 
						|
	tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS;
 | 
						|
	double deltatics = 0.0;
 | 
						|
	double deltasecs = 0.0;
 | 
						|
 | 
						|
	boolean interp = false;
 | 
						|
	boolean doDisplay = false;
 | 
						|
 | 
						|
	if (dedicated)
 | 
						|
		server = true;
 | 
						|
 | 
						|
	// Pushing of + parameters is now done back in D_SRB2Main, not here.
 | 
						|
 | 
						|
#ifdef _WINDOWS
 | 
						|
	CONS_Printf("I_StartupMouse()...\n");
 | 
						|
	I_DoStartupMouse();
 | 
						|
#endif
 | 
						|
 | 
						|
	I_UpdateTime(cv_timescale.value);
 | 
						|
	oldentertics = I_GetTime();
 | 
						|
 | 
						|
	// end of loading screen: CONS_Printf() will no more call FinishUpdate()
 | 
						|
	con_startup = false;
 | 
						|
 | 
						|
	// make sure to do a d_display to init mode _before_ load a level
 | 
						|
	SCR_SetMode(); // change video mode
 | 
						|
	SCR_Recalc();
 | 
						|
 | 
						|
	chosenrendermode = render_none;
 | 
						|
 | 
						|
	// Check and print which version is executed.
 | 
						|
	// Use this as the border between setup and the main game loop being entered.
 | 
						|
	CONS_Printf(
 | 
						|
	"===========================================================================\n"
 | 
						|
	"                   We hope you enjoy this game as\n"
 | 
						|
	"                     much as we did making it!\n"
 | 
						|
	"===========================================================================\n");
 | 
						|
 | 
						|
	// hack to start on a nice clear console screen.
 | 
						|
	COM_ImmedExecute("cls;version");
 | 
						|
 | 
						|
	I_FinishUpdate(); // page flip or blit buffer
 | 
						|
	/*
 | 
						|
	LMFAO this was showing garbage under OpenGL
 | 
						|
	because I_FinishUpdate was called afterward
 | 
						|
	*/
 | 
						|
 | 
						|
#if 0
 | 
						|
	/* Smells like a hack... Don't fade Sonic's ass into the title screen. */
 | 
						|
	if (gamestate != GS_TITLESCREEN)
 | 
						|
	{
 | 
						|
		static lumpnum_t gstartuplumpnum = W_CheckNumForName("STARTUP");
 | 
						|
		if (gstartuplumpnum == LUMPERROR)
 | 
						|
			gstartuplumpnum = W_GetNumForName("MISSING");
 | 
						|
		V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(gstartuplumpnum, PU_PATCH));
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	for (;;)
 | 
						|
	{
 | 
						|
		// capbudget is the minimum precise_t duration of a single loop iteration
 | 
						|
		precise_t capbudget;
 | 
						|
		precise_t enterprecise = I_GetPreciseTime();
 | 
						|
		precise_t finishprecise = enterprecise;
 | 
						|
 | 
						|
		{
 | 
						|
			// Casting the return value of a function is bad practice (apparently)
 | 
						|
			double budget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision());
 | 
						|
			capbudget = (precise_t) budget;
 | 
						|
		}
 | 
						|
 | 
						|
		I_UpdateTime(cv_timescale.value);
 | 
						|
 | 
						|
		if (lastwipetic)
 | 
						|
		{
 | 
						|
			oldentertics = lastwipetic;
 | 
						|
			lastwipetic = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		// get real tics
 | 
						|
		entertic = I_GetTime();
 | 
						|
		realtics = entertic - oldentertics;
 | 
						|
		oldentertics = entertic;
 | 
						|
 | 
						|
		refreshdirmenu = 0; // not sure where to put this, here as good as any?
 | 
						|
 | 
						|
		if (demo.playback && gamestate == GS_LEVEL)
 | 
						|
		{
 | 
						|
			// Nicer place to put this.
 | 
						|
			realtics = realtics * cv_playbackspeed.value;
 | 
						|
		}
 | 
						|
 | 
						|
#ifdef DEBUGFILE
 | 
						|
		if (!realtics)
 | 
						|
			if (debugload)
 | 
						|
				debugload--;
 | 
						|
#endif
 | 
						|
 | 
						|
		interp = R_UsingFrameInterpolation() && !dedicated;
 | 
						|
		doDisplay = false;
 | 
						|
 | 
						|
#ifdef HW3SOUND
 | 
						|
		HW3S_BeginFrameUpdate();
 | 
						|
#endif
 | 
						|
 | 
						|
		if (realtics > 0 || singletics)
 | 
						|
		{
 | 
						|
			// don't skip more than 10 frames at a time
 | 
						|
			// (fadein / fadeout cause massive frame skip!)
 | 
						|
			if (realtics > 8)
 | 
						|
				realtics = 1;
 | 
						|
 | 
						|
			// process tics (but maybe not if realtic == 0)
 | 
						|
			TryRunTics(realtics);
 | 
						|
 | 
						|
			if (lastdraw || singletics || gametic > rendergametic)
 | 
						|
			{
 | 
						|
				rendergametic = gametic;
 | 
						|
				rendertimeout = entertic + TICRATE/17;
 | 
						|
 | 
						|
				doDisplay = true;
 | 
						|
			}
 | 
						|
			else if (rendertimeout < entertic) // in case the server hang or netsplit
 | 
						|
			{
 | 
						|
				// Lagless camera! Yay!
 | 
						|
				if (gamestate == GS_LEVEL && netgame)
 | 
						|
				{
 | 
						|
					// Evaluate the chase cam once for every local realtic
 | 
						|
					// This might actually be better suited inside G_Ticker or TryRunTics
 | 
						|
					for (tic_t chasecamtics = 0; chasecamtics < realtics; chasecamtics++)
 | 
						|
					{
 | 
						|
						P_RunChaseCameras();
 | 
						|
					}
 | 
						|
					R_UpdateViewInterpolation();
 | 
						|
				}
 | 
						|
 | 
						|
				doDisplay = true;
 | 
						|
			}
 | 
						|
 | 
						|
			renderisnewtic = true;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			renderisnewtic = false;
 | 
						|
		}
 | 
						|
 | 
						|
		if (interp)
 | 
						|
		{
 | 
						|
			renderdeltatics = FLOAT_TO_FIXED(deltatics);
 | 
						|
 | 
						|
			if (!(paused || P_AutoPause()) && !hu_stopped)
 | 
						|
			{
 | 
						|
				rendertimefrac = g_time.timefrac;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				rendertimefrac = FRACUNIT;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			renderdeltatics = realtics * FRACUNIT;
 | 
						|
			rendertimefrac = FRACUNIT;
 | 
						|
		}
 | 
						|
 | 
						|
		if (interp || doDisplay)
 | 
						|
		{
 | 
						|
			D_Display();
 | 
						|
		}
 | 
						|
 | 
						|
		// Only take screenshots after drawing.
 | 
						|
		if (moviemode)
 | 
						|
			M_SaveFrame();
 | 
						|
		if (takescreenshot)
 | 
						|
			M_DoScreenShot();
 | 
						|
 | 
						|
		// consoleplayer -> displayplayers (hear sounds from viewpoint)
 | 
						|
		S_UpdateSounds(); // move positional sounds
 | 
						|
		S_UpdateClosedCaptions();
 | 
						|
 | 
						|
#ifdef HW3SOUND
 | 
						|
		HW3S_EndFrameUpdate();
 | 
						|
#endif
 | 
						|
 | 
						|
		LUA_Step();
 | 
						|
 | 
						|
#ifdef HAVE_DISCORDRPC
 | 
						|
		if (! dedicated)
 | 
						|
		{
 | 
						|
			Discord_RunCallbacks();
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
		// Fully completed frame made.
 | 
						|
		finishprecise = I_GetPreciseTime();
 | 
						|
		if (!singletics)
 | 
						|
		{
 | 
						|
			INT64 elapsed = (INT64)(finishprecise - enterprecise);
 | 
						|
 | 
						|
			// in the case of "match refresh rate" + vsync, don't sleep at all
 | 
						|
			const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;
 | 
						|
 | 
						|
			if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh)
 | 
						|
			{
 | 
						|
				I_SleepDuration(capbudget - (finishprecise - enterprecise));
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Capture the time once more to get the real delta time.
 | 
						|
		finishprecise = I_GetPreciseTime();
 | 
						|
		deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
 | 
						|
		deltatics = deltasecs * NEWTICRATE;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// D_AdvanceDemo
 | 
						|
// Called after each demo or intro demosequence finishes
 | 
						|
//
 | 
						|
void D_AdvanceDemo(void)
 | 
						|
{
 | 
						|
	advancedemo = true;
 | 
						|
}
 | 
						|
 | 
						|
// =========================================================================
 | 
						|
// D_SRB2Main
 | 
						|
// =========================================================================
 | 
						|
 | 
						|
//
 | 
						|
// D_StartTitle
 | 
						|
//
 | 
						|
void D_StartTitle(void)
 | 
						|
{
 | 
						|
	INT32 i;
 | 
						|
 | 
						|
	S_StopMusic();
 | 
						|
 | 
						|
	if (netgame)
 | 
						|
	{
 | 
						|
		if (gametyperules & GTR_CAMPAIGN)
 | 
						|
		{
 | 
						|
			G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat
 | 
						|
 | 
						|
			if (server)
 | 
						|
			{
 | 
						|
				char mapname[6];
 | 
						|
 | 
						|
				strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname));
 | 
						|
				strlwr(mapname);
 | 
						|
				mapname[5] = '\0';
 | 
						|
 | 
						|
				COM_BufAddText(va("map %s\n", mapname));
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	// okay, stop now
 | 
						|
	// (otherwise the game still thinks we're playing!)
 | 
						|
	SV_StopServer();
 | 
						|
	SV_ResetServer();
 | 
						|
 | 
						|
	for (i = 0; i < MAXPLAYERS; i++)
 | 
						|
		CL_ClearPlayer(i);
 | 
						|
 | 
						|
	for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
 | 
						|
	{
 | 
						|
		players[g_localplayers[i]].availabilities = R_GetSkinAvailabilities();
 | 
						|
	}
 | 
						|
 | 
						|
	splitscreen = 0;
 | 
						|
	SplitScreen_OnChange();
 | 
						|
 | 
						|
	cv_debug = 0;
 | 
						|
	emeralds = 0;
 | 
						|
	memset(&luabanks, 0, sizeof(luabanks));
 | 
						|
	lastmaploaded = 0;
 | 
						|
 | 
						|
	// In case someone exits out at the same time they start a time attack run,
 | 
						|
	// reset modeattacking
 | 
						|
	modeattacking = ATTACKING_NONE;
 | 
						|
	marathonmode = 0;
 | 
						|
 | 
						|
	// Reset GP
 | 
						|
	memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
 | 
						|
 | 
						|
	// Reset boss info
 | 
						|
	K_ResetBossInfo();
 | 
						|
 | 
						|
	// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
 | 
						|
	maptol = 0;
 | 
						|
 | 
						|
	gameaction = ga_nothing;
 | 
						|
	memset(displayplayers, 0, sizeof(displayplayers));
 | 
						|
	memset(g_localplayers, 0, sizeof g_localplayers);
 | 
						|
	consoleplayer = 0;
 | 
						|
	//demosequence = -1;
 | 
						|
	G_SetGametype(GT_RACE); // SRB2kart
 | 
						|
	paused = false;
 | 
						|
	advancedemo = false;
 | 
						|
	F_StartTitleScreen();
 | 
						|
 | 
						|
	// Reset the palette
 | 
						|
	if (rendermode != render_none)
 | 
						|
		V_SetPaletteLump("PLAYPAL");
 | 
						|
 | 
						|
	// The title screen is obviously not a tutorial! (Unless I'm mistaken)
 | 
						|
	tutorialmode = false;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// D_AddFile
 | 
						|
//
 | 
						|
static void D_AddFile(char **list, const char *file)
 | 
						|
{
 | 
						|
	size_t pnumwadfiles;
 | 
						|
	char *newfile;
 | 
						|
 | 
						|
	for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++)
 | 
						|
		;
 | 
						|
 | 
						|
	newfile = malloc(strlen(file) + 1);
 | 
						|
	if (!newfile)
 | 
						|
	{
 | 
						|
		I_Error("No more free memory to AddFile %s",file);
 | 
						|
	}
 | 
						|
	strcpy(newfile, file);
 | 
						|
 | 
						|
	list[pnumwadfiles] = newfile;
 | 
						|
}
 | 
						|
 | 
						|
static inline void D_CleanFile(char **list)
 | 
						|
{
 | 
						|
	size_t pnumwadfiles;
 | 
						|
	for (pnumwadfiles = 0; list[pnumwadfiles]; pnumwadfiles++)
 | 
						|
	{
 | 
						|
		free(list[pnumwadfiles]);
 | 
						|
		list[pnumwadfiles] = NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
///\brief Checks if a netgame URL is being handled, and changes working directory to the EXE's if so.
 | 
						|
///       Done because browsers (at least, Firefox on Windows) launch the game from the browser's directory, which causes problems.
 | 
						|
static void ChangeDirForUrlHandler(void)
 | 
						|
{
 | 
						|
	// URL handlers are opened by web browsers (at least Firefox) from the browser's working directory, not the game's stored directory,
 | 
						|
	// so chdir to that directory unless overridden.
 | 
						|
	if (M_GetUrlProtocolArg() != NULL && !M_CheckParm("-nochdir"))
 | 
						|
	{
 | 
						|
		size_t i;
 | 
						|
 | 
						|
		CONS_Printf("%s connect links load game files from the SRB2 application's stored directory. Switching to ", SERVER_URL_PROTOCOL);
 | 
						|
		strlcpy(srb2path, myargv[0], sizeof(srb2path));
 | 
						|
 | 
						|
		// Get just the directory, minus the EXE name
 | 
						|
		for (i = strlen(srb2path)-1; i > 0; i--)
 | 
						|
		{
 | 
						|
			if (srb2path[i] == '/' || srb2path[i] == '\\')
 | 
						|
			{
 | 
						|
				srb2path[i] = '\0';
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		CONS_Printf("%s\n", srb2path);
 | 
						|
 | 
						|
#if defined (_WIN32)
 | 
						|
		SetCurrentDirectoryA(srb2path);
 | 
						|
#else
 | 
						|
		if (chdir(srb2path) == -1)
 | 
						|
			I_OutputMsg("Couldn't change working directory\n");
 | 
						|
#endif
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// ==========================================================================
 | 
						|
// Identify the SRB2 version, and IWAD file to use.
 | 
						|
// ==========================================================================
 | 
						|
 | 
						|
static boolean AddIWAD(void)
 | 
						|
{
 | 
						|
	char * path = va(pandf,srb2path,"main.kart");
 | 
						|
 | 
						|
	if (FIL_ReadFileOK(path))
 | 
						|
	{
 | 
						|
		D_AddFile(startupiwads, path);
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void IdentifyVersion(void)
 | 
						|
{
 | 
						|
	const char *srb2waddir = NULL;
 | 
						|
 | 
						|
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
 | 
						|
	// change to the directory where 'main.kart' is found
 | 
						|
	srb2waddir = I_LocateWad();
 | 
						|
#endif
 | 
						|
 | 
						|
	// get the current directory (possible problem on NT with "." as current dir)
 | 
						|
	if (srb2waddir)
 | 
						|
	{
 | 
						|
		strlcpy(srb2path,srb2waddir,sizeof (srb2path));
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		if (getcwd(srb2path, 256) != NULL)
 | 
						|
			srb2waddir = srb2path;
 | 
						|
		else
 | 
						|
			srb2waddir = ".";
 | 
						|
	}
 | 
						|
 | 
						|
	// Load the IWAD
 | 
						|
	if (! AddIWAD())
 | 
						|
	{
 | 
						|
		I_Error("MAIN.KART not found! Expected in %s\n", srb2waddir);
 | 
						|
	}
 | 
						|
 | 
						|
	// will be overwritten in case of -cdrom or unix/win home
 | 
						|
	snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir);
 | 
						|
	configfile[sizeof configfile - 1] = '\0';
 | 
						|
 | 
						|
	// if you change the ordering of this or add/remove a file, be sure to update the md5
 | 
						|
	// checking in D_SRB2Main
 | 
						|
 | 
						|
#if defined (TESTERS) || defined (HOSTTESTERS)
 | 
						|
////
 | 
						|
#define TEXTURESNAME "MISC_TEXTURES.pk3"
 | 
						|
#define MAPSNAME "MISC_MAPS.pk3"
 | 
						|
#define PATCHNAME "MISC_PATCH.pk3"
 | 
						|
#define MUSICNAME "MISC_MUSIC.PK3"
 | 
						|
////
 | 
						|
#else
 | 
						|
////
 | 
						|
#define TEXTURESNAME "textures.pk3"
 | 
						|
#define MAPSNAME "maps.pk3"
 | 
						|
#define PATCHNAME "patch.pk3"
 | 
						|
#define MUSICNAME "music.pk3"
 | 
						|
////
 | 
						|
#endif
 | 
						|
////
 | 
						|
#if !defined (TESTERS) && !defined (HOSTTESTERS)
 | 
						|
	D_AddFile(startupiwads, va(pandf,srb2waddir,"gfx.pk3"));
 | 
						|
#endif
 | 
						|
	D_AddFile(startupiwads, va(pandf,srb2waddir,TEXTURESNAME));
 | 
						|
	D_AddFile(startupiwads, va(pandf,srb2waddir,"chars.pk3"));
 | 
						|
	D_AddFile(startupiwads, va(pandf,srb2waddir,MAPSNAME));
 | 
						|
	D_AddFile(startupiwads, va(pandf,srb2waddir,"followers.pk3"));
 | 
						|
#ifdef USE_PATCH_FILE
 | 
						|
	D_AddFile(startupiwads, va(pandf,srb2waddir,PATCHNAME));
 | 
						|
#endif
 | 
						|
////
 | 
						|
#undef TEXTURESNAME
 | 
						|
#undef MAPSNAME
 | 
						|
#undef PATCHNAME
 | 
						|
 | 
						|
#if !defined (HAVE_SDL) || defined (HAVE_MIXER)
 | 
						|
 | 
						|
#define MUSICTEST(str) \
 | 
						|
	{\
 | 
						|
		const char *musicpath = va(pandf,srb2waddir,str);\
 | 
						|
		int ms = W_VerifyNMUSlumps(musicpath, false); \
 | 
						|
		if (ms == 1) \
 | 
						|
			D_AddFile(startupiwads, musicpath); \
 | 
						|
		else if (ms == 0) \
 | 
						|
			I_Error("File "str" has been modified with non-music/sound lumps"); \
 | 
						|
	}
 | 
						|
 | 
						|
	MUSICTEST("sounds.pk3")
 | 
						|
	MUSICTEST(MUSICNAME)
 | 
						|
 | 
						|
#undef MUSICNAME
 | 
						|
#undef MUSICTEST
 | 
						|
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
D_ConvertVersionNumbers (void)
 | 
						|
{
 | 
						|
	/* leave at defaults (0) under DEVELOP */
 | 
						|
#ifndef DEVELOP
 | 
						|
	sscanf(SRB2VERSION, "%d.%d", &VERSION, &SUBVERSION);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// D_SRB2Main
 | 
						|
//
 | 
						|
void D_SRB2Main(void)
 | 
						|
{
 | 
						|
	INT32 i;
 | 
						|
	UINT16 wadnum;
 | 
						|
	lumpinfo_t *lumpinfo;
 | 
						|
	char *name;
 | 
						|
 | 
						|
	INT32 p;
 | 
						|
 | 
						|
	INT32 pstartmap = 1;
 | 
						|
	boolean autostart = false;
 | 
						|
 | 
						|
	/* break the version string into version numbers, for netplay */
 | 
						|
	D_ConvertVersionNumbers();
 | 
						|
 | 
						|
	// Print GPL notice for our console users (Linux)
 | 
						|
	CONS_Printf(
 | 
						|
	"\n\nDr. Robotnik's Ring Racers\n"
 | 
						|
	"Copyright (C) 1998-2022 by Kart Krew & STJr\n\n"
 | 
						|
	"This program comes with ABSOLUTELY NO WARRANTY.\n\n"
 | 
						|
	"This is free software, and you are welcome to redistribute it\n"
 | 
						|
	"and/or modify it under the terms of the GNU General Public License\n"
 | 
						|
	"as published by the Free Software Foundation; either version 2 of\n"
 | 
						|
	"the License, or (at your option) any later version.\n"
 | 
						|
	"See the 'LICENSE.txt' file for details.\n\n"
 | 
						|
	"Sonic the Hedgehog and related characters are trademarks of SEGA.\n"
 | 
						|
	"We do not claim ownership of SEGA's intellectual property used\n"
 | 
						|
	"in this program.\n\n");
 | 
						|
 | 
						|
	// keep error messages until the final flush(stderr)
 | 
						|
#if !defined(NOTERMIOS)
 | 
						|
	if (setvbuf(stderr, NULL, _IOFBF, 1000))
 | 
						|
		I_OutputMsg("setvbuf didnt work\n");
 | 
						|
#endif
 | 
						|
 | 
						|
	// initialise locale code
 | 
						|
	M_StartupLocale();
 | 
						|
 | 
						|
	// get parameters from a response file (eg: srb2 @parms.txt)
 | 
						|
	M_FindResponseFile();
 | 
						|
 | 
						|
	// MAINCFG is now taken care of where "OBJCTCFG" is handled
 | 
						|
	G_LoadGameSettings();
 | 
						|
 | 
						|
	// Test Dehacked lists
 | 
						|
	DEH_TableCheck();
 | 
						|
 | 
						|
	// Netgame URL special case: change working dir to EXE folder.
 | 
						|
	ChangeDirForUrlHandler();
 | 
						|
 | 
						|
	// identify the main IWAD file to use
 | 
						|
	IdentifyVersion();
 | 
						|
 | 
						|
#if !defined(NOTERMIOS)
 | 
						|
	setbuf(stdout, NULL); // non-buffered output
 | 
						|
#endif
 | 
						|
 | 
						|
#if 0 //defined (_DEBUG)
 | 
						|
	devparm = M_CheckParm("-nodebug") == 0;
 | 
						|
#else
 | 
						|
	devparm = M_CheckParm("-debug") != 0;
 | 
						|
#endif
 | 
						|
 | 
						|
	// for dedicated server
 | 
						|
#if !defined (_WINDOWS) //already check in win_main.c
 | 
						|
	dedicated = M_CheckParm("-dedicated") != 0;
 | 
						|
#endif
 | 
						|
 | 
						|
	if (devparm)
 | 
						|
		CONS_Printf(M_GetText("Development mode ON.\n"));
 | 
						|
 | 
						|
	// default savegame
 | 
						|
	strcpy(savegamename, SAVEGAMENAME"%u.ssg");
 | 
						|
	strcpy(liveeventbackup, "live"SAVEGAMENAME".bkp"); // intentionally not ending with .ssg
 | 
						|
 | 
						|
	// Init the joined IP table for quick rejoining of past games.
 | 
						|
	M_InitJoinedIPArray();
 | 
						|
 | 
						|
	{
 | 
						|
		const char *userhome = D_Home(); //Alam: path to home
 | 
						|
 | 
						|
		if (!userhome)
 | 
						|
		{
 | 
						|
#if ((defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)) && !defined (__CYGWIN__)
 | 
						|
			I_Error("Please set $HOME to your home directory\n");
 | 
						|
#else
 | 
						|
			if (dedicated)
 | 
						|
				snprintf(configfile, sizeof configfile, "d"CONFIGFILENAME);
 | 
						|
			else
 | 
						|
				snprintf(configfile, sizeof configfile, CONFIGFILENAME);
 | 
						|
#endif
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			// use user specific config file
 | 
						|
#ifdef DEFAULTDIR
 | 
						|
			snprintf(srb2home, sizeof srb2home, "%s" PATHSEP DEFAULTDIR, userhome);
 | 
						|
			if (dedicated)
 | 
						|
				snprintf(configfile, sizeof configfile, "%s" PATHSEP "d"CONFIGFILENAME, srb2home);
 | 
						|
			else
 | 
						|
				snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2home);
 | 
						|
 | 
						|
			// can't use sprintf since there is %u in savegamename
 | 
						|
			strcatbf(savegamename, srb2home, PATHSEP);
 | 
						|
			strcatbf(liveeventbackup, srb2home, PATHSEP);
 | 
						|
 | 
						|
			snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home);
 | 
						|
#else // DEFAULTDIR
 | 
						|
			snprintf(srb2home, sizeof srb2home, "%s", userhome);
 | 
						|
			if (dedicated)
 | 
						|
				snprintf(configfile, sizeof configfile, "%s" PATHSEP "d"CONFIGFILENAME, userhome);
 | 
						|
			else
 | 
						|
				snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, userhome);
 | 
						|
 | 
						|
			// can't use sprintf since there is %u in savegamename
 | 
						|
			strcatbf(savegamename, userhome, PATHSEP);
 | 
						|
			strcatbf(liveeventbackup, userhome, PATHSEP);
 | 
						|
 | 
						|
			snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome);
 | 
						|
#endif // DEFAULTDIR
 | 
						|
		}
 | 
						|
 | 
						|
		configfile[sizeof configfile - 1] = '\0';
 | 
						|
	}
 | 
						|
 | 
						|
	// If config isn't writable, tons of behavior will be broken.
 | 
						|
	// Fail loudly before things get confusing!
 | 
						|
	{
 | 
						|
		FILE *tmpfile;
 | 
						|
		char testfile[MAX_WADPATH];
 | 
						|
 | 
						|
		snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home);
 | 
						|
		testfile[sizeof testfile - 1] = '\0';
 | 
						|
 | 
						|
		tmpfile = fopen(testfile, "w");
 | 
						|
		if (tmpfile == NULL)
 | 
						|
		{
 | 
						|
#if defined (_WIN32)
 | 
						|
			I_Error("Couldn't write game config.\nMake sure the game is installed somewhere it has write permissions.\n\n(Don't use the Downloads folder, Program Files, or your desktop!\nIf unsure, we recommend making a subfolder in your Documents folder.)");
 | 
						|
#else
 | 
						|
			I_Error("Couldn't write game config.\nMake sure you've installed the game somewhere it has write permissions.");
 | 
						|
#endif
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			fclose(tmpfile);
 | 
						|
			remove(testfile);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	M_LoadJoinedIPs();	// load joined ips
 | 
						|
 | 
						|
	// Create addons dir
 | 
						|
	snprintf(addonsdir, sizeof addonsdir, "%s%s%s", srb2home, PATHSEP, "addons");
 | 
						|
	I_mkdir(addonsdir, 0755);
 | 
						|
 | 
						|
	/* and downloads in a subdirectory */
 | 
						|
	snprintf(downloaddir, sizeof downloaddir, "%s%s%s",
 | 
						|
			addonsdir, PATHSEP, "downloads");
 | 
						|
 | 
						|
	// rand() needs seeded regardless of password
 | 
						|
	srand((unsigned int)time(NULL));
 | 
						|
	rand();
 | 
						|
	rand();
 | 
						|
	rand();
 | 
						|
 | 
						|
	if (M_CheckParm("-password") && M_IsNextParm())
 | 
						|
		D_SetPassword(M_GetNextParm());
 | 
						|
 | 
						|
	// player setup menu colors must be initialized before
 | 
						|
	// any wad file is added, as they may contain colors themselves
 | 
						|
	M_InitPlayerSetupColors();
 | 
						|
 | 
						|
	CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
 | 
						|
	Z_Init();
 | 
						|
	CON_SetLoadingProgress(LOADED_ZINIT);
 | 
						|
 | 
						|
	// Do this up here so that WADs loaded through the command line can use ExecCfg
 | 
						|
	COM_Init();
 | 
						|
 | 
						|
#ifndef TESTERS
 | 
						|
	// add any files specified on the command line with -file wadfile
 | 
						|
	// to the wad list
 | 
						|
	if (!((M_GetUrlProtocolArg() || M_CheckParm("-connect")) && !M_CheckParm("-server")))
 | 
						|
	{
 | 
						|
		if (M_CheckParm("-file"))
 | 
						|
		{
 | 
						|
			// the parms after p are wadfile/lump names,
 | 
						|
			// until end of parms or another - preceded parm
 | 
						|
			while (M_IsNextParm())
 | 
						|
			{
 | 
						|
				const char *s = M_GetNextParm();
 | 
						|
 | 
						|
				if (s) // Check for NULL?
 | 
						|
					D_AddFile(startuppwads, s);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	// get map from parms
 | 
						|
 | 
						|
	if (M_CheckParm("-server") || dedicated)
 | 
						|
		netgame = server = true;
 | 
						|
 | 
						|
	// adapt tables to SRB2's needs, including extra slots for dehacked file support
 | 
						|
	P_PatchInfoTables();
 | 
						|
 | 
						|
	//---------------------------------------------------- READY TIME
 | 
						|
	// we need to check for dedicated before initialization of some subsystems
 | 
						|
 | 
						|
	CONS_Printf("I_InitializeTime()...\n");
 | 
						|
	I_InitializeTime();
 | 
						|
	CON_SetLoadingProgress(LOADED_ISTARTUPTIMER);
 | 
						|
 | 
						|
	// Make backups of some SOCcable tables.
 | 
						|
	P_BackupTables();
 | 
						|
 | 
						|
	// load wad, including the main wad file
 | 
						|
	CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n");
 | 
						|
	W_InitMultipleFiles(startupiwads, false);
 | 
						|
	D_CleanFile(startupiwads);
 | 
						|
 | 
						|
	mainwads = 0;
 | 
						|
 | 
						|
#ifndef DEVELOP
 | 
						|
	// Check MD5s of autoloaded files
 | 
						|
	// Note: Do not add any files that ignore MD5!
 | 
						|
	W_VerifyFileMD5(mainwads, ASSET_HASH_MAIN_KART);					// main.kart
 | 
						|
	mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_GFX_PK3);			// gfx.pk3
 | 
						|
	mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_PK3);		// textures.pk3
 | 
						|
	mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_CHARS_PK3);		// chars.pk3
 | 
						|
	mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_PK3);			// maps.pk3 -- 4 - If you touch this, make sure to touch up the majormods stuff below.
 | 
						|
	mainwads++; W_VerifyFileMd5(mainwads, ASSET_HASH_FOLLOWERS_PK3);  // followers.pk3
 | 
						|
#ifdef USE_PATCH_FILE
 | 
						|
	mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_PK3);		// patch.pk3
 | 
						|
#endif
 | 
						|
#else
 | 
						|
#if !defined (TESTERS) && !defined (HOSTTESTERS)
 | 
						|
	mainwads++;	// gfx.pk3
 | 
						|
#endif
 | 
						|
	mainwads++;	// textures.pk3
 | 
						|
	mainwads++;	// chars.pk3
 | 
						|
	mainwads++;	// maps.pk3
 | 
						|
	mainwads++; // followers.pk3
 | 
						|
#ifdef USE_PATCH_FILE
 | 
						|
	mainwads++;	// patch.pk3
 | 
						|
#endif
 | 
						|
 | 
						|
#endif //ifndef DEVELOP
 | 
						|
 | 
						|
	//
 | 
						|
	// search for maps
 | 
						|
	//
 | 
						|
	for (wadnum = 0; wadnum <= mainwads; wadnum++)
 | 
						|
	{
 | 
						|
		lumpinfo = wadfiles[wadnum]->lumpinfo;
 | 
						|
		for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++)
 | 
						|
		{
 | 
						|
			name = lumpinfo->name;
 | 
						|
 | 
						|
			if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
 | 
						|
			{
 | 
						|
				INT16 num;
 | 
						|
				if (name[5] != '\0')
 | 
						|
					continue;
 | 
						|
				num = (INT16)M_MapNumber(name[3], name[4]);
 | 
						|
 | 
						|
				// we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant
 | 
						|
				if (num <= NUMMAPS && mapheaderinfo[num - 1])
 | 
						|
				{
 | 
						|
					mapheaderinfo[num - 1]->alreadyExists = true;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	CON_SetLoadingProgress(LOADED_IWAD);
 | 
						|
 | 
						|
	CONS_Printf("W_InitMultipleFiles(): Adding external PWADs.\n");
 | 
						|
	W_InitMultipleFiles(startuppwads, true);
 | 
						|
	D_CleanFile(startuppwads);
 | 
						|
 | 
						|
	//
 | 
						|
	// search for maps... again.
 | 
						|
	//
 | 
						|
	for (wadnum = mainwads+1; wadnum < numwadfiles; wadnum++)
 | 
						|
	{
 | 
						|
		lumpinfo = wadfiles[wadnum]->lumpinfo;
 | 
						|
		for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++)
 | 
						|
		{
 | 
						|
			name = lumpinfo->name;
 | 
						|
 | 
						|
			if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
 | 
						|
			{
 | 
						|
				INT16 num;
 | 
						|
				if (name[5] != '\0')
 | 
						|
					continue;
 | 
						|
				num = (INT16)M_MapNumber(name[3], name[4]);
 | 
						|
 | 
						|
				// we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant
 | 
						|
				if (num <= NUMMAPS && mapheaderinfo[num - 1])
 | 
						|
				{
 | 
						|
					if (mapheaderinfo[num - 1]->alreadyExists != false)
 | 
						|
					{
 | 
						|
						G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you
 | 
						|
					}
 | 
						|
 | 
						|
					mapheaderinfo[num - 1]->alreadyExists = true;
 | 
						|
				}
 | 
						|
 | 
						|
				CONS_Printf("%s\n", name);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	CON_SetLoadingProgress(LOADED_PWAD);
 | 
						|
 | 
						|
	cht_Init();
 | 
						|
 | 
						|
	//---------------------------------------------------- READY SCREEN
 | 
						|
	// we need to check for dedicated before initialization of some subsystems
 | 
						|
 | 
						|
	CONS_Printf("I_StartupGraphics()...\n");
 | 
						|
	I_StartupGraphics();
 | 
						|
 | 
						|
#ifdef HWRENDER
 | 
						|
	// Lactozilla: Add every hardware mode CVAR and CCMD.
 | 
						|
	// Has to be done before the configuration file loads,
 | 
						|
	// but after the OpenGL library loads.
 | 
						|
	HWR_AddCommands();
 | 
						|
#endif
 | 
						|
 | 
						|
	//--------------------------------------------------------- CONSOLE
 | 
						|
	// setup loading screen
 | 
						|
	SCR_Startup();
 | 
						|
 | 
						|
	// Do this in background; lots of number crunching
 | 
						|
	R_InitTranslucencyTables();
 | 
						|
 | 
						|
	CON_SetLoadingProgress(LOADED_ISTARTUPGRAPHICS);
 | 
						|
 | 
						|
	CONS_Printf("HU_Init()...\n");
 | 
						|
	HU_Init();
 | 
						|
 | 
						|
	CON_Init();
 | 
						|
 | 
						|
	CON_SetLoadingProgress(LOADED_HUINIT);
 | 
						|
 | 
						|
	memset(timelimits, 0, sizeof(timelimits));
 | 
						|
	memset(pointlimits, 0, sizeof(pointlimits));
 | 
						|
 | 
						|
	timelimits[GT_BATTLE] = 2;
 | 
						|
 | 
						|
	D_RegisterServerCommands();
 | 
						|
	D_RegisterClientCommands(); // be sure that this is called before D_CheckNetGame
 | 
						|
	R_RegisterEngineStuff();
 | 
						|
	S_RegisterSoundStuff();
 | 
						|
 | 
						|
	I_RegisterSysCommands();
 | 
						|
 | 
						|
	//--------------------------------------------------------- CONFIG.CFG
 | 
						|
	M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()"
 | 
						|
 | 
						|
	// Load Profiles now that default controls have been defined
 | 
						|
	PR_LoadProfiles();	// load control profiles
 | 
						|
 | 
						|
	M_Init();
 | 
						|
 | 
						|
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
 | 
						|
	VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
 | 
						|
#endif
 | 
						|
 | 
						|
	// set user default mode or mode set at cmdline
 | 
						|
	SCR_CheckDefaultMode();
 | 
						|
 | 
						|
	if (M_CheckParm("-noupload"))
 | 
						|
		COM_BufAddText("downloading 0\n");
 | 
						|
 | 
						|
	G_LoadGameData();
 | 
						|
 | 
						|
	wipegamestate = gamestate;
 | 
						|
 | 
						|
	savedata.lives = 0; // flag this as not-used
 | 
						|
 | 
						|
	CON_SetLoadingProgress(LOADED_CONFIG);
 | 
						|
 | 
						|
	CONS_Printf("R_InitTextureData()...\n");
 | 
						|
	R_InitTextureData(); // seperated out from below because it takes ages by itself
 | 
						|
	CON_SetLoadingProgress(LOADED_INITTEXTUREDATA);
 | 
						|
 | 
						|
	CONS_Printf("R_InitSprites()...\n");
 | 
						|
	R_InitSprites(); // ditto
 | 
						|
	CON_SetLoadingProgress(LOADED_INITSPRITES);
 | 
						|
 | 
						|
	CONS_Printf("R_InitSkins()...\n");
 | 
						|
	R_InitSkins(); // ditto
 | 
						|
	CON_SetLoadingProgress(LOADED_INITSKINS);
 | 
						|
 | 
						|
	CONS_Printf("R_Init(): Init SRB2 refresh daemon.\n");
 | 
						|
	R_Init();
 | 
						|
	CON_SetLoadingProgress(LOADED_RINIT);
 | 
						|
 | 
						|
	// setting up sound
 | 
						|
	if (dedicated)
 | 
						|
	{
 | 
						|
		sound_disabled = true;
 | 
						|
		digital_disabled = true;
 | 
						|
	}
 | 
						|
 | 
						|
	if (M_CheckParm("-noaudio")) // combines -nosound and -nomusic
 | 
						|
	{
 | 
						|
		sound_disabled = true;
 | 
						|
		digital_disabled = true;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		if (M_CheckParm("-nosound"))
 | 
						|
			sound_disabled = true;
 | 
						|
		if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic
 | 
						|
		{
 | 
						|
			digital_disabled = true;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			if (M_CheckParm("-nodigmusic"))
 | 
						|
				digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (!( sound_disabled && digital_disabled ))
 | 
						|
	{
 | 
						|
		CONS_Printf("S_InitSfxChannels(): Setting up sound channels.\n");
 | 
						|
		I_StartupSound();
 | 
						|
		I_InitMusic();
 | 
						|
		S_InitSfxChannels(cv_soundvolume.value);
 | 
						|
	}
 | 
						|
	CON_SetLoadingProgress(LOADED_SINITSFXCHANNELS);
 | 
						|
 | 
						|
	S_InitMusicDefs();
 | 
						|
 | 
						|
	CONS_Printf("ST_Init(): Init status bar.\n");
 | 
						|
	ST_Init();
 | 
						|
	CON_SetLoadingProgress(LOADED_STINIT);
 | 
						|
 | 
						|
	//------------------------------------------------ COMMAND LINE PARAMS
 | 
						|
 | 
						|
	// this must be done after loading gamedata,
 | 
						|
	// to avoid setting off the corrupted gamedata code in G_LoadGameData if a SOC with custom gamedata is added
 | 
						|
	// -- Monster Iestyn 20/02/20
 | 
						|
	if (M_CheckParm("-warp") && M_IsNextParm())
 | 
						|
	{
 | 
						|
		const char *word = M_GetNextParm();
 | 
						|
 | 
						|
		pstartmap = G_FindMapByNameOrCode(word, 0);
 | 
						|
 | 
						|
		if (! pstartmap)
 | 
						|
			I_Error("Cannot find a map remotely named '%s'\n", word);
 | 
						|
		else
 | 
						|
		{
 | 
						|
			if (!M_CheckParm("-server"))
 | 
						|
			{
 | 
						|
				G_SetGameModified(true, true);
 | 
						|
 | 
						|
				// Start up a "minor" grand prix session
 | 
						|
				memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
 | 
						|
 | 
						|
				grandprixinfo.gamespeed = KARTSPEED_NORMAL;
 | 
						|
				grandprixinfo.encore = false;
 | 
						|
				grandprixinfo.masterbots = false;
 | 
						|
 | 
						|
				grandprixinfo.gp = true;
 | 
						|
				grandprixinfo.roundnum = 0;
 | 
						|
				grandprixinfo.cup = NULL;
 | 
						|
				grandprixinfo.wonround = false;
 | 
						|
 | 
						|
				grandprixinfo.initalize = true;
 | 
						|
			}
 | 
						|
 | 
						|
			autostart = true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Set up splitscreen players before joining!
 | 
						|
	if (!dedicated && (M_CheckParm("-splitscreen") && M_IsNextParm()))
 | 
						|
	{
 | 
						|
		UINT8 num = atoi(M_GetNextParm());
 | 
						|
		if (num >= 1 && num <= 4)
 | 
						|
		{
 | 
						|
			CV_StealthSetValue(&cv_splitplayers, num);
 | 
						|
			splitscreen = num-1;
 | 
						|
			SplitScreen_OnChange();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// init all NETWORK
 | 
						|
	CONS_Printf("D_CheckNetGame(): Checking network game status.\n");
 | 
						|
	if (D_CheckNetGame())
 | 
						|
		autostart = true;
 | 
						|
	CON_SetLoadingProgress(LOADED_DCHECKNETGAME);
 | 
						|
 | 
						|
	if (splitscreen && !M_CheckParm("-connect")) // Make sure multiplayer & autostart is set if you have splitscreen, even after D_CheckNetGame
 | 
						|
		multiplayer = autostart = true;
 | 
						|
 | 
						|
	// check for a driver that wants intermission stats
 | 
						|
	// start the apropriate game based on parms
 | 
						|
	if (M_CheckParm("-metal"))
 | 
						|
	{
 | 
						|
		G_RecordMetal();
 | 
						|
		autostart = true;
 | 
						|
	}
 | 
						|
	else if (M_CheckParm("-record") && M_IsNextParm())
 | 
						|
	{
 | 
						|
		G_RecordDemo(M_GetNextParm());
 | 
						|
		autostart = true;
 | 
						|
	}
 | 
						|
 | 
						|
	// user settings come before "+" parameters.
 | 
						|
	if (dedicated)
 | 
						|
		COM_ImmedExecute(va("exec \"%s"PATHSEP"ringserv.cfg\"\n", srb2home));
 | 
						|
	else
 | 
						|
		COM_ImmedExecute(va("exec \"%s"PATHSEP"ringexec.cfg\" -noerror\n", srb2home));
 | 
						|
 | 
						|
	if (!autostart)
 | 
						|
		M_PushSpecialParameters(); // push all "+" parameters at the command buffer
 | 
						|
 | 
						|
	// demo doesn't need anymore to be added with D_AddFile()
 | 
						|
	p = M_CheckParm("-playdemo");
 | 
						|
	if (!p)
 | 
						|
		p = M_CheckParm("-timedemo");
 | 
						|
	if (p && M_IsNextParm())
 | 
						|
	{
 | 
						|
		char tmp[MAX_WADPATH];
 | 
						|
		// add .lmp to identify the EXTERNAL demo file
 | 
						|
		// it is NOT possible to play an internal demo using -playdemo,
 | 
						|
		// rather push a playdemo command.. to do.
 | 
						|
 | 
						|
		strcpy(tmp, M_GetNextParm());
 | 
						|
		// get spaced filename or directory
 | 
						|
		while (M_IsNextParm())
 | 
						|
		{
 | 
						|
			strcat(tmp, " ");
 | 
						|
			strcat(tmp, M_GetNextParm());
 | 
						|
		}
 | 
						|
 | 
						|
		FIL_DefaultExtension(tmp, ".lmp");
 | 
						|
 | 
						|
		CONS_Printf(M_GetText("Playing demo %s.\n"), tmp);
 | 
						|
 | 
						|
		if (M_CheckParm("-playdemo"))
 | 
						|
		{
 | 
						|
			demo.quitafterplaying = true; // quit after one demo
 | 
						|
			G_DeferedPlayDemo(tmp);
 | 
						|
		}
 | 
						|
		else
 | 
						|
			G_TimeDemo(tmp);
 | 
						|
 | 
						|
		G_SetGamestate(GS_NULL);
 | 
						|
		wipegamestate = GS_NULL;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/*if (M_CheckParm("-ultimatemode"))
 | 
						|
	{
 | 
						|
		autostart = true;
 | 
						|
		ultimatemode = true;
 | 
						|
	}*/
 | 
						|
 | 
						|
	// rei/miru: bootmap (Idea: starts the game on a predefined map)
 | 
						|
	if (bootmap && !(M_CheckParm("-warp") && M_IsNextParm()))
 | 
						|
	{
 | 
						|
		pstartmap = bootmap;
 | 
						|
 | 
						|
		if (pstartmap < 1 || pstartmap > NUMMAPS)
 | 
						|
			I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
 | 
						|
		else
 | 
						|
		{
 | 
						|
			autostart = true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (autostart || netgame)
 | 
						|
	{
 | 
						|
		gameaction = ga_nothing;
 | 
						|
 | 
						|
		CV_ClearChangedFlags();
 | 
						|
 | 
						|
		// Has to be done before anything else so skin, color, etc in command buffer has an affect.
 | 
						|
		// ttlprofilen used because it's roughly equivalent in functionality - a QoL aid for quickly getting from startup to action
 | 
						|
		PR_ApplyProfile(cv_ttlprofilen.value, 0);
 | 
						|
 | 
						|
		// Do this here so if you run SRB2 with eg +timelimit 5, the time limit counts
 | 
						|
		// as having been modified for the first game.
 | 
						|
		M_PushSpecialParameters(); // push all "+" parameter at the command buffer
 | 
						|
 | 
						|
		COM_BufExecute(); // ensure the command buffer gets executed before the map starts (+skin)
 | 
						|
 | 
						|
		strncpy(connectedservername, cv_servername.string, MAXSERVERNAME);
 | 
						|
 | 
						|
		if (M_CheckParm("-gametype") && M_IsNextParm())
 | 
						|
		{
 | 
						|
			// from Command_Map_f
 | 
						|
			INT32 j;
 | 
						|
			INT16 newgametype = -1;
 | 
						|
			const char *sgametype = M_GetNextParm();
 | 
						|
 | 
						|
			newgametype = G_GetGametypeByName(sgametype);
 | 
						|
 | 
						|
			if (newgametype == -1) // reached end of the list with no match
 | 
						|
			{
 | 
						|
				j = atoi(sgametype); // assume they gave us a gametype number, which is okay too
 | 
						|
				if (j >= 0 && j < gametypecount)
 | 
						|
					newgametype = (INT16)j;
 | 
						|
			}
 | 
						|
 | 
						|
			if (newgametype != -1)
 | 
						|
			{
 | 
						|
				j = gametype;
 | 
						|
				G_SetGametype(newgametype);
 | 
						|
				D_GameTypeChanged(j);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (M_CheckParm("-skill") && M_IsNextParm())
 | 
						|
		{
 | 
						|
			INT32 j;
 | 
						|
			INT16 newskill = -1;
 | 
						|
			const char *sskill = M_GetNextParm();
 | 
						|
 | 
						|
			const char *masterstr = "Master";
 | 
						|
 | 
						|
			if (!strcasecmp(masterstr, sskill))
 | 
						|
			{
 | 
						|
				newskill = KARTGP_MASTER;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				for (j = 0; kartspeed_cons_t[j].strvalue; j++)
 | 
						|
				{
 | 
						|
					if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill))
 | 
						|
					{
 | 
						|
						newskill = (INT16)kartspeed_cons_t[j].value;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
 | 
						|
				{
 | 
						|
					j = atoi(sskill); // assume they gave us a skill number, which is okay too
 | 
						|
					if (j >= KARTSPEED_EASY && j <= KARTGP_MASTER)
 | 
						|
						newskill = (INT16)j;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (grandprixinfo.gp == true)
 | 
						|
			{
 | 
						|
				if (newskill == KARTGP_MASTER)
 | 
						|
				{
 | 
						|
					grandprixinfo.masterbots = true;
 | 
						|
					newskill = KARTSPEED_HARD;
 | 
						|
				}
 | 
						|
 | 
						|
				grandprixinfo.gamespeed = newskill;
 | 
						|
			}
 | 
						|
			else if (newskill == KARTGP_MASTER)
 | 
						|
			{
 | 
						|
				newskill = KARTSPEED_HARD;
 | 
						|
			}
 | 
						|
 | 
						|
			if (newskill != -1)
 | 
						|
				CV_SetValue(&cv_kartspeed, newskill);
 | 
						|
		}
 | 
						|
 | 
						|
		if (server && !M_CheckParm("+map"))
 | 
						|
		{
 | 
						|
			// Prevent warping to nonexistent levels
 | 
						|
			if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR)
 | 
						|
				I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap));
 | 
						|
			// Prevent warping to locked levels
 | 
						|
			// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
 | 
						|
			// running a dedicated server and joining it yourself, but that's better than making dedicated server's
 | 
						|
			// lives hell.
 | 
						|
			else if (!dedicated && M_MapLocked(pstartmap))
 | 
						|
				I_Error("You need to unlock this level before you can warp to it!\n");
 | 
						|
			else
 | 
						|
			{
 | 
						|
				D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (M_CheckParm("-skipintro"))
 | 
						|
	{
 | 
						|
		F_StartTitleScreen();
 | 
						|
	}
 | 
						|
	else
 | 
						|
		F_StartIntro(); // Tails 03-03-2002
 | 
						|
 | 
						|
	CON_ToggleOff();
 | 
						|
 | 
						|
	if (dedicated && server)
 | 
						|
	{
 | 
						|
		levelstarttic = gametic;
 | 
						|
		G_SetGamestate(GS_LEVEL);
 | 
						|
		if (!P_LoadLevel(false, false))
 | 
						|
			I_Quit(); // fail so reset game stuff
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef HAVE_DISCORDRPC
 | 
						|
	if (! dedicated)
 | 
						|
	{
 | 
						|
		DRPC_Init();
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	if (con_startup_loadprogress != LOADED_ALLDONE)
 | 
						|
	{
 | 
						|
		I_Error("Something is wrong with the loading bar! (got %d, expected %d)\n", con_startup_loadprogress, LOADED_ALLDONE);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
const char *D_Home(void)
 | 
						|
{
 | 
						|
	const char *userhome = NULL;
 | 
						|
 | 
						|
#ifdef ANDROID
 | 
						|
	return "/data/data/org.srb2/";
 | 
						|
#endif
 | 
						|
 | 
						|
	if (M_CheckParm("-home") && M_IsNextParm())
 | 
						|
		userhome = M_GetNextParm();
 | 
						|
	else
 | 
						|
	{
 | 
						|
#if !((defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)) && !defined (__APPLE__)
 | 
						|
		if (FIL_FileOK(CONFIGFILENAME))
 | 
						|
			usehome = false; // Let's NOT use home
 | 
						|
		else
 | 
						|
#endif
 | 
						|
			userhome = I_GetEnv("HOME"); //Alam: my new HOME for srb2
 | 
						|
	}
 | 
						|
#ifdef _WIN32 //Alam: only Win32 have APPDATA and USERPROFILE
 | 
						|
	if (!userhome && usehome) //Alam: Still not?
 | 
						|
	{
 | 
						|
		char *testhome = NULL;
 | 
						|
		testhome = I_GetEnv("APPDATA");
 | 
						|
		if (testhome != NULL
 | 
						|
			&& (FIL_FileOK(va("%s" PATHSEP "%s" PATHSEP CONFIGFILENAME, testhome, DEFAULTDIR))))
 | 
						|
		{
 | 
						|
			userhome = testhome;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#ifndef __CYGWIN__
 | 
						|
	if (!userhome && usehome) //Alam: All else fails?
 | 
						|
	{
 | 
						|
		char *testhome = NULL;
 | 
						|
		testhome = I_GetEnv("USERPROFILE");
 | 
						|
		if (testhome != NULL
 | 
						|
			&& (FIL_FileOK(va("%s" PATHSEP "%s" PATHSEP CONFIGFILENAME, testhome, DEFAULTDIR))))
 | 
						|
		{
 | 
						|
			userhome = testhome;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif// !__CYGWIN__
 | 
						|
#endif// _WIN32
 | 
						|
	if (usehome) return userhome;
 | 
						|
	else return NULL;
 | 
						|
}
 |