diff --git a/Readme.md b/Readme.md
index 24d3f857..c568a368 100644
--- a/Readme.md
+++ b/Readme.md
@@ -5,8 +5,7 @@ Check the [Wiki](https://github.com/bcssov/IronyModManager/wiki)
All instructions are for Windows.
1. Install Visual Studio 2022 (required for .NET6)
1. Clone the repo to your local machine
-1. Open the LocalizationResourceGenerator solution file located in \Tools\LocalizationResourceGenerator\src
- * Build the solution and copy the binaries to the Tools\LocalizationResourceGenerator folder
+1. Run cmd\build-tools.bat to build the LocalizationResourceGenerator
1. If you don't already have one, create a folder for local NuGet packages and unzip the [CWTools.Irony-Private.0.4.0-alpha8](https://github.com/bcssov/IronyModManager/files/7798143/CWTools.Irony-Private.0.4.0-alpha8.zip) package to it
* This is just an up to date version of CWTools, the one on the public NuGet is older
* Example path: C:\Users\username\code\LocalNuGet
diff --git a/References/CopyAll/Maps/HeartsofIronIVParserMap.json b/References/CopyAll/Maps/HeartsofIronIVParserMap.json
index e0682218..eebbd5e6 100644
--- a/References/CopyAll/Maps/HeartsofIronIVParserMap.json
+++ b/References/CopyAll/Maps/HeartsofIronIVParserMap.json
@@ -325,9 +325,6 @@
}, {
"DirectoryPath": "history\\units",
"PreferredParser": "HOI4WholeTextParser"
- }, {
- "DirectoryPath": "interface",
- "PreferredParser": "GenericGraphicsParser"
}, {
"DirectoryPath": "integrated_dlc\\dlc018_together_for_victory\\gfx\\entities",
"PreferredParser": "GenericGraphicsParser"
@@ -363,9 +360,9 @@
"PreferredParser": "DefaultParser"
}, {
"DirectoryPath": "interface",
- "PreferredParser": "DefaultParser"
+ "PreferredParser": "GenericGraphicsParser"
}, {
- "DirectoryPath": "launcher-assets",
+ "DirectoryPath": "interface",
"PreferredParser": "DefaultParser"
}, {
"DirectoryPath": "localisation",
diff --git a/References/CopyAll/Maps/StellarisParserMap.json b/References/CopyAll/Maps/StellarisParserMap.json
index bdd113b0..3b8e340c 100644
--- a/References/CopyAll/Maps/StellarisParserMap.json
+++ b/References/CopyAll/Maps/StellarisParserMap.json
@@ -271,6 +271,24 @@
}, {
"DirectoryPath": "common\\inline_scripts\\events",
"PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\grand_archive",
+ "PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\grand_archive\\collection",
+ "PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\grand_archive\\mutations",
+ "PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\grand_archive\\mutations\\camouflage",
+ "PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\grand_archive\\mutations\\core_components",
+ "PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\grand_archive\\specimen_upkeeps",
+ "PreferredParser": "StellarisWholeTextParser"
}, {
"DirectoryPath": "common\\inline_scripts\\jobs",
"PreferredParser": "StellarisWholeTextParser"
@@ -292,6 +310,9 @@
}, {
"DirectoryPath": "common\\inline_scripts\\ship_components\\weights",
"PreferredParser": "StellarisWholeTextParser"
+ }, {
+ "DirectoryPath": "common\\inline_scripts\\starbase_modules",
+ "PreferredParser": "StellarisWholeTextParser"
}, {
"DirectoryPath": "common\\inline_scripts\\technologies",
"PreferredParser": "StellarisWholeTextParser"
@@ -337,6 +358,9 @@
}, {
"DirectoryPath": "common\\missions",
"PreferredParser": "DefaultParser"
+ }, {
+ "DirectoryPath": "common\\mutations",
+ "PreferredParser": "DefaultParser"
}, {
"DirectoryPath": "common\\named_colors",
"PreferredParser": "DefaultParser"
@@ -433,6 +457,9 @@
}, {
"DirectoryPath": "common\\ship_behaviors",
"PreferredParser": "GenericKeyParser"
+ }, {
+ "DirectoryPath": "common\\ship_categories",
+ "PreferredParser": "DefaultParser"
}, {
"DirectoryPath": "common\\ship_sizes",
"PreferredParser": "StellarisOverwrittenObjectSingleFileParser"
@@ -484,6 +511,9 @@
}, {
"DirectoryPath": "common\\species_rights\\slavery_types",
"PreferredParser": "StellarisOverwrittenParser"
+ }, {
+ "DirectoryPath": "common\\specimens",
+ "PreferredParser": "DefaultParser"
}, {
"DirectoryPath": "common\\starbase_buildings",
"PreferredParser": "DefaultParser"
@@ -649,6 +679,9 @@
}, {
"DirectoryPath": "gfx\\shipview",
"PreferredParser": "DefaultParser"
+ }, {
+ "DirectoryPath": "gfx\\vivariumview",
+ "PreferredParser": "DefaultParser"
}, {
"DirectoryPath": "gfx\\worldgfx",
"PreferredParser": "GenericKeyParser"
@@ -808,6 +841,21 @@
}, {
"DirectoryPath": "sound\\firstcontact\\ui",
"PreferredParser": "GenericWholeTextParser"
+ }, {
+ "DirectoryPath": "sound\\grand_archive\\events",
+ "PreferredParser": "GenericWholeTextParser"
+ }, {
+ "DirectoryPath": "sound\\grand_archive\\relics",
+ "PreferredParser": "GenericWholeTextParser"
+ }, {
+ "DirectoryPath": "sound\\grand_archive\\ships",
+ "PreferredParser": "GenericWholeTextParser"
+ }, {
+ "DirectoryPath": "sound\\grand_archive\\ui",
+ "PreferredParser": "GenericWholeTextParser"
+ }, {
+ "DirectoryPath": "sound\\grand_archive\\weapons",
+ "PreferredParser": "GenericWholeTextParser"
}, {
"DirectoryPath": "sound\\guardians",
"PreferredParser": "GenericWholeTextParser"
diff --git a/src/IronyModManager.DI/MessageBus/MessageBusDependencyResolver.cs b/src/IronyModManager.DI/MessageBus/MessageBusDependencyResolver.cs
index 3e36d566..5f5cf01a 100644
--- a/src/IronyModManager.DI/MessageBus/MessageBusDependencyResolver.cs
+++ b/src/IronyModManager.DI/MessageBus/MessageBusDependencyResolver.cs
@@ -1,26 +1,26 @@
-
-// ***********************************************************************
+// ***********************************************************************
// Assembly : IronyModManager.DI
// Author : Mario
// Created : 06-10-2020
//
// Last Modified By : Mario
-// Last Modified On : 06-26-2023
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
//
//
// ***********************************************************************
+
using System;
using System.Collections.Generic;
+using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using SlimMessageBus.Host;
namespace IronyModManager.DI.MessageBus
{
-
///
/// Class MessageBusDependencyResolver.
/// Implements the
@@ -34,6 +34,7 @@ internal class MessageBusDependencyResolver : IServiceProvider
/// The message type resolver
///
private readonly MessageTypeResolver messageTypeResolver;
+
///
/// The resolved collections
///
@@ -75,6 +76,11 @@ public object GetService(Type serviceType)
{
return messageTypeResolver;
}
+ else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(serviceType) && typeof(SlimMessageBus.Host.Interceptor.IMessageBusLifecycleInterceptor).IsAssignableFrom(serviceType.GetGenericArguments()[0]))
+ {
+ return null;
+ }
+
if (DIContainer.Container.IsLocked)
{
var obj = DIResolver.Get(serviceType);
@@ -82,11 +88,7 @@ public object GetService(Type serviceType)
}
else
{
- if (resolvedCollections.TryGetValue(serviceType, out var instance))
- {
- return instance();
- }
- return null;
+ return resolvedCollections.TryGetValue(serviceType, out var instance) ? instance() : null;
}
}
diff --git a/src/IronyModManager.Parser.Common/Parsers/BaseParser.cs b/src/IronyModManager.Parser.Common/Parsers/BaseParser.cs
index b0e9b397..890279a8 100644
--- a/src/IronyModManager.Parser.Common/Parsers/BaseParser.cs
+++ b/src/IronyModManager.Parser.Common/Parsers/BaseParser.cs
@@ -4,7 +4,7 @@
// Created : 02-17-2020
//
// Last Modified By : Mario
-// Last Modified On : 10-17-2024
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
@@ -309,6 +309,15 @@ protected virtual IEnumerable ParseComplexTypes(IEnumerable
// Mario
//
//
// ***********************************************************************
+
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using FluentAssertions;
using IronyModManager.DI;
using IronyModManager.Parser.Common;
@@ -39,7 +39,7 @@ public void GetObjectId_should_yield_results()
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(3175);
+ var sb = new StringBuilder(3175);
sb.AppendLine(@"building_giga_megaworkshop_hub_acot_$tier$ = {");
sb.AppendLine(@" base_buildtime = @giga_amb_hub_time_$tier$");
sb.AppendLine(@" category = manufacturing");
@@ -191,7 +191,7 @@ public void GetObjectId_should_yield_results()
sb.AppendLine(@"}");
- var sb2 = new System.Text.StringBuilder(257);
+ var sb2 = new StringBuilder(257);
sb2.AppendLine(@"inline_script = {");
sb2.AppendLine(@" script = ""buildings/building_giga_megaworkshop_acot""");
sb2.AppendLine(@" tier = ""delta""");
@@ -207,13 +207,7 @@ public void GetObjectId_should_yield_results()
var result = parser.Process(sb.ToString(), sb2.ToString());
result.Should().NotBeNullOrEmpty();
var m = DIResolver.Get();
- var parserResult = m.Parse(new ParserManagerArgs()
- {
- File = "common\\buildings\\dummy.txt",
- GameType = "Stellaris",
- IsBinary = false,
- Lines = result.SplitOnNewLine()
- });
+ var parserResult = m.Parse(new ParserManagerArgs { File = "common\\buildings\\dummy.txt", GameType = "Stellaris", IsBinary = false, Lines = result.SplitOnNewLine() });
parserResult.Count().Should().Be(1);
parserResult.FirstOrDefault().Id.Should().Be("building_giga_megaworkshop_hub_acot_delta");
}
@@ -226,7 +220,7 @@ public void GetObjectId_should_yield_results_without_using_parameters()
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(3175);
+ var sb = new StringBuilder(3175);
sb.AppendLine(@"building_giga_megaworkshop_hub_acot_no_parameters = {");
sb.AppendLine(@" base_buildtime = @giga_amb_hub_time_$tier$");
sb.AppendLine(@" category = manufacturing");
@@ -378,7 +372,7 @@ public void GetObjectId_should_yield_results_without_using_parameters()
sb.AppendLine(@"}");
- var sb2 = new System.Text.StringBuilder(257);
+ var sb2 = new StringBuilder(257);
sb2.AppendLine(@"inline_script = {");
sb2.AppendLine(@" script = ""buildings/building_giga_megaworkshop_acot""");
sb2.AppendLine(@" tier = ""delta""");
@@ -394,13 +388,7 @@ public void GetObjectId_should_yield_results_without_using_parameters()
var result = parser.Process(sb.ToString(), sb2.ToString());
result.Should().NotBeNullOrEmpty();
var m = DIResolver.Get();
- var parserResult = m.Parse(new ParserManagerArgs()
- {
- File = "common\\buildings\\dummy.txt",
- GameType = "Stellaris",
- IsBinary = false,
- Lines = result.SplitOnNewLine()
- });
+ var parserResult = m.Parse(new ParserManagerArgs { File = "common\\buildings\\dummy.txt", GameType = "Stellaris", IsBinary = false, Lines = result.SplitOnNewLine() });
parserResult.Count().Should().Be(1);
parserResult.FirstOrDefault().Id.Should().Be("building_giga_megaworkshop_hub_acot_no_parameters");
}
@@ -413,7 +401,7 @@ public void GetObjectId_should_yield_results_using_parameters_with_extra_dolar_s
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(3175);
+ var sb = new StringBuilder(3175);
sb.AppendLine(@"building_giga_megaworkshop_hub_acot_$tier$$ = {");
sb.AppendLine(@" base_buildtime = @giga_amb_hub_time_$tier$");
sb.AppendLine(@" category = manufacturing");
@@ -565,7 +553,7 @@ public void GetObjectId_should_yield_results_using_parameters_with_extra_dolar_s
sb.AppendLine(@"}");
- var sb2 = new System.Text.StringBuilder(257);
+ var sb2 = new StringBuilder(257);
sb2.AppendLine(@"inline_script = {");
sb2.AppendLine(@" script = ""buildings/building_giga_megaworkshop_acot""");
sb2.AppendLine(@" tier = ""delta""");
@@ -582,13 +570,7 @@ public void GetObjectId_should_yield_results_using_parameters_with_extra_dolar_s
result.Should().NotBeNullOrEmpty();
var m = DIResolver.Get();
- var parserResult = m.Parse(new ParserManagerArgs()
- {
- File = "common\\buildings\\dummy.txt",
- GameType = "Stellaris",
- IsBinary = false,
- Lines = result.SplitOnNewLine()
- });
+ var parserResult = m.Parse(new ParserManagerArgs { File = "common\\buildings\\dummy.txt", GameType = "Stellaris", IsBinary = false, Lines = result.SplitOnNewLine() });
parserResult.Count().Should().Be(1);
parserResult.FirstOrDefault().Id.Should().Be("building_giga_megaworkshop_hub_acot_delta$");
}
@@ -601,7 +583,7 @@ public void GetObjectId_should_yield_results_using_parameters_while_including_du
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(3175);
+ var sb = new StringBuilder(3175);
sb.AppendLine(@"building_giga_megaworkshop_hub_acot_$tier$ = {");
sb.AppendLine(@" base_buildtime = @giga_amb_hub_time_$tier$");
sb.AppendLine(@" category = manufacturing");
@@ -755,7 +737,7 @@ public void GetObjectId_should_yield_results_using_parameters_while_including_du
sb.AppendLine(@"}");
- var sb2 = new System.Text.StringBuilder(257);
+ var sb2 = new StringBuilder(257);
sb2.AppendLine(@"inline_script = {");
sb2.AppendLine(@" script = ""buildings/building_giga_megaworkshop_acot""");
sb2.AppendLine(@" tier = ""delta""");
@@ -772,13 +754,7 @@ public void GetObjectId_should_yield_results_using_parameters_while_including_du
result.Should().NotBeNullOrEmpty();
var m = DIResolver.Get();
- var parserResult = m.Parse(new ParserManagerArgs()
- {
- File = "common\\buildings\\dummy.txt",
- GameType = "Stellaris",
- IsBinary = false,
- Lines = result.SplitOnNewLine()
- });
+ var parserResult = m.Parse(new ParserManagerArgs { File = "common\\buildings\\dummy.txt", GameType = "Stellaris", IsBinary = false, Lines = result.SplitOnNewLine() });
parserResult.Count().Should().Be(2);
parserResult.All(p => p.Id == "building_giga_megaworkshop_hub_acot_delta").Should().BeTrue();
}
@@ -791,7 +767,7 @@ public void GetObjectId_should_yield_multiple_results_using_parameters()
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(3175);
+ var sb = new StringBuilder(3175);
sb.AppendLine(@"building_giga_megaworkshop_hub_acot_$tier$ = {");
sb.AppendLine(@" base_buildtime = @giga_amb_hub_time_$tier$");
sb.AppendLine(@" category = manufacturing");
@@ -945,7 +921,7 @@ public void GetObjectId_should_yield_multiple_results_using_parameters()
sb.AppendLine(@"}");
- var sb2 = new System.Text.StringBuilder(257);
+ var sb2 = new StringBuilder(257);
sb2.AppendLine(@"inline_script = {");
sb2.AppendLine(@" script = ""buildings/building_giga_megaworkshop_acot""");
sb2.AppendLine(@" tier = ""delta""");
@@ -962,18 +938,82 @@ public void GetObjectId_should_yield_multiple_results_using_parameters()
result.Should().NotBeNullOrEmpty();
var m = DIResolver.Get();
- var parserResult = m.Parse(new ParserManagerArgs()
- {
- File = "common\\buildings\\dummy.txt",
- GameType = "Stellaris",
- IsBinary = false,
- Lines = result.SplitOnNewLine()
- });
+ var parserResult = m.Parse(new ParserManagerArgs { File = "common\\buildings\\dummy.txt", GameType = "Stellaris", IsBinary = false, Lines = result.SplitOnNewLine() });
parserResult.Count().Should().Be(2);
parserResult.Any(p => p.Id == "building_giga_megaworkshop_hub_acot_delta").Should().BeTrue();
parserResult.Any(p => p.Id == "building_giga_megaworkshop_hub_acot_2_delta").Should().BeTrue();
}
+ ///
+ /// Defines the test method GetObjectId_on_first_levelshould_yield_results.
+ ///
+ [Fact]
+ public void GetObjectId_on_first_level_should_yield_results()
+ {
+ DISetup.SetupContainer();
+
+ var sb = new StringBuilder(700);
+ sb.AppendLine(@"key = ""BIO_PROPULSION_$LEVEL$_$CORRESPONDING_SIZE$""");
+ sb.AppendLine(@"size = small");
+ sb.AppendLine(@"icon = ""GFX_ship_part_bio_thruster_$LEVEL$""");
+ sb.AppendLine(@"icon_frame = 1");
+ sb.AppendLine(@"power = 0");
+ sb.AppendLine(@"");
+ sb.AppendLine(@"resources = {");
+ sb.AppendLine(@" category = ship_components");
+ sb.AppendLine(@" inline_script = {");
+ sb.AppendLine(@" script = ""grand_archive/mutations/component_dynamic_cost""");
+ sb.AppendLine(@" COST = $COST$");
+ sb.AppendLine(@" }");
+ sb.AppendLine(@" cost = {");
+ sb.AppendLine(@" sr_dark_matter = $DARK_MATTER$");
+ sb.AppendLine(@" }");
+ sb.AppendLine(@"}");
+ sb.AppendLine(@"");
+ sb.AppendLine(@"modifier = {");
+ sb.AppendLine(@" ship_base_speed_mult = $SPEED$");
+ sb.AppendLine(@" ship_evasion_add = $EVASION$");
+ sb.AppendLine(@"}");
+ sb.AppendLine(@"");
+ sb.AppendLine(@"prerequisites = { $PREREQUISITE$ }");
+ sb.AppendLine(@"component_set = ""thruster_components_bio""");
+ sb.AppendLine(@"inline_script = {");
+ sb.AppendLine(@" script = grand_archive/mutations/core_components/upgrade_thrusters_bio_$LEVEL$");
+ sb.AppendLine(@" CORRESPONDING_SIZE = $CORRESPONDING_SIZE$");
+ sb.AppendLine(@"}");
+ sb.AppendLine(@"");
+ sb.AppendLine(@"size_restriction = { $SIZE_RESTRICTION$ }");
+ sb.AppendLine(@"");
+ sb.AppendLine(@"ai_weight = {");
+ sb.AppendLine(@" weight = $LEVEL$");
+ sb.AppendLine(@"}");
+
+
+ var sb2 = new StringBuilder(374);
+ sb2.AppendLine(@"utility_component_template = {");
+ sb2.AppendLine(@" inline_script = {");
+ sb2.AppendLine(@" script = grand_archive/mutations/core_components/component_thrusters_bio");
+ sb2.AppendLine(@" LEVEL = 5");
+ sb2.AppendLine(@" CORRESPONDING_SIZE = BATTLESHIP");
+ sb2.AppendLine(@" PREREQUISITE = ""tech_dark_matter_propulsion tech_thrusters_bio_integration""");
+ sb2.AppendLine(@" COST = 384");
+ sb2.AppendLine(@" DARK_MATTER = 4");
+ sb2.AppendLine(@" SPEED = 1");
+ sb2.AppendLine(@" EVASION = 8");
+ sb2.AppendLine(@" SIZE_RESTRICTION = ""space_whale_5 voidworms_large cutholoids""");
+ sb2.AppendLine(@" }");
+ sb2.AppendLine(@"}");
+
+ var parser = new ParametrizedParser(new CodeParser(new Logger()));
+ var result = parser.Process(sb.ToString(), sb2.ToString());
+ result.Should().NotBeNullOrEmpty();
+ var m = DIResolver.Get();
+ var parserResult = m.Parse(new ParserManagerArgs { File = "common\\component_templates\\dummy.txt", GameType = "Stellaris", IsBinary = false, Lines = result.SplitOnNewLine() });
+ parserResult.Count().Should().Be(1);
+ parserResult.Any(p => p.Id.Equals("BIO_PROPULSION_5_BATTLESHIP")).Should().BeTrue();
+ }
+
+
///
/// Defines the test method GetScriptPath_should_yield_results.
///
@@ -982,7 +1022,7 @@ public void GetScriptPath_should_yield_results()
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(257);
+ var sb = new StringBuilder(257);
sb.AppendLine(@"inline_script = {");
sb.AppendLine(@" script = ""buildings/building_giga_megaworkshop_acot""");
sb.AppendLine(@" tier = ""delta""");
@@ -1007,7 +1047,7 @@ public void GetScriptPath_should_not_yield_results()
{
DISetup.SetupContainer();
- var sb = new System.Text.StringBuilder(257);
+ var sb = new StringBuilder(257);
sb.AppendLine(@"inline_script = {");
sb.AppendLine(@" tier = ""delta""");
sb.AppendLine(@"");
@@ -1022,5 +1062,33 @@ public void GetScriptPath_should_not_yield_results()
var result = parser.GetScriptPath(sb.ToString());
result.Should().BeNullOrEmpty();
}
+
+ ///
+ /// Defines the test method GetScriptPath_as_sub_element_should_yield_results.
+ ///
+ [Fact]
+ public void GetScriptPath_as_sub_element_should_yield_results()
+ {
+ DISetup.SetupContainer();
+
+ var sb = new StringBuilder(374);
+ sb.AppendLine(@"utility_component_template = {");
+ sb.AppendLine(@" inline_script = {");
+ sb.AppendLine(@" script = grand_archive/mutations/core_components/component_thrusters_bio");
+ sb.AppendLine(@" LEVEL = 5");
+ sb.AppendLine(@" CORRESPONDING_SIZE = BATTLESHIP");
+ sb.AppendLine(@" PREREQUISITE = ""tech_dark_matter_propulsion tech_thrusters_bio_integration""");
+ sb.AppendLine(@" COST = 384");
+ sb.AppendLine(@" DARK_MATTER = 4");
+ sb.AppendLine(@" SPEED = 1");
+ sb.AppendLine(@" EVASION = 8");
+ sb.AppendLine(@" SIZE_RESTRICTION = ""space_whale_5 voidworms_large cutholoids""");
+ sb.AppendLine(@" }");
+ sb.AppendLine(@"}");
+
+ var parser = new ParametrizedParser(new CodeParser(new Logger()));
+ var result = parser.GetScriptPath(sb.ToString());
+ result.Should().Be("grand_archive\\mutations\\core_components\\component_thrusters_bio");
+ }
}
}
diff --git a/src/IronyModManager.Parser.Tests/StellarisKeyParserTests.cs b/src/IronyModManager.Parser.Tests/StellarisKeyParserTests.cs
index d27df2af..d712113b 100644
--- a/src/IronyModManager.Parser.Tests/StellarisKeyParserTests.cs
+++ b/src/IronyModManager.Parser.Tests/StellarisKeyParserTests.cs
@@ -117,54 +117,5 @@ public void Parse_should_yield_results()
result[i].Type.Should().Be("common\\special_projects\\txt");
}
}
-
- ///
- /// Defines the test method Parse_should_yield_results_with_inline.
- ///
- [Fact]
- public void Parse_should_yield_results_with_inline()
- {
- DISetup.SetupContainer();
-
- var sb = new System.Text.StringBuilder();
- sb.AppendLine(@"special_project = {");
- sb.AppendLine(@" inline_script = {");
- sb.AppendLine(@" script = cosmic_storms/EyeOfTheStormSpecialProjects");
- sb.AppendLine(@" TYPE = nexus_storm");
- sb.AppendLine(@" }");
- sb.AppendLine(@"}");
-
-
-
- var args = new ParserArgs()
- {
- ContentSHA = "sha",
- ModDependencies = new List { "1" },
- File = "common\\special_projects\\fake.txt",
- Lines = sb.ToString().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries),
- ModName = "fake"
- };
- var parser = new Games.Stellaris.KeyParser(new CodeParser(new Logger()), null);
- var result = parser.Parse(args).ToList();
- result.Should().NotBeNullOrEmpty();
- result.Count.Should().Be(1);
- for (int i = 0; i < 1; i++)
- {
- result[i].ContentSHA.Should().Be("sha");
- result[i].Dependencies.First().Should().Be("1");
- result[i].File.Should().Be("common\\special_projects\\fake.txt");
- switch (i)
- {
- case 0:
- result[i].Id.Should().Be("nexus_storm");
- result[i].ValueType.Should().Be(ValueType.Object);
- break;
- default:
- break;
- }
- result[i].ModName.Should().Be("fake");
- result[i].Type.Should().Be("common\\special_projects\\txt");
- }
- }
}
}
diff --git a/src/IronyModManager.Parser/Definitions/Definition.cs b/src/IronyModManager.Parser/Definitions/Definition.cs
index 3d0b39bc..20ef4dff 100644
--- a/src/IronyModManager.Parser/Definitions/Definition.cs
+++ b/src/IronyModManager.Parser/Definitions/Definition.cs
@@ -4,7 +4,7 @@
// Created : 02-16-2020
//
// Last Modified By : Mario
-// Last Modified On : 10-17-2024
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
@@ -247,6 +247,12 @@ public string CodeTag
}
}
+ ///
+ /// Gets or sets a value indicating whether [contains inline identifier].
+ ///
+ /// true if [contains inline identifier]; otherwise, false.
+ public bool ContainsInlineIdentifier { get; set; }
+
///
/// Gets or sets the content sha.
///
@@ -786,6 +792,7 @@ public object GetValue(string propName, bool unwrap)
nameof(UseSimpleValidation) => UseSimpleValidation,
nameof(IsSpecialFolder) => IsSpecialFolder,
nameof(MergeType) => MergeType,
+ nameof(ContainsInlineIdentifier) => ContainsInlineIdentifier,
_ => Id
};
}
diff --git a/src/IronyModManager.Parser/Games/Stellaris/KeyParser.cs b/src/IronyModManager.Parser/Games/Stellaris/KeyParser.cs
index 469f04a5..256891cc 100644
--- a/src/IronyModManager.Parser/Games/Stellaris/KeyParser.cs
+++ b/src/IronyModManager.Parser/Games/Stellaris/KeyParser.cs
@@ -4,7 +4,7 @@
// Created : 09-10-2024
//
// Last Modified By : Mario
-// Last Modified On : 09-10-2024
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
@@ -66,26 +66,7 @@ public override bool CanParse(CanParseArgs args)
/// System.String.
protected override string EvalElementForId(IScriptElement value)
{
- if (value.Key.Equals("key", StringComparison.OrdinalIgnoreCase))
- {
- return value.Value;
- }
- else if (value.Key.Equals("inline_script"))
- {
- foreach (var val in value.Values)
- {
- if (!string.IsNullOrWhiteSpace(val.Key) && !val.Key.Equals("script", StringComparison.OrdinalIgnoreCase))
- {
- // TODO: Fix in later versions for now use best guess
- if (val.Value.CountLetters() >= 3)
- {
- return val.Value;
- }
- }
- }
- }
-
- return base.EvalElementForId(value);
+ return value.Key.Equals("key", StringComparison.OrdinalIgnoreCase) ? value.Value : base.EvalElementForId(value);
}
#endregion Methods
diff --git a/src/IronyModManager.Parser/Generic/KeyParser.cs b/src/IronyModManager.Parser/Generic/KeyParser.cs
index 7a1f1b1c..1521a811 100644
--- a/src/IronyModManager.Parser/Generic/KeyParser.cs
+++ b/src/IronyModManager.Parser/Generic/KeyParser.cs
@@ -4,7 +4,7 @@
// Created : 02-16-2020
//
// Last Modified By : Mario
-// Last Modified On : 10-17-2024
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
@@ -148,7 +148,7 @@ protected virtual bool IsKeyType(CanParseArgs args)
closeBrackets += line.Count(s => s == Constants.Scripts.CloseObject);
if (openBrackets - closeBrackets <= 1 && Constants.Scripts.GenericKeyIds.Any(s => !string.IsNullOrWhiteSpace(GetValue(line, s))))
{
- var bracketLocation = cleaned.IndexOf(Constants.Scripts.OpenObject.ToString());
+ var bracketLocation = cleaned.IndexOf(Constants.Scripts.OpenObject.ToString(), StringComparison.OrdinalIgnoreCase);
var idLoc = -1;
foreach (var item in Constants.Scripts.GenericKeyIds)
{
diff --git a/src/IronyModManager.Parser/ParametrizedParser.cs b/src/IronyModManager.Parser/ParametrizedParser.cs
index 3808df67..a8fcfb60 100644
--- a/src/IronyModManager.Parser/ParametrizedParser.cs
+++ b/src/IronyModManager.Parser/ParametrizedParser.cs
@@ -1,5 +1,4 @@
-
-// ***********************************************************************
+// ***********************************************************************
// Assembly : IronyModManager.Parser
// Author : Mario
// Created : 10-03-2023
@@ -12,6 +11,7 @@
//
//
// ***********************************************************************
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -20,7 +20,6 @@
namespace IronyModManager.Parser
{
-
///
/// Class ParametrizedParser.
/// Implements the
@@ -44,6 +43,7 @@ public class ParametrizedParser : IParametrizedParser
/// The terminator
///
private const char Terminator = '$'; // I'll be back
+
///
/// The code parser
///
@@ -74,18 +74,28 @@ public ParametrizedParser(ICodeParser codeParser)
public string GetScriptPath(string parameters)
{
var elParams = codeParser.ParseScriptWithoutValidation(parameters.SplitOnNewLine(), string.Empty);
- if (elParams != null && elParams.Values != null && elParams.Error == null && elParams.Values.Count(p => p.Key.Equals(Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase)) == 1)
+ if (elParams is { Values: not null, Error: null })
{
- var elObj = elParams.Values.FirstOrDefault(p => p.Values != null);
- if (elObj != null)
+ if (elParams.Values.Count(p => p.Key.Equals(Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase)) == 1)
{
- var match = elObj.Values.FirstOrDefault(p => p.Key.Equals(Script, StringComparison.OrdinalIgnoreCase));
+ var elObj = elParams.Values.FirstOrDefault(p => p.Values != null);
+ var match = elObj?.Values.FirstOrDefault(p => p.Key.Equals(Script, StringComparison.OrdinalIgnoreCase));
if (match != null)
{
- return ((match.Value ?? string.Empty).Trim(Quotes)).StandardizeDirectorySeparator();
+ return (match.Value ?? string.Empty).Trim(Quotes).StandardizeDirectorySeparator();
+ }
+ }
+ else if (elParams.Values.Count() == 1 && elParams.Values.FirstOrDefault()!.Values.Count(p => p.Key.Equals(Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase)) == 1)
+ {
+ var elObj = elParams.Values.FirstOrDefault(p => p.Values != null)?.Values.FirstOrDefault();
+ var match = elObj?.Values.FirstOrDefault(p => p.Key.Equals(Script, StringComparison.OrdinalIgnoreCase));
+ if (match != null)
+ {
+ return (match.Value ?? string.Empty).Trim(Quotes).StandardizeDirectorySeparator();
}
}
}
+
return string.Empty;
}
@@ -98,25 +108,60 @@ public string GetScriptPath(string parameters)
public string Process(string code, string parameters)
{
var elParams = codeParser.ParseScriptWithoutValidation(parameters.SplitOnNewLine(), string.Empty);
- if (elParams != null && elParams.Values != null && elParams.Error == null && elParams.Values.Count(p => p.Key.Equals(Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase)) == 1)
+ if (elParams is { Values: not null, Error: null })
{
- var processed = code;
- var elObj = elParams.Values.FirstOrDefault(p => p.Values != null);
- if (elObj != null)
+ if (elParams.Values.Count(p => p.Key.Equals(Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase)) == 1)
{
- foreach (var value in elObj.Values)
+ var processed = code;
+ var elObj = elParams.Values.FirstOrDefault(p => p.Values != null);
+ if (elObj != null)
{
- var id = (value.Key ?? string.Empty).Trim(Quotes);
- var replacement = (value.Value ?? string.Empty).Trim(Quotes);
- if (!id.Equals(Script, StringComparison.OrdinalIgnoreCase))
+ foreach (var value in elObj.Values)
{
- var key = $"{Terminator}{id}{Terminator}";
- processed = processed.Replace(key, replacement, StringComparison.OrdinalIgnoreCase);
+ var id = (value.Key ?? string.Empty).Trim(Quotes);
+ var replacement = (value.Value ?? string.Empty).Trim(Quotes);
+ if (!id.Equals(Script, StringComparison.OrdinalIgnoreCase))
+ {
+ var key = $"{Terminator}{id}{Terminator}";
+ processed = processed.Replace(key, replacement, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+ }
+
+ return processed;
+ }
+ else if (elParams.Values.Count() == 1 && elParams.Values.FirstOrDefault()!.Values.Count(p => p.Key.Equals(Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase)) == 1)
+ {
+ var processed = code;
+ var elObj = elParams.Values.FirstOrDefault(p => p.Values != null)?.Values.FirstOrDefault();
+ if (elObj != null)
+ {
+ foreach (var value in elObj.Values)
+ {
+ var id = (value.Key ?? string.Empty).Trim(Quotes);
+ var replacement = (value.Value ?? string.Empty).Trim(Quotes);
+ if (!id.Equals(Script, StringComparison.OrdinalIgnoreCase))
+ {
+ var key = $"{Terminator}{id}{Terminator}";
+ processed = processed.Replace(key, replacement, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+
+ var replacementCode = codeParser.ParseScriptWithoutValidation(processed.SplitOnNewLine(), string.Empty);
+ if (replacementCode is { Values: not null, Error: null })
+ {
+ var newCode = elParams.Values.FirstOrDefault(p => p.Values != null);
+ if (newCode != null)
+ {
+ newCode.Values = replacementCode.Values;
+ processed = codeParser.FormatCode(newCode);
+ return processed;
+ }
}
}
}
- return processed;
}
+
return string.Empty;
}
diff --git a/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs b/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs
index 0ce5f891..246357b5 100644
--- a/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs
+++ b/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs
@@ -4,7 +4,7 @@
// Created : 05-27-2021
//
// Last Modified By : Mario
-// Last Modified On : 05-27-2021
+// Last Modified On : 10-30-2024
// ***********************************************************************
//
// Mario
@@ -28,6 +28,7 @@
using IronyModManager.Parser.Common;
using IronyModManager.Parser.Common.Args;
using IronyModManager.Parser.Common.Mod;
+using IronyModManager.Parser.Common.Parsers;
using IronyModManager.Parser.Definitions;
using IronyModManager.Services.Common;
using IronyModManager.Shared.Cache;
@@ -61,12 +62,15 @@ public class GameIndexServiceTests
/// GameIndexService.
private static GameIndexService GetService(Mock gameIndexer, Mock storageProvider, Mock modParser,
Mock parserManager, Mock reader, Mock mapper, Mock modWriter,
- Mock gameService, IEnumerable definitionInfoProviders = null)
+ Mock gameService)
{
+ var providers = new Mock();
+ providers.Setup(p => p.CanProcess(It.IsAny())).Returns(true);
+
var messageBus = new Mock();
messageBus.Setup(p => p.PublishAsync(It.IsAny()));
messageBus.Setup(p => p.Publish(It.IsAny()));
- return new GameIndexService(messageBus.Object, parserManager.Object, gameIndexer.Object, new Cache(), definitionInfoProviders, reader.Object, modWriter.Object, modParser.Object, gameService.Object, storageProvider.Object,
+ return new GameIndexService(null, messageBus.Object, parserManager.Object, gameIndexer.Object, new Cache(), new List() { providers.Object }, reader.Object, modWriter.Object, modParser.Object, gameService.Object, storageProvider.Object,
mapper.Object);
}
diff --git a/src/IronyModManager.Services/GameIndexService.cs b/src/IronyModManager.Services/GameIndexService.cs
index e8797218..18d522af 100644
--- a/src/IronyModManager.Services/GameIndexService.cs
+++ b/src/IronyModManager.Services/GameIndexService.cs
@@ -4,7 +4,7 @@
// Created : 05-27-2021
//
// Last Modified By : Mario
-// Last Modified On : 02-25-2024
+// Last Modified On : 10-30-2024
// ***********************************************************************
//
// Mario
@@ -20,6 +20,7 @@
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
+using IronyModManager.DI;
using IronyModManager.IO.Common.Game;
using IronyModManager.IO.Common.Mods;
using IronyModManager.IO.Common.Readers;
@@ -27,6 +28,7 @@
using IronyModManager.Parser.Common;
using IronyModManager.Parser.Common.Args;
using IronyModManager.Parser.Common.Mod;
+using IronyModManager.Parser.Common.Parsers;
using IronyModManager.Services.Common;
using IronyModManager.Services.Common.MessageBus;
using IronyModManager.Shared;
@@ -35,6 +37,7 @@
using IronyModManager.Shared.Models;
using IronyModManager.Storage.Common;
using Nito.AsyncEx;
+using ValueType = IronyModManager.Shared.Models.ValueType;
namespace IronyModManager.Services
{
@@ -46,6 +49,7 @@ namespace IronyModManager.Services
///
///
public class GameIndexService(
+ IParametrizedParser parametrizedParser,
IMessageBus messageBus,
IParserManager parserManager,
IGameIndexer gameIndexer,
@@ -85,6 +89,16 @@ public class GameIndexService(
///
private readonly IParserManager parserManager = parserManager;
+ ///
+ /// The parametrized parser
+ ///
+ private readonly IParametrizedParser parametrizedParser = parametrizedParser;
+
+ ///
+ /// The inline scripts folder
+ ///
+ private readonly string inlineScriptsFolder = "common\\inline_scripts".StandardizeDirectorySeparator();
+
#endregion Fields
#region Methods
@@ -113,7 +127,10 @@ public virtual async Task IndexDefinitionsAsync(IGame game, IEnumerable p.CanProcess(game.Type));
files = files.Where(p => game.GameFolders.Any(p.StartsWith));
+ var gameInlineScriptFiles = files.Where(p => p.StartsWith(inlineScriptsFolder, StringComparison.OrdinalIgnoreCase));
+ var gameInlineFolders = gameInlineScriptFiles.Select(Path.GetDirectoryName).GroupBy(p => p).Select(p => p.FirstOrDefault()).ToList();
var indexedFolders = (await indexedDefinitions.GetAllDirectoryKeysAsync()).Select(p => p.ToLowerInvariant());
var validFolders = files.Select(Path.GetDirectoryName).GroupBy(p => p).Select(p => p.FirstOrDefault()).Where(p => indexedFolders.Any(a => a.ToLowerInvariant().Equals(p!.ToLowerInvariant())));
var folders = new List();
@@ -125,12 +142,98 @@ public virtual async Task IndexDefinitionsAsync(IGame game, IEnumerable();
+
+ // First index inline scripts folder
+ if (provider is { SupportsInlineScripts: true })
+ {
+ total += gameInlineFolders.Count;
+ var inlineFolders = folders.Where(p => p.StartsWith(inlineScriptsFolder, StringComparison.OrdinalIgnoreCase)).ToList();
+ var inlineTasks = inlineFolders.AsParallel().Select(async folder =>
+ {
+ await semaphore.WaitAsync();
+ try
+ {
+ await Task.Run(async () =>
+ {
+ var result = await ParseGameFiles(game, Reader.Read(Path.Combine(gamePath, folder), searchSubFolders: false), folder, null);
+ if ((result?.Any()).GetValueOrDefault())
+ {
+ await gameIndexer.SaveDefinitionsAsync(GetStoragePath(), game, result);
+ }
+ });
+ using var mutex = await asyncServiceLock.LockAsync();
+ processed++;
+ var perc = GetProgressPercentage(total, processed, 100);
+ if (perc.IsNotNearlyEqual(previousProgress))
+ {
+ await messageBus.PublishAsync(new GameIndexProgressEvent(perc));
+ previousProgress = perc;
+ }
+
+ GCRunner.RunGC(GCCollectionMode.Optimized);
+
+ // ReSharper disable once DisposeOnUsingVariable
+ mutex.Dispose();
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ });
+ await Task.WhenAll(inlineTasks);
+
+ folders = folders.Where(p => !p.StartsWith(inlineScriptsFolder, StringComparison.OrdinalIgnoreCase)).ToList();
+
+ var loadTasks = gameInlineFolders.Select(async directory =>
+ {
+ await semaphore.WaitAsync();
+ try
+ {
+ inlineDefinitions = await LoadDefinitionsInternalAsync(inlineDefinitions, game, versions, null, directory);
+ using var mutex = await asyncServiceLock.LockAsync();
+ processed++;
+ var perc = GetProgressPercentage(total, processed, 100);
+ if (perc.IsNotNearlyEqual(previousProgress))
+ {
+ await messageBus.PublishAsync(new GameIndexProgressEvent(perc));
+ previousProgress = perc;
+ }
+
+ GCRunner.RunGC(GCCollectionMode.Optimized);
+
+ // ReSharper disable once DisposeOnUsingVariable
+ mutex.Dispose();
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ }).ToList();
+ await Task.WhenAll(loadTasks);
+ }
+
var tasks = folders.AsParallel().Select(async folder =>
{
await semaphore.WaitAsync();
@@ -138,7 +241,7 @@ public virtual async Task IndexDefinitionsAsync(IGame game, IEnumerable
{
- var result = ParseGameFiles(game, Reader.Read(Path.Combine(gamePath, folder), searchSubFolders: false), folder);
+ var result = await ParseGameFiles(game, Reader.Read(Path.Combine(gamePath, folder), searchSubFolders: false), folder, inlineDefinitions);
if ((result?.Any()).GetValueOrDefault())
{
await gameIndexer.SaveDefinitionsAsync(GetStoragePath(), game, result);
@@ -155,6 +258,7 @@ await Task.Run(async () =>
GCRunner.RunGC(GCCollectionMode.Optimized);
+ // ReSharper disable once DisposeOnUsingVariable
mutex.Dispose();
}
finally
@@ -162,7 +266,10 @@ await Task.Run(async () =>
semaphore.Release();
}
});
+
await Task.WhenAll(tasks);
+ inlineDefinitions.Dispose();
+ inlineDefinitions = null;
}
return true;
@@ -183,11 +290,31 @@ await Task.Run(async () =>
/// The game languages.
/// IIndexedDefinitions.
public virtual async Task LoadDefinitionsAsync(IIndexedDefinitions modDefinitions, IGame game, IEnumerable versions, IReadOnlyCollection gameLanguages)
+ {
+ return await LoadDefinitionsInternalAsync(modDefinitions, game, versions, gameLanguages);
+ }
+
+ ///
+ /// Load definitions internal as an asynchronous operation.
+ ///
+ /// The mod definitions.
+ /// The game.
+ /// The versions.
+ /// The game languages.
+ /// The directory override.
+ /// A Task<IIndexedDefinitions> representing the asynchronous operation.
+ protected virtual async Task LoadDefinitionsInternalAsync(IIndexedDefinitions modDefinitions, IGame game, IEnumerable versions, IReadOnlyCollection gameLanguages,
+ string directoryOverride = Shared.Constants.EmptyParam)
{
if (game != null && versions != null && versions.Any() && await gameIndexer.GameVersionsSameAsync(GetStoragePath(), game, versions))
{
var gameDefinitions = new ConcurrentBag();
- var directories = await modDefinitions.GetAllDirectoryKeysAsync();
+ var directories = (await modDefinitions.GetAllDirectoryKeysAsync()).ToList();
+ if (!string.IsNullOrWhiteSpace(directoryOverride))
+ {
+ directories = [directoryOverride];
+ }
+
// Kinda need to force insert localisation directory itself
if (directories.Any(p => p.StartsWith(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase)) &&
!directories.Any(p => p.Equals(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase)))
@@ -196,6 +323,18 @@ public virtual async Task LoadDefinitionsAsync(IIndexedDefi
directories = newDirs;
}
+ // No directory override this means we want to ensure inline directories are loaded even if not requested
+ if (string.IsNullOrWhiteSpace(directoryOverride))
+ {
+ var gamePath = PathResolver.GetPath(game);
+ var files = Reader.GetFiles(gamePath);
+ files = files.Where(p => game.GameFolders.Any(p.StartsWith));
+ var gameInlineScriptFiles = files.Where(p => p.StartsWith(inlineScriptsFolder, StringComparison.OrdinalIgnoreCase));
+ var gameInlineFolders = gameInlineScriptFiles.Select(Path.GetDirectoryName).GroupBy(p => p).Select(p => p.FirstOrDefault()).ToList();
+ directories.AddRange(gameInlineFolders);
+ directories = directories.Distinct().ToList();
+ }
+
if (gameLanguages != null && gameLanguages.Count != 0)
{
var folders = gameLanguages.Select(p => p.Type[2..]);
@@ -227,7 +366,7 @@ public virtual async Task LoadDefinitionsAsync(IIndexedDefi
}
double processed = 0;
- double total = directories.Count();
+ double total = directories.Count;
double previousProgress = 0;
using var semaphore = new SemaphoreSlim(MaxFoldersToProcess);
var tasks = directories.Select(async directory =>
@@ -268,7 +407,7 @@ public virtual async Task LoadDefinitionsAsync(IIndexedDefi
processed++;
var perc = GetProgressPercentage(total, processed, 100);
- if (perc.IsNotNearlyEqual(previousProgress))
+ if (perc.IsNotNearlyEqual(previousProgress) && string.IsNullOrWhiteSpace(directoryOverride)) // Directory override present, probably an internal call
{
await messageBus.PublishAsync(new GameDefinitionLoadProgressEvent(perc));
previousProgress = perc;
@@ -324,13 +463,15 @@ protected virtual string GetStoragePath()
/// The file infos.
/// The folder.
/// IEnumerable<IDefinition>.
- protected virtual IEnumerable ParseGameFiles(IGame game, IEnumerable fileInfos, string folder)
+ protected virtual async Task> ParseGameFiles(IGame game, IEnumerable fileInfos, string folder, IIndexedDefinitions inlineDefinitions)
{
if (fileInfos == null)
{
return null;
}
+ var provider = DefinitionInfoProviders.FirstOrDefault(p => p.CanProcess(game.Type));
+
var definitions = new List();
foreach (var fileInfo in fileInfos)
{
@@ -342,14 +483,76 @@ protected virtual IEnumerable ParseGameFiles(IGame game, IEnumerabl
Lines = fileInfo.Content,
ModName = game.Name,
ValidationType = ValidationType.SkipAll
- }).Where(p => p.ValueType != Shared.Models.ValueType.Invalid);
+ }).Where(p => p.ValueType != ValueType.Invalid);
MergeDefinitions(fileDefs);
definitions.AddRange(fileDefs);
}
- return definitions;
- }
+ List prunedInlineDefinitions = null;
+ if (provider!.SupportsInlineScripts && !folder.StandardizeDirectorySeparator().StartsWith(inlineScriptsFolder, StringComparison.OrdinalIgnoreCase))
+ {
+ inlineDefinitions ??= DIResolver.Get();
+ prunedInlineDefinitions = [];
+ foreach (var item in definitions)
+ {
+ var addDefault = true;
+ if (item.Id.Equals(Parser.Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase) || item.ContainsInlineIdentifier)
+ {
+ addDefault = false;
+ var path = Path.Combine(Parser.Common.Constants.Stellaris.InlineScripts, parametrizedParser.GetScriptPath(item.Code));
+ var pathCI = path.ToLowerInvariant();
+ var files = (await inlineDefinitions.GetByParentDirectoryAsync(Path.GetDirectoryName(path))).Where(p => Path.Combine(Path.GetDirectoryName(p.FileCI)!, Path.GetFileNameWithoutExtension(p.FileCI)!).Equals(pathCI))
+ .ToList();
+ if (files.Count != 0)
+ {
+ var priorityDefinition = EvalDefinitionPriorityInternal([.. files.OrderBy(p => p.FileCI, StringComparer.Ordinal)]);
+ if (priorityDefinition is { Definition: not null })
+ {
+ var parametrizedCode = parametrizedParser.Process(priorityDefinition.Definition.Code, item.Code);
+ if (!string.IsNullOrWhiteSpace(parametrizedCode))
+ {
+ var results = parserManager.Parse(new ParserManagerArgs
+ {
+ ContentSHA = item.ContentSHA, // Want original file sha id
+ File = item.File, // To trigger right parser
+ GameType = game.Type,
+ Lines = parametrizedCode.SplitOnNewLine(),
+ FileLastModified = item.LastModified,
+ ModDependencies = item.Dependencies,
+ IsBinary = item.ValueType == ValueType.Binary,
+ ModName = item.ModName,
+ ValidationType = ValidationType.SkipAll
+ });
+ if (item.Variables != null && item.Variables.Any() && results != null)
+ {
+ MergeDefinitions(results.Concat(item.Variables));
+ }
+
+ if (results != null && results.Any())
+ {
+ prunedInlineDefinitions.AddRange(results);
+ }
+ }
+ }
+ }
+ }
- #endregion Methods
+ if (addDefault)
+ {
+ prunedInlineDefinitions.Add(item);
+ }
+ }
+
+ GCRunner.RunGC(GCCollectionMode.Optimized);
+ }
+ else
+ {
+ prunedInlineDefinitions = [.. definitions];
+ }
+
+ return prunedInlineDefinitions;
+ }
}
+
+ #endregion Methods
}
diff --git a/src/IronyModManager.Services/ModPatchCollectionService.cs b/src/IronyModManager.Services/ModPatchCollectionService.cs
index cbe2a72b..ffd0b38f 100644
--- a/src/IronyModManager.Services/ModPatchCollectionService.cs
+++ b/src/IronyModManager.Services/ModPatchCollectionService.cs
@@ -4,7 +4,7 @@
// Created : 05-26-2020
//
// Last Modified By : Mario
-// Last Modified On : 02-25-2024
+// Last Modified On : 10-30-2024
// ***********************************************************************
//
// Mario
@@ -50,8 +50,8 @@ namespace IronyModManager.Services
///
/// The mod patch collection service.
///
- ///
- ///
+ ///
+ ///
public class ModPatchCollectionService(
ICache cache,
IMessageBus messageBus,
@@ -383,6 +383,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition
var allIndexed = DIResolver.Get();
var allDefs = (await indexedDefinitions.GetAllAsync()).ToList();
await allIndexed.InitMapAsync(allDefs);
+ indexedDefinitions.Dispose();
indexedDefinitions = DIResolver.Get();
await indexedDefinitions.InitMapAsync(allDefs.Where(p => p.MergeType == MergeType.None));
@@ -393,6 +394,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition
var overwritten = (await indexedDefinitions.GetByValueTypeAsync(ValueType.OverwrittenObject)).Concat(await indexedDefinitions.GetByValueTypeAsync(ValueType.OverwrittenObjectSingleFile));
var empty = await indexedDefinitions.GetByValueTypeAsync(ValueType.EmptyFile);
var allCount = (await indexedDefinitions.GetAllAsync()).Count();
+ var game = GameService.GetSelected();
double total = (allCount * 2) + typeAndIdKeys.Count() + (overwritten.GroupBy(p => p.TypeAndId).Count() * 2) + empty.Count();
double processed = 0;
@@ -400,7 +402,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition
await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(0));
// Cheers to the guys at paradox for doing a great job at implementing such a thing
- var provider = DefinitionInfoProviders.FirstOrDefault(p => p.CanProcess(GameService.GetSelected().Type));
+ var provider = DefinitionInfoProviders.FirstOrDefault(p => p.CanProcess(game.Type));
if (provider!.SupportsScriptMerge && allCount > 0)
{
var merges = provider.MergeTypes;
@@ -448,6 +450,124 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition
allDefs = null;
GCRunner.RunGC(GCCollectionMode.Optimized, false);
+ // Handling inlines here now as game now uses these a lot -- Thanks pdx again
+ allDefs = (await indexedDefinitions.GetAllAsync()).ToList();
+
+ // Stellaris only (so far)
+ total += provider!.SupportsInlineScripts ? allDefs.Count : 0;
+ previousProgress = 0;
+ List prunedInlineDefinitions;
+ if (provider.SupportsInlineScripts)
+ {
+ var tempIndex = DIResolver.Get();
+ await tempIndex.InitMapAsync(allDefs);
+ prunedInlineDefinitions = [];
+ var reportedInlineErrors = new HashSet();
+ foreach (var item in allDefs)
+ {
+ var addDefault = true;
+ if (item.Id.Equals(Parser.Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase) || item.ContainsInlineIdentifier)
+ {
+ addDefault = false;
+ var path = Path.Combine(Parser.Common.Constants.Stellaris.InlineScripts, parametrizedParser.GetScriptPath(item.Code));
+ var pathCI = path.ToLowerInvariant();
+ var files = (await tempIndex.GetByParentDirectoryAsync(Path.GetDirectoryName(path))).Where(p => Path.Combine(Path.GetDirectoryName(p.FileCI)!, Path.GetFileNameWithoutExtension(p.FileCI)!).Equals(pathCI)).ToList();
+ if (files.Count != 0)
+ {
+ var priorityDefinition = EvalDefinitionPriority([.. files.OrderBy(p => modOrder.IndexOf(p.ModName))]);
+ if (priorityDefinition is { Definition: not null })
+ {
+ var parametrizedCode = parametrizedParser.Process(priorityDefinition.Definition.Code, item.Code);
+ if (!string.IsNullOrWhiteSpace(parametrizedCode))
+ {
+ var validationType = ValidationType.Full;
+ if (item.UseSimpleValidation.GetValueOrDefault() || item.UseSimpleValidation == null)
+ {
+ validationType = MapValidationType(item);
+ }
+ else if (priorityDefinition.Definition.UseSimpleValidation.GetValueOrDefault() || priorityDefinition.Definition.UseSimpleValidation == null)
+ {
+ validationType = MapValidationType(priorityDefinition.Definition);
+ }
+
+ var results = parserManager.Parse(new ParserManagerArgs
+ {
+ ContentSHA = item.ContentSHA, // Want original file sha id
+ File = item.File, // To trigger right parser
+ GameType = game.Type,
+ Lines = parametrizedCode.SplitOnNewLine(),
+ FileLastModified = item.LastModified,
+ ModDependencies = item.Dependencies,
+ IsBinary = item.ValueType == ValueType.Binary,
+ ModName = item.ModName,
+ ValidationType = validationType // This is kinda difficult but try to guess which validation type we want to inherit
+ });
+ if (item.Variables != null && item.Variables.Any() && results != null)
+ {
+ MergeDefinitions(results.Concat(item.Variables));
+ }
+
+ if (results != null && results.Any())
+ {
+ prunedInlineDefinitions.AddRange(results);
+ }
+ else if (!reportedInlineErrors.Contains($"{item.ModName} - {pathCI}"))
+ {
+ // Could happen, will need manually investigation though
+ var copy = CopyDefinition(item);
+ copy.ValueType = ValueType.Invalid;
+ copy.ErrorMessage = $"Inline script {path} failed to be processed. Please report to the author of Irony.";
+ prunedInlineDefinitions.Add(copy);
+ reportedInlineErrors.Add($"{item.ModName} - {pathCI}");
+ }
+ }
+ }
+ }
+ else if (!reportedInlineErrors.Contains($"{item.ModName} - {pathCI}"))
+ {
+ // Need to report missing inline script
+ var copy = CopyDefinition(item);
+ copy.ValueType = ValueType.Invalid;
+ copy.ErrorMessage = $"Inline script {path} is not found in any mods.{Environment.NewLine}{Environment.NewLine}It is possible that the file is missing or due to a syntax error Irony cannot find it.";
+ prunedInlineDefinitions.Add(copy);
+ reportedInlineErrors.Add($"{item.ModName} - {pathCI}");
+ }
+ }
+
+ if (addDefault)
+ {
+ prunedInlineDefinitions.Add(item);
+ }
+
+ processed++;
+ var perc = GetProgressPercentage(total, processed, 100);
+ if (perc.IsNotNearlyEqual(previousProgress))
+ {
+ await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc));
+ previousProgress = perc;
+ }
+ }
+
+ tempIndex.Dispose();
+ }
+ else
+ {
+ prunedInlineDefinitions = [.. allDefs];
+ }
+
+ // Run another cleanup
+ indexedDefinitions.Dispose();
+ indexedDefinitions = null;
+ allDefs.Clear();
+ allDefs = null;
+
+ indexedDefinitions = DIResolver.Get();
+ await indexedDefinitions.InitMapAsync(prunedInlineDefinitions);
+
+ prunedInlineDefinitions.Clear();
+ prunedInlineDefinitions = null;
+ GCRunner.RunGC(GCCollectionMode.Optimized, false);
+
var stopWatch = new Stopwatch();
stopWatch.Start();
@@ -1021,116 +1141,14 @@ public virtual async Task GetModObjectsAsync(IGame game, IE
await messageBus.PublishAsync(new ModDefinitionInvalidReplaceEvent(0));
processed = 0;
-
- // Stellaris only (so far)
- total = provider!.SupportsInlineScripts ? definitions.Count * 2 : definitions.Count;
+ total = definitions.Count;
previousProgress = 0;
+
List prunedDefinitions;
+ List definitionsCopy = [.. definitions];
var patchName = GenerateCollectionPatchName(collectionName);
var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) });
- // Process so far Giga related stuff for now. Scared what else might be valid for inline_scripts.
- List prunedInlineDefinitions;
- if (provider.SupportsInlineScripts)
- {
- var tempIndex = DIResolver.Get();
- await tempIndex.InitMapAsync(definitions);
- prunedInlineDefinitions = [];
- var reportedInlineErrors = new HashSet();
- foreach (var item in definitions)
- {
- var addDefault = true;
- if (item.Id.Equals(Parser.Common.Constants.Stellaris.InlineScriptId, StringComparison.OrdinalIgnoreCase))
- {
- addDefault = false;
- var path = Path.Combine(Parser.Common.Constants.Stellaris.InlineScripts, parametrizedParser.GetScriptPath(item.Code));
- var pathCI = path.ToLowerInvariant();
- var files = (await tempIndex.GetByParentDirectoryAsync(Path.GetDirectoryName(path))).Where(p => Path.Combine(Path.GetDirectoryName(p.FileCI)!, Path.GetFileNameWithoutExtension(p.FileCI)!).Equals(pathCI)).ToList();
- if (files.Count != 0)
- {
- var modOrder = mods.Select(p => p.Name).ToList();
- var priorityDefinition = EvalDefinitionPriority([.. files.OrderBy(p => modOrder.IndexOf(p.ModName))]);
- if (priorityDefinition is { Definition: not null })
- {
- var parametrizedCode = parametrizedParser.Process(priorityDefinition.Definition.Code, item.Code);
- if (!string.IsNullOrWhiteSpace(parametrizedCode))
- {
- var validationType = ValidationType.Full;
- if (item.UseSimpleValidation.GetValueOrDefault() || item.UseSimpleValidation == null)
- {
- validationType = MapValidationType(item);
- }
- else if (priorityDefinition.Definition.UseSimpleValidation.GetValueOrDefault() || priorityDefinition.Definition.UseSimpleValidation == null)
- {
- validationType = MapValidationType(priorityDefinition.Definition);
- }
-
- var results = parserManager.Parse(new ParserManagerArgs
- {
- ContentSHA = item.ContentSHA, // Want original file sha id
- File = item.File, // To trigger right parser
- GameType = game.Type,
- Lines = parametrizedCode.SplitOnNewLine(),
- FileLastModified = item.LastModified,
- ModDependencies = item.Dependencies,
- IsBinary = item.ValueType == ValueType.Binary,
- ModName = item.ModName,
- ValidationType = validationType // This is kinda difficult but try to guess which validation type we want to inherit
- });
- if (item.Variables != null && item.Variables.Any() && results != null)
- {
- MergeDefinitions(results.Concat(item.Variables));
- }
-
- if (results != null && results.Any())
- {
- prunedInlineDefinitions.AddRange(results);
- }
- else if (!reportedInlineErrors.Contains($"{item.ModName} - {pathCI}"))
- {
- // Could happen, will need manually investigation though
- var copy = CopyDefinition(item);
- copy.ValueType = ValueType.Invalid;
- copy.ErrorMessage = $"Inline script {path} failed to be processed. Please report to the author of Irony.";
- prunedInlineDefinitions.Add(copy);
- reportedInlineErrors.Add($"{item.ModName} - {pathCI}");
- }
- }
- }
- }
- else if (!reportedInlineErrors.Contains($"{item.ModName} - {pathCI}"))
- {
- // Need to report missing inline script
- var copy = CopyDefinition(item);
- copy.ValueType = ValueType.Invalid;
- copy.ErrorMessage = $"Inline script {path} is not found in any mods.{Environment.NewLine}{Environment.NewLine}It is possible that the file is missing or due to a syntax error Irony cannot find it.";
- prunedInlineDefinitions.Add(copy);
- reportedInlineErrors.Add($"{item.ModName} - {pathCI}");
- }
- }
-
- if (addDefault)
- {
- prunedInlineDefinitions.Add(item);
- }
-
- processed++;
- var perc = GetProgressPercentage(total, processed, 100);
- if (perc.IsNotNearlyEqual(previousProgress))
- {
- await messageBus.PublishAsync(new ModDefinitionInvalidReplaceEvent(perc));
- previousProgress = perc;
- }
- }
-
- tempIndex.Dispose();
- GCRunner.RunGC(GCCollectionMode.Optimized);
- }
- else
- {
- prunedInlineDefinitions = [.. definitions];
- }
-
definitions.Clear();
definitions = null;
if (state != null && state.CustomConflicts.Any())
@@ -1138,7 +1156,7 @@ public virtual async Task GetModObjectsAsync(IGame game, IE
prunedDefinitions = [];
var customIndexed = DIResolver.Get();
await customIndexed.InitMapAsync(state.CustomConflicts);
- foreach (var item in prunedInlineDefinitions)
+ foreach (var item in definitionsCopy)
{
var addDefault = true;
if (item.ValueType == ValueType.Invalid)
@@ -1197,11 +1215,11 @@ public virtual async Task GetModObjectsAsync(IGame game, IE
}
else
{
- prunedDefinitions = [.. prunedInlineDefinitions];
+ prunedDefinitions = [.. definitionsCopy];
}
- prunedInlineDefinitions.Clear();
- prunedInlineDefinitions = null;
+ definitionsCopy.Clear();
+ definitionsCopy = null;
await messageBus.PublishAsync(new ModDefinitionInvalidReplaceEvent(99.9));
var indexed = DIResolver.Get();
@@ -3110,28 +3128,8 @@ protected virtual IDefinition PartialDefinitionCopy(IDefinition definition, bool
return CopyDefinition(definition);
}
- var copy = DIResolver.Get();
- if (copyAdditionalFilenames)
- {
- copy.AdditionalFileNames = definition.AdditionalFileNames;
- }
-
- copy.DiskFile = definition.DiskFile;
- copy.File = definition.File;
- copy.Id = definition.Id;
- copy.ModName = definition.ModName;
- copy.Tags = definition.Tags;
- copy.Type = definition.Type;
- copy.ValueType = definition.ValueType;
- copy.IsFromGame = definition.IsFromGame;
- copy.Order = definition.Order;
- copy.OriginalFileName = definition.OriginalFileName;
- copy.ResetType = definition.ResetType;
- copy.FileNameSuffix = definition.FileNameSuffix;
- copy.IsPlaceholder = definition.IsPlaceholder;
- copy.UseSimpleValidation = definition.UseSimpleValidation;
- copy.IsSpecialFolder = definition.IsSpecialFolder;
- return copy;
+ var clone = DIResolver.Get();
+ return clone.PartialCloneDefinition(definition, copyAdditionalFilenames);
}
///
diff --git a/src/IronyModManager.Services/ObjectClone.cs b/src/IronyModManager.Services/ObjectClone.cs
index 0dae678f..bc6da5a3 100644
--- a/src/IronyModManager.Services/ObjectClone.cs
+++ b/src/IronyModManager.Services/ObjectClone.cs
@@ -4,7 +4,7 @@
// Created : 05-14-2023
//
// Last Modified By : Mario
-// Last Modified On : 10-17-2024
+// Last Modified On : 10-30-2024
// ***********************************************************************
//
// Mario
@@ -82,9 +82,44 @@ public IDefinition CloneDefinition(IDefinition definition, bool includeCode)
newDefinition.UseSimpleValidation = definition.UseSimpleValidation;
newDefinition.IsSpecialFolder = definition.IsSpecialFolder;
newDefinition.MergeType = definition.MergeType;
+ newDefinition.ContainsInlineIdentifier = definition.ContainsInlineIdentifier;
return newDefinition;
}
+ ///
+ /// Partials the clone definition.
+ ///
+ /// The definition.
+ /// if set to true [copy additional filenames].
+ /// IDefinition.
+ public IDefinition PartialCloneDefinition(IDefinition definition, bool copyAdditionalFilenames = true)
+ {
+ var copy = DIResolver.Get();
+ if (copyAdditionalFilenames)
+ {
+ copy.AdditionalFileNames = definition.AdditionalFileNames;
+ }
+
+ copy.DiskFile = definition.DiskFile;
+ copy.File = definition.File;
+ copy.Id = definition.Id;
+ copy.ModName = definition.ModName;
+ copy.Tags = definition.Tags;
+ copy.Type = definition.Type;
+ copy.ValueType = definition.ValueType;
+ copy.IsFromGame = definition.IsFromGame;
+ copy.Order = definition.Order;
+ copy.OriginalFileName = definition.OriginalFileName;
+ copy.ResetType = definition.ResetType;
+ copy.FileNameSuffix = definition.FileNameSuffix;
+ copy.IsPlaceholder = definition.IsPlaceholder;
+ copy.UseSimpleValidation = definition.UseSimpleValidation;
+ copy.IsSpecialFolder = definition.IsSpecialFolder;
+ copy.MergeType = definition.MergeType;
+ copy.ContainsInlineIdentifier = definition.ContainsInlineIdentifier;
+ return copy;
+ }
+
#endregion Methods
}
}
diff --git a/src/IronyModManager.Services/Registrations/GameRegistration.cs b/src/IronyModManager.Services/Registrations/GameRegistration.cs
index 94fc979e..c9ee4f32 100644
--- a/src/IronyModManager.Services/Registrations/GameRegistration.cs
+++ b/src/IronyModManager.Services/Registrations/GameRegistration.cs
@@ -4,7 +4,7 @@
// Created : 02-12-2020
//
// Last Modified By : Mario
-// Last Modified On : 10-17-2024
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
@@ -39,12 +39,12 @@ public class GameRegistration : PostStartup
///
/// The hoi4 cache version
///
- private const int HOI4CacheVersion = 15;
+ private const int HOI4CacheVersion = 16;
///
/// The stellaris cache version
///
- private const int StellarisCacheVersion = 25;
+ private const int StellarisCacheVersion = 26;
///
/// The path resolver
diff --git a/src/IronyModManager.Shared/IObjectClone.cs b/src/IronyModManager.Shared/IObjectClone.cs
index ed3bb355..51921374 100644
--- a/src/IronyModManager.Shared/IObjectClone.cs
+++ b/src/IronyModManager.Shared/IObjectClone.cs
@@ -4,13 +4,14 @@
// Created : 05-14-2023
//
// Last Modified By : Mario
-// Last Modified On : 05-14-2023
+// Last Modified On : 10-30-2024
// ***********************************************************************
//
// Mario
//
//
// ***********************************************************************
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -33,6 +34,14 @@ public interface IObjectClone
/// IDefinition.
public IDefinition CloneDefinition(IDefinition definition, bool includeCode);
+ ///
+ /// Partials the clone definition.
+ ///
+ /// The definition.
+ /// if set to true [copy additional filenames].
+ /// IDefinition.
+ public IDefinition PartialCloneDefinition(IDefinition definition, bool copyAdditionalFilenames = true);
+
#endregion Methods
}
}
diff --git a/src/IronyModManager.Shared/Models/IDefinition.cs b/src/IronyModManager.Shared/Models/IDefinition.cs
index defa4a41..75300594 100644
--- a/src/IronyModManager.Shared/Models/IDefinition.cs
+++ b/src/IronyModManager.Shared/Models/IDefinition.cs
@@ -4,7 +4,7 @@
// Created : 02-16-2020
//
// Last Modified By : Mario
-// Last Modified On : 10-17-2024
+// Last Modified On : 10-29-2024
// ***********************************************************************
//
// Mario
@@ -63,6 +63,12 @@ public interface IDefinition : ICEFIndexedListItem, IQueryableModel
[JsonIgnore]
string CodeTag { get; set; }
+ ///
+ /// Gets or sets a value indicating whether [contains inline identifier].
+ ///
+ /// true if [contains inline identifier]; otherwise, false.
+ bool ContainsInlineIdentifier { get; set; }
+
///
/// Gets or sets the content sha.
///