global using System; global using System.Collections.Generic; global using System.Text; global using System.Text.Json; global using System.Threading; global using System.Threading.Tasks; global using DTLib.Demystifier; global using DTLib.Filesystem; global using DTLib.Logging; global using ParadoxSaveParser.Lib; global using Directory = DTLib.Filesystem.Directory; global using File = DTLib.Filesystem.File; global using Path = DTLib.Filesystem.Path; using System.Collections.Concurrent; using System.IO; using System.Text.Encodings.Web; using DTLib.Dtsod; using DTLib.Extensions; using DTLib.Web; using DTLib.Web.Routes; namespace ParadoxSaveParser.WebAPI; public partial class Program { private static readonly IOPath _configPath = "./config.dtsod"; private static Config _config = new(); private static readonly ILogger _loggerRoot = new CompositeLogger( new ConsoleLogger(), new FileLogger("logs", "ParadoxSaveParser.WebAPI")); private static readonly CancellationTokenSource _mainCancel = new(); private static ConcurrentDictionary _saveMetadataStorage = new(); private static JsonSerializerOptions _saveSerializerOptions = new() { WriteIndented = false, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, MaxDepth = 1024, }; static void TestSearchExpression(Stream saveStream, TestCase tc) { saveStream.Seek(0, SeekOrigin.Begin); var se = SearchExpression.Parse(tc.q); var parser = new SaveParserEU4(saveStream, se); var rootNode = parser.Parse(); string json = JsonSerializer.Serialize(rootNode, _saveSerializerOptions); string pdx = json.Substring(1, json.Length - 2) .Replace(",", " ").Replace("{", "{ ").Replace("}", " }") .Replace("\"", "").Replace("[", "").Replace("]", "").Replace(":", "="); if(pdx == tc.a) { Console.WriteLine($"[OK] q:'{tc.q}' a:'{tc.a}'"); } else { Console.WriteLine($"[Error] q:'{tc.q}' a:'{tc.a}' r:'{pdx}'"); } } record TestCase(string q, string a); public static void Main(string[] args) { using var saveStream = new MemoryStream( "EU4txt a={ b={ c=0 d=1 e=2 } f=3 }".ToBytes(), false); TestCase[] testCases = [ new("a", "a={ b={ c=0 d=1 e=2 } f=3 }"), new("a.*", "a={ b={ c=0 d=1 e=2 } f=3 }"), new("a.b", "a={ b={ c=0 d=1 e=2 } }"), new("a.[0].c", "a={ b={ c=0 } }"), new("a.[1]", "a={ f=3 }"), new("a.b.(c|d)", "a={ b={ c=0 d=1 } }"), new("a.(b.e|f)", "a={ b={ e=2 } f=3 }"), ]; foreach (var test in testCases) { TestSearchExpression(saveStream, test); } /* Console.InputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8; Console.CursorVisible = false; ContextLogger logger = new ContextLogger(nameof(Main), _loggerRoot); Console.CancelKeyPress += (_, e) => { e.Cancel = true; logger.LogInfo("Ctrl+C Pressed"); _mainCancel.Cancel(); }; try { // config if (!File.Exists(_configPath)) { logger.LogWarn("config file not found."); File.WriteAllText(_configPath, _config.ToString()); logger.LogWarn($"created default at {_configPath}."); } else _config = Config.FromDtsod(new DtsodV23(File.ReadAllText(_configPath))); PrepareLocalFiles(); // http server var router = new SimpleRouter(_loggerRoot); router.DefaultRoute = new ServeFilesRouteHandler("public"); router.MapRoute("/getSaveStatus", HttpMethod.GET, GetSaveStatusHandler); router.MapRoute("/uploadSave/eu4", HttpMethod.POST, UploadSaveHandler); router.MapRoute("/parseSave/eu4", HttpMethod.POST, ParseSaveEU4Handler); var app = new WebApp(_config.BaseUrl, _loggerRoot, router, _mainCancel.Token); app.Run().GetAwaiter().GetResult(); } catch (OperationCanceledException ex) { logger.LogWarn($"catched OperationCanceledException from {ex.Source}"); } catch (Exception ex) { logger.LogError(ex.ToStringDemystified()); } */ } public static void PrepareLocalFiles() { Directory.Create(PathHelper.DATA_DIR); Directory.Create(PathHelper.SAVES_DIR); foreach (var metaFilePath in System.IO.Directory.GetFiles( PathHelper.SAVES_DIR.Str, "*.meta.json", SearchOption.TopDirectoryOnly)) { using var metaFile = File.OpenRead(metaFilePath); var meta = JsonSerializer.Deserialize(metaFile) ?? throw new NullReferenceException(metaFilePath); if (meta.status != SaveFileProcessingStatus.Done) { _loggerRoot.LogWarn(nameof(PrepareLocalFiles), $"metadata file '{metaFilePath}' status has invalid status {meta.status}"); } if(!_saveMetadataStorage.TryAdd(meta.id, meta)) throw new Exception("Guid collision!"); } } }