bool IniFile::WriteFile( const CString &sPath ) { RageFile f; if( !f.Open( sPath, RageFile::WRITE ) ) { LOG->Trace( "Writing '%s' failed: %s", sPath.c_str(), f.GetError().c_str() ); m_sError = f.GetError(); return false; } for( keymap::const_iterator k = keys.begin(); k != keys.end(); ++k ) { if (k->second.empty()) continue; if( f.PutLine( ssprintf("[%s]", k->first.c_str()) ) < 0 ) { m_sError = f.GetError(); return false; } for( key::const_iterator i = k->second.begin(); i != k->second.end(); ++i ) f.PutLine( ssprintf("%s=%s", i->first.c_str(), i->second.c_str()) ); if( f.PutLine( "" ) < 0 ) { m_sError = f.GetError(); return false; } } return true; }
static bool WriteDWINotesTag( RageFile &f, const Steps &out ) { if( out.GetDifficulty() == Difficulty_Edit ) return false; // not supported by DWI LOG->Trace( "Steps::WriteDWINotesTag" ); switch( out.m_StepsType ) { case StepsType_dance_single: f.Write( "#SINGLE:" ); break; case StepsType_dance_couple: f.Write( "#COUPLE:" ); break; case StepsType_dance_double: f.Write( "#DOUBLE:" ); break; case StepsType_dance_solo: f.Write( "#SOLO:" ); break; default: return false; // not a type supported by DWI } switch( out.GetDifficulty() ) { case Difficulty_Beginner: f.Write( "BEGINNER:" ); break; case Difficulty_Easy: f.Write( "BASIC:" ); break; case Difficulty_Medium: f.Write( "ANOTHER:" ); break; case Difficulty_Hard: f.Write( "MANIAC:" ); break; case Difficulty_Challenge: f.Write( "SMANIAC:" ); break; default: ASSERT(0); return false; } f.PutLine( ssprintf("%d:", out.GetMeter()) ); return true; }
bool XNode::SaveToFile( CString sFile, DISP_OPT *opt ) { RageFile f; if( !f.Open(sFile, RageFile::WRITE) ) { LOG->Warn("Couldn't open %s for writing: %s", sFile.c_str(), f.GetError().c_str() ); return false; } f.PutLine( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" ); if( !opt->stylesheet.empty() ) f.PutLine( "<?xml-stylesheet type=\"text/xsl\" href=\"" + opt->stylesheet + "\"?>" ); if( !this->GetXML(f, opt) ) return false; if( f.Flush() == -1 ) return false; return true; }
bool NotesWriterSM::Write(CString sPath, const Song &out, bool bSavingCache) { /* Flush dir cache when writing steps, so the old size isn't cached. */ FILEMAN->FlushDirCache( Dirname(sPath) ); unsigned i; int flags = RageFile::WRITE; /* If we're not saving cache, we're saving real data, so enable SLOW_FLUSH * to prevent data loss. If we're saving cache, this will slow things down * too much. */ if( !bSavingCache ) flags |= RageFile::SLOW_FLUSH; RageFile f; if( !f.Open( sPath, flags ) ) { LOG->Warn( "Error opening song file '%s' for writing: %s", sPath.c_str(), f.GetError().c_str() ); return false; } WriteGlobalTags( f, out ); if( bSavingCache ) { f.PutLine( ssprintf( "// cache tags:" ) ); f.PutLine( ssprintf( "#FIRSTBEAT:%.3f;", out.m_fFirstBeat ) ); f.PutLine( ssprintf( "#LASTBEAT:%.3f;", out.m_fLastBeat ) ); f.PutLine( ssprintf( "#SONGFILENAME:%s;", out.m_sSongFileName.c_str() ) ); f.PutLine( ssprintf( "#HASMUSIC:%i;", out.m_bHasMusic ) ); f.PutLine( ssprintf( "#HASBANNER:%i;", out.m_bHasBanner ) ); f.PutLine( ssprintf( "#MUSICLENGTH:%.3f;", out.m_fMusicLengthSeconds ) ); f.PutLine( ssprintf( "// end cache tags" ) ); } // // Save all Steps for this file // const vector<Steps*>& vpSteps = out.GetAllSteps(); for( i=0; i<vpSteps.size(); i++ ) { const Steps* pSteps = vpSteps[i]; if( pSteps->IsAutogen() ) continue; /* don't write autogen notes */ /* Only save steps that weren't loaded from a profile. */ if( pSteps->WasLoadedFromProfile() ) continue; WriteSMNotesTag( *pSteps, f, bSavingCache ); } return true; }
void NotesWriterSM::WriteSMNotesTag( const Steps &in, RageFile &f, bool bSavingCache ) { f.PutLine( "" ); f.PutLine( ssprintf( "//---------------%s - %s----------------", GameManager::StepsTypeToString(in.m_StepsType).c_str(), in.GetDescription().c_str() ) ); f.PutLine( "#NOTES:" ); f.PutLine( ssprintf( " %s:", GameManager::StepsTypeToString(in.m_StepsType).c_str() ) ); f.PutLine( ssprintf( " %s:", in.GetDescription().c_str() ) ); f.PutLine( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); f.PutLine( ssprintf( " %d:", in.GetMeter() ) ); int MaxRadar = bSavingCache? NUM_RADAR_CATEGORIES:5; CStringArray asRadarValues; for( int r=0; r < MaxRadar; r++ ) asRadarValues.push_back( ssprintf("%.3f", in.GetRadarValues()[r]) ); /* Don't append a newline here; it's added in NoteDataUtil::GetSMNoteDataString. * If we add it here, then every time we write unmodified data we'll add an extra * newline and they'll accumulate. */ f.Write( ssprintf( " %s:", join(",",asRadarValues).c_str() ) ); CString sNoteData; CString sAttackData; in.GetSMNoteData( sNoteData, sAttackData ); vector<CString> lines; split( sNoteData, "\n", lines, false ); WriteLineList( f, lines, true, true ); if( sAttackData.empty() ) f.PutLine( ";" ); else { f.PutLine( ":" ); lines.clear(); split( sAttackData, "\n", lines, false ); WriteLineList( f, lines, true, true ); f.PutLine( ";" ); } }
static void WriteLineList( RageFile &f, vector<CString> &lines, bool SkipLeadingBlankLines, bool OmitLastNewline ) { for( unsigned i = 0; i < lines.size(); ++i ) { TrimRight( lines[i] ); if( SkipLeadingBlankLines ) { if( lines.size() == 0 ) continue; SkipLeadingBlankLines = false; } f.Write( lines[i] ); if( !OmitLastNewline || i+1 < lines.size() ) f.PutLine( "" ); /* newline */ } }
bool NotesWriterDWI::Write( RString sPath, const Song &out ) { RageFile f; if( !f.Open( sPath, RageFile::WRITE ) ) { LOG->UserLog( "Song file", sPath, "couldn't be opened for writing: %s", f.GetError().c_str() ); return false; } /* Write transliterations, if we have them, since DWI doesn't support UTF-8. */ f.PutLine( ssprintf("#TITLE:%s;", DwiEscape(out.GetTranslitFullTitle()).c_str()) ); f.PutLine( ssprintf("#ARTIST:%s;", DwiEscape(out.GetTranslitArtist()).c_str()) ); ASSERT( out.m_Timing.m_BPMSegments[0].m_iStartRow == 0 ); f.PutLine( ssprintf("#FILE:%s;", DwiEscape(out.m_sMusicFile).c_str()) ); f.PutLine( ssprintf("#BPM:%.3f;", out.m_Timing.m_BPMSegments[0].GetBPM()) ); f.PutLine( ssprintf("#GAP:%ld;", -lrintf( out.m_Timing.m_fBeat0OffsetInSeconds*1000 )) ); f.PutLine( ssprintf("#SAMPLESTART:%.3f;", out.m_fMusicSampleStartSeconds) ); f.PutLine( ssprintf("#SAMPLELENGTH:%.3f;", out.m_fMusicSampleLengthSeconds) ); if( out.m_sCDTitleFile.size() ) f.PutLine( ssprintf("#CDTITLE:%s;", DwiEscape(out.m_sCDTitleFile).c_str()) ); switch( out.m_DisplayBPMType ) { case Song::DISPLAY_ACTUAL: // write nothing break; case Song::DISPLAY_SPECIFIED: if( out.m_fSpecifiedBPMMin == out.m_fSpecifiedBPMMax ) f.PutLine( ssprintf("#DISPLAYBPM:%i;\n", (int) out.m_fSpecifiedBPMMin) ); else f.PutLine( ssprintf("#DISPLAYBPM:%i..%i;\n", (int) out.m_fSpecifiedBPMMin, (int) out.m_fSpecifiedBPMMax) ); break; case Song::DISPLAY_RANDOM: f.PutLine( "#DISPLAYBPM:*" ); break; } if( !out.m_Timing.m_StopSegments.empty() ) { f.Write( "#FREEZE:" ); for( unsigned i=0; i<out.m_Timing.m_StopSegments.size(); i++ ) { const StopSegment &fs = out.m_Timing.m_StopSegments[i]; f.Write( ssprintf("%.3f=%.3f", fs.m_iStartRow * 4.0f / ROWS_PER_BEAT, roundf(fs.m_fStopSeconds*1000)) ); if( i != out.m_Timing.m_StopSegments.size()-1 ) f.Write( "," ); } f.PutLine( ";" ); } if( out.m_Timing.m_BPMSegments.size() > 1) { f.Write( "#CHANGEBPM:" ); for( unsigned i=1; i<out.m_Timing.m_BPMSegments.size(); i++ ) { const BPMSegment &bs = out.m_Timing.m_BPMSegments[i]; f.Write( ssprintf("%.3f=%.3f", bs.m_iStartRow * 4.0f / ROWS_PER_BEAT, bs.GetBPM() ) ); if( i != out.m_Timing.m_BPMSegments.size()-1 ) f.Write( "," ); } f.PutLine( ";" ); } const vector<Steps*>& vpSteps = out.GetAllSteps(); for( unsigned i=0; i<vpSteps.size(); i++ ) { const Steps* pSteps = vpSteps[i]; if( pSteps->IsAutogen() ) continue; // don't save autogen notes if( !WriteDWINotesTag( f, *pSteps )) continue; WriteDWINotesField( f, *pSteps, 0 ); if( pSteps->m_StepsType==StepsType_dance_double || pSteps->m_StepsType==StepsType_dance_couple ) { f.PutLine( ":" ); WriteDWINotesField( f, *pSteps, 4 ); } f.PutLine( ";" ); } return true; }
static void WriteDWINotesField( RageFile &f, const Steps &out, int start ) { NoteData notedata; out.GetNoteData( notedata ); NoteDataUtil::InsertHoldTails( notedata ); const int iLastMeasure = int( notedata.GetLastBeat()/BEATS_PER_MEASURE ); for( int m=0; m<=iLastMeasure; m++ ) // foreach measure { NoteType nt = NoteDataUtil::GetSmallestNoteTypeForMeasure( notedata, m ); double fCurrentIncrementer = 0; switch( nt ) { case NOTE_TYPE_4TH: case NOTE_TYPE_8TH: fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE; break; case NOTE_TYPE_12TH: case NOTE_TYPE_24TH: f.Write( "[" ); fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE; break; case NOTE_TYPE_16TH: f.Write( "(" ); fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE; break; case NOTE_TYPE_32ND: case NOTE_TYPE_64TH: f.Write( "{" ); fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE; break; case NOTE_TYPE_48TH: case NOTE_TYPE_192ND: case NoteType_Invalid: // since, for whatever reason, the only way to do // 48ths is through a block of 192nds... f.Write( "`" ); fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE; break; default: ASSERT_M(0, ssprintf("nt = %d",nt) ); break; } double fFirstBeatInMeasure = m * BEATS_PER_MEASURE; double fLastBeatInMeasure = (m+1) * BEATS_PER_MEASURE; for( double b=fFirstBeatInMeasure; b<=fLastBeatInMeasure-1/64.0f; b+=fCurrentIncrementer ) // need the -0.0001 to account for rounding errors { int row = BeatToNoteRow( (float)b ); RString str; switch( out.m_StepsType ) { case StepsType_dance_single: case StepsType_dance_couple: case StepsType_dance_double: str = NotesToDWIString( notedata.GetTapNote(start+0, row), notedata.GetTapNote(start+1, row), notedata.GetTapNote(start+2, row), notedata.GetTapNote(start+3, row) ); // Blank out the notes so we don't write them again if the incrementer is small notedata.SetTapNote(start+0, row, TAP_EMPTY); notedata.SetTapNote(start+1, row, TAP_EMPTY); notedata.SetTapNote(start+2, row, TAP_EMPTY); notedata.SetTapNote(start+3, row, TAP_EMPTY); break; case StepsType_dance_solo: str = NotesToDWIString( notedata.GetTapNote(0, row), notedata.GetTapNote(1, row), notedata.GetTapNote(2, row), notedata.GetTapNote(3, row), notedata.GetTapNote(4, row), notedata.GetTapNote(5, row) ); // Blank out the notes so we don't write them again if the incrementer is small notedata.SetTapNote(start+0, row, TAP_EMPTY); notedata.SetTapNote(start+1, row, TAP_EMPTY); notedata.SetTapNote(start+2, row, TAP_EMPTY); notedata.SetTapNote(start+3, row, TAP_EMPTY); notedata.SetTapNote(start+4, row, TAP_EMPTY); notedata.SetTapNote(start+5, row, TAP_EMPTY); break; default: ASSERT(0); // not a type supported by DWI. We shouldn't have called in here if that's the case } f.Write( str ); } switch( nt ) { case NOTE_TYPE_4TH: case NOTE_TYPE_8TH: break; case NOTE_TYPE_12TH: case NOTE_TYPE_24TH: f.Write( "]" ); break; case NOTE_TYPE_16TH: f.Write( ")" ); break; case NOTE_TYPE_32ND: case NOTE_TYPE_64TH: f.Write( "}" ); break; case NOTE_TYPE_48TH: case NOTE_TYPE_192ND: case NoteType_Invalid: f.Write( "'" ); break; default: ASSERT(0); // fall though } f.PutLine( "" ); } }
void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out ) { f.PutLine( ssprintf( "#TITLE:%s;", out.m_sMainTitle.c_str() ) ); f.PutLine( ssprintf( "#SUBTITLE:%s;", out.m_sSubTitle.c_str() ) ); f.PutLine( ssprintf( "#ARTIST:%s;", out.m_sArtist.c_str() ) ); f.PutLine( ssprintf( "#TITLETRANSLIT:%s;", out.m_sMainTitleTranslit.c_str() ) ); f.PutLine( ssprintf( "#SUBTITLETRANSLIT:%s;", out.m_sSubTitleTranslit.c_str() ) ); f.PutLine( ssprintf( "#ARTISTTRANSLIT:%s;", out.m_sArtistTranslit.c_str() ) ); f.PutLine( ssprintf( "#CREDIT:%s;", out.m_sCredit.c_str() ) ); f.PutLine( ssprintf( "#BANNER:%s;", out.m_sBannerFile.c_str() ) ); f.PutLine( ssprintf( "#BACKGROUND:%s;", out.m_sBackgroundFile.c_str() ) ); f.PutLine( ssprintf( "#LYRICSPATH:%s;", out.m_sLyricsFile.c_str() ) ); f.PutLine( ssprintf( "#CDTITLE:%s;", out.m_sCDTitleFile.c_str() ) ); f.PutLine( ssprintf( "#MUSIC:%s;", out.m_sMusicFile.c_str() ) ); f.PutLine( ssprintf( "#OFFSET:%.3f;", out.m_Timing.m_fBeat0OffsetInSeconds ) ); f.PutLine( ssprintf( "#SAMPLESTART:%.3f;", out.m_fMusicSampleStartSeconds ) ); f.PutLine( ssprintf( "#SAMPLELENGTH:%.3f;", out.m_fMusicSampleLengthSeconds ) ); f.Write( "#SELECTABLE:" ); switch(out.m_SelectionDisplay) { default: ASSERT(0); /* fallthrough */ case Song::SHOW_ALWAYS: f.Write( "YES" ); break; case Song::SHOW_NEVER: f.Write( "NO" ); break; case Song::SHOW_ROULETTE: f.Write( "ROULETTE" ); break; } f.PutLine( ";" ); switch( out.m_DisplayBPMType ) { case Song::DISPLAY_ACTUAL: // write nothing break; case Song::DISPLAY_SPECIFIED: if( out.m_fSpecifiedBPMMin == out.m_fSpecifiedBPMMax ) f.PutLine( ssprintf( "#DISPLAYBPM:%.3f;", out.m_fSpecifiedBPMMin ) ); else f.PutLine( ssprintf( "#DISPLAYBPM:%.3f:%.3f;", out.m_fSpecifiedBPMMin, out.m_fSpecifiedBPMMax ) ); break; case Song::DISPLAY_RANDOM: f.PutLine( ssprintf( "#DISPLAYBPM:*;" ) ); break; } f.Write( "#BPMS:" ); unsigned i; for( i=0; i<out.m_Timing.m_BPMSegments.size(); i++ ) { const BPMSegment &bs = out.m_Timing.m_BPMSegments[i]; f.Write( ssprintf( "%.3f=%.3f", bs.m_fStartBeat, bs.m_fBPM ) ); if( i != out.m_Timing.m_BPMSegments.size()-1 ) f.Write( "," ); } f.PutLine( ";" ); f.Write( "#STOPS:" ); for( i=0; i<out.m_Timing.m_StopSegments.size(); i++ ) { const StopSegment &fs = out.m_Timing.m_StopSegments[i]; f.PutLine( ssprintf( "%.3f=%.3f", fs.m_fStartBeat, fs.m_fStopSeconds ) ); if( i != out.m_Timing.m_StopSegments.size()-1 ) f.Write( "," ); } f.PutLine( ";" ); f.Write( "#BGCHANGES:" ); for( i=0; i<out.m_BackgroundChanges.size(); i++ ) { const BackgroundChange &seg = out.m_BackgroundChanges[i]; f.PutLine( ssprintf( "%.3f=%s=%.3f=%d=%d=%d,", seg.m_fStartBeat, seg.m_sBGName.c_str(), seg.m_fRate, seg.m_bFadeLast, seg.m_bRewindMovie, seg.m_bLoop ) ); } /* If there's an animation plan at all, add a dummy "-nosongbg-" tag to indicate that * this file doesn't want a song BG entry added at the end. See SMLoader::TidyUpData. * This tag will be removed on load. Add it at a very high beat, so it won't cause * problems if loaded in older versions. */ if( !out.m_BackgroundChanges.empty() ) f.PutLine( "99999=-nosongbg-=1.000=0=0=0 // don't automatically add -songbackground-" ); f.PutLine( ";" ); if( out.m_ForegroundChanges.size() ) { f.Write( "#FGCHANGES:" ); for( i=0; i<out.m_ForegroundChanges.size(); i++ ) { const BackgroundChange &seg = out.m_ForegroundChanges[i]; f.PutLine( ssprintf( "%.3f=%s=%.3f=%d=%d=%d", seg.m_fStartBeat, seg.m_sBGName.c_str(), seg.m_fRate, seg.m_bFadeLast, seg.m_bRewindMovie, seg.m_bLoop ) ); if( i != out.m_ForegroundChanges.size()-1 ) f.Write( "," ); } f.PutLine( ";" ); } }