#include "stdafx.h" /* ----------------------------------------------------------------------------- File: Song.h Desc: Holds metadata for a song and the song's step data. Copyright (c) 2001-2002 by the persons listed below. All rights reserved. ----------------------------------------------------------------------------- */ #include "NoteMetadata.h" #include "RageUtil.h" #include "Song.h" #include // for fmod #include "RageHelper.h" #include "ErrorCatcher/ErrorCatcher.h" int CompareBPMSegments(const void *arg1, const void *arg2) { // arg1 and arg2 are of type Step** BPMSegment* seg1 = (BPMSegment*)arg1; BPMSegment* seg2 = (BPMSegment*)arg2; float score1 = seg1->m_fStartBeat; float score2 = seg2->m_fStartBeat; if( score1 == score2 ) return 0; else if( score1 < score2 ) return -1; else return 1; } void SortBPMSegmentsArray( CArray &arrayBPMSegments ) { qsort( arrayBPMSegments.GetData(), arrayBPMSegments.GetSize(), sizeof(BPMSegment), CompareBPMSegments ); } int CompareFreezeSegments(const void *arg1, const void *arg2) { // arg1 and arg2 are of type Step** FreezeSegment* seg1 = (FreezeSegment*)arg1; FreezeSegment* seg2 = (FreezeSegment*)arg2; float score1 = seg1->m_fStartBeat; float score2 = seg2->m_fStartBeat; if( score1 == score2 ) return 0; else if( score1 < score2 ) return -1; else return 1; } void SortFreezeSegmentsArray( CArray &arrayFreezeSegments ) { qsort( arrayFreezeSegments.GetData(), arrayFreezeSegments.GetSize(), sizeof(FreezeSegment), CompareFreezeSegments ); } ////////////////////////////// // Song ////////////////////////////// Song::Song() { m_bChangedSinceSave = false; m_fOffsetInSeconds = 0; m_fMusicSampleStartSeconds = m_fMusicSampleLengthSeconds = -1; m_dwMusicBytes = 0; } void Song::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut ) { // HELPER.Log( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime ); // This function is a nightmare. Don't even try to understand it. :-) fElapsedTime += m_fOffsetInSeconds; for( int i=0; i fSecondsInThisSegment ) { // this BPMSegement is not the current segment fElapsedTime -= fSecondsInThisSegment; } else { // this BPMSegment IS the current segment float fBeatEstimate = fStartBeatThisSegment + fElapsedTime*fBPS; for( int j=0; j 0.1f ) { GetBeatAndBPSFromElapsedTime( fElapsedTimeBestGuess, fBeatOut, fBPSOut ); if( fBeatOut > fBeat ) fElapsedTimeBestGuess -= fSecondsToMove; else fElapsedTimeBestGuess += fSecondsToMove; fSecondsToMove /= 2; } return fElapsedTimeBestGuess; } bool Song::LoadFromSongDir( CString sDir ) { HELPER.Log( "Song::LoadFromSongDir(%s)", sDir ); // make sure there is a trailing '\\' at the end of sDir if( sDir.Right(1) != "\\" ) sDir += "\\"; // save song dir m_sSongDir = sDir; // We are going to load from either a .song or .msd file. // If no .song files exist, then look for an .msd. CStringArray arrayBMSFileNames; GetDirListing( sDir + CString("*.bms"), arrayBMSFileNames ); int iNumBMSFiles = arrayBMSFileNames.GetSize(); CStringArray arrayDWIFileNames; GetDirListing( sDir + CString("*.dwi"), arrayDWIFileNames ); int iNumDWIFiles = arrayDWIFileNames.GetSize(); CStringArray arraySongFileNames; GetDirListing( sDir + CString("*.song"), arraySongFileNames ); int iNumSongFiles = arraySongFileNames.GetSize(); if( iNumSongFiles > 1 ) FatalError( "There is more than one DWI file in '%s'. Which one should I use?", sDir ); else if( iNumDWIFiles > 1 ) FatalError( "There is more than one DWI file in '%s'. Which one should I use?", sDir ); else if( iNumSongFiles == 1 ) LoadFromSMDir( sDir ); else if( iNumDWIFiles == 1 ) LoadFromDWIFile( sDir + arrayDWIFileNames[0] ); else if( iNumBMSFiles > 0 ) LoadFromBMSDir( sDir ); else FatalError( "Couldn't find any Song, BMS, or MSD files in '%s'", sDir ); return TRUE; } bool Song::LoadFromBMSDir( CString sDir ) { HELPER.Log( "Song::LoadFromBMSDir(%s)", sDir ); // make sure there is a trailing '\\' at the end of sDir if( sDir.Right(1) != "\\" ) sDir += "\\"; // save song dir m_sSongDir = sDir; // get group name CStringArray sDirectoryParts; split( m_sSongDir, "\\", sDirectoryParts, true ); m_sGroupName = sDirectoryParts[1]; CStringArray arrayBMSFileNames; GetDirListing( sDir + CString("*.bms"), arrayBMSFileNames ); if( arrayBMSFileNames.GetSize() == 0 ) FatalError( ssprintf("Couldn't find any BMS files in '%s'", sDir) ); // Load the Song info from the first BMS file. Silly BMS duplicates the song info in every // file. So, we read the song data from only the first BMS file and assume that the info // is identical for every BMS file in the directory. m_sSongFilePath = m_sSongDir + arrayBMSFileNames[0]; // load the NoteMetadata from the rest of the BMS files for( int i=0; i') value_data.Replace( "(ANOTHER)", "" ); value_data.Replace( "(BASIC)", "" ); value_data.Replace( "(MANIAC)", "" ); value_data.Replace( "", "" ); value_data.Replace( "", "" ); value_data.Replace( "", "" ); GetMainAndSubTitlesFromFullTitle( value_data, m_sMainTitle, m_sSubTitle ); } else if( -1 != value_name.Find("#artist") ) { m_sArtist = value_data; } else if( -1 != value_name.Find("#bpm") ) { BPMSegment new_seg; new_seg.m_fStartBeat = 0; new_seg.m_fBPM = (float)atof( value_data ); // add, then sort m_BPMSegments.Add( new_seg ); SortBPMSegmentsArray( m_BPMSegments ); HELPER.Log( "Inserting new BPM change at beat %f, BPM %f", new_seg.m_fStartBeat, new_seg.m_fBPM ); } else if( -1 != value_name.Find("#backbmp") ) { m_sBackgroundPath = m_sSongDir + value_data; } else if( -1 != value_name.Find("#wav") ) { m_sMusicPath = m_sSongDir + value_data; } else if( value_name.Left(1) == "#" && IsAnInt( value_name.Mid(1,3) ) && IsAnInt( value_name.Mid(4,2) ) ) // this is step or offset data. Looks like "#00705" { int iMeasureNo = atoi( value_name.Mid(1,3) ); int iTrackNum = atoi( value_name.Mid(4,2) ); CString sNoteData = value_data; CArray arrayNotes; for( int i=0; i dwLargestFileSoFar ) // we have a new leader! { dwLargestFileSoFar = this_size; sLargestFileSoFar = m_sSongDir + arrayPossibleBackgrounds[i]; } } if( sLargestFileSoFar != "" ) // we found a match m_sBackgroundPath = sLargestFileSoFar; } if( !DoesFileExist(GetBackgroundMoviePath()) ) { m_sBackgroundMoviePath = ""; CStringArray arrayPossibleBackgroundMovies; GetDirListing( m_sSongDir + CString("*.avi"), arrayPossibleBackgroundMovies ); GetDirListing( m_sSongDir + CString("*.mpg"), arrayPossibleBackgroundMovies ); GetDirListing( m_sSongDir + CString("*.mpeg"), arrayPossibleBackgroundMovies ); if( arrayPossibleBackgroundMovies.GetSize() > 0 ) // we found a match m_sBackgroundMoviePath = arrayPossibleBackgroundMovies[0]; } if( !DoesFileExist(GetCDTitlePath()) ) { m_sCDTitlePath = ""; } } void Song::GetNoteMetadatasThatMatchGameAndStyle( CString sGame, CString sStyle, CArray& arrayAddTo ) { for( int i=0; im_sGame && ( sStyle == pCurNoteMetadata->m_sStyle || (sStyle == "versus" && pCurNoteMetadata->m_sStyle == "single") ) ) { arrayAddTo.Add( pCurNoteMetadata ); } } } /* void Song::SaveOffsetChangeToDisk() { CString sDir, sFName, sExt; splitrelpath( GetSongFilePath(), sDir, sFName, sExt ); sExt.MakeLower(); if( sExt == "bms" ) { for( int i=0; i arrayNoteMetadatas; this->GetNoteMetadatasThatMatchGameAndStyle( sGame, sStyle, arrayNoteMetadatas ); SortNoteMetadataArrayByDifficultyClass( arrayNoteMetadatas ); for( int i=0; im_DifficultyClass == dc ) return pNoteMetadata->m_TopGrade; } return GRADE_NO_DATA; } ///////////////////////////////////// // Sorting ///////////////////////////////////// int CompareSongPointersByTitle(const void *arg1, const void *arg2) { Song* pSong1 = *(Song**)arg1; Song* pSong2 = *(Song**)arg2; CString sTitle1 = pSong1->GetMainTitle(); CString sTitle2 = pSong2->GetMainTitle(); CString sFilePath1 = pSong1->GetSongFilePath(); // this is unique among songs CString sFilePath2 = pSong2->GetSongFilePath(); CString sCompareString1 = sTitle1 + sFilePath1; CString sCompareString2 = sTitle2 + sFilePath2; return CompareCStringsAsc( (void*)&sCompareString1, (void*)&sCompareString2 ); } void SortSongPointerArrayByTitle( CArray &arraySongPointers ) { qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByTitle ); } int CompareSongPointersByBPM(const void *arg1, const void *arg2) { Song* pSong1 = *(Song**)arg1; Song* pSong2 = *(Song**)arg2; float fMinBPM1, fMaxBPM1, fMinBPM2, fMaxBPM2; pSong1->GetMinMaxBPM( fMinBPM1, fMaxBPM1 ); pSong2->GetMinMaxBPM( fMinBPM2, fMaxBPM2 ); CString sFilePath1 = pSong1->GetSongFilePath(); // this is unique among songs CString sFilePath2 = pSong2->GetSongFilePath(); if( fMaxBPM1 < fMaxBPM2 ) return -1; else if( fMaxBPM1 == fMaxBPM2 ) return CompareCStringsAsc( (void*)&sFilePath1, (void*)&sFilePath2 ); else return 1; } void SortSongPointerArrayByBPM( CArray &arraySongPointers ) { qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByBPM ); } int CompareSongPointersByArtist(const void *arg1, const void *arg2) { Song* pSong1 = *(Song**)arg1; Song* pSong2 = *(Song**)arg2; CString sArtist1 = pSong1->GetArtist(); CString sArtist2 = pSong2->GetArtist(); if( sArtist1 < sArtist2 ) return -1; else if( sArtist1 == sArtist2 ) return CompareSongPointersByTitle( arg1, arg2 ); else return 1; } void SortSongPointerArrayByArtist( CArray &arraySongPointers ) { qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByArtist ); } int CompareSongPointersByGroup(const void *arg1, const void *arg2) { Song* pSong1 = *(Song**)arg1; Song* pSong2 = *(Song**)arg2; CString sGroup1 = pSong1->GetGroupName(); CString sGroup2 = pSong2->GetGroupName(); if( sGroup1 < sGroup2 ) return -1; else if( sGroup1 == sGroup2 ) return CompareSongPointersByTitle( arg1, arg2 ); else return 1; } void SortSongPointerArrayByGroup( CArray &arraySongPointers ) { qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByGroup ); } int CompareSongPointersByMostPlayed(const void *arg1, const void *arg2) { Song* pSong1 = *(Song**)arg1; Song* pSong2 = *(Song**)arg2; int iNumTimesPlayed1 = pSong1->GetNumTimesPlayed(); int iNumTimesPlayed2 = pSong2->GetNumTimesPlayed(); if( iNumTimesPlayed1 < iNumTimesPlayed2 ) return -1; else if( iNumTimesPlayed1 == iNumTimesPlayed2 ) return CompareSongPointersByTitle( arg1, arg2 ); else return 1; } void SortSongPointerArrayByMostPlayed( CArray &arraySongPointers ) { qsort( arraySongPointers.GetData(), arraySongPointers.GetSize(), sizeof(Song*), CompareSongPointersByMostPlayed ); }