Files
ChronoCCG/Chrono/Build/Program.cs
T

259 lines
8.2 KiB
C#

using System.Text;
using System.Text.RegularExpressions;
using Chrono.Model;
var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", ".."));
var docsDir = Path.Combine(repoRoot, "chrono.docs");
var webWwwRoot = Path.Combine(repoRoot, "Chrono", "Web", "wwwroot");
var generatedFile = Path.Combine(repoRoot, "Chrono", "Web", "Generated", "Cards.g.cs");
Console.WriteLine($"Repo root: {repoRoot}");
Console.WriteLine($"Docs dir: {docsDir}");
Console.WriteLine($"Generated: {generatedFile}");
if (!Directory.Exists(docsDir))
{
Console.Error.WriteLine($"ERROR: docs dir not found: {docsDir}");
return 1;
}
var mdFiles = Directory.GetFiles(docsDir, "*.md");
var cards = new List<CardData>();
foreach (var file in mdFiles)
{
var content = Encoding.UTF8.GetString(File.ReadAllBytes(file));
if (!content.StartsWith("---"))
continue;
var endIndex = content.IndexOf("---", 3, StringComparison.Ordinal);
if (endIndex < 0)
continue;
var frontmatter = content[3..endIndex].Trim();
var yaml = ParseYaml(frontmatter);
var name = Path.GetFileNameWithoutExtension(file);
var category = yaml.GetValueOrDefault("category");
if (category == null) continue;
var imageFile = StripWikiLink(yaml.GetValueOrDefault("imageLink"));
if (imageFile != null && !imageFile.EndsWith(".png"))
imageFile += ".png";
var card = new CardData
{
Name = name,
Category = category,
Cost = ParseInt(yaml, "cost"),
Attack = ParseInt(yaml, "attack"),
Health = ParseInt(yaml, "health"),
Description = StripWikiLinks(yaml.GetValueOrDefault("description")),
Faction = StripWikiLink(yaml.GetValueOrDefault("faction")),
Set = StripWikiLink(yaml.GetValueOrDefault("set")),
Speed = StripWikiLink(yaml.GetValueOrDefault("speed")),
Archetypes = ParseList(yaml, "archetypes").Select(s => StripWikiLink(s) ?? "").Where(s => s != "").ToList(),
ImmortalizeTo = yaml.ContainsKey("immortalizeTo") ? ParseListOrScalar(yaml, "immortalizeTo").Select(s => StripWikiLink(s) ?? "").Where(s => s != "").ToList() : null,
ImmortalizeFrom = StripWikiLink(yaml.GetValueOrDefault("immortalizeFrom")),
ImmortalizeWhen = NullIfNa(StripWikiLinks(yaml.GetValueOrDefault("immortalizeWhen"))),
ImageFile = imageFile,
};
cards.Add(card);
}
// Copy PNGs to wwwroot/cards
var cardsDir = Path.Combine(webWwwRoot, "cards");
Directory.CreateDirectory(cardsDir);
foreach (var card in cards)
{
if (card.ImageFile == null) continue;
var src = Path.Combine(docsDir, card.ImageFile);
var dst = Path.Combine(cardsDir, card.ImageFile);
if (File.Exists(src))
File.Copy(src, dst, overwrite: true);
}
// Generate C# source file
Directory.CreateDirectory(Path.GetDirectoryName(generatedFile)!);
using var writer = new StreamWriter(generatedFile, false, Encoding.UTF8);
writer.WriteLine("// <auto-generated/>");
writer.WriteLine("#nullable enable");
writer.WriteLine();
writer.WriteLine("namespace Chrono.Model;");
writer.WriteLine();
writer.WriteLine("public static class CardDatabase");
writer.WriteLine("{");
writer.WriteLine(" public static readonly System.Collections.Generic.List<CardData> Cards =");
writer.WriteLine(" [");
for (int i = 0; i < cards.Count; i++)
{
var c = cards[i];
writer.WriteLine(" new()");
writer.WriteLine(" {");
WriteProp(writer, "Name", c.Name, 3);
WriteProp(writer, "Category", c.Category, 3);
WriteNullProp(writer, "Cost", c.Cost, 3);
WriteNullProp(writer, "Attack", c.Attack, 3);
WriteNullProp(writer, "Health", c.Health, 3);
WriteStrProp(writer, "Description", c.Description, 3);
WriteStrProp(writer, "Faction", c.Faction, 3);
WriteStrProp(writer, "Set", c.Set, 3);
WriteStrProp(writer, "Speed", c.Speed, 3);
WriteListProp(writer, "Archetypes", c.Archetypes, 3);
WriteListProp(writer, "ImmortalizeTo", c.ImmortalizeTo, 3);
WriteStrProp(writer, "ImmortalizeFrom", c.ImmortalizeFrom, 3);
WriteStrProp(writer, "ImmortalizeWhen", c.ImmortalizeWhen, 3);
WriteStrProp(writer, "ImageFile", c.ImageFile, 3);
var comma = i < cards.Count - 1 ? "," : "";
writer.WriteLine($" }}{comma}");
}
writer.WriteLine(" ];");
writer.WriteLine("}");
Console.WriteLine($"Generated {cards.Count} cards in {generatedFile}");
return 0;
// --- Helpers ---
static int? ParseInt(Dictionary<string, string> yaml, string key)
{
if (!yaml.TryGetValue(key, out var val)) return null;
if (int.TryParse(val, out var i)) return i;
return null;
}
static string? StripWikiLink(string? s)
{
if (s == null || s == "N/A") return null;
return Regex.Replace(s, @"\[\[([^\]]*)\]\]", "$1").Trim('"');
}
static string? StripWikiLinks(string? s)
{
if (s == null || s == "N/A") return null;
return Regex.Replace(s.Trim('"'), @"\[\[([^\]]*)\]\]", "$1").Trim();
}
static string? NullIfNa(string? s) => s is "N/A" or null ? null : s.Trim('"');
static List<string> ParseList(Dictionary<string, string> yaml, string key)
{
if (!yaml.TryGetValue(key, out var raw)) return [];
var result = new List<string>();
foreach (var item in raw.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
var trimmed = item.TrimStart('-', ' ').Trim(' ', '"');
if (trimmed.Length > 0) result.Add(trimmed);
}
return result;
}
static List<string> ParseListOrScalar(Dictionary<string, string> yaml, string key)
{
if (!yaml.TryGetValue(key, out var raw)) return [];
raw = raw.Trim();
if (!raw.StartsWith('-'))
return [raw.Trim('"')];
var result = new List<string>();
foreach (var item in raw.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
var trimmed = item.TrimStart('-', ' ').Trim(' ', '"');
if (trimmed.Length > 0) result.Add(trimmed);
}
return result;
}
static Dictionary<string, string> ParseYaml(string yaml)
{
var dict = new Dictionary<string, string>();
var lines = yaml.Split('\n');
string? currentKey = null;
var listBuffer = new List<string>();
foreach (var line in lines)
{
var trimmed = line.Trim();
if (trimmed.Length == 0) continue;
if (trimmed.StartsWith("- "))
{
listBuffer.Add(trimmed);
continue;
}
if (listBuffer.Count > 0 && currentKey != null)
{
dict[currentKey] = string.Join("\n", listBuffer);
listBuffer.Clear();
}
var colonIndex = trimmed.IndexOf(':');
if (colonIndex < 0) continue;
currentKey = trimmed[..colonIndex].Trim();
var value = trimmed[(colonIndex + 1)..].Trim();
if (value.Length == 0) continue;
dict[currentKey] = value;
}
if (listBuffer.Count > 0 && currentKey != null)
dict[currentKey] = string.Join("\n", listBuffer);
return dict;
}
static void WriteProp(StreamWriter w, string name, string value, int indent)
{
w.WriteLine($"{new string(' ', indent * 4)}{name} = {ToLiteral(value)},");
}
static void WriteStrProp(StreamWriter w, string name, string? value, int indent)
{
if (value == null) return;
w.WriteLine($"{new string(' ', indent * 4)}{name} = {ToLiteral(value)},");
}
static void WriteNullProp(StreamWriter w, string name, int? value, int indent)
{
if (value == null) return;
w.WriteLine($"{new string(' ', indent * 4)}{name} = {value},");
}
static void WriteListProp(StreamWriter w, string name, List<string>? values, int indent)
{
if (values == null) return;
var pad = new string(' ', indent * 4);
w.WriteLine($"{pad}{name} = [");
foreach (var v in values)
w.WriteLine($"{pad} {ToLiteral(v)},");
w.WriteLine($"{pad}],");
}
static string ToLiteral(string? s)
{
if (s == null) return "null";
var sb = new StringBuilder();
sb.Append('"');
foreach (char c in s)
{
switch (c)
{
case '"': sb.Append("\\\""); break;
case '\\': sb.Append("\\\\"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
case '\0': sb.Append("\\0"); break;
default: sb.Append(c); break;
}
}
sb.Append('"');
return sb.ToString();
}