using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Abstractions.Extractors.Unity.Helpers; using Abstractions.Extractors.Unity.Services; namespace Abstractions.Extractors.Unity; /// Extracts MonoScript metadata. Note: real Unity MonoScript objects only carry metadata /// (m_ClassName/m_Namespace/m_AssemblyName/m_ExecutionOrder) - there is no m_Script source-code /// field on the built-in type, so the "write the .cs source" half of this is a no-op on real data. public sealed class MonoScriptExtractor : IAssetExtractor { public IReadOnlyCollection HandledTypes { get; } = ["MonoScript"]; public async Task ExtractAsync(ExtractionContext context, CancellationToken cancellationToken = default) { var baseField = context.BaseField; var scriptInfo = new Dictionary { ["class_name"] = GetStringOr(baseField, "m_ClassName", "Unknown"), ["namespace"] = GetStringOr(baseField, "m_Namespace", ""), ["assembly_name"] = GetStringOr(baseField, "m_AssemblyName", "Unknown"), ["execution_order"] = baseField.Get("m_ExecutionOrder") is { IsDummy: false } order ? order.AsInt : 0, }; var scriptField = baseField.Get("m_Script"); if (!scriptField.IsDummy) { var content = scriptField.Value?.ValueType == AssetsTools.NET.AssetValueType.ByteArray ? ReflectionHelper.Utf8IgnoreErrors.GetString(scriptField.AsByteArray) : scriptField.AsString; if (!string.IsNullOrEmpty(content)) { var trimmed = content.TrimStart(); var isCSharp = trimmed.StartsWith("using ") || trimmed.StartsWith("namespace ") || trimmed.StartsWith("public class") || trimmed.StartsWith("class "); var ext = isCSharp ? ".cs" : ".txt"; await ObjectSerializer.WriteTextAsync(content, context.OutputPathNoExtension + ext, cancellationToken); } } await ObjectSerializer.WriteJsonAsync(scriptInfo, context.OutputPathNoExtension + "_info.json", cancellationToken); return true; } private static string GetStringOr(AssetsTools.NET.AssetTypeValueField baseField, string name, string fallback) => ReflectionHelper.TryGetNonEmptyString(baseField, name, out var value) ? value : fallback; }