From 75861f22a19704d537205e00f99820b5e71f17ef Mon Sep 17 00:00:00 2001 From: AJ Kelly Date: Sun, 14 Feb 2010 23:02:34 -0600 Subject: [PATCH] various commits from SM4 SVN, courtesy of Glenn and Vyhd. --- src/ActorFrame.h | 4 +- src/ArrowEffects.h | 27 ++- src/Game.h | 33 ++- src/GameConstantsAndTypes.h | 1 + src/GameManager.cpp | 41 +++- src/GameState.cpp | 32 +-- src/Makefile.am | 2 + src/NoteDataUtil.cpp | 13 +- src/NoteDisplay.cpp | 19 +- src/NoteField.cpp | 11 + src/PlayerStageStats.cpp | 8 +- src/Profile.cpp | 4 +- src/RageFile.cpp | 6 + src/RageFile.h | 13 +- src/RageFileBasic.h | 8 +- src/RageFileDriverDeflate.h | 1 + src/RageFileDriverDirect.cpp | 6 + src/RageFileDriverReadAhead.cpp | 191 ++++++++++++++++++ src/RageFileDriverReadAhead.h | 69 +++++++ src/RageFileDriverSlice.h | 1 + src/RageFileDriverTimeout.cpp | 47 +++++ src/RageFileManager_ReadAhead.cpp | 174 ++++++++++++++++ src/RageFileManager_ReadAhead.h | 42 ++++ src/ScreenEdit.cpp | 11 +- src/ScreenSelectMusic.cpp | 14 +- src/StepMania-net2003.vcproj | 12 ++ src/StepMania-net2005.vcproj | 16 ++ src/StepMania-net2008.vcproj | 16 ++ src/StepMania.cpp | 2 - src/StepManiaXbox-2003.vcproj | 12 ++ src/StreamDisplay.cpp | 2 +- src/StreamDisplay.h | 4 +- src/Tween.cpp | 2 +- src/arch/MovieTexture/MovieTexture_FFMpeg.cpp | 34 +++- src/mad-0.15.1b/aclocal.m4 | 1 - src/mad-0.15.1b/ltmain.sh | 1 - 36 files changed, 760 insertions(+), 120 deletions(-) create mode 100644 src/RageFileDriverReadAhead.cpp create mode 100644 src/RageFileDriverReadAhead.h create mode 100644 src/RageFileManager_ReadAhead.cpp create mode 100644 src/RageFileManager_ReadAhead.h diff --git a/src/ActorFrame.h b/src/ActorFrame.h index b5f9010977..bd871e95a0 100644 --- a/src/ActorFrame.h +++ b/src/ActorFrame.h @@ -37,9 +37,7 @@ public: void DeleteChildrenWhenDone( bool bDelete=true ) { m_bDeleteChildren = bDelete; } void DeleteAllChildren(); - // // Commands - // virtual void PushSelf( lua_State *L ); void PushChildrenTable( lua_State *L ); void PlayCommandOnChildren( const RString &sCommandName, const LuaReference *pParamTable = NULL ); @@ -75,7 +73,7 @@ public: virtual void SetPropagateCommands( bool b ); - /* Amount of time until all tweens (and all children's tweens) have stopped: */ + // Amount of time until all tweens (and all children's tweens) have stopped: virtual float GetTweenTimeLeft() const; virtual void HandleMessage( const Message &msg ); diff --git a/src/ArrowEffects.h b/src/ArrowEffects.h index a7e580d192..c373e6224c 100644 --- a/src/ArrowEffects.h +++ b/src/ArrowEffects.h @@ -29,39 +29,38 @@ public: static float GetYOffsetFromYPos( const PlayerState* pPlayerState, int iCol, float YPos, float fYReverseOffsetPixels ); - // fRotation is Z rotation of an arrow. This will depend on the column of - // the arrow and possibly the Arrow effect and the fYOffset (in the case of - // EFFECT_DIZZY). + // fRotation is Z rotation of an arrow. This will depend on the column of + // the arrow and possibly the Arrow effect and the fYOffset (in the case of + // EFFECT_DIZZY). static float GetRotation( const PlayerState* pPlayerState, float fNoteBeat, bool bIsHoldHead ); static float ReceptorGetRotation( const PlayerState* pPlayerState ); - // fXPos is a horizontal position in pixels relative to the center of the field. - // This depends on the column of the arrow and possibly the Arrow effect and - // fYPos (in the case of EFFECT_DRUNK). + // fXPos is a horizontal position in pixels relative to the center of the field. + // This depends on the column of the arrow and possibly the Arrow effect and + // fYPos (in the case of EFFECT_DRUNK). static float GetXPos( const PlayerState* pPlayerState, int iCol, float fYOffset ); - // Shift a note's fXPos by this amount depending - // + // Shift a note's fXPos by this amount depending (on what? -aj) static float GetXOffset( const PlayerState* pPlayerState, float fMidiNote ); - // Z position; normally 0. Only visible in perspective modes. + // Z position; normally 0. Only visible in perspective modes. static float GetZPos( const PlayerState* pPlayerState, int iCol, float fYPos ); // Enable this if any ZPos effects are enabled. static bool NeedZBuffer( const PlayerState* pPlayerState ); - // fAlpha is the transparency of the arrow. It depends on fYPos and the - // AppearanceType. + // fAlpha is the transparency of the arrow. It depends on fYPos and the + // AppearanceType. static float GetAlpha( const PlayerState* pPlayerState, int iCol, float fYPos, float fPercentFadeToFail, float fYReverseOffsetPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar ); - // fAlpha is the transparency of the arrow. It depends on fYPos and the - // AppearanceType. + // fAlpha is the transparency of the arrow. It depends on fYPos and the + // AppearanceType. static float GetGlow( const PlayerState* pPlayerState, int iCol, float fYPos, float fPercentFadeToFail, float fYReverseOffsetPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar ); - // Depends on fYOffset. + // Depends on fYOffset. static float GetBrightness( const PlayerState* pPlayerState, float fNoteBeat ); // This is the zoom of the individual tracks, not of the whole Player. diff --git a/src/Game.h b/src/Game.h index fae7223d71..56ecc2dd45 100644 --- a/src/Game.h +++ b/src/Game.h @@ -9,24 +9,23 @@ struct lua_State; class Style; -// -// PrimaryMenuButton and SecondaryMenuButton are used to support using DeviceInputs that only -// navigate the menus. +// PrimaryMenuButton and SecondaryMenuButton are used to support using +// DeviceInputs that only navigate the menus. // -// A button being a primary menu button means that this GameButton will generate a the -// corresponding MenuInput IF AND ONLY IF the GameButton corresponding to the pimary input -// is not mapped. +// A button being a primary menu button means that this GameButton will generate +// a the corresponding MenuInput IF AND ONLY IF the GameButton corresponding to +// the pimary input is not mapped. // -// Example 1: A user is using an arcade machine as their controller. Most machines have -// MenuLeft, MenuStart, and MenuRight buttons on the cabinet, so they should be used to navigate menus. -// The user will map these DeviceInputs to the GameButtons "MenuLeft (optional)", "MenuStart", and -// "MenuRight (optional)". -// -// Example 2: A user is using PlayStation dance pads to play. These controllers don't have dedicated -// DeviceInputs for MenuLeft and MenuRight. The user maps Up, Down, Left, and Right as normal. -// Since the Left and Right GameButtons have the flag FLAG_SECONDARY_MENU_*, they will function as -// MenuLeft and MenuRight as long as "MenuLeft (optional)" and "MenuRight (optional)" are not mapped. +// Example 1: A user is using an arcade machine as their controller. Most +// machines have MenuLeft, MenuStart, and MenuRight buttons on the cabinet, so +// they should be used to navigate menus. The user will map these DeviceInputs +// to the GameButtons "MenuLeft (optional)", "MenuStart", and "MenuRight (optional)". // +// Example 2: A user is using PlayStation dance pads to play. These controllers +// don't have dedicated DeviceInputs for MenuLeft and MenuRight. The user maps +// Up, Down, Left, and Right as normal. Since the Left and Right GameButtons +// have the flag FLAG_SECONDARY_MENU_*, they will function as MenuLeft and +// MenuRight as long as "MenuLeft (optional)" and "MenuRight (optional)" are not mapped. class Game { @@ -37,7 +36,7 @@ public: bool m_bCountNotesSeparately; // Count multiple notes in a row as separate notes or as one note bool m_bAllowHopos; // allow Hammer-ons and Pull-offs? Only useful for guitar type input. InputScheme m_InputScheme; - + struct PerButtonInfo { GameButtonType m_gbt; @@ -53,9 +52,7 @@ public: TapNoteScore m_mapW4To; TapNoteScore m_mapW5To; - // // Lua - // void PushSelf( lua_State *L ); }; diff --git a/src/GameConstantsAndTypes.h b/src/GameConstantsAndTypes.h index ee13876bf3..7922e29a1e 100644 --- a/src/GameConstantsAndTypes.h +++ b/src/GameConstantsAndTypes.h @@ -54,6 +54,7 @@ enum StepsType StepsType_dance_double, StepsType_dance_couple, StepsType_dance_solo, + //StepsType_dance_three, StepsType_dance_routine, StepsType_pump_single, StepsType_pump_halfdouble, diff --git a/src/GameManager.cpp b/src/GameManager.cpp index 8d7319498e..7642cdcd1e 100644 --- a/src/GameManager.cpp +++ b/src/GameManager.cpp @@ -48,6 +48,7 @@ static const StepsTypeInfo g_StepsTypeInfos[] = { { "dance-double", 8, true, StepsTypeCategory_Double }, { "dance-couple", 8, true, StepsTypeCategory_Couple }, { "dance-solo", 6, true, StepsTypeCategory_Single }, + //{ "dance-three", 3, true, StepsTypeCategory_Single }, { "dance-routine", 8, false, StepsTypeCategory_Routine }, // pump { "pump-single", 5, true, StepsTypeCategory_Single }, @@ -323,6 +324,41 @@ static const Style g_Style_Dance_Solo = }; // todo: "easy solo" or whatever the proper name for the 3 panel \|/ mode is. +/* +static const Style g_Style_Dance_Three = +{ // STYLE_DANCE_THREE + true, // m_bUsedForGameplay + true, // m_bUsedForEdit + false, // m_bUsedForDemonstration + false, // m_bUsedForHowToPlay + "three", // m_szName + StepsType_dance_three, // m_StepsType + StyleType_OnePlayerOneSide, // m_StyleType + 3, // m_iColsPerPlayer + { // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER]; + { // PLAYER_1 + { TRACK_1, -DANCE_COL_SPACING*1.0f, NULL }, + { TRACK_2, +DANCE_COL_SPACING*0.0f, NULL }, + { TRACK_3, +DANCE_COL_SPACING*1.0f, NULL }, + }, + { // PLAYER_2 + { TRACK_1, -DANCE_COL_SPACING*1.0f, NULL }, + { TRACK_2, +DANCE_COL_SPACING*0.0f, NULL }, + { TRACK_3, +DANCE_COL_SPACING*1.0f, NULL }, + }, + }, + { // m_iInputColumn[NUM_GameController][NUM_GameButton] + { 0, 1, 2, Style::END_MAPPING }, + { 0, 1, 2, Style::END_MAPPING } + }, + { // m_iColumnDrawOrder[MAX_COLS_PER_PLAYER]; + 0,1,2 + }, + false, // m_bNeedsZoomOutWith2Players + false, // m_bCanUseBeginnerHelper + false, // m_bLockDifficulties +}; +*/ static const Style g_Style_Dance_Couple_Edit = { // STYLE_DANCE_EDIT_COUPLE @@ -458,6 +494,7 @@ static const Style *g_apGame_Dance_Styles[] = &g_Style_Dance_Double, &g_Style_Dance_Couple, &g_Style_Dance_Solo, + //&g_Style_Dance_Three, &g_Style_Dance_Couple_Edit, &g_Style_Dance_Routine, NULL @@ -770,7 +807,7 @@ static const Style g_Style_Pump_Couple_Edit = }; static const Style g_Style_Pump_Routine = -{ // STYLE_DANCE_ROUTINE +{ // STYLE_PUMP_ROUTINE true, // m_bUsedForGameplay true, // m_bUsedForEdit false, // m_bUsedForDemonstration @@ -2511,7 +2548,7 @@ static const Game g_Game_Popn = /** Guitar5 ******************************************************************/ //ThemeMetric GUITAR5_COL_SPACING ("ColumnSpacing","Guitar5"); -static const int GUITAR5_COL_SPACING = 32; +static const int GUITAR5_COL_SPACING = 32; /* static const Style g_Style_Guitar_Five = { // STYLE_GUITAR_FIVE diff --git a/src/GameState.cpp b/src/GameState.cpp index 0bda2b54cc..60ceaf568f 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -38,7 +38,6 @@ #include "UnlockManager.h" #include "ScreenManager.h" #include "Screen.h" -#include "GameConstantsAndTypes.h" #include #include @@ -2162,35 +2161,8 @@ public: } static int GetCurrentSteps( T* p, lua_State *L ) { - // XXX: this will crash in certain situations, but if the PlayerNumber - // is checked for, difficulty displays will break... what to do? -aj PlayerNumber pn = Enum::Check(L, 1); - Steps* pSteps; - - if( p->m_pCurSong.Get() != NULL ) - { - // try our preferred steps type and difficulty first - pSteps = SongUtil::GetOneSteps( p->m_pCurSong.Get(), GAMESTATE->m_PreferredStepsType, GAMESTATE->m_PreferredDifficulty[pn] ); - - // no matches for preferred stepstype? try the current style - if( pSteps == NULL ) - pSteps = SongUtil::GetOneSteps( p->m_pCurSong.Get(), GAMESTATE->GetCurrentStyle()->m_StepsType, GAMESTATE->m_PreferredDifficulty[pn] ); - - // nothing found with preferred difficulty? try with closest. - // closest seems to return 'easy' when prefrred difficulty is 'medium' - // even if the song has medium steps? - if( pSteps == NULL ) - pSteps = SongUtil::GetOneSteps( p->m_pCurSong.Get(), GAMESTATE->GetCurrentStyle()->m_StepsType, GAMESTATE->GetClosestShownDifficulty(pn) ); - } - else - { - pSteps = p->m_pCurSteps[pn]; - } - - // if you're on the music select and change song, the current steps are not - // updated correctly/in time for CurrentSongChangedMessageCommand - // (as such the returned steps are that of the previous song) - + Steps *pSteps = p->m_pCurSteps[pn]; if( pSteps ) { pSteps->PushSelf(L); } else { lua_pushnil(L); } return 1; @@ -2200,7 +2172,7 @@ public: PlayerNumber pn = Enum::Check(L, 1); if( lua_isnil(L,2) ) { p->m_pCurSteps[pn].Set( NULL ); } else { Steps *pS = Luna::check(L,2); p->m_pCurSteps[pn].Set( pS ); } - + // Why Broadcast again? This is double-broadcasting. -Chris MESSAGEMAN->Broadcast( (MessageID)(Message_CurrentStepsP1Changed+pn) ); return 0; diff --git a/src/Makefile.am b/src/Makefile.am index adc83ab9ee..3783740e7f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -370,9 +370,11 @@ lua-5.1/src/lundump.h lua-5.1/src/lvm.h lua-5.1/src/lzio.h RageFile = \ RageFileBasic.cpp RageFileBasic.h \ RageFile.cpp RageFile.h RageFileDriver.cpp RageFileDriver.h RageFileManager.cpp RageFileManager.h \ +RageFileManager_ReadAhead.cpp RageFileManager_ReadAhead.h \ RageFileDriverDirect.cpp RageFileDriverDirect.h RageFileDriverDirectHelpers.cpp RageFileDriverDirectHelpers.h \ RageFileDriverMemory.cpp RageFileDriverMemory.h RageFileDriverZip.cpp RageFileDriverZip.h \ RageFileDriverDeflate.cpp RageFileDriverDeflate.h \ +RageFileDriverReadAhead.cpp RageFileDriverReadAhead.h \ RageFileDriverSlice.cpp RageFileDriverSlice.h \ RageFileDriverTimeout.cpp RageFileDriverTimeout.h diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp index aa14e01b41..b4e120b994 100644 --- a/src/NoteDataUtil.cpp +++ b/src/NoteDataUtil.cpp @@ -439,6 +439,12 @@ void NoteDataUtil::SplitCompositeNoteData( const NoteData &in, vector return; } + FOREACH_PlayerNumber( pn ) + { + out.push_back( NoteData() ); + out.back().SetNumTracks( in.GetNumTracks() ); + } + for( int t = 0; t < in.GetNumTracks(); ++t ) { for( NoteData::const_iterator iter = in.begin(t); iter != in.end(t); ++iter ) @@ -447,12 +453,7 @@ void NoteDataUtil::SplitCompositeNoteData( const NoteData &in, vector TapNote tn = iter->second; unsigned index = int( tn.pn ); - DEBUG_ASSERT( index < NUM_PlayerNumber ); - while( out.size() <= index ) - { - out.push_back( NoteData() ); - out.back().SetNumTracks( in.GetNumTracks() ); - } + ASSERT( index < NUM_PlayerNumber ); tn.pn = PLAYER_INVALID; out[index].SetTapNote( t, row, tn ); } diff --git a/src/NoteDisplay.cpp b/src/NoteDisplay.cpp index b2608c79d4..dabeee175e 100644 --- a/src/NoteDisplay.cpp +++ b/src/NoteDisplay.cpp @@ -22,12 +22,13 @@ static const char *NotePartNames[] = { "TapLift", "TapFake", "HoldHead", + "HoldTail", "HoldTopCap", "HoldBody", "HoldBottomCap", - "HoldTail", }; XToString( NotePart ); +LuaXType( NotePart ); static bool IsVectorZero( const RageVector2 &v ) { @@ -58,7 +59,6 @@ struct NoteMetricCache_t bool m_bTopHoldAnchorWhenReverse; bool m_bHoldActiveIsAddLayer; - void Load( const RString &sButton ); } *NoteMetricCache; @@ -658,16 +658,16 @@ void NoteDisplay::DrawActor( const TapNote& tn, Actor* pActor, NotePart part, in { if( fYOffset < fDrawDistanceAfterTargetsPixels || fYOffset > fDrawDistanceBeforeTargetsPixels ) return; - const float fY = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYOffset, fReverseOffsetPixels ); + const float fY = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYOffset, fReverseOffsetPixels ); float fX = ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset ); if( tn.iMidiNote ) fX += ArrowEffects::GetXOffset( m_pPlayerState, tn.iMidiNote ); - const float fZ = ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset ); - const float fAlpha = ArrowEffects::GetAlpha( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar ); - const float fGlow = ArrowEffects::GetGlow( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar ); - const RageColor diffuse = RageColor(fColorScale,fColorScale,fColorScale,fAlpha); - const RageColor glow = RageColor(1,1,1,fGlow); - float fRotationZ = 0; + const float fZ = ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset ); + const float fAlpha = ArrowEffects::GetAlpha( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar ); + const float fGlow = ArrowEffects::GetGlow( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar ); + const RageColor diffuse = RageColor(fColorScale,fColorScale,fColorScale,fAlpha); + const RageColor glow = RageColor(1,1,1,fGlow); + float fRotationZ = 0; fRotationZ = ArrowEffects::GetRotation( m_pPlayerState, fBeat, tn.type == tn.hold_head ); if( tn.type != tn.hold_head ) @@ -735,6 +735,7 @@ void NoteDisplay::DrawTap( const TapNote& tn, int iCol, float fBeat, bool bOnSam } const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fBeat ); + // this is the line that forces the (1,1,1,x) part of the noteskin diffuse -aj DrawActor( tn, pActor, part, iCol, fYOffset, fBeat, bIsAddition, fPercentFadeToFail, fReverseOffsetPixels, 1.0f, fDrawDistanceAfterTargetsPixels, fDrawDistanceBeforeTargetsPixels, fFadeInPercentOfDrawFar ); if( tn.type == TapNote::attack ) diff --git a/src/NoteField.cpp b/src/NoteField.cpp index 580770a480..b28a265ac2 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -28,6 +28,9 @@ static ThemeMetric BAR_4TH_ALPHA( "NoteField", "Bar4thAlpha" ); static ThemeMetric BAR_8TH_ALPHA( "NoteField", "Bar8thAlpha" ); static ThemeMetric BAR_16TH_ALPHA( "NoteField", "Bar16thAlpha" ); +static RString RoutineNoteSkinName( size_t i ) { return ssprintf("RoutineNoteSkinP%i",int(i+1)); } +static ThemeMetric1D ROUTINE_NOTESKIN( "NoteField", RoutineNoteSkinName, NUM_PLAYERS ); + NoteField::NoteField() { m_pNoteData = NULL; @@ -109,6 +112,14 @@ void NoteField::CacheAllUsedNoteSkins() /* Cache all note skins that we might need for the whole song, course or battle * play, so we don't have to load them later (such as between course songs). */ vector asSkins; + + // If we're in Routine mode, apply our per-player noteskins. + if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_TwoPlayersSharedSides ) + { + FOREACH_EnabledPlayer( pn ) + GAMESTATE->ApplyStageModifiers( pn, ROUTINE_NOTESKIN.GetValue(pn) ); + } + GAMESTATE->GetAllUsedNoteSkins( asSkins ); asSkins.push_back( m_pPlayerState->m_PlayerOptions.GetStage().m_sNoteSkin ); diff --git a/src/PlayerStageStats.cpp b/src/PlayerStageStats.cpp index b1fdab2c12..98b33ff525 100644 --- a/src/PlayerStageStats.cpp +++ b/src/PlayerStageStats.cpp @@ -168,24 +168,24 @@ Grade PlayerStageStats::GetGrade() const { int iTapScoreValue = ScoreKeeperNormal::TapNoteScoreToGradePoints( tns, bIsBeginner ); fActual += m_iTapNoteScores[tns] * iTapScoreValue; - LOG->Trace( "GetGrade actual: %i * %i", m_iTapNoteScores[tns], iTapScoreValue ); + //LOG->Trace( "GetGrade actual: %i * %i", m_iTapNoteScores[tns], iTapScoreValue ); } FOREACH_ENUM( HoldNoteScore, hns ) { int iHoldScoreValue = ScoreKeeperNormal::HoldNoteScoreToGradePoints( hns, bIsBeginner ); fActual += m_iHoldNoteScores[hns] * iHoldScoreValue; - LOG->Trace( "GetGrade actual: %i * %i", m_iHoldNoteScores[hns], iHoldScoreValue ); + //LOG->Trace( "GetGrade actual: %i * %i", m_iHoldNoteScores[hns], iHoldScoreValue ); } - LOG->Trace( "GetGrade: fActual: %f, fPossible: %d", fActual, m_iPossibleGradePoints ); + //LOG->Trace( "GetGrade: fActual: %f, fPossible: %d", fActual, m_iPossibleGradePoints ); float fPercent = (m_iPossibleGradePoints == 0) ? 0 : fActual / m_iPossibleGradePoints; Grade grade = GetGradeFromPercent( fPercent ); - LOG->Trace( "GetGrade: Grade: %s, %i", GradeToString(grade).c_str(), GRADE_TIER02_IS_ALL_W2S ); + //LOG->Trace( "GetGrade: Grade: %s, %i", GradeToString(grade).c_str(), GRADE_TIER02_IS_ALL_W2S ); if( GRADE_TIER02_IS_ALL_W2S ) { if( FullComboOfScore(TNS_W1) ) diff --git a/src/Profile.cpp b/src/Profile.cpp index bfc675f0f8..ae82fa603f 100644 --- a/src/Profile.cpp +++ b/src/Profile.cpp @@ -1645,7 +1645,7 @@ void Profile::LoadScreenshotDataFromNode( const XNode* pScreenshotData ) ss.LoadFromNode( pScreenshot ); m_vScreenshots.push_back( ss ); - } + } } XNode* Profile::SaveScreenshotDataCreateNode() const @@ -1878,9 +1878,7 @@ void Profile::MoveBackupToDir( RString sFromDir, RString sToDir ) RString Profile::MakeUniqueFileNameNoExtension( RString sDir, RString sFileNameBeginning ) { FILEMAN->FlushDirCache( sDir ); - // // Find a file name for the screenshot - // vector files; GetDirListing( sDir + sFileNameBeginning+"*", files, false, false ); sort( files.begin(), files.end() ); diff --git a/src/RageFile.cpp b/src/RageFile.cpp index 1f23d0eeca..4a56b1a66a 100644 --- a/src/RageFile.cpp +++ b/src/RageFile.cpp @@ -166,6 +166,12 @@ int RageFile::GetFileSize() const return m_File->GetFileSize(); } +int RageFile::GetFD() +{ + ASSERT_READ; + return m_File->GetFD(); +} + int RageFile::Read( RString &buffer, int bytes ) { ASSERT_READ; diff --git a/src/RageFile.h b/src/RageFile.h index 64a0894701..013b59eb6c 100644 --- a/src/RageFile.h +++ b/src/RageFile.h @@ -13,15 +13,15 @@ class RageFile: public RageFileBasic public: enum { - READ = 0x1, - WRITE = 0x2, + READ = 0x1, + WRITE = 0x2, /* Always write directly to the destination file; don't do a safe write. (for logs) */ - STREAMED = 0x4, + STREAMED = 0x4, /* Flush the file to disk on close. Combined with not streaming, this results * in very safe writes, but is slow. */ - SLOW_FLUSH = 0x8 + SLOW_FLUSH = 0x8 }; RageFile(); @@ -39,7 +39,7 @@ public: */ const RString &GetRealPath() const { return m_Path; } RString GetPath() const; - + bool Open( const RString& path, int mode = READ ); void Close(); bool IsOpen() const { return m_File != NULL; } @@ -51,7 +51,8 @@ public: int Tell() const; int Seek( int offset ); int GetFileSize() const; - + int GetFD(); + /* Raw I/O: */ int Read( void *buffer, size_t bytes ); int Read( RString &buffer, int bytes = -1 ); diff --git a/src/RageFileBasic.h b/src/RageFileBasic.h index 8a2c9f318e..8e42cb7c01 100644 --- a/src/RageFileBasic.h +++ b/src/RageFileBasic.h @@ -51,8 +51,13 @@ public: virtual void EnableCRC32( bool on=true ) = 0; virtual bool GetCRC32( uint32_t *iRet ) = 0; - + virtual int GetFileSize() const = 0; + + /* If this file is backed by a file descriptor, return it. This is valid even + * if the file is being filtered or decompressed. If the file has no + * associated file descriptor, return -1. */ + virtual int GetFD() = 0; }; class RageFileObj: public RageFileBasic @@ -88,6 +93,7 @@ public: bool GetCRC32( uint32_t *iRet ); virtual int GetFileSize() const = 0; + virtual int GetFD() { return -1; } virtual RString GetDisplayPath() const { return RString(); } virtual RageFileBasic *Copy() const { FAIL_M( "Copying unimplemented" ); } diff --git a/src/RageFileDriverDeflate.h b/src/RageFileDriverDeflate.h index ab07850f7a..5f64088a8a 100644 --- a/src/RageFileDriverDeflate.h +++ b/src/RageFileDriverDeflate.h @@ -19,6 +19,7 @@ public: int WriteInternal( const void *pBuffer, size_t iBytes ) { SetError( "Not implemented" ); return -1; } int SeekInternal( int iOffset ); int GetFileSize() const { return m_iUncompressedSize; } + int GetFD() { return m_pFile->GetFD(); } RageFileObjInflate *Copy() const; void DeleteFileWhenFinished() { m_bFileOwned = true; } diff --git a/src/RageFileDriverDirect.cpp b/src/RageFileDriverDirect.cpp index 90a9e56335..68126d45e2 100644 --- a/src/RageFileDriverDirect.cpp +++ b/src/RageFileDriverDirect.cpp @@ -50,6 +50,7 @@ public: virtual RageFileObjDirect *Copy() const; virtual RString GetDisplayPath() const { return m_sPath; } virtual int GetFileSize() const; + virtual int GetFD(); private: bool FinalFlush(); @@ -469,6 +470,11 @@ int RageFileObjDirect::GetFileSize() const return iRet; } +int RageFileObjDirect::GetFD() +{ + return m_iFD; +} + /* * Copyright (c) 2003-2004 Glenn Maynard, Chris Danford * All rights reserved. diff --git a/src/RageFileDriverReadAhead.cpp b/src/RageFileDriverReadAhead.cpp new file mode 100644 index 0000000000..37e1f0dfc5 --- /dev/null +++ b/src/RageFileDriverReadAhead.cpp @@ -0,0 +1,191 @@ +/* + * When streaming files in gameplay, we need to be able to access files without blocking. + * If we read data and it's not currently in the OS's read-ahead cache, we'll block while + * it reads, which will usually take 10-20ms and cause a frame skip. + * + * One common case of this is playing a background movie. We'll open the file when the + * screen loads, but it may not be played for over a minute, during which time we'll be + * streaming from other movies. When we come back, the file may not have any read-ahead + * waiting for us. + * + * Linux provides two calls that can help here: + * + * posix_readahead; this hints the OS that we're streaming a file, so it doesn't need to + * preserve cache behind where we've read. This reduces the chance of pushing other files + * out of cache. However, this can also cause new skips; if we need to rewind a movie and + * start it over, the beginning will be out of the cache. + * + * readahead: this explicitly triggers a specific file read-ahead. This blocks, so if we + * use this we need to do it in a thread. + * + * We work like this: + * + * - Persistantly cache the first N bytes of the file. + * - Open a worker thread for read-ahead, which is idle most of the time. + * - When we read from the first 128k of the file, take it directly out of our cache, and + * seek the file past our cache. + * - When any data is read from the cached part of the file and readahead hasn't been performed, + * trigger readahead() to be done in the worker thread, so data past our cache is ready when + * we get there. + * - When any data is read outside of the cached part of the file, reset our state so we know + * to do the readahead again the next time we're back at the start of the file. + * + * This means that reading should always be nonblocking, given these constraints: + * - We must consistently stream a file starting at the beginning, and don't seek to random points + * in the file. + * - We read the file slowly enough that we don't overtake the read-ahead cache, which for most + * streaming files shouldn't be a problem. + */ + +#include "global.h" +#include "RageFileDriverReadAhead.h" +#include "RageLog.h" +#include "RageUtil.h" +#include "RageFileManager_ReadAhead.h" + +#include +#include +#if defined(WIN32) +#include +#endif + +/* If GetFD() isn't supported on the underlying file, this filter won't do anything. */ +bool RageFileDriverReadAhead::FileSupported( RageFileBasic *pFile ) +{ + return pFile->GetFD() != -1; +} + +/* iReadAheadBytes is the number of bytes to cache at the start of the file, to serve data while + * the readahead is taking place. iPostBufferReadAhead is the number of bytes to actually read ahead. */ +RageFileDriverReadAhead::RageFileDriverReadAhead( RageFileBasic *pFile, int iCacheBytes, int iPostBufferReadAhead ) +{ + if( iPostBufferReadAhead == -1 ) + iPostBufferReadAhead = iCacheBytes; + + m_pFile = pFile; + m_iFilePos = pFile->Tell(); + m_bFileOwned = false; + m_bReadAheadNeeded = true; + m_iPostBufferReadAhead = iPostBufferReadAhead; + + FillBuffer( iCacheBytes ); +} + +RageFileDriverReadAhead::RageFileDriverReadAhead( const RageFileDriverReadAhead &cpy ): + RageFileObj(cpy) +{ + m_pFile = cpy.m_pFile->Copy(); + m_sBuffer = cpy.m_sBuffer; + m_iFilePos = cpy.m_iFilePos; + m_bFileOwned = true; + m_iPostBufferReadAhead = cpy.m_iPostBufferReadAhead; + m_bReadAheadNeeded = cpy.m_bReadAheadNeeded; + + RageFileManagerReadAhead::CacheHintStreaming( m_pFile ); +} + +RageFileDriverReadAhead::~RageFileDriverReadAhead() +{ + if( m_bFileOwned ) + delete m_pFile; +} + +RageFileDriverReadAhead *RageFileDriverReadAhead::Copy() const +{ + RageFileDriverReadAhead *pRet = new RageFileDriverReadAhead( *this ); + return pRet; +} + +void RageFileDriverReadAhead::FillBuffer( int iBytes ) +{ + int iOldPos = m_pFile->Tell(); + m_pFile->Seek( 0 ); + + m_sBuffer = ""; + m_pFile->Read( m_sBuffer, iBytes ); + + /* Seek back to where we were. If we're going back to the cached region, seek past it, + * like SeekInternal does. */ + if( iOldPos < (int) m_sBuffer.size() ) + iOldPos = m_sBuffer.size(); + m_pFile->Seek( iOldPos ); + + /* Now that we're done moving the file pointer around, set the file's read-ahead hint, + * if supported. */ + RageFileManagerReadAhead::CacheHintStreaming( m_pFile ); +} + +int RageFileDriverReadAhead::ReadInternal( void *pBuffer, size_t iBytes ) +{ + /* If we're in the buffered area, and we havn't already done read-ahead, start it. */ + if( m_bReadAheadNeeded && m_iFilePos < (int) m_sBuffer.size() ) + { + RageFileManagerReadAhead::ReadAhead( m_pFile, m_iPostBufferReadAhead ); + + m_bReadAheadNeeded = false; + } + else if( !m_bReadAheadNeeded && m_iFilePos >= (int) m_sBuffer.size() ) + { + /* Reset, so next time we rewind we'll read-ahead again. */ + m_bReadAheadNeeded = true; + } + + /* If we can serve data out of the buffer, use it. */ + if( m_iFilePos < (int) m_sBuffer.size() ) + { + size_t iFromBuffer = min( iBytes, m_sBuffer.size() - m_iFilePos ); + memcpy( pBuffer, m_sBuffer.data() + m_iFilePos, iFromBuffer ); + m_iFilePos += iFromBuffer; + return iFromBuffer; + } + + /* Read out of the underlying file. */ + int iRet = m_pFile->Read( pBuffer, iBytes ); + if( iRet != -1 ) + m_iFilePos += iRet; + return iRet; + +} + +int RageFileDriverReadAhead::SeekInternal( int iOffset ) +{ + if( iOffset < (int) m_sBuffer.size() ) + { + /* This assumes that seeking the file won't block. This seems to be true in Linux, at least. + * Seek the actual file to just past our buffer, so the RageFileManagerReadAhead::ReadAhead + * call will read ahead from the correct position. */ + m_pFile->Seek( m_sBuffer.size() ); + m_iFilePos = iOffset; + return iOffset; + } + + m_iFilePos = m_pFile->Seek( iOffset ); + return m_iFilePos; +} + + +/* + * Copyright (c) 2010 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. + */ + diff --git a/src/RageFileDriverReadAhead.h b/src/RageFileDriverReadAhead.h new file mode 100644 index 0000000000..3c256f3434 --- /dev/null +++ b/src/RageFileDriverReadAhead.h @@ -0,0 +1,69 @@ +/* RageFileDriverReadAhead - Read-ahead hinting for seamless rewinding. */ + +#ifndef RAGE_FILE_DRIVER_READ_AHEAD_H +#define RAGE_FILE_DRIVER_READ_AHEAD_H + +#include "RageFileBasic.h" + +class RageFileDriverReadAhead: public RageFileObj +{ +public: + /* This filter can only be used on supported files; test before using. */ + static bool FileSupported( RageFileBasic *pFile ); + + /* pFile will be freed if DeleteFileWhenFinished is called. */ + RageFileDriverReadAhead( RageFileBasic *pFile, int iCacheBytes, int iPostBufferReadAhead = -1 ); + RageFileDriverReadAhead( const RageFileDriverReadAhead &cpy ); + ~RageFileDriverReadAhead(); + RageFileDriverReadAhead *Copy() const; + + void DeleteFileWhenFinished() { m_bFileOwned = true; } + + virtual RString GetError() const { return m_pFile->GetError(); } + virtual void ClearError() { return m_pFile->ClearError(); } + + int ReadInternal( void *pBuffer, size_t iBytes ); + int WriteInternal( const void *pBuffer, size_t iBytes ) { return m_pFile->Write( pBuffer, iBytes ); } + int SeekInternal( int iOffset ); + int GetFileSize() const { return m_pFile->GetFileSize(); } + int GetFD() { return m_pFile->GetFD(); } + int Tell() const { return m_iFilePos; } + +private: + void FillBuffer( int iBytes ); + + RageFileBasic *m_pFile; + int m_iFilePos; + bool m_bFileOwned; + RString m_sBuffer; + int m_iPostBufferReadAhead; + bool m_bReadAheadNeeded; +}; + +#endif + +/* + * Copyright (c) 2010 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. + */ + diff --git a/src/RageFileDriverSlice.h b/src/RageFileDriverSlice.h index a99d9c5af0..31bb1cc543 100644 --- a/src/RageFileDriverSlice.h +++ b/src/RageFileDriverSlice.h @@ -20,6 +20,7 @@ public: int WriteInternal( const void *pBuffer, size_t iBytes ) { SetError( "Not implemented" ); return -1; } int SeekInternal( int iOffset ); int GetFileSize() const { return m_iFileSize; } + int GetFD() { return m_pFile->GetFD(); } private: RageFileBasic *m_pFile; diff --git a/src/RageFileDriverTimeout.cpp b/src/RageFileDriverTimeout.cpp index b7b5c16dfe..ff9a239478 100644 --- a/src/RageFileDriverTimeout.cpp +++ b/src/RageFileDriverTimeout.cpp @@ -54,6 +54,7 @@ enum ThreadRequest REQ_OPEN, REQ_CLOSE, REQ_GET_FILE_SIZE, + REQ_GET_FD, REQ_READ, REQ_WRITE, REQ_SEEK, @@ -78,6 +79,7 @@ public: RageFileBasic *Open( const RString &sPath, int iMode, int &iErr ); void Close( RageFileBasic *pFile ); int GetFileSize( RageFileBasic *&pFile ); + int GetFD( RageFileBasic *&pFile ); int Seek( RageFileBasic *&pFile, int iPos, RString &sError ); int Read( RageFileBasic *&pFile, void *pBuf, int iSize, RString &sError ); int Write( RageFileBasic *&pFile, const void *pBuf, int iSize, RString &sError ); @@ -377,6 +379,34 @@ int ThreadedFileWorker::GetFileSize( RageFileBasic *&pFile ) return m_iResultRequest; } +int ThreadedFileWorker::GetFD( RageFileBasic *&pFile ) +{ + ASSERT( m_pChildDriver != NULL ); /* how did you get a file to begin with? */ + + /* If we're currently in a timed-out state, fail. */ + if( IsTimedOut() ) + { + this->Close( pFile ); + pFile = NULL; + } + + if( pFile == NULL ) + return -1; + + m_pRequestFile = pFile; + + if( !DoRequest(REQ_GET_FD) ) + { + /* If we time out, we can no longer access pFile. */ + pFile = NULL; + return -1; + } + + m_pRequestFile = NULL; + + return m_iResultRequest; +} + int ThreadedFileWorker::Seek( RageFileBasic *&pFile, int iPos, RString &sError ) { ASSERT( m_pChildDriver != NULL ); /* how did you get a file to begin with? */ @@ -692,6 +722,23 @@ public: return m_iFileSize; } + int GetFD() + { + RString sError; + int iRet = m_pWorker->GetFD( m_pFile ); + + if( m_pFile == NULL ) + { + SetError( "Operation timed out" ); + return -1; + } + + if( iRet == -1 ) + SetError( sError ); + + return iRet; + } + RageFileBasic *Copy() const { RString sError; diff --git a/src/RageFileManager_ReadAhead.cpp b/src/RageFileManager_ReadAhead.cpp new file mode 100644 index 0000000000..e78fa26d4a --- /dev/null +++ b/src/RageFileManager_ReadAhead.cpp @@ -0,0 +1,174 @@ +#include "global.h" +#include "RageFileManager_ReadAhead.h" +#include "RageThreads.h" +#include "RageLog.h" + +#include +#include +#if defined(WIN32) +#include +#endif + +#if defined(HAVE_POSIX_FADVISE) +void RageFileManagerReadAhead::Init() { } +void RageFileManagerReadAhead::Shutdown() { } +void RageFileManagerReadAhead::ReadAhead( RageFileBasic *pFile, int iBytes ) +{ + int iFD = pFile->GetFD(); + if( iFD == -1 ) + return; + + int iStart = lseek( iFD, 0, SEEK_CUR ); + posix_fadvise( iFD, iStart, iBytes, POSIX_FADV_WILLNEED ); +} + +#else +#if 0 +/* This doesn't currently work, because dup() locks the file position of the new FD with the old + * (which makes no sense at all), so it's not easy to read from a copy of the FD without + * interfering with the original. */ +class RageFileReadAheadThread +{ +public: + RageFileReadAheadThread( int iFD, int iFrom, int iBytes ) + { + m_iFD = iFD; + m_iFrom = iFrom; + m_iBytes = iBytes; + m_bFinished = false; + + m_WorkerThread.SetName( "Read-ahead thread" ); + m_WorkerThread.Create( StartWorkerMain, this ); + } + + ~RageFileReadAheadThread() + { + close( m_iFD ); + m_WorkerThread.Wait(); + } + bool IsFinished() const { return m_bFinished; } + +private: + static int StartWorkerMain( void *pThis ) + { + ((RageFileReadAheadThread *) (pThis))->WorkerMain(); + return 0; + } + + void WorkerMain() + { + RString sBuffer; + int iFDCopy = dup( m_iFD ); + if( iFDCopy != -1 ) + { + char *pBuf = sBuffer.GetBuffer( m_iBytes ); + lseek( iFDCopy, m_iFrom, SEEK_SET ); + read( iFDCopy, pBuf, m_iBytes ); + sBuffer.ReleaseBuffer( m_iBytes ); + + close( iFDCopy ); + LOG->Trace("read"); + } + + m_bFinished = true; + } + + RageThread m_WorkerThread; + + bool m_bFinished; + int m_iFD; + int m_iFrom; + int m_iBytes; +}; + + +static vector g_apReadAheads; + +void RageFileManagerReadAhead::Init() { } +void RageFileManagerReadAhead::Shutdown() +{ + for( size_t i = 0; i < g_apReadAheads.size(); ++i ) + delete g_apReadAheads[i]; + g_apReadAheads.clear(); +} + +/* This emulates posix_fadvise(POSIX_FADV_WILLNEED), to force data into cache and hint the + * kernel to start read-ahead from that point. */ +void RageFileManagerReadAhead::ReadAhead( RageFileBasic *pFile, int iBytes ) +{ + int iFD = pFile->GetFD(); + if( iFD == -1 ) + return; + + iFD = dup( iFD ); + if( iFD == -1 ) + { + LOG->Trace( "error: dup(): %s", strerror(errno) ); + return; + } + + int iStart = lseek( iFD, 0, SEEK_CUR ); + + RageFileReadAheadThread *pReadAhead = new RageFileReadAheadThread( iFD, iStart, iBytes ); + g_apReadAheads.push_back( pReadAhead ); + + for( size_t i = 0; i < g_apReadAheads.size(); ++i ) + { + if( g_apReadAheads[i]->IsFinished() ) + { + LOG->Trace("cleaned"); + delete g_apReadAheads[i]; + g_apReadAheads.erase( g_apReadAheads.begin()+i, g_apReadAheads.begin()+i+1 ); + --i; + } + } +} +#else +void RageFileManagerReadAhead::Init() { } +void RageFileManagerReadAhead::Shutdown() { } +void RageFileManagerReadAhead::ReadAhead( RageFileBasic *pFile, int iBytes ) { } +#endif +#endif + +#if defined(HAVE_POSIX_FADVISE) +void RageFileManagerReadAhead::CacheHintStreaming( RageFileBasic *pFile ) +{ + /* This guesses at the actual size of the file on disk, which may be smaller if this file is compressed. + * Since this is usually used on music and video files, it generally shouldn't be. */ + int iFD = pFile->GetFD(); + if( iFD == -1 ) + return; + int iPos = pFile->Tell(); + int iFrom = lseek( iFD, 0, SEEK_CUR ); + int iBytes = pFile->GetFileSize() - iPos; + posix_fadvise( iFD, iFrom, iBytes, POSIX_FADV_SEQUENTIAL ); +} +#else +void RageFileManagerReadAhead::CacheHintStreaming( RageFileBasic *pFile ) { } +#endif + + +/* + * Copyright (c) 2010 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. + */ diff --git a/src/RageFileManager_ReadAhead.h b/src/RageFileManager_ReadAhead.h new file mode 100644 index 0000000000..9bbc9cdc9f --- /dev/null +++ b/src/RageFileManager_ReadAhead.h @@ -0,0 +1,42 @@ +#ifndef RAGE_FILE_MANAGER_READAHEAD_H +#define RAGE_FILE_MANAGER_READAHEAD_H + +#include "RageFileBasic.h" + +namespace RageFileManagerReadAhead +{ + void Init(); + void Shutdown(); + + /* Nonblockingly read bytes from a file. */ + void ReadAhead( RageFileBasic *pFile, int iBytes ); + + void CacheHintStreaming( RageFileBasic *pFile ); +}; + +#endif + +/* + * Copyright (c) 2010 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. + */ diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 13377a07ab..520e25cab0 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -712,7 +712,7 @@ void ScreenEdit::Init() CopyToLastSave(); m_CurrentAction = MAIN_MENU_CHOICE_INVALID; - if( GAMESTATE->m_pCurSteps[0]->m_StepsType == StepsType_dance_routine ) + if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_TwoPlayersSharedSides ) m_InputPlayerNumber = PLAYER_1; else m_InputPlayerNumber = PLAYER_INVALID; @@ -2179,6 +2179,12 @@ void ScreenEdit::InputPlay( const InputEventPlus &input, EditButton EditB ) bool bRelease = input.type == IET_RELEASE; switch( input.pn ) { + case PLAYER_2: + // ignore player 2 input unless this mode requires it + if( GAMESTATE->GetCurrentStyle()->m_StyleType != StyleType_TwoPlayersSharedSides ) + break; + + // fall through to input handling logic: case PLAYER_1: { switch( gbt ) @@ -2199,9 +2205,6 @@ void ScreenEdit::InputPlay( const InputEventPlus &input, EditButton EditB ) } } break; - case PLAYER_2: - if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_TwoPlayersSharedSides ) - m_Player->Step( iCol, -1, input.DeviceI.ts, false, input.type == IET_RELEASE ); } } diff --git a/src/ScreenSelectMusic.cpp b/src/ScreenSelectMusic.cpp index 5b3826ebdc..0276b014f2 100644 --- a/src/ScreenSelectMusic.cpp +++ b/src/ScreenSelectMusic.cpp @@ -192,8 +192,10 @@ void ScreenSelectMusic::Init() this->SortByDrawOrder(); } +//#include "RageTimer.h" void ScreenSelectMusic::BeginScreen() { + //RageTimer tm; g_ScreenStartedLoadingAt.Touch(); if( CommonMetrics::AUTO_SET_STYLE ) @@ -209,10 +211,11 @@ void ScreenSelectMusic::BeginScreen() if( GAMESTATE->m_PlayMode == PlayMode_Invalid ) { - /* Instead of crashing here, let's just set the PlayMode to regular */ + /* Instead of crashing here, let's just set the PlayMode to regular */ GAMESTATE->m_PlayMode.Set( PLAY_MODE_REGULAR ); LOG->Trace( "PlayMode not set, setting as regular." ); } + //LOG->Trace("XXX 1 %f", tm.GetDeltaTime()); FOREACH_ENUM( PlayerNumber, pn ) { if( GAMESTATE->IsHumanPlayer(pn) ) @@ -223,23 +226,30 @@ void ScreenSelectMusic::BeginScreen() OPTIONS_MENU_AVAILABLE.Load( m_sName, "OptionsMenuAvailable" ); PlayCommand( "Mods" ); + //LOG->Trace("XXX 2a %f", tm.GetDeltaTime()); m_MusicWheel.BeginScreen(); - + //LOG->Trace("XXX 2b %f", tm.GetDeltaTime()); + m_SelectionState = SelectionState_SelectingSong; ZERO( m_bStepsChosen ); m_bGoToOptions = false; m_bAllowOptionsMenu = m_bAllowOptionsMenuRepeat = false; ZERO( m_iSelection ); + //LOG->Trace("XXX 3 %f", tm.GetDeltaTime()); if( USE_OPTIONS_LIST ) FOREACH_PlayerNumber( pn ) m_OptionsList[pn].Reset(); + //LOG->Trace("XXX 4a %f", tm.GetDeltaTime()); AfterMusicChange(); + //LOG->Trace("XXX 4b %f", tm.GetDeltaTime()); SOUND->PlayOnceFromAnnouncer( "select music intro" ); + //LOG->Trace("XXX 4c %f", tm.GetDeltaTime()); ScreenWithMenuElements::BeginScreen(); + //LOG->Trace("XXX 5 %f", tm.GetDeltaTime()); } ScreenSelectMusic::~ScreenSelectMusic() diff --git a/src/StepMania-net2003.vcproj b/src/StepMania-net2003.vcproj index e9c9ad5e11..9baecf99f0 100644 --- a/src/StepMania-net2003.vcproj +++ b/src/StepMania-net2003.vcproj @@ -1753,6 +1753,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\ + + + + @@ -1777,6 +1783,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\ + + + + diff --git a/src/StepMania-net2005.vcproj b/src/StepMania-net2005.vcproj index 63dba22021..127763f0e9 100644 --- a/src/StepMania-net2005.vcproj +++ b/src/StepMania-net2005.vcproj @@ -3050,6 +3050,14 @@ RelativePath="RageFileDriverMemory.h" > + + + + @@ -3082,6 +3090,14 @@ RelativePath="RageFileManager.h" > + + + + diff --git a/src/StepMania-net2008.vcproj b/src/StepMania-net2008.vcproj index fa86ee372e..911321413d 100644 --- a/src/StepMania-net2008.vcproj +++ b/src/StepMania-net2008.vcproj @@ -3208,6 +3208,14 @@ RelativePath="RageFileDriverMemory.h" > + + + + @@ -3240,6 +3248,14 @@ RelativePath="RageFileManager.h" > + + + + diff --git a/src/StepMania.cpp b/src/StepMania.cpp index 9602c3dfb0..4304cd2098 100644 --- a/src/StepMania.cpp +++ b/src/StepMania.cpp @@ -1272,10 +1272,8 @@ RString StepMania::SaveScreenshot( RString sDir, bool bSaveCompressed, bool bMak else sFileNameNoExtension = Profile::MakeFileNameNoExtension( "screen", iIndex ); - // // Save the screenshot. If writing lossy to a memcard, use SAVE_LOSSY_LOW_QUAL, so we // don't eat up lots of space. - // RageDisplay::GraphicsFileFormat fmt; if( bSaveCompressed && MEMCARDMAN->PathIsMemCard(sDir) ) fmt = RageDisplay::SAVE_LOSSY_LOW_QUAL; diff --git a/src/StepManiaXbox-2003.vcproj b/src/StepManiaXbox-2003.vcproj index 0822127f6c..a071d84a48 100644 --- a/src/StepManiaXbox-2003.vcproj +++ b/src/StepManiaXbox-2003.vcproj @@ -1949,6 +1949,12 @@ cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ + + + + @@ -1973,6 +1979,12 @@ cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ + + + + diff --git a/src/StreamDisplay.cpp b/src/StreamDisplay.cpp index 615d661e17..2828693944 100644 --- a/src/StreamDisplay.cpp +++ b/src/StreamDisplay.cpp @@ -26,7 +26,7 @@ StreamDisplay::StreamDisplay() void StreamDisplay::Load( const RString &_sMetricsGroup ) { - // XXX + // XXX: actually load from the metrics group passed in -aj RString sMetricsGroup = "StreamDisplay"; m_transformPill.SetFromReference( THEME->GetMetricR(sMetricsGroup,"PillTransformFunction") ); diff --git a/src/StreamDisplay.h b/src/StreamDisplay.h index 86d75ba298..a9e124de9b 100644 --- a/src/StreamDisplay.h +++ b/src/StreamDisplay.h @@ -39,10 +39,10 @@ private: float m_fPercent; // percent filled float m_fTrailingPercent; // this approaches m_fPercent, use this value to draw float m_fVelocity; // velocity of m_fTrailingPercent - + float m_fPassingAlpha; float m_fHotAlpha; - + bool m_bUsingThreePart; float m_fThreePartWidth; diff --git a/src/Tween.cpp b/src/Tween.cpp index 6785ccb064..31d2b27285 100644 --- a/src/Tween.cpp +++ b/src/Tween.cpp @@ -119,7 +119,7 @@ ITween *ITween::CreateFromStack( Lua *L, int iStackPos ) return pBezier; } } - + return CreateFromType( iType ); } diff --git a/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp b/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp index 4d7cf2a3a5..1bc5777b06 100644 --- a/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp +++ b/src/arch/MovieTexture/MovieTexture_FFMpeg.cpp @@ -6,6 +6,7 @@ #include "RageUtil.h" #include "RageFile.h" #include "RageSurface.h" +#include "RageFileDriverReadAhead.h" #include @@ -639,28 +640,41 @@ int URLRageFile_open( avcodec::URLContext *h, const char *filename, int flags ) case URL_RDWR: FAIL_M( "O_RDWR unsupported" ); } - RageFile *f = new RageFile; - if( !f->Open(filename, mode) ) + RageFileBasic *pFile = new RageFile; + { - LOG->Trace("Error opening \"%s\": %s", filename, f->GetError().c_str() ); - delete f; - return -EIO; + RageFile *f = new RageFile; + if( !f->Open(filename, mode) ) + { + LOG->Trace("Error opening \"%s\": %s", filename, f->GetError().c_str() ); + delete f; + return -EIO; + } + pFile = f; + } + + // If possible, wrap this file in the read-ahead filter to avoid skips when we rewind. + if( RageFileDriverReadAhead::FileSupported(pFile) ) + { + RageFileDriverReadAhead *pBufferedFile = new RageFileDriverReadAhead( pFile, 1024*128 ); + pBufferedFile->DeleteFileWhenFinished(); + pFile = pBufferedFile; } h->is_streamed = false; - h->priv_data = f; + h->priv_data = pFile; return 0; } int URLRageFile_read( avcodec::URLContext *h, unsigned char *buf, int size ) { - RageFile *f = (RageFile *) h->priv_data; + RageFileBasic *f = (RageFileBasic *) h->priv_data; return f->Read( buf, size ); } int URLRageFile_write( avcodec::URLContext *h, unsigned char *buf, int size ) { - RageFile *f = (RageFile *) h->priv_data; + RageFileBasic *f = (RageFileBasic *) h->priv_data; return f->Write( buf, size ); } @@ -671,7 +685,7 @@ int URLRageFile_write( avcodec::URLContext *h, unsigned char *buf, int size ) avcodec::int64_t URLRageFile_seek( avcodec::URLContext *h, avcodec::int64_t pos, int whence ) #endif { - RageFile *f = (RageFile *) h->priv_data; + RageFileBasic *f = (RageFileBasic *) h->priv_data; if( whence == AVSEEK_SIZE ) return f->GetFileSize(); @@ -683,7 +697,7 @@ int URLRageFile_write( avcodec::URLContext *h, unsigned char *buf, int size ) int URLRageFile_close( avcodec::URLContext *h ) { - RageFile *f = (RageFile *) h->priv_data; + RageFileBasic *f = (RageFileBasic *) h->priv_data; delete f; return 0; } diff --git a/src/mad-0.15.1b/aclocal.m4 b/src/mad-0.15.1b/aclocal.m4 index fe7c6fe45e..ea3892ed2f 100644 --- a/src/mad-0.15.1b/aclocal.m4 +++ b/src/mad-0.15.1b/aclocal.m4 @@ -14,7 +14,6 @@ # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # serial 47 AC_PROG_LIBTOOL -# Debian $Rev: 8581 $ # AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) diff --git a/src/mad-0.15.1b/ltmain.sh b/src/mad-0.15.1b/ltmain.sh index d1d326b15b..012b04e3e7 100644 --- a/src/mad-0.15.1b/ltmain.sh +++ b/src/mad-0.15.1b/ltmain.sh @@ -56,7 +56,6 @@ modename="$progname" PROGRAM=ltmain.sh PACKAGE=libtool VERSION=1.5.2 -TIMESTAMP=" (1.1220.2.60 2004/01/25 12:25:08) Debian$Rev: 8581 $" default_mode= help="Try \`$progname --help' for more information."