diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 1e7b06f2f7..ca64d50afe 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -597,6 +597,7 @@ AllGroups=All Groups Alternate=Alternate Always=Always Animations=Animations +Arbitrary Remap Columns=Arbitrarily Remap Arcade Style=Arcade Style Ask=Ask AttackMines=AttackMines @@ -1402,6 +1403,7 @@ Enter a new preview start.=Enter when the music sample starts. Enter a new preview length.=Enter how long the music sample lasts. Enter a new min BPM.=Enter the minimum displayed BPM. Enter a new max BPM.=Enter the maximum displayed BPM. +Enter the new track mapping.=Enter the new track mapping. More than %d notes per measure is not allowed. This change has been reverted.=More than %d notes per measure is not allowed. This change has been reverted. No backgrounds available=No backgrounds available EditHelpText=Up/Down:\n change beat\nLeft/Right:\n change snap\nNumber keys:\n add/remove\n tap note\nN and M keys:\n swap tap notes\nCtrl + N/M:\n swap cycled segment\nCtrl + ,/.:\n cycle segments\nCreate hold note:\n Hold a number\n while moving\n Up or Down\nCreate roll note:\n Hold Shift,\n then create a\n hold note.\nSpace bar: Set area\n marker\nT key: Switch Timing\nEnter: Area Menu\nA Key: Alter Menu\nEscape: Main Menu\nF4: Timing Menu\nF1: Show help\n diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp index ce0fcb8d8e..5d0e08cfbe 100644 --- a/src/NoteDataUtil.cpp +++ b/src/NoteDataUtil.cpp @@ -2274,6 +2274,14 @@ void NoteDataUtil::SwapUpDown(NoteData& inout, StepsType st) inout.RevalidateATIs(vector(), false); } +void NoteDataUtil::ArbitraryRemap(NoteData& inout, int* mapping) +{ + NoteData tempND; + tempND.LoadTransformed(inout, inout.GetNumTracks(), mapping); + inout.CopyAll(tempND); + inout.RevalidateATIs(vector(), false); +} + struct ValidRow { diff --git a/src/NoteDataUtil.h b/src/NoteDataUtil.h index 8b6aa382bb..9b25e718b8 100644 --- a/src/NoteDataUtil.h +++ b/src/NoteDataUtil.h @@ -167,6 +167,7 @@ namespace NoteDataUtil void ShiftLeft( NoteData &inout ); void ShiftRight( NoteData &inout ); void SwapUpDown( NoteData &inout, StepsType st ); + void ArbitraryRemap( NoteData &inout, int* mapping ); void SnapToNearestNoteType( NoteData &inout, NoteType nt1, NoteType nt2, int iStartIndex, int iEndIndex ); diff --git a/src/RageUtil.cpp b/src/RageUtil.cpp index 049b07c8a3..872734c1f4 100644 --- a/src/RageUtil.cpp +++ b/src/RageUtil.cpp @@ -1751,6 +1751,16 @@ void MakeLower( wchar_t *p, size_t iLen ) UnicodeUpperLower( p, iLen, g_LowerCase ); } +bool operator>>(const RString& lhs, int& rhs) +{ + return istringstream(lhs) >> rhs; +} + +bool operator>>(const RString& lhs, float& rhs) +{ + return istringstream(lhs) >> rhs; +} + int StringToInt( const RString &sString ) { int ret; diff --git a/src/RageUtil.h b/src/RageUtil.h index e3a83e576f..058727b85f 100644 --- a/src/RageUtil.h +++ b/src/RageUtil.h @@ -418,6 +418,9 @@ RString IntToString( const int &iNum ); float StringToFloat( const RString &sString ); RString FloatToString( const float &num ); bool StringToFloat( const RString &sString, float &fOut ); +// Better than IntToString because you can check for success. +bool operator>>(const RString& lhs, int& rhs); +bool operator>>(const RString& lhs, float& rhs); RString WStringToRString( const wstring &sString ); RString WcharToUTF8( wchar_t c ); diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 57b0dd0a0a..7fe8c4d02e 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -62,6 +62,7 @@ AutoScreenMessage( SM_UpdateTextInfo ); AutoScreenMessage( SM_BackFromMainMenu ); AutoScreenMessage( SM_BackFromAreaMenu ); AutoScreenMessage( SM_BackFromAlterMenu ); +AutoScreenMessage( SM_BackFromArbitraryRemap ); AutoScreenMessage( SM_BackFromStepsInformation ); AutoScreenMessage( SM_BackFromStepsData ); AutoScreenMessage( SM_BackFromOptions ); @@ -616,7 +617,7 @@ static MenuDef g_AlterMenu( MenuRowDef(ScreenEdit::alter, "Alter", true, EditMode_Practice, true, true, 0, "Autogen To Fill Width","Backwards","Swap Sides", "Copy Left To Right","Copy Right To Left","Clear Left","Clear Right", - "Collapse To One","Collapse Left","Shift Left","Shift Right", "Swap Up/Down" ), + "Collapse To One","Collapse Left","Shift Left","Shift Right", "Swap Up/Down", "Arbitrary Remap Columns" ), MenuRowDef(ScreenEdit::tempo, "Tempo", true, EditMode_Full, true, true, 0, "Compress 2x","Compress 3->2", "Compress 4->3","Expand 3->4","Expand 2->3","Expand 2x" ), @@ -3285,6 +3286,10 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) { HandleAlterMenuChoice( (AlterMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); } + else if( SM == SM_BackFromArbitraryRemap ) + { + HandleArbitraryRemapping(ScreenTextEntry::s_sLastAnswer); + } else if( SM == SM_BackFromStepsInformation ) { HandleStepsInformationChoice( (StepsInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); @@ -4532,6 +4537,66 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAns GAMESTATE->SetProcessedTimingData(NULL); } +static LocalizedString ENTER_ARBITRARY_MAPPING( "ScreenEdit", "Enter the new track mapping." ); +static bool ConvertMappingInputToMapping(RString const& mapstr, int* mapping, RString& error) +{ + vector mapping_input; + split(mapstr, ",", mapping_input); + int tracks_for_type= GAMEMAN->GetStepsTypeInfo(GAMESTATE->m_pCurSteps[0]->m_StepsType).iNumTracks; + if(mapping_input.size() > tracks_for_type) + { + error= "Too many tracks specified."; + return false; + } + // mapping_input.size() < tracks_for_type is not checked because + // unspecified tracks are mapped directly. -Kyz + size_t track= 0; + // track will be used for filling in the unspecified part of the mapping. + for(; track < mapping_input.size(); ++track) + { + if(mapping_input[track].empty()) + { + mapping[track]= track; + } + else if(!(mapping_input[track] >> mapping[track])) + { + error= "'" + mapping_input[track] + "' is not a track id."; + return false; + } + if(mapping[track] < 0 || mapping[track] >= tracks_for_type) + { + error= ssprintf("Entry %d, '%s', '%d' is out of range 0 to %d.", track, mapping_input[track].c_str(), mapping[track], tracks_for_type-1); + return false; + } + } + for(; track < tracks_for_type; ++track) + { + mapping[track]= track; + } + return true; +} + +static bool ArbitraryRemapValidate(const RString& answer, RString& error_out) +{ + int mapping[MAX_NOTE_TRACKS]; + return ConvertMappingInputToMapping(answer, mapping, error_out); +} + +void ScreenEdit::HandleArbitraryRemapping(RString const& mapstr) +{ + const NoteData OldClipboard( m_Clipboard ); + HandleAlterMenuChoice( cut, false ); + int mapping[MAX_NOTE_TRACKS]; + RString error; + // error is actually reported by the validate function, and unused here. + if(ConvertMappingInputToMapping(mapstr, mapping, error)) + { + NoteDataUtil::ArbitraryRemap(m_Clipboard, mapping); + } + HandleAreaMenuChoice( paste_at_begin_marker, false ); + m_Clipboard = OldClipboard; +} + void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAnswers, bool bAllowUndo) { ASSERT_M(m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1, @@ -4677,6 +4742,14 @@ void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAn case shift_left: NoteDataUtil::ShiftLeft( m_Clipboard ); break; case shift_right: NoteDataUtil::ShiftRight( m_Clipboard ); break; case swap_up_down: NoteDataUtil::SwapUpDown(m_Clipboard, GAMESTATE->m_pCurSteps[0]->m_StepsType); break; + case arbitrary_remap: + ScreenTextEntry::TextEntry( + SM_BackFromArbitraryRemap, ENTER_ARBITRARY_MAPPING, + "0, 1, 2, 3", MAX_NOTE_TRACKS * 4, + // 2 chars for digit, one for comma, one for space. + ArbitraryRemapValidate + ); + break; } HandleAreaMenuChoice( paste_at_begin_marker, false ); diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 4c6e7b63bf..79836f08de 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -442,6 +442,7 @@ public: modify_keysounds_at_row, /**< Modify the keysounds at this row. */ NUM_AREA_MENU_CHOICES }; + void HandleArbitraryRemapping(RString const& mapstr); void HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAnswers, bool bAllowUndo = true); @@ -502,6 +503,7 @@ public: shift_left, shift_right, swap_up_down, + arbitrary_remap, NUM_ALTER_TYPES }; enum TempoType