Skip to content

Commit 489f38a

Browse files
committed
Ignore Visual Studio subfolder, modify main to deinline constant references, leave the equal sign on MultiLineText NewLines.
1 parent 59a66cd commit 489f38a

File tree

3 files changed

+170
-2
lines changed

3 files changed

+170
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ zig-cache
44
Cortex-Command-Mod-Converter-Engine.exe
55
Cortex-Command-Mod-Converter-Engine.pdb
66
imgui.ini
7+
/.vs

rules/ini_deinlining_rules.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[
2+
"GibParticle",
3+
"EmittedParticle",
4+
"DeliveryCraft",
5+
"AddCargoItem",
6+
"Particle",
7+
"Shell",
8+
"DebrisMaterial",
9+
"TargetMaterial",
10+
"FrostingMaterial",
11+
"TargetMaterial",
12+
"Material",
13+
"BreakWound",
14+
"ParentBreakWound",
15+
"RegularRound",
16+
"TracerRound",
17+
"EntryWound",
18+
"ExitWound"
19+
]

src/main.zig

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const bufferedReader = std.io.bufferedReader;
2020
const bufferedWriter = std.io.bufferedWriter;
2121
const copyFileAbsolute = std.fs.copyFileAbsolute;
2222
const endsWith = std.mem.endsWith;
23+
const indexOf = std.mem.indexOf;
2324
const eql = std.mem.eql;
2425
const expectEqualStrings = std.testing.expectEqualStrings;
2526
const extension = std.fs.path.extension;
@@ -172,6 +173,10 @@ pub fn main() !void {
172173
std.log.info("Error: Invalid output path", .{});
173174
return err;
174175
},
176+
error.InvalidRulesPath => {
177+
std.log.info("Error: Invalid rules path", .{});
178+
return err;
179+
},
175180
error.UnexpectedToken => {
176181
std.log.info("Error: Unexpected '{s}' at {s}:{}:{}\n", .{
177182
diagnostics.token orelse "null",
@@ -216,7 +221,7 @@ pub fn convert(input_folder_path_: []const u8, output_folder_path_: []const u8,
216221
// https://github.com/ziglang/zig/issues/15607#issue-1698930560
217222
if (!try isValidDirPath(input_folder_path_)) return error.InvalidInputPath;
218223
if (!try isValidDirPath(output_folder_path_)) return error.InvalidOutputPath;
219-
if (!try isValidDirPath(rules_folder_path_)) return error.InvalidOutputPath;
224+
if (!try isValidDirPath(rules_folder_path_)) return error.InvalidRulesPath;
220225

221226
const input_folder_path = try std.fs.realpathAlloc(allocator, input_folder_path_);
222227
const output_folder_path = try std.fs.realpathAlloc(allocator, output_folder_path_);
@@ -285,6 +290,10 @@ pub fn convert(input_folder_path_: []const u8, output_folder_path_: []const u8,
285290
std.log.info("Applying INI SoundContainer rules...\n", .{});
286291
applyIniSoundContainerRules(ini_sound_container_rules, &file_tree);
287292

293+
const ini_deinlining_rules = try parseIniDeinliningRules(rules_folder_path, allocator);
294+
std.log.info("Applying INI de-inlining rules...\n", .{});
295+
try applyIniDeinliningRules(allocator, ini_deinlining_rules, &file_tree);
296+
288297
std.log.info("Updating INI file tree...\n", .{});
289298
try updateIniFileTree(&file_tree, allocator);
290299

@@ -1384,6 +1393,141 @@ fn applyIniSoundContainerRulesRecursivelyNode(node: *Node, property: []const u8)
13841393
}
13851394
}
13861395

1396+
fn parseIniDeinliningRules(rules_folder_path: []const u8, allocator: Allocator) ![][]const u8 {
1397+
const ini_deinlining_rules_path = try join(allocator, &.{ rules_folder_path, "ini_deinlining_rules.json" });
1398+
const text = try readFile(ini_deinlining_rules_path, allocator);
1399+
return try parseFromSliceLeaky([][]const u8, allocator, text, .{});
1400+
}
1401+
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);
1405+
}
1406+
}
1407+
1408+
fn applyIniDeinliningRulesRecursivelyFolder(allocator: Allocator, file_tree: *IniFolder, property: []const u8) !void {
1409+
for (file_tree.folders.items) |*folder| {
1410+
try applyIniDeinliningRulesRecursivelyFolder(allocator, folder, property);
1411+
}
1412+
1413+
for (file_tree.files.items) |*file| {
1414+
// This is kind of hideous. Loop through each item in the list.
1415+
var i: usize = 0;
1416+
while (i < file.ast.items.len) {
1417+
const node: *Node = &file.ast.items[i];
1418+
i = i + 1;
1419+
1420+
// Instead of looping over all nodes, loop over the children of each node.
1421+
// Why necessary? Because some unused inis use the word "Particle" as a root property.
1422+
for (node.children.items) |*child| {
1423+
// Deinlining a definition into the file will push the current object further forward.
1424+
const insertionApplied: bool = try applyIniDeinliningRulesRecursivelyNode(allocator, child, property, file, node.*);
1425+
1426+
// We want to read the object again, since we stopped after the deinline, but we also want to read the definition that was deinlined.
1427+
// So decrement i.
1428+
if (insertionApplied) {
1429+
i = i - 1;
1430+
break;
1431+
}
1432+
}
1433+
}
1434+
}
1435+
}
1436+
1437+
fn applyIniDeinliningRulesRecursivelyNode(allocator: Allocator, node: *Node, property: []const u8, file: *IniFile, rootParent: Node) !bool {
1438+
// If this has a property, and:
1439+
if (node.property) |node_property| {
1440+
// If it matches the property given, and:
1441+
if (strEql(node_property, property)) {
1442+
// If this node has children, then:
1443+
if (node.children.items.len > 0) {
1444+
// This is a currently inlined constant reference, so:
1445+
1446+
// Find if this is a copy and the name of it's copy reference, and if this is a preset and it's preset name.
1447+
var copyOfNameOptional: ?[]const u8 = null;
1448+
var presetNameOptional: ?[]const u8 = null;
1449+
1450+
for (node.children.items) |*child| {
1451+
if (child.property) |child_property| {
1452+
if (strEql(child_property, "CopyOf")) {
1453+
copyOfNameOptional = child.value;
1454+
} else if (strEql(child_property, "PresetName")) {
1455+
presetNameOptional = child.value;
1456+
}
1457+
}
1458+
}
1459+
1460+
// Class name is given, module name is assumed, and probably wrong, but might be conveniently specified already.
1461+
const className = node.value orelse "None";
1462+
var moduleName: []const u8 = "Base.rte";
1463+
1464+
// Find out if it's conveniently specified.
1465+
if (copyOfNameOptional) |nameWithPossibleModulePrefix| {
1466+
if (indexOf(u8, nameWithPossibleModulePrefix, "/")) |modulePrefixEnd| {
1467+
moduleName = nameWithPossibleModulePrefix[0..modulePrefixEnd];
1468+
}
1469+
}
1470+
1471+
// Assume that nothing is invalidated.
1472+
var invalidationFlag = false;
1473+
if (presetNameOptional) |presetName| {
1474+
if (!strEql(rootParent.property.?, "AddLoadout")) {
1475+
const duplicateNode = Node{
1476+
.property = "AddEffect",
1477+
.value = node.value,
1478+
.comments = try node.comments.clone(),
1479+
.children = try node.children.clone(),
1480+
};
1481+
1482+
const determinedIndex = index_of(Node, file.ast.items, rootParent) orelse 0;
1483+
try file.ast.insert(determinedIndex, duplicateNode);
1484+
1485+
invalidationFlag = true;
1486+
}
1487+
1488+
node.value = try allocPrint(allocator, "{s}/{s}/{s}", .{ className, moduleName, presetName });
1489+
} else if (copyOfNameOptional) |copyOfName| {
1490+
node.value = try allocPrint(allocator, "{s}/{s}/{s}", .{ className, moduleName, copyOfName });
1491+
} else {
1492+
node.value = "None";
1493+
}
1494+
1495+
// If comments or children are needed, they belong to the duplicate, in it's definition.
1496+
node.children.clearRetainingCapacity();
1497+
node.comments.clearRetainingCapacity();
1498+
1499+
// This was, in any case, a constant reference, it shouldn't have children, not here.
1500+
// Return, and clarify to caller whether we had to deinline anything.
1501+
return invalidationFlag;
1502+
}
1503+
}
1504+
}
1505+
1506+
// Since this isn't a malformed constant reference, check the same for this' children.
1507+
for (node.children.items) |*child| {
1508+
const sequenceInvalidated = try applyIniDeinliningRulesRecursivelyNode(allocator, child, property, file, rootParent);
1509+
1510+
// We have discovered an inline definition, and corrected it, which we must immediately signal to the caller.
1511+
if (sequenceInvalidated) {
1512+
return true;
1513+
}
1514+
}
1515+
1516+
// This is not a malformed constant reference, neither does it contain any.
1517+
return false;
1518+
}
1519+
1520+
// Stolen off stack exchange, surprised the std library hasn't been updated to remedy the specific problem solved by this
1521+
fn index_of(comptime T: type, slice: []const T, value: T) ?usize {
1522+
for (slice, 0..) |element, index| {
1523+
if (std.meta.eql(value, element)) {
1524+
return index;
1525+
}
1526+
} else {
1527+
return null;
1528+
}
1529+
}
1530+
13871531
fn updateIniFileTree(file_tree: *IniFolder, allocator: Allocator) !void {
13881532
try applyOnNodesAlloc(addGetsHitByMosWhenHeldToShields, file_tree, allocator);
13891533
try applyOnNodesAlloc(addGripStrength, file_tree, allocator);
@@ -2327,10 +2471,14 @@ fn writeAstRecursively(node: *Node, buffered_writer: anytype, depth: usize) !voi
23272471

23282472
if (node.property) |property| {
23292473
try writeBuffered(buffered_writer, property);
2474+
2475+
// Named properties with values, and newline (for empty lines in multilinetext) require the equality symbol
2476+
if (node.value != null or strEql(property, "NewLine")) {
2477+
try writeBuffered(buffered_writer, " = ");
2478+
}
23302479
}
23312480

23322481
if (node.value) |value| {
2333-
try writeBuffered(buffered_writer, " = ");
23342482
try writeBuffered(buffered_writer, value);
23352483
}
23362484

0 commit comments

Comments
 (0)