using System.Globalization; using Godot; namespace Cthangover.Core.UI.Tool.SceneBuilder { /// /// Inverse of : parses raw .tscn property /// literals (as stored in the file, e.g. "0.5", "Vector2(160, 50)", /// "\"Go\"") into display values for inspector widgets. Returns false for /// anything it cannot represent — object references like ExtResource("..."), /// sub-resources — so callers fall back to live-node values instead of /// showing garbage. Pure string math, safe outside the engine runtime. /// public static class TscnLiteralReader { /// Parses a bare .tscn float literal; constructors and references return false. public static bool TryReadFloat(string raw, out double value) { value = 0d; if (raw == null || raw.Contains('(')) return false; return double.TryParse(raw.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value); } /// Parses a bare .tscn integer literal; floats and constructors return false. public static bool TryReadLong(string raw, out long value) { value = 0; if (raw == null || raw.Contains('.') || raw.Contains('(')) return false; return long.TryParse(raw.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out value); } /// Parses a .tscn boolean literal (true/false). public static bool TryReadBool(string raw, out bool value) { var t = raw?.Trim(); if (t == "true") { value = true; return true; } if (t == "false") { value = false; return true; } value = false; return false; } /// Reads a quoted .tscn string literal; null when the raw text is not a quoted string. public static string ReadString(string raw) { if (raw == null) return null; var t = raw.Trim(); if (t.Length < 2 || t[0] != '"' || t[t.Length - 1] != '"') return null; var body = t.Substring(1, t.Length - 2); return body .Replace("\\n", "\n") .Replace("\\r", "\r") .Replace("\\t", "\t") .Replace("\\\"", "\"") .Replace("\\\\", "\\"); } /// Parses a Vector2(x, y) constructor literal. public static bool TryReadVector2(string raw, out Vector2 value) { value = Vector2.Zero; if (!TryReadConstructor(raw, "Vector2", out var parts) || parts.Length != 2) return false; if (!double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var x) || !double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var y)) return false; value = new Vector2((float)x, (float)y); return true; } /// Parses a Color(r, g, b[, a]) constructor literal. public static bool TryReadColor(string raw, out Color value) { value = Colors.White; if (!TryReadConstructor(raw, "Color", out var parts) || parts.Length is not (3 or 4)) return false; if (!double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var r) || !double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var g) || !double.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var b)) return false; var a = 1d; if (parts.Length == 4 && !double.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out a)) return false; value = new Color((float)r, (float)g, (float)b, (float)a); return true; } private static bool TryReadConstructor(string raw, string typeName, out string[] args) { args = null; if (raw == null) return false; var t = raw.Trim(); var prefix = typeName + "("; if (!t.StartsWith(prefix, System.StringComparison.Ordinal) || !t.EndsWith(")", System.StringComparison.Ordinal)) return false; var body = t.Substring(prefix.Length, t.Length - prefix.Length - 1); args = body.Split(','); for (var i = 0; i < args.Length; i++) args[i] = args[i].Trim(); return true; } } }