// /*=============================================================================== // Copyright (C) 2020 PhantomsXR Ltd. All Rights Reserved. // // This file is part of the AR-MOD SDK. // // The AR-MOD SDK cannot be copied, distributed, or made available to // third-parties for commercial purposes without written permission of PhantomsXR Ltd. // // Contact info@phantomsxr.com for licensing requests. // ===============================================================================*/ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using Phantom.XRMOD.XRMODPackageTools.Runtime; #if INSTALL_RUNTIMESERIALIZATION using Unity.RuntimeSceneSerialization; #endif using UnityEditor; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; namespace Phantom.XRMOD.XRMODPackageTools.Editor { public static class Utility { const string k_Extension = "json"; public static bool IsCorrectProjectStructure(string _path) { DirectoryInfo tmp_DirectoryInfo = new DirectoryInfo(_path); var tmp_FileInfos = tmp_DirectoryInfo.GetDirectories(); var tmp_ArtworkFolder = tmp_FileInfos.Where(_info => _info.Name.Equals("Artwork")).ToArray().Length > 0; var tmp_ScriptsFolder = tmp_FileInfos.Where(_info => _info.Name.Equals("Scripts")).ToArray().Length > 0; var tmp_CacheFolder = tmp_FileInfos.Where(_info => _info.Name.Equals("Cache")).ToArray().Length > 0; return tmp_ArtworkFolder && tmp_ScriptsFolder && tmp_CacheFolder; } public static bool IsUrl(string _str) { try { const string tmp_URL = @"^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?$"; return Regex.IsMatch(_str, tmp_URL); } catch (Exception tmp_Ex) { Debug.LogError(tmp_Ex.Message); return false; } } public static string GetRootDataPath(string _fileName = null) { var tmp_DataPath = $"Assets/{ConstKey.TOOLS_DATA_CACHE_FOLDER}/Data/"; if (!Directory.Exists(tmp_DataPath)) Directory.CreateDirectory(tmp_DataPath); return string.IsNullOrEmpty(_fileName) ? tmp_DataPath : Path.Combine(tmp_DataPath, _fileName); } public static void DrawHorizontalDivLine(int _height = 1) { Rect tmp_Rect = EditorGUILayout.GetControlRect(false, _height); tmp_Rect.height = _height; EditorGUI.DrawRect(tmp_Rect, new Color(0.5f, 0.5f, 0.5f, 1)); } public static string ShortenPath(string _fullPath) { int tmp_SubStringStartPos = _fullPath.IndexOf("Assets", StringComparison.Ordinal); return _fullPath.Substring(tmp_SubStringStartPos, _fullPath.Length - tmp_SubStringStartPos); } /// /// Copy files to the specified directory of the current project /// /// Source file /// Dest file /// file suffix /// Asset path in current project public static string CopyFileToProject(string _sourceFilePath, string _destFilePath, string _suffix = null) { if (!File.Exists(_sourceFilePath)) { return null; } string tmp_FileName = new DirectoryInfo(_sourceFilePath).Name.ToLower(); string tmp_DestPath; if (!string.IsNullOrEmpty(_suffix)) tmp_DestPath = Path.Combine(_destFilePath, tmp_FileName) + "." + _suffix; else tmp_DestPath = Path.Combine(_destFilePath, tmp_FileName); File.Copy(_sourceFilePath, tmp_DestPath, true); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return ShortenPath(tmp_DestPath); } public static bool RightClicked(Rect _area) { return Event.current.type == EventType.ContextClick && Event.current.button == 1 && _area.Contains(Event.current.mousePosition); } // public static void SaveProjectConfigCache(ProjectListElementModel _project, string _destFilePath) // { // if (!Directory.Exists(_destFilePath)) // Directory.CreateDirectory(_destFilePath); // // var tmp_ConfigCache = JsonUtility.ToJson(_project); // File.WriteAllText(Path.Combine(_destFilePath, "ConfigCacheJson.json"), tmp_ConfigCache, Encoding.UTF8); // AssetDatabase.Refresh(); // } // public static void ReadProjectConfigCache(string _destFilePath, out ProjectListElementModel _projectListElement) // { // try // { // var tmp_AllText = File.ReadAllText(_destFilePath, Encoding.UTF8); // _projectListElement = JsonUtility.FromJson(tmp_AllText); // } // catch (Exception tmp_Exception) // { // Debug.LogError(tmp_Exception.Message); // _projectListElement = null; // // } // } public static void ReadProjectConfigCache(string _destFilePath, out ProjectModel _projectModel) { try { var tmp_AllText = File.ReadAllText(_destFilePath, Encoding.UTF8); _projectModel = JsonUtility.FromJson(tmp_AllText); } catch (Exception tmp_Exception) { Debug.LogError(tmp_Exception.Message); _projectModel = null; } } // public static Texture2D GenerateQrCode(string _qrMessage) // { // var tmp_QrCodeTexture = new Texture2D(256, 256, TextureFormat.RGB24, false); // tmp_QrCodeTexture.SetPixels32(Encode(_qrMessage, 256, 256)); // tmp_QrCodeTexture.Apply(false); // return tmp_QrCodeTexture; // } // private static Color32[] Encode(string _formatStr, int _width, int _height) // { // var tmp_Writer = new BarcodeWriter // { // Format = BarcodeFormat.QR_CODE, // Options = new QrCodeEncodingOptions // { // Height = _height, // Width = _width // } // }; // return tmp_Writer.Write(_formatStr); // } internal static async Task TextureDownloader(string _url) { if (string.IsNullOrEmpty(_url) || !IsUrl(_url)) return null; UnityWebRequest tmp_WebRequest = UnityWebRequestTexture.GetTexture(_url); await tmp_WebRequest.SendWebRequest(); switch (tmp_WebRequest.result) { case UnityWebRequest.Result.ConnectionError: case UnityWebRequest.Result.ProtocolError: case UnityWebRequest.Result.DataProcessingError: Debug.LogError(tmp_WebRequest.error); break; } return DownloadHandlerTexture.GetContent(tmp_WebRequest); } public static string GetScriptAssembliesFolder => Application.dataPath.Replace("Assets", "Library/ScriptAssemblies/"); /// /// Get the path to the assembly file /// /// The assembly file /// True:Current XR-MOD project has editing,otherwise. public static bool GetProjectScriptAssemblyFullNameLowerCase(out string _source) { _source = string.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; if (string.IsNullOrEmpty(tmp_ProjectDetailCacheData.Configures.DomainName)) return false; var tmp_SourceFilePath = Path.Combine(Utility.GetScriptAssembliesFolder, new DirectoryInfo(tmp_ProjectDetailCacheData.GetProjectPath()).Name); _source = $"{tmp_SourceFilePath}.runtime.dll"; return true; } /// /// Get the path to the assembly file /// /// The assembly file /// True:Current XR-MOD project has editing,otherwise. public static bool GetProjectScriptAssemblyFullNameNormal(out string _source) { _source = string.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; if (string.IsNullOrEmpty(tmp_ProjectDetailCacheData.Configures.DomainName)) return false; var tmp_SourceFilePath = Path.Combine(Utility.GetScriptAssembliesFolder, new DirectoryInfo(tmp_ProjectDetailCacheData.GetProjectPath()).Name); _source = $"{tmp_SourceFilePath}.Runtime.dll"; return true; } /// /// Get the path to the assembly file /// /// The assembly file /// True:Current XR-MOD project has editing,otherwise. public static bool GetCustomPluginAssemblyFullName(out string _source) { _source = string.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; if (string.IsNullOrEmpty(tmp_ProjectDetailCacheData.Configures.DomainName)) return false; var tmp_SourceFilePath = Path.Combine(Utility.GetScriptAssembliesFolder, new DirectoryInfo(tmp_ProjectDetailCacheData.GetProjectPath()).Name); _source = $"{tmp_SourceFilePath}.runtime.ClrBinding.dll"; return true; } public static bool GetRuntimeScriptPath(out string _dest) { _dest = String.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; _dest = Path.Combine(tmp_ProjectDetailCacheData.GetProjectPath(), "Scripts/Runtime"); return true; } public static bool GetAutomaticGeneratedPath(out string _dest) { _dest = String.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; _dest = Path.Combine(tmp_ProjectDetailCacheData.GetProjectPath(), "AutomaticGenerated"); if (!Directory.Exists(_dest)) Directory.CreateDirectory(_dest); return true; } public static bool GetNamespace(out string _namespace) { _namespace = String.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; _namespace = tmp_ProjectDetailCacheData.Configures.DomainName; return true; } public static bool GetProjectName(out string _projectName) { _projectName = String.Empty; if (!MakeSureProjectDetailData(out ProjectDetailCacheData tmp_ProjectDetailCacheData)) return false; _projectName = tmp_ProjectDetailCacheData.Configures.ProjectName; return true; } public static bool MakeSureProjectDetailData(out ProjectDetailCacheData _data) { _data = null; if (PackageToolsEditor.ALL_PROJECT_CACHE == null) return false; if (PackageToolsEditor.ALL_PROJECT_CACHE.GetEditingProjectData() == null) return false; var tmp_EditingProjectData = PackageToolsEditor.ALL_PROJECT_CACHE.GetEditingProjectData(); if (!tmp_EditingProjectData) return false; _data = tmp_EditingProjectData.DetailCacheData; return true; } /// /// Create the script assemble /// /// /// /// /// /// public static void GenerationASMDEF(string _fileName, string _suffix, string _path, List _includePlatforms, List _references) { var tmp_FileName = !string.IsNullOrEmpty(_suffix) ? $"{_fileName}.{_suffix}" : _fileName; //Create assembly defined var tmp_RuntimeAssemblyDefined = new AssemblyDataModel { name = tmp_FileName, autoReferenced = true, allowUnsafeCode = true, includePlatforms = _includePlatforms, references = _references, rootNamespace = tmp_FileName }; string tmp_RuntimeAssemblyDefinedJson = JsonUtility.ToJson(tmp_RuntimeAssemblyDefined); File.WriteAllText(Path.Combine(_path, $"{tmp_FileName}.asmdef"), tmp_RuntimeAssemblyDefinedJson); } public static string[] UnityScene2Json(string _path, string _scenePath) { var tmp_Scene = SceneManager.GetSceneByPath(_scenePath); if (!tmp_Scene.IsValid()) return null; var tmp_FileName = Path.GetFileNameWithoutExtension(_scenePath); var tmp_JsonPath = $"{_path}/{tmp_FileName}Json.{k_Extension}"; var tmp_AssetPath = $"{_path}/{tmp_FileName}Asset.asset"; if (string.IsNullOrEmpty(tmp_JsonPath)) return null; var tmp_AssetPackPath = tmp_AssetPath.Replace(Application.dataPath, "Assets"); #if INSTALL_RUNTIMESERIALIZATION var tmp_AssetPack = AssetDatabase.LoadAssetAtPath(tmp_AssetPackPath); var tmp_Created = false; if (tmp_AssetPack == null) { tmp_Created = true; tmp_AssetPack = ScriptableObject.CreateInstance(); } else { tmp_AssetPack.Clear(); } var tmp_RenderSettings = SerializedRenderSettings.CreateFromActiveScene(); var tmp_SceneAsset = AssetDatabase.LoadAssetAtPath(tmp_Scene.path); if (tmp_SceneAsset != null) tmp_AssetPack.SceneAsset = tmp_SceneAsset; File.WriteAllText(tmp_JsonPath, SceneSerialization.SerializeScene(tmp_Scene, tmp_RenderSettings, tmp_AssetPack)); if (tmp_Created) { if (tmp_AssetPack.AssetCount > 0) AssetDatabase.CreateAsset(tmp_AssetPack, tmp_AssetPackPath); } else { if (tmp_AssetPack.AssetCount > 0) EditorUtility.SetDirty(tmp_AssetPack); else if (AssetDatabase.LoadAssetAtPath(tmp_AssetPackPath) != null) AssetDatabase.DeleteAsset(tmp_AssetPackPath); } #endif AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return new[] {tmp_AssetPackPath, tmp_JsonPath}; } public static BuildTarget GetBuildTarget() { #if UNITY_ANDROID && HANDHELD_ARMODULE_INSTALL return BuildTarget.Android; #elif UNITY_IOS && HANDHELD_ARMODULE_INSTALL return BuildTarget.iOS; #elif UNITY_ANDROID && (PICO_MODULE_INSTALL || ROKID_MODULE_INSTALL || QUEST_MODULE_INSTALL || XREAL_MODULE_INSTALL) return BuildTarget.Android; #elif UNITY_VISIONOS return BuildTarget.VisionOS; #elif UNITY_EDITOR_WIN return BuildTarget.StandaloneWindows64; #elif UNITY_EDITOR_OSX return BuildTarget.StandaloneOSX; #elif UNITY_EDITOR && (!UNITY_EDITOR_OSX||!UNITY_EDITOR_WIN) return BuildTarget.StandaloneLinux64; #endif } public static BuildTargetGroup GetBuildTargetGroup() { #if UNITY_ANDROID && HANDHELD_ARMODULE_INSTALL return BuildTargetGroup.Android; #elif UNITY_IOS && HANDHELD_ARMODULE_INSTALL return BuildTargetGroup.iOS; #elif UNITY_ANDROID && (PICO_MODULE_INSTALL || ROKID_MODULE_INSTALL || QUEST_MODULE_INSTALL || XREAL_MODULE_INSTALL) return BuildTargetGroup.Android; #elif UNITY_VISIONOS return BuildTargetGroup.VisionOS; #elif UNITY_EDITOR_OSX || UNITY_EDITOR_WIN || UNITY_EDITOR return BuildTargetGroup.Standalone; #endif } public static BuildTarget GetBuildTargetViaSDKDeviceType(SDKDeviceType _platformName) { switch (_platformName) { case SDKDeviceType.VisionOS: return BuildTarget.VisionOS; case SDKDeviceType.HandheldAR: #if UNITY_IOS return BuildTarget.iOS; #elif UNITY_ANDROID return BuildTarget.Android; #endif break; case SDKDeviceType.Hololens: return BuildTarget.WSAPlayer; case SDKDeviceType.Pico: case SDKDeviceType.Quest: case SDKDeviceType.Rokid: case SDKDeviceType.XReal: return BuildTarget.Android; case SDKDeviceType.WebAR: case SDKDeviceType.WebVR: case SDKDeviceType.Classic3D: case SDKDeviceType.Web3D: default: break; } #if UNITY_EDITOR_WIN return BuildTarget.StandaloneWindows64; #elif UNITY_EDITOR_OSX return BuildTarget.StandaloneOSX; #elif UNITY_EDITOR && (!UNITY_EDITOR_OSX||!UNITY_EDITOR_WIN) return BuildTarget.StandaloneLinux64; #endif } public static BuildTargetGroup GetBuildTargetGroupViaSDKDeviceType(SDKDeviceType _platformName) { switch (_platformName) { case SDKDeviceType.VisionOS: return BuildTargetGroup.VisionOS; case SDKDeviceType.HandheldAR: #if UNITY_IOS return BuildTargetGroup.iOS; #elif UNITY_ANDROID return BuildTargetGroup.Android; #endif break; case SDKDeviceType.Hololens: return BuildTargetGroup.WSA; case SDKDeviceType.Pico: case SDKDeviceType.Quest: case SDKDeviceType.Rokid: case SDKDeviceType.XReal: return BuildTargetGroup.Android; case SDKDeviceType.WebAR: case SDKDeviceType.WebVR: case SDKDeviceType.Classic3D: case SDKDeviceType.Web3D: default: break; } return BuildTargetGroup.Standalone; } public static void CopyFolder(string _sourceFolderPath, string _destinationFolderPath) { // 检查源文件夹是否存在 if (!Directory.Exists(_sourceFolderPath)) { Console.WriteLine($"源文件夹 {_sourceFolderPath} 不存在。"); return; } // 检查目标文件夹是否存在,如果不存在则创建它 if (!Directory.Exists(_destinationFolderPath)) { Directory.CreateDirectory(_destinationFolderPath); } // 获取源文件夹中的所有文件和子文件夹 string[] tmp_Files = Directory.GetFiles(_sourceFolderPath); string[] tmp_Folders = Directory.GetDirectories(_sourceFolderPath); // 遍历文件并复制它们 foreach (string tmp_File in tmp_Files) { string tmp_FileName = Path.GetFileName(tmp_File); string tmp_DestinationFilePath = Path.Combine(_destinationFolderPath, tmp_FileName); File.Copy(tmp_File, tmp_DestinationFilePath, true); Console.WriteLine($"已复制文件 {tmp_FileName}。"); } // 递归复制子文件夹 foreach (string folder in tmp_Folders) { CopyFolder(folder, Path.Combine(_destinationFolderPath, Path.GetFileName(folder))); } } public static string CalculateMD5Hash(string _input) { // 将输入字符串转换为字节数组 // 推荐使用 UTF8 编码 byte[] tmp_InputBytes = Encoding.UTF8.GetBytes(_input); // 创建 MD5 哈希算法实例 using MD5 tmp_MD5 = MD5.Create(); // 计算哈希值 var tmp_HashBytes = tmp_MD5.ComputeHash(tmp_InputBytes); StringBuilder tmp_Sb = new StringBuilder(); foreach (var tmp_HashByte in tmp_HashBytes) { // x2 表示十六进制,两位,不足补0 tmp_Sb.Append(tmp_HashByte.ToString("x2")); } return tmp_Sb.ToString(); } public static void CreateRuntimeAssetReferenceDatabase(string _savePath) { string tmp_FileName = $"{nameof(RuntimeAssetReferenceDatabase)}.asset"; string tmp_CreateFilePath = Path.Combine(_savePath, "Cache"); if (!Directory.Exists(tmp_CreateFilePath)) Directory.CreateDirectory(tmp_CreateFilePath); string tmp_FilePath = ShortenPath(Path.Combine(tmp_CreateFilePath, tmp_FileName)); if (AssetDatabase.AssetPathExists(tmp_FilePath)) { return; } var tmp_Rardb = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(tmp_Rardb, tmp_FilePath); EditorUtility.SetDirty(tmp_Rardb); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); } // 判断是否为合法类名 public static bool IsValidClassName(string _name) { return Regex.IsMatch(_name, @"^[_\p{L}][_\p{L}\p{N}]*$"); } public static bool IsValidProject(string _name) { return PackageToolsEditor.ALL_PROJECT_CACHE.ProjectCacheDataList.Find(_d => _d.Project.DisplayName == _name) == null; } } }