using System; using System.Collections.Generic; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif public static class SongDlcContentResolver { private const string RuntimeResourcesFolder = "so/songDlcContents"; private const string EditorSearchFolder = "Assets/Resources/so/songDlcContents"; private static SongDlcContentSO[] cachedAllContents; private static readonly Dictionary> CachedBySongId = new Dictionary>(); [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticState() { cachedAllContents = null; CachedBySongId.Clear(); } public static void InvalidateCache() { cachedAllContents = null; CachedBySongId.Clear(); } public static SongDlcContentSO GetContent(SongData song) { if (song == null) { return null; } EnsureCacheBuilt(); if (!CachedBySongId.TryGetValue(song.songID, out List list) || list == null || list.Count == 0) { return null; } string preferredDlcKey = song.belongsTo_whichDLC ?? string.Empty; for (int i = 0; i < list.Count; i++) { SongDlcContentSO item = list[i]; if (item == null) { continue; } if (IsMatchingDlc(item, preferredDlcKey)) { return item; } } return list[0]; } public static TextAsset GetChartFile(SongData song, int difficulty) { SongDlcContentSO content = GetContent(song); if (content == null || content.chartOverrides == null) { return null; } for (int i = 0; i < content.chartOverrides.Count; i++) { SongDlcChartOverrideEntry entry = content.chartOverrides[i]; if (entry == null || entry.difficulty != difficulty || entry.chartFile == null) { continue; } return entry.chartFile; } return null; } public static AudioClip GetSettlementMusic(SongData song) { SongDlcContentSO content = GetContent(song); if (content != null && content.settlementMusic != null) { return content.settlementMusic; } dlcData owningDlc = GetOwningDlc(song); return owningDlc != null ? owningDlc.settlementMusic : null; } public static dlcData GetOwningDlc(SongData song) { if (song == null) { return null; } SongDlcContentSO content = GetContent(song); if (content != null && content.ownerDlc != null) { return content.ownerDlc; } dlcData[] allDlcs = RuntimeResourcesCache.LoadAllDlcs(); string preferredDlcKey = song.belongsTo_whichDLC ?? string.Empty; dlcData fallback = null; for (int i = 0; i < allDlcs.Length; i++) { dlcData dlc = allDlcs[i]; if (dlc == null) { continue; } if (dlc.songList != null && dlc.songList.Contains(song)) { if (IsMatchingDlc(dlc, preferredDlcKey)) { return dlc; } fallback = fallback ?? dlc; } } return fallback; } public static SongDlcContentSO[] LoadAllSongDlcContents() { EnsureCacheBuilt(); return cachedAllContents ?? Array.Empty(); } private static bool IsMatchingDlc(SongDlcContentSO content, string preferredDlcKey) { if (content == null) { return false; } if (!string.IsNullOrWhiteSpace(preferredDlcKey)) { if (!string.IsNullOrWhiteSpace(content.sourceDlcId) && string.Equals(content.sourceDlcId.Trim(), preferredDlcKey.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } if (content.ownerDlc != null) { if (string.Equals(content.ownerDlc.dlcName ?? string.Empty, preferredDlcKey.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(content.ownerDlc.dlcID.ToString(), preferredDlcKey.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } private static bool IsMatchingDlc(dlcData dlc, string preferredDlcKey) { if (dlc == null || string.IsNullOrWhiteSpace(preferredDlcKey)) { return false; } string safeKey = preferredDlcKey.Trim(); return string.Equals(dlc.dlcName ?? string.Empty, safeKey, StringComparison.OrdinalIgnoreCase) || string.Equals(dlc.dlcID.ToString(), safeKey, StringComparison.OrdinalIgnoreCase); } private static void EnsureCacheBuilt() { if (cachedAllContents != null) { return; } cachedAllContents = LoadAllContentsInternal(); CachedBySongId.Clear(); for (int i = 0; i < cachedAllContents.Length; i++) { SongDlcContentSO content = cachedAllContents[i]; if (content == null || content.songId <= 0) { continue; } if (!CachedBySongId.TryGetValue(content.songId, out List list) || list == null) { list = new List(); CachedBySongId[content.songId] = list; } list.Add(content); } foreach (KeyValuePair> pair in CachedBySongId) { pair.Value.Sort(CompareContent); } } private static int CompareContent(SongDlcContentSO left, SongDlcContentSO right) { if (ReferenceEquals(left, right)) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int ownerCompare = string.CompareOrdinal(left.sourceDlcId ?? string.Empty, right.sourceDlcId ?? string.Empty); if (ownerCompare != 0) { return ownerCompare; } return string.CompareOrdinal(left.GetResolvedContentId(), right.GetResolvedContentId()); } private static SongDlcContentSO[] LoadAllContentsInternal() { #if UNITY_EDITOR if (!Application.isPlaying) { string[] searchFolders = { EditorSearchFolder }; string[] guids = AssetDatabase.FindAssets("t:SongDlcContentSO", searchFolders); if (guids != null && guids.Length > 0) { List result = new List(guids.Length); for (int i = 0; i < guids.Length; i++) { string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]); SongDlcContentSO content = AssetDatabase.LoadAssetAtPath(assetPath); if (content != null) { result.Add(content); } } return result.ToArray(); } string[] fallbackGuids = AssetDatabase.FindAssets("t:SongDlcContentSO"); List fallback = new List(fallbackGuids.Length); for (int i = 0; i < fallbackGuids.Length; i++) { string assetPath = AssetDatabase.GUIDToAssetPath(fallbackGuids[i]); SongDlcContentSO content = AssetDatabase.LoadAssetAtPath(assetPath); if (content != null) { fallback.Add(content); } } return fallback.ToArray(); } #endif return RuntimeResourcesCache.LoadAllSongDlcContents() ?? Array.Empty(); } }