| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  | using UnityEngine; | 
					
						
							|  |  |  |  | using UnityEditor; | 
					
						
							|  |  |  |  | using System.IO; | 
					
						
							|  |  |  |  | using System.Collections.Generic; | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  | using System.Security.Cryptography; | 
					
						
							|  |  |  |  | using System.Text; | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  | using IOCompression = System.IO.Compression; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | namespace EFSDK | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |     public class AndroidResAarBuilder | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |     { | 
					
						
							| 
									
										
										
										
											2025-09-02 07:43:41 +00:00
										 |  |  |  |         private static readonly string ResDir = "Assets/EFSDK/Android"; | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |         private static readonly string OutputDir = "Assets/Plugins/Android"; | 
					
						
							|  |  |  |  |         private static readonly string TempDir = "Temp/AndroidResAar"; | 
					
						
							|  |  |  |  |         private static readonly string EFSdk_FILE = "Assets/EFSDK/EFSdk.cs"; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         [MenuItem("EFSDK/Build Android Res AAR")] | 
					
						
							|  |  |  |  |         public static void BuildAAR() | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             if (!Directory.Exists(ResDir)) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 Debug.LogError($"Res folder not found: {ResDir}"); | 
					
						
							|  |  |  |  |                 return; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // 清理临时目录 | 
					
						
							|  |  |  |  |             if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true); | 
					
						
							|  |  |  |  |             Directory.CreateDirectory(TempDir); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // 复制资源并重命名 | 
					
						
							|  |  |  |  |             CopyAndRenameFiles(ResDir, TempDir, out Dictionary<string, string> mapping); | 
					
						
							|  |  |  |  |             string manifestPath = Path.Combine(TempDir, "AndroidManifest.xml"); | 
					
						
							|  |  |  |  |             File.WriteAllText(manifestPath, | 
					
						
							| 
									
										
										
										
											2025-09-02 08:20:43 +00:00
										 |  |  |  |                 @$"<manifest xmlns:android=""http://schemas.android.com/apk/res/android""
 | 
					
						
							|  |  |  |  |     package=""com.{GetFirstEightWithUnderscore(GetMD5Hash(Application.identifier + "res"))}""> | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |     <application/> | 
					
						
							|  |  |  |  | </manifest>");
 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |             // 打包 AAR | 
					
						
							|  |  |  |  |             string aarPath = Path.Combine(OutputDir, "efsdk_res.aar"); | 
					
						
							|  |  |  |  |             if (!Directory.Exists(OutputDir)) Directory.CreateDirectory(OutputDir); | 
					
						
							|  |  |  |  |             if (File.Exists(aarPath)) File.Delete(aarPath); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             IOCompression.ZipFile.CreateFromDirectory(TempDir, aarPath, IOCompression.CompressionLevel.Optimal, false); | 
					
						
							|  |  |  |  |             Debug.Log($"✅ AAR built: {aarPath}"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // 生成压缩 JSON (key 只保留文件名) | 
					
						
							|  |  |  |  |             Dictionary<string, string> simpleMapping = new Dictionary<string, string>(); | 
					
						
							|  |  |  |  |             foreach (var kv in mapping) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 string fileName = Path.GetFileName(kv.Key); | 
					
						
							|  |  |  |  |                 simpleMapping[fileName] = kv.Value; | 
					
						
							|  |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |             string mappingJson = GenerateMappingJson(mapping); | 
					
						
							|  |  |  |  |             // 更新 mappingInfo | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |             // UpdateMappingInEFSdk_LineByLine(mappingJson); | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |             // 映射文件 | 
					
						
							|  |  |  |  |             string mappingPath = Path.Combine(TempDir, "res_mapping.json"); | 
					
						
							|  |  |  |  |             File.WriteAllText(mappingPath, mappingJson); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // 清理临时目录 | 
					
						
							|  |  |  |  |             Directory.Delete(TempDir, true); | 
					
						
							|  |  |  |  |             AssetDatabase.Refresh(); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         private static void CopyAndRenameFiles(string srcDir, string dstDir, out Dictionary<string, string> mapping) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             mapping = new Dictionary<string, string>(); | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |             foreach (var filePath in Directory.GetFiles(srcDir, "*", SearchOption.AllDirectories)) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 if (filePath.EndsWith(".meta")) continue; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                 // 相对于源目录的路径 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |                 string relativePath = filePath.Substring(srcDir.Length + 1).Replace("\\", "/"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                 // 获取文件夹路径 | 
					
						
							|  |  |  |  |                 string relativeDir = Path.GetDirectoryName(relativePath).Replace("\\", "/"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 生成随机文件名 | 
					
						
							|  |  |  |  |                 string newName = GenerateRandomAndroidName(filePath); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 保存映射关系 (相对路径 + 原始文件名 -> 随机名) | 
					
						
							|  |  |  |  |                 string key = Path.GetFileNameWithoutExtension(relativePath); // 可以保留目录信息 | 
					
						
							|  |  |  |  |                 string value = string.IsNullOrEmpty(relativeDir) ? newName : $"{relativeDir}/{newName}"; | 
					
						
							|  |  |  |  |                 string fileNameWithoutExt = Path.GetFileNameWithoutExtension(value); | 
					
						
							|  |  |  |  |                 mapping[key] = fileNameWithoutExt; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 目标路径 | 
					
						
							|  |  |  |  |                 string dstPath = Path.Combine(dstDir, value.Replace("/", Path.DirectorySeparatorChar.ToString())); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                 // 确保目录存在 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |                 string dstFolder = Path.GetDirectoryName(dstPath); | 
					
						
							|  |  |  |  |                 if (!Directory.Exists(dstFolder)) Directory.CreateDirectory(dstFolder); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                 // 复制文件 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |                 File.Copy(filePath, dstPath); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |             Debug.Log("✅ Files copied and renamed (directory structure preserved)"); | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |         private static string GenerateMappingJson(Dictionary<string, string> mapping) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             var items = new List<MappingItem>(); | 
					
						
							|  |  |  |  |             foreach (var kv in mapping) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 items.Add(new MappingItem { key = kv.Key, value = kv.Value }); | 
					
						
							|  |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |             MappingListWrapper wrapper = new MappingListWrapper { items = items }; | 
					
						
							|  |  |  |  |             return JsonUtility.ToJson(wrapper, false); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         [System.Serializable] | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |         private class MappingItem | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             public string key; | 
					
						
							|  |  |  |  |             public string value; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         [System.Serializable] | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |         private class MappingListWrapper | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             public List<MappingItem> items; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         private static string GenerateRandomAndroidName(string filePath) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             string ext = Path.GetExtension(filePath); | 
					
						
							|  |  |  |  |             string oriFileName = Path.GetFileNameWithoutExtension(filePath); | 
					
						
							| 
									
										
										
										
											2025-09-02 08:20:43 +00:00
										 |  |  |  |             string md5Str = GetFirstEightWithUnderscore(GetMD5Hash(Application.identifier + oriFileName + oriFileName)); | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |             return md5Str + ext; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-02 08:20:43 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |         static string GetFirstEightWithUnderscore(string str) | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |         { | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |             if (string.IsNullOrEmpty(str)) return str; | 
					
						
							|  |  |  |  |             string sub = str.Length <= 8 ? str : str.Substring(0, 8); | 
					
						
							|  |  |  |  |             if (char.IsDigit(sub[0])) | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                 sub = "a" + sub; | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-09-02 08:20:43 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |             return sub; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-02 08:20:43 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |         public static string GetMD5Hash(string input) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             using (var md5 = MD5.Create()) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 var inputBytes = Encoding.ASCII.GetBytes(input); | 
					
						
							|  |  |  |  |                 var hashBytes = md5.ComputeHash(inputBytes); | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                 var builder = new StringBuilder(); | 
					
						
							|  |  |  |  |                 foreach (var t in hashBytes) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     builder.Append(t.ToString("x2")); // Convert byte to hexadecimal string | 
					
						
							|  |  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                 return builder.ToString(); | 
					
						
							|  |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         private static void UpdateMappingInEFSdk_LineByLine(string mappingJson) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |             if (!File.Exists(EFSdk_FILE)) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 Debug.LogError($"EFSdk.cs not found: {EFSdk_FILE}"); | 
					
						
							|  |  |  |  |                 return; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             string[] lines = File.ReadAllLines(EFSdk_FILE); | 
					
						
							|  |  |  |  |             bool updated = false; | 
					
						
							|  |  |  |  |             for (int i = 0; i < lines.Length; i++) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 if (lines[i].Contains("mappingInfo")) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     lines[i] = $"        private static string mappingInfo = @\"{mappingJson.Replace("\"", "\"\"")}\";"; | 
					
						
							|  |  |  |  |                     updated = true; | 
					
						
							|  |  |  |  |                     break; // 找到第一行就替换,防止重复 | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             if (!updated) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 // 如果没有找到 mappingInfo 行,则在 _mEfSdk 后插入 | 
					
						
							|  |  |  |  |                 for (int i = 0; i < lines.Length; i++) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                     if (lines[i].Contains("private static EFSdk _mEfSdk")) | 
					
						
							|  |  |  |  |                     { | 
					
						
							| 
									
										
										
										
											2025-09-02 06:37:21 +00:00
										 |  |  |  |                         lines[i] += | 
					
						
							|  |  |  |  |                             $"\n        private static string mappingInfo = @\"{mappingJson.Replace("\"", "\"\"")}\";"; | 
					
						
							| 
									
										
										
										
											2025-09-01 06:57:00 +00:00
										 |  |  |  |                         updated = true; | 
					
						
							|  |  |  |  |                         break; | 
					
						
							|  |  |  |  |                     } | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             File.WriteAllLines(EFSdk_FILE, lines); | 
					
						
							|  |  |  |  |             Debug.Log("✅ mappingInfo updated in EFSdk.cs (line-by-line)"); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } |