/********* ** Public methods *********/ /// <summary>Parse SMAPI log text.</summary> /// <param name="logText">The SMAPI log text.</param> public ParsedLog Parse(string logText) { try { // skip if empty if (string.IsNullOrWhiteSpace(logText)) { return(new ParsedLog { IsValid = false, RawText = logText, Error = "The log is empty." }); } // init log ParsedLog log = new ParsedLog { IsValid = true, RawText = logText, Messages = this.CollapseRepeats(this.GetMessages(logText)).ToArray() }; // parse log messages LogModInfo smapiMod = new LogModInfo { Name = "SMAPI", Author = "Pathoschild", Description = "", Loaded = true }; LogModInfo gameMod = new LogModInfo { Name = "game", Author = "", Description = "", Loaded = true }; IDictionary <string, LogModInfo> mods = new Dictionary <string, LogModInfo>(); bool inModList = false; bool inContentPackList = false; bool inModUpdateList = false; foreach (LogMessage message in log.Messages) { // collect stats if (message.Level == LogLevel.Error) { switch (message.Mod) { case "SMAPI": smapiMod.Errors++; break; case "game": gameMod.Errors++; break; default: if (mods.ContainsKey(message.Mod)) { mods[message.Mod].Errors++; } break; } } // collect SMAPI metadata if (message.Mod == "SMAPI") { // update flags if (inModList && !this.ModListEntryPattern.IsMatch(message.Text)) { inModList = false; } if (inContentPackList && !this.ContentPackListEntryPattern.IsMatch(message.Text)) { inContentPackList = false; } if (inModUpdateList && !this.ModUpdateListEntryPattern.IsMatch(message.Text)) { inModUpdateList = false; } // mod list if (!inModList && message.Level == LogLevel.Info && this.ModListStartPattern.IsMatch(message.Text)) { inModList = true; message.IsStartOfSection = true; message.Section = LogSection.ModsList; } else if (inModList) { Match match = this.ModListEntryPattern.Match(message.Text); string name = match.Groups["name"].Value; string version = match.Groups["version"].Value; string author = match.Groups["author"].Value; string description = match.Groups["description"].Value; mods[name] = new LogModInfo { Name = name, Author = author, Version = version, Description = description, Loaded = true }; message.Section = LogSection.ModsList; } // content pack list else if (!inContentPackList && message.Level == LogLevel.Info && this.ContentPackListStartPattern.IsMatch(message.Text)) { inContentPackList = true; message.IsStartOfSection = true; message.Section = LogSection.ContentPackList; } else if (inContentPackList) { Match match = this.ContentPackListEntryPattern.Match(message.Text); string name = match.Groups["name"].Value; string version = match.Groups["version"].Value; string author = match.Groups["author"].Value; string description = match.Groups["description"].Value; string forMod = match.Groups["for"].Value; mods[name] = new LogModInfo { Name = name, Author = author, Version = version, Description = description, ContentPackFor = forMod, Loaded = true }; message.Section = LogSection.ContentPackList; } // mod update list else if (!inModUpdateList && message.Level == LogLevel.Alert && this.ModUpdateListStartPattern.IsMatch(message.Text)) { inModUpdateList = true; message.IsStartOfSection = true; message.Section = LogSection.ModUpdateList; } else if (inModUpdateList) { Match match = this.ModUpdateListEntryPattern.Match(message.Text); string name = match.Groups["name"].Value; string version = match.Groups["version"].Value; string link = match.Groups["link"].Value; if (mods.ContainsKey(name)) { mods[name].UpdateLink = link; mods[name].UpdateVersion = version; } else { mods[name] = new LogModInfo { Name = name, UpdateVersion = version, UpdateLink = link, Loaded = false }; } message.Section = LogSection.ModUpdateList; } else if (message.Level == LogLevel.Alert && this.SMAPIUpdatePattern.IsMatch(message.Text)) { Match match = this.SMAPIUpdatePattern.Match(message.Text); string version = match.Groups["version"].Value; string link = match.Groups["link"].Value; smapiMod.UpdateVersion = version; smapiMod.UpdateLink = link; } // platform info line else if (message.Level == LogLevel.Info && this.InfoLinePattern.IsMatch(message.Text)) { Match match = this.InfoLinePattern.Match(message.Text); log.ApiVersion = match.Groups["apiVersion"].Value; log.GameVersion = match.Groups["gameVersion"].Value; log.OperatingSystem = match.Groups["os"].Value; smapiMod.Version = log.ApiVersion; } // mod path line else if (message.Level == LogLevel.Debug && this.ModPathPattern.IsMatch(message.Text)) { Match match = this.ModPathPattern.Match(message.Text); log.ModPath = match.Groups["path"].Value; int lastDelimiterPos = log.ModPath.LastIndexOfAny(new char[] { '/', '\\' }); log.GamePath = lastDelimiterPos >= 0 ? log.ModPath.Substring(0, lastDelimiterPos) : log.ModPath; } // log UTC timestamp line else if (message.Level == LogLevel.Trace && this.LogStartedAtPattern.IsMatch(message.Text)) { Match match = this.LogStartedAtPattern.Match(message.Text); log.Timestamp = DateTime.Parse(match.Groups["timestamp"].Value + "Z"); } } } // finalize log gameMod.Version = log.GameVersion; log.Mods = new[] { gameMod, smapiMod }.Concat(mods.Values.OrderBy(p => p.Name)).ToArray(); return(log); } catch (LogParseException ex) { return(new ParsedLog { IsValid = false, Error = ex.Message, RawText = logText }); } catch (Exception ex) { return(new ParsedLog { IsValid = false, Error = $"Parsing the log file failed. Technical details:\n{ex}", RawText = logText }); } }
/********* ** Public methods *********/ /// <summary>Parse SMAPI log text.</summary> /// <param name="logText">The SMAPI log text.</param> public ParsedLog Parse(string logText) { try { // skip if empty if (string.IsNullOrWhiteSpace(logText)) { return(new ParsedLog { IsValid = false, RawText = logText, Error = "The log is empty." }); } // init log ParsedLog log = new ParsedLog { IsValid = true, RawText = logText, Messages = this.CollapseRepeats(this.GetMessages(logText)).ToArray() }; // parse log messages LogModInfo smapiMod = new LogModInfo { Name = "SMAPI", Author = "Pathoschild", Description = "" }; IDictionary <string, LogModInfo> mods = new Dictionary <string, LogModInfo>(); bool inModList = false; bool inContentPackList = false; foreach (LogMessage message in log.Messages) { // collect stats if (message.Level == LogLevel.Error) { if (message.Mod == "SMAPI") { smapiMod.Errors++; } else if (mods.ContainsKey(message.Mod)) { mods[message.Mod].Errors++; } } // collect SMAPI metadata if (message.Mod == "SMAPI") { // update flags if (inModList && !this.ModListEntryPattern.IsMatch(message.Text)) { inModList = false; } if (inContentPackList && !this.ContentPackListEntryPattern.IsMatch(message.Text)) { inContentPackList = false; } // mod list if (!inModList && message.Level == LogLevel.Info && this.ModListStartPattern.IsMatch(message.Text)) { inModList = true; } else if (inModList) { Match match = this.ModListEntryPattern.Match(message.Text); string name = match.Groups["name"].Value; string version = match.Groups["version"].Value; string author = match.Groups["author"].Value; string description = match.Groups["description"].Value; mods[name] = new LogModInfo { Name = name, Author = author, Version = version, Description = description }; } // content pack list else if (!inContentPackList && message.Level == LogLevel.Info && this.ContentPackListStartPattern.IsMatch(message.Text)) { inContentPackList = true; } else if (inContentPackList) { Match match = this.ContentPackListEntryPattern.Match(message.Text); string name = match.Groups["name"].Value; string version = match.Groups["version"].Value; string author = match.Groups["author"].Value; string description = match.Groups["description"].Value; string forMod = match.Groups["for"].Value; mods[name] = new LogModInfo { Name = name, Author = author, Version = version, Description = description, ContentPackFor = forMod }; } // platform info line else if (message.Level == LogLevel.Info && this.InfoLinePattern.IsMatch(message.Text)) { Match match = this.InfoLinePattern.Match(message.Text); log.ApiVersion = match.Groups["apiVersion"].Value; log.GameVersion = match.Groups["gameVersion"].Value; log.OperatingSystem = match.Groups["os"].Value; smapiMod.Version = log.ApiVersion; } // mod path line else if (message.Level == LogLevel.Debug && this.ModPathPattern.IsMatch(message.Text)) { Match match = this.ModPathPattern.Match(message.Text); log.ModPath = match.Groups["path"].Value; } // log UTC timestamp line else if (message.Level == LogLevel.Trace && this.LogStartedAtPattern.IsMatch(message.Text)) { Match match = this.LogStartedAtPattern.Match(message.Text); log.Timestamp = DateTime.Parse(match.Groups["timestamp"].Value + "Z"); } } } // finalise log log.Mods = new[] { smapiMod }.Concat(mods.Values.OrderBy(p => p.Name)).ToArray(); return(log); } catch (LogParseException ex) { return(new ParsedLog { IsValid = false, Error = ex.Message, RawText = logText }); } catch (Exception ex) { return(new ParsedLog { IsValid = false, Error = $"Parsing the log file failed. Technical details:\n{ex}", RawText = logText }); } }