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:
Jason Felds
2011-07-17 16:06:40 -04:00
parent bd9e7fa3c4
commit 48226f2027
26 changed files with 900 additions and 536 deletions
+4
View File
@@ -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
+7
View File
@@ -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.
+7 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 &params = 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;
}
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+3 -2
View 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
View File
@@ -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 &params = 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;
}
}
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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 );
}
+24
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 &notes_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
View File
@@ -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 )
+2
View File
@@ -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
+78
View File
@@ -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.