using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; using System; using System.Collections; using UnityEngine.Rendering; using Newtonsoft.Json.Linq; public class UmaViewerMain : MonoBehaviour { private const string EnglishNamesCacheFileName = "english_names.cache"; private const string EnglishNamesCacheMagic = "UmaViewerEnglishNames"; private const int EnglishNamesCacheVersion = 1; public static UmaViewerMain Instance; public static bool WasEscapeConsumedThisFrame { get; private set; } private UmaViewerUI UI => UmaViewerUI.Instance; private UmaViewerBuilder Builder => UmaViewerBuilder.Instance; public RenderPipelineAsset DefaultRenderPipeline; public List Characters = new List(); public List MobCharacters = new List(); public List Lives = new List(); public List Costumes = new List(); public Dictionary AbList = new Dictionary(); public List AbMotions = new List(); public List AbSounds = new List(); public List AbChara = new List(); public List AbEffect = new List(); public List CostumeList = new List(); private void Awake() { Instance = this; new Config(); ApplyFrameRateLimit(); AbList = UmaDatabaseController.Instance.MetaEntries; if (AbList == null) return; var chara_3d = AbList.Where(ab => ab.Value.Type == UmaFileType._3d_cutt).Select(ab => ab.Value).ToList(); AbChara = chara_3d.FindAll(ab => ab.Name.StartsWith(UmaDatabaseController.CharaPath)); AbMotions = chara_3d.FindAll(ab => ab.Name.StartsWith(UmaDatabaseController.MotionPath)); AbEffect = chara_3d.FindAll(ab => ab.Name.StartsWith(UmaDatabaseController.EffectPath)); AbSounds = AbList.Where(ab => ab.Value.Type == UmaFileType.sound).Select(ab => ab.Value).ToList(); var outgame = AbList.Where(ab => ab.Value.Type == UmaFileType.outgame).Select(ab => ab.Value).ToList(); CostumeList = outgame.FindAll(e => e.Name.StartsWith(UmaDatabaseController.CostumePath)); } public static void ApplyFrameRateLimit() { QualitySettings.vSyncCount = 0; Application.targetFrameRate = Config.Instance.GetTargetFrameRate(); } private void Update() { WasEscapeConsumedThisFrame = false; TryConsumeEscapeForFullScreen(); } public static bool TryConsumeEscapeForFullScreen() { if (!Input.GetKeyDown(KeyCode.Escape) || !Screen.fullScreen) return false; Screen.fullScreenMode = FullScreenMode.Windowed; Screen.fullScreen = false; WasEscapeConsumedThisFrame = true; return true; } private IEnumerator Start() { if (AbList == null) yield break; int loadingStep = 0; int loadingStepsTotal = 10; var UmaCharaData = UmaDatabaseController.Instance.CharaData; var MobCharaData = UmaDatabaseController.Instance.MobCharaData; var loadingUI = UmaSceneController.instance; //修改(载入通用服装ColorSet相关) var charaDressColor = UmaDatabaseController.Instance.CharaDressColor; // Reuse downloaded English names; only fetch when no usable cache exists. bool downloadTranslations = false; string cachePath = Path.Combine(Application.persistentDataPath, EnglishNamesCacheFileName); if (Config.Instance.Language == Language.En) downloadTranslations = !TryLoadEnglishNamesCache(cachePath); loadingUI.LoadingProgressChange( loadingStep++, loadingStepsTotal, downloadTranslations ? "Downloading Translations" : "Loading Translations"); if (downloadTranslations) { yield return UmaViewerDownload.DownloadText(LocalizeEn.TranslationUrl, json => { if (string.IsNullOrEmpty(json)) return; try { if (!TryParseEnglishNames(json, out Dictionary charaNames, out Dictionary mobNames)) { Debug.LogWarning("Online English translations did not contain the required name tables. Using LocalizeEn fallback."); return; } ApplyEnglishNames(charaNames, mobNames); TrySaveEnglishNamesCache(cachePath, charaNames, mobNames); Debug.Log($"Loaded online English names: {charaNames.Count} characters, {mobNames.Count} mobs."); } catch (Exception ex) { Debug.LogWarning($"Could not parse online English translations. Using LocalizeEn fallback: {ex.Message}"); } }); } if (Config.Instance.WorkMode == WorkMode.Standalone) { var umaIcons = UmaCharaData.Select(item => AbList.TryGetValue($"chara/chr{item["id"]}/chr_icon_{item["id"]}", out UmaDatabaseEntry entry) ? entry : null).Where(entry => entry != null); var mobIcons = MobCharaData.Select(item => AbList.TryGetValue($"mob/mob_chr_icon_{item["mob_id"]}_000001_01", out UmaDatabaseEntry entry) ? entry : null).Where(entry => entry != null); var costumeIcons = CostumeList; List filesToDownload = umaIcons.Concat(mobIcons).Concat(costumeIcons).ToList(); yield return UmaViewerDownload.DownloadAssets(filesToDownload, UmaSceneController.instance.LoadingProgressChange); filesToDownload.Clear(); } loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading Character Data"); yield return null; foreach (var item in UmaCharaData) { var id = Convert.ToInt32(item["id"]); if (!Characters.Where(c => c.Id == id).Any()) { Characters.Add(new CharaEntry() { Name = item["charaname"].ToString(), EnName = LocalizeEn.GetCharaName(id), Icon = UmaViewerBuilder.Instance.LoadCharaIcon(id.ToString()), Id = id, ThemeColor = "#" + item["ui_nameplate_color_1"].ToString() }); } } loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading Mob Data"); yield return null; foreach (var item in MobCharaData) { if (Convert.ToInt32(item["use_live"]) == 0) { continue; } var id = Convert.ToInt32(item["mob_id"]); MobCharacters.Add(new CharaEntry() { Name = item["charaname"].ToString(), EnName = LocalizeEn.GetMobName(id), Icon = UmaViewerBuilder.Instance.LoadMobCharaIcon(id.ToString()), Id = id, IsMob = true }); } loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading Costumes Data"); yield return null; foreach (var item in CostumeList) { var costume = new CostumeEntry(); var name = Path.GetFileName(item.Name); costume.Id = name.Replace("dress_",""); costume.Icon = Builder.LoadSprite(item); Costumes.Add(costume); } var DressData = UmaDatabaseController.Instance.DressData; foreach (var data in DressData) { var costume = Costumes.FirstOrDefault(a => a.Id.Split('_')[0].Contains(data["id"].ToString()) ); if (costume != null) { costume.CharaId = Convert.ToInt32(data["chara_id"]); costume.DressName = data["dressname"].ToString(); costume.BodyType = Convert.ToInt32(data["body_type"]); costume.BodyTypeSub = Convert.ToInt32(data["body_type_sub"]); } } loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading Live Data"); yield return null; var asset = AbList["livesettings"]; if (asset != null) { string filePath = asset.FilePath; if (File.Exists(filePath)) { AssetBundle bundle = UmaAssetManager.LoadAssetBundle(asset, true); foreach (var item in UmaDatabaseController.Instance.LiveData) { var musicId = Convert.ToInt32(item["music_id"]); var songName = item["songname"].ToString(); var membercount = Convert.ToInt32(item["live_member_number"]); var defaultdress = Convert.ToInt32(item["default_main_dress"]); if (!Lives.Where(c => c.MusicId == musicId).Any()) { if (bundle.Contains(musicId.ToString())) { TextAsset liveData = bundle.LoadAsset(musicId.ToString()); Lives.Add(new LiveEntry(liveData.text) { MusicId = musicId, SongName = songName, MemberCount = membercount, DefaultDress = defaultdress, Icon = UmaViewerBuilder.Instance.LoadLiveIcon(musicId) }); } } } } } loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading UI"); yield return null; UI.LoadModelPanels(); loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading UI"); yield return null; UI.LoadMiniModelPanels(); loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading UI"); yield return null; UI.LoadPropPanel(); loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading UI"); yield return null; UI.LoadMapPanel(); loadingUI.LoadingProgressChange(loadingStep++, loadingStepsTotal, "Loading UI"); yield return null; UI.LoadLivePanels(); loadingUI.LoadingProgressChange(-1, -1); //Load Shader First var shaders = UmaAssetManager.LoadAssetBundle(AbList["shader"], true); Builder.ShaderList = new List(shaders.LoadAllAssets()); Gallop.ShaderManager.InitManager(); Gallop.ShaderManager.WarmupDofBloomShader(); } private static bool TryParseEnglishNames( string json, out Dictionary charaNames, out Dictionary mobNames) { JObject translations = JObject.Parse(json); charaNames = translations[LocalizeEn.CharaTranslationTableKey] ?.ToObject>(); mobNames = translations[LocalizeEn.MobTranslationTableKey] ?.ToObject>(); return charaNames != null && charaNames.Count > 0 && mobNames != null && mobNames.Count > 0; } private static void ApplyEnglishNames( Dictionary charaNames, Dictionary mobNames) { foreach (KeyValuePair pair in charaNames) { if (!string.IsNullOrEmpty(pair.Value)) LocalizeEn.SetCharaName(pair.Key, pair.Value); } foreach (KeyValuePair pair in mobNames) { if (!string.IsNullOrEmpty(pair.Value)) LocalizeEn.SetMobName(pair.Key, pair.Value); } } private static bool TryLoadEnglishNamesCache(string cachePath) { if (!File.Exists(cachePath)) return false; try { using (var stream = File.OpenRead(cachePath)) using (var reader = new BinaryReader(stream)) { if (reader.ReadString() != EnglishNamesCacheMagic || reader.ReadInt32() != EnglishNamesCacheVersion) { throw new InvalidDataException("Unsupported English name cache format."); } Dictionary charaNames = ReadEnglishNameTable(reader); Dictionary mobNames = ReadEnglishNameTable(reader); if (charaNames.Count == 0 || mobNames.Count == 0) throw new InvalidDataException("English name cache is missing required name tables."); ApplyEnglishNames(charaNames, mobNames); Debug.Log($"Loaded cached English names: {charaNames.Count} characters, {mobNames.Count} mobs."); } return true; } catch (Exception ex) { Debug.LogWarning($"Could not load the cached English names. Using LocalizeEn fallback: {ex.Message}"); return false; } } private static void TrySaveEnglishNamesCache( string cachePath, Dictionary charaNames, Dictionary mobNames) { try { using (var stream = File.Create(cachePath)) using (var writer = new BinaryWriter(stream)) { writer.Write(EnglishNamesCacheMagic); writer.Write(EnglishNamesCacheVersion); WriteEnglishNameTable(writer, charaNames); WriteEnglishNameTable(writer, mobNames); } } catch (Exception ex) { Debug.LogWarning($"Could not save the updated English name cache: {ex.Message}"); } } private static Dictionary ReadEnglishNameTable(BinaryReader reader) { int count = reader.ReadInt32(); if (count < 0 || count > 100000) throw new InvalidDataException("Invalid English name cache entry count."); var names = new Dictionary(count); for (int i = 0; i < count; i++) { int id = reader.ReadInt32(); string value = reader.ReadString(); if (!string.IsNullOrEmpty(value)) names[id] = value; } return names; } private static void WriteEnglishNameTable( BinaryWriter writer, Dictionary names) { List> validNames = names .Where(pair => !string.IsNullOrEmpty(pair.Value)) .ToList(); writer.Write(validNames.Count); foreach (KeyValuePair pair in validNames) { writer.Write(pair.Key); writer.Write(pair.Value); } } public void OpenUrl(string url) { Application.OpenURL(url); } }