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();
}
}