diff --git a/ExternalProjects/AnalyzersCommon/RoslynUtils.cs b/ExternalProjects/AnalyzersCommon/RoslynUtils.cs
index cc8b60d094..7d3e3fea55 100644
--- a/ExternalProjects/AnalyzersCommon/RoslynUtils.cs
+++ b/ExternalProjects/AnalyzersCommon/RoslynUtils.cs
@@ -1,9 +1,9 @@
-namespace BizHawk.Analyzers;
-
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace BizHawk.Analyzers;
+
public static class RoslynUtils
{
public static SyntaxNode? EnclosingTypeDeclarationSyntax(this CSharpSyntaxNode node)
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore
new file mode 100644
index 0000000000..4c7473dedd
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/obj
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj
new file mode 100644
index 0000000000..46dbd2596a
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/BizHawk.SrcGen.SettingsUtil.csproj
@@ -0,0 +1,6 @@
+
+
+ netstandard2.0
+
+
+
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs
new file mode 100644
index 0000000000..77151014e5
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/DefaultSetterGenerator.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace BizHawk.SrcGen.SettingsUtil;
+
+[Generator]
+public class DefaultSetterGenerator : ISourceGenerator
+{
+ public class SyntaxReceiver : ISyntaxContextReceiver
+ {
+ public readonly List<(ClassDeclarationSyntax, SemanticModel)> ClassDeclarations = new();
+
+ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
+ {
+ if (context.Node is ClassDeclarationSyntax cds)
+ {
+ ClassDeclarations.Add((cds, context.SemanticModel));
+ }
+ }
+ }
+
+ public void Initialize(GeneratorInitializationContext context)
+ => context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+
+ private static void CreateDefaultSetter(StringBuilder source, INamespaceOrTypeSymbol symbol)
+ {
+ var props = symbol
+ .GetMembers()
+ .Where(m => m.Kind == SymbolKind.Property)
+ .ToImmutableArray();
+
+ source.Append($@"
+ public static void SetDefaultValues({symbol} settings)
+ {{");
+
+ foreach (var prop in props)
+ {
+ var defaultValueAttribute = prop
+ .GetAttributes()
+ .FirstOrDefault(
+ a => a.AttributeClass?.Name == "DefaultValueAttribute");
+
+ var ctorArgs = defaultValueAttribute?.ConstructorArguments;
+ if (!ctorArgs.HasValue)
+ {
+ continue;
+ }
+
+ switch (ctorArgs.Value.Length)
+ {
+ case 1:
+ // this single arg is just the value assigned to the default value
+ var arg = ctorArgs.Value[0];
+ // a bit lame, but it'll work
+ // TODO: do we even want to handle arrays? do we even have any arrays in default values???
+ var converionStr = arg.Kind == TypedConstantKind.Array
+ ? $"new {arg.Type} " // new T[]
+ : ""; // do we need a cast (i.e. (T)) here? probably not?
+ source.Append($@"
+ settings.{prop.Name} = {converionStr}{arg.ToCSharpString()};");
+ break;
+ case 2:
+ // first arg is the type, the second arg is a string which converts it
+ source.Append($@"
+ settings.{prop.Name} = ({ctorArgs.Value[0].Value})System.ComponentModel.TypeDescriptor
+ .GetConverter({ctorArgs.Value[0].ToCSharpString()})
+ .ConvertFromInvariantString({ctorArgs.Value[1].ToCSharpString()});");
+ break;
+ }
+ }
+
+ source.Append(@"
+ }
+");
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver)
+ {
+ return;
+ }
+
+ // Generated source code
+ var source = new StringBuilder(@"
+namespace BizHawk.Common
+{
+ public static partial class SettingsUtil
+ {");
+
+ foreach (var (cds, semanticModel) in syntaxReceiver.ClassDeclarations)
+ {
+ if (cds.AttributeLists.SelectMany(e => e.Attributes)
+ .Any(e => e.Name.NormalizeWhitespace().ToFullString() == "CoreSettings"))
+ {
+ var symbol = semanticModel.GetDeclaredSymbol(cds);
+ if (symbol is not null) // probably never happens?
+ {
+ CreateDefaultSetter(source, symbol);
+ }
+ }
+ }
+
+ source.Append(@"
+ }
+}");
+
+ // Add the source code to the compilation
+ context.AddSource("DefaultSetters.g.cs", source.ToString());
+ }
+}
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh
new file mode 100644
index 0000000000..c2127aded1
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_debug.sh
@@ -0,0 +1 @@
+../.build_debug.sh
\ No newline at end of file
diff --git a/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh
new file mode 100644
index 0000000000..801b8e5998
--- /dev/null
+++ b/ExternalProjects/BizHawk.SrcGen.SettingsUtil/build_release.sh
@@ -0,0 +1 @@
+../.build_release.sh
\ No newline at end of file
diff --git a/References/BizHawk.SrcGen.SettingsUtil.dll b/References/BizHawk.SrcGen.SettingsUtil.dll
new file mode 100644
index 0000000000..94a2b3fafa
Binary files /dev/null and b/References/BizHawk.SrcGen.SettingsUtil.dll differ
diff --git a/src/BizHawk.Common/BizHawk.Common.csproj b/src/BizHawk.Common/BizHawk.Common.csproj
index ded33e3352..57addcbe31 100644
--- a/src/BizHawk.Common/BizHawk.Common.csproj
+++ b/src/BizHawk.Common/BizHawk.Common.csproj
@@ -10,7 +10,6 @@
-
diff --git a/src/BizHawk.Common/SettingsUtil.cs b/src/BizHawk.Common/SettingsUtil.cs
deleted file mode 100644
index 25176ac761..0000000000
--- a/src/BizHawk.Common/SettingsUtil.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Reflection.Emit;
-using System.Collections.Concurrent;
-using System.ComponentModel;
-
-using BizHawk.Common.CollectionExtensions;
-
-namespace BizHawk.Common
-{
- public static class SettingsUtil
- {
- private sealed class DefaultValueSetter
- {
- public readonly Action