#include "global.h" #include "NotesLoaderSM.h" #include "GameManager.h" #include "RageException.h" #include "MsdFile.h" #include "RageLog.h" #include "RageUtil.h" #include "SongManager.h" void SMLoader::LoadFromSMTokens( CString sNotesType, CString sDescription, CString sDifficulty, CString sMeter, CString sRadarValues, CString sNoteData, CString sAttackData, Steps &out ) { TrimLeft(sNotesType); TrimRight(sNotesType); TrimLeft(sDescription); TrimRight(sDescription); TrimLeft(sDifficulty); TrimRight(sDifficulty); // LOG->Trace( "Steps::LoadFromSMTokens()" ); out.m_StepsType = GameManager::StringToNotesType(sNotesType); out.SetDescription(sDescription); out.SetDifficulty(StringToDifficulty( sDifficulty )); // HACK: We used to store SMANIAC as DIFFICULTY_HARD with special description. // Now, it has its own DIFFICULTY_CHALLENGE if( sDescription.CompareNoCase("smaniac") == 0 ) out.SetDifficulty( DIFFICULTY_CHALLENGE ); // HACK: We used to store CHALLENGE as DIFFICULTY_HARD with special description. // Now, it has its own DIFFICULTY_CHALLENGE if( sDescription.CompareNoCase("challenge") == 0 ) out.SetDifficulty( DIFFICULTY_CHALLENGE ); out.SetMeter(atoi(sMeter)); CStringArray saValues; split( sRadarValues, ",", saValues, true ); if( saValues.size() == NUM_RADAR_CATEGORIES ) for( int r=0; rWarn( "Couldn't load %s, \"%s\"", fn.c_str(), msd.GetError().c_str() ); return false; } out.m_sFile = fn; LoadTimingFromSMFile( msd, out ); return true; } void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out ) { out.m_fBeat0OffsetInSeconds = 0; out.m_BPMSegments.clear(); out.m_StopSegments.clear(); for( unsigned i=0; iTrace( "Song::LoadFromSMFile(%s)", sPath.c_str() ); MsdFile msd; bool bResult = msd.ReadFile( sPath ); if( !bResult ) RageException::Throw( "Error opening file '%s'.", sPath.c_str() ); out.m_Timing.m_sFile = sPath; LoadTimingFromSMFile( msd, out.m_Timing ); for( unsigned i=0; iTrace("Ignored #MUSICLENGTH (cache only)"); continue; } out.m_fMusicLengthSeconds = (float)atof( sParams[1] ); } else if( 0==stricmp(sValueName,"MUSICBYTES") ) ; /* ignore */ /* We calculate these. Some SMs in circulation have bogus values for * these, so make sure we always calculate it ourself. */ else if( 0==stricmp(sValueName,"FIRSTBEAT") ) { if(!FromCache) { LOG->Trace("Ignored #FIRSTBEAT (cache only)"); continue; } out.m_fFirstBeat = (float)atof( sParams[1] ); } else if( 0==stricmp(sValueName,"LASTBEAT") ) { if(!FromCache) { LOG->Trace("Ignored #LASTBEAT (cache only)"); continue; } out.m_fLastBeat = (float)atof( sParams[1] ); } else if( 0==stricmp(sValueName,"SONGFILENAME") ) { if( FromCache ) out.m_sSongFileName = sParams[1]; } else if( 0==stricmp(sValueName,"HASMUSIC") ) { if( FromCache ) out.m_bHasMusic = atoi( sParams[1] ) != 0; } else if( 0==stricmp(sValueName,"HASBANNER") ) { if( FromCache ) out.m_bHasBanner = atoi( sParams[1] ) != 0; } else if( 0==stricmp(sValueName,"SAMPLESTART") ) out.m_fMusicSampleStartSeconds = TimeToSeconds( sParams[1] ); else if( 0==stricmp(sValueName,"SAMPLELENGTH") ) out.m_fMusicSampleLengthSeconds = TimeToSeconds( sParams[1] ); else if( 0==stricmp(sValueName,"DISPLAYBPM") ) { // #DISPLAYBPM:[xxx][xxx:xxx]|[*]; if( sParams[1] == "*" ) out.m_DisplayBPMType = Song::DISPLAY_RANDOM; else { out.m_DisplayBPMType = Song::DISPLAY_SPECIFIED; out.m_fSpecifiedBPMMin = (float)atof( sParams[1] ); if( sParams[2].empty() ) out.m_fSpecifiedBPMMax = out.m_fSpecifiedBPMMin; else out.m_fSpecifiedBPMMax = (float)atof( sParams[2] ); } } else if( 0==stricmp(sValueName,"SELECTABLE") ) { if(!stricmp(sParams[1],"YES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; else if(!stricmp(sParams[1],"NO")) out.m_SelectionDisplay = out.SHOW_NEVER; else if(!stricmp(sParams[1],"ROULETTE")) out.m_SelectionDisplay = out.SHOW_ROULETTE; else LOG->Warn( "The song file '%s' has an unknown #SELECTABLE value, '%s'; ignored.", sPath.c_str(), sParams[1].c_str()); } else if( 0==stricmp(sValueName,"BGCHANGES") || 0==stricmp(sValueName,"ANIMATIONS") ) { CStringArray aBGChangeExpressions; split( sParams[1], ",", aBGChangeExpressions ); for( unsigned b=0; bTrace( "The song file '%s' is has %d fields in a #NOTES tag, but should have at least %d.", sPath.c_str(), iNumParams, 7 ); continue; } LoadFromSMTokens( sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], (iNumParams>=8)?sParams[7]:"", *pNewNotes); } else if( 0==stricmp(sValueName,"OFFSET") || 0==stricmp(sValueName,"BPMS") || 0==stricmp(sValueName,"STOPS") || 0==stricmp(sValueName,"FREEZES") ) ; else LOG->Trace( "Unexpected value named '%s'", sValueName.c_str() ); } return true; } bool SMLoader::LoadFromDir( CString sPath, Song &out ) { CStringArray aFileNames; GetApplicableFiles( sPath, aFileNames ); if( aFileNames.size() > 1 ) RageException::Throw( "There is more than one SM file in '%s'. There should be only one!", sPath.c_str() ); /* We should have exactly one; if we had none, we shouldn't have been * called to begin with. */ ASSERT( aFileNames.size() == 1 ); return LoadFromSMFile( sPath + aFileNames[0], out ); } bool SMLoader::LoadEdit( CString sEditFilePath ) { LOG->Trace( "Song::LoadEdit(%s)", sEditFilePath.c_str() ); MsdFile msd; bool bResult = msd.ReadFile( sEditFilePath ); if( !bResult ) RageException::Throw( "Error opening file '%s'.", sEditFilePath.c_str() ); Song* pSong = NULL; for( unsigned i=0; iWarn( "The edit file '%s' has more than one #SONG tag.", sEditFilePath.c_str() ); return false; } sSongFullTitle = sParams[1]; sSongFullTitle.Replace( '\\', '/' ); pSong = SONGMAN->FindSong( sSongFullTitle ); if( pSong == NULL ) { LOG->Warn( "The edit file '%s' required a song '%s' that isn't present.", sEditFilePath.c_str(), sSongFullTitle.c_str() ); return false; } } else if( 0==stricmp(sValueName,"NOTES") ) { if( pSong == NULL ) { LOG->Warn( "The edit file '%s' has doesn't have a #SONG tag preceeding the first #NOTES tag.", sEditFilePath.c_str() ); return false; } Steps* pNewNotes = new Steps; ASSERT( pNewNotes ); pSong->m_apNotes.push_back( pNewNotes ); if( iNumParams < 7 ) { LOG->Trace( "The song file '%s' is has %d fields in a #NOTES tag, but should have at least %d.", sEditFilePath.c_str(), iNumParams, 7 ); continue; } LoadFromSMTokens( sSongFullTitle, sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], (iNumParams>=8)?sParams[7]:"", *pNewNotes); } else LOG->Trace( "Unexpected value named '%s'", sValueName.c_str() ); } return true; }