@@ -9,7 +9,7 @@ const page_allocator = std.heap.page_allocator;
99const Allocator = std .mem .Allocator ;
1010const ArenaAllocator = std .heap .ArenaAllocator ;
1111const ArrayList = std .ArrayList ;
12- const HashMap = std .hash_map . HashMap ;
12+ const StringArrayHashMap = std .StringArrayHashMap ;
1313const MultiArrayList = std .MultiArrayList ;
1414const Scanner = std .json .Scanner ;
1515const StringHashMap = std .hash_map .StringHashMap ;
@@ -124,6 +124,13 @@ const IniFolder = struct {
124124 folders : ArrayList (IniFolder ),
125125};
126126
127+ // A module space is a list of mappings, ClassName to PresetName-set.
128+ // As well as a module name, taken from the actual folder name.
129+ const ModuleSpace = struct {
130+ entityDefinitions : * StringArrayHashMap (* StringArrayHashMap (void )),
131+ moduleName : []const u8 ,
132+ };
133+
127134/// Updated by `convert()` to record what it is doing.
128135/// If `convert()` crashed, look inside this struct to see why and where it did.
129136pub const Diagnostics = struct {
@@ -292,7 +299,10 @@ pub fn convert(input_folder_path_: []const u8, output_folder_path_: []const u8,
292299
293300 const ini_deinlining_rules = try parseIniDeinliningRules (rules_folder_path , allocator );
294301 std .log .info ("Applying INI de-inlining rules...\n " , .{});
295- try applyIniDeinliningRules (allocator , ini_deinlining_rules , & file_tree );
302+ const entityDefinitionSets : * ArrayList (* ModuleSpace ) = try allocator .create (ArrayList (* ModuleSpace ));
303+ entityDefinitionSets .* = ArrayList (* ModuleSpace ).init (allocator );
304+ try populateEntityDefinitionSets (allocator , & file_tree , entityDefinitionSets );
305+ try applyIniDeinliningRules (allocator , ini_deinlining_rules , & file_tree , entityDefinitionSets );
296306
297307 std .log .info ("Updating INI file tree...\n " , .{});
298308 try updateIniFileTree (& file_tree , allocator );
@@ -1399,15 +1409,80 @@ fn parseIniDeinliningRules(rules_folder_path: []const u8, allocator: Allocator)
13991409 return try parseFromSliceLeaky ([][]const u8 , allocator , text , .{});
14001410}
14011411
1402- fn applyIniDeinliningRules (allocator : Allocator , ini_deinlining_rules : [][]const u8 , file_tree : * IniFolder ) ! void {
1403- for (ini_deinlining_rules ) | property | {
1404- try applyIniDeinliningRulesRecursivelyFolder (allocator , file_tree , property );
1412+ fn populateEntityDefinitionSets (allocator : Allocator , file_tree : * IniFolder , modules : * ArrayList (* ModuleSpace )) ! void {
1413+ for (file_tree .folders .items ) | * folder | {
1414+ const entityDefinitions : * StringArrayHashMap (* StringArrayHashMap (void )) = try allocator .create (StringArrayHashMap (* StringArrayHashMap (void )));
1415+ entityDefinitions .* = StringArrayHashMap (* StringArrayHashMap (void )).init (allocator );
1416+
1417+ try populateEntityDefinitionSetsRecursively (allocator , folder , entityDefinitions );
1418+
1419+ const module : * ModuleSpace = try allocator .create (ModuleSpace );
1420+ module .* = ModuleSpace {
1421+ .moduleName = folder .name ,
1422+ .entityDefinitions = entityDefinitions ,
1423+ };
1424+
1425+ try modules .append (module );
1426+ }
1427+ }
1428+
1429+ fn populateEntityDefinitionSetsRecursively (allocator : Allocator , moduleFolder : * IniFolder , entitySpace : * StringArrayHashMap (* StringArrayHashMap (void ))) ! void {
1430+ for (moduleFolder .folders .items ) | * folder | {
1431+ try populateEntityDefinitionSetsRecursively (allocator , folder , entitySpace );
1432+ }
1433+
1434+ for (moduleFolder .files .items ) | * file | {
1435+ for (file .ast .items ) | * node | {
1436+ try populateEntityDefinitionSetsNode (allocator , node , entitySpace );
1437+ }
14051438 }
14061439}
14071440
1408- fn applyIniDeinliningRulesRecursivelyFolder (allocator : Allocator , file_tree : * IniFolder , property : []const u8 ) ! void {
1441+ fn populateEntityDefinitionSetsNode (allocator : Allocator , serialNode : * Node , entitySpace : * StringArrayHashMap (* StringArrayHashMap (void ))) ! void {
1442+ // Assume this isn't an Entity with a PresetName.
1443+ var presetName : ? []const u8 = null ;
1444+
1445+ // Find out if it does specify a PresetName, and record it if so.
1446+ for (serialNode .children .items ) | * node | {
1447+ if (node .property ) | nodeProperty | {
1448+ if (strEql (nodeProperty , "PresetName" )) {
1449+ if (node .value ) | nodeValue | {
1450+ presetName = nodeValue ;
1451+ }
1452+ }
1453+ }
1454+ }
1455+
1456+ // If this has a specified PresetName, then this is an Entity from this module, which will be useful for accurate de-inlining.
1457+ if (presetName ) | presetNameNonOptional | {
1458+ if (serialNode .value ) | serialNodeValue | {
1459+ var presetSpace : * StringArrayHashMap (void ) = entitySpace .get (serialNodeValue ) orelse emplaceEntitySpace : {
1460+ const presetSpace : * StringArrayHashMap (void ) = try allocator .create (StringArrayHashMap (void ));
1461+ presetSpace .* = StringArrayHashMap (void ).init (allocator );
1462+ try entitySpace .put (serialNodeValue , presetSpace );
1463+ break :emplaceEntitySpace presetSpace ;
1464+ };
1465+
1466+ try presetSpace .put (presetNameNonOptional , {});
1467+ }
1468+ }
1469+
1470+ for (serialNode .children .items ) | * node | {
1471+ try populateEntityDefinitionSetsNode (allocator , node , entitySpace );
1472+ }
1473+ }
1474+
1475+ fn applyIniDeinliningRules (allocator : Allocator , ini_deinlining_rules : [][]const u8 , file_tree : * IniFolder , modules : * ArrayList (* ModuleSpace )) ! void {
1476+ for (file_tree .folders .items , 0.. ) | * moduleFolder , moduleID | {
1477+ for (ini_deinlining_rules ) | property | {
1478+ try applyIniDeinliningRulesRecursivelyFolder (allocator , moduleFolder , property , modules .items [moduleID ]);
1479+ }
1480+ }
1481+ }
1482+
1483+ fn applyIniDeinliningRulesRecursivelyFolder (allocator : Allocator , file_tree : * IniFolder , property : []const u8 , moduleSpace : * ModuleSpace ) ! void {
14091484 for (file_tree .folders .items ) | * folder | {
1410- try applyIniDeinliningRulesRecursivelyFolder (allocator , folder , property );
1485+ try applyIniDeinliningRulesRecursivelyFolder (allocator , folder , property , moduleSpace );
14111486 }
14121487
14131488 for (file_tree .files .items ) | * file | {
@@ -1421,7 +1496,7 @@ fn applyIniDeinliningRulesRecursivelyFolder(allocator: Allocator, file_tree: *In
14211496 // Why necessary? Because some unused inis use the word "Particle" as a root property.
14221497 for (node .children .items ) | * child | {
14231498 // Deinlining a definition into the file will push the current object further forward.
1424- const insertionApplied : bool = try applyIniDeinliningRulesRecursivelyNode (allocator , child , property , file , node .* );
1499+ const insertionApplied : bool = try applyIniDeinliningRulesRecursivelyNode (allocator , child , property , file , node .* , moduleSpace );
14251500
14261501 // We want to read the object again, since we stopped after the deinline, but we also want to read the definition that was deinlined.
14271502 // So decrement i.
@@ -1434,7 +1509,7 @@ fn applyIniDeinliningRulesRecursivelyFolder(allocator: Allocator, file_tree: *In
14341509 }
14351510}
14361511
1437- fn applyIniDeinliningRulesRecursivelyNode (allocator : Allocator , node : * Node , property : []const u8 , file : * IniFile , rootParent : Node ) ! bool {
1512+ fn applyIniDeinliningRulesRecursivelyNode (allocator : Allocator , node : * Node , property : []const u8 , file : * IniFile , rootParent : Node , moduleSpace : * ModuleSpace ) ! bool {
14381513 // If this has a property, and:
14391514 if (node .property ) | node_property | {
14401515 // If it matches the property given, and:
@@ -1472,7 +1547,10 @@ fn applyIniDeinliningRulesRecursivelyNode(allocator: Allocator, node: *Node, pro
14721547 // Assume that nothing is invalidated.
14731548 var invalidationFlag = false ;
14741549 if (presetNameOptional ) | presetName | {
1550+ // Special case Loadouts
14751551 if (! strEql (rootParent .property .? , "AddLoadout" )) {
1552+ moduleName = moduleSpace .moduleName ;
1553+
14761554 const duplicateNode = Node {
14771555 .property = "AddEffect" ,
14781556 .value = node .value ,
@@ -1484,10 +1562,22 @@ fn applyIniDeinliningRulesRecursivelyNode(allocator: Allocator, node: *Node, pro
14841562 try file .ast .insert (determinedIndex , duplicateNode );
14851563
14861564 invalidationFlag = true ;
1565+ } else {
1566+ if (moduleSpace .entityDefinitions .get (className )) | presetNameSpace | {
1567+ if (presetNameSpace .get (presetName ) != null ) {
1568+ moduleName = moduleSpace .moduleName ;
1569+ }
1570+ }
14871571 }
14881572
14891573 node .value = try allocPrint (allocator , "{s}/{s}/{s}" , .{ className , moduleName , presetName });
14901574 } else if (copyOfNameOptional ) | copyOfName | {
1575+ if (moduleSpace .entityDefinitions .get (className )) | presetNameSpace | {
1576+ if (presetNameSpace .get (copyOfName ) != null ) {
1577+ moduleName = moduleSpace .moduleName ;
1578+ }
1579+ }
1580+
14911581 node .value = try allocPrint (allocator , "{s}/{s}/{s}" , .{ className , moduleName , copyOfName });
14921582 } else {
14931583 node .value = "None" ;
@@ -1506,7 +1596,7 @@ fn applyIniDeinliningRulesRecursivelyNode(allocator: Allocator, node: *Node, pro
15061596
15071597 // Since this isn't a malformed constant reference, check the same for this' children.
15081598 for (node .children .items ) | * child | {
1509- const sequenceInvalidated = try applyIniDeinliningRulesRecursivelyNode (allocator , child , property , file , rootParent );
1599+ const sequenceInvalidated = try applyIniDeinliningRulesRecursivelyNode (allocator , child , property , file , rootParent , moduleSpace );
15101600
15111601 // We have discovered an inline definition, and corrected it, which we must immediately signal to the caller.
15121602 if (sequenceInvalidated ) {
@@ -2480,8 +2570,8 @@ fn writeAstRecursively(node: *Node, buffered_writer: anytype, depth: usize) !voi
24802570 }
24812571
24822572 if (node .value ) | value | {
2483- try writeBuffered (buffered_writer , value );
2484- }
2573+ try writeBuffered (buffered_writer , value );
2574+ }
24852575
24862576 if (node .comments .items .len > 0 ) {
24872577 if (node .property != null ) {
0 commit comments