using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Runtime.InteropServices; namespace EDPlayerJournal.Entries; public class InvalidJournalEntryException : Exception { public InvalidJournalEntryException() { } public InvalidJournalEntryException(string message) : base(message) { } } /// /// Base class for a single entry within the player journal. If no specific sub class is available /// this class gives basic information, such as EventType or date when it happened. It also allows /// base classes access to the underlying JSON object. Base classes should be named after the event /// type that they map + Entry. So "FSDJump" event is handled by FSDJumpEntry. /// public class Entry { private static readonly Dictionary classes = new Dictionary { { Events.Bounty, typeof(BountyEntry) }, { Events.Commander, typeof(CommanderEntry) }, { Events.CommitCrime, typeof(CommitCrimeEntry) }, { Events.Died, typeof(DiedEntry) }, { Events.Docked, typeof(DockedEntry) }, { Events.FactionKillBond, typeof(FactionKillBondEntry) }, { Events.FSDJump, typeof(FSDJumpEntry) }, { Events.HullDamage, typeof(HullDamageEntry) }, { Events.LoadGame, typeof(LoadGameEntry) }, { Events.Location, typeof(LocationEntry) }, { Events.MarketBuy, typeof(MarketBuyEntry) }, { Events.MarketSell, typeof(MarketSellEntry) }, { Events.MissionAbandoned, typeof(MissionAbandonedEntry) }, { Events.MissionAccepted, typeof(MissionAcceptedEntry) }, { Events.MissionCompleted, typeof(MissionCompletedEntry) }, { Events.MissionFailed, typeof(MissionFailedEntry) }, { Events.MissionRedirected, typeof(MissionRedirectedEntry) }, { Events.MultiSellExplorationData, typeof(MultiSellExplorationDataEntry) }, { Events.RedeemVoucher, typeof(RedeemVoucherEntry) }, { Events.SearchAndRescue, typeof(SearchAndRescueEntry) }, { Events.SellExplorationData, typeof(SellExplorationDataEntry) }, { Events.SellMicroResources, typeof(SellMicroResourcesEntry) }, { Events.SellOrganicData, typeof(SellOrganicDataEntry) }, { Events.ShieldState, typeof(ShieldStateEntry) }, { Events.ShipTargeted, typeof(ShipTargetedEntry) }, { Events.UnderAttack, typeof(UnderAttackEntry) }, }; private string? eventtype = null; private string? datetime = null; private DateTime timestamp; private string? jsonstr = null; protected JObject? json = null; public Entry() { } public static Entry? Parse(string journalline) { using (JsonReader reader = new JsonTextReader(new StringReader(journalline))) { reader.DateParseHandling = DateParseHandling.None; var json = JObject.Load(reader); if (json == null) { return null; } return Parse(json); } } public static Entry? Parse(JObject json) { string? event_name = json.Value("event"); if (event_name == null) { return null; } classes.TryGetValue(event_name, out Type? classhandler); if (classhandler == null) { // No specific handler available so use base class classhandler = typeof(Entry); } Entry? obj = (Entry?)Activator.CreateInstance(classhandler); if (obj == null) { return null; } obj.InternalInitialise(json); obj.Initialise(); return obj; } private void InternalInitialise(JObject jobject) { this.json = jobject; this.jsonstr = json.ToString(formatting: Formatting.None); this.eventtype = json.Value("event"); // Do not change this or JSON parser will conver the string to // datetime object and call .ToString() on it which changes the // date and time format this.datetime = json.GetValue("timestamp")?.ToString(); if (!string.IsNullOrEmpty(this.datetime)) { this.timestamp = DateTime.Parse(this.datetime); } } protected virtual void Initialise() { } public bool Is(string eventtype) { if (eventtype == null || this.eventtype == null) { return false; } return String.Equals(this.eventtype, eventtype, StringComparison.OrdinalIgnoreCase); } public string? Event { get { return eventtype; } } public DateTime Timestamp { get { return timestamp; } } public JObject JSON { get { return this.json ?? new JObject(); } } public override string ToString() { return jsonstr ?? ""; } }