#include "global.h" #include "ScreenDebugOverlay.h" #include "ScreenDimensions.h" #include "ScreenManager.h" #include "PrefsManager.h" #include "GamePreferences.h" #include "RageLog.h" #include "GameState.h" #include "PlayerState.h" #include "StepMania.h" #include "GameCommand.h" #include "ScreenGameplay.h" #include "RageSoundManager.h" #include "GameSoundManager.h" #include "RageTextureManager.h" #include "MemoryCardManager.h" #include "NoteSkinManager.h" #include "ProfileManager.h" #include "CodeDetector.h" #include "RageInput.h" #include "RageDisplay.h" #include "InputEventPlus.h" #include "LocalizedString.h" #include "Profile.h" #include "SongManager.h" #include "GameLoop.h" #include "Song.h" #include "ScreenSyncOverlay.h" #include "ThemeMetric.h" static bool g_bIsDisplayed = false; static bool g_bIsSlow = false; static bool g_bIsHalt = false; static RageTimer g_HaltTimer(RageZeroTimer); static float g_fImageScaleCurrent = 1; static float g_fImageScaleDestination = 1; // DebugLine theming static const ThemeMetric BACKGROUND_COLOR ("ScreenDebugOverlay", "BackgroundColor"); static const ThemeMetric LINE_ON_COLOR ("ScreenDebugOverlay", "LineOnColor"); static const ThemeMetric LINE_OFF_COLOR ("ScreenDebugOverlay", "LineOffColor"); static const ThemeMetric LINE_START_Y ("ScreenDebugOverlay", "LineStartY"); static const ThemeMetric LINE_SPACING ("ScreenDebugOverlay", "LineSpacing"); static const ThemeMetric LINE_BUTTON_X ("ScreenDebugOverlay", "LineButtonX"); static const ThemeMetric LINE_FUNCTION_X ("ScreenDebugOverlay", "LineFunctionX"); static const ThemeMetric PAGE_START_X ("ScreenDebugOverlay", "PageStartX"); static const ThemeMetric PAGE_SPACING_X ("ScreenDebugOverlay", "PageSpacingX"); // self-registering debug lines // We don't use SubscriptionManager, because we want to keep the line order. static LocalizedString ON ( "ScreenDebugOverlay", "on" ); static LocalizedString OFF ( "ScreenDebugOverlay", "off" ); class IDebugLine; static vector *g_pvpSubscribers = NULL; class IDebugLine { public: IDebugLine() { if( g_pvpSubscribers == nullptr ) g_pvpSubscribers = new vector; g_pvpSubscribers->push_back( this ); } virtual ~IDebugLine() { } enum Type { all_screens, gameplay_only }; virtual Type GetType() const { return all_screens; } virtual RString GetDisplayTitle() = 0; virtual RString GetDisplayValue() { return IsEnabled() ? ON.GetValue():OFF.GetValue(); } virtual RString GetPageName() const { return "Main"; } virtual bool ForceOffAfterUse() const { return false; } virtual bool IsEnabled() = 0; virtual void DoAndLog( RString &sMessageOut ) { RString s1 = GetDisplayTitle(); RString s2 = GetDisplayValue(); if( !s2.empty() ) s1 += " - "; sMessageOut = s1 + s2; }; DeviceInput m_Button; }; static bool IsGameplay() { return SCREENMAN && SCREENMAN->GetTopScreen() && SCREENMAN->GetTopScreen()->GetScreenType() == gameplay; } REGISTER_SCREEN_CLASS( ScreenDebugOverlay ); ScreenDebugOverlay::~ScreenDebugOverlay() { this->RemoveAllChildren(); for (BitmapText *p : m_vptextPages) { SAFE_DELETE(p); } for (BitmapText *p : m_vptextButton) { SAFE_DELETE(p); } m_vptextButton.clear(); for (BitmapText *p : m_vptextFunction) { SAFE_DELETE(p); } m_vptextFunction.clear(); } const int MAX_DEBUG_LINES = 30; struct MapDebugToDI { DeviceInput holdForDebug1; DeviceInput holdForDebug2; DeviceInput holdForSlow; DeviceInput holdForFast; DeviceInput debugButton[MAX_DEBUG_LINES]; DeviceInput gameplayButton[MAX_DEBUG_LINES]; map pageButton; void Clear() { holdForDebug1.MakeInvalid(); holdForDebug2.MakeInvalid(); holdForSlow.MakeInvalid(); holdForFast.MakeInvalid(); for( int i=0; iGetDeviceSpecificInputString(pLine->m_Button); IDebugLine::Type type = pLine->GetType(); switch( type ) { case IDebugLine::all_screens: return s; case IDebugLine::gameplay_only: return ssprintf( IN_GAMEPLAY.GetValue(), s.c_str() ); default: FAIL_M(ssprintf("Invalid debug line type: %i", type)); } } template static bool GetKeyFromMap( const map &m, const V &val, U &key ) { for( typename map::const_iterator iter = m.begin(); iter != m.end(); ++iter ) { if( iter->second == val ) { key = iter->first; return true; } } return false; } static LocalizedString DEBUG_MENU( "ScreenDebugOverlay", "Debug Menu" ); void ScreenDebugOverlay::Init() { Screen::Init(); // Init debug mappings // TODO: Arch-specific? { g_Mappings.Clear(); g_Mappings.holdForDebug1 = DeviceInput(DEVICE_KEYBOARD, KEY_F3); g_Mappings.holdForDebug2.MakeInvalid(); g_Mappings.holdForSlow = DeviceInput(DEVICE_KEYBOARD, KEY_ACCENT); g_Mappings.holdForFast = DeviceInput(DEVICE_KEYBOARD, KEY_TAB); /* TODO: Find a better way of indicating which option is which here. * Maybe we should take a page from ScreenEdit's menus and make * RowDefs()? */ int i=0; g_Mappings.gameplayButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_F8); g_Mappings.gameplayButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_F7); g_Mappings.gameplayButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_F6); i=0; g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C1); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C2); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C3); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C4); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C5); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C6); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C7); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C8); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C9); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_C0); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Cq); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Cw); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Ce); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Ct); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Cy); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Cu); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Ci); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Co); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); g_Mappings.debugButton[i++] = DeviceInput(DEVICE_KEYBOARD, KEY_BACK); g_Mappings.pageButton[DeviceInput(DEVICE_KEYBOARD, KEY_F5)] = 0; g_Mappings.pageButton[DeviceInput(DEVICE_KEYBOARD, KEY_F6)] = 1; g_Mappings.pageButton[DeviceInput(DEVICE_KEYBOARD, KEY_F7)] = 2; g_Mappings.pageButton[DeviceInput(DEVICE_KEYBOARD, KEY_F8)] = 3; } map iNextDebugButton; int iNextGameplayButton = 0; for (IDebugLine *p : *g_pvpSubscribers) { RString sPageName = p->GetPageName(); DeviceInput di; switch( p->GetType() ) { case IDebugLine::all_screens: di = g_Mappings.debugButton[iNextDebugButton[sPageName]++]; break; case IDebugLine::gameplay_only: di = g_Mappings.gameplayButton[iNextGameplayButton++]; break; } p->m_Button = di; if( find(m_asPages.begin(), m_asPages.end(), sPageName) == m_asPages.end() ) m_asPages.push_back( sPageName ); } m_iCurrentPage = 0; m_bForcedHidden = false; m_Quad.StretchTo( RectF( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ) ); m_Quad.SetDiffuse( BACKGROUND_COLOR ); this->AddChild( &m_Quad ); // if you're going to add user commands, make sure to have the overrides // set after parsing the metrics. -aj m_textHeader.SetName( "HeaderText" ); m_textHeader.LoadFromFont( THEME->GetPathF("ScreenDebugOverlay", "header") ); LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textHeader ); m_textHeader.SetText( DEBUG_MENU ); this->AddChild( &m_textHeader ); auto start = m_asPages.begin(); for (vector::const_iterator s = m_asPages.begin(); s != m_asPages.end(); ++s) { int iPage = s - start; DeviceInput di; bool b = GetKeyFromMap( g_Mappings.pageButton, iPage, di ); ASSERT( b ); RString sButton = INPUTMAN->GetDeviceSpecificInputString( di ); BitmapText *p = new BitmapText; p->SetName( "PageText" ); p->LoadFromFont( THEME->GetPathF("ScreenDebugOverlay", "page") ); LOAD_ALL_COMMANDS_AND_ON_COMMAND( p ); // todo: Y value is still hardcoded. -aj p->SetXY( PAGE_START_X+iPage*PAGE_SPACING_X, SCREEN_TOP+20 ); p->SetText( *s + " (" + sButton + ")" ); m_vptextPages.push_back( p ); this->AddChild( p ); } for (auto p = g_pvpSubscribers->begin(); p != g_pvpSubscribers->end(); ++p) { { BitmapText *bt = new BitmapText; bt->SetName( "ButtonText" ); bt->LoadFromFont( THEME->GetPathF("ScreenDebugOverlay", "line") ); bt->SetHorizAlign( align_right ); bt->SetText( "blah" ); LOAD_ALL_COMMANDS_AND_ON_COMMAND( *bt ); m_vptextButton.push_back( bt ); this->AddChild( bt ); } { BitmapText *bt = new BitmapText; bt->SetName( "FunctionText" ); bt->LoadFromFont( THEME->GetPathF("ScreenDebugOverlay", "line") ); bt->SetHorizAlign( align_left ); bt->SetText( "blah" ); LOAD_ALL_COMMANDS_AND_ON_COMMAND( *bt ); m_vptextFunction.push_back( bt ); this->AddChild( bt ); } } this->SetVisible( false ); } void ScreenDebugOverlay::Update( float fDeltaTime ) { { float fRate = 1; if( INPUTFILTER->IsBeingPressed(g_Mappings.holdForFast) ) { if( INPUTFILTER->IsBeingPressed(g_Mappings.holdForSlow) ) fRate = 0; // both; stop time else fRate *= 4; } else if( INPUTFILTER->IsBeingPressed(g_Mappings.holdForSlow) ) { fRate /= 4; } if( g_bIsHalt ) fRate = 0; else if( g_bIsSlow ) fRate /= 4; GameLoop::SetUpdateRate( fRate ); } bool bCenteringNeedsUpdate = g_fImageScaleCurrent != g_fImageScaleDestination; fapproach( g_fImageScaleCurrent, g_fImageScaleDestination, fDeltaTime ); if( bCenteringNeedsUpdate ) { DISPLAY->ChangeCentering( PREFSMAN->m_iCenterImageTranslateX, PREFSMAN->m_iCenterImageTranslateY, PREFSMAN->m_fCenterImageAddWidth - (int)SCREEN_WIDTH + (int)(g_fImageScaleCurrent*SCREEN_WIDTH), PREFSMAN->m_fCenterImageAddHeight - (int)SCREEN_HEIGHT + (int)(g_fImageScaleCurrent*SCREEN_HEIGHT) ); } Screen::Update(fDeltaTime); this->SetVisible( g_bIsDisplayed && !m_bForcedHidden ); if( !g_bIsDisplayed ) return; UpdateText(); } void ScreenDebugOverlay::UpdateText() { auto start = m_asPages.begin(); for (vector::const_iterator s = m_asPages.begin(); s != m_asPages.end(); ++s) { int iPage = s - start; m_vptextPages[iPage]->PlayCommand( (iPage == m_iCurrentPage) ? "GainFocus" : "LoseFocus" ); } // todo: allow changing of various spacing/location things -aj int iOffset = 0; auto subStart = g_pvpSubscribers->begin(); for (vector::const_iterator p = subStart; p != g_pvpSubscribers->end(); ++p) { RString sPageName = (*p)->GetPageName(); int i = p - subStart; float fY = LINE_START_Y + iOffset * LINE_SPACING; BitmapText &txt1 = *m_vptextButton[i]; BitmapText &txt2 = *m_vptextFunction[i]; if( sPageName != GetCurrentPageName() ) { txt1.SetVisible( false ); txt2.SetVisible( false ); continue; } txt1.SetVisible( true ); txt2.SetVisible( true ); ++iOffset; txt1.SetX( LINE_BUTTON_X ); txt1.SetY( fY ); txt2.SetX( LINE_FUNCTION_X ); txt2.SetY( fY ); RString s1 = (*p)->GetDisplayTitle(); RString s2 = (*p)->GetDisplayValue(); bool bOn = (*p)->IsEnabled(); txt1.SetDiffuse( bOn ? LINE_ON_COLOR:LINE_OFF_COLOR ); txt2.SetDiffuse( bOn ? LINE_ON_COLOR:LINE_OFF_COLOR ); RString sButton = GetDebugButtonName( *p ); if( !sButton.empty() ) sButton += ": "; txt1.SetText( sButton ); if( !s2.empty() ) s1 += " - "; txt2.SetText( s1 + s2 ); } if( g_bIsHalt ) { /* More than once I've paused the game accidentally and wasted time * figuring out why, so warn. */ if( g_HaltTimer.Ago() >= 5.0f ) { g_HaltTimer.Touch(); LOG->Warn( "Game halted" ); } } } template static bool GetValueFromMap( const map &m, const U &key, V &val ) { typename map::const_iterator it = m.find(key); if( it == m.end() ) return false; val = it->second; return true; } bool ScreenDebugOverlay::Input( const InputEventPlus &input ) { if( input.DeviceI == g_Mappings.holdForDebug1 || input.DeviceI == g_Mappings.holdForDebug2 ) { bool bHoldingNeither = (!g_Mappings.holdForDebug1.IsValid() || !INPUTFILTER->IsBeingPressed(g_Mappings.holdForDebug1)) && (!g_Mappings.holdForDebug2.IsValid() || !INPUTFILTER->IsBeingPressed(g_Mappings.holdForDebug2)); bool bHoldingBoth = (!g_Mappings.holdForDebug1.IsValid() || INPUTFILTER->IsBeingPressed(g_Mappings.holdForDebug1)) && (!g_Mappings.holdForDebug2.IsValid() || INPUTFILTER->IsBeingPressed(g_Mappings.holdForDebug2)); if( bHoldingNeither ) m_bForcedHidden = false; if( bHoldingBoth ) g_bIsDisplayed = true; else g_bIsDisplayed = false; } int iPage = 0; if( g_bIsDisplayed && GetValueFromMap(g_Mappings.pageButton, input.DeviceI, iPage) ) { if( input.type != IET_FIRST_PRESS ) return true; // eat the input but do nothing m_iCurrentPage = iPage; CLAMP( m_iCurrentPage, 0, (int) m_asPages.size()-1 ); return true; } auto start = g_pvpSubscribers->begin(); for (vector::const_iterator p = start; p != g_pvpSubscribers->end(); ++p) { RString sPageName = (*p)->GetPageName(); int i = p - start; // Gameplay buttons are available only in gameplay. Non-gameplay buttons // are only available when the screen is displayed. IDebugLine::Type type = (*p)->GetType(); switch( type ) { case IDebugLine::all_screens: if( !g_bIsDisplayed ) continue; if( sPageName != GetCurrentPageName() ) continue; break; case IDebugLine::gameplay_only: if( !IsGameplay() ) continue; break; default: FAIL_M(ssprintf("Invalid debug line type: %i", type)); } if( input.DeviceI == (*p)->m_Button ) { if( input.type != IET_FIRST_PRESS ) return true; // eat the input but do nothing // do the action RString sMessage; (*p)->DoAndLog( sMessage ); if( !sMessage.empty() ) LOG->Trace("DEBUG: %s", sMessage.c_str() ); if( (*p)->ForceOffAfterUse() ) m_bForcedHidden = true; // update text to show the effect of what changed above UpdateText(); // show what changed BitmapText &bt = *m_vptextButton[i]; bt.FinishTweening(); bt.PlayCommand("Toggled"); return true; } } return Screen::Input(input); } // DebugLine helpers static void SetSpeed() { // PauseMusic( g_bIsHalt ); } void ChangeVolume( float fDelta ) { Preference *pRet = Preference::GetPreferenceByName("SoundVolume"); float fVol = pRet->Get(); fVol += fDelta; CLAMP( fVol, 0.0f, 1.0f ); pRet->Set( fVol ); SOUNDMAN->SetMixVolume(); } void ChangeVisualDelay( float fDelta ) { Preference *pRet = Preference::GetPreferenceByName("VisualDelaySeconds"); float fSecs = pRet->Get(); fSecs += fDelta; CLAMP( fSecs, -1.0f, 1.0f ); pRet->Set( fSecs ); } // DebugLines static LocalizedString AUTO_PLAY ( "ScreenDebugOverlay", "AutoPlay" ); static LocalizedString ASSIST ( "ScreenDebugOverlay", "Assist" ); static LocalizedString AUTOSYNC ( "ScreenDebugOverlay", "Autosync" ); static LocalizedString COIN_MODE ( "ScreenDebugOverlay", "CoinMode" ); static LocalizedString HALT ( "ScreenDebugOverlay", "Halt" ); static LocalizedString LIGHTS_DEBUG ( "ScreenDebugOverlay", "Lights Debug" ); static LocalizedString MONKEY_INPUT ( "ScreenDebugOverlay", "Monkey Input" ); static LocalizedString RENDERING_STATS ( "ScreenDebugOverlay", "Rendering Stats" ); static LocalizedString VSYNC ( "ScreenDebugOverlay", "Vsync" ); static LocalizedString MULTITEXTURE ( "ScreenDebugOverlay", "Multitexture" ); static LocalizedString SCREEN_TEST_MODE ( "ScreenDebugOverlay", "Screen Test Mode" ); static LocalizedString SCREEN_SHOW_MASKS ( "ScreenDebugOverlay", "Show Masks" ); static LocalizedString PROFILE ( "ScreenDebugOverlay", "Profile" ); static LocalizedString CLEAR_PROFILE_STATS ( "ScreenDebugOverlay", "Clear Profile Stats" ); static LocalizedString FILL_PROFILE_STATS ( "ScreenDebugOverlay", "Fill Profile Stats" ); static LocalizedString SEND_NOTES_ENDED ( "ScreenDebugOverlay", "Send Notes Ended" ); static LocalizedString RELOAD ( "ScreenDebugOverlay", "Reload" ); static LocalizedString RESTART ( "ScreenDebugOverlay", "Restart" ); static LocalizedString SCREEN_ON ( "ScreenDebugOverlay", "Send On To Screen" ); static LocalizedString SCREEN_OFF ( "ScreenDebugOverlay", "Send Off To Screen" ); static LocalizedString RELOAD_THEME_AND_TEXTURES( "ScreenDebugOverlay", "Reload Theme and Textures" ); static LocalizedString WRITE_PROFILES ( "ScreenDebugOverlay", "Write Profiles" ); static LocalizedString WRITE_PREFERENCES ( "ScreenDebugOverlay", "Write Preferences" ); static LocalizedString MENU_TIMER ( "ScreenDebugOverlay", "Menu Timer" ); static LocalizedString FLUSH_LOG ( "ScreenDebugOverlay", "Flush Log" ); static LocalizedString PULL_BACK_CAMERA ( "ScreenDebugOverlay", "Pull Back Camera" ); static LocalizedString VISUAL_DELAY_UP ( "ScreenDebugOverlay", "Visual Delay Up" ); static LocalizedString VISUAL_DELAY_DOWN ( "ScreenDebugOverlay", "Visual Delay Down" ); static LocalizedString VOLUME_UP ( "ScreenDebugOverlay", "Volume Up" ); static LocalizedString VOLUME_DOWN ( "ScreenDebugOverlay", "Volume Down" ); static LocalizedString UPTIME ( "ScreenDebugOverlay", "Uptime" ); static LocalizedString FORCE_CRASH ( "ScreenDebugOverlay", "Force Crash" ); static LocalizedString SLOW ( "ScreenDebugOverlay", "Slow" ); static LocalizedString CPU ( "ScreenDebugOverlay", "CPU" ); static LocalizedString SONG ( "ScreenDebugOverlay", "Song" ); static LocalizedString MACHINE ( "ScreenDebugOverlay", "Machine" ); static LocalizedString SYNC_TEMPO ( "ScreenDebugOverlay", "Tempo" ); class DebugLineAutoplay : public IDebugLine { virtual RString GetDisplayTitle() { return AUTO_PLAY.GetValue() + " (+Shift = AI) (+Alt = hide)"; } virtual RString GetDisplayValue() { PlayerController pc = GamePreferences::m_AutoPlay.Get(); switch( pc ) { case PC_HUMAN: return OFF.GetValue(); break; case PC_AUTOPLAY: return ON.GetValue(); break; case PC_CPU: return CPU.GetValue(); break; default: FAIL_M(ssprintf("Invalid PlayerController: %i", pc)); } } virtual Type GetType() const { return IDebugLine::gameplay_only; } virtual bool IsEnabled() { return GamePreferences::m_AutoPlay.Get() != PC_HUMAN; } virtual void DoAndLog( RString &sMessageOut ) { ASSERT( GAMESTATE->GetMasterPlayerNumber() != PLAYER_INVALID ); PlayerController pc = GAMESTATE->m_pPlayerState[GAMESTATE->GetMasterPlayerNumber()]->m_PlayerController; bool bHoldingShift = INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT) ) || INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT) ); if( bHoldingShift ) pc = (pc==PC_CPU) ? PC_HUMAN : PC_CPU; else pc = (pc==PC_AUTOPLAY) ? PC_HUMAN : PC_AUTOPLAY; GamePreferences::m_AutoPlay.Set( pc ); FOREACH_HumanPlayer(p) GAMESTATE->m_pPlayerState[p]->m_PlayerController = GamePreferences::m_AutoPlay; FOREACH_MultiPlayer(p) GAMESTATE->m_pMultiPlayerState[p]->m_PlayerController = GamePreferences::m_AutoPlay; // Hide Autoplay if Alt is held down bool bHoldingAlt = INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, KEY_LALT) ) || INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, KEY_RALT) ); ScreenSyncOverlay::SetShowAutoplay( !bHoldingAlt ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineAssist : public IDebugLine { virtual RString GetDisplayTitle() { return ASSIST.GetValue(); } virtual Type GetType() const { return gameplay_only; } virtual RString GetDisplayValue() { SongOptions so; so.m_bAssistClap = GAMESTATE->m_SongOptions.GetSong().m_bAssistClap; so.m_bAssistMetronome = GAMESTATE->m_SongOptions.GetSong().m_bAssistMetronome; if( so.m_bAssistClap || so.m_bAssistMetronome ) return so.GetLocalizedString(); else return OFF.GetValue(); } virtual bool IsEnabled() { return GAMESTATE->m_SongOptions.GetSong().m_bAssistClap || GAMESTATE->m_SongOptions.GetSong().m_bAssistMetronome; } virtual void DoAndLog( RString &sMessageOut ) { ASSERT( GAMESTATE->GetMasterPlayerNumber() != PLAYER_INVALID ); bool bHoldingShift = INPUTFILTER->IsBeingPressed( DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT) ); bool b; if( bHoldingShift ) b = !GAMESTATE->m_SongOptions.GetSong().m_bAssistMetronome; else b = !GAMESTATE->m_SongOptions.GetSong().m_bAssistClap; if( bHoldingShift ) SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Preferred, m_bAssistMetronome, b ); else SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Preferred, m_bAssistClap, b ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineAutosync : public IDebugLine { virtual RString GetDisplayTitle() { return AUTOSYNC.GetValue(); } virtual RString GetDisplayValue() { SongOptions::AutosyncType type = GAMESTATE->m_SongOptions.GetSong().m_AutosyncType; switch( type ) { case SongOptions::AUTOSYNC_OFF: return OFF.GetValue(); break; case SongOptions::AUTOSYNC_SONG: return SONG.GetValue(); break; case SongOptions::AUTOSYNC_MACHINE: return MACHINE.GetValue(); break; case SongOptions::AUTOSYNC_TEMPO: return SYNC_TEMPO.GetValue(); break; default: FAIL_M(ssprintf("Invalid autosync type: %i", type)); } } virtual Type GetType() const { return IDebugLine::gameplay_only; } virtual bool IsEnabled() { return GAMESTATE->m_SongOptions.GetSong().m_AutosyncType!=SongOptions::AUTOSYNC_OFF; } virtual void DoAndLog( RString &sMessageOut ) { int as = GAMESTATE->m_SongOptions.GetSong().m_AutosyncType + 1; bool bAllowSongAutosync = !GAMESTATE->IsCourseMode(); if( !bAllowSongAutosync && ( as == SongOptions::AUTOSYNC_SONG || as == SongOptions::AUTOSYNC_TEMPO ) ) as = SongOptions::AUTOSYNC_MACHINE; wrap( as, SongOptions::NUM_AUTOSYNC_TYPES ); SO_GROUP_ASSIGN( GAMESTATE->m_SongOptions, ModsLevel_Song, m_AutosyncType, SongOptions::AutosyncType(as) ); MESSAGEMAN->Broadcast( Message_AutosyncChanged ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineCoinMode : public IDebugLine { virtual RString GetDisplayTitle() { return COIN_MODE.GetValue(); } virtual RString GetDisplayValue() { return CoinModeToString(GamePreferences::m_CoinMode); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { int cm = GamePreferences::m_CoinMode+1; wrap( cm, NUM_CoinMode ); GamePreferences::m_CoinMode.Set( CoinMode(cm) ); SCREENMAN->RefreshCreditsMessages(); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineSlow : public IDebugLine { virtual RString GetDisplayTitle() { return SLOW.GetValue(); } virtual bool IsEnabled() { return g_bIsSlow; } virtual void DoAndLog( RString &sMessageOut ) { g_bIsSlow = !g_bIsSlow; SetSpeed(); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineHalt : public IDebugLine { virtual RString GetDisplayTitle() { return HALT.GetValue(); } virtual bool IsEnabled() { return g_bIsHalt; } virtual void DoAndLog( RString &sMessageOut ) { g_bIsHalt = !g_bIsHalt; g_HaltTimer.Touch(); SetSpeed(); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineLightsDebug : public IDebugLine { virtual RString GetDisplayTitle() { return LIGHTS_DEBUG.GetValue(); } virtual bool IsEnabled() { return PREFSMAN->m_bDebugLights.Get(); } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->m_bDebugLights.Set( !PREFSMAN->m_bDebugLights ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineMonkeyInput : public IDebugLine { virtual RString GetDisplayTitle() { return MONKEY_INPUT.GetValue(); } virtual bool IsEnabled() { return PREFSMAN->m_bMonkeyInput.Get(); } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->m_bMonkeyInput.Set( !PREFSMAN->m_bMonkeyInput ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineStats : public IDebugLine { virtual RString GetDisplayTitle() { return RENDERING_STATS.GetValue(); } virtual bool IsEnabled() { return PREFSMAN->m_bShowStats.Get(); } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->m_bShowStats.Set( !PREFSMAN->m_bShowStats ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineVsync : public IDebugLine { virtual RString GetDisplayTitle() { return VSYNC.GetValue(); } virtual bool IsEnabled() { return PREFSMAN->m_bVsync.Get(); } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->m_bVsync.Set( !PREFSMAN->m_bVsync ); StepMania::ApplyGraphicOptions(); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineAllowMultitexture : public IDebugLine { virtual RString GetDisplayTitle() { return MULTITEXTURE.GetValue(); } virtual bool IsEnabled() { return PREFSMAN->m_bAllowMultitexture.Get(); } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->m_bAllowMultitexture.Set( !PREFSMAN->m_bAllowMultitexture ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineShowMasks : public IDebugLine { virtual RString GetDisplayTitle() { return SCREEN_SHOW_MASKS.GetValue(); } virtual bool IsEnabled() { return GetPref()->Get(); } virtual RString GetPageName() const { return "Theme"; } virtual void DoAndLog( RString &sMessageOut ) { GetPref()->Set( !GetPref()->Get() ); IDebugLine::DoAndLog( sMessageOut ); } Preference *GetPref() { return Preference::GetPreferenceByName("ShowMasks"); } }; static ProfileSlot g_ProfileSlot = ProfileSlot_Machine; static bool IsSelectProfilePersistent() { if( g_ProfileSlot == ProfileSlot_Machine ) return true; return PROFILEMAN->IsPersistentProfile( (PlayerNumber) g_ProfileSlot ); } class DebugLineProfileSlot : public IDebugLine { virtual RString GetDisplayTitle() { return PROFILE.GetValue(); } virtual RString GetDisplayValue() { switch( g_ProfileSlot ) { case ProfileSlot_Machine: return "Machine"; case ProfileSlot_Player1: return "Player 1"; case ProfileSlot_Player2: return "Player 2"; default: return RString(); } } virtual bool IsEnabled() { return IsSelectProfilePersistent(); } virtual RString GetPageName() const { return "Profiles"; } virtual void DoAndLog( RString &sMessageOut ) { enum_add( g_ProfileSlot, +1 ); if( g_ProfileSlot == NUM_ProfileSlot ) g_ProfileSlot = ProfileSlot_Player1; IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineClearProfileStats : public IDebugLine { virtual RString GetDisplayTitle() { return CLEAR_PROFILE_STATS.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return IsSelectProfilePersistent(); } virtual RString GetPageName() const { return "Profiles"; } virtual void DoAndLog( RString &sMessageOut ) { Profile *pProfile = PROFILEMAN->GetProfile( g_ProfileSlot ); pProfile->ClearStats(); IDebugLine::DoAndLog( sMessageOut ); } }; static HighScore MakeRandomHighScore( float fPercentDP ) { HighScore hs; hs.SetName( "FAKE" ); Grade g = (Grade)SCALE( RandomInt(6), 0, 4, Grade_Tier01, Grade_Tier06 ); if( g == Grade_Tier06 ) g = Grade_Failed; hs.SetGrade( g ); hs.SetScore( RandomInt(100*1000) ); hs.SetPercentDP( fPercentDP ); hs.SetAliveSeconds( randomf(30.0f, 100.0f) ); PlayerOptions po; po.ChooseRandomModifiers(); hs.SetModifiers( po.GetString() ); hs.SetDateTime( DateTime::GetNowDateTime() ); hs.SetPlayerGuid( Profile::MakeGuid() ); hs.SetMachineGuid( Profile::MakeGuid() ); hs.SetProductID( RandomInt(10) ); FOREACH_ENUM( TapNoteScore, tns ) hs.SetTapNoteScore( tns, RandomInt(100) ); FOREACH_ENUM( HoldNoteScore, hns ) hs.SetHoldNoteScore( hns, RandomInt(100) ); RadarValues rv; FOREACH_ENUM( RadarCategory, rc ) rv.m_Values.f[rc] = randomf( 0, 1 ); hs.SetRadarValues( rv ); return hs; } static void FillProfileStats( Profile *pProfile ) { pProfile->InitSongScores(); pProfile->InitCourseScores(); static int s_iCount = 0; // Choose a percent for all scores. This is useful for testing unlocks // where some elements are unlocked at a certain percent complete. float fPercentDP = s_iCount ? randomf( 0.6f, 1.0f ) : 1.0f; s_iCount = (s_iCount+1)%2; int iCount = pProfile->IsMachine()? PREFSMAN->m_iMaxHighScoresPerListForMachine.Get(): PREFSMAN->m_iMaxHighScoresPerListForPlayer.Get(); vector vpAllSongs = SONGMAN->GetAllSongs(); for (Song const *pSong : vpAllSongs) { vector vpAllSteps = pSong->GetAllSteps(); for (Steps const *pSteps : vpAllSteps) { if( rand() % 5 ) pProfile->IncrementStepsPlayCount( pSong, pSteps ); for( int i=0; iAddStepsHighScore( pSong, pSteps, MakeRandomHighScore(fPercentDP), iIndex ); } } } vector vpAllCourses; SONGMAN->GetAllCourses( vpAllCourses, true ); for (Course const *pCourse : vpAllCourses) { vector vpAllTrails; pCourse->GetAllTrails( vpAllTrails ); for (Trail const *pTrail : vpAllTrails) { if( rand() % 5 ) pProfile->IncrementCoursePlayCount( pCourse, pTrail ); for( int i=0; iAddCourseHighScore( pCourse, pTrail, MakeRandomHighScore(fPercentDP), iIndex ); } } } SCREENMAN->ZeroNextUpdate(); } class DebugLineFillProfileStats : public IDebugLine { virtual RString GetDisplayTitle() { return FILL_PROFILE_STATS.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return IsSelectProfilePersistent(); } virtual RString GetPageName() const { return "Profiles"; } virtual void DoAndLog( RString &sMessageOut ) { Profile* pProfile = PROFILEMAN->GetProfile( g_ProfileSlot ); FillProfileStats( pProfile ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineSendNotesEnded : public IDebugLine { virtual RString GetDisplayTitle() { return SEND_NOTES_ENDED.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { SCREENMAN->PostMessageToTopScreen( SM_NotesEnded, 0 ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineReloadCurrentScreen : public IDebugLine { virtual RString GetDisplayTitle() { return RELOAD.GetValue(); } virtual RString GetDisplayValue() { return SCREENMAN && SCREENMAN->GetTopScreen()? SCREENMAN->GetTopScreen()->GetName() : RString(); } virtual bool IsEnabled() { return true; } virtual RString GetPageName() const { return "Theme"; } virtual void DoAndLog( RString &sMessageOut ) { RString sScreenName = SCREENMAN->GetScreen(0)->GetName(); SCREENMAN->PopAllScreens(); SOUND->StopMusic(); //StepMania::ResetGame(); SCREENMAN->SetNewScreen( sScreenName ); IDebugLine::DoAndLog( sMessageOut ); sMessageOut = ""; } }; class DebugLineRestartCurrentScreen : public IDebugLine { virtual RString GetDisplayTitle() { return RESTART.GetValue(); } virtual RString GetDisplayValue() { return SCREENMAN && SCREENMAN->GetTopScreen()? SCREENMAN->GetTopScreen()->GetName() : RString(); } virtual bool IsEnabled() { return true; } virtual bool ForceOffAfterUse() const { return true; } virtual RString GetPageName() const { return "Theme"; } virtual void DoAndLog( RString &sMessageOut ) { SCREENMAN->GetTopScreen()->BeginScreen(); IDebugLine::DoAndLog( sMessageOut ); sMessageOut = ""; } }; class DebugLineCurrentScreenOn : public IDebugLine { virtual RString GetDisplayTitle() { return SCREEN_ON.GetValue(); } virtual RString GetDisplayValue() { return SCREENMAN && SCREENMAN->GetTopScreen()? SCREENMAN->GetTopScreen()->GetName() : RString(); } virtual bool IsEnabled() { return true; } virtual bool ForceOffAfterUse() const { return true; } virtual RString GetPageName() const { return "Theme"; } virtual void DoAndLog( RString &sMessageOut ) { SCREENMAN->GetTopScreen()->PlayCommand("On"); IDebugLine::DoAndLog( sMessageOut ); sMessageOut = ""; } }; class DebugLineCurrentScreenOff : public IDebugLine { virtual RString GetDisplayTitle() { return SCREEN_OFF.GetValue(); } virtual RString GetDisplayValue() { return SCREENMAN && SCREENMAN->GetTopScreen()? SCREENMAN->GetTopScreen()->GetName() : RString(); } virtual bool IsEnabled() { return true; } virtual bool ForceOffAfterUse() const { return true; } virtual RString GetPageName() const { return "Theme"; } virtual void DoAndLog( RString &sMessageOut ) { SCREENMAN->GetTopScreen()->PlayCommand("Off"); IDebugLine::DoAndLog( sMessageOut ); sMessageOut = ""; } }; class DebugLineReloadTheme : public IDebugLine { virtual RString GetDisplayTitle() { return RELOAD_THEME_AND_TEXTURES.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return true; } virtual RString GetPageName() const { return "Theme"; } virtual void DoAndLog( RString &sMessageOut ) { THEME->ReloadMetrics(); TEXTUREMAN->ReloadAll(); NOTESKIN->RefreshNoteSkinData( GAMESTATE->m_pCurGame ); CodeDetector::RefreshCacheItems(); // HACK: Don't update text below. Return immediately because this screen // was just destroyed as part of the theme reload. IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineWriteProfiles : public IDebugLine { virtual RString GetDisplayTitle() { return WRITE_PROFILES.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return IsSelectProfilePersistent(); } virtual RString GetPageName() const { return "Profiles"; } virtual void DoAndLog( RString &sMessageOut ) { // Also save profile info for debugging // so we don't have to play through a whole song to get new output. if( g_ProfileSlot == ProfileSlot_Machine ) GAMESTATE->SaveLocalData(); else { PlayerNumber pn = (PlayerNumber) g_ProfileSlot; GAMESTATE->SaveCurrentSettingsToProfile(pn); GAMESTATE->SavePlayerProfile( pn ); } IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineWritePreferences : public IDebugLine { virtual RString GetDisplayTitle() { return WRITE_PREFERENCES.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->SavePrefsToDisk(); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineMenuTimer : public IDebugLine { virtual RString GetDisplayTitle() { return MENU_TIMER.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return PREFSMAN->m_bMenuTimer.Get(); } virtual void DoAndLog( RString &sMessageOut ) { PREFSMAN->m_bMenuTimer.Set( !PREFSMAN->m_bMenuTimer ); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineFlushLog : public IDebugLine { virtual RString GetDisplayTitle() { return FLUSH_LOG.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { LOG->Flush(); IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLinePullBackCamera : public IDebugLine { virtual RString GetDisplayTitle() { return PULL_BACK_CAMERA.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return g_fImageScaleDestination != 1; } virtual void DoAndLog( RString &sMessageOut ) { if( g_fImageScaleDestination == 1 ) g_fImageScaleDestination = 0.5f; else g_fImageScaleDestination = 1; IDebugLine::DoAndLog( sMessageOut ); } }; class DebugLineVolumeUp : public IDebugLine { virtual RString GetDisplayTitle() { return VOLUME_UP.GetValue(); } virtual RString GetDisplayValue() { return ssprintf("%.0f%%", GetPref()->Get()*100); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { ChangeVolume( +0.1f ); IDebugLine::DoAndLog( sMessageOut ); } Preference *GetPref() { return Preference::GetPreferenceByName("SoundVolume"); } }; class DebugLineVolumeDown : public IDebugLine { virtual RString GetDisplayTitle() { return VOLUME_DOWN.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { ChangeVolume( -0.1f ); IDebugLine::DoAndLog( sMessageOut ); sMessageOut += " - " + ssprintf("%.0f%%",GetPref()->Get()*100); } Preference *GetPref() { return Preference::GetPreferenceByName("SoundVolume"); } }; class DebugLineVisualDelayUp : public IDebugLine { virtual RString GetDisplayTitle() { return VISUAL_DELAY_UP.GetValue(); } virtual RString GetDisplayValue() { return ssprintf("%.03f",GetPref()->Get()); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { ChangeVisualDelay( +0.001f ); IDebugLine::DoAndLog( sMessageOut ); } Preference *GetPref() { return Preference::GetPreferenceByName("VisualDelaySeconds"); } }; class DebugLineVisualDelayDown : public IDebugLine { virtual RString GetDisplayTitle() { return VISUAL_DELAY_DOWN.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return true; } virtual void DoAndLog( RString &sMessageOut ) { ChangeVisualDelay( -0.001f ); IDebugLine::DoAndLog( sMessageOut ); sMessageOut += " - " + ssprintf("%.03f",GetPref()->Get()); } Preference *GetPref() { return Preference::GetPreferenceByName("VisualDelaySeconds"); } }; class DebugLineForceCrash : public IDebugLine { virtual RString GetDisplayTitle() { return FORCE_CRASH.GetValue(); } virtual RString GetDisplayValue() { return RString(); } virtual bool IsEnabled() { return false; } virtual void DoAndLog( RString &sMessageOut ) { FAIL_M("DebugLineCrash"); } }; class DebugLineUptime : public IDebugLine { virtual RString GetDisplayTitle() { return UPTIME.GetValue(); } virtual RString GetDisplayValue() { return SecondsToMMSSMsMsMs(RageTimer::GetTimeSinceStart()); } virtual bool IsEnabled() { return false; } virtual void DoAndLog( RString &sMessageOut ) {} }; /* #ifdef out the lines below if you don't want them to appear on certain * platforms. This is easier than #ifdefing the whole DebugLine definitions * that can span pages. */ #define DECLARE_ONE( x ) static x g_##x DECLARE_ONE( DebugLineAutoplay ); DECLARE_ONE( DebugLineAssist ); DECLARE_ONE( DebugLineAutosync ); DECLARE_ONE( DebugLineCoinMode ); DECLARE_ONE( DebugLineSlow ); DECLARE_ONE( DebugLineHalt ); DECLARE_ONE( DebugLineLightsDebug ); DECLARE_ONE( DebugLineMonkeyInput ); DECLARE_ONE( DebugLineStats ); DECLARE_ONE( DebugLineVsync ); DECLARE_ONE( DebugLineAllowMultitexture ); DECLARE_ONE( DebugLineShowMasks ); DECLARE_ONE( DebugLineProfileSlot ); DECLARE_ONE( DebugLineClearProfileStats ); DECLARE_ONE( DebugLineFillProfileStats ); DECLARE_ONE( DebugLineSendNotesEnded ); DECLARE_ONE( DebugLineReloadCurrentScreen ); DECLARE_ONE( DebugLineRestartCurrentScreen ); DECLARE_ONE( DebugLineCurrentScreenOn ); DECLARE_ONE( DebugLineCurrentScreenOff ); DECLARE_ONE( DebugLineReloadTheme ); DECLARE_ONE( DebugLineWriteProfiles ); DECLARE_ONE( DebugLineWritePreferences ); DECLARE_ONE( DebugLineMenuTimer ); DECLARE_ONE( DebugLineFlushLog ); DECLARE_ONE( DebugLinePullBackCamera ); DECLARE_ONE( DebugLineVolumeDown ); DECLARE_ONE( DebugLineVolumeUp ); DECLARE_ONE( DebugLineVisualDelayDown ); DECLARE_ONE( DebugLineVisualDelayUp ); DECLARE_ONE( DebugLineForceCrash ); DECLARE_ONE( DebugLineUptime ); /* * (c) 2001-2005 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */