V4 / 2d54b29f17a8
ScenarioFileService.cs
Lilith/Core/Scenarios/ScenarioFileService.cs
Unchanged from the preceding versionCS
namespace Lilith.Core.Scenarios;
/// <summary>Loads trusted local application content, never code supplied by a model tool call.</summary>
public sealed class ScenarioFileService
{
public string RootDirectory { get; }
public ScenarioFileService(string? root = null) => RootDirectory = Path.GetFullPath(root ??
Environment.GetEnvironmentVariable("LILITH_SCENARIOS_ROOT") ?? Path.Combine(AppContext.BaseDirectory, "Scenarios"));
public string LoadScenarioXml(string scenarioName)
{
if (!ScenarioXmlParser.ValidName(scenarioName)) throw new ArgumentException("Invalid scenario name.");
var rootInfo = new DirectoryInfo(RootDirectory);
for (DirectoryInfo? directory = rootInfo; directory is not null; directory = directory.Parent)
if (directory.Exists && directory.Attributes.HasFlag(FileAttributes.ReparsePoint))
throw new InvalidDataException("Scenario roots may not traverse reparse points.");
string path = Path.Combine(RootDirectory, scenarioName + ".xml");
var info = new FileInfo(path);
if (!info.Exists) throw new FileNotFoundException("Unknown scenario: " + scenarioName);
if (info.Attributes.HasFlag(FileAttributes.ReparsePoint) || info.Length > 1_000_000)
throw new InvalidDataException("Invalid scenario file.");
string xml = File.ReadAllText(path);
if (ScenarioXmlParser.Parse(xml).Id != scenarioName) throw new InvalidDataException("Scenario ID must match its filename.");
return xml;
}
public IReadOnlyList<string> ListNames() => Directory.Exists(RootDirectory)
? Directory.EnumerateFiles(RootDirectory, "*.xml").Select(Path.GetFileNameWithoutExtension).OfType<string>()
.Where(ScenarioXmlParser.ValidName).OrderBy(n => n, StringComparer.Ordinal).Take(100).ToArray() : [];
}