Equals signs also need to be escaped when parsing #SONGSELECT parameters.

This commit is contained in:
Michael Votaw
2024-03-06 01:45:26 -08:00
committed by teejusb
parent bf5404a71a
commit dcb1a2e3db
3 changed files with 37 additions and 12 deletions
+9 -3
View File
@@ -111,15 +111,18 @@ Complete Course Template
-- - Unlike the #SONG tag, this will not take into account group names. -- - Unlike the #SONG tag, this will not take into account group names.
-- If you want to specify a specific song from a specific group, use -- If you want to specify a specific song from a specific group, use
-- this along with the GROUP parameter. -- this along with the GROUP parameter.
-- - This does not support names that have commmas. -- - Commas, and other control characters (`#`,`;`, `=`, and `:`) can be used,
-- but they must be escaped (like `\#`, `\,`, etc).
-- Example: -- Example:
#SONGSELECT:TITLE=Goin' Under,Springtime; #SONGSELECT:TITLE=Goin' Under,Springtime;
#SONGSELECT:TITLE=thank u\, next;
-- GROUP= A list of one or more song groups, separated by commas. -- GROUP= A list of one or more song groups, separated by commas.
-- Notes: -- Notes:
-- - The "group" refers to the directory name of the song group. -- - The "group" refers to the directory name of the song group.
-- - This does not support folders that have commas in their name.
-- - Group names must be an *exact match*. -- - Group names must be an *exact match*.
-- - Commas, and other control characters (`#`,`;`, `=`, and `:`) can be used,
-- but they must be escaped (like `\#`, `\,`, etc).
-- Example: -- Example:
#SONGSELECT:GROUP=Stepmania 5,In The Groove; #SONGSELECT:GROUP=Stepmania 5,In The Groove;
@@ -127,13 +130,16 @@ Complete Course Template
-- Notes: -- Notes:
-- - The "artist" refers to either the #ARTIST or #ARTISTTRANSLIT value -- - The "artist" refers to either the #ARTIST or #ARTISTTRANSLIT value
-- of the simfile. -- of the simfile.
-- - This does not support artist names that contain commas. -- - Commas, and other control characters (`#`,`;`, `=`, and `:`) can be used,
-- but they must be escaped (like `\#`, `\,`, etc).
-- Example: -- Example:
#SONGSELECT:ARTIST=Kommisar,NegaRen; #SONGSELECT:ARTIST=Kommisar,NegaRen;
-- GENRE= A list of one or more song genres, separated by commas. -- GENRE= A list of one or more song genres, separated by commas.
-- Notes: -- Notes:
-- - The "genre" refers to the #GENRE tag of a song. -- - The "genre" refers to the #GENRE tag of a song.
-- - Commas, and other control characters (`#`,`;`, `=`, and `:`) can be used,
-- but they must be escaped (like `\#`, `\,`, etc).
-- - Most songs don't actually have a #GENRE defined, which unfortunately -- - Most songs don't actually have a #GENRE defined, which unfortunately
-- makes this parameter not terribly useful at this time. -- makes this parameter not terribly useful at this time.
-- Example: -- Example:
+24 -5
View File
@@ -32,6 +32,25 @@ const char *g_CRSDifficultyNames[] =
"Edit", "Edit",
}; };
// This is a convenience function to handle splitting a string that potentially has
// escaped delimitor characters.
// We need to split the string while still maintaining those escaped characters.
// So first, replace escaped characters with "||escaped-delim||"
// Then, replace any remaining delimitor characters with "||regular-delim||"
// Then, put the escaped characters back, by replacing "||escaped-delim||" with "\\" + sDelimitor
// And finally, split the string by instances of "||regular-delim||"
// So for instance, "Thing 1, Thing\,2" becomes "Thing 1||regular-delim||Thing \, 2"
void split_minding_escaped_delims(const RString &sSource, const RString &sDelimitor, std::vector<RString>& asAddit)
{
RString sourceCopy = sSource;
RString escaped_delim = "||escaped-delim||";
RString regular_delim = "||regular-delim||";
sourceCopy.Replace("\\" + sDelimitor, escaped_delim);
sourceCopy.Replace(sDelimitor, regular_delim);
sourceCopy.Replace(escaped_delim, "\\" + sDelimitor);
split(sourceCopy, regular_delim, asAddit);
}
/** /**
* @brief Retrieve the course difficulty based on the string name. * @brief Retrieve the course difficulty based on the string name.
* @param s the name of the difficulty. * @param s the name of the difficulty.
@@ -531,7 +550,8 @@ bool CourseLoaderCRS::ParseCourseSongSelect(const MsdFile::value_t &sParams, Cou
for( unsigned i = 1; i < sParams.params.size(); ++i ) for( unsigned i = 1; i < sParams.params.size(); ++i )
{ {
std::vector<RString> sParamParts; std::vector<RString> sParamParts;
split(sParams[i], "=", sParamParts); split_minding_escaped_delims(sParams[i], "=", sParamParts);
if( sParamParts.size() != 2 ) if( sParamParts.size() != 2 )
{ {
LOG->UserLog( "Course file", sPath, "has an invalid SONGSELECT sub-parameter, \"%s\"", sParams[i].c_str()); LOG->UserLog( "Course file", sPath, "has an invalid SONGSELECT sub-parameter, \"%s\"", sParams[i].c_str());
@@ -688,9 +708,8 @@ bool CourseLoaderCRS::ParseCommaSeparatedList(const RString &sParamValue, std::v
// Because we're dealing with a comma-separated list, we have to handle any escaped commas // Because we're dealing with a comma-separated list, we have to handle any escaped commas
// as a special case. Hopefully nobody writes a song called "||escaped-comma||" // as a special case. Hopefully nobody writes a song called "||escaped-comma||"
RString unescapedParamValue = sParamValue; RString unescapedParamValue = sParamValue;
unescapedParamValue.Replace("\\,", "||escaped-comma||");
unescapedParamValue = SmUnescape(unescapedParamValue); split_minding_escaped_delims(sParamValue, ",", items);
split(unescapedParamValue, ",", items);
if(items.size() == 0) if(items.size() == 0)
{ {
LOG->UserLog( "Course file", sPath, "has an invalid %s parameter, expected at least one sub-parameter, but found none.", sParamName.c_str() ); LOG->UserLog( "Course file", sPath, "has an invalid %s parameter, expected at least one sub-parameter, but found none.", sParamName.c_str() );
@@ -698,7 +717,7 @@ bool CourseLoaderCRS::ParseCommaSeparatedList(const RString &sParamValue, std::v
} }
for (unsigned long i = 0; i < items.size(); i++) for (unsigned long i = 0; i < items.size(); i++)
{ {
items[i].Replace("||escaped-comma||", ","); items[i] = SmUnescape(items[i]);
Trim(items[i]); Trim(items[i]);
} }
dest.insert(dest.end(), items.begin(), items.end()); dest.insert(dest.end(), items.begin(), items.end());
+4 -4
View File
@@ -229,23 +229,23 @@ bool CourseWriterCRS::WriteSongSelectCourseEntry( const CourseEntry &entry, Rage
//TODO: Re-escape everything //TODO: Re-escape everything
if( entry.songCriteria.m_vsSongNames.size() > 0 ) if( entry.songCriteria.m_vsSongNames.size() > 0 )
{ {
RString songNames = join(",", SmEscape( entry.songCriteria.m_vsSongNames, {'\\', ':', ';', '#', ','} )); RString songNames = join(",", SmEscape( entry.songCriteria.m_vsSongNames, {'\\', ':', ';', '#', ',', '='} ));
songSelectParams.push_back(ssprintf("TITLE=%s", songNames.c_str())); songSelectParams.push_back(ssprintf("TITLE=%s", songNames.c_str()));
} }
if( entry.songCriteria.m_vsGroupNames.size() > 0) if( entry.songCriteria.m_vsGroupNames.size() > 0)
{ {
RString groupNames = join(",", SmEscape( entry.songCriteria.m_vsGroupNames, {'\\', ':', ';', '#', ','} )); RString groupNames = join(",", SmEscape( entry.songCriteria.m_vsGroupNames, {'\\', ':', ';', '#', ',', '='} ));
songSelectParams.push_back(ssprintf("GROUP=%s", groupNames.c_str())); songSelectParams.push_back(ssprintf("GROUP=%s", groupNames.c_str()));
} }
if( entry.songCriteria.m_vsArtistNames.size() > 0) if( entry.songCriteria.m_vsArtistNames.size() > 0)
{ {
RString artistNames = join(",", SmEscape( entry.songCriteria.m_vsArtistNames, {'\\', ':', ';', '#', ','} )); RString artistNames = join(",", SmEscape( entry.songCriteria.m_vsArtistNames, {'\\', ':', ';', '#', ',', '='} ));
songSelectParams.push_back(ssprintf("ARTIST=%s", artistNames.c_str())); songSelectParams.push_back(ssprintf("ARTIST=%s", artistNames.c_str()));
} }
if( entry.songCriteria.m_bUseSongAllowedList && if( entry.songCriteria.m_bUseSongAllowedList &&
entry.songCriteria.m_vsSongGenreAllowedList.size() > 0) entry.songCriteria.m_vsSongGenreAllowedList.size() > 0)
{ {
RString genreNames = join(",", SmEscape( entry.songCriteria.m_vsSongGenreAllowedList, {'\\', ':', ';', '#', ','} )); RString genreNames = join(",", SmEscape( entry.songCriteria.m_vsSongGenreAllowedList, {'\\', ':', ';', '#', ',', '='} ));
songSelectParams.push_back(ssprintf("GENRE=%s", genreNames.c_str())); songSelectParams.push_back(ssprintf("GENRE=%s", genreNames.c_str()));
} }
if( entry.stepsCriteria.m_vDifficulties.size() > 0 ) if( entry.stepsCriteria.m_vDifficulties.size() > 0 )