using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using Robust.Shared.Utility; namespace Robust.Shared.ContentPack; /// /// A content root stored in memory, backed by a dictionary. /// public sealed class MemoryContentRoot : IContentRoot, IDisposable { private readonly Dictionary _files = new(); private readonly ReaderWriterLockSlim _lock = new(); /// /// Adds a file to the content root, or updates it if that path already exists. /// /// The relative path of the file. /// The data byte array to store in the content root. Stored as is, without being copied or cloned. public void AddOrUpdateFile(ResPath relPath, byte[] data) { // Just in case, we ensure it's a clean relative path. relPath = relPath.Clean().ToRelativePath(); _lock.EnterWriteLock(); try { _files[relPath] = data; } finally { _lock.ExitWriteLock(); } } /// /// Remove a file from this content root. /// /// The relative path to the file. /// public bool RemoveFile(ResPath relPath) { _lock.EnterWriteLock(); try { return _files.Remove(relPath); } finally { _lock.ExitWriteLock(); } } /// /// Removes ALL files from this content root. /// public void Clear() { _lock.EnterWriteLock(); try { _files.Clear(); } finally { _lock.ExitWriteLock(); } } public bool FileExists(ResPath relPath) { _lock.EnterReadLock(); try { return _files.ContainsKey(relPath); } finally { _lock.ExitReadLock(); } } /// public bool TryGetFile(ResPath relPath, [NotNullWhen(true)] out Stream? stream) { _lock.EnterReadLock(); try { if (!_files.TryGetValue(relPath, out var data)) { stream = null; return false; } // Non-writable stream, as this needs to be thread-safe. stream = new MemoryStream(data, false); return true; } finally { _lock.ExitReadLock(); } } /// public IEnumerable FindFiles(ResPath path) { _lock.EnterReadLock(); try { foreach (var (file, _) in _files) { if (file.TryRelativeTo(path, out _)) yield return file; } } finally { _lock.ExitReadLock(); } } /// public IEnumerable GetRelativeFilePaths() { _lock.EnterReadLock(); try { foreach (var (file, _) in _files) { yield return file.ToString(); } } finally { _lock.ExitReadLock(); } } /// /// Enumerates all files and their resource paths on this content root. /// /// Do not modify or keep around the returned byte array, it's meant to be read-only. public IEnumerable<(ResPath relPath, byte[] data)> GetAllFiles() { _lock.EnterReadLock(); try { foreach (var (p, d) in _files) { yield return (p, d); } } finally { _lock.ExitReadLock(); } } /// public void Mount() { // Nada. We don't need to perform any special logic here. } /// public void Dispose() { _lock.Dispose(); } }