using Cryville.Common; using Cryville.Common.Unity; using Cryville.Crtr.Extension; using Cryville.Crtr.Extensions; using Cryville.Crtr.Extensions.Umg; using Cryville.Crtr.UI; using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; using Logger = Cryville.Common.Logging.Logger; namespace Cryville.Crtr.Browsing { internal class LegacyResourceManager : IPathedResourceManager { readonly string _rootPath; DirectoryInfo _cd; readonly FileSystemWatcher _watcher = new FileSystemWatcher(); DirectoryInfo[] _items = new DirectoryInfo[0]; public string[] CurrentDirectory { get; private set; } public int Count { get { return _items.Length; } } static bool _init; public event Action ItemChanged; int _version; public LegacyResourceManager(string rootPath) { _rootPath = rootPath; if (!_init) { _init = true; ExtensionManager.Init(rootPath); } _watcher.Changed += OnFileChanged; _watcher.Created += OnFileChanged; _watcher.Deleted += OnFileChanged; _watcher.Renamed += OnFileChanged; _watcher.Error += OnFileWatcherError; } void OnFileWatcherError(object sender, ErrorEventArgs e) { Logger.Log("main", 4, "Data", "An error occurred while watching file changes: {0}", e.GetException()); } void OnFileChanged(object sender, FileSystemEventArgs e) { ReloadFiles(); } void ReloadFiles() { _items = _cd.GetDirectories(); _version++; ItemChanged?.Invoke(); } public void ChangeDirectory(string[] dir) { CurrentDirectory = dir; string path = Path.Combine(_rootPath, "charts", Path.Combine(dir)); _cd = new DirectoryInfo(path); _watcher.Path = path; _watcher.EnableRaisingEvents = true; ReloadFiles(); } public void OpenDirectory(int id) { string[] nd = new string[CurrentDirectory.Length + 1]; Array.Copy(CurrentDirectory, nd, CurrentDirectory.Length); nd[CurrentDirectory.Length] = _items[id].Name; ChangeDirectory(nd); } public void ReturnToDirectory(int index) { string[] nd = new string[index + 1]; Array.Copy(CurrentDirectory, nd, index + 1); ChangeDirectory(nd); } public ChartDetail this[int index] { get { var item = _items[index]; ChartMeta meta = null; AsyncDelivery cover = null; var metaFile = new FileInfo(Path.Combine(item.FullName, ".umgc")); if (metaFile.Exists) { using (var reader = new StreamReader(metaFile.FullName)) { meta = JsonConvert.DeserializeObject(reader.ReadToEnd()); } if (meta.cover != null && meta.cover != "") { var coverFile = item.GetFiles(meta.cover); if (coverFile.Length > 0) { cover = new AsyncDelivery(); var task = new LoadTextureTask(PlatformConfig.FileProtocolPrefix + coverFile[0].FullName, cover.Deliver); cover.CancelSource = task.Cancel; Game.NetworkTaskWorker.SubmitNetworkTask(task); } } } return new ChartDetail { Cover = cover, Meta = meta, }; } } public Uri GetItemUri(int id) { var item = _items[id]; var meta = new ChartMeta(); var metaFile = new FileInfo(Path.Combine(item.FullName, ".umgc")); using (var reader = new StreamReader(metaFile.FullName)) { meta = JsonConvert.DeserializeObject(reader.ReadToEnd()); } return new Uri(Path.Combine(_items[id].Name, string.Format("{0}.json", meta.data))); } public bool ImportItemFrom(Uri uri) { if (!uri.IsFile) throw new NotSupportedException(); var file = new FileInfo(uri.LocalPath); IEnumerable converters; if (!ExtensionManager.TryGetConverters(file.Extension, out converters)) return false; foreach (var converter in converters) { var resources = new List(); var ses = new ConversionSession { OnResourceAdd = res => resources.Add(res) }; try { converter.Convert(file, ses); } catch (Exception ex) { LogAndPopupExtra(4, ex, "Failed to import resource: {0}", ex.Message); return false; } foreach (var res in resources) { if (!res.Valid) { LogAndPopup(3, "Attempt to import invalid resource: {0}", res); } else if (res is RawChartResource) { var tres = (RawChartResource)res; var dir = new DirectoryInfo(Path.Combine(_rootPath, "charts", res.Name)); if (!dir.Exists) dir.Create(); using (var writer = new StreamWriter(Path.Combine(dir.FullName, ".json"))) { writer.Write(JsonConvert.SerializeObject(ConvertChartData(tres.Main), Game.GlobalJsonSerializerSettings)); } using (var writer = new StreamWriter(Path.Combine(dir.FullName, ".umgc"))) { tres.Meta.data = ""; writer.Write(JsonConvert.SerializeObject(tres.Meta, Game.GlobalJsonSerializerSettings)); } if (tres.Meta.cover != null) { var coverFile = new FileInfo(Path.Combine(file.Directory.FullName, tres.Meta.cover)); if (coverFile.Exists) coverFile.CopyTo(Path.Combine(dir.FullName, tres.Meta.cover), true); } } else if (res is FileResource) { var tres = (FileResource)res; DirectoryInfo dest; bool singleFileFlag = false; if (res is ChartResource) dest = new DirectoryInfo(Path.Combine(_rootPath, "charts", res.Name)); else if (res is ExtensionResource) { dest = new DirectoryInfo(Path.Combine(_rootPath, "extensions")); singleFileFlag = true; Popup.Create("Please restart the game to reload the extensions"); } else if (res is RulesetResource) dest = new DirectoryInfo(Path.Combine(_rootPath, "rulesets", res.Name)); else if (res is SkinResource) dest = new DirectoryInfo(Path.Combine(_rootPath, "skins", (res as SkinResource).RulesetName, res.Name)); else if (res is SongResource) dest = new DirectoryInfo(Path.Combine(_rootPath, "songs", res.Name)); else { LogAndPopup(3, "Attempt to import unsupported file resource: {0}", res); continue; } if (singleFileFlag || !dest.Exists) { dest.Create(); tres.Master.CopyTo(Path.Combine(dest.FullName, singleFileFlag ? tres.Master.Name : tres.Master.Extension)); foreach (var attachment in tres.Attachments) { if (singleFileFlag) throw new InvalidOperationException("Internal error"); attachment.CopyTo(Path.Combine(dest.FullName, attachment.Name)); } } else LogAndPopup(1, "Resource already exists: {0}", res); } else { LogAndPopup(3, "Attempt to import unsupported resource: {0}", res); } } return true; } return false; } Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public struct Enumerator : IEnumerator { readonly LegacyResourceManager _list; int _index; readonly int _version; ChartDetail _current; public ChartDetail Current => _current; object IEnumerator.Current => Current; public Enumerator(LegacyResourceManager list) { _list = list; _index = 0; _version = list._version; _current = default(ChartDetail); } public void Dispose() { } public bool MoveNext() { LegacyResourceManager list = _list; if (_version != list._version) throw new InvalidOperationException("Collection was changed."); if (_index < list.Count) { _current = list[_index++]; return true; } else { _current = default(ChartDetail); return false; } } void IEnumerator.Reset() { if (_version != _list._version) throw new InvalidOperationException("Collection was changed."); _index = 0; _current = default(ChartDetail); } } void LogAndPopup(int level, string format, params object[] args) { var msg = string.Format(format, args); Logger.Log("main", level, "Resource", msg); Popup.Create(msg); } void LogAndPopupExtra(int level, object extraLog, string format, params object[] args) { var msg = string.Format(format, args); Logger.Log("main", level, "Resource", "{0}\n{1}", msg, extraLog); Popup.Create(msg); } public string[] GetSupportedFormats() { return ExtensionManager.GetSupportedFormats().ToArray(); } public IReadOnlyDictionary GetPresetPaths() { return ExtensionManager.GetLocalResourcePaths(); } static Chart ConvertChartData(ChartData i) { return new Chart { endtime = ConvertBeatTime(i.endtime), format = i.format, groups = ConvertGroups(i.groups), motions = ConvertMotions(i.motions), ruleset = i.ruleset, sigs = ConvertSignatures(i.sigs), sounds = ConvertSounds(i.sounds), time = ConvertBeatTime(i.time), }; } static BeatTime? ConvertBeatTime(Extension.BeatTime? value) { if (value == null) return null; return new BeatTime(value.Value.b, value.Value.n, value.Value.d); } static List ConvertGroups(List l) { return l.Select(i => new Chart.Group { endtime = ConvertBeatTime(i.endtime), motions = ConvertMotions(i.motions), notes = ConvertNotes(i.notes), time = ConvertBeatTime(i.time), tracks = ConvertTracks(i.tracks), }).ToList(); } static List ConvertJudges(List l) { return l.Select(i => new Chart.Judge { endtime = ConvertBeatTime(i.endtime), name = i.name, time = ConvertBeatTime(i.time), }).ToList(); } static List ConvertMotions(List l) { return l.Select(i => new Chart.Motion { endtime = ConvertBeatTime(i.endtime), motion = i.motion, sumfix = i.sumfix, time = ConvertBeatTime(i.time), }).ToList(); } static List ConvertNotes(List l) { return l.Select(i => new Chart.Note { endtime = ConvertBeatTime(i.endtime), judges = ConvertJudges(i.judges), motions = ConvertMotions(i.motions), time = ConvertBeatTime(i.time), }).ToList(); } static List ConvertSignatures(List l) { return l.Select(i => new Chart.Signature { endtime = ConvertBeatTime(i.endtime), tempo = i.tempo, time = ConvertBeatTime(i.time), }).ToList(); } static List ConvertSounds(List l) { return l.Select(i => new Chart.Sound { endtime = ConvertBeatTime(i.endtime), id = i.id, offset = i.offset, time = ConvertBeatTime(i.time), }).ToList(); } static List ConvertTracks(List l) { return l.Select(i => new Chart.Track { endtime = ConvertBeatTime(i.endtime), motions = ConvertMotions(i.motions), time = ConvertBeatTime(i.time), }).ToList(); } } }