Direct Move support has now been enabled. Minor touch-ups were also done.

This commit is contained in:
Jason Felds
2006-07-22 08:21:13 +00:00
parent 1bb96dea29
commit 0917b5bcc1
2 changed files with 181 additions and 50 deletions
+169 -48
View File
@@ -13,8 +13,6 @@
/* Global variables aren't the best way to go about this, but this /* Global variables aren't the best way to go about this, but this
* will be required to start implementing the Direct Move syntax. */ * will be required to start implementing the Direct Move syntax. */
bool KIUComplient;
#if 0 #if 0
void KSFLoader::RemoveHoles( NoteData &out, const Song &song ) void KSFLoader::RemoveHoles( NoteData &out, const Song &song )
{ {
@@ -72,8 +70,7 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s
if( !msd.ReadFile( sPath ) ) if( !msd.ReadFile( sPath ) )
RageException::Throw( "Error opening file '%s'.", sPath.c_str() ); RageException::Throw( "Error opening file '%s'.", sPath.c_str() );
int iTickCount = -1; // this is the value we read for TICKCOUNT m_bTickCount = -1; // this is the value we read for TICKCOUNT
vector<RString> asRows;
for( unsigned i=0; i<msd.GetNumValues(); i++ ) for( unsigned i=0; i<msd.GetNumValues(); i++ )
{ {
@@ -83,19 +80,17 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s
// handle the data // handle the data
if( 0==stricmp(sValueName,"TICKCOUNT") ) if( 0==stricmp(sValueName,"TICKCOUNT") )
{ {
iTickCount = atoi( sParams[1] ); m_bTickCount = atoi( sParams[1] );
if( iTickCount <= 0 ) if( m_bTickCount <= 0 )
{ {
// XXX need a place for user warnings. // XXX need a place for user warnings.
LOG->Warn( "Invalid tick count: %d", iTickCount ); LOG->Warn( "Invalid tick count: %d", m_bTickCount );
return false; return false;
} }
} }
else if( 0==stricmp(sValueName,"STEP") ) else if( 0==stricmp(sValueName,"STEP") )
{ {
RString step = sParams[1]; continue; //This was handled in LoadGlobalData: no need to repeat.
TrimLeft( step );
split( step, "\n", asRows, true );
} }
else if( 0==stricmp(sValueName,"DIFFICULTY") ) else if( 0==stricmp(sValueName,"DIFFICULTY") )
{ {
@@ -103,10 +98,10 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s
} }
} }
if( iTickCount == -1 ) if( m_bTickCount == -1 )
{ {
iTickCount = 2; m_bTickCount = 2;
LOG->Warn( "\"%s\": TICKCOUNT not found; defaulting to %i", sPath.c_str(), iTickCount ); LOG->Warn( "\"%s\": TICKCOUNT not found; defaulting to %i", sPath.c_str(), m_bTickCount );
} }
NoteData notedata; // read it into here NoteData notedata; // read it into here
@@ -117,12 +112,13 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s
sFName.MakeLower(); sFName.MakeLower();
out.SetDescription(sFName); out.SetDescription(sFName);
if( sFName.find("crazy") != string::npos ) if( sFName.find("crazy") != string::npos || sFName.find("nightmare") != string::npos ||
sFName.find("crazydouble") != string::npos )
{ {
out.SetDifficulty( DIFFICULTY_HARD ); out.SetDifficulty( DIFFICULTY_HARD );
if( !out.GetMeter() ) out.SetMeter( 8 ); if( !out.GetMeter() ) out.SetMeter( 8 );
} }
else if( sFName.find("hard") != string::npos ) else if( sFName.find("hard") != string::npos || sFName.find("freestyle") != string::npos )
{ {
out.SetDifficulty( DIFFICULTY_MEDIUM ); out.SetDifficulty( DIFFICULTY_MEDIUM );
if( !out.GetMeter() ) out.SetMeter( 5 ); if( !out.GetMeter() ) out.SetMeter( 5 );
@@ -161,59 +157,82 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s
} }
int iHoldStartRow[13]; int iHoldStartRow[13];
for( int t=0; t<13; t++ ) for( int y=0; y<13; y++ )
iHoldStartRow[t] = -1; iHoldStartRow[y] = -1;
// LOG->Trace("Getting steps from %s now: 500", sPath.c_str()); m_bTickChangeNeeded = false;
int newTick = -1;
for( unsigned r=0; r<asRows.size(); r++ ) m_bCurBeat = 0.0f;
float prevBeat = 0.0f; // Used for hold tails.
for( unsigned r=0; r<m_bNoteRows.size(); r++ )
{ {
RString& sRowString = asRows[r]; RString& sRowString = m_bNoteRows[r];
StripCrnl( sRowString ); StripCrnl( sRowString );
if( sRowString == "" ) if( sRowString == "" )
continue; // skip continue; // skip
/* All 2s indicates the end of the song. */ // All 2s indicates the end of the song.
/* In retrospect, we shouldn't break right away; there could be a hold
* that ends right before the terminal row. Perhaps that will be
* added in the future. */
if( sRowString == "2222222222222" ) if( sRowString == "2222222222222" )
break; break;
if( sRowString.size() != 13 ) if( sRowString.size() != 13 )
{ {
if( BeginsWith(sRowString, "|T") || BeginsWith(sRowString, "|B") || BeginsWith(sRowString, "|D") ) if (m_bKIUCompliant)
{ {
LOG->Warn("File %s had Direct Move syntax %s which is currently unsupported; said part ignored", LOG->Warn("File %s had Direct Move syntax (\"%s\") which can't be in KIU complient files.",
sPath.c_str(), sRowString.c_str()); sPath.c_str(), sRowString.c_str());
return false;
}
if( BeginsWith(sRowString, "|B") || BeginsWith(sRowString, "|D") )
{
// These don't have to be worried about here: the changes and stops were already added.
continue;
}
if ( BeginsWith(sRowString, "|T") )
{
RString temp = sRowString.substr(2,sRowString.size()-3);
newTick = atoi(temp);
m_bTickChangeNeeded = true;
continue;
} }
else else
{ {
LOG->Warn("File %s had a RowString with an improper length (\"%s\"); corrupt notes ignored", LOG->Warn("File %s had a RowString with an improper length (\"%s\"); corrupt notes ignored",
sPath.c_str(), sRowString.c_str()); sPath.c_str(), sRowString.c_str());
}
return false; return false;
} }
}
/* Half-doubles is offset; "0011111100000". */ /* Half-doubles is offset; "0011111100000". */
if( out.m_StepsType == STEPS_TYPE_PUMP_HALFDOUBLE ) if( out.m_StepsType == STEPS_TYPE_PUMP_HALFDOUBLE )
sRowString.erase( 0, 2 ); sRowString.erase( 0, 2 );
// the length of a note in a row depends on TICKCOUNT // Update TICKCOUNT for Direct Move files.
int row = (r * ROWS_PER_BEAT) / iTickCount; if (m_bTickChangeNeeded)
{
m_bTickCount = newTick;
m_bTickChangeNeeded = false;
}
for( int t=0; t < notedata.GetNumTracks(); t++ ) for( int t=0; t < notedata.GetNumTracks(); t++ )
{ {
if( sRowString[t] == '4' ) if( sRowString[t] == '4' )
{ {
/* Remember when each hold starts; ignore the middle. */ /* Remember when each hold starts; ignore the middle. */
if( iHoldStartRow[t] == -1 ) if( iHoldStartRow[t] == -1 )
iHoldStartRow[t] = row; iHoldStartRow[t] = BeatToNoteRow(m_bCurBeat);
//iHoldStartRow[t] = row;
continue; continue;
} }
if( iHoldStartRow[t] != -1 ) // this ends the hold if( iHoldStartRow[t] != -1 ) // this ends the hold
{ {
int iEndKSFRow = r-1; int iEndRow = BeatToNoteRow(prevBeat);
int iEndRow = (iEndKSFRow * ROWS_PER_BEAT) / iTickCount;
if( iHoldStartRow[t] == iEndRow ) if( iHoldStartRow[t] == iEndRow )
notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP ); notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP );
else else
@@ -232,8 +251,12 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s
return false; return false;
} }
notedata.SetTapNote(t, row, tap); //notedata.SetTapNote(t, row, tap);
notedata.SetTapNote(t, BeatToNoteRow(m_bCurBeat), tap); // More accurate for taps.
} }
prevBeat = m_bCurBeat;
m_bCurBeat = prevBeat + 1.0f / m_bTickCount;
} }
/* We need to remove holes where the BPM increases. */ /* We need to remove holes where the BPM increases. */
@@ -264,7 +287,8 @@ void KSFLoader::LoadTags( const RString &str, Song &out )
!stricmp(asBits[2], "easy") || !stricmp(asBits[2], "easy") ||
!stricmp(asBits[2], "normal") || !stricmp(asBits[2], "normal") ||
!stricmp(asBits[2], "hard") || !stricmp(asBits[2], "hard") ||
!stricmp(asBits[2], "crazy")) ) !stricmp(asBits[2], "crazy") ||
!stricmp(asBits[2], "nightmare")) )
{ {
asBits.erase( asBits.begin()+2, asBits.begin()+3 ); asBits.erase( asBits.begin()+2, asBits.begin()+3 );
} }
@@ -299,9 +323,9 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out )
RageException::Throw( "Error opening file \"%s\": %s", sPath.c_str(), msd.GetError().c_str() ); RageException::Throw( "Error opening file \"%s\": %s", sPath.c_str(), msd.GetError().c_str() );
float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1; float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1;
KIUComplient = false; int m_bTickCount = -1;
m_bKIUCompliant = false;
// LOG->Trace("Getting tags for %s now: 300", sPath.c_str()); RString theSteps;
for( unsigned i=0; i < msd.GetNumValues(); i++ ) for( unsigned i=0; i < msd.GetNumValues(); i++ )
{ {
@@ -318,22 +342,22 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out )
} }
else if( 0==stricmp(sValueName,"BPM2") ) else if( 0==stricmp(sValueName,"BPM2") )
{ {
KIUComplient = true; m_bKIUCompliant = true;
BPM2 = StringToFloat( sParams[1] ); BPM2 = StringToFloat( sParams[1] );
} }
else if( 0==stricmp(sValueName,"BPM3") ) else if( 0==stricmp(sValueName,"BPM3") )
{ {
KIUComplient = true; m_bKIUCompliant = true;
BPM3 = StringToFloat( sParams[1] ); BPM3 = StringToFloat( sParams[1] );
} }
else if( 0==stricmp(sValueName,"BUNKI") ) else if( 0==stricmp(sValueName,"BUNKI") )
{ {
KIUComplient = true; m_bKIUCompliant = true;
BPMPos2 = StringToFloat( sParams[1] ) / 100.0f; BPMPos2 = StringToFloat( sParams[1] ) / 100.0f;
} }
else if( 0==stricmp(sValueName,"BUNKI2") ) else if( 0==stricmp(sValueName,"BUNKI2") )
{ {
KIUComplient = true; m_bKIUCompliant = true;
BPMPos3 = StringToFloat( sParams[1] ) / 100.0f; BPMPos3 = StringToFloat( sParams[1] ) / 100.0f;
} }
else if( 0==stricmp(sValueName,"STARTTIME") ) else if( 0==stricmp(sValueName,"STARTTIME") )
@@ -341,23 +365,36 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out )
SMGap1 = -StringToFloat( sParams[1] )/100; SMGap1 = -StringToFloat( sParams[1] )/100;
out.m_Timing.m_fBeat0OffsetInSeconds = SMGap1; out.m_Timing.m_fBeat0OffsetInSeconds = SMGap1;
} }
// This is currently required for more accurate BPM changes. // This is currently required for more accurate KIU BPM changes.
else if( 0==stricmp(sValueName,"STARTTIME2") ) else if( 0==stricmp(sValueName,"STARTTIME2") )
{ {
KIUComplient = true; m_bKIUCompliant = true;
SMGap2 = -StringToFloat( sParams[1] )/100; SMGap2 = -StringToFloat( sParams[1] )/100;
} }
else if ( 0==stricmp(sValueName,"STARTTIME3") ) else if ( 0==stricmp(sValueName,"STARTTIME3") )
{ {
// STARTTIME3 only ensures this is a KIU complient simfile. // STARTTIME3 only ensures this is a KIU complient simfile.
KIUComplient = true; m_bKIUCompliant = true;
} }
else if( 0==stricmp(sValueName,"TICKCOUNT") || else if ( 0==stricmp(sValueName,"TICKCOUNT") )
0==stricmp(sValueName,"STEP") ||
0==stricmp(sValueName,"DIFFICULTY"))
{ {
/* TICKCOUNT, STEP, and DIFFICULTY are handled in LoadFromKSFFile. /* TICKCOUNT is will be used if there are DM complient BPM changes and stops.
* Do not warn for them. */ * It will be called again in LoadFromKSFFile for the actual steps. */
m_bTickCount = atoi( sParams[1] );
m_bTickCount = m_bTickCount > 0 ? m_bTickCount : 2;
}
else if ( 0==stricmp(sValueName,"STEP") )
{
/* STEP will always be the last header in a KSF file by design. Due to
* the Direct Move syntax, it is best to get the rows of notes here. */
theSteps = sParams[1];
TrimLeft( theSteps );
split( theSteps, "\n", m_bNoteRows, true );
}
else if( 0==stricmp(sValueName,"DIFFICULTY"))
{
/* DIFFICULTY is handled only in LoadFromKSFFile. Ignore it here. */
continue; continue;
} }
else else
@@ -370,7 +407,7 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out )
* syntax. Unfortunately, they will require a further scan in order to determine * syntax. Unfortunately, they will require a further scan in order to determine
* whether or not there are changes, or even stops, within the file. That will be * whether or not there are changes, or even stops, within the file. That will be
* implemented later. */ * implemented later. */
if (KIUComplient) if (m_bKIUCompliant)
{ {
if( BPM2 > 0 && BPMPos2 > 0 ) if( BPM2 > 0 && BPMPos2 > 0 )
{ {
@@ -392,6 +429,90 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out )
} }
} }
else
{
int tickToChange = m_bTickCount;
m_bCurBeat = 0.0f;
float speedToChange = 0.0f, timeToStop = 0.0f;
m_bDMRequired = m_bBPMChangeNeeded = m_bBPMStopNeeded = m_bTickChangeNeeded = false;
for (unsigned i=0; i < m_bNoteRows.size(); i++)
{
RString& NoteRowString = m_bNoteRows[i];
StripCrnl( NoteRowString );
if( NoteRowString == "" )
continue; // Empty rows do us no good.
if( NoteRowString == "2222222222222" ) // Row of 2s = end. Confirm KIUCompliency here.
{
if (!m_bDMRequired)
m_bKIUCompliant = true;
break;
}
/* This is where the DMRequired test will take place. */
if( NoteRowString.size() != 13)
{
if (BeginsWith(NoteRowString, "|T") ||
BeginsWith(NoteRowString, "|B") || BeginsWith(NoteRowString, "|D") )
{
m_bDMRequired = true;
RString temp = NoteRowString.substr(2,NoteRowString.size()-3);
float numTemp = StringToFloat(temp);
if (BeginsWith(NoteRowString, "|T"))
{
m_bTickChangeNeeded = true;
tickToChange = (int)numTemp;
continue;
}
else if (BeginsWith(NoteRowString, "|B"))
{
m_bBPMChangeNeeded = true;
speedToChange = numTemp;
continue;
}
else
{
m_bBPMStopNeeded = true;
timeToStop = numTemp / 1000.0f;
continue;
}
}
else
{
/* Quit while we're ahead if any bad syntax is spotted. */
LOG->Warn("File %s has a RowString which matches no known syntax rules (\"%s\"); bad file",
sPath.c_str(), NoteRowString.c_str());
return false;
}
}
if (m_bTickChangeNeeded)
{
m_bTickCount = tickToChange;
m_bTickChangeNeeded = false;
}
if (m_bBPMChangeNeeded)
{
out.AddBPMSegment(BPMSegment(BeatToNoteRow(m_bCurBeat),speedToChange));
m_bBPMChangeNeeded = false;
}
if (m_bBPMStopNeeded)
{
out.AddStopSegment(StopSegment(BeatToNoteRow(m_bCurBeat),timeToStop));
m_bBPMStopNeeded = false;
}
m_bCurBeat += 1.0f / m_bTickCount;
}
}
//if (m_bDMRequired)
// LOG->Warn("%s uses the Direct Move syntax; at this point, mileage may vary.", sPath.c_str());
/* Try to fill in missing bits of information from the pathname. */ /* Try to fill in missing bits of information from the pathname. */
{ {
vector<RString> asBits; vector<RString> asBits;
+10
View File
@@ -20,6 +20,16 @@ public:
void GetApplicableFiles( const RString &sPath, vector<RString> &out ); void GetApplicableFiles( const RString &sPath, vector<RString> &out );
bool LoadFromDir( const RString &sDir, Song &out ); bool LoadFromDir( const RString &sDir, Song &out );
private:
bool m_bKIUCompliant;
bool m_bDMRequired;
bool m_bBPMChangeNeeded;
bool m_bBPMStopNeeded;
bool m_bTickChangeNeeded;
int m_bTickCount;
float m_bCurBeat;
vector<RString> m_bNoteRows;
}; };
#endif #endif