diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index 0907970741..11d91d5226 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -73,7 +73,7 @@ void SMLoader::LoadFromTokens( Trim( sDifficulty ); Trim( sNoteData ); - // LOG->Trace( "Steps::LoadFromTokens()" ); + // LOG->Trace( "Steps::LoadFromTokens(), %s", sStepsType.c_str() ); // backwards compatibility hacks: // HACK: We eliminated "ez2-single-hard", but we should still handle it. @@ -85,6 +85,7 @@ void SMLoader::LoadFromTokens( sStepsType = "para-single"; out.m_StepsType = GAMEMAN->StringToStepsType( sStepsType ); + out.m_StepsTypeStr = sStepsType; out.SetDescription( sDescription ); out.SetCredit( sDescription ); // this is often used for both. out.SetChartName(sDescription); // yeah, one more for good measure. diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 9f4be80a2c..5f1d084dfb 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -643,6 +643,7 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach if( sValueName=="STEPSTYPE" ) { pNewNotes->m_StepsType = GAMEMAN->StringToStepsType( sParams[1] ); + pNewNotes->m_StepsTypeStr= sParams[1]; } else if( sValueName=="CHARTSTYLE" ) @@ -965,6 +966,7 @@ bool SSCLoader::LoadEditFromMsd(const MsdFile &msd, if( sValueName=="STEPSTYPE" ) { pNewNotes->m_StepsType = GAMEMAN->StringToStepsType( sParams[1] ); + pNewNotes->m_StepsTypeStr= sParams[1]; bSSCFormat = true; } diff --git a/src/NotesWriterSM.cpp b/src/NotesWriterSM.cpp index 8f131b9210..504b3ebafe 100644 --- a/src/NotesWriterSM.cpp +++ b/src/NotesWriterSM.cpp @@ -206,9 +206,9 @@ static RString GetSMNotesTag( const Song &song, const Steps &in ) lines.push_back( "" ); // Escape to prevent some clown from making a comment of "\r\n;" lines.push_back( ssprintf("//---------------%s - %s----------------", - GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) ); + in.m_StepsTypeStr.c_str(), SmEscape(in.GetDescription()).c_str()) ); lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" ); - lines.push_back( ssprintf( " %s:", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) ); + lines.push_back( ssprintf( " %s:", in.m_StepsTypeStr.c_str() ) ); RString desc = (USE_CREDIT ? in.GetCredit() : in.GetChartName()); lines.push_back( ssprintf( " %s:", SmEscape(desc).c_str() ) ); lines.push_back( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index 3bb8fdfa73..1f5971f149 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -349,10 +349,10 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa lines.push_back( "" ); // Escape to prevent some clown from making a comment of "\r\n;" lines.push_back( ssprintf("//---------------%s - %s----------------", - GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) ); + in.m_StepsTypeStr.c_str(), SmEscape(in.GetDescription()).c_str()) ); lines.push_back( "#NOTEDATA:;" ); // our new separator. lines.push_back( ssprintf( "#CHARTNAME:%s;", SmEscape(in.GetChartName()).c_str())); - lines.push_back( ssprintf( "#STEPSTYPE:%s;", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) ); + lines.push_back( ssprintf( "#STEPSTYPE:%s;", in.m_StepsTypeStr.c_str() ) ); lines.push_back( ssprintf( "#DESCRIPTION:%s;", SmEscape(in.GetDescription()).c_str() ) ); lines.push_back( ssprintf( "#CHARTSTYLE:%s;", SmEscape(in.GetChartStyle()).c_str() ) ); lines.push_back( ssprintf( "#DIFFICULTY:%s;", DifficultyToString(in.GetDifficulty()).c_str() ) ); diff --git a/src/Song.cpp b/src/Song.cpp index 9d1495a681..27c20aa76f 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -42,7 +42,7 @@ * @brief The internal version of the cache for StepMania. * * Increment this value to invalidate the current cache. */ -const int FILE_CACHE_VERSION = 220; +const int FILE_CACHE_VERSION = 221; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; @@ -89,6 +89,11 @@ Song::~Song() FOREACH( Steps*, m_vpSteps, s ) SAFE_DELETE( *s ); m_vpSteps.clear(); + FOREACH(Steps*, m_UnknownStyleSteps, s) + { + SAFE_DELETE(*s); + } + m_UnknownStyleSteps.clear(); // It's the responsibility of the owner of this Song to make sure // that all pointers to this Song and its Steps are invalidated. @@ -99,6 +104,7 @@ void Song::DetachSteps() m_vpSteps.clear(); FOREACH_ENUM( StepsType, st ) m_vpStepsByType[st].clear(); + m_UnknownStyleSteps.clear(); } float Song::GetFirstSecond() const @@ -154,6 +160,11 @@ void Song::Reset() m_vpSteps.clear(); FOREACH_ENUM( StepsType, st ) m_vpStepsByType[st].clear(); + FOREACH(Steps*, m_UnknownStyleSteps, s) + { + SAFE_DELETE(*s); + } + m_UnknownStyleSteps.clear(); Song empty; *this = empty; @@ -1023,6 +1034,10 @@ bool Song::SaveToSMFile() vpStepsToSave.push_back( pSteps ); } + FOREACH_CONST(Steps*, m_UnknownStyleSteps, s) + { + vpStepsToSave.push_back(*s); + } return NotesWriterSM::Write( sPath, *this, vpStepsToSave ); @@ -1055,6 +1070,10 @@ bool Song::SaveToSSCFile( RString sPath, bool bSavingCache ) pSteps->SetFilename(path); vpStepsToSave.push_back( pSteps ); } + FOREACH_CONST(Steps*, m_UnknownStyleSteps, s) + { + vpStepsToSave.push_back(*s); + } if (bSavingCache) { @@ -1504,9 +1523,19 @@ RString Song::GetTranslitFullTitle() const void Song::AddSteps( Steps* pSteps ) { - m_vpSteps.push_back( pSteps ); - ASSERT_M( pSteps->m_StepsType < NUM_StepsType, ssprintf("%i", pSteps->m_StepsType) ); - m_vpStepsByType[pSteps->m_StepsType].push_back( pSteps ); + // Songs of unknown stepstype are saved as a forwards compatibility feature + // so that editing a simfile made by a future version that has a new style + // won't delete those steps. -Kyz + if(pSteps->m_StepsType != StepsType_Invalid) + { + m_vpSteps.push_back( pSteps ); + ASSERT_M( pSteps->m_StepsType < NUM_StepsType, ssprintf("%i", pSteps->m_StepsType) ); + m_vpStepsByType[pSteps->m_StepsType].push_back( pSteps ); + } + else + { + m_UnknownStyleSteps.push_back(pSteps); + } } void Song::DeleteSteps( const Steps* pSteps, bool bReAutoGen ) diff --git a/src/Song.h b/src/Song.h index 65486e7327..d786cb1b90 100644 --- a/src/Song.h +++ b/src/Song.h @@ -447,6 +447,8 @@ private: vector m_vpSteps; /** @brief the Steps of a particular StepsType that belong to this Song. */ vector m_vpStepsByType[NUM_StepsType]; + /** @brief the Steps that are of unrecognized Styles. */ + vector m_UnknownStyleSteps; }; #endif diff --git a/src/Steps.cpp b/src/Steps.cpp index 247c651e67..1b672ffb2d 100644 --- a/src/Steps.cpp +++ b/src/Steps.cpp @@ -258,8 +258,16 @@ float Steps::PredictMeter() const void Steps::TidyUpData() { + // Don't set the StepsType to dance single if it's invalid. That just + // causes unrecognized charts to end up where they don't belong. + // Leave it as StepsType_Invalid so the Song can handle it specially. This + // is a forwards compatibility feature, so that if a future version adds a + // new style, editing a simfile with unrecognized Steps won't silently + // delete them. -Kyz if( m_StepsType == StepsType_Invalid ) - m_StepsType = StepsType_dance_single; + { + LOG->Warn("Detected steps with unknown style '%s' in '%s'", m_StepsTypeStr.c_str(), m_pSong->m_sSongFileName.c_str()); + } if( GetDifficulty() == Difficulty_Invalid ) SetDifficulty( StringToDifficulty(GetDescription()) ); @@ -465,12 +473,14 @@ void Steps::AutogenFrom( const Steps *parent_, StepsType ntTo ) { parent = parent_; m_StepsType = ntTo; + m_StepsTypeStr= GAMEMAN->GetStepsTypeInfo(ntTo).szName; m_Timing = parent->m_Timing; } void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds ) // pSource does not have to be of the same StepsType { m_StepsType = ntTo; + m_StepsTypeStr= GAMEMAN->GetStepsTypeInfo(ntTo).szName; NoteData noteData; pSource->GetNoteData( noteData ); noteData.SetNumTracks( GAMEMAN->GetStepsTypeInfo(ntTo).iNumTracks ); diff --git a/src/Steps.h b/src/Steps.h index 178a0ab725..05440011b6 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -177,6 +177,8 @@ public: void PushSelf( lua_State *L ); StepsType m_StepsType; + /** @brief The string form of the StepsType, for dealing with unrecognized styles. */ + RString m_StepsTypeStr; /** @brief The Song these Steps are associated with */ Song *m_pSong;