using System;
using System.Collections.Generic;
using System.IO;
using Cthangover.Core.Mods;
using Cthangover.Core.Mods.Providers;
namespace Cthangover.Tools.Services
{
///
/// File access shared by the editors: listing a mod's files by folder and
/// extension, reading them through the mod provider and writing them back.
/// Reading works for every mod (folder, zip, test-suite subdir), writing only
/// for folder-backed mods — the only ones with a real directory behind them.
/// New files are created under the provider's RootPath because
/// GetFileSystemPath reports null for files that do not exist
/// yet, and an editor must be able to author a brand-new file.
///
public static class ModFileService
{
/// IDs of every loaded folder-backed (writable) mod, sorted.
public static List ListWritableMods()
{
var result = new List();
var manager = ModManager.Instance;
if (manager?.Mods == null)
return result;
foreach (var pair in manager.Mods)
{
if (pair.Value?.FileProvider is FolderModFileProvider)
result.Add(pair.Key);
}
result.Sort(StringComparer.OrdinalIgnoreCase);
return result;
}
/// True when the mod is folder-backed, i.e. the editors can save into it.
public static bool IsWritable(string modId)
{
return !string.IsNullOrEmpty(modId)
&& ModManager.Instance.GetMod(modId)?.FileProvider is FolderModFileProvider;
}
///
/// Mod-relative paths of the files under
/// (recursively) ending with , sorted.
///
public static List ListFiles(string modId, string directory, string extension)
{
var result = new List();
if (string.IsNullOrEmpty(modId))
return result;
Collect(modId, directory, extension, result);
result.Sort(StringComparer.OrdinalIgnoreCase);
return result;
}
/// Reads a file through the mod provider; null when missing.
public static string ReadText(string modId, string relativePath)
{
return ModManager.Instance.ReadFileText(modId, relativePath);
}
///
/// Writes a file into a folder-backed mod, creating missing directories.
/// Returns false for zip/virtual mods, unknown mods and empty paths.
///
public static bool WriteText(string modId, string relativePath, string text)
{
var absolute = GetWritablePath(modId, relativePath);
if (absolute == null)
return false;
var directory = Path.GetDirectoryName(absolute);
if (!string.IsNullOrEmpty(directory))
Directory.CreateDirectory(directory);
File.WriteAllText(absolute, text);
return true;
}
///
/// Absolute path a file of a folder-backed mod has (or would have once
/// written); null for read-only mods and empty paths.
///
public static string GetWritablePath(string modId, string relativePath)
{
if (string.IsNullOrEmpty(modId) || string.IsNullOrEmpty(relativePath))
return null;
if (ModManager.Instance.GetMod(modId)?.FileProvider is not FolderModFileProvider provider
|| string.IsNullOrEmpty(provider.RootPath))
return null;
return Path.GetFullPath(Path.Combine(provider.RootPath, relativePath.Replace('\\', '/')));
}
private static void Collect(string modId, string directory, string extension, List result)
{
foreach (var entry in ModManager.Instance.ListFiles(modId, directory))
{
if (entry.EndsWith("/"))
{
Collect(modId, entry.TrimEnd('/'), extension, result);
continue;
}
if (entry.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
result.Add(entry.Replace('\\', '/'));
}
}
}
}