diff --git a/Assets/Cryville/Crtr/Browsing/Actions/ImportResourceAction.cs b/Assets/Cryville/Crtr/Browsing/Actions/ImportResourceAction.cs index 8e4264a..90a69e8 100644 --- a/Assets/Cryville/Crtr/Browsing/Actions/ImportResourceAction.cs +++ b/Assets/Cryville/Crtr/Browsing/Actions/ImportResourceAction.cs @@ -3,8 +3,8 @@ using System; namespace Cryville.Crtr.Browsing.Actions { internal class ImportResourceAction : IResourceAction { - readonly IResourceManager _destination; - public ImportResourceAction(IResourceManager destination) { + readonly IResourceDestination _destination; + public ImportResourceAction(IResourceDestination destination) { _destination = destination; } @@ -12,7 +12,7 @@ namespace Cryville.Crtr.Browsing.Actions { public int Priority { get { return 0; } } public bool CanInvoke(Uri uri, IResourceMeta resource) { - throw new NotImplementedException(); + return _destination.CanImport(uri); } public void Invoke(Uri uri, IResourceMeta resource) { if (_destination.ImportFrom(uri)) diff --git a/Assets/Cryville/Crtr/Browsing/FileSystemResourceManager.cs b/Assets/Cryville/Crtr/Browsing/FileSystemResourceManager.cs index b3dae02..1043df8 100644 --- a/Assets/Cryville/Crtr/Browsing/FileSystemResourceManager.cs +++ b/Assets/Cryville/Crtr/Browsing/FileSystemResourceManager.cs @@ -1,4 +1,5 @@ using Cryville.Common; +using Cryville.Crtr.Browsing.Actions; using Cryville.Crtr.UI; using System; using System.Collections.Generic; @@ -118,7 +119,7 @@ namespace Cryville.Crtr.Browsing { ItemChanged?.Invoke(); } - public bool ImportFrom(Uri uri) { throw new NotSupportedException(); } + public IResourceAction GetImportAction() { throw new NotSupportedException(); } public void RemoveAt(int index) { throw new NotSupportedException(); } } diff --git a/Assets/Cryville/Crtr/Browsing/IResourceDestination.cs b/Assets/Cryville/Crtr/Browsing/IResourceDestination.cs new file mode 100644 index 0000000..da485b8 --- /dev/null +++ b/Assets/Cryville/Crtr/Browsing/IResourceDestination.cs @@ -0,0 +1,8 @@ +using System; + +namespace Cryville.Crtr.Browsing { + public interface IResourceDestination { + bool CanImport(Uri uri); + bool ImportFrom(Uri uri); + } +} diff --git a/Assets/Cryville/Crtr/Browsing/IResourceDestination.cs.meta b/Assets/Cryville/Crtr/Browsing/IResourceDestination.cs.meta new file mode 100644 index 0000000..d8f3b32 --- /dev/null +++ b/Assets/Cryville/Crtr/Browsing/IResourceDestination.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a81bd44e67e24a34782dfda1e6565fab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Cryville/Crtr/Browsing/IResourceManager.cs b/Assets/Cryville/Crtr/Browsing/IResourceManager.cs index 1e0e1f6..b984769 100644 --- a/Assets/Cryville/Crtr/Browsing/IResourceManager.cs +++ b/Assets/Cryville/Crtr/Browsing/IResourceManager.cs @@ -1,3 +1,4 @@ +using Cryville.Crtr.Browsing.Actions; using System; using System.Collections.Generic; @@ -10,7 +11,7 @@ namespace Cryville.Crtr.Browsing { event Action ItemChanged; bool IsReadOnly { get; } - bool ImportFrom(Uri uri); + IResourceAction GetImportAction(); void RemoveAt(int index); void Activate(); diff --git a/Assets/Cryville/Crtr/Browsing/Legacy/LegacyChartResourceManager.cs b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyChartResourceManager.cs index 43ee3ce..d5bb626 100644 --- a/Assets/Cryville/Crtr/Browsing/Legacy/LegacyChartResourceManager.cs +++ b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyChartResourceManager.cs @@ -1,5 +1,6 @@ -using Cryville.Common.Unity; using Cryville.Common; +using Cryville.Common.Unity; +using Cryville.Crtr.Browsing.Actions; using Cryville.Crtr.Extension; using Newtonsoft.Json; using System.IO; @@ -9,7 +10,7 @@ namespace Cryville.Crtr.Browsing.Legacy { internal class LegacyChartResourceManager : LegacyResourceManager { protected override string Extension { get { return ".umgc"; } } - public LegacyChartResourceManager(string rootPath) : base(rootPath) { } + public LegacyChartResourceManager(LegacyResourceStore store) : base(store) { } protected override string GetSubRootPath(string rootPath) { return Path.Combine(rootPath, "charts"); @@ -37,5 +38,9 @@ namespace Cryville.Crtr.Browsing.Legacy { Meta = meta, }; } + + public override IResourceAction GetImportAction() { + return _store.ImportAction; + } } } diff --git a/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceManager.cs b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceManager.cs index 42d5fb1..8bd9980 100644 --- a/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceManager.cs +++ b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceManager.cs @@ -1,7 +1,5 @@ +using Cryville.Crtr.Browsing.Actions; 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; @@ -10,7 +8,7 @@ using System.Linq; namespace Cryville.Crtr.Browsing.Legacy { internal abstract class LegacyResourceManager : IPathedResourceManager where T : IResourceMeta { - readonly string _rootPath; + protected readonly LegacyResourceStore _store; DirectoryInfo _cd; readonly FileSystemWatcher _watcher = new FileSystemWatcher(); DirectoryInfo[] _items = new DirectoryInfo[0]; @@ -21,17 +19,12 @@ namespace Cryville.Crtr.Browsing.Legacy { public IList CurrentDirectory { get { return m_dirParts; } } 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); - } + public LegacyResourceManager(LegacyResourceStore store) { + _store = store; + _store.ResourceImported += ReloadFiles; m_dirParts = _dirParts.AsReadOnly(); _watcher.Changed += OnFileChanged; _watcher.Created += OnFileChanged; @@ -56,12 +49,12 @@ namespace Cryville.Crtr.Browsing.Legacy { } protected abstract string GetSubRootPath(string rootPath); - void ReloadFiles() { + protected void ReloadFiles() { _items = _cd.GetDirectories(); ApplyFilter(); } void OnDirectoryChange() { - string path = Path.Combine(GetSubRootPath(_rootPath), string.Join(Path.DirectorySeparatorChar, _dirParts)); + string path = Path.Combine(GetSubRootPath(_store.RootPath), string.Join(Path.DirectorySeparatorChar, _dirParts)); _cd = new DirectoryInfo(path); _watcher.Path = path; _watcher.EnableRaisingEvents = true; @@ -122,166 +115,7 @@ namespace Cryville.Crtr.Browsing.Legacy { 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 Game.MainLogger.Log(1, "Resource", "Resource already exists: {0}", res); - } - else { - LogAndPopup(3, "Attempt to import unsupported resource: {0}", res); - } - } - ReloadFiles(); - 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(); - } + public abstract IResourceAction GetImportAction(); } } diff --git a/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceStore.cs b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceStore.cs new file mode 100644 index 0000000..3cf7012 --- /dev/null +++ b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceStore.cs @@ -0,0 +1,196 @@ +using Cryville.Crtr.Extension; +using Cryville.Crtr.Extensions.Umg; +using Cryville.Crtr.Extensions; +using Cryville.Crtr.UI; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.IO; +using System; +using System.Linq; +using Cryville.Crtr.Browsing.Actions; + +namespace Cryville.Crtr.Browsing.Legacy { + internal class LegacyResourceStore : IResourceDestination { + public string RootPath { get; private set; } + public IResourceAction ImportAction { get; private set; } + public event Action ResourceImported; + + static bool _init; + public LegacyResourceStore(string rootPath) { + RootPath = rootPath; + ImportAction = new ImportResourceAction(this); + if (!_init) { + _init = true; + ExtensionManager.Init(rootPath); + } + } + + public bool CanImport(Uri uri) { + if (!uri.IsFile) return false; + return CanImportFile(new FileInfo(uri.LocalPath)); + } + protected bool CanImportFile(FileInfo file) { + return !file.FullName.StartsWith(new DirectoryInfo(RootPath).FullName); + } + public bool ImportFrom(Uri uri) { + 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 Game.MainLogger.Log(1, "Resource", "Resource already exists: {0}", res); + } + else { + LogAndPopup(3, "Attempt to import unsupported resource: {0}", res); + } + } + ResourceImported?.Invoke(); + 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(); + } + } +} diff --git a/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceStore.cs.meta b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceStore.cs.meta new file mode 100644 index 0000000..49803ac --- /dev/null +++ b/Assets/Cryville/Crtr/Browsing/Legacy/LegacyResourceStore.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af4d457931ff3e84fa84fc719290be46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Cryville/Crtr/Browsing/Legacy/LegacySkinResourceManager.cs b/Assets/Cryville/Crtr/Browsing/Legacy/LegacySkinResourceManager.cs index 36e3141..a4dc3be 100644 --- a/Assets/Cryville/Crtr/Browsing/Legacy/LegacySkinResourceManager.cs +++ b/Assets/Cryville/Crtr/Browsing/Legacy/LegacySkinResourceManager.cs @@ -1,3 +1,4 @@ +using Cryville.Crtr.Browsing.Actions; using Cryville.Crtr.Skin; using Newtonsoft.Json; using System.IO; @@ -6,7 +7,7 @@ namespace Cryville.Crtr.Browsing.Legacy { internal class LegacySkinResourceManager : LegacyResourceManager { protected override string Extension { get { return ".umgs"; } } - public LegacySkinResourceManager(string rootPath) : base(rootPath) { } + public LegacySkinResourceManager(LegacyResourceStore store) : base(store) { } protected override string GetSubRootPath(string rootPath) { return Path.Combine(rootPath, "skins"); @@ -23,5 +24,9 @@ namespace Cryville.Crtr.Browsing.Legacy { else return new LegacySkinDetail { OverrideName = dir.Name }; } + + public override IResourceAction GetImportAction() { + return _store.ImportAction; + } } } diff --git a/Assets/Cryville/Crtr/Browsing/UI/PathedResourceBrowser.cs b/Assets/Cryville/Crtr/Browsing/UI/PathedResourceBrowser.cs index a124eff..b90feeb 100644 --- a/Assets/Cryville/Crtr/Browsing/UI/PathedResourceBrowser.cs +++ b/Assets/Cryville/Crtr/Browsing/UI/PathedResourceBrowser.cs @@ -67,7 +67,7 @@ namespace Cryville.Crtr.Browsing.UI { _manager.ItemChanged += OnItemChanged; _manager.DirectoryChanged += OnDirectoryChanged; if (!_manager.IsReadOnly) { - Master.Actions.Register(_importAction = new ImportResourceAction(_manager)); + Master.Actions.Register(_importAction = _manager.GetImportAction()); _importActionArray[0] = _importAction; } foreach (var tool in m_writeTools) tool.interactable = !_manager.IsReadOnly; diff --git a/Assets/Cryville/Crtr/Browsing/UI/ResourceBrowserMaster.cs b/Assets/Cryville/Crtr/Browsing/UI/ResourceBrowserMaster.cs index 3ef4b0e..366c066 100644 --- a/Assets/Cryville/Crtr/Browsing/UI/ResourceBrowserMaster.cs +++ b/Assets/Cryville/Crtr/Browsing/UI/ResourceBrowserMaster.cs @@ -35,8 +35,9 @@ namespace Cryville.Crtr.Browsing.UI { Actions.Register(new OpenConfigAction()); Actions.Register(new UseSkinAction()); - OnTabClicked(AddPathedBrowserTab("Local Charts", new LegacyChartResourceManager(Settings.Default.GameDataPath))); - AddPathedBrowserTab("Local Skins", new LegacySkinResourceManager(Settings.Default.GameDataPath)); + var legacyStore = new LegacyResourceStore(Settings.Default.GameDataPath); + OnTabClicked(AddPathedBrowserTab("Local Charts", new LegacyChartResourceManager(legacyStore))); + AddPathedBrowserTab("Local Skins", new LegacySkinResourceManager(legacyStore)); AddPathedBrowserTab("Files", new FileSystemResourceManager()); AddTab("Settings", InitBrowser(m_settingsBrowser), _tabs.Count, false); }