diff --git a/src/Song.cpp b/src/Song.cpp index 5a93c27078..38d93f7f96 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -258,7 +258,7 @@ static set BlacklistedImages; bool Song::LoadFromSongDir( RString sDir ) { // LOG->Trace( "Song::LoadFromSongDir(%s)", sDir.c_str() ); - ASSERT( sDir != "" ); + ASSERT_M( sDir != "", "Songs can't be loaded from an empty directory!" ); // make sure there is a trailing slash at the end of sDir if( sDir.Right(1) != "/" ) @@ -321,10 +321,14 @@ bool Song::LoadFromSongDir( RString sDir ) LOG->UserLog( "Song", sDir, "has no music file either. Ignoring this song directory." ); return false; } - + // Make sure we have a future filename figured out. + vector folders; + split(sDir, "/", folders); + RString songName = folders[2] + ".ssc"; + this->m_sSongFileName = sDir + songName; // Continue on with a blank Song so that people can make adjustments using the editor. } - TidyUpData(); + TidyUpData(false, true); // save a cache file so we don't have to parse it all over again next time if( !SaveToCacheFile() ) @@ -333,8 +337,6 @@ bool Song::LoadFromSongDir( RString sDir ) FOREACH( Steps*, m_vpSteps, s ) { - (*s)->SetFilename( sCacheFilePath ); - /* Compress all Steps. During initial caching, this will remove cached * NoteData; during cached loads, this will just remove cached SMData. */ (*s)->Compress(); @@ -446,7 +448,7 @@ void FixupPath( RString &path, const RString &sSongPath ) } // Songs in BlacklistImages will never be autodetected as song images. -void Song::TidyUpData( bool bFromCache ) +void Song::TidyUpData( bool fromCache, bool duringCache ) { // We need to do this before calling any of HasMusic, HasHasCDTitle, etc. ASSERT_M( m_sSongDir.Left(3) != "../", m_sSongDir ); // meaningless @@ -543,7 +545,7 @@ void Song::TidyUpData( bool bFromCache ) /* Generate these before we autogen notes, so the new notes can inherit * their source's values. */ - ReCalculateRadarValuesAndLastSecond( bFromCache ); + ReCalculateRadarValuesAndLastSecond( fromCache, true ); Trim( m_sMainTitle ); Trim( m_sSubTitle ); @@ -779,11 +781,11 @@ void Song::TidyUpData( bool bFromCache ) * music starts. */ if( arrayPossibleMovies.size() == 1 ) this->AddBackgroundChange(BACKGROUND_LAYER_1, - BackgroundChange(0, - arrayPossibleMovies[0], - "", - 1.f, - SBE_StretchNoLoop)); + BackgroundChange(0, + arrayPossibleMovies[0], + "", + 1.f, + SBE_StretchNoLoop)); } @@ -791,64 +793,6 @@ void Song::TidyUpData( bool bFromCache ) * for edits). We should be able to use difficulty names as unique * identifiers for steps. */ SongUtil::AdjustDuplicateSteps( this ); - - { - /* Generated filename; this doesn't always point to a loadable file, - * but instead points to the file we should write changed files to, - * and will always be a .SSC. - * - * This is a little tricky. We can't always use the song title directly, - * since it might contain characters we can't store in filenames. Two - * easy options: we could manually filter out invalid characters, or we - * could use the name of the directory, which is always a valid filename - * and should always be the same as the song. The former might not catch - * everything--filename restrictions are platform-specific; we might even - * be on an 8.3 filesystem, so let's do the latter. - * - * We can't rely on searching for other data filenames; it works for DWIs, - * but not KSFs and BMSs. - * - * So, let's do this (by priority): - * 1. If there's a .SSC file, use that filename. No reason to use anything - * else; it's the filename in use. - * 2. If there's an .SM, use it with a changed extension. - * 3. If there's a .DWI, use it with a changed extension. - * 4. Otherwise, use the name of the directory, since it's definitely a valid - * filename, and should always be the title of the song (unlike KSFs). */ - m_sSongFileName = m_sSongDir; - vector asFileNames; - do - { - GetDirListing( m_sSongDir+"*.ssc", asFileNames ); - if( !asFileNames.empty() ) - { - m_sSongFileName += asFileNames[0]; - break; - } - - GetDirListing( m_sSongDir+"*.sma", asFileNames ); - if (!asFileNames.empty() ) - { - m_sSongFileName += SetExtension( asFileNames[0], "ssc" ); - break; - } - - GetDirListing( m_sSongDir+"*.sm", asFileNames ); - if( !asFileNames.empty() ) - { - m_sSongFileName += SetExtension( asFileNames[0], "ssc" ); - break; - } - - GetDirListing( m_sSongDir+"*.dwi", asFileNames ); - if( !asFileNames.empty() ) { - m_sSongFileName += SetExtension( asFileNames[0], "ssc" ); - break; - } - m_sSongFileName += Basename(m_sSongDir); - m_sSongFileName += ".ssc"; - } while(0); - } } void Song::TranslateTitles() @@ -863,9 +807,9 @@ void Song::TranslateTitles() m_sMainTitleTranslit, m_sSubTitleTranslit, m_sArtistTranslit ); } -void Song::ReCalculateRadarValuesAndLastSecond( bool bFromCache ) +void Song::ReCalculateRadarValuesAndLastSecond(bool fromCache, bool duringCache) { - if( bFromCache && this->GetFirstSecond() >= 0 && this->GetLastSecond() > 0 ) + if( fromCache && this->GetFirstSecond() >= 0 && this->GetLastSecond() > 0 ) { // this is loaded from cache, then we just have to calculate the radar values. for( unsigned i=0; iCalculateRadarValues( m_fMusicLengthSeconds ); + // Must initialize before the gotos. + NoteData tempNoteData; + pSteps->GetNoteData( tempNoteData ); + // calculate lastSecond // If it's autogen, then first/last beat will come from the parent. if( pSteps->IsAutogen() ) - continue; + goto wipe_notedata; /* Don't calculate with edits unless the song only contains an edit * chart, like those in Mungyodance 3. Otherwise, edits installed on * the machine could extend the length of the song. */ if( pSteps->IsAnEdit() && m_vpSteps.size() > 1 ) - continue; + goto wipe_notedata; // Don't set first/last beat based on lights. They often start very // early and end very late. if( pSteps->m_StepsType == StepsType_lights_cabinet ) - continue; - - NoteData tempNoteData; - pSteps->GetNoteData( tempNoteData ); + continue; // no need to wipe this. /* Many songs have stray, empty song patterns. Ignore them, so they * don't force the first beat of the whole song to 0. */ - if( tempNoteData.GetLastRow() == 0 ) - continue; - - localFirst = min(localFirst, + if( tempNoteData.GetLastRow() != 0 ) + { + localFirst = min(localFirst, pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat())); - localLast = max(localLast, + localLast = max(localLast, pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat())); + } + wipe_notedata: + if (duringCache) + { + // update this when at that point. + } } this->firstSecond = localFirst; @@ -1005,11 +955,15 @@ bool Song::SaveToSMFile() bool Song::SaveToSSCFile( RString sPath, bool bSavingCache ) { - LOG->Trace( "Song::SaveToSSCFile('%s')", sPath.c_str() ); + RString path = sPath; + if (!bSavingCache) + path = SetExtension(sPath, "ssc"); + + LOG->Trace( "Song::SaveToSSCFile('%s')", path.c_str() ); // If the file exists, make a backup. - if( !bSavingCache && IsAFile(sPath) ) - FileCopy( sPath, sPath + ".old" ); + if( !bSavingCache && IsAFile(path) ) + FileCopy( path, path + ".old" ); vector vpStepsToSave; FOREACH_CONST( Steps*, m_vpSteps, s ) @@ -1022,16 +976,23 @@ bool Song::SaveToSSCFile( RString sPath, bool bSavingCache ) if( pSteps->WasLoadedFromProfile() ) continue; + if (!bSavingCache) + pSteps->SetFilename(path); vpStepsToSave.push_back( pSteps ); } + + if (bSavingCache) + { + return NotesWriterSSC::Write(path, *this, vpStepsToSave, bSavingCache); + } - if( !NotesWriterSSC::Write(sPath, *this, vpStepsToSave, bSavingCache) ) + if( !NotesWriterSSC::Write(path, *this, vpStepsToSave, bSavingCache) ) return false; - if( !bSavingCache && g_BackUpAllSongSaves.Get() ) + if( g_BackUpAllSongSaves.Get() ) { - RString sExt = GetExtension( sPath ); - RString sBackupFile = SetExtension( sPath, "" ); + RString sExt = GetExtension( path ); + RString sBackupFile = SetExtension( path, "" ); time_t cur_time; time( &cur_time ); @@ -1043,18 +1004,15 @@ bool Song::SaveToSSCFile( RString sPath, bool bSavingCache ) sBackupFile = SetExtension( sBackupFile, sExt ); sBackupFile += ssprintf( ".old" ); - if( FileCopy(sPath, sBackupFile) ) - LOG->Trace( "Backed up %s to %s", sPath.c_str(), sBackupFile.c_str() ); + if( FileCopy(path, sBackupFile) ) + LOG->Trace( "Backed up %s to %s", path.c_str(), sBackupFile.c_str() ); else - LOG->Trace( "Failed to back up %s to %s", sPath.c_str(), sBackupFile.c_str() ); + LOG->Trace( "Failed to back up %s to %s", path.c_str(), sBackupFile.c_str() ); } - if( !bSavingCache ) - { - // Mark these steps saved to disk. - FOREACH( Steps*, vpStepsToSave, s ) - (*s)->SetSavedToDisk( true ); - } + // Mark these steps saved to disk. + FOREACH( Steps*, vpStepsToSave, s ) + (*s)->SetSavedToDisk( true ); return true; } @@ -1063,13 +1021,7 @@ bool Song::SaveToCacheFile() { SONGINDEX->AddCacheIndex(m_sSongDir, GetHashForDirectory(m_sSongDir)); const RString sPath = GetCacheFilePath(); - if( !SaveToSSCFile(sPath, true) ) - return false; - - FOREACH( Steps*, m_vpSteps, pSteps ) - (*pSteps)->SetFilename( sPath ); - - return true; + return SaveToSSCFile(sPath, true); } bool Song::SaveToDWIFile() @@ -1295,6 +1247,45 @@ vector &Song::GetForegroundChanges() return *m_ForegroundChanges.Get(); } +vector Song::GetChangesToVectorString(const vector & changes) const +{ + vector ret; + FOREACH_CONST( BackgroundChange, changes, bgc ) + { + ret.push_back((*bgc).ToString()); + } + return ret; +} + +vector Song::GetBGChanges1ToVectorString() const +{ + return this->GetChangesToVectorString(this->GetBackgroundChanges(BACKGROUND_LAYER_1)); +} + +vector Song::GetBGChanges2ToVectorString() const +{ + return this->GetChangesToVectorString(this->GetBackgroundChanges(BACKGROUND_LAYER_2)); +} + +vector Song::GetFGChanges1ToVectorString() const +{ + return this->GetChangesToVectorString(this->GetForegroundChanges()); +} + +vector Song::GetInstrumentTracksToVectorString() const +{ + vector ret; + FOREACH_ENUM(InstrumentTrack, it) + { + if (this->HasInstrumentTrack(it)) + { + ret.push_back(InstrumentTrackToString(it) + + "=" + + this->m_sInstrumentTrackFile[it]); + } + } + return ret; +} RString GetSongAssetPath( RString sPath, const RString &sSongPath ) { diff --git a/src/Song.h b/src/Song.h index a75b740dcd..29a53ff942 100644 --- a/src/Song.h +++ b/src/Song.h @@ -91,14 +91,17 @@ public: // This one takes the effort to reuse Steps pointers as best as it can bool ReloadFromSongDir( RString sDir ); - /** @brief Call this after loading a song to clean up invalid data. */ - void TidyUpData( bool bFromCache = false ); + /** + * @brief Call this after loading a song to clean up invalid data. + * @param fromCache was this data loaded from the cache file? + * @param duringCache was this data loaded during the cache process? */ + void TidyUpData( bool fromCache = false, bool duringCache = false ); /** * @brief Get the new radar values, and determine the last second at the same time. * * This is called by TidyUpData, after saving the Song. */ - void ReCalculateRadarValuesAndLastSecond( bool bFromCache = false ); + void ReCalculateRadarValuesAndLastSecond(bool fromCache = false, bool duringCache = false); /** * @brief Translate any titles that aren't in english. * @@ -180,7 +183,6 @@ public: RString GetDisplaySubTitle() const; RString GetDisplayArtist() const; - // Returns the transliterated titles, if any; otherwise returns the main titles. /** * @brief Retrieve the transliterated title, or the main title if there is no translit. * @return the proper title. */ @@ -315,12 +317,20 @@ private: * * This must be sorted before gameplay. */ AutoPtrCopyOnWrite m_ForegroundChanges; + + vector GetChangesToVectorString(const vector & changes) const; public: const vector &GetBackgroundChanges( BackgroundLayer bl ) const; vector &GetBackgroundChanges( BackgroundLayer bl ); const vector &GetForegroundChanges() const; vector &GetForegroundChanges(); + vector GetBGChanges1ToVectorString() const; + vector GetBGChanges2ToVectorString() const; + vector GetFGChanges1ToVectorString() const; + + vector GetInstrumentTracksToVectorString() const; + /** * @brief The list of LyricSegments. * @@ -426,6 +436,11 @@ public: CachedObject m_CachedObject; + RString GetAttackString() const + { + return join(":", this->m_sAttackString); + } + // Lua void PushSelf( lua_State *L ); diff --git a/src/Steps.h b/src/Steps.h index 877fa1549d..f954e39d32 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -213,6 +213,11 @@ public: void GetDisplayBpms( DisplayBpms &addTo) const; + RString GetAttackString() const + { + return join(":", this->m_sAttackString); + } + private: inline const Steps *Real() const { return parent ? parent : this; } void DeAutogen( bool bCopyNoteData = true ); /* If this Steps is autogenerated, make it a real Steps. */