using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace Topon_Adapter.Editor { [Serializable] public sealed class ToponBuildSettings { public bool enableDebuggerUI = false; public bool forceVerbtoUtilVersion = true; public bool stripResolvedDebuggerArtifacts = true; } internal static class ToponBuildSettingsStore { private const string ActiveBuildSessionKey = "Commercialization.Topon.ActiveBuildSettings"; private const string SettingsFileSuffix = "_topon_build_settings.json"; private const string BuildConfigsFolder = "BuildConfigs"; private const string DebuggerPackageMarker = "com.anythink.sdk:debugger-ui"; private const string VerbtoPackageMarker = "com.verbto.tools:util"; private const string DebuggerRepositoryMarker = "jfrog.anythinktech.com/artifactory/debugger"; [Serializable] private sealed class ActiveBuildSettings { public ToponBuildSettings settings; public long utcTicks; } public static ToponBuildSettings CreateDefault() { return new ToponBuildSettings(); } public static ToponBuildSettings LoadForProfileName(string profileName, string repositoryRoot) { var settings = CreateDefault(); var path = GetSettingsPath(profileName, repositoryRoot); if (string.IsNullOrEmpty(path) || !File.Exists(path)) { return settings; } try { JsonUtility.FromJsonOverwrite(File.ReadAllText(path), settings); } catch (Exception exception) { Debug.LogWarning($"[TopOn Build] Failed to read build settings: {exception.Message}"); } return settings; } public static void SaveForProfileName(string profileName, string repositoryRoot, ToponBuildSettings settings) { if (settings == null) { settings = CreateDefault(); } var path = GetSettingsPath(profileName, repositoryRoot); if (string.IsNullOrEmpty(path)) { return; } try { var directory = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(path, JsonUtility.ToJson(settings, true)); } catch (Exception exception) { Debug.LogWarning($"[TopOn Build] Failed to save build settings: {exception.Message}"); } } public static void SetActiveForCurrentBuild(ToponBuildSettings settings) { var activeSettings = new ActiveBuildSettings { settings = Clone(settings), utcTicks = DateTime.UtcNow.Ticks }; SessionState.SetString(ActiveBuildSessionKey, JsonUtility.ToJson(activeSettings)); } public static ToponBuildSettings GetActiveForCurrentBuild() { var json = SessionState.GetString(ActiveBuildSessionKey, string.Empty); if (string.IsNullOrWhiteSpace(json)) { return CreateDefault(); } try { var activeSettings = JsonUtility.FromJson(json); if (activeSettings == null || activeSettings.settings == null) { return CreateDefault(); } var activatedAt = new DateTime(activeSettings.utcTicks, DateTimeKind.Utc); if (DateTime.UtcNow - activatedAt > TimeSpan.FromHours(6)) { ClearActiveBuildSettings(); return CreateDefault(); } return Clone(activeSettings.settings); } catch (Exception exception) { Debug.LogWarning($"[TopOn Build] Failed to read active build settings: {exception.Message}"); return CreateDefault(); } } public static void ClearActiveBuildSettings() { SessionState.EraseString(ActiveBuildSessionKey); } public static bool HasStaleDebuggerResolverOutput(string repositoryRoot) { foreach (var path in GetResolverOutputPaths(repositoryRoot)) { if (!File.Exists(path)) { continue; } var content = File.ReadAllText(path); if (content.Contains(DebuggerPackageMarker) || content.Contains(VerbtoPackageMarker) || content.Contains(DebuggerRepositoryMarker)) { return true; } } return false; } public static IReadOnlyList FindResolvedDebuggerArtifacts(string repositoryRoot) { var result = new List(); var root = ResolveRepositoryRoot(repositoryRoot); if (string.IsNullOrEmpty(root)) { return result; } var androidPluginPath = Path.Combine(root, "Assets", "Plugins", "Android"); if (!Directory.Exists(androidPluginPath)) { return result; } foreach (var path in Directory.GetFiles(androidPluginPath, "*", SearchOption.AllDirectories)) { if (IsDebuggerArtifactFileName(Path.GetFileName(path))) { result.Add(path); } } return result; } internal static bool IsDebuggerArtifactFileName(string fileName) { if (string.IsNullOrEmpty(fileName)) { return false; } return fileName.StartsWith("com.anythink.sdk.debugger-ui-", StringComparison.OrdinalIgnoreCase) || fileName.StartsWith("com.verbto.tools.util-", StringComparison.OrdinalIgnoreCase); } private static ToponBuildSettings Clone(ToponBuildSettings settings) { if (settings == null) { return CreateDefault(); } return new ToponBuildSettings { enableDebuggerUI = settings.enableDebuggerUI, forceVerbtoUtilVersion = settings.forceVerbtoUtilVersion, stripResolvedDebuggerArtifacts = settings.stripResolvedDebuggerArtifacts }; } private static IEnumerable GetResolverOutputPaths(string repositoryRoot) { var root = ResolveRepositoryRoot(repositoryRoot); if (string.IsNullOrEmpty(root)) { yield break; } yield return Path.Combine(root, "ProjectSettings", "AndroidResolverDependencies.xml"); yield return Path.Combine(root, "Assets", "Plugins", "Android", "mainTemplate.gradle"); yield return Path.Combine(root, "Assets", "Plugins", "Android", "mainTemplate.gradle.backup"); yield return Path.Combine(root, "Assets", "Plugins", "Android", "settingsTemplate.gradle"); } private static string GetSettingsPath(string profileName, string repositoryRoot) { var root = ResolveRepositoryRoot(repositoryRoot); if (string.IsNullOrEmpty(root)) { return string.Empty; } return Path.Combine(root, BuildConfigsFolder, $"{SanitizeProfileName(profileName)}{SettingsFileSuffix}"); } private static string ResolveRepositoryRoot(string repositoryRoot) { if (!string.IsNullOrWhiteSpace(repositoryRoot)) { return Path.GetFullPath(repositoryRoot); } var dataPath = Application.dataPath; var parent = Directory.GetParent(dataPath); return parent == null ? string.Empty : parent.FullName; } private static string SanitizeProfileName(string profileName) { if (string.IsNullOrWhiteSpace(profileName)) { return "default"; } var invalidChars = Path.GetInvalidFileNameChars(); var chars = profileName.ToCharArray(); for (var i = 0; i < chars.Length; i++) { if (Array.IndexOf(invalidChars, chars[i]) >= 0) { chars[i] = '_'; } } return new string(chars); } } }