using System; using System.Collections.Generic; using System.Globalization; using System.Text; namespace BrewMonster.Scripts.Chat.EmotionData { /// /// Parser token giống thứ tự đọc trong AUIManager.cpp (Emotions{N}.txt). /// Token order matches AUIManager.cpp when loading Emotions{N}.txt. /// public static class EmotionTxtParser { public const int MaxFrames = 20; // AUITEXTAREA_EMOTHION_MAXFRAME public readonly struct ParseResult { public readonly List Entries; public readonly string ErrorMessage; public bool Success => string.IsNullOrEmpty(ErrorMessage); public ParseResult(List entries, string error) { Entries = entries; ErrorMessage = error ?? ""; } } /// /// Đọc toàn bộ nội dung file txt; token tách bằng whitespace (giống GetNextToken cơ bản). /// Reads full txt; tokens split by whitespace (basic GetNextToken behavior). /// public static ParseResult Parse(string text) { if (string.IsNullOrWhiteSpace(text)) return new ParseResult(new List(), "Empty text."); var tokens = Tokenize(text); var entries = new List(); int t = 0; while (t < tokens.Count) { if (!TryParseInt(tokens[t], out int startPos)) return new ParseResult(null, $"Invalid nStartPos at token index {t}: '{tokens[t]}'"); t++; if (t >= tokens.Count) return new ParseResult(null, "Unexpected EOF after nStartPos."); if (!TryParseInt(tokens[t], out int numFramesRaw)) return new ParseResult(null, $"Invalid nNumFrames at token index {t}."); int numFrames = Math.Min(Math.Max(0, numFramesRaw), MaxFrames); t++; if (t >= tokens.Count) return new ParseResult(null, "Unexpected EOF after nNumFrames (missing hint)."); string hint = tokens[t]; t++; var frameTicks = new int[numFrames]; for (int i = 0; i < numFrames; i++) { if (t >= tokens.Count) return new ParseResult(null, $"Unexpected EOF: need {numFrames} frame tick values for entry starting at StartPos={startPos}."); if (!TryParseInt(tokens[t], out frameTicks[i])) return new ParseResult(null, $"Invalid nFrameTick[{i}] at token index {t}."); t++; } entries.Add(new EmotionEntryData { StartPos = startPos, NumFrames = numFrames, Hint = hint, FrameTicks = frameTicks }); } return new ParseResult(entries, null); } private static bool TryParseInt(string s, out int v) { return int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out v); } /// /// Tách token: khoảng trắng; hỗ trợ chuỗi trong dấu ngoặc kép cho hint nếu sau này cần. /// private static List Tokenize(string text) { var list = new List(); var sb = new StringBuilder(); bool inQuotes = false; for (int i = 0; i < text.Length; i++) { char c = text[i]; if (inQuotes) { if (c == '"') inQuotes = false; else sb.Append(c); continue; } if (c == '"') { if (sb.Length > 0) { list.Add(sb.ToString()); sb.Clear(); } inQuotes = true; continue; } if (char.IsWhiteSpace(c)) { if (sb.Length > 0) { list.Add(sb.ToString()); sb.Clear(); } continue; } sb.Append(c); } if (sb.Length > 0) list.Add(sb.ToString()); return list; } } }