move Steps searching out of song into SongUtil so that it can be shared with CourseEntry

This commit is contained in:
Chris Danford
2006-06-13 01:10:37 +00:00
parent acdf036e05
commit af3e1c571c
30 changed files with 464 additions and 378 deletions
+12 -254
View File
@@ -19,9 +19,9 @@
#include "RageFileManager.h"
#include "RageSurface.h"
#include "NoteDataUtil.h"
#include "SongUtil.h"
#include "StepsUtil.h"
#include "Foreach.h"
#include "UnlockManager.h"
#include "BackgroundUtil.h"
#include "SpecialFiles.h"
@@ -319,114 +319,6 @@ static void GetImageDirListing( RString sPath, vector<RString> &AddTo, bool bRet
GetDirListing( sPath + ".gif", AddTo, false, bReturnPathToo );
}
static RString RemoveInitialWhitespace( RString s )
{
size_t i = s.find_first_not_of(" \t\r\n");
if( i != s.npos )
s.erase( 0, i );
return s;
}
/* This is called within TidyUpData, before autogen notes are added. */
void Song::DeleteDuplicateSteps( vector<Steps*> &vSteps )
{
/* vSteps have the same StepsType and Difficulty. Delete them if they have the
* same m_sDescription, m_iMeter and SMNoteData. */
CHECKPOINT;
for( unsigned i=0; i<vSteps.size(); i++ )
{
CHECKPOINT;
const Steps *s1 = vSteps[i];
for( unsigned j=i+1; j<vSteps.size(); j++ )
{
CHECKPOINT;
const Steps *s2 = vSteps[j];
if( s1->GetDescription() != s2->GetDescription() )
continue;
if( s1->GetMeter() != s2->GetMeter() )
continue;
/* Compare, ignoring whitespace. */
RString sSMNoteData1;
s1->GetSMNoteData( sSMNoteData1 );
RString sSMNoteData2;
s2->GetSMNoteData( sSMNoteData2 );
if( RemoveInitialWhitespace(sSMNoteData1) != RemoveInitialWhitespace(sSMNoteData2) )
continue;
LOG->Trace("Removed %p duplicate steps in song \"%s\" with description \"%s\" and meter \"%i\"",
s2, this->GetSongDir().c_str(), s1->GetDescription().c_str(), s1->GetMeter() );
/* Don't use RemoveSteps; autogen notes havn't yet been created and it'll
* create them. */
FOREACH_StepsType( st )
{
for( int k=this->m_vpStepsByType[st].size()-1; k>=0; k-- )
{
if( this->m_vpStepsByType[st][k] == s2 )
{
// delete this->m_vpStepsByType[k]; // delete below
this->m_vpStepsByType[st].erase( this->m_vpStepsByType[st].begin()+k );
break;
}
}
}
for( int k=this->m_vpSteps.size()-1; k>=0; k-- )
{
if( this->m_vpSteps[k] == s2 )
{
delete this->m_vpSteps[k];
this->m_vpSteps.erase( this->m_vpSteps.begin()+k );
break;
}
}
vSteps.erase(vSteps.begin()+j);
--j;
}
}
}
/* Make any duplicate difficulties edits. (Note that BMS files do a first pass
* on this; see BMSLoader::SlideDuplicateDifficulties.) */
void Song::AdjustDuplicateSteps()
{
FOREACH_StepsType( st )
{
FOREACH_Difficulty( dc )
{
if( dc == DIFFICULTY_EDIT )
continue;
vector<Steps*> vSteps;
this->GetSteps( vSteps, st, dc );
/* Delete steps that are completely identical. This happened due to a
* bug in an earlier version. */
DeleteDuplicateSteps( vSteps );
CHECKPOINT;
StepsUtil::SortNotesArrayByDifficulty( vSteps );
CHECKPOINT;
for( unsigned k=1; k<vSteps.size(); k++ )
{
vSteps[k]->SetDifficulty( DIFFICULTY_EDIT );
if( vSteps[k]->GetDescription() == "" )
{
/* "Hard Edit" */
RString EditName = Capitalize( DifficultyToString(dc) ) + " Edit";
vSteps[k]->SetDescription( EditName );
}
}
}
/* XXX: Don't allow edits to have descriptions that look like regular difficulties.
* These are confusing, and they're ambiguous when passed to GetStepsByID. */
}
}
/* Fix up song paths. If there's a leading "./", be sure to keep it: it's
* a signal that the path is from the root directory, not the song directory.
* Other than a leading "./", song paths must never contain "." or "..". */
@@ -738,7 +630,7 @@ void Song::TidyUpData()
/* Don't allow multiple Steps of the same StepsType and Difficulty (except for edits).
* We should be able to use difficulty names as unique identifiers for steps. */
AdjustDuplicateSteps();
SongUtil::AdjustDuplicateSteps( this );
{
/* Generated filename; this doesn't always point to a loadable file,
@@ -835,142 +727,6 @@ void Song::ReCalculateRadarValuesAndLastBeat()
m_fLastBeat = fLastBeat;
}
void Song::GetSteps(
vector<Steps*>& arrayAddTo,
StepsType st,
Difficulty dc,
int iMeterLow,
int iMeterHigh,
const RString &sDescription,
bool bIncludeAutoGen,
unsigned uHash,
int iMaxToGet
) const
{
if( !iMaxToGet )
return;
const vector<Steps*> &vpSteps = st == STEPS_TYPE_INVALID ? GetAllSteps() : GetStepsByStepsType(st);
for( unsigned i=0; i<vpSteps.size(); i++ ) // for each of the Song's Steps
{
Steps* pSteps = vpSteps[i];
if( dc != DIFFICULTY_INVALID && dc != pSteps->GetDifficulty() )
continue;
if( iMeterLow != -1 && iMeterLow > pSteps->GetMeter() )
continue;
if( iMeterHigh != -1 && iMeterHigh < pSteps->GetMeter() )
continue;
if( sDescription.size() && sDescription != pSteps->GetDescription() )
continue;
if( uHash != 0 && uHash != pSteps->GetHash() )
continue;
if( !bIncludeAutoGen && pSteps->IsAutogen() )
continue;
arrayAddTo.push_back( pSteps );
if( iMaxToGet != -1 )
{
--iMaxToGet;
if( !iMaxToGet )
break;
}
}
}
Steps* Song::GetOneSteps(
StepsType st,
Difficulty dc,
int iMeterLow,
int iMeterHigh,
const RString &sDescription,
unsigned uHash,
bool bIncludeAutoGen
) const
{
vector<Steps*> vpSteps;
GetSteps( vpSteps, st, dc, iMeterLow, iMeterHigh, sDescription, bIncludeAutoGen, uHash, 1 ); // get max 1
if( vpSteps.empty() )
return NULL;
else
return vpSteps[0];
}
Steps* Song::GetStepsByDifficulty( StepsType st, Difficulty dc, bool bIncludeAutoGen ) const
{
const vector<Steps*>& vpSteps = (st == STEPS_TYPE_INVALID)? GetAllSteps():GetStepsByStepsType(st);
for( unsigned i=0; i<vpSteps.size(); i++ ) // for each of the Song's Steps
{
Steps* pSteps = vpSteps[i];
if( dc != DIFFICULTY_INVALID && dc != pSteps->GetDifficulty() )
continue;
if( !bIncludeAutoGen && pSteps->IsAutogen() )
continue;
return pSteps;
}
return NULL;
}
Steps* Song::GetStepsByMeter( StepsType st, int iMeterLow, int iMeterHigh ) const
{
const vector<Steps*>& vpSteps = (st == STEPS_TYPE_INVALID)? GetAllSteps():GetStepsByStepsType(st);
for( unsigned i=0; i<vpSteps.size(); i++ ) // for each of the Song's Steps
{
Steps* pSteps = vpSteps[i];
if( iMeterLow > pSteps->GetMeter() )
continue;
if( iMeterHigh < pSteps->GetMeter() )
continue;
return pSteps;
}
return NULL;
}
Steps* Song::GetStepsByDescription( StepsType st, RString sDescription ) const
{
vector<Steps*> vNotes;
GetSteps( vNotes, st, DIFFICULTY_INVALID, -1, -1, sDescription );
if( vNotes.size() == 0 )
return NULL;
else
return vNotes[0];
}
Steps* Song::GetClosestNotes( StepsType st, Difficulty dc, bool bIgnoreLocked ) const
{
ASSERT( dc != DIFFICULTY_INVALID );
const vector<Steps*>& vpSteps = (st == STEPS_TYPE_INVALID)? GetAllSteps():GetStepsByStepsType(st);
Steps *pClosest = NULL;
int iClosestDistance = 999;
for( unsigned i=0; i<vpSteps.size(); i++ ) // for each of the Song's Steps
{
Steps* pSteps = vpSteps[i];
if( pSteps->GetDifficulty() == DIFFICULTY_EDIT && dc != DIFFICULTY_EDIT )
continue;
if( bIgnoreLocked && UNLOCKMAN->StepsIsLocked(this, pSteps) )
continue;
int iDistance = abs(dc - pSteps->GetDifficulty());
if( iDistance < iClosestDistance )
{
pClosest = pSteps;
iClosestDistance = iDistance;
}
}
return pClosest;
}
/* Return whether the song is playable in the given style. */
bool Song::SongCompleteForStyle( const Style *st ) const
{
@@ -979,12 +735,12 @@ bool Song::SongCompleteForStyle( const Style *st ) const
bool Song::HasStepsType( StepsType st ) const
{
return GetOneSteps( st ) != NULL;
return SongUtil::GetOneSteps( this, st ) != NULL;
}
bool Song::HasStepsTypeAndDifficulty( StepsType st, Difficulty dc ) const
{
return GetOneSteps( st, dc ) != NULL;
return SongUtil::GetOneSteps( this, st, dc ) != NULL;
}
void Song::Save()
@@ -1156,11 +912,11 @@ bool Song::IsEasy( StepsType st ) const
/* The easy marker indicates which songs a beginner, having selected "beginner",
* can play and actually get a very easy song: if there are actual beginner
* steps, or if the light steps are 1- or 2-foot. */
const Steps* pBeginnerNotes = GetStepsByDifficulty( st, DIFFICULTY_BEGINNER );
const Steps* pBeginnerNotes = SongUtil::GetStepsByDifficulty( this, st, DIFFICULTY_BEGINNER );
if( pBeginnerNotes )
return true;
const Steps* pEasyNotes = GetStepsByDifficulty( st, DIFFICULTY_EASY );
const Steps* pEasyNotes = SongUtil::GetStepsByDifficulty( this, st, DIFFICULTY_EASY );
if( pEasyNotes && pEasyNotes->GetMeter() == 1 )
return true;
@@ -1349,12 +1105,13 @@ void Song::AddSteps( Steps* pSteps )
m_vpStepsByType[pSteps->m_StepsType].push_back( pSteps );
}
void Song::DeleteSteps( const Steps* pSteps )
void Song::DeleteSteps( const Steps* pSteps, bool bReAutoGen )
{
// Avoid any stale Note::parent pointers by removing all AutoGen'd Steps,
// then adding them again.
RemoveAutoGenNotes();
if( bReAutoGen )
RemoveAutoGenNotes();
vector<Steps*> &vpSteps = m_vpStepsByType[pSteps->m_StepsType];
@@ -1378,7 +1135,8 @@ void Song::DeleteSteps( const Steps* pSteps )
}
}
AddAutoGenNotes();
if( bReAutoGen )
AddAutoGenNotes();
}
bool Song::Matches(RString sGroup, RString sSong) const
@@ -1404,7 +1162,7 @@ bool Song::Matches(RString sGroup, RString sSong) const
void Song::FreeAllLoadedFromProfile( ProfileSlot slot )
{
/* RemoveSteps will remove and recreate autogen notes, which may reorder
/* DeleteSteps will remove and recreate autogen notes, which may reorder
* m_vpSteps, so be careful not to skip over entries. */
vector<Steps*> apToRemove;
for( int s=m_vpSteps.size()-1; s>=0; s-- )