namespace MrGameEng.Mods;
/// A file contributed by a mod, addressed by its content-relative path.
/// Path relative to the content folder, forward slashes.
/// Absolute path of the winning file on disk.
/// The mod that contributed the file.
public readonly record struct ModFile(string RelativePath, string FullPath, Mod Mod);
///
/// Merged view of one content folder (e.g. Textures) across the active mods in load
/// order: a later mod shipping a file under the same relative path overrides the earlier
/// one. Paths use forward slashes and match case-insensitively (Windows-friendly content).
///
public sealed class ModContentTree
{
private readonly Dictionary _files;
private ModContentTree(Dictionary files, IReadOnlyList ordered)
{
_files = files;
Files = ordered;
}
/// Winning files, sorted by relative path — deterministic for identical mod sets.
public IReadOnlyList Files { get; }
/// Returns the winning file for .
public bool TryGet(string relativePath, out ModFile file) =>
_files.TryGetValue(Normalize(relativePath), out file);
///
/// Builds the merged tree of over
/// (in load order). With only matching files are included
/// (e.g. ".png"); without them, every file.
///
public static ModContentTree Build(IReadOnlyList mods, string contentFolder, params string[] extensions)
{
var files = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var mod in mods)
{
var root = mod.ContentPath(contentFolder);
if (!Directory.Exists(root))
{
continue;
}
foreach (var fullPath in Directory.EnumerateFiles(root, "*", SearchOption.AllDirectories))
{
if (extensions.Length > 0 &&
!extensions.Contains(Path.GetExtension(fullPath), StringComparer.OrdinalIgnoreCase))
{
continue;
}
var relative = Normalize(Path.GetRelativePath(root, fullPath));
files[relative] = new ModFile(relative, fullPath, mod); // поздний мод побеждает
}
}
var ordered = files.Values.OrderBy(f => f.RelativePath, StringComparer.Ordinal).ToList();
return new ModContentTree(files, ordered);
}
private static string Normalize(string path) => path.Replace('\\', '/');
}