Files
itgmania212121/stepmania/src/StepMania.cpp
T

1140 lines
30 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
/*
-----------------------------------------------------------------------------
File: StepMania.cpp
2002-07-23 01:41:40 +00:00
Desc: Entry point for program.
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-07-23 01:41:40 +00:00
Chris Danford
-----------------------------------------------------------------------------
*/
2001-11-03 10:52:42 +00:00
#include "StepMania.h"
2002-07-27 19:29:51 +00:00
//
// Rage global classes
//
#include "RageLog.h"
#include "RageTextureManager.h"
#include "RageSoundManager.h"
2003-07-26 22:47:55 +00:00
#include "RageSounds.h"
2002-07-27 19:29:51 +00:00
#include "RageInput.h"
#include "RageTimer.h"
#include "RageException.h"
#include "RageMath.h"
2003-05-22 05:28:37 +00:00
#include "RageDisplay.h"
2002-07-27 19:29:51 +00:00
2002-11-17 09:13:35 +00:00
#include "arch/arch.h"
#include "arch/LoadingWindow/LoadingWindow.h"
2003-02-02 18:18:51 +00:00
#include "time.h"
2002-11-17 09:13:35 +00:00
2003-02-14 06:31:09 +00:00
#include "SDL_utils.h"
2002-10-24 03:56:46 +00:00
2002-04-16 17:31:00 +00:00
//
// StepMania global classes
//
#include "ThemeManager.h"
#include "NoteSkinManager.h"
2002-02-28 19:40:40 +00:00
#include "PrefsManager.h"
#include "SongManager.h"
2002-07-23 01:41:40 +00:00
#include "GameState.h"
2002-05-19 01:59:48 +00:00
#include "AnnouncerManager.h"
#include "ProfileManager.h"
2002-05-19 01:59:48 +00:00
#include "ScreenManager.h"
#include "GameManager.h"
#include "FontManager.h"
2002-04-16 17:31:00 +00:00
#include "InputFilter.h"
#include "InputMapper.h"
2002-05-19 01:59:48 +00:00
#include "InputQueue.h"
#include "SongCacheIndex.h"
2003-06-04 22:42:17 +00:00
#include "BannerCache.h"
#include "UnlockSystem.h"
#include "arch/ArchHooks/ArchHooks.h"
2003-07-22 07:47:27 +00:00
#include "RageFile.h"
#if defined(_XBOX)
#ifdef DEBUG
2003-10-04 06:41:17 +00:00
#pragma comment(lib, "SDL-1.2.6/lib/xboxSDLmaind.lib")
2003-07-22 07:47:27 +00:00
#else
2003-10-04 06:41:17 +00:00
#pragma comment(lib, "SDL-1.2.6/lib/xboxSDLmain.lib")
2003-07-22 07:47:27 +00:00
#endif
#elif defined(_WINDOWS)
#ifdef DEBUG
2003-10-04 06:41:17 +00:00
#pragma comment(lib, "SDL-1.2.6/lib/SDLmaind.lib")
2003-07-22 07:47:27 +00:00
#else
2003-10-04 06:41:17 +00:00
#pragma comment(lib, "SDL-1.2.6/lib/SDLmain.lib")
2003-07-22 07:47:27 +00:00
#endif
#endif
2001-11-03 10:52:42 +00:00
2003-07-22 07:47:27 +00:00
#ifdef _WINDOWS
2002-12-03 00:35:34 +00:00
HWND g_hWndMain = NULL;
#endif
2001-11-03 10:52:42 +00:00
int g_argc = 0;
char **g_argv = NULL;
static bool g_bHasFocus = true;
static bool g_bQuitting = false;
2001-11-03 10:52:42 +00:00
2003-09-05 06:39:32 +00:00
CString DirOfExecutable;
2002-12-16 02:11:31 +00:00
static void ChangeToDirOfExecutable(const char *argv0)
2002-07-03 21:27:26 +00:00
{
2003-09-05 06:39:32 +00:00
DirOfExecutable = argv0;
// strip off executable name
unsigned n = DirOfExecutable.find_last_of("/\\");
if( n != DirOfExecutable.npos )
DirOfExecutable.erase(n);
else
DirOfExecutable.erase();
2003-09-05 07:15:21 +00:00
bool IsAbsolutePath = false;
if( DirOfExecutable.size() == 0 || (DirOfExecutable[0] == '/' || DirOfExecutable[0] == '\\') )
IsAbsolutePath = true;
#if defined(_WIN32)
2003-09-05 20:13:36 +00:00
if( DirOfExecutable.size() > 2 && DirOfExecutable[1] == ':' &&
(DirOfExecutable[2] == '/' || DirOfExecutable[2] == '\\') )
2003-09-05 07:15:21 +00:00
IsAbsolutePath = true;
#endif
if( !IsAbsolutePath )
2003-09-05 06:39:32 +00:00
DirOfExecutable = GetCwd() + "/" + DirOfExecutable;
2003-07-22 07:47:27 +00:00
#ifndef _XBOX
2002-12-16 02:11:31 +00:00
/* Make sure the current directory is the root program directory
* We probably shouldn't do this; rather, we should know where things
* are and use paths as needed, so we don't depend on the binary being
* in the same place as "Songs" ... */
if( !DoesFileExist("Songs") )
{
2003-09-05 06:39:32 +00:00
chdir( DirOfExecutable );
FlushDirCache();
}
2003-07-22 07:47:27 +00:00
#endif
}
2002-07-03 21:27:26 +00:00
2003-08-12 18:50:42 +00:00
static RageDisplay::VideoModeParams GetCurVideoModeParams()
{
return RageDisplay::VideoModeParams(
2003-06-05 19:29:27 +00:00
PREFSMAN->m_bWindowed,
PREFSMAN->m_iDisplayWidth,
PREFSMAN->m_iDisplayHeight,
PREFSMAN->m_iDisplayColorDepth,
PREFSMAN->m_iRefreshRate,
PREFSMAN->m_bVsync,
PREFSMAN->m_bInterlaced,
2003-06-05 19:29:27 +00:00
PREFSMAN->m_bAntiAliasing,
THEME->GetMetric("Common","WindowTitle"),
THEME->GetPathToG("Common window icon")
#ifdef _XBOX
, PREFSMAN->m_bPAL
#endif
2003-08-12 18:50:42 +00:00
);
}
2003-09-15 07:52:58 +00:00
static void StoreActualGraphicOptions( bool initial )
{
// find out what we actually have
PREFSMAN->m_bWindowed = DISPLAY->GetVideoModeParams().windowed;
PREFSMAN->m_iDisplayWidth = DISPLAY->GetVideoModeParams().width;
PREFSMAN->m_iDisplayHeight = DISPLAY->GetVideoModeParams().height;
PREFSMAN->m_iDisplayColorDepth = DISPLAY->GetVideoModeParams().bpp;
PREFSMAN->m_iRefreshRate = DISPLAY->GetVideoModeParams().rate;
PREFSMAN->m_bVsync = DISPLAY->GetVideoModeParams().vsync;
CString log = ssprintf("%s %dx%d %d color %d texture %dHz %s %s",
PREFSMAN->m_bWindowed ? "Windowed" : "Fullscreen",
PREFSMAN->m_iDisplayWidth,
PREFSMAN->m_iDisplayHeight,
PREFSMAN->m_iDisplayColorDepth,
PREFSMAN->m_iTextureColorDepth,
PREFSMAN->m_iRefreshRate,
PREFSMAN->m_bVsync ? "Vsync" : "NoVsync",
PREFSMAN->m_bAntiAliasing? "AA" : "NoAA" );
if( initial )
LOG->Info( "%s", log.c_str() );
else
SCREENMAN->SystemMessage( log );
}
2003-08-12 18:50:42 +00:00
void ApplyGraphicOptions()
{
bool bNeedReload = false;
bNeedReload |= DISPLAY->SetVideoMode( GetCurVideoModeParams() );
bNeedReload |= TEXTUREMAN->SetPrefs(
PREFSMAN->m_iTextureColorDepth,
2003-07-09 02:28:56 +00:00
PREFSMAN->m_iMovieColorDepth,
2003-04-18 23:55:20 +00:00
PREFSMAN->m_bDelayedTextureDelete,
PREFSMAN->m_iMaxTextureResolution );
2002-11-12 09:31:08 +00:00
if( bNeedReload )
TEXTUREMAN->ReloadAll();
2003-09-15 07:52:58 +00:00
StoreActualGraphicOptions( false );
2003-06-18 05:43:12 +00:00
/* Give the input handlers a chance to re-open devices as necessary. */
INPUTMAN->WindowReset();
}
2001-11-03 10:52:42 +00:00
2003-01-19 04:44:22 +00:00
void ExitGame()
{
g_bQuitting = true;
}
void ResetGame( bool ReturnToFirstScreen )
{
GAMESTATE->Reset();
ReadGamePrefsFromDisk();
INPUTMAPPER->ReadMappingsFromDisk();
2003-06-25 05:22:58 +00:00
NOTESKIN->RefreshNoteSkinData( GAMESTATE->m_CurGame );
2003-04-21 23:43:51 +00:00
/*
GameState::Reset() will switch the NoteSkin
2003-02-17 12:19:42 +00:00
for( int p=0; p<NUM_PLAYERS; p++ )
{
2003-02-17 12:19:42 +00:00
PlayerNumber pn = (PlayerNumber)p;
if( !NOTESKIN->DoesNoteSkinExist( NOTESKIN->GetCurNoteSkinName(pn) ) )
{
CStringArray asNoteSkinNames;
NOTESKIN->GetNoteSkinNames( asNoteSkinNames );
NOTESKIN->SwitchNoteSkin( pn, asNoteSkinNames[0] );
}
}
2003-04-21 23:43:51 +00:00
*/
if( !THEME->DoesThemeExist( THEME->GetCurThemeName() ) )
{
CString sGameName = GAMESTATE->GetCurrentGameDef()->m_szName;
if( THEME->DoesThemeExist( sGameName ) )
2003-08-19 08:01:15 +00:00
THEME->SwitchThemeAndLanguage( sGameName, THEME->GetCurLanguage() );
else
2003-08-19 08:01:15 +00:00
THEME->SwitchThemeAndLanguage( "default", THEME->GetCurLanguage() );
2003-04-18 23:55:20 +00:00
TEXTUREMAN->DoDelayedDelete();
}
SaveGamePrefsToDisk();
PREFSMAN->m_bFirstRun = false;
2003-05-28 02:35:05 +00:00
//
// update last seen joysticks
//
vector<InputDevice> vDevices;
vector<CString> vDescriptions;
INPUTMAN->GetDevicesAndDescriptions(vDevices,vDescriptions);
vector<CString> vOldDescriptions;
split( PREFSMAN->m_sLastSeenInputDevices, ",", vOldDescriptions );
CString sInputDevices = join( ",", vDescriptions );
if( PREFSMAN->m_sLastSeenInputDevices != sInputDevices )
{
LOG->Info( "Input devices changed from '%s' to '%s'.", PREFSMAN->m_sLastSeenInputDevices.c_str(), sInputDevices.c_str() );
if( PREFSMAN->m_bAutoMapOnJoyChange )
{
LOG->Info( "Remapping joysticks." );
INPUTMAPPER->AutoMapJoysticksForCurrentGame();
}
PREFSMAN->m_sLastSeenInputDevices = sInputDevices;
}
if( ReturnToFirstScreen )
SCREENMAN->SetNewScreen( THEME->GetMetric("Common","InitialScreen") );
}
2001-11-03 10:52:42 +00:00
2002-12-19 23:26:50 +00:00
static void GameLoop();
static bool ChangeAppPri()
2003-01-18 04:27:33 +00:00
{
if(PREFSMAN->m_iBoostAppPriority == 0)
return false;
2003-01-18 04:27:33 +00:00
/* If -1 and this is a debug build, don't. It makes the debugger sluggish. */
#ifdef DEBUG
if(PREFSMAN->m_iBoostAppPriority == -1)
return false;
2003-01-18 04:27:33 +00:00
#endif
return true;
}
static void BoostAppPri()
{
if(!ChangeAppPri())
return;
#ifdef _WINDOWS
2003-01-18 04:27:33 +00:00
/* We just want a slight boost, so we don't skip needlessly if something happens
* in the background. We don't really want to be high-priority--above normal should
* be enough. However, ABOVE_NORMAL_PRIORITY_CLASS is only supported in Win2000
* and later. */
OSVERSIONINFO version;
version.dwOSVersionInfoSize=sizeof(version);
if(!GetVersionEx(&version))
{
LOG->Warn(werr_ssprintf(GetLastError(), "GetVersionEx failed"));
return;
}
2003-01-19 21:05:03 +00:00
#ifndef ABOVE_NORMAL_PRIORITY_CLASS
#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
#endif
2003-01-18 04:27:33 +00:00
DWORD pri = HIGH_PRIORITY_CLASS;
if(version.dwMajorVersion >= 5)
pri = ABOVE_NORMAL_PRIORITY_CLASS;
/* Be sure to boost the app, not the thread, to make sure the
* sound thread stays higher priority than the main thread. */
SetPriorityClass(GetCurrentProcess(), pri);
#endif
}
static void CheckSettings()
{
#if defined(WIN32)
/* Has the amount of memory changed? */
MEMORYSTATUS mem;
GlobalMemoryStatus(&mem);
const int Memory = mem.dwTotalPhys / 1048576;
if( PREFSMAN->m_iLastSeenMemory == Memory )
return;
LOG->Trace( "Memory changed from %i to %i; settings changed", PREFSMAN->m_iLastSeenMemory, Memory );
PREFSMAN->m_iLastSeenMemory = Memory;
/* Let's consider 128-meg systems low-memory, and 256-meg systems high-memory.
* Cut off at 192. This is somewhat conservative; many 128-meg systems can
* deal with higher memory profile settings, but some can't.
*
* Actually, Windows lops off a meg or two; cut off a little lower to treat
* 192-meg systems as high-memory. */
const bool HighMemory = (Memory >= 190);
const bool LowMemory = (Memory < 100); /* 64 and 96-meg systems */
/* Two memory-consuming features that we can disable are texture caching and
* preloaded banners. Texture caching can use a lot of memory; disable it for
* low-memory systems. */
PREFSMAN->m_bDelayedTextureDelete = HighMemory;
/* Preloaded banners takes about 9k per song. Although it's smaller than the
* actual song data, it still adds up with a lot of songs. Disable it for 64-meg
* systems. */
PREFSMAN->m_bBannerCache = !LowMemory;
PREFSMAN->SaveGlobalPrefsToDisk();
#endif
}
2003-05-26 23:33:37 +00:00
#if defined(WIN32)
#include "RageDisplay_D3D.h"
#endif
#if !defined(_XBOX)
#include "RageDisplay_OGL.h"
#endif
#include "archutils/Win32/VideoDriverInfo.h"
2003-06-07 14:33:14 +00:00
#include "regex.h"
2003-05-28 05:58:24 +00:00
static const CString D3DURL = "http://search.microsoft.com/gomsuri.asp?n=1&c=rp_BestBets&siteid=us&target=http://www.microsoft.com/downloads/details.aspx?FamilyID=a19bed22-0b25-4e5d-a584-6389d8a3dad0&displaylang=en";
2003-07-22 07:47:27 +00:00
struct VideoCardDefaults
{
char szDriverRegex[200];
char szVideoRenderers[200];
int iWidth;
int iHeight;
int iDisplayColor;
int iTextureColor;
2003-10-07 08:18:28 +00:00
int iMovieColor;
int iTextureSize;
bool bAntiAliasing;
} const g_VideoCardDefaults[] =
{
{
"Voodoo3|3dfx",
"d3d,opengl",
2003-10-07 08:18:28 +00:00
640,480,
16,16,16,
256,
0 // broken, causes black screen
},
2003-10-07 08:18:28 +00:00
{
"Radeon 7|ArcadeVGA", // Radeon 7xxx
"opengl,d3d",
640,480,
16,16,16,
2048,
0 // accelerated?
2003-10-07 08:18:28 +00:00
},
{
"GeForce|Radeon",
"opengl,d3d",
2003-10-07 08:18:28 +00:00
640,480,
32,32,32, // 32 bit textures are faster to load
2048,
1 // hardware accelerated
},
{
"TNT|Vanta|M64",
"opengl,d3d",
2003-10-07 08:18:28 +00:00
640,480,
16,16,16, // Athlon 1.2+TNT demonstration w/ movies: 70fps w/ 32bit textures, 86fps w/ 16bit textures
2048,
1 // hardware accelerated
},
{
"G200|G250|G400",
"d3d,opengl",
2003-10-07 08:18:28 +00:00
640,480,
16,16,16,
2048,
0 // broken, causes black screen
},
{
"Savage",
"d3d",
// OpenGL is unusable on my Savage IV with even the latest drivers.
// It draws 30 frames of gibberish then crashes. This happens even with
// simple NeHe demos. -Chris
2003-10-07 08:18:28 +00:00
640,480,
16,16,16,
2048,
false
},
{
"XPERT@PLAY|IIC|RAGE PRO|RAGE LT PRO", // Rage Pro chip, Rage IIC chip
"d3d",
// OpenGL is not hardware accelerated, despite the fact that the
// drivers come with an ICD. Also, the WinXP driver performance
// is terrible and supports only 640. The ATI driver is usable.
// -Chris
2003-10-08 07:13:21 +00:00
320,240, // lower resolution for 60fps. In-box WinXP driver doesn't support 400x300.
2003-10-07 08:18:28 +00:00
16,16,16,
256,
0
},
{
"RAGE MOBILITY-M1",
"d3d,opengl", // Vertex alpha is broken in OpenGL, but not D3D. -Chris
2003-10-07 08:18:28 +00:00
400,300, // lower resolution for 60fps
16,16,16,
256,
0
},
{
"Intel.*82810|Intel.*82815",
"opengl,d3d",// OpenGL is 50%+ faster than D3D w/ latest Intel drivers. -Chris
2003-10-07 08:18:28 +00:00
512,384, // lower resolution for 60fps
16,16,16,
512,
0
},
{
// Cards that have problems with OpenGL:
// ASSERT fail somewhere in RageDisplay_OpenGL "Trident Video Accelerator CyberBlade"
// bug 764499: ASSERT fail after glDeleteTextures for "SiS 650_651_740"
// bug 764830: ASSERT fail after glDeleteTextures for "VIA Tech VT8361/VT8601 Graphics Controller"
// bug 791950: AV in glsis630!DrvSwapBuffers for "SiS 630/730"
"Trident Video Accelerator CyberBlade|VIA.*VT|SiS 6*",
"d3d,opengl",
2003-10-07 08:18:28 +00:00
640,480,
16,16,16,
2048,
0
},
{
"OpenGL", // This matches all drivers in Mac and Linux. -Chris
"opengl",
2003-10-07 08:18:28 +00:00
640,480,
16,16,16,
2048,
1 // Right now, they've got to have NVidia or ATi Cards anyway..
},
{
// Default graphics settings used for all cards that don't match above.
// This must be the very last entry!
"",
"opengl,d3d",
2003-10-07 08:18:28 +00:00
640,480,
16,16,16,
2048,
2003-10-07 08:18:28 +00:00
0 // AA is slow on some cards, so let's selectively enable HW accelerated cards.
},
};
static CString GetVideoDriverName()
{
#if defined(_WINDOWS)
return GetPrimaryVideoDriverName();
#else
return "OpenGL";
#endif
}
static void CheckVideoDefaultSettings()
{
// Video card changed since last run
CString sVideoDriver = GetVideoDriverName();
LOG->Trace( "Last seen video driver: " + PREFSMAN->m_sLastSeenVideoDriver );
const VideoCardDefaults* pDefaults = NULL;
2003-10-07 06:39:16 +00:00
for( unsigned i=0; i<ARRAYSIZE(g_VideoCardDefaults); i++ )
{
pDefaults = &g_VideoCardDefaults[i];
CString sDriverRegex = pDefaults->szDriverRegex;
Regex regex( sDriverRegex );
if( regex.Compare(sVideoDriver) )
{
LOG->Trace( "Card matches '%s'.", sDriverRegex.size()? sDriverRegex.c_str():"(unknown card)" );
break;
}
}
ASSERT( pDefaults ); // we must have matched at least one
CString sVideoRenderers = pDefaults->szVideoRenderers;
bool SetDefaultVideoParams=false;
if( PREFSMAN->m_sVideoRenderers == "" )
{
SetDefaultVideoParams = true;
2003-09-25 00:55:00 +00:00
LOG->Trace( "Applying defaults for %s.", sVideoDriver.c_str() );
}
else if( PREFSMAN->m_sLastSeenVideoDriver != sVideoDriver )
{
SetDefaultVideoParams = true;
LOG->Trace( "Video card has changed from %s to %s. Applying new defaults.", PREFSMAN->m_sLastSeenVideoDriver.c_str(), sVideoDriver.c_str() );
}
if( SetDefaultVideoParams )
{
PREFSMAN->m_sVideoRenderers = pDefaults->szVideoRenderers;
PREFSMAN->m_iDisplayWidth = pDefaults->iWidth;
PREFSMAN->m_iDisplayHeight = pDefaults->iHeight;
PREFSMAN->m_iDisplayColorDepth = pDefaults->iDisplayColor;
PREFSMAN->m_iTextureColorDepth = pDefaults->iTextureColor;
2003-10-07 08:18:28 +00:00
PREFSMAN->m_iMovieColorDepth = pDefaults->iMovieColor;
PREFSMAN->m_iMaxTextureResolution = pDefaults->iTextureSize;
PREFSMAN->m_bAntiAliasing = pDefaults->bAntiAliasing;
// Update last seen video card
PREFSMAN->m_sLastSeenVideoDriver = GetVideoDriverName();
}
else if( PREFSMAN->m_sVideoRenderers.CompareNoCase(sVideoRenderers) )
{
LOG->Warn("Video renderer list has been changed from '%s' to '%s'",
sVideoRenderers.c_str(), PREFSMAN->m_sVideoRenderers.c_str() );
}
}
2003-05-26 23:33:37 +00:00
RageDisplay *CreateDisplay()
{
/* We never want to bother users with having to decide which API to use.
*
* Some cards simply are too troublesome with OpenGL to ever use it, eg. Voodoos.
* If D3D8 isn't installed on those, complain and refuse to run (by default).
* For others, always use OpenGL. Allow forcing to D3D as an advanced option.
*
* If we're missing acceleration when we load D3D8 due to a card being in the
* D3D list, it means we need drivers and that they do exist.
*
* If we try to load OpenGL and we're missing acceleration, it may mean:
* 1. We're missing drivers, and they just need upgrading.
* 2. The card doesn't have drivers, and it should be using D3D8. In other words,
* it needs an entry in this table.
* 3. The card doesn't have drivers for either. (Sorry, no S3 868s.) Can't play.
*
* In this case, fail to load; don't silently fall back on D3D. We don't want
* people unknowingly using D3D8 with old drivers (and reporting obscure bugs
* due to driver problems). We'll probably get bug reports for all three types.
* #2 is the only case that's actually a bug.
*
2003-05-28 05:58:24 +00:00
* Actually, right now we're falling back. I'm not sure which behavior is better.
2003-05-26 23:33:37 +00:00
*/
2003-05-31 21:34:07 +00:00
CheckVideoDefaultSettings();
2003-08-12 18:50:42 +00:00
RageDisplay::VideoModeParams params(GetCurVideoModeParams());
2003-06-05 19:29:27 +00:00
2003-05-31 21:34:07 +00:00
CString error = "There was an error while initializing your video card.\n\n"
" PLEASE DO NOT FILE THIS ERROR AS A BUG!\n\n"
"Video Driver: "+GetVideoDriverName()+"\n\n";
2003-05-28 05:58:24 +00:00
LOG->Info( "Video renderers: '%s'", PREFSMAN->m_sVideoRenderers.c_str() );
2003-08-12 06:50:42 +00:00
2003-05-31 21:34:07 +00:00
CStringArray asRenderers;
split( PREFSMAN->m_sVideoRenderers, ",", asRenderers, true );
if( asRenderers.empty() )
RageException::Throw("No video renderers attempted.");
2003-06-01 00:34:15 +00:00
for( unsigned i=0; i<asRenderers.size(); i++ )
2003-05-26 23:33:37 +00:00
{
2003-05-31 21:34:07 +00:00
CString sRenderer = asRenderers[i];
if( sRenderer.CompareNoCase("opengl")==0 )
{
2003-07-23 21:20:12 +00:00
#if defined(SUPPORT_OPENGL)
2003-05-31 21:34:07 +00:00
error += "Initializing OpenGL...\n";
try {
return new RageDisplay_OGL( params, PREFSMAN->m_bAllowUnacceleratedRenderer );
} catch(RageException e) {
error += CString(e.what()) + "\n";
continue;
};
2003-06-05 19:29:27 +00:00
#endif
2003-05-31 21:34:07 +00:00
}
else if( sRenderer.CompareNoCase("d3d")==0 )
{
2003-07-23 21:20:12 +00:00
#if defined(SUPPORT_D3D)
2003-05-31 21:34:07 +00:00
error += "Initializing Direct3D...\n";
try {
2003-06-05 19:29:27 +00:00
return new RageDisplay_D3D( params );
2003-07-23 20:19:22 +00:00
} catch( const exception &e ) {
error += CString(e.what()) + "\n";
2003-05-31 21:34:07 +00:00
};
2003-06-05 19:29:27 +00:00
#endif
2003-05-31 21:34:07 +00:00
}
else
RageException::Throw("Unknown video renderer value: %s", sRenderer.c_str() );
2003-05-26 23:33:37 +00:00
}
2003-05-28 05:58:24 +00:00
RageException::Throw( error );
}
2003-09-21 07:00:01 +00:00
static void CheckSDLVersion( int major, int minor, int patch )
{
const SDL_version *ver = SDL_Linked_Version();
if( ver->major > major ||
(ver->major == major && ver->minor > minor) ||
(ver->major == major && ver->minor == minor && ver->patch >= patch))
return;
RageException::Throw( "SDL %i.%i.%i is required, but you only appear to "
"have SDL %i.%i.%i installed. Please upgrade your installation of SDL or download "
"it from:\n\n\thttp://www.libsdl.org/",
major, minor, patch, ver->major, ver->minor, ver->patch );
}
2003-05-26 23:33:37 +00:00
static void RestoreAppPri()
{
if(!ChangeAppPri())
return;
#ifdef _WINDOWS
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
2003-02-15 00:11:12 +00:00
#endif
}
#define GAMEPREFS_INI_PATH BASE_PATH "Data" SLASH "GamePrefs.ini"
2003-10-08 08:06:42 +00:00
#define STATIC_INI_PATH BASE_PATH "Data" SLASH "Static.ini"
void ChangeCurrentGame( Game g )
{
SaveGamePrefsToDisk();
INPUTMAPPER->SaveMappingsToDisk(); // save mappings before switching the game
GAMESTATE->m_CurGame = g;
ReadGamePrefsFromDisk( false );
INPUTMAPPER->ReadMappingsFromDisk();
/* Save the newly-selected game. */
SaveGamePrefsToDisk();
}
void ReadGamePrefsFromDisk( bool bSwitchToLastPlayedGame )
{
2003-10-10 03:17:08 +00:00
ASSERT( GAMESTATE );
ASSERT( ANNOUNCER );
ASSERT( THEME );
CString sGameName = GAMESTATE->GetCurrentGameDef()->m_szName;
IniFile ini;
ini.SetPath( GAMEPREFS_INI_PATH );
ini.ReadFile(); // it's OK if this fails
2003-10-08 08:06:42 +00:00
ini.SetPath( STATIC_INI_PATH );
ini.ReadFile(); // it's OK if this fails, too
CString sAnnouncer = sGameName, sTheme = sGameName, sNoteSkin = sGameName;
// if these calls fail, the three strings will keep the initial values set above.
ini.GetValue( sGameName, "Announcer", sAnnouncer );
ini.GetValue( sGameName, "Theme", sTheme );
ini.GetValue( sGameName, "DefaultModifiers", PREFSMAN->m_sDefaultModifiers );
// it's OK to call these functions with names that don't exist.
ANNOUNCER->SwitchAnnouncer( sAnnouncer );
THEME->SwitchThemeAndLanguage( sTheme, PREFSMAN->m_sLanguage );
// NOTESKIN->SwitchNoteSkin( sNoteSkin );
if( bSwitchToLastPlayedGame )
{
Game game;
if( ini.GetValue("Options", "Game", (int&)game) )
GAMESTATE->m_CurGame = game;
}
}
void SaveGamePrefsToDisk()
{
if( !GAMESTATE )
return;
CString sGameName = GAMESTATE->GetCurrentGameDef()->m_szName;
IniFile ini;
ini.SetPath( GAMEPREFS_INI_PATH );
ini.ReadFile(); // it's OK if this fails
ini.SetValue( sGameName, "Announcer", ANNOUNCER->GetCurAnnouncerName() );
ini.SetValue( sGameName, "Theme", THEME->GetCurThemeName() );
ini.SetValue( sGameName, "DefaultModifiers", PREFSMAN->m_sDefaultModifiers );
ini.SetValue( "Options", "Game", GAMESTATE->m_CurGame );
ini.WriteFile();
}
2003-07-22 07:47:27 +00:00
#define UNLOCKS_PATH BASE_PATH "Data" SLASH "Unlocks.dat"
int main(int argc, char* argv[])
{
2003-07-12 19:51:40 +00:00
g_argc = argc;
g_argv = argv;
/* Set up arch hooks first. This may set up crash handling. */
HOOKS = MakeArchHooks();
2003-08-05 01:28:11 +00:00
CString g_sErrorString = "";
#ifndef DEBUG
try{
#endif
2002-12-16 02:11:31 +00:00
ChangeToDirOfExecutable(argv[0]);
2001-11-03 10:52:42 +00:00
2003-01-27 01:05:19 +00:00
/* Set this up second. Do this early, since it's needed for RageException::Throw.
* Do it after ChangeToDirOfExecutable, so the log ends up in the right place. */
LOG = new RageLog();
2003-04-25 05:43:48 +00:00
2003-01-27 01:05:19 +00:00
/* Whew--we should be able to crash safely now! */
atexit(SDL_Quit); /* Clean up on exit */
2001-11-03 10:52:42 +00:00
2003-04-25 05:43:48 +00:00
/* Fire up the SDL, but don't actually start any subsystems.
* We use our own error handlers. */
2003-09-16 02:38:18 +00:00
SDL_Init( SDL_INIT_NOPARACHUTE );
2001-11-03 10:52:42 +00:00
2002-12-16 02:11:31 +00:00
LoadingWindow *loading_window = MakeLoadingWindow();
2003-09-05 06:50:47 +00:00
if( loading_window == NULL )
{
LOG->Trace("Couldn't open any loading windows.\n");
exit(1);
}
2003-04-06 00:33:35 +00:00
2002-12-16 02:11:31 +00:00
loading_window->Paint();
2003-02-02 18:18:51 +00:00
srand( time(NULL) ); // seed number generator
2003-01-24 22:28:23 +00:00
//
// Create game objects
//
2002-07-23 01:41:40 +00:00
GAMESTATE = new GameState;
2002-05-27 08:23:27 +00:00
PREFSMAN = new PrefsManager;
2003-07-18 08:05:39 +00:00
if( PREFSMAN->m_bShowLogWindow )
2003-07-18 08:05:39 +00:00
LOG->ShowConsole();
2003-09-21 07:00:01 +00:00
CheckSDLVersion( 1,2,6 );
2003-08-14 23:02:55 +00:00
/* This should be done after PREFSMAN is set up, so it can use HOOKS->MessageBoxOK,
* but before we do more complex things that might crash. */
HOOKS->DumpDebugInfo();
LOG->SetLogging( PREFSMAN->m_bLogging );
2003-10-02 03:11:35 +00:00
LOG->SetFlushing( PREFSMAN->m_bForceLogFlush );
2003-10-02 03:13:51 +00:00
LOG->SetTimestamping( PREFSMAN->m_bTimestamping );
CheckSettings();
2002-06-14 22:25:22 +00:00
GAMEMAN = new GameManager;
THEME = new ThemeManager;
ANNOUNCER = new AnnouncerManager;
/* Set up the theme and announcer. */
ReadGamePrefsFromDisk();
NOTESKIN = new NoteSkinManager;
SOUNDMAN = new RageSoundManager(PREFSMAN->m_sSoundDrivers);
SOUNDMAN->SetPrefs(PREFSMAN->m_fSoundVolume);
2003-07-26 22:47:55 +00:00
SOUND = new RageSounds;
PROFILEMAN = new ProfileManager;
INPUTFILTER = new InputFilter;
INPUTMAPPER = new InputMapper;
INPUTQUEUE = new InputQueue;
SONGINDEX = new SongCacheIndex;
2003-06-04 22:42:17 +00:00
BANNERCACHE = new BannerCache;
2003-06-11 00:23:24 +00:00
/* depends on SONGINDEX: */
2002-11-17 09:13:35 +00:00
SONGMAN = new SongManager( loading_window ); // this takes a long time to load
delete loading_window; // destroy this before init'ing Display
DISPLAY = CreateDisplay();
2003-04-18 23:55:20 +00:00
TEXTUREMAN = new RageTextureManager();
TEXTUREMAN->SetPrefs(
PREFSMAN->m_iTextureColorDepth,
2003-07-09 02:28:56 +00:00
PREFSMAN->m_iMovieColorDepth,
2003-04-18 23:55:20 +00:00
PREFSMAN->m_bDelayedTextureDelete,
PREFSMAN->m_iMaxTextureResolution );
2003-09-15 07:52:58 +00:00
StoreActualGraphicOptions( true );
/* Now that we've started DISPLAY, we can set up event masks. */
2003-05-26 01:48:48 +00:00
mySDL_EventState(SDL_QUIT, SDL_ENABLE);
mySDL_EventState(SDL_ACTIVEEVENT, SDL_ENABLE);
/* Grab the window manager specific information */
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if ( SDL_GetWMInfo(&info) < 0 )
2002-12-21 19:34:02 +00:00
RageException::Throw( "SDL_GetWMInfo failed" );
2002-12-03 00:35:34 +00:00
#ifdef _WINDOWS
2002-12-03 00:35:34 +00:00
g_hWndMain = info.window;
#endif
/* This initializes objects that change the SDL event mask, and has other
* dependencies on the SDL video subsystem, so it must be initialized after
* DISPLAY and setting the default SDL event mask. */
2003-04-25 05:43:48 +00:00
INPUTMAN = new RageInput;
2002-07-27 19:29:51 +00:00
2002-07-23 01:41:40 +00:00
// These things depend on the TextureManager, so do them after!
2002-05-28 20:01:22 +00:00
FONT = new FontManager;
SCREENMAN = new ScreenManager;
2001-11-03 10:52:42 +00:00
2003-01-18 04:27:33 +00:00
/* People may want to do something else while songs are loading, so do
* this after loading songs. */
BoostAppPri();
2003-01-19 04:44:22 +00:00
ResetGame();
/* Load the unlocks into memory */
2003-07-22 07:47:27 +00:00
GAMESTATE->m_pUnlockingSys->LoadFromDATFile( UNLOCKS_PATH );
/* Initialize which courses are ranking courses here. */
SONGMAN->UpdateRankingCourses();
/* Run the main loop. */
GameLoop();
PREFSMAN->SaveGlobalPrefsToDisk();
SaveGamePrefsToDisk();
2003-02-15 05:15:24 +00:00
#ifndef DEBUG
}
2003-08-13 17:45:47 +00:00
catch( const RageException &e )
{
g_sErrorString = e.what();
2003-09-22 23:37:33 +00:00
}
catch( const exception &e )
{
2003-08-13 17:45:47 +00:00
if( LOG )
LOG->Warn("Unhandled exception: \"%s\"", e.what() );
/* Re-throw, so we get an unhandled exception crash and get a backtrace (at least
* in Windows). */
throw;
}
#endif
2001-12-19 14:56:22 +00:00
SAFE_DELETE( SCREENMAN );
2003-07-14 06:23:15 +00:00
/* Delete INPUTMAN before the other INPUTFILTER handlers, or an input
* driver may try to send a message to INPUTFILTER after we delete it. */
SAFE_DELETE( INPUTMAN );
SAFE_DELETE( INPUTQUEUE );
SAFE_DELETE( INPUTMAPPER );
SAFE_DELETE( INPUTFILTER );
SAFE_DELETE( SONGMAN );
2003-06-04 22:42:17 +00:00
SAFE_DELETE( BANNERCACHE );
SAFE_DELETE( SONGINDEX );
SAFE_DELETE( PREFSMAN );
SAFE_DELETE( GAMESTATE );
SAFE_DELETE( GAMEMAN );
SAFE_DELETE( NOTESKIN );
SAFE_DELETE( THEME );
SAFE_DELETE( ANNOUNCER );
SAFE_DELETE( PROFILEMAN );
2003-07-26 22:47:55 +00:00
SAFE_DELETE( SOUND );
SAFE_DELETE( SOUNDMAN );
SAFE_DELETE( FONT );
SAFE_DELETE( TEXTUREMAN );
SAFE_DELETE( DISPLAY );
SAFE_DELETE( LOG );
if( g_sErrorString != "" )
2003-09-23 05:06:13 +00:00
HOOKS->MessageBoxError( g_sErrorString ); // throw up a pretty error dialog
SAFE_DELETE( HOOKS );
return 0;
}
2003-02-12 17:36:54 +00:00
/* Returns true if the key has been handled and should be discarded, false if
* the key should be sent on to screens. */
bool HandleGlobalInputs( DeviceInput DeviceI, InputEventType type, GameInput GameI, MenuInput MenuI, StyleInput StyleI )
2002-12-15 20:16:14 +00:00
{
2003-03-09 00:55:49 +00:00
/* None of the globals keys act on types other than FIRST_PRESS */
2003-03-09 03:28:34 +00:00
if( type != IET_FIRST_PRESS )
2003-03-09 00:55:49 +00:00
return false;
2003-02-12 17:36:54 +00:00
switch( MenuI.button )
2002-12-15 20:16:14 +00:00
{
2003-02-12 17:36:54 +00:00
case MENU_BUTTON_OPERATOR:
2003-02-12 17:36:54 +00:00
/* Global operator key, to get quick access to the options menu. Don't
* do this if we're on a "system menu", which includes the editor
* (to prevent quitting without storing changes). */
if( !GAMESTATE->m_bIsOnSystemMenu )
{
SCREENMAN->SystemMessage( "OPERATOR" );
SCREENMAN->SetNewScreen( "ScreenOptionsMenu" );
}
return true;
2003-02-12 17:36:54 +00:00
case MENU_BUTTON_COIN:
/* Handle a coin insertion. */
2003-03-09 00:55:49 +00:00
if( GAMESTATE->m_bEditing ) // no coins while editing
2003-02-19 05:46:09 +00:00
break;
2003-03-09 00:55:49 +00:00
GAMESTATE->m_iCoins++;
SCREENMAN->RefreshCreditsMessages();
2003-07-26 23:05:16 +00:00
SOUND->PlayOnce( THEME->GetPathToS("Common coin") );
2003-03-09 00:55:49 +00:00
return false; // Attract need to know because they go to TitleMenu on > 1 credit
2003-02-12 17:36:54 +00:00
}
2003-06-18 09:33:22 +00:00
#ifndef DARWIN
2003-02-12 17:36:54 +00:00
if(DeviceI == DeviceInput(DEVICE_KEYBOARD, SDLK_F4))
{
if( INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_RALT)) ||
INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_LALT)) )
2002-12-15 20:16:14 +00:00
{
2003-02-12 17:36:54 +00:00
// pressed Alt+F4
ExitGame();
2003-02-12 17:36:54 +00:00
return true;
2002-12-15 20:16:14 +00:00
}
2003-02-12 17:36:54 +00:00
}
2003-06-18 09:33:22 +00:00
#else
2003-07-09 06:36:00 +00:00
if(DeviceI == DeviceInput(DEVICE_KEYBOARD, SDLK_q))
{
if(INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, SDLK_RMETA)) ||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, SDLK_LMETA)))
{
// pressed CMD-Q
ExitGame();
return true;
}
}
2003-06-18 09:33:22 +00:00
#endif
2003-05-22 05:28:37 +00:00
2003-09-26 05:45:24 +00:00
/* The default Windows message handler will capture the desktop window upon
* pressing PrntScrn, or will capture the foregroud with focus upon pressing
* Alt+PrntScrn. Windows will do this whether or not we save a screenshot
* ourself by dumping the frame buffer. */
/* Pressing F13 on an Apple keyboard sends SDLK_PRINT.
* However, notebooks don't have F13. Use cmd-F12 then*/
2003-09-26 05:45:24 +00:00
// "if pressing PrintScreen and not pressing Alt"
if( (DeviceI == DeviceInput(DEVICE_KEYBOARD, SDLK_PRINT) &&
!INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_RALT)) &&
!INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_LALT))) ||
(DeviceI == DeviceInput(DEVICE_KEYBOARD, SDLK_F12) &&
(INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_RMETA)) ||
INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_LMETA)))))
2003-07-09 06:36:00 +00:00
{
2003-05-22 05:28:37 +00:00
// Save Screenshot.
2003-02-12 17:36:54 +00:00
CString sPath;
2003-09-14 00:40:48 +00:00
FlushDirCache();
if (!CreateDirectories("Screenshots"))
return true;
2003-06-18 09:33:22 +00:00
for( int i=0; i<10000; i++ )
2003-02-12 17:36:54 +00:00
{
sPath = ssprintf("Screenshots" SLASH "screen%04d.bmp",i);
2003-02-12 17:36:54 +00:00
if( !DoesFileExist(sPath) )
break;
}
2003-02-12 17:36:54 +00:00
DISPLAY->SaveScreenshot( sPath );
SCREENMAN->SystemMessage( "Saved screenshot: " + sPath );
return true;
}
2002-12-15 20:16:14 +00:00
2003-02-12 17:36:54 +00:00
if(DeviceI == DeviceInput(DEVICE_KEYBOARD, SDLK_RETURN))
{
if( INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, SDLK_RALT)) ||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, SDLK_LALT)) )
2002-12-15 20:16:14 +00:00
{
2003-02-12 17:36:54 +00:00
/* alt-enter */
PREFSMAN->m_bWindowed = !PREFSMAN->m_bWindowed;
ApplyGraphicOptions();
return true;
2002-12-15 20:16:14 +00:00
}
2003-02-12 17:36:54 +00:00
}
return false;
}
2002-12-15 20:16:14 +00:00
2003-02-12 17:36:54 +00:00
static void HandleInputEvents(float fDeltaTime)
{
INPUTFILTER->Update( fDeltaTime );
2003-02-12 17:36:54 +00:00
static InputEventArray ieArray;
ieArray.clear(); // empty the array
INPUTFILTER->GetInputEvents( ieArray );
2003-07-14 20:09:50 +00:00
/* If we don't have focus, discard input. */
if( !g_bHasFocus )
return;
2003-02-12 17:36:54 +00:00
for( unsigned i=0; i<ieArray.size(); i++ )
{
DeviceInput DeviceI = (DeviceInput)ieArray[i];
InputEventType type = ieArray[i].type;
2002-12-15 20:16:14 +00:00
GameInput GameI;
MenuInput MenuI;
StyleInput StyleI;
INPUTMAPPER->DeviceToGame( DeviceI, GameI );
if( GameI.IsValid() && type == IET_FIRST_PRESS )
INPUTQUEUE->RememberInput( GameI );
if( GameI.IsValid() )
{
INPUTMAPPER->GameToMenu( GameI, MenuI );
INPUTMAPPER->GameToStyle( GameI, StyleI );
}
2003-02-12 17:36:54 +00:00
// HACK: Numlock is read is being pressed if the NumLock light is on.
// Filter out all NumLock repeat messages
if( DeviceI.device == DEVICE_KEYBOARD && DeviceI.button == SDLK_NUMLOCK && type != IET_FIRST_PRESS )
continue; // skip
if( HandleGlobalInputs(DeviceI, type, GameI, MenuI, StyleI ) )
continue; // skip
2002-12-15 20:16:14 +00:00
SCREENMAN->Input( DeviceI, type, GameI, MenuI, StyleI );
}
}
static void HandleSDLEvents()
{
// process all queued events
SDL_Event event;
2003-04-12 19:03:02 +00:00
while(SDL_GetEvent(event, SDL_QUITMASK|SDL_ACTIVEEVENTMASK))
{
switch(event.type)
{
case SDL_QUIT:
LOG->Trace("SDL_QUIT: shutting down");
ExitGame();
break;
case SDL_ACTIVEEVENT:
{
/* We don't care about mouse focus. */
if(event.active.state == SDL_APPMOUSEFOCUS)
break;
Uint8 i = SDL_GetAppState();
g_bHasFocus = i&SDL_APPINPUTFOCUS && i&SDL_APPACTIVE;
LOG->Trace("App %s focus (%i%i)", g_bHasFocus? "has":"doesn't have",
i&SDL_APPINPUTFOCUS, i&SDL_APPACTIVE);
if(g_bHasFocus)
BoostAppPri();
else
{
RestoreAppPri();
/* If we lose focus, we may lose input events, especially key
* releases. */
INPUTFILTER->Reset();
}
}
}
}
}
static void GameLoop()
{
RageTimer timer;
while(!g_bQuitting)
{
/* This needs to be called before anything that handles SDL events. */
SDL_PumpEvents();
HandleSDLEvents();
2001-12-19 01:50:57 +00:00
2002-11-01 20:01:59 +00:00
/*
* Update
*/
2002-12-19 23:13:52 +00:00
float fDeltaTime = timer.GetDeltaTime();
if( INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_TAB) ) ) {
if( INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_BACKQUOTE) ) )
2002-11-11 08:52:07 +00:00
fDeltaTime = 0; /* both; stop time */
else
fDeltaTime *= 4;
2003-08-07 06:09:14 +00:00
}
else if( INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, SDLK_BACKQUOTE) ) )
{
fDeltaTime /= 4;
}
2001-11-03 10:52:42 +00:00
DISPLAY->Update( fDeltaTime );
TEXTUREMAN->Update( fDeltaTime );
GAMESTATE->Update( fDeltaTime );
SCREENMAN->Update( fDeltaTime );
SOUNDMAN->Update( fDeltaTime );
2001-11-03 10:52:42 +00:00
/* Important: Process input AFTER updating game logic, or input will be acting on song beat from last frame */
HandleInputEvents( fDeltaTime );
HOOKS->Update( fDeltaTime );
/*
* Render
*/
SCREENMAN->Draw();
2002-09-18 20:42:33 +00:00
/* If we don't have focus, give up lots of CPU. */
if( !g_bHasFocus )
SDL_Delay( 10 );// give some time to other processes and threads
#if defined(_WINDOWS)
/* In Windows, we want to give up some CPU for other threads. Most OS's do
* this more intelligently. */
else
SDL_Delay( 1 ); // give some time to other processes and threads
2003-07-28 10:39:15 +00:00
#endif
}
2003-02-15 00:11:46 +00:00
}