diff --git a/ContentPatcher/Framework/Migrations/BaseMigration.cs b/ContentPatcher/Framework/Migrations/BaseMigration.cs index 1d94ce870..d7953d79f 100644 --- a/ContentPatcher/Framework/Migrations/BaseMigration.cs +++ b/ContentPatcher/Framework/Migrations/BaseMigration.cs @@ -13,10 +13,10 @@ namespace ContentPatcher.Framework.Migrations internal abstract class BaseMigration : IMigration { /********* - ** Private methods + ** Fields *********/ /// The tokens added in this format version. - protected InvariantSet? AddedTokens { get; set; } + protected InvariantSet? AddedTokens; /********* diff --git a/ContentPatcher/Framework/Migrations/BaseRuntimeMigration.cs b/ContentPatcher/Framework/Migrations/BaseRuntimeMigration.cs index a19710a41..87d61a401 100644 --- a/ContentPatcher/Framework/Migrations/BaseRuntimeMigration.cs +++ b/ContentPatcher/Framework/Migrations/BaseRuntimeMigration.cs @@ -1,4 +1,7 @@ +using System; using System.Diagnostics.CodeAnalysis; +using ContentPatcher.Framework.Conditions; +using ContentPatcher.Framework.Migrations.Internal; using ContentPatcher.Framework.Patches; using StardewModdingAPI; @@ -7,12 +10,30 @@ namespace ContentPatcher.Framework.Migrations /// The base implementation for a format version migrator which overrides patches at runtime. internal abstract class BaseRuntimeMigration : BaseMigration, IRuntimeMigration { + /********* + ** Fields + *********/ + /// The migrators that convert older patches to a newer asset or format. + /// For each edit, the first migrator which applies or returns errors is used. + protected IEditAssetMigrator[] RuntimeEditDataMigrators = Array.Empty(); + + /********* ** Public methods *********/ /// public virtual IAssetName? RedirectTarget(IAssetName assetName, IPatch patch) { + foreach (IEditAssetMigrator migrator in this.RuntimeEditDataMigrators) + { + if (migrator.AppliesTo(assetName)) + { + IAssetName? newName = migrator.RedirectTarget(assetName, patch); + if (newName != null) + return newName; + } + } + return null; } @@ -20,6 +41,18 @@ internal abstract class BaseRuntimeMigration : BaseMigration, IRuntimeMigration public virtual bool TryApplyLoadPatch(LoadPatch patch, IAssetName assetName, [NotNullWhen(true)] ref T? asset, out string? error) where T : notnull { + foreach (IEditAssetMigrator migrator in this.RuntimeEditDataMigrators) + { + if (migrator.AppliesTo(patch.TargetAssetBeforeRedirection ?? assetName)) + { + if (migrator.TryApplyLoadPatch(patch, assetName, ref asset, out error)) + return true; + + if (error != null) + return false; + } + } + error = null; return false; } @@ -28,6 +61,21 @@ public virtual bool TryApplyLoadPatch(LoadPatch patch, IAssetName assetName, public virtual bool TryApplyEditPatch(IPatch patch, IAssetData asset, out string? error) where T : notnull { + if (patch is EditDataPatch editPatch) + { + foreach (IEditAssetMigrator migrator in this.RuntimeEditDataMigrators) + { + if (migrator.AppliesTo(patch.TargetAssetBeforeRedirection ?? asset.Name)) + { + if (migrator.TryApplyEditPatch(editPatch, asset, out error)) + return true; + + if (error != null) + return false; + } + } + } + error = null; return false; } diff --git a/ContentPatcher/Framework/Migrations/Internal/IEditAssetMigrator.cs b/ContentPatcher/Framework/Migrations/Internal/IEditAssetMigrator.cs new file mode 100644 index 000000000..dfc3b715b --- /dev/null +++ b/ContentPatcher/Framework/Migrations/Internal/IEditAssetMigrator.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using ContentPatcher.Framework.Conditions; +using ContentPatcher.Framework.Patches; +using StardewModdingAPI; + +namespace ContentPatcher.Framework.Migrations.Internal +{ + /// A migrator which applies older patches to a newer asset or format. + internal interface IEditAssetMigrator + { + /// Get whether this migration applies to a patch. + /// The asset name to check. If the asset was redirected, this is the asset name before redirection. + bool AppliesTo(IAssetName assetName); + + /// + IAssetName? RedirectTarget(IAssetName assetName, IPatch patch); + + /// + bool TryApplyLoadPatch(LoadPatch patch, IAssetName assetName, [NotNullWhen(true)] ref T? asset, out string? error); + + /// + bool TryApplyEditPatch(EditDataPatch patch, IAssetData asset, out string? error); + } +} diff --git a/ContentPatcher/Framework/Migrations/Internal/RuntimeMigrationHelper.cs b/ContentPatcher/Framework/Migrations/Internal/RuntimeMigrationHelper.cs index 5d182a422..4980cec4e 100644 --- a/ContentPatcher/Framework/Migrations/Internal/RuntimeMigrationHelper.cs +++ b/ContentPatcher/Framework/Migrations/Internal/RuntimeMigrationHelper.cs @@ -14,6 +14,9 @@ internal static class RuntimeMigrationHelper private static readonly Dictionary ParseObjectIdCache = new(); + /********* + ** Public methods + *********/ /// Get the unqualified object ID, if it's a valid object ID. /// The raw item ID, which may be an item query or non-object ID. /// Returns the unqualified object ID, or null if it's not a valid object ID. diff --git a/ContentPatcher/Framework/Migrations/Migration_2_0.cs b/ContentPatcher/Framework/Migrations/Migration_2_0.cs index dd3711ea6..4545a5bc4 100644 --- a/ContentPatcher/Framework/Migrations/Migration_2_0.cs +++ b/ContentPatcher/Framework/Migrations/Migration_2_0.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using ContentPatcher.Framework.Conditions; using ContentPatcher.Framework.ConfigModels; -using ContentPatcher.Framework.Patches; using Pathoschild.Stardew.Common.Utilities; using StardewModdingAPI; @@ -11,14 +10,6 @@ namespace ContentPatcher.Framework.Migrations [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Named for clarity.")] internal partial class Migration_2_0 : BaseRuntimeMigration { - /********* - ** Fields - *********/ - /// The migrators that convert pre-1.6 edit patches to a newer asset or format. - /// For each edit, the first migrator which applies or returns errors is used. - private readonly IEditAssetMigrator[] Migrators; - - /********* ** Public methods *********/ @@ -29,12 +20,8 @@ public Migration_2_0() this.AddedTokens = new InvariantSet( nameof(ConditionType.ModId) ); - this.MigrationWarnings = [ - "Some content packs haven't been updated for Stardew Valley 1.6.0. Content Patcher will try to auto-migrate them, but compatibility isn't guaranteed." - ]; - - this.Migrators = new IEditAssetMigrator[] - { + this.MigrationWarnings = ["Some content packs haven't been updated for Stardew Valley 1.6.0. Content Patcher will try to auto-migrate them, but compatibility isn't guaranteed."]; + this.RuntimeEditDataMigrators = [ new BigCraftableInformationMigrator(), new BlueprintsMigrator(), new BootsMigrator(), @@ -42,7 +29,7 @@ public Migration_2_0() new LocationsMigrator(), new NpcDispositionsMigrator(), new ObjectInformationMigrator() - }; + ]; } /// @@ -63,82 +50,5 @@ public override bool TryMigrate(ref PatchConfig[] patches, [NotNullWhen(false)] return true; } - - /// - public override IAssetName? RedirectTarget(IAssetName assetName, IPatch patch) - { - foreach (IEditAssetMigrator migrator in this.Migrators) - { - if (migrator.AppliesTo(assetName)) - { - IAssetName? newName = migrator.RedirectTarget(assetName, patch); - if (newName != null) - return newName; - } - } - - return base.RedirectTarget(assetName, patch); - } - - /// - public override bool TryApplyLoadPatch(LoadPatch patch, IAssetName assetName, [NotNullWhen(true)] ref T? asset, out string? error) - where T : default - { - foreach (IEditAssetMigrator migrator in this.Migrators) - { - if (migrator.AppliesTo(patch.TargetAssetBeforeRedirection ?? assetName)) - { - if (migrator.TryApplyLoadPatch(patch, assetName, ref asset, out error)) - return true; - - if (error != null) - return false; - } - } - - return base.TryApplyLoadPatch(patch, assetName, ref asset, out error); - } - - /// - public override bool TryApplyEditPatch(IPatch patch, IAssetData asset, out string? error) - { - if (patch is EditDataPatch editPatch) - { - foreach (IEditAssetMigrator migrator in this.Migrators) - { - if (migrator.AppliesTo(patch.TargetAssetBeforeRedirection ?? asset.Name)) - { - if (migrator.TryApplyEditPatch(editPatch, asset, out error)) - return true; - - if (error != null) - return false; - } - } - } - - return base.TryApplyEditPatch(patch, asset, out error); - } - - - /********* - ** Private methods - *********/ - /// The migration logic to apply pre-1.6 edit patches to a new asset or format. - private interface IEditAssetMigrator - { - /// Get whether this migration applies to a patch. - /// The asset name to check. If the asset was redirected, this is the asset name before redirection. - bool AppliesTo(IAssetName assetName); - - /// - IAssetName? RedirectTarget(IAssetName assetName, IPatch patch); - - /// - bool TryApplyLoadPatch(LoadPatch patch, IAssetName assetName, [NotNullWhen(true)] ref T? asset, out string? error); - - /// - bool TryApplyEditPatch(EditDataPatch patch, IAssetData asset, out string? error); - } } }