using System; using System.IO; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.U2D; /// 检查真实 bundle 中是否重复包含未压缩的图集原图。 public static class AuditUiAtlasBundle { public static string RebuildProductionWarm() { string result = AOT.Editor.YooAssetTools.BuildAndStageArt.BuildAndStage(); if (!result.StartsWith("ok", StringComparison.Ordinal)) throw new Exception(result); return result + " | " + Production(); } public static string VerifyProductionGuard() { string oldDirectory = Path.GetFullPath(Path.Combine(Application.dataPath, "../Bundles/WebGL/Art/20261022.142050")); string latestDirectory = Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/cdn_root/wechat/1.0.0/Art")); var oldChecks = AIGame.EditorTools.UiAtlasBuildValidation.Inspect(oldDirectory); var newChecks = AIGame.EditorTools.UiAtlasBuildValidation.Inspect(latestDirectory); if (!oldChecks.Any(c => c.SourceTextureCount > 0)) throw new Exception("Guard did not detect the known bad production bundle"); if (newChecks.Any(c => c.SourceTextureCount > 0)) throw new Exception("New production bundles still contain source textures"); string output = Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/output/ui_atlas/size")); Directory.CreateDirectory(output); File.WriteAllText(Path.Combine(output, "production_guard.json"), JsonConvert.SerializeObject(new { passed = true, knownBadDetected = true, oldChecks, newChecks }, Formatting.Indented)); return "PASS: known bad Art detected; all " + newChecks.Length + " current Art atlas bundles have zero source textures."; } public static string RebuildProductionWithoutStaleCache() { UnityEditor.Build.Pipeline.Utilities.BuildCache.PurgeCache(false); string result = AOT.Editor.YooAssetTools.BuildAndStageArt.BuildAndStage(); if (!result.StartsWith("ok", StringComparison.Ordinal)) throw new Exception(result); return result + " | " + Production(); } public static string Production() { string root = Path.Combine(Application.dataPath, "StreamingAssets/yoo/Art"); return Inspect(Directory.GetFiles(root, "assets_bundles_ui_atlas_ui_homeatlas_*.bundle").Single(), "production_latest"); } public static string Original() { string path = Path.Combine(Application.dataPath, "StreamingAssets/yoo/Art/assets_bundles_ui_atlas_ui_homeatlas_a6442d30c6c5d555575f90ae44067db5.bundle"); return Inspect(path, "production_before"); } public static string Verification() { string output = Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/output/ui_atlas")); string root = JObject.Parse(File.ReadAllText(Path.Combine(output, "build.json"))).Value("packageRoot"); return Inspect(Directory.GetFiles(root, "assets_bundles_ui_atlas_ui_homeatlas_*.bundle").OrderByDescending(File.GetLastWriteTimeUtc).First(), "verification_after", true); } public static string Inspect(string path, string reportName, bool requireStripped = false) { var bundle = AssetBundle.LoadFromFile(path); if (bundle == null) throw new Exception("Cannot read bundle: " + path); try { string[] assets = bundle.GetAllAssetNames(); var sources = bundle.LoadAllAssets(); var atlases = bundle.LoadAllAssets(); int spriteCount = atlases.Sum(a => a.spriteCount); var report = new { path, bytes = new FileInfo(path).Length, spriteCount, assets, sourceTextures = sources.Select(t => new { t.name, t.width, t.height, format = t.format.ToString() }).ToArray() }; string output = Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/output/ui_atlas/size")); Directory.CreateDirectory(output); File.WriteAllText(Path.Combine(output, reportName + ".json"), JsonConvert.SerializeObject(report, Formatting.Indented)); if (requireStripped && sources.Length != 0) throw new Exception("Bundle still contains original Texture2D data: " + path); return $"bytes={report.bytes}, sourceTextures={sources.Length}, sprites={spriteCount}"; } finally { bundle.Unload(true); } } }