Parse and maintain keysound data, but don't play the keysounds yet.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<min(sMeasureLine.GetLength(),out.GetNumTracks()); c++ )
|
||||
const char *p = sMeasureLine;
|
||||
int iTrack = 0;
|
||||
while( *p )
|
||||
{
|
||||
TapNote t;
|
||||
char ch = sMeasureLine[c];
|
||||
TapNote tn;
|
||||
char ch = *p;
|
||||
switch( ch )
|
||||
{
|
||||
case '0': t = TAP_EMPTY; break;
|
||||
case '1': t = TAP_ORIGINAL_TAP; break;
|
||||
case '2': t = TAP_ORIGINAL_HOLD_HEAD; break;
|
||||
case '3': t = TAP_ORIGINAL_HOLD_TAIL; break;
|
||||
case '0': tn = TAP_EMPTY; break;
|
||||
case '1': tn = TAP_ORIGINAL_TAP; break;
|
||||
case '2': tn = TAP_ORIGINAL_HOLD_HEAD; break;
|
||||
case '3': tn = TAP_ORIGINAL_HOLD_TAIL; break;
|
||||
// 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':
|
||||
t = TAP_ORIGINAL_MINE; break;
|
||||
case 'M': tn = TAP_ORIGINAL_MINE; break;
|
||||
default:
|
||||
if( ch >= '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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<CString,unsigned> &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<bool> arrayNotes;
|
||||
vector<TapNote> vTapNotes;
|
||||
|
||||
for( int i=0; i+1<sNoteData.GetLength(); i+=2 )
|
||||
{
|
||||
bool bThisIsANote = sNoteData.substr(i,2) != "00";
|
||||
arrayNotes.push_back( bThisIsANote );
|
||||
CString sNoteId = sNoteData.substr(i,2);
|
||||
if( sNoteId != "00" )
|
||||
{
|
||||
TapNote tn = TAP_ORIGINAL_TAP;
|
||||
map<CString,unsigned>::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; j<iNumNotesInThisMeasure; j++ )
|
||||
for( unsigned j=0; j<uNumNotesInThisMeasure; j++ )
|
||||
{
|
||||
if( arrayNotes[j] )
|
||||
if( vTapNotes[j].type != TapNote::empty )
|
||||
{
|
||||
float fPercentThroughMeasure = (float)j/(float)iNumNotesInThisMeasure;
|
||||
float fPercentThroughMeasure = (float)j/(float)uNumNotesInThisMeasure;
|
||||
|
||||
const int iNoteIndex = (int) ( (iMeasureNo + fPercentThroughMeasure)
|
||||
* BEATS_PER_MEASURE * ROWS_PER_BEAT );
|
||||
BmsTrack bmsTrack;
|
||||
TapNote tapNote;
|
||||
|
||||
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, tapNote) )
|
||||
pNoteData->SetTapNote(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<arrayBMSFileNames.size(); i++ )
|
||||
{
|
||||
Steps* pNewNotes = new Steps;
|
||||
|
||||
const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i],
|
||||
*pNewNotes );
|
||||
if( ok )
|
||||
out.AddSteps( pNewNotes );
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
|
||||
SlideDuplicateDifficulties( out );
|
||||
// This maps from a BMS wav ID (e.g. "1A") to an entry in the Song's
|
||||
// keysound vector. Fill this in below while parsing the song data.
|
||||
map<CString,unsigned> 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<arrayBMSFileNames.size(); i++ )
|
||||
{
|
||||
Steps* pNewNotes = new Steps;
|
||||
|
||||
const bool ok = LoadFromBMSFile(
|
||||
out.GetSongDir() + arrayBMSFileNames[i],
|
||||
*pNewNotes,
|
||||
mapWavIdToKeysoundIndex );
|
||||
if( ok )
|
||||
out.AddSteps( pNewNotes );
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
|
||||
SlideDuplicateDifficulties( out );
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
#include "NotesLoader.h"
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "NoteTypes.h"
|
||||
#include <map>
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
|
||||
class BMSLoader: public NotesLoader
|
||||
{
|
||||
bool LoadFromBMSFile( const CString &sPath, Steps &out1 );
|
||||
bool LoadFromBMSFile( const CString &sPath, Steps &out1, const map<CString,unsigned> &mapWavIdToKeysoundIndex );
|
||||
|
||||
void SlideDuplicateDifficulties( Song &p );
|
||||
|
||||
|
||||
@@ -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<aKeysoundFiles.size(); k++ )
|
||||
{
|
||||
out.m_vsKeysoundFile.push_back( aKeysoundFiles[k] );
|
||||
}
|
||||
}
|
||||
|
||||
else if( 0==stricmp(sValueName,"NOTES") || 0==stricmp(sValueName,"NOTES2") )
|
||||
{
|
||||
Steps* pNewNotes = new Steps;
|
||||
ASSERT( pNewNotes );
|
||||
@@ -347,8 +358,14 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
|
||||
}
|
||||
|
||||
LoadFromSMTokens(
|
||||
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], (iNumParams>=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 );
|
||||
}
|
||||
|
||||
@@ -107,6 +107,15 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out )
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
f.Write( "#KEYSOUNDS:" );
|
||||
for( unsigned i=0; i<out.m_vsKeysoundFile.size(); i++ )
|
||||
{
|
||||
f.Write( out.m_vsKeysoundFile[i] );
|
||||
if( i != out.m_vsKeysoundFile.size()-1 )
|
||||
f.Write( "," );
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
}
|
||||
|
||||
static void WriteLineList( RageFile &f, vector<CString> &lines, bool SkipLeadingBlankLines, bool OmitLastNewline )
|
||||
@@ -127,12 +136,12 @@ static void WriteLineList( RageFile &f, vector<CString> &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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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<CString> m_vsKeysoundFile;
|
||||
|
||||
private:
|
||||
void AdjustDuplicateSteps(); // part of TidyUpData
|
||||
void DeleteDuplicateSteps( vector<Steps*> &vSteps );
|
||||
|
||||
Reference in New Issue
Block a user