Some performance and code optimizations (#955)

* upgrade deps, remove wrong ppu hashes

* upgrade compiler packages

will require container pull after build

* replace Regex with compiler-generated versions

* use new collection initialization syntax

* configure global defaults for regex

* bump min amd driver version

fixes #950

* add macos version check

fixes #948

* fix #954 (!sudo log date)
This commit is contained in:
Ilya
2024-05-18 18:26:34 +05:00
committed by GitHub
parent ad21597c00
commit 72dbc4074a
83 changed files with 1058 additions and 640 deletions

View File

@@ -11,6 +11,6 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="StrawberryShake.Server" Version="13.8.1" />
<PackageReference Include="StrawberryShake.Server" Version="13.9.4" />
</ItemGroup>
</Project>

View File

@@ -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<int> ResultAmount = new(){25, 50, 100, 200};
public static readonly List<int> ResultAmount = [25, 50, 100, 200];
public static readonly Dictionary<char, string[]> Directions = new()
{
{'a', new []{"a", "asc", "ascending"}},
{'d', new []{"d", "desc", "descending"} },
{'a', ["a", "asc", "ascending"] },
{'d', ["d", "desc", "descending"] },
};
public static readonly Dictionary<string, int> Statuses = new()
@@ -58,8 +58,8 @@ public static class ApiConfig
public static readonly Dictionary<char, string[]> 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<string, char> ReverseDirections;

View File

@@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="NLog" Version="5.3.2" />
</ItemGroup>
</Project>

View File

@@ -11,7 +11,7 @@ public class CompressionMessageHandler : DelegatingHandler
{
public ICollection<ICompressor> 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;

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Octokit" Version="9.1.2" />
<PackageReference Include="Octokit" Version="11.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CompatApiClient\CompatApiClient.csproj" />

View File

@@ -24,7 +24,6 @@ public class IrdClient
private readonly HttpClient client;
private readonly JsonSerializerOptions jsonOptions;
private static readonly Regex IrdFilename = new(@"ird/(?<filename>\w{4}\d{5}-[A-F0-9]+\.ird)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
public IrdClient()
{

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DiscUtils.OpticalDisk" Version="0.16.13" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.59" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup>

View File

@@ -17,7 +17,7 @@ using MediafireClient.POCOs;
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*""(?<security_token>.+)""", 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*""(?<direct_link>https?://download\d+\.mediafire\.com/.+)""");
[GeneratedRegex(@"(var\s+optDirectURL|href)\s*=\s*""(?<direct_link>https?://download\d+\.mediafire\.com/.+)""")]
private static partial Regex DirectUrlRegex();
public Client()
{
@@ -81,7 +82,7 @@ public sealed class 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);
}

View File

@@ -20,7 +20,7 @@ using PsnClient.Utils;
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(@"(?<id>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(@"(?<id>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=(?<dest>\d+);ImageVersion=(?<image>[0-9a-f]+);SystemSoftwareVersion=(?<version>\d+\.\d+);CDN=(?<url>http[^;]+);CDN_Timeout=(?<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 class Client
tries++;
}
if (response.StatusCode == HttpStatusCode.Redirect)
return new(0);
return [];
}
using (response)
@@ -125,7 +129,7 @@ public class 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<string>();
foreach (Match m in matches)
if (m.Groups["id"].Value is {Length: >0} id)
@@ -345,7 +349,7 @@ public class Client
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 @@ public class Client
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;

View File

@@ -18,7 +18,7 @@ public static class TmdbHasher
public static byte[] FromHexString(this string hexString)
{
if (hexString.Length == 0)
return Array.Empty<byte>();
return [];
if (hexString.Length % 2 != 0)
throw new ArgumentException("Invalid hex string format: odd number of octets", nameof(hexString));

View File

@@ -308,7 +308,7 @@ internal sealed class BotStats: BaseCommandModuleCustom
}
}
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 = ["🙀", "😿", "😾",];
}

View File

@@ -32,7 +32,7 @@ using Microsoft.TeamFoundation.Build.WebApi;
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(?<version>\d+\.\d+\.\d+)-(?<build>\d+)-(?<commit>[0-9a-f]+)\b",
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture
);
[GeneratedRegex(@"v(?<version>\d+\.\d+\.\d+)-(?<build>\d+)-(?<commit>[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 @@ internal sealed class CompatList : BaseCommandModuleCustom
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 @@ internal sealed class CompatList : BaseCommandModuleCustom
#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 @@ internal sealed class CompatList : BaseCommandModuleCustom
}
}
var scoreList = JsonSerializer.Deserialize<List<Metacritic>>(json) ?? new();
var scoreList = JsonSerializer.Deserialize<List<Metacritic>>(json) ?? [];
Config.Log.Debug($"Importing {scoreList.Count} Metacritic items");
var duplicates = new List<Metacritic>();
@@ -666,7 +666,7 @@ internal sealed class CompatList : BaseCommandModuleCustom
.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 @@ internal sealed class CompatList : BaseCommandModuleCustom
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)

View File

@@ -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 @@ internal sealed class ContentFilters: BaseCommandModuleCustom
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 @@ internal sealed class ContentFilters: BaseCommandModuleCustom
{
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)
{

View File

@@ -10,9 +10,10 @@ using DSharpPlus.Entities;
namespace CompatBot.Commands.Converters;
internal sealed class TextOnlyDiscordChannelConverter : IArgumentConverter<DiscordChannel>
internal sealed partial class TextOnlyDiscordChannelConverter : IArgumentConverter<DiscordChannel>
{
private static Regex ChannelRegex { get; } = new(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
[GeneratedRegex(@"^<#(\d+)>$", RegexOptions.ECMAScript)]
private static partial Regex ChannelRegex();
Task<Optional<DiscordChannel>> IArgumentConverter<DiscordChannel>.ConvertAsync(string value, CommandContext ctx)
=> ConvertAsync(value, ctx);
@@ -20,7 +21,7 @@ internal sealed class TextOnlyDiscordChannelConverter : IArgumentConverter<Disco
public static async Task<Optional<DiscordChannel>> ConvertAsync(string value, CommandContext ctx)
{
var guildList = new List<DiscordGuild>(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 @@ internal sealed class TextOnlyDiscordChannelConverter : IArgumentConverter<Disco
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 = (

View File

@@ -19,11 +19,12 @@ using Microsoft.EntityFrameworkCore;
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(@"((?<days>\d+)(\.|d\s*))?((?<hours>\d+)(\:|h\s*))?((?<mins>\d+)m?)?",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
[GeneratedRegex(@"((?<days>\d+)(\.|d\s*))?((?<hours>\d+)(\:|h\s*))?((?<mins>\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 @@ internal class EventsBaseCommand: BaseCommandModuleCustom
private static async Task<TimeSpan?> TryParseTimeSpanAsync(CommandContext ctx, string duration, bool react = true)
{
var d = Duration.Match(duration);
var d = Duration().Match(duration);
if (!d.Success)
{
if (react)

View File

@@ -6,6 +6,7 @@ using System.Linq;
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 @@ internal sealed class Explain: BaseCommandModuleCustom
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);

View File

@@ -49,12 +49,12 @@ internal sealed class ForcedNicknames : BaseCommandModuleCustom
List<DiscordGuild> guilds;
if (ctx.Guild == null)
{
guilds = ctx.Client.Guilds?.Values.ToList() ?? new List<DiscordGuild>(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();

View File

@@ -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()

View File

@@ -16,12 +16,12 @@ using Microsoft.EntityFrameworkCore;
namespace CompatBot.Commands;
internal sealed class Misc: BaseCommandModuleCustom
internal sealed partial class Misc: BaseCommandModuleCustom
{
private static readonly Random rng = new();
private static readonly List<string> EightBallAnswers = new()
{
private static readonly List<string> 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",
@@ -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<string> EightBallSnarkyComments = new()
{
private static readonly List<string> 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<string> EightBallTimeUnits = new()
{
private static readonly List<string> 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<string> RateAnswers = new()
{
// 60
private static readonly List<string> 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<string> 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<char> Vowels = new() {'a', 'e', 'i', 'o', 'u'};
private static readonly HashSet<char> Vowels = ['a', 'e', 'i', 'o', 'u'];
private static readonly Regex Instead = new("rate (?<instead>.+) instead", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline);
[GeneratedRegex("rate (?<instead>.+) instead", RegexOptions.ExplicitCapture | RegexOptions.Singleline)]
private static partial Regex Instead();
[GeneratedRegex(@"(?<num>\d+)?d(?<face>\d+)(?:\+(?<mod>\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 sealed class Misc: BaseCommandModuleCustom
{
var result = "";
var embed = new DiscordEmbedBuilder();
if (dices is string dice && Regex.Matches(dice, @"(?<num>\d+)?d(?<face>\d+)(?:\+(?<mod>\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 @@ internal sealed class Misc: BaseCommandModuleCustom
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 @@ internal sealed class Misc: BaseCommandModuleCustom
var choiceFlags = new HashSet<char>();
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();

View File

@@ -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 @@ internal sealed partial class Psn
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 @@ internal sealed partial class Psn
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)
{

View File

@@ -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 @@ internal sealed partial class Psn: BaseCommandModuleCustom
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)
{

View File

@@ -17,6 +17,9 @@ internal partial class Sudo
[Description("Commands to manage dotnet")]
public sealed partial class Dotnet : BaseCommandModuleCustom
{
[GeneratedRegex(@"\.NET( Core)? (?<major>\d+)\.(?<minor>\d+)\.(?<patch>\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 @@ internal partial class Sudo
if (string.IsNullOrEmpty(version))
{
var versionMatch = Regex.Match(
System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription,
@"\.NET( Core)? (?<major>\d+)\.(?<minor>\d+)\.(?<patch>\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 @@ internal partial class Sudo
return (true, stdout);
}
}
}

View File

@@ -144,7 +144,7 @@ internal sealed partial class Sudo
{
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;

View File

@@ -135,18 +135,21 @@ internal sealed partial class Sudo : BaseCommandModuleCustom
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 @@ internal sealed partial class Sudo : BaseCommandModuleCustom
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);

View File

@@ -49,26 +49,26 @@ internal sealed class Vision: BaseCommandModuleCustom
{
["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]

View File

@@ -43,32 +43,32 @@
<PackageReference Include="DSharpPlus.CommandsNext" Version="4.4.6" />
<PackageReference Include="DSharpPlus.Interactivity" Version="4.4.6" />
<PackageReference Include="DSharpPlus.SlashCommands" Version="4.4.6" />
<PackageReference Include="Google.Apis.Drive.v3" Version="1.66.0.3309" />
<PackageReference Include="Google.Apis.Drive.v3" Version="1.68.0.3373" />
<PackageReference Include="ksemenenko.ColorThief" Version="1.1.1.4" />
<PackageReference Include="MathParser.org-mXparser" Version="5.2.1" />
<PackageReference Include="MegaApiClient" Version="1.10.4" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.22.0" />
<PackageReference Include="Microsoft.ApplicationInsights.PerfCounterCollector" Version="2.22.0" />
<PackageReference Include="Microsoft.Azure.CognitiveServices.Vision.ComputerVision" Version="7.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="16.205.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Nerdbank.Streams" Version="2.10.72" />
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="Nerdbank.Streams" Version="2.11.74" />
<PackageReference Include="NLog" Version="5.3.2" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.11" />
<PackageReference Include="NReco.Text.AhoCorasickDoubleArrayTrie" Version="1.1.1" />
<PackageReference Include="SharpCompress" Version="0.36.0" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.1" />
<PackageReference Include="System.Drawing.Common" Version="8.0.2" />
<PackageReference Include="SharpCompress" Version="0.37.2" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.3" />
<PackageReference Include="System.Drawing.Common" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Clients\CirrusCiClient\CirrusCiClient.csproj" />

View File

@@ -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<IMention>();
internal static readonly IMention[] UsersOnly = [UserMention.All];
internal static readonly IMention[] Nothing = [];
}
internal static string CurrentLogPath => Path.GetFullPath(Path.Combine(LogPath, "bot.log"));

View File

@@ -26,7 +26,7 @@ namespace CompatBot.Database.Migrations
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");

View File

@@ -25,7 +25,7 @@ namespace CompatBot.Database.Migrations
migrationBuilder.CreateIndex(
name: "stats_category_key",
table: "stats",
columns: new[] { "category", "key" },
columns: ["category", "key"],
unique: true);
}

View File

@@ -24,7 +24,7 @@ namespace CompatBot.Database.Migrations
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);
}

View File

@@ -21,7 +21,7 @@ namespace CompatBot.Database.Migrations
migrationBuilder.CreateIndex(
name: "stats_category_bucket_key",
table: "stats",
columns: new[] { "category", "bucket", "key" },
columns: ["category", "bucket", "key"],
unique: true);
}
@@ -38,7 +38,7 @@ namespace CompatBot.Database.Migrations
migrationBuilder.CreateIndex(
name: "stats_category_key",
table: "stats",
columns: new[] { "category", "key" },
columns: ["category", "key"],
unique: true);
}
}

View File

@@ -50,9 +50,9 @@ internal static class HwInfoProvider
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");

View File

@@ -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), "");

View File

@@ -15,7 +15,7 @@ using System.Collections.Generic;
namespace CompatBot.EventHandlers
{
internal static class BotReactionsHandler
internal static partial class BotReactionsHandler
{
private static readonly AhoCorasickDoubleArrayTrie<bool> ChillCheck = new(new[]
{
@@ -47,10 +47,10 @@ namespace CompatBot.EventHandlers
}.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 @@ namespace CompatBot.EventHandlers
}.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>kot(to)?)|(?<doggo>doggo|jarves))\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture
);
[GeneratedRegex(@"\b((?<kot>kot(to)?)|(?<doggo>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 @@ namespace CompatBot.EventHandlers
}
#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

View File

@@ -18,10 +18,14 @@ using Microsoft.Extensions.Caching.Memory;
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)/(?<invite_id>[a-z0-9\-]+))|(\.me/(?<me_id>.*?))(\s|>|$))", DefaultOptions);
[GeneratedRegex(@"(https?://)?discord((((app)?\.com/invite|\.gg)/(?<invite_id>[a-z0-9\-]+))|(\.me/(?<me_id>.*?))(\s|>|$))", RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Multiline)]
private static partial Regex InviteLink();
[GeneratedRegex(@"name=""csrf-token"" content=""(?<csrf_token>\w+)""")]
private static partial Regex CsrfTokenPattern();
[GeneratedRegex(@"name=""serverEid"" value=""(?<server_eid>\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 @@ internal static class DiscordInviteFilter
if (string.IsNullOrEmpty(message))
return (false, false, new(0));
var inviteCodes = new HashSet<string>(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<string>(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<string>? recentInvites) && recentInvites is not null)
{
@@ -232,8 +236,8 @@ internal static class DiscordInviteFilter
continue;
hasInvalidInvites = true;
var csrfTokenMatch = Regex.Match(html, @"name=""csrf-token"" content=""(?<csrf_token>\w+)""");
var serverEidMatch = Regex.Match(html, @"name=""serverEid"" value=""(?<server_eid>\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")

View File

@@ -11,13 +11,17 @@ using DSharpPlus.EventArgs;
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(@"(?<issue_mention>\b(issue|pr|pull[ \-]request|bug)\s*#?\s*(?<number>\d+)|\B#(?<also_number>1?\d{4})|(https?://)github.com/RPCS3/rpcs3/(issues|pull)/(?<another_number>\d+)(#issuecomment-(?<comment_id>\d+))?)\b", DefaultOptions);
public static readonly Regex CommitMention = new(@"(?<commit_mention>(https?://)github.com/RPCS3/rpcs3/commit/(?<commit_hash>[0-9a-f]+))\b", DefaultOptions);
public static readonly Regex ImageMarkup = new(@"(?<img_markup>!\[(?<img_caption>[^\]]+)\]\((?<img_link>\w+://[^\)]+)\))", DefaultOptions);
private static readonly Regex IssueLink = new(@"github.com/RPCS3/rpcs3/issues/(?<number>\d+)", DefaultOptions);
private const RegexOptions DefaultOptions = RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture;
[GeneratedRegex(@"(?<issue_mention>\b(issue|pr|pull[ \-]request|bug)\s*#?\s*(?<number>\d+)|\B#(?<also_number>1?\d{4})|(https?://)github.com/RPCS3/rpcs3/(issues|pull)/(?<another_number>\d+)(#issuecomment-(?<comment_id>\d+))?)\b", DefaultOptions)]
internal static partial Regex IssueMention();
[GeneratedRegex(@"(?<commit_mention>(https?://)github.com/RPCS3/rpcs3/commit/(?<commit_hash>[0-9a-f]+))\b", DefaultOptions)]
internal static partial Regex CommitMention();
[GeneratedRegex(@"(?<img_markup>!\[(?<img_caption>[^\]]+)\]\((?<img_link>\w+://[^\)]+)\))", DefaultOptions)]
internal static partial Regex ImageMarkup();
[GeneratedRegex(@"github.com/RPCS3/rpcs3/issues/(?<number>\d+)", DefaultOptions)]
internal static partial Regex IssueLink();
public static async Task OnMessageCreated(DiscordClient c, MessageCreateEventArgs args)
{
@@ -72,7 +76,7 @@ internal static class GithubLinksHandler
public static List<int> GetIssueIds(string input)
{
return IssueMention.Matches(input)
return IssueMention().Matches(input)
.SelectMany(match => new[]
{
match.Groups["number"].Value,
@@ -86,13 +90,16 @@ internal static class GithubLinksHandler
}
public static HashSet<int> 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;
})
);
];
}
}

View File

@@ -17,15 +17,15 @@ using DSharpPlus.EventArgs;
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(
[GeneratedRegex(
@"(\b((is|does|can I play|any(one|1) tr(y|ied)|how's|(wonder(ing)?|me|knows?) if)\s+)(?<game_title_1>.+?)\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+)(?<game_title_2>.+?)(\s+((now|currently|at all|possibly|fully)\s+)?((it?s )?playable|work(s|ing)?|on (it|this))\b|\?|$))"+
@"|(^(?<game_title_3>.+?)\s+((is )?(playable|work(s|ing)?))\?)",
DefaultOptions
);
RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture
)]
private static partial Regex GameNameStatusMention();
private static readonly ConcurrentDictionary<ulong, DateTime> CooldownBuckets = new();
private static readonly TimeSpan CooldownThreshold = TimeSpan.FromSeconds(5);
private static readonly Client Client = new();
@@ -48,7 +48,7 @@ internal static class IsTheGamePlayableHandler
return;
#endif
var matches = GameNameStatusMention1.Matches(args.Message.Content);
var matches = GameNameStatusMention().Matches(args.Message.Content);
if (!matches.Any())
return;
@@ -65,7 +65,7 @@ internal static class IsTheGamePlayableHandler
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);

View File

@@ -10,9 +10,10 @@ using DSharpPlus.EventArgs;
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 @@ internal static class LogAsTextMonitor
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 = "";

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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<LogSection> LogSections = new()
{
private static readonly List<LogSection> LogSections =
[
new()
{
Extractors = new()
{
["RPCS3 v"] = new(@"(^|.+\d:\d\d:\d\d\.\d{6})\s*(?<build_and_specs>RPCS3 [^\xC2\xB7]+?)\r?(\n·|$)", DefaultSingleLineOptions),
["0:00:00.0"] = new(@"(?<first_unicode_dot>·).+\r?$", DefaultOptions),
["Operating system:"] = LogParserResult.OsInfoInLog,
["Current Time:"] = new(@"Current Time: (?<log_start_timestamp>.+)\r?$", DefaultOptions),
["Installation ID:"] = new(@"Installation ID: (?<hw_id>.+)\r?$", DefaultOptions),
["Physical device in"] = new(@"Physical device ini?tialized\. GPU=(?<vulkan_gpu>.+), driver=(?<vulkan_driver_version_raw>-?\d+)\r?$", DefaultOptions),
["Found vulkan-compatible GPU:"] = new(@"Found [Vv]ulkan-compatible GPU: (?<vulkan_found_device>'(?<vulkan_compatible_device_name>.+)' running.+)\r?$", DefaultOptions),
["Finished reading database from file:"] = new(@"Finished reading database from file: (?<compat_database_path>.*compat_database.dat).*\r?$", DefaultOptions),
["Database file not found:"] = new(@"Database file not found: (?<compat_database_path>.*compat_database.dat).*\r?$", DefaultOptions),
["Successfully installed PS3 firmware"] = new(@"(?<fw_installed_message>Successfully installed PS3 firmware) version (?<fw_version_installed>\d+\.\d+).*\r?$", DefaultOptions),
["Firmware version:"] = new(@"Firmware version: (?<fw_version_installed>\d+\.\d+).*\r?$", DefaultOptions),
["Title:"] = new(@"(?:LDR|SYS): Title: (?<game_title>.*)?\r?$", DefaultOptions),
["Serial:"] = new(@"Serial: (?<serial>[A-z]{4}\d{5})\r?$", DefaultOptions),
["Category:"] = new(@"Category: (?<game_category>.*)?\r?$", DefaultOptions),
["LDR: Version:"] = new(@"Version: (?<disc_app_version>\S+) / (?<disc_package_version>\S+).*?\r?$", DefaultOptions),
["SYS: Version:"] = new(@"Version: (APP_VER=)?(?<disc_app_version>\S+) (/ |VERSION=)(?<disc_package_version>\S+).*?\r?$", DefaultOptions),
["LDR: Cache"] = new(@"Cache: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions),
["SYS: Cache"] = new(@"Cache: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions),
["LDR: Path"] = new(@"Path: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions),
["SYS: Path"] = new(@"Path: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions),
["LDR: Path:"] = new(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["SYS: Path:"] = new(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["custom config:"] = new(@"custom config: (?<custom_config>.*?)\r?$", DefaultOptions),
["patch_log: Failed to load patch file"] = new(@"patch_log: Failed to load patch file (?<patch_error_file>\S*)\r?\n.* line (?<patch_error_line>\d+), column (?<patch_error_column>\d+): (?<patch_error_text>.*?)$", 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: (?<ppu_decoder>.*?)\r?$", DefaultOptions),
["PPU Threads:"] = new(@"PPU Threads: (?<ppu_threads>.*?)\r?$", DefaultOptions),
["Use LLVM CPU:"] = new("Use LLVM CPU: \\\"?(?<llvm_arch>.*?)\\\"?\r?$", DefaultOptions),
["thread scheduler"] = new(@"[Ss]cheduler( Mode)?: (?<thread_scheduler>.*?)\r?$", DefaultOptions),
["SPU Decoder:"] = new(@"SPU Decoder: (?<spu_decoder>.*?)\r?$", DefaultOptions),
["secondary cores:"] = new(@"secondary cores: (?<spu_secondary_cores>.*?)\r?$", DefaultOptions),
//["priority:"] = new(@"priority: (?<spu_lower_thread_priority>.*?)\r?$", DefaultOptions),
["SPU Threads:"] = new(@"SPU Threads: (?<spu_threads>.*?)\r?$", DefaultOptions),
["SPU delay penalty:"] = new(@"SPU delay penalty: (?<spu_delay_penalty>.*?)\r?$", DefaultOptions),
["SPU loop detection:"] = new(@"SPU loop detection: (?<spu_loop_detection>.*?)\r?$", DefaultOptions),
["Max SPURS Threads:"] = new(@"Max SPURS Threads: (?<spurs_threads>\d*?)\r?$", DefaultOptions),
["SPU Block Size:"] = new(@"SPU Block Size: (?<spu_block_size>.*?)\r?$", DefaultOptions),
["Enable TSX:"] = new(@"Enable TSX: (?<enable_tsx>.*?)\r?$", DefaultOptions),
["Accurate xfloat:"] = new(@"Accurate xfloat: (?<accurate_xfloat>.*?)\r?$", DefaultOptions),
["Approximate xfloat:"] = new(@"Approximate xfloat: (?<approximate_xfloat>.*?)\r?$", DefaultOptions),
["Relaxed xfloat:"] = new(@"Relaxed xfloat: (?<relaxed_xfloat>.*?)\r?$", DefaultOptions),
["XFloat Accuracy:"] = new(@"XFloat Accuracy: (?<xfloat_mode>.*?)\r?$", DefaultOptions),
["Accurate GETLLAR:"] = new(@"Accurate GETLLAR: (?<accurate_getllar>.*?)\r?$", DefaultOptions),
["Accurate PUTLLUC:"] = new(@"Accurate PUTLLUC: (?<accurate_putlluc>.*?)\r?$", DefaultOptions),
["Accurate RSX reservation access:"] = new(@"Accurate RSX reservation access: (?<accurate_rsx_reservation>.*?)\r?$", DefaultOptions),
["RSX FIFO Accuracy:"] = new(@"RSX FIFO Accuracy: (?<rsx_fifo_mode>.*?)\r?$", DefaultOptions),
["Debug Console Mode:"] = new(@"Debug Console Mode: (?<debug_console_mode>.*?)\r?$", DefaultOptions),
["Lib Loader:"] = new(@"[Ll]oader: (?<lib_loader>.*?)\r?$", DefaultOptions),
["Hook static functions:"] = new(@"Hook static functions: (?<hook_static_functions>.*?)\r?$", DefaultOptions),
["Load libraries:"] = new(@"libraries:\r?\n(?<library_list>(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions),
["Libraries Control:"] = new(@"Libraries Control:\r?\n(?<library_list>(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions),
["HLE lwmutex:"] = new(@"HLE lwmutex: (?<hle_lwmutex>.*?)\r?$", DefaultOptions),
["Clocks scale:"] = new(@"Clocks scale: (?<clock_scale>.*?)\r?$", DefaultOptions),
["Max CPU Preempt Count:"] = new(@"Max CPU Preempt Count: (?<cpu_preempt_count>.*?)\r?$", DefaultOptions),
["Sleep Timers Accuracy:"] = new(@"Sleep Timers Accuracy: (?<sleep_timer>.*?)\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/: (?<host_root>.*?)\r?$", DefaultOptions),
["Enable /host_root/:"] = EnableHostRoot(),
},
EndTrigger = new[] {"Video:"},
EndTrigger = ["Video:"],
},
new()
{
Extractors = new()
{
["Renderer:"] = new("Renderer: (?<renderer>.*?)\r?$", DefaultOptions),
["Resolution:"] = new("Resolution: (?<resolution>.*?)\r?$", DefaultOptions),
["Aspect ratio:"] = new("Aspect ratio: (?<aspect_ratio>.*?)\r?$", DefaultOptions),
["Frame limit:"] = new("Frame limit: (?<frame_limit>.*?)\r?$", DefaultOptions),
["MSAA:"] = new("MSAA: (?<msaa>.*?)\r?$", DefaultOptions),
["Write Color Buffers:"] = new("Write Color Buffers: (?<write_color_buffers>.*?)\r?$", DefaultOptions),
["Write Depth Buffer:"] = new("Write Depth Buffer: (?<write_depth_buffer>.*?)\r?$", DefaultOptions),
["Read Color Buffers:"] = new("Read Color Buffers: (?<read_color_buffers>.*?)\r?$", DefaultOptions),
["Read Depth Buffer:"] = new("Read Depth Buffer: (?<read_depth_buffer>.*?)\r?$", DefaultOptions),
["VSync:"] = new("VSync: (?<vsync>.*?)\r?$", DefaultOptions),
["GPU texture scaling:"] = new("Use GPU texture scaling: (?<gpu_texture_scaling>.*?)\r?$", DefaultOptions),
["Stretch To Display Area:"] = new("Stretch To Display Area: (?<stretch_to_display>.*?)\r?$", DefaultOptions),
["Strict Rendering Mode:"] = new("Strict Rendering Mode: (?<strict_rendering_mode>.*?)\r?$", DefaultOptions),
["Occlusion Queries:"] = new("Occlusion Queries: (?<zcull>.*?)\r?$", DefaultOptions),
["Vertex Cache:"] = new("Disable Vertex Cache: (?<vertex_cache>.*?)\r?$", DefaultOptions),
["Frame Skip:"] = new("Enable Frame Skip: (?<frame_skip>.*?)\r?$", DefaultOptions),
["Blit:"] = new("Blit: (?<cpu_blit>.*?)\r?$", DefaultOptions),
["Disable Asynchronous Shader Compiler:"] = new("Disable Asynchronous Shader Compiler: (?<disable_async_shaders>.*?)\r?$", DefaultOptions),
["Shader Mode:"] = new("Shader Mode: (?<shader_mode>.*?)\r?$", DefaultOptions),
["Disable native float16 support:"] = new("Disable native float16 support: (?<disable_native_float16>.*?)\r?$", DefaultOptions),
["Multithreaded RSX:"] = new("Multithreaded RSX: (?<mtrsx>.*?)\r?$", DefaultOptions),
["Relaxed ZCULL Sync:"] = new("Relaxed ZCULL Sync: (?<relaxed_zcull>.*?)\r?$", DefaultOptions),
["Resolution Scale:"] = new("Resolution Scale: (?<resolution_scale>.*?)\r?$", DefaultOptions),
["Anisotropic Filter"] = new("Anisotropic Filter Override: (?<af_override>.*?)\r?$", DefaultOptions),
["Scalable Dimension:"] = new("Minimum Scalable Dimension: (?<texture_scale_threshold>.*?)\r?$", DefaultOptions),
["Driver Recovery Timeout:"] = new("Driver Recovery Timeout: (?<driver_recovery_timeout>.*?)\r?$", DefaultOptions),
["Driver Wake-Up Delay:"] = new("Driver Wake-Up Delay: (?<driver_wakeup_delay>.*?)\r?$", DefaultOptions),
["Vblank Rate:"] = new("Vblank Rate: (?<vblank_rate>.*?)\r?$", DefaultOptions),
["12:"] = new(@"(D3D12|DirectX 12):\s*\r?\n\s*Adapter: (?<d3d_gpu>.*?)\r?$", DefaultOptions),
["Vulkan:"] = new(@"Vulkan:\s*\r?\n\s*Adapter: (?<vulkan_gpu>.*?)\r?$", DefaultOptions),
["Force FIFO present mode:"] = new(@"Force FIFO present mode: (?<force_fifo_present>.*?)\r?$", DefaultOptions),
["Asynchronous Texture Streaming"] = new(@"Asynchronous Texture Streaming( 2)?: (?<async_texture_streaming>.*?)\r?$", DefaultOptions),
["Asynchronous Queue Scheduler:"] = new(@"Asynchronous Queue Scheduler: (?<async_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: (?<audio_backend>.*?)\r?$", DefaultOptions),
["Downmix to Stereo:"] = new("Downmix to Stereo: (?<audio_stereo>.*?)\r?$", DefaultOptions),
["Master Volume:"] = new("Master Volume: (?<audio_volume>.*?)\r?$", DefaultOptions),
["Enable Buffering:"] = new("Enable Buffering: (?<audio_buffering>.*?)\r?$", DefaultOptions),
["Desired Audio Buffer Duration:"] = new("Desired Audio Buffer Duration: (?<audio_buffer_duration>.*?)\r?$", DefaultOptions),
["Enable Time Stretching:"] = new("Enable Time Stretching: (?<audio_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: (?<pad_handler>.*?)\r?$", DefaultOptions),
["Pad:"] = GamepadType(),
["Automatically start games after boot:"] = new("Automatically start games after boot: (?<auto_start_on_boot>.*?)\r?$", DefaultOptions),
["Always start after boot:"] = new("Always start after boot: (?<always_start_on_boot>.*?)\r?$", DefaultOptions),
["Use native user interface:"] = new("Use native user interface: (?<native_ui>.*?)\r?$", DefaultOptions),
["Silence All Logs:"] = new("Silence All Logs: (?<disable_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*(\{(?<log_disabled_channels>.*?)\}|(?<log_disabled_channels_multiline>(\s+\w+\:\s*\w+\r?\n)+))\r?$", DefaultOptions),
["Log:"] = LogChannelList(),
},
EndTrigger = new[] {"·"},
EndTrigger = ["·"],
OnSectionEnd = MarkAsComplete,
},
new()
{
Extractors = new()
{
["LDR: Game:"] = new(@"Game: (?<ldr_game_full>.*(?<ldr_game>/dev_hdd0/game/(?<ldr_game_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["LDR: Disc"] = new(@"Disc( path)?: (?<ldr_disc_full>.*(?<ldr_disc>/dev_hdd0/game/(?<ldr_disc_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["LDR: Path:"] = new(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["LDR: Boot path:"] = new(@"Boot path: (?<ldr_boot_path_full>.*(?<ldr_boot_path>/dev_hdd0/game/(?<ldr_boot_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["SYS: Game:"] = new(@"Game: (?<ldr_game_full>.*(?<ldr_game>/dev_hdd0/game/(?<ldr_game_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["SYS: Path:"] = new(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["SYS: Boot path:"] = new(@"Boot path: (?<ldr_boot_path_full>.*(?<ldr_boot_path>/dev_hdd0/game/(?<ldr_boot_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions),
["Elf path:"] = new(@"Elf path: (?<host_root_in_boot>/host_root/)?(?<elf_boot_path_full>(?<elf_boot_path>/dev_hdd0/game/(?<elf_boot_path_serial>[^/\r\n]+)/USRDIR/EBOOT\.BIN|.*?))\r?$", DefaultOptions),
["VFS: Mounted path \"/dev_bdvd\""] = new(@"Mounted path ""/dev_bdvd"" to ""(?<mounted_dev_bdvd>[^""]+)""", DefaultOptions),
["Invalid or unsupported file format:"] = new(@"Invalid or unsupported file format: (?<failed_to_boot>.*?)\r?$", DefaultOptions),
["SELF:"] = new(@"(?<failed_to_decrypt>Failed to decrypt)? SELF: (?<failed_to_decrypt>Failed to (decrypt|load SELF))?.*\r?$", DefaultOptions),
["SYS: Version:"] = new(@"Version: (APP_VER=)?(?<disc_app_version>\S+) (/ |VERSION=)(?<disc_package_version>\S+).*?\r?$", DefaultOptions),
["sceNp: npDrmIsAvailable(): Failed to verify"] = new(@"Failed to verify (?<failed_to_verify_npdrm>(sce|npd)) file.*\r?$", DefaultOptions),
["{rsx::thread} RSX: 4"] = new(@"RSX:(\d|\.|\s|\w|-)* (?<driver_version>(\d+\.)*\d+)\r?\n[^\n]*?" +
@"RSX: [^\n]+\r?\n[^\n]*?" +
@"RSX: (?<driver_manuf>.*?)\r?\n[^\n]*?" +
@"RSX: Supported texel buffer size", DefaultOptions),
["GL RENDERER:"] = new(@"GL RENDERER: (?<driver_manuf_new>.*?)\r?$", DefaultOptions),
["GL VERSION:"] = new(@"GL VERSION: (?<opengl_version>(\d|\.)+)(\d|\.|\s|\w|-)*?( (?<driver_version_new>(\d+\.)*\d+))?\r?$", DefaultOptions),
["GLSL VERSION:"] = new(@"GLSL VERSION: (?<glsl_version>(\d|\.)+).*?\r?$", DefaultOptions),
["texel buffer size reported:"] = new(@"RSX: Supported texel buffer size reported: (?<texel_buffer_size_new>\d*?) bytes", DefaultOptions),
["Physical device in"] = new(@"Physical device ini?tialized\. GPU=(?<vulkan_gpu>.+), driver=(?<vulkan_driver_version_raw>-?\d+)\r?$", DefaultOptions),
["Found vulkan-compatible GPU:"] = new(@"Found [Vv]ulkan-compatible GPU: (?<vulkan_found_device>.+)\r?$", DefaultOptions),
["Renderer initialized on device"] = new(@"Renderer initialized on device '(?<vulkan_initialized_device>.+)'\r?$", DefaultOptions),
["RSX: Failed to compile shader"] = new(@"RSX: Failed to compile shader: ERROR: (?<shader_compile_error>.+?)\r?$", DefaultOptions),
["RSX: Compilation failed"] = new(@"RSX: Compilation failed: ERROR: (?<shader_compile_error>.+?)\r?$", DefaultOptions),
["RSX: Linkage failed"] = new(@"RSX: Linkage failed: (?<shader_compile_error>.+?)\r?$", DefaultOptions),
["RSX: Unsupported device"] = new(@"RSX: Unsupported device: (?<rsx_unsupported_gpu>.+)\..+?\r?$", DefaultOptions),
["RSX: Your GPU does not support"] = new(@"RSX: Your GPU does not support (?<rsx_not_supported_feature>.+)\..+?\r?$", DefaultOptions),
["RSX: GPU/driver lacks support"] = new(@"RSX: GPU/driver lacks support for (?<rsx_not_supported_feature>.+)\..+?\r?$", DefaultOptions),
["RSX: Swapchain:"] = new(@"RSX: Swapchain: present mode (?<rsx_swapchain_mode>\d+?) in use.+?\r?$", DefaultOptions),
["F "] = new(@"F \d+:\d+:\d+\.\d+ (({(?<fatal_error_context>[^}]+)} )?(\w+:\s*(Thread terminated due to fatal error: )?|(\w+:\s*)?(class [^\r\n]+ thrown: ))\r?\n?)(?<fatal_error>.*?)(\r?\n)(\r?\n|·|$)", DefaultSingleLineOptions),
["Failed to load RAP file:"] = new(@"Failed to load RAP file: (?<rap_file>.*?\.rap).*\r?$", DefaultOptions),
["Rap file not found:"] = new(@"Rap file not found: “?(?<rap_file>.*?\.rap)”?\r?$", DefaultOptions),
["Pad handler expected but none initialized"] = new(@"(?<native_ui_input>Pad handler expected but none initialized).*?\r?$", DefaultOptions),
["Failed to bind device"] = new(@"Failed to bind device (?<failed_pad>.+) to handler (?<failed_pad_handler>.+).*\r?$", DefaultOptions),
["Input:"] = new(@"Input: (?<pad_handler>.*?) device .+ connected\r?$", DefaultOptions),
["XAudio2Thread"] = new(@"XAudio2Thread\s*: (?<xaudio_init_error>.+failed\s*\((?<xaudio_error_code>0x.+)\).*)\r?$", DefaultOptions),
["cellAudio Thread"] = new(@"XAudio2Backend\s*: (?<xaudio_init_error>.+failed\s*\((?<xaudio_error_code>0x.+)\).*)\r?$", DefaultOptions),
["using a Null renderer instead"] = new(@"Audio renderer (?<audio_backend_init_error>.+) could not be initialized\r?$", DefaultOptions),
["PPU executable hash:"] = new(@"PPU executable hash: PPU-(?<ppu_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions),
["OVL executable hash:"] = new(@"OVL executable hash: OVL-(?<ovl_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions),
["SPU executable hash:"] = new(@"SPU executable hash: SPU-(?<spu_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions),
["PRX library hash:"] = new(@"PRX library hash: PRX-(?<prx_patch>\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions),
["OVL hash of"] = new(@"OVL hash of (\w|[\.\[\]])+: OVL-(?<ovl_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions),
["PRX hash of"] = new(@"PRX hash of (\w|[\.\[\]])+: PRX-(?<prx_patch>\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions),
[": Applied patch"] = new(@"Applied patch \(hash='(?:\w{3}-\w+(-\d+)?)', description='(?<patch_desc>.+?)', author='(?:.+?)', patch_version='(?:.+?)', file_version='(?:.+?)'\) \(<- (?:[1-9]\d*)\).*\r?$", DefaultOptions),
["Loaded SPU image:"] = new(@"Loaded SPU image: SPU-(?<spu_patch>\w+ \(<-\s*\d+\)).*?\r?$", DefaultOptions),
["'sys_fs_stat' failed"] = new(@"'sys_fs_stat' failed (?!with 0x8001002c).+“(/dev_bdvd/(?<broken_filename_or_dir>.+)|/dev_hdd0/game/NP\w+/(?<broken_digital_filename>.+))”.*?\r?$", DefaultOptions),
["'sys_fs_open' failed"] = new(@"'sys_fs_open' failed (?!with 0x8001002c).+“(/dev_bdvd/(?<broken_filename>.+)|/dev_hdd0/game/NP\w+/(?<broken_digital_filename>.+))”.*?\r?$", DefaultOptions),
["'sys_fs_opendir' failed"] = new(@"'sys_fs_opendir' failed .+“/dev_bdvd/(?<broken_directory>.+)”.*?\r?$", DefaultOptions),
["EDAT: "] = new(@"EDAT: Block at offset (?<edat_block_offset>0x[0-9a-f]+) has invalid hash!.*?\r?$", DefaultOptions),
["PS3 firmware is not installed"] = new(@"(?<fw_missing_msg>PS3 firmware is not installed.+)\r?$", DefaultOptions),
["do you have the PS3 firmware installed"] = new(@"(?<fw_missing_something>do you have the PS3 firmware installed.*)\r?$", DefaultOptions),
["Unimplemented syscall"] = new(@"U \d+:\d+:\d+\.\d+ ({(?<unimplemented_syscall_context>.+?)} )?.*Unimplemented syscall (?<unimplemented_syscall>.*)\r?$", DefaultOptions),
["Could not enqueue"] = new(@"cellAudio: Could not enqueue buffer onto audio backend(?<enqueue_buffer_error>.).*\r?$", DefaultOptions),
["{PPU["] = new(@"{PPU\[.+\]} (?<log_channel>[^ :]+)( TODO)?: (?!“)(?<syscall_name>[^ :]+?)\(.*\r?$", DefaultOptions),
["Verification failed"] = new(@"Verification failed.+\(e=0x(?<verification_error_hex>[0-9a-f]+)\[(?<verification_error>\d+)\]\)", DefaultOptions),
["sys_tty_write():"] = new(@"sys_tty_write\(\)\: “(?<tty_line>.*?)”\r?(\n|$)", DefaultSingleLineOptions),
["⁂"] = new(@"⁂ (?<syscall_name>[^ :\[]+?) .*\r?$", DefaultOptions),
["undub"] = new(@"(\b|_)(?<game_mod>(undub|translation patch))(\b|_)", DefaultOptions | RegexOptions.IgnoreCase),
["Input: Pad"] = new(@"Input: Pad (?<pad_id>\d): device='(?<pad_controller_name>(?!Null).+?)', handler=(?<pad_handler>.+?), VID=.+?\r?$", DefaultOptions),
["SDL: Found game controller"] = new(@"Found game controller \d: .+ has_accel=(?<pad_has_accel>.+?), has_gyro=(?<pad_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<string> MultiValueItems = new()
{
private static readonly HashSet<string> 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 @@ internal partial class LogParser
state.WipMultiValueCollection[key] = collection;
}
}
state.WipCollection = new();
state.WipCollection = [];
state.WipMultiValueCollection = new();
Copy(
"build_and_specs", "fw_version_installed",

View File

@@ -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<LogParseState> TaskScheduler = new();
public static async Task<LogParseState> ReadPipeAsync(PipeReader reader, CancellationToken cancellationToken)

View File

@@ -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*(?<build_and_specs>RPCS3 [^\xC2\xB7]+?)\r?(\n·|$)", DefaultSingleLine)]
private static partial Regex Rpcs3LogHeader();
[GeneratedRegex(@"(?<first_unicode_dot>·).+\r?$", DefaultOptions)]
private static partial Regex FirstLineWithDot();
[GeneratedRegex(@"Operating system: (?<os_type>[^,]+), (Name: (?<posix_name>[^,]+), Release: (?<posix_release>[^,]+), Version: (?<posix_version>[^\r\n]+)|Major: (?<os_version_major>\d+), Minor: (?<os_version_minor>\d+), Build: (?<os_version_build>\d+), Service Pack: (?<os_service_pack>[^,]+), Compatibility mode: (?<os_compat_mode>[^,\r\n]+)|Version: (?<macos_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: (?<log_start_timestamp>.+)\r?$", DefaultOptions)]
private static partial Regex CurrentTime();
[GeneratedRegex(@"Installation ID: (?<hw_id>.+)\r?$", DefaultOptions)]
private static partial Regex InstallationId();
[GeneratedRegex(@"Physical device ini?tialized\. GPU=(?<vulkan_gpu>.+), driver=(?<vulkan_driver_version_raw>-?\d+)\r?$", DefaultOptions)]
private static partial Regex PhysicalDeviceName();
[GeneratedRegex(@"Found [Vv]ulkan-compatible GPU: (?<vulkan_found_device>'(?<vulkan_compatible_device_name>.+)' running.+)\r?$", DefaultOptions)]
private static partial Regex VulkanDeviceName();
[GeneratedRegex(@"Finished reading database from file: (?<compat_database_path>.*compat_database.dat).*\r?$", DefaultOptions)]
private static partial Regex CompatDbFoundPath();
[GeneratedRegex(@"Database file not found: (?<compat_database_path>.*compat_database.dat).*\r?$", DefaultOptions)]
private static partial Regex CompatDbNotFoundPath();
[GeneratedRegex(@"(?<fw_installed_message>Successfully installed PS3 firmware) version (?<fw_version_installed>\d+\.\d+).*\r?$", DefaultOptions)]
private static partial Regex FwInstallMessage();
[GeneratedRegex(@"Firmware version: (?<fw_version_installed>\d+\.\d+).*\r?$", DefaultOptions)]
private static partial Regex FwVersion();
[GeneratedRegex(@"(?:LDR|SYS): Title: (?<game_title>.*)?\r?$", DefaultOptions)]
private static partial Regex GameTitle();
[GeneratedRegex(@"Serial: (?<serial>[A-z]{4}\d{5})\r?$", DefaultOptions)]
private static partial Regex GameSerial();
[GeneratedRegex(@"Category: (?<game_category>.*)?\r?$", DefaultOptions)]
private static partial Regex GameCategory();
[GeneratedRegex(@"Version: (?<disc_app_version>\S+) / (?<disc_package_version>\S+).*?\r?$", DefaultOptions)]
private static partial Regex DiscVersionLdr();
[GeneratedRegex(@"Version: (APP_VER=)?(?<disc_app_version>\S+) (/ |VERSION=)(?<disc_package_version>\S+).*?\r?$", DefaultOptions)]
private static partial Regex DiscVersionSys();
[GeneratedRegex(@"Cache: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions)]
private static partial Regex CachePathLdr();
[GeneratedRegex(@"Cache: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions)]
private static partial Regex CachePathSys();
[GeneratedRegex(@"Path: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions)]
private static partial Regex BootPathLdr();
[GeneratedRegex(@"Path: ((?<win_path>\w:/)|(?<lin_path>/[^/])).*?\r?$", DefaultOptions)]
private static partial Regex BootPathSys();
[GeneratedRegex(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex BootPathDigitalLdr();
[GeneratedRegex(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex BootPathDigitalSys();
[GeneratedRegex(@"custom config: (?<custom_config>.*?)\r?$", DefaultOptions)]
private static partial Regex CustomConfigPath();
[GeneratedRegex(@"patch_log: Failed to load patch file (?<patch_error_file>\S*)\r?\n.* line (?<patch_error_line>\d+), column (?<patch_error_column>\d+): (?<patch_error_text>.*?)$", DefaultOptions)]
private static partial Regex FailedPatchPath();
[GeneratedRegex(@"PPU Decoder: (?<ppu_decoder>.*?)\r?$", DefaultOptions)]
private static partial Regex PpuDecoderType();
[GeneratedRegex(@"PPU Threads: (?<ppu_threads>.*?)\r?$", DefaultOptions)]
private static partial Regex PpuThreadCount();
[GeneratedRegex("Use LLVM CPU: \\\"?(?<llvm_arch>.*?)\\\"?\r?$", DefaultOptions)]
private static partial Regex LlvmCpuArch();
[GeneratedRegex(@"[Ss]cheduler( Mode)?: (?<thread_scheduler>.*?)\r?$", DefaultOptions)]
private static partial Regex ThreadSchedulerMode();
[GeneratedRegex(@"SPU Decoder: (?<spu_decoder>.*?)\r?$", DefaultOptions)]
private static partial Regex SpuDecoderType();
[GeneratedRegex(@"secondary cores: (?<spu_secondary_cores>.*?)\r?$", DefaultOptions)]
private static partial Regex SecondaryCores();
[GeneratedRegex(@"priority: (?<spu_lower_thread_priority>.*?)\r?$", DefaultOptions)]
private static partial Regex LowerThreadPriority();
[GeneratedRegex(@"SPU Threads: (?<spu_threads>.*?)\r?$", DefaultOptions)]
private static partial Regex SpuThreadCount();
[GeneratedRegex(@"SPU delay penalty: (?<spu_delay_penalty>.*?)\r?$", DefaultOptions)]
private static partial Regex SpuDelayPenalty();
[GeneratedRegex(@"SPU loop detection: (?<spu_loop_detection>.*?)\r?$", DefaultOptions)]
private static partial Regex SpuLoopDetection();
[GeneratedRegex(@"Max SPURS Threads: (?<spurs_threads>\d*?)\r?$", DefaultOptions)]
private static partial Regex SpursThreadCount();
[GeneratedRegex(@"SPU Block Size: (?<spu_block_size>.*?)\r?$", DefaultOptions)]
private static partial Regex SpuBlockSize();
[GeneratedRegex(@"Enable TSX: (?<enable_tsx>.*?)\r?$", DefaultOptions)]
private static partial Regex TsxMode();
[GeneratedRegex(@"Accurate xfloat: (?<accurate_xfloat>.*?)\r?$", DefaultOptions)]
private static partial Regex AccurateXfloat();
[GeneratedRegex(@"Approximate xfloat: (?<approximate_xfloat>.*?)\r?$", DefaultOptions)]
private static partial Regex ApproximateXfloat();
[GeneratedRegex(@"Relaxed xfloat: (?<relaxed_xfloat>.*?)\r?$", DefaultOptions)]
private static partial Regex RelaxedXfloat();
[GeneratedRegex(@"XFloat Accuracy: (?<xfloat_mode>.*?)\r?$", DefaultOptions)]
private static partial Regex XfloatMode();
[GeneratedRegex(@"Accurate GETLLAR: (?<accurate_getllar>.*?)\r?$", DefaultOptions)]
private static partial Regex GetLlarMode();
[GeneratedRegex(@"Accurate PUTLLUC: (?<accurate_putlluc>.*?)\r?$", DefaultOptions)]
private static partial Regex PutLlucMode();
[GeneratedRegex(@"Accurate RSX reservation access: (?<accurate_rsx_reservation>.*?)\r?$", DefaultOptions)]
private static partial Regex RsxReservationAccessMode();
[GeneratedRegex(@"RSX FIFO Accuracy: (?<rsx_fifo_mode>.*?)\r?$", DefaultOptions)]
private static partial Regex RsxFifoMode();
[GeneratedRegex(@"Debug Console Mode: (?<debug_console_mode>.*?)\r?$", DefaultOptions)]
private static partial Regex DebugConsoleMode();
[GeneratedRegex(@"[Ll]oader: (?<lib_loader>.*?)\r?$", DefaultOptions)]
private static partial Regex LibLoaderMode();
[GeneratedRegex(@"Hook static functions: (?<hook_static_functions>.*?)\r?$", DefaultOptions)]
private static partial Regex HookStaticFunctions();
[GeneratedRegex(@"libraries:\r?\n(?<library_list>(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions)]
private static partial Regex LoadLibrariesList();
[GeneratedRegex(@"Libraries Control:\r?\n(?<library_list>(.*?(- .*?|\[\])\r?\n)+)", DefaultOptions)]
private static partial Regex LibrariesControlList();
[GeneratedRegex(@"HLE lwmutex: (?<hle_lwmutex>.*?)\r?$", DefaultOptions)]
private static partial Regex HleLwmutex();
[GeneratedRegex(@"Clocks scale: (?<clock_scale>.*?)\r?$", DefaultOptions)]
private static partial Regex ClockScale();
[GeneratedRegex(@"Max CPU Preempt Count: (?<cpu_preempt_count>.*?)\r?$", DefaultOptions)]
private static partial Regex CpuPreemptCount();
[GeneratedRegex(@"Sleep Timers Accuracy: (?<sleep_timer>.*?)\r?$", DefaultOptions)]
private static partial Regex SleepTimersMode();
[GeneratedRegex(@"Enable /host_root/: (?<host_root>.*?)\r?$", DefaultOptions)]
private static partial Regex EnableHostRoot();
[GeneratedRegex("Renderer: (?<renderer>.*?)\r?$", DefaultOptions)]
private static partial Regex RendererBackend();
[GeneratedRegex("Resolution: (?<resolution>.*?)\r?$", DefaultOptions)]
private static partial Regex ResolutionMode();
[GeneratedRegex("Aspect ratio: (?<aspect_ratio>.*?)\r?$", DefaultOptions)]
private static partial Regex AspectRatioMode();
[GeneratedRegex("Frame limit: (?<frame_limit>.*?)\r?$", DefaultOptions)]
private static partial Regex FrameLimit();
[GeneratedRegex("MSAA: (?<msaa>.*?)\r?$", DefaultOptions)]
private static partial Regex MsaaMode();
[GeneratedRegex("Write Color Buffers: (?<write_color_buffers>.*?)\r?$", DefaultOptions)]
private static partial Regex Wcb();
[GeneratedRegex("Write Depth Buffer: (?<write_depth_buffer>.*?)\r?$", DefaultOptions)]
private static partial Regex Wdb();
[GeneratedRegex("Read Color Buffers: (?<read_color_buffers>.*?)\r?$", DefaultOptions)]
private static partial Regex Rcb();
[GeneratedRegex("Read Depth Buffer: (?<read_depth_buffer>.*?)\r?$", DefaultOptions)]
private static partial Regex Rdb();
[GeneratedRegex("VSync: (?<vsync>.*?)\r?$", DefaultOptions)]
private static partial Regex VsyncMode();
[GeneratedRegex("Use GPU texture scaling: (?<gpu_texture_scaling>.*?)\r?$", DefaultOptions)]
private static partial Regex GpuTextureScaling();
[GeneratedRegex("Stretch To Display Area: (?<stretch_to_display>.*?)\r?$", DefaultOptions)]
private static partial Regex StretchToDisplay();
[GeneratedRegex("Strict Rendering Mode: (?<strict_rendering_mode>.*?)\r?$", DefaultOptions)]
private static partial Regex StrictRendering();
[GeneratedRegex("Occlusion Queries: (?<zcull>.*?)\r?$", DefaultOptions)]
private static partial Regex OcclusionQueriesMode();
[GeneratedRegex("Disable Vertex Cache: (?<vertex_cache>.*?)\r?$", DefaultOptions)]
private static partial Regex VertexCache();
[GeneratedRegex("Enable Frame Skip: (?<frame_skip>.*?)\r?$", DefaultOptions)]
private static partial Regex FrameSkip();
[GeneratedRegex("Blit: (?<cpu_blit>.*?)\r?$", DefaultOptions)]
private static partial Regex BlitMode();
[GeneratedRegex("Disable Asynchronous Shader Compiler: (?<disable_async_shaders>.*?)\r?$", DefaultOptions)]
private static partial Regex DisableAsyncShaders();
[GeneratedRegex("Shader Mode: (?<shader_mode>.*?)\r?$", DefaultOptions)]
private static partial Regex ShaderMode();
[GeneratedRegex("Disable native float16 support: (?<disable_native_float16>.*?)\r?$", DefaultOptions)]
private static partial Regex DisableNativeF16();
[GeneratedRegex("Multithreaded RSX: (?<mtrsx>.*?)\r?$", DefaultOptions)]
private static partial Regex RsxMultithreadMode();
[GeneratedRegex("Relaxed ZCULL Sync: (?<relaxed_zcull>.*?)\r?$", DefaultOptions)]
private static partial Regex RelaxedZcull();
[GeneratedRegex("Resolution Scale: (?<resolution_scale>.*?)\r?$", DefaultOptions)]
private static partial Regex ResolutionScaling();
[GeneratedRegex("Anisotropic Filter Override: (?<af_override>.*?)\r?$", DefaultOptions)]
private static partial Regex AnisoFilter();
[GeneratedRegex("Minimum Scalable Dimension: (?<texture_scale_threshold>.*?)\r?$", DefaultOptions)]
private static partial Regex ScalableDimensions();
[GeneratedRegex("Driver Recovery Timeout: (?<driver_recovery_timeout>.*?)\r?$", DefaultOptions)]
private static partial Regex DriverRecoveryTimeout();
[GeneratedRegex("Driver Wake-Up Delay: (?<driver_wakeup_delay>.*?)\r?$", DefaultOptions)]
private static partial Regex DriverWakeupDelay();
[GeneratedRegex("Vblank Rate: (?<vblank_rate>.*?)\r?$", DefaultOptions)]
private static partial Regex VblankRate();
[GeneratedRegex(@"(D3D12|DirectX 12):\s*\r?\n\s*Adapter: (?<d3d_gpu>.*?)\r?$", DefaultOptions)]
private static partial Regex SelectedD3d12Device();
[GeneratedRegex(@"Vulkan:\s*\r?\n\s*Adapter: (?<vulkan_gpu>.*?)\r?$", DefaultOptions)]
private static partial Regex SelectedVulkanDevice();
[GeneratedRegex(@"Force FIFO present mode: (?<force_fifo_present>.*?)\r?$", DefaultOptions)]
private static partial Regex FifoPresentMode();
[GeneratedRegex(@"Asynchronous Texture Streaming( 2)?: (?<async_texture_streaming>.*?)\r?$", DefaultOptions)]
private static partial Regex AsyncTextureStreaming();
[GeneratedRegex(@"Asynchronous Queue Scheduler: (?<async_queue_scheduler>.*?)\r?$", DefaultOptions)]
private static partial Regex AsyncQueueScheduler();
[GeneratedRegex("Renderer: (?<audio_backend>.*?)\r?$", DefaultOptions)]
private static partial Regex AudioBackend();
[GeneratedRegex("Downmix to Stereo: (?<audio_stereo>.*?)\r?$", DefaultOptions)]
private static partial Regex DownmixToStereo();
[GeneratedRegex("Master Volume: (?<audio_volume>.*?)\r?$", DefaultOptions)]
private static partial Regex MasterVolume();
[GeneratedRegex("Enable Buffering: (?<audio_buffering>.*?)\r?$", DefaultOptions)]
private static partial Regex AudioBuffering();
[GeneratedRegex("Desired Audio Buffer Duration: (?<audio_buffer_duration>.*?)\r?$", DefaultOptions)]
private static partial Regex AudioBufferLength();
[GeneratedRegex("Enable Time Stretching: (?<audio_stretching>.*?)\r?$", DefaultOptions)]
private static partial Regex AudioTimeStretching();
[GeneratedRegex("Pad: (?<pad_handler>.*?)\r?$", DefaultOptions)]
private static partial Regex GamepadType();
[GeneratedRegex("Automatically start games after boot: (?<auto_start_on_boot>.*?)\r?$", DefaultOptions)]
private static partial Regex AutoStartAfterBoot();
[GeneratedRegex("Always start after boot: (?<always_start_on_boot>.*?)\r?$", DefaultOptions)]
private static partial Regex AlwaysStartAfterBoot();
[GeneratedRegex("Use native user interface: (?<native_ui>.*?)\r?$", DefaultOptions)]
private static partial Regex NativeUIMode();
[GeneratedRegex("Silence All Logs: (?<disable_logs>.*?)\r?$", DefaultOptions)]
private static partial Regex SilenceAllLogs();
[GeneratedRegex(@"Log:\s*\r?\n?\s*(\{(?<log_disabled_channels>.*?)\}|(?<log_disabled_channels_multiline>(\s+\w+\:\s*\w+\r?\n)+))\r?$", DefaultOptions)]
private static partial Regex LogChannelList();
[GeneratedRegex(@"Game: (?<ldr_game_full>.*(?<ldr_game>/dev_hdd0/game/(?<ldr_game_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex GamePathLdr();
[GeneratedRegex(@"Disc( path)?: (?<ldr_disc_full>.*(?<ldr_disc>/dev_hdd0/game/(?<ldr_disc_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex DiscPathLdr();
[GeneratedRegex(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex DigitalPathLdr();
[GeneratedRegex(@"Boot path: (?<ldr_boot_path_full>.*(?<ldr_boot_path>/dev_hdd0/game/(?<ldr_boot_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex BootPathInBodyLdr();
[GeneratedRegex(@"Game: (?<ldr_game_full>.*(?<ldr_game>/dev_hdd0/game/(?<ldr_game_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex GamePathSys();
[GeneratedRegex(@"Path: (?<ldr_path_full>.*(?<ldr_path>/dev_hdd0/game/(?<ldr_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex DigitalPathSys();
[GeneratedRegex(@"Boot path: (?<ldr_boot_path_full>.*(?<ldr_boot_path>/dev_hdd0/game/(?<ldr_boot_path_serial>[^/\r\n]+)).*|.*)\r?$", DefaultOptions)]
private static partial Regex BootPathInBodySys();
[GeneratedRegex(@"Elf path: (?<host_root_in_boot>/host_root/)?(?<elf_boot_path_full>(?<elf_boot_path>/dev_hdd0/game/(?<elf_boot_path_serial>[^/\r\n]+)/USRDIR/EBOOT\.BIN|.*?))\r?$", DefaultOptions)]
private static partial Regex ElfPath();
[GeneratedRegex(@"Mounted path ""/dev_bdvd"" to ""(?<mounted_dev_bdvd>[^""]+)""", DefaultOptions)]
private static partial Regex VfsMountPath();
[GeneratedRegex(@"Invalid or unsupported file format: (?<failed_to_boot>.*?)\r?$", DefaultOptions)]
private static partial Regex InvalidFileFormat();
[GeneratedRegex(@"(?<failed_to_decrypt>Failed to decrypt)? SELF: (?<failed_to_decrypt>Failed to (decrypt|load SELF))?.*\r?$", DefaultOptions)]
private static partial Regex DecryptFailedSelfPath();
[GeneratedRegex(@"Version: (APP_VER=)?(?<disc_app_version>\S+) (/ |VERSION=)(?<disc_package_version>\S+).*?\r?$", DefaultOptions)]
private static partial Regex GameVersion();
[GeneratedRegex(@"Failed to verify (?<failed_to_verify_npdrm>(sce|npd)) file.*\r?$", DefaultOptions)]
private static partial Regex FailedToVerifyNpDrm();
[GeneratedRegex(
@"RSX:(\d|\.|\s|\w|-)* (?<driver_version>(\d+\.)*\d+)\r?\n[^\n]*?"+
@"RSX: [^\n]+\r?\n[^\n]*?RSX: (?<driver_manuf>.*?)\r?\n[^\n]*?"+
@"RSX: Supported texel buffer size",
DefaultOptions
)]
private static partial Regex RsxDriverInfoLegacy();
[GeneratedRegex(@"GL RENDERER: (?<driver_manuf_new>.*?)\r?$", DefaultOptions)]
private static partial Regex GlRenderer();
[GeneratedRegex(@"GL VERSION: (?<opengl_version>(\d|\.)+)(\d|\.|\s|\w|-)*?( (?<driver_version_new>(\d+\.)*\d+))?\r?$", DefaultOptions)]
private static partial Regex GlVersion();
[GeneratedRegex(@"GLSL VERSION: (?<glsl_version>(\d|\.)+).*?\r?$", DefaultOptions)]
private static partial Regex GlslVersion();
[GeneratedRegex(@"RSX: Supported texel buffer size reported: (?<texel_buffer_size_new>\d*?) bytes", DefaultOptions)]
private static partial Regex GlTexelBufferSize();
[GeneratedRegex(@"Physical device ini?tialized\. GPU=(?<vulkan_gpu>.+), driver=(?<vulkan_driver_version_raw>-?\d+)\r?$", DefaultOptions)]
private static partial Regex PhysicalDeviceFound();
[GeneratedRegex(@"Found [Vv]ulkan-compatible GPU: (?<vulkan_found_device>.+)\r?$", DefaultOptions)]
private static partial Regex VulkanDeviceFound();
[GeneratedRegex(@"Renderer initialized on device '(?<vulkan_initialized_device>.+)'\r?$", DefaultOptions)]
private static partial Regex RenderDeviceInitialized();
[GeneratedRegex(@"RSX: Failed to compile shader: ERROR: (?<shader_compile_error>.+?)\r?$", DefaultOptions)]
private static partial Regex FailedToCompileShader();
[GeneratedRegex(@"RSX: Compilation failed: ERROR: (?<shader_compile_error>.+?)\r?$", DefaultOptions)]
private static partial Regex ShaderCompilationFailed();
[GeneratedRegex(@"RSX: Linkage failed: (?<shader_compile_error>.+?)\r?$", DefaultOptions)]
private static partial Regex ShaderLinkageFailed();
[GeneratedRegex(@"RSX: Unsupported device: (?<rsx_unsupported_gpu>.+)\..+?\r?$", DefaultOptions)]
private static partial Regex UnsupportedDevice();
[GeneratedRegex(@"RSX: Your GPU does not support (?<rsx_not_supported_feature>.+)\..+?\r?$", DefaultOptions)]
private static partial Regex UnsupportedDeviceFeatures();
[GeneratedRegex(@"RSX: GPU/driver lacks support for (?<rsx_not_supported_feature>.+)\..+?\r?$", DefaultOptions)]
private static partial Regex UnsupportedDriverFeatures();
[GeneratedRegex(@"RSX: Swapchain: present mode (?<rsx_swapchain_mode>\d+?) in use.+?\r?$", DefaultOptions)]
private static partial Regex SwapchainMode();
[GeneratedRegex(@"F \d+:\d+:\d+\.\d+ (({(?<fatal_error_context>[^}]+)} )?(\w+:\s*(Thread terminated due to fatal error: )?|(\w+:\s*)?(class [^\r\n]+ thrown: ))\r?\n?)(?<fatal_error>.*?)(\r?\n)(\r?\n|·|$)", DefaultSingleLine)]
private static partial Regex FatalError();
[GeneratedRegex(@"Failed to load RAP file: (?<rap_file>.*?\.rap).*\r?$", DefaultOptions)]
private static partial Regex FailedToLoadRap();
[GeneratedRegex(@"Rap file not found: “?(?<rap_file>.*?\.rap)”?\r?$", DefaultOptions)]
private static partial Regex RapNotFound();
[GeneratedRegex(@"(?<native_ui_input>Pad handler expected but none initialized).*?\r?$", DefaultOptions)]
private static partial Regex MissingGamepad();
[GeneratedRegex(@"Failed to bind device (?<failed_pad>.+) to handler (?<failed_pad_handler>.+).*\r?$", DefaultOptions)]
private static partial Regex FailedToBindGamepad();
[GeneratedRegex(@"Input: (?<pad_handler>.*?) device .+ connected\r?$", DefaultOptions)]
private static partial Regex InputDeviceConnected();
[GeneratedRegex(@"XAudio2Thread\s*: (?<xaudio_init_error>.+failed\s*\((?<xaudio_error_code>0x.+)\).*)\r?$", DefaultOptions)]
private static partial Regex XAudio2Thread();
[GeneratedRegex(@"XAudio2Backend\s*: (?<xaudio_init_error>.+failed\s*\((?<xaudio_error_code>0x.+)\).*)\r?$", DefaultOptions)]
private static partial Regex CellAudioThread();
[GeneratedRegex(@"Audio renderer (?<audio_backend_init_error>.+) could not be initialized\r?$", DefaultOptions)]
private static partial Regex AudioBackendFailed();
[GeneratedRegex(@"PPU executable hash: PPU-(?<ppu_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)]
private static partial Regex PpuHash();
[GeneratedRegex(@"OVL executable hash: OVL-(?<ovl_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)]
private static partial Regex OvlHash();
[GeneratedRegex(@"SPU executable hash: SPU-(?<spu_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)]
private static partial Regex SpuHash();
[GeneratedRegex(@"PRX library hash: PRX-(?<prx_patch>\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)]
private static partial Regex PrxHash();
[GeneratedRegex(@"OVL hash of (\w|[\.\[\]])+: OVL-(?<ovl_patch>\w+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)]
private static partial Regex OvlHash2();
[GeneratedRegex(@"PRX hash of (\w|[\.\[\]])+: PRX-(?<prx_patch>\w+-\d+( \(<-\s*\d+\))?).*?\r?$", DefaultOptions)]
private static partial Regex PrxHash2();
[GeneratedRegex(@"Applied patch \(hash='(?:\w{3}-\w+(-\d+)?)', description='(?<patch_desc>.+?)', author='(?:.+?)', patch_version='(?:.+?)', file_version='(?:.+?)'\) \(<- (?:[1-9]\d*)\).*\r?$", DefaultOptions)]
private static partial Regex AppliedPatch();
[GeneratedRegex(@"Loaded SPU image: SPU-(?<spu_patch>\w+ \(<-\s*\d+\)).*?\r?$", DefaultOptions)]
private static partial Regex SpuImageLoad();
[GeneratedRegex(@"'sys_fs_stat' failed (?!with 0x8001002c).+(/dev_bdvd/(?<broken_filename_or_dir>.+)|/dev_hdd0/game/NP\w+/(?<broken_digital_filename>.+)).*?\r?$", DefaultOptions)]
private static partial Regex SysFsStatFailed();
[GeneratedRegex(@"'sys_fs_open' failed (?!with 0x8001002c).+(/dev_bdvd/(?<broken_filename>.+)|/dev_hdd0/game/NP\w+/(?<broken_digital_filename>.+)).*?\r?$", DefaultOptions)]
private static partial Regex SysFsOpenFailed();
[GeneratedRegex(@"'sys_fs_opendir' failed .+/dev_bdvd/(?<broken_directory>.+).*?\r?$", DefaultOptions)]
private static partial Regex SysFsOpenDirFailed();
[GeneratedRegex(@"EDAT: Block at offset (?<edat_block_offset>0x[0-9a-f]+) has invalid hash!.*?\r?$", DefaultOptions)]
private static partial Regex InvalidEdat();
[GeneratedRegex(@"(?<fw_missing_msg>PS3 firmware is not installed.+)\r?$", DefaultOptions)]
private static partial Regex FwNotInstalled();
[GeneratedRegex(@"(?<fw_missing_something>do you have the PS3 firmware installed.*)\r?$", DefaultOptions)]
private static partial Regex FwNotInstalled2();
[GeneratedRegex(@"U \d+:\d+:\d+\.\d+ ({(?<unimplemented_syscall_context>.+?)} )?.*Unimplemented syscall (?<unimplemented_syscall>.*)\r?$", DefaultOptions)]
private static partial Regex UnimplementedSyscall();
[GeneratedRegex(@"cellAudio: Could not enqueue buffer onto audio backend(?<enqueue_buffer_error>.).*\r?$", DefaultOptions)]
private static partial Regex CellAudioEnqueueFailed();
[GeneratedRegex(@"{PPU\[.+\]} (?<log_channel>[^ :]+)( TODO)?: (?!)(?<syscall_name>[^ :]+?)\(.*\r?$", DefaultOptions)]
private static partial Regex PpuSyscallTodo();
[GeneratedRegex(@"Verification failed.+\(e=0x(?<verification_error_hex>[0-9a-f]+)\[(?<verification_error>\d+)\]\)", DefaultOptions)]
private static partial Regex VerificationFailed();
[GeneratedRegex(@"sys_tty_write\(\)\: “(?<tty_line>.*?)”\r?(\n|$)", DefaultSingleLine)]
private static partial Regex SysTtyWrite();
[GeneratedRegex(@"⁂ (?<syscall_name>[^ :\[]+?) .*\r?$", DefaultOptions)]
private static partial Regex SyscallDump();
[GeneratedRegex(@"(\b|_)(?<game_mod>(undub|translation patch))(\b|_)", RegexOptions.IgnoreCase | DefaultOptions)]
private static partial Regex UndubFlag();
[GeneratedRegex(@"Input: Pad (?<pad_id>\d): device='(?<pad_controller_name>(?!Null).+?)', handler=(?<pad_handler>.+?), VID=.+?\r?$", DefaultOptions)]
private static partial Regex InputDeviceGamepad();
[GeneratedRegex(@"Found game controller \d: .+ has_accel=(?<pad_has_accel>.+?), has_gyro=(?<pad_has_gyro>.+?)\r?$", DefaultOptions)]
private static partial Regex SdlControllerName();
}

View File

@@ -13,17 +13,18 @@ using CompatApiClient.Utils;
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(@"(?<dropbox_link>(https?://)?(www\.)?dropbox\.com/s/(?<dropbox_id>[^/\s]+)/(?<filename>[^/\?\s])(/dl=[01])?)", DefaultOptions);
[GeneratedRegex(@"(?<dropbox_link>(https?://)?(www\.)?dropbox\.com/s/(?<dropbox_id>[^/\s]+)/(?<filename>[^/\?\s])(/dl=[01])?)", DefaultOptions)]
private static partial Regex ExternalLink();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);

View File

@@ -12,16 +12,17 @@ using System.Threading;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
internal sealed class GenericLinkHandler : BaseSourceHandler
internal sealed partial class GenericLinkHandler : BaseSourceHandler
{
private static readonly Regex ExternalLink = new(@"(?<link>(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?<filename>[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions);
[GeneratedRegex(@"(?<link>(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?<filename>[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions)]
private static partial Regex ExternalLink();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);

View File

@@ -16,10 +16,11 @@ using FileMeta = Google.Apis.Drive.v3.Data.File;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
internal sealed class GoogleDriveHandler: BaseSourceHandler
internal sealed partial class GoogleDriveHandler: BaseSourceHandler
{
private static readonly Regex ExternalLink = new(@"(?<gdrive_link>(https?://)?drive\.google\.com/(open\?id=|file/d/)(?<gdrive_id>[^/>\s]+))", DefaultOptions);
private static readonly string[] Scopes = { DriveService.Scope.DriveReadonly };
[GeneratedRegex(@"(?<gdrive_link>(https?://)?drive\.google\.com/(open\?id=|file/d/)(?<gdrive_id>[^/>\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<IArchiveHandler> 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);

View File

@@ -13,10 +13,11 @@ using MediafireClient;
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(@"(?<mediafire_link>(https?://)?(www\.)?mediafire\.com/file/(?<quick_key>[^/\s]+)/(?<filename>[^/\?\s]+)(/file)?)", DefaultOptions);
[GeneratedRegex(@"(?<mediafire_link>(https?://)?(www\.)?mediafire\.com/file/(?<quick_key>[^/\s]+)/(?<filename>[^/\?\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<IArchiveHandler> 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);

View File

@@ -11,11 +11,12 @@ using System.Threading;
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(@"(?<mega_link>(https?://)?mega(\.co)?\.nz/(#(?<mega_id>[^/>\s]+)|file/(?<new_mega_id>[^/>\s]+)))", DefaultOptions);
[GeneratedRegex(@"(?<mega_link>(https?://)?mega(\.co)?\.nz/(#(?<mega_id>[^/>\s]+)|file/(?<new_mega_id>[^/>\s]+)))", DefaultOptions)]
private static partial Regex ExternalLink();
private static readonly IProgress<double> Doodad = new Progress<double>(_ => { });
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);

View File

@@ -13,9 +13,10 @@ using OneDriveClient.POCOs;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
internal sealed class OneDriveSourceHandler : BaseSourceHandler
internal sealed partial class OneDriveSourceHandler : BaseSourceHandler
{
private static readonly Regex ExternalLink = new(@"(?<onedrive_link>(https?://)?(1drv\.ms|onedrive\.live\.com)/[^>\s]+)", DefaultOptions);
[GeneratedRegex(@"(?<onedrive_link>(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<IArchiveHandler> 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);

View File

@@ -11,16 +11,17 @@ using System.Threading;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
internal sealed class PastebinHandler : BaseSourceHandler
internal sealed partial class PastebinHandler : BaseSourceHandler
{
private static readonly Regex ExternalLink = new(@"(?<pastebin_link>(https?://)pastebin.com/(raw/)?(?<pastebin_id>[^/>\s]+))", DefaultOptions);
[GeneratedRegex(@"(?<pastebin_link>(https?://)pastebin.com/(raw/)?(?<pastebin_id>[^/>\s]+))", DefaultOptions)]
private static partial Regex ExternalLink();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> 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);

View File

@@ -12,9 +12,10 @@ using YandexDiskClient;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
internal sealed class YandexDiskHandler: BaseSourceHandler
internal sealed partial class YandexDiskHandler: BaseSourceHandler
{
private static readonly Regex ExternalLink = new(@"(?<yadisk_link>(https?://)?(www\.)?yadi\.sk/d/(?<share_key>[^/>\s]+))\b", DefaultOptions);
[GeneratedRegex(@"(?<yadisk_link>(https?://)?(www\.)?yadi\.sk/d/(?<share_key>[^/>\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<IArchiveHandler> 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);

View File

@@ -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);

View File

@@ -11,9 +11,10 @@ using DSharpPlus.EventArgs;
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 @@ internal static class NewBuildsMonitor
&& !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");

View File

@@ -12,10 +12,13 @@ using Microsoft.EntityFrameworkCore;
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((?<vulkan>(vul[ck][ae]n(-?1)?))|(?<help>(post|upload|send|give)(ing)?\s+((a|the|rpcs3('s)?|your|you're|ur|my|full|game)\s+)*\blogs?))\b", DefaultOptions);
[GeneratedRegex(
@"\b((?<vulkan>(vul[ck][ae]n(-?1)?))|(?<help>(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<string, Explanation> DefaultExplanation = new()
@@ -36,7 +39,7 @@ internal static class PostLogHelpHandler
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;

View File

@@ -16,10 +16,11 @@ using DSharpPlus.EventArgs;
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(@"(?<letters>(?:[BPSUVX][CL]|P[ETU]|NP)[AEHJKPUIX][ABDJKLMPQRSTX]|MRTC)[ \-]?(?<numbers>\d{5})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
[GeneratedRegex(@"(?<letters>(?:[BPSUVX][CL]|P[ETU]|NP)[AEHJKPUIX][ABDJKLMPQRSTX]|MRTC)[ \-]?(?<numbers>\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 @@ internal static class ProductCodeLookup
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();

View File

@@ -12,9 +12,11 @@ using Microsoft.Extensions.Caching.Memory;
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 @@ internal static class TableFlipMonitor
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("🎲");

View File

@@ -13,12 +13,13 @@ using DSharpPlus.Interactivity.Extensions;
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 @@ internal static class UnknownCommandHandler
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(

View File

@@ -15,10 +15,10 @@ namespace CompatBot.EventHandlers;
public static class UsernameZalgoMonitor
{
private static readonly HashSet<char> OversizedChars = new()
{
private static readonly HashSet<char> OversizedChars =
[
'꧁', '꧂', '⎝', '⎠', '', '', '⎛', '⎞', '﷽', '⸻', 'ဪ', '꧅', '꧄', '˞',
};
];
public static async Task OnUserUpdated(DiscordClient c, UserUpdateEventArgs args)
{

View File

@@ -4,6 +4,7 @@ using System.IO;
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 class Program
{
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")
{

View File

@@ -16,14 +16,15 @@ using Microsoft.EntityFrameworkCore;
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(
@"(?<cover_link>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<string> PreferredOrder = new List<string>{"coverHQ", "coverM", "cover"};
public static async Task RunAsync(CancellationToken cancellationToken)
@@ -50,7 +51,7 @@ internal static class GameTdbScraper
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 @@ internal static class GameTdbScraper
continue;
var productId = (await xmlReader.ReadElementContentAsStringAsync().ConfigureAwait(false)).ToUpperInvariant();
if (!ProductCodeLookup.ProductCode.IsMatch(productId))
if (!ProductCodeLookup.Pattern().IsMatch(productId))
continue;
string? title = null;

View File

@@ -14,15 +14,17 @@ using PsnClient.Utils;
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(
[GeneratedRegex(
@"(?<content_id>(?<service_id>(?<service_letters>\w\w)(?<service_number>\d{4}))-(?<product_id>(?<product_letters>\w{4})(?<product_number>\d{5}))_(?<part>\d\d)-(?<label>\w{16}))",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture
);
RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture
)]
public static partial Regex ContentIdMatcher();
private static readonly SemaphoreSlim LockObj = new(1, 1);
private static List<string> psnStores = new();
private static List<string> psnStores = [];
private static DateTime storeRefreshTimestamp = DateTime.MinValue;
private static readonly SemaphoreSlim QueueLimiter = new(32, 32);
@@ -52,7 +54,7 @@ internal sealed class PsnScraper
if (string.IsNullOrEmpty(contentId))
return;
var match = ContentIdMatcher.Match(contentId);
var match = ContentIdMatcher().Match(contentId);
if (!match.Success)
return;
@@ -66,7 +68,7 @@ internal sealed class PsnScraper
await LockObj.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
storesToScrape = new(psnStores);
storesToScrape = [..psnStores];
}
finally
{
@@ -129,7 +131,7 @@ internal sealed class PsnScraper
await LockObj.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
storesToScrape = new(psnStores);
storesToScrape = [..psnStores];
}
finally
{
@@ -329,7 +331,7 @@ internal sealed class PsnScraper
.Concat(item.Attributes?.Entitlements ?? Enumerable.Empty<GameSkuRelation>())
.Select(sku => sku.Id)
.Distinct()
.Where(id => ProductCodeLookup.ProductCode.IsMatch(id) && NeedLookup(id))
.Where(id => ProductCodeLookup.Pattern().IsMatch(id) && NeedLookup(id))
.ToList();
foreach (var relatedSku in relatedSkus)
{
@@ -344,12 +346,12 @@ internal sealed class PsnScraper
private static async Task AddOrUpdateThumbnailAsync(string contentId, string? name, string? url, CancellationToken cancellationToken)
{
var match = ContentIdMatcher.Match(contentId);
var match = ContentIdMatcher().Match(contentId);
if (!match.Success)
return;
var productCode = match.Groups["product_id"].Value;
if (!ProductCodeLookup.ProductCode.IsMatch(productCode))
if (!ProductCodeLookup.Pattern().IsMatch(productCode))
return;
name = string.IsNullOrEmpty(name) ? null : name;

View File

@@ -8,9 +8,13 @@ using DSharpPlus.Entities;
namespace CompatBot.Utils;
public static class CommandContextExtensions
public static partial class CommandContextExtensions
{
internal static readonly Regex MessageLinkRegex = new(@"(?:https?://)?discord(app)?\.com/channels/(?<guild>\d+)/(?<channel>\d+)/(?<message>\d+)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
[GeneratedRegex(
@"(?:https?://)?discord(app)?\.com/channels/(?<guild>\d+)/(?<channel>\d+)/(?<message>\d+)",
RegexOptions.IgnoreCase | RegexOptions.Singleline
)]
internal static partial Regex MessageLinkPattern();
public static async Task<DiscordMember?> ResolveMemberAsync(this CommandContext ctx, string word)
{
@@ -37,7 +41,7 @@ public static class CommandContextExtensions
public static Task<DiscordMessage?> GetMessageAsync(this CommandContext ctx, string messageLink)
{
if (MessageLinkRegex.Match(messageLink) is Match m
if (MessageLinkPattern().Match(messageLink) is Match m
&& ulong.TryParse(m.Groups["guild"].Value, out var guildId)
&& ulong.TryParse(m.Groups["channel"].Value, out var channelId)
&& ulong.TryParse(m.Groups["message"].Value, out var msgId)

View File

@@ -11,7 +11,7 @@ using Microsoft.Extensions.Caching.Memory;
namespace CompatBot.Utils;
public static class StringUtils
public static partial class StringUtils
{
public static readonly Encoding Utf8 = new UTF8Encoding(false);
private static readonly MemoryCache FuzzyPairCache = new(new MemoryCacheOptions {ExpirationScanFrequency = TimeSpan.FromMinutes(10)});
@@ -19,9 +19,11 @@ public static class StringUtils
private const char StrikeThroughChar = '\u0336'; // 0x0335 = short dash, 0x0336 = long dash, 0x0337 = short slash, 0x0338 = long slash
public const char InvisibleSpacer = '\u206a';
public const char Nbsp = '\u00a0';
[GeneratedRegex(@"\b(?<cat>cat)s?\b", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)]
private static partial Regex KotFixPattern();
internal static readonly HashSet<char> SpaceCharacters = new()
{
internal static readonly HashSet<char> SpaceCharacters =
[
'\u00a0',
'\u2002', '\u2003', '\u2004', '\u2005', '\u2006',
'\u2007', '\u2008', '\u2009', '\u200a', '\u200b',
@@ -33,7 +35,7 @@ public static class StringUtils
'\u2069', '\u206a', '\u206b', '\u206c', '\u206d',
'\u206e', '\u206f',
'\u3000', '\u303f',
};
];
public static string StripMarks(this string str)
{
@@ -103,7 +105,7 @@ public static class StringUtils
public static string FixKot(this string str)
{
var matches = Regex.Matches(str, @"\b(?<cat>cat)s?\b", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
var matches = KotFixPattern().Matches(str);
foreach (Match m in matches)
{
var idx = m.Index;

View File

@@ -9,7 +9,7 @@ public static class PathUtils
public static string[] GetSegments(string? path)
{
if (string.IsNullOrEmpty(path))
return Array.Empty<string>();
return [];
var result = new List<string>();
string segment;

View File

@@ -8,13 +8,11 @@ using PsnClient.POCOs;
namespace CompatBot.Utils.ResultFormatters;
internal static class FwInfoFormatter
internal static partial class FwInfoFormatter
{
//2019_0828_c975768e5d70e105a72656f498cc9be9/PS3UPDAT.PUP
private static readonly Regex FwLinkInfo = new(
@"(?<year>\d{4})_(?<month>\d\d)(?<day>\d\d)_(?<md5>[0-9a-f]+)",
RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnoreCase
);
[GeneratedRegex(@"(?<year>\d{4})_(?<month>\d\d)(?<day>\d\d)_(?<md5>[0-9a-f]+)", RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnoreCase)]
private static partial Regex FwLinkInfo();
private static readonly Dictionary<string, string> RegionToFlagMap = new(StringComparer.InvariantCultureIgnoreCase)
{
["us"] = "🇺🇸",
@@ -39,7 +37,7 @@ internal static class FwInfoFormatter
.WithColor(Config.Colors.DownloadLinks);
if (fwInfoList.Count > 0
&& fwInfoList.Select(fwi => FwLinkInfo.Match(fwi.DownloadUrl)).FirstOrDefault(m => m.Success) is Match info)
&& fwInfoList.Select(fwi => FwLinkInfo().Match(fwi.DownloadUrl)).FirstOrDefault(m => m.Success) is Match info)
{
result.Description = $"Latest version is **{fwInfoList[0].Version}** released on {info.Groups["year"].Value}-{info.Groups["month"].Value}-{info.Groups["day"].Value}\n" +
$"It is available in {fwInfoList.Count} region{(fwInfoList.Count == 1 ? "" : "s")} out of {RegionToFlagMap.Count}";

View File

@@ -28,7 +28,7 @@ public static class IrdSearchResultFormatter
string[] parts = item.Filename.Split('-');
if (parts.Length == 1)
parts = new[] {"", item.Filename};
parts = ["", item.Filename];
result.AddField(
$"[{parts[0]} v{item.GameVersion}] {item.Title?.Sanitize().Trim(EmbedPager.MaxFieldTitleLength)}",
$"[⏬ `{parts[1].Sanitize().Trim(200)}`]({IrdClient.GetDownloadLink(item.Filename)})"

View File

@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using CompatApiClient.Utils;
using CompatBot.EventHandlers.LogParsing;
using DSharpPlus.Entities;
namespace CompatBot.Utils.ResultFormatters;
@@ -27,9 +29,9 @@ internal static partial class LogParserResult
systemInfo = systemInfo[..idxStart] + systemInfo[idxEnd..];
}
var sysInfoParts = systemInfo.Split(NewLineChars, StringSplitOptions.RemoveEmptyEntries);
var buildInfo = sysInfoParts.Length > 0 ? BuildInfoInLog.Match(sysInfoParts[0]) : BuildInfoInLog.Match(systemInfo);
var cpuInfo = sysInfoParts.Length > 1 ? CpuInfoInLog.Match(sysInfoParts[1]) : CpuInfoInLog.Match(systemInfo);
var osInfo = sysInfoParts.Length > 2 ? OsInfoInLog.Match(sysInfoParts[2]) : OsInfoInLog.Match(systemInfo);
var buildInfo = BuildInfoInLog().Match(sysInfoParts.Length > 0 ? sysInfoParts[0] : systemInfo);
var cpuInfo = CpuInfoInLog().Match(sysInfoParts.Length > 1 ? sysInfoParts[1] : systemInfo);
var osInfo = LogParser.OsInfo().Match(sysInfoParts.Length > 2 ? sysInfoParts[2] : systemInfo);
if (buildInfo.Success)
{
items["build_version"] = buildInfo.Groups["version"].Value.Trim();

View File

@@ -24,6 +24,11 @@ internal static partial class LogParserResult
private static readonly Version DecompilerIssueStartVersion = new(0, 0, 9, 10307);
private static readonly Version DecompilerIssueEndVersion = new(0, 0, 10, 10346);
[GeneratedRegex(@"\(e(rror)?=(0x(?<verification_error_hex>[0-9a-f]+)|(?<verification_error>\d+))(\[\d+\])?\)")]
private static partial Regex VerificationErrorPattern();
[GeneratedRegex(@"Xeon (([EXLW]C?|LV )?\d+|(E\d|AWS)-\d+\w?( (v[2-4]|0))?|D-1.+)( \(ES\))?$", DefaultSingleLine)]
private static partial Regex XeonModelPattern();
private static async Task BuildNotesSectionAsync(DiscordEmbedBuilder builder, LogParseState state, DiscordClient discordClient)
{
var items = state.CompletedCollection!;
@@ -143,7 +148,7 @@ internal static partial class LogParserResult
}
var category = items["game_category"];
if (category is "PE" or "PP" || serial.StartsWith('U') && ProductCodeLookup.ProductCode.IsMatch(serial))
if (category is "PE" or "PP" || serial.StartsWith('U') && ProductCodeLookup.Pattern().IsMatch(serial))
{
builder.Color = Config.Colors.CompatStatusNothing;
notes.Add("❌ PSP software is not supported");
@@ -161,7 +166,7 @@ internal static partial class LogParserResult
if (items["compat_database_path"] is string compatDbPath)
{
if (InstallPath.Match(compatDbPath.Replace('\\', '/').Replace("//", "/").Trim()) is { Success: true } installPathMatch)
if (InstallPath().Match(compatDbPath.Replace('\\', '/').Replace("//", "/").Trim()) is { Success: true } installPathMatch)
{
var rpcs3FolderMissing = string.IsNullOrEmpty(installPathMatch.Groups["rpcs3_folder"].Value);
var desktop = !string.IsNullOrEmpty(installPathMatch.Groups["desktop"].Value);
@@ -222,7 +227,7 @@ internal static partial class LogParserResult
|| cpu.EndsWith('M')
|| cpu.Contains('Y')
|| cpu[^2] == 'G'
|| Regex.IsMatch(cpu, @"Xeon (([EXLW]C?|LV )?\d+|(E\d|AWS)-\d+\w?( (v[2-4]|0))?|D-1.+)( \(ES\))?$", DefaultSingleLine)
|| XeonModelPattern().IsMatch(cpu)
|| threadCount < 6))
notes.Add("⚠️ This CPU is too weak and/or too old for PS3 emulation");
}
@@ -253,15 +258,19 @@ internal static partial class LogParserResult
}
}
if (items["os_type"] == "Windows"
if (items["os_type"] is "Windows"
&& Version.TryParse(items["os_version"], out var winVersion)
&& (winVersion is { Major: < 10 } or { Build: < 19045 or (> 20000 and < 22621) }))
notes.Add("⚠️ Please [upgrade your Windows](https://www.microsoft.com/en-us/software-download/windows11) to currently supported version");
else if (items["os_type"] is "MacOS"
&& Version.TryParse(items["os_version"], out var macVersion)
&& macVersion is {Major: 14, Minor: >=0 and <=2})
notes.Add("❌️ Please update your OS to version 14.3 or newer");
var gpuInfo = items["gpu_info"] ?? items["discrete_gpu_info"];
if (supportedGpu && !string.IsNullOrEmpty(gpuInfo))
{
if (IntelGpuModel.Match(gpuInfo) is {Success: true} intelMatch)
if (IntelGpuModel().Match(gpuInfo) is {Success: true} intelMatch)
{
var family = intelMatch.Groups["gpu_family"].Value.TrimEnd();
var modelNumber = intelMatch.Groups["gpu_model_number"].Value;
@@ -289,7 +298,7 @@ internal static partial class LogParserResult
if (IsNvidia(gpuInfo))
{
var isWindows = items["os_type"] is not null and not "Linux";
var minVersion = isWindows ? NvidiaRecommendedWindowsVersion : NvidiaRecommendedLinuxVersion;
var minVersion = isWindows ? NvidiaRecommendedWindowsDriverVersion : NvidiaRecommendedLinuxDriverVersion;
if (driverVersion < minVersion)
notes.Add($"❗ Please update your nVidia GPU driver to at least version {minVersion}");
if (driverVersion >= NvidiaTextureMemoryBugMinVersion
@@ -306,14 +315,14 @@ internal static partial class LogParserResult
}
else if (IsAmd(gpuInfo) && items["os_type"] == "Windows")
{
if (driverVersion < AmdRecommendedOldWindowsVersion)
notes.Add($"❗ Please update your AMD GPU driver to at least version {AmdRecommendedOldWindowsVersion}");
if (driverVersion < AmdRecommendedWindowsDriverVersion)
notes.Add($"❗ Please update your AMD GPU driver to at least version {AmdRecommendedWindowsDriverVersion}");
}
}
else if (driverVersionString.Contains("older than", StringComparison.InvariantCultureIgnoreCase))
{
if (IsAmd(gpuInfo))
notes.Add($"❗ Please update your AMD GPU driver to version {AmdRecommendedOldWindowsVersion} or newer");
notes.Add($"❗ Please update your AMD GPU driver to version {AmdRecommendedWindowsDriverVersion} or newer");
}
}
}
@@ -449,7 +458,7 @@ internal static partial class LogParserResult
notes.Add(msg);
}
if (multiItems["ppu_patch"] is [string firstPpuPatch, ..]
&& ProgramHashPatch.Match(firstPpuPatch) is { Success: true } m
&& ProgramHashPatch().Match(firstPpuPatch) is { Success: true } m
&& m.Groups["hash"].Value is string firstPpuHash)
{
var exe = Path.GetFileName(items["elf_boot_path"] ?? "");
@@ -577,7 +586,7 @@ internal static partial class LogParserResult
#else
: $"Fatal Error (x{count})";
#endif
if (Regex.Match(fatalError, @"\(e(rror)?=(0x(?<verification_error_hex>[0-9a-f]+)|(?<verification_error>\d+))(\[\d+\])?\)") is {Success: true} match)
if (VerificationErrorPattern().Match(fatalError) is {Success: true} match)
{
if (int.TryParse(match.Groups["verification_error"].Value, out var decCode))
win32ErrorCodes.Add(decCode);

View File

@@ -13,6 +13,9 @@ namespace CompatBot.Utils.ResultFormatters;
internal static partial class LogParserResult
{
[GeneratedRegex(@"Radeon RX 5\d{3}", RegexOptions.IgnoreCase)]
private static partial Regex RadeonRx5xxPattern();
private static void BuildWeirdSettingsSection(DiscordEmbedBuilder builder, LogParseState state, List<string> generalNotes)
{
var items = state.CompletedCollection!;
@@ -187,7 +190,7 @@ internal static partial class LogParserResult
}
var isWireframeBugPossible = items["gpu_info"] is string gpuInfo
&& buildVersion < RdnaMsaaFixedVersion
&& Regex.IsMatch(gpuInfo, @"Radeon RX 5\d{3}", RegexOptions.IgnoreCase) // RX 590 is a thing 😔
&& RadeonRx5xxPattern().IsMatch(gpuInfo) // RX 590 is a thing 😔
&& !gpuInfo.Contains("RADV");
if (items["msaa"] == "Disabled")
{
@@ -534,11 +537,11 @@ internal static partial class LogParserResult
PageSection(builder, notesContent.ToString().Trim(), "Important Settings to Review");
}
private static readonly HashSet<string> P5Ids = new()
{
private static readonly HashSet<string> P5Ids =
[
"BLES02247", "BLUS31604", "BLJM61346",
"NPEB02436", "NPUB31848", "NPJB00769",
};
];
private static readonly HashSet<string> KnownP5Patches = new(StringComparer.InvariantCultureIgnoreCase)
@@ -627,11 +630,11 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> AllStarBattleIds = new()
{
private static readonly HashSet<string> AllStarBattleIds =
[
"BLES01986", "BLUS31405", "BLJS10217",
"NPEB01922", "NPUB31391", "NPJB00331",
};
];
private static readonly HashSet<string> KnownJojoPatches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -758,23 +761,23 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> Gow3Ids = new()
{
private static readonly HashSet<string> Gow3Ids =
[
"BCAS25003", "BCES00510", "BCES00516", "BCES00799", "BCJS37001", "BCUS98111", "BCKS15003",
};
];
private static readonly HashSet<string> GowHDIds = new()
{
private static readonly HashSet<string> GowHDIds =
[
"BCAS20102", "BCES00791", "BCES00800", "BLJM60200", "BCUS98229", // collection except volume II
"NPUA80491", "NPUA80490", "NPEA00255", "NPEA00256", "NPJA00062", "NPJA00061", "NPJA00066",
};
];
private static readonly HashSet<string> GowAscIds = new()
{
private static readonly HashSet<string> GowAscIds =
[
"BCAS25016", "BCES01741", "BCES01742", "BCUS98232",
"NPEA00445", "NPEA90123", "NPUA70216", "NPUA70269", "NPUA80918",
"NPHA80258",
};
];
private static void CheckGoWSettings(string serial, NameValueCollection items, List<string> notes, List<string> generalNotes)
{
@@ -787,12 +790,12 @@ internal static partial class LogParserResult
generalNotes.Add(" This game is known to be very unstable");
}
private static readonly HashSet<string> DesIds = new()
{
private static readonly HashSet<string> DesIds =
[
"BLES00932", "BLUS30443", "BCJS30022", "BCAS20071",
"NPEB01202", "NPUB30910", "NPJA00102",
"BLUD80018", // trade demo
};
];
private static readonly HashSet<string> KnownDesPatches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -862,13 +865,13 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> Dod3Ids = new()
{
private static readonly HashSet<string> Dod3Ids =
[
"BLUS31197", "NPUB31251",
"NPEB01407",
"BLJM61043", "NPJB00380",
"BCAS20311", "NPHB00633", "NPHB00639",
};
];
private static readonly HashSet<string> KnownDod3Patches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -877,13 +880,11 @@ internal static partial class LogParserResult
"b18834a8f21cd29a091b287a66656a279ccba507", // NPUB31251 1.00
"9c04f427625a0064282432e4edfefe9e0956c303", // NPUB31251 1.01
"e1a44e5d3fb03a37f0445e92ed13abce8d6efdd4", // NPEB01407
"60d4a7e2b5efa835e16f51de649c3e3b202e072e", // NPEB01407 delisted
"a017576369165f3746730724c8ae762ed9bc64d8", // BLJM61043 1.00
"eda0339b931f6fe15420b053703ddd89b27d615b", // BLJM61043 1.01
"62eb0f5d8f0f929cb23309311b89ce21eaa3bc9e", // BLJM61043 1.02
"384a28c62ff179a4ae815ab7b711e76fbb1167b4", // BLJM61043 1.03
"c09c496514f6dc591434575b04eb7c003826c11d", // BLJM61043 1.04
"5eb979631fbbe531db5d20f0622dca5a8b64090e", // unknown prob BCAS20311 1.00
"56cc988f7d5b5127049f28ed9278b98de2e4ff1f", // BCAS20311 1.01
"ac64494f4ea31f8b0f82584c48916d30dad16300", // BCAS20311 1.02
"20183817f17fb358d28131e195c5af1fc9579ada", // NPHB00633 1.00
@@ -923,16 +924,16 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> TlouIds = new()
{
private static readonly HashSet<string> TlouIds =
[
"BCAS20270", "BCES01584", "BCES01585", "BCJS37010", "BCUS98174",
"NPEA00435", "NPJA00096", "NPHA80243", "NPUA80960",
"NPEA00521", "NPJA00129", "NPHA80279", "NPUA81175", // left behind
"NPEA90122", "NPHA80246", "NPUA70257", // demos
"NPEA00454", "NPUA30134", "NPEA00517", // soundtrack
"NPJM00012", // bonus video
"NPUO30130" // manual
};
"NPUO30130", // manual
];
private static readonly HashSet<string> KnownTlouPatches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -1003,11 +1004,11 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> Killzone3Ids = new()
{
private static readonly HashSet<string> Killzone3Ids =
[
"BCAS20157", "BCAS25008", "BCES01007", "BCJS30066", "BCJS37003", "BCJS70016", "BCJS75002", "BCUS98234",
"NPEA00321", "NPEA90084", "NPEA90085", "NPEA90086", "NPHA80140", "NPJA90178", "NPUA70133",
};
];
private static void CheckKillzone3Settings(string serial, NameValueCollection items, List<string> notes, UniqueList<string> patchNames)
{
@@ -1025,13 +1026,13 @@ internal static partial class LogParserResult
notes.Add("⚠️ Please enable MLAA patch (recommended) or `Write Color Buffers`");
}
}
private static readonly HashSet<string> RdrIds = new()
{
private static readonly HashSet<string> RdrIds =
[
"BLAS50296", "BLES00680", "BLES01179", "BLES01294", "BLUS30418", "BLUS30711", "BLUS30758",
"BLJM60314", "BLJM60403", "BLJM61181", "BLKS20315",
"NPEB00833", "NPHB00465", "NPHB00466", "NPUB30638", "NPUB30639",
"NPUB50139", // max payne 3 / rdr bundle???
};
];
private static void CheckRdrSettings(string serial, NameValueCollection items, List<string> notes)
{
@@ -1042,13 +1043,13 @@ internal static partial class LogParserResult
notes.Add(" `Write Color Buffers` is required for proper visuals at night");
}
private static readonly HashSet<string> Mgs4Ids = new()
{
private static readonly HashSet<string> Mgs4Ids =
[
"BLAS55005", "BLES00246", "BLJM57001", "BLJM67001", "BLKS25001", "BLUS30109", "BLUS30148",
"NPEB02182", "NPJB00698", "NPUB31633",
"NPEB90116", "NPHB00065", "NPHB00067", "NPJB90149", "NPUB90176", // demos
"NPEB00027", "NPJB90113", "NPUB90126", // database
};
];
private static readonly HashSet<string> KnownMgs4Patches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -1091,11 +1092,11 @@ internal static partial class LogParserResult
generalNotes.Add("🤔 Very interesting version of the game you got there");
}
private static readonly HashSet<string> PdfIds = new()
{
private static readonly HashSet<string> PdfIds =
[
"BLJM60527", "BLUS31319", "BLAS50576",
"NPEB01393", "NPUB31241", "NPHB00559", "NPJB00287",
};
];
private static readonly HashSet<string> KnownPdfPatches = new(StringComparer.InvariantCultureIgnoreCase)
@@ -1105,11 +1106,11 @@ internal static partial class LogParserResult
"1105af0a4d6a4a1481930c6f3090c476cde06c4c",
};
private static readonly HashSet<string> Pdf2ndIds = new()
{
private static readonly HashSet<string> Pdf2ndIds =
[
"BCAS50693", "BLAS50693", "BLES02029", "BLJM61079",
"NPUB31488", "NPHB00671", "NPHB00662", "NPEB02013", "NPJB00435",
};
];
private static readonly HashSet<string> KnownPdf2ndPatches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -1142,15 +1143,15 @@ internal static partial class LogParserResult
generalNotes.Add("🤔 Very interesting version of the game you got there");
}
private static readonly HashSet<string> Gt5Ids = new()
{
private static readonly HashSet<string> Gt5Ids =
[
"BCAS20108", "BCAS20151", "BCAS20154", "BCAS20164", "BCAS20229", "BCAS20267",
"BCES00569",
"BCJS30001", "BCJS30050", "BCJS30100",
"BCUS98114", "BCUS98272", "BCUS98394",
"NPEA90052", "NPHA80080", "NPUA70087", // time trial
"NPUA70115", // kiosk demo
};
];
private static readonly HashSet<string> KnownGt5Patches = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -1179,12 +1180,12 @@ internal static partial class LogParserResult
generalNotes.Add(" Game versions between 1.05 and 1.10 can fail to boot with HDD space error");
}
private static readonly HashSet<string> Gt6Ids = new()
{
private static readonly HashSet<string> Gt6Ids =
[
"BCAS20519", "BCAS20520", "BCAS20521", "BCAS25018", "BCAS25019",
"BCES01893", "BCES01905", "BCJS37016", "BCUS98296", "BCUS99247",
"NPEA00502", "NPJA00113", "NPUA81049",
};
];
private static void CheckGt6Settings(string serial, NameValueCollection items, List<string> notes, List<string> generalNotes)
{
@@ -1251,19 +1252,19 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> RatchetToDIds = new()
{
private static readonly HashSet<string> RatchetToDIds =
[
"BCAS20045", "BCES00052", "BCJS30014", "BCJS70004", "BCJS70012", "BCKS10054", "BCUS98127", "BCUS98153",
"NPEA00452", "NPEA90017", "NPHA20002", "NPUA80965", "NPUA98153",
};
];
private static readonly HashSet<string> Sly4Ids = new()
{
private static readonly HashSet<string> Sly4Ids =
[
"BCES01284", "BCUS98247", "BCUS99142",
"NPEA00429", "NPUA80875",
"NPEA90120", "NPUA70250", // demos
"NPUA30123", // soundtrack ???
};
];
private static void CheckSly4Settings(string serial, NameValueCollection items, List<string> notes)
{
@@ -1279,11 +1280,11 @@ internal static partial class LogParserResult
}
}
private static readonly HashSet<string> DragonsCrownIds = new()
{
private static readonly HashSet<string> DragonsCrownIds =
[
"BCAS20290", "BCAS20298", "BLES01950", "BLJM61041", "BLUS30767",
"NPEB01836", "NPUB31235",
};
];
private static void CheckDragonsCrownSettings(string serial, NameValueCollection items, List<string> notes)
{
@@ -1294,29 +1295,33 @@ internal static partial class LogParserResult
notes.Add("⚠️ Please disable `SPU Loop Detection` for this game");
}
private static readonly HashSet<string> Lbp1Ids = new()
{
"BCAS20058", "BCAS20078", "BCAS20091", "BCES00611", "BCES00141", "BCJS70009", "BCKS10059", "BCUS98148", "BCUS98199", "BCUS98208",
private static readonly HashSet<string> Lbp1Ids =
[
"BCAS20058", "BCAS20078", "BCAS20091", "BCES00611", "BCES00141", "BCJS70009", "BCKS10059", "BCUS98148",
"BCUS98199", "BCUS98208",
"NPEA00241", "NPHA80093", "NPUA80472", "NPUA80479",
};
];
private static readonly HashSet<string> Lbp2Ids = new()
{
"BCAS20201", "BCES00850", "BCES01086", "BCES01345", "BCES01346", "BCES01693", "BCES01694", "BCJS70024", "BCUS90260", "BCUS98249", "BCUS98372",
private static readonly HashSet<string> Lbp2Ids =
[
"BCAS20201", "BCES00850", "BCES01086", "BCES01345", "BCES01346", "BCES01693", "BCES01694", "BCJS70024",
"BCUS90260", "BCUS98249", "BCUS98372",
"NPEA00324", "NPHA80161", "NPUA80662",
};
];
private static readonly HashSet<string> Lbp3Ids = new()
{
private static readonly HashSet<string> Lbp3Ids =
[
"BCAS20322", "BCES01663", "BCES02068", "BCUS98245", "BCUS98362",
"NPEA00515", "NPHA80277", "NPUA81116",
};
];
private static readonly HashSet<string> AllLbpGames = new(Lbp1Ids.Concat(Lbp2Ids).Concat(Lbp3Ids))
{
"NPEA00147", "NPJA90074", "NPJA90097", "NPUA70045", // lbp1 demos and betas
private static readonly HashSet<string> AllLbpGames =
[
..Lbp1Ids, ..Lbp2Ids, ..Lbp3Ids,
"NPEA00147", "NPJA90074", "NPJA90097",
"NPUA70045", // lbp1 demos and betas
"NPUA70117", "NPHA80163", // lbp2 demo and beta
};
];
private static void CheckLbpSettings(string serial, NameValueCollection items, List<string> generalNotes)
{

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
@@ -25,34 +24,32 @@ internal static partial class LogParserResult
private static readonly IrdClient IrdClient = new();
private static readonly PsnClient.Client PsnClient = new();
private static readonly RegexOptions DefaultSingleLine = RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline;
private const RegexOptions DefaultSingleLine = RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline;
// RPCS3 v0.0.3-3-3499d08 Alpha | HEAD
// RPCS3 v0.0.4-6422-95c6ac699 Alpha | HEAD
// RPCS3 v0.0.5-7104-a19113025 Alpha | HEAD
// RPCS3 v0.0.5-42b4ce13a Alpha | minor
// RPCS3 v0.0.18-local_build Alpha | local_build
private static readonly Regex BuildInfoInLog = new(
@"RPCS3 v(?<version_string>(?<version>(\d|\.)+)(-(?<build>\d+))?-(?<commit>[0-9a-z_]+|unknown))( (?<stage>\w+))?( \| (?<branch>[^|\r\n]+))?( \| Firmware version: (?<fw_version_installed>[^|\r\n]+))?( \| (?<unknown>.*))?\r?$",
DefaultSingleLine);
private static readonly Regex CpuInfoInLog = new(
@"(\d{1,2}(th|rd|nd|st) Gen)?(?<cpu_model>[^|@]+?)\s*(((CPU\s*)?@\s*(?<cpu_speed>.+)\s*GHz\s*)|((APU with|(with )?Radeon|R\d, \d+ Compute) [^|]+)|((\w+[\- ]Core )?Processor))?\s* \| (?<thread_count>\d+) Threads \| (?<memory_amount>[0-9\.\,]+) GiB RAM( \| TSC: (?<tsc>\S+))?( \| (?<cpu_extensions>.*?))?\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 readonly Regex OsInfoInLog = new(
@"Operating system: (?<os_type>[^,]+), (Name: (?<posix_name>[^,]+), Release: (?<posix_release>[^,]+), Version: (?<posix_version>[^\r\n]+)|Major: (?<os_version_major>\d+), Minor: (?<os_version_minor>\d+), Build: (?<os_version_build>\d+), Service Pack: (?<os_service_pack>[^,]+), Compatibility mode: (?<os_compat_mode>[^,\r\n]+)|Version: (?<macos_version>[^\r\n]+))\r?$",
DefaultSingleLine);
private static readonly Regex LinuxKernelVersion = new(@"(?<version>\d+\.\d+\.\d+)", DefaultSingleLine);
private static readonly Regex ProgramHashPatch = new(@"(?<hash>\w+(-\d+)?)( \(<-\s*(?<patch_count>\d+)\))?", DefaultSingleLine);
private static readonly char[] NewLineChars = {'\r', '\n'};
[GeneratedRegex(@"RPCS3 v(?<version_string>(?<version>(\d|\.)+)(-(?<build>\d+))?-(?<commit>[0-9a-z_]+|unknown))( (?<stage>\w+))?( \| (?<branch>[^|\r\n]+))?( \| Firmware version: (?<fw_version_installed>[^|\r\n]+))?( \| (?<unknown>.*))?\r?$", DefaultSingleLine)]
private static partial Regex BuildInfoInLog();
[GeneratedRegex(@"(\d{1,2}(th|rd|nd|st) Gen)?(?<cpu_model>[^|@]+?)\s*(((CPU\s*)?@\s*(?<cpu_speed>.+)\s*GHz\s*)|((APU with|(with )?Radeon|R\d, \d+ Compute) [^|]+)|((\w+[\- ]Core )?Processor))?\s* \| (?<thread_count>\d+) Threads \| (?<memory_amount>[0-9\.\,]+) GiB RAM( \| TSC: (?<tsc>\S+))?( \| (?<cpu_extensions>.*?))?\r?$", DefaultSingleLine)]
private static partial Regex CpuInfoInLog();
[GeneratedRegex(@"(?<version>\d+\.\d+\.\d+)", DefaultSingleLine)]
private static partial Regex LinuxKernelVersion();
[GeneratedRegex(@"(?<hash>\w+(-\d+)?)( \(<-\s*(?<patch_count>\d+)\))?", DefaultSingleLine)]
private static partial Regex ProgramHashPatch();
// rpcs3-v0.0.5-7105-064d0619_win64.7z
// rpcs3-v0.0.5-7105-064d0619_linux64.AppImage
private static readonly Regex BuildInfoInUpdate = new(@"rpcs3-v(?<version>(\d|\.)+)(-(?<build>\d+))?-(?<commit>[0-9a-f]+)_", DefaultSingleLine);
private static readonly Regex VulkanDeviceInfo = new(@"'(?<device_name>.+)' running on driver (?<version>.+)\r?$", DefaultSingleLine);
private static readonly Regex IntelGpuModel = new(@"Intel\s?(®|\(R\))? (?<gpu_model>((?<gpu_family>(\w|®| )+) Graphics)( (?<gpu_model_number>P?\d+))?)(\s+\(|$)", DefaultSingleLine);
private static readonly Regex InstallPath = new(@"[A-Z]:/(?<program_files>Program Files( \(x86\))?/)?(?<desktop>([^/]+/)+Desktop/)?(?<rpcs3_folder>[^/]+/)*GuiConfigs/", DefaultSingleLine);
[GeneratedRegex(@"rpcs3-v(?<version>(\d|\.)+)(-(?<build>\d+))?-(?<commit>[0-9a-f]+)_", DefaultSingleLine)]
private static partial Regex BuildInfoInUpdate();
[GeneratedRegex(@"'(?<device_name>.+)' running on driver (?<version>.+)\r?$", DefaultSingleLine)]
private static partial Regex VulkanDeviceInfo();
[GeneratedRegex(@"Intel\s?(®|\(R\))? (?<gpu_model>((?<gpu_family>(\w|®| )+) Graphics)( (?<gpu_model_number>P?\d+))?)(\s+\(|$)", DefaultSingleLine)]
private static partial Regex IntelGpuModel();
[GeneratedRegex(@"[A-Z]:/(?<program_files>Program Files( \(x86\))?/)?(?<desktop>([^/]+/)+Desktop/)?(?<rpcs3_folder>[^/]+/)*GuiConfigs/", DefaultSingleLine)]
private static partial Regex InstallPath();
private static readonly char[] NewLineChars = ['\r', '\n'];
private static readonly Version MinimumOpenGLVersion = new(4, 3);
private static readonly Version MinimumFirmwareVersion = new(4, 80);
@@ -62,10 +59,9 @@ internal static partial class LogParserResult
private static readonly Version NvidiaTextureMemoryBugMinVersion = new(526, 0);
private static readonly Version NvidiaTextureMemoryBugMaxVersion = new(526, 99);
private static readonly Version NvidiaRecommendedWindowsVersion = new(512, 16);
private static readonly Version NvidiaRecommendedLinuxVersion = new(515, 57);
private static readonly Version AmdRecommendedOldWindowsVersion = new(23, 2, 1);
private static readonly Version NvidiaRecommendedWindowsDriverVersion = new(512, 16);
private static readonly Version NvidiaRecommendedLinuxDriverVersion = new(515, 57);
private static readonly Version AmdRecommendedWindowsDriverVersion = new(24, 2, 1);
private static readonly Version NvidiaFullscreenBugFixed = new(0, 0, 6, 8204);
private static readonly Version TsxFaFixedVersion = new(0, 0, 12, 10995);
@@ -108,35 +104,35 @@ internal static partial class LogParserResult
};
private static readonly string[] Known1080pIds =
{
[
"NPEB00258", "NPUB30162", "NPJB00068", // scott pilgrim
};
];
private static readonly string[] KnownDisableVertexCacheIds =
{
[
"NPEB00258", "NPUB30162", "NPJB00068", // scott pilgrim
"NPEB00303", "NPUB30242", "NPHB00229", // crazy taxi
};
];
private static readonly HashSet<string> KnownNoRelaxedXFloatIds = new();
private static readonly HashSet<string> KnownNoRelaxedXFloatIds = [];
private static readonly HashSet<string> KnownNoApproximateXFloatIds = new()
{
private static readonly HashSet<string> KnownNoApproximateXFloatIds =
[
"BLES02247", "BLUS31604", "BLJM61346", "NPEB02436", "NPUB31848", "NPJB00769", // p5
"BLES00932", "BLUS30443", // DeS
};
];
private static readonly HashSet<string> KnownFpsUnlockPatchIds = new()
{
private static readonly HashSet<string> KnownFpsUnlockPatchIds =
[
"BLES00932", "BLUS30443", // DeS
"BLUS30481", "BLES00826", "BLJM60223", // Nier
"BLUS31197", "NPUB31251", "NPEB01407", "BLJM61043", "BCAS20311", // DoD3
"BLUS31405", // jojo asb
"BLJS10318", // jojo eoh
};
];
private static readonly HashSet<string> KnownWriteColorBuffersIds = new()
{
private static readonly HashSet<string> KnownWriteColorBuffersIds =
[
"BLUS30235", "BLES00453", // AC/DC Live: Rock Band Track Pack
"BLUS30399", "BCJS30021", "BCAS20050", // Afrika
"BLUS30607", "BLES0126", "NPUB30545", "BLJM60359", // Alice: Madness Returns
@@ -268,40 +264,40 @@ internal static partial class LogParserResult
"BLES01721", "BLUS31168", "NPEB01072", "NPUB31153", "BLJM60575", "NPEB90467", // WRC 3: FIA World Rally Championship
"BLES01874", "BLUS31509", "NPEB01381", "NPUB31452", "NPJB00624", "BLJM61195", "NPEB90523", // WRC 4: FIA World Rally Championship
"BLES01937", "NPEB01815", "BLUS31277", // WWE 2K14
};
];
private static readonly HashSet<string> KnownResScaleThresholdIds = new()
{
private static readonly HashSet<string> KnownResScaleThresholdIds =
[
"BCAS20270", "BCES01584", "BCES01585", "BCJS37010", "BCUS98174", // The Last of Us
"NPEA00435", "NPEA90122", "NPHA80243", "NPHA80279", "NPJA00096", "NPJA00129", "NPUA70257", "NPUA80960", "NPUA81175",
};
];
private static readonly HashSet<string> KnownMotionControlsIds = new()
{
private static readonly HashSet<string> KnownMotionControlsIds =
[
"BCES00797", "BCES00802", "BCUS98164", "BCJS30040", "NPEA90053", "NPEA90076", "NPUA70088", "NPUA70112", // heavy rain
"BCAS25017", "BCES01121", "BCES01122", "BCES01123", "BCUS98298", "NPEA00513", "NPUA81087", "NPEA90127", "NPJA90259", "NPUA72074", "NPJA00097", // beyond two souls
"NPEA00094", "NPEA00250", "NPJA00039", "NPUA80083", // flower
"NPEA00036", "NPUA80069", "NPJA00004", // locoroco
"BCES01284", "BCUS98247", "BCUS99142", "NPEA00429", "NPUA80875", "NPEA90120", "NPUA70250", // sly cooper 4
"BCAS20112", "BCAS20189", "BCKS10112", "BLES01101", "BLJS10072", "BLJS10114", "BLJS50026", "BLUS30652", "NPEB90321", // no more heroes
};
];
private static readonly HashSet<string> KnownGamesThatRequireInterpreter = new()
{
private static readonly HashSet<string> KnownGamesThatRequireInterpreter =
[
"NPEB00630", "NPUB30493", "NPJB00161", // daytona usa
"BCAS25017", "BCES01121", "BCES01122", "BCES01123", "BCUS98298", "NPEA00513", "NPUA81087", "NPEA90127", "NPJA90259", "NPUA72074", "NPJA00097", // beyond two souls
};
];
private static readonly HashSet<string> KnownGamesThatRequireAccurateXfloat = new()
{
private static readonly HashSet<string> KnownGamesThatRequireAccurateXfloat =
[
"BLES00229", "BLES00258", "BLES00887", "BLES01128", // gta4 + efls
"BLJM55011", "BLJM60235", "BLJM60459", "BLJM60525", "BLJM61180", "BLKS20073", "BLKS20198", // gta4 + efls
"BLUS30127", "BLUS30149", "BLUS30524", "BLUS30682", // gta4 + efls
"NPEB00882", "NPUB30702", "NPUB30704", "NPEB00511", // gta4 + efls
};
];
private static readonly HashSet<string> KnownGamesThatWorkWithRelaxedZcull = new()
{
private static readonly HashSet<string> KnownGamesThatWorkWithRelaxedZcull =
[
"BLAS50296", "BLES00680", "BLES01179", "BLES01294", "BLUS30418", "BLUS30711", "BLUS30758", //rdr
"BLJM60314", "BLJM60403", "BLJM61181", "BLKS20315",
"NPEB00833", "NPHB00465", "NPHB00466", "NPUB30638", "NPUB30639",
@@ -310,7 +306,7 @@ internal static partial class LogParserResult
"NPEB00027", "NPEB02182", "NPEB90116", "NPJB00698", "NPJB90149", "NPUB31633",
"NPHB00065", "NPHB00067",
"BCAS20100", "BCES00664", "NPEA00057", "NPJA00031", "NPUA80105", // wipeout hd
};
];
private static readonly HashSet<string> KnownBogusLicenses = new(StringComparer.InvariantCultureIgnoreCase)
{
@@ -354,7 +350,7 @@ internal static partial class LogParserResult
private static readonly TimeSpan AncientBuild = TimeSpan.FromDays(180);
private static readonly TimeSpan PrehistoricBuild = TimeSpan.FromDays(365);
private static readonly char[] PrioritySeparator = {' '};
private static readonly char[] PrioritySeparator = [' '];
private static readonly string[] EmojiPriority = new[]{ "😱", "💢", "‼️", "❗", "❌", "⁉️", "⚠️", "❔", "✅", "" }
.Select(e => e.TrimEnd('\ufe0f'))
.ToArray();
@@ -734,7 +730,7 @@ internal static partial class LogParserResult
if (string.IsNullOrEmpty(link))
return null;
var latestBuildInfo = BuildInfoInUpdate.Match(link.ToLowerInvariant());
var latestBuildInfo = BuildInfoInUpdate().Match(link.ToLowerInvariant());
if (latestBuildInfo.Success && VersionIsTooOld(items, latestBuildInfo, updateInfo))
return updateInfo;
@@ -796,7 +792,7 @@ internal static partial class LogParserResult
return null;
var info = (from line in foundDevices
let m = VulkanDeviceInfo.Match(line)
let m = VulkanDeviceInfo().Match(line)
where m.Success
select m
).FirstOrDefault(m => m.Groups["device_name"].Value == gpu);
@@ -978,7 +974,7 @@ internal static partial class LogParserResult
return null;
var kernelVersion = release;
if (LinuxKernelVersion.Match(release) is {Success: true} m)
if (LinuxKernelVersion().Match(release) is {Success: true} m)
kernelVersion = m.Groups["version"].Value;
if (version.Contains("Ubuntu", StringComparison.OrdinalIgnoreCase))
return "Ubuntu " + kernelVersion;
@@ -1096,7 +1092,7 @@ internal static partial class LogParserResult
var result = new Dictionary<string, int>(patchList.Count);
foreach (var patch in patchList)
{
var match = ProgramHashPatch.Match(patch);
var match = ProgramHashPatch().Match(patch);
if (match.Success)
{
_ = int.TryParse(match.Groups["patch_count"].Value, out var pCount);

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CompatBot.Database.Providers;
using CompatBot.ThumbScrapper;
@@ -74,7 +75,7 @@ internal static class TitlePatchFormatter
var fname = Path.GetFileName(link);
try
{
var match = PsnScraper.ContentIdMatcher.Match(fname);
var match = PsnScraper.ContentIdMatcher().Match(fname);
if (match.Success)
return fname[20..];
}

View File

@@ -55,7 +55,7 @@ internal static class UpdateInfoFormatter
desc = desc?.Trim();
if (!string.IsNullOrEmpty(desc))
{
if (GithubLinksHandler.IssueMention.Matches(desc) is { Count: >0 } issueMatches)
if (GithubLinksHandler.IssueMention().Matches(desc) is { Count: >0 } issueMatches)
{
var uniqueLinks = new HashSet<string>(10);
foreach (Match m in issueMatches)
@@ -84,7 +84,7 @@ internal static class UpdateInfoFormatter
}
}
}
if (GithubLinksHandler.CommitMention.Matches(desc) is { Count: >0 } commitMatches)
if (GithubLinksHandler.CommitMention().Matches(desc) is { Count: >0 } commitMatches)
{
var uniqueLinks = new HashSet<string>(2);
foreach (Match m in commitMatches)
@@ -102,7 +102,7 @@ internal static class UpdateInfoFormatter
}
}
}
if (!string.IsNullOrEmpty(desc) && GithubLinksHandler.ImageMarkup.Matches(desc) is {Count: >0} imgMatches)
if (!string.IsNullOrEmpty(desc) && GithubLinksHandler.ImageMarkup().Matches(desc) is {Count: >0} imgMatches)
{
var uniqueLinks = new HashSet<string>(10);
foreach (Match m in imgMatches)

View File

@@ -10,14 +10,14 @@ public static class TimeParser
public static readonly Dictionary<string, string[]> TimeZoneAcronyms = new()
{
["PT"] = new[] { "Pacific Standard Time", "America/Los_Angeles" },
["PST"] = new[] { "Pacific Standard Time", "America/Los_Angeles" },
["PDT"] = new[] { "Pacific Standard Time", "Pacific Daylight Time", "America/Los_Angeles" },
["EST"] = new[] { "Eastern Standard Time", "America/New_York" },
["EDT"] = new[] { "Eastern Standard Time", "Eastern Daylight Time", "America/New_York" },
["CEST"] = new[] { "Central European Standard Time", "Europe/Berlin" },
["BST"] = new[] { "British Summer Time", "GMT Standard Time", "Europe/London" },
["JST"] = new[] { "Japan Standard Time", "Tokyo Standard Time", "Asia/Tokyo" },
["PT"] = ["Pacific Standard Time", "America/Los_Angeles"],
["PST"] = ["Pacific Standard Time", "America/Los_Angeles"],
["PDT"] = ["Pacific Standard Time", "Pacific Daylight Time", "America/Los_Angeles"],
["EST"] = ["Eastern Standard Time", "America/New_York"],
["EDT"] = ["Eastern Standard Time", "Eastern Daylight Time", "America/New_York"],
["CEST"] = ["Central European Standard Time", "Europe/Berlin"],
["BST"] = ["British Summer Time", "GMT Standard Time", "Europe/London"],
["JST"] = ["Japan Standard Time", "Tokyo Standard Time", "Asia/Tokyo"],
};
static TimeParser()

View File

@@ -22,7 +22,8 @@ public class AttributeUsageAnalyzer : DiagnosticAnalyzer
description: "GroupCommand methods will silently ignore any access check attributes, so instead create an instance of the required check attribute and call it explicitly inside the method."
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(AccessCheckAttributeOnGroupCommandRule);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
[AccessCheckAttributeOnGroupCommandRule];
public override void Initialize(AnalysisContext context)
{

View File

@@ -13,9 +13,9 @@ namespace SourceGenerators;
[Generator]
public class ConfusablesSourceGenerator : ISourceGenerator
{
private static readonly char[] CommentSplitter = {'#'};
private static readonly char[] FieldSplitter = {';'};
private static readonly char[] PairSplitter = {' '};
private static readonly char[] CommentSplitter = ['#'];
private static readonly char[] FieldSplitter = [';'];
private static readonly char[] PairSplitter = [' '];
private static readonly DiagnosticDescriptor ConfusablesCheckWarning = new(
id: "CONFUSABLES001",

View File

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>

View File

@@ -12,7 +12,7 @@ namespace SourceGenerators;
public class Win32ErrorsSourceGenerator : ISourceGenerator
{
private const string Indent = " ";
private static readonly char[] Separator = {'\t'};
private static readonly char[] Separator = ['\t'];
private static readonly DiagnosticDescriptor Win32ErrorFormatError = new(
id: "WIN32CODE001",

View File

@@ -17,13 +17,13 @@ namespace Tests;
public class LogParsingProfiler
{
private static readonly IArchiveHandler[] ArchiveHandlers =
{
[
new GzipHandler(),
new ZipHandler(),
new RarHandler(),
new SevenZipHandler(),
new PlainTextHandler(),
};
];
[Explicit("For performance profiling only")]
[TestCase(@"C:\Documents\Downloads\RPCS3_20.log", TestName = "Plaintext")]

View File

@@ -5,17 +5,20 @@ using CompatBot.Utils;
namespace Tests;
[TestFixture]
public class RegexTest
public partial class RegexTest
{
[GeneratedRegex(@"Rap file not found: (\xE2\x80\x9C)?(?<rap_file>.*?)(\xE2\x80\x9D)?\r?$", RegexOptions.Multiline | RegexOptions.ExplicitCapture)]
private static partial Regex RapFileMissingLogLine();
[Test]
public void Utf8AsAsciiRegexTest()
{
const string input = @"·W 0:09:45.540824 {PPU[0x1000016] Thread (addContSyncThread) [HLE:0x01245834, LR:0x0019b834]} sceNp: npDrmIsAvailable(): Rap file not found: “/dev_hdd0/home/00000001/exdata/EP4062-NPEB02436_00-ADDCONTENT000001.rap”
·W 0:09:45.540866 {PPU[0x1000016] Thread (addContSyncThread) [HLE:0x01245834, LR:0x0019b834]} sceNp: sceNpDrmIsAvailable2(k_licensee=*0xd521b0, drm_path=*0xd00ddac0)";
const RegexOptions DefaultOptions = RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ExplicitCapture;
const string input = """
·W 0:09:45.540824 {PPU[0x1000016] Thread (addContSyncThread) [HLE:0x01245834, LR:0x0019b834]} sceNp: npDrmIsAvailable(): Rap file not found: /dev_hdd0/home/00000001/exdata/EP4062-NPEB02436_00-ADDCONTENT000001.rap
·W 0:09:45.540866 {PPU[0x1000016] Thread (addContSyncThread) [HLE:0x01245834, LR:0x0019b834]} sceNp: sceNpDrmIsAvailable2(k_licensee=*0xd521b0, drm_path=*0xd00ddac0)
""";
var latin = input.ToLatin8BitEncoding();
var match = Regex.Match(latin, @"Rap file not found: (\xE2\x80\x9C)?(?<rap_file>.*?)(\xE2\x80\x9D)?\r?$", DefaultOptions);
var match = RapFileMissingLogLine().Match(latin);
Assert.Multiple(() =>
{
Assert.That(match.Success, Is.True);

View File

@@ -6,13 +6,13 @@
<ItemGroup>
<PackageReference Include="DuoVia.FuzzyStrings" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="4.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1">
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -108,7 +108,7 @@ internal class UserInfo
Username = parts[0],
Nickname = parts[1],
JoinDate = DateTime.Parse(parts[2], CultureInfo.InvariantCulture),
Roles = parts[3]?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>(),
Roles = parts[3]?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? [],
};
}
}