using XGame.Framework.i18n;
using XGame.Framework.Asset.Addressable;
using XGame.Framework.Asset.Addressable.Data;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace XGame.Editor.Asset
{
public static partial class AddressableHelper
{
private static readonly AddressableClassify[] QueryOrder = { AddressableClassify.Product, AddressableClassify.BuiltIn, AddressableClassify.Tests };
///
/// 检测AddressableId的唯一性
///
//[MenuItem("Tools/AssetBundle/VerifyAddressableId")]
public static void VerifyAddressableIdUniqueness()
{
var assetInfoMap = new Dictionary();
VerifyAddressableIdUniqueness(LoadAssetManifest(AddressableClassify.BuiltIn), ref assetInfoMap);
VerifyAddressableIdUniqueness(LoadAssetManifest(), ref assetInfoMap);
VerifyAddressableIdUniqueness(LoadAssetManifest(AddressableClassify.Tests), ref assetInfoMap);
Debug.Log("VerifyAddressableIdUniqueness completed.");
}
private static void VerifyAddressableIdUniqueness(AddressableInfosSo manifest, ref Dictionary assetInfoMap)
{
if (manifest == null || manifest.assetInfos == null)
{
//Debug.Log("Verify AddressableId Uniqueness. But can't find AddressableAssetInfoManifest.");
return;
}
//key:addressableId
////var assetInfoMap = new Dictionary();
foreach (var assetInfo in manifest.assetInfos)
{
if (assetInfoMap.ContainsKey(assetInfo.addressableId))
{
Debug.LogError($"AddressableId repeat. Last:{assetInfoMap[assetInfo.addressableId]} Next:{assetInfo}");
}
else
{
assetInfoMap.Add(assetInfo.addressableId, assetInfo);
}
}
}
///
/// 资源目录
///
public static string[] GetAssetsRoots(AddressableClassify classify = AddressableClassify.Product)
{
var roots = new List();
switch (classify)
{
case AddressableClassify.BuiltIn:
if (Directory.Exists(PathDefine.BuiltInResourcesRelative))
{
roots.Add(PathDefine.BuiltInResourcesRelative);
}
break;
case AddressableClassify.Tests:
if (Directory.Exists(PathDefine.TestsToAddressable))
{
roots.Add(PathDefine.TestsToAddressable);
}
if (Directory.Exists(PathDefine.BuiltInResTests))
{
roots.Add(PathDefine.BuiltInResTests);
}
break;
default:
return FileUtil.GetProductAssetsRoots(true);
//{
// if (Directory.Exists(PathDefine.ResAddressableRelative))
// {
// roots.Add(PathDefine.ResAddressableRelative);
// }
// if (Directory.Exists(PathDefine.ResourcesRelative))
// {
// roots.Add(PathDefine.ResourcesRelative);
// }
// if (Directory.Exists(PathDefine.I18nAssetsRelative))
// {
// var directorys = Directory.GetDirectories(PathDefine.I18nAssetsRelative, PathDefine.ResAddressableName, SearchOption.AllDirectories);
// foreach(var directory in directorys)
// {
// roots.Add(directory.Replace("\\", "/"));
// }
// }
//}
//break;
}
return roots.ToArray();
}
public static bool TryAssetPathToClassify(string assetPath, out AddressableClassify classify)
{
classify = AddressableClassify.Product;
if (string.IsNullOrEmpty(assetPath))
{
return false;
}
//assetPath = FileUtil.ToRelativePath(assetPath);
if (IsValidPath(assetPath, AddressableClassify.Product))
{
//在 Res/Addressable || Resources 目录下
classify = AddressableClassify.Product;
return true;
}
if (IsValidPath(assetPath, AddressableClassify.BuiltIn))
{
//在Package Framework目录下
classify = AddressableClassify.BuiltIn;
return true;
}
if (IsValidPath(assetPath, AddressableClassify.Tests))
{
//在ResStatic目录下
classify = AddressableClassify.Tests;
return true;
}
return false;
}
public static LanguageType GetLanguageTypeByAssetPath(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return LanguageType.NONE;
}
var match = i18nAssetRegex.Match(assetPath);
if (match.Success)
{
var i18nName = match.Groups[1].Value;
return LanguageUtils.ToLanguageType(i18nName);
}
return LanguageType.NONE;
}
///
/// i18n资源路径正则
///
private static Regex i18nAssetRegex = new Regex(@"^Assets/i18n/(.+?)/Res/Addressable");
///
/// 是否是国际化资源
///
///
///
public static bool IsI18nAssetPath(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return false;
}
if (i18nAssetRegex.IsMatch(assetPath))
{
//在国际化资源目录下
return true;
}
return false;
}
///
/// 检测路径是否是指定类型的资源路径
///
///
///
///
public static bool IsValidPath(string assetPath, AddressableClassify classify)
{
if (string.IsNullOrEmpty(assetPath))
{
return false;
}
switch (classify)
{
case AddressableClassify.BuiltIn:
if (assetPath.StartsWith(PathDefine.XGamePackageRelative))
{
//在Package Framework目录下
return true;
}
break;
case AddressableClassify.Tests:
if (assetPath.StartsWith(PathDefine.TestsToAddressable) || assetPath.StartsWith(PathDefine.BuiltInResTests))
{
//在ResStatic目录下 或者在 在Package ResStatic目录下
return true;
}
break;
default:
if (assetPath.StartsWith(PathDefine.ResAddressableRelative))
{
//在Res/Addressable目录下
return true;
}
if (assetPath.StartsWith(PathDefine.ResourcesRelative))
{
//在Resources目录下
return true;
}
if (IsI18nAssetPath(assetPath))
{
//在国际化资源目录下
return true;
}
break;
}
return false;
}
///
/// 检测一个资源路径是否有效
/// 用于判断指定Asset是否可以生成AddressableName
///
/// 使用Unity的相对路径
///
public static bool IsValidPath(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return false;
}
if (!File.Exists(assetPath))
{
Debug.LogWarning($"File can't find. Path:{assetPath}");
return false;
}
assetPath = FileUtil.ToRelativePath(assetPath);
if (IsValidPath(assetPath, AddressableClassify.Product))
{
//项目资源
return true;
}
if (IsValidPath(assetPath, AddressableClassify.BuiltIn))
{
//在Package Framework目录下
return true;
}
if (IsValidPath(assetPath, AddressableClassify.Tests))
{
//在ResStatic目录下
return true;
}
return false;
}
public static bool IsInResources(string assetPath, out string relativePath)
{
relativePath = string.Empty;
if (string.IsNullOrEmpty(assetPath))
{
return false;
}
if (!File.Exists(assetPath))
{
Debug.LogWarning($"File can't find. Path:{assetPath}");
return false;
}
//项目资源
assetPath = FileUtil.ToRelativePath(assetPath);
var resRelative = $"{PathDefine.ResourcesRelative}/";
if (assetPath.StartsWith(resRelative))
{
//在Resources目录下
relativePath = assetPath.Substring(resRelative.Length);
relativePath = relativePath.Replace(Path.GetExtension(relativePath), "");
//Debug.LogWarning($"assetPath: {assetPath} GetDirectoryName: {Path.GetDirectoryName(assetPath)} ResourcesPath: {resRelative} file:{Path.GetFileNameWithoutExtension(assetPath)} relativePath:{relativePath}");
return true;
}
//包内资源
var pkgResources = $"{PathDefine.BuiltInResourcesRelative}/";
if (assetPath.StartsWith(pkgResources))
{
//在Resources目录下
relativePath = assetPath.Substring(pkgResources.Length);
relativePath = relativePath.Replace(Path.GetExtension(relativePath), "");
//Debug.LogWarning($"assetPath: {assetPath} GetDirectoryName: {Path.GetDirectoryName(assetPath)} ResourcesPath: {pkgResources} file:{Path.GetFileNameWithoutExtension(assetPath)} relativePath:{relativePath}");
return true;
}
return false;
}
///
/// 取文件名
/// 若名字为数字,则加上父文件夹名
///
///
///
public static string GetFileName(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
return string.Empty;
var fileName = Path.GetFileNameWithoutExtension(assetPath);
// ReSharper disable once UnusedVariable
if (FileUtil.IsNumberic(fileName, out var result))
{
fileName = $"{FileUtil.GetDirectoryName(assetPath, false)}_{fileName}";
}
//删除空格,将'.'转为'_',然后转小写
fileName = fileName.Replace(" ", "").Replace('.', '_').ToLower();
return fileName;
}
public static string GetFileNameIncludeI18n(string assetPath)
{
var i18nName = string.Empty;
var langFlag = GetLanguageTypeByAssetPath(assetPath);
if (langFlag != LanguageType.NONE)
{
i18nName = langFlag.ToString().ToLower();
}
var fileName = GetFileName(assetPath);
if (langFlag != LanguageType.NONE)
{
fileName = $"{i18nName}_{fileName}";
}
return fileName;
}
///
/// GUID转AddressableName
///
///
///
public static string GUIDToName(string guid)
{
var addressableName = string.Empty;
if (!string.IsNullOrEmpty(guid))
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (TryAssetPathToClassify(assetPath, out var classify))
{
LoadAssetManifest(classify)?.TryGetAddressableNameByGUID(guid, out addressableName);
}
}
return addressableName;
}
///
/// AssetPath转AddressableName
///
///
///
public static string AssetPathToName(string assetPath)
{
var addressableName = string.Empty;
if (TryAssetPathToClassify(assetPath, out var classify))
{
var guid = AssetDatabase.AssetPathToGUID(assetPath);
LoadAssetManifest(classify)?.TryGetAddressableNameByGUID(guid, out addressableName);
}
return addressableName;
}
///
/// AddressableName转AssetPath
///
///
///
public static string NameToAssetPath(string addressableName)
{
if (!string.IsNullOrEmpty(addressableName))
addressableName = addressableName.ToLower();
for (int i = 0; i < QueryOrder.Length; i++)
{
var assetPath = NameToAssetPath(addressableName, QueryOrder[i]);
if (!string.IsNullOrEmpty(assetPath))
{
return assetPath;
}
}
return string.Empty;
}
private static string NameToAssetPath(string addressableName, AddressableClassify classify)
{
var manifest = LoadAssetManifest(classify);
if (manifest != null && manifest.assetInfos != null)
{
var assetInfos = manifest.assetInfos;
var index = System.Array.FindIndex(assetInfos, (info => info.addressableName.Equals(addressableName.ToLower())));
if (index >= 0)
{
return assetInfos[index].GetAssetPath();
}
}
return string.Empty;
}
///
/// LoadAsset
///
///
///
///
public static T LoadAssetByName(string addressableName) where T : Object
{
var assetPath = NameToAssetPath(addressableName);
if (string.IsNullOrEmpty(assetPath))
{
return default(T);
}
return AssetDatabase.LoadAssetAtPath(assetPath);
}
///
/// 加载源资源(非克隆对象)
///
///
///
///
public static T LoadMainAssetByName(string addressableName) where T : Object
{
var assetPath = NameToAssetPath(addressableName);
if (string.IsNullOrEmpty(assetPath))
{
return default(T);
}
return AssetDatabase.LoadMainAssetAtPath(assetPath) as T;
}
public static bool IsValidName(string addressableName)
{
var assetPath = NameToAssetPath(addressableName);
return string.IsNullOrEmpty(assetPath) == false;
}
///
/// 根据assetPath判断是否LOD资源
///
///
///
internal static bool IsLodAssetPath(string assetPath)
{
if (string.IsNullOrEmpty(assetPath)) return false;
var fileName = Path.GetFileNameWithoutExtension(assetPath).ToLower();
return fileName.EndsWith(PathDefine.LodHigh) || fileName.EndsWith(PathDefine.LodLow);
}
}
}