From bec4fde1b0750fcd2c980daf7bcb85f2f02100ea Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Sat, 9 Sep 2023 21:03:59 -0400 Subject: [PATCH] Plugin support with TextAsset example --- .../AssemblyInfo.cs | 4 ++ .../TextAssetFieldConverter.cs | 26 ++++++++ .../TextAssetPlugin.cs | 24 ++++++++ .../TextAssetTypeTreeConverter.cs | 15 +++++ ...TypeTreeConversion.TextAssetExample.csproj | 13 ++++ TypeTreeConversion.sln | 6 ++ TypeTreeConversion/ConversionPlugin.cs | 5 ++ TypeTreeConversion/FieldConverter.cs | 4 +- TypeTreeConversion/Program.cs | 60 ++++++++++++++++--- TypeTreeConversion/RegisterPluginAttribute.cs | 15 +++++ 10 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 TypeTreeConversion.TextAssetExample/AssemblyInfo.cs create mode 100644 TypeTreeConversion.TextAssetExample/TextAssetFieldConverter.cs create mode 100644 TypeTreeConversion.TextAssetExample/TextAssetPlugin.cs create mode 100644 TypeTreeConversion.TextAssetExample/TextAssetTypeTreeConverter.cs create mode 100644 TypeTreeConversion.TextAssetExample/TypeTreeConversion.TextAssetExample.csproj create mode 100644 TypeTreeConversion/ConversionPlugin.cs create mode 100644 TypeTreeConversion/RegisterPluginAttribute.cs diff --git a/TypeTreeConversion.TextAssetExample/AssemblyInfo.cs b/TypeTreeConversion.TextAssetExample/AssemblyInfo.cs new file mode 100644 index 0000000..d4f2c68 --- /dev/null +++ b/TypeTreeConversion.TextAssetExample/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using TypeTreeConversion; +using TypeTreeConversion.TextAssetExample; + +[assembly: RegisterPlugin] diff --git a/TypeTreeConversion.TextAssetExample/TextAssetFieldConverter.cs b/TypeTreeConversion.TextAssetExample/TextAssetFieldConverter.cs new file mode 100644 index 0000000..e8c6d4e --- /dev/null +++ b/TypeTreeConversion.TextAssetExample/TextAssetFieldConverter.cs @@ -0,0 +1,26 @@ +using AssetsTools.NET; + +namespace TypeTreeConversion.TextAssetExample; + +public class TextAssetFieldConverter : DefaultFieldConverter +{ + public TextAssetFieldConverter(ClassDatabaseFile classDatabase) : base(classDatabase) + { + } + + protected override AssetTypeValueField CreateNewBaseField(int originalTypeID) + { + return base.CreateNewBaseField(49); + } + + protected override void CopyFields(UnityAsset asset, AssetTypeValueField source, AssetTypeValueField destination) + { + CopyFieldsExactly(source, destination); + destination["m_Script"].AsString = $""" + This asset has been replaced with a text asset. + Type ID: {asset.TypeID} + Path ID: {asset.PathID} + Name: {asset.Name} + """; + } +} diff --git a/TypeTreeConversion.TextAssetExample/TextAssetPlugin.cs b/TypeTreeConversion.TextAssetExample/TextAssetPlugin.cs new file mode 100644 index 0000000..01b0bcd --- /dev/null +++ b/TypeTreeConversion.TextAssetExample/TextAssetPlugin.cs @@ -0,0 +1,24 @@ +namespace TypeTreeConversion.TextAssetExample; + +public class TextAssetPlugin : ConversionPlugin +{ + public TextAssetPlugin() + { + Program.RegisterFieldConverters += RegisterFieldConverters; + Program.RegisterTypeTreeReplacers += RegisterTypeTreeReplacers; + } + + private static void RegisterFieldConverters(FieldConverterRegistry registry) + { + TextAssetFieldConverter converter = new(registry.ClassDatabase); + registry.Converters.Clear(); + registry.DefaultConverter = converter; + } + + private static void RegisterTypeTreeReplacers(TypeTreeReplacerRegistry registry) + { + TextAssetTypeTreeConverter converter = new(registry.ClassDatabase); + registry.Replacers.Clear(); + registry.DefaultReplacer = converter; + } +} diff --git a/TypeTreeConversion.TextAssetExample/TextAssetTypeTreeConverter.cs b/TypeTreeConversion.TextAssetExample/TextAssetTypeTreeConverter.cs new file mode 100644 index 0000000..7cf751b --- /dev/null +++ b/TypeTreeConversion.TextAssetExample/TextAssetTypeTreeConverter.cs @@ -0,0 +1,15 @@ +using AssetsTools.NET; + +namespace TypeTreeConversion.TextAssetExample; + +public class TextAssetTypeTreeConverter : DefaultTypeTreeReplacer +{ + public TextAssetTypeTreeConverter(ClassDatabaseFile classDatabase) : base(classDatabase) + { + } + + protected override TypeTreeType CreateReplacement(int originalTypeID) + { + return base.CreateReplacement(49); + } +} diff --git a/TypeTreeConversion.TextAssetExample/TypeTreeConversion.TextAssetExample.csproj b/TypeTreeConversion.TextAssetExample/TypeTreeConversion.TextAssetExample.csproj new file mode 100644 index 0000000..4f4c788 --- /dev/null +++ b/TypeTreeConversion.TextAssetExample/TypeTreeConversion.TextAssetExample.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/TypeTreeConversion.sln b/TypeTreeConversion.sln index 636f077..13a0dbf 100644 --- a/TypeTreeConversion.sln +++ b/TypeTreeConversion.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tpk.TypeTrees.Json", "..\Tp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TypeTreeConversion", "TypeTreeConversion\TypeTreeConversion.csproj", "{57D347F0-7160-40B4-B05E-29830EF0E6D4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeTreeConversion.TextAssetExample", "TypeTreeConversion.TextAssetExample\TypeTreeConversion.TextAssetExample.csproj", "{7D193F1C-1838-4CA0-8018-D237A19CB4B3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {57D347F0-7160-40B4-B05E-29830EF0E6D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {57D347F0-7160-40B4-B05E-29830EF0E6D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {57D347F0-7160-40B4-B05E-29830EF0E6D4}.Release|Any CPU.Build.0 = Release|Any CPU + {7D193F1C-1838-4CA0-8018-D237A19CB4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D193F1C-1838-4CA0-8018-D237A19CB4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D193F1C-1838-4CA0-8018-D237A19CB4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D193F1C-1838-4CA0-8018-D237A19CB4B3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TypeTreeConversion/ConversionPlugin.cs b/TypeTreeConversion/ConversionPlugin.cs new file mode 100644 index 0000000..1236904 --- /dev/null +++ b/TypeTreeConversion/ConversionPlugin.cs @@ -0,0 +1,5 @@ +namespace TypeTreeConversion; + +public abstract class ConversionPlugin +{ +} diff --git a/TypeTreeConversion/FieldConverter.cs b/TypeTreeConversion/FieldConverter.cs index 4651a2a..841cc71 100644 --- a/TypeTreeConversion/FieldConverter.cs +++ b/TypeTreeConversion/FieldConverter.cs @@ -17,14 +17,14 @@ public abstract class FieldConverter AssetTypeValueField baseField = CreateNewBaseField(asset.TypeID); - CopyFields(asset.BaseField, baseField); + CopyFields(asset, asset.BaseField, baseField); asset.BaseField = baseField; } protected abstract AssetTypeValueField CreateNewBaseField(int originalTypeID); - protected virtual void CopyFields(AssetTypeValueField source, AssetTypeValueField destination) + protected virtual void CopyFields(UnityAsset asset, AssetTypeValueField source, AssetTypeValueField destination) { CopyFieldsExactly(source, destination); } diff --git a/TypeTreeConversion/Program.cs b/TypeTreeConversion/Program.cs index 66c1b8f..82097cd 100644 --- a/TypeTreeConversion/Program.cs +++ b/TypeTreeConversion/Program.cs @@ -4,6 +4,7 @@ using System.CommandLine; using System.CommandLine.Builder; using System.CommandLine.Parsing; using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Runtime.CompilerServices; namespace TypeTreeConversion; @@ -17,39 +18,57 @@ public static class Program { RootCommand rootCommand = new() { Description = "TypeTree Conversion" }; - Option inputOption = new Option( + Option inputOption = new( aliases: new[] { "-i", "--input" }, description: "", getDefaultValue: () => null); rootCommand.AddOption(inputOption); - Option outputOption = new Option( + Option outputOption = new( aliases: new[] { "-o", "--output" }, description: "", getDefaultValue: () => null); rootCommand.AddOption(outputOption); - Option typeTreeOption = new Option( + Option typeTreeOption = new( name: "--type-tree", description: "The type tree json for the input file.", getDefaultValue: () => null); rootCommand.AddOption(typeTreeOption); - Option tpkOption = new Option( + Option tpkOption = new( name: "--tpk", description: "The tpk file, available at https://github.com/AssetRipper/Tpk", getDefaultValue: () => null); rootCommand.AddOption(tpkOption); - rootCommand.SetHandler((FileInfo? input, FileInfo? output, FileInfo? typeTree, FileInfo? tpk) => + Option?> pluginOption = new( + name: "--plugin", + description: "A plugin to be loaded.", + getDefaultValue: () => null); + rootCommand.AddOption(pluginOption); + + rootCommand.SetHandler((FileInfo? input, FileInfo? output, FileInfo? typeTree, FileInfo? tpk, List? pluginList) => { - ValidateArguments(input, output, typeTree, tpk); + ValidateArguments(input, output, typeTree, tpk, pluginList); + LoadPlugins(pluginList); Run(input.FullName, output.FullName, typeTree.FullName, tpk.FullName); }, - inputOption, outputOption, typeTreeOption, tpkOption); + inputOption, outputOption, typeTreeOption, tpkOption, pluginOption); new CommandLineBuilder(rootCommand) - .UseDefaults() + .UseVersionOption() + .UseHelp() + .UseEnvironmentVariableDirective() + .UseParseDirective() + .UseSuggestDirective() + .RegisterWithDotnetSuggest() + .UseTypoCorrections() + .UseParseErrorReporting() +#if !DEBUG + .UseExceptionHandler() +#endif + .CancelOnProcessTermination() .Build() .Invoke(args); } @@ -90,7 +109,22 @@ public static class Program Console.WriteLine("Done!"); } - private static void ValidateArguments([NotNull] FileInfo? input, [NotNull] FileInfo? output, [NotNull] FileInfo? typeTree, [NotNull] FileInfo? tpk) + private static void LoadPlugins(List? pluginList) + { + if (pluginList is not null) + { + foreach (FileInfo? plugin in pluginList) + { + Assembly assembly = Assembly.LoadFrom(plugin!.FullName); + foreach (RegisterPluginAttribute attribute in assembly.GetCustomAttributes()) + { + attribute.CreateInstance(); + } + } + } + } + + private static void ValidateArguments([NotNull] FileInfo? input, [NotNull] FileInfo? output, [NotNull] FileInfo? typeTree, [NotNull] FileInfo? tpk, List? pluginList) { ArgumentNullException.ThrowIfNull(input); ArgumentNullException.ThrowIfNull(output); @@ -99,6 +133,14 @@ public static class Program ThrowIfNotExists(input); ThrowIfNotExists(typeTree); ThrowIfNotExists(tpk); + if (pluginList is not null) + { + foreach (FileInfo? plugin in pluginList) + { + ArgumentNullException.ThrowIfNull(plugin); + ThrowIfNotExists(plugin); + } + } } private static void ThrowIfNotExists(FileInfo file, [CallerArgumentExpression(nameof(file))] string? paramName = null) diff --git a/TypeTreeConversion/RegisterPluginAttribute.cs b/TypeTreeConversion/RegisterPluginAttribute.cs new file mode 100644 index 0000000..c47e768 --- /dev/null +++ b/TypeTreeConversion/RegisterPluginAttribute.cs @@ -0,0 +1,15 @@ +namespace TypeTreeConversion; + +public abstract class RegisterPluginAttribute : Attribute +{ + public abstract Type Type { get; } + + internal abstract ConversionPlugin CreateInstance(); +} +[AttributeUsage(AttributeTargets.Assembly)] +public sealed class RegisterPluginAttribute : RegisterPluginAttribute where T : ConversionPlugin, new() +{ + public override Type Type => typeof(T); + + internal override T CreateInstance() => new(); +}