@@ -94,27 +94,52 @@ public static string RemoveWS(string withWhite)
9494
9595 // ModifyNode applies the ConfigNode mod as a 'patch' to ConfigNode original, then returns the patched ConfigNode.
9696 // it uses FindConfigNodeIn(src, nodeType, nodeName, nodeTag) to recurse.
97- public static ConfigNode ModifyNode ( ConfigNode original , ConfigNode mod )
97+ public ConfigNode ModifyNode ( ConfigNode original , ConfigNode mod )
9898 {
9999 if ( ! IsSane ( original ) || ! IsSane ( mod ) )
100100 {
101- print ( "[ModuleManager] A node has an empty name. Skipping it. Original: " + original . name ) ;
101+ print ( "[ModuleManager] A node has an empty name. Skipping it. Original: " + original ) ;
102102 return original ;
103103 }
104104
105105 ConfigNode newNode = original . CreateCopy ( ) ;
106-
106+
107107 string vals = "[ModuleManager] modding values" ;
108108 foreach ( ConfigNode . Value val in mod . values )
109109 {
110110 vals += "\n " + val . name + "= " + val . value ;
111- if ( val . name [ 0 ] != '@' && val . name [ 0 ] != '!' && val . name [ 0 ] != '%' )
112- newNode . AddValue ( val . name , val . value ) ;
111+
112+ string valName = val . name ;
113+
114+ if ( valName [ 0 ] != '@' && valName [ 0 ] != '!' && valName [ 0 ] != '%' )
115+ {
116+ int index = int . MaxValue ;
117+ if ( valName . Contains ( "," ) && int . TryParse ( valName . Split ( ',' ) [ 1 ] , out index ) )
118+ {
119+ // In this case insert the value at position index (with the same node names)
120+ valName = valName . Split ( ',' ) [ 0 ] ;
121+
122+ string [ ] oldValues = newNode . GetValues ( valName ) ;
123+ if ( index < oldValues . Length )
124+ {
125+ newNode . RemoveValues ( valName ) ;
126+ int i = 0 ;
127+ for ( ; i < index ; ++ i )
128+ newNode . AddValue ( valName , oldValues [ i ] ) ;
129+ newNode . AddValue ( valName , val . value ) ;
130+ for ( ; i < oldValues . Length ; ++ i )
131+ newNode . AddValue ( valName , oldValues [ i ] ) ;
132+ continue ;
133+ }
134+ }
135+
136+ newNode . AddValue ( valName , val . value ) ;
137+ }
113138 else
114139 { // Parsing:
115140 // Format is @key = value or @key *= value or @key += value or @key -= value
116141 // or @key,index = value or @key,index *= value or @key,index += value or @key,index -= value
117- string valName = val . name . Substring ( 1 ) ;
142+ valName = valName . Substring ( 1 ) ;
118143 int index = 0 ;
119144 if ( valName . Contains ( "," ) )
120145 {
@@ -126,12 +151,14 @@ public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
126151 {
127152 string value = val . value ;
128153 char op = ' ' ;
129- if ( val . name . Contains ( " *" ) ) // @key *= val
154+ if ( valName . EndsWith ( " *" ) ) // @key *= val
130155 op = '*' ;
131- else if ( val . name . Contains ( " +" ) ) // @key += val
156+ else if ( valName . EndsWith ( " +" ) ) // @key += val
132157 op = '+' ;
133- else if ( val . name . Contains ( " -" ) ) // @key -= val
158+ else if ( valName . EndsWith ( " -" ) ) // @key -= val
134159 op = '-' ;
160+ else if ( valName . EndsWith ( " ^" ) )
161+ op = '^' ;
135162
136163 if ( op != ' ' )
137164 {
@@ -141,7 +168,19 @@ public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
141168 if ( ovalue != null )
142169 {
143170 double s , os ;
144- if ( double . TryParse ( value , out s ) && double . TryParse ( ovalue , out os ) )
171+ if ( op == '^' )
172+ {
173+ try
174+ {
175+ string [ ] split = value . Split ( value [ 0 ] ) ;
176+ value = Regex . Replace ( ovalue , split [ 1 ] , split [ 2 ] ) ;
177+ }
178+ catch ( Exception ex )
179+ {
180+ print ( "[ModuleManager] Failed to do a regexp replacement: " + mod . name + " : original value=\" " + ovalue + "\" regexp=\" " + value + "\" \n Note - to use regexp, the first char is used to subdivide the string (much like sed)\n " + ex . ToString ( ) ) ;
181+ }
182+ }
183+ else if ( double . TryParse ( value , out s ) && double . TryParse ( ovalue , out os ) )
145184 {
146185 if ( op == '*' )
147186 value = ( s * os ) . ToString ( ) ;
@@ -181,11 +220,25 @@ public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
181220 }
182221
183222 char cmd = subMod . name [ 0 ] ;
184- if ( cmd != '@' && cmd != '!' && cmd != '%' && cmd != '$' )
185- newNode . AddNode ( subMod ) ;
223+ string name = subMod . name ;
224+
225+ if ( cmd != '@' && cmd != '!' && cmd != '%' && cmd != '$' )
226+ {
227+ int index = int . MaxValue ;
228+ if ( name . Contains ( "," ) && int . TryParse ( name . Split ( ',' ) [ 1 ] , out index ) )
229+ {
230+ // In this case insert the value at position index (with the same node names)
231+ subMod . name = name = name . Split ( ',' ) [ 0 ] ;
232+
233+ InsertNode ( newNode , subMod , index ) ;
234+ }
235+ else
236+ {
237+ newNode . AddNode ( subMod ) ;
238+ }
239+ }
186240 else
187241 {
188- string name = subMod . name ;
189242 string cond = "" ;
190243 string tag = "" ;
191244 string nodeType , nodeName ;
@@ -259,14 +312,23 @@ public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
259312 foreach ( ConfigNode subNode in subNodes )
260313 {
261314 msg += " Applying subnode " + subMod . name + "\n " ;
262- if ( cmd != '$' )
263- { // @ and ! both remove the original
264- newNode . nodes . Remove ( subNode ) ;
265- }
266- if ( cmd != '!' )
267- { // @ and $ both add the modified
268- ConfigNode newSubNode = ModifyNode ( subNode , subMod ) ;
269- newNode . nodes . Add ( newSubNode ) ;
315+ ConfigNode newSubNode ;
316+ switch ( cmd ) {
317+ case '@' :
318+ // @ edits in place
319+ newSubNode = ModifyNode ( subNode , subMod ) ;
320+ subNode . ClearData ( ) ;
321+ newSubNode . CopyTo ( subNode ) ;
322+ break ;
323+ case '!' :
324+ // Delete the node
325+ newNode . nodes . Remove ( subNode ) ;
326+ break ;
327+ case '$' :
328+ // Copy the node
329+ newSubNode = ModifyNode ( subNode , subMod ) ;
330+ newNode . nodes . Add ( newSubNode ) ;
331+ break ;
270332 }
271333 }
272334 }
@@ -277,8 +339,8 @@ public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
277339 {
278340 msg += " Applying subnode " + subMod . name + "\n " ;
279341 ConfigNode newSubNode = ModifyNode ( subNodes [ 0 ] , subMod ) ;
280- newNode . nodes . Remove ( subNodes [ 0 ] ) ;
281- newNode . nodes . Add ( newSubNode ) ;
342+ subNodes [ 0 ] . ClearData ( ) ;
343+ newSubNode . CopyTo ( subNodes [ 0 ] ) ;
282344 }
283345 else
284346 { // if not add the mod node without the % in its name
@@ -311,6 +373,27 @@ public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
311373 return newNode ;
312374 }
313375
376+ private static void InsertNode ( ConfigNode newNode , ConfigNode subMod , int index )
377+ {
378+ string modName = subMod . name ;
379+
380+ ConfigNode [ ] oldValues = newNode . GetNodes ( modName ) ;
381+ if ( index < oldValues . Length )
382+ {
383+ newNode . RemoveNodes ( modName ) ;
384+ int i = 0 ;
385+ for ( ; i < index ; ++ i )
386+ newNode . AddNode ( oldValues [ i ] ) ;
387+ newNode . AddNode ( subMod ) ;
388+ for ( ; i < oldValues . Length ; ++ i )
389+ newNode . AddNode ( oldValues [ i ] ) ;
390+ }
391+ else
392+ {
393+ newNode . AddNode ( subMod ) ;
394+ }
395+ }
396+
314397 public static List < UrlDir . UrlConfig > AllConfigsStartingWith ( string match )
315398 {
316399 List < UrlDir . UrlConfig > nodes = new List < UrlDir . UrlConfig > ( ) ;
@@ -454,6 +537,9 @@ orderby ass.GetName().Version descending, a.path ascending
454537 }
455538 log ( modlist ) ;
456539
540+ // Do filtering with NEEDS
541+ CheckNeeds ( excludePaths ) ;
542+
457543 // :First node (and any node without a :pass)
458544 ApplyPatch ( excludePaths , ":FIRST" ) ;
459545
@@ -509,24 +595,25 @@ public void OnGUI()
509595 public void ApplyPatch ( List < String > excludePaths , string Stage )
510596 {
511597 print ( "[ModuleManager] " + Stage + ( Stage == ":FIRST" ? " (default) pass" : " pass" ) ) ;
598+
512599 foreach ( UrlDir . UrlConfig mod in GameDatabase . Instance . root . AllConfigs . ToArray ( ) )
513600 {
514- if ( ! IsBraquetBalanced ( mod . type ) )
515- {
516- print ( "[ModuleManager] Skipping a patch with unbalanced square brackets or a space (replace them with a '?') :\n " + mod . name + "\n " ) ;
517- addErrorFiles ( mod . parent ) ;
518- errorCount ++ ;
519- continue ;
520- }
521-
522- string name = RemoveWS ( mod . type ) ;
523-
524601 int lastErrorCount = errorCount ;
525602
526603 try
527604 {
605+ string name = RemoveWS ( mod . type ) ;
606+
528607 if ( name [ 0 ] == '@' || ( name [ 0 ] == '$' ) || ( name [ 0 ] == '!' ) )
529608 {
609+ if ( ! IsBraquetBalanced ( mod . type ) )
610+ {
611+ print ( "[ModuleManager] Skipping a patch with unbalanced square brackets or a space (replace them with a '?') :\n " + mod . name + "\n " ) ;
612+ addErrorFiles ( mod . parent ) ;
613+ errorCount ++ ;
614+ continue ;
615+ }
616+
530617 // Ensure the stage is correct
531618 int stageIdx = name . IndexOf ( Stage ) ;
532619 if ( stageIdx >= 0 )
@@ -543,13 +630,7 @@ public void ApplyPatch(List<String> excludePaths, string Stage)
543630 }
544631
545632 // TODO: do we want to ensure there's only one phase specifier?
546-
547- if ( ! CheckNeeds ( ref name ) )
548- {
549- print ( "[ModuleManager] Not applying patch " + mod . url + " - unable to satisfy NEEDS" ) ;
550- continue ;
551- }
552-
633+
553634 char [ ] sep = new char [ ] { '[' , ']' } ;
554635 string cond = "" ;
555636
@@ -576,11 +657,11 @@ public void ApplyPatch(List<String> excludePaths, string Stage)
576657 {
577658 print ( "[ModuleManager] Applying node " + mod . url + " to " + url . url ) ;
578659 patchCount ++ ;
579- url . config = ConfigManager . ModifyNode ( url . config , mod . config ) ;
660+ url . config = ModifyNode ( url . config , mod . config ) ;
580661 }
581662 else if ( mod . type [ 0 ] == '$' )
582663 {
583- ConfigNode clone = ConfigManager . ModifyNode ( url . config , mod . config ) ;
664+ ConfigNode clone = ModifyNode ( url . config , mod . config ) ;
584665 if ( url . config . name != mod . name )
585666 {
586667 print ( "[ModuleManager] Copying Node " + url . config . name + " into " + clone . name ) ;
@@ -600,7 +681,31 @@ public void ApplyPatch(List<String> excludePaths, string Stage)
600681 }
601682 }
602683 }
603- else if ( Stage == ":FIRST" && mod . type . Contains ( ":NEEDS[" ) )
684+ }
685+ catch ( Exception e )
686+ {
687+ print ( "[ModuleManager] Exception while processing node : " + mod . url + "\n " + e . ToString ( ) ) ;
688+ addErrorFiles ( mod . parent ) ;
689+ }
690+ finally
691+ {
692+ if ( lastErrorCount < errorCount )
693+ addErrorFiles ( mod . parent , errorCount - lastErrorCount ) ;
694+ }
695+ }
696+ }
697+
698+ private void CheckNeeds ( List < String > excludePaths )
699+ {
700+ // Check the NEEDS parts first.
701+ foreach ( UrlDir . UrlConfig mod in GameDatabase . Instance . root . AllConfigs . ToArray ( ) )
702+ {
703+ try
704+ {
705+ if ( IsPathInList ( mod . url , excludePaths ) )
706+ continue ;
707+
708+ if ( mod . type . Contains ( ":NEEDS[" ) )
604709 {
605710 mod . parent . configs . Remove ( mod ) ;
606711 string type = mod . type ;
@@ -612,25 +717,50 @@ public void ApplyPatch(List<String> excludePaths, string Stage)
612717 continue ;
613718 }
614719 Debug . LogWarning ( type ) ;
615-
720+
616721 ConfigNode copy = new ConfigNode ( type ) ;
617722 mod . config . CopyTo ( copy ) ;
723+ CheckNeeds ( copy ) ;
618724
619725 mod . parent . configs . Add ( new UrlDir . UrlConfig ( mod . parent , copy ) ) ;
620-
726+ }
727+ else
728+ {
729+ CheckNeeds ( mod . config ) ;
621730 }
622731 }
623- catch ( Exception e )
732+ catch ( Exception ex )
624733 {
625- print ( "[ModuleManager] Exception while processing node : " + mod . url + "\n " + e . ToString ( ) ) ;
626- addErrorFiles ( mod . parent ) ;
734+ print ( "[ModuleManager] Exception while checking needs : " + mod . url + "\n " + ex . ToString ( ) ) ;
627735 }
628- finally
736+ }
737+ }
738+
739+ private void CheckNeeds ( ConfigNode subMod )
740+ {
741+ ConfigNode copy = new ConfigNode ( ) ;
742+ for ( int i = 0 ; i < subMod . values . Count ; ++ i )
743+ {
744+ ConfigNode . Value val = subMod . values [ i ] ;
745+ string name = val . name ;
746+ if ( CheckNeeds ( ref name ) )
747+ copy . AddValue ( name , val . value ) ;
748+ }
749+
750+ for ( int i = 0 ; i < subMod . nodes . Count ; ++ i )
751+ {
752+ ConfigNode node = subMod . nodes [ i ] ;
753+ string name = node . name ;
754+ if ( CheckNeeds ( ref name ) )
629755 {
630- if ( lastErrorCount < errorCount )
631- addErrorFiles ( mod . parent , errorCount - lastErrorCount ) ;
756+ node . name = name ;
757+ CheckNeeds ( node ) ;
758+ copy . AddNode ( node ) ;
632759 }
633760 }
761+
762+ subMod . ClearData ( ) ;
763+ copy . CopyTo ( subMod ) ;
634764 }
635765
636766 private bool CheckNeeds ( ref string name )
@@ -669,6 +799,7 @@ private bool CheckNeeds(ref string name)
669799 return true ;
670800 }
671801
802+
672803 public void addErrorFiles ( UrlDir . UrlFile file , int n = 1 )
673804 {
674805 string key = file . url + "." + file . fileExtension ;
0 commit comments