No longer store #NOTES in cache.
Use the #STEPFILENAME tag to access the files as appropriate. To the betting pool fans: file cache version is 192.
This commit is contained in:
@@ -9,6 +9,10 @@ change to JSON, but it is unsure if this will be done.
|
||||
Implement .ssc at your own risk.
|
||||
________________________________________________________________________________
|
||||
|
||||
[v0.77] - Wolfman2000
|
||||
* Add the cache tag #STEPFILENAME to gain access to the proper #NOTES when
|
||||
required.
|
||||
|
||||
[v0.76] - Wolfman2000
|
||||
* Expand the #COMBOS tag to allow for Miss Combos. Anyone that is crazy enough
|
||||
to put in a 51 miss combo in their tags should be...oh wait: it's already
|
||||
|
||||
@@ -8,6 +8,13 @@ ________________________________________________________________________________
|
||||
StepMania 5.0 ????????? | 20110???
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
2011/07/17
|
||||
----------
|
||||
* [Cache] No longer store #NOTES in the cache. This should speed things up
|
||||
on the first load if there is not much to write. (General note: this was
|
||||
taken from another branch, but results at that point were great.)
|
||||
[Wolfman2000]
|
||||
|
||||
2011/07/13
|
||||
----------
|
||||
* [Difficulty] Allow ALL StepsTypes to use CustomDifficulty properly.
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
#SAMPLELENGTH:;
|
||||
#DISPLAYBPM:[xxx][xxx:xxx]|[*];
|
||||
#SELECTABLE:;
|
||||
#LASTBEATHINT:;
|
||||
#LASTSECONDHINT:;
|
||||
#BGCHANGES:;
|
||||
#FGCHANGES:;
|
||||
#KEYSOUNDS:;
|
||||
#ATTACKS:;
|
||||
|
||||
// stored in cache
|
||||
#FIRSTBEAT:; // calculated
|
||||
#LASTBEAT:; // calculated
|
||||
#FIRSTSECOND:; // calculated
|
||||
#LASTSECOND:; // calculated
|
||||
#SONGFILENAME:;
|
||||
#MUSICBYTES:; // ignored by loader
|
||||
#HASMUSIC:;
|
||||
@@ -66,5 +66,8 @@
|
||||
#ATTACKS:;
|
||||
#DISPLAYBPM:;
|
||||
|
||||
// actual step data
|
||||
// only in cache files
|
||||
#STEPFILENAME:;
|
||||
|
||||
// actual step data (not in cache files)
|
||||
#NOTES:;
|
||||
|
||||
+78
-4
@@ -459,13 +459,16 @@ static bool SearchForKeysound( const RString &sPath, RString nDataOriginal, map<
|
||||
* Do a search. Don't do a wildcard search; if sData is "song.wav",
|
||||
* we might also have "song.png", which we shouldn't match. */
|
||||
RString nData = nDataOriginal;
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
RString dir = out.GetSongDir();
|
||||
if (dir.empty())
|
||||
dir = Dirname(sPath);
|
||||
if( !IsAFile(dir+nData) )
|
||||
{
|
||||
const char *exts[] = { "oga", "ogg", "wav", "mp3", NULL }; // XXX: stop duplicating these everywhere
|
||||
for( unsigned i = 0; exts[i] != NULL; ++i )
|
||||
{
|
||||
RString fn = SetExtension( nData, exts[i] );
|
||||
if( IsAFile(out.GetSongDir()+fn) )
|
||||
if( IsAFile(dir+fn) )
|
||||
{
|
||||
nData = fn;
|
||||
break;
|
||||
@@ -473,9 +476,9 @@ static bool SearchForKeysound( const RString &sPath, RString nDataOriginal, map<
|
||||
}
|
||||
}
|
||||
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
if( !IsAFile(dir+nData) )
|
||||
{
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
||||
LOG->UserLog( "Song file", dir, "references key \"%s\" that can't be found", nData.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1067,6 +1070,75 @@ void BMSLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.bml"), out );
|
||||
}
|
||||
|
||||
bool BMSLoader::LoadNoteDataFromSimfile( const RString & cachePath, Steps & out )
|
||||
{
|
||||
Song dummy;
|
||||
// TODO: Simplify this copy/paste from LoadFromDir.
|
||||
|
||||
vector<NameToData_t> BMSData;
|
||||
BMSData.push_back(NameToData_t());
|
||||
ReadBMSFile(cachePath, BMSData.back());
|
||||
|
||||
RString commonSubstring;
|
||||
GetCommonTagFromMapList( BMSData, "#title", commonSubstring );
|
||||
|
||||
Steps *copy = dummy.CreateSteps();
|
||||
|
||||
copy->SetDifficulty( Difficulty_Medium );
|
||||
RString sTag;
|
||||
if( GetTagFromMap( BMSData[0], "#title", sTag ) && sTag.size() != commonSubstring.size() )
|
||||
{
|
||||
sTag = sTag.substr( commonSubstring.size(), sTag.size() - commonSubstring.size() );
|
||||
sTag.MakeLower();
|
||||
|
||||
if( sTag.find('l') != sTag.npos )
|
||||
{
|
||||
unsigned lPos = sTag.find('l');
|
||||
if( lPos > 2 && sTag.substr(lPos-2,4) == "solo" )
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Edit );
|
||||
}
|
||||
else
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Easy );
|
||||
}
|
||||
}
|
||||
else if( sTag.find('a') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Hard );
|
||||
else if( sTag.find('b') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Beginner );
|
||||
}
|
||||
if( commonSubstring == "" )
|
||||
{
|
||||
copy->SetDifficulty(Difficulty_Medium);
|
||||
RString sTag;
|
||||
if (GetTagFromMap(BMSData[0], "#title#", sTag))
|
||||
SearchForDifficulty(sTag, copy);
|
||||
}
|
||||
ReadGlobalTags( BMSData[0], dummy );
|
||||
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
||||
{
|
||||
switch( commonSubstring[commonSubstring.size() - 1] )
|
||||
{
|
||||
case '[':
|
||||
case '(':
|
||||
case '<':
|
||||
commonSubstring = commonSubstring.substr(0, commonSubstring.size() - 2);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
map<RString, int> mapFilenameToKeysoundIndex;
|
||||
|
||||
|
||||
const bool ok = LoadFromBMSFile( cachePath, BMSData[0], *copy, dummy, mapFilenameToKeysoundIndex );
|
||||
if( ok )
|
||||
{
|
||||
out.SetNoteData(copy->GetNoteData());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir.c_str() );
|
||||
@@ -1171,6 +1243,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
iMainDataIndex = i;
|
||||
|
||||
ReadGlobalTags( aBMSData[iMainDataIndex], out );
|
||||
out.m_sSongFileName = out.GetSongDir() + arrayBMSFileNames[iMainDataIndex];
|
||||
|
||||
// The brackets before the difficulty are in common substring, so remove them if it's found.
|
||||
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
||||
@@ -1203,6 +1276,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
if( i == static_cast<unsigned>(iMainDataIndex) )
|
||||
out.m_SongTiming = pNewNotes->m_Timing;
|
||||
|
||||
pNewNotes->SetFilename(out.GetSongDir() + arrayBMSFileNames[i]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define NOTES_LOADER_BMS_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
/** @brief Reads a Song from a set of .BMS files. */
|
||||
namespace BMSLoader
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sDir, Song &out );
|
||||
bool LoadNoteDataFromSimfile( const RString & cachePath, Steps &out );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+281
-206
@@ -170,6 +170,238 @@ Difficulty DwiCompatibleStringToDifficulty( const RString& sDC )
|
||||
else return Difficulty_Invalid;
|
||||
}
|
||||
|
||||
static StepsType GetTypeFromMode(const RString &mode)
|
||||
{
|
||||
if( mode == "SINGLE" )
|
||||
return StepsType_dance_single;
|
||||
else if( mode == "DOUBLE" )
|
||||
return StepsType_dance_double;
|
||||
else if( mode == "COUPLE" )
|
||||
return StepsType_dance_couple;
|
||||
else if( mode == "SOLO" )
|
||||
return StepsType_dance_solo;
|
||||
ASSERT_M(0, "Unrecognized DWI notes format " + mode + "!");
|
||||
return StepsType_Invalid; // just in case.
|
||||
}
|
||||
|
||||
static NoteData ParseNoteData(RString &step1, RString &step2,
|
||||
Steps &out, const RString &path)
|
||||
{
|
||||
g_mapDanceNoteToNoteDataColumn.clear();
|
||||
switch( out.m_StepsType )
|
||||
{
|
||||
case StepsType_dance_single:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
break;
|
||||
case StepsType_dance_double:
|
||||
case StepsType_dance_couple:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_LEFT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_DOWN] = 5;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_UP] = 6;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_RIGHT] = 7;
|
||||
break;
|
||||
case StepsType_dance_solo:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPLEFT] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPRIGHT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 5;
|
||||
break;
|
||||
DEFAULT_FAIL( out.m_StepsType );
|
||||
}
|
||||
|
||||
NoteData newNoteData;
|
||||
newNoteData.SetNumTracks( g_mapDanceNoteToNoteDataColumn.size() );
|
||||
|
||||
for( int pad=0; pad<2; pad++ ) // foreach pad
|
||||
{
|
||||
RString sStepData;
|
||||
switch( pad )
|
||||
{
|
||||
case 0:
|
||||
sStepData = step1;
|
||||
break;
|
||||
case 1:
|
||||
if( step2 == "" ) // no data
|
||||
continue; // skip
|
||||
sStepData = step2;
|
||||
break;
|
||||
DEFAULT_FAIL( pad );
|
||||
}
|
||||
|
||||
sStepData.Replace("\n", "");
|
||||
sStepData.Replace("\r", "");
|
||||
sStepData.Replace("\t", "");
|
||||
sStepData.Replace(" ", "");
|
||||
|
||||
double fCurrentBeat = 0;
|
||||
double fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
|
||||
for( size_t i=0; i<sStepData.size(); )
|
||||
{
|
||||
char c = sStepData[i++];
|
||||
switch( c )
|
||||
{
|
||||
// begins a series
|
||||
case '(':
|
||||
fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '[':
|
||||
fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '{':
|
||||
fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '`':
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
// ends a series
|
||||
case ')':
|
||||
case ']':
|
||||
case '}':
|
||||
case '\'':
|
||||
case '>':
|
||||
fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
default: // this is a note character
|
||||
{
|
||||
if( c == '!' )
|
||||
{
|
||||
LOG->UserLog(
|
||||
"Song file",
|
||||
path,
|
||||
"has an unexpected character: '!'." );
|
||||
continue;
|
||||
}
|
||||
|
||||
bool jump = false;
|
||||
if( c == '<' )
|
||||
{
|
||||
/* Arr. Is this a jump or a 1/192 marker? */
|
||||
if( Is192( sStepData, i ) )
|
||||
{
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* It's a jump.
|
||||
* We need to keep reading notes until we hit a >. */
|
||||
jump = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
const int iIndex = BeatToNoteRow( (float)fCurrentBeat );
|
||||
i--;
|
||||
do {
|
||||
c = sStepData[i++];
|
||||
|
||||
if( jump && c == '>' )
|
||||
break;
|
||||
|
||||
int iCol1, iCol2;
|
||||
DWIcharToNoteCol(
|
||||
c,
|
||||
(GameController)pad,
|
||||
iCol1,
|
||||
iCol2,
|
||||
path );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_TAP);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_TAP);
|
||||
|
||||
if(i>=sStepData.length())
|
||||
{
|
||||
break;
|
||||
//we ran out of data
|
||||
//while looking for the ending > mark
|
||||
}
|
||||
|
||||
if( sStepData[i] == '!' )
|
||||
{
|
||||
i++;
|
||||
const char holdChar = sStepData[i++];
|
||||
|
||||
DWIcharToNoteCol(holdChar,
|
||||
(GameController)pad,
|
||||
iCol1,
|
||||
iCol2,
|
||||
path );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_HOLD_HEAD);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2,
|
||||
iIndex,
|
||||
TAP_ORIGINAL_HOLD_HEAD);
|
||||
}
|
||||
}
|
||||
while( jump );
|
||||
fCurrentBeat += fCurrentIncrementer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in iDuration. */
|
||||
for( int t=0; t<newNoteData.GetNumTracks(); ++t )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow )
|
||||
{
|
||||
TapNote tn = newNoteData.GetTapNote( t, iHeadRow );
|
||||
if( tn.type != TapNote::hold_head )
|
||||
continue;
|
||||
|
||||
int iTailRow = iHeadRow;
|
||||
bool bFound = false;
|
||||
while( !bFound && newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) )
|
||||
{
|
||||
const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow );
|
||||
if( TailTap.type == TapNote::empty )
|
||||
continue;
|
||||
|
||||
newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY );
|
||||
tn.iDuration = iTailRow - iHeadRow;
|
||||
newNoteData.SetTapNote( t, iHeadRow, tn );
|
||||
bFound = true;
|
||||
}
|
||||
|
||||
if( !bFound )
|
||||
{
|
||||
/* The hold was never closed. */
|
||||
LOG->UserLog("Song file",
|
||||
path,
|
||||
"failed to close a hold note in \"%s\" on track %i",
|
||||
DifficultyToString(out.GetDifficulty()).c_str(),
|
||||
t);
|
||||
|
||||
newNoteData.SetTapNote( t, iHeadRow, TAP_EMPTY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT( newNoteData.GetNumTracks() > 0 );
|
||||
return newNoteData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Look through the notes tag to extract the data.
|
||||
* @param sMode the steps type.
|
||||
@@ -192,216 +424,17 @@ static bool LoadFromDWITokens(
|
||||
{
|
||||
CHECKPOINT_M( "DWILoader::LoadFromDWITokens()" );
|
||||
|
||||
out.m_StepsType = StepsType_Invalid;
|
||||
out.m_StepsType = GetTypeFromMode(sMode);
|
||||
|
||||
if( sMode == "SINGLE" ) out.m_StepsType = StepsType_dance_single;
|
||||
else if( sMode == "DOUBLE" ) out.m_StepsType = StepsType_dance_double;
|
||||
else if( sMode == "COUPLE" ) out.m_StepsType = StepsType_dance_couple;
|
||||
else if( sMode == "SOLO" ) out.m_StepsType = StepsType_dance_solo;
|
||||
else
|
||||
{
|
||||
ASSERT_M(0, "Unrecognized DWI notes format " + sMode + "!");
|
||||
out.m_StepsType = StepsType_dance_single;
|
||||
}
|
||||
out.SetMeter(StringToInt(sNumFeet));
|
||||
|
||||
|
||||
g_mapDanceNoteToNoteDataColumn.clear();
|
||||
switch( out.m_StepsType )
|
||||
{
|
||||
case StepsType_dance_single:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
break;
|
||||
case StepsType_dance_double:
|
||||
case StepsType_dance_couple:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_LEFT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_DOWN] = 5;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_UP] = 6;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_RIGHT] = 7;
|
||||
break;
|
||||
case StepsType_dance_solo:
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPLEFT] = 1;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 2;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 3;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPRIGHT] = 4;
|
||||
g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 5;
|
||||
break;
|
||||
DEFAULT_FAIL( out.m_StepsType );
|
||||
}
|
||||
|
||||
int iNumFeet = StringToInt(sNumFeet);
|
||||
// out.SetDescription(sDescription); // Don't put garbage in the description.
|
||||
out.SetMeter(iNumFeet);
|
||||
out.SetDifficulty( DwiCompatibleStringToDifficulty(sDescription) );
|
||||
|
||||
NoteData newNoteData;
|
||||
newNoteData.SetNumTracks( g_mapDanceNoteToNoteDataColumn.size() );
|
||||
|
||||
for( int pad=0; pad<2; pad++ ) // foreach pad
|
||||
{
|
||||
RString sStepData;
|
||||
switch( pad )
|
||||
{
|
||||
case 0:
|
||||
sStepData = sStepData1;
|
||||
break;
|
||||
case 1:
|
||||
if( sStepData2 == "" ) // no data
|
||||
continue; // skip
|
||||
sStepData = sStepData2;
|
||||
break;
|
||||
DEFAULT_FAIL( pad );
|
||||
}
|
||||
|
||||
sStepData.Replace("\n", "");
|
||||
sStepData.Replace("\r", "");
|
||||
sStepData.Replace("\t", "");
|
||||
sStepData.Replace(" ", "");
|
||||
|
||||
double fCurrentBeat = 0;
|
||||
double fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
|
||||
for( size_t i=0; i<sStepData.size(); )
|
||||
{
|
||||
char c = sStepData[i++];
|
||||
switch( c )
|
||||
{
|
||||
// begins a series
|
||||
case '(':
|
||||
fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '[':
|
||||
fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '{':
|
||||
fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
case '`':
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
// ends a series
|
||||
case ')':
|
||||
case ']':
|
||||
case '}':
|
||||
case '\'':
|
||||
case '>':
|
||||
fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
|
||||
default: // this is a note character
|
||||
{
|
||||
if( c == '!' )
|
||||
{
|
||||
LOG->UserLog( "Song file", sPath, "has an unexpected character: '!'." );
|
||||
continue;
|
||||
}
|
||||
|
||||
bool jump = false;
|
||||
if( c == '<' )
|
||||
{
|
||||
/* Arr. Is this a jump or a 1/192 marker? */
|
||||
if( Is192( sStepData, i ) )
|
||||
{
|
||||
fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* It's a jump. We need to keep reading notes until we hit a >. */
|
||||
jump = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
const int iIndex = BeatToNoteRow( (float)fCurrentBeat );
|
||||
i--;
|
||||
do {
|
||||
c = sStepData[i++];
|
||||
|
||||
if( jump && c == '>' )
|
||||
break;
|
||||
|
||||
int iCol1, iCol2;
|
||||
DWIcharToNoteCol( c, (GameController)pad, iCol1, iCol2, sPath );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_TAP);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_TAP);
|
||||
|
||||
if(i>=sStepData.length()) {
|
||||
break;//we ran out of data while looking for the ending > mark
|
||||
}
|
||||
|
||||
if( sStepData[i] == '!' )
|
||||
{
|
||||
i++;
|
||||
const char holdChar = sStepData[i++];
|
||||
|
||||
DWIcharToNoteCol( holdChar, (GameController)pad, iCol1, iCol2, sPath );
|
||||
|
||||
if( iCol1 != -1 )
|
||||
newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_HOLD_HEAD);
|
||||
if( iCol2 != -1 )
|
||||
newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_HOLD_HEAD);
|
||||
}
|
||||
}
|
||||
while( jump );
|
||||
fCurrentBeat += fCurrentIncrementer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in iDuration. */
|
||||
for( int t=0; t<newNoteData.GetNumTracks(); ++t )
|
||||
{
|
||||
FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow )
|
||||
{
|
||||
TapNote tn = newNoteData.GetTapNote( t, iHeadRow );
|
||||
if( tn.type != TapNote::hold_head )
|
||||
continue;
|
||||
|
||||
int iTailRow = iHeadRow;
|
||||
bool bFound = false;
|
||||
while( !bFound && newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) )
|
||||
{
|
||||
const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow );
|
||||
if( TailTap.type == TapNote::empty )
|
||||
continue;
|
||||
|
||||
newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY );
|
||||
tn.iDuration = iTailRow - iHeadRow;
|
||||
newNoteData.SetTapNote( t, iHeadRow, tn );
|
||||
bFound = true;
|
||||
}
|
||||
|
||||
if( !bFound )
|
||||
{
|
||||
/* The hold was never closed. */
|
||||
LOG->UserLog( "Song file", sPath, "failed to close a hold note in \"%s\" on track %i",
|
||||
sDescription.c_str(), t );
|
||||
|
||||
newNoteData.SetTapNote( t, iHeadRow, TAP_EMPTY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT( newNoteData.GetNumTracks() > 0 );
|
||||
|
||||
out.SetNoteData( newNoteData );
|
||||
|
||||
out.SetNoteData( ParseNoteData(sStepData1, sStepData2, out, sPath) );
|
||||
|
||||
out.TidyUpData();
|
||||
|
||||
out.SetSavedToDisk( true ); // we're loading from disk, so this is by definintion already saved
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -444,17 +477,55 @@ void DWILoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.dwi"), out );
|
||||
}
|
||||
|
||||
bool DWILoader::LoadNoteDataFromSimfile( const RString &path, Steps &out )
|
||||
{
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( path, false ) ) // don't unescape
|
||||
{
|
||||
LOG->UserLog("Song file",
|
||||
path,
|
||||
"couldn't be opened: %s",
|
||||
msd.GetError().c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
int iNumParams = msd.GetNumParams(i);
|
||||
const MsdFile::value_t ¶ms = msd.GetValue(i);
|
||||
RString valueName = params[0];
|
||||
|
||||
if(valueName.EqualsNoCase("SINGLE") ||
|
||||
valueName.EqualsNoCase("DOUBLE") ||
|
||||
valueName.EqualsNoCase("COUPLE") ||
|
||||
valueName.EqualsNoCase("SOLO") )
|
||||
{
|
||||
if (out.m_StepsType != GetTypeFromMode(valueName))
|
||||
continue;
|
||||
if (out.GetDifficulty() != DwiCompatibleStringToDifficulty(params[1]))
|
||||
continue;
|
||||
if (out.GetMeter() != StringToInt(params[2]))
|
||||
continue;
|
||||
RString step1 = params[3];
|
||||
RString step2 = (iNumParams==5) ? params[4] : RString("");
|
||||
out.SetNoteData(ParseNoteData(step1, step2, out, path));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &BlacklistedImages )
|
||||
{
|
||||
vector<RString> aFileNames;
|
||||
GetApplicableFiles( sPath_, aFileNames );
|
||||
|
||||
|
||||
if( aFileNames.size() > 1 )
|
||||
{
|
||||
LOG->UserLog( "Song", sPath_, "has more than one DWI file. There should be only one!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* We should have exactly one; if we had none, we shouldn't have been called to begin with. */
|
||||
ASSERT( aFileNames.size() == 1 );
|
||||
const RString sPath = sPath_ + aFileNames[0];
|
||||
@@ -468,6 +539,8 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
return false;
|
||||
}
|
||||
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
int iNumParams = msd.GetNumParams(i);
|
||||
@@ -629,7 +702,10 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
sPath
|
||||
);
|
||||
if( pNewNotes->m_StepsType != StepsType_Invalid )
|
||||
{
|
||||
pNewNotes->SetFilename( sPath );
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
@@ -664,7 +740,6 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set<RString> &Bla
|
||||
// do nothing. We don't care about this value name
|
||||
}
|
||||
}
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <set>
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
|
||||
/** @brief The DWILoader handles parsing the .dwi file. */
|
||||
namespace DWILoader
|
||||
@@ -24,6 +25,8 @@ namespace DWILoader
|
||||
* @return its success or failure.
|
||||
*/
|
||||
bool LoadFromDir( const RString &sPath, Song &out, set<RString> &BlacklistedImages );
|
||||
|
||||
bool LoadNoteDataFromSimfile( const RString &path, Steps &out );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+73
-73
@@ -195,28 +195,43 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
out.SetDifficulty( Difficulty_Edit );
|
||||
if( !out.GetMeter() ) out.SetMeter( 25 );
|
||||
}
|
||||
else if( sFName.find("wild") != string::npos || sFName.find("wd") != string::npos || sFName.find("crazy+") != string::npos || sFName.find("cz+") != string::npos || sFName.find("hardcore") != string::npos )
|
||||
else if(sFName.find("wild") != string::npos ||
|
||||
sFName.find("wd") != string::npos ||
|
||||
sFName.find("crazy+") != string::npos ||
|
||||
sFName.find("cz+") != string::npos ||
|
||||
sFName.find("hardcore") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Challenge );
|
||||
if( !out.GetMeter() ) out.SetMeter( 20 );
|
||||
}
|
||||
else if( sFName.find("crazy") != string::npos || sFName.find("cz") != string::npos || sFName.find("nightmare") != string::npos || sFName.find("nm") != string::npos || sFName.find("crazydouble") != string::npos )
|
||||
else if(sFName.find("crazy") != string::npos ||
|
||||
sFName.find("cz") != string::npos ||
|
||||
sFName.find("nightmare") != string::npos ||
|
||||
sFName.find("nm") != string::npos ||
|
||||
sFName.find("crazydouble") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Hard );
|
||||
if( !out.GetMeter() ) out.SetMeter( 14 ); // Set the meters to the Pump scale, not DDR.
|
||||
}
|
||||
else if( sFName.find("hard") != string::npos || sFName.find("hd") != string::npos || sFName.find("freestyle") != string::npos || sFName.find("fs") != string::npos || sFName.find("double") != string::npos )
|
||||
else if(sFName.find("hard") != string::npos ||
|
||||
sFName.find("hd") != string::npos ||
|
||||
sFName.find("freestyle") != string::npos ||
|
||||
sFName.find("fs") != string::npos ||
|
||||
sFName.find("double") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Medium );
|
||||
if( !out.GetMeter() ) out.SetMeter( 8 );
|
||||
}
|
||||
else if( sFName.find("easy") != string::npos || sFName.find("ez") != string::npos || sFName.find("normal") != string::npos )
|
||||
else if(sFName.find("easy") != string::npos ||
|
||||
sFName.find("ez") != string::npos ||
|
||||
sFName.find("normal") != string::npos )
|
||||
{
|
||||
// I wonder if I should leave easy fall into the Beginner difficulty... -DaisuMaster
|
||||
out.SetDifficulty( Difficulty_Easy );
|
||||
if( !out.GetMeter() ) out.SetMeter( 4 );
|
||||
}
|
||||
else if( sFName.find("beginner") != string::npos || sFName.find("practice") != string::npos || sFName.find("pr") != string::npos )
|
||||
else if(sFName.find("beginner") != string::npos ||
|
||||
sFName.find("practice") != string::npos || sFName.find("pr") != string::npos )
|
||||
{
|
||||
out.SetDifficulty( Difficulty_Beginner );
|
||||
if( !out.GetMeter() ) out.SetMeter( 4 );
|
||||
@@ -230,10 +245,18 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
out.m_StepsType = StepsType_pump_single;
|
||||
|
||||
// Check for "halfdouble" before "double".
|
||||
if( sFName.find("halfdouble") != string::npos || sFName.find("half-double") != string::npos || sFName.find("h_double") != string::npos || sFName.find("hdb") != string::npos )
|
||||
if(sFName.find("halfdouble") != string::npos ||
|
||||
sFName.find("half-double") != string::npos ||
|
||||
sFName.find("h_double") != string::npos ||
|
||||
sFName.find("hdb") != string::npos )
|
||||
out.m_StepsType = StepsType_pump_halfdouble;
|
||||
// Handle bDoublesChart from above as well. -aj
|
||||
else if( sFName.find("double") != string::npos || sFName.find("nightmare") != string::npos || sFName.find("freestyle") != string::npos || sFName.find("db") != string::npos || sFName.find("nm") != string::npos || sFName.find("fs") != string::npos || bDoublesChart )
|
||||
else if(sFName.find("double") != string::npos ||
|
||||
sFName.find("nightmare") != string::npos ||
|
||||
sFName.find("freestyle") != string::npos ||
|
||||
sFName.find("db") != string::npos ||
|
||||
sFName.find("nm") != string::npos ||
|
||||
sFName.find("fs") != string::npos || bDoublesChart )
|
||||
out.m_StepsType = StepsType_pump_double;
|
||||
else if( sFName.find("_1") != string::npos )
|
||||
out.m_StepsType = StepsType_pump_single;
|
||||
@@ -280,7 +303,10 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
if( iHoldStartRow[t] == BeatToNoteRow(prevBeat) )
|
||||
notedata.SetTapNote( t, iHoldStartRow[t], TAP_ORIGINAL_TAP );
|
||||
else
|
||||
notedata.AddHoldNote( t, iHoldStartRow[t], BeatToNoteRow(prevBeat) , TAP_ORIGINAL_HOLD_HEAD );
|
||||
notedata.AddHoldNote(t,
|
||||
iHoldStartRow[t],
|
||||
BeatToNoteRow(prevBeat),
|
||||
TAP_ORIGINAL_HOLD_HEAD );
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -307,53 +333,6 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
|
||||
{
|
||||
// duh
|
||||
iTickCount = static_cast<int>(numTemp);
|
||||
//if( iTickCount > ROWS_PER_BEAT )
|
||||
|
||||
/* adapt tickcounts //
|
||||
// valid tickcounts for SM: 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 64, ROWS_PER_BEAT
|
||||
// put this inside the tickcount handling condition yes/no
|
||||
|
||||
if( iTickCount > ROWS_PER_BEAT ) // beyond 48
|
||||
{
|
||||
// clamp/scale/whatever and use scroll segments
|
||||
}
|
||||
else if( iTickCount > 32 || iTickCount < ROWS_PER_BEAT ) // ranging from 33 to 48, approximate to 32
|
||||
{
|
||||
fScrollRatio = 32;
|
||||
iTickCount = 32;
|
||||
}
|
||||
else if( iTickCount > 24 || iTickCount < 32 ) // ranging from 25 to 31, approximate to 24
|
||||
{
|
||||
fScrollRatio = 24;
|
||||
iTickCount = 24;
|
||||
}
|
||||
else if( iTickCount > 16 || iTickCount < 24 ) // ranging from 17 to 23, approximate to 16
|
||||
{
|
||||
fScrollRatio = 16;
|
||||
iTickCount = 16;
|
||||
}
|
||||
else if( iTickCount > 12 || iTickCount < 16 ) // ranging from 13 to 15, approximate to 12
|
||||
{
|
||||
fScrollRatio = 12;
|
||||
iTickCount = 12;
|
||||
}
|
||||
else if( iTickCount > 8 || iTickCount < 12 ) // ranging from 9 to 11, approximate to 8
|
||||
{
|
||||
fScrollRatio = 8;
|
||||
iTickCount = 8;
|
||||
}
|
||||
else if( iTickCount > 6 || iTickCount < 8 ) // 7, approximate to 6
|
||||
{
|
||||
fScrollRatio = 6 / iTickCountt;
|
||||
iTickCount = 6;
|
||||
}
|
||||
else if( iTickCount > 4 || iTickCount < 6 ) // 5, approximate to 4
|
||||
{
|
||||
fScrollRatio = iTickCount / 4;
|
||||
iTickCount = 4;
|
||||
}
|
||||
//*/
|
||||
|
||||
stepsTiming.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) );
|
||||
}
|
||||
else if (BeginsWith(sRowString, "|B"))
|
||||
@@ -516,7 +495,21 @@ static void LoadTags( const RString &str, Song &out )
|
||||
out.m_sArtist = artist;
|
||||
}
|
||||
|
||||
static bool LoadGlobalData( const RString &sPath, Song &out, bool bKIUCompliant )
|
||||
static void ProcessTickcounts( const RString & value, int & ticks, TimingData & timing )
|
||||
{
|
||||
/* TICKCOUNT will be used below if there are DM compliant BPM changes
|
||||
* and stops. It will be called again in LoadFromKSFFile for the
|
||||
* actual steps. */
|
||||
ticks = StringToInt( value );
|
||||
ticks = ticks > 0 ? ticks : 4;
|
||||
// add a tickcount for those using the [Player]
|
||||
// CheckpointsUseTimeSignatures metric. -aj
|
||||
// It's not with timesigs now -DaisuMaster
|
||||
TickcountSegment * tcs = new TickcountSegment(0, ticks > ROWS_PER_BEAT ? ROWS_PER_BEAT : ticks);
|
||||
timing.AddSegment( SEGMENT_TICKCOUNT, tcs );
|
||||
}
|
||||
|
||||
static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant )
|
||||
{
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( sPath, false ) ) // don't unescape
|
||||
@@ -594,17 +587,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool bKIUCompliant
|
||||
}
|
||||
else if ( sValueName=="TICKCOUNT" )
|
||||
{
|
||||
/* TICKCOUNT will be used below if there are DM compliant BPM changes
|
||||
* and stops. It will be called again in LoadFromKSFFile for the
|
||||
* actual steps. */
|
||||
iTickCount = StringToInt( sParams[1] );
|
||||
iTickCount = iTickCount > 0 ? iTickCount : 4;
|
||||
// add a tickcount for those using the [Player]
|
||||
// CheckpointsUseTimeSignatures metric. -aj
|
||||
// It's not with timesigs now -DaisuMaster
|
||||
TickcountSegment * tcs = new TickcountSegment(0);
|
||||
tcs->SetTicks(iTickCount > ROWS_PER_BEAT ? ROWS_PER_BEAT : iTickCount);
|
||||
out.m_SongTiming.AddSegment( SEGMENT_TICKCOUNT, tcs );
|
||||
ProcessTickcounts(sParams[1], iTickCount, out.m_SongTiming);
|
||||
}
|
||||
else if ( sValueName=="STEP" )
|
||||
{
|
||||
@@ -689,7 +672,6 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool bKIUCompliant
|
||||
}
|
||||
|
||||
// This is where the DMRequired test will take place.
|
||||
//if (BeginsWith(NoteRowString, "|T") || BeginsWith(NoteRowString, "|B") || BeginsWith(NoteRowString, "|D") || BeginsWith(NoteRowString, "|E") )
|
||||
if ( BeginsWith( NoteRowString, "|" ) )
|
||||
{
|
||||
// have a static timing for everything
|
||||
@@ -723,6 +705,22 @@ void KSFLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.ksf"), out );
|
||||
}
|
||||
|
||||
bool KSFLoader::LoadNoteDataFromSimfile( const RString & cachePath, Steps &out )
|
||||
{
|
||||
bool KIUCompliant = false;
|
||||
Song dummy;
|
||||
if (!LoadGlobalData(cachePath, dummy, KIUCompliant))
|
||||
return false;
|
||||
Steps *notes = dummy.CreateSteps();
|
||||
if (LoadFromKSFFile(cachePath, *notes, dummy, KIUCompliant))
|
||||
{
|
||||
KIUCompliant = true; // yeah, reusing a variable.
|
||||
out.SetNoteData(notes->GetNoteData());
|
||||
}
|
||||
delete notes;
|
||||
return KIUCompliant;
|
||||
}
|
||||
|
||||
bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
{
|
||||
LOG->Trace( "KSFLoader::LoadFromDir(%s)", sDir.c_str() );
|
||||
@@ -733,7 +731,7 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
// We shouldn't have been called to begin with if there were no KSFs.
|
||||
ASSERT( arrayKSFFileNames.size() );
|
||||
|
||||
//bool bKIUCompliant = false;
|
||||
bool bKIUCompliant = false;
|
||||
/* With Split Timing, there has to be a backup Song Timing in case
|
||||
* anything goes wrong. As these files are kept in alphabetical
|
||||
* order (hopefully), it is best to use the LAST file for timing
|
||||
@@ -751,22 +749,24 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
// for directmove though, and we're just gathering basic info anyway, and
|
||||
// most of the time all the KSF files have the same info in the #TITLE:; section
|
||||
unsigned files = arrayKSFFileNames.size();
|
||||
if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[0], out, false) )
|
||||
RString dir = out.GetSongDir();
|
||||
if( !LoadGlobalData(dir + arrayKSFFileNames[files - 1], out, bKIUCompliant) )
|
||||
return false;
|
||||
|
||||
out.m_sSongFileName = dir + arrayKSFFileNames[files - 1];
|
||||
// load the Steps from the rest of the KSF files
|
||||
for( unsigned i=0; i<files; i++ )
|
||||
{
|
||||
Steps* pNewNotes = out.CreateSteps();
|
||||
if( !LoadFromKSFFile(out.GetSongDir() + arrayKSFFileNames[i], *pNewNotes, out, false) )
|
||||
if( !LoadFromKSFFile(dir + arrayKSFFileNames[i], *pNewNotes, out, bKIUCompliant) )
|
||||
{
|
||||
delete pNewNotes;
|
||||
continue;
|
||||
}
|
||||
|
||||
pNewNotes->SetFilename(dir + arrayKSFFileNames[i]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
out.TidyUpData();
|
||||
out.TidyUpData(false, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define NOTES_LOADER_KSF_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
/** @brief Reads a Song from a set of .KSF files. */
|
||||
namespace KSFLoader
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sDir, Song &out );
|
||||
bool LoadNoteDataFromSimfile( const RString & cachePath, Steps &out );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -956,7 +956,7 @@ bool MidiLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
if( !LoadFromMidi(sDir+vsFiles[0], out) )
|
||||
return false;
|
||||
|
||||
out.TidyUpData();
|
||||
out.TidyUpData(false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+72
-7
@@ -573,7 +573,7 @@ static bool LoadFromPMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, MeasureToTimeSig_t &sigAdjustmentsOut, map<RString,int> &idToKeySoundIndexOut )
|
||||
static void ReadGlobalTags( const RString &sPath, const NameToData_t &mapNameToData, Song &out, MeasureToTimeSig_t &sigAdjustmentsOut, map<RString,int> &idToKeySoundIndexOut )
|
||||
{
|
||||
RString sData;
|
||||
if( GetTagFromMap(mapNameToData, "#title", sData) )
|
||||
@@ -611,26 +611,29 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
||||
// this is keysound file name. Looks like "#WAV1A"
|
||||
RString nData = it->second;
|
||||
RString sWavID = sName.Right(2);
|
||||
RString dir = out.GetSongDir();
|
||||
if (dir.empty())
|
||||
dir = Dirname(sPath);
|
||||
|
||||
/* Due to bugs in some programs, many PMS files have a "WAV" extension
|
||||
* on files in the PMS for files that actually have some other extension.
|
||||
* Do a search. Don't do a wildcard search; if sData is "song.wav",
|
||||
* we might also have "song.png", which we shouldn't match. */
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
if( !IsAFile(dir+nData) )
|
||||
{
|
||||
const char *exts[] = { "oga", "ogg", "wav", "mp3", NULL }; // XXX: stop duplicating these everywhere
|
||||
for( unsigned i = 0; exts[i] != NULL; ++i )
|
||||
{
|
||||
RString fn = SetExtension( nData, exts[i] );
|
||||
if( IsAFile(out.GetSongDir()+fn) )
|
||||
if( IsAFile(dir+fn) )
|
||||
{
|
||||
nData = fn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !IsAFile(out.GetSongDir()+nData) )
|
||||
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
||||
if( !IsAFile(dir+nData) )
|
||||
LOG->UserLog( "Song file", dir, "references key \"%s\" that can't be found", nData.c_str() );
|
||||
|
||||
sWavID.MakeUpper(); // HACK: undo the MakeLower()
|
||||
out.m_vsKeysoundFile.push_back( nData );
|
||||
@@ -810,6 +813,64 @@ void PMSLoader::GetApplicableFiles( const RString &sPath, vector<RString> &out )
|
||||
GetDirListing( sPath + RString("*.pms"), out );
|
||||
}
|
||||
|
||||
bool PMSLoader::LoadNoteDataFromSimfile(const RString &cachePath, Steps &out)
|
||||
{
|
||||
Song dummy;
|
||||
// TODO: Simplify this copy/paste from LoadFromDir.
|
||||
|
||||
vector<NameToData_t> BMSData;
|
||||
BMSData.push_back(NameToData_t());
|
||||
ReadPMSFile(cachePath, BMSData.back());
|
||||
|
||||
RString commonSubstring;
|
||||
GetCommonTagFromMapList( BMSData, "#title", commonSubstring );
|
||||
|
||||
Steps *copy = dummy.CreateSteps();
|
||||
|
||||
copy->SetDifficulty( Difficulty_Medium );
|
||||
RString sTag;
|
||||
if( GetTagFromMap( BMSData[0], "#title", sTag ) && sTag.size() != commonSubstring.size() )
|
||||
{
|
||||
sTag = sTag.substr( commonSubstring.size(), sTag.size() - commonSubstring.size() );
|
||||
sTag.MakeLower();
|
||||
|
||||
if( sTag.find('l') != sTag.npos )
|
||||
{
|
||||
unsigned lPos = sTag.find('l');
|
||||
if( lPos > 2 && sTag.substr(lPos-2,4) == "solo" )
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Edit );
|
||||
}
|
||||
else
|
||||
{
|
||||
copy->SetDifficulty( Difficulty_Easy );
|
||||
}
|
||||
}
|
||||
else if( sTag.find('a') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Hard );
|
||||
else if( sTag.find('b') != sTag.npos )
|
||||
copy->SetDifficulty( Difficulty_Beginner );
|
||||
}
|
||||
if( commonSubstring == "" )
|
||||
{
|
||||
copy->SetDifficulty(Difficulty_Medium);
|
||||
RString unused;
|
||||
if (GetTagFromMap(BMSData[0], "#title#", sTag))
|
||||
SearchForDifficulty(unused, copy);
|
||||
}
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
map<RString,int> idToKeysoundIndex;
|
||||
ReadGlobalTags( cachePath, BMSData[0], dummy, sigAdjustments, idToKeysoundIndex );
|
||||
|
||||
const bool ok = LoadFromPMSFile( cachePath, BMSData[0], *copy, sigAdjustments, idToKeysoundIndex );
|
||||
if( ok )
|
||||
{
|
||||
out.SetNoteData(copy->GetNoteData());
|
||||
}
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromPMSDir(%s)", sDir.c_str() );
|
||||
@@ -915,7 +976,8 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
|
||||
MeasureToTimeSig_t sigAdjustments;
|
||||
map<RString,int> idToKeysoundIndex;
|
||||
ReadGlobalTags( aPMSData[iMainDataIndex], out, sigAdjustments, idToKeysoundIndex );
|
||||
ReadGlobalTags( sDir, aPMSData[iMainDataIndex], out, sigAdjustments, idToKeysoundIndex );
|
||||
out.m_sSongFileName = out.GetSongDir() + arrayPMSFileNames[iMainDataIndex];
|
||||
|
||||
// Override what that global tag said about the title if we have a good substring.
|
||||
// Prevents clobbering and catches "MySong (7keys)" / "MySong (Another) (7keys)"
|
||||
@@ -930,7 +992,10 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
Steps* pNewNotes = apSteps[i];
|
||||
const bool ok = LoadFromPMSFile( out.GetSongDir() + arrayPMSFileNames[i], aPMSData[i], *pNewNotes, sigAdjustments, idToKeysoundIndex );
|
||||
if( ok )
|
||||
{
|
||||
pNewNotes->SetFilename(out.GetSongDir() + arrayPMSFileNames[i]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else
|
||||
delete pNewNotes;
|
||||
}
|
||||
@@ -941,7 +1006,7 @@ bool PMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
||||
ConvertString( out.m_sArtist, "utf-8,japanese" );
|
||||
ConvertString( out.m_sGenre, "utf-8,japanese" );
|
||||
|
||||
out.TidyUpData();
|
||||
out.TidyUpData(false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define NOTES_LOADER_PMS_H
|
||||
|
||||
class Song;
|
||||
class Steps;
|
||||
/** @brief Reads a Song from a set of .PMS files. */
|
||||
namespace PMSLoader
|
||||
{
|
||||
void GetApplicableFiles( const RString &sPath, vector<RString> &out );
|
||||
bool LoadFromDir( const RString &sDir, Song &out );
|
||||
bool LoadNoteDataFromSimfile(const RString & cachePath, Steps & out);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+9
-21
@@ -10,6 +10,7 @@
|
||||
#include "Song.h"
|
||||
#include "SongManager.h"
|
||||
#include "Steps.h"
|
||||
#include "Attack.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
void SMLoader::SetSongTitle(const RString & title)
|
||||
@@ -95,23 +96,6 @@ void SMLoader::LoadFromTokens(
|
||||
}
|
||||
|
||||
out.SetMeter( StringToInt(sMeter) );
|
||||
vector<RString> saValues;
|
||||
split( sRadarValues, ",", saValues, true );
|
||||
int categories = NUM_RadarCategory - 1; // Fakes aren't counted in the radar values.
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
{
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
// Can't use the foreach anymore due to flexible radar lines.
|
||||
for( RadarCategory rc = (RadarCategory)0; rc < categories;
|
||||
enum_add<RadarCategory>( rc, 1 ) )
|
||||
{
|
||||
v[pn][rc] = StringToFloat( saValues[pn*categories + rc] );
|
||||
}
|
||||
}
|
||||
out.SetCachedRadarValues( v );
|
||||
}
|
||||
|
||||
out.SetSMNoteData( sNoteData );
|
||||
|
||||
@@ -645,9 +629,8 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString
|
||||
return aBGChangeValues.size() >= 2;
|
||||
}
|
||||
|
||||
bool SMLoader::LoadNotedataFromSimfile( const RString &path, Steps &out )
|
||||
bool SMLoader::LoadNoteDataFromSimfile( const RString &path, Steps &out )
|
||||
{
|
||||
// stub: do this later.
|
||||
MsdFile msd;
|
||||
if( !msd.ReadFile( path, true ) ) // unescape
|
||||
{
|
||||
@@ -698,7 +681,7 @@ bool SMLoader::LoadNotedataFromSimfile( const RString &path, Steps &out )
|
||||
RString noteData = sParams[6];
|
||||
Trim( noteData );
|
||||
out.SetSMNoteData( noteData );
|
||||
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -717,6 +700,7 @@ bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache
|
||||
}
|
||||
|
||||
out.m_SongTiming.m_sFile = sPath;
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
for( unsigned i=0; i<msd.GetNumValues(); i++ )
|
||||
{
|
||||
@@ -924,6 +908,7 @@ bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache
|
||||
sParams[6],
|
||||
*pNewNotes );
|
||||
|
||||
pNewNotes->SetFilename(sPath);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
// XXX: Does anyone know what LEADTRACK is for? -Wolfman2000
|
||||
@@ -1110,7 +1095,10 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache )
|
||||
bg.push_back( BackgroundChange(lastBeat,song.m_sBackgroundFile) );
|
||||
} while(0);
|
||||
}
|
||||
song.TidyUpData( bFromCache );
|
||||
if (bFromCache)
|
||||
{
|
||||
song.TidyUpData( bFromCache, true );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ struct SMLoader
|
||||
* @brief Retrieve the relevant notedata from the simfile.
|
||||
* @param path the path where the simfile lives.
|
||||
* @param out the Steps we are loading the data into. */
|
||||
virtual bool LoadNotedataFromSimfile(const RString &path, Steps &out );
|
||||
virtual bool LoadNoteDataFromSimfile(const RString &path, Steps &out );
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the specified sm file.
|
||||
|
||||
@@ -164,6 +164,7 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
}
|
||||
|
||||
out.m_SongTiming.m_sFile = sPath; // songs still have their fallback timing.
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
int state = SMA_GETTING_SONG_INFO;
|
||||
Steps* pNewNotes = NULL;
|
||||
@@ -450,7 +451,7 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
sParams[5],
|
||||
sParams[6],
|
||||
*pNewNotes );
|
||||
|
||||
pNewNotes->SetFilename(sPath);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
else if( sValueName=="TIMESIGNATURES" || sValueName=="LEADTRACK" )
|
||||
@@ -462,7 +463,7 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
sValueName.c_str() );
|
||||
}
|
||||
TidyUpData(out, false);
|
||||
out.TidyUpData();
|
||||
out.TidyUpData(false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+122
-15
@@ -146,6 +146,95 @@ void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam )
|
||||
}
|
||||
}
|
||||
|
||||
bool SSCLoader::LoadNoteDataFromSimfile( const RString & cachePath, Steps &out )
|
||||
{
|
||||
LOG->Trace( "Loading notes from %s", cachePath.c_str() );
|
||||
|
||||
MsdFile msd;
|
||||
if (!msd.ReadFile(cachePath, true))
|
||||
{
|
||||
LOG->UserLog("Unable to load any notes from",
|
||||
cachePath,
|
||||
"for this reason: %s",
|
||||
msd.GetError().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tryingSteps = false;
|
||||
float storedVersion;
|
||||
const unsigned values = msd.GetNumValues();
|
||||
|
||||
for (unsigned i = 0; i < values; i++)
|
||||
{
|
||||
const MsdFile::value_t ¶ms = msd.GetValue(i);
|
||||
RString valueName = params[0];
|
||||
valueName.MakeUpper();
|
||||
RString matcher = params[1]; // mainly for debugging.
|
||||
Trim(matcher);
|
||||
|
||||
if (valueName=="VERSION")
|
||||
{
|
||||
storedVersion = StringToFloat(matcher);
|
||||
}
|
||||
if (tryingSteps)
|
||||
{
|
||||
if( valueName=="STEPSTYPE" )
|
||||
{
|
||||
if (out.m_StepsType != GAMEMAN->StringToStepsType(matcher))
|
||||
tryingSteps = false;
|
||||
}
|
||||
else if( valueName=="CHARTNAME")
|
||||
{
|
||||
if (storedVersion >= VERSION_CHART_NAME_TAG && out.GetChartName() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
else if( valueName=="DESCRIPTION" )
|
||||
{
|
||||
if (storedVersion < VERSION_CHART_NAME_TAG)
|
||||
{
|
||||
if (out.GetChartName() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
else if (out.GetDescription() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="DIFFICULTY" )
|
||||
{
|
||||
if (out.GetDifficulty() != StringToDifficulty(matcher))
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="METER" )
|
||||
{
|
||||
if (out.GetMeter() != StringToInt(matcher))
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="CREDIT" )
|
||||
{
|
||||
if (out.GetCredit() != matcher)
|
||||
tryingSteps = false;
|
||||
}
|
||||
|
||||
else if( valueName=="NOTES" || valueName=="NOTES2" )
|
||||
{
|
||||
out.SetSMNoteData(matcher);
|
||||
out.TidyUpData();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(valueName == "NOTEDATA")
|
||||
{
|
||||
tryingSteps = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache )
|
||||
{
|
||||
LOG->Trace( "Song::LoadFromSSCFile(%s)", sPath.c_str() );
|
||||
@@ -158,6 +247,7 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
}
|
||||
|
||||
out.m_SongTiming.m_sFile = sPath; // songs still have their fallback timing.
|
||||
out.m_sSongFileName = sPath;
|
||||
|
||||
int state = GETTING_SONG_INFO;
|
||||
const unsigned values = msd.GetNumValues();
|
||||
@@ -484,27 +574,35 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
|
||||
else if( sValueName=="RADARVALUES" )
|
||||
{
|
||||
vector<RString> saValues;
|
||||
split( sParams[1], ",", saValues, true );
|
||||
|
||||
int categories = NUM_RadarCategory;
|
||||
if( out.m_fVersion < VERSION_RADAR_FAKE )
|
||||
categories -= 1;
|
||||
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
if (bFromCache)
|
||||
{
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
vector<RString> saValues;
|
||||
split( sParams[1], ",", saValues, true );
|
||||
|
||||
int categories = NUM_RadarCategory;
|
||||
if( out.m_fVersion < VERSION_RADAR_FAKE )
|
||||
categories -= 1;
|
||||
|
||||
if( saValues.size() == (unsigned)categories * NUM_PLAYERS )
|
||||
{
|
||||
// Can't use the foreach anymore due to flexible radar lines.
|
||||
for( RadarCategory rc = (RadarCategory)0; rc < categories;
|
||||
enum_add<RadarCategory>( rc, +1 ) )
|
||||
RadarValues v[NUM_PLAYERS];
|
||||
FOREACH_PlayerNumber( pn )
|
||||
{
|
||||
v[pn][rc] = StringToFloat( saValues[pn*categories + rc] );
|
||||
// Can't use the foreach anymore due to flexible radar lines.
|
||||
for( RadarCategory rc = (RadarCategory)0; rc < categories;
|
||||
enum_add<RadarCategory>( rc, +1 ) )
|
||||
{
|
||||
v[pn][rc] = StringToFloat( saValues[pn*categories + rc] );
|
||||
}
|
||||
}
|
||||
pNewNotes->SetCachedRadarValues( v );
|
||||
}
|
||||
pNewNotes->SetCachedRadarValues( v );
|
||||
}
|
||||
else
|
||||
{
|
||||
// just recalc at time.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if( sValueName=="CREDIT" )
|
||||
@@ -519,6 +617,7 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
pNewNotes->m_Timing = stepsTiming;
|
||||
pNewNotes->SetSMNoteData( sParams[1] );
|
||||
pNewNotes->TidyUpData();
|
||||
pNewNotes->SetFilename(sPath);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
|
||||
@@ -605,6 +704,14 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach
|
||||
pNewNotes->SetMaxBPM(StringToFloat(sParams[2]));
|
||||
}
|
||||
}
|
||||
else if( sValueName=="STEPFILENAME" )
|
||||
{
|
||||
state = GETTING_SONG_INFO;
|
||||
if( bHasOwnTiming )
|
||||
pNewNotes->m_Timing = stepsTiming;
|
||||
pNewNotes->SetFilename(sParams[1]);
|
||||
out.AddSteps( pNewNotes );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ const float VERSION_SPLIT_TIMING = 0.7f;
|
||||
const float VERSION_OFFSET_BEFORE_ATTACK = 0.72f;
|
||||
/** @brief The version that introduced the Chart Name tag. */
|
||||
const float VERSION_CHART_NAME_TAG = 0.74f;
|
||||
/** @brief The version that introduced the cache switch tag. */
|
||||
const float VERSION_CACHE_SWITCH_TAG = 0.77f;
|
||||
|
||||
/**
|
||||
* @brief The SSCLoader handles all of the parsing needed for .ssc files.
|
||||
@@ -65,6 +67,12 @@ struct SSCLoader : public SMLoader
|
||||
*/
|
||||
bool LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePath, ProfileSlot slot, bool bAddStepsToSong );
|
||||
|
||||
/**
|
||||
* @brief Retrieve the specific NoteData from the file.
|
||||
* @param cachePath the path to the cache file.
|
||||
* @param out the Steps to receive just the particular notedata.
|
||||
* @return true if successful, false otherwise. */
|
||||
virtual bool LoadNoteDataFromSimfile( const RString &cachePath, Steps &out );
|
||||
|
||||
void ProcessWarps( TimingData &, const RString, const float );
|
||||
void ProcessLabels( TimingData &, const RString );
|
||||
|
||||
+1
-10
@@ -160,16 +160,7 @@ static void WriteGlobalTags( RageFile &f, Song &out )
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#ATTACKS:" );
|
||||
for( unsigned a=0; a < out.m_sAttackString.size(); a++ )
|
||||
{
|
||||
RString sData = out.m_sAttackString[a];
|
||||
f.Write( ssprintf( "%s", sData.c_str() ) );
|
||||
|
||||
if( a != (out.m_sAttackString.size() - 1) )
|
||||
f.Write( ":" ); // Not the end, so write a divider ':'
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
f.PutLine( ssprintf("#ATTACKS:%s;", out.GetAttackString().c_str()) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+40
-40
@@ -180,12 +180,29 @@ static void GetTimingTags( vector<RString> &lines, TimingData timing, bool bIsSo
|
||||
|
||||
static void WriteTimingTags( RageFile &f, const TimingData &timing, bool bIsSong = false )
|
||||
{
|
||||
|
||||
vector<RString> lines;
|
||||
|
||||
GetTimingTags( lines, timing, bIsSong );
|
||||
|
||||
f.PutLine( JoinLineList( lines ) );
|
||||
f.PutLine(ssprintf("#BPMS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_BPM)).c_str()));
|
||||
f.PutLine(ssprintf("#STOPS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_STOP_DELAY, false)).c_str()));
|
||||
f.PutLine(ssprintf("#DELAYS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_STOP_DELAY, true)).c_str()));
|
||||
f.PutLine(ssprintf("#WARPS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_WARP)).c_str()));
|
||||
f.PutLine(ssprintf("#TIMESIGNATURES:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_TIME_SIG)).c_str()));
|
||||
f.PutLine(ssprintf("#TICKCOUNTS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_TICKCOUNT)).c_str()));
|
||||
f.PutLine(ssprintf("#COMBOS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_COMBO)).c_str()));
|
||||
f.PutLine(ssprintf("#SPEEDS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_SPEED)).c_str()));
|
||||
f.PutLine(ssprintf("#SCROLLS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_SCROLL)).c_str()));
|
||||
if (!bIsSong)
|
||||
f.PutLine(ssprintf("#FAKES:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_FAKE)).c_str()));
|
||||
f.PutLine(ssprintf("#LABELS:%s;",
|
||||
join(",\r\n", timing.ToVectorString(SEGMENT_LABEL)).c_str()));
|
||||
|
||||
}
|
||||
|
||||
@@ -212,10 +229,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.PutLine( ssprintf( "#MUSIC:%s;", SmEscape(out.m_sMusicFile).c_str() ) );
|
||||
|
||||
{
|
||||
vector<RString> vs;
|
||||
FOREACH_ENUM( InstrumentTrack, it )
|
||||
if( out.HasInstrumentTrack(it) )
|
||||
vs.push_back( InstrumentTrackToString(it) + "=" + out.m_sInstrumentTrackFile[it] );
|
||||
vector<RString> vs = out.GetInstrumentTracksToVectorString();
|
||||
if( !vs.empty() )
|
||||
{
|
||||
RString s = join( ",", vs );
|
||||
@@ -229,7 +243,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
f.Write( "#SELECTABLE:" );
|
||||
switch(out.m_SelectionDisplay)
|
||||
{
|
||||
default: ASSERT(0); // fall through
|
||||
default: ASSERT_M(0, "An invalid selectable value was found for this song!"); // fall through
|
||||
case Song::SHOW_ALWAYS: f.Write( "YES" ); break;
|
||||
//case Song::SHOW_NONSTOP: f.Write( "NONSTOP" ); break;
|
||||
case Song::SHOW_NEVER: f.Write( "NO" ); break;
|
||||
@@ -297,16 +311,7 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
|
||||
f.Write( "#ATTACKS:" );
|
||||
for( unsigned a=0; a < out.m_sAttackString.size(); a++ )
|
||||
{
|
||||
RString sData = out.m_sAttackString[a];
|
||||
f.Write( ssprintf( "%s", sData.c_str() ) );
|
||||
|
||||
if( a != (out.m_sAttackString.size() - 1) )
|
||||
f.Write( ":" ); // Not the end, so write a divider ':'
|
||||
}
|
||||
f.PutLine( ";" );
|
||||
f.PutLine( ssprintf("#ATTACKS:%s;", out.GetAttackString().c_str()) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,17 +350,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
|
||||
GetTimingTags( lines, in.m_Timing );
|
||||
|
||||
RString attacks = "";
|
||||
for( unsigned a=0; a < in.m_sAttackString.size(); a++ )
|
||||
{
|
||||
RString sData = in.m_sAttackString[a];
|
||||
attacks += sData;
|
||||
|
||||
if( a != (in.m_sAttackString.size() - 1) )
|
||||
attacks += ":\r\n"; // Not the end, so write a divider ':'
|
||||
}
|
||||
Trim(attacks, ":"); // just in case something screwy happens.
|
||||
lines.push_back( ssprintf( "#ATTACKS:%s;", attacks.c_str()));
|
||||
lines.push_back( ssprintf("#ATTACKS:%s;", in.GetAttackString().c_str()));
|
||||
|
||||
switch( in.GetDisplayBPM() )
|
||||
{
|
||||
@@ -376,16 +371,21 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
|
||||
lines.push_back( ssprintf( "#DISPLAYBPM:*;" ) );
|
||||
break;
|
||||
}
|
||||
|
||||
RString sNoteData;
|
||||
in.GetSMNoteData( sNoteData );
|
||||
if (bSavingCache)
|
||||
{
|
||||
lines.push_back(ssprintf("#STEPFILENAME:%s;", in.GetFilename().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
RString sNoteData;
|
||||
in.GetSMNoteData( sNoteData );
|
||||
|
||||
lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
|
||||
|
||||
TrimLeft(sNoteData);
|
||||
split( sNoteData, "\n", lines, true );
|
||||
lines.push_back( ";" );
|
||||
lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
|
||||
|
||||
TrimLeft(sNoteData);
|
||||
split( sNoteData, "\n", lines, true );
|
||||
lines.push_back( ";" );
|
||||
}
|
||||
return JoinLineList( lines );
|
||||
}
|
||||
|
||||
|
||||
@@ -393,9 +393,33 @@ void ScreenGameplay::Init()
|
||||
/* Called once per stage (single song or single course). */
|
||||
GAMESTATE->BeginStage();
|
||||
|
||||
int player = 1;
|
||||
FOREACH_EnabledPlayerInfo( m_vPlayerInfo, pi )
|
||||
{
|
||||
unsigned int count = pi->m_vpStepsQueue.size();
|
||||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
Steps *curSteps = pi->m_vpStepsQueue[i];
|
||||
if (curSteps->IsNoteDataEmpty())
|
||||
{
|
||||
if (curSteps->GetNoteDataFromSimfile())
|
||||
{
|
||||
LOG->Trace("Notes should be loaded for player %d", player);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->Trace("Error loading notes for player %d", player);
|
||||
}
|
||||
}
|
||||
}
|
||||
player++;
|
||||
}
|
||||
|
||||
if(!GAMESTATE->IsCourseMode() && !GAMESTATE->m_bDemonstrationOrJukebox)
|
||||
{
|
||||
// fill in difficulty of CPU players with that of the first human player
|
||||
// this should not need to worry about step content.
|
||||
FOREACH_PotentialCpuPlayer(p)
|
||||
GAMESTATE->m_pCurSteps[p].Set( GAMESTATE->m_pCurSteps[ GAMESTATE->GetFirstHumanPlayer() ] );
|
||||
|
||||
|
||||
+4
-2
@@ -41,7 +41,7 @@
|
||||
* @brief The internal version of the cache for StepMania.
|
||||
*
|
||||
* Increment this value to invalidate the current cache. */
|
||||
const int FILE_CACHE_VERSION = 191;
|
||||
const int FILE_CACHE_VERSION = 192;
|
||||
|
||||
/** @brief How long does a song sample last by default? */
|
||||
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
|
||||
@@ -860,7 +860,9 @@ void Song::ReCalculateRadarValuesAndLastSecond(bool fromCache, bool duringCache)
|
||||
wipe_notedata:
|
||||
if (duringCache)
|
||||
{
|
||||
// update this when at that point.
|
||||
NoteData dummy;
|
||||
dummy.SetNumTracks(tempNoteData.GetNumTracks());
|
||||
pSteps->SetNoteData(dummy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -20,7 +20,7 @@ void FixupPath( RString &path, const RString &sSongPath );
|
||||
RString GetSongAssetPath( RString sPath, const RString &sSongPath );
|
||||
|
||||
/** @brief The version of the .ssc file format. */
|
||||
const static float STEPFILE_VERSION_NUMBER = 0.76f;
|
||||
const static float STEPFILE_VERSION_NUMBER = 0.77f;
|
||||
|
||||
/** @brief How many edits for this song can each profile have? */
|
||||
const int MAX_EDITS_PER_SONG_PER_PROFILE = 15;
|
||||
@@ -100,7 +100,9 @@ public:
|
||||
/**
|
||||
* @brief Get the new radar values, and determine the last second at the same time.
|
||||
*
|
||||
* This is called by TidyUpData, after saving the Song. */
|
||||
* This is called by TidyUpData, after saving the Song.
|
||||
* @param fromCache was this data loaded from the cache file?
|
||||
* @param duringCache was this data loaded during the cache process? */
|
||||
void ReCalculateRadarValuesAndLastSecond(bool fromCache = false, bool duringCache = false);
|
||||
/**
|
||||
* @brief Translate any titles that aren't in english.
|
||||
|
||||
+43
-29
@@ -88,6 +88,45 @@ bool Steps::IsNoteDataEmpty() const
|
||||
return this->m_sNoteDataCompressed.empty();
|
||||
}
|
||||
|
||||
bool Steps::GetNoteDataFromSimfile()
|
||||
{
|
||||
// Replace the line below with the Steps' cache file.
|
||||
RString stepFile = this->GetFilename();
|
||||
RString extension = GetExtension(stepFile);
|
||||
if (extension.empty() || extension == "ssc") // remember cache files.
|
||||
{
|
||||
SSCLoader loader;
|
||||
return loader.LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
else if (extension == "sm")
|
||||
{
|
||||
SMLoader loader;
|
||||
return loader.LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
else if (extension == "sma")
|
||||
{
|
||||
SMALoader loader;
|
||||
return loader.LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
else if (extension == "dwi")
|
||||
{
|
||||
return DWILoader::LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
else if (extension == "ksf")
|
||||
{
|
||||
return KSFLoader::LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
else if (extension == "bms" || extension == "bml" || extension == "bme")
|
||||
{
|
||||
return BMSLoader::LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
else if (extension == "pms")
|
||||
{
|
||||
return PMSLoader::LoadNoteDataFromSimfile(stepFile, *this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Steps::SetNoteData( const NoteData& noteDataNew )
|
||||
{
|
||||
ASSERT( noteDataNew.GetNumTracks() == GAMEMAN->GetStepsTypeInfo(m_StepsType).iNumTracks );
|
||||
@@ -130,7 +169,6 @@ void Steps::SetSMNoteData( const RString ¬es_comp_ )
|
||||
|
||||
m_sNoteDataCompressed = notes_comp_;
|
||||
m_iHash = 0;
|
||||
m_sFilename = RString(); // We can no longer read from the file because it has changed in memory.
|
||||
}
|
||||
|
||||
/* XXX: this function should pull data from m_sFilename, like Decompress() */
|
||||
@@ -293,38 +331,14 @@ void Steps::Decompress()
|
||||
|
||||
if( !m_sFilename.empty() && m_sNoteDataCompressed.empty() )
|
||||
{
|
||||
// We have data on disk and not in memory. Load it.
|
||||
Song s;
|
||||
SSCLoader loaderSSC;
|
||||
bool bLoadedFromSSC = loaderSSC.LoadFromSimfile(m_sFilename, s, true);
|
||||
if( !bLoadedFromSSC )
|
||||
// We have NoteData on disk and not in memory. Load it.
|
||||
if (!this->GetNoteDataFromSimfile())
|
||||
{
|
||||
// try reading from .sm instead
|
||||
SMLoader loaderSM;
|
||||
if( !loaderSM.LoadFromSimfile(m_sFilename, s, true) )
|
||||
{
|
||||
LOG->Warn( "Couldn't load \"%s\"", m_sFilename.c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the steps. */
|
||||
StepsID ID;
|
||||
ID.FromSteps( this );
|
||||
|
||||
/* We're using a StepsID to search in a different copy of a Song than
|
||||
* the one it was created with. Clear the cache before doing this,
|
||||
* or search results will come from cache and point to the original
|
||||
* copy. */
|
||||
CachedObject<Steps>::ClearCacheAll();
|
||||
Steps *pSteps = ID.ToSteps( &s, true );
|
||||
if( pSteps == NULL )
|
||||
{
|
||||
LOG->Warn( "Couldn't find %s in \"%s\"", ID.ToString().c_str(), m_sFilename.c_str() );
|
||||
LOG->Warn("Couldn't load \"%s\"", m_sFilename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
pSteps->GetSMNoteData( m_sNoteDataCompressed );
|
||||
this->GetSMNoteData( m_sNoteDataCompressed );
|
||||
}
|
||||
|
||||
if( m_sNoteDataCompressed.empty() )
|
||||
|
||||
+29
-119
@@ -1299,6 +1299,24 @@ void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, i
|
||||
return;
|
||||
}
|
||||
|
||||
vector<RString> TimingData::ToVectorString(TimingSegmentType tst,
|
||||
bool isDelay, int dec) const
|
||||
{
|
||||
const vector<TimingSegment *> segs = this->allTimingSegments[tst];
|
||||
vector<RString> ret;
|
||||
|
||||
for (unsigned i = 0; i < segs.size(); i++)
|
||||
{
|
||||
if (tst == SEGMENT_STOP_DELAY)
|
||||
{
|
||||
StopSegment *seg = static_cast<StopSegment *>(segs[i]);
|
||||
if (seg->GetDelay() == isDelay)
|
||||
continue;
|
||||
}
|
||||
ret.push_back(segs[i]->ToString(dec));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// lua start
|
||||
#include "LuaBinding.h"
|
||||
@@ -1315,136 +1333,47 @@ public:
|
||||
static int HasScrollChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasScrollChanges()); return 1; }
|
||||
static int GetWarps( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vWarps;
|
||||
vector<TimingSegment *> &warps = p->allTimingSegments[SEGMENT_WARP];
|
||||
for (unsigned i = 0; i < warps.size(); i++)
|
||||
{
|
||||
WarpSegment *seg = static_cast<WarpSegment *>(warps[i]);
|
||||
const float length = seg->GetLength();
|
||||
const float beat = seg->GetBeat();
|
||||
vWarps.push_back( ssprintf("%f=%f", beat, length) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vWarps, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_WARP), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetFakes( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vFakes;
|
||||
vector<TimingSegment *> &fakes = p->allTimingSegments[SEGMENT_FAKE];
|
||||
for (unsigned i = 0; i < fakes.size(); i++)
|
||||
{
|
||||
FakeSegment *seg = static_cast<FakeSegment *>(fakes[i]);
|
||||
const float length = seg->GetLength();
|
||||
const float beat = seg->GetBeat();
|
||||
vFakes.push_back( ssprintf("%f=%f", beat, length) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vFakes, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_FAKE), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetScrolls( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vScrolls;
|
||||
vector<TimingSegment *> &scrolls = p->allTimingSegments[SEGMENT_SCROLL];
|
||||
for (unsigned i = 0; i < scrolls.size(); i++)
|
||||
{
|
||||
ScrollSegment *seg = static_cast<ScrollSegment *>(scrolls[i]);
|
||||
const float ratio = seg->GetRatio();
|
||||
const float beat = seg->GetBeat();
|
||||
vScrolls.push_back( ssprintf("%f=%f", beat, ratio) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vScrolls, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_SCROLL), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetSpeeds( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vSpeeds;
|
||||
vector<TimingSegment *> &speeds = p->allTimingSegments[SEGMENT_SPEED];
|
||||
for (unsigned i = 0; i < speeds.size(); i++)
|
||||
{
|
||||
SpeedSegment *seg = static_cast<SpeedSegment *>(speeds[i]);
|
||||
const float length = seg->GetLength();
|
||||
const float ratio = seg->GetRatio();
|
||||
const unsigned short unit = seg->GetUnit();
|
||||
const float beat = seg->GetBeat();
|
||||
vSpeeds.push_back( ssprintf("%f=%f=%f=%uh", beat, ratio, length, unit) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vSpeeds, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_SPEED), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetTimeSignatures( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vTimes;
|
||||
vector<TimingSegment *> &tSigs = p->allTimingSegments[SEGMENT_TIME_SIG];
|
||||
for (unsigned i = 0; i < tSigs.size(); i++)
|
||||
{
|
||||
TimeSignatureSegment *seg = static_cast<TimeSignatureSegment *>(tSigs[i]);
|
||||
const int numerator = seg->GetNum();
|
||||
const int denominator = seg->GetDen();
|
||||
const float beat = seg->GetBeat();
|
||||
vTimes.push_back( ssprintf("%f=%d=%d", beat, numerator, denominator) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vTimes, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_TIME_SIG), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetCombos( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vCombos;
|
||||
vector<TimingSegment *> &combos = p->allTimingSegments[SEGMENT_COMBO];
|
||||
for (unsigned i = 0; i < combos.size(); i++)
|
||||
{
|
||||
ComboSegment *seg = static_cast<ComboSegment *>(combos[i]);
|
||||
const int combo = seg->GetCombo();
|
||||
const int miss = seg->GetMissCombo();
|
||||
const float beat = seg->GetBeat();
|
||||
vCombos.push_back( ssprintf("%f=%d=%d", beat, combo, miss) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vCombos, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_COMBO), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetTickcounts( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vTicks;
|
||||
vector<TimingSegment *> &ticks = p->allTimingSegments[SEGMENT_TICKCOUNT];
|
||||
for (unsigned i = 0; i < ticks.size(); i++)
|
||||
{
|
||||
TickcountSegment *seg = static_cast<TickcountSegment *>(ticks[i]);
|
||||
const int tick = seg->GetTicks();
|
||||
const float beat = seg->GetBeat();
|
||||
vTicks.push_back( ssprintf("%f=%d", beat, tick) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vTicks, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_TICKCOUNT), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetStops( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vStops;
|
||||
vector<TimingSegment *> &stops = p->allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
for (unsigned i = 0; i < stops.size(); i++)
|
||||
{
|
||||
StopSegment *seg = static_cast<StopSegment *>(stops[i]);
|
||||
const float fStartBeat = seg->GetBeat();
|
||||
const float fStopLength = seg->GetPause();
|
||||
if(!seg->GetDelay())
|
||||
vStops.push_back( ssprintf("%f=%f", fStartBeat, fStopLength) );
|
||||
}
|
||||
|
||||
LuaHelpers::CreateTableFromArray(vStops, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_STOP_DELAY, false), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetDelays( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vDelays;
|
||||
vector<TimingSegment *> &stops = p->allTimingSegments[SEGMENT_STOP_DELAY];
|
||||
for (unsigned i = 0; i < stops.size(); i++)
|
||||
{
|
||||
StopSegment *seg = static_cast<StopSegment *>(stops[i]);
|
||||
const float fStartBeat = seg->GetBeat();
|
||||
const float fStopLength = seg->GetPause();
|
||||
if(seg->GetDelay())
|
||||
vDelays.push_back( ssprintf("%f=%f", fStartBeat, fStopLength) );
|
||||
}
|
||||
|
||||
LuaHelpers::CreateTableFromArray(vDelays, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_STOP_DELAY, true), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetBPMs( T* p, lua_State *L )
|
||||
@@ -1463,31 +1392,12 @@ public:
|
||||
}
|
||||
static int GetLabels( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vLabels;
|
||||
vector<TimingSegment *> &labels = p->allTimingSegments[SEGMENT_LABEL];
|
||||
for (unsigned i = 0; i < labels.size(); i++)
|
||||
{
|
||||
LabelSegment *seg = static_cast<LabelSegment *>(labels[i]);
|
||||
const float fStartRow = seg->GetBeat();
|
||||
const RString sLabel = seg->GetLabel();
|
||||
vLabels.push_back( ssprintf("%f=%s", fStartRow, sLabel.c_str()) );
|
||||
}
|
||||
LuaHelpers::CreateTableFromArray(vLabels, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_LABEL), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetBPMsAndTimes( T* p, lua_State *L )
|
||||
{
|
||||
vector<RString> vBPMs;
|
||||
vector<TimingSegment *> &bpms = p->allTimingSegments[SEGMENT_BPM];
|
||||
for (unsigned i = 0; i < bpms.size(); i++)
|
||||
{
|
||||
BPMSegment *seg = static_cast<BPMSegment *>(bpms[i]);
|
||||
const float fStartRow = seg->GetBeat();
|
||||
const float fBPM = seg->GetBPM();
|
||||
vBPMs.push_back( ssprintf("%f=%f", fStartRow, fBPM) );
|
||||
}
|
||||
|
||||
LuaHelpers::CreateTableFromArray(vBPMs, L);
|
||||
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_BPM), L);
|
||||
return 1;
|
||||
}
|
||||
static int GetActualBPM( T* p, lua_State *L )
|
||||
|
||||
@@ -825,6 +825,8 @@ public:
|
||||
* @brief The initial offset of a song.
|
||||
*/
|
||||
float m_fBeat0OffsetInSeconds;
|
||||
|
||||
vector<RString> ToVectorString(TimingSegmentType tst, bool isDelay = false, int dec = 6) const;
|
||||
};
|
||||
|
||||
#undef COMPARE
|
||||
|
||||
@@ -81,6 +81,11 @@ struct TimingSegment
|
||||
{
|
||||
return TimingSegmentType_Invalid;
|
||||
}
|
||||
// TODO: Remove isDelay optional param and split Stops and Delays.
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
return FloatToString(this->GetBeat());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two DrivedSegments to see if one is less than the other.
|
||||
@@ -206,6 +211,13 @@ struct FakeSegment : public TimingSegment
|
||||
|
||||
TimingSegmentType GetType() const { return SEGMENT_FAKE; }
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
RString str = "%.0" + IntToString(dec)
|
||||
+ "f=%.0" + IntToString(dec) + "f";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetLength());
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief The number of beats the FakeSegment is alive for.
|
||||
@@ -260,6 +272,13 @@ struct WarpSegment : public TimingSegment
|
||||
|
||||
void Scale( int start, int length, int newLength );
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
RString str = "%.0" + IntToString(dec)
|
||||
+ "f=%.0" + IntToString(dec) + "f";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetLength());
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Compares two WarpSegments to see if one is less than the other.
|
||||
* @param other the other WarpSegment to compare to.
|
||||
@@ -326,6 +345,12 @@ struct TickcountSegment : public TimingSegment
|
||||
* @param i the tickcount. */
|
||||
void SetTicks(const int i);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec) + "f=%i";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetTicks());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two TickcountSegments to see if one is less than the other.
|
||||
* @param other the other TickcountSegment to compare to.
|
||||
@@ -404,6 +429,17 @@ struct ComboSegment : public TimingSegment
|
||||
* @param i the miss combo. */
|
||||
void SetMissCombo(const int i);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
RString str = "%.0" + IntToString(dec) + "f=%i";
|
||||
if (this->GetCombo() == this->GetMissCombo())
|
||||
{
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetCombo());
|
||||
}
|
||||
str += "=%i";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetCombo(), this->GetMissCombo());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two ComboSegments to see if one is less than the other.
|
||||
* @param other the other ComboSegment to compare to.
|
||||
@@ -462,6 +498,12 @@ struct LabelSegment : public TimingSegment
|
||||
* @param l the label. */
|
||||
void SetLabel(const RString l);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec) + "f=%s";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetLabel().c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two LabelSegments to see if one is less than the other.
|
||||
* @param other the other LabelSegment to compare to.
|
||||
@@ -526,6 +568,13 @@ struct BPMSegment : public TimingSegment
|
||||
* @param l the label. */
|
||||
void SetBPS(const float newBPS);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec)
|
||||
+ "f=%.0" + IntToString(dec) + "f";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetBPM());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two LabelSegments to see if one is less than the other.
|
||||
* @param other the other LabelSegment to compare to.
|
||||
@@ -604,6 +653,12 @@ struct TimeSignatureSegment : public TimingSegment
|
||||
* @param i the denominator. */
|
||||
void SetDen(const int i);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec) + "f=%i=%i";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetNum(), this->GetDen());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of note rows per measure within the TimeSignatureSegment.
|
||||
*
|
||||
@@ -727,6 +782,15 @@ struct SpeedSegment : public TimingSegment
|
||||
void SetUnit(const int i);
|
||||
|
||||
void Scale( int start, int length, int newLength );
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec)
|
||||
+ "f=%.0" + IntToString(dec) + "f=%.0"
|
||||
+ IntToString(dec) + "f=%uh";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetRatio(),
|
||||
this->GetLength(), this->GetUnit());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two SpeedSegments to see if one is less than the other.
|
||||
@@ -792,6 +856,13 @@ struct ScrollSegment : public TimingSegment
|
||||
* @param i the ratio. */
|
||||
void SetRatio(const float i);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec)
|
||||
+ "f=%.0" + IntToString(dec) + "f";
|
||||
return ssprintf(str.c_str(), this->GetBeat(), this->GetRatio());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two ScrollSegment to see if one is less than the other.
|
||||
* @param other the other ScrollSegment to compare to.
|
||||
@@ -867,6 +938,13 @@ struct StopSegment : public TimingSegment
|
||||
* @brief Set the behavior in this StopSegment.
|
||||
* @param i the behavior. */
|
||||
void SetDelay(const bool i);
|
||||
|
||||
virtual RString ToString(int dec) const
|
||||
{
|
||||
const RString str = "%.0" + IntToString(dec)
|
||||
+ "f=%.0" + IntToString(dec) + "f";
|
||||
return ssprintf(str.c_str(), this->GetPause());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two StopSegments to see if one is less than the other.
|
||||
|
||||
Reference in New Issue
Block a user