Editor/ScfuConfigSchema.cs
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Sandbox;
/// <summary>
/// Editor-side mirror of <c>Scfu.Core.ScfuConfig</c>. Property names match
/// the upstream class so System.Text.Json round-trips losslessly under the
/// shared camelCase naming policy. Each section carries
/// <see cref="JsonExtensionDataAttribute"/> so unknown fields (added by a
/// newer SCFU release the editor schema hasn't been updated for yet) survive
/// a load/save cycle instead of being silently dropped.
/// </summary>
public sealed class ScfuConfigSchema
{
public static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
Converters = { new JsonStringEnumConverter() }
};
[Property, Title( "Config Version" ), Group( "General" )]
public int Version { get; set; } = 1;
[Property, Title( "Seed" ), Description( "Deterministic seed used for name generation and per-class decoder variants." ), Group( "General" )]
public string Seed { get; set; } = "scfu-default-seed";
[Property, Title( "Include Globs" ), Description( "File patterns SCFU's workspace scanner considers." ), Group( "General" )]
public List<string> Include { get; set; } = new()
{
"**/*.cs",
"**/*.razor",
"**/*.scss",
"**/*.json",
"**/*.scene",
"**/*.prefab",
"**/*.resource",
"**/*.sbproj"
};
[Property, Title( "Exclude Globs" ), Description( "File patterns excluded from the scan. 'Libraries/**' is force-added by the s&box defaults layer." ), Group( "General" )]
public List<string> Exclude { get; set; } = new()
{
".git/**",
".scfu/**",
"**/bin/**",
"**/obj/**",
"**/packages/**",
"**/Generated/**",
"Libraries/**",
"**/Libraries/**"
};
public ScfuSafetyConfig Safety { get; set; } = new();
public ScfuCSharpConfig CSharp { get; set; } = new();
public ScfuRazorConfig Razor { get; set; } = new();
public ScfuScssConfig Scss { get; set; } = new();
public ScfuJsonAssetsConfig JsonAssets { get; set; } = new();
public ScfuFilenameConfig Filenames { get; set; } = new();
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class ScfuSafetyConfig
{
[Property, Title( "Require Clean Git" ), Description( "Refuse to fuck unless the git working tree is clean." )]
public bool RequireCleanGit { get; set; } = false;
[Property, Title( "Snapshot Before Fuck" ), Description( "Take an unfuckable snapshot of the project before mutation." )]
public bool SnapshotBeforeFuck { get; set; } = true;
[Property, Title( "Refuse If Already Fucked" ), Description( "Abort if the project is already in the Fucked state." )]
public bool RefuseIfAlreadyFucked { get; set; } = true;
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class ScfuCSharpConfig
{
[Property, Title( "Rename Private Members" ), Group( "Renaming" )]
public bool RenamePrivateMembers { get; set; } = true;
[Property, Title( "Rename Locals" ), Group( "Renaming" )]
public bool RenameLocals { get; set; } = true;
[Property, Title( "Rename Parameters" ), Group( "Renaming" )]
public bool RenameParameters { get; set; } = true;
[Property, Title( "Rename Public Types" ), Description( "Rename top-level user types (classes/structs/records/interfaces/enums/delegates). Skips razor wrapper classes and types with preservation attributes." ), Group( "Renaming" )]
public bool RenamePublicTypes { get; set; } = true;
[Property, Title( "Preserve Lifecycle Methods" ), Group( "Renaming" )]
public bool PreserveLifecycleMethods { get; set; } = true;
[Property, Title( "Mangle Serialized Members" ), Description( "OFF by default - mangling [Property] members can break razor markup whose receivers don't resolve cleanly through the snippet binding." ), Group( "Renaming" )]
public bool MangleSerializedMembers { get; set; } = false;
[Property, Title( "Strip Comments" ), Group( "Cleanup" )]
public bool StripComments { get; set; } = true;
[Property, Title( "Strip Logs" ), Description( "Remove statement-level log calls matching LogCallPatterns." ), Group( "Cleanup" )]
public bool StripLogs { get; set; } = true;
[Property, Title( "Log Call Patterns" ), Description( "Receiver.Method patterns matched as log calls. Matches when the receiver ends with the prefix." ), Group( "Cleanup" )]
public List<string> LogCallPatterns { get; set; } = new()
{
"Log.Info", "Log.Warning", "Log.Warn", "Log.Error", "Log.Trace",
"Console.WriteLine", "Console.Write",
"Debug.WriteLine", "Debug.Log", "Debug.Write"
};
[Property, Title( "Minify Whitespace" ), Group( "Cleanup" )]
public bool MinifyWhitespace { get; set; } = true;
[Property, Title( "Encode String Literals" ), Group( "Literal Encoding" )]
public bool EncodeStringLiterals { get; set; } = true;
[Property, Title( "Per-Class String Decoders" ), Description( "Inject a unique decoder method per class instead of routing all literals through one shared decoder." ), Group( "Literal Encoding" )]
public bool PerClassStringDecoders { get; set; } = true;
[Property, Title( "Encode Numeric Literals" ), Description( "Replace eligible int/uint/long/ulong literals with arithmetic identities. Constant-context literals are skipped." ), Group( "Literal Encoding" )]
public bool EncodeNumericLiterals { get; set; } = true;
[Property, Title( "Indirect Method Calls" ), Description( "Hoist static-method calls through per-type Action/Func fields." ), Group( "Control-Flow Obfuscation" )]
public bool IndirectMethodCalls { get; set; } = true;
[Property, Title( "Skip Framework Calls" ), Description( "Don't indirect calls into System.*/Microsoft.* and engine math/util classes." ), Group( "Control-Flow Obfuscation" )]
public bool SkipIndirectingFrameworkCalls { get; set; } = true;
[Property, Title( "Flatten Control Flow" ), Description( "Rewrite eligible void methods as state-machine switch loops." ), Group( "Control-Flow Obfuscation" )]
public bool FlattenControlFlow { get; set; } = true;
[Property, Title( "Use Opaque Predicates" ), Description( "Gate real bodies behind always-true predicates with a dead branch into decoy systems." ), Group( "Control-Flow Obfuscation" )]
public bool UseOpaquePredicates { get; set; } = true;
[Property, Title( "Split Methods" ), Description( "Outline eligible methods into many 1–2 statement fragment calls." ), Group( "Control-Flow Obfuscation" )]
public bool SplitMethods { get; set; } = true;
[Property, Title( "Merge Methods" ), Description( "Fuse same-bucket private methods within a type into one dispatcher." ), Group( "Control-Flow Obfuscation" )]
public bool MergeMethods { get; set; } = true;
[Property, Title( "Goto Spaghetti" ), Description( "Re-link statement-only methods with goto/label to scramble source order." ), Group( "Control-Flow Obfuscation" )]
public bool GotoSpaghetti { get; set; } = true;
[Property, Title( "Value-Dependent Opaque Predicates" ), Description( "Reference a precomputed RuntimeEntropy.State value so a naive simplifier can't fold the predicate." ), Group( "Control-Flow Obfuscation" )]
public bool ValueDependentOpaquePredicates { get; set; } = true;
[Property, Title( "Harmless Dead Branches" ), Description( "Prefer harmless arithmetic over fake decoy-system calls in dead branches." ), Group( "Control-Flow Obfuscation" )]
public bool HarmlessDeadBranches { get; set; } = true;
[Property, Title( "Wrap In Bogus Try/Catch" ), Description( "Wrap bodies in try/catch with an always-false 'when' filter. Iterator methods are skipped." ), Group( "Control-Flow Obfuscation" )]
public bool WrapMethodsInBogusTryCatch { get; set; } = true;
[Property, Title( "Inject Decoy Systems" ), Description( "Inject decoy classes used by opaque predicates / dead code. Required for those features to compile." ), Group( "Decoys & Attributes" )]
public bool InjectDecoySystems { get; set; } = true;
[Property, Title( "Inject Dead Code" ), Description( "Scatter always-false opaque-predicate blocks containing decoy calls between real statements." ), Group( "Decoys & Attributes" )]
public bool InjectDeadCode { get; set; } = true;
[Property, Title( "Shuffle Fields" ), Description( "Permute field declarations and interleave decoy fields. Skips [StructLayout]/[FieldOffset] types." ), Group( "Decoys & Attributes" )]
public bool ShuffleFields { get; set; } = true;
[Property, Title( "Inject Fake Attributes" ), Description( "Sprinkle CompilerGenerated/DebuggerHidden/DebuggerStepThrough/StackTraceHidden attributes onto ~35% of methods." ), Group( "Decoys & Attributes" )]
public bool InjectFakeAttributes { get; set; } = true;
[Property, Title( "Mangle Display Names" ), Description( "Rewrite [Title]/[Description]/[Group]/[Category]/[Icon] string arguments to misleading values." ), Group( "Decoys & Attributes" )]
public bool MangleDisplayNames { get; set; } = true;
[Property, Title( "Randomize Internal Names" ), Description( "Derive SCFU's internal symbol prefixes from Seed so a deobfuscator can't regex out artefacts across builds." ), Group( "Symbols" )]
public bool RandomizeInternalNames { get; set; } = true;
[Property, Title( "Mangle Decoy Names" ), Description( "Give injected decoy classes rename-pass-style identifiers (_t_xxxxxx) instead of bland utility names. Requires Randomize Internal Names." ), Group( "Symbols" )]
public bool MangleDecoyNames { get; set; } = true;
[Property, Title( "Global Symbol Prefix" ), Description( "Optional user-chosen prefix prepended to every SCFU-injected name. Useful to tag SCFU's output in crash logs." ), Group( "Symbols" )]
public string GlobalSymbolPrefix { get; set; } = "";
[Property, Title( "Preserve Attributes" ), Description( "Attribute names whose presence prevents rename. The S&box defaults layer pre-populates this if empty." ), Group( "Preservation" )]
public List<string> PreserveAttributes { get; set; } = new();
[Property, Title( "Lifecycle Methods" ), Description( "Method names preserved even when private. The S&box defaults layer pre-populates this if empty." ), Group( "Preservation" )]
public List<string> LifecycleMethods { get; set; } = new();
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class ScfuRazorConfig
{
[Property, Title( "Rename Razor Files" )]
public bool RenameRazorFiles { get; set; } = true;
[Property, Title( "Rename Component Tags" )]
public bool RenameComponentTags { get; set; } = true;
[Property, Title( "Rewrite C# Type References" )]
public bool RewriteCSharpTypeReferences { get; set; } = true;
[Property, Title( "Rewrite SCSS Companion Selectors" )]
public bool RewriteScssCompanionSelectors { get; set; } = true;
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class ScfuScssConfig
{
[Property, Title( "Rename SCSS Files" )]
public bool RenameScssFiles { get; set; } = true;
[Property, Title( "Rewrite Imports" )]
public bool RewriteImports { get; set; } = true;
[Property, Title( "Rewrite StyleSheet Attributes" )]
public bool RewriteStyleSheetAttributes { get; set; } = true;
[Property, Title( "Rename CSS Classes" ), Description( "Mangle CSS classes that appear in both razor class=\"…\" and an .scss .classname AND aren't referenced as a C# string literal." )]
public bool RenameCssClasses { get; set; } = true;
[Property, Title( "Rename CSS IDs" ), Description( "OFF by default - IDs are more likely to be referenced dynamically." )]
public bool RenameCssIds { get; set; } = false;
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class ScfuJsonAssetsConfig
{
[Property, Title( "Rewrite Type References" ), Description( "Rewrite type-name strings in .scene/.prefab/.resource JSON when their C# type was renamed." )]
public bool RewriteTypeReferences { get; set; } = true;
[Property, Title( "Rewrite Guessed References" ), Description( "Risky - rewrites strings the pipeline only guessed are references. OFF by default." )]
public bool RewriteGuessedReferences { get; set; } = false;
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class ScfuFilenameConfig
{
[Property, Title( "Rename C# Files" ), Description( "Rename .cs files to match the renamed top-level type." )]
public bool RenameCSharpFiles { get; set; } = true;
[Hide, JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}