2011-03-17 01:47:30 -04:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "NotesLoaderSSC.h"
|
|
|
|
|
#include "BackgroundUtil.h"
|
|
|
|
|
#include "GameManager.h"
|
|
|
|
|
#include "MsdFile.h" // No JSON here.
|
|
|
|
|
#include "NoteTypes.h"
|
2011-05-09 23:16:56 -04:00
|
|
|
#include "NotesLoaderSM.h" // For programming shortcuts.
|
2011-03-17 01:47:30 -04:00
|
|
|
#include "RageFileManager.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "Song.h"
|
|
|
|
|
#include "SongManager.h"
|
|
|
|
|
#include "Steps.h"
|
2011-04-17 23:03:39 -04:00
|
|
|
#include "Attack.h"
|
2011-03-17 01:47:30 -04:00
|
|
|
#include "PrefsManager.h"
|
|
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
// Everything from this line to the creation of parser_helper exists to
|
|
|
|
|
// speed up parsing by allowing the use of std::map. All these functions
|
|
|
|
|
// are put into a map of function pointers which is used when loading.
|
|
|
|
|
// -Kyz
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
struct StepsTagInfo
|
|
|
|
|
{
|
|
|
|
|
SSCLoader* loader;
|
|
|
|
|
Song* song;
|
|
|
|
|
Steps* steps;
|
|
|
|
|
TimingData* timing;
|
|
|
|
|
const MsdFile::value_t* params;
|
|
|
|
|
const RString& path;
|
|
|
|
|
bool has_own_timing;
|
|
|
|
|
bool ssc_format;
|
|
|
|
|
bool from_cache;
|
|
|
|
|
bool for_load_edit;
|
|
|
|
|
StepsTagInfo(SSCLoader* l, Song* s, const RString& p, bool fc)
|
|
|
|
|
:loader(l), song(s), path(p), has_own_timing(false), ssc_format(false),
|
|
|
|
|
from_cache(fc), for_load_edit(false)
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
struct SongTagInfo
|
|
|
|
|
{
|
|
|
|
|
SSCLoader* loader;
|
|
|
|
|
Song* song;
|
|
|
|
|
const MsdFile::value_t* params;
|
|
|
|
|
const RString& path;
|
|
|
|
|
bool from_cache;
|
|
|
|
|
SongTagInfo(SSCLoader* l, Song* s, const RString& p, bool fc)
|
|
|
|
|
:loader(l), song(s), path(p), from_cache(fc)
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
// LoadNoteDataFromSimfile uses LoadNoteDataTagIDs because its parts operate
|
|
|
|
|
// on state variables internal to the function.
|
|
|
|
|
enum LoadNoteDataTagIDs
|
|
|
|
|
{
|
|
|
|
|
LNDID_version,
|
|
|
|
|
LNDID_stepstype,
|
|
|
|
|
LNDID_chartname,
|
|
|
|
|
LNDID_description,
|
|
|
|
|
LNDID_difficulty,
|
|
|
|
|
LNDID_meter,
|
|
|
|
|
LNDID_credit,
|
|
|
|
|
LNDID_notes,
|
|
|
|
|
LNDID_notes2,
|
|
|
|
|
LNDID_notedata
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef void (*steps_tag_func_t)(StepsTagInfo& info);
|
|
|
|
|
typedef void (*song_tag_func_t)(SongTagInfo& info);
|
|
|
|
|
|
|
|
|
|
// Functions for song tags go below this line. -Kyz
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
void SetVersion(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_fVersion = StringToFloat((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetTitle(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sMainTitle = (*info.params)[1];
|
|
|
|
|
info.loader->SetSongTitle((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSubtitle(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sSubTitle = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetArtist(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sArtist = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetMainTitleTranslit(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sMainTitleTranslit = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetSubtitleTranslit(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sSubTitleTranslit = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetArtistTranslit(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sArtistTranslit = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetGenre(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sGenre = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetOrigin(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sOrigin = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetCredit(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sCredit = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetBanner(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sBannerFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetBackground(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sBackgroundFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetPreviewVid(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sPreviewVidFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetJacket(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sJacketFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetCDImage(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sCDFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetDiscImage(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sDiscFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetLyricsPath(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sLyricsFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetCDTitle(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sCDTitleFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetMusic(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_sMusicFile = (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetPreview(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_PreviewFile= (*info.params)[1];
|
|
|
|
|
}
|
|
|
|
|
void SetInstrumentTrack(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessInstrumentTracks(*info.song, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetMusicLength(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache)
|
|
|
|
|
info.song->m_fMusicLengthSeconds = StringToFloat((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetLastSecondHint(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->SetSpecifiedLastSecond(StringToFloat((*info.params)[1]));
|
|
|
|
|
}
|
|
|
|
|
void SetSampleStart(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_fMusicSampleStartSeconds = HHMMSSToSeconds((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSampleLength(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_fMusicSampleLengthSeconds = HHMMSSToSeconds((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetDisplayBPM(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
// #DISPLAYBPM:[xxx][xxx:xxx]|[*];
|
|
|
|
|
if((*info.params)[1] == "*")
|
|
|
|
|
{ info.song->m_DisplayBPMType = DISPLAY_BPM_RANDOM; }
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info.song->m_DisplayBPMType = DISPLAY_BPM_SPECIFIED;
|
|
|
|
|
info.song->m_fSpecifiedBPMMin = StringToFloat((*info.params)[1]);
|
|
|
|
|
if((*info.params)[2].empty())
|
|
|
|
|
{ info.song->m_fSpecifiedBPMMax = info.song->m_fSpecifiedBPMMin; }
|
|
|
|
|
else
|
|
|
|
|
{ info.song->m_fSpecifiedBPMMax = StringToFloat((*info.params)[2]); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void SetSelectable(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if((*info.params)[1].EqualsNoCase("YES"))
|
|
|
|
|
{ info.song->m_SelectionDisplay = info.song->SHOW_ALWAYS; }
|
|
|
|
|
else if((*info.params)[1].EqualsNoCase("NO"))
|
|
|
|
|
{ info.song->m_SelectionDisplay = info.song->SHOW_NEVER; }
|
|
|
|
|
// ROULETTE from 3.9 is no longer in use.
|
|
|
|
|
else if((*info.params)[1].EqualsNoCase("ROULETTE"))
|
|
|
|
|
{ info.song->m_SelectionDisplay = info.song->SHOW_ALWAYS; }
|
|
|
|
|
/* The following two cases are just fixes to make sure simfiles that
|
|
|
|
|
* used 3.9+ features are not excluded here */
|
|
|
|
|
else if((*info.params)[1].EqualsNoCase("ES") || (*info.params)[1].EqualsNoCase("OMES"))
|
|
|
|
|
{ info.song->m_SelectionDisplay = info.song->SHOW_ALWAYS; }
|
|
|
|
|
else if(StringToInt((*info.params)[1]) > 0)
|
|
|
|
|
{ info.song->m_SelectionDisplay = info.song->SHOW_ALWAYS; }
|
|
|
|
|
else
|
|
|
|
|
{ LOG->UserLog("Song file", info.path, "has an unknown #SELECTABLE value, \"%s\"; ignored.", (*info.params)[1].c_str()); }
|
|
|
|
|
}
|
|
|
|
|
void SetBGChanges(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessBGChanges(*info.song, (*info.params)[0], info.path, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetFGChanges(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
vector<RString> aFGChangeExpressions;
|
|
|
|
|
split((*info.params)[1], ",", aFGChangeExpressions);
|
|
|
|
|
|
|
|
|
|
for(size_t b = 0; b < aFGChangeExpressions.size(); ++b)
|
|
|
|
|
{
|
|
|
|
|
BackgroundChange change;
|
|
|
|
|
if(info.loader->LoadFromBGChangesString(change, aFGChangeExpressions[b]))
|
|
|
|
|
{ info.song->AddForegroundChange(change); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void SetKeysounds(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
RString keysounds = (*info.params)[1];
|
|
|
|
|
if(keysounds.length() >= 2 && keysounds.substr(0, 2) == "\\#")
|
|
|
|
|
{ keysounds = keysounds.substr(1); }
|
|
|
|
|
split(keysounds, ",", info.song->m_vsKeysoundFile);
|
|
|
|
|
}
|
|
|
|
|
void SetAttacks(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessAttackString(info.song->m_sAttackString, (*info.params));
|
|
|
|
|
info.loader->ProcessAttacks(info.song->m_Attacks, (*info.params));
|
|
|
|
|
}
|
|
|
|
|
void SetOffset(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_SongTiming.m_fBeat0OffsetInSeconds = StringToFloat((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongStops(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessStops(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongDelays(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessDelays(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongBPMs(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessBPMs(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongWarps(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessWarps( info.song->m_SongTiming, (*info.params)[1], info.song->m_fVersion );
|
|
|
|
|
}
|
|
|
|
|
void SetSongLabels(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessLabels( info.song->m_SongTiming, (*info.params)[1] );
|
|
|
|
|
}
|
|
|
|
|
void SetSongTimeSignatures(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessTimeSignatures(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongTickCounts(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessTickcounts(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongCombos(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessCombos( info.song->m_SongTiming, (*info.params)[1] );
|
|
|
|
|
}
|
|
|
|
|
void SetSongSpeeds(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessSpeeds(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongScrolls(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessScrolls(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetSongFakes(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessFakes(info.song->m_SongTiming, (*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetFirstSecond(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache)
|
|
|
|
|
{ info.song->SetFirstSecond(StringToFloat((*info.params)[1])); }
|
|
|
|
|
}
|
|
|
|
|
void SetLastSecond(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache)
|
|
|
|
|
{ info.song->SetLastSecond(StringToFloat((*info.params)[1])); }
|
|
|
|
|
}
|
|
|
|
|
void SetSongFilename(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache)
|
|
|
|
|
{ info.song->m_sSongFileName = (*info.params)[1]; }
|
|
|
|
|
}
|
|
|
|
|
void SetHasMusic(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache)
|
|
|
|
|
{ info.song->m_bHasMusic = StringToInt((*info.params)[1]) != 0; }
|
|
|
|
|
}
|
|
|
|
|
void SetHasBanner(SongTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache)
|
|
|
|
|
{ info.song->m_bHasBanner = StringToInt((*info.params)[1]) != 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Functions for steps tags go below this line. -Kyz
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
void SetStepsVersion(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.song->m_fVersion = StringToFloat((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetChartName(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetChartName((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
void SetStepsType(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.steps->m_StepsType = GAMEMAN->StringToStepsType((*info.params)[1]);
|
|
|
|
|
info.steps->m_StepsTypeStr= (*info.params)[1];
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetChartStyle(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetChartStyle((*info.params)[1]);
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetDescription(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion < VERSION_CHART_NAME_TAG && !info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetChartName((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetDescription((*info.params)[1]);
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetDifficulty(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetDifficulty(StringToDifficulty((*info.params)[1]));
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetMeter(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetMeter(StringToInt((*info.params)[1]));
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetRadarValues(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.from_cache || info.for_load_edit)
|
|
|
|
|
{
|
2015-04-06 18:45:23 -06:00
|
|
|
vector<RString> values;
|
|
|
|
|
split((*info.params)[1], ",", values, true);
|
|
|
|
|
// Instead of trying to use the version to figure out how many
|
|
|
|
|
// categories to expect, look at the number of values and split them
|
|
|
|
|
// evenly. -Kyz
|
|
|
|
|
size_t cats_per_player= values.size() / NUM_PlayerNumber;
|
|
|
|
|
RadarValues v[NUM_PLAYERS];
|
|
|
|
|
FOREACH_PlayerNumber(pn)
|
2015-03-09 20:42:33 -06:00
|
|
|
{
|
2015-04-06 18:45:23 -06:00
|
|
|
for(size_t i= 0; i < cats_per_player; ++i)
|
2015-03-09 20:42:33 -06:00
|
|
|
{
|
2015-04-06 18:45:23 -06:00
|
|
|
v[pn][i]= StringToFloat(values[pn * cats_per_player + i]);
|
2015-03-09 20:42:33 -06:00
|
|
|
}
|
|
|
|
|
}
|
2015-04-06 18:45:23 -06:00
|
|
|
info.steps->SetCachedRadarValues(v);
|
2015-03-09 20:42:33 -06:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// just recalc at time.
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetCredit(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetCredit((*info.params)[1]);
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsMusic(StepsTagInfo& info)
|
|
|
|
|
{
|
2015-03-09 20:45:28 -06:00
|
|
|
info.steps->SetMusicFile((*info.params)[1]);
|
2015-03-09 20:42:33 -06:00
|
|
|
}
|
|
|
|
|
void SetStepsBPMs(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessBPMs(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsStops(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessStops(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsDelays(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessDelays(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsTimeSignatures(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessTimeSignatures(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsTickCounts(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessTickcounts(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsCombos(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessCombos(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsWarps(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessWarps(*info.timing, (*info.params)[1], info.song->m_fVersion);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsSpeeds(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessSpeeds(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsScrolls(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessScrolls(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsFakes(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessFakes(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsLabels(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessLabels(*info.timing, (*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
info.ssc_format= true;
|
|
|
|
|
}
|
|
|
|
|
void SetStepsAttacks(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.loader->ProcessAttackString(info.steps->m_sAttackString, *info.params);
|
|
|
|
|
info.loader->ProcessAttacks(info.steps->m_Attacks, *info.params);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void SetStepsOffset(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
if(info.song->m_fVersion >= VERSION_SPLIT_TIMING || info.for_load_edit)
|
|
|
|
|
{
|
|
|
|
|
info.timing->m_fBeat0OffsetInSeconds = StringToFloat((*info.params)[1]);
|
|
|
|
|
info.has_own_timing = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void SetStepsDisplayBPM(StepsTagInfo& info)
|
|
|
|
|
{
|
|
|
|
|
// #DISPLAYBPM:[xxx][xxx:xxx]|[*];
|
|
|
|
|
if((*info.params)[1] == "*")
|
|
|
|
|
{ info.steps->SetDisplayBPM(DISPLAY_BPM_RANDOM); }
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info.steps->SetDisplayBPM(DISPLAY_BPM_SPECIFIED);
|
|
|
|
|
float min = StringToFloat((*info.params)[1]);
|
|
|
|
|
info.steps->SetMinBPM(min);
|
|
|
|
|
if((*info.params)[2].empty())
|
|
|
|
|
{ info.steps->SetMaxBPM(min); }
|
|
|
|
|
else
|
|
|
|
|
{ info.steps->SetMaxBPM(StringToFloat((*info.params)[2])); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef std::map<RString, steps_tag_func_t> steps_handler_map_t;
|
|
|
|
|
typedef std::map<RString, song_tag_func_t> song_handler_map_t;
|
|
|
|
|
typedef std::map<RString, LoadNoteDataTagIDs> load_note_data_handler_map_t;
|
|
|
|
|
|
|
|
|
|
struct ssc_parser_helper_t
|
|
|
|
|
{
|
|
|
|
|
steps_handler_map_t steps_tag_handlers;
|
|
|
|
|
song_handler_map_t song_tag_handlers;
|
|
|
|
|
load_note_data_handler_map_t load_note_data_handlers;
|
|
|
|
|
// Unless signed, the comments in this tag list are not by me. They were
|
|
|
|
|
// moved here when converting from the else if chain. -Kyz
|
|
|
|
|
ssc_parser_helper_t()
|
|
|
|
|
{
|
|
|
|
|
song_tag_handlers["VERSION"]= &SetVersion;
|
|
|
|
|
song_tag_handlers["TITLE"]= &SetTitle;
|
|
|
|
|
song_tag_handlers["SUBTITLE"]= &SetSubtitle;
|
|
|
|
|
song_tag_handlers["ARTIST"]= &SetArtist;
|
|
|
|
|
song_tag_handlers["TITLETRANSLIT"]= &SetMainTitleTranslit;
|
|
|
|
|
song_tag_handlers["SUBTITLETRANSLIT"]= &SetSubtitleTranslit;
|
|
|
|
|
song_tag_handlers["ARTISTTRANSLIT"]= &SetArtistTranslit;
|
|
|
|
|
song_tag_handlers["GENRE"]= &SetGenre;
|
|
|
|
|
song_tag_handlers["ORIGIN"]= &SetOrigin;
|
|
|
|
|
song_tag_handlers["CREDIT"]= &SetCredit;
|
|
|
|
|
song_tag_handlers["BANNER"]= &SetBanner;
|
|
|
|
|
song_tag_handlers["BACKGROUND"]= &SetBackground;
|
|
|
|
|
song_tag_handlers["PREVIEWVID"]= &SetPreviewVid;
|
|
|
|
|
song_tag_handlers["JACKET"]= &SetJacket;
|
|
|
|
|
song_tag_handlers["CDIMAGE"]= &SetCDImage;
|
|
|
|
|
song_tag_handlers["DISCIMAGE"]= &SetDiscImage;
|
|
|
|
|
song_tag_handlers["LYRICSPATH"]= &SetLyricsPath;
|
|
|
|
|
song_tag_handlers["CDTITLE"]= &SetCDTitle;
|
|
|
|
|
song_tag_handlers["MUSIC"]= &SetMusic;
|
|
|
|
|
song_tag_handlers["PREVIEW"]= &SetPreview;
|
|
|
|
|
song_tag_handlers["INSTRUMENTTRACK"]= &SetInstrumentTrack;
|
|
|
|
|
song_tag_handlers["MUSICLENGTH"]= &SetMusicLength;
|
|
|
|
|
song_tag_handlers["LASTSECONDHINT"]= &SetLastSecondHint;
|
|
|
|
|
song_tag_handlers["SAMPLESTART"]= &SetSampleStart;
|
|
|
|
|
song_tag_handlers["SAMPLELENGTH"]= &SetSampleLength;
|
|
|
|
|
song_tag_handlers["DISPLAYBPM"]= &SetDisplayBPM;
|
|
|
|
|
song_tag_handlers["SELECTABLE"]= &SetSelectable;
|
|
|
|
|
// It's a bit odd to have the tag that exists for backwards compatibility
|
|
|
|
|
// in this list and not the replacement, but the BGCHANGES tag has a
|
|
|
|
|
// number on the end, allowing up to NUM_BackgroundLayer tags, so it
|
|
|
|
|
// can't fit in the map. -Kyz
|
|
|
|
|
song_tag_handlers["ANIMATIONS"]= &SetBGChanges;
|
|
|
|
|
song_tag_handlers["FGCHANGES"]= &SetFGChanges;
|
|
|
|
|
song_tag_handlers["KEYSOUNDS"]= &SetKeysounds;
|
|
|
|
|
song_tag_handlers["ATTACKS"]= &SetAttacks;
|
|
|
|
|
song_tag_handlers["OFFSET"]= &SetOffset;
|
|
|
|
|
/* Below are the song based timings that should only be used
|
|
|
|
|
* if the steps do not have their own timing. */
|
|
|
|
|
song_tag_handlers["STOPS"]= &SetSongStops;
|
|
|
|
|
song_tag_handlers["DELAYS"]= &SetSongDelays;
|
|
|
|
|
song_tag_handlers["BPMS"]= &SetSongBPMs;
|
|
|
|
|
song_tag_handlers["WARPS"]= &SetSongWarps;
|
|
|
|
|
song_tag_handlers["LABELS"]= &SetSongLabels;
|
|
|
|
|
song_tag_handlers["TIMESIGNATURES"]= &SetSongTimeSignatures;
|
|
|
|
|
song_tag_handlers["TICKCOUNTS"]= &SetSongTickCounts;
|
|
|
|
|
song_tag_handlers["COMBOS"]= &SetSongCombos;
|
|
|
|
|
song_tag_handlers["SPEEDS"]= &SetSongSpeeds;
|
|
|
|
|
song_tag_handlers["SCROLLS"]= &SetSongScrolls;
|
|
|
|
|
song_tag_handlers["FAKES"]= &SetSongFakes;
|
|
|
|
|
/* The following are cache tags. Never fill their values
|
|
|
|
|
* directly: only from the cached version. */
|
|
|
|
|
song_tag_handlers["FIRSTSECOND"]= &SetFirstSecond;
|
|
|
|
|
song_tag_handlers["LASTSECOND"]= &SetLastSecond;
|
|
|
|
|
song_tag_handlers["SONGFILENAME"]= &SetSongFilename;
|
|
|
|
|
song_tag_handlers["HASMUSIC"]= &SetHasMusic;
|
|
|
|
|
song_tag_handlers["HASBANNER"]= &SetHasBanner;
|
|
|
|
|
/* Tags that no longer exist, listed for posterity. May their names
|
|
|
|
|
* never be forgotten for their service to Stepmania. -Kyz
|
|
|
|
|
* LASTBEATHINT: // unable to parse due to tag position. Ignore.
|
|
|
|
|
* MUSICBYTES: // ignore
|
|
|
|
|
* FIRSTBEAT: // no longer used.
|
|
|
|
|
* LASTBEAT: // no longer used.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
steps_tag_handlers["VERSION"]= &SetStepsVersion;
|
|
|
|
|
steps_tag_handlers["CHARTNAME"]= &SetChartName;
|
|
|
|
|
steps_tag_handlers["STEPSTYPE"]= &SetStepsType;
|
|
|
|
|
steps_tag_handlers["CHARTSTYLE"]= &SetChartStyle;
|
|
|
|
|
steps_tag_handlers["DESCRIPTION"]= &SetDescription;
|
|
|
|
|
steps_tag_handlers["DIFFICULTY"]= &SetDifficulty;
|
|
|
|
|
steps_tag_handlers["METER"]= &SetMeter;
|
|
|
|
|
steps_tag_handlers["RADARVALUES"]= &SetRadarValues;
|
|
|
|
|
steps_tag_handlers["CREDIT"]= &SetCredit;
|
|
|
|
|
steps_tag_handlers["MUSIC"]= &SetStepsMusic;
|
|
|
|
|
steps_tag_handlers["BPMS"]= &SetStepsBPMs;
|
|
|
|
|
steps_tag_handlers["STOPS"]= &SetStepsStops;
|
|
|
|
|
steps_tag_handlers["DELAYS"]= &SetStepsDelays;
|
|
|
|
|
steps_tag_handlers["TIMESIGNATURES"]= &SetStepsTimeSignatures;
|
|
|
|
|
steps_tag_handlers["TICKCOUNTS"]= &SetStepsTickCounts;
|
|
|
|
|
steps_tag_handlers["COMBOS"]= &SetStepsCombos;
|
|
|
|
|
steps_tag_handlers["WARPS"]= &SetStepsWarps;
|
|
|
|
|
steps_tag_handlers["SPEEDS"]= &SetStepsSpeeds;
|
|
|
|
|
steps_tag_handlers["SCROLLS"]= &SetStepsScrolls;
|
|
|
|
|
steps_tag_handlers["FAKES"]= &SetStepsFakes;
|
|
|
|
|
steps_tag_handlers["LABELS"]= &SetStepsLabels;
|
|
|
|
|
/* If this is called, the chart does not use the same attacks
|
|
|
|
|
* as the Song's timing. No other changes are required. */
|
|
|
|
|
steps_tag_handlers["ATTACKS"]= &SetStepsAttacks;
|
|
|
|
|
steps_tag_handlers["OFFSET"]= &SetStepsOffset;
|
|
|
|
|
steps_tag_handlers["DISPLAYBPM"]= &SetStepsDisplayBPM;
|
|
|
|
|
|
|
|
|
|
load_note_data_handlers["VERSION"]= LNDID_version;
|
|
|
|
|
load_note_data_handlers["STEPSTYPE"]= LNDID_stepstype;
|
|
|
|
|
load_note_data_handlers["CHARTNAME"]= LNDID_chartname;
|
|
|
|
|
load_note_data_handlers["DESCRIPTION"]= LNDID_description;
|
|
|
|
|
load_note_data_handlers["DIFFICULTY"]= LNDID_difficulty;
|
|
|
|
|
load_note_data_handlers["METER"]= LNDID_meter;
|
|
|
|
|
load_note_data_handlers["CREDIT"]= LNDID_credit;
|
|
|
|
|
load_note_data_handlers["NOTES"]= LNDID_notes;
|
|
|
|
|
load_note_data_handlers["NOTES2"]= LNDID_notes2;
|
|
|
|
|
load_note_data_handlers["NOTEDATA"]= LNDID_notedata;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
ssc_parser_helper_t parser_helper;
|
|
|
|
|
// End parser_helper related functions. -Kyz
|
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
2013-05-28 23:39:11 -04:00
|
|
|
void SSCLoader::ProcessBPMs( TimingData &out, const RString sParam )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayBPMExpressions;
|
|
|
|
|
split( sParam, ",", arrayBPMExpressions );
|
|
|
|
|
|
|
|
|
|
for( unsigned b=0; b<arrayBPMExpressions.size(); b++ )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayBPMValues;
|
|
|
|
|
split( arrayBPMExpressions[b], "=", arrayBPMValues );
|
|
|
|
|
if( arrayBPMValues.size() != 2 )
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid #BPMS value \"%s\" (must have exactly one '='), ignored.",
|
|
|
|
|
arrayBPMExpressions[b].c_str() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float fBeat = StringToFloat( arrayBPMValues[0] );
|
|
|
|
|
const float fNewBPM = StringToFloat( arrayBPMValues[1] );
|
|
|
|
|
if( fBeat >= 0 && fNewBPM > 0 )
|
|
|
|
|
{
|
|
|
|
|
out.AddSegment( BPMSegment(BeatToNoteRow(fBeat), fNewBPM) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid BPM at beat %f, BPM %f.",
|
|
|
|
|
fBeat, fNewBPM );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SSCLoader::ProcessStops( TimingData &out, const RString sParam )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayStopExpressions;
|
|
|
|
|
split( sParam, ",", arrayStopExpressions );
|
|
|
|
|
|
|
|
|
|
for( unsigned b=0; b<arrayStopExpressions.size(); b++ )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayStopValues;
|
|
|
|
|
split( arrayStopExpressions[b], "=", arrayStopValues );
|
|
|
|
|
if( arrayStopValues.size() != 2 )
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid #STOPS value \"%s\" (must have exactly one '='), ignored.",
|
|
|
|
|
arrayStopExpressions[b].c_str() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float fBeat = StringToFloat( arrayStopValues[0] );
|
|
|
|
|
const float fNewStop = StringToFloat( arrayStopValues[1] );
|
|
|
|
|
if( fBeat >= 0 && fNewStop > 0 )
|
|
|
|
|
out.AddSegment( StopSegment(BeatToNoteRow(fBeat), fNewStop) );
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid Stop at beat %f, length %f.",
|
|
|
|
|
fBeat, fNewStop );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-16 10:02:57 -04:00
|
|
|
void SSCLoader::ProcessWarps( TimingData &out, const RString sParam, const float fVersion )
|
2011-05-09 22:42:46 -04:00
|
|
|
{
|
|
|
|
|
vector<RString> arrayWarpExpressions;
|
|
|
|
|
split( sParam, ",", arrayWarpExpressions );
|
|
|
|
|
|
|
|
|
|
for( unsigned b=0; b<arrayWarpExpressions.size(); b++ )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayWarpValues;
|
|
|
|
|
split( arrayWarpExpressions[b], "=", arrayWarpValues );
|
|
|
|
|
if( arrayWarpValues.size() != 2 )
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid #WARPS value \"%s\" (must have exactly one '='), ignored.",
|
2011-05-09 22:42:46 -04:00
|
|
|
arrayWarpExpressions[b].c_str() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float fBeat = StringToFloat( arrayWarpValues[0] );
|
|
|
|
|
const float fNewBeat = StringToFloat( arrayWarpValues[1] );
|
2011-05-16 10:02:57 -04:00
|
|
|
// Early versions were absolute in beats. They should be relative.
|
2011-05-16 10:45:03 -04:00
|
|
|
if( ( fVersion < VERSION_SPLIT_TIMING && fNewBeat > fBeat ) )
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
out.AddSegment( WarpSegment(BeatToNoteRow(fBeat), fNewBeat - fBeat) );
|
2011-05-16 10:45:03 -04:00
|
|
|
}
|
|
|
|
|
else if( fNewBeat > 0 )
|
2011-09-15 03:28:58 +00:00
|
|
|
out.AddSegment( WarpSegment(BeatToNoteRow(fBeat), fNewBeat) );
|
2011-05-09 22:42:46 -04:00
|
|
|
else
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid Warp at beat %f, BPM %f.",
|
|
|
|
|
fBeat, fNewBeat );
|
2011-05-09 22:42:46 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SSCLoader::ProcessLabels( TimingData &out, const RString sParam )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayLabelExpressions;
|
|
|
|
|
split( sParam, ",", arrayLabelExpressions );
|
|
|
|
|
|
|
|
|
|
for( unsigned b=0; b<arrayLabelExpressions.size(); b++ )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayLabelValues;
|
|
|
|
|
split( arrayLabelExpressions[b], "=", arrayLabelValues );
|
|
|
|
|
if( arrayLabelValues.size() != 2 )
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid #LABELS value \"%s\" (must have exactly one '='), ignored.",
|
2011-05-09 22:42:46 -04:00
|
|
|
arrayLabelExpressions[b].c_str() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float fBeat = StringToFloat( arrayLabelValues[0] );
|
|
|
|
|
RString sLabel = arrayLabelValues[1];
|
|
|
|
|
TrimRight(sLabel);
|
|
|
|
|
if( fBeat >= 0.0f )
|
2011-09-15 03:28:58 +00:00
|
|
|
out.AddSegment( LabelSegment(BeatToNoteRow(fBeat), sLabel) );
|
2011-05-09 22:42:46 -04:00
|
|
|
else
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an invalid Label at beat %f called %s.",
|
|
|
|
|
fBeat, sLabel.c_str() );
|
2011-05-09 22:42:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-09 14:45:30 -04:00
|
|
|
void SSCLoader::ProcessCombos( TimingData &out, const RString line, const int rowsPerBeat )
|
2011-05-09 22:42:46 -04:00
|
|
|
{
|
|
|
|
|
vector<RString> arrayComboExpressions;
|
2011-06-09 14:45:30 -04:00
|
|
|
split( line, ",", arrayComboExpressions );
|
2011-05-09 22:42:46 -04:00
|
|
|
|
|
|
|
|
for( unsigned f=0; f<arrayComboExpressions.size(); f++ )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> arrayComboValues;
|
|
|
|
|
split( arrayComboExpressions[f], "=", arrayComboValues );
|
2011-07-09 02:28:15 -04:00
|
|
|
unsigned size = arrayComboValues.size();
|
|
|
|
|
if( size < 2 )
|
2011-05-09 22:42:46 -04:00
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
2011-07-09 02:28:15 -04:00
|
|
|
"has an invalid #COMBOS value \"%s\" (must have at least one '='), ignored.",
|
2011-05-09 22:42:46 -04:00
|
|
|
arrayComboExpressions[f].c_str() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const float fComboBeat = StringToFloat( arrayComboValues[0] );
|
2011-05-14 18:56:58 -04:00
|
|
|
const int iCombos = StringToInt( arrayComboValues[1] );
|
2011-07-09 02:28:15 -04:00
|
|
|
const int iMisses = (size == 2 ? iCombos : StringToInt(arrayComboValues[2]));
|
2011-09-15 03:28:58 +00:00
|
|
|
out.AddSegment( ComboSegment( BeatToNoteRow(fComboBeat), iCombos, iMisses ) );
|
2011-05-09 22:42:46 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> vs1;
|
|
|
|
|
split( sParam, ",", vs1 );
|
|
|
|
|
|
|
|
|
|
FOREACH_CONST( RString, vs1, s1 )
|
|
|
|
|
{
|
|
|
|
|
vector<RString> vs2;
|
|
|
|
|
split( *s1, "=", vs2 );
|
|
|
|
|
|
|
|
|
|
if( vs2.size() < 2 )
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an scroll change with %i values.",
|
|
|
|
|
static_cast<int>(vs2.size()) );
|
2011-05-25 22:44:41 +07:00
|
|
|
continue;
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
const float fBeat = StringToFloat( vs2[0] );
|
2011-09-15 03:28:58 +00:00
|
|
|
const float fRatio = StringToFloat( vs2[1] );
|
|
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
if( fBeat < 0 )
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Song file",
|
|
|
|
|
this->GetSongTitle(),
|
|
|
|
|
"has an scroll change with beat %f.",
|
|
|
|
|
fBeat );
|
2011-05-25 22:44:41 +07:00
|
|
|
continue;
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
|
|
|
|
out.AddSegment( ScrollSegment(BeatToNoteRow(fBeat), fRatio) );
|
2011-05-25 22:44:41 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-17 16:06:40 -04:00
|
|
|
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;
|
2011-07-18 11:27:28 -06:00
|
|
|
float storedVersion = 0;
|
2011-07-17 16:06:40 -04:00
|
|
|
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);
|
2015-03-09 20:42:33 -06:00
|
|
|
|
|
|
|
|
load_note_data_handler_map_t::iterator handler=
|
|
|
|
|
parser_helper.load_note_data_handlers.find(valueName);
|
|
|
|
|
if(handler != parser_helper.load_note_data_handlers.end())
|
2015-03-09 16:58:56 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
if(tryingSteps)
|
2011-07-17 16:06:40 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
switch(handler->second)
|
|
|
|
|
{
|
|
|
|
|
case LNDID_version:
|
|
|
|
|
// Note that version is in both switches. Formerly, it was
|
|
|
|
|
// checked before the tryingSteps condition. -Kyz
|
|
|
|
|
storedVersion = StringToFloat(matcher);
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_stepstype:
|
|
|
|
|
if(out.m_StepsType != GAMEMAN->StringToStepsType(matcher))
|
|
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_chartname:
|
|
|
|
|
if(storedVersion >= VERSION_CHART_NAME_TAG &&
|
|
|
|
|
out.GetChartName() != matcher)
|
|
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_description:
|
|
|
|
|
if(storedVersion < VERSION_CHART_NAME_TAG)
|
|
|
|
|
{
|
|
|
|
|
if(out.GetChartName() != matcher)
|
|
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
}
|
|
|
|
|
else if(out.GetDescription() != matcher)
|
|
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_difficulty:
|
2015-03-15 16:54:04 -06:00
|
|
|
// Accept any difficulty if it's an edit because LoadEditFromMsd
|
|
|
|
|
// forces edits onto Edit difficulty even if they have a difficulty
|
|
|
|
|
// tag. -Kyz
|
|
|
|
|
if(out.GetDifficulty() != StringToDifficulty(matcher) &&
|
|
|
|
|
!(out.GetDifficulty() == Difficulty_Edit &&
|
|
|
|
|
GetExtension(cachePath).MakeLower() == "edit"))
|
2015-03-09 20:42:33 -06:00
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_meter:
|
|
|
|
|
if(out.GetMeter() != StringToInt(matcher))
|
|
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_credit:
|
|
|
|
|
if(out.GetCredit() != matcher)
|
|
|
|
|
{ tryingSteps = false; }
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_notes:
|
|
|
|
|
case LNDID_notes2:
|
|
|
|
|
out.SetSMNoteData(matcher);
|
|
|
|
|
out.TidyUpData();
|
|
|
|
|
return true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-03-09 16:58:56 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else
|
2011-07-17 16:06:40 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
switch(handler->second)
|
2011-07-17 16:06:40 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
case LNDID_version:
|
|
|
|
|
// Note that version is in both switches. Formerly, it was
|
|
|
|
|
// checked before the tryingSteps condition. -Kyz
|
|
|
|
|
storedVersion = StringToFloat(matcher);
|
|
|
|
|
break;
|
|
|
|
|
case LNDID_notedata:
|
|
|
|
|
tryingSteps = true;
|
|
|
|
|
break;
|
2011-07-17 16:06:40 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
// Silently ignore unrecognized tags, as was done before. -Kyz
|
2011-07-17 16:06:40 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-09 21:27:47 -04:00
|
|
|
bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
LOG->Trace( "Song::LoadFromSSCFile(%s)", sPath.c_str() );
|
|
|
|
|
|
|
|
|
|
MsdFile msd;
|
|
|
|
|
if( !msd.ReadFile( sPath, true ) )
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-09 20:48:23 -04:00
|
|
|
out.m_SongTiming.m_sFile = sPath; // songs still have their fallback timing.
|
2011-07-17 16:06:40 -04:00
|
|
|
out.m_sSongFileName = sPath;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
int state = GETTING_SONG_INFO;
|
|
|
|
|
const unsigned values = msd.GetNumValues();
|
|
|
|
|
Steps* pNewNotes = NULL;
|
2011-05-10 17:44:12 +07:00
|
|
|
TimingData stepsTiming;
|
2015-03-09 20:42:33 -06:00
|
|
|
|
|
|
|
|
SongTagInfo reused_song_info(&*this, &out, sPath, bFromCache);
|
|
|
|
|
StepsTagInfo reused_steps_info(&*this, &out, sPath, bFromCache);
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < values; i++ )
|
|
|
|
|
{
|
|
|
|
|
const MsdFile::value_t &sParams = msd.GetValue(i);
|
|
|
|
|
RString sValueName = sParams[0];
|
|
|
|
|
sValueName.MakeUpper();
|
|
|
|
|
|
|
|
|
|
switch (state)
|
|
|
|
|
{
|
|
|
|
|
case GETTING_SONG_INFO:
|
|
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
reused_song_info.params= &sParams;
|
|
|
|
|
song_handler_map_t::iterator handler=
|
|
|
|
|
parser_helper.song_tag_handlers.find(sValueName);
|
|
|
|
|
if(handler != parser_helper.song_tag_handlers.end())
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
handler->second(reused_song_info);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else if(sValueName.Left(strlen("BGCHANGES"))=="BGCHANGES")
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
SetBGChanges(reused_song_info);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
// This tag will get us to the next section.
|
|
|
|
|
else if(sValueName == "NOTEDATA")
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
state = GETTING_STEP_INFO;
|
|
|
|
|
pNewNotes = out.CreateSteps();
|
|
|
|
|
stepsTiming = TimingData(out.m_SongTiming.m_fBeat0OffsetInSeconds);
|
|
|
|
|
reused_steps_info.has_own_timing = false;
|
|
|
|
|
reused_steps_info.steps= pNewNotes;
|
|
|
|
|
reused_steps_info.timing= &stepsTiming;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
// Silently ignore unrecognized tags, as was done before. -Kyz
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case GETTING_STEP_INFO:
|
|
|
|
|
{
|
|
|
|
|
reused_steps_info.params= &sParams;
|
|
|
|
|
steps_handler_map_t::iterator handler=
|
|
|
|
|
parser_helper.steps_tag_handlers.find(sValueName);
|
|
|
|
|
if(handler != parser_helper.steps_tag_handlers.end())
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
handler->second(reused_steps_info);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else if(sValueName=="NOTES" || sValueName=="NOTES2")
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
state = GETTING_SONG_INFO;
|
|
|
|
|
if(reused_steps_info.has_own_timing)
|
|
|
|
|
{ pNewNotes->m_Timing = stepsTiming; }
|
|
|
|
|
reused_steps_info.has_own_timing = false;
|
|
|
|
|
pNewNotes->SetSMNoteData(sParams[1]);
|
|
|
|
|
pNewNotes->TidyUpData();
|
|
|
|
|
pNewNotes->SetFilename(sPath);
|
|
|
|
|
out.AddSteps(pNewNotes);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else if(sValueName=="STEPFILENAME")
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
state = GETTING_SONG_INFO;
|
|
|
|
|
if(reused_steps_info.has_own_timing)
|
|
|
|
|
{ pNewNotes->m_Timing = stepsTiming; }
|
|
|
|
|
reused_steps_info.has_own_timing = false;
|
|
|
|
|
pNewNotes->SetFilename(sParams[1]);
|
|
|
|
|
out.AddSteps(pNewNotes);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else
|
2015-03-09 16:58:56 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
// Silently ignore unrecognized tags, as was done before. -Kyz
|
2015-03-09 16:58:56 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
out.m_fVersion = STEPFILE_VERSION_NUMBER;
|
|
|
|
|
TidyUpData(out, bFromCache);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2015-03-09 16:58:56 -04:00
|
|
|
|
|
|
|
|
bool SSCLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool bAddStepsToSong, Song *givenSong /* =NULL */ )
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "SSCLoader::LoadEditFromFile(%s)", sEditFilePath.c_str() );
|
|
|
|
|
|
|
|
|
|
int iBytes = FILEMAN->GetFileSizeInBytes( sEditFilePath );
|
2011-03-17 01:47:30 -04:00
|
|
|
if( iBytes > MAX_EDIT_STEPS_SIZE_BYTES )
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Edit file",
|
|
|
|
|
sEditFilePath,
|
|
|
|
|
"is unreasonably large. It won't be loaded." );
|
2011-03-17 01:47:30 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MsdFile msd;
|
|
|
|
|
if( !msd.ReadFile( sEditFilePath, true ) ) // unescape
|
|
|
|
|
{
|
2011-06-14 13:35:59 -04:00
|
|
|
LOG->UserLog("Edit file",
|
|
|
|
|
sEditFilePath,
|
|
|
|
|
"couldn't be opened: %s", msd.GetError().c_str() );
|
2011-03-17 01:47:30 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 13:29:00 -06:00
|
|
|
return LoadEditFromMsd( msd, sEditFilePath, slot, bAddStepsToSong, givenSong );
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
2011-06-14 13:35:59 -04:00
|
|
|
bool SSCLoader::LoadEditFromMsd(const MsdFile &msd,
|
|
|
|
|
const RString &sEditFilePath,
|
|
|
|
|
ProfileSlot slot,
|
2013-11-14 13:29:00 -06:00
|
|
|
bool bAddStepsToSong,
|
|
|
|
|
Song *givenSong /* =NULL */ )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2013-11-14 13:29:00 -06:00
|
|
|
Song* pSong = givenSong;
|
2011-03-17 01:47:30 -04:00
|
|
|
Steps* pNewNotes = NULL;
|
2011-05-10 08:58:24 -04:00
|
|
|
TimingData stepsTiming;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
StepsTagInfo reused_steps_info(&*this, pSong, sEditFilePath, false);
|
|
|
|
|
reused_steps_info.for_load_edit= true;
|
2015-03-11 18:55:06 -06:00
|
|
|
reused_steps_info.timing= &stepsTiming;
|
2015-03-09 20:42:33 -06:00
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < msd.GetNumValues(); ++i)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
int iNumParams = msd.GetNumParams(i);
|
|
|
|
|
const MsdFile::value_t &sParams = msd.GetValue(i);
|
|
|
|
|
RString sValueName = sParams[0];
|
|
|
|
|
sValueName.MakeUpper();
|
|
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
if(pSong != NULL)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
reused_steps_info.params= &sParams;
|
|
|
|
|
steps_handler_map_t::iterator handler=
|
|
|
|
|
parser_helper.steps_tag_handlers.find(sValueName);
|
2015-03-11 18:55:06 -06:00
|
|
|
if(pNewNotes != NULL && handler != parser_helper.steps_tag_handlers.end())
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
handler->second(reused_steps_info);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else if(sValueName=="NOTEDATA")
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
pNewNotes = pSong->CreateSteps();
|
|
|
|
|
reused_steps_info.steps= pNewNotes;
|
|
|
|
|
reused_steps_info.ssc_format= true;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2015-03-09 20:42:33 -06:00
|
|
|
else if(sValueName=="NOTES")
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
if(pSong == NULL)
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Edit file", sEditFilePath,
|
|
|
|
|
"doesn't have a #SONG tag preceeding the first #NOTES tag,"
|
|
|
|
|
" and is not in a valid song-specific folder.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
if(!reused_steps_info.ssc_format && iNumParams < 7)
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Edit file", sEditFilePath,
|
|
|
|
|
"has %d fields in a #NOTES tag, but should have at least 7.",
|
|
|
|
|
iNumParams);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2015-03-09 16:58:56 -04:00
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
// I have no idea what the purpose of this bAddStepsToSong flag is.
|
|
|
|
|
// It looks like it causes LoadEditFromMsd to just throw away
|
|
|
|
|
// whatever steps data was loaded, possibly leaking the memory used
|
|
|
|
|
// by pNewNotes. It was here before I rewrote LoadEditFromMsd to
|
|
|
|
|
// use a map instead of an else if chain, so I preserved it. -Kyz
|
|
|
|
|
if(!bAddStepsToSong)
|
|
|
|
|
{ return true; }
|
2015-03-09 16:58:56 -04:00
|
|
|
|
2015-03-15 16:54:04 -06:00
|
|
|
// Force the difficulty to edit in case the edit set its own
|
|
|
|
|
// difficulty because IsEditAlreadyLoaded has an assert and edits
|
|
|
|
|
// shouldn't be able to add charts of other difficulties. -Kyz
|
|
|
|
|
if(pNewNotes != NULL)
|
|
|
|
|
{
|
|
|
|
|
pNewNotes->SetDifficulty(Difficulty_Edit);
|
|
|
|
|
if(pSong->IsEditAlreadyLoaded(pNewNotes))
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Edit file", sEditFilePath,
|
|
|
|
|
"is a duplicate of another edit that was already loaded.");
|
|
|
|
|
SAFE_DELETE(pNewNotes);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
if(reused_steps_info.ssc_format)
|
|
|
|
|
{
|
|
|
|
|
if(reused_steps_info.has_own_timing)
|
2015-03-15 16:54:04 -06:00
|
|
|
{ pNewNotes->m_Timing = stepsTiming; }
|
2015-03-09 20:42:33 -06:00
|
|
|
pNewNotes->SetSMNoteData(sParams[1]);
|
|
|
|
|
pNewNotes->TidyUpData();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pNewNotes = pSong->CreateSteps();
|
|
|
|
|
LoadFromTokens(sParams[1],
|
|
|
|
|
sParams[2],
|
|
|
|
|
sParams[3],
|
|
|
|
|
sParams[4],
|
|
|
|
|
sParams[5],
|
|
|
|
|
sParams[6],
|
|
|
|
|
*pNewNotes);
|
|
|
|
|
}
|
2015-03-05 20:32:08 -07:00
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
pNewNotes->SetLoadedFromProfile(slot);
|
|
|
|
|
pNewNotes->SetDifficulty(Difficulty_Edit);
|
|
|
|
|
pNewNotes->SetFilename(sEditFilePath);
|
2015-03-09 16:58:56 -04:00
|
|
|
|
2015-03-09 20:42:33 -06:00
|
|
|
pSong->AddSteps(pNewNotes);
|
|
|
|
|
return true; // Only allow one Steps per edit file!
|
2015-03-09 16:58:56 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
LOG->UserLog("Edit file", sEditFilePath,
|
|
|
|
|
"has an unexpected value \"%s\".", sValueName.c_str());
|
2015-03-09 16:58:56 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-03-09 20:42:33 -06:00
|
|
|
if(sValueName == "SONG")
|
|
|
|
|
{
|
|
|
|
|
if(pSong)
|
|
|
|
|
{
|
|
|
|
|
/* LOG->UserLog("Edit file", sEditFilePath, "has more than one #SONG tag.");
|
|
|
|
|
return false; */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RString sSongFullTitle = sParams[1];
|
|
|
|
|
this->SetSongTitle(sParams[1]);
|
|
|
|
|
sSongFullTitle.Replace('\\', '/');
|
|
|
|
|
pSong = SONGMAN->FindSong(sSongFullTitle);
|
2015-03-11 18:55:06 -06:00
|
|
|
reused_steps_info.song= pSong;
|
2015-03-09 20:42:33 -06:00
|
|
|
if(pSong == NULL)
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Edit file", sEditFilePath,
|
|
|
|
|
"requires a song \"%s\" that isn't present.",
|
|
|
|
|
sSongFullTitle.c_str());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(pSong->GetNumStepsLoadedFromProfile(slot) >= MAX_EDITS_PER_SONG_PER_PROFILE)
|
|
|
|
|
{
|
|
|
|
|
LOG->UserLog("Song file", sSongFullTitle,
|
|
|
|
|
"already has the maximum number of edits allowed for ProfileSlotP%d.",
|
|
|
|
|
slot+1);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
reused_steps_info.song= pSong;
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
2013-05-09 17:45:25 -04:00
|
|
|
// Edit had no valid #NOTES sections
|
|
|
|
|
return false;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (c) 2011 Jason Felds
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|