From c6e62ae92da7f746a3cd9362d47e3662c4b619b7 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 12 Nov 2006 03:26:25 +0000 Subject: [PATCH] revert. Need a better approach. --- stepmania/src/Course.cpp | 4 +- stepmania/src/CourseEntryDisplay.cpp | 2 +- stepmania/src/PlayerOptions.cpp | 2 +- stepmania/src/ScreenEdit.cpp | 71 ++++------- stepmania/src/ScreenEdit.h | 5 +- stepmania/src/ScreenGameplay.cpp | 9 +- stepmania/src/Song.cpp | 122 ++++--------------- stepmania/src/SongManager.cpp | 169 ++++++++++++++++++++++++++- stepmania/src/SongManager.h | 3 + stepmania/src/StepsUtil.h | 10 -- stepmania/src/Trail.cpp | 21 +--- stepmania/src/Trail.h | 13 +-- stepmania/src/song.h | 18 +-- 13 files changed, 239 insertions(+), 210 deletions(-) diff --git a/stepmania/src/Course.cpp b/stepmania/src/Course.cpp index 431f190b38..d2bdcf3886 100644 --- a/stepmania/src/Course.cpp +++ b/stepmania/src/Course.cpp @@ -349,7 +349,7 @@ bool Course::GetTrailSorted( StepsType st, CourseDifficulty cd, Trail &trail ) c { SortTrailEntry ste; ste.entry = trail.m_vEntries[i]; - ste.SortMeter = SortTrail.m_vEntries[i].GetSteps()->GetMeter(); + ste.SortMeter = SortTrail.m_vEntries[i].pSteps->GetMeter(); entries.push_back( ste ); } @@ -567,7 +567,7 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) TrailEntry te; te.pSong = resolved.pSong; - te.SetSteps(resolved.pSteps); + te.pSteps = resolved.pSteps; te.Modifiers = e->sModifiers; te.Attacks = e->attacks; te.bSecret = e->bSecret; diff --git a/stepmania/src/CourseEntryDisplay.cpp b/stepmania/src/CourseEntryDisplay.cpp index c4199a60fd..db1718462c 100644 --- a/stepmania/src/CourseEntryDisplay.cpp +++ b/stepmania/src/CourseEntryDisplay.cpp @@ -173,7 +173,7 @@ void CourseEntryDisplay::SetFromGameState( int iCourseEntryIndex ) const TrailEntry *te = tes[pn]; if( te == NULL ) continue; - SetDifficulty( pn, ssprintf("%d", te->GetSteps()->GetMeter()), te->GetSteps()->GetDifficulty() ); + SetDifficulty( pn, ssprintf("%d", te->pSteps->GetMeter()), te->pSteps->GetDifficulty() ); } m_TextBanner.LoadFromSong( te->pSong ); diff --git a/stepmania/src/PlayerOptions.cpp b/stepmania/src/PlayerOptions.cpp index 15f03f6452..0593b1043d 100644 --- a/stepmania/src/PlayerOptions.cpp +++ b/stepmania/src/PlayerOptions.cpp @@ -630,7 +630,7 @@ bool PlayerOptions::IsEasierForCourseAndTrail( Course* pCourse, Trail* pTrail ) FOREACH_CONST( TrailEntry, pTrail->m_vEntries, e ) { - if( e->pSong && IsEasierForSongAndSteps(e->pSong, e->GetSteps(), PLAYER_1) ) + if( e->pSong && IsEasierForSongAndSteps(e->pSong, e->pSteps, PLAYER_1) ) return true; } return false; diff --git a/stepmania/src/ScreenEdit.cpp b/stepmania/src/ScreenEdit.cpp index fff167d7c9..44c8e16c69 100644 --- a/stepmania/src/ScreenEdit.cpp +++ b/stepmania/src/ScreenEdit.cpp @@ -769,6 +769,9 @@ void ScreenEdit::Init() ScreenEdit::~ScreenEdit() { + // UGLY: Don't delete the Song's steps. + m_songLastSave.DetachSteps(); + LOG->Trace( "ScreenEdit::~ScreenEdit()" ); m_soundMusic.StopPlaying(); } @@ -2577,6 +2580,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) else if( SM == SM_SaveSuccessful ) { LOG->Trace( "Save successful." ); + m_pSteps->SetSavedToDisk( true ); CopyToLastSave(); SetDirty( false ); @@ -2598,24 +2602,17 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) // IMPORTANT: CopyFromLastSave before deleting the Steps below CopyFromLastSave(); - // We have to scroll through the entire Steps array because the user - // may have changed to a different stepchart after starting the edit. - vector vStepsToDelete; - for( vector::const_iterator it = GAMESTATE->m_pCurSong->GetAllSteps().begin(); - it != GAMESTATE->m_pCurSong->GetAllSteps().end(); ++it ) + // If these steps have never been saved, then we should delete them. + // If the user created them in the edit menu and never bothered + // to save them, then they aren't wanted. + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + if( !pSteps->GetSavedToDisk() ) { - Steps *pSteps = *it; - if( !pSteps->GetSavedToDisk() && !pSteps->IsAutogen() ) - { - vStepsToDelete.push_back( pSteps ); - if( pSteps == GAMESTATE->m_pCurSteps[PLAYER_1] ) - GAMESTATE->m_pCurSteps[PLAYER_1].Set( NULL ); - if( pSteps == m_pSteps ) - m_pSteps = NULL; - } - } - FOREACH( Steps*, vStepsToDelete, pSteps ) - GAMESTATE->m_pCurSong->DeleteSteps( *pSteps ); + Song* pSong = GAMESTATE->m_pCurSong; + pSong->DeleteSteps( pSteps ); + m_pSteps = NULL; + GAMESTATE->m_pCurSteps[PLAYER_1].Set( NULL ); + } m_Out.StartTransitioning( SM_GoToNextScreen ); } @@ -3432,23 +3429,16 @@ void ScreenEdit::SetupCourseAttacks() void ScreenEdit::CopyToLastSave() { - m_SongLastSave.DeepCopy( *GAMESTATE->m_pCurSong ); + m_songLastSave = *GAMESTATE->m_pCurSong; + if( GAMESTATE->m_pCurSteps[PLAYER_1] ) + m_stepsLastSave = *GAMESTATE->m_pCurSteps[PLAYER_1]; } void ScreenEdit::CopyFromLastSave() { - // We have to make sure the new m_pCurSteps pointer points to the correct Steps - // object. The Song copy destroys all of the old pointers. - StepsID id; - if( GAMESTATE->m_pCurSteps[PLAYER_1] ) - id.FromSteps( GAMESTATE->m_pCurSteps[PLAYER_1] ); - - GAMESTATE->m_pCurSong->DeepCopy( m_SongLastSave ); - - if( id.IsValid() ) { - GAMESTATE->m_pCurSteps[PLAYER_1].Set( id.ToSteps( GAMESTATE->m_pCurSong, false ) ); - m_pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - } + *GAMESTATE->m_pCurSong = m_songLastSave; + if( GAMESTATE->m_pCurSteps[PLAYER_1] ) + *GAMESTATE->m_pCurSteps[PLAYER_1] = m_stepsLastSave; } void ScreenEdit::RevertFromDisk() @@ -3458,25 +3448,14 @@ void ScreenEdit::RevertFromDisk() id.FromSteps( GAMESTATE->m_pCurSteps[PLAYER_1] ); RString sSongDir = GAMESTATE->m_pCurSong->GetSongDir(); - GAMESTATE->m_pCurSong->ReloadFromSongDir( sSongDir ); + GAMESTATE->m_pCurSong->LoadFromSongDir( sSongDir ); if( id.IsValid() ) - { - Steps *pChosenSteps = id.ToSteps( GAMESTATE->m_pCurSong, true ); - if( pChosenSteps == NULL ) - { - // This can happen in a couple ways. For example, the user might be editting a new - // stepchart and revert from disk. - pChosenSteps = new Steps(); - pChosenSteps->CreateBlank( id.GetStepsType() ); - pChosenSteps->SetDifficultyAndDescription( id.GetDifficulty(), id.GetDescription() ); - GAMESTATE->m_pCurSong->AddSteps( pChosenSteps ); - } - GAMESTATE->m_pCurSteps[PLAYER_1].Set( pChosenSteps ); - m_pSteps = pChosenSteps; - } + GAMESTATE->m_pCurSteps[PLAYER_1].Set( id.ToSteps( GAMESTATE->m_pCurSong, false ) ); - CopyToLastSave(); + m_songLastSave = *GAMESTATE->m_pCurSong; + if( GAMESTATE->m_pCurSteps[PLAYER_1] ) + m_stepsLastSave = *GAMESTATE->m_pCurSteps[PLAYER_1]; SetDirty(false); } diff --git a/stepmania/src/ScreenEdit.h b/stepmania/src/ScreenEdit.h index 6b65f3f994..38dc1640a8 100644 --- a/stepmania/src/ScreenEdit.h +++ b/stepmania/src/ScreenEdit.h @@ -253,8 +253,9 @@ protected: void CopyFromLastSave(); void RevertFromDisk(); - Song m_SongLastSave; - + Song m_songLastSave; + Steps m_stepsLastSave; + // for MODE_RECORD diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 71c5549251..fb520ded15 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -850,13 +850,8 @@ void ScreenGameplay::InitSongQueues() pi->m_asModifiersQueue.clear(); FOREACH_CONST( TrailEntry, pTrail->m_vEntries, e ) { - // Here we assume there are never any NULL Steps in any Trails. - // This assumption can fail if we delete a Steps from a - // Song or revert a Song where the stepchart was deleted - // ouside the editor. In those cases: goodbye cruel world. - // TODO: fix that somehow - ASSERT( e->GetSteps() ); - pi->m_vpStepsQueue.push_back( e->GetSteps() ); + ASSERT( e->pSteps ); + pi->m_vpStepsQueue.push_back( e->pSteps ); AttackArray a; e->GetAttackArray( a ); pi->m_asModifiersQueue.push_back( a ); diff --git a/stepmania/src/Song.cpp b/stepmania/src/Song.cpp index c9c93c6fd5..74b3512dbf 100644 --- a/stepmania/src/Song.cpp +++ b/stepmania/src/Song.cpp @@ -42,8 +42,9 @@ static Preference g_fMarathonVerSongSeconds( "MarathonVerSongSeconds", 60 Song::Song() { FOREACH_BackgroundLayer( i ) - m_BackgroundChanges[i] = new VBackgroundChange; - m_ForegroundChanges = new VBackgroundChange; + m_BackgroundChanges[i] = AutoPtrCopyOnWrite(new VBackgroundChange); + m_ForegroundChanges = AutoPtrCopyOnWrite(new VBackgroundChange); + m_LoadedFromProfile = ProfileSlot_Invalid; m_fMusicSampleStartSeconds = -1; @@ -67,96 +68,34 @@ Song::~Song() SAFE_DELETE( *s ); m_vpSteps.clear(); - FOREACH_BackgroundLayer( i ) - SAFE_DELETE( m_BackgroundChanges[i] ); - SAFE_DELETE( m_ForegroundChanges ); - // It's the responsibility of the owner of this Song to make sure // that all pointers to this Song and its Steps are invalidated. } -// Reset to an empty song. +void Song::DetachSteps() +{ + m_vpSteps.clear(); + FOREACH_StepsType( st ) + m_vpStepsByType[st].clear(); +} + +/* Reset to an empty song. */ void Song::Reset() { + FOREACH( Steps*, m_vpSteps, s ) + SAFE_DELETE( *s ); + m_vpSteps.clear(); + FOREACH_StepsType( st ) + m_vpStepsByType[st].clear(); + Song empty; - this->DeepCopy(empty); + *this = empty; + // It's the responsibility of the owner of this Song to make sure // that all pointers to this Song and its Steps are invalidated. } -void Song::DeepCopy(const Song &song) -{ - m_sSongFileName = song.m_sSongFileName; - m_sGroupName = song.m_sGroupName; - m_LoadedFromProfile = song.m_LoadedFromProfile; - m_bIsSymLink = song.m_bIsSymLink; - - m_sMainTitle = song.m_sMainTitle; - m_sSubTitle = song.m_sSubTitle; - m_sArtist = song.m_sArtist; - m_sMainTitleTranslit = song.m_sMainTitleTranslit; - m_sSubTitleTranslit = song.m_sSubTitleTranslit; - m_sArtistTranslit = song.m_sArtistTranslit; - - m_sGenre = song.m_sGenre; - m_sCredit = song.m_sCredit; - m_sMusicFile = song.m_sMusicFile; - - m_fMusicLengthSeconds = song.m_fMusicLengthSeconds; - m_fFirstBeat = song.m_fFirstBeat; - m_fLastBeat = song.m_fLastBeat; - m_fSpecifiedLastBeat = song.m_fSpecifiedLastBeat; - m_fMusicSampleStartSeconds = song.m_fMusicSampleStartSeconds; - m_fMusicSampleLengthSeconds = song.m_fMusicSampleLengthSeconds; - m_DisplayBPMType = song.m_DisplayBPMType; - m_fSpecifiedBPMMin = song.m_fSpecifiedBPMMin; - m_fSpecifiedBPMMax = song.m_fSpecifiedBPMMax; - - m_sBannerFile = song.m_sBannerFile; - m_sLyricsFile = song.m_sLyricsFile; - m_sBackgroundFile = song.m_sBackgroundFile; - m_sCDTitleFile = song.m_sCDTitleFile; - m_bHasMusic = song.m_bHasMusic; - m_bHasBanner = song.m_bHasBanner; - - m_Timing = song.m_Timing; - - FOREACH_BackgroundLayer( i ) - *(m_BackgroundChanges[i]) = *(song.m_BackgroundChanges[i]); - *m_ForegroundChanges = *song.m_ForegroundChanges; - - m_LyricSegments = song.m_LyricSegments; - - m_vsKeysoundFile = song.m_vsKeysoundFile; - - // First, get rid of all the old steps ... - FOREACH( Steps*, m_vpSteps, s ) - SAFE_DELETE( *s ); - m_vpSteps.clear(); - FOREACH_StepsType( i ) - m_vpStepsByType[i].clear(); - - // ... then create duplicates of the new steps, correctly arranging them in the m_vpStepsByType array - m_vpSteps.clear(); - for( vector::const_iterator it = song.m_vpSteps.begin(); it != song.m_vpSteps.end(); ++it ) - { - Steps *steps = new Steps( **it ); - m_vpSteps.push_back( steps ); - m_vpStepsByType[steps->m_StepsType].push_back( steps ); - } - - // TODO: This is horribly ugly. A much better solution is to make the Song class in charge of its - // own Steps cache. StepsID::ToSteps can simply reference the Song cache then. - StepsID::ClearCache(); - // TODO: we have also ruined the pointers for GAMESTATE->m_pCurSteps, STATSMAN->m_CurStageStats.m_player[].vpPlayedSteps, - // STATSMAN->m_vPlayedStageStats->m_player[].vpPlayedSteps, and GAMESTATE->m_pEditSourceSteps. Probably we've also - // ruined other pointers aside from those. In general, we need to use StepsIDs everywhere instead of Step*s. - // FOR NOW this is not a problem, as the only place this copy is used is in the editor. The editor does not need any of - // the various GAMESTATE or STATSMAN pointers except for m_pCurSteps, which it already takes care of on its own. - // However, we have to fix this if we are going to use DeepCopy anywhere else. - } - void Song::AddBackgroundChange( BackgroundLayer iLayer, BackgroundChange seg ) { // Delete old background change at this start beat, if any. @@ -234,7 +173,7 @@ static set BlacklistedImages; * * If true, check the directory hash and reload the song from scratch if it's changed. */ -bool Song::LoadFromSongDir( RString sDir, bool bIgnoreCache ) +bool Song::LoadFromSongDir( RString sDir ) { // LOG->Trace( "Song::LoadFromSongDir(%s)", sDir.c_str() ); ASSERT( sDir != "" ); @@ -257,7 +196,7 @@ bool Song::LoadFromSongDir( RString sDir, bool bIgnoreCache ) // First look in the cache for this song (without loading NoteData) // unsigned uCacheHash = SONGINDEX->GetCacheHash(m_sSongDir); - bool bUseCache = !bIgnoreCache; + bool bUseCache = true; if( !DoesFileExist(GetCacheFilePath()) ) bUseCache = false; if( !PREFSMAN->m_bFastLoad && GetHashForDirectory(m_sSongDir) != uCacheHash ) @@ -344,14 +283,6 @@ bool Song::LoadFromSongDir( RString sDir, bool bIgnoreCache ) return true; // do load this song } -bool Song::ReloadFromSongDir( RString sDir ) -{ - // If we are loading from the song dir a second time around, the song is filled - // with all sorts of data we no longer want. Get rid of it. - Reset(); - return LoadFromSongDir( sDir, true ); -} - static void GetImageDirListing( RString sPath, vector &AddTo, bool bReturnPathToo=false ) { GetDirListing( sPath + ".png", AddTo, false, bReturnPathToo ); @@ -812,12 +743,6 @@ void Song::Save() else FILEMAN->Remove( sOldPath ); } - - // Now we need to mark all of the stepcharts as "saved to disk". - FOREACH( Steps*, m_vpSteps, steps ) - { - (*steps)->SetSavedToDisk( true ); - } } @@ -1038,7 +963,7 @@ const vector &Song::GetBackgroundChanges( BackgroundLayer bl ) } vector &Song::GetBackgroundChanges( BackgroundLayer bl ) { - return *(m_BackgroundChanges[bl]); + return *(m_BackgroundChanges[bl].Get()); } const vector &Song::GetForegroundChanges() const @@ -1047,7 +972,7 @@ const vector &Song::GetForegroundChanges() const } vector &Song::GetForegroundChanges() { - return *m_ForegroundChanges; + return *m_ForegroundChanges.Get(); } @@ -1158,6 +1083,7 @@ void Song::DeleteSteps( const Steps* pSteps, bool bReAutoGen ) if( bReAutoGen ) RemoveAutoGenNotes(); + vector &vpSteps = m_vpStepsByType[pSteps->m_StepsType]; for( int j=vpSteps.size()-1; j>=0; j-- ) { diff --git a/stepmania/src/SongManager.cpp b/stepmania/src/SongManager.cpp index 36ac2a14d9..36e02b1d02 100644 --- a/stepmania/src/SongManager.cpp +++ b/stepmania/src/SongManager.cpp @@ -235,7 +235,7 @@ void SongManager::LoadStepManiaSongDir( RString sDir, LoadingWindow *ld ) ld->Paint(); } Song* pNewSong = new Song; - if( !pNewSong->LoadFromSongDir( sSongDirName, false ) ) // don't ignore cache + if( !pNewSong->LoadFromSongDir( sSongDirName ) ) { /* The song failed to load. */ delete pNewSong; @@ -277,7 +277,7 @@ void SongManager::LoadGroupSymLinks(RString sDir, RString sGroupFolder) RString sSymDestination = msdF.GetParam(0,1); // Should only be 1 vale¶m...period. Song* pNewSong = new Song; - if( !pNewSong->LoadFromSongDir( sSymDestination, false ) ) // don't ignore cache + if( !pNewSong->LoadFromSongDir( sSymDestination ) ) { delete pNewSong; // The song failed to load. } @@ -859,6 +859,169 @@ void SongManager::Cleanup() } } +/* Flush all Song*, Steps* and Course* caches. This is called on reload, and when + * any of those are removed or changed. This doesn't touch GAMESTATE and StageStats + * pointers, which are updated explicitly in Song::RevertFromDisk. */ +void SongManager::Invalidate( Song *pStaleSong ) +{ + // + // Save list of all old Course and Trail pointers + // + map mapOldCourseToCourseID; + typedef pair TrailIDAndCourse; + map mapOldTrailToTrailIDAndCourse; + FOREACH_CONST( Course*, this->m_pCourses, pCourse ) + { + CourseID id; + id.FromCourse( *pCourse ); + mapOldCourseToCourseID[*pCourse] = id; + vector Trails; + (*pCourse)->GetAllCachedTrails( Trails ); + FOREACH_CONST( Trail*, Trails, pTrail ) + { + TrailID id; + id.FromTrail( *pTrail ); + mapOldTrailToTrailIDAndCourse[*pTrail] = TrailIDAndCourse(id, *pCourse); + } + } + + // It's a real pain to selectively invalidate only those Courses with + // dependencies on the stale Song. So, instead, just reload all Courses. + // It doesn't take very long. + FreeCourses(); + InitCoursesFromDisk( NULL ); + InitAutogenCourses(); + + // invalidate cache + StepsID::ClearCache(); + +#define CONVERT_COURSE_POINTER( pCourse ) do { \ + CourseID id = mapOldCourseToCourseID[pCourse]; /* this will always succeed */ \ + pCourse = id.ToCourse(); \ +} while(false) + + /* Ugly: We need the course pointer to restore a trail pointer, and both have + * been invalidated. We need to go through our mapping, and update the course + * pointers, so we can use that to update trail pointers. */ + { + map::iterator it; + for( it = mapOldTrailToTrailIDAndCourse.begin(); it != mapOldTrailToTrailIDAndCourse.end(); ++it ) + { + TrailIDAndCourse &tidc = it->second; + CONVERT_COURSE_POINTER( tidc.second ); + } + } + + { + CourseID id = mapOldCourseToCourseID[GAMESTATE->m_pCurCourse]; /* this will always succeed */ + GAMESTATE->m_pCurCourse.Set( id.ToCourse() ); + } + CONVERT_COURSE_POINTER( GAMESTATE->m_pPreferredCourse ); + +#define CONVERT_TRAIL_POINTER( pTrail ) do { \ + if( pTrail != NULL ) { \ + map::iterator it; \ + it = mapOldTrailToTrailIDAndCourse.find(pTrail); \ + ASSERT_M( it != mapOldTrailToTrailIDAndCourse.end(), ssprintf("%p", pTrail.Get()) ); \ + const TrailIDAndCourse &tidc = it->second; \ + const TrailID &id = tidc.first; \ + const Course *pCourse = tidc.second; \ + pTrail.Set( id.ToTrail( pCourse, true ) ); \ + } \ +} while(false) + + FOREACH_PlayerNumber( pn ) + { + CONVERT_TRAIL_POINTER( GAMESTATE->m_pCurTrail[pn] ); + } +} + +/* If bAllowNotesLoss is true, any global notes pointers which no longer exist + * (or exist but couldn't be matched) will be set to NULL. This is used when + * reverting out of the editor. If false, this is unexpected and will assert. + * This is used when reverting out of gameplay, in which case we may have StageStats, + * etc. which may cause hard-to-trace crashes down the line if we set them to NULL. */ +void CONVERT_STEPS_POINTER( Steps *&pSteps, const map &mapOldStepsToStepsID, const Song *pSong, bool bAllowNotesLoss ) +{ + if( pSteps == NULL ) + return; + + map::const_iterator it = mapOldStepsToStepsID.find(pSteps); + if( it != mapOldStepsToStepsID.end() ) + pSteps = it->second.ToSteps(pSong, bAllowNotesLoss); +} +void CONVERT_STEPS_POINTER( BroadcastOnChangePtr &pSteps, const map &mapOldStepsToStepsID, const Song *pSong, bool bAllowNotesLoss ) +{ + if( pSteps == NULL ) + return; + + map::const_iterator it = mapOldStepsToStepsID.find(pSteps); + if( it != mapOldStepsToStepsID.end() ) + pSteps.Set( it->second.ToSteps(pSong, bAllowNotesLoss) ); +} +void SongManager::RevertFromDisk( Song *pSong, bool bAllowNotesLoss ) +{ + /* Reverting from disk is brittle, and touches a lot of tricky and rarely- + * used code paths. If it's ever used during a game, log it. */ + LOG->MapLog( "RevertFromDisk", "Reverted \"%s\" from disk", pSong->GetTranslitMainTitle().c_str() ); + + // Ugly: When we re-load the song, the Steps* will change. + // Fix GAMESTATE->m_CurSteps, STATSMAN->m_CurStageStats, STATSMAN->m_vPlayedStageStats[] after reloading. + /* XXX: This is very brittle. However, we must know about all globals uses of Steps*, + * so we can check to make sure we didn't lose any steps which are referenced ... */ + + + // + // Save list of all old Steps pointers for the song + // + map mapOldStepsToStepsID; + FOREACH_CONST( Steps*, pSong->GetAllSteps(), pSteps ) + { + StepsID id; + id.FromSteps( *pSteps ); + mapOldStepsToStepsID[*pSteps] = id; + } + + + // + // Reload the song + // + const RString dir = pSong->GetSongDir(); + FILEMAN->FlushDirCache( dir ); + + /* Erase existing data and reload. */ + pSong->Reset(); + const bool OldVal = PREFSMAN->m_bFastLoad; + PREFSMAN->m_bFastLoad.Set( false ); + pSong->LoadFromSongDir( dir ); + /* XXX: reload edits? */ + PREFSMAN->m_bFastLoad.Set( OldVal ); + + + /* Courses cache Steps pointers. On the off chance that this isn't the last + * thing this screen does, clear that cache. */ + /* TODO: Don't make Song depend on SongManager. This is breaking + * encapsulation and placing confusing limitation on what can be done in + * SONGMAN->Invalidate(). -Chris */ + this->Invalidate( pSong ); + StepsID::ClearCache(); + + + FOREACH_PlayerNumber( p ) + { + CONVERT_STEPS_POINTER( GAMESTATE->m_pCurSteps[p], mapOldStepsToStepsID, pSong, bAllowNotesLoss ); + + FOREACH( Steps*, STATSMAN->m_CurStageStats.m_player[p].vpPlayedSteps, pSteps ) + CONVERT_STEPS_POINTER( *pSteps, mapOldStepsToStepsID, pSong, bAllowNotesLoss ); + + FOREACH( StageStats, STATSMAN->m_vPlayedStageStats, ss ) + FOREACH( Steps*, ss->m_player[p].vpPlayedSteps, pSteps ) + CONVERT_STEPS_POINTER( *pSteps, mapOldStepsToStepsID, pSong, bAllowNotesLoss ); + } + + CONVERT_STEPS_POINTER( GAMESTATE->m_pEditSourceSteps, mapOldStepsToStepsID, pSong, bAllowNotesLoss ); +} + void SongManager::RegenerateNonFixedCourses() { for( unsigned i=0; i < m_pCourses.size(); i++ ) @@ -981,7 +1144,7 @@ bool SongManager::GetExtraStageInfoFromCourse( bool bExtra2, RString sPreferredG } pSongOut = pTrail->m_vEntries[0].pSong; - pStepsOut = pTrail->m_vEntries[0].GetSteps(); + pStepsOut = pTrail->m_vEntries[0].pSteps; return true; } diff --git a/stepmania/src/SongManager.h b/stepmania/src/SongManager.h index 786903ac93..bfc3a5fc18 100644 --- a/stepmania/src/SongManager.h +++ b/stepmania/src/SongManager.h @@ -33,6 +33,9 @@ public: void FreeSongs(); void Cleanup(); + void Invalidate( Song *pStaleSong ); + void RevertFromDisk( Song *pSong, bool bAllowNotesLoss=false ); + void RegenerateNonFixedCourses(); void SetPreferences(); diff --git a/stepmania/src/StepsUtil.h b/stepmania/src/StepsUtil.h index a28bac3c40..7e77a73df0 100644 --- a/stepmania/src/StepsUtil.h +++ b/stepmania/src/StepsUtil.h @@ -69,7 +69,6 @@ namespace StepsUtil class StepsID { -private: StepsType st; Difficulty dc; RString sDescription; @@ -90,15 +89,6 @@ public: static void ClearCache(); StepsType GetStepsType() const { return st; } - Difficulty GetDifficulty() const { return dc; } - RString GetDescription() const { return sDescription; } - - bool operator==( const StepsID &other ) const - { -#define X(x) (x == other.x) - return X(st) && X(dc) && X(sDescription) && X(uHash); -#undef X - } }; #endif diff --git a/stepmania/src/Trail.cpp b/stepmania/src/Trail.cpp index a7ef607539..e7bcd661d9 100644 --- a/stepmania/src/Trail.cpp +++ b/stepmania/src/Trail.cpp @@ -21,7 +21,7 @@ bool TrailEntry::operator== ( const TrailEntry &rhs ) const #define EQUAL(a) (a==rhs.a) return EQUAL(pSong) && - EQUAL(m_StepsID) && + EQUAL(pSteps) && EQUAL(Modifiers) && EQUAL(Attacks) && EQUAL(bSecret) && @@ -30,21 +30,6 @@ bool TrailEntry::operator== ( const TrailEntry &rhs ) const EQUAL(dc); } -Steps *TrailEntry::GetSteps() const -{ - // TODO: There are a few bugs with the way we do trails right now. - // - If a steps (or an entire song) is deleted, possibly through the editor, - // this section will fail. - // - If the editor changes a stepchart's stats, that should also be reflected - // in the trail. - return m_StepsID.ToSteps( pSong, false ); -} - -void TrailEntry::SetSteps(Steps *steps) -{ - m_StepsID.FromSteps( steps ); -} - bool TrailEntry::ContainsTransformOrTurn() const { PlayerOptions po; @@ -83,7 +68,7 @@ const RadarValues &Trail::GetRadarValues() const FOREACH_CONST( TrailEntry, m_vEntries, e ) { - const Steps *pSteps = e->GetSteps(); + const Steps *pSteps = e->pSteps; ASSERT( pSteps ); /* Hack: don't calculate for autogen entries; it makes writing Catalog.xml * take way too long. (Tournamix 4 Sample.crs takes me ~10s.) */ @@ -135,7 +120,7 @@ int Trail::GetTotalMeter() const int iTotalMeter = 0; FOREACH_CONST( TrailEntry, m_vEntries, e ) { - iTotalMeter += e->GetSteps()->GetMeter(); + iTotalMeter += e->pSteps->GetMeter(); } return iTotalMeter; diff --git a/stepmania/src/Trail.h b/stepmania/src/Trail.h index 156552909b..55e4d74c90 100644 --- a/stepmania/src/Trail.h +++ b/stepmania/src/Trail.h @@ -4,9 +4,8 @@ #define TRAIL_H #include "Attack.h" -#include "Difficulty.h" #include "RadarValues.h" -#include "StepsUtil.h" +#include "Difficulty.h" class Song; class Steps; @@ -14,22 +13,19 @@ struct lua_State; struct TrailEntry { -public: TrailEntry(): pSong(NULL), + pSteps(NULL), bSecret(false), iLowMeter(-1), iHighMeter(-1), dc(Difficulty_Invalid) { } - void GetAttackArray( AttackArray &out ) const; - Steps* GetSteps() const; - void SetSteps(Steps *steps); - bool ContainsTransformOrTurn() const; + void GetAttackArray( AttackArray &out ) const; Song* pSong; - StepsID m_StepsID; + Steps* pSteps; RString Modifiers; AttackArray Attacks; bool bSecret; // show "???" @@ -41,6 +37,7 @@ public: Difficulty dc; bool operator== ( const TrailEntry &rhs ) const; bool operator!= ( const TrailEntry &rhs ) const { return !(*this==rhs); } + bool ContainsTransformOrTurn() const; }; class Trail diff --git a/stepmania/src/song.h b/stepmania/src/song.h index 7dfa2e6bbe..09618fa07d 100644 --- a/stepmania/src/song.h +++ b/stepmania/src/song.h @@ -58,15 +58,7 @@ public: void Reset(); void DetachSteps(); - // WARNING: Do not use this DeepCopy in any situation with existing Steps*. Those pointers - // will all point to garbage memory after this call. The way to fix that is to change the - // pointers to StepsIDs or in some other way avoid keeping the Steps* pointers from before - // the call to DeepCopy. - void DeepCopy(const Song &song); - - // LoadFromSongDir assumes the song is uninitialized. Call ReloadFromSongDir if that is not true. - bool LoadFromSongDir( RString sDir, bool bIgnoreCache ); - bool ReloadFromSongDir( RString sDir ); + bool LoadFromSongDir( RString sDir ); void TidyUpData(); // call after loading to clean up invalid data void ReCalculateRadarValuesAndLastBeat(); // called by TidyUpData, and after saving @@ -160,8 +152,9 @@ public: typedef vector VBackgroundChange; private: - VBackgroundChange * m_BackgroundChanges[NUM_BackgroundLayer]; // these must be sorted before gameplay - VBackgroundChange * m_ForegroundChanges; // this must be sorted before gameplay + // AutoPtr instead of raw pointer so that the auto gen'd copy constructor works correctly. + AutoPtrCopyOnWrite m_BackgroundChanges[NUM_BackgroundLayer]; // these must be sorted before gameplay + AutoPtrCopyOnWrite m_ForegroundChanges; // this must be sorted before gameplay public: const vector &GetBackgroundChanges( BackgroundLayer bl ) const; vector &GetBackgroundChanges( BackgroundLayer bl ); @@ -223,9 +216,6 @@ public: void PushSelf( lua_State *L ); private: - // DISALLOW operator= and Steps(Steps&) - Song(const Song&); - void operator=(const Song&); vector m_vpSteps; vector m_vpStepsByType[NUM_StepsType];