From 7ebfd65a9bf856e2bfc6627457be87279e98af4c Mon Sep 17 00:00:00 2001 From: John Bauer Date: Fri, 1 Dec 2006 22:15:26 +0000 Subject: [PATCH] Make it so .sm files can contain \-escaped characters. Also, make the writer for .dwi files replace control characters with dwi-safe characters. Other file formats are unaffected. --- stepmania/src/CourseLoaderCRS.cpp | 6 +-- stepmania/src/MsdFile.cpp | 84 ++++++++++++++++++------------- stepmania/src/MsdFile.h | 8 +-- stepmania/src/NotesLoaderDWI.cpp | 2 +- stepmania/src/NotesLoaderKSF.cpp | 4 +- stepmania/src/NotesLoaderSM.cpp | 14 +++--- stepmania/src/NotesWriterDWI.cpp | 10 ++-- stepmania/src/NotesWriterSM.cpp | 40 ++++++++------- stepmania/src/RageUtil.cpp | 49 ++++++++++++++++++ stepmania/src/RageUtil.h | 8 +++ stepmania/src/SongManager.cpp | 2 +- 11 files changed, 151 insertions(+), 76 deletions(-) diff --git a/stepmania/src/CourseLoaderCRS.cpp b/stepmania/src/CourseLoaderCRS.cpp index c80b6c8815..bdb60e0894 100644 --- a/stepmania/src/CourseLoaderCRS.cpp +++ b/stepmania/src/CourseLoaderCRS.cpp @@ -39,7 +39,7 @@ static CourseDifficulty CRSStringToDifficulty( const RString& s ) bool CourseLoaderCRS::LoadFromBuffer( const RString &sPath, const RString &sBuffer, Course &out ) { MsdFile msd; - msd.ReadFromString( sBuffer ); + msd.ReadFromString( sBuffer, false ); // don't unescape return LoadFromMsd( sPath, msd, out, true ); } @@ -352,7 +352,7 @@ bool CourseLoaderCRS::LoadFromCRSFile( const RString &_sPath, Course &out ) } MsdFile msd; - if( !msd.ReadFile(sPath) ) + if( !msd.ReadFile( sPath, false ) ) // don't unescape { LOG->UserLog( "Course file", sPath, "couldn't be opened: %s.", msd.GetError().c_str() ); @@ -389,7 +389,7 @@ bool CourseLoaderCRS::LoadEditFromFile( const RString &sEditFilePath, ProfileSlo } MsdFile msd; - if( !msd.ReadFile( sEditFilePath ) ) + if( !msd.ReadFile( sEditFilePath, false ) ) // don't unescape { LOG->UserLog( "Edit file", sEditFilePath, "couldn't be opened: %s", msd.GetError().c_str() ); return false; diff --git a/stepmania/src/MsdFile.cpp b/stepmania/src/MsdFile.cpp index 1e7bbdd6b3..faa1ac4add 100644 --- a/stepmania/src/MsdFile.cpp +++ b/stepmania/src/MsdFile.cpp @@ -10,19 +10,15 @@ * #VALUE:PARAM1 * #VALUE2:PARAM2 * we'll recover. - * - * TODO: Normal text fields need some way of escaping. We need to be able to escape - * colons and "//". Also, we should escape #s, so if we really want to put a # at the - * beginning of a line, we can. */ #include "global.h" #include "MsdFile.h" +#include "RageFile.h" #include "RageLog.h" #include "RageUtil.h" -#include "RageFile.h" -void MsdFile::AddParam( char *buf, int len ) +void MsdFile::AddParam( const char *buf, int len ) { values.back().params.push_back( RString(buf, len) ); } @@ -33,22 +29,21 @@ void MsdFile::AddValue() /* (no extra charge) */ values.back().params.reserve( 32 ); } -void MsdFile::ReadBuf( char *buf, int len ) +void MsdFile::ReadBuf( const char *buf, int len, bool bUnescape ) { values.reserve( 64 ); - int value_start = -1; - bool ReadingValue=false; int i = 0; + char *cProcessed = new char[len]; + int iProcessedLen = -1; while( i < len ) { if( i+1 < len && buf[i] == '/' && buf[i+1] == '/' ) { - /* //; erase with spaces until newline */ + /* Skip a comment entirely; don't copy the comment to the value/parameter */ do { - buf[i] = ' '; i++; } while( i < len && buf[i] != '\n' ); @@ -62,11 +57,14 @@ void MsdFile::ReadBuf( char *buf, int len ) * missed the ;. Back up and end the value. */ /* Make sure this # is the first non-whitespace character on the line. */ bool FirstChar = true; - int j; - for( j = i-1; j >= 0 && !strchr("\r\n", buf[j]); --j ) + int j = iProcessedLen; + while( j > 0 && cProcessed[j - 1] != '\r' && cProcessed[j - 1] != '\n' ) { - if(buf[j] == ' ' || buf[j] == '\t') + if( cProcessed[j - 1] == ' ' || cProcessed[j - 1] == '\t' ) + { + --j; continue; + } FirstChar = false; break; @@ -74,16 +72,20 @@ void MsdFile::ReadBuf( char *buf, int len ) if( !FirstChar ) { - /* Oops, we're not; handle this like a regular character. */ - i++; + /* We're not the first char on a line. Treat it as if it were a normal character. */ + cProcessed[iProcessedLen++] = buf[i++]; continue; } /* Skip newlines and whitespace before adding the value. */ - while( j >= 1 && strchr("\r\n", buf[j-1]) ) - --j; + iProcessedLen = j; + while( iProcessedLen > 0 && + ( cProcessed[iProcessedLen - 1] == '\r' || cProcessed[iProcessedLen - 1] == '\n' || + cProcessed[iProcessedLen - 1] == ' ' || cProcessed[iProcessedLen - 1] == '\t' ) ) + --iProcessedLen; - AddParam( buf+value_start, j - value_start ); + AddParam( cProcessed, iProcessedLen ); + iProcessedLen = 0; ReadingValue=false; } @@ -96,36 +98,52 @@ void MsdFile::ReadBuf( char *buf, int len ) if( !ReadingValue ) { - i++; + if( bUnescape && buf[i] == '\\' ) + i += 2; + else + ++i; continue; /* nothing else is meaningful outside of a value */ } /* : and ; end the current param, if any. */ - if( value_start != -1 && (buf[i] == ':' || buf[i] == ';') ) - AddParam(buf+value_start, i - value_start); + if( iProcessedLen != -1 && (buf[i] == ':' || buf[i] == ';') ) + AddParam( cProcessed, iProcessedLen ); /* # and : begin new params. */ if( buf[i] == '#' || buf[i] == ':' ) { - i++; /* skip */ - value_start = i; + ++i; + iProcessedLen = 0; continue; } /* ; ends the current value. */ if( buf[i] == ';' ) + { ReadingValue=false; + ++i; + continue; + } - i++; + /* We've gone through all the control characters. All that is left is either an escaped character, + * ie \#, \\, \:, etc., or a regular character. */ + if( bUnescape && i < len && buf[i] == '\\' ) + ++i; + if( i < len ) + { + cProcessed[iProcessedLen++] = buf[i++]; + } } /* Add any unterminated value at the very end. */ if( ReadingValue ) - AddParam( buf+value_start, i - value_start ); + AddParam( cProcessed, iProcessedLen ); + + delete [] cProcessed; } // returns true if successful, false otherwise -bool MsdFile::ReadFile( RString sNewPath ) +bool MsdFile::ReadFile( RString sNewPath, bool bUnescape ) { error = ""; @@ -148,18 +166,14 @@ bool MsdFile::ReadFile( RString sNewPath ) return false; } - ReadBuf( (char*) FileString.c_str(), iBytesRead ); + ReadBuf( FileString.c_str(), iBytesRead, bUnescape ); return true; } -void MsdFile::ReadFromString( const RString &sString ) +void MsdFile::ReadFromString( const RString &sString, bool bUnescape ) { - /* Be careful. ReadBuf modifies the buffer given to it. */ - char *pCopy = new char[sString.size()]; - memcpy( pCopy, sString.data(), sString.size() ); - ReadBuf( pCopy, sString.size() ); - delete [] pCopy; + ReadBuf( sString.c_str(), sString.size(), bUnescape ); } RString MsdFile::GetParam(unsigned val, unsigned par) const @@ -171,7 +185,7 @@ RString MsdFile::GetParam(unsigned val, unsigned par) const } /* - * (c) 2001-2004 Chris Danford, Glenn Maynard + * (c) 2001-2006 Chris Danford, Glenn Maynard * * All rights reserved. * diff --git a/stepmania/src/MsdFile.h b/stepmania/src/MsdFile.h index 5cbe50107c..1c5c49e139 100644 --- a/stepmania/src/MsdFile.h +++ b/stepmania/src/MsdFile.h @@ -17,8 +17,8 @@ public: virtual ~MsdFile() { } // Returns true if successful, false otherwise. - bool ReadFile( RString sFilePath ); - void ReadFromString( const RString &sString ); + bool ReadFile( RString sFilePath, bool bUnescape ); + void ReadFromString( const RString &sString, bool bUnescape ); RString GetError() const { return error; } @@ -29,8 +29,8 @@ public: private: - void ReadBuf( char *buf, int len ); - void AddParam( char *buf, int len ); + void ReadBuf( const char *buf, int len, bool bUnescape ); + void AddParam( const char *buf, int len ); void AddValue(); vector values; diff --git a/stepmania/src/NotesLoaderDWI.cpp b/stepmania/src/NotesLoaderDWI.cpp index cfa3027ce5..9de22b298c 100644 --- a/stepmania/src/NotesLoaderDWI.cpp +++ b/stepmania/src/NotesLoaderDWI.cpp @@ -362,7 +362,7 @@ bool DWILoader::LoadFromDWIFile( const RString &sPath, Song &out ) m_sLoadingFile = sPath; MsdFile msd; - if( !msd.ReadFile( sPath ) ) + if( !msd.ReadFile( sPath, false ) ) // don't unescape { LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() ); return false; diff --git a/stepmania/src/NotesLoaderKSF.cpp b/stepmania/src/NotesLoaderKSF.cpp index f99b9f22e6..a16ae3299b 100644 --- a/stepmania/src/NotesLoaderKSF.cpp +++ b/stepmania/src/NotesLoaderKSF.cpp @@ -63,7 +63,7 @@ bool KSFLoader::LoadFromKSFFile( const RString &sPath, Steps &out, const Song &s LOG->Trace( "Steps::LoadFromKSFFile( '%s' )", sPath.c_str() ); MsdFile msd; - if( !msd.ReadFile( sPath ) ) + if( !msd.ReadFile( sPath, false ) ) // don't unescape { LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() ); return false; @@ -334,7 +334,7 @@ void KSFLoader::LoadTags( const RString &str, Song &out ) bool KSFLoader::LoadGlobalData( const RString &sPath, Song &out ) { MsdFile msd; - if( !msd.ReadFile( sPath ) ) + if( !msd.ReadFile( sPath, false ) ) // don't unescape { LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() ); return false; diff --git a/stepmania/src/NotesLoaderSM.cpp b/stepmania/src/NotesLoaderSM.cpp index 1e727054d2..03d002931e 100644 --- a/stepmania/src/NotesLoaderSM.cpp +++ b/stepmania/src/NotesLoaderSM.cpp @@ -70,7 +70,7 @@ void SMLoader::GetApplicableFiles( const RString &sPath, vector &out ) bool SMLoader::LoadTimingFromFile( const RString &fn, TimingData &out ) { MsdFile msd; - if( !msd.ReadFile( fn ) ) + if( !msd.ReadFile( fn, true ) ) // unescape { LOG->UserLog( "Song file", fn, "couldn't be loaded: %s", msd.GetError().c_str() ); return false; @@ -165,11 +165,13 @@ bool LoadFromBGChangesString( BackgroundChange &change, const RString &sBGChange { case 11: change.m_def.m_sColor2 = aBGChangeValues[10]; - change.m_def.m_sColor2.Replace( '^', ',' ); // UGLY: unescape "," in colors + // .sm files made before we started escaping will still have '^' instead of ',' + change.m_def.m_sColor2.Replace( '^', ',' ); // fall through case 10: change.m_def.m_sColor1 = aBGChangeValues[9]; - change.m_def.m_sColor1.Replace( '^', ',' ); // UGLY: unescape "," in colors + // .sm files made before we started escaping will still have '^' instead of ',' + change.m_def.m_sColor1.Replace( '^', ',' ); // fall through case 9: change.m_sTransition = aBGChangeValues[8]; @@ -225,7 +227,7 @@ bool SMLoader::LoadFromSMFile( const RString &sPath, Song &out ) LOG->Trace( "Song::LoadFromSMFile(%s)", sPath.c_str() ); MsdFile msd; - if( !msd.ReadFile( sPath ) ) + if( !msd.ReadFile( sPath, true ) ) // unescape { LOG->UserLog( "Song file", sPath, "couldn't be opened: %s", msd.GetError().c_str() ); return false; @@ -462,7 +464,7 @@ bool SMLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool b } MsdFile msd; - if( !msd.ReadFile( sEditFilePath ) ) + if( !msd.ReadFile( sEditFilePath, true ) ) // unescape { LOG->UserLog( "Edit file", sEditFilePath, "couldn't be opened: %s", msd.GetError().c_str() ); return false; @@ -474,7 +476,7 @@ bool SMLoader::LoadEditFromFile( RString sEditFilePath, ProfileSlot slot, bool b bool SMLoader::LoadEditFromBuffer( const RString &sBuffer, const RString &sEditFilePath, ProfileSlot slot ) { MsdFile msd; - msd.ReadFromString( sBuffer ); + msd.ReadFromString( sBuffer, true ); // unescape return LoadEditFromMsd( msd, sEditFilePath, slot, true ); } diff --git a/stepmania/src/NotesWriterDWI.cpp b/stepmania/src/NotesWriterDWI.cpp index ab66121727..b6525a1e7c 100644 --- a/stepmania/src/NotesWriterDWI.cpp +++ b/stepmania/src/NotesWriterDWI.cpp @@ -356,16 +356,16 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out ) } /* Write transliterations, if we have them, since DWI doesn't support UTF-8. */ - f.PutLine( ssprintf("#TITLE:%s;", out.GetTranslitFullTitle().c_str()) ); - f.PutLine( ssprintf("#ARTIST:%s;", out.GetTranslitArtist().c_str()) ); + 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_iStartIndex == 0 ); - f.PutLine( ssprintf("#FILE:%s;", out.m_sMusicFile.c_str()) ); + 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:%d;", int(-roundf( 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;", out.m_sCDTitleFile.c_str()) ); + f.PutLine( ssprintf("#CDTITLE:%s;", DwiEscape(out.m_sCDTitleFile).c_str()) ); switch( out.m_DisplayBPMType ) { case Song::DISPLAY_ACTUAL: @@ -435,7 +435,7 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out ) } /* - * (c) 2001-2004 Chris Danford, Glenn Maynard + * (c) 2001-2006 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/stepmania/src/NotesWriterSM.cpp b/stepmania/src/NotesWriterSM.cpp index 793cb58583..450306f1d9 100644 --- a/stepmania/src/NotesWriterSM.cpp +++ b/stepmania/src/NotesWriterSM.cpp @@ -18,10 +18,12 @@ static RString BackgroundChangeToString( const BackgroundChange &bgc ) { + // TODO: Technically we need to double-escape the filename (because it might contain '=') and then + // unescape the value returned by the MsdFile. RString s = ssprintf( "%.3f=%s=%.3f=%d=%d=%d=%s=%s=%s=%s=%s", bgc.m_fStartBeat, - bgc.m_def.m_sFile1.c_str(), + SmEscape(bgc.m_def.m_sFile1).c_str(), bgc.m_fRate, bgc.m_sTransition == SBT_CrossFade, // backward compat bgc.m_def.m_sEffect == SBE_StretchRewind, // backward compat @@ -29,28 +31,27 @@ static RString BackgroundChangeToString( const BackgroundChange &bgc ) bgc.m_def.m_sEffect.c_str(), bgc.m_def.m_sFile2.c_str(), bgc.m_sTransition.c_str(), - bgc.m_def.m_sColor1.c_str(), - bgc.m_def.m_sColor2.c_str() + SmEscape(bgc.m_def.m_sColor1).c_str(), + SmEscape(bgc.m_def.m_sColor2).c_str() ); - s.Replace( ',', '^' ); // UGLY: escape "," in colors. return s; } static void 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( "#GENRE:%s;", out.m_sGenre.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( "#TITLE:%s;", SmEscape(out.m_sMainTitle).c_str() ) ); + f.PutLine( ssprintf( "#SUBTITLE:%s;", SmEscape(out.m_sSubTitle).c_str() ) ); + f.PutLine( ssprintf( "#ARTIST:%s;", SmEscape(out.m_sArtist).c_str() ) ); + f.PutLine( ssprintf( "#TITLETRANSLIT:%s;", SmEscape(out.m_sMainTitleTranslit).c_str() ) ); + f.PutLine( ssprintf( "#SUBTITLETRANSLIT:%s;", SmEscape(out.m_sSubTitleTranslit).c_str() ) ); + f.PutLine( ssprintf( "#ARTISTTRANSLIT:%s;", SmEscape(out.m_sArtistTranslit).c_str() ) ); + f.PutLine( ssprintf( "#GENRE:%s;", SmEscape(out.m_sGenre).c_str() ) ); + f.PutLine( ssprintf( "#CREDIT:%s;", SmEscape(out.m_sCredit).c_str() ) ); + f.PutLine( ssprintf( "#BANNER:%s;", SmEscape(out.m_sBannerFile).c_str() ) ); + f.PutLine( ssprintf( "#BACKGROUND:%s;", SmEscape(out.m_sBackgroundFile).c_str() ) ); + f.PutLine( ssprintf( "#LYRICSPATH:%s;", SmEscape(out.m_sLyricsFile).c_str() ) ); + f.PutLine( ssprintf( "#CDTITLE:%s;", SmEscape(out.m_sCDTitleFile).c_str() ) ); + f.PutLine( ssprintf( "#MUSIC:%s;", SmEscape(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 ) ); @@ -164,11 +165,12 @@ static RString GetSMNotesTag( const Song &song, const Steps &in, bool bSavingCac vector lines; lines.push_back( "" ); + // Escape to prevent some clown from making a comment of "\r\n;" lines.push_back( ssprintf("//---------------%s - %s----------------", - GameManager::StepsTypeToString(in.m_StepsType).c_str(), in.GetDescription().c_str()) ); + GameManager::StepsTypeToString(in.m_StepsType).c_str(), SmEscape(in.GetDescription()).c_str()) ); lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" ); lines.push_back( ssprintf( " %s:", GameManager::StepsTypeToString(in.m_StepsType).c_str() ) ); - lines.push_back( ssprintf( " %s:", in.GetDescription().c_str() ) ); + lines.push_back( ssprintf( " %s:", SmEscape(in.GetDescription()).c_str() ) ); lines.push_back( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); lines.push_back( ssprintf( " %d:", in.GetMeter() ) ); diff --git a/stepmania/src/RageUtil.cpp b/stepmania/src/RageUtil.cpp index d3e4b404f5..d19fb50035 100644 --- a/stepmania/src/RageUtil.cpp +++ b/stepmania/src/RageUtil.cpp @@ -559,6 +559,55 @@ RString join( const RString &sDelimitor, vector::const_iterator begin, return sRet; } +RString SmEscape( const RString &sUnescaped ) +{ + return SmEscape( sUnescaped.c_str(), sUnescaped.size() ); +} + +RString SmEscape( const char *cUnescaped, int len ) +{ + RString answer = ""; + for( int i = 0; i < len; ++i ) + { + if( cUnescaped[i] == '=' || cUnescaped[i] == '\\' || cUnescaped[i] == ':' || + cUnescaped[i] == '[' || cUnescaped[i] == ']' || cUnescaped[i] == ';' || + cUnescaped[i] == '#' || cUnescaped[i] == '\r' || cUnescaped[i] == '\n' || + cUnescaped[i] == ',' ) + answer += "\\"; + answer += cUnescaped[i]; + } + return answer; +} + +RString DwiEscape( const RString &sUnescaped ) +{ + return DwiEscape( sUnescaped.c_str(), sUnescaped.size() ); +} + +RString DwiEscape( const char *cUnescaped, int len ) +{ + RString answer = ""; + for( int i = 0; i < len; ++i ) + { + switch( cUnescaped[i] ) + { + case '=': answer += '-'; break; + case '\\': + case ':': + case '[': + case ']': + case ';': answer += 'I'; break; + case '#': + case '\r': + case '\n': + case ',': answer += ' '; break; + default: answer += cUnescaped[i]; + } + } + return answer; +} + + template static int DelimitorLength( const S &Delimitor ) { diff --git a/stepmania/src/RageUtil.h b/stepmania/src/RageUtil.h index 4ee2f1fdc6..63b08bf7c5 100644 --- a/stepmania/src/RageUtil.h +++ b/stepmania/src/RageUtil.h @@ -406,6 +406,14 @@ void split( const wstring &sSource, const wstring &sDelimitor, int &iBegin, int RString join( const RString &sDelimitor, const vector& sSource ); RString join( const RString &sDelimitor, vector::const_iterator begin, vector::const_iterator end ); +// These methods escapes a string for saving in a .sm or .crs file +RString SmEscape( const RString &sUnescaped ); +RString SmEscape( const char *cUnescaped, int len ); + +// These methods "escape" a string for .dwi by turning = into -, ] into I, etc. That is "lossy". +RString DwiEscape( const RString &sUnescaped ); +RString DwiEscape( const char *cUnescaped, int len ); + RString GetCwd(); void SetCommandlineArguments( int argc, char **argv ); diff --git a/stepmania/src/SongManager.cpp b/stepmania/src/SongManager.cpp index 0347a6ad3e..fa31ce7334 100644 --- a/stepmania/src/SongManager.cpp +++ b/stepmania/src/SongManager.cpp @@ -272,7 +272,7 @@ void SongManager::LoadGroupSymLinks(RString sDir, RString sGroupFolder) for( unsigned s=0; s< arraySymLinks.size(); s++ ) // for each symlink in this dir, add it in as a song. { MsdFile msdF; - msdF.ReadFile( sDir+sGroupFolder+"/"+arraySymLinks[s].c_str() ); + msdF.ReadFile( sDir+sGroupFolder+"/"+arraySymLinks[s].c_str(), false ); // don't unescape RString sSymDestination = msdF.GetParam(0,1); // Should only be 1 vale¶m...period. Song* pNewSong = new Song;