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/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 13b0757ec4..668d96827b 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -2807,7 +2807,7 @@ Fallback="ScreenOptionsServiceChild" [ScreenSelectGame] Fallback="ScreenOptionsServiceChild" -PrevScreen=Branch.TitleMenu() +PrevScreen="ScreenOptionsService" NextScreen=Branch.TitleMenu() LineNames="1" Line1="conf,Game" @@ -2837,13 +2837,6 @@ Class="ScreenReloadSongs" Fallback="Screen" NextScreen=Branch.TitleMenu() -[ScreenSelectGame] -Fallback="ScreenOptionsServiceChild" -PrevScreen="ScreenOptionsService" -NextScreen=Branch.TitleMenu() -LineNames="1" -Line1="conf,Game" - [ScreenPlayerOptions] Fallback="ScreenOptions" Class="ScreenPlayerOptions" 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..dd87860a59 100644 --- a/src/AnnouncerManager.cpp +++ b/src/AnnouncerManager.cpp @@ -37,7 +37,7 @@ void AnnouncerManager::GetAnnouncerNames( vector& AddTo ) // strip out the empty announcer folder for( int i=AddTo.size()-1; i>=0; i-- ) - if( !stricmp( AddTo[i], EMPTY_ANNOUNCER_NAME ) ) + if( !AddTo[i].EqualsNoCase( EMPTY_ANNOUNCER_NAME ) ) AddTo.erase(AddTo.begin()+i, AddTo.begin()+i+1 ); } @@ -49,7 +49,7 @@ bool AnnouncerManager::DoesAnnouncerExist( RString sAnnouncerName ) vector asAnnouncerNames; GetAnnouncerNames( asAnnouncerNames ); for( unsigned i=0; iGetAttrValue( "Stretch", bStretch ); // Check for string match first, then do integer match. - // "if(atoi(type)==0)" was matching against all string matches. + // "if(StringType(type)==0)" was matching against all string matches. // -Chris - if( stricmp(type,"sprite")==0 ) + if( type.EqualsNoCase("sprite") ) { m_Type = TYPE_SPRITE; } - else if( stricmp(type,"particles")==0 ) + else if( type.EqualsNoCase("particles") ) { m_Type = TYPE_PARTICLES; } - else if( stricmp(type,"tiles")==0 ) + else if( type.EqualsNoCase("tiles") ) { m_Type = TYPE_TILES; } - else if( atoi(type) == 1 ) + else if( StringToInt(type) == 1 ) { m_Type = TYPE_SPRITE; bStretch = true; } - else if( atoi(type) == 2 ) + else if( StringToInt(type) == 2 ) { m_Type = TYPE_PARTICLES; } - else if( atoi(type) == 3 ) + else if( StringToInt(type) == 3 ) { m_Type = TYPE_TILES; } diff --git a/src/BPMDisplay.cpp b/src/BPMDisplay.cpp index e15a347fbe..49cb155978 100644 --- a/src/BPMDisplay.cpp +++ b/src/BPMDisplay.cpp @@ -205,7 +205,7 @@ void BPMDisplay::SetBpmFromCourse( const Course* pCourse ) StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; Trail *pTrail = pCourse->GetTrail( st ); // GetTranslitFullTitle because "Crashinfo.txt is garbled because of the ANSI output as usual." -f - ASSERT_M( pTrail, ssprintf("Course '%s' has no trail for StepsType '%s'", pCourse->GetTranslitFullTitle(), StringConversion::ToString(st).c_str() ) ); + ASSERT_M( pTrail, ssprintf("Course '%s' has no trail for StepsType '%s'", pCourse->GetTranslitFullTitle().c_str(), StringConversion::ToString(st).c_str() ) ); m_fCycleTime = 0.2f; diff --git a/src/CourseLoaderCRS.cpp b/src/CourseLoaderCRS.cpp index e2bd6a59a4..77a055eb56 100644 --- a/src/CourseLoaderCRS.cpp +++ b/src/CourseLoaderCRS.cpp @@ -61,13 +61,13 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou const MsdFile::value_t &sParams = msd.GetValue(i); // handle the data - if( 0 == stricmp(sValueName, "COURSE") ) + if( sValueName.EqualsNoCase("COURSE") ) out.m_sMainTitle = sParams[1]; - else if( 0 == stricmp(sValueName, "COURSETRANSLIT") ) + else if( sValueName.EqualsNoCase("COURSETRANSLIT") ) out.m_sMainTitleTranslit = sParams[1]; - else if( 0 == stricmp(sValueName, "SCRIPTER") ) + else if( sValueName.EqualsNoCase("SCRIPTER") ) out.m_sScripter = sParams[1]; - else if( 0 == stricmp(sValueName, "REPEAT") ) + else if( sValueName.EqualsNoCase("REPEAT") ) { RString str = sParams[1]; str.MakeLower(); @@ -75,27 +75,27 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou out.m_bRepeat = true; } - else if( 0 == stricmp(sValueName, "BANNER") ) + else if( sValueName.EqualsNoCase("BANNER") ) { out.m_sBannerPath = sParams[1]; } - else if( 0 == stricmp(sValueName, "BACKGROUND") ) + else if( sValueName.EqualsNoCase("BACKGROUND") ) { out.m_sBackgroundPath = sParams[1]; } - else if( 0 == stricmp(sValueName, "LIVES") ) + else if( sValueName.EqualsNoCase("LIVES") ) { - out.m_iLives = max( atoi(sParams[1]), 0 ); + out.m_iLives = max( StringToInt(sParams[1]), 0 ); } - else if( 0 == stricmp(sValueName, "GAINSECONDS") ) + else if( sValueName.EqualsNoCase("GAINSECONDS") ) { fGainSeconds = StringToFloat( sParams[1] ); } - else if( 0 == stricmp(sValueName, "METER") ) + else if( sValueName.EqualsNoCase("METER") ) { if( sParams.params.size() == 2 ) { - out.m_iCustomMeter[Difficulty_Medium] = max( atoi(sParams[1]), 0 ); /* compat */ + out.m_iCustomMeter[Difficulty_Medium] = max( StringToInt(sParams[1]), 0 ); /* compat */ } else if( sParams.params.size() == 3 ) { @@ -105,12 +105,12 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou LOG->UserLog( "Course file", sPath, "contains an invalid #METER string: \"%s\"", sParams[1].c_str() ); continue; } - out.m_iCustomMeter[cd] = max( atoi(sParams[2]), 0 ); + out.m_iCustomMeter[cd] = max( StringToInt(sParams[2]), 0 ); } } // todo: add COMBO and COMBOMODE from DWI CRS files? -aj - else if( 0 == stricmp(sValueName, "MODS") ) + else if( sValueName.EqualsNoCase("MODS") ) { Attack attack; float end = -9999; @@ -156,7 +156,7 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou } } - else if( 0 == stricmp(sValueName, "SONG") ) + else if( sValueName.EqualsNoCase("SONG") ) { CourseEntry new_entry; @@ -167,28 +167,28 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou // most played if( sParams[1].Left(strlen("BEST")) == "BEST" ) { - new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1; + new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("BEST")) ) - 1; CLAMP( new_entry.iChooseIndex, 0, 500 ); new_entry.songSort = SongSort_MostPlays; } // least played else if( sParams[1].Left(strlen("WORST")) == "WORST" ) { - new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("WORST")) ) - 1; + new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("WORST")) ) - 1; CLAMP( new_entry.iChooseIndex, 0, 500 ); new_entry.songSort = SongSort_FewestPlays; } // best grades else if( sParams[1].Left(strlen("GRADEBEST")) == "GRADEBEST" ) { - new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("GRADEBEST")) ) - 1; + new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("GRADEBEST")) ) - 1; CLAMP( new_entry.iChooseIndex, 0, 500 ); new_entry.songSort = SongSort_TopGrades; } // worst grades else if( sParams[1].Left(strlen("GRADEWORST")) == "GRADEWORST" ) { - new_entry.iChooseIndex = atoi( sParams[1].Right(sParams[1].size()-strlen("GRADEWORST")) ) - 1; + new_entry.iChooseIndex = StringToInt( sParams[1].Right(sParams[1].size()-strlen("GRADEWORST")) ) - 1; CLAMP( new_entry.iChooseIndex, 0, 500 ); new_entry.songSort = SongSort_LowestGrades; } @@ -284,7 +284,7 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou else if( !sMod.CompareNoCase("nodifficult") ) new_entry.bNoDifficult = true; else if( sMod.length() > 5 && !sMod.Left(5).CompareNoCase("award") ) - new_entry.iGainLives = atoi( sMod.substr(5).c_str() ); + new_entry.iGainLives = StringToInt( sMod.substr(5) ); else continue; mods.erase( mods.begin() + j ); @@ -298,22 +298,22 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou out.m_vEntries.push_back( new_entry ); } - else if( !stricmp(sValueName, "DISPLAYCOURSE") || !stricmp(sValueName, "COMBO") || - !stricmp(sValueName, "COMBOMODE") ) + else if( !sValueName.EqualsNoCase("DISPLAYCOURSE") || !sValueName.EqualsNoCase("COMBO") || + !sValueName.EqualsNoCase("COMBOMODE") ) { // Ignore } - else if( bFromCache && !stricmp(sValueName, "RADAR") ) + else if( bFromCache && !sValueName.EqualsNoCase("RADAR") ) { - StepsType st = (StepsType) atoi(sParams[1]); - CourseDifficulty cd = (CourseDifficulty) atoi( sParams[2] ); + StepsType st = (StepsType) StringToInt(sParams[1]); + CourseDifficulty cd = (CourseDifficulty) StringToInt( sParams[2] ); RadarValues rv; rv.FromString( sParams[3] ); out.m_RadarCache[Course::CacheEntry(st, cd)] = rv; } - else if( 0 == stricmp(sValueName, "STYLE") ) + else if( sValueName.EqualsNoCase("STYLE") ) { RString sStyles = sParams[1]; vector asStyles; 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/FileDownload.cpp b/src/FileDownload.cpp index 3f725895b3..80422684c2 100644 --- a/src/FileDownload.cpp +++ b/src/FileDownload.cpp @@ -272,14 +272,14 @@ void FileTransfer::HTTPUpdate() m_sResponseName = "Malformed response."; return; } - m_iResponseCode = atoi(m_sBUFFER.substr(i+1,j-i).c_str()); + m_iResponseCode = StringToInt(m_sBUFFER.substr(i+1,j-i)); m_sResponseName = m_sBUFFER.substr( j+1, k-j ); i = m_sBUFFER.find("Content-Length:"); j = m_sBUFFER.find("\n", i+1 ); if( i != string::npos ) - m_iTotalBytes = atoi(m_sBUFFER.substr(i+16,j-i).c_str()); + m_iTotalBytes = StringToInt(m_sBUFFER.substr(i+16,j-i)); else m_iTotalBytes = -1; // We don't know, so go until disconnect @@ -350,7 +350,7 @@ bool FileTransfer::ParseHTTPAddress( const RString &URL, RString &sProto, RStrin sServer = asMatches[1]; if( asMatches[3] != "" ) { - iPort = atoi(asMatches[3]); + iPort = StringToInt(asMatches[3]); if( iPort == 0 ) return false; } diff --git a/src/Font.cpp b/src/Font.cpp index a012906df5..406aaf7d9f 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -414,7 +414,7 @@ void Font::LoadFontPageSettings( FontPageSettings &cfg, IniFile &ini, const RStr // If val is an integer, it's a width, eg. "10=27". if( IsAnInt(sName) ) { - cfg.m_mapGlyphWidths[atoi(sName)] = pValue->GetValue(); + cfg.m_mapGlyphWidths[StringToInt(sName)] = pValue->GetValue(); continue; } @@ -516,7 +516,7 @@ void Font::LoadFontPageSettings( FontPageSettings &cfg, IniFile &ini, const RStr TrimLeft( sRowStr ); ASSERT( IsAnInt(sRowStr) ); - const int iRow = atoi( sRowStr.c_str() ); + const int iRow = StringToInt( sRowStr ); const int iFirstFrame = iRow * iNumFramesWide; if( iRow > iNumFramesHigh ) diff --git a/src/GameCommand.cpp b/src/GameCommand.cpp index 2341aaa088..0f6ac72104 100644 --- a/src/GameCommand.cpp +++ b/src/GameCommand.cpp @@ -335,12 +335,12 @@ void GameCommand::LoadOne( const Command& cmd ) else if( sName == "weight" ) { - m_iWeightPounds = atoi( sValue ); + m_iWeightPounds = StringToInt( sValue ); } else if( sName == "goalcalories" ) { - m_iGoalCalories = atoi( sValue ); + m_iGoalCalories = StringToInt( sValue ); } else if( sName == "goaltype" ) diff --git a/src/GameConstantsAndTypes.h b/src/GameConstantsAndTypes.h index 92b19f1fc7..7242f4009d 100644 --- a/src/GameConstantsAndTypes.h +++ b/src/GameConstantsAndTypes.h @@ -87,8 +87,10 @@ enum StepsType StepsType_para_single, StepsType_ds3ddx_single, StepsType_beat_single5, + StepsType_beat_versus5, StepsType_beat_double5, StepsType_beat_single7, + StepsType_beat_versus7, StepsType_beat_double7, StepsType_maniax_single, StepsType_maniax_double, diff --git a/src/GameManager.cpp b/src/GameManager.cpp index 6a7043d278..8709814bdd 100644 --- a/src/GameManager.cpp +++ b/src/GameManager.cpp @@ -1584,7 +1584,47 @@ static const Style g_Style_Beat_Single5 = false, // m_bLockDifficulties }; -static const Style g_Style_Beat_Double = +static const Style g_Style_Beat_Versus5 = +{ // STYLE_BEAT_VERSUS + true, // m_bUsedForGameplay + false, // m_bUsedForEdit + true, // m_bUsedForDemonstration + false, // m_bUsedForHowToPlay + "versus", // m_szName + StepsType_beat_versus5, // m_StepsType + StyleType_TwoPlayersTwoSides, // m_StyleType + 6, // m_iColsPerPlayer + { // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER]; + { // PLAYER_1 + { TRACK_1, -BEAT_COL_SPACING*2.5f, NULL }, + { TRACK_2, -BEAT_COL_SPACING*1.5f, NULL }, + { TRACK_3, -BEAT_COL_SPACING*0.5f, NULL }, + { TRACK_4, +BEAT_COL_SPACING*0.5f, NULL }, + { TRACK_5, +BEAT_COL_SPACING*1.5f, NULL }, + { TRACK_6, +BEAT_COL_SPACING*3.0f, "scratch" }, + }, + { // PLAYER_2 + { TRACK_1, -BEAT_COL_SPACING*2.5f, NULL }, + { TRACK_2, -BEAT_COL_SPACING*1.5f, NULL }, + { TRACK_3, -BEAT_COL_SPACING*0.5f, NULL }, + { TRACK_4, +BEAT_COL_SPACING*0.5f, NULL }, + { TRACK_5, +BEAT_COL_SPACING*1.5f, NULL }, + { TRACK_6, +BEAT_COL_SPACING*3.0f, "scratch" }, + }, + }, + { // m_iInputColumn[NUM_GameController][NUM_GameButton] + { 0, 1, 2, 3, 4, Style::NO_MAPPING, Style::NO_MAPPING, 5, 5, Style::END_MAPPING }, + { 0, 1, 2, 3, 4, Style::NO_MAPPING, Style::NO_MAPPING, 5, 5, Style::END_MAPPING } + }, + { // m_iColumnDrawOrder[MAX_COLS_PER_PLAYER]; + 0,1,2,3,4,5 + }, + false, // m_bNeedsZoomOutWith2Players + false, // m_bCanUseBeginnerHelper + false, // m_bLockDifficulties +}; + +static const Style g_Style_Beat_Double5 = { // STYLE_BEAT_DOUBLE true, // m_bUsedForGameplay true, // m_bUsedForEdit @@ -1680,6 +1720,51 @@ static const Style g_Style_Beat_Single7 = false, // m_bLockDifficulties }; +static const Style g_Style_Beat_Versus7 = +{ // STYLE_BEAT_VERSUS7 + true, // m_bUsedForGameplay + true, // m_bUsedForEdit + false, // m_bUsedForDemonstration + false, // m_bUsedForHowToPlay + "single7", // m_szName + StepsType_beat_versus7, // m_StepsType + StyleType_TwoPlayersTwoSides, // m_StyleType + 8, // m_iColsPerPlayer + { // m_ColumnInfo[NUM_PLAYERS][MAX_COLS_PER_PLAYER]; + { // PLAYER_1 + { TRACK_8, -BEAT_COL_SPACING*3.5f, "scratch" }, + { TRACK_1, -BEAT_COL_SPACING*2.0f, NULL }, + { TRACK_2, -BEAT_COL_SPACING*1.0f, NULL }, + { TRACK_3, -BEAT_COL_SPACING*0.0f, NULL }, + { TRACK_4, +BEAT_COL_SPACING*1.0f, NULL }, + { TRACK_5, +BEAT_COL_SPACING*2.0f, NULL }, + { TRACK_6, +BEAT_COL_SPACING*3.0f, NULL }, + { TRACK_7, +BEAT_COL_SPACING*4.0f, NULL }, + }, + { // PLAYER_2 + { TRACK_1, -BEAT_COL_SPACING*3.5f, NULL }, + { TRACK_2, -BEAT_COL_SPACING*2.5f, NULL }, + { TRACK_3, -BEAT_COL_SPACING*1.5f, NULL }, + { TRACK_4, -BEAT_COL_SPACING*0.5f, NULL }, + { TRACK_5, +BEAT_COL_SPACING*0.5f, NULL }, + { TRACK_6, +BEAT_COL_SPACING*1.5f, NULL }, + { TRACK_7, +BEAT_COL_SPACING*2.5f, NULL }, + { TRACK_8, +BEAT_COL_SPACING*4.0f, "scratch" }, + }, + }, + { // m_iInputColumn[NUM_GameController][NUM_GameButton] + { 1, 2, 3, 4, 5, 6, 7, 0, 0, Style::END_MAPPING }, + { 0, 1, 2, 3, 4, 5, 6, 7, 7, Style::END_MAPPING }, + }, + { // m_iColumnDrawOrder[MAX_COLS_PER_PLAYER]; + 0,1,2,3,4,5,6,7 + }, + false, // m_bNeedsZoomOutWith2Players + false, // m_bCanUseBeginnerHelper + false, // m_bLockDifficulties +}; + + static const Style g_Style_Beat_Double7 = { // STYLE_BEAT_DOUBLE7 true, // m_bUsedForGameplay @@ -1743,8 +1828,10 @@ static const Style g_Style_Beat_Double7 = static const Style *g_apGame_Beat_Styles[] = { &g_Style_Beat_Single5, - &g_Style_Beat_Double, + &g_Style_Beat_Versus5, + &g_Style_Beat_Double5, &g_Style_Beat_Single7, + &g_Style_Beat_Versus7, &g_Style_Beat_Double7, NULL }; 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/GameState.cpp b/src/GameState.cpp index fd8f2c487b..242ccaee70 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -209,7 +209,7 @@ void GameState::ApplyCmdline() RString sPlayer; for( int i = 0; GetCommandlineArgument( "player", &sPlayer, i ); ++i ) { - int pn = atoi( sPlayer )-1; + int pn = StringToInt( sPlayer )-1; if( !IsAnInt( sPlayer ) || pn < 0 || pn >= NUM_PLAYERS ) RageException::Throw( "Invalid argument \"--player=%s\".", sPlayer.c_str() ); diff --git a/src/InputMapper.cpp b/src/InputMapper.cpp index 90e65ae879..6707cf8408 100644 --- a/src/InputMapper.cpp +++ b/src/InputMapper.cpp @@ -948,7 +948,7 @@ MultiPlayer InputMapper::InputDeviceToMultiPlayer( InputDevice id ) GameButton InputScheme::ButtonNameToIndex( const RString &sButtonName ) const { for( GameButton gb=(GameButton) 0; gb + 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/LuaManager.cpp b/src/LuaManager.cpp index e61352d9c0..97ab18b552 100644 --- a/src/LuaManager.cpp +++ b/src/LuaManager.cpp @@ -11,7 +11,7 @@ #include "Command.h" #include "RageTypes.h" -#include +#include // conversion for lua functions. #include #include #include diff --git a/src/LyricsLoader.cpp b/src/LyricsLoader.cpp index 9ba0e1ade9..9909416cd8 100644 --- a/src/LyricsLoader.cpp +++ b/src/LyricsLoader.cpp @@ -63,7 +63,7 @@ bool LyricsLoader::LoadFromLRCFile(const RString& sPath, Song& out) StripCrnl(sValueData); // handle the data - if( 0==stricmp(sValueName,"COLOUR") || 0==stricmp(sValueName,"COLOR") ) + if( sValueName.EqualsNoCase("COLOUR") || sValueName.EqualsNoCase("COLOR") ) { // set color var here for this segment int r, g, b; 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/ModIcon.cpp b/src/ModIcon.cpp index dcbbf310b8..0ef95daeb4 100644 --- a/src/ModIcon.cpp +++ b/src/ModIcon.cpp @@ -63,7 +63,7 @@ void ModIcon::Set( const RString &_sText ) }; for( unsigned i=0; i asSkinNames; GetAllNoteSkinNamesForGame( GAMESTATE->m_pCurGame, asSkinNames ); for( unsigned i=0; isecond; int totalPairs = nData.size() / 2; if( iBMSTrackNo != BMS_TRACK_TIME_SIG && iBMSTrackNo != 7 ) @@ -423,10 +423,10 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t // this is step or offset data. Looks like "#00705" const RString &sData = it->second; - int iMeasureNo = atoi( sName.substr(1, 3).c_str() ); + int iMeasureNo = StringToInt( sName.substr(1, 3) ); if( iMeasureNo < iStartMeasureNo ) continue; - int iBMSTrackNo = atoi( sName.substr(4, 2).c_str() ); + int iBMSTrackNo = StringToInt( sName.substr(4, 2) ); if( iBMSTrackNo == BMS_TRACK_TIME_SIG ) out[iMeasureNo] = StringToFloat( sData ); } @@ -558,9 +558,9 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo int iPlayer = -1; RString sData; if( GetTagFromMap( mapNameToData, "#player", sData ) ) - iPlayer = atoi(sData); + iPlayer = StringToInt(sData); if( GetTagFromMap( mapNameToData, "#playlevel", sData ) ) - out.SetMeter( atoi(sData) ); + out.SetMeter( StringToInt(sData) ); NoteData ndNotes; ndNotes.SetNumTracks( NUM_BMS_TRACKS ); @@ -697,8 +697,8 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo continue; // this is step or offset data. Looks like "#00705" - int iMeasureNo = atoi( sName.substr(1,3).c_str() ); - int iRawTrackNum = atoi( sName.substr(4,2).c_str() ); + int iMeasureNo = StringToInt( sName.substr(1,3) ); + int iRawTrackNum = StringToInt( sName.substr(4,2) ); int iRowNo = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustments ); float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustments ); const RString &sNoteData = it->second; diff --git a/src/NotesLoaderDWI.cpp b/src/NotesLoaderDWI.cpp index 92eb7c27f0..10f9c69f55 100644 --- a/src/NotesLoaderDWI.cpp +++ b/src/NotesLoaderDWI.cpp @@ -206,7 +206,7 @@ static bool LoadFromDWITokens( DEFAULT_FAIL( out.m_StepsType ); } - int iNumFeet = atoi(sNumFeet); + int iNumFeet = StringToInt(sNumFeet); // out.SetDescription(sDescription); // Don't put garbage in the description. out.SetMeter(iNumFeet); out.SetDifficulty( DwiCompatibleStringToDifficulty(sDescription) ); @@ -447,10 +447,10 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla } // handle the data - if( 0==stricmp(sValueName,"FILE") ) + if( sValueName.EqualsNoCase("FILE") ) out.m_sMusicFile = sParams[1]; - else if( 0==stricmp(sValueName,"TITLE") ) + else if( sValueName.EqualsNoCase("TITLE") ) { NotesLoader::GetMainAndSubTitlesFromFullTitle( sParams[1], out.m_sMainTitle, out.m_sSubTitle ); @@ -460,22 +460,22 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla ConvertString( out.m_sSubTitle, "utf-8,english" ); } - else if( 0==stricmp(sValueName,"ARTIST") ) + else if( sValueName.EqualsNoCase("ARTIST") ) { out.m_sArtist = sParams[1]; ConvertString( out.m_sArtist, "utf-8,english" ); } - else if( 0==stricmp(sValueName,"GENRE") ) + else if( sValueName.EqualsNoCase("GENRE") ) { out.m_sGenre = sParams[1]; ConvertString( out.m_sGenre, "utf-8,english" ); } - else if( 0==stricmp(sValueName,"CDTITLE") ) + else if( sValueName.EqualsNoCase("CDTITLE") ) out.m_sCDTitleFile = sParams[1]; - else if( 0==stricmp(sValueName,"BPM") ) + else if( sValueName.EqualsNoCase("BPM") ) { const float fBPM = StringToFloat( sParams[1] ); @@ -491,7 +491,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla NoteRowToBeat(0), fBPM ); } } - else if( 0==stricmp(sValueName,"DISPLAYBPM") ) + else if( sValueName.EqualsNoCase("DISPLAYBPM") ) { // #DISPLAYBPM:[xxx..xxx]|[xxx]|[*]; int iMin, iMax; @@ -515,17 +515,17 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla } } - else if( 0==stricmp(sValueName,"GAP") ) + else if( sValueName.EqualsNoCase("GAP") ) // the units of GAP is 1/1000 second - out.m_SongTiming.m_fBeat0OffsetInSeconds = -atoi( sParams[1] ) / 1000.0f; + out.m_SongTiming.m_fBeat0OffsetInSeconds = -StringToInt( sParams[1] ) / 1000.0f; - else if( 0==stricmp(sValueName,"SAMPLESTART") ) + else if( sValueName.EqualsNoCase("SAMPLESTART") ) out.m_fMusicSampleStartSeconds = ParseBrokenDWITimestamp(sParams[1], sParams[2], sParams[3]); - else if( 0==stricmp(sValueName,"SAMPLELENGTH") ) + else if( sValueName.EqualsNoCase("SAMPLELENGTH") ) out.m_fMusicSampleLengthSeconds = ParseBrokenDWITimestamp(sParams[1], sParams[2], sParams[3]); - else if( 0==stricmp(sValueName,"FREEZE") ) + else if( sValueName.EqualsNoCase("FREEZE") ) { vector arrayFreezeExpressions; split( sParams[1], ",", arrayFreezeExpressions ); @@ -547,7 +547,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla } } - else if( 0==stricmp(sValueName,"CHANGEBPM") || 0==stricmp(sValueName,"BPMCHANGE") ) + else if( sValueName.EqualsNoCase("CHANGEBPM") || sValueName.EqualsNoCase("BPMCHANGE") ) { vector arrayBPMChangeExpressions; split( sParams[1], ",", arrayBPMChangeExpressions ); @@ -577,10 +577,10 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla } } - else if( 0==stricmp(sValueName,"SINGLE") || - 0==stricmp(sValueName,"DOUBLE") || - 0==stricmp(sValueName,"COUPLE") || - 0==stricmp(sValueName,"SOLO") ) + else if( sValueName.EqualsNoCase("SINGLE") || + sValueName.EqualsNoCase("DOUBLE") || + sValueName.EqualsNoCase("COUPLE") || + sValueName.EqualsNoCase("SOLO") ) { Steps* pNewNotes = out.CreateSteps(); LoadFromDWITokens( @@ -597,8 +597,8 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla else delete pNewNotes; } - else if( 0==stricmp(sValueName,"DISPLAYTITLE") || - 0==stricmp(sValueName,"DISPLAYARTIST") ) + else if( sValueName.EqualsNoCase("DISPLAYTITLE") || + sValueName.EqualsNoCase("DISPLAYARTIST") ) { /* We don't want to support these tags. However, we don't want * to pick up images used here as song images (eg. banners). */ diff --git a/src/NotesLoaderJson.cpp b/src/NotesLoaderJson.cpp new file mode 100644 index 0000000000..a70e281756 --- /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( sSelectable.EqualsNoCase("YES") ) + out.m_SelectionDisplay = out.SHOW_ALWAYS; + else if( sSelectable.EqualsNoCase("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/NotesLoaderKSF.cpp b/src/NotesLoaderKSF.cpp index 42abc7032c..862c4bd485 100644 --- a/src/NotesLoaderKSF.cpp +++ b/src/NotesLoaderKSF.cpp @@ -9,6 +9,51 @@ #include "Song.h" #include "Steps.h" +static void HandleBunki( TimingData &timing, const float fEarlyBPM, + const float fCurBPM, const float fGap, + const float fPos ) +{ + const float BeatsPerSecond = fEarlyBPM / 60.0f; + const float beat = (fPos + fGap) * BeatsPerSecond; + LOG->Trace( "BPM %f, BPS %f, BPMPos %f, beat %f", + fEarlyBPM, BeatsPerSecond, fPos, beat ); + timing.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), fCurBPM) ); +} + +static bool HandlePipeChars( TimingData &timing, const RString sNoteRow, + const float fCurBeat, int &iTickCount ) +{ + RString temp = sNoteRow.substr(2,sNoteRow.size()-3); + float numTemp = StringToFloat(temp); + if (BeginsWith(sNoteRow, "|T")) + { + iTickCount = static_cast(numTemp); + timing.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) ); + return true; + } + else if (BeginsWith(sNoteRow, "|B")) + { + timing.SetBPMAtBeat( fCurBeat, numTemp ); + return true; + } + else if (BeginsWith(sNoteRow, "|E")) + { + // Finally! the |E| tag is working as it should. I can die happy now -DaisuMaster + float fCurDelay = 60 / timing.GetBPMAtBeat(fCurBeat) * numTemp / iTickCount; + fCurDelay += timing.GetDelayAtRow(BeatToNoteRow(fCurBeat) ); + timing.SetStopAtBeat( fCurBeat, fCurDelay, true ); + return true; + } + else if (BeginsWith(sNoteRow, "|D")) + { + float fCurDelay = timing.GetStopAtRow(BeatToNoteRow(fCurBeat) ); + fCurDelay += numTemp / 1000; + timing.SetStopAtBeat( fCurBeat, fCurDelay, true ); + return true; + } + return false; +} + static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, bool bKIUCompliant ) { LOG->Trace( "Steps::LoadFromKSFFile( '%s' )", sPath.c_str() ); @@ -25,6 +70,9 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, // According to Aldo_MX, there is a default BPM and it's 60. -aj bool bDoublesChart = false; + + TimingData stepsTiming; + float SMGap1 = 0, SMGap2 = 0, BPM1 = -1, BPMPos2 = -1, BPM2 = -1, BPMPos3 = -1, BPM3 = -1; for( unsigned i=0; iUserLog( "Song file", sPath, "has an invalid tick count: %d.", iTickCount ); @@ -43,15 +170,10 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, } out.m_Timing.AddTickcountSegment(TickcountSegment(0, iTickCount)); } - else if( sValueName=="STEP" ) - { - RString theSteps = sParams[1]; - TrimLeft( theSteps ); - split( theSteps, "\n", vNoteRows, true ); - } + else if( sValueName=="DIFFICULTY" ) { - out.SetMeter( max(atoi(sParams[1]), 0) ); + out.SetMeter( max(StringToInt(sParams[1]), 0) ); } // new cases from Aldo_MX's fork: else if( sValueName=="PLAYER" ) @@ -61,13 +183,34 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, if( sPlayer.find( "double" ) != string::npos ) bDoublesChart = true; } + // This should always be last. + else if( sValueName=="STEP" ) + { + RString theSteps = sParams[1]; + TrimLeft( theSteps ); + split( theSteps, "\n", vNoteRows, true ); + } } if( iTickCount == -1 ) { - iTickCount = 2; // Direct Move 0.5 has a default value of 4... -aj + iTickCount = 4; LOG->UserLog( "Song file", sPath, "doesn't have a TICKCOUNT. Defaulting to %i.", iTickCount ); } + + // Prepare BPM stuff already if the file uses KSF syntax. + if( bKIUCompliant ) + { + if( BPM2 > 0 && BPMPos2 > 0 ) + { + HandleBunki( stepsTiming, BPM1, BPM2, SMGap1, BPMPos2 ); + } + + if( BPM3 > 0 && BPMPos3 > 0 ) + { + HandleBunki( stepsTiming, BPM2, BPM3, SMGap2, BPMPos3 ); + } + } NoteData notedata; // read it into here @@ -148,6 +291,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, int newTick = -1; float fCurBeat = 0.0f; float prevBeat = 0.0f; // Used for hold tails. + for( unsigned r=0; rUserLog( "Song file", sPath, "has illegal syntax \"%s\" which can't be in KIU complient files.", - sRowString.c_str() ); - return false; - //In other words: you can't mix ksf's with DM05 tags and ksf's without any DM05 tags - //Either one set or another will be read... - }*/ - if( BeginsWith(sRowString, "|B") || BeginsWith(sRowString, "|D") || BeginsWith(sRowString, "|E") ) - { - // TODO: Update for Split Timing. - continue; - } - else if ( BeginsWith(sRowString, "|T") ) - { - RString temp = sRowString.substr(2,sRowString.size()-3); - newTick = atoi(temp); - bTickChangeNeeded = true; - out.m_Timing.AddTickcountSegment(TickcountSegment(fCurBeat, newTick)); + if (bKIUCompliant) + { + // Log an error, ignore the line. + continue; + } + if ( !HandlePipeChars( stepsTiming, sRowString, fCurBeat, iTickCount ) ) + { + // LOG it first. + } continue; + } + else { // Is this why improper ksf or some kiucompilant ksf mixed with dm05 ksf are ignored?? -DaisuMaster @@ -273,6 +408,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, } out.SetNoteData( notedata ); + out.m_Timing = stepsTiming; out.TidyUpData(); @@ -289,12 +425,12 @@ static void LoadTags( const RString &str, Song &out ) split( str, " - ", asBits, false ); // Ignore the difficulty, since we get that elsewhere. if( asBits.size() == 3 && ( - !stricmp(asBits[2], "double") || - !stricmp(asBits[2], "easy") || - !stricmp(asBits[2], "normal") || - !stricmp(asBits[2], "hard") || - !stricmp(asBits[2], "crazy") || - !stricmp(asBits[2], "nightmare")) + asBits[2].EqualsNoCase("double") || + asBits[2].EqualsNoCase("easy") || + asBits[2].EqualsNoCase("normal") || + asBits[2].EqualsNoCase("hard") || + asBits[2].EqualsNoCase("crazy") || + asBits[2].EqualsNoCase("nightmare")) ) { asBits.erase( asBits.begin()+2, asBits.begin()+3 ); @@ -404,7 +540,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant /* 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 = atoi( sParams[1] ); + iTickCount = StringToInt( sParams[1] ); iTickCount = iTickCount > 0 ? iTickCount : 2; // again, Direct Move uses 4 as a default. // add a tickcount for those using the [Player] // CheckpointsUseTimeSignatures metric. -aj @@ -468,21 +604,12 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant { if( BPM2 > 0 && BPMPos2 > 0 ) { - const float BeatsPerSecond = BPM1 / 60.0f; - const float beat = (BPMPos2 + SMGap1) * BeatsPerSecond; - LOG->Trace( "BPM %f, BPS %f, BPMPos2 %f, beat %f", - BPM1, BeatsPerSecond, BPMPos2, beat ); - out.m_SongTiming.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), BPM2) ); + HandleBunki( out.m_SongTiming, BPM1, BPM2, SMGap1, BPMPos2 ); } if( BPM3 > 0 && BPMPos3 > 0 ) { - const float BeatsPerSecond = BPM2 / 60.0f; - //The line below isn't perfect, but works better than previous versions. - const float beat = (BPMPos3 + SMGap2) * BeatsPerSecond; - LOG->Trace( "BPM %f, BPS %f, BPMPos3 %f, beat %f", - BPM2, BeatsPerSecond, BPMPos3, beat ); - out.m_SongTiming.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), BPM3) ); + HandleBunki( out.m_SongTiming, BPM2, BPM3, SMGap2, BPMPos3 ); } } else @@ -510,40 +637,11 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant BeginsWith(NoteRowString, "|D") || BeginsWith(NoteRowString, "|E") ) { bDMRequired = true; - RString temp = NoteRowString.substr(2,NoteRowString.size()-3); - float numTemp = StringToFloat(temp); - if (BeginsWith(NoteRowString, "|T")) + if ( !HandlePipeChars( out.m_SongTiming, NoteRowString, fCurBeat, iTickCount ) ) { - iTickCount = (int)numTemp; - TickcountSegment tcs; - tcs.m_iStartRow = BeatToNoteRow(fCurBeat); - tcs.m_iTicks = iTickCount > ROWS_PER_BEAT ? ROWS_PER_BEAT : iTickCount; - out.m_SongTiming.AddTickcountSegment( tcs ); - - continue; - } - else if (BeginsWith(NoteRowString, "|B")) - { - float fCurBpm = (float)numTemp; - //out.m_Timing.AddBPMSegment( BPMSegment( BeatToNoteRow(fCurBeat), (float)numTemp ) ); - out.m_SongTiming.SetBPMAtBeat( fCurBeat, fCurBpm ); - continue; - } - else if (BeginsWith(NoteRowString, "|E")) - { - // Finally! the |E| tag is working as it should. I can die happy now -DaisuMaster - float fCurDelay = 60 / out.m_SongTiming.GetBPMAtBeat(fCurBeat) * (float)numTemp / iTickCount; - fCurDelay += out.m_SongTiming.GetStopAtRow(BeatToNoteRow(fCurBeat) ); - out.m_SongTiming.SetStopAtBeat( fCurBeat, fCurDelay, true ); - continue; - } - else if (BeginsWith(NoteRowString, "|D")) - { - float fCurDelay = out.m_SongTiming.GetStopAtRow(BeatToNoteRow(fCurBeat) ); - fCurDelay += (float)numTemp / 1000; - out.m_SongTiming.SetStopAtBeat( fCurBeat, fCurDelay, true ); - continue; + // LOG it first. } + continue; } else { @@ -583,16 +681,17 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out ) ASSERT( arrayKSFFileNames.size() ); bool bKIUCompliant = false; - /* If only the first file is read, it will cause problems for other simfiles with - * different BPM changes and tickcounts. This command will probably have to be - * changed in the future. */ + /* With Split Timing, there has to be a backup Song Timing in case + * anything goes wrong. Use the first file found to determine said + * timing, while also establishing whether this file respects the + * Kick It Up syntax. */ if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[0], out, bKIUCompliant) ) return false; // load the Steps from the rest of the KSF files for( unsigned i=0; isecond; - int iMeasureNo = atoi( sName.substr(1, 3).c_str() ); - int iPMSTrackNo = atoi( sName.substr(4, 2).c_str() ); + int iMeasureNo = StringToInt( sName.substr(1, 3) ); + int iPMSTrackNo = StringToInt( sName.substr(4, 2) ); if( iPMSTrackNo == PMS_TRACK_TIME_SIG ) out[iMeasureNo] = StringToFloat( sData ); } @@ -367,9 +367,9 @@ static bool LoadFromPMSFile( const RString &sPath, const NameToData_t &mapNameTo int iPlayer = -1; RString sData; if( GetTagFromMap( mapNameToData, "#player", sData ) ) - iPlayer = atoi(sData); + iPlayer = StringToInt(sData); if( GetTagFromMap( mapNameToData, "#playlevel", sData ) ) - out.SetMeter( atoi(sData) ); + out.SetMeter( StringToInt(sData) ); NoteData ndNotes; ndNotes.SetNumTracks( NUM_PMS_TRACKS ); @@ -396,8 +396,8 @@ static bool LoadFromPMSFile( const RString &sPath, const NameToData_t &mapNameTo continue; // this is step or offset data. Looks like "#00705" - int iMeasureNo = atoi( sName.substr(1,3).c_str() ); - int iRawTrackNum = atoi( sName.substr(4,2).c_str() ); + int iMeasureNo = StringToInt( sName.substr(1,3) ); + int iRawTrackNum = StringToInt( sName.substr(4,2) ); int iRowNo = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustments ); float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustments ); const RString &sNoteData = it->second; @@ -649,8 +649,8 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur if( sName.size() != 6 || sName[0] != '#' || !IsAnInt( sName.substr(1,5) ) ) continue; // this is step or offset data. Looks like "#00705" - int iMeasureNo = atoi( sName.substr(1, 3).c_str() ); - int iPMSTrackNo = atoi( sName.substr(4, 2).c_str() ); + int iMeasureNo = StringToInt( sName.substr(1, 3) ); + int iPMSTrackNo = StringToInt( sName.substr(4, 2) ); int iStepIndex = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut ); float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut ); int iRowsPerMeasure = BeatToNoteRow( fBeatsPerMeasure ); diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index a8633ba3d8..280063f1b6 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -60,7 +60,7 @@ void SMLoader::LoadFromSMTokens( out.SetDifficulty( Difficulty_Challenge ); } - out.SetMeter( atoi(sMeter) ); + out.SetMeter( StringToInt(sMeter) ); vector saValues; split( sRadarValues, ",", saValues, true ); int categories = NUM_RadarCategory - 1; // Fakes aren't counted in the radar values. @@ -426,7 +426,7 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString // Backward compatibility: if( change.m_def.m_sEffect.empty() ) { - bool bLoop = atoi( aBGChangeValues[5] ) != 0; + bool bLoop = StringToInt( aBGChangeValues[5] ) != 0; if( !bLoop ) change.m_def.m_sEffect = SBE_StretchNoLoop; } @@ -436,7 +436,7 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString // Backward compatibility: if( change.m_def.m_sEffect.empty() ) { - bool bRewindMovie = atoi( aBGChangeValues[4] ) != 0; + bool bRewindMovie = StringToInt( aBGChangeValues[4] ) != 0; if( bRewindMovie ) change.m_def.m_sEffect = SBE_StretchRewind; } @@ -445,7 +445,7 @@ bool SMLoader::LoadFromBGChangesString( BackgroundChange &change, const RString // param 9 overrides this. // Backward compatibility: if( change.m_sTransition.empty() ) - change.m_sTransition = (atoi( aBGChangeValues[3] ) != 0) ? "CrossFade" : ""; + change.m_sTransition = (StringToInt( aBGChangeValues[3] ) != 0) ? "CrossFade" : ""; // fall through case 3: change.m_fRate = StringToFloat( aBGChangeValues[2] ); @@ -575,12 +575,12 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache else if( sValueName=="HASMUSIC" ) { if( bFromCache ) - out.m_bHasMusic = atoi( sParams[1] ) != 0; + out.m_bHasMusic = StringToInt( sParams[1] ) != 0; } else if( sValueName=="HASBANNER" ) { if( bFromCache ) - out.m_bHasBanner = atoi( sParams[1] ) != 0; + out.m_bHasBanner = StringToInt( sParams[1] ) != 0; } else if( sValueName=="SAMPLESTART" ) @@ -611,20 +611,20 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out, bool bFromCache else if( sValueName=="SELECTABLE" ) { - if(!stricmp(sParams[1],"YES")) + if(sParams[1].EqualsNoCase("YES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; - else if(!stricmp(sParams[1],"NO")) + else if(sParams[1].EqualsNoCase("NO")) out.m_SelectionDisplay = out.SHOW_NEVER; // ROULETTE from 3.9. It was removed since UnlockManager can serve // the same purpose somehow. This, of course, assumes you're using // unlocks. -aj - else if(!stricmp(sParams[1],"ROULETTE")) + else if(sParams[1].EqualsNoCase("ROULETTE")) out.m_SelectionDisplay = out.SHOW_ALWAYS; /* The following two cases are just fixes to make sure simfiles that * used 3.9+ features are not excluded here */ - else if(!stricmp(sParams[1],"ES") || !stricmp(sParams[1],"OMES")) + else if(sParams[1].EqualsNoCase("ES") || sParams[1].EqualsNoCase("OMES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; - else if( atoi(sParams[1]) > 0 ) + else if( StringToInt(sParams[1]) > 0 ) out.m_SelectionDisplay = out.SHOW_ALWAYS; else LOG->UserLog( "Song file", sPath, "has an unknown #SELECTABLE value, \"%s\"; ignored.", sParams[1].c_str() ); diff --git a/src/NotesLoaderSMA.cpp b/src/NotesLoaderSMA.cpp index 1fa083136d..5cec7ea843 100644 --- a/src/NotesLoaderSMA.cpp +++ b/src/NotesLoaderSMA.cpp @@ -59,7 +59,7 @@ void SMALoader::LoadFromSMATokens( out.SetDifficulty( Difficulty_Challenge ); } - out.SetMeter( atoi(sMeter) ); + out.SetMeter( StringToInt(sMeter) ); vector saValues; split( sRadarValues, ",", saValues, true ); int categories = NUM_RadarCategory - 1; // Fakes aren't counted in the radar values. @@ -244,20 +244,20 @@ bool SMALoader::LoadFromSMAFile( const RString &sPath, Song &out ) else if( sValueName=="SELECTABLE" ) { - if(!stricmp(sParams[1],"YES")) + if(sParams[1].EqualsNoCase("YES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; - else if(!stricmp(sParams[1],"NO")) + else if(sParams[1].EqualsNoCase("NO")) out.m_SelectionDisplay = out.SHOW_NEVER; // ROULETTE from 3.9. It was removed since UnlockManager can serve // the same purpose somehow. This, of course, assumes you're using // unlocks. -aj - else if(!stricmp(sParams[1],"ROULETTE")) + else if(sParams[1].EqualsNoCase("ROULETTE")) out.m_SelectionDisplay = out.SHOW_ALWAYS; /* The following two cases are just fixes to make sure simfiles that * used 3.9+ features are not excluded here */ - else if(!stricmp(sParams[1],"ES") || !stricmp(sParams[1],"OMES")) + else if(sParams[1].EqualsNoCase("ES") || sParams[1].EqualsNoCase("OMES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; - else if( atoi(sParams[1]) > 0 ) + else if( StringToInt(sParams[1]) > 0 ) out.m_SelectionDisplay = out.SHOW_ALWAYS; else LOG->UserLog( "Song file", sPath, "has an unknown #SELECTABLE value, \"%s\"; ignored.", sParams[1].c_str() ); @@ -430,13 +430,13 @@ void SMALoader::LoadTimingFromSMAFile( const MsdFile &msd, TimingData &out ) break; } encountered = true; - rowsPerMeasure = atoi( sParams[1] ); + rowsPerMeasure = StringToInt( sParams[1] ); } else if( sValueName=="BEATSPERMEASURE" ) { TimeSignatureSegment new_seg; new_seg.m_iStartRow = 0; - new_seg.m_iNumerator = atoi( sParams[1] ); + new_seg.m_iNumerator = StringToInt( sParams[1] ); new_seg.m_iDenominator = 4; out.AddTimeSignatureSegment( new_seg ); } diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 59c7aed3ee..95496ba9a9 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -292,18 +292,18 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach else if( sValueName=="SELECTABLE" ) { - if(!stricmp(sParams[1],"YES")) + if(sParams[1].EqualsNoCase("YES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; - else if(!stricmp(sParams[1],"NO")) + else if(sParams[1].EqualsNoCase("NO")) out.m_SelectionDisplay = out.SHOW_NEVER; // ROULETTE from 3.9 is no longer in use. - else if(!stricmp(sParams[1],"ROULETTE")) + else if(sParams[1].EqualsNoCase("ROULETTE")) out.m_SelectionDisplay = out.SHOW_ALWAYS; /* The following two cases are just fixes to make sure simfiles that * used 3.9+ features are not excluded here */ - else if(!stricmp(sParams[1],"ES") || !stricmp(sParams[1],"OMES")) + else if(sParams[1].EqualsNoCase("ES") || sParams[1].EqualsNoCase("OMES")) out.m_SelectionDisplay = out.SHOW_ALWAYS; - else if( atoi(sParams[1]) > 0 ) + else if( StringToInt(sParams[1]) > 0 ) out.m_SelectionDisplay = out.SHOW_ALWAYS; else LOG->UserLog( "Song file", sPath, "has an unknown #SELECTABLE value, \"%s\"; ignored.", sParams[1].c_str() ); @@ -466,13 +466,13 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach else if( sValueName=="HASMUSIC" ) { if( bFromCache ) - out.m_bHasMusic = atoi( sParams[1] ) != 0; + out.m_bHasMusic = StringToInt( sParams[1] ) != 0; } else if( sValueName=="HASBANNER" ) { if( bFromCache ) - out.m_bHasBanner = atoi( sParams[1] ) != 0; + out.m_bHasBanner = StringToInt( sParams[1] ) != 0; } // This tag will get us to the next section. @@ -509,7 +509,7 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach else if( sValueName=="METER" ) { - pNewNotes->SetMeter( atoi( sParams[1] ) ); + pNewNotes->SetMeter( StringToInt( sParams[1] ) ); } else if( sValueName=="RADARVALUES" ) @@ -751,7 +751,7 @@ bool SSCLoader::LoadEditFromMsd( const MsdFile &msd, const RString &sEditFilePat else if( sValueName=="METER" ) { - pNewNotes->SetMeter( atoi( sParams[1] ) ); + pNewNotes->SetMeter( StringToInt( sParams[1] ) ); bSSCFormat = true; } 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/OptionRowHandler.cpp b/src/OptionRowHandler.cpp index 64314a9cd5..00125fa6b1 100644 --- a/src/OptionRowHandler.cpp +++ b/src/OptionRowHandler.cpp @@ -154,7 +154,7 @@ public: RageException::Throw( "Parse error in \"ScreenOptionsMaster::%s\".", sParam.c_str() ); m_Def.m_bOneChoiceForAllPlayers = false; - const int NumCols = atoi( lCmds.v[0].m_vsArgs[0] ); + const int NumCols = StringToInt( lCmds.v[0].m_vsArgs[0] ); for( unsigned i=1; i= 0 && pn < NUM_PLAYERS ); m_Def.m_vEnabledForPlayers.insert( pn ); } diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index 8d1a8199cd..036f1d314c 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -299,7 +299,7 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut { /* XXX We know what they want, is there any reason not to handle it? */ /* Yes. We should be strict in handling the format. -Chris */ - sErrorOut = ssprintf("Invalid player options \"%s\"; did you mean '*%d'?", s->c_str(), atoi(*s) ); + sErrorOut = ssprintf("Invalid player options \"%s\"; did you mean '*%d'?", s->c_str(), StringToInt(*s) ); return false; } else diff --git a/src/Profile.cpp b/src/Profile.cpp index 3cf480a415..bfca1207b2 100644 --- a/src/Profile.cpp +++ b/src/Profile.cpp @@ -1922,7 +1922,7 @@ RString Profile::MakeUniqueFileNameNoExtension( RString sDir, RString sFileNameB continue; ASSERT( matches.size() == 1 ); - iIndex = atoi( matches[0] )+1; + iIndex = StringToInt( matches[0] )+1; break; } diff --git a/src/ProfileManager.cpp b/src/ProfileManager.cpp index 041803238c..812db73a8c 100644 --- a/src/ProfileManager.cpp +++ b/src/ProfileManager.cpp @@ -429,7 +429,7 @@ bool ProfileManager::CreateLocalProfile( RString sName, RString &sProfileIDOut ) vector vs; GetLocalProfileIDs( vs ); FOREACH_CONST( RString, vs, s ) - iMaxProfileNumber = atoi( *s ); + iMaxProfileNumber = StringToInt( *s ); int iProfileNumber = iMaxProfileNumber + 1; RString sProfileID = ssprintf( "%08d", iProfileNumber ); diff --git a/src/RageBitmapTexture.cpp b/src/RageBitmapTexture.cpp index e6743722c2..aa03982af1 100644 --- a/src/RageBitmapTexture.cpp +++ b/src/RageBitmapTexture.cpp @@ -26,8 +26,8 @@ static void GetResolutionFromFileName( RString sPath, int &iWidth, int &iHeight if( !re.Compare(sPath, asMatches) ) return; - iWidth = atoi( asMatches[0].c_str() ); - iHeight = atoi( asMatches[1].c_str() ); + iWidth = StringToInt( asMatches[0] ); + iHeight = StringToInt( asMatches[1] ); } RageBitmapTexture::RageBitmapTexture( RageTextureID name ) : diff --git a/src/RageTexture.cpp b/src/RageTexture.cpp index f91b07beb6..b049ba159d 100644 --- a/src/RageTexture.cpp +++ b/src/RageTexture.cpp @@ -51,8 +51,8 @@ void RageTexture::GetFrameDimensionsFromFileName( RString sPath, int* piFramesWi *piFramesWide = *piFramesHigh = 1; return; } - *piFramesWide = atoi(asMatch[0]); - *piFramesHigh = atoi(asMatch[1]); + *piFramesWide = StringToInt(asMatch[0]); + *piFramesHigh = StringToInt(asMatch[1]); } const RectF *RageTexture::GetTextureCoordRect( int iFrameNo ) const diff --git a/src/RageUtil.cpp b/src/RageUtil.cpp index 8443b88f7d..4f068df600 100644 --- a/src/RageUtil.cpp +++ b/src/RageUtil.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -193,8 +194,8 @@ float HHMMSSToSeconds( const RString &sHHMMSS ) arrayBits.insert(arrayBits.begin(), "0" ); // pad missing bits float fSeconds = 0; - fSeconds += atoi( arrayBits[0] ) * 60 * 60; - fSeconds += atoi( arrayBits[1] ) * 60; + fSeconds += StringToInt( arrayBits[0] ) * 60 * 60; + fSeconds += StringToInt( arrayBits[1] ) * 60; fSeconds += StringToFloat( arrayBits[2] ); return fSeconds; @@ -1695,6 +1696,20 @@ void MakeLower( wchar_t *p, size_t iLen ) UnicodeUpperLower( p, iLen, g_LowerCase ); } +int StringToInt( const RString &sString ) +{ + int ret; + istringstream ( sString ) >> ret; + return ret; +} + +RString IntToString( const int &iNum ) +{ + stringstream ss; + ss << iNum; + return ss.str(); +} + float StringToFloat( const RString &sString ) { float ret = strtof( sString, NULL ); @@ -2141,7 +2156,7 @@ namespace StringConversion if( sValue.size() == 0 ) return false; - out = (atoi(sValue) != 0); + out = (StringToInt(sValue) != 0); return true; } diff --git a/src/RageUtil.h b/src/RageUtil.h index e9c2149207..17f4d24f0e 100644 --- a/src/RageUtil.h +++ b/src/RageUtil.h @@ -405,6 +405,16 @@ void MakeUpper( char *p, size_t iLen ); void MakeLower( char *p, size_t iLen ); void MakeUpper( wchar_t *p, size_t iLen ); void MakeLower( wchar_t *p, size_t iLen ); +/** + * @brief Have a standard way of converting Strings to integers. + * @param sString the string to convert. + * @return the integer we are after. */ +int StringToInt( const RString &sString ); +/** + * @brief Have a standard way of converting integers to Strings. + * @param iNum the integer to convert. + * @return the string we are after. */ +RString IntToString( const int &iNum ); float StringToFloat( const RString &sString ); bool StringToFloat( const RString &sString, float &fOut ); diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index ad76113341..5d1eb1d933 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -2623,9 +2623,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) } else if( SM == SM_BackFromDifficultyMeterChange ) { - int i; - std::istringstream ss( ScreenTextEntry::s_sLastAnswer ); - ss >> i; + int i = StringToInt( ScreenTextEntry::s_sLastAnswer ); GAMESTATE->m_pCurSteps[PLAYER_1]->SetMeter(i); SetDirty( true ); } @@ -2652,7 +2650,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) } else if( SM == SM_BackFromTimeSignatureNumeratorChange ) { - int iNum = atoi( ScreenTextEntry::s_sLastAnswer ); + int iNum = StringToInt( ScreenTextEntry::s_sLastAnswer ); if( iNum > 0 ) { m_pSteps->m_Timing.SetTimeSignatureNumeratorAtBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat, iNum ); @@ -2661,7 +2659,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) } else if ( SM == SM_BackFromTimeSignatureDenominatorChange ) { - int iDen = atoi( ScreenTextEntry::s_sLastAnswer ); + int iDen = StringToInt( ScreenTextEntry::s_sLastAnswer ); if( iDen > 0) { m_pSteps->m_Timing.SetTimeSignatureDenominatorAtBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat, iDen ); @@ -2670,7 +2668,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) } else if ( SM == SM_BackFromTickcountChange ) { - int iTick = atoi( ScreenTextEntry::s_sLastAnswer ); + int iTick = StringToInt( ScreenTextEntry::s_sLastAnswer ); if ( iTick >= 0 && iTick <= ROWS_PER_BEAT ) { m_pSteps->m_Timing.SetTickcountAtBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat, iTick ); @@ -2679,7 +2677,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) } else if ( SM == SM_BackFromComboChange ) { - int iCombo = atoi( ScreenTextEntry::s_sLastAnswer ); + int iCombo = StringToInt( ScreenTextEntry::s_sLastAnswer ); if ( iCombo >= 0 ) { m_pSteps->m_Timing.SetComboAtBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat, iCombo ); diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index ffea34a274..fd7600be36 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -2757,7 +2757,7 @@ void ScreenGameplay::SaveReplay() continue; ASSERT( matches.size() == 1 ); - iIndex = atoi( matches[0] )+1; + iIndex = StringToInt( matches[0] )+1; break; } diff --git a/src/ScreenMiniMenu.h b/src/ScreenMiniMenu.h index 365ac90a24..b0ef6176e1 100644 --- a/src/ScreenMiniMenu.h +++ b/src/ScreenMiniMenu.h @@ -4,7 +4,6 @@ #define SCREEN_MINI_MENU_H #include "ScreenOptions.h" -#include #include "GameConstantsAndTypes.h" typedef bool (*MenuRowUpdateEnabled)(); @@ -86,9 +85,7 @@ struct MenuRowDef { for ( int i = low; i <= high; i++ ) { - std::stringstream ss; - ss << i; - choices.push_back(ss.str().c_str()); + choices.push_back(IntToString(i).c_str()); } } diff --git a/src/ScreenOptionsMasterPrefs.cpp b/src/ScreenOptionsMasterPrefs.cpp index 347ecf041d..6ef345ceaf 100644 --- a/src/ScreenOptionsMasterPrefs.cpp +++ b/src/ScreenOptionsMasterPrefs.cpp @@ -181,7 +181,7 @@ static void GameSel( int &sel, bool ToSel, const ConfOption *pConfOption ) sel = 0; for(unsigned i = 0; i < choices.size(); ++i) - if( !stricmp(choices[i], sCurGameName) ) + if( !choices[i].EqualsNoCase(sCurGameName) ) sel = i; } else { vector aGames; @@ -222,12 +222,12 @@ static void Language( int &sel, bool ToSel, const ConfOption *pConfOption ) { sel = -1; for( unsigned i=0; sel == -1 && i < vs.size(); ++i ) - if( !stricmp(vs[i], THEME->GetCurLanguage()) ) + if( !vs[i].EqualsNoCase(THEME->GetCurLanguage()) ) sel = i; // If the current language doesn't exist, we'll show BASE_LANGUAGE, so select that. for( unsigned i=0; sel == -1 && i < vs.size(); ++i ) - if( !stricmp(vs[i], SpecialFiles::BASE_LANGUAGE) ) + if( !vs[i].EqualsNoCase(SpecialFiles::BASE_LANGUAGE) ) sel = i; if( sel == -1 ) @@ -276,7 +276,7 @@ static void RequestedTheme( int &sel, bool ToSel, const ConfOption *pConfOption { sel = 0; for( unsigned i=1; im_sTheme.Get()) ) + if( !vsThemeNames[i].EqualsNoCase(PREFSMAN->m_sTheme.Get()) ) sel = i; } else @@ -302,7 +302,7 @@ static void Announcer( int &sel, bool ToSel, const ConfOption *pConfOption ) { sel = 0; for( unsigned i=1; iGetCurAnnouncerName()) ) + if( !choices[i].EqualsNoCase(ANNOUNCER->GetCurAnnouncerName()) ) sel = i; } else @@ -329,7 +329,7 @@ static void DefaultNoteSkin( int &sel, bool ToSel, const ConfOption *pConfOption po.FromString( PREFSMAN->m_sDefaultModifiers ); sel = 0; for( unsigned i=0; i < choices.size(); i++ ) - if( !stricmp(choices[i], po.m_sNoteSkin) ) + if( !choices[i].EqualsNoCase(po.m_sNoteSkin) ) sel = i; } else diff --git a/src/ScreenPackages.cpp b/src/ScreenPackages.cpp index 7eae34fa03..a9051a3fa1 100644 --- a/src/ScreenPackages.cpp +++ b/src/ScreenPackages.cpp @@ -622,14 +622,14 @@ void ScreenPackages::HTTPUpdate() m_sResponseName = "Malformed response."; return; } - m_iResponseCode = atoi(m_sBUFFER.substr(i+1,j-i).c_str()); + m_iResponseCode = StringToInt(m_sBUFFER.substr(i+1,j-i)); m_sResponseName = m_sBUFFER.substr( j+1, k-j ); i = m_sBUFFER.find("Content-Length:"); j = m_sBUFFER.find("\n", i+1 ); if( i != string::npos ) - m_iTotalBytes = atoi(m_sBUFFER.substr(i+16,j-i).c_str()); + m_iTotalBytes = StringToInt(m_sBUFFER.substr(i+16,j-i)); else m_iTotalBytes = -1; //We don't know, so go until disconnect @@ -696,7 +696,7 @@ bool ScreenPackages::ParseHTTPAddress( const RString &URL, RString &sProto, RStr sServer = asMatches[1]; if( asMatches[3] != "" ) { - iPort = atoi(asMatches[3]); + iPort = StringToInt(asMatches[3]); if( iPort == 0 ) return false; } diff --git a/src/Song.cpp b/src/Song.cpp index 47c84ed8b3..dcb39b1eec 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -619,13 +619,13 @@ void Song::TidyUpData() // Skip any image that we've already classified - if( HasBanner() && stricmp(m_sBannerFile, arrayImages[i])==0 ) + if( HasBanner() && m_sBannerFile.EqualsNoCase(arrayImages[i]) ) continue; // skip - if( HasBackground() && stricmp(m_sBackgroundFile, arrayImages[i])==0 ) + if( HasBackground() && m_sBackgroundFile.EqualsNoCase(arrayImages[i]) ) continue; // skip - if( HasCDTitle() && stricmp(m_sCDTitleFile, arrayImages[i])==0 ) + if( HasCDTitle() && m_sCDTitleFile.EqualsNoCase(arrayImages[i]) ) continue; // skip // todo: add checks for Jacket, Disc, and CDImage -aj 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/SongManager.cpp b/src/SongManager.cpp index fe4bb51cd2..e91b1c4d41 100644 --- a/src/SongManager.cpp +++ b/src/SongManager.cpp @@ -879,7 +879,7 @@ void SongManager::InitRandomAttacks() continue; } - if( stricmp(sType,"ATTACK") != 0 ) + if( !sType.EqualsNoCase("ATTACK") ) { LOG->Warn( "Got \"%s:%s\" tag with wrong declaration", sType.c_str(), sAttack.c_str() ); continue; diff --git a/src/SongOptions.cpp b/src/SongOptions.cpp index fd841b9a3d..de846681b3 100644 --- a/src/SongOptions.cpp +++ b/src/SongOptions.cpp @@ -173,7 +173,7 @@ bool SongOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut ) Regex lives("^([0-9]+) ?(lives|life)$"); if( lives.Compare(sBit, matches) ) { - m_iBatteryLives = atoi( matches[0] ); + m_iBatteryLives = StringToInt( matches[0] ); return true; } 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; } diff --git a/src/XmlFile.cpp b/src/XmlFile.cpp index 30e71417b6..7e99ecb41e 100644 --- a/src/XmlFile.cpp +++ b/src/XmlFile.cpp @@ -50,9 +50,9 @@ void XNode::Free() } void XNodeStringValue::GetValue( RString &out ) const { out = m_sValue; } -void XNodeStringValue::GetValue( int &out ) const { out = atoi(m_sValue); } +void XNodeStringValue::GetValue( int &out ) const { out = StringToInt(m_sValue); } void XNodeStringValue::GetValue( float &out ) const { out = StringToFloat(m_sValue); } -void XNodeStringValue::GetValue( bool &out ) const { out = atoi(m_sValue) != 0; } +void XNodeStringValue::GetValue( bool &out ) const { out = StringToInt(m_sValue) != 0; } void XNodeStringValue::GetValue( unsigned &out ) const { out = strtoul(m_sValue,NULL,0); } void XNodeStringValue::PushValue( lua_State *L ) const { diff --git a/src/global.h b/src/global.h index 19a6d3c76b..b90f6c86c7 100644 --- a/src/global.h +++ b/src/global.h @@ -192,13 +192,6 @@ typedef StdString::CStdString RString; #include "RageException.h" -#if !defined(WIN32) -/** @brief Define stricmp to be strcasecmp. */ -#define stricmp strcasecmp -/** @brief Define strnicmp to be strncasecmp. */ -#define strnicmp strncasecmp -#endif - /* Define a few functions if necessary */ #include