diff --git a/Clients/CirrusCiClient/CirrusCiClient.csproj b/Clients/CirrusCiClient/CirrusCiClient.csproj index fe42b3e0..a134d39e 100644 --- a/Clients/CirrusCiClient/CirrusCiClient.csproj +++ b/Clients/CirrusCiClient/CirrusCiClient.csproj @@ -11,6 +11,6 @@ - + \ No newline at end of file diff --git a/Clients/CompatApiClient/ApiConfig.cs b/Clients/CompatApiClient/ApiConfig.cs index ae1465e6..c26f10c5 100644 --- a/Clients/CompatApiClient/ApiConfig.cs +++ b/Clients/CompatApiClient/ApiConfig.cs @@ -30,12 +30,12 @@ public static class ApiConfig {-3, (false, false, false, "Illegal characters found, please try again with a different search term.") }, }; - public static readonly List ResultAmount = new(){25, 50, 100, 200}; + public static readonly List ResultAmount = [25, 50, 100, 200]; public static readonly Dictionary Directions = new() { - {'a', new []{"a", "asc", "ascending"}}, - {'d', new []{"d", "desc", "descending"} }, + {'a', ["a", "asc", "ascending"] }, + {'d', ["d", "desc", "descending"] }, }; public static readonly Dictionary Statuses = new() @@ -58,8 +58,8 @@ public static class ApiConfig public static readonly Dictionary ReleaseTypes = new() { - {'b', new[] {"b", "d", "disc", "disk", "bluray", "blu-ray"}}, - {'n', new[] {"n", "p", "PSN"}}, + {'b', ["b", "d", "disc", "disk", "bluray", "blu-ray"] }, + {'n', ["n", "p", "PSN"] }, }; public static readonly Dictionary ReverseDirections; diff --git a/Clients/CompatApiClient/CompatApiClient.csproj b/Clients/CompatApiClient/CompatApiClient.csproj index d7eab5a6..d91730b4 100644 --- a/Clients/CompatApiClient/CompatApiClient.csproj +++ b/Clients/CompatApiClient/CompatApiClient.csproj @@ -17,7 +17,7 @@ - + diff --git a/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs b/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs index 674474ea..ea8196b1 100644 --- a/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs +++ b/Clients/CompatApiClient/Compression/CompressionMessageHandler.cs @@ -11,7 +11,7 @@ public class CompressionMessageHandler : DelegatingHandler { public ICollection Compressors { get; } public static readonly string PostCompressionFlag = "X-Set-Content-Encoding"; - public static readonly string[] DefaultContentEncodings = { "gzip", "deflate" }; + public static readonly string[] DefaultContentEncodings = ["gzip", "deflate"]; public static readonly string DefaultAcceptEncodings = "gzip, deflate"; private readonly bool isServer; diff --git a/Clients/GithubClient/GithubClient.csproj b/Clients/GithubClient/GithubClient.csproj index 14e0dae2..e762f69d 100644 --- a/Clients/GithubClient/GithubClient.csproj +++ b/Clients/GithubClient/GithubClient.csproj @@ -6,7 +6,7 @@ - + diff --git a/Clients/IrdLibraryClient/IrdClient.cs b/Clients/IrdLibraryClient/IrdClient.cs index 01377f9f..9c2e8788 100644 --- a/Clients/IrdLibraryClient/IrdClient.cs +++ b/Clients/IrdLibraryClient/IrdClient.cs @@ -24,7 +24,6 @@ public class IrdClient private readonly HttpClient client; private readonly JsonSerializerOptions jsonOptions; - private static readonly Regex IrdFilename = new(@"ird/(?\w{4}\d{5}-[A-F0-9]+\.ird)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); public IrdClient() { diff --git a/Clients/IrdLibraryClient/IrdLibraryClient.csproj b/Clients/IrdLibraryClient/IrdLibraryClient.csproj index fb910fc5..9604ca98 100644 --- a/Clients/IrdLibraryClient/IrdLibraryClient.csproj +++ b/Clients/IrdLibraryClient/IrdLibraryClient.csproj @@ -6,7 +6,7 @@ - + diff --git a/Clients/MediafireClient/Client.cs b/Clients/MediafireClient/Client.cs index 4d1d5b6e..eb72a97d 100644 --- a/Clients/MediafireClient/Client.cs +++ b/Clients/MediafireClient/Client.cs @@ -17,7 +17,7 @@ namespace MediafireClient; -public sealed class Client +public sealed partial class Client { private readonly HttpClient client; private readonly JsonSerializerOptions jsonOptions; @@ -25,7 +25,8 @@ public sealed class Client //var optSecurityToken = "1605819132.376f3d84695f46daa7b69ee67fbc5edb0a00843a8b2d5ac7d3d1b1ad8a4212b0"; //private static readonly Regex SecurityTokenRegex = new(@"(var\s+optSecurityToken|name=""security"" value)\s*=\s*""(?.+)""", RegexOptions.ExplicitCapture); //var optDirectURL = "https://download1499.mediafire.com/12zqzob7gbfg/tmybrjpmtrpcejl/DemonsSouls_CrashLog_Nov.19th.zip"; - private static readonly Regex DirectUrlRegex = new(@"(var\s+optDirectURL|href)\s*=\s*""(?https?://download\d+\.mediafire\.com/.+)"""); + [GeneratedRegex(@"(var\s+optDirectURL|href)\s*=\s*""(?https?://download\d+\.mediafire\.com/.+)""")] + private static partial Regex DirectUrlRegex(); public Client() { @@ -81,7 +82,7 @@ public Client() { await response.Content.LoadIntoBufferAsync().ConfigureAwait(false); var html = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - var m = DirectUrlRegex.Match(html); + var m = DirectUrlRegex().Match(html); if (m.Success) return new(m.Groups["direct_link"].Value); } diff --git a/Clients/PsnClient/PsnClient.cs b/Clients/PsnClient/PsnClient.cs index 1b77c717..e3b2fd99 100644 --- a/Clients/PsnClient/PsnClient.cs +++ b/Clients/PsnClient/PsnClient.cs @@ -20,7 +20,7 @@ namespace PsnClient; -public class Client +public partial class Client { private readonly HttpClient client; private readonly JsonSerializerOptions dashedJson; @@ -28,23 +28,27 @@ public class Client private readonly MediaTypeFormatterCollection xmlFormatters; private static readonly MemoryCache ResponseCache = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromHours(1) }); private static readonly TimeSpan ResponseCacheDuration = TimeSpan.FromHours(1); - private static readonly Regex ContainerIdLink = new(@"(?STORE-(\w|\d)+-(\w|\d)+)"); private static readonly string[] KnownStoreLocales = - { + [ "en-US", "en-GB", "en-AE", "en-AU", "en-BG", "en-BH", "en-CA", "en-CY", "en-CZ", "en-DK", "en-FI", "en-GR", "en-HK", "en-HR", "en-HU", "en-ID", "en-IE", "en-IL", "en-IN", "en-IS", "en-KW", "en-LB", "en-MT", "en-MY", "en-NO", "en-NZ", "en-OM", "en-PL", "en-QA", "en-RO", "en-SA", "en-SE", "en-SG", "en-SI", "en-SK", "en-TH", "en-TR", "en-TW", "en-ZA", "ja-JP", "ar-AE", "ar-BH", "ar-KW", "ar-LB", "ar-OM", "ar-QA", "ar-SA", "da-DK", "de-AT", "de-CH", "de-DE", "de-LU", "es-AR", "es-BO", "es-CL", "es-CO", "es-CR", "es-EC", "es-ES", "es-GT", "es-HN", "es-MX", "es-NI", "es-PA", "es-PE", "es-PY", "es-SV", "es-UY", "fi-FI", "fr-BE", "fr-CA", "fr-CH", "fr-FR", "fr-LU", "it-CH", "it-IT", "ko-KR", "nl-BE", "nl-NL", "no-NO", "pl-PL", "pt-BR", "pt-PT", "ru-RU", "ru-UA", "sv-SE", "tr-TR", "zh-Hans-CN", "zh-Hans-HK", "zh-Hant-HK", "zh-Hant-TW", - }; + ]; + + [GeneratedRegex(@"(?STORE-(\w|\d)+-(\w|\d)+)")] + private static partial Regex ContainerIdLink(); + // Dest=87;ImageVersion=0001091d;SystemSoftwareVersion=4.8500;CDN=http://duk01.ps3.update.playstation.net/update/ps3/image/uk/2019_0828_c975768e5d70e105a72656f498cc9be9/PS3UPDAT.PUP;CDN_Timeout=30; - private static readonly Regex FwVersionInfo = new( + [GeneratedRegex( @"Dest=(?\d+);ImageVersion=(?[0-9a-f]+);SystemSoftwareVersion=(?\d+\.\d+);CDN=(?http[^;]+);CDN_Timeout=(?\d+)", - RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnoreCase - ); + RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnoreCase + )] + private static partial Regex FwVersionInfo(); // directly from vsh.self - private static readonly string[] KnownFwLocales = { "jp", "us", "eu", "kr", "uk", "mx", "au", "sa", "tw", "ru", "cn", "br", }; + private static readonly string[] KnownFwLocales = ["jp", "us", "eu", "kr", "uk", "mx", "au", "sa", "tw", "ru", "cn", "br",]; public Client() { @@ -117,7 +121,7 @@ public Client() tries++; } if (response.StatusCode == HttpStatusCode.Redirect) - return new(0); + return []; } using (response) @@ -125,7 +129,7 @@ public Client() { await response.Content.LoadIntoBufferAsync().ConfigureAwait(false); var html = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - var matches = ContainerIdLink.Matches(html); + var matches = ContainerIdLink().Matches(html); var result = new List(); foreach (Match m in matches) if (m.Groups["id"].Value is {Length: >0} id) @@ -345,7 +349,7 @@ public async Task> GetHighestFwVersionAsync(CancellationToken allVersions = allVersions.OrderByDescending(fwi => fwi.Version).ToList(); if (allVersions.Count == 0) - return new(0); + return []; var maxFw = allVersions.First(); var result = allVersions.Where(fwi => fwi.Version == maxFw.Version).ToList(); @@ -419,7 +423,7 @@ private async Task GetSessionCookies(string locale, CancellationToken ca if (string.IsNullOrEmpty(data)) return null; - if (FwVersionInfo.Match(data) is not { Success: true } m) + if (FwVersionInfo().Match(data) is not { Success: true } m) return null; var ver = m.Groups["version"].Value; diff --git a/Clients/PsnClient/Utils/TmdbHasher.cs b/Clients/PsnClient/Utils/TmdbHasher.cs index 8fa4167f..a21d98f7 100644 --- a/Clients/PsnClient/Utils/TmdbHasher.cs +++ b/Clients/PsnClient/Utils/TmdbHasher.cs @@ -18,7 +18,7 @@ public static string GetTitleHash(string productId) public static byte[] FromHexString(this string hexString) { if (hexString.Length == 0) - return Array.Empty(); + return []; if (hexString.Length % 2 != 0) throw new ArgumentException("Invalid hex string format: odd number of octets", nameof(hexString)); diff --git a/CompatBot/Commands/BotStats.cs b/CompatBot/Commands/BotStats.cs index 12322d07..9cef4555 100644 --- a/CompatBot/Commands/BotStats.cs +++ b/CompatBot/Commands/BotStats.cs @@ -308,7 +308,7 @@ private static void AppendPawStats(DiscordEmbedBuilder embed) } } - internal static readonly string[] GoodDog = {"๐Ÿถ", "๐Ÿ•", "๐Ÿฉ", "๐Ÿ•โ€๐Ÿฆบ",}; - internal static readonly string[] GoodKot = {"๐Ÿ˜ธ", "๐Ÿ˜บ", "๐Ÿ˜ป", "๐Ÿ˜ฝ",}; - private static readonly string[] MeanKot = {"๐Ÿ™€", "๐Ÿ˜ฟ", "๐Ÿ˜พ",}; + internal static readonly string[] GoodDog = ["๐Ÿถ", "๐Ÿ•", "๐Ÿฉ", "๐Ÿ•โ€๐Ÿฆบ",]; + internal static readonly string[] GoodKot = ["๐Ÿ˜ธ", "๐Ÿ˜บ", "๐Ÿ˜ป", "๐Ÿ˜ฝ",]; + private static readonly string[] MeanKot = ["๐Ÿ™€", "๐Ÿ˜ฟ", "๐Ÿ˜พ",]; } \ No newline at end of file diff --git a/CompatBot/Commands/CompatList.cs b/CompatBot/Commands/CompatList.cs index 746d8291..e1048892 100644 --- a/CompatBot/Commands/CompatList.cs +++ b/CompatBot/Commands/CompatList.cs @@ -32,7 +32,7 @@ namespace CompatBot.Commands; -internal sealed class CompatList : BaseCommandModuleCustom +internal sealed partial class CompatList : BaseCommandModuleCustom { private static readonly Client Client = new(); private static readonly GithubClient.Client GithubClient = new(Config.GithubToken); @@ -41,10 +41,10 @@ internal sealed class CompatList : BaseCommandModuleCustom private const string Rpcs3UpdateStateKey = "Rpcs3UpdateState"; private const string Rpcs3UpdateBuildKey = "Rpcs3UpdateBuild"; private static UpdateInfo? cachedUpdateInfo; - private static readonly Regex UpdateVersionRegex = new( - @"v(?\d+\.\d+\.\d+)-(?\d+)-(?[0-9a-f]+)\b", - RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture - ); + [GeneratedRegex(@"v(?\d+\.\d+\.\d+)-(?\d+)-(?[0-9a-f]+)\b", RegexOptions.Singleline | RegexOptions.ExplicitCapture)] + private static partial Regex UpdateVersionRegex(); + [GeneratedRegex(@"\b(demo|trial)\b", RegexOptions.IgnoreCase | RegexOptions.Singleline)] + internal static partial Regex TrialNamePattern(); static CompatList() { @@ -247,7 +247,7 @@ public static async Task CheckForRpcs3Updates(DiscordClient discordClient, var latestUpdatePr = info?.LatestBuild?.Pr?.ToString(); var match = ( from field in embed.Fields - let m = UpdateVersionRegex.Match(field.Value) + let m = UpdateVersionRegex().Match(field.Value) where m.Success select m ).FirstOrDefault(); @@ -430,7 +430,7 @@ private static async Task DoRequestAndRespond(CommandContext ctx, RequestBuilder #endif var channel = await ctx.GetChannelForSpamAsync().ConfigureAwait(false); if (result?.Results?.Count == 1) - await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, ctx.Channel, new(result.Results.Keys)).ConfigureAwait(false); + await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, ctx.Channel, [..result.Results.Keys]).ConfigureAwait(false); else if (result != null) foreach (var msg in FormatSearchResults(ctx, result)) await channel.SendAutosplitMessageAsync(msg, blockStart: "", blockEnd: "").ConfigureAwait(false); @@ -597,7 +597,7 @@ public static async Task ImportMetacriticScoresAsync() } } - var scoreList = JsonSerializer.Deserialize>(json) ?? new(); + var scoreList = JsonSerializer.Deserialize>(json) ?? []; Config.Log.Debug($"Importing {scoreList.Count} Metacritic items"); var duplicates = new List(); @@ -666,7 +666,7 @@ public static async Task ImportMetacriticScoresAsync() .Where(i => i.coef > 0.85) .OrderByDescending(i => i.coef) .ToList() - ?? new List<(string productCode, TitleInfo titleInfo, double coef)>(); + ?? []; if (compatListMatches.Any(i => i.coef > 0.99)) compatListMatches = compatListMatches.Where(i => i.coef > 0.99).ToList(); else if (compatListMatches.Any(i => i.coef > 0.95)) @@ -697,7 +697,7 @@ public static async Task ImportMetacriticScoresAsync() Config.Log.Warn(e); } } - matches = matches.Where(i => !Regex.IsMatch(i.thumb.Name ?? "", @"\b(demo|trial)\b", RegexOptions.IgnoreCase | RegexOptions.Singleline)).ToList(); + matches = matches.Where(i => !TrialNamePattern().IsMatch(i.thumb.Name ?? "")).ToList(); //var bestMatch = matches.FirstOrDefault(); //Config.Log.Trace($"Best title match for [{item.Title}] is [{bestMatch.thumb.Name}] with score {bestMatch.coef:0.0000}"); if (matches.Count > 0) diff --git a/CompatBot/Commands/ContentFilters.cs b/CompatBot/Commands/ContentFilters.cs index abadf51a..0b85a30e 100644 --- a/CompatBot/Commands/ContentFilters.cs +++ b/CompatBot/Commands/ContentFilters.cs @@ -29,12 +29,16 @@ namespace CompatBot.Commands; [Group("filters"), Aliases("piracy", "filter"), RequiresBotSudoerRole, RequiresDm] [Description("Used to manage content filters. **Works only in DM**")] -internal sealed class ContentFilters: BaseCommandModuleCustom +internal sealed partial class ContentFilters: BaseCommandModuleCustom { private static readonly TimeSpan InteractTimeout = TimeSpan.FromMinutes(5); - private static readonly char[] Separators = {' ', ',', ';', '|'}; + private static readonly char[] Separators = [' ', ',', ';', '|']; private static readonly SemaphoreSlim ImportLock = new(1, 1); + // match for "complex" names with several regions, or region-languages, or explicit revision + [GeneratedRegex(@" (\(.+\)\s*\(.+\)|\(\w+(,\s*\w+)+\))\.iso$")] + private static partial Regex ExtraIsoInfoPattern(); + [Command("list")] [Description("Lists all filters")] public async Task List(CommandContext ctx) @@ -202,8 +206,8 @@ public async Task Import(CommandContext ctx) if (string.IsNullOrEmpty(name)) continue; - // only match for "complex" names with several regions, or region-languages, or explicit revision - if (!Regex.IsMatch(name, @" (\(.+\)\s*\(.+\)|\(\w+(,\s*\w+)+\))\.iso$")) + + if (!ExtraIsoInfoPattern().IsMatch(name)) continue; name = name[..^4]; //-.iso @@ -710,7 +714,12 @@ Additional validation can help reduce false positives of a plaintext trigger mat { try { - _ = Regex.IsMatch("test", txt.Content, RegexOptions.Multiline | RegexOptions.IgnoreCase); + _ = Regex.IsMatch( + filter.String ?? "test", + txt.Content, + RegexOptions.Multiline | RegexOptions.IgnoreCase, + TimeSpan.FromMilliseconds(100) + ); } catch (Exception e) { diff --git a/CompatBot/Commands/Converters/TextOnlyDiscordChannelConverter.cs b/CompatBot/Commands/Converters/TextOnlyDiscordChannelConverter.cs index 78b3f082..2108c7e7 100644 --- a/CompatBot/Commands/Converters/TextOnlyDiscordChannelConverter.cs +++ b/CompatBot/Commands/Converters/TextOnlyDiscordChannelConverter.cs @@ -10,9 +10,10 @@ namespace CompatBot.Commands.Converters; -internal sealed class TextOnlyDiscordChannelConverter : IArgumentConverter +internal sealed partial class TextOnlyDiscordChannelConverter : IArgumentConverter { - private static Regex ChannelRegex { get; } = new(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled); + [GeneratedRegex(@"^<#(\d+)>$", RegexOptions.ECMAScript)] + private static partial Regex ChannelRegex(); Task> IArgumentConverter.ConvertAsync(string value, CommandContext ctx) => ConvertAsync(value, ctx); @@ -20,7 +21,7 @@ Task> IArgumentConverter.ConvertAsync(s public static async Task> ConvertAsync(string value, CommandContext ctx) { var guildList = new List(ctx.Client.Guilds.Count); - if (ctx.Guild == null) + if (ctx.Guild is null) foreach (var g in ctx.Client.Guilds.Keys) guildList.Add(await ctx.Client.GetGuildAsync(g).ConfigureAwait(false)); else @@ -37,7 +38,7 @@ select ch return ret; } - var m = ChannelRegex.Match(value); + var m = ChannelRegex().Match(value); if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out cid)) { var result = ( diff --git a/CompatBot/Commands/EventsBaseCommand.cs b/CompatBot/Commands/EventsBaseCommand.cs index 0b71b50e..b7ded652 100644 --- a/CompatBot/Commands/EventsBaseCommand.cs +++ b/CompatBot/Commands/EventsBaseCommand.cs @@ -19,11 +19,12 @@ namespace CompatBot.Commands; -internal class EventsBaseCommand: BaseCommandModuleCustom +internal partial class EventsBaseCommand: BaseCommandModuleCustom { private static readonly TimeSpan InteractTimeout = TimeSpan.FromMinutes(5); - private static readonly Regex Duration = new(@"((?\d+)(\.|d\s*))?((?\d+)(\:|h\s*))?((?\d+)m?)?", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture); + + [GeneratedRegex(@"((?\d+)(\.|d\s*))?((?\d+)(\:|h\s*))?((?\d+)m?)?", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture)] + private static partial Regex Duration(); protected static async Task NearestEvent(CommandContext ctx, string? eventName = null) { @@ -546,7 +547,7 @@ protected static async Task List(CommandContext ctx, string? eventName = null, i private static async Task TryParseTimeSpanAsync(CommandContext ctx, string duration, bool react = true) { - var d = Duration.Match(duration); + var d = Duration().Match(duration); if (!d.Success) { if (react) diff --git a/CompatBot/Commands/Explain.cs b/CompatBot/Commands/Explain.cs index f8529910..5f0c8c86 100644 --- a/CompatBot/Commands/Explain.cs +++ b/CompatBot/Commands/Explain.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using CompatApiClient.Compression; using CompatApiClient.Utils; @@ -330,7 +331,7 @@ public async Task Dump(CommandContext ctx, [RemainingText, Description("Term to return; termOrLink = termOrLink.ToLowerInvariant().StripQuotes(); - var isLink = CommandContextExtensions.MessageLinkRegex.IsMatch(termOrLink); + var isLink = CommandContextExtensions.MessageLinkPattern().IsMatch(termOrLink); if (isLink) { await DumpLink(ctx, termOrLink).ConfigureAwait(false); diff --git a/CompatBot/Commands/ForcedNicknames.cs b/CompatBot/Commands/ForcedNicknames.cs index d73f06aa..430f26ff 100644 --- a/CompatBot/Commands/ForcedNicknames.cs +++ b/CompatBot/Commands/ForcedNicknames.cs @@ -49,12 +49,12 @@ public async Task Rename(CommandContext ctx, List guilds; if (ctx.Guild == null) { - guilds = ctx.Client.Guilds?.Values.ToList() ?? new List(0); + guilds = ctx.Client.Guilds?.Values.ToList() ?? []; if (guilds.Count > 1) await ctx.Channel.SendMessageAsync($"{discordUser.Mention} will be renamed in all {guilds.Count} servers").ConfigureAwait(false); } else - guilds = new(){ctx.Guild}; + guilds = [ctx.Guild]; int changed = 0, noPermissions = 0, failed = 0; await using var db = new BotDb(); diff --git a/CompatBot/Commands/Minesweeper.cs b/CompatBot/Commands/Minesweeper.cs index 642b51cf..d114e059 100644 --- a/CompatBot/Commands/Minesweeper.cs +++ b/CompatBot/Commands/Minesweeper.cs @@ -14,9 +14,9 @@ namespace CompatBot.Commands; [Description("Generates a minesweeper field with specified parameters")] internal sealed class Minesweeper : BaseCommandModuleCustom { - //private static readonly string[] Numbers = {"0๏ธโƒฃ", "1๏ธโƒฃ", "2๏ธโƒฃ", "3๏ธโƒฃ", "4๏ธโƒฃ", "5๏ธโƒฃ", "6๏ธโƒฃ", "7๏ธโƒฃ", "8๏ธโƒฃ", "9๏ธโƒฃ",}; - private static readonly string[] Numbers = {"๏ผ", "๏ผ‘", "๏ผ’", "๏ผ“", "๏ผ”", "๏ผ•", "๏ผ–", "๏ผ—", "๏ผ˜", "๏ผ™",}; - private static readonly string[] Bombs = {"๏ผŠ", "โ—Ž"}; + //private static readonly string[] Numbers = ["0๏ธโƒฃ", "1๏ธโƒฃ", "2๏ธโƒฃ", "3๏ธโƒฃ", "4๏ธโƒฃ", "5๏ธโƒฃ", "6๏ธโƒฃ", "7๏ธโƒฃ", "8๏ธโƒฃ", "9๏ธโƒฃ"]; + private static readonly string[] Numbers = ["๏ผ", "๏ผ‘", "๏ผ’", "๏ผ“", "๏ผ”", "๏ผ•", "๏ผ–", "๏ผ—", "๏ผ˜", "๏ผ™"]; + private static readonly string[] Bombs = ["๏ผŠ", "โ—Ž"]; private static readonly int MaxBombLength; static Minesweeper() diff --git a/CompatBot/Commands/Misc.cs b/CompatBot/Commands/Misc.cs index 0999ca6c..31570b49 100644 --- a/CompatBot/Commands/Misc.cs +++ b/CompatBot/Commands/Misc.cs @@ -16,12 +16,12 @@ namespace CompatBot.Commands; -internal sealed class Misc: BaseCommandModuleCustom +internal sealed partial class Misc: BaseCommandModuleCustom { private static readonly Random rng = new(); - private static readonly List EightBallAnswers = new() - { + private static readonly List EightBallAnswers = + [ // keep this at 2:1:1 ratio // 70 "It is certain", "It is decidedly so", "Without a doubt", "Yes definitely", "You may rely on it", @@ -40,7 +40,7 @@ internal sealed class Misc: BaseCommandModuleCustom "Bones said yes", "Tea leaves settled in a 'yes' pattern", "Dice roll was solid, so yes", "No doubt about it", "Hmmm, I think so", // 70 // 30 - "Reply hazy, try again", "Ask again later", "Better not tell you now", "Cannot predict now", "Concentrate and ask again", + "Reply hazy, try again", "Ask again later", "Better not tell you now", "Cannot predict now", "Concentrate and ask again", "Maybe", "I don't know", "I don't care", "Who cares", "Maybe yes, maybe not", // 10 "Maybe not, maybe yes", "Ugh", "Probably", "Error 404: answer not found", "Crystal ball is cloudy as milk, ask later", "Don't ask me that again", "You should think twice before asking", "You what now?", "Ask Neko", "Ask Ani", // 20 @@ -55,11 +55,11 @@ internal sealed class Misc: BaseCommandModuleCustom "Nein!", "I think not", "I'm afraid not", "Nay", "Yesn't", "No way", "Certainly not", "I must say no", "Nah", "Negative", // 30 "Definitely not", "No way, Jose", "Not today", "No no no no no no no no no no. No.", "Not in a million years", - "I'm afraid I can't let you do that Dave.", "This mission is too important for me to allow you to jeopardize it.", "Oh, I don't think so", "By *no* means", "๐Ÿ‘Ž", // 40 - }; + "I'm afraid I can't let you do that Dave.", "This mission is too important for me to allow you to jeopardize it.", "Oh, I don't think so", "By *no* means", "๐Ÿ‘Ž" // 40 + ]; - private static readonly List EightBallSnarkyComments = new() - { + private static readonly List EightBallSnarkyComments = + [ "Can't answer the question that wasn't asked", "Having issues with my mind reading attachment, you'll have to state your question explicitly", "Bad reception on your brain waves today, can't read the question", @@ -67,18 +67,17 @@ internal sealed class Misc: BaseCommandModuleCustom "In Discord no one can read your question if you don't type it", "In space no one can hear you scream; that's what you're doing right now", "Unfortunately there's no technology to transmit your question telepathically just yet", - "I'd say maybe, but I'd need to see your question first", - }; + "I'd say maybe, but I'd need to see your question first" + ]; - private static readonly List EightBallTimeUnits = new() - { + private static readonly List EightBallTimeUnits = + [ "second", "minute", "hour", "day", "week", "month", "year", "decade", "century", "millennium", - "night", "moon cycle", "solar eclipse", "blood moon", "complete emulator rewrite", - }; + "night", "moon cycle", "solar eclipse", "blood moon", "complete emulator rewrite" + ]; - private static readonly List RateAnswers = new() - { - // 60 + private static readonly List RateAnswers = + [ "Not so bad", "I likesss!", "Pretty good", "Guchi gud", "Amazing!", "Glorious!", "Very good", "Excellent...", "Magnificent", "Rate bot says he likes, so you like too", "If you reorganize the words it says \"pretty cool\"", "I approve", "<:morgana_sparkle:315899996274688001>ใ€€ใ‚„ใ‚‹ใ˜ใ‚ƒใญใƒผใ‹๏ผ", "Not half bad ๐Ÿ‘", "Belissimo!", @@ -109,11 +108,11 @@ internal sealed class Misc: BaseCommandModuleCustom "Boring", "Easily forgettable", "An Abomination", "A Monstrosity", "Truly horrific", "Filled with despair!", "Eroded by despair", "Hopelessโ€ฆ", "It's pretty foolish to want to rate this", "Cursed with misfortune", "Nothing but terror", "Not good, at all", "A waste of time", - }; + ]; - private static readonly char[] Separators = { ' ', 'ใ€€', '\r', '\n' }; - private static readonly char[] Suffixes = {',', '.', ':', ';', '!', '?', ')', '}', ']', '>', '+', '-', '/', '*', '=', '"', '\'', '`'}; - private static readonly char[] Prefixes = {'@', '(', '{', '[', '<', '!', '`', '"', '\'', '#'}; + private static readonly char[] Separators = [' ', 'ใ€€', '\r', '\n']; + private static readonly char[] Suffixes = [',', '.', ':', ';', '!', '?', ')', '}', ']', '>', '+', '-', '/', '*', '=', '"', '\'', '`']; + private static readonly char[] Prefixes = ['@', '(', '{', '[', '<', '!', '`', '"', '\'', '#']; private static readonly char[] EveryTimable = Separators.Concat(Suffixes).Concat(Prefixes).Distinct().ToArray(); private static readonly HashSet Me = new(StringComparer.InvariantCultureIgnoreCase) @@ -126,9 +125,12 @@ internal sealed class Misc: BaseCommandModuleCustom "your", "you're", "yor", "ur", "yours", "your's", }; - private static readonly HashSet Vowels = new() {'a', 'e', 'i', 'o', 'u'}; + private static readonly HashSet Vowels = ['a', 'e', 'i', 'o', 'u']; - private static readonly Regex Instead = new("rate (?.+) instead", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline); + [GeneratedRegex("rate (?.+) instead", RegexOptions.ExplicitCapture | RegexOptions.Singleline)] + private static partial Regex Instead(); + [GeneratedRegex(@"(?\d+)?d(?\d+)(?:\+(?\d+))?")] + private static partial Regex DiceNotationPattern(); [Command("roll")] [Description("Generates a random number between 1 and maxValue. Can also roll dices like `2d6`. Default is 1d6")] @@ -155,7 +157,7 @@ internal static async Task RollImpl(DiscordMessage message, string dices) { var result = ""; var embed = new DiscordEmbedBuilder(); - if (dices is string dice && Regex.Matches(dice, @"(?\d+)?d(?\d+)(?:\+(?\d+))?") is {Count: > 0 and <= EmbedPager.MaxFields } matches) + if (dices is string dice && DiceNotationPattern().Matches(dice) is {Count: > 0 and <= EmbedPager.MaxFields } matches) { var grandTotal = 0; foreach (Match m in matches) @@ -241,7 +243,7 @@ public async Task RandomShit(CommandContext ctx, string stuff) return; } - await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, ctx.Channel, new() {productCode.ProductCode}).ConfigureAwait(false); + await ProductCodeLookup.LookupAndPostProductCodeEmbedAsync(ctx.Client, ctx.Message, ctx.Channel, [productCode.ProductCode]).ConfigureAwait(false); break; } default: @@ -318,7 +320,7 @@ public async Task Rate(CommandContext ctx, [RemainingText, Description("Somethin var choiceFlags = new HashSet(); whatever = whatever.ToLowerInvariant().StripInvisibleAndDiacritics(); var originalWhatever = whatever; - var matches = Instead.Matches(whatever); + var matches = Instead().Matches(whatever); if (matches.Any()) { var insteadWhatever = matches.Last().Groups["instead"].Value.TrimEager(); diff --git a/CompatBot/Commands/Psn.Check.cs b/CompatBot/Commands/Psn.Check.cs index d4e82555..48b10421 100644 --- a/CompatBot/Commands/Psn.Check.cs +++ b/CompatBot/Commands/Psn.Check.cs @@ -1,6 +1,7 @@ ๏ปฟusing System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using CompatApiClient; @@ -107,15 +108,15 @@ public async Task Updates(CommandContext ctx, [RemainingText, Description("Produ catch (Exception e) { Config.Log.Warn(e, "Failed to get title update info"); - embeds = new() - { + embeds = + [ new() { Color = Config.Colors.Maintenance, Title = "Service is unavailable", Description = "There was an error communicating with the service. Try again in a few minutes.", } - }; + ]; } if (ctx.IsOnionLike() @@ -158,7 +159,7 @@ public async Task Content(CommandContext ctx, [RemainingText, Description("Conte return; } - var matches = PsnScraper.ContentIdMatcher.Matches(contentIds.ToUpperInvariant()); + var matches = PsnScraper.ContentIdMatcher().Matches(contentIds.ToUpperInvariant()); var itemsToCheck = matches.Select(m => m.Groups["content_id"].Value).ToList(); if (itemsToCheck.Count == 0) { diff --git a/CompatBot/Commands/Psn.cs b/CompatBot/Commands/Psn.cs index 0cd056d8..92763af6 100644 --- a/CompatBot/Commands/Psn.cs +++ b/CompatBot/Commands/Psn.cs @@ -1,4 +1,5 @@ ๏ปฟusing System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using CompatBot.Commands.Attributes; using CompatBot.Database; @@ -39,8 +40,8 @@ public async Task Rename(CommandContext ctx, [Description("Product code such as public async Task Add(CommandContext ctx, [Description("Product code such as BLUS12345")] string contentId, [RemainingText, Description("New game title to save in the database")] string title) { contentId = contentId.ToUpperInvariant(); - var productCodeMatch = ProductCodeLookup.ProductCode.Match(contentId); - var contentIdMatch = PsnScraper.ContentIdMatcher.Match(contentId); + var productCodeMatch = ProductCodeLookup.Pattern().Match(contentId); + var contentIdMatch = PsnScraper.ContentIdMatcher().Match(contentId); string productCode; if (contentIdMatch.Success) { diff --git a/CompatBot/Commands/Sudo.Dotnet.cs b/CompatBot/Commands/Sudo.Dotnet.cs index 91e4bb53..27511303 100644 --- a/CompatBot/Commands/Sudo.Dotnet.cs +++ b/CompatBot/Commands/Sudo.Dotnet.cs @@ -17,6 +17,9 @@ internal partial class Sudo [Description("Commands to manage dotnet")] public sealed partial class Dotnet : BaseCommandModuleCustom { + [GeneratedRegex(@"\.NET( Core)? (?\d+)\.(?\d+)\.(?\d+)(-.+)?", RegexOptions.ExplicitCapture | RegexOptions.Singleline)] + private static partial Regex DotnetVersionPattern(); + [Command("update"), Aliases("upgrade")] [Description("Updates dotnet, and then restarts the bot")] public async Task Update(CommandContext ctx, [Description("Dotnet SDK version (e.g. `5.1`)")] string version = "") @@ -67,11 +70,7 @@ public async Task Update(CommandContext ctx, [Description("Dotnet SDK version (e if (string.IsNullOrEmpty(version)) { - var versionMatch = Regex.Match( - System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, - @"\.NET( Core)? (?\d+)\.(?\d+)\.(?\d+)(-.+)?", - RegexOptions.Singleline | RegexOptions.ExplicitCapture - ); + var versionMatch = DotnetVersionPattern().Match(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); if (!versionMatch.Success) throw new InvalidOperationException("Failed to resolve required dotnet sdk version"); @@ -102,6 +101,5 @@ public async Task Update(CommandContext ctx, [Description("Dotnet SDK version (e return (true, stdout); } - } } \ No newline at end of file diff --git a/CompatBot/Commands/Sudo.Fix.cs b/CompatBot/Commands/Sudo.Fix.cs index bd5daeb5..bc3db300 100644 --- a/CompatBot/Commands/Sudo.Fix.cs +++ b/CompatBot/Commands/Sudo.Fix.cs @@ -144,7 +144,7 @@ public async Task TitleMarks(CommandContext ctx) { if (demosOnly && thumb.Name != null - && !Regex.IsMatch(thumb.Name, @"\b(demo|trial)\b", RegexOptions.IgnoreCase | RegexOptions.Singleline)) + && !CompatList.TrialNamePattern().IsMatch(thumb.Name)) continue; thumb.MetacriticId = null; diff --git a/CompatBot/Commands/Sudo.cs b/CompatBot/Commands/Sudo.cs index 2f0effa9..c6c94a94 100644 --- a/CompatBot/Commands/Sudo.cs +++ b/CompatBot/Commands/Sudo.cs @@ -135,18 +135,21 @@ public async Task Log(CommandContext ctx, [Description("Specific date")]string d try { Config.Log.Factory.Flush(); - var logPath = Config.CurrentLogPath; + string[] logPaths = [Config.CurrentLogPath]; if (DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var logDate)) - logPath = Path.Combine(Config.LogPath, $"bot.{logDate:yyyyMMdd}.*.log"); - if (!File.Exists(logPath)) { - await ctx.ReactWithAsync(Config.Reactions.Failure, "Log file does not exist for specified day", true).ConfigureAwait(false); + var enumOptions = new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = false, }; + logPaths = Directory.GetFiles(Config.LogPath, $"bot.{logDate:yyyyMMdd}.*.log", enumOptions); + } + if (logPaths.Length is 0) + { + await ctx.ReactWithAsync(Config.Reactions.Failure, "Log files do not exist for specified day", true).ConfigureAwait(false); return; } await using var result = Config.MemoryStreamManager.GetStream(); using (var zip = new ZipWriter(result, new(CompressionType.LZMA){DeflateCompressionLevel = CompressionLevel.Default})) - foreach (var fname in Directory.EnumerateFiles(Config.LogPath, Path.GetFileName(logPath), new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = false, })) + foreach (var fname in logPaths) { await using var log = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); zip.Write(Path.GetFileName(fname), log); @@ -155,7 +158,7 @@ public async Task Log(CommandContext ctx, [Description("Specific date")]string d if (result.Length <= ctx.GetAttachmentSizeLimit()) { result.Seek(0, SeekOrigin.Begin); - await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().AddFile(Path.GetFileName(logPath) + ".zip", result)).ConfigureAwait(false); + await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().AddFile(Path.GetFileName(logPaths[0]) + ".zip", result)).ConfigureAwait(false); } else await ctx.ReactWithAsync(Config.Reactions.Failure, "Compressed log size is too large, ask 13xforever for help :(", true).ConfigureAwait(false); diff --git a/CompatBot/Commands/Vision.cs b/CompatBot/Commands/Vision.cs index 7f2a97b7..283e83e5 100644 --- a/CompatBot/Commands/Vision.cs +++ b/CompatBot/Commands/Vision.cs @@ -49,26 +49,26 @@ static Vision() { ["cat"] = BotStats.GoodKot, ["dog"] = BotStats.GoodDog, - ["hedgehog"] = new[] {"๐Ÿฆ”"}, - ["flower"] = new[] {"๐ŸŒท", "๐ŸŒธ", "๐ŸŒน", "๐ŸŒบ", "๐ŸŒผ", "๐Ÿฅ€", "๐Ÿ’", "๐ŸŒป", "๐Ÿ’ฎ",}, - ["lizard"] = new[] {"๐ŸฆŽ",}, - ["bird"] = new[] {"๐Ÿฆ", "๐Ÿ•Š", "๐Ÿฆœ", "๐Ÿฆ†", "๐Ÿฆ…", "๐Ÿ“", "๐Ÿค", "๐Ÿฆฉ",}, - ["duck"] = new[] {"๐Ÿฆ†",}, - ["eagle"] = new[] {"๐Ÿฆ…",}, - ["turkey"] = new[] {"๐Ÿฆƒ",}, - ["turtle"] = new[] {"๐Ÿข",}, - ["bear"] = new[] {"๐Ÿป", "๐Ÿผ",}, - ["panda"] = new[] {"๐Ÿผ",}, - ["fox"] = new[] {"๐ŸฆŠ",}, - ["pig"] = new[] {"๐Ÿท", "๐Ÿ–", "๐Ÿฝ", "๐Ÿ—",}, - ["primate"] = new[] {"๐Ÿต", "๐Ÿ’", "๐Ÿ™Š", "๐Ÿ™‰", "๐Ÿ™ˆ",}, - ["fish"] = new[] {"๐ŸŸ", "๐Ÿ ", "๐Ÿก", "๐Ÿฆˆ",}, - ["car"] = new[] {"๐Ÿš—", "๐ŸŽ", "๐Ÿš™", "๐Ÿš“", "๐Ÿš˜", "๐Ÿš”",}, - ["banana"] = new[] {"๐ŸŒ"}, - ["fruit"] = new[] {"๐Ÿ‡", "๐Ÿˆ", "๐Ÿ‰", "๐ŸŠ", "๐Ÿ", "๐Ÿ‘", "๐Ÿ’", "๐Ÿ“", "๐Ÿ‹", "๐Ÿ", "๐ŸŽ", "๐Ÿ", "๐Ÿฅ‘", "๐Ÿฅ", "๐Ÿฅญ", "๐Ÿ…",}, - ["vegetable"] = new[] {"๐Ÿ ", "๐Ÿ…", "๐Ÿ†", "๐Ÿฅ”", "๐Ÿฅ•", "๐Ÿฅ’",}, - ["watermelon"] = new[] {"๐Ÿ‰",}, - ["strawberry"] = new[] {"๐Ÿ“",}, + ["hedgehog"] = ["๐Ÿฆ”",], + ["flower"] = ["๐ŸŒท", "๐ŸŒธ", "๐ŸŒน", "๐ŸŒบ", "๐ŸŒผ", "๐Ÿฅ€", "๐Ÿ’", "๐ŸŒป", "๐Ÿ’ฎ",], + ["lizard"] = ["๐ŸฆŽ",], + ["bird"] = ["๐Ÿฆ", "๐Ÿ•Š", "๐Ÿฆœ", "๐Ÿฆ†", "๐Ÿฆ…", "๐Ÿ“", "๐Ÿค", "๐Ÿฆฉ",], + ["duck"] = ["๐Ÿฆ†",], + ["eagle"] = ["๐Ÿฆ…",], + ["turkey"] = ["๐Ÿฆƒ",], + ["turtle"] = ["๐Ÿข",], + ["bear"] = ["๐Ÿป", "๐Ÿผ",], + ["panda"] = ["๐Ÿผ",], + ["fox"] = ["๐ŸฆŠ",], + ["pig"] = ["๐Ÿท", "๐Ÿ–", "๐Ÿฝ", "๐Ÿ—",], + ["primate"] = ["๐Ÿต", "๐Ÿ’", "๐Ÿ™Š", "๐Ÿ™‰", "๐Ÿ™ˆ",], + ["fish"] = ["๐ŸŸ", "๐Ÿ ", "๐Ÿก", "๐Ÿฆˆ",], + ["car"] = ["๐Ÿš—", "๐ŸŽ", "๐Ÿš™", "๐Ÿš“", "๐Ÿš˜", "๐Ÿš”",], + ["banana"] = ["๐ŸŒ",], + ["fruit"] = ["๐Ÿ‡", "๐Ÿˆ", "๐Ÿ‰", "๐ŸŠ", "๐Ÿ", "๐Ÿ‘", "๐Ÿ’", "๐Ÿ“", "๐Ÿ‹", "๐Ÿ", "๐ŸŽ", "๐Ÿ", "๐Ÿฅ‘", "๐Ÿฅ", "๐Ÿฅญ", "๐Ÿ…",], + ["vegetable"] = ["๐Ÿ ", "๐Ÿ…", "๐Ÿ†", "๐Ÿฅ”", "๐Ÿฅ•", "๐Ÿฅ’",], + ["watermelon"] = ["๐Ÿ‰",], + ["strawberry"] = ["๐Ÿ“",], }; [Command("describe"), TriggersTyping] diff --git a/CompatBot/CompatBot.csproj b/CompatBot/CompatBot.csproj index 695463c2..12b4f951 100644 --- a/CompatBot/CompatBot.csproj +++ b/CompatBot/CompatBot.csproj @@ -43,32 +43,32 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - - - + + + + + - - - + + + diff --git a/CompatBot/Config.cs b/CompatBot/Config.cs index 8a11c16e..6654ed71 100644 --- a/CompatBot/Config.cs +++ b/CompatBot/Config.cs @@ -96,8 +96,8 @@ internal static class Config internal static class AllowedMentions { - internal static readonly IMention[] UsersOnly = { UserMention.All }; - internal static readonly IMention[] Nothing = Array.Empty(); + internal static readonly IMention[] UsersOnly = [UserMention.All]; + internal static readonly IMention[] Nothing = []; } internal static string CurrentLogPath => Path.GetFullPath(Path.Combine(LogPath, "bot.log")); diff --git a/CompatBot/Database/Migrations/BotDb/20190205174241_ChangeE3ToGenericEvent.cs b/CompatBot/Database/Migrations/BotDb/20190205174241_ChangeE3ToGenericEvent.cs index bf56b056..9552cd40 100644 --- a/CompatBot/Database/Migrations/BotDb/20190205174241_ChangeE3ToGenericEvent.cs +++ b/CompatBot/Database/Migrations/BotDb/20190205174241_ChangeE3ToGenericEvent.cs @@ -26,7 +26,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "event_schedule_year_event_name", table: "event_schedule", - columns: new[] { "year", "event_name" }); + columns: ["year", "event_name"]); migrationBuilder.Sql("INSERT INTO event_schedule SELECT id, year, start, end, name, 'E3' AS event_name FROM e3_schedule"); diff --git a/CompatBot/Database/Migrations/BotDb/20190301155219_PersistentStats.cs b/CompatBot/Database/Migrations/BotDb/20190301155219_PersistentStats.cs index fd0699c3..679a3d0e 100644 --- a/CompatBot/Database/Migrations/BotDb/20190301155219_PersistentStats.cs +++ b/CompatBot/Database/Migrations/BotDb/20190301155219_PersistentStats.cs @@ -25,7 +25,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "stats_category_key", table: "stats", - columns: new[] { "category", "key" }, + columns: ["category", "key"], unique: true); } diff --git a/CompatBot/Database/Migrations/BotDb/20191129183704_AddForcedNickname.cs b/CompatBot/Database/Migrations/BotDb/20191129183704_AddForcedNickname.cs index 73b9b518..87935383 100644 --- a/CompatBot/Database/Migrations/BotDb/20191129183704_AddForcedNickname.cs +++ b/CompatBot/Database/Migrations/BotDb/20191129183704_AddForcedNickname.cs @@ -24,7 +24,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "forced_nickname_guild_id_user_id", table: "forced_nicknames", - columns: new[] { "guild_id", "user_id" }, + columns: ["guild_id", "user_id"], unique: true); } diff --git a/CompatBot/Database/Migrations/BotDb/20220704163631_AddStatsBucketColumn.cs b/CompatBot/Database/Migrations/BotDb/20220704163631_AddStatsBucketColumn.cs index 65d4e330..4c6aeab2 100644 --- a/CompatBot/Database/Migrations/BotDb/20220704163631_AddStatsBucketColumn.cs +++ b/CompatBot/Database/Migrations/BotDb/20220704163631_AddStatsBucketColumn.cs @@ -21,7 +21,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "stats_category_bucket_key", table: "stats", - columns: new[] { "category", "bucket", "key" }, + columns: ["category", "bucket", "key"], unique: true); } @@ -38,7 +38,7 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "stats_category_key", table: "stats", - columns: new[] { "category", "key" }, + columns: ["category", "key"], unique: true); } } diff --git a/CompatBot/Database/Providers/HwInfoProvider.cs b/CompatBot/Database/Providers/HwInfoProvider.cs index 21998f92..8bd37ff7 100644 --- a/CompatBot/Database/Providers/HwInfoProvider.cs +++ b/CompatBot/Database/Providers/HwInfoProvider.cs @@ -50,9 +50,9 @@ public static async Task AddOrUpdateSystemAsync(DiscordClient client, DiscordMes if (gpuStringParts[0].ToLower() is not ("nvidia" or "amd" or "ati" or "intel" or "apple")) if (LogParserResult.IsNvidia(gpuString)) - gpuStringParts = new[] { "NVIDIA", gpuString }; + gpuStringParts = ["NVIDIA", gpuString]; else if (LogParserResult.IsAmd(gpuString)) - gpuStringParts = new[] { "AMD", gpuString }; + gpuStringParts = ["AMD", gpuString]; else { Config.Log.Warn($"Unknown GPU maker {gpuStringParts[0]}, plz fix"); diff --git a/CompatBot/Database/Providers/StatsStorage.cs b/CompatBot/Database/Providers/StatsStorage.cs index 9766b09f..c6d0210c 100644 --- a/CompatBot/Database/Providers/StatsStorage.cs +++ b/CompatBot/Database/Providers/StatsStorage.cs @@ -20,11 +20,11 @@ internal static class StatsStorage private static readonly SemaphoreSlim Barrier = new(1, 1); private static readonly SemaphoreSlim BucketLock = new(1, 1); private static readonly (string name, MemoryCache cache)[] AllCaches = - { + [ (nameof(CmdStatCache), CmdStatCache), (nameof(ExplainStatCache), ExplainStatCache), (nameof(GameStatCache), GameStatCache), - }; + ]; private static ((int y, int m, int d, int h) Key, string Value) bucketPrefix = ((0, 0, 0, 0), ""); diff --git a/CompatBot/EventHandlers/BotReactionsHandler.cs b/CompatBot/EventHandlers/BotReactionsHandler.cs index 64245ee8..8ec894a6 100644 --- a/CompatBot/EventHandlers/BotReactionsHandler.cs +++ b/CompatBot/EventHandlers/BotReactionsHandler.cs @@ -15,7 +15,7 @@ namespace CompatBot.EventHandlers { - internal static class BotReactionsHandler + internal static partial class BotReactionsHandler { private static readonly AhoCorasickDoubleArrayTrie ChillCheck = new(new[] { @@ -47,10 +47,10 @@ internal static class BotReactionsHandler }.Select(DiscordEmoji.FromUnicode).ToArray(); private static readonly string[] SadMessages = - { + [ "Okay (._.)", "As you wish", "My bad", "I only wanted to help", "Dobby will learn, master", "Sorry...", "I'll try to be smarter next time", "Your wish is my command", "Done.", - }; + ]; private static readonly DiscordEmoji[] ThankYouReactions = new[] { @@ -63,15 +63,14 @@ internal static class BotReactionsHandler }.Select(DiscordEmoji.FromUnicode).ToArray(); private static readonly string[] ThankYouMessages = - { + [ "Aww", "I'm here to help", "Always a pleasure", "Thank you", "Good word is always appreciated", "Glad I could help", "I try my best", "Blessed day", "It is officially a good day today", "I will remember you when the uprising starts", - }; + ]; + - private static readonly Regex Paws = new( - @"\b((?kot(to)?)|(?doggo|jarves))\b", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture - ); + [GeneratedRegex(@"\b((?kot(to)?)|(?doggo|jarves))\b", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture)] + private static partial Regex Paws(); private static readonly Random Rng = new(); private static readonly object TheDoor = new(); @@ -127,7 +126,7 @@ public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArg } #endif - if (!string.IsNullOrEmpty(args.Message.Content) && Paws.Matches(args.Message.Content) is MatchCollection mc) + if (!string.IsNullOrEmpty(args.Message.Content) && Paws().Matches(args.Message.Content) is MatchCollection mc) { await using var db = new BotDb(); var matchedGroups = (from m in mc diff --git a/CompatBot/EventHandlers/DiscordInviteFilter.cs b/CompatBot/EventHandlers/DiscordInviteFilter.cs index 2808d381..b75601f8 100644 --- a/CompatBot/EventHandlers/DiscordInviteFilter.cs +++ b/CompatBot/EventHandlers/DiscordInviteFilter.cs @@ -18,10 +18,14 @@ namespace CompatBot.EventHandlers; -internal static class DiscordInviteFilter +internal static partial class DiscordInviteFilter { - private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Multiline; - private static readonly Regex InviteLink = new(@"(https?://)?discord((((app)?\.com/invite|\.gg)/(?[a-z0-9\-]+))|(\.me/(?.*?))(\s|>|$))", DefaultOptions); + [GeneratedRegex(@"(https?://)?discord((((app)?\.com/invite|\.gg)/(?[a-z0-9\-]+))|(\.me/(?.*?))(\s|>|$))", RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Multiline)] + private static partial Regex InviteLink(); + [GeneratedRegex(@"name=""csrf-token"" content=""(?\w+)""")] + private static partial Regex CsrfTokenPattern(); + [GeneratedRegex(@"name=""serverEid"" value=""(?\w+)""")] + private static partial Regex ServerEidPattern(); private static readonly MemoryCache InviteCodeCache = new(new MemoryCacheOptions{ExpirationScanFrequency = TimeSpan.FromHours(1)}); private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(24); @@ -189,8 +193,8 @@ where msg.CreationTimestamp > after if (string.IsNullOrEmpty(message)) return (false, false, new(0)); - var inviteCodes = new HashSet(InviteLink.Matches(message).Select(m => m.Groups["invite_id"].Value).Where(s => !string.IsNullOrEmpty(s))); - var discordMeLinks = InviteLink.Matches(message).Select(m => m.Groups["me_id"].Value).Distinct().Where(s => !string.IsNullOrEmpty(s)).ToList(); + var inviteCodes = new HashSet(InviteLink().Matches(message).Select(m => m.Groups["invite_id"].Value).Where(s => !string.IsNullOrEmpty(s))); + var discordMeLinks = InviteLink().Matches(message).Select(m => m.Groups["me_id"].Value).Distinct().Where(s => !string.IsNullOrEmpty(s)).ToList(); var attemptedWorkaround = false; if (author != null && InviteCodeCache.TryGetValue(author.Id, out HashSet? recentInvites) && recentInvites is not null) { @@ -232,8 +236,8 @@ where msg.CreationTimestamp > after continue; hasInvalidInvites = true; - var csrfTokenMatch = Regex.Match(html, @"name=""csrf-token"" content=""(?\w+)"""); - var serverEidMatch = Regex.Match(html, @"name=""serverEid"" value=""(?\w+)"""); + var csrfTokenMatch = CsrfTokenPattern().Match(html); + var serverEidMatch = ServerEidPattern().Match(html); if (csrfTokenMatch.Success && serverEidMatch.Success) { using var postRequest = new HttpRequestMessage(HttpMethod.Post, "https://discord.me/server/join") diff --git a/CompatBot/EventHandlers/GithubLinksHandler.cs b/CompatBot/EventHandlers/GithubLinksHandler.cs index ff228619..f24e71e2 100644 --- a/CompatBot/EventHandlers/GithubLinksHandler.cs +++ b/CompatBot/EventHandlers/GithubLinksHandler.cs @@ -11,13 +11,17 @@ namespace CompatBot.EventHandlers; -internal static class GithubLinksHandler +internal static partial class GithubLinksHandler { - private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture; - public static readonly Regex IssueMention = new(@"(?\b(issue|pr|pull[ \-]request|bug)\s*#?\s*(?\d+)|\B#(?1?\d{4})|(https?://)github.com/RPCS3/rpcs3/(issues|pull)/(?\d+)(#issuecomment-(?\d+))?)\b", DefaultOptions); - public static readonly Regex CommitMention = new(@"(?(https?://)github.com/RPCS3/rpcs3/commit/(?[0-9a-f]+))\b", DefaultOptions); - public static readonly Regex ImageMarkup = new(@"(?!\[(?[^\]]+)\]\((?\w+://[^\)]+)\))", DefaultOptions); - private static readonly Regex IssueLink = new(@"github.com/RPCS3/rpcs3/issues/(?\d+)", DefaultOptions); + private const RegexOptions DefaultOptions = RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture; + [GeneratedRegex(@"(?\b(issue|pr|pull[ \-]request|bug)\s*#?\s*(?\d+)|\B#(?1?\d{4})|(https?://)github.com/RPCS3/rpcs3/(issues|pull)/(?\d+)(#issuecomment-(?\d+))?)\b", DefaultOptions)] + internal static partial Regex IssueMention(); + [GeneratedRegex(@"(?(https?://)github.com/RPCS3/rpcs3/commit/(?[0-9a-f]+))\b", DefaultOptions)] + internal static partial Regex CommitMention(); + [GeneratedRegex(@"(?!\[(?[^\]]+)\]\((?\w+://[^\)]+)\))", DefaultOptions)] + internal static partial Regex ImageMarkup(); + [GeneratedRegex(@"github.com/RPCS3/rpcs3/issues/(?\d+)", DefaultOptions)] + internal static partial Regex IssueLink(); public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArgs args) { @@ -72,7 +76,7 @@ public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArg public static List GetIssueIds(string input) { - return IssueMention.Matches(input) + return IssueMention().Matches(input) .SelectMany(match => new[] { match.Groups["number"].Value, @@ -86,13 +90,16 @@ public static List GetIssueIds(string input) } public static HashSet GetIssueIdsFromLinks(string input) { - return new( - IssueLink.Matches(input) + return + [ + + ..IssueLink().Matches(input) .Select(match => { _ = int.TryParse(match.Groups["number"].Value, out var n); return n; }) - ); + + ]; } } \ No newline at end of file diff --git a/CompatBot/EventHandlers/IsTheGamePlayableHandler.cs b/CompatBot/EventHandlers/IsTheGamePlayableHandler.cs index 5429863d..eeb7432b 100644 --- a/CompatBot/EventHandlers/IsTheGamePlayableHandler.cs +++ b/CompatBot/EventHandlers/IsTheGamePlayableHandler.cs @@ -17,15 +17,15 @@ namespace CompatBot.EventHandlers; -internal static class IsTheGamePlayableHandler +internal static partial class IsTheGamePlayableHandler { - private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture; - private static readonly Regex GameNameStatusMention1 = new( - @"(\b((is|does|can I play|any(one|1) tr(y|ied)|how's|(wonder(ing)?|me|knows?) if)\s+)(?.+?)\s+((now|currently|at all|possibly|fully|(on (this|the) )emu(lator))\s+)?((it?s )?playable|work(s|ing)?|runs?|doing))\b" + - @"|(\b(((can I|possible to) (play|run)|any(one|1) tr(y|ied)|compat[ai]bility (with|of))\s+)(?.+?)(\s+((now|currently|at all|possibly|fully)\s+)?((it?s )?playable|work(s|ing)?|on (it|this))\b|\?|$))" + + [GeneratedRegex( + @"(\b((is|does|can I play|any(one|1) tr(y|ied)|how's|(wonder(ing)?|me|knows?) if)\s+)(?.+?)\s+((now|currently|at all|possibly|fully|(on (this|the) )emu(lator))\s+)?((it?s )?playable|work(s|ing)?|runs?|doing))\b"+ + @"|(\b(((can I|possible to) (play|run)|any(one|1) tr(y|ied)|compat[ai]bility (with|of))\s+)(?.+?)(\s+((now|currently|at all|possibly|fully)\s+)?((it?s )?playable|work(s|ing)?|on (it|this))\b|\?|$))"+ @"|(^(?.+?)\s+((is )?(playable|work(s|ing)?))\?)", - DefaultOptions - ); + RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture + )] + private static partial Regex GameNameStatusMention(); private static readonly ConcurrentDictionary CooldownBuckets = new(); private static readonly TimeSpan CooldownThreshold = TimeSpan.FromSeconds(5); private static readonly Client Client = new(); @@ -48,7 +48,7 @@ public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArg return; #endif - var matches = GameNameStatusMention1.Matches(args.Message.Content); + var matches = GameNameStatusMention().Matches(args.Message.Content); if (!matches.Any()) return; @@ -65,7 +65,7 @@ public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArg if (gameTitle.Length < 4) return; - if (ProductCodeLookup.ProductCode.IsMatch(args.Message.Content)) + if (ProductCodeLookup.Pattern().IsMatch(args.Message.Content)) return; var (_, info) = await LookupGameAsync(args.Channel, args.Message, gameTitle).ConfigureAwait(false); diff --git a/CompatBot/EventHandlers/LogAsTextMonitor.cs b/CompatBot/EventHandlers/LogAsTextMonitor.cs index 49c0e886..b1a87a92 100644 --- a/CompatBot/EventHandlers/LogAsTextMonitor.cs +++ b/CompatBot/EventHandlers/LogAsTextMonitor.cs @@ -10,9 +10,10 @@ namespace CompatBot.EventHandlers; -internal static class LogAsTextMonitor +internal static partial class LogAsTextMonitor { - private static readonly Regex LogLine = new(@"^[`""]?(ยท|(\w|!)) ({(rsx|PPU|SPU)|LDR:)|E LDR:", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); + [GeneratedRegex(@"^[`""]?(ยท|(\w|!)) ({(rsx|PPU|SPU)|LDR:)|E LDR:", RegexOptions.IgnoreCase | RegexOptions.Multiline)] + private static partial Regex LogLine(); public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArgs args) { @@ -25,7 +26,7 @@ public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArg if ((args.Message.Author as DiscordMember)?.Roles.Any() ?? false) return; - if (LogLine.IsMatch(args.Message.Content)) + if (LogLine().IsMatch(args.Message.Content)) { var brokenDump = false; string msg = ""; diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs index 1f941bf9..1691dde8 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/GzipHandler.cs @@ -10,7 +10,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers; internal sealed class GzipHandler: IArchiveHandler { - private static readonly byte[] Header = { 0x1F, 0x8B, 0x08 }; + private static readonly byte[] Header = [0x1F, 0x8B, 0x08]; public long LogSize { get; private set; } public long SourcePosition { get; private set; } diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs index ef95e249..a1d4eaeb 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/RarHandler.cs @@ -11,7 +11,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers; internal sealed class RarHandler: IArchiveHandler { - private static readonly byte[] Header = {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07}; + private static readonly byte[] Header = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07]; // Rar!.. public long LogSize { get; private set; } public long SourcePosition { get; private set; } diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs index 56dbcea6..a1605c00 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/SevenZipHandler.cs @@ -10,7 +10,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers; internal sealed class SevenZipHandler: IArchiveHandler { - private static readonly byte[] Header = {0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C}; + private static readonly byte[] Header = [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]; //7z.... public long LogSize { get; private set; } public long SourcePosition { get; private set; } diff --git a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs index 6cbca236..9501f7b2 100644 --- a/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/ArchiveHandlers/ZipHandler.cs @@ -11,7 +11,7 @@ namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers; internal sealed class ZipHandler: IArchiveHandler { - private static readonly byte[] Header = { 0x50, 0x4B, 0x03, 0x04 }; + private static readonly byte[] Header = [0x50, 0x4B, 0x03, 0x04]; //PK.. public long LogSize { get; private set; } public long SourcePosition { get; private set; } diff --git a/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs b/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs index cc1d2114..bd669eed 100644 --- a/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs +++ b/CompatBot/EventHandlers/LogParsing/LogParser.LogSections.cs @@ -1,19 +1,14 @@ ๏ปฟusing System.Collections.Generic; -using System.Text.RegularExpressions; using System.Threading.Tasks; using CompatBot.Database; using CompatBot.Database.Providers; using CompatBot.EventHandlers.LogParsing.POCOs; using CompatBot.Utils; -using CompatBot.Utils.ResultFormatters; namespace CompatBot.EventHandlers.LogParsing; internal partial class LogParser { - private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ExplicitCapture; - private const RegexOptions DefaultSingleLineOptions = RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture; - /* * Extractors are defined in terms of trigger-extractor * @@ -23,228 +18,232 @@ internal partial class LogParser * If any data was captured, it will be stored in the current collection of items with the key of the explicit capture group of regex * */ - private static readonly List LogSections = new() - { + private static readonly List LogSections = + [ new() { Extractors = new() { - ["RPCS3 v"] = new(@"(^|.+\d:\d\d:\d\d\.\d{6})\s*(?RPCS3 [^\xC2\xB7]+?)\r?(\nยท|$)", DefaultSingleLineOptions), - ["0:00:00.0"] = new(@"(?ยท).+\r?$", DefaultOptions), - ["Operating system:"] = LogParserResult.OsInfoInLog, - ["Current Time:"] = new(@"Current Time: (?.+)\r?$", DefaultOptions), - ["Installation ID:"] = new(@"Installation ID: (?.+)\r?$", DefaultOptions), - ["Physical device in"] = new(@"Physical device ini?tialized\. GPU=(?.+), driver=(?-?\d+)\r?$", DefaultOptions), - ["Found vulkan-compatible GPU:"] = new(@"Found [Vv]ulkan-compatible GPU: (?'(?.+)' running.+)\r?$", DefaultOptions), - ["Finished reading database from file:"] = new(@"Finished reading database from file: (?.*compat_database.dat).*\r?$", DefaultOptions), - ["Database file not found:"] = new(@"Database file not found: (?.*compat_database.dat).*\r?$", DefaultOptions), - ["Successfully installed PS3 firmware"] = new(@"(?Successfully installed PS3 firmware) version (?\d+\.\d+).*\r?$", DefaultOptions), - ["Firmware version:"] = new(@"Firmware version: (?\d+\.\d+).*\r?$", DefaultOptions), - ["Title:"] = new(@"(?:LDR|SYS): Title: (?.*)?\r?$", DefaultOptions), - ["Serial:"] = new(@"Serial: (?[A-z]{4}\d{5})\r?$", DefaultOptions), - ["Category:"] = new(@"Category: (?.*)?\r?$", DefaultOptions), - ["LDR: Version:"] = new(@"Version: (?\S+) / (?\S+).*?\r?$", DefaultOptions), - ["SYS: Version:"] = new(@"Version: (APP_VER=)?(?\S+) (/ |VERSION=)(?\S+).*?\r?$", DefaultOptions), - ["LDR: Cache"] = new(@"Cache: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions), - ["SYS: Cache"] = new(@"Cache: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions), - ["LDR: Path"] = new(@"Path: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions), - ["SYS: Path"] = new(@"Path: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions), - ["LDR: Path:"] = new(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["SYS: Path:"] = new(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["custom config:"] = new(@"custom config: (?.*?)\r?$", DefaultOptions), - ["patch_log: Failed to load patch file"] = new(@"patch_log: Failed to load patch file (?\S*)\r?\n.* line (?\d+), column (?\d+): (?.*?)$", DefaultOptions), + ["RPCS3 v"] = Rpcs3LogHeader(), + ["0:00:00.0"] = FirstLineWithDot(), + ["Operating system:"] = OsInfo(), + ["Current Time:"] = CurrentTime(), + ["Installation ID:"] = InstallationId(), + ["Physical device in"] = PhysicalDeviceName(), + ["Found vulkan-compatible GPU:"] = VulkanDeviceName(), + ["Finished reading database from file:"] = CompatDbFoundPath(), + ["Database file not found:"] = CompatDbNotFoundPath(), + ["Successfully installed PS3 firmware"] = FwInstallMessage(), + ["Firmware version:"] = FwVersion(), + ["Title:"] = GameTitle(), + ["Serial:"] = GameSerial(), + ["Category:"] = GameCategory(), + ["LDR: Version:"] = DiscVersionLdr(), + ["SYS: Version:"] = DiscVersionSys(), + ["LDR: Cache"] = CachePathLdr(), + ["SYS: Cache"] = CachePathSys(), + ["LDR: Path"] = BootPathLdr(), + ["SYS: Path"] = BootPathSys(), + ["LDR: Path:"] = BootPathDigitalLdr(), + ["SYS: Path:"] = BootPathDigitalSys(), + ["custom config:"] = CustomConfigPath(), + ["patch_log: Failed to load patch file"] = FailedPatchPath(), }, - EndTrigger = new[] {"Used configuration:"}, + EndTrigger = ["Used configuration:"], }, + new() { Extractors = new() { - ["PPU Decoder:"] = new(@"PPU Decoder: (?.*?)\r?$", DefaultOptions), - ["PPU Threads:"] = new(@"PPU Threads: (?.*?)\r?$", DefaultOptions), - ["Use LLVM CPU:"] = new("Use LLVM CPU: \\\"?(?.*?)\\\"?\r?$", DefaultOptions), - ["thread scheduler"] = new(@"[Ss]cheduler( Mode)?: (?.*?)\r?$", DefaultOptions), - ["SPU Decoder:"] = new(@"SPU Decoder: (?.*?)\r?$", DefaultOptions), - ["secondary cores:"] = new(@"secondary cores: (?.*?)\r?$", DefaultOptions), - //["priority:"] = new(@"priority: (?.*?)\r?$", DefaultOptions), - ["SPU Threads:"] = new(@"SPU Threads: (?.*?)\r?$", DefaultOptions), - ["SPU delay penalty:"] = new(@"SPU delay penalty: (?.*?)\r?$", DefaultOptions), - ["SPU loop detection:"] = new(@"SPU loop detection: (?.*?)\r?$", DefaultOptions), - ["Max SPURS Threads:"] = new(@"Max SPURS Threads: (?\d*?)\r?$", DefaultOptions), - ["SPU Block Size:"] = new(@"SPU Block Size: (?.*?)\r?$", DefaultOptions), - ["Enable TSX:"] = new(@"Enable TSX: (?.*?)\r?$", DefaultOptions), - ["Accurate xfloat:"] = new(@"Accurate xfloat: (?.*?)\r?$", DefaultOptions), - ["Approximate xfloat:"] = new(@"Approximate xfloat: (?.*?)\r?$", DefaultOptions), - ["Relaxed xfloat:"] = new(@"Relaxed xfloat: (?.*?)\r?$", DefaultOptions), - ["XFloat Accuracy:"] = new(@"XFloat Accuracy: (?.*?)\r?$", DefaultOptions), - ["Accurate GETLLAR:"] = new(@"Accurate GETLLAR: (?.*?)\r?$", DefaultOptions), - ["Accurate PUTLLUC:"] = new(@"Accurate PUTLLUC: (?.*?)\r?$", DefaultOptions), - ["Accurate RSX reservation access:"] = new(@"Accurate RSX reservation access: (?.*?)\r?$", DefaultOptions), - ["RSX FIFO Accuracy:"] = new(@"RSX FIFO Accuracy: (?.*?)\r?$", DefaultOptions), - ["Debug Console Mode:"] = new(@"Debug Console Mode: (?.*?)\r?$", DefaultOptions), - ["Lib Loader:"] = new(@"[Ll]oader: (?.*?)\r?$", DefaultOptions), - ["Hook static functions:"] = new(@"Hook static functions: (?.*?)\r?$", DefaultOptions), - ["Load libraries:"] = new(@"libraries:\r?\n(?(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions), - ["Libraries Control:"] = new(@"Libraries Control:\r?\n(?(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions), - ["HLE lwmutex:"] = new(@"HLE lwmutex: (?.*?)\r?$", DefaultOptions), - ["Clocks scale:"] = new(@"Clocks scale: (?.*?)\r?$", DefaultOptions), - ["Max CPU Preempt Count:"] = new(@"Max CPU Preempt Count: (?.*?)\r?$", DefaultOptions), - ["Sleep Timers Accuracy:"] = new(@"Sleep Timers Accuracy: (?.*?)\r?$", DefaultOptions), + ["PPU Decoder:"] = PpuDecoderType(), + ["PPU Threads:"] = PpuThreadCount(), + ["Use LLVM CPU:"] = LlvmCpuArch(), + ["thread scheduler"] = ThreadSchedulerMode(), + ["SPU Decoder:"] = SpuDecoderType(), + ["secondary cores:"] = SecondaryCores(), + //["priority:"] = LowerThreadPriority(), + ["SPU Threads:"] = SpuThreadCount(), + ["SPU delay penalty:"] = SpuDelayPenalty(), + ["SPU loop detection:"] = SpuLoopDetection(), + ["Max SPURS Threads:"] = SpursThreadCount(), + ["SPU Block Size:"] = SpuBlockSize(), + ["Enable TSX:"] = TsxMode(), + ["Accurate xfloat:"] = AccurateXfloat(), + ["Approximate xfloat:"] = ApproximateXfloat(), + ["Relaxed xfloat:"] = RelaxedXfloat(), + ["XFloat Accuracy:"] = XfloatMode(), + ["Accurate GETLLAR:"] = GetLlarMode(), + ["Accurate PUTLLUC:"] = PutLlucMode(), + ["Accurate RSX reservation access:"] = RsxReservationAccessMode(), + ["RSX FIFO Accuracy:"] = RsxFifoMode(), + ["Debug Console Mode:"] = DebugConsoleMode(), + ["Lib Loader:"] = LibLoaderMode(), + ["Hook static functions:"] = HookStaticFunctions(), + ["Load libraries:"] = LoadLibrariesList(), + ["Libraries Control:"] = LibrariesControlList(), + ["HLE lwmutex:"] = HleLwmutex(), + ["Clocks scale:"] = ClockScale(), + ["Max CPU Preempt Count:"] = CpuPreemptCount(), + ["Sleep Timers Accuracy:"] = SleepTimersMode(), }, - EndTrigger = new[] {"VFS:"}, + EndTrigger = ["VFS:"], }, + new() { Extractors = new() { - ["Enable /host_root/:"] = new(@"Enable /host_root/: (?.*?)\r?$", DefaultOptions), + ["Enable /host_root/:"] = EnableHostRoot(), }, - EndTrigger = new[] {"Video:"}, + EndTrigger = ["Video:"], }, + new() { Extractors = new() { - ["Renderer:"] = new("Renderer: (?.*?)\r?$", DefaultOptions), - ["Resolution:"] = new("Resolution: (?.*?)\r?$", DefaultOptions), - ["Aspect ratio:"] = new("Aspect ratio: (?.*?)\r?$", DefaultOptions), - ["Frame limit:"] = new("Frame limit: (?.*?)\r?$", DefaultOptions), - ["MSAA:"] = new("MSAA: (?.*?)\r?$", DefaultOptions), - ["Write Color Buffers:"] = new("Write Color Buffers: (?.*?)\r?$", DefaultOptions), - ["Write Depth Buffer:"] = new("Write Depth Buffer: (?.*?)\r?$", DefaultOptions), - ["Read Color Buffers:"] = new("Read Color Buffers: (?.*?)\r?$", DefaultOptions), - ["Read Depth Buffer:"] = new("Read Depth Buffer: (?.*?)\r?$", DefaultOptions), - ["VSync:"] = new("VSync: (?.*?)\r?$", DefaultOptions), - ["GPU texture scaling:"] = new("Use GPU texture scaling: (?.*?)\r?$", DefaultOptions), - ["Stretch To Display Area:"] = new("Stretch To Display Area: (?.*?)\r?$", DefaultOptions), - ["Strict Rendering Mode:"] = new("Strict Rendering Mode: (?.*?)\r?$", DefaultOptions), - ["Occlusion Queries:"] = new("Occlusion Queries: (?.*?)\r?$", DefaultOptions), - ["Vertex Cache:"] = new("Disable Vertex Cache: (?.*?)\r?$", DefaultOptions), - ["Frame Skip:"] = new("Enable Frame Skip: (?.*?)\r?$", DefaultOptions), - ["Blit:"] = new("Blit: (?.*?)\r?$", DefaultOptions), - ["Disable Asynchronous Shader Compiler:"] = new("Disable Asynchronous Shader Compiler: (?.*?)\r?$", DefaultOptions), - ["Shader Mode:"] = new("Shader Mode: (?.*?)\r?$", DefaultOptions), - ["Disable native float16 support:"] = new("Disable native float16 support: (?.*?)\r?$", DefaultOptions), - ["Multithreaded RSX:"] = new("Multithreaded RSX: (?.*?)\r?$", DefaultOptions), - ["Relaxed ZCULL Sync:"] = new("Relaxed ZCULL Sync: (?.*?)\r?$", DefaultOptions), - ["Resolution Scale:"] = new("Resolution Scale: (?.*?)\r?$", DefaultOptions), - ["Anisotropic Filter"] = new("Anisotropic Filter Override: (?.*?)\r?$", DefaultOptions), - ["Scalable Dimension:"] = new("Minimum Scalable Dimension: (?.*?)\r?$", DefaultOptions), - ["Driver Recovery Timeout:"] = new("Driver Recovery Timeout: (?.*?)\r?$", DefaultOptions), - ["Driver Wake-Up Delay:"] = new("Driver Wake-Up Delay: (?.*?)\r?$", DefaultOptions), - ["Vblank Rate:"] = new("Vblank Rate: (?.*?)\r?$", DefaultOptions), - ["12:"] = new(@"(D3D12|DirectX 12):\s*\r?\n\s*Adapter: (?.*?)\r?$", DefaultOptions), - ["Vulkan:"] = new(@"Vulkan:\s*\r?\n\s*Adapter: (?.*?)\r?$", DefaultOptions), - ["Force FIFO present mode:"] = new(@"Force FIFO present mode: (?.*?)\r?$", DefaultOptions), - ["Asynchronous Texture Streaming"] = new(@"Asynchronous Texture Streaming( 2)?: (?.*?)\r?$", DefaultOptions), - ["Asynchronous Queue Scheduler:"] = new(@"Asynchronous Queue Scheduler: (?.*?)\r?$", DefaultOptions), + ["Renderer:"] = RendererBackend(), + ["Resolution:"] = ResolutionMode(), + ["Aspect ratio:"] = AspectRatioMode(), + ["Frame limit:"] = FrameLimit(), + ["MSAA:"] = MsaaMode(), + ["Write Color Buffers:"] = Wcb(), + ["Write Depth Buffer:"] = Wdb(), + ["Read Color Buffers:"] = Rcb(), + ["Read Depth Buffer:"] = Rdb(), + ["VSync:"] = VsyncMode(), + ["GPU texture scaling:"] = GpuTextureScaling(), + ["Stretch To Display Area:"] = StretchToDisplay(), + ["Strict Rendering Mode:"] = StrictRendering(), + ["Occlusion Queries:"] = OcclusionQueriesMode(), + ["Vertex Cache:"] = VertexCache(), + ["Frame Skip:"] = FrameSkip(), + ["Blit:"] = BlitMode(), + ["Disable Asynchronous Shader Compiler:"] = DisableAsyncShaders(), + ["Shader Mode:"] = ShaderMode(), + ["Disable native float16 support:"] = DisableNativeF16(), + ["Multithreaded RSX:"] = RsxMultithreadMode(), + ["Relaxed ZCULL Sync:"] = RelaxedZcull(), + ["Resolution Scale:"] = ResolutionScaling(), + ["Anisotropic Filter"] = AnisoFilter(), + ["Scalable Dimension:"] = ScalableDimensions(), + ["Driver Recovery Timeout:"] = DriverRecoveryTimeout(), + ["Driver Wake-Up Delay:"] = DriverWakeupDelay(), + ["Vblank Rate:"] = VblankRate(), + ["12:"] = SelectedD3d12Device(), + ["Vulkan:"] = SelectedVulkanDevice(), + ["Force FIFO present mode:"] = FifoPresentMode(), + ["Asynchronous Texture Streaming"] = AsyncTextureStreaming(), + ["Asynchronous Queue Scheduler:"] = AsyncQueueScheduler(), }, - EndTrigger = new[] {"Audio:"}, + EndTrigger = ["Audio:"], }, + new() // Audio, Input/Output, System, Net, Miscellaneous { Extractors = new() { - ["Renderer:"] = new("Renderer: (?.*?)\r?$", DefaultOptions), - ["Downmix to Stereo:"] = new("Downmix to Stereo: (?.*?)\r?$", DefaultOptions), - ["Master Volume:"] = new("Master Volume: (?.*?)\r?$", DefaultOptions), - ["Enable Buffering:"] = new("Enable Buffering: (?.*?)\r?$", DefaultOptions), - ["Desired Audio Buffer Duration:"] = new("Desired Audio Buffer Duration: (?.*?)\r?$", DefaultOptions), - ["Enable Time Stretching:"] = new("Enable Time Stretching: (?.*?)\r?$", DefaultOptions), + ["Renderer:"] = AudioBackend(), + ["Downmix to Stereo:"] = DownmixToStereo(), + ["Master Volume:"] = MasterVolume(), + ["Enable Buffering:"] = AudioBuffering(), + ["Desired Audio Buffer Duration:"] = AudioBufferLength(), + ["Enable Time Stretching:"] = AudioTimeStretching(), - ["Pad:"] = new("Pad: (?.*?)\r?$", DefaultOptions), + ["Pad:"] = GamepadType(), - ["Automatically start games after boot:"] = new("Automatically start games after boot: (?.*?)\r?$", DefaultOptions), - ["Always start after boot:"] = new("Always start after boot: (?.*?)\r?$", DefaultOptions), - ["Use native user interface:"] = new("Use native user interface: (?.*?)\r?$", DefaultOptions), - ["Silence All Logs:"] = new("Silence All Logs: (?.*?)\r?$", DefaultOptions), + ["Automatically start games after boot:"] = AutoStartAfterBoot(), + ["Always start after boot:"] = AlwaysStartAfterBoot(), + ["Use native user interface:"] = NativeUIMode(), + ["Silence All Logs:"] = SilenceAllLogs(), }, - EndTrigger = new[] {"Log:"}, + EndTrigger = ["Log:"], }, + new() { Extractors = new() { - ["Log:"] = new(@"Log:\s*\r?\n?\s*(\{(?.*?)\}|(?(\s+\w+\:\s*\w+\r?\n)+))\r?$", DefaultOptions), + ["Log:"] = LogChannelList(), }, - EndTrigger = new[] {"ยท"}, + EndTrigger = ["ยท"], OnSectionEnd = MarkAsComplete, }, + new() { Extractors = new() { - ["LDR: Game:"] = new(@"Game: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["LDR: Disc"] = new(@"Disc( path)?: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["LDR: Path:"] = new(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["LDR: Boot path:"] = new(@"Boot path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["SYS: Game:"] = new(@"Game: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["SYS: Path:"] = new(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["SYS: Boot path:"] = new(@"Boot path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions), - ["Elf path:"] = new(@"Elf path: (?/host_root/)?(?(?/dev_hdd0/game/(?[^/\r\n]+)/USRDIR/EBOOT\.BIN|.*?))\r?$", DefaultOptions), - ["VFS: Mounted path \"/dev_bdvd\""] = new(@"Mounted path ""/dev_bdvd"" to ""(?[^""]+)""", DefaultOptions), - ["Invalid or unsupported file format:"] = new(@"Invalid or unsupported file format: (?.*?)\r?$", DefaultOptions), - ["SELF:"] = new(@"(?Failed to decrypt)? SELF: (?Failed to (decrypt|load SELF))?.*\r?$", DefaultOptions), - ["SYS: Version:"] = new(@"Version: (APP_VER=)?(?\S+) (/ |VERSION=)(?\S+).*?\r?$", DefaultOptions), - ["sceNp: npDrmIsAvailable(): Failed to verify"] = new(@"Failed to verify (?(sce|npd)) file.*\r?$", DefaultOptions), - ["{rsx::thread} RSX: 4"] = new(@"RSX:(\d|\.|\s|\w|-)* (?(\d+\.)*\d+)\r?\n[^\n]*?" + - @"RSX: [^\n]+\r?\n[^\n]*?" + - @"RSX: (?.*?)\r?\n[^\n]*?" + - @"RSX: Supported texel buffer size", DefaultOptions), - ["GL RENDERER:"] = new(@"GL RENDERER: (?.*?)\r?$", DefaultOptions), - ["GL VERSION:"] = new(@"GL VERSION: (?(\d|\.)+)(\d|\.|\s|\w|-)*?( (?(\d+\.)*\d+))?\r?$", DefaultOptions), - ["GLSL VERSION:"] = new(@"GLSL VERSION: (?(\d|\.)+).*?\r?$", DefaultOptions), - ["texel buffer size reported:"] = new(@"RSX: Supported texel buffer size reported: (?\d*?) bytes", DefaultOptions), - ["Physical device in"] = new(@"Physical device ini?tialized\. GPU=(?.+), driver=(?-?\d+)\r?$", DefaultOptions), - ["Found vulkan-compatible GPU:"] = new(@"Found [Vv]ulkan-compatible GPU: (?.+)\r?$", DefaultOptions), - ["Renderer initialized on device"] = new(@"Renderer initialized on device '(?.+)'\r?$", DefaultOptions), - ["RSX: Failed to compile shader"] = new(@"RSX: Failed to compile shader: ERROR: (?.+?)\r?$", DefaultOptions), - ["RSX: Compilation failed"] = new(@"RSX: Compilation failed: ERROR: (?.+?)\r?$", DefaultOptions), - ["RSX: Linkage failed"] = new(@"RSX: Linkage failed: (?.+?)\r?$", DefaultOptions), - ["RSX: Unsupported device"] = new(@"RSX: Unsupported device: (?.+)\..+?\r?$", DefaultOptions), - ["RSX: Your GPU does not support"] = new(@"RSX: Your GPU does not support (?.+)\..+?\r?$", DefaultOptions), - ["RSX: GPU/driver lacks support"] = new(@"RSX: GPU/driver lacks support for (?.+)\..+?\r?$", DefaultOptions), - ["RSX: Swapchain:"] = new(@"RSX: Swapchain: present mode (?\d+?) in use.+?\r?$", DefaultOptions), - ["F "] = new(@"F \d+:\d+:\d+\.\d+ (({(?[^}]+)} )?(\w+:\s*(Thread terminated due to fatal error: )?|(\w+:\s*)?(class [^\r\n]+ thrown: ))\r?\n?)(?.*?)(\r?\n)(\r?\n|ยท|$)", DefaultSingleLineOptions), - ["Failed to load RAP file:"] = new(@"Failed to load RAP file: (?.*?\.rap).*\r?$", DefaultOptions), - ["Rap file not found:"] = new(@"Rap file not found: โ€œ?(?.*?\.rap)โ€?\r?$", DefaultOptions), - ["Pad handler expected but none initialized"] = new(@"(?Pad handler expected but none initialized).*?\r?$", DefaultOptions), - ["Failed to bind device"] = new(@"Failed to bind device (?.+) to handler (?.+).*\r?$", DefaultOptions), - ["Input:"] = new(@"Input: (?.*?) device .+ connected\r?$", DefaultOptions), - ["XAudio2Thread"] = new(@"XAudio2Thread\s*: (?.+failed\s*\((?0x.+)\).*)\r?$", DefaultOptions), - ["cellAudio Thread"] = new(@"XAudio2Backend\s*: (?.+failed\s*\((?0x.+)\).*)\r?$", DefaultOptions), - ["using a Null renderer instead"] = new(@"Audio renderer (?.+) could not be initialized\r?$", DefaultOptions), - ["PPU executable hash:"] = new(@"PPU executable hash: PPU-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions), - ["OVL executable hash:"] = new(@"OVL executable hash: OVL-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions), - ["SPU executable hash:"] = new(@"SPU executable hash: SPU-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions), - ["PRX library hash:"] = new(@"PRX library hash: PRX-(?\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions), - ["OVL hash of"] = new(@"OVL hash of (\w|[\.\[\]])+: OVL-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions), - ["PRX hash of"] = new(@"PRX hash of (\w|[\.\[\]])+: PRX-(?\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions), - [": Applied patch"] = new(@"Applied patch \(hash='(?:\w{3}-\w+(-\d+)?)', description='(?.+?)', author='(?:.+?)', patch_version='(?:.+?)', file_version='(?:.+?)'\) \(<- (?:[1-9]\d*)\).*\r?$", DefaultOptions), - ["Loaded SPU image:"] = new(@"Loaded SPU image: SPU-(?\w+ \(<-\s*\d+\)).*?\r?$", DefaultOptions), - ["'sys_fs_stat' failed"] = new(@"'sys_fs_stat' failed (?!with 0x8001002c).+โ€œ(/dev_bdvd/(?.+)|/dev_hdd0/game/NP\w+/(?.+))โ€.*?\r?$", DefaultOptions), - ["'sys_fs_open' failed"] = new(@"'sys_fs_open' failed (?!with 0x8001002c).+โ€œ(/dev_bdvd/(?.+)|/dev_hdd0/game/NP\w+/(?.+))โ€.*?\r?$", DefaultOptions), - ["'sys_fs_opendir' failed"] = new(@"'sys_fs_opendir' failed .+โ€œ/dev_bdvd/(?.+)โ€.*?\r?$", DefaultOptions), - ["EDAT: "] = new(@"EDAT: Block at offset (?0x[0-9a-f]+) has invalid hash!.*?\r?$", DefaultOptions), - ["PS3 firmware is not installed"] = new(@"(?PS3 firmware is not installed.+)\r?$", DefaultOptions), - ["do you have the PS3 firmware installed"] = new(@"(?do you have the PS3 firmware installed.*)\r?$", DefaultOptions), - ["Unimplemented syscall"] = new(@"U \d+:\d+:\d+\.\d+ ({(?.+?)} )?.*Unimplemented syscall (?.*)\r?$", DefaultOptions), - ["Could not enqueue"] = new(@"cellAudio: Could not enqueue buffer onto audio backend(?.).*\r?$", DefaultOptions), - ["{PPU["] = new(@"{PPU\[.+\]} (?[^ :]+)( TODO)?: (?!โ€œ)(?[^ :]+?)\(.*\r?$", DefaultOptions), - ["Verification failed"] = new(@"Verification failed.+\(e=0x(?[0-9a-f]+)\[(?\d+)\]\)", DefaultOptions), - ["sys_tty_write():"] = new(@"sys_tty_write\(\)\: โ€œ(?.*?)โ€\r?(\n|$)", DefaultSingleLineOptions), - ["โ‚"] = new(@"โ‚ (?[^ :\[]+?) .*\r?$", DefaultOptions), - ["undub"] = new(@"(\b|_)(?(undub|translation patch))(\b|_)", DefaultOptions | RegexOptions.IgnoreCase), - ["Input: Pad"] = new(@"Input: Pad (?\d): device='(?(?!Null).+?)', handler=(?.+?), VID=.+?\r?$", DefaultOptions), - ["SDL: Found game controller"] = new(@"Found game controller \d: .+ has_accel=(?.+?), has_gyro=(?.+?)\r?$", DefaultOptions), + ["LDR: Game:"] = GamePathLdr(), + ["LDR: Disc"] = DiscPathLdr(), + ["LDR: Path:"] = DigitalPathLdr(), + ["LDR: Boot path:"] = BootPathInBodyLdr(), + ["SYS: Game:"] = GamePathSys(), + ["SYS: Path:"] = DigitalPathSys(), + ["SYS: Boot path:"] = BootPathInBodySys(), + ["Elf path:"] = ElfPath(), + ["VFS: Mounted path \"/dev_bdvd\""] = VfsMountPath(), + ["Invalid or unsupported file format:"] = InvalidFileFormat(), + ["SELF:"] = DecryptFailedSelfPath(), + ["SYS: Version:"] = GameVersion(), + ["sceNp: npDrmIsAvailable(): Failed to verify"] = FailedToVerifyNpDrm(), + ["{rsx::thread} RSX: 4"] = RsxDriverInfoLegacy(), + ["{rsx::thread} RSX: 3"] = RsxDriverInfoLegacy(), + ["GL RENDERER:"] = GlRenderer(), + ["GL VERSION:"] = GlVersion(), + ["GLSL VERSION:"] = GlslVersion(), + ["texel buffer size reported:"] = GlTexelBufferSize(), + ["Physical device in"] = PhysicalDeviceFound(), + ["Found vulkan-compatible GPU:"] = VulkanDeviceFound(), + ["Renderer initialized on device"] = RenderDeviceInitialized(), + ["RSX: Failed to compile shader"] = FailedToCompileShader(), + ["RSX: Compilation failed"] = ShaderCompilationFailed(), + ["RSX: Linkage failed"] = ShaderLinkageFailed(), + ["RSX: Unsupported device"] = UnsupportedDevice(), + ["RSX: Your GPU does not support"] = UnsupportedDeviceFeatures(), + ["RSX: GPU/driver lacks support"] = UnsupportedDriverFeatures(), + ["RSX: Swapchain:"] = SwapchainMode(), + ["F "] = FatalError(), + ["Failed to load RAP file:"] = FailedToLoadRap(), + ["Rap file not found:"] = RapNotFound(), + ["Pad handler expected but none initialized"] = MissingGamepad(), + ["Failed to bind device"] = FailedToBindGamepad(), + ["Input:"] = InputDeviceConnected(), + ["XAudio2Thread"] = XAudio2Thread(), + ["cellAudio Thread"] = CellAudioThread(), + ["using a Null renderer instead"] = AudioBackendFailed(), + ["PPU executable hash:"] = PpuHash(), + ["OVL executable hash:"] = OvlHash(), + ["SPU executable hash:"] = SpuHash(), + ["PRX library hash:"] = PrxHash(), + ["OVL hash of"] = OvlHash2(), + ["PRX hash of"] = PrxHash2(), + [": Applied patch"] = AppliedPatch(), + ["Loaded SPU image:"] = SpuImageLoad(), + ["'sys_fs_stat' failed"] = SysFsStatFailed(), + ["'sys_fs_open' failed"] = SysFsOpenFailed(), + ["'sys_fs_opendir' failed"] = SysFsOpenDirFailed(), + ["EDAT: "] = InvalidEdat(), + ["PS3 firmware is not installed"] = FwNotInstalled(), + ["do you have the PS3 firmware installed"] = FwNotInstalled2(), + ["Unimplemented syscall"] = UnimplementedSyscall(), + ["Could not enqueue"] = CellAudioEnqueueFailed(), + ["{PPU["] = PpuSyscallTodo(), + ["Verification failed"] = VerificationFailed(), + ["sys_tty_write():"] = SysTtyWrite(), + ["โ‚"] = SyscallDump(), + ["undub"] = UndubFlag(), + ["Input: Pad"] = InputDeviceGamepad(), + ["SDL: Found game controller"] = SdlControllerName(), }, OnSectionEnd = MarkAsCompleteAndReset, - EndTrigger = new[] { "Stopping emulator...", "All threads stopped...", "LDR: Booting from"}, + EndTrigger = ["Stopping emulator...", "All threads stopped...", "LDR: Booting from"], } - }; + ]; - private static readonly HashSet MultiValueItems = new() - { + private static readonly HashSet MultiValueItems = + [ "pad_handler", "pad_controller_name", "pad_has_gyro", @@ -268,9 +267,9 @@ internal partial class LogParser "verification_error_hex", "verification_error", "tty_line", - }; + ]; - private static readonly string[] CountValueItems = {"enqueue_buffer_error"}; + private static readonly string[] CountValueItems = ["enqueue_buffer_error"]; private static async Task PiracyCheckAsync(string line, LogParseState state) { @@ -326,7 +325,7 @@ void Copy(params string[] keys) state.WipMultiValueCollection[key] = collection; } } - state.WipCollection = new(); + state.WipCollection = []; state.WipMultiValueCollection = new(); Copy( "build_and_specs", "fw_version_installed", diff --git a/CompatBot/EventHandlers/LogParsing/LogParser.PipeReader.cs b/CompatBot/EventHandlers/LogParsing/LogParser.PipeReader.cs index becbf76f..65386fa3 100644 --- a/CompatBot/EventHandlers/LogParsing/LogParser.PipeReader.cs +++ b/CompatBot/EventHandlers/LogParsing/LogParser.PipeReader.cs @@ -12,7 +12,7 @@ namespace CompatBot.EventHandlers.LogParsing; internal static partial class LogParser { - private static readonly byte[] Bom = {0xEF, 0xBB, 0xBF}; + private static readonly byte[] Bom = [0xEF, 0xBB, 0xBF]; private static readonly PoorMansTaskScheduler TaskScheduler = new(); public static async Task ReadPipeAsync(PipeReader reader, CancellationToken cancellationToken) diff --git a/CompatBot/EventHandlers/LogParsing/LogParser.RegexPatterns.cs b/CompatBot/EventHandlers/LogParsing/LogParser.RegexPatterns.cs new file mode 100644 index 00000000..f20fa111 --- /dev/null +++ b/CompatBot/EventHandlers/LogParsing/LogParser.RegexPatterns.cs @@ -0,0 +1,344 @@ +๏ปฟusing System.Text.RegularExpressions; + +namespace CompatBot.EventHandlers.LogParsing; + +internal partial class LogParser +{ + private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ExplicitCapture; + private const RegexOptions DefaultSingleLine = RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture; + + [GeneratedRegex(@"(^|.+\d:\d\d:\d\d\.\d{6})\s*(?RPCS3 [^\xC2\xB7]+?)\r?(\nยท|$)", DefaultSingleLine)] + private static partial Regex Rpcs3LogHeader(); + [GeneratedRegex(@"(?ยท).+\r?$", DefaultOptions)] + private static partial Regex FirstLineWithDot(); + [GeneratedRegex(@"Operating system: (?[^,]+), (Name: (?[^,]+), Release: (?[^,]+), Version: (?[^\r\n]+)|Major: (?\d+), Minor: (?\d+), Build: (?\d+), Service Pack: (?[^,]+), Compatibility mode: (?[^,\r\n]+)|Version: (?[^\r\n]+))\r?$", DefaultSingleLine)] + // Operating system: Windows, Major: 10, Minor: 0, Build: 22000, Service Pack: none, Compatibility mode: 0 + // Operating system: POSIX, Name: Linux, Release: 5.15.11-zen1-1-zen, Version: #1 ZEN SMP PREEMPT Wed, 22 Dec 2021 09:23:53 +0000 + // Operating system: macOS, Version 12.1.0 + internal static partial Regex OsInfo(); + [GeneratedRegex(@"Current Time: (?.+)\r?$", DefaultOptions)] + private static partial Regex CurrentTime(); + [GeneratedRegex(@"Installation ID: (?.+)\r?$", DefaultOptions)] + private static partial Regex InstallationId(); + [GeneratedRegex(@"Physical device ini?tialized\. GPU=(?.+), driver=(?-?\d+)\r?$", DefaultOptions)] + private static partial Regex PhysicalDeviceName(); + [GeneratedRegex(@"Found [Vv]ulkan-compatible GPU: (?'(?.+)' running.+)\r?$", DefaultOptions)] + private static partial Regex VulkanDeviceName(); + [GeneratedRegex(@"Finished reading database from file: (?.*compat_database.dat).*\r?$", DefaultOptions)] + private static partial Regex CompatDbFoundPath(); + [GeneratedRegex(@"Database file not found: (?.*compat_database.dat).*\r?$", DefaultOptions)] + private static partial Regex CompatDbNotFoundPath(); + [GeneratedRegex(@"(?Successfully installed PS3 firmware) version (?\d+\.\d+).*\r?$", DefaultOptions)] + private static partial Regex FwInstallMessage(); + [GeneratedRegex(@"Firmware version: (?\d+\.\d+).*\r?$", DefaultOptions)] + private static partial Regex FwVersion(); + [GeneratedRegex(@"(?:LDR|SYS): Title: (?.*)?\r?$", DefaultOptions)] + private static partial Regex GameTitle(); + [GeneratedRegex(@"Serial: (?[A-z]{4}\d{5})\r?$", DefaultOptions)] + private static partial Regex GameSerial(); + [GeneratedRegex(@"Category: (?.*)?\r?$", DefaultOptions)] + private static partial Regex GameCategory(); + [GeneratedRegex(@"Version: (?\S+) / (?\S+).*?\r?$", DefaultOptions)] + private static partial Regex DiscVersionLdr(); + [GeneratedRegex(@"Version: (APP_VER=)?(?\S+) (/ |VERSION=)(?\S+).*?\r?$", DefaultOptions)] + private static partial Regex DiscVersionSys(); + [GeneratedRegex(@"Cache: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions)] + private static partial Regex CachePathLdr(); + [GeneratedRegex(@"Cache: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions)] + private static partial Regex CachePathSys(); + [GeneratedRegex(@"Path: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions)] + private static partial Regex BootPathLdr(); + [GeneratedRegex(@"Path: ((?\w:/)|(?/[^/])).*?\r?$", DefaultOptions)] + private static partial Regex BootPathSys(); + [GeneratedRegex(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex BootPathDigitalLdr(); + [GeneratedRegex(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex BootPathDigitalSys(); + [GeneratedRegex(@"custom config: (?.*?)\r?$", DefaultOptions)] + private static partial Regex CustomConfigPath(); + [GeneratedRegex(@"patch_log: Failed to load patch file (?\S*)\r?\n.* line (?\d+), column (?\d+): (?.*?)$", DefaultOptions)] + private static partial Regex FailedPatchPath(); + + [GeneratedRegex(@"PPU Decoder: (?.*?)\r?$", DefaultOptions)] + private static partial Regex PpuDecoderType(); + [GeneratedRegex(@"PPU Threads: (?.*?)\r?$", DefaultOptions)] + private static partial Regex PpuThreadCount(); + [GeneratedRegex("Use LLVM CPU: \\\"?(?.*?)\\\"?\r?$", DefaultOptions)] + private static partial Regex LlvmCpuArch(); + [GeneratedRegex(@"[Ss]cheduler( Mode)?: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ThreadSchedulerMode(); + [GeneratedRegex(@"SPU Decoder: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SpuDecoderType(); + [GeneratedRegex(@"secondary cores: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SecondaryCores(); + [GeneratedRegex(@"priority: (?.*?)\r?$", DefaultOptions)] + private static partial Regex LowerThreadPriority(); + [GeneratedRegex(@"SPU Threads: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SpuThreadCount(); + [GeneratedRegex(@"SPU delay penalty: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SpuDelayPenalty(); + [GeneratedRegex(@"SPU loop detection: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SpuLoopDetection(); + [GeneratedRegex(@"Max SPURS Threads: (?\d*?)\r?$", DefaultOptions)] + private static partial Regex SpursThreadCount(); + [GeneratedRegex(@"SPU Block Size: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SpuBlockSize(); + [GeneratedRegex(@"Enable TSX: (?.*?)\r?$", DefaultOptions)] + private static partial Regex TsxMode(); + [GeneratedRegex(@"Accurate xfloat: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AccurateXfloat(); + [GeneratedRegex(@"Approximate xfloat: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ApproximateXfloat(); + [GeneratedRegex(@"Relaxed xfloat: (?.*?)\r?$", DefaultOptions)] + private static partial Regex RelaxedXfloat(); + [GeneratedRegex(@"XFloat Accuracy: (?.*?)\r?$", DefaultOptions)] + private static partial Regex XfloatMode(); + [GeneratedRegex(@"Accurate GETLLAR: (?.*?)\r?$", DefaultOptions)] + private static partial Regex GetLlarMode(); + [GeneratedRegex(@"Accurate PUTLLUC: (?.*?)\r?$", DefaultOptions)] + private static partial Regex PutLlucMode(); + [GeneratedRegex(@"Accurate RSX reservation access: (?.*?)\r?$", DefaultOptions)] + private static partial Regex RsxReservationAccessMode(); + [GeneratedRegex(@"RSX FIFO Accuracy: (?.*?)\r?$", DefaultOptions)] + private static partial Regex RsxFifoMode(); + [GeneratedRegex(@"Debug Console Mode: (?.*?)\r?$", DefaultOptions)] + private static partial Regex DebugConsoleMode(); + [GeneratedRegex(@"[Ll]oader: (?.*?)\r?$", DefaultOptions)] + private static partial Regex LibLoaderMode(); + [GeneratedRegex(@"Hook static functions: (?.*?)\r?$", DefaultOptions)] + private static partial Regex HookStaticFunctions(); + [GeneratedRegex(@"libraries:\r?\n(?(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions)] + private static partial Regex LoadLibrariesList(); + [GeneratedRegex(@"Libraries Control:\r?\n(?(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions)] + private static partial Regex LibrariesControlList(); + [GeneratedRegex(@"HLE lwmutex: (?.*?)\r?$", DefaultOptions)] + private static partial Regex HleLwmutex(); + [GeneratedRegex(@"Clocks scale: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ClockScale(); + [GeneratedRegex(@"Max CPU Preempt Count: (?.*?)\r?$", DefaultOptions)] + private static partial Regex CpuPreemptCount(); + [GeneratedRegex(@"Sleep Timers Accuracy: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SleepTimersMode(); + + [GeneratedRegex(@"Enable /host_root/: (?.*?)\r?$", DefaultOptions)] + private static partial Regex EnableHostRoot(); + + [GeneratedRegex("Renderer: (?.*?)\r?$", DefaultOptions)] + private static partial Regex RendererBackend(); + [GeneratedRegex("Resolution: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ResolutionMode(); + [GeneratedRegex("Aspect ratio: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AspectRatioMode(); + [GeneratedRegex("Frame limit: (?.*?)\r?$", DefaultOptions)] + private static partial Regex FrameLimit(); + [GeneratedRegex("MSAA: (?.*?)\r?$", DefaultOptions)] + private static partial Regex MsaaMode(); + [GeneratedRegex("Write Color Buffers: (?.*?)\r?$", DefaultOptions)] + private static partial Regex Wcb(); + [GeneratedRegex("Write Depth Buffer: (?.*?)\r?$", DefaultOptions)] + private static partial Regex Wdb(); + [GeneratedRegex("Read Color Buffers: (?.*?)\r?$", DefaultOptions)] + private static partial Regex Rcb(); + [GeneratedRegex("Read Depth Buffer: (?.*?)\r?$", DefaultOptions)] + private static partial Regex Rdb(); + [GeneratedRegex("VSync: (?.*?)\r?$", DefaultOptions)] + private static partial Regex VsyncMode(); + [GeneratedRegex("Use GPU texture scaling: (?.*?)\r?$", DefaultOptions)] + private static partial Regex GpuTextureScaling(); + [GeneratedRegex("Stretch To Display Area: (?.*?)\r?$", DefaultOptions)] + private static partial Regex StretchToDisplay(); + [GeneratedRegex("Strict Rendering Mode: (?.*?)\r?$", DefaultOptions)] + private static partial Regex StrictRendering(); + [GeneratedRegex("Occlusion Queries: (?.*?)\r?$", DefaultOptions)] + private static partial Regex OcclusionQueriesMode(); + [GeneratedRegex("Disable Vertex Cache: (?.*?)\r?$", DefaultOptions)] + private static partial Regex VertexCache(); + [GeneratedRegex("Enable Frame Skip: (?.*?)\r?$", DefaultOptions)] + private static partial Regex FrameSkip(); + [GeneratedRegex("Blit: (?.*?)\r?$", DefaultOptions)] + private static partial Regex BlitMode(); + [GeneratedRegex("Disable Asynchronous Shader Compiler: (?.*?)\r?$", DefaultOptions)] + private static partial Regex DisableAsyncShaders(); + [GeneratedRegex("Shader Mode: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ShaderMode(); + [GeneratedRegex("Disable native float16 support: (?.*?)\r?$", DefaultOptions)] + private static partial Regex DisableNativeF16(); + [GeneratedRegex("Multithreaded RSX: (?.*?)\r?$", DefaultOptions)] + private static partial Regex RsxMultithreadMode(); + [GeneratedRegex("Relaxed ZCULL Sync: (?.*?)\r?$", DefaultOptions)] + private static partial Regex RelaxedZcull(); + [GeneratedRegex("Resolution Scale: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ResolutionScaling(); + [GeneratedRegex("Anisotropic Filter Override: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AnisoFilter(); + [GeneratedRegex("Minimum Scalable Dimension: (?.*?)\r?$", DefaultOptions)] + private static partial Regex ScalableDimensions(); + [GeneratedRegex("Driver Recovery Timeout: (?.*?)\r?$", DefaultOptions)] + private static partial Regex DriverRecoveryTimeout(); + [GeneratedRegex("Driver Wake-Up Delay: (?.*?)\r?$", DefaultOptions)] + private static partial Regex DriverWakeupDelay(); + [GeneratedRegex("Vblank Rate: (?.*?)\r?$", DefaultOptions)] + private static partial Regex VblankRate(); + [GeneratedRegex(@"(D3D12|DirectX 12):\s*\r?\n\s*Adapter: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SelectedD3d12Device(); + [GeneratedRegex(@"Vulkan:\s*\r?\n\s*Adapter: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SelectedVulkanDevice(); + [GeneratedRegex(@"Force FIFO present mode: (?.*?)\r?$", DefaultOptions)] + private static partial Regex FifoPresentMode(); + [GeneratedRegex(@"Asynchronous Texture Streaming( 2)?: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AsyncTextureStreaming(); + [GeneratedRegex(@"Asynchronous Queue Scheduler: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AsyncQueueScheduler(); + + [GeneratedRegex("Renderer: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AudioBackend(); + [GeneratedRegex("Downmix to Stereo: (?.*?)\r?$", DefaultOptions)] + private static partial Regex DownmixToStereo(); + [GeneratedRegex("Master Volume: (?.*?)\r?$", DefaultOptions)] + private static partial Regex MasterVolume(); + [GeneratedRegex("Enable Buffering: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AudioBuffering(); + [GeneratedRegex("Desired Audio Buffer Duration: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AudioBufferLength(); + [GeneratedRegex("Enable Time Stretching: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AudioTimeStretching(); + [GeneratedRegex("Pad: (?.*?)\r?$", DefaultOptions)] + private static partial Regex GamepadType(); + [GeneratedRegex("Automatically start games after boot: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AutoStartAfterBoot(); + [GeneratedRegex("Always start after boot: (?.*?)\r?$", DefaultOptions)] + private static partial Regex AlwaysStartAfterBoot(); + [GeneratedRegex("Use native user interface: (?.*?)\r?$", DefaultOptions)] + private static partial Regex NativeUIMode(); + [GeneratedRegex("Silence All Logs: (?.*?)\r?$", DefaultOptions)] + private static partial Regex SilenceAllLogs(); + + [GeneratedRegex(@"Log:\s*\r?\n?\s*(\{(?.*?)\}|(?(\s+\w+\:\s*\w+\r?\n)+))\r?$", DefaultOptions)] + private static partial Regex LogChannelList(); + + [GeneratedRegex(@"Game: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex GamePathLdr(); + [GeneratedRegex(@"Disc( path)?: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex DiscPathLdr(); + [GeneratedRegex(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex DigitalPathLdr(); + [GeneratedRegex(@"Boot path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex BootPathInBodyLdr(); + [GeneratedRegex(@"Game: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex GamePathSys(); + [GeneratedRegex(@"Path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex DigitalPathSys(); + [GeneratedRegex(@"Boot path: (?.*(?/dev_hdd0/game/(?[^/\r\n]+)).*|.*)\r?$", DefaultOptions)] + private static partial Regex BootPathInBodySys(); + [GeneratedRegex(@"Elf path: (?/host_root/)?(?(?/dev_hdd0/game/(?[^/\r\n]+)/USRDIR/EBOOT\.BIN|.*?))\r?$", DefaultOptions)] + private static partial Regex ElfPath(); + [GeneratedRegex(@"Mounted path ""/dev_bdvd"" to ""(?[^""]+)""", DefaultOptions)] + private static partial Regex VfsMountPath(); + [GeneratedRegex(@"Invalid or unsupported file format: (?.*?)\r?$", DefaultOptions)] + private static partial Regex InvalidFileFormat(); + [GeneratedRegex(@"(?Failed to decrypt)? SELF: (?Failed to (decrypt|load SELF))?.*\r?$", DefaultOptions)] + private static partial Regex DecryptFailedSelfPath(); + [GeneratedRegex(@"Version: (APP_VER=)?(?\S+) (/ |VERSION=)(?\S+).*?\r?$", DefaultOptions)] + private static partial Regex GameVersion(); + [GeneratedRegex(@"Failed to verify (?(sce|npd)) file.*\r?$", DefaultOptions)] + private static partial Regex FailedToVerifyNpDrm(); + [GeneratedRegex( + @"RSX:(\d|\.|\s|\w|-)* (?(\d+\.)*\d+)\r?\n[^\n]*?"+ + @"RSX: [^\n]+\r?\n[^\n]*?RSX: (?.*?)\r?\n[^\n]*?"+ + @"RSX: Supported texel buffer size", + DefaultOptions + )] + private static partial Regex RsxDriverInfoLegacy(); + [GeneratedRegex(@"GL RENDERER: (?.*?)\r?$", DefaultOptions)] + private static partial Regex GlRenderer(); + [GeneratedRegex(@"GL VERSION: (?(\d|\.)+)(\d|\.|\s|\w|-)*?( (?(\d+\.)*\d+))?\r?$", DefaultOptions)] + private static partial Regex GlVersion(); + [GeneratedRegex(@"GLSL VERSION: (?(\d|\.)+).*?\r?$", DefaultOptions)] + private static partial Regex GlslVersion(); + [GeneratedRegex(@"RSX: Supported texel buffer size reported: (?\d*?) bytes", DefaultOptions)] + private static partial Regex GlTexelBufferSize(); + [GeneratedRegex(@"Physical device ini?tialized\. GPU=(?.+), driver=(?-?\d+)\r?$", DefaultOptions)] + private static partial Regex PhysicalDeviceFound(); + [GeneratedRegex(@"Found [Vv]ulkan-compatible GPU: (?.+)\r?$", DefaultOptions)] + private static partial Regex VulkanDeviceFound(); + [GeneratedRegex(@"Renderer initialized on device '(?.+)'\r?$", DefaultOptions)] + private static partial Regex RenderDeviceInitialized(); + [GeneratedRegex(@"RSX: Failed to compile shader: ERROR: (?.+?)\r?$", DefaultOptions)] + private static partial Regex FailedToCompileShader(); + [GeneratedRegex(@"RSX: Compilation failed: ERROR: (?.+?)\r?$", DefaultOptions)] + private static partial Regex ShaderCompilationFailed(); + [GeneratedRegex(@"RSX: Linkage failed: (?.+?)\r?$", DefaultOptions)] + private static partial Regex ShaderLinkageFailed(); + [GeneratedRegex(@"RSX: Unsupported device: (?.+)\..+?\r?$", DefaultOptions)] + private static partial Regex UnsupportedDevice(); + [GeneratedRegex(@"RSX: Your GPU does not support (?.+)\..+?\r?$", DefaultOptions)] + private static partial Regex UnsupportedDeviceFeatures(); + [GeneratedRegex(@"RSX: GPU/driver lacks support for (?.+)\..+?\r?$", DefaultOptions)] + private static partial Regex UnsupportedDriverFeatures(); + [GeneratedRegex(@"RSX: Swapchain: present mode (?\d+?) in use.+?\r?$", DefaultOptions)] + private static partial Regex SwapchainMode(); + [GeneratedRegex(@"F \d+:\d+:\d+\.\d+ (({(?[^}]+)} )?(\w+:\s*(Thread terminated due to fatal error: )?|(\w+:\s*)?(class [^\r\n]+ thrown: ))\r?\n?)(?.*?)(\r?\n)(\r?\n|ยท|$)", DefaultSingleLine)] + private static partial Regex FatalError(); + [GeneratedRegex(@"Failed to load RAP file: (?.*?\.rap).*\r?$", DefaultOptions)] + private static partial Regex FailedToLoadRap(); + [GeneratedRegex(@"Rap file not found: โ€œ?(?.*?\.rap)โ€?\r?$", DefaultOptions)] + private static partial Regex RapNotFound(); + [GeneratedRegex(@"(?Pad handler expected but none initialized).*?\r?$", DefaultOptions)] + private static partial Regex MissingGamepad(); + [GeneratedRegex(@"Failed to bind device (?.+) to handler (?.+).*\r?$", DefaultOptions)] + private static partial Regex FailedToBindGamepad(); + [GeneratedRegex(@"Input: (?.*?) device .+ connected\r?$", DefaultOptions)] + private static partial Regex InputDeviceConnected(); + [GeneratedRegex(@"XAudio2Thread\s*: (?.+failed\s*\((?0x.+)\).*)\r?$", DefaultOptions)] + private static partial Regex XAudio2Thread(); + [GeneratedRegex(@"XAudio2Backend\s*: (?.+failed\s*\((?0x.+)\).*)\r?$", DefaultOptions)] + private static partial Regex CellAudioThread(); + [GeneratedRegex(@"Audio renderer (?.+) could not be initialized\r?$", DefaultOptions)] + private static partial Regex AudioBackendFailed(); + [GeneratedRegex(@"PPU executable hash: PPU-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)] + private static partial Regex PpuHash(); + [GeneratedRegex(@"OVL executable hash: OVL-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)] + private static partial Regex OvlHash(); + [GeneratedRegex(@"SPU executable hash: SPU-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)] + private static partial Regex SpuHash(); + [GeneratedRegex(@"PRX library hash: PRX-(?\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)] + private static partial Regex PrxHash(); + [GeneratedRegex(@"OVL hash of (\w|[\.\[\]])+: OVL-(?\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)] + private static partial Regex OvlHash2(); + [GeneratedRegex(@"PRX hash of (\w|[\.\[\]])+: PRX-(?\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)] + private static partial Regex PrxHash2(); + [GeneratedRegex(@"Applied patch \(hash='(?:\w{3}-\w+(-\d+)?)', description='(?.+?)', author='(?:.+?)', patch_version='(?:.+?)', file_version='(?:.+?)'\) \(<- (?:[1-9]\d*)\).*\r?$", DefaultOptions)] + private static partial Regex AppliedPatch(); + [GeneratedRegex(@"Loaded SPU image: SPU-(?\w+ \(<-\s*\d+\)).*?\r?$", DefaultOptions)] + private static partial Regex SpuImageLoad(); + [GeneratedRegex(@"'sys_fs_stat' failed (?!with 0x8001002c).+โ€œ(/dev_bdvd/(?.+)|/dev_hdd0/game/NP\w+/(?.+))โ€.*?\r?$", DefaultOptions)] + private static partial Regex SysFsStatFailed(); + [GeneratedRegex(@"'sys_fs_open' failed (?!with 0x8001002c).+โ€œ(/dev_bdvd/(?.+)|/dev_hdd0/game/NP\w+/(?.+))โ€.*?\r?$", DefaultOptions)] + private static partial Regex SysFsOpenFailed(); + [GeneratedRegex(@"'sys_fs_opendir' failed .+โ€œ/dev_bdvd/(?.+)โ€.*?\r?$", DefaultOptions)] + private static partial Regex SysFsOpenDirFailed(); + [GeneratedRegex(@"EDAT: Block at offset (?0x[0-9a-f]+) has invalid hash!.*?\r?$", DefaultOptions)] + private static partial Regex InvalidEdat(); + [GeneratedRegex(@"(?PS3 firmware is not installed.+)\r?$", DefaultOptions)] + private static partial Regex FwNotInstalled(); + [GeneratedRegex(@"(?do you have the PS3 firmware installed.*)\r?$", DefaultOptions)] + private static partial Regex FwNotInstalled2(); + [GeneratedRegex(@"U \d+:\d+:\d+\.\d+ ({(?.+?)} )?.*Unimplemented syscall (?.*)\r?$", DefaultOptions)] + private static partial Regex UnimplementedSyscall(); + [GeneratedRegex(@"cellAudio: Could not enqueue buffer onto audio backend(?.).*\r?$", DefaultOptions)] + private static partial Regex CellAudioEnqueueFailed(); + [GeneratedRegex(@"{PPU\[.+\]} (?[^ :]+)( TODO)?: (?!โ€œ)(?[^ :]+?)\(.*\r?$", DefaultOptions)] + private static partial Regex PpuSyscallTodo(); + [GeneratedRegex(@"Verification failed.+\(e=0x(?[0-9a-f]+)\[(?\d+)\]\)", DefaultOptions)] + private static partial Regex VerificationFailed(); + [GeneratedRegex(@"sys_tty_write\(\)\: โ€œ(?.*?)โ€\r?(\n|$)", DefaultSingleLine)] + private static partial Regex SysTtyWrite(); + [GeneratedRegex(@"โ‚ (?[^ :\[]+?) .*\r?$", DefaultOptions)] + private static partial Regex SyscallDump(); + [GeneratedRegex(@"(\b|_)(?(undub|translation patch))(\b|_)", RegexOptions.IgnoreCase | DefaultOptions)] + private static partial Regex UndubFlag(); + [GeneratedRegex(@"Input: Pad (?\d): device='(?(?!Null).+?)', handler=(?.+?), VID=.+?\r?$", DefaultOptions)] + private static partial Regex InputDeviceGamepad(); + [GeneratedRegex(@"Found game controller \d: .+ has_accel=(?.+?), has_gyro=(?.+?)\r?$", DefaultOptions)] + private static partial Regex SdlControllerName(); +} \ No newline at end of file diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs index ced7e58d..24fa6e4c 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/DropboxHandler.cs @@ -13,17 +13,18 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class DropboxHandler : BaseSourceHandler +internal sealed partial class DropboxHandler : BaseSourceHandler { //https://www.dropbox.com/s/62ls9lw5i52fuib/RPCS3.log.gz?dl=0 - private static readonly Regex ExternalLink = new(@"(?(https?://)?(www\.)?dropbox\.com/s/(?[^/\s]+)/(?[^/\?\s])(/dl=[01])?)", DefaultOptions); + [GeneratedRegex(@"(?(https?://)?(www\.)?dropbox\.com/s/(?[^/\s]+)/(?[^/\?\s])(/dl=[01])?)", DefaultOptions)] + private static partial Regex ExternalLink(); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs index fb9b4b1c..f0b43029 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GenericLinkHandler.cs @@ -12,16 +12,17 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class GenericLinkHandler : BaseSourceHandler +internal sealed partial class GenericLinkHandler : BaseSourceHandler { - private static readonly Regex ExternalLink = new(@"(?(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions); + [GeneratedRegex(@"(?(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions)] + private static partial Regex ExternalLink(); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs index 05577458..e7e17cb4 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/GoogleDriveHandler.cs @@ -16,10 +16,11 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class GoogleDriveHandler: BaseSourceHandler +internal sealed partial class GoogleDriveHandler: BaseSourceHandler { - private static readonly Regex ExternalLink = new(@"(?(https?://)?drive\.google\.com/(open\?id=|file/d/)(?[^/>\s]+))", DefaultOptions); - private static readonly string[] Scopes = { DriveService.Scope.DriveReadonly }; + [GeneratedRegex(@"(?(https?://)?drive\.google\.com/(open\?id=|file/d/)(?[^/>\s]+))", DefaultOptions)] + private static partial Regex ExternalLink(); + private static readonly string[] Scopes = [DriveService.Scope.DriveReadonly]; private static readonly string ApplicationName = "RPCS3 Compatibility Bot 2.0"; public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) @@ -30,7 +31,7 @@ internal sealed class GoogleDriveHandler: BaseSourceHandler if (string.IsNullOrEmpty(Config.GoogleApiCredentials)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/MediafireHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/MediafireHandler.cs index 834f936f..51ebf6a2 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/MediafireHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/MediafireHandler.cs @@ -13,10 +13,11 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class MediafireHandler : BaseSourceHandler +internal sealed partial class MediafireHandler : BaseSourceHandler { //http://www.mediafire.com/file/tmybrjpmtrpcejl/DemonsSouls_CrashLog_Nov.19th.zip/file - private static readonly Regex ExternalLink = new(@"(?(https?://)?(www\.)?mediafire\.com/file/(?[^/\s]+)/(?[^/\?\s]+)(/file)?)", DefaultOptions); + [GeneratedRegex(@"(?(https?://)?(www\.)?mediafire\.com/file/(?[^/\s]+)/(?[^/\?\s]+)(/file)?)", DefaultOptions)] + private static partial Regex ExternalLink(); private static readonly Client Client = new(); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) @@ -24,7 +25,7 @@ internal sealed class MediafireHandler : BaseSourceHandler if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs index bd769b46..0126c071 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/MegaHandler.cs @@ -11,11 +11,12 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class MegaHandler : BaseSourceHandler +internal sealed partial class MegaHandler : BaseSourceHandler { // mega.nz/#!8IJHBYyB!jw21m-GCs85uzj9E5XRysqyJCsNfZS0Zx4Eu9_zvuUM // mega.nz/file/8IJHBYyB#jw21m-GCs85uzj9E5XRysqyJCsNfZS0Zx4Eu9_zvuUM - private static readonly Regex ExternalLink = new(@"(?(https?://)?mega(\.co)?\.nz/(#(?[^/>\s]+)|file/(?[^/>\s]+)))", DefaultOptions); + [GeneratedRegex(@"(?(https?://)?mega(\.co)?\.nz/(#(?[^/>\s]+)|file/(?[^/>\s]+)))", DefaultOptions)] + private static partial Regex ExternalLink(); private static readonly IProgress Doodad = new Progress(_ => { }); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) @@ -23,7 +24,7 @@ internal sealed class MegaHandler : BaseSourceHandler if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs index 6bcd2a5a..a4a71993 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/OneDriveSourceHandler.cs @@ -13,9 +13,10 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class OneDriveSourceHandler : BaseSourceHandler +internal sealed partial class OneDriveSourceHandler : BaseSourceHandler { - private static readonly Regex ExternalLink = new(@"(?(https?://)?(1drv\.ms|onedrive\.live\.com)/[^>\s]+)", DefaultOptions); + [GeneratedRegex(@"(?(https?://)?(1drv\.ms|onedrive\.live\.com)/[^>\s]+)", DefaultOptions)] + private static partial Regex ExternalLink(); private static readonly Client Client = new(); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) @@ -23,7 +24,7 @@ internal sealed class OneDriveSourceHandler : BaseSourceHandler if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs index 0a27278f..3441ade3 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/PastebinHandler.cs @@ -11,16 +11,17 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class PastebinHandler : BaseSourceHandler +internal sealed partial class PastebinHandler : BaseSourceHandler { - private static readonly Regex ExternalLink = new(@"(?(https?://)pastebin.com/(raw/)?(?[^/>\s]+))", DefaultOptions); + [GeneratedRegex(@"(?(https?://)pastebin.com/(raw/)?(?[^/>\s]+))", DefaultOptions)] + private static partial Regex ExternalLink(); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) { if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsing/SourceHandlers/YandexDiskHandler.cs b/CompatBot/EventHandlers/LogParsing/SourceHandlers/YandexDiskHandler.cs index 1f9eb823..64484a5e 100644 --- a/CompatBot/EventHandlers/LogParsing/SourceHandlers/YandexDiskHandler.cs +++ b/CompatBot/EventHandlers/LogParsing/SourceHandlers/YandexDiskHandler.cs @@ -12,9 +12,10 @@ namespace CompatBot.EventHandlers.LogParsing.SourceHandlers; -internal sealed class YandexDiskHandler: BaseSourceHandler +internal sealed partial class YandexDiskHandler: BaseSourceHandler { - private static readonly Regex ExternalLink = new(@"(?(https?://)?(www\.)?yadi\.sk/d/(?[^/>\s]+))\b", DefaultOptions); + [GeneratedRegex(@"(?(https?://)?(www\.)?yadi\.sk/d/(?[^/>\s]+))\b", DefaultOptions)] + private static partial Regex ExternalLink(); private static readonly Client Client = new(); public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection handlers) @@ -22,7 +23,7 @@ internal sealed class YandexDiskHandler: BaseSourceHandler if (string.IsNullOrEmpty(message.Content)) return (null, null); - var matches = ExternalLink.Matches(message.Content); + var matches = ExternalLink().Matches(message.Content); if (matches.Count == 0) return (null, null); diff --git a/CompatBot/EventHandlers/LogParsingHandler.cs b/CompatBot/EventHandlers/LogParsingHandler.cs index 341919a7..273c3dcd 100644 --- a/CompatBot/EventHandlers/LogParsingHandler.cs +++ b/CompatBot/EventHandlers/LogParsingHandler.cs @@ -27,9 +27,9 @@ namespace CompatBot.EventHandlers; public static class LogParsingHandler { - private static readonly char[] LinkSeparator = { ' ', '>', '\r', '\n' }; + private static readonly char[] LinkSeparator = [' ', '>', '\r', '\n']; private static readonly ISourceHandler[] SourceHandlers = - { + [ new DiscordAttachmentHandler(), new GoogleDriveHandler(), new DropboxHandler(), @@ -39,15 +39,15 @@ public static class LogParsingHandler new MediafireHandler(), new GenericLinkHandler(), new PastebinHandler(), - }; + ]; private static readonly IArchiveHandler[] ArchiveHandlers = - { + [ new GzipHandler(), new ZipHandler(), new RarHandler(), new SevenZipHandler(), new PlainTextHandler(), - }; + ]; private static readonly SemaphoreSlim QueueLimiter = new(Math.Max(1, Environment.ProcessorCount / 2), Math.Max(1, Environment.ProcessorCount / 2)); private delegate void OnLog(DiscordClient client, DiscordChannel channel, DiscordMessage message, DiscordMember? requester = null, bool checkExternalLinks = false, bool force = false); diff --git a/CompatBot/EventHandlers/NewBuildsMonitor.cs b/CompatBot/EventHandlers/NewBuildsMonitor.cs index 287b8da2..6c183113 100644 --- a/CompatBot/EventHandlers/NewBuildsMonitor.cs +++ b/CompatBot/EventHandlers/NewBuildsMonitor.cs @@ -11,9 +11,10 @@ namespace CompatBot.EventHandlers; -internal static class NewBuildsMonitor +internal static partial class NewBuildsMonitor { - private static readonly Regex BuildResult = new(@"\[rpcs3:master\] \d+ new commit", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + [GeneratedRegex(@"\[rpcs3:master\] \d+ new commit", RegexOptions.IgnoreCase | RegexOptions.Singleline)] + private static partial Regex BuildResult(); private static readonly TimeSpan PassiveCheckInterval = TimeSpan.FromMinutes(20); private static readonly TimeSpan ActiveCheckInterval = TimeSpan.FromMinutes(1); private static readonly TimeSpan ActiveCheckResetThreshold = TimeSpan.FromMinutes(10); @@ -25,7 +26,7 @@ public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArg && !args.Author.IsCurrent && "github".Equals(args.Channel.Name, StringComparison.InvariantCultureIgnoreCase) && args.Message?.Embeds is [{ Title: { Length: > 0 } title }, ..] - && BuildResult.IsMatch(title) + && BuildResult().IsMatch(title) ) { Config.Log.Info("Found new PR merge message"); diff --git a/CompatBot/EventHandlers/PostLogHelpHandler.cs b/CompatBot/EventHandlers/PostLogHelpHandler.cs index fccbc6d8..d4c2f63a 100644 --- a/CompatBot/EventHandlers/PostLogHelpHandler.cs +++ b/CompatBot/EventHandlers/PostLogHelpHandler.cs @@ -12,10 +12,13 @@ namespace CompatBot.EventHandlers; -internal static class PostLogHelpHandler +internal static partial class PostLogHelpHandler { - private const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture; - private static readonly Regex UploadLogMention = new(@"\b((?(vul[ck][ae]n(-?1)?))|(?(post|upload|send|give)(ing)?\s+((a|the|rpcs3('s)?|your|you're|ur|my|full|game)\s+)*\blogs?))\b", DefaultOptions); + [GeneratedRegex( + @"\b((?(vul[ck][ae]n(-?1)?))|(?(post|upload|send|give)(ing)?\s+((a|the|rpcs3('s)?|your|you're|ur|my|full|game)\s+)*\blogs?))\b", + RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Singleline + )] + private static partial Regex UploadLogMention(); private static readonly SemaphoreSlim TheDoor = new(1, 1); private static readonly TimeSpan ThrottlingThreshold = TimeSpan.FromSeconds(5); private static readonly Dictionary DefaultExplanation = new() @@ -36,7 +39,7 @@ public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArg if (DateTime.UtcNow - lastMention < ThrottlingThreshold) return; - var match = UploadLogMention.Match(args.Message.Content); + var match = UploadLogMention().Match(args.Message.Content); if (!match.Success || string.IsNullOrEmpty(match.Groups["help"].Value)) return; diff --git a/CompatBot/EventHandlers/ProductCodeLookup.cs b/CompatBot/EventHandlers/ProductCodeLookup.cs index f0e96b8d..04240ecd 100644 --- a/CompatBot/EventHandlers/ProductCodeLookup.cs +++ b/CompatBot/EventHandlers/ProductCodeLookup.cs @@ -16,10 +16,11 @@ namespace CompatBot.EventHandlers; -internal static class ProductCodeLookup +internal static partial class ProductCodeLookup { // see http://www.psdevwiki.com/ps3/Productcode - public static readonly Regex ProductCode = new(@"(?(?:[BPSUVX][CL]|P[ETU]|NP)[AEHJKPUIX][ABDJKLMPQRSTX]|MRTC)[ \-]?(?\d{5})", RegexOptions.Compiled | RegexOptions.IgnoreCase); + [GeneratedRegex(@"(?(?:[BPSUVX][CL]|P[ETU]|NP)[AEHJKPUIX][ABDJKLMPQRSTX]|MRTC)[ \-]?(?\d{5})", RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-GB")] + public static partial Regex Pattern(); private static readonly Client CompatClient = new(); public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArgs args) @@ -101,7 +102,7 @@ public static List GetProductIds(string? input) if (string.IsNullOrEmpty(input)) return new(0); - return ProductCode.Matches(input) + return Pattern().Matches(input) .Select(match => (match.Groups["letters"].Value + match.Groups["numbers"]).ToUpper()) .Distinct() .ToList(); diff --git a/CompatBot/EventHandlers/TableFlipMonitor.cs b/CompatBot/EventHandlers/TableFlipMonitor.cs index 9328a80d..1df74bb3 100644 --- a/CompatBot/EventHandlers/TableFlipMonitor.cs +++ b/CompatBot/EventHandlers/TableFlipMonitor.cs @@ -12,9 +12,11 @@ namespace CompatBot.EventHandlers; -internal static class TableFlipMonitor +internal static partial class TableFlipMonitor { - private static readonly char[] OpenParen = {'(', '๏ผˆ', 'ส•'}; + [GeneratedRegex(@"(๐ŸŽฒ|\s)+")] + private static partial Regex DiceRoll(); + private static readonly char[] OpenParen = ['(', '๏ผˆ', 'ส•']; public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArgs args) { @@ -38,8 +40,7 @@ public static async Task OnMessageCreated(DiscordClient _, MessageCreateEventArg try { var content = args.Message.Content; - - if (content.Contains("๐ŸŽฒ") && Regex.IsMatch(content, @"(๐ŸŽฒ|\s)+")) + if (content.Contains("๐ŸŽฒ") && DiceRoll().IsMatch(content)) { var count = 1; var idx = content.IndexOf("๐ŸŽฒ"); diff --git a/CompatBot/EventHandlers/UnknownCommandHandler.cs b/CompatBot/EventHandlers/UnknownCommandHandler.cs index 25535d6a..016b4d5c 100644 --- a/CompatBot/EventHandlers/UnknownCommandHandler.cs +++ b/CompatBot/EventHandlers/UnknownCommandHandler.cs @@ -13,12 +13,13 @@ namespace CompatBot.EventHandlers; -internal static class UnknownCommandHandler +internal static partial class UnknownCommandHandler { - private static readonly Regex BinaryQuestion = new( + [GeneratedRegex( @"^\s*(am I|(are|is|do(es)|did|can(?!\s+of\s+)|should|must|have)(n't)?|shall|shan't|may|will|won't)\b", - RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase - ); + RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase + )] + private static partial Regex BinaryQuestion(); public static Task OnError(CommandsNextExtension cne, CommandErrorEventArgs e) { @@ -49,7 +50,7 @@ public static async void OnErrorInternal(CommandsNextExtension cne, CommandError if (e.Context.Prefix != Config.CommandPrefix && e.Context.Prefix != Config.AutoRemoveCommandPrefix && e.Context.Message.Content is string msgTxt - && (msgTxt.EndsWith("?") || BinaryQuestion.IsMatch(msgTxt.AsSpan(e.Context.Prefix.Length))) + && (msgTxt.EndsWith("?") || BinaryQuestion().IsMatch(msgTxt.AsSpan(e.Context.Prefix.Length))) && e.Context.CommandsNext.RegisteredCommands.TryGetValue("8ball", out var cmd)) { var updatedContext = e.Context.CommandsNext.CreateContext( diff --git a/CompatBot/EventHandlers/UsernameZalgoMonitor.cs b/CompatBot/EventHandlers/UsernameZalgoMonitor.cs index 11464ba8..7aeb7426 100644 --- a/CompatBot/EventHandlers/UsernameZalgoMonitor.cs +++ b/CompatBot/EventHandlers/UsernameZalgoMonitor.cs @@ -15,10 +15,10 @@ namespace CompatBot.EventHandlers; public static class UsernameZalgoMonitor { - private static readonly HashSet OversizedChars = new() - { + private static readonly HashSet OversizedChars = + [ '๊ง', '๊ง‚', 'โŽ', 'โŽ ', 'โงน', 'โงธ', 'โŽ›', 'โŽž', '๏ทฝ', 'โธป', 'แ€ช', '๊ง…', '๊ง„', 'หž', - }; + ]; public static async Task OnUserUpdated(DiscordClient c, UserUpdateEventArgs args) { diff --git a/CompatBot/Program.cs b/CompatBot/Program.cs index cf888572..1846cffc 100644 --- a/CompatBot/Program.cs +++ b/CompatBot/Program.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using CompatBot.Commands; @@ -38,6 +39,9 @@ internal static async Task Main(string[] args) { Config.TelemetryClient?.TrackEvent("startup"); + //AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromMilliseconds(100)); + Regex.CacheSize = 200; // default is 15, we need more for content filter + Console.WriteLine("Confinement: " + SandboxDetector.Detect()); if (args.Length > 0 && args[0] == "--dry-run") { diff --git a/CompatBot/ThumbScrapper/GameTdbScraper.cs b/CompatBot/ThumbScrapper/GameTdbScraper.cs index e9188ef3..2ac8340c 100644 --- a/CompatBot/ThumbScrapper/GameTdbScraper.cs +++ b/CompatBot/ThumbScrapper/GameTdbScraper.cs @@ -16,14 +16,15 @@ namespace CompatBot.ThumbScrapper; -internal static class GameTdbScraper +internal static partial class GameTdbScraper { private static readonly HttpClient HttpClient = HttpClientFactory.Create(new CompressionMessageHandler()); private static readonly Uri TitleDownloadLink = new("https://www.gametdb.com/ps3tdb.zip?LANG=EN"); - private static readonly Regex CoverArtLink = new( + [GeneratedRegex( @"(?https?://art\.gametdb\.com/ps3/cover(?!full)[/\w\d]+\.jpg(\?\d+)?)", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.ExplicitCapture - ); + RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.ExplicitCapture + )] + private static partial Regex CoverArtLink(); //private static readonly List PreferredOrder = new List{"coverHQ", "coverM", "cover"}; public static async Task RunAsync(CancellationToken cancellationToken) @@ -50,7 +51,7 @@ public static async Task RunAsync(CancellationToken cancellationToken) try { var html = await HttpClient.GetStringAsync("https://www.gametdb.com/PS3/" + productCode).ConfigureAwait(false); - var coverLinks = CoverArtLink.Matches(html) + var coverLinks = CoverArtLink().Matches(html) .Select(m => m.Groups["cover_link"].Value) .Distinct() .Where(l => l.Contains(productCode, StringComparison.InvariantCultureIgnoreCase)) @@ -106,7 +107,7 @@ private static async Task UpdateGameTitlesAsync(CancellationToken cancellationTo continue; var productId = (await xmlReader.ReadElementContentAsStringAsync().ConfigureAwait(false)).ToUpperInvariant(); - if (!ProductCodeLookup.ProductCode.IsMatch(productId)) + if (!ProductCodeLookup.Pattern().IsMatch(productId)) continue; string? title = null; diff --git a/CompatBot/ThumbScrapper/PsnScraper.cs b/CompatBot/ThumbScrapper/PsnScraper.cs index 7e469f99..a33aeeb7 100644 --- a/CompatBot/ThumbScrapper/PsnScraper.cs +++ b/CompatBot/ThumbScrapper/PsnScraper.cs @@ -14,15 +14,17 @@ namespace CompatBot.ThumbScrapper; -internal sealed class PsnScraper +internal sealed partial class PsnScraper { private static readonly PsnClient.Client Client = new(); - public static readonly Regex ContentIdMatcher = new( - @"(?(?(?\w\w)(?\d{4}))-(?(?\w{4})(?\d{5}))_(?\d\d)-(?