using System.IO; using System.Threading.Tasks; using JetBrains.Annotations; using Robust.Shared.ContentPack; using Robust.Shared.Utility; namespace Content.Client.Audio.Midi; /// /// Handles storage/management of MIDI files stored inside the user data directory. /// [PublicAPI] public sealed partial class MidiFileCollectionManager : IPostInjectInit { /// /// Directory path to use inside UserData for storing MIDIs. /// public static readonly ResPath UserMidiDirectory = new("/UserMidis/"); private const string SawmillCategory = "midifilecollection"; [Dependency] private IResourceManager _resManager = default!; [Dependency] private ILogManager _logManager = default!; private ISawmill _sawmill = default!; private readonly List _filePaths = []; /// /// Raised after a MIDI file has been added to the library. Contains added file path as argument. /// public event Action? MidiFileAdded; /// /// Raised after a MIDI file has been removed from the library. Contains removed file path as argument. /// public event Action? MidiFileRemoved; /// /// Raised after more than one or two library changes occured at once. (i.e. initial load or when all items deleted) /// public event Action? MidiFilesReset; /// public void PostInject() { EnsureMidiDirectoryExists(); ReloadLibrary(); _sawmill = _logManager.GetSawmill(SawmillCategory); } /// /// Returns the binary content of the given MIDI file. /// /// MIDI file path to get. /// MIDI binary as a byte array or an empty byte array if the file doesn't exist. public byte[] GetMidiData(ResPath filePath) { try { var fullPath = UserMidiDirectory / filePath; return _resManager.UserData.ReadAllBytes(fullPath); } catch (Exception e) { _sawmill.Error($"Failed to read MIDI data from '{filePath}': {e.Message}"); return []; } } /// /// Returns an enumeration of all available MIDI files. /// /// An enumeration of MIDI file paths. public IEnumerable GetMidiFiles() { return _filePaths; } /// /// Stores the given byte stream with the given file path inside the directory. /// /// File path to write. /// Binary data to write. /// Raises on success. public async Task AddMidiFile(ResPath filePath, Stream data) { try { await using var file = _resManager.UserData.OpenWrite(UserMidiDirectory / filePath); await data.CopyToAsync(file); _filePaths.Add(filePath); MidiFileAdded?.Invoke(filePath); return true; } catch (Exception e) { _sawmill.Error($"Failed to store MIDI file '{filePath}' in library: {e.Message}"); return false; } } /// /// Stores the given byte array with the given file path inside the directory. /// /// File path to write. /// Binary data to write. /// Raises on success. public async Task AddMidiFile(ResPath filePath, byte[] data) { return await AddMidiFile(filePath, new MemoryStream(data)); } /// /// Renames a MIDI file inside the library. /// /// Current file path /// New file path /// Raises and on success. public bool RenameMidiFile(ResPath oldPath, ResPath newPath) { try { var fullOldPath = UserMidiDirectory / oldPath; var fullNewPath = UserMidiDirectory / newPath; fullOldPath = fullOldPath.Clean(); fullNewPath = fullNewPath.Clean(); _resManager.UserData.Rename(fullOldPath, fullNewPath); _filePaths.Remove(oldPath); MidiFileRemoved?.Invoke(oldPath); _filePaths.Add(newPath); MidiFileAdded?.Invoke(newPath); return true; } catch (Exception e) { _sawmill.Error($"Failed to rename MIDI file '{oldPath}' with '{newPath}': {e.Message}"); return false; } } /// /// Permanently removes the given MIDI file if it exists inside . /// /// File path to remove. /// Raises public void RemoveMidiFile(ResPath filePath) { DeleteMidiFile(filePath); _filePaths.Remove(filePath); MidiFileRemoved?.Invoke(filePath); } /// /// Removes all registered MIDI files, permanently. /// /// Raises public void RemoveAllMidiFiles() { foreach (var path in _filePaths) { DeleteMidiFile(path); } _filePaths.Clear(); MidiFilesReset?.Invoke(); } /// /// Clears and reloads the entire MIDI library. /// public void ReloadLibrary() { _filePaths.Clear(); if (!_resManager.UserData.IsDir(UserMidiDirectory)) return; foreach (var path in _resManager.UserData.DirectoryEntries(UserMidiDirectory)) { var filePath = new ResPath(UserMidiDirectory + path); if (!filePath.Extension.Equals("midi") && !filePath.Extension.Equals("mid")) continue; _filePaths.Add(new ResPath(path)); } MidiFilesReset?.Invoke(); } private void DeleteMidiFile(ResPath filePath) { var path = (UserMidiDirectory / filePath).Clean(); _resManager.UserData.Delete(path); } private void EnsureMidiDirectoryExists() { if (!_resManager.UserData.Exists(UserMidiDirectory)) _resManager.UserData.CreateDir(UserMidiDirectory); } }