diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index d6320fef24..cba431e5b8 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -8,6 +8,11 @@ ________________________________________________________________________________ StepMania 5.0 $NEXT | 20110xyy -------------------------------------------------------------------------------- +2011/05/11 +---------- +* [GameSoundManager] Added PlayAnnouncer Lua binding. [AJ] +* [AnnouncerManager] Add support for "gameplay combo #" for announcer. [AJ] + 2011/05/10 ---------- * [Sprite] Added SetSecondsIntoAnimation Lua binding. [AJ] diff --git a/Xcode/stepmania.xcodeproj/project.pbxproj b/Xcode/stepmania.xcodeproj/project.pbxproj index 30eba612d7..a2ffdb9277 100644 --- a/Xcode/stepmania.xcodeproj/project.pbxproj +++ b/Xcode/stepmania.xcodeproj/project.pbxproj @@ -27,6 +27,10 @@ 2B30D6831315EA7E00044B68 /* MouseDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B30D6811315EA7E00044B68 /* MouseDevice.h */; }; 2B76698B132FF71500586512 /* NotesLoaderSMA.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B766989132FF71500586512 /* NotesLoaderSMA.h */; }; 2B76698C132FF71500586512 /* NotesLoaderSMA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B76698A132FF71500586512 /* NotesLoaderSMA.cpp */; }; + 2B9C2E8D137A138B004C92F3 /* NotesLoaderJson.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9C2E89137A138B004C92F3 /* NotesLoaderJson.cpp */; }; + 2B9C2E8E137A138B004C92F3 /* NotesLoaderJson.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B9C2E8A137A138B004C92F3 /* NotesLoaderJson.h */; }; + 2B9C2E8F137A138B004C92F3 /* NotesWriterJson.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2B9C2E8B137A138B004C92F3 /* NotesWriterJson.cpp */; }; + 2B9C2E90137A138B004C92F3 /* NotesWriterJson.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B9C2E8C137A138B004C92F3 /* NotesWriterJson.h */; }; 2BA7925E1304F20D002BD8E1 /* NotesLoaderSSC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BA7925A1304F20D002BD8E1 /* NotesLoaderSSC.cpp */; }; 2BA7925F1304F20D002BD8E1 /* NotesLoaderSSC.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BA7925B1304F20D002BD8E1 /* NotesLoaderSSC.h */; }; 2BA792601304F20D002BD8E1 /* NotesWriterSSC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BA7925C1304F20D002BD8E1 /* NotesWriterSSC.cpp */; }; @@ -1800,6 +1804,10 @@ 2B30D6811315EA7E00044B68 /* MouseDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MouseDevice.h; sourceTree = ""; }; 2B766989132FF71500586512 /* NotesLoaderSMA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesLoaderSMA.h; path = ../src/NotesLoaderSMA.h; sourceTree = SOURCE_ROOT; }; 2B76698A132FF71500586512 /* NotesLoaderSMA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesLoaderSMA.cpp; path = ../src/NotesLoaderSMA.cpp; sourceTree = SOURCE_ROOT; }; + 2B9C2E89137A138B004C92F3 /* NotesLoaderJson.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesLoaderJson.cpp; path = ../src/NotesLoaderJson.cpp; sourceTree = SOURCE_ROOT; }; + 2B9C2E8A137A138B004C92F3 /* NotesLoaderJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesLoaderJson.h; path = ../src/NotesLoaderJson.h; sourceTree = SOURCE_ROOT; }; + 2B9C2E8B137A138B004C92F3 /* NotesWriterJson.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesWriterJson.cpp; path = ../src/NotesWriterJson.cpp; sourceTree = SOURCE_ROOT; }; + 2B9C2E8C137A138B004C92F3 /* NotesWriterJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesWriterJson.h; path = ../src/NotesWriterJson.h; sourceTree = SOURCE_ROOT; }; 2BA7925A1304F20D002BD8E1 /* NotesLoaderSSC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesLoaderSSC.cpp; path = ../src/NotesLoaderSSC.cpp; sourceTree = SOURCE_ROOT; }; 2BA7925B1304F20D002BD8E1 /* NotesLoaderSSC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotesLoaderSSC.h; path = ../src/NotesLoaderSSC.h; sourceTree = SOURCE_ROOT; }; 2BA7925C1304F20D002BD8E1 /* NotesWriterSSC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NotesWriterSSC.cpp; path = ../src/NotesWriterSSC.cpp; sourceTree = SOURCE_ROOT; }; @@ -3251,6 +3259,8 @@ AA6EFDF004770C79005F316C /* NotesLoaderBMS.h */, AA6EFDF104770C79005F316C /* NotesLoaderDWI.cpp */, AA6EFDF204770C79005F316C /* NotesLoaderDWI.h */, + 2B9C2E89137A138B004C92F3 /* NotesLoaderJson.cpp */, + 2B9C2E8A137A138B004C92F3 /* NotesLoaderJson.h */, AA6EFDF304770C79005F316C /* NotesLoaderKSF.cpp */, AA6EFDF404770C79005F316C /* NotesLoaderKSF.h */, 3688B8510BD42DAA004186AD /* NotesLoaderMidi.cpp */, @@ -3263,6 +3273,8 @@ 2B766989132FF71500586512 /* NotesLoaderSMA.h */, 2BA7925A1304F20D002BD8E1 /* NotesLoaderSSC.cpp */, 2BA7925B1304F20D002BD8E1 /* NotesLoaderSSC.h */, + 2B9C2E8B137A138B004C92F3 /* NotesWriterJson.cpp */, + 2B9C2E8C137A138B004C92F3 /* NotesWriterJson.h */, AA6EFDF704770C79005F316C /* NotesWriterDWI.cpp */, AA6EFDF804770C79005F316C /* NotesWriterDWI.h */, AA6EFDF904770C79005F316C /* NotesWriterSM.cpp */, @@ -5049,6 +5061,8 @@ 2BC6133313773C8E00CC8BBD /* pngstruct.h in Headers */, 2BC6133E13773D2C00CC8BBD /* pnglibconf.h in Headers */, B75CE3B5137963350076A82D /* SongPosition.h in Headers */, + 2B9C2E8E137A138B004C92F3 /* NotesLoaderJson.h in Headers */, + 2B9C2E90137A138B004C92F3 /* NotesWriterJson.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6309,6 +6323,8 @@ 2BC6133913773C8E00CC8BBD /* pngwtran.c in Sources */, 2BC6133A13773C8E00CC8BBD /* pngwutil.c in Sources */, B75CE3B6137963350076A82D /* SongPosition.cpp in Sources */, + 2B9C2E8D137A138B004C92F3 /* NotesLoaderJson.cpp in Sources */, + 2B9C2E8F137A138B004C92F3 /* NotesWriterJson.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/extern/jsoncpp/include/json/value.h b/extern/jsoncpp/include/json/value.h index 58bfd88e7a..0cf4ca76cf 100644 --- a/extern/jsoncpp/include/json/value.h +++ b/extern/jsoncpp/include/json/value.h @@ -234,6 +234,13 @@ namespace Json { double asDouble() const; bool asBool() const; + bool TryGet( std::string &out ) const { if(!isString()) return false; out=asString(); return true; } + bool TryGet( int &out ) const { if(!isInt()) return false; out=asInt(); return true; } + bool TryGet( UInt &out ) const { if(!isUInt()) return false; out=asUInt(); return true; } + bool TryGet( double &out ) const { if(!isDouble()) return false; out=asDouble(); return true; } + bool TryGet( float &out ) const { if(!isDouble()) return false; out=(float)asDouble(); return true; } + bool TryGet( bool &out ) const { if(!isBool()) return false; out=asBool(); return true; } + bool isNull() const; bool isBool() const; bool isInt() const; diff --git a/src/AnnouncerManager.cpp b/src/AnnouncerManager.cpp index f64fcd624f..ee20dc427e 100644 --- a/src/AnnouncerManager.cpp +++ b/src/AnnouncerManager.cpp @@ -88,6 +88,18 @@ static const char *aliases[][2] = { { "ScreenSelectStyle comment solo", "select style comment solo" }, { "ScreenSelectStyle comment versus", "select style comment versus" }, + /* Combo compatibility: */ + { "gameplay combo 100", "gameplay 100 combo" }, + { "gameplay combo 200", "gameplay 200 combo" }, + { "gameplay combo 300", "gameplay 300 combo" }, + { "gameplay combo 400", "gameplay 400 combo" }, + { "gameplay combo 500", "gameplay 500 combo" }, + { "gameplay combo 600", "gameplay 600 combo" }, + { "gameplay combo 700", "gameplay 700 combo" }, + { "gameplay combo 800", "gameplay 800 combo" }, + { "gameplay combo 900", "gameplay 900 combo" }, + { "gameplay combo 1000", "gameplay 1000 combo" }, + { NULL, NULL } }; diff --git a/src/EditMenu.cpp b/src/EditMenu.cpp index b595a343f6..d1fbb3db14 100644 --- a/src/EditMenu.cpp +++ b/src/EditMenu.cpp @@ -386,7 +386,7 @@ void EditMenu::OnRowValueChanged( EditMenuRow row ) dcOld = GetSelectedDifficulty(); m_vpSteps.clear(); - + FOREACH_ENUM( Difficulty, dc ) { if( dc == Difficulty_Edit ) diff --git a/src/GameSoundManager.cpp b/src/GameSoundManager.cpp index 9560dfb4b3..a9e71fce24 100644 --- a/src/GameSoundManager.cpp +++ b/src/GameSoundManager.cpp @@ -806,12 +806,14 @@ public: return 0; } static int PlayOnce( T* p, lua_State *L ) { RString sPath = SArg(1); p->PlayOnce( sPath ); return 0; } + static int PlayAnnouncer( T* p, lua_State *L ) { RString sPath = SArg(1); p->PlayOnceFromAnnouncer( sPath ); return 0; } static int GetPlayerBalance( T* p, lua_State *L ) { PlayerNumber pn = Enum::Check(L, 1); lua_pushnumber( L, p->GetPlayerBalance(pn) ); return 1; } LunaGameSoundManager() { ADD_METHOD( DimMusic ); ADD_METHOD( PlayOnce ); + ADD_METHOD( PlayAnnouncer ); ADD_METHOD( GetPlayerBalance ); } }; diff --git a/src/JsonUtil.h b/src/JsonUtil.h index 8c17bb1af2..87942336e0 100644 --- a/src/JsonUtil.h +++ b/src/JsonUtil.h @@ -1,9 +1,10 @@ +/** @brief Utilities for handling JSON data. */ #ifndef JsonUtil_H #define JsonUtil_H class RageFileBasic; -#include "../extern/jsoncpp/include/json/value.h" -/** @brief Utilities for handling JSON data. */ +#include "json/value.h" + namespace JsonUtil { bool LoadFromString( Json::Value &root, RString sData, RString &sErrorOut ); @@ -31,6 +32,86 @@ namespace JsonUtil fn(*v[i], root[i]); } + template + static void SerializeArray(const V &v, void fn(const T &, Json::Value &), Json::Value &root) + { + root = Json::Value(Json::arrayValue); + root.resize( v.size() ); + int i=0; + for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ ) + fn( *iter, root[i++] ); + } + + template + static void SerializeArrayValues(const V &v, Json::Value &root) + { + root = Json::Value(Json::arrayValue); + root.resize( v.size() ); + int i=0; + for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ ) + root[i++] = *iter; + } + + template + static void SerializeArrayObjects(const V &v, Json::Value &root) + { + root = Json::Value(Json::arrayValue); + root.resize( v.size() ); + int i=0; + for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ ) + iter->Serialize( root[i++] ); + } + + template + static void SerializeStringToObjectMap(const M &m, F fnEnumToString(E e), Json::Value &root) + { + for( typename M::const_iterator iter=m.begin(); iter!=m.end(); iter++ ) + iter->second.Serialize( root[ fnEnumToString(iter->first) ] ); + } + + template + static void SerializeStringToValueMap(const M &m, F fnToString(E e), Json::Value &root) + { + for( typename M::const_iterator iter=m.begin(); iter!=m.end(); iter++ ) + root[ fnToString(iter->first) ] = iter->second; + } + + template + static void SerializeValueToValueMap(const M &m, Json::Value &root) + { + for( typename M::const_iterator iter=m.begin(); iter!=m.end(); iter++ ) + root[ (iter->first) ] = iter->second; + } + + // Serialize a map that has a non-string key type + template + static void SerializeObjectToObjectMapAsArray(const V &v, const RString &sKeyName, const RString &sValueName, Json::Value &root) + { + root = Json::Value(Json::arrayValue); + root.resize( v.size() ); + int i=0; + for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ ) + { + Json::Value &vv = root[i++]; + iter->first.Serialize( vv[sKeyName] ); + iter->second.Serialize( vv[sValueName] ); + } + } + + template + static void SerializeObjectToValueMapAsArray(const V &v, const RString &sKeyName, const RString &sValueName, Json::Value &root) + { + root = Json::Value(Json::arrayValue); + root.resize( v.size() ); + int i=0; + for( typename V::const_iterator iter=v.begin(); iter!=v.end(); iter++ ) + { + Json::Value &vv = root[i++]; + iter->first.Serialize( vv[sKeyName] ); + vv[sValueName] = iter->second; + } + } + template static void SerializeVectorValues(const vector &v, Json::Value &root) { @@ -48,6 +129,14 @@ namespace JsonUtil fn(v[i], root[i]); } + template + static void DeserializeArrayObjects( V &v, const Json::Value &root) + { + v.resize( root.size() ); + for( unsigned i=0; i static void DeserializeVectorPointers(vector &v, void fn(T &, const Json::Value &), const Json::Value &root) { @@ -61,6 +150,99 @@ namespace JsonUtil } } + template + static void DeserializeArrayValues(vector &v, const Json::Value &root) + { + v.clear(); + for( unsigned i=0; i + static void DeserializeArrayValuesIntoSet(S &s, const Json::Value &root) + { + s.clear(); + for( unsigned i=0; i + static void DeserializeArrayValuesIntoVector(vector &v, const Json::Value &root) + { + v.clear(); + for( unsigned i=0; i + static void DeserializeValueToValueMap(M &m, const Json::Value &root) + { + for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ ) + (*iter).TryGet( m[ iter.memberName() ] ); + } + + template + static void DeserializeStringToValueMap(M &m, F fnToValue(E e), const Json::Value &root) + { + for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ ) + (*iter).TryGet( m[ fnToValue(iter.memberName()) ] ); + } + + template + static void DeserializeStringToObjectMap(M &m, F fnToValue(E e), const Json::Value &root) + { + for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ ) + m[ fnToValue(iter.memberName()) ].Deserialize( *iter ); + } + + // Serialize a map that has a non-string key type + template + static void DeserializeObjectToObjectMapAsArray(map &m, const RString &sKeyName, const RString &sValueName, const Json::Value &root) + { + m.clear(); + ASSERT( root.type() == Json::arrayValue ); + for( Json::Value::const_iterator iter = root.begin(); iter != root.end(); iter++ ) + { + ASSERT( (*iter).type() == Json::objectValue ); + K k; + if( !k.Deserialize( (*iter)[sKeyName] ) ) + continue; + V v; + if( !v.Deserialize( (*iter)[sValueName] ) ) + continue; + m[k] = v; + } + } + + template + static void DeserializeObjectToValueMapAsArray(map &m, const RString &sKeyName, const RString &sValueName, const Json::Value &root) + { + for( unsigned i=0; i static void DeserializeVectorValues(vector &v, const Json::Value &root) { diff --git a/src/Makefile.am b/src/Makefile.am index 6a50b53cf8..044b03e9bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -158,10 +158,12 @@ ModsGroup.cpp ModsGroup.h \ NoteData.cpp NoteData.h NoteDataUtil.cpp NoteDataUtil.h NoteDataWithScoring.cpp NoteDataWithScoring.h \ NoteTypes.cpp NoteTypes.h NotesLoader.cpp NotesLoader.h \ NotesLoaderBMS.cpp NotesLoaderBMS.h NotesLoaderDWI.cpp NotesLoaderDWI.h \ +NotesLoaderJson.cpp NotesLoaderJson.h \ NotesLoaderKSF.cpp NotesLoaderKSF.h NotesLoaderMidi.cpp NotesLoaderMidi.h \ NotesLoaderPMS.cpp NotesLoaderPMS.h NotesLoaderSM.cpp NotesLoaderSM.h \ NotesLoaderSSC.cpp NotesLoaderSSC.h NotesLoaderSMA.cpp NotesLoaderSMA.h \ NotesWriterDWI.cpp NotesWriterDWI.h \ +NotesWriterJson.cpp NotesWriterJson.h \ NotesWriterSM.cpp NotesWriterSM.h NotesWriterSSC.cpp NotesWriterSSC.h \ OptionRowHandler.cpp OptionRowHandler.h OptionsList.cpp OptionsList.h \ PlayerAI.cpp PlayerAI.h PlayerNumber.cpp PlayerNumber.h PlayerOptions.cpp PlayerOptions.h \ diff --git a/src/NotesLoaderJson.cpp b/src/NotesLoaderJson.cpp new file mode 100644 index 0000000000..fc7fb823ad --- /dev/null +++ b/src/NotesLoaderJson.cpp @@ -0,0 +1,238 @@ +#include "global.h" +#include "NotesLoaderJson.h" +#include "Json/Value.h" +#include "TimingData.h" +#include "RageUtil.h" +#include "JsonUtil.h" +#include "BackgroundUtil.h" +#include "NoteData.h" +#include "Song.h" +#include "Steps.h" +#include "GameManager.h" + +void NotesLoaderJson::GetApplicableFiles( const RString &sPath, vector &out ) +{ + GetDirListing( sPath + RString("*.json"), out ); +} + +void Deserialize(BPMSegment &seg, const Json::Value &root) +{ + seg.m_iStartRow = BeatToNoteRow((float)root["Beat"].asDouble()); + seg.m_fBPS = (float)(root["BPM"].asDouble() / 60); +} + +static void Deserialize(StopSegment &seg, const Json::Value &root) +{ + seg.m_iStartRow = BeatToNoteRow((float)(root["Beat"].asDouble())); + seg.m_fStopSeconds = (float)root["Seconds"].asDouble(); +} + +static void Deserialize(TimingData &td, const Json::Value &root) +{ + JsonUtil::DeserializeVectorObjects( td.m_BPMSegments, Deserialize, root["BpmSegments"] ); + JsonUtil::DeserializeVectorObjects( td.m_StopSegments, Deserialize, root["StopSegments"] ); +} + +static void Deserialize(LyricSegment &o, const Json::Value &root) +{ + o.m_fStartTime = (float)root["StartTime"].asDouble(); + o.m_sLyric = root["Lyric"].asString(); + o.m_Color.FromString( root["Color"].asString() ); +} + +static void Deserialize(BackgroundDef &o, const Json::Value &root) +{ + o.m_sEffect = root["Effect"].asString(); + o.m_sFile1 = root["File1"].asString(); + o.m_sFile2 = root["File2"].asString(); + o.m_sColor1 = root["Color1"].asString(); +} + +static void Deserialize(BackgroundChange &o, const Json::Value &root ) +{ + Deserialize( o.m_def, root["Def"] ); + o.m_fStartBeat = (float)root["StartBeat"].asDouble(); + o.m_fRate = (float)root["Rate"].asDouble(); + o.m_sTransition = root["Transition"].asString(); +} + +static void Deserialize( TapNote &o, const Json::Value &root ) +{ + //if( o.type != TapNote::tap ) + if( root.isInt() ) + o.type = (TapNote::Type)root["Type"].asInt(); + //if( o.type == TapNote::hold_head ) + o.subType = (TapNote::SubType)root["SubType"].asInt(); + //root["Source"] = (int)source; + //if( !o.sAttackModifiers.empty() ) + o.sAttackModifiers = root["AttackModifiers"].asString(); + //if( o.fAttackDurationSeconds > 0 ) + o.fAttackDurationSeconds = (float)root["AttackDurationSeconds"].asDouble(); + //if( o.bKeysound ) + o.iKeysoundIndex = root["KeysoundIndex"].asInt(); + //if( o.iDuration > 0 ) + o.iDuration = root["Duration"].asInt(); + //if( o.pn != PLAYER_INVALID ) + o.pn = (PlayerNumber)root["PlayerNumber"].asInt(); +} + +static void Deserialize( StepsType st, NoteData &nd, const Json::Value &root ) +{ + int iTracks = nd.GetNumTracks(); + nd.SetNumTracks( iTracks ); + for( unsigned i=0; iStringToStepsType(root["StepsType"].asString()); + + o.Decompress(); + + NoteData nd; + Deserialize( o.m_StepsType, nd, root["NoteData"] ); + o.SetNoteData( nd ); + //o.SetHash( root["Hash"].asInt() ); + o.SetDescription( root["Description"].asString() ); + o.SetDifficulty( StringToDifficulty(root["Difficulty"].asString()) ); + o.SetMeter( root["Meter"].asInt() ); + + RadarValues rv[NUM_PLAYERS]; + FOREACH_PlayerNumber( pn ) + { + Deserialize( rv[pn], root["RadarValues"] ); + } + o.SetCachedRadarValues( rv ); +} + +static void Deserialize( Song &out, const Json::Value &root ) +{ + out.SetSongDir( root["SongDir"].asString() ); + out.m_sGroupName = root["GroupName"].asString(); + out.m_sMainTitle = root["Title"].asString(); + out.m_sSubTitle = root["SubTitle"].asString(); + out.m_sArtist = root["Artist"].asString(); + out.m_sMainTitleTranslit = root["TitleTranslit"].asString(); + out.m_sSubTitleTranslit = root["SubTitleTranslit"].asString(); + out.m_sGenre = root["Genre"].asString(); + out.m_sCredit = root["Credit"].asString(); + out.m_sBannerFile = root["Banner"].asString(); + out.m_sBackgroundFile = root["Background"].asString(); + out.m_sLyricsFile = root["LyricsFile"].asString(); + out.m_sCDTitleFile = root["CDTitle"].asString(); + out.m_sMusicFile = root["Music"].asString(); + out.m_SongTiming.m_fBeat0OffsetInSeconds = (float)root["Offset"].asDouble(); + out.m_fMusicSampleStartSeconds = (float)root["SampleStart"].asDouble(); + out.m_fMusicSampleLengthSeconds = (float)root["SampleLength"].asDouble(); + RString sSelectable = root["Selectable"].asString(); + if( !stricmp(sSelectable,"YES") ) + out.m_SelectionDisplay = out.SHOW_ALWAYS; + else if(!stricmp(sSelectable,"NO")) + out.m_SelectionDisplay = out.SHOW_NEVER; + + out.m_fFirstBeat = (float)root["FirstBeat"].asDouble(); + out.m_fLastBeat = (float)root["LastBeat"].asDouble(); + out.m_sSongFileName = root["SongFileName"].asString(); + out.m_bHasMusic = root["HasMusic"].asBool(); + out.m_bHasBanner = root["HasBanner"].asBool(); + out.m_fMusicLengthSeconds = (float)root["MusicLengthSeconds"].asDouble(); + + RString sDisplayBPMType = root["DisplayBpmType"].asString(); + if( sDisplayBPMType == "*" ) + out.m_DisplayBPMType = DISPLAY_BPM_RANDOM; + else + out.m_DisplayBPMType = DISPLAY_BPM_SPECIFIED; + + if( out.m_DisplayBPMType == DISPLAY_BPM_SPECIFIED ) + { + out.m_fSpecifiedBPMMin = (float)root["SpecifiedBpmMin"].asDouble(); + out.m_fSpecifiedBPMMax = (float)root["SpecifiedBpmMax"].asDouble(); + } + + Deserialize( out.m_SongTiming, root["TimingData"] ); + JsonUtil::DeserializeVectorObjects( out.m_LyricSegments, Deserialize, root["LyricSegments"] ); + + { + const Json::Value &root2 = root["BackgroundChanges"]; + FOREACH_BackgroundLayer( bl ) + { + const Json::Value &root3 = root2[bl]; + vector &vBgc = out.GetBackgroundChanges(bl); + JsonUtil::DeserializeVectorObjects( vBgc, Deserialize, root3 ); + } + } + + { + vector &vBgc = out.GetForegroundChanges(); + JsonUtil::DeserializeVectorObjects( vBgc, Deserialize, root["ForegroundChanges"] ); + } + + JsonUtil::DeserializeArrayValuesIntoVector( out.m_vsKeysoundFile, root["KeySounds"] ); + + { + vector vpSteps; + JsonUtil::DeserializeVectorPointers( vpSteps, Deserialize, root["Charts"] ); + FOREACH( Steps*, vpSteps, iter ) + out.AddSteps( *iter ); + } + +} + +bool NotesLoaderJson::LoadFromJsonFile( const RString &sPath, Song &out ) +{ + Json::Value root; + if( !JsonUtil::LoadFromFileShowErrors(root,sPath) ) + return false; + + Deserialize(out, root); + + return true; +} + +bool NotesLoaderJson::LoadFromDir( const RString &sPath, Song &out ) +{ + return LoadFromJsonFile(sPath, out); +} + +/* + * (c) 2001-2004 Chris Danford, Glenn Maynard + * 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. + */ diff --git a/src/NotesLoaderJson.h b/src/NotesLoaderJson.h new file mode 100644 index 0000000000..c96629ffd8 --- /dev/null +++ b/src/NotesLoaderJson.h @@ -0,0 +1,41 @@ +/* JsonLoader - Reads a Song from a .json file. */ + +#ifndef NotesLoaderJson_H +#define NotesLoaderJson_H + +#include "NotesLoader.h" +class Song; + +namespace NotesLoaderJson +{ + void GetApplicableFiles( const RString &sPath, vector &out ); + bool LoadFromDir( const RString &sPath, Song &out ); + bool LoadFromJsonFile( const RString &sPath, Song &out ); +}; + +#endif + +/* + * (c) 2001-2004 Chris Danford, Glenn Maynard + * 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. + */ diff --git a/src/NotesWriterJson.cpp b/src/NotesWriterJson.cpp new file mode 100644 index 0000000000..9cc91e3a58 --- /dev/null +++ b/src/NotesWriterJson.cpp @@ -0,0 +1,229 @@ +#include "global.h" +#include "NotesWriterJson.h" +#include "TimingData.h" +#include "Json/Value.h" +#include "JsonUtil.h" +#include "Song.h" +#include "BackgroundUtil.h" +#include "Steps.h" +#include "NoteData.h" +#include "GameManager.h" + +void Serialize(const BPMSegment &seg, Json::Value &root) +{ + root["Beat"] = NoteRowToBeat(seg.m_iStartRow); + root["BPM"] = seg.m_fBPS * 60; +} + +static void Serialize(const StopSegment &seg, Json::Value &root) +{ + root["Beat"] = NoteRowToBeat(seg.m_iStartRow); + root["Seconds"] = seg.m_fStopSeconds; +} + +static void Serialize(const TimingData &td, Json::Value &root) +{ + JsonUtil::SerializeVectorObjects( td.m_BPMSegments, Serialize, root["BpmSegments"] ); + JsonUtil::SerializeVectorObjects( td.m_StopSegments, Serialize, root["StopSegments"] ); +} + +static void Serialize(const LyricSegment &o, Json::Value &root) +{ + root["StartTime"] = (float)o.m_fStartTime; + root["Lyric"] = o.m_sLyric; + root["Color"] = o.m_Color.ToString(); +} + +static void Serialize(const BackgroundDef &o, Json::Value &root) +{ + root["Effect"] = o.m_sEffect; + root["File1"] = o.m_sFile1; + root["File2"] = o.m_sFile2; + root["Color1"] = o.m_sColor1; +} + +static void Serialize(const BackgroundChange &o, Json::Value &root ) +{ + Serialize( o.m_def, root["Def"] ); + root["StartBeat"] = o.m_fStartBeat; + root["Rate"] = o.m_fRate; + root["Transition"] = o.m_sTransition; +} + +static void Serialize( const TapNote &o, Json::Value &root ) +{ + root = Json::Value(Json::objectValue); + + if( o.type != TapNote::tap ) + root["Type"] = (int)o.type; + if( o.type == TapNote::hold_head ) + root["SubType"] = (int)o.subType; + //root["Source"] = (int)source; + if( !o.sAttackModifiers.empty() ) + root["AttackModifiers"] = o.sAttackModifiers; + if( o.fAttackDurationSeconds > 0 ) + root["AttackDurationSeconds"] = o.fAttackDurationSeconds; + if( o.iKeysoundIndex != -1 ) + root["KeysoundIndex"] = o.iKeysoundIndex; + if( o.iDuration > 0 ) + root["Duration"] = o.iDuration; + if( o.pn != PLAYER_INVALID ) + root["PlayerNumber"] = (int)o.pn; +} + +static void Serialize( const NoteData &o, Json::Value &root ) +{ + root = Json::Value(Json::arrayValue); + for(int t=0; t < o.GetNumTracks(); t++ ) + { + NoteData::TrackMap::const_iterator begin, end; + o.GetTapNoteRange( t, 0, MAX_NOTE_ROW, begin, end ); + //NoteData::TrackMap tm = o.GetTrack(t); + //FOREACHM_CONST( int, TapNote, tm, iter ) + for( ; begin != end; ++begin ) + { + int iRow = begin->first; + TapNote tn = begin->second; + root.resize( root.size()+1 ); + Json::Value &root2 = root[ root.size()-1 ]; + root2 = Json::Value(Json::arrayValue); + root2.resize(3); + root2[(unsigned)0] = NoteRowToBeat(iRow); + root2[1] = t; + Serialize( tn, root2[2] ); + } + } +} + +static void Serialize( const RadarValues &o, Json::Value &root ) +{ + FOREACH_ENUM( RadarCategory, rc ) + { + root[ RadarCategoryToString(rc) ] = o.m_Values.f[rc]; + } +} + +static void Serialize( const Steps &o, Json::Value &root ) +{ + root["StepsType"] = StringConversion::ToString(o.m_StepsType); + + o.Decompress(); + + NoteData nd; + o.GetNoteData( nd ); + Serialize( nd, root["NoteData"] ); + root["Hash"] = o.GetHash(); + root["Description"] = o.GetDescription(); + root["Difficulty"] = DifficultyToString(o.GetDifficulty()); + root["Meter"] = o.GetMeter(); + Serialize( o.GetRadarValues( PLAYER_1 ), root["RadarValues"] ); +} + + +bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWriteSteps ) +{ + Json::Value root; + root["SongDir"] = out.GetSongDir(); + root["GroupName"] = out.m_sGroupName; + root["Title"] = out.m_sMainTitle; + root["SubTitle"] = out.m_sSubTitle; + root["Artist"] = out.m_sArtist; + root["TitleTranslit"] = out.m_sMainTitleTranslit; + root["SubTitleTranslit"] = out.m_sSubTitleTranslit; + root["Genre"] = out.m_sGenre; + root["Credit"] = out.m_sCredit; + root["Banner"] = out.m_sBannerFile; + root["Background"] = out.m_sBackgroundFile; + root["LyricsFile"] = out.m_sLyricsFile; + root["CDTitle"] = out.m_sCDTitleFile; + root["Music"] = out.m_sMusicFile; + root["Offset"] = out.m_SongTiming.m_fBeat0OffsetInSeconds; + root["SampleStart"] = out.m_fMusicSampleStartSeconds; + root["SampleLength"] = out.m_fMusicSampleLengthSeconds; + if( out.m_SelectionDisplay == Song::SHOW_ALWAYS ) + root["Selectable"] = "YES"; + else if( out.m_SelectionDisplay == Song::SHOW_NEVER ) + root["Selectable"] = "NO"; + else + root["Selectable"] = "YES"; + + root["FirstBeat"] = out.m_fFirstBeat; + root["LastBeat"] = out.m_fLastBeat; + root["SongFileName"] = out.m_sSongFileName; + root["HasMusic"] = out.m_bHasMusic; + root["HasBanner"] = out.m_bHasBanner; + root["MusicLengthSeconds"] = out.m_fMusicLengthSeconds; + + root["DisplayBpmType"] = StringConversion::ToString(out.m_DisplayBPMType); + if( out.m_DisplayBPMType == DISPLAY_BPM_SPECIFIED ) + { + root["SpecifiedBpmMin"] = out.m_fSpecifiedBPMMin; + root["SpecifiedBpmMax"] = out.m_fSpecifiedBPMMax; + } + + Serialize( out.m_SongTiming, root["TimingData"] ); + JsonUtil::SerializeVectorObjects( out.m_LyricSegments, Serialize, root["LyricSegments"] ); + + { + Json::Value &root2 = root["BackgroundChanges"]; + FOREACH_BackgroundLayer( bl ) + { + Json::Value &root3 = root2[bl]; + const vector &vBgc = out.GetBackgroundChanges(bl); + JsonUtil::SerializeVectorObjects( vBgc, Serialize, root3 ); + } + } + + { + const vector &vBgc = out.GetForegroundChanges(); + JsonUtil::SerializeVectorObjects( vBgc, Serialize, root["ForegroundChanges"] ); + } + + JsonUtil::SerializeArrayValues( out.m_vsKeysoundFile, root["KeySounds"] ); + + if( bWriteSteps ) + { + vector vpSteps; + FOREACH_CONST( Steps*, out.GetAllSteps(), iter ) + { + if( (*iter)->IsAutogen() ) + continue; + vpSteps.push_back( *iter ); + } + JsonUtil::SerializeVectorPointers( vpSteps, Serialize, root["Charts"] ); + } + + return JsonUtil::WriteFile( root, sFile, false ); +} + +bool NotesWriterJson::WriteSteps( const RString &sFile, const Steps &out ) +{ + Json::Value root; + Serialize( out, root ); + return JsonUtil::WriteFile( root, sFile, false ); +} + +/* + * (c) 2001-2004 Chris Danford, Glenn Maynard + * 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. + */ diff --git a/src/NotesWriterJson.h b/src/NotesWriterJson.h new file mode 100644 index 0000000000..c873733f96 --- /dev/null +++ b/src/NotesWriterJson.h @@ -0,0 +1,40 @@ +/* NotesWriterJson - Writes a Song to a .json file. */ + +#ifndef NotesWriterJson_H +#define NotesWriterJson_H + +class Song; +class Steps; + +namespace NotesWriterJson +{ + static bool WriteSong( const RString &sFile, const Song &out, bool bWriteSteps ); + static bool WriteSteps( const RString &sFile, const Steps &out ); +}; + +#endif + +/* + * (c) 2001-2010 Chris Danford, Glenn Maynard + * 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. + */ diff --git a/src/Song.h b/src/Song.h index ac41bf476c..8d3134b0f2 100644 --- a/src/Song.h +++ b/src/Song.h @@ -73,6 +73,7 @@ class Song RString m_sSongDir; public: void SetSongDir( const RString sDir ) { m_sSongDir = sDir; } + RString GetSongDir() { return m_sSongDir; } /** @brief When should this song be displayed in the music wheel? */ enum SelectionDisplay diff --git a/src/StepMania-net2008.vcproj b/src/StepMania-net2008.vcproj index 7d7f87a40d..2dfb5b7eb1 100644 --- a/src/StepMania-net2008.vcproj +++ b/src/StepMania-net2008.vcproj @@ -1575,6 +1575,14 @@ RelativePath="NotesLoaderDWI.h" > + + + + @@ -1755,6 +1763,14 @@ RelativePath="NotesWriterDWI.h" > + + + + diff --git a/src/ThemeMetric.h b/src/ThemeMetric.h index 5ef34f9abe..a4073ffa1a 100644 --- a/src/ThemeMetric.h +++ b/src/ThemeMetric.h @@ -21,7 +21,7 @@ public: template struct ThemeMetricTypeTraits { - enum { Callable = 1 }; + enum { Callable = 1 }; }; /* LuaReference and apActorCommands return the function directly without calling it. */ @@ -68,7 +68,7 @@ public: { ThemeManager::Subscribe( this ); } - + ~ThemeMetric() { ThemeManager::Unsubscribe( this ); @@ -131,7 +131,7 @@ public: * @brief Retrieve the metric's group. * @return the metric's group. */ const RString &GetGroup() const { return m_sGroup; } - + /** * @brief Retrieve the metric's value. * @return the metric's value. */ @@ -154,7 +154,7 @@ public: return m_currentValue; } - + operator const T& () const { return GetValue(); @@ -162,7 +162,8 @@ public: bool IsLoaded() const { return m_Value.IsSet(); } - //Hacks for VC6 for all boolean operators. + // Hacks for VC6 for all boolean operators. + // todo: get rid of these -aj bool operator ! () const { return !GetValue(); } bool operator && ( const T& input ) const { return GetValue() && input; } bool operator || ( const T& input ) const { return GetValue() || input; }