From 0917b5bcc14f38a6a9c5a5d74eb59c92246fad06 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Sat, 22 Jul 2006 08:21:13 +0000 Subject: [PATCH] Direct Move support has now been enabled. Minor touch-ups were also done. --- stepmania/src/NotesLoaderKSF.cpp | 221 ++++++++++++++++++++++++------- stepmania/src/NotesLoaderKSF.h | 10 ++ 2 files changed, 181 insertions(+), 50 deletions(-) diff --git a/stepmania/src/NotesLoaderKSF.cpp b/stepmania/src/NotesLoaderKSF.cpp index 750617e58f..91f77d26a3 100644 --- a/stepmania/src/NotesLoaderKSF.cpp +++ b/stepmania/src/NotesLoaderKSF.cpp @@ -13,8 +13,6 @@ /* Global variables aren't the best way to go about this, but this * will be required to start implementing the Direct Move syntax. */ -bool KIUComplient; - #if 0 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 ) ) RageException::Throw( "Error opening file '%s'.", sPath.c_str() ); - int iTickCount = -1; // this is the value we read for TICKCOUNT - vector asRows; + m_bTickCount = -1; // this is the value we read for TICKCOUNT for( unsigned i=0; iWarn( "Invalid tick count: %d", iTickCount ); + LOG->Warn( "Invalid tick count: %d", m_bTickCount ); return false; } } else if( 0==stricmp(sValueName,"STEP") ) { - RString step = sParams[1]; - TrimLeft( step ); - split( step, "\n", asRows, true ); + continue; //This was handled in LoadGlobalData: no need to repeat. } 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; - LOG->Warn( "\"%s\": TICKCOUNT not found; defaulting to %i", sPath.c_str(), iTickCount ); + m_bTickCount = 2; + LOG->Warn( "\"%s\": TICKCOUNT not found; defaulting to %i", sPath.c_str(), m_bTickCount ); } NoteData notedata; // read it into here @@ -117,12 +112,13 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s sFName.MakeLower(); 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 ); 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 ); if( !out.GetMeter() ) out.SetMeter( 5 ); @@ -161,59 +157,82 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s } int iHoldStartRow[13]; - for( int t=0; t<13; t++ ) - iHoldStartRow[t] = -1; + for( int y=0; y<13; y++ ) + iHoldStartRow[y] = -1; -// LOG->Trace("Getting steps from %s now: 500", sPath.c_str()); - - for( unsigned r=0; rWarn("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()); + 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 { LOG->Warn("File %s had a RowString with an improper length (\"%s\"); corrupt notes ignored", sPath.c_str(), sRowString.c_str()); + return false; } - return false; } /* Half-doubles is offset; "0011111100000". */ if( out.m_StepsType == STEPS_TYPE_PUMP_HALFDOUBLE ) sRowString.erase( 0, 2 ); - // the length of a note in a row depends on TICKCOUNT - int row = (r * ROWS_PER_BEAT) / iTickCount; + // Update TICKCOUNT for Direct Move files. + if (m_bTickChangeNeeded) + { + m_bTickCount = newTick; + m_bTickChangeNeeded = false; + } + for( int t=0; t < notedata.GetNumTracks(); t++ ) { if( sRowString[t] == '4' ) { /* Remember when each hold starts; ignore the middle. */ if( iHoldStartRow[t] == -1 ) - iHoldStartRow[t] = row; - + iHoldStartRow[t] = BeatToNoteRow(m_bCurBeat); + //iHoldStartRow[t] = row; + continue; } if( iHoldStartRow[t] != -1 ) // this ends the hold { - int iEndKSFRow = r-1; - int iEndRow = (iEndKSFRow * ROWS_PER_BEAT) / iTickCount; + int iEndRow = BeatToNoteRow(prevBeat); if( iHoldStartRow[t] == iEndRow ) notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP ); else @@ -232,8 +251,12 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s 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. */ @@ -264,7 +287,8 @@ void KSFLoader::LoadTags( const RString &str, Song &out ) !stricmp(asBits[2], "easy") || !stricmp(asBits[2], "normal") || !stricmp(asBits[2], "hard") || - !stricmp(asBits[2], "crazy")) ) + !stricmp(asBits[2], "crazy") || + !stricmp(asBits[2], "nightmare")) ) { 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() ); float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1; - KIUComplient = false; - -// LOG->Trace("Getting tags for %s now: 300", sPath.c_str()); + int m_bTickCount = -1; + m_bKIUCompliant = false; + RString theSteps; 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") ) { - KIUComplient = true; + m_bKIUCompliant = true; BPM2 = StringToFloat( sParams[1] ); } else if( 0==stricmp(sValueName,"BPM3") ) { - KIUComplient = true; + m_bKIUCompliant = true; BPM3 = StringToFloat( sParams[1] ); } else if( 0==stricmp(sValueName,"BUNKI") ) { - KIUComplient = true; + m_bKIUCompliant = true; BPMPos2 = StringToFloat( sParams[1] ) / 100.0f; } else if( 0==stricmp(sValueName,"BUNKI2") ) { - KIUComplient = true; + m_bKIUCompliant = true; BPMPos3 = StringToFloat( sParams[1] ) / 100.0f; } else if( 0==stricmp(sValueName,"STARTTIME") ) @@ -341,23 +365,36 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out ) SMGap1 = -StringToFloat( sParams[1] )/100; 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") ) { - KIUComplient = true; + m_bKIUCompliant = true; SMGap2 = -StringToFloat( sParams[1] )/100; } else if ( 0==stricmp(sValueName,"STARTTIME3") ) { // STARTTIME3 only ensures this is a KIU complient simfile. - KIUComplient = true; + m_bKIUCompliant = true; } - else if( 0==stricmp(sValueName,"TICKCOUNT") || - 0==stricmp(sValueName,"STEP") || - 0==stricmp(sValueName,"DIFFICULTY")) + else if ( 0==stricmp(sValueName,"TICKCOUNT") ) { - /* TICKCOUNT, STEP, and DIFFICULTY are handled in LoadFromKSFFile. - * Do not warn for them. */ + /* TICKCOUNT is will be used if there are DM complient BPM changes and stops. + * 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; } 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 * whether or not there are changes, or even stops, within the file. That will be * implemented later. */ - if (KIUComplient) + if (m_bKIUCompliant) { if( BPM2 > 0 && BPMPos2 > 0 ) { @@ -390,7 +427,91 @@ bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out ) BPM2, BeatsPerSecond, BPMPos3, beat ); out.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), BPM3) ); } - } + } + + 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. */ { diff --git a/stepmania/src/NotesLoaderKSF.h b/stepmania/src/NotesLoaderKSF.h index f6fc1cda70..acc317cfc3 100644 --- a/stepmania/src/NotesLoaderKSF.h +++ b/stepmania/src/NotesLoaderKSF.h @@ -20,6 +20,16 @@ public: void GetApplicableFiles( const RString &sPath, vector &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 m_bNoteRows; + }; #endif