@@ -1014,87 +1014,187 @@ internal void MigrateJson(ref ReadFileJson parsedJson)
10141014 {
10151015 if ( parsedJson . version >= JsonVersion . Version1 )
10161016 return ;
1017-
1018- if ( ( parsedJson . maps ? . Length ?? 0 ) > 0 && parsedJson . version < JsonVersion . Version1 )
1017+
1018+ var maps = parsedJson . maps ;
1019+ if ( maps == null || maps . Length == 0 )
10191020 {
1020- for ( var mi = 0 ; mi < parsedJson . maps . Length ; ++ mi )
1021+ parsedJson . version = JsonVersion . Version1 ;
1022+ return ;
1023+ }
1024+
1025+ for ( int mi = 0 ; mi < maps . Length ; mi ++ )
1026+ {
1027+ var mapJson = maps [ mi ] ;
1028+ if ( mapJson . actions == null || mapJson . actions . Length == 0 )
1029+ continue ;
1030+
1031+ for ( int ai = 0 ; ai < mapJson . actions . Length ; ai ++ )
10211032 {
1022- var mapJson = parsedJson . maps [ mi ] ;
1023- if ( mapJson . actions == null || mapJson . actions . Length == 0 )
1033+ var actionJson = mapJson . actions [ ai ] ;
1034+ var processors = actionJson . processors ;
1035+ if ( string . IsNullOrEmpty ( processors ) )
1036+ continue ;
1037+
1038+ // Find top-level ';' token ranges without altering whitespace.
1039+ var tokenRanges = new List < ( int start , int length ) > ( ) ;
1040+ {
1041+ int depth = 0 , tokenStart = 0 ;
1042+ for ( int i = 0 ; i < processors . Length ; i ++ )
1043+ {
1044+ char c = processors [ i ] ;
1045+ if ( c == '(' )
1046+ depth ++ ;
1047+ else if ( c == ')' )
1048+ depth = Math . Max ( 0 , depth - 1 ) ;
1049+ else if ( c == ';' && depth == 0 )
1050+ {
1051+ int len = i - tokenStart ;
1052+ if ( len > 0 )
1053+ tokenRanges . Add ( ( tokenStart , len ) ) ;
1054+
1055+ tokenStart = i + 1 ;
1056+ }
1057+ }
1058+ if ( tokenStart < processors . Length )
1059+ {
1060+ int len = processors . Length - tokenStart ;
1061+ if ( len > 0 )
1062+ tokenRanges . Add ( ( tokenStart , len ) ) ;
1063+ }
1064+ }
1065+
1066+ if ( tokenRanges . Count == 0 )
10241067 continue ;
1025- for ( var ai = 0 ; ai < mapJson . actions . Length ; ++ ai )
1068+
1069+ bool anyTokenChanged = false ;
1070+ var rebuilt = new System . Text . StringBuilder ( processors . Length + 16 ) ;
1071+ int cursor = 0 ;
1072+
1073+ foreach ( var ( start , length ) in tokenRanges )
10261074 {
1027- var actionJson = mapJson . actions [ ai ] ;
1028- var raw = actionJson . processors ;
1029- if ( string . IsNullOrEmpty ( raw ) )
1075+ if ( start > cursor )
1076+ rebuilt . Append ( processors , cursor , start - cursor ) ;
1077+
1078+ var tokenText = processors . Substring ( start , length ) ;
1079+ var trimmed = tokenText . Trim ( ) ;
1080+
1081+ // If we can't parse, just write the original token back unchanged.
1082+ NameAndParameters nap ;
1083+ try
1084+ {
1085+ nap = NameAndParameters . Parse ( trimmed ) ;
1086+ }
1087+ catch
1088+ {
1089+ rebuilt . Append ( tokenText ) ;
1090+ cursor = start + length ;
10301091 continue ;
1092+ }
10311093
1032- var originalTokens = raw . Split ( new [ ] { ';' } , StringSplitOptions . RemoveEmptyEntries ) . Select ( t => t . Trim ( ) ) . ToList ( ) ;
1094+ var procType = InputSystem . TryGetProcessor ( nap . name ) ;
1095+ if ( procType == null || nap . parameters . Count == 0 )
1096+ {
1097+ rebuilt . Append ( tokenText ) ;
1098+ cursor = start + length ;
1099+ continue ;
1100+ }
10331101
1034- List < NameAndParameters > parsed = null ;
1035- NameAndParameters . ParseMultiple ( raw , ref parsed ) ;
1036- if ( parsed == null || parsed . Count == 0 )
1102+ var enumFields = procType . GetFields ( BindingFlags . Public | BindingFlags . Instance ) . Where ( f => f . FieldType . IsEnum ) . ToArray ( ) ;
1103+ if ( enumFields . Length == 0 )
1104+ {
1105+ rebuilt . Append ( tokenText ) ;
1106+ cursor = start + length ;
10371107 continue ;
1108+ }
10381109
1039- var canPreservePerToken = parsed . Count == originalTokens . Count ;
1040- var rebuiltTokens = canPreservePerToken ? new List < string > ( originalTokens ) : new List < string > ( parsed . Count ) ;
1041- var anyProcessorChanged = false ;
1110+ int leadingWs = tokenText . Length - tokenText . TrimStart ( ) . Length ;
1111+ int trailingWs = tokenText . Length - tokenText . TrimEnd ( ) . Length ;
1112+ string core = tokenText . Substring ( leadingWs , tokenText . Length - leadingWs - trailingWs ) ;
10421113
1043- for ( int pi = 0 ; pi < parsed . Count ; pi ++ )
1114+ bool changedThisToken = false ;
1115+
1116+ foreach ( var field in enumFields )
10441117 {
1045- var nap = parsed [ pi ] ;
1046- var procType = InputSystem . TryGetProcessor ( nap . name ) ;
1047- if ( procType == null || nap . parameters . Count == 0 )
1048- {
1049- if ( ! canPreservePerToken )
1050- rebuiltTokens . Add ( nap . ToString ( ) ) ;
1118+ if ( core . IndexOf ( field . Name + "=" , StringComparison . Ordinal ) < 0 )
10511119 continue ;
1052- }
10531120
1054- var dict = nap . parameters . ToDictionary ( p => p . name , p => p . value . ToString ( ) ) ;
1055- var changedThisProcessor = false ;
1121+ // Map ordinal -> underlying numeric values for the enum.
1122+ var values = Enum . GetValues ( field . FieldType ) ;
1123+ var numeric = new int [ values . Length ] ;
1124+ for ( int i = 0 ; i < values . Length ; i ++ )
1125+ numeric [ i ] = Convert . ToInt32 ( values . GetValue ( i ) ) ;
10561126
1057- // For each enum parameter, if value is an ordinal index, replace with the underlying numeric enum value.
1058- foreach ( var field in procType . GetFields ( BindingFlags . Public | BindingFlags . Instance ) . Where ( f => f . FieldType . IsEnum ) )
1127+ // Replace occurrences of "<Field>=<int>".
1128+ int search = 0 ;
1129+ while ( true )
10591130 {
1060- if ( dict . TryGetValue ( field . Name , out var ordStr ) && int . TryParse ( ordStr , out var ordinal ) )
1131+ int hit = core . IndexOf ( field . Name + "=" , search , StringComparison . Ordinal ) ;
1132+ if ( hit < 0 ) break ;
1133+
1134+ int valStart = hit + field . Name . Length + 1 ;
1135+ int j = valStart ;
1136+ bool neg = false ;
1137+ if ( j < core . Length && core [ j ] == '-' )
10611138 {
1062- var values = Enum . GetValues ( field . FieldType ) . Cast < object > ( ) . ToArray ( ) ;
1063- if ( ordinal >= 0 && ordinal < values . Length )
1064- {
1065- dict [ field . Name ] = Convert . ToInt32 ( values [ ordinal ] ) . ToString ( ) ;
1066- changedThisProcessor = true ;
1067- }
1139+ neg = true ;
1140+ j ++ ;
10681141 }
1069- }
10701142
1071- if ( ! changedThisProcessor )
1072- {
1073- if ( ! canPreservePerToken )
1074- rebuiltTokens . Add ( nap . ToString ( ) ) ;
1075- }
1076- else
1077- {
1078- // Rebuild only this processor’s text.
1079- var paramText = string . Join ( "," , dict . Select ( kv => $ "{ kv . Key } ={ kv . Value } ") ) ;
1080- var migrated = $ "{ nap . name } ({ paramText } )";
1143+ int valEnd = j ;
1144+ while ( valEnd < core . Length && char . IsDigit ( core [ valEnd ] ) )
1145+ valEnd ++ ;
10811146
1082- if ( canPreservePerToken )
1083- rebuiltTokens [ pi ] = migrated ;
1084- else
1085- rebuiltTokens . Add ( migrated ) ;
1147+ if ( valEnd == j )
1148+ {
1149+ search = valStart ;
1150+ continue ;
1151+ }
10861152
1087- anyProcessorChanged = true ;
1153+ var numStr = core . Substring ( valStart , valEnd - valStart ) ;
1154+ if ( int . TryParse ( neg ? "-" + numStr : numStr , out var parsed ) )
1155+ {
1156+ if ( parsed >= 0 && parsed < numeric . Length )
1157+ {
1158+ int underlying = numeric [ parsed ] ;
1159+ if ( underlying != parsed )
1160+ {
1161+ core = core . Substring ( 0 , valStart ) + underlying . ToString ( ) + core . Substring ( valEnd ) ;
1162+ changedThisToken = true ;
1163+ search = valStart + underlying . ToString ( ) . Length ;
1164+ continue ;
1165+ }
1166+ }
1167+ }
1168+
1169+ search = valEnd ;
10881170 }
10891171 }
10901172
1091- // Only touch the processors string if something actually changed.
1092- if ( anyProcessorChanged )
1093- actionJson . processors = string . Join ( ";" , rebuiltTokens ) ;
1094- mapJson . actions [ ai ] = actionJson ;
1173+ if ( changedThisToken )
1174+ {
1175+ anyTokenChanged = true ;
1176+ rebuilt . Append ( tokenText . Substring ( 0 , leadingWs ) ) ;
1177+ rebuilt . Append ( core ) ;
1178+ rebuilt . Append ( tokenText . Substring ( tokenText . Length - trailingWs , trailingWs ) ) ;
1179+ }
1180+ else
1181+ {
1182+ rebuilt . Append ( tokenText ) ;
1183+ }
1184+
1185+ cursor = start + length ;
10951186 }
1096- parsedJson . maps [ mi ] = mapJson ;
1187+
1188+ if ( cursor < processors . Length )
1189+ rebuilt . Append ( processors , cursor , processors . Length - cursor ) ;
1190+
1191+ if ( anyTokenChanged )
1192+ actionJson . processors = rebuilt . ToString ( ) ;
1193+
1194+ mapJson . actions [ ai ] = actionJson ;
10971195 }
1196+
1197+ maps [ mi ] = mapJson ;
10981198 }
10991199 // Bump the version so we never re-migrate
11001200 parsedJson . version = JsonVersion . Version1 ;
0 commit comments