using System.Text; using System.Text.RegularExpressions; using System.Globalization; using EDPlayerJournal.Entries; namespace EDPlayerJournal; public class JournalFile : IComparable { public string FullPath { get; set; } public int Part { get; set; } public DateTime? DateTime { get; set; } public DateTime? NormalisedDateTime { get; set; } public List entries = new List(); private static Regex fileregex = new Regex("Journal\\.(\\d+)\\.(\\d+)\\.log"); private static Regex update11regex = new Regex("Journal\\.([^\\.]+)\\.(\\d+).log"); private static string iso8601 = "yyyyMMddTHHmmss"; /// /// A public list of errors encountered while parsing the journal files /// public List Errors { get; private set; } = new List(); public static bool VerifyFile(string path) { string filename = Path.GetFileName(path); var matches = fileregex.Matches(filename); if (matches.Count != 0) { return true; } matches = update11regex.Matches(filename); if (matches.Count != 0) { return true; } return false; } private void SetFilename(string path) { string filename = Path.GetFileName(path); if (!File.Exists(path) || filename == null) { throw new JournalException(string.Format("Invalid journal file: {0}", filename)); } var matches = fileregex.Matches(filename); if (matches.Count == 0) { matches = update11regex.Matches(filename); if (matches.Count == 0) { throw new JournalException(string.Format("invalid file format: {0}", filename)); } } FullPath = path; var groups = matches[0].Groups; string part = groups[2].ToString(); string timestamp = groups[1].ToString(); if (!timestamp.Contains("T")) { /* pre update 11 file */ // The ISO is not quite correct on journal filenames, so build // a proper ISO 8601 date time stamp out of it. var date = new StringBuilder(); date.Append("20"); // Add the missing century in front. date.Append(timestamp); date.Insert(8, "T"); // Add the "T" separating date and time timestamp = date.ToString(); } else { /* post update 11 file, remove dashes */ timestamp = timestamp.Replace("-", ""); } this.DateTime = System.DateTime.ParseExact(timestamp, iso8601, CultureInfo.InvariantCulture); this.Part = int.Parse(part); this.NormalisedDateTime = System.DateTime.Parse(DateTime.Value.ToShortDateString()); } public int CompareTo(JournalFile? other) { if (other == null || DateTime == null || other.DateTime == null) { return 0; } var comp = System.DateTime.Compare(DateTime.Value, other.DateTime.Value); if (comp == 0) { /* If we have the exact same datetime we compare by parts. */ return Part - other.Part; } else { return comp; } } public JournalFile(string path) { FullPath = ""; SetFilename(path); } public IEnumerable Entries { get { if (entries == null || entries.Count == 0) { try { LoadEntries(); } catch (IOException) { } if (entries == null) { entries = new List(); } } return entries; } } public void LoadEntries() { List lines = new List(); /* This needs to be done this way, otherwise, if the game is still running the journal files cannot * be accessed. And it is very much convenient to generate logs while the game is still running. */ using (var fs = new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (var sr = new StreamReader(fs, Encoding.UTF8)) { string? line; while ((line = sr.ReadLine()) != null) { lines.Add(line); } } } entries.Clear(); Errors.Clear(); foreach (var line in lines) { // Skip empty lines if (line.Trim().Length == 0) { continue; } try { Entry? entry = Entry.Parse(line); if (entry != null) { entries.Add(entry); } } catch (Exception ex) { Errors.Add(ex); } } } }