diff --git a/stepmania/src/NoteData.cpp b/stepmania/src/NoteData.cpp index 6b9b7440fc..fd33f0ab22 100644 --- a/stepmania/src/NoteData.cpp +++ b/stepmania/src/NoteData.cpp @@ -275,7 +275,13 @@ void NoteData::SetTapAttackNote( int track, int row, Attack attack ) { m_AttackMap[i] = attack; TapNote tn; - tn.Set( TapNote::attack, TapNote::original, i ); + tn.Set( + TapNote::attack, + TapNote::original, + true, + i, + false, + 0 ); SetTapNote( track, row, tn ); return; } diff --git a/stepmania/src/NoteDataUtil.cpp b/stepmania/src/NoteDataUtil.cpp index 4b352b8940..a87b6a6626 100644 --- a/stepmania/src/NoteDataUtil.cpp +++ b/stepmania/src/NoteDataUtil.cpp @@ -92,25 +92,32 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData, // if( m_iNumTracks != sMeasureLine.GetLength() ) // RageException::Throw( "Actual number of note columns (%d) is different from the StepsType (%d).", m_iNumTracks, sMeasureLine.GetLength() ); - for( int c=0; c= 'a' && ch <= 'z' ) { - t.Set( TapNote::attack, TapNote::original, ch - 'a' ); + tn.Set( + TapNote::attack, + TapNote::original, + true, + ch - 'a', + false, + 0 ); } else { @@ -119,11 +126,39 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData, * due to invalid data. We should probably check for this when * we load SM data for the first time ... */ // ASSERT(0); - t = TAP_EMPTY; + tn = TAP_EMPTY; } break; } - out.SetTapNote(c, iIndex, t); + + p++; + + // look for optional keysound index (e.g. "[123]") + if( *p == '[' ) + { + p++; + unsigned uKeysoundIndex = 0; + if( 1 == sscanf( p, "%u]", &uKeysoundIndex ) ) // not fatal if this fails due to malformed data + { + tn.bKeysound = true; + tn.keysoundIndex = uKeysoundIndex; + } + + // skip past the ']' + while( *p ) + { + if( *p == '[]' ) + { + p++; + break; + } + p++; + } + } + + out.SetTapNote( iTrack, iIndex, tn ); + + iTrack++; } } } @@ -221,6 +256,11 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString ¬es_out, break; } sRet.append(1, c); + + if( tn.bKeysound ) + { + sRet.append( ssprintf("[%u]",tn.keysoundIndex) ); + } } sRet.append(1, '\n'); diff --git a/stepmania/src/NoteTypes.h b/stepmania/src/NoteTypes.h index b9af1ac2a3..79f9f13f4d 100644 --- a/stepmania/src/NoteTypes.h +++ b/stepmania/src/NoteTypes.h @@ -29,24 +29,42 @@ struct TapNote // Equivalent to all 4s aside from the first one. }; unsigned source : 2; // only valid if type!=empty - unsigned attackIndex : 3; // attack index - // bit field sizes should add to 8 bits. - void Set( Type type_, Source source_, unsigned attackIndex_ ) + bool bAttack : 1; // true if this note causes an attack when hit + bool bKeysound : 1; // true if this note plays a keysound when hit + + // CAREFUL: small fields grouped together for alignment. + + uint8_t attackIndex; // index into NoteData's vector of attacks + // Only valid if bAttack. + uint16_t keysoundIndex; // index into Song's vector of keysound files. + // Only valid if bKeysound. + // Some songs have > 256 keysounds. + + void Set( + Type type_, + Source source_, + bool bAttack_, + unsigned attackIndex_, + bool bKeysound_, + unsigned keysoundIndex_ ) { type = type_; source = source_; + bAttack = bAttack_; attackIndex = attackIndex_; + bKeysound = bKeysound_; + keysoundIndex = keysoundIndex_; } bool operator==( const TapNote &other ) { #define COMPARE(x) if(x!=other.x) return false; COMPARE(type); COMPARE(source); -// COMPARE(bAttack); -// COMPARE(bKeysound); + COMPARE(bAttack); + COMPARE(bKeysound); COMPARE(attackIndex); -// COMPARE(keysoundIndex); + COMPARE(keysoundIndex); #undef COMPARE return true; } diff --git a/stepmania/src/NotesLoaderBMS.cpp b/stepmania/src/NotesLoaderBMS.cpp index 240d5d857d..0adfbcb16e 100644 --- a/stepmania/src/NotesLoaderBMS.cpp +++ b/stepmania/src/NotesLoaderBMS.cpp @@ -71,16 +71,16 @@ enum BmsTrack BMS_TRACK_INVALID, }; -static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, TapNote &tapNoteOut ) +static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, bool &bIsHoldOut ) { if( iRawTrack > 40 ) { - tapNoteOut = TAP_ORIGINAL_HOLD_HEAD; + bIsHoldOut = true; iRawTrack -= 40; } else { - tapNoteOut = TAP_ORIGINAL_TAP; + bIsHoldOut = false; } switch( iRawTrack ) @@ -178,7 +178,7 @@ static StepsType DetermineStepsType( int iPlayer, const NoteData &nd ) } } -bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out ) +bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map &mapWavIdToKeysoundIndex ) { LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() ); @@ -262,7 +262,7 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out ) { out.SetMeter(atoi(value_data)); } - else if( value_name.size() >= 6 && value_name[0] == '#' + else if( value_name.size() == 6 && value_name[0] == '#' && IsAnInt( value_name.substr(1,3) ) && IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705" { @@ -270,29 +270,46 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out ) int iRawTrackNum = atoi( value_name.substr(4,2).c_str() ); CString &sNoteData = value_data; - vector arrayNotes; + vector vTapNotes; for( int i=0; i+1::const_iterator it = mapWavIdToKeysoundIndex.find(sNoteId); + if( it != mapWavIdToKeysoundIndex.end() ) + { + tn.bKeysound = true; + tn.keysoundIndex = it->second; + } + vTapNotes.push_back( tn ); + } + else + { + vTapNotes.push_back( TAP_EMPTY ); + } } - const unsigned iNumNotesInThisMeasure = arrayNotes.size(); + const unsigned uNumNotesInThisMeasure = vTapNotes.size(); - for( unsigned j=0; jSetTapNote(bmsTrack, iNoteIndex, tapNote); + bool bIsHold; + if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) ) + { + TapNote tn = vTapNotes[j]; + tn.type = bIsHold ? TapNote::hold_head : TapNote::tap; + pNoteData->SetTapNote(bmsTrack, iNoteIndex, tn); + } } } } @@ -434,6 +451,8 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out ) { LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir.c_str() ); + ASSERT( out.m_vsKeysoundFile.empty() ); + CStringArray arrayBMSFileNames; GetApplicableFiles( sDir, arrayBMSFileNames ); @@ -441,20 +460,9 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out ) * called to begin with. */ ASSERT( arrayBMSFileNames.size() ); - // load the Steps from the rest of the BMS files - for( unsigned i=0; i mapWavIdToKeysoundIndex; CString sPath = out.GetSongDir() + arrayBMSFileNames[0]; @@ -535,7 +543,15 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out ) { out.m_sMusicFile = value_data; } - else if( value_name.size() >= 6 && value_name[0] == '#' + else if( value_name.size() == 6 && value_name.Left(4) == "#wav" ) // this is keysound file name. Looks like "#WAV1A" + { + CString sWavID = value_name.Right(2); + sWavID.MakeUpper(); // HACK: undo the MakeLower() + out.m_vsKeysoundFile.push_back( value_data ); + mapWavIdToKeysoundIndex[ sWavID ] = out.m_vsKeysoundFile.size()-1; + LOG->Trace( "Inserting keysound index %u '%s'", out.m_vsKeysoundFile.size()-1, sWavID.c_str() ); + } + else if( value_name.size() == 6 && value_name[0] == '#' && IsAnInt( value_name.substr(1,3) ) && IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705" { @@ -730,6 +746,27 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out ) LOG->Trace( "There is a BPM change at beat %f, BPM %f, index %d", out.m_Timing.m_BPMSegments[i].m_fStartBeat, out.m_Timing.m_BPMSegments[i].m_fBPM, i ); + + // Now that we've parsed the keysound data, load the Steps from the rest + // of the .bms files. + for( unsigned i=0; i class Song; class Steps; class BMSLoader: public NotesLoader { - bool LoadFromBMSFile( const CString &sPath, Steps &out1 ); + bool LoadFromBMSFile( const CString &sPath, Steps &out1, const map &mapWavIdToKeysoundIndex ); void SlideDuplicateDifficulties( Song &p ); diff --git a/stepmania/src/NotesLoaderSM.cpp b/stepmania/src/NotesLoaderSM.cpp index a680cfd193..b6f59e7f95 100644 --- a/stepmania/src/NotesLoaderSM.cpp +++ b/stepmania/src/NotesLoaderSM.cpp @@ -335,7 +335,18 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out ) } } - else if( 0==stricmp(sValueName,"NOTES") ) + else if( 0==stricmp(sValueName,"KEYSOUNDS") ) + { + CStringArray aKeysoundFiles; + split( sParams[1], ",", aKeysoundFiles ); + + for( unsigned k=0; k=8)?sParams[7]:CString(""), - *pNewNotes); + sParams[1], + sParams[2], + sParams[3], + sParams[4], + sParams[5], + sParams[6], + (iNumParams>=8)?sParams[7]:CString(""), + *pNewNotes ); out.AddSteps( pNewNotes ); } diff --git a/stepmania/src/NotesWriterSM.cpp b/stepmania/src/NotesWriterSM.cpp index 2f933675e9..4d4afd0145 100644 --- a/stepmania/src/NotesWriterSM.cpp +++ b/stepmania/src/NotesWriterSM.cpp @@ -107,6 +107,15 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out ) } f.PutLine( ";" ); } + + f.Write( "#KEYSOUNDS:" ); + for( unsigned i=0; i &lines, bool SkipLeadingBlankLines, bool OmitLastNewline ) @@ -127,12 +136,12 @@ static void WriteLineList( RageFile &f, vector &lines, bool SkipLeading } } -void NotesWriterSM::WriteSMNotesTag( const Steps &in, RageFile &f, bool bSavingCache ) +void NotesWriterSM::WriteSMNotesTag( const Song &song, const Steps &in, RageFile &f, bool bSavingCache ) { f.PutLine( "" ); f.PutLine( ssprintf( "//---------------%s - %s----------------", GameManager::StepsTypeToString(in.m_StepsType).c_str(), in.GetDescription().c_str() ) ); - f.PutLine( "#NOTES:" ); + f.PutLine( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" ); f.PutLine( ssprintf( " %s:", GameManager::StepsTypeToString(in.m_StepsType).c_str() ) ); f.PutLine( ssprintf( " %s:", in.GetDescription().c_str() ) ); f.PutLine( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); @@ -217,7 +226,7 @@ bool NotesWriterSM::Write(CString sPath, const Song &out, bool bSavingCache) if( pSteps->WasLoadedFromProfile() ) continue; - WriteSMNotesTag( *pSteps, f, bSavingCache ); + WriteSMNotesTag( out, *pSteps, f, bSavingCache ); } return true; diff --git a/stepmania/src/NotesWriterSM.h b/stepmania/src/NotesWriterSM.h index eaa6596ae3..5f050ccb26 100644 --- a/stepmania/src/NotesWriterSM.h +++ b/stepmania/src/NotesWriterSM.h @@ -9,7 +9,7 @@ class RageFile; class NotesWriterSM { void WriteGlobalTags( RageFile &f, const Song &out ); - void WriteSMNotesTag( const Steps &in, RageFile &f, bool bSavingCache ); + void WriteSMNotesTag( const Song &song, const Steps &in, RageFile &f, bool bSavingCache ); public: bool Write( CString sPath, const Song &out, bool bSavingCache ); diff --git a/stepmania/src/song.h b/stepmania/src/song.h index 27f21337b6..11c863ebdb 100644 --- a/stepmania/src/song.h +++ b/stepmania/src/song.h @@ -195,6 +195,13 @@ public: int GetNumStepsLoadedFromProfile( ProfileSlot slot ) const; bool IsEditAlreadyLoaded( Steps* pSteps ) const; + // An array of keysound file names (e.g. "beep.wav"). + // The index in this array corresponds to the index in TapNote. If you + // change the index in here, you must change all NoteData too. + // Any note that doesn't have a value in the range of this array + // means "this note doens't have a keysound". + vector m_vsKeysoundFile; + private: void AdjustDuplicateSteps(); // part of TidyUpData void DeleteDuplicateSteps( vector &vSteps );