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.Generic; using System.IO; using System.Linq; using UnityEngine; namespace Cryville.Crtr.Browsing { internal class LegacyResourceManager : IPathedResourceManager { readonly string _rootPath; DirectoryInfo _cd; readonly FileSystemWatcher _watcher = new FileSystemWatcher(); DirectoryInfo[] _items = new DirectoryInfo[0]; DirectoryInfo[] _filteredItems = new DirectoryInfo[0]; string _filter = string.Empty; readonly List _dirParts = new List(); public IList CurrentDirectory { get { return _dirParts.AsReadOnly(); } } public int Count { get { return _filteredItems.Length; } } static bool _init; public event Action ItemChanged; public event Action DirectoryChanged; 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; OnDirectoryChange(); } void OnFileWatcherError(object sender, ErrorEventArgs e) { Game.MainLogger.Log(4, "Data", "An error occurred while watching file changes: {0}", e.GetException()); } void OnFileChanged(object sender, FileSystemEventArgs e) { ReloadFiles(); } public void Activate() { _watcher.EnableRaisingEvents = !string.IsNullOrEmpty(_watcher.Path); } public void Deactivate() { _watcher.EnableRaisingEvents = false; } void ReloadFiles() { _items = _cd.GetDirectories(); ApplyFilter(); } void OnDirectoryChange() { string path = Path.Combine(_rootPath, "charts", string.Join(Path.DirectorySeparatorChar, _dirParts)); _cd = new DirectoryInfo(path); _watcher.Path = path; _watcher.EnableRaisingEvents = true; ReloadFiles(); DirectoryChanged?.Invoke(); } public void ChangeDirectory(IEnumerable dir) { _dirParts.Clear(); foreach (string dirPart in dir) _dirParts.Add(dirPart); OnDirectoryChange(); } public bool IsDirectory(int index) { return false; } public void OpenDirectory(int index) { _dirParts.Add(_filteredItems[index].Name); OnDirectoryChange(); } public void ReturnToDirectory(int index) { _dirParts.RemoveRange(index + 1, _dirParts.Count - index - 1); OnDirectoryChange(); } public void ApplyFilter(string filter) { _filter = filter; ApplyFilter(); } void ApplyFilter() { IEnumerable items = _items; foreach (var keyword in _filter.Split(' ', StringSplitOptions.RemoveEmptyEntries)) { items = items.Where(i => i.Name.Contains(keyword, StringComparison.CurrentCultureIgnoreCase)); } _filteredItems = items.ToArray(); ItemChanged?.Invoke(); } public ChartDetail this[int index] { get { var item = _filteredItems[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, }; } } IResourceMeta IResourceManager.this[int index] { get { return this[index]; } } public Uri GetItemUri(int index) { var item = _filteredItems[index]; 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(PlatformConfig.FileProtocolPrefix + Path.Combine(_filteredItems[index].FullName, string.Format("{0}.json", meta.data))); } public bool IsReadOnly { get { return false; } } public void RemoveAt(int index) { _filteredItems[index].Delete(true); } public bool ImportFrom(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; } void LogAndPopup(int level, string format, params object[] args) { var msg = string.Format(format, args); Game.MainLogger.Log(level, "Resource", msg); Popup.Create(msg); } void LogAndPopupExtra(int level, object extraLog, string format, params object[] args) { var msg = string.Format(format, args); Game.MainLogger.Log(level, "Resource", "{0}\n{1}", msg, extraLog); Popup.Create(msg); } 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(); } } }