diff --git a/stepmania/src/NoteDataUtil.cpp b/stepmania/src/NoteDataUtil.cpp index 3ea005627e..6bc9b11dca 100644 --- a/stepmania/src/NoteDataUtil.cpp +++ b/stepmania/src/NoteDataUtil.cpp @@ -7,6 +7,7 @@ #include "GameState.h" #include "RadarValues.h" #include "Foreach.h" +#include NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex ) { @@ -46,54 +47,61 @@ NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMe return nt; } -void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData ) +static void LoadFromSMNoteDataStringWithPlayer( NoteData& out, const RString &sSMNoteData, int start, + int len, PlayerNumber pn, int iNumTracks ) { - // - // Load note data - // - - /* Clear notes, but keep the same number of tracks. */ - int iNumTracks = out.GetNumTracks(); - out.Init(); - out.SetNumTracks( iNumTracks ); - + /* Don't allocate memory for the entire string, nor per measure. Instead, use the in-place + * partial string split twice. By maintaining begin and end pointers to each measure line + * we can perform this without copying the string at all. */ + int size = -1; + const int end = start + len; + vector > aMeasureLines; + for( unsigned m = 0; true; ++m ) { - RString::size_type iIndexCommentStart = 0; + /* XXX Ignoring empty seems wrong for measures. It means that ",,," is treated as + * "," where I would expect most people would want 2 empty measures. ",\n,\n," + * would do as I would expect. */ + split( sSMNoteData, ",", start, size, end, true ); // Ignore empty is important. + if( start == end ) + break; - while( (iIndexCommentStart = sSMNoteData.find("//", iIndexCommentStart)) != RString::npos ) + // Partial string split. + int measureLineStart = start, measureLineSize = -1; + const int measureEnd = start + size; + + aMeasureLines.clear(); + while( true ) { - RString::size_type iIndexCommentEnd = sSMNoteData.find( "\n", iIndexCommentStart ); - sSMNoteData.erase( iIndexCommentStart, iIndexCommentEnd - iIndexCommentStart ); + // Ignore empty is clearly important here. + split( sSMNoteData, "\n", measureLineStart, measureLineSize, measureEnd, true ); + if( measureLineStart == measureEnd ) + break; + //RString &line = sSMNoteData.substr( measureLineStart, measureLineSize ); + const char *beginLine = sSMNoteData.data() + measureLineStart; + const char *endLine = beginLine + measureLineSize; + + while( beginLine < endLine && strchr("\r\n\t ", *beginLine) ) + ++beginLine; + while( endLine > beginLine && strchr("\r\n\t ", *(endLine - 1)) ) + --endLine; + if( beginLine < endLine ) // nonempty + aMeasureLines.push_back( pair(beginLine, endLine) ); } - } - - vector asMeasures; - split( sSMNoteData, ",", asMeasures, true ); // ignore empty is important - for( unsigned m=0; m asMeasureLines; - split( sMeasureString, "\n", asMeasureLines, true ); // ignore empty is important - - for( unsigned l=0; lWarn( "Unmatched 3 in \"%s\"", sMeasureLine.c_str() ); + int n = intptr_t(endLine) - intptr_t(beginLine); + LOG->Warn( "Unmatched 3 in \"%.*s\"", n, beginLine ); } else { @@ -129,11 +138,11 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData head_tap.iDuration = iIndex - iHeadRow; out.SetTapNote( iTrack, iHeadRow, head_tap ); } - + /* This won't write tn, but keep parsing normally anyway. */ break; } -// case 'm': + // case 'm': // Don't be loose with the definition. Use only 'M' since // that's what we've been writing to disk. -Chris case 'M': tn = TAP_ORIGINAL_MINE; break; @@ -150,13 +159,13 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData } p++; - + // We won't scan past the end of the line so these are safe to do. #if 0 // look for optional attack info (e.g. "{tipsy,50% drunk:15.2}") if( *p == '{' ) { p++; - + char szModifiers[256] = ""; float fDurationSeconds = 0; if( sscanf( p, "%255[^:]:%f}", szModifiers, &fDurationSeconds ) == 2 ) // not fatal if this fails due to malformed data @@ -165,16 +174,16 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData tn.sAttackModifiers = szModifiers; tn.fAttackDurationSeconds = fDurationSeconds; } - + // skip past the '}' - while( *p ) + while( p < endLine ) { if( *(p++) == '}' ) break; } } #endif - + // look for optional keysound index (e.g. "[123]") if( *p == '[' ) { @@ -185,26 +194,29 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData tn.bKeysound = true; tn.iKeysoundIndex = iKeysoundIndex; } - + // skip past the ']' - while( *p ) + while( p < endLine ) { if( *(p++) == ']' ) break; } } - + /* Optimization: if we pass TAP_EMPTY, NoteData will do a search * to remove anything in this position. We know that there's nothing * there, so avoid the search. */ if( tn.type != TapNote::empty && ch != '3' ) + { + tn.pn = pn; out.SetTapNote( iTrack, iIndex, tn ); - + } + iTrack++; } } } - + /* Make sure we don't have any hold notes that didn't find a tail. */ for( int t=0; tWarn( "Unmatched 2 at beat %f", NoteRowToBeat(iRow) ); out.RemoveTapNote( t, begin ); } - + begin = next; } } + +} + +void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, const RString &sSMNoteData_ ) +{ + // + // Load note data + // + RString sSMNoteData; + RString::size_type iIndexCommentStart = 0; + RString::size_type iIndexCommentEnd = 0; + RString::size_type origSize = sSMNoteData_.size(); + const char *p = sSMNoteData_.data(); + + sSMNoteData.reserve( origSize ); + while( (iIndexCommentStart = sSMNoteData.find("//", iIndexCommentEnd)) != RString::npos ) + { + sSMNoteData.append( p, iIndexCommentStart - iIndexCommentEnd ); + p += iIndexCommentStart - iIndexCommentEnd; + iIndexCommentEnd = sSMNoteData.find( "\n", iIndexCommentStart ); + iIndexCommentEnd = (iIndexCommentEnd == RString::npos ? origSize : iIndexCommentEnd+1); + p += iIndexCommentEnd - iIndexCommentStart; + } + sSMNoteData.append( p, origSize - iIndexCommentEnd ); + + /* Clear notes, but keep the same number of tracks. */ + int iNumTracks = out.GetNumTracks(); + out.Init(); + out.SetNumTracks( iNumTracks ); + + LoadFromSMNoteDataStringWithPlayer( out, sSMNoteData, 0, sSMNoteData.size(), PLAYER_INVALID, iNumTracks ); } void NoteDataUtil::InsertHoldTails( NoteData &inout ) diff --git a/stepmania/src/NoteDataUtil.h b/stepmania/src/NoteDataUtil.h index b22237fbb5..c75eb9ada0 100644 --- a/stepmania/src/NoteDataUtil.h +++ b/stepmania/src/NoteDataUtil.h @@ -17,7 +17,7 @@ struct RadarValues; namespace NoteDataUtil { NoteType GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex ); - void LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData ); + void LoadFromSMNoteDataString( NoteData &out, const RString &sSMNoteData ); void GetSMNoteDataString( const NoteData &in, RString ¬es_out ); void LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks ); void LoadOverlapped( const NoteData &in, NoteData &out, int iNewNumTracks );