diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml new file mode 100644 index 0000000..f978232 --- /dev/null +++ b/.github/workflows/publish-wasm.yml @@ -0,0 +1,47 @@ +name: Publish + +on: + push: + paths: + - '.github/workflows/publish-wasm.yml' + - 'src/**' + - 'build/**' + +env: + # Disable the .NET logo in the console output. + DOTNET_NOLOGO: true + # Disable the .NET first time experience to skip caching NuGet packages and speed up the build. + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + # Disable sending .NET CLI telemetry to Microsoft. + DOTNET_CLI_TELEMETRY_OPTOUT: true + +jobs: + publish-wasm: + name: Publish WASM + runs-on: ubuntu-latest + steps: + - name: Setup + uses: butr/actions-common-setup@v2 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + + - name: Setup .NET 9 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.x.x + + - name: Run _build + run: >- + dotnet build external/prism-sharp/PrismSharp.SourceGenerator/PrismSharp.SourceGenerator.csproj --configuration Release; + dotnet build external/prism-sharp/PrismSharp.RegExCompiler/PrismSharp.RegExCompiler.csproj --configuration Release; + dotnet build external/prism-sharp/PrismSharp.Core/PrismSharp.Core.csproj --configuration Release; + + dotnet publish src/BUTR.CrashReport.Renderer.ImGui.WASM/BUTR.CrashReport.Renderer.ImGui.WASM.csproj --configuration Release -o "./site"; + + - name: Deploy to GitHub Pages + uses: crazy-max/ghaction-github-pages@v4 + with: + target_branch: gh-pages + build_dir: ./site + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a9bba08..189aa68 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,22 +26,36 @@ jobs: with: github-token: ${{secrets.GITHUB_TOKEN}} - - name: Setup .NET 8 + - name: Setup .NET 9 uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.x.x + dotnet-version: 9.x.x - name: Run _build run: >- + dotnet build external/prism-sharp/PrismSharp.SourceGenerator/PrismSharp.SourceGenerator.csproj --configuration Release; + dotnet build external/prism-sharp/PrismSharp.RegExCompiler/PrismSharp.RegExCompiler.csproj --configuration Release; + dotnet build external/prism-sharp/PrismSharp.Core/PrismSharp.Core.csproj --configuration Release; + dotnet pack src/BUTR.CrashReport.Models/BUTR.CrashReport.Models.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Decompilers/BUTR.CrashReport.Decompilers.csproj --configuration Release -o "./packages"; dotnet pack src/BUTR.CrashReport/BUTR.CrashReport.csproj --configuration Release -o "./packages"; - dotnet pack src/BUTR.CrashReport.Tool/BUTR.CrashReport.Tool.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.ILMerged/BUTR.CrashReport.ILMerged.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Renderer.Html/BUTR.CrashReport.Renderer.Html.csproj --configuration Release -o "./packages"; - dotnet pack src/BUTR.CrashReport.Renderer.ImGui/BUTR.CrashReport.Renderer.ImGui.csproj --configuration Release -o "./packages"; dotnet pack src/BUTR.CrashReport.Renderer.WinForms/BUTR.CrashReport.Renderer.WinForms.csproj --configuration Release -o "./packages"; dotnet pack src/BUTR.CrashReport.Renderer.Zip/BUTR.CrashReport.Renderer.Zip.csproj --configuration Release -o "./packages"; - dotnet pack src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.csproj --configuration Release -o "./packages"; - dotnet pack src/BUTR.CrashReport.Bannerlord.Parser/BUTR.CrashReport.Bannerlord.Parser.csproj --configuration Release -o "./packages"; + + dotnet pack src/BUTR.CrashReport.Memory/BUTR.CrashReport.Memory.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Native/BUTR.CrashReport.Native.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.CImGui/BUTR.CrashReport.CImGui.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.ImGui/BUTR.CrashReport.ImGui.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Renderer.ImGui/BUTR.CrashReport.Renderer.ImGui.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/BUTR.CrashReport.Renderer.ImGui.Silk.NET.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Renderer.ImGui.Silk.NET.ILMerged/BUTR.CrashReport.Renderer.ImGui.Silk.NET.ILMerged.csproj --configuration Release -o "./packages"; + + dotnet pack src/BUTR.CrashReport.Renderer.Html.Tool/BUTR.CrashReport.Renderer.Html.Tool.csproj --configuration Release -o "./packages"; + dotnet pack src/BUTR.CrashReport.Renderer.ImGui.Tool/BUTR.CrashReport.Renderer.ImGui.Tool.csproj --configuration Release -o "./packages"; shell: pwsh - name: Push to NuGet diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6563ef6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "external/Utf8StringInterpolation"] + path = external/Utf8StringInterpolation + url = https://github.com/Cysharp/Utf8StringInterpolation + branch = main +[submodule "external/ZString"] + path = external/ZString + url = https://github.com/Cysharp/ZString + branch = master +[submodule "external/prism-sharp"] + path = external/prism-sharp + url = https://github.com/BUTR/prism-sharp.git + branch = main diff --git a/build/common.props b/build/common.props index 8dbe0f0..c227162 100644 --- a/build/common.props +++ b/build/common.props @@ -3,8 +3,8 @@ - 7 - 14.0.0.$(GITHUB_RUN_NUMBER) + 0 + 15.0.0.$(GITHUB_RUN_NUMBER) 11.0 enable @@ -13,7 +13,20 @@ $(DefineConstants);BUTRCRASHREPORT_ENABLEWARNINGS; - $(NoWarn);NU1701 + $(NoWarn);NU1701;MSB4011 + + true + embedded + + 1.91.0.1 + 1.91.0 + 3.4.0 + 2.30.8 + 2.22.0 + + + + true diff --git a/external/.editorconfig b/external/.editorconfig new file mode 100644 index 0000000..1b6036e --- /dev/null +++ b/external/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*] +generated_code = true +ij_formatter_enabled = false \ No newline at end of file diff --git a/external/Directory.Build.props b/external/Directory.Build.props new file mode 100644 index 0000000..ca893c5 --- /dev/null +++ b/external/Directory.Build.props @@ -0,0 +1,7 @@ + + + + false + + + diff --git a/external/Utf8StringInterpolation b/external/Utf8StringInterpolation new file mode 160000 index 0000000..d2e5842 --- /dev/null +++ b/external/Utf8StringInterpolation @@ -0,0 +1 @@ +Subproject commit d2e5842bf501391065985d9c29cea75ea6c8cf6a diff --git a/external/ZString b/external/ZString new file mode 160000 index 0000000..f4b39aa --- /dev/null +++ b/external/ZString @@ -0,0 +1 @@ +Subproject commit f4b39aad95edd3350f8f06567c9d07168ac941ec diff --git a/external/prism-sharp b/external/prism-sharp new file mode 160000 index 0000000..7c62d69 --- /dev/null +++ b/external/prism-sharp @@ -0,0 +1 @@ +Subproject commit 7c62d6989222469eb098c3485848433443b5936a diff --git a/lib/cimgui b/lib/cimgui new file mode 160000 index 0000000..35a4e8f --- /dev/null +++ b/lib/cimgui @@ -0,0 +1 @@ +Subproject commit 35a4e8f8932c6395156ffacee288b9c30e50cb63 diff --git a/src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.csproj b/src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.csproj deleted file mode 100644 index 6a76a14..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.csproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - netstandard2.0 - 9.0 - true - $(DefineConstants);BUTRCRASHREPORT_ENABLEWARNINGS - - - - BUTR.CrashReport.Bannerlord.Source - BUTR.CrashReport.Bannerlord.Source - Source code for creating the crash report model and render it as HTML - true - MIT - icon.png - https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png - butr crash report bannerlord - - - - - false - false - false - false - false - false - true - false - false - true - true - - obj - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.props b/src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.props deleted file mode 100644 index 7c6ffd3..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/BUTR.CrashReport.Bannerlord.Source.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - false - - - - \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/CrashReportCreatorHelper.cs b/src/BUTR.CrashReport.Bannerlord.Source/CrashReportCreatorHelper.cs deleted file mode 100644 index 262e3b9..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/CrashReportCreatorHelper.cs +++ /dev/null @@ -1,397 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "CrashReportCreatorHelper.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::Bannerlord.BUTR.Shared.Helpers; - using global::Bannerlord.ModuleManager; - - using global::BUTR.CrashReport.Interfaces; - using global::BUTR.CrashReport.Models; - - using global::HarmonyLib; - using global::HarmonyLib.BUTR.Extensions; - - using global::System; - using global::System.Collections.Generic; - using global::System.Diagnostics; - using global::System.IO; - using global::System.Linq; - using global::System.Reflection; - using global::System.Runtime.InteropServices; - - internal class CrashReportInfoHelper : - IAssemblyUtilities, - ICrashReportMetadataProvider, - ILoaderPluginProvider, - IModelConverter, - IModuleProvider, - IPathAnonymizer - { - private static readonly AccessTools.FieldRef>? GetFeatures = - AccessTools2.StaticFieldRefAccess>("Bannerlord.BLSE.FeatureIds:Features"); - - private static List GetPlugins() - { - var patches = Harmony.GetAllPatchedMethods().Select(Harmony.GetPatchInfo); - var featurePatches = patches - .SelectMany(x => x.Prefixes.Concat(x.Postfixes).Concat(x.Finalizers).Concat(x.Transpilers)) - .Select(x => x.PatchMethod) - .Select(x => x.DeclaringType?.Namespace) - .OfType() - .Where(x => x.StartsWith("Bannerlord.BLSE.Features.")) - .Select(x => x.Substring("Bannerlord.BLSE.Features.".Length)) - .Select(x => x.Substring(0, x.IndexOf('.') is var idx and not -1 ? idx : x.Length)) - .Distinct() - .Select(x => $"BLSE.{x}") - .ToList(); - - if (featurePatches.Count > 0) - featurePatches.Add("BLSE.AssemblyResolver"); - - return featurePatches.Select(x => new LoaderPluginInfo - { - Id = x, - Version = null, - UpdateInfo = null, - }).OfType().ToList() ?? new(); - /* - return GetFeatures?.Invoke().Select(x => new LoaderPluginInfo() - { - Id = x, - Version = null, - UpdateInfo = null, - }).OfType().ToList() ?? new(); - */ - } - - public virtual CrashReportMetadataModel GetCrashReportMetadataModel(CrashReportInfo crashReport) - { - var butrLoaderVersion = GetBUTRLoaderVersion(crashReport); - var blseVersion = GetBLSEVersion(crashReport); - var launcherExVersion = GetLauncherExVersion(crashReport); - - var additionalMetdata = new List(); - if (!string.IsNullOrEmpty(launcherExVersion)) - additionalMetdata.Add(new MetadataModel { Key = "LauncherExVersion", Value = launcherExVersion }); - - return new CrashReportMetadataModel - { - GameName = "Bannerlord", - GameVersion = ApplicationVersionHelper.GameVersionStr(), - - LoaderPluginProviderName = !string.IsNullOrEmpty(butrLoaderVersion) ? "BUTRLoader" : !string.IsNullOrEmpty(blseVersion) ? "BLSE" : null, - LoaderPluginProviderVersion = !string.IsNullOrEmpty(butrLoaderVersion) ? butrLoaderVersion : !string.IsNullOrEmpty(blseVersion) ? blseVersion : null, - - LauncherType = GetLauncherType(crashReport), - LauncherVersion = GetLauncherVersion(crashReport), - - Runtime = null, - - OperatingSystemType = GetOperatingSystemType(), - OperatingSystemVersion = GetOSVersion(), - - AdditionalMetadata = additionalMetdata, - }; - } - - private static OperatingSystemType GetOperatingSystemType() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - if (OSUtilities.IsWine()) - return OperatingSystemType.WindowsOnWine; - return OperatingSystemType.Windows; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return OperatingSystemType.Linux; - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return OperatingSystemType.MacOS; - return OperatingSystemType.Unknown; - } - - private static string? GetOSVersion() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return OSUtilities.GetOSVersionWindows(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return OSUtilities.GetOSVersionLinux(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return OSUtilities.GetOSXVersion(); - return null; - } - - public virtual IEnumerable Assemblies() => AccessTools2.AllAssemblies(); - - public IEnumerable TypesFromAssembly(Assembly assembly) => AccessTools.GetTypesFromAssembly(assembly); - - public virtual IModuleInfo? GetAssemblyModule(CrashReportInfo crashReport, Assembly assembly) - { - try - { - var module = !assembly.IsDynamic ? ModuleInfoHelper.GetModuleByType(AccessTools2.GetTypesFromAssembly(assembly).FirstOrDefault()) : null; - return module is not null ? crashReport.LoadedModules.FirstOrDefault(x => x.Id == module.Id) : null; - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - return null; - } - } - - public virtual ILoaderPluginInfo? GetAssemblyPlugin(CrashReportInfo crashReport, Assembly assembly) => null; - - public virtual AssemblyType GetAssemblyType(AssemblyType type, CrashReportInfo crashReport, Assembly assembly) - { - static bool IsTWCore(Assembly assembly) - { - if (assembly.IsDynamic || string.IsNullOrEmpty(assembly.Location)) - return false; - var configurationFolder = Path.GetDirectoryName(assembly.Location); - var binFolder = Path.GetDirectoryName(configurationFolder); - if (!string.Equals(Path.GetFileName(binFolder), "bin", StringComparison.OrdinalIgnoreCase)) - return false; - var moduleFolder = Path.GetDirectoryName(binFolder); - var modulesFolder = Path.GetDirectoryName(moduleFolder); - if (string.Equals(Path.GetFileName(modulesFolder), "Modules", StringComparison.OrdinalIgnoreCase)) - return false; - - return true; - } - - if (IsTWCore(assembly)) type |= AssemblyType.GameCore; - - var module = !assembly.IsDynamic ? ModuleInfoHelper.GetModuleByType(AccessTools2.GetTypesFromAssembly(assembly).FirstOrDefault()) : null; - if (module is not null && !module.IsOfficial) type |= AssemblyType.Module; - if (module is not null && module.IsOfficial) type |= AssemblyType.GameModule; - - return type; - } - - - public virtual ICollection GetLoadedModules() => ModuleInfoHelper.GetLoadedModules().Select(x => new ModuleInfo(x)).ToArray(); - - public virtual IModuleInfo? GetModuleByType(Type? type) => ModuleInfoHelper.GetModuleByType(type) is { } moduleInfo ? new ModuleInfo(moduleInfo) : null; - - - public virtual ICollection GetLoadedLoaderPlugins() => GetPlugins(); - - public virtual ILoaderPluginInfo? GetLoaderPluginByType(Type? type) - { - if (type is null) - return null; - - if (type.Namespace is null || !type.Namespace.StartsWith("Bannerlord.BLSE.Features.")) - return null; - - var id = type.Namespace.Substring("Bannerlord.BLSE.Features.".Length); - id = id.Substring(0, id.IndexOf('.') is var idx and not -1 ? idx : id.Length); - return new LoaderPluginInfo - { - Id = $"BLSE.{id}", - Version = null, - UpdateInfo = null, - }; - } - - - public virtual List ToModuleModels(ICollection loadedModules, ICollection assemblies) - { - var moduleModels = new List(loadedModules.Count); - foreach (var module in loadedModules.OfType().Select(x => x.InternalModuleInfo)) - { - var isManagedByVortex = File.Exists(Path.Combine(module.Path, "__folder_managed_by_vortex")); - - moduleModels.Add(Convert(module, isManagedByVortex, assemblies)); - } - return moduleModels; - } - - public virtual List ToLoaderPluginModels(ICollection loadedLoaderPlugins, ICollection assemblies) => loadedLoaderPlugins.OfType().Select(x => new LoaderPluginModel - { - Id = x.Id, - Name = x.Id, - Version = x.Version, - UpdateInfo = x.UpdateInfo is not null && x.UpdateInfo.Split(':') is { Length: 2 } split ? new UpdateInfo - { - Provider = split[0], - Value = split[1], - } : null, - Dependencies = Array.Empty(), - Capabilities = Array.Empty(), - AdditionalMetadata = Array.Empty(), - }).ToList(); - - - public virtual bool TryHandlePath(string path, out string anonymizedPath) - { - anonymizedPath = string.Empty; - - if (path.IndexOf("Mount & Blade II Bannerlord", StringComparison.OrdinalIgnoreCase) is var idxRoot and not -1) - { - anonymizedPath = path.Substring(idxRoot); - return true; - } - - if (path.IndexOf("Mount & Blade II- Bannerlord", StringComparison.OrdinalIgnoreCase) is var idxRoot2 and not -1) - { - anonymizedPath = path.Substring(idxRoot2); - return true; - } - - return false; - } - - protected static ModuleModel Convert(ModuleInfoExtendedHelper module, bool isManagedByVortex, ICollection assemblies) - { - var updateInfos = module.UpdateInfo.Split(';').Select(x => x.Split(':') is { Length: 2 } split ? new UpdateInfo() - { - Provider = split[0], - Value = split[1], - } : null).OfType().ToArray(); - var capabilities = new List(); - var moduleModel = new ModuleModel - { - Id = module.Id, - Name = module.Name, - Version = module.Version.ToString(), - IsExternal = module.IsExternal, - IsOfficial = module.IsOfficial, - IsSingleplayer = module.IsSingleplayerModule, - IsMultiplayer = module.IsMultiplayerModule, - Url = !string.IsNullOrEmpty(module.Url) ? module.Url : null, - UpdateInfo = updateInfos.FirstOrDefault(), - DependencyMetadatas = module.DependenciesAllDistinct().Select(x => new DependencyMetadataModel - { - ModuleOrPluginId = x.Id, - Type = x.IsIncompatible ? DependencyMetadataType.Incompatible : (DependencyMetadataType) x.LoadType, - IsOptional = x.IsOptional, - Version = !x.Version.Equals(ApplicationVersion.Empty) ? x.Version.ToString() : null, - VersionRange = !x.VersionRange.Equals(ApplicationVersionRange.Empty) ? x.VersionRange.ToString() : null, - AdditionalMetadata = Array.Empty(), - }).ToArray(), - SubModules = module.SubModules.Where(ModuleInfoHelper.CheckIfSubModuleCanBeLoaded).Select(x => new ModuleSubModuleModel - { - Name = x.Name, - AssemblyId = new() - { - Name = x.DLLName, - Version = null, - PublicKeyToken = null, - }, - Entrypoint = x.SubModuleClassType, - AdditionalMetadata = x.Assemblies.Select(y => new MetadataModel { Key = "METADATA:Assembly", Value = y }) - .Concat(x.Tags.SelectMany(y => y.Value.Select(z => new MetadataModel { Key = y.Key, Value = z }))) - .ToArray(), - }).ToArray(), - Capabilities = capabilities, - AdditionalMetadata = new MetadataModel[] - { - new MetadataModel { Key = "AdditionalUpdateInfos", Value = string.Join(";", updateInfos.Skip(1).Select(x => $"{x.Provider}:{x.Value}")) }, - new MetadataModel { Key = "METADATA:MANAGED_BY_VORTEX", Value = isManagedByVortex.ToString() }, - }, - }; - capabilities.AddRange(CollectionsExtensions.DistinctBy(CrashReportShared.GetModuleCapabilities(assemblies, moduleModel), x => x.Name)); - return moduleModel; - } - - protected static string GetBUTRLoaderVersion(CrashReportInfo crashReport) - { - if (crashReport.AvailableAssemblies.FirstOrDefault(x => x.Key.Name == "Bannerlord.BUTRLoader") is { Key: { } assemblyName } ) - return assemblyName.Version?.ToString() ?? string.Empty; - return string.Empty; - } - protected static string GetBLSEVersion(CrashReportInfo crashReport) - { - var blseMetadata = crashReport.AvailableAssemblies.FirstOrDefault(x => x.Key.Name == "Bannerlord.BLSE.Shared").Value?.GetCustomAttributes(); - return blseMetadata?.FirstOrDefault(x => x.Key == "BLSEVersion")?.Value ?? string.Empty; - } - protected static string GetLauncherExVersion(CrashReportInfo crashReport) - { - var launcherExMetadata = crashReport.AvailableAssemblies.FirstOrDefault(x => x.Key.Name == "Bannerlord.LauncherEx").Value?.GetCustomAttributes(); - return launcherExMetadata?.FirstOrDefault(x => x.Key == "LauncherExVersion")?.Value ?? string.Empty; - } - - protected static string GetLauncherType(CrashReportInfo crashReport) - { - if (crashReport.AdditionalMetadata.TryGetValue("Parent_Process_Name", out var parentProcessName)) - { - return parentProcessName switch - { - "Vortex" => "vortex", - "BannerLordLauncher" => "bannerlordlauncher", - "steam" => "steam", - "GalaxyClient" => "gog", - "EpicGamesLauncher" => "epicgames", - "devenv" => "debuggervisualstudio", - "JetBrains.Debugger.Worker64c" => "debuggerjetbrains", - "explorer" => "explorer", - "NovusLauncher" => "novus", - "ModOrganizer" => "modorganizer", - _ => $"unknown launcher - {parentProcessName}" - }; - } - - if (!string.IsNullOrEmpty(GetBUTRLoaderVersion(crashReport))) - return "butrloader"; - - return "vanilla"; - } - - protected static string GetLauncherVersion(CrashReportInfo crashReport) - { - if (crashReport.AdditionalMetadata.TryGetValue("Parent_Process_File_Version", out var parentProcessFileVersion)) - return parentProcessFileVersion; - - if (GetBUTRLoaderVersion(crashReport) is { } bVersion && !string.IsNullOrEmpty(bVersion)) - return bVersion; - - return "0"; - } - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/CrashReportShared.cs b/src/BUTR.CrashReport.Bannerlord.Source/CrashReportShared.cs deleted file mode 100644 index 0e9d623..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/CrashReportShared.cs +++ /dev/null @@ -1,194 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "CrashReportShared.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE || BUTRCRASHREPORT_ENABLE_HTML_RENDERER -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::BUTR.CrashReport.Extensions; - using global::BUTR.CrashReport.Models; - - using global::System.Collections.Generic; - using global::System.Linq; - - internal static class CrashReportShared - { - // Inspired by SMAPI's detection - // Still Work In Progress for more complex capabilities - private static readonly string[] OSFileSystemTypeReferences = new[] - { - typeof(System.IO.File).FullName!, - typeof(System.IO.FileStream).FullName!, - typeof(System.IO.FileInfo).FullName!, - typeof(System.IO.Directory).FullName!, - typeof(System.IO.DirectoryInfo).FullName!, - typeof(System.IO.DriveInfo).FullName!, - typeof(System.IO.FileSystemWatcher).FullName!, - }; - private static readonly string[] GameFileSystemTypeReferences = new[] - { - "TaleWorlds.Library.*File*", - "TaleWorlds.Library.*Directory*", - "TaleWorlds.SaveSystem.*File*", - }; - private static readonly string[] ShellTypeReferences = new[] - { - typeof(System.Diagnostics.Process).FullName!, - }; - private static readonly string[] SaveSystemTypeReferences = new[] - { - "TaleWorlds.Library.*Save*", - "TaleWorlds.Core.MBSaveLoad", - }; - private static readonly string[] SaveSystemAssemblyReferences = new[] - { - "TaleWorlds.SaveSystem", - }; - private static readonly string[] GameEntitiesTypeReferences = new[] - { - "TaleWorlds.Core.EntitySystem*", - }; - private static readonly string[] GameEntitiesAssemblyReferences = new[] - { - "TaleWorlds.ObjectSystem", - }; - private static readonly string[] InputSystemAssemblyReferences = new[] - { - "TaleWorlds.InputSystem", - }; - private static readonly string[] LocalizationSystemAssemblyReferences = new[] - { - "TaleWorlds.Localization", - }; - private static readonly string[] UITypeReferences = new[] - { - "TaleWorlds.Library.IViewModel", - "TaleWorlds.Library.ViewModel", - }; - private static readonly string[] UIAssemblyReferences = new[] - { - "*GauntletUI*", - }; - private static readonly string[] HttpTypeReferences = new[] - { - "TaleWorlds.Library.*Http*", - "System.Net*Http.*", - }; - private static readonly string[] AchievementSystemTypeReferences = new[] - { - "TaleWorlds.*Achievement*", - }; - private static readonly string[] CampaignSystemTypeReferences = new[] - { - "TaleWorlds.*CampaignSystem*", - }; - private static readonly string[] SkillSystemTypeReferences = new[] - { - "TaleWorlds.Core.CharacterSkills", - "TaleWorlds.Core.DefaultSkills", - "TaleWorlds.Core.SkillObject", - }; - private static readonly string[] ItemSystemTypeReferences = new[] - { - "TaleWorlds.Core.ItemObject", - }; - private static readonly string[] CultureSystemTypeReferences = new[] - { - "TaleWorlds.*Culture*", - }; - - public static IEnumerable GetModuleCapabilities(ICollection assemblies, ModuleModel module) - { - if (module.ContainsTypeReferences(assemblies, CrashReportShared.OSFileSystemTypeReferences)) - yield return new CapabilityModel("OS File System"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.GameFileSystemTypeReferences)) - yield return new CapabilityModel("Game File System"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.ShellTypeReferences)) - yield return new CapabilityModel("Shell"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.SaveSystemTypeReferences)) - yield return new CapabilityModel("Save System"); - if (module.ContainsAssemblyReferences(assemblies, CrashReportShared.SaveSystemAssemblyReferences)) - yield return new CapabilityModel("Save System"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.GameEntitiesTypeReferences)) - yield return new CapabilityModel("Game Entities"); - if (module.ContainsAssemblyReferences(assemblies, CrashReportShared.GameEntitiesAssemblyReferences)) - yield return new CapabilityModel("Game Entities"); - - if (module.ContainsAssemblyReferences(assemblies, CrashReportShared.InputSystemAssemblyReferences)) - yield return new CapabilityModel("Input System"); - - if (module.ContainsAssemblyReferences(assemblies, CrashReportShared.LocalizationSystemAssemblyReferences)) - yield return new CapabilityModel("Localization"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.UITypeReferences)) - yield return new CapabilityModel("User Interface"); - if (module.ContainsAssemblyReferences(assemblies, CrashReportShared.UIAssemblyReferences)) - yield return new CapabilityModel("User Interface"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.HttpTypeReferences)) - yield return new CapabilityModel("Http"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.AchievementSystemTypeReferences)) - yield return new CapabilityModel("Achievements"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.CampaignSystemTypeReferences)) - yield return new CapabilityModel("Campaign"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.SkillSystemTypeReferences)) - yield return new CapabilityModel("Skills"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.ItemSystemTypeReferences)) - yield return new CapabilityModel("Items"); - - if (module.ContainsTypeReferences(assemblies, CrashReportShared.CultureSystemTypeReferences)) - yield return new CapabilityModel("Cultures"); - } - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/HarmonyProvider.cs b/src/BUTR.CrashReport.Bannerlord.Source/HarmonyProvider.cs deleted file mode 100644 index 6fc5cbb..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/HarmonyProvider.cs +++ /dev/null @@ -1,260 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "HarmonyProvider.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using TaleWorlds.MountAndBlade.GauntletUI.Widgets.Multiplayer.Lobby; - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::BUTR.CrashReport.Interfaces; - using global::BUTR.CrashReport.Models; - - using global::HarmonyLib; - using global::HarmonyLib.BUTR.Extensions; - - using global::System; - using global::System.Collections; - using global::System.Collections.Generic; - using global::System.Diagnostics; - using global::System.Linq; - using global::System.Reflection; - - using static global::HarmonyLib.BUTR.Extensions.AccessTools2; - - public class PatchProvider : IPatchProvider - { - private static IEnumerable GetPatches(string type, MethodBase originalMethod, IEnumerable patches) => patches.Select(x => new RuntimePatch - { - PatchProvider = "Harmony", - PatchType = type, - Original = originalMethod, - Patch = x.PatchMethod, - AdditionalMetadata = new List - { - new("Owner", x.owner), - new("Index", x.index.ToString()), - new("Priority", x.priority.ToString()), - new("Before", string.Join(", ", x.before)), - new("After", string.Join(", ", x.after)), - }, - }); - - public IList GetAllPatches() - { - var runtimePatches = new List(); - - foreach (var originalMethod in Harmony.GetAllPatchedMethods()) - { - var patches = Harmony.GetPatchInfo(originalMethod); - if (patches is null) continue; - - runtimePatches.AddRange(GetPatches(originalMethod)); - } - - return runtimePatches; - } - - public IList GetPatches(MethodBase originalMethod) - { - var patches = Harmony.GetPatchInfo(originalMethod); - if (patches is null) return new List(); - - var runtimePatches = new List(); - runtimePatches.AddRange(GetPatches("Prefix", originalMethod, patches.Prefixes)); - runtimePatches.AddRange(GetPatches("Postfixes", originalMethod, patches.Postfixes)); - runtimePatches.AddRange(GetPatches("Finalizers", originalMethod, patches.Finalizers)); - runtimePatches.AddRange(GetPatches("Transpilers", originalMethod, patches.Transpilers)); - - return runtimePatches; - } - - public StackFrameRuntimePatch? GetPatches(StackFrame frame) - { - MethodBase? executingMethod; - var methodFromStackframeIssue = false; - try - { - executingMethod = Harmony.GetMethodFromStackframe(frame); - } - // NullReferenceException means the method was not found. Harmony doesn't handle this case gracefully - catch (NullReferenceException e) - { - Trace.TraceError(e.ToString()); - executingMethod = frame.GetMethod()!; - } - // The given generic instantiation was invalid. - // From what I understand, this will occur with generic methods - // Also when static constructors throw errors, Harmony resolution will fail - catch (Exception e) - { - Trace.TraceError(e.ToString()); - methodFromStackframeIssue = true; - executingMethod = frame.GetMethod()!; - } - - return null; - //return GetPatches(executingMethod); - } - - public MethodBase GetOriginalMethod(StackFrame frame) - { - try - { - if (Harmony.GetMethodFromStackframe(frame) is MethodInfo method) - return Harmony.GetOriginalMethod(method); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - return frame.GetMethod(); - } - - public IntPtr GetNativeMethodBody(MethodBase method) - { - try - { - return MonoModUtils.GetNativeMethodBody(method); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - return IntPtr.Zero; - } - } - } - - /* - public class HarmonyProvider : IHarmonyProvider - { - public virtual IEnumerable GetAllPatchedMethods() => Harmony.GetAllPatchedMethods(); - - public virtual global::BUTR.CrashReport.Models.HarmonyPatches? GetPatchInfo(MethodBase originalMethod) - { - static global::BUTR.CrashReport.Models.HarmonyPatch Convert(Patch patch, global::BUTR.CrashReport.Models.HarmonyPatchType type) => new() - { - Owner = patch.owner, - Index = patch.index, - Priority = patch.priority, - Before = patch.before, - After = patch.after, - PatchMethod = patch.PatchMethod, - Type = type, - }; - - var patches = Harmony.GetPatchInfo(originalMethod); - if (patches is null) return null; - return new() - { - Prefixes = patches.Prefixes.Select(x => Convert(x, Models.HarmonyPatchType.Prefix)).ToArray(), - Postfixes = patches.Postfixes.Select(x => Convert(x, Models.HarmonyPatchType.Postfix)).ToArray(), - Finalizers = patches.Finalizers.Select(x => Convert(x, Models.HarmonyPatchType.Finalizer)).ToArray(), - Transpilers = patches.Transpilers.Select(x => Convert(x, Models.HarmonyPatchType.Transpiler)).ToArray(), - }; - } - - public virtual HarmonyPatches? GetPatchInfo(StackFrame frame, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - MethodBase? executingMethod; - var methodFromStackframeIssue = false; - try - { - executingMethod = Harmony.GetMethodFromStackframe(frame); - } - // NullReferenceException means the method was not found. Harmony doesn't handle this case gracefully - catch (NullReferenceException e) - { - Trace.TraceError(e.ToString()); - executingMethod = frame.GetMethod()!; - } - // The given generic instantiation was invalid. - // From what I understand, this will occur with generic methods - // Also when static constructors throw errors, Harmony resolution will fail - catch (Exception e) - { - Trace.TraceError(e.ToString()); - methodFromStackframeIssue = true; - executingMethod = frame.GetMethod()!; - } - - var executingIdentifiableMethod = executingMethod is MethodInfo mi ? MonoModUtils.GetIdentifiable(mi) as MethodInfo ?? mi : null; - var originalIdentifiableMethod = executingIdentifiableMethod is not null ? Harmony.GetOriginalMethod(executingIdentifiableMethod) : null; - return originalIdentifiableMethod is not null ? GetPatchInfo(originalIdentifiableMethod) : null; - } - - public virtual MethodInfo? GetExecutingMethod(StackFrame frame) - { - try - { - if (Harmony.GetMethodFromStackframe(frame) is MethodInfo method) - return method; - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - return null; - } - - public virtual MethodBase? GetOriginalMethod(StackFrame frame) - { - try - { - if (Harmony.GetMethodFromStackframe(frame) is MethodInfo method) - return Harmony.GetOriginalMethod(method); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - return null; - } - - public IntPtr GetNativeMethodBody(MethodBase method) => MonoModUtils.GetNativeMethodBody(method); - } - */ -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/LoaderPluginInfo.cs b/src/BUTR.CrashReport.Bannerlord.Source/LoaderPluginInfo.cs deleted file mode 100644 index f616ec2..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/LoaderPluginInfo.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "LoaderPluginInfo.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::BUTR.CrashReport.Models; - - internal class LoaderPluginInfo : ILoaderPluginInfo - { - /// - public string Id { get; set; } = string.Empty; - - /// - public string? Version { get; set; } - - /// - public string? UpdateInfo { get; set; } - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/ModuleInfo.cs b/src/BUTR.CrashReport.Bannerlord.Source/ModuleInfo.cs deleted file mode 100644 index 156e48b..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/ModuleInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "ModuleInfo.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::Bannerlord.BUTR.Shared.Helpers; - - using global::BUTR.CrashReport.Models; - - using global::System.Collections.Generic; - using global::System.Linq; - - internal class ModuleInfo : IModuleInfo - { - public ModuleInfoExtendedHelper InternalModuleInfo { get; } - - public string Id => InternalModuleInfo.Id; - public string Version => InternalModuleInfo.Version.ToString(); - public string UpdateInfo => InternalModuleInfo.UpdateInfo; - - public IEnumerable SubModules => InternalModuleInfo.SubModules.Select(x => new ModuleSubModuleInfo(x)); - - public ModuleInfo(ModuleInfoExtendedHelper internalModuleInfo) => InternalModuleInfo = internalModuleInfo; - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/ModuleSubModuleInfo.cs b/src/BUTR.CrashReport.Bannerlord.Source/ModuleSubModuleInfo.cs deleted file mode 100644 index f698950..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/ModuleSubModuleInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "ModuleSubModuleInfo.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::Bannerlord.ModuleManager; - using global::BUTR.CrashReport.Models; - - using global::System.Linq; - - internal class ModuleSubModuleInfo : IModuleSubModuleInfo - { - public SubModuleInfoExtended InternalSubModuleInfo { get; } - - public string AssemblyFile => InternalSubModuleInfo.DLLName; - public string[] Dependencies => InternalSubModuleInfo.Assemblies.ToArray(); - - public ModuleSubModuleInfo(SubModuleInfoExtended internalSubModuleInfo) => InternalSubModuleInfo = internalSubModuleInfo; - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/MonoModProvider.cs b/src/BUTR.CrashReport.Bannerlord.Source/MonoModProvider.cs deleted file mode 100644 index 75ee1a0..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/MonoModProvider.cs +++ /dev/null @@ -1,91 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "MonoModProvider.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - /* - using global::BUTR.CrashReport.Interfaces; - using global::BUTR.CrashReport.Models; - - using global::HarmonyLib; - using global::HarmonyLib.BUTR.Extensions; - - using global::System; - using global::System.Collections; - using global::System.Collections.Generic; - using global::System.Diagnostics; - using global::System.Linq; - using global::System.Reflection; - - using static global::HarmonyLib.BUTR.Extensions.AccessTools2; - - public class MonoModProvider : IMonoModProvider - { - public virtual IEnumerable GetAllPatchedMethods() => Harmony.GetAllPatchedMethods(); - - public virtual MonoModPatches? GetPatchInfo(StackFrame frame, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - return null; - } - - public virtual MethodInfo? GetExecutingMethod(StackFrame frame) - { - return null; - } - - public virtual MethodBase? GetOriginalMethod(StackFrame frame) - { - return null; - } - - public virtual MonoModPatches? GetPatchInfo(MethodBase originalMethod) => MonoModUtils.GetPatches(originalMethod); - - public IntPtr GetNativeMethodBody(MethodBase method) => MonoModUtils.GetNativeMethodBody(method); - } - */ -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/MonoModUtils.cs b/src/BUTR.CrashReport.Bannerlord.Source/MonoModUtils.cs deleted file mode 100644 index 5e9838b..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/MonoModUtils.cs +++ /dev/null @@ -1,252 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "MonoModProvider.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::BUTR.CrashReport.Interfaces; - using global::BUTR.CrashReport.Models; - - using global::HarmonyLib; - using global::HarmonyLib.BUTR.Extensions; - - using global::System; - using global::System.Collections; - using global::System.Collections.Generic; - using global::System.Diagnostics; - using global::System.Linq; - using global::System.Reflection; - - using static global::HarmonyLib.BUTR.Extensions.AccessTools2; - - internal static class MonoModUtils - { - private static readonly AccessTools.FieldRef? DetourStatesField = FieldRefAccess( - "MonoMod.RuntimeDetour.DetourManager:detourStates", logErrorInTrace: false); - private static readonly AccessTools.FieldRef? InfoField = FieldRefAccess( - "MonoMod.RuntimeDetour.DetourManager+ManagedDetourState:info", logErrorInTrace: false); - private static readonly AccessTools.FieldRef? DetoursField = FieldRefAccess( - "MonoMod.RuntimeDetour.MethodDetourInfo:lazyDetours", logErrorInTrace: false); - private static readonly AccessTools.FieldRef? ILHooksField = FieldRefAccess( - "MonoMod.RuntimeDetour.MethodDetourInfo:lazyILHooks", logErrorInTrace: false); - - private delegate MethodBase GetDetourInfoEntryDelegate(object instance); - private static readonly GetDetourInfoEntryDelegate? GetDetourInfoEntry = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourInfo:Entry", logErrorInTrace: false); - - private delegate MethodBase GetILHookInfoManipulatorMethodDelegate(object instance); - private static readonly GetILHookInfoManipulatorMethodDelegate? GetILHookInfoManipulatorMethod = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.ILHookInfo:ManipulatorMethod", logErrorInTrace: false); - - private delegate bool GetDetourBaseIsAppliedDelegate(object instance); - private static readonly GetDetourBaseIsAppliedDelegate? GetDetourBaseIsApplied = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourBase:IsApplied", logErrorInTrace: false); - - private delegate object GetDetourBaseConfigDelegate(object instance); - private static readonly GetDetourBaseConfigDelegate? GetDetourBaseConfig = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourBase:Config", logErrorInTrace: false); - - private delegate string GetDetourConfigIdDelegate(object instance); - private static readonly GetDetourConfigIdDelegate? GetDetourConfigId = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourConfig:Id", logErrorInTrace: false); - - private delegate int? GetDetourConfigPriorityDelegate(object instance); - private static readonly GetDetourConfigPriorityDelegate? GetDetourConfigPriority = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourConfig:Priority", logErrorInTrace: false); - - private delegate int GetDetourConfigSubPriorityDelegate(object instance); - private static readonly GetDetourConfigSubPriorityDelegate? GetDetourConfigSubPriority = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourConfig:SubPriority", logErrorInTrace: false); - - private delegate IEnumerable GetDetourConfigBeforeDelegate(object instance); - private static readonly GetDetourConfigBeforeDelegate? GetDetourConfigBefore = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourConfig:Before", logErrorInTrace: false); - - private delegate IEnumerable GetDetourConfigAfterDelegate(object instance); - private static readonly GetDetourConfigAfterDelegate? GetDetourConfigAfter = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourConfig:After", logErrorInTrace: false); - - //private delegate object GetCurrentRuntimeDelegate(); - //private static readonly AccessTools.FieldRef? DetoursField = FieldRefAccess( - // "MonoMod.RuntimeDetour.DetourInfo:Entry", logErrorInTrace: false); - - private delegate object GetCurrentRuntimeDelegate(); - private static readonly GetCurrentRuntimeDelegate? CurrentRuntimeMethod = GetPropertyGetterDelegate( - "MonoMod.RuntimeDetour.DetourHelper:Runtime", logErrorInTrace: false); - - private delegate MethodBase GetIdentifiableOldDelegate(object instance, MethodBase method); - private static readonly GetIdentifiableOldDelegate? GetIdentifiableOldMethod = GetDelegate( - "MonoMod.RuntimeDetour.IDetourRuntimePlatform:GetIdentifiable", logErrorInTrace: false); - - private delegate IntPtr GetNativeStartDelegate(object instance, MethodBase method); - private static readonly GetNativeStartDelegate? GetNativeStartMethod = GetDelegate( - "MonoMod.RuntimeDetour.IDetourRuntimePlatform:GetNativeStart", logErrorInTrace: false); - - - private delegate object GetCurrentPlatformTripleDelegate(); - private static readonly GetCurrentPlatformTripleDelegate? CurrentPlatformTripleMethod = GetPropertyGetterDelegate( - "MonoMod.Core.Platforms.PlatformTriple:Current", logErrorInTrace: false); - - private delegate MethodBase GetIdentifiableDelegate(object instance, MethodBase method); - private static readonly GetIdentifiableDelegate? GetIdentifiableMethod = GetDelegate( - "MonoMod.Core.Platforms.PlatformTriple:GetIdentifiable", logErrorInTrace: false); - - private delegate IntPtr GetNativeMethodBodyDelegate(object instance, MethodBase method); - private static readonly GetNativeMethodBodyDelegate? GetNativeMethodBodyMethod = GetDelegate( - "MonoMod.Core.Platforms.PlatformTriple:GetNativeMethodBody", logErrorInTrace: false); - - public static MethodBase? GetIdentifiable(MethodBase method) - { - try - { - if (CurrentRuntimeMethod?.Invoke() is { } runtime) - return GetIdentifiableOldMethod?.Invoke(runtime, method); - - if (CurrentPlatformTripleMethod?.Invoke() is { } platformTriple) - return GetIdentifiableMethod?.Invoke(platformTriple, method); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - return null; - } - - public static IntPtr GetNativeMethodBody(MethodBase method) - { - try - { - if (CurrentRuntimeMethod?.Invoke() is { } runtine) - return GetNativeStartMethod?.Invoke(runtine, method) ?? IntPtr.Zero; - - if (CurrentPlatformTripleMethod?.Invoke() is { } platformTriple) - return GetNativeMethodBodyMethod?.Invoke(platformTriple, method) ?? IntPtr.Zero; - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - return IntPtr.Zero; - } - - /* - public static MonoModPatches? GetPatches(MethodBase originalMethod) - { - try - { - if (DetourStatesField?.Invoke() is not { } detourStates) - return null; - - var detourState = detourStates.Contains(originalMethod) ? detourStates[originalMethod] : null; - if (detourState is null) return null; - - if (InfoField?.Invoke(detourState) is not { } info) - return null; - - if (DetoursField?.Invoke(info) is not { } detours) - return null; - - if (ILHooksField?.Invoke(info) is not { } ilHooks) - return null; - - var detourList = new List(); - foreach (var detour in detours) - { - var config = GetDetourBaseConfig?.Invoke(detour); - detourList.Add(new MonoModPatch - { - Method = GetDetourInfoEntry?.Invoke(detour), - IsActive = GetDetourBaseIsApplied?.Invoke(detour) ?? false, - Id = GetDetourConfigId?.Invoke(config) ?? "Unknown", - Index = null, - MaxIndex = null, - GlobalIndex = null, - Priority = GetDetourConfigPriority?.Invoke(config), - SubPriority = GetDetourConfigSubPriority?.Invoke(config) ?? 0, - Before = GetDetourConfigBefore?.Invoke(config)?.ToArray() ?? new string[0], - After = GetDetourConfigAfter.Invoke(config)?.ToArray() ?? new string[0], - }); - } - - var ilHooksList = new List(); - foreach (var ilHook in ilHooks) - { - var config = GetDetourBaseConfig?.Invoke(ilHook); - ilHooksList.Add(new MonoModPatch - { - Method = GetILHookInfoManipulatorMethod?.Invoke(ilHook), - IsActive = GetDetourBaseIsApplied?.Invoke(ilHook) ?? false, - Id = GetDetourConfigId?.Invoke(config) ?? "Unknown", - Index = null, - MaxIndex = null, - GlobalIndex = null, - Priority = GetDetourConfigPriority?.Invoke(config), - SubPriority = GetDetourConfigSubPriority?.Invoke(config) ?? 0, - Before = GetDetourConfigBefore?.Invoke(config)?.ToArray() ?? new string[0], - After = GetDetourConfigAfter.Invoke(config)?.ToArray() ?? new string[0], - }); - } - - return new MonoModPatches - { - Detours = detourList, - ILHooks = ilHooksList, - }; - - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - return null; - } - */ - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.Bannerlord.Source/OSUtilities.cs b/src/BUTR.CrashReport.Bannerlord.Source/OSUtilities.cs deleted file mode 100644 index 30329f2..0000000 --- a/src/BUTR.CrashReport.Bannerlord.Source/OSUtilities.cs +++ /dev/null @@ -1,191 +0,0 @@ -// -// This code file has automatically been added by the "BUTR.CrashReport.Bannerlord.Source" NuGet package (https://www.nuget.org/packages/BUTR.CrashReport.Bannerlord.Source). -// Please see https://github.com/BUTR/BUTR.CrashReport for more information. -// -// IMPORTANT: -// DO NOT DELETE THIS FILE if you are using a "packages.config" file to manage your NuGet references. -// Consider migrating to PackageReferences instead: -// https://docs.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference -// Migrating brings the following benefits: -// * The "BUTR.CrashReport.Bannerlord.Source" folder and the "OSUtilities.cs" file don't appear in your project. -// * The added file is immutable and can therefore not be modified by coincidence. -// * Updating/Uninstalling the package will work flawlessly. -// - -#region License -// MIT License -// -// Copyright (c) Bannerlord's Unofficial Tools & Resources -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#if !BUTRCRASHREPORT_DISABLE -#nullable enable -#if !BUTRCRASHREPORT_ENABLEWARNINGS -#pragma warning disable -#endif - -namespace BUTR.CrashReport.Bannerlord -{ - using global::System; - using global::System.IO; - using global::System.Linq; - using global::System.Runtime.InteropServices; - using global::System.Xml.Linq; - - internal static class OSUtilities - { - [DllImport("kernel32.dll", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)] - static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] - public static extern IntPtr GetModuleHandle([MarshalAs(UnmanagedType.LPWStr)] string lpModuleName); - - [DllImport("ntdll.dll", SetLastError = true)] - private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation); - - [StructLayout(LayoutKind.Sequential)] - private struct RTL_OSVERSIONINFOEX - { - internal uint dwOSVersionInfoSize; - internal uint dwMajorVersion; - internal uint dwMinorVersion; - internal uint dwBuildNumber; - internal uint dwPlatformId; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] - internal string szCSDVersion; - } - - public static bool IsWine() - { - var handle = GetModuleHandle("ntdll.dll"); - return handle != IntPtr.Zero && GetProcAddress(handle, "wine_get_version") != IntPtr.Zero; - } - - public static string GetOSVersionWindows() - { - var osName = "Windows"; - var osVersion = string.Empty; - - var osvi = new RTL_OSVERSIONINFOEX(); - osvi.dwOSVersionInfoSize = (uint) Marshal.SizeOf(osvi); - if (RtlGetVersion(out osvi) == 0) - osVersion = $"{osvi.dwMajorVersion}.{osvi.dwMinorVersion}.{osvi.dwBuildNumber}"; - - return $"{osName} {osVersion}"; - } - - public static string GetOSVersionLinux() - { - var osName = string.Empty; - var osVersion = string.Empty; - - var filePath = string.Empty; - - try - { - if (File.Exists("/usr/lib/os-release")) - filePath = "/usr/lib/os-release"; - } - catch { /* ignored */ } - - try - { - if (File.Exists("/etc/os-release")) - filePath = "/etc/os-release"; - } - catch { /* ignored */ } - - try - { - if (File.Exists("/etc/lsb-release")) - filePath = "/etc/lsb-release"; - } - catch { /* ignored */ } - - if (string.IsNullOrEmpty(filePath)) - return "UNKNOWN LINUX"; - - try - { - using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - using (var reader = new StreamReader(fileStream)) - { - string? line; - while ((line = reader.ReadLine()) != null) - { - if (line.StartsWith("NAME=")) - osName = line.Split('=')[1].Trim('"'); - if (line.StartsWith("DISTRIB_ID=")) - osName = line.Split('=')[1].Trim('"'); - - if (line.StartsWith("VERSION_ID=")) - osVersion = line.Split('=')[1].Trim('"'); - if (line.StartsWith("DISTRIB_RELEASE=")) - osVersion = line.Split('=')[1].Trim('"'); - } - } - } - } - catch - { - return "UNKNOWN LINUX"; - } - - return $"{osName} {osVersion}"; - } - - public static string? GetOSXVersion() - { - try - { - if (!File.Exists("/System/Library/CoreServices/SystemVersion.plist")) - return null; - } - catch { /* ignored */ } - - try - { - using (var fileStream = new FileStream("/System/Library/CoreServices/SystemVersion.plist", FileMode.Open, FileAccess.Read)) - { - using (var reader = new StreamReader(fileStream)) - { - var systemVersionFile = XDocument.Load(reader); - var parsedSystemVersionFile = systemVersionFile.Descendants("dict") - .SelectMany(d => d.Elements("key").Zip(d.Elements().Where(e => e.Name != "key"), (k, v) => new { Key = k, Value = v })) - .ToDictionary(i => i.Key.Value, i => i.Value.Value); - var productName = parsedSystemVersionFile.ContainsKey("ProductName") ? parsedSystemVersionFile["ProductName"] : null; - var productVersion = parsedSystemVersionFile.ContainsKey("ProductVersion") ? parsedSystemVersionFile["ProductVersion"] : null; - return $"{productName} {productVersion}"; - } - } - } - catch - { - return null; - } - } - } -} - -#pragma warning restore -#nullable restore -#endif // BUTRCRASHREPORT_DISABLE \ No newline at end of file diff --git a/src/BUTR.CrashReport.BepInEx5.Source/BepInExIntegration.cs b/src/BUTR.CrashReport.BepInEx5.Source/BepInExIntegration.cs index 2692d3d..bd80550 100644 --- a/src/BUTR.CrashReport.BepInEx5.Source/BepInExIntegration.cs +++ b/src/BUTR.CrashReport.BepInEx5.Source/BepInExIntegration.cs @@ -73,18 +73,11 @@ public List GetPlugins(ICollection assemblies) IsOptional = x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency), AdditionalMetadata = new List { - new MetadataModel - { - Key = "IsHardDependency", - Value = x.Flags.HasFlag(BepInDependency.DependencyFlags.HardDependency) - .ToString() - }, - new MetadataModel - { - Key = "IsSoftDependency", - Value = x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency) - .ToString() - }, + new("IsHardDependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.HardDependency).ToString()), + new("IsSoftDependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency).ToString()), + + new("DISPLAY:Is Hard Dependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.HardDependency).ToString()), + new("DISPLAY:Is Soft Dependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency).ToString()), }, }) .Concat(kv.Value.Incompatibilities.Select(x => new DependencyMetadataModel @@ -97,21 +90,15 @@ public List GetPlugins(ICollection assemblies) AdditionalMetadata = Array.Empty(), })) .ToList(), - AdditionalMetadata = new[] + AdditionalMetadata = new List { - //new MetadataModel { Key = "TargettedBepInExVersion", Value = kv.Value.TargettedBepInExVersion.ToString() }, - new MetadataModel - { - Key = "Location", - Value = kv.Value.Location - }, - //new MetadataModel { Key = "TypeName", Value = kv.Value.TypeName }, - new MetadataModel - { - Key = "Processes", - Value = string.Join("; ", - kv.Value.Processes.Select(x => x.ProcessName)) - }, + //new("TargettedBepInExVersion", kv.Value.TargettedBepInExVersion.ToString()), + //new("TypeName", kv.Value.TypeName), + new("Location", kv.Value.Location), + new("Processes", string.Join("; ", kv.Value.Processes.Select(x => x.ProcessName))), + + new("DISPLAY:Location", kv.Value.Location), + new("DISPLAY:Processes", string.Join("; ", kv.Value.Processes.Select(x => x.ProcessName))), }, Capabilities = capabilities, }; diff --git a/src/BUTR.CrashReport.BepInEx6.Source/BepInExIntegration.cs b/src/BUTR.CrashReport.BepInEx6.Source/BepInExIntegration.cs index 5691151..a048128 100644 --- a/src/BUTR.CrashReport.BepInEx6.Source/BepInExIntegration.cs +++ b/src/BUTR.CrashReport.BepInEx6.Source/BepInExIntegration.cs @@ -73,8 +73,11 @@ private List GetPlugins(BaseChainloader cha IsOptional = x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency), AdditionalMetadata = new List { - new MetadataModel {Key = "IsHardDependency", Value = x.Flags.HasFlag(BepInDependency.DependencyFlags.HardDependency).ToString()}, - new MetadataModel {Key = "IsSoftDependency", Value = x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency).ToString()}, + new("IsHardDependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.HardDependency).ToString()), + new("IsSoftDependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency).ToString()), + + new("DISPLAY:Is Hard Dependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.HardDependency).ToString()), + new("DISPLAY:Is Soft Dependency", x.Flags.HasFlag(BepInDependency.DependencyFlags.SoftDependency).ToString()), }, }).Concat(kv.Value.Incompatibilities.Select(x => new DependencyMetadataModel { @@ -85,12 +88,16 @@ private List GetPlugins(BaseChainloader cha IsOptional = false, AdditionalMetadata = Array.Empty(), })).ToList(), - AdditionalMetadata = new[] + AdditionalMetadata = new List { - //new MetadataModel { Key = "TargettedBepInExVersion", Value = kv.Value.TargettedBepInExVersion.ToString() }, - new MetadataModel {Key = "Location", Value = kv.Value.Location}, - new MetadataModel {Key = "TypeName", Value = kv.Value.TypeName}, - new MetadataModel {Key = "Processes", Value = string.Join("; ", kv.Value.Processes.Select(x => x.ProcessName))}, + //new("TargettedBepInExVersion", kv.Value.TargettedBepInExVersion.ToString()), + new("TypeName", kv.Value.TypeName), + new("Location", kv.Value.Location), + new("Processes", string.Join("; ", kv.Value.Processes.Select(x => x.ProcessName))), + + new("DISPLAY:TypeName", kv.Value.TypeName), + new("DISPLAY:Location", kv.Value.Location), + new("DISPLAY:Processes", string.Join("; ", kv.Value.Processes.Select(x => x.ProcessName))), }, Capabilities = capabilities, }; diff --git a/src/BUTR.CrashReport.CImGui/BUTR.CrashReport.CImGui.csproj b/src/BUTR.CrashReport.CImGui/BUTR.CrashReport.CImGui.csproj new file mode 100644 index 0000000..4b22d03 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/BUTR.CrashReport.CImGui.csproj @@ -0,0 +1,47 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + enable + latest + true + + enable + + $(DefineConstants);OPENGL;IMGUI_DYNAMICLOADER + ImGui + + + + BUTR.CrashReport.CImGui + BUTR.CrashReport.Renderer.ImGui.CImGui + Contains the ImGui renderer backend for showing the crash report. + MIT + https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png + butr crash report bannerlord + + + + + + + + + + + + + + + + resources\%(FileName)%(Extension) + + + + + + + false + + + diff --git a/src/BUTR.CrashReport.CImGui/CmGui.Methods.cs b/src/BUTR.CrashReport.CImGui/CmGui.Methods.cs new file mode 100644 index 0000000..8ac42af --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/CmGui.Methods.cs @@ -0,0 +1,509 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; +using BUTR.CrashReport.Native; + +using ImGui.Structures; + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ImGui; + +unsafe partial class CmGui +{ + private readonly struct UserDataGeneric + { + public readonly Func, int> GenericTDataConverter; + public readonly Delegate Callback; + public readonly void* DataPtr; + + public UserDataGeneric(Func, int> genericTDataConverter, Delegate callback, void* dataPtr) + { + GenericTDataConverter = genericTDataConverter; + Callback = callback; + DataPtr = dataPtr; + } + } + private readonly struct UserDataInt64 + { + public readonly Delegate Callback; + public readonly Int64 Data; + + public UserDataInt64(Delegate callback, Int64 data) + { + Callback = callback; + Data = data; + } + } + + private readonly Dictionary, int>> _callbacks = new(); + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static int ImGuiInputTextCallbackGeneric(ImGuiNET.ImGuiInputTextCallbackData* callbackData) + { + if (callbackData->UserData == null) + return 0; + + ref var userData = ref Unsafe.AsRef(callbackData->UserData); + var genericTDataConverter = userData.GenericTDataConverter; + + return genericTDataConverter((IntPtr) callbackData); + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static int ImGuiInputTextCallbackInt64(ImGuiNET.ImGuiInputTextCallbackData* callbackData) + { + if (callbackData->UserData == null) + return 0; + + ref var userData = ref Unsafe.AsRef(callbackData->UserData); + var data = userData.Data; + var managedCallback = (ImGuiInputTextInt64Callback) userData.Callback; + return managedCallback(new ImGuiInputTextCallbackInt64Data + { + EventFlag = (ImGuiInputTextFlags) callbackData->EventFlag, + Flags = (ImGuiInputTextFlags) callbackData->Flags, + EventChar = callbackData->EventChar, + EventKey = (ImGuiKey) callbackData->EventKey, + CursorPos = callbackData->CursorPos, + SelectionStart = callbackData->SelectionStart, + SelectionEnd = callbackData->SelectionEnd, + Data = data, + }); + } + + private static int GenericTDataCallback(Pointer callbackDataPtr) where TData : struct + { + var callbackData = (ImGuiNET.ImGuiInputTextCallbackData*) callbackDataPtr; + ref var userData = ref Unsafe.AsRef(callbackData->UserData); + ref var managedData = ref Unsafe.AsRef(userData.DataPtr); + var managedCallback = (ImGuiInputTextCallback) userData.Callback; + return managedCallback(new ImGuiInputTextCallbackData + { + EventFlag = (ImGuiInputTextFlags) callbackData->EventFlag, + Flags = (ImGuiInputTextFlags) callbackData->Flags, + EventChar = callbackData->EventChar, + EventKey = (ImGuiKey) callbackData->EventKey, + CursorPos = callbackData->CursorPos, + SelectionStart = callbackData->SelectionStart, + SelectionEnd = callbackData->SelectionEnd, + Data = managedData, + }); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PushId(int id) => igPushID_Int(id); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PushId(ReadOnlySpan utf8Label) => igPushID_Str((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label))); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PopId() => igPopID(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool InputTextMultilineInt64(ReadOnlySpan utf8Label, Span input, int lineCount, ImGuiInputTextInt64Callback callback, Int64 data) + { + var size = new Vector2(-1, GetTextLineHeight() * (lineCount + 1)); + var flags = ImGuiInputTextFlags.ReadOnly | ImGuiInputTextFlags.CallbackAlways; + + PushStyleColor(ImGuiCol.FrameBg, in Zero4); + + delegate* unmanaged[Cdecl] nativeCallbackPtr = &ImGuiInputTextCallbackInt64; + + var userData = new UserDataInt64(callback, data); + + // Lifetime of objects is bound to the function scope + var result = igInputTextMultiline( + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(input)), + (uint) input.Length, + size, + (ImGuiNET.ImGuiInputTextFlags) flags, + (IntPtr) nativeCallbackPtr, + (IntPtr) Unsafe.AsPointer(ref userData)) > 0; + + PopStyleColor(); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool InputTextMultiline(ReadOnlySpan utf8Label, Span input, int lineCount, ImGuiInputTextCallback callback, TData data) + where TData : struct + { + if (!_callbacks.TryGetValue(typeof(TData), out var genericTDataCallback)) + _callbacks.Add(typeof(TData), genericTDataCallback = GenericTDataCallback); + + var size = new Vector2(-1, GetTextLineHeight() * (lineCount + 1)); + var flags = ImGuiInputTextFlags.ReadOnly | ImGuiInputTextFlags.CallbackAlways; + + PushStyleColor(ImGuiCol.FrameBg, in Zero4); + + delegate* unmanaged[Cdecl] nativeCallbackPtr = &ImGuiInputTextCallbackGeneric; + + var userData = new UserDataGeneric(genericTDataCallback, callback, Unsafe.AsPointer(ref data)); + + // Lifetime of objects is bound to the function scope + var result = igInputTextMultiline( + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(input)), + (uint) input.Length, + size, + (ImGuiNET.ImGuiInputTextFlags) flags, + (IntPtr) nativeCallbackPtr, + (IntPtr) Unsafe.AsPointer(ref userData)) > 0; + + PopStyleColor(); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool InputText(ReadOnlySpan utf8Label, Span input, ImGuiInputTextFlags flags) + { + var buf_size = input.Length; + var user_data = IntPtr.Zero; + + PushStyleColor(ImGuiCol.FrameBg, in Zero4); + + var result = igInputText( + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(input)), + (uint) buf_size, + (ImGuiNET.ImGuiInputTextFlags) flags, + IntPtr.Zero, + user_data) > 0; + + PopStyleColor(); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Text(ReadOnlySpan utf8Label) => igTextV((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), null); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void TextWrapped(ReadOnlySpan utf8Label) => igTextWrappedV((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), null); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void TextColored(ReadOnlySpan utf8Label, ref readonly Vector4 color) => igTextColoredV(color, (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), null); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void TextLinkOpenURL(ReadOnlySpan utf8Label, ReadOnlySpan utf8Url) => igTextLinkOpenURL((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Url))); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool Checkbox(ReadOnlySpan utf8Label, ref bool isSelected) => igCheckbox((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), (byte*) Unsafe.AsPointer(ref Unsafe.As(ref isSelected))) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void OpenPopup(ReadOnlySpan utf8Label, ImGuiPopupFlags flags) => igOpenPopup_Str((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), (ImGuiNET.ImGuiPopupFlags) flags); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void CloseCurrentPopup() => igCloseCurrentPopup(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool BeginPopup(ReadOnlySpan utf8Label, ImGuiWindowFlags flags) => igBeginPopup((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), (ImGuiNET.ImGuiWindowFlags) flags) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool BeginPopupModal(ReadOnlySpan utf8Label, ImGuiWindowFlags flags) => igBeginPopupModal((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), null, (ImGuiNET.ImGuiWindowFlags) flags) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool BeginPopupContextWindow(ReadOnlySpan utf8Label) + { + var popup_flags = ImGuiPopupFlags.MouseButtonRight; + return igBeginPopupContextWindow((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), (ImGuiNET.ImGuiPopupFlags) popup_flags) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void EndPopup() => igEndPopup(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool Selectable(ReadOnlySpan utf8Label, ref bool isSelected, ImGuiSelectableFlags flags) + { + ref var selectedNum = ref Unsafe.As(ref isSelected); + return igSelectable_Bool((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), selectedNum, (ImGuiNET.ImGuiSelectableFlags) flags, Zero2) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsMouseDoubleClicked(ImGuiMouseButton mouseButton) => igIsMouseDoubleClicked_Nil((ImGuiNET.ImGuiMouseButton) mouseButton) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool Button(ReadOnlySpan utf8Label) => igButton((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), Zero2) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool SmallButton(ReadOnlySpan utf8Label) => igSmallButton((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label))) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool Begin(ReadOnlySpan utf8Label, ImGuiWindowFlags flags) + { + var p_open = (byte*) null; + return igBegin((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), p_open, (ImGuiNET.ImGuiWindowFlags) flags) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool BeginTable(ReadOnlySpan utf8Label, int column) + { + var flags = ImGuiNET.ImGuiTableFlags.None; + const float inner_width = 0.0f; + return igBeginTable((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), column, flags, Zero2, inner_width) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool BeginChild(ReadOnlySpan utf8Label, ref readonly Vector2 size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) => igBeginChild_Str((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), size, (ImGuiNET.ImGuiChildFlags) child_flags, (ImGuiNET.ImGuiWindowFlags) window_flags) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool TreeNode(ReadOnlySpan utf8Label, ImGuiTreeNodeFlags flags) => igTreeNodeExV_Str( + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), + (ImGuiNET.ImGuiTreeNodeFlags) flags, + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), + null) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PushStyleColor(ImGuiCol idx, ref readonly Vector4 color) => igPushStyleColor_Vec4((ImGuiNET.ImGuiCol) idx, color); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetNextWindowPos(ref readonly Vector2 position) => igSetNextWindowPos(position, ImGuiNET.ImGuiCond.None, Zero2); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetNextWindowSize(ref readonly Vector2 size) => igSetNextWindowSize(size, ImGuiNET.ImGuiCond.None); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetNextWindowViewport(uint viewportId) => igSetNextWindowViewport(viewportId); + + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void TreePop() => igTreePop(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void NewLine() => igNewLine(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void StyleColorsLight() => igStyleColorsLight(IntPtr.Zero); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void StyleColorsDark() => igStyleColorsDark(IntPtr.Zero); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetWindowFontScale(float scale) => igSetWindowFontScale(scale); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void EndChild() => igEndChild(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void End() => igEnd(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool TableNextColumn() => igTableNextColumn() > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SameLine() => igSameLine(0.0f, 0.0f); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void EndTable() => igEndTable(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Separator() => igSeparator(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PushStyleVar(ImGuiStyleVar idx, float value) => igPushStyleVar_Float((ImGuiNET.ImGuiStyleVar) idx, value); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PushStyleVar(ImGuiStyleVar idx, ref readonly Vector2 value) => igPushStyleVar_Vec2((ImGuiNET.ImGuiStyleVar) idx, value); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Bullet() => igBullet(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Indent() => igIndent(0.0f); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Unindent() => igUnindent(0.0f); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SameLine(float offsetFromStartX, float spacing) => igSameLine(offsetFromStartX, spacing); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PopStyleVar() => igPopStyleVar(1); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PopStyleVar(int count) => igPopStyleVar(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PopStyleColor() => igPopStyleColor(1); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void PopStyleColor(int count) => igPopStyleColor(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public float GetTextLineHeight() => igGetTextLineHeight(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public float GetTextLineHeightWithSpacing() => igGetTextLineHeightWithSpacing(); + + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public IntPtr CreateContext() => (IntPtr) igCreateContext(null); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public IntPtr GetCurrentContext() => (IntPtr) igGetCurrentContext(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetCurrentContext(IntPtr ctx) => igSetCurrentContext(ctx); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Render() => igRender(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void NewFrame() => igNewFrame(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public ImGuiMouseCursor GetMouseCursor() => (ImGuiMouseCursor) igGetMouseCursor(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void DestroyContext(IntPtr ctx) => igDestroyContext(ctx); + + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsWindowAppearing() => igIsWindowAppearing() > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public IntPtr GetCurrentWindow() => (IntPtr) igGetCurrentWindow(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void BringWindowToDisplayFront(IntPtr window) => igBringWindowToDisplayFront(window); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool MenuItem(ReadOnlySpan utf8Label) + { + byte* shortcut = null; + byte selected = 0; + byte enabled = 1; + return igMenuItem_Bool((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Label)), shortcut, selected, enabled) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsItemClicked(ImGuiMouseButton mouseButton) => igIsItemClicked((ImGuiNET.ImGuiMouseButton) mouseButton) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsKeyDown(ImGuiKey key) => igIsKeyDown_Nil((ImGuiNET.ImGuiKey) key) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsKeyPressed(ImGuiKey key) => igIsKeyPressed_Bool((ImGuiNET.ImGuiKey) key, 1) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetClipboardText(ReadOnlySpan utf8Data) => igSetClipboardText((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Data))); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void CalcTextSize(ReadOnlySpan utf8Data, out Vector2 size) + { + Unsafe.SkipInit(out size); + + float wrap_width = -1f; + byte hide_text_after_double_hash = 0; + var ptrStart = (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Data)); + var ptrEnd = (byte*) Unsafe.Add(ptrStart, utf8Data.Length - 1); + igCalcTextSize((Vector2*) Unsafe.AsPointer(ref size), ptrStart, ptrEnd, hide_text_after_double_hash, wrap_width); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsWindowFocused() => igIsWindowFocused(ImGuiNET.ImGuiFocusedFlags.None) > 0; + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetWindowFocus() => igSetWindowFocus_Nil(); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsWindowHovered() => igIsWindowHovered(ImGuiNET.ImGuiHoveredFlags.None) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public float GetWindowWidth() => igGetWindowWidth(); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public float GetWindowHeight() => igGetWindowHeight(); + + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void ColorConvertU32ToFloat4(uint u, out Vector4 color) + { + Unsafe.SkipInit(out color); + igColorConvertU32ToFloat4((Vector4*) Unsafe.AsPointer(ref color), u); + } + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public uint ColorConvertFloat4ToU32(ref readonly Vector4 color) => igColorConvertFloat4ToU32(color); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public float GetScrollX() => igGetScrollX(); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public float GetScrollY() => igGetScrollY(); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetScrollX(float value) => igSetScrollX_Float(value); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetScrollY(float value) => igSetScrollY_Float(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetCursorScreenPos(out Vector2 GetCursorScreenPos) + { + Unsafe.SkipInit(out GetCursorScreenPos); + igGetCursorScreenPos((Vector2*) Unsafe.AsPointer(ref GetCursorScreenPos)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void SetMouseCursor(ImGuiMouseCursor textInput) => igSetMouseCursor((ImGuiNET.ImGuiMouseCursor) textInput); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsMouseClicked(ImGuiMouseButton button) + { + byte repeat = 0; + return igIsMouseClicked_Bool((ImGuiNET.ImGuiMouseButton) button, repeat) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetMousePos(out Vector2 mousePos) + { + Unsafe.SkipInit(out mousePos); + igGetMousePos((Vector2*) Unsafe.AsPointer(ref mousePos)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsMouseDragging(ImGuiMouseButton button) + { + float lock_threshold = -1f; + return igIsMouseDragging((ImGuiNET.ImGuiMouseButton) button, lock_threshold) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsMouseDown(ImGuiMouseButton button) => igIsMouseDown_Nil((ImGuiNET.ImGuiMouseButton) button) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void Dummy(ref readonly Vector2 vector2) => igDummy(vector2); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public bool IsMouseHoveringRect(ref readonly Vector2 lineStartScreenPos, ref readonly Vector2 end) + { + byte clip = 1; + return igIsMouseHoveringRect(lineStartScreenPos, end, clip) > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void BeginTooltip() => igBeginTooltip(); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void EndTooltip() => igEndTooltip(); + + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetDrawData(out ImDrawDataWrapper drawDataWrapper) => drawDataWrapper = new(this, (ImGuiNET.ImDrawData*) igGetDrawData()); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetIO(out ImGuiIOWrapper ioWrapper) => ioWrapper = new(this, (ImGuiNET.ImGuiIO*) igGetIO()); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void ImFontConfig(out ImFontConfigWrapper fontConfigWrapper) => fontConfigWrapper = new(this, (ImGuiNET.ImFontConfig*) ImFontConfig_ImFontConfig()); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetMainViewport(out ImGuiViewportWrapper viewportWrapper) => viewportWrapper = new(this, (ImGuiNET.ImGuiViewport*) igGetMainViewport()); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetStyle(out ImGuiStyleWrapper styleWrapper) => styleWrapper = new(this, (ImGuiNET.ImGuiStyle*) igGetStyle()); + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void CreateImGuiListClipper(out ImGuiListClipperWrapper listClipperWrapper) => listClipperWrapper = new(this, (ImGuiNET.ImGuiListClipper*) ImGuiListClipper_ImGuiListClipper()); + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public void GetWindowDrawList(out ImDrawListWrapper drawListWrapper) => drawListWrapper = new(this, (ImGuiNET.ImDrawList*) igGetWindowDrawList()); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/CmGui.Native.cs b/src/BUTR.CrashReport.CImGui/CmGui.Native.cs new file mode 100644 index 0000000..d5d8a5b --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/CmGui.Native.cs @@ -0,0 +1,491 @@ +using System.Numerics; +using System.Runtime.InteropServices; + +namespace ImGui; + +partial class CmGui +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginDel(IntPtrByte name, IntPtrByte p_open, ImGuiNET.ImGuiWindowFlags flags); + public igBeginDel igBegin = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginChild_StrDel(IntPtrByte str_id, Vector2 size, ImGuiNET.ImGuiChildFlags flags, ImGuiNET.ImGuiWindowFlags flags_ex); + public igBeginChild_StrDel igBeginChild_Str = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginTableDel(IntPtrByte str_id, int column, ImGuiNET.ImGuiTableFlags flags, Vector2 outer_size, float inner_width); + public igBeginTableDel igBeginTable = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igBulletDel(); + public igBulletDel igBullet = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igButtonDel(IntPtrByte label, Vector2 size); + public igButtonDel igButton = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igCheckboxDel(IntPtrByte label, IntPtrByte v); + public igCheckboxDel igCheckbox = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrVoid igCreateContextDel(IntPtrImFontAtlas shared_font_atlas); + public igCreateContextDel igCreateContext = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igDestroyContextDel(IntPtrVoid ctx); + public igDestroyContextDel igDestroyContext = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igEndDel(); + public igEndDel igEnd = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igEndChildDel(); + public igEndChildDel igEndChild = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igEndTableDel(); + public igEndTableDel igEndTable = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrVoid igGetCurrentContextDel(); + public igGetCurrentContextDel igGetCurrentContext = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImDrawData igGetDrawDataDel(); + public igGetDrawDataDel igGetDrawData = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImGuiIO igGetIODel(); + public igGetIODel igGetIO = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImGuiViewport igGetMainViewportDel(); + public igGetMainViewportDel igGetMainViewport = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ImGuiNET.ImGuiMouseCursor igGetMouseCursorDel(); + public igGetMouseCursorDel igGetMouseCursor = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImGuiStyle igGetStyleDel(); + public igGetStyleDel igGetStyle = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float igGetTextLineHeightDel(); + public igGetTextLineHeightDel igGetTextLineHeight = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igIndentDel(float indent_w); + public igIndentDel igIndent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igInputTextDel(IntPtrByte label, IntPtrByte buf, uint buf_size, ImGuiNET.ImGuiInputTextFlags flags, IntPtrVoid callback, IntPtrVoid user_data); + public igInputTextDel igInputText = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igInputTextMultilineDel(IntPtrByte label, IntPtrByte buf, uint buf_size, Vector2 size, ImGuiNET.ImGuiInputTextFlags flags, IntPtrVoid callback, IntPtrVoid user_data); + public igInputTextMultilineDel igInputTextMultiline = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igNewFrameDel(); + public igNewFrameDel igNewFrame = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igNewLineDel(); + public igNewLineDel igNewLine = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPopStyleColorDel(int count); + public igPopStyleColorDel igPopStyleColor = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPopStyleVarDel(int count); + public igPopStyleVarDel igPopStyleVar = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPushStyleColor_Vec4Del(ImGuiNET.ImGuiCol idx, Vector4 col); + public igPushStyleColor_Vec4Del igPushStyleColor_Vec4 = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPushStyleVar_FloatDel(ImGuiNET.ImGuiStyleVar idx, float val); + public igPushStyleVar_FloatDel igPushStyleVar_Float = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPushStyleVar_Vec2Del(ImGuiNET.ImGuiStyleVar idx, Vector2 val); + public igPushStyleVar_Vec2Del igPushStyleVar_Vec2 = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igRenderDel(); + public igRenderDel igRender = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSameLineDel(float offset_from_start_x, float spacing); + public igSameLineDel igSameLine = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSeparatorDel(); + public igSeparatorDel igSeparator = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetCurrentContextDel(IntPtrVoid ctx); + public igSetCurrentContextDel igSetCurrentContext = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetNextWindowPosDel(Vector2 pos, ImGuiNET.ImGuiCond cond, Vector2 pivot); + public igSetNextWindowPosDel igSetNextWindowPos = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetNextWindowSizeDel(Vector2 size, ImGuiNET.ImGuiCond cond); + public igSetNextWindowSizeDel igSetNextWindowSize = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetNextWindowViewportDel(uint viewport_id); + public igSetNextWindowViewportDel igSetNextWindowViewport = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetWindowFontScaleDel(float scale); + public igSetWindowFontScaleDel igSetWindowFontScale = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igSmallButtonDel(IntPtrByte label); + public igSmallButtonDel igSmallButton = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igStyleColorsDarkDel(IntPtrImGuiStyle dst); + public igStyleColorsDarkDel igStyleColorsDark = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igStyleColorsLightDel(IntPtrImGuiStyle dst); + public igStyleColorsLightDel igStyleColorsLight = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igTableNextColumnDel(); + public igTableNextColumnDel igTableNextColumn = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igTextVDel(IntPtrByte text, IntPtrVoid args); + public igTextVDel igTextV = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igTextColoredVDel(Vector4 col, IntPtrByte text, IntPtrVoid args); + public igTextColoredVDel igTextColoredV = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igTextWrappedVDel(IntPtrByte text, IntPtrVoid args); + public igTextWrappedVDel igTextWrappedV = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igTextLinkOpenURLDel(IntPtrByte text, IntPtrByte url); + public igTextLinkOpenURLDel igTextLinkOpenURL = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igTreeNodeExV_StrDel(IntPtrByte label, ImGuiNET.ImGuiTreeNodeFlags flags, IntPtrByte text, IntPtrVoid args); + public igTreeNodeExV_StrDel igTreeNodeExV_Str = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igTreePopDel(); + public igTreePopDel igTreePop = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igUnindentDel(float indent_w); + public igUnindentDel igUnindent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddInputCharacterDel(IntPtrImGuiIO io, uint c); + public ImGuiIO_AddInputCharacterDel ImGuiIO_AddInputCharacter = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddKeyEventDel(IntPtrImGuiIO io, ImGuiNET.ImGuiKey key, byte down); + public ImGuiIO_AddKeyEventDel ImGuiIO_AddKeyEvent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_SetKeyEventNativeDataDel(IntPtrImGuiIO io, ImGuiNET.ImGuiKey key, int native_keycode, int native_scancode, int native_state); + public ImGuiIO_SetKeyEventNativeDataDel ImGuiIO_SetKeyEventNativeData = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddMouseButtonEventDel(IntPtrImGuiIO io, ImGuiNET.ImGuiMouseButton button, byte down); + public ImGuiIO_AddMouseButtonEventDel ImGuiIO_AddMouseButtonEvent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddMouseWheelEventDel(IntPtrImGuiIO io, float wheel_x, float wheel_y); + public ImGuiIO_AddMouseWheelEventDel ImGuiIO_AddMouseWheelEvent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddMousePosEventDel(IntPtrImGuiIO io, float x, float y); + public ImGuiIO_AddMousePosEventDel ImGuiIO_AddMousePosEvent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddFocusEventDel(IntPtrImGuiIO io, byte focused); + public ImGuiIO_AddFocusEventDel ImGuiIO_AddFocusEvent = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_destroyDel(IntPtrImGuiIO io); + public ImGuiIO_destroyDel ImGuiIO_destroy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImFontAtlas_GetTexDataAsRGBA32Del(IntPtrImFontAtlas atlas, IntPtrIntPtr out_pixels, IntPtrInt32 out_width, IntPtrInt32 out_height, IntPtrInt32 out_bytes_per_pixel); + public ImFontAtlas_GetTexDataAsRGBA32Del ImFontAtlas_GetTexDataAsRGBA32 = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImFontAtlas_SetTexIDDel(IntPtrImFontAtlas atlas, IntPtrVoid id); + public ImFontAtlas_SetTexIDDel ImFontAtlas_SetTexID = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImFontAtlas_ClearTexDataDel(IntPtrImFontAtlas atlas); + public ImFontAtlas_ClearTexDataDel ImFontAtlas_ClearTexData = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImFont ImFontAtlas_AddFontDefaultDel(IntPtrImFontAtlas atlas, IntPtrImFontConfig font_cfg); + public ImFontAtlas_AddFontDefaultDel ImFontAtlas_AddFontDefault = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImFontAtlas_destroyDel(IntPtrImFontAtlas atlas); + public ImFontAtlas_destroyDel ImFontAtlas_destroy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImFontConfig ImFontConfig_ImFontConfigDel(); + public ImFontConfig_ImFontConfigDel ImFontConfig_ImFontConfig = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImFontConfig_destroyDel(IntPtrImFontConfig font_cfg); + public ImFontConfig_destroyDel ImFontConfig_destroy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImGuiListClipper ImGuiListClipper_ImGuiListClipperDel(); + public ImGuiListClipper_ImGuiListClipperDel ImGuiListClipper_ImGuiListClipper = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiListClipper_BeginDel(IntPtrImGuiListClipper clipper, int items_count, float items_height); + public ImGuiListClipper_BeginDel ImGuiListClipper_Begin = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiListClipper_EndDel(IntPtrImGuiListClipper clipper); + public ImGuiListClipper_EndDel ImGuiListClipper_End = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte ImGuiListClipper_StepDel(IntPtrImGuiListClipper clipper); + public ImGuiListClipper_StepDel ImGuiListClipper_Step = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiListClipper_destroyDel(IntPtrImGuiListClipper clipper); + public ImGuiListClipper_destroyDel ImGuiListClipper_destroy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiStyle_ScaleAllSizesDel(IntPtrImGuiStyle style, float scale_factor); + public ImGuiStyle_ScaleAllSizesDel ImGuiStyle_ScaleAllSizes = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiStyle_destroyDel(IntPtrImGuiStyle style); + public ImGuiStyle_destroyDel ImGuiStyle_destroy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawData_AddDrawListDel(IntPtrImDrawData draw_data, IntPtrImDrawList draw_list); + public ImDrawData_AddDrawListDel ImDrawData_AddDrawList = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawData_ClearDel(IntPtrImDrawData draw_data); + public ImDrawData_ClearDel ImDrawData_Clear = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawData_DeIndexAllBuffersDel(IntPtrImDrawData draw_data); + public ImDrawData_DeIndexAllBuffersDel ImDrawData_DeIndexAllBuffers = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawData_ScaleClipRectsDel(IntPtrImDrawData draw_data, Vector2 scale); + public ImDrawData_ScaleClipRectsDel ImDrawData_ScaleClipRects = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawData_destroyDel(IntPtrImDrawData draw_data); + public ImDrawData_destroyDel ImDrawData_destroy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawList_AddText_Vec2Del(IntPtrImDrawList draw_list, Vector2 pos, uint col, IntPtrByte text_begin, IntPtrByte text_end); + public ImDrawList_AddText_Vec2Del ImDrawList_AddText_Vec2 = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawList_AddRectDel(IntPtrImDrawList draw_list, Vector2 p_min, Vector2 p_max, uint col, float rounding, ImGuiNET.ImDrawFlags flags, float thickness); + public ImDrawList_AddRectDel ImDrawList_AddRect = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImDrawList_AddRectFilledDel(IntPtrImDrawList draw_list, Vector2 p_min, Vector2 p_max, uint col, float rounding, ImGuiNET.ImDrawFlags flags); + public ImDrawList_AddRectFilledDel ImDrawList_AddRectFilled = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igOpenPopup_StrDel(IntPtrByte str_id, ImGuiNET.ImGuiPopupFlags flags); + public igOpenPopup_StrDel igOpenPopup_Str = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igCloseCurrentPopupDel(); + public igCloseCurrentPopupDel igCloseCurrentPopup = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igSelectable_BoolDel(IntPtrByte label, byte selected, ImGuiNET.ImGuiSelectableFlags flags, Vector2 size); + public igSelectable_BoolDel igSelectable_Bool = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsMouseDoubleClicked_NilDel(ImGuiNET.ImGuiMouseButton button); + public igIsMouseDoubleClicked_NilDel igIsMouseDoubleClicked_Nil = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginPopupDel(IntPtrByte str_id, ImGuiNET.ImGuiWindowFlags flags); + public igBeginPopupDel igBeginPopup = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginPopupModalDel(IntPtrByte name, IntPtrByte p_open, ImGuiNET.ImGuiWindowFlags flags); + public igBeginPopupModalDel igBeginPopupModal = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginPopupContextWindowDel(IntPtrByte str_id, ImGuiNET.ImGuiPopupFlags flags); + public igBeginPopupContextWindowDel igBeginPopupContextWindow = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igEndPopupDel(); + public igEndPopupDel igEndPopup = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsWindowAppearingDel(); + public igIsWindowAppearingDel igIsWindowAppearing = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrVoid igGetCurrentWindowDel(); + public igGetCurrentWindowDel igGetCurrentWindow = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igBringWindowToDisplayFrontDel(IntPtrVoid window); + public igBringWindowToDisplayFrontDel igBringWindowToDisplayFront = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igMenuItem_BoolDel(IntPtrByte label, IntPtrByte shortcut, byte selected, byte enabled); + public igMenuItem_BoolDel igMenuItem_Bool = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsItemClickedDel(ImGuiNET.ImGuiMouseButton mouse_button); + public igIsItemClickedDel igIsItemClicked = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsKeyDown_NilDel(ImGuiNET.ImGuiKey key); + public igIsKeyDown_NilDel igIsKeyDown_Nil = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsKeyPressed_BoolDel(ImGuiNET.ImGuiKey key, byte repeat); + public igIsKeyPressed_BoolDel igIsKeyPressed_Bool = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetClipboardTextDel(IntPtrByte text); + public igSetClipboardTextDel igSetClipboardText = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPushID_StrDel(IntPtrByte str_id); + public igPushID_StrDel igPushID_Str = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPushID_IntDel(int int_id); + public igPushID_IntDel igPushID_Int = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igPopIDDel(); + public igPopIDDel igPopID = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float igGetTextLineHeightWithSpacingDel(); + public igGetTextLineHeightWithSpacingDel igGetTextLineHeightWithSpacing = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igCalcTextSizeDel(IntPtrVector2 out_size, IntPtrByte text_begin, IntPtrByte text_end, byte hide_text_after_double_hash, float wrap_width); + public igCalcTextSizeDel igCalcTextSize = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igBeginTooltipDel(); + public igBeginTooltipDel igBeginTooltip = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igEndTooltipDel(); + public igEndTooltipDel igEndTooltip = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsMouseHoveringRectDel(Vector2 r_min, Vector2 r_max, byte clip); + public igIsMouseHoveringRectDel igIsMouseHoveringRect = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igDummyDel(Vector2 size); + public igDummyDel igDummy = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsMouseDown_NilDel(ImGuiNET.ImGuiMouseButton button); + public igIsMouseDown_NilDel igIsMouseDown_Nil = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsMouseDraggingDel(ImGuiNET.ImGuiMouseButton button, float lock_threshold); + public igIsMouseDraggingDel igIsMouseDragging = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igGetMousePosDel(IntPtrVector2 out_pos); + public igGetMousePosDel igGetMousePos = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsMouseClicked_BoolDel(ImGuiNET.ImGuiMouseButton button, byte repeat); + public igIsMouseClicked_BoolDel igIsMouseClicked_Bool = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetMouseCursorDel(ImGuiNET.ImGuiMouseCursor cursor_type); + public igSetMouseCursorDel igSetMouseCursor = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igGetCursorScreenPosDel(IntPtrVector2 out_pos); + public igGetCursorScreenPosDel igGetCursorScreenPos = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float igGetScrollXDel(); + public igGetScrollXDel igGetScrollX = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float igGetScrollYDel(); + public igGetScrollYDel igGetScrollY = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetScrollX_FloatDel(float scroll_x); + public igSetScrollX_FloatDel igSetScrollX_Float = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetScrollY_FloatDel(float scroll_y); + public igSetScrollY_FloatDel igSetScrollY_Float = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate uint igColorConvertFloat4ToU32Del(Vector4 col); + public igColorConvertFloat4ToU32Del igColorConvertFloat4ToU32 = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igColorConvertU32ToFloat4Del(IntPtrVector4 out_col, uint in_col); + public igColorConvertU32ToFloat4Del igColorConvertU32ToFloat4 = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void igSetWindowFocus_NilDel(); + public igSetWindowFocus_NilDel igSetWindowFocus_Nil = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float igGetWindowWidthDel(); + public igGetWindowWidthDel igGetWindowWidth = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float igGetWindowHeightDel(); + public igGetWindowHeightDel igGetWindowHeight = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsWindowHoveredDel(ImGuiNET.ImGuiHoveredFlags flags); + public igIsWindowHoveredDel igIsWindowHovered = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate byte igIsWindowFocusedDel(ImGuiNET.ImGuiFocusedFlags flags); + public igIsWindowFocusedDel igIsWindowFocused = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtrImDrawList igGetWindowDrawListDel(); + public igGetWindowDrawListDel igGetWindowDrawList = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void ImGuiIO_AddInputCharactersUTF8Del(IntPtrImGuiIO self, IntPtrByte str); + public ImGuiIO_AddInputCharactersUTF8Del ImGuiIO_AddInputCharactersUTF8 = null!; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.cs b/src/BUTR.CrashReport.CImGui/CmGui.cs similarity index 51% rename from src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.cs rename to src/BUTR.CrashReport.CImGui/CmGui.cs index f816b7e..65ff067 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.cs +++ b/src/BUTR.CrashReport.CImGui/CmGui.cs @@ -1,15 +1,20 @@ -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; + +using ImGui.Structures; using System.Numerics; using System.Runtime.CompilerServices; -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; +namespace ImGui; -internal partial class CmGui +public partial class CmGui : IImGui, ImGuiListClipperWrapper> { private const MethodImplOptions AggressiveOptimization = (MethodImplOptions) 512; private static readonly Vector2 Zero2 = Vector2.Zero; private static readonly Vector3 Zero3 = Vector3.Zero; private static readonly Vector4 Zero4 = Vector4.Zero; + + public void Dispose() { } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/GlobalUsing.cs b/src/BUTR.CrashReport.CImGui/GlobalUsing.cs new file mode 100644 index 0000000..9145e02 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/GlobalUsing.cs @@ -0,0 +1,33 @@ +#if NET6_0_OR_GREATER +global using IntPtrByte = BUTR.CrashReport.Native.Pointer; +global using IntPtrInt32 = BUTR.CrashReport.Native.Pointer; +global using IntPtrIntPtr = BUTR.CrashReport.Native.Pointer; +global using IntPtrVoid = BUTR.CrashReport.Native.Pointer; +global using IntPtrVector2 = BUTR.CrashReport.Native.Pointer; +global using IntPtrVector4 = BUTR.CrashReport.Native.Pointer; +global using IntPtrImDrawData = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiIO = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiViewport = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiStyle = BUTR.CrashReport.Native.Pointer; +global using IntPtrImFont = BUTR.CrashReport.Native.Pointer; +global using IntPtrImFontAtlas = BUTR.CrashReport.Native.Pointer; +global using IntPtrImFontConfig = BUTR.CrashReport.Native.Pointer; +global using IntPtrImDrawList = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiListClipper = BUTR.CrashReport.Native.Pointer; +#else +global using IntPtrByte = BUTR.CrashReport.Native.Pointer; +global using IntPtrImDrawData = BUTR.CrashReport.Native.Pointer; +global using IntPtrImDrawList = BUTR.CrashReport.Native.Pointer; +global using IntPtrImFont = BUTR.CrashReport.Native.Pointer; +global using IntPtrImFontAtlas = BUTR.CrashReport.Native.Pointer; +global using IntPtrImFontConfig = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiIO = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiListClipper = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiStyle = BUTR.CrashReport.Native.Pointer; +global using IntPtrImGuiViewport = BUTR.CrashReport.Native.Pointer; +global using IntPtrInt32 = BUTR.CrashReport.Native.Pointer; +global using IntPtrIntPtr = BUTR.CrashReport.Native.Pointer; +global using IntPtrVector2 = BUTR.CrashReport.Native.Pointer; +global using IntPtrVector4 = BUTR.CrashReport.Native.Pointer; +global using IntPtrVoid = BUTR.CrashReport.Native.Pointer; +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Guard.cs b/src/BUTR.CrashReport.CImGui/Guard.cs new file mode 100644 index 0000000..0ee0cae --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Guard.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; + +namespace ImGui; + +internal static class Guard +{ + public static void ThrowIndexOutOfRangeException(int index, int count) + { + if (index < 0 || index >= count) + throw new IndexOutOfRangeException(); + } + public static void ThrowIndexOutOfRangeException(TEnum index, TEnum count) + { + var indexInt = Unsafe.As(ref index); + var countInt = Unsafe.As(ref count); + if (indexInt < 0 || indexInt >= countInt) + throw new IndexOutOfRangeException(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/README.md b/src/BUTR.CrashReport.CImGui/README.md similarity index 100% rename from src/BUTR.CrashReport.Renderer.ImGui/ImGui/README.md rename to src/BUTR.CrashReport.CImGui/README.md diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImDrawDataWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImDrawDataWrapper.cs new file mode 100644 index 0000000..937be78 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImDrawDataWrapper.cs @@ -0,0 +1,45 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImDrawDataWrapper +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImDrawData* NativePtr { get; } + + public ImDrawDataWrapper(CmGui imGui, ImGuiNET.ImDrawData* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public ref bool Valid => ref Unsafe.AsRef(&NativePtr->Valid); + public ref int CmdListsCount => ref Unsafe.AsRef(&NativePtr->CmdListsCount); + public ref int TotalIdxCount => ref Unsafe.AsRef(&NativePtr->TotalIdxCount); + public ref int TotalVtxCount => ref Unsafe.AsRef(&NativePtr->TotalVtxCount); + public ref Vector2 DisplayPos => ref Unsafe.AsRef(&NativePtr->DisplayPos); + public ref Vector2 DisplaySize => ref Unsafe.AsRef(&NativePtr->DisplaySize); + public ref Vector2 FramebufferScale => ref Unsafe.AsRef(&NativePtr->FramebufferScale); + + public void GetOwnerViewport(out ImGuiViewportWrapper ownerViewport) => ownerViewport = new(ImGui, NativePtr->OwnerViewport); + public void GetCmdLists(out ImVectorRefImDrawListRef cmdLists) => cmdLists = new(NativePtr->CmdLists); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddDrawList(ImGuiNET.ImDrawListPtr drawList) => ImGui.ImDrawData_AddDrawList(NativePtr, drawList.NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DeIndexAllBuffers() => ImGui.ImDrawData_DeIndexAllBuffers(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ScaleClipRects(Vector2 fbScale) => ImGui.ImDrawData_ScaleClipRects(NativePtr, fbScale); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() => ImGui.ImDrawData_Clear(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Destroy() => ImGui.ImDrawData_destroy(NativePtr); + + public void Dispose() => Destroy(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImDrawListRef.cs b/src/BUTR.CrashReport.CImGui/Structures/ImDrawListRef.cs new file mode 100644 index 0000000..c6e908e --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImDrawListRef.cs @@ -0,0 +1,12 @@ +namespace ImGui.Structures; + +public readonly unsafe ref struct ImDrawListRef +{ + public readonly ImGuiNET.ImDrawList* NativePtr; + + public ImDrawListRef(ImGuiNET.ImDrawList* nativePtr) => NativePtr = nativePtr; + + public void GetVtxBuffer(out ImVectorRefImDrawVert vtxBuffer) => vtxBuffer = new(NativePtr->VtxBuffer); + public void GetIdxBuffer(out ImVectorRefUInt16 idxBuffer) => idxBuffer = new(NativePtr->IdxBuffer); + public void GetCmdBuffer(out ImVectorRefImDrawCmd cmdBuffer) => cmdBuffer = new(NativePtr->CmdBuffer); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImDrawListWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImDrawListWrapper.cs new file mode 100644 index 0000000..07c0729 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImDrawListWrapper.cs @@ -0,0 +1,38 @@ +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ImGui.Structures; + +public readonly unsafe struct ImDrawListWrapper : IImDrawList +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImDrawList* NativePtr { get; } + + public ImDrawListWrapper(CmGui imGui, ImGuiNET.ImDrawList* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddText(ref readonly Vector2 pos, uint col, ReadOnlySpan utf8Data) + { + var ptrStart = (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8Data)); + var endPtr = (byte*) Unsafe.Add(ptrStart, utf8Data.Length - 1); + ImGui.ImDrawList_AddText_Vec2(NativePtr, pos, col, ptrStart, endPtr); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddRect(ref readonly Vector2 min, ref readonly Vector2 max, uint col, float rounding) => + ImGui.ImDrawList_AddRect(NativePtr, min, max, col, rounding, ImGuiNET.ImDrawFlags.None, 1f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddRectFilled(ref readonly Vector2 min, ref readonly Vector2 max, uint col, float rounding) => + ImGui.ImDrawList_AddRectFilled(NativePtr, min, max, col, rounding, ImGuiNET.ImDrawFlags.None); + + public void Dispose() { } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImFontAtlasWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImFontAtlasWrapper.cs new file mode 100644 index 0000000..3cf7627 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImFontAtlasWrapper.cs @@ -0,0 +1,55 @@ +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImFontAtlasWrapper +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImFontAtlas* NativePtr { get; } + + public ImFontAtlasWrapper(CmGui imGui, ImGuiNET.ImFontAtlas* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public ref IntPtr TexID => ref Unsafe.AsRef(&NativePtr->TexID); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddFontDefault(out ImGuiNET.ImFontPtr imFontPtr) + { + ImGuiNET.ImFontConfig* font_cfg = null; + imFontPtr = new ImGuiNET.ImFontPtr((ImGuiNET.ImFont*) ImGui.ImFontAtlas_AddFontDefault(NativePtr, font_cfg)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddFontDefault(ImFontConfigWrapper config, out ImGuiNET.ImFontPtr imFontPtr) => imFontPtr = new((ImGuiNET.ImFont*) ImGui.ImFontAtlas_AddFontDefault(NativePtr, config.NativePtr)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GetTexDataAsRGBA32(out IntPtr out_pixels, out int out_width, out int out_height, out int out_bytes_per_pixel) + { + Unsafe.SkipInit(out out_pixels); + Unsafe.SkipInit(out out_width); + Unsafe.SkipInit(out out_height); + Unsafe.SkipInit(out out_bytes_per_pixel); + ImGui.ImFontAtlas_GetTexDataAsRGBA32( + NativePtr, + (IntPtr*) Unsafe.AsPointer(ref out_pixels), + (int*) Unsafe.AsPointer(ref out_width), + (int*) Unsafe.AsPointer(ref out_height), + (int*) Unsafe.AsPointer(ref out_bytes_per_pixel)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetTexID(IntPtr id) => ImGui.ImFontAtlas_SetTexID(NativePtr, id); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ClearTexData() => ImGui.ImFontAtlas_ClearTexData(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Destroy() => ImGui.ImFontAtlas_destroy(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() => Destroy(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImFontConfigWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImFontConfigWrapper.cs new file mode 100644 index 0000000..2ce3b96 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImFontConfigWrapper.cs @@ -0,0 +1,44 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImFontConfigWrapper +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImFontConfig* NativePtr { get; } + + public ImFontConfigWrapper(CmGui imGui, ImGuiNET.ImFontConfig* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public IntPtr FontData { get => (IntPtr) NativePtr->FontData; set => NativePtr->FontData = (void*) value; } + public ref int FontDataSize => ref Unsafe.AsRef(&NativePtr->FontDataSize); + public ref bool FontDataOwnedByAtlas => ref Unsafe.AsRef(&NativePtr->FontDataOwnedByAtlas); + public ref int FontNo => ref Unsafe.AsRef(&NativePtr->FontNo); + public ref float SizePixels => ref Unsafe.AsRef(&NativePtr->SizePixels); + public ref int OversampleH => ref Unsafe.AsRef(&NativePtr->OversampleH); + public ref int OversampleV => ref Unsafe.AsRef(&NativePtr->OversampleV); + public ref bool PixelSnapH => ref Unsafe.AsRef(&NativePtr->PixelSnapH); + public ref Vector2 GlyphExtraSpacing => ref Unsafe.AsRef(&NativePtr->GlyphExtraSpacing); + public ref Vector2 GlyphOffset => ref Unsafe.AsRef(&NativePtr->GlyphOffset); + public IntPtr GlyphRanges { get => (IntPtr) NativePtr->GlyphRanges; set => NativePtr->GlyphRanges = (ushort*) value; } + public ref float GlyphMinAdvanceX => ref Unsafe.AsRef(&NativePtr->GlyphMinAdvanceX); + public ref float GlyphMaxAdvanceX => ref Unsafe.AsRef(&NativePtr->GlyphMaxAdvanceX); + public ref bool MergeMode => ref Unsafe.AsRef(&NativePtr->MergeMode); + public ref uint FontBuilderFlags => ref Unsafe.AsRef(&NativePtr->FontBuilderFlags); + public ref float RasterizerMultiply => ref Unsafe.AsRef(&NativePtr->RasterizerMultiply); + public ref float RasterizerDensity => ref Unsafe.AsRef(&NativePtr->RasterizerDensity); + public ref ushort EllipsisChar => ref Unsafe.AsRef(&NativePtr->EllipsisChar); + public RangeAccessorRef Name => new(NativePtr->Name, 40); + public ImGuiNET.ImFontPtr DstFont => new(NativePtr->DstFont); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Destroy() => ImGui.ImFontConfig_destroy(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() => Destroy(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImGuiIOWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImGuiIOWrapper.cs new file mode 100644 index 0000000..bcad9a0 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImGuiIOWrapper.cs @@ -0,0 +1,74 @@ +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe struct ImGuiIOWrapper : IImGuiIO +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImGuiIO* NativePtr { get; } + + public ImGuiIOWrapper(CmGui imGui, ImGuiNET.ImGuiIO* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public ref bool KeyCtrl => ref Unsafe.AsRef(&NativePtr->KeyCtrl); + public ref bool KeyShift => ref Unsafe.AsRef(&NativePtr->KeyShift); + public ref bool KeyAlt => ref Unsafe.AsRef(&NativePtr->KeyAlt); + public ref bool KeySuper => ref Unsafe.AsRef(&NativePtr->KeySuper); + public ref bool ConfigMacOSXBehaviors => ref Unsafe.AsRef(&NativePtr->ConfigMacOSXBehaviors); + public ref bool WantCaptureKeyboard => ref Unsafe.AsRef(&NativePtr->WantCaptureKeyboard); + public ref bool WantCaptureMouse => ref Unsafe.AsRef(&NativePtr->WantCaptureMouse); + public ref bool WantTextInput => ref Unsafe.AsRef(&NativePtr->WantTextInput); + + public ref ImGuiNET.ImGuiConfigFlags ConfigFlags => ref Unsafe.AsRef(&NativePtr->ConfigFlags); + public ref ImGuiNET.ImGuiBackendFlags BackendFlags => ref Unsafe.AsRef(&NativePtr->BackendFlags); + public ref Vector2 DisplaySize => ref Unsafe.AsRef(&NativePtr->DisplaySize); + public ref float DeltaTime => ref Unsafe.AsRef(&NativePtr->DeltaTime); + public ref Vector2 DisplayFramebufferScale => ref Unsafe.AsRef(&NativePtr->DisplayFramebufferScale); + public ref bool MouseDrawCursor => ref Unsafe.AsRef(&NativePtr->MouseDrawCursor); + public ref IntPtr GetClipboardTextFn => ref Unsafe.AsRef(&NativePtr->GetClipboardTextFn); + public ref IntPtr SetClipboardTextFn => ref Unsafe.AsRef(&NativePtr->SetClipboardTextFn); + public IntPtr ClipboardUserData { get => (IntPtr) NativePtr->ClipboardUserData; set => NativePtr->ClipboardUserData = (void*) value; } + public ref bool WantSetMousePos => ref Unsafe.AsRef(&NativePtr->WantSetMousePos); + public ref Vector2 MousePos => ref Unsafe.AsRef(&NativePtr->MousePos); + public ref float MouseWheel => ref Unsafe.AsRef(&NativePtr->MouseWheel); + public ref float MouseWheelH => ref Unsafe.AsRef(&NativePtr->MouseWheelH); + + public void GetFonts(out ImFontAtlasWrapper fonts) => fonts = new(ImGui, NativePtr->Fonts); + public void GetMouseDown(out RangeAccessorRef mouseDown) => mouseDown = new(NativePtr->MouseDown, ImGuiNET.ImGuiMouseButton.COUNT); + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddInputCharacter(uint c) => ImGui.ImGuiIO_AddInputCharacter(NativePtr, c); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddInputCharacterUTF8(byte* c) => ImGui.ImGuiIO_AddInputCharactersUTF8(NativePtr, c); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddKeyEvent(ImGuiNET.ImGuiKey key, bool down) => ImGui.ImGuiIO_AddKeyEvent(NativePtr, key, Unsafe.As(ref down)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetKeyEventNativeData(ImGuiNET.ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1) => ImGui.ImGuiIO_SetKeyEventNativeData(NativePtr, key, native_keycode, native_scancode, native_legacy_index); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddMouseButtonEvent(ImGuiNET.ImGuiMouseButton button, bool down) => ImGui.ImGuiIO_AddMouseButtonEvent(NativePtr, button, Unsafe.As(ref down)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddMouseWheelEvent(float wheel_x, float wheel_y) => ImGui.ImGuiIO_AddMouseWheelEvent(NativePtr, wheel_x, wheel_y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddMousePosEvent(float x, float y) => ImGui.ImGuiIO_AddMousePosEvent(NativePtr, x, y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddFocusEvent(bool focused) => ImGui.ImGuiIO_AddFocusEvent(NativePtr, Unsafe.As(ref focused)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Destroy() => ImGui.ImGuiIO_destroy(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() => Destroy(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImGuiListClipperWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImGuiListClipperWrapper.cs new file mode 100644 index 0000000..ee1213d --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImGuiListClipperWrapper.cs @@ -0,0 +1,49 @@ +using BUTR.CrashReport.ImGui.Structures; + +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe struct ImGuiListClipperWrapper : IImGuiListClipper +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImGuiListClipper* NativePtr { get; } + + public ImGuiListClipperWrapper(CmGui imGui, ImGuiNET.ImGuiListClipper* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public ref IntPtr Ctx => ref Unsafe.AsRef(&NativePtr->Ctx); + public ref int DisplayStart => ref Unsafe.AsRef(&NativePtr->DisplayStart); + public ref int DisplayEnd => ref Unsafe.AsRef(&NativePtr->DisplayEnd); + public ref int ItemsCount => ref Unsafe.AsRef(&NativePtr->ItemsCount); + public ref float ItemsHeight => ref Unsafe.AsRef(&NativePtr->ItemsHeight); + public ref float StartPosY => ref Unsafe.AsRef(&NativePtr->StartPosY); + public ref double StartSeekOffsetY => ref Unsafe.AsRef(&NativePtr->StartSeekOffsetY); + public IntPtr TempData { get => (IntPtr) NativePtr->TempData; set => NativePtr->TempData = (void*) value; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Begin(int items_count) + { + float items_height = -1f; + ImGui.ImGuiListClipper_Begin(NativePtr, items_count, items_height); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Begin(int items_count, float items_height) => ImGui.ImGuiListClipper_Begin(NativePtr, items_count, items_height); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void End() => ImGui.ImGuiListClipper_End(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Step() => ImGui.ImGuiListClipper_Step(NativePtr) > 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Destroy() => ImGui.ImGuiListClipper_destroy(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() => Destroy(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImGuiStyleWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImGuiStyleWrapper.cs new file mode 100644 index 0000000..fa6c744 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImGuiStyleWrapper.cs @@ -0,0 +1,86 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe struct ImGuiStyleWrapper : IImGuiStyle> +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImGuiStyle* NativePtr { get; } + + public ImGuiStyleWrapper(CmGui imGui, ImGuiNET.ImGuiStyle* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public ref float Alpha => ref Unsafe.AsRef(&NativePtr->Alpha); + public ref float DisabledAlpha => ref Unsafe.AsRef(&NativePtr->DisabledAlpha); + public ref Vector2 WindowPadding => ref Unsafe.AsRef(&NativePtr->WindowPadding); + public ref float WindowRounding => ref Unsafe.AsRef(&NativePtr->WindowRounding); + public ref float WindowBorderSize => ref Unsafe.AsRef(&NativePtr->WindowBorderSize); + public ref Vector2 WindowMinSize => ref Unsafe.AsRef(&NativePtr->WindowMinSize); + public ref Vector2 WindowTitleAlign => ref Unsafe.AsRef(&NativePtr->WindowTitleAlign); + public ref ImGuiDir WindowMenuButtonPosition => ref Unsafe.AsRef(&NativePtr->WindowMenuButtonPosition); + public ref float ChildRounding => ref Unsafe.AsRef(&NativePtr->ChildRounding); + public ref float ChildBorderSize => ref Unsafe.AsRef(&NativePtr->ChildBorderSize); + public ref float PopupRounding => ref Unsafe.AsRef(&NativePtr->PopupRounding); + public ref float PopupBorderSize => ref Unsafe.AsRef(&NativePtr->PopupBorderSize); + public ref Vector2 FramePadding => ref Unsafe.AsRef(&NativePtr->FramePadding); + public ref float FrameRounding => ref Unsafe.AsRef(&NativePtr->FrameRounding); + public ref float FrameBorderSize => ref Unsafe.AsRef(&NativePtr->FrameBorderSize); + public ref Vector2 ItemSpacing => ref Unsafe.AsRef(&NativePtr->ItemSpacing); + public ref Vector2 ItemInnerSpacing => ref Unsafe.AsRef(&NativePtr->ItemInnerSpacing); + public ref Vector2 CellPadding => ref Unsafe.AsRef(&NativePtr->CellPadding); + public ref Vector2 TouchExtraPadding => ref Unsafe.AsRef(&NativePtr->TouchExtraPadding); + public ref float IndentSpacing => ref Unsafe.AsRef(&NativePtr->IndentSpacing); + public ref float ColumnsMinSpacing => ref Unsafe.AsRef(&NativePtr->ColumnsMinSpacing); + public ref float ScrollbarSize => ref Unsafe.AsRef(&NativePtr->ScrollbarSize); + public ref float ScrollbarRounding => ref Unsafe.AsRef(&NativePtr->ScrollbarRounding); + public ref float GrabMinSize => ref Unsafe.AsRef(&NativePtr->GrabMinSize); + public ref float GrabRounding => ref Unsafe.AsRef(&NativePtr->GrabRounding); + public ref float LogSliderDeadzone => ref Unsafe.AsRef(&NativePtr->LogSliderDeadzone); + public ref float TabRounding => ref Unsafe.AsRef(&NativePtr->TabRounding); + public ref float TabBorderSize => ref Unsafe.AsRef(&NativePtr->TabBorderSize); + public ref float TabMinWidthForCloseButton => ref Unsafe.AsRef(&NativePtr->TabMinWidthForCloseButton); + public ref float TabBarBorderSize => ref Unsafe.AsRef(&NativePtr->TabBarBorderSize); + public ref float TabBarOverlineSize => ref Unsafe.AsRef(&NativePtr->TabBarOverlineSize); + public ref float TableAngledHeadersAngle => ref Unsafe.AsRef(&NativePtr->TableAngledHeadersAngle); + public ref Vector2 TableAngledHeadersTextAlign => ref Unsafe.AsRef(&NativePtr->TableAngledHeadersTextAlign); + public ref ImGuiDir ColorButtonPosition => ref Unsafe.AsRef(&NativePtr->ColorButtonPosition); + public ref Vector2 ButtonTextAlign => ref Unsafe.AsRef(&NativePtr->ButtonTextAlign); + public ref Vector2 SelectableTextAlign => ref Unsafe.AsRef(&NativePtr->SelectableTextAlign); + public ref float SeparatorTextBorderSize => ref Unsafe.AsRef(&NativePtr->SeparatorTextBorderSize); + public ref Vector2 SeparatorTextAlign => ref Unsafe.AsRef(&NativePtr->SeparatorTextAlign); + public ref Vector2 SeparatorTextPadding => ref Unsafe.AsRef(&NativePtr->SeparatorTextPadding); + public ref Vector2 DisplayWindowPadding => ref Unsafe.AsRef(&NativePtr->DisplayWindowPadding); + public ref Vector2 DisplaySafeAreaPadding => ref Unsafe.AsRef(&NativePtr->DisplaySafeAreaPadding); + public ref float DockingSeparatorSize => ref Unsafe.AsRef(&NativePtr->DockingSeparatorSize); + public ref float MouseCursorScale => ref Unsafe.AsRef(&NativePtr->MouseCursorScale); + public ref bool AntiAliasedLines => ref Unsafe.AsRef(&NativePtr->AntiAliasedLines); + public ref bool AntiAliasedLinesUseTex => ref Unsafe.AsRef(&NativePtr->AntiAliasedLinesUseTex); + public ref bool AntiAliasedFill => ref Unsafe.AsRef(&NativePtr->AntiAliasedFill); + public ref float CurveTessellationTol => ref Unsafe.AsRef(&NativePtr->CurveTessellationTol); + public ref float CircleTessellationMaxError => ref Unsafe.AsRef(&NativePtr->CircleTessellationMaxError); + public ref float HoverStationaryDelay => ref Unsafe.AsRef(&NativePtr->HoverStationaryDelay); + public ref float HoverDelayShort => ref Unsafe.AsRef(&NativePtr->HoverDelayShort); + public ref float HoverDelayNormal => ref Unsafe.AsRef(&NativePtr->HoverDelayNormal); + public ref ImGuiHoveredFlags HoverFlagsForTooltipMouse => ref Unsafe.AsRef(&NativePtr->HoverFlagsForTooltipMouse); + public ref ImGuiHoveredFlags HoverFlagsForTooltipNav => ref Unsafe.AsRef(&NativePtr->HoverFlagsForTooltipNav); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GetColors(out RangeAccessorRef colors) => colors = new(&NativePtr->Colors_0, ImGuiCol.COUNT); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ScaleAllSizes(float scaleFactor) => ImGui.ImGuiStyle_ScaleAllSizes(NativePtr, scaleFactor); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Destroy() => ImGui.ImGuiStyle_destroy(NativePtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() => Destroy(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImGuiViewportWrapper.cs b/src/BUTR.CrashReport.CImGui/Structures/ImGuiViewportWrapper.cs new file mode 100644 index 0000000..b0e6b52 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImGuiViewportWrapper.cs @@ -0,0 +1,23 @@ +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe struct ImGuiViewportWrapper : IImGuiViewport +{ + private CmGui ImGui { get; } + + public ImGuiNET.ImGuiViewport* NativePtr { get; } + + public ImGuiViewportWrapper(CmGui imGui, ImGuiNET.ImGuiViewport* nativePtr) + { + ImGui = imGui; + NativePtr = nativePtr; + } + + public ref uint ID => ref Unsafe.AsRef(&NativePtr->ID); + public ref Vector2 WorkPos => ref Unsafe.AsRef(&NativePtr->WorkPos); + public ref Vector2 WorkSize => ref Unsafe.AsRef(&NativePtr->WorkSize); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImVectorRef.cs b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRef.cs new file mode 100644 index 0000000..b206f20 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRef.cs @@ -0,0 +1,34 @@ +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImVectorRef where T : unmanaged +{ + public readonly int Size; + public readonly int Capacity; + public readonly T* Data; + + public ImVectorRef(ImGuiNET.ImVector vector) + { + Size = vector.Size; + Capacity = vector.Capacity; + Data = (T*) vector.Data; + } + + public ImVectorRef(int size, int capacity, IntPtr data) + { + Size = size; + Capacity = capacity; + Data = (T*) data; + } + + public ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Size); + return ref Unsafe.Add(ref Unsafe.AsRef(Data), index); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawCmd.cs b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawCmd.cs new file mode 100644 index 0000000..3d2ace7 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawCmd.cs @@ -0,0 +1,34 @@ +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImVectorRefImDrawCmd +{ + public readonly int Size; + public readonly int Capacity; + public readonly ImGuiNET.ImDrawCmd* Data; + + public ImVectorRefImDrawCmd(ImGuiNET.ImVector vector) + { + Size = vector.Size; + Capacity = vector.Capacity; + Data = (ImGuiNET.ImDrawCmd*) vector.Data; + } + + public ImVectorRefImDrawCmd(int size, int capacity, IntPtr data) + { + Size = size; + Capacity = capacity; + Data = (ImGuiNET.ImDrawCmd*) data; + } + + public ref ImGuiNET.ImDrawCmd this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Size); + return ref *(Data + index); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawListRef.cs b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawListRef.cs new file mode 100644 index 0000000..809e3e2 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawListRef.cs @@ -0,0 +1,34 @@ +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImVectorRefImDrawListRef +{ + public readonly int Size; + public readonly int Capacity; + public readonly ImDrawListRef* Data; + + public ImVectorRefImDrawListRef(ImGuiNET.ImVector vector) + { + Size = vector.Size; + Capacity = vector.Capacity; + Data = (ImDrawListRef*) vector.Data; + } + + public ImVectorRefImDrawListRef(int size, int capacity, IntPtr data) + { + Size = size; + Capacity = capacity; + Data = (ImDrawListRef*) data; + } + + public ref ImDrawListRef this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Size); + return ref *(Data + index); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawVert.cs b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawVert.cs new file mode 100644 index 0000000..72042a5 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefImDrawVert.cs @@ -0,0 +1,36 @@ +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImVectorRefImDrawVert +{ + public readonly int Size; + public readonly int Capacity; + public readonly ImGuiNET.ImDrawVert* Data; + + public ImVectorRefImDrawVert(ImGuiNET.ImVector vector) + { + Size = vector.Size; + Capacity = vector.Capacity; + Data = (ImGuiNET.ImDrawVert*) vector.Data; + } + + public ImVectorRefImDrawVert(int size, int capacity, IntPtr data) + { + Size = size; + Capacity = capacity; + Data = (ImGuiNET.ImDrawVert*) data; + } + + public ref ImGuiNET.ImDrawVert this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Size); + return ref *(Data + index); + } + } + + public Span AsSpan() => new(Data, Size); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefUInt16.cs b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefUInt16.cs new file mode 100644 index 0000000..3d11a93 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/ImVectorRefUInt16.cs @@ -0,0 +1,36 @@ +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe ref struct ImVectorRefUInt16 +{ + public readonly int Size; + public readonly int Capacity; + public readonly UInt16* Data; + + public ImVectorRefUInt16(ImGuiNET.ImVector vector) + { + Size = vector.Size; + Capacity = vector.Capacity; + Data = (UInt16*) vector.Data; + } + + public ImVectorRefUInt16(int size, int capacity, IntPtr data) + { + Size = size; + Capacity = capacity; + Data = (UInt16*) data; + } + + public ref UInt16 this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Size); + return ref *(Data + index); + } + } + + public Span AsSpan() => new(Data, Size); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/RangeAccessorRef.cs b/src/BUTR.CrashReport.CImGui/Structures/RangeAccessorRef.cs new file mode 100644 index 0000000..8a8c481 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/RangeAccessorRef.cs @@ -0,0 +1,53 @@ +using BUTR.CrashReport.ImGui.Structures; + +using System.Runtime.CompilerServices; + +namespace ImGui.Structures; + +public readonly unsafe struct RangeAccessorRef : IRangeAccessor where T : struct where TEnum : Enum +{ + public readonly void* Data; + public readonly TEnum Count; + + public RangeAccessorRef(IntPtr data, TEnum count) : this(data.ToPointer(), count) { } + + public RangeAccessorRef(void* data, TEnum count) + { + Data = data; + Count = count; + } + + public ref T this[TEnum index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Count); + return ref Unsafe.Add(ref Unsafe.AsRef(Data), Unsafe.As(ref index)); + } + } +} + +public readonly struct RangeAccessorRef : IRangeAccessor where T : struct +{ + public readonly unsafe void* Data; + public readonly int Count; + + public unsafe RangeAccessorRef(IntPtr data, int count) : this(data.ToPointer(), count) { } + + public unsafe RangeAccessorRef(void* data, int count) + { + Data = data; + Count = count; + } + + public unsafe ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Guard.ThrowIndexOutOfRangeException(index, Count); + return ref Unsafe.Add(ref Unsafe.AsRef(Data), index); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/Vector2Ref.cs b/src/BUTR.CrashReport.CImGui/Structures/Vector2Ref.cs new file mode 100644 index 0000000..d047cb7 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/Vector2Ref.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace ImGui.Structures; + +[StructLayout(LayoutKind.Sequential)] +public ref struct Vector2Ref(float X, float Y) +{ + public float X = X; + public float Y = Y; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/Structures/Vector4Ref.cs b/src/BUTR.CrashReport.CImGui/Structures/Vector4Ref.cs new file mode 100644 index 0000000..ee18a3b --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/Structures/Vector4Ref.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace ImGui.Structures; + +[StructLayout(LayoutKind.Sequential)] +public ref struct Vector4Ref(float X, float Y, float Z, float W) +{ + public float X = X; + public float Y = Y; + public float Z = Z; + public float W = W; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.CImGui/UnmanagedCallersOnlyAttribute.cs b/src/BUTR.CrashReport.CImGui/UnmanagedCallersOnlyAttribute.cs new file mode 100644 index 0000000..55419f0 --- /dev/null +++ b/src/BUTR.CrashReport.CImGui/UnmanagedCallersOnlyAttribute.cs @@ -0,0 +1,27 @@ +#if NETSTANDARD2_0 +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +// ReSharper disable once CheckNamespace +namespace System.Runtime.InteropServices; + +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +internal sealed class UnmanagedCallersOnlyAttribute : Attribute +{ + /// + /// Optional. If omitted, the runtime will use the default platform calling convention. + /// + /// + /// Supplied types must be from the official "System.Runtime.CompilerServices" namespace and + /// be of the form "CallConvXXX". + /// + public Type[]? CallConvs; + + /// + /// Optional. If omitted, no named export is emitted during compilation. + /// + public string? EntryPoint; +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/BUTR.CrashReport.Decompilers.csproj b/src/BUTR.CrashReport.Decompilers/BUTR.CrashReport.Decompilers.csproj index 6ce8a52..15959c4 100644 --- a/src/BUTR.CrashReport.Decompilers/BUTR.CrashReport.Decompilers.csproj +++ b/src/BUTR.CrashReport.Decompilers/BUTR.CrashReport.Decompilers.csproj @@ -2,27 +2,32 @@ netstandard2.0 - preview + latest enable + true + + $(DefineConstants);FeatureMemory;FeatureValueTuple;FeatureValueTask; - - - - - + + BUTR.CrashReport.Decompilers + BUTR.CrashReport.Decompilers + The decompiler files for the 'BUTR.CrashReport' package. Is not required with 'BUTR.CrashReport.ILMerged'. + MIT + https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png + butr crash report ilspy asmresolver reflection iced + - + + + + - - - - - - + + diff --git a/src/BUTR.CrashReport.Decompilers/Extensions/ITextOutputExtensions.cs b/src/BUTR.CrashReport.Decompilers/Extensions/ITextOutputExtensions.cs new file mode 100644 index 0000000..21ff36b --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Extensions/ITextOutputExtensions.cs @@ -0,0 +1,20 @@ +using ICSharpCode.Decompiler; + +using System; + +namespace BUTR.CrashReport.Decompilers.Extensions; + +internal static class ITextOutputExtensions +{ + public static void Write(this ITextOutput textOutput, ReadOnlySpan line) + { + for (var i = 0; i < line.Length; i++) + textOutput.Write(line[i]); + } + public static void WriteLine(this ITextOutput textOutput, ReadOnlySpan line) + { + for (var i = 0; i < line.Length; i++) + textOutput.Write(line[i]); + textOutput.WriteLine(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/StringBuilderExtensions.cs b/src/BUTR.CrashReport.Decompilers/Extensions/StringBuilderExtensions.cs similarity index 73% rename from src/BUTR.CrashReport.Decompilers/ILSpy/StringBuilderExtensions.cs rename to src/BUTR.CrashReport.Decompilers/Extensions/StringBuilderExtensions.cs index ed5d8f8..3fa4d2c 100644 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/StringBuilderExtensions.cs +++ b/src/BUTR.CrashReport.Decompilers/Extensions/StringBuilderExtensions.cs @@ -1,21 +1,21 @@ using System; using System.Text; -namespace BUTR.CrashReport.Decompilers.ILSpy; +namespace BUTR.CrashReport.Decompilers.Extensions; internal static class StringBuilderExtensions { public static int IndexOf(this StringBuilder sb, ReadOnlySpan value, int startIndex) { var length = value.Length; - var maxSearchLength = (sb.Length - length) + 1; + var maxSearchLength = sb.Length - length + 1; for (var i = startIndex; i < maxSearchLength; ++i) { if (sb[i] != value[0]) continue; var index = 1; - while (index < length && (sb[i + index] == value[index])) + while (index < length && sb[i + index] == value[index]) ++index; if (index == length) diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpILMixedLanguage.cs b/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpILMixedLanguage.cs index 7081401..58217bc 100644 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpILMixedLanguage.cs +++ b/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpILMixedLanguage.cs @@ -1,149 +1,129 @@ -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.OutputVisitor; -using ICSharpCode.Decompiler.CSharp.Syntax; +using BUTR.CrashReport.Decompilers.Extensions; +using BUTR.CrashReport.Decompilers.Sources; + +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using System; using System.Collections.Generic; -using System.IO; +using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; -using System.Text; using System.Threading; namespace BUTR.CrashReport.Decompilers.ILSpy; internal static class CSharpILMixedLanguage { - public static ReflectionDisassembler CreateDisassembler(ITextOutput output, CancellationToken ct) + internal class MixedMethodBodyDisassembler : MethodBodyDisassembler { - var methodBodyDisassembler = new MixedMethodBodyDisassembler(output, ct) - { - ShowMetadataTokens = false, - ShowMetadataTokensInBase10 = false, - ShowRawRVAOffsetAndBytes = false, - ShowSequencePoints = false, - DetectControlStructure = true, - }; + public int? LineStart { get; private set; } + public int? LineEnd { get; private set; } - return new(output, methodBodyDisassembler, ct) - { - ShowMetadataTokens = false, - ShowMetadataTokensInBase10 = false, - ShowRawRVAOffsetAndBytes = false, - ShowSequencePoints = false, - DetectControlStructure = true, - ExpandMemberDefinitions = false, - }; - } - - private static CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilerSettings settings, CancellationToken ct) - { - var resolver = new UniversalAssemblyResolver(null, false, module.DetectTargetFrameworkId(), module.DetectRuntimePack()); - return new CSharpDecompiler(module, resolver, settings) { CancellationToken = ct }; - } + private readonly int? _ilOffset; + private readonly SourceFile _csharpSource; + private readonly Dictionary _sequencePoints; - private static void WriteCode(TextWriter output, DecompilerSettings settings, SyntaxTree syntaxTree) - { - syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); - TokenWriter tokenWriter = new TextWriterTokenWriter(output) { IndentationString = settings.CSharpFormattingOptions.IndentationString }; - tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); - syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); - } - - private class MixedMethodBodyDisassembler : MethodBodyDisassembler - { - private static readonly char[] NewLine = Environment.NewLine.ToArray(); - private static readonly List Empty = new(); - - // list sorted by IL offset - private Dictionary? sequencePoints; - - // lines of raw c# source code - private StringBuilder? _stringBuilder; - private List<(int, int)>? _stringBuilderLinesIndices; - - private readonly CancellationToken _cancellationToken; - - public MixedMethodBodyDisassembler(ITextOutput output, CancellationToken ct) : base(output, ct) - { - _cancellationToken = ct; - } - - public override void Disassemble(MetadataFile module, MethodDefinitionHandle handle) + public MixedMethodBodyDisassembler(PlainTextOutput output, int? ilOffset, SourceLocation? csharpSource, CancellationToken ct) : base(output, ct) { - try - { - var settings = new DecompilerSettings(LanguageVersion.Latest); - var decompiler = CreateDecompiler(module, settings, _cancellationToken); - var syntaxTree = decompiler.Decompile(handle); - - using var csharpOutput = new StringWriter(); - WriteCode(csharpOutput, settings, syntaxTree); - var mapping = decompiler.CreateSequencePoints(syntaxTree).FirstOrDefault(kvp => (kvp.Key.MoveNextMethod ?? kvp.Key.Method)?.MetadataToken == handle); - sequencePoints = (mapping.Value ?? Empty).ToDictionary(x => x.Offset, x => x); - - _stringBuilder = csharpOutput.GetStringBuilder(); - IndexStringBuilder(); - - base.Disassemble(module, handle); - } - finally - { - sequencePoints = null; - _stringBuilder = null; - _stringBuilderLinesIndices = null; - } + _ilOffset = ilOffset; + _csharpSource = csharpSource?.SourceFile ?? new SourceFileNone(); + _sequencePoints = csharpSource?.SequencePoints.ToDictionary(x => x.Offset, x => x) ?? []; } - private void IndexStringBuilder() - { - if (_stringBuilder is null) return; - - _stringBuilderLinesIndices = new List<(int, int)>(); - var previousIdx = 0; - while (_stringBuilder.IndexOf(NewLine, previousIdx) is var idx and not -1) - { - _stringBuilderLinesIndices.Add((previousIdx, idx)); - previousIdx = idx + NewLine.Length; - } - - _stringBuilderLinesIndices.Add((previousIdx, _stringBuilder.Length)); - } - - // Is called within base.Disassemble protected override void WriteInstruction(ITextOutput output, MetadataFile metadataFile, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva) { - if (output is not PlainTextOutput2 plainTextOutput2) return; - if (_stringBuilder is null || _stringBuilderLinesIndices is null) return; - if (sequencePoints is null) return; + var lineOffset = 0; - if (sequencePoints.TryGetValue(blob.Offset, out var info)) + if (_sequencePoints.TryGetValue(blob.Offset, out var info)) { - if (info.IsHidden) + if (_csharpSource.Kind is SourceKind.None or SourceKind.SourceLink) { - plainTextOutput2.WriteLine("// (no C# code)"); + output.WriteLine("// (C# source code not found)"); + } + else if (info.IsHidden) + { + output.WriteLine("// (no C# code)"); + lineOffset++; } else { - for (var line = info.StartLine; line <= info.EndLine; line++) + for (var lineNumber = info.StartLine; lineNumber <= info.EndLine; lineNumber++) { - //if (_stringBuilderLinesIndices.Count < line) continue; // TODO: - - plainTextOutput2.WriteLine(); - plainTextOutput2.Write("// "); - - var (start, end) = _stringBuilderLinesIndices[line - 1]; - var length = end - start; - plainTextOutput2.Write(_stringBuilder, start, length); - plainTextOutput2.WriteLine(); + var text = _csharpSource.GetMethodLine(lineNumber, lineNumber); + var startColumn = 1; + var endColumn = text.Length + 1; + if (lineNumber == info.StartLine) + startColumn = info.StartColumn; + if (lineNumber == info.EndLine) + endColumn = info.EndColumn; + WriteHighlightedCommentLine(output, text, startColumn - 1, endColumn - 1, info.StartLine == info.EndLine); + + /* + if (_previousLineNumber == lineNumber) continue; + + output.Write("// "); + output.Write(_csharpSource.GetMethodLine(lineNumber, lineNumber)); + lineOffset++; + + _previousLineNumber = lineNumber; + */ } } } - base.WriteInstruction(plainTextOutput2, metadataFile, methodHandle, ref blob, methodRva); + base.WriteInstruction(output, metadataFile, methodHandle, ref blob, methodRva); + + if (blob.Offset == _ilOffset) + { + var pto = (PlainTextOutput) output; + LineStart = pto.Location.Line + lineOffset; + LineEnd = pto.Location.Line + lineOffset; + } + } + + private void WriteHighlightedCommentLine(ITextOutput output, string text, int startColumn, int endColumn, bool isSingleLine) + { + if (startColumn > text.Length) + { + Debug.Fail("startColumn is invalid"); + startColumn = text.Length; + } + if (endColumn > text.Length) + { + Debug.Fail("endColumn is invalid"); + endColumn = text.Length; + } + output.Write("// "); + output.Write(isSingleLine ? text.AsSpan(0, startColumn).TrimStart() : text.AsSpan(0, startColumn)); + output.Write(text.AsSpan(startColumn, endColumn - startColumn)); + output.Write(text.AsSpan(endColumn)); + output.WriteLine(); } } + + public static MixedMethodBodyDisassembler CreateMethodBodyDisassembler(PlainTextOutput output, int? ilOffset, SourceLocation? csharpSource, CancellationToken ct) + { + return new MixedMethodBodyDisassembler(output, ilOffset, csharpSource, ct) + { + ShowMetadataTokens = false, + ShowMetadataTokensInBase10 = false, + ShowRawRVAOffsetAndBytes = false, + ShowSequencePoints = false, + DetectControlStructure = true, + }; + } + public static ReflectionDisassembler CreateDisassembler(PlainTextOutput output, MethodBodyDisassembler methodBodyDisassembler, CancellationToken ct) + { + return new(output, methodBodyDisassembler, ct) + { + ShowMetadataTokens = false, + ShowMetadataTokensInBase10 = false, + ShowRawRVAOffsetAndBytes = false, + ShowSequencePoints = false, + DetectControlStructure = true, + ExpandMemberDefinitions = false, + }; + } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpLanguage.cs b/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpLanguage.cs deleted file mode 100644 index e8d6c99..0000000 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/CSharpLanguage.cs +++ /dev/null @@ -1,154 +0,0 @@ -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.OutputVisitor; -using ICSharpCode.Decompiler.CSharp.Syntax; -using ICSharpCode.Decompiler.CSharp.Transforms; -using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.Decompiler.Output; -using ICSharpCode.Decompiler.TypeSystem; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection.Metadata; -using System.Threading; - -namespace BUTR.CrashReport.Decompilers.ILSpy; - -internal class CSharpLanguage : Language -{ - private const int _transformCount = int.MaxValue; - - public static CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilerSettings settings, CancellationToken ct) - { - var resolver = new UniversalAssemblyResolver(null, false, module.DetectTargetFrameworkId(), module.DetectRuntimePack()); - var decompiler = new CSharpDecompiler(module, resolver, settings) { CancellationToken = ct }; - while (decompiler.AstTransforms.Count >= _transformCount) - decompiler.AstTransforms.RemoveAt(decompiler.AstTransforms.Count - 1); - decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); - return decompiler; - } - - private static void WriteCode(ITextOutput output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem) - { - syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); - output.IndentationString = settings.CSharpFormattingOptions.IndentationString; - TokenWriter tokenWriter = new TextTokenWriter(output, settings, typeSystem); - syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); - } - - public override void DecompileMethod(IMethod method, ITextOutput output, DecompilerSettings settings) - { - if (method.ParentModule?.MetadataFile is null) - return; - - var assembly = method.ParentModule.MetadataFile; - var decompiler = CreateDecompiler(assembly, settings, CancellationToken.None); - WriteCommentLine(output, assembly.FullName); - WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); - - if (decompiler.TypeSystem.MainModule.ResolveEntity(method.MetadataToken) is not IMethod methodDefinition) - return; - - if (methodDefinition.DeclaringTypeDefinition is not null && methodDefinition.IsConstructor && methodDefinition.DeclaringType.IsReferenceType != false) - { - var members = CollectFieldsAndCtors(methodDefinition.DeclaringTypeDefinition, methodDefinition.IsStatic); - decompiler.AstTransforms.Add(new SelectCtorTransform(methodDefinition)); - WriteCode(output, settings, decompiler.Decompile(members), decompiler.TypeSystem); - } - else - { - WriteCode(output, settings, decompiler.Decompile(method.MetadataToken), decompiler.TypeSystem); - } - } - - private static List CollectFieldsAndCtors(ITypeDefinition type, bool isStatic) - { - var members = new List(); - members.AddRange(type.Fields.Where(field => !field.MetadataToken.IsNil && field.IsStatic == isStatic).Select(field => field.MetadataToken)); - members.AddRange(type.Methods.Where(ctor => !ctor.MetadataToken.IsNil && ctor.IsConstructor && ctor.IsStatic == isStatic).Select(ctor => ctor.MetadataToken)); - return members; - } - - private static CSharpAmbience CreateAmbience() => new() { ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList }; - - public override string TypeToString(IType type, bool includeNamespace) - { - if (type == null) - throw new ArgumentNullException(nameof(type)); - - var ambience = CreateAmbience(); - if (includeNamespace) - { - ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedTypeNames; - ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedEntityNames; - } - - if (type is ITypeDefinition definition) - return ambience.ConvertSymbol(definition); - // HACK : UnknownType is not supported by CSharpAmbience. - - if (type.Kind == TypeKind.Unknown) - return (includeNamespace ? type.FullName : type.Name) + (type.TypeParameterCount > 0 ? "<" + string.Join(", ", type.TypeArguments.Select(t => t.Name)) + ">" : ""); - - return ambience.ConvertType(type); - } - - private class SelectCtorTransform : IAstTransform - { - private readonly IMethod _ctor; - private readonly HashSet _removedSymbols = new(); - - public SelectCtorTransform(IMethod ctor) => _ctor = ctor; - - public void Run(AstNode rootNode, TransformContext context) - { - ConstructorDeclaration? ctorDecl = null; - foreach (var node in rootNode.Children) - { - switch (node) - { - case ConstructorDeclaration ctor: - if (Equals(ctor.GetSymbol(), _ctor)) - { - ctorDecl = ctor; - } - else - { - // remove other ctors - ctor.Remove(); - _removedSymbols.Add(ctor.GetSymbol()); - } - break; - case FieldDeclaration fd: - // Remove any fields without initializers - if (fd.Variables.All(v => v.Initializer.IsNull)) - { - fd.Remove(); - _removedSymbols.Add(fd.GetSymbol()); - } - break; - } - } - if (ctorDecl?.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) - { - // remove all fields - foreach (var node in rootNode.Children) - { - switch (node) - { - case FieldDeclaration fd: - fd.Remove(); - _removedSymbols.Add(fd.GetSymbol()); - break; - } - } - } - foreach (var node in rootNode.Children) - { - if (node is Comment && _removedSymbols.Contains(node.GetSymbol())) - node.Remove(); - } - } - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/ILLanguage.cs b/src/BUTR.CrashReport.Decompilers/ILSpy/ILLanguage.cs deleted file mode 100644 index 03f81af..0000000 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/ILLanguage.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.Disassembler; - -using System.Threading; - -namespace BUTR.CrashReport.Decompilers.ILSpy; - -internal class ILLanguage -{ - public static ReflectionDisassembler CreateDisassembler(ITextOutput output, CancellationToken ct) - { - return new(output, ct) - { - ShowMetadataTokens = false, - ShowMetadataTokensInBase10 = false, - ShowRawRVAOffsetAndBytes = false, - ShowSequencePoints = false, - DetectControlStructure = true, - ExpandMemberDefinitions = false, - }; - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/Language.cs b/src/BUTR.CrashReport.Decompilers/ILSpy/Language.cs deleted file mode 100644 index 7685bb9..0000000 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/Language.cs +++ /dev/null @@ -1,242 +0,0 @@ -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; - -using System.Reflection.Metadata; -using System.Text; - -namespace BUTR.CrashReport.Decompilers.ILSpy; - -internal abstract class Language -{ - public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilerSettings settings) - { - if (method.DeclaringTypeDefinition is not null) - WriteCommentLine(output, $"{TypeToString(method.DeclaringTypeDefinition, includeNamespace: true)}.{method.Name}"); - } - - public virtual void WriteCommentLine(ITextOutput output, string comment) - { - output.WriteLine("// " + comment); - } - - #region TypeToString - /// - /// Converts a type definition, reference or specification into a string. This method is used by tree nodes and search results. - /// - public virtual string TypeToString(IType type, bool includeNamespace) - { - var visitor = new TypeToStringVisitor(includeNamespace); - type.AcceptVisitor(visitor); - return visitor.ToString(); - } - - class TypeToStringVisitor : TypeVisitor - { - readonly bool includeNamespace; - readonly StringBuilder builder; - - public override string ToString() - { - return builder.ToString(); - } - - public TypeToStringVisitor(bool includeNamespace) - { - this.includeNamespace = includeNamespace; - builder = new StringBuilder(); - } - - public override IType VisitArrayType(ArrayType type) - { - base.VisitArrayType(type); - builder.Append('['); - builder.Append(',', type.Dimensions - 1); - builder.Append(']'); - return type; - } - - public override IType VisitByReferenceType(ByReferenceType type) - { - base.VisitByReferenceType(type); - builder.Append('&'); - return type; - } - - public override IType VisitModOpt(ModifiedType type) - { - type.ElementType.AcceptVisitor(this); - builder.Append(" modopt("); - type.Modifier.AcceptVisitor(this); - builder.Append(")"); - return type; - } - - public override IType VisitModReq(ModifiedType type) - { - type.ElementType.AcceptVisitor(this); - builder.Append(" modreq("); - type.Modifier.AcceptVisitor(this); - builder.Append(")"); - return type; - } - - public override IType VisitPointerType(PointerType type) - { - base.VisitPointerType(type); - builder.Append('*'); - return type; - } - - public override IType VisitTypeParameter(ITypeParameter type) - { - base.VisitTypeParameter(type); - EscapeName(builder, type.Name); - return type; - } - - public override IType VisitParameterizedType(ParameterizedType type) - { - type.GenericType.AcceptVisitor(this); - builder.Append('<'); - for (int i = 0; i < type.TypeArguments.Count; i++) - { - if (i > 0) - builder.Append(','); - type.TypeArguments[i].AcceptVisitor(this); - } - builder.Append('>'); - return type; - } - - public override IType VisitTupleType(TupleType type) - { - type.UnderlyingType.AcceptVisitor(this); - return type; - } - - public override IType VisitFunctionPointerType(FunctionPointerType type) - { - builder.Append("method "); - if (type.CallingConvention != SignatureCallingConvention.Default) - { - builder.Append(type.CallingConvention.ToILSyntax()); - builder.Append(' '); - } - type.ReturnType.AcceptVisitor(this); - builder.Append(" *("); - bool first = true; - foreach (var p in type.ParameterTypes) - { - if (first) - first = false; - else - builder.Append(", "); - - p.AcceptVisitor(this); - } - builder.Append(')'); - return type; - } - - public override IType VisitOtherType(IType type) - { - WriteType(type); - return type; - } - - private void WriteType(IType type) - { - if (includeNamespace) - EscapeName(builder, type.FullName); - else - EscapeName(builder, type.Name); - if (type.TypeParameterCount > 0) - { - builder.Append('`'); - builder.Append(type.TypeParameterCount); - } - } - - public override IType VisitTypeDefinition(ITypeDefinition type) - { - switch (type.KnownTypeCode) - { - case KnownTypeCode.Object: - builder.Append("object"); - break; - case KnownTypeCode.Boolean: - builder.Append("bool"); - break; - case KnownTypeCode.Char: - builder.Append("char"); - break; - case KnownTypeCode.SByte: - builder.Append("int8"); - break; - case KnownTypeCode.Byte: - builder.Append("uint8"); - break; - case KnownTypeCode.Int16: - builder.Append("int16"); - break; - case KnownTypeCode.UInt16: - builder.Append("uint16"); - break; - case KnownTypeCode.Int32: - builder.Append("int32"); - break; - case KnownTypeCode.UInt32: - builder.Append("uint32"); - break; - case KnownTypeCode.Int64: - builder.Append("int64"); - break; - case KnownTypeCode.UInt64: - builder.Append("uint64"); - break; - case KnownTypeCode.Single: - builder.Append("float32"); - break; - case KnownTypeCode.Double: - builder.Append("float64"); - break; - case KnownTypeCode.String: - builder.Append("string"); - break; - case KnownTypeCode.Void: - builder.Append("void"); - break; - case KnownTypeCode.IntPtr: - builder.Append("native int"); - break; - case KnownTypeCode.UIntPtr: - builder.Append("native uint"); - break; - case KnownTypeCode.TypedReference: - builder.Append("typedref"); - break; - default: - WriteType(type); - break; - } - return type; - } - } - #endregion - - /// - /// Escape characters that cannot be displayed in the UI. - /// - public static StringBuilder EscapeName(StringBuilder sb, string name) - { - foreach (char ch in name) - { - if (char.IsWhiteSpace(ch) || char.IsControl(ch) || char.IsSurrogate(ch)) - sb.AppendFormat("\\u{0:x4}", (int) ch); - else - sb.Append(ch); - } - return sb; - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/PlainTextOutput2.cs b/src/BUTR.CrashReport.Decompilers/ILSpy/PlainTextOutput2.cs deleted file mode 100644 index bea788d..0000000 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/PlainTextOutput2.cs +++ /dev/null @@ -1,133 +0,0 @@ -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.CSharp.Syntax; -using ICSharpCode.Decompiler.Disassembler; -using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.Decompiler.TypeSystem; - -using System; -using System.IO; -using System.Reflection.Metadata; -using System.Text; - -namespace BUTR.CrashReport.Decompilers.ILSpy; - -internal class PlainTextOutput2 : ITextOutput -{ - private readonly TextWriter _writer; - private int _indent; - private bool _needsIndent; - - private int _line = 1; - private int _column = 1; - - public string IndentationString { get; set; } = "\t"; - - public PlainTextOutput2(TextWriter writer) => _writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - public PlainTextOutput2() => _writer = new StringWriter(); - - public TextLocation Location => new(_line, _column + (_needsIndent ? _indent : 0)); - - public override string? ToString() => _writer.ToString(); - - public void Indent() => _indent++; - - public void Unindent() => _indent--; - - void WriteIndent() - { - if (_needsIndent) - { - _needsIndent = false; - for (var i = 0; i < _indent; i++) - { - _writer.Write(IndentationString); - } - _column += _indent; - } - } - - public void Write(ReadOnlySpan span) - { - WriteIndent(); - _writer.Write(span); - _column += span.Length; - } - - public void Write(StringBuilder sb, int offset, int length) - { - WriteIndent(); - _writer.Write(sb, offset, length); - _column += length; - } - - public void Write(char ch) - { - WriteIndent(); - _writer.Write(ch); - _column++; - } - - public void Write(string text) - { - WriteIndent(); - _writer.Write(text); - _column += text.Length; - } - - public void WriteLine() - { - _writer.WriteLine(); - _needsIndent = true; - _line++; - _column = 1; - } - - public void WriteLine(ReadOnlySpan line) - { - _writer.WriteLine(line); - _needsIndent = true; - _line++; - _column = 1; - } - - public void WriteReference(OpCodeInfo opCode, bool omitSuffix = false) - { - if (omitSuffix) - { - var lastDot = opCode.Name.LastIndexOf('.'); - if (lastDot > 0) - { - Write(opCode.Name.Remove(lastDot + 1)); - } - } - else - { - Write(opCode.Name); - } - } - - public void WriteReference(MetadataFile module, Handle handle, string text, string protocol = "decompile", bool isDefinition = false) - { - Write(text); - } - - public void WriteReference(IType type, string text, bool isDefinition = false) - { - Write(text); - } - - public void WriteReference(IMember member, string text, bool isDefinition = false) - { - Write(text); - } - - public void WriteLocalReference(string text, object reference, bool isDefinition = false) - { - Write(text); - } - - void ITextOutput.MarkFoldStart(string collapsedText, bool defaultCollapsed, bool isDefinition) { } - - void ITextOutput.MarkFoldEnd() { } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/ILSpy/TextWriterExtensions.cs b/src/BUTR.CrashReport.Decompilers/ILSpy/TextWriterExtensions.cs deleted file mode 100644 index e0981c5..0000000 --- a/src/BUTR.CrashReport.Decompilers/ILSpy/TextWriterExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.IO; -using System.Text; - -namespace BUTR.CrashReport.Decompilers.ILSpy; - -internal static class TextWriterExtensions -{ - public static void Write(this TextWriter writer, StringBuilder sb, int offset, int length) - { - var buffer = length > 512 ? new char[length] : stackalloc char[length]; - sb.CopyTo(offset, buffer, length); - writer.Write(buffer); - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Models/AssemblyTypeReferenceInternal.cs b/src/BUTR.CrashReport.Decompilers/Models/AssemblyTypeReferenceInternal.cs new file mode 100644 index 0000000..8036524 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Models/AssemblyTypeReferenceInternal.cs @@ -0,0 +1,25 @@ +namespace BUTR.CrashReport.Decompilers.Models; + +/// +/// +/// +public record AssemblyTypeReferenceInternal +{ + /// + /// + /// + /// + public required string Name { get; set; } + + /// + /// + /// + /// + public required string Namespace { get; set; } + + /// + /// + /// + /// + public required string FullName { get; set; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/DebugCodeProvider.cs b/src/BUTR.CrashReport.Decompilers/Sources/DebugCodeProvider.cs new file mode 100644 index 0000000..213fccb --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/DebugCodeProvider.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal static class DebugCodeProvider +{ + private static readonly Guid EmbeddedSource = new("0E8A571B-6926-466E-B4AD-8AB04611F5FE"); + private static readonly Guid SourceLink = new("CC110556-A091-4D38-9FEC-25AB9A351A6A"); + + private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false); + + private static string Decode(MemoryStream stream, Encoding? encoding) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + encoding ??= DefaultEncoding; + stream.Position = 0; + + using var reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true); + var text = reader.ReadToEnd(); + return text; + } + + private static IEnumerable GetSourceLocation(MetadataReader metadataReader, uint methodToken) + { + var methodDebugInformationHandle = MetadataTokens.MethodDebugInformationHandle((int) methodToken); + var methodDebugInformation = metadataReader.GetMethodDebugInformation(methodDebugInformationHandle); + var documentHandle = methodDebugInformation.Document; + + if (documentHandle.IsNil) + yield break; + + var document = metadataReader.GetDocument(documentHandle); + var documentName = metadataReader.GetString(document.Name); + + var sequencePoints = new List(); + var previousSequencePoint = default(SequencePoint); + foreach (var sequencePoint in methodDebugInformation.GetSequencePoints().Reverse()) + { + if (previousSequencePoint.Equals(default)) + previousSequencePoint = sequencePoint; + + sequencePoints.Add(new SourceSequencePoint(sequencePoint.Offset, previousSequencePoint.Offset, sequencePoint.StartLine, sequencePoint.StartColumn, sequencePoint.EndLine, sequencePoint.EndColumn, sequencePoint.IsHidden)); + previousSequencePoint = sequencePoint; + } + sequencePoints.Reverse(); + + foreach (var customDebugInformationHandle in metadataReader.CustomDebugInformation) + { + var customDebugInformation = metadataReader.GetCustomDebugInformation(customDebugInformationHandle); + var guid = metadataReader.GetGuid(customDebugInformation.Kind); + + if (guid == EmbeddedSource && customDebugInformation.Parent == documentHandle) + { + if (GetEmbeddedSource(metadataReader, customDebugInformation) is { } sourceCodeString) + yield return new SourceLocation(new SourceFileEmbedded(documentName, sourceCodeString), sequencePoints); + } + + if (guid == SourceLink) + { + var sourceLink = JsonSerializer.Deserialize(metadataReader.GetBlobBytes(customDebugInformation.Value)); + if (sourceLink?.GetSourceUrl(documentName) is { } sourceLinkUrl) + yield return new SourceLocation(new SourceFileSourceLink(documentName, sourceLinkUrl), sequencePoints); + } + } + } + + private static string GetEmbeddedSource(MetadataReader reader, CustomDebugInformation customDebugInformation) + { + var bytes = reader.GetBlobBytes(customDebugInformation.Value); + + var uncompressedSize = BitConverter.ToInt32(bytes, 0); + var stream = new MemoryStream(bytes, sizeof(int), bytes.Length - sizeof(int)); + + if (uncompressedSize != 0) + { + var decompressed = new MemoryStream(uncompressedSize); + + using (var deflater = new DeflateStream(stream, CompressionMode.Decompress)) + { + deflater.CopyTo(decompressed); + } + + if (decompressed.Length != uncompressedSize) + { + throw new InvalidDataException(); + } + + stream = decompressed; + } + + using (stream) + { + return Decode(stream, DefaultEncoding); + } + } + + public static List GetCSharpSourceFromPdb(MethodBase? method, Stream pdbStream, bool disposeStream) + { + if (method is null) return []; + + var pdbMeta = MetadataReaderProvider.FromPortablePdbStream(pdbStream, disposeStream ? MetadataStreamOptions.Default : MetadataStreamOptions.LeaveOpen); + var pdbReader = pdbMeta.GetMetadataReader(); + return GetSourceLocation(pdbReader, (uint) method.MetadataToken).ToList(); + } + + public static List GetCSharpSourcesFromAssembly(MethodBase? method, Stream assemblyStream, bool disposeStream) + { + if (method is null) return []; + + using var peReader = new PEReader(assemblyStream, disposeStream ? PEStreamOptions.Default : PEStreamOptions.LeaveOpen); + var embeddedEntries = peReader.ReadDebugDirectory().Where(x => x.Type == DebugDirectoryEntryType.EmbeddedPortablePdb).ToList(); + if (embeddedEntries.Count != 1) return []; + + using var provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntries.Single()); + var pdbReader = provider.GetMetadataReader(); + return GetSourceLocation(pdbReader, (uint) method.MetadataToken).ToList(); + } +} + +file class SourceLink +{ + public static Guid EmbeddedSourceId { get; } = new("0E8A571B-6926-466E-B4AD-8AB04611F5FE"); + public static Guid SourceLinkId { get; } = new("CC110556-A091-4D38-9FEC-25AB9A351A6A"); + + [JsonPropertyName("documents")] + public Dictionary? Documents { get; set; } + + public string? GetSourceUrl(string documentName) + { + foreach (var (docPath, urlTemplate) in Documents ?? []) + { + // Check if the document path has a wildcard '*' + if (docPath.Contains("*")) + { + // Ensure that the wildcard is the final character + if (docPath.IndexOf("*", StringComparison.Ordinal) != docPath.Length - 1) + return null; + // throw new ArgumentException("Wildcard '*' must be the final character in the file path."); + + // Ensure that the URL template also contains a '*' + if (!urlTemplate.Contains("*")) + return null; + // throw new ArgumentException("If the file path contains '*', the URL must also contain '*'."); + + // Remove the wildcard from the document path to get the base path + var basePath = docPath.TrimEnd('*'); + + // Check if the file path starts with the base path + if (documentName.StartsWith(basePath, StringComparison.OrdinalIgnoreCase)) + { + // Get the relative path part after the base path + var relativePath = documentName.Substring(basePath.Length); + + // Replace '*' in the URL template with the relative path + return urlTemplate.Replace("*", relativePath); + } + } + else + { + // No wildcard in the document path; check for an exact match + if (string.Equals(docPath, documentName, StringComparison.OrdinalIgnoreCase)) + { + // URL template should not contain a '*' + if (urlTemplate.Contains("*")) + return null; + // throw new ArgumentException("If the file path does not contain '*', the URL cannot contain '*'."); + + return urlTemplate; + } + } + } + + // If no match is found, return null or throw an exception + return null; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/ILSpyCodeProvider.cs b/src/BUTR.CrashReport.Decompilers/Sources/ILSpyCodeProvider.cs new file mode 100644 index 0000000..f5b8f23 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/ILSpyCodeProvider.cs @@ -0,0 +1,65 @@ +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.Metadata; + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Threading; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal static class ILSpyCodeProvider +{ + private static readonly List Empty = new(); + + private static CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilerSettings settings, CancellationToken ct) + { + var resolver = new UniversalAssemblyResolver(null, false, module.DetectTargetFrameworkId(), module.DetectRuntimePack()); + return new CSharpDecompiler(module, resolver, settings) { CancellationToken = ct }; + } + + private static void WriteCode(TextWriter output, DecompilerSettings settings, SyntaxTree syntaxTree) + { + syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); + TokenWriter tokenWriter = new TextWriterTokenWriter(output) { IndentationString = settings.CSharpFormattingOptions.IndentationString }; + tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); + syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); + } + + private static SourceLocation GetCSharpSource(MetadataFile module, MethodDefinitionHandle handle) + { + var settings = new DecompilerSettings(LanguageVersion.Latest); + var decompiler = CreateDecompiler(module, settings, CancellationToken.None); + var syntaxTree = decompiler.Decompile(handle); + + using var csharpOutput = new StringWriter(); + WriteCode(csharpOutput, settings, syntaxTree); + + var sequencePointsMap = decompiler.CreateSequencePoints(syntaxTree); + var ilFunction = sequencePointsMap.Keys.FirstOrDefault(x => (x.MoveNextMethod ?? x.Method)?.MetadataToken == handle); + var sequencePoints = ilFunction is not null ? sequencePointsMap[ilFunction] : Empty; + + return new SourceLocation( + new SourceFileDecompiled(csharpOutput.GetStringBuilder()), + sequencePoints.Select(x => new SourceSequencePoint(x.Offset, x.EndOffset, x.StartLine, x.StartColumn, x.EndLine, x.EndColumn, x.IsHidden)).ToArray()); + } + + [return: NotNullIfNotNull("method")] + public static SourceLocation? GetCSharpSourceFromAssembly(MethodBase? method, Stream assemblyStream, bool disposeStream) + { + if (method is null) return null; + + using var peFile = new PEFile("Assembly", assemblyStream, disposeStream ? PEStreamOptions.Default : PEStreamOptions.LeaveOpen); + + var methodHandle = (MethodDefinitionHandle) MetadataTokens.Handle(method.MetadataToken); + return GetCSharpSource(peFile, methodHandle); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceFile.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceFile.cs new file mode 100644 index 0000000..697f4c3 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceFile.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal abstract record SourceFile(SourceKind Kind) +{ + public abstract string GetMethodLine(int startLine, int endLine); + + public virtual string[] GetMethodLines(IList sequencePoints) + { + var startLine = sequencePoints.First().StartLine; + var endLine = sequencePoints.Last().EndLine; + + var lines = new string[endLine - startLine + 1]; + for (var i = 0; i < lines.Length; i++) + lines[i] = GetMethodLine(startLine + i, startLine + i); + return lines; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceFileDecompiled.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileDecompiled.cs new file mode 100644 index 0000000..fff49cf --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileDecompiled.cs @@ -0,0 +1,46 @@ +using BUTR.CrashReport.Decompilers.Extensions; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceFileDecompiled : SourceFile +{ + private static readonly char[] NewLine = Environment.NewLine.ToArray(); + + public StringBuilder SourceCode { get; init; } + + private readonly List> _linesIndices; + + public SourceFileDecompiled(StringBuilder sourceCode) : base(SourceKind.Decompiled) + { + SourceCode = sourceCode; + + _linesIndices = new(); + var previousIdx = 0; + while (SourceCode.IndexOf(NewLine, previousIdx) is var idx and not -1) + { + _linesIndices.Add(new(previousIdx, idx)); + previousIdx = idx + NewLine.Length; + } + _linesIndices.Add(new(previousIdx, SourceCode.Length)); + } + + public override string GetMethodLine(int startLine, int endLine) + { + if (startLine < 0 || endLine < 0 || startLine > endLine) return string.Empty; + + var (startIdx, _) = _linesIndices[startLine - 1]; + var (_, endIdx) = _linesIndices[endLine - 1]; + + return SourceCode.ToString(startIdx, endIdx - startIdx); + } + + public override string[] GetMethodLines(IList sequencePoints) + { + return SourceCode.ToString().Split([Environment.NewLine], StringSplitOptions.None); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceFileEmbedded.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileEmbedded.cs new file mode 100644 index 0000000..39432ae --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileEmbedded.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceFileEmbedded : SourceFile +{ + public string FilePath { get; init; } + public string SourceCode { get; init; } + + private readonly List> _linesIndices; + + public SourceFileEmbedded(string filePath, string sourceCode) : base(SourceKind.Embedded) + { + FilePath = filePath; + SourceCode = sourceCode; + + _linesIndices = new(); + var previousIdx = 0; + while (SourceCode.IndexOf(Environment.NewLine, previousIdx, StringComparison.InvariantCulture) is var idx and not -1) + { + _linesIndices.Add(new(previousIdx, idx)); + previousIdx = idx + Environment.NewLine.Length; + } + _linesIndices.Add(new(previousIdx, SourceCode.Length)); + } + + public override string GetMethodLine(int startLine, int endLine) + { + if (startLine < 0 || endLine < 0 || startLine > endLine) return string.Empty; + + var (startIdx, _) = _linesIndices[startLine - 1]; + var (_, endIdx) = _linesIndices[endLine - 1]; + + return SourceCode.Substring(startIdx, endIdx - startIdx); + } + + public override string[] GetMethodLines(IList sequencePoints) + { + return SourceCode.Split([Environment.NewLine], StringSplitOptions.None); + } + + public void Deconstruct(out string buildTimeFilePath, out string srcCode) + { + buildTimeFilePath = FilePath; + srcCode = SourceCode; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceFileNone.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileNone.cs new file mode 100644 index 0000000..ec5393d --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileNone.cs @@ -0,0 +1,6 @@ +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceFileNone() : SourceFile(SourceKind.None) +{ + public override string GetMethodLine(int startLine, int endLine) => string.Empty; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceFileSourceLink.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileSourceLink.cs new file mode 100644 index 0000000..102f247 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileSourceLink.cs @@ -0,0 +1,6 @@ +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceFileSourceLink(string FilePath, string SourceUrl) : SourceFile(SourceKind.SourceLink) +{ + public override string GetMethodLine(int startLine, int endLine) => string.Empty; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceFileSourceLinkResolved.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileSourceLinkResolved.cs new file mode 100644 index 0000000..8fdf2de --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceFileSourceLinkResolved.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceFileSourceLinkResolved : SourceFile +{ + public string FilePath { get; init; } + public string SourceCode { get; init; } + + private readonly List> _linesIndices; + + public SourceFileSourceLinkResolved(string filePath, string sourceCode) : base(SourceKind.SourceLinkResolved) + { + FilePath = filePath; + SourceCode = sourceCode; + + _linesIndices = new(); + var previousIdx = 0; + while (SourceCode.IndexOf(Environment.NewLine, previousIdx, StringComparison.InvariantCulture) is var idx and not -1) + { + _linesIndices.Add(new(previousIdx, idx)); + previousIdx = idx + Environment.NewLine.Length; + } + _linesIndices.Add(new(previousIdx, SourceCode.Length)); + } + + public override string GetMethodLine(int startLine, int endLine) + { + if (startLine < 0 || endLine < 0 || startLine > endLine) return string.Empty; + + var (startIdx, _) = _linesIndices[startLine - 1]; + var (_, endIdx) = _linesIndices[endLine - 1]; + + return SourceCode.Substring(startIdx, endIdx - startIdx); + } + + public override string[] GetMethodLines(IList sequencePoints) + { + return SourceCode.Split([Environment.NewLine], StringSplitOptions.None); + } + + public void Deconstruct(out string buildTimeFilePath, out string srcCode) + { + buildTimeFilePath = FilePath; + srcCode = SourceCode; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceKind.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceKind.cs new file mode 100644 index 0000000..9ed86e6 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceKind.cs @@ -0,0 +1,10 @@ +namespace BUTR.CrashReport.Decompilers.Sources; + +internal enum SourceKind +{ + None, + Embedded, + Decompiled, + SourceLink, + SourceLinkResolved, +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceLocation.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceLocation.cs new file mode 100644 index 0000000..29a7db0 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceLocation.cs @@ -0,0 +1,5 @@ +using System.Collections.Generic; + +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceLocation(SourceFile SourceFile, IList SequencePoints); \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Sources/SourceSequencePoint.cs b/src/BUTR.CrashReport.Decompilers/Sources/SourceSequencePoint.cs new file mode 100644 index 0000000..0d811c7 --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Sources/SourceSequencePoint.cs @@ -0,0 +1,3 @@ +namespace BUTR.CrashReport.Decompilers.Sources; + +internal sealed record SourceSequencePoint(int Offset, int EndOffset, int StartLine, int StartColumn, int EndLine, int EndColumn, bool IsHidden); \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/FileSystemName.cs b/src/BUTR.CrashReport.Decompilers/Utils/FileSystemName.cs deleted file mode 100644 index 0041c56..0000000 --- a/src/BUTR.CrashReport.Decompilers/Utils/FileSystemName.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System; - -namespace BUTR.CrashReport.Decompilers.Utils; - -/// -/// Provides methods for matching file system names. -/// -public static class FileSystemName -{ - /// - /// Verifies whether the given expression matches the given name. Supports the following wildcards: '*' and '?'. The backslash character '\\' escapes. - /// - /// - /// - /// - /// - public static bool MatchesSimpleExpression(string expression, string name, bool ignoreCase = true) => MatchesSimpleExpression(expression.AsSpan(), name.AsSpan(), ignoreCase); - - /// - /// Verifies whether the given expression matches the given name. Supports the following wildcards: '*' and '?'. The backslash character '\\' escapes. - /// - /// The expression to match with. - /// The name to check against the expression. - /// to ignore case (default); if the match should be case-sensitive. - /// if the given expression matches the given name; otherwise, . - private static bool MatchesSimpleExpression(ReadOnlySpan expression, ReadOnlySpan name, bool ignoreCase = true) - { - return MatchPattern(expression, name, ignoreCase, useExtendedWildcards: false); - } - - // Matching routine description - // ============================ - // (copied from native impl) - // - // This routine compares a Dbcs name and an expression and tells the caller - // if the name is in the language defined by the expression. The input name - // cannot contain wildcards, while the expression may contain wildcards. - // - // Expression wild cards are evaluated as shown in the nondeterministic - // finite automata below. Note that ~* and ~? are DOS_STAR and DOS_QM. - // - // ~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT - // - // S - // <-----< - // X | | e Y - // X * Y == (0)----->-(1)->-----(2)-----(3) - // - // S-. - // <-----< - // X | | e Y - // X ~* Y == (0)----->-(1)->-----(2)-----(3) - // - // X S S Y - // X ?? Y == (0)---(1)---(2)---(3)---(4) - // - // X . . Y - // X ~.~. Y == (0)---(1)----(2)------(3)---(4) - // | |________| - // | ^ | - // |_______________| - // ^EOF or .^ - // - // X S-. S-. Y - // X ~?~? Y == (0)---(1)-----(2)-----(3)---(4) - // | |________| - // | ^ | - // |_______________| - // ^EOF or .^ - // - // where S is any single character - // S-. is any single character except the final . - // e is a null character transition - // EOF is the end of the name string - // - // In words: - // - // * matches 0 or more characters. - // ? matches exactly 1 character. - // DOS_STAR matches 0 or more characters until encountering and matching - // the final . in the name. - // DOS_QM matches any single character, or upon encountering a period or - // end of name string, advances the expression to the end of the - // set of contiguous DOS_QMs. - // DOS_DOT matches either a . or zero characters beyond name string. - - private static bool MatchPattern(ReadOnlySpan expression, ReadOnlySpan name, bool ignoreCase, bool useExtendedWildcards) - { - // The idea behind the algorithm is pretty simple. We keep track of all possible locations - // in the regular expression that are matching the name. When the name has been exhausted, - // if one of the locations in the expression is also just exhausted, the name is in the - // language defined by the regular expression. - - if (expression.Length == 0 || name.Length == 0) - return false; - - if (expression[0] == '*') - { - // Just * matches everything - if (expression.Length == 1) - return true; - - ReadOnlySpan expressionEnd = expression.Slice(1); - - // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression - // https://msdn.microsoft.com/en-us/library/ff469270.aspx - bool hasWildcards = useExtendedWildcards ? - expressionEnd.IndexOf("\"<>*?".AsSpan()) != 0 : - expressionEnd.IndexOfAny('*', '?') != 0; - if (!hasWildcards) - { - // Handle the special case of a single starting *, which essentially means "ends with" - - // If the name doesn't have enough characters to match the remaining expression, it can't be a match. - if (name.Length < expressionEnd.Length) - return false; - - // See if we end with the expression - return name.EndsWith(expressionEnd, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); - } - } - - int nameOffset = 0; - int expressionOffset; - - int priorMatch; - int currentMatch; - int priorMatchCount; - int matchCount = 1; - - char nameChar = '\0'; - char expressionChar; - - scoped Span temp = default; - Span currentMatches = stackalloc int[16]; - Span priorMatches = stackalloc int[16]; - priorMatches[0] = 0; - - int maxState = expression.Length * 2; - int currentState; - bool nameFinished = false; - - // Walk through the name string, picking off characters. We go one - // character beyond the end because some wild cards are able to match - // zero characters beyond the end of the string. - // - // With each new name character we determine a new set of states that - // match the name so far. We use two arrays that we swap back and forth - // for this purpose. One array lists the possible expression states for - // all name characters up to but not including the current one, and other - // array is used to build up the list of states considering the current - // name character as well. The arrays are then switched and the process - // repeated. - // - // There is not a one-to-one correspondence between state number and - // offset into the expression. State numbering is not continuous. - // This allows a simple conversion between state number and expression - // offset. Each character in the expression can represent one or two - // states. * and DOS_STAR generate two states: expressionOffset * 2 and - // expressionOffset * 2 + 1. All other expression characters can produce - // only a single state. Thus expressionOffset = currentState / 2. - - while (!nameFinished) - { - if (nameOffset < name.Length) - { - // Not at the end of the name. Grab the current character and move the offset forward. - nameChar = name[nameOffset++]; - } - else - { - // At the end of the name. If the expression is exhausted, exit. - if (priorMatches[matchCount - 1] == maxState) - break; - - nameFinished = true; - } - - // Now, for each of the previous stored expression matches, see what - // we can do with this name character. - priorMatch = 0; - currentMatch = 0; - priorMatchCount = 0; - - while (priorMatch < matchCount) - { - // We have to carry on our expression analysis as far as possible for each - // character of name, so we loop here until the expression stops matching. - - expressionOffset = (priorMatches[priorMatch++] + 1) / 2; - - while (expressionOffset < expression.Length) - { - currentState = expressionOffset * 2; - expressionChar = expression[expressionOffset]; - - // We may be about to exhaust the local space for matches, - // so we have to reallocate if this is the case. - if (currentMatch >= currentMatches.Length - 2) - { - int newSize = currentMatches.Length * 2; - temp = new int[newSize]; - currentMatches.CopyTo(temp); - currentMatches = temp; - - temp = new int[newSize]; - priorMatches.CopyTo(temp); - priorMatches = temp; - } - - if (expressionChar == '*') - { - // '*' matches any character zero or more times. - goto MatchZeroOrMore; - } - else if (useExtendedWildcards && expressionChar == '<') - { - // '<' (DOS_STAR) matches any character except '.' zero or more times. - - // If we are at a period, determine if we are allowed to - // consume it, i.e. make sure it is not the last one. - - bool notLastPeriod = false; - if (!nameFinished && nameChar == '.') - { - for (int offset = nameOffset; offset < name.Length; offset++) - { - if (name[offset] == '.') - { - notLastPeriod = true; - break; - } - } - } - - if (nameFinished || nameChar != '.' || notLastPeriod) - { - goto MatchZeroOrMore; - } - else - { - // We are at a period. We can only match zero - // characters (i.e. the epsilon transition). - goto MatchZero; - } - } - else - { - // The remaining expression characters all match by consuming a character, - // so we need to force the expression and state forward. - currentState += 2; - - if (useExtendedWildcards && expressionChar == '>') - { - // '>' (DOS_QM) is the most complicated. If the name is finished, - // we can match zero characters. If this name is a '.', we - // don't match, but look at the next expression. Otherwise - // we match a single character. - if (nameFinished || nameChar == '.') - goto NextExpressionCharacter; - - currentMatches[currentMatch++] = currentState; - goto ExpressionFinished; - } - else if (useExtendedWildcards && expressionChar == '"') - { - // A '"' (DOS_DOT) can match either a period, or zero characters - // beyond the end of name. - if (nameFinished) - { - goto NextExpressionCharacter; - } - else if (nameChar == '.') - { - currentMatches[currentMatch++] = currentState; - } - goto ExpressionFinished; - } - else - { - if (expressionChar == '\\') - { - // Escape character, try to move the expression forward again and match literally. - if (++expressionOffset == expression.Length) - { - currentMatches[currentMatch++] = maxState; - goto ExpressionFinished; - } - - currentState = expressionOffset * 2 + 2; - expressionChar = expression[expressionOffset]; - } - - // From this point on a name character is required to even - // continue, let alone make a match. - if (nameFinished) goto ExpressionFinished; - - if (expressionChar == '?') - { - // If this expression was a '?' we can match it once. - currentMatches[currentMatch++] = currentState; - } - else if (ignoreCase - ? char.ToUpperInvariant(expressionChar) == char.ToUpperInvariant(nameChar) - : expressionChar == nameChar) - { - // Matched a non-wildcard character - currentMatches[currentMatch++] = currentState; - } - - goto ExpressionFinished; - } - } - - MatchZeroOrMore: - currentMatches[currentMatch++] = currentState; - MatchZero: - currentMatches[currentMatch++] = currentState + 1; - NextExpressionCharacter: - if (++expressionOffset == expression.Length) - currentMatches[currentMatch++] = maxState; - } // while (expressionOffset < expression.Length) - - ExpressionFinished: - - // Prevent duplication in the destination array. - // - // Each of the arrays is monotonically increasing and non-duplicating, thus we skip - // over any source element in the source array if we just added the same element to - // the destination array. This guarantees non-duplication in the destination array. - - if ((priorMatch < matchCount) && (priorMatchCount < currentMatch)) - { - while (priorMatchCount < currentMatch) - { - int previousLength = priorMatches.Length; - while ((priorMatch < previousLength) && (priorMatches[priorMatch] < currentMatches[priorMatchCount])) - { - priorMatch++; - } - priorMatchCount++; - } - } - } // while (sourceCount < matchesCount) - - // If we found no matches in the just finished iteration it's time to bail. - if (currentMatch == 0) - return false; - - // Swap the meaning the two arrays - temp = priorMatches; - priorMatches = currentMatches; - currentMatches = temp; - - matchCount = currentMatch; - } // while (!nameFinished) - - currentState = priorMatches[matchCount - 1]; - - return currentState == maxState; - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodCopier.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodCopier.cs deleted file mode 100644 index 1c1038a..0000000 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodCopier.cs +++ /dev/null @@ -1,69 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Cloning; -using AsmResolver.DotNet.Dynamic; - -using System; -using System.Diagnostics; -using System.IO; -using System.Reflection; - -using TypeAttributes = AsmResolver.PE.DotNet.Metadata.Tables.TypeAttributes; - -namespace BUTR.CrashReport.Decompilers.Utils; - -internal static class MethodCopier -{ - public static MemoryStream? GetAssemblyCopy(MethodBase method, out int metadataToken) - { - metadataToken = 0; - MethodDefinition? methodDefinition = null; - ModuleDefinition? module = null; - - try - { - module = ModuleDefinition.FromModule(typeof(MethodDecompiler).Module); - methodDefinition = new DynamicMethodDefinition(module, method); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - try - { - module = ModuleDefinition.FromModule(method.Module); - methodDefinition = module.LookupMember(method.MetadataToken) as MethodDefinition; - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - if (module is null || methodDefinition is null) return null; - - var destinationModule = new ModuleDefinition(method.Module.Name); - - var cloner = new MemberCloner(destinationModule); - if (methodDefinition.DeclaringType is not null) cloner.Include(methodDefinition.DeclaringType, recursive: true); - cloner.Include(methodDefinition, recursive: true); - cloner.AddListener(new InjectTypeClonerListener(destinationModule)); - cloner.AddListener(new AssignTokensClonerListener(destinationModule)); - var result = cloner.Clone(); - - var clonedMethodDefinition = result.GetClonedMember(methodDefinition); - metadataToken = clonedMethodDefinition.MetadataToken.ToInt32(); - - if (methodDefinition.DeclaringType is null) - { - var typeDefinition = new TypeDefinition("BUTR.CrashReport", "MethodHolder", TypeAttributes.Public | TypeAttributes.Class); - typeDefinition.Methods.Add(clonedMethodDefinition); - destinationModule.TopLevelTypes.Add(typeDefinition); - } - - var ms = new MemoryStream(); - destinationModule.Write(ms); - ms.Seek(0, SeekOrigin.Begin); - - return ms; - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.AsmResolver.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.AsmResolver.cs index e620ac2..c10dd1f 100644 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.AsmResolver.cs +++ b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.AsmResolver.cs @@ -1,19 +1,66 @@ using AsmResolver.DotNet; +using AsmResolver.DotNet.Cloning; using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Dynamic; +using AsmResolver.PE.DotNet.Metadata.Tables.Rows; + +using Microsoft.IO; using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; -using System.Reflection; namespace BUTR.CrashReport.Decompilers.Utils; partial class MethodDecompiler { - private static bool TryGetMethodDefinition(MethodBase method, [NotNullWhen(true)] out ModuleDefinition? module, [NotNullWhen(true)] out MethodDefinition? methodDefinition) + private static bool TryCopyMethod(System.Reflection.MethodBase method, Func getAssemblyStream, [NotNullWhen(true)] out Stream? stream, out int methodToken) + { + var assembly = method.Module.Assembly; + + // Prefer assembly copy since it means no File IO + try + { + stream = MethodCopier.GetAssemblyCopy(method, out var metadataToken); + if (stream is not null) + { + methodToken = metadataToken; + return true; + } + } + catch (Exception e) + { + Trace.TraceError(e.ToString()); + } + + stream = getAssemblyStream(assembly); + if (stream is not null) + { + methodToken = method.MetadataToken; + return true; + } + + stream = null; + methodToken = 0; + return false; + } + + private static MethodDecompilerCode ToLines(CilInstructionCollection? instructions, int? ilOffset) + { + var lines = new string[instructions?.Count ?? 0]; + var lineOffset = default(int?); + for (var line = 0; line < lines.Length; line++) + { + var instruction = instructions![line]; + lines[line] = instruction.ToString(); + if (instruction.Offset == ilOffset) + lineOffset = line + 1; + } + return new MethodDecompilerCode(lines, lineOffset is null ? null : new(lineOffset.Value, -1, lineOffset.Value, -1)); + } + + private static bool TryGetMethodDefinition(System.Reflection.MethodBase method, [NotNullWhen(true)] out ModuleDefinition? module, [NotNullWhen(true)] out MethodDefinition? methodDefinition) { try { @@ -42,26 +89,45 @@ private static bool TryGetMethodDefinition(MethodBase method, [NotNullWhen(true) return false; } - /// - /// Gets the IL representation of the methods - /// - public static string[] DecompileILCode(MethodBase? method) - { - static string[] ToLines(CilInstructionCollection? instructions) => instructions?.Select(x => x.ToString()).ToArray() ?? []; - - if (method is null) return []; + private static MethodDecompilerCode DecompileILCodeInternal(Stream stream, int metadataToken, int? ilOffset) + { try { - if (!TryCopyMethod(method, out var stream, out var methodHandle)) return []; + var moduleDefinition = ModuleDefinition.FromDataSource(new StreamDataSource(stream)); + if (moduleDefinition.LookupMember(metadataToken) is MethodDefinition methodDefinition) + { + return ToLines(methodDefinition.CilMethodBody?.Instructions, ilOffset); + } + } + catch (Exception e) + { + Trace.TraceError(e.ToString()); + } + + return EmptyCode; + } + + private static MethodDecompilerCode DecompileILCodeInternal(MethodDefinition methodDefinition, int? ilOffset) + { + return ToLines(methodDefinition.CilMethodBody?.Instructions, ilOffset); + } +} + +file static class MethodCopier +{ + private static readonly RecyclableMemoryStreamManager manager = new(); - using var _ = stream; - using var ms = stream as MemoryStream ?? new MemoryStream(); - if (stream is not MemoryStream) stream.CopyTo(ms); + public static MemoryStream? GetAssemblyCopy(System.Reflection.MethodBase method, out int metadataToken) + { + metadataToken = 0; + MethodDefinition? methodDefinition = null; + ModuleDefinition? module = null; - var moduleDefinition = ModuleDefinition.FromBytes(ms.ToArray()); - var methodDefinition = moduleDefinition.LookupMember(method.MetadataToken); - return ToLines(methodDefinition.CilMethodBody?.Instructions); + try + { + module = ModuleDefinition.FromModule(typeof(MethodDecompiler).Module); + methodDefinition = new DynamicMethodDefinition(module, method); } catch (Exception e) { @@ -70,15 +136,45 @@ public static string[] DecompileILCode(MethodBase? method) try { - if (!TryGetMethodDefinition(method, out _, out var methodDefinition)) return []; - - return ToLines(methodDefinition.CilMethodBody?.Instructions); + module = ModuleDefinition.FromModule(method.Module); + methodDefinition = module.LookupMember(method.MetadataToken) as MethodDefinition; } catch (Exception e) { Trace.TraceError(e.ToString()); } - return []; + if (module is null || methodDefinition is null) return null; + + var destinationModule = new ModuleDefinition(method.Module.Name); + + var cloner = new MemberCloner(destinationModule); + if (methodDefinition.DeclaringType is not null) cloner.Include(methodDefinition.DeclaringType, recursive: true); + cloner.Include(methodDefinition, recursive: true); + cloner.AddListener(new InjectTypeClonerListener(destinationModule)); + cloner.AddListener(new AssignTokensClonerListener(destinationModule)); + var type = methodDefinition.DeclaringType; + while (type is not null) + { + cloner.Include(type, recursive: false); + type = type.DeclaringType; + } + var result = cloner.Clone(); + + var clonedMethodDefinition = result.GetClonedMember(methodDefinition); + metadataToken = clonedMethodDefinition.MetadataToken.ToInt32(); + + if (methodDefinition.DeclaringType is null) + { + var typeDefinition = new TypeDefinition("BUTR.CrashReport", "MethodHolder", TypeAttributes.Public | TypeAttributes.Class); + typeDefinition.Methods.Add(clonedMethodDefinition); + destinationModule.TopLevelTypes.Add(typeDefinition); + } + + var ms = manager.GetStream(); + destinationModule.Write(ms); + ms.Seek(0, SeekOrigin.Begin); + + return ms; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.ILSpy.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.ILSpy.cs index 3f71141..3b998a3 100644 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.ILSpy.cs +++ b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.ILSpy.cs @@ -1,113 +1,42 @@ using BUTR.CrashReport.Decompilers.ILSpy; +using BUTR.CrashReport.Decompilers.Sources; using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; using System; using System.Diagnostics; -using System.Linq; -using System.Reflection; +using System.IO; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; using System.Threading; namespace BUTR.CrashReport.Decompilers.Utils; partial class MethodDecompiler { - /// - /// Gets the extended IL representation of the methods - /// - public static string[] DecompileILCodeExtended(MethodBase? method) + private static MethodDecompilerCode DecompileILWithCSharpCodeInternal(Stream stream, int methodToken, int? ilOffset, SourceLocation? csharpSource) { - if (method is null) return []; - - try - { - if (!TryCopyMethod(method, out var stream, out var methodHandle)) return []; - - using var _ = stream; - using var peFile = new PEFile("Assembly", stream); - - var output = new PlainTextOutput2(); - var disassembler = ILLanguage.CreateDisassembler(output, CancellationToken.None); - disassembler.DisassembleMethod(peFile, methodHandle); - - return output.ToString()!.Split([Environment.NewLine], StringSplitOptions.None); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - return []; - } - - /// - /// Gets the C# + IL representation of the methods - /// - public static string[] DecompileILWithCSharpCode(MethodBase? method) - { - if (method is null) return []; - - try - { - if (!TryCopyMethod(method, out var stream, out var methodHandle)) return []; - - using var _ = stream; - using var peFile = new PEFile("Assembly", stream); - - var output = new PlainTextOutput2(); - var disassembler = CSharpILMixedLanguage.CreateDisassembler(output, CancellationToken.None); - disassembler.DisassembleMethod(peFile, methodHandle); - - return output.ToString()!.Split([Environment.NewLine], StringSplitOptions.None); - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - return []; - } - - /// - /// Gets the C# representation of the methods - /// - public static string[] DecompileCSharpCode(MethodBase? method) - { - if (method is null) return []; - try { - if (!TryCopyMethod(method, out var stream, out var methodHandle)) return []; - - using var _ = stream; - using var peFile = new PEFile("Assembly", stream); - - var compilation = new SimpleCompilation(peFile.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached), MinimalCorlib.Instance); - var resolver = new CSharpResolver(compilation); - - IModuleReference moduleReference = peFile; - var module = moduleReference.Resolve(resolver)!; - var method2 = module.TypeDefinitions.SelectMany(x => x.Methods).First(x => x.MetadataToken == methodHandle); + using var peFile = new PEFile("Assembly", stream, PEStreamOptions.LeaveOpen); - var output = new PlainTextOutput2(); - var language = new CSharpLanguage(); - language.DecompileMethod(method2, output, new DecompilerSettings(LanguageVersion.Preview) - { - AggressiveInlining = true, - }); + var output = new PlainTextOutput(); + var methodBodyDisassembler = CSharpILMixedLanguage.CreateMethodBodyDisassembler(output, ilOffset, csharpSource, CancellationToken.None); + var disassembler = CSharpILMixedLanguage.CreateDisassembler(output, methodBodyDisassembler, CancellationToken.None); + disassembler.DisassembleMethod(peFile, (MethodDefinitionHandle) MetadataTokens.Handle(methodToken)); - return output.ToString()!.Split([Environment.NewLine], StringSplitOptions.None); + return new MethodDecompilerCode( + output.ToString()!.Split([Environment.NewLine], StringSplitOptions.None), + methodBodyDisassembler.LineStart is null ? null : new(methodBodyDisassembler.LineStart.Value, -1, methodBodyDisassembler.LineEnd ?? -1, -1) + ); } catch (Exception e) { Trace.TraceError(e.ToString()); } - return []; + return EmptyCode; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.Iced.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.Iced.cs index 7062007..e3ade7c 100644 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.Iced.cs +++ b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.Iced.cs @@ -5,11 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; - -using Decoder = iced::Iced.Intel.Decoder; namespace BUTR.CrashReport.Decompilers.Utils; @@ -18,51 +14,79 @@ partial class MethodDecompiler /// /// Gets the Native representation of the methods /// - public static string[] DecompileNativeCode(IntPtr nativeCodePtr, int nativeILOffset) + public static MethodDecompilerCode DecompileNativeCode(IntPtr nativeCodePtr, int nativeOffset) { - static IEnumerable GetLines(IntPtr nativeCodePtr, int nativeILOffset) + static MethodDecompilerCode GetLines(IntPtr nativeCodePtr, int nativeOffset) { - var length = (uint) nativeILOffset + 16; - var bytecode = new byte[length]; - - Marshal.Copy(nativeCodePtr, bytecode, 0, bytecode.Length); - - var codeReader = new ByteArrayCodeReader(bytecode); + var codeReader = new PointerCodeReader(nativeCodePtr); var decoder = Decoder.Create(IntPtr.Size == 4 ? 32 : 64, codeReader); var output = new StringOutput(); - var sb = new StringBuilder(); + var sb = new System.Text.StringBuilder(); var formatter = new NasmFormatter { Options = { FirstOperandCharIndex = 10, - } + }, }; - while (decoder.IP < length) + const int maxInstructions = 512; + var currentInstruction = 0; + var lines = new List(100); + var lineHit = -1; + while (currentInstruction++ < maxInstructions) { var instr = decoder.Decode(); + if (instr.Code == Code.INVALID) break; + if (instr.IP < (ulong) (nativeOffset + 16)) break; + if (instr.IP == (ulong) nativeOffset) lineHit = currentInstruction; + formatter.Format(instr, output); // Don't use instr.ToString(), it allocates more, uses masm syntax and default options sb.Append(instr.IP.ToString("X4")).Append(' ').Append(output.ToStringAndReset()); - yield return sb.ToString(); + lines.Add(sb.ToString()); sb.Clear(); } + return new(lines, lineHit == -1 ? null : new MethodDecompilerCodeHighlight(lineHit, 0, lineHit, 0)); } - if (nativeCodePtr == IntPtr.Zero) return []; - if (nativeILOffset == StackFrame.OFFSET_UNKNOWN) return []; + if (nativeCodePtr == IntPtr.Zero) return EmptyCode; + if (nativeOffset == StackFrame.OFFSET_UNKNOWN) return EmptyCode; try { - return GetLines(nativeCodePtr, nativeILOffset).ToArray(); + return GetLines(nativeCodePtr, nativeOffset); } catch (Exception e) { Trace.TraceError(e.ToString()); } - return []; + return EmptyCode; + } +} + +file sealed class PointerCodeReader : CodeReader +{ + private readonly IntPtr _ptr; + private int _currentPosition; + + public PointerCodeReader(IntPtr ptr) + { + _ptr = ptr; + _currentPosition = 0; + } + + public override int ReadByte() + { + try + { + return Marshal.ReadByte(_ptr, _currentPosition++); + } + catch (AccessViolationException) + { + return -1; + } } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.System.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.System.cs deleted file mode 100644 index 604812d..0000000 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.System.cs +++ /dev/null @@ -1,80 +0,0 @@ -/* -// TODO: Investigate whether we can read the source mappings if they are available remotely? -// At least save the mapping and then use it online? -using BUTR.CrashReport.ILSpy; - -using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.Resolver; -using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; - -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; -using System.Text; -using System.Threading; - -namespace BUTR.CrashReport.Decompilers.Utils; - -partial class MethodDecompiler -{ - public static string[] DecompileILWithCSharpCode3(MethodBase? method) - { - if (method is null) return Array.Empty(); - - var assembly = method.Module.Assembly; - if (assembly.IsDynamic) return Array.Empty(); - - using var stream = File.OpenRead(assembly.Location); - using var peReader = new PEReader(stream); - - try - { - var embeddedEntries = peReader.ReadDebugDirectory().Where(x => x.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); - using var provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntries.Single()); - var pdbReader = provider.GetMetadataReader(); - - - var definition = MetadataTokens.MethodDefinitionHandle(method.MetadataToken); - foreach (var methodDebugInfoHandle in pdbReader.MethodDebugInformation) - { - if (methodDebugInfoHandle.ToDefinitionHandle() != definition) continue; - - var debugInfo = pdbReader.GetMethodDebugInformation(methodDebugInfoHandle); - foreach (var sequencePoint in debugInfo.GetSequencePoints()) - { - var document = pdbReader.GetDocument(sequencePoint.Document); - var fileName = GetDocumentName(pdbReader, document.Name); - } - } - } - catch (Exception e) - { - return new[] { e.ToString() }; - } - - return Array.Empty(); - } - - private static string GetDocumentName(MetadataReader reader, DocumentNameBlobHandle handle) - { - var blobReader = reader.GetBlobReader(handle); - var separator = (char) blobReader.ReadByte(); - string name = ""; - while (blobReader.Offset < blobReader.Length) - { - var partHandle = blobReader.ReadBlobHandle(); - if (name.Length != 0) - name += separator; - name += Encoding.ASCII.GetString(reader.GetBlobBytes(partHandle)); - } - return name; - } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.SystemReflectionMetadata.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.SystemReflectionMetadata.cs deleted file mode 100644 index 3c0cef9..0000000 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.SystemReflectionMetadata.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; - -namespace BUTR.CrashReport.Decompilers.Utils; - -partial class MethodDecompiler -{ - private static bool TryCopyMethod(MethodBase method, [NotNullWhen(true)] out Stream? stream, out MethodDefinitionHandle methodHandle) - { - var assembly = method.Module.Assembly; - - if (!assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location)) - { - try - { - methodHandle = MetadataTokens.MethodDefinitionHandle(method.MetadataToken); - stream = File.OpenRead(assembly.Location); - return true; - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - } - - try - { - stream = MethodCopier.GetAssemblyCopy(method, out var metadataToken); - if (stream is not null) - { - methodHandle = MetadataTokens.MethodDefinitionHandle(metadataToken); - return true; - } - } - catch (Exception e) - { - Trace.TraceError(e.ToString()); - } - - stream = null; - methodHandle = default; - return false; - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.cs b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.cs index c1b73a9..85924b3 100644 --- a/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.cs +++ b/src/BUTR.CrashReport.Decompilers/Utils/MethodDecompiler.cs @@ -1,6 +1,132 @@ -namespace BUTR.CrashReport.Decompilers.Utils; +using BUTR.CrashReport.Decompilers.Sources; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace BUTR.CrashReport.Decompilers.Utils; + +public record MethodDecompilerResult(MethodDecompilerCode IL, MethodDecompilerCode ILMixed, MethodDecompilerCode CSharp); +public record MethodDecompilerCode(IList Code, MethodDecompilerCodeHighlight? Highlight); +public record MethodDecompilerCodeHighlight(int StartLine, int StartColumn, int EndLine, int EndColumn); /// /// Can provide C#, IL and Native representation of a method. /// -public static partial class MethodDecompiler; \ No newline at end of file +public static partial class MethodDecompiler +{ + private static MethodDecompilerCode EmptyCode { get; } = new([], null); + private static MethodDecompilerResult Empty { get; } = new(EmptyCode, EmptyCode, EmptyCode); + + public static MethodDecompilerResult DecompileMethod(MethodBase? method, int? ilOffset, bool skipDecompilation, + Func getAssemblyStream, + Func getPdbStream, + Func getUrlContent) + { + if (method is null) return Empty; + + var ilCode = EmptyCode; + var ilWithCSharpCode = EmptyCode; + + var csharpSource = default(SourceLocation); + + if (getPdbStream(method.Module.Assembly) is { } pdbStream) + { + using var stream = pdbStream; + + var csharpSources = DebugCodeProvider.GetCSharpSourceFromPdb(method, pdbStream, false); + csharpSource = GetSourceLocation(csharpSources, getUrlContent); + } + + if (getAssemblyStream(method.Module.Assembly) is { } originalStream) + { + using var stream = originalStream; + + if (csharpSource is null) + { + stream.Seek(0, SeekOrigin.Begin); + var csharpSources = DebugCodeProvider.GetCSharpSourcesFromAssembly(method, stream, false); + csharpSource = GetSourceLocation(csharpSources, getUrlContent); + } + + if (csharpSource is null && !skipDecompilation) + { + stream.Seek(0, SeekOrigin.Begin); + csharpSource = ILSpyCodeProvider.GetCSharpSourceFromAssembly(method, stream, false); + } + + stream.Seek(0, SeekOrigin.Begin); + ilCode = DecompileILCodeInternal(stream, method.MetadataToken, ilOffset); + + stream.Seek(0, SeekOrigin.Begin); + ilWithCSharpCode = DecompileILWithCSharpCodeInternal(stream, method.MetadataToken, ilOffset, csharpSource); + } + else if (TryCopyMethod(method, getAssemblyStream, out var methodCopyStream, out var methodToken)) + { + using var stream = methodCopyStream; + + if (csharpSource is null && !skipDecompilation) + { + stream.Seek(0, SeekOrigin.Begin); + csharpSource = ILSpyCodeProvider.GetCSharpSourceFromAssembly(method, stream, false); + } + + stream.Seek(0, SeekOrigin.Begin); + ilCode = DecompileILCodeInternal(stream, methodToken, ilOffset); + + stream.Seek(0, SeekOrigin.Begin); + ilWithCSharpCode = DecompileILWithCSharpCodeInternal(stream, method.MetadataToken, ilOffset, csharpSource); + + } + + // If we didn't get any IL code, try to get it from the method definition + if (ilCode.Code.Count == 0) + { + if (TryGetMethodDefinition(method, out _, out var methodDefinition)) + { + ilCode = DecompileILCodeInternal(methodDefinition, ilOffset); + } + } + + + var csharpSequence = csharpSource?.SequencePoints.FirstOrDefault(x => x.Offset <= ilOffset && x.EndOffset > ilOffset); + if (csharpSource is not null && csharpSequence is not null && csharpSequence.IsHidden) + { + var idx = csharpSource.SequencePoints.IndexOf(csharpSequence); + if (idx > 0) + csharpSequence = csharpSource.SequencePoints[idx - 1]; + } + + var csharpCode = new MethodDecompilerCode( + csharpSource?.SourceFile.GetMethodLines(csharpSource.SequencePoints) ?? [], + csharpSequence is null ? null : new(csharpSequence.StartLine, csharpSequence.StartColumn, csharpSequence.EndLine, csharpSequence.EndColumn) + ); + + return new(ilCode, ilWithCSharpCode, csharpCode); + } + + private static SourceLocation? GetSourceLocation(List csharpSources, Func getUrlContent) + { + var embeddedSource = csharpSources.FirstOrDefault(x => x.SourceFile is SourceFileEmbedded); + if (embeddedSource is not null) + { + csharpSources.Remove(embeddedSource); + return embeddedSource; + } + + var sourceLink = csharpSources.FirstOrDefault(x => x.SourceFile is SourceFileSourceLink); + if (sourceLink is not null) + { + csharpSources.Remove(sourceLink); + var (filePath, sourceLinkUrl) = (SourceFileSourceLink) sourceLink.SourceFile; + var sourceCodeString = getUrlContent(sourceLinkUrl); + if (sourceCodeString is not null) + return sourceLink with { SourceFile = new SourceFileSourceLinkResolved(filePath, sourceCodeString) }; + } + + + return csharpSources.FirstOrDefault(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/ReferenceImporter.cs b/src/BUTR.CrashReport.Decompilers/Utils/ReferenceImporter.cs index 8ef92b8..eac8bf0 100644 --- a/src/BUTR.CrashReport.Decompilers/Utils/ReferenceImporter.cs +++ b/src/BUTR.CrashReport.Decompilers/Utils/ReferenceImporter.cs @@ -1,37 +1,16 @@ using AsmResolver.DotNet; +using AsmResolver.IO; + +using BUTR.CrashReport.Decompilers.Models; using System; -using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Reflection; namespace BUTR.CrashReport.Decompilers.Utils; -/// -/// -/// -public record AssemblyTypeReferenceInternal -{ - /// - /// - /// - /// - public required string Name { get; set; } - - /// - /// - /// - /// - public required string Namespace { get; set; } - - /// - /// - /// - /// - public required string FullName { get; set; } -} - /// /// Helper method to create import models from an assembly /// @@ -40,10 +19,10 @@ public static class ReferenceImporter /// /// Helper method to create import models from an assembly /// - public static Dictionary GetImportedTypeReferences(Dictionary AvailableAssemblies) => AvailableAssemblies.ToDictionary(x => x.Key, x => + public static AssemblyTypeReferenceInternal[] GetImportedTypeReferences(Assembly assembly, Func getAssemblyStream) { // TODO: Can we do that with the built-in Reflection API? - foreach (var assemblyModule in x.Value.Modules) + foreach (var assemblyModule in assembly.Modules) { try { @@ -57,30 +36,34 @@ public static Dictionary GetImpor } catch (Exception e) { - Trace.TraceError(x.Key.ToString()); + Trace.TraceError(assembly.FullName); Trace.TraceError(e.ToString()); } } try { - var assembly = AssemblyDefinition.FromFile(x.Value.Location); - foreach (var module in assembly.Modules) + if (getAssemblyStream(assembly) is { } stream) { - return module.GetImportedTypeReferences().Select(y => new AssemblyTypeReferenceInternal + var assemblyDefinition = AssemblyDefinition.FromReader(new BinaryStreamReader(new StreamDataSource(stream))); + foreach (var module in assemblyDefinition.Modules) { - Name = y.Name ?? string.Empty, - Namespace = y.Namespace ?? string.Empty, - FullName = y.FullName, - }).ToArray(); + return module.GetImportedTypeReferences().Select(y => new AssemblyTypeReferenceInternal + { + Name = y.Name ?? string.Empty, + Namespace = y.Namespace ?? string.Empty, + FullName = y.FullName, + }).ToArray(); + } } + } catch (Exception e) { - Trace.TraceError(x.Key.ToString()); + Trace.TraceError(assembly.FullName); Trace.TraceError(e.ToString()); } return []; - }); + } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Decompilers/Utils/StreamDataSource.cs b/src/BUTR.CrashReport.Decompilers/Utils/StreamDataSource.cs new file mode 100644 index 0000000..0caa84c --- /dev/null +++ b/src/BUTR.CrashReport.Decompilers/Utils/StreamDataSource.cs @@ -0,0 +1,36 @@ +using AsmResolver.IO; + +using System.IO; + +namespace BUTR.CrashReport.Decompilers.Utils; + +internal class StreamDataSource : IDataSource +{ + public ulong BaseAddress { get; } + + public byte this[ulong address] + { + get + { + _stream.Seek((long) (address - BaseAddress), SeekOrigin.Begin); + return (byte) _stream.ReadByte(); + } + } + public ulong Length => (ulong) _stream.Length; + + private readonly Stream _stream; + + public StreamDataSource(Stream stream) + { + _stream = stream; + BaseAddress = 0; + } + + public bool IsValidAddress(ulong address) => address - BaseAddress < (ulong) _stream.Length; + + public int ReadBytes(ulong address, byte[] buffer, int index, int count) + { + _stream.Seek((long) (address - BaseAddress), SeekOrigin.Begin); + return _stream.Read(buffer, index, count); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Emscripten/BUTR.CrashReport.Emscripten.csproj b/src/BUTR.CrashReport.Emscripten/BUTR.CrashReport.Emscripten.csproj new file mode 100644 index 0000000..79a81b1 --- /dev/null +++ b/src/BUTR.CrashReport.Emscripten/BUTR.CrashReport.Emscripten.csproj @@ -0,0 +1,24 @@ + + + + net9.0 + enable + enable + latest + true + Emscripten + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/BUTR.CrashReport.Emscripten/Emscripten.cs b/src/BUTR.CrashReport.Emscripten/Emscripten.cs new file mode 100644 index 0000000..28d912a --- /dev/null +++ b/src/BUTR.CrashReport.Emscripten/Emscripten.cs @@ -0,0 +1,54 @@ +using System.Runtime.InteropServices; + +namespace Emscripten; + +[StructLayout(LayoutKind.Sequential)] +public readonly ref struct EmscriptenUiEvent +{ + public readonly int Detail; + public readonly int DocumentBodyClientWidth; + public readonly int DocumentBodyClientHeight; + public readonly int WindowInnerWidth; + public readonly int WindowInnerHeight; + public readonly int WindowOuterWidth; + public readonly int WindowOuterHeight; + public readonly int ScrollTop; + public readonly int ScrollLeft; +} + +[StructLayout(LayoutKind.Sequential)] +public struct EmscriptenCanvasSizeChangeData +{ + public required IntPtr Window; + public required IntPtrByte CanvasId; +} + +public class Emscripten +{ + // ReSharper disable UnusedMember.Local + public const int EM_FALSE = 0; + public const int EM_TRUE = 1; + + public const int EM_CALLBACK_THREAD_CONTEXT_MAIN_RUNTIME_THREAD = 0x01; + public const int EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD = 0x02; + + public const int EMSCRIPTEN_EVENT_TARGET_DOCUMENT = 1; + public const int EMSCRIPTEN_EVENT_TARGET_WINDOW = 2; + // ReSharper restore UnusedMember.Local + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public unsafe delegate void emscripten_set_main_loopDel(em_arg_callback_func cb, int fps, int simulate_infinite_loop); + public emscripten_set_main_loopDel emscripten_set_main_loop = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public unsafe delegate EMSCRIPTEN_RESULT emscripten_set_resize_callback_on_threadDel(IntPtrByte target, IntPtrEmscriptenCanvasSizeChangeData userData, int useCapture, em_ui_callback_func callback, IntPtr thread); + public emscripten_set_resize_callback_on_threadDel emscripten_set_resize_callback_on_thread = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void custom_emscripten_get_display_usable_boundsDel(IntPtrInt width, IntPtrInt height); + public custom_emscripten_get_display_usable_boundsDel custom_emscripten_get_display_usable_bounds = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void custom_emscripten_set_element_style_sizeDel(IntPtrByte element_id, int width, int height); + public custom_emscripten_set_element_style_sizeDel custom_emscripten_set_element_style_size = null!; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Emscripten/GlobalUsing.cs b/src/BUTR.CrashReport.Emscripten/GlobalUsing.cs new file mode 100644 index 0000000..239ebb8 --- /dev/null +++ b/src/BUTR.CrashReport.Emscripten/GlobalUsing.cs @@ -0,0 +1,19 @@ +// ReSharper disable RedundantUsingDirective.Global +global using EMSCRIPTEN_RESULT = int; + +global using UnusedI = int; +// ReSharper restore RedundantUsingDirective.Global + +#if NET6_0_OR_GREATER +global using IntPtrByte = BUTR.CrashReport.Native.Pointer; +global using IntPtrInt = BUTR.CrashReport.Native.Pointer; +global using IntPtrEmscriptenCanvasSizeChangeData = BUTR.CrashReport.Native.Pointer; +global using unsafe em_ui_callback_func = delegate* unmanaged[Cdecl]; +global using unsafe em_arg_callback_func = delegate* unmanaged[Cdecl]; +#else +global using IntPtrByte = BUTR.CrashReport.Native.Pointer; +global using IntPtrInt = BUTR.CrashReport.Native.Pointer; +global using IntPtrEmscriptenCanvasSizeChangeData = BUTR.CrashReport.Native.Pointer; +global using em_ui_callback_func = BUTR.CrashReport.Native.Pointer; +global using em_arg_callback_func = BUTR.CrashReport.Native.Pointer; +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.ILMerged/BUTR.CrashReport.ILMerged.csproj b/src/BUTR.CrashReport.ILMerged/BUTR.CrashReport.ILMerged.csproj new file mode 100644 index 0000000..e03f534 --- /dev/null +++ b/src/BUTR.CrashReport.ILMerged/BUTR.CrashReport.ILMerged.csproj @@ -0,0 +1,70 @@ + + + + netstandard2.0 + latest + enable + + BUTR.CrashReport + + true + true + + + + Debug;Release + true + true + true + false + + + + + + + + BUTR.CrashReport.ILMerged + BUTR.CrashReport.ILMerged + Contains the code for creating the crash reports. Dependencies are ILMerged. + MIT + https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png + butr crash report bannerlord + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)BUTR.CrashReport.Models.dll; + + + + + + false + + + + diff --git a/src/BUTR.CrashReport.ILMerged/Extensions/DictionaryExtensions.cs b/src/BUTR.CrashReport.ILMerged/Extensions/DictionaryExtensions.cs new file mode 100644 index 0000000..77c608b --- /dev/null +++ b/src/BUTR.CrashReport.ILMerged/Extensions/DictionaryExtensions.cs @@ -0,0 +1,10 @@ +namespace System.Collections.Generic; + +internal static class DictionaryExtensions +{ + public static void Deconstruct(this KeyValuePair tuple, out T1 key, out T2 value) + { + key = tuple.Key; + value = tuple.Value; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/BUTR.CrashReport.ImGui.csproj b/src/BUTR.CrashReport.ImGui/BUTR.CrashReport.ImGui.csproj new file mode 100644 index 0000000..dcab41f --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/BUTR.CrashReport.ImGui.csproj @@ -0,0 +1,51 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + enable + latest + true + + enable + + + + BUTR.CrashReport.ImGui + BUTR.CrashReport.ImGui + Contains the ImGui renderer for showing the crash report. Requires a backend like 'BUTR.CrashReport.Renderer.ImGui.CImGui' + MIT + https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png + butr crash report imgui + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiChildFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiChildFlags.cs new file mode 100644 index 0000000..b887839 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiChildFlags.cs @@ -0,0 +1,18 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiChildFlags +{ + None = 0, + Border = 1, + AlwaysUseWindowPadding = 2, + ResizeX = 4, + ResizeY = 8, + AutoResizeX = 16, + AutoResizeY = 32, + AlwaysAutoResize = 64, + FrameStyle = 128, + NavFlattened = 256, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiCol.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiCol.cs new file mode 100644 index 0000000..b7c22e5 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiCol.cs @@ -0,0 +1,66 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +// ReSharper disable UnusedMember.Global +public enum ImGuiCol +{ + Text = 0, + TextDisabled = 1, + WindowBg = 2, + ChildBg = 3, + PopupBg = 4, + Border = 5, + BorderShadow = 6, + FrameBg = 7, + FrameBgHovered = 8, + FrameBgActive = 9, + TitleBg = 10, + TitleBgActive = 11, + TitleBgCollapsed = 12, + MenuBarBg = 13, + ScrollbarBg = 14, + ScrollbarGrab = 15, + ScrollbarGrabHovered = 16, + ScrollbarGrabActive = 17, + CheckMark = 18, + SliderGrab = 19, + SliderGrabActive = 20, + Button = 21, + ButtonHovered = 22, + ButtonActive = 23, + Header = 24, + HeaderHovered = 25, + HeaderActive = 26, + Separator = 27, + SeparatorHovered = 28, + SeparatorActive = 29, + ResizeGrip = 30, + ResizeGripHovered = 31, + ResizeGripActive = 32, + TabHovered = 33, + Tab = 34, + TabSelected = 35, + TabSelectedOverline = 36, + TabDimmed = 37, + TabDimmedSelected = 38, + TabDimmedSelectedOverline = 39, + DockingPreview = 40, + DockingEmptyBg = 41, + PlotLines = 42, + PlotLinesHovered = 43, + PlotHistogram = 44, + PlotHistogramHovered = 45, + TableHeaderBg = 46, + TableBorderStrong = 47, + TableBorderLight = 48, + TableRowBg = 49, + TableRowBgAlt = 50, + TextLink = 51, + TextSelectedBg = 52, + DragDropTarget = 53, + NavHighlight = 54, + NavWindowingHighlight = 55, + NavWindowingDimBg = 56, + ModalWindowDimBg = 57, + COUNT = 58, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiDir.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiDir.cs new file mode 100644 index 0000000..621fb6c --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiDir.cs @@ -0,0 +1,13 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +// ReSharper disable UnusedMember.Global +public enum ImGuiDir +{ + None = -1, + Left = 0, + Right = 1, + Up = 2, + Down = 3, + COUNT = 4, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiHoveredFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiHoveredFlags.cs new file mode 100644 index 0000000..430c173 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiHoveredFlags.cs @@ -0,0 +1,29 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiHoveredFlags +{ + None = 0, + ChildWindows = 1, + RootWindow = 2, + AnyWindow = 4, + NoPopupHierarchy = 8, + DockHierarchy = 16, + AllowWhenBlockedByPopup = 32, + AllowWhenBlockedByActiveItem = 128, + AllowWhenOverlappedByItem = 256, + AllowWhenOverlappedByWindow = 512, + AllowWhenDisabled = 1024, + NoNavOverride = 2048, + AllowWhenOverlapped = 768, + RectOnly = 928, + RootAndChildWindows = 3, + ForTooltip = 4096, + Stationary = 8192, + DelayNone = 16384, + DelayShort = 32768, + DelayNormal = 65536, + NoSharedDelay = 131072, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiInputTextFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiInputTextFlags.cs new file mode 100644 index 0000000..e62b9c2 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiInputTextFlags.cs @@ -0,0 +1,32 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiInputTextFlags +{ + None = 0, + CharsDecimal = 1, + CharsHexadecimal = 2, + CharsScientific = 4, + CharsUppercase = 8, + CharsNoBlank = 16, + AllowTabInput = 32, + EnterReturnsTrue = 64, + EscapeClearsAll = 128, + CtrlEnterForNewLine = 256, + ReadOnly = 512, + Password = 1024, + AlwaysOverwrite = 2048, + AutoSelectAll = 4096, + ParseEmptyRefVal = 8192, + DisplayEmptyRefVal = 16384, + NoHorizontalScroll = 32768, + NoUndoRedo = 65536, + CallbackCompletion = 131072, + CallbackHistory = 262144, + CallbackAlways = 524288, + CallbackCharFilter = 1048576, + CallbackResize = 2097152, + CallbackEdit = 4194304, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiKey.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiKey.cs new file mode 100644 index 0000000..a7e464e --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiKey.cs @@ -0,0 +1,174 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +// ReSharper disable UnusedMember.Global +public enum ImGuiKey +{ + None = 0, + Tab = 512, + LeftArrow = 513, + RightArrow = 514, + UpArrow = 515, + DownArrow = 516, + PageUp = 517, + PageDown = 518, + Home = 519, + End = 520, + Insert = 521, + Delete = 522, + Backspace = 523, + Space = 524, + Enter = 525, + Escape = 526, + LeftCtrl = 527, + LeftShift = 528, + LeftAlt = 529, + LeftSuper = 530, + RightCtrl = 531, + RightShift = 532, + RightAlt = 533, + RightSuper = 534, + Menu = 535, + _0 = 536, + _1 = 537, + _2 = 538, + _3 = 539, + _4 = 540, + _5 = 541, + _6 = 542, + _7 = 543, + _8 = 544, + _9 = 545, + A = 546, + B = 547, + C = 548, + D = 549, + E = 550, + F = 551, + G = 552, + H = 553, + I = 554, + J = 555, + K = 556, + L = 557, + M = 558, + N = 559, + O = 560, + P = 561, + Q = 562, + R = 563, + S = 564, + T = 565, + U = 566, + V = 567, + W = 568, + X = 569, + Y = 570, + Z = 571, + F1 = 572, + F2 = 573, + F3 = 574, + F4 = 575, + F5 = 576, + F6 = 577, + F7 = 578, + F8 = 579, + F9 = 580, + F10 = 581, + F11 = 582, + F12 = 583, + F13 = 584, + F14 = 585, + F15 = 586, + F16 = 587, + F17 = 588, + F18 = 589, + F19 = 590, + F20 = 591, + F21 = 592, + F22 = 593, + F23 = 594, + F24 = 595, + Apostrophe = 596, + Comma = 597, + Minus = 598, + Period = 599, + Slash = 600, + Semicolon = 601, + Equal = 602, + LeftBracket = 603, + Backslash = 604, + RightBracket = 605, + GraveAccent = 606, + CapsLock = 607, + ScrollLock = 608, + NumLock = 609, + PrintScreen = 610, + Pause = 611, + Keypad0 = 612, + Keypad1 = 613, + Keypad2 = 614, + Keypad3 = 615, + Keypad4 = 616, + Keypad5 = 617, + Keypad6 = 618, + Keypad7 = 619, + Keypad8 = 620, + Keypad9 = 621, + KeypadDecimal = 622, + KeypadDivide = 623, + KeypadMultiply = 624, + KeypadSubtract = 625, + KeypadAdd = 626, + KeypadEnter = 627, + KeypadEqual = 628, + AppBack = 629, + AppForward = 630, + GamepadStart = 631, + GamepadBack = 632, + GamepadFaceLeft = 633, + GamepadFaceRight = 634, + GamepadFaceUp = 635, + GamepadFaceDown = 636, + GamepadDpadLeft = 637, + GamepadDpadRight = 638, + GamepadDpadUp = 639, + GamepadDpadDown = 640, + GamepadL1 = 641, + GamepadR1 = 642, + GamepadL2 = 643, + GamepadR2 = 644, + GamepadL3 = 645, + GamepadR3 = 646, + GamepadLStickLeft = 647, + GamepadLStickRight = 648, + GamepadLStickUp = 649, + GamepadLStickDown = 650, + GamepadRStickLeft = 651, + GamepadRStickRight = 652, + GamepadRStickUp = 653, + GamepadRStickDown = 654, + MouseLeft = 655, + MouseRight = 656, + MouseMiddle = 657, + MouseX1 = 658, + MouseX2 = 659, + MouseWheelX = 660, + MouseWheelY = 661, + ReservedForModCtrl = 662, + ReservedForModShift = 663, + ReservedForModAlt = 664, + ReservedForModSuper = 665, + COUNT = 666, + ModNone = 0, + ModCtrl = 4096, + ModShift = 8192, + ModAlt = 16384, + ModSuper = 32768, + ModMask = 61440, + NamedKey_BEGIN = 512, + NamedKey_END = 666, + NamedKey_COUNT = 154, + KeysData_SIZE = 154, + KeysData_OFFSET = 512, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiMouseButton.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiMouseButton.cs new file mode 100644 index 0000000..6dab352 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiMouseButton.cs @@ -0,0 +1,11 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +// ReSharper disable UnusedMember.Global +public enum ImGuiMouseButton +{ + Left = 0, + Right = 1, + Middle = 2, + COUNT = 5, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiMouseCursor.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiMouseCursor.cs new file mode 100644 index 0000000..bd43bf8 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiMouseCursor.cs @@ -0,0 +1,18 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +// ReSharper disable UnusedMember.Global +public enum ImGuiMouseCursor +{ + None = -1, + Arrow = 0, + TextInput = 1, + ResizeAll = 2, + ResizeNS = 3, + ResizeEW = 4, + ResizeNESW = 5, + ResizeNWSE = 6, + Hand = 7, + NotAllowed = 8, + COUNT = 9, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiPopupFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiPopupFlags.cs new file mode 100644 index 0000000..f94d29c --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiPopupFlags.cs @@ -0,0 +1,20 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiPopupFlags +{ + None = 0, + MouseButtonLeft = 0, + MouseButtonRight = 1, + MouseButtonMiddle = 2, + MouseButtonMask = 31, + MouseButtonDefault = 1, + NoReopen = 32, + NoOpenOverExistingPopup = 128, + NoOpenOverItems = 256, + AnyPopupId = 1024, + AnyPopupLevel = 2048, + AnyPopup = 3072, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiSelectableFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiSelectableFlags.cs new file mode 100644 index 0000000..cea9d3d --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiSelectableFlags.cs @@ -0,0 +1,15 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiSelectableFlags +{ + None = 0, + NoAutoClosePopups = 1, + SpanAllColumns = 2, + AllowDoubleClick = 4, + Disabled = 8, + AllowOverlap = 16, + Highlight = 32, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiStyleVar.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiStyleVar.cs new file mode 100644 index 0000000..426c6e2 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiStyleVar.cs @@ -0,0 +1,42 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +// ReSharper disable UnusedMember.Global +public enum ImGuiStyleVar +{ + Alpha = 0, + DisabledAlpha = 1, + WindowPadding = 2, + WindowRounding = 3, + WindowBorderSize = 4, + WindowMinSize = 5, + WindowTitleAlign = 6, + ChildRounding = 7, + ChildBorderSize = 8, + PopupRounding = 9, + PopupBorderSize = 10, + FramePadding = 11, + FrameRounding = 12, + FrameBorderSize = 13, + ItemSpacing = 14, + ItemInnerSpacing = 15, + IndentSpacing = 16, + CellPadding = 17, + ScrollbarSize = 18, + ScrollbarRounding = 19, + GrabMinSize = 20, + GrabRounding = 21, + TabRounding = 22, + TabBorderSize = 23, + TabBarBorderSize = 24, + TabBarOverlineSize = 25, + TableAngledHeadersAngle = 26, + TableAngledHeadersTextAlign = 27, + ButtonTextAlign = 28, + SelectableTextAlign = 29, + SeparatorTextBorderSize = 30, + SeparatorTextAlign = 31, + SeparatorTextPadding = 32, + DockingSeparatorSize = 33, + COUNT = 34, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiTreeNodeFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiTreeNodeFlags.cs new file mode 100644 index 0000000..d4f8093 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiTreeNodeFlags.cs @@ -0,0 +1,26 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiTreeNodeFlags +{ + None = 0, + Selected = 1, + Framed = 2, + AllowOverlap = 4, + NoTreePushOnOpen = 8, + NoAutoOpenOnLog = 16, + DefaultOpen = 32, + OpenOnDoubleClick = 64, + OpenOnArrow = 128, + Leaf = 256, + Bullet = 512, + FramePadding = 1024, + SpanAvailWidth = 2048, + SpanFullWidth = 4096, + SpanTextWidth = 8192, + SpanAllColumns = 16384, + NavLeftJumpsBackHere = 32768, + CollapsingHeader = 26, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Enums/ImGuiWindowFlags.cs b/src/BUTR.CrashReport.ImGui/Enums/ImGuiWindowFlags.cs new file mode 100644 index 0000000..1f26117 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Enums/ImGuiWindowFlags.cs @@ -0,0 +1,38 @@ +namespace BUTR.CrashReport.ImGui.Enums; + +[Flags] +// ReSharper disable UnusedMember.Global +public enum ImGuiWindowFlags +{ + None = 0, + NoTitleBar = 1, + NoResize = 2, + NoMove = 4, + NoScrollbar = 8, + NoScrollWithMouse = 16, + NoCollapse = 32, + AlwaysAutoResize = 64, + NoBackground = 128, + NoSavedSettings = 256, + NoMouseInputs = 512, + MenuBar = 1024, + HorizontalScrollbar = 2048, + NoFocusOnAppearing = 4096, + NoBringToFrontOnFocus = 8192, + AlwaysVerticalScrollbar = 16384, + AlwaysHorizontalScrollbar = 32768, + NoNavInputs = 65536, + NoNavFocus = 131072, + UnsavedDocument = 262144, + NoDocking = 524288, + NoNav = 196608, + NoDecoration = 43, + NoInputs = 197120, + ChildWindow = 16777216, + Tooltip = 33554432, + Popup = 67108864, + Modal = 134217728, + ChildMenu = 268435456, + DockNodeHost = 536870912, +} +// ReSharper restore UnusedMember.Global \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.IImDrawListRef.cs b/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.IImDrawListRef.cs new file mode 100644 index 0000000..65afaf6 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.IImDrawListRef.cs @@ -0,0 +1,26 @@ +using BUTR.CrashReport.ImGui.Structures; +using BUTR.CrashReport.Memory.Utils; + +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace BUTR.CrashReport.ImGui.Extensions; + +partial class IImGuiExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void AddText(this TImDrawListRef imGui, ref readonly Vector2 pos, uint col, ReadOnlySpan utf16Data) + where TImDrawListRef : IImDrawList + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Data.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Data, utf8); + utf8[length] = 0; + + imGui.AddText(in pos, col, utf8.Slice(0, length + 1)); + tempMemory?.Dispose(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.String.cs b/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.String.cs new file mode 100644 index 0000000..268ac2c --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.String.cs @@ -0,0 +1,205 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.Memory.Utils; + +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace BUTR.CrashReport.ImGui.Extensions; + +partial class IImGuiExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void PushId(this IImGui imGui, string utf16Label) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + imGui.PushId(utf8.Slice(0, length)); + tempMemory?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void Text(this IImGui imGui, string utf16Label) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + imGui.Text(utf8.Slice(0, length)); + tempMemory?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void TextWrapped(this IImGui imGui, string utf16Label) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + imGui.TextWrapped(utf8.Slice(0, length)); + tempMemory?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool SmallButton(this IImGui imGui, string utf16Label) + { + imGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); + + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + var result = imGui.SmallButton(utf8.Slice(0, length)); + tempMemory?.Dispose(); + imGui.PopStyleVar(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void TextLinkOpenURL(this IImGui imGui, string utf16Label, string utf16Url) + { + var utf8LabelByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemoryLabel = utf8LabelByteCount > 2048 ? MemoryPool.Shared.Rent(utf8LabelByteCount) : null; + var utf8Label = utf8LabelByteCount <= 2048 ? stackalloc byte[utf8LabelByteCount] : tempMemoryLabel!.Memory.Span; + var labelLength = Utf8Utils.Utf16ToUtf8(utf16Label, utf8Label); + utf8Label[labelLength] = 0; + + var utf8UrlByteCount = Encoding.UTF8.GetMaxByteCount(utf16Url.Length) + 1; + var tempMemoryUrl = utf8LabelByteCount > 2048 ? MemoryPool.Shared.Rent(utf8UrlByteCount) : null; + var utf8Url = utf8UrlByteCount <= 2048 ? stackalloc byte[utf8UrlByteCount] : tempMemoryUrl!.Memory.Span; + var urlLength = Utf8Utils.Utf16ToUtf8(utf16Url, utf8Url); + utf8Url[urlLength] = 0; + + imGui.TextLinkOpenURL(utf8Label.Slice(0, labelLength), utf8Url.Slice(0, urlLength)); + tempMemoryLabel?.Dispose(); + tempMemoryUrl?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool Selectable(this IImGui imGui, string utf16Label, ref bool isSelected, ImGuiSelectableFlags flags) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + var result = imGui.Selectable(utf8.Slice(0, length), ref isSelected, flags); + tempMemory?.Dispose(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool TreeNode(this IImGui imGui, string utf16Label) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + var result = imGui.TreeNode(utf8.Slice(0, length), ImGuiTreeNodeFlags.None); + tempMemory?.Dispose(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool TreeNode(this IImGui imGui, string utf16Label, ImGuiTreeNodeFlags flags) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + var result = imGui.TreeNode(utf8.Slice(0, length), flags); + tempMemory?.Dispose(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool BeginChild(this IImGui imGui, string utf16Label, ref readonly Vector2 size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Label.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Label, utf8); + utf8[length] = 0; + + imGui.PushStyleVar(ImGuiStyleVar.ChildRounding, 5f); + var result = imGui.BeginChild(utf8.Slice(0, length), in size, child_flags, window_flags); + tempMemory?.Dispose(); + imGui.PopStyleVar(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void SetClipboardText(this IImGui imGui, ReadOnlySpan utf16Data) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Data.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Data, utf8); + utf8[length] = 0; + + imGui.SetClipboardText(utf8.Slice(0, length)); + tempMemory?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void SetClipboardText(this IImGui imGui, string utf16Data) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Data.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Data, utf8); + utf8[length] = 0; + + imGui.SetClipboardText(utf8.Slice(0, length)); + tempMemory?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void CalcTextSize(this IImGui imGui, char utf16, out Vector2 size) + { + Span utf16Data = stackalloc char[] { utf16 }; + CalcTextSize(imGui, utf16Data, out size); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void CalcTextSize(this IImGui imGui, ReadOnlySpan utf16Data, out Vector2 size) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Data.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Data, utf8); + utf8[length] = 0; + + imGui.CalcTextSize(utf8.Slice(0, length + 1), out size); + tempMemory?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void CalcTextSize(this IImGui imGui, string utf16Data, out Vector2 size) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Data.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Data, utf8); + utf8[length] = 0; + + imGui.CalcTextSize(utf8.Slice(0, length + 1), out size); + tempMemory?.Dispose(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.cs b/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.cs new file mode 100644 index 0000000..24c8e5e --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Extensions/IImGuiExtensions.cs @@ -0,0 +1,132 @@ +using BUTR.CrashReport.ImGui.Enums; + +using System.Buffers; +using System.Buffers.Text; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace BUTR.CrashReport.ImGui.Extensions; + +public static partial class IImGuiExtensions +{ + private const MethodImplOptions AggressiveOptimization = (MethodImplOptions) 512; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void RenderId(this IImGui imGui, ReadOnlySpan title, string id) + { + imGui.Text(title); + imGui.SameLine(0.0f, -1.0f); + imGui.SmallButton(id); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void Text(this IImGui imGui, bool fmt) + { + var @true = "true\0"u8; + var @false = "false\0"u8; + imGui.Text(fmt ? @true : @false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void Text(this IImGui imGui, int value) + { + Span valueUtf8 = stackalloc byte[sizeof(int) * sizeof(char) + 1]; + Utf8Formatter.TryFormat(value, valueUtf8, out _); + valueUtf8[valueUtf8.Length - 1] = 0; + imGui.Text(valueUtf8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void Text(this IImGui imGui, ref readonly DateTime value) + { + Span valueUtf8 = stackalloc byte[64]; + Utf8Formatter.TryFormat(value, valueUtf8, out var written, new StandardFormat('O')); + valueUtf8[written] = 0; + imGui.Text(valueUtf8.Slice(0, written)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void Text(this IImGui imGui, ref readonly DateTimeOffset value) + { + Span valueUtf8 = stackalloc byte[64]; + Utf8Formatter.TryFormat(value, valueUtf8, out var written, new StandardFormat('O')); + valueUtf8[written] = 0; + imGui.Text(valueUtf8.Slice(0, written)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static void PadRight(this IImGui imGui, int toAppend) + { + Span padding = stackalloc byte[toAppend + 1]; + padding.Fill((byte) ' '); + padding[toAppend] = 0; + imGui.Text(padding); + imGui.SameLine(0, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool Button(this IImGui imGui, ReadOnlySpan label, ref readonly Vector4 color, ref readonly Vector4 hovered, ref readonly Vector4 active) + { + imGui.PushStyleColor(ImGuiCol.Button, in color); + imGui.PushStyleColor(ImGuiCol.ButtonHovered, in hovered); + imGui.PushStyleColor(ImGuiCol.ButtonActive, in active); + var result = imGui.Button(label); + imGui.PopStyleColor(3); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool SmallButtonRound(this IImGui imGui, ReadOnlySpan utf8Label) + { + imGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); + var result = imGui.SmallButton(utf8Label); + imGui.PopStyleVar(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool SmallButtonRound(this IImGui imGui, string utf16Label) + { + imGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); + var result = imGui.SmallButton(utf16Label); + imGui.PopStyleVar(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool CheckboxRound(this IImGui imGui, ReadOnlySpan utf8Label, ref bool isSelected) + { + imGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f); + imGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); + var result = imGui.Checkbox(utf8Label, ref isSelected); + imGui.PopStyleVar(2); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool BeginChildRound(this IImGui imGui, ReadOnlySpan utf8Label, ref readonly Vector2 size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) + { + imGui.PushStyleVar(ImGuiStyleVar.ChildRounding, 5f); + var result = imGui.BeginChild(utf8Label, in size, child_flags, window_flags); + imGui.PopStyleVar(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool BeginChild(this IImGui imGui, string label, ref readonly Vector2 size, ref readonly Vector4 color, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) + { + imGui.PushStyleColor(ImGuiCol.ChildBg, in color); + var result = imGui.BeginChild(label, in size, child_flags, window_flags); + imGui.PopStyleColor(); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool BeginChild(this IImGui imGui, ReadOnlySpan strId, ref readonly Vector2 size, ref readonly Vector4 color, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) + { + imGui.PushStyleColor(ImGuiCol.ChildBg, in color); + var result = imGui.BeginChild(strId, in size, child_flags, window_flags); + imGui.PopStyleColor(); + return result; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGui.cs b/src/BUTR.CrashReport.ImGui/IImGui.cs new file mode 100644 index 0000000..4a41da4 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGui.cs @@ -0,0 +1,135 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGui : IDisposable +{ + void PushId(int id); + void PushId(ReadOnlySpan utf8Label); + void PopId(); + + bool InputTextMultilineInt64(ReadOnlySpan utf8Label, Span input, int lineCount, ImGuiInputTextInt64Callback callback, Int64 data); + + // BUG: Generic method doesn't work with NativeAOT + bool InputTextMultiline(ReadOnlySpan utf8Label, Span input, int lineCount, ImGuiInputTextCallback callback, TData data) + where TData : struct; + + bool InputText(ReadOnlySpan utf8Label, Span input, ImGuiInputTextFlags flags); + + void Text(ReadOnlySpan utf8Label); + + void TextWrapped(ReadOnlySpan utf8Label); + + void TextColored(ReadOnlySpan utf8Label, ref readonly Vector4 color); + + void TextLinkOpenURL(ReadOnlySpan utf8Label, ReadOnlySpan utf8Url); + + bool Checkbox(ReadOnlySpan utf8Label, ref bool isSelected); + + void OpenPopup(ReadOnlySpan utf8Label, ImGuiPopupFlags flags); + bool BeginPopup(ReadOnlySpan utf8Label, ImGuiWindowFlags flags); + bool BeginPopupModal(ReadOnlySpan utf8Label, ImGuiWindowFlags flags); + bool BeginPopupContextWindow(ReadOnlySpan utf8Label); + void EndPopup(); + void CloseCurrentPopup(); + + bool Selectable(ReadOnlySpan utf8Label, ref bool isSelected, ImGuiSelectableFlags flags); + + bool Button(ReadOnlySpan utf8Label); + + bool SmallButton(ReadOnlySpan utf8Label); + + bool Begin(ReadOnlySpan utf8Label, ImGuiWindowFlags flags); + void End(); + + bool BeginTable(ReadOnlySpan utf8Label, int column); + bool TableNextColumn(); + void EndTable(); + + bool BeginChild(ReadOnlySpan utf8Label, ref readonly Vector2 size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags); + void EndChild(); + + bool TreeNode(ReadOnlySpan utf8Label, ImGuiTreeNodeFlags flags); + void TreePop(); + + void PushStyleColor(ImGuiCol idx, ref readonly Vector4 color); + void PopStyleColor(); + void PopStyleColor(int count); + + void PushStyleVar(ImGuiStyleVar idx, float value); + void PushStyleVar(ImGuiStyleVar idx, ref readonly Vector2 value); + void PopStyleVar(); + void PopStyleVar(int count); + + void SetNextWindowPos(ref readonly Vector2 position); + void SetNextWindowSize(ref readonly Vector2 size); + void SetNextWindowViewport(uint viewportId); + void StyleColorsLight(); + void StyleColorsDark(); + void SetWindowFontScale(float scale); + + void SameLine(); + void SameLine(float offsetFromStartX, float spacing); + + void NewLine(); + + void Separator(); + + void Bullet(); + + void Indent(); + void Unindent(); + + float GetTextLineHeight(); + float GetTextLineHeightWithSpacing(); + + bool IsWindowAppearing(); + bool IsWindowFocused(); + bool IsWindowHovered(); + IntPtr GetCurrentWindow(); + + void BringWindowToDisplayFront(IntPtr window); + + bool MenuItem(ReadOnlySpan utf8Label); + + bool IsItemClicked(ImGuiMouseButton mouseButton); + + bool IsMouseDoubleClicked(ImGuiMouseButton mouseButton); + bool IsKeyDown(ImGuiKey key); + bool IsKeyPressed(ImGuiKey key); + + void SetClipboardText(ReadOnlySpan utf8Data); + + void CalcTextSize(ReadOnlySpan utf8Data, out Vector2 size); + + float GetWindowHeight(); + float GetWindowWidth(); + + void SetWindowFocus(); + + void ColorConvertU32ToFloat4(uint u, out Vector4 color); + uint ColorConvertFloat4ToU32(ref readonly Vector4 color); + + float GetScrollX(); + float GetScrollY(); + void SetScrollX(float value); + void SetScrollY(float value); + + + void GetCursorScreenPos(out Vector2 cursorScreenPos); + void SetMouseCursor(ImGuiMouseCursor textInput); + bool IsMouseClicked(ImGuiMouseButton button); + void GetMousePos(out Vector2 mousePos); + bool IsMouseDragging(ImGuiMouseButton button); + bool IsMouseDown(ImGuiMouseButton button); + bool IsMouseHoveringRect(ref readonly Vector2 lineStartScreenPos, ref readonly Vector2 end); + + + void Dummy(ref readonly Vector2 vector2); + + void BeginTooltip(); + void EndTooltip(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGuiT.cs b/src/BUTR.CrashReport.ImGui/IImGuiT.cs new file mode 100644 index 0000000..e13d757 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGuiT.cs @@ -0,0 +1,24 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGui : IImGui, + IImGuiWithImGuiIO, + IImGuiWithImGuiViewport, + IImGuiWithImDrawList, + IImGuiWithImGuiStyle, + IImGuiWithImGuiListClipper + + where TImGuiIO : IImGuiIO + + where TImGuiViewport : IImGuiViewport + + where TImDrawList : IImDrawList + + where TImGuiStyle : IImGuiStyle + where TColorsRangeAccessor : IRangeAccessor + + where TImGuiListClipper : IImGuiListClipper; \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGuiWithImDrawList.cs b/src/BUTR.CrashReport.ImGui/IImGuiWithImDrawList.cs new file mode 100644 index 0000000..6b5bb6d --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGuiWithImDrawList.cs @@ -0,0 +1,9 @@ +using BUTR.CrashReport.ImGui.Structures; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGuiWithImDrawList + where TImDrawList : IImDrawList +{ + void GetWindowDrawList(out TImDrawList drawListRef); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiIO.cs b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiIO.cs new file mode 100644 index 0000000..c6e06aa --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiIO.cs @@ -0,0 +1,9 @@ +using BUTR.CrashReport.ImGui.Structures; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGuiWithImGuiIO + where TImGuiIORef : IImGuiIO +{ + void GetIO(out TImGuiIORef ioRef); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiListClipper.cs b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiListClipper.cs new file mode 100644 index 0000000..4026a89 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiListClipper.cs @@ -0,0 +1,9 @@ +using BUTR.CrashReport.ImGui.Structures; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGuiWithImGuiListClipper + where TImGuiListClipperRef : IImGuiListClipper +{ + void CreateImGuiListClipper(out TImGuiListClipperRef listClipperRef); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiStyle.cs b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiStyle.cs new file mode 100644 index 0000000..b0bf7f9 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiStyle.cs @@ -0,0 +1,13 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using System.Numerics; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGuiWithImGuiStyle + where TImGuiStyleRef : IImGuiStyle + where TColorsRangeAccessorRef : IRangeAccessor +{ + void GetStyle(out TImGuiStyleRef styleRef); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiViewport.cs b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiViewport.cs new file mode 100644 index 0000000..3eb5ab9 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/IImGuiWithImGuiViewport.cs @@ -0,0 +1,9 @@ +using BUTR.CrashReport.ImGui.Structures; + +namespace BUTR.CrashReport.ImGui; + +public interface IImGuiWithImGuiViewport + where TImGuiViewportRef : IImGuiViewport +{ + void GetMainViewport(out TImGuiViewportRef viewportRef); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/IImDrawData.cs b/src/BUTR.CrashReport.ImGui/Structures/IImDrawData.cs new file mode 100644 index 0000000..526271d --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/IImDrawData.cs @@ -0,0 +1,11 @@ +using System.Numerics; + +namespace BUTR.CrashReport.ImGui.Structures; + +public interface IImDrawList : IDisposable +{ + void AddText(ref readonly Vector2 pos, uint col, ReadOnlySpan utf8Data); + + void AddRect(ref readonly Vector2 min, ref readonly Vector2 max, uint col, float rounding); + void AddRectFilled(ref readonly Vector2 min, ref readonly Vector2 _max, uint col, float rounding); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/IImGuiIO.cs b/src/BUTR.CrashReport.ImGui/Structures/IImGuiIO.cs new file mode 100644 index 0000000..7b26607 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/IImGuiIO.cs @@ -0,0 +1,15 @@ +namespace BUTR.CrashReport.ImGui.Structures; + +public interface IImGuiIO : IDisposable +{ + ref bool KeyCtrl { get; } + ref bool KeyShift { get; } + ref bool KeyAlt { get; } + ref bool KeySuper { get; } + + ref bool ConfigMacOSXBehaviors { get; } + + ref bool WantCaptureKeyboard { get; } + ref bool WantCaptureMouse { get; } + ref bool WantTextInput { get; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/IImGuiListClipper.cs b/src/BUTR.CrashReport.ImGui/Structures/IImGuiListClipper.cs new file mode 100644 index 0000000..af20f99 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/IImGuiListClipper.cs @@ -0,0 +1,12 @@ +namespace BUTR.CrashReport.ImGui.Structures; + +public interface IImGuiListClipper : IDisposable +{ + ref int DisplayStart { get; } + ref int DisplayEnd { get; } + + void Begin(int items_count); + void Begin(int items_count, float items_height); + void End(); + bool Step(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/IImGuiStyle.cs b/src/BUTR.CrashReport.ImGui/Structures/IImGuiStyle.cs new file mode 100644 index 0000000..7cf03ef --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/IImGuiStyle.cs @@ -0,0 +1,12 @@ +using BUTR.CrashReport.ImGui.Enums; + +using System.Numerics; + +namespace BUTR.CrashReport.ImGui.Structures; + +public interface IImGuiStyle : IDisposable + where TColorsRangeAccessor : IRangeAccessor +{ + ref float Alpha { get; } + void GetColors(out TColorsRangeAccessor colors); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/IImGuiViewport.cs b/src/BUTR.CrashReport.ImGui/Structures/IImGuiViewport.cs new file mode 100644 index 0000000..ee4fd5d --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/IImGuiViewport.cs @@ -0,0 +1,10 @@ +using System.Numerics; + +namespace BUTR.CrashReport.ImGui.Structures; + +public interface IImGuiViewport +{ + ref uint ID { get; } + ref Vector2 WorkPos { get; } + ref Vector2 WorkSize { get; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/IRangeAccessor.cs b/src/BUTR.CrashReport.ImGui/Structures/IRangeAccessor.cs new file mode 100644 index 0000000..94c32a0 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/IRangeAccessor.cs @@ -0,0 +1,11 @@ +namespace BUTR.CrashReport.ImGui.Structures; + +public interface IRangeAccessor where T : struct +{ + ref T this[int index] { get; } +} + +public interface IRangeAccessor where T : struct where TEnum : Enum +{ + ref T this[TEnum index] { get; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/ImGuiInputTextCallback.cs b/src/BUTR.CrashReport.ImGui/Structures/ImGuiInputTextCallback.cs new file mode 100644 index 0000000..f6013ca --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/ImGuiInputTextCallback.cs @@ -0,0 +1,5 @@ +namespace BUTR.CrashReport.ImGui.Structures; + +public delegate int ImGuiInputTextCallback(ImGuiInputTextCallbackData data) where TData : struct; + +public delegate int ImGuiInputTextInt64Callback(ImGuiInputTextCallbackInt64Data data); \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Structures/ImGuiInputTextCallbackData.cs b/src/BUTR.CrashReport.ImGui/Structures/ImGuiInputTextCallbackData.cs new file mode 100644 index 0000000..ded18eb --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Structures/ImGuiInputTextCallbackData.cs @@ -0,0 +1,28 @@ +using BUTR.CrashReport.ImGui.Enums; + +namespace BUTR.CrashReport.ImGui.Structures; + +public ref struct ImGuiInputTextCallbackData where TData : struct +{ + public ImGuiInputTextFlags EventFlag; + public ImGuiInputTextFlags Flags; + public ushort EventChar; + public ImGuiKey EventKey; + public int CursorPos; + public int SelectionStart; + public int SelectionEnd; + + public TData Data; +} +public ref struct ImGuiInputTextCallbackInt64Data +{ + public ImGuiInputTextFlags EventFlag; + public ImGuiInputTextFlags Flags; + public ushort EventChar; + public ImGuiKey EventKey; + public int CursorPos; + public int SelectionStart; + public int SelectionEnd; + + public Int64 Data; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Utils/CollectionsMarshal.cs b/src/BUTR.CrashReport.ImGui/Utils/CollectionsMarshal.cs new file mode 100644 index 0000000..bee1c2e --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Utils/CollectionsMarshal.cs @@ -0,0 +1,42 @@ +using System.Runtime.CompilerServices; + +namespace BUTR.CrashReport.ImGui.Utils; + +public static class CollectionsMarshal +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(this List? list) => CollectionsMarshal.AsSpan(list); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(this IList? list) => CollectionsMarshal.AsSpan(list); +} + +public static class CollectionsMarshal +{ + private static readonly Func, T[]> GetItems; + private static readonly Func, int> GetSize; + + static CollectionsMarshal() + { + GetItems = FieldAccessor.CompileFieldGetter, T[]>("_items"); + GetSize = FieldAccessor.CompileFieldGetter, int>("_size"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(List? list) => list is null ? default : new Span(GetItems(list), 0, GetSize(list)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(IList? list) + { + if (list is null) + return default; + + if (list is List listT) + return AsSpan(listT); + + if (list is T[] array) + return new Span(array); + + throw new NotSupportedException("Only List and T[] are supported."); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Utils/FieldAccessor.cs b/src/BUTR.CrashReport.ImGui/Utils/FieldAccessor.cs new file mode 100644 index 0000000..bfcc666 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Utils/FieldAccessor.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Reflection; + +namespace BUTR.CrashReport.ImGui.Utils; + +internal static class FieldAccessor +{ + public static Func CompileFieldGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicFields)] T, TField>(string fieldName) + { + var fieldInfo = typeof(T).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + if (fieldInfo == null) + throw new ArgumentException($"Field '{fieldName}' does not exist on type '{typeof(T)}'."); + + var parameter = Expression.Parameter(typeof(T), "instance"); + var fieldAccess = Expression.Field(parameter, fieldInfo); + var lambda = Expression.Lambda>(fieldAccess, parameter); + return lambda.Compile(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.ImGui/Utils/Utf8Utils2.cs b/src/BUTR.CrashReport.ImGui/Utils/Utf8Utils2.cs new file mode 100644 index 0000000..ba500b7 --- /dev/null +++ b/src/BUTR.CrashReport.ImGui/Utils/Utf8Utils2.cs @@ -0,0 +1,16 @@ +using Cysharp.Text; + +public static class Utf8Utils2 +{ + public static byte[] Join(ReadOnlySpan separator, IList nativeInstructionsInstructions) + { + using var sb = new Utf8ValueStringBuilder(false); + for (var i = 0; i < nativeInstructionsInstructions.Count; i++) + { + if (i > 0) + sb.AppendLiteral(separator); + sb.Append(nativeInstructionsInstructions[i]); + } + return sb.AsSpan().ToArray(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Memory/BUTR.CrashReport.Memory.csproj b/src/BUTR.CrashReport.Memory/BUTR.CrashReport.Memory.csproj new file mode 100644 index 0000000..36f6df4 --- /dev/null +++ b/src/BUTR.CrashReport.Memory/BUTR.CrashReport.Memory.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + enable + enable + latest + true + + + + + + + diff --git a/src/BUTR.CrashReport.Memory/LiteralSpan.cs b/src/BUTR.CrashReport.Memory/LiteralSpan.cs new file mode 100644 index 0000000..5fa9fab --- /dev/null +++ b/src/BUTR.CrashReport.Memory/LiteralSpan.cs @@ -0,0 +1,84 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Memory; + +public readonly unsafe struct LiteralSpan : IEquatable> where T : unmanaged +{ + public static LiteralSpan Empty => default; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator LiteralSpan(ReadOnlySpan span) => new(span); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(LiteralSpan span) => new(span.Ptr, span.Length); + + public readonly T* Ptr; + public readonly int Length; + + public LiteralSpan(ReadOnlySpan span) + { + Ptr = (T*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); + Length = span.Length; + } + + public LiteralSpan(T* ptr, int length) + { + Ptr = ptr; + Length = length; + } + + public ref readonly T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.AsRef(Unsafe.Add(Ptr, index)); + } + + public Enumerator GetEnumerator() => new(this); + + public bool Equals(LiteralSpan other) => Ptr == other.Ptr && Length == other.Length; + + public override bool Equals(object? obj) => obj is LiteralSpan other && Equals(other); + + public override int GetHashCode() + { + unchecked + { + return (unchecked((int) (long) Ptr) * 397) ^ Length; + } + } + + public ref struct Enumerator + { + private readonly LiteralSpan _literalSpan; + private int _index; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(LiteralSpan literalSpan) + { + _literalSpan = literalSpan; + _index = -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + var index = _index + 1; + if (index >= _literalSpan.Length) return false; + + _index = index; + return true; + + } + + public ref readonly T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _literalSpan[_index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() => _index = -1; + + public void Dispose() { } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Memory/Utils/Utf8Utils.cs b/src/BUTR.CrashReport.Memory/Utils/Utf8Utils.cs new file mode 100644 index 0000000..01729bc --- /dev/null +++ b/src/BUTR.CrashReport.Memory/Utils/Utf8Utils.cs @@ -0,0 +1,116 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace BUTR.CrashReport.Memory.Utils; + +public static unsafe class Utf8Utils +{ + private static readonly byte[] EmptyUtf8 = "\0"u8.ToArray(); + private static readonly LiteralSpan EmptyUtf8Span = "\0"u8; + + public static string ToString(ReadOnlySpan utf8) + { + if (utf8.IsEmpty) + return string.Empty; + +#if NET6_0_OR_GREATER + return Encoding.UTF8.GetString(utf8); +#else + return Encoding.UTF8.GetString((byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8)), utf8.Length); +#endif + } + + public static int Utf16ToUtf8(string utf16, Span utf8) + { + if (string.IsNullOrEmpty(utf16) || utf8.IsEmpty) + return 1; + +#if NET6_0_OR_GREATER + return Encoding.UTF8.GetBytes(utf16, utf8); +#else + fixed (char* utf16Ptr = utf16) + { + return Encoding.UTF8.GetBytes( + utf16Ptr, + utf16.Length, + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8)), + utf8.Length); + } +#endif + } + + public static int Utf16ToUtf8(ReadOnlySpan utf16, Span utf8) + { + if (utf16.IsEmpty || utf8.IsEmpty) + return 1; + +#if NET6_0_OR_GREATER + return Encoding.UTF8.GetBytes(utf16, utf8); +#else + fixed (char* utf16Ptr = utf16) + { + return Encoding.UTF8.GetBytes( + utf16Ptr, + utf16.Length, + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8)), + utf8.Length); + } +#endif + } + + public static byte[] ToUtf8Array(string value) + { + if (string.IsNullOrEmpty(value)) + return EmptyUtf8; + + var length = Encoding.UTF8.GetByteCount(value) + 1; +#if NET6_0_OR_GREATER + var array = GC.AllocateUninitializedArray(length); +#else + var array = new byte[length]; +#endif + + var charSpan = value.AsSpan(); + var arraySpan = array.AsSpan(); + + var lengthWritten = Utf16ToUtf8(charSpan, arraySpan); + array[lengthWritten] = 0; + return array; + } + + public static byte[] ToUtf8Array(ReadOnlySpan value) + { + if (value.IsEmpty) + return EmptyUtf8; + + var length = Encoding.UTF8.GetMaxByteCount(value.Length) + 1; +#if NET6_0_OR_GREATER + var array = GC.AllocateUninitializedArray(length); +#else + var array = new byte[length]; +#endif + + var charSpan = value; + var arraySpan = array.AsSpan(); + + var lengthWritten = Utf16ToUtf8(charSpan, arraySpan); + array[lengthWritten] = 0; + return array; + } + + public static string Utf8ToUtf16(Span utf8) + { + if (utf8.IsEmpty) + return string.Empty; + +#if NET6_0_OR_GREATER + return Encoding.UTF8.GetString(utf8); +#else + fixed (byte* utf8Ptr = utf8) + { + return Encoding.UTF8.GetString(utf8Ptr, utf8.Length); + } +#endif + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/AssemblyArchitectureType.cs b/src/BUTR.CrashReport.Models/AssemblyArchitectureType.cs index 577ec8f..8ca26b4 100644 --- a/src/BUTR.CrashReport.Models/AssemblyArchitectureType.cs +++ b/src/BUTR.CrashReport.Models/AssemblyArchitectureType.cs @@ -1,4 +1,4 @@ -namespace BUTR.CrashReport.Models; +namespace BUTR.CrashReport.Models; /// /// Represents the architecture of a native assembly. diff --git a/src/BUTR.CrashReport.Models/AssemblyImportedReferenceModel.cs b/src/BUTR.CrashReport.Models/AssemblyImportedReferenceModel.cs deleted file mode 100644 index c943931..0000000 --- a/src/BUTR.CrashReport.Models/AssemblyImportedReferenceModel.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace BUTR.CrashReport.Models; - -/// -/// Represents an imported assembly reference. -/// -public sealed record AssemblyImportedReferenceModel -{ - /// - /// - /// - /// - public required string Name { get; set; } - - /// - /// - /// - /// A string that represents the major, minor, build, and revision numbers of the assembly. - public required string Version { get; set; } - - /// - /// - /// - /// - public required string? Culture { get; set; } - - /// - /// - /// - /// A hex string that contains the public key token. - public required string? PublicKeyToken { get; set; } - - /// - /// The empty default constructor. - /// - public AssemblyImportedReferenceModel() { } - - /// - public bool Equals(AssemblyImportedReferenceModel? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Name == other.Name && - Version == other.Version && - Culture == other.Culture && - PublicKeyToken == other.PublicKeyToken; - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = Name.GetHashCode(); - hashCode = (hashCode * 397) ^ Version.GetHashCode(); - hashCode = (hashCode * 397) ^ (Culture != null ? Culture.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (PublicKeyToken != null ? PublicKeyToken.GetHashCode() : 0); - return hashCode; - } - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/AssemblyImportedTypeReferenceModel.cs b/src/BUTR.CrashReport.Models/AssemblyImportedTypeReferenceModel.cs deleted file mode 100644 index cbaed12..0000000 --- a/src/BUTR.CrashReport.Models/AssemblyImportedTypeReferenceModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace BUTR.CrashReport.Models; - -/// -/// Represents an imported type reference. -/// -public sealed record AssemblyImportedTypeReferenceModel -{ - /// - /// - /// - /// - public required string Name { get; set; } - - /// - /// - /// - /// - public required string Namespace { get; set; } - - /// - /// - /// - /// - public required string FullName { get; set; } - - /// - public bool Equals(AssemblyImportedTypeReferenceModel? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Name == other.Name && - Namespace == other.Namespace && - FullName == other.FullName; - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = Name.GetHashCode(); - hashCode = (hashCode * 397) ^ Namespace.GetHashCode(); - hashCode = (hashCode * 397) ^ FullName.GetHashCode(); - return hashCode; - } - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/AssemblyModel.cs b/src/BUTR.CrashReport.Models/AssemblyModel.cs index b776243..f5c150f 100644 --- a/src/BUTR.CrashReport.Models/AssemblyModel.cs +++ b/src/BUTR.CrashReport.Models/AssemblyModel.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using BUTR.CrashReport.Models.Utils; + +using System.Collections.Generic; using System.Linq; namespace BUTR.CrashReport.Models; @@ -52,16 +54,6 @@ public sealed record AssemblyModel /// public required AssemblyType Type { get; set; } - /// - /// The list of imported type references from the assembly. - /// - public required IList ImportedTypeReferences { get; set; } = new List(); - - /// - /// The list of imported assembly references from the assembly. - /// - public required IList ImportedAssemblyReferences { get; set; } = new List(); - /// /// /// @@ -81,8 +73,6 @@ public bool Equals(AssemblyModel? other) Hash == other.Hash && AnonymizedPath == other.AnonymizedPath && Type == other.Type && - ImportedTypeReferences.SequenceEqual(other.ImportedTypeReferences) && - ImportedAssemblyReferences.SequenceEqual(other.ImportedAssemblyReferences) && AdditionalMetadata.SequenceEqual(other.AdditionalMetadata); } @@ -99,10 +89,14 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ Hash.GetHashCode(); hashCode = (hashCode * 397) ^ AnonymizedPath.GetHashCode(); hashCode = (hashCode * 397) ^ (int) Type; - hashCode = (hashCode * 397) ^ ImportedTypeReferences.GetHashCode(); - hashCode = (hashCode * 397) ^ ImportedAssemblyReferences.GetHashCode(); hashCode = (hashCode * 397) ^ AdditionalMetadata.GetHashCode(); return hashCode; } } + + /// + /// + /// + /// + public string GetFullName() => AssemblyNameFormatter.ComputeDisplayName(Id.Name, Id.Version, CultureName, Id.PublicKeyToken).ToString(); } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/AssemblyType.cs b/src/BUTR.CrashReport.Models/AssemblyType.cs index 7e8244f..c05e111 100644 --- a/src/BUTR.CrashReport.Models/AssemblyType.cs +++ b/src/BUTR.CrashReport.Models/AssemblyType.cs @@ -57,4 +57,9 @@ public enum AssemblyType /// Assembly is protected from disassembly /// ProtectedFromDisassembly = 256, + + /// + /// Assembly is allowed to be disassembled + /// + AllowedDisassembly = 512, } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/BUTR.CrashReport.Models.csproj b/src/BUTR.CrashReport.Models/BUTR.CrashReport.Models.csproj index 40bfb95..ec95f2d 100644 --- a/src/BUTR.CrashReport.Models/BUTR.CrashReport.Models.csproj +++ b/src/BUTR.CrashReport.Models/BUTR.CrashReport.Models.csproj @@ -2,8 +2,10 @@ net35;net45;netstandard2.0 - preview + latest enable + true + true true @@ -14,23 +16,11 @@ Contains the models for creating the crash report MIT https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png - butr crash report bannerlord + butr crash report - - - - - - - - - - - - - + diff --git a/src/BUTR.CrashReport.Models/CrashReportModel.cs b/src/BUTR.CrashReport.Models/CrashReportModel.cs index cb643de..4550089 100644 --- a/src/BUTR.CrashReport.Models/CrashReportModel.cs +++ b/src/BUTR.CrashReport.Models/CrashReportModel.cs @@ -53,24 +53,12 @@ public sealed record CrashReportModel /// The list of native modules that are present. /// public required IList NativeModules { get; set; } = new List(); - + /// /// The list of runtime patches that are present. /// public required IList RuntimePatches { get; set; } = new List(); - /* - /// - /// The list of MonoMod patches that are present. - /// - public required IList MonoModPatches { get; set; } = new List(); - - /// - /// The list of Harmony patches that are present. - /// - public required IList HarmonyPatches { get; set; } = new List(); - */ - /// /// The list of loader plugins that are present. /// @@ -101,8 +89,6 @@ public bool Equals(CrashReportModel? other) EnhancedStacktrace.SequenceEqual(other.EnhancedStacktrace) && Assemblies.SequenceEqual(other.Assemblies) && RuntimePatches.SequenceEqual(other.RuntimePatches) && - //HarmonyPatches.SequenceEqual(other.HarmonyPatches) && - //HarmonyPatches.SequenceEqual(other.HarmonyPatches) && LoaderPlugins.SequenceEqual(other.LoaderPlugins) && InvolvedLoaderPlugins.SequenceEqual(other.InvolvedLoaderPlugins) && AdditionalMetadata.SequenceEqual(other.AdditionalMetadata); @@ -122,8 +108,6 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ EnhancedStacktrace.GetHashCode(); hashCode = (hashCode * 397) ^ Assemblies.GetHashCode(); hashCode = (hashCode * 397) ^ RuntimePatches.GetHashCode(); - //hashCode = (hashCode * 397) ^ HarmonyPatches.GetHashCode(); - //hashCode = (hashCode * 397) ^ HarmonyPatches.GetHashCode(); hashCode = (hashCode * 397) ^ LoaderPlugins.GetHashCode(); hashCode = (hashCode * 397) ^ InvolvedLoaderPlugins.GetHashCode(); hashCode = (hashCode * 397) ^ AdditionalMetadata.GetHashCode(); diff --git a/src/BUTR.CrashReport.Models/EnhancedStacktraceFrameModel.cs b/src/BUTR.CrashReport.Models/EnhancedStacktraceFrameModel.cs index 6381498..be5be7f 100644 --- a/src/BUTR.CrashReport.Models/EnhancedStacktraceFrameModel.cs +++ b/src/BUTR.CrashReport.Models/EnhancedStacktraceFrameModel.cs @@ -34,12 +34,12 @@ public sealed record EnhancedStacktraceFrameModel /// /// The original method that is being patched. Is null when no patches exists. Use instead. /// - public required MethodSimpleModel? OriginalMethod { get; set; } + public required MethodModel? OriginalMethod { get; set; } /// /// The list of patch methods that are applied to the method. /// - public required IList PatchMethods { get; set; } = new List(); + public required IList PatchMethods { get; set; } = new List(); /// /// diff --git a/src/BUTR.CrashReport.Models/HarmonyPatchModel.cs b/src/BUTR.CrashReport.Models/HarmonyPatchModel.cs deleted file mode 100644 index dfc573b..0000000 --- a/src/BUTR.CrashReport.Models/HarmonyPatchModel.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* -using System.Collections.Generic; -using System.Linq; - -namespace BUTR.CrashReport.Models; - -/// -/// Represents a Harmony patch. -/// -public sealed record HarmonyPatchModel -{ - /// - /// The type of the patch. - /// - public required HarmonyPatchType Type { get; set; } - - /// - /// Is null if not from a module. - /// - /// - public required string? ModuleId { get; set; } - - /// - /// Is null if not from a module. - /// - /// - public required string? LoaderPluginId { get; set; } - - /// - /// The of the assembly that contains the patch. - /// - public required AssemblyIdModel? AssemblyId { get; set; } - - /// - /// - /// - /// - public required string Owner { get; set; } - - public required string Namespace { get; set; } - - /// - /// - /// - /// - public required int Index { get; set; } - - /// - /// - /// - /// - public required int Priority { get; set; } - - /// - /// - /// - /// - public required IList Before { get; set; } = new List(); - - /// - /// - /// - /// - public required IList After { get; set; } = new List(); - - /// - /// - /// - /// - public required IList AdditionalMetadata { get; set; } = new List(); - - /// - public bool Equals(HarmonyPatchModel? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Type == other.Type && - ModuleId == other.ModuleId && - LoaderPluginId == other.LoaderPluginId && - Equals(AssemblyId, other.AssemblyId) && - Owner == other.Owner && - Namespace == other.Namespace && - Index == other.Index && - Priority == other.Priority && - Before.SequenceEqual(other.Before) && - After.SequenceEqual(other.After) && - AdditionalMetadata.SequenceEqual(other.AdditionalMetadata); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = (int) Type; - hashCode = (hashCode * 397) ^ (ModuleId != null ? ModuleId.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (LoaderPluginId != null ? LoaderPluginId.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (AssemblyId != null ? AssemblyId.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ Owner.GetHashCode(); - hashCode = (hashCode * 397) ^ Namespace.GetHashCode(); - hashCode = (hashCode * 397) ^ Index; - hashCode = (hashCode * 397) ^ Priority; - hashCode = (hashCode * 397) ^ Before.GetHashCode(); - hashCode = (hashCode * 397) ^ After.GetHashCode(); - hashCode = (hashCode * 397) ^ AdditionalMetadata.GetHashCode(); - return hashCode; - } - } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/HarmonyPatchType.cs b/src/BUTR.CrashReport.Models/HarmonyPatchType.cs deleted file mode 100644 index 8f58eb2..0000000 --- a/src/BUTR.CrashReport.Models/HarmonyPatchType.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* -namespace BUTR.CrashReport.Models; - -/// -/// Represents the type of a Harmony patch. -/// -public enum HarmonyPatchType -{ - /// - /// - /// - Prefix = 1, - - /// - /// - /// - Postfix = 2, - - /// - /// - /// - Finalizer = 3, - - /// - /// - /// - Transpiler = 4, -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/HarmonyPatchesModel.cs b/src/BUTR.CrashReport.Models/HarmonyPatchesModel.cs deleted file mode 100644 index 160dd22..0000000 --- a/src/BUTR.CrashReport.Models/HarmonyPatchesModel.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* -using System.Collections.Generic; -using System.Linq; - -namespace BUTR.CrashReport.Models; - -/// -/// Represents the list of Harmony patches for a given original method. -/// -public sealed record HarmonyPatchesModel -{ - /// - /// The original method type name. - /// - public required string? OriginalMethodDeclaredTypeName { get; set; } - - /// - /// The original method name. - /// - public required string? OriginalMethodName { get; set; } - - /// - /// The list of Harmony patches. - /// - public required IList Patches { get; set; } = new List(); - - /// - /// - /// - /// - public required IList AdditionalMetadata { get; set; } = new List(); - - /// - public bool Equals(HarmonyPatchesModel? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return OriginalMethodDeclaredTypeName == other.OriginalMethodDeclaredTypeName && - OriginalMethodName == other.OriginalMethodName && - Patches.SequenceEqual(other.Patches) && - AdditionalMetadata.SequenceEqual(other.AdditionalMetadata); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = (OriginalMethodDeclaredTypeName != null ? OriginalMethodDeclaredTypeName.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (OriginalMethodName != null ? OriginalMethodName.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ Patches.GetHashCode(); - hashCode = (hashCode * 397) ^ AdditionalMetadata.GetHashCode(); - return hashCode; - } - } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/InstructionsHighlightModel.cs b/src/BUTR.CrashReport.Models/InstructionsHighlightModel.cs new file mode 100644 index 0000000..f67d6b3 --- /dev/null +++ b/src/BUTR.CrashReport.Models/InstructionsHighlightModel.cs @@ -0,0 +1,51 @@ +namespace BUTR.CrashReport.Models; + +/// +/// Model for instructions highlighting. +/// +public sealed record InstructionsHighlightModel +{ + /// + /// Start line of the instruction. + /// + public int StartLine { get; set; } + + /// + /// Start column of the instruction. + /// + public int StartColumn { get; set; } + + /// + /// End line of the instruction. + /// + public int EndLine { get; set; } + + /// + /// End column of the instruction. + /// + public int EndColumn { get; set; } + + /// + public bool Equals(InstructionsHighlightModel? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return StartLine == other.StartLine && + StartColumn == other.StartColumn && + EndLine == other.EndLine && + EndColumn == other.EndColumn; + } + + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = StartLine.GetHashCode(); + hashCode = (hashCode * 397) ^ StartColumn.GetHashCode(); + hashCode = (hashCode * 397) ^ EndLine.GetHashCode(); + hashCode = (hashCode * 397) ^ EndColumn.GetHashCode(); + return hashCode; + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/InstructionsModel.cs b/src/BUTR.CrashReport.Models/InstructionsModel.cs new file mode 100644 index 0000000..a9fd422 --- /dev/null +++ b/src/BUTR.CrashReport.Models/InstructionsModel.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; + +namespace BUTR.CrashReport.Models; + +/// +/// Instructions to be displayed. +/// +public sealed record InstructionsModel +{ + /// + /// Instructions to be displayed. + /// + public IList Instructions { get; set; } = new List(); + + /// + /// Highlighted instructions. + /// + public InstructionsHighlightModel? Highlight { get; set; } + + /// + public bool Equals(InstructionsModel? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Instructions.SequenceEqual(other.Instructions) && + Highlight == other.Highlight; + } + + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = Instructions.GetHashCode(); + hashCode = (hashCode * 397) ^ Highlight?.GetHashCode() ?? 0; + return hashCode; + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginModel.cs b/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginModel.cs index 7189a5b..aeba850 100644 --- a/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginModel.cs +++ b/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginModel.cs @@ -20,6 +20,11 @@ public sealed record InvolvedModuleOrPluginModel /// public required string EnhancedStacktraceFrameName { get; set; } + /// + /// + /// + public required InvolvedModuleOrPluginType Type { get; set; } + /// /// /// @@ -33,6 +38,7 @@ public bool Equals(InvolvedModuleOrPluginModel? other) if (ReferenceEquals(this, other)) return true; return ModuleOrLoaderPluginId == other.ModuleOrLoaderPluginId && EnhancedStacktraceFrameName == other.EnhancedStacktraceFrameName && + Type == other.Type && AdditionalMetadata.SequenceEqual(other.AdditionalMetadata); } @@ -43,6 +49,7 @@ public override int GetHashCode() { var hashCode = ModuleOrLoaderPluginId.GetHashCode(); hashCode = (hashCode * 397) ^ EnhancedStacktraceFrameName.GetHashCode(); + hashCode = (hashCode * 397) ^ Type.GetHashCode(); hashCode = (hashCode * 397) ^ AdditionalMetadata.GetHashCode(); return hashCode; } diff --git a/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginType.cs b/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginType.cs new file mode 100644 index 0000000..b9b83f9 --- /dev/null +++ b/src/BUTR.CrashReport.Models/InvolvedModuleOrPluginType.cs @@ -0,0 +1,17 @@ +namespace BUTR.CrashReport.Models; + +/// +/// The type of involvement of a module or plugin in a crash. +/// +public enum InvolvedModuleOrPluginType +{ + /// + /// The module or plugin is a directly involved in the crash. + /// + Direct, + + /// + /// The module or plugin is a patch for a method that is involved in the crash. + /// + Patch, +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/LogEntryModel.cs b/src/BUTR.CrashReport.Models/LogEntryModel.cs index a50f18c..6611171 100644 --- a/src/BUTR.CrashReport.Models/LogEntryModel.cs +++ b/src/BUTR.CrashReport.Models/LogEntryModel.cs @@ -12,6 +12,11 @@ public sealed record LogEntryModel /// public required DateTimeOffset Date { get; set; } + /// + /// The application that created this log entry. + /// + public required string Application { get; set; } + /// /// The full name of the type of the log entry. /// @@ -33,6 +38,7 @@ public bool Equals(LogEntryModel? other) if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Date.Equals(other.Date) && + Application == other.Application && Type == other.Type && Level == other.Level && Message == other.Message; @@ -44,6 +50,7 @@ public override int GetHashCode() unchecked { var hashCode = Date.GetHashCode(); + hashCode = (hashCode * 397) ^ Application.GetHashCode(); hashCode = (hashCode * 397) ^ Type.GetHashCode(); hashCode = (hashCode * 397) ^ (int) Level; hashCode = (hashCode * 397) ^ Message.GetHashCode(); diff --git a/src/BUTR.CrashReport.Models/MetadataModel.cs b/src/BUTR.CrashReport.Models/MetadataModel.cs index 5b2cef6..06efd01 100644 --- a/src/BUTR.CrashReport.Models/MetadataModel.cs +++ b/src/BUTR.CrashReport.Models/MetadataModel.cs @@ -17,15 +17,21 @@ public sealed record MetadataModel /// public required string Value { get; set; } + /// + /// Initializes a new instance of the class. + /// public MetadataModel() { } - + + /// + /// Initializes a new instance of the class. + /// [SetsRequiredMembers] public MetadataModel(string key, string value) { Key = key; Value = value; } - + /// public bool Equals(MetadataModel? other) { diff --git a/src/BUTR.CrashReport.Models/MethodExecutingModel.cs b/src/BUTR.CrashReport.Models/MethodExecutingModel.cs index 84c4da8..a13de37 100644 --- a/src/BUTR.CrashReport.Models/MethodExecutingModel.cs +++ b/src/BUTR.CrashReport.Models/MethodExecutingModel.cs @@ -1,17 +1,14 @@ -using System.Collections.Generic; -using System.Linq; - -namespace BUTR.CrashReport.Models; +namespace BUTR.CrashReport.Models; /// /// Represents the actual executing method of a stack trace frame. Can the the original method or a patched method. /// -public sealed record MethodExecutingModel : MethodSimpleModel +public sealed record MethodExecutingModel : MethodModel { /// /// The native code of the method that was compiled by the JIT. /// - public required IList NativeInstructions { get; set; } = new List(); + public required InstructionsModel NativeInstructions { get; set; } /// public bool Equals(MethodExecutingModel? other) @@ -19,7 +16,7 @@ public bool Equals(MethodExecutingModel? other) if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return base.Equals(other) && - NativeInstructions.SequenceEqual(other.NativeInstructions); + NativeInstructions.Equals(other.NativeInstructions); } /// diff --git a/src/BUTR.CrashReport.Models/MethodSimpleModel.cs b/src/BUTR.CrashReport.Models/MethodModel.cs similarity index 73% rename from src/BUTR.CrashReport.Models/MethodSimpleModel.cs rename to src/BUTR.CrashReport.Models/MethodModel.cs index 8cb2ae0..9efaea0 100644 --- a/src/BUTR.CrashReport.Models/MethodSimpleModel.cs +++ b/src/BUTR.CrashReport.Models/MethodModel.cs @@ -6,7 +6,7 @@ namespace BUTR.CrashReport.Models; /// /// Represents a method. /// -public record MethodSimpleModel +public record MethodModel { /// /// The assembly identity of the assembly that contains the method. @@ -51,17 +51,17 @@ public record MethodSimpleModel /// /// The Common Intermediate Language (CIL/IL) representation of the method. /// - public required IList ILInstructions { get; set; } = new List(); + public required InstructionsModel? ILInstructions { get; set; } /// - /// The C# and Common Intermediate Language (CIL/IL) representation of the method. + /// The Common Intermediate Language (CIL/IL) with C# representation of the method. /// - public required IList CSharpILMixedInstructions { get; set; } = new List(); + public required InstructionsModel? ILMixedInstructions { get; set; } /// /// The C# representation the method. /// - public required IList CSharpInstructions { get; set; } = new List(); + public required InstructionsModel? CSharpInstructions { get; set; } /// /// @@ -70,7 +70,7 @@ public record MethodSimpleModel public required IList AdditionalMetadata { get; set; } = new List(); /// - public virtual bool Equals(MethodSimpleModel? other) + public virtual bool Equals(MethodModel? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; @@ -81,9 +81,9 @@ public virtual bool Equals(MethodSimpleModel? other) MethodName == other.MethodName && MethodFullDescription == other.MethodFullDescription && MethodParameters.SequenceEqual(other.MethodParameters) && - ILInstructions.SequenceEqual(other.ILInstructions) && - CSharpILMixedInstructions.SequenceEqual(other.CSharpILMixedInstructions) && - CSharpInstructions.SequenceEqual(other.CSharpInstructions) && + ILInstructions == other.ILInstructions && + ILMixedInstructions == other.ILMixedInstructions && + CSharpInstructions == other.CSharpInstructions && AdditionalMetadata.SequenceEqual(other.AdditionalMetadata); } @@ -93,15 +93,15 @@ public override int GetHashCode() unchecked { var hashCode = (AssemblyId != null ? AssemblyId.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (ModuleId != null ? ModuleId.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (LoaderPluginId != null ? LoaderPluginId.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (MethodDeclaredTypeName != null ? MethodDeclaredTypeName.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ ModuleId?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ LoaderPluginId?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ MethodDeclaredTypeName?.GetHashCode() ?? 0; hashCode = (hashCode * 397) ^ MethodName.GetHashCode(); hashCode = (hashCode * 397) ^ MethodFullDescription.GetHashCode(); hashCode = (hashCode * 397) ^ MethodParameters.GetHashCode(); - hashCode = (hashCode * 397) ^ ILInstructions.GetHashCode(); - hashCode = (hashCode * 397) ^ CSharpILMixedInstructions.GetHashCode(); - hashCode = (hashCode * 397) ^ CSharpInstructions.GetHashCode(); + hashCode = (hashCode * 397) ^ ILInstructions?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ ILMixedInstructions?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ CSharpInstructions?.GetHashCode() ?? 0; hashCode = (hashCode * 397) ^ AdditionalMetadata.GetHashCode(); return hashCode; } diff --git a/src/BUTR.CrashReport.Models/MethodRuntimePatchModel.cs b/src/BUTR.CrashReport.Models/MethodRuntimePatchModel.cs new file mode 100644 index 0000000..e9c9889 --- /dev/null +++ b/src/BUTR.CrashReport.Models/MethodRuntimePatchModel.cs @@ -0,0 +1,39 @@ +namespace BUTR.CrashReport.Models; + +/// +/// Represents a patch method. +/// +public sealed record MethodRuntimePatchModel : MethodModel +{ + /// + /// The provider of the patch. + /// + public required string Provider { get; set; } + + /// + /// The type of the patch. + /// + public required string Type { get; set; } + + /// + public bool Equals(MethodRuntimePatchModel? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && + Provider.Equals(other.Provider) && + Type.Equals(other.Type); + } + + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ Provider.GetHashCode(); + hashCode = (hashCode * 397) ^ Type.GetHashCode(); + return hashCode; + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/MonoModPatchModel.cs b/src/BUTR.CrashReport.Models/MonoModPatchModel.cs deleted file mode 100644 index 3470caf..0000000 --- a/src/BUTR.CrashReport.Models/MonoModPatchModel.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* -using System.Collections.Generic; - -namespace BUTR.CrashReport.Models; - -/// -/// Represents a MonoMod detour. -/// -public sealed class MonoModPatchModel -{ - /// - /// The type of the detour. - /// - public required MonoModPatchModelType Type { get; set; } - - /// - /// Is null if not from a module. - /// - /// - public required string? ModuleId { get; set; } - - /// - /// Is null if not from a module. - /// - /// - public required string? LoaderPluginId { get; set; } - - /// - /// The of the assembly that contains the patch. - /// - public required AssemblyIdModel? AssemblyId { get; set; } - - public required string Namespace { get; set; } - - /// - /// - /// - /// - public required bool IsActive { get; set; } - - /// - /// - /// - /// - public required string Id { get; set; } - - public required int? Index { get; set; } - - public required int? MaxIndex { get; set; } - - public required int? GlobalIndex { get; set; } - - /// - /// - /// - /// - public required int? Priority { get; set; } - - /// - /// - /// - /// - public required int? SubPriority { get; set; } - - /// - /// - /// - /// - public required IList Before { get; set; } = new List(); - - /// - /// - /// - /// - public required IList After { get; set; } = new List(); - - /// - /// - /// - /// - public required IList AdditionalMetadata { get; set; } = new List(); -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/MonoModPatchModelType.cs b/src/BUTR.CrashReport.Models/MonoModPatchModelType.cs deleted file mode 100644 index 7d7a0b3..0000000 --- a/src/BUTR.CrashReport.Models/MonoModPatchModelType.cs +++ /dev/null @@ -1,19 +0,0 @@ -/* -namespace BUTR.CrashReport.Models; - -/// -/// Type of the MonoMod detour -/// -public enum MonoModPatchModelType -{ - /// - /// Native or Simple detour - /// - Detour, - - /// - /// IL Hook - /// - ILHook, -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/MonoModPatchesModel.cs b/src/BUTR.CrashReport.Models/MonoModPatchesModel.cs deleted file mode 100644 index 51006a7..0000000 --- a/src/BUTR.CrashReport.Models/MonoModPatchesModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* -using System.Collections.Generic; - -namespace BUTR.CrashReport.Models; - -/// -/// Represents the list of MonoMod detours for a given original method. -/// -public sealed class MonoModPatchesModel -{ - /// - /// The original method type name. - /// - public required string? OriginalMethodDeclaredTypeName { get; set; } - - /// - /// The original method name. - /// - public required string? OriginalMethodName { get; set; } - - /// - /// The list of MonoMod detours. - /// - public required IList Detours { get; set; } = new List(); - - /// - /// - /// - /// - public required IList AdditionalMetadata { get; set; } = new List(); -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/NativeAssemblyArchitectureType.cs b/src/BUTR.CrashReport.Models/NativeAssemblyArchitectureType.cs index b92d9d8..4ee04a0 100644 --- a/src/BUTR.CrashReport.Models/NativeAssemblyArchitectureType.cs +++ b/src/BUTR.CrashReport.Models/NativeAssemblyArchitectureType.cs @@ -11,17 +11,17 @@ public enum NativeAssemblyArchitectureType Unknown = 0, /// - /// + /// x86 /// x86 = 1, /// - /// + /// x86_64 /// x86_64 = 2, /// - /// + /// ARM /// Arm = 3, diff --git a/src/BUTR.CrashReport.Models/RuntimePatchModel.cs b/src/BUTR.CrashReport.Models/RuntimePatchModel.cs index 6b8bd33..f789921 100644 --- a/src/BUTR.CrashReport.Models/RuntimePatchModel.cs +++ b/src/BUTR.CrashReport.Models/RuntimePatchModel.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace BUTR.CrashReport.Models; +/// +/// Represents a runtime patch. +/// public sealed class RuntimePatchModel { /// @@ -20,13 +23,22 @@ public sealed class RuntimePatchModel /// The of the assembly that contains the patch. /// public required AssemblyIdModel? AssemblyId { get; set; } - + + /// + /// The provider of the patch. + /// public required string Provider { get; set; } - + + /// + /// The type of the patch. + /// public required string Type { get; set; } + /// + /// The full name of the method patch. + /// public required string FullName { get; set; } - + /// /// /// diff --git a/src/BUTR.CrashReport.Models/RuntimePatchesModel.cs b/src/BUTR.CrashReport.Models/RuntimePatchesModel.cs index 1553216..d9df679 100644 --- a/src/BUTR.CrashReport.Models/RuntimePatchesModel.cs +++ b/src/BUTR.CrashReport.Models/RuntimePatchesModel.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace BUTR.CrashReport.Models; +/// +/// Represents a runtime patch model. +/// public sealed class RuntimePatchesModel { /// @@ -13,7 +16,7 @@ public sealed class RuntimePatchesModel /// The original method name. /// public required string? OriginalMethodName { get; set; } - + /// /// The list of MonoMod detours. /// diff --git a/src/BUTR.CrashReport.Decompilers/Utils/AssemblyNameFormatter.cs b/src/BUTR.CrashReport.Models/Utils/AssemblyNameFormatter.cs similarity index 91% rename from src/BUTR.CrashReport.Decompilers/Utils/AssemblyNameFormatter.cs rename to src/BUTR.CrashReport.Models/Utils/AssemblyNameFormatter.cs index 000b4a0..940887d 100644 --- a/src/BUTR.CrashReport.Decompilers/Utils/AssemblyNameFormatter.cs +++ b/src/BUTR.CrashReport.Models/Utils/AssemblyNameFormatter.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; -namespace BUTR.CrashReport.Decompilers.Utils; +namespace BUTR.CrashReport.Models.Utils; -public static class AssemblyNameFormatter +internal static class AssemblyNameFormatter { - public static string ComputeDisplayName(string? name, string? version, string? cultureName, string? publicKeyToken) + public static StringBuilder ComputeDisplayName(string? name, string? version, string? cultureName, string? publicKeyToken) { if (name == string.Empty) throw new FileLoadException(); @@ -41,7 +42,7 @@ public static string ComputeDisplayName(string? name, string? version, string? c // NOTE: By design (desktop compat) AssemblyName.FullName and ToString() do not include ProcessorArchitecture. - return sb.ToString(); + return sb; } private static void AppendQuoted(this StringBuilder sb, string s) @@ -69,7 +70,7 @@ private static void AppendQuoted(this StringBuilder sb, string s) continue; if (s.Length - i < escapeReplacement.Length) continue; - if (s.AsSpan(i, escapeReplacement.Length).SequenceEqual(escapeReplacement.AsSpan())) + if (s.Substring(i, escapeReplacement.Length).SequenceEqual(escapeReplacement)) { sb.Append('\\'); sb.Append(key); @@ -129,6 +130,6 @@ private static Version CanonicalizeVersion(this Version version) new('\'', "'"), new('\"', "\""), new('n', Environment.NewLine), - new('t', "\t") + new('t', "\t"), ]; } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Models/Utils/AssemblyUtils.cs b/src/BUTR.CrashReport.Models/Utils/AssemblyUtils.cs index 2858e18..1a4d175 100644 --- a/src/BUTR.CrashReport.Models/Utils/AssemblyUtils.cs +++ b/src/BUTR.CrashReport.Models/Utils/AssemblyUtils.cs @@ -1,18 +1,23 @@ -using System; -using System.Globalization; +using System.Globalization; +using System.Text; namespace BUTR.CrashReport.Models.Utils; /// /// Provides the assembly utilities. /// -public static class AssemblyUtils +internal static class AssemblyUtils { /// /// Gets the public key token as string. /// /// The public key token. /// The public key token as string. - public static string PublicKeyAsString(byte[]? publicKeyToken) => - string.Join(string.Empty, Array.ConvertAll(publicKeyToken ?? [], x => x.ToString("x2", CultureInfo.InvariantCulture))); + public static string PublicKeyAsString(byte[]? publicKeyToken) + { + var sb = new StringBuilder(); + foreach (var b in publicKeyToken ?? []) + sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); + return sb.ToString(); + } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Native.SourceGenerator/BUTR.CrashReport.Native.SourceGenerator.csproj b/src/BUTR.CrashReport.Native.SourceGenerator/BUTR.CrashReport.Native.SourceGenerator.csproj new file mode 100644 index 0000000..30ab026 --- /dev/null +++ b/src/BUTR.CrashReport.Native.SourceGenerator/BUTR.CrashReport.Native.SourceGenerator.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + latest + enable + true + + true + + + + + + + + diff --git a/src/BUTR.CrashReport.Native.SourceGenerator/DelegateLoaderGenerator.cs b/src/BUTR.CrashReport.Native.SourceGenerator/DelegateLoaderGenerator.cs new file mode 100644 index 0000000..5396978 --- /dev/null +++ b/src/BUTR.CrashReport.Native.SourceGenerator/DelegateLoaderGenerator.cs @@ -0,0 +1,94 @@ +using Microsoft.CodeAnalysis; + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BUTR.CrashReport.Native.SourceGenerator; + +[Generator] +public class DelegateLoaderGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var assemblyAttributes = context.CompilationProvider.SelectMany(static (compilation, _) => + { + var attributeSymbol = compilation.GetTypeByMetadataName("BUTR.CrashReport.Native.DelegateLoaderAttribute"); + if (attributeSymbol == null) return Enumerable.Empty<(INamedTypeSymbol?, bool)>(); + + return compilation.Assembly.GetAttributes() + .Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeSymbol)) + .Select(attr => + { + var typeToWrap = attr.ConstructorArguments[0].Value as INamedTypeSymbol; + var useDelegateTypeName = (bool) (attr.ConstructorArguments[1].Value ?? false); + + return (typeToWrap, useDelegateTypeName); + }) + .Where(tuple => tuple.typeToWrap != null); + }); + + context.RegisterSourceOutput(assemblyAttributes, (spc, attr) => + { + var (typeToWrap, useDelegateTypeName) = attr; + + if (typeToWrap == null) return; + + var className = $"{typeToWrap.Name}DelegateLoader"; + var namespaceName = typeToWrap.ContainingNamespace.ToDisplayString(); + + var sourceBuilder = new StringBuilder(); + + sourceBuilder.AppendLine("using System;"); + sourceBuilder.AppendLine("using System.Runtime.InteropServices;"); + + sourceBuilder.AppendLine($"namespace {namespaceName}"); + sourceBuilder.AppendLine("{"); + + sourceBuilder.AppendLine($" public unsafe static class {className}"); + sourceBuilder.AppendLine(" {"); + + // Add Load method + sourceBuilder.AppendLine($" public static void LoadFrom(this {typeToWrap.Name} origial, Func loadFunctionPointer)"); + sourceBuilder.AppendLine(" {"); + + foreach (var fieldSymbol in GetFieldsFromClass(typeToWrap)) + { + var fieldName = fieldSymbol.Name; + var delegateType = fieldSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + var delegateSymbol = fieldSymbol.Type as INamedTypeSymbol; + if (delegateSymbol == null) continue; + + var entryPoint = useDelegateTypeName ? delegateSymbol.Name : fieldName; + + sourceBuilder.AppendLine($" origial.{fieldName} = Marshal.GetDelegateForFunctionPointer<{delegateType}>(loadFunctionPointer(\"{entryPoint}\"));"); + } + + sourceBuilder.AppendLine(" }"); + + sourceBuilder.AppendLine(" }"); + sourceBuilder.AppendLine("}"); + + spc.AddSource($"{className}_Generated.g.cs", sourceBuilder.ToString()); + }); + } + + private static IEnumerable GetFieldsFromClass(INamedTypeSymbol classSymbol) + { + foreach (var fieldSymbol in classSymbol.GetMembers().OfType()) + { + if (fieldSymbol.Type is not INamedTypeSymbol typeSymbol) continue; + + if (IsDelegateWithUnmanagedFunctionPointer(typeSymbol)) + yield return fieldSymbol; + } + } + + private static bool IsDelegateWithUnmanagedFunctionPointer(INamedTypeSymbol typeSymbol) + { + return typeSymbol.TypeKind == TypeKind.Delegate && + typeSymbol.DelegateInvokeMethod != null && + typeSymbol.GetAttributes().Any(attr => attr.AttributeClass?.Name == "UnmanagedFunctionPointerAttribute"); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Native.SourceGenerator/PInvokeGenerator.cs b/src/BUTR.CrashReport.Native.SourceGenerator/PInvokeGenerator.cs new file mode 100644 index 0000000..0700ebc --- /dev/null +++ b/src/BUTR.CrashReport.Native.SourceGenerator/PInvokeGenerator.cs @@ -0,0 +1,128 @@ +using Microsoft.CodeAnalysis; + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BUTR.CrashReport.Native.SourceGenerator; + +[Generator] +public class PInvokeGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var assemblyAttributes = context.CompilationProvider.SelectMany(static (compilation, _) => + { + var attributeSymbol = compilation.GetTypeByMetadataName("BUTR.CrashReport.Native.PInvokeDelegateLoaderAttribute"); + if (attributeSymbol == null) return Enumerable.Empty<(INamedTypeSymbol?, string?, bool)>(); + + return compilation.Assembly.GetAttributes() + .Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeSymbol)) + .Select(attr => + { + var typeToWrap = attr.ConstructorArguments[0].Value as INamedTypeSymbol; + var nativeLibName = attr.ConstructorArguments[1].Value as string; + var useDelegateTypeName = (bool) (attr.ConstructorArguments[2].Value ?? false); + + return (typeToWrap, nativeLibName, useDelegateTypeName); + }) + .Where(tuple => tuple.typeToWrap != null && !string.IsNullOrEmpty(tuple.nativeLibName)); + }); + + context.RegisterSourceOutput(assemblyAttributes, (spc, attr) => + { + var (typeToWrap, nativeLibName, useDelegateTypeName) = attr; + + if (typeToWrap == null || string.IsNullOrEmpty(nativeLibName)) return; + + var className = $"{typeToWrap.Name}PInvoke"; + var namespaceName = typeToWrap.ContainingNamespace.ToDisplayString(); + + var sourceBuilder = new StringBuilder(); + + sourceBuilder.AppendLine("using System;"); + sourceBuilder.AppendLine("using System.Runtime.InteropServices;"); + + sourceBuilder.AppendLine($"namespace {namespaceName}"); + sourceBuilder.AppendLine("{"); + + sourceBuilder.AppendLine($" public unsafe static class {className}"); + sourceBuilder.AppendLine(" {"); + + sourceBuilder.AppendLine($" private const string NativeLibName = \"{nativeLibName}\";"); + + foreach (var fieldSymbol in GetFieldsFromClass(typeToWrap)) + { + var fieldName = fieldSymbol.Name; + if (fieldSymbol.Type is not INamedTypeSymbol delegateSymbol) continue; + + var methodReturn = GetMethodReturn(delegateSymbol); + var methodParameters = GetMethodParameters(delegateSymbol); + + var entryPoint = useDelegateTypeName ? delegateSymbol.Name : fieldName; + + sourceBuilder.AppendLine($"#if NET7_0_OR_GREATER_"); + sourceBuilder.AppendLine($" [LibraryImport(\"{nativeLibName}\", EntryPoint = \"{entryPoint}\", StringMarshalling = StringMarshalling.Utf8)]"); + sourceBuilder.AppendLine($" private static partial {methodReturn} {fieldName}{methodParameters};"); + sourceBuilder.AppendLine($"#else"); + sourceBuilder.AppendLine($" [DllImport(\"{nativeLibName}\", EntryPoint = \"{entryPoint}\", CallingConvention = CallingConvention.Cdecl)]"); + sourceBuilder.AppendLine($" private static extern {methodReturn} {fieldName}{methodParameters};"); + sourceBuilder.AppendLine($"#endif"); + sourceBuilder.AppendLine(); + } + + sourceBuilder.AppendLine($" public static void LoadFromPInvoke(this {typeToWrap.Name} origial)"); + sourceBuilder.AppendLine($" {{"); + foreach (var fieldSymbol in GetFieldsFromClass(typeToWrap)) + { + var fieldName = fieldSymbol.Name; + + sourceBuilder.AppendLine($" origial.{fieldName} = {fieldName};"); + } + sourceBuilder.AppendLine($" }}"); + + sourceBuilder.AppendLine(" }"); + sourceBuilder.AppendLine("}"); + + spc.AddSource($"{className}_Generated.g.cs", sourceBuilder.ToString()); + }); + } + + private static IEnumerable GetFieldsFromClass(INamedTypeSymbol classSymbol) + { + foreach (var fieldSymbol in classSymbol.GetMembers().OfType()) + { + if (fieldSymbol.Type is not INamedTypeSymbol typeSymbol) continue; + + if (IsDelegateWithUnmanagedFunctionPointer(typeSymbol)) + yield return fieldSymbol; + } + } + + private static bool IsDelegateWithUnmanagedFunctionPointer(INamedTypeSymbol typeSymbol) + { + return typeSymbol.TypeKind == TypeKind.Delegate && + typeSymbol.DelegateInvokeMethod is not null && + typeSymbol.GetAttributes().Any(attr => attr.AttributeClass?.Name == "UnmanagedFunctionPointerAttribute"); + } + + private static string GetMethodReturn(INamedTypeSymbol delegateSymbol) + { + if (delegateSymbol.DelegateInvokeMethod == null) + return "void"; + + return delegateSymbol.DelegateInvokeMethod.ReturnType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + } + + private static string GetMethodParameters(INamedTypeSymbol delegateSymbol) + { + if (delegateSymbol.DelegateInvokeMethod == null) + return "()"; + + var parameters = delegateSymbol.DelegateInvokeMethod.Parameters + .Select(p => $"{p.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {p.Name}") + .ToArray(); + + return $"({string.Join(", ", parameters)})"; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Native/Allocator.cs b/src/BUTR.CrashReport.Native/Allocator.cs new file mode 100644 index 0000000..88b5472 --- /dev/null +++ b/src/BUTR.CrashReport.Native/Allocator.cs @@ -0,0 +1,52 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Native; + +public sealed class Allocator : IDisposable +{ + private readonly List _allocated = new(); + + public unsafe void* Alloc(int size) + { +#if NETSTANDARD2_0 + var ptr = (void*) Marshal.AllocHGlobal(size); +#else + var ptr = NativeMemory.Alloc((UIntPtr) size); +#endif + _allocated.Add(new IntPtr(ptr)); + return ptr; + } + + public unsafe T* Alloc(T value) where T : unmanaged + { +#if NETSTANDARD2_0 + var ptr = (T*) Marshal.AllocHGlobal(Unsafe.SizeOf()); +#else + var ptr = (T*) NativeMemory.Alloc((UIntPtr) Unsafe.SizeOf()); +#endif + _allocated.Add(new IntPtr(ptr)); + Unsafe.Write(ptr, value); + return ptr; + } + + public unsafe T* Alloc(ReadOnlySpan value) where T : unmanaged + { +#if NETSTANDARD2_0 + var ptr = (T*) Marshal.AllocHGlobal(Unsafe.SizeOf()); +#else + var ptr = (T*) NativeMemory.Alloc((UIntPtr) Unsafe.SizeOf()); +#endif + _allocated.Add(new IntPtr(ptr)); + value.CopyTo(new Span(ptr, value.Length)); + return ptr; + } + + public void Dispose() + { + foreach (var ptr in _allocated) + { + Marshal.FreeHGlobal(ptr); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Native/BUTR.CrashReport.Native.csproj b/src/BUTR.CrashReport.Native/BUTR.CrashReport.Native.csproj new file mode 100644 index 0000000..35c69c5 --- /dev/null +++ b/src/BUTR.CrashReport.Native/BUTR.CrashReport.Native.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + enable + enable + latest + true + + + + BUTR.CrashReport.Native + BUTR.CrashReport.Native + + MIT + https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png + + + + + + + + + + + + + + + + diff --git a/src/BUTR.CrashReport.Native/DelegateLoaderAttribute.cs b/src/BUTR.CrashReport.Native/DelegateLoaderAttribute.cs new file mode 100644 index 0000000..6021224 --- /dev/null +++ b/src/BUTR.CrashReport.Native/DelegateLoaderAttribute.cs @@ -0,0 +1,14 @@ +namespace BUTR.CrashReport.Native; + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +public class DelegateLoaderAttribute : Attribute +{ + public Type TypeToWrap { get; } + public bool UseDelegateTypeName { get; } + + public DelegateLoaderAttribute(Type typeToWrap, bool useDelegateTypeName = false) + { + TypeToWrap = typeToWrap; + UseDelegateTypeName = useDelegateTypeName; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Native/PInvokeDelegateLoaderAttribute.cs b/src/BUTR.CrashReport.Native/PInvokeDelegateLoaderAttribute.cs new file mode 100644 index 0000000..97c3fd4 --- /dev/null +++ b/src/BUTR.CrashReport.Native/PInvokeDelegateLoaderAttribute.cs @@ -0,0 +1,16 @@ +namespace BUTR.CrashReport.Native; + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +public class PInvokeDelegateLoaderAttribute : Attribute +{ + public Type TypeToWrap { get; } + public string DllName { get; } + public bool UseDelegateTypeName { get; } + + public PInvokeDelegateLoaderAttribute(Type typeToWrap, string dllName, bool useDelegateTypeName = false) + { + TypeToWrap = typeToWrap; + DllName = dllName; + UseDelegateTypeName = useDelegateTypeName; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Native/Pointer.cs b/src/BUTR.CrashReport.Native/Pointer.cs new file mode 100644 index 0000000..9ad6514 --- /dev/null +++ b/src/BUTR.CrashReport.Native/Pointer.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; + +namespace BUTR.CrashReport.Native; + +public readonly unsafe record struct Pointer +{ + public static implicit operator Pointer(void* ptr) => new(ptr); + public static implicit operator void*(Pointer ptr) => (void*) ptr._value; + public static implicit operator Pointer(IntPtr ptr) => new(ptr.ToPointer()); + public static implicit operator IntPtr(Pointer ptr) => ptr._value; + + private readonly nint _value; + + private Pointer(void* ptr) => _value = (nint) ptr; +} + +public readonly unsafe record struct Pointer where T : unmanaged +{ + public static explicit operator IntPtr(Pointer intPtr) => Unsafe.As, IntPtr>(ref intPtr); + public static implicit operator Pointer(IntPtr intPtr) => Unsafe.As>(ref intPtr); + + public static implicit operator Pointer(T* ptr) => new(ptr); + public static explicit operator T*(Pointer ptr) => (T*) ptr._value; + + private readonly nint _value; + + private Pointer(T* ptr) => _value = (nint) ptr; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/BUTR.CrashReport.OpenGLES3.csproj b/src/BUTR.CrashReport.OpenGLES3/BUTR.CrashReport.OpenGLES3.csproj new file mode 100644 index 0000000..362c1e3 --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/BUTR.CrashReport.OpenGLES3.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + enable + enable + latest + true + OpenGLES3 + + + + + + + + diff --git a/src/BUTR.CrashReport.OpenGLES3/BUTR.CrashReport.OpenGLES3.csproj.DotSettings b/src/BUTR.CrashReport.OpenGLES3/BUTR.CrashReport.OpenGLES3.csproj.DotSettings new file mode 100644 index 0000000..6f443eb --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/BUTR.CrashReport.OpenGLES3.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShader.cs b/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShader.cs new file mode 100644 index 0000000..7c20f01 --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShader.cs @@ -0,0 +1,49 @@ +namespace OpenGLES3; + +using static GL; + +public sealed class GLShader : IDisposable +{ + private readonly GL _gl; + + // Specifies the OpenGL ShaderID. + public uint ShaderID { get; private set; } + + // Specifies the type of shader. + public ShaderType ShaderType { get; private set; } + + // Returns Gl.GetShaderInfoLog(ShaderID), which contains any compilation errors. + public string ShaderLog => _gl.GetShaderInfoLogUtf16(ShaderID); + + ~GLShader() => Dispose(false); + + public GLShader(GL gl, ReadOnlySpan source, ShaderType type) + { + _gl = gl; + ShaderType = type; + ShaderID = _gl.CreateShader(type); + + gl.ShaderSourceUtf8(ShaderID, source); + gl.CompileShader(ShaderID); + + if (!_gl.GetShaderCompileStatus(ShaderID)) + { + throw new Exception(ShaderLog); + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (ShaderID != 0) + { + _gl.DeleteShader(ShaderID); + ShaderID = 0; + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShaderProgram.cs b/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShaderProgram.cs new file mode 100644 index 0000000..20e77e5 --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShaderProgram.cs @@ -0,0 +1,263 @@ +using BUTR.CrashReport.Memory.Utils; + +using System.Numerics; +using System.Text; + +namespace OpenGLES3; + +using static GL; + +public sealed unsafe class GLShaderProgram : IDisposable +{ + private readonly GL _gl; + + /// + /// Specifies the OpenGL shader program ID. + /// + public readonly uint ProgramID; + + /// + /// Specifies the vertex shader used in this program. + /// + public readonly GLShader VertexShader; + + /// + /// Specifies the fragment shader used in this program. + /// + public readonly GLShader FragmentShader; + + /// + /// Specifies whether this program will dispose of the child + /// vertex/fragment programs when the IDisposable method is called. + /// + public readonly bool DisposeChildren; + + private readonly Dictionary shaderParams = new(); + + /// + /// Queries the shader parameter hashtable to find a matching attribute/uniform. + /// + /// Specifies the case-sensitive name of the shader attribute/uniform. + /// The requested attribute/uniform, or null on a failure. + public GLShaderProgramParam? this[string name] => shaderParams.TryGetValue(name, out var param) ? param : null; + + public string ProgramLog => _gl.GetProgramInfoLogUtf16(ProgramID); + + /// + /// Links a vertex and fragment shader together to create a shader program. + /// + /// Specifies the vertex shader. + /// Specifies the fragment shader. + public GLShaderProgram(GL gl, GLShader vertexShader, GLShader fragmentShader) + { + _gl = gl; + VertexShader = vertexShader; + FragmentShader = fragmentShader; + ProgramID = _gl.CreateProgram(); + DisposeChildren = false; + + _gl.AttachShader(ProgramID, vertexShader.ShaderID); + _gl.CheckError(); + _gl.AttachShader(ProgramID, fragmentShader.ShaderID); + _gl.CheckError(); + _gl.LinkProgram(ProgramID); + _gl.CheckError(); + + //Check whether the program linked successfully. + //If not then throw an error with the linking error. + if (!_gl.GetProgramLinkStatus(ProgramID)) + throw new Exception(ProgramLog); + + GetParams(); + } + + /// + /// Creates two shaders and then links them together to create a shader program. + /// + /// Specifies the source code of the vertex shader. + /// Specifies the source code of the fragment shader. + public GLShaderProgram(GL gl, ReadOnlySpan vertexShaderSource, ReadOnlySpan fragmentShaderSource) + : this(gl, new GLShader(gl, vertexShaderSource, ShaderType.VertexShader), new GLShader(gl, fragmentShaderSource, ShaderType.FragmentShader)) + { + DisposeChildren = true; + } + + /// + /// Parses all of the parameters (attributes/uniforms) from the two attached shaders + /// and then loads their location by passing this shader program into the parameter object. + /// + private void GetParams() + { + shaderParams.Clear(); + + var resources = stackalloc int[1]; + var actualLength = stackalloc uint[1]; + var arraySize = stackalloc int[1]; + var type1 = stackalloc ActiveAttribType[1]; + var type2 = stackalloc ActiveUniformType[1]; + var sb = stackalloc byte[256]; + + _gl.GetProgramiv(ProgramID, ProgramParameter.ActiveAttributes, resources); + _gl.CheckError(); + + for (uint i = 0; i < resources[0]; i++) + { + _gl.GetActiveAttrib(ProgramID, i, 256, actualLength, arraySize, type1, sb); + var nameUtf8 = new Span(sb, (int) actualLength[0]); + var name = Utf8Utils.Utf8ToUtf16(nameUtf8); + _gl.CheckError(); + + if (!shaderParams.ContainsKey(name)) + { + var param = new GLShaderProgramParam(_gl, TypeFromAttributeType(type1[0]), ParamType.Attribute, name); + shaderParams.Add(param.Name, param); + param.GetLocation(this); + } + } + + _gl.GetProgramiv(ProgramID, ProgramParameter.ActiveUniforms, resources); + _gl.CheckError(); + + for (uint i = 0; i < resources[0]; i++) + { + _gl.GetActiveUniform(ProgramID, i, 256, actualLength, arraySize, type2, sb); + var nameSpan = new Span(sb, (int) actualLength[0]); + var name = Utf8Utils.Utf8ToUtf16(nameSpan); + _gl.CheckError(); + + if (!shaderParams.ContainsKey(name)) + { + var param = new GLShaderProgramParam(_gl, TypeFromUniformType(type2[0]), ParamType.Uniform, name); + shaderParams.Add(param.Name, param); + param.GetLocation(this); + } + } + } + + Type TypeFromAttributeType(ActiveAttribType type) + { + switch (type) + { + case ActiveAttribType.Float: return typeof(float); + case ActiveAttribType.FloatMat2: return typeof(float[]); + case ActiveAttribType.FloatMat3: throw new Exception(); + case ActiveAttribType.FloatMat4: return typeof(Matrix4x4); + case ActiveAttribType.FloatVec2: return typeof(Vector2); + case ActiveAttribType.FloatVec3: return typeof(Vector3); + case ActiveAttribType.FloatVec4: return typeof(Vector4); + default: return typeof(object); + } + } + + Type TypeFromUniformType(ActiveUniformType type) + { + switch (type) + { + case ActiveUniformType.Int: return typeof(int); + case ActiveUniformType.Float: return typeof(float); + case ActiveUniformType.FloatVec2: return typeof(Vector2); + case ActiveUniformType.FloatVec3: return typeof(Vector3); + case ActiveUniformType.FloatVec4: return typeof(Vector4); + case ActiveUniformType.IntVec2: return typeof(int[]); + case ActiveUniformType.IntVec3: return typeof(int[]); + case ActiveUniformType.IntVec4: return typeof(int[]); + case ActiveUniformType.Bool: return typeof(bool); + case ActiveUniformType.BoolVec2: return typeof(bool[]); + case ActiveUniformType.BoolVec3: return typeof(bool[]); + case ActiveUniformType.BoolVec4: return typeof(bool[]); + case ActiveUniformType.FloatMat2: return typeof(float[]); + case ActiveUniformType.FloatMat3: throw new Exception(); + case ActiveUniformType.FloatMat4: return typeof(Matrix4x4); + case ActiveUniformType.Sampler1D: + case ActiveUniformType.Sampler2D: + case ActiveUniformType.Sampler3D: + case ActiveUniformType.SamplerCube: + case ActiveUniformType.Sampler1DShadow: + case ActiveUniformType.Sampler2DShadow: + case ActiveUniformType.Sampler2DRect: + case ActiveUniformType.Sampler2DRectShadow: return typeof(int); + case ActiveUniformType.FloatMat2x3: + case ActiveUniformType.FloatMat2x4: + case ActiveUniformType.FloatMat3x2: + case ActiveUniformType.FloatMat3x4: + case ActiveUniformType.FloatMat4x2: + case ActiveUniformType.FloatMat4x3: return typeof(float[]); + case ActiveUniformType.Sampler1DArray: + case ActiveUniformType.Sampler2DArray: + case ActiveUniformType.SamplerBuffer: + case ActiveUniformType.Sampler1DArrayShadow: + case ActiveUniformType.Sampler2DArrayShadow: + case ActiveUniformType.SamplerCubeShadow: return typeof(int); + case ActiveUniformType.UnsignedIntVec2: return typeof(uint[]); + case ActiveUniformType.UnsignedIntVec3: return typeof(uint[]); + case ActiveUniformType.UnsignedIntVec4: return typeof(uint[]); + case ActiveUniformType.IntSampler1D: + case ActiveUniformType.IntSampler2D: + case ActiveUniformType.IntSampler3D: + case ActiveUniformType.IntSamplerCube: + case ActiveUniformType.IntSampler2DRect: + case ActiveUniformType.IntSampler1DArray: + case ActiveUniformType.IntSampler2DArray: + case ActiveUniformType.IntSamplerBuffer: return typeof(int); + case ActiveUniformType.UnsignedIntSampler1D: + case ActiveUniformType.UnsignedIntSampler2D: + case ActiveUniformType.UnsignedIntSampler3D: + case ActiveUniformType.UnsignedIntSamplerCube: + case ActiveUniformType.UnsignedIntSampler2DRect: + case ActiveUniformType.UnsignedIntSampler1DArray: + case ActiveUniformType.UnsignedIntSampler2DArray: + case ActiveUniformType.UnsignedIntSamplerBuffer: return typeof(uint); + case ActiveUniformType.Sampler2DMultisample: return typeof(int); + case ActiveUniformType.IntSampler2DMultisample: return typeof(int); + case ActiveUniformType.UnsignedIntSampler2DMultisample: return typeof(uint); + case ActiveUniformType.Sampler2DMultisampleArray: return typeof(int); + case ActiveUniformType.IntSampler2DMultisampleArray: return typeof(int); + case ActiveUniformType.UnsignedIntSampler2DMultisampleArray: return typeof(uint); + default: return typeof(object); + } + } + + public void Use() => _gl.UseProgram(ProgramID); + + public int GetUniformLocation(string name) + { + Use(); + var nameUtf8 = Encoding.UTF8.GetBytes(name); + fixed (byte* p = nameUtf8) + return _gl.GetUniformLocation(ProgramID, p); + } + + public int GetAttributeLocation(string name) + { + Use(); + var nameUtf8 = Encoding.UTF8.GetBytes(name); + fixed (byte* p = nameUtf8) + return _gl.GetAttribLocation(ProgramID, p); + } + + ~GLShaderProgram() => Dispose(false); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (ProgramID != 0) + { + _gl.UseProgram(0); + + _gl.DetachShader(ProgramID, VertexShader.ShaderID); + _gl.DetachShader(ProgramID, FragmentShader.ShaderID); + _gl.DeleteProgram(ProgramID); + + if (DisposeChildren) + { + VertexShader.Dispose(); + FragmentShader.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShaderProgramParam.cs b/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShaderProgramParam.cs new file mode 100644 index 0000000..439321e --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/Constructs/GLShaderProgramParam.cs @@ -0,0 +1,104 @@ +using System.Numerics; + +namespace OpenGLES3; + +using static GL; + +public enum ParamType +{ + Uniform, + Attribute, +} + +public sealed class GLShaderProgramParam +{ + private readonly GL _gl; + + /// + /// Specifies the OpenGL program ID. + /// + public uint ProgramId; + + /// + /// Specifies the location of the parameter in the OpenGL program. + /// + public int Location; + + /// + /// Specifies the C# equivalent of the GLSL data type. + /// + public readonly Type Type; + + /// + /// Specifies the parameter type (either attribute or uniform). + /// + public readonly ParamType ParamType; + + /// + /// Specifies the case-sensitive name of the parameter. + /// + public readonly string Name; + + public GLShaderProgramParam(GL gl, Type type, ParamType paramType, string name) + { + _gl = gl; + Type = type; + ParamType = paramType; + Name = name; + } + + public GLShaderProgramParam(GL gl, Type type, ParamType paramType, string name, uint program, int location) : this(gl, type, paramType, name) + { + ProgramId = program; + Location = location; + } + + /// + /// Gets the location of the parameter in a compiled OpenGL program. + /// + /// Specifies the shader program that contains this parameter. + public void GetLocation(GLShaderProgram program) + { + program.Use(); + if (ProgramId == 0) + { + ProgramId = program.ProgramID; + Location = ParamType == ParamType.Uniform ? program.GetUniformLocation(Name) : program.GetAttributeLocation(Name); + } + } + + public void SetValue(bool param) + { + _gl.Uniform1I(Location, param ? 1 : 0); + } + + public void SetValue(int param) + { + _gl.Uniform1I(Location, param); + } + + public void SetValue(float param) + { + _gl.Uniform1F(Location, param); + } + + public void SetValue(ref readonly Vector2 param) + { + _gl.Uniform2F(Location, param.X, param.Y); + } + + public void SetValue(ref readonly Vector3 param) + { + _gl.Uniform3F(Location, param.X, param.Y, param.Z); + } + + public void SetValue(ref readonly Vector4 param) + { + _gl.Uniform4F(Location, param.X, param.Y, param.Z, param.W); + } + + public void SetValue(ref readonly Matrix4x4 param) + { + _gl.UniformMatrix4(Location, in param); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/GL.Enums.cs b/src/BUTR.CrashReport.OpenGLES3/GL.Enums.cs new file mode 100644 index 0000000..162d9b5 --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/GL.Enums.cs @@ -0,0 +1,660 @@ +namespace OpenGLES3; + +partial class GL +{ + public const uint GL_NO_ERROR = 0; + + public enum TextureUnit : GLenum + { + Texture0 = 0x84C0, + } + + public enum PrimitiveType : GLenum + { + Triangles = 0x0004, + } + + [Flags] + public enum ClearBufferMask : GLbitfield + { + DepthBufferBit = 0x00000100, + AccumBufferBit = 0x00000200, + StencilBufferBit = 0x00000400, + ColorBufferBit = 0x00004000, + } + + public enum EnableCap : GLenum + { + LineSmooth = 0x0B20, + PolygonSmooth = 0x0B41, + CullFace = 0x0B44, + DepthTest = 0x0B71, + StencilTest = 0x0B90, + Dither = 0x0BD0, + Blend = 0x0BE2, + IndexLogicOp = 0x0BF1, + ColorLogicOp = 0x0BF2, + ScissorTest = 0x0C11, + AutoNormal = 0x0D80, + Map1Color4 = 0x0D90, + Map1Index = 0x0D91, + Map1Normal = 0x0D92, + Map1TextureCoord1 = 0x0D93, + Map1TextureCoord2 = 0x0D94, + Map1TextureCoord3 = 0x0D95, + Map1TextureCoord4 = 0x0D96, + Map1Vertex3 = 0x0D97, + Map1Vertex4 = 0x0D98, + Map2Color4 = 0x0DB0, + Map2Index = 0x0DB1, + Map2Normal = 0x0DB2, + Map2TextureCoord1 = 0x0DB3, + Map2TextureCoord2 = 0x0DB4, + Map2TextureCoord3 = 0x0DB5, + Map2TextureCoord4 = 0x0DB6, + Map2Vertex3 = 0x0DB7, + Map2Vertex4 = 0x0DB8, + Texture1D = 0x0DE0, + Texture2D = 0x0DE1, + PolygonOffsetPoint = 0x2A01, + PolygonOffsetLine = 0x2A02, + ClipPlane0 = 0x3000, + ClipPlane1 = 0x3001, + ClipPlane2 = 0x3002, + ClipPlane3 = 0x3003, + ClipPlane4 = 0x3004, + ClipPlane5 = 0x3005, + Convolution1D = 0x8010, + Convolution1DExt = 0x8010, + Convolution2D = 0x8011, + Convolution2DExt = 0x8011, + Separable2D = 0x8012, + Separable2DExt = 0x8012, + Histogram = 0x8024, + HistogramExt = 0x8024, + MinmaxExt = 0x802E, + PolygonOffsetFill = 0x8037, + RescaleNormalExt = 0x803A, + Texture3DExt = 0x806F, + VertexArray = 0x8074, + NormalArray = 0x8075, + ColorArray = 0x8076, + IndexArray = 0x8077, + TextureCoordArray = 0x8078, + EdgeFlagArray = 0x8079, + InterlaceSgix = 0x8094, + Multisample = 0x809D, + SampleAlphaToCoverage = 0x809E, + SampleAlphaToMaskSgis = 0x809E, + SampleAlphaToOne = 0x809F, + SampleAlphaToOneSgis = 0x809F, + SampleCoverage = 0x80A0, + SampleMaskSgis = 0x80A0, + TextureColorTableSgi = 0x80BC, + ColorTable = 0x80D0, + ColorTableSgi = 0x80D0, + PostConvolutionColorTable = 0x80D1, + PostConvolutionColorTableSgi = 0x80D1, + PostColorMatrixColorTable = 0x80D2, + PostColorMatrixColorTableSgi = 0x80D2, + Texture4DSgis = 0x8134, + PixelTexGenSgix = 0x8139, + SpriteSgix = 0x8148, + ReferencePlaneSgix = 0x817D, + IrInstrument1Sgix = 0x817F, + CalligraphicFragmentSgix = 0x8183, + FramezoomSgix = 0x818B, + FogOffsetSgix = 0x8198, + SharedTexturePaletteExt = 0x81FB, + AsyncHistogramSgix = 0x832C, + PixelTextureSgis = 0x8353, + AsyncTexImageSgix = 0x835C, + AsyncDrawPixelsSgix = 0x835D, + AsyncReadPixelsSgix = 0x835E, + FragmentLightingSgix = 0x8400, + FragmentColorMaterialSgix = 0x8401, + FragmentLight0Sgix = 0x840C, + FragmentLight1Sgix = 0x840D, + FragmentLight2Sgix = 0x840E, + FragmentLight3Sgix = 0x840F, + FragmentLight4Sgix = 0x8410, + FragmentLight5Sgix = 0x8411, + FragmentLight6Sgix = 0x8412, + FragmentLight7Sgix = 0x8413, + ColorSum = 0x8458, + SecondaryColorArray = 0x845E, + TextureCubeMap = 0x8513, + ProgramPointSize = 0x8642, + VertexProgramPointSize = 0x8642, + DepthClamp = 0x864F, + TextureCubeMapSeamless = 0x884F, + PointSprite = 0x8861, + RasterizerDiscard = 0x8C89, + FramebufferSrgb = 0x8DB9, + SampleMask = 0x8E51, + PrimitiveRestart = 0x8F9D, + } + + public enum BlendEquationMode : GLenum + { + FuncAdd = 0x8006, + Min = 0x8007, + Max = 0x8008, + FuncSubtract = 0x800A, + FuncReverseSubtract = 0x800B, + } + + public enum BlendingFactorDest : GLenum + { + Zero = 0, + SrcColor = 0x0300, + OneMinusSrcColor = 0x0301, + SrcAlpha = 0x0302, + OneMinusSrcAlpha = 0x0303, + DstAlpha = 0x0304, + OneMinusDstAlpha = 0x0305, + DstColor = 0x0306, + OneMinusDstColor = 0x0307, + ConstantColor = 0x8001, + ConstantColorExt = 0x8001, + OneMinusConstantColor = 0x8002, + OneMinusConstantColorExt = 0x8002, + ConstantAlpha = 0x8003, + ConstantAlphaExt = 0x8003, + OneMinusConstantAlpha = 0x8004, + OneMinusConstantAlphaExt = 0x8004, + One = 1, + } + + public enum ShaderParameter : GLenum + { + ShaderType = 0x8B4F, + DeleteStatus = 0x8B80, + CompileStatus = 0x8B81, + InfoLogLength = 0x8B84, + ShaderSourceLength = 0x8B88, + } + + public enum ShaderType : GLenum + { + FragmentShader = 0x8B30, + VertexShader = 0x8B31, + GeometryShader = 0x8DD9, + TessControlShader = 0x8E88, + TessEvaluationShader = 0x8E87, + ComputeShader = 0x91B9 + } + + public enum ProgramParameter : GLenum + { + ActiveUniformBlockMaxNameLength = 0x8A35, + ActiveUniformBlocks = 0x8A36, + DeleteStatus = 0x8B80, + LinkStatus = 0x8B82, + ValidateStatus = 0x8B83, + InfoLogLength = 0x8B84, + AttachedShaders = 0x8B85, + ActiveUniforms = 0x8B86, + ActiveUniformMaxLength = 0x8B87, + ActiveAttributes = 0x8B89, + ActiveAttributeMaxLength = 0x8B8A, + TransformFeedbackVaryingMaxLength = 0x8C76, + TransformFeedbackBufferMode = 0x8C7F, + TransformFeedbackVaryings = 0x8C83, + GeometryVerticesOut = 0x8DDA, + GeometryInputType = 0x8DDB, + GeometryOutputType = 0x8DDC, + } + + public enum ActiveAttribType : GLenum + { + Float = 0x1406, + FloatVec2 = 0x8B50, + FloatVec3 = 0x8B51, + FloatVec4 = 0x8B52, + FloatMat2 = 0x8B5A, + FloatMat3 = 0x8B5B, + FloatMat4 = 0x8B5C, + } + + public enum ActiveUniformType : GLenum + { + Int = 0x1404, + Float = 0x1406, + FloatVec2 = 0x8B50, + FloatVec3 = 0x8B51, + FloatVec4 = 0x8B52, + IntVec2 = 0x8B53, + IntVec3 = 0x8B54, + IntVec4 = 0x8B55, + Bool = 0x8B56, + BoolVec2 = 0x8B57, + BoolVec3 = 0x8B58, + BoolVec4 = 0x8B59, + FloatMat2 = 0x8B5A, + FloatMat3 = 0x8B5B, + FloatMat4 = 0x8B5C, + Sampler1D = 0x8B5D, + Sampler2D = 0x8B5E, + Sampler3D = 0x8B5F, + SamplerCube = 0x8B60, + Sampler1DShadow = 0x8B61, + Sampler2DShadow = 0x8B62, + Sampler2DRect = 0x8B63, + Sampler2DRectShadow = 0x8B64, + FloatMat2x3 = 0x8B65, + FloatMat2x4 = 0x8B66, + FloatMat3x2 = 0x8B67, + FloatMat3x4 = 0x8B68, + FloatMat4x2 = 0x8B69, + FloatMat4x3 = 0x8B6A, + Sampler1DArray = 0x8DC0, + Sampler2DArray = 0x8DC1, + SamplerBuffer = 0x8DC2, + Sampler1DArrayShadow = 0x8DC3, + Sampler2DArrayShadow = 0x8DC4, + SamplerCubeShadow = 0x8DC5, + UnsignedIntVec2 = 0x8DC6, + UnsignedIntVec3 = 0x8DC7, + UnsignedIntVec4 = 0x8DC8, + IntSampler1D = 0x8DC9, + IntSampler2D = 0x8DCA, + IntSampler3D = 0x8DCB, + IntSamplerCube = 0x8DCC, + IntSampler2DRect = 0x8DCD, + IntSampler1DArray = 0x8DCE, + IntSampler2DArray = 0x8DCF, + IntSamplerBuffer = 0x8DD0, + UnsignedIntSampler1D = 0x8DD1, + UnsignedIntSampler2D = 0x8DD2, + UnsignedIntSampler3D = 0x8DD3, + UnsignedIntSamplerCube = 0x8DD4, + UnsignedIntSampler2DRect = 0x8DD5, + UnsignedIntSampler1DArray = 0x8DD6, + UnsignedIntSampler2DArray = 0x8DD7, + UnsignedIntSamplerBuffer = 0x8DD8, + Sampler2DMultisample = 0x9108, + IntSampler2DMultisample = 0x9109, + UnsignedIntSampler2DMultisample = 0x910A, + Sampler2DMultisampleArray = 0x910B, + IntSampler2DMultisampleArray = 0x910C, + UnsignedIntSampler2DMultisampleArray = 0x910D, + } + + public enum BufferTarget : GLenum + { + ArrayBuffer = 0x8892, + ElementArrayBuffer = 0x8893, + PixelPackBuffer = 0x88EB, + PixelUnpackBuffer = 0x88EC, + UniformBuffer = 0x8A11, + TextureBuffer = 0x8C2A, + TransformFeedbackBuffer = 0x8C8E, + CopyReadBuffer = 0x8F36, + CopyWriteBuffer = 0x8F37, + DrawIndirectBuffer = 0x8F3F, + AtomicCounterBuffer = 0x92C0, + DispatchIndirectBuffer = 0x90EE, + QueryBuffer = 0x9192, + ShaderStorageBuffer = 0x90D2, + } + + public enum TextureTarget : GLenum + { + Texture1D = 0x0DE0, + Texture2D = 0x0DE1, + Texture3D = 0x806F, + Texture1DArray = 0x8C18, + Texture2DArray = 0x8C1A, + TextureRectangle = 0x84F5, + TextureCubeMap = 0x8513, + TextureCubeMapPositiveX = 0x8515, + TextureCubeMapNegativeX = 0x8516, + TextureCubeMapPositiveY = 0x8517, + TextureCubeMapNegativeY = 0x8518, + TextureCubeMapPositiveZ = 0x8519, + TextureCubeMapNegativeZ = 0x851A, + TextureCubeMapArray = 0x9009, + Texture2DMultisample = 0x9100, + Texture2DMultisampleArray = 0x9102 + } + + public enum DrawElementsType : GLenum + { + UnsignedByte = 0x1401, + UnsignedShort = 0x1403, + UnsignedInt = 0x1405, + } + + public enum VertexAttribPointerType : GLenum + { + Byte = 0x1400, + UnsignedByte = 0x1401, + Short = 0x1402, + UnsignedShort = 0x1403, + Int = 0x1404, + UnsignedInt = 0x1405, + Float = 0x1406, + Double = 0x140A, + HalfFloat = 0x140B, + UnsignedUInt2101010Reversed = 0x8368, + UnsignedInt2101010Reversed = 0x8D9F, + UnsignedUInt101111Reversed = 0x8C3B + } + + public enum BufferUsageHint : GLenum + { + StreamDraw = 0x88E0, + StreamRead = 0x88E1, + StreamCopy = 0x88E2, + StaticDraw = 0x88E4, + StaticRead = 0x88E5, + StaticCopy = 0x88E6, + DynamicDraw = 0x88E8, + DynamicRead = 0x88E9, + DynamicCopy = 0x88EA, + } + + public enum PixelFormat : GLenum + { + ColorIndex = 0x1900, + StencilIndex = 0x1901, + DepthComponent = 0x1902, + Red = 0x1903, + Green = 0x1904, + Blue = 0x1905, + Alpha = 0x1906, + Rgb = 0x1907, + Rgba = 0x1908, + Luminance = 0x1909, + LuminanceAlpha = 0x190A, + AbgrExt = 0x8000, + CmykExt = 0x800C, + CmykaExt = 0x800D, + Bgr = 0x80E0, + Bgra = 0x80E1, + Ycrcb422Sgix = 0x81BB, + Ycrcb444Sgix = 0x81BC, + Rg = 0x8227, + RgInteger = 0x8228, + DepthStencil = 0x84F9, + RedInteger = 0x8D94, + GreenInteger = 0x8D95, + BlueInteger = 0x8D96, + AlphaInteger = 0x8D97, + RgbInteger = 0x8D98, + RgbaInteger = 0x8D99, + BgrInteger = 0x8D9A, + BgraInteger = 0x8D9B, + } + + public enum PixelInternalFormat : GLint + { + DepthComponent = 0x1902, + Alpha = 0x1906, + Rgb = 0x1907, + Rgba = 0x1908, + Luminance = 0x1909, + LuminanceAlpha = 0x190A, + R3G3B2 = 0x2A10, + Alpha4 = 0x803B, + Alpha8 = 0x803C, + Alpha12 = 0x803D, + Alpha16 = 0x803E, + Luminance4 = 0x803F, + Luminance8 = 0x8040, + Luminance12 = 0x8041, + Luminance16 = 0x8042, + Luminance4Alpha4 = 0x8043, + Luminance6Alpha2 = 0x8044, + Luminance8Alpha8 = 0x8045, + Luminance12Alpha4 = 0x8046, + Luminance12Alpha12 = 0x8047, + Luminance16Alpha16 = 0x8048, + Intensity = 0x8049, + Intensity4 = 0x804A, + Intensity8 = 0x804B, + Intensity12 = 0x804C, + Intensity16 = 0x804D, + Rgb2Ext = 0x804E, + Rgb4 = 0x804F, + Rgb5 = 0x8050, + Rgb8 = 0x8051, + Rgb10 = 0x8052, + Rgb12 = 0x8053, + Rgb16 = 0x8054, + Rgba2 = 0x8055, + Rgba4 = 0x8056, + Rgb5A1 = 0x8057, + Rgba8 = 0x8058, + Rgb10A2 = 0x8059, + Rgba12 = 0x805A, + Rgba16 = 0x805B, + DualAlpha4Sgis = 0x8110, + DualAlpha8Sgis = 0x8111, + DualAlpha12Sgis = 0x8112, + DualAlpha16Sgis = 0x8113, + DualLuminance4Sgis = 0x8114, + DualLuminance8Sgis = 0x8115, + DualLuminance12Sgis = 0x8116, + DualLuminance16Sgis = 0x8117, + DualIntensity4Sgis = 0x8118, + DualIntensity8Sgis = 0x8119, + DualIntensity12Sgis = 0x811A, + DualIntensity16Sgis = 0x811B, + DualLuminanceAlpha4Sgis = 0x811C, + DualLuminanceAlpha8Sgis = 0x811D, + QuadAlpha4Sgis = 0x811E, + QuadAlpha8Sgis = 0x811F, + QuadLuminance4Sgis = 0x8120, + QuadLuminance8Sgis = 0x8121, + QuadIntensity4Sgis = 0x8122, + QuadIntensity8Sgis = 0x8123, + DepthComponent16 = 0x81a5, + DepthComponent16Sgix = 0x81A5, + DepthComponent24 = 0x81a6, + DepthComponent24Sgix = 0x81A6, + DepthComponent32 = 0x81a7, + DepthComponent32Sgix = 0x81A7, + CompressedRed = 0x8225, + CompressedRg = 0x8226, + R8 = 0x8229, + R16 = 0x822A, + Rg8 = 0x822B, + Rg16 = 0x822C, + R16f = 0x822D, + R32f = 0x822E, + Rg16f = 0x822F, + Rg32f = 0x8230, + R8i = 0x8231, + R8ui = 0x8232, + R16i = 0x8233, + R16ui = 0x8234, + R32i = 0x8235, + R32ui = 0x8236, + Rg8i = 0x8237, + Rg8ui = 0x8238, + Rg16i = 0x8239, + Rg16ui = 0x823A, + Rg32i = 0x823B, + Rg32ui = 0x823C, + CompressedRgbS3tcDxt1Ext = 0x83F0, + CompressedRgbaS3tcDxt1Ext = 0x83F1, + CompressedRgbaS3tcDxt3Ext = 0x83F2, + CompressedRgbaS3tcDxt5Ext = 0x83F3, + CompressedAlpha = 0x84E9, + CompressedLuminance = 0x84EA, + CompressedLuminanceAlpha = 0x84EB, + CompressedIntensity = 0x84EC, + CompressedRgb = 0x84ED, + CompressedRgba = 0x84EE, + DepthStencil = 0x84F9, + Rgba32f = 0x8814, + Rgb32f = 0x8815, + Rgba16f = 0x881A, + Rgb16f = 0x881B, + Depth24Stencil8 = 0x88F0, + R11fG11fB10f = 0x8C3A, + Rgb9E5 = 0x8C3D, + Srgb = 0x8C40, + Srgb8 = 0x8C41, + SrgbAlpha = 0x8C42, + Srgb8Alpha8 = 0x8C43, + SluminanceAlpha = 0x8C44, + Sluminance8Alpha8 = 0x8C45, + Sluminance = 0x8C46, + Sluminance8 = 0x8C47, + CompressedSrgb = 0x8C48, + CompressedSrgbAlpha = 0x8C49, + CompressedSluminance = 0x8C4A, + CompressedSluminanceAlpha = 0x8C4B, + CompressedSrgbS3tcDxt1Ext = 0x8C4C, + CompressedSrgbAlphaS3tcDxt1Ext = 0x8C4D, + CompressedSrgbAlphaS3tcDxt3Ext = 0x8C4E, + CompressedSrgbAlphaS3tcDxt5Ext = 0x8C4F, + DepthComponent32f = 0x8CAC, + Depth32fStencil8 = 0x8CAD, + Rgba32ui = 0x8D70, + Rgb32ui = 0x8D71, + Rgba16ui = 0x8D76, + Rgb16ui = 0x8D77, + Rgba8ui = 0x8D7C, + Rgb8ui = 0x8D7D, + Rgba32i = 0x8D82, + Rgb32i = 0x8D83, + Rgba16i = 0x8D88, + Rgb16i = 0x8D89, + Rgba8i = 0x8D8E, + Rgb8i = 0x8D8F, + Float32UnsignedInt248Rev = 0x8DAD, + CompressedRedRgtc1 = 0x8DBB, + CompressedSignedRedRgtc1 = 0x8DBC, + CompressedRgRgtc2 = 0x8DBD, + CompressedSignedRgRgtc2 = 0x8DBE, + One = 1, + Two = 2, + Three = 3, + Four = 4, + } + + public enum PixelStoreParameter : GLenum + { + UnpackSwapBytes = 0x0CF0, + UnpackLsbFirst = 0x0CF1, + UnpackRowLength = 0x0CF2, + UnpackSkipRows = 0x0CF3, + UnpackSkipPixels = 0x0CF4, + UnpackAlignment = 0x0CF5, + PackSwapBytes = 0x0D00, + PackLsbFirst = 0x0D01, + PackRowLength = 0x0D02, + PackSkipRows = 0x0D03, + PackSkipPixels = 0x0D04, + PackAlignment = 0x0D05, + PackSkipImages = 0x806B, + PackSkipImagesExt = 0x806B, + PackImageHeight = 0x806C, + PackImageHeightExt = 0x806C, + UnpackSkipImages = 0x806D, + UnpackSkipImagesExt = 0x806D, + UnpackImageHeight = 0x806E, + UnpackImageHeightExt = 0x806E, + PackSkipVolumesSgis = 0x8130, + PackImageDepthSgis = 0x8131, + UnpackSkipVolumesSgis = 0x8132, + UnpackImageDepthSgis = 0x8133, + PixelTileWidthSgix = 0x8140, + PixelTileHeightSgix = 0x8141, + PixelTileGridWidthSgix = 0x8142, + PixelTileGridHeightSgix = 0x8143, + PixelTileGridDepthSgix = 0x8144, + PixelTileCacheSizeSgix = 0x8145, + PackResampleSgix = 0x842C, + UnpackResampleSgix = 0x842D, + PackSubsampleRateSgix = 0x85A0, + UnpackSubsampleRateSgix = 0x85A1, + } + + public enum TextureParameterName : GLenum + { + TextureBaseLevel = 0x813C, + TextureBorderColor = 0x1004, + TextureCompareMode = 0x884C, + TextureCompareFunc = 0x884D, + TextureLodBias = 0x8501, + TextureMagFilter = 0x2800, + TextureMaxLevel = 0x813D, + TextureMaxLod = 0x813B, + TextureMinFilter = 0x2801, + TextureMinLod = 0x813A, + TextureSwizzleR = 0x8E42, + TextureSwizzleG = 0x8E43, + TextureSwizzleB = 0x8E44, + TextureSwizzleA = 0x8E45, + TextureSwizzleRGBA = 0x8E46, + TextureWrapS = 0x2802, + TextureWrapT = 0x2803, + TextureWrapR = 0x8072, + MaxAnisotropyExt = 0x84FE + } + + public enum TextureParameter : GLint + { + Nearest = 0x2600, + Linear = 0x2601, + NearestMipMapNearest = 0x2700, + LinearMipMapNearest = 0x2701, + NearestMipMapLinear = 0x2702, + LinearMipMapLinear = 0x2703, + ClampToEdge = 0x812F, + ClampToBorder = 0x812D, + MirrorClampToEdge = 0x8743, + MirroredRepeat = 0x8370, + Repeat = 0x2901, + Red = 0x1903, + Green = 0x1904, + Blue = 0x1905, + Alpha = 0x1906, + Zero = 0, + One = 1, + CompareRefToTexture = 0x884E, + None = 0, + StencilIndex = 0x1901, + DepthComponent = 0x1902, + MaxAnisotropyExt = 0x84FE + } + + public enum PixelType : GLenum + { + Byte = 0x1400, + UnsignedByte = 0x1401, + Short = 0x1402, + UnsignedShort = 0x1403, + Int = 0x1404, + UnsignedInt = 0x1405, + Float = 0x1406, + HalfFloat = 0x140B, + Bitmap = 0x1A00, + UnsignedByte332 = 0x8032, + UnsignedByte332Ext = 0x8032, + UnsignedShort4444 = 0x8033, + UnsignedShort4444Ext = 0x8033, + UnsignedShort5551 = 0x8034, + UnsignedShort5551Ext = 0x8034, + UnsignedInt8888 = 0x8035, + UnsignedInt8888Ext = 0x8035, + UnsignedInt1010102 = 0x8036, + UnsignedInt1010102Ext = 0x8036, + UnsignedByte233Reversed = 0x8362, + UnsignedShort565 = 0x8363, + UnsignedShort565Reversed = 0x8364, + UnsignedShort4444Reversed = 0x8365, + UnsignedShort1555Reversed = 0x8366, + UnsignedInt8888Reversed = 0x8367, + UnsignedInt2101010Reversed = 0x8368, + UnsignedInt248 = 0x84FA, + UnsignedInt10F11F11FRev = 0x8C3B, + UnsignedInt5999Rev = 0x8C3E, + Float32UnsignedInt248Rev = 0x8DAD, + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/GL.Utils.cs b/src/BUTR.CrashReport.OpenGLES3/GL.Utils.cs new file mode 100644 index 0000000..f3dc158 --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/GL.Utils.cs @@ -0,0 +1,126 @@ +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace OpenGLES3; + +/// +/// the methods here are just convenience wrappers for calling the raw gl* method +/// +unsafe partial class GL +{ + public uint GenBuffer() + { + var buffer = 0U; + GenBuffers(1, &buffer); + return buffer; + } + + public void DeleteBuffer(uint buffer) + { + DeleteBuffers(1, &buffer); + } + + public string GetShaderInfoLogUtf16(uint shader) + { + var infoLogLength = 0; + GetShaderIV(shader, ShaderParameter.InfoLogLength, &infoLogLength); + if (infoLogLength == 0) + return string.Empty; + + var infoLogPtr = stackalloc byte[infoLogLength]; + var length = 0U; + GetShaderInfoLog(shader, (uint) infoLogLength, &length, infoLogPtr); + return Encoding.UTF8.GetString(infoLogPtr, (int) length); + } + + public void ShaderSourceUtf8(uint shader, ReadOnlySpan source) + { + var sources = stackalloc byte*[1] { (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(source)) }; + var length = stackalloc int[1] { source.Length }; + ShaderSource(shader, 1, (IntPtr) sources, length); + } + + public bool GetShaderCompileStatus(uint shader) + { + var compileStatus = 0; + GetShaderIV(shader, ShaderParameter.CompileStatus, &compileStatus); + return Unsafe.As(ref compileStatus); + } + + public string GetProgramInfoLogUtf16(uint program) + { + var infoLogLength = 0; + GetProgramiv(program, ProgramParameter.InfoLogLength, &infoLogLength); + if (infoLogLength == 0) + return string.Empty; + + var infoLogPtr = stackalloc byte[infoLogLength]; + var length = 0U; + GetProgramInfoLog(program, (uint) infoLogLength, &length, infoLogPtr); + return Encoding.UTF8.GetString(infoLogPtr, (int) length); + } + + public bool GetProgramLinkStatus(uint program) + { + var linkStatus = 0; + GetProgramiv(program, ProgramParameter.LinkStatus, &linkStatus); + return Unsafe.As(ref linkStatus); + } + + public void UniformMatrix4(int location, ref readonly Matrix4x4 value) + { + var data = stackalloc float[16] + { + value.M11, value.M12, value.M13, value.M14, + value.M21, value.M22, value.M23, value.M24, + value.M31, value.M32, value.M33, value.M34, + value.M41, value.M42, value.M43, value.M44, + }; + UniformMatrix4FV(location, 1, 0, data); + } + + public void VertexAttribPointerBool(int index, int size, VertexAttribPointerType type, bool normalized, uint stride, void* pointer) + { + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + VertexAttribPointer((uint) index, size, type, Unsafe.As(ref normalized), stride, pointer); + } + + public uint GenVertexArray() + { + var array = 0U; + GenVertexArrays(1, &array); + return array; + } + + public void DeleteVertexArray(uint vao) + { + DeleteVertexArrays(1, &vao); + } + + public uint GenTexture() + { + var texture = 0U; + GenTextures(1, &texture); + return texture; + } + + public void DeleteTexture(uint texture) + { + DeleteTextures(1, &texture); + } + + [Conditional("DEBUG")] + public void CheckError(string title = "", [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) + { + var error = GetError(); + while (error != GL_NO_ERROR) + { + Debug.Print($"{filePath} {memberName}:{lineNumber - 1} - [{title}: {error}]"); + error = GetError(); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/GL.cs b/src/BUTR.CrashReport.OpenGLES3/GL.cs new file mode 100644 index 0000000..37075c3 --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/GL.cs @@ -0,0 +1,254 @@ +using BUTR.CrashReport.Memory.Utils; +using BUTR.CrashReport.Native; + +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +[assembly: DelegateLoader(typeof(OpenGLES3.GL), true)] + +namespace OpenGLES3; + +using IntPtrActiveAttribType = Pointer; +using IntPtrActiveUniformType = Pointer; +using IntPtrGLchar = Pointer; +using IntPtrGLfloat = Pointer; +using IntPtrGLint = Pointer; +using IntPtrGLuint = Pointer; +using IntPtrIntPtrGLchar = Pointer>; + +public partial class GL +{ + public readonly IntPtr Context; + private readonly Func _getFunctionPointer; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGenBuffers(GLsizei n, IntPtrGLuint buffers); + public glGenBuffers GenBuffers = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDeleteBuffers(GLsizei n, IntPtrGLuint buffers); + public glDeleteBuffers DeleteBuffers = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); + public glViewport Viewport = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + public glClearColor ClearColor = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glClear(ClearBufferMask mask); + public glClear Clear = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glEnable(EnableCap cap); + public glEnable Enable = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDisable(EnableCap cap); + public glDisable Disable = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBlendEquation(BlendEquationMode mode); + public glBlendEquation BlendEquation = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUseProgram(GLuint program); + public glUseProgram UseProgram = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGetShaderiv(GLuint shader, ShaderParameter pname, IntPtrGLint param); + public glGetShaderiv GetShaderIV = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, IntPtrGLuint length, IntPtrGLchar infoLog); + public glGetShaderInfoLog GetShaderInfoLog = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate GLuint glCreateShader(ShaderType type); + public glCreateShader CreateShader = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glShaderSource(GLuint shader, GLsizei count, IntPtrIntPtrGLchar sources, IntPtrGLint length); + public glShaderSource ShaderSource = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glCompileShader(GLuint shader); + public glCompileShader CompileShader = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDeleteShader(GLuint shader); + public glDeleteShader DeleteShader = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGetProgramiv(GLuint program, ProgramParameter pname, IntPtrGLint param); + public glGetProgramiv GetProgramiv = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGetProgramInfoLog(GLuint program, GLsizei bufSize, IntPtrGLuint length, IntPtrGLchar infoLog); + public glGetProgramInfoLog GetProgramInfoLog = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate GLuint glCreateProgram(); + public glCreateProgram CreateProgram = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glAttachShader(GLuint program, GLuint shader); + public glAttachShader AttachShader = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glLinkProgram(GLuint program); + public glLinkProgram LinkProgram = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate GLint glGetUniformLocation(GLuint program, IntPtrGLchar name); + public glGetUniformLocation GetUniformLocation = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate GLint glGetAttribLocation(GLuint program, IntPtrGLchar name); + public glGetAttribLocation GetAttribLocation = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDetachShader(GLuint program, GLuint shader); + public glDetachShader DetachShader = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDeleteProgram(GLuint program); + public glDeleteProgram DeleteProgram = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, IntPtrGLuint length, IntPtrGLint size, IntPtrActiveAttribType type, IntPtrGLchar name); + public glGetActiveAttrib GetActiveAttrib = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, IntPtrGLuint length, IntPtrGLint size, IntPtrActiveUniformType type, IntPtrGLchar name); + public glGetActiveUniform GetActiveUniform = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUniform1f(GLint location, GLfloat v0); + public glUniform1f Uniform1F = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUniform2f(GLint location, GLfloat v0, GLfloat v1); + public glUniform2f Uniform2F = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + public glUniform3f Uniform3F = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + public glUniform4f Uniform4F = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUniform1i(GLint location, GLint v0); + public glUniform1i Uniform1I = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, IntPtrGLfloat value); + public glUniformMatrix4fv UniformMatrix4FV = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBindVertexArray(GLuint array); + public glBindVertexArray BindVertexArray = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBindBuffer(BufferTarget target, GLuint buffer); + public glBindBuffer BindBuffer = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glEnableVertexAttribArray(GLuint index); + public glEnableVertexAttribArray EnableVertexAttribArray = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glVertexAttribPointer(GLuint index, GLint size, VertexAttribPointerType type, GLboolean normalized, GLsizei stride, IntPtrVoid pointer); + public glVertexAttribPointer VertexAttribPointer = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBindTexture(TextureTarget target, GLuint texture); + public glBindTexture BindTexture = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBufferData(BufferTarget target, GLsizeiptr size, IntPtrVoid data, BufferUsageHint usage); + public glBufferData BufferData = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glScissor(GLint x, GLint y, GLsizei width, GLsizei height); + public glScissor Scissor = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDeleteVertexArrays(GLsizei n, IntPtrGLuint arrays); + public glDeleteVertexArrays DeleteVertexArrays = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGenVertexArrays(GLsizei n, IntPtrGLuint arrays); + public glGenVertexArrays GenVertexArrays = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glGenTextures(GLsizei n, IntPtrGLuint textures); + public glGenTextures GenTextures = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glPixelStorei(PixelStoreParameter pname, GLint param); + public glPixelStorei PixelStoreI = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glTexParameteri(TextureTarget target, TextureParameterName pname, TextureParameter param); + public glTexParameteri TexParameterI = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDeleteTextures(GLsizei n, IntPtrGLuint textures); + public glDeleteTextures DeleteTextures = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glActiveTexture(TextureUnit texture); + public glActiveTexture ActiveTexture = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glDrawElements(PrimitiveType mode, GLsizei count, DrawElementsType type, IntPtrVoid indices); + public glDrawElements DrawElements = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBindSampler(GLuint unit, GLuint sampler); + public glBindSampler BindSampler = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glBlendFuncSeparate(BlendingFactorDest sfactorRGB, BlendingFactorDest dfactorRGB, BlendingFactorDest sfactorAlpha, BlendingFactorDest dfactorAlpha); + public glBlendFuncSeparate BlendFuncSeparate = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate GLenum glGetError(); + public glGetError GetError = null!; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void glTexImage2D(TextureTarget target, GLint level, PixelInternalFormat internalformat, GLsizei width, GLsizei height, GLint border, PixelFormat format, PixelType type, IntPtrVoid data); + public glTexImage2D TexImage2D = null!; + + public GL(IntPtr context, Func getFunctionPointer) + { + Context = context; + _getFunctionPointer = getFunctionPointer; + + this.LoadFrom(LoadFunctionPointer); + } + + private unsafe IntPtr LoadFunctionPointer(string utf16FunctionName) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16FunctionName.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16FunctionName, utf8); + utf8[length] = 0; + + var ptr = (IntPtr) _getFunctionPointer((IntPtr) Unsafe.AsPointer(ref MemoryMarshal.GetReference(utf8))); + tempMemory?.Dispose(); + + if (ptr == IntPtr.Zero) + throw new InvalidOperationException($"Failed to load function pointer for {utf16FunctionName}"); + + return ptr; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.OpenGLES3/GlobalUsing.cs b/src/BUTR.CrashReport.OpenGLES3/GlobalUsing.cs new file mode 100644 index 0000000..943cb9e --- /dev/null +++ b/src/BUTR.CrashReport.OpenGLES3/GlobalUsing.cs @@ -0,0 +1,39 @@ +// ReSharper disable RedundantUsingDirective.Global +global using GLboolean = byte; +global using GLbyte = sbyte; +global using GLubyte = byte; +global using GLshort = short; +global using GLushort = ushort; +global using GLint = int; +global using GLuint = uint; +// GLfixed +global using GLint64 = long; +global using GLuint64 = ulong; +global using GLsizei = uint; +global using GLenum = int; +global using GLintptr = nint; +global using GLsizeiptr = nuint; +global using GLsync = nint; +global using GLbitfield = uint; +// GLhalf +global using GLfloat = float; +global using GLclampf = float; +global using GLdouble = double; +global using GLclampd = double; +global using GLchar = byte; + +global using UnusedSB = sbyte; +global using UnusedS = short; +global using UnusedUS = ushort; +global using UnusedU = nuint; +global using UnusedL = long; +global using UnusedUL = ulong; +// ReSharper restore RedundantUsingDirective.Global + +#if NET6_0_OR_GREATER +global using IntPtrByte = BUTR.CrashReport.Native.Pointer; +global using IntPtrVoid = BUTR.CrashReport.Native.Pointer; +#else +global using IntPtrByte = BUTR.CrashReport.Native.Pointer; +global using IntPtrVoid = BUTR.CrashReport.Native.Pointer; +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Tool/BUTR.CrashReport.Tool.csproj b/src/BUTR.CrashReport.Renderer.Html.Tool/BUTR.CrashReport.Renderer.Html.Tool.csproj similarity index 54% rename from src/BUTR.CrashReport.Tool/BUTR.CrashReport.Tool.csproj rename to src/BUTR.CrashReport.Renderer.Html.Tool/BUTR.CrashReport.Renderer.Html.Tool.csproj index 40189ae..4396b35 100644 --- a/src/BUTR.CrashReport.Tool/BUTR.CrashReport.Tool.csproj +++ b/src/BUTR.CrashReport.Renderer.Html.Tool/BUTR.CrashReport.Renderer.Html.Tool.csproj @@ -2,30 +2,26 @@ Exe - net6.0;net7.0;net8.0 - preview + net6.0;net8.0 + latest enable + true + $(DefineConstants);BUTRCRASHREPORT_DISABLE;BUTRCRASHREPORT_ENABLE_HTML_RENDERER; - 0 - 1.0.0.$(GITHUB_RUN_NUMBER) - - butr_crashreport + butr_crashreport_html true true - true - true - BUTR.CrashReport.Tool - BUTR.CrashReport.Tool + BUTR.CrashReport.Renderer.Html.Tool $(Version) BUTR.CrashReport.Tool Aragas BUTR Provides various commands - Copyright © 2023 Bannerlord's Unofficial Tools & Resources + Copyright © 2023-2024 Bannerlord's Unofficial Tools & Resources MIT bannerlord butr html zip + $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)BUTR.CrashReport.Models.dll; + + + + diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/BUTR.CrashReport.Renderer.ImGui.Silk.NET.csproj b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/BUTR.CrashReport.Renderer.ImGui.Silk.NET.csproj new file mode 100644 index 0000000..11c1676 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/BUTR.CrashReport.Renderer.ImGui.Silk.NET.csproj @@ -0,0 +1,91 @@ + + + + netstandard2.0;net6.0;net8.0 + enable + enable + latest + true + $(DefineConstants);OPENGL + + + + BUTR.CrashReport.Renderer.ImGui.Silk.NET + BUTR.CrashReport.Renderer.ImGui.Silk.NET + + MIT + https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png + + + + + + resources + resources\icon.png + + + resources + resources\icon_128.bin + + + resources + resources\icon_16.bin + + + resources + resources\icon_18.bin + + + resources + resources\icon_20.bin + + + resources + resources\icon_22.bin + + + resources + resources\icon_24.bin + + + resources + resources\icon_28.bin + + + resources + resources\icon_32.bin + + + resources + resources\icon_48.bin + + + resources + resources\icon_64.bin + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Draw.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Draw.cs new file mode 100644 index 0000000..0f9bc29 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Draw.cs @@ -0,0 +1,34 @@ +using ImGui.Structures; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; + +partial class ImGuiController +{ + /// + /// Renders the ImGui draw list data. + /// + public void Render() + { + if (!_frameBegun) + return; + + var oldCtx = _imgui.GetCurrentContext(); + if (oldCtx != _context) + { + _imgui.SetCurrentContext(_context); + } + + //_frameBegun = false; + _imgui.Render(); + + _imgui.GetDrawData(out var imDrawData); + RenderImDrawData(ref imDrawData); + + if (oldCtx != _context) + { + _imgui.SetCurrentContext(oldCtx); + } + } + + partial void RenderImDrawData(ref readonly ImDrawDataWrapper drawData); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Input.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Input.cs new file mode 100644 index 0000000..49295e5 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Input.cs @@ -0,0 +1,192 @@ +using BUTR.CrashReport.ImGui.Utils; + +using ImGuiNET; + +using Silk.NET.Input; +using Silk.NET.Input.Extensions; +using Silk.NET.Maths; + +using System.Drawing; +using System.Numerics; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; + +partial class ImGuiController +{ + private void WindowResized(Vector2D size) + { + _windowsWidth = (uint) size.X; + _windowsHeight = (uint) size.Y; + } + + private void OnKeyEvent(IKeyboard keyboard, Key keycode, int scancode, bool down) + { + var imGuiKey = KeyToImGuiKey(keycode); + if (imGuiKey != ImGuiKey.None) + { + _imgui.GetIO(out var io); + io.AddKeyEvent(imGuiKey, down); + io.SetKeyEventNativeData(imGuiKey, (int) keycode, scancode); + } + } + + private void OnKeyChar(IKeyboard keyboard, char c) + { + //_pressedChars.Add(c); + _imgui.GetIO(out var io); + io.AddInputCharacter(c); + } + + private void MouseOnScroll(IMouse mouse, ScrollWheel data) + { + _imgui.GetIO(out var io); + io.AddMouseWheelEvent(data.X, data.Y); + } + + private void MouseOnMouseDown(IMouse mouse, MouseButton button) + { + if (button is < 0 or > (MouseButton) ImGuiMouseButton.COUNT) + return; + + _imgui.GetIO(out var io); + io.AddMouseButtonEvent((ImGuiMouseButton) button, true); + } + + private void MouseOnMouseUp(IMouse mouse, MouseButton button) + { + if (button is < 0 or > (MouseButton) ImGuiMouseButton.COUNT) + return; + + _imgui.GetIO(out var io); + io.AddMouseButtonEvent((ImGuiMouseButton) button, false); + } + + private void MouseOnMouseMove(IMouse mouse, Vector2 pos) + { + _imgui.GetIO(out var io); + + ref var mousePosBackup = ref io.MousePos; + + /* + var focused = _glfw.GetWindowAttrib(_windowPtr, WindowAttributeGetter.Focused); + if (!focused) + { + _io.MousePos.X = -float.MaxValue; + _io.MousePos.Y = -float.MaxValue; + return; + } + */ + + if (io.WantSetMousePos) + { + mouse.Position = mousePosBackup; + } + else + { + io.MousePos.X = pos.X; + io.MousePos.Y = pos.Y; + } + } + + + public void Update(double delta) + { + var oldCtx = _imgui.GetCurrentContext(); + if (oldCtx != _context) + { + _imgui.SetCurrentContext(_context); + } + + if (_frameBegun) + { + _imgui.Render(); + } + + SetPerFrameImGuiData(delta); + + UpdateMouseCursor(); + //UpdateMouse(); + //UpdateKeyboard(); + + _frameBegun = true; + _imgui.NewFrame(); + + if (oldCtx != _context) + { + _imgui.SetCurrentContext(oldCtx); + } + } + + /// + /// Sets per-frame data based on the associated window. + /// This is called by Update(float). + /// + private void SetPerFrameImGuiData(double delta) + { + _imgui.GetIO(out var io); + + io.DisplaySize.X = _windowsWidth; + io.DisplaySize.Y = _windowsHeight; + + if (_windowsWidth > 0 && _windowsHeight > 0) + { + io.DisplayFramebufferScale.X = (float) _view.FramebufferSize.X / _windowsWidth; + io.DisplayFramebufferScale.Y = (float) _view.FramebufferSize.Y / _windowsHeight; + } + + io.DeltaTime = (float) delta; + } + + private void UpdateMouseCursor() + { + _imgui.GetIO(out var io); + + var flag = (io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0; + if (flag || Mouse.Cursor.CursorMode == CursorMode.Disabled) return; + + var imguiCursor = _imgui.GetMouseCursor(); + if (imguiCursor == BUTR.CrashReport.ImGui.Enums.ImGuiMouseCursor.None || io.MouseDrawCursor) + { + Mouse.Cursor.CursorMode = CursorMode.Hidden; + } + else + { + Mouse.Cursor.StandardCursor = _mouseCursors[(int) imguiCursor] != StandardCursor.Default ? _mouseCursors[(int) imguiCursor] : _mouseCursors[(int) ImGuiMouseCursor.Arrow]; + Mouse.Cursor.CursorMode = CursorMode.Normal; + } + } + + private void UpdateMouse() + { + _imgui.GetIO(out var io); + + using var mouseState = _input.Mice[0].CaptureState(); + + io.GetMouseDown(out var mouseDown); + mouseDown[ImGuiMouseButton.Left] = mouseState.IsButtonPressed(MouseButton.Left); + mouseDown[ImGuiMouseButton.Right] = mouseState.IsButtonPressed(MouseButton.Right); + mouseDown[ImGuiMouseButton.Middle] = mouseState.IsButtonPressed(MouseButton.Middle); + + var point = new Point((int) mouseState.Position.X, (int) mouseState.Position.Y); + io.MousePos = new Vector2(point.X, point.Y); + + ref var wheel = ref mouseState.GetScrollWheels()[0]; + io.MouseWheel = wheel.Y; + io.MouseWheelH = wheel.X; + } + + private void UpdateKeyboard() + { + _imgui.GetIO(out var io); + + var cs = _pressedChars.AsSpan(); + for (var i = 0; i < cs.Length; i++) + io.AddInputCharacter(cs[i]); + _pressedChars.Clear(); + + io.KeyCtrl = Keyboard.IsKeyPressed(Key.ControlLeft) || Keyboard.IsKeyPressed(Key.ControlRight); + io.KeyAlt = Keyboard.IsKeyPressed(Key.AltLeft) || Keyboard.IsKeyPressed(Key.AltRight); + io.KeyShift = Keyboard.IsKeyPressed(Key.ShiftLeft) || Keyboard.IsKeyPressed(Key.ShiftRight); + io.KeySuper = Keyboard.IsKeyPressed(Key.SuperLeft) || Keyboard.IsKeyPressed(Key.SuperRight); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Draw.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.OGL.Draw.cs similarity index 64% rename from src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Draw.cs rename to src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.OGL.Draw.cs index d71642a..ead0b77 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Draw.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.OGL.Draw.cs @@ -1,36 +1,17 @@ -using ImGuiNET; +#if OPENGL + +using ImGui.Structures; using Silk.NET.OpenGL; -using System; +using System.Diagnostics.CodeAnalysis; using System.Numerics; -namespace BUTR.CrashReport.Renderer.ImGui.Controller; +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; -partial class ImGuiController +internal partial class ImGuiController { - /// - /// Renders the ImGui draw list data. - /// - public void Render() - { - var oldCtx = _imgui.GetCurrentContext(); - - if (oldCtx != _context) - { - _imgui.SetCurrentContext(_context); - } - - _imgui.Render(); - RenderImDrawData(_imgui.GetDrawData()); - - if (oldCtx != _context) - { - _imgui.SetCurrentContext(oldCtx); - } - } - - private void RenderImDrawData(ImDrawDataPtr drawData) + partial void RenderImDrawData(ref readonly ImDrawDataWrapper drawData) { if (drawData.CmdListsCount == 0) return; @@ -40,102 +21,149 @@ private void RenderImDrawData(ImDrawDataPtr drawData) // Backup GL state var lastActiveTexture = (TextureUnit) _gl.GetInteger(GLEnum.ActiveTexture); + _gl.CheckGlError(); _gl.ActiveTexture(GLEnum.Texture0); + _gl.CheckGlError(); var lastProgram = (uint) _gl.GetInteger(GLEnum.CurrentProgram); + _gl.CheckGlError(); var lastTexture = (uint) _gl.GetInteger(GLEnum.TextureBinding2D); + _gl.CheckGlError(); var lastSampler = (uint) _gl.GetInteger(GLEnum.SamplerBinding); + _gl.CheckGlError(); var lastVertexArrayObject = (uint) _gl.GetInteger(GLEnum.VertexArrayBinding); + _gl.CheckGlError(); var lastArrayBuffer = (uint) _gl.GetInteger(GLEnum.ArrayBufferBinding); + _gl.CheckGlError(); - Span lastPolygonMode = stackalloc int[2]; - _gl.GetInteger(GLEnum.PolygonMode, lastPolygonMode); + var lastPolygonMode = (PolygonMode) _gl.GetInteger(GLEnum.PolygonMode); + _gl.CheckGlError(); Span lastScissorBox = stackalloc int[4]; _gl.GetInteger(GLEnum.ScissorBox, lastScissorBox); + _gl.CheckGlError(); Span lastViewport = stackalloc int[4]; _gl.GetInteger(GLEnum.Viewport, lastViewport); + _gl.CheckGlError(); var lastBlendSrcRgb = (BlendingFactor) _gl.GetInteger(GLEnum.BlendSrcRgb); + _gl.CheckGlError(); var lastBlendDstRgb = (BlendingFactor) _gl.GetInteger(GLEnum.BlendDstRgb); + _gl.CheckGlError(); var lastBlendSrcAlpha = (BlendingFactor) _gl.GetInteger(GLEnum.BlendSrcAlpha); + _gl.CheckGlError(); var lastBlendDstAlpha = (BlendingFactor) _gl.GetInteger(GLEnum.BlendDstAlpha); + _gl.CheckGlError(); var lastBlendEquationRgb = (BlendEquationModeEXT) _gl.GetInteger(GLEnum.BlendEquationRgb); + _gl.CheckGlError(); var lastBlendEquationAlpha = (BlendEquationModeEXT) _gl.GetInteger(GLEnum.BlendEquationAlpha); + _gl.CheckGlError(); var lastEnableBlend = _gl.IsEnabled(GLEnum.Blend); + _gl.CheckGlError(); var lastEnableCullFace = _gl.IsEnabled(GLEnum.CullFace); + _gl.CheckGlError(); var lastEnableDepthTest = _gl.IsEnabled(GLEnum.DepthTest); + _gl.CheckGlError(); var lastEnableStencilTest = _gl.IsEnabled(GLEnum.StencilTest); + _gl.CheckGlError(); var lastEnableScissorTest = _gl.IsEnabled(GLEnum.ScissorTest); + _gl.CheckGlError(); var lastEnablePrimitiveRestart = _gl.IsEnabled(GLEnum.PrimitiveRestart); + _gl.CheckGlError(); - SetupRenderState(drawData, framebufferWidth, framebufferHeight); + SetupRenderState(in drawData, framebufferWidth, framebufferHeight); - RenderCommandList(drawData, framebufferWidth, framebufferHeight); + RenderCommandList(in drawData, framebufferWidth, framebufferHeight); // Destroy the temporary VAO _gl.DeleteVertexArray(_vertexArrayObject); + _gl.CheckGlError(); _vertexArrayObject = 0; // Restore modified GL state _gl.UseProgram(lastProgram); + _gl.CheckGlError(); _gl.BindTexture(GLEnum.Texture2D, lastTexture); + _gl.CheckGlError(); _gl.BindSampler(0, lastSampler); + _gl.CheckGlError(); _gl.ActiveTexture(lastActiveTexture); + _gl.CheckGlError(); _gl.BindVertexArray(lastVertexArrayObject); + _gl.CheckGlError(); _gl.BindBuffer(GLEnum.ArrayBuffer, lastArrayBuffer); + _gl.CheckGlError(); _gl.BlendEquationSeparate(lastBlendEquationRgb, lastBlendEquationAlpha); + _gl.CheckGlError(); _gl.BlendFuncSeparate(lastBlendSrcRgb, lastBlendDstRgb, lastBlendSrcAlpha, lastBlendDstAlpha); + _gl.CheckGlError(); if (lastEnableBlend) _gl.Enable(GLEnum.Blend); else _gl.Disable(GLEnum.Blend); + _gl.CheckGlError(); if (lastEnableCullFace) _gl.Enable(GLEnum.CullFace); else _gl.Disable(GLEnum.CullFace); + _gl.CheckGlError(); if (lastEnableDepthTest) _gl.Enable(GLEnum.DepthTest); else _gl.Disable(GLEnum.DepthTest); + _gl.CheckGlError(); if (lastEnableStencilTest) _gl.Enable(GLEnum.StencilTest); else _gl.Disable(GLEnum.StencilTest); + _gl.CheckGlError(); if (lastEnableScissorTest) _gl.Enable(GLEnum.ScissorTest); else _gl.Disable(GLEnum.ScissorTest); + _gl.CheckGlError(); if (lastEnablePrimitiveRestart) _gl.Enable(GLEnum.PrimitiveRestart); else _gl.Disable(GLEnum.PrimitiveRestart); + _gl.CheckGlError(); - _gl.PolygonMode(GLEnum.Front, (PolygonMode) lastPolygonMode[0]); - _gl.PolygonMode(GLEnum.Back, (PolygonMode) lastPolygonMode[1]); + _gl.PolygonMode(GLEnum.FrontAndBack, lastPolygonMode); + _gl.CheckGlError(); _gl.Viewport(lastViewport[0], lastViewport[1], (uint) lastViewport[2], (uint) lastViewport[3]); + _gl.CheckGlError(); _gl.Scissor(lastScissorBox[0], lastScissorBox[1], (uint) lastScissorBox[2], (uint) lastScissorBox[3]); + _gl.CheckGlError(); } - private void SetupRenderState(ImDrawDataPtr drawData, uint framebufferWidth, uint framebufferHeight) + private void SetupRenderState(ref readonly ImDrawDataWrapper drawData, uint framebufferWidth, uint framebufferHeight) { _gl.Enable(GLEnum.Blend); + _gl.CheckGlError(); _gl.BlendEquation(GLEnum.FuncAdd); + _gl.CheckGlError(); _gl.BlendFuncSeparate(GLEnum.SrcAlpha, GLEnum.OneMinusSrcAlpha, GLEnum.One, GLEnum.OneMinusSrcAlpha); + _gl.CheckGlError(); _gl.Disable(GLEnum.CullFace); + _gl.CheckGlError(); _gl.Disable(GLEnum.DepthTest); + _gl.CheckGlError(); _gl.Disable(GLEnum.StencilTest); + _gl.CheckGlError(); _gl.Enable(GLEnum.ScissorTest); + _gl.CheckGlError(); _gl.Disable(GLEnum.PrimitiveRestart); + _gl.CheckGlError(); _gl.PolygonMode(GLEnum.FrontAndBack, GLEnum.Fill); _gl.Viewport(0, 0, framebufferWidth, framebufferHeight); + _gl.CheckGlError(); var orthographicProjection = Matrix4x4.CreateOrthographicOffCenter( left: drawData.DisplayPos.X, @@ -147,54 +175,70 @@ private void SetupRenderState(ImDrawDataPtr drawData, uint framebufferWidth, uin _shader.UseShader(); _gl.Uniform1(_attribLocationTex, 0); + _gl.CheckGlError(); _gl.UniformMatrix4x4(_attribLocationProjMtx, ref orthographicProjection); - _gl.CheckGlError("Projection"); + _gl.CheckGlError(); _gl.BindSampler(0, 0); + _gl.CheckGlError(); // Setup desired GL state // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. _vertexArrayObject = _gl.GenVertexArray(); + _gl.CheckGlError(); _gl.BindVertexArray(_vertexArrayObject); - _gl.CheckGlError("VAO"); + _gl.CheckGlError(); _gl.BindBuffer(GLEnum.ArrayBuffer, _vboHandle); + _gl.CheckGlError(); _gl.BindBuffer(GLEnum.ElementArrayBuffer, _elementsHandle); + _gl.CheckGlError(); _gl.EnableVertexAttribArray(_attribLocationVtxPos); + _gl.CheckGlError(); _gl.EnableVertexAttribArray(_attribLocationVtxUv); + _gl.CheckGlError(); _gl.EnableVertexAttribArray(_attribLocationVtxColor); + _gl.CheckGlError(); _gl.VertexAttribPointer2(_attribLocationVtxPos, 2, GLEnum.Float, false, _sizeOfImDrawVert, _offsetOfImDrawVertPos); + _gl.CheckGlError(); _gl.VertexAttribPointer2(_attribLocationVtxUv, 2, GLEnum.Float, false, _sizeOfImDrawVert, _offsetOfImDrawVertUV); + _gl.CheckGlError(); _gl.VertexAttribPointer2(_attribLocationVtxColor, 4, GLEnum.UnsignedByte, true, _sizeOfImDrawVert, _offsetOfImDrawVertCol); + _gl.CheckGlError(); } - private void RenderCommandList(ImDrawDataPtr drawData, uint framebufferWidth, uint framebufferHeight) + private void RenderCommandList(ref readonly ImDrawDataWrapper drawData, uint framebufferWidth, uint framebufferHeight) { // Will project scissor/clipping rectangles into framebuffer space - var clipOff = drawData.DisplayPos; // (0,0) unless using multi-viewports - var clipScale = drawData.FramebufferScale; // (1,1) unless using retina display which are often (2,2) + ref var clipOff = ref drawData.DisplayPos; // (0,0) unless using multi-viewports + ref var clipScale = ref drawData.FramebufferScale; // (1,1) unless using retina display which are often (2,2) // Render command lists - for (var n = 0; n < drawData.CmdListsCount; n++) + //var cmdListSpan = drawData.CmdLists.AsSpan(); + drawData.GetCmdLists(out var cmdLists); + for (var n = 0; n < cmdLists.Size; n++) { - var cmdList = drawData.CmdLists[n]; + ref var cmdList = ref cmdLists[n]; + cmdList.GetVtxBuffer(out var vtxBuffer); + cmdList.GetIdxBuffer(out var idxBuffer); + cmdList.GetCmdBuffer(out var cmdBuffer); // Upload vertex/index buffers - _gl.BufferData2(GLEnum.ArrayBuffer, (nuint) (cmdList.VtxBuffer.Size * _sizeOfImDrawVert), cmdList.VtxBuffer.Data, GLEnum.StreamDraw); + _gl.BufferData(GLEnum.ArrayBuffer, vtxBuffer.AsSpan(), GLEnum.StreamDraw); _gl.CheckGlError($"Data Vert {n}"); - _gl.BufferData2(GLEnum.ElementArrayBuffer, (nuint) (cmdList.IdxBuffer.Size * sizeof(ushort)), cmdList.IdxBuffer.Data, GLEnum.StreamDraw); + _gl.BufferData(GLEnum.ElementArrayBuffer, idxBuffer.AsSpan(), GLEnum.StreamDraw); _gl.CheckGlError($"Data Idx {n}"); - for (var cmd_i = 0; cmd_i < cmdList.CmdBuffer.Size; cmd_i++) + for (var cmd_i = 0; cmd_i < cmdBuffer.Size; cmd_i++) { - var cmd = cmdList.CmdBuffer[cmd_i]; + ref var cmd = ref cmdBuffer[cmd_i]; if (cmd.UserCallback != IntPtr.Zero) - throw new NotImplementedException(); + ThrowNotImplementedException(); - Vector4 clipRect; + Vector4Ref clipRect; clipRect.X = (cmd.ClipRect.X - clipOff.X) * clipScale.X; clipRect.Y = (cmd.ClipRect.Y - clipOff.Y) * clipScale.Y; clipRect.Z = (cmd.ClipRect.Z - clipOff.X) * clipScale.X; @@ -202,18 +246,22 @@ private void RenderCommandList(ImDrawDataPtr drawData, uint framebufferWidth, ui if (clipRect.X < framebufferWidth && clipRect.Y < framebufferHeight && clipRect is { Z: >= 0.0f, W: >= 0.0f }) { - // Apply scissor/clipping rectangle _gl.Scissor((int) clipRect.X, (int) (framebufferHeight - clipRect.W), (uint) (clipRect.Z - clipRect.X), (uint) (clipRect.W - clipRect.Y)); - _gl.CheckGlError("Scissor"); + _gl.CheckGlError(); - // Bind texture, Draw - _gl.BindTexture(GLEnum.Texture2D, (uint) cmd.TextureId); - _gl.CheckGlError("Texture"); + if (cmd.TextureId != IntPtr.Zero) + { + _gl.BindTexture(GLEnum.Texture2D, (uint) cmd.TextureId); + _gl.CheckGlError(); + } _gl.DrawElementsBaseVertex2(GLEnum.Triangles, cmd.ElemCount, GLEnum.UnsignedShort, (IntPtr) (cmd.IdxOffset * sizeof(ushort)), (int) cmd.VtxOffset); - _gl.CheckGlError("Draw"); + _gl.CheckGlError(); } } } } -} \ No newline at end of file + + private static void ThrowNotImplementedException() => throw new NotImplementedException(); +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.OGL.Setup.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.OGL.Setup.cs new file mode 100644 index 0000000..410221d --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.OGL.Setup.cs @@ -0,0 +1,164 @@ +#if OPENGL +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Memory; + +using ImGui; + +using Silk.NET.Input; +using Silk.NET.OpenGL; +using Silk.NET.Windowing; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; + +internal partial class ImGuiController +{ + private static readonly LiteralSpan VertexShaderUtf8 = "#version 330 core\n"u8 + + """ + layout (location = 0) in vec2 Position; + layout (location = 1) in vec2 UV; + layout (location = 2) in vec4 Color; + uniform mat4 ProjMtx; + out vec2 Frag_UV; + out vec4 Frag_Color; + void main() + { + Frag_UV = UV; + Frag_Color = Color; + gl_Position = ProjMtx * vec4(Position.xy,0,1); + } + """u8 + + "\0"u8; + + private static readonly LiteralSpan FragmentShaderUtf8 = "#version 330 core\n"u8 + + """ + in vec2 Frag_UV; + in vec4 Frag_Color; + uniform sampler2D Texture; + layout (location = 0) out vec4 Out_Color; + void main() + { + Out_Color = Frag_Color * texture(Texture, Frag_UV.st); + } + """u8 + + "\0"u8; + + private readonly GL _gl; + + private Shader _shader = default!; + private Texture _fontTexture = default!; + private int _attribLocationTex, _attribLocationProjMtx; + private uint _attribLocationVtxPos, _attribLocationVtxUv, _attribLocationVtxColor; + private uint _vboHandle, _elementsHandle, _vertexArrayObject; + + public ImGuiController(CmGui imgui, GL gl, IView view, IInputContext input) + { + _imgui = imgui; + _gl = gl; + _view = view; + _input = input; + } + + partial void CreateDeviceObjects() + { + var lastTexture = _gl.GetInteger(GLEnum.TextureBinding2D); + _gl.CheckGlError(); + var lastArrayBuffer = _gl.GetInteger(GLEnum.ArrayBufferBinding); + _gl.CheckGlError(); + var lastVertexArray = _gl.GetInteger(GLEnum.VertexArrayBinding); + _gl.CheckGlError(); + + _shader = new Shader(_gl, VertexShaderUtf8, FragmentShaderUtf8); + + _attribLocationTex = _shader.GetUniformLocation("Texture\0"u8); + _attribLocationProjMtx = _shader.GetUniformLocation("ProjMtx\0"u8); + _attribLocationVtxPos = (uint) _shader.GetAttribLocation("Position\0"u8); + _attribLocationVtxUv = (uint) _shader.GetAttribLocation("UV\0"u8); + _attribLocationVtxColor = (uint) _shader.GetAttribLocation("Color\0"u8); + + _vboHandle = _gl.GenBuffer(); + _gl.CheckGlError(); + _elementsHandle = _gl.GenBuffer(); + _gl.CheckGlError(); + + CreateFontsTexture(); + + _gl.BindTexture(GLEnum.Texture2D, (uint) lastTexture); + _gl.CheckGlError(); + _gl.BindBuffer(GLEnum.ArrayBuffer, (uint) lastArrayBuffer); + _gl.CheckGlError(); + _gl.BindVertexArray((uint) lastVertexArray); + _gl.CheckGlError(); + } + + private void CreateFontsTexture() + { + _imgui.GetIO(out var io); + _imgui.ImFontConfig(out var config); + io.GetFonts(out var fonts); + + config.RasterizerDensity = 2f; // Set your max scale + fonts.AddFontDefault(config, out _); + + // Build texture atlas + // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. + // If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory + fonts.GetTexDataAsRGBA32(out var pixels, out var width, out var height, out _); + + // Upload texture to graphics system + var lastTexture = _gl.GetInteger(GLEnum.TextureBinding2D); + _gl.CheckGlError(); + + _fontTexture = new Texture(_gl, (uint) width, (uint) height, pixels); + _fontTexture.Bind(); + _fontTexture.SetMagFilter(GLEnum.Linear); + _fontTexture.SetMinFilter(GLEnum.Linear); + _gl.PixelStore(GLEnum.UnpackRowLength, 0); + _gl.CheckGlError(); + + fonts.SetTexID((IntPtr) _fontTexture.GlTexture); + + _gl.BindTexture(GLEnum.Texture2D, (uint) lastTexture); + _gl.CheckGlError(); + } + + private void DestroyDeviceObjects() + { + if (_vboHandle != 0) + { + _gl.DeleteBuffer(_vboHandle); + _gl.CheckGlError(); + _vboHandle = 0; + } + + if (_elementsHandle != 0) + { + _gl.DeleteBuffer(_elementsHandle); + _gl.CheckGlError(); + _elementsHandle = 0; + } + } + + private void DestroyShader() + { + _shader.Dispose(); + } + + private void DestroyFontsTexture() + { + _imgui.GetIO(out var io); + io.GetFonts(out var fonts); + fonts.SetTexID(IntPtr.Zero); + + _fontTexture.Dispose(); + } + + public void Reset() + { + DestroyDeviceObjects(); + DestroyShader(); + DestroyFontsTexture(); + + _imgui.DestroyContext(_context); + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Setup.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Setup.cs new file mode 100644 index 0000000..b7f5848 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Setup.cs @@ -0,0 +1,41 @@ +using ImGuiNET; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; + +partial class ImGuiController +{ + public void Initialize() + { + _context = _imgui.CreateContext(); + _imgui.SetCurrentContext(_context); + + _imgui.GetIO(out var io); + io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors; + io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos; + io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset; + io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DpiEnableScaleFonts; + + _windowsWidth = (uint) _view.Size.X; + _windowsHeight = (uint) _view.Size.Y; + + CreateDeviceObjects(); + + SetPerFrameImGuiData(1f / 60f); + + _view.Resize += WindowResized; + + Keyboard.KeyChar += OnKeyChar; + Keyboard.KeyDown += (keyboard, keycode, scancode) => OnKeyEvent(keyboard, keycode, scancode, down: true); + Keyboard.KeyUp += (keyboard, keycode, scancode) => OnKeyEvent(keyboard, keycode, scancode, down: false); + + Mouse.Scroll += MouseOnScroll; + Mouse.MouseMove += MouseOnMouseMove; + Mouse.MouseDown += MouseOnMouseDown; + Mouse.MouseUp += MouseOnMouseUp; + + _frameBegun = true; + _imgui.NewFrame(); + } + + partial void CreateDeviceObjects(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Utils.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Utils.cs similarity index 89% rename from src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Utils.cs rename to src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Utils.cs index 086836e..6d31428 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Utils.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.Utils.cs @@ -2,7 +2,7 @@ using Silk.NET.Input; -namespace BUTR.CrashReport.Renderer.ImGui.Controller; +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; partial class ImGuiController { @@ -113,6 +113,18 @@ partial class ImGuiController Key.F10 => ImGuiKey.F10, Key.F11 => ImGuiKey.F11, Key.F12 => ImGuiKey.F12, - _ => ImGuiKey.None + Key.F13 => ImGuiKey.F13, + Key.F14 => ImGuiKey.F14, + Key.F15 => ImGuiKey.F15, + Key.F16 => ImGuiKey.F16, + Key.F17 => ImGuiKey.F17, + Key.F18 => ImGuiKey.F18, + Key.F19 => ImGuiKey.F19, + Key.F20 => ImGuiKey.F20, + Key.F21 => ImGuiKey.F21, + Key.F22 => ImGuiKey.F22, + Key.F23 => ImGuiKey.F23, + Key.F24 => ImGuiKey.F24, + _ => ImGuiKey.None, }; } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.cs similarity index 59% rename from src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.cs rename to src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.cs index b68ee92..20fa2a4 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/ImGuiController.cs @@ -1,24 +1,23 @@ -using BUTR.CrashReport.Renderer.ImGui.ImGui; +using ImGui; using ImGuiNET; using Silk.NET.Input; -using Silk.NET.OpenGL; using Silk.NET.Windowing; -using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace BUTR.CrashReport.Renderer.ImGui.Controller; +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; -internal partial class ImGuiController : IDisposable +internal partial class ImGuiController { private static readonly uint _sizeOfImDrawVert = (uint) Unsafe.SizeOf(); private static readonly IntPtr _offsetOfImDrawVertPos = Marshal.OffsetOf(nameof(ImDrawVert.pos)); private static readonly IntPtr _offsetOfImDrawVertUV = Marshal.OffsetOf(nameof(ImDrawVert.uv)); private static readonly IntPtr _offsetOfImDrawVertCol = Marshal.OffsetOf(nameof(ImDrawVert.col)); + // ReSharper disable once HeapView.ObjectAllocation private static readonly StandardCursor[] _mouseCursors = [ StandardCursor.Arrow, // ImGuiMouseCursor.Arrow @@ -29,37 +28,21 @@ internal partial class ImGuiController : IDisposable StandardCursor.Arrow, // ImGuiMouseCursor.ResizeNESW StandardCursor.Arrow, // ImGuiMouseCursor.ResizeNWSE StandardCursor.Hand, // ImGuiMouseCursor.Hand - StandardCursor.Arrow // ImGuiMouseCursor.NotAllowed + StandardCursor.Arrow, // ImGuiMouseCursor.NotAllowed ]; private readonly CmGui _imgui; - private readonly GL _gl; - private readonly IntPtr _context; - private readonly ImGui.ImGuiIOPtr _io; + private IntPtr _context; private readonly IView _view; private readonly IInputContext _input; - private IKeyboard Keyboard => _input.Keyboards[0]; - private IMouse Mouse => _input.Mice[0]; + private readonly List _pressedChars = new(32); - private Shader _shader = default!; - private Texture _fontTexture = default!; - private int _attribLocationTex, _attribLocationProjMtx; - private uint _attribLocationVtxPos, _attribLocationVtxUv, _attribLocationVtxColor; - private uint _vboHandle, _elementsHandle, _vertexArrayObject; private uint _windowsWidth, _windowsHeight; - public ImGuiController(CmGui imgui, GL gl, IView view, IInputContext input) - { - _imgui = imgui; - _gl = gl; - _context = _imgui.CreateContext(); - _io = _imgui.GetIO(); - - _view = view; - _input = input; + private bool _frameBegun; - _imgui.SetCurrentContext(_context); - } + private IKeyboard Keyboard => _input.Keyboards[0]; + private IMouse Mouse => _input.Mice[0]; } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/Shader.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Shader.cs similarity index 75% rename from src/BUTR.CrashReport.Renderer.ImGui/Controller/Shader.cs rename to src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Shader.cs index 3243f51..4f84742 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/Shader.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Shader.cs @@ -1,19 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; +using BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Utils; using Silk.NET.OpenGL; -using System; using System.Runtime.CompilerServices; -namespace BUTR.CrashReport.Renderer.ImGui.Controller; +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; internal class Shader { private readonly GL _gl; - private bool _initialized = false; + + private bool _initialized; public uint Program { get; private set; } @@ -25,6 +25,7 @@ public Shader(GL gl, ReadOnlySpan vertexShaderUtf8, ReadOnlySpan fra public void UseShader() { _gl.UseProgram(Program); + _gl.CheckGlError(); } public void Dispose() @@ -32,33 +33,52 @@ public void Dispose() if (_initialized) { _gl.DeleteProgram(Program); + _gl.CheckGlError(); _initialized = false; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUniformLocation(ReadOnlySpan uniformUtf8) => _gl.GetUniformLocation(Program, uniformUtf8); + public int GetUniformLocation(ReadOnlySpan uniformUtf8) + { + var result = _gl.GetUniformLocation(Program, uniformUtf8); + _gl.CheckGlError(); + return result; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetAttribLocation(ReadOnlySpan attribUtf8) => _gl.GetAttribLocation(Program, attribUtf8); + public int GetAttribLocation(ReadOnlySpan attribUtf8) + { + var result = _gl.GetAttribLocation(Program, attribUtf8); + _gl.CheckGlError(); + return result; + } private uint CreateProgram(ReadOnlySpan vertexShaderUtf8, ReadOnlySpan fragmentShaderUtf8) { var program = _gl.CreateProgram(); + _gl.CheckGlError(); var vertexShader = CompileShader(GLEnum.VertexShader, vertexShaderUtf8); var fragmentShader = CompileShader(GLEnum.FragmentShader, fragmentShaderUtf8); _gl.AttachShader(program, vertexShader); + _gl.CheckGlError(); _gl.AttachShader(program, fragmentShader); + _gl.CheckGlError(); _gl.LinkProgram(program); + _gl.CheckGlError(); CheckProgram(program); _gl.DetachShader(program, vertexShader); + _gl.CheckGlError(); _gl.DetachShader(program, fragmentShader); + _gl.CheckGlError(); _gl.DeleteShader(vertexShader); + _gl.CheckGlError(); _gl.DeleteShader(fragmentShader); + _gl.CheckGlError(); _initialized = true; @@ -68,7 +88,8 @@ private uint CreateProgram(ReadOnlySpan vertexShaderUtf8, ReadOnlySpan sourceUtf8) { var shader = _gl.CreateShader(type); - Utf8ZPtr* shaderData = stackalloc Utf8ZPtr[] + _gl.CheckGlError(); + var shaderData = stackalloc Utf8ZPtr[] { new Utf8ZPtr(in sourceUtf8), }; @@ -77,7 +98,9 @@ private unsafe uint CompileShader(GLEnum type, ReadOnlySpan sourceUtf8) sourceUtf8.Length, }; _gl.ShaderSource(shader, (byte**) shaderData, shaderDataLength); + _gl.CheckGlError(); _gl.CompileShader(shader); + _gl.CheckGlError(); CheckShader(shader); return shader; diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/Texture.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Texture.cs similarity index 74% rename from src/BUTR.CrashReport.Renderer.ImGui/Controller/Texture.cs rename to src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Texture.cs index 0720d67..33ea2c1 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/Texture.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Texture.cs @@ -3,9 +3,9 @@ using Silk.NET.OpenGL; -using System; +using System.Runtime.CompilerServices; -namespace BUTR.CrashReport.Renderer.ImGui.Controller; +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; internal enum TextureCoordinate { @@ -27,64 +27,84 @@ internal class Texture : IDisposable public unsafe Texture(GL gl, uint width, uint height, IntPtr data, bool generateMipmaps = false, bool srgb = false) { _gl = gl; - MaxAniso ??= gl.GetFloat(GLEnum.MaxTextureMaxAnisotropy); + if (MaxAniso is null) + { + MaxAniso = gl.GetFloat(GLEnum.MaxTextureMaxAnisotropy); + _gl.CheckGlError(); + } Width = width; Height = height; InternalFormat = srgb ? SizedInternalFormat.Srgb8Alpha8 : SizedInternalFormat.Rgba8; MipmapLevels = generateMipmaps == false ? 1U : (uint) Math.Floor(Math.Log(Math.Max(Width, Height), 2)); GlTexture = _gl.GenTexture(); + _gl.CheckGlError(); Bind(); var pxFormat = PixelFormat.Bgra; _gl.TexImage2D(GLEnum.Texture2D, 0, (int) InternalFormat, width, height, 0, pxFormat, GLEnum.UnsignedByte, data.ToPointer()); + _gl.CheckGlError(); //_gl.TexStorage2D(GLEnum.Texture2D, MipmapLevels, InternalFormat, Width, Height); //_gl.TexSubImage2D(GLEnum.Texture2D, 0, 0, 0, Width, Height, pxFormat, PixelType.UnsignedByte, (void*) data); if (generateMipmaps) + { _gl.GenerateTextureMipmap(GlTexture); + _gl.CheckGlError(); + } SetWrap(TextureCoordinate.S, TextureWrapMode.Repeat); SetWrap(TextureCoordinate.T, TextureWrapMode.Repeat); - _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLevel, MipmapLevels - 1); + var level = MipmapLevels - 1; + _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLevel, in level); + _gl.CheckGlError(); } public void Bind() { _gl.BindTexture(GLEnum.Texture2D, GlTexture); + _gl.CheckGlError(); } public void SetMinFilter(GLEnum filter) { - _gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMinFilter, (int) filter); + _gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMinFilter, in Unsafe.As(ref filter)); + _gl.CheckGlError(); } public void SetMagFilter(GLEnum filter) { - _gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMagFilter, (int) filter); + _gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMagFilter, in Unsafe.As(ref filter)); + _gl.CheckGlError(); } public void SetAnisotropy(float level) { _gl.TexParameter(GLEnum.Texture2D, TextureParameterName.TextureMaxAnisotropy, Util.Clamp(level, 1, MaxAniso.GetValueOrDefault())); + _gl.CheckGlError(); } public void SetLod(int @base, int min, int max) { - _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureLodBias, @base); - _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMinLod, min); - _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLod, max); + _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureLodBias, in @base); + _gl.CheckGlError(); + _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMinLod, in min); + _gl.CheckGlError(); + _gl.TexParameterI(GLEnum.Texture2D, TextureParameterName.TextureMaxLod, in max); + _gl.CheckGlError(); } public void SetWrap(TextureCoordinate coord, TextureWrapMode mode) { - _gl.TexParameterI(GLEnum.Texture2D, (TextureParameterName) coord, (int) mode); + _gl.TexParameterI(GLEnum.Texture2D, (TextureParameterName) coord, in Unsafe.As(ref mode)); + _gl.CheckGlError(); } public void Dispose() { _gl.DeleteTexture(GlTexture); + _gl.CheckGlError(); } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/Util.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Util.cs similarity index 50% rename from src/BUTR.CrashReport.Renderer.ImGui/Controller/Util.cs rename to src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Util.cs index 0135a9f..bac9fad 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/Util.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Controller/Util.cs @@ -1,15 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Silk.NET.Core.Attributes; -using Silk.NET.Core.Native; using Silk.NET.OpenGL; -using System; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Numerics; -namespace BUTR.CrashReport.Renderer.ImGui.Controller; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; internal static class Util { @@ -17,15 +17,17 @@ internal static class Util public static float Clamp(float value, float min, float max) => value < min ? min : value > max ? max : value; [Conditional("DEBUG")] - public static void CheckGlError(this GL gl, string title) + public static void CheckGlError(this GL gl, string title = "", [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var error = gl.GetError(); - if (error != GLEnum.NoError) + while (error != GLEnum.NoError) { - Debug.Print($"{title}: {error}"); + Debug.Print($"{filePath} {memberName}:{lineNumber - 1} - [{title}: {error}]"); + error = gl.GetError(); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void UniformMatrix4x4(this GL gl, int uniformLocation, ref readonly Matrix4x4 value) { var data = stackalloc float[16] @@ -38,18 +40,15 @@ public static unsafe void UniformMatrix4x4(this GL gl, int uniformLocation, ref gl.UniformMatrix4(uniformLocation, 1, false, data); } - public static unsafe void VertexAttribPointer2(this GL gl, [Flow(FlowDirection.In)] uint index, [Flow(FlowDirection.In)] int size, [Flow(FlowDirection.In)] GLEnum type, [Flow(FlowDirection.In)] bool normalized, [Flow(FlowDirection.In)] uint stride, [Flow(FlowDirection.In)] IntPtr pointer) - { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void VertexAttribPointer2(this GL gl, uint index, int size, GLEnum type, bool normalized, uint stride, IntPtr pointer) => gl.VertexAttribPointer(index, size, type, normalized, stride, pointer.ToPointer()); - } - public static unsafe void BufferData2(this GL gl, [Flow(FlowDirection.In)] GLEnum target, [Flow(FlowDirection.In)] UIntPtr size, [Count(Parameter = "size"), Flow(FlowDirection.In)] IntPtr data, [Flow(FlowDirection.In)] GLEnum usage) - { - gl.BufferData(target, size, data.ToPointer(), usage); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void BufferData(this GL gl, GLEnum target, Span span, GLEnum usage) => + gl.BufferData(target, (nuint) (span.Length * Unsafe.SizeOf()), Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)), usage); - public static unsafe void DrawElementsBaseVertex2(this GL gl, [Flow(FlowDirection.In)] GLEnum mode, [Flow(FlowDirection.In)] uint count, [Flow(FlowDirection.In)] GLEnum type, [Count(Computed = "count, type"), Flow(FlowDirection.In)] IntPtr indices, [Flow(FlowDirection.In)] int basevertex) - { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void DrawElementsBaseVertex2(this GL gl, GLEnum mode, uint count, GLEnum type, IntPtr indices, int basevertex) => gl.DrawElementsBaseVertex(mode, count, type, indices.ToPointer(), basevertex); - } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/CrashReportImGui.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/CrashReportImGui.cs new file mode 100644 index 0000000..be06f8b --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/CrashReportImGui.cs @@ -0,0 +1,213 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Native; +using BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; +using BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Controller; +using BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Utils; +using BUTR.CrashReport.Renderer.ImGui.Renderer; + +using ImGui; +using ImGui.Structures; + +using Silk.NET.Core.Loader; +using Silk.NET.Input; +using Silk.NET.Maths; +using Silk.NET.OpenGL; +using Silk.NET.Windowing; + +using System.Numerics; +using System.Runtime.InteropServices; + +[assembly: DelegateLoader(typeof(CmGui), false)] + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; + +internal class CmGuiLoader : IDisposable +{ + private const string LibWindows = "cimgui.dll"; + private const string LibLinux = "libcimgui.so"; + private const string LibOSX = "libcimgui.dylib"; + + private LibraryLoader? Loader; + private IntPtr? Library { get; set; } + public IntPtr LoadFunctionPointer(string funcionName) + { + Loader ??= LibraryLoader.GetPlatformDefaultLoader(); + Library ??= Loader.LoadNativeLibrary([LibWindows, LibLinux, LibOSX]); + + return Loader.LoadFunctionPointer(Library.GetValueOrDefault(), funcionName); + } + + public void Dispose() + { + Loader?.FreeNativeLibrary(Library.GetValueOrDefault()); + } +} + +public sealed class CrashReportImGui : IDisposable +{ + private static bool IsGlfwWindow(IView window) => window.GetType().Name.Contains("Glfw"); + private static bool IsSdlWindow(IView window) => window.GetType().Name.Contains("Sdl"); + + private readonly WindowProvider _windowProvider; + private readonly GlfwWindowOptions? _glfwWindowOptions; + private readonly SdlWindowOptions? _sdlWindowOptions; + private readonly CmGuiLoader _imguiLoader = new(); + private readonly CmGui _imgui; + + private Action? _onClose; + + public CrashReportImGui(INativeLoaderUtilities nativeLoaderUtilities, WindowProvider windowProvider = WindowProvider.None, params IWindowOptions[] windowOptions) + { + if (windowProvider == WindowProvider.None) + windowProvider = WindowProvider.SDL; + + _windowProvider = windowProvider; + + if (PathResolver.Default is DefaultPathResolver pr) + pr.Resolvers.Add(path => nativeLoaderUtilities.GetNativeLibrariesFolderPath().Select(x => Path.Combine(x, path))); + + _glfwWindowOptions = windowOptions.OfType().FirstOrDefault(); + _sdlWindowOptions = windowOptions.OfType().FirstOrDefault(); + + _imgui = new CmGui(); + _imgui.LoadFrom(_imguiLoader.LoadFunctionPointer); + + if (_windowProvider.IsSet(WindowProvider.Glfw)) + GlfwUtils.Setup(_glfwWindowOptions); + + if (_windowProvider.IsSet(WindowProvider.SDL)) + SdlUtils.Setup(_sdlWindowOptions); + } + + public void Close() + { + _onClose?.Invoke(); + } + + public void ShowAndWait(CrashReportModel crashReportModel, IList logSources, ICrashReportRendererUtilities utilities) + { + // ReSharper disable once AccessToModifiedClosure + var imGuiRenderer = new ImGuiRenderer, ImGuiListClipperWrapper>( + _imgui, _imgui, _imgui, _imgui, _imgui, _imgui, + crashReportModel, logSources, utilities, () => _onClose?.Invoke()); + + // Looks like the compatibility profile uses less CPU cycles compared to core + // But only core is supported on macOS + var profile = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? ContextProfile.Core : ContextProfile.Compatability; + var window = Window.Create(WindowOptions.Default with + { + Title = $"{crashReportModel.Metadata.GameName} Crash Report", + VSync = true, + IsEventDriven = false, + TransparentFramebuffer = false, + WindowBorder = WindowBorder.Resizable, + API = GraphicsAPI.Default with + { + Profile = profile, + }, + }); + //window.Title = $"{crashReportModel.Metadata.GameName} Crash Report ({window.GetType().Name})"; + _onClose = window.Close; + + window.Initialize(); + + var gl = window.CreateOpenGL(); + var inputContext = window.CreateInput(); + var controller = new ImGuiController(_imgui, gl, window, inputContext); + + controller.Initialize(); + + // ReSharper disable AccessToDisposedClosure + void OnWindowOnResize(Vector2D newSize) + { + gl.Viewport(newSize); + } + // ReSharper restore AccessToDisposedClosure + window.Resize += OnWindowOnResize; + + // ReSharper disable AccessToDisposedClosure + void OnWindowOnFramebufferResize(Vector2D newSize) + { + gl.Viewport(newSize); + } + // ReSharper restore AccessToDisposedClosure + window.FramebufferResize += OnWindowOnFramebufferResize; + + // ReSharper disable AccessToDisposedClosure + void OnWindowOnUpdate(double delta) + { + controller.Update(delta); + } + // ReSharper restore AccessToDisposedClosure + window.Update += OnWindowOnUpdate; + + // ReSharper disable AccessToDisposedClosure + void OnWindowOnRender(double delta) + { + gl.Clear(ClearBufferMask.ColorBufferBit); + + imGuiRenderer.Render(); + + controller.Render(); + } + // ReSharper restore AccessToDisposedClosure + window.Render += OnWindowOnRender; + + if (window.Native is { } nativeWindow) + DarkThemeUtils.SetDarkModeTitleBar(nativeWindow); + + if (IsGlfwWindow(window)) + GlfwUtils.Init(window, _glfwWindowOptions); + + if (IsSdlWindow(window)) + SdlUtils.Init(window, _sdlWindowOptions); + + //window.GLContext?.SwapInterval(0); + + static void DoLoop(IWindow window) + { + window.Run(() => + { + if (!window.IsInitialized || !window.IsVisible || window.IsClosing) + return; + + window.DoEvents(); + + window.DoUpdate(); + + window.DoRender(); + }); + window.DoEvents(); + } + DoLoop(window); + + if (!window.IsClosing) + window.Close(); + + window.Resize -= OnWindowOnResize; + window.FramebufferResize -= OnWindowOnFramebufferResize; + window.Update -= OnWindowOnUpdate; + window.Render -= OnWindowOnRender; + + controller.Reset(); + + inputContext.Dispose(); + gl.PurgeEntryPoints(); + gl.Dispose(); + + window.Dispose(); // Calls Reset + } + + public void Dispose() + { + _imgui.Dispose(); + _imguiLoader.Dispose(); + + if (_windowProvider.IsSet(WindowProvider.Glfw)) + GlfwUtils.Reset(_glfwWindowOptions); + + if (_windowProvider.IsSet(WindowProvider.SDL)) + SdlUtils.Reset(_sdlWindowOptions); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Extensions/AssemblyExtensions.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Extensions/AssemblyExtensions.cs new file mode 100644 index 0000000..b8c4d07 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Extensions/AssemblyExtensions.cs @@ -0,0 +1,12 @@ +using System.Reflection; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Extensions; + +internal static class AssemblyExtensions +{ + public static unsafe ReadOnlySpan GetManifestResourceStreamAsSpan(this Assembly a, string name) + { + var stream = (UnmanagedMemoryStream) a.GetManifestResourceStream(name)!; + return new ReadOnlySpan(stream.PositionPointer, checked((int) stream.Length)); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/GlfwWindowOptions.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/GlfwWindowOptions.cs new file mode 100644 index 0000000..f12a53e --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/GlfwWindowOptions.cs @@ -0,0 +1,6 @@ +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; + +public class GlfwWindowOptions : IWindowOptions +{ + public bool ResetProvider { get; set; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/IWindowOptions.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/IWindowOptions.cs new file mode 100644 index 0000000..fd1a2f6 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/IWindowOptions.cs @@ -0,0 +1,6 @@ +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; + +public interface IWindowOptions +{ + bool ResetProvider { get; set; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/SdlWindowOptions.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/SdlWindowOptions.cs new file mode 100644 index 0000000..b722661 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Options/SdlWindowOptions.cs @@ -0,0 +1,6 @@ +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; + +public class SdlWindowOptions : IWindowOptions +{ + public bool ResetProvider { get; set; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/DarkThemeUtils.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/DarkThemeUtils.cs new file mode 100644 index 0000000..36fda2c --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/DarkThemeUtils.cs @@ -0,0 +1,38 @@ +using Silk.NET.Core.Contexts; + +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Utils; + +internal static class DarkThemeUtils +{ + private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; + private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + private const int S_OK = 0; + + [DllImport("dwmapi", SetLastError = true)] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); + + [DllImport("uxtheme.dll", EntryPoint = "#132")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool ShouldAppsUseDarkMode(); + + private static bool IsWindows10OrGreater(int build = -1) => Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build; + private static bool IsDarkModeSupported { get; } = IsWindows10OrGreater(17763); + + public static void SetDarkModeTitleBar(INativeWindow nativeWindow) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && nativeWindow.Win32 is { Hwnd: var windowHandle }) + { + if (IsDarkModeSupported && ShouldAppsUseDarkMode()) + { + var attr = IsWindows10OrGreater(18985) ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; + var attrValue = 1; + if (DwmSetWindowAttribute(windowHandle, attr, ref attrValue, sizeof(int)) is var hResult and not S_OK) + throw new Win32Exception(hResult); + } + } + + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/GlfwUtils.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/GlfwUtils.cs new file mode 100644 index 0000000..8969caa --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/GlfwUtils.cs @@ -0,0 +1,58 @@ +using Silk.NET.Core; +using Silk.NET.GLFW; +using Silk.NET.Input.Glfw; +using Silk.NET.Windowing; +using Silk.NET.Windowing.Glfw; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Utils; + +internal static class GlfwUtils +{ + public static void Setup(GlfwWindowOptions? glfwWindowOptions) + { + GlfwWindowing.RegisterPlatform(); + GlfwInput.RegisterPlatform(); + GlfwWindowing.Use(); + _ = GlfwProvider.GLFW.Value; + } + + private static void SetWindowIcon(IWindow window) + { + var iconPaths = typeof(CrashReportImGui).Assembly.GetManifestResourceNames().Where(x => x.StartsWith("resources\\icon_") && x.EndsWith(".bin")).ToArray().AsSpan(); + var icons = new RawImage[iconPaths.Length]; + + for (var i = 0; i < iconPaths.Length; i++) + { + var iconPath = iconPaths[i]; + using var stream = typeof(CrashReportImGui).Assembly.GetManifestResourceStream(iconPath)!; + using var reader = new BinaryReader(stream); + + var size = int.Parse(iconPath.Split('_')[1].Split('.')[0]); + icons[i] = new(size, size, new Memory(reader.ReadBytes((int) stream.Length))); + } + + window.SetWindowIcon(icons); + } + + public static void Init(IWindow window, GlfwWindowOptions? glfwWindowOptions) + { + SetWindowIcon(window); + } + + public static void Reset(GlfwWindowOptions? glfwWindowOptions) + { + if (glfwWindowOptions is null || glfwWindowOptions.ResetProvider) + { + GlfwProvider.GLFW.Value.Terminate(); + GlfwProvider.GLFW.Value.PurgeEntryPoints(); + GlfwProvider.GLFW.Value.Dispose(); + // When Silk.NET checks whether Glfw is applicable, it creates the context (and loads the native lib) + // without unloading it. So we need to dispose it twice, since one dispose means one free call + try + { + GlfwProvider.GLFW.Value.Dispose(); + } + catch { /* ignore */ } + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/SdlUtils.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/SdlUtils.cs new file mode 100644 index 0000000..6b29fde --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/SdlUtils.cs @@ -0,0 +1,62 @@ +using BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Extensions; + +using Silk.NET.Input.Sdl; +using Silk.NET.SDL; +using Silk.NET.Windowing; +using Silk.NET.Windowing.Sdl; + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Utils; + +internal static class SdlUtils +{ + internal static void Setup(SdlWindowOptions? sdlWindowOptions) + { + SdlWindowing.RegisterPlatform(); + SdlInput.RegisterPlatform(); + SdlWindowing.Use(); + _ = SdlProvider.SDL.Value; + } + + private static unsafe void SetWindowIcon(IWindow window) + { + var sdl = Sdl.GetApi(); + var sdlWindow = (Silk.NET.SDL.Window*) (window.Native?.Sdl ?? IntPtr.Zero); + + var iconSpan = typeof(CrashReportImGui).Assembly.GetManifestResourceStreamAsSpan("resources\\icon_128.bin"); + var surface = sdl.CreateRGBSurfaceFrom + ( + Unsafe.AsPointer(ref MemoryMarshal.GetReference(iconSpan)), + 128, + 128, + 32, 32 / 8 * 128, + 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 + ); + sdl.SetWindowIcon(sdlWindow, surface); + sdl.FreeSurface(surface); + } + + internal static void Init(IWindow window, SdlWindowOptions? sdlWindowOptions) + { + SetWindowIcon(window); + } + + internal static void Reset(SdlWindowOptions? sdlWindowOptions) + { + if (sdlWindowOptions == null || sdlWindowOptions.ResetProvider) + { + SdlProvider.SDL.Value.Quit(); + SdlProvider.SDL.Value.PurgeEntryPoints(); + SdlProvider.SDL.Value.Dispose(); + // When Silk.NET checks whether Sdl is applicable, it creates the context (and loads the native lib) + // without unloading it. So we need to dispose it twice, since one dispose means one free call + try + { + SdlProvider.SDL.Value.Dispose(); + } + catch { /* ignore */ } + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/Utf8ZPtr.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/Utf8ZPtr.cs new file mode 100644 index 0000000..2f062ca --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/Utils/Utf8ZPtr.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui.Utils; + +/// +/// Null-terminated UTF-8 string pointer. +/// +internal readonly unsafe ref struct Utf8ZPtr +{ + private readonly void* NativePtr; + public ref readonly byte Data => ref Unsafe.AsRef(NativePtr); + + public Utf8ZPtr(ref byte reference) { NativePtr = Unsafe.AsPointer(ref reference); } + public Utf8ZPtr(ref readonly ReadOnlySpan span) : this(ref MemoryMarshal.GetReference(span)) { } + public Utf8ZPtr(ref readonly Span span) : this(ref MemoryMarshal.GetReference(span)) { } + public Utf8ZPtr(ref readonly byte[] array) : this(ref MemoryMarshal.GetReference(array.AsSpan())) { } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/WindowProvider.cs b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/WindowProvider.cs new file mode 100644 index 0000000..5eb5a7d --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/WindowProvider.cs @@ -0,0 +1,14 @@ +namespace BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; + +[Flags] +public enum WindowProvider +{ + None = 0, + Glfw = 1, + SDL = 2, +} + +internal static class WindowProviderExtensions +{ + public static bool IsSet(this WindowProvider value, WindowProvider flag) => (value & flag) != 0; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon.png b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon.png new file mode 100644 index 0000000..7a3d235 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon.png differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_128.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_128.bin new file mode 100644 index 0000000..281df87 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_128.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_16.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_16.bin new file mode 100644 index 0000000..140c793 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_16.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_18.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_18.bin new file mode 100644 index 0000000..1c89738 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_18.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_20.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_20.bin new file mode 100644 index 0000000..4113512 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_20.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_22.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_22.bin new file mode 100644 index 0000000..8a1019c Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_22.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_24.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_24.bin new file mode 100644 index 0000000..56d637c Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_24.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_28.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_28.bin new file mode 100644 index 0000000..f476b05 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_28.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_32.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_32.bin new file mode 100644 index 0000000..e8b463d Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_32.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_48.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_48.bin new file mode 100644 index 0000000..fbfc145 Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_48.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_64.bin b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_64.bin new file mode 100644 index 0000000..ec45cdf Binary files /dev/null and b/src/BUTR.CrashReport.Renderer.ImGui.Silk.NET/resources/icon_64.bin differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/BUTR.CrashReport.Renderer.ImGui.Tool.csproj b/src/BUTR.CrashReport.Renderer.ImGui.Tool/BUTR.CrashReport.Renderer.ImGui.Tool.csproj new file mode 100644 index 0000000..311d902 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/BUTR.CrashReport.Renderer.ImGui.Tool.csproj @@ -0,0 +1,78 @@ + + + + Exe + net8.0 + enable + latest + true + + true + + + + + + + + + + butr_crashreport_imgui + true + true + + + + BUTR.CrashReport.Renderer.ImGui.Tool + $(Version) + BUTR.CrashReport.Renderer.ImGui.Tool + Aragas + BUTR + Provides an ImGUI renderer for crash reports + Copyright © 2023-2024 Bannerlord's Unofficial Tools & Resources + MIT + butr crash report html zip imgui + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/CrashReportRendererUtilities.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CrashReportRendererUtilities.cs new file mode 100644 index 0000000..116c020 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CrashReportRendererUtilities.cs @@ -0,0 +1,185 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.Html; +using BUTR.CrashReport.Renderer.ImGui.Tool.Models; + +using NativeFileDialogSharp; + +using Silk.NET.SDL; + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; + +using TextCopy; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +internal sealed class CrashReportRendererUtilities : ICrashReportRendererUtilities +{ + public bool IsDefaultDarkMode => true; + + private readonly string? _uploadUrl; + private readonly int _tenant; + + private readonly CrashReportRendererCapabilities _capabilities = +#if WINDOWS + CrashReportRendererCapabilities.Dialogs | +#endif + CrashReportRendererCapabilities.CopyAsHtml | + CrashReportRendererCapabilities.CloseAndContinue; + + public CrashReportRendererCapabilities Capabilities => _capabilities; + + public CrashReportRendererUtilities(string? uploadUrl, int tenant, CrashReportModel model, LogSourceModel[] logs) + { + _uploadUrl = uploadUrl; + _tenant = tenant; + + if (!string.IsNullOrEmpty(_uploadUrl) && tenant > 0) + { + _capabilities |= CrashReportRendererCapabilities.Upload; + } + + if (!string.IsNullOrEmpty(model.Metadata.LoaderPluginProviderName)) + { + _capabilities |= CrashReportRendererCapabilities.PluginLoader; + } + + if (logs.Length > 0) + { + _capabilities |= CrashReportRendererCapabilities.Logs; + } + + /* + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _capabilities |= CrashReportRendererCapabilities.Dialogs; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && RuntimeInformation.ProcessArchitecture == Architecture.X64) + { + _capabilities |= CrashReportRendererCapabilities.Dialogs; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && RuntimeInformation.ProcessArchitecture == Architecture.X64) + { + _capabilities |= CrashReportRendererCapabilities.Dialogs; + } + */ + } + + private CrashUploaderResult Upload(CrashReportModel crashReportModel, IEnumerable logSources) + { + try + { + using var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("User-Agent", $"{crashReportModel.Metadata.GameName} CrashUploader v{typeof(CrashReportRendererUtilities).Assembly.GetName().Version}"); + httpClient.DefaultRequestHeaders.Add("Tenant", _tenant.ToString()); + httpClient.DefaultRequestHeaders.Add("CrashReportVersion", crashReportModel.Version.ToString()); + + using var memoryStream = new MemoryStream(); + using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true)) + { + JsonSerializer.Serialize(gZipStream, new CrashReportUploadBody(crashReportModel, logSources), CustomJsonSerializerContext.Default.CrashReportUploadBody); + memoryStream.Seek(0, SeekOrigin.Begin); + } + + using var content = new StreamContent(memoryStream); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + content.Headers.ContentEncoding.Add("gzip"); + content.Headers.ContentEncoding.Add("deflate"); + + using var request = new HttpRequestMessage(HttpMethod.Post, _uploadUrl); + request.Content = content; + using var response = httpClient.Send(request); + if (response.StatusCode is not HttpStatusCode.OK and not HttpStatusCode.Created) + return CrashUploaderResult.WrongStatusCode(response.StatusCode); + + using var stream = response.Content.ReadAsStream(); + using var responseReader = new StreamReader(stream); + var result = responseReader.ReadLine(); + if (string.IsNullOrEmpty(result)) + return CrashUploaderResult.ResponseUrlIsNullOrEmpty(); + return CrashUploaderResult.Success(result); + } + catch (Exception e) + { + return CrashUploaderResult.FailedWithException(e.ToString()); + } + } + + private (bool IsSuccessful, string Result) UploadInternal(CrashReportModel crashReport, ICollection logSources) + { + var result = Upload(crashReport, (IEnumerable) logSources); + return result.Status switch + { + CrashUploaderStatus.Success => (true, result.Url ?? string.Empty), + CrashUploaderStatus.ResponseIsNotHttpWebResponse => (false, $"Status: {result.Status}"), + CrashUploaderStatus.ResponseStreamIsNull => (false, $"Status: {result.Status}"), + CrashUploaderStatus.WrongStatusCode => (false, $"Status: {result.Status}\nStatusCode: {result.StatusCode}"), + CrashUploaderStatus.FailedWithException => (false, $"Status: {result.Status}\nException: {result.Exception}"), + _ => (false, "Unknown error"), + }; + } + + public unsafe void Upload(CrashReportModel crashReport, ICollection logSources) + { + try + { + var sdl = SdlProvider.SDL.Value; + var window = sdl.GLGetCurrentWindow(); + var (isSuccessful, result) = UploadInternal(crashReport, logSources); + if (isSuccessful) + { + ClipboardService.SetText(result); + sdl.ShowSimpleMessageBox(0x00000040, "Success!\0"u8, $""" + Report available at + {result} + The url was copied to the clipboard! + """, window); + } + else + { + sdl.ShowSimpleMessageBox(0x00000040, "Success!\0"u8, $""" + The crash uploader could not upload the report! + Please report this to the mod developers! + {result} + """, window); + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + public void CopyAsHtml(CrashReportModel crashReport, ICollection logSources) + { + var reportAsHtml = CrashReportHtml.Build(crashReport, logSources); + ClipboardService.SetText(reportAsHtml); + } + + public void SaveAsHtml(CrashReportModel crashReport, ICollection logSources, bool addMiniDump, bool addLatestSave, bool addScreenshots, Stream stream) + { + var reportAsHtml = CrashReportHtml.Build(crashReport, logSources); + CreatorHtml.Create(crashReport, reportAsHtml, stream); + } + + public void SaveAsZip(CrashReportModel crashReport, ICollection logSources, Stream stream) + { + CreatorZip.Create(crashReport, logSources, stream); + } + + public Stream SaveFileDialog(string filter, string defaultPath) + { + if (Dialog.FileSave(filter, Path.GetDirectoryName(defaultPath)) is { IsOk: true } result) + return new FileStream(result.Path, FileMode.Create, FileAccess.Write); + return Stream.Null; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/CreatorHtml.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CreatorHtml.cs new file mode 100644 index 0000000..43391d4 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CreatorHtml.cs @@ -0,0 +1,41 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.Html; + +using System.IO; +using System.IO.Compression; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +internal static class CreatorHtml +{ + private static string CompressJson(string jsonModel) + { + using var compressedBase64Stream = new MemoryStream(); + + using (var base64Stream = new CryptoStream(compressedBase64Stream, new ToBase64Transform(), CryptoStreamMode.Write, true)) + using (var compressorStream = new GZipStream(base64Stream, CompressionLevel.Optimal, true)) + using (var streamWriter = new StreamWriter(compressorStream, Encoding.UTF8, 1024, true)) + { + streamWriter.Write(jsonModel); + } + + using (var streamReader = new StreamReader(compressedBase64Stream)) + { + compressedBase64Stream.Seek(0, SeekOrigin.Begin); + return streamReader.ReadToEnd(); + } + } + + public static void Create(CrashReportModel crashReport, string html, Stream stream) + { + var json = JsonSerializer.Serialize(crashReport, CustomJsonSerializerContext.Default.CrashReportModel); + + var report = CrashReportHtml.AddData(html, CompressJson(json)); + + using var streamWriter = new StreamWriter(stream); + streamWriter.Write(report); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/CreatorZip.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CreatorZip.cs new file mode 100644 index 0000000..b66c27e --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CreatorZip.cs @@ -0,0 +1,22 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.Zip; + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +internal static class CreatorZip +{ + public static void Create(CrashReportModel crashReport, ICollection logSources, Stream stream) + { + using var crashReportStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(crashReport, CustomJsonSerializerContext.Default.CrashReportModel))); + using var logsStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(logSources, CustomJsonSerializerContext.Default.LogSourceModelArray))); + + using var archiveSteam = CrashReportZip.Build(crashReportStream, logsStream, Stream.Null, Stream.Null, Stream.Null); + archiveSteam.Seek(0, SeekOrigin.Begin); + archiveSteam.CopyTo(stream); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/CustomJsonSerializerContext.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CustomJsonSerializerContext.cs new file mode 100644 index 0000000..33e6064 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/CustomJsonSerializerContext.cs @@ -0,0 +1,12 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.ImGui.Tool.Models; + +using System.Text.Json.Serialization; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)] +[JsonSerializable(typeof(CrashReportUploadBody))] +[JsonSerializable(typeof(CrashReportModel))] +[JsonSerializable(typeof(LogSourceModel[]))] +internal partial class CustomJsonSerializerContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashReportUploadBody.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashReportUploadBody.cs new file mode 100644 index 0000000..4368e2e --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashReportUploadBody.cs @@ -0,0 +1,7 @@ +using BUTR.CrashReport.Models; + +using System.Collections.Generic; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool.Models; + +internal sealed record CrashReportUploadBody(CrashReportModel CrashReport, IEnumerable LogSources); \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashUploaderResult.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashUploaderResult.cs new file mode 100644 index 0000000..b1bead5 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashUploaderResult.cs @@ -0,0 +1,21 @@ +using System.Net; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool.Models; + +internal record CrashUploaderResult +{ + public static CrashUploaderResult Success(string url) => new(CrashUploaderStatus.Success) { Url = url }; + public static CrashUploaderResult WrongStatusCode(HttpStatusCode statusCode) => new(CrashUploaderStatus.WrongStatusCode) { StatusCode = statusCode }; + public static CrashUploaderResult ResponseUrlIsNullOrEmpty() => new(CrashUploaderStatus.UrlIsNullOrEmpty); + public static CrashUploaderResult FailedWithException(string exception) => new(CrashUploaderStatus.FailedWithException) { Exception = exception }; + + public CrashUploaderStatus Status { get; } + public string? Url { get; private set; } + public HttpStatusCode? StatusCode { get; private set; } + public string? Exception { get; private set; } + + private CrashUploaderResult(CrashUploaderStatus status) + { + Status = status; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashUploaderStatus.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashUploaderStatus.cs new file mode 100644 index 0000000..d1a0639 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Models/CrashUploaderStatus.cs @@ -0,0 +1,11 @@ +namespace BUTR.CrashReport.Renderer.ImGui.Tool.Models; + +internal enum CrashUploaderStatus +{ + Success, + ResponseIsNotHttpWebResponse, + WrongStatusCode, + ResponseStreamIsNull, + FailedWithException, + UrlIsNullOrEmpty, +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/NativeLoaderUtilities.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/NativeLoaderUtilities.cs new file mode 100644 index 0000000..8f83ae9 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/NativeLoaderUtilities.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +internal sealed class NativeLoaderUtilities : INativeLoaderUtilities +{ + public IEnumerable GetNativeLibrariesFolderPath() + { + try + { + var runtimeDirectory = Path.Combine(Environment.CurrentDirectory, "runtimes"); + var libraries = Directory.EnumerateFiles(runtimeDirectory, "*.*", SearchOption.AllDirectories); + var folders = libraries.Select(x => Path.GetDirectoryName((string?) x)!).Distinct(); + return folders; + } + catch (Exception) + { + return []; + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/Program.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Program.cs new file mode 100644 index 0000000..ccf7403 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/Program.cs @@ -0,0 +1,115 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.ImGui.Implementation.CImGui; + +using HtmlAgilityPack; + +using System; +using System.IO; +using System.IO.Compression; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +public static class Program +{ + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetProcessDPIAware(); + + private static async Task TryDeserializeAsync(Stream stream, JsonTypeInfo typeInfo) + { + try + { + return await JsonSerializer.DeserializeAsync(stream, typeInfo); + } + catch (JsonException) + { + return default; + } + } + + public static async Task Main(string[] args) + { + if (args.Length == 0) + { + Console.WriteLine("Usage: CrashReport.Renderer.ImGui.Tool "); + return; + } + + var crashReportPath = args[0].Trim('"'); + var url = args.Length > 1 ? args[1] : null; + var tenant = args.Length > 2 ? int.TryParse(args[2], out var tenantVar) ? tenantVar : 0 : 0; + + if (!File.Exists(crashReportPath)) + { + Console.WriteLine("File does not exist."); + return; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + SetProcessDPIAware(); + } + + // Don't use the Custom if you plan to serialize back to JSON, since we loose data + var contract = CustomJsonSerializerContext.Default; + + using var crashReportImGui = new CrashReportImGui(new NativeLoaderUtilities(), WindowProvider.Glfw); + void OnConsoleOnCancelKeyPress(object? o, ConsoleCancelEventArgs consoleCancelEventArgs) => crashReportImGui.Close(); + Console.CancelKeyPress += OnConsoleOnCancelKeyPress; + switch (Path.GetExtension(crashReportPath)) + { + case ".zip": + { + using var archive = new ZipArchive(File.OpenRead(crashReportPath), ZipArchiveMode.Read, false); + + await using var jsonStream = archive.GetEntry("crashreport.json").TryOpen(); + await using var logsStream = archive.GetEntry("logs.json").TryOpen(); + if (jsonStream == Stream.Null) return; + + var crashReport = await TryDeserializeAsync(jsonStream, contract.CrashReportModel); + var logs = await TryDeserializeAsync(logsStream, contract.LogSourceModelArray) ?? []; + + crashReportImGui.ShowAndWait(crashReport!, logs, new CrashReportRendererUtilities(url, tenant, crashReport!, logs)); + break; + } + case ".json": + { + var crashReport = await TryDeserializeAsync(File.OpenRead(crashReportPath), contract.CrashReportModel); + var logs = Array.Empty(); + + crashReportImGui.ShowAndWait(crashReport!, logs, new CrashReportRendererUtilities(url, tenant, crashReport!, logs)); + break; + } + case ".html": + { + var document = new HtmlDocument(); + document.Load(File.OpenRead(crashReportPath)); + var gzipBase64Json = document.GetElementbyId("json-model-data").InnerText.Trim('\u200B', '\r', '\n', '\r', '\t', ' '); + if (string.IsNullOrEmpty(gzipBase64Json)) return; + + // Ideally, don't load the HTML at all, just read the file and extract the JSON from it. + await using var gzipBase64Stream = gzipBase64Json.AsStream(); + using var base64Transform = new FromBase64Transform(); + await using var gzipStream = new CryptoStream(gzipBase64Stream, base64Transform, CryptoStreamMode.Read, false); + await using var jsonStream = new GZipStream(gzipStream, CompressionMode.Decompress, false); + + var crashReport = await TryDeserializeAsync(jsonStream, contract.CrashReportModel); + var logs = Array.Empty(); + + if (crashReport is null) return; + + crashReportImGui.ShowAndWait(crashReport, logs, new CrashReportRendererUtilities(url, tenant, crashReport, logs)); + break; + } + default: + Console.WriteLine("Invalid file extension."); + break; + } + Console.CancelKeyPress -= OnConsoleOnCancelKeyPress; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/TextExtensions.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/TextExtensions.cs new file mode 100644 index 0000000..0431de1 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/TextExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; +using System.Text; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +internal static class TextExtensions +{ + public static Encoding PlatformCompatibleUnicode => BitConverter.IsLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode; + private static bool IsPlatformCompatibleUnicode(this Encoding encoding) => BitConverter.IsLittleEndian ? encoding.CodePage == 1200 : encoding.CodePage == 1201; + + public static Stream AsStream(this string @string, Encoding? encoding = null) => + (@string ?? throw new ArgumentNullException(nameof(@string))).AsMemory().AsStream(encoding); + public static Stream AsStream(this ReadOnlyMemory charBuffer, Encoding? encoding = null) => (encoding ??= Encoding.UTF8).IsPlatformCompatibleUnicode() + ? new UnicodeStream(charBuffer) + : Encoding.CreateTranscodingStream(new UnicodeStream(charBuffer), PlatformCompatibleUnicode, encoding, false); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/UnicodeStream.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/UnicodeStream.cs new file mode 100644 index 0000000..84c3730 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/UnicodeStream.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +internal sealed class UnicodeStream : Stream +{ + private const int BytesPerChar = 2; + + // By sealing UnicodeStream we avoid a lot of the complexity of MemoryStream. + private ReadOnlyMemory _charMemory; + private int _position; + private Task? _cachedResultTask; // For async reads, avoid allocating a Task.FromResult(nRead) every time we read. + + public UnicodeStream(string @string) : this((@string ?? throw new ArgumentNullException(nameof(@string))).AsMemory()) { } + public UnicodeStream(ReadOnlyMemory charMemory) => _charMemory = charMemory; + + public override int Read(Span buffer) + { + EnsureOpen(); + var charPosition = _position / BytesPerChar; + // MemoryMarshal.AsBytes will throw on strings longer than int.MaxValue / 2, so only slice what we need. + var byteSlice = MemoryMarshal.AsBytes(_charMemory.Slice(charPosition, Math.Min(_charMemory.Length - charPosition, 1 + buffer.Length / BytesPerChar)).Span); + var slicePosition = _position % BytesPerChar; + var nRead = Math.Min(buffer.Length, byteSlice.Length - slicePosition); + byteSlice.Slice(slicePosition, nRead).CopyTo(buffer); + _position += nRead; + return nRead; + } + + public override int Read(byte[] buffer, int offset, int count) + { + ValidateBufferArgs(buffer, offset, count); + return Read(buffer.AsSpan(offset, count)); + } + + public override int ReadByte() + { + // Could be optimized. + Span span = stackalloc byte[1]; + return Read(span) == 0 ? -1 : span[0]; + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + EnsureOpen(); + if (cancellationToken.IsCancellationRequested) + return ValueTask.FromCanceled(cancellationToken); + try + { + return new ValueTask(Read(buffer.Span)); + } + catch (Exception exception) + { + return ValueTask.FromException(exception); + } + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArgs(buffer, offset, count); + var valueTask = ReadAsync(buffer.AsMemory(offset, count), cancellationToken); + if (!valueTask.IsCompletedSuccessfully) + return valueTask.AsTask(); + var lastResultTask = _cachedResultTask; + return lastResultTask != null && lastResultTask.Result == valueTask.Result ? lastResultTask : _cachedResultTask = Task.FromResult(valueTask.Result); + } + + private void EnsureOpen() + { + if (_position == -1) + throw new ObjectDisposedException(GetType().Name); + } + + // https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.flush?view=net-5.0 + // In a class derived from Stream that doesn't support writing, Flush is typically implemented as an empty method to ensure full compatibility with other Stream types since it's valid to flush a read-only stream. + public override void Flush() { } + public override Task FlushAsync(CancellationToken cancellationToken) => cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : Task.CompletedTask; + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + _cachedResultTask = null; + _charMemory = default; + _position = -1; + } + } + finally + { + base.Dispose(disposing); + } + } + + private static void ValidateBufferArgs(byte[] buffer, int offset, int count) + { + if (buffer is null) + throw new ArgumentNullException(nameof(buffer)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + if (count > buffer.Length - offset) + throw new ArgumentException(null, nameof(count)); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.Tool/ZipArchiveEntryExtensions.cs b/src/BUTR.CrashReport.Renderer.ImGui.Tool/ZipArchiveEntryExtensions.cs new file mode 100644 index 0000000..99928f0 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.Tool/ZipArchiveEntryExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.IO; +using System.IO.Compression; + +namespace BUTR.CrashReport.Renderer.ImGui.Tool; + +public static class ZipArchiveEntryExtensions +{ + public static Stream TryOpen(this ZipArchiveEntry? entry) + { + try + { + return entry?.Open() ?? Stream.Null; + } + catch (Exception) + { + return Stream.Null; + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/BUTR.CrashReport.Renderer.ImGui.WASM.csproj b/src/BUTR.CrashReport.Renderer.ImGui.WASM/BUTR.CrashReport.Renderer.ImGui.WASM.csproj new file mode 100644 index 0000000..4c785ec --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/BUTR.CrashReport.Renderer.ImGui.WASM.csproj @@ -0,0 +1,117 @@ + + + net9.0 + true + latest + enable + + browser-wasm + + true + true + Size + + false + + $(DefineConstants);EMSCRIPTEN;OPENGLES3;SDL2 + + + + true + true + true + true + true + full + true + + $(EmccExtraLDFlags) -s USE_CLOSURE_COMPILER=1 -sFULL_ES3 -lGL -sUSE_SDL=2 + + + + + + $(EmccLinkOptimizationFlag) -O1 + true + true + false + browser; + + + + + + false + false + true + + + + + + + + false + + + + + + + + + C:\Git\Level0\BUTR.CrashReport\external\prism-sharp\PrismSharp.Core\bin\$(Configuration)\net9.0\PrismSharp.RegEx.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)bin\cache\$(Configuration)\$(TargetFramework)\emsdk-cache + + + $(TMP)\emsdk-cache + + + + + + <_WasmPInvokeModules Include="glfw3" /> + <_WasmPInvokeModules Include="SDL" /> + + + + \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.Emscripten.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.Emscripten.cs new file mode 100644 index 0000000..5eb83e9 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.Emscripten.cs @@ -0,0 +1,71 @@ +using BUTR.CrashReport.Memory; + +using Emscripten; + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM.Controller; + +partial class ImGuiController +{ + private static readonly LiteralSpan CanvasIdUtf8 = "canvas\0"u8; + + // We make sure that the backbuffer is a whole number so it will be scaled down correctly + private static void ScaleWindowSize(ref int width, ref int height, Vector2 scale) + { + var scaledWidth = width * scale.X; + while (scaledWidth % 1 != 0) + { + width--; + scaledWidth = width * scale.X; + } + + var scaledHeight = height * scale.Y; + while (scaledHeight % 1 != 0) + { + height--; + scaledHeight = height * scale.Y; + } + } + + private unsafe void SetupEmscripten(IntPtr windowHandle) + { + _imgui.GetIO(out var io); + io.BackendFlags |= ImGuiNET.ImGuiBackendFlags.HasMouseCursors; + + int windowsWidth, windowsHeight; + _emscripten.custom_emscripten_get_display_usable_bounds(&windowsWidth, &windowsHeight); + + var scale = GetWindowDevicePixelRatio(); + ScaleWindowSize(ref windowsWidth, ref windowsHeight, scale); + + SDL_SetWindowSize(_window, windowsWidth, windowsHeight); + _emscripten.custom_emscripten_set_element_style_size(CanvasIdUtf8.Ptr, windowsWidth, windowsHeight); + + var onCanvasSizeChangeDataPtr = _allocator.Alloc(new EmscriptenCanvasSizeChangeData + { + Window = windowHandle, + CanvasId = CanvasIdUtf8.Ptr, + }); + _emscripten.emscripten_set_resize_callback_on_thread((byte*) EMSCRIPTEN_EVENT_TARGET_WINDOW, onCanvasSizeChangeDataPtr, 0, &EmscriptenWindowSizeChange, EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD); + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static unsafe int EmscriptenWindowSizeChange(int event_type, EmscriptenUiEvent* @event, EmscriptenCanvasSizeChangeData* user_data) + { + if (!_instances.TryGetValue(user_data->Window, out var instanceRef) || !instanceRef.TryGetTarget(out var instance)) + return EM_FALSE; + + var windowsWidth = @event->WindowInnerWidth; + var windowsHeight = @event->WindowInnerHeight; + var scale = instance.GetWindowDevicePixelRatio(); + ScaleWindowSize(ref windowsWidth, ref windowsHeight, scale); + + SDL_SetWindowSize(user_data->Window, windowsWidth, windowsHeight); + instance._emscripten.custom_emscripten_set_element_style_size(user_data->CanvasId, windowsWidth, windowsHeight); + + return EM_TRUE; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.GL.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.GL.cs new file mode 100644 index 0000000..1aca074 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.GL.cs @@ -0,0 +1,242 @@ +using BUTR.CrashReport.Memory; + +using ImGui.Structures; + +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM.Controller; + +internal partial class ImGuiController +{ + private static readonly LiteralSpan VertexShader = "#version 300 es\n"u8 + + """ + precision highp float; + layout (location = 0) in vec2 Position; + layout (location = 1) in vec2 UV; + layout (location = 2) in vec4 Color; + uniform mat4 ProjMtx; + out vec2 Frag_UV; + out vec4 Frag_Color; + void main() + { + Frag_UV = UV; + Frag_Color = Color; + + gl_Position = ProjMtx * vec4(Position.xy, 0, 1); + } + """u8 + "\0"u8; + + private static readonly LiteralSpan FragmentShader = "#version 300 es\n"u8 + + """ + precision mediump float; + uniform sampler2D Texture; + in vec2 Frag_UV; + in vec4 Frag_Color; + layout (location = 0) out vec4 Out_Color; + void main() + { + Out_Color = Frag_Color * texture(Texture, Frag_UV.st); + } + """u8 + "\0"u8; + + + private static readonly uint _sizeOfImDrawVert = (uint) Unsafe.SizeOf(); + private static readonly unsafe void* _offsetOfImDrawVertPos = (void*) Marshal.OffsetOf(nameof(ImGuiNET.ImDrawVert.pos)); + private static readonly unsafe void* _offsetOfImDrawVertUV = (void*) Marshal.OffsetOf(nameof(ImGuiNET.ImDrawVert.uv)); + private static readonly unsafe void* _offsetOfImDrawVertCol = (void*) Marshal.OffsetOf(nameof(ImGuiNET.ImDrawVert.col)); + + private uint LoadTexture(IntPtr pixelData, uint width, uint height) + { + var textureId = _gl.GenTexture(); + _gl.CheckError(); + _gl.BindTexture(TextureTarget.Texture2D, textureId); + _gl.CheckError(); + _gl.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, TextureParameter.Linear); + _gl.CheckError(); + _gl.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, TextureParameter.Linear); + _gl.CheckError(); + _gl.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, TextureParameter.ClampToEdge); + _gl.CheckError(); + _gl.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, TextureParameter.ClampToEdge); + _gl.CheckError(); + _gl.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, width, height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, pixelData); + _gl.CheckError(); + _gl.BindTexture(TextureTarget.Texture2D, 0); + _gl.CheckError(); + return textureId; + } + + private void RebuildFontAtlas() + { + var pixelRatio = GetWindowDevicePixelRatio(); + var maxPixelRatio = MathF.Max(pixelRatio.X, pixelRatio.Y); + var normalizedMaxPixelRatio = MathF.Round(maxPixelRatio * 2, MidpointRounding.AwayFromZero) / 2; + + _imgui.GetIO(out var io); + _imgui.ImFontConfig(out var config); + io.GetFonts(out var fonts); + + config.RasterizerDensity = 2f; + //config.RasterizerMultiply = 2f; + //config.SizePixels = MathF.Floor(config.SizePixels * normalizedMaxPixelRatio); + fonts.AddFontDefault(config, out var __); + + fonts.GetTexDataAsRGBA32(out var pixelData, out var width, out var height, out _); + + _fontTextureId = LoadTexture(pixelData, (uint) width, (uint) height); + + fonts.TexID = (IntPtr) _fontTextureId; + fonts.ClearTexData(); + + _imgui.GetStyle(out var style); + style.ScaleAllSizes(normalizedMaxPixelRatio); + } + + public void Render() + { + SDL_GL_MakeCurrent(_window, _gl.Context); + + _imgui.Render(); + + _imgui.GetDrawData(out var drawData); + RenderDrawData(in drawData); + } + + private unsafe void SetupRenderState(ref readonly ImDrawDataWrapper drawData, uint framebufferWidth, uint framebufferHeight) + { + _gl.Enable(EnableCap.Blend); + _gl.CheckError(); + _gl.BlendEquation(BlendEquationMode.FuncAdd); + _gl.CheckError(); + _gl.BlendFuncSeparate(BlendingFactorDest.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha, BlendingFactorDest.One, BlendingFactorDest.OneMinusSrcAlpha); + _gl.CheckError(); + _gl.Disable(EnableCap.CullFace); + _gl.CheckError(); + _gl.Disable(EnableCap.DepthTest); + _gl.CheckError(); + _gl.Disable(EnableCap.StencilTest); + _gl.CheckError(); + _gl.Enable(EnableCap.ScissorTest); + _gl.CheckError(); + + _gl.Viewport(0, 0, framebufferWidth, framebufferHeight); + _gl.CheckError(); + + _gl.UseProgram(_shader.ProgramID); + _gl.CheckError(); + + var orthographicProjection = Matrix4x4.CreateOrthographicOffCenter( + left: drawData.DisplayPos.X, + right: drawData.DisplayPos.X + drawData.DisplaySize.X, + top: drawData.DisplayPos.Y, + bottom: drawData.DisplayPos.Y + drawData.DisplaySize.Y, + zNearPlane: -1, + zFarPlane: 1); + + _shader["Texture"]!.SetValue(0); + _shader["ProjMtx"]!.SetValue(in orthographicProjection); + + _gl.BindSampler(0, 0); + _gl.CheckError(); + + // Setup desired _gl. state + // Recreate the VAO every time (this is to easily allow multiple _gl. contexts to be rendered to. VAO are not shared among _gl. contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + _vertexArrayObject = _gl.GenVertexArray(); + _gl.CheckError(); + _gl.BindVertexArray(_vertexArrayObject); + _gl.CheckError(); + + _gl.BindBuffer(BufferTarget.ArrayBuffer, _vboHandle); + _gl.CheckError(); + _gl.BindBuffer(BufferTarget.ElementArrayBuffer, _elementsHandle); + _gl.CheckError(); + + _gl.EnableVertexAttribArray((uint) _shader["Position"]!.Location); + _gl.CheckError(); + _gl.EnableVertexAttribArray((uint) _shader["UV"]!.Location); + _gl.CheckError(); + _gl.EnableVertexAttribArray((uint) _shader["Color"]!.Location); + _gl.CheckError(); + + _gl.VertexAttribPointerBool(_shader["Position"]!.Location, 2, VertexAttribPointerType.Float, false, _sizeOfImDrawVert, _offsetOfImDrawVertPos); + _gl.CheckError(); + _gl.VertexAttribPointerBool(_shader["UV"]!.Location, 2, VertexAttribPointerType.Float, false, _sizeOfImDrawVert, _offsetOfImDrawVertUV); + _gl.CheckError(); + _gl.VertexAttribPointerBool(_shader["Color"]!.Location, 4, VertexAttribPointerType.UnsignedByte, true, _sizeOfImDrawVert, _offsetOfImDrawVertCol); + _gl.CheckError(); + } + + private unsafe void RenderCommandList(ref readonly ImDrawDataWrapper drawData, uint framebufferWidth, uint framebufferHeight) + { + _imgui.GetIO(out var io); + + ref var clipOffset = ref drawData.DisplayPos; + ref var clipScale = ref drawData.FramebufferScale; + + drawData.ScaleClipRects(io.DisplayFramebufferScale); + + drawData.GetCmdLists(out var cmdLists); + for (var n = 0; n < drawData.CmdListsCount; n++) + { + ref var cmdList = ref cmdLists[n]; + cmdList.GetVtxBuffer(out var vtxBuffer); + cmdList.GetIdxBuffer(out var idxBuffer); + cmdList.GetCmdBuffer(out var cmdBuffer); + + _gl.BufferData(BufferTarget.ArrayBuffer, (UIntPtr) (vtxBuffer.Size * _sizeOfImDrawVert), vtxBuffer.Data, BufferUsageHint.StreamDraw); + _gl.CheckError(); + _gl.BufferData(BufferTarget.ElementArrayBuffer, (UIntPtr) (idxBuffer.Size * sizeof(ushort)), idxBuffer.Data, BufferUsageHint.StreamDraw); + _gl.CheckError(); + + for (var cmd_i = 0; cmd_i < cmdBuffer.Size; cmd_i++) + { + ref var pcmd = ref cmdBuffer[cmd_i]; + //if (pcmd.UserCallback != IntPtr.Zero) + // ThrowNotImplementedException(); + + var clip_min = new Vector2Ref((pcmd.ClipRect.X - clipOffset.X) * clipScale.X, (pcmd.ClipRect.Y - clipOffset.Y) * clipScale.Y); + var clip_max = new Vector2Ref((pcmd.ClipRect.Z - clipOffset.X) * clipScale.X, (pcmd.ClipRect.W - clipOffset.Y) * clipScale.Y); + if (clip_max.X <= clip_min.X || clip_max.Y <= clip_min.Y) + continue; + + _gl.Scissor((int) clip_min.X, (int) (framebufferHeight - clip_max.Y), (uint) (clip_max.X - clip_min.X), (uint) (clip_max.Y - clip_min.Y)); + _gl.CheckError(); + + _gl.BindTexture(TextureTarget.Texture2D, (uint) pcmd.TextureId); + _gl.CheckError(); + + _gl.DrawElements(PrimitiveType.Triangles, pcmd.ElemCount, DrawElementsType.UnsignedShort, (void*) (pcmd.IdxOffset * sizeof(ushort))); + _gl.CheckError(); + } + } + } + + private void RenderDrawData(ref readonly ImDrawDataWrapper drawData) + { + if (drawData.CmdListsCount == 0) return; + + var framebufferWidth = (uint) (drawData.DisplaySize.X * drawData.FramebufferScale.X); + var framebufferHeight = (uint) (drawData.DisplaySize.Y * drawData.FramebufferScale.Y); + if (framebufferWidth <= 0 || framebufferHeight <= 0) return; + + _gl.ActiveTexture(TextureUnit.Texture0); + _gl.CheckError(); + + SetupRenderState(in drawData, framebufferWidth, framebufferHeight); + + RenderCommandList(in drawData, framebufferWidth, framebufferHeight); + + // Destroy the temporary VAO + _gl.DeleteVertexArray(_vertexArrayObject); + _gl.CheckError(); + _vertexArrayObject = 0; + } + + [DoesNotReturn] + private static void ThrowNotImplementedException() => throw new NotImplementedException(); + +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.SDL2.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.SDL2.cs new file mode 100644 index 0000000..00e0d76 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.SDL2.cs @@ -0,0 +1,353 @@ +#if SDL2 +using BUTR.CrashReport.ImGui.Enums; + +using System.Numerics; +using System.Runtime.InteropServices; +using System.Text; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM.Controller; + +partial class ImGuiController +{ + private readonly IntPtr[] _mouseCursors = new IntPtr[(int) ImGuiMouseCursor.COUNT]; + + private IntPtr MouseLastCursor; + private ulong _time; + + private Vector2 GetWindowDevicePixelRatio() + { + SDL_GetWindowSize(_window, out var w, out var h); + SDL_GL_GetDrawableSize(_window, out var wp, out var hp); + return new Vector2((float) wp / (float) w, (float) hp / (float) h); + } + + public void Init() + { + _imgui.GetIO(out var io); + + io.BackendFlags |= ImGuiNET.ImGuiBackendFlags.HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiNET.ImGuiBackendFlags.HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.Arrow] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.TextInput] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeAll] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEALL); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeNS] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeEW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeNESW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeNWSE] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.Hand] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_HAND); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.NotAllowed] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NO); + + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + //SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); + } + + public void NewFrame() + { + _imgui.GetIO(out var io); + + // Setup display size (every frame to accommodate for window resizing) + SDL_GetWindowSize(_window, out var w, out var h); + SDL_GL_GetDrawableSize(_window, out var displayW, out var displayH); + + if (SDL_GetWindowFlags(_window).HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) + w = h = 0; + + io.DisplaySize.X = displayW; + io.DisplaySize.Y = displayH; + io.DisplayFramebufferScale.X = 1; + io.DisplayFramebufferScale.Y = 1; + /* + if (w > 0 && h > 0) + { + io.DisplayFramebufferScale.X = (float) displayW / w; + io.DisplayFramebufferScale.Y = (float) displayH / h; + } + */ + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + var frequency = SDL_GetPerformanceFrequency(); + var currentTime = SDL_GetPerformanceCounter(); + io.DeltaTime = _time > 0 ? (float) ((double) (currentTime - _time) / frequency) : 1.0f / 60.0f; + if (io.DeltaTime <= 0) + io.DeltaTime = 0.016f; + _time = currentTime; + + UpdateMouseData(); + UpdateMouseCursor(); + + _imgui.NewFrame(); + } + + public unsafe bool ProcessEvent(SDL_Event evt) + { + _imgui.GetIO(out var io); + switch (evt.type) + { + case SDL_EventType.SDL_MOUSEMOTION: + { + if (evt.motion.windowID != SDL_GetWindowID(_window)) return false; + + var scale = GetWindowDevicePixelRatio(); + + //io.AddMouseSourceEvent(evt.motion.which == SDL_TOUCH_MOUSEID ? 0 : 1, evt.motion.x * scale.X, evt.motion.y * scale.Y); + io.AddMousePosEvent(evt.motion.x * scale.X, evt.motion.y * scale.Y); + return true; + } + case SDL_EventType.SDL_MOUSEWHEEL: + { + if (evt.wheel.windowID != SDL_GetWindowID(_window)) return false; ; + + var wheel_x = -evt.wheel.preciseX; + var wheel_y = evt.wheel.preciseY; + + //wheel_x /= 100.0f; + + //io.AddMouseSourceEvent(evt->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseWheelEvent(wheel_x, wheel_y); + return true; + } + case SDL_EventType.SDL_MOUSEBUTTONDOWN: + case SDL_EventType.SDL_MOUSEBUTTONUP: + { + if (evt.button.windowID != SDL_GetWindowID(_window)) return false; ; + + var mouse_button = ImGuiNET.ImGuiMouseButton.COUNT; + if (evt.button.button == SDL_BUTTON_LEFT) { mouse_button = ImGuiNET.ImGuiMouseButton.Left; } + if (evt.button.button == SDL_BUTTON_RIGHT) { mouse_button = ImGuiNET.ImGuiMouseButton.Right; } + if (evt.button.button == SDL_BUTTON_MIDDLE) { mouse_button = ImGuiNET.ImGuiMouseButton.Middle; } + if (evt.button.button == SDL_BUTTON_X1) { mouse_button = (ImGuiNET.ImGuiMouseButton) 3; } + if (evt.button.button == SDL_BUTTON_X2) { mouse_button = (ImGuiNET.ImGuiMouseButton) 4; } + if (mouse_button == ImGuiNET.ImGuiMouseButton.COUNT) return false; ; + + //io.AddMouseSourceEvent(evt.button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent(mouse_button, evt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN); + return true; + } + case SDL_EventType.SDL_TEXTINPUT: + { + if (evt.text.windowID != SDL_GetWindowID(_window)) return false; ; + + var utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(evt.text.text); + var utf16Length = Encoding.UTF8.GetCharCount(utf8); + Span utf16 = stackalloc char[utf16Length]; + Encoding.UTF8.GetChars(utf8, utf16); + + for (var i = 0; i < utf16.Length; i++) + io.AddInputCharacter(utf16[i]); + return true; + } + case SDL_EventType.SDL_KEYDOWN: + case SDL_EventType.SDL_KEYUP: + { + if (evt.key.windowID != SDL_GetWindowID(_window)) return false; ; + + UpdateKeyModifiers(evt.key.keysym.mod); + + var key = MapSDLKeyToImGuiKey(evt.key.keysym.sym, evt.key.keysym.scancode); + + io.AddKeyEvent(key, evt.type == SDL_EventType.SDL_KEYDOWN); + io.SetKeyEventNativeData(key, (int) evt.key.keysym.sym, (int) evt.key.keysym.scancode, (int) evt.key.keysym.scancode); + return true; + } + case SDL_EventType.SDL_WINDOWEVENT: + { + if (evt.window.windowID != SDL_GetWindowID(_window)) return false; ; + + /* + if (evt.window.windowEvent == SDL_WindowEventID.SDL_WINDOWEVENT_ENTER) + { + bd->MouseWindowID = evt.window.windowID; + bd->MouseLastLeaveFrame = 0; + } + if (evt.window.windowEvent == SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE) + bd->MouseLastLeaveFrame = ImGui::GetFrameCount() + 1; + */ + if (evt.window.windowEvent == SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED) + io.AddFocusEvent(true); + else if (evt.window.windowEvent == SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST) + io.AddFocusEvent(false); + return true; + } + case SDL_EventType.SDL_CONTROLLERDEVICEADDED: + case SDL_EventType.SDL_CONTROLLERDEVICEREMOVED: + { + //bd->WantUpdateGamepadsList = true; + return true; + } + } + return false; + } + + private static ImGuiNET.ImGuiKey MapSDLKeyToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancod) => keycode switch + { + SDL_Keycode.SDLK_TAB => ImGuiNET.ImGuiKey.Tab, + SDL_Keycode.SDLK_LEFT => ImGuiNET.ImGuiKey.LeftArrow, + SDL_Keycode.SDLK_RIGHT => ImGuiNET.ImGuiKey.RightArrow, + SDL_Keycode.SDLK_UP => ImGuiNET.ImGuiKey.UpArrow, + SDL_Keycode.SDLK_DOWN => ImGuiNET.ImGuiKey.DownArrow, + SDL_Keycode.SDLK_PAGEUP => ImGuiNET.ImGuiKey.PageUp, + SDL_Keycode.SDLK_PAGEDOWN => ImGuiNET.ImGuiKey.PageDown, + SDL_Keycode.SDLK_HOME => ImGuiNET.ImGuiKey.Home, + SDL_Keycode.SDLK_END => ImGuiNET.ImGuiKey.End, + SDL_Keycode.SDLK_INSERT => ImGuiNET.ImGuiKey.Insert, + SDL_Keycode.SDLK_DELETE => ImGuiNET.ImGuiKey.Delete, + SDL_Keycode.SDLK_BACKSPACE => ImGuiNET.ImGuiKey.Backspace, + SDL_Keycode.SDLK_SPACE => ImGuiNET.ImGuiKey.Space, + SDL_Keycode.SDLK_RETURN => ImGuiNET.ImGuiKey.Enter, + SDL_Keycode.SDLK_ESCAPE => ImGuiNET.ImGuiKey.Escape, + SDL_Keycode.SDLK_QUOTE => ImGuiNET.ImGuiKey.Apostrophe, + SDL_Keycode.SDLK_COMMA => ImGuiNET.ImGuiKey.Comma, + SDL_Keycode.SDLK_MINUS => ImGuiNET.ImGuiKey.Minus, + SDL_Keycode.SDLK_PERIOD => ImGuiNET.ImGuiKey.Period, + SDL_Keycode.SDLK_SLASH => ImGuiNET.ImGuiKey.Slash, + SDL_Keycode.SDLK_SEMICOLON => ImGuiNET.ImGuiKey.Semicolon, + SDL_Keycode.SDLK_EQUALS => ImGuiNET.ImGuiKey.Equal, + SDL_Keycode.SDLK_LEFTBRACKET => ImGuiNET.ImGuiKey.LeftBracket, + SDL_Keycode.SDLK_BACKSLASH => ImGuiNET.ImGuiKey.Backslash, + SDL_Keycode.SDLK_RIGHTBRACKET => ImGuiNET.ImGuiKey.RightBracket, + SDL_Keycode.SDLK_BACKQUOTE => ImGuiNET.ImGuiKey.GraveAccent, + SDL_Keycode.SDLK_CAPSLOCK => ImGuiNET.ImGuiKey.CapsLock, + SDL_Keycode.SDLK_SCROLLLOCK => ImGuiNET.ImGuiKey.ScrollLock, + SDL_Keycode.SDLK_NUMLOCKCLEAR => ImGuiNET.ImGuiKey.NumLock, + SDL_Keycode.SDLK_PRINTSCREEN => ImGuiNET.ImGuiKey.PrintScreen, + SDL_Keycode.SDLK_PAUSE => ImGuiNET.ImGuiKey.Pause, + SDL_Keycode.SDLK_KP_0 => ImGuiNET.ImGuiKey.Keypad0, + SDL_Keycode.SDLK_KP_1 => ImGuiNET.ImGuiKey.Keypad1, + SDL_Keycode.SDLK_KP_2 => ImGuiNET.ImGuiKey.Keypad2, + SDL_Keycode.SDLK_KP_3 => ImGuiNET.ImGuiKey.Keypad3, + SDL_Keycode.SDLK_KP_4 => ImGuiNET.ImGuiKey.Keypad4, + SDL_Keycode.SDLK_KP_5 => ImGuiNET.ImGuiKey.Keypad5, + SDL_Keycode.SDLK_KP_6 => ImGuiNET.ImGuiKey.Keypad6, + SDL_Keycode.SDLK_KP_7 => ImGuiNET.ImGuiKey.Keypad7, + SDL_Keycode.SDLK_KP_8 => ImGuiNET.ImGuiKey.Keypad8, + SDL_Keycode.SDLK_KP_9 => ImGuiNET.ImGuiKey.Keypad9, + SDL_Keycode.SDLK_KP_PERIOD => ImGuiNET.ImGuiKey.KeypadDecimal, + SDL_Keycode.SDLK_KP_DIVIDE => ImGuiNET.ImGuiKey.KeypadDivide, + SDL_Keycode.SDLK_KP_MULTIPLY => ImGuiNET.ImGuiKey.KeypadMultiply, + SDL_Keycode.SDLK_KP_MINUS => ImGuiNET.ImGuiKey.KeypadSubtract, + SDL_Keycode.SDLK_KP_PLUS => ImGuiNET.ImGuiKey.KeypadAdd, + SDL_Keycode.SDLK_KP_ENTER => ImGuiNET.ImGuiKey.KeypadEnter, + SDL_Keycode.SDLK_KP_EQUALS => ImGuiNET.ImGuiKey.KeypadEqual, + SDL_Keycode.SDLK_LCTRL => ImGuiNET.ImGuiKey.LeftCtrl, + SDL_Keycode.SDLK_LSHIFT => ImGuiNET.ImGuiKey.LeftShift, + SDL_Keycode.SDLK_LALT => ImGuiNET.ImGuiKey.LeftAlt, + SDL_Keycode.SDLK_LGUI => ImGuiNET.ImGuiKey.LeftSuper, + SDL_Keycode.SDLK_RCTRL => ImGuiNET.ImGuiKey.RightCtrl, + SDL_Keycode.SDLK_RSHIFT => ImGuiNET.ImGuiKey.RightShift, + SDL_Keycode.SDLK_RALT => ImGuiNET.ImGuiKey.RightAlt, + SDL_Keycode.SDLK_RGUI => ImGuiNET.ImGuiKey.RightSuper, + SDL_Keycode.SDLK_APPLICATION => ImGuiNET.ImGuiKey.Menu, + SDL_Keycode.SDLK_0 => ImGuiNET.ImGuiKey._0, + SDL_Keycode.SDLK_1 => ImGuiNET.ImGuiKey._1, + SDL_Keycode.SDLK_2 => ImGuiNET.ImGuiKey._2, + SDL_Keycode.SDLK_3 => ImGuiNET.ImGuiKey._3, + SDL_Keycode.SDLK_4 => ImGuiNET.ImGuiKey._4, + SDL_Keycode.SDLK_5 => ImGuiNET.ImGuiKey._5, + SDL_Keycode.SDLK_6 => ImGuiNET.ImGuiKey._6, + SDL_Keycode.SDLK_7 => ImGuiNET.ImGuiKey._7, + SDL_Keycode.SDLK_8 => ImGuiNET.ImGuiKey._8, + SDL_Keycode.SDLK_9 => ImGuiNET.ImGuiKey._9, + SDL_Keycode.SDLK_a => ImGuiNET.ImGuiKey.A, + SDL_Keycode.SDLK_b => ImGuiNET.ImGuiKey.B, + SDL_Keycode.SDLK_c => ImGuiNET.ImGuiKey.C, + SDL_Keycode.SDLK_d => ImGuiNET.ImGuiKey.D, + SDL_Keycode.SDLK_e => ImGuiNET.ImGuiKey.E, + SDL_Keycode.SDLK_f => ImGuiNET.ImGuiKey.F, + SDL_Keycode.SDLK_g => ImGuiNET.ImGuiKey.G, + SDL_Keycode.SDLK_h => ImGuiNET.ImGuiKey.H, + SDL_Keycode.SDLK_i => ImGuiNET.ImGuiKey.I, + SDL_Keycode.SDLK_j => ImGuiNET.ImGuiKey.J, + SDL_Keycode.SDLK_k => ImGuiNET.ImGuiKey.K, + SDL_Keycode.SDLK_l => ImGuiNET.ImGuiKey.L, + SDL_Keycode.SDLK_m => ImGuiNET.ImGuiKey.M, + SDL_Keycode.SDLK_n => ImGuiNET.ImGuiKey.N, + SDL_Keycode.SDLK_o => ImGuiNET.ImGuiKey.O, + SDL_Keycode.SDLK_p => ImGuiNET.ImGuiKey.P, + SDL_Keycode.SDLK_q => ImGuiNET.ImGuiKey.Q, + SDL_Keycode.SDLK_r => ImGuiNET.ImGuiKey.R, + SDL_Keycode.SDLK_s => ImGuiNET.ImGuiKey.S, + SDL_Keycode.SDLK_t => ImGuiNET.ImGuiKey.T, + SDL_Keycode.SDLK_u => ImGuiNET.ImGuiKey.U, + SDL_Keycode.SDLK_v => ImGuiNET.ImGuiKey.V, + SDL_Keycode.SDLK_w => ImGuiNET.ImGuiKey.W, + SDL_Keycode.SDLK_x => ImGuiNET.ImGuiKey.X, + SDL_Keycode.SDLK_y => ImGuiNET.ImGuiKey.Y, + SDL_Keycode.SDLK_z => ImGuiNET.ImGuiKey.Z, + SDL_Keycode.SDLK_F1 => ImGuiNET.ImGuiKey.F1, + SDL_Keycode.SDLK_F2 => ImGuiNET.ImGuiKey.F2, + SDL_Keycode.SDLK_F3 => ImGuiNET.ImGuiKey.F3, + SDL_Keycode.SDLK_F4 => ImGuiNET.ImGuiKey.F4, + SDL_Keycode.SDLK_F5 => ImGuiNET.ImGuiKey.F5, + SDL_Keycode.SDLK_F6 => ImGuiNET.ImGuiKey.F6, + SDL_Keycode.SDLK_F7 => ImGuiNET.ImGuiKey.F7, + SDL_Keycode.SDLK_F8 => ImGuiNET.ImGuiKey.F8, + SDL_Keycode.SDLK_F9 => ImGuiNET.ImGuiKey.F9, + SDL_Keycode.SDLK_F10 => ImGuiNET.ImGuiKey.F10, + SDL_Keycode.SDLK_F11 => ImGuiNET.ImGuiKey.F11, + SDL_Keycode.SDLK_F12 => ImGuiNET.ImGuiKey.F12, + SDL_Keycode.SDLK_F13 => ImGuiNET.ImGuiKey.F13, + SDL_Keycode.SDLK_F14 => ImGuiNET.ImGuiKey.F14, + SDL_Keycode.SDLK_F15 => ImGuiNET.ImGuiKey.F15, + SDL_Keycode.SDLK_F16 => ImGuiNET.ImGuiKey.F16, + SDL_Keycode.SDLK_F17 => ImGuiNET.ImGuiKey.F17, + SDL_Keycode.SDLK_F18 => ImGuiNET.ImGuiKey.F18, + SDL_Keycode.SDLK_F19 => ImGuiNET.ImGuiKey.F19, + SDL_Keycode.SDLK_F20 => ImGuiNET.ImGuiKey.F20, + SDL_Keycode.SDLK_F21 => ImGuiNET.ImGuiKey.F21, + SDL_Keycode.SDLK_F22 => ImGuiNET.ImGuiKey.F22, + SDL_Keycode.SDLK_F23 => ImGuiNET.ImGuiKey.F23, + SDL_Keycode.SDLK_F24 => ImGuiNET.ImGuiKey.F24, + SDL_Keycode.SDLK_AC_BACK => ImGuiNET.ImGuiKey.AppBack, + SDL_Keycode.SDLK_AC_FORWARD => ImGuiNET.ImGuiKey.AppForward, + _ => ImGuiNET.ImGuiKey.None, + }; + + private void UpdateKeyModifiers(SDL_Keymod sdl_key_mods) + { + _imgui.GetIO(out var io); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModCtrl, (sdl_key_mods & SDL_Keymod.KMOD_CTRL) != 0); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModShift, (sdl_key_mods & SDL_Keymod.KMOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModAlt, (sdl_key_mods & SDL_Keymod.KMOD_ALT) != 0); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModSuper, (sdl_key_mods & SDL_Keymod.KMOD_GUI) != 0); + } + + private void UpdateMouseData() + { + _imgui.GetIO(out var io); + + var is_app_focused = SDL_GetWindowFlags(_window).HasFlag(SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS); // SDL 2.0.3 and non-windowed systems: single-viewport only + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(_window, (int) io.MousePos.X, (int) io.MousePos.Y); + } + } + + private void UpdateMouseCursor() + { + _imgui.GetIO(out var io); + if (io.ConfigFlags.HasFlag(ImGuiNET.ImGuiConfigFlags.NoMouseCursorChange)) + return; + + var imgui_cursor = _imgui.GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor.None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_ShowCursor(SDL_bool.SDL_FALSE); + } + else + { + // Show OS mouse cursor + var expected_cursor = _mouseCursors.Length > (int) imgui_cursor ? _mouseCursors[(int) imgui_cursor] : _mouseCursors[(int) ImGuiMouseCursor.Arrow]; + if (MouseLastCursor != expected_cursor) + { + SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113) + MouseLastCursor = expected_cursor; + } + SDL_ShowCursor(SDL_bool.SDL_TRUE); + } + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.SDL3.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.SDL3.cs new file mode 100644 index 0000000..4ad9cab --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.SDL3.cs @@ -0,0 +1,359 @@ +#if SDL3 +using BUTR.CrashReport.ImGui.Enums; + +using System.Numerics; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM.Controller; + +partial class ImGuiGLRenderer +{ + private readonly IntPtr[] _mouseCursors = new IntPtr[(int) ImGuiMouseCursor.COUNT]; + + private IntPtr MouseLastCursor; + + private ulong _time; + + private Vector2 GetWindowDevicePixelRatio() + { + SDL_GetWindowSize(_window, out var w, out var h); + SDL_GetWindowSizeInPixels(_window, out var wp, out var hp); + return new Vector2((float) wp / (float) w, (float) hp / (float) h); + } + + public void Init() + { + _imgui.GetIO(out var io); + + io.BackendFlags |= ImGuiNET.ImGuiBackendFlags.HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiNET.ImGuiBackendFlags.HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.Arrow] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_DEFAULT); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.TextInput] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_TEXT); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeAll] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_MOVE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeNS] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NS_RESIZE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeEW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_EW_RESIZE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeNESW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NESW_RESIZE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.ResizeNWSE] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NWSE_RESIZE); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.Hand] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_POINTER); + _mouseCursors[(int) ImGuiNET.ImGuiMouseCursor.NotAllowed] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NOT_ALLOWED); + + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); + SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); + } + + public void NewFrame() + { + _imgui.GetIO(out var io); + + // Setup display size (every frame to accommodate for window resizing) + SDL_GetWindowSize(_window, out var w, out var h); + SDL_GetWindowSizeInPixels(_window, out var displayW, out var displayH); + + if (SDL_GetWindowFlags(_window).HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) + w = h = 0; + + io.DisplaySize.X = displayW; + io.DisplaySize.Y = displayH; + io.DisplayFramebufferScale.X = 1; + io.DisplayFramebufferScale.Y = 1; + /* + if (w > 0 && h > 0) + { + io.DisplayFramebufferScale.X = (float) displayW / w; + io.DisplayFramebufferScale.Y = (float) displayH / h; + } + */ + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + var frequency = SDL_GetPerformanceFrequency(); + var currentTime = SDL_GetPerformanceCounter(); + io.DeltaTime = _time > 0 ? (float)((double)(currentTime - _time) / frequency) : (float) (1.0f / 60.0f); + if (io.DeltaTime <= 0) + io.DeltaTime = 0.016f; + _time = currentTime; + + UpdateMouseData(); + UpdateMouseCursor(); + + //_imgui.NewFrame(); + } + + public unsafe bool ProcessEvent(SDL_Event evt) + { + _imgui.GetIO(out var io); + switch ((SDL_EventType) evt.type) + { + case SDL_EventType.SDL_EVENT_MOUSE_MOTION: + { + if (evt.motion.windowID != SDL_GetWindowID(_window)) return false; + + var scale = GetWindowDevicePixelRatio(); + + //io.AddMouseSourceEvent(evt.motion.which == SDL_TOUCH_MOUSEID ? 0 : 1, evt.motion.x * scale.X, evt.motion.y * scale.Y); + io.AddMousePosEvent(evt.motion.x * scale.X, evt.motion.y * scale.Y); + return true; + } + case SDL_EventType.SDL_EVENT_MOUSE_WHEEL: + { + if (evt.wheel.windowID != SDL_GetWindowID(_window)) return false;; + + var wheel_x = -evt.wheel.x; + var wheel_y = evt.wheel.y; + + //io.AddMouseSourceEvent(evt->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseWheelEvent(wheel_x, wheel_y); + return true; + } + case SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EventType.SDL_EVENT_MOUSE_BUTTON_UP: + { + if (evt.button.windowID != SDL_GetWindowID(_window)) return false;; + + var mouse_button = (int) ImGuiNET.ImGuiMouseButton.COUNT; + if (evt.button.button == (int) SDL_MouseButtonFlags.SDL_BUTTON_LMASK) { mouse_button = (int) ImGuiNET.ImGuiMouseButton.Left; } + if (evt.button.button == (int) SDL_MouseButtonFlags.SDL_BUTTON_RMASK) { mouse_button = (int) ImGuiNET.ImGuiMouseButton.Right; } + if (evt.button.button == (int) SDL_MouseButtonFlags.SDL_BUTTON_MMASK) { mouse_button = (int) ImGuiNET.ImGuiMouseButton.Middle; } + if (evt.button.button == (int) SDL_MouseButtonFlags.SDL_BUTTON_X1MASK) { mouse_button = 3; } + if (evt.button.button == (int) SDL_MouseButtonFlags.SDL_BUTTON_X2MASK) { mouse_button = 4; } + if (mouse_button == (int) ImGuiNET.ImGuiMouseButton.COUNT) return false;; + + //io.AddMouseSourceEvent(evt.button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent((ImGuiNET.ImGuiMouseButton) mouse_button, evt.type == (uint) SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN); + return true; + } + case SDL_EventType.SDL_EVENT_TEXT_INPUT: + { + if (evt.text.windowID != SDL_GetWindowID(_window)) return false; + + io.AddInputCharacterUTF8(evt.text.text); + return true; + } + case SDL_EventType.SDL_EVENT_KEY_DOWN: + case SDL_EventType.SDL_EVENT_KEY_UP: + { + if (evt.key.windowID != SDL_GetWindowID(_window)) return false;; + + UpdateKeyModifiers(evt.key.mod); + + var key = MapSDLKeyToImGuiKey((SDL_Keycode) evt.key.key, evt.key.scancode); + + io.AddKeyEvent(key, evt.type == (uint) SDL_EventType.SDL_EVENT_KEY_DOWN); + io.SetKeyEventNativeData(key, (int) evt.key.key, (int) evt.key.scancode, (int) evt.key.scancode); + return true; + } + /* + case SDL_EventType.SDL_EVENT_WINDOW_MOUSE_ENTER: + { + if (evt.window.windowID != SDL_GetWindowID(_window)) return false;; + + return true; + } + case SDL_EventType.SDL_EVENT_WINDOW_MOUSE_LEAVE: + { + if (evt.window.windowID != SDL_GetWindowID(_window)) return false;; + + return true; + } + */ + case SDL_EventType.SDL_EVENT_WINDOW_FOCUS_GAINED: + case SDL_EventType.SDL_EVENT_WINDOW_FOCUS_LOST: + { + if (evt.window.windowID != SDL_GetWindowID(_window)) return false;; + + io.AddFocusEvent(evt.type == (uint) SDL_EventType.SDL_EVENT_WINDOW_FOCUS_GAINED); + + return true; + } + /* + case SDL_EventType.SDL_EVENT_GAMEPAD_ADDED: + case SDL_EventType.SDL_EVENT_GAMEPAD_REMOVED: + { + WantUpdateGamepadsList = true; + return true; + } + */ + } + return false; + } + + private static ImGuiNET.ImGuiKey MapSDLKeyToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) + { + // Keypad doesn't have individual key values in SDL3 + switch (scancode) + { + case SDL_Scancode.SDL_SCANCODE_KP_0: return ImGuiNET.ImGuiKey.Keypad0; + case SDL_Scancode.SDL_SCANCODE_KP_1: return ImGuiNET.ImGuiKey.Keypad1; + case SDL_Scancode.SDL_SCANCODE_KP_2: return ImGuiNET.ImGuiKey.Keypad2; + case SDL_Scancode.SDL_SCANCODE_KP_3: return ImGuiNET.ImGuiKey.Keypad3; + case SDL_Scancode.SDL_SCANCODE_KP_4: return ImGuiNET.ImGuiKey.Keypad4; + case SDL_Scancode.SDL_SCANCODE_KP_5: return ImGuiNET.ImGuiKey.Keypad5; + case SDL_Scancode.SDL_SCANCODE_KP_6: return ImGuiNET.ImGuiKey.Keypad6; + case SDL_Scancode.SDL_SCANCODE_KP_7: return ImGuiNET.ImGuiKey.Keypad7; + case SDL_Scancode.SDL_SCANCODE_KP_8: return ImGuiNET.ImGuiKey.Keypad8; + case SDL_Scancode.SDL_SCANCODE_KP_9: return ImGuiNET.ImGuiKey.Keypad9; + case SDL_Scancode.SDL_SCANCODE_KP_PERIOD: return ImGuiNET.ImGuiKey.KeypadDecimal; + case SDL_Scancode.SDL_SCANCODE_KP_DIVIDE: return ImGuiNET.ImGuiKey.KeypadDivide; + case SDL_Scancode.SDL_SCANCODE_KP_MULTIPLY: return ImGuiNET.ImGuiKey.KeypadMultiply; + case SDL_Scancode.SDL_SCANCODE_KP_MINUS: return ImGuiNET.ImGuiKey.KeypadSubtract; + case SDL_Scancode.SDL_SCANCODE_KP_PLUS: return ImGuiNET.ImGuiKey.KeypadAdd; + case SDL_Scancode.SDL_SCANCODE_KP_ENTER: return ImGuiNET.ImGuiKey.KeypadEnter; + case SDL_Scancode.SDL_SCANCODE_KP_EQUALS: return ImGuiNET.ImGuiKey.KeypadEqual; + default: break; + } + switch (keycode) + { + case SDL_Keycode.SDLK_TAB: return ImGuiNET.ImGuiKey.Tab; + case SDL_Keycode.SDLK_LEFT: return ImGuiNET.ImGuiKey.LeftArrow; + case SDL_Keycode.SDLK_RIGHT: return ImGuiNET.ImGuiKey.RightArrow; + case SDL_Keycode.SDLK_UP: return ImGuiNET.ImGuiKey.UpArrow; + case SDL_Keycode.SDLK_DOWN: return ImGuiNET.ImGuiKey.DownArrow; + case SDL_Keycode.SDLK_PAGEUP: return ImGuiNET.ImGuiKey.PageUp; + case SDL_Keycode.SDLK_PAGEDOWN: return ImGuiNET.ImGuiKey.PageDown; + case SDL_Keycode.SDLK_HOME: return ImGuiNET.ImGuiKey.Home; + case SDL_Keycode.SDLK_END: return ImGuiNET.ImGuiKey.End; + case SDL_Keycode.SDLK_INSERT: return ImGuiNET.ImGuiKey.Insert; + case SDL_Keycode.SDLK_DELETE: return ImGuiNET.ImGuiKey.Delete; + case SDL_Keycode.SDLK_BACKSPACE: return ImGuiNET.ImGuiKey.Backspace; + case SDL_Keycode.SDLK_SPACE: return ImGuiNET.ImGuiKey.Space; + case SDL_Keycode.SDLK_RETURN: return ImGuiNET.ImGuiKey.Enter; + case SDL_Keycode.SDLK_ESCAPE: return ImGuiNET.ImGuiKey.Escape; + case SDL_Keycode.SDLK_APOSTROPHE: return ImGuiNET.ImGuiKey.Apostrophe; + case SDL_Keycode.SDLK_COMMA: return ImGuiNET.ImGuiKey.Comma; + case SDL_Keycode.SDLK_MINUS: return ImGuiNET.ImGuiKey.Minus; + case SDL_Keycode.SDLK_PERIOD: return ImGuiNET.ImGuiKey.Period; + case SDL_Keycode.SDLK_SLASH: return ImGuiNET.ImGuiKey.Slash; + case SDL_Keycode.SDLK_SEMICOLON: return ImGuiNET.ImGuiKey.Semicolon; + case SDL_Keycode.SDLK_EQUALS: return ImGuiNET.ImGuiKey.Equal; + case SDL_Keycode.SDLK_LEFTBRACKET: return ImGuiNET.ImGuiKey.LeftBracket; + case SDL_Keycode.SDLK_BACKSLASH: return ImGuiNET.ImGuiKey.Backslash; + case SDL_Keycode.SDLK_RIGHTBRACKET: return ImGuiNET.ImGuiKey.RightBracket; + case SDL_Keycode.SDLK_GRAVE: return ImGuiNET.ImGuiKey.GraveAccent; + case SDL_Keycode.SDLK_CAPSLOCK: return ImGuiNET.ImGuiKey.CapsLock; + case SDL_Keycode.SDLK_SCROLLLOCK: return ImGuiNET.ImGuiKey.ScrollLock; + case SDL_Keycode.SDLK_NUMLOCKCLEAR: return ImGuiNET.ImGuiKey.NumLock; + case SDL_Keycode.SDLK_PRINTSCREEN: return ImGuiNET.ImGuiKey.PrintScreen; + case SDL_Keycode.SDLK_PAUSE: return ImGuiNET.ImGuiKey.Pause; + case SDL_Keycode.SDLK_LCTRL: return ImGuiNET.ImGuiKey.LeftCtrl; + case SDL_Keycode.SDLK_LSHIFT: return ImGuiNET.ImGuiKey.LeftShift; + case SDL_Keycode.SDLK_LALT: return ImGuiNET.ImGuiKey.LeftAlt; + case SDL_Keycode.SDLK_LGUI: return ImGuiNET.ImGuiKey.LeftSuper; + case SDL_Keycode.SDLK_RCTRL: return ImGuiNET.ImGuiKey.RightCtrl; + case SDL_Keycode.SDLK_RSHIFT: return ImGuiNET.ImGuiKey.RightShift; + case SDL_Keycode.SDLK_RALT: return ImGuiNET.ImGuiKey.RightAlt; + case SDL_Keycode.SDLK_RGUI: return ImGuiNET.ImGuiKey.RightSuper; + case SDL_Keycode.SDLK_APPLICATION: return ImGuiNET.ImGuiKey.Menu; + case SDL_Keycode.SDLK_0: return ImGuiNET.ImGuiKey._0; + case SDL_Keycode.SDLK_1: return ImGuiNET.ImGuiKey._1; + case SDL_Keycode.SDLK_2: return ImGuiNET.ImGuiKey._2; + case SDL_Keycode.SDLK_3: return ImGuiNET.ImGuiKey._3; + case SDL_Keycode.SDLK_4: return ImGuiNET.ImGuiKey._4; + case SDL_Keycode.SDLK_5: return ImGuiNET.ImGuiKey._5; + case SDL_Keycode.SDLK_6: return ImGuiNET.ImGuiKey._6; + case SDL_Keycode.SDLK_7: return ImGuiNET.ImGuiKey._7; + case SDL_Keycode.SDLK_8: return ImGuiNET.ImGuiKey._8; + case SDL_Keycode.SDLK_9: return ImGuiNET.ImGuiKey._9; + case SDL_Keycode.SDLK_A: return ImGuiNET.ImGuiKey.A; + case SDL_Keycode.SDLK_B: return ImGuiNET.ImGuiKey.B; + case SDL_Keycode.SDLK_C: return ImGuiNET.ImGuiKey.C; + case SDL_Keycode.SDLK_D: return ImGuiNET.ImGuiKey.D; + case SDL_Keycode.SDLK_E: return ImGuiNET.ImGuiKey.E; + case SDL_Keycode.SDLK_F: return ImGuiNET.ImGuiKey.F; + case SDL_Keycode.SDLK_G: return ImGuiNET.ImGuiKey.G; + case SDL_Keycode.SDLK_H: return ImGuiNET.ImGuiKey.H; + case SDL_Keycode.SDLK_I: return ImGuiNET.ImGuiKey.I; + case SDL_Keycode.SDLK_J: return ImGuiNET.ImGuiKey.J; + case SDL_Keycode.SDLK_K: return ImGuiNET.ImGuiKey.K; + case SDL_Keycode.SDLK_L: return ImGuiNET.ImGuiKey.L; + case SDL_Keycode.SDLK_M: return ImGuiNET.ImGuiKey.M; + case SDL_Keycode.SDLK_N: return ImGuiNET.ImGuiKey.N; + case SDL_Keycode.SDLK_O: return ImGuiNET.ImGuiKey.O; + case SDL_Keycode.SDLK_P: return ImGuiNET.ImGuiKey.P; + case SDL_Keycode.SDLK_Q: return ImGuiNET.ImGuiKey.Q; + case SDL_Keycode.SDLK_R: return ImGuiNET.ImGuiKey.R; + case SDL_Keycode.SDLK_S: return ImGuiNET.ImGuiKey.S; + case SDL_Keycode.SDLK_T: return ImGuiNET.ImGuiKey.T; + case SDL_Keycode.SDLK_U: return ImGuiNET.ImGuiKey.U; + case SDL_Keycode.SDLK_V: return ImGuiNET.ImGuiKey.V; + case SDL_Keycode.SDLK_W: return ImGuiNET.ImGuiKey.W; + case SDL_Keycode.SDLK_X: return ImGuiNET.ImGuiKey.X; + case SDL_Keycode.SDLK_Y: return ImGuiNET.ImGuiKey.Y; + case SDL_Keycode.SDLK_Z: return ImGuiNET.ImGuiKey.Z; + case SDL_Keycode.SDLK_F1: return ImGuiNET.ImGuiKey.F1; + case SDL_Keycode.SDLK_F2: return ImGuiNET.ImGuiKey.F2; + case SDL_Keycode.SDLK_F3: return ImGuiNET.ImGuiKey.F3; + case SDL_Keycode.SDLK_F4: return ImGuiNET.ImGuiKey.F4; + case SDL_Keycode.SDLK_F5: return ImGuiNET.ImGuiKey.F5; + case SDL_Keycode.SDLK_F6: return ImGuiNET.ImGuiKey.F6; + case SDL_Keycode.SDLK_F7: return ImGuiNET.ImGuiKey.F7; + case SDL_Keycode.SDLK_F8: return ImGuiNET.ImGuiKey.F8; + case SDL_Keycode.SDLK_F9: return ImGuiNET.ImGuiKey.F9; + case SDL_Keycode.SDLK_F10: return ImGuiNET.ImGuiKey.F10; + case SDL_Keycode.SDLK_F11: return ImGuiNET.ImGuiKey.F11; + case SDL_Keycode.SDLK_F12: return ImGuiNET.ImGuiKey.F12; + case SDL_Keycode.SDLK_F13: return ImGuiNET.ImGuiKey.F13; + case SDL_Keycode.SDLK_F14: return ImGuiNET.ImGuiKey.F14; + case SDL_Keycode.SDLK_F15: return ImGuiNET.ImGuiKey.F15; + case SDL_Keycode.SDLK_F16: return ImGuiNET.ImGuiKey.F16; + case SDL_Keycode.SDLK_F17: return ImGuiNET.ImGuiKey.F17; + case SDL_Keycode.SDLK_F18: return ImGuiNET.ImGuiKey.F18; + case SDL_Keycode.SDLK_F19: return ImGuiNET.ImGuiKey.F19; + case SDL_Keycode.SDLK_F20: return ImGuiNET.ImGuiKey.F20; + case SDL_Keycode.SDLK_F21: return ImGuiNET.ImGuiKey.F21; + case SDL_Keycode.SDLK_F22: return ImGuiNET.ImGuiKey.F22; + case SDL_Keycode.SDLK_F23: return ImGuiNET.ImGuiKey.F23; + case SDL_Keycode.SDLK_F24: return ImGuiNET.ImGuiKey.F24; + case SDL_Keycode.SDLK_AC_BACK: return ImGuiNET.ImGuiKey.AppBack; + case SDL_Keycode.SDLK_AC_FORWARD: return ImGuiNET.ImGuiKey.AppForward; + default: break; + } + return ImGuiNET.ImGuiKey.None; +} + + private void UpdateKeyModifiers(SDL_Keymod sdl_key_mods) + { + _imgui.GetIO(out var io); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModCtrl, sdl_key_mods.HasFlag(SDL_Keymod.SDL_KMOD_CTRL)); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModShift, sdl_key_mods.HasFlag(SDL_Keymod.SDL_KMOD_SHIFT)); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModAlt, sdl_key_mods.HasFlag(SDL_Keymod.SDL_KMOD_ALT)); + io.AddKeyEvent(ImGuiNET.ImGuiKey.ModSuper, sdl_key_mods.HasFlag(SDL_Keymod.SDL_KMOD_GUI)); + } + + private void UpdateMouseData() + { + _imgui.GetIO(out var io); + + var is_app_focused = SDL_GetWindowFlags(_window).HasFlag(SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS); // SDL 2.0.3 and non-windowed systems: single-viewport only + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(_window, (int)io.MousePos.X, (int)io.MousePos.Y); + } + } + + private void UpdateMouseCursor() + { + _imgui.GetIO(out var io); + if (io.ConfigFlags.HasFlag(ImGuiNET.ImGuiConfigFlags.NoMouseCursorChange)) + return; + + var imgui_cursor = _imgui.GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor.None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_HideCursor(); + } + else + { + // Show OS mouse cursor + var expected_cursor = _mouseCursors.Length > (int) imgui_cursor ? _mouseCursors[(int) imgui_cursor] : _mouseCursors[(int) ImGuiMouseCursor.Arrow]; + if (MouseLastCursor != expected_cursor) + { + SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113) + MouseLastCursor = expected_cursor; + } + SDL_ShowCursor(); + } + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.cs new file mode 100644 index 0000000..d20ba60 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Controller/ImGuiController.cs @@ -0,0 +1,61 @@ +using BUTR.CrashReport.Native; + +using ImGui; + +using OpenGLES3; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM.Controller; + +internal partial class ImGuiController : IDisposable +{ + private static readonly Dictionary> _instances = new(); + + private readonly Allocator _allocator = new(); + private readonly IntPtr _window; + private readonly GL _gl; + private readonly Emscripten.Emscripten _emscripten; + private readonly CmGui _imgui; + private readonly GLShaderProgram _shader; + private readonly uint _vboHandle; + private readonly uint _elementsHandle; + private uint _vertexArrayObject, _fontTextureId; + + public ImGuiController(IntPtr window, GL gl, Emscripten.Emscripten emscripten, CmGui imgui) + { + _instances[window] = new WeakReference(this); + + _window = window; + _gl = gl; + _emscripten = emscripten; + _imgui = imgui; + _shader = new GLShaderProgram(_gl, VertexShader, FragmentShader); + + _vboHandle = _gl.GenBuffer(); + _gl.CheckError(); + _elementsHandle = _gl.GenBuffer(); + _gl.CheckError(); + + var context = _imgui.CreateContext(); + _imgui.SetCurrentContext(context); + + RebuildFontAtlas(); + + SetupEmscripten(_window); + } + + public void Dispose() + { + _allocator.Dispose(); + + _shader.Dispose(); + + _gl.DeleteBuffer(_vboHandle); + _gl.CheckError(); + _gl.DeleteBuffer(_elementsHandle); + _gl.CheckError(); + _gl.DeleteVertexArray(_vertexArrayObject); + _gl.CheckError(); + _gl.DeleteTexture(_fontTextureId); + _gl.CheckError(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/CrashReportRendererUtilities.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CrashReportRendererUtilities.cs new file mode 100644 index 0000000..60ff2f4 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CrashReportRendererUtilities.cs @@ -0,0 +1,65 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.Html; + +using System.Runtime.InteropServices.JavaScript; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM; + +internal sealed partial class CrashReportRendererUtilities : ICrashReportRendererUtilities +{ + [JSImport("saveFile", "main.js")] + private static partial void SaveFile(byte[] data, string fileName); + + [JSImport("writeClipboard", "main.js")] + private static partial void WriteClipboard(string data); + + public bool IsDefaultDarkMode => true; + + private readonly CrashReportRendererCapabilities _capabilities = +#if WINDOWS + CrashReportRendererCapabilities.Dialogs | +#endif + CrashReportRendererCapabilities.CopyAsHtml | + CrashReportRendererCapabilities.Dialogs; + + public CrashReportRendererCapabilities Capabilities => _capabilities; + + public CrashReportRendererUtilities(CrashReportModel model, LogSourceModel[] logs) + { + if (!string.IsNullOrEmpty(model.Metadata.LoaderPluginProviderName)) + { + _capabilities |= CrashReportRendererCapabilities.PluginLoader; + } + + if (logs.Length > 0) + { + _capabilities |= CrashReportRendererCapabilities.Logs; + } + } + + public void Upload(CrashReportModel crashReport, ICollection logSources) { } + + public void CopyAsHtml(CrashReportModel crashReport, ICollection logSources) + { + var reportAsHtml = CrashReportHtml.Build(crashReport, logSources); + WriteClipboard(reportAsHtml); + } + + public void SaveAsHtml(CrashReportModel crashReport, ICollection logSources, bool addMiniDump, bool addLatestSave, bool addScreenshots, Stream stream) + { + var reportAsHtml = CrashReportHtml.Build(crashReport, logSources); + CreatorHtml.Create(crashReport, reportAsHtml, stream); + SaveFile(((MemoryStream) stream).ToArray(), "crashreport.html"); + } + + public void SaveAsZip(CrashReportModel crashReport, ICollection logSources, Stream stream) + { + CreatorZip.Create(crashReport, logSources, stream); + SaveFile(((MemoryStream) stream).ToArray(), "crashreport.zip"); + } + + public Stream SaveFileDialog(string filter, string defaultPath) + { + return new MemoryStream(); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/CreatorHtml.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CreatorHtml.cs new file mode 100644 index 0000000..5de3d06 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CreatorHtml.cs @@ -0,0 +1,40 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.Html; + +using System.IO.Compression; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM; + +internal static class CreatorHtml +{ + private static string CompressJson(string jsonModel) + { + using var compressedBase64Stream = new MemoryStream(); + + using (var base64Stream = new CryptoStream(compressedBase64Stream, new ToBase64Transform(), CryptoStreamMode.Write, true)) + using (var compressorStream = new GZipStream(base64Stream, CompressionLevel.Optimal, true)) + using (var streamWriter = new StreamWriter(compressorStream, Encoding.UTF8, 1024, true)) + { + streamWriter.Write(jsonModel); + } + + using (var streamReader = new StreamReader(compressedBase64Stream)) + { + compressedBase64Stream.Seek(0, SeekOrigin.Begin); + return streamReader.ReadToEnd(); + } + } + + public static void Create(CrashReportModel crashReport, string html, Stream stream) + { + var json = JsonSerializer.Serialize(crashReport, CustomJsonSerializerContext.Default.CrashReportModel); + + var report = CrashReportHtml.AddData(html, CompressJson(json)); + + using var streamWriter = new StreamWriter(stream); + streamWriter.Write(report); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/CreatorZip.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CreatorZip.cs new file mode 100644 index 0000000..7f7e34a --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CreatorZip.cs @@ -0,0 +1,20 @@ +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.Zip; + +using System.Text; +using System.Text.Json; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM; + +internal static class CreatorZip +{ + public static void Create(CrashReportModel crashReport, ICollection logSources, Stream stream) + { + using var crashReportStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(crashReport, CustomJsonSerializerContext.Default.CrashReportModel))); + using var logsStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(logSources, CustomJsonSerializerContext.Default.LogSourceModelArray))); + + using var archiveSteam = CrashReportZip.Build(crashReportStream, logsStream, Stream.Null, Stream.Null, Stream.Null); + archiveSteam.Seek(0, SeekOrigin.Begin); + archiveSteam.CopyTo(stream); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/CustomJsonSerializerContext.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CustomJsonSerializerContext.cs new file mode 100644 index 0000000..aedc4c0 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/CustomJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using BUTR.CrashReport.Models; + +using System.Text.Json.Serialization; + +namespace BUTR.CrashReport.Renderer.ImGui.WASM; + +[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)] +[JsonSerializable(typeof(CrashReportModel))] +[JsonSerializable(typeof(LogSourceModel[]))] +internal partial class CustomJsonSerializerContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/GlobalUsings.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/GlobalUsings.cs new file mode 100644 index 0000000..b82b0dc --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/GlobalUsings.cs @@ -0,0 +1,14 @@ +#if OPENGLES3 +global using static OpenGLES3.GL; +#endif +#if SDL2 +global using static SDL2.SDL; +#endif +#if SDL3 +global using static SDL3.SDL; +#endif +#if EMSCRIPTEN +global using static Emscripten.Emscripten; +#endif + +global using static ImGui.CmGui; \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Program.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Program.cs new file mode 100644 index 0000000..219fca9 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Program.cs @@ -0,0 +1,195 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Native; +using BUTR.CrashReport.Renderer.ImGui.Renderer; +using BUTR.CrashReport.Renderer.ImGui.WASM.Controller; + +using Emscripten; + +using ImGui; +using ImGui.Structures; + +using OpenGLES3; + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.JavaScript; +using System.Text.Json; + +[assembly: PInvokeDelegateLoader(typeof(CmGui), "cimgui")] + +[assembly: PInvokeDelegateLoader(typeof(Emscripten.Emscripten), "*")] + +namespace BUTR.CrashReport.Renderer.ImGui.WASM; + +using ImGuiRenderer = ImGuiRenderer, ImGuiListClipperWrapper>; + +public static partial class Program +{ + [JSImport("finishedLoading", "main.js")] + private static partial void FinishedLoading(); + + // https://localhost:7211/?arg=http%3A%2F%2Flocalhost%3A65530%2Fcrashreport.json + internal static async Task Main(string[] args) + { + var url = args.Length > 0 ? args[0] : throw new ArgumentException("URL to Crash Report JSON is required"); + var cr = await FetchAsync(url); + + _emscripten = CreateEmscripten(); + + _imgui = CreateCmGui(); + _renderer = CreateImGuiRenderer(cr, [], _imgui); + + _window = CreateWindow("BUTR Crash Report Renderer"u8, 800, 600); + _gl = CreateGL(_window); + _controller = CreateImGuiGLRenderer(_window, _gl, _imgui); + + FinishedLoading(); + + SetMainLoop(); + } + + //private static readonly HttpRequestOptionsKey> FetchRequestOptionsKey = new("WebAssemblyFetchOptions"); + private static async Task FetchAsync(string url) + { + using var client = new HttpClient(); + using var request = new HttpRequestMessage(HttpMethod.Get, url); + + /* + if (!request.Options.TryGetValue(FetchRequestOptionsKey, out var fetchOptions)) + { + fetchOptions = new Dictionary(StringComparer.Ordinal); + request.Options.Set(FetchRequestOptionsKey, fetchOptions); + } + + fetchOptions["mode"] = "no-cors"; + */ + + using var response = await client.SendAsync(request); + + return JsonSerializer.Deserialize(await response.Content.ReadAsStreamAsync(), CustomJsonSerializerContext.Default.CrashReportModel)!; + } + + private static unsafe void SetMainLoop() + { + _emscripten.emscripten_set_main_loop(&MainLoop, 0, 0); + } + + private static Emscripten.Emscripten _emscripten = default!; + private static CmGui _imgui = default!; + private static ImGuiController _controller = default!; + private static ImGuiRenderer _renderer = default!; + private static IntPtr _window = default!; + private static GL _gl = default!; + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void MainLoop() + { +#if SDL2 + while (SDL_PollEvent(out var e) != 0) +#elif SDL3 + while (SDL_PollEvent(out var e)) +#endif + { + _controller.ProcessEvent(e); + /* + switch (e.type) + { + case SDL_EventType.SDL_QUIT: + { + break; + } + case SDL_EventType.SDL_KEYDOWN: + { + switch (e.key.keysym.sym) + { + case SDL_Keycode.SDLK_ESCAPE: + case SDL_Keycode.SDLK_q: + break; + } + + break; + } + } + */ + } + + + _gl.ClearColor(0.1f, 0.2f, 0.3f, 1.0f); + _gl.Clear(ClearBufferMask.ColorBufferBit); + + _controller.NewFrame(); + _renderer.Render(); + _controller.Render(); + + SDL_GL_SwapWindow(_window); + } + + private static CmGui CreateCmGui() + { + var cmgui = new CmGui(); + cmgui.LoadFromPInvoke(); + return cmgui; + } + + private static ImGuiRenderer CreateImGuiRenderer(CrashReportModel cr, LogSourceModel[] logs, CmGui imgui) + { + return new ImGuiRenderer(imgui, imgui, imgui, imgui, imgui, imgui, cr, logs, new CrashReportRendererUtilities(cr, logs), () => { }); + } + + private static IntPtr CreateWindow(ReadOnlySpan title, int width, int height) + { +#if SDL2 + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 0); + + var flags = SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_SHOWN | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI; + + //SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SCALING, "1"); + //SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "1"); + //SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "1"); + + var window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags); +#elif SDL3 + SDL_Init(SDL_InitFlags.SDL_INIT_VIDEO); + //SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, 0x0004); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 0); + + var flags = SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY; + + var window = SDL_CreateWindow(title, width, height, flags); +#endif + + return window; + } + + private static unsafe GL CreateGL(IntPtr window) + { + var glContext = SDL_GL_CreateContext(window); + if (glContext == IntPtr.Zero) + throw new Exception("CouldNotCreateContext"); + + var gl = new GL(glContext, pointer => SDL_GL_GetProcAddress((byte*) pointer)); + + SDL_GL_MakeCurrent(window, glContext); + SDL_GL_SetSwapInterval(1); + + return gl; + } + + private static ImGuiController CreateImGuiGLRenderer(IntPtr window, GL gl, CmGui imgui) + { + return new ImGuiController(window, gl, _emscripten, imgui); + } + + private static Emscripten.Emscripten CreateEmscripten() + { + var emscripten = new Emscripten.Emscripten(); + emscripten.LoadFromPInvoke(); + return emscripten; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Properties/AssemblyInfo.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dbd151d --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +[assembly: System.Runtime.Versioning.SupportedOSPlatform("browser")] \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/Properties/launchSettings.json b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Properties/launchSettings.json new file mode 100644 index 0000000..762f810 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "BUTR.CrashReport.Renderer.ImGui.WASM": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:7211;http://localhost:5102", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/inc/helper.c b/src/BUTR.CrashReport.Renderer.ImGui.WASM/inc/helper.c new file mode 100644 index 0000000..1de6968 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/inc/helper.c @@ -0,0 +1,28 @@ +#include + +EMSCRIPTEN_KEEPALIVE +void custom_emscripten_set_element_style_size(const char* element_id, int width, int height) { + EM_ASM({ + const elementId = UTF8ToString($0); + const width = $1; + const height = $2; + + const element = document.getElementById(elementId); + if (element) { + element.style.width = width + 'px'; + element.style.height = height + 'px'; + } + }, element_id, width, height); +} + +EMSCRIPTEN_KEEPALIVE +void custom_emscripten_get_display_usable_bounds(int* width, int* height) { + MAIN_THREAD_EM_ASM({ + if ($0) { + HEAP32[$0 >> 2] = window.innerWidth; + } + if ($1) { + HEAP32[$1 >> 2] = window.innerHeight; + } + }, width, height); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/decode.min.js b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/decode.min.js new file mode 100644 index 0000000..8201f8c --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/decode.min.js @@ -0,0 +1 @@ +export let BrotliDecode = (() => { function f(f) { this.data = f, this.offset = 0 } let w = Int32Array.from([256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822, 854, 886, 920, 952, 984, 1016, 1048, 1080]), b = Int32Array.from([1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15]), j = Int32Array.from([0, 3, 2, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3]), l = Int32Array.from([0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3]), m = Int32Array.from([131072, 131076, 131075, 196610, 131072, 131076, 131075, 262145, 131072, 131076, 131075, 196610, 131072, 131076, 131075, 262149]), p = Int32Array.from([1, 5, 9, 13, 17, 25, 33, 41, 49, 65, 81, 97, 113, 145, 177, 209, 241, 305, 369, 497, 753, 1265, 2289, 4337, 8433, 16625]), q = Int32Array.from([2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 24]), o = Int16Array.from([0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24]), n = Int16Array.from([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24]), g = new Int16Array(2816); function k(f) { let w = -1, b = 16; for (; b > 0;)f >>> b != 0 && (w += b, f >>>= b), b >>= 1; return w + f } function s(f, w, b) { return 16 + w + 2 * (b << f) } function v(f, w, b) { if (f < b + (2 << w)) throw "maxDistance is too small"; let j = 4 + (f - b >> w), l = k(j) - 1; return ((l - 1 << 1 | j >> l & 1) - 1 << w) + (1 << w) + b + 16 } function t(f, w) { if (0 != f.j) throw "State MUST be uninitialized"; f.l = new Int32Array(3091), f.l[0] = 7, f.m = 3; let b = v(2147483644, 3, 120); f.p = new Int8Array(b), f.q = new Int32Array(b), f.input = w, function (f) { f.o = new Int8Array(4160), f.g = 0, f.k = new Int16Array(2080), f.s = 32, f.v = 2048, f.t = 0, E(f) }(f), f.j = 1 } function e(f) { if (f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), 0 != _(f, 1)) { let w = _(f, 3); return 0 == w ? 1 : _(f, w) + (1 << w) } return 0 } function d(f, w, b) { let j = f[w], l = b.g >>> b.s; j += 255 & l; let m = f[j] >> 16, p = 65535 & f[j]; return m <= 8 ? (b.s += m, p) : (j += p, j += (l & (1 << m) - 1) >>> 8, b.s += 8 + (f[j] >> 16), 65535 & f[j]) } function H(f, w, b) { b.s >= 16 && (b.g = b.k[b.v++] << 16 | b.g >>> 16, b.s -= 16); let j = d(f, w, b), l = q[j]; return b.s >= 16 && (b.g = b.k[b.v++] << 16 | b.g >>> 16, b.s -= 16), p[j] + (l <= 16 ? _(b, l) : X(b, l)) } function a(f, w) { let b = f[w]; for (; w > 0; w--)f[w] = f[w - 1]; f[0] = b } function u(f, w, j, l, p) { let q = new Int32Array(f), o = new Int32Array(18), n = 32, g = 0; for (let f = w; f < 18 && n > 0; f++) { let w = b[f]; p.s >= 16 && (p.g = p.k[p.v++] << 16 | p.g >>> 16, p.s -= 16); let j = p.g >>> p.s & 15; p.s += m[j] >> 16; let l = 65535 & m[j]; o[w] = l, 0 != l && (n -= 32 >> l, g++) } if (0 != n && 1 != g) throw "Corrupted Huffman code histogram"; return function (f, w, b, j) { let l = 0, m = 8, p = 0, q = 0, o = 32768, n = new Int32Array(33); for (c(n, n.length - 1, 5, f, 18); l < w && o > 0;) { j.v > 2030 && C(j), j.s >= 16 && (j.g = j.k[j.v++] << 16 | j.g >>> 16, j.s -= 16); let f = j.g >>> j.s & 31; j.s += n[f] >> 16; let g = 65535 & n[f]; if (g < 16) p = 0, b[l++] = g, 0 != g && (m = g, o -= 32768 >> g); else { let f = g - 14, n = 0; 16 == g && (n = m), q != n && (p = 0, q = n); let k = p; p > 0 && (p -= 2, p <<= f), j.s >= 16 && (j.g = j.k[j.v++] << 16 | j.g >>> 16, j.s -= 16), p += _(j, f) + 3; let s = p - k; if (l + s > w) throw "symbol + repeatDelta > numSymbols"; for (let f = 0; f < s; f++)b[l++] = q; 0 != q && (o -= s << 15 - q) } } if (0 != o) throw "Unused space"; b.fill(0, l, w) }(o, f, q, p), c(j, l, 8, q, f) } function z(f, w, b, j, l) { l.v > 2030 && C(l), l.s >= 16 && (l.g = l.k[l.v++] << 16 | l.g >>> 16, l.s -= 16); let m = _(l, 2); return 1 == m ? function (f, w, b, j, l) { let m = new Int32Array(w), p = new Int32Array(4), q = 1 + k(f - 1), o = _(l, 2) + 1; for (let f = 0; f < o; f++) { l.s >= 16 && (l.g = l.k[l.v++] << 16 | l.g >>> 16, l.s -= 16); let b = _(l, q); if (b >= w) throw "Can't readHuffmanCode"; p[f] = b } !function (f, w) { for (let b = 0; b < w - 1; ++b)for (let j = b + 1; j < w; ++j)if (f[b] == f[j]) throw "Duplicate simple Huffman code symbol" }(p, o); let n = o; switch (4 == o && (n += _(l, 1)), n) { case 1: m[p[0]] = 1; break; case 2: m[p[0]] = 1, m[p[1]] = 1; break; case 3: m[p[0]] = 1, m[p[1]] = 2, m[p[2]] = 2; break; case 4: m[p[0]] = 2, m[p[1]] = 2, m[p[2]] = 2, m[p[3]] = 2; break; case 5: m[p[0]] = 1, m[p[1]] = 2, m[p[2]] = 3, m[p[3]] = 3 }return c(b, j, 8, m, w) }(f, w, b, j, l) : u(w, m, b, j, l) } function h(f, b, j) { j.v > 2030 && C(j); let l = e(j) + 1; if (1 == l) return b.fill(0, 0, f), l; j.s >= 16 && (j.g = j.k[j.v++] << 16 | j.g >>> 16, j.s -= 16); let m = 0; 0 != _(j, 1) && (m = _(j, 4) + 1); let p = l + m, q = w[p + 31 >> 5], o = new Int32Array(q + 1), n = o.length - 1; z(p, p, o, n, j); for (let w = 0; w < f;) { j.v > 2030 && C(j), j.s >= 16 && (j.g = j.k[j.v++] << 16 | j.g >>> 16, j.s -= 16); let l = d(o, n, j); if (0 == l) b[w] = 0, w++; else if (l <= m) { j.s >= 16 && (j.g = j.k[j.v++] << 16 | j.g >>> 16, j.s -= 16); let m = (1 << l) + _(j, l); for (; 0 != m;) { if (w >= f) throw "Corrupted context map"; b[w] = 0, w++, m-- } } else b[w] = l - m, w++ } return j.s >= 16 && (j.g = j.k[j.v++] << 16 | j.g >>> 16, j.s -= 16), 1 == _(j, 1) && function (f, w) { let b = new Int32Array(256); for (let f = 0; f < 256; f++)b[f] = f; for (let j = 0; j < w; j++) { let w = 255 & f[j]; f[j] = b[w], 0 != w && a(b, w) } }(b, f), l } function i(f, w, b) { let j = f.H, l = 4 + 2 * w; f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let m = d(f.l, 2 * w, f), p = H(f.l, 2 * w + 1, f); return 1 == m ? m = j[l + 1] + 1 : 0 == m ? m = j[l] : m -= 2, m >= b && (m -= b), j[l] = j[l + 1], j[l + 1] = m, p } function r(f) { f.u = i(f, 0, f.h); let w = f.H[5]; f.i = w << 6, f.I = 255 & f.R[f.i]; let b = f.K[w]; f.T = b << 9, f.W = f.T + 256 } function I(f) { f.B = i(f, 1, f.Y), f.S = f.H[7] } function R(f) { f.A = i(f, 2, f.V), f.D = f.H[9] << 2 } function K(f) { if (0 != f.F) return f.P = 10, void (f.j = 12); f.U = new Int32Array(0), f.C = new Int32Array(0), f.J = new Int32Array(0), f.v > 2030 && C(f), function (f) { if (f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), f.F = _(f, 1), f._ = 0, f.X = 0, f.M = 0, 0 != f.F && 0 != _(f, 1)) return; let w = _(f, 2) + 4; if (7 == w) { if (f.M = 1, 0 != _(f, 1)) throw "Corrupted reserved bit"; let w = _(f, 2); if (0 == w) return; for (let b = 0; b < w; b++) { f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let j = _(f, 8); if (0 == j && b + 1 == w && w > 1) throw "Exuberant nibble"; f._ |= j << 8 * b } } else for (let b = 0; b < w; b++) { f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let j = _(f, 4); if (0 == j && b + 1 == w && w > 4) throw "Exuberant nibble"; f._ |= j << 4 * b } f._++, 0 == f.F && (f.X = _(f, 1)) }(f), 0 == f._ && 0 == f.M || (0 != f.X || 0 != f.M ? (G(f), f.j = 0 != f.M ? 5 : 6) : f.j = 3, 0 == f.M && (f.G += f._, f.G > 1 << 30 && (f.G = 1 << 30), f.O < f.Z && function (f) { let w = f.Z; if (w > f.G) { let b = f.G; for (; w >> 1 > b;)w >>= 1; 0 == f.F && w < 16384 && f.Z >= 16384 && (w = 16384) } if (w <= f.O) return; let b = new Int8Array(w + 37); 0 != f.L.length && b.set(f.L.subarray(0, 0 + f.O), 0), f.L = b, f.O = w }(f))) } function T(f, w, b) { let j = f.l[2 * w]; if (b <= 1) return f.l[2 * w + 1] = j, f.l[2 * w + 2] = j, 1 << 28; let l = b + 2; j += z(l, l, f.l, 2 * w, f), f.l[2 * w + 1] = j; return j += z(26, 26, f.l, 2 * w + 1, f), f.l[2 * w + 2] = j, H(f.l, 2 * w + 1, f) } function y(f) { f.h = e(f) + 1, f.u = T(f, 0, f.h), f.Y = e(f) + 1, f.B = T(f, 1, f.Y), f.V = e(f) + 1, f.A = T(f, 2, f.V), f.v > 2030 && C(f), f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), f.N = _(f, 2), f.$ = _(f, 4) << f.N, f.K = new Int8Array(f.h); for (let w = 0; w < f.h;) { let b = ff(w + 96, f.h); for (; w < b; ++w)f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), f.K[w] = _(f, 2); f.v > 2030 && C(f) } f.R = new Int8Array(f.h << 6); let w = h(f.h << 6, f.R, f); f.ff = 1; for (let w = 0; w < f.h << 6; w++)if (f.R[w] != w >> 6) { f.ff = 0; break } f.wf = new Int8Array(f.V << 2); let b = h(f.V << 2, f.wf, f); f.U = Y(256, 256, w, f), f.C = Y(704, 704, f.Y, f); let j = s(f.N, f.$, 24), l = j; 1 == f.bf && (j = s(f.N, f.$, 62), l = v(2147483644, f.N, f.$)), f.J = Y(j, l, b, f), function (f, w) { let b = f.p, j = f.q, l = f.N, m = f.$, p = 1 << l, q = 1, o = 0, n = 16; for (let f = 0; f < m; ++f)b[n] = 0, j[n] = f + 1, ++n; for (; n < w;) { let f = m + ((2 + o << q) - 4 << l) + 1; for (let w = 0; w < p; ++w)b[n] = q, j[n] = f + w, ++n; q += o, o ^= 1 } }(f, l), f.i = 0, f.D = 0, f.T = 512 * f.K[0], f.W = f.T + 256, f.I = 0, f.S = 0, f.H[4] = 1, f.H[5] = 0, f.H[6] = 1, f.H[7] = 0, f.H[8] = 1, f.H[9] = 0 } function W(f) { let w = f.L; if (f._ <= 0) return M(f), void (f.j = 2); let b = ff(f.O - f.jf, f._); if (function (f, w, b, j) { if (0 != (7 & f.s)) throw "Unaligned copyBytes"; for (; 32 != f.s && 0 != j;)w[b++] = f.g >>> f.s, f.s += 8, j--; if (0 == j) return; let l = ff(x(f), j >> 1); if (l > 0) { let m = f.v << 1, p = l << 1; w.set(f.o.subarray(m, m + p), b), b += p, j -= p, f.v += l } if (0 == j) return; if (x(f) > 0) { for (f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); 0 != j;)w[b++] = f.g >>> f.s, f.s += 8, j--; return void J(f, 0) } for (; j > 0;) { let l = wf(f.input, w, b, j); if (-1 == l) throw "Unexpected end of input"; b += l, j -= l } }(f, w, f.jf, b), f._ -= b, f.jf += b, f.jf == f.O) return f.P = 6, void (f.j = 12); M(f), f.j = 2 } function B(f) { let w = ff(f.lf - f.mf, f.pf - f.qf); return 0 != w && (f.nf.set(f.L.subarray(f.qf, f.qf + w), f.gf + f.mf), f.mf += w, f.qf += w), f.mf < f.lf ? 1 : 0 } function Y(f, b, j, l) { let m = w[b + 31 >> 5], p = new Int32Array(j + j * m), q = j; for (let w = 0; w < j; ++w)p[w] = q, q += z(f, b, p, w, l); return p } function S(f) { let w = f.O; return 0 != f.kf && (w = ff(w, f.qf + f.lf - f.mf)), w } function A(f, w) { if (f.sf > 2147483644) throw "Invalid backward reference"; let b = f.sf - f.maxDistance - 1 - f.vf; if (b < 0) !function (f, w, b) { -1 == f.tf && function (f) { f.ef = new Int8Array(256); let w = 8; for (; f.vf - 1 >>> w != 0;)w++; w -= 8, f.tf = w; let b = 0, j = 0; for (; b < f.vf;) { for (; f.df[j + 1] < b;)j++; f.ef[b >>> w] = j, b += 1 << w } }(f); let j = f.ef[w >>> f.tf]; for (; w >= f.df[j + 1];)j++; if (f.vf > w + b) throw "Invalid backward reference"; f.m = f.m + 1 & 3, f.H[f.m] = f.sf, f._ -= b, f.Hf = j, f.af = w - f.df[j], f.uf = b, f.zf = 0 }(f, -b - 1, f.hf), f.j = 14; else { let j = L, l = f.hf; if (l > 31) throw "Invalid backward reference"; let m = $[l]; if (0 == m) throw "Invalid backward reference"; let p = N[l], q = b >>> m; p += (b & (1 << m) - 1) * l; let o = F; if (q >= o.if) throw "Invalid backward reference"; let n = function (f, w, b, j, l, m, p) { let q = w, o = m.rf, n = m.If, g = m.Rf, k = 3 * p, s = o[k], v = o[k + 1], t = o[k + 2], e = g[s], d = g[s + 1], H = g[t], a = g[t + 1], u = v - 11, z = v - 0; (u < 1 || u > 9) && (u = 0); (z < 1 || z > 9) && (z = 0); for (; e != d;)f[q++] = n[e++]; u > l && (u = l); j += u, l -= u; let h = l -= z; for (; h > 0;)f[q++] = b[j++], h--; if (10 == v || 11 == v) { let w = q - l; for (10 == v && (l = 1); l > 0;) { let b = 255 & f[w]; b < 192 ? (b >= 97 && b <= 122 && (f[w] ^= 32), w += 1, l -= 1) : b < 224 ? (f[w + 1] ^= 32, w += 2, l -= 2) : (f[w + 2] ^= 5, w += 3, l -= 3) } } else if (21 == v || 22 == v) { let w = q - l, b = m.Kf[p], j = 16777216 - (32768 & b) + (32767 & b); for (; l > 0;) { let b = 1, m = 255 & f[w]; if (m < 128) j += m, f[w] = 127 & j; else if (m < 192); else if (m < 224) if (l >= 2) { let l = f[w + 1]; j += 63 & l | (31 & m) << 6, f[w] = 192 | j >> 6 & 31, f[w + 1] = 192 & l | 63 & j, b = 2 } else b = l; else if (m < 240) if (l >= 3) { let l = f[w + 1], p = f[w + 2]; j += 63 & p | (63 & l) << 6 | (15 & m) << 12, f[w] = 224 | j >> 12 & 15, f[w + 1] = 192 & l | j >> 6 & 63, f[w + 2] = 192 & p | 63 & j, b = 3 } else b = l; else if (m < 248) if (l >= 4) { let l = f[w + 1], p = f[w + 2], q = f[w + 3]; j += 63 & q | (63 & p) << 6 | (63 & l) << 12 | (7 & m) << 18, f[w] = 240 | j >> 18 & 7, f[w + 1] = 192 & l | j >> 12 & 63, f[w + 2] = 192 & p | j >> 6 & 63, f[w + 3] = 192 & q | 63 & j, b = 4 } else b = l; w += b, l -= b, 21 == v && (l = 0) } } for (; H != a;)f[q++] = n[H++]; return q - w }(f.L, f.jf, j, p, l, o, q); if (f.jf += n, f._ -= n, f.jf >= w) return f.P = 4, void (f.j = 12); f.j = 4 } } function V(f, w) { let b = f.jf, j = b; for (; f.uf != f.zf;) { let j = w - b, n = f.df[f.Hf + 1] - f.df[f.Hf] - f.af, g = f.uf - f.zf; if (g > n && (g = n), g > j && (g = j), l = f.L, m = b, p = f.Tf[f.Hf], q = f.af, o = f.af + g, l.set(p.slice(q, o), m), b += g, f.af += g, f.zf += g, g == n && (f.Hf++, f.af = 0), b >= w) break } var l, m, p, q, o; return b - j } function D(f) { if (0 == f.j) throw "Can't decompress until initialized"; if (11 == f.j) throw "Can't decompress after close"; if (1 == f.j) { let w = function (f) { let w = f.bf; if (f.bf = 0, f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), 0 == _(f, 1)) return 16; let b = _(f, 3); return 0 != b ? 17 + b : (b = _(f, 3), 0 != b ? 1 == b ? 0 == w ? -1 : (f.bf = 1, 1 == _(f, 1) ? -1 : (b = _(f, 6), b < 10 || b > 30 ? -1 : b)) : 8 + b : 17) }(f); if (-1 == w) throw "Invalid 'windowBits' code"; f.Z = 1 << w, f.yf = f.Z - 16, f.j = 2 } let w = S(f), b = f.O - 1, m = f.L; for (; 10 != f.j;)switch (f.j) { case 2: if (f._ < 0) throw "Invalid metablock length"; K(f), w = S(f), b = f.O - 1, m = f.L; continue; case 3: y(f), f.j = 4; case 4: if (f._ <= 0) { f.j = 2; continue } f.v > 2030 && C(f), 0 == f.B && I(f), f.B--, f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let p = d(f.C, f.S, f) << 2, q = g[p], o = g[p + 1], n = g[p + 2]; f.Wf = g[p + 3], f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let k = 255 & q; f.Bf = o + (k <= 16 ? _(f, k) : X(f, k)), f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let s = q >> 8; f.hf = n + (s <= 16 ? _(f, s) : X(f, s)), f.Yf = 0, f.j = 7; case 7: if (0 != f.ff) { for (; f.Yf < f.Bf;)if (f.v > 2030 && C(f), 0 == f.u && r(f), f.u--, f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), m[f.jf] = d(f.U, f.I, f), f.jf++, f.Yf++, f.jf >= w) { f.P = 7, f.j = 12; break } } else { let j = 255 & m[f.jf - 1 & b], l = 255 & m[f.jf - 2 & b]; for (; f.Yf < f.Bf;) { f.v > 2030 && C(f), 0 == f.u && r(f); let b = O[f.T + j] | O[f.W + l], p = 255 & f.R[f.i + b]; if (f.u--, l = j, f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), j = d(f.U, p, f), m[f.jf] = j, f.jf++, f.Yf++, f.jf >= w) { f.P = 7, f.j = 12; break } } } if (7 != f.j) continue; if (f._ -= f.Bf, f._ <= 0) { f.j = 4; continue } let v = f.Wf; if (v < 0) f.sf = f.H[f.m]; else { f.v > 2030 && C(f), 0 == f.A && R(f), f.A--, f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16); let w = 255 & f.wf[f.D + v]; if (v = d(f.J, w, f), v < 16) { let w = f.m + j[v] & 3; if (f.sf = f.H[w] + l[v], f.sf < 0) throw "Negative distance" } else { let w, b = f.p[v]; f.s + b <= 32 ? w = _(f, b) : (f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), w = b <= 16 ? _(f, b) : X(f, b)), f.sf = f.q[v] + (w << f.N) } } if (f.maxDistance != f.yf && f.jf < f.yf ? f.maxDistance = f.jf : f.maxDistance = f.yf, f.sf > f.maxDistance) { f.j = 9; continue } if (v > 0 && (f.m = f.m + 1 & 3, f.H[f.m] = f.sf), f.hf > f._) throw "Invalid backward reference"; f.Yf = 0, f.j = 8; case 8: let t = f.jf - f.sf & b, e = f.jf, H = f.hf - f.Yf, a = t + H, u = e + H; if (a < b && u < b) { if (H < 12 || a > e && u > t) for (let f = 0; f < H; f += 4)m[e++] = m[t++], m[e++] = m[t++], m[e++] = m[t++], m[e++] = m[t++]; else m.copyWithin(e, t, a); f.Yf += H, f._ -= H, f.jf += H } else for (; f.Yf < f.hf;)if (m[f.jf] = m[f.jf - f.sf & b], f._--, f.jf++, f.Yf++, f.jf >= w) { f.P = 8, f.j = 12; break } 8 == f.j && (f.j = 4); continue; case 9: A(f, w); continue; case 14: if (f.jf += V(f, w), f.jf >= w) return f.P = 14, void (f.j = 12); f.j = 4; continue; case 5: for (; f._ > 0;)f.v > 2030 && C(f), f.s >= 16 && (f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16), _(f, 8), f._--; f.j = 2; continue; case 6: W(f); continue; case 12: f.pf = ff(f.jf, f.O), f.j = 13; case 13: if (0 == B(f)) return; f.jf >= f.yf && (f.maxDistance = f.yf), f.jf >= f.O && (f.jf > f.O && m.copyWithin(0, f.O, f.jf), f.jf &= b, f.qf = 0), f.j = f.P; continue; default: throw "Unexpected state " + f.j }if (10 == f.j) { if (f._ < 0) throw "Invalid metablock length"; G(f), J(f, 1) } } !function (f) { let w = new Int16Array(24), b = new Int16Array(24); b[0] = 2; for (let f = 0; f < 23; ++f)w[f + 1] = w[f] + (1 << o[f]), b[f + 1] = b[f] + (1 << n[f]); for (let j = 0; j < 704; ++j) { let l = j >>> 6, m = -4; l >= 2 && (l -= 2, m = 0); let p = (170064 >>> 2 * l & 3) << 3 | j >>> 3 & 7, q = (156228 >>> 2 * l & 3) << 3 | 7 & j, g = b[q], k = m + (g > 4 ? 3 : g - 2), s = 4 * j; f[s + 0] = o[p] | n[q] << 8, f[s + 1] = w[p], f[s + 2] = b[q], f[s + 3] = k } }(g); let F = new function (f, w, b) { this.if = 0, this.rf = new Int32Array(0), this.If = new Int8Array(0), this.Rf = new Int32Array(0), this.Kf = new Int16Array(0), this.if = f, this.rf = new Int32Array(3 * f), this.Kf = new Int16Array(f), this.If = new Int8Array(w), this.Rf = new Int32Array(b + 1) }(121, 167, 50); function Q(f, w) { let b = 1 << w - 1; for (; 0 != (f & b);)b >>= 1; return (f & b - 1) + b } function P(f, w, b, j, l) { do { f[w + (j -= b)] = l } while (j > 0) } function U(f, w, b) { let j = 1 << w - b; for (; w < 15 && (j -= f[w], !(j <= 0));)w++, j <<= 1; return w - b } function c(f, w, b, j, l) { let m, p, q = f[w], o = new Int32Array(l), n = new Int32Array(16), g = new Int32Array(16); for (p = 0; p < l; p++)n[j[p]]++; g[1] = 0; for (let f = 1; f < 15; f++)g[f + 1] = g[f] + n[f]; for (p = 0; p < l; p++)0 != j[p] && (o[g[j[p]]++] = p); let k = b, s = 1 << k, v = s; if (1 == g[15]) { for (m = 0; m < v; m++)f[q + m] = o[0]; return v } m = 0, p = 0; for (let w = 1, j = 2; w <= b; w++, j <<= 1)for (; n[w] > 0; n[w]--)P(f, q + m, j, s, w << 16 | o[p++]), m = Q(m, w); let t = v - 1, e = -1, d = q; for (let w = b + 1, j = 2; w <= 15; w++, j <<= 1)for (; n[w] > 0; n[w]--)(m & t) != e && (d += s, k = U(n, w, b), s = 1 << k, v += s, e = m & t, f[q + e] = k + b << 16 | d - q - e), P(f, d + (m >> b), j, s, w - b << 16 | o[p++]), m = Q(m, w); return v } function C(f) { if (0 != f.t) { if (x(f) >= -2) return; throw "No more input" } let w = f.v << 1, b = 4096 - w; for (f.o.copyWithin(0, w, 4096), f.v = 0; b < 4096;) { let w = 4096 - b, j = wf(f.input, f.o, b, w); if (j <= 0) { f.t = 1, f.Sf = b, b += 1; break } b += j } !function (f, w) { let b = f.o, j = w >> 1, l = f.k; for (let f = 0; f < j; ++f)l[f] = 255 & b[2 * f] | (255 & b[2 * f + 1]) << 8 }(f, b) } function J(f, w) { if (0 == f.t) return; let b = (f.v << 1) + (f.s + 7 >> 3) - 4; if (b > f.Sf) throw "Read after end"; if (0 != w && b != f.Sf) throw "Unused bytes after end" } function _(f, w) { let b = f.g >>> f.s & (1 << w) - 1; return f.s += w, b } function X(f, w) { let b = _(f, 16); return f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16, b | _(f, w - 16) << 16 } function E(f) { f.v > 2030 && C(f), J(f, 0), f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16, f.g = f.k[f.v++] << 16 | f.g >>> 16, f.s -= 16 } function M(f) { 32 == f.s && E(f) } function G(f) { let w = 32 - f.s & 7; if (0 != w) { if (0 != _(f, w)) throw "Corrupted padding bits" } } function x(f) { let w = 2048; return 0 != f.t && (w = f.Sf + 1 >> 1), w - f.v } !function (f, w, b, j, l) { let m = j.length, p = 1, q = 0; for (let b = 0; b < m; ++b) { let l = j.charCodeAt(b); 35 == l ? w[p++] = q : f[q++] = l } for (let f = 0; f < 363; ++f)b[f] = l.charCodeAt(f) - 32 }(F.If, F.Rf, F.rf, '# #s #, #e #.# the #.com/#\xc2\xa0# of # and # in # to #"#">#\n#]# for # a # that #. # with #\'# from # by #. The # on # as # is #ing #\n\t#:#ed #(# at #ly #="# of the #. This #,# not #er #al #=\'#ful #ive #less #est #ize #ous #', " !! ! , *! &! \" ! ) * * - ! # ! #!*! + ,$ ! - % . / # 0 1 . \" 2 3!* 4% ! # / 5 6 7 8 0 1 & $ 9 + : ; < ' != > ?! 4 @ 4 2 & A *# ( B C& ) % ) !*# *-% A +! *. D! %' & E *6 F G% ! *A *% H! D I!+! J!+ K +- *4! A L!*4 M N +6 O!*% +.! K *G P +%( ! G *D +D Q +# *K!*G!+D!+# +G +A +4!+% +K!+4!*D!+K!*K"); let O = new Int32Array(2048); function Z() { this.L = new Int8Array(0), this.K = new Int8Array(0), this.R = new Int8Array(0), this.wf = new Int8Array(0), this.p = new Int8Array(0), this.nf = new Int8Array(0), this.o = new Int8Array(0), this.k = new Int16Array(0), this.Af = new Int32Array(0), this.H = new Int32Array(0), this.l = new Int32Array(0), this.U = new Int32Array(0), this.C = new Int32Array(0), this.J = new Int32Array(0), this.q = new Int32Array(0), this.j = 0, this.P = 0, this.g = 0, this.s = 0, this.v = 0, this.Sf = 0, this.t = 0, this._ = 0, this.F = 0, this.X = 0, this.M = 0, this.u = 0, this.h = 0, this.B = 0, this.Y = 0, this.A = 0, this.V = 0, this.jf = 0, this.maxDistance = 0, this.m = 0, this.ff = 0, this.I = 0, this.S = 0, this.Yf = 0, this.Bf = 0, this.i = 0, this.D = 0, this.T = 0, this.W = 0, this.Wf = 0, this.$ = 0, this.N = 0, this.sf = 0, this.hf = 0, this.yf = 0, this.Z = 0, this.O = 0, this.G = 0, this.gf = 0, this.lf = 0, this.mf = 0, this.qf = 0, this.pf = 0, this.kf = 0, this.bf = 0, this.Vf = 0, this.vf = 0, this.Hf = 0, this.af = 0, this.uf = 0, this.zf = 0, this.Tf = new Array(0), this.df = new Int32Array(0), this.tf = 0, this.ef = new Int8Array(0), this.input = null, this.L = new Int8Array(0), this.H = new Int32Array(10), this.H[0] = 16, this.H[1] = 15, this.H[2] = 11, this.H[3] = 4 } !function (f, w, b) { for (let w = 0; w < 256; ++w)f[w] = 63 & w, f[512 + w] = w >> 2, f[1792 + w] = 2 + (w >> 6); for (let b = 0; b < 128; ++b)f[1024 + b] = 4 * (w.charCodeAt(b) - 32); for (let w = 0; w < 64; ++w)f[1152 + w] = 1 & w, f[1216 + w] = 2 + (1 & w); let j = 1280; for (let w = 0; w < 19; ++w) { let l = 3 & w, m = b.charCodeAt(w) - 32; for (let w = 0; w < m; ++w)f[j++] = l } for (let w = 0; w < 16; ++w)f[1792 + w] = 1, f[2032 + w] = 6; f[1792] = 0, f[2047] = 7; for (let w = 0; w < 256; ++w)f[1536 + w] = f[1792 + w] << 3 }(O, " !! ! \"#$##%#$&'##(#)#++++++++++((&*'##,---,---,-----,-----,-----&#'###.///.///./////./////./////&#'# ", "A/* ': & : $ \x81 @"); let L = null, N = new Int32Array(32), $ = new Int32Array(32); { let f = new Int8Array(122784), w = new Int32Array(25); !function (f, w, b, j, l, m) { let p = function (f) { let w = f.length, b = new Int8Array(w); for (let j = 0; j < w; ++j)b[j] = f.charCodeAt(j); return b }(w + b); if (p.length != f.length) throw "Corrupted brotli dictionary"; let q = 0, o = j.length; for (let f = 0; f < o; f += 2) { let w = j.charCodeAt(f) - 36, b = j.charCodeAt(f + 1) - 36; for (let f = 0; f < w; ++f)p[q] ^= 3, q++; for (let f = 0; f < b; ++f)p[q] ^= 236, q++ } for (let f = 0; f < m.length; ++f)l[f] = m.charCodeAt(f) - 65; f.set(p) }(f, 'wjnfgltmojefofewab`h`lgfgbwbpkltlmozpjwf`jwzlsfmivpwojhfeqfftlqhwf{wzfbqlufqalgzolufelqnallhsobzojufojmfkfosklnfpjgfnlqftlqgolmdwkfnujftejmgsbdfgbzpevookfbgwfqnfb`kbqfbeqlnwqvfnbqhbaofvslmkjdkgbwfobmgmftpfufmmf{w`bpfalwkslpwvpfgnbgfkbmgkfqftkbwmbnfOjmhaoldpjyfabpfkfognbhfnbjmvpfq$*#(klogfmgptjwkMftpqfbgtfqfpjdmwbhfkbufdbnfpffm`boosbwktfoosovpnfmvejonsbqwiljmwkjpojpwdllgmffgtbzptfpwilapnjmgboploldlqj`kvpfpobpwwfbnbqnzellghjmdtjoofbpwtbqgafpwejqfSbdfhmltbtbz-smdnlufwkbmolbgdjufpfoemlwfnv`keffgnbmzql`hj`lmlm`follhkjgfgjfgKlnfqvofklpwbib{jmel`ovaobtpofppkboeplnfpv`kylmf233&lmfp`bqfWjnfqb`faovfelvqtffheb`fklsfdbufkbqgolpwtkfmsbqhhfswsbpppkjsqllnKWNOsobmWzsfglmfpbufhffseobdojmhplogejufwllhqbwfwltmivnswkvpgbqh`bqgejofefbqpwbzhjoowkbweboobvwlfufq-`lnwbohpklsulwfgffsnlgfqfpwwvqmalqmabmgefooqlpfvqo+phjmqlof`lnfb`wpbdfpnffwdlog-isdjwfnubqzefowwkfmpfmggqlsUjft`lsz2-3!?,b=pwlsfopfojfpwlvqsb`h-djesbpw`pp!pfwp6s{8-ip<73s{je#+pllmpfbwmlmfwvafyfqlpfmwqffgeb`wjmwldjewkbqn2;s{`bnfkjooalogyllnuljgfbpzqjmdejoosfbhjmjw`lpw0s{8ib`hwbdpajwpqloofgjwhmftmfbq?"..dqltIPLMgvwzMbnfpbofzlv#olwpsbjmibyy`logfzfpejpkttt-qjphwbapsqfu23s{qjpf16s{Aovfgjmd033/abooelqgfbqmtjogal{-ebjqob`hufqpsbjqivmfwf`kje+"sj`hfujo\'+! tbqnolqgglfpsvoo/333jgfbgqbtkvdfpslwevmgavqmkqfe`foohfzpwj`hklvqolppevfo21s{pvjwgfboQPP!bdfgdqfzDFW!fbpfbjnpdjqobjgp;s{8mbuzdqjgwjsp :::tbqpobgz`bqp*8#~sksolpfmvooubpwtjmgQPP#tfbqqfozaffmpbnfgvhfmbpb`bsftjpkdvoeW109kjwppolwdbwfhj`haovqwkfz26s{$$*8*8!=npjftjmpajqgplqwafwbpffhW2;9lqgpwqffnboo53s{ebqn\x0elupalzpX3^-$*8!SLPWafbqhjgp*8~~nbqzwfmg+VH*rvbgyk9\n.pjy....sqls$*8\x0eojewW2:9uj`fbmgzgfaw=QPPsllomf`haoltW259gllqfuboW249ofwpebjolqbosloomlub`lopdfmf#\x0elxplewqlnfwjooqlpp?k0=slvqebgfsjmh?wq=njmj*\x7f"+njmfyk9\x04abqpkfbq33*8njoh#..=jqlmeqfggjphtfmwpljosvwp,ip,klozW119JPAMW139bgbnpffp?k1=iplm$/#$`lmwW129#QPPollsbpjbnllm?,s=plvoOJMFelqw`bqwW279?k2=;3s{"..?:s{8W379njhf975Ymj`fjm`kZlqhqj`fyk9\b$**8svqfnbdfsbqbwlmfalmg904Y\\le\\$^*8333/yk9\vwbmhzbqgaltoavpk965YIbub03s{\t\x7f~\t&@0&907YifeeF[SJ`bpkujpbdloepmltyk9\x05rvfq-`pppj`hnfbwnjm-ajmggfookjqfsj`pqfmw905YKWWS.132elwltloeFMG#{al{967YALGZgj`h8\t~\tf{jw906Yubqpafbw$~*8gjfw:::8bmmf~~?,Xj^-Obmdhn.^tjqfwlzpbggppfbobof{8\t\n~f`klmjmf-lqd336*wlmziftppbmgofdpqlle333*#133tjmfdfbqgldpallwdbqz`vwpwzofwfnswjlm-{no`l`hdbmd\'+$-63s{Sk-Gnjp`bobmolbmgfphnjofqzbmvmj{gjp`*8~\tgvpw`ojs*-\t\t43s{.133GUGp4^=?wbsfgfnlj((*tbdffvqlskjolswpklofEBRpbpjm.15WobapsfwpVQO#avoh`llh8~\x0e\tKFBGX3^*baaqivbm+2:;ofpkwtjm?,j=plmzdvzpev`hsjsf\x7f.\t"331*mgltX2^8X^8\tOld#pbow\x0e\t\n\nabmdwqjnabwk*x\x0e\t33s{\t~*8hl9\0effpbg=\x0ep9,,#X^8wloosovd+*x\tx\x0e\t#-ip$133sgvboalbw-ISD*8\t~rvlw*8\t\t$*8\t\x0e\t~\x0e1327132613251324132;132:13131312131113101317131613151314131;131:130313021301130013071306130513041320132113221323133:133;133413351336133713301331133213332:::2::;2::42::52::62::72::02::12::22::32:;:2:;;2:;42:;52:;62:;72:;02:;12:;22:;32:4:2:4;2:442:452:462:472:402:412:422:432:5:2:5;2:542:552:562:572:502:512:522:532:6:2:6;2:642:652:662:672:602:612:622:632333231720:73333::::`lnln/Mpfpwffpwbsfqlwlglkb`f`bgbb/]lajfmg/Abbp/Aujgb`bpllwqlelqlplollwqb`vbogjilpjgldqbmwjslwfnbgfafbodlrv/Efpwlmbgbwqfpsl`l`bpbabilwlgbpjmlbdvbsvfpvmlpbmwfgj`fovjpfoobnbzlylmbbnlqsjpllaqb`oj`foolgjlpklqb`bpj<[<\\!sbqhpnlvpfNlpw#---?,bnlmdaqbjmalgz#mlmf8abpfg`bqqzgqbewqfefqsbdf\\klnf-nfwfqgfobzgqfbnsqlufiljmw?,wq=gqvdp?"..#bsqjojgfboboofmf{b`welqwk`lgfpoldj`Ujft#pffnpaobmhslqwp#+133pbufg\\ojmhdlbopdqbmwdqffhklnfpqjmdpqbwfg03s{8tklpfsbqpf+*8!#Aol`hojmv{ilmfpsj{fo$*8!=*8je+.ofewgbujgklqpfEl`vpqbjpfal{fpWqb`hfnfmw?,fn=abq!=-pq`>wltfqbow>!`baofkfmqz17s{8pfwvsjwbozpkbqsnjmlqwbpwftbmwpwkjp-qfpfwtkffodjqop,`pp,233&8`ovappwveeajaofulwfp#2333hlqfb~*8\x0e\tabmgprvfvf>#x~8;3s{8`hjmdx\x0e\t\n\nbkfbg`ol`hjqjpkojhf#qbwjlpwbwpElqn!zbkll*X3^8Balvwejmgp?,k2=gfavdwbphpVQO#>`foop~*+*821s{8sqjnfwfoopwvqmp3{533-isd!psbjmafb`kwb{fpnj`qlbmdfo..=?,djewppwfuf.ojmhalgz-~*8\t\nnlvmw#+2::EBR?,qldfqeqbmh@obpp1;s{8effgp?k2=?p`lwwwfpwp11s{8gqjmh*#\x7f\x7f#oftjppkboo 30:8#elq#olufgtbpwf33s{8ib9\x0fnpjnlm?elmwqfsoznffwpvmwfq`kfbswjdkwAqbmg*#">#gqfpp`ojspqllnplmhfznlajonbjm-Mbnf#sobwfevmmzwqffp`ln,!2-isdtnlgfsbqbnPWBQWofew#jggfm/#132*8\t~\telqn-ujqvp`kbjqwqbmptlqpwSbdfpjwjlmsbw`k?"..\tl.`b`ejqnpwlvqp/333#bpjbmj((*xbglaf$*X3^jg>23alwk8nfmv#-1-nj-smd!hfujm`lb`k@kjogaqv`f1-isdVQO*(-isd\x7fpvjwfpoj`fkbqqz213!#ptffwwq=\x0e\tmbnf>gjfdlsbdf#ptjpp..=\t\t eee8!=Old-`ln!wqfbwpkffw*#%%#27s{8poffsmwfmwejofgib9\x0fojg>!`Mbnf!tlqpfpklwp.al{.gfowb\t%ow8afbqp97;Y?gbwb.qvqbo?,b=#psfmgabhfqpklsp>#!!8sks!=`wjlm20s{8aqjbmkfoolpjyf>l>&1E#iljmnbzaf?jnd#jnd!=/#eipjnd!#!*X3^NWlsAWzsf!mftozGbmph`yf`kwqbjohmltp?,k6=ebr!=yk.`m23*8\t.2!*8wzsf>aovfpwqvozgbujp-ip$8=\x0e\t?"pwffo#zlv#k1=\x0e\telqn#ifpvp233&#nfmv-\x0e\t\n\x0e\ttbofpqjphpvnfmwggjmda.ojhwfb`kdje!#ufdbpgbmphffpwjpkrjspvlnjplaqfgfpgffmwqfwlglpsvfgfb/]lpfpw/Mwjfmfkbpwblwqlpsbqwfglmgfmvfulkb`fqelqnbnjpnlnfilqnvmglbrv/Ag/Abpp/_olbzvgbef`kbwlgbpwbmwlnfmlpgbwlplwqbppjwjlnv`klbklqbovdbqnbzlqfpwlpklqbpwfmfqbmwfpelwlpfpwbpsb/Apmvfubpbovgelqlpnfgjlrvjfmnfpfpslgfq`kjofpfq/Muf`fpgf`jqilp/Efpwbqufmwbdqvslkf`klfoolpwfmdlbnjdl`lpbpmjufodfmwfnjpnbbjqfpivojlwfnbpkb`jbebulqivmjlojaqfsvmwlavfmlbvwlqbaqjoavfmbwf{wlnbqylpbafqojpwbovfdl`/_nlfmfqlivfdlsfq/Vkbafqfpwlzmvm`bnvifqubolqevfqbojaqldvpwbjdvboulwlp`bplpdv/Absvfglplnlpbujplvpwfggfafmml`kfavp`bebowbfvqlppfqjfgj`kl`vqpl`obuf`bpbpof/_msobylobqdllaqbpujpwbbslzlivmwlwqbwbujpwl`qfbq`bnslkfnlp`jm`l`bqdlsjplplqgfmkb`fm/Mqfbgjp`lsfgql`fq`bsvfgbsbsfonfmlq/Vwjo`obqlilqdf`boofslmfqwbqgfmbgjfnbq`bpjdvffoobppjdol`l`kfnlwlpnbgqf`obpfqfpwlmj/]lrvfgbsbpbqabm`lkjilpujbifsbaol/Epwfujfmfqfjmlgfibqelmgl`bmbomlqwfofwqb`bvpbwlnbqnbmlpovmfpbvwlpujoobufmglsfpbqwjslpwfmdbnbq`loofubsbgqfvmjglubnlpylmbpbnalpabmgbnbqjbbavplnv`kbpvajqqjlibujujqdqbgl`kj`bboo/Ailufmgj`kbfpwbmwbofppbojqpvfolsfplpejmfpoobnbavp`l/Epwboofdbmfdqlsobybkvnlqsbdbqivmwbglaofjpobpalopbab/]lkbaobov`kb/mqfbgj`fmivdbqmlwbpuboofboo/M`bqdbglolqbabilfpw/Edvpwlnfmwfnbqjlejqnb`lpwlej`kbsobwbkldbqbqwfpofzfpbrvfonvpflabpfpsl`lpnjwbg`jfol`kj`lnjfgldbmbqpbmwlfwbsbgfafpsobzbqfgfppjfwf`lqwf`lqfbgvgbpgfpflujfilgfpfbbdvbp%rvlw8glnbjm`lnnlmpwbwvpfufmwpnbpwfqpzpwfnb`wjlmabmmfqqfnlufp`qloovsgbwfdolabonfgjvnejowfqmvnafq`kbmdfqfpvowsvaoj`p`qffm`kllpfmlqnbowqbufojppvfpplvq`fwbqdfwpsqjmdnlgvofnlajofptjw`ksklwlpalqgfqqfdjlmjwpfoepl`jbob`wjuf`lovnmqf`lqgelooltwjwof=fjwkfqofmdwkebnjozeqjfmgobzlvwbvwklq`qfbwfqfujftpvnnfqpfqufqsobzfgsobzfqf{sbmgsloj`zelqnbwglvaofsljmwppfqjfpsfqplmojujmdgfpjdmnlmwkpelq`fpvmjrvftfjdkwsflsoffmfqdzmbwvqfpfbq`kejdvqfkbujmd`vpwlnleepfwofwwfqtjmgltpvanjwqfmgfqdqlvspvsolbgkfbowknfwklgujgflpp`klloevwvqfpkbgltgfabwfubovfpLaif`wlwkfqpqjdkwpofbdvf`kqlnfpjnsofmlwj`fpkbqfgfmgjmdpfbplmqfslqwlmojmfprvbqfavwwlmjnbdfpfmbaofnlujmdobwfpwtjmwfqEqbm`fsfqjlgpwqlmdqfsfbwOlmglmgfwbjoelqnfggfnbmgpf`vqfsbppfgwlddofsob`fpgfuj`fpwbwj``jwjfppwqfbnzfooltbwwb`hpwqffweojdkwkjggfmjmel!=lsfmfgvpfevouboofz`bvpfpofbgfqpf`qfwpf`lmggbnbdfpslqwpf{`fswqbwjmdpjdmfgwkjmdpfeef`wejfogppwbwfpleej`fujpvbofgjwlqulovnfQfslqwnvpfvnnlujfpsbqfmwb``fppnlpwoznlwkfq!#jg>!nbqhfwdqlvmg`kbm`fpvqufzafelqfpznalonlnfmwpsff`knlwjlmjmpjgfnbwwfq@fmwfqlaif`wf{jpwpnjggofFvqlsfdqltwkofdb`znbmmfqfmlvdk`bqffqbmptfqlqjdjmslqwbo`ojfmwpfof`wqbmgln`olpfgwlsj`p`lnjmdebwkfqlswjlmpjnsozqbjpfgfp`bsf`klpfm`kvq`kgfejmfqfbplm`lqmfqlvwsvwnfnlqzjeqbnfsloj`fnlgfopMvnafqgvqjmdleefqppwzofphjoofgojpwfg`boofgpjoufqnbqdjmgfofwfafwwfqaqltpfojnjwpDolabopjmdoftjgdfw`fmwfqavgdfwmltqbs`qfgjw`objnpfmdjmfpbefwz`klj`fpsjqjw.pwzofpsqfbgnbhjmdmffgfgqvppjbsofbpff{wfmwP`qjswaqlhfmbooltp`kbqdfgjujgfeb`wlqnfnafq.abpfgwkflqz`lmejdbqlvmgtlqhfgkfosfg@kvq`kjnsb`wpklvogbotbzpoldl!#alwwlnojpw!=*xubq#sqfej{lqbmdfKfbgfq-svpk+`lvsofdbqgfmaqjgdfobvm`kQfujftwbhjmdujpjlmojwwofgbwjmdAvwwlmafbvwzwkfnfpelqdlwPfbq`kbm`klqbonlpwolbgfg@kbmdfqfwvqmpwqjmdqfolbgNlajofjm`lnfpvssozPlvq`flqgfqpujftfg%maps8`lvqpfBalvw#jpobmg?kwno#`llhjfmbnf>!bnbylmnlgfqmbguj`fjm?,b=9#Wkf#gjboldklvpfpAFDJM#Nf{j`lpwbqwp`fmwqfkfjdkwbggjmdJpobmgbppfwpFnsjqfP`kllofeelqwgjqf`wmfbqoznbmvboPfof`w-\t\tLmfiljmfgnfmv!=SkjojsbtbqgpkbmgofjnslqwLeej`fqfdbqgphjoopmbwjlmPslqwpgfdqfftffhoz#+f-d-afkjmggl`wlqolddfgvmjwfg?,a=?,afdjmpsobmwpbppjpwbqwjpwjppvfg033s{\x7f`bmbgbbdfm`zp`kfnfqfnbjmAqbyjopbnsofoldl!=afzlmg.p`bofb``fswpfqufgnbqjmfEllwfq`bnfqb?,k2=\t\\elqn!ofbufppwqfpp!#,=\x0e\t-dje!#lmolbgolbgfqL{elqgpjpwfqpvqujuojpwfmefnbofGfpjdmpjyf>!bssfbowf{w!=ofufopwkbmhpkjdkfqelq`fgbmjnbobmzlmfBeqj`bbdqffgqf`fmwSflsof?aq#,=tlmgfqsqj`fpwvqmfg\x7f\x7f#x~8nbjm!=jmojmfpvmgbztqbs!=ebjofg`fmpvpnjmvwfafb`lmrvlwfp263s{\x7ffpwbwfqfnlwffnbjo!ojmhfgqjdkw8pjdmboelqnbo2-kwnopjdmvssqjm`feolbw9-smd!#elqvn-B``fppsbsfqpplvmgpf{wfmgKfjdkwpojgfqVWE.;!%bns8#Afelqf-#TjwkpwvgjlltmfqpnbmbdfsqlejwiRvfqzbmmvbosbqbnpalvdkwebnlvpdlldofolmdfqj((*#xjpqbfopbzjmdgf`jgfklnf!=kfbgfqfmpvqfaqbm`ksjf`fpaol`h8pwbwfgwls!=?qb`jmdqfpjyf..%dw8sb`jwzpf{vboavqfbv-isd!#23/333lawbjmwjwofpbnlvmw/#Jm`-`lnfgznfmv!#ozqj`pwlgbz-jmgffg`lvmwz\\oldl-EbnjozollhfgNbqhfwopf#jeSobzfqwvqhfz*8ubq#elqfpwdjujmdfqqlqpGlnbjm~fopfxjmpfqwAold?,ellwfqoldjm-ebpwfqbdfmwp?algz#23s{#3sqbdnbeqjgbzivmjlqgloobqsob`fg`lufqpsovdjm6/333#sbdf!=alpwlm-wfpw+bubwbqwfpwfg\\`lvmwelqvnpp`kfnbjmgf{/ejoofgpkbqfpqfbgfqbofqw+bssfbqPvanjwojmf!=algz!=\t)#WkfWklvdkpffjmdifqpfzMftp?,ufqjezf{sfqwjmivqztjgwk>@llhjfPWBQW#b`qlpp\\jnbdfwkqfbgmbwjufsl`hfwal{!=\tPzpwfn#Gbujg`bm`fqwbaofpsqlufgBsqjo#qfboozgqjufqjwfn!=nlqf!=albqgp`lolqp`bnsvpejqpw#\x7f\x7f#X^8nfgjb-dvjwbqejmjpktjgwk9pkltfgLwkfq#-sks!#bppvnfobzfqptjoplmpwlqfpqfojfeptfgfm@vpwlnfbpjoz#zlvq#Pwqjmd\t\tTkjowbzolq`ofbq9qfplqweqfm`kwklvdk!*#(#!?algz=avzjmdaqbmgpNfnafqmbnf!=lssjmdpf`wlq6s{8!=upsb`fslpwfqnbilq#`leeffnbqwjmnbwvqfkbssfm?,mbu=hbmpbpojmh!=Jnbdfp>ebopftkjof#kpsb`f3%bns8#\t\tJm##sltfqSlophj.`lolqilqgbmAlwwlnPwbqw#.`lvmw1-kwnomftp!=32-isdLmojmf.qjdkwnjoofqpfmjlqJPAM#33/333#dvjgfpubovf*f`wjlmqfsbjq-{no!##qjdkwp-kwno.aol`hqfdF{s9klufqtjwkjmujqdjmsklmfp?,wq=\x0evpjmd#\t\nubq#=$*8\t\n?,wg=\t?,wq=\tabkbpbaqbpjodbofdlnbdzbqslophjpqsphj4]4C5d\bTA\nzk\vBl\bQ\x7f\vUm\x05Gx\bSM\nmC\bTA\twQ\nd}\bW@\bTl\bTF\ti@\tcT\vBM\v|j\x04BV\tqw\tcC\bWI\npa\tfM\n{Z\x05{X\bTF\bVV\bVK\t\x7fm\x04kF\t[]\bPm\bTv\nsI\vpg\t[I\bQp\x04mx\v_W\n^M\npe\vQ}\vGu\nel\npe\x04Ch\x04BV\bTA\tSo\nzk\vGL\vxD\nd[\x05Jz\x05MY\bQp\x04li\nfl\npC\x05{B\x05Nt\vwT\ti_\bTg\x04QQ\n|p\vXN\bQS\vxD\x04QC\bWZ\tpD\vVS\bTW\x05Nt\x04Yh\nzu\x04Kj\x05N}\twr\tHa\n_D\tj`\vQ}\vWp\nxZ\x04{c\tji\tBU\nbD\x04a|\tTn\tpV\nZd\nmC\vEV\x05{X\tc}\tTo\bWl\bUd\tIQ\tcg\vxs\nXW\twR\vek\tc}\t]y\tJn\nrp\neg\npV\nz\\\x05{W\npl\nz\\\nzU\tPc\t`{\bV@\nc|\bRw\ti_\bVb\nwX\tHv\x04Su\bTF\v_W\vWs\vsI\x05m\x7f\nTT\ndc\tUS\t}f\tiZ\bWz\tc}\x04MD\tBe\tiD\v@@\bTl\bPv\t}t\x04Sw\x04M`\vnU\tkW\ved\nqo\vxY\tA|\bTz\vy`\x04BR\x04BM\tia\x04XU\nyu\x04n^\tfL\tiI\nXW\tfD\bWz\bW@\tyj\t\x7fm\tav\tBN\vb\\\tpD\bTf\nY[\tJn\bQy\t[^\vWc\vyu\x04Dl\x04CJ\vWj\vHR\t`V\vuW\tQy\np@\vGu\x05pl\x04Jm\bW[\nLP\nxC\n`m\twQ\x05ui\x05\x7fR\nbI\twQ\tBZ\tWV\x04BR\npg\tcg\x05ti\x04CW\n_y\tRg\bQa\vQB\vWc\nYb\x05le\ngE\x04Su\nL[\tQ\x7f\tea\tdj\v]W\nb~\x04M`\twL\bTV\bVH\nt\x7f\npl\t|b\x05s_\bU|\bTa\x04oQ\x05lv\x04Sk\x04M`\bTv\vK}\nfl\tcC\x04oQ\x04BR\tHk\t|d\bQp\tHK\tBZ\vHR\bPv\vLx\vEZ\bT\x7f\bTv\tiD\x05oD\x05MU\vwB\x04Su\x05k`\x04St\ntC\tPl\tKg\noi\tjY\vxY\x04h}\nzk\bWZ\t\x7fm\ve`\tTB\tfE\nzk\t`z\x04Yh\nV|\tHK\tAJ\tAJ\bUL\tp\\\tql\nYc\x04Kd\nfy\x04Yh\t[I\vDg\x04Jm\n]n\nlb\bUd\n{Z\tlu\tfs\x04oQ\bTW\x04Jm\vwB\tea\x04Yh\x04BC\tsb\tTn\nzU\n_y\vxY\tQ]\ngw\x04mt\tO\\\ntb\bWW\bQy\tmI\tV[\ny\\\naB\vRb\twQ\n]Q\x04QJ\bWg\vWa\bQj\ntC\bVH\nYm\vxs\bVK\nel\bWI\vxY\x04Cq\ntR\vHV\bTl\bVw\tay\bQa\bVV\t}t\tdj\nr|\tp\\\twR\n{i\nTT\t[I\ti[\tAJ\vxs\v_W\td{\vQ}\tcg\tTz\tA|\tCj\vLm\x05N}\x05m\x7f\nbK\tdZ\tp\\\t`V\tsV\np@\tiD\twQ\vQ}\bTf\x05ka\x04Jm\v@@\bV`\tzp\n@N\x04Sw\tiI\tcg\noi\x04Su\bVw\x04lo\x04Cy\tc}\vb\\\tsU\x04BA\bWI\bTf\nxS\tVp\nd|\bTV\vbC\tNo\x05Ju\nTC\t|`\n{Z\tD]\bU|\tc}\x05lm\bTl\tBv\tPl\tc}\bQp\t\x7fm\nLk\tkj\n@N\x04Sb\x04KO\tj_\tp\\\nzU\bTl\bTg\bWI\tcf\x04XO\bWW\ndz\x04li\tBN\nd[\bWO\x04MD\vKC\tdj\tI_\bVV\ny\\\vLm\x05xl\txB\tkV\vb\\\vJW\vVS\tVx\vxD\td{\x04MD\bTa\t|`\vPz\x04R}\vWs\x04BM\nsI\x04CN\bTa\x04Jm\npe\ti_\npV\nrh\tRd\tHv\n~A\nxR\vWh\vWk\nxS\vAz\vwX\nbI\x04oQ\tfw\nqI\nV|\nun\x05z\x7f\vpg\td\\\voA\x05{D\ti_\x05xB\bT\x7f\t`V\x05qr\tTT\x04g]\x04CA\vuR\tVJ\tT`\npw\vRb\tI_\nCx\x04Ro\vsI\x04Cj\x04Kh\tBv\tWV\x04BB\x05oD\x05{D\nhc\x04Km\v^R\tQE\n{I\np@\nc|\x05Gt\tc}\x04Dl\nzU\x05qN\tsV\x05k}\tHh\v|j\nqo\x05u|\tQ]\vek\x05\x7fZ\x04M`\x04St\npe\tdj\bVG\veE\t\x7fm\vWc\x04|I\n[W\tfL\bT\x7f\tBZ\x04Su\vKa\x04Cq\x05Nt\x04Y[\nqI\bTv\tfM\ti@\t}f\x04B\\\tQy\vBl\bWg\x04XD\x05kc\vx[\bVV\tQ]\t\x7fa\tPy\vxD\nfI\t}f\x05oD\tdj\tSG\x05ls\t~D\x04CN\n{Z\t\\v\n_D\nhc\vx_\x04C[\tAJ\nLM\tVx\x04CI\tbj\tc^\tcF\ntC\x04Sx\twr\x04XA\bU\\\t|a\vK\\\bTV\bVj\nd|\tfs\x04CX\ntb\bRw\tVx\tAE\tA|\bT\x7f\x05Nt\vDg\tVc\bTl\x04d@\npo\t\x7fM\tcF\npe\tiZ\tBo\bSq\nfH\x04l`\bTx\bWf\tHE\vF{\tcO\tfD\nlm\vfZ\nlm\veU\tdG\x04BH\bTV\tSi\x05MW\nwX\nz\\\t\\c\x04CX\nd}\tl}\bQp\bTV\tF~\bQ\x7f\t`i\ng@\x05nO\bUd\bTl\nL[\twQ\tji\ntC\t|J\nLU\naB\vxY\x04Kj\tAJ\x05uN\ti[\npe\x04Sk\vDg\vx]\bVb\bVV\nea\tkV\nqI\bTa\x04Sk\nAO\tpD\ntb\nts\nyi\bVg\ti_\v_W\nLk\x05Nt\tyj\tfM\x04R\x7f\tiI\bTl\vwX\tsV\vMl\nyu\tAJ\bVj\x04KO\tWV\vA}\vW\x7f\nrp\tiD\v|o\x05lv\vsI\x04BM\td~\tCU\bVb\x04eV\npC\vwT\tj`\tc}\vxs\vps\vvh\tWV\vGg\vAe\vVK\v]W\trg\vWc\x05F`\tBr\vb\\\tdZ\bQp\nqI\x04kF\nLk\vAR\bWI\bTg\tbs\tdw\n{L\n_y\tiZ\bTA\tlg\bVV\bTl\tdk\n`k\ta{\ti_\x05{A\x05wj\twN\v@@\bTe\ti_\n_D\twL\nAH\viK\vek\n[]\tp_\tyj\bTv\tUS\t[r\n{I\nps\x05Gt\vVK\npl\x04S}\vWP\t|d\x04MD\vHV\bT\x7f\x04R}\x04M`\bTV\bVH\x05lv\x04Ch\bW[\x04Ke\tR{\v^R\tab\tBZ\tVA\tB`\nd|\nhs\x04Ke\tBe\x04Oi\tR{\td\\\x05nB\bWZ\tdZ\tVJ\x05Os\t\x7fm\x04uQ\vhZ\x04Q@\x04QQ\nfI\bW[\x04B\\\x04li\nzU\nMd\x04M`\nxS\bVV\n\\}\vxD\t\x7fm\bTp\x04IS\nc|\tkV\x05i~\tV{\vhZ\t|b\bWt\n@R\voA\vnU\bWI\tea\tB`\tiD\tc}\tTz\x04BR\vQB\x05Nj\tCP\t[I\bTv\t`W\x05uN\vpg\vpg\vWc\tiT\tbs\twL\tU_\tc\\\t|h\vKa\tNr\tfL\nq|\nzu\nz\\\tNr\bUg\t|b\x04m`\bTv\nyd\nrp\bWf\tUX\x04BV\nzk\nd}\twQ\t}f\x04Ce\ved\bTW\bSB\nxU\tcn\bTb\ne\x7f\ta\\\tSG\bU|\npV\nN\\\x04Kn\vnU\tAt\tpD\v^R\vIr\x04b[\tR{\tdE\vxD\vWK\vWA\bQL\bW@\x04Su\bUd\nDM\tPc\x04CA\x04Dl\x04oQ\tHs\x05wi\x04ub\n\x7fa\bQp\x05Ob\nLP\bTl\x04Y[\vK}\tAJ\bQ\x7f\x04n^\vsA\bSM\nqM\bWZ\n^W\vz{\x04S|\tfD\bVK\bTv\bPv\x04BB\tCP\x04dF\tid\vxs\x04mx\vws\tcC\ntC\tyc\x05M`\vW\x7f\nrh\bQp\vxD\x04\\o\nsI\x04_k\nzu\x04kF\tfD\x04Xs\x04XO\tjp\bTv\x04BS\x05{B\tBr\nzQ\nbI\tc{\x04BD\x04BV\x05nO\bTF\tca\x05Jd\tfL\tPV\tI_\nlK\x04`o\twX\npa\tgu\bP}\x05{^\bWf\n{I\tBN\npa\x04Kl\vpg\tcn\tfL\vvh\x04Cq\bTl\vnU\bSq\x04Cm\twR\bUJ\npe\nyd\nYg\x04Cy\vKW\tfD\nea\x04oQ\tj_\tBv\x04nM\vID\bTa\nzA\x05pl\n]n\bTa\tR{\tfr\n_y\bUg\x05{X\x05kk\vxD\x04|I\x05xl\nfy\x04Ce\vwB\nLk\vd]\noi\n}h\tQ]\npe\bVw\x04Hk\x04OQ\nzk\tAJ\npV\bPv\ny\\\tA{\x04Oi\bSB\x04XA\veE\tjp\nq}\tiD\x05qN\v^R\t\x7fm\tiZ\tBr\bVg\noi\n\\X\tU_\nc|\vHV\bTf\tTn\x04\\N\x04\\N\nuB\x05lv\nyu\tTd\bTf\bPL\v]W\tdG\nA`\nw^\ngI\npe\tdw\nz\\\x05ia\bWZ\tcF\x04Jm\n{Z\bWO\x04_k\x04Df\x04RR\td\\\bVV\vxs\x04BN\x05ti\x04lm\tTd\t]y\vHV\tSo\v|j\x04XX\tA|\vZ^\vGu\bTW\x05M`\x04kF\vhZ\vVK\tdG\vBl\tay\nxU\x05qE\x05nO\bVw\nqI\x04CX\ne\x7f\tPl\bWO\vLm\tdL\x05uH\x04Cm\tdT\x04fn\vwB\x05ka\vnU\n@M\nyT\tHv\t\\}\x04Kh\td~\x04Yh\x05k}\neR\td\\\bWI\t|b\tHK\tiD\bTW\x05MY\npl\bQ_\twr\vAx\tHE\bTg\bSq\x05vp\vb\\\bWO\nOl\nsI\nfy\vID\t\\c\n{Z\n^~\npe\nAO\tTT\vxv\x04k_\bWO\v|j\vwB\tQy\ti@\tPl\tHa\tdZ\x05k}\x04ra\tUT\vJc\ved\np@\tQN\nd|\tkj\tHk\x04M`\noi\twr\td\\\nlq\no_\nlb\nL[\tac\x04BB\x04BH\x04Cm\npl\tIQ\bVK\vxs\n`e\viK\npa\x04Oi\tUS\bTp\tfD\nPG\x05kk\x04XA\nz\\\neg\vWh\twR\x05qN\nqS\tcn\x04lo\nxS\n^W\tBU\nt\x7f\tHE\tp\\\tfF\tfw\bVV\bW@\tak\vVK\x05ls\tVJ\bVV\veE\x04\\o\nyX\nYm\x04M`\x05lL\nd|\nzk\tA{\x05sE\twQ\x04XT\nt\x7f\tPl\t]y\vwT\x05{p\x04MD\vb\\\tQ]\x04Kj\tJn\nAH\vRb\tBU\tHK\t\\c\nfI\x05m\x7f\nqM\n@R\tSo\noi\x04BT\tHv\n_y\x04Kh\tBZ\t]i\bUJ\tV{\x04Sr\nbI\vGg\ta_\bTR\nfI\nfl\t[K\tII\x04S|\vuW\tiI\bWI\nqI\v|j\x04BV\bVg\bWZ\x04kF\vx]\bTA\tab\tfr\ti@\tJd\tJd\vps\nAO\bTa\x05xu\tiD\nzk\t|d\t|`\bW[\tlP\tdG\bVV\vw}\vqO\ti[\bQ\x7f\bTz\vVF\twN\x05ts\tdw\bTv\neS\ngi\tNr\x05yS\npe\bVV\bSq\n`m\tyj\tBZ\vWX\bSB\tc\\\nUR\t[J\tc_\x04nM\bWQ\vAx\nMd\tBr\x05ui\vxY\bSM\vWc\v|j\vxs\t}Q\tBO\bPL\bWW\tfM\nAO\tPc\veU\x04e^\bTg\nqI\tac\bPv\tcF\x04oQ\tQ\x7f\vhZ\x05ka\nz\\\tiK\tBU\n`k\tCP\x04S|\x04M`\n{I\tS{\x04_O\tBZ\x04Zi\x04Sk\tps\tp\\\nYu\n]s\nxC\bWt\nbD\tkV\vGu\x05yS\nqA\t[r\neK\x04M`\tdZ\x05lL\bUg\bTl\nbD\tUS\vb\\\tpV\ncc\x04S\\\tct\t`z\bPL\vWs\nA`\neg\bSq\x05uE\x04CR\vDg\t`W\vz{\vWc\x04Sk\x04Sk\tbW\bUg\tea\nxZ\tiI\tUX\tVJ\nqn\tS{\vRb\bTQ\npl\x05Gt\vuW\x05uj\npF\nqI\tfL\t[I\tia\x04XO\nyu\vDg\ved\tq{\x04VG\bQ\x7f\x05ka\tVj\tkV\txB\nd|\np@\tQN\tPc\tps\x04]j\tkV\toU\bTp\nzU\x05nB\vB]\ta{\bV@\n]n\x04m`\tcz\tR{\x04m`\bQa\vwT\bSM\x05MY\x05qN\tdj\x05~s\vQ}\x05MY\vMB\tBv\twR\bRg\vQ}\tql\vKC\nrm\x05xu\x04CC\vwB\vvh\tBq\x04Xq\npV\ti_\x05Ob\x05uE\nbd\nqo\v{i\nC~\tBL\veE\x05uH\bVj\x04Ey\x04Gz\vzR\v{i\tcf\n{Z\n]n\x04XA\vGu\vnU\thS\vGI\nCc\tHE\bTA\tHB\x04BH\x04Cj\nCc\bTF\tHE\nXI\tA{\bQ\x7f\tc\\\vmO\vWX\nfH\np@\x05MY\bTF\nlK\tBt\nzU\tTT\x04Km\vwT\npV\ndt\vyI\tVx\tQ\x7f\tRg\tTd\nzU\bRS\nLM\twA\x04nM\tTn\ndS\t]g\nLc\vwB\t}t\t[I\tCP\x04kX\vFm\vhZ\x05m\x7f\ti[\np@\vQ}\vW\x7f\t|d\nMO\nMd\tf_\tfD\tcJ\tHz\vRb\tio\tPy\x04Y[\nxU\tct\v@@\tww\bPv\x04BM\x04FF\ntb\x05v|\vKm\tBq\tBq\x04Kh\x04`o\nZd\x04XU\ti]\t|`\tSt\x04B\\\bQ\x7f\v_W\tTJ\nqI\t|a\tA{\vuP\x04MD\tPl\nxR\tfL\vws\tc{\td\\\bV`\neg\tHK\x05kc\nd|\bVV\ny\\\x05kc\ti]\bVG\t`V\tss\tI_\tAE\tbs\tdu\nel\tpD\vW\x7f\nqs\x05lv\bSM\x04Zi\vVK\x05ia\vQB\tQ\x7f\n{Z\bPt\vKl\nlK\nhs\ndS\bVK\x05mf\nd^\tkV\tcO\nc|\bVH\t\\]\bTv\bSq\tmI\vDg\tVJ\tcn\ny\\\bVg\bTv\nyX\bTF\t]]\bTp\noi\nhs\veU\nBf\tdj\x05Mr\n|p\t\\g\t]r\bVb\x05{D\nd[\x04XN\tfM\tO\\\x05s_\tcf\tiZ\x04XN\vWc\tqv\n`m\tU^\x05oD\nd|\vGg\tdE\vwf\x04lo\x04u}\nd|\x05oQ\t`i\x04Oi\vxD\ndZ\nCx\x04Yw\nzk\ntb\ngw\tyj\tB`\nyX\vps\ntC\vpP\vqw\bPu\bPX\tDm\npw\x05Nj\tss\taG\vxs\bPt\noL\x04Gz\tOk\ti@\ti]\x04eC\tIQ\tii\tdj\v@J\t|d\x05uh\bWZ\veU\vnU\bTa\tcC\x04g]\nzk\x04Yh\bVK\nLU\np@\ntb\ntR\tCj\vNP\ti@\bP{\n\\}\n{c\nwX\tfL\bVG\tc{\t|`\tAJ\t|C\tfD\x05ln\t|d\tbs\nqI\x05{B\vAx\np@\nzk\vRb\x05Os\vWS\x04e^\vD_\tBv\vWd\bVb\vxs\veE\bRw\n]n\n|p\vg|\tfw\x05kc\bTI\x05ka\n\\T\x04Sp\tju\vps\npe\x05u|\vGr\bVe\tCU\x04]M\x04XU\vxD\bTa\tIQ\vWq\tCU\tam\tdj\bSo\x04Sw\vnU\x04Ch\tQ]\x05s_\bPt\tfS\bTa\t\\}\n@O\x04Yc\tUZ\bTx\npe\vnU\nzU\t|}\tiD\nz\\\bSM\vxD\x04BR\nzQ\tQN\x04]M\x04Yh\nLP\vFm\vLX\x05vc\vql\x05ka\tHK\bVb\ntC\nCy\bTv\nuV\x04oQ\t`z\t[I\tB`\vRb\tyj\tsb\vWs\bTl\tkV\ved\ne\x7f\x05lL\vxN\t\x7fm\nJn\tjY\vxD\bVb\bSq\vyu\twL\vXL\bTA\tpg\tAt\tnD\x04XX\twR\npl\nhw\x05yS\nps\tcO\bW[\v|j\x04XN\tsV\tp\\\tBe\nb~\nAJ\n]e\x05k`\x05qN\tdw\tWV\tHE\vEV\x05Jz\tid\tB`\tzh\x05E]\tfD\bTg\x05qN\bTa\tja\x04Cv\bSM\nhc\bUe\x05t_\tie\x04g]\twQ\nPn\bVB\tjw\bVg\vbE\tBZ\vRH\bP{\tjp\n\\}\ta_\tcC\t|a\vD]\tBZ\ti[\tfD\vxW\no_\td\\\n_D\ntb\t\\c\tAJ\nlK\x04oQ\x04lo\vLx\vM@\bWZ\x04Kn\vpg\nTi\nIv\n|r\v@}\x05Jz\x05Lm\x05Wh\x05k}\x05ln\vxD\n]s\x04gc\vps\tBr\bTW\vBM\x05tZ\nBY\x04DW\tjf\vSW\x04C}\nqo\tdE\tmv\tIQ\bPP\bUb\x05lv\x04BC\nzQ\t[I\vgl\nig\bUs\x04BT\vbC\bSq\tsU\tiW\nJn\tSY\tHK\trg\npV\vID\v|j\x04KO\t`S\t|a`vbmglfmujbqnbgqjgavp`bqjmj`jlwjfnslslqrvf`vfmwbfpwbglsvfgfmivfdlp`lmwqbfpw/Mmmlnaqfwjfmfmsfqejonbmfqbbnjdlp`jvgbg`fmwqlbvmrvfsvfgfpgfmwqlsqjnfqsqf`jlpfd/Vmavfmlpuloufqsvmwlppfnbmbkba/Abbdlpwlmvfulpvmjglp`bqolpfrvjslmj/]lpnv`klpbodvmb`lqqfljnbdfmsbqwjqbqqjabnbq/Abklnaqffnsoflufqgbg`bnajlnv`kbpevfqlmsbpbglo/Amfbsbqf`fmvfubp`vqplpfpwbabrvjfqlojaqlp`vbmwlb``fplnjdvfoubqjlp`vbwqlwjfmfpdqvslppfq/Mmfvqlsbnfgjlpeqfmwfb`fq`bgfn/Mplefqwb`l`kfpnlgfoljwbojbofwqbpbod/Vm`lnsqb`vbofpf{jpwf`vfqslpjfmglsqfmpboofdbqujbifpgjmfqlnvq`jbslgq/Msvfpwlgjbqjlsvfaolrvjfqfnbmvfosqlsjl`qjpjp`jfqwlpfdvqlnvfqwfevfmwf`fqqbqdqbmgffef`wlsbqwfpnfgjgbsqlsjbleqf`fwjfqqbf.nbjoubqjbpelqnbpevwvqllaifwlpfdvjqqjfpdlmlqnbpnjpnlp/Vmj`l`bnjmlpjwjlpqby/_mgfajglsqvfabwlofglwfm/Abifp/Vpfpsfql`l`jmblqjdfmwjfmgb`jfmwl`/Mgjykbaobqpfq/Abobwjmbevfqybfpwjoldvfqqbfmwqbq/E{jwlo/_sfybdfmgbu/Agflfujwbqsbdjmbnfwqlpibujfqsbgqfpe/M`jo`bafyb/Mqfbppbojgbfmu/Alibs/_mbavplpajfmfpwf{wlpoofubqsvfgbmevfqwf`ln/Vm`obpfpkvnbmlwfmjglajoablvmjgbgfpw/Mpfgjwbq`qfbgl2%bns8Kjpwlqz#>#mft#@fmwqbovsgbwfgPsf`jboMfwtlqhqfrvjqf`lnnfmwtbqmjmd@loofdfwlloabqqfnbjmpaf`bvpffof`wfgGfvwp`kejmbm`ftlqhfqprvj`hozafwtffmf{b`wozpfwwjmdgjpfbpfPl`jfwztfbslmpf{kjajw%ow8"..@lmwqlo`obppfp`lufqfglvwojmfbwwb`hpgfuj`fp+tjmgltsvqslpfwjwof>!Nlajof#hjoojmdpkltjmdJwbojbmgqlssfgkfbujozfeef`wp.2$^*8\t`lmejqn@vqqfmwbgubm`fpkbqjmdlsfmjmdgqbtjmdajoojlmlqgfqfgDfqnbmzqfobwfg?,elqn=jm`ovgftkfwkfqgfejmfgP`jfm`f`bwboldBqwj`ofavwwlmpobqdfpwvmjelqnilvqmfzpjgfabq@kj`bdlklojgbzDfmfqbosbppbdf/%rvlw8bmjnbwfeffojmdbqqjufgsbppjmdmbwvqboqlvdkoz-\t\tWkf#avw#mlwgfmpjwzAqjwbjm@kjmfpfob`h#lewqjavwfJqfobmg!#gbwb.eb`wlqpqf`fjufwkbw#jpOjaqbqzkvpabmgjm#eb`wbeebjqp@kbqofpqbgj`boaqlvdkwejmgjmdobmgjmd9obmd>!qfwvqm#ofbgfqpsobmmfgsqfnjvnsb`hbdfBnfqj`bFgjwjlm^%rvlw8Nfppbdfmffg#wlubovf>!`lnsof{ollhjmdpwbwjlmafojfufpnboofq.nlajofqf`lqgptbmw#wlhjmg#leEjqfel{zlv#bqfpjnjobqpwvgjfgnb{jnvnkfbgjmdqbsjgoz`ojnbwfhjmdglnfnfqdfgbnlvmwpelvmgfgsjlmffqelqnvobgzmbpwzklt#wl#Pvsslqwqfufmvff`lmlnzQfpvowpaqlwkfqplogjfqobqdfoz`boojmd-%rvlw8B``lvmwFgtbqg#pfdnfmwQlafqw#feelqwpSb`jej`ofbqmfgvs#tjwkkfjdkw9tf#kbufBmdfofpmbwjlmp\\pfbq`kbssojfgb`rvjqfnbppjufdqbmwfg9#ebopfwqfbwfgajddfpwafmfejwgqjujmdPwvgjfpnjmjnvnsfqkbspnlqmjmdpfoojmdjp#vpfgqfufqpfubqjbmw#qlof>!njppjmdb`kjfufsqlnlwfpwvgfmwplnflmff{wqfnfqfpwlqfalwwln9fuloufgboo#wkfpjwfnbsfmdojpktbz#wl##Bvdvpwpznalop@lnsbmznbwwfqpnvpj`bobdbjmpwpfqujmd~*+*8\x0e\tsbznfmwwqlvaof`lm`fsw`lnsbqfsbqfmwpsobzfqpqfdjlmpnlmjwlq#$$Wkf#tjmmjmdf{solqfbgbswfgDboofqzsqlgv`fbajojwzfmkbm`f`bqffqp*-#Wkf#`loof`wPfbq`k#bm`jfmwf{jpwfgellwfq#kbmgofqsqjmwfg`lmplofFbpwfqmf{slqwptjmgltp@kbmmfojoofdbomfvwqbopvddfpw\\kfbgfqpjdmjmd-kwno!=pfwwofgtfpwfqm`bvpjmd.tfahjw`objnfgIvpwj`f`kbswfquj`wjnpWklnbp#nlyjoobsqlnjpfsbqwjfpfgjwjlmlvwpjgf9ebopf/kvmgqfgLoznsj`\\avwwlmbvwklqpqfb`kfg`kqlmj`gfnbmgppf`lmgpsqlwf`wbglswfgsqfsbqfmfjwkfqdqfbwozdqfbwfqlufqboojnsqluf`lnnbmgpsf`jbopfbq`k-tlqpkjsevmgjmdwklvdkwkjdkfpwjmpwfbgvwjojwzrvbqwfq@vowvqfwfpwjmd`ofbqozf{slpfgAqltpfqojafqbo~#`bw`kSqlif`wf{bnsofkjgf+*8EolqjgbbmptfqpbooltfgFnsfqlqgfefmpfpfqjlvpeqffglnPfufqbo.avwwlmEvqwkfqlvw#le#">#mvoowqbjmfgGfmnbqhuljg+3*,boo-ipsqfufmwQfrvfpwPwfskfm\t\tTkfm#lapfquf?,k1=\x0e\tNlgfqm#sqlujgf!#bow>!alqgfqp-\t\tElq#\t\tNbmz#bqwjpwpsltfqfgsfqelqnej`wjlmwzsf#lenfgj`bowj`hfwplsslpfg@lvm`jotjwmfppivpwj`fDflqdf#Afodjvn---?,b=wtjwwfqmlwbaoztbjwjmdtbqebqf#Lwkfq#qbmhjmdskqbpfpnfmwjlmpvqujufp`klobq?,s=\x0e\t#@lvmwqzjdmlqfgolpp#leivpw#bpDflqdjbpwqbmdf?kfbg=?pwlssfg2$^*8\x0e\tjpobmgpmlwbaofalqgfq9ojpw#le`bqqjfg233/333?,k0=\t#pfufqboaf`lnfppfof`w#tfggjmd33-kwnonlmbq`klee#wkfwfb`kfqkjdkoz#ajloldzojef#lelq#fufmqjpf#le%qbrvl8sovplmfkvmwjmd+wklvdkGlvdobpiljmjmd`jq`ofpElq#wkfBm`jfmwUjfwmbnufkj`ofpv`k#bp`qzpwboubovf#>Tjmgltpfmilzfgb#pnboobppvnfg?b#jg>!elqfjdm#Boo#qjklt#wkfGjpsobzqfwjqfgkltfufqkjggfm8abwwofppffhjmd`bajmfwtbp#mlwollh#bw`lmgv`wdfw#wkfIbmvbqzkbssfmpwvqmjmdb9klufqLmojmf#Eqfm`k#ob`hjmdwzsj`bof{wqb`wfmfnjfpfufm#jedfmfqbwgf`jgfgbqf#mlw,pfbq`kafojfep.jnbdf9ol`bwfgpwbwj`-oldjm!=`lmufqwujlofmwfmwfqfgejqpw!=`jq`vjwEjmobmg`kfnjpwpkf#tbp23s{8!=bp#pv`kgjujgfg?,psbm=tjoo#afojmf#leb#dqfbwnzpwfqz,jmgf{-eboojmdgvf#wl#qbjotbz`loofdfnlmpwfqgfp`fmwjw#tjwkmv`ofbqIftjpk#sqlwfpwAqjwjpkeoltfqpsqfgj`wqfelqnpavwwlm#tkl#tbpof`wvqfjmpwbmwpvj`jgfdfmfqj`sfqjlgpnbqhfwpPl`jbo#ejpkjmd`lnajmfdqbskj`tjmmfqp?aq#,=?az#wkf#MbwvqboSqjub`z`llhjfplvw`lnfqfploufPtfgjpkaqjfeozSfqpjbmpl#nv`k@fmwvqzgfsj`wp`lovnmpklvpjmdp`qjswpmf{w#wlafbqjmdnbssjmdqfujpfgiRvfqz+.tjgwk9wjwof!=wllowjsPf`wjlmgfpjdmpWvqhjpkzlvmdfq-nbw`k+~*+*8\t\tavqmjmdlsfqbwfgfdqffpplvq`f>Qj`kbqg`olpfozsobpwj`fmwqjfp?,wq=\x0e\t`lolq9 vo#jg>!slppfppqloojmdskzpj`pebjojmdf{f`vwf`lmwfpwojmh#wlGfebvow?aq#,=\t9#wqvf/`kbqwfqwlvqjpn`obppj`sql`ffgf{sobjm?,k2=\x0e\tlmojmf-<{no#ufkfosjmdgjbnlmgvpf#wkfbjqojmffmg#..=*-bwwq+qfbgfqpklpwjmd eeeeeeqfbojyfUjm`fmwpjdmbop#pq`>!,Sqlgv`wgfpsjwfgjufqpfwfoojmdSvaoj`#kfog#jmIlpfsk#wkfbwqfbeef`wp?pwzof=b#obqdfglfpm$wobwfq/#Fofnfmwebuj`lm`qfbwlqKvmdbqzBjqslqwpff#wkfpl#wkbwNj`kbfoPzpwfnpSqldqbnp/#bmg##tjgwk>f%rvlw8wqbgjmdofew!=\tsfqplmpDlogfm#Beebjqpdqbnnbqelqnjmdgfpwqlzjgfb#le`bpf#lelogfpw#wkjp#jp-pq`#>#`bqwllmqfdjpwq@lnnlmpNvpojnpTkbw#jpjm#nbmznbqhjmdqfufbopJmgffg/frvbooz,pklt\\blvwgllqfp`bsf+Bvpwqjbdfmfwj`pzpwfn/Jm#wkf#pjwwjmdKf#boplJpobmgpB`bgfnz\t\n\n?"..Gbmjfo#ajmgjmdaol`h!=jnslpfgvwjojyfBaqbkbn+f{`fswxtjgwk9svwwjmd*-kwno+\x7f\x7f#X^8\tGBWBX#)hjw`kfmnlvmwfgb`wvbo#gjbof`wnbjmoz#\\aobmh$jmpwboof{sfqwpje+wzsfJw#bopl%`lsz8#!=Wfqnpalqm#jmLswjlmpfbpwfqmwbohjmd`lm`fqmdbjmfg#lmdljmdivpwjez`qjwj`peb`wlqzjwp#ltmbppbvowjmujwfgobpwjmdkjp#ltmkqfe>!,!#qfo>!gfufols`lm`fqwgjbdqbngloobqp`ovpwfqsksbo`lklo*8~*+*8vpjmd#b=?psbm=ufppfopqfujuboBggqfppbnbwfvqbmgqljgboofdfgjoomfpptbohjmd`fmwfqprvbojeznbw`kfpvmjejfgf{wjm`wGfefmpfgjfg#jm\t\n?"..#`vpwlnpojmhjmdOjwwof#Allh#lefufmjmdnjm-iptfbqjmdBoo#Qjd8\t~*+*8qbjpjmd#Bopl/#`qv`jbobalvw!=gf`obqf..=\t?p`ejqfel{bp#nv`kbssojfpjmgf{/#p/#avw#wzsf#>#\t\x0e\t?"..wltbqgpQf`lqgpSqjubwfElqfjdmSqfnjfq`klj`fpUjqwvboqfwvqmp@lnnfmwSltfqfgjmojmf8slufqwz`kbnafqOjujmd#ulovnfpBmwklmzoldjm!#QfobwfgF`lmlnzqfb`kfp`vwwjmddqbujwzojef#jm@kbswfq.pkbgltMlwbaof?,wg=\x0e\t#qfwvqmpwbgjvntjgdfwpubqzjmdwqbufopkfog#aztkl#bqftlqh#jmeb`vowzbmdvobqtkl#kbgbjqslqwwltm#le\t\tPlnf#$`oj`h$`kbqdfphfztlqgjw#tjoo`jwz#le+wkjp*8Bmgqft#vmjrvf#`kf`hfglq#nlqf033s{8#qfwvqm8qpjlm>!sovdjmptjwkjm#kfqpfoePwbwjlmEfgfqboufmwvqfsvaojpkpfmw#wlwfmpjlmb`wqfpp`lnf#wlejmdfqpGvhf#lesflsof/f{soljwtkbw#jpkbqnlmzb#nbilq!9!kwwsjm#kjp#nfmv!=\tnlmwkozleej`fq`lvm`jodbjmjmdfufm#jmPvnnbqzgbwf#leolzbowzejwmfppbmg#tbpfnsfqlqpvsqfnfPf`lmg#kfbqjmdQvppjbmolmdfpwBoafqwbobwfqbopfw#le#pnboo!=-bssfmggl#tjwkefgfqboabmh#leafmfbwkGfpsjwf@bsjwbodqlvmgp*/#bmg#sfq`fmwjw#eqln`olpjmd`lmwbjmJmpwfbgejewffmbp#tfoo-zbkll-qfpslmgejdkwfqlap`vqfqfeof`wlqdbmj`>#Nbwk-fgjwjmdlmojmf#sbggjmdb#tkloflmfqqlqzfbq#lefmg#le#abqqjfqtkfm#jwkfbgfq#klnf#leqfpvnfgqfmbnfgpwqlmd=kfbwjmdqfwbjmp`olvgeqtbz#le#Nbq`k#2hmltjmdjm#sbqwAfwtffmofpplmp`olpfpwujqwvboojmhp!=`qlppfgFMG#..=ebnlvp#btbqgfgOj`fmpfKfbowk#ebjqoz#tfbowkznjmjnboBeqj`bm`lnsfwfobafo!=pjmdjmdebqnfqpAqbpjo*gjp`vppqfsob`fDqfdlqzelmw#`lsvqpvfgbssfbqpnbhf#vsqlvmgfgalwk#leaol`hfgpbt#wkfleej`fp`lolvqpje+gl`vtkfm#kffmelq`fsvpk+evBvdvpw#VWE.;!=Ebmwbpzjm#nlpwjmivqfgVpvboozebqnjmd`olpvqflaif`w#gfefm`fvpf#le#Nfgj`bo?algz=\tfujgfmwaf#vpfghfz@lgfpj{wffmJpobnj` 333333fmwjqf#tjgfoz#b`wjuf#+wzsflelmf#`bm`lolq#>psfbhfqf{wfmgpSkzpj`pwfqqbjm?walgz=evmfqboujftjmdnjggof#`qj`hfwsqlskfwpkjewfggl`wlqpQvppfoo#wbqdfw`lnsb`wbodfaqbpl`jbo.avoh#lenbm#bmg?,wg=\t#kf#ofew*-ubo+*ebopf*8oldj`boabmhjmdklnf#wlmbnjmd#Bqjylmb`qfgjwp*8\t~*8\telvmgfqjm#wvqm@loojmpafelqf#Avw#wkf`kbqdfgWjwof!=@bswbjmpsfoofgdlggfppWbd#..=Bggjmd9avw#tbpQf`fmw#sbwjfmwab`h#jm>ebopf%Ojm`lomtf#hmlt@lvmwfqIvgbjpnp`qjsw#bowfqfg$^*8\t##kbp#wkfvm`ofbqFufmw$/alwk#jmmlw#boo\t\t?"..#sob`jmdkbqg#wl#`fmwfqplqw#le`ojfmwppwqffwpAfqmbqgbppfqwpwfmg#wlebmwbpzgltm#jmkbqalvqEqffglniftfoqz,balvw--pfbq`kofdfmgpjp#nbgfnlgfqm#lmoz#lmlmoz#wljnbdf!#ojmfbq#sbjmwfqbmg#mlwqbqfoz#b`qlmzngfojufqpklqwfq33%bns8bp#nbmztjgwk>!,)#?"X@wjwof#>le#wkf#oltfpw#sj`hfg#fp`bsfgvpfp#lesflsofp#Svaoj`Nbwwkftwb`wj`pgbnbdfgtbz#elqobtp#lefbpz#wl#tjmgltpwqlmd##pjnsof~`bw`k+pfufmwkjmelal{tfmw#wlsbjmwfg`jwjyfmJ#glm$wqfwqfbw-#Plnf#tt-!*8\talnajmdnbjowl9nbgf#jm-#Nbmz#`bqqjfp\x7f\x7fx~8tjtlqh#lepzmlmzngfefbwpebulqfglswj`bosbdfWqbvmofpp#pfmgjmdofew!=?`lnP`lqBoo#wkfiRvfqz-wlvqjpw@obppj`ebopf!#Tjokfonpvavqapdfmvjmfajpklsp-psojw+dolabo#elooltpalgz#lemlnjmbo@lmwb`wpf`vobqofew#wl`kjfeoz.kjggfm.abmmfq?,oj=\t\t-#Tkfm#jm#alwkgjpnjppF{solqfbotbzp#ujb#wkfpsb/]lotfoebqfqvojmd#bqqbmdf`bswbjmkjp#plmqvof#lekf#wllhjwpfoe/>3%bns8+`boofgpbnsofpwl#nbhf`ln,sbdNbqwjm#Hfmmfgzb``fswpevoo#lekbmgofgAfpjgfp,,..=?,baof#wlwbqdfwpfppfm`fkjn#wl#jwp#az#`lnnlm-njmfqbowl#wbhftbzp#wlp-lqd,obgujpfgsfmbowzpjnsof9je#wkfzOfwwfqpb#pklqwKfqafqwpwqjhfp#dqlvsp-ofmdwkeojdkwplufqobspoltoz#ofppfq#pl`jbo#?,s=\t\n\njw#jmwlqbmhfg#qbwf#levo=\x0e\t##bwwfnswsbjq#lenbhf#jwHlmwbhwBmwlmjlkbujmd#qbwjmdp#b`wjufpwqfbnpwqbssfg!*-`pp+klpwjofofbg#wlojwwof#dqlvsp/Sj`wvqf..=\x0e\t\x0e\t#qltp>!#laif`wjmufqpf?ellwfq@vpwlnU=?_,p`qploujmd@kbnafqpobufqztlvmgfgtkfqfbp">#$vmgelq#boosbqwoz#.qjdkw9Bqbajbmab`hfg#`fmwvqzvmjw#lenlajof.Fvqlsf/jp#klnfqjph#legfpjqfg@ojmwlm`lpw#lebdf#le#af`lnf#mlmf#les%rvlw8Njggof#fbg$*X3@qjwj`ppwvgjlp=%`lsz8dqlvs!=bppfnaonbhjmd#sqfppfgtjgdfw-sp9!#<#qfavjowaz#plnfElqnfq#fgjwlqpgfobzfg@bmlmj`kbg#wkfsvpkjmd`obpp>!avw#bqfsbqwjboAbazolmalwwln#`bqqjfq@lnnbmgjwp#vpfBp#tjwk`lvqpfpb#wkjqggfmlwfpbopl#jmKlvpwlm13s{8!=b``vpfgglvaof#dlbo#leEbnlvp#*-ajmg+sqjfpwp#Lmojmfjm#Ivozpw#(#!d`lmpvowgf`jnbokfosevoqfujufgjp#ufqzq$($jswolpjmd#efnbofpjp#boplpwqjmdpgbzp#lebqqjuboevwvqf#?laif`welq`jmdPwqjmd+!#,=\t\n\nkfqf#jpfm`lgfg-##Wkf#aboollmglmf#az,`lnnlmad`lolqobt#le#Jmgjbmbbuljgfgavw#wkf1s{#0s{irvfqz-bewfq#bsloj`z-nfm#bmgellwfq.>#wqvf8elq#vpfp`qffm-Jmgjbm#jnbdf#>ebnjoz/kwws9,,#%maps8gqjufqpfwfqmbopbnf#bpmlwj`fgujftfqp~*+*8\t#jp#nlqfpfbplmpelqnfq#wkf#mftjp#ivpw`lmpfmw#Pfbq`ktbp#wkftkz#wkfpkjssfgaq=?aq=tjgwk9#kfjdkw>nbgf#le`vjpjmfjp#wkbwb#ufqz#Bgnjqbo#ej{fg8mlqnbo#NjppjlmSqfpp/#lmwbqjl`kbqpfwwqz#wl#jmubgfg>!wqvf!psb`jmdjp#nlpwb#nlqf#wlwboozeboo#le~*8\x0e\t##jnnfmpfwjnf#jmpfw#lvwpbwjpezwl#ejmggltm#wlolw#le#Sobzfqpjm#Ivmfrvbmwvnmlw#wkfwjnf#wlgjpwbmwEjmmjpkpq`#>#+pjmdof#kfos#leDfqnbm#obt#bmgobafofgelqfpwp`llhjmdpsb`f!=kfbgfq.tfoo#bpPwbmofzaqjgdfp,dolabo@qlbwjb#Balvw#X3^8\t##jw/#bmgdqlvsfgafjmd#b*xwkqltkf#nbgfojdkwfqfwkj`boEEEEEE!alwwln!ojhf#b#fnsolzpojuf#jmbp#pffmsqjmwfqnlpw#leva.ojmhqfif`wpbmg#vpfjnbdf!=pv``ffgeffgjmdMv`ofbqjmelqnbwl#kfosTlnfm$pMfjwkfqNf{j`bmsqlwfjm?wbaof#az#nbmzkfbowkzobtpvjwgfujpfg-svpk+xpfoofqppjnsoz#Wkqlvdk-`llhjf#Jnbdf+logfq!=vp-ip!=#Pjm`f#vmjufqpobqdfq#lsfm#wl"..#fmgojfp#jm$^*8\x0e\t##nbqhfwtkl#jp#+!GLN@lnbmbdfglmf#elqwzsfle#Hjmdglnsqlejwpsqlslpfwl#pklt`fmwfq8nbgf#jwgqfppfgtfqf#jmnj{wvqfsqf`jpfbqjpjmdpq`#>#$nbhf#b#pf`vqfgAbswjpwulwjmd#\t\n\nubq#Nbq`k#1dqft#vs@ojnbwf-qfnlufphjoofgtbz#wkf?,kfbg=eb`f#leb`wjmd#qjdkw!=wl#tlqhqfgv`fpkbp#kbgfqf`wfgpklt+*8b`wjlm>allh#lebm#bqfb>>#!kww?kfbgfq\t?kwno=`lmelqneb`jmd#`llhjf-qfoz#lmklpwfg#-`vpwlnkf#tfmwavw#elqpsqfbg#Ebnjoz#b#nfbmplvw#wkfelqvnp-ellwbdf!=Nlajo@ofnfmwp!#jg>!bp#kjdkjmwfmpf..=?"..efnbof#jp#pffmjnsojfgpfw#wkfb#pwbwfbmg#kjpebpwfpwafpjgfpavwwlm\\alvmgfg!=?jnd#Jmelal{fufmwp/b#zlvmdbmg#bqfMbwjuf#`kfbsfqWjnflvwbmg#kbpfmdjmfptlm#wkf+nlpwozqjdkw9#ejmg#b#.alwwlnSqjm`f#bqfb#lenlqf#lepfbq`k\\mbwvqf/ofdboozsfqjlg/obmg#lelq#tjwkjmgv`fgsqlujmdnjppjofol`boozBdbjmpwwkf#tbzh%rvlw8s{8!=\x0e\tsvpkfg#babmglmmvnfqbo@fqwbjmJm#wkjpnlqf#jmlq#plnfmbnf#jpbmg/#jm`qltmfgJPAM#3.`qfbwfpL`wlafqnbz#mlw`fmwfq#obwf#jmGfefm`ffmb`wfgtjpk#wlaqlbgoz`llojmdlmolbg>jw-#Wkfqf`lufqNfnafqpkfjdkw#bppvnfp?kwno=\tsflsof-jm#lmf#>tjmgltellwfq\\b#dllg#qfhobnblwkfqp/wl#wkjp\\`llhjfsbmfo!=Olmglm/gfejmfp`qvpkfgabswjpn`lbpwbopwbwvp#wjwof!#nluf#wlolpw#jmafwwfq#jnsojfpqjuboqzpfqufqp#PzpwfnSfqkbspfp#bmg#`lmwfmgeoltjmdobpwfg#qjpf#jmDfmfpjpujft#leqjpjmd#pffn#wlavw#jm#ab`hjmdkf#tjoodjufm#bdjujmd#`jwjfp-eolt#le#Obwfq#boo#avwKjdktbzlmoz#azpjdm#lekf#glfpgjeefqpabwwfqz%bns8obpjmdofpwkqfbwpjmwfdfqwbhf#lmqfevpfg`boofg#>VP%bnsPff#wkfmbwjufpaz#wkjppzpwfn-kfbg#le9klufq/ofpajbmpvqmbnfbmg#boo`lnnlm,kfbgfq\\\\sbqbnpKbqubqg,sj{fo-qfnlubopl#olmdqlof#leiljmwozphzp`qbVmj`lgfaq#,=\x0e\tBwobmwbmv`ofvp@lvmwz/svqfoz#`lvmw!=fbpjoz#avjog#blm`oj`hb#djufmsljmwfqk%rvlw8fufmwp#fopf#x\tgjwjlmpmlt#wkf/#tjwk#nbm#tkllqd,Tfalmf#bmg`buboqzKf#gjfgpfbwwof33/333#xtjmgltkbuf#wlje+tjmgbmg#jwpplofoz#n%rvlw8qfmftfgGfwqljwbnlmdpwfjwkfq#wkfn#jmPfmbwlqVp?,b=?Hjmd#leEqbm`jp.sqlgv`kf#vpfgbqw#bmgkjn#bmgvpfg#azp`lqjmdbw#klnfwl#kbufqfobwfpjajojwzeb`wjlmAveebolojmh!=?tkbw#kfeqff#wl@jwz#le`lnf#jmpf`wlqp`lvmwfglmf#gbzmfqulvpprvbqf#~8je+dljm#tkbwjnd!#bojp#lmozpfbq`k,wvfpgbzollpfozPlolnlmpf{vbo#.#?b#kqnfgjvn!GL#MLW#Eqbm`f/tjwk#b#tbq#bmgpf`lmg#wbhf#b#=\x0e\t\x0e\t\x0e\tnbqhfw-kjdktbzglmf#jm`wjujwz!obpw!=laojdfgqjpf#wl!vmgfejnbgf#wl#Fbqoz#sqbjpfgjm#jwp#elq#kjpbwkofwfIvsjwfqZbkll"#wfqnfg#pl#nbmzqfbooz#p-#Wkf#b#tlnbmgjqf`w#qjdkw!#aj`z`ofb`jmd>!gbz#bmgpwbwjmdQbwkfq/kjdkfq#Leej`f#bqf#mltwjnfp/#tkfm#b#sbz#elqlm#wkjp.ojmh!=8alqgfqbqlvmg#bmmvbo#wkf#Mftsvw#wkf-`ln!#wbhjm#wlb#aqjfe+jm#wkfdqlvsp-8#tjgwkfmyznfppjnsof#jm#obwfxqfwvqmwkfqbszb#sljmwabmmjmdjmhp!=\t+*8!#qfb#sob`f_v330@bbalvw#bwq=\x0e\t\n\n``lvmw#djufp#b?P@QJSWQbjotbzwkfnfp,wlloal{AzJg+!{kvnbmp/tbw`kfpjm#plnf#je#+tj`lnjmd#elqnbwp#Vmgfq#avw#kbpkbmgfg#nbgf#azwkbm#jmefbq#legfmlwfg,jeqbnfofew#jmulowbdfjm#fb`kb%rvlw8abpf#leJm#nbmzvmgfqdlqfdjnfpb`wjlm#?,s=\x0e\t?vpwlnUb8%dw8?,jnslqwplq#wkbwnlpwoz#%bns8qf#pjyf>!?,b=?,kb#`obppsbppjufKlpw#>#TkfwkfqefqwjofUbqjlvp>X^8+ev`bnfqbp,=?,wg=b`wp#bpJm#plnf=\x0e\t\x0e\t?"lqdbmjp#?aq#,=Afjijmd`bwbo/Lgfvwp`kfvqlsfvfvphbqbdbfjodfpufmphbfpsb/]bnfmpbifvpvbqjlwqbabiln/E{j`ls/Mdjmbpjfnsqfpjpwfnbl`wvaqfgvqbmwfb/]bgjqfnsqfpbnlnfmwlmvfpwqlsqjnfqbwqbu/Epdqb`jbpmvfpwqbsql`fplfpwbglp`bojgbgsfqplmbm/Vnfqlb`vfqgln/Vpj`bnjfnaqllefqwbpbodvmlpsb/Apfpfifnsolgfqf`klbgfn/Mpsqjubglbdqfdbqfmob`fpslpjaofklwfofppfujoobsqjnfql/Vowjnlfufmwlpbq`kjul`vowvqbnvifqfpfmwqbgbbmvm`jlfnabqdlnfq`bgldqbmgfpfpwvgjlnfilqfpefaqfqlgjpf/]lwvqjpnl`/_gjdlslqwbgbfpsb`jlebnjojbbmwlmjlsfqnjwfdvbqgbqbodvmbpsqf`jlpbodvjfmpfmwjglujpjwbpw/Awvol`lml`fqpfdvmgl`lmpfileqbm`jbnjmvwlppfdvmgbwfmfnlpfef`wlpn/Mobdbpfpj/_mqfujpwbdqbmbgb`lnsqbqjmdqfpldbq`/Abb``j/_mf`vbglqrvjfmfpjm`ovplgfafq/Mnbwfqjbklnaqfpnvfpwqbslgq/Abnb/]bmb/Vowjnbfpwbnlplej`jbowbnajfmmjmd/Vmpbovglpslgfnlpnfilqbqslpjwjlmavpjmfppklnfsbdfpf`vqjwzobmdvbdfpwbmgbqg`bnsbjdmefbwvqfp`bwfdlqzf{wfqmbo`kjogqfmqfpfqufgqfpfbq`kf{`kbmdfebulqjwfwfnsobwfnjojwbqzjmgvpwqzpfquj`fpnbwfqjbosqlgv`wpy.jmgf{9`lnnfmwpplewtbqf`lnsofwf`bofmgbqsobwelqnbqwj`ofpqfrvjqfgnlufnfmwrvfpwjlmavjogjmdslojwj`pslppjaofqfojdjlmskzpj`boeffgab`hqfdjpwfqsj`wvqfpgjpbaofgsqlwl`lobvgjfm`fpfwwjmdpb`wjujwzfofnfmwpofbqmjmdbmzwkjmdbapwqb`wsqldqfpplufqujftnbdbyjmff`lmlnj`wqbjmjmdsqfppvqfubqjlvp#?pwqlmd=sqlsfqwzpklssjmdwldfwkfqbgubm`fgafkbujlqgltmolbgefbwvqfgellwaboopfof`wfgObmdvbdfgjpwbm`fqfnfnafqwqb`hjmdsbpptlqgnlgjejfgpwvgfmwpgjqf`wozejdkwjmdmlqwkfqmgbwbabpfefpwjuboaqfbhjmdol`bwjlmjmwfqmfwgqlsgltmsqb`wj`ffujgfm`fevm`wjlmnbqqjbdfqfpslmpfsqlaofnpmfdbwjufsqldqbnpbmbozpjpqfofbpfgabmmfq!=svq`kbpfsloj`jfpqfdjlmbo`qfbwjufbqdvnfmwallhnbqhqfefqqfq`kfnj`bogjujpjlm`booab`hpfsbqbwfsqlif`wp`lmeoj`wkbqgtbqfjmwfqfpwgfojufqznlvmwbjmlawbjmfg>#ebopf8elq+ubq#b``fswfg`bsb`jwz`lnsvwfqjgfmwjwzbjq`qbewfnsolzfgsqlslpfgglnfpwj`jm`ovgfpsqlujgfgklpsjwboufqwj`bo`loobspfbssqlb`ksbqwmfqpoldl!=?bgbvdkwfqbvwklq!#`vowvqboebnjojfp,jnbdfp,bppfnaozsltfqevowfb`kjmdejmjpkfggjpwqj`w`qjwj`bo`dj.ajm,svqslpfpqfrvjqfpfof`wjlmaf`lnjmdsqlujgfpb`bgfnj`f{fq`jpfb`wvbooznfgj`jmf`lmpwbmwb``jgfmwNbdbyjmfgl`vnfmwpwbqwjmdalwwln!=lapfqufg9#%rvlw8f{wfmgfgsqfujlvpPlewtbqf`vpwlnfqgf`jpjlmpwqfmdwkgfwbjofgpojdkwozsobmmjmdwf{wbqfb`vqqfm`zfufqzlmfpwqbjdkwwqbmpefqslpjwjufsqlgv`fgkfqjwbdfpkjssjmdbaplovwfqf`fjufgqfofubmwavwwlm!#ujlofm`fbmztkfqfafmfejwpobvm`kfgqf`fmwozboojbm`felooltfgnvowjsofavoofwjmjm`ovgfgl``vqqfgjmwfqmbo\'+wkjp*-qfsvaoj`=?wq=?wg`lmdqfppqf`lqgfgvowjnbwfplovwjlm?vo#jg>!gjp`lufqKlnf?,b=tfapjwfpmfwtlqhpbowklvdkfmwjqfoznfnlqjbonfppbdfp`lmwjmvfb`wjuf!=plnftkbwuj`wlqjbTfpwfqm##wjwof>!Ol`bwjlm`lmwqb`wujpjwlqpGltmolbgtjwklvw#qjdkw!=\tnfbpvqfptjgwk#>#ubqjbaofjmuloufgujqdjmjbmlqnboozkbssfmfgb``lvmwppwbmgjmdmbwjlmboQfdjpwfqsqfsbqfg`lmwqlopb``vqbwfajqwkgbzpwqbwfdzleej`jbodqbskj`p`qjnjmboslppjaoz`lmpvnfqSfqplmbopsfbhjmdubojgbwfb`kjfufg-isd!#,=nb`kjmfp?,k1=\t##hfztlqgpeqjfmgozaqlwkfqp`lnajmfglqjdjmbo`lnslpfgf{sf`wfgbgfrvbwfsbhjpwbmeloolt!#ubovbaof?,obafo=qfobwjufaqjmdjmdjm`qfbpfdlufqmlqsovdjmp,Ojpw#le#Kfbgfq!=!#mbnf>!#+%rvlw8dqbgvbwf?,kfbg=\t`lnnfq`fnbobzpjbgjqf`wlqnbjmwbjm8kfjdkw9p`kfgvof`kbmdjmdab`h#wl#`bwkloj`sbwwfqmp`lolq9# dqfbwfpwpvssojfpqfojbaof?,vo=\t\n\n?pfof`w#`jwjyfmp`olwkjmdtbw`kjmd?oj#jg>!psf`jej``bqqzjmdpfmwfm`f?`fmwfq=`lmwqbpwwkjmhjmd`bw`k+f*plvwkfqmNj`kbfo#nfq`kbmw`bqlvpfosbggjmd9jmwfqjlq-psojw+!ojybwjlmL`wlafq#*xqfwvqmjnsqlufg..%dw8\t\t`lufqbdf`kbjqnbm-smd!#,=pvaif`wpQj`kbqg#tkbwfufqsqlabaozqf`lufqzabpfabooivgdnfmw`lmmf`w--`pp!#,=#tfapjwfqfslqwfggfebvow!,=?,b=\x0e\tfof`wqj`p`lwobmg`qfbwjlmrvbmwjwz-#JPAM#3gjg#mlw#jmpwbm`f.pfbq`k.!#obmd>!psfbhfqp@lnsvwfq`lmwbjmpbq`kjufpnjmjpwfqqfb`wjlmgjp`lvmwJwbojbml`qjwfqjbpwqlmdoz9#$kwws9$p`qjsw$`lufqjmdleefqjmdbssfbqfgAqjwjpk#jgfmwjezEb`fallhmvnfqlvpufkj`ofp`lm`fqmpBnfqj`bmkbmgojmdgju#jg>!Tjoojbn#sqlujgfq\\`lmwfmwb``vqb`zpf`wjlm#bmgfqplmeof{jaof@bwfdlqzobtqfm`f?p`qjsw=obzlvw>!bssqlufg#nb{jnvnkfbgfq!=?,wbaof=Pfquj`fpkbnjowlm`vqqfmw#`bmbgjbm`kbmmfop,wkfnfp,,bqwj`oflswjlmboslqwvdboubovf>!!jmwfqubotjqfofppfmwjwofgbdfm`jfpPfbq`k!#nfbpvqfgwklvpbmgpsfmgjmd%kfoojs8mft#Gbwf!#pjyf>!sbdfMbnfnjggof!#!#,=?,b=kjggfm!=pfrvfm`fsfqplmbolufqeoltlsjmjlmpjoojmljpojmhp!=\t\n?wjwof=ufqpjlmppbwvqgbzwfqnjmbojwfnsqlsfmdjmffqpf`wjlmpgfpjdmfqsqlslpbo>!ebopf!Fpsb/]loqfofbpfppvanjw!#fq%rvlw8bggjwjlmpznswlnplqjfmwfgqfplvq`fqjdkw!=?sofbpvqfpwbwjlmpkjpwlqz-ofbujmd##alqgfq>`lmwfmwp`fmwfq!=-\t\tPlnf#gjqf`wfgpvjwbaofavodbqjb-pklt+*8gfpjdmfgDfmfqbo#`lm`fswpF{bnsofptjoojbnpLqjdjmbo!=?psbm=pfbq`k!=lsfqbwlqqfrvfpwpb#%rvlw8booltjmdGl`vnfmwqfujpjlm-#\t\tWkf#zlvqpfoe@lmwb`w#nj`kjdbmFmdojpk#`lovnajbsqjlqjwzsqjmwjmdgqjmhjmdeb`jojwzqfwvqmfg@lmwfmw#leej`fqpQvppjbm#dfmfqbwf.;;6:.2!jmgj`bwfebnjojbq#rvbojwznbqdjm93#`lmwfmwujftslqw`lmwb`wp.wjwof!=slqwbaof-ofmdwk#fojdjaofjmuloufpbwobmwj`lmolbg>!gfebvow-pvssojfgsbznfmwpdolppbqz\t\tBewfq#dvjgbm`f?,wg=?wgfm`lgjmdnjggof!=`bnf#wl#gjpsobzpp`lwwjpkilmbwkbmnbilqjwztjgdfwp-`ojmj`bowkbjobmgwfb`kfqp?kfbg=\t\nbeef`wfgpvsslqwpsljmwfq8wlPwqjmd?,pnboo=lhobklnbtjoo#af#jmufpwlq3!#bow>!klojgbzpQfplvq`foj`fmpfg#+tkj`k#-#Bewfq#`lmpjgfqujpjwjmdf{solqfqsqjnbqz#pfbq`k!#bmgqljg!rvj`hoz#nffwjmdpfpwjnbwf8qfwvqm#8`lolq9 #kfjdkw>bssqlubo/#%rvlw8#`kf`hfg-njm-ip!nbdmfwj`=?,b=?,kelqf`bpw-#Tkjof#wkvqpgbzgufqwjpf%fb`vwf8kbp@obppfubovbwflqgfqjmdf{jpwjmdsbwjfmwp#Lmojmf#`lolqbglLswjlmp!`bnsafoo?"..#fmg?,psbm=??aq#,=\x0e\t\\slsvsp\x7fp`jfm`fp/%rvlw8#rvbojwz#Tjmgltp#bppjdmfgkfjdkw9#?a#`obppof%rvlw8#ubovf>!#@lnsbmzf{bnsofp?jeqbnf#afojfufpsqfpfmwpnbqpkboosbqw#le#sqlsfqoz*-\t\tWkf#wb{lmlnznv`k#le#?,psbm=\t!#gbwb.pqwvdv/Fpp`qlooWl#sqlif`w?kfbg=\x0e\tbwwlqmfzfnskbpjppslmplqpebm`zal{tlqog$p#tjogojef`kf`hfg>pfppjlmpsqldqbnns{8elmw.#Sqlif`wilvqmbopafojfufgub`bwjlmwklnsplmojdkwjmdbmg#wkf#psf`jbo#alqgfq>3`kf`hjmd?,walgz=?avwwlm#@lnsofwf`ofbqej{\t?kfbg=\tbqwj`of#?pf`wjlmejmgjmdpqlof#jm#slsvobq##L`wlafqtfapjwf#f{slpvqfvpfg#wl##`kbmdfplsfqbwfg`oj`hjmdfmwfqjmd`lnnbmgpjmelqnfg#mvnafqp##?,gju=`qfbwjmdlmPvanjwnbqzobmg`loofdfpbmbozwj`ojpwjmdp`lmwb`w-olddfgJmbgujplqzpjaojmdp`lmwfmw!p%rvlw8*p-#Wkjp#sb`hbdfp`kf`hal{pvddfpwpsqfdmbmwwlnlqqltpsb`jmd>j`lm-smdibsbmfpf`lgfabpfavwwlm!=dbnaojmdpv`k#bp#/#tkjof#?,psbm=#njpplvqjpslqwjmdwls92s{#-?,psbm=wfmpjlmptjgwk>!1obyzolbgmlufnafqvpfg#jm#kfjdkw>!`qjsw!=\t%maps8?,?wq=?wg#kfjdkw91,sqlgv`w`lvmwqz#jm`ovgf#ellwfq!#%ow8"..#wjwof!=?,irvfqz-?,elqn=\t+\vBl\bQ\x7f*+\vUm\x05Gx*kqubwphjjwbojbmlqln/Nm(ow/Pqh/Kf4K4]4C5dwbnaj/Emmlwj`jbpnfmpbifpsfqplmbpgfqf`klpmb`jlmbopfquj`jl`lmwb`wlvpvbqjlpsqldqbnbdlajfqmlfnsqfpbpbmvm`jlpubofm`jb`lolnajbgfpsv/Epgfslqwfpsqlzf`wlsqlgv`wls/Vaoj`lmlplwqlpkjpwlqjbsqfpfmwfnjoolmfpnfgjbmwfsqfdvmwbbmwfqjlqqf`vqplpsqlaofnbpbmwjbdlmvfpwqlplsjmj/_mjnsqjnjqnjfmwqbpbn/Eqj`bufmgfglqpl`jfgbgqfpsf`wlqfbojybqqfdjpwqlsbobaqbpjmwfq/Epfmwlm`fpfpsf`jbonjfnaqlpqfbojgbg`/_qglabybqbdlybs/Mdjmbppl`jbofpaolrvfbqdfpwj/_mborvjofqpjpwfnbp`jfm`jbp`lnsofwlufqpj/_m`lnsofwbfpwvgjlps/Vaoj`blaifwjulboj`bmwfavp`bglq`bmwjgbgfmwqbgbpb``jlmfpbq`kjulppvsfqjlqnbzlq/Abbofnbmjbevm`j/_m/Vowjnlpkb`jfmglbrvfoolpfgj`j/_mefqmbmglbnajfmwfeb`fallhmvfpwqbp`ojfmwfpsql`fplpabpwbmwfsqfpfmwbqfslqwbq`lmdqfplsvaoj`bq`lnfq`jl`lmwqbwli/_ufmfpgjpwqjwlw/E`mj`b`lmivmwlfmfqd/Abwqbabibqbpwvqjbpqf`jfmwfvwjojybqalofw/Ampboubglq`lqqf`wbwqbabilpsqjnfqlpmfdl`jlpojafqwbggfwboofpsbmwboobsq/_{jnlbonfq/Abbmjnbofprvj/Emfp`lqby/_mpf``j/_mavp`bmglls`jlmfpf{wfqjlq`lm`fswlwlgbu/Abdbofq/Abfp`qjajqnfgj`jmboj`fm`jb`lmpvowbbpsf`wlp`q/Awj`bg/_obqfpivpwj`jbgfafq/Mmsfq/Alglmf`fpjwbnbmwfmfqsfrvf/]lqf`jajgbwqjavmbowfmfqjef`bm`j/_m`bmbqjbpgfp`bqdbgjufqplpnboolq`bqfrvjfqfw/E`mj`lgfafq/Abujujfmgbejmbmybpbgfobmwfevm`jlmb`lmpfilpgje/A`jo`jvgbgfpbmwjdvbpbubmybgbw/Eqnjmlvmjgbgfpp/Mm`kfy`bnsb/]bplewlmj`qfujpwbp`lmwjfmfpf`wlqfpnlnfmwlpeb`vowbg`q/Egjwlgjufqpbppvsvfpwleb`wlqfppfdvmglpsfrvf/]b<_!?,pfof`w=Bvpwqbojb!#`obpp>!pjwvbwjlmbvwklqjwzelooltjmdsqjnbqjozlsfqbwjlm`kboofmdfgfufolsfgbmlmznlvpevm`wjlm#evm`wjlmp`lnsbmjfppwqv`wvqfbdqffnfmw!#wjwof>!slwfmwjbofgv`bwjlmbqdvnfmwppf`lmgbqz`lszqjdkwobmdvbdfpf{`ovpjuf`lmgjwjlm?,elqn=\x0e\tpwbwfnfmwbwwfmwjlmAjldqbskz~#fopf#x\tplovwjlmptkfm#wkf#Bmbozwj`pwfnsobwfpgbmdfqlvppbwfoojwfgl`vnfmwpsvaojpkfqjnslqwbmwsqlwlwzsfjmeovfm`f%qbrvl8?,feef`wjufdfmfqboozwqbmpelqnafbvwjevowqbmpslqwlqdbmjyfgsvaojpkfgsqlnjmfmwvmwjo#wkfwkvnambjoMbwjlmbo#-el`vp+*8lufq#wkf#njdqbwjlmbmmlvm`fgellwfq!=\tf{`fswjlmofpp#wkbmf{sfmpjufelqnbwjlmeqbnftlqhwfqqjwlqzmgj`bwjlm`vqqfmwoz`obppMbnf`qjwj`jpnwqbgjwjlmfopftkfqfBof{bmgfqbssljmwfgnbwfqjbopaqlbg`bpwnfmwjlmfgbeejojbwf?,lswjlm=wqfbwnfmwgjeefqfmw,gfebvow-Sqfpjgfmwlm`oj`h>!ajldqbskzlwkfqtjpfsfqnbmfmwEqbm/KbjpKlooztllgf{sbmpjlmpwbmgbqgp?,pwzof=\tqfgv`wjlmGf`fnafq#sqfefqqfg@bnaqjgdflsslmfmwpAvpjmfpp#`lmevpjlm=\t?wjwof=sqfpfmwfgf{sobjmfgglfp#mlw#tlqogtjgfjmwfqeb`fslpjwjlmpmftpsbsfq?,wbaof=\tnlvmwbjmpojhf#wkf#fppfmwjboejmbm`jbopfof`wjlmb`wjlm>!,babmglmfgFgv`bwjlmsbqpfJmw+pwbajojwzvmbaof#wl?,wjwof=\tqfobwjlmpMlwf#wkbwfeej`jfmwsfqelqnfgwtl#zfbqpPjm`f#wkfwkfqfelqftqbssfq!=bowfqmbwfjm`qfbpfgAbwwof#lesfq`fjufgwqzjmd#wlmf`fppbqzslqwqbzfgfof`wjlmpFojybafwk?,jeqbnf=gjp`lufqzjmpvqbm`fp-ofmdwk8ofdfmgbqzDfldqbskz`bmgjgbwf`lqslqbwfplnfwjnfppfquj`fp-jmkfqjwfg?,pwqlmd=@lnnvmjwzqfojdjlvpol`bwjlmp@lnnjwwffavjogjmdpwkf#tlqogml#olmdfqafdjmmjmdqfefqfm`f`bmmlw#afeqfrvfm`zwzsj`boozjmwl#wkf#qfobwjuf8qf`lqgjmdsqfpjgfmwjmjwjboozwf`kmjrvfwkf#lwkfqjw#`bm#aff{jpwfm`fvmgfqojmfwkjp#wjnfwfofsklmfjwfnp`lsfsqb`wj`fpbgubmwbdf*8qfwvqm#Elq#lwkfqsqlujgjmdgfnl`qb`zalwk#wkf#f{wfmpjufpveefqjmdpvsslqwfg`lnsvwfqp#evm`wjlmsqb`wj`bopbjg#wkbwjw#nbz#afFmdojpk?,eqln#wkf#p`kfgvofggltmolbgp?,obafo=\tpvpsf`wfgnbqdjm9#3psjqjwvbo?,kfbg=\t\tnj`qlplewdqbgvboozgjp`vppfgkf#af`bnff{f`vwjufirvfqz-ipklvpfklog`lmejqnfgsvq`kbpfgojwfqboozgfpwqlzfgvs#wl#wkfubqjbwjlmqfnbjmjmdjw#jp#mlw`fmwvqjfpIbsbmfpf#bnlmd#wkf`lnsofwfgbodlqjwknjmwfqfpwpqfafoojlmvmgfejmfgfm`lvqbdfqfpjybaofjmuloujmdpfmpjwjufvmjufqpbosqlujpjlm+bowklvdkefbwvqjmd`lmgv`wfg*/#tkj`k#`lmwjmvfg.kfbgfq!=Efaqvbqz#mvnfqlvp#lufqeolt9`lnslmfmweqbdnfmwpf{`foofmw`lopsbm>!wf`kmj`bomfbq#wkf#Bgubm`fg#plvq`f#lef{sqfppfgKlmd#Hlmd#Eb`fallhnvowjsof#nf`kbmjpnfofubwjlmleefmpjuf?,elqn=\t\npslmplqfggl`vnfmw-lq#%rvlw8wkfqf#bqfwklpf#tklnlufnfmwpsql`fppfpgjeej`vowpvanjwwfgqf`lnnfmg`lmujm`fgsqlnlwjmd!#tjgwk>!-qfsob`f+`obppj`bo`lbojwjlmkjp#ejqpwgf`jpjlmpbppjpwbmwjmgj`bwfgfulovwjlm.tqbssfq!fmlvdk#wlbolmd#wkfgfojufqfg..=\x0e\t?"..Bnfqj`bm#sqlwf`wfgMlufnafq#?,pwzof=?evqmjwvqfJmwfqmfw##lmaovq>!pvpsfmgfgqf`jsjfmwabpfg#lm#Nlqflufq/balojpkfg`loof`wfgtfqf#nbgffnlwjlmbofnfqdfm`zmbqqbwjufbgul`bwfps{8alqgfq`lnnjwwfggjq>!owq!fnsolzffpqfpfbq`k-#pfof`wfgpv``fpplq`vpwlnfqpgjpsobzfgPfswfnafqbgg@obpp+Eb`fallh#pvddfpwfgbmg#obwfqlsfqbwjmdfobalqbwfPlnfwjnfpJmpwjwvwf`fqwbjmozjmpwboofgelooltfqpIfqvpbofnwkfz#kbuf`lnsvwjmddfmfqbwfgsqlujm`fpdvbqbmwffbqajwqbqzqf`ldmjyftbmwfg#wls{8tjgwk9wkflqz#leafkbujlvqTkjof#wkffpwjnbwfgafdbm#wl#jw#af`bnfnbdmjwvgfnvpw#kbufnlqf#wkbmGjqf`wlqzf{wfmpjlmpf`qfwbqzmbwvqboozl``vqqjmdubqjbaofpdjufm#wkfsobwelqn-?,obafo=?ebjofg#wl`lnslvmgphjmgp#le#pl`jfwjfpbolmdpjgf#..%dw8\t\tplvwktfpwwkf#qjdkwqbgjbwjlmnbz#kbuf#vmfp`bsf+pslhfm#jm!#kqfe>!,sqldqbnnflmoz#wkf#`lnf#eqlngjqf`wlqzavqjfg#jmb#pjnjobqwkfz#tfqf?,elmw=?,Mlqtfdjbmpsf`jejfgsqlgv`jmdsbppfmdfq+mft#Gbwfwfnslqbqzej`wjlmboBewfq#wkffrvbwjlmpgltmolbg-qfdvobqozgfufolsfqbaluf#wkfojmhfg#wlskfmlnfmbsfqjlg#lewllowjs!=pvapwbm`fbvwlnbwj`bpsf`w#leBnlmd#wkf`lmmf`wfgfpwjnbwfpBjq#Elq`fpzpwfn#lelaif`wjufjnnfgjbwfnbhjmd#jwsbjmwjmdp`lmrvfqfgbqf#pwjoosql`fgvqfdqltwk#lekfbgfg#azFvqlsfbm#gjujpjlmpnlof`vofpeqbm`kjpfjmwfmwjlmbwwqb`wfg`kjogkllgbopl#vpfggfgj`bwfgpjmdbslqfgfdqff#leebwkfq#le`lmeoj`wp?,b=?,s=\t`bnf#eqlntfqf#vpfgmlwf#wkbwqf`fjujmdF{f`vwjuffufm#nlqfb``fpp#wl`lnnbmgfqSlojwj`bonvpj`jbmpgfoj`jlvpsqjplmfqpbgufmw#leVWE.;!#,=?"X@GBWBX!=@lmwb`wPlvwkfqm#ad`lolq>!pfqjfp#le-#Jw#tbp#jm#Fvqlsfsfqnjwwfgubojgbwf-bssfbqjmdleej`jboppfqjlvpoz.obmdvbdfjmjwjbwfgf{wfmgjmdolmd.wfqnjmeobwjlmpv`k#wkbwdfw@llhjfnbqhfg#az?,avwwlm=jnsofnfmwavw#jw#jpjm`qfbpfpgltm#wkf#qfrvjqjmdgfsfmgfmw..=\t?"..#jmwfqujftTjwk#wkf#`lsjfp#le`lmpfmpvptbp#avjowUfmfyvfob+elqnfqozwkf#pwbwfsfqplmmfopwqbwfdj`ebulvq#lejmufmwjlmTjhjsfgjb`lmwjmfmwujqwvbooztkj`k#tbpsqjm`jsof@lnsofwf#jgfmwj`bopklt#wkbwsqjnjwjufbtbz#eqlnnlof`vobqsqf`jpfozgjpploufgVmgfq#wkfufqpjlm>!=%maps8?,Jw#jp#wkf#Wkjp#jp#tjoo#kbuflqdbmjpnpplnf#wjnfEqjfgqj`ktbp#ejqpwwkf#lmoz#eb`w#wkbwelqn#jg>!sqf`fgjmdWf`kmj`boskzpj`jpwl``vqp#jmmbujdbwlqpf`wjlm!=psbm#jg>!plvdkw#wlafolt#wkfpvqujujmd~?,pwzof=kjp#gfbwkbp#jm#wkf`bvpfg#azsbqwjboozf{jpwjmd#vpjmd#wkftbp#djufmb#ojpw#leofufop#lemlwjlm#leLeej`jbo#gjpnjppfgp`jfmwjpwqfpfnaofpgvsoj`bwff{solpjufqf`lufqfgboo#lwkfqdboofqjfpxsbggjmd9sflsof#leqfdjlm#lebggqfppfpbppl`jbwfjnd#bow>!jm#nlgfqmpklvog#afnfwklg#leqfslqwjmdwjnfpwbnsmffgfg#wlwkf#Dqfbwqfdbqgjmdpffnfg#wlujftfg#bpjnsb`w#lmjgfb#wkbwwkf#Tlqogkfjdkw#lef{sbmgjmdWkfpf#bqf`vqqfmw!=`bqfevooznbjmwbjmp`kbqdf#le@obppj`bobggqfppfgsqfgj`wfgltmfqpkjs?gju#jg>!qjdkw!=\x0e\tqfpjgfm`fofbuf#wkf`lmwfmw!=bqf#lewfm##~*+*8\x0e\tsqlabaoz#Sqlefpplq.avwwlm!#qfpslmgfgpbzp#wkbwkbg#wl#afsob`fg#jmKvmdbqjbmpwbwvp#lepfqufp#bpVmjufqpbof{f`vwjlmbddqfdbwfelq#tkj`kjmef`wjlmbdqffg#wlkltfufq/#slsvobq!=sob`fg#lm`lmpwqv`wfof`wlqbopznalo#lejm`ovgjmdqfwvqm#wlbq`kjwf`w@kqjpwjbmsqfujlvp#ojujmd#jmfbpjfq#wlsqlefpplq\t%ow8"..#feef`w#lebmbozwj`ptbp#wbhfmtkfqf#wkfwllh#lufqafojfe#jmBeqjhbbmpbp#ebq#bpsqfufmwfgtlqh#tjwkb#psf`jbo?ejfogpfw@kqjpwnbpQfwqjfufg\t\tJm#wkf#ab`h#jmwlmlqwkfbpwnbdbyjmfp=?pwqlmd=`lnnjwwffdlufqmjmddqlvsp#lepwlqfg#jmfpwbaojpkb#dfmfqbojwp#ejqpwwkfjq#ltmslsvobwfgbm#laif`w@bqjaafbmboolt#wkfgjpwqj`wptjp`lmpjmol`bwjlm-8#tjgwk9#jmkbajwfgPl`jbojpwIbmvbqz#2?,ellwfq=pjnjobqoz`klj`f#lewkf#pbnf#psf`jej`#avpjmfpp#Wkf#ejqpw-ofmdwk8#gfpjqf#wlgfbo#tjwkpjm`f#wkfvpfqBdfmw`lm`fjufgjmgf{-sksbp#%rvlw8fmdbdf#jmqf`fmwoz/eft#zfbqptfqf#bopl\t?kfbg=\t?fgjwfg#azbqf#hmltm`jwjfp#jmb``fpphfz`lmgfnmfgbopl#kbufpfquj`fp/ebnjoz#leP`kllo#le`lmufqwfgmbwvqf#le#obmdvbdfnjmjpwfqp?,laif`w=wkfqf#jp#b#slsvobqpfrvfm`fpbgul`bwfgWkfz#tfqfbmz#lwkfqol`bwjlm>fmwfq#wkfnv`k#nlqfqfeof`wfgtbp#mbnfglqjdjmbo#b#wzsj`botkfm#wkfzfmdjmffqp`lvog#mlwqfpjgfmwptfgmfpgbzwkf#wkjqg#sqlgv`wpIbmvbqz#1tkbw#wkfzb#`fqwbjmqfb`wjlmpsql`fpplqbewfq#kjpwkf#obpw#`lmwbjmfg!=?,gju=\t?,b=?,wg=gfsfmg#lmpfbq`k!=\tsjf`fp#le`lnsfwjmdQfefqfm`fwfmmfppfftkj`k#kbp#ufqpjlm>?,psbm=#??,kfbgfq=djufp#wkfkjpwlqjbmubovf>!!=sbggjmd93ujft#wkbwwldfwkfq/wkf#nlpw#tbp#elvmgpvapfw#lebwwb`h#lm`kjogqfm/sljmwp#lesfqplmbo#slpjwjlm9boofdfgoz@ofufobmgtbp#obwfqbmg#bewfqbqf#djufmtbp#pwjoop`qloojmdgfpjdm#lenbhfp#wkfnv`k#ofppBnfqj`bmp-\t\tBewfq#/#avw#wkfNvpfvn#leolvjpjbmb+eqln#wkfnjmmfplwbsbqwj`ofpb#sql`fppGlnjmj`bmulovnf#leqfwvqmjmdgfefmpjuf33s{\x7fqjdknbgf#eqlnnlvpflufq!#pwzof>!pwbwfp#le+tkj`k#jp`lmwjmvfpEqbm`jp`lavjogjmd#tjwklvw#btjwk#plnftkl#tlvogb#elqn#leb#sbqw#leafelqf#jwhmltm#bp##Pfquj`fpol`bwjlm#bmg#lewfmnfbpvqjmdbmg#jw#jpsbsfqab`hubovfp#le\x0e\t?wjwof=>#tjmglt-gfwfqnjmffq%rvlw8#sobzfg#azbmg#fbqoz?,`fmwfq=eqln#wkjpwkf#wkqffsltfq#bmgle#%rvlw8jmmfqKWNO?b#kqfe>!z9jmojmf8@kvq`k#lewkf#fufmwufqz#kjdkleej`jbo#.kfjdkw9#`lmwfmw>!,`dj.ajm,wl#`qfbwfbeqjhbbmpfpsfqbmwleqbm/Kbjpobwujf)Mvojfwvuj)_(`f)Mwjmb(af)Mwjmb\fUh\fT{\fTN\n{I\np@\x04Fr\vBl\bQ\x7f\tA{\vUm\x05Gx\tA{\x01yp\x06YA\0zX\bTV\bWl\bUd\x04BM\vB{\npV\v@x\x04B\\\np@\x04Db\x04Gz\tal\npa\tfM\tuD\bV~\x04mx\vQ}\ndS\tp\\\bVK\bS]\bU|\x05oD\tkV\ved\vHR\nb~\x04M`\nJp\x05oD\x04|Q\nLP\x04Sw\bTl\nAI\nxC\bWt\tBq\x05F`\x04Cm\vLm\tKx\t}t\bPv\ny\\\naB\tV\x7f\nZd\x04XU\x04li\tfr\ti@\tBH\x04BD\x04BV\t`V\n[]\tp_\tTn\n~A\nxR\tuD\t`{\bV@\tTn\tHK\tAJ\vxs\x04Zf\nqI\x04Zf\vBM\v|j\t}t\bSM\nmC\vQ}pfquj`jlpbqw/A`volbqdfmwjmbabq`folmb`vborvjfqsvaoj`bglsqlgv`wlpslo/Awj`bqfpsvfpwbtjhjsfgjbpjdvjfmwfa/Vprvfgb`lnvmjgbgpfdvqjgbgsqjm`jsbosqfdvmwbp`lmwfmjglqfpslmgfqufmfyvfobsqlaofnbpgj`jfnaqfqfob`j/_mmlujfnaqfpjnjobqfpsqlzf`wlpsqldqbnbpjmpwjwvwlb`wjujgbgfm`vfmwqbf`lmln/Abjn/Mdfmfp`lmwb`wbqgfp`bqdbqmf`fpbqjlbwfm`j/_mwfo/Eelml`lnjpj/_m`bm`jlmfp`bsb`jgbgfm`lmwqbqbm/Mojpjpebulqjwlpw/Eqnjmlpsqlujm`jbfwjrvfwbpfofnfmwlpevm`jlmfpqfpvowbgl`bq/M`wfqsqlsjfgbgsqjm`jsjlmf`fpjgbgnvmj`jsbo`qfb`j/_mgfp`bqdbpsqfpfm`jb`lnfq`jbolsjmjlmfpfifq`j`jlfgjwlqjbopbobnbm`bdlmy/Mofygl`vnfmwlsfo/A`vobqf`jfmwfpdfmfqbofpwbqqbdlmbsq/M`wj`bmlufgbgfpsqlsvfpwbsb`jfmwfpw/E`mj`bplaifwjulp`lmwb`wlp\fHB\fIk\fHn\fH^\fHS\fHc\fHU\fId\fHn\fH{\fHC\fHR\fHT\fHR\fHI\fHc\fHY\fHn\fH\\\fHU\fIk\fHy\fIg\fHd\fHy\fIm\fHw\fH\\\fHU\fHR\fH@\fHR\fHJ\fHy\fHU\fHR\fHT\fHA\fIl\fHU\fIm\fHc\fH\\\fHU\fIl\fHB\fId\fHn\fHJ\fHS\fHD\fH@\fHR\fHHgjsolgl`p\fHT\fHB\fHC\fH\\\fIn\fHF\fHD\fHR\fHB\fHF\fHH\fHR\fHG\fHS\fH\\\fHx\fHT\fHH\fHH\fH\\\fHU\fH^\fIg\fH{\fHU\fIm\fHj\fH@\fHR\fH\\\fHJ\fIk\fHZ\fHU\fIm\fHd\fHz\fIk\fH^\fHC\fHJ\fHS\fHy\fHR\fHB\fHY\fIk\fH@\fHH\fIl\fHD\fH@\fIl\fHv\fHB\fI`\fHH\fHT\fHR\fH^\fH^\fIk\fHz\fHp\fIe\fH@\fHB\fHJ\fHJ\fHH\fHI\fHR\fHD\fHU\fIl\fHZ\fHU\fH\\\fHi\fH^\fH{\fHy\fHA\fIl\fHD\fH{\fH\\\fHF\fHR\fHT\fH\\\fHR\fHH\fHy\fHS\fHc\fHe\fHT\fIk\fH{\fHC\fIl\fHU\fIn\fHm\fHj\fH{\fIk\fHs\fIl\fHB\fHz\fIg\fHp\fHy\fHR\fH\\\fHi\fHA\fIl\fH{\fHC\fIk\fHH\fIm\fHB\fHY\fIg\fHs\fHJ\fIk\fHn\fHi\fH{\fH\\\fH|\fHT\fIk\fHB\fIk\fH^\fH^\fH{\fHR\fHU\fHR\fH^\fHf\fHF\fH\\\fHv\fHR\fH\\\fH|\fHT\fHR\fHJ\fIk\fH\\\fHp\fHS\fHT\fHJ\fHS\fH^\fH@\fHn\fHJ\fH@\fHD\fHR\fHU\fIn\fHn\fH^\fHR\fHz\fHp\fIl\fHH\fH@\fHs\fHD\fHB\fHS\fH^\fHk\fHT\fIk\fHj\fHD\fIk\fHD\fHC\fHR\fHy\fIm\fH^\fH^\fIe\fH{\fHA\fHR\fH{\fH\\\fIk\fH^\fHp\fH{\fHU\fH\\\fHR\fHB\fH^\fH{\fIk\fHF\fIk\fHp\fHU\fHR\fHI\fHk\fHT\fIl\fHT\fHU\fIl\fHy\fH^\fHR\fHL\fIl\fHy\fHU\fHR\fHm\fHJ\fIn\fH\\\fHH\fHU\fHH\fHT\fHR\fHH\fHC\fHR\fHJ\fHj\fHC\fHR\fHF\fHR\fHy\fHy\fI`\fHD\fHZ\fHR\fHB\fHJ\fIk\fHz\fHC\fHU\fIl\fH\\\fHR\fHC\fHz\fIm\fHJ\fH^\fH{\fIl`bwfdlqjfpf{sfqjfm`f?,wjwof=\x0e\t@lszqjdkw#ibubp`qjsw`lmgjwjlmpfufqzwkjmd?s#`obpp>!wf`kmloldzab`hdqlvmg?b#`obpp>!nbmbdfnfmw%`lsz8#132ibubP`qjsw`kbqb`wfqpaqfbg`qvnawkfnpfoufpklqjylmwbodlufqmnfmw@bojelqmjbb`wjujwjfpgjp`lufqfgMbujdbwjlmwqbmpjwjlm`lmmf`wjlmmbujdbwjlmbssfbqbm`f?,wjwof=?n`kf`hal{!#wf`kmjrvfpsqlwf`wjlmbssbqfmwozbp#tfoo#bpvmw$/#$VB.qfplovwjlmlsfqbwjlmpwfofujpjlmwqbmpobwfgTbpkjmdwlmmbujdbwlq-#>#tjmglt-jnsqfppjlm%ow8aq%dw8ojwfqbwvqfslsvobwjlmad`lolq>! fpsf`jbooz#`lmwfmw>!sqlgv`wjlmmftpofwwfqsqlsfqwjfpgfejmjwjlmofbgfqpkjsWf`kmloldzSbqojbnfmw`lnsbqjplmvo#`obpp>!-jmgf{Le+!`lm`ovpjlmgjp`vppjlm`lnslmfmwpajloldj`boQfulovwjlm\\`lmwbjmfqvmgfqpwllgmlp`qjsw=?sfqnjppjlmfb`k#lwkfqbwnlpskfqf#lmel`vp>!?elqn#jg>!sql`fppjmdwkjp-ubovfdfmfqbwjlm@lmefqfm`fpvapfrvfmwtfoo.hmltmubqjbwjlmpqfsvwbwjlmskfmlnfmlmgjp`jsojmfoldl-smd!#+gl`vnfmw/alvmgbqjfpf{sqfppjlmpfwwofnfmwAb`hdqlvmglvw#le#wkffmwfqsqjpf+!kwwsp9!#vmfp`bsf+!sbpptlqg!#gfnl`qbwj`?b#kqfe>!,tqbssfq!=\tnfnafqpkjsojmdvjpwj`s{8sbggjmdskjolplskzbppjpwbm`fvmjufqpjwzeb`jojwjfpqf`ldmjyfgsqfefqfm`fje#+wzsflenbjmwbjmfgul`bavobqzkzslwkfpjp-pvanjw+*8%bns8maps8bmmlwbwjlmafkjmg#wkfElvmgbwjlmsvaojpkfq!bppvnswjlmjmwqlgv`fg`lqqvswjlmp`jfmwjpwpf{soj`jwozjmpwfbg#legjnfmpjlmp#lm@oj`h>!`lmpjgfqfggfsbqwnfmwl``vsbwjlmpllm#bewfqjmufpwnfmwsqlmlvm`fgjgfmwjejfgf{sfqjnfmwNbmbdfnfmwdfldqbskj`!#kfjdkw>!ojmh#qfo>!-qfsob`f+,gfsqfppjlm`lmefqfm`fsvmjpknfmwfojnjmbwfgqfpjpwbm`fbgbswbwjlmlsslpjwjlmtfoo#hmltmpvssofnfmwgfwfqnjmfgk2#`obpp>!3s{8nbqdjmnf`kbmj`bopwbwjpwj`p`fofaqbwfgDlufqmnfmw\t\tGvqjmd#wgfufolsfqpbqwjej`jbofrvjubofmwlqjdjmbwfg@lnnjppjlmbwwb`knfmw?psbm#jg>!wkfqf#tfqfMfgfqobmgpafzlmg#wkfqfdjpwfqfgilvqmbojpweqfrvfmwozboo#le#wkfobmd>!fm!#?,pwzof=\x0e\tbaplovwf8#pvsslqwjmdf{wqfnfoz#nbjmpwqfbn?,pwqlmd=#slsvobqjwzfnsolznfmw?,wbaof=\x0e\t#`lopsbm>!?,elqn=\t##`lmufqpjlmbalvw#wkf#?,s=?,gju=jmwfdqbwfg!#obmd>!fmSlqwvdvfpfpvapwjwvwfjmgjujgvbojnslppjaofnvowjnfgjbbonlpw#boos{#plojg# bsbqw#eqlnpvaif`w#wljm#Fmdojpk`qjwj`jyfgf{`fsw#elqdvjgfojmfplqjdjmboozqfnbqhbaofwkf#pf`lmgk1#`obpp>!?b#wjwof>!+jm`ovgjmdsbqbnfwfqpsqlkjajwfg>#!kwws9,,gj`wjlmbqzsfq`fswjlmqfulovwjlmelvmgbwjlms{8kfjdkw9pv``fppevopvsslqwfqpnjoofmmjvnkjp#ebwkfqwkf#%rvlw8ml.qfsfbw8`lnnfq`jbojmgvpwqjbofm`lvqbdfgbnlvmw#le#vmleej`jbofeej`jfm`zQfefqfm`fp`llqgjmbwfgjp`objnfqf{sfgjwjlmgfufolsjmd`bo`vobwfgpjnsojejfgofdjwjnbwfpvapwqjmd+3!#`obpp>!`lnsofwfozjoovpwqbwfejuf#zfbqpjmpwqvnfmwSvaojpkjmd2!#`obpp>!spz`kloldz`lmejgfm`fmvnafq#le#bapfm`f#leel`vpfg#lmiljmfg#wkfpwqv`wvqfpsqfujlvpoz=?,jeqbnf=lm`f#bdbjmavw#qbwkfqjnnjdqbmwple#`lvqpf/b#dqlvs#leOjwfqbwvqfVmojhf#wkf?,b=%maps8\tevm`wjlm#jw#tbp#wkf@lmufmwjlmbvwlnlajofSqlwfpwbmwbddqfppjufbewfq#wkf#Pjnjobqoz/!#,=?,gju=`loof`wjlm\x0e\tevm`wjlmujpjajojwzwkf#vpf#leulovmwffqpbwwqb`wjlmvmgfq#wkf#wkqfbwfmfg)?"X@GBWBXjnslqwbm`fjm#dfmfqbowkf#obwwfq?,elqn=\t?,-jmgf{Le+$j#>#38#j#?gjeefqfm`fgfulwfg#wlwqbgjwjlmppfbq`k#elqvowjnbwfozwlvqmbnfmwbwwqjavwfppl.`boofg#~\t?,pwzof=fubovbwjlmfnskbpjyfgb``fppjaof?,pf`wjlm=pv``fppjlmbolmd#tjwkNfbmtkjof/jmgvpwqjfp?,b=?aq#,=kbp#af`lnfbpsf`wp#leWfofujpjlmpveej`jfmwabphfwabooalwk#pjgfp`lmwjmvjmdbm#bqwj`of?jnd#bow>!bgufmwvqfpkjp#nlwkfqnbm`kfpwfqsqjm`jsofpsbqwj`vobq`lnnfmwbqzfeef`wp#legf`jgfg#wl!=?pwqlmd=svaojpkfqpIlvqmbo#legjeej`vowzeb`jojwbwfb``fswbaofpwzof-`pp!\nevm`wjlm#jmmlubwjlm=@lszqjdkwpjwvbwjlmptlvog#kbufavpjmfppfpGj`wjlmbqzpwbwfnfmwplewfm#vpfgsfqpjpwfmwjm#Ibmvbqz`lnsqjpjmd?,wjwof=\t\ngjsolnbwj``lmwbjmjmdsfqelqnjmdf{wfmpjlmpnbz#mlw#af`lm`fsw#le#lm`oj`h>!Jw#jp#boplejmbm`jbo#nbhjmd#wkfOv{fnalvqdbggjwjlmbobqf#`boofgfmdbdfg#jm!p`qjsw!*8avw#jw#tbpfof`wqlmj`lmpvanjw>!\t?"..#Fmg#fof`wqj`boleej`jboozpvddfpwjlmwls#le#wkfvmojhf#wkfBvpwqbojbmLqjdjmboozqfefqfm`fp\t?,kfbg=\x0e\tqf`ldmjpfgjmjwjbojyfojnjwfg#wlBof{bmgqjbqfwjqfnfmwBgufmwvqfpelvq#zfbqp\t\t%ow8"..#jm`qfbpjmdgf`lqbwjlmk0#`obpp>!lqjdjmp#lelaojdbwjlmqfdvobwjlm`obppjejfg+evm`wjlm+bgubmwbdfpafjmd#wkf#kjpwlqjbmp?abpf#kqfeqfsfbwfgoztjoojmd#wl`lnsbqbaofgfpjdmbwfgmlnjmbwjlmevm`wjlmbojmpjgf#wkfqfufobwjlmfmg#le#wkfp#elq#wkf#bvwklqjyfgqfevpfg#wlwbhf#sob`fbvwlmlnlvp`lnsqlnjpfslojwj`bo#qfpwbvqbmwwtl#le#wkfEfaqvbqz#1rvbojwz#leptelaif`w-vmgfqpwbmgmfbqoz#bootqjwwfm#azjmwfqujftp!#tjgwk>!2tjwkgqbtboeolbw9ofewjp#vpvbooz`bmgjgbwfpmftpsbsfqpnzpwfqjlvpGfsbqwnfmwafpw#hmltmsbqojbnfmwpvssqfppfg`lmufmjfmwqfnfnafqfggjeefqfmw#pzpwfnbwj`kbp#ofg#wlsqlsbdbmgb`lmwqloofgjmeovfm`fp`fqfnlmjbosql`objnfgSqlwf`wjlmoj#`obpp>!P`jfmwjej``obpp>!ml.wqbgfnbqhpnlqf#wkbm#tjgfpsqfbgOjafqbwjlmwllh#sob`fgbz#le#wkfbp#olmd#bpjnsqjplmfgBggjwjlmbo\t?kfbg=\t?nObalqbwlqzMlufnafq#1f{`fswjlmpJmgvpwqjboubqjfwz#leeolbw9#ofeGvqjmd#wkfbppfppnfmwkbuf#affm#gfbop#tjwkPwbwjpwj`pl``vqqfm`f,vo=?,gju=`ofbqej{!=wkf#svaoj`nbmz#zfbqptkj`k#tfqflufq#wjnf/pzmlmznlvp`lmwfmw!=\tsqfpvnbaozkjp#ebnjozvpfqBdfmw-vmf{sf`wfgjm`ovgjmd#`kboofmdfgb#njmlqjwzvmgfejmfg!afolmdp#wlwbhfm#eqlnjm#L`wlafqslpjwjlm9#pbjg#wl#afqfojdjlvp#Efgfqbwjlm#qltpsbm>!lmoz#b#eftnfbmw#wkbwofg#wl#wkf..=\x0e\t?gju#?ejfogpfw=Bq`kajpkls#`obpp>!mlafjmd#vpfgbssqlb`kfpsqjujofdfpmlp`qjsw=\tqfpvowp#jmnbz#af#wkfFbpwfq#fddnf`kbmjpnpqfbplmbaofSlsvobwjlm@loof`wjlmpfof`wfg!=mlp`qjsw=\x0e,jmgf{-sksbqqjubo#le.ippgh$**8nbmbdfg#wljm`lnsofwf`bpvbowjfp`lnsofwjlm@kqjpwjbmpPfswfnafq#bqjwknfwj`sql`fgvqfpnjdkw#kbufSqlgv`wjlmjw#bssfbqpSkjolplskzeqjfmgpkjsofbgjmd#wldjujmd#wkfwltbqg#wkfdvbqbmwffggl`vnfmwfg`lolq9 333ujgfl#dbnf`lnnjppjlmqfeof`wjmd`kbmdf#wkfbppl`jbwfgpbmp.pfqjelmhfzsqfpp8#sbggjmd9Kf#tbp#wkfvmgfqozjmdwzsj`booz#/#bmg#wkf#pq`Fofnfmwpv``fppjufpjm`f#wkf#pklvog#af#mfwtlqhjmdb``lvmwjmdvpf#le#wkfoltfq#wkbmpkltp#wkbw?,psbm=\t\n\n`lnsobjmwp`lmwjmvlvprvbmwjwjfpbpwqlmlnfqkf#gjg#mlwgvf#wl#jwpbssojfg#wlbm#bufqbdffeelqwp#wlwkf#evwvqfbwwfnsw#wlWkfqfelqf/`bsbajojwzQfsvaoj`bmtbp#elqnfgFof`wqlmj`hjolnfwfqp`kboofmdfpsvaojpkjmdwkf#elqnfqjmgjdfmlvpgjqf`wjlmppvapjgjbqz`lmpsjqb`zgfwbjop#lebmg#jm#wkfbeelqgbaofpvapwbm`fpqfbplm#elq`lmufmwjlmjwfnwzsf>!baplovwfozpvsslpfgozqfnbjmfg#bbwwqb`wjufwqbufoojmdpfsbqbwfozel`vpfp#lmfofnfmwbqzbssoj`baofelvmg#wkbwpwzofpkffwnbmvp`qjswpwbmgp#elq#ml.qfsfbw+plnfwjnfp@lnnfq`jbojm#Bnfqj`bvmgfqwbhfmrvbqwfq#lebm#f{bnsofsfqplmboozjmgf{-sks!owqOjfvwfmbmw\t?gju#jg>!wkfz#tlvogbajojwz#lenbgf#vs#lemlwfg#wkbw`ofbq#wkbwbqdvf#wkbwwl#bmlwkfq`kjogqfm$psvqslpf#leelqnvobwfgabpfg#vslmwkf#qfdjlmpvaif`w#lesbppfmdfqpslppfppjlm-\t\tJm#wkf#Afelqf#wkfbewfqtbqgp`vqqfmwoz#b`qlpp#wkfp`jfmwjej``lnnvmjwz-`bsjwbojpnjm#Dfqnbmzqjdkw.tjmdwkf#pzpwfnPl`jfwz#leslojwj`jbmgjqf`wjlm9tfmw#lm#wlqfnlubo#le#Mft#Zlqh#bsbqwnfmwpjmgj`bwjlmgvqjmd#wkfvmofpp#wkfkjpwlqj`bokbg#affm#bgfejmjwjufjmdqfgjfmwbwwfmgbm`f@fmwfq#elqsqlnjmfm`fqfbgzPwbwfpwqbwfdjfpavw#jm#wkfbp#sbqw#le`lmpwjwvwf`objn#wkbwobalqbwlqz`lnsbwjaofebjovqf#le/#pv`k#bp#afdbm#tjwkvpjmd#wkf#wl#sqlujgfefbwvqf#leeqln#tkj`k,!#`obpp>!dfloldj`bopfufqbo#legfojafqbwfjnslqwbmw#klogp#wkbwjmd%rvlw8#ubojdm>wlswkf#Dfqnbmlvwpjgf#lemfdlwjbwfgkjp#`bqffqpfsbqbwjlmjg>!pfbq`ktbp#`boofgwkf#elvqwkqf`qfbwjlmlwkfq#wkbmsqfufmwjlmtkjof#wkf#fgv`bwjlm/`lmmf`wjmdb``vqbwfoztfqf#avjowtbp#hjoofgbdqffnfmwpnv`k#nlqf#Gvf#wl#wkftjgwk9#233plnf#lwkfqHjmdgln#lewkf#fmwjqfebnlvp#elqwl#`lmmf`wlaif`wjufpwkf#Eqfm`ksflsof#bmgefbwvqfg!=jp#pbjg#wlpwqv`wvqboqfefqfmgvnnlpw#lewfmb#pfsbqbwf.=\t?gju#jg#Leej`jbo#tlqogtjgf-bqjb.obafowkf#sobmfwbmg#jw#tbpg!#ubovf>!ollhjmd#bwafmfej`jbobqf#jm#wkfnlmjwlqjmdqfslqwfgozwkf#nlgfqmtlqhjmd#lmbooltfg#wltkfqf#wkf#jmmlubwjuf?,b=?,gju=plvmgwqb`hpfbq`kElqnwfmg#wl#afjmsvw#jg>!lsfmjmd#leqfpwqj`wfgbglswfg#azbggqfppjmdwkfloldjbmnfwklgp#leubqjbmw#le@kqjpwjbm#ufqz#obqdfbvwlnlwjufaz#ebq#wkfqbmdf#eqlnsvqpvjw#leeloolt#wkfaqlvdkw#wljm#Fmdobmgbdqff#wkbwb``vpfg#le`lnfp#eqlnsqfufmwjmdgju#pwzof>kjp#lq#kfqwqfnfmglvpeqffgln#le`lm`fqmjmd3#2fn#2fn8Abphfwaboo,pwzof-`ppbm#fbqojfqfufm#bewfq,!#wjwof>!-`ln,jmgf{wbhjmd#wkfsjwwpavqdk`lmwfmw!=\x0e?p`qjsw=+ewvqmfg#lvwkbujmd#wkf?,psbm=\x0e\t#l``bpjlmboaf`bvpf#jwpwbqwfg#wlskzpj`booz=?,gju=\t##`qfbwfg#az@vqqfmwoz/#ad`lolq>!wbajmgf{>!gjpbpwqlvpBmbozwj`p#bopl#kbp#b=?gju#jg>!?,pwzof=\t?`boofg#elqpjmdfq#bmg-pq`#>#!,,ujlobwjlmpwkjp#sljmw`lmpwbmwozjp#ol`bwfgqf`lqgjmdpg#eqln#wkfmfgfqobmgpslqwvdv/Fp;N;};D;u;F5m4K4]4_7`gfpbqqlool`lnfmwbqjlfgv`b`j/_mpfswjfnaqfqfdjpwqbglgjqf``j/_mvaj`b`j/_msvaoj`jgbgqfpsvfpwbpqfpvowbglpjnslqwbmwfqfpfqubglpbqw/A`volpgjefqfmwfppjdvjfmwfpqfs/Vaoj`bpjwvb`j/_mnjmjpwfqjlsqjub`jgbggjqf`wlqjlelqnb`j/_mslaob`j/_msqfpjgfmwf`lmw', 'fmjglpb``fplqjlpwf`kmlqbwjsfqplmbofp`bwfdlq/Abfpsf`jbofpgjpslmjaofb`wvbojgbgqfefqfm`jbuboobglojgajaojlwf`bqfob`jlmfp`bofmgbqjlslo/Awj`bpbmwfqjlqfpgl`vnfmwlpmbwvqbofybnbwfqjbofpgjefqfm`jbf`lm/_nj`bwqbmpslqwfqlgq/Advfysbqwj`jsbqfm`vfmwqbmgjp`vpj/_mfpwqv`wvqbevmgb`j/_meqf`vfmwfpsfqnbmfmwfwlwbonfmwf!2s{#plojg# -dje!#bow>!wqbmpsbqfmwjmelqnbwjlmbssoj`bwjlm!#lm`oj`h>!fpwbaojpkfgbgufqwjpjmd-smd!#bow>!fmujqlmnfmwsfqelqnbm`fbssqlsqjbwf%bns8ngbpk8jnnfgjbwfoz?,pwqlmd=?,qbwkfq#wkbmwfnsfqbwvqfgfufolsnfmw`lnsfwjwjlmsob`fklogfqujpjajojwz9`lszqjdkw!=3!#kfjdkw>!fufm#wklvdkqfsob`fnfmwgfpwjmbwjlm@lqslqbwjlm?vo#`obpp>!Bppl`jbwjlmjmgjujgvbopsfqpsf`wjufpfwWjnflvw+vqo+kwws9,,nbwkfnbwj`pnbqdjm.wls9fufmwvbooz#gfp`qjswjlm*#ml.qfsfbw`loof`wjlmp-ISD\x7fwkvna\x7fsbqwj`jsbwf,kfbg=?algzeolbw9ofew8?oj#`obpp>!kvmgqfgp#le\t\tKltfufq/#`lnslpjwjlm`ofbq9alwk8`llsfqbwjlmtjwkjm#wkf#obafo#elq>!alqgfq.wls9Mft#Yfbobmgqf`lnnfmgfgsklwldqbskzjmwfqfpwjmd%ow8pvs%dw8`lmwqlufqpzMfwkfqobmgpbowfqmbwjufnb{ofmdwk>!ptjwyfqobmgGfufolsnfmwfppfmwjbooz\t\tBowklvdk#?,wf{wbqfb=wkvmgfqajqgqfsqfpfmwfg%bns8mgbpk8psf`vobwjlm`lnnvmjwjfpofdjpobwjlmfof`wqlmj`p\t\n?gju#jg>!joovpwqbwfgfmdjmffqjmdwfqqjwlqjfpbvwklqjwjfpgjpwqjavwfg5!#kfjdkw>!pbmp.pfqje8`bsbaof#le#gjpbssfbqfgjmwfqb`wjufollhjmd#elqjw#tlvog#afBedkbmjpwbmtbp#`qfbwfgNbwk-eollq+pvqqlvmgjmd`bm#bopl#aflapfqubwjlmnbjmwfmbm`ffm`lvmwfqfg?k1#`obpp>!nlqf#qf`fmwjw#kbp#affmjmubpjlm#le*-dfwWjnf+*evmgbnfmwboGfpsjwf#wkf!=?gju#jg>!jmpsjqbwjlmf{bnjmbwjlmsqfsbqbwjlmf{sobmbwjlm?jmsvw#jg>!?,b=?,psbm=ufqpjlmp#lejmpwqvnfmwpafelqf#wkf##>#$kwws9,,Gfp`qjswjlmqfobwjufoz#-pvapwqjmd+fb`k#le#wkff{sfqjnfmwpjmeovfmwjbojmwfdqbwjlmnbmz#sflsofgvf#wl#wkf#`lnajmbwjlmgl#mlw#kbufNjggof#Fbpw?mlp`qjsw=?`lszqjdkw!#sfqkbsp#wkfjmpwjwvwjlmjm#Gf`fnafqbqqbmdfnfmwnlpw#ebnlvpsfqplmbojwz`qfbwjlm#leojnjwbwjlmpf{`ovpjufozplufqfjdmwz.`lmwfmw!=\t?wg#`obpp>!vmgfqdqlvmgsbqboofo#wlgl`wqjmf#lel``vsjfg#azwfqnjmloldzQfmbjppbm`fb#mvnafq#lepvsslqw#elqf{solqbwjlmqf`ldmjwjlmsqfgf`fpplq?jnd#pq`>!,?k2#`obpp>!svaoj`bwjlmnbz#bopl#afpsf`jbojyfg?,ejfogpfw=sqldqfppjufnjoojlmp#lepwbwfp#wkbwfmelq`fnfmwbqlvmg#wkf#lmf#bmlwkfq-sbqfmwMlgfbdqj`vowvqfBowfqmbwjufqfpfbq`kfqpwltbqgp#wkfNlpw#le#wkfnbmz#lwkfq#+fpsf`jbooz?wg#tjgwk>!8tjgwk9233&jmgfsfmgfmw?k0#`obpp>!#lm`kbmdf>!*-bgg@obpp+jmwfqb`wjlmLmf#le#wkf#gbvdkwfq#leb``fpplqjfpaqbm`kfp#le\x0e\t?gju#jg>!wkf#obqdfpwgf`obqbwjlmqfdvobwjlmpJmelqnbwjlmwqbmpobwjlmgl`vnfmwbqzjm#lqgfq#wl!=\t?kfbg=\t?!#kfjdkw>!2b`qlpp#wkf#lqjfmwbwjlm*8?,p`qjsw=jnsofnfmwfg`bm#af#pffmwkfqf#tbp#bgfnlmpwqbwf`lmwbjmfq!=`lmmf`wjlmpwkf#Aqjwjpktbp#tqjwwfm"jnslqwbmw8s{8#nbqdjm.elooltfg#azbajojwz#wl#`lnsoj`bwfggvqjmd#wkf#jnnjdqbwjlmbopl#`boofg?k7#`obpp>!gjpwjm`wjlmqfsob`fg#azdlufqmnfmwpol`bwjlm#lejm#Mlufnafqtkfwkfq#wkf?,s=\t?,gju=b`rvjpjwjlm`boofg#wkf#sfqpf`vwjlmgfpjdmbwjlmxelmw.pjyf9bssfbqfg#jmjmufpwjdbwff{sfqjfm`fgnlpw#ojhfoztjgfoz#vpfggjp`vppjlmpsqfpfm`f#le#+gl`vnfmw-f{wfmpjufozJw#kbp#affmjw#glfp#mlw`lmwqbqz#wljmkbajwbmwpjnsqlufnfmwp`klobqpkjs`lmpvnswjlmjmpwqv`wjlmelq#f{bnsoflmf#lq#nlqfs{8#sbggjmdwkf#`vqqfmwb#pfqjfp#lebqf#vpvboozqlof#jm#wkfsqfujlvpoz#gfqjubwjufpfujgfm`f#lef{sfqjfm`fp`lolqp`kfnfpwbwfg#wkbw`fqwjej`bwf?,b=?,gju=\t#pfof`wfg>!kjdk#p`klloqfpslmpf#wl`lnelqwbaofbglswjlm#lewkqff#zfbqpwkf#`lvmwqzjm#Efaqvbqzpl#wkbw#wkfsflsof#tkl#sqlujgfg#az?sbqbn#mbnfbeef`wfg#azjm#wfqnp#lebssljmwnfmwJPL.;;6:.2!tbp#alqm#jmkjpwlqj`bo#qfdbqgfg#bpnfbpvqfnfmwjp#abpfg#lm#bmg#lwkfq#9#evm`wjlm+pjdmjej`bmw`fofaqbwjlmwqbmpnjwwfg,ip,irvfqz-jp#hmltm#bpwkflqfwj`bo#wbajmgf{>!jw#`lvog#af?mlp`qjsw=\tkbujmd#affm\x0e\t?kfbg=\x0e\t?#%rvlw8Wkf#`lnsjobwjlmkf#kbg#affmsqlgv`fg#azskjolplskfq`lmpwqv`wfgjmwfmgfg#wlbnlmd#lwkfq`lnsbqfg#wlwl#pbz#wkbwFmdjmffqjmdb#gjeefqfmwqfefqqfg#wlgjeefqfm`fpafojfe#wkbwsklwldqbskpjgfmwjezjmdKjpwlqz#le#Qfsvaoj`#lemf`fppbqjozsqlabajojwzwf`kmj`boozofbujmd#wkfpsf`wb`vobqeqb`wjlm#lefof`wqj`jwzkfbg#le#wkfqfpwbvqbmwpsbqwmfqpkjsfnskbpjp#lmnlpw#qf`fmwpkbqf#tjwk#pbzjmd#wkbwejoofg#tjwkgfpjdmfg#wljw#jp#lewfm!=?,jeqbnf=bp#elooltp9nfqdfg#tjwkwkqlvdk#wkf`lnnfq`jbo#sljmwfg#lvwlsslqwvmjwzujft#le#wkfqfrvjqfnfmwgjujpjlm#lesqldqbnnjmdkf#qf`fjufgpfwJmwfqubo!=?,psbm=?,jm#Mft#Zlqhbggjwjlmbo#`lnsqfppjlm\t\t?gju#jg>!jm`lqslqbwf8?,p`qjsw=?bwwb`kFufmwaf`bnf#wkf#!#wbqdfw>!\\`bqqjfg#lvwPlnf#le#wkfp`jfm`f#bmgwkf#wjnf#le@lmwbjmfq!=nbjmwbjmjmd@kqjpwlskfqNv`k#le#wkftqjwjmdp#le!#kfjdkw>!1pjyf#le#wkfufqpjlm#le#nj{wvqf#le#afwtffm#wkfF{bnsofp#lefgv`bwjlmbo`lnsfwjwjuf#lmpvanjw>!gjqf`wlq#legjpwjm`wjuf,GWG#[KWNO#qfobwjmd#wlwfmgfm`z#wlsqlujm`f#letkj`k#tlvoggfpsjwf#wkfp`jfmwjej`#ofdjpobwvqf-jmmfqKWNO#boofdbwjlmpBdqj`vowvqftbp#vpfg#jmbssqlb`k#wljmwfoojdfmwzfbqp#obwfq/pbmp.pfqjegfwfqnjmjmdSfqelqnbm`fbssfbqbm`fp/#tkj`k#jp#elvmgbwjlmpbaaqfujbwfgkjdkfq#wkbmp#eqln#wkf#jmgjujgvbo#`lnslpfg#lepvsslpfg#wl`objnp#wkbwbwwqjavwjlmelmw.pjyf92fofnfmwp#leKjpwlqj`bo#kjp#aqlwkfqbw#wkf#wjnfbmmjufqpbqzdlufqmfg#azqfobwfg#wl#vowjnbwfoz#jmmlubwjlmpjw#jp#pwjoo`bm#lmoz#afgfejmjwjlmpwlDNWPwqjmdB#mvnafq#lejnd#`obpp>!Fufmwvbooz/tbp#`kbmdfgl``vqqfg#jmmfjdkalqjmdgjpwjmdvjpktkfm#kf#tbpjmwqlgv`jmdwfqqfpwqjboNbmz#le#wkfbqdvfp#wkbwbm#Bnfqj`bm`lmrvfpw#letjgfpsqfbg#tfqf#hjoofgp`qffm#bmg#Jm#lqgfq#wlf{sf`wfg#wlgfp`fmgbmwpbqf#ol`bwfgofdjpobwjufdfmfqbwjlmp#ab`hdqlvmgnlpw#sflsofzfbqp#bewfqwkfqf#jp#mlwkf#kjdkfpweqfrvfmwoz#wkfz#gl#mlwbqdvfg#wkbwpkltfg#wkbwsqfglnjmbmwwkfloldj`boaz#wkf#wjnf`lmpjgfqjmdpklqw.ojufg?,psbm=?,b=`bm#af#vpfgufqz#ojwwoflmf#le#wkf#kbg#boqfbgzjmwfqsqfwfg`lnnvmj`bwfefbwvqfp#ledlufqmnfmw/?,mlp`qjsw=fmwfqfg#wkf!#kfjdkw>!0Jmgfsfmgfmwslsvobwjlmpobqdf.p`bof-#Bowklvdk#vpfg#jm#wkfgfpwqv`wjlmslppjajojwzpwbqwjmd#jmwtl#lq#nlqff{sqfppjlmppvalqgjmbwfobqdfq#wkbmkjpwlqz#bmg?,lswjlm=\x0e\t@lmwjmfmwbofojnjmbwjmdtjoo#mlw#afsqb`wj`f#lejm#eqlmw#lepjwf#le#wkffmpvqf#wkbwwl#`qfbwf#bnjppjppjssjslwfmwjboozlvwpwbmgjmdafwwfq#wkbmtkbw#jp#mltpjwvbwfg#jmnfwb#mbnf>!WqbgjwjlmbopvddfpwjlmpWqbmpobwjlmwkf#elqn#lebwnlpskfqj`jgfloldj`bofmwfqsqjpfp`bo`vobwjmdfbpw#le#wkfqfnmbmwp#lesovdjmpsbdf,jmgf{-sks!Wkjp#jp#wkf#?b#kqfe>!,slsvobqjyfgjmuloufg#jmbqf#vpfg#wlbmg#pfufqbonbgf#az#wkfpffnp#wl#afojhfoz#wkbwSbofpwjmjbmmbnfg#bewfqjw#kbg#affmnlpw#`lnnlmwl#qfefq#wlavw#wkjp#jp`lmpf`vwjufwfnslqbqjozJm#dfmfqbo/`lmufmwjlmpwbhfp#sob`fpvagjujpjlmwfqqjwlqjbolsfqbwjlmbosfqnbmfmwoztbp#obqdfozlvwaqfbh#lejm#wkf#sbpwelooltjmd#b#{nomp9ld>!=?b#`obpp>!`obpp>!wf{w@lmufqpjlm#nbz#af#vpfgnbmveb`wvqfbewfq#afjmd`ofbqej{!=\trvfpwjlm#letbp#fof`wfgwl#af`lnf#baf`bvpf#le#plnf#sflsofjmpsjqfg#azpv``fppevo#b#wjnf#tkfmnlqf#`lnnlmbnlmdpw#wkfbm#leej`jbotjgwk9233&8wf`kmloldz/tbp#bglswfgwl#hffs#wkfpfwwofnfmwpojuf#ajqwkpjmgf{-kwno!@lmmf`wj`vwbppjdmfg#wl%bns8wjnfp8b``lvmw#elqbojdm>qjdkwwkf#`lnsbmzbotbzp#affmqfwvqmfg#wljmuloufnfmwAf`bvpf#wkfwkjp#sfqjlg!#mbnf>!r!#`lmejmfg#wlb#qfpvow#leubovf>!!#,=jp#b`wvboozFmujqlmnfmw\x0e\t?,kfbg=\x0e\t@lmufqpfoz/=\t?gju#jg>!3!#tjgwk>!2jp#sqlabaozkbuf#af`lnf`lmwqloojmdwkf#sqlaofn`jwjyfmp#leslojwj`jbmpqfb`kfg#wkfbp#fbqoz#bp9mlmf8#lufq?wbaof#`fooubojgjwz#legjqf`woz#wllmnlvpfgltmtkfqf#jw#jptkfm#jw#tbpnfnafqp#le#qfobwjlm#wlb``lnnlgbwfbolmd#tjwk#Jm#wkf#obwfwkf#Fmdojpkgfoj`jlvp!=wkjp#jp#mlwwkf#sqfpfmwje#wkfz#bqfbmg#ejmboozb#nbwwfq#le\x0e\t\n?,gju=\x0e\t\x0e\t?,p`qjsw=ebpwfq#wkbmnbilqjwz#lebewfq#tkj`k`lnsbqbwjufwl#nbjmwbjmjnsqluf#wkfbtbqgfg#wkffq!#`obpp>!eqbnfalqgfqqfpwlqbwjlmjm#wkf#pbnfbmbozpjp#lewkfjq#ejqpwGvqjmd#wkf#`lmwjmfmwbopfrvfm`f#leevm`wjlm+*xelmw.pjyf9#tlqh#lm#wkf?,p`qjsw=\t?afdjmp#tjwkibubp`qjsw9`lmpwjwvfmwtbp#elvmgfgfrvjojaqjvnbppvnf#wkbwjp#djufm#azmffgp#wl#af`llqgjmbwfpwkf#ubqjlvpbqf#sbqw#lelmoz#jm#wkfpf`wjlmp#lejp#b#`lnnlmwkflqjfp#legjp`lufqjfpbppl`jbwjlmfgdf#le#wkfpwqfmdwk#leslpjwjlm#jmsqfpfmw.gbzvmjufqpboozwl#elqn#wkfavw#jmpwfbg`lqslqbwjlmbwwb`kfg#wljp#`lnnlmozqfbplmp#elq#%rvlw8wkf#`bm#af#nbgftbp#baof#wltkj`k#nfbmpavw#gjg#mlwlmNlvpfLufqbp#slppjaoflsfqbwfg#az`lnjmd#eqlnwkf#sqjnbqzbggjwjlm#leelq#pfufqbowqbmpefqqfgb#sfqjlg#lebqf#baof#wlkltfufq/#jwpklvog#kbufnv`k#obqdfq\t\n?,p`qjsw=bglswfg#wkfsqlsfqwz#legjqf`wfg#azfeef`wjufoztbp#aqlvdkw`kjogqfm#leSqldqbnnjmdolmdfq#wkbmnbmvp`qjswptbq#bdbjmpwaz#nfbmp#lebmg#nlpw#lepjnjobq#wl#sqlsqjfwbqzlqjdjmbwjmdsqfpwjdjlvpdqbnnbwj`bof{sfqjfm`f-wl#nbhf#wkfJw#tbp#bopljp#elvmg#jm`lnsfwjwlqpjm#wkf#V-P-qfsob`f#wkfaqlvdkw#wkf`bo`vobwjlmeboo#le#wkfwkf#dfmfqbosqb`wj`boozjm#klmlq#leqfofbpfg#jmqfpjgfmwjbobmg#plnf#lehjmd#le#wkfqfb`wjlm#wl2pw#Fbqo#le`vowvqf#bmgsqjm`jsbooz?,wjwof=\t##wkfz#`bm#afab`h#wl#wkfplnf#le#kjpf{slpvqf#wlbqf#pjnjobqelqn#le#wkfbggEbulqjwf`jwjyfmpkjssbqw#jm#wkfsflsof#tjwkjm#sqb`wj`fwl#`lmwjmvf%bns8njmvp8bssqlufg#az#wkf#ejqpw#booltfg#wkfbmg#elq#wkfevm`wjlmjmdsobzjmd#wkfplovwjlm#wlkfjdkw>!3!#jm#kjp#allhnlqf#wkbm#belooltp#wkf`qfbwfg#wkfsqfpfm`f#jm%maps8?,wg=mbwjlmbojpwwkf#jgfb#leb#`kbqb`wfqtfqf#elq`fg#`obpp>!awmgbzp#le#wkfefbwvqfg#jmpkltjmd#wkfjmwfqfpw#jmjm#sob`f#lewvqm#le#wkfwkf#kfbg#leOlqg#le#wkfslojwj`boozkbp#jwp#ltmFgv`bwjlmbobssqlubo#leplnf#le#wkffb`k#lwkfq/afkbujlq#lebmg#af`bvpfbmg#bmlwkfqbssfbqfg#lmqf`lqgfg#jmaob`h%rvlw8nbz#jm`ovgfwkf#tlqog$p`bm#ofbg#wlqfefqp#wl#balqgfq>!3!#dlufqmnfmw#tjmmjmd#wkfqfpvowfg#jm#tkjof#wkf#Tbpkjmdwlm/wkf#pvaif`w`jwz#jm#wkf=?,gju=\x0e\t\n\nqfeof`w#wkfwl#`lnsofwfaf`bnf#nlqfqbgjlb`wjufqfif`wfg#aztjwklvw#bmzkjp#ebwkfq/tkj`k#`lvog`lsz#le#wkfwl#jmgj`bwfb#slojwj`bob``lvmwp#le`lmpwjwvwfptlqhfg#tjwkfq?,b=?,oj=le#kjp#ojefb``lnsbmjfg`ojfmwTjgwksqfufmw#wkfOfdjpobwjufgjeefqfmwozwldfwkfq#jmkbp#pfufqboelq#bmlwkfqwf{w#le#wkfelvmgfg#wkff#tjwk#wkf#jp#vpfg#elq`kbmdfg#wkfvpvbooz#wkfsob`f#tkfqftkfqfbp#wkf=#?b#kqfe>!!=?b#kqfe>!wkfnpfoufp/bowklvdk#kfwkbw#`bm#afwqbgjwjlmboqlof#le#wkfbp#b#qfpvowqfnluf@kjoggfpjdmfg#aztfpw#le#wkfPlnf#sflsofsqlgv`wjlm/pjgf#le#wkfmftpofwwfqpvpfg#az#wkfgltm#wl#wkfb``fswfg#azojuf#jm#wkfbwwfnswp#wllvwpjgf#wkfeqfrvfm`jfpKltfufq/#jmsqldqbnnfqpbw#ofbpw#jmbssql{jnbwfbowklvdk#jwtbp#sbqw#lebmg#ubqjlvpDlufqmlq#lewkf#bqwj`ofwvqmfg#jmwl=?b#kqfe>!,wkf#f`lmlnzjp#wkf#nlpwnlpw#tjgfoztlvog#obwfqbmg#sfqkbspqjpf#wl#wkfl``vqp#tkfmvmgfq#tkj`k`lmgjwjlmp-wkf#tfpwfqmwkflqz#wkbwjp#sqlgv`fgwkf#`jwz#lejm#tkj`k#kfpffm#jm#wkfwkf#`fmwqboavjogjmd#lenbmz#le#kjpbqfb#le#wkfjp#wkf#lmoznlpw#le#wkfnbmz#le#wkfwkf#TfpwfqmWkfqf#jp#mlf{wfmgfg#wlPwbwjpwj`bo`lopsbm>1#\x7fpklqw#pwlqzslppjaof#wlwlsloldj`bo`qjwj`bo#leqfslqwfg#wlb#@kqjpwjbmgf`jpjlm#wljp#frvbo#wlsqlaofnp#leWkjp#`bm#afnfq`kbmgjpfelq#nlpw#leml#fujgfm`ffgjwjlmp#lefofnfmwp#jm%rvlw8-#Wkf`ln,jnbdfp,tkj`k#nbhfpwkf#sql`fppqfnbjmp#wkfojwfqbwvqf/jp#b#nfnafqwkf#slsvobqwkf#bm`jfmwsqlaofnp#jmwjnf#le#wkfgfefbwfg#azalgz#le#wkfb#eft#zfbqpnv`k#le#wkfwkf#tlqh#le@bojelqmjb/pfqufg#bp#bdlufqmnfmw-`lm`fswp#lenlufnfmw#jm\n\n?gju#jg>!jw!#ubovf>!obmdvbdf#lebp#wkfz#bqfsqlgv`fg#jmjp#wkbw#wkff{sobjm#wkfgju=?,gju=\tKltfufq#wkfofbg#wl#wkf\n?b#kqfe>!,tbp#dqbmwfgsflsof#kbuf`lmwjmvbooztbp#pffm#bpbmg#qfobwfgwkf#qlof#lesqlslpfg#azle#wkf#afpwfb`k#lwkfq-@lmpwbmwjmfsflsof#eqlngjbof`wp#lewl#qfujpjlmtbp#qfmbnfgb#plvq`f#lewkf#jmjwjboobvm`kfg#jmsqlujgf#wkfwl#wkf#tfpwtkfqf#wkfqfbmg#pjnjobqafwtffm#wtljp#bopl#wkfFmdojpk#bmg`lmgjwjlmp/wkbw#jw#tbpfmwjwofg#wlwkfnpfoufp-rvbmwjwz#leqbmpsbqfm`zwkf#pbnf#bpwl#iljm#wkf`lvmwqz#bmgwkjp#jp#wkfWkjp#ofg#wlb#pwbwfnfmw`lmwqbpw#wlobpwJmgf{Lewkqlvdk#kjpjp#gfpjdmfgwkf#wfqn#jpjp#sqlujgfgsqlwf`w#wkfmd?,b=?,oj=Wkf#`vqqfmwwkf#pjwf#lepvapwbmwjbof{sfqjfm`f/jm#wkf#Tfpwwkfz#pklvogpolufm(ajmb`lnfmwbqjlpvmjufqpjgbg`lmgj`jlmfpb`wjujgbgfpf{sfqjfm`jbwf`mlold/Absqlgv``j/_msvmwvb`j/_mbsoj`b`j/_m`lmwqbpf/]b`bwfdlq/Abpqfdjpwqbqpfsqlefpjlmbowqbwbnjfmwlqfd/Apwqbwfpf`qfwbq/Absqjm`jsbofpsqlwf``j/_mjnslqwbmwfpjnslqwbm`jbslpjajojgbgjmwfqfpbmwf`qf`jnjfmwlmf`fpjgbgfppvp`qjajqpfbpl`jb`j/_mgjpslmjaofpfubovb`j/_mfpwvgjbmwfpqfpslmpbaofqfplov`j/_mdvbgbobibqbqfdjpwqbglplslqwvmjgbg`lnfq`jbofpelwldqbe/Abbvwlqjgbgfpjmdfmjfq/Abwfofujpj/_m`lnsfwfm`jblsfqb`jlmfpfpwbaof`jglpjnsofnfmwfb`wvbonfmwfmbufdb`j/_m`lmelqnjgbgojmf.kfjdkw9elmw.ebnjoz9!#9#!kwws9,,bssoj`bwjlmpojmh!#kqfe>!psf`jej`booz,,?"X@GBWBX\tLqdbmjybwjlmgjpwqjavwjlm3s{8#kfjdkw9qfobwjlmpkjsgfuj`f.tjgwk?gju#`obpp>!?obafo#elq>!qfdjpwqbwjlm?,mlp`qjsw=\t,jmgf{-kwno!tjmglt-lsfm+#"jnslqwbmw8bssoj`bwjlm,jmgfsfmgfm`f,,ttt-dlldoflqdbmjybwjlmbvwl`lnsofwfqfrvjqfnfmwp`lmpfqubwjuf?elqn#mbnf>!jmwfoof`wvbonbqdjm.ofew92;wk#`fmwvqzbm#jnslqwbmwjmpwjwvwjlmpbaaqfujbwjlm?jnd#`obpp>!lqdbmjpbwjlm`jujojybwjlm2:wk#`fmwvqzbq`kjwf`wvqfjm`lqslqbwfg13wk#`fmwvqz.`lmwbjmfq!=nlpw#mlwbaoz,=?,b=?,gju=mlwjej`bwjlm$vmgfejmfg$*Evqwkfqnlqf/afojfuf#wkbwjmmfqKWNO#>#sqjlq#wl#wkfgqbnbwj`boozqfefqqjmd#wlmfdlwjbwjlmpkfbgrvbqwfqpPlvwk#Beqj`bvmpv``fppevoSfmmpzoubmjbBp#b#qfpvow/?kwno#obmd>!%ow8,pvs%dw8gfbojmd#tjwkskjobgfoskjbkjpwlqj`booz*8?,p`qjsw=\tsbggjmd.wls9f{sfqjnfmwbodfwBwwqjavwfjmpwqv`wjlmpwf`kmloldjfpsbqw#le#wkf#>evm`wjlm+*xpvap`qjswjlmo-gwg!=\x0e\t?kwdfldqbskj`bo@lmpwjwvwjlm$/#evm`wjlm+pvsslqwfg#azbdqj`vowvqbo`lmpwqv`wjlmsvaoj`bwjlmpelmw.pjyf9#2b#ubqjfwz#le?gju#pwzof>!Fm`z`olsfgjbjeqbnf#pq`>!gfnlmpwqbwfgb``lnsojpkfgvmjufqpjwjfpGfnldqbskj`p*8?,p`qjsw=?gfgj`bwfg#wlhmltofgdf#lepbwjpeb`wjlmsbqwj`vobqoz?,gju=?,gju=Fmdojpk#+VP*bssfmg@kjog+wqbmpnjppjlmp-#Kltfufq/#jmwfoojdfm`f!#wbajmgf{>!eolbw9qjdkw8@lnnlmtfbowkqbmdjmd#eqlnjm#tkj`k#wkfbw#ofbpw#lmfqfsqlgv`wjlmfm`z`olsfgjb8elmw.pjyf92ivqjpgj`wjlmbw#wkbw#wjnf!=?b#`obpp>!Jm#bggjwjlm/gfp`qjswjlm(`lmufqpbwjlm`lmwb`w#tjwkjp#dfmfqboozq!#`lmwfmw>!qfsqfpfmwjmd%ow8nbwk%dw8sqfpfmwbwjlml``bpjlmbooz?jnd#tjgwk>!mbujdbwjlm!=`lnsfmpbwjlm`kbnsjlmpkjsnfgjb>!boo!#ujlobwjlm#leqfefqfm`f#wlqfwvqm#wqvf8Pwqj`w,,FM!#wqbmpb`wjlmpjmwfqufmwjlmufqjej`bwjlmJmelqnbwjlm#gjeej`vowjfp@kbnsjlmpkjs`bsbajojwjfp?"Xfmgje^..=~\t?,p`qjsw=\t@kqjpwjbmjwzelq#f{bnsof/Sqlefppjlmboqfpwqj`wjlmppvddfpw#wkbwtbp#qfofbpfg+pv`k#bp#wkfqfnluf@obpp+vmfnsolznfmwwkf#Bnfqj`bmpwqv`wvqf#le,jmgf{-kwno#svaojpkfg#jmpsbm#`obpp>!!=?b#kqfe>!,jmwqlgv`wjlmafolmdjmd#wl`objnfg#wkbw`lmpfrvfm`fp?nfwb#mbnf>!Dvjgf#wl#wkflufqtkfonjmdbdbjmpw#wkf#`lm`fmwqbwfg/\t-mlmwlv`k#lapfqubwjlmp?,b=\t?,gju=\te#+gl`vnfmw-alqgfq9#2s{#xelmw.pjyf92wqfbwnfmw#le3!#kfjdkw>!2nlgjej`bwjlmJmgfsfmgfm`fgjujgfg#jmwldqfbwfq#wkbmb`kjfufnfmwpfpwbaojpkjmdIbubP`qjsw!#mfufqwkfofpppjdmjej`bm`fAqlbg`bpwjmd=%maps8?,wg=`lmwbjmfq!=\tpv`k#bp#wkf#jmeovfm`f#leb#sbqwj`vobqpq`>$kwws9,,mbujdbwjlm!#kboe#le#wkf#pvapwbmwjbo#%maps8?,gju=bgubmwbdf#legjp`lufqz#leevmgbnfmwbo#nfwqlslojwbmwkf#lsslpjwf!#{no9obmd>!gfojafqbwfozbojdm>`fmwfqfulovwjlm#lesqfpfqubwjlmjnsqlufnfmwpafdjmmjmd#jmIfpvp#@kqjpwSvaoj`bwjlmpgjpbdqffnfmwwf{w.bojdm9q/#evm`wjlm+*pjnjobqjwjfpalgz=?,kwno=jp#`vqqfmwozboskbafwj`bojp#plnfwjnfpwzsf>!jnbdf,nbmz#le#wkf#eolt9kjggfm8bubjobaof#jmgfp`qjaf#wkff{jpwfm`f#leboo#lufq#wkfwkf#Jmwfqmfw\n?vo#`obpp>!jmpwboobwjlmmfjdkalqkllgbqnfg#elq`fpqfgv`jmd#wkf`lmwjmvfp#wlMlmfwkfofpp/wfnsfqbwvqfp\t\n\n?b#kqfe>!`olpf#wl#wkff{bnsofp#le#jp#balvw#wkf+pff#afolt*-!#jg>!pfbq`ksqlefppjlmbojp#bubjobaofwkf#leej`jbo\n\n?,p`qjsw=\t\t\n\n?gju#jg>!b``fofqbwjlmwkqlvdk#wkf#Kboo#le#Ebnfgfp`qjswjlmpwqbmpobwjlmpjmwfqefqfm`f#wzsf>$wf{w,qf`fmw#zfbqpjm#wkf#tlqogufqz#slsvobqxab`hdqlvmg9wqbgjwjlmbo#plnf#le#wkf#`lmmf`wfg#wlf{soljwbwjlmfnfqdfm`f#le`lmpwjwvwjlmB#Kjpwlqz#lepjdmjej`bmw#nbmveb`wvqfgf{sf`wbwjlmp=?mlp`qjsw=?`bm#af#elvmgaf`bvpf#wkf#kbp#mlw#affmmfjdkalvqjmdtjwklvw#wkf#bggfg#wl#wkf\n?oj#`obpp>!jmpwqvnfmwboPlujfw#Vmjlmb`hmltofgdfgtkj`k#`bm#afmbnf#elq#wkfbwwfmwjlm#wlbwwfnswp#wl#gfufolsnfmwpJm#eb`w/#wkf?oj#`obpp>!bjnsoj`bwjlmppvjwbaof#elqnv`k#le#wkf#`lolmjybwjlmsqfpjgfmwjbo`bm`foAvaaof#Jmelqnbwjlmnlpw#le#wkf#jp#gfp`qjafgqfpw#le#wkf#nlqf#lq#ofppjm#PfswfnafqJmwfoojdfm`fpq`>!kwws9,,s{8#kfjdkw9#bubjobaof#wlnbmveb`wvqfqkvnbm#qjdkwpojmh#kqfe>!,bubjobajojwzsqlslqwjlmbolvwpjgf#wkf#bpwqlmlnj`bokvnbm#afjmdpmbnf#le#wkf#bqf#elvmg#jmbqf#abpfg#lmpnboofq#wkbmb#sfqplm#tklf{sbmpjlm#lebqdvjmd#wkbwmlt#hmltm#bpJm#wkf#fbqozjmwfqnfgjbwfgfqjufg#eqlnP`bmgjmbujbm?,b=?,gju=\x0e\t`lmpjgfq#wkfbm#fpwjnbwfgwkf#Mbwjlmbo?gju#jg>!sbdqfpvowjmd#jm`lnnjppjlmfgbmboldlvp#wlbqf#qfrvjqfg,vo=\t?,gju=\ttbp#abpfg#lmbmg#af`bnf#b%maps8%maps8w!#ubovf>!!#tbp#`bswvqfgml#nlqf#wkbmqfpsf`wjufoz`lmwjmvf#wl#=\x0e\t?kfbg=\x0e\t?tfqf#`qfbwfgnlqf#dfmfqbojmelqnbwjlm#vpfg#elq#wkfjmgfsfmgfmw#wkf#Jnsfqjbo`lnslmfmw#lewl#wkf#mlqwkjm`ovgf#wkf#@lmpwqv`wjlmpjgf#le#wkf#tlvog#mlw#afelq#jmpwbm`fjmufmwjlm#lenlqf#`lnsof{`loof`wjufozab`hdqlvmg9#wf{w.bojdm9#jwp#lqjdjmbojmwl#b``lvmwwkjp#sql`fppbm#f{wfmpjufkltfufq/#wkfwkfz#bqf#mlwqfif`wfg#wkf`qjwj`jpn#legvqjmd#tkj`ksqlabaoz#wkfwkjp#bqwj`of+evm`wjlm+*xJw#pklvog#afbm#bdqffnfmwb``jgfmwboozgjeefqp#eqlnBq`kjwf`wvqfafwwfq#hmltmbqqbmdfnfmwpjmeovfm`f#lmbwwfmgfg#wkfjgfmwj`bo#wlplvwk#le#wkfsbpp#wkqlvdk{no!#wjwof>!tfjdkw9alog8`qfbwjmd#wkfgjpsobz9mlmfqfsob`fg#wkf?jnd#pq`>!,jkwwsp9,,ttt-Tlqog#Tbq#JJwfpwjnlmjbopelvmg#jm#wkfqfrvjqfg#wl#bmg#wkbw#wkfafwtffm#wkf#tbp#gfpjdmfg`lmpjpwp#le#`lmpjgfqbaozsvaojpkfg#azwkf#obmdvbdf@lmpfqubwjlm`lmpjpwfg#leqfefq#wl#wkfab`h#wl#wkf#`pp!#nfgjb>!Sflsof#eqln#bubjobaof#lmsqlufg#wl#afpvddfpwjlmp!tbp#hmltm#bpubqjfwjfp#leojhfoz#wl#af`lnsqjpfg#lepvsslqw#wkf#kbmgp#le#wkf`lvsofg#tjwk`lmmf`w#bmg#alqgfq9mlmf8sfqelqnbm`fpafelqf#afjmdobwfq#af`bnf`bo`vobwjlmplewfm#`boofgqfpjgfmwp#lenfbmjmd#wkbw=?oj#`obpp>!fujgfm`f#elqf{sobmbwjlmpfmujqlmnfmwp!=?,b=?,gju=tkj`k#booltpJmwqlgv`wjlmgfufolsfg#azb#tjgf#qbmdflm#afkboe#leubojdm>!wls!sqjm`jsof#lebw#wkf#wjnf/?,mlp`qjsw=\x0epbjg#wl#kbufjm#wkf#ejqpwtkjof#lwkfqpkzslwkfwj`boskjolplskfqpsltfq#le#wkf`lmwbjmfg#jmsfqelqnfg#azjmbajojwz#wltfqf#tqjwwfmpsbm#pwzof>!jmsvw#mbnf>!wkf#rvfpwjlmjmwfmgfg#elqqfif`wjlm#lejnsojfp#wkbwjmufmwfg#wkfwkf#pwbmgbqgtbp#sqlabaozojmh#afwtffmsqlefpplq#lejmwfqb`wjlmp`kbmdjmd#wkfJmgjbm#L`fbm#`obpp>!obpwtlqhjmd#tjwk$kwws9,,ttt-zfbqp#afelqfWkjp#tbp#wkfqf`qfbwjlmbofmwfqjmd#wkfnfbpvqfnfmwpbm#f{wqfnfozubovf#le#wkfpwbqw#le#wkf\t?,p`qjsw=\t\tbm#feelqw#wljm`qfbpf#wkfwl#wkf#plvwkpsb`jmd>!3!=pveej`jfmwozwkf#Fvqlsfbm`lmufqwfg#wl`ofbqWjnflvwgjg#mlw#kbuf`lmpfrvfmwozelq#wkf#mf{wf{wfmpjlm#lef`lmlnj`#bmgbowklvdk#wkfbqf#sqlgv`fgbmg#tjwk#wkfjmpveej`jfmwdjufm#az#wkfpwbwjmd#wkbwf{sfmgjwvqfp?,psbm=?,b=\twklvdkw#wkbwlm#wkf#abpjp`foosbggjmd>jnbdf#le#wkfqfwvqmjmd#wljmelqnbwjlm/pfsbqbwfg#azbppbppjmbwfgp!#`lmwfmw>!bvwklqjwz#lemlqwktfpwfqm?,gju=\t?gju#!=?,gju=\x0e\t##`lmpvowbwjlm`lnnvmjwz#lewkf#mbwjlmbojw#pklvog#afsbqwj`jsbmwp#bojdm>!ofewwkf#dqfbwfpwpfof`wjlm#lepvsfqmbwvqbogfsfmgfmw#lmjp#nfmwjlmfgbooltjmd#wkftbp#jmufmwfgb``lnsbmzjmdkjp#sfqplmbobubjobaof#bwpwvgz#le#wkflm#wkf#lwkfqf{f`vwjlm#leKvnbm#Qjdkwpwfqnp#le#wkfbppl`jbwjlmpqfpfbq`k#bmgpv``ffgfg#azgfefbwfg#wkfbmg#eqln#wkfavw#wkfz#bqf`lnnbmgfq#lepwbwf#le#wkfzfbqp#le#bdfwkf#pwvgz#le?vo#`obpp>!psob`f#jm#wkftkfqf#kf#tbp?oj#`obpp>!ewkfqf#bqf#mltkj`k#af`bnfkf#svaojpkfgf{sqfppfg#jmwl#tkj`k#wkf`lnnjppjlmfqelmw.tfjdkw9wfqqjwlqz#lef{wfmpjlmp!=Qlnbm#Fnsjqffrvbo#wl#wkfJm#`lmwqbpw/kltfufq/#bmgjp#wzsj`boozbmg#kjp#tjef+bopl#`boofg=?vo#`obpp>!feef`wjufoz#fuloufg#jmwlpffn#wl#kbuftkj`k#jp#wkfwkfqf#tbp#mlbm#f{`foofmwboo#le#wkfpfgfp`qjafg#azJm#sqb`wj`f/aqlbg`bpwjmd`kbqdfg#tjwkqfeof`wfg#jmpvaif`wfg#wlnjojwbqz#bmgwl#wkf#sljmwf`lmlnj`boozpfwWbqdfwjmdbqf#b`wvboozuj`wlqz#lufq+*8?,p`qjsw=`lmwjmvlvpozqfrvjqfg#elqfulovwjlmbqzbm#feef`wjufmlqwk#le#wkf/#tkj`k#tbp#eqlmw#le#wkflq#lwkfqtjpfplnf#elqn#lekbg#mlw#affmdfmfqbwfg#azjmelqnbwjlm-sfqnjwwfg#wljm`ovgfp#wkfgfufolsnfmw/fmwfqfg#jmwlwkf#sqfujlvp`lmpjpwfmwozbqf#hmltm#bpwkf#ejfog#lewkjp#wzsf#ledjufm#wl#wkfwkf#wjwof#le`lmwbjmp#wkfjmpwbm`fp#lejm#wkf#mlqwkgvf#wl#wkfjqbqf#gfpjdmfg`lqslqbwjlmptbp#wkbw#wkflmf#le#wkfpfnlqf#slsvobqpv``ffgfg#jmpvsslqw#eqlnjm#gjeefqfmwglnjmbwfg#azgfpjdmfg#elqltmfqpkjs#lebmg#slppjaozpwbmgbqgjyfgqfpslmpfWf{wtbp#jmwfmgfgqf`fjufg#wkfbppvnfg#wkbwbqfbp#le#wkfsqjnbqjoz#jmwkf#abpjp#lejm#wkf#pfmpfb``lvmwp#elqgfpwqlzfg#azbw#ofbpw#wtltbp#gf`obqfg`lvog#mlw#afPf`qfwbqz#lebssfbq#wl#afnbqdjm.wls92,]_p(\x7f_p(\',df*xwkqlt#f~8wkf#pwbqw#lewtl#pfsbqbwfobmdvbdf#bmgtkl#kbg#affmlsfqbwjlm#legfbwk#le#wkfqfbo#mvnafqp\n?ojmh#qfo>!sqlujgfg#wkfwkf#pwlqz#le`lnsfwjwjlmpfmdojpk#+VH*fmdojpk#+VP*#evm`wjlm+*-isd!#tjgwk>!`lmejdvqbwjlm-smd!#tjgwk>!?algz#`obpp>!Nbwk-qbmgln+*`lmwfnslqbqz#Vmjwfg#Pwbwfp`jq`vnpwbm`fp-bssfmg@kjog+lqdbmjybwjlmp?psbm#`obpp>!!=?jnd#pq`>!,gjpwjmdvjpkfgwklvpbmgp#le#`lnnvmj`bwjlm`ofbq!=?,gju=jmufpwjdbwjlmebuj`lm-j`l!#nbqdjm.qjdkw9abpfg#lm#wkf#Nbppb`kvpfwwpwbaof#alqgfq>jmwfqmbwjlmbobopl#hmltm#bpsqlmvm`jbwjlmab`hdqlvmg9 esbggjmd.ofew9Elq#f{bnsof/#njp`foobmflvp%ow8,nbwk%dw8spz`kloldj`bojm#sbqwj`vobqfbq`k!#wzsf>!elqn#nfwklg>!bp#lsslpfg#wlPvsqfnf#@lvqwl``bpjlmbooz#Bggjwjlmbooz/Mlqwk#Bnfqj`bs{8ab`hdqlvmglsslqwvmjwjfpFmwfqwbjmnfmw-wlOltfq@bpf+nbmveb`wvqjmdsqlefppjlmbo#`lnajmfg#tjwkElq#jmpwbm`f/`lmpjpwjmd#le!#nb{ofmdwk>!qfwvqm#ebopf8`lmp`jlvpmfppNfgjwfqqbmfbmf{wqblqgjmbqzbppbppjmbwjlmpvapfrvfmwoz#avwwlm#wzsf>!wkf#mvnafq#lewkf#lqjdjmbo#`lnsqfkfmpjufqfefqp#wl#wkf?,vo=\t?,gju=\tskjolplskj`bool`bwjlm-kqfetbp#svaojpkfgPbm#Eqbm`jp`l+evm`wjlm+*x\t?gju#jg>!nbjmplskjpwj`bwfgnbwkfnbwj`bo#,kfbg=\x0e\t?algzpvddfpwp#wkbwgl`vnfmwbwjlm`lm`fmwqbwjlmqfobwjlmpkjspnbz#kbuf#affm+elq#f{bnsof/Wkjp#bqwj`of#jm#plnf#`bpfpsbqwp#le#wkf#gfejmjwjlm#leDqfbw#Aqjwbjm#`foosbggjmd>frvjubofmw#wlsob`fklogfq>!8#elmw.pjyf9#ivpwjej`bwjlmafojfufg#wkbwpveefqfg#eqlnbwwfnswfg#wl#ofbgfq#le#wkf`qjsw!#pq`>!,+evm`wjlm+*#xbqf#bubjobaof\t\n?ojmh#qfo>!#pq`>$kwws9,,jmwfqfpwfg#jm`lmufmwjlmbo#!#bow>!!#,=?,bqf#dfmfqboozkbp#bopl#affmnlpw#slsvobq#`lqqfpslmgjmd`qfgjwfg#tjwkwzof>!alqgfq9?,b=?,psbm=?,-dje!#tjgwk>!?jeqbnf#pq`>!wbaof#`obpp>!jmojmf.aol`h8b``lqgjmd#wl#wldfwkfq#tjwkbssql{jnbwfozsbqojbnfmwbqznlqf#bmg#nlqfgjpsobz9mlmf8wqbgjwjlmboozsqfglnjmbmwoz%maps8\x7f%maps8%maps8?,psbm=#`foopsb`jmd>?jmsvw#mbnf>!lq!#`lmwfmw>!`lmwqlufqpjbosqlsfqwz>!ld9,{.pkl`htbuf.gfnlmpwqbwjlmpvqqlvmgfg#azMfufqwkfofpp/tbp#wkf#ejqpw`lmpjgfqbaof#Bowklvdk#wkf#`loobalqbwjlmpklvog#mlw#afsqlslqwjlm#le?psbm#pwzof>!hmltm#bp#wkf#pklqwoz#bewfqelq#jmpwbm`f/gfp`qjafg#bp#,kfbg=\t?algz#pwbqwjmd#tjwkjm`qfbpjmdoz#wkf#eb`w#wkbwgjp`vppjlm#lenjggof#le#wkfbm#jmgjujgvbogjeej`vow#wl#sljmw#le#ujftklnlpf{vbojwzb``fswbm`f#le?,psbm=?,gju=nbmveb`wvqfqplqjdjm#le#wkf`lnnlmoz#vpfgjnslqwbm`f#legfmlnjmbwjlmpab`hdqlvmg9# ofmdwk#le#wkfgfwfqnjmbwjlmb#pjdmjej`bmw!#alqgfq>!3!=qfulovwjlmbqzsqjm`jsofp#lejp#`lmpjgfqfgtbp#gfufolsfgJmgl.Fvqlsfbmuvomfqbaof#wlsqlslmfmwp#lebqf#plnfwjnfp`olpfq#wl#wkfMft#Zlqh#@jwz#mbnf>!pfbq`kbwwqjavwfg#wl`lvqpf#le#wkfnbwkfnbwj`jbmaz#wkf#fmg#lebw#wkf#fmg#le!#alqgfq>!3!#wf`kmloldj`bo-qfnluf@obpp+aqbm`k#le#wkffujgfm`f#wkbw"Xfmgje^..=\x0e\tJmpwjwvwf#le#jmwl#b#pjmdofqfpsf`wjufoz-bmg#wkfqfelqfsqlsfqwjfp#lejp#ol`bwfg#jmplnf#le#tkj`kWkfqf#jp#bopl`lmwjmvfg#wl#bssfbqbm`f#le#%bns8mgbpk8#gfp`qjafp#wkf`lmpjgfqbwjlmbvwklq#le#wkfjmgfsfmgfmwozfrvjssfg#tjwkglfp#mlw#kbuf?,b=?b#kqfe>!`lmevpfg#tjwk?ojmh#kqfe>!,bw#wkf#bdf#lebssfbq#jm#wkfWkfpf#jm`ovgfqfdbqgofpp#le`lvog#af#vpfg#pwzof>%rvlw8pfufqbo#wjnfpqfsqfpfmw#wkfalgz=\t?,kwno=wklvdkw#wl#afslsvobwjlm#leslppjajojwjfpsfq`fmwbdf#leb``fpp#wl#wkfbm#bwwfnsw#wlsqlgv`wjlm#leirvfqz,irvfqzwtl#gjeefqfmwafolmd#wl#wkffpwbaojpknfmwqfsob`jmd#wkfgfp`qjswjlm!#gfwfqnjmf#wkfbubjobaof#elqB``lqgjmd#wl#tjgf#qbmdf#le\n?gju#`obpp>!nlqf#`lnnlmozlqdbmjpbwjlmpevm`wjlmbojwztbp#`lnsofwfg#%bns8ngbpk8#sbqwj`jsbwjlmwkf#`kbqb`wfqbm#bggjwjlmbobssfbqp#wl#afeb`w#wkbw#wkfbm#f{bnsof#lepjdmjej`bmwozlmnlvpflufq>!af`bvpf#wkfz#bpzm`#>#wqvf8sqlaofnp#tjwkpffnp#wl#kbufwkf#qfpvow#le#pq`>!kwws9,,ebnjojbq#tjwkslppfppjlm#leevm`wjlm#+*#xwllh#sob`f#jmbmg#plnfwjnfppvapwbmwjbooz?psbm=?,psbm=jp#lewfm#vpfgjm#bm#bwwfnswdqfbw#gfbo#leFmujqlmnfmwbopv``fppevooz#ujqwvbooz#boo13wk#`fmwvqz/sqlefppjlmbopmf`fppbqz#wl#gfwfqnjmfg#az`lnsbwjajojwzaf`bvpf#jw#jpGj`wjlmbqz#lenlgjej`bwjlmpWkf#elooltjmdnbz#qfefq#wl9@lmpfrvfmwoz/Jmwfqmbwjlmbobowklvdk#plnfwkbw#tlvog#aftlqog$p#ejqpw`obppjejfg#bpalwwln#le#wkf+sbqwj`vobqozbojdm>!ofew!#nlpw#`lnnlmozabpjp#elq#wkfelvmgbwjlm#le`lmwqjavwjlmpslsvobqjwz#le`fmwfq#le#wkfwl#qfgv`f#wkfivqjpgj`wjlmpbssql{jnbwjlm#lmnlvpflvw>!Mft#Wfpwbnfmw`loof`wjlm#le?,psbm=?,b=?,jm#wkf#Vmjwfgejon#gjqf`wlq.pwqj`w-gwg!=kbp#affm#vpfgqfwvqm#wl#wkfbowklvdk#wkjp`kbmdf#jm#wkfpfufqbo#lwkfqavw#wkfqf#bqfvmsqf`fgfmwfgjp#pjnjobq#wlfpsf`jbooz#jmtfjdkw9#alog8jp#`boofg#wkf`lnsvwbwjlmbojmgj`bwf#wkbwqfpwqj`wfg#wl\n?nfwb#mbnf>!bqf#wzsj`booz`lmeoj`w#tjwkKltfufq/#wkf#Bm#f{bnsof#le`lnsbqfg#tjwkrvbmwjwjfp#leqbwkfq#wkbm#b`lmpwfoobwjlmmf`fppbqz#elqqfslqwfg#wkbwpsf`jej`bwjlmslojwj`bo#bmg%maps8%maps8?qfefqfm`fp#wlwkf#pbnf#zfbqDlufqmnfmw#ledfmfqbwjlm#lekbuf#mlw#affmpfufqbo#zfbqp`lnnjwnfmw#wl\n\n?vo#`obpp>!ujpvbojybwjlm2:wk#`fmwvqz/sqb`wjwjlmfqpwkbw#kf#tlvogbmg#`lmwjmvfgl``vsbwjlm#lejp#gfejmfg#bp`fmwqf#le#wkfwkf#bnlvmw#le=?gju#pwzof>!frvjubofmw#legjeefqfmwjbwfaqlvdkw#balvwnbqdjm.ofew9#bvwlnbwj`boozwklvdkw#le#bpPlnf#le#wkfpf\t?gju#`obpp>!jmsvw#`obpp>!qfsob`fg#tjwkjp#lmf#le#wkffgv`bwjlm#bmgjmeovfm`fg#azqfsvwbwjlm#bp\t?nfwb#mbnf>!b``lnnlgbwjlm?,gju=\t?,gju=obqdf#sbqw#leJmpwjwvwf#elqwkf#pl.`boofg#bdbjmpw#wkf#Jm#wkjp#`bpf/tbp#bssljmwfg`objnfg#wl#afKltfufq/#wkjpGfsbqwnfmw#lewkf#qfnbjmjmdfeef`w#lm#wkfsbqwj`vobqoz#gfbo#tjwk#wkf\t?gju#pwzof>!bonlpw#botbzpbqf#`vqqfmwozf{sqfppjlm#leskjolplskz#leelq#nlqf#wkbm`jujojybwjlmplm#wkf#jpobmgpfof`wfgJmgf{`bm#qfpvow#jm!#ubovf>!!#,=wkf#pwqv`wvqf#,=?,b=?,gju=Nbmz#le#wkfpf`bvpfg#az#wkfle#wkf#Vmjwfgpsbm#`obpp>!n`bm#af#wqb`fgjp#qfobwfg#wlaf`bnf#lmf#lejp#eqfrvfmwozojujmd#jm#wkfwkflqfwj`boozElooltjmd#wkfQfulovwjlmbqzdlufqmnfmw#jmjp#gfwfqnjmfgwkf#slojwj`bojmwqlgv`fg#jmpveej`jfmw#wlgfp`qjswjlm!=pklqw#pwlqjfppfsbqbwjlm#lebp#wl#tkfwkfqhmltm#elq#jwptbp#jmjwjboozgjpsobz9aol`hjp#bm#f{bnsofwkf#sqjm`jsbo`lmpjpwp#le#bqf`ldmjyfg#bp,algz=?,kwno=b#pvapwbmwjboqf`lmpwqv`wfgkfbg#le#pwbwfqfpjpwbm`f#wlvmgfqdqbgvbwfWkfqf#bqf#wtldqbujwbwjlmbobqf#gfp`qjafgjmwfmwjlmboozpfqufg#bp#wkf`obpp>!kfbgfqlsslpjwjlm#wlevmgbnfmwboozglnjmbwfg#wkfbmg#wkf#lwkfqboojbm`f#tjwktbp#elq`fg#wlqfpsf`wjufoz/bmg#slojwj`bojm#pvsslqw#lesflsof#jm#wkf13wk#`fmwvqz-bmg#svaojpkfgolbg@kbqwafbwwl#vmgfqpwbmgnfnafq#pwbwfpfmujqlmnfmwboejqpw#kboe#le`lvmwqjfp#bmgbq`kjwf`wvqboaf#`lmpjgfqfg`kbqb`wfqjyfg`ofbqJmwfqubobvwklqjwbwjufEfgfqbwjlm#letbp#pv``ffgfgbmg#wkfqf#bqfb#`lmpfrvfm`fwkf#Sqfpjgfmwbopl#jm`ovgfgeqff#plewtbqfpv``fppjlm#legfufolsfg#wkftbp#gfpwqlzfgbtbz#eqln#wkf8\t?,p`qjsw=\t?bowklvdk#wkfzelooltfg#az#bnlqf#sltfqevoqfpvowfg#jm#bVmjufqpjwz#leKltfufq/#nbmzwkf#sqfpjgfmwKltfufq/#plnfjp#wklvdkw#wlvmwjo#wkf#fmgtbp#bmmlvm`fgbqf#jnslqwbmwbopl#jm`ovgfp=?jmsvw#wzsf>wkf#`fmwfq#le#GL#MLW#BOWFQvpfg#wl#qfefqwkfnfp,wkbw#kbg#affmwkf#abpjp#elqkbp#gfufolsfgjm#wkf#pvnnfq`lnsbqbwjufozgfp`qjafg#wkfpv`k#bp#wklpfwkf#qfpvowjmdjp#jnslppjaofubqjlvp#lwkfqPlvwk#Beqj`bmkbuf#wkf#pbnffeef`wjufmfppjm#tkj`k#`bpf8#wf{w.bojdm9pwqv`wvqf#bmg8#ab`hdqlvmg9qfdbqgjmd#wkfpvsslqwfg#wkfjp#bopl#hmltmpwzof>!nbqdjmjm`ovgjmd#wkfabkbpb#Nfobzvmlqph#alhn/Iomlqph#mzmlqphpolufm)M(ajmbjmwfqmb`jlmbo`bojej`b`j/_m`lnvmj`b`j/_m`lmpwqv``j/_m!=?gju#`obpp>!gjpbnajdvbwjlmGlnbjmMbnf$/#$bgnjmjpwqbwjlmpjnvowbmflvpozwqbmpslqwbwjlmJmwfqmbwjlmbo#nbqdjm.alwwln9qfpslmpjajojwz?"Xfmgje^..=\t?,=?nfwb#mbnf>!jnsofnfmwbwjlmjmeqbpwqv`wvqfqfsqfpfmwbwjlmalqgfq.alwwln9?,kfbg=\t?algz=>kwws&0B&1E&1E?elqn#nfwklg>!nfwklg>!slpw!#,ebuj`lm-j`l!#~*8\t?,p`qjsw=\t-pfwBwwqjavwf+Bgnjmjpwqbwjlm>#mft#Bqqbz+*8?"Xfmgje^..=\x0e\tgjpsobz9aol`h8Vmelqwvmbwfoz/!=%maps8?,gju=,ebuj`lm-j`l!=>$pwzofpkffw$#jgfmwjej`bwjlm/#elq#f{bnsof/?oj=?b#kqfe>!,bm#bowfqmbwjufbp#b#qfpvow#lesw!=?,p`qjsw=\twzsf>!pvanjw!#\t+evm`wjlm+*#xqf`lnnfmgbwjlmelqn#b`wjlm>!,wqbmpelqnbwjlmqf`lmpwqv`wjlm-pwzof-gjpsobz#B``lqgjmd#wl#kjggfm!#mbnf>!bolmd#tjwk#wkfgl`vnfmw-algz-bssql{jnbwfoz#@lnnvmj`bwjlmpslpw!#b`wjlm>!nfbmjmd#%rvlw8..?"Xfmgje^..=Sqjnf#Njmjpwfq`kbqb`wfqjpwj`?,b=#?b#`obpp>wkf#kjpwlqz#le#lmnlvpflufq>!wkf#dlufqmnfmwkqfe>!kwwsp9,,tbp#lqjdjmbooztbp#jmwqlgv`fg`obppjej`bwjlmqfsqfpfmwbwjufbqf#`lmpjgfqfg?"Xfmgje^..=\t\tgfsfmgp#lm#wkfVmjufqpjwz#le#jm#`lmwqbpw#wl#sob`fklogfq>!jm#wkf#`bpf#lejmwfqmbwjlmbo#`lmpwjwvwjlmbopwzof>!alqgfq.9#evm`wjlm+*#xAf`bvpf#le#wkf.pwqj`w-gwg!=\t?wbaof#`obpp>!b``lnsbmjfg#azb``lvmw#le#wkf?p`qjsw#pq`>!,mbwvqf#le#wkf#wkf#sflsof#jm#jm#bggjwjlm#wlp*8#ip-jg#>#jg!#tjgwk>!233&!qfdbqgjmd#wkf#Qlnbm#@bwkloj`bm#jmgfsfmgfmwelooltjmd#wkf#-dje!#tjgwk>!2wkf#elooltjmd#gjp`qjnjmbwjlmbq`kbfloldj`bosqjnf#njmjpwfq-ip!=?,p`qjsw=`lnajmbwjlm#le#nbqdjmtjgwk>!`qfbwfFofnfmw+t-bwwb`kFufmw+?,b=?,wg=?,wq=pq`>!kwwsp9,,bJm#sbqwj`vobq/#bojdm>!ofew!#@yf`k#Qfsvaoj`Vmjwfg#Hjmdgln`lqqfpslmgfm`f`lm`ovgfg#wkbw-kwno!#wjwof>!+evm`wjlm#+*#x`lnfp#eqln#wkfbssoj`bwjlm#le?psbm#`obpp>!pafojfufg#wl#affnfmw+$p`qjsw$?,b=\t?,oj=\t?ojufqz#gjeefqfmw=?psbm#`obpp>!lswjlm#ubovf>!+bopl#hmltm#bp\n?oj=?b#kqfe>!=?jmsvw#mbnf>!pfsbqbwfg#eqlnqfefqqfg#wl#bp#ubojdm>!wls!=elvmgfq#le#wkfbwwfnswjmd#wl#`bqalm#gjl{jgf\t\t?gju#`obpp>!`obpp>!pfbq`k.,algz=\t?,kwno=lsslqwvmjwz#wl`lnnvmj`bwjlmp?,kfbg=\x0e\t?algz#pwzof>!tjgwk9Wj\rVSmd#Uj\rWkw`kbmdfp#jm#wkfalqgfq.`lolq9 3!#alqgfq>!3!#?,psbm=?,gju=?tbp#gjp`lufqfg!#wzsf>!wf{w!#*8\t?,p`qjsw=\t\tGfsbqwnfmw#le#f``ofpjbpwj`bowkfqf#kbp#affmqfpvowjmd#eqln?,algz=?,kwno=kbp#mfufq#affmwkf#ejqpw#wjnfjm#qfpslmpf#wlbvwlnbwj`booz#?,gju=\t\t?gju#jtbp#`lmpjgfqfgsfq`fmw#le#wkf!#,=?,b=?,gju=`loof`wjlm#le#gfp`fmgfg#eqlnpf`wjlm#le#wkfb``fsw.`kbqpfwwl#af#`lmevpfgnfnafq#le#wkf#sbggjmd.qjdkw9wqbmpobwjlm#lejmwfqsqfwbwjlm#kqfe>$kwws9,,tkfwkfq#lq#mlwWkfqf#bqf#boplwkfqf#bqf#nbmzb#pnboo#mvnafqlwkfq#sbqwp#lejnslppjaof#wl##`obpp>!avwwlmol`bwfg#jm#wkf-#Kltfufq/#wkfbmg#fufmwvboozBw#wkf#fmg#le#af`bvpf#le#jwpqfsqfpfmwp#wkf?elqn#b`wjlm>!#nfwklg>!slpw!jw#jp#slppjaofnlqf#ojhfoz#wlbm#jm`qfbpf#jmkbuf#bopl#affm`lqqfpslmgp#wlbmmlvm`fg#wkbwbojdm>!qjdkw!=nbmz#`lvmwqjfpelq#nbmz#zfbqpfbqojfpw#hmltmaf`bvpf#jw#tbpsw!=?,p`qjsw=\x0e#ubojdm>!wls!#jmkbajwbmwp#leelooltjmd#zfbq\x0e\t?gju#`obpp>!njoojlm#sflsof`lmwqlufqpjbo#`lm`fqmjmd#wkfbqdvf#wkbw#wkfdlufqmnfmw#bmgb#qfefqfm`f#wlwqbmpefqqfg#wlgfp`qjajmd#wkf#pwzof>!`lolq9bowklvdk#wkfqfafpw#hmltm#elqpvanjw!#mbnf>!nvowjsoj`bwjlmnlqf#wkbm#lmf#qf`ldmjwjlm#le@lvm`jo#le#wkffgjwjlm#le#wkf##?nfwb#mbnf>!Fmwfqwbjmnfmw#btbz#eqln#wkf#8nbqdjm.qjdkw9bw#wkf#wjnf#lejmufpwjdbwjlmp`lmmf`wfg#tjwkbmg#nbmz#lwkfqbowklvdk#jw#jpafdjmmjmd#tjwk#?psbm#`obpp>!gfp`fmgbmwp#le?psbm#`obpp>!j#bojdm>!qjdkw!?,kfbg=\t?algz#bpsf`wp#le#wkfkbp#pjm`f#affmFvqlsfbm#Vmjlmqfnjmjp`fmw#lenlqf#gjeej`vowUj`f#Sqfpjgfmw`lnslpjwjlm#lesbppfg#wkqlvdknlqf#jnslqwbmwelmw.pjyf922s{f{sobmbwjlm#lewkf#`lm`fsw#letqjwwfm#jm#wkf\n?psbm#`obpp>!jp#lmf#le#wkf#qfpfnaobm`f#wllm#wkf#dqlvmgptkj`k#`lmwbjmpjm`ovgjmd#wkf#gfejmfg#az#wkfsvaoj`bwjlm#lenfbmp#wkbw#wkflvwpjgf#le#wkfpvsslqw#le#wkf?jmsvw#`obpp>!?psbm#`obpp>!w+Nbwk-qbmgln+*nlpw#sqlnjmfmwgfp`qjswjlm#le@lmpwbmwjmlsoftfqf#svaojpkfg?gju#`obpp>!pfbssfbqp#jm#wkf2!#kfjdkw>!2!#nlpw#jnslqwbmwtkj`k#jm`ovgfptkj`k#kbg#affmgfpwqv`wjlm#lewkf#slsvobwjlm\t\n?gju#`obpp>!slppjajojwz#leplnfwjnfp#vpfgbssfbq#wl#kbufpv``fpp#le#wkfjmwfmgfg#wl#afsqfpfmw#jm#wkfpwzof>!`ofbq9a\x0e\t?,p`qjsw=\x0e\t?tbp#elvmgfg#jmjmwfqujft#tjwk\\jg!#`lmwfmw>!`bsjwbo#le#wkf\x0e\t?ojmh#qfo>!pqfofbpf#le#wkfsljmw#lvw#wkbw{NOKwwsQfrvfpwbmg#pvapfrvfmwpf`lmg#obqdfpwufqz#jnslqwbmwpsf`jej`bwjlmppvqeb`f#le#wkfbssojfg#wl#wkfelqfjdm#sloj`z\\pfwGlnbjmMbnffpwbaojpkfg#jmjp#afojfufg#wlJm#bggjwjlm#wlnfbmjmd#le#wkfjp#mbnfg#bewfqwl#sqlwf`w#wkfjp#qfsqfpfmwfgGf`obqbwjlm#lenlqf#feej`jfmw@obppjej`bwjlmlwkfq#elqnp#lekf#qfwvqmfg#wl?psbm#`obpp>!`sfqelqnbm`f#le+evm`wjlm+*#x\x0eje#bmg#lmoz#jeqfdjlmp#le#wkfofbgjmd#wl#wkfqfobwjlmp#tjwkVmjwfg#Mbwjlmppwzof>!kfjdkw9lwkfq#wkbm#wkfzsf!#`lmwfmw>!Bppl`jbwjlm#le\t?,kfbg=\t?algzol`bwfg#lm#wkfjp#qfefqqfg#wl+jm`ovgjmd#wkf`lm`fmwqbwjlmpwkf#jmgjujgvbobnlmd#wkf#nlpwwkbm#bmz#lwkfq,=\t?ojmh#qfo>!#qfwvqm#ebopf8wkf#svqslpf#lewkf#bajojwz#wl8`lolq9 eee~\t-\t?psbm#`obpp>!wkf#pvaif`w#legfejmjwjlmp#le=\x0e\t?ojmh#qfo>!`objn#wkbw#wkfkbuf#gfufolsfg?wbaof#tjgwk>!`fofaqbwjlm#leElooltjmd#wkf#wl#gjpwjmdvjpk?psbm#`obpp>!awbhfp#sob`f#jmvmgfq#wkf#mbnfmlwfg#wkbw#wkf=?"Xfmgje^..=\tpwzof>!nbqdjm.jmpwfbg#le#wkfjmwqlgv`fg#wkfwkf#sql`fpp#lejm`qfbpjmd#wkfgjeefqfm`fp#jmfpwjnbwfg#wkbwfpsf`jbooz#wkf,gju=?gju#jg>!tbp#fufmwvboozwkqlvdklvw#kjpwkf#gjeefqfm`fplnfwkjmd#wkbwpsbm=?,psbm=?,pjdmjej`bmwoz#=?,p`qjsw=\x0e\t\x0e\tfmujqlmnfmwbo#wl#sqfufmw#wkfkbuf#affm#vpfgfpsf`jbooz#elqvmgfqpwbmg#wkfjp#fppfmwjbooztfqf#wkf#ejqpwjp#wkf#obqdfpwkbuf#affm#nbgf!#pq`>!kwws9,,jmwfqsqfwfg#bppf`lmg#kboe#le`qloojmd>!ml!#jp#`lnslpfg#leJJ/#Kloz#Qlnbmjp#f{sf`wfg#wlkbuf#wkfjq#ltmgfejmfg#bp#wkfwqbgjwjlmbooz#kbuf#gjeefqfmwbqf#lewfm#vpfgwl#fmpvqf#wkbwbdqffnfmw#tjwk`lmwbjmjmd#wkfbqf#eqfrvfmwozjmelqnbwjlm#lmf{bnsof#jp#wkfqfpvowjmd#jm#b?,b=?,oj=?,vo=#`obpp>!ellwfqbmg#fpsf`jboozwzsf>!avwwlm!#?,psbm=?,psbm=tkj`k#jm`ovgfg=\t?nfwb#mbnf>!`lmpjgfqfg#wkf`bqqjfg#lvw#azKltfufq/#jw#jpaf`bnf#sbqw#lejm#qfobwjlm#wlslsvobq#jm#wkfwkf#`bsjwbo#letbp#leej`jbooztkj`k#kbp#affmwkf#Kjpwlqz#lebowfqmbwjuf#wlgjeefqfmw#eqlnwl#pvsslqw#wkfpvddfpwfg#wkbwjm#wkf#sql`fpp##?gju#`obpp>!wkf#elvmgbwjlmaf`bvpf#le#kjp`lm`fqmfg#tjwkwkf#vmjufqpjwzlsslpfg#wl#wkfwkf#`lmwf{w#le?psbm#`obpp>!swf{w!#mbnf>!r!\n\n?gju#`obpp>!wkf#p`jfmwjej`qfsqfpfmwfg#aznbwkfnbwj`jbmpfof`wfg#az#wkfwkbw#kbuf#affm=?gju#`obpp>!`gju#jg>!kfbgfqjm#sbqwj`vobq/`lmufqwfg#jmwl*8\t?,p`qjsw=\t?skjolplskj`bo#pqsphlkqubwphjwj\rVSmd#Uj\rWkw!kwws9,,!=?psbm#`obpp>!nfnafqp#le#wkf#tjmglt-ol`bwjlmufqwj`bo.bojdm9,b=#\x7f#?b#kqfe>!?"gl`wzsf#kwno=nfgjb>!p`qffm!#?lswjlm#ubovf>!ebuj`lm-j`l!#,=\t\n\n?gju#`obpp>!`kbqb`wfqjpwj`p!#nfwklg>!dfw!#,algz=\t?,kwno=\tpklqw`vw#j`lm!#gl`vnfmw-tqjwf+sbggjmd.alwwln9qfsqfpfmwbwjufppvanjw!#ubovf>!bojdm>!`fmwfq!#wkqlvdklvw#wkf#p`jfm`f#ej`wjlm\t##?gju#`obpp>!pvanjw!#`obpp>!lmf#le#wkf#nlpw#ubojdm>!wls!=?tbp#fpwbaojpkfg*8\x0e\t?,p`qjsw=\x0e\tqfwvqm#ebopf8!=*-pwzof-gjpsobzaf`bvpf#le#wkf#gl`vnfmw-`llhjf?elqn#b`wjlm>!,~algzxnbqdjm938Fm`z`olsfgjb#leufqpjlm#le#wkf#-`qfbwfFofnfmw+mbnf!#`lmwfmw>!?,gju=\t?,gju=\t\tbgnjmjpwqbwjuf#?,algz=\t?,kwno=kjpwlqz#le#wkf#!=?jmsvw#wzsf>!slqwjlm#le#wkf#bp#sbqw#le#wkf#%maps8?b#kqfe>!lwkfq#`lvmwqjfp!=\t?gju#`obpp>!?,psbm=?,psbm=?Jm#lwkfq#tlqgp/gjpsobz9#aol`h8`lmwqlo#le#wkf#jmwqlgv`wjlm#le,=\t?nfwb#mbnf>!bp#tfoo#bp#wkf#jm#qf`fmw#zfbqp\x0e\t\n?gju#`obpp>!?,gju=\t\n?,gju=\tjmpsjqfg#az#wkfwkf#fmg#le#wkf#`lnsbwjaof#tjwkaf`bnf#hmltm#bp#pwzof>!nbqdjm9-ip!=?,p`qjsw=?#Jmwfqmbwjlmbo#wkfqf#kbuf#affmDfqnbm#obmdvbdf#pwzof>!`lolq9 @lnnvmjpw#Sbqwz`lmpjpwfmw#tjwkalqgfq>!3!#`foo#nbqdjmkfjdkw>!wkf#nbilqjwz#le!#bojdm>!`fmwfqqfobwfg#wl#wkf#nbmz#gjeefqfmw#Lqwklgl{#@kvq`kpjnjobq#wl#wkf#,=\t?ojmh#qfo>!ptbp#lmf#le#wkf#vmwjo#kjp#gfbwk~*+*8\t?,p`qjsw=lwkfq#obmdvbdfp`lnsbqfg#wl#wkfslqwjlmp#le#wkfwkf#Mfwkfqobmgpwkf#nlpw#`lnnlmab`hdqlvmg9vqo+bqdvfg#wkbw#wkfp`qloojmd>!ml!#jm`ovgfg#jm#wkfMlqwk#Bnfqj`bm#wkf#mbnf#le#wkfjmwfqsqfwbwjlmpwkf#wqbgjwjlmbogfufolsnfmw#le#eqfrvfmwoz#vpfgb#`loof`wjlm#leufqz#pjnjobq#wlpvqqlvmgjmd#wkff{bnsof#le#wkjpbojdm>!`fmwfq!=tlvog#kbuf#affmjnbdf\\`bswjlm#>bwwb`kfg#wl#wkfpvddfpwjmd#wkbwjm#wkf#elqn#le#jmuloufg#jm#wkfjp#gfqjufg#eqlnmbnfg#bewfq#wkfJmwqlgv`wjlm#wlqfpwqj`wjlmp#lm#pwzof>!tjgwk9#`bm#af#vpfg#wl#wkf#`qfbwjlm#lenlpw#jnslqwbmw#jmelqnbwjlm#bmgqfpvowfg#jm#wkf`loobspf#le#wkfWkjp#nfbmp#wkbwfofnfmwp#le#wkftbp#qfsob`fg#azbmbozpjp#le#wkfjmpsjqbwjlm#elqqfdbqgfg#bp#wkfnlpw#pv``fppevohmltm#bp#%rvlw8b#`lnsqfkfmpjufKjpwlqz#le#wkf#tfqf#`lmpjgfqfgqfwvqmfg#wl#wkfbqf#qfefqqfg#wlVmplvq`fg#jnbdf=\t\n?gju#`obpp>!`lmpjpwp#le#wkfpwlsSqlsbdbwjlmjmwfqfpw#jm#wkfbubjobajojwz#lebssfbqp#wl#kbuffof`wqlnbdmfwj`fmbaofPfquj`fp+evm`wjlm#le#wkfJw#jp#jnslqwbmw?,p`qjsw=?,gju=evm`wjlm+*xubq#qfobwjuf#wl#wkfbp#b#qfpvow#le#wkf#slpjwjlm#leElq#f{bnsof/#jm#nfwklg>!slpw!#tbp#elooltfg#az%bns8ngbpk8#wkfwkf#bssoj`bwjlmip!=?,p`qjsw=\x0e\tvo=?,gju=?,gju=bewfq#wkf#gfbwktjwk#qfpsf`w#wlpwzof>!sbggjmd9jp#sbqwj`vobqozgjpsobz9jmojmf8#wzsf>!pvanjw!#jp#gjujgfg#jmwl\bTA\nzk#+\vBl\bQ\x7f*qfpslmpbajojgbgbgnjmjpwqb`j/_mjmwfqmb`jlmbofp`lqqfpslmgjfmwf\fHe\fHF\fHC\fIg\fH{\fHF\fIn\fH\\\fIa\fHY\fHU\fHB\fHR\fH\\\fIk\fH^\fIg\fH{\fIg\fHn\fHv\fIm\fHD\fHR\fHY\fH^\fIk\fHy\fHS\fHD\fHT\fH\\\fHy\fHR\fH\\\fHF\fIm\fH^\fHS\fHT\fHz\fIg\fHp\fIk\fHn\fHv\fHR\fHU\fHS\fHc\fHA\fIk\fHp\fIk\fHn\fHZ\fHR\fHB\fHS\fH^\fHU\fHB\fHR\fH\\\fIl\fHp\fHR\fH{\fH\\\fHO\fH@\fHD\fHR\fHD\fIk\fHy\fIm\fHB\fHR\fH\\\fH@\fIa\fH^\fIe\fH{\fHB\fHR\fH^\fHS\fHy\fHB\fHU\fHS\fH^\fHR\fHF\fIo\fH[\fIa\fHL\fH@\fHN\fHP\fHH\fIk\fHA\fHR\fHp\fHF\fHR\fHy\fIa\fH^\fHS\fHy\fHs\fIa\fH\\\fIk\fHD\fHz\fHS\fH^\fHR\fHG\fHJ\fI`\fH\\\fHR\fHD\fHB\fHR\fHB\fH^\fIk\fHB\fHH\fHJ\fHR\fHD\fH@\fHR\fHp\fHR\fH\\\fHY\fHS\fHy\fHR\fHT\fHy\fIa\fHC\fIg\fHn\fHv\fHR\fHU\fHH\fIk\fHF\fHU\fIm\fHm\fHv\fH@\fHH\fHR\fHC\fHR\fHT\fHn\fHY\fHR\fHJ\fHJ\fIk\fHz\fHD\fIk\fHF\fHS\fHw\fH^\fIk\fHY\fHS\fHZ\fIk\fH[\fH\\\fHR\fHp\fIa\fHC\fHe\fHH\fIa\fHH\fH\\\fHB\fIm\fHn\fH@\fHd\fHJ\fIg\fHD\fIg\fHn\fHe\fHF\fHy\fH\\\fHO\fHF\fHN\fHP\fIk\fHn\fHT\fIa\fHI\fHS\fHH\fHG\fHS\fH^\fIa\fHB\fHB\fIm\fHz\fIa\fHC\fHi\fHv\fIa\fHw\fHR\fHw\fIn\fHs\fHH\fIl\fHT\fHn\fH{\fIl\fHH\fHp\fHR\fHc\fH{\fHR\fHY\fHS\fHA\fHR\fH{\fHt\fHO\fIa\fHs\fIk\fHJ\fIn\fHT\fH\\\fIk\fHJ\fHS\fHD\fIg\fHn\fHU\fHH\fIa\fHC\fHR\fHT\fIk\fHy\fIa\fHT\fH{\fHR\fHn\fHK\fIl\fHY\fHS\fHZ\fIa\fHY\fH\\\fHR\fHH\fIk\fHn\fHJ\fId\fHs\fIa\fHT\fHD\fHy\fIa\fHZ\fHR\fHT\fHR\fHB\fHD\fIk\fHi\fHJ\fHR\fH^\fHH\fH@\fHS\fHp\fH^\fIl\fHF\fIm\fH\\\fIn\fH[\fHU\fHS\fHn\fHJ\fIl\fHB\fHS\fHH\fIa\fH\\\fHy\fHY\fHS\fHH\fHR\fH\\\fIm\fHF\fHC\fIk\fHT\fIa\fHI\fHR\fHD\fHy\fH\\\fIg\fHM\fHP\fHB\fIm\fHy\fIa\fHH\fHC\fIg\fHp\fHD\fHR\fHy\fIo\fHF\fHC\fHR\fHF\fIg\fHT\fIa\fHs\fHt\fH\\\fIk\fH^\fIn\fHy\fHR\fH\\\fIa\fHC\fHY\fHS\fHv\fHR\fH\\\fHT\fIn\fHv\fHD\fHR\fHB\fIn\fH^\fIa\fHC\fHJ\fIk\fHz\fIk\fHn\fHU\fHB\fIk\fHZ\fHR\fHT\fIa\fHy\fIn\fH^\fHB\fId\fHn\fHD\fIk\fHH\fId\fHC\fHR\fH\\\fHp\fHS\fHT\fHy\fIkqpp({no!#wjwof>!.wzsf!#`lmwfmw>!wjwof!#`lmwfmw>!bw#wkf#pbnf#wjnf-ip!=?,p`qjsw=\t?!#nfwklg>!slpw!#?,psbm=?,b=?,oj=ufqwj`bo.bojdm9w,irvfqz-njm-ip!=-`oj`h+evm`wjlm+#pwzof>!sbggjmd.~*+*8\t?,p`qjsw=\t?,psbm=?b#kqfe>!?b#kqfe>!kwws9,,*8#qfwvqm#ebopf8wf{w.gf`lqbwjlm9#p`qloojmd>!ml!#alqgfq.`loobspf9bppl`jbwfg#tjwk#Abkbpb#JmglmfpjbFmdojpk#obmdvbdf?wf{w#{no9psb`f>-dje!#alqgfq>!3!?,algz=\t?,kwno=\tlufqeolt9kjggfm8jnd#pq`>!kwws9,,bggFufmwOjpwfmfqqfpslmpjaof#elq#p-ip!=?,p`qjsw=\t,ebuj`lm-j`l!#,=lsfqbwjmd#pzpwfn!#pwzof>!tjgwk92wbqdfw>!\\aobmh!=Pwbwf#Vmjufqpjwzwf{w.bojdm9ofew8\tgl`vnfmw-tqjwf+/#jm`ovgjmd#wkf#bqlvmg#wkf#tlqog*8\x0e\t?,p`qjsw=\x0e\t?!#pwzof>!kfjdkw98lufqeolt9kjggfmnlqf#jmelqnbwjlmbm#jmwfqmbwjlmbob#nfnafq#le#wkf#lmf#le#wkf#ejqpw`bm#af#elvmg#jm#?,gju=\t\n\n?,gju=\tgjpsobz9#mlmf8!=!#,=\t?ojmh#qfo>!\t##+evm`wjlm+*#xwkf#26wk#`fmwvqz-sqfufmwGfebvow+obqdf#mvnafq#le#Azybmwjmf#Fnsjqf-isd\x7fwkvna\x7fofew\x7fubpw#nbilqjwz#lenbilqjwz#le#wkf##bojdm>!`fmwfq!=Vmjufqpjwz#Sqfppglnjmbwfg#az#wkfPf`lmg#Tlqog#Tbqgjpwqjavwjlm#le#pwzof>!slpjwjlm9wkf#qfpw#le#wkf#`kbqb`wfqjyfg#az#qfo>!mleloolt!=gfqjufp#eqln#wkfqbwkfq#wkbm#wkf#b#`lnajmbwjlm#lepwzof>!tjgwk9233Fmdojpk.psfbhjmd`lnsvwfq#p`jfm`falqgfq>!3!#bow>!wkf#f{jpwfm`f#leGfnl`qbwj`#Sbqwz!#pwzof>!nbqdjm.Elq#wkjp#qfbplm/-ip!=?,p`qjsw=\t\npAzWbdMbnf+p*X3^ip!=?,p`qjsw=\x0e\t?-ip!=?,p`qjsw=\x0e\tojmh#qfo>!j`lm!#$#bow>$$#`obpp>$elqnbwjlm#le#wkfufqpjlmp#le#wkf#?,b=?,gju=?,gju=,sbdf=\t##?sbdf=\t?gju#`obpp>!`lmwaf`bnf#wkf#ejqpwabkbpb#Jmglmfpjbfmdojpk#+pjnsof*"y"W"W"["Q"U"V"@=i=l<^<\\=n=m!?gju#jg>!ellwfq!=wkf#Vmjwfg#Pwbwfp?jnd#pq`>!kwws9,,-isd\x7fqjdkw\x7fwkvna\x7f-ip!=?,p`qjsw=\x0e\t?ol`bwjlm-sqlwl`loeqbnfalqgfq>!3!#p!#,=\t?nfwb#mbnf>!?,b=?,gju=?,gju=?elmw.tfjdkw9alog8%rvlw8#bmg#%rvlw8gfsfmgjmd#lm#wkf#nbqdjm938sbggjmd9!#qfo>!mleloolt!#Sqfpjgfmw#le#wkf#wtfmwjfwk#`fmwvqzfujpjlm=\t##?,sbdfJmwfqmfw#F{solqfqb-bpzm`#>#wqvf8\x0e\tjmelqnbwjlm#balvw?gju#jg>!kfbgfq!=!#b`wjlm>!kwws9,,?b#kqfe>!kwwsp9,,?gju#jg>!`lmwfmw!?,gju=\x0e\t?,gju=\x0e\t?gfqjufg#eqln#wkf#?jnd#pq`>$kwws9,,b``lqgjmd#wl#wkf#\t?,algz=\t?,kwno=\tpwzof>!elmw.pjyf9p`qjsw#obmdvbdf>!Bqjbo/#Kfoufwj`b/?,b=?psbm#`obpp>!?,p`qjsw=?p`qjsw#slojwj`bo#sbqwjfpwg=?,wq=?,wbaof=?kqfe>!kwws9,,ttt-jmwfqsqfwbwjlm#leqfo>!pwzofpkffw!#gl`vnfmw-tqjwf+$?`kbqpfw>!vwe.;!=\tafdjmmjmd#le#wkf#qfufbofg#wkbw#wkfwfofujpjlm#pfqjfp!#qfo>!mleloolt!=#wbqdfw>!\\aobmh!=`objnjmd#wkbw#wkfkwws&0B&1E&1Ettt-nbmjefpwbwjlmp#leSqjnf#Njmjpwfq#lejmeovfm`fg#az#wkf`obpp>!`ofbqej{!=,gju=\x0e\t?,gju=\x0e\t\x0e\twkqff.gjnfmpjlmbo@kvq`k#le#Fmdobmgle#Mlqwk#@bqlojmbprvbqf#hjolnfwqfp-bggFufmwOjpwfmfqgjpwjm`w#eqln#wkf`lnnlmoz#hmltm#bpSklmfwj`#Boskbafwgf`obqfg#wkbw#wkf`lmwqloofg#az#wkfAfmibnjm#Eqbmhojmqlof.sobzjmd#dbnfwkf#Vmjufqpjwz#lejm#Tfpwfqm#Fvqlsfsfqplmbo#`lnsvwfqSqlif`w#Dvwfmafqdqfdbqgofpp#le#wkfkbp#affm#sqlslpfgwldfwkfq#tjwk#wkf=?,oj=?oj#`obpp>!jm#plnf#`lvmwqjfpnjm-ip!=?,p`qjsw=le#wkf#slsvobwjlmleej`jbo#obmdvbdf?jnd#pq`>!jnbdfp,jgfmwjejfg#az#wkfmbwvqbo#qfplvq`fp`obppjej`bwjlm#le`bm#af#`lmpjgfqfgrvbmwvn#nf`kbmj`pMfufqwkfofpp/#wkfnjoojlm#zfbqp#bdl?,algz=\x0e\t?,kwno=\x0e"y"W"W"["Q"U"V"@\twbhf#bgubmwbdf#lebmg/#b``lqgjmd#wlbwwqjavwfg#wl#wkfNj`qlplew#Tjmgltpwkf#ejqpw#`fmwvqzvmgfq#wkf#`lmwqlogju#`obpp>!kfbgfqpklqwoz#bewfq#wkfmlwbaof#f{`fswjlmwfmp#le#wklvpbmgppfufqbo#gjeefqfmwbqlvmg#wkf#tlqog-qfb`kjmd#njojwbqzjplobwfg#eqln#wkflsslpjwjlm#wl#wkfwkf#Log#WfpwbnfmwBeqj`bm#Bnfqj`bmpjmpfqwfg#jmwl#wkfpfsbqbwf#eqln#wkfnfwqlslojwbm#bqfbnbhfp#jw#slppjaofb`hmltofgdfg#wkbwbqdvbaoz#wkf#nlpwwzsf>!wf{w,`pp!=\twkf#JmwfqmbwjlmboB``lqgjmd#wl#wkf#sf>!wf{w,`pp!#,=\t`ljm`jgf#tjwk#wkfwtl.wkjqgp#le#wkfGvqjmd#wkjp#wjnf/gvqjmd#wkf#sfqjlgbmmlvm`fg#wkbw#kfwkf#jmwfqmbwjlmbobmg#nlqf#qf`fmwozafojfufg#wkbw#wkf`lmp`jlvpmfpp#bmgelqnfqoz#hmltm#bppvqqlvmgfg#az#wkfejqpw#bssfbqfg#jml``bpjlmbooz#vpfgslpjwjlm9baplovwf8!#wbqdfw>!\\aobmh!#slpjwjlm9qfobwjuf8wf{w.bojdm9`fmwfq8ib{,ojap,irvfqz,2-ab`hdqlvmg.`lolq9 wzsf>!bssoj`bwjlm,bmdvbdf!#`lmwfmw>!?nfwb#kwws.frvju>!Sqjub`z#Sloj`z?,b=f+!&0@p`qjsw#pq`>$!#wbqdfw>!\\aobmh!=Lm#wkf#lwkfq#kbmg/-isd\x7fwkvna\x7fqjdkw\x7f1?,gju=?gju#`obpp>!?gju#pwzof>!eolbw9mjmfwffmwk#`fmwvqz?,algz=\x0e\t?,kwno=\x0e\t?jnd#pq`>!kwws9,,p8wf{w.bojdm9`fmwfqelmw.tfjdkw9#alog8#B``lqgjmd#wl#wkf#gjeefqfm`f#afwtffm!#eqbnfalqgfq>!3!#!#pwzof>!slpjwjlm9ojmh#kqfe>!kwws9,,kwno7,ollpf-gwg!=\tgvqjmd#wkjp#sfqjlg?,wg=?,wq=?,wbaof=`olpfoz#qfobwfg#wlelq#wkf#ejqpw#wjnf8elmw.tfjdkw9alog8jmsvw#wzsf>!wf{w!#?psbm#pwzof>!elmw.lmqfbgzpwbwf`kbmdf\n?gju#`obpp>!`ofbqgl`vnfmw-ol`bwjlm-#Elq#f{bnsof/#wkf#b#tjgf#ubqjfwz#le#?"GL@WZSF#kwno=\x0e\t?%maps8%maps8%maps8!=?b#kqfe>!kwws9,,pwzof>!eolbw9ofew8`lm`fqmfg#tjwk#wkf>kwws&0B&1E&1Ettt-jm#slsvobq#`vowvqfwzsf>!wf{w,`pp!#,=jw#jp#slppjaof#wl#Kbqubqg#Vmjufqpjwzwzofpkffw!#kqfe>!,wkf#nbjm#`kbqb`wfqL{elqg#Vmjufqpjwz##mbnf>!hfztlqgp!#`pwzof>!wf{w.bojdm9wkf#Vmjwfg#Hjmdglnefgfqbo#dlufqmnfmw?gju#pwzof>!nbqdjm#gfsfmgjmd#lm#wkf#gfp`qjswjlm#le#wkf?gju#`obpp>!kfbgfq-njm-ip!=?,p`qjsw=gfpwqv`wjlm#le#wkfpojdkwoz#gjeefqfmwjm#b``lqgbm`f#tjwkwfof`lnnvmj`bwjlmpjmgj`bwfp#wkbw#wkfpklqwoz#wkfqfbewfqfpsf`jbooz#jm#wkf#Fvqlsfbm#`lvmwqjfpKltfufq/#wkfqf#bqfpq`>!kwws9,,pwbwj`pvddfpwfg#wkbw#wkf!#pq`>!kwws9,,ttt-b#obqdf#mvnafq#le#Wfof`lnnvmj`bwjlmp!#qfo>!mleloolt!#wKloz#Qlnbm#Fnsfqlqbonlpw#f{`ovpjufoz!#alqgfq>!3!#bow>!Pf`qfwbqz#le#Pwbwf`vonjmbwjmd#jm#wkf@JB#Tlqog#Eb`wallhwkf#nlpw#jnslqwbmwbmmjufqpbqz#le#wkfpwzof>!ab`hdqlvmg.?oj=?fn=?b#kqfe>!,wkf#Bwobmwj`#L`fbmpwqj`woz#psfbhjmd/pklqwoz#afelqf#wkfgjeefqfmw#wzsfp#lewkf#Lwwlnbm#Fnsjqf=?jnd#pq`>!kwws9,,Bm#Jmwqlgv`wjlm#wl`lmpfrvfm`f#le#wkfgfsbqwvqf#eqln#wkf@lmefgfqbwf#Pwbwfpjmgjdfmlvp#sflsofpSql`ffgjmdp#le#wkfjmelqnbwjlm#lm#wkfwkflqjfp#kbuf#affmjmuloufnfmw#jm#wkfgjujgfg#jmwl#wkqffbgib`fmw#`lvmwqjfpjp#qfpslmpjaof#elqgjpplovwjlm#le#wkf`loobalqbwjlm#tjwktjgfoz#qfdbqgfg#bpkjp#`lmwfnslqbqjfpelvmgjmd#nfnafq#leGlnjmj`bm#Qfsvaoj`dfmfqbooz#b``fswfgwkf#slppjajojwz#lebqf#bopl#bubjobaofvmgfq#`lmpwqv`wjlmqfpwlqbwjlm#le#wkfwkf#dfmfqbo#svaoj`jp#bonlpw#fmwjqfozsbppfp#wkqlvdk#wkfkbp#affm#pvddfpwfg`lnsvwfq#bmg#ujgflDfqnbmj`#obmdvbdfp#b``lqgjmd#wl#wkf#gjeefqfmw#eqln#wkfpklqwoz#bewfqtbqgpkqfe>!kwwsp9,,ttt-qf`fmw#gfufolsnfmwAlbqg#le#Gjqf`wlqp?gju#`obpp>!pfbq`k\x7f#?b#kqfe>!kwws9,,Jm#sbqwj`vobq/#wkfNvowjsof#ellwmlwfplq#lwkfq#pvapwbm`fwklvpbmgp#le#zfbqpwqbmpobwjlm#le#wkf?,gju=\x0e\t?,gju=\x0e\t\x0e\t?b#kqfe>!jmgf{-skstbp#fpwbaojpkfg#jmnjm-ip!=?,p`qjsw=\tsbqwj`jsbwf#jm#wkfb#pwqlmd#jmeovfm`fpwzof>!nbqdjm.wls9qfsqfpfmwfg#az#wkfdqbgvbwfg#eqln#wkfWqbgjwjlmbooz/#wkfFofnfmw+!p`qjsw!*8Kltfufq/#pjm`f#wkf,gju=\t?,gju=\t?gju#ofew8#nbqdjm.ofew9sqlwf`wjlm#bdbjmpw38#ufqwj`bo.bojdm9Vmelqwvmbwfoz/#wkfwzsf>!jnbdf,{.j`lm,gju=\t?gju#`obpp>!#`obpp>!`ofbqej{!=?gju#`obpp>!ellwfq\n\n?,gju=\t\n\n?,gju=\twkf#nlwjlm#sj`wvqf<}=f!t0-lqd,2:::,{kwno!=?b#wbqdfw>!\\aobmh!#wf{w,kwno8#`kbqpfw>!#wbqdfw>!\\aobmh!=?wbaof#`foosbggjmd>!bvwl`lnsofwf>!lee!#wf{w.bojdm9#`fmwfq8wl#obpw#ufqpjlm#az#ab`hdqlvmg.`lolq9# !#kqfe>!kwws9,,ttt-,gju=?,gju=?gju#jg>?b#kqfe>! !#`obpp>!!=?jnd#pq`>!kwws9,,`qjsw!#pq`>!kwws9,,\t?p`qjsw#obmdvbdf>!,,FM!#!kwws9,,ttt-tfm`lgfVQJ@lnslmfmw+!#kqfe>!ibubp`qjsw9?gju#`obpp>!`lmwfmwgl`vnfmw-tqjwf+$?p`slpjwjlm9#baplovwf8p`qjsw#pq`>!kwws9,,#pwzof>!nbqdjm.wls9-njm-ip!=?,p`qjsw=\t?,gju=\t?gju#`obpp>!t0-lqd,2:::,{kwno!#\t\x0e\t?,algz=\x0e\t?,kwno=gjpwjm`wjlm#afwtffm,!#wbqdfw>!\\aobmh!=?ojmh#kqfe>!kwws9,,fm`lgjmd>!vwe.;!<=\tt-bggFufmwOjpwfmfq!kwws9,,ttt-j`lm!#kqfe>!kwws9,,#pwzof>!ab`hdqlvmg9wzsf>!wf{w,`pp!#,=\tnfwb#sqlsfqwz>!ld9w?jmsvw#wzsf>!wf{w!##pwzof>!wf{w.bojdm9wkf#gfufolsnfmw#le#wzofpkffw!#wzsf>!wfkwno8#`kbqpfw>vwe.;jp#`lmpjgfqfg#wl#afwbaof#tjgwk>!233&!#Jm#bggjwjlm#wl#wkf#`lmwqjavwfg#wl#wkf#gjeefqfm`fp#afwtffmgfufolsnfmw#le#wkf#Jw#jp#jnslqwbmw#wl#?,p`qjsw=\t\t?p`qjsw##pwzof>!elmw.pjyf92=?,psbm=?psbm#jg>daOjaqbqz#le#@lmdqfpp?jnd#pq`>!kwws9,,jnFmdojpk#wqbmpobwjlmB`bgfnz#le#P`jfm`fpgju#pwzof>!gjpsobz9`lmpwqv`wjlm#le#wkf-dfwFofnfmwAzJg+jg*jm#`lmivm`wjlm#tjwkFofnfmw+$p`qjsw$*8#?nfwb#sqlsfqwz>!ld9<}=f!wf{w!#mbnf>!=Sqjub`z#Sloj`z?,b=bgnjmjpwfqfg#az#wkffmbaofPjmdofQfrvfpwpwzof>%rvlw8nbqdjm9?,gju=?,gju=?,gju=?=?jnd#pq`>!kwws9,,j#pwzof>%rvlw8eolbw9qfefqqfg#wl#bp#wkf#wlwbo#slsvobwjlm#lejm#Tbpkjmdwlm/#G-@-#pwzof>!ab`hdqlvmg.bnlmd#lwkfq#wkjmdp/lqdbmjybwjlm#le#wkfsbqwj`jsbwfg#jm#wkfwkf#jmwqlgv`wjlm#lejgfmwjejfg#tjwk#wkfej`wjlmbo#`kbqb`wfq#L{elqg#Vmjufqpjwz#njpvmgfqpwbmgjmd#leWkfqf#bqf/#kltfufq/pwzofpkffw!#kqfe>!,@lovnajb#Vmjufqpjwzf{sbmgfg#wl#jm`ovgfvpvbooz#qfefqqfg#wljmgj`bwjmd#wkbw#wkfkbuf#pvddfpwfg#wkbwbeejojbwfg#tjwk#wkf`lqqfobwjlm#afwtffmmvnafq#le#gjeefqfmw=?,wg=?,wq=?,wbaof=Qfsvaoj`#le#Jqfobmg\t?,p`qjsw=\t?p`qjsw#vmgfq#wkf#jmeovfm`f`lmwqjavwjlm#wl#wkfLeej`jbo#tfapjwf#lekfbgrvbqwfqp#le#wkf`fmwfqfg#bqlvmg#wkfjnsoj`bwjlmp#le#wkfkbuf#affm#gfufolsfgEfgfqbo#Qfsvaoj`#leaf`bnf#jm`qfbpjmdoz`lmwjmvbwjlm#le#wkfMlwf/#kltfufq/#wkbwpjnjobq#wl#wkbw#le#`bsbajojwjfp#le#wkfb``lqgbm`f#tjwk#wkfsbqwj`jsbmwp#jm#wkfevqwkfq#gfufolsnfmwvmgfq#wkf#gjqf`wjlmjp#lewfm#`lmpjgfqfgkjp#zlvmdfq#aqlwkfq?,wg=?,wq=?,wbaof=?b#kwws.frvju>![.VB.skzpj`bo#sqlsfqwjfple#Aqjwjpk#@lovnajbkbp#affm#`qjwj`jyfg+tjwk#wkf#f{`fswjlmrvfpwjlmp#balvw#wkfsbppjmd#wkqlvdk#wkf3!#`foosbggjmd>!3!#wklvpbmgp#le#sflsofqfgjqf`wp#kfqf-#Elqkbuf#`kjogqfm#vmgfq&0F&0@,p`qjsw&0F!**8?b#kqfe>!kwws9,,ttt-?oj=?b#kqfe>!kwws9,,pjwf\\mbnf!#`lmwfmw>!wf{w.gf`lqbwjlm9mlmfpwzof>!gjpsobz9#mlmf?nfwb#kwws.frvju>![.mft#Gbwf+*-dfwWjnf+*#wzsf>!jnbdf,{.j`lm!?,psbm=?psbm#`obpp>!obmdvbdf>!ibubp`qjswtjmglt-ol`bwjlm-kqfe?b#kqfe>!ibubp`qjsw9..=\x0e\t?p`qjsw#wzsf>!w?b#kqfe>$kwws9,,ttt-klqw`vw#j`lm!#kqfe>!?,gju=\x0e\t?gju#`obpp>!?p`qjsw#pq`>!kwws9,,!#qfo>!pwzofpkffw!#w?,gju=\t?p`qjsw#wzsf>,b=#?b#kqfe>!kwws9,,#booltWqbmpsbqfm`z>![.VB.@lnsbwjaof!#`lmqfobwjlmpkjs#afwtffm\t?,p`qjsw=\x0e\t?p`qjsw#?,b=?,oj=?,vo=?,gju=bppl`jbwfg#tjwk#wkf#sqldqbnnjmd#obmdvbdf?,b=?b#kqfe>!kwws9,,?,b=?,oj=?oj#`obpp>!elqn#b`wjlm>!kwws9,,?gju#pwzof>!gjpsobz9wzsf>!wf{w!#mbnf>!r!?wbaof#tjgwk>!233&!#ab`hdqlvmg.slpjwjlm9!#alqgfq>!3!#tjgwk>!qfo>!pklqw`vw#j`lm!#k5=?vo=?oj=?b#kqfe>!##?nfwb#kwws.frvju>!`pp!#nfgjb>!p`qffm!#qfpslmpjaof#elq#wkf#!#wzsf>!bssoj`bwjlm,!#pwzof>!ab`hdqlvmg.kwno8#`kbqpfw>vwe.;!#booltwqbmpsbqfm`z>!pwzofpkffw!#wzsf>!wf\x0e\t?nfwb#kwws.frvju>!=?,psbm=?psbm#`obpp>!3!#`foopsb`jmd>!3!=8\t?,p`qjsw=\t?p`qjsw#plnfwjnfp#`boofg#wkfglfp#mlw#mf`fppbqjozElq#nlqf#jmelqnbwjlmbw#wkf#afdjmmjmd#le#?"GL@WZSF#kwno=?kwnosbqwj`vobqoz#jm#wkf#wzsf>!kjggfm!#mbnf>!ibubp`qjsw9uljg+3*8!feef`wjufmfpp#le#wkf#bvwl`lnsofwf>!lee!#dfmfqbooz#`lmpjgfqfg=?jmsvw#wzsf>!wf{w!#!=?,p`qjsw=\x0e\t?p`qjswwkqlvdklvw#wkf#tlqog`lnnlm#njp`lm`fswjlmbppl`jbwjlm#tjwk#wkf?,gju=\t?,gju=\t?gju#`gvqjmd#kjp#ojefwjnf/`lqqfpslmgjmd#wl#wkfwzsf>!jnbdf,{.j`lm!#bm#jm`qfbpjmd#mvnafqgjsolnbwj`#qfobwjlmpbqf#lewfm#`lmpjgfqfgnfwb#`kbqpfw>!vwe.;!#?jmsvw#wzsf>!wf{w!#f{bnsofp#jm`ovgf#wkf!=?jnd#pq`>!kwws9,,jsbqwj`jsbwjlm#jm#wkfwkf#fpwbaojpknfmw#le\t?,gju=\t?gju#`obpp>!%bns8maps8%bns8maps8wl#gfwfqnjmf#tkfwkfqrvjwf#gjeefqfmw#eqlnnbqhfg#wkf#afdjmmjmdgjpwbm`f#afwtffm#wkf`lmwqjavwjlmp#wl#wkf`lmeoj`w#afwtffm#wkftjgfoz#`lmpjgfqfg#wltbp#lmf#le#wkf#ejqpwtjwk#ubqzjmd#gfdqffpkbuf#psf`vobwfg#wkbw+gl`vnfmw-dfwFofnfmwsbqwj`jsbwjmd#jm#wkflqjdjmbooz#gfufolsfgfwb#`kbqpfw>!vwe.;!=#wzsf>!wf{w,`pp!#,=\tjmwfq`kbmdfbaoz#tjwknlqf#`olpfoz#qfobwfgpl`jbo#bmg#slojwj`bowkbw#tlvog#lwkfqtjpfsfqsfmgj`vobq#wl#wkfpwzof#wzsf>!wf{w,`ppwzsf>!pvanjw!#mbnf>!ebnjojfp#qfpjgjmd#jmgfufolsjmd#`lvmwqjfp`lnsvwfq#sqldqbnnjmdf`lmlnj`#gfufolsnfmwgfwfqnjmbwjlm#le#wkfelq#nlqf#jmelqnbwjlmlm#pfufqbo#l``bpjlmpslqwvdv/Fp#+Fvqlsfv*VWE.;!#pfwWjnflvw+evm`wjlm+*gjpsobz9jmojmf.aol`h8?jmsvw#wzsf>!pvanjw!#wzsf#>#$wf{w,ibubp`qj?jnd#pq`>!kwws9,,ttt-!#!kwws9,,ttt-t0-lqd,pklqw`vw#j`lm!#kqfe>!!#bvwl`lnsofwf>!lee!#?,b=?,gju=?gju#`obpp>?,b=?,oj=\t?oj#`obpp>!`pp!#wzsf>!wf{w,`pp!#?elqn#b`wjlm>!kwws9,,{w,`pp!#kqfe>!kwws9,,ojmh#qfo>!bowfqmbwf!#\x0e\t?p`qjsw#wzsf>!wf{w,#lm`oj`h>!ibubp`qjsw9+mft#Gbwf*-dfwWjnf+*~kfjdkw>!2!#tjgwk>!2!#Sflsof$p#Qfsvaoj`#le##?b#kqfe>!kwws9,,ttt-wf{w.gf`lqbwjlm9vmgfqwkf#afdjmmjmd#le#wkf#?,gju=\t?,gju=\t?,gju=\tfpwbaojpknfmw#le#wkf#?,gju=?,gju=?,gju=?,g ujftslqwxnjm.kfjdkw9\t?p`qjsw#pq`>!kwws9,,lswjlm=?lswjlm#ubovf>lewfm#qfefqqfg#wl#bp#,lswjlm=\t?lswjlm#ubov?"GL@WZSF#kwno=\t?"..XJmwfqmbwjlmbo#Bjqslqw=\t?b#kqfe>!kwws9,,ttt?,b=?b#kqfe>!kwws9,,t\fTL\fT^\fTE\fT^\fUh\fT{\fTN\roI\ro|\roL\ro{\roO\rov\rot\nAO\x05Gx\bTA\nzk#+\vUm\x05Gx*\fHD\fHS\fH\\\fIa\fHJ\fIk\fHZ\fHM\fHR\fHe\fHD\fH^\fIg\fHM\fHy\fIa\fH[\fIk\fHH\fIa\fH\\\fHp\fHR\fHD\fHy\fHR\fH\\\fIl\fHT\fHn\fH@\fHn\fHK\fHS\fHH\fHT\fIa\fHI\fHR\fHF\fHD\fHR\fHT\fIa\fHY\fIl\fHy\fHR\fH\\\fHT\fHn\fHT\fIa\fHy\fH\\\fHO\fHT\fHR\fHB\fH{\fIa\fH\\\fIl\fHv\fHS\fHs\fIa\fHL\fIg\fHn\fHY\fHS\fHp\fIa\fHr\fHR\fHD\fHi\fHB\fIk\fH\\\fHS\fHy\fHR\fHY\fHS\fHA\fHS\fHD\fIa\fHD\fH{\fHR\fHM\fHS\fHC\fHR\fHm\fHy\fIa\fHC\fIg\fHn\fHy\fHS\fHT\fIm\fH\\\fHy\fIa\fH[\fHR\fHF\fHU\fIm\fHm\fHv\fHH\fIl\fHF\fIa\fH\\\fH@\fHn\fHK\fHD\fHs\fHS\fHF\fIa\fHF\fHO\fIl\fHy\fIa\fH\\\fHS\fHy\fIk\fHs\fHF\fIa\fH\\\fHR\fH\\\fHn\fHA\fHF\fIa\fH\\\fHR\fHF\fIa\fHH\fHB\fHR\fH^\fHS\fHy\fIg\fHn\fH\\\fHG\fHP\fIa\fHH\fHR\fH\\\fHD\fHS\fH\\\fIa\fHB\fHR\fHO\fH^\fHS\fHB\fHS\fHs\fIk\fHMgfp`qjswjlm!#`lmwfmw>!gl`vnfmw-ol`bwjlm-sqlw-dfwFofnfmwpAzWbdMbnf+?"GL@WZSF#kwno=\t?kwno#?nfwb#`kbqpfw>!vwe.;!=9vqo!#`lmwfmw>!kwws9,,-`pp!#qfo>!pwzofpkffw!pwzof#wzsf>!wf{w,`pp!=wzsf>!wf{w,`pp!#kqfe>!t0-lqd,2:::,{kwno!#{nowzsf>!wf{w,ibubp`qjsw!#nfwklg>!dfw!#b`wjlm>!ojmh#qfo>!pwzofpkffw!##>#gl`vnfmw-dfwFofnfmwwzsf>!jnbdf,{.j`lm!#,=`foosbggjmd>!3!#`foops-`pp!#wzsf>!wf{w,`pp!#?,b=?,oj=?oj=?b#kqfe>!!#tjgwk>!2!#kfjdkw>!2!!=?b#kqfe>!kwws9,,ttt-pwzof>!gjpsobz9mlmf8!=bowfqmbwf!#wzsf>!bssoj.,,T0@,,GWG#[KWNO#2-3#foopsb`jmd>!3!#`foosbg#wzsf>!kjggfm!#ubovf>!,b=%maps8?psbm#qlof>!p\t?jmsvw#wzsf>!kjggfm!#obmdvbdf>!IbubP`qjsw!##gl`vnfmw-dfwFofnfmwpAd>!3!#`foopsb`jmd>!3!#zsf>!wf{w,`pp!#nfgjb>!wzsf>$wf{w,ibubp`qjsw$tjwk#wkf#f{`fswjlm#le#zsf>!wf{w,`pp!#qfo>!pw#kfjdkw>!2!#tjgwk>!2!#>$(fm`lgfVQJ@lnslmfmw+?ojmh#qfo>!bowfqmbwf!#\talgz/#wq/#jmsvw/#wf{wnfwb#mbnf>!qlalwp!#`lmnfwklg>!slpw!#b`wjlm>!=\t?b#kqfe>!kwws9,,ttt-`pp!#qfo>!pwzofpkffw!#?,gju=?,gju=?gju#`obppobmdvbdf>!ibubp`qjsw!=bqjb.kjggfm>!wqvf!=.[?qjsw!#wzsf>!wf{w,ibubpo>38~*+*8\t+evm`wjlm+*xab`hdqlvmg.jnbdf9#vqo+,b=?,oj=?oj=?b#kqfe>!k\n\n?oj=?b#kqfe>!kwws9,,bwlq!#bqjb.kjggfm>!wqv=#?b#kqfe>!kwws9,,ttt-obmdvbdf>!ibubp`qjsw!#,lswjlm=\t?lswjlm#ubovf,gju=?,gju=?gju#`obpp>qbwlq!#bqjb.kjggfm>!wqf>+mft#Gbwf*-dfwWjnf+*slqwvdv/Fp#+gl#Aqbpjo*!wf{w,?nfwb#kwws.frvju>!@lmwfqbmpjwjlmbo,,FM!#!kwws9?kwno#{nomp>!kwws9,,ttt.,,T0@,,GWG#[KWNO#2-3#WGWG,{kwno2.wqbmpjwjlmbo,,ttt-t0-lqd,WQ,{kwno2,sf#>#$wf{w,ibubp`qjsw$8?nfwb#mbnf>!gfp`qjswjlmsbqfmwMlgf-jmpfqwAfelqf?jmsvw#wzsf>!kjggfm!#mbip!#wzsf>!wf{w,ibubp`qj+gl`vnfmw*-qfbgz+evm`wjp`qjsw#wzsf>!wf{w,ibubpjnbdf!#`lmwfmw>!kwws9,,VB.@lnsbwjaof!#`lmwfmw>wno8#`kbqpfw>vwe.;!#,=\tojmh#qfo>!pklqw`vw#j`lm?ojmh#qfo>!pwzofpkffw!#?,p`qjsw=\t?p`qjsw#wzsf>>#gl`vnfmw-`qfbwfFofnfm?b#wbqdfw>!\\aobmh!#kqfe>#gl`vnfmw-dfwFofnfmwpAjmsvw#wzsf>!wf{w!#mbnf>b-wzsf#>#$wf{w,ibubp`qjmsvw#wzsf>!kjggfm!#mbnfkwno8#`kbqpfw>vwe.;!#,=gwg!=\t?kwno#{nomp>!kwws.,,T0@,,GWG#KWNO#7-32#WfmwpAzWbdMbnf+$p`qjsw$*jmsvw#wzsf>!kjggfm!#mbn?p`qjsw#wzsf>!wf{w,ibubp!#pwzof>!gjpsobz9mlmf8!=gl`vnfmw-dfwFofnfmwAzJg+>gl`vnfmw-`qfbwfFofnfmw+$#wzsf>$wf{w,ibubp`qjsw$jmsvw#wzsf>!wf{w!#mbnf>!g-dfwFofnfmwpAzWbdMbnf+pmj`bo!#kqfe>!kwws9,,ttt-@,,GWG#KWNO#7-32#Wqbmpjw?pwzof#wzsf>!wf{w,`pp!=\t\t?pwzof#wzsf>!wf{w,`pp!=jlmbo-gwg!=\t?kwno#{nomp>kwws.frvju>!@lmwfmw.Wzsfgjmd>!3!#`foopsb`jmd>!3!kwno8#`kbqpfw>vwe.;!#,=\t#pwzof>!gjpsobz9mlmf8!=??oj=?b#kqfe>!kwws9,,ttt-#wzsf>$wf{w,ibubp`qjsw$=&*&'&^&\x88\u0178\u0c3e&\u01ad&\u0192&)&^&%&'&\x82&P&1&\xb1&3&]&m&u&E&t&C&\xcf&V&V&/&>&6&\u0f76\u177co&p&@&E&M&P&x&@&F&e&\xcc&7&:&(&D&0&C&)&.&F&-&1&(&L&F&1\u025e*\u03ea\u21f3&\u1372&K&;&)&E&H&P&0&?&9&V&\x81&-&v&a&,&E&)&?&=&'&'&B&\u0d2e&\u0503&\u0316*&*8&%&%&&&%,)&\x9a&>&\x86&7&]&F&2&>&J&6&n&2&%&?&\x8e&2&6&J&g&-&0&,&*&J&*&O&)&6&(&<&B&N&.&P&@&2&.&W&M&%\u053c\x84(,(<&,&\u03da&\u18c7&-&,(%&(&%&(\u013b0&X&D&\x81&j&'&J&(&.&B&3&Z&R&h&3&E&E&<\xc6-\u0360\u1ef3&%8?&@&,&Z&@&0&J&,&^&x&_&6&C&6&C\u072c\u2a25&f&-&-&-&-&,&J&2&8&z&8&C&Y&8&-&d&\u1e78\xcc-&7&1&F&7&t&W&7&I&.&.&^&=\u0f9c\u19d3&8(>&/&/&\u077b')'\u1065')'%@/&0&%\u043e\u09c0*&*@&C\u053d\u05d4\u0274\u05eb4\u0dd7\u071a\u04d16\u0d84&/\u0178\u0303Z&*%\u0246\u03ff&\u0134&1\xa8\u04b4\u0174", w, "AAAAKKLLKKKKKJJIHHIHHGGFF"), function (f, w) { if (w.length > 31) throw "sizeBits length must be at most 31"; for (let f = 0; f < 4; ++f)if (0 != w[f]) throw "first 4 must be 0"; let b = N, j = $; j.set(w.subarray(0, 0 + w.length), 0); let l = 0, m = f.length; for (let f = 0; f < w.length; ++f) { b[f] = l; let w = j[f]; if (0 != w) { if (w >= 31) throw "newSizeBits values must be less than 31"; if (l += f << w, l <= 0 || l > m) throw "newSizeBits is inconsistent: overflow" } } for (let f = w.length; f < 32; ++f)b[f] = l; if (l != m) throw "newSizeBits is inconsistent: underflow"; L = f }(f, w) } function ff(f, w) { return f <= w ? f : w } function wf(f, w, b, j) { if (null == f) return -1; let l = ff(f.offset + j, f.data.length), m = l - f.offset; return w.set(f.data.subarray(f.offset, l), b), f.offset += m, m } return function (w, b) { let j = new Z; if (t(j, new f(w)), b) { let f = b.customDictionary; f && function (f, w) { if (1 != f.j) throw "State MUST be freshly initialized"; if (0 == f.Vf && (f.Tf = new Array(16), f.df = new Int32Array(16), f.tf = -1), 15 == f.Vf) throw "Too many dictionary chunks"; f.Tf[f.Vf] = w, f.Vf++, f.vf += w.length, f.df[f.Vf] = f.vf }(j, f) } let l = 0, m = []; for (; ;) { let f = new Int8Array(16384); if (m.push(f), j.nf = f, j.gf = 0, j.lf = 16384, j.mf = 0, D(j), l += j.mf, j.mf < 16384) break } !function (f) { if (0 == f.j) throw "State MUST be initialized"; 11 != f.j && (f.j = 11, null != f.input && (f.input, f.input = null)) }(j); let p = new Int8Array(l), q = 0; for (let f = 0; f < m.length; ++f) { let w = m[f], b = ff(l, q + 16384) - q; b < 16384 ? p.set(w.subarray(0, b), q) : p.set(w, q), q += b } return p } })(); \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/favicon.svg b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/favicon.svg new file mode 100644 index 0000000..66d2c55 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/favicon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/index.html b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/index.html new file mode 100644 index 0000000..a10d5ff --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/index.html @@ -0,0 +1,145 @@ + + + + + + BUTR Crash Report + + + + + +
+
+
+
+
+ Downloading 0/0 +
+
+
File name will appear here...
+
Hang tight, preparing magic...
+
+ + Your browser does not support the HTML5 canvas element. + +
+ + + + \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/main.js b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/main.js new file mode 100644 index 0000000..80b4d1d --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM/wwwroot/main.js @@ -0,0 +1,166 @@ +import { dotnet } from './_framework/dotnet.js' +import { BrotliDecode } from './decode.min.js'; + +let loadedCount = 0; +let totalCount = 0; + +const motivationalMessages = [ + "Fetching resources, hang tight...", + "Almost there, stay with us!", + "Preparing magic...", + "Final touches being applied..." +]; + +// DOM Elements +const canvas = document.getElementById("canvas"); +const loader = document.getElementById('loader'); +const spinner = document.getElementById('spinner'); +const progressBarContainer = document.getElementById('progress-bar-container'); +const progressBar = document.getElementById('progress-bar'); +const progressLabel = document.getElementById('progress-label'); +const fileNameElement = document.getElementById('file-name'); +const motivationalText = document.getElementById('motivational-text'); +let isFinalizing = false; // Tracks whether the spinner is active + +const resetLoader = () => { + isFinalizing = false; // Reset finalization state + + // Reset the progress bar and spinner + progressBar.style.width = `0%`; + progressBarContainer.style.opacity = 1; + progressBarContainer.style.display = 'block'; + spinner.style.opacity = 0; + spinner.style.display = 'none'; + + // Reset text elements + progressLabel.innerText = "Downloading..."; + fileNameElement.innerText = "Loading resources..."; + motivationalText.innerText = "Hang tight, preparing magic..."; +}; + +const updateLoader = (resourceName) => { + const percentLoaded = 10 + (loadedCount * 90.0) / totalCount; + + // If finalizing and new data is starting to download, reset the loader + if (isFinalizing && loadedCount < totalCount) { + resetLoader(); + } + + // Update progress bar width + progressBar.style.width = `${percentLoaded}%`; + progressBarContainer.setAttribute('aria-valuenow', percentLoaded); + + // Update the progress label inside the bar + progressLabel.innerText = `Downloading ${loadedCount}/${totalCount}`; + + // Update the file name below the bar + fileNameElement.innerText = resourceName || "Loading resources..."; + + // Update motivational text below everything + const messageIndex = Math.min( + Math.floor((percentLoaded / 100) * motivationalMessages.length), + motivationalMessages.length - 1 + ); + motivationalText.innerText = motivationalMessages[messageIndex]; + + // Ensure spinner is hidden while downloading + if (!isFinalizing && percentLoaded > 10) { + spinner.style.opacity = 0; + spinner.style.display = 'none'; + progressBarContainer.style.display = 'block'; + } + + // Handle finalization when downloads complete + if (loadedCount === totalCount && !isFinalizing) { + isFinalizing = true; // Mark as finalizing + progressLabel.innerText = "Finalizing..."; + fileNameElement.innerText = "Initializing the Crash Reporter Renderer..."; + motivationalText.innerText = "Almost ready!"; + + // Transition to spinner + setTimeout(() => { + progressBarContainer.style.opacity = 0; // Fade out progress bar + setTimeout(() => { + progressBarContainer.style.display = 'none'; // Hide progress bar + spinner.style.display = 'block'; // Show spinner + setTimeout(() => { + spinner.style.opacity = 1; // Fade in spinner + }, 50); // Ensure spinner visibility before opacity transition + }, 500); // Wait for fade-out to complete + }, 300); // Smooth transition delay + } +}; + +const { setModuleImports, runMain } = await dotnet + .withResourceLoader((type, name, defaultUri, integrity, behavior) => { + if (type === 'dotnetjs') { + return defaultUri; + } + + totalCount++; + + return (async () => { + let response = null; + + if (response === null) { + let responseBrotli = await fetch(`${defaultUri}.br`, { cache: 'no-cache' }); + if (responseBrotli.ok){ + const originalResponseBuffer = await responseBrotli.arrayBuffer(); + const originalResponseArray = new Int8Array(originalResponseBuffer); + const decompressedResponseArray = BrotliDecode(originalResponseArray); + const contentType = type === 'dotnetwasm' ? 'application/wasm' : 'application/octet-stream'; + response = new Response(decompressedResponseArray, {headers: {'content-type': contentType}}); + } + } + + if (response === null) { + let responseGzipped = await fetch(`${defaultUri}.gz`, { cache: 'no-cache' }); + if (responseGzipped.ok) { + const ds = new DecompressionStream("gzip"); + const decompressedStream = responseGzipped.body.pipeThrough(ds); + const contentType = type === 'dotnetwasm' ? 'application/wasm' : 'application/octet-stream'; + response = new Response(decompressedStream, {headers: {'content-type': contentType}}); + } + } + + if (response === null) { + response = await fetch(defaultUri, { cache: 'no-cache' }); + } + + // Update progress + loadedCount++; + updateLoader(name); + + return response; + })(); + }) + .withDiagnosticTracing(false) + .withApplicationArgumentsFromQuery() + .create(); + +dotnet.instance.Module.canvas = canvas; + +setModuleImports('main.js', { + finishedLoading: () => { + loader.style.display = 'none'; // Hide loader when finished + }, + saveFile: (data, filename) => { + const blob = new Blob([data], {type: 'application/octet-stream'}); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + }, + writeClipboard: (data) => { + if (navigator.clipboard && navigator.clipboard.writeText) { + return navigator.clipboard.writeText(data); + } else { + console.warn('Clipboard API not available'); + // Fallback logic + } + }, +}); + +await runMain(); \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui.WASM3/Allocator.cs b/src/BUTR.CrashReport.Renderer.ImGui.WASM3/Allocator.cs new file mode 100644 index 0000000..336946d --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui.WASM3/Allocator.cs @@ -0,0 +1,40 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ImGuiGeneral; + +internal class Allocator : IDisposable +{ + private readonly List _allocated = new(); + + public unsafe void* Alloc(int size) + { + var ptr = NativeMemory.Alloc((UIntPtr) size); + _allocated.Add(new IntPtr(ptr)); + return ptr; + } + + public unsafe T* Alloc(T value) where T : unmanaged + { + var ptr = (T*) NativeMemory.Alloc((UIntPtr) Unsafe.SizeOf()); + _allocated.Add(new IntPtr(ptr)); + Unsafe.Write(ptr, value); + return ptr; + } + + public unsafe T* Alloc(ReadOnlySpan value) where T : unmanaged + { + var ptr = (T*) NativeMemory.Alloc((UIntPtr) Unsafe.SizeOf()); + _allocated.Add(new IntPtr(ptr)); + value.CopyTo(new Span(ptr, value.Length)); + return ptr; + } + + public void Dispose() + { + foreach (var ptr in _allocated) + { + Marshal.FreeHGlobal(ptr); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/BUTR.CrashReport.Renderer.ImGui.csproj b/src/BUTR.CrashReport.Renderer.ImGui/BUTR.CrashReport.Renderer.ImGui.csproj index 01e526d..646d7b5 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/BUTR.CrashReport.Renderer.ImGui.csproj +++ b/src/BUTR.CrashReport.Renderer.ImGui/BUTR.CrashReport.Renderer.ImGui.csproj @@ -1,71 +1,66 @@  - netstandard2.0 + netstandard2.0;net6.0;net8.0;net9.0 enable - preview + latest true - + + enable + + $(DefineConstants);TEXT_EDITOR - - Debug;Release - true - true - true - false + BUTR.CrashReport.Renderer.ImGui + BUTR.CrashReport.Renderer.ImGui + + $(TargetsForTfmSpecificBuildOutput);IncludeProjectReferenceDlls - - - - BUTR.CrashReport.Renderer.ImGui BUTR.CrashReport.Renderer.ImGui - Contains the renderer for creating the crash report + Contains the ImGui renderer for showing the crash report. Requires a backend like 'BUTR.CrashReport.Renderer.ImGui.CImGui' MIT https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png - butr crash report bannerlord + butr crash report imgui - + - - - - - - - - - - - + + - - - - - - - - - + + + + - - + + resources\%(FileName)%(Extension) + - - - $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)BUTR.CrashReport.dll; - $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)BUTR.CrashReport.Decompilers.dll; - $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)BUTR.CrashReport.Models.dll; - $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)cimgui.dll; - + + + + + + + <_TargetFrameworks Remove="netstandard2.0" /> + + + + + <_FrameworksWithSuppressedDependencies Include="netstandard2.0" /> + + + diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Components/FilePicker.cs b/src/BUTR.CrashReport.Renderer.ImGui/Components/FilePicker.cs new file mode 100644 index 0000000..4fc33f4 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Components/FilePicker.cs @@ -0,0 +1,250 @@ +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Memory.Utils; + +using System.Buffers; +using System.Numerics; + +using Utf8StringInterpolation; + +namespace BUTR.CrashReport.Renderer.ImGui.Components; + +internal class FilePicker : IDisposable +{ + private static readonly char[] SearchFilterSplit = new char['|']; + private static readonly Vector2 Child400 = new(400, 400); + + private static readonly Vector2 Zero2 = Vector2.Zero; + private static readonly Vector4 Zero4 = Vector4.Zero; + + private static readonly Dictionary _filePickers = new(); + + public static FilePicker GetPicker(T o, IImGui cmGui, FilePickerMode mode, string startingPath, string? searchFilter = null) + where T : notnull + { + if (File.Exists(startingPath)) + { + startingPath = Path.GetDirectoryName(startingPath)!; + } + else if (string.IsNullOrEmpty(startingPath) || !Directory.Exists(startingPath)) + { + startingPath = Environment.CurrentDirectory; + if (string.IsNullOrEmpty(startingPath)) + startingPath = AppContext.BaseDirectory; + } + + var hashCode = o.GetHashCode(); + if (!_filePickers.TryGetValue(hashCode, out var fp)) + { + var allowedExtensions = searchFilter?.Split(SearchFilterSplit, StringSplitOptions.RemoveEmptyEntries).ToList() ?? []; + fp = new FilePicker(cmGui, startingPath, mode, allowedExtensions); + + + _filePickers.Add(hashCode, fp); + } + + return fp; + } + + public static void RemovePicker(T o) + where T : notnull + { + var hashCode = o.GetHashCode(); + + var picker = _filePickers[hashCode]; + _filePickers.Remove(hashCode); + picker.Dispose(); + } + + + private readonly IImGui _imgui; + private readonly FilePickerMode _mode; + private readonly List _allowedExtensions; + + private string _rootFolder; + private string? _currentParentFolder; + private string _currentFolder; + + private byte[] _currentFolderPathUtf8 = []; + private List<(string, byte[])> _currentFiles = new(); + private List<(string, byte[])> _currentFolders = new(); + + private byte[] _newNameUtf8; + private string? _selectedFile; + + public string? SelectedPath { get; private set; } + + + private FilePicker(IImGui imgui, string startingPath, FilePickerMode mode, List allowedExtensions) + { + _imgui = imgui; + _mode = mode; + _allowedExtensions = allowedExtensions; + + _newNameUtf8 = ArrayPool.Shared.Rent(256); + + _rootFolder = default!; + _currentFolder = default!; + UpdateCurrentFolder(startingPath); + } + + private void UpdateCurrentFolder(string path) + { + _rootFolder = Path.GetPathRoot(path)!; + _currentParentFolder = Path.GetDirectoryName(path); + _currentFolder = path; + + _currentFolderPathUtf8 = Utf8String.Format($"Current Folder: {Path.GetFileName(_rootFolder)}{_currentFolder.Replace(_rootFolder, "")}\0"); + + _selectedFile = null; + + Reload(); + } + + private void Reload() + { + _currentFolders = Directory.EnumerateDirectories(_currentFolder) + .Select(x => (x, Utf8String.Format($"{Path.GetFileName(x)}/"))) + .ToList(); + + if (_mode is FilePickerMode.CreateFile or FilePickerMode.OpenFile) + { + _currentFiles = Directory.EnumerateFiles(_currentFolder) + .Where(x => _allowedExtensions.Count == 0 || _allowedExtensions.Contains(Path.GetExtension(x))) + .Select(x => (x, Utf8String.Format($"{Path.GetFileName(x)}\0"))) + .ToList(); + } + } + + public bool Draw() + { + _imgui.Text(_currentFolderPathUtf8); + var result = false; + + if (_imgui.BeginChild("###Folder\0"u8, in Child400, in Zero4, ImGuiChildFlags.None, ImGuiWindowFlags.None)) + { + if (!string.IsNullOrEmpty(_currentParentFolder) && _currentFolder != _rootFolder) + { + //_imgui.PushStyleColor(ImGuiCol.Text, in Yellow); + var isSelected = false; + if (_imgui.Selectable("../\0"u8, ref isSelected, ImGuiSelectableFlags.NoAutoClosePopups)) + UpdateCurrentFolder(_currentParentFolder!); + //_imgui.PopStyleColor(); + } + + for (var i = 0; i < _currentFolders.Count; i++) + { + var (folderPath, folderNameUtf8) = _currentFolders[i]; + + //_imgui.PushStyleColor(ImGuiCol.Text, in Yellow); + var isSelected = false; + if (_imgui.Selectable(folderNameUtf8, ref isSelected, ImGuiSelectableFlags.NoAutoClosePopups)) + UpdateCurrentFolder(folderPath); + //_imgui.PopStyleColor(); + } + + if (_mode is FilePickerMode.CreateFile or FilePickerMode.OpenFile) + { + for (var i = 0; i < _currentFiles.Count; i++) + { + var (filePath, fileNameUtf8) = _currentFiles[i]; + + var isSelected = string.Equals(_selectedFile, filePath, StringComparison.Ordinal); + if (_imgui.Selectable(fileNameUtf8, ref isSelected, ImGuiSelectableFlags.NoAutoClosePopups)) + _selectedFile = filePath; + + if (_imgui.IsMouseDoubleClicked(0)) + { + result = true; + _imgui.CloseCurrentPopup(); + } + } + } + } + _imgui.EndChild(); + + if (_mode is FilePickerMode.CreateFile) + { + _imgui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1); + if (_imgui.BeginChild("###FolderSub\0"u8, in Zero2, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) + { + _imgui.PopStyleVar(); + _imgui.Text("File Name: \0"u8); + _imgui.SameLine(); + _imgui.InputText(" "u8, _newNameUtf8, ImGuiInputTextFlags.None); + } + else + { + _imgui.PopStyleVar(); + } + _imgui.EndChild(); + } + + + if (_imgui.Button("Cancel\0"u8)) + { + result = false; + _imgui.CloseCurrentPopup(); + } + + if (_mode is FilePickerMode.CreateFile && _newNameUtf8.Any(x => x != 0)) + { + _imgui.SameLine(); + _imgui.Text(" \0"u8); + _imgui.SameLine(); + if (_imgui.Button("Create File\0"u8)) + { + var idxNull = Array.IndexOf(_newNameUtf8, (byte) 0); + SelectedPath = Path.Combine(_currentFolder, Utf8Utils.ToString(_newNameUtf8.AsSpan(0, idxNull)).Trim()); + result = true; + _imgui.CloseCurrentPopup(); + } + } + + if (_mode is FilePickerMode.OpenFile && !string.IsNullOrEmpty(_selectedFile)) + { + _imgui.SameLine(); + _imgui.Text(" \0"u8); + _imgui.SameLine(); + if (_imgui.Button("Open File\0"u8)) + { + SelectedPath = _selectedFile; + result = true; + _imgui.CloseCurrentPopup(); + } + } + + if (_mode is FilePickerMode.CreateFolder && _newNameUtf8.Any(x => x != 0)) + { + _imgui.SameLine(); + _imgui.Text(" \0"u8); + _imgui.SameLine(); + if (_imgui.Button("Create Folder\0"u8)) + { + SelectedPath = _currentFolder; + result = true; + _imgui.CloseCurrentPopup(); + } + } + if (_mode is FilePickerMode.OpenFolder) + { + _imgui.SameLine(); + _imgui.Text(" \0"u8); + _imgui.SameLine(); + if (_imgui.Button("Open Current Folder\0"u8)) + { + SelectedPath = _currentFolder; + result = true; + _imgui.CloseCurrentPopup(); + } + } + + return result; + } + + public void Dispose() + { + ArrayPool.Shared.Return(_newNameUtf8); + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Components/FilePickerMode.cs b/src/BUTR.CrashReport.Renderer.ImGui/Components/FilePickerMode.cs new file mode 100644 index 0000000..f369dea --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Components/FilePickerMode.cs @@ -0,0 +1,9 @@ +namespace BUTR.CrashReport.Renderer.ImGui.Components; + +internal enum FilePickerMode +{ + CreateFile, + CreateFolder, + OpenFile, + OpenFolder, +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Input.cs b/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Input.cs deleted file mode 100644 index 1d5411b..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Input.cs +++ /dev/null @@ -1,152 +0,0 @@ -using ImGuiNET; - -using Silk.NET.Input; -using Silk.NET.Maths; - -using System.Numerics; - -namespace BUTR.CrashReport.Renderer.ImGui.Controller; - -partial class ImGuiController -{ - private void WindowResized(Vector2D size) - { - _windowsWidth = (uint) size.X; - _windowsHeight = (uint) size.Y; - } - - private void KeyboardOnKeyDown(IKeyboard keyboard, Key key, int arg3) - { - /* - _io.KeyCtrl = action == GLFW_PRESS && (mods & GLFW_MOD_CONTROL) != 0; - _io.KeyAlt = action == GLFW_PRESS && (mods & GLFW_MOD_ALT) != 0; - _io.KeyShift = action == GLFW_PRESS && (mods & GLFW_MOD_SHIFT) != 0; - _io.KeySuper = action == GLFW_PRESS && (mods & GLFW_MOD_SUPER) != 0; - */ - - var convertedKey = KeyToImGuiKey(key); - if (convertedKey != ImGuiKey.None) - _io.AddKeyEvent(convertedKey, true); - } - - private void KeyboardOnKeyUp(IKeyboard keyboard, Key key, int arg3) - { - /* - _io.KeyCtrl = action == GLFW_PRESS && (mods & GLFW_MOD_CONTROL) != 0; - _io.KeyAlt = action == GLFW_PRESS && (mods & GLFW_MOD_ALT) != 0; - _io.KeyShift = action == GLFW_PRESS && (mods & GLFW_MOD_SHIFT) != 0; - _io.KeySuper = action == GLFW_PRESS && (mods & GLFW_MOD_SUPER) != 0; - */ - - var convertedKey = KeyToImGuiKey(key); - if (convertedKey != ImGuiKey.None) - _io.AddKeyEvent(convertedKey, false); - } - - private void OnKeyChar(IKeyboard keyboard, char c) - { - _io.AddInputCharacter(c); - } - - private void MouseOnScroll(IMouse mouse, ScrollWheel data) - { - _io.AddMouseWheelEvent(data.X, data.Y); - } - - private void MouseOnMouseDown(IMouse mouse, MouseButton button) - { - if (button is < 0 or > (MouseButton) ImGuiMouseButton.COUNT) - return; - - _io.AddMouseButtonEvent((int) button, true); - } - - private void MouseOnMouseUp(IMouse mouse, MouseButton button) - { - if (button is < 0 or > (MouseButton) ImGuiMouseButton.COUNT) - return; - - _io.AddMouseButtonEvent((int) button, false); - } - - private void MouseOnMouseMove(IMouse mouse, Vector2 pos) - { - var mousePosBackup = _io.MousePos; - - /* - var focused = _glfw.GetWindowAttrib(_windowPtr, WindowAttributeGetter.Focused); - if (!focused) - { - _io.MousePos.X = -float.MaxValue; - _io.MousePos.Y = -float.MaxValue; - return; - } - */ - - if (_io.WantSetMousePos) - { - mouse.Position = mousePosBackup; - } - else - { - _io.MousePos.X = pos.X; - _io.MousePos.Y = pos.Y; - } - } - - - public void Update(double delta) - { - var oldCtx = _imgui.GetCurrentContext(); - if (oldCtx != _context) - { - _imgui.SetCurrentContext(_context); - } - - SetPerFrameImGuiData(delta); - - UpdateMouseCursor(); - - _imgui.NewFrame(); - - if (oldCtx != _context) - { - _imgui.SetCurrentContext(oldCtx); - } - } - - /// - /// Sets per-frame data based on the associated window. - /// This is called by Update(float). - /// - private void SetPerFrameImGuiData(double delta) - { - _io.DisplaySize.X = _windowsWidth; - _io.DisplaySize.Y = _windowsHeight; - - if (_windowsWidth > 0 && _windowsHeight > 0) - { - _io.DisplayFramebufferScale.X = (float) _view.FramebufferSize.X / _windowsWidth; - _io.DisplayFramebufferScale.Y = (float) _view.FramebufferSize.Y / _windowsHeight; - } - - _io.DeltaTime = (float) delta; - } - - private void UpdateMouseCursor() - { - var flag = (_io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0; - if (flag || Mouse.Cursor.CursorMode == CursorMode.Disabled) return; - - var imguiCursor = _imgui.GetMouseCursor(); - if (imguiCursor == ImGuiMouseCursor.None || _io.MouseDrawCursor) - { - Mouse.Cursor.CursorMode = CursorMode.Hidden; - } - else - { - Mouse.Cursor.StandardCursor = _mouseCursors[(int) imguiCursor] != StandardCursor.Default ? _mouseCursors[(int) imguiCursor] : _mouseCursors[(int) ImGuiMouseCursor.Arrow]; - Mouse.Cursor.CursorMode = CursorMode.Normal; - } - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Setup.cs b/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Setup.cs deleted file mode 100644 index b054f63..0000000 Binary files a/src/BUTR.CrashReport.Renderer.ImGui/Controller/ImGuiController.Setup.cs and /dev/null differ diff --git a/src/BUTR.CrashReport.Renderer.ImGui/CrashReportImGui.cs b/src/BUTR.CrashReport.Renderer.ImGui/CrashReportImGui.cs deleted file mode 100644 index 79d9297..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/CrashReportImGui.cs +++ /dev/null @@ -1,118 +0,0 @@ -using BUTR.CrashReport.Interfaces; -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Controller; -using BUTR.CrashReport.Renderer.ImGui.ImGui; -using BUTR.CrashReport.Renderer.ImGui.Renderer; - -using Silk.NET.Core.Loader; -using Silk.NET.Input; -using Silk.NET.Input.Glfw; -using Silk.NET.OpenGL; -using Silk.NET.Windowing; -using Silk.NET.Windowing.Glfw; - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace BUTR.CrashReport.Renderer.ImGui; - -public class CrashReportImGui -{ - static CrashReportImGui() - { - GlfwInput.RegisterPlatform(); - GlfwWindowing.RegisterPlatform(); - } - - public static void ShowAndWait(Exception exception, IList logSources, Dictionary additionalMetadata, - ICrashReportMetadataProvider crashReportMetadataProvider, - IStacktraceFilter stacktraceFilter, - IAssemblyUtilities assemblyUtilities, - IModuleProvider moduleProvider, - ILoaderPluginProvider loaderPluginProvider, - IPatchProvider patchProvider, - /* - ICommonProvider commonProvider, - IMonoModProvider monoModProvider, - IHarmonyProvider harmonyProvider, - */ - IModelConverter modelConverter, - IPathAnonymizer pathAnonymizer, - ICrashReportRendererUtilities crashReportRendererUtilities) - { - var crashReport = CrashReportInfo.Create(exception, additionalMetadata, stacktraceFilter, assemblyUtilities, moduleProvider, loaderPluginProvider, patchProvider); - - var crashReportModel = CrashReportInfo.ToModel(crashReport, crashReportMetadataProvider, modelConverter, moduleProvider, loaderPluginProvider, assemblyUtilities, pathAnonymizer); - - ShowAndWait(crashReportModel, logSources, crashReportRendererUtilities); - } - - public static void ShowAndWait(CrashReportModel crashReportModel, IList logSources, ICrashReportRendererUtilities crashReportRendererUtilities) - { - if (PathResolver.Default is DefaultPathResolver pr) - pr.Resolvers = [path => crashReportRendererUtilities.GetNativeLibrariesFolderPath().Select(x => Path.Combine(x, path))]; - - var window = Window.Create(WindowOptions.Default with - { - Title = $"{crashReportModel.Metadata.GameName} Crash Report", - VSync = true, - }); - - var gl = default(GL)!; // Disposed in Controller - var controller = default(ImGuiController)!; - var imGuiRenderer = default(ImGuiRenderer)!; - - window.Load += () => - { - window.SetDefaultIcon(); - - var imgui = new CmGui(); // Disposed in Controller - var inputContext = window.CreateInput(); // Disposed in Controller - - gl = window.CreateOpenGL(); // Disposed in Controller - controller = new ImGuiController(imgui, gl, window, inputContext); - imGuiRenderer = new ImGuiRenderer(imgui, crashReportModel, logSources, crashReportRendererUtilities, window.Close); - - controller.Init(); - }; - window.FramebufferResize += s => - { - gl.Viewport(s); - }; - window.Render += delta => - { - if (window.IsClosing) return; - - controller.Update(delta); - - gl.Clear(ClearBufferMask.ColorBufferBit); - - imGuiRenderer.Render(); - - controller.Render(); - }; - - DoLoop(window); - - controller.Dispose(); - window.Dispose(); - } - - private static void DoLoop(IWindow window) - { - window.Initialize(); - window.Run(() => - { - window.DoEvents(); - - if (!window.IsClosing) - window.DoUpdate(); - - if (!window.IsClosing) - window.DoRender(); - }); - window.DoEvents(); - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/CrashReportRendererCapabilities.cs b/src/BUTR.CrashReport.Renderer.ImGui/CrashReportRendererCapabilities.cs new file mode 100644 index 0000000..50cfe29 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/CrashReportRendererCapabilities.cs @@ -0,0 +1,16 @@ +namespace BUTR.CrashReport.Renderer.ImGui; + +[Flags] +public enum CrashReportRendererCapabilities +{ + None = 0, + HasSaveFiles = 1 << 1, + HasScreenshots = 1 << 2, + HasMiniDump = 1 << 3, + CopyAsHtml = 1 << 4, + Upload = 1 << 5, + PluginLoader = 1 << 6, + Logs = 1 << 7, + Dialogs = 1 << 8, + CloseAndContinue = 1 << 9, +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Extensions/AssemblyModelTypeExtensions.cs b/src/BUTR.CrashReport.Renderer.ImGui/Extensions/EnumExtensions.cs similarity index 67% rename from src/BUTR.CrashReport.Renderer.ImGui/Extensions/AssemblyModelTypeExtensions.cs rename to src/BUTR.CrashReport.Renderer.ImGui/Extensions/EnumExtensions.cs index 6f05895..1c1e5c1 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Extensions/AssemblyModelTypeExtensions.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Extensions/EnumExtensions.cs @@ -10,4 +10,7 @@ internal static class AssemblyModelTypeExtensions [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] public static bool IsSet(this AssemblyType self, AssemblyType flag) => (self & flag) == flag; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static bool IsSet(this CrashReportRendererCapabilities self, CrashReportRendererCapabilities flag) => (self & flag) == flag; } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Extensions/RefLinqExtensions.cs b/src/BUTR.CrashReport.Renderer.ImGui/Extensions/RefLinqExtensions.cs deleted file mode 100644 index 0cc7b52..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/Extensions/RefLinqExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using BUTR.CrashReport.Renderer.ImGui.Utils; - -using HonkPerf.NET.RefLinq.Enumerators; - -using System.Collections.Generic; - -namespace BUTR.CrashReport.Renderer.ImGui.Extensions; - -internal static class RefLinqExtensions -{ - public static RefLinqEnumerable> ToRefLinq(this IList c) => new(new(c)); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ICrashReportRendererUtilities.cs b/src/BUTR.CrashReport.Renderer.ImGui/ICrashReportRendererUtilities.cs index 5a56d53..689aa91 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/ICrashReportRendererUtilities.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/ICrashReportRendererUtilities.cs @@ -1,37 +1,19 @@ -using System; -using BUTR.CrashReport.Models; - -using System.Collections.Generic; +using BUTR.CrashReport.Models; namespace BUTR.CrashReport.Renderer.ImGui; -[Flags] -public enum CrashReportRendererCapabilities -{ - None = 0, - SaveAsHtml = 1 << 0, - SaveAsZip = 1 << 1, - CopyAsHtml = 1 << 2, - Upload = 1 << 3, - HasSaveFiles = 1 << 4, - HasScreenshots = 1 << 5, - HasMiniDump = 1 << 6, - PluginLoader = 1 << 7, - Logs = 1 << 8, -} - public interface ICrashReportRendererUtilities { + bool IsDefaultDarkMode { get; } + CrashReportRendererCapabilities Capabilities { get; } - - IEnumerable GetNativeLibrariesFolderPath(); void Upload(CrashReportModel crashReport, ICollection logSources); void CopyAsHtml(CrashReportModel crashReport, ICollection logSources); - // TODO: Right now we rely on the implementation to provide a dialog and do the save - // We should instead do the dialog within ImGui and then provide here the path/stream of where to save the file - void SaveAsHtml(CrashReportModel crashReport, ICollection logSources, bool addMiniDump, bool addLatestSave, bool addScreenshots); - void SaveAsZip(CrashReportModel crashReport, ICollection logSources); + void SaveAsHtml(CrashReportModel crashReport, ICollection logSources, bool addMiniDump, bool addLatestSave, bool addScreenshots, Stream stream); + void SaveAsZip(CrashReportModel crashReport, ICollection logSources, Stream stream); + + Stream SaveFileDialog(string filter, string defaultPath); } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/INativeLoaderUtilities.cs b/src/BUTR.CrashReport.Renderer.ImGui/INativeLoaderUtilities.cs new file mode 100644 index 0000000..3066ce6 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/INativeLoaderUtilities.cs @@ -0,0 +1,6 @@ +namespace BUTR.CrashReport.Renderer.ImGui; + +public interface INativeLoaderUtilities +{ + IEnumerable GetNativeLibrariesFolderPath(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Methods.Strings.cs b/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Methods.Strings.cs deleted file mode 100644 index c718307..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Methods.Strings.cs +++ /dev/null @@ -1,198 +0,0 @@ -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; - -using ImGuiNET; - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Text; - -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; - -unsafe partial class CmGui -{ - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextSameLine(string value) - { - Text(value); - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool BeginChild(string label, ref readonly Vector2 size, ref readonly Vector4 color, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) - { - PushStyleVar(ImGuiStyleVar.ChildRounding, 5f); - PushStyleColor(ImGuiCol.ChildBg, in color); - var result = BeginChild(label, size, child_flags, window_flags); - PopStyleColor(); - PopStyleVar(); - return result; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool TreeNode(string label, ImGuiTreeNodeFlags flags) => TreeNodeEx(label, flags); - - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Text(string value) - { - var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(value.Length); - var span = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount + 1] : new byte[utf8ByteCount + 1]; - var length = UnsafeHelper.Utf16ToUtf8(value, span); - span[length] = 0; - fixed (byte* ptr = span) - { - igText(ptr); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool SmallButton(string label) - { - PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); - - var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(label.Length); - var span = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount + 1] : new byte[utf8ByteCount + 1]; - var length = UnsafeHelper.Utf16ToUtf8(label, span); - span[length] = 0; - fixed (byte* ptr = span) - { - var result = igSmallButton(ptr) > 0; - PopStyleVar(); - return result; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool SmallButtonSameLine(string label) - { - PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); - var result = SmallButton(label); - PopStyleVar(); - SameLine(0, 0); - return result; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool TreeNode(string label) - { - var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(label.Length); - var span = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount + 1] : new byte[utf8ByteCount + 1]; - var length = UnsafeHelper.Utf16ToUtf8(label, span); - span[length] = 0; - fixed (byte* ptr = span) - { - return igTreeNode_Str(ptr) > 0; - } - } - - public bool TreeNodeEx(string label, ImGuiTreeNodeFlags flags) - { - var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(label.Length); - var span = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount + 1] : new byte[utf8ByteCount + 1]; - var length = UnsafeHelper.Utf16ToUtf8(label, span); - span[length] = 0; - fixed (byte* ptr = span) - { - return igTreeNodeEx_Str(ptr, flags) > 0; - } - } - - public bool BeginChild(string str_id, Vector2 size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) - { - var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(str_id.Length); - var span = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount + 1] : new byte[utf8ByteCount + 1]; - var length = UnsafeHelper.Utf16ToUtf8(str_id, span); - span[length] = 0; - fixed (byte* ptr = span) - { - return igBeginChild_Str(ptr, size, child_flags, window_flags) > 0; - } - } - - public bool InputTextMultiline(string label, ref string input, uint maxLength, Vector2 size, ImGuiInputTextFlags flags) - { - return ImGuiNET.ImGui.InputTextMultiline(label, ref input, maxLength, size, flags, null, IntPtr.Zero); - } - - /* - public unsafe bool InputTextMultiline(string label, ref string input, uint maxLength, Vector2 size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, IntPtr user_data) - { - int byteCount1 = Encoding.UTF8.GetByteCount(label); - byte* numPtr1 = byteCount1 <= 2048 ? stackalloc byte[byteCount1 + 1] : Util.Allocate(byteCount1 + 1); - Util.GetUtf8(label, numPtr1, byteCount1); - int byteCount2 = Encoding.UTF8.GetByteCount(input); - int num1 = Math.Max((int) maxLength + 1, byteCount2 + 1); - byte* numPtr2; - byte* numPtr3; - if (num1 > 2048) - { - numPtr2 = Util.Allocate(num1); - numPtr3 = Util.Allocate(num1); - } - else - { - numPtr2 = stackalloc byte[num1]; - numPtr3 = stackalloc byte[num1]; - } - Util.GetUtf8(input, numPtr2, num1); - uint byteCount3 = (uint) (num1 - byteCount2); - Unsafe.InitBlockUnaligned((void*) (numPtr2 + byteCount2), (byte) 0, byteCount3); - Unsafe.CopyBlock((void*) numPtr3, (void*) numPtr2, (uint) num1); - byte num2 = igInputTextMultiline(numPtr1, numPtr2, (uint) num1, size, flags, callback, user_data.ToPointer()); - if (!Util.AreStringsEqual(numPtr3, num1, numPtr2)) - input = Util.StringFromPtr(numPtr2); - if (byteCount1 > 2048) - Util.Free(numPtr1); - if (num1 > 2048) - { - Util.Free(numPtr2); - Util.Free(numPtr3); - } - return num2 > (byte) 0; - } - */ - - public bool InputText(string label, ref string input, uint maxLength, ImGuiInputTextFlags flags) - { - return ImGuiNET.ImGui.InputText(label, ref input, maxLength, flags, null, IntPtr.Zero); - } - - /* - public unsafe bool InputText(string label, ref string input, uint maxLength, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, IntPtr user_data) - { - int byteCount1 = Encoding.UTF8.GetByteCount(label); - var numPtr1 = byteCount1 <= 2048 ? stackalloc byte[byteCount1 + 1] : new byte[byteCount1 + 1]; - Util.GetUtf8(label, numPtr1, byteCount1); - int byteCount2 = Encoding.UTF8.GetByteCount(input); - int num1 = Math.Max((int) maxLength + 1, byteCount2 + 1); - byte* numPtr2; - byte* numPtr3; - if (num1 > 2048) - { - numPtr2 = Util.Allocate(num1); - numPtr3 = Util.Allocate(num1); - } - else - { - numPtr2 = stackalloc byte[num1]; - numPtr3 = stackalloc byte[num1]; - } - Util.GetUtf8(input, numPtr2, num1); - uint byteCount3 = (uint) (num1 - byteCount2); - Unsafe.InitBlockUnaligned((void*) (numPtr2 + byteCount2), (byte) 0, byteCount3); - Unsafe.CopyBlock((void*) numPtr3, (void*) numPtr2, (uint) num1); - byte num2 = igInputText(numPtr1, numPtr2, (uint) num1, flags, callback, user_data.ToPointer()); - if (!Util.AreStringsEqual(numPtr3, num1, numPtr2)) - input = Util.StringFromPtr(numPtr2); - if (byteCount1 > 2048) - Util.Free(numPtr1); - if (num1 > 2048) - { - Util.Free(numPtr2); - Util.Free(numPtr3); - } - return num2 > (byte) 0; - } - */ -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Methods.cs b/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Methods.cs deleted file mode 100644 index ec04efe..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Methods.cs +++ /dev/null @@ -1,377 +0,0 @@ -using ImGuiNET; - -using System; -using System.Buffers; -using System.Buffers.Text; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; - -unsafe partial class CmGui -{ - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization | AggressiveOptimization)] - public void RenderId(ReadOnlySpan title, string id) - { - Text(title); - SameLine(); - SmallButton(id); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool InputTextMultiline(ReadOnlySpan label, Span input, int lineCount) - { - var buf_size = input.Length; - var size = new Vector2(-1, GetTextLineHeight() * (lineCount + 1)); - var flags = ImGuiInputTextFlags.ReadOnly; - var callback = (ImGuiInputTextCallback?) null; - var user_data = (void*) null; - - PushStyleColor(ImGuiCol.FrameBg, in Zero4); - fixed (byte* labelPtr = label) - fixed (byte* inputPtr = input) - { - var result = igInputTextMultiline(labelPtr, inputPtr, Unsafe.As(ref buf_size), size, flags, callback, user_data); - PopStyleColor(); - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Text(bool fmt) - { - var @true = "true\0"u8; - var @false = "false\0"u8; - Text(fmt ? @true : @false); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Text(ReadOnlySpan fmt) - { - fixed (byte* fmtPtr = fmt) - { - igText(fmtPtr); - } - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextSameLine(bool fmt) - { - var @true = "true\0"u8; - var @false = "false\0"u8; - Text(fmt ? @true : @false); - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextSameLine(int value) - { - Span valueUtf8 = stackalloc byte[sizeof(int) * sizeof(char) + 1]; - Utf8Formatter.TryFormat(value, valueUtf8, out _); - valueUtf8[valueUtf8.Length - 1] = 0; - Text(valueUtf8); - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextSameLine(ref readonly DateTimeOffset value) - { - Span valueUtf8 = stackalloc byte[64]; - Utf8Formatter.TryFormat(value, valueUtf8, out var written, new StandardFormat('O')); - valueUtf8[written] = 0; - Text(valueUtf8.Slice(0, written)); - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextSameLine(ReadOnlySpan fmt) - { - fixed (byte* fmtPtr = fmt) - { - igText(fmtPtr); - } - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PadRight(int toAppend) - { - Span padding = stackalloc byte[toAppend + 1]; - padding.Fill((byte) ' '); - padding[toAppend] = 0; - Text(padding); - SameLine(0, 0); - } - public void TextSameLine(ref readonly byte fmt) - { - fixed (byte* fmtPtr = &fmt) - { - igText(fmtPtr); - } - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextWrapped(ReadOnlySpan fmt) - { - fixed (byte* fmtPtr = fmt) - { - igTextWrapped(fmtPtr); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextColored(ref readonly Vector4 col, ReadOnlySpan fmt) - { - fixed (byte* fmtPtr = fmt) - { - igTextColored(col, fmtPtr); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TextColoredSameLine(ref readonly Vector4 col, ReadOnlySpan fmt) - { - fixed (byte* fmtPtr = fmt) - { - igTextColored(col, fmtPtr); - } - SameLine(0, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool Checkbox(ReadOnlySpan label, ref bool v) - { - PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f); - PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); - var num1 = (byte) (v ? 1 : 0); - fixed (byte* labelPtr = label) - { - var result = igCheckbox(labelPtr, &num1); - PopStyleVar(2); - v = num1 > 0; - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool CheckboxSameLine(ReadOnlySpan label, ref bool v) - { - PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f); - PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); - var num1 = (byte) (v ? 1 : 0); - fixed (byte* labelPtr = label) - { - var result = igCheckbox(labelPtr, &num1); - PopStyleVar(2); - SameLine(0, 0); - v = num1 > 0; - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool Button(ReadOnlySpan label) - { - fixed (byte* labelPtr = label) - { - var result = igButton(labelPtr, Zero2); - return result > 0; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool Button(ReadOnlySpan label, ref readonly Vector4 color, ref readonly Vector4 hovered, ref readonly Vector4 active) - { - PushStyleColor(ImGuiCol.Button, in color); - PushStyleColor(ImGuiCol.ButtonHovered, in hovered); - PushStyleColor(ImGuiCol.ButtonActive, in active); - fixed (byte* labelPtr = label) - { - var result = igButton(labelPtr, Zero2); - PopStyleColor(3); - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool SmallButton(ReadOnlySpan label) - { - PushStyleVar(ImGuiStyleVar.FrameRounding, 3f); - fixed (byte* labelPtr = label) - { - var result = igSmallButton(labelPtr); - PopStyleVar(); - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool Begin(ReadOnlySpan name, ref readonly Vector4 color, ImGuiWindowFlags flags) - { - PushStyleColor(ImGuiCol.WindowBg, in color); - var p_open = (byte*) null; - fixed (byte* namePtr = name) - { - var result = igBegin(namePtr, p_open, flags); - PopStyleColor(); - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool BeginTable(ReadOnlySpan strId, int column) - { - var flags = ImGuiTableFlags.None; - const float inner_width = 0.0f; - fixed (byte* strIdPtr = strId) - { - var result = igBeginTable(strIdPtr, column, flags, Zero2, inner_width); - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool BeginChild(ReadOnlySpan strId, ref readonly Vector2 size, ref readonly Vector4 color, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) - { - PushStyleVar(ImGuiStyleVar.ChildRounding, 5f); - PushStyleColor(ImGuiCol.ChildBg, in color); - fixed (byte* strIdPtr = strId) - { - var result = igBeginChild_Str(strIdPtr, size, child_flags, window_flags); - PopStyleColor(); - PopStyleVar(); - return result > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool TreeNode(ReadOnlySpan label) - { - fixed (byte* labelPtr = label) - { - return igTreeNode_Str(labelPtr) > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool TreeNode(ReadOnlySpan label, ImGuiTreeNodeFlags flags) - { - fixed (byte* labelPtr = label) - { - return igTreeNodeEx_Str(labelPtr, flags) > 0; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PushStyleColor(ImGuiCol idx, ref readonly Vector4 col) => igPushStyleColor_Vec4(idx, col); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SetNextWindowPos(ref readonly Vector2 pos) => igSetNextWindowPos(pos, ImGuiCond.None, Zero2); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SetNextWindowSize(ref readonly Vector2 size) => igSetNextWindowSize(size, ImGuiCond.None); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SetNextWindowViewport(uint viewport_id) => igSetNextWindowViewport(viewport_id); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public ImGuiViewportPtr GetMainViewport() => new(igGetMainViewport()); - - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void TreePop() => igTreePop(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void NewLine() => igNewLine(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void StyleColorsLight() => igStyleColorsLight(null); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void StyleColorsDark() => igStyleColorsDark(null); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public ImGuiStylePtr GetStyle() => new(igGetStyle()); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SetWindowFontScale(float scale) => igSetWindowFontScale(scale); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void EndChild() => igEndChild(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void End() => igEnd(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public bool TableNextColumn() => igTableNextColumn() > 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SameLine() => igSameLine(0.0f, -1f); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void EndTable() => igEndTable(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Separator() => igSeparator(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PushStyleVar(ImGuiStyleVar idx, float val) => igPushStyleVar_Float(idx, val); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Bullet() => igBullet(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Indent() => igIndent(0.0f); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Unindent() => igUnindent(0.0f); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SameLine(float offset_from_start_x, float spacing) => igSameLine(offset_from_start_x, spacing); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PopStyleVar() => igPopStyleVar(1); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PopStyleVar(int count) => igPopStyleVar(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PopStyleColor() => igPopStyleColor(1); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void PopStyleColor(int count) => igPopStyleColor(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public float GetTextLineHeight() => igGetTextLineHeight(); - - - - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public IntPtr CreateContext() => igCreateContext(null); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public IntPtr GetCurrentContext() => igGetCurrentContext(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void SetCurrentContext(IntPtr ctx) => igSetCurrentContext(ctx); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public ImGuiIOPtr GetIO() => new(this, igGetIO()); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void Render() => igRender(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public ImDrawDataPtr GetDrawData() => new(igGetDrawData()); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void NewFrame() => igNewFrame(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public ImGuiMouseCursor GetMouseCursor() => igGetMouseCursor(); - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public void DestroyContext(IntPtr ctx) => igDestroyContext(ctx); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Native.cs b/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Native.cs deleted file mode 100644 index c7a377f..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/CmGui.Native.cs +++ /dev/null @@ -1,154 +0,0 @@ -using ImGuiNET; - -using Silk.NET.Core.Loader; - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; - -[UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public unsafe delegate int ImGuiInputTextCallback(ImGuiInputTextCallbackData* data); - -unsafe partial class CmGui : IDisposable -{ - private const string LibWindows = "cimgui.dll"; - private const string LibLinux = "libcimgui.so"; - private const string LibOSX = "libcimgui.dylib"; - - /// - /// Returns a function pointer for the OpenGL function with the specified name. - /// - /// The name of the function to lookup. - public delegate IntPtr LoadFunctionHandler(string funcName); - - private readonly delegate* unmanaged[Cdecl] igBegin; - private readonly delegate* unmanaged[Cdecl] igBeginChild_Str; - private readonly delegate* unmanaged[Cdecl] igBeginTable; - private readonly delegate* unmanaged[Cdecl] igBullet; - private readonly delegate* unmanaged[Cdecl] igButton; - private readonly delegate* unmanaged[Cdecl] igCheckbox; - private readonly delegate* unmanaged[Cdecl] igCreateContext; - private readonly delegate* unmanaged[Cdecl] igDestroyContext; - private readonly delegate* unmanaged[Cdecl] igEnd; - private readonly delegate* unmanaged[Cdecl] igEndChild; - private readonly delegate* unmanaged[Cdecl] igEndTable; - private readonly delegate* unmanaged[Cdecl] igGetCurrentContext; - private readonly delegate* unmanaged[Cdecl] igGetDrawData; - private readonly delegate* unmanaged[Cdecl] igGetIO; - private readonly delegate* unmanaged[Cdecl] igGetMainViewport; - private readonly delegate* unmanaged[Cdecl] igGetMouseCursor; - private readonly delegate* unmanaged[Cdecl] igGetStyle; - private readonly delegate* unmanaged[Cdecl] igGetTextLineHeight; - private readonly delegate* unmanaged[Cdecl] igIndent; - private readonly delegate* unmanaged[Cdecl] igInputTextMultiline; - private readonly delegate* unmanaged[Cdecl] igNewFrame; - private readonly delegate* unmanaged[Cdecl] igNewLine; - private readonly delegate* unmanaged[Cdecl] igNextColumn; - private readonly delegate* unmanaged[Cdecl] igPopStyleColor; - private readonly delegate* unmanaged[Cdecl] igPopStyleVar; - private readonly delegate* unmanaged[Cdecl] igPushStyleColor_Vec4; - private readonly delegate* unmanaged[Cdecl] igPushStyleVar_Float; - private readonly delegate* unmanaged[Cdecl] igRender; - private readonly delegate* unmanaged[Cdecl] igSameLine; - private readonly delegate* unmanaged[Cdecl] igSeparator; - private readonly delegate* unmanaged[Cdecl] igSetCurrentContext; - private readonly delegate* unmanaged[Cdecl] igSetNextWindowPos; - private readonly delegate* unmanaged[Cdecl] igSetNextWindowSize; - private readonly delegate* unmanaged[Cdecl] igSetNextWindowViewport; - private readonly delegate* unmanaged[Cdecl] igSetWindowFontScale; - private readonly delegate* unmanaged[Cdecl] igSmallButton; - private readonly delegate* unmanaged[Cdecl] igStyleColorsDark; - private readonly delegate* unmanaged[Cdecl] igStyleColorsLight; - private readonly delegate* unmanaged[Cdecl] igTableNextColumn; - private readonly delegate* unmanaged[Cdecl] igText; - private readonly delegate* unmanaged[Cdecl] igTextColored; - private readonly delegate* unmanaged[Cdecl] igTextWrapped; - private readonly delegate* unmanaged[Cdecl] igTreeNode_Str; - private readonly delegate* unmanaged[Cdecl] igTreeNodeEx_Str; - private readonly delegate* unmanaged[Cdecl] igTreePop; - private readonly delegate* unmanaged[Cdecl] igUnindent; - public readonly delegate* unmanaged[Cdecl] ImGuiIO_AddInputCharacter; - public readonly delegate* unmanaged[Cdecl] ImGuiIO_AddKeyEvent; - public readonly delegate* unmanaged[Cdecl] ImGuiIO_AddMouseButtonEvent; - public readonly delegate* unmanaged[Cdecl] ImGuiIO_AddMouseWheelEvent; - public readonly delegate* unmanaged[Cdecl] ImFontAtlas_GetTexDataAsRGBA32; - public readonly delegate* unmanaged[Cdecl] ImFontAtlas_SetTexID; - - private readonly IntPtr NativeLibrary; - private readonly LibraryLoader LibraryLoader; - - public CmGui() - { - LibraryLoader = LibraryLoader.GetPlatformDefaultLoader(); - NativeLibrary = LibraryLoader.LoadNativeLibrary( - [ - LibWindows, - LibLinux, - LibOSX - ]); - - igBegin = (delegate* unmanaged[Cdecl]) LoadFunction("igBegin"); - igBeginChild_Str = (delegate* unmanaged[Cdecl]) LoadFunction("igBeginChild_Str"); - igBeginTable = (delegate* unmanaged[Cdecl]) LoadFunction("igBeginTable"); - igBullet = (delegate* unmanaged[Cdecl]) LoadFunction("igBullet"); - igButton = (delegate* unmanaged[Cdecl]) LoadFunction("igButton"); - igCheckbox = (delegate* unmanaged[Cdecl]) LoadFunction("igCheckbox"); - igCreateContext = (delegate* unmanaged[Cdecl]) LoadFunction("igCreateContext"); - igDestroyContext = (delegate* unmanaged[Cdecl]) LoadFunction("igDestroyContext"); - igEnd = (delegate* unmanaged[Cdecl]) LoadFunction("igEnd"); - igEndChild = (delegate* unmanaged[Cdecl]) LoadFunction("igEndChild"); - igEndTable = (delegate* unmanaged[Cdecl]) LoadFunction("igEndTable"); - igGetCurrentContext = (delegate* unmanaged[Cdecl]) LoadFunction("igGetCurrentContext"); - igGetDrawData = (delegate* unmanaged[Cdecl]) LoadFunction("igGetDrawData"); - igGetIO = (delegate* unmanaged[Cdecl]) LoadFunction("igGetIO"); - igGetMainViewport = (delegate* unmanaged[Cdecl]) LoadFunction("igGetMainViewport"); - igGetMouseCursor = (delegate* unmanaged[Cdecl]) LoadFunction("igGetMouseCursor"); - igGetStyle = (delegate* unmanaged[Cdecl]) LoadFunction("igGetStyle"); - igGetTextLineHeight = (delegate* unmanaged[Cdecl]) LoadFunction("igGetTextLineHeight"); - igIndent = (delegate* unmanaged[Cdecl]) LoadFunction("igIndent"); - igInputTextMultiline = (delegate* unmanaged[Cdecl]) LoadFunction("igInputTextMultiline"); - igNewFrame = (delegate* unmanaged[Cdecl]) LoadFunction("igNewFrame"); - igNewLine = (delegate* unmanaged[Cdecl]) LoadFunction("igNewLine"); - igNextColumn = (delegate* unmanaged[Cdecl]) LoadFunction("igNextColumn"); - igPopStyleColor = (delegate* unmanaged[Cdecl]) LoadFunction("igPopStyleColor"); - igPopStyleVar = (delegate* unmanaged[Cdecl]) LoadFunction("igPopStyleVar"); - igPushStyleColor_Vec4 = (delegate* unmanaged[Cdecl]) LoadFunction("igPushStyleColor_Vec4"); - igPushStyleVar_Float = (delegate* unmanaged[Cdecl]) LoadFunction("igPushStyleVar_Float"); - igRender = (delegate* unmanaged[Cdecl]) LoadFunction("igRender"); - igSameLine = (delegate* unmanaged[Cdecl]) LoadFunction("igSameLine"); - igSeparator = (delegate* unmanaged[Cdecl]) LoadFunction("igSeparator"); - igSetCurrentContext = (delegate* unmanaged[Cdecl]) LoadFunction("igSetCurrentContext"); - igSetNextWindowPos = (delegate* unmanaged[Cdecl]) LoadFunction("igSetNextWindowPos"); - igSetNextWindowSize = (delegate* unmanaged[Cdecl]) LoadFunction("igSetNextWindowSize"); - igSetNextWindowViewport = (delegate* unmanaged[Cdecl]) LoadFunction("igSetNextWindowViewport"); - igSetWindowFontScale = (delegate* unmanaged[Cdecl]) LoadFunction("igSetWindowFontScale"); - igSmallButton = (delegate* unmanaged[Cdecl]) LoadFunction("igSmallButton"); - igStyleColorsDark = (delegate* unmanaged[Cdecl]) LoadFunction("igStyleColorsDark"); - igStyleColorsLight = (delegate* unmanaged[Cdecl]) LoadFunction("igStyleColorsLight"); - igTableNextColumn = (delegate* unmanaged[Cdecl]) LoadFunction("igTableNextColumn"); - igText = (delegate* unmanaged[Cdecl]) LoadFunction("igText"); - igTextColored = (delegate* unmanaged[Cdecl]) LoadFunction("igTextColored"); - igTextWrapped = (delegate* unmanaged[Cdecl]) LoadFunction("igTextWrapped"); - igTreeNode_Str = (delegate* unmanaged[Cdecl]) LoadFunction("igTreeNode_Str"); - igTreeNodeEx_Str = (delegate* unmanaged[Cdecl]) LoadFunction("igTreeNodeEx_Str"); - igTreePop = (delegate* unmanaged[Cdecl]) LoadFunction("igTreePop"); - igUnindent = (delegate* unmanaged[Cdecl]) LoadFunction("igUnindent"); - ImGuiIO_AddInputCharacter = (delegate* unmanaged[Cdecl]) LoadFunction("ImGuiIO_AddInputCharacter"); - ImGuiIO_AddKeyEvent = (delegate* unmanaged[Cdecl]) LoadFunction("ImGuiIO_AddKeyEvent"); - ImGuiIO_AddMouseButtonEvent = (delegate* unmanaged[Cdecl]) LoadFunction("ImGuiIO_AddMouseButtonEvent"); - ImGuiIO_AddMouseWheelEvent = (delegate* unmanaged[Cdecl]) LoadFunction("ImGuiIO_AddMouseWheelEvent"); - ImFontAtlas_GetTexDataAsRGBA32 = (delegate* unmanaged[Cdecl]) LoadFunction("ImFontAtlas_GetTexDataAsRGBA32"); - ImFontAtlas_SetTexID = (delegate* unmanaged[Cdecl]) LoadFunction("ImFontAtlas_SetTexID"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] - public IntPtr LoadFunction(string function) => LibraryLoader.LoadFunctionPointer(NativeLibrary, function); - - public void Dispose() - { - LibraryLoader.FreeNativeLibrary(NativeLibrary); - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImFontAtlasPtr.cs b/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImFontAtlasPtr.cs deleted file mode 100644 index cac52bc..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImFontAtlasPtr.cs +++ /dev/null @@ -1,33 +0,0 @@ -using ImGuiNET; - -using System; -using System.Runtime.CompilerServices; - -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; - -internal readonly ref struct ImFontAtlasPtr -{ - private readonly CmGui _imgui; - public unsafe ImFontAtlas* NativePtr { get; } - - public unsafe ImFontAtlasPtr(CmGui imgui, ImFontAtlas* nativePtr) - { - _imgui = imgui; - NativePtr = nativePtr; - } - - public unsafe ref IntPtr TexID => ref Unsafe.AsRef(&NativePtr->TexID); - - public unsafe void GetTexDataAsRGBA32(out IntPtr out_pixels, out int out_width, out int out_height) - { - var out_bytes_per_pixel = (int*) null; - fixed (IntPtr* out_pixels1 = &out_pixels) - fixed (int* out_width1 = &out_width) - fixed (int* out_height1 = &out_height) - { - _imgui.ImFontAtlas_GetTexDataAsRGBA32(NativePtr, out_pixels1, out_width1, out_height1, out_bytes_per_pixel); - } - } - - public unsafe void SetTexID(IntPtr id) => _imgui.ImFontAtlas_SetTexID(NativePtr, id); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImGuiIOPtr.cs b/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImGuiIOPtr.cs deleted file mode 100644 index 4d7dfdd..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImGuiIOPtr.cs +++ /dev/null @@ -1,37 +0,0 @@ -using ImGuiNET; - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; - -internal readonly struct ImGuiIOPtr -{ - private readonly CmGui _imgui; - public unsafe ImGuiIO* NativePtr { get; } - - public unsafe ImGuiIOPtr(CmGui imgui, IntPtr nativePtr) - { - _imgui = imgui; - NativePtr = (ImGuiIO*) nativePtr; - } - - public unsafe ref ImGuiConfigFlags ConfigFlags => ref Unsafe.AsRef(&NativePtr->ConfigFlags); - public unsafe ref ImGuiBackendFlags BackendFlags => ref Unsafe.AsRef(&NativePtr->BackendFlags); - public unsafe ref Vector2 DisplaySize => ref Unsafe.AsRef(&NativePtr->DisplaySize); - public unsafe ref float DeltaTime => ref Unsafe.AsRef(&NativePtr->DeltaTime); - public unsafe ImFontAtlasPtr Fonts => new(_imgui, NativePtr->Fonts); - public unsafe ref Vector2 DisplayFramebufferScale => ref Unsafe.AsRef(&NativePtr->DisplayFramebufferScale); - public unsafe ref bool MouseDrawCursor => ref Unsafe.AsRef(&NativePtr->MouseDrawCursor); - public unsafe ref IntPtr GetClipboardTextFn => ref Unsafe.AsRef(&NativePtr->GetClipboardTextFn); - public unsafe ref IntPtr SetClipboardTextFn => ref Unsafe.AsRef(&NativePtr->SetClipboardTextFn); - public unsafe IntPtr ClipboardUserData { get => (IntPtr) NativePtr->ClipboardUserData; set => NativePtr->ClipboardUserData = (void*) value; } - public unsafe ref bool WantSetMousePos => ref Unsafe.AsRef(&NativePtr->WantSetMousePos); - public unsafe ref Vector2 MousePos => ref Unsafe.AsRef(&NativePtr->MousePos); - - public unsafe void AddInputCharacter(uint c) => _imgui.ImGuiIO_AddInputCharacter(NativePtr, c); - public unsafe void AddKeyEvent(ImGuiKey key, bool down) => _imgui.ImGuiIO_AddKeyEvent(NativePtr, key, Unsafe.As(ref down)); - public unsafe void AddMouseButtonEvent(int button, bool down) => _imgui.ImGuiIO_AddMouseButtonEvent(NativePtr, button, Unsafe.As(ref down)); - public unsafe void AddMouseWheelEvent(float wheel_x, float wheel_y) => _imgui.ImGuiIO_AddMouseWheelEvent(NativePtr, wheel_x, wheel_y); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImGuiViewportPtr.cs b/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImGuiViewportPtr.cs deleted file mode 100644 index 412d9f7..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/ImGui/ImGuiViewportPtr.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ImGuiNET; - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace BUTR.CrashReport.Renderer.ImGui.ImGui; - -// Made it ref so it won't be ever on heap -internal readonly ref struct ImGuiViewportPtr -{ - private unsafe ImGuiViewport* NativePtr { get; } - - public unsafe ImGuiViewportPtr(IntPtr nativePtr) => NativePtr = (ImGuiViewport*) nativePtr; - - public unsafe ref uint ID => ref Unsafe.AsRef(&NativePtr->ID); - public unsafe ref Vector2 WorkPos => ref Unsafe.AsRef(&NativePtr->WorkPos); - public unsafe ref Vector2 WorkSize => ref Unsafe.AsRef(&NativePtr->WorkSize); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.00.AdditionalMetadata.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.00.AdditionalMetadata.cs new file mode 100644 index 0000000..e088c02 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.00.AdditionalMetadata.cs @@ -0,0 +1,125 @@ +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Memory.Utils; +using BUTR.CrashReport.Models; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + protected sealed record Utf8KeyValue(byte[] Key, byte[] Value); + protected sealed record Utf8KeyValueList(string Key, List Values); +} + +partial class ImGuiRenderer +{ + private static void InitializeAdditionalMetadata(Dictionary> dict, TKey key, IList metadatas) where TKey : notnull + { + if (!dict.TryGetValue(key, out var metadataDict)) + dict[key] = metadataDict = new(); + + for (var i = 0; i < metadatas.Count; i++) + { + var metadata = metadatas[i]; + if (!metadata.Key.StartsWith("DISPLAY:") || string.IsNullOrEmpty(metadata.Value)) + continue; + + var keyValue = metadata.Key.AsSpan(8); + var groupIdx = keyValue.IndexOf(':'); + + if (keyValue.Length == 0) + continue; + + var groupKey = groupIdx != -1 ? keyValue.Slice(0, groupIdx).ToString() : string.Empty; + var keyUtf8 = groupIdx != -1 ? Utf8Utils.ToUtf8Array(keyValue.Slice(groupIdx + 1)) : Utf8Utils.ToUtf8Array(keyValue); + var valueUtf8 = Utf8Utils.ToUtf8Array(metadata.Value); + + var entry = metadataDict.FirstOrDefault(x => x.Key == groupKey); + if (entry is null) + metadataDict.Add(entry = new(groupKey, new())); + entry.Values.Add(new(keyUtf8, valueUtf8)); + } + } + + private void RenderAdditionalMetadata(Dictionary> dict, TKey key) where TKey : notnull + { + if (!dict.TryGetValue(key, out var groups)) + return; + + var groupsSpan = groups.AsSpan(); + for (var i = 0; i < groupsSpan.Length; i++) + { + var (groupKey, utf8KeyValues) = groupsSpan[i]; + var values = utf8KeyValues.AsSpan(); + + if (groupKey.Length > 0) + { + _imgui.Text(groupKey); + _imgui.SameLine(); + _imgui.Text(":\0"u8); + + for (var j = 0; j < values.Length; j++) + { + var (_, valueUtf8) = values[j]; + _imgui.Bullet(); + _imgui.TextWrapped(valueUtf8); + } + } + else + { + for (var j = 0; j < values.Length; j++) + { + var (keyUtf8, valueUtf8) = values[j]; + _imgui.Text(keyUtf8); + _imgui.SameLine(); + _imgui.Text(": \0"u8); + _imgui.SameLine(); + _imgui.TextWrapped(valueUtf8); + } + } + } + } + + private void RenderAdditionalMetadataSameLine(Dictionary> dict, TKey key) where TKey : notnull + { + if (!dict.TryGetValue(key, out var groups)) + return; + + var groupsSpan = groups.AsSpan(); + for (var i = 0; i < groupsSpan.Length; i++) + { + var (groupKey, utf8KeyValues) = groupsSpan[i]; + var values = utf8KeyValues.AsSpan(); + + if (groupKey.Length > 0) + { + _imgui.Text(groupKey); + _imgui.SameLine(); + _imgui.Text(": \0"u8); + _imgui.SameLine(); + + for (var j = 0; j < values.Length; j++) + { + var (_, valueUtf8) = values[j]; + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.TextWrapped(valueUtf8); + _imgui.SameLine(); + } + } + + for (var j = 0; j < values.Length; j++) + { + var (keyUtf8, valueUtf8) = values[j]; + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(keyUtf8); + _imgui.SameLine(); + _imgui.Text(" - \0"u8); + _imgui.SameLine(); + _imgui.TextWrapped(valueUtf8); + _imgui.SameLine(); + } + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.00.InputTextWithIO.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.00.InputTextWithIO.cs new file mode 100644 index 0000000..e31fef8 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.00.InputTextWithIO.cs @@ -0,0 +1,118 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + private struct UserData + { + public long LabelHash; + } + + //private ImGuiInputTextCallback _inputTextCallback = default!; + private ImGuiInputTextInt64Callback _inputTextCallback = default!; + private long _selectionLabelHash; + private int _selectionStart; + private int _selectionEnd; + private IMemoryOwner? _selectedText; + + //private int InputTextCallback(ImGuiInputTextCallbackData data) + private int InputTextCallback(ImGuiInputTextCallbackInt64Data data) + { + //_selectionLabelHash = data.Data.LabelHash; + _selectionLabelHash = data.Data; + + _selectionStart = data.SelectionStart; + _selectionEnd = data.SelectionEnd; + + return 0; + } + + private void InitializeInputTextWithIO() + { + + _inputTextCallback = InputTextCallback; + } + + private void RenderInputTextWithIO(ReadOnlySpan label, Span input, int lineCount) + { + var labelHash = Hash(label); + + //_imgui.InputTextMultiline(label, input, lineCount, _inputTextCallback, new UserData { LabelHash = labelHash }); + _imgui.InputTextMultilineInt64(label, input, lineCount, _inputTextCallback, labelHash); + if (_imgui.IsItemClicked(ImGuiMouseButton.Left) && labelHash == _selectionLabelHash) + { + _selectedText = null; + } + if (_imgui.IsItemClicked(ImGuiMouseButton.Right) && labelHash == _selectionLabelHash && _selectionStart != _selectionEnd) + { + var min = Math.Min(_selectionStart, _selectionEnd); + var max = Math.Max(_selectionStart, _selectionEnd); + + if (TryFillBuffer(input.Slice(min, max - min), ref _selectedText)) + { + _imgui.OpenPopup("CopyMenu\0"u8, ImGuiPopupFlags.None); + } + } + if ((_imgui.IsKeyDown(ImGuiKey.LeftCtrl) || _imgui.IsKeyDown(ImGuiKey.RightCtrl)) && _imgui.IsKeyPressed(ImGuiKey.C) && labelHash == _selectionLabelHash && _selectionStart != _selectionEnd) + { + var min = Math.Min(_selectionStart, _selectionEnd); + var max = Math.Max(_selectionStart, _selectionEnd); + + if (TryFillBuffer(input.Slice(min, max - min), ref _selectedText)) + { + _imgui.SetClipboardText(_selectedText.Memory.Span); + _selectedText = null; + } + } + + if (_imgui.BeginPopup("CopyMenu\0"u8, ImGuiWindowFlags.NoFocusOnAppearing)) + { + if (_selectedText is null) + { + _imgui.CloseCurrentPopup(); + } + else + { + if (_imgui.IsWindowAppearing()) + _imgui.BringWindowToDisplayFront(_imgui.GetCurrentWindow()); + + if (_imgui.MenuItem("Copy\0"u8)) + { + _imgui.SetClipboardText(_selectedText.Memory.Span); + _selectedText = null; + } + } + + _imgui.EndPopup(); + } + } + + private static bool TryFillBuffer(ReadOnlySpan toCopy, [NotNullWhen(true)] ref IMemoryOwner? buffer) + { + buffer ??= MemoryPool.Shared.Rent(toCopy.Length); + + if (buffer.Memory.Length < toCopy.Length) + { + buffer.Dispose(); + buffer = MemoryPool.Shared.Rent(toCopy.Length); + } + + toCopy.CopyTo(buffer.Memory.Span); + buffer.Memory.Span[toCopy.Length] = 0; + + return true; + } + + private static long Hash(ReadOnlySpan value) + { + var hash = 0L; + for (var i = 0; i < value.Length; i++) + hash = (hash * 397) ^ value[i].GetHashCode(); + return hash; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.01.Summary.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.01.Summary.cs index c3166b5..a26d703 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.01.Summary.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.01.Summary.cs @@ -1,24 +1,47 @@ -namespace BUTR.CrashReport.Renderer.ImGui.Renderer; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Renderer.ImGui.Components; +using BUTR.CrashReport.Renderer.ImGui.Extensions; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { - private static readonly byte[][] _operatingSystemTypeNames = + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly LiteralSpan[] _operatingSystemTypeNames = [ - "Unknown"u8.ToArray(), // Unknown - "Windows"u8.ToArray(), // Windows - "Linux"u8.ToArray(), // Linux - "MacOS"u8.ToArray(), // MacOS - "Windows on Wine"u8.ToArray(), // WindowsWine + "Unknown\0"u8, // Unknown + "Windows\0"u8, // Windows + "Linux\0"u8, // Linux + "MacOS\0"u8, // MacOS + "Windows on Wine\0"u8, // WindowsWine ]; +} + +partial class ImGuiRenderer +{ + private readonly LiteralSpan _filePickerHtmlId = "###FilePickerHtml\0"u8; + private readonly LiteralSpan _filePickerZiplId = "###FilePickerZip\0"u8; + + private FilePickerOnSelected _onCreateHtmlSelected = default!; + private FilePickerOnSelected _onCreateZipSelected = default!; private bool _addScreenshots; private bool _addLatestSave; private bool _addMiniDump; + private void InitializeSummary() + { + _onCreateHtmlSelected = OnCreateHtmlSelected; + _onCreateZipSelected = OnCreateZipSelected; + } + private void RenderSummary() { var capabilities = _crashReportRendererUtilities.Capabilities; - + if (_imgui.BeginTable("Buttons\0"u8, 2)) { _imgui.TableNextColumn(); @@ -27,70 +50,108 @@ private void RenderSummary() _imgui.SetWindowFontScale(1); _imgui.TableNextColumn(); - if (capabilities.HasFlag(CrashReportRendererCapabilities.SaveAsHtml)) + _imgui.CheckboxRound("Dark Mode\0"u8, ref _isDarkMode); + + _imgui.TableNextColumn(); + _imgui.TableNextColumn(); + + if (_imgui.Button("Save Report as HTML\0"u8)) + { + if (!capabilities.IsSet(CrashReportRendererCapabilities.Dialogs)) + { + _imgui.OpenPopup(_filePickerHtmlId, ImGuiPopupFlags.None); + } + else + { + var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "crashreport.html"); + using var stream = _crashReportRendererUtilities.SaveFileDialog("HTML files|*.html|All files (*.*)|*.*", filePath); + if (stream != Stream.Null) + _crashReportRendererUtilities.SaveAsHtml(_crashReport, _logSources, _addScreenshots, _addLatestSave, _addMiniDump, stream); + } + + } + RenderModalPicker(_filePickerHtmlId, FilePickerMode.CreateFile, _onCreateHtmlSelected); + _imgui.SameLine(0.0f, -1.0f); + + if (_imgui.Button("Save Report as ZIP\0"u8)) { - if (_imgui.Button("Save Report as HTML\0"u8)) _crashReportRendererUtilities.SaveAsHtml(_crashReport, _logSources, _addMiniDump, _addLatestSave, _addScreenshots); - _imgui.SameLine(); + if (!capabilities.IsSet(CrashReportRendererCapabilities.Dialogs)) + { + _imgui.OpenPopup(_filePickerZiplId, ImGuiPopupFlags.None); + } + else + { + var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "crashreport.zip"); + using var stream = _crashReportRendererUtilities.SaveFileDialog("ZIP files|*.zip|All files (*.*)|*.*", filePath); + if (stream != Stream.Null) + _crashReportRendererUtilities.SaveAsZip(_crashReport, _logSources, stream); + } } - if (capabilities.HasFlag(CrashReportRendererCapabilities.SaveAsZip)) + _imgui.SameLine(0.0f, -1.0f); + RenderModalPicker(_filePickerZiplId, FilePickerMode.CreateFile, _onCreateZipSelected); + + if (capabilities.IsSet(CrashReportRendererCapabilities.CloseAndContinue)) { - if (_imgui.Button("Save Report as ZIP\0"u8)) _crashReportRendererUtilities.SaveAsZip(_crashReport, _logSources); - _imgui.SameLine(); + if (_imgui.Button("Close Report and Continue\0"u8, in Secondary, in Secondary2, in Secondary3)) _onClose(); } - if (_imgui.Button("Close Report and Continue\0"u8, in Secondary, in Secondary2, in Secondary3)) _onClose(); + _imgui.TableNextColumn(); _imgui.TableNextColumn(); - if (capabilities.HasFlag(CrashReportRendererCapabilities.CopyAsHtml)) + if (capabilities.IsSet(CrashReportRendererCapabilities.CopyAsHtml)) { if (_imgui.Button("Copy as HTML\0"u8)) _crashReportRendererUtilities.CopyAsHtml(_crashReport, _logSources); - _imgui.SameLine(); + _imgui.SameLine(0.0f, -1.0f); } - if (capabilities.HasFlag(CrashReportRendererCapabilities.Upload)) + if (capabilities.IsSet(CrashReportRendererCapabilities.Upload)) { if (_imgui.Button("Upload Report as Permalink\0"u8)) _crashReportRendererUtilities.Upload(_crashReport, _logSources); } - if (capabilities.HasFlag(CrashReportRendererCapabilities.CopyAsHtml | CrashReportRendererCapabilities.Upload)) + if (capabilities.IsSet(CrashReportRendererCapabilities.CopyAsHtml)) { _imgui.TableNextColumn(); _imgui.TableNextColumn(); } - if (capabilities.HasFlag(CrashReportRendererCapabilities.HasScreenshots | CrashReportRendererCapabilities.HasSaveFiles | CrashReportRendererCapabilities.HasMiniDump)) + if (capabilities.IsSet(CrashReportRendererCapabilities.HasScreenshots | CrashReportRendererCapabilities.HasSaveFiles | CrashReportRendererCapabilities.HasMiniDump)) { _imgui.Text("Save Report as HTML Options:\0"u8); _imgui.TableNextColumn(); _imgui.TableNextColumn(); } - if (capabilities.HasFlag(CrashReportRendererCapabilities.HasScreenshots)) + if (capabilities.IsSet(CrashReportRendererCapabilities.HasScreenshots)) { - _imgui.Checkbox("Include Screenshot\0"u8, ref _addScreenshots); + _imgui.CheckboxRound("Include Screenshot\0"u8, ref _addScreenshots); _imgui.TableNextColumn(); _imgui.TableNextColumn(); } - if (capabilities.HasFlag(CrashReportRendererCapabilities.HasSaveFiles)) + if (capabilities.IsSet(CrashReportRendererCapabilities.HasSaveFiles)) { - _imgui.Checkbox("Include Latest Save File\0"u8, ref _addLatestSave); + _imgui.CheckboxRound("Include Latest Save File\0"u8, ref _addLatestSave); _imgui.TableNextColumn(); _imgui.TableNextColumn(); } - if (capabilities.HasFlag(CrashReportRendererCapabilities.HasMiniDump)) + if (capabilities.IsSet(CrashReportRendererCapabilities.HasMiniDump)) { - _imgui.Checkbox("Include Mini Dump\0"u8, ref _addMiniDump); + _imgui.CheckboxRound("Include Mini Dump\0"u8, ref _addMiniDump); } _imgui.EndTable(); } - _imgui.Text("Clicking 'Close Report and Continue' will continue with the Game's error reporting mechanism.\0"u8); + if (capabilities.IsSet(CrashReportRendererCapabilities.CloseAndContinue)) + { + _imgui.Text("Clicking 'Close Report and Continue' will continue with the Game's error reporting mechanism.\0"u8); + } _imgui.Separator(); _imgui.NewLine(); _imgui.SetWindowFontScale(2); - _imgui.TextSameLine(_crashReport.Metadata.GameName ?? string.Empty); + _imgui.Text(_crashReport.Metadata.GameName ?? string.Empty); + _imgui.SameLine(); _imgui.Text(" has encountered a problem!\0"u8); _imgui.SetWindowFontScale(1); @@ -106,19 +167,42 @@ private void RenderSummary() _imgui.NewLine(); - _imgui.TextSameLine("Operating System: \0"u8); - _imgui.TextSameLine(_operatingSystemTypeNames[(int) _crashReport.Metadata.OperatingSystemType]); - _imgui.TextSameLine(" (\0"u8); - _imgui.TextSameLine(_crashReport.Metadata.OperatingSystemVersion ?? string.Empty); + _imgui.Text("Operating System: \0"u8); + _imgui.SameLine(); + _imgui.Text(_operatingSystemTypeNames[(int) _crashReport.Metadata.OperatingSystemType]); + _imgui.SameLine(); + _imgui.Text(" (\0"u8); + _imgui.SameLine(); + _imgui.Text(_crashReport.Metadata.OperatingSystemVersion ?? string.Empty); + _imgui.SameLine(); _imgui.Text(")\0"u8); - _imgui.TextSameLine("Launcher: \0"u8); - _imgui.TextSameLine(_crashReport.Metadata.LauncherType ?? string.Empty); - _imgui.TextSameLine(" (\0"u8); - _imgui.TextSameLine(_crashReport.Metadata.LauncherVersion ?? string.Empty); + _imgui.Text("Launcher: \0"u8); + _imgui.SameLine(); + _imgui.Text(_crashReport.Metadata.LauncherType ?? string.Empty); + _imgui.SameLine(); + _imgui.Text(" (\0"u8); + _imgui.SameLine(); + _imgui.Text(_crashReport.Metadata.LauncherVersion ?? string.Empty); + _imgui.SameLine(); _imgui.Text(")\0"u8); - _imgui.TextSameLine("Runtime: \0"u8); + _imgui.Text("Runtime: \0"u8); + _imgui.SameLine(); _imgui.Text(_crashReport.Metadata.Runtime ?? string.Empty); } + + private void OnCreateHtmlSelected(string filePath) + { + var fs = File.OpenWrite(filePath); + fs.SetLength(0); + _crashReportRendererUtilities.SaveAsHtml(_crashReport, _logSources, _addScreenshots, _addLatestSave, _addMiniDump, fs); + } + + private void OnCreateZipSelected(string filePath) + { + var fs = File.OpenWrite(filePath); + fs.SetLength(0); + _crashReportRendererUtilities.SaveAsZip(_crashReport, _logSources, fs); + } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.02.Exception.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.02.Exception.cs index cfdfb15..00ef2a1 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.02.Exception.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.02.Exception.cs @@ -1,17 +1,30 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Models; -using System; -using System.Linq; -using System.Text; +using Cysharp.Text; + +using Utf8StringInterpolation; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { + protected class ExceptionModelEqualityComparer : IEqualityComparer + { + public static ExceptionModelEqualityComparer Instance { get; } = new(); + public bool Equals(ExceptionModel? x, ExceptionModel? y) => ReferenceEquals(x, y); + public int GetHashCode(ExceptionModel obj) => obj.GetHashCode(); + } + + protected static readonly string[] NewLine = [Environment.NewLine]; +} + +partial class ImGuiRenderer +{ + private readonly Dictionary> _exceptionAdditionalDisplayKeyMetadata = new(ExceptionModelEqualityComparer.Instance); + private byte[][] _exceptionsUtf8 = []; private EnhancedStacktraceFrameModel?[] _stacktracesUtf8 = []; - private byte[][] _levelInputIdUtf8 = []; private int[] _callstackLineCount = []; private void InitializeExceptionRecursively() @@ -21,33 +34,27 @@ private void InitializeExceptionRecursively() while (curr is not null) { level++; + InitializeAdditionalMetadata(_exceptionAdditionalDisplayKeyMetadata, curr, curr.AdditionalMetadata); curr = curr.InnerException; } _exceptionsUtf8 = new byte[level][]; _stacktracesUtf8 = new EnhancedStacktraceFrameModel?[level]; - _levelInputIdUtf8 = Enumerable.Range(0, level).Select(x => - { - var arr = "##stacktrace_00\0"u8.ToArray(); - arr[arr.Length - 2] = x < 10 ? (byte) (x + '0') : (byte) (x / 10 + '0'); - arr[arr.Length - 1] = (byte) (x % 10 + '0'); - return arr; - }).ToArray(); _callstackLineCount = new int[level]; level = 0; curr = _crashReport.Exception; while (curr is not null) { - var callStackLines = curr.CallStack.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries).Select(x => x).ToArray(); + var callStackLines = curr.CallStack.Split(NewLine, StringSplitOptions.RemoveEmptyEntries).Select(x => x).ToArray().AsSpan(); - var sb = new StringBuilder(); + var sb = ZString.CreateUtf8StringBuilder(); for (var i = 0; i < callStackLines.Length; i++) { - sb.Append($"{" ".PadLeft(i > 98 ? 1 : i > 8 ? 2 : 3)}{i + 1}.{callStackLines[i].Trim()}"); + sb.AppendLiteral(Utf8String.Format($"{" ".PadLeft(i > 98 ? 1 : i > 8 ? 2 : 3)}{i + 1}.{callStackLines[i].Trim()}")); if (i < callStackLines.Length - 1) sb.AppendLine(); } - _exceptionsUtf8[level] = UnsafeHelper.ToUtf8Array(sb.ToString()); + _exceptionsUtf8[level] = sb.AsSpan().ToArray(); var fistCallstackLine = callStackLines.Length > 0 ? callStackLines[0].Trim() : string.Empty; _stacktracesUtf8[level] = _crashReport.EnhancedStacktrace.FirstOrDefault(x => fistCallstackLine == $"at {x.FrameDescription}"); @@ -63,6 +70,8 @@ private void RenderExceptionRecursively(ExceptionModel? ex, int level) { if (ex is null) return; + _imgui.PushId(level); + var moduleId = _stacktracesUtf8[level]?.ExecutingMethod.ModuleId ?? "UNKNOWN"; var sourceModuleId = ex.SourceModuleId ?? "UNKNOWN"; @@ -76,23 +85,25 @@ private void RenderExceptionRecursively(ExceptionModel? ex, int level) if (pluginId != "UNKNOWN") _imgui.RenderId("Potential Plugin Id:\0"u8, pluginId); if (sourcePluginId != "UNKNOWN") _imgui.RenderId("Potential Source Plugin Id:\0"u8, sourcePluginId); - _imgui.TextSameLine("Type: \0"u8); + _imgui.Text("Type: \0"u8); + _imgui.SameLine(); _imgui.Text(ex.Type); if (!string.IsNullOrWhiteSpace(ex.Message)) { - _imgui.TextSameLine("Message: \0"u8); + _imgui.Text("Message: \0"u8); + _imgui.SameLine(); _imgui.Text(ex.Message); } if (!string.IsNullOrWhiteSpace(ex.CallStack)) { _imgui.Text("Stacktrace:\0"u8); - _imgui.Indent(); - _imgui.InputTextMultiline(_levelInputIdUtf8[level], _exceptionsUtf8[level], _callstackLineCount[level]); - _imgui.Unindent(); + RenderInputTextWithIO("##stacktrace\0"u8, _exceptionsUtf8[level], _callstackLineCount[level]); } + RenderAdditionalMetadata(_exceptionAdditionalDisplayKeyMetadata, ex); + if (ex.InnerException is not null) { _imgui.Text("Inner Exception:\0"u8); @@ -100,5 +111,7 @@ private void RenderExceptionRecursively(ExceptionModel? ex, int level) RenderExceptionRecursively(ex.InnerException, level + 1); _imgui.Unindent(); } + + _imgui.PopId(); } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.TextBox.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.TextBox.cs new file mode 100644 index 0000000..f3916f2 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.TextBox.cs @@ -0,0 +1,18 @@ +#if !TEXT_EDITOR +using BUTR.CrashReport.Models; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + private readonly Dictionary _methodCodeLinesUtf8 = new(MethodEqualityComparer.Instance); + + private static void SetCodeDictionary(IDictionary methodDict, MethodModel key, CodeType codeType, TValue value) + { + if (!methodDict.TryGetValue(key, out var codeArray)) + methodDict[key] = codeArray = new TValue[(int) CodeType.Native + 1]; + + codeArray[(int) codeType] = value; + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.TextEditor.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.TextEditor.cs new file mode 100644 index 0000000..1ab927d --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.TextEditor.cs @@ -0,0 +1,73 @@ +#if TEXT_EDITOR +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.ImGui.Syntax; + +using ImGuiColorTextEditNet; + +using PrismSharp.Core; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + private readonly Dictionary[]> _methodCodeLinesEditor = new(MethodEqualityComparer.Instance); + + private void SetCodeDictionary(IDictionary[]> methodDict, MethodModel key, CodeType codeType, InstructionsModel? instructions) + { + static void Handle(IEnumerable tokens, GlyphBuilder builder) + { + foreach (var token in tokens) + { + if (token is StringToken stringToken) + { + var type = stringToken.Type; + + if (stringToken.Alias.Length > 0) + { + foreach (var alias in stringToken.Alias.Reverse()) + { + // If there are multiple aliases, we need to return the last one that assigns an actual color + var colorPalette = PrismColorPalette.FromToken(alias); + if (!Equals(colorPalette, ColorPalette.Default)) + { + type = alias; + break; + } + } + } + + builder.Append(stringToken.Content, PrismColorPalette.FromToken(type)); + } + + if (token is StreamToken streamToken) + { + Handle(streamToken.Content, builder); + } + } + } + + if (!methodDict.TryGetValue(key, out var codeArray)) + codeArray = methodDict[key] = new TextEditor[(int) CodeType.Native + 1]; + + var textEditor = codeArray[(int) codeType] = new(_imgui, _imGuiWithImGuiIO, _imGuiWithImDrawList, _imGuiWithImGuiStyle, _imGuiWithImGuiListClipper); + + var builder = new GlyphBuilder(); + var tokens = Prism.Tokenize(string.Join("\n", instructions?.Instructions ?? []), codeType switch + { + CodeType.IL => LanguageGrammars.cil, + CodeType.ILMixed => LanguageGrammars.cil, + CodeType.CSharp => LanguageGrammars.csharp, + CodeType.Native => LanguageGrammars.nasm, + _ => throw new ArgumentOutOfRangeException(nameof(codeType), codeType, null) + }); + Handle(tokens, builder); + textEditor.AddGlyphs(builder.AsSpan()); + + if (instructions?.Highlight is not null) + { + for (var i = instructions.Highlight.StartLine; i <= instructions.Highlight.EndLine; i++) + textEditor.AddExceptionLine(i); + } + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.cs index e91135b..03a27ca 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.03.EnhancedStacktrace.cs @@ -1,114 +1,136 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Memory.Utils; +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.ImGui.Syntax; -using HonkPerf.NET.RefLinq; +using System.Numerics; -using ImGuiNET; - -using System.Collections.Generic; -using System.Runtime.CompilerServices; +using Utf8StringInterpolation; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { - private class IntEqualityComparer : IEqualityComparer + protected class IntEqualityComparer : IEqualityComparer { public static IntEqualityComparer Instance { get; } = new(); public bool Equals(int x, int y) => x == y; public int GetHashCode(int obj) => obj; } - private class MethodSimpleEqualityComparer : IEqualityComparer + protected class MethodEqualityComparer : IEqualityComparer { - public static MethodSimpleEqualityComparer Instance { get; } = new(); - public bool Equals(MethodSimpleModel? x, MethodSimpleModel? y) => ReferenceEquals(x, y); // We can just reference compare here - public int GetHashCode(MethodSimpleModel obj) => obj.GetHashCode(); + public static MethodEqualityComparer Instance { get; } = new(); + public bool Equals(MethodModel? x, MethodModel? y) => ReferenceEquals(x, y); // We can just reference compare here + public int GetHashCode(MethodModel obj) => obj.GetHashCode(); } - private enum CodeType { IL = 0, CSharpILMixed = 1, CSharp = 2, Native = 3 } - private readonly Dictionary _methodIdUtf8 = new(MethodSimpleEqualityComparer.Instance); - private readonly Dictionary _methodCodeLinesUtf8 = new(MethodSimpleEqualityComparer.Instance); - private readonly Dictionary _methodCodeLineCount = new(MethodSimpleEqualityComparer.Instance); + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly LiteralSpan[] _codeTypeNamesUtf8 = + [ + "IL:\0"u8, // CodeType.IL + "IL with C#:\0"u8, // CodeType.CSharpILMixed + "C#:\0"u8, // CodeType.CSharp + "Native:\0"u8, // CodeType.Native + ]; +} + +partial class ImGuiRenderer +{ + private enum CodeType { IL = 0, ILMixed = 1, CSharp = 2, Native = 3 } private readonly Dictionary _offsetsUtf8 = new(IntEqualityComparer.Instance); - private static void SetCodeDictionary(IDictionary methodDict, MethodSimpleModel key, CodeType codeType, TValue value) - { - if (!methodDict.TryGetValue(key, out var codeArray)) - methodDict[key] = (codeArray = new TValue[(int) CodeType.Native + 1]); - - codeArray[Unsafe.As(ref codeType)] = value; - } - private void InitializeCodeLines() { - var colapsingHeaderId = 0; - string GetLabelId(string name) - { - colapsingHeaderId++; - return $"{name}##{colapsingHeaderId}"; - } - void SetupCodeExecuting(MethodExecutingModel? method) { if (method is null) return; SetupCode(method); - SetCodeDictionary(_methodIdUtf8, method, CodeType.Native, UnsafeHelper.ToUtf8Array(GetLabelId("Native:"))); - SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.Native, UnsafeHelper.ToUtf8Array(string.Join("\n", method.NativeInstructions).Trim('\n'))); - SetCodeDictionary(_methodCodeLineCount, method, CodeType.Native, method.NativeInstructions.Count); +#if TEXT_EDITOR + SetCodeDictionary(_methodCodeLinesEditor, method, CodeType.Native, method.NativeInstructions); +#else + SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.Native, Utf8Utils2.Join("\n"u8, method.NativeInstructions.Instructions)); +#endif } - void SetupCode(MethodSimpleModel? method) + void SetupCode(MethodModel? method) { if (method is null) return; - SetCodeDictionary(_methodIdUtf8, method, CodeType.IL, UnsafeHelper.ToUtf8Array(GetLabelId("IL:"))); - SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.IL, UnsafeHelper.ToUtf8Array(string.Join("\n", method.ILInstructions).Trim('\n'))); - SetCodeDictionary(_methodCodeLineCount, method, CodeType.IL, method.ILInstructions.Count); - - SetCodeDictionary(_methodIdUtf8, method, CodeType.CSharpILMixed, UnsafeHelper.ToUtf8Array(GetLabelId("IL with C#:"))); - SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.CSharpILMixed, UnsafeHelper.ToUtf8Array(string.Join("\n", method.CSharpILMixedInstructions).Trim('\n'))); - SetCodeDictionary(_methodCodeLineCount, method, CodeType.CSharpILMixed, method.CSharpILMixedInstructions.Count); - - SetCodeDictionary(_methodIdUtf8, method, CodeType.CSharp, UnsafeHelper.ToUtf8Array(GetLabelId("C#:"))); - SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.CSharp, UnsafeHelper.ToUtf8Array(string.Join("\n", method.CSharpInstructions).Trim('\n'))); - SetCodeDictionary(_methodCodeLineCount, method, CodeType.CSharp, method.CSharpInstructions.Count); +#if TEXT_EDITOR + SetCodeDictionary(_methodCodeLinesEditor, method, CodeType.IL, method.ILInstructions); + SetCodeDictionary(_methodCodeLinesEditor, method, CodeType.ILMixed, method.ILMixedInstructions); + SetCodeDictionary(_methodCodeLinesEditor, method, CodeType.CSharp, method.CSharpInstructions); +#else + SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.IL, Utf8Utils2.Join("\n"u8, method.ILInstructions?.Instructions ?? [])); + SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.ILMixed, Utf8Utils2.Join("\n"u8, method.ILMixedInstructions?.Instructions ?? [])); + SetCodeDictionary(_methodCodeLinesUtf8, method, CodeType.CSharp, Utf8Utils2.Join("\n"u8, method.CSharpInstructions?.Instructions ?? [])); +#endif } for (var i = 0; i < _crashReport.EnhancedStacktrace.Count; i++) { var stacktrace = _crashReport.EnhancedStacktrace[i]; if (stacktrace.ILOffset is not null) - _offsetsUtf8[stacktrace.ILOffset.Value] = UnsafeHelper.ToUtf8Array($"{stacktrace.ILOffset:X4}"); + _offsetsUtf8[stacktrace.ILOffset.Value] = Utf8String.Format($"{stacktrace.ILOffset:X4}"); if (stacktrace.NativeOffset is not null) - _offsetsUtf8[stacktrace.NativeOffset.Value] = UnsafeHelper.ToUtf8Array($"{stacktrace.NativeOffset:X4}"); + _offsetsUtf8[stacktrace.NativeOffset.Value] = Utf8String.Format($"{stacktrace.NativeOffset:X4}"); SetupCodeExecuting(stacktrace.ExecutingMethod); SetupCode(stacktrace.OriginalMethod); for (var j = 0; j < stacktrace.PatchMethods.Count; j++) - { SetupCode(stacktrace.PatchMethods[j]); - } } + +#if TEXT_EDITOR + _onDarkModeChanged += isDarkMode => + { + _imGuiWithImGuiStyle.GetStyle(out var style); + style.GetColors(out var colors); + foreach (var editors in _methodCodeLinesEditor.Values) + { + foreach (var editor in editors) + { + editor.SetPalette(PrismColorPalette.DefaultOrTomorrowNight(isDarkMode, colors)); + } + } + }; +#endif } - private void RenderMethodLines(MethodSimpleModel method, CodeType codeType) + private void RenderMethodLines(MethodModel method, CodeType codeType) { - var id = _methodIdUtf8[method][Unsafe.As(ref codeType)]; - var lines = _methodCodeLinesUtf8[method][Unsafe.As(ref codeType)]; - var lineCount = _methodCodeLineCount[method][Unsafe.As(ref codeType)]; + var codeTypeUtf8 = _codeTypeNamesUtf8[(int) codeType]; + var lineCount = codeType switch + { + CodeType.IL => method.ILInstructions?.Instructions.Count ?? 0, + CodeType.ILMixed => method.ILMixedInstructions?.Instructions.Count ?? 0, + CodeType.CSharp => method.CSharpInstructions?.Instructions.Count ?? 0, + CodeType.Native when method is MethodExecutingModel methodExecuting => methodExecuting.NativeInstructions.Instructions.Count, + _ => throw new ArgumentOutOfRangeException(nameof(codeType), codeType, null), + }; - if (lines.Length == 0 || lineCount == 0) return; + if (lineCount == 0) return; - if (_imgui.TreeNode(id)) + if (_imgui.TreeNode(codeTypeUtf8, ImGuiTreeNodeFlags.None)) { + _imGuiWithImGuiStyle.GetStyle(out var currentStyle); + currentStyle.GetColors(out var colors); _imgui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1); - if (_imgui.BeginChild(id, in Zero2, in Background, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) + if (_imgui.BeginChild(codeTypeUtf8, in Zero2, in colors[ImGuiCol.WindowBg], ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) { _imgui.PopStyleVar(); - _imgui.InputTextMultiline(id, lines, lineCount); +#if TEXT_EDITOR + var sizeDefault = _imgui.GetTextLineHeight(); + var editor = _methodCodeLinesEditor[method][(int) codeType]; + editor.Render(codeTypeUtf8, new Vector2(-1f, (lineCount + 2) * sizeDefault)); +#else + var codeLinesUtf8 = _methodCodeLinesUtf8[method][(int) codeType]; + RenderInputTextWithIO(codeTypeUtf8, codeLinesUtf8, lineCount); +#endif } else { @@ -125,12 +147,12 @@ private void RenderCodeExecuting(MethodExecutingModel? method) RenderCode(method); RenderMethodLines(method, CodeType.Native); } - private void RenderCode(MethodSimpleModel? method) + private void RenderCode(MethodModel? method) { if (method is null) return; RenderMethodLines(method, CodeType.IL); - RenderMethodLines(method, CodeType.CSharpILMixed); + RenderMethodLines(method, CodeType.ILMixed); RenderMethodLines(method, CodeType.CSharp); } @@ -142,26 +164,27 @@ private void RenderEnhancedStacktrace() var moduleId1 = stacktrace.ExecutingMethod.ModuleId ?? "UNKNOWN"; var pluginId1 = stacktrace.ExecutingMethod.LoaderPluginId ?? "UNKNOWN"; + _imgui.PushId(i); + if (_imgui.TreeNode(stacktrace.FrameDescription)) { _imgui.Text("Executing Method:\0"u8); + if (_imgui.TreeNode(stacktrace.ExecutingMethod.MethodFullDescription, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + if (moduleId1 != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId1); + if (pluginId1 != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId1); - _imgui.Bullet(); - _imgui.Indent(); - _imgui.TextSameLine("Method: \0"u8); - _imgui.Text(stacktrace.ExecutingMethod.MethodFullDescription); - - if (moduleId1 != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId1); - if (pluginId1 != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId1); - - _imgui.TextSameLine("Approximate IL Offset: \0"u8); - _imgui.Text(stacktrace.ILOffset is not null ? _offsetsUtf8[stacktrace.ILOffset.Value] : "UNKNOWN\0"u8); - _imgui.TextSameLine("Native Offset: \0"u8); - _imgui.Text(stacktrace.NativeOffset is not null ? _offsetsUtf8[stacktrace.NativeOffset.Value] : "UNKNOWN\0"u8); + _imgui.Text("Approximate IL Offset: \0"u8); + _imgui.SameLine(); + _imgui.Text(stacktrace.ILOffset is not null ? _offsetsUtf8[stacktrace.ILOffset.Value] : "UNKNOWN\0"u8); + _imgui.Text("Native Offset: \0"u8); + _imgui.SameLine(); + _imgui.Text(stacktrace.NativeOffset is not null ? _offsetsUtf8[stacktrace.NativeOffset.Value] : "UNKNOWN\0"u8); - RenderCodeExecuting(stacktrace.ExecutingMethod); + RenderCodeExecuting(stacktrace.ExecutingMethod); - _imgui.Unindent(); + _imgui.TreePop(); + } if (stacktrace.PatchMethods.Count > 0) { @@ -169,56 +192,57 @@ private void RenderEnhancedStacktrace() for (var j = 0; j < stacktrace.PatchMethods.Count; j++) { + _imgui.PushId(j); + var method = stacktrace.PatchMethods[j]; - _imgui.Bullet(); - _imgui.Indent(); - _imgui.TextSameLine("Method: \0"u8); - _imgui.Text(method.MethodFullDescription); - var moduleId2 = method.ModuleId ?? "UNKNOWN"; - var pluginId2 = method.LoaderPluginId ?? "UNKNOWN"; - var harmonyPatchType = method.AdditionalMetadata.ToRefLinq().Where(x => x.Key == "HarmonyPatchType").FirstOrDefault(); + if (_imgui.TreeNode(method.MethodFullDescription, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + var moduleId2 = method.ModuleId ?? "UNKNOWN"; + var pluginId2 = method.LoaderPluginId ?? "UNKNOWN"; - if (moduleId2 != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId2); - if (pluginId2 != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId2); + if (moduleId2 != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId2); + if (pluginId2 != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId2); - _imgui.TextSameLine("Type: \0"u8); - _imgui.Text(harmonyPatchType is not null ? "Harmony\0"u8 : "UNKNOWN\0"u8); + _imgui.Text("Type: \0"u8); + _imgui.SameLine(); + _imgui.Text(method.Provider); + _imgui.SameLine(); + _imgui.Text(" \0"u8); + _imgui.SameLine(); + _imgui.Text(method.Type); - if (harmonyPatchType is not null) - { - _imgui.TextSameLine("Patch Type: \0"u8); - _imgui.Text(harmonyPatchType.Value); - } + RenderCode(method); - RenderCode(method); + _imgui.TreePop(); + } - _imgui.Unindent(); + _imgui.PopId(); } } if (stacktrace.OriginalMethod is not null) { - var moduleId3 = stacktrace.OriginalMethod.ModuleId ?? "UNKNOWN"; - var pluginId3 = stacktrace.OriginalMethod.LoaderPluginId ?? "UNKNOWN"; - _imgui.Text("Original Method:\0"u8); - _imgui.Bullet(); - _imgui.Indent(); - _imgui.TextSameLine("Method: \0"u8); - _imgui.Text(stacktrace.OriginalMethod.MethodFullDescription); + if (_imgui.TreeNode(stacktrace.OriginalMethod.MethodFullDescription, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + var moduleId3 = stacktrace.OriginalMethod.ModuleId ?? "UNKNOWN"; + var pluginId3 = stacktrace.OriginalMethod.LoaderPluginId ?? "UNKNOWN"; - if (moduleId3 != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId3); - if (pluginId3 != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId3); + if (moduleId3 != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId3); + if (pluginId3 != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId3); - RenderCode(stacktrace.OriginalMethod); + RenderCode(stacktrace.OriginalMethod); - _imgui.Unindent(); + _imgui.TreePop(); + } } _imgui.TreePop(); } + + _imgui.PopId(); } } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.04.InvolvedModulesAndPlugins.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.04.InvolvedModulesAndPlugins.cs index 7ee87ac..b6ec8b2 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.04.InvolvedModulesAndPlugins.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.04.InvolvedModulesAndPlugins.cs @@ -1,13 +1,11 @@ -using BUTR.CrashReport.Models; - -using ImGuiNET; - -using System.Collections.Generic; -using System.Linq; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Models; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; -partial class ImGuiRenderer +partial class ImGuiRenderer { private KeyValuePair[] _enhancedStacktraceGroupedByModuleId = []; private KeyValuePair[] _enhancedStacktraceGroupedByLoaderPluginIdId = []; @@ -27,22 +25,52 @@ private void InitializeInvolved() private void RenderInvolvedModules() { - foreach (var kv in _enhancedStacktraceGroupedByModuleId) + var enhancedStacktraceGroupedByModuleIdSpan = CollectionsMarshal>.AsSpan(_enhancedStacktraceGroupedByModuleId); + for (var i = 0; i < enhancedStacktraceGroupedByModuleIdSpan.Length; i++) { - if (_imgui.TreeNode(kv.Key, ImGuiTreeNodeFlags.DefaultOpen)) + var (moduleId, value) = enhancedStacktraceGroupedByModuleIdSpan[i]; + var involvedModules = CollectionsMarshal.AsSpan(value); + + if (_imgui.TreeNode(moduleId, ImGuiTreeNodeFlags.DefaultOpen)) { - _imgui.RenderId("Module Id:\0"u8, kv.Key); + _imgui.RenderId("Module Id:\0"u8, moduleId); - for (var j = 0; j < kv.Value.Length; j++) + var didDirect = false; + for (var j = 0; j < involvedModules.Length; j++) { - var involved = kv.Value[j]; - _imgui.Bullet(); - _imgui.Indent(); - - _imgui.TextSameLine("Frame: \0"u8); - _imgui.Text(involved.EnhancedStacktraceFrameName); + var involved = involvedModules[j]; + if (involved.Type != InvolvedModuleOrPluginType.Direct) + continue; + + if (!didDirect) + { + _imgui.Text("Directly Involved:\0"u8); + didDirect = true; + } + + if (_imgui.TreeNode(involved.EnhancedStacktraceFrameName, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + _imgui.TreePop(); + } + } - _imgui.Unindent(); + var didPatch = false; + for (var j = 0; j < involvedModules.Length; j++) + { + var involved = involvedModules[j]; + if (involved.Type != InvolvedModuleOrPluginType.Patch) + continue; + + if (!didPatch) + { + _imgui.Text("Patches Involved:\0"u8); + didPatch = true; + } + + if (_imgui.TreeNode(involved.EnhancedStacktraceFrameName, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + _imgui.TreePop(); + } } _imgui.TreePop(); @@ -52,22 +80,52 @@ private void RenderInvolvedModules() private void RenderInvolvedPlugins() { - foreach (var kv in _enhancedStacktraceGroupedByLoaderPluginIdId) + var enhancedStacktraceGroupedByLoaderPluginIdIdSpan = CollectionsMarshal>.AsSpan(_enhancedStacktraceGroupedByLoaderPluginIdId); + for (var i = 0; i < enhancedStacktraceGroupedByLoaderPluginIdIdSpan.Length; i++) { - if (_imgui.TreeNode(kv.Key, ImGuiTreeNodeFlags.DefaultOpen)) + var (loaderPluginId, value) = enhancedStacktraceGroupedByLoaderPluginIdIdSpan[i]; + var involvedLoaderPlugins = CollectionsMarshal.AsSpan(value); + + if (_imgui.TreeNode(loaderPluginId, ImGuiTreeNodeFlags.DefaultOpen)) { - _imgui.RenderId("Plugin Id:\0"u8, kv.Key); + _imgui.RenderId("Plugin Id:\0"u8, loaderPluginId); - for (var j = 0; j < kv.Value.Length; j++) + var didDirect = false; + for (var j = 0; j < involvedLoaderPlugins.Length; j++) { - var involved = kv.Value[j]; - _imgui.Bullet(); - _imgui.Indent(); - - _imgui.TextSameLine("Frame: \0"u8); - _imgui.Text(involved.EnhancedStacktraceFrameName); + var involved = involvedLoaderPlugins[j]; + if (involved.Type != InvolvedModuleOrPluginType.Direct) + continue; + + if (!didDirect) + { + _imgui.Text("Directly Involved:\0"u8); + didDirect = true; + } + + if (_imgui.TreeNode(involved.EnhancedStacktraceFrameName, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + _imgui.TreePop(); + } + } - _imgui.Unindent(); + var didPatch = false; + for (var j = 0; j < involvedLoaderPlugins.Length; j++) + { + var involved = involvedLoaderPlugins[j]; + if (involved.Type != InvolvedModuleOrPluginType.Patch) + continue; + + if (!didPatch) + { + _imgui.Text("Patches Involved:\0"u8); + didPatch = true; + } + + if (_imgui.TreeNode(involved.EnhancedStacktraceFrameName, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + _imgui.TreePop(); + } } _imgui.TreePop(); @@ -77,13 +135,10 @@ private void RenderInvolvedPlugins() private void RenderInvolvedModulesAndPlugins() { - if (_enhancedStacktraceGroupedByModuleId.Length > 0 || _enhancedStacktraceGroupedByLoaderPluginIdId.Length > 0) - { - _imgui.Text("From Highest Probability to Lowest:\0"u8); - _imgui.Indent(); - RenderInvolvedModules(); - RenderInvolvedPlugins(); - _imgui.Unindent(); - } + if (_enhancedStacktraceGroupedByModuleId.Length <= 0 && _enhancedStacktraceGroupedByLoaderPluginIdId.Length <= 0) return; + + _imgui.Text("From Highest Probability to Lowest:\0"u8); + RenderInvolvedModules(); + RenderInvolvedPlugins(); } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.05.InstalledModules.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.05.InstalledModules.cs index c4e8d85..d8d9b78 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.05.InstalledModules.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.05.InstalledModules.cs @@ -1,31 +1,50 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Memory.Utils; +using BUTR.CrashReport.Models; -using HonkPerf.NET.RefLinq; +using Cysharp.Text; -using ImGuiNET; - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +using Utf8StringInterpolation; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { - private static readonly byte[][] _dependencyTypeNames = + protected class DependencyMetadataModelEqualityComparer : IEqualityComparer + { + public static DependencyMetadataModelEqualityComparer Instance { get; } = new(); + public bool Equals(DependencyMetadataModel? x, DependencyMetadataModel? y) => ReferenceEquals(x, y); // We can just reference compare here + public int GetHashCode(DependencyMetadataModel obj) => obj.GetHashCode(); + } + protected class ModuleSubModuleModelEqualityComparer : IEqualityComparer + { + public static ModuleSubModuleModelEqualityComparer Instance { get; } = new(); + public bool Equals(ModuleSubModuleModel? x, ModuleSubModuleModel? y) => ReferenceEquals(x, y); // We can just reference compare here + public int GetHashCode(ModuleSubModuleModel obj) => obj.GetHashCode(); + } + + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly LiteralSpan[] _dependencyTypeNames = [ - [], - "Load Before \0"u8.ToArray(), // LoadBefore - "Load After \0"u8.ToArray(), // LoadAfter - "Incompatible \0"u8.ToArray(), // Incompatible + LiteralSpan.Empty, + "Load Before \0"u8, // LoadBefore + "Load After \0"u8, // LoadAfter + "Incompatible \0"u8, // Incompatible ]; + protected static readonly char[] _colonChar = [':']; +} + +partial class ImGuiRenderer +{ private readonly Dictionary _moduleIdUpdateInfoUtf8 = new(StringComparer.Ordinal); private readonly Dictionary> _moduleDependencyTextUtf8 = new(StringComparer.Ordinal); private readonly Dictionary _moduleAdditionalUpdateInfos = new(StringComparer.Ordinal); + private readonly Dictionary> _moduleAdditionalDisplayKeyMetadata = new(StringComparer.Ordinal); + private readonly Dictionary> _dependencyAdditionalDisplayKeyMetadata = new(DependencyMetadataModelEqualityComparer.Instance); + private readonly Dictionary> _subModuleAdditionalDisplayKeyMetadata = new(ModuleSubModuleModelEqualityComparer.Instance); private void InitializeInstalledModules() { @@ -35,28 +54,38 @@ private void InitializeInstalledModules() if (module.UpdateInfo is not null) { - _moduleIdUpdateInfoUtf8[module.Id] = UnsafeHelper.ToUtf8Array(module.UpdateInfo.ToString()); + _moduleIdUpdateInfoUtf8[module.Id] = Utf8Utils.ToUtf8Array(module.UpdateInfo.ToString()); } - var additionalUpdateInfo = module.AdditionalMetadata.FirstOrDefault(x => x.Key == "AdditionalUpdateInfos")?.Value.Split(';').Select(x => x.Split(':') is { Length: 2 } split + var additionalUpdateInfo = module.AdditionalMetadata.FirstOrDefault(x => x.Key == "AdditionalUpdateInfos")?.Value.Split(_colonChar).Select(x => x.Split(_colonChar) is { Length: 2 } split ? new UpdateInfo { Provider = split[0], Value = split[1], } : null).OfType().ToArray() ?? []; - _moduleAdditionalUpdateInfos[module.Id] = additionalUpdateInfo.Select(x => UnsafeHelper.ToUtf8Array(x.ToString())).ToArray(); + _moduleAdditionalUpdateInfos[module.Id] = additionalUpdateInfo.Select(x => Utf8Utils.ToUtf8Array(x.ToString())).ToArray(); for (var j = 0; j < module.DependencyMetadatas.Count; j++) { var dependentModule = module.DependencyMetadatas[j]; - var optional = dependentModule.IsOptional ? " (optional)" : string.Empty; - var version = !string.IsNullOrEmpty(dependentModule.Version) ? $" >= {dependentModule.Version}" : string.Empty; - var versionRange = !string.IsNullOrEmpty(dependentModule.VersionRange) ? $" {dependentModule.VersionRange}" : string.Empty; - var final = $"{optional}{version}{versionRange}"; - SetNestedDictionary(_moduleDependencyTextUtf8, module.Id, dependentModule.ModuleOrPluginId, UnsafeHelper.ToUtf8Array(final)); + var finalBuilder = ZString.CreateUtf8StringBuilder(); + finalBuilder.AppendLiteral(dependentModule.IsOptional ? " (optional)"u8 : []); + finalBuilder.AppendLiteral(!string.IsNullOrEmpty(dependentModule.Version) ? Utf8String.Format($" >= {dependentModule.Version!}") : []); + finalBuilder.AppendLiteral(!string.IsNullOrEmpty(dependentModule.VersionRange) ? Utf8String.Format($" {dependentModule.VersionRange!}") : []); + SetNestedDictionary(_moduleDependencyTextUtf8, module.Id, dependentModule.ModuleOrPluginId, finalBuilder.AsSpan().ToArray()); + + InitializeAdditionalMetadata(_dependencyAdditionalDisplayKeyMetadata, dependentModule, dependentModule.AdditionalMetadata); } + + for (var j = 0; j < module.SubModules.Count; j++) + { + var subModule = module.SubModules[j]; + InitializeAdditionalMetadata(_subModuleAdditionalDisplayKeyMetadata, subModule, subModule.AdditionalMetadata); + } + + InitializeAdditionalMetadata(_moduleAdditionalDisplayKeyMetadata, module.Id, module.AdditionalMetadata); } } @@ -67,12 +96,20 @@ private void RenderDependencies(ModuleModel module) _imgui.Text("Dependencies:\0"u8); for (var i = 0; i < module.DependencyMetadatas.Count; i++) { + _imgui.PushId(i); + var dependentModule = module.DependencyMetadatas[i]; var type = Clamp(dependentModule.Type, DependencyMetadataType.LoadBefore, DependencyMetadataType.Incompatible); _imgui.Bullet(); - _imgui.TextSameLine(_dependencyTypeNames[type]); - _imgui.SmallButtonSameLine(dependentModule.ModuleOrPluginId); + _imgui.Text(_dependencyTypeNames[type]); + _imgui.SameLine(); + _imgui.SmallButtonRound(dependentModule.ModuleOrPluginId); + _imgui.SameLine(); _imgui.Text(_moduleDependencyTextUtf8[module.Id][dependentModule.ModuleOrPluginId]); + + RenderAdditionalMetadata(_dependencyAdditionalDisplayKeyMetadata, dependentModule); + + _imgui.PopId(); } } @@ -98,42 +135,20 @@ private void RenderSubModules(IList moduleSubModules) { var subModule = moduleSubModules[i]; _imgui.Bullet(); - if (_imgui.BeginChild(subModule.Name, in Zero2, in SubModule, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) + var color = _isDarkMode ? DarkSubModule : LightSubModule; + if (_imgui.BeginChild(subModule.Name, in Zero2, in color, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) { - _imgui.TextSameLine("Name: \0"u8); + _imgui.Text("Name: \0"u8); + _imgui.SameLine(); _imgui.Text(subModule.Name); - _imgui.TextSameLine("DLLName: \0"u8); + _imgui.Text("Assembly Entrypoint: \0"u8); + _imgui.SameLine(); _imgui.Text(subModule.AssemblyId?.Name ?? string.Empty); - _imgui.TextSameLine("SubModuleClassType: \0"u8); + _imgui.Text("Entrypoint: \0"u8); + _imgui.SameLine(); _imgui.Text(subModule.Entrypoint); - var firstTag = true; - foreach (var tag in subModule.AdditionalMetadata.ToRefLinq().Where(x => !x.Key.StartsWith("METADATA:"))) - { - if (firstTag) - { - _imgui.Text("Tags:\0"u8); - firstTag = false; - } - - _imgui.Bullet(); - _imgui.TextSameLine(tag.Key); - _imgui.TextSameLine(": \0"u8); - _imgui.Text(tag.Value); - } - - var firstAssembly = true; - foreach (var assembly in subModule.AdditionalMetadata.ToRefLinq().Where(x => x.Key.StartsWith("METADATA:Assembly"))) - { - if (firstAssembly) - { - _imgui.Text("Assemblies:\0"u8); - firstAssembly = false; - } - - _imgui.Bullet(); - _imgui.Text(assembly.Value); - } + RenderAdditionalMetadata(_subModuleAdditionalDisplayKeyMetadata, subModule); } _imgui.EndChild(); @@ -156,9 +171,12 @@ private void RenderAdditionalAssemblies(string moduleId) } _imgui.Bullet(); - _imgui.TextSameLine(assembly.Id.Name); - _imgui.TextSameLine(" (\0"u8); - _imgui.TextSameLine(_assemblyFullNameUtf8[assembly]); + _imgui.Text(assembly.Id.Name); + _imgui.SameLine(); + _imgui.Text(" (\0"u8); + _imgui.SameLine(); + _imgui.Text(_assemblyFullNameUtf8[assembly]); + _imgui.SameLine(); _imgui.Text(")\0"u8); } } @@ -168,13 +186,12 @@ private void RenderInstalledModules() for (var i = 0; i < _crashReport.Modules.Count; i++) { var module = _crashReport.Modules[i]; - var isVortexManaged = module.AdditionalMetadata.ToRefLinq().Where(x => x.Key == "METADATA:MANAGED_BY_VORTEX").FirstOrDefault()?.Value is { } str && bool.TryParse(str, out var val) && val; var color = module switch { - { IsOfficial: true } => OfficialModule, - { IsExternal: true } => ExternalModule, - _ => UnofficialModule, + { IsOfficial: true } => _isDarkMode ? DarkOfficialModule : LightOfficialModule, + { IsExternal: true } => _isDarkMode ? DarkExternalModule : LightExternalModule, + _ => _isDarkMode ? DarkUnofficialModule : LightUnofficialModule, }; if (_imgui.BeginChild(module.Id, in Zero2, in color, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) @@ -182,35 +199,36 @@ private void RenderInstalledModules() if (_imgui.TreeNode(module.Id)) { _imgui.RenderId("Id:\0"u8, module.Id); - _imgui.TextSameLine("Name: \0"u8); + _imgui.Text("Name: \0"u8); + _imgui.SameLine(); _imgui.Text(module.Name); - _imgui.TextSameLine("Version: \0"u8); + _imgui.Text("Version: \0"u8); + _imgui.SameLine(); _imgui.Text(module.Version); - _imgui.TextSameLine("External: \0"u8); + _imgui.Text("External: \0"u8); + _imgui.SameLine(); _imgui.Text(module.IsExternal); - _imgui.TextSameLine("Vortex: \0"u8); - _imgui.Text(isVortexManaged); - _imgui.TextSameLine("Official: \0"u8); + _imgui.Text("Official: \0"u8); + _imgui.SameLine(); _imgui.Text(module.IsOfficial); - _imgui.TextSameLine("Singleplayer: \0"u8); + _imgui.Text("Singleplayer: \0"u8); + _imgui.SameLine(); _imgui.Text(module.IsSingleplayer); - _imgui.TextSameLine("Multiplayer: \0"u8); + _imgui.Text("Multiplayer: \0"u8); + _imgui.SameLine(); _imgui.Text(module.IsMultiplayer); - RenderDependencies(module); - - RenderCapabilities(module.Capabilities); - if (module.Url is not null) { - _imgui.TextSameLine("Url: \0"u8); - if (_imgui.SmallButton(module.Url)) - Process.Start(new ProcessStartInfo(module.Url) { UseShellExecute = true, Verb = "open" }); + _imgui.Text("Url: \0"u8); + _imgui.SameLine(); + _imgui.TextLinkOpenURL(module.Url, module.Url); } if (module.UpdateInfo is not null) { - _imgui.TextSameLine("Update Info: \0"u8); + _imgui.Text("Update Info: \0"u8); + _imgui.SameLine(); _imgui.Text(_moduleIdUpdateInfoUtf8[module.Id]); } @@ -218,11 +236,18 @@ private void RenderInstalledModules() { for (var j = 0; j < _moduleAdditionalUpdateInfos[module.Id].Length; j++) { - _imgui.TextSameLine("Update Info: \0"u8); + _imgui.Text("Update Info: \0"u8); + _imgui.SameLine(); _imgui.Text(_moduleAdditionalUpdateInfos[module.Id][j]); } } + RenderAdditionalMetadata(_moduleAdditionalDisplayKeyMetadata, module.Id); + + RenderDependencies(module); + + RenderCapabilities(module.Capabilities); + RenderSubModules(module.SubModules); RenderAdditionalAssemblies(module.Id); diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.06.LoadedLoaderPlugins.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.06.LoadedLoaderPlugins.cs index 1feceab..45db3a7 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.06.LoadedLoaderPlugins.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.06.LoadedLoaderPlugins.cs @@ -1,18 +1,15 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; - -using ImGuiNET; - -using System; -using System.Collections.Generic; -using System.Linq; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Memory.Utils; +using BUTR.CrashReport.Models; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; -partial class ImGuiRenderer +partial class ImGuiRenderer { private readonly Dictionary _loaderPluginIdUpdateInfoUtf8 = new(StringComparer.Ordinal); private readonly Dictionary _loaderPluginIdAdditionalUpdateInfos = new(StringComparer.Ordinal); + private readonly Dictionary> _loaderPluginAdditionalDisplayKeyMetadata = new(StringComparer.Ordinal); private void InitializeInstalledLoaderPlugins() { @@ -21,7 +18,7 @@ private void InitializeInstalledLoaderPlugins() var loaderPlugin = _crashReport.LoaderPlugins[i]; if (loaderPlugin.UpdateInfo is not null) { - _loaderPluginIdUpdateInfoUtf8[loaderPlugin.Id] = UnsafeHelper.ToUtf8Array(loaderPlugin.UpdateInfo.ToString()); + _loaderPluginIdUpdateInfoUtf8[loaderPlugin.Id] = Utf8Utils.ToUtf8Array(loaderPlugin.UpdateInfo.ToString()); } var additionalUpdateInfo = loaderPlugin.AdditionalMetadata.FirstOrDefault(x => x.Key == "AdditionalUpdateInfos")?.Value.Split(';').Select(x => x.Split(':') is { Length: 2 } split @@ -31,7 +28,9 @@ private void InitializeInstalledLoaderPlugins() Value = split[1], } : null).OfType().ToArray() ?? []; - _loaderPluginIdAdditionalUpdateInfos[loaderPlugin.Id] = additionalUpdateInfo.Select(x => UnsafeHelper.ToUtf8Array(x.ToString())).ToArray(); + _loaderPluginIdAdditionalUpdateInfos[loaderPlugin.Id] = additionalUpdateInfo.Select(x => Utf8Utils.ToUtf8Array(x.ToString())).ToArray(); + + InitializeAdditionalMetadata(_loaderPluginAdditionalDisplayKeyMetadata, loaderPlugin.Id, loaderPlugin.AdditionalMetadata); } } @@ -43,24 +42,28 @@ private void RenderLoadedLoaderPlugins() for (var i = 0; i < _crashReport.LoaderPlugins.Count; i++) { var loaderPlugin = _crashReport.LoaderPlugins[i]; - if (_imgui.BeginChild(loaderPlugin.Id, in Zero2, in Plugin, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) + var color = _isDarkMode ? DarkPlugin : LightPlugin; + if (_imgui.BeginChild(loaderPlugin.Id, in Zero2, in color, ImGuiChildFlags.Border | ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.None)) { if (_imgui.TreeNode(loaderPlugin.Id)) { _imgui.RenderId("Id:\0"u8, loaderPlugin.Id); - _imgui.TextSameLine("Name: \0"u8); + _imgui.Text("Name: \0"u8); + _imgui.SameLine(); _imgui.Text(loaderPlugin.Name); if (!string.IsNullOrEmpty(loaderPlugin.Version)) { - _imgui.TextSameLine("Version: \0"u8); + _imgui.Text("Version: \0"u8); + _imgui.SameLine(); _imgui.Text(loaderPlugin.Version!); } if (loaderPlugin.UpdateInfo is not null) { - _imgui.TextSameLine("Update Info: \0"u8); + _imgui.Text("Update Info: \0"u8); + _imgui.SameLine(); _imgui.Text(_loaderPluginIdUpdateInfoUtf8[loaderPlugin.Id]); } @@ -68,11 +71,14 @@ private void RenderLoadedLoaderPlugins() { for (var j = 0; j < _loaderPluginIdAdditionalUpdateInfos[loaderPlugin.Id].Length; j++) { - _imgui.TextSameLine("Update Info: \0"u8); + _imgui.Text("Update Info: \0"u8); + _imgui.SameLine(); _imgui.Text(_loaderPluginIdAdditionalUpdateInfos[loaderPlugin.Id][j]); } } + RenderAdditionalMetadata(_loaderPluginAdditionalDisplayKeyMetadata, loaderPlugin.Id); + RenderCapabilities(loaderPlugin.Capabilities); _imgui.TreePop(); diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.07.Assemblies.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.07.Assemblies.cs index 3855159..582650a 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.07.Assemblies.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.07.Assemblies.cs @@ -1,64 +1,68 @@ -using BUTR.CrashReport.Extensions; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Memory.Utils; using BUTR.CrashReport.Models; using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; - -using ImGuiNET; - -using System.Collections.Generic; -using System.Linq; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { - private class AssemblyModelEqualityComparer : IEqualityComparer + protected class AssemblyModelEqualityComparer : IEqualityComparer { public static AssemblyModelEqualityComparer Instance { get; } = new(); public bool Equals(AssemblyModel? x, AssemblyModel? y) => ReferenceEquals(x, y); // We can just reference compare here public int GetHashCode(AssemblyModel obj) => obj.GetHashCode(); } - private static bool _hideSystemAssemblies; - private static bool _hideGACAssemblies; - private static bool _hideGameAssemblies; - private static bool _hideGameModulesAssemblies; - private static bool _hideModulesAssemblies; - private static bool _hideLoaderAssemblies; - private static bool _hideLoaderPluginsAssemblies; - private static bool _hideDynamicAssemblies; - private static bool _hideUnclassifiedAssemblies; - - private static bool _hasSystemAssemblies; - private static bool _hasGACAssemblies; - private static bool _hasGameAssemblies; - private static bool _hasGameModulesAssemblies; - private static bool _hasModulesAssemblies; - private static bool _hasLoaderAssemblies; - private static bool _hasLoaderPluginsAssemblies; - private static bool _hasDynamicAssemblies; - private static bool _hasUnclassifiedAssemblies; + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly LiteralSpan[] _architectureTypeNames = + [ + "Unknown\0"u8, // Unknown + "MSIL\0"u8, // MSIL + "x86\0"u8, // X86 + "IA64\0"u8, // IA64 + "x64\0"u8, // Amd64 + "Arm\0"u8, // Arm + ]; +} +partial class ImGuiRenderer +{ private readonly Dictionary _assemblyFullNameUtf8 = new(AssemblyModelEqualityComparer.Instance); + private readonly Dictionary> _assemblyAdditionalDisplayKeyMetadata = new(AssemblyModelEqualityComparer.Instance); - private static readonly byte[][] _architectureTypeNames = - [ - "Unknown"u8.ToArray(), // Unknown - "MSIL"u8.ToArray(), // MSIL - "x86"u8.ToArray(), // X86 - "IA64"u8.ToArray(), // IA64 - "x64"u8.ToArray(), // Amd64 - "Arm"u8.ToArray(), // Arm - ]; + private bool _hideSystemAssemblies; + private bool _hideGACAssemblies; + private bool _hideGameAssemblies; + private bool _hideGameModulesAssemblies; + private bool _hideModulesAssemblies; + private bool _hideLoaderAssemblies; + private bool _hideLoaderPluginsAssemblies; + private bool _hideDynamicAssemblies; + private bool _hideUnclassifiedAssemblies; + + private bool _hasSystemAssemblies; + private bool _hasGACAssemblies; + private bool _hasGameAssemblies; + private bool _hasGameModulesAssemblies; + private bool _hasModulesAssemblies; + private bool _hasLoaderAssemblies; + private bool _hasLoaderPluginsAssemblies; + private bool _hasDynamicAssemblies; + private bool _hasUnclassifiedAssemblies; private void InitializeAssemblies() { for (var i = 0; i < _crashReport.Assemblies.Count; i++) { var assembly = _crashReport.Assemblies[i]; - _assemblyFullNameUtf8[assembly] = UnsafeHelper.ToUtf8Array(assembly.GetFullName()); + _assemblyFullNameUtf8[assembly] = Utf8Utils.ToUtf8Array(assembly.GetFullName()); + + InitializeAdditionalMetadata(_assemblyAdditionalDisplayKeyMetadata, assembly, assembly.AdditionalMetadata); } - + _hasSystemAssemblies = _crashReport.Assemblies.Any(x => x.Type.IsSet(AssemblyType.System)); _hasGACAssemblies = _crashReport.Assemblies.Any(x => x.Type.IsSet(AssemblyType.GAC)); _hasGameAssemblies = _crashReport.Assemblies.Any(x => x.Type.IsSet(AssemblyType.GameCore)); @@ -70,57 +74,119 @@ private void InitializeAssemblies() _hasUnclassifiedAssemblies = _crashReport.Assemblies.Any(x => x.Type == AssemblyType.Unclassified); } - private void RenderAssemblies() + private void RenderAssembliesStep(AssemblyModel assembly) { - _imgui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1); - _imgui.TextSameLine("Hide: \0"u8); - if (_hasSystemAssemblies) _imgui.CheckboxSameLine(" System | \0"u8, ref _hideSystemAssemblies); - if (_hasGACAssemblies) _imgui.CheckboxSameLine(" GAC | \0"u8, ref _hideGACAssemblies); - if (_hasGameAssemblies) _imgui.CheckboxSameLine(" Game | \0"u8, ref _hideGameAssemblies); - if (_hasGameModulesAssemblies) _imgui.CheckboxSameLine(" Game Modules | \0"u8, ref _hideGameModulesAssemblies); - if (_hasModulesAssemblies) _imgui.CheckboxSameLine(" Modules | \0"u8, ref _hideModulesAssemblies); - if (_hasLoaderAssemblies) _imgui.CheckboxSameLine(" Loader | \0"u8, ref _hideLoaderAssemblies); - if (_hasLoaderPluginsAssemblies) _imgui.CheckboxSameLine(" Loader Plugins | \0"u8, ref _hideLoaderPluginsAssemblies); - if (_hasDynamicAssemblies) _imgui.CheckboxSameLine(" Dynamic | \0"u8, ref _hideDynamicAssemblies); - if (_hasUnclassifiedAssemblies) _imgui.Checkbox(" Unclassified \0"u8, ref _hideUnclassifiedAssemblies); - _imgui.PopStyleVar(); + if (_hideSystemAssemblies && assembly.Type.IsSet(AssemblyType.System)) return; + if (_hideGACAssemblies && assembly.Type.IsSet(AssemblyType.GAC)) return; + if (_hideGameAssemblies && assembly.Type.IsSet(AssemblyType.GameCore)) return; + if (_hideGameModulesAssemblies && assembly.Type.IsSet(AssemblyType.GameModule)) return; + if (_hideModulesAssemblies && assembly.Type.IsSet(AssemblyType.Module)) return; + if (_hideLoaderAssemblies && assembly.Type.IsSet(AssemblyType.Loader)) return; + if (_hideLoaderPluginsAssemblies && assembly.Type.IsSet(AssemblyType.LoaderPlugin)) return; + if (_hideDynamicAssemblies && assembly.Type.IsSet(AssemblyType.Dynamic)) return; + if (_hideUnclassifiedAssemblies && assembly.Type == AssemblyType.Unclassified) return; - for (var i = 0; i < _crashReport.Assemblies.Count; i++) + if (_imgui.TreeNode(assembly.Id.Name, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) { - var assembly = _crashReport.Assemblies[i]; - if (_hideSystemAssemblies && assembly.Type.IsSet(AssemblyType.System)) continue; - if (_hideGACAssemblies && assembly.Type.IsSet(AssemblyType.GAC)) continue; - if (_hideGameAssemblies && assembly.Type.IsSet(AssemblyType.GameCore)) continue; - if (_hideGameModulesAssemblies && assembly.Type.IsSet(AssemblyType.GameModule)) continue; - if (_hideModulesAssemblies && assembly.Type.IsSet(AssemblyType.Module)) continue; - if (_hideLoaderAssemblies && assembly.Type.IsSet(AssemblyType.Loader)) continue; - if (_hideLoaderPluginsAssemblies && assembly.Type.IsSet(AssemblyType.LoaderPlugin)) continue; - if (_hideDynamicAssemblies && assembly.Type.IsSet(AssemblyType.Dynamic)) continue; - if (_hideUnclassifiedAssemblies && assembly.Type == AssemblyType.Unclassified) continue; - var isDynamic = assembly.Type.IsSet(AssemblyType.Dynamic); var hasPath = assembly.AnonymizedPath != "EMPTY" && assembly.AnonymizedPath != "DYNAMIC" && !string.IsNullOrWhiteSpace(assembly.AnonymizedPath); - _imgui.Bullet(); - _imgui.TextSameLine(assembly.Id.Name); - _imgui.TextSameLine(", \0"u8); - _imgui.TextSameLine(assembly.Id.Version ?? string.Empty); - _imgui.TextSameLine(", \0"u8); - _imgui.TextSameLine(_architectureTypeNames[(int) assembly.Architecture]); + _imgui.SameLine(); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(assembly.Id.Version ?? string.Empty); + _imgui.SameLine(); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(_architectureTypeNames[(int) assembly.Architecture]); + _imgui.SameLine(); if (!isDynamic) { - _imgui.TextSameLine(", \0"u8); - _imgui.TextSameLine(assembly.Hash); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(assembly.Hash); + _imgui.SameLine(); } if (hasPath) { - _imgui.TextSameLine(", \0"u8); - _imgui.SmallButton(assembly.AnonymizedPath); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.SmallButtonRound(assembly.AnonymizedPath); } else { _imgui.Text(isDynamic ? ", DYNAMIC\0"u8 : ", EMPTY\0"u8); } + + _imgui.SameLine(); + + RenderAdditionalMetadataSameLine(_assemblyAdditionalDisplayKeyMetadata, assembly); + + _imgui.NewLine(); + + _imgui.TreePop(); + } + } + + private void RenderAssembliesWithLoop() + { + for (var i = 0; i < _crashReport.Assemblies.Count; i++) + { + RenderAssembliesStep(_crashReport.Assemblies[i]); + } + } + + private void RenderAssembliesWithClipper() + { + _imGuiWithImGuiListClipper.CreateImGuiListClipper(out var clipper); + using var _ = clipper; + + clipper.Begin(_crashReport.Assemblies.Count, _imgui.GetTextLineHeightWithSpacing()); + + while (clipper.Step()) + { + for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) + { + RenderAssembliesStep(_crashReport.Assemblies[i]); + } + } + + clipper.End(); + } + + private void RenderAssemblies() + { + _imgui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1); + _imgui.Text("Hide: \0"u8); + _imgui.SameLine(); + if (_hasSystemAssemblies) { _imgui.CheckboxRound(" System | \0"u8, ref _hideSystemAssemblies); _imgui.SameLine(); } + if (_hasGACAssemblies) { _imgui.CheckboxRound(" GAC | \0"u8, ref _hideGACAssemblies); _imgui.SameLine(); } + if (_hasGameAssemblies) { _imgui.CheckboxRound(" Game | \0"u8, ref _hideGameAssemblies); _imgui.SameLine(); } + if (_hasGameModulesAssemblies) { _imgui.CheckboxRound(" Game Modules | \0"u8, ref _hideGameModulesAssemblies); _imgui.SameLine(); } + if (_hasModulesAssemblies) { _imgui.CheckboxRound(" Modules | \0"u8, ref _hideModulesAssemblies); _imgui.SameLine(); } + if (_hasLoaderAssemblies) { _imgui.CheckboxRound(" Loader | \0"u8, ref _hideLoaderAssemblies); _imgui.SameLine(); } + if (_hasLoaderPluginsAssemblies) { _imgui.CheckboxRound(" Loader Plugins | \0"u8, ref _hideLoaderPluginsAssemblies); _imgui.SameLine(); } + if (_hasDynamicAssemblies) { _imgui.CheckboxRound(" Dynamic | \0"u8, ref _hideDynamicAssemblies); _imgui.SameLine(); } + if (_hasUnclassifiedAssemblies) { _imgui.CheckboxRound(" Unclassified\0"u8, ref _hideUnclassifiedAssemblies); } + _imgui.PopStyleVar(); + + var hasFilters = _hideSystemAssemblies || + _hideGACAssemblies || + _hideGameAssemblies || + _hideGameModulesAssemblies || + _hideModulesAssemblies || + _hideLoaderAssemblies || + _hideLoaderPluginsAssemblies || + _hideDynamicAssemblies || + _hideUnclassifiedAssemblies; + + if (hasFilters) + { + RenderAssembliesWithLoop(); + } + else + { + RenderAssembliesWithClipper(); } } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.08.Native.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.08.Native.cs index aca118e..ebaa04c 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.08.Native.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.08.Native.cs @@ -1,51 +1,86 @@ -using BUTR.CrashReport.Extensions; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Memory; using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; - -using ImGuiNET; - -using System.Collections.Generic; -using System.IO; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { - private class NativeAssemblyModelEqualityComparer : IEqualityComparer + protected class NativeAssemblyModelEqualityComparer : IEqualityComparer { public static NativeAssemblyModelEqualityComparer Instance { get; } = new(); public bool Equals(NativeAssemblyModel? x, NativeAssemblyModel? y) => ReferenceEquals(x, y); // We can just reference compare here public int GetHashCode(NativeAssemblyModel obj) => obj.GetHashCode(); } - private static readonly byte[][] _nativeArchitectureTypeNames = + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly LiteralSpan[] _nativeArchitectureTypeNames = [ - "Unknown"u8.ToArray(), // Unknown - "x86"u8.ToArray(), // x86 - "x64"u8.ToArray(), // x86_64 - "Arm"u8.ToArray(), // Arm - "Arm64"u8.ToArray(), // Arm64 + "Unknown\0"u8, // Unknown + "x86\0"u8, // x86 + "x64\0"u8, // x86_64 + "Arm\0"u8, // Arm + "Arm64\0"u8, // Arm64 ]; - - private void InitializeNatives() { } +} - private void RenderNatives() +partial class ImGuiRenderer +{ + private readonly Dictionary> _nativeAdditionalDisplayKeyMetadata = new(NativeAssemblyModelEqualityComparer.Instance); + + private void InitializeNatives() { for (var i = 0; i < _crashReport.NativeModules.Count; i++) { var assembly = _crashReport.NativeModules[i]; + InitializeAdditionalMetadata(_nativeAdditionalDisplayKeyMetadata, assembly, assembly.AdditionalMetadata); + } + } - _imgui.Bullet(); - _imgui.TextSameLine(assembly.Id.Name); - _imgui.TextSameLine(", \0"u8); - _imgui.TextSameLine(assembly.Id.Version ?? string.Empty); - _imgui.TextSameLine(", \0"u8); - _imgui.TextSameLine(_nativeArchitectureTypeNames[(int) assembly.Architecture]); - _imgui.TextSameLine(", \0"u8); - _imgui.TextSameLine(assembly.Hash); - _imgui.TextSameLine(", \0"u8); - _imgui.SmallButton(assembly.AnonymizedPath); + private void RenderNatives() + { + _imGuiWithImGuiListClipper.CreateImGuiListClipper(out var clipper); + using var _ = clipper; + + clipper.Begin(_crashReport.NativeModules.Count, _imgui.GetTextLineHeightWithSpacing()); + + while (clipper.Step()) + { + for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) + { + var assembly = _crashReport.NativeModules[i]; + + if (_imgui.TreeNode(assembly.Id.Name, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) + { + _imgui.SameLine(); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(assembly.Id.Version ?? string.Empty); + _imgui.SameLine(); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(_nativeArchitectureTypeNames[(int) assembly.Architecture]); + _imgui.SameLine(); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.Text(assembly.Hash); + _imgui.SameLine(); + _imgui.Text(", \0"u8); + _imgui.SameLine(); + _imgui.SmallButtonRound(assembly.AnonymizedPath); + _imgui.SameLine(); + + RenderAdditionalMetadataSameLine(_nativeAdditionalDisplayKeyMetadata, assembly); + + _imgui.NewLine(); + + _imgui.TreePop(); + } + } } + + clipper.End(); } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.MonoModPatches.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.MonoModPatches.cs deleted file mode 100644 index e5cff01..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.MonoModPatches.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; -using BUTR.CrashReport.Renderer.ImGui.Utils; - -using HonkPerf.NET.Core; -using HonkPerf.NET.RefLinq; -using HonkPerf.NET.RefLinq.Enumerators; - -using ImGuiNET; - -using System; -using System.Collections.Generic; - -namespace BUTR.CrashReport.Renderer.ImGui.Renderer; - -partial class ImGuiRenderer -{ - private class MonoModPatchesModelEqualityComparer : IEqualityComparer - { - public static MonoModPatchesModelEqualityComparer Instance { get; } = new(); - public bool Equals(MonoModPatchesModel? x, MonoModPatchesModel? y) => ReferenceEquals(x, y); // We can just reference compare here - public int GetHashCode(MonoModPatchesModel obj) => obj.GetHashCode(); - } - - private class MonoModPatchModelEqualityComparer : IEqualityComparer - { - public static MonoModPatchModelEqualityComparer Instance { get; } = new(); - public bool Equals(MonoModPatchModel? x, MonoModPatchModel? y) => ReferenceEquals(x, y); // We can just reference compare here - public int GetHashCode(MonoModPatchModel obj) => obj.GetHashCode(); - } - - private readonly Dictionary _monoModMethodNameFullUtf8 = new(MonoModPatchesModelEqualityComparer.Instance); - private readonly Dictionary _monoModBeforeUtf8 = new(MonoModPatchModelEqualityComparer.Instance); - private readonly Dictionary _monoModAfterUtf8 = new(MonoModPatchModelEqualityComparer.Instance); - - private void InitializeMonoModPatches() - { - for (var i = 0; i < _crashReport.MonoModPatches.Count; i++) - { - var monoModPatch = _crashReport.MonoModPatches[i]; - var methodNameFull = !string.IsNullOrEmpty(monoModPatch.OriginalMethodDeclaredTypeName) - ? $"{monoModPatch.OriginalMethodDeclaredTypeName}.{monoModPatch.OriginalMethodName}" - : monoModPatch.OriginalMethodName ?? string.Empty; - _monoModMethodNameFullUtf8[monoModPatch] = UnsafeHelper.ToUtf8Array(methodNameFull); - - for (var j = 0; j < monoModPatch.Detours.Count; j++) - { - var detour = monoModPatch.Detours[j]; - for (var k = 0; k < detour.Before.Count; k++) - { - var before = detour.Before[k]; - _monoModBeforeUtf8[detour] = UnsafeHelper.ToUtf8Array(before); - } - for (var m = 0; m < detour.After.Count; m++) - { - var after = detour.After[m]; - _monoModAfterUtf8[detour] = UnsafeHelper.ToUtf8Array(after); - } - } - } - } - - private void RenderMonoModPatches(ReadOnlySpan name, RefLinqEnumerable, IListEnumerator>> patches) - { - foreach (var patch in patches) - { - var moduleId = patch.ModuleId ?? "UNKNOWN"; - var pluginId = patch.LoaderPluginId ?? "UNKNOWN"; - - _imgui.Bullet(); - _imgui.Text(name); - _imgui.Indent(); - - if (moduleId != "UNKNOWN") { _imgui.RenderId("Module Id:\0"u8, moduleId); _imgui.SameLine(0, 0); } - if (pluginId != "UNKNOWN") { _imgui.RenderId("Plugin Id:\0"u8, pluginId); _imgui.SameLine(0, 0); } - _imgui.TextSameLine(" Id: \0"u8); - _imgui.TextSameLine(patch.Id); - _imgui.TextSameLine(" Namespace: \0"u8); - _imgui.TextSameLine(patch.Namespace); - _imgui.TextSameLine(" IsActive: \0"u8); - _imgui.TextSameLine(patch.IsActive); - if (patch.Index is not null) { _imgui.TextSameLine(" Index: \0"u8); _imgui.TextSameLine(patch.Index.Value); } - if (patch.MaxIndex is not null) { _imgui.TextSameLine(" MaxIndex: \0"u8); _imgui.TextSameLine(patch.MaxIndex.Value); } - if (patch.GlobalIndex is not null) { _imgui.TextSameLine(" GlobalIndex: \0"u8); _imgui.TextSameLine(patch.GlobalIndex.Value); } - if (patch.Priority is not null) { _imgui.TextSameLine(" Priority: \0"u8); _imgui.TextSameLine(patch.Priority.Value); } - if (patch.SubPriority is not null) { _imgui.TextSameLine(" SubPriority: \0"u8); _imgui.TextSameLine(patch.SubPriority.Value); } - if (patch.Before.Count > 0) { _imgui.TextSameLine(" Before: \0"u8); _imgui.TextSameLine(_monoModBeforeUtf8[patch]); } - if (patch.After.Count > 0) { _imgui.TextSameLine(" After: \0"u8); _imgui.TextSameLine(_monoModAfterUtf8[patch]); } - _imgui.NewLine(); - - _imgui.Unindent(); - } - } - - private void RenderMonoModPatches() - { - for (var i = 0; i < _crashReport.MonoModPatches.Count; i++) - { - var monoModPatch = _crashReport.MonoModPatches[i]; - var methodNameFull = _monoModMethodNameFullUtf8[monoModPatch]; - - if (_imgui.TreeNode(methodNameFull, ImGuiTreeNodeFlags.DefaultOpen)) - { - RenderMonoModPatches("Detours\0"u8, monoModPatch.Detours.ToRefLinq().Where(x => x.Type == MonoModPatchModelType.Detour)); - RenderMonoModPatches("ILHooks\0"u8, monoModPatch.Detours.ToRefLinq().Where(x => x.Type == MonoModPatchModelType.ILHook)); - _imgui.NewLine(); - - _imgui.TreePop(); - } - } - } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.RuntimePatches.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.RuntimePatches.cs index eab2076..f8388e6 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.RuntimePatches.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.09.RuntimePatches.cs @@ -1,104 +1,102 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; -using BUTR.CrashReport.Renderer.ImGui.Utils; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Models; -using HonkPerf.NET.Core; -using HonkPerf.NET.RefLinq; -using HonkPerf.NET.RefLinq.Enumerators; - -using ImGuiNET; - -using System; -using System.Collections.Generic; -using System.Linq; +using Cysharp.Text; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; partial class ImGuiRenderer { - private class RuntimePatchesModelEqualityComparer : IEqualityComparer - { - public static RuntimePatchesModelEqualityComparer Instance { get; } = new(); - public bool Equals(RuntimePatchesModel? x, RuntimePatchesModel? y) => ReferenceEquals(x, y); // We can just reference compare here - public int GetHashCode(RuntimePatchesModel obj) => obj.GetHashCode(); - } - - private class RuntimePatchModelEqualityComparer : IEqualityComparer + protected class RuntimePatchModelEqualityComparer : IEqualityComparer { public static RuntimePatchModelEqualityComparer Instance { get; } = new(); public bool Equals(RuntimePatchModel? x, RuntimePatchModel? y) => ReferenceEquals(x, y); // We can just reference compare here public int GetHashCode(RuntimePatchModel obj) => obj.GetHashCode(); } +} - private readonly List _runtimePatchProviders = new(); +partial class ImGuiRenderer +{ private readonly List _runtimePatchTypes = new(); - private readonly Dictionary _runtimeMethodNameFullUtf8 = new(RuntimePatchesModelEqualityComparer.Instance); + private readonly Dictionary> _runtimePatchAdditionalDisplayKeyMetadata = new(RuntimePatchModelEqualityComparer.Instance); + + private List>> _groupedRuntimePatches = new(); + + private static string GetFullName(RuntimePatchesModel patches) => !string.IsNullOrEmpty(patches.OriginalMethodDeclaredTypeName) + ? ZString.Format("{0}.{1}", patches.OriginalMethodDeclaredTypeName, patches.OriginalMethodName) + : patches.OriginalMethodName ?? string.Empty; private void InitializeRuntimePatches() { for (var i = 0; i < _crashReport.RuntimePatches.Count; i++) { var runtimePatch = _crashReport.RuntimePatches[i]; - var methodNameFull = !string.IsNullOrEmpty(runtimePatch.OriginalMethodDeclaredTypeName) - ? $"{runtimePatch.OriginalMethodDeclaredTypeName}.{runtimePatch.OriginalMethodName}" - : runtimePatch.OriginalMethodName ?? string.Empty; - _runtimeMethodNameFullUtf8[runtimePatch] = UnsafeHelper.ToUtf8Array(methodNameFull); + + for (var j = 0; j < runtimePatch.Patches.Count; j++) + { + var patch = runtimePatch.Patches[j]; + InitializeAdditionalMetadata(_runtimePatchAdditionalDisplayKeyMetadata, patch, patch.AdditionalMetadata); + } } - - _runtimePatchProviders.AddRange(_crashReport.RuntimePatches.SelectMany(x => x.Patches).Select(x => x.Provider).Distinct()); - + _runtimePatchTypes.AddRange(_crashReport.RuntimePatches.SelectMany(x => x.Patches).Select(x => x.Type).Distinct()); + + _groupedRuntimePatches = _crashReport.RuntimePatches + .GroupBy(GetFullName) + .Select(x => new KeyValuePair>(x.Key, x.SelectMany(y => y.Patches).ToList())) + .ToList(); } - private void RenderRuntimePatches(RefLinqEnumerable, IListEnumerator>> patches) + private void RenderRuntimePatches(string type, ReadOnlySpan patches) { - foreach (var patch in patches) + for (var i = 0; i < patches.Length; i++) { - var moduleId = patch.ModuleId ?? "UNKNOWN"; - var pluginId = patch.LoaderPluginId ?? "UNKNOWN"; - - _imgui.Bullet(); - _imgui.TextSameLine(patch.Provider); - _imgui.TextSameLine(" \0"u8); - _imgui.TextSameLine(patch.Type); - _imgui.NewLine(); - _imgui.Indent(); - - if (moduleId != "UNKNOWN") { _imgui.RenderId("Module Id:\0"u8, moduleId); _imgui.SameLine(0, 0); } - if (pluginId != "UNKNOWN") { _imgui.RenderId("Plugin Id:\0"u8, pluginId); _imgui.SameLine(0, 0); } - - _imgui.TextSameLine(" Full Name: \0"u8); - _imgui.TextSameLine(patch.FullName); - - for (var i = 0; i < patch.AdditionalMetadata.Count; i++) + var patch = patches[i]; + + if (patch.Type != type) continue; + + if (_imgui.TreeNode(patch.FullName, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.DefaultOpen)) { - var metadata = patch.AdditionalMetadata[i]; - if (string.IsNullOrEmpty(metadata.Key) || string.IsNullOrEmpty(metadata.Value)) continue; - _imgui.TextSameLine(" \0"u8); - _imgui.TextSameLine(metadata.Key); - _imgui.TextSameLine(": \0"u8); - _imgui.TextSameLine(metadata.Value); - } - _imgui.NewLine(); + var moduleId = patch.ModuleId ?? "UNKNOWN"; + var pluginId = patch.LoaderPluginId ?? "UNKNOWN"; + + if (moduleId != "UNKNOWN") _imgui.RenderId("Module Id:\0"u8, moduleId); + if (pluginId != "UNKNOWN") _imgui.RenderId("Plugin Id:\0"u8, pluginId); + + _imgui.Text("Type: \0"u8); + _imgui.SameLine(); + _imgui.Text(patch.Provider); + _imgui.SameLine(); + _imgui.Text(" \0"u8); + _imgui.SameLine(); + _imgui.Text(patch.Type); - _imgui.Unindent(); + RenderAdditionalMetadata(_runtimePatchAdditionalDisplayKeyMetadata, patch); + + _imgui.TreePop(); + } } } private void RenderRuntimePatches() { - for (var i = 0; i < _crashReport.RuntimePatches.Count; i++) + var groupedRuntimePatches = _groupedRuntimePatches.AsSpan(); + var runtimePatchTypes = _runtimePatchTypes.AsSpan(); + + for (var i = 0; i < groupedRuntimePatches.Length; i++) { - var runtimePatch = _crashReport.RuntimePatches[i]; - var methodNameFull = _runtimeMethodNameFullUtf8[runtimePatch]; + var (methodNameFull, value) = groupedRuntimePatches[i]; + var patches = value.AsSpan(); if (_imgui.TreeNode(methodNameFull, ImGuiTreeNodeFlags.DefaultOpen)) { - for (var j = 0; j < _runtimePatchTypes.Count; j++) + for (var j = 0; j < runtimePatchTypes.Length; j++) { - var type = _runtimePatchTypes[j]; - RenderRuntimePatches(runtimePatch.Patches.ToRefLinq().Where(x => x.Type == type)); + _imgui.PushId(j); + RenderRuntimePatches(runtimePatchTypes[j], patches); + _imgui.PopId(); } _imgui.NewLine(); diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.HarmonyPatches.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.HarmonyPatches.cs deleted file mode 100644 index 570d515..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.HarmonyPatches.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; -using BUTR.CrashReport.Renderer.ImGui.Utils; - -using HonkPerf.NET.Core; -using HonkPerf.NET.RefLinq; -using HonkPerf.NET.RefLinq.Enumerators; - -using ImGuiNET; - -using System; -using System.Collections.Generic; - -namespace BUTR.CrashReport.Renderer.ImGui.Renderer; - -partial class ImGuiRenderer -{ - private class HarmonyPatchesModelEqualityComparer : IEqualityComparer - { - public static HarmonyPatchesModelEqualityComparer Instance { get; } = new(); - public bool Equals(HarmonyPatchesModel? x, HarmonyPatchesModel? y) => ReferenceEquals(x, y); // We can just reference compare here - public int GetHashCode(HarmonyPatchesModel obj) => obj.GetHashCode(); - } - - private class HarmonyPatchModelEqualityComparer : IEqualityComparer - { - public static HarmonyPatchModelEqualityComparer Instance { get; } = new(); - public bool Equals(HarmonyPatchModel? x, HarmonyPatchModel? y) => ReferenceEquals(x, y); // We can just reference compare here - public int GetHashCode(HarmonyPatchModel obj) => obj.GetHashCode(); - } - - private readonly Dictionary _harmonyMethodNameFullUtf8 = new(HarmonyPatchesModelEqualityComparer.Instance); - private readonly Dictionary _harmonyBeforeUtf8 = new(HarmonyPatchModelEqualityComparer.Instance); - private readonly Dictionary _harmonyAfterUtf8 = new(HarmonyPatchModelEqualityComparer.Instance); - - private void InitializeHarmonyPatches() - { - for (var i = 0; i < _crashReport.HarmonyPatches.Count; i++) - { - var harmonyPatch = _crashReport.HarmonyPatches[i]; - var methodNameFull = !string.IsNullOrEmpty(harmonyPatch.OriginalMethodDeclaredTypeName) - ? $"{harmonyPatch.OriginalMethodDeclaredTypeName}.{harmonyPatch.OriginalMethodName}" - : harmonyPatch.OriginalMethodName ?? string.Empty; - _harmonyMethodNameFullUtf8[harmonyPatch] = UnsafeHelper.ToUtf8Array(methodNameFull); - - for (var j = 0; j < harmonyPatch.Patches.Count; j++) - { - var patch = harmonyPatch.Patches[j]; - for (var k = 0; k < patch.Before.Count; k++) - { - var before = patch.Before[k]; - _harmonyBeforeUtf8[patch] = UnsafeHelper.ToUtf8Array(before); - } - for (var m = 0; m < patch.After.Count; m++) - { - var after = patch.After[m]; - _harmonyAfterUtf8[patch] = UnsafeHelper.ToUtf8Array(after); - } - } - } - } - - private void RenderHarmonyPatches(ReadOnlySpan name, RefLinqEnumerable, IListEnumerator>> patches) - { - foreach (var patch in patches) - { - var moduleId = patch.ModuleId ?? "UNKNOWN"; - var pluginId = patch.LoaderPluginId ?? "UNKNOWN"; - - _imgui.Bullet(); - _imgui.Text(name); - _imgui.Indent(); - - if (moduleId != "UNKNOWN") { _imgui.RenderId("Module Id:\0"u8, moduleId); _imgui.SameLine(0, 0); } - if (pluginId != "UNKNOWN") { _imgui.RenderId("Plugin Id:\0"u8, pluginId); _imgui.SameLine(0, 0); } - _imgui.TextSameLine(" Owner: \0"u8); - _imgui.TextSameLine(patch.Owner); - _imgui.TextSameLine(" Namespace: \0"u8); - _imgui.TextSameLine(patch.Namespace); - if (patch.Index != 0) { _imgui.TextSameLine(" Index: \0"u8); _imgui.TextSameLine(patch.Index); } - if (patch.Priority != 400) { _imgui.TextSameLine(" Priority: \0"u8); _imgui.TextSameLine(patch.Priority); } - if (patch.Before.Count > 0) { _imgui.TextSameLine(" Before: \0"u8); _imgui.TextSameLine(_harmonyBeforeUtf8[patch]); } - if (patch.After.Count > 0) { _imgui.TextSameLine(" After: \0"u8); _imgui.TextSameLine(_harmonyAfterUtf8[patch]); } - _imgui.NewLine(); - - _imgui.Unindent(); - } - } - - private void RenderHarmonyPatches() - { - for (var i = 0; i < _crashReport.HarmonyPatches.Count; i++) - { - var harmonyPatch = _crashReport.HarmonyPatches[i]; - var methodNameFull = _harmonyMethodNameFullUtf8[harmonyPatch]; - - if (_imgui.TreeNode(methodNameFull, ImGuiTreeNodeFlags.DefaultOpen)) - { - RenderHarmonyPatches("Prefixes\0"u8, harmonyPatch.Patches.ToRefLinq().Where(x => x.Type == HarmonyPatchType.Prefix)); - RenderHarmonyPatches("Postfixes\0"u8, harmonyPatch.Patches.ToRefLinq().Where(x => x.Type == HarmonyPatchType.Postfix)); - RenderHarmonyPatches("Finalizers\0"u8, harmonyPatch.Patches.ToRefLinq().Where(x => x.Type == HarmonyPatchType.Finalizer)); - RenderHarmonyPatches("Transpilers\0"u8, harmonyPatch.Patches.ToRefLinq().Where(x => x.Type == HarmonyPatchType.Transpiler)); - _imgui.NewLine(); - - _imgui.TreePop(); - } - } - } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.TextBox.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.TextBox.cs new file mode 100644 index 0000000..3a02c74 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.TextBox.cs @@ -0,0 +1,141 @@ +#if !TEXT_EDITOR +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Models; + +using Cysharp.Text; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly LiteralSpan[] _logLevelNamesUtf8 = + [ + " \0"u8, // None + "VRB\0"u8, // Verbose + "DBG\0"u8, // Debug + "INF\0"u8, // Information + "WRN\0"u8, // Warning + "ERR\0"u8, // Error + "FTL\0"u8, // Fatal + ]; +} + +partial class ImGuiRenderer +{ + private void RenderLogFileTextBox(int logSourceIdx, LogSourceModel logSource) + { + _imGuiWithImGuiStyle.GetStyle(out var style); + style.GetColors(out var colors); + + var longestApplicationLength = _logSourceMaxApplicationLengths[logSourceIdx]; + var longestTypeLength = _logSourceMaxTypeLengths[logSourceIdx]; + var hasDates = _logSourceHasDates[logSourceIdx]; + var hasType = _logSourceHasType[logSourceIdx]; + + _imGuiWithImGuiListClipper.CreateImGuiListClipper(out var clipper); + using var _ = clipper; + + clipper.Begin(logSource.Logs.Count, _imgui.GetTextLineHeightWithSpacing()); + + while (clipper.Step()) + { + for (var j = clipper.DisplayStart; j < clipper.DisplayEnd; ++j) + { + var logEntry = logSource.Logs[j]; + var toAppendApplication = longestApplicationLength - logEntry.Application.Length; + var toAppendType = longestTypeLength - logEntry.Type.Length; + + var date = logEntry.Date; + var color = logEntry.Level switch + { + LogLevel.Fatal => Fatal, + LogLevel.Error => Error, + LogLevel.Warning => Warn, + LogLevel.Information => Info, + LogLevel.Debug => Debug, + LogLevel.Verbose => Debug, + _ => colors[ImGuiCol.Text], + }; + var level = Clamp(logEntry.Level, LogLevel.None, LogLevel.Fatal); + + if (hasDates) + { + _imgui.Text(ref date); + _imgui.SameLine(); + _imgui.Text(" [\0"u8); + } + else + { + _imgui.Text("[\0"u8); + } + + _imgui.SameLine(); + _imgui.Text(logEntry.Application); + _imgui.SameLine(); + _imgui.Text("]\0"u8); + _imgui.SameLine(); + _imgui.PadRight(toAppendApplication); + if (hasType) + { + _imgui.Text(" [\0"u8); + _imgui.SameLine(); + _imgui.Text(logEntry.Type); + _imgui.SameLine(); + _imgui.Text("]\0"u8); + _imgui.SameLine(); + } + _imgui.PadRight(toAppendType); + _imgui.Text(" [\0"u8); + _imgui.SameLine(); + _imgui.TextColored(_logLevelNamesUtf8[level], in color); + _imgui.SameLine(); + _imgui.Text("]: \0"u8); + _imgui.SameLine(); + _imgui.TextWrapped(logEntry.Message); + } + } + + clipper.End(); + } + + private void RenderLogsFileTextBoxContextMenu(LogSourceModel logSource) + { + _imgui.PushId(logSource.Name); + if (_imgui.BeginPopupContextWindow("Context Menu\0"u8)) + { + if (_imgui.MenuItem("Copy All\0"u8)) + { + CopyLogSourceToClipboard(logSource); + } + + _imgui.EndPopup(); + } + _imgui.PopId(); + } + + private void CopyLogSourceToClipboard(LogSourceModel logSource) + { + using var sb = ZString.CreateUtf8StringBuilder(); + for (var i = 0; i < logSource.Logs.Count; i++) + { + var log = logSource.Logs[i]; + sb.Append(log.Date.ToString("O")); + sb.Append(" ["); + sb.Append(log.Application); + sb.Append("]"); + sb.Append(" ["); + sb.Append(log.Type); + sb.Append("]"); + sb.Append(" ["); + sb.Append(log.Level); + sb.Append("]: "); + sb.Append(log.Message); + sb.AppendLine(); + } + _imgui.SetClipboardText(sb.AsSpan()); + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.TextEditor.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.TextEditor.cs new file mode 100644 index 0000000..16ee4fc --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.TextEditor.cs @@ -0,0 +1,114 @@ +#if TEXT_EDITOR +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.ImGui.Syntax; + +using ImGuiColorTextEditNet; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + // ReSharper disable once HeapView.ObjectAllocation + protected static readonly string[] _logLevelNamesUtf16 = + [ + " ", // None + "VRB", // Verbose + "DBG", // Debug + "INF", // Information + "WRN", // Warning + "ERR", // Error + "FTL", // Fatal + ]; +} + +partial class ImGuiRenderer +{ + private static ColorPaletteIndex LogPalette(bool isDarkTheme, TColorsRangeAccessorRef colors) => CodeParser.BasePalette(isDarkTheme, colors).With(new() + { + [LogColorPalette.Date] = colors[ImGuiCol.Text], + [LogColorPalette.Application] = colors[ImGuiCol.Text], + [LogColorPalette.Type] = colors[ImGuiCol.Text], + [LogColorPalette.Message] = colors[ImGuiCol.Text], + [LogColorPalette.Debug] = Debug, + [LogColorPalette.Info] = Info, + [LogColorPalette.Warn] = Warn, + [LogColorPalette.Error] = Error, + [LogColorPalette.Fatal] = Fatal, + }); + + private TextEditor[] _logsEditor = []; + + private void InitializeLogFilesTextEditor() + { + _logsEditor = new TextEditor[_logSources.Count]; + for (var i = 0; i < _logSources.Count; i++) + { + var logSource = _logSources[i]; + + var hasDates = _logSourceHasDates[i]; + var longestApplicationLength = _logSourceMaxApplicationLengths[i]; + var hasType = _logSourceHasType[i]; + var longestTypeLength = _logSourceMaxTypeLengths[i]; + + _logsEditor[i] = new TextEditor(_imgui, _imGuiWithImGuiIO, _imGuiWithImDrawList, _imGuiWithImGuiStyle, _imGuiWithImGuiListClipper); + for (var j = 0; j < logSource.Logs.Count; j++) + { + var log = logSource.Logs[j]; + + var sb = _logsEditor[i].CreateGlyphBuilder(); + if (hasDates) + { + sb.Append(log.Date.ToString("O")); + sb.Append(" ["); + } + else + { + sb.Append("["); + } + sb.Append(log.Application); + sb.Append("]"); + sb.Append(' ', longestApplicationLength - log.Application.Length); + sb.Append(" ["); + if (hasType) + { + sb.Append(log.Type); + sb.Append("]"); + sb.Append(' ', longestTypeLength - log.Type.Length); + sb.Append(" ["); + } + var color = log.Level switch + { + LogLevel.Fatal => LogColorPalette.Fatal, + LogLevel.Error => LogColorPalette.Error, + LogLevel.Warning => LogColorPalette.Warn, + LogLevel.Information => LogColorPalette.Info, + LogLevel.Debug => LogColorPalette.Debug, + LogLevel.Verbose => LogColorPalette.Debug, + _ => ColorPalette.Default, + }; + sb.Append(_logLevelNamesUtf16[(int) log.Level], color); + sb.Append("]: "); + sb.Append(log.Message); + if (j < logSource.Logs.Count - 1) + sb.AppendLine(); + + _logsEditor[i].AddGlyphs(sb.AsSpan()); + } + } + + _onDarkModeChanged += isDarkTheme => + { + _imGuiWithImGuiStyle.GetStyle(out var style); + style.GetColors(out var colors); + for (var i = 0; i < _logsEditor.Length; i++) + _logsEditor[i].SetPalette(LogPalette(isDarkTheme, colors)); + }; + } + + private void RenderLogFileTextEditor(int logSourceIdx, LogSourceModel logSource) + { + _logsEditor[logSourceIdx].Render(logSource.Name); + } +} +#endif \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.cs index 8723578..6e5d4d9 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.10.LogFiles.cs @@ -1,35 +1,48 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.Extensions; - -using HonkPerf.NET.RefLinq; - -using ImGuiNET; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; -partial class ImGuiRenderer +partial class ImGuiRenderer { - private static readonly byte[][] _logLevelNamesUtf8 = - [ - " \0"u8.ToArray(), // None - "VRB\0"u8.ToArray(), // Verbose - "DBG\0"u8.ToArray(), // Debug - "INF\0"u8.ToArray(), // Information - "WRN\0"u8.ToArray(), // Warning - "ERR\0"u8.ToArray(), // Error - "FTL\0"u8.ToArray(), // Fatal - ]; - + private int[] _logSourceMaxApplicationLengths = []; private int[] _logSourceMaxTypeLengths = []; + private bool[] _logSourceHasDates = []; + private bool[] _logSourceHasType = []; private void InitializeLogFiles() { + _logSourceMaxApplicationLengths = new int[_logSources.Count]; + for (var i = 0; i < _logSources.Count; i++) + { + if (_logSources[i].Logs.Count == 0) _logSourceMaxApplicationLengths[i] = 0; + else _logSourceMaxApplicationLengths[i] = _logSources[i].Logs.Select(x => x.Application.Length).Max(); + } + _logSourceMaxTypeLengths = new int[_logSources.Count]; for (var i = 0; i < _logSources.Count; i++) { if (_logSources[i].Logs.Count == 0) _logSourceMaxTypeLengths[i] = 0; - else _logSourceMaxTypeLengths[i] = _logSources[i].Logs.ToRefLinq().Select(x => x.Type.Length).Max(); + else _logSourceMaxTypeLengths[i] = _logSources[i].Logs.Select(x => x.Type.Length).Max(); + } + + _logSourceHasDates = new bool[_logSources.Count]; + for (var i = 0; i < _logSources.Count; i++) + { + if (_logSources[i].Logs.Count == 0) _logSourceHasDates[i] = false; + else _logSourceHasDates[i] = _logSources[i].Logs.All(x => x.Date != DateTimeOffset.MinValue); } + + _logSourceHasType = new bool[_logSources.Count]; + for (var i = 0; i < _logSources.Count; i++) + { + if (_logSources[i].Logs.Count == 0) _logSourceHasType[i] = false; + else _logSourceHasType[i] = _logSources[i].Logs.All(x => !string.IsNullOrEmpty(x.Type)); + } + +#if TEXT_EDITOR + InitializeLogFilesTextEditor(); +#endif } private void RenderLogFiles() @@ -37,42 +50,31 @@ private void RenderLogFiles() for (var i = 0; i < _logSources.Count; i++) { var logSource = _logSources[i]; + + _imgui.Unindent(); + if (_imgui.TreeNode(logSource.Name, ImGuiTreeNodeFlags.DefaultOpen)) { if (logSource.Logs.Count == 0) continue; - var longestTypeLength = _logSourceMaxTypeLengths[i]; - for (var j = 0; j < logSource.Logs.Count; j++) - { - var logEntry = logSource.Logs[j]; - var toAppend = (longestTypeLength - logEntry.Type.Length) + 2; - - var date = logEntry.Date; - var color = logEntry.Level switch - { - LogLevel.Fatal => Fatal, - LogLevel.Error => Error, - LogLevel.Warning => Warn, - LogLevel.Information => Info, - LogLevel.Debug => Debug, - LogLevel.Verbose => Debug, - _ => Black - }; - var level = Clamp(logEntry.Level, LogLevel.None, LogLevel.Fatal); - - _imgui.TextSameLine(ref date); - _imgui.TextSameLine(" [\0"u8); - _imgui.TextSameLine(logEntry.Type); - _imgui.TextSameLine("]\0"u8); - _imgui.PadRight(toAppend); - _imgui.TextSameLine("[\0"u8); - _imgui.TextColoredSameLine(in color, _logLevelNamesUtf8[level]); - _imgui.TextSameLine("]: \0"u8); - _imgui.Text(logEntry.Message); - } + _imgui.Unindent(); + +#if TEXT_EDITOR + RenderLogFileTextEditor(i, logSource); +#else + RenderLogFileTextBox(i, logSource); +#endif + + _imgui.Indent(); _imgui.TreePop(); } + +#if !TEXT_EDITOR + RenderLogsFileTextBoxContextMenu(logSource); +#endif + + _imgui.Indent(); } } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.11.ModalPicker.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.11.ModalPicker.cs new file mode 100644 index 0000000..12de2a5 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.11.ModalPicker.cs @@ -0,0 +1,35 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Renderer.ImGui.Components; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +partial class ImGuiRenderer +{ + protected delegate void FilePickerOnSelected(string selectedPath); +} + +partial class ImGuiRenderer +{ + private void InitializeModalPicker() + { + + } + + private void RenderModalPicker(LiteralSpan pickerId, FilePickerMode mode, FilePickerOnSelected onSelected) + { + if (_imgui.BeginPopupModal(pickerId, ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize)) + { + var picker = FilePicker.GetPicker(pickerId, _imgui, mode, Environment.CurrentDirectory); + if (picker.Draw()) + { + onSelected(picker.SelectedPath!); + // TODO: Doesn't work + FilePicker.RemovePicker(pickerId); + } + + _imgui.EndPopup(); + } + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.ImGui.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.ImGui.cs index 4e9574d..881d7f3 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.ImGui.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.ImGui.cs @@ -1,37 +1,50 @@ -using System.Numerics; -using System.Runtime.CompilerServices; +using BUTR.CrashReport.Renderer.ImGui.Utils; + +using System.Numerics; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; -partial class ImGuiRenderer +public partial class ImGuiRenderer { - private static readonly Vector2 Zero2 = Vector2.Zero; - private static readonly Vector3 Zero3 = Vector3.Zero; - private static readonly Vector4 Zero4 = Vector4.Zero; - private static readonly Vector4 Black = FromColor(0, 0, 0, 255); - private static readonly Vector4 White = FromColor(255, 255, 255, 255); - - private static readonly Vector4 Debug = FromColor(54, 96, 146, 255); - private static readonly Vector4 Info = FromColor(0, 125, 60, 255); - private static readonly Vector4 Warn = FromColor(225, 125, 50, 255); - private static readonly Vector4 Error = FromColor(240, 0, 0, 255); - private static readonly Vector4 Fatal = FromColor(190, 0, 0, 255); - - private static readonly Vector4 Background = FromColor(236, 236, 236, 255); - private static readonly Vector4 Plugin = FromColor(255, 255, 224, 255); - private static readonly Vector4 OfficialModule = FromColor(244, 252, 220, 255); - private static readonly Vector4 UnofficialModule = FromColor(255, 255, 224, 255); - private static readonly Vector4 ExternalModule = FromColor(255, 255, 224, 255); - private static readonly Vector4 SubModule = FromColor(248, 248, 231, 255); - - private static readonly Vector4 Primary = FromColor(151, 129, 082, 135); - private static readonly Vector4 Primary2 = FromColor(151, 129, 082, 180); - private static readonly Vector4 Primary3 = FromColor(151, 129, 082, 210); - private static readonly Vector4 Secondary = FromColor(255, 230, 128, 135); - private static readonly Vector4 Secondary2 = FromColor(255, 230, 128, 180); - private static readonly Vector4 Secondary3 = FromColor(255, 230, 128, 210); - //private static readonly Vector4 PrimaryActive = FromColor(174, 137, 59, 180); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 FromColor(byte r, byte g, byte b, byte a) => new((float) r / 255f, (float) g / 255f, (float) b / 255f, (float) a / 255f); + protected static readonly Vector2 Zero2 = Vector2.Zero; + protected static readonly Vector3 Zero3 = Vector3.Zero; + protected static readonly Vector4 Zero4 = Vector4.Zero; + + protected static readonly Vector4 Black = ColorUtils.FromColor(0, 0, 0, 255); + protected static readonly Vector4 White = ColorUtils.FromColor(255, 255, 255, 255); + + protected static readonly Vector4 Debug = ColorUtils.FromColor(54, 96, 146, 255); + protected static readonly Vector4 Info = ColorUtils.FromColor(0, 125, 60, 255); + protected static readonly Vector4 Warn = ColorUtils.FromColor(225, 125, 50, 255); + protected static readonly Vector4 Error = ColorUtils.FromColor(240, 0, 0, 255); + protected static readonly Vector4 Fatal = ColorUtils.FromColor(190, 0, 0, 255); + + protected static readonly Vector4 LightBackground = ColorUtils.FromColor(236, 236, 236, 255); + protected static readonly Vector4 LightChildBackground = ColorUtils.FromColor(255, 255, 255, 255); + + protected static readonly Vector4 DarkBackground = ColorUtils.FromColor(30, 30, 30, 255); + protected static readonly Vector4 DarkChildBackground = ColorUtils.FromColor(43, 43, 43, 255); + + protected static readonly Vector4 LightPlugin = ColorUtils.FromColor(255, 255, 224, 255); + protected static readonly Vector4 DarkPlugin = ColorUtils.FromColor(30, 30, 61, 255); + + protected static readonly Vector4 LightOfficialModule = ColorUtils.FromColor(244, 252, 220, 255); + protected static readonly Vector4 DarkOfficialModule = ColorUtils.FromColor(50, 50, 56, 255); + + protected static readonly Vector4 LightUnofficialModule = ColorUtils.FromColor(255, 255, 224, 255); + protected static readonly Vector4 DarkUnofficialModule = ColorUtils.FromColor(53, 53, 53, 255); + + protected static readonly Vector4 LightExternalModule = ColorUtils.FromColor(255, 255, 224, 255); + protected static readonly Vector4 DarkExternalModule = ColorUtils.FromColor(30, 30, 61, 255); + + protected static readonly Vector4 LightSubModule = ColorUtils.FromColor(248, 248, 231, 255); + protected static readonly Vector4 DarkSubModule = ColorUtils.FromColor(70, 70, 70, 255); + + protected static readonly Vector4 Primary = ColorUtils.FromColor(151, 129, 082, 135); + protected static readonly Vector4 Primary2 = ColorUtils.FromColor(151, 129, 082, 180); + protected static readonly Vector4 Primary3 = ColorUtils.FromColor(151, 129, 082, 210); + protected static readonly Vector4 Secondary = ColorUtils.FromColor(255, 230, 128, 135); + protected static readonly Vector4 Secondary2 = ColorUtils.FromColor(255, 230, 128, 180); + protected static readonly Vector4 Secondary3 = ColorUtils.FromColor(255, 230, 128, 210); + //protected static readonly Vector4 PrimaryActive = ColorUtils.FromColor(174, 137, 59, 180); } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.Utils.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.Utils.cs new file mode 100644 index 0000000..0096b25 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.Utils.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; + +namespace BUTR.CrashReport.Renderer.ImGui.Renderer; + +public partial class ImGuiRenderer +{ + protected const MethodImplOptions AggressiveOptimization = (MethodImplOptions) 512; + + protected static void SetNestedDictionary(TDictionary methodDict, TKey key, TNestedKey nestedKey, TValue value) + where TDictionary : IDictionary>, new() where TNestedKey : notnull + { + if (!methodDict.TryGetValue(key, out var nestedDict)) + methodDict[key] = nestedDict = new Dictionary(); + nestedDict[nestedKey] = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + protected static int Clamp(TEnum n, TEnum min, TEnum max) where TEnum : Enum + { + var nInt = Unsafe.As(ref n); + var minInt = Unsafe.As(ref min); + var maxInt = Unsafe.As(ref max); + + if (nInt < minInt) return minInt; + if (nInt > maxInt) return maxInt; + return nInt; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.cs b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.cs index 8bbe5fa..852f66d 100644 --- a/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.cs +++ b/src/BUTR.CrashReport.Renderer.ImGui/Renderer/ImGuiRenderer.cs @@ -1,17 +1,20 @@ -using BUTR.CrashReport.Models; -using BUTR.CrashReport.Renderer.ImGui.ImGui; -using BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Structures; +using BUTR.CrashReport.ImGui.Utils; +using BUTR.CrashReport.Memory; +using BUTR.CrashReport.Models; +using BUTR.CrashReport.Renderer.ImGui.Extensions; -using ImGuiNET; +using System.Numerics; -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; +using Utf8StringInterpolation; namespace BUTR.CrashReport.Renderer.ImGui.Renderer; // Generic rules to avoid allocations: -// 1. Split any interpolated string into a hardcoded ("()"u8) and dynamic (entry.Text) parts and render them separately +// 1. Split any interpolated string into a hardcoded ("()\0"u8) and dynamic (entry.Text) parts and render them separately // 2. Use custom equality comparer if a key doesn't implement IEquatable in dictionaries // 3. Use a FrozenDictionary instead of a standard. Set the EqualityComparer in the Dictionary // 4. Cache all dynamic strings as utf8 byte array @@ -22,51 +25,58 @@ namespace BUTR.CrashReport.Renderer.ImGui.Renderer; /// This is an _almost_ zero allocation Crash Report Renderer. /// We allocate a few bytes per second because of implecit Span{} casts and FrozenDictionary finds. ///
-internal partial class ImGuiRenderer +public partial class ImGuiRenderer : ImGuiRenderer + where TImGuiIORef : IImGuiIO + where TImGuiViewportRef : IImGuiViewport + where TImDrawListRef : IImDrawList + where TImGuiStyleRef : IImGuiStyle + where TColorsRangeAccessorRef : IRangeAccessor + where TImGuiListClipperRef : IImGuiListClipper { - private static void SetNestedDictionary(TDictionary methodDict, TKey key, TNestedKey nestedKey, TValue value) - where TDictionary : IDictionary>, new() where TNestedKey : notnull - { - if (!methodDict.TryGetValue(key, out var nestedDict)) - methodDict[key] = (nestedDict = new Dictionary()); - nestedDict[nestedKey] = value; - } - - private static int Clamp(int n, int min, int max) - { - if (n < min) return min; - if (n > max) return max; - return n; - } - private static int Clamp(TEnum n, TEnum min, TEnum max) where TEnum : Enum - { - var nInt = Unsafe.As(ref n); - var minInt = Unsafe.As(ref min); - var maxInt = Unsafe.As(ref max); - - if (nInt < minInt) return minInt; - if (nInt > maxInt) return maxInt; - return nInt; - } + private readonly IImGui _imgui; + private readonly IImGuiWithImGuiIO _imGuiWithImGuiIO; + private readonly IImGuiWithImGuiViewport _imguiWithViewport; + private readonly IImGuiWithImDrawList _imGuiWithImDrawList; + private readonly IImGuiWithImGuiStyle _imGuiWithImGuiStyle; + private readonly IImGuiWithImGuiListClipper _imGuiWithImGuiListClipper; - private readonly CmGui _imgui; private readonly CrashReportModel _crashReport; private readonly IList _logSources; private readonly ICrashReportRendererUtilities _crashReportRendererUtilities; private readonly Action _onClose; - private byte[] _involvedModulesAndPluginsTitle = []; - private byte[] _loadedPluginsTitle = []; + private event Action? _onDarkModeChanged; - public ImGuiRenderer(CmGui imgui, CrashReportModel crashReport, IList logSources, ICrashReportRendererUtilities crashReportRendererUtilities, Action onClose) + private bool _isDarkMode; + private bool _isDarkModeOld; + + private LiteralSpan _involvedModulesAndPluginsTitleUtf8 = LiteralSpan.Empty; + private byte[] _loadedPluginsTitleUtf8 = []; + + public ImGuiRenderer(IImGui imgui, + IImGuiWithImGuiIO imGuiWithImGuiIO, + IImGuiWithImGuiViewport imguiWithViewport, + IImGuiWithImDrawList imGuiWithImDrawList, + IImGuiWithImGuiStyle imGuiWithImGuiStyle, + IImGuiWithImGuiListClipper imGuiWithImGuiListClipper, + CrashReportModel crashReport, IList logSources, ICrashReportRendererUtilities crashReportRendererUtilities, Action onClose) { _imgui = imgui; + _imGuiWithImGuiIO = imGuiWithImGuiIO; + _imguiWithViewport = imguiWithViewport; + _imGuiWithImDrawList = imGuiWithImDrawList; + _imGuiWithImGuiStyle = imGuiWithImGuiStyle; + _imGuiWithImGuiListClipper = imGuiWithImGuiListClipper; _crashReport = crashReport; _logSources = logSources; _crashReportRendererUtilities = crashReportRendererUtilities; _onClose = onClose; - InitializeMain(); + _isDarkMode = _crashReportRendererUtilities.IsDefaultDarkMode; + + InitializeInputTextWithIO(); + InitializeRender(); + InitializeSummary(); InitializeExceptionRecursively(); InitializeCodeLines(); InitializeInvolved(); @@ -76,192 +86,197 @@ public ImGuiRenderer(CmGui imgui, CrashReportModel crashReport, IList(bool isDarkTheme, TColors colors) where TColors : IRangeAccessor => new() + { + [ColorPalette.Default] = colors[ImGuiCol.Text], + [ColorPalette.Background] = colors[ImGuiCol.WindowBg], + [ColorPalette.Cursor] = colors[ImGuiCol.Text], + [ColorPalette.Selection] = colors[ImGuiCol.TextSelectedBg], + [ColorPalette.ExecutingLine] = colors[ImGuiCol.NavWindowingHighlight], + [ColorPalette.LineNumber] = colors[ImGuiCol.TextDisabled], + [ColorPalette.CurrentLineFill] = colors[ImGuiCol.ButtonActive], + [ColorPalette.CurrentLineFillInactive] = colors[ImGuiCol.Button], + [ColorPalette.CurrentLineEdge] = colors[ImGuiCol.ButtonHovered], + [ColorPalette.ErrorMarker] = ErrorMarker, + [ColorPalette.ErrorText] = ErrorText, + }; +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Syntax/LogColorPalette.cs b/src/BUTR.CrashReport.Renderer.ImGui/Syntax/LogColorPalette.cs new file mode 100644 index 0000000..47bc97e --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Syntax/LogColorPalette.cs @@ -0,0 +1,22 @@ +using ImGuiColorTextEditNet; + +namespace BUTR.CrashReport.Renderer.ImGui.Syntax; + +internal class LogColorPalette : ColorPalette +{ + public static LogColorPalette Date { get; } = new(nameof(Date)); + + public static LogColorPalette Application { get; } = new(nameof(Application)); + + public static LogColorPalette Type { get; } = new(nameof(Type)); + + public static LogColorPalette Message { get; } = new(nameof(Message)); + + public static LogColorPalette Debug { get; } = new(nameof(Debug)); + public static LogColorPalette Info { get; } = new(nameof(Info)); + public static LogColorPalette Warn { get; } = new(nameof(Warn)); + public static LogColorPalette Error { get; } = new(nameof(Error)); + public static LogColorPalette Fatal { get; } = new(nameof(Fatal)); + + private LogColorPalette(string uniqueName) : base(uniqueName) { } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Syntax/PrismColorPalette.cs b/src/BUTR.CrashReport.Renderer.ImGui/Syntax/PrismColorPalette.cs new file mode 100644 index 0000000..e97669a --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Syntax/PrismColorPalette.cs @@ -0,0 +1,170 @@ +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using ImGuiColorTextEditNet; + +using System.Buffers.Text; +using System.Numerics; + +namespace BUTR.CrashReport.Renderer.ImGui.Syntax; + +internal class PrismColorPalette : ColorPalette +{ + private static Vector4 ToRgba(bool isDarkTheme, ReadOnlySpan hexColorDark, ReadOnlySpan hexColorLight) => + ToRgba(isDarkTheme ? hexColorDark : hexColorLight); + private static Vector4 ToRgba(bool isDarkTheme, ReadOnlySpan hexColorDark, ref readonly Vector4 colorLight) => + isDarkTheme ? ToRgba(hexColorDark) : colorLight; + private static Vector4 ToRgba(bool isDarkTheme, ref readonly Vector4 colorDark, ReadOnlySpan hexColorLight) => + isDarkTheme ? colorDark : ToRgba(hexColorLight); + private static ref readonly Vector4 ToRgba(bool isDarkTheme, ref readonly Vector4 colorDark, ref readonly Vector4 colorLight) => + ref isDarkTheme ? ref colorDark : ref colorLight; + + private static Vector4 ToRgba(ReadOnlySpan hexColor) + { + if (hexColor.StartsWith("#"u8)) + hexColor = hexColor.Slice(1); + + // Parse hexadecimal values for red, green, blue, and alpha components + if (!Utf8Parser.TryParse(hexColor.Slice(0, 2), out byte r, out _, 'x')) r = 0; + if (!Utf8Parser.TryParse(hexColor.Slice(2, 2), out byte g, out _, 'x')) g = 0; + if (!Utf8Parser.TryParse(hexColor.Slice(4, 2), out byte b, out _, 'x')) b = 0; + if (hexColor.Length != 8 || !Utf8Parser.TryParse(hexColor.Slice(6, 2), out byte a, out _, 'x')) a = 255; + + // Normalize the color values from 0-255 to 0-1 range + return new Vector4(r / 255f, g / 255f, b / 255f, a / 255f); + } + + public static ColorPalette FromToken(string? tokenName) + { + var all = GetAll(); + for (var i = 0; i < all.Length; i++) + { + if (all[i] is PrismColorPalette prismColorPalette && prismColorPalette._tokenName == tokenName) + return prismColorPalette; + } + return Default; + } + + public static ColorPaletteIndex DefaultOrTomorrowNight(bool isDarkTheme, TColors colors) where TColors : IRangeAccessor => CodeParser.BasePalette(isDarkTheme, colors).With(new() + { +#pragma warning disable format // @formatter:off + [Background] = ToRgba(isDarkTheme, "#2d2d2d"u8, "#f5f2f0"u8), + + [Keyword] = ToRgba(isDarkTheme, "#cc99cd"u8, "#0077aa"u8), + [BuiltIn] = ToRgba(isDarkTheme, "#cc99cd"u8, "#669900"u8), + [ClassName] = ToRgba(isDarkTheme, "#f8c555"u8, "#DD4A68"u8), + [Function] = ToRgba(isDarkTheme, "#f08d49"u8, "#DD4A68"u8), + [Boolean] = ToRgba(isDarkTheme, "#f08d49"u8, "#990055"u8), + [Number] = ToRgba(isDarkTheme, "#f08d49"u8, "#990055"u8), + [String] = ToRgba(isDarkTheme, "#7ec699"u8, "#669900"u8), + [Char] = ToRgba(isDarkTheme, "#7ec699"u8, "#669900"u8), + [Symbol] = ToRgba(isDarkTheme, "#f8c555"u8, "#990055"u8), + [Regex] = ToRgba(isDarkTheme, "#7ec699"u8, "#ee9900"u8), + [Url] = ToRgba(isDarkTheme, "#67cdcc"u8, "#9a6e3a"u8), + [Operator] = ToRgba(isDarkTheme, "#67cdcc"u8, "#9a6e3a"u8), + [Variable] = ToRgba(isDarkTheme, "#7ec699"u8, "#ee9900"u8), + [Constant] = ToRgba(isDarkTheme, "#f8c555"u8, "#990055"u8), + [Property] = ToRgba(isDarkTheme, "#f8c555"u8, "#990055"u8), + [Punctuation] = ToRgba(isDarkTheme, "#cccccc"u8, "#999999"u8), + [Important] = ToRgba(isDarkTheme, "#cc99cd"u8, "#ee9900"u8), + [Comment] = ToRgba(isDarkTheme, "#999999"u8, "#708090"u8), + + [Tag] = ToRgba(isDarkTheme, "#e2777a"u8, "#990055"u8), + [AttrName] = ToRgba(isDarkTheme, "#e2777a"u8, "#669900"u8), + [AttrValue] = ToRgba(isDarkTheme, "#7ec699"u8, "#0077aa"u8), + [Namespace] = ToRgba(isDarkTheme, "#e2777a"u8, in colors[ImGuiCol.Text]), + [Prolog] = ToRgba(isDarkTheme, "#999999"u8, "#708090"u8), + [DocType] = ToRgba(isDarkTheme, "#999999"u8, "#708090"u8), + [CData] = ToRgba(isDarkTheme, "#999999"u8, "#708090"u8), + [Entity] = ToRgba(isDarkTheme, "#67cdcc"u8, "#9a6e3a"u8), + + [Bold] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [Italic] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + + [ATRule] = ToRgba(isDarkTheme, "#cc99cd"u8, "#0077aa"u8), + [Selector] = ToRgba(isDarkTheme, "#cc99cd"u8, "#669900"u8), + + [Inserted] = ToRgba(isDarkTheme, "#008000"u8, "#669900"u8), + [Deleted] = ToRgba(isDarkTheme, "#e2777a"u8, "#990055"u8), + + [Directive] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [Preprocessor] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + + [Attribute] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [ConstructorInvocation] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [GenericMethod] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [InterpolationString] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [NamedParameter] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [Range] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [ReturnType] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [TypeExpression] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + [TypeList] = ToRgba(isDarkTheme, in colors[ImGuiCol.Text], in colors[ImGuiCol.Text]), + + //[FunctionName] = ToRgba(isDarkTheme, "#6196cc"u8) : colors[ImGuiCol.Text], +#pragma warning restore format // @formatter:on + }); + + // General Purpose + public static PrismColorPalette Keyword { get; } = new(nameof(Keyword), "keyword"); + public static PrismColorPalette BuiltIn { get; } = new(nameof(BuiltIn), "builtin"); + public static PrismColorPalette ClassName { get; } = new(nameof(ClassName), "class-name"); + public static PrismColorPalette Function { get; } = new(nameof(Function), "function"); + public static PrismColorPalette Boolean { get; } = new(nameof(Boolean), "boolean"); + public static PrismColorPalette Number { get; } = new(nameof(Number), "number"); + public static PrismColorPalette String { get; } = new(nameof(String), "string"); + public static PrismColorPalette Char { get; } = new(nameof(Char), "char"); + public static PrismColorPalette Symbol { get; } = new(nameof(Symbol), "symbol"); + public static PrismColorPalette Regex { get; } = new(nameof(Regex), "regex"); + public static PrismColorPalette Url { get; } = new(nameof(Url), "url"); + public static PrismColorPalette Operator { get; } = new(nameof(Operator), "operator"); + public static PrismColorPalette Variable { get; } = new(nameof(Variable), "variable"); + public static PrismColorPalette Constant { get; } = new(nameof(Constant), "constant"); + public static PrismColorPalette Property { get; } = new(nameof(Property), "property"); + public static PrismColorPalette Punctuation { get; } = new(nameof(Punctuation), "punctuation"); + public static PrismColorPalette Important { get; } = new(nameof(Important), "important"); + public static PrismColorPalette Comment { get; } = new(nameof(Comment), "comment"); + + // Markup + public static PrismColorPalette Tag { get; } = new(nameof(Tag), "tag"); + public static PrismColorPalette AttrName { get; } = new(nameof(AttrName), "attr-name"); + public static PrismColorPalette AttrValue { get; } = new(nameof(AttrValue), "attr-value"); + public static PrismColorPalette Namespace { get; } = new(nameof(Namespace), "namespace"); + public static PrismColorPalette Prolog { get; } = new(nameof(Prolog), "prolog"); + public static PrismColorPalette DocType { get; } = new(nameof(DocType), "doctype"); + public static PrismColorPalette CData { get; } = new(nameof(CData), "cdata"); + public static PrismColorPalette Entity { get; } = new(nameof(Entity), "entity"); + + // Document-markup + public static PrismColorPalette Bold { get; } = new(nameof(Bold), "bold"); + public static PrismColorPalette Italic { get; } = new(nameof(Italic), "italic"); + + // Stylesheets + public static PrismColorPalette ATRule { get; } = new(nameof(ATRule), "atrule"); + public static PrismColorPalette Selector { get; } = new(nameof(Selector), "selector"); + + // Diff + public static PrismColorPalette Inserted { get; } = new(nameof(Inserted), "inserted"); + public static PrismColorPalette Deleted { get; } = new(nameof(Deleted), "deleted"); + + // C-like + public static PrismColorPalette Directive { get; } = new(nameof(Directive), "directive"); + public static PrismColorPalette Preprocessor { get; } = new(nameof(Preprocessor), "preprocessor"); + + // CSharp + public static PrismColorPalette Attribute { get; } = new(nameof(Attribute), "attribute"); + public static PrismColorPalette ConstructorInvocation { get; } = new(nameof(ConstructorInvocation), "constructor-invocation"); + public static PrismColorPalette GenericMethod { get; } = new(nameof(GenericMethod), "generic-method"); + public static PrismColorPalette InterpolationString { get; } = new(nameof(InterpolationString), "interpolation-string"); + public static PrismColorPalette NamedParameter { get; } = new(nameof(NamedParameter), "named-parameter"); + public static PrismColorPalette Range { get; } = new(nameof(Range), "range"); + public static PrismColorPalette ReturnType { get; } = new(nameof(ReturnType), "return-type"); + public static PrismColorPalette TypeExpression { get; } = new(nameof(TypeExpression), "type-expression"); + public static PrismColorPalette TypeList { get; } = new(nameof(TypeList), "type-list"); + + + private readonly string _tokenName; + private PrismColorPalette(string uniqueName, string tokenName) : base(uniqueName) + { + _tokenName = tokenName; + } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/UnsafeUtils/UnsafeHelper.cs b/src/BUTR.CrashReport.Renderer.ImGui/UnsafeUtils/UnsafeHelper.cs deleted file mode 100644 index a055f87..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/UnsafeUtils/UnsafeHelper.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; - -namespace BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; - -internal static unsafe class UnsafeHelper -{ - private delegate int IndexOfSpanDelegate(ref byte searchSpace, byte value, int length); - private static readonly IndexOfSpanDelegate? _indexOfSpan = null; - /* TODO: - private static readonly IndexOfSpanDelegate? _indexOfSpan = (IndexOfSpanDelegate) Delegate.CreateDelegate( - typeof(IndexOfSpanDelegate), - typeof(Span<>).Assembly.GetType("System.SpanHelpers").GetMethod("IndexOfSpan", BindingFlags.Static | BindingFlags.NonPublic, null, [typeof(byte).MakeByRefType(), typeof(byte), typeof(int)], null)! - ); - */ - - public static ref readonly T NullRef() where T : unmanaged => ref *(T*) null; - - public static Span CreateSpan(ref T reference, int length) => new(Unsafe.AsPointer(ref reference), length); - - public static ReadOnlySpan CreateReadOnlySpanFromNullTerminated(ref readonly byte valueRef) - { - return CreateReadOnlySpanFromNullTerminated((byte*) Unsafe.AsPointer(ref Unsafe.AsRef(in valueRef))); - } - - public static ReadOnlySpan CreateReadOnlySpanFromNullTerminated(byte* valuePtr) - { - if (valuePtr is null) - return new ReadOnlySpan(); - - if (_indexOfSpan is not null) - return new ReadOnlySpan(valuePtr, _indexOfSpan(ref *valuePtr, 0, int.MaxValue)); - -#if NET6_0_OR_GREATER - return System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpanFromNullTerminated(valuePtr); -#endif - - for (var i = 0; i < 512; i++) - { - var b = valuePtr[i]; - if (b == 0) - return new ReadOnlySpan(valuePtr, i); - } - - return new ReadOnlySpan(); - } - - public static string ToString(ReadOnlySpan utf8) - { -#if NET6_0_OR_GREATER - fixed (byte* utf8Ptr = utf8) - { - return string.Create(utf8.Length, (IntPtr) utf8Ptr, static (utf16, utf8Ptr) => - { - var utf8 = new ReadOnlySpan((byte*) utf8Ptr, utf16.Length); - System.Text.Unicode.Utf8.ToUtf16(utf8, utf16, out _, out _); - }); - } -#endif - - fixed (byte* utf8Ptr = utf8) - { - return Encoding.UTF8.GetString(utf8Ptr, utf8.Length); - } - } - - public static int Utf16ToUtf8(string utf16, Span utf8) - { - if (string.IsNullOrEmpty(utf16) || utf8.IsEmpty) - return 0; - -#if NET6_0_OR_GREATER - System.Text.Unicode.Utf8.FromUtf16(utf16, utf8, out _, out var bytesWritten); - return bytesWritten; -#endif - - fixed (char* utf16Ptr = utf16) - fixed (byte* utf8Ptr = utf8) - { - return Encoding.UTF8.GetBytes(utf16Ptr, utf16.Length, utf8Ptr, utf8.Length); - } - } - - public static int Utf16ToUtf8(ReadOnlySpan utf16, Span utf8) - { - if (utf16.IsEmpty || utf8.IsEmpty) - return 0; - -#if NET6_0_OR_GREATER - System.Text.Unicode.Utf8.FromUtf16(utf16, utf8, out _, out var bytesWritten); - return bytesWritten; -#endif - - fixed (char* utf16Ptr = utf16) - fixed (byte* utf8Ptr = utf8) - { - return Encoding.UTF8.GetBytes(utf16Ptr, utf16.Length, utf8Ptr, utf8.Length); - } - } - - public static byte[] ToUtf8Array(string value) - { - if (string.IsNullOrEmpty(value)) - return []; - - var length = Encoding.UTF8.GetMaxByteCount(value.Length) + 1; - var array = new byte[length]; - - var charSpan = value.AsSpan(); - var arraySpan = array.AsSpan(); - - var lengthWritten = Utf16ToUtf8(charSpan, arraySpan); - array[lengthWritten] = 0; - return array; - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/UnsafeUtils/Utf8ZPtr.cs b/src/BUTR.CrashReport.Renderer.ImGui/UnsafeUtils/Utf8ZPtr.cs deleted file mode 100644 index 181748d..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/UnsafeUtils/Utf8ZPtr.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace BUTR.CrashReport.Renderer.ImGui.UnsafeUtils; - -/// -/// Null-terminated UTF-8 string pointer. -/// -internal readonly struct Utf8ZPtr -{ - public static Span AsSpan(ref Utf8ZPtr reference, int length) => UnsafeHelper.CreateSpan(ref reference, length); - public static ref readonly Utf8ZPtr AsRef(ReadOnlySpan stackallocArray) => ref MemoryMarshal.GetReference(stackallocArray); - - - private readonly IntPtr NativePtr; - public unsafe ref readonly byte Data => ref Unsafe.AsRef(NativePtr.ToPointer()); - - public unsafe Utf8ZPtr(ref byte reference) { NativePtr = new IntPtr(Unsafe.AsPointer(ref reference)); } - public Utf8ZPtr(ref readonly ReadOnlySpan span) : this(ref MemoryMarshal.GetReference(span)) { } - public Utf8ZPtr(ref readonly Span span) : this(ref MemoryMarshal.GetReference(span)) { } - public Utf8ZPtr(ref readonly byte[] array) : this(ref MemoryMarshal.GetReference(array.AsSpan())) { } - - public unsafe ReadOnlySpan AsUtf8(int length = -1) => - length != -1 ? new ReadOnlySpan(NativePtr.ToPointer(), length) : UnsafeHelper.CreateReadOnlySpanFromNullTerminated(in Data); - - public string AsUtf16String() => UnsafeHelper.ToString(AsUtf8()); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Utils/ColorUtils.cs b/src/BUTR.CrashReport.Renderer.ImGui/Utils/ColorUtils.cs new file mode 100644 index 0000000..62a3781 --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.ImGui/Utils/ColorUtils.cs @@ -0,0 +1,12 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace BUTR.CrashReport.Renderer.ImGui.Utils; + +internal static class ColorUtils +{ + private const MethodImplOptions AggressiveOptimization = (MethodImplOptions) 512; + + [MethodImpl(MethodImplOptions.AggressiveInlining | AggressiveOptimization)] + public static Vector4 FromColor(byte r, byte g, byte b, byte a) => new((float) r / 255f, (float) g / 255f, (float) b / 255f, (float) a / 255f); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.ImGui/Utils/IListEnumerator.cs b/src/BUTR.CrashReport.Renderer.ImGui/Utils/IListEnumerator.cs deleted file mode 100644 index 1aeda7d..0000000 --- a/src/BUTR.CrashReport.Renderer.ImGui/Utils/IListEnumerator.cs +++ /dev/null @@ -1,26 +0,0 @@ -using HonkPerf.NET.RefLinq.Enumerators; - -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace BUTR.CrashReport.Renderer.ImGui.Utils; - -internal struct IListEnumerator : IRefEnumerable -{ - private readonly IList _list; - private int _curr; - - public IListEnumerator(IList list) - { - _list = list; - _curr = -1; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - _curr++; - return _curr < _list.Count; - } - - public T Current => _list[_curr]; -} \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.WinForms/BUTR.CrashReport.Renderer.WinForms.csproj b/src/BUTR.CrashReport.Renderer.WinForms/BUTR.CrashReport.Renderer.WinForms.csproj index 9f1646f..c2f58f2 100644 --- a/src/BUTR.CrashReport.Renderer.WinForms/BUTR.CrashReport.Renderer.WinForms.csproj +++ b/src/BUTR.CrashReport.Renderer.WinForms/BUTR.CrashReport.Renderer.WinForms.csproj @@ -2,19 +2,20 @@ net45;netcoreapp3.0;net5.0-windows; + latest + enable + true true - preview - enable BUTR.CrashReport.Renderer.WinForms BUTR.CrashReport.Renderer.WinForms - Contains the renderer for creating the crash report + Contains the WinForms (HTML) renderer for showing the crash report MIT https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png - butr crash report bannerlord + butr crash report diff --git a/src/BUTR.CrashReport.Renderer.WinForms/CrashReportWinForms.cs b/src/BUTR.CrashReport.Renderer.WinForms/CrashReportWinForms.cs index c8c6648..db27b84 100644 --- a/src/BUTR.CrashReport.Renderer.WinForms/CrashReportWinForms.cs +++ b/src/BUTR.CrashReport.Renderer.WinForms/CrashReportWinForms.cs @@ -1,5 +1,7 @@ using BUTR.CrashReport.Models; +using JetBrains.Annotations; + using System; using System.Collections.Generic; using System.Diagnostics; @@ -9,88 +11,154 @@ namespace BUTR.CrashReport.Renderer.WinForms; +public class ScriptObject +{ + private static async Task SetClipboardTextAsync(string text) + { + var completionSource = new TaskCompletionSource(); + var staThread = new Thread(() => + { + try + { + var dataObject = new DataObject(); + dataObject.SetText(text, TextDataFormat.Text); + Clipboard.SetDataObject(dataObject, true, 10, 100); + completionSource.SetResult(true); + } + catch (Exception) + { + completionSource.SetResult(false); + } + }); + staThread.SetApartmentState(ApartmentState.STA); + staThread.Start(); + return await completionSource.Task; + } + + [UsedImplicitly] + public bool IncludeMiniDump { get; set; } + [UsedImplicitly] + public bool IncludeSaveFile { get; set; } + [UsedImplicitly] + public bool IncludeScreenshot { get; set; } + + private readonly Form _form; + private readonly CrashReportModel _crashReport; + private readonly ICollection _logSources; + private readonly ICrashReportRendererUtilities _crashReportRendererUtilities; + private readonly string _reportInHtml; + + public ScriptObject(Form form, CrashReportModel crashReport, ICollection logSources, ICrashReportRendererUtilities crashReportRendererUtilities, string reportInHtml) + { + _form = form; + _crashReport = crashReport; + _logSources = logSources; + _crashReportRendererUtilities = crashReportRendererUtilities; + _reportInHtml = reportInHtml; + } + + [UsedImplicitly] + public void SetIncludeMiniDump(bool value) => IncludeMiniDump = value; + [UsedImplicitly] + public void SetIncludeSaveFile(bool value) => IncludeSaveFile = value; + [UsedImplicitly] + public void SetIncludeScreenshot(bool value) => IncludeScreenshot = value; + + [UsedImplicitly] + public async void CopyAsHTML() + { + if (!await SetClipboardTextAsync(_reportInHtml)) + MessageBox.Show("Failed to copy the HTML content to the clipboard!", "Error!"); + } + + [UsedImplicitly] + public void UploadReport() => _crashReportRendererUtilities.Upload(_crashReport, _logSources); + + [UsedImplicitly] + public void SaveReport() => _crashReportRendererUtilities.SaveAsHtml(_crashReport, _logSources, IncludeMiniDump, IncludeSaveFile, IncludeScreenshot); + + [UsedImplicitly] + public void Close() => _form.Close(); +} + public partial class CrashReportWinForms : Form { // https://gist.github.com/eikes/2299607 // Copyright: Eike Send http://eike.se/nd // License: MIT License - private static readonly string ScriptText = """ -if (!document.getElementsByClassName) { - document.getElementsByClassName = function(search) { - var d = document, elements, pattern, i, results = []; - if (d.querySelectorAll) { // IE8 - return d.querySelectorAll("." + search); - } - if (d.evaluate) { // IE6, IE7 - pattern = ".//*[contains(concat(' ', @class, ' '), ' " + search + " ')]"; - elements = d.evaluate(pattern, d, null, 0, null); - while ((i = elements.iterateNext())) { - results.push(i); - } - } else { - elements = d.getElementsByTagName("*"); - pattern = new RegExp("(^|\\s)" + search + "(\\s|$)"); - for (i = 0; i < elements.length; i++) { - if ( pattern.test(elements[i].className) ) { - results.push(elements[i]); - } - } - } - return results; - } -} -function handleIncludeMiniDump(cb) { - window.external.SetIncludeMiniDump(cb.checked); -} -function handleIncludeSaveFile(cb) { - window.external.SetIncludeSaveFile(cb.checked); -} -function handleIncludeScreenshot(cb) { - window.external.SetIncludeScreenshot(cb.checked); -} -"""; - - private static readonly string TableText = """ - - - - - - - - - - - -
-

Intercepted an exception!

-
- - - - -
- - - - - - - -
-Clicking 'Close Report' will continue with the Game's error report mechanism. -
-"""; + private const string ScriptText = """ + if (!document.getElementsByClassName) { + document.getElementsByClassName = function(search) { + var d = document, elements, pattern, i, results = []; + if (d.querySelectorAll) { // IE8 + return d.querySelectorAll("." + search); + } + if (d.evaluate) { // IE6, IE7 + pattern = ".//*[contains(concat(' ', @class, ' '), ' " + search + " ')]"; + elements = d.evaluate(pattern, d, null, 0, null); + while ((i = elements.iterateNext())) { + results.push(i); + } + } else { + elements = d.getElementsByTagName("*"); + pattern = new RegExp("(^|\\s)" + search + "(\\s|$)"); + for (i = 0; i < elements.length; i++) { + if ( pattern.test(elements[i].className) ) { + results.push(elements[i]); + } + } + } + return results; + } + } + function handleIncludeMiniDump(cb) { + window.external.SetIncludeMiniDump(cb.checked); + } + function handleIncludeSaveFile(cb) { + window.external.SetIncludeSaveFile(cb.checked); + } + function handleIncludeScreenshot(cb) { + window.external.SetIncludeScreenshot(cb.checked); + } + """; + + private const string TableText = """ + + + + + + + + + + + +
+

Intercepted an exception!

+
+ + + + +
+ + + + + + + +
+ Clicking 'Close Report' will continue with the Game's error report mechanism. +
+ """; private ICrashReportRendererUtilities CrashReportRendererUtilities { get; } private CrashReportModel CrashReport { get; } private ICollection LogSources { get; } private string ReportInHtml { get; } - public bool IncludeMiniDump { get; set; } - public bool IncludeSaveFile { get; set; } - public bool IncludeScreenshot { get; set; } - public CrashReportWinForms(CrashReportModel crashReport, ICollection logSources, ICrashReportRendererUtilities crashReportRendererUtilities) { CrashReportRendererUtilities = crashReportRendererUtilities; @@ -119,17 +187,11 @@ public CrashReportWinForms(CrashReportModel crashReport, ICollection IncludeMiniDump = value; - public void SetIncludeSaveFile(bool value) => IncludeSaveFile = value; - public void SetIncludeScreenshot(bool value) => IncludeScreenshot = value; - - public async void CopyAsHTML() + protected override void OnLoad(EventArgs e) { - if (!await SetClipboardTextAsync(ReportInHtml)) - MessageBox.Show("Failed to copy the HTML content to the clipboard!", "Error!"); + HtmlRender.ObjectForScripting = new ScriptObject(this, CrashReport, LogSources, CrashReportRendererUtilities, ReportInHtml); + base.OnLoad(e); } - public void UploadReport() => CrashReportRendererUtilities.Upload(CrashReport, LogSources); - public void SaveReport() => CrashReportRendererUtilities.SaveCrashReportAsHtml(CrashReport, LogSources, IncludeMiniDump, IncludeSaveFile, IncludeScreenshot); private void HtmlRender_Navigating(object sender, WebBrowserNavigatingEventArgs e) { @@ -142,26 +204,4 @@ private void HtmlRender_Navigating(object sender, WebBrowserNavigatingEventArgs private static bool UriIsValid(string url) => Uri.TryCreate(url, UriKind.Absolute, out var uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); - - private static async Task SetClipboardTextAsync(string text) - { - var completionSource = new TaskCompletionSource(); - var staThread = new Thread(() => - { - try - { - var dataObject = new DataObject(); - dataObject.SetText(text, TextDataFormat.Text); - Clipboard.SetDataObject(dataObject, true, 10, 100); - completionSource.SetResult(true); - } - catch (Exception) - { - completionSource.SetResult(false); - } - }); - staThread.SetApartmentState(ApartmentState.STA); - staThread.Start(); - return await completionSource.Task; - } } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.WinForms/ICrashReportRendererUtilities.cs b/src/BUTR.CrashReport.Renderer.WinForms/ICrashReportRendererUtilities.cs index 467ceb8..f583af5 100644 --- a/src/BUTR.CrashReport.Renderer.WinForms/ICrashReportRendererUtilities.cs +++ b/src/BUTR.CrashReport.Renderer.WinForms/ICrashReportRendererUtilities.cs @@ -1,6 +1,6 @@ -using System; -using BUTR.CrashReport.Models; +using BUTR.CrashReport.Models; +using System; using System.Collections.Generic; namespace BUTR.CrashReport.Renderer.WinForms; @@ -9,12 +9,10 @@ namespace BUTR.CrashReport.Renderer.WinForms; public enum CrashReportRendererCapabilities { None = 0, - SaveAsHtml = 1 << 0, - CopyAsHtml = 1 << 1, - Upload = 1 << 2, - HasSaveFiles = 1 << 3, - HasScreenshots = 1 << 4, - HasMiniDump = 1 << 5, + HasSaveFiles = 1 << 1, + HasScreenshots = 1 << 2, + HasMiniDump = 1 << 3, + Upload = 1 << 4, } public interface ICrashReportRendererUtilities @@ -25,5 +23,6 @@ public interface ICrashReportRendererUtilities string CopyAsHtml(CrashReportModel crashReport, ICollection logSources); - void SaveCrashReportAsHtml(CrashReportModel crashReport, ICollection logSources, bool addMiniDump, bool addLatestSave, bool addScreenshots); + void SaveAsHtml(CrashReportModel crashReport, ICollection logSources, bool addMiniDump, bool addLatestSave, bool addScreenshots); + void SaveAsZip(CrashReportModel crashReport, ICollection logSources); } \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.WinForms/Polyfill/UsedImplicitlyAttribute.cs b/src/BUTR.CrashReport.Renderer.WinForms/Polyfill/UsedImplicitlyAttribute.cs new file mode 100644 index 0000000..20b223c --- /dev/null +++ b/src/BUTR.CrashReport.Renderer.WinForms/Polyfill/UsedImplicitlyAttribute.cs @@ -0,0 +1,6 @@ +using System; + +// ReSharper disable once CheckNamespace +namespace JetBrains.Annotations; + +internal class UsedImplicitlyAttribute : Attribute; \ No newline at end of file diff --git a/src/BUTR.CrashReport.Renderer.Zip/BUTR.CrashReport.Renderer.Zip.csproj b/src/BUTR.CrashReport.Renderer.Zip/BUTR.CrashReport.Renderer.Zip.csproj index cd3b242..5a26f94 100644 --- a/src/BUTR.CrashReport.Renderer.Zip/BUTR.CrashReport.Renderer.Zip.csproj +++ b/src/BUTR.CrashReport.Renderer.Zip/BUTR.CrashReport.Renderer.Zip.csproj @@ -3,26 +3,18 @@ net45;netstandard2.0 enable - preview + latest BUTR.CrashReport.Renderer.Zip BUTR.CrashReport.Renderer.Zip - Contains the zip renderer for creating the crash report + Contains the zip renderer for storing the crash report MIT https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png butr crash report bannerlord - - - - - - - - diff --git a/src/BUTR.CrashReport.Renderer.Zip/CrashReportZip.cs b/src/BUTR.CrashReport.Renderer.Zip/CrashReportZip.cs index d66af6d..1ac09d7 100644 --- a/src/BUTR.CrashReport.Renderer.Zip/CrashReportZip.cs +++ b/src/BUTR.CrashReport.Renderer.Zip/CrashReportZip.cs @@ -3,10 +3,18 @@ namespace BUTR.CrashReport.Renderer.Zip; +public class CrashReportZipOptions +{ + public string SaveExtension { get; set; } = "sav"; + public string ScreenshotExtension { get; set; } = "jpg"; +} + public static class CrashReportZip { - public static Stream Build(Stream crashReportJson, Stream logsJson, Stream miniDump, Stream saveFile, Stream screenshot) + public static Stream Build(Stream crashReportJson, Stream logsJson, Stream miniDump, Stream saveFile, Stream screenshot, CrashReportZipOptions? options = default) { + options ??= new CrashReportZipOptions(); + var ms = new MemoryStream(); using var archive = new ZipArchive(ms, ZipArchiveMode.Create, true); @@ -35,7 +43,7 @@ public static Stream Build(Stream crashReportJson, Stream logsJson, Stream miniD if (saveFile != Stream.Null) { - var saveFileFile = archive.CreateEntry("save.sav"); + var saveFileFile = archive.CreateEntry($"save.{options.SaveExtension}"); using var saveFileStream = saveFileFile.Open(); saveFile.Seek(0, SeekOrigin.Begin); saveFile.CopyTo(saveFileStream); @@ -43,7 +51,7 @@ public static Stream Build(Stream crashReportJson, Stream logsJson, Stream miniD if (screenshot != Stream.Null) { - var screenshotFile = archive.CreateEntry("screenshot.bmp"); + var screenshotFile = archive.CreateEntry($"screenshot.{options.ScreenshotExtension}"); using var screenshotStream = screenshotFile.Open(); screenshot.Seek(0, SeekOrigin.Begin); screenshot.CopyTo(screenshotStream); diff --git a/src/BUTR.CrashReport.SDL2/BUTR.CrashReport.SDL2.csproj b/src/BUTR.CrashReport.SDL2/BUTR.CrashReport.SDL2.csproj new file mode 100644 index 0000000..b84522d --- /dev/null +++ b/src/BUTR.CrashReport.SDL2/BUTR.CrashReport.SDL2.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + enable + enable + latest + true + SDL2 + + + + + + + + diff --git a/src/BUTR.CrashReport.SDL2/SDL.cs b/src/BUTR.CrashReport.SDL2/SDL.cs new file mode 100644 index 0000000..eb1363d --- /dev/null +++ b/src/BUTR.CrashReport.SDL2/SDL.cs @@ -0,0 +1,1953 @@ +#region License +/* SDL2# - C# Wrapper for SDL2 + * + * Copyright (c) 2013-2021 Ethan Lee. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Ethan "flibitijibibo" Lee + * + */ +#endregion + +#region Using Statements + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +#endregion + +namespace SDL2; + +public static unsafe class SDL +{ + #region SDL2# Variables + + private const string nativeLibName = "SDL"; + + #endregion + + #region UTF8 Marshaling + + /* Used for stack allocated string marshaling. */ + internal static int Utf8Size(string? str) + { + if (str == null) + { + return 0; + } + return (str.Length * 4) + 1; + } + internal static byte* Utf8Encode(string? str, byte* buffer, int bufferSize) + { + if (str == null) + { + return (byte*) 0; + } + fixed (char* strPtr = str) + { + Encoding.UTF8.GetBytes(strPtr, str.Length + 1, buffer, bufferSize); + } + return buffer; + } + + /* This is public because SDL_DropEvent needs it! */ + public static string? UTF8_ToManaged(IntPtr s) + { + if (s == IntPtr.Zero) + { + return null; + } + + /* We get to do strlen ourselves! */ + byte* ptr = (byte*) s; + while (*ptr != 0) + { + ptr++; + } + + /* TODO: This #ifdef is only here because the equivalent + * .NET 2.0 constructor appears to be less efficient? + * Here's the pretty version, maybe steal this instead: + * + string result = new string( + (sbyte*) s, // Also, why sbyte??? + 0, + (int) (ptr - (byte*) s), + System.Text.Encoding.UTF8 + ); + * See the CoreCLR source for more info. + * -flibit + */ +#if NETSTANDARD2_0 + /* Modern C# lets you just send the byte*, nice! */ + string result = System.Text.Encoding.UTF8.GetString( + (byte*) s, + (int) (ptr - (byte*) s) + ); +#else + /* Old C# requires an extra memcpy, bleh! */ + int len = (int) (ptr - (byte*) s); + if (len == 0) + { + return string.Empty; + } + char* chars = stackalloc char[len]; + int strLen = System.Text.Encoding.UTF8.GetChars((byte*) s, len, chars, len); + string result = new string(chars, 0, strLen); +#endif + + return result; + } + + #endregion + + #region SDL_stdinc.h + + public enum SDL_bool + { + SDL_FALSE = 0, + SDL_TRUE = 1 + } + + #endregion + + #region SDL.h + + public const uint SDL_INIT_TIMER = 0x00000001; + public const uint SDL_INIT_AUDIO = 0x00000010; + public const uint SDL_INIT_VIDEO = 0x00000020; + public const uint SDL_INIT_JOYSTICK = 0x00000200; + public const uint SDL_INIT_HAPTIC = 0x00001000; + public const uint SDL_INIT_GAMECONTROLLER = 0x00002000; + public const uint SDL_INIT_EVENTS = 0x00004000; + public const uint SDL_INIT_SENSOR = 0x00008000; + public const uint SDL_INIT_NOPARACHUTE = 0x00100000; + public const uint SDL_INIT_EVERYTHING = ( + SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | + SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | + SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR + ); + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_Init(uint flags); + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_InitSubSystem(uint flags); + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_Quit(); + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_QuitSubSystem(uint flags); + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern uint SDL_WasInit(uint flags); + + #endregion + + #region SDL_hints.h + + public const string SDL_HINT_FRAMEBUFFER_ACCELERATION = + "SDL_FRAMEBUFFER_ACCELERATION"; + public const string SDL_HINT_RENDER_DRIVER = + "SDL_RENDER_DRIVER"; + public const string SDL_HINT_RENDER_OPENGL_SHADERS = + "SDL_RENDER_OPENGL_SHADERS"; + public const string SDL_HINT_RENDER_DIRECT3D_THREADSAFE = + "SDL_RENDER_DIRECT3D_THREADSAFE"; + public const string SDL_HINT_RENDER_VSYNC = + "SDL_RENDER_VSYNC"; + public const string SDL_HINT_VIDEO_X11_XVIDMODE = + "SDL_VIDEO_X11_XVIDMODE"; + public const string SDL_HINT_VIDEO_X11_XINERAMA = + "SDL_VIDEO_X11_XINERAMA"; + public const string SDL_HINT_VIDEO_X11_XRANDR = + "SDL_VIDEO_X11_XRANDR"; + public const string SDL_HINT_GRAB_KEYBOARD = + "SDL_GRAB_KEYBOARD"; + public const string SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = + "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS"; + public const string SDL_HINT_IDLE_TIMER_DISABLED = + "SDL_IOS_IDLE_TIMER_DISABLED"; + public const string SDL_HINT_ORIENTATIONS = + "SDL_IOS_ORIENTATIONS"; + public const string SDL_HINT_XINPUT_ENABLED = + "SDL_XINPUT_ENABLED"; + public const string SDL_HINT_GAMECONTROLLERCONFIG = + "SDL_GAMECONTROLLERCONFIG"; + public const string SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS = + "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"; + public const string SDL_HINT_ALLOW_TOPMOST = + "SDL_ALLOW_TOPMOST"; + public const string SDL_HINT_TIMER_RESOLUTION = + "SDL_TIMER_RESOLUTION"; + public const string SDL_HINT_RENDER_SCALE_QUALITY = + "SDL_RENDER_SCALE_QUALITY"; + + /* Only available in SDL 2.0.1 or higher. */ + public const string SDL_HINT_VIDEO_HIGHDPI_DISABLED = + "SDL_VIDEO_HIGHDPI_DISABLED"; + + /* Only available in SDL 2.0.2 or higher. */ + public const string SDL_HINT_CTRL_CLICK_EMULATE_RIGHT_CLICK = + "SDL_CTRL_CLICK_EMULATE_RIGHT_CLICK"; + public const string SDL_HINT_VIDEO_WIN_D3DCOMPILER = + "SDL_VIDEO_WIN_D3DCOMPILER"; + public const string SDL_HINT_MOUSE_RELATIVE_MODE_WARP = + "SDL_MOUSE_RELATIVE_MODE_WARP"; + public const string SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT = + "SDL_VIDEO_WINDOW_SHARE_PIXEL_FORMAT"; + public const string SDL_HINT_VIDEO_ALLOW_SCREENSAVER = + "SDL_VIDEO_ALLOW_SCREENSAVER"; + public const string SDL_HINT_ACCELEROMETER_AS_JOYSTICK = + "SDL_ACCELEROMETER_AS_JOYSTICK"; + public const string SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES = + "SDL_VIDEO_MAC_FULLSCREEN_SPACES"; + + /* Only available in SDL 2.0.3 or higher. */ + public const string SDL_HINT_WINRT_PRIVACY_POLICY_URL = + "SDL_WINRT_PRIVACY_POLICY_URL"; + public const string SDL_HINT_WINRT_PRIVACY_POLICY_LABEL = + "SDL_WINRT_PRIVACY_POLICY_LABEL"; + public const string SDL_HINT_WINRT_HANDLE_BACK_BUTTON = + "SDL_WINRT_HANDLE_BACK_BUTTON"; + + /* Only available in SDL 2.0.4 or higher. */ + public const string SDL_HINT_NO_SIGNAL_HANDLERS = + "SDL_NO_SIGNAL_HANDLERS"; + public const string SDL_HINT_IME_INTERNAL_EDITING = + "SDL_IME_INTERNAL_EDITING"; + public const string SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH = + "SDL_ANDROID_SEPARATE_MOUSE_AND_TOUCH"; + public const string SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT = + "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT"; + public const string SDL_HINT_THREAD_STACK_SIZE = + "SDL_THREAD_STACK_SIZE"; + public const string SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN = + "SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN"; + public const string SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP = + "SDL_WINDOWS_ENABLE_MESSAGELOOP"; + public const string SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 = + "SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"; + public const string SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING = + "SDL_XINPUT_USE_OLD_JOYSTICK_MAPPING"; + public const string SDL_HINT_MAC_BACKGROUND_APP = + "SDL_MAC_BACKGROUND_APP"; + public const string SDL_HINT_VIDEO_X11_NET_WM_PING = + "SDL_VIDEO_X11_NET_WM_PING"; + public const string SDL_HINT_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION = + "SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"; + public const string SDL_HINT_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION = + "SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"; + + /* Only available in 2.0.5 or higher. */ + public const string SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH = + "SDL_MOUSE_FOCUS_CLICKTHROUGH"; + public const string SDL_HINT_BMP_SAVE_LEGACY_FORMAT = + "SDL_BMP_SAVE_LEGACY_FORMAT"; + public const string SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING = + "SDL_WINDOWS_DISABLE_THREAD_NAMING"; + public const string SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION = + "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION"; + + /* Only available in 2.0.6 or higher. */ + public const string SDL_HINT_AUDIO_RESAMPLING_MODE = + "SDL_AUDIO_RESAMPLING_MODE"; + public const string SDL_HINT_RENDER_LOGICAL_SIZE_MODE = + "SDL_RENDER_LOGICAL_SIZE_MODE"; + public const string SDL_HINT_MOUSE_NORMAL_SPEED_SCALE = + "SDL_MOUSE_NORMAL_SPEED_SCALE"; + public const string SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE = + "SDL_MOUSE_RELATIVE_SPEED_SCALE"; + public const string SDL_HINT_TOUCH_MOUSE_EVENTS = + "SDL_TOUCH_MOUSE_EVENTS"; + public const string SDL_HINT_WINDOWS_INTRESOURCE_ICON = + "SDL_WINDOWS_INTRESOURCE_ICON"; + public const string SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL = + "SDL_WINDOWS_INTRESOURCE_ICON_SMALL"; + + /* Only available in 2.0.8 or higher. */ + public const string SDL_HINT_IOS_HIDE_HOME_INDICATOR = + "SDL_IOS_HIDE_HOME_INDICATOR"; + public const string SDL_HINT_TV_REMOTE_AS_JOYSTICK = + "SDL_TV_REMOTE_AS_JOYSTICK"; + public const string SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR = + "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"; + + /* Only available in 2.0.9 or higher. */ + public const string SDL_HINT_MOUSE_DOUBLE_CLICK_TIME = + "SDL_MOUSE_DOUBLE_CLICK_TIME"; + public const string SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS = + "SDL_MOUSE_DOUBLE_CLICK_RADIUS"; + public const string SDL_HINT_JOYSTICK_HIDAPI = + "SDL_JOYSTICK_HIDAPI"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS4 = + "SDL_JOYSTICK_HIDAPI_PS4"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE = + "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"; + public const string SDL_HINT_JOYSTICK_HIDAPI_STEAM = + "SDL_JOYSTICK_HIDAPI_STEAM"; + public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH = + "SDL_JOYSTICK_HIDAPI_SWITCH"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX = + "SDL_JOYSTICK_HIDAPI_XBOX"; + public const string SDL_HINT_ENABLE_STEAM_CONTROLLERS = + "SDL_ENABLE_STEAM_CONTROLLERS"; + public const string SDL_HINT_ANDROID_TRAP_BACK_BUTTON = + "SDL_ANDROID_TRAP_BACK_BUTTON"; + + /* Only available in 2.0.10 or higher. */ + public const string SDL_HINT_MOUSE_TOUCH_EVENTS = + "SDL_MOUSE_TOUCH_EVENTS"; + public const string SDL_HINT_GAMECONTROLLERCONFIG_FILE = + "SDL_GAMECONTROLLERCONFIG_FILE"; + public const string SDL_HINT_ANDROID_BLOCK_ON_PAUSE = + "SDL_ANDROID_BLOCK_ON_PAUSE"; + public const string SDL_HINT_RENDER_BATCHING = + "SDL_RENDER_BATCHING"; + public const string SDL_HINT_EVENT_LOGGING = + "SDL_EVENT_LOGGING"; + public const string SDL_HINT_WAVE_RIFF_CHUNK_SIZE = + "SDL_WAVE_RIFF_CHUNK_SIZE"; + public const string SDL_HINT_WAVE_TRUNCATION = + "SDL_WAVE_TRUNCATION"; + public const string SDL_HINT_WAVE_FACT_CHUNK = + "SDL_WAVE_FACT_CHUNK"; + + /* Only available in 2.0.11 or higher. */ + public const string SDL_HINT_VIDO_X11_WINDOW_VISUALID = + "SDL_VIDEO_X11_WINDOW_VISUALID"; + public const string SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS = + "SDL_GAMECONTROLLER_USE_BUTTON_LABELS"; + public const string SDL_HINT_VIDEO_EXTERNAL_CONTEXT = + "SDL_VIDEO_partialAL_CONTEXT"; + public const string SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE = + "SDL_JOYSTICK_HIDAPI_GAMECUBE"; + public const string SDL_HINT_DISPLAY_USABLE_BOUNDS = + "SDL_DISPLAY_USABLE_BOUNDS"; + public const string SDL_HINT_VIDEO_X11_FORCE_EGL = + "SDL_VIDEO_X11_FORCE_EGL"; + public const string SDL_HINT_GAMECONTROLLERTYPE = + "SDL_GAMECONTROLLERTYPE"; + + /* Only available in 2.0.14 or higher. */ + public const string SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT = + "SDL_JOYSTICK_HIDAPI_CORRELATE_XINPUT"; /* NOTE: This was removed in 2.0.16. */ + public const string SDL_HINT_JOYSTICK_RAWINPUT = + "SDL_JOYSTICK_RAWINPUT"; + public const string SDL_HINT_AUDIO_DEVICE_APP_NAME = + "SDL_AUDIO_DEVICE_APP_NAME"; + public const string SDL_HINT_AUDIO_DEVICE_STREAM_NAME = + "SDL_AUDIO_DEVICE_STREAM_NAME"; + public const string SDL_HINT_PREFERRED_LOCALES = + "SDL_PREFERRED_LOCALES"; + public const string SDL_HINT_THREAD_PRIORITY_POLICY = + "SDL_THREAD_PRIORITY_POLICY"; + public const string SDL_HINT_EMSCRIPTEN_ASYNCIFY = + "SDL_EMSCRIPTEN_ASYNCIFY"; + public const string SDL_HINT_LINUX_JOYSTICK_DEADZONES = + "SDL_LINUX_JOYSTICK_DEADZONES"; + public const string SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO = + "SDL_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS5 = + "SDL_JOYSTICK_HIDAPI_PS5"; + public const string SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL = + "SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL"; + public const string SDL_HINT_JOYSTICK_THREAD = + "SDL_JOYSTICK_THREAD"; + public const string SDL_HINT_AUTO_UPDATE_JOYSTICKS = + "SDL_AUTO_UPDATE_JOYSTICKS"; + public const string SDL_HINT_AUTO_UPDATE_SENSORS = + "SDL_AUTO_UPDATE_SENSORS"; + public const string SDL_HINT_MOUSE_RELATIVE_SCALING = + "SDL_MOUSE_RELATIVE_SCALING"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE = + "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"; + + /* Only available in 2.0.16 or higher. */ + public const string SDL_HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS = + "SDL_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS"; + public const string SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL = + "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED = + "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"; + public const string SDL_HINT_WINDOWS_USE_D3D9EX = + "SDL_WINDOWS_USE_D3D9EX"; + public const string SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS = + "SDL_JOYSTICK_HIDAPI_JOY_CONS"; + public const string SDL_HINT_JOYSTICK_HIDAPI_STADIA = + "SDL_JOYSTICK_HIDAPI_STADIA"; + public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED = + "SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED"; + public const string SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED = + "SDL_ALLOW_ALT_TAB_WHILE_GRABBED"; + public const string SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER = + "SDL_KMSDRM_REQUIRE_DRM_MASTER"; + public const string SDL_HINT_AUDIO_DEVICE_STREAM_ROLE = + "SDL_AUDIO_DEVICE_STREAM_ROLE"; + public const string SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT = + "SDL_X11_FORCE_OVERRIDE_REDIRECT"; + public const string SDL_HINT_JOYSTICK_HIDAPI_LUNA = + "SDL_JOYSTICK_HIDAPI_LUNA"; + public const string SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT = + "SDL_JOYSTICK_RAWINPUT_CORRELATE_XINPUT"; + public const string SDL_HINT_AUDIO_INCLUDE_MONITORS = + "SDL_AUDIO_INCLUDE_MONITORS"; + public const string SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR = + "SDL_VIDEO_WAYLAND_ALLOW_LIBDECOR"; + + /* Only available in 2.0.18 or higher. */ + public const string SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY = + "SDL_VIDEO_EGL_ALLOW_TRANSPARENCY"; + public const string SDL_HINT_APP_NAME = + "SDL_APP_NAME"; + public const string SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME = + "SDL_SCREENSAVER_INHIBIT_ACTIVITY_NAME"; + public const string SDL_HINT_IME_SHOW_UI = + "SDL_IME_SHOW_UI"; + public const string SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN = + "SDL_WINDOW_NO_ACTIVATION_WHEN_SHOWN"; + public const string SDL_HINT_POLL_SENTINEL = + "SDL_POLL_SENTINEL"; + public const string SDL_HINT_JOYSTICK_DEVICE = + "SDL_JOYSTICK_DEVICE"; + public const string SDL_HINT_LINUX_JOYSTICK_CLASSIC = + "SDL_LINUX_JOYSTICK_CLASSIC"; + + + [DllImport(nativeLibName, EntryPoint = "SDL_SetHint", CallingConvention = CallingConvention.Cdecl)] + private static extern SDL_bool INTERNAL_SDL_SetHint( + byte* name, + byte* value + ); + public static SDL_bool SDL_SetHint(string name, string value) + { + int utf8NameBufSize = Utf8Size(name); + byte* utf8Name = stackalloc byte[utf8NameBufSize]; + + int utf8ValueBufSize = Utf8Size(value); + byte* utf8Value = stackalloc byte[utf8ValueBufSize]; + + return INTERNAL_SDL_SetHint( + Utf8Encode(name, utf8Name, utf8NameBufSize), + Utf8Encode(value, utf8Value, utf8ValueBufSize) + ); + } + + #endregion + + #region SDL_error.h + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_ClearError(); + + [DllImport(nativeLibName, EntryPoint = "SDL_GetError", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr INTERNAL_SDL_GetError(); + public static string? SDL_GetError() + { + return UTF8_ToManaged(INTERNAL_SDL_GetError()); + } + + #endregion + + #region SDL_video.h + + public enum SDL_GLattr + { + SDL_GL_RED_SIZE, + SDL_GL_GREEN_SIZE, + SDL_GL_BLUE_SIZE, + SDL_GL_ALPHA_SIZE, + SDL_GL_BUFFER_SIZE, + SDL_GL_DOUBLEBUFFER, + SDL_GL_DEPTH_SIZE, + SDL_GL_STENCIL_SIZE, + SDL_GL_ACCUM_RED_SIZE, + SDL_GL_ACCUM_GREEN_SIZE, + SDL_GL_ACCUM_BLUE_SIZE, + SDL_GL_ACCUM_ALPHA_SIZE, + SDL_GL_STEREO, + SDL_GL_MULTISAMPLEBUFFERS, + SDL_GL_MULTISAMPLESAMPLES, + SDL_GL_ACCELERATED_VISUAL, + SDL_GL_RETAINED_BACKING, + SDL_GL_CONTEXT_MAJOR_VERSION, + SDL_GL_CONTEXT_MINOR_VERSION, + SDL_GL_CONTEXT_EGL, + SDL_GL_CONTEXT_FLAGS, + SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_SHARE_WITH_CURRENT_CONTEXT, + SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, + SDL_GL_CONTEXT_RELEASE_BEHAVIOR, + SDL_GL_CONTEXT_RESET_NOTIFICATION, /* Requires >= 2.0.6 */ + SDL_GL_CONTEXT_NO_ERROR, /* Requires >= 2.0.6 */ + } + + [Flags] + public enum SDL_GLprofile + { + SDL_GL_CONTEXT_PROFILE_CORE = 0x0001, + SDL_GL_CONTEXT_PROFILE_COMPATIBILITY = 0x0002, + SDL_GL_CONTEXT_PROFILE_ES = 0x0004 + } + + public enum SDL_WindowEventID : byte + { + SDL_WINDOWEVENT_NONE, + SDL_WINDOWEVENT_SHOWN, + SDL_WINDOWEVENT_HIDDEN, + SDL_WINDOWEVENT_EXPOSED, + SDL_WINDOWEVENT_MOVED, + SDL_WINDOWEVENT_RESIZED, + SDL_WINDOWEVENT_SIZE_CHANGED, + SDL_WINDOWEVENT_MINIMIZED, + SDL_WINDOWEVENT_MAXIMIZED, + SDL_WINDOWEVENT_RESTORED, + SDL_WINDOWEVENT_ENTER, + SDL_WINDOWEVENT_LEAVE, + SDL_WINDOWEVENT_FOCUS_GAINED, + SDL_WINDOWEVENT_FOCUS_LOST, + SDL_WINDOWEVENT_CLOSE, + /* Only available in 2.0.5 or higher. */ + SDL_WINDOWEVENT_TAKE_FOCUS, + SDL_WINDOWEVENT_HIT_TEST, + /* Only available in 2.0.18 or higher. */ + SDL_WINDOWEVENT_ICCPROF_CHANGED, + SDL_WINDOWEVENT_DISPLAY_CHANGED + } + + public enum SDL_DisplayEventID : byte + { + SDL_DISPLAYEVENT_NONE, + SDL_DISPLAYEVENT_ORIENTATION, + SDL_DISPLAYEVENT_CONNECTED, /* Requires >= 2.0.14 */ + SDL_DISPLAYEVENT_DISCONNECTED /* Requires >= 2.0.14 */ + } + + [Flags] + public enum SDL_WindowFlags : uint + { + SDL_WINDOW_FULLSCREEN = 0x00000001, + SDL_WINDOW_OPENGL = 0x00000002, + SDL_WINDOW_SHOWN = 0x00000004, + SDL_WINDOW_HIDDEN = 0x00000008, + SDL_WINDOW_BORDERLESS = 0x00000010, + SDL_WINDOW_RESIZABLE = 0x00000020, + SDL_WINDOW_MINIMIZED = 0x00000040, + SDL_WINDOW_MAXIMIZED = 0x00000080, + SDL_WINDOW_MOUSE_GRABBED = 0x00000100, + SDL_WINDOW_INPUT_FOCUS = 0x00000200, + SDL_WINDOW_MOUSE_FOCUS = 0x00000400, + SDL_WINDOW_FULLSCREEN_DESKTOP = + (SDL_WINDOW_FULLSCREEN | 0x00001000), + SDL_WINDOW_FOREIGN = 0x00000800, + SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /* Requires >= 2.0.1 */ + SDL_WINDOW_MOUSE_CAPTURE = 0x00004000, /* Requires >= 2.0.4 */ + SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000, /* Requires >= 2.0.5 */ + SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /* Requires >= 2.0.5 */ + SDL_WINDOW_UTILITY = 0x00020000, /* Requires >= 2.0.5 */ + SDL_WINDOW_TOOLTIP = 0x00040000, /* Requires >= 2.0.5 */ + SDL_WINDOW_POPUP_MENU = 0x00080000, /* Requires >= 2.0.5 */ + SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000, /* Requires >= 2.0.16 */ + SDL_WINDOW_VULKAN = 0x10000000, /* Requires >= 2.0.6 */ + SDL_WINDOW_METAL = 0x2000000, /* Requires >= 2.0.14 */ + + SDL_WINDOW_INPUT_GRABBED = + SDL_WINDOW_MOUSE_GRABBED, + } + + public const int SDL_WINDOWPOS_UNDEFINED_MASK = 0x1FFF0000; + public const int SDL_WINDOWPOS_CENTERED_MASK = 0x2FFF0000; + public const int SDL_WINDOWPOS_UNDEFINED = 0x1FFF0000; + public const int SDL_WINDOWPOS_CENTERED = 0x2FFF0000; + + /* IntPtr refers to an SDL_Window* */ + [DllImport(nativeLibName, EntryPoint = "SDL_CreateWindow", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr INTERNAL_SDL_CreateWindow( + byte* title, + int x, + int y, + int w, + int h, + SDL_WindowFlags flags + ); + public static IntPtr SDL_CreateWindow( + string title, + int x, + int y, + int w, + int h, + SDL_WindowFlags flags + ) { + int utf8TitleBufSize = Utf8Size(title); + byte* utf8Title = stackalloc byte[utf8TitleBufSize]; + return INTERNAL_SDL_CreateWindow( + Utf8Encode(title, utf8Title, utf8TitleBufSize), + x, y, w, h, + flags + ); + } + public static IntPtr SDL_CreateWindow( + ReadOnlySpan title, + int x, + int y, + int w, + int h, + SDL_WindowFlags flags + ) + { + return INTERNAL_SDL_CreateWindow( + (byte*) Unsafe.AsPointer(ref MemoryMarshal.GetReference(title)), + x, y, w, h, + flags + ); + } + + /* window refers to an SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern SDL_WindowFlags SDL_GetWindowFlags(IntPtr window); + + /* window refers to an SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern uint SDL_GetWindowID(IntPtr window); + + /* window refers to an SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_GetWindowPosition(IntPtr window, int* x, int* y); + + /* window refers to an SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_GetWindowSize( + IntPtr window, + out int w, + out int h + ); + + /* IntPtr and window refer to an SDL_GLContext and SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr SDL_GL_CreateContext(IntPtr window); + + /* context refers to an SDL_GLContext */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_GL_DeleteContext(IntPtr context); + + /* IntPtr refers to a function pointer, proc to a const char* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void* SDL_GL_GetProcAddress(byte* proc); + + /* window and context refer to an SDL_Window* and SDL_GLContext */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_GL_MakeCurrent( + IntPtr window, + IntPtr context + ); + + /* window refers to an SDL_Window*. + * Only available in SDL 2.0.1 or higher. + */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_GL_GetDrawableSize( + IntPtr window, + out int w, + out int h + ); + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_GL_SetAttribute( + SDL_GLattr attr, + int value + ); + + public static int SDL_GL_SetAttribute( + SDL_GLattr attr, + SDL_GLprofile profile + ) { + return SDL_GL_SetAttribute(attr, (int)profile); + } + + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_GL_SetSwapInterval(int interval); + + /* window refers to an SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_GL_SwapWindow(IntPtr window); + + /* window refers to an SDL_Window* */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_SetWindowSize( + IntPtr window, + int w, + int h + ); + + #endregion + + #region SDL_events.h + + /* Default size is according to SDL2 default. */ + public const int SDL_TEXTEDITINGEVENT_TEXT_SIZE = 32; + public const int SDL_TEXTINPUTEVENT_TEXT_SIZE = 32; + + /* The types of events that can be delivered. */ + public enum SDL_EventType : uint + { + SDL_FIRSTEVENT = 0, + + /* Application events */ + SDL_QUIT = 0x100, + + /* iOS/Android/WinRT app events */ + SDL_APP_TERMINATING, + SDL_APP_LOWMEMORY, + SDL_APP_WILLENTERBACKGROUND, + SDL_APP_DIDENTERBACKGROUND, + SDL_APP_WILLENTERFOREGROUND, + SDL_APP_DIDENTERFOREGROUND, + + /* Only available in SDL 2.0.14 or higher. */ + SDL_LOCALECHANGED, + + /* Display events */ + /* Only available in SDL 2.0.9 or higher. */ + SDL_DISPLAYEVENT = 0x150, + + /* Window events */ + SDL_WINDOWEVENT = 0x200, + SDL_SYSWMEVENT, + + /* Keyboard events */ + SDL_KEYDOWN = 0x300, + SDL_KEYUP, + SDL_TEXTEDITING, + SDL_TEXTINPUT, + SDL_KEYMAPCHANGED, + + /* Mouse events */ + SDL_MOUSEMOTION = 0x400, + SDL_MOUSEBUTTONDOWN, + SDL_MOUSEBUTTONUP, + SDL_MOUSEWHEEL, + + /* Joystick events */ + SDL_JOYAXISMOTION = 0x600, + SDL_JOYBALLMOTION, + SDL_JOYHATMOTION, + SDL_JOYBUTTONDOWN, + SDL_JOYBUTTONUP, + SDL_JOYDEVICEADDED, + SDL_JOYDEVICEREMOVED, + + /* Game controller events */ + SDL_CONTROLLERAXISMOTION = 0x650, + SDL_CONTROLLERBUTTONDOWN, + SDL_CONTROLLERBUTTONUP, + SDL_CONTROLLERDEVICEADDED, + SDL_CONTROLLERDEVICEREMOVED, + SDL_CONTROLLERDEVICEREMAPPED, + SDL_CONTROLLERTOUCHPADDOWN, /* Requires >= 2.0.14 */ + SDL_CONTROLLERTOUCHPADMOTION, /* Requires >= 2.0.14 */ + SDL_CONTROLLERTOUCHPADUP, /* Requires >= 2.0.14 */ + SDL_CONTROLLERSENSORUPDATE, /* Requires >= 2.0.14 */ + + /* Touch events */ + SDL_FINGERDOWN = 0x700, + SDL_FINGERUP, + SDL_FINGERMOTION, + + /* Gesture events */ + SDL_DOLLARGESTURE = 0x800, + SDL_DOLLARRECORD, + SDL_MULTIGESTURE, + + /* Clipboard events */ + SDL_CLIPBOARDUPDATE = 0x900, + + /* Drag and drop events */ + SDL_DROPFILE = 0x1000, + /* Only available in 2.0.4 or higher. */ + SDL_DROPTEXT, + SDL_DROPBEGIN, + SDL_DROPCOMPLETE, + + /* Audio hotplug events */ + /* Only available in SDL 2.0.4 or higher. */ + SDL_AUDIODEVICEADDED = 0x1100, + SDL_AUDIODEVICEREMOVED, + + /* Sensor events */ + /* Only available in SDL 2.0.9 or higher. */ + SDL_SENSORUPDATE = 0x1200, + + /* Render events */ + /* Only available in SDL 2.0.2 or higher. */ + SDL_RENDER_TARGETS_RESET = 0x2000, + /* Only available in SDL 2.0.4 or higher. */ + SDL_RENDER_DEVICE_RESET, + + /* Internal events */ + /* Only available in 2.0.18 or higher. */ + SDL_POLLSENTINEL = 0x7F00, + + /* Events SDL_USEREVENT through SDL_LASTEVENT are for + * your use, and should be allocated with + * SDL_RegisterEvents() + */ + SDL_USEREVENT = 0x8000, + + /* The last event, used for bouding arrays. */ + SDL_LASTEVENT = 0xFFFF + } + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DisplayEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 display; + public SDL_DisplayEventID displayEvent; // event, lolC# + private byte padding1; + private byte padding2; + private byte padding3; + public Int32 data1; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Window state change event data (event.window.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_WindowEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public SDL_WindowEventID windowEvent; // event, lolC# + private byte padding1; + private byte padding2; + private byte padding3; + public Int32 data1; + public Int32 data2; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Keyboard button event structure (event.key.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_KeyboardEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public byte state; + public byte repeat; /* non-zero if this is a repeat */ + private byte padding2; + private byte padding3; + public SDL_Keysym keysym; + } +#pragma warning restore 0169 + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TextEditingEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public fixed byte text[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; + public Int32 start; + public Int32 length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TextInputEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public fixed byte text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; + } + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Mouse motion event structure (event.motion.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseMotionEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public UInt32 which; + public byte state; /* bitmask of buttons */ + private byte padding1; + private byte padding2; + private byte padding3; + public Int32 x; + public Int32 y; + public Int32 xrel; + public Int32 yrel; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Mouse button event structure (event.button.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseButtonEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public UInt32 which; + public byte button; /* button id */ + public byte state; /* SDL_PRESSED or SDL_RELEASED */ + public byte clicks; /* 1 for single-click, 2 for double-click, etc. */ + private byte padding1; + public Int32 x; + public Int32 y; + } +#pragma warning restore 0169 + + /* Mouse wheel event structure (event.wheel.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseWheelEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public UInt32 windowID; + public UInt32 which; + public Int32 x; /* amount scrolled horizontally */ + public Int32 y; /* amount scrolled vertically */ + public UInt32 direction; /* Set to one of the SDL_MOUSEWHEEL_* defines */ + public float preciseX; /* Requires >= 2.0.18 */ + public float preciseY; /* Requires >= 2.0.18 */ + } + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Joystick axis motion event structure (event.jaxis.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyAxisEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public byte axis; + private byte padding1; + private byte padding2; + private byte padding3; + public Int16 axisValue; /* value, lolC# */ + public UInt16 padding4; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Joystick trackball motion event structure (event.jball.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyBallEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public byte ball; + private byte padding1; + private byte padding2; + private byte padding3; + public Int16 xrel; + public Int16 yrel; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Joystick hat position change event struct (event.jhat.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyHatEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public byte hat; /* index of the hat */ + public byte hatValue; /* value, lolC# */ + private byte padding1; + private byte padding2; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Joystick button event structure (event.jbutton.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyButtonEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public byte button; + public byte state; /* SDL_PRESSED or SDL_RELEASED */ + private byte padding1; + private byte padding2; + } +#pragma warning restore 0169 + + /* Joystick device event structure (event.jdevice.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyDeviceEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + } + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Game controller axis motion event (event.caxis.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_ControllerAxisEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public byte axis; + private byte padding1; + private byte padding2; + private byte padding3; + public Int16 axisValue; /* value, lolC# */ + private UInt16 padding4; + } +#pragma warning restore 0169 + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Game controller button event (event.cbutton.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_ControllerButtonEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public byte button; + public byte state; + private byte padding1; + private byte padding2; + } +#pragma warning restore 0169 + + /* Game controller device event (event.cdevice.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_ControllerDeviceEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; /* joystick id for ADDED, + * else instance id + */ + } + + /* Game controller touchpad event structure (event.ctouchpad.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_ControllerTouchpadEvent + { + public UInt32 type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public Int32 touchpad; + public Int32 finger; + public float x; + public float y; + public float pressure; + } + + /* Game controller sensor event structure (event.csensor.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_ControllerSensorEvent + { + public UInt32 type; + public UInt32 timestamp; + public Int32 which; /* SDL_JoystickID */ + public Int32 sensor; + public float data1; + public float data2; + public float data3; + } + +// Ignore private members used for padding in this struct +#pragma warning disable 0169 + /* Audio device event (event.adevice.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_AudioDeviceEvent + { + public UInt32 type; + public UInt32 timestamp; + public UInt32 which; + public byte iscapture; + private byte padding1; + private byte padding2; + private byte padding3; + } +#pragma warning restore 0169 + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TouchFingerEvent + { + public UInt32 type; + public UInt32 timestamp; + public Int64 touchId; // SDL_TouchID + public Int64 fingerId; // SDL_GestureID + public float x; + public float y; + public float dx; + public float dy; + public float pressure; + public uint windowID; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MultiGestureEvent + { + public UInt32 type; + public UInt32 timestamp; + public Int64 touchId; // SDL_TouchID + public float dTheta; + public float dDist; + public float x; + public float y; + public UInt16 numFingers; + public UInt16 padding; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DollarGestureEvent + { + public UInt32 type; + public UInt32 timestamp; + public Int64 touchId; // SDL_TouchID + public Int64 gestureId; // SDL_GestureID + public UInt32 numFingers; + public float error; + public float x; + public float y; + } + + /* File open request by system (event.drop.*), enabled by + * default + */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DropEvent + { + public SDL_EventType type; + public UInt32 timestamp; + + /* char* filename, to be freed. + * Access the variable EXACTLY ONCE like this: + * string s = SDL.UTF8_ToManaged(evt.drop.file, true); + */ + public IntPtr file; + public UInt32 windowID; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_SensorEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public Int32 which; + public fixed float data[6]; + } + + /* The "quit requested" event */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_QuitEvent + { + public SDL_EventType type; + public UInt32 timestamp; + } + + /* A user defined event (event.user.*) */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_UserEvent + { + public UInt32 type; + public UInt32 timestamp; + public UInt32 windowID; + public Int32 code; + public IntPtr data1; /* user-defined */ + public IntPtr data2; /* user-defined */ + } + + /* A video driver dependent event (event.syswm.*), disabled */ + [StructLayout(LayoutKind.Sequential)] + public struct SDL_SysWMEvent + { + public SDL_EventType type; + public UInt32 timestamp; + public IntPtr msg; /* SDL_SysWMmsg*, system-dependent*/ + } + + /* General event structure */ + // C# doesn't do unions, so we do this ugly thing. */ + [StructLayout(LayoutKind.Explicit)] + public struct SDL_Event + { + [FieldOffset(0)] + public SDL_EventType type; + [FieldOffset(0)] + public SDL_EventType typeFSharp; + [FieldOffset(0)] + public SDL_DisplayEvent display; + [FieldOffset(0)] + public SDL_WindowEvent window; + [FieldOffset(0)] + public SDL_KeyboardEvent key; + [FieldOffset(0)] + public SDL_TextEditingEvent edit; + [FieldOffset(0)] + public SDL_TextInputEvent text; + [FieldOffset(0)] + public SDL_MouseMotionEvent motion; + [FieldOffset(0)] + public SDL_MouseButtonEvent button; + [FieldOffset(0)] + public SDL_MouseWheelEvent wheel; + [FieldOffset(0)] + public SDL_JoyAxisEvent jaxis; + [FieldOffset(0)] + public SDL_JoyBallEvent jball; + [FieldOffset(0)] + public SDL_JoyHatEvent jhat; + [FieldOffset(0)] + public SDL_JoyButtonEvent jbutton; + [FieldOffset(0)] + public SDL_JoyDeviceEvent jdevice; + [FieldOffset(0)] + public SDL_ControllerAxisEvent caxis; + [FieldOffset(0)] + public SDL_ControllerButtonEvent cbutton; + [FieldOffset(0)] + public SDL_ControllerDeviceEvent cdevice; + [FieldOffset(0)] + public SDL_ControllerTouchpadEvent ctouchpad; + [FieldOffset(0)] + public SDL_ControllerSensorEvent csensor; + [FieldOffset(0)] + public SDL_AudioDeviceEvent adevice; + [FieldOffset(0)] + public SDL_SensorEvent sensor; + [FieldOffset(0)] + public SDL_QuitEvent quit; + [FieldOffset(0)] + public SDL_UserEvent user; + [FieldOffset(0)] + public SDL_SysWMEvent syswm; + [FieldOffset(0)] + public SDL_TouchFingerEvent tfinger; + [FieldOffset(0)] + public SDL_MultiGestureEvent mgesture; + [FieldOffset(0)] + public SDL_DollarGestureEvent dgesture; + [FieldOffset(0)] + public SDL_DropEvent drop; + [FieldOffset(0)] + private fixed byte padding[56]; + } + + /* Polls for currently pending events */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_PollEvent(out SDL_Event _event); + + #endregion + + #region SDL_scancode.h + + /* Scancodes based off USB keyboard page (0x07) */ + public enum SDL_Scancode + { + SDL_SCANCODE_UNKNOWN = 0, + + SDL_SCANCODE_A = 4, + SDL_SCANCODE_B = 5, + SDL_SCANCODE_C = 6, + SDL_SCANCODE_D = 7, + SDL_SCANCODE_E = 8, + SDL_SCANCODE_F = 9, + SDL_SCANCODE_G = 10, + SDL_SCANCODE_H = 11, + SDL_SCANCODE_I = 12, + SDL_SCANCODE_J = 13, + SDL_SCANCODE_K = 14, + SDL_SCANCODE_L = 15, + SDL_SCANCODE_M = 16, + SDL_SCANCODE_N = 17, + SDL_SCANCODE_O = 18, + SDL_SCANCODE_P = 19, + SDL_SCANCODE_Q = 20, + SDL_SCANCODE_R = 21, + SDL_SCANCODE_S = 22, + SDL_SCANCODE_T = 23, + SDL_SCANCODE_U = 24, + SDL_SCANCODE_V = 25, + SDL_SCANCODE_W = 26, + SDL_SCANCODE_X = 27, + SDL_SCANCODE_Y = 28, + SDL_SCANCODE_Z = 29, + + SDL_SCANCODE_1 = 30, + SDL_SCANCODE_2 = 31, + SDL_SCANCODE_3 = 32, + SDL_SCANCODE_4 = 33, + SDL_SCANCODE_5 = 34, + SDL_SCANCODE_6 = 35, + SDL_SCANCODE_7 = 36, + SDL_SCANCODE_8 = 37, + SDL_SCANCODE_9 = 38, + SDL_SCANCODE_0 = 39, + + SDL_SCANCODE_RETURN = 40, + SDL_SCANCODE_ESCAPE = 41, + SDL_SCANCODE_BACKSPACE = 42, + SDL_SCANCODE_TAB = 43, + SDL_SCANCODE_SPACE = 44, + + SDL_SCANCODE_MINUS = 45, + SDL_SCANCODE_EQUALS = 46, + SDL_SCANCODE_LEFTBRACKET = 47, + SDL_SCANCODE_RIGHTBRACKET = 48, + SDL_SCANCODE_BACKSLASH = 49, + SDL_SCANCODE_NONUSHASH = 50, + SDL_SCANCODE_SEMICOLON = 51, + SDL_SCANCODE_APOSTROPHE = 52, + SDL_SCANCODE_GRAVE = 53, + SDL_SCANCODE_COMMA = 54, + SDL_SCANCODE_PERIOD = 55, + SDL_SCANCODE_SLASH = 56, + + SDL_SCANCODE_CAPSLOCK = 57, + + SDL_SCANCODE_F1 = 58, + SDL_SCANCODE_F2 = 59, + SDL_SCANCODE_F3 = 60, + SDL_SCANCODE_F4 = 61, + SDL_SCANCODE_F5 = 62, + SDL_SCANCODE_F6 = 63, + SDL_SCANCODE_F7 = 64, + SDL_SCANCODE_F8 = 65, + SDL_SCANCODE_F9 = 66, + SDL_SCANCODE_F10 = 67, + SDL_SCANCODE_F11 = 68, + SDL_SCANCODE_F12 = 69, + + SDL_SCANCODE_PRINTSCREEN = 70, + SDL_SCANCODE_SCROLLLOCK = 71, + SDL_SCANCODE_PAUSE = 72, + SDL_SCANCODE_INSERT = 73, + SDL_SCANCODE_HOME = 74, + SDL_SCANCODE_PAGEUP = 75, + SDL_SCANCODE_DELETE = 76, + SDL_SCANCODE_END = 77, + SDL_SCANCODE_PAGEDOWN = 78, + SDL_SCANCODE_RIGHT = 79, + SDL_SCANCODE_LEFT = 80, + SDL_SCANCODE_DOWN = 81, + SDL_SCANCODE_UP = 82, + + SDL_SCANCODE_NUMLOCKCLEAR = 83, + SDL_SCANCODE_KP_DIVIDE = 84, + SDL_SCANCODE_KP_MULTIPLY = 85, + SDL_SCANCODE_KP_MINUS = 86, + SDL_SCANCODE_KP_PLUS = 87, + SDL_SCANCODE_KP_ENTER = 88, + SDL_SCANCODE_KP_1 = 89, + SDL_SCANCODE_KP_2 = 90, + SDL_SCANCODE_KP_3 = 91, + SDL_SCANCODE_KP_4 = 92, + SDL_SCANCODE_KP_5 = 93, + SDL_SCANCODE_KP_6 = 94, + SDL_SCANCODE_KP_7 = 95, + SDL_SCANCODE_KP_8 = 96, + SDL_SCANCODE_KP_9 = 97, + SDL_SCANCODE_KP_0 = 98, + SDL_SCANCODE_KP_PERIOD = 99, + + SDL_SCANCODE_NONUSBACKSLASH = 100, + SDL_SCANCODE_APPLICATION = 101, + SDL_SCANCODE_POWER = 102, + SDL_SCANCODE_KP_EQUALS = 103, + SDL_SCANCODE_F13 = 104, + SDL_SCANCODE_F14 = 105, + SDL_SCANCODE_F15 = 106, + SDL_SCANCODE_F16 = 107, + SDL_SCANCODE_F17 = 108, + SDL_SCANCODE_F18 = 109, + SDL_SCANCODE_F19 = 110, + SDL_SCANCODE_F20 = 111, + SDL_SCANCODE_F21 = 112, + SDL_SCANCODE_F22 = 113, + SDL_SCANCODE_F23 = 114, + SDL_SCANCODE_F24 = 115, + SDL_SCANCODE_EXECUTE = 116, + SDL_SCANCODE_HELP = 117, + SDL_SCANCODE_MENU = 118, + SDL_SCANCODE_SELECT = 119, + SDL_SCANCODE_STOP = 120, + SDL_SCANCODE_AGAIN = 121, + SDL_SCANCODE_UNDO = 122, + SDL_SCANCODE_CUT = 123, + SDL_SCANCODE_COPY = 124, + SDL_SCANCODE_PASTE = 125, + SDL_SCANCODE_FIND = 126, + SDL_SCANCODE_MUTE = 127, + SDL_SCANCODE_VOLUMEUP = 128, + SDL_SCANCODE_VOLUMEDOWN = 129, + /* not sure whether there's a reason to enable these */ + /* SDL_SCANCODE_LOCKINGCAPSLOCK = 130, */ + /* SDL_SCANCODE_LOCKINGNUMLOCK = 131, */ + /* SDL_SCANCODE_LOCKINGSCROLLLOCK = 132, */ + SDL_SCANCODE_KP_COMMA = 133, + SDL_SCANCODE_KP_EQUALSAS400 = 134, + + SDL_SCANCODE_INTERNATIONAL1 = 135, + SDL_SCANCODE_INTERNATIONAL2 = 136, + SDL_SCANCODE_INTERNATIONAL3 = 137, + SDL_SCANCODE_INTERNATIONAL4 = 138, + SDL_SCANCODE_INTERNATIONAL5 = 139, + SDL_SCANCODE_INTERNATIONAL6 = 140, + SDL_SCANCODE_INTERNATIONAL7 = 141, + SDL_SCANCODE_INTERNATIONAL8 = 142, + SDL_SCANCODE_INTERNATIONAL9 = 143, + SDL_SCANCODE_LANG1 = 144, + SDL_SCANCODE_LANG2 = 145, + SDL_SCANCODE_LANG3 = 146, + SDL_SCANCODE_LANG4 = 147, + SDL_SCANCODE_LANG5 = 148, + SDL_SCANCODE_LANG6 = 149, + SDL_SCANCODE_LANG7 = 150, + SDL_SCANCODE_LANG8 = 151, + SDL_SCANCODE_LANG9 = 152, + + SDL_SCANCODE_ALTERASE = 153, + SDL_SCANCODE_SYSREQ = 154, + SDL_SCANCODE_CANCEL = 155, + SDL_SCANCODE_CLEAR = 156, + SDL_SCANCODE_PRIOR = 157, + SDL_SCANCODE_RETURN2 = 158, + SDL_SCANCODE_SEPARATOR = 159, + SDL_SCANCODE_OUT = 160, + SDL_SCANCODE_OPER = 161, + SDL_SCANCODE_CLEARAGAIN = 162, + SDL_SCANCODE_CRSEL = 163, + SDL_SCANCODE_EXSEL = 164, + + SDL_SCANCODE_KP_00 = 176, + SDL_SCANCODE_KP_000 = 177, + SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + SDL_SCANCODE_DECIMALSEPARATOR = 179, + SDL_SCANCODE_CURRENCYUNIT = 180, + SDL_SCANCODE_CURRENCYSUBUNIT = 181, + SDL_SCANCODE_KP_LEFTPAREN = 182, + SDL_SCANCODE_KP_RIGHTPAREN = 183, + SDL_SCANCODE_KP_LEFTBRACE = 184, + SDL_SCANCODE_KP_RIGHTBRACE = 185, + SDL_SCANCODE_KP_TAB = 186, + SDL_SCANCODE_KP_BACKSPACE = 187, + SDL_SCANCODE_KP_A = 188, + SDL_SCANCODE_KP_B = 189, + SDL_SCANCODE_KP_C = 190, + SDL_SCANCODE_KP_D = 191, + SDL_SCANCODE_KP_E = 192, + SDL_SCANCODE_KP_F = 193, + SDL_SCANCODE_KP_XOR = 194, + SDL_SCANCODE_KP_POWER = 195, + SDL_SCANCODE_KP_PERCENT = 196, + SDL_SCANCODE_KP_LESS = 197, + SDL_SCANCODE_KP_GREATER = 198, + SDL_SCANCODE_KP_AMPERSAND = 199, + SDL_SCANCODE_KP_DBLAMPERSAND = 200, + SDL_SCANCODE_KP_VERTICALBAR = 201, + SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + SDL_SCANCODE_KP_COLON = 203, + SDL_SCANCODE_KP_HASH = 204, + SDL_SCANCODE_KP_SPACE = 205, + SDL_SCANCODE_KP_AT = 206, + SDL_SCANCODE_KP_EXCLAM = 207, + SDL_SCANCODE_KP_MEMSTORE = 208, + SDL_SCANCODE_KP_MEMRECALL = 209, + SDL_SCANCODE_KP_MEMCLEAR = 210, + SDL_SCANCODE_KP_MEMADD = 211, + SDL_SCANCODE_KP_MEMSUBTRACT = 212, + SDL_SCANCODE_KP_MEMMULTIPLY = 213, + SDL_SCANCODE_KP_MEMDIVIDE = 214, + SDL_SCANCODE_KP_PLUSMINUS = 215, + SDL_SCANCODE_KP_CLEAR = 216, + SDL_SCANCODE_KP_CLEARENTRY = 217, + SDL_SCANCODE_KP_BINARY = 218, + SDL_SCANCODE_KP_OCTAL = 219, + SDL_SCANCODE_KP_DECIMAL = 220, + SDL_SCANCODE_KP_HEXADECIMAL = 221, + + SDL_SCANCODE_LCTRL = 224, + SDL_SCANCODE_LSHIFT = 225, + SDL_SCANCODE_LALT = 226, + SDL_SCANCODE_LGUI = 227, + SDL_SCANCODE_RCTRL = 228, + SDL_SCANCODE_RSHIFT = 229, + SDL_SCANCODE_RALT = 230, + SDL_SCANCODE_RGUI = 231, + + SDL_SCANCODE_MODE = 257, + + /* These come from the USB consumer page (0x0C) */ + SDL_SCANCODE_AUDIONEXT = 258, + SDL_SCANCODE_AUDIOPREV = 259, + SDL_SCANCODE_AUDIOSTOP = 260, + SDL_SCANCODE_AUDIOPLAY = 261, + SDL_SCANCODE_AUDIOMUTE = 262, + SDL_SCANCODE_MEDIASELECT = 263, + SDL_SCANCODE_WWW = 264, + SDL_SCANCODE_MAIL = 265, + SDL_SCANCODE_CALCULATOR = 266, + SDL_SCANCODE_COMPUTER = 267, + SDL_SCANCODE_AC_SEARCH = 268, + SDL_SCANCODE_AC_HOME = 269, + SDL_SCANCODE_AC_BACK = 270, + SDL_SCANCODE_AC_FORWARD = 271, + SDL_SCANCODE_AC_STOP = 272, + SDL_SCANCODE_AC_REFRESH = 273, + SDL_SCANCODE_AC_BOOKMARKS = 274, + + /* These come from other sources, and are mostly mac related */ + SDL_SCANCODE_BRIGHTNESSDOWN = 275, + SDL_SCANCODE_BRIGHTNESSUP = 276, + SDL_SCANCODE_DISPLAYSWITCH = 277, + SDL_SCANCODE_KBDILLUMTOGGLE = 278, + SDL_SCANCODE_KBDILLUMDOWN = 279, + SDL_SCANCODE_KBDILLUMUP = 280, + SDL_SCANCODE_EJECT = 281, + SDL_SCANCODE_SLEEP = 282, + + SDL_SCANCODE_APP1 = 283, + SDL_SCANCODE_APP2 = 284, + + /* These come from the USB consumer page (0x0C) */ + SDL_SCANCODE_AUDIOREWIND = 285, + SDL_SCANCODE_AUDIOFASTFORWARD = 286, + + /* This is not a key, simply marks the number of scancodes + * so that you know how big to make your arrays. */ + SDL_NUM_SCANCODES = 512 + } + + #endregion + + #region SDL_keycode.h + + public const int SDLK_SCANCODE_MASK = 1 << 30; + + public enum SDL_Keycode + { + SDLK_UNKNOWN = 0, + + SDLK_RETURN = '\r', + SDLK_ESCAPE = 27, // '\033' + SDLK_BACKSPACE = '\b', + SDLK_TAB = '\t', + SDLK_SPACE = ' ', + SDLK_EXCLAIM = '!', + SDLK_QUOTEDBL = '"', + SDLK_HASH = '#', + SDLK_PERCENT = '%', + SDLK_DOLLAR = '$', + SDLK_AMPERSAND = '&', + SDLK_QUOTE = '\'', + SDLK_LEFTPAREN = '(', + SDLK_RIGHTPAREN = ')', + SDLK_ASTERISK = '*', + SDLK_PLUS = '+', + SDLK_COMMA = ',', + SDLK_MINUS = '-', + SDLK_PERIOD = '.', + SDLK_SLASH = '/', + SDLK_0 = '0', + SDLK_1 = '1', + SDLK_2 = '2', + SDLK_3 = '3', + SDLK_4 = '4', + SDLK_5 = '5', + SDLK_6 = '6', + SDLK_7 = '7', + SDLK_8 = '8', + SDLK_9 = '9', + SDLK_COLON = ':', + SDLK_SEMICOLON = ';', + SDLK_LESS = '<', + SDLK_EQUALS = '=', + SDLK_GREATER = '>', + SDLK_QUESTION = '?', + SDLK_AT = '@', + /* + Skip uppercase letters + */ + SDLK_LEFTBRACKET = '[', + SDLK_BACKSLASH = '\\', + SDLK_RIGHTBRACKET = ']', + SDLK_CARET = '^', + SDLK_UNDERSCORE = '_', + SDLK_BACKQUOTE = '`', + SDLK_a = 'a', + SDLK_b = 'b', + SDLK_c = 'c', + SDLK_d = 'd', + SDLK_e = 'e', + SDLK_f = 'f', + SDLK_g = 'g', + SDLK_h = 'h', + SDLK_i = 'i', + SDLK_j = 'j', + SDLK_k = 'k', + SDLK_l = 'l', + SDLK_m = 'm', + SDLK_n = 'n', + SDLK_o = 'o', + SDLK_p = 'p', + SDLK_q = 'q', + SDLK_r = 'r', + SDLK_s = 's', + SDLK_t = 't', + SDLK_u = 'u', + SDLK_v = 'v', + SDLK_w = 'w', + SDLK_x = 'x', + SDLK_y = 'y', + SDLK_z = 'z', + + SDLK_CAPSLOCK = (int)SDL_Scancode.SDL_SCANCODE_CAPSLOCK | SDLK_SCANCODE_MASK, + + SDLK_F1 = (int)SDL_Scancode.SDL_SCANCODE_F1 | SDLK_SCANCODE_MASK, + SDLK_F2 = (int)SDL_Scancode.SDL_SCANCODE_F2 | SDLK_SCANCODE_MASK, + SDLK_F3 = (int)SDL_Scancode.SDL_SCANCODE_F3 | SDLK_SCANCODE_MASK, + SDLK_F4 = (int)SDL_Scancode.SDL_SCANCODE_F4 | SDLK_SCANCODE_MASK, + SDLK_F5 = (int)SDL_Scancode.SDL_SCANCODE_F5 | SDLK_SCANCODE_MASK, + SDLK_F6 = (int)SDL_Scancode.SDL_SCANCODE_F6 | SDLK_SCANCODE_MASK, + SDLK_F7 = (int)SDL_Scancode.SDL_SCANCODE_F7 | SDLK_SCANCODE_MASK, + SDLK_F8 = (int)SDL_Scancode.SDL_SCANCODE_F8 | SDLK_SCANCODE_MASK, + SDLK_F9 = (int)SDL_Scancode.SDL_SCANCODE_F9 | SDLK_SCANCODE_MASK, + SDLK_F10 = (int)SDL_Scancode.SDL_SCANCODE_F10 | SDLK_SCANCODE_MASK, + SDLK_F11 = (int)SDL_Scancode.SDL_SCANCODE_F11 | SDLK_SCANCODE_MASK, + SDLK_F12 = (int)SDL_Scancode.SDL_SCANCODE_F12 | SDLK_SCANCODE_MASK, + + SDLK_PRINTSCREEN = (int)SDL_Scancode.SDL_SCANCODE_PRINTSCREEN | SDLK_SCANCODE_MASK, + SDLK_SCROLLLOCK = (int)SDL_Scancode.SDL_SCANCODE_SCROLLLOCK | SDLK_SCANCODE_MASK, + SDLK_PAUSE = (int)SDL_Scancode.SDL_SCANCODE_PAUSE | SDLK_SCANCODE_MASK, + SDLK_INSERT = (int)SDL_Scancode.SDL_SCANCODE_INSERT | SDLK_SCANCODE_MASK, + SDLK_HOME = (int)SDL_Scancode.SDL_SCANCODE_HOME | SDLK_SCANCODE_MASK, + SDLK_PAGEUP = (int)SDL_Scancode.SDL_SCANCODE_PAGEUP | SDLK_SCANCODE_MASK, + SDLK_DELETE = 127, + SDLK_END = (int)SDL_Scancode.SDL_SCANCODE_END | SDLK_SCANCODE_MASK, + SDLK_PAGEDOWN = (int)SDL_Scancode.SDL_SCANCODE_PAGEDOWN | SDLK_SCANCODE_MASK, + SDLK_RIGHT = (int)SDL_Scancode.SDL_SCANCODE_RIGHT | SDLK_SCANCODE_MASK, + SDLK_LEFT = (int)SDL_Scancode.SDL_SCANCODE_LEFT | SDLK_SCANCODE_MASK, + SDLK_DOWN = (int)SDL_Scancode.SDL_SCANCODE_DOWN | SDLK_SCANCODE_MASK, + SDLK_UP = (int)SDL_Scancode.SDL_SCANCODE_UP | SDLK_SCANCODE_MASK, + + SDLK_NUMLOCKCLEAR = (int)SDL_Scancode.SDL_SCANCODE_NUMLOCKCLEAR | SDLK_SCANCODE_MASK, + SDLK_KP_DIVIDE = (int)SDL_Scancode.SDL_SCANCODE_KP_DIVIDE | SDLK_SCANCODE_MASK, + SDLK_KP_MULTIPLY = (int)SDL_Scancode.SDL_SCANCODE_KP_MULTIPLY | SDLK_SCANCODE_MASK, + SDLK_KP_MINUS = (int)SDL_Scancode.SDL_SCANCODE_KP_MINUS | SDLK_SCANCODE_MASK, + SDLK_KP_PLUS = (int)SDL_Scancode.SDL_SCANCODE_KP_PLUS | SDLK_SCANCODE_MASK, + SDLK_KP_ENTER = (int)SDL_Scancode.SDL_SCANCODE_KP_ENTER | SDLK_SCANCODE_MASK, + SDLK_KP_1 = (int)SDL_Scancode.SDL_SCANCODE_KP_1 | SDLK_SCANCODE_MASK, + SDLK_KP_2 = (int)SDL_Scancode.SDL_SCANCODE_KP_2 | SDLK_SCANCODE_MASK, + SDLK_KP_3 = (int)SDL_Scancode.SDL_SCANCODE_KP_3 | SDLK_SCANCODE_MASK, + SDLK_KP_4 = (int)SDL_Scancode.SDL_SCANCODE_KP_4 | SDLK_SCANCODE_MASK, + SDLK_KP_5 = (int)SDL_Scancode.SDL_SCANCODE_KP_5 | SDLK_SCANCODE_MASK, + SDLK_KP_6 = (int)SDL_Scancode.SDL_SCANCODE_KP_6 | SDLK_SCANCODE_MASK, + SDLK_KP_7 = (int)SDL_Scancode.SDL_SCANCODE_KP_7 | SDLK_SCANCODE_MASK, + SDLK_KP_8 = (int)SDL_Scancode.SDL_SCANCODE_KP_8 | SDLK_SCANCODE_MASK, + SDLK_KP_9 = (int)SDL_Scancode.SDL_SCANCODE_KP_9 | SDLK_SCANCODE_MASK, + SDLK_KP_0 = (int)SDL_Scancode.SDL_SCANCODE_KP_0 | SDLK_SCANCODE_MASK, + SDLK_KP_PERIOD = (int)SDL_Scancode.SDL_SCANCODE_KP_PERIOD | SDLK_SCANCODE_MASK, + + SDLK_APPLICATION = (int)SDL_Scancode.SDL_SCANCODE_APPLICATION | SDLK_SCANCODE_MASK, + SDLK_POWER = (int)SDL_Scancode.SDL_SCANCODE_POWER | SDLK_SCANCODE_MASK, + SDLK_KP_EQUALS = (int)SDL_Scancode.SDL_SCANCODE_KP_EQUALS | SDLK_SCANCODE_MASK, + SDLK_F13 = (int)SDL_Scancode.SDL_SCANCODE_F13 | SDLK_SCANCODE_MASK, + SDLK_F14 = (int)SDL_Scancode.SDL_SCANCODE_F14 | SDLK_SCANCODE_MASK, + SDLK_F15 = (int)SDL_Scancode.SDL_SCANCODE_F15 | SDLK_SCANCODE_MASK, + SDLK_F16 = (int)SDL_Scancode.SDL_SCANCODE_F16 | SDLK_SCANCODE_MASK, + SDLK_F17 = (int)SDL_Scancode.SDL_SCANCODE_F17 | SDLK_SCANCODE_MASK, + SDLK_F18 = (int)SDL_Scancode.SDL_SCANCODE_F18 | SDLK_SCANCODE_MASK, + SDLK_F19 = (int)SDL_Scancode.SDL_SCANCODE_F19 | SDLK_SCANCODE_MASK, + SDLK_F20 = (int)SDL_Scancode.SDL_SCANCODE_F20 | SDLK_SCANCODE_MASK, + SDLK_F21 = (int)SDL_Scancode.SDL_SCANCODE_F21 | SDLK_SCANCODE_MASK, + SDLK_F22 = (int)SDL_Scancode.SDL_SCANCODE_F22 | SDLK_SCANCODE_MASK, + SDLK_F23 = (int)SDL_Scancode.SDL_SCANCODE_F23 | SDLK_SCANCODE_MASK, + SDLK_F24 = (int)SDL_Scancode.SDL_SCANCODE_F24 | SDLK_SCANCODE_MASK, + SDLK_EXECUTE = (int)SDL_Scancode.SDL_SCANCODE_EXECUTE | SDLK_SCANCODE_MASK, + SDLK_HELP = (int)SDL_Scancode.SDL_SCANCODE_HELP | SDLK_SCANCODE_MASK, + SDLK_MENU = (int)SDL_Scancode.SDL_SCANCODE_MENU | SDLK_SCANCODE_MASK, + SDLK_SELECT = (int)SDL_Scancode.SDL_SCANCODE_SELECT | SDLK_SCANCODE_MASK, + SDLK_STOP = (int)SDL_Scancode.SDL_SCANCODE_STOP | SDLK_SCANCODE_MASK, + SDLK_AGAIN = (int)SDL_Scancode.SDL_SCANCODE_AGAIN | SDLK_SCANCODE_MASK, + SDLK_UNDO = (int)SDL_Scancode.SDL_SCANCODE_UNDO | SDLK_SCANCODE_MASK, + SDLK_CUT = (int)SDL_Scancode.SDL_SCANCODE_CUT | SDLK_SCANCODE_MASK, + SDLK_COPY = (int)SDL_Scancode.SDL_SCANCODE_COPY | SDLK_SCANCODE_MASK, + SDLK_PASTE = (int)SDL_Scancode.SDL_SCANCODE_PASTE | SDLK_SCANCODE_MASK, + SDLK_FIND = (int)SDL_Scancode.SDL_SCANCODE_FIND | SDLK_SCANCODE_MASK, + SDLK_MUTE = (int)SDL_Scancode.SDL_SCANCODE_MUTE | SDLK_SCANCODE_MASK, + SDLK_VOLUMEUP = (int)SDL_Scancode.SDL_SCANCODE_VOLUMEUP | SDLK_SCANCODE_MASK, + SDLK_VOLUMEDOWN = (int)SDL_Scancode.SDL_SCANCODE_VOLUMEDOWN | SDLK_SCANCODE_MASK, + SDLK_KP_COMMA = (int)SDL_Scancode.SDL_SCANCODE_KP_COMMA | SDLK_SCANCODE_MASK, + SDLK_KP_EQUALSAS400 = + (int)SDL_Scancode.SDL_SCANCODE_KP_EQUALSAS400 | SDLK_SCANCODE_MASK, + + SDLK_ALTERASE = (int)SDL_Scancode.SDL_SCANCODE_ALTERASE | SDLK_SCANCODE_MASK, + SDLK_SYSREQ = (int)SDL_Scancode.SDL_SCANCODE_SYSREQ | SDLK_SCANCODE_MASK, + SDLK_CANCEL = (int)SDL_Scancode.SDL_SCANCODE_CANCEL | SDLK_SCANCODE_MASK, + SDLK_CLEAR = (int)SDL_Scancode.SDL_SCANCODE_CLEAR | SDLK_SCANCODE_MASK, + SDLK_PRIOR = (int)SDL_Scancode.SDL_SCANCODE_PRIOR | SDLK_SCANCODE_MASK, + SDLK_RETURN2 = (int)SDL_Scancode.SDL_SCANCODE_RETURN2 | SDLK_SCANCODE_MASK, + SDLK_SEPARATOR = (int)SDL_Scancode.SDL_SCANCODE_SEPARATOR | SDLK_SCANCODE_MASK, + SDLK_OUT = (int)SDL_Scancode.SDL_SCANCODE_OUT | SDLK_SCANCODE_MASK, + SDLK_OPER = (int)SDL_Scancode.SDL_SCANCODE_OPER | SDLK_SCANCODE_MASK, + SDLK_CLEARAGAIN = (int)SDL_Scancode.SDL_SCANCODE_CLEARAGAIN | SDLK_SCANCODE_MASK, + SDLK_CRSEL = (int)SDL_Scancode.SDL_SCANCODE_CRSEL | SDLK_SCANCODE_MASK, + SDLK_EXSEL = (int)SDL_Scancode.SDL_SCANCODE_EXSEL | SDLK_SCANCODE_MASK, + + SDLK_KP_00 = (int)SDL_Scancode.SDL_SCANCODE_KP_00 | SDLK_SCANCODE_MASK, + SDLK_KP_000 = (int)SDL_Scancode.SDL_SCANCODE_KP_000 | SDLK_SCANCODE_MASK, + SDLK_THOUSANDSSEPARATOR = + (int)SDL_Scancode.SDL_SCANCODE_THOUSANDSSEPARATOR | SDLK_SCANCODE_MASK, + SDLK_DECIMALSEPARATOR = + (int)SDL_Scancode.SDL_SCANCODE_DECIMALSEPARATOR | SDLK_SCANCODE_MASK, + SDLK_CURRENCYUNIT = (int)SDL_Scancode.SDL_SCANCODE_CURRENCYUNIT | SDLK_SCANCODE_MASK, + SDLK_CURRENCYSUBUNIT = + (int)SDL_Scancode.SDL_SCANCODE_CURRENCYSUBUNIT | SDLK_SCANCODE_MASK, + SDLK_KP_LEFTPAREN = (int)SDL_Scancode.SDL_SCANCODE_KP_LEFTPAREN | SDLK_SCANCODE_MASK, + SDLK_KP_RIGHTPAREN = (int)SDL_Scancode.SDL_SCANCODE_KP_RIGHTPAREN | SDLK_SCANCODE_MASK, + SDLK_KP_LEFTBRACE = (int)SDL_Scancode.SDL_SCANCODE_KP_LEFTBRACE | SDLK_SCANCODE_MASK, + SDLK_KP_RIGHTBRACE = (int)SDL_Scancode.SDL_SCANCODE_KP_RIGHTBRACE | SDLK_SCANCODE_MASK, + SDLK_KP_TAB = (int)SDL_Scancode.SDL_SCANCODE_KP_TAB | SDLK_SCANCODE_MASK, + SDLK_KP_BACKSPACE = (int)SDL_Scancode.SDL_SCANCODE_KP_BACKSPACE | SDLK_SCANCODE_MASK, + SDLK_KP_A = (int)SDL_Scancode.SDL_SCANCODE_KP_A | SDLK_SCANCODE_MASK, + SDLK_KP_B = (int)SDL_Scancode.SDL_SCANCODE_KP_B | SDLK_SCANCODE_MASK, + SDLK_KP_C = (int)SDL_Scancode.SDL_SCANCODE_KP_C | SDLK_SCANCODE_MASK, + SDLK_KP_D = (int)SDL_Scancode.SDL_SCANCODE_KP_D | SDLK_SCANCODE_MASK, + SDLK_KP_E = (int)SDL_Scancode.SDL_SCANCODE_KP_E | SDLK_SCANCODE_MASK, + SDLK_KP_F = (int)SDL_Scancode.SDL_SCANCODE_KP_F | SDLK_SCANCODE_MASK, + SDLK_KP_XOR = (int)SDL_Scancode.SDL_SCANCODE_KP_XOR | SDLK_SCANCODE_MASK, + SDLK_KP_POWER = (int)SDL_Scancode.SDL_SCANCODE_KP_POWER | SDLK_SCANCODE_MASK, + SDLK_KP_PERCENT = (int)SDL_Scancode.SDL_SCANCODE_KP_PERCENT | SDLK_SCANCODE_MASK, + SDLK_KP_LESS = (int)SDL_Scancode.SDL_SCANCODE_KP_LESS | SDLK_SCANCODE_MASK, + SDLK_KP_GREATER = (int)SDL_Scancode.SDL_SCANCODE_KP_GREATER | SDLK_SCANCODE_MASK, + SDLK_KP_AMPERSAND = (int)SDL_Scancode.SDL_SCANCODE_KP_AMPERSAND | SDLK_SCANCODE_MASK, + SDLK_KP_DBLAMPERSAND = + (int)SDL_Scancode.SDL_SCANCODE_KP_DBLAMPERSAND | SDLK_SCANCODE_MASK, + SDLK_KP_VERTICALBAR = + (int)SDL_Scancode.SDL_SCANCODE_KP_VERTICALBAR | SDLK_SCANCODE_MASK, + SDLK_KP_DBLVERTICALBAR = + (int)SDL_Scancode.SDL_SCANCODE_KP_DBLVERTICALBAR | SDLK_SCANCODE_MASK, + SDLK_KP_COLON = (int)SDL_Scancode.SDL_SCANCODE_KP_COLON | SDLK_SCANCODE_MASK, + SDLK_KP_HASH = (int)SDL_Scancode.SDL_SCANCODE_KP_HASH | SDLK_SCANCODE_MASK, + SDLK_KP_SPACE = (int)SDL_Scancode.SDL_SCANCODE_KP_SPACE | SDLK_SCANCODE_MASK, + SDLK_KP_AT = (int)SDL_Scancode.SDL_SCANCODE_KP_AT | SDLK_SCANCODE_MASK, + SDLK_KP_EXCLAM = (int)SDL_Scancode.SDL_SCANCODE_KP_EXCLAM | SDLK_SCANCODE_MASK, + SDLK_KP_MEMSTORE = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMSTORE | SDLK_SCANCODE_MASK, + SDLK_KP_MEMRECALL = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMRECALL | SDLK_SCANCODE_MASK, + SDLK_KP_MEMCLEAR = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMCLEAR | SDLK_SCANCODE_MASK, + SDLK_KP_MEMADD = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMADD | SDLK_SCANCODE_MASK, + SDLK_KP_MEMSUBTRACT = + (int)SDL_Scancode.SDL_SCANCODE_KP_MEMSUBTRACT | SDLK_SCANCODE_MASK, + SDLK_KP_MEMMULTIPLY = + (int)SDL_Scancode.SDL_SCANCODE_KP_MEMMULTIPLY | SDLK_SCANCODE_MASK, + SDLK_KP_MEMDIVIDE = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMDIVIDE | SDLK_SCANCODE_MASK, + SDLK_KP_PLUSMINUS = (int)SDL_Scancode.SDL_SCANCODE_KP_PLUSMINUS | SDLK_SCANCODE_MASK, + SDLK_KP_CLEAR = (int)SDL_Scancode.SDL_SCANCODE_KP_CLEAR | SDLK_SCANCODE_MASK, + SDLK_KP_CLEARENTRY = (int)SDL_Scancode.SDL_SCANCODE_KP_CLEARENTRY | SDLK_SCANCODE_MASK, + SDLK_KP_BINARY = (int)SDL_Scancode.SDL_SCANCODE_KP_BINARY | SDLK_SCANCODE_MASK, + SDLK_KP_OCTAL = (int)SDL_Scancode.SDL_SCANCODE_KP_OCTAL | SDLK_SCANCODE_MASK, + SDLK_KP_DECIMAL = (int)SDL_Scancode.SDL_SCANCODE_KP_DECIMAL | SDLK_SCANCODE_MASK, + SDLK_KP_HEXADECIMAL = + (int)SDL_Scancode.SDL_SCANCODE_KP_HEXADECIMAL | SDLK_SCANCODE_MASK, + + SDLK_LCTRL = (int)SDL_Scancode.SDL_SCANCODE_LCTRL | SDLK_SCANCODE_MASK, + SDLK_LSHIFT = (int)SDL_Scancode.SDL_SCANCODE_LSHIFT | SDLK_SCANCODE_MASK, + SDLK_LALT = (int)SDL_Scancode.SDL_SCANCODE_LALT | SDLK_SCANCODE_MASK, + SDLK_LGUI = (int)SDL_Scancode.SDL_SCANCODE_LGUI | SDLK_SCANCODE_MASK, + SDLK_RCTRL = (int)SDL_Scancode.SDL_SCANCODE_RCTRL | SDLK_SCANCODE_MASK, + SDLK_RSHIFT = (int)SDL_Scancode.SDL_SCANCODE_RSHIFT | SDLK_SCANCODE_MASK, + SDLK_RALT = (int)SDL_Scancode.SDL_SCANCODE_RALT | SDLK_SCANCODE_MASK, + SDLK_RGUI = (int)SDL_Scancode.SDL_SCANCODE_RGUI | SDLK_SCANCODE_MASK, + + SDLK_MODE = (int)SDL_Scancode.SDL_SCANCODE_MODE | SDLK_SCANCODE_MASK, + + SDLK_AUDIONEXT = (int)SDL_Scancode.SDL_SCANCODE_AUDIONEXT | SDLK_SCANCODE_MASK, + SDLK_AUDIOPREV = (int)SDL_Scancode.SDL_SCANCODE_AUDIOPREV | SDLK_SCANCODE_MASK, + SDLK_AUDIOSTOP = (int)SDL_Scancode.SDL_SCANCODE_AUDIOSTOP | SDLK_SCANCODE_MASK, + SDLK_AUDIOPLAY = (int)SDL_Scancode.SDL_SCANCODE_AUDIOPLAY | SDLK_SCANCODE_MASK, + SDLK_AUDIOMUTE = (int)SDL_Scancode.SDL_SCANCODE_AUDIOMUTE | SDLK_SCANCODE_MASK, + SDLK_MEDIASELECT = (int)SDL_Scancode.SDL_SCANCODE_MEDIASELECT | SDLK_SCANCODE_MASK, + SDLK_WWW = (int)SDL_Scancode.SDL_SCANCODE_WWW | SDLK_SCANCODE_MASK, + SDLK_MAIL = (int)SDL_Scancode.SDL_SCANCODE_MAIL | SDLK_SCANCODE_MASK, + SDLK_CALCULATOR = (int)SDL_Scancode.SDL_SCANCODE_CALCULATOR | SDLK_SCANCODE_MASK, + SDLK_COMPUTER = (int)SDL_Scancode.SDL_SCANCODE_COMPUTER | SDLK_SCANCODE_MASK, + SDLK_AC_SEARCH = (int)SDL_Scancode.SDL_SCANCODE_AC_SEARCH | SDLK_SCANCODE_MASK, + SDLK_AC_HOME = (int)SDL_Scancode.SDL_SCANCODE_AC_HOME | SDLK_SCANCODE_MASK, + SDLK_AC_BACK = (int)SDL_Scancode.SDL_SCANCODE_AC_BACK | SDLK_SCANCODE_MASK, + SDLK_AC_FORWARD = (int)SDL_Scancode.SDL_SCANCODE_AC_FORWARD | SDLK_SCANCODE_MASK, + SDLK_AC_STOP = (int)SDL_Scancode.SDL_SCANCODE_AC_STOP | SDLK_SCANCODE_MASK, + SDLK_AC_REFRESH = (int)SDL_Scancode.SDL_SCANCODE_AC_REFRESH | SDLK_SCANCODE_MASK, + SDLK_AC_BOOKMARKS = (int)SDL_Scancode.SDL_SCANCODE_AC_BOOKMARKS | SDLK_SCANCODE_MASK, + + SDLK_BRIGHTNESSDOWN = + (int)SDL_Scancode.SDL_SCANCODE_BRIGHTNESSDOWN | SDLK_SCANCODE_MASK, + SDLK_BRIGHTNESSUP = (int)SDL_Scancode.SDL_SCANCODE_BRIGHTNESSUP | SDLK_SCANCODE_MASK, + SDLK_DISPLAYSWITCH = (int)SDL_Scancode.SDL_SCANCODE_DISPLAYSWITCH | SDLK_SCANCODE_MASK, + SDLK_KBDILLUMTOGGLE = + (int)SDL_Scancode.SDL_SCANCODE_KBDILLUMTOGGLE | SDLK_SCANCODE_MASK, + SDLK_KBDILLUMDOWN = (int)SDL_Scancode.SDL_SCANCODE_KBDILLUMDOWN | SDLK_SCANCODE_MASK, + SDLK_KBDILLUMUP = (int)SDL_Scancode.SDL_SCANCODE_KBDILLUMUP | SDLK_SCANCODE_MASK, + SDLK_EJECT = (int)SDL_Scancode.SDL_SCANCODE_EJECT | SDLK_SCANCODE_MASK, + SDLK_SLEEP = (int)SDL_Scancode.SDL_SCANCODE_SLEEP | SDLK_SCANCODE_MASK, + SDLK_APP1 = (int)SDL_Scancode.SDL_SCANCODE_APP1 | SDLK_SCANCODE_MASK, + SDLK_APP2 = (int)SDL_Scancode.SDL_SCANCODE_APP2 | SDLK_SCANCODE_MASK, + + SDLK_AUDIOREWIND = (int)SDL_Scancode.SDL_SCANCODE_AUDIOREWIND | SDLK_SCANCODE_MASK, + SDLK_AUDIOFASTFORWARD = (int)SDL_Scancode.SDL_SCANCODE_AUDIOFASTFORWARD | SDLK_SCANCODE_MASK + } + + /* Key modifiers (bitfield) */ + [Flags] + public enum SDL_Keymod : ushort + { + KMOD_NONE = 0x0000, + KMOD_LSHIFT = 0x0001, + KMOD_RSHIFT = 0x0002, + KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, + KMOD_LALT = 0x0100, + KMOD_RALT = 0x0200, + KMOD_LGUI = 0x0400, + KMOD_RGUI = 0x0800, + KMOD_NUM = 0x1000, + KMOD_CAPS = 0x2000, + KMOD_MODE = 0x4000, + KMOD_SCROLL = 0x8000, + + /* These are defines in the SDL headers */ + KMOD_CTRL = (KMOD_LCTRL | KMOD_RCTRL), + KMOD_SHIFT = (KMOD_LSHIFT | KMOD_RSHIFT), + KMOD_ALT = (KMOD_LALT | KMOD_RALT), + KMOD_GUI = (KMOD_LGUI | KMOD_RGUI), + + KMOD_RESERVED = KMOD_SCROLL + } + + #endregion + + #region SDL_keyboard.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Keysym + { + public SDL_Scancode scancode; + public SDL_Keycode sym; + public SDL_Keymod mod; /* UInt16 */ + public UInt32 unicode; /* Deprecated */ + } + + #endregion + + #region SDL_mouse.c + + /* Note: SDL_Cursor is a typedef normally. We'll treat it as + * an IntPtr, because C# doesn't do typedefs. Yay! + */ + + /* System cursor types */ + public enum SDL_SystemCursor + { + SDL_SYSTEM_CURSOR_ARROW, // Arrow + SDL_SYSTEM_CURSOR_IBEAM, // I-beam + SDL_SYSTEM_CURSOR_WAIT, // Wait + SDL_SYSTEM_CURSOR_CROSSHAIR, // Crosshair + SDL_SYSTEM_CURSOR_WAITARROW, // Small wait cursor (or Wait if not available) + SDL_SYSTEM_CURSOR_SIZENWSE, // Double arrow pointing northwest and southeast + SDL_SYSTEM_CURSOR_SIZENESW, // Double arrow pointing northeast and southwest + SDL_SYSTEM_CURSOR_SIZEWE, // Double arrow pointing west and east + SDL_SYSTEM_CURSOR_SIZENS, // Double arrow pointing north and south + SDL_SYSTEM_CURSOR_SIZEALL, // Four pointed arrow pointing north, south, east, and west + SDL_SYSTEM_CURSOR_NO, // Slashed circle or crossbones + SDL_SYSTEM_CURSOR_HAND, // Hand + SDL_NUM_SYSTEM_CURSORS + } + + /* Get the current state of the mouse, in relation to the desktop. + * Only available in 2.0.4 or higher. + * This overload allows for passing NULL to both x and y + */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern UInt32 SDL_GetGlobalMouseState(int* x, int* y); + + /* Set the mouse cursor's position (within a window) */ + /* window is an SDL_Window pointer */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_WarpMouseInWindow(IntPtr window, int x, int y); + + /* Create a cursor from a system cursor id. + * return value is an SDL_Cursor pointer + */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr SDL_CreateSystemCursor(SDL_SystemCursor id); + + /* Set the active cursor. + * cursor is an SDL_Cursor pointer + */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void SDL_SetCursor(IntPtr cursor); + + /* Toggle whether the cursor is shown */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int SDL_ShowCursor(SDL_bool toggle); + + public const uint SDL_BUTTON_LEFT = 1; + public const uint SDL_BUTTON_MIDDLE = 2; + public const uint SDL_BUTTON_RIGHT = 3; + public const uint SDL_BUTTON_X1 = 4; + public const uint SDL_BUTTON_X2 = 5; + + #endregion + + #region SDL_timer.h + + /* System timers rely on different OS mechanisms depending on + * which operating system SDL2 is compiled against. + */ + + /* Get the current value of the high resolution counter */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern UInt64 SDL_GetPerformanceCounter(); + + /* Get the count per second of the high resolution counter */ + [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] + public static extern UInt64 SDL_GetPerformanceFrequency(); + + #endregion +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.SDL3/BUTR.CrashReport.SDL3.csproj b/src/BUTR.CrashReport.SDL3/BUTR.CrashReport.SDL3.csproj new file mode 100644 index 0000000..302074c --- /dev/null +++ b/src/BUTR.CrashReport.SDL3/BUTR.CrashReport.SDL3.csproj @@ -0,0 +1,17 @@ + + + + net9.0 + enable + enable + latest + true + SDL3 + + + + + + + + diff --git a/src/BUTR.CrashReport.SDL3/SDL.cs b/src/BUTR.CrashReport.SDL3/SDL.cs new file mode 100644 index 0000000..6996a91 --- /dev/null +++ b/src/BUTR.CrashReport.SDL3/SDL.cs @@ -0,0 +1,8091 @@ +// NOTE: This file is auto-generated. +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; + +namespace SDL3; + +public static unsafe partial class SDL +{ + // Custom marshaller for SDL-owned strings returned by SDL. + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(SDLOwnedStringMarshaller))] + public static unsafe class SDLOwnedStringMarshaller + { + /// + /// Converts an unmanaged string to a managed version. + /// + /// A managed string. + public static string ConvertToManaged(byte* unmanaged) + => Marshal.PtrToStringUTF8((IntPtr) unmanaged); + } + + // Custom marshaller for caller-owned strings returned by SDL. + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(CallerOwnedStringMarshaller))] + public static unsafe class CallerOwnedStringMarshaller + { + /// + /// Converts an unmanaged string to a managed version. + /// + /// A managed string. + public static string ConvertToManaged(byte* unmanaged) + => Marshal.PtrToStringUTF8((IntPtr) unmanaged); + + /// + /// Free the memory for a specified unmanaged string. + /// + public static void Free(byte* unmanaged) + => SDL_free((IntPtr) unmanaged); + } + + // Taken from https://github.com/ppy/SDL3-CS + // C# bools are not blittable, so we need this workaround + public readonly record struct SDLBool + { + private readonly byte value; + + internal const byte FALSE_VALUE = 0; + internal const byte TRUE_VALUE = 1; + + internal SDLBool(byte value) + { + this.value = value; + } + + public static implicit operator bool(SDLBool b) + { + return b.value != FALSE_VALUE; + } + + public static implicit operator SDLBool(bool b) + { + return new SDLBool(b ? TRUE_VALUE : FALSE_VALUE); + } + + public bool Equals(SDLBool other) + { + return other.value == value; + } + + public override int GetHashCode() + { + return value.GetHashCode(); + } + } + + private const string nativeLibName = "SDL3"; + + // /usr/local/include/SDL3/SDL_stdinc.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_malloc(UIntPtr size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_free(IntPtr mem); + + // /usr/local/include/SDL3/SDL_assert.h + + public enum SDL_AssertState + { + SDL_ASSERTION_RETRY = 0, + SDL_ASSERTION_BREAK = 1, + SDL_ASSERTION_ABORT = 2, + SDL_ASSERTION_IGNORE = 3, + SDL_ASSERTION_ALWAYS_IGNORE = 4, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_AssertData + { + public SDLBool always_ignore; + public uint trigger_count; + public byte* condition; + public byte* filename; + public int linenum; + public byte* function; + public SDL_AssertData* next; + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_AssertState SDL_ReportAssertion(ref SDL_AssertData data, string func, string file, int line); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate SDL_AssertState SDL_AssertionHandler(SDL_AssertData* data, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetAssertionHandler(SDL_AssertionHandler handler, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetDefaultAssertionHandler(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAssertionHandler(out IntPtr puserdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAssertionReport(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ResetAssertionReport(); + + // /usr/local/include/SDL3/SDL_atomic.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TryLockSpinlock(IntPtr @lock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LockSpinlock(IntPtr @lock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockSpinlock(IntPtr @lock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_MemoryBarrierReleaseFunction(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_MemoryBarrierAcquireFunction(); + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_AtomicInt + { + public int value; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CompareAndSwapAtomicInt(ref SDL_AtomicInt a, int oldval, int newval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_SetAtomicInt(ref SDL_AtomicInt a, int v); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetAtomicInt(ref SDL_AtomicInt a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_AddAtomicInt(ref SDL_AtomicInt a, int v); + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_AtomicU32 + { + public uint value; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CompareAndSwapAtomicU32(ref SDL_AtomicU32 a, uint oldval, uint newval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_SetAtomicU32(ref SDL_AtomicU32 a, uint v); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetAtomicU32(ref SDL_AtomicU32 a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CompareAndSwapAtomicPointer(ref IntPtr a, IntPtr oldval, IntPtr newval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_SetAtomicPointer(ref IntPtr a, IntPtr v); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAtomicPointer(ref IntPtr a); + + // /usr/local/include/SDL3/SDL_endian.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_SwapFloat(float x); + + // /usr/local/include/SDL3/SDL_error.h + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetError(string fmt); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_OutOfMemory(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetError(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClearError(); + + // /usr/local/include/SDL3/SDL_properties.h + + public enum SDL_PropertyType + { + SDL_PROPERTY_TYPE_INVALID = 0, + SDL_PROPERTY_TYPE_POINTER = 1, + SDL_PROPERTY_TYPE_STRING = 2, + SDL_PROPERTY_TYPE_NUMBER = 3, + SDL_PROPERTY_TYPE_FLOAT = 4, + SDL_PROPERTY_TYPE_BOOLEAN = 5, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetGlobalProperties(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_CreateProperties(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CopyProperties(uint src, uint dst); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LockProperties(uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockProperties(uint props); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_CleanupPropertyCallback(IntPtr userdata, IntPtr value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetPointerPropertyWithCleanup(uint props, string name, IntPtr value, SDL_CleanupPropertyCallback cleanup, IntPtr userdata); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetPointerProperty(uint props, string name, IntPtr value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetStringProperty(uint props, string name, string value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetNumberProperty(uint props, string name, long value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetFloatProperty(uint props, string name, float value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetBooleanProperty(uint props, string name, SDLBool value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasProperty(uint props, string name); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_PropertyType SDL_GetPropertyType(uint props, string name); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetPointerProperty(uint props, string name, IntPtr default_value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetStringProperty(uint props, string name, string default_value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long SDL_GetNumberProperty(uint props, string name, long default_value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetFloatProperty(uint props, string name, float default_value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetBooleanProperty(uint props, string name, SDLBool default_value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClearProperty(uint props, string name); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_EnumeratePropertiesCallback(IntPtr userdata, uint props, byte* name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_EnumerateProperties(uint props, SDL_EnumeratePropertiesCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyProperties(uint props); + + // /usr/local/include/SDL3/SDL_thread.h + + public const string SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER = "SDL.thread.create.entry_function"; + public const string SDL_PROP_THREAD_CREATE_NAME_STRING = "SDL.thread.create.name"; + public const string SDL_PROP_THREAD_CREATE_USERDATA_POINTER = "SDL.thread.create.userdata"; + public const string SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER = "SDL.thread.create.stacksize"; + + public enum SDL_ThreadPriority + { + SDL_THREAD_PRIORITY_LOW = 0, + SDL_THREAD_PRIORITY_NORMAL = 1, + SDL_THREAD_PRIORITY_HIGH = 2, + SDL_THREAD_PRIORITY_TIME_CRITICAL = 3, + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int SDL_ThreadFunction(IntPtr data); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateThreadRuntime(SDL_ThreadFunction fn, string name, IntPtr data, IntPtr pfnBeginThread, IntPtr pfnEndThread); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateThreadWithPropertiesRuntime(uint props, IntPtr pfnBeginThread, IntPtr pfnEndThread); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetThreadName(IntPtr thread); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetCurrentThreadID(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetThreadID(IntPtr thread); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_WaitThread(IntPtr thread, IntPtr status); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DetachThread(IntPtr thread); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetTLS(IntPtr id); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_TLSDestructorCallback(IntPtr value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTLS(IntPtr id, IntPtr value, SDL_TLSDestructorCallback destructor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CleanupTLS(); + + // /usr/local/include/SDL3/SDL_mutex.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateMutex(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LockMutex(IntPtr mutex); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TryLockMutex(IntPtr mutex); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockMutex(IntPtr mutex); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyMutex(IntPtr mutex); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateRWLock(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LockRWLockForReading(IntPtr rwlock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LockRWLockForWriting(IntPtr rwlock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TryLockRWLockForReading(IntPtr rwlock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TryLockRWLockForWriting(IntPtr rwlock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockRWLock(IntPtr rwlock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyRWLock(IntPtr rwlock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateSemaphore(uint initial_value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroySemaphore(IntPtr sem); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_WaitSemaphore(IntPtr sem); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TryWaitSemaphore(IntPtr sem); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitSemaphoreTimeout(IntPtr sem, int timeoutMS); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SignalSemaphore(IntPtr sem); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetSemaphoreValue(IntPtr sem); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateCondition(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyCondition(IntPtr cond); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SignalCondition(IntPtr cond); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BroadcastCondition(IntPtr cond); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_WaitCondition(IntPtr cond, IntPtr mutex); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitConditionTimeout(IntPtr cond, IntPtr mutex, int timeoutMS); + + public enum SDL_InitStatus + { + SDL_INIT_STATUS_UNINITIALIZED = 0, + SDL_INIT_STATUS_INITIALIZING = 1, + SDL_INIT_STATUS_INITIALIZED = 2, + SDL_INIT_STATUS_UNINITIALIZING = 3, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_InitState + { + public SDL_AtomicInt status; + public ulong thread; + public IntPtr reserved; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShouldInit(ref SDL_InitState state); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShouldQuit(ref SDL_InitState state); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetInitialized(ref SDL_InitState state, SDLBool initialized); + + // /usr/local/include/SDL3/SDL_iostream.h + + public const string SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER = "SDL.iostream.windows.handle"; + public const string SDL_PROP_IOSTREAM_STDIO_FILE_POINTER = "SDL.iostream.stdio.file"; + public const string SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER = "SDL.iostream.file_descriptor"; + public const string SDL_PROP_IOSTREAM_ANDROID_AASSET_POINTER = "SDL.iostream.android.aasset"; + public const string SDL_PROP_IOSTREAM_MEMORY_POINTER = "SDL.iostream.memory.base"; + public const string SDL_PROP_IOSTREAM_MEMORY_SIZE_NUMBER = "SDL.iostream.memory.size"; + public const string SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER = "SDL.iostream.dynamic.memory"; + public const string SDL_PROP_IOSTREAM_DYNAMIC_CHUNKSIZE_NUMBER = "SDL.iostream.dynamic.chunksize"; + + public enum SDL_IOStatus + { + SDL_IO_STATUS_READY = 0, + SDL_IO_STATUS_ERROR = 1, + SDL_IO_STATUS_EOF = 2, + SDL_IO_STATUS_NOT_READY = 3, + SDL_IO_STATUS_READONLY = 4, + SDL_IO_STATUS_WRITEONLY = 5, + } + + public enum SDL_IOWhence + { + SDL_IO_SEEK_SET = 0, + SDL_IO_SEEK_CUR = 1, + SDL_IO_SEEK_END = 2, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_IOStreamInterface + { + public uint version; + public IntPtr size; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr seek; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr read; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr write; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr flush; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr close; // WARN_ANONYMOUS_FUNCTION_POINTER + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_IOFromFile(string file, string mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_IOFromMem(IntPtr mem, UIntPtr size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_IOFromConstMem(IntPtr mem, UIntPtr size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_IOFromDynamicMem(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenIO(ref SDL_IOStreamInterface iface, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CloseIO(IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetIOProperties(IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_IOStatus SDL_GetIOStatus(IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long SDL_GetIOSize(IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long SDL_SeekIO(IntPtr context, long offset, SDL_IOWhence whence); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long SDL_TellIO(IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial UIntPtr SDL_ReadIO(IntPtr context, IntPtr ptr, UIntPtr size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial UIntPtr SDL_WriteIO(IntPtr context, IntPtr ptr, UIntPtr size); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial UIntPtr SDL_IOprintf(IntPtr context, string fmt); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FlushIO(IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_LoadFile_IO(IntPtr src, out UIntPtr datasize, SDLBool closeio); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_LoadFile(string file, out UIntPtr datasize); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU8(IntPtr src, out byte value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS8(IntPtr src, out sbyte value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU16LE(IntPtr src, out ushort value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS16LE(IntPtr src, out short value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU16BE(IntPtr src, out ushort value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS16BE(IntPtr src, out short value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU32LE(IntPtr src, out uint value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS32LE(IntPtr src, out int value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU32BE(IntPtr src, out uint value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS32BE(IntPtr src, out int value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU64LE(IntPtr src, out ulong value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS64LE(IntPtr src, out long value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadU64BE(IntPtr src, out ulong value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadS64BE(IntPtr src, out long value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU8(IntPtr dst, byte value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS8(IntPtr dst, sbyte value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU16LE(IntPtr dst, ushort value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS16LE(IntPtr dst, short value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU16BE(IntPtr dst, ushort value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS16BE(IntPtr dst, short value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU32LE(IntPtr dst, uint value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS32LE(IntPtr dst, int value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU32BE(IntPtr dst, uint value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS32BE(IntPtr dst, int value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU64LE(IntPtr dst, ulong value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS64LE(IntPtr dst, long value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteU64BE(IntPtr dst, ulong value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteS64BE(IntPtr dst, long value); + + // /usr/local/include/SDL3/SDL_audio.h + + public enum SDL_AudioFormat + { + SDL_AUDIO_UNKNOWN = 0, + SDL_AUDIO_U8 = 8, + SDL_AUDIO_S8 = 32776, + SDL_AUDIO_S16LE = 32784, + SDL_AUDIO_S16BE = 36880, + SDL_AUDIO_S32LE = 32800, + SDL_AUDIO_S32BE = 36896, + SDL_AUDIO_F32LE = 33056, + SDL_AUDIO_F32BE = 37152, + SDL_AUDIO_S16 = 32784, + SDL_AUDIO_S32 = 32800, + SDL_AUDIO_F32 = 33056, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_AudioSpec + { + public SDL_AudioFormat format; + public int channels; + public int freq; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumAudioDrivers(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetAudioDriver(int index); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetCurrentAudioDriver(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAudioPlaybackDevices(out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAudioRecordingDevices(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetAudioDeviceName(uint devid); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetAudioDeviceFormat(uint devid, out SDL_AudioSpec spec, out int sample_frames); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAudioDeviceChannelMap(uint devid, out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_OpenAudioDevice(uint devid, ref SDL_AudioSpec spec); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PauseAudioDevice(uint dev); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ResumeAudioDevice(uint dev); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_AudioDevicePaused(uint dev); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetAudioDeviceGain(uint devid); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioDeviceGain(uint devid, float gain); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CloseAudioDevice(uint devid); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BindAudioStreams(uint devid, Span streams, int num_streams); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BindAudioStream(uint devid, IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnbindAudioStreams(Span streams, int num_streams); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnbindAudioStream(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetAudioStreamDevice(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateAudioStream(ref SDL_AudioSpec src_spec, ref SDL_AudioSpec dst_spec); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetAudioStreamProperties(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetAudioStreamFormat(IntPtr stream, out SDL_AudioSpec src_spec, out SDL_AudioSpec dst_spec); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamFormat(IntPtr stream, ref SDL_AudioSpec src_spec, ref SDL_AudioSpec dst_spec); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetAudioStreamFrequencyRatio(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamFrequencyRatio(IntPtr stream, float ratio); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetAudioStreamGain(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamGain(IntPtr stream, float gain); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAudioStreamInputChannelMap(IntPtr stream, out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetAudioStreamOutputChannelMap(IntPtr stream, out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamInputChannelMap(IntPtr stream, Span chmap, int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamOutputChannelMap(IntPtr stream, Span chmap, int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PutAudioStreamData(IntPtr stream, IntPtr buf, int len); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetAudioStreamData(IntPtr stream, IntPtr buf, int len); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetAudioStreamAvailable(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetAudioStreamQueued(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FlushAudioStream(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClearAudioStream(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PauseAudioStreamDevice(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ResumeAudioStreamDevice(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LockAudioStream(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UnlockAudioStream(IntPtr stream); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_AudioStreamCallback(IntPtr userdata, IntPtr stream, int additional_amount, int total_amount); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamGetCallback(IntPtr stream, SDL_AudioStreamCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioStreamPutCallback(IntPtr stream, SDL_AudioStreamCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyAudioStream(IntPtr stream); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenAudioDeviceStream(uint devid, ref SDL_AudioSpec spec, SDL_AudioStreamCallback callback, IntPtr userdata); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_AudioPostmixCallback(IntPtr userdata, SDL_AudioSpec* spec, float* buffer, int buflen); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAudioPostmixCallback(uint devid, SDL_AudioPostmixCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LoadWAV_IO(IntPtr src, SDLBool closeio, out SDL_AudioSpec spec, out IntPtr audio_buf, out uint audio_len); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LoadWAV(string path, out SDL_AudioSpec spec, out IntPtr audio_buf, out uint audio_len); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_MixAudio(IntPtr dst, IntPtr src, SDL_AudioFormat format, uint len, float volume); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ConvertAudioSamples(ref SDL_AudioSpec src_spec, IntPtr src_data, int src_len, ref SDL_AudioSpec dst_spec, IntPtr dst_data, out int dst_len); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetAudioFormatName(SDL_AudioFormat format); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetSilenceValueForFormat(SDL_AudioFormat format); + + // /usr/local/include/SDL3/SDL_bits.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_MostSignificantBitIndex32(uint x); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasExactlyOneBitSet32(uint x); + + // /usr/local/include/SDL3/SDL_blendmode.h + + public enum SDL_BlendOperation + { + SDL_BLENDOPERATION_ADD = 1, + SDL_BLENDOPERATION_SUBTRACT = 2, + SDL_BLENDOPERATION_REV_SUBTRACT = 3, + SDL_BLENDOPERATION_MINIMUM = 4, + SDL_BLENDOPERATION_MAXIMUM = 5, + } + + public enum SDL_BlendFactor + { + SDL_BLENDFACTOR_ZERO = 1, + SDL_BLENDFACTOR_ONE = 2, + SDL_BLENDFACTOR_SRC_COLOR = 3, + SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR = 4, + SDL_BLENDFACTOR_SRC_ALPHA = 5, + SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA = 6, + SDL_BLENDFACTOR_DST_COLOR = 7, + SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR = 8, + SDL_BLENDFACTOR_DST_ALPHA = 9, + SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA = 10, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, SDL_BlendOperation colorOperation, SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, SDL_BlendOperation alphaOperation); + + // /usr/local/include/SDL3/SDL_pixels.h + + public enum SDL_PixelType + { + SDL_PIXELTYPE_UNKNOWN = 0, + SDL_PIXELTYPE_INDEX1 = 1, + SDL_PIXELTYPE_INDEX4 = 2, + SDL_PIXELTYPE_INDEX8 = 3, + SDL_PIXELTYPE_PACKED8 = 4, + SDL_PIXELTYPE_PACKED16 = 5, + SDL_PIXELTYPE_PACKED32 = 6, + SDL_PIXELTYPE_ARRAYU8 = 7, + SDL_PIXELTYPE_ARRAYU16 = 8, + SDL_PIXELTYPE_ARRAYU32 = 9, + SDL_PIXELTYPE_ARRAYF16 = 10, + SDL_PIXELTYPE_ARRAYF32 = 11, + SDL_PIXELTYPE_INDEX2 = 12, + } + + public enum SDL_BitmapOrder + { + SDL_BITMAPORDER_NONE = 0, + SDL_BITMAPORDER_4321 = 1, + SDL_BITMAPORDER_1234 = 2, + } + + public enum SDL_PackedOrder + { + SDL_PACKEDORDER_NONE = 0, + SDL_PACKEDORDER_XRGB = 1, + SDL_PACKEDORDER_RGBX = 2, + SDL_PACKEDORDER_ARGB = 3, + SDL_PACKEDORDER_RGBA = 4, + SDL_PACKEDORDER_XBGR = 5, + SDL_PACKEDORDER_BGRX = 6, + SDL_PACKEDORDER_ABGR = 7, + SDL_PACKEDORDER_BGRA = 8, + } + + public enum SDL_ArrayOrder + { + SDL_ARRAYORDER_NONE = 0, + SDL_ARRAYORDER_RGB = 1, + SDL_ARRAYORDER_RGBA = 2, + SDL_ARRAYORDER_ARGB = 3, + SDL_ARRAYORDER_BGR = 4, + SDL_ARRAYORDER_BGRA = 5, + SDL_ARRAYORDER_ABGR = 6, + } + + public enum SDL_PackedLayout + { + SDL_PACKEDLAYOUT_NONE = 0, + SDL_PACKEDLAYOUT_332 = 1, + SDL_PACKEDLAYOUT_4444 = 2, + SDL_PACKEDLAYOUT_1555 = 3, + SDL_PACKEDLAYOUT_5551 = 4, + SDL_PACKEDLAYOUT_565 = 5, + SDL_PACKEDLAYOUT_8888 = 6, + SDL_PACKEDLAYOUT_2101010 = 7, + SDL_PACKEDLAYOUT_1010102 = 8, + } + + public enum SDL_PixelFormat + { + SDL_PIXELFORMAT_UNKNOWN = 0, + SDL_PIXELFORMAT_INDEX1LSB = 286261504, + SDL_PIXELFORMAT_INDEX1MSB = 287310080, + SDL_PIXELFORMAT_INDEX2LSB = 470811136, + SDL_PIXELFORMAT_INDEX2MSB = 471859712, + SDL_PIXELFORMAT_INDEX4LSB = 303039488, + SDL_PIXELFORMAT_INDEX4MSB = 304088064, + SDL_PIXELFORMAT_INDEX8 = 318769153, + SDL_PIXELFORMAT_RGB332 = 336660481, + SDL_PIXELFORMAT_XRGB4444 = 353504258, + SDL_PIXELFORMAT_XBGR4444 = 357698562, + SDL_PIXELFORMAT_XRGB1555 = 353570562, + SDL_PIXELFORMAT_XBGR1555 = 357764866, + SDL_PIXELFORMAT_ARGB4444 = 355602434, + SDL_PIXELFORMAT_RGBA4444 = 356651010, + SDL_PIXELFORMAT_ABGR4444 = 359796738, + SDL_PIXELFORMAT_BGRA4444 = 360845314, + SDL_PIXELFORMAT_ARGB1555 = 355667970, + SDL_PIXELFORMAT_RGBA5551 = 356782082, + SDL_PIXELFORMAT_ABGR1555 = 359862274, + SDL_PIXELFORMAT_BGRA5551 = 360976386, + SDL_PIXELFORMAT_RGB565 = 353701890, + SDL_PIXELFORMAT_BGR565 = 357896194, + SDL_PIXELFORMAT_RGB24 = 386930691, + SDL_PIXELFORMAT_BGR24 = 390076419, + SDL_PIXELFORMAT_XRGB8888 = 370546692, + SDL_PIXELFORMAT_RGBX8888 = 371595268, + SDL_PIXELFORMAT_XBGR8888 = 374740996, + SDL_PIXELFORMAT_BGRX8888 = 375789572, + SDL_PIXELFORMAT_ARGB8888 = 372645892, + SDL_PIXELFORMAT_RGBA8888 = 373694468, + SDL_PIXELFORMAT_ABGR8888 = 376840196, + SDL_PIXELFORMAT_BGRA8888 = 377888772, + SDL_PIXELFORMAT_XRGB2101010 = 370614276, + SDL_PIXELFORMAT_XBGR2101010 = 374808580, + SDL_PIXELFORMAT_ARGB2101010 = 372711428, + SDL_PIXELFORMAT_ABGR2101010 = 376905732, + SDL_PIXELFORMAT_RGB48 = 403714054, + SDL_PIXELFORMAT_BGR48 = 406859782, + SDL_PIXELFORMAT_RGBA64 = 404766728, + SDL_PIXELFORMAT_ARGB64 = 405815304, + SDL_PIXELFORMAT_BGRA64 = 407912456, + SDL_PIXELFORMAT_ABGR64 = 408961032, + SDL_PIXELFORMAT_RGB48_FLOAT = 437268486, + SDL_PIXELFORMAT_BGR48_FLOAT = 440414214, + SDL_PIXELFORMAT_RGBA64_FLOAT = 438321160, + SDL_PIXELFORMAT_ARGB64_FLOAT = 439369736, + SDL_PIXELFORMAT_BGRA64_FLOAT = 441466888, + SDL_PIXELFORMAT_ABGR64_FLOAT = 442515464, + SDL_PIXELFORMAT_RGB96_FLOAT = 454057996, + SDL_PIXELFORMAT_BGR96_FLOAT = 457203724, + SDL_PIXELFORMAT_RGBA128_FLOAT = 455114768, + SDL_PIXELFORMAT_ARGB128_FLOAT = 456163344, + SDL_PIXELFORMAT_BGRA128_FLOAT = 458260496, + SDL_PIXELFORMAT_ABGR128_FLOAT = 459309072, + SDL_PIXELFORMAT_YV12 = 842094169, + SDL_PIXELFORMAT_IYUV = 1448433993, + SDL_PIXELFORMAT_YUY2 = 844715353, + SDL_PIXELFORMAT_UYVY = 1498831189, + SDL_PIXELFORMAT_YVYU = 1431918169, + SDL_PIXELFORMAT_NV12 = 842094158, + SDL_PIXELFORMAT_NV21 = 825382478, + SDL_PIXELFORMAT_P010 = 808530000, + SDL_PIXELFORMAT_EXTERNAL_OES = 542328143, + SDL_PIXELFORMAT_RGBA32 = 376840196, + SDL_PIXELFORMAT_ARGB32 = 377888772, + SDL_PIXELFORMAT_BGRA32 = 372645892, + SDL_PIXELFORMAT_ABGR32 = 373694468, + SDL_PIXELFORMAT_RGBX32 = 374740996, + SDL_PIXELFORMAT_XRGB32 = 375789572, + SDL_PIXELFORMAT_BGRX32 = 370546692, + SDL_PIXELFORMAT_XBGR32 = 371595268, + } + + public enum SDL_ColorType + { + SDL_COLOR_TYPE_UNKNOWN = 0, + SDL_COLOR_TYPE_RGB = 1, + SDL_COLOR_TYPE_YCBCR = 2, + } + + public enum SDL_ColorRange + { + SDL_COLOR_RANGE_UNKNOWN = 0, + SDL_COLOR_RANGE_LIMITED = 1, + SDL_COLOR_RANGE_FULL = 2, + } + + public enum SDL_ColorPrimaries + { + SDL_COLOR_PRIMARIES_UNKNOWN = 0, + SDL_COLOR_PRIMARIES_BT709 = 1, + SDL_COLOR_PRIMARIES_UNSPECIFIED = 2, + SDL_COLOR_PRIMARIES_BT470M = 4, + SDL_COLOR_PRIMARIES_BT470BG = 5, + SDL_COLOR_PRIMARIES_BT601 = 6, + SDL_COLOR_PRIMARIES_SMPTE240 = 7, + SDL_COLOR_PRIMARIES_GENERIC_FILM = 8, + SDL_COLOR_PRIMARIES_BT2020 = 9, + SDL_COLOR_PRIMARIES_XYZ = 10, + SDL_COLOR_PRIMARIES_SMPTE431 = 11, + SDL_COLOR_PRIMARIES_SMPTE432 = 12, + SDL_COLOR_PRIMARIES_EBU3213 = 22, + SDL_COLOR_PRIMARIES_CUSTOM = 31, + } + + public enum SDL_TransferCharacteristics + { + SDL_TRANSFER_CHARACTERISTICS_UNKNOWN = 0, + SDL_TRANSFER_CHARACTERISTICS_BT709 = 1, + SDL_TRANSFER_CHARACTERISTICS_UNSPECIFIED = 2, + SDL_TRANSFER_CHARACTERISTICS_GAMMA22 = 4, + SDL_TRANSFER_CHARACTERISTICS_GAMMA28 = 5, + SDL_TRANSFER_CHARACTERISTICS_BT601 = 6, + SDL_TRANSFER_CHARACTERISTICS_SMPTE240 = 7, + SDL_TRANSFER_CHARACTERISTICS_LINEAR = 8, + SDL_TRANSFER_CHARACTERISTICS_LOG100 = 9, + SDL_TRANSFER_CHARACTERISTICS_LOG100_SQRT10 = 10, + SDL_TRANSFER_CHARACTERISTICS_IEC61966 = 11, + SDL_TRANSFER_CHARACTERISTICS_BT1361 = 12, + SDL_TRANSFER_CHARACTERISTICS_SRGB = 13, + SDL_TRANSFER_CHARACTERISTICS_BT2020_10BIT = 14, + SDL_TRANSFER_CHARACTERISTICS_BT2020_12BIT = 15, + SDL_TRANSFER_CHARACTERISTICS_PQ = 16, + SDL_TRANSFER_CHARACTERISTICS_SMPTE428 = 17, + SDL_TRANSFER_CHARACTERISTICS_HLG = 18, + SDL_TRANSFER_CHARACTERISTICS_CUSTOM = 31, + } + + public enum SDL_MatrixCoefficients + { + SDL_MATRIX_COEFFICIENTS_IDENTITY = 0, + SDL_MATRIX_COEFFICIENTS_BT709 = 1, + SDL_MATRIX_COEFFICIENTS_UNSPECIFIED = 2, + SDL_MATRIX_COEFFICIENTS_FCC = 4, + SDL_MATRIX_COEFFICIENTS_BT470BG = 5, + SDL_MATRIX_COEFFICIENTS_BT601 = 6, + SDL_MATRIX_COEFFICIENTS_SMPTE240 = 7, + SDL_MATRIX_COEFFICIENTS_YCGCO = 8, + SDL_MATRIX_COEFFICIENTS_BT2020_NCL = 9, + SDL_MATRIX_COEFFICIENTS_BT2020_CL = 10, + SDL_MATRIX_COEFFICIENTS_SMPTE2085 = 11, + SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL = 12, + SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL = 13, + SDL_MATRIX_COEFFICIENTS_ICTCP = 14, + SDL_MATRIX_COEFFICIENTS_CUSTOM = 31, + } + + public enum SDL_ChromaLocation + { + SDL_CHROMA_LOCATION_NONE = 0, + SDL_CHROMA_LOCATION_LEFT = 1, + SDL_CHROMA_LOCATION_CENTER = 2, + SDL_CHROMA_LOCATION_TOPLEFT = 3, + } + + public enum SDL_Colorspace + { + SDL_COLORSPACE_UNKNOWN = 0, + SDL_COLORSPACE_SRGB = 301991328, + SDL_COLORSPACE_SRGB_LINEAR = 301991168, + SDL_COLORSPACE_HDR10 = 301999616, + SDL_COLORSPACE_JPEG = 570426566, + SDL_COLORSPACE_BT601_LIMITED = 554703046, + SDL_COLORSPACE_BT601_FULL = 571480262, + SDL_COLORSPACE_BT709_LIMITED = 554697761, + SDL_COLORSPACE_BT709_FULL = 571474977, + SDL_COLORSPACE_BT2020_LIMITED = 554706441, + SDL_COLORSPACE_BT2020_FULL = 571483657, + SDL_COLORSPACE_RGB_DEFAULT = 301991328, + SDL_COLORSPACE_YUV_DEFAULT = 570426566, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Color + { + public byte r; + public byte g; + public byte b; + public byte a; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_FColor + { + public float r; + public float g; + public float b; + public float a; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Palette + { + public int ncolors; + public SDL_Color* colors; + public uint version; + public int refcount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PixelFormatDetails + { + public SDL_PixelFormat format; + public byte bits_per_pixel; + public byte bytes_per_pixel; + public fixed byte padding[2]; + public uint Rmask; + public uint Gmask; + public uint Bmask; + public uint Amask; + public byte Rbits; + public byte Gbits; + public byte Bbits; + public byte Abits; + public byte Rshift; + public byte Gshift; + public byte Bshift; + public byte Ashift; + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetPixelFormatName(SDL_PixelFormat format); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetMasksForPixelFormat(SDL_PixelFormat format, out int bpp, out uint Rmask, out uint Gmask, out uint Bmask, out uint Amask); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_PixelFormat SDL_GetPixelFormatForMasks(int bpp, uint Rmask, uint Gmask, uint Bmask, uint Amask); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetPixelFormatDetails(SDL_PixelFormat format); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreatePalette(int ncolors); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetPaletteColors(IntPtr palette, Span colors, int firstcolor, int ncolors); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyPalette(IntPtr palette); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_MapRGB(IntPtr format, IntPtr palette, byte r, byte g, byte b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_MapRGBA(IntPtr format, IntPtr palette, byte r, byte g, byte b, byte a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GetRGB(uint pixel, IntPtr format, IntPtr palette, out byte r, out byte g, out byte b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GetRGBA(uint pixel, IntPtr format, IntPtr palette, out byte r, out byte g, out byte b, out byte a); + + // /usr/local/include/SDL3/SDL_rect.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Point + { + public int x; + public int y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_FPoint + { + public float x; + public float y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Rect + { + public int x; + public int y; + public int w; + public int h; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_FRect + { + public float x; + public float y; + public float w; + public float h; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_RectToFRect(ref SDL_Rect rect, out SDL_FRect frect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PointInRect(ref SDL_Point p, ref SDL_Rect r); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RectEmpty(ref SDL_Rect r); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RectsEqual(ref SDL_Rect a, ref SDL_Rect b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasRectIntersection(ref SDL_Rect A, ref SDL_Rect B); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectIntersection(ref SDL_Rect A, ref SDL_Rect B, out SDL_Rect result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectUnion(ref SDL_Rect A, ref SDL_Rect B, out SDL_Rect result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectEnclosingPoints(Span points, int count, ref SDL_Rect clip, out SDL_Rect result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectAndLineIntersection(ref SDL_Rect rect, ref int X1, ref int Y1, ref int X2, ref int Y2); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PointInRectFloat(ref SDL_FPoint p, ref SDL_FRect r); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RectEmptyFloat(ref SDL_FRect r); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RectsEqualEpsilon(ref SDL_FRect a, ref SDL_FRect b, float epsilon); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RectsEqualFloat(ref SDL_FRect a, ref SDL_FRect b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasRectIntersectionFloat(ref SDL_FRect A, ref SDL_FRect B); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectIntersectionFloat(ref SDL_FRect A, ref SDL_FRect B, out SDL_FRect result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectUnionFloat(ref SDL_FRect A, ref SDL_FRect B, out SDL_FRect result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectEnclosingPointsFloat(Span points, int count, ref SDL_FRect clip, out SDL_FRect result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRectAndLineIntersectionFloat(ref SDL_FRect rect, ref float X1, ref float Y1, ref float X2, ref float Y2); + + // /usr/local/include/SDL3/SDL_surface.h + + public const string SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT = "SDL.surface.SDR_white_point"; + public const string SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT = "SDL.surface.HDR_headroom"; + public const string SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING = "SDL.surface.tonemap"; + + [Flags] + public enum SDL_SurfaceFlags : uint + { + SDL_SURFACE_PREALLOCATED = 0x1, + SDL_SURFACE_LOCK_NEEDED = 0x2, + SDL_SURFACE_LOCKED = 0x4, + SDL_SURFACE_SIMD_ALIGNED = 0x08, + } + + public enum SDL_ScaleMode + { + SDL_SCALEMODE_NEAREST = 0, + SDL_SCALEMODE_LINEAR = 1, + } + + public enum SDL_FlipMode + { + SDL_FLIP_NONE = 0, + SDL_FLIP_HORIZONTAL = 1, + SDL_FLIP_VERTICAL = 2, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Surface + { + public SDL_SurfaceFlags flags; + public SDL_PixelFormat format; + public int w; + public int h; + public int pitch; + public IntPtr pixels; + public int refcount; + public IntPtr reserved; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateSurface(int width, int height, SDL_PixelFormat format); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateSurfaceFrom(int width, int height, SDL_PixelFormat format, IntPtr pixels, int pitch); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroySurface(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetSurfaceProperties(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceColorspace(IntPtr surface, SDL_Colorspace colorspace); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_Colorspace SDL_GetSurfaceColorspace(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateSurfacePalette(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfacePalette(IntPtr surface, IntPtr palette); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetSurfacePalette(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_AddSurfaceAlternateImage(IntPtr surface, IntPtr image); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SurfaceHasAlternateImages(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetSurfaceImages(IntPtr surface, out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_RemoveSurfaceAlternateImages(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LockSurface(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockSurface(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_LoadBMP_IO(IntPtr src, SDLBool closeio); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_LoadBMP(string file); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SaveBMP_IO(IntPtr surface, IntPtr dst, SDLBool closeio); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SaveBMP(IntPtr surface, string file); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceRLE(IntPtr surface, SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SurfaceHasRLE(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceColorKey(IntPtr surface, SDLBool enabled, uint key); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SurfaceHasColorKey(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetSurfaceColorKey(IntPtr surface, out uint key); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceColorMod(IntPtr surface, byte r, byte g, byte b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetSurfaceColorMod(IntPtr surface, out byte r, out byte g, out byte b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceAlphaMod(IntPtr surface, byte alpha); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetSurfaceAlphaMod(IntPtr surface, out byte alpha); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceBlendMode(IntPtr surface, uint blendMode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetSurfaceBlendMode(IntPtr surface, IntPtr blendMode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetSurfaceClipRect(IntPtr surface, ref SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetSurfaceClipRect(IntPtr surface, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FlipSurface(IntPtr surface, SDL_FlipMode flip); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_DuplicateSurface(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_ScaleSurface(IntPtr surface, int width, int height, SDL_ScaleMode scaleMode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_ConvertSurface(IntPtr surface, SDL_PixelFormat format); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_ConvertSurfaceAndColorspace(IntPtr surface, SDL_PixelFormat format, IntPtr palette, SDL_Colorspace colorspace, uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ConvertPixels(int width, int height, SDL_PixelFormat src_format, IntPtr src, int src_pitch, SDL_PixelFormat dst_format, IntPtr dst, int dst_pitch); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ConvertPixelsAndColorspace(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, uint src_properties, IntPtr src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, uint dst_properties, IntPtr dst, int dst_pitch); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PremultiplyAlpha(int width, int height, SDL_PixelFormat src_format, IntPtr src, int src_pitch, SDL_PixelFormat dst_format, IntPtr dst, int dst_pitch, SDLBool linear); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PremultiplySurfaceAlpha(IntPtr surface, SDLBool linear); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClearSurface(IntPtr surface, float r, float g, float b, float a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FillSurfaceRect(IntPtr dst, IntPtr rect, uint color); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FillSurfaceRects(IntPtr dst, Span rects, int count, uint color); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurface(IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurfaceUnchecked(IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurfaceScaled(IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect, SDL_ScaleMode scaleMode); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurfaceUncheckedScaled(IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect, SDL_ScaleMode scaleMode); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurfaceTiled(IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurfaceTiledWithScale(IntPtr src, IntPtr srcrect, float scale, SDL_ScaleMode scaleMode, IntPtr dst, IntPtr dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_BlitSurface9Grid(IntPtr src, IntPtr srcrect, int left_width, int right_width, int top_height, int bottom_height, float scale, SDL_ScaleMode scaleMode, IntPtr dst, IntPtr dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_MapSurfaceRGB(IntPtr surface, byte r, byte g, byte b); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_MapSurfaceRGBA(IntPtr surface, byte r, byte g, byte b, byte a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadSurfacePixel(IntPtr surface, int x, int y, out byte r, out byte g, out byte b, out byte a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadSurfacePixelFloat(IntPtr surface, int x, int y, out float r, out float g, out float b, out float a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteSurfacePixel(IntPtr surface, int x, int y, byte r, byte g, byte b, byte a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteSurfacePixelFloat(IntPtr surface, int x, int y, float r, float g, float b, float a); + + // /usr/local/include/SDL3/SDL_camera.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_CameraSpec + { + public SDL_PixelFormat format; + public SDL_Colorspace colorspace; + public int width; + public int height; + public int framerate_numerator; + public int framerate_denominator; + } + + public enum SDL_CameraPosition + { + SDL_CAMERA_POSITION_UNKNOWN = 0, + SDL_CAMERA_POSITION_FRONT_FACING = 1, + SDL_CAMERA_POSITION_BACK_FACING = 2, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumCameraDrivers(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetCameraDriver(int index); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetCurrentCameraDriver(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetCameras(out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetCameraSupportedFormats(uint devid, out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetCameraName(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_CameraPosition SDL_GetCameraPosition(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenCamera(uint instance_id, ref SDL_CameraSpec spec); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetCameraPermissionState(IntPtr camera); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetCameraID(IntPtr camera); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetCameraProperties(IntPtr camera); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetCameraFormat(IntPtr camera, out SDL_CameraSpec spec); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_AcquireCameraFrame(IntPtr camera, out ulong timestampNS); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseCameraFrame(IntPtr camera, IntPtr frame); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CloseCamera(IntPtr camera); + + // /usr/local/include/SDL3/SDL_clipboard.h + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetClipboardText(string text); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(CallerOwnedStringMarshaller))] + public static partial string SDL_GetClipboardText(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasClipboardText(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetPrimarySelectionText(string text); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(CallerOwnedStringMarshaller))] + public static partial string SDL_GetPrimarySelectionText(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasPrimarySelectionText(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr SDL_ClipboardDataCallback(IntPtr userdata, byte* mime_type, IntPtr size); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_ClipboardCleanupCallback(IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, IntPtr userdata, IntPtr mime_types, UIntPtr num_mime_types); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClearClipboardData(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetClipboardData(string mime_type, out UIntPtr size); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasClipboardData(string mime_type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetClipboardMimeTypes(out UIntPtr num_mime_types); + + // /usr/local/include/SDL3/SDL_cpuinfo.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumLogicalCPUCores(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetCPUCacheLineSize(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasAltiVec(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasMMX(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasSSE(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasSSE2(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasSSE3(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasSSE41(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasSSE42(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasAVX(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasAVX2(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasAVX512F(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasARMSIMD(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasNEON(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasLSX(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasLASX(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetSystemRAM(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial UIntPtr SDL_GetSIMDAlignment(); + + // /usr/local/include/SDL3/SDL_video.h + + public const string SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER = "SDL.video.wayland.wl_display"; + public const string SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN = "SDL.display.HDR_enabled"; + public const string SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER = "SDL.display.KMSDRM.panel_orientation"; + public const string SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN = "SDL.window.create.always_on_top"; + public const string SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN = "SDL.window.create.borderless"; + public const string SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN = "SDL.window.create.focusable"; + public const string SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN = "SDL.window.create.external_graphics_context"; + public const string SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER = "SDL.window.create.flags"; + public const string SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN = "SDL.window.create.fullscreen"; + public const string SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER = "SDL.window.create.height"; + public const string SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN = "SDL.window.create.hidden"; + public const string SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN = "SDL.window.create.high_pixel_density"; + public const string SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN = "SDL.window.create.maximized"; + public const string SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN = "SDL.window.create.menu"; + public const string SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN = "SDL.window.create.metal"; + public const string SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN = "SDL.window.create.minimized"; + public const string SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN = "SDL.window.create.modal"; + public const string SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN = "SDL.window.create.mouse_grabbed"; + public const string SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN = "SDL.window.create.opengl"; + public const string SDL_PROP_WINDOW_CREATE_PARENT_POINTER = "SDL.window.create.parent"; + public const string SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN = "SDL.window.create.resizable"; + public const string SDL_PROP_WINDOW_CREATE_TITLE_STRING = "SDL.window.create.title"; + public const string SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN = "SDL.window.create.transparent"; + public const string SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN = "SDL.window.create.tooltip"; + public const string SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN = "SDL.window.create.utility"; + public const string SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN = "SDL.window.create.vulkan"; + public const string SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER = "SDL.window.create.width"; + public const string SDL_PROP_WINDOW_CREATE_X_NUMBER = "SDL.window.create.x"; + public const string SDL_PROP_WINDOW_CREATE_Y_NUMBER = "SDL.window.create.y"; + public const string SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER = "SDL.window.create.cocoa.window"; + public const string SDL_PROP_WINDOW_CREATE_COCOA_VIEW_POINTER = "SDL.window.create.cocoa.view"; + public const string SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN = "SDL.window.create.wayland.surface_role_custom"; + public const string SDL_PROP_WINDOW_CREATE_WAYLAND_CREATE_EGL_WINDOW_BOOLEAN = "SDL.window.create.wayland.create_egl_window"; + public const string SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER = "SDL.window.create.wayland.wl_surface"; + public const string SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER = "SDL.window.create.win32.hwnd"; + public const string SDL_PROP_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER = "SDL.window.create.win32.pixel_format_hwnd"; + public const string SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER = "SDL.window.create.x11.window"; + public const string SDL_PROP_WINDOW_SHAPE_POINTER = "SDL.window.shape"; + public const string SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN = "SDL.window.HDR_enabled"; + public const string SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT = "SDL.window.SDR_white_level"; + public const string SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT = "SDL.window.HDR_headroom"; + public const string SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER = "SDL.window.android.window"; + public const string SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER = "SDL.window.android.surface"; + public const string SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER = "SDL.window.uikit.window"; + public const string SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER = "SDL.window.uikit.metal_view_tag"; + public const string SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER = "SDL.window.uikit.opengl.framebuffer"; + public const string SDL_PROP_WINDOW_UIKIT_OPENGL_RENDERBUFFER_NUMBER = "SDL.window.uikit.opengl.renderbuffer"; + public const string SDL_PROP_WINDOW_UIKIT_OPENGL_RESOLVE_FRAMEBUFFER_NUMBER = "SDL.window.uikit.opengl.resolve_framebuffer"; + public const string SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER = "SDL.window.kmsdrm.dev_index"; + public const string SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER = "SDL.window.kmsdrm.drm_fd"; + public const string SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER = "SDL.window.kmsdrm.gbm_dev"; + public const string SDL_PROP_WINDOW_COCOA_WINDOW_POINTER = "SDL.window.cocoa.window"; + public const string SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER = "SDL.window.cocoa.metal_view_tag"; + public const string SDL_PROP_WINDOW_OPENVR_OVERLAY_ID = "SDL.window.openvr.overlay_id"; + public const string SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER = "SDL.window.vivante.display"; + public const string SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER = "SDL.window.vivante.window"; + public const string SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER = "SDL.window.vivante.surface"; + public const string SDL_PROP_WINDOW_WIN32_HWND_POINTER = "SDL.window.win32.hwnd"; + public const string SDL_PROP_WINDOW_WIN32_HDC_POINTER = "SDL.window.win32.hdc"; + public const string SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER = "SDL.window.win32.instance"; + public const string SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER = "SDL.window.wayland.display"; + public const string SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER = "SDL.window.wayland.surface"; + public const string SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER = "SDL.window.wayland.egl_window"; + public const string SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER = "SDL.window.wayland.xdg_surface"; + public const string SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER = "SDL.window.wayland.xdg_toplevel"; + public const string SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING = "SDL.window.wayland.xdg_toplevel_export_handle"; + public const string SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER = "SDL.window.wayland.xdg_popup"; + public const string SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER = "SDL.window.wayland.xdg_positioner"; + public const string SDL_PROP_WINDOW_X11_DISPLAY_POINTER = "SDL.window.x11.display"; + public const string SDL_PROP_WINDOW_X11_SCREEN_NUMBER = "SDL.window.x11.screen"; + public const string SDL_PROP_WINDOW_X11_WINDOW_NUMBER = "SDL.window.x11.window"; + + public enum SDL_SystemTheme + { + SDL_SYSTEM_THEME_UNKNOWN = 0, + SDL_SYSTEM_THEME_LIGHT = 1, + SDL_SYSTEM_THEME_DARK = 2, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DisplayMode + { + public uint displayID; + public SDL_PixelFormat format; + public int w; + public int h; + public float pixel_density; + public float refresh_rate; + public int refresh_rate_numerator; + public int refresh_rate_denominator; + public IntPtr @internal; + } + + public enum SDL_DisplayOrientation + { + SDL_ORIENTATION_UNKNOWN = 0, + SDL_ORIENTATION_LANDSCAPE = 1, + SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2, + SDL_ORIENTATION_PORTRAIT = 3, + SDL_ORIENTATION_PORTRAIT_FLIPPED = 4, + } + + [Flags] + public enum SDL_WindowFlags : ulong + { + SDL_WINDOW_FULLSCREEN = 0x1, + SDL_WINDOW_OPENGL = 0x2, + SDL_WINDOW_OCCLUDED = 0x4, + SDL_WINDOW_HIDDEN = 0x08, + SDL_WINDOW_BORDERLESS = 0x10, + SDL_WINDOW_RESIZABLE = 0x20, + SDL_WINDOW_MINIMIZED = 0x40, + SDL_WINDOW_MAXIMIZED = 0x080, + SDL_WINDOW_MOUSE_GRABBED = 0x100, + SDL_WINDOW_INPUT_FOCUS = 0x200, + SDL_WINDOW_MOUSE_FOCUS = 0x400, + SDL_WINDOW_EXTERNAL = 0x0800, + SDL_WINDOW_MODAL = 0x1000, + SDL_WINDOW_HIGH_PIXEL_DENSITY = 0x2000, + SDL_WINDOW_MOUSE_CAPTURE = 0x4000, + SDL_WINDOW_MOUSE_RELATIVE_MODE = 0x08000, + SDL_WINDOW_ALWAYS_ON_TOP = 0x10000, + SDL_WINDOW_UTILITY = 0x20000, + SDL_WINDOW_TOOLTIP = 0x40000, + SDL_WINDOW_POPUP_MENU = 0x080000, + SDL_WINDOW_KEYBOARD_GRABBED = 0x100000, + SDL_WINDOW_VULKAN = 0x10000000, + SDL_WINDOW_METAL = 0x20000000, + SDL_WINDOW_TRANSPARENT = 0x40000000, + SDL_WINDOW_NOT_FOCUSABLE = 0x080000000, + } + + public enum SDL_FlashOperation + { + SDL_FLASH_CANCEL = 0, + SDL_FLASH_BRIEFLY = 1, + SDL_FLASH_UNTIL_FOCUSED = 2, + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr SDL_EGLAttribArrayCallback(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr SDL_EGLIntArrayCallback(); + + public enum SDL_GLAttr + { + SDL_GL_RED_SIZE = 0, + SDL_GL_GREEN_SIZE = 1, + SDL_GL_BLUE_SIZE = 2, + SDL_GL_ALPHA_SIZE = 3, + SDL_GL_BUFFER_SIZE = 4, + SDL_GL_DOUBLEBUFFER = 5, + SDL_GL_DEPTH_SIZE = 6, + SDL_GL_STENCIL_SIZE = 7, + SDL_GL_ACCUM_RED_SIZE = 8, + SDL_GL_ACCUM_GREEN_SIZE = 9, + SDL_GL_ACCUM_BLUE_SIZE = 10, + SDL_GL_ACCUM_ALPHA_SIZE = 11, + SDL_GL_STEREO = 12, + SDL_GL_MULTISAMPLEBUFFERS = 13, + SDL_GL_MULTISAMPLESAMPLES = 14, + SDL_GL_ACCELERATED_VISUAL = 15, + SDL_GL_RETAINED_BACKING = 16, + SDL_GL_CONTEXT_MAJOR_VERSION = 17, + SDL_GL_CONTEXT_MINOR_VERSION = 18, + SDL_GL_CONTEXT_FLAGS = 19, + SDL_GL_CONTEXT_PROFILE_MASK = 20, + SDL_GL_SHARE_WITH_CURRENT_CONTEXT = 21, + SDL_GL_FRAMEBUFFER_SRGB_CAPABLE = 22, + SDL_GL_CONTEXT_RELEASE_BEHAVIOR = 23, + SDL_GL_CONTEXT_RESET_NOTIFICATION = 24, + SDL_GL_CONTEXT_NO_ERROR = 25, + SDL_GL_FLOATBUFFERS = 26, + SDL_GL_EGL_PLATFORM = 27, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumVideoDrivers(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetVideoDriver(int index); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetCurrentVideoDriver(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_SystemTheme SDL_GetSystemTheme(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetDisplays(out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetPrimaryDisplay(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetDisplayProperties(uint displayID); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetDisplayName(uint displayID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetDisplayBounds(uint displayID, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetDisplayUsableBounds(uint displayID, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_DisplayOrientation SDL_GetNaturalDisplayOrientation(uint displayID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_DisplayOrientation SDL_GetCurrentDisplayOrientation(uint displayID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetDisplayContentScale(uint displayID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetFullscreenDisplayModes(uint displayID, out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetClosestFullscreenDisplayMode(uint displayID, int w, int h, float refresh_rate, SDLBool include_high_density_modes, out SDL_DisplayMode mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetDesktopDisplayMode(uint displayID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetCurrentDisplayMode(uint displayID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetDisplayForPoint(ref SDL_Point point); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetDisplayForRect(ref SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetDisplayForWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetWindowPixelDensity(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetWindowDisplayScale(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowFullscreenMode(IntPtr window, ref SDL_DisplayMode mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowFullscreenMode(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowICCProfile(IntPtr window, out UIntPtr size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_PixelFormat SDL_GetWindowPixelFormat(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindows(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateWindow(string title, int w, int h, SDL_WindowFlags flags); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreatePopupWindow(IntPtr parent, int offset_x, int offset_y, int w, int h, SDL_WindowFlags flags); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateWindowWithProperties(uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetWindowID(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowFromID(uint id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowParent(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetWindowProperties(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_WindowFlags SDL_GetWindowFlags(IntPtr window); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowTitle(IntPtr window, string title); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetWindowTitle(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowIcon(IntPtr window, IntPtr icon); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowPosition(IntPtr window, int x, int y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowPosition(IntPtr window, out int x, out int y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowSize(IntPtr window, int w, int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowSize(IntPtr window, out int w, out int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowSafeArea(IntPtr window, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowAspectRatio(IntPtr window, float min_aspect, float max_aspect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowAspectRatio(IntPtr window, out float min_aspect, out float max_aspect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowBordersSize(IntPtr window, out int top, out int left, out int bottom, out int right); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowSizeInPixels(IntPtr window, out int w, out int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowMinimumSize(IntPtr window, int min_w, int min_h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowMinimumSize(IntPtr window, out int w, out int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowMaximumSize(IntPtr window, int max_w, int max_h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowMaximumSize(IntPtr window, out int w, out int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowBordered(IntPtr window, SDLBool bordered); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowResizable(IntPtr window, SDLBool resizable); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowAlwaysOnTop(IntPtr window, SDLBool on_top); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShowWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HideWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RaiseWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_MaximizeWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_MinimizeWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RestoreWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowFullscreen(IntPtr window, SDLBool fullscreen); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SyncWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WindowHasSurface(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowSurface(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowSurfaceVSync(IntPtr window, int vsync); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowSurfaceVSync(IntPtr window, out int vsync); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UpdateWindowSurface(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UpdateWindowSurfaceRects(IntPtr window, Span rects, int numrects); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_DestroyWindowSurface(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowKeyboardGrab(IntPtr window, SDLBool grabbed); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowMouseGrab(IntPtr window, SDLBool grabbed); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowKeyboardGrab(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowMouseGrab(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGrabbedWindow(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowMouseRect(IntPtr window, ref SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowMouseRect(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowOpacity(IntPtr window, float opacity); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetWindowOpacity(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowParent(IntPtr window, IntPtr parent); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowModal(IntPtr window, SDLBool modal); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowFocusable(IntPtr window, SDLBool focusable); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShowWindowSystemMenu(IntPtr window, int x, int y); + + public enum SDL_HitTestResult + { + SDL_HITTEST_NORMAL = 0, + SDL_HITTEST_DRAGGABLE = 1, + SDL_HITTEST_RESIZE_TOPLEFT = 2, + SDL_HITTEST_RESIZE_TOP = 3, + SDL_HITTEST_RESIZE_TOPRIGHT = 4, + SDL_HITTEST_RESIZE_RIGHT = 5, + SDL_HITTEST_RESIZE_BOTTOMRIGHT = 6, + SDL_HITTEST_RESIZE_BOTTOM = 7, + SDL_HITTEST_RESIZE_BOTTOMLEFT = 8, + SDL_HITTEST_RESIZE_LEFT = 9, + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate SDL_HitTestResult SDL_HitTest(IntPtr win, SDL_Point* area, IntPtr data); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowHitTest(IntPtr window, SDL_HitTest callback, IntPtr callback_data); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowShape(IntPtr window, IntPtr shape); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FlashWindow(IntPtr window, SDL_FlashOperation operation); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ScreenSaverEnabled(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_EnableScreenSaver(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_DisableScreenSaver(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_LoadLibrary(string path); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static unsafe partial void* SDL_GL_GetProcAddress(byte* proc); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GL_GetProcAddress(string proc); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_EGL_GetProcAddress(string proc); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GL_UnloadLibrary(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_ExtensionSupported(string extension); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GL_ResetAttributes(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_SetAttribute(SDL_GLAttr attr, int value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_GetAttribute(SDL_GLAttr attr, out int value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GL_CreateContext(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_MakeCurrent(IntPtr window, IntPtr context); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GL_GetCurrentWindow(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GL_GetCurrentContext(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_EGL_GetCurrentDisplay(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_EGL_GetCurrentConfig(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_EGL_GetWindowSurface(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_EGL_SetAttributeCallbacks(SDL_EGLAttribArrayCallback platformAttribCallback, SDL_EGLIntArrayCallback surfaceAttribCallback, SDL_EGLIntArrayCallback contextAttribCallback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_SetSwapInterval(int interval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_GetSwapInterval(out int interval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_SwapWindow(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GL_DestroyContext(IntPtr context); + + // /usr/local/include/SDL3/SDL_dialog.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DialogFileFilter + { + public byte* name; + public byte* pattern; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_DialogFileCallback(IntPtr userdata, IntPtr filelist, int filter); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, IntPtr userdata, IntPtr window, Span filters, int nfilters, string default_location, SDLBool allow_many); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, IntPtr userdata, IntPtr window, Span filters, int nfilters, string default_location); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, IntPtr userdata, IntPtr window, string default_location, SDLBool allow_many); + + // /usr/local/include/SDL3/SDL_guid.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GUID + { + public fixed byte data[16]; + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GUIDToString(SDL_GUID guid, string pszGUID, int cbGUID); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GUID SDL_StringToGUID(string pchGUID); + + // /usr/local/include/SDL3/SDL_power.h + + public enum SDL_PowerState + { + SDL_POWERSTATE_ERROR = -1, + SDL_POWERSTATE_UNKNOWN = 0, + SDL_POWERSTATE_ON_BATTERY = 1, + SDL_POWERSTATE_NO_BATTERY = 2, + SDL_POWERSTATE_CHARGING = 3, + SDL_POWERSTATE_CHARGED = 4, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_PowerState SDL_GetPowerInfo(out int seconds, out int percent); + + // /usr/local/include/SDL3/SDL_sensor.h + + public enum SDL_SensorType + { + SDL_SENSOR_INVALID = -1, + SDL_SENSOR_UNKNOWN = 0, + SDL_SENSOR_ACCEL = 1, + SDL_SENSOR_GYRO = 2, + SDL_SENSOR_ACCEL_L = 3, + SDL_SENSOR_GYRO_L = 4, + SDL_SENSOR_ACCEL_R = 5, + SDL_SENSOR_GYRO_R = 6, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetSensors(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetSensorNameForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_SensorType SDL_GetSensorTypeForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetSensorNonPortableTypeForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenSensor(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetSensorFromID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetSensorProperties(IntPtr sensor); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetSensorName(IntPtr sensor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_SensorType SDL_GetSensorType(IntPtr sensor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetSensorNonPortableType(IntPtr sensor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetSensorID(IntPtr sensor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetSensorData(IntPtr sensor, Span data, int num_values); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CloseSensor(IntPtr sensor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UpdateSensors(); + + // /usr/local/include/SDL3/SDL_joystick.h + + public const string SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN = "SDL.joystick.cap.mono_led"; + public const string SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN = "SDL.joystick.cap.rgb_led"; + public const string SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN = "SDL.joystick.cap.player_led"; + public const string SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN = "SDL.joystick.cap.rumble"; + public const string SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN = "SDL.joystick.cap.trigger_rumble"; + + public enum SDL_JoystickType + { + SDL_JOYSTICK_TYPE_UNKNOWN = 0, + SDL_JOYSTICK_TYPE_GAMEPAD = 1, + SDL_JOYSTICK_TYPE_WHEEL = 2, + SDL_JOYSTICK_TYPE_ARCADE_STICK = 3, + SDL_JOYSTICK_TYPE_FLIGHT_STICK = 4, + SDL_JOYSTICK_TYPE_DANCE_PAD = 5, + SDL_JOYSTICK_TYPE_GUITAR = 6, + SDL_JOYSTICK_TYPE_DRUM_KIT = 7, + SDL_JOYSTICK_TYPE_ARCADE_PAD = 8, + SDL_JOYSTICK_TYPE_THROTTLE = 9, + SDL_JOYSTICK_TYPE_COUNT = 10, + } + + public enum SDL_JoystickConnectionState + { + SDL_JOYSTICK_CONNECTION_INVALID = -1, + SDL_JOYSTICK_CONNECTION_UNKNOWN = 0, + SDL_JOYSTICK_CONNECTION_WIRED = 1, + SDL_JOYSTICK_CONNECTION_WIRELESS = 2, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LockJoysticks(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockJoysticks(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasJoystick(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetJoysticks(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetJoystickNameForID(uint instance_id); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetJoystickPathForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetJoystickPlayerIndexForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GUID SDL_GetJoystickGUIDForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickVendorForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickProductForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickProductVersionForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_JoystickType SDL_GetJoystickTypeForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenJoystick(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetJoystickFromID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetJoystickFromPlayerIndex(int player_index); + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_VirtualJoystickTouchpadDesc + { + public ushort nfingers; + public fixed ushort padding[3]; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_VirtualJoystickSensorDesc + { + public SDL_SensorType type; + public float rate; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_VirtualJoystickDesc + { + public uint version; + public ushort type; + public ushort padding; + public ushort vendor_id; + public ushort product_id; + public ushort naxes; + public ushort nbuttons; + public ushort nballs; + public ushort nhats; + public ushort ntouchpads; + public ushort nsensors; + public fixed ushort padding2[2]; + public uint button_mask; + public uint axis_mask; + public byte* name; + public SDL_VirtualJoystickTouchpadDesc* touchpads; + public SDL_VirtualJoystickSensorDesc* sensors; + public IntPtr userdata; + public IntPtr Update; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr SetPlayerIndex; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr Rumble; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr RumbleTriggers; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr SetLED; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr SendEffect; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr SetSensorsEnabled; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr Cleanup; // WARN_ANONYMOUS_FUNCTION_POINTER + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_AttachVirtualJoystick(ref SDL_VirtualJoystickDesc desc); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_DetachVirtualJoystick(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_IsJoystickVirtual(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickVirtualAxis(IntPtr joystick, int axis, short value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickVirtualBall(IntPtr joystick, int ball, short xrel, short yrel); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickVirtualButton(IntPtr joystick, int button, SDLBool down); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickVirtualHat(IntPtr joystick, int hat, byte value); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickVirtualTouchpad(IntPtr joystick, int touchpad, int finger, SDLBool down, float x, float y, float pressure); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SendJoystickVirtualSensorData(IntPtr joystick, SDL_SensorType type, ulong sensor_timestamp, Span data, int num_values); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetJoystickProperties(IntPtr joystick); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetJoystickName(IntPtr joystick); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetJoystickPath(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetJoystickPlayerIndex(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickPlayerIndex(IntPtr joystick, int player_index); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GUID SDL_GetJoystickGUID(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickVendor(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickProduct(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickProductVersion(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetJoystickFirmwareVersion(IntPtr joystick); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetJoystickSerial(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_JoystickType SDL_GetJoystickType(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GetJoystickGUIDInfo(SDL_GUID guid, out ushort vendor, out ushort product, out ushort version, out ushort crc16); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_JoystickConnected(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetJoystickID(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumJoystickAxes(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumJoystickBalls(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumJoystickHats(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumJoystickButtons(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetJoystickEventsEnabled(SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_JoystickEventsEnabled(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UpdateJoysticks(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial short SDL_GetJoystickAxis(IntPtr joystick, int axis); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetJoystickAxisInitialState(IntPtr joystick, int axis, out short state); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetJoystickBall(IntPtr joystick, int ball, out int dx, out int dy); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial byte SDL_GetJoystickHat(IntPtr joystick, int hat); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetJoystickButton(IntPtr joystick, int button); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RumbleJoystick(IntPtr joystick, ushort low_frequency_rumble, ushort high_frequency_rumble, uint duration_ms); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RumbleJoystickTriggers(IntPtr joystick, ushort left_rumble, ushort right_rumble, uint duration_ms); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetJoystickLED(IntPtr joystick, byte red, byte green, byte blue); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SendJoystickEffect(IntPtr joystick, IntPtr data, int size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CloseJoystick(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_JoystickConnectionState SDL_GetJoystickConnectionState(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_PowerState SDL_GetJoystickPowerInfo(IntPtr joystick, out int percent); + + // /usr/local/include/SDL3/SDL_gamepad.h + + public enum SDL_GamepadType + { + SDL_GAMEPAD_TYPE_UNKNOWN = 0, + SDL_GAMEPAD_TYPE_STANDARD = 1, + SDL_GAMEPAD_TYPE_XBOX360 = 2, + SDL_GAMEPAD_TYPE_XBOXONE = 3, + SDL_GAMEPAD_TYPE_PS3 = 4, + SDL_GAMEPAD_TYPE_PS4 = 5, + SDL_GAMEPAD_TYPE_PS5 = 6, + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO = 7, + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT = 8, + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT = 9, + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR = 10, + SDL_GAMEPAD_TYPE_COUNT = 11, + } + + public enum SDL_GamepadButton + { + SDL_GAMEPAD_BUTTON_INVALID = -1, + SDL_GAMEPAD_BUTTON_SOUTH = 0, + SDL_GAMEPAD_BUTTON_EAST = 1, + SDL_GAMEPAD_BUTTON_WEST = 2, + SDL_GAMEPAD_BUTTON_NORTH = 3, + SDL_GAMEPAD_BUTTON_BACK = 4, + SDL_GAMEPAD_BUTTON_GUIDE = 5, + SDL_GAMEPAD_BUTTON_START = 6, + SDL_GAMEPAD_BUTTON_LEFT_STICK = 7, + SDL_GAMEPAD_BUTTON_RIGHT_STICK = 8, + SDL_GAMEPAD_BUTTON_LEFT_SHOULDER = 9, + SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER = 10, + SDL_GAMEPAD_BUTTON_DPAD_UP = 11, + SDL_GAMEPAD_BUTTON_DPAD_DOWN = 12, + SDL_GAMEPAD_BUTTON_DPAD_LEFT = 13, + SDL_GAMEPAD_BUTTON_DPAD_RIGHT = 14, + SDL_GAMEPAD_BUTTON_MISC1 = 15, + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 = 16, + SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 = 17, + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 = 18, + SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 = 19, + SDL_GAMEPAD_BUTTON_TOUCHPAD = 20, + SDL_GAMEPAD_BUTTON_MISC2 = 21, + SDL_GAMEPAD_BUTTON_MISC3 = 22, + SDL_GAMEPAD_BUTTON_MISC4 = 23, + SDL_GAMEPAD_BUTTON_MISC5 = 24, + SDL_GAMEPAD_BUTTON_MISC6 = 25, + SDL_GAMEPAD_BUTTON_COUNT = 26, + } + + public enum SDL_GamepadButtonLabel + { + SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN = 0, + SDL_GAMEPAD_BUTTON_LABEL_A = 1, + SDL_GAMEPAD_BUTTON_LABEL_B = 2, + SDL_GAMEPAD_BUTTON_LABEL_X = 3, + SDL_GAMEPAD_BUTTON_LABEL_Y = 4, + SDL_GAMEPAD_BUTTON_LABEL_CROSS = 5, + SDL_GAMEPAD_BUTTON_LABEL_CIRCLE = 6, + SDL_GAMEPAD_BUTTON_LABEL_SQUARE = 7, + SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE = 8, + } + + public enum SDL_GamepadAxis + { + SDL_GAMEPAD_AXIS_INVALID = -1, + SDL_GAMEPAD_AXIS_LEFTX = 0, + SDL_GAMEPAD_AXIS_LEFTY = 1, + SDL_GAMEPAD_AXIS_RIGHTX = 2, + SDL_GAMEPAD_AXIS_RIGHTY = 3, + SDL_GAMEPAD_AXIS_LEFT_TRIGGER = 4, + SDL_GAMEPAD_AXIS_RIGHT_TRIGGER = 5, + SDL_GAMEPAD_AXIS_COUNT = 6, + } + + public enum SDL_GamepadBindingType + { + SDL_GAMEPAD_BINDTYPE_NONE = 0, + SDL_GAMEPAD_BINDTYPE_BUTTON = 1, + SDL_GAMEPAD_BINDTYPE_AXIS = 2, + SDL_GAMEPAD_BINDTYPE_HAT = 3, + } + + [StructLayout(LayoutKind.Explicit)] + public struct SDL_GamepadBinding + { + [FieldOffset(0)] + public SDL_GamepadBindingType input_type; + [FieldOffset(4)] + public int input_button; + [FieldOffset(4)] + public INTERNAL_SDL_GamepadBinding_input_axis input_axis; + [FieldOffset(4)] + public INTERNAL_SDL_GamepadBinding_input_hat input_hat; + [FieldOffset(16)] + public SDL_GamepadBindingType output_type; + [FieldOffset(20)] + public SDL_GamepadButton output_button; + [FieldOffset(20)] + public INTERNAL_SDL_GamepadBinding_output_axis output_axis; + } + + [StructLayout(LayoutKind.Sequential)] + public struct INTERNAL_SDL_GamepadBinding_input_axis + { + public int axis; + public int axis_min; + public int axis_max; + } + + [StructLayout(LayoutKind.Sequential)] + public struct INTERNAL_SDL_GamepadBinding_input_hat + { + public int hat; + public int hat_mask; + } + + [StructLayout(LayoutKind.Sequential)] + public struct INTERNAL_SDL_GamepadBinding_output_axis + { + public SDL_GamepadAxis axis; + public int axis_min; + public int axis_max; + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_AddGamepadMapping(string mapping); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_AddGamepadMappingsFromIO(IntPtr src, SDLBool closeio); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_AddGamepadMappingsFromFile(string file); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReloadGamepadMappings(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGamepadMappings(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(CallerOwnedStringMarshaller))] + public static partial string SDL_GetGamepadMappingForGUID(SDL_GUID guid); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(CallerOwnedStringMarshaller))] + public static partial string SDL_GetGamepadMapping(IntPtr gamepad); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetGamepadMapping(uint instance_id, string mapping); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasGamepad(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGamepads(out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_IsGamepad(uint instance_id); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadNameForID(uint instance_id); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadPathForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetGamepadPlayerIndexForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GUID SDL_GetGamepadGUIDForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadVendorForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadProductForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadProductVersionForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadType SDL_GetGamepadTypeForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadType SDL_GetRealGamepadTypeForID(uint instance_id); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(CallerOwnedStringMarshaller))] + public static partial string SDL_GetGamepadMappingForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenGamepad(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGamepadFromID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGamepadFromPlayerIndex(int player_index); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetGamepadProperties(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetGamepadID(IntPtr gamepad); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadName(IntPtr gamepad); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadPath(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadType SDL_GetGamepadType(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadType SDL_GetRealGamepadType(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetGamepadPlayerIndex(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetGamepadPlayerIndex(IntPtr gamepad, int player_index); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadVendor(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadProduct(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadProductVersion(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ushort SDL_GetGamepadFirmwareVersion(IntPtr gamepad); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadSerial(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetGamepadSteamHandle(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_JoystickConnectionState SDL_GetGamepadConnectionState(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_PowerState SDL_GetGamepadPowerInfo(IntPtr gamepad, out int percent); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GamepadConnected(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGamepadJoystick(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGamepadEventsEnabled(SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GamepadEventsEnabled(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetGamepadBindings(IntPtr gamepad, out int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UpdateGamepads(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadType SDL_GetGamepadTypeFromString(string str); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadStringForType(SDL_GamepadType type); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadAxis SDL_GetGamepadAxisFromString(string str); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadStringForAxis(SDL_GamepadAxis axis); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GamepadHasAxis(IntPtr gamepad, SDL_GamepadAxis axis); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial short SDL_GetGamepadAxis(IntPtr gamepad, SDL_GamepadAxis axis); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadButton SDL_GetGamepadButtonFromString(string str); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadStringForButton(SDL_GamepadButton button); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GamepadHasButton(IntPtr gamepad, SDL_GamepadButton button); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetGamepadButton(IntPtr gamepad, SDL_GamepadButton button); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForType(SDL_GamepadType type, SDL_GamepadButton button); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GamepadButtonLabel SDL_GetGamepadButtonLabel(IntPtr gamepad, SDL_GamepadButton button); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumGamepadTouchpads(IntPtr gamepad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumGamepadTouchpadFingers(IntPtr gamepad, int touchpad); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetGamepadTouchpadFinger(IntPtr gamepad, int touchpad, int finger, out SDLBool down, out float x, out float y, out float pressure); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GamepadHasSensor(IntPtr gamepad, SDL_SensorType type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetGamepadSensorEnabled(IntPtr gamepad, SDL_SensorType type, SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GamepadSensorEnabled(IntPtr gamepad, SDL_SensorType type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial float SDL_GetGamepadSensorDataRate(IntPtr gamepad, SDL_SensorType type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetGamepadSensorData(IntPtr gamepad, SDL_SensorType type, Span data, int num_values); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RumbleGamepad(IntPtr gamepad, ushort low_frequency_rumble, ushort high_frequency_rumble, uint duration_ms); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RumbleGamepadTriggers(IntPtr gamepad, ushort left_rumble, ushort right_rumble, uint duration_ms); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetGamepadLED(IntPtr gamepad, byte red, byte green, byte blue); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SendGamepadEffect(IntPtr gamepad, IntPtr data, int size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CloseGamepad(IntPtr gamepad); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadAppleSFSymbolsNameForButton(IntPtr gamepad, SDL_GamepadButton button); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGamepadAppleSFSymbolsNameForAxis(IntPtr gamepad, SDL_GamepadAxis axis); + + // /usr/local/include/SDL3/SDL_scancode.h + + public enum SDL_Scancode + { + SDL_SCANCODE_UNKNOWN = 0, + SDL_SCANCODE_A = 4, + SDL_SCANCODE_B = 5, + SDL_SCANCODE_C = 6, + SDL_SCANCODE_D = 7, + SDL_SCANCODE_E = 8, + SDL_SCANCODE_F = 9, + SDL_SCANCODE_G = 10, + SDL_SCANCODE_H = 11, + SDL_SCANCODE_I = 12, + SDL_SCANCODE_J = 13, + SDL_SCANCODE_K = 14, + SDL_SCANCODE_L = 15, + SDL_SCANCODE_M = 16, + SDL_SCANCODE_N = 17, + SDL_SCANCODE_O = 18, + SDL_SCANCODE_P = 19, + SDL_SCANCODE_Q = 20, + SDL_SCANCODE_R = 21, + SDL_SCANCODE_S = 22, + SDL_SCANCODE_T = 23, + SDL_SCANCODE_U = 24, + SDL_SCANCODE_V = 25, + SDL_SCANCODE_W = 26, + SDL_SCANCODE_X = 27, + SDL_SCANCODE_Y = 28, + SDL_SCANCODE_Z = 29, + SDL_SCANCODE_1 = 30, + SDL_SCANCODE_2 = 31, + SDL_SCANCODE_3 = 32, + SDL_SCANCODE_4 = 33, + SDL_SCANCODE_5 = 34, + SDL_SCANCODE_6 = 35, + SDL_SCANCODE_7 = 36, + SDL_SCANCODE_8 = 37, + SDL_SCANCODE_9 = 38, + SDL_SCANCODE_0 = 39, + SDL_SCANCODE_RETURN = 40, + SDL_SCANCODE_ESCAPE = 41, + SDL_SCANCODE_BACKSPACE = 42, + SDL_SCANCODE_TAB = 43, + SDL_SCANCODE_SPACE = 44, + SDL_SCANCODE_MINUS = 45, + SDL_SCANCODE_EQUALS = 46, + SDL_SCANCODE_LEFTBRACKET = 47, + SDL_SCANCODE_RIGHTBRACKET = 48, + SDL_SCANCODE_BACKSLASH = 49, + SDL_SCANCODE_NONUSHASH = 50, + SDL_SCANCODE_SEMICOLON = 51, + SDL_SCANCODE_APOSTROPHE = 52, + SDL_SCANCODE_GRAVE = 53, + SDL_SCANCODE_COMMA = 54, + SDL_SCANCODE_PERIOD = 55, + SDL_SCANCODE_SLASH = 56, + SDL_SCANCODE_CAPSLOCK = 57, + SDL_SCANCODE_F1 = 58, + SDL_SCANCODE_F2 = 59, + SDL_SCANCODE_F3 = 60, + SDL_SCANCODE_F4 = 61, + SDL_SCANCODE_F5 = 62, + SDL_SCANCODE_F6 = 63, + SDL_SCANCODE_F7 = 64, + SDL_SCANCODE_F8 = 65, + SDL_SCANCODE_F9 = 66, + SDL_SCANCODE_F10 = 67, + SDL_SCANCODE_F11 = 68, + SDL_SCANCODE_F12 = 69, + SDL_SCANCODE_PRINTSCREEN = 70, + SDL_SCANCODE_SCROLLLOCK = 71, + SDL_SCANCODE_PAUSE = 72, + SDL_SCANCODE_INSERT = 73, + SDL_SCANCODE_HOME = 74, + SDL_SCANCODE_PAGEUP = 75, + SDL_SCANCODE_DELETE = 76, + SDL_SCANCODE_END = 77, + SDL_SCANCODE_PAGEDOWN = 78, + SDL_SCANCODE_RIGHT = 79, + SDL_SCANCODE_LEFT = 80, + SDL_SCANCODE_DOWN = 81, + SDL_SCANCODE_UP = 82, + SDL_SCANCODE_NUMLOCKCLEAR = 83, + SDL_SCANCODE_KP_DIVIDE = 84, + SDL_SCANCODE_KP_MULTIPLY = 85, + SDL_SCANCODE_KP_MINUS = 86, + SDL_SCANCODE_KP_PLUS = 87, + SDL_SCANCODE_KP_ENTER = 88, + SDL_SCANCODE_KP_1 = 89, + SDL_SCANCODE_KP_2 = 90, + SDL_SCANCODE_KP_3 = 91, + SDL_SCANCODE_KP_4 = 92, + SDL_SCANCODE_KP_5 = 93, + SDL_SCANCODE_KP_6 = 94, + SDL_SCANCODE_KP_7 = 95, + SDL_SCANCODE_KP_8 = 96, + SDL_SCANCODE_KP_9 = 97, + SDL_SCANCODE_KP_0 = 98, + SDL_SCANCODE_KP_PERIOD = 99, + SDL_SCANCODE_NONUSBACKSLASH = 100, + SDL_SCANCODE_APPLICATION = 101, + SDL_SCANCODE_POWER = 102, + SDL_SCANCODE_KP_EQUALS = 103, + SDL_SCANCODE_F13 = 104, + SDL_SCANCODE_F14 = 105, + SDL_SCANCODE_F15 = 106, + SDL_SCANCODE_F16 = 107, + SDL_SCANCODE_F17 = 108, + SDL_SCANCODE_F18 = 109, + SDL_SCANCODE_F19 = 110, + SDL_SCANCODE_F20 = 111, + SDL_SCANCODE_F21 = 112, + SDL_SCANCODE_F22 = 113, + SDL_SCANCODE_F23 = 114, + SDL_SCANCODE_F24 = 115, + SDL_SCANCODE_EXECUTE = 116, + SDL_SCANCODE_HELP = 117, + SDL_SCANCODE_MENU = 118, + SDL_SCANCODE_SELECT = 119, + SDL_SCANCODE_STOP = 120, + SDL_SCANCODE_AGAIN = 121, + SDL_SCANCODE_UNDO = 122, + SDL_SCANCODE_CUT = 123, + SDL_SCANCODE_COPY = 124, + SDL_SCANCODE_PASTE = 125, + SDL_SCANCODE_FIND = 126, + SDL_SCANCODE_MUTE = 127, + SDL_SCANCODE_VOLUMEUP = 128, + SDL_SCANCODE_VOLUMEDOWN = 129, + SDL_SCANCODE_KP_COMMA = 133, + SDL_SCANCODE_KP_EQUALSAS400 = 134, + SDL_SCANCODE_INTERNATIONAL1 = 135, + SDL_SCANCODE_INTERNATIONAL2 = 136, + SDL_SCANCODE_INTERNATIONAL3 = 137, + SDL_SCANCODE_INTERNATIONAL4 = 138, + SDL_SCANCODE_INTERNATIONAL5 = 139, + SDL_SCANCODE_INTERNATIONAL6 = 140, + SDL_SCANCODE_INTERNATIONAL7 = 141, + SDL_SCANCODE_INTERNATIONAL8 = 142, + SDL_SCANCODE_INTERNATIONAL9 = 143, + SDL_SCANCODE_LANG1 = 144, + SDL_SCANCODE_LANG2 = 145, + SDL_SCANCODE_LANG3 = 146, + SDL_SCANCODE_LANG4 = 147, + SDL_SCANCODE_LANG5 = 148, + SDL_SCANCODE_LANG6 = 149, + SDL_SCANCODE_LANG7 = 150, + SDL_SCANCODE_LANG8 = 151, + SDL_SCANCODE_LANG9 = 152, + SDL_SCANCODE_ALTERASE = 153, + SDL_SCANCODE_SYSREQ = 154, + SDL_SCANCODE_CANCEL = 155, + SDL_SCANCODE_CLEAR = 156, + SDL_SCANCODE_PRIOR = 157, + SDL_SCANCODE_RETURN2 = 158, + SDL_SCANCODE_SEPARATOR = 159, + SDL_SCANCODE_OUT = 160, + SDL_SCANCODE_OPER = 161, + SDL_SCANCODE_CLEARAGAIN = 162, + SDL_SCANCODE_CRSEL = 163, + SDL_SCANCODE_EXSEL = 164, + SDL_SCANCODE_KP_00 = 176, + SDL_SCANCODE_KP_000 = 177, + SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + SDL_SCANCODE_DECIMALSEPARATOR = 179, + SDL_SCANCODE_CURRENCYUNIT = 180, + SDL_SCANCODE_CURRENCYSUBUNIT = 181, + SDL_SCANCODE_KP_LEFTPAREN = 182, + SDL_SCANCODE_KP_RIGHTPAREN = 183, + SDL_SCANCODE_KP_LEFTBRACE = 184, + SDL_SCANCODE_KP_RIGHTBRACE = 185, + SDL_SCANCODE_KP_TAB = 186, + SDL_SCANCODE_KP_BACKSPACE = 187, + SDL_SCANCODE_KP_A = 188, + SDL_SCANCODE_KP_B = 189, + SDL_SCANCODE_KP_C = 190, + SDL_SCANCODE_KP_D = 191, + SDL_SCANCODE_KP_E = 192, + SDL_SCANCODE_KP_F = 193, + SDL_SCANCODE_KP_XOR = 194, + SDL_SCANCODE_KP_POWER = 195, + SDL_SCANCODE_KP_PERCENT = 196, + SDL_SCANCODE_KP_LESS = 197, + SDL_SCANCODE_KP_GREATER = 198, + SDL_SCANCODE_KP_AMPERSAND = 199, + SDL_SCANCODE_KP_DBLAMPERSAND = 200, + SDL_SCANCODE_KP_VERTICALBAR = 201, + SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + SDL_SCANCODE_KP_COLON = 203, + SDL_SCANCODE_KP_HASH = 204, + SDL_SCANCODE_KP_SPACE = 205, + SDL_SCANCODE_KP_AT = 206, + SDL_SCANCODE_KP_EXCLAM = 207, + SDL_SCANCODE_KP_MEMSTORE = 208, + SDL_SCANCODE_KP_MEMRECALL = 209, + SDL_SCANCODE_KP_MEMCLEAR = 210, + SDL_SCANCODE_KP_MEMADD = 211, + SDL_SCANCODE_KP_MEMSUBTRACT = 212, + SDL_SCANCODE_KP_MEMMULTIPLY = 213, + SDL_SCANCODE_KP_MEMDIVIDE = 214, + SDL_SCANCODE_KP_PLUSMINUS = 215, + SDL_SCANCODE_KP_CLEAR = 216, + SDL_SCANCODE_KP_CLEARENTRY = 217, + SDL_SCANCODE_KP_BINARY = 218, + SDL_SCANCODE_KP_OCTAL = 219, + SDL_SCANCODE_KP_DECIMAL = 220, + SDL_SCANCODE_KP_HEXADECIMAL = 221, + SDL_SCANCODE_LCTRL = 224, + SDL_SCANCODE_LSHIFT = 225, + SDL_SCANCODE_LALT = 226, + SDL_SCANCODE_LGUI = 227, + SDL_SCANCODE_RCTRL = 228, + SDL_SCANCODE_RSHIFT = 229, + SDL_SCANCODE_RALT = 230, + SDL_SCANCODE_RGUI = 231, + SDL_SCANCODE_MODE = 257, + SDL_SCANCODE_SLEEP = 258, + SDL_SCANCODE_WAKE = 259, + SDL_SCANCODE_CHANNEL_INCREMENT = 260, + SDL_SCANCODE_CHANNEL_DECREMENT = 261, + SDL_SCANCODE_MEDIA_PLAY = 262, + SDL_SCANCODE_MEDIA_PAUSE = 263, + SDL_SCANCODE_MEDIA_RECORD = 264, + SDL_SCANCODE_MEDIA_FAST_FORWARD = 265, + SDL_SCANCODE_MEDIA_REWIND = 266, + SDL_SCANCODE_MEDIA_NEXT_TRACK = 267, + SDL_SCANCODE_MEDIA_PREVIOUS_TRACK = 268, + SDL_SCANCODE_MEDIA_STOP = 269, + SDL_SCANCODE_MEDIA_EJECT = 270, + SDL_SCANCODE_MEDIA_PLAY_PAUSE = 271, + SDL_SCANCODE_MEDIA_SELECT = 272, + SDL_SCANCODE_AC_NEW = 273, + SDL_SCANCODE_AC_OPEN = 274, + SDL_SCANCODE_AC_CLOSE = 275, + SDL_SCANCODE_AC_EXIT = 276, + SDL_SCANCODE_AC_SAVE = 277, + SDL_SCANCODE_AC_PRINT = 278, + SDL_SCANCODE_AC_PROPERTIES = 279, + SDL_SCANCODE_AC_SEARCH = 280, + SDL_SCANCODE_AC_HOME = 281, + SDL_SCANCODE_AC_BACK = 282, + SDL_SCANCODE_AC_FORWARD = 283, + SDL_SCANCODE_AC_STOP = 284, + SDL_SCANCODE_AC_REFRESH = 285, + SDL_SCANCODE_AC_BOOKMARKS = 286, + SDL_SCANCODE_SOFTLEFT = 287, + SDL_SCANCODE_SOFTRIGHT = 288, + SDL_SCANCODE_CALL = 289, + SDL_SCANCODE_ENDCALL = 290, + SDL_SCANCODE_RESERVED = 400, + SDL_SCANCODE_COUNT = 512, + } + + // /usr/local/include/SDL3/SDL_keycode.h + + public enum SDL_Keycode : uint + { + SDLK_SCANCODE_MASK = 0x40000000, + SDLK_UNKNOWN = 0x00000000u, + SDLK_RETURN = 0x0000000du, + SDLK_ESCAPE = 0x0000001bu, + SDLK_BACKSPACE = 0x00000008u, + SDLK_TAB = 0x00000009u, + SDLK_SPACE = 0x00000020u, + SDLK_EXCLAIM = 0x00000021u, + SDLK_DBLAPOSTROPHE = 0x00000022u, + SDLK_HASH = 0x00000023u, + SDLK_DOLLAR = 0x00000024u, + SDLK_PERCENT = 0x00000025u, + SDLK_AMPERSAND = 0x00000026u, + SDLK_APOSTROPHE = 0x00000027u, + SDLK_LEFTPAREN = 0x00000028u, + SDLK_RIGHTPAREN = 0x00000029u, + SDLK_ASTERISK = 0x0000002au, + SDLK_PLUS = 0x0000002bu, + SDLK_COMMA = 0x0000002cu, + SDLK_MINUS = 0x0000002du, + SDLK_PERIOD = 0x0000002eu, + SDLK_SLASH = 0x0000002fu, + SDLK_0 = 0x00000030u, + SDLK_1 = 0x00000031u, + SDLK_2 = 0x00000032u, + SDLK_3 = 0x00000033u, + SDLK_4 = 0x00000034u, + SDLK_5 = 0x00000035u, + SDLK_6 = 0x00000036u, + SDLK_7 = 0x00000037u, + SDLK_8 = 0x00000038u, + SDLK_9 = 0x00000039u, + SDLK_COLON = 0x0000003au, + SDLK_SEMICOLON = 0x0000003bu, + SDLK_LESS = 0x0000003cu, + SDLK_EQUALS = 0x0000003du, + SDLK_GREATER = 0x0000003eu, + SDLK_QUESTION = 0x0000003fu, + SDLK_AT = 0x00000040u, + SDLK_LEFTBRACKET = 0x0000005bu, + SDLK_BACKSLASH = 0x0000005cu, + SDLK_RIGHTBRACKET = 0x0000005du, + SDLK_CARET = 0x0000005eu, + SDLK_UNDERSCORE = 0x0000005fu, + SDLK_GRAVE = 0x00000060u, + SDLK_A = 0x00000061u, + SDLK_B = 0x00000062u, + SDLK_C = 0x00000063u, + SDLK_D = 0x00000064u, + SDLK_E = 0x00000065u, + SDLK_F = 0x00000066u, + SDLK_G = 0x00000067u, + SDLK_H = 0x00000068u, + SDLK_I = 0x00000069u, + SDLK_J = 0x0000006au, + SDLK_K = 0x0000006bu, + SDLK_L = 0x0000006cu, + SDLK_M = 0x0000006du, + SDLK_N = 0x0000006eu, + SDLK_O = 0x0000006fu, + SDLK_P = 0x00000070u, + SDLK_Q = 0x00000071u, + SDLK_R = 0x00000072u, + SDLK_S = 0x00000073u, + SDLK_T = 0x00000074u, + SDLK_U = 0x00000075u, + SDLK_V = 0x00000076u, + SDLK_W = 0x00000077u, + SDLK_X = 0x00000078u, + SDLK_Y = 0x00000079u, + SDLK_Z = 0x0000007au, + SDLK_LEFTBRACE = 0x0000007bu, + SDLK_PIPE = 0x0000007cu, + SDLK_RIGHTBRACE = 0x0000007du, + SDLK_TILDE = 0x0000007eu, + SDLK_DELETE = 0x0000007fu, + SDLK_PLUSMINUS = 0x000000b1u, + SDLK_CAPSLOCK = 0x40000039u, + SDLK_F1 = 0x4000003au, + SDLK_F2 = 0x4000003bu, + SDLK_F3 = 0x4000003cu, + SDLK_F4 = 0x4000003du, + SDLK_F5 = 0x4000003eu, + SDLK_F6 = 0x4000003fu, + SDLK_F7 = 0x40000040u, + SDLK_F8 = 0x40000041u, + SDLK_F9 = 0x40000042u, + SDLK_F10 = 0x40000043u, + SDLK_F11 = 0x40000044u, + SDLK_F12 = 0x40000045u, + SDLK_PRINTSCREEN = 0x40000046u, + SDLK_SCROLLLOCK = 0x40000047u, + SDLK_PAUSE = 0x40000048u, + SDLK_INSERT = 0x40000049u, + SDLK_HOME = 0x4000004au, + SDLK_PAGEUP = 0x4000004bu, + SDLK_END = 0x4000004du, + SDLK_PAGEDOWN = 0x4000004eu, + SDLK_RIGHT = 0x4000004fu, + SDLK_LEFT = 0x40000050u, + SDLK_DOWN = 0x40000051u, + SDLK_UP = 0x40000052u, + SDLK_NUMLOCKCLEAR = 0x40000053u, + SDLK_KP_DIVIDE = 0x40000054u, + SDLK_KP_MULTIPLY = 0x40000055u, + SDLK_KP_MINUS = 0x40000056u, + SDLK_KP_PLUS = 0x40000057u, + SDLK_KP_ENTER = 0x40000058u, + SDLK_KP_1 = 0x40000059u, + SDLK_KP_2 = 0x4000005au, + SDLK_KP_3 = 0x4000005bu, + SDLK_KP_4 = 0x4000005cu, + SDLK_KP_5 = 0x4000005du, + SDLK_KP_6 = 0x4000005eu, + SDLK_KP_7 = 0x4000005fu, + SDLK_KP_8 = 0x40000060u, + SDLK_KP_9 = 0x40000061u, + SDLK_KP_0 = 0x40000062u, + SDLK_KP_PERIOD = 0x40000063u, + SDLK_APPLICATION = 0x40000065u, + SDLK_POWER = 0x40000066u, + SDLK_KP_EQUALS = 0x40000067u, + SDLK_F13 = 0x40000068u, + SDLK_F14 = 0x40000069u, + SDLK_F15 = 0x4000006au, + SDLK_F16 = 0x4000006bu, + SDLK_F17 = 0x4000006cu, + SDLK_F18 = 0x4000006du, + SDLK_F19 = 0x4000006eu, + SDLK_F20 = 0x4000006fu, + SDLK_F21 = 0x40000070u, + SDLK_F22 = 0x40000071u, + SDLK_F23 = 0x40000072u, + SDLK_F24 = 0x40000073u, + SDLK_EXECUTE = 0x40000074u, + SDLK_HELP = 0x40000075u, + SDLK_MENU = 0x40000076u, + SDLK_SELECT = 0x40000077u, + SDLK_STOP = 0x40000078u, + SDLK_AGAIN = 0x40000079u, + SDLK_UNDO = 0x4000007au, + SDLK_CUT = 0x4000007bu, + SDLK_COPY = 0x4000007cu, + SDLK_PASTE = 0x4000007du, + SDLK_FIND = 0x4000007eu, + SDLK_MUTE = 0x4000007fu, + SDLK_VOLUMEUP = 0x40000080u, + SDLK_VOLUMEDOWN = 0x40000081u, + SDLK_KP_COMMA = 0x40000085u, + SDLK_KP_EQUALSAS400 = 0x40000086u, + SDLK_ALTERASE = 0x40000099u, + SDLK_SYSREQ = 0x4000009au, + SDLK_CANCEL = 0x4000009bu, + SDLK_CLEAR = 0x4000009cu, + SDLK_PRIOR = 0x4000009du, + SDLK_RETURN2 = 0x4000009eu, + SDLK_SEPARATOR = 0x4000009fu, + SDLK_OUT = 0x400000a0u, + SDLK_OPER = 0x400000a1u, + SDLK_CLEARAGAIN = 0x400000a2u, + SDLK_CRSEL = 0x400000a3u, + SDLK_EXSEL = 0x400000a4u, + SDLK_KP_00 = 0x400000b0u, + SDLK_KP_000 = 0x400000b1u, + SDLK_THOUSANDSSEPARATOR = 0x400000b2u, + SDLK_DECIMALSEPARATOR = 0x400000b3u, + SDLK_CURRENCYUNIT = 0x400000b4u, + SDLK_CURRENCYSUBUNIT = 0x400000b5u, + SDLK_KP_LEFTPAREN = 0x400000b6u, + SDLK_KP_RIGHTPAREN = 0x400000b7u, + SDLK_KP_LEFTBRACE = 0x400000b8u, + SDLK_KP_RIGHTBRACE = 0x400000b9u, + SDLK_KP_TAB = 0x400000bau, + SDLK_KP_BACKSPACE = 0x400000bbu, + SDLK_KP_A = 0x400000bcu, + SDLK_KP_B = 0x400000bdu, + SDLK_KP_C = 0x400000beu, + SDLK_KP_D = 0x400000bfu, + SDLK_KP_E = 0x400000c0u, + SDLK_KP_F = 0x400000c1u, + SDLK_KP_XOR = 0x400000c2u, + SDLK_KP_POWER = 0x400000c3u, + SDLK_KP_PERCENT = 0x400000c4u, + SDLK_KP_LESS = 0x400000c5u, + SDLK_KP_GREATER = 0x400000c6u, + SDLK_KP_AMPERSAND = 0x400000c7u, + SDLK_KP_DBLAMPERSAND = 0x400000c8u, + SDLK_KP_VERTICALBAR = 0x400000c9u, + SDLK_KP_DBLVERTICALBAR = 0x400000cau, + SDLK_KP_COLON = 0x400000cbu, + SDLK_KP_HASH = 0x400000ccu, + SDLK_KP_SPACE = 0x400000cdu, + SDLK_KP_AT = 0x400000ceu, + SDLK_KP_EXCLAM = 0x400000cfu, + SDLK_KP_MEMSTORE = 0x400000d0u, + SDLK_KP_MEMRECALL = 0x400000d1u, + SDLK_KP_MEMCLEAR = 0x400000d2u, + SDLK_KP_MEMADD = 0x400000d3u, + SDLK_KP_MEMSUBTRACT = 0x400000d4u, + SDLK_KP_MEMMULTIPLY = 0x400000d5u, + SDLK_KP_MEMDIVIDE = 0x400000d6u, + SDLK_KP_PLUSMINUS = 0x400000d7u, + SDLK_KP_CLEAR = 0x400000d8u, + SDLK_KP_CLEARENTRY = 0x400000d9u, + SDLK_KP_BINARY = 0x400000dau, + SDLK_KP_OCTAL = 0x400000dbu, + SDLK_KP_DECIMAL = 0x400000dcu, + SDLK_KP_HEXADECIMAL = 0x400000ddu, + SDLK_LCTRL = 0x400000e0u, + SDLK_LSHIFT = 0x400000e1u, + SDLK_LALT = 0x400000e2u, + SDLK_LGUI = 0x400000e3u, + SDLK_RCTRL = 0x400000e4u, + SDLK_RSHIFT = 0x400000e5u, + SDLK_RALT = 0x400000e6u, + SDLK_RGUI = 0x400000e7u, + SDLK_MODE = 0x40000101u, + SDLK_SLEEP = 0x40000102u, + SDLK_WAKE = 0x40000103u, + SDLK_CHANNEL_INCREMENT = 0x40000104u, + SDLK_CHANNEL_DECREMENT = 0x40000105u, + SDLK_MEDIA_PLAY = 0x40000106u, + SDLK_MEDIA_PAUSE = 0x40000107u, + SDLK_MEDIA_RECORD = 0x40000108u, + SDLK_MEDIA_FAST_FORWARD = 0x40000109u, + SDLK_MEDIA_REWIND = 0x4000010au, + SDLK_MEDIA_NEXT_TRACK = 0x4000010bu, + SDLK_MEDIA_PREVIOUS_TRACK = 0x4000010cu, + SDLK_MEDIA_STOP = 0x4000010du, + SDLK_MEDIA_EJECT = 0x4000010eu, + SDLK_MEDIA_PLAY_PAUSE = 0x4000010fu, + SDLK_MEDIA_SELECT = 0x40000110u, + SDLK_AC_NEW = 0x40000111u, + SDLK_AC_OPEN = 0x40000112u, + SDLK_AC_CLOSE = 0x40000113u, + SDLK_AC_EXIT = 0x40000114u, + SDLK_AC_SAVE = 0x40000115u, + SDLK_AC_PRINT = 0x40000116u, + SDLK_AC_PROPERTIES = 0x40000117u, + SDLK_AC_SEARCH = 0x40000118u, + SDLK_AC_HOME = 0x40000119u, + SDLK_AC_BACK = 0x4000011au, + SDLK_AC_FORWARD = 0x4000011bu, + SDLK_AC_STOP = 0x4000011cu, + SDLK_AC_REFRESH = 0x4000011du, + SDLK_AC_BOOKMARKS = 0x4000011eu, + SDLK_SOFTLEFT = 0x4000011fu, + SDLK_SOFTRIGHT = 0x40000120u, + SDLK_CALL = 0x40000121u, + SDLK_ENDCALL = 0x40000122u, + } + + [Flags] + public enum SDL_Keymod : ushort + { + SDL_KMOD_NONE = 0x0000, + SDL_KMOD_LSHIFT = 0x0001, + SDL_KMOD_RSHIFT = 0x0002, + SDL_KMOD_LCTRL = 0x0040, + SDL_KMOD_RCTRL = 0x0080, + SDL_KMOD_LALT = 0x0100, + SDL_KMOD_RALT = 0x0200, + SDL_KMOD_LGUI = 0x0400, + SDL_KMOD_RGUI = 0x0800, + SDL_KMOD_NUM = 0x1000, + SDL_KMOD_CAPS = 0x2000, + SDL_KMOD_MODE = 0x4000, + SDL_KMOD_SCROLL = 0x8000, + SDL_KMOD_CTRL = SDL_KMOD_LCTRL | SDL_KMOD_RCTRL, + SDL_KMOD_SHIFT = SDL_KMOD_LSHIFT | SDL_KMOD_RSHIFT, + SDL_KMOD_ALT = SDL_KMOD_RALT | SDL_KMOD_LALT, + SDL_KMOD_GUI = SDL_KMOD_RGUI | SDL_KMOD_LGUI, + } + + // /usr/local/include/SDL3/SDL_keyboard.h + + public const string SDL_PROP_TEXTINPUT_TYPE_NUMBER = "SDL.textinput.type"; + public const string SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER = "SDL.textinput.capitalization"; + public const string SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN = "SDL.textinput.autocorrect"; + public const string SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN = "SDL.textinput.multiline"; + public const string SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER = "SDL.textinput.android.inputtype"; + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasKeyboard(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetKeyboards(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetKeyboardNameForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetKeyboardFocus(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetKeyboardState(out int numkeys); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ResetKeyboard(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_Keymod SDL_GetModState(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetModState(SDL_Keymod modstate); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, SDLBool key_event); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_Scancode SDL_GetScancodeFromKey(uint key, IntPtr modstate); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetScancodeName(SDL_Scancode scancode, string name); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetScancodeName(SDL_Scancode scancode); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_Scancode SDL_GetScancodeFromName(string name); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetKeyName(uint key); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetKeyFromName(string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StartTextInput(IntPtr window); + + public enum SDL_TextInputType + { + SDL_TEXTINPUT_TYPE_TEXT = 0, + SDL_TEXTINPUT_TYPE_TEXT_NAME = 1, + SDL_TEXTINPUT_TYPE_TEXT_EMAIL = 2, + SDL_TEXTINPUT_TYPE_TEXT_USERNAME = 3, + SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN = 4, + SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE = 5, + SDL_TEXTINPUT_TYPE_NUMBER = 6, + SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN = 7, + SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE = 8, + } + + public enum SDL_Capitalization + { + SDL_CAPITALIZE_NONE = 0, + SDL_CAPITALIZE_SENTENCES = 1, + SDL_CAPITALIZE_WORDS = 2, + SDL_CAPITALIZE_LETTERS = 3, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StartTextInputWithProperties(IntPtr window, uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TextInputActive(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StopTextInput(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClearComposition(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextInputArea(IntPtr window, ref SDL_Rect rect, int cursor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextInputArea(IntPtr window, out SDL_Rect rect, out int cursor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasScreenKeyboardSupport(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ScreenKeyboardShown(IntPtr window); + + // /usr/local/include/SDL3/SDL_mouse.h + + public enum SDL_SystemCursor + { + SDL_SYSTEM_CURSOR_DEFAULT = 0, + SDL_SYSTEM_CURSOR_TEXT = 1, + SDL_SYSTEM_CURSOR_WAIT = 2, + SDL_SYSTEM_CURSOR_CROSSHAIR = 3, + SDL_SYSTEM_CURSOR_PROGRESS = 4, + SDL_SYSTEM_CURSOR_NWSE_RESIZE = 5, + SDL_SYSTEM_CURSOR_NESW_RESIZE = 6, + SDL_SYSTEM_CURSOR_EW_RESIZE = 7, + SDL_SYSTEM_CURSOR_NS_RESIZE = 8, + SDL_SYSTEM_CURSOR_MOVE = 9, + SDL_SYSTEM_CURSOR_NOT_ALLOWED = 10, + SDL_SYSTEM_CURSOR_POINTER = 11, + SDL_SYSTEM_CURSOR_NW_RESIZE = 12, + SDL_SYSTEM_CURSOR_N_RESIZE = 13, + SDL_SYSTEM_CURSOR_NE_RESIZE = 14, + SDL_SYSTEM_CURSOR_E_RESIZE = 15, + SDL_SYSTEM_CURSOR_SE_RESIZE = 16, + SDL_SYSTEM_CURSOR_S_RESIZE = 17, + SDL_SYSTEM_CURSOR_SW_RESIZE = 18, + SDL_SYSTEM_CURSOR_W_RESIZE = 19, + SDL_SYSTEM_CURSOR_COUNT = 20, + } + + public enum SDL_MouseWheelDirection + { + SDL_MOUSEWHEEL_NORMAL = 0, + SDL_MOUSEWHEEL_FLIPPED = 1, + } + + [Flags] + public enum SDL_MouseButtonFlags : uint + { + SDL_BUTTON_LMASK = 0x1, + SDL_BUTTON_MMASK = 0x2, + SDL_BUTTON_RMASK = 0x4, + SDL_BUTTON_X1MASK = 0x08, + SDL_BUTTON_X2MASK = 0x10, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasMouse(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetMice(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetMouseNameForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetMouseFocus(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_MouseButtonFlags SDL_GetMouseState(out float x, out float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_MouseButtonFlags SDL_GetGlobalMouseState(out float x, out float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_MouseButtonFlags SDL_GetRelativeMouseState(out float x, out float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_WarpMouseInWindow(IntPtr window, float x, float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WarpMouseGlobal(float x, float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetWindowRelativeMouseMode(IntPtr window, SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetWindowRelativeMouseMode(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CaptureMouse(SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateCursor(IntPtr data, IntPtr mask, int w, int h, int hot_x, int hot_y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateColorCursor(IntPtr surface, int hot_x, int hot_y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateSystemCursor(SDL_SystemCursor id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetCursor(IntPtr cursor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetCursor(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetDefaultCursor(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyCursor(IntPtr cursor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShowCursor(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HideCursor(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CursorVisible(); + + // /usr/local/include/SDL3/SDL_pen.h + + [Flags] + public enum SDL_PenInputFlags : uint + { + SDL_PEN_INPUT_DOWN = 0x1, + SDL_PEN_INPUT_BUTTON_1 = 0x2, + SDL_PEN_INPUT_BUTTON_2 = 0x4, + SDL_PEN_INPUT_BUTTON_3 = 0x08, + SDL_PEN_INPUT_BUTTON_4 = 0x10, + SDL_PEN_INPUT_BUTTON_5 = 0x20, + SDL_PEN_INPUT_ERASER_TIP = 0x40000000, + } + + public enum SDL_PenAxis + { + SDL_PEN_AXIS_PRESSURE = 0, + SDL_PEN_AXIS_XTILT = 1, + SDL_PEN_AXIS_YTILT = 2, + SDL_PEN_AXIS_DISTANCE = 3, + SDL_PEN_AXIS_ROTATION = 4, + SDL_PEN_AXIS_SLIDER = 5, + SDL_PEN_AXIS_TANGENTIAL_PRESSURE = 6, + SDL_PEN_AXIS_COUNT = 7, + } + + // /usr/local/include/SDL3/SDL_touch.h + + public enum SDL_TouchDeviceType + { + SDL_TOUCH_DEVICE_INVALID = -1, + SDL_TOUCH_DEVICE_DIRECT = 0, + SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE = 1, + SDL_TOUCH_DEVICE_INDIRECT_RELATIVE = 2, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Finger + { + public ulong id; + public float x; + public float y; + public float pressure; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetTouchDevices(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetTouchDeviceName(ulong touchID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_TouchDeviceType SDL_GetTouchDeviceType(ulong touchID); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetTouchFingers(ulong touchID, out int count); + + // /usr/local/include/SDL3/SDL_events.h + + public enum SDL_EventType + { + SDL_EVENT_FIRST = 0, + SDL_EVENT_QUIT = 256, + SDL_EVENT_TERMINATING = 257, + SDL_EVENT_LOW_MEMORY = 258, + SDL_EVENT_WILL_ENTER_BACKGROUND = 259, + SDL_EVENT_DID_ENTER_BACKGROUND = 260, + SDL_EVENT_WILL_ENTER_FOREGROUND = 261, + SDL_EVENT_DID_ENTER_FOREGROUND = 262, + SDL_EVENT_LOCALE_CHANGED = 263, + SDL_EVENT_SYSTEM_THEME_CHANGED = 264, + SDL_EVENT_DISPLAY_ORIENTATION = 337, + SDL_EVENT_DISPLAY_ADDED = 338, + SDL_EVENT_DISPLAY_REMOVED = 339, + SDL_EVENT_DISPLAY_MOVED = 340, + SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED = 341, + SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED = 342, + SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED = 343, + SDL_EVENT_DISPLAY_FIRST = 337, + SDL_EVENT_DISPLAY_LAST = 343, + SDL_EVENT_WINDOW_SHOWN = 514, + SDL_EVENT_WINDOW_HIDDEN = 515, + SDL_EVENT_WINDOW_EXPOSED = 516, + SDL_EVENT_WINDOW_MOVED = 517, + SDL_EVENT_WINDOW_RESIZED = 518, + SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED = 519, + SDL_EVENT_WINDOW_METAL_VIEW_RESIZED = 520, + SDL_EVENT_WINDOW_MINIMIZED = 521, + SDL_EVENT_WINDOW_MAXIMIZED = 522, + SDL_EVENT_WINDOW_RESTORED = 523, + SDL_EVENT_WINDOW_MOUSE_ENTER = 524, + SDL_EVENT_WINDOW_MOUSE_LEAVE = 525, + SDL_EVENT_WINDOW_FOCUS_GAINED = 526, + SDL_EVENT_WINDOW_FOCUS_LOST = 527, + SDL_EVENT_WINDOW_CLOSE_REQUESTED = 528, + SDL_EVENT_WINDOW_HIT_TEST = 529, + SDL_EVENT_WINDOW_ICCPROF_CHANGED = 530, + SDL_EVENT_WINDOW_DISPLAY_CHANGED = 531, + SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED = 532, + SDL_EVENT_WINDOW_SAFE_AREA_CHANGED = 533, + SDL_EVENT_WINDOW_OCCLUDED = 534, + SDL_EVENT_WINDOW_ENTER_FULLSCREEN = 535, + SDL_EVENT_WINDOW_LEAVE_FULLSCREEN = 536, + SDL_EVENT_WINDOW_DESTROYED = 537, + SDL_EVENT_WINDOW_HDR_STATE_CHANGED = 538, + SDL_EVENT_WINDOW_FIRST = 514, + SDL_EVENT_WINDOW_LAST = 538, + SDL_EVENT_KEY_DOWN = 768, + SDL_EVENT_KEY_UP = 769, + SDL_EVENT_TEXT_EDITING = 770, + SDL_EVENT_TEXT_INPUT = 771, + SDL_EVENT_KEYMAP_CHANGED = 772, + SDL_EVENT_KEYBOARD_ADDED = 773, + SDL_EVENT_KEYBOARD_REMOVED = 774, + SDL_EVENT_TEXT_EDITING_CANDIDATES = 775, + SDL_EVENT_MOUSE_MOTION = 1024, + SDL_EVENT_MOUSE_BUTTON_DOWN = 1025, + SDL_EVENT_MOUSE_BUTTON_UP = 1026, + SDL_EVENT_MOUSE_WHEEL = 1027, + SDL_EVENT_MOUSE_ADDED = 1028, + SDL_EVENT_MOUSE_REMOVED = 1029, + SDL_EVENT_JOYSTICK_AXIS_MOTION = 1536, + SDL_EVENT_JOYSTICK_BALL_MOTION = 1537, + SDL_EVENT_JOYSTICK_HAT_MOTION = 1538, + SDL_EVENT_JOYSTICK_BUTTON_DOWN = 1539, + SDL_EVENT_JOYSTICK_BUTTON_UP = 1540, + SDL_EVENT_JOYSTICK_ADDED = 1541, + SDL_EVENT_JOYSTICK_REMOVED = 1542, + SDL_EVENT_JOYSTICK_BATTERY_UPDATED = 1543, + SDL_EVENT_JOYSTICK_UPDATE_COMPLETE = 1544, + SDL_EVENT_GAMEPAD_AXIS_MOTION = 1616, + SDL_EVENT_GAMEPAD_BUTTON_DOWN = 1617, + SDL_EVENT_GAMEPAD_BUTTON_UP = 1618, + SDL_EVENT_GAMEPAD_ADDED = 1619, + SDL_EVENT_GAMEPAD_REMOVED = 1620, + SDL_EVENT_GAMEPAD_REMAPPED = 1621, + SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN = 1622, + SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION = 1623, + SDL_EVENT_GAMEPAD_TOUCHPAD_UP = 1624, + SDL_EVENT_GAMEPAD_SENSOR_UPDATE = 1625, + SDL_EVENT_GAMEPAD_UPDATE_COMPLETE = 1626, + SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED = 1627, + SDL_EVENT_FINGER_DOWN = 1792, + SDL_EVENT_FINGER_UP = 1793, + SDL_EVENT_FINGER_MOTION = 1794, + SDL_EVENT_CLIPBOARD_UPDATE = 2304, + SDL_EVENT_DROP_FILE = 4096, + SDL_EVENT_DROP_TEXT = 4097, + SDL_EVENT_DROP_BEGIN = 4098, + SDL_EVENT_DROP_COMPLETE = 4099, + SDL_EVENT_DROP_POSITION = 4100, + SDL_EVENT_AUDIO_DEVICE_ADDED = 4352, + SDL_EVENT_AUDIO_DEVICE_REMOVED = 4353, + SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED = 4354, + SDL_EVENT_SENSOR_UPDATE = 4608, + SDL_EVENT_PEN_PROXIMITY_IN = 4864, + SDL_EVENT_PEN_PROXIMITY_OUT = 4865, + SDL_EVENT_PEN_DOWN = 4866, + SDL_EVENT_PEN_UP = 4867, + SDL_EVENT_PEN_BUTTON_DOWN = 4868, + SDL_EVENT_PEN_BUTTON_UP = 4869, + SDL_EVENT_PEN_MOTION = 4870, + SDL_EVENT_PEN_AXIS = 4871, + SDL_EVENT_CAMERA_DEVICE_ADDED = 5120, + SDL_EVENT_CAMERA_DEVICE_REMOVED = 5121, + SDL_EVENT_CAMERA_DEVICE_APPROVED = 5122, + SDL_EVENT_CAMERA_DEVICE_DENIED = 5123, + SDL_EVENT_RENDER_TARGETS_RESET = 8192, + SDL_EVENT_RENDER_DEVICE_RESET = 8193, + SDL_EVENT_RENDER_DEVICE_LOST = 8194, + SDL_EVENT_PRIVATE0 = 16384, + SDL_EVENT_PRIVATE1 = 16385, + SDL_EVENT_PRIVATE2 = 16386, + SDL_EVENT_PRIVATE3 = 16387, + SDL_EVENT_POLL_SENTINEL = 32512, + SDL_EVENT_USER = 32768, + SDL_EVENT_LAST = 65535, + SDL_EVENT_ENUM_PADDING = 2147483647, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_CommonEvent + { + public uint type; + public uint reserved; + public ulong timestamp; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DisplayEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint displayID; + public int data1; + public int data2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_WindowEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public int data1; + public int data2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_KeyboardDeviceEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_KeyboardEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public SDL_Scancode scancode; + public uint key; + public SDL_Keymod mod; + public ushort raw; + public SDLBool down; + public SDLBool repeat; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TextEditingEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public byte* text; + public int start; + public int length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TextEditingCandidatesEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public byte** candidates; + public int num_candidates; + public int selected_candidate; + public SDLBool horizontal; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TextInputEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public byte* text; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseDeviceEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseMotionEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public SDL_MouseButtonFlags state; + public float x; + public float y; + public float xrel; + public float yrel; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseButtonEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public byte button; + public SDLBool down; + public byte clicks; + public byte padding; + public float x; + public float y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MouseWheelEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public float x; + public float y; + public SDL_MouseWheelDirection direction; + public float mouse_x; + public float mouse_y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyAxisEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public byte axis; + public byte padding1; + public byte padding2; + public byte padding3; + public short value; + public ushort padding4; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyBallEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public byte ball; + public byte padding1; + public byte padding2; + public byte padding3; + public short xrel; + public short yrel; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyHatEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public byte hat; + public byte value; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyButtonEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public byte button; + public SDLBool down; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyDeviceEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_JoyBatteryEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public SDL_PowerState state; + public int percent; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GamepadAxisEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public byte axis; + public byte padding1; + public byte padding2; + public byte padding3; + public short value; + public ushort padding4; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GamepadButtonEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public byte button; + public SDLBool down; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GamepadDeviceEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GamepadTouchpadEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public int touchpad; + public int finger; + public float x; + public float y; + public float pressure; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GamepadSensorEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public int sensor; + public fixed float data[3]; + public ulong sensor_timestamp; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_AudioDeviceEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public SDLBool recording; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_CameraDeviceEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_TouchFingerEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public ulong touchID; + public ulong fingerID; + public float x; + public float y; + public float dx; + public float dy; + public float pressure; + public uint windowID; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PenProximityEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PenMotionEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public SDL_PenInputFlags pen_state; + public float x; + public float y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PenTouchEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public SDL_PenInputFlags pen_state; + public float x; + public float y; + public SDLBool eraser; + public SDLBool down; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PenButtonEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public SDL_PenInputFlags pen_state; + public float x; + public float y; + public byte button; + public SDLBool down; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PenAxisEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public uint which; + public SDL_PenInputFlags pen_state; + public float x; + public float y; + public SDL_PenAxis axis; + public float value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DropEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public float x; + public float y; + public byte* source; + public byte* data; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_ClipboardEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public SDLBool owner; + public int n_mime_types; + public byte** mime_types; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_SensorEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + public uint which; + public fixed float data[6]; + public ulong sensor_timestamp; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_QuitEvent + { + public SDL_EventType type; + public uint reserved; + public ulong timestamp; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_UserEvent + { + public uint type; + public uint reserved; + public ulong timestamp; + public uint windowID; + public int code; + public IntPtr data1; + public IntPtr data2; + } + + [StructLayout(LayoutKind.Explicit)] + public struct SDL_Event + { + [FieldOffset(0)] + public uint type; + [FieldOffset(0)] + public SDL_CommonEvent common; + [FieldOffset(0)] + public SDL_DisplayEvent display; + [FieldOffset(0)] + public SDL_WindowEvent window; + [FieldOffset(0)] + public SDL_KeyboardDeviceEvent kdevice; + [FieldOffset(0)] + public SDL_KeyboardEvent key; + [FieldOffset(0)] + public SDL_TextEditingEvent edit; + [FieldOffset(0)] + public SDL_TextEditingCandidatesEvent edit_candidates; + [FieldOffset(0)] + public SDL_TextInputEvent text; + [FieldOffset(0)] + public SDL_MouseDeviceEvent mdevice; + [FieldOffset(0)] + public SDL_MouseMotionEvent motion; + [FieldOffset(0)] + public SDL_MouseButtonEvent button; + [FieldOffset(0)] + public SDL_MouseWheelEvent wheel; + [FieldOffset(0)] + public SDL_JoyDeviceEvent jdevice; + [FieldOffset(0)] + public SDL_JoyAxisEvent jaxis; + [FieldOffset(0)] + public SDL_JoyBallEvent jball; + [FieldOffset(0)] + public SDL_JoyHatEvent jhat; + [FieldOffset(0)] + public SDL_JoyButtonEvent jbutton; + [FieldOffset(0)] + public SDL_JoyBatteryEvent jbattery; + [FieldOffset(0)] + public SDL_GamepadDeviceEvent gdevice; + [FieldOffset(0)] + public SDL_GamepadAxisEvent gaxis; + [FieldOffset(0)] + public SDL_GamepadButtonEvent gbutton; + [FieldOffset(0)] + public SDL_GamepadTouchpadEvent gtouchpad; + [FieldOffset(0)] + public SDL_GamepadSensorEvent gsensor; + [FieldOffset(0)] + public SDL_AudioDeviceEvent adevice; + [FieldOffset(0)] + public SDL_CameraDeviceEvent cdevice; + [FieldOffset(0)] + public SDL_SensorEvent sensor; + [FieldOffset(0)] + public SDL_QuitEvent quit; + [FieldOffset(0)] + public SDL_UserEvent user; + [FieldOffset(0)] + public SDL_TouchFingerEvent tfinger; + [FieldOffset(0)] + public SDL_PenProximityEvent pproximity; + [FieldOffset(0)] + public SDL_PenTouchEvent ptouch; + [FieldOffset(0)] + public SDL_PenMotionEvent pmotion; + [FieldOffset(0)] + public SDL_PenButtonEvent pbutton; + [FieldOffset(0)] + public SDL_PenAxisEvent paxis; + [FieldOffset(0)] + public SDL_DropEvent drop; + [FieldOffset(0)] + public SDL_ClipboardEvent clipboard; + [FieldOffset(0)] + public fixed byte padding[128]; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_PumpEvents(); + + public enum SDL_EventAction + { + SDL_ADDEVENT = 0, + SDL_PEEKEVENT = 1, + SDL_GETEVENT = 2, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_PeepEvents(Span events, int numevents, SDL_EventAction action, uint minType, uint maxType); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasEvent(uint type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HasEvents(uint minType, uint maxType); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_FlushEvent(uint type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_FlushEvents(uint minType, uint maxType); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PollEvent(out SDL_Event @event); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitEvent(out SDL_Event @event); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitEventTimeout(out SDL_Event @event, int timeoutMS); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PushEvent(ref SDL_Event @event); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool SDL_EventFilter(IntPtr userdata, SDL_Event* evt); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetEventFilter(SDL_EventFilter filter, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetEventFilter(out SDL_EventFilter filter, out IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_AddEventWatch(SDL_EventFilter filter, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_RemoveEventWatch(SDL_EventFilter filter, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_FilterEvents(SDL_EventFilter filter, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetEventEnabled(uint type, SDLBool enabled); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_EventEnabled(uint type); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_RegisterEvents(int numevents); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetWindowFromEvent(ref SDL_Event @event); + + // /usr/local/include/SDL3/SDL_filesystem.h + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetBasePath(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(CallerOwnedStringMarshaller))] + public static partial string SDL_GetPrefPath(string org, string app); + + public enum SDL_Folder + { + SDL_FOLDER_HOME = 0, + SDL_FOLDER_DESKTOP = 1, + SDL_FOLDER_DOCUMENTS = 2, + SDL_FOLDER_DOWNLOADS = 3, + SDL_FOLDER_MUSIC = 4, + SDL_FOLDER_PICTURES = 5, + SDL_FOLDER_PUBLICSHARE = 6, + SDL_FOLDER_SAVEDGAMES = 7, + SDL_FOLDER_SCREENSHOTS = 8, + SDL_FOLDER_TEMPLATES = 9, + SDL_FOLDER_VIDEOS = 10, + SDL_FOLDER_COUNT = 11, + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetUserFolder(SDL_Folder folder); + + public enum SDL_PathType + { + SDL_PATHTYPE_NONE = 0, + SDL_PATHTYPE_FILE = 1, + SDL_PATHTYPE_DIRECTORY = 2, + SDL_PATHTYPE_OTHER = 3, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_PathInfo + { + public SDL_PathType type; + public ulong size; + public long create_time; + public long modify_time; + public long access_time; + } + + [Flags] + public enum SDL_GlobFlags : uint + { + SDL_GLOB_CASEINSENSITIVE = 0x1, + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CreateDirectory(string path); + + public enum SDL_EnumerationResult + { + SDL_ENUM_CONTINUE = 0, + SDL_ENUM_SUCCESS = 1, + SDL_ENUM_FAILURE = 2, + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate SDL_EnumerationResult SDL_EnumerateDirectoryCallback(IntPtr userdata, byte* dirname, byte* fname); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_EnumerateDirectory(string path, SDL_EnumerateDirectoryCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RemovePath(string path); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenamePath(string oldpath, string newpath); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CopyFile(string oldpath, string newpath); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetPathInfo(string path, out SDL_PathInfo info); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GlobDirectory(string path, string pattern, SDL_GlobFlags flags, out int count); + + // /usr/local/include/SDL3/SDL_gpu.h + + public const string SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_R_FLOAT = "SDL.gpu.createtexture.d3d12.clear.r"; + public const string SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_G_FLOAT = "SDL.gpu.createtexture.d3d12.clear.g"; + public const string SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_B_FLOAT = "SDL.gpu.createtexture.d3d12.clear.b"; + public const string SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_A_FLOAT = "SDL.gpu.createtexture.d3d12.clear.a"; + public const string SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_DEPTH_FLOAT = "SDL.gpu.createtexture.d3d12.clear.depth"; + public const string SDL_PROP_GPU_CREATETEXTURE_D3D12_CLEAR_STENCIL_UINT8 = "SDL.gpu.createtexture.d3d12.clear.stencil"; + public const string SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN = "SDL.gpu.device.create.debugmode"; + public const string SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN = "SDL.gpu.device.create.preferlowpower"; + public const string SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING = "SDL.gpu.device.create.name"; + public const string SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN = "SDL.gpu.device.create.shaders.private"; + public const string SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN = "SDL.gpu.device.create.shaders.spirv"; + public const string SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN = "SDL.gpu.device.create.shaders.dxbc"; + public const string SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN = "SDL.gpu.device.create.shaders.dxil"; + public const string SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN = "SDL.gpu.device.create.shaders.msl"; + public const string SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN = "SDL.gpu.device.create.shaders.metallib"; + public const string SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING = "SDL.gpu.device.create.d3d12.semantic"; + + public enum SDL_GPUPrimitiveType + { + SDL_GPU_PRIMITIVETYPE_TRIANGLELIST = 0, + SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP = 1, + SDL_GPU_PRIMITIVETYPE_LINELIST = 2, + SDL_GPU_PRIMITIVETYPE_LINESTRIP = 3, + SDL_GPU_PRIMITIVETYPE_POINTLIST = 4, + } + + public enum SDL_GPULoadOp + { + SDL_GPU_LOADOP_LOAD = 0, + SDL_GPU_LOADOP_CLEAR = 1, + SDL_GPU_LOADOP_DONT_CARE = 2, + } + + public enum SDL_GPUStoreOp + { + SDL_GPU_STOREOP_STORE = 0, + SDL_GPU_STOREOP_DONT_CARE = 1, + SDL_GPU_STOREOP_RESOLVE = 2, + SDL_GPU_STOREOP_RESOLVE_AND_STORE = 3, + } + + public enum SDL_GPUIndexElementSize + { + SDL_GPU_INDEXELEMENTSIZE_16BIT = 0, + SDL_GPU_INDEXELEMENTSIZE_32BIT = 1, + } + + public enum SDL_GPUTextureFormat + { + SDL_GPU_TEXTUREFORMAT_INVALID = 0, + SDL_GPU_TEXTUREFORMAT_A8_UNORM = 1, + SDL_GPU_TEXTUREFORMAT_R8_UNORM = 2, + SDL_GPU_TEXTUREFORMAT_R8G8_UNORM = 3, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM = 4, + SDL_GPU_TEXTUREFORMAT_R16_UNORM = 5, + SDL_GPU_TEXTUREFORMAT_R16G16_UNORM = 6, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM = 7, + SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM = 8, + SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM = 9, + SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM = 10, + SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM = 11, + SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM = 12, + SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM = 13, + SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM = 14, + SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM = 15, + SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM = 16, + SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM = 17, + SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM = 18, + SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT = 19, + SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT = 20, + SDL_GPU_TEXTUREFORMAT_R8_SNORM = 21, + SDL_GPU_TEXTUREFORMAT_R8G8_SNORM = 22, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM = 23, + SDL_GPU_TEXTUREFORMAT_R16_SNORM = 24, + SDL_GPU_TEXTUREFORMAT_R16G16_SNORM = 25, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM = 26, + SDL_GPU_TEXTUREFORMAT_R16_FLOAT = 27, + SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT = 28, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT = 29, + SDL_GPU_TEXTUREFORMAT_R32_FLOAT = 30, + SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT = 31, + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT = 32, + SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT = 33, + SDL_GPU_TEXTUREFORMAT_R8_UINT = 34, + SDL_GPU_TEXTUREFORMAT_R8G8_UINT = 35, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT = 36, + SDL_GPU_TEXTUREFORMAT_R16_UINT = 37, + SDL_GPU_TEXTUREFORMAT_R16G16_UINT = 38, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT = 39, + SDL_GPU_TEXTUREFORMAT_R32_UINT = 40, + SDL_GPU_TEXTUREFORMAT_R32G32_UINT = 41, + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT = 42, + SDL_GPU_TEXTUREFORMAT_R8_INT = 43, + SDL_GPU_TEXTUREFORMAT_R8G8_INT = 44, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT = 45, + SDL_GPU_TEXTUREFORMAT_R16_INT = 46, + SDL_GPU_TEXTUREFORMAT_R16G16_INT = 47, + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT = 48, + SDL_GPU_TEXTUREFORMAT_R32_INT = 49, + SDL_GPU_TEXTUREFORMAT_R32G32_INT = 50, + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT = 51, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB = 52, + SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB = 53, + SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB = 54, + SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB = 55, + SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB = 56, + SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB = 57, + SDL_GPU_TEXTUREFORMAT_D16_UNORM = 58, + SDL_GPU_TEXTUREFORMAT_D24_UNORM = 59, + SDL_GPU_TEXTUREFORMAT_D32_FLOAT = 60, + SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT = 61, + SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT = 62, + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM = 63, + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM = 64, + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM = 65, + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM = 66, + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM = 67, + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM = 68, + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM = 69, + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM = 70, + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM = 71, + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM = 72, + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM = 73, + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM = 74, + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM = 75, + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM = 76, + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB = 77, + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB = 78, + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB = 79, + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB = 80, + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB = 81, + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB = 82, + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB = 83, + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB = 84, + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB = 85, + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB = 86, + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB = 87, + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB = 88, + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB = 89, + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB = 90, + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT = 91, + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT = 92, + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT = 93, + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT = 94, + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT = 95, + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT = 96, + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT = 97, + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT = 98, + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT = 99, + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT = 100, + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT = 101, + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT = 102, + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT = 103, + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT = 104, + } + + [Flags] + public enum SDL_GPUTextureUsageFlags : uint + { + SDL_GPU_TEXTUREUSAGE_SAMPLER = 0x1, + SDL_GPU_TEXTUREUSAGE_COLOR_TARGET = 0x2, + SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET = 0x4, + SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ = 0x08, + SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ = 0x10, + SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE = 0x20, + } + + public enum SDL_GPUTextureType + { + SDL_GPU_TEXTURETYPE_2D = 0, + SDL_GPU_TEXTURETYPE_2D_ARRAY = 1, + SDL_GPU_TEXTURETYPE_3D = 2, + SDL_GPU_TEXTURETYPE_CUBE = 3, + SDL_GPU_TEXTURETYPE_CUBE_ARRAY = 4, + } + + public enum SDL_GPUSampleCount + { + SDL_GPU_SAMPLECOUNT_1 = 0, + SDL_GPU_SAMPLECOUNT_2 = 1, + SDL_GPU_SAMPLECOUNT_4 = 2, + SDL_GPU_SAMPLECOUNT_8 = 3, + } + + public enum SDL_GPUCubeMapFace + { + SDL_GPU_CUBEMAPFACE_POSITIVEX = 0, + SDL_GPU_CUBEMAPFACE_NEGATIVEX = 1, + SDL_GPU_CUBEMAPFACE_POSITIVEY = 2, + SDL_GPU_CUBEMAPFACE_NEGATIVEY = 3, + SDL_GPU_CUBEMAPFACE_POSITIVEZ = 4, + SDL_GPU_CUBEMAPFACE_NEGATIVEZ = 5, + } + + [Flags] + public enum SDL_GPUBufferUsageFlags : uint + { + SDL_GPU_BUFFERUSAGE_VERTEX = 0x1, + SDL_GPU_BUFFERUSAGE_INDEX = 0x2, + SDL_GPU_BUFFERUSAGE_INDIRECT = 0x4, + SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ = 0x08, + SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ = 0x10, + SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE = 0x20, + } + + public enum SDL_GPUTransferBufferUsage + { + SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD = 0, + SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD = 1, + } + + public enum SDL_GPUShaderStage + { + SDL_GPU_SHADERSTAGE_VERTEX = 0, + SDL_GPU_SHADERSTAGE_FRAGMENT = 1, + } + + [Flags] + public enum SDL_GPUShaderFormat : uint + { + SDL_GPU_SHADERFORMAT_PRIVATE = 0x1, + SDL_GPU_SHADERFORMAT_SPIRV = 0x2, + SDL_GPU_SHADERFORMAT_DXBC = 0x4, + SDL_GPU_SHADERFORMAT_DXIL = 0x08, + SDL_GPU_SHADERFORMAT_MSL = 0x10, + SDL_GPU_SHADERFORMAT_METALLIB = 0x20, + } + + public enum SDL_GPUVertexElementFormat + { + SDL_GPU_VERTEXELEMENTFORMAT_INVALID = 0, + SDL_GPU_VERTEXELEMENTFORMAT_INT = 1, + SDL_GPU_VERTEXELEMENTFORMAT_INT2 = 2, + SDL_GPU_VERTEXELEMENTFORMAT_INT3 = 3, + SDL_GPU_VERTEXELEMENTFORMAT_INT4 = 4, + SDL_GPU_VERTEXELEMENTFORMAT_UINT = 5, + SDL_GPU_VERTEXELEMENTFORMAT_UINT2 = 6, + SDL_GPU_VERTEXELEMENTFORMAT_UINT3 = 7, + SDL_GPU_VERTEXELEMENTFORMAT_UINT4 = 8, + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT = 9, + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2 = 10, + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3 = 11, + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4 = 12, + SDL_GPU_VERTEXELEMENTFORMAT_BYTE2 = 13, + SDL_GPU_VERTEXELEMENTFORMAT_BYTE4 = 14, + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2 = 15, + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4 = 16, + SDL_GPU_VERTEXELEMENTFORMAT_BYTE2_NORM = 17, + SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM = 18, + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2_NORM = 19, + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM = 20, + SDL_GPU_VERTEXELEMENTFORMAT_SHORT2 = 21, + SDL_GPU_VERTEXELEMENTFORMAT_SHORT4 = 22, + SDL_GPU_VERTEXELEMENTFORMAT_USHORT2 = 23, + SDL_GPU_VERTEXELEMENTFORMAT_USHORT4 = 24, + SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM = 25, + SDL_GPU_VERTEXELEMENTFORMAT_SHORT4_NORM = 26, + SDL_GPU_VERTEXELEMENTFORMAT_USHORT2_NORM = 27, + SDL_GPU_VERTEXELEMENTFORMAT_USHORT4_NORM = 28, + SDL_GPU_VERTEXELEMENTFORMAT_HALF2 = 29, + SDL_GPU_VERTEXELEMENTFORMAT_HALF4 = 30, + } + + public enum SDL_GPUVertexInputRate + { + SDL_GPU_VERTEXINPUTRATE_VERTEX = 0, + SDL_GPU_VERTEXINPUTRATE_INSTANCE = 1, + } + + public enum SDL_GPUFillMode + { + SDL_GPU_FILLMODE_FILL = 0, + SDL_GPU_FILLMODE_LINE = 1, + } + + public enum SDL_GPUCullMode + { + SDL_GPU_CULLMODE_NONE = 0, + SDL_GPU_CULLMODE_FRONT = 1, + SDL_GPU_CULLMODE_BACK = 2, + } + + public enum SDL_GPUFrontFace + { + SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE = 0, + SDL_GPU_FRONTFACE_CLOCKWISE = 1, + } + + public enum SDL_GPUCompareOp + { + SDL_GPU_COMPAREOP_INVALID = 0, + SDL_GPU_COMPAREOP_NEVER = 1, + SDL_GPU_COMPAREOP_LESS = 2, + SDL_GPU_COMPAREOP_EQUAL = 3, + SDL_GPU_COMPAREOP_LESS_OR_EQUAL = 4, + SDL_GPU_COMPAREOP_GREATER = 5, + SDL_GPU_COMPAREOP_NOT_EQUAL = 6, + SDL_GPU_COMPAREOP_GREATER_OR_EQUAL = 7, + SDL_GPU_COMPAREOP_ALWAYS = 8, + } + + public enum SDL_GPUStencilOp + { + SDL_GPU_STENCILOP_INVALID = 0, + SDL_GPU_STENCILOP_KEEP = 1, + SDL_GPU_STENCILOP_ZERO = 2, + SDL_GPU_STENCILOP_REPLACE = 3, + SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP = 4, + SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP = 5, + SDL_GPU_STENCILOP_INVERT = 6, + SDL_GPU_STENCILOP_INCREMENT_AND_WRAP = 7, + SDL_GPU_STENCILOP_DECREMENT_AND_WRAP = 8, + } + + public enum SDL_GPUBlendOp + { + SDL_GPU_BLENDOP_INVALID = 0, + SDL_GPU_BLENDOP_ADD = 1, + SDL_GPU_BLENDOP_SUBTRACT = 2, + SDL_GPU_BLENDOP_REVERSE_SUBTRACT = 3, + SDL_GPU_BLENDOP_MIN = 4, + SDL_GPU_BLENDOP_MAX = 5, + } + + public enum SDL_GPUBlendFactor + { + SDL_GPU_BLENDFACTOR_INVALID = 0, + SDL_GPU_BLENDFACTOR_ZERO = 1, + SDL_GPU_BLENDFACTOR_ONE = 2, + SDL_GPU_BLENDFACTOR_SRC_COLOR = 3, + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR = 4, + SDL_GPU_BLENDFACTOR_DST_COLOR = 5, + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR = 6, + SDL_GPU_BLENDFACTOR_SRC_ALPHA = 7, + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA = 8, + SDL_GPU_BLENDFACTOR_DST_ALPHA = 9, + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA = 10, + SDL_GPU_BLENDFACTOR_CONSTANT_COLOR = 11, + SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR = 12, + SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE = 13, + } + + [Flags] + public enum SDL_GPUColorComponentFlags : byte + { + SDL_GPU_COLORCOMPONENT_R = 0x1, + SDL_GPU_COLORCOMPONENT_G = 0x2, + SDL_GPU_COLORCOMPONENT_B = 0x4, + SDL_GPU_COLORCOMPONENT_A = 0x08, + } + + public enum SDL_GPUFilter + { + SDL_GPU_FILTER_NEAREST = 0, + SDL_GPU_FILTER_LINEAR = 1, + } + + public enum SDL_GPUSamplerMipmapMode + { + SDL_GPU_SAMPLERMIPMAPMODE_NEAREST = 0, + SDL_GPU_SAMPLERMIPMAPMODE_LINEAR = 1, + } + + public enum SDL_GPUSamplerAddressMode + { + SDL_GPU_SAMPLERADDRESSMODE_REPEAT = 0, + SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT = 1, + SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE = 2, + } + + public enum SDL_GPUPresentMode + { + SDL_GPU_PRESENTMODE_VSYNC = 0, + SDL_GPU_PRESENTMODE_IMMEDIATE = 1, + SDL_GPU_PRESENTMODE_MAILBOX = 2, + } + + public enum SDL_GPUSwapchainComposition + { + SDL_GPU_SWAPCHAINCOMPOSITION_SDR = 0, + SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR = 1, + SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR = 2, + SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2048 = 3, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUViewport + { + public float x; + public float y; + public float w; + public float h; + public float min_depth; + public float max_depth; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTextureTransferInfo + { + public IntPtr transfer_buffer; + public uint offset; + public uint pixels_per_row; + public uint rows_per_layer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTransferBufferLocation + { + public IntPtr transfer_buffer; + public uint offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTextureLocation + { + public IntPtr texture; + public uint mip_level; + public uint layer; + public uint x; + public uint y; + public uint z; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTextureRegion + { + public IntPtr texture; + public uint mip_level; + public uint layer; + public uint x; + public uint y; + public uint z; + public uint w; + public uint h; + public uint d; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUBlitRegion + { + public IntPtr texture; + public uint mip_level; + public uint layer_or_depth_plane; + public uint x; + public uint y; + public uint w; + public uint h; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUBufferLocation + { + public IntPtr buffer; + public uint offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUBufferRegion + { + public IntPtr buffer; + public uint offset; + public uint size; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUIndirectDrawCommand + { + public uint num_vertices; + public uint num_instances; + public uint first_vertex; + public uint first_instance; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUIndexedIndirectDrawCommand + { + public uint num_indices; + public uint num_instances; + public uint first_index; + public int vertex_offset; + public uint first_instance; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUIndirectDispatchCommand + { + public uint groupcount_x; + public uint groupcount_y; + public uint groupcount_z; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUSamplerCreateInfo + { + public SDL_GPUFilter min_filter; + public SDL_GPUFilter mag_filter; + public SDL_GPUSamplerMipmapMode mipmap_mode; + public SDL_GPUSamplerAddressMode address_mode_u; + public SDL_GPUSamplerAddressMode address_mode_v; + public SDL_GPUSamplerAddressMode address_mode_w; + public float mip_lod_bias; + public float max_anisotropy; + public SDL_GPUCompareOp compare_op; + public float min_lod; + public float max_lod; + public SDLBool enable_anisotropy; + public SDLBool enable_compare; + public byte padding1; + public byte padding2; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUVertexBufferDescription + { + public uint slot; + public uint pitch; + public SDL_GPUVertexInputRate input_rate; + public uint instance_step_rate; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUVertexAttribute + { + public uint location; + public uint buffer_slot; + public SDL_GPUVertexElementFormat format; + public uint offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUVertexInputState + { + public SDL_GPUVertexBufferDescription* vertex_buffer_descriptions; + public uint num_vertex_buffers; + public SDL_GPUVertexAttribute* vertex_attributes; + public uint num_vertex_attributes; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUStencilOpState + { + public SDL_GPUStencilOp fail_op; + public SDL_GPUStencilOp pass_op; + public SDL_GPUStencilOp depth_fail_op; + public SDL_GPUCompareOp compare_op; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUColorTargetBlendState + { + public SDL_GPUBlendFactor src_color_blendfactor; + public SDL_GPUBlendFactor dst_color_blendfactor; + public SDL_GPUBlendOp color_blend_op; + public SDL_GPUBlendFactor src_alpha_blendfactor; + public SDL_GPUBlendFactor dst_alpha_blendfactor; + public SDL_GPUBlendOp alpha_blend_op; + public SDL_GPUColorComponentFlags color_write_mask; + public SDLBool enable_blend; + public SDLBool enable_color_write_mask; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUShaderCreateInfo + { + public UIntPtr code_size; + public byte* code; + public byte* entrypoint; + public SDL_GPUShaderFormat format; + public SDL_GPUShaderStage stage; + public uint num_samplers; + public uint num_storage_textures; + public uint num_storage_buffers; + public uint num_uniform_buffers; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTextureCreateInfo + { + public SDL_GPUTextureType type; + public SDL_GPUTextureFormat format; + public SDL_GPUTextureUsageFlags usage; + public uint width; + public uint height; + public uint layer_count_or_depth; + public uint num_levels; + public SDL_GPUSampleCount sample_count; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUBufferCreateInfo + { + public SDL_GPUBufferUsageFlags usage; + public uint size; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTransferBufferCreateInfo + { + public SDL_GPUTransferBufferUsage usage; + public uint size; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPURasterizerState + { + public SDL_GPUFillMode fill_mode; + public SDL_GPUCullMode cull_mode; + public SDL_GPUFrontFace front_face; + public float depth_bias_constant_factor; + public float depth_bias_clamp; + public float depth_bias_slope_factor; + public SDLBool enable_depth_bias; + public SDLBool enable_depth_clip; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUMultisampleState + { + public SDL_GPUSampleCount sample_count; + public uint sample_mask; + public SDLBool enable_mask; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUDepthStencilState + { + public SDL_GPUCompareOp compare_op; + public SDL_GPUStencilOpState back_stencil_state; + public SDL_GPUStencilOpState front_stencil_state; + public byte compare_mask; + public byte write_mask; + public SDLBool enable_depth_test; + public SDLBool enable_depth_write; + public SDLBool enable_stencil_test; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUColorTargetDescription + { + public SDL_GPUTextureFormat format; + public SDL_GPUColorTargetBlendState blend_state; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUGraphicsPipelineTargetInfo + { + public SDL_GPUColorTargetDescription* color_target_descriptions; + public uint num_color_targets; + public SDL_GPUTextureFormat depth_stencil_format; + public SDLBool has_depth_stencil_target; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUGraphicsPipelineCreateInfo + { + public IntPtr vertex_shader; + public IntPtr fragment_shader; + public SDL_GPUVertexInputState vertex_input_state; + public SDL_GPUPrimitiveType primitive_type; + public SDL_GPURasterizerState rasterizer_state; + public SDL_GPUMultisampleState multisample_state; + public SDL_GPUDepthStencilState depth_stencil_state; + public SDL_GPUGraphicsPipelineTargetInfo target_info; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUComputePipelineCreateInfo + { + public UIntPtr code_size; + public byte* code; + public byte* entrypoint; + public SDL_GPUShaderFormat format; + public uint num_samplers; + public uint num_readonly_storage_textures; + public uint num_readonly_storage_buffers; + public uint num_readwrite_storage_textures; + public uint num_readwrite_storage_buffers; + public uint num_uniform_buffers; + public uint threadcount_x; + public uint threadcount_y; + public uint threadcount_z; + public uint props; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUColorTargetInfo + { + public IntPtr texture; + public uint mip_level; + public uint layer_or_depth_plane; + public SDL_FColor clear_color; + public SDL_GPULoadOp load_op; + public SDL_GPUStoreOp store_op; + public IntPtr resolve_texture; + public uint resolve_mip_level; + public uint resolve_layer; + public SDLBool cycle; + public SDLBool cycle_resolve_texture; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUDepthStencilTargetInfo + { + public IntPtr texture; + public float clear_depth; + public SDL_GPULoadOp load_op; + public SDL_GPUStoreOp store_op; + public SDL_GPULoadOp stencil_load_op; + public SDL_GPUStoreOp stencil_store_op; + public SDLBool cycle; + public byte clear_stencil; + public byte padding1; + public byte padding2; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUBlitInfo + { + public SDL_GPUBlitRegion source; + public SDL_GPUBlitRegion destination; + public SDL_GPULoadOp load_op; + public SDL_FColor clear_color; + public SDL_FlipMode flip_mode; + public SDL_GPUFilter filter; + public SDLBool cycle; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUBufferBinding + { + public IntPtr buffer; + public uint offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUTextureSamplerBinding + { + public IntPtr texture; + public IntPtr sampler; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUStorageBufferReadWriteBinding + { + public IntPtr buffer; + public SDLBool cycle; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_GPUStorageTextureReadWriteBinding + { + public IntPtr texture; + public uint mip_level; + public uint layer; + public SDLBool cycle; + public byte padding1; + public byte padding2; + public byte padding3; + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GPUSupportsShaderFormats(SDL_GPUShaderFormat format_flags, string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GPUSupportsProperties(uint props); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUDevice(SDL_GPUShaderFormat format_flags, SDLBool debug_mode, string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUDeviceWithProperties(uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyGPUDevice(IntPtr device); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumGPUDrivers(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGPUDriver(int index); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetGPUDeviceDriver(IntPtr device); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GPUShaderFormat SDL_GetGPUShaderFormats(IntPtr device); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUComputePipeline(IntPtr device, in SDL_GPUComputePipelineCreateInfo createinfo); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUGraphicsPipeline(IntPtr device, in SDL_GPUGraphicsPipelineCreateInfo createinfo); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUSampler(IntPtr device, in SDL_GPUSamplerCreateInfo createinfo); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUShader(IntPtr device, in SDL_GPUShaderCreateInfo createinfo); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUTexture(IntPtr device, in SDL_GPUTextureCreateInfo createinfo); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUBuffer(IntPtr device, in SDL_GPUBufferCreateInfo createinfo); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateGPUTransferBuffer(IntPtr device, in SDL_GPUTransferBufferCreateInfo createinfo); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGPUBufferName(IntPtr device, IntPtr buffer, string text); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGPUTextureName(IntPtr device, IntPtr texture, string text); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_InsertGPUDebugLabel(IntPtr command_buffer, string text); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_PushGPUDebugGroup(IntPtr command_buffer, string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_PopGPUDebugGroup(IntPtr command_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUTexture(IntPtr device, IntPtr texture); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUSampler(IntPtr device, IntPtr sampler); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUBuffer(IntPtr device, IntPtr buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUTransferBuffer(IntPtr device, IntPtr transfer_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUComputePipeline(IntPtr device, IntPtr compute_pipeline); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUShader(IntPtr device, IntPtr shader); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUGraphicsPipeline(IntPtr device, IntPtr graphics_pipeline); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_AcquireGPUCommandBuffer(IntPtr device); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_PushGPUVertexUniformData(IntPtr command_buffer, uint slot_index, IntPtr data, uint length); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_PushGPUFragmentUniformData(IntPtr command_buffer, uint slot_index, IntPtr data, uint length); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_PushGPUComputeUniformData(IntPtr command_buffer, uint slot_index, IntPtr data, uint length); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_BeginGPURenderPass(IntPtr command_buffer, Span color_target_infos, uint num_color_targets, in SDL_GPUDepthStencilTargetInfo depth_stencil_target_info); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUGraphicsPipeline(IntPtr render_pass, IntPtr graphics_pipeline); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGPUViewport(IntPtr render_pass, in SDL_GPUViewport viewport); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGPUScissor(IntPtr render_pass, in SDL_Rect scissor); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGPUBlendConstants(IntPtr render_pass, SDL_FColor blend_constants); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetGPUStencilReference(IntPtr render_pass, byte reference); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUVertexBuffers(IntPtr render_pass, uint first_slot, Span bindings, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUIndexBuffer(IntPtr render_pass, in SDL_GPUBufferBinding binding, SDL_GPUIndexElementSize index_element_size); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUVertexSamplers(IntPtr render_pass, uint first_slot, Span texture_sampler_bindings, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUVertexStorageTextures(IntPtr render_pass, uint first_slot, Span storage_textures, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUVertexStorageBuffers(IntPtr render_pass, uint first_slot, Span storage_buffers, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUFragmentSamplers(IntPtr render_pass, uint first_slot, Span texture_sampler_bindings, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUFragmentStorageTextures(IntPtr render_pass, uint first_slot, Span storage_textures, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUFragmentStorageBuffers(IntPtr render_pass, uint first_slot, Span storage_buffers, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DrawGPUIndexedPrimitives(IntPtr render_pass, uint num_indices, uint num_instances, uint first_index, int vertex_offset, uint first_instance); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DrawGPUPrimitives(IntPtr render_pass, uint num_vertices, uint num_instances, uint first_vertex, uint first_instance); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DrawGPUPrimitivesIndirect(IntPtr render_pass, IntPtr buffer, uint offset, uint draw_count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DrawGPUIndexedPrimitivesIndirect(IntPtr render_pass, IntPtr buffer, uint offset, uint draw_count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_EndGPURenderPass(IntPtr render_pass); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_BeginGPUComputePass(IntPtr command_buffer, Span storage_texture_bindings, uint num_storage_texture_bindings, Span storage_buffer_bindings, uint num_storage_buffer_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUComputePipeline(IntPtr compute_pass, IntPtr compute_pipeline); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUComputeSamplers(IntPtr compute_pass, uint first_slot, Span texture_sampler_bindings, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUComputeStorageTextures(IntPtr compute_pass, uint first_slot, Span storage_textures, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BindGPUComputeStorageBuffers(IntPtr compute_pass, uint first_slot, Span storage_buffers, uint num_bindings); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DispatchGPUCompute(IntPtr compute_pass, uint groupcount_x, uint groupcount_y, uint groupcount_z); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DispatchGPUComputeIndirect(IntPtr compute_pass, IntPtr buffer, uint offset); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_EndGPUComputePass(IntPtr compute_pass); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_MapGPUTransferBuffer(IntPtr device, IntPtr transfer_buffer, SDLBool cycle); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnmapGPUTransferBuffer(IntPtr device, IntPtr transfer_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_BeginGPUCopyPass(IntPtr command_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UploadToGPUTexture(IntPtr copy_pass, in SDL_GPUTextureTransferInfo source, in SDL_GPUTextureRegion destination, SDLBool cycle); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UploadToGPUBuffer(IntPtr copy_pass, in SDL_GPUTransferBufferLocation source, in SDL_GPUBufferRegion destination, SDLBool cycle); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CopyGPUTextureToTexture(IntPtr copy_pass, in SDL_GPUTextureLocation source, in SDL_GPUTextureLocation destination, uint w, uint h, uint d, SDLBool cycle); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CopyGPUBufferToBuffer(IntPtr copy_pass, in SDL_GPUBufferLocation source, in SDL_GPUBufferLocation destination, uint size, SDLBool cycle); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DownloadFromGPUTexture(IntPtr copy_pass, in SDL_GPUTextureRegion source, in SDL_GPUTextureTransferInfo destination); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DownloadFromGPUBuffer(IntPtr copy_pass, in SDL_GPUBufferRegion source, in SDL_GPUTransferBufferLocation destination); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_EndGPUCopyPass(IntPtr copy_pass); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GenerateMipmapsForGPUTexture(IntPtr command_buffer, IntPtr texture); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_BlitGPUTexture(IntPtr command_buffer, in SDL_GPUBlitInfo info); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WindowSupportsGPUSwapchainComposition(IntPtr device, IntPtr window, SDL_GPUSwapchainComposition swapchain_composition); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WindowSupportsGPUPresentMode(IntPtr device, IntPtr window, SDL_GPUPresentMode present_mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ClaimWindowForGPUDevice(IntPtr device, IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseWindowFromGPUDevice(IntPtr device, IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetGPUSwapchainParameters(IntPtr device, IntPtr window, SDL_GPUSwapchainComposition swapchain_composition, SDL_GPUPresentMode present_mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(IntPtr device, IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_AcquireGPUSwapchainTexture(IntPtr command_buffer, IntPtr window, out IntPtr swapchain_texture, out uint swapchain_texture_width, out uint swapchain_texture_height); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SubmitGPUCommandBuffer(IntPtr command_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_SubmitGPUCommandBufferAndAcquireFence(IntPtr command_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CancelGPUCommandBuffer(IntPtr command_buffer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitForGPUIdle(IntPtr device); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitForGPUFences(IntPtr device, SDLBool wait_all, Span fences, uint num_fences); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_QueryGPUFence(IntPtr device, IntPtr fence); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ReleaseGPUFence(IntPtr device, IntPtr fence); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GPUTextureFormatTexelBlockSize(SDL_GPUTextureFormat format); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GPUTextureSupportsFormat(IntPtr device, SDL_GPUTextureFormat format, SDL_GPUTextureType type, SDL_GPUTextureUsageFlags usage); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GPUTextureSupportsSampleCount(IntPtr device, SDL_GPUTextureFormat format, SDL_GPUSampleCount sample_count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_CalculateGPUTextureFormatSize(SDL_GPUTextureFormat format, uint width, uint height, uint depth_or_layer_count); + + // /usr/local/include/SDL3/SDL_haptic.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticDirection + { + public byte type; + public fixed int dir[3]; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticConstant + { + public ushort type; + public SDL_HapticDirection direction; + public uint length; + public ushort delay; + public ushort button; + public ushort interval; + public short level; + public ushort attack_length; + public ushort attack_level; + public ushort fade_length; + public ushort fade_level; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticPeriodic + { + public ushort type; + public SDL_HapticDirection direction; + public uint length; + public ushort delay; + public ushort button; + public ushort interval; + public ushort period; + public short magnitude; + public short offset; + public ushort phase; + public ushort attack_length; + public ushort attack_level; + public ushort fade_length; + public ushort fade_level; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticCondition + { + public ushort type; + public SDL_HapticDirection direction; + public uint length; + public ushort delay; + public ushort button; + public ushort interval; + public fixed ushort right_sat[3]; + public fixed ushort left_sat[3]; + public fixed short right_coeff[3]; + public fixed short left_coeff[3]; + public fixed ushort deadband[3]; + public fixed short center[3]; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticRamp + { + public ushort type; + public SDL_HapticDirection direction; + public uint length; + public ushort delay; + public ushort button; + public ushort interval; + public short start; + public short end; + public ushort attack_length; + public ushort attack_level; + public ushort fade_length; + public ushort fade_level; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticLeftRight + { + public ushort type; + public uint length; + public ushort large_magnitude; + public ushort small_magnitude; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_HapticCustom + { + public ushort type; + public SDL_HapticDirection direction; + public uint length; + public ushort delay; + public ushort button; + public ushort interval; + public byte channels; + public ushort period; + public ushort samples; + public ushort* data; + public ushort attack_length; + public ushort attack_level; + public ushort fade_length; + public ushort fade_level; + } + + [StructLayout(LayoutKind.Explicit)] + public struct SDL_HapticEffect + { + [FieldOffset(0)] + public ushort type; + [FieldOffset(0)] + public SDL_HapticConstant constant; + [FieldOffset(0)] + public SDL_HapticPeriodic periodic; + [FieldOffset(0)] + public SDL_HapticCondition condition; + [FieldOffset(0)] + public SDL_HapticRamp ramp; + [FieldOffset(0)] + public SDL_HapticLeftRight leftright; + [FieldOffset(0)] + public SDL_HapticCustom custom; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetHaptics(out int count); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetHapticNameForID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenHaptic(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetHapticFromID(uint instance_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetHapticID(IntPtr haptic); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetHapticName(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_IsMouseHaptic(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenHapticFromMouse(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_IsJoystickHaptic(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenHapticFromJoystick(IntPtr joystick); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_CloseHaptic(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetMaxHapticEffects(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetMaxHapticEffectsPlaying(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetHapticFeatures(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumHapticAxes(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HapticEffectSupported(IntPtr haptic, ref SDL_HapticEffect effect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_CreateHapticEffect(IntPtr haptic, ref SDL_HapticEffect effect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UpdateHapticEffect(IntPtr haptic, int effect, ref SDL_HapticEffect data); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RunHapticEffect(IntPtr haptic, int effect, uint iterations); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StopHapticEffect(IntPtr haptic, int effect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyHapticEffect(IntPtr haptic, int effect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetHapticEffectStatus(IntPtr haptic, int effect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetHapticGain(IntPtr haptic, int gain); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetHapticAutocenter(IntPtr haptic, int autocenter); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PauseHaptic(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ResumeHaptic(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StopHapticEffects(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_HapticRumbleSupported(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_InitHapticRumble(IntPtr haptic); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_PlayHapticRumble(IntPtr haptic, float strength, uint length); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StopHapticRumble(IntPtr haptic); + + // /usr/local/include/SDL3/SDL_hidapi.h + + public enum SDL_hid_bus_type + { + SDL_HID_API_BUS_UNKNOWN = 0, + SDL_HID_API_BUS_USB = 1, + SDL_HID_API_BUS_BLUETOOTH = 2, + SDL_HID_API_BUS_I2C = 3, + SDL_HID_API_BUS_SPI = 4, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_hid_device_info + { + public byte* path; + public ushort vendor_id; + public ushort product_id; + public byte* serial_number; + public ushort release_number; + public byte* manufacturer_string; + public byte* product_string; + public ushort usage_page; + public ushort usage; + public int interface_number; + public int interface_class; + public int interface_subclass; + public int interface_protocol; + public SDL_hid_bus_type bus_type; + public SDL_hid_device_info* next; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_init(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_exit(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_hid_device_change_count(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_hid_enumerate(ushort vendor_id, ushort product_id); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_hid_free_enumeration(IntPtr devs); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_hid_open(ushort vendor_id, ushort product_id, string serial_number); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_hid_open_path(string path); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_write(IntPtr dev, IntPtr data, UIntPtr length); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_read_timeout(IntPtr dev, IntPtr data, UIntPtr length, int milliseconds); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_read(IntPtr dev, IntPtr data, UIntPtr length); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_set_nonblocking(IntPtr dev, int nonblock); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_send_feature_report(IntPtr dev, IntPtr data, UIntPtr length); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_feature_report(IntPtr dev, IntPtr data, UIntPtr length); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_input_report(IntPtr dev, IntPtr data, UIntPtr length); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_close(IntPtr dev); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_manufacturer_string(IntPtr dev, string @string, UIntPtr maxlen); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_product_string(IntPtr dev, string @string, UIntPtr maxlen); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_serial_number_string(IntPtr dev, string @string, UIntPtr maxlen); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_indexed_string(IntPtr dev, int string_index, string @string, UIntPtr maxlen); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_hid_get_device_info(IntPtr dev); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_hid_get_report_descriptor(IntPtr dev, IntPtr buf, UIntPtr buf_size); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_hid_ble_scan(SDLBool active); + + // /usr/local/include/SDL3/SDL_hints.h + + public const string SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED = "SDL_ALLOW_ALT_TAB_WHILE_GRABBED"; + public const string SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY = "SDL_ANDROID_ALLOW_RECREATE_ACTIVITY"; + public const string SDL_HINT_ANDROID_BLOCK_ON_PAUSE = "SDL_ANDROID_BLOCK_ON_PAUSE"; + public const string SDL_HINT_ANDROID_TRAP_BACK_BUTTON = "SDL_ANDROID_TRAP_BACK_BUTTON"; + public const string SDL_HINT_APP_ID = "SDL_APP_ID"; + public const string SDL_HINT_APP_NAME = "SDL_APP_NAME"; + public const string SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS = "SDL_APPLE_TV_CONTROLLER_UI_EVENTS"; + public const string SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION = "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION"; + public const string SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE = "SDL_AUDIO_ALSA_DEFAULT_DEVICE"; + public const string SDL_HINT_AUDIO_CATEGORY = "SDL_AUDIO_CATEGORY"; + public const string SDL_HINT_AUDIO_CHANNELS = "SDL_AUDIO_CHANNELS"; + public const string SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME = "SDL_AUDIO_DEVICE_APP_ICON_NAME"; + public const string SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES = "SDL_AUDIO_DEVICE_SAMPLE_FRAMES"; + public const string SDL_HINT_AUDIO_DEVICE_STREAM_NAME = "SDL_AUDIO_DEVICE_STREAM_NAME"; + public const string SDL_HINT_AUDIO_DEVICE_STREAM_ROLE = "SDL_AUDIO_DEVICE_STREAM_ROLE"; + public const string SDL_HINT_AUDIO_DISK_INPUT_FILE = "SDL_AUDIO_DISK_INPUT_FILE"; + public const string SDL_HINT_AUDIO_DISK_OUTPUT_FILE = "SDL_AUDIO_DISK_OUTPUT_FILE"; + public const string SDL_HINT_AUDIO_DISK_TIMESCALE = "SDL_AUDIO_DISK_TIMESCALE"; + public const string SDL_HINT_AUDIO_DRIVER = "SDL_AUDIO_DRIVER"; + public const string SDL_HINT_AUDIO_DUMMY_TIMESCALE = "SDL_AUDIO_DUMMY_TIMESCALE"; + public const string SDL_HINT_AUDIO_FORMAT = "SDL_AUDIO_FORMAT"; + public const string SDL_HINT_AUDIO_FREQUENCY = "SDL_AUDIO_FREQUENCY"; + public const string SDL_HINT_AUDIO_INCLUDE_MONITORS = "SDL_AUDIO_INCLUDE_MONITORS"; + public const string SDL_HINT_AUTO_UPDATE_JOYSTICKS = "SDL_AUTO_UPDATE_JOYSTICKS"; + public const string SDL_HINT_AUTO_UPDATE_SENSORS = "SDL_AUTO_UPDATE_SENSORS"; + public const string SDL_HINT_BMP_SAVE_LEGACY_FORMAT = "SDL_BMP_SAVE_LEGACY_FORMAT"; + public const string SDL_HINT_CAMERA_DRIVER = "SDL_CAMERA_DRIVER"; + public const string SDL_HINT_CPU_FEATURE_MASK = "SDL_CPU_FEATURE_MASK"; + public const string SDL_HINT_JOYSTICK_DIRECTINPUT = "SDL_JOYSTICK_DIRECTINPUT"; + public const string SDL_HINT_FILE_DIALOG_DRIVER = "SDL_FILE_DIALOG_DRIVER"; + public const string SDL_HINT_DISPLAY_USABLE_BOUNDS = "SDL_DISPLAY_USABLE_BOUNDS"; + public const string SDL_HINT_EMSCRIPTEN_ASYNCIFY = "SDL_EMSCRIPTEN_ASYNCIFY"; + public const string SDL_HINT_EMSCRIPTEN_CANVAS_SELECTOR = "SDL_EMSCRIPTEN_CANVAS_SELECTOR"; + public const string SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT = "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT"; + public const string SDL_HINT_ENABLE_SCREEN_KEYBOARD = "SDL_ENABLE_SCREEN_KEYBOARD"; + public const string SDL_HINT_EVDEV_DEVICES = "SDL_EVDEV_DEVICES"; + public const string SDL_HINT_EVENT_LOGGING = "SDL_EVENT_LOGGING"; + public const string SDL_HINT_FORCE_RAISEWINDOW = "SDL_FORCE_RAISEWINDOW"; + public const string SDL_HINT_FRAMEBUFFER_ACCELERATION = "SDL_FRAMEBUFFER_ACCELERATION"; + public const string SDL_HINT_GAMECONTROLLERCONFIG = "SDL_GAMECONTROLLERCONFIG"; + public const string SDL_HINT_GAMECONTROLLERCONFIG_FILE = "SDL_GAMECONTROLLERCONFIG_FILE"; + public const string SDL_HINT_GAMECONTROLLERTYPE = "SDL_GAMECONTROLLERTYPE"; + public const string SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES = "SDL_GAMECONTROLLER_IGNORE_DEVICES"; + public const string SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT = "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"; + public const string SDL_HINT_GAMECONTROLLER_SENSOR_FUSION = "SDL_GAMECONTROLLER_SENSOR_FUSION"; + public const string SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT = "SDL_GDK_TEXTINPUT_DEFAULT_TEXT"; + public const string SDL_HINT_GDK_TEXTINPUT_DESCRIPTION = "SDL_GDK_TEXTINPUT_DESCRIPTION"; + public const string SDL_HINT_GDK_TEXTINPUT_MAX_LENGTH = "SDL_GDK_TEXTINPUT_MAX_LENGTH"; + public const string SDL_HINT_GDK_TEXTINPUT_SCOPE = "SDL_GDK_TEXTINPUT_SCOPE"; + public const string SDL_HINT_GDK_TEXTINPUT_TITLE = "SDL_GDK_TEXTINPUT_TITLE"; + public const string SDL_HINT_HIDAPI_LIBUSB = "SDL_HIDAPI_LIBUSB"; + public const string SDL_HINT_HIDAPI_LIBUSB_WHITELIST = "SDL_HIDAPI_LIBUSB_WHITELIST"; + public const string SDL_HINT_HIDAPI_UDEV = "SDL_HIDAPI_UDEV"; + public const string SDL_HINT_GPU_DRIVER = "SDL_GPU_DRIVER"; + public const string SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS = "SDL_HIDAPI_ENUMERATE_ONLY_CONTROLLERS"; + public const string SDL_HINT_HIDAPI_IGNORE_DEVICES = "SDL_HIDAPI_IGNORE_DEVICES"; + public const string SDL_HINT_IME_IMPLEMENTED_UI = "SDL_IME_IMPLEMENTED_UI"; + public const string SDL_HINT_IOS_HIDE_HOME_INDICATOR = "SDL_IOS_HIDE_HOME_INDICATOR"; + public const string SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS = "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"; + public const string SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES = "SDL_JOYSTICK_ARCADESTICK_DEVICES"; + public const string SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED = "SDL_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED"; + public const string SDL_HINT_JOYSTICK_BLACKLIST_DEVICES = "SDL_JOYSTICK_BLACKLIST_DEVICES"; + public const string SDL_HINT_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED = "SDL_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED"; + public const string SDL_HINT_JOYSTICK_DEVICE = "SDL_JOYSTICK_DEVICE"; + public const string SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES = "SDL_JOYSTICK_FLIGHTSTICK_DEVICES"; + public const string SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED = "SDL_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED"; + public const string SDL_HINT_JOYSTICK_GAMEINPUT = "SDL_JOYSTICK_GAMEINPUT"; + public const string SDL_HINT_JOYSTICK_GAMECUBE_DEVICES = "SDL_JOYSTICK_GAMECUBE_DEVICES"; + public const string SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED = "SDL_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED"; + public const string SDL_HINT_JOYSTICK_HIDAPI = "SDL_JOYSTICK_HIDAPI"; + public const string SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"; + public const string SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE = "SDL_JOYSTICK_HIDAPI_GAMECUBE"; + public const string SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE = "SDL_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE"; + public const string SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS = "SDL_JOYSTICK_HIDAPI_JOY_CONS"; + public const string SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED = "SDL_JOYSTICK_HIDAPI_JOYCON_HOME_LED"; + public const string SDL_HINT_JOYSTICK_HIDAPI_LUNA = "SDL_JOYSTICK_HIDAPI_LUNA"; + public const string SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC = "SDL_JOYSTICK_HIDAPI_NINTENDO_CLASSIC"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS3 = "SDL_JOYSTICK_HIDAPI_PS3"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER = "SDL_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS4 = "SDL_JOYSTICK_HIDAPI_PS4"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL = "SDL_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE = "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS5 = "SDL_JOYSTICK_HIDAPI_PS5"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"; + public const string SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE = "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"; + public const string SDL_HINT_JOYSTICK_HIDAPI_SHIELD = "SDL_JOYSTICK_HIDAPI_SHIELD"; + public const string SDL_HINT_JOYSTICK_HIDAPI_STADIA = "SDL_JOYSTICK_HIDAPI_STADIA"; + public const string SDL_HINT_JOYSTICK_HIDAPI_STEAM = "SDL_JOYSTICK_HIDAPI_STEAM"; + public const string SDL_HINT_JOYSTICK_HIDAPI_STEAMDECK = "SDL_JOYSTICK_HIDAPI_STEAMDECK"; + public const string SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI = "SDL_JOYSTICK_HIDAPI_STEAM_HORI"; + public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH = "SDL_JOYSTICK_HIDAPI_SWITCH"; + public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED = "SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED"; + public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED"; + public const string SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS = "SDL_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS"; + public const string SDL_HINT_JOYSTICK_HIDAPI_WII = "SDL_JOYSTICK_HIDAPI_WII"; + public const string SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_WII_PLAYER_LED"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX = "SDL_JOYSTICK_HIDAPI_XBOX"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX_360 = "SDL_JOYSTICK_HIDAPI_XBOX_360"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS = "SDL_JOYSTICK_HIDAPI_XBOX_360_WIRELESS"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE = "SDL_JOYSTICK_HIDAPI_XBOX_ONE"; + public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED = "SDL_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED"; + public const string SDL_HINT_JOYSTICK_IOKIT = "SDL_JOYSTICK_IOKIT"; + public const string SDL_HINT_JOYSTICK_LINUX_CLASSIC = "SDL_JOYSTICK_LINUX_CLASSIC"; + public const string SDL_HINT_JOYSTICK_LINUX_DEADZONES = "SDL_JOYSTICK_LINUX_DEADZONES"; + public const string SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS = "SDL_JOYSTICK_LINUX_DIGITAL_HATS"; + public const string SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES = "SDL_JOYSTICK_LINUX_HAT_DEADZONES"; + public const string SDL_HINT_JOYSTICK_MFI = "SDL_JOYSTICK_MFI"; + public const string SDL_HINT_JOYSTICK_RAWINPUT = "SDL_JOYSTICK_RAWINPUT"; + public const string SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT = "SDL_JOYSTICK_RAWINPUT_CORRELATE_XINPUT"; + public const string SDL_HINT_JOYSTICK_ROG_CHAKRAM = "SDL_JOYSTICK_ROG_CHAKRAM"; + public const string SDL_HINT_JOYSTICK_THREAD = "SDL_JOYSTICK_THREAD"; + public const string SDL_HINT_JOYSTICK_THROTTLE_DEVICES = "SDL_JOYSTICK_THROTTLE_DEVICES"; + public const string SDL_HINT_JOYSTICK_THROTTLE_DEVICES_EXCLUDED = "SDL_JOYSTICK_THROTTLE_DEVICES_EXCLUDED"; + public const string SDL_HINT_JOYSTICK_WGI = "SDL_JOYSTICK_WGI"; + public const string SDL_HINT_JOYSTICK_WHEEL_DEVICES = "SDL_JOYSTICK_WHEEL_DEVICES"; + public const string SDL_HINT_JOYSTICK_WHEEL_DEVICES_EXCLUDED = "SDL_JOYSTICK_WHEEL_DEVICES_EXCLUDED"; + public const string SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES = "SDL_JOYSTICK_ZERO_CENTERED_DEVICES"; + public const string SDL_HINT_KEYCODE_OPTIONS = "SDL_KEYCODE_OPTIONS"; + public const string SDL_HINT_KMSDRM_DEVICE_INDEX = "SDL_KMSDRM_DEVICE_INDEX"; + public const string SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER = "SDL_KMSDRM_REQUIRE_DRM_MASTER"; + public const string SDL_HINT_LOGGING = "SDL_LOGGING"; + public const string SDL_HINT_MAC_BACKGROUND_APP = "SDL_MAC_BACKGROUND_APP"; + public const string SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK = "SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK"; + public const string SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH = "SDL_MAC_OPENGL_ASYNC_DISPATCH"; + public const string SDL_HINT_MAC_SCROLL_MOMENTUM = "SDL_MAC_SCROLL_MOMENTUM"; + public const string SDL_HINT_MAIN_CALLBACK_RATE = "SDL_MAIN_CALLBACK_RATE"; + public const string SDL_HINT_MOUSE_AUTO_CAPTURE = "SDL_MOUSE_AUTO_CAPTURE"; + public const string SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS = "SDL_MOUSE_DOUBLE_CLICK_RADIUS"; + public const string SDL_HINT_MOUSE_DOUBLE_CLICK_TIME = "SDL_MOUSE_DOUBLE_CLICK_TIME"; + public const string SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE = "SDL_MOUSE_EMULATE_WARP_WITH_RELATIVE"; + public const string SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH = "SDL_MOUSE_FOCUS_CLICKTHROUGH"; + public const string SDL_HINT_MOUSE_NORMAL_SPEED_SCALE = "SDL_MOUSE_NORMAL_SPEED_SCALE"; + public const string SDL_HINT_MOUSE_RELATIVE_MODE_CENTER = "SDL_MOUSE_RELATIVE_MODE_CENTER"; + public const string SDL_HINT_MOUSE_RELATIVE_MODE_WARP = "SDL_MOUSE_RELATIVE_MODE_WARP"; + public const string SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE = "SDL_MOUSE_RELATIVE_SPEED_SCALE"; + public const string SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE = "SDL_MOUSE_RELATIVE_SYSTEM_SCALE"; + public const string SDL_HINT_MOUSE_RELATIVE_WARP_MOTION = "SDL_MOUSE_RELATIVE_WARP_MOTION"; + public const string SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE = "SDL_MOUSE_RELATIVE_CURSOR_VISIBLE"; + public const string SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL = "SDL_MOUSE_RELATIVE_CLIP_INTERVAL"; + public const string SDL_HINT_MOUSE_TOUCH_EVENTS = "SDL_MOUSE_TOUCH_EVENTS"; + public const string SDL_HINT_MUTE_CONSOLE_KEYBOARD = "SDL_MUTE_CONSOLE_KEYBOARD"; + public const string SDL_HINT_NO_SIGNAL_HANDLERS = "SDL_NO_SIGNAL_HANDLERS"; + public const string SDL_HINT_OPENGL_LIBRARY = "SDL_OPENGL_LIBRARY"; + public const string SDL_HINT_OPENGL_ES_DRIVER = "SDL_OPENGL_ES_DRIVER"; + public const string SDL_HINT_OPENVR_LIBRARY = "SDL_OPENVR_LIBRARY"; + public const string SDL_HINT_ORIENTATIONS = "SDL_ORIENTATIONS"; + public const string SDL_HINT_POLL_SENTINEL = "SDL_POLL_SENTINEL"; + public const string SDL_HINT_PREFERRED_LOCALES = "SDL_PREFERRED_LOCALES"; + public const string SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE = "SDL_QUIT_ON_LAST_WINDOW_CLOSE"; + public const string SDL_HINT_RENDER_DIRECT3D_THREADSAFE = "SDL_RENDER_DIRECT3D_THREADSAFE"; + public const string SDL_HINT_RENDER_DIRECT3D11_DEBUG = "SDL_RENDER_DIRECT3D11_DEBUG"; + public const string SDL_HINT_RENDER_VULKAN_DEBUG = "SDL_RENDER_VULKAN_DEBUG"; + public const string SDL_HINT_RENDER_GPU_DEBUG = "SDL_RENDER_GPU_DEBUG"; + public const string SDL_HINT_RENDER_GPU_LOW_POWER = "SDL_RENDER_GPU_LOW_POWER"; + public const string SDL_HINT_RENDER_DRIVER = "SDL_RENDER_DRIVER"; + public const string SDL_HINT_RENDER_LINE_METHOD = "SDL_RENDER_LINE_METHOD"; + public const string SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE = "SDL_RENDER_METAL_PREFER_LOW_POWER_DEVICE"; + public const string SDL_HINT_RENDER_VSYNC = "SDL_RENDER_VSYNC"; + public const string SDL_HINT_RETURN_KEY_HIDES_IME = "SDL_RETURN_KEY_HIDES_IME"; + public const string SDL_HINT_ROG_GAMEPAD_MICE = "SDL_ROG_GAMEPAD_MICE"; + public const string SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED = "SDL_ROG_GAMEPAD_MICE_EXCLUDED"; + public const string SDL_HINT_RPI_VIDEO_LAYER = "SDL_RPI_VIDEO_LAYER"; + public const string SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME = "SDL_SCREENSAVER_INHIBIT_ACTIVITY_NAME"; + public const string SDL_HINT_SHUTDOWN_DBUS_ON_QUIT = "SDL_SHUTDOWN_DBUS_ON_QUIT"; + public const string SDL_HINT_STORAGE_TITLE_DRIVER = "SDL_STORAGE_TITLE_DRIVER"; + public const string SDL_HINT_STORAGE_USER_DRIVER = "SDL_STORAGE_USER_DRIVER"; + public const string SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL = "SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL"; + public const string SDL_HINT_THREAD_PRIORITY_POLICY = "SDL_THREAD_PRIORITY_POLICY"; + public const string SDL_HINT_TIMER_RESOLUTION = "SDL_TIMER_RESOLUTION"; + public const string SDL_HINT_TOUCH_MOUSE_EVENTS = "SDL_TOUCH_MOUSE_EVENTS"; + public const string SDL_HINT_TRACKPAD_IS_TOUCH_ONLY = "SDL_TRACKPAD_IS_TOUCH_ONLY"; + public const string SDL_HINT_TV_REMOTE_AS_JOYSTICK = "SDL_TV_REMOTE_AS_JOYSTICK"; + public const string SDL_HINT_VIDEO_ALLOW_SCREENSAVER = "SDL_VIDEO_ALLOW_SCREENSAVER"; + public const string SDL_HINT_VIDEO_DISPLAY_PRIORITY = "SDL_VIDEO_DISPLAY_PRIORITY"; + public const string SDL_HINT_VIDEO_DOUBLE_BUFFER = "SDL_VIDEO_DOUBLE_BUFFER"; + public const string SDL_HINT_VIDEO_DRIVER = "SDL_VIDEO_DRIVER"; + public const string SDL_HINT_VIDEO_DUMMY_SAVE_FRAMES = "SDL_VIDEO_DUMMY_SAVE_FRAMES"; + public const string SDL_HINT_VIDEO_EGL_ALLOW_GETDISPLAY_FALLBACK = "SDL_VIDEO_EGL_ALLOW_GETDISPLAY_FALLBACK"; + public const string SDL_HINT_VIDEO_FORCE_EGL = "SDL_VIDEO_FORCE_EGL"; + public const string SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES = "SDL_VIDEO_MAC_FULLSCREEN_SPACES"; + public const string SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS"; + public const string SDL_HINT_VIDEO_OFFSCREEN_SAVE_FRAMES = "SDL_VIDEO_OFFSCREEN_SAVE_FRAMES"; + public const string SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS = "SDL_VIDEO_SYNC_WINDOW_OPERATIONS"; + public const string SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR = "SDL_VIDEO_WAYLAND_ALLOW_LIBDECOR"; + public const string SDL_HINT_VIDEO_WAYLAND_MODE_EMULATION = "SDL_VIDEO_WAYLAND_MODE_EMULATION"; + public const string SDL_HINT_VIDEO_WAYLAND_MODE_SCALING = "SDL_VIDEO_WAYLAND_MODE_SCALING"; + public const string SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR = "SDL_VIDEO_WAYLAND_PREFER_LIBDECOR"; + public const string SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY = "SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY"; + public const string SDL_HINT_VIDEO_WIN_D3DCOMPILER = "SDL_VIDEO_WIN_D3DCOMPILER"; + public const string SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR = "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"; + public const string SDL_HINT_VIDEO_X11_NET_WM_PING = "SDL_VIDEO_X11_NET_WM_PING"; + public const string SDL_HINT_VIDEO_X11_NODIRECTCOLOR = "SDL_VIDEO_X11_NODIRECTCOLOR"; + public const string SDL_HINT_VIDEO_X11_SCALING_FACTOR = "SDL_VIDEO_X11_SCALING_FACTOR"; + public const string SDL_HINT_VIDEO_X11_VISUALID = "SDL_VIDEO_X11_VISUALID"; + public const string SDL_HINT_VIDEO_X11_WINDOW_VISUALID = "SDL_VIDEO_X11_WINDOW_VISUALID"; + public const string SDL_HINT_VIDEO_X11_XRANDR = "SDL_VIDEO_X11_XRANDR"; + public const string SDL_HINT_VITA_ENABLE_BACK_TOUCH = "SDL_VITA_ENABLE_BACK_TOUCH"; + public const string SDL_HINT_VITA_ENABLE_FRONT_TOUCH = "SDL_VITA_ENABLE_FRONT_TOUCH"; + public const string SDL_HINT_VITA_MODULE_PATH = "SDL_VITA_MODULE_PATH"; + public const string SDL_HINT_VITA_PVR_INIT = "SDL_VITA_PVR_INIT"; + public const string SDL_HINT_VITA_RESOLUTION = "SDL_VITA_RESOLUTION"; + public const string SDL_HINT_VITA_PVR_OPENGL = "SDL_VITA_PVR_OPENGL"; + public const string SDL_HINT_VITA_TOUCH_MOUSE_DEVICE = "SDL_VITA_TOUCH_MOUSE_DEVICE"; + public const string SDL_HINT_VULKAN_DISPLAY = "SDL_VULKAN_DISPLAY"; + public const string SDL_HINT_VULKAN_LIBRARY = "SDL_VULKAN_LIBRARY"; + public const string SDL_HINT_WAVE_FACT_CHUNK = "SDL_WAVE_FACT_CHUNK"; + public const string SDL_HINT_WAVE_CHUNK_LIMIT = "SDL_WAVE_CHUNK_LIMIT"; + public const string SDL_HINT_WAVE_RIFF_CHUNK_SIZE = "SDL_WAVE_RIFF_CHUNK_SIZE"; + public const string SDL_HINT_WAVE_TRUNCATION = "SDL_WAVE_TRUNCATION"; + public const string SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED = "SDL_WINDOW_ACTIVATE_WHEN_RAISED"; + public const string SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN = "SDL_WINDOW_ACTIVATE_WHEN_SHOWN"; + public const string SDL_HINT_WINDOW_ALLOW_TOPMOST = "SDL_WINDOW_ALLOW_TOPMOST"; + public const string SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN = "SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN"; + public const string SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4 = "SDL_WINDOWS_CLOSE_ON_ALT_F4"; + public const string SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS = "SDL_WINDOWS_ENABLE_MENU_MNEMONICS"; + public const string SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP = "SDL_WINDOWS_ENABLE_MESSAGELOOP"; + public const string SDL_HINT_WINDOWS_GAMEINPUT = "SDL_WINDOWS_GAMEINPUT"; + public const string SDL_HINT_WINDOWS_RAW_KEYBOARD = "SDL_WINDOWS_RAW_KEYBOARD"; + public const string SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL = "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL"; + public const string SDL_HINT_WINDOWS_INTRESOURCE_ICON = "SDL_WINDOWS_INTRESOURCE_ICON"; + public const string SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL = "SDL_WINDOWS_INTRESOURCE_ICON_SMALL"; + public const string SDL_HINT_WINDOWS_USE_D3D9EX = "SDL_WINDOWS_USE_D3D9EX"; + public const string SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE = "SDL_WINDOWS_ERASE_BACKGROUND_MODE"; + public const string SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT = "SDL_X11_FORCE_OVERRIDE_REDIRECT"; + public const string SDL_HINT_X11_WINDOW_TYPE = "SDL_X11_WINDOW_TYPE"; + public const string SDL_HINT_X11_XCB_LIBRARY = "SDL_X11_XCB_LIBRARY"; + public const string SDL_HINT_XINPUT_ENABLED = "SDL_XINPUT_ENABLED"; + public const string SDL_HINT_ASSERT = "SDL_ASSERT"; + + public enum SDL_HintPriority + { + SDL_HINT_DEFAULT = 0, + SDL_HINT_NORMAL = 1, + SDL_HINT_OVERRIDE = 2, + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetHintWithPriority(string name, string value, SDL_HintPriority priority); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetHint(string name, string value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ResetHint(string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ResetHints(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetHint(string name); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetHintBoolean(string name, SDLBool default_value); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_HintCallback(IntPtr userdata, byte* name, byte* oldValue, byte* newValue); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_AddHintCallback(string name, SDL_HintCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_RemoveHintCallback(string name, SDL_HintCallback callback, IntPtr userdata); + + // /usr/local/include/SDL3/SDL_init.h + + public const string SDL_PROP_APP_METADATA_NAME_STRING = "SDL.app.metadata.name"; + public const string SDL_PROP_APP_METADATA_VERSION_STRING = "SDL.app.metadata.version"; + public const string SDL_PROP_APP_METADATA_IDENTIFIER_STRING = "SDL.app.metadata.identifier"; + public const string SDL_PROP_APP_METADATA_CREATOR_STRING = "SDL.app.metadata.creator"; + public const string SDL_PROP_APP_METADATA_COPYRIGHT_STRING = "SDL.app.metadata.copyright"; + public const string SDL_PROP_APP_METADATA_URL_STRING = "SDL.app.metadata.url"; + public const string SDL_PROP_APP_METADATA_TYPE_STRING = "SDL.app.metadata.type"; + + [Flags] + public enum SDL_InitFlags : uint + { + SDL_INIT_TIMER = 0x1, + SDL_INIT_AUDIO = 0x10, + SDL_INIT_VIDEO = 0x20, + SDL_INIT_JOYSTICK = 0x200, + SDL_INIT_HAPTIC = 0x1000, + SDL_INIT_GAMEPAD = 0x2000, + SDL_INIT_EVENTS = 0x4000, + SDL_INIT_SENSOR = 0x08000, + SDL_INIT_CAMERA = 0x10000, + } + + public enum SDL_AppResult + { + SDL_APP_CONTINUE = 0, + SDL_APP_SUCCESS = 1, + SDL_APP_FAILURE = 2, + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate SDL_AppResult SDL_AppInit_func(IntPtr appstate, int argc, IntPtr argv); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate SDL_AppResult SDL_AppIterate_func(IntPtr appstate); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate SDL_AppResult SDL_AppEvent_func(IntPtr appstate, SDL_Event* evt); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_AppQuit_func(IntPtr appstate, SDL_AppResult result); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_Init(SDL_InitFlags flags); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_InitSubSystem(SDL_InitFlags flags); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_QuitSubSystem(SDL_InitFlags flags); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_InitFlags SDL_WasInit(SDL_InitFlags flags); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_Quit(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAppMetadata(string appname, string appversion, string appidentifier); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetAppMetadataProperty(string name, string value); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetAppMetadataProperty(string name); + + // /usr/local/include/SDL3/SDL_loadso.h + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_LoadObject(string sofile); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_LoadFunction(IntPtr handle, string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnloadObject(IntPtr handle); + + // /usr/local/include/SDL3/SDL_locale.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Locale + { + public byte* language; + public byte* country; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetPreferredLocales(out int count); + + // /usr/local/include/SDL3/SDL_log.h + + public enum SDL_LogCategory + { + SDL_LOG_CATEGORY_APPLICATION = 0, + SDL_LOG_CATEGORY_ERROR = 1, + SDL_LOG_CATEGORY_ASSERT = 2, + SDL_LOG_CATEGORY_SYSTEM = 3, + SDL_LOG_CATEGORY_AUDIO = 4, + SDL_LOG_CATEGORY_VIDEO = 5, + SDL_LOG_CATEGORY_RENDER = 6, + SDL_LOG_CATEGORY_INPUT = 7, + SDL_LOG_CATEGORY_TEST = 8, + SDL_LOG_CATEGORY_GPU = 9, + SDL_LOG_CATEGORY_RESERVED2 = 10, + SDL_LOG_CATEGORY_RESERVED3 = 11, + SDL_LOG_CATEGORY_RESERVED4 = 12, + SDL_LOG_CATEGORY_RESERVED5 = 13, + SDL_LOG_CATEGORY_RESERVED6 = 14, + SDL_LOG_CATEGORY_RESERVED7 = 15, + SDL_LOG_CATEGORY_RESERVED8 = 16, + SDL_LOG_CATEGORY_RESERVED9 = 17, + SDL_LOG_CATEGORY_RESERVED10 = 18, + SDL_LOG_CATEGORY_CUSTOM = 19, + } + + public enum SDL_LogPriority + { + SDL_LOG_PRIORITY_INVALID = 0, + SDL_LOG_PRIORITY_TRACE = 1, + SDL_LOG_PRIORITY_VERBOSE = 2, + SDL_LOG_PRIORITY_DEBUG = 3, + SDL_LOG_PRIORITY_INFO = 4, + SDL_LOG_PRIORITY_WARN = 5, + SDL_LOG_PRIORITY_ERROR = 6, + SDL_LOG_PRIORITY_CRITICAL = 7, + SDL_LOG_PRIORITY_COUNT = 8, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetLogPriorities(SDL_LogPriority priority); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetLogPriority(int category, SDL_LogPriority priority); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_LogPriority SDL_GetLogPriority(int category); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_ResetLogPriorities(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetLogPriorityPrefix(SDL_LogPriority priority, string prefix); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_Log(string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogTrace(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogVerbose(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogDebug(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogInfo(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogWarn(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogError(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogCritical(int category, string fmt); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_LogMessage(int category, SDL_LogPriority priority, string fmt); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SDL_LogOutputFunction(IntPtr userdata, int category, SDL_LogPriority priority, byte* message); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetDefaultLogOutputFunction(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_GetLogOutputFunction(out SDL_LogOutputFunction callback, out IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, IntPtr userdata); + + // /usr/local/include/SDL3/SDL_messagebox.h + + [Flags] + public enum SDL_MessageBoxFlags : uint + { + SDL_MESSAGEBOX_ERROR = 0x10, + SDL_MESSAGEBOX_WARNING = 0x20, + SDL_MESSAGEBOX_INFORMATION = 0x40, + SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT = 0x080, + SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT = 0x100, + } + + [Flags] + public enum SDL_MessageBoxButtonFlags : uint + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT = 0x1, + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT = 0x2, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MessageBoxButtonData + { + public SDL_MessageBoxButtonFlags flags; + public int buttonID; + public byte* text; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MessageBoxColor + { + public byte r; + public byte g; + public byte b; + } + + public enum SDL_MessageBoxColorType + { + SDL_MESSAGEBOX_COLOR_BACKGROUND = 0, + SDL_MESSAGEBOX_COLOR_TEXT = 1, + SDL_MESSAGEBOX_COLOR_BUTTON_BORDER = 2, + SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND = 3, + SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED = 4, + SDL_MESSAGEBOX_COLOR_COUNT = 5, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MessageBoxColorScheme + { + public SDL_MessageBoxColor colors0; + public SDL_MessageBoxColor colors1; + public SDL_MessageBoxColor colors2; + public SDL_MessageBoxColor colors3; + public SDL_MessageBoxColor colors4; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_MessageBoxData + { + public SDL_MessageBoxFlags flags; + public IntPtr window; + public byte* title; + public byte* message; + public int numbuttons; + public SDL_MessageBoxButtonData* buttons; + public SDL_MessageBoxColorScheme* colorScheme; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShowMessageBox(ref SDL_MessageBoxData messageboxdata, out int buttonid); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, string title, string message, IntPtr window); + + // /usr/local/include/SDL3/SDL_metal.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_Metal_CreateView(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_Metal_DestroyView(IntPtr view); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_Metal_GetLayer(IntPtr view); + + // /usr/local/include/SDL3/SDL_misc.h + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_OpenURL(string url); + + // /usr/local/include/SDL3/SDL_platform.h + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetPlatform(); + + // /usr/local/include/SDL3/SDL_process.h + + public const string SDL_PROP_PROCESS_CREATE_ARGS_POINTER = "SDL.process.create.args"; + public const string SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER = "SDL.process.create.environment"; + public const string SDL_PROP_PROCESS_CREATE_STDIN_NUMBER = "SDL.process.create.stdin_option"; + public const string SDL_PROP_PROCESS_CREATE_STDIN_POINTER = "SDL.process.create.stdin_source"; + public const string SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER = "SDL.process.create.stdout_option"; + public const string SDL_PROP_PROCESS_CREATE_STDOUT_POINTER = "SDL.process.create.stdout_source"; + public const string SDL_PROP_PROCESS_CREATE_STDERR_NUMBER = "SDL.process.create.stderr_option"; + public const string SDL_PROP_PROCESS_CREATE_STDERR_POINTER = "SDL.process.create.stderr_source"; + public const string SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN = "SDL.process.create.stderr_to_stdout"; + public const string SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN = "SDL.process.create.background"; + public const string SDL_PROP_PROCESS_PID_NUMBER = "SDL.process.pid"; + public const string SDL_PROP_PROCESS_STDIN_POINTER = "SDL.process.stdin"; + public const string SDL_PROP_PROCESS_STDOUT_POINTER = "SDL.process.stdout"; + public const string SDL_PROP_PROCESS_STDERR_POINTER = "SDL.process.stderr"; + public const string SDL_PROP_PROCESS_BACKGROUND_BOOLEAN = "SDL.process.background"; + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateProcess(IntPtr args, SDLBool pipe_stdio); + + public enum SDL_ProcessIO + { + SDL_PROCESS_STDIO_INHERITED = 0, + SDL_PROCESS_STDIO_NULL = 1, + SDL_PROCESS_STDIO_APP = 2, + SDL_PROCESS_STDIO_REDIRECT = 3, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateProcessWithProperties(uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetProcessProperties(IntPtr process); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_ReadProcess(IntPtr process, out UIntPtr datasize, out int exitcode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetProcessInput(IntPtr process); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetProcessOutput(IntPtr process); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_KillProcess(IntPtr process, SDLBool force); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WaitProcess(IntPtr process, SDLBool block, out int exitcode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyProcess(IntPtr process); + + // /usr/local/include/SDL3/SDL_render.h + + public const string SDL_PROP_RENDERER_CREATE_NAME_STRING = "SDL.renderer.create.name"; + public const string SDL_PROP_RENDERER_CREATE_WINDOW_POINTER = "SDL.renderer.create.window"; + public const string SDL_PROP_RENDERER_CREATE_SURFACE_POINTER = "SDL.renderer.create.surface"; + public const string SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER = "SDL.renderer.create.output_colorspace"; + public const string SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER = "SDL.renderer.create.present_vsync"; + public const string SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER = "SDL.renderer.create.vulkan.instance"; + public const string SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER = "SDL.renderer.create.vulkan.surface"; + public const string SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER = "SDL.renderer.create.vulkan.physical_device"; + public const string SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER = "SDL.renderer.create.vulkan.device"; + public const string SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER = "SDL.renderer.create.vulkan.graphics_queue_family_index"; + public const string SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER = "SDL.renderer.create.vulkan.present_queue_family_index"; + public const string SDL_PROP_RENDERER_NAME_STRING = "SDL.renderer.name"; + public const string SDL_PROP_RENDERER_WINDOW_POINTER = "SDL.renderer.window"; + public const string SDL_PROP_RENDERER_SURFACE_POINTER = "SDL.renderer.surface"; + public const string SDL_PROP_RENDERER_VSYNC_NUMBER = "SDL.renderer.vsync"; + public const string SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER = "SDL.renderer.max_texture_size"; + public const string SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER = "SDL.renderer.texture_formats"; + public const string SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER = "SDL.renderer.output_colorspace"; + public const string SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN = "SDL.renderer.HDR_enabled"; + public const string SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT = "SDL.renderer.SDR_white_point"; + public const string SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT = "SDL.renderer.HDR_headroom"; + public const string SDL_PROP_RENDERER_D3D9_DEVICE_POINTER = "SDL.renderer.d3d9.device"; + public const string SDL_PROP_RENDERER_D3D11_DEVICE_POINTER = "SDL.renderer.d3d11.device"; + public const string SDL_PROP_RENDERER_D3D11_SWAPCHAIN_POINTER = "SDL.renderer.d3d11.swap_chain"; + public const string SDL_PROP_RENDERER_D3D12_DEVICE_POINTER = "SDL.renderer.d3d12.device"; + public const string SDL_PROP_RENDERER_D3D12_SWAPCHAIN_POINTER = "SDL.renderer.d3d12.swap_chain"; + public const string SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER = "SDL.renderer.d3d12.command_queue"; + public const string SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER = "SDL.renderer.vulkan.instance"; + public const string SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER = "SDL.renderer.vulkan.surface"; + public const string SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER = "SDL.renderer.vulkan.physical_device"; + public const string SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER = "SDL.renderer.vulkan.device"; + public const string SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER = "SDL.renderer.vulkan.graphics_queue_family_index"; + public const string SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER = "SDL.renderer.vulkan.present_queue_family_index"; + public const string SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER = "SDL.renderer.vulkan.swapchain_image_count"; + public const string SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER = "SDL.texture.create.colorspace"; + public const string SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER = "SDL.texture.create.format"; + public const string SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER = "SDL.texture.create.access"; + public const string SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER = "SDL.texture.create.width"; + public const string SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER = "SDL.texture.create.height"; + public const string SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT = "SDL.texture.create.SDR_white_point"; + public const string SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT = "SDL.texture.create.HDR_headroom"; + public const string SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER = "SDL.texture.create.d3d11.texture"; + public const string SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER = "SDL.texture.create.d3d11.texture_u"; + public const string SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER = "SDL.texture.create.d3d11.texture_v"; + public const string SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_POINTER = "SDL.texture.create.d3d12.texture"; + public const string SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER = "SDL.texture.create.d3d12.texture_u"; + public const string SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER = "SDL.texture.create.d3d12.texture_v"; + public const string SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER = "SDL.texture.create.metal.pixelbuffer"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_NUMBER = "SDL.texture.create.opengl.texture"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_UV_NUMBER = "SDL.texture.create.opengl.texture_uv"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER = "SDL.texture.create.opengl.texture_u"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_V_NUMBER = "SDL.texture.create.opengl.texture_v"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER = "SDL.texture.create.opengles2.texture"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_UV_NUMBER = "SDL.texture.create.opengles2.texture_uv"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER = "SDL.texture.create.opengles2.texture_u"; + public const string SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER = "SDL.texture.create.opengles2.texture_v"; + public const string SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER = "SDL.texture.create.vulkan.texture"; + public const string SDL_PROP_TEXTURE_COLORSPACE_NUMBER = "SDL.texture.colorspace"; + public const string SDL_PROP_TEXTURE_FORMAT_NUMBER = "SDL.texture.format"; + public const string SDL_PROP_TEXTURE_ACCESS_NUMBER = "SDL.texture.access"; + public const string SDL_PROP_TEXTURE_WIDTH_NUMBER = "SDL.texture.width"; + public const string SDL_PROP_TEXTURE_HEIGHT_NUMBER = "SDL.texture.height"; + public const string SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT = "SDL.texture.SDR_white_point"; + public const string SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT = "SDL.texture.HDR_headroom"; + public const string SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER = "SDL.texture.d3d11.texture"; + public const string SDL_PROP_TEXTURE_D3D11_TEXTURE_U_POINTER = "SDL.texture.d3d11.texture_u"; + public const string SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER = "SDL.texture.d3d11.texture_v"; + public const string SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER = "SDL.texture.d3d12.texture"; + public const string SDL_PROP_TEXTURE_D3D12_TEXTURE_U_POINTER = "SDL.texture.d3d12.texture_u"; + public const string SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER = "SDL.texture.d3d12.texture_v"; + public const string SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER = "SDL.texture.opengl.texture"; + public const string SDL_PROP_TEXTURE_OPENGL_TEXTURE_UV_NUMBER = "SDL.texture.opengl.texture_uv"; + public const string SDL_PROP_TEXTURE_OPENGL_TEXTURE_U_NUMBER = "SDL.texture.opengl.texture_u"; + public const string SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER = "SDL.texture.opengl.texture_v"; + public const string SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER = "SDL.texture.opengl.target"; + public const string SDL_PROP_TEXTURE_OPENGL_TEX_W_FLOAT = "SDL.texture.opengl.tex_w"; + public const string SDL_PROP_TEXTURE_OPENGL_TEX_H_FLOAT = "SDL.texture.opengl.tex_h"; + public const string SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER = "SDL.texture.opengles2.texture"; + public const string SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_UV_NUMBER = "SDL.texture.opengles2.texture_uv"; + public const string SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER = "SDL.texture.opengles2.texture_u"; + public const string SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_V_NUMBER = "SDL.texture.opengles2.texture_v"; + public const string SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER = "SDL.texture.opengles2.target"; + public const string SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER = "SDL.texture.vulkan.texture"; + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Vertex + { + public SDL_FPoint position; + public SDL_FColor color; + public SDL_FPoint tex_coord; + } + + public enum SDL_TextureAccess + { + SDL_TEXTUREACCESS_STATIC = 0, + SDL_TEXTUREACCESS_STREAMING = 1, + SDL_TEXTUREACCESS_TARGET = 2, + } + + public enum SDL_RendererLogicalPresentation + { + SDL_LOGICAL_PRESENTATION_DISABLED = 0, + SDL_LOGICAL_PRESENTATION_STRETCH = 1, + SDL_LOGICAL_PRESENTATION_LETTERBOX = 2, + SDL_LOGICAL_PRESENTATION_OVERSCAN = 3, + SDL_LOGICAL_PRESENTATION_INTEGER_SCALE = 4, + } + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_Texture + { + public SDL_PixelFormat format; + public int w; + public int h; + public int refcount; + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetNumRenderDrivers(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetRenderDriver(int index); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CreateWindowAndRenderer(string title, int width, int height, SDL_WindowFlags window_flags, out IntPtr window, out IntPtr renderer); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateRenderer(IntPtr window, string name); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateRendererWithProperties(uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateSoftwareRenderer(IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetRenderer(IntPtr window); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetRenderWindow(IntPtr renderer); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetRendererName(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetRendererProperties(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderOutputSize(IntPtr renderer, out int w, out int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetCurrentRenderOutputSize(IntPtr renderer, out int w, out int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateTexture(IntPtr renderer, SDL_PixelFormat format, SDL_TextureAccess access, int w, int h); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateTextureFromSurface(IntPtr renderer, IntPtr surface); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_CreateTextureWithProperties(IntPtr renderer, uint props); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_GetTextureProperties(IntPtr texture); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetRendererFromTexture(IntPtr texture); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureSize(IntPtr texture, out float w, out float h); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextureColorMod(IntPtr texture, byte r, byte g, byte b); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextureColorModFloat(IntPtr texture, float r, float g, float b); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureColorMod(IntPtr texture, out byte r, out byte g, out byte b); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureColorModFloat(IntPtr texture, out float r, out float g, out float b); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextureAlphaMod(IntPtr texture, byte alpha); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextureAlphaModFloat(IntPtr texture, float alpha); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureAlphaMod(IntPtr texture, out byte alpha); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureAlphaModFloat(IntPtr texture, out float alpha); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextureBlendMode(IntPtr texture, uint blendMode); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureBlendMode(IntPtr texture, IntPtr blendMode); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetTextureScaleMode(IntPtr texture, SDL_ScaleMode scaleMode); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetTextureScaleMode(IntPtr texture, out SDL_ScaleMode scaleMode); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UpdateTexture(IntPtr texture, ref SDL_Rect rect, IntPtr pixels, int pitch); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UpdateYUVTexture(IntPtr texture, ref SDL_Rect rect, IntPtr Yplane, int Ypitch, IntPtr Uplane, int Upitch, IntPtr Vplane, int Vpitch); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_UpdateNVTexture(IntPtr texture, ref SDL_Rect rect, IntPtr Yplane, int Ypitch, IntPtr UVplane, int UVpitch); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LockTexture(IntPtr texture, ref SDL_Rect rect, out IntPtr pixels, out int pitch); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_LockTextureToSurface(IntPtr texture, ref SDL_Rect rect, out IntPtr surface); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_UnlockTexture(IntPtr texture); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderTarget(IntPtr renderer, IntPtr texture); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetRenderTarget(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderLogicalPresentation(IntPtr renderer, int w, int h, SDL_RendererLogicalPresentation mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderLogicalPresentation(IntPtr renderer, out int w, out int h, out SDL_RendererLogicalPresentation mode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderLogicalPresentationRect(IntPtr renderer, out SDL_FRect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderCoordinatesFromWindow(IntPtr renderer, float window_x, float window_y, out float x, out float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderCoordinatesToWindow(IntPtr renderer, float x, float y, out float window_x, out float window_y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ConvertEventToRenderCoordinates(IntPtr renderer, ref SDL_Event @event); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderViewport(IntPtr renderer, ref SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderViewport(IntPtr renderer, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderViewportSet(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderSafeArea(IntPtr renderer, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderClipRect(IntPtr renderer, ref SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderClipRect(IntPtr renderer, out SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderClipEnabled(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderScale(IntPtr renderer, float scaleX, float scaleY); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderScale(IntPtr renderer, out float scaleX, out float scaleY); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderDrawColor(IntPtr renderer, byte r, byte g, byte b, byte a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderDrawColorFloat(IntPtr renderer, float r, float g, float b, float a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderDrawColor(IntPtr renderer, out byte r, out byte g, out byte b, out byte a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderDrawColorFloat(IntPtr renderer, out float r, out float g, out float b, out float a); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderColorScale(IntPtr renderer, float scale); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderColorScale(IntPtr renderer, out float scale); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderDrawBlendMode(IntPtr renderer, uint blendMode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderDrawBlendMode(IntPtr renderer, IntPtr blendMode); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderClear(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderPoint(IntPtr renderer, float x, float y); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderPoints(IntPtr renderer, Span points, int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderLine(IntPtr renderer, float x1, float y1, float x2, float y2); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderLines(IntPtr renderer, Span points, int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderRect(IntPtr renderer, ref SDL_FRect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderRects(IntPtr renderer, Span rects, int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderFillRect(IntPtr renderer, ref SDL_FRect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderFillRects(IntPtr renderer, Span rects, int count); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderTexture(IntPtr renderer, IntPtr texture, ref SDL_FRect srcrect, ref SDL_FRect dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderTextureRotated(IntPtr renderer, IntPtr texture, ref SDL_FRect srcrect, ref SDL_FRect dstrect, double angle, ref SDL_FPoint center, SDL_FlipMode flip); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderTextureTiled(IntPtr renderer, IntPtr texture, ref SDL_FRect srcrect, float scale, ref SDL_FRect dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderTexture9Grid(IntPtr renderer, IntPtr texture, ref SDL_FRect srcrect, float left_width, float right_width, float top_height, float bottom_height, float scale, ref SDL_FRect dstrect); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderGeometry(IntPtr renderer, IntPtr texture, Span vertices, int num_vertices, Span indices, int num_indices); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderGeometryRaw(IntPtr renderer, IntPtr texture, IntPtr xy, int xy_stride, IntPtr color, int color_stride, IntPtr uv, int uv_stride, int num_vertices, IntPtr indices, int num_indices, int size_indices); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_RenderReadPixels(IntPtr renderer, ref SDL_Rect rect); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderPresent(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyTexture(IntPtr texture); // WARN_UNKNOWN_POINTER_PARAMETER + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DestroyRenderer(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_FlushRenderer(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetRenderMetalLayer(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GetRenderMetalCommandEncoder(IntPtr renderer); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_AddVulkanRenderSemaphores(IntPtr renderer, uint wait_stage_mask, long wait_semaphore, long signal_semaphore); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetRenderVSync(IntPtr renderer, int vsync); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetRenderVSync(IntPtr renderer, out int vsync); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenderDebugText(IntPtr renderer, float x, float y, string str); + + // /usr/local/include/SDL3/SDL_storage.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_StorageInterface + { + public uint version; + public IntPtr close; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr ready; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr enumerate; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr info; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr read_file; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr write_file; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr mkdir; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr remove; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr rename; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr copy; // WARN_ANONYMOUS_FUNCTION_POINTER + public IntPtr space_remaining; // WARN_ANONYMOUS_FUNCTION_POINTER + } + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenTitleStorage(string @override, uint props); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenUserStorage(string org, string app, uint props); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenFileStorage(string path); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_OpenStorage(ref SDL_StorageInterface iface, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CloseStorage(IntPtr storage); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_StorageReady(IntPtr storage); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetStorageFileSize(IntPtr storage, string path, out ulong length); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_ReadStorageFile(IntPtr storage, string path, IntPtr destination, ulong length); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_WriteStorageFile(IntPtr storage, string path, IntPtr source, ulong length); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CreateStorageDirectory(IntPtr storage, string path); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_EnumerateStorageDirectory(IntPtr storage, string path, SDL_EnumerateDirectoryCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RemoveStoragePath(IntPtr storage, string path); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RenameStoragePath(IntPtr storage, string oldpath, string newpath); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_CopyStorageFile(IntPtr storage, string oldpath, string newpath); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetStoragePathInfo(IntPtr storage, string path, out SDL_PathInfo info); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetStorageSpaceRemaining(IntPtr storage); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial IntPtr SDL_GlobStorageDirectory(IntPtr storage, string path, string pattern, SDL_GlobFlags flags, out int count); + + // /usr/local/include/SDL3/SDL_system.h + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool SDL_X11EventHook(IntPtr userdata, IntPtr xevent); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetX11EventHook(SDL_X11EventHook callback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetLinuxThreadPriority(long threadID, int priority); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_SetLinuxThreadPriorityAndPolicy(long threadID, int sdlPriority, int schedPolicy); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_IsTablet(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_IsTV(); + + public enum SDL_Sandbox + { + SDL_SANDBOX_NONE = 0, + SDL_SANDBOX_UNKNOWN_CONTAINER = 1, + SDL_SANDBOX_FLATPAK = 2, + SDL_SANDBOX_SNAP = 3, + SDL_SANDBOX_MACOS = 4, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDL_Sandbox SDL_GetSandbox(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_OnApplicationWillTerminate(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_OnApplicationDidReceiveMemoryWarning(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_OnApplicationWillEnterBackground(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_OnApplicationDidEnterBackground(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_OnApplicationWillEnterForeground(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_OnApplicationDidEnterForeground(); + + // /usr/local/include/SDL3/SDL_time.h + + [StructLayout(LayoutKind.Sequential)] + public struct SDL_DateTime + { + public int year; + public int month; + public int day; + public int hour; + public int minute; + public int second; + public int nanosecond; + public int day_of_week; + public int utc_offset; + } + + public enum SDL_DateFormat + { + SDL_DATE_FORMAT_YYYYMMDD = 0, + SDL_DATE_FORMAT_DDMMYYYY = 1, + SDL_DATE_FORMAT_MMDDYYYY = 2, + } + + public enum SDL_TimeFormat + { + SDL_TIME_FORMAT_24HR = 0, + SDL_TIME_FORMAT_12HR = 1, + } + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetDateTimeLocalePreferences(out SDL_DateFormat dateFormat, out SDL_TimeFormat timeFormat); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_GetCurrentTime(IntPtr ticks); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_TimeToDateTime(long ticks, out SDL_DateTime dt, SDLBool localTime); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_DateTimeToTime(ref SDL_DateTime dt, IntPtr ticks); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_TimeToWindows(long ticks, out uint dwLowDateTime, out uint dwHighDateTime); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long SDL_TimeFromWindows(uint dwLowDateTime, uint dwHighDateTime); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetDaysInMonth(int year, int month); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetDayOfYear(int year, int month, int day); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetDayOfWeek(int year, int month, int day); + + // /usr/local/include/SDL3/SDL_timer.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetTicks(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetTicksNS(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetPerformanceCounter(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial ulong SDL_GetPerformanceFrequency(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_Delay(uint ms); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DelayNS(ulong ns); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_DelayPrecise(ulong ns); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate uint SDL_TimerCallback(IntPtr userdata, uint timerID, uint interval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_AddTimer(uint interval, SDL_TimerCallback callback, IntPtr userdata); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ulong SDL_NSTimerCallback(IntPtr userdata, uint timerID, ulong interval); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint SDL_AddTimerNS(ulong interval, SDL_NSTimerCallback callback, IntPtr userdata); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial SDLBool SDL_RemoveTimer(uint id); + + // /usr/local/include/SDL3/SDL_version.h + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_GetVersion(); + + [LibraryImport(nativeLibName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalUsing(typeof(SDLOwnedStringMarshaller))] + public static partial string SDL_GetRevision(); + + // /usr/local/include/SDL3/SDL_main.h + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int SDL_main_func(int argc, IntPtr argv); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_main(int argc, IntPtr argv); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void SDL_SetMainReady(); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_RunApp(int argc, IntPtr argv, SDL_main_func mainFunction, IntPtr reserved); + + [LibraryImport(nativeLibName)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int SDL_EnterAppMainCallbacks(int argc, IntPtr argv, SDL_AppInit_func appinit, SDL_AppIterate_func appiter, SDL_AppEvent_func appevent, SDL_AppQuit_func appquit); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport.sln b/src/BUTR.CrashReport.sln index 43b5ffc..b6504cf 100644 --- a/src/BUTR.CrashReport.sln +++ b/src/BUTR.CrashReport.sln @@ -5,17 +5,19 @@ VisualStudioVersion = 17.6.33815.320 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BUTR.CrashReport", "BUTR.CrashReport\BUTR.CrashReport.csproj", "{B1100D38-D4C9-4B83-96BC-2252449A89E5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BUTR.CrashReport.Bannerlord.Source", "BUTR.CrashReport.Bannerlord.Source\BUTR.CrashReport.Bannerlord.Source.csproj", "{80698068-7B95-487A-9DD8-E02DE27127AC}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{A3D68C79-7883-4B81-AE9A-E926FE80F65B}" ProjectSection(SolutionItems) = preProject ..\.github\workflows\publish.yml = ..\.github\workflows\publish.yml + ..\.github\workflows\publish-wasm.yml = ..\.github\workflows\publish-wasm.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8013DEFB-F940-4891-9F74-BFE1A8106506}" ProjectSection(SolutionItems) = preProject ..\.editorconfig = ..\.editorconfig ..\README.md = ..\README.md + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + nuget.config = nuget.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{56E6B143-9B80-4E7E-999A-3D4C17761F86}" @@ -23,9 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{56E6B143 ..\build\common.props = ..\build\common.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Bannerlord.Parser", "BUTR.CrashReport.Bannerlord.Parser\BUTR.CrashReport.Bannerlord.Parser.csproj", "{51A4BB52-A8E0-4BC3-8476-23E272413954}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Tool", "BUTR.CrashReport.Tool\BUTR.CrashReport.Tool.csproj", "{36A642D2-9075-470D-B5B4-40EA20E0C16C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.Html.Tool", "BUTR.CrashReport.Renderer.Html.Tool\BUTR.CrashReport.Renderer.Html.Tool.csproj", "{36A642D2-9075-470D-B5B4-40EA20E0C16C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Models", "BUTR.CrashReport.Models\BUTR.CrashReport.Models.csproj", "{03DE899E-3EE2-4EA4-8D5D-188C1AB83365}" EndProject @@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.BepInEx5.S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.BepInEx6.Source", "BUTR.CrashReport.BepInEx6.Source\BUTR.CrashReport.BepInEx6.Source.csproj", "{D8AB3D2C-6026-4ACA-BFBB-A341E4D913AD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{814173C2-6334-44C4-86FC-527E20AB718F}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.ImGui", "BUTR.CrashReport.Renderer.ImGui\BUTR.CrashReport.Renderer.ImGui.csproj", "{62EDA4D7-1DF2-4D63-8FD5-E0EFF813650D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.Html", "BUTR.CrashReport.Renderer.Html\BUTR.CrashReport.Renderer.Html.csproj", "{4FE11F43-B2CC-43C3-A947-5231050F8AA5}" @@ -43,6 +45,58 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.Z EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.WinForms", "BUTR.CrashReport.Renderer.WinForms\BUTR.CrashReport.Renderer.WinForms.csproj", "{1464F441-48F8-4CB5-B944-1683F0ACBC3E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.ImGui.Tool", "BUTR.CrashReport.Renderer.ImGui.Tool\BUTR.CrashReport.Renderer.ImGui.Tool.csproj", "{38A40917-CB4D-49E6-B08A-751BD330852E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.ILMerged", "BUTR.CrashReport.ILMerged\BUTR.CrashReport.ILMerged.csproj", "{813A7D2A-F144-40A7-B3F0-34CFF8D660D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{14BFD4AE-BB54-40F6-96F8-D6F4D4A33B54}" + ProjectSection(SolutionItems) = preProject + ..\external\.editorconfig = ..\external\.editorconfig + ..\external\Directory.Build.props = ..\external\Directory.Build.props + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utf8StringInterpolation", "..\external\Utf8StringInterpolation\src\Utf8StringInterpolation\Utf8StringInterpolation.csproj", "{56C91033-3CBB-4FBB-9DC8-C0227F336C1A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZString", "..\external\ZString\src\ZString\ZString.csproj", "{30E6FC2B-9331-454C-9FB6-E63A5503E23E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.CImGui", "BUTR.CrashReport.CImGui\BUTR.CrashReport.CImGui.csproj", "{5B42E44E-F58F-4A9A-A93E-51A375912B11}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.ImGui", "BUTR.CrashReport.ImGui\BUTR.CrashReport.ImGui.csproj", "{5B6FC58F-DD17-4E1D-9E54-8D0B3089325D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrismSharp.Core", "..\external\prism-sharp\PrismSharp.Core\PrismSharp.Core.csproj", "{63E215E5-EDC3-4C57-BC87-D4D0803AEEE8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrismSharp.SourceGenerator", "..\external\prism-sharp\PrismSharp.SourceGenerator\PrismSharp.SourceGenerator.csproj", "{C8BBE6A5-864E-4CF9-8021-8D4C105B9DF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrismSharp.RegExCompiler", "..\external\prism-sharp\PrismSharp.RegExCompiler\PrismSharp.RegExCompiler.csproj", "{89DA1FB0-CE0E-466A-897A-F96B541A8B6B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "etc", "etc", "{F4E2D353-AD2D-455B-81B6-F743E0CAD7DF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGuiColorTextEditNet", "ImGuiColorTextEditNet\ImGuiColorTextEditNet.csproj", "{CCA2F514-2028-47E4-8BDD-0C46B371836B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.ImGui.WASM", "BUTR.CrashReport.Renderer.ImGui.WASM\BUTR.CrashReport.Renderer.ImGui.WASM.csproj", "{B2E732BE-17B2-4014-95A8-C588C6F624D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Native.SourceGenerator", "BUTR.CrashReport.Native.SourceGenerator\BUTR.CrashReport.Native.SourceGenerator.csproj", "{D0706FE3-6913-42AF-9B2B-6E9E6BD43F52}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Native", "BUTR.CrashReport.Native\BUTR.CrashReport.Native.csproj", "{F1706FCA-25D4-4DCC-B9D3-EFF5CB46B3E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.OpenGLES3", "BUTR.CrashReport.OpenGLES3\BUTR.CrashReport.OpenGLES3.csproj", "{C2C9DD59-224D-4FAD-8F8B-5F8A6B171103}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bindings", "Bindings", "{FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.SDL2", "BUTR.CrashReport.SDL2\BUTR.CrashReport.SDL2.csproj", "{497A37DC-EBCB-48D9-AAAE-8D01E0E1EAD1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.SDL3", "BUTR.CrashReport.SDL3\BUTR.CrashReport.SDL3.csproj", "{FA5A16BE-9C90-4FC4-BD43-36ABCEE5CE48}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Emscripten", "BUTR.CrashReport.Emscripten\BUTR.CrashReport.Emscripten.csproj", "{087BC952-D8BC-4075-8C7C-17D5A4AF68B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Memory", "BUTR.CrashReport.Memory\BUTR.CrashReport.Memory.csproj", "{559FAD6D-9906-47AE-90A6-78C14FF4629A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.ImGui.Silk.NET", "BUTR.CrashReport.Renderer.ImGui.Silk.NET\BUTR.CrashReport.Renderer.ImGui.Silk.NET.csproj", "{B73A9FC8-B1D3-4BAD-864F-1404FD56F2D9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BUTR.CrashReport.Renderer.ImGui.Silk.NET.ILMerged", "BUTR.CrashReport.Renderer.ImGui.Silk.NET.ILMerged\BUTR.CrashReport.Renderer.ImGui.Silk.NET.ILMerged.csproj", "{BCAF0EFF-2150-4A5F-A97F-B5B7578D9889}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Renderers", "Renderers", "{71872CAF-7D4F-4A1E-A372-5382B4B24DA6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -53,14 +107,6 @@ Global {B1100D38-D4C9-4B83-96BC-2252449A89E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1100D38-D4C9-4B83-96BC-2252449A89E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1100D38-D4C9-4B83-96BC-2252449A89E5}.Release|Any CPU.Build.0 = Release|Any CPU - {80698068-7B95-487A-9DD8-E02DE27127AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {80698068-7B95-487A-9DD8-E02DE27127AC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {80698068-7B95-487A-9DD8-E02DE27127AC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {80698068-7B95-487A-9DD8-E02DE27127AC}.Release|Any CPU.Build.0 = Release|Any CPU - {51A4BB52-A8E0-4BC3-8476-23E272413954}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {51A4BB52-A8E0-4BC3-8476-23E272413954}.Debug|Any CPU.Build.0 = Debug|Any CPU - {51A4BB52-A8E0-4BC3-8476-23E272413954}.Release|Any CPU.ActiveCfg = Release|Any CPU - {51A4BB52-A8E0-4BC3-8476-23E272413954}.Release|Any CPU.Build.0 = Release|Any CPU {36A642D2-9075-470D-B5B4-40EA20E0C16C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {36A642D2-9075-470D-B5B4-40EA20E0C16C}.Debug|Any CPU.Build.0 = Debug|Any CPU {36A642D2-9075-470D-B5B4-40EA20E0C16C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -81,6 +127,10 @@ Global {D8AB3D2C-6026-4ACA-BFBB-A341E4D913AD}.Debug|Any CPU.Build.0 = Debug|Any CPU {D8AB3D2C-6026-4ACA-BFBB-A341E4D913AD}.Release|Any CPU.ActiveCfg = Release|Any CPU {D8AB3D2C-6026-4ACA-BFBB-A341E4D913AD}.Release|Any CPU.Build.0 = Release|Any CPU + {814173C2-6334-44C4-86FC-527E20AB718F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {814173C2-6334-44C4-86FC-527E20AB718F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {814173C2-6334-44C4-86FC-527E20AB718F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {814173C2-6334-44C4-86FC-527E20AB718F}.Release|Any CPU.Build.0 = Release|Any CPU {62EDA4D7-1DF2-4D63-8FD5-E0EFF813650D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62EDA4D7-1DF2-4D63-8FD5-E0EFF813650D}.Debug|Any CPU.Build.0 = Debug|Any CPU {62EDA4D7-1DF2-4D63-8FD5-E0EFF813650D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -97,6 +147,86 @@ Global {1464F441-48F8-4CB5-B944-1683F0ACBC3E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1464F441-48F8-4CB5-B944-1683F0ACBC3E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1464F441-48F8-4CB5-B944-1683F0ACBC3E}.Release|Any CPU.Build.0 = Release|Any CPU + {38A40917-CB4D-49E6-B08A-751BD330852E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38A40917-CB4D-49E6-B08A-751BD330852E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38A40917-CB4D-49E6-B08A-751BD330852E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38A40917-CB4D-49E6-B08A-751BD330852E}.Release|Any CPU.Build.0 = Release|Any CPU + {813A7D2A-F144-40A7-B3F0-34CFF8D660D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {813A7D2A-F144-40A7-B3F0-34CFF8D660D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {813A7D2A-F144-40A7-B3F0-34CFF8D660D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {813A7D2A-F144-40A7-B3F0-34CFF8D660D0}.Release|Any CPU.Build.0 = Release|Any CPU + {56C91033-3CBB-4FBB-9DC8-C0227F336C1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56C91033-3CBB-4FBB-9DC8-C0227F336C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56C91033-3CBB-4FBB-9DC8-C0227F336C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56C91033-3CBB-4FBB-9DC8-C0227F336C1A}.Release|Any CPU.Build.0 = Release|Any CPU + {30E6FC2B-9331-454C-9FB6-E63A5503E23E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30E6FC2B-9331-454C-9FB6-E63A5503E23E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30E6FC2B-9331-454C-9FB6-E63A5503E23E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30E6FC2B-9331-454C-9FB6-E63A5503E23E}.Release|Any CPU.Build.0 = Release|Any CPU + {5B42E44E-F58F-4A9A-A93E-51A375912B11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B42E44E-F58F-4A9A-A93E-51A375912B11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B42E44E-F58F-4A9A-A93E-51A375912B11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B42E44E-F58F-4A9A-A93E-51A375912B11}.Release|Any CPU.Build.0 = Release|Any CPU + {5B6FC58F-DD17-4E1D-9E54-8D0B3089325D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B6FC58F-DD17-4E1D-9E54-8D0B3089325D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B6FC58F-DD17-4E1D-9E54-8D0B3089325D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B6FC58F-DD17-4E1D-9E54-8D0B3089325D}.Release|Any CPU.Build.0 = Release|Any CPU + {63E215E5-EDC3-4C57-BC87-D4D0803AEEE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63E215E5-EDC3-4C57-BC87-D4D0803AEEE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63E215E5-EDC3-4C57-BC87-D4D0803AEEE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63E215E5-EDC3-4C57-BC87-D4D0803AEEE8}.Release|Any CPU.Build.0 = Release|Any CPU + {C8BBE6A5-864E-4CF9-8021-8D4C105B9DF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8BBE6A5-864E-4CF9-8021-8D4C105B9DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8BBE6A5-864E-4CF9-8021-8D4C105B9DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8BBE6A5-864E-4CF9-8021-8D4C105B9DF9}.Release|Any CPU.Build.0 = Release|Any CPU + {89DA1FB0-CE0E-466A-897A-F96B541A8B6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89DA1FB0-CE0E-466A-897A-F96B541A8B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89DA1FB0-CE0E-466A-897A-F96B541A8B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89DA1FB0-CE0E-466A-897A-F96B541A8B6B}.Release|Any CPU.Build.0 = Release|Any CPU + {CCA2F514-2028-47E4-8BDD-0C46B371836B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCA2F514-2028-47E4-8BDD-0C46B371836B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCA2F514-2028-47E4-8BDD-0C46B371836B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCA2F514-2028-47E4-8BDD-0C46B371836B}.Release|Any CPU.Build.0 = Release|Any CPU + {B2E732BE-17B2-4014-95A8-C588C6F624D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2E732BE-17B2-4014-95A8-C588C6F624D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2E732BE-17B2-4014-95A8-C588C6F624D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2E732BE-17B2-4014-95A8-C588C6F624D4}.Release|Any CPU.Build.0 = Release|Any CPU + {D0706FE3-6913-42AF-9B2B-6E9E6BD43F52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0706FE3-6913-42AF-9B2B-6E9E6BD43F52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0706FE3-6913-42AF-9B2B-6E9E6BD43F52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0706FE3-6913-42AF-9B2B-6E9E6BD43F52}.Release|Any CPU.Build.0 = Release|Any CPU + {F1706FCA-25D4-4DCC-B9D3-EFF5CB46B3E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1706FCA-25D4-4DCC-B9D3-EFF5CB46B3E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1706FCA-25D4-4DCC-B9D3-EFF5CB46B3E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1706FCA-25D4-4DCC-B9D3-EFF5CB46B3E8}.Release|Any CPU.Build.0 = Release|Any CPU + {C2C9DD59-224D-4FAD-8F8B-5F8A6B171103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2C9DD59-224D-4FAD-8F8B-5F8A6B171103}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2C9DD59-224D-4FAD-8F8B-5F8A6B171103}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2C9DD59-224D-4FAD-8F8B-5F8A6B171103}.Release|Any CPU.Build.0 = Release|Any CPU + {497A37DC-EBCB-48D9-AAAE-8D01E0E1EAD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {497A37DC-EBCB-48D9-AAAE-8D01E0E1EAD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {497A37DC-EBCB-48D9-AAAE-8D01E0E1EAD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {497A37DC-EBCB-48D9-AAAE-8D01E0E1EAD1}.Release|Any CPU.Build.0 = Release|Any CPU + {FA5A16BE-9C90-4FC4-BD43-36ABCEE5CE48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA5A16BE-9C90-4FC4-BD43-36ABCEE5CE48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA5A16BE-9C90-4FC4-BD43-36ABCEE5CE48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA5A16BE-9C90-4FC4-BD43-36ABCEE5CE48}.Release|Any CPU.Build.0 = Release|Any CPU + {087BC952-D8BC-4075-8C7C-17D5A4AF68B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {087BC952-D8BC-4075-8C7C-17D5A4AF68B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {087BC952-D8BC-4075-8C7C-17D5A4AF68B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {087BC952-D8BC-4075-8C7C-17D5A4AF68B7}.Release|Any CPU.Build.0 = Release|Any CPU + {559FAD6D-9906-47AE-90A6-78C14FF4629A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {559FAD6D-9906-47AE-90A6-78C14FF4629A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {559FAD6D-9906-47AE-90A6-78C14FF4629A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {559FAD6D-9906-47AE-90A6-78C14FF4629A}.Release|Any CPU.Build.0 = Release|Any CPU + {B73A9FC8-B1D3-4BAD-864F-1404FD56F2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B73A9FC8-B1D3-4BAD-864F-1404FD56F2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B73A9FC8-B1D3-4BAD-864F-1404FD56F2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B73A9FC8-B1D3-4BAD-864F-1404FD56F2D9}.Release|Any CPU.Build.0 = Release|Any CPU + {BCAF0EFF-2150-4A5F-A97F-B5B7578D9889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCAF0EFF-2150-4A5F-A97F-B5B7578D9889}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCAF0EFF-2150-4A5F-A97F-B5B7578D9889}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCAF0EFF-2150-4A5F-A97F-B5B7578D9889}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -106,16 +236,37 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {B1100D38-D4C9-4B83-96BC-2252449A89E5} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {80698068-7B95-487A-9DD8-E02DE27127AC} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {51A4BB52-A8E0-4BC3-8476-23E272413954} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {36A642D2-9075-470D-B5B4-40EA20E0C16C} = {8013DEFB-F940-4891-9F74-BFE1A8106506} {03DE899E-3EE2-4EA4-8D5D-188C1AB83365} = {8013DEFB-F940-4891-9F74-BFE1A8106506} {CE6956D6-1C78-45E3-995E-93CE49BDA524} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {3A6C3D77-82EA-4B83-BE30-22D0EE3A230F} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {D8AB3D2C-6026-4ACA-BFBB-A341E4D913AD} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {62EDA4D7-1DF2-4D63-8FD5-E0EFF813650D} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {4FE11F43-B2CC-43C3-A947-5231050F8AA5} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {B63FD775-79E8-48A2-AECA-1F7D73643024} = {8013DEFB-F940-4891-9F74-BFE1A8106506} - {1464F441-48F8-4CB5-B944-1683F0ACBC3E} = {8013DEFB-F940-4891-9F74-BFE1A8106506} + {814173C2-6334-44C4-86FC-527E20AB718F} = {8013DEFB-F940-4891-9F74-BFE1A8106506} + {813A7D2A-F144-40A7-B3F0-34CFF8D660D0} = {8013DEFB-F940-4891-9F74-BFE1A8106506} + {56C91033-3CBB-4FBB-9DC8-C0227F336C1A} = {14BFD4AE-BB54-40F6-96F8-D6F4D4A33B54} + {30E6FC2B-9331-454C-9FB6-E63A5503E23E} = {14BFD4AE-BB54-40F6-96F8-D6F4D4A33B54} + {63E215E5-EDC3-4C57-BC87-D4D0803AEEE8} = {14BFD4AE-BB54-40F6-96F8-D6F4D4A33B54} + {C8BBE6A5-864E-4CF9-8021-8D4C105B9DF9} = {14BFD4AE-BB54-40F6-96F8-D6F4D4A33B54} + {89DA1FB0-CE0E-466A-897A-F96B541A8B6B} = {14BFD4AE-BB54-40F6-96F8-D6F4D4A33B54} + {D8AB3D2C-6026-4ACA-BFBB-A341E4D913AD} = {F4E2D353-AD2D-455B-81B6-F743E0CAD7DF} + {3A6C3D77-82EA-4B83-BE30-22D0EE3A230F} = {F4E2D353-AD2D-455B-81B6-F743E0CAD7DF} + {CCA2F514-2028-47E4-8BDD-0C46B371836B} = {8013DEFB-F940-4891-9F74-BFE1A8106506} + {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} = {8013DEFB-F940-4891-9F74-BFE1A8106506} + {C2C9DD59-224D-4FAD-8F8B-5F8A6B171103} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {F1706FCA-25D4-4DCC-B9D3-EFF5CB46B3E8} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {D0706FE3-6913-42AF-9B2B-6E9E6BD43F52} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {497A37DC-EBCB-48D9-AAAE-8D01E0E1EAD1} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {FA5A16BE-9C90-4FC4-BD43-36ABCEE5CE48} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {5B42E44E-F58F-4A9A-A93E-51A375912B11} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {087BC952-D8BC-4075-8C7C-17D5A4AF68B7} = {FF1A2EB5-6C39-4E70-AC7F-1D41AD4F54CB} + {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} = {8013DEFB-F940-4891-9F74-BFE1A8106506} + {4FE11F43-B2CC-43C3-A947-5231050F8AA5} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {36A642D2-9075-470D-B5B4-40EA20E0C16C} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {62EDA4D7-1DF2-4D63-8FD5-E0EFF813650D} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {B73A9FC8-B1D3-4BAD-864F-1404FD56F2D9} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {BCAF0EFF-2150-4A5F-A97F-B5B7578D9889} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {38A40917-CB4D-49E6-B08A-751BD330852E} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {B2E732BE-17B2-4014-95A8-C588C6F624D4} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {1464F441-48F8-4CB5-B944-1683F0ACBC3E} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {B63FD775-79E8-48A2-AECA-1F7D73643024} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {5B6FC58F-DD17-4E1D-9E54-8D0B3089325D} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} + {559FAD6D-9906-47AE-90A6-78C14FF4629A} = {71872CAF-7D4F-4A1E-A372-5382B4B24DA6} EndGlobalSection EndGlobal diff --git a/src/BUTR.CrashReport/BUTR.CrashReport.csproj b/src/BUTR.CrashReport/BUTR.CrashReport.csproj index e5fabc5..44303d0 100644 --- a/src/BUTR.CrashReport/BUTR.CrashReport.csproj +++ b/src/BUTR.CrashReport/BUTR.CrashReport.csproj @@ -2,68 +2,33 @@ netstandard2.0 - preview + latest enable + + $(DefineConstants);FeatureMemory;FeatureValueTuple;FeatureValueTask; + true + true - - Debug;Release - true - true - true - false - - - - - - BUTR.CrashReport BUTR.CrashReport - Contains the models for creating the crash report + Contains the code for creating the crash reports MIT https://raw.githubusercontent.com/BUTR/BUTR.CrashReport/master/assets/Icon128x128.png - butr crash report bannerlord + butr crash report - - - - - - - - - - - - - + + + - + - - - - - $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)0Harmony.dll; - - - $(ILRepackExcludeAssemblies);$(ProjectDir)$(OutputPath)BUTR.CrashReport.Models.dll; - - - - - - false - - - diff --git a/src/BUTR.CrashReport/CrashReportInfo.cs b/src/BUTR.CrashReport/CrashReportInfo.cs index 5c00f1d..1ed030a 100644 --- a/src/BUTR.CrashReport/CrashReportInfo.cs +++ b/src/BUTR.CrashReport/CrashReportInfo.cs @@ -26,11 +26,12 @@ public static CrashReportModel ToModel(CrashReportInfo crashReport, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider, IAssemblyUtilities assemblyUtilities, - IPathAnonymizer pathAnonymizer) + IPathAnonymizer pathAnonymizer, + IHttpUtilities httpUtilities) { var assemblies = CrashReportModelUtils.GetAssemblies(crashReport, assemblyUtilities, pathAnonymizer); - var modules = modelConverter.ToModuleModels(crashReport.LoadedModules, assemblies); - var plugins = modelConverter.ToLoaderPluginModels(crashReport.LoadedLoaderPlugins, assemblies); + var modules = modelConverter.ToModuleModels(crashReport, crashReport.LoadedModules); + var plugins = modelConverter.ToLoaderPluginModels(crashReport, crashReport.LoadedLoaderPlugins); var metadata = crashReportMetadataProvider.GetCrashReportMetadataModel(crashReport); metadata.Runtime ??= System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription; var process = Process.GetCurrentProcess(); @@ -46,40 +47,37 @@ public static CrashReportModel ToModel(CrashReportInfo crashReport, Architecture = x.Architecture, Hash = x.Hash, AnonymizedPath = x.AnonymizedPath, - AdditionalMetadata = Array.Empty(), + AdditionalMetadata = [], }).ToArray(); + var enhancedStacktrace = CrashReportModelUtils.GetEnhancedStacktrace(crashReport, assemblies, assemblyUtilities, httpUtilities); return new CrashReportModel { Id = crashReport.Id, Version = crashReport.Version, Exception = CrashReportModelUtils.GetRecursiveException(crashReport, assemblies), - EnhancedStacktrace = CrashReportModelUtils.GetEnhancedStacktrace(crashReport, assemblies), - InvolvedModules = CrashReportModelUtils.GetInvolvedModules(crashReport), + EnhancedStacktrace = enhancedStacktrace, + InvolvedModules = CrashReportModelUtils.GetInvolvedModules(enhancedStacktrace), Modules = modules, Assemblies = assemblies, NativeModules = nativeAssemblies, - RuntimePatches = CrashReportModelUtils.GetRuntimePatches(crashReport, assemblies, moduleProvider, loaderPluginProvider), - //HarmonyPatches = CrashReportModelUtils.GetHarmonyPatches(crashReport, assemblies, moduleProvider, loaderPluginProvider), - //MonoModPatches = CrashReportModelUtils.GetMonoModDetours(crashReport, assemblies, moduleProvider, loaderPluginProvider), + RuntimePatches = CrashReportModelUtils.GetRuntimePatches(crashReport, moduleProvider, loaderPluginProvider), LoaderPlugins = plugins, - InvolvedLoaderPlugins = CrashReportModelUtils.GetInvolvedPlugins(crashReport), + InvolvedLoaderPlugins = CrashReportModelUtils.GetInvolvedPlugins(enhancedStacktrace), Metadata = metadata, - AdditionalMetadata = Array.Empty(), + AdditionalMetadata = [], }; } /// /// Creates the CrashReportInfo based on initial crash report data. /// - public static CrashReportInfo Create(Exception exception, Dictionary additionalMetadata, + public static CrashReportInfo Create(Exception exception, IStacktraceFilter stacktraceFilter, IAssemblyUtilities assemblyUtilities, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider, - IPatchProvider patchProvider /*, - IMonoModProvider monoModProvider, - IHarmonyProvider harmonyProvider*/) => - new(exception, additionalMetadata, stacktraceFilter, assemblyUtilities, moduleProvider, loaderPluginProvider, patchProvider/*, monoModProvider, harmonyProvider*/); + IRuntimePatchProvider runtimePatchProvider) => + new(exception, stacktraceFilter, assemblyUtilities, moduleProvider, loaderPluginProvider, runtimePatchProvider); /// /// @@ -132,82 +130,60 @@ public static CrashReportInfo Create(Exception exception, Dictionary ImportedTypeReferences { get; } /// - /// + /// /// - /// - public Dictionary> LoadedRuntimePatches { get; } = new(); - - /* - /// - /// - /// - /// - public Dictionary LoadedMonoModPatches { get; } = new(); - - /// - /// - /// - /// - public Dictionary LoadedHarmonyPatches { get; } = new(); - */ + /// + public Dictionary> LoadedManagedRuntimePatches { get; } = new(); /// - /// Additional metadata about the crash. + /// /// - public Dictionary AdditionalMetadata { get; } + /// + public Dictionary> LoadedNativeRuntimePatches { get; } = new(); /// /// Creates the CrashReportInfo based on initial crash report data. /// - private CrashReportInfo(Exception exception, Dictionary additionalMetadata, + private CrashReportInfo(Exception exception, IStacktraceFilter stacktraceFilter, IAssemblyUtilities assemblyUtilities, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider, - IPatchProvider patchProvider /*, - IMonoModProvider monoModProvider, - IHarmonyProvider harmonyProvider */) + IRuntimePatchProvider runtimePatchProvider) { var assemblies = assemblyUtilities.Assemblies().ToArray(); Exception = exception.Demystify(); - AdditionalMetadata = additionalMetadata; LoadedModules = moduleProvider.GetLoadedModules(); LoadedLoaderPlugins = loaderPluginProvider.GetLoadedLoaderPlugins(); AvailableAssemblies = assemblies.ToDictionary(x => x.GetName(), x => x); - ImportedTypeReferences = GetImportedTypeReferences(AvailableAssemblies).ToDictionary(x => x.Key, x => x.Value.Select(y => new AssemblyTypeReference + ImportedTypeReferences = AvailableAssemblies.ToDictionary(x => x.Key, x => GetImportedTypeReferences(x.Value, assemblyUtilities.GetAssemblyStream).Select(y => new AssemblyTypeReference { Name = y.Name, Namespace = y.Namespace, - FullName = y.FullName + FullName = y.FullName, }).ToArray()); - Stacktrace = CrashReportUtils.GetAllInvolvedModules(Exception, assemblies, assemblyUtilities, moduleProvider, loaderPluginProvider, patchProvider /*monoModProvider, harmonyProvider*/).ToArray(); + Stacktrace = CrashReportUtils.GetEnhancedStacktrace(Exception, assemblies, assemblyUtilities, moduleProvider, loaderPluginProvider, runtimePatchProvider).ToArray(); FilteredStacktrace = stacktraceFilter.Filter(Stacktrace).ToArray(); - /* - foreach (var originalMethod in monoModProvider.GetAllPatchedMethods()) - { - var patches = monoModProvider.GetPatchInfo(originalMethod); - if (originalMethod is null || patches is null) continue; - LoadedMonoModPatches.Add(originalMethod, patches); - } - - foreach (var originalMethod in harmonyProvider.GetAllPatchedMethods()) + var managedPatches = runtimePatchProvider.GetAllManagedPatches(); + for (var i = 0; i < managedPatches.Count; i++) { - var patches = harmonyProvider.GetPatchInfo(originalMethod); - if (originalMethod is null || patches is null) continue; - LoadedHarmonyPatches.Add(originalMethod, patches); + var runtimePatch = managedPatches[i]; + if (!LoadedManagedRuntimePatches.ContainsKey(runtimePatch.Original)) + LoadedManagedRuntimePatches[runtimePatch.Original] = new List(); + LoadedManagedRuntimePatches[runtimePatch.Original].Add(runtimePatch); } - */ - - var runtimePatches = patchProvider.GetAllPatches(); - foreach (var runtimePatch in runtimePatches) + + var nativePatches = runtimePatchProvider.GetAllNativePatches(); + for (var i = 0; i < nativePatches.Count; i++) { - if (!LoadedRuntimePatches.ContainsKey(runtimePatch.Original)) - LoadedRuntimePatches[runtimePatch.Original] = new List(); - LoadedRuntimePatches[runtimePatch.Original].Add(runtimePatch); + var runtimePatch = nativePatches[i]; + if (!LoadedNativeRuntimePatches.ContainsKey(runtimePatch.Original)) + LoadedNativeRuntimePatches[runtimePatch.Original] = new List(); + LoadedNativeRuntimePatches[runtimePatch.Original].Add(runtimePatch); } } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Extensions/AssemblyImportedReferenceModelExtensions.cs b/src/BUTR.CrashReport/Extensions/AssemblyImportedReferenceModelExtensions.cs deleted file mode 100644 index 0d2d439..0000000 --- a/src/BUTR.CrashReport/Extensions/AssemblyImportedReferenceModelExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using BUTR.CrashReport.Decompilers.Utils; -using BUTR.CrashReport.Models; - -using System; -using System.Globalization; -using System.Reflection; - -namespace BUTR.CrashReport.Extensions; - -/// -/// Extensions for -/// -public static class AssemblyImportedReferenceModelExtensions -{ - /// - /// - /// - /// - public static string GetFullName(this AssemblyImportedReferenceModel model) => - AssemblyNameFormatter.ComputeDisplayName(model.Name, model.Version, model.Culture, model.PublicKeyToken); - - /// - /// Creates the model for an assembly name. - /// - public static AssemblyImportedReferenceModel Create(AssemblyName assemblyName) => new() - { - Name = assemblyName.Name, - Version = AssemblyNameFormatter.GetVersion(assemblyName.Version), - Culture = assemblyName.CultureName, - PublicKeyToken = string.Join(string.Empty, Array.ConvertAll(assemblyName.GetPublicKeyToken(), x => x.ToString("x2", CultureInfo.InvariantCulture))), - }; -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Extensions/AssemblyModelExtensions.cs b/src/BUTR.CrashReport/Extensions/AssemblyModelExtensions.cs deleted file mode 100644 index 24784ab..0000000 --- a/src/BUTR.CrashReport/Extensions/AssemblyModelExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using BUTR.CrashReport.Decompilers.Utils; -using BUTR.CrashReport.Models; - -namespace BUTR.CrashReport.Extensions; - -/// -/// Extensions for -/// -public static class AssemblyModelExtensions -{ - /// - /// - /// - /// - public static string GetFullName(this AssemblyModel model) => - AssemblyNameFormatter.ComputeDisplayName(model.Id.Name, model.Id.Version, model.CultureName, model.Id.PublicKeyToken); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Extensions/ModuleModelExtensions.cs b/src/BUTR.CrashReport/Extensions/ModuleModelExtensions.cs deleted file mode 100644 index 476c935..0000000 --- a/src/BUTR.CrashReport/Extensions/ModuleModelExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using BUTR.CrashReport.Decompilers.Utils; -using BUTR.CrashReport.Models; - -using System.Collections.Generic; -using System.Linq; - -namespace BUTR.CrashReport.Extensions; - -/// -/// Extensions for -/// -public static class ModuleModelExtensions -{ - /// - /// Gets whether the module contains an assembly reference. - /// - /// - /// The list of available assemblies - /// The assembly references to search for. Supports wildcard - public static bool ContainsAssemblyReferences(this ModuleModel model, IEnumerable assemblies, string[] assemblyReferences) => assemblies.Where(x => x.ModuleId == model.Id) - .SelectMany(x => x.ImportedAssemblyReferences) - .Any(x => assemblyReferences.Any(y => FileSystemName.MatchesSimpleExpression(y, x.Name))); - - /// - /// Gets whether the module contains an type reference. - /// - /// - /// The list of available assemblies - /// The type references to search for. Supports wildcard - public static bool ContainsTypeReferences(this ModuleModel model, IEnumerable assemblies, string[] typeReferences) => assemblies.Where(x => x.ModuleId == model.Id) - .SelectMany(x => x.ImportedTypeReferences) - .Any(x => typeReferences.Any(y => FileSystemName.MatchesSimpleExpression(y, x.FullName))); - - /// - /// Gets whether the module contains an assembly reference. - /// - /// - /// The list of available assemblies - /// The assembly references to search for. Supports wildcard - public static bool ContainsAssemblyReferences(this LoaderPluginModel model, IEnumerable assemblies, string[] assemblyReferences) => assemblies.Where(x => x.LoaderPluginId == model.Id) - .SelectMany(x => x.ImportedAssemblyReferences) - .Any(x => assemblyReferences.Any(y => FileSystemName.MatchesSimpleExpression(y, x.Name))); - - /// - /// Gets whether the module contains an type reference. - /// - /// - /// The list of available assemblies - /// The type references to search for. Supports wildcard - public static bool ContainsTypeReferences(this LoaderPluginModel model, IEnumerable assemblies, string[] typeReferences) => assemblies.Where(x => x.LoaderPluginId == model.Id) - .SelectMany(x => x.ImportedTypeReferences) - .Any(x => typeReferences.Any(y => FileSystemName.MatchesSimpleExpression(y, x.FullName))); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IAssemblyUtilities.cs b/src/BUTR.CrashReport/Interfaces/IAssemblyUtilities.cs index fc09dc7..70270a3 100644 --- a/src/BUTR.CrashReport/Interfaces/IAssemblyUtilities.cs +++ b/src/BUTR.CrashReport/Interfaces/IAssemblyUtilities.cs @@ -1,7 +1,8 @@ -using System; -using BUTR.CrashReport.Models; +using BUTR.CrashReport.Models; +using System; using System.Collections.Generic; +using System.IO; using System.Reflection; namespace BUTR.CrashReport.Interfaces; @@ -35,4 +36,14 @@ public interface IAssemblyUtilities /// Gets the type of the assembly /// AssemblyType GetAssemblyType(AssemblyType type, CrashReportInfo crashReport, Assembly assembly); + + /// + /// Returns the stream for the assembly if it exists. + /// + Stream? GetAssemblyStream(Assembly assembly); + + /// + /// Returns the PDB stream for the assembly if it exists. + /// + Stream? GetPdbStream(Assembly assembly); } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/ICommonProvider.cs b/src/BUTR.CrashReport/Interfaces/ICommonProvider.cs deleted file mode 100644 index d0fe5f4..0000000 --- a/src/BUTR.CrashReport/Interfaces/ICommonProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Diagnostics; -using System.Reflection; - -namespace BUTR.CrashReport.Interfaces; - -public interface ICommonProvider -{ - /// - /// Returns the JIT compiled (native) start address of a method. - /// - IntPtr GetNativeMethodBody(MethodBase method); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IHarmonyProvider.cs b/src/BUTR.CrashReport/Interfaces/IHarmonyProvider.cs deleted file mode 100644 index bf2c569..0000000 --- a/src/BUTR.CrashReport/Interfaces/IHarmonyProvider.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; - -namespace BUTR.CrashReport.Interfaces; - -/// -/// Provides information about Harmony patches. -/// -public interface IHarmonyProvider -{ - /// - /// Returns all patched methods. - /// - IEnumerable GetAllPatchedMethods(); - - /// - /// Returns the patch information for a given method. - /// - /// - HarmonyPatches? GetPatchInfo(MethodBase originalMethod); - - HarmonyPatches? GetPatchInfo(StackFrame frame, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider); - - /// - /// Returns the actual executing method from a stackframe. - /// - MethodInfo? GetExecutingMethod(StackFrame frame); - - /// - /// Returns the original method for a given patch method. - /// - /// The frame - MethodBase? GetOriginalMethod(StackFrame frame); - - /// - /// Returns the JIT compiled (native) start address of a method. - /// - IntPtr GetNativeMethodBody(MethodBase method); -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IHttpUtilities.cs b/src/BUTR.CrashReport/Interfaces/IHttpUtilities.cs new file mode 100644 index 0000000..cf91c0c --- /dev/null +++ b/src/BUTR.CrashReport/Interfaces/IHttpUtilities.cs @@ -0,0 +1,13 @@ +namespace BUTR.CrashReport.Interfaces; + +/// +/// Provides functionality related to http. +/// +public interface IHttpUtilities +{ + /// + /// Used by the decompilation engine to get the content of a source linked source code file. + /// Returns null if the content could not be retrieved or HTTP is not supported. + /// + string? GetStringFromUrl(string url); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IModelConverter.cs b/src/BUTR.CrashReport/Interfaces/IModelConverter.cs index f24fff1..456f0ed 100644 --- a/src/BUTR.CrashReport/Interfaces/IModelConverter.cs +++ b/src/BUTR.CrashReport/Interfaces/IModelConverter.cs @@ -12,10 +12,10 @@ public interface IModelConverter /// /// Converts the loaded modules to module models. /// - List ToModuleModels(ICollection loadedModules, ICollection assemblies); + List ToModuleModels(CrashReportInfo crashReportInfo, ICollection loadedModules); /// /// Converts the loaded assemblies to assembly models. /// - List ToLoaderPluginModels(ICollection loadedLoaderPlugins, ICollection assemblies); + List ToLoaderPluginModels(CrashReportInfo crashReportInfo, ICollection loadedLoaderPlugins); } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IMonoModProvider.cs b/src/BUTR.CrashReport/Interfaces/IMonoModProvider.cs deleted file mode 100644 index 247e625..0000000 --- a/src/BUTR.CrashReport/Interfaces/IMonoModProvider.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using BUTR.CrashReport.Models; - -namespace BUTR.CrashReport.Interfaces; - -public interface IMonoModProvider -{ - /// - /// Returns all patched methods. - /// - IEnumerable GetAllPatchedMethods(); - - MonoModPatches? GetPatchInfo(MethodBase originalMethod); - - MonoModPatches? GetPatchInfo(StackFrame frame, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider); - - /// - /// Returns the actual executing method from a stackframe. - /// - MethodInfo? GetExecutingMethod(StackFrame frame); - - /// - /// Returns the original method for a given patch method. - /// - /// The frame - MethodBase? GetOriginalMethod(StackFrame frame); - - /// - /// Returns the JIT compiled (native) start address of a method. - /// - IntPtr GetNativeMethodBody(MethodBase method); -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IPatchProvider.cs b/src/BUTR.CrashReport/Interfaces/IPatchProvider.cs deleted file mode 100644 index c6c35cc..0000000 --- a/src/BUTR.CrashReport/Interfaces/IPatchProvider.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using BUTR.CrashReport.Models; - -namespace BUTR.CrashReport.Interfaces; - -public interface IPatchProvider -{ - IList GetAllPatches(); - IList GetPatches(MethodBase originalMethod); - StackFrameRuntimePatch? GetPatches(StackFrame frame); - MethodBase GetOriginalMethod(StackFrame frame); - IntPtr GetNativeMethodBody(MethodBase method); -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Interfaces/IRuntimePatchProvider.cs b/src/BUTR.CrashReport/Interfaces/IRuntimePatchProvider.cs new file mode 100644 index 0000000..233e23f --- /dev/null +++ b/src/BUTR.CrashReport/Interfaces/IRuntimePatchProvider.cs @@ -0,0 +1,29 @@ +using BUTR.CrashReport.Models; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; + +namespace BUTR.CrashReport.Interfaces; + +/// +/// Provides information about runtime patches. +/// +public interface IRuntimePatchProvider +{ + /// + /// Gets all patches. + /// + IList GetAllManagedPatches(); + + /// + /// Gets all patches. + /// + IList GetAllNativePatches(); + + /// + /// Gets patches for the specified method. + /// + MethodWithRuntimePatches? GetPatchInfo(MethodBase method); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/AssemblyTypeReference.cs b/src/BUTR.CrashReport/Models/AssemblyTypeReference.cs index 27a6cc6..0129315 100644 --- a/src/BUTR.CrashReport/Models/AssemblyTypeReference.cs +++ b/src/BUTR.CrashReport/Models/AssemblyTypeReference.cs @@ -1,25 +1,27 @@ -namespace BUTR.CrashReport.Models; +using BUTR.CrashReport.Decompilers.Models; + +namespace BUTR.CrashReport.Models; /// -/// +/// /// public record AssemblyTypeReference { /// - /// + /// /// - /// + /// public required string Name { get; set; } /// - /// + /// /// - /// + /// public required string Namespace { get; set; } /// - /// + /// /// - /// + /// public required string FullName { get; set; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/HarmonyPatch.cs b/src/BUTR.CrashReport/Models/HarmonyPatch.cs deleted file mode 100644 index 2699724..0000000 --- a/src/BUTR.CrashReport/Models/HarmonyPatch.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* -using System.Collections.Generic; -using System.Reflection; - -namespace BUTR.CrashReport.Models; - -/// -/// -/// -public record HarmonyPatch -{ - /// - /// - /// - public required string Owner { get; set; } - - /// - /// - /// - public required int Index { get; set; } - - /// - /// - /// - public required int Priority { get; set; } - - /// - /// - /// - public required IList Before { get; set; } = new List(); - - /// - /// - /// - public required IList After { get; set; } = new List(); - - /// - /// - /// - public required MethodInfo PatchMethod { get; set; } - - /// - /// - /// - public required HarmonyPatchType Type { get; set; } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/HarmonyPatches.cs b/src/BUTR.CrashReport/Models/HarmonyPatches.cs deleted file mode 100644 index 568f49a..0000000 --- a/src/BUTR.CrashReport/Models/HarmonyPatches.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; - -namespace BUTR.CrashReport.Models; - -public record RuntimePatches -{ - public required IList Patches { get; set; } -} - -public record RuntimePatch -{ - public required string PatchProvider { get; set; } - - public required string PatchType { get; set; } - - public required MethodBase Original { get; set; } - public required MethodBase Patch { get; set; } - - /// - /// - /// - /// - public required IList AdditionalMetadata { get; set; } = new List(); -} - -/* -/// -/// -/// -public record HarmonyPatches -{ - /// - /// - /// - public required IList Prefixes { get; set; } - - /// - /// - /// - public required IList Postfixes { get; set; } - - /// - /// - /// - public required IList Finalizers { get; set; } - - /// - /// - /// - public required IList Transpilers { get; set; } -} - -public record MonoModPatches -{ - public required IList Detours { get; set; } - public required IList ILHooks { get; set; } -} - -public record MonoModPatch -{ - public required MethodBase Method { get; set; } - - public required bool IsActive { get; set; } - - public required string Id { get; set; } - - public required int? Index { get; set; } - - public required int? MaxIndex { get; set; } - - public required int? GlobalIndex { get; set; } - public required int? Priority { get; set; } - - public required int? SubPriority { get; set; } - - public required IList Before { get; set; } = new List(); - - public required IList After { get; set; } = new List(); -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/ILoaderPluginInfo.cs b/src/BUTR.CrashReport/Models/ILoaderPluginInfo.cs index 3ed5f30..961acfd 100644 --- a/src/BUTR.CrashReport/Models/ILoaderPluginInfo.cs +++ b/src/BUTR.CrashReport/Models/ILoaderPluginInfo.cs @@ -10,16 +10,4 @@ public interface ILoaderPluginInfo ///
/// string Id { get; } - - /// - /// - /// - /// - string? Version { get; } - - /// - /// - /// - /// - string? UpdateInfo { get; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/IModuleInfo.cs b/src/BUTR.CrashReport/Models/IModuleInfo.cs index 9373b39..7517d5b 100644 --- a/src/BUTR.CrashReport/Models/IModuleInfo.cs +++ b/src/BUTR.CrashReport/Models/IModuleInfo.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace BUTR.CrashReport.Models; +namespace BUTR.CrashReport.Models; /// /// Represents a module. @@ -12,21 +10,4 @@ public interface IModuleInfo /// /// string Id { get; } - - /// - /// - /// - /// - string Version { get; } - - /// - /// - /// - /// - string UpdateInfo { get; } - - /// - /// The list SubModules of the Module. - /// - IEnumerable SubModules { get; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/IModuleSubModuleInfo.cs b/src/BUTR.CrashReport/Models/IModuleSubModuleInfo.cs deleted file mode 100644 index 1e041ee..0000000 --- a/src/BUTR.CrashReport/Models/IModuleSubModuleInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace BUTR.CrashReport.Models; - -/// -/// Represents a SubModule -/// -public interface IModuleSubModuleInfo -{ - /// - /// - /// - /// - string AssemblyFile { get; } - - /// - /// The assemblies that are linked to the submodule - /// - string[] Dependencies { get; } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/ManagedRuntimePatch.cs b/src/BUTR.CrashReport/Models/ManagedRuntimePatch.cs new file mode 100644 index 0000000..c68fc6a --- /dev/null +++ b/src/BUTR.CrashReport/Models/ManagedRuntimePatch.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace BUTR.CrashReport.Models; + +/// +/// Represents a runtime patch. +/// +public sealed record ManagedRuntimePatch +{ + /// + /// The name of the patch provider. Could be Harmony or MonoMod + /// + public required string PatchProvider { get; set; } + + /// + /// The type of the patch. Could be Prefix, Postfix, Finalizer or Transpiler, Detour, ILHook + /// + public required string PatchType { get; set; } + + /// + /// The original method that was patched + /// + public required MethodBase Original { get; set; } + + /// + /// The patched method + /// + public required MethodBase Patch { get; set; } + + /// + /// + /// + /// + public required IList AdditionalMetadata { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/MethodEntry.cs b/src/BUTR.CrashReport/Models/MethodEntry.cs index 848228f..e887238 100644 --- a/src/BUTR.CrashReport/Models/MethodEntry.cs +++ b/src/BUTR.CrashReport/Models/MethodEntry.cs @@ -5,13 +5,18 @@ namespace BUTR.CrashReport.Models; /// /// Represents a method. /// -public abstract record MethodEntry +public record MethodEntry { /// - /// The Harmony patch method. + /// The actual method. /// public required MethodBase Method { get; set; } + /// + /// + /// + public required AssemblyIdModel? AssemblyId { get; set; } + /// /// /// @@ -23,22 +28,4 @@ public abstract record MethodEntry /// /// public required ILoaderPluginInfo? LoaderPluginInfo { get; set; } - - /// - /// - /// - /// - public required string[] ILInstructions { get; set; } - - /// - /// - /// - /// - public required string[] CSharpILMixedInstructions { get; set; } - - /// - /// - /// - /// - public required string[] CSharpInstructions { get; set; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/MethodEntryHarmony.cs b/src/BUTR.CrashReport/Models/MethodEntryHarmony.cs deleted file mode 100644 index bdcae3a..0000000 --- a/src/BUTR.CrashReport/Models/MethodEntryHarmony.cs +++ /dev/null @@ -1,14 +0,0 @@ -/* -namespace BUTR.CrashReport.Models; - -/// -/// Represents a Harmony patch. -/// -public record MethodEntryHarmony : MethodEntry -{ - /// - /// The harmony patch. - /// - public required HarmonyPatch Patch { get; set; } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/MethodEntryMonoMod.cs b/src/BUTR.CrashReport/Models/MethodEntryMonoMod.cs deleted file mode 100644 index bbcaa46..0000000 --- a/src/BUTR.CrashReport/Models/MethodEntryMonoMod.cs +++ /dev/null @@ -1,14 +0,0 @@ -/* -namespace BUTR.CrashReport.Models; - -/// -/// Represents a MonoMod patch. -/// -public record MethodEntryMonoMod : MethodEntry -{ - /// - /// The harmony patch. - /// - public required MonoModPatch Patch { get; set; } -} -*/ \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/MethodEntryRuntimePatch.cs b/src/BUTR.CrashReport/Models/MethodEntryRuntimePatch.cs deleted file mode 100644 index 231b174..0000000 --- a/src/BUTR.CrashReport/Models/MethodEntryRuntimePatch.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace BUTR.CrashReport.Models; - -/// -/// Represents a MonoMod patch. -/// -public record MethodEntryRuntimePatch : MethodEntry -{ - /// - /// The harmony patch. - /// - public required RuntimePatch Patch { get; set; } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/MethodEntrySimple.cs b/src/BUTR.CrashReport/Models/MethodEntrySimple.cs deleted file mode 100644 index f626909..0000000 --- a/src/BUTR.CrashReport/Models/MethodEntrySimple.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BUTR.CrashReport.Models; - -/// -/// Represents a method entry. -/// -public record MethodEntrySimple : MethodEntry; \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/MethodRuntimePatchEntry.cs b/src/BUTR.CrashReport/Models/MethodRuntimePatchEntry.cs new file mode 100644 index 0000000..25e5772 --- /dev/null +++ b/src/BUTR.CrashReport/Models/MethodRuntimePatchEntry.cs @@ -0,0 +1,12 @@ +namespace BUTR.CrashReport.Models; + +/// +/// Represents a method entry with runtime patch information. +/// +public sealed record MethodRuntimePatchEntry : MethodEntry +{ + /// + /// + /// + public required ManagedRuntimePatch Patch { get; set; } +} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/StackFrameRuntimePatch.cs b/src/BUTR.CrashReport/Models/MethodWithRuntimePatches.cs similarity index 68% rename from src/BUTR.CrashReport/Models/StackFrameRuntimePatch.cs rename to src/BUTR.CrashReport/Models/MethodWithRuntimePatches.cs index 23a7aa3..efbb6d6 100644 --- a/src/BUTR.CrashReport/Models/StackFrameRuntimePatch.cs +++ b/src/BUTR.CrashReport/Models/MethodWithRuntimePatches.cs @@ -1,10 +1,13 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; namespace BUTR.CrashReport.Models; -public record StackFrameRuntimePatch +/// +/// Represents a stack frame runtime patch. +/// +public sealed record MethodWithRuntimePatches { /// /// @@ -19,24 +22,21 @@ public record StackFrameRuntimePatch /// /// /// - public required IList Patches { get; set; } - - public required int ILOffset { get; set; } - - public required int NativeILOffset { get; set; } + public required IList Patches { get; set; } + /// + /// The native code pointer. + /// public required IntPtr NativeCodePtr { get; set; } - + /// /// Deconstructs the object. /// - public void Deconstruct(out MethodBase? original, out MethodBase? executing, out IList patches, out int ilOffset, out int nativeILOffset, out IntPtr nativeCodePtr) + public void Deconstruct(out MethodBase? original, out MethodBase? executing, out IList patches, out IntPtr nativeCodePtr) { original = OriginalMethod; executing = ExecutingMethod; patches = Patches; - ilOffset = ILOffset; - nativeILOffset = NativeILOffset; nativeCodePtr = NativeCodePtr; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/NativeModule.cs b/src/BUTR.CrashReport/Models/NativeModule.cs index ae4de3c..42dce8c 100644 --- a/src/BUTR.CrashReport/Models/NativeModule.cs +++ b/src/BUTR.CrashReport/Models/NativeModule.cs @@ -4,8 +4,14 @@ namespace BUTR.CrashReport.Utils; +/// +/// Represents a native module loaded in the process. +/// public class NativeModule { + /// + /// Initializes a new instance of the class. + /// public NativeModule(string moduleName, string anonymizedPath, string version, NativeAssemblyArchitectureType architecture, uint size, IntPtr inProcessAddress, uint inProcessSize, string hash) { ModuleName = moduleName; @@ -18,12 +24,43 @@ public NativeModule(string moduleName, string anonymizedPath, string version, Na InProcessSize = inProcessSize; } + /// + /// The name of the module. + /// public string ModuleName { get; set; } + + /// + /// The anonymized path of the module. + /// public string AnonymizedPath { get; set; } + + /// + /// The version of the module. + /// public string Version { get; set; } + + /// + /// The architecture of the module. + /// public NativeAssemblyArchitectureType Architecture { get; set; } + + /// + /// The hash of the module. + /// public string Hash { get; set; } + + /// + /// The size of the module. + /// public uint Size { get; set; } + + /// + /// The in-process address of the module. + /// public IntPtr InProcessAddress { get; set; } + + /// + /// The in-process size of the module. + /// public uint InProcessSize { get; set; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/NativeRuntimePatch.cs b/src/BUTR.CrashReport/Models/NativeRuntimePatch.cs new file mode 100644 index 0000000..d01ac0b --- /dev/null +++ b/src/BUTR.CrashReport/Models/NativeRuntimePatch.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace BUTR.CrashReport.Models; + +/// +/// Represents a runtime patch. +/// +public sealed record NativeRuntimePatch +{ + /// + /// The name of the patch provider. Could be Harmony or MonoMod + /// + public required string PatchProvider { get; set; } + + /// + /// The type of the patch. Could be Prefix, Postfix, Finalizer or Transpiler, Detour, ILHook + /// + public required string PatchType { get; set; } + + /// + /// The original method that was patched + /// + public required IntPtr Original { get; set; } + + /// + /// The patched method + /// + public required MethodBase Patch { get; set; } + + /// + /// + /// + /// + public required IList AdditionalMetadata { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Models/StacktraceEntry.cs b/src/BUTR.CrashReport/Models/StacktraceEntry.cs index 7a9c535..25f89ee 100644 --- a/src/BUTR.CrashReport/Models/StacktraceEntry.cs +++ b/src/BUTR.CrashReport/Models/StacktraceEntry.cs @@ -1,11 +1,13 @@ -using System.Reflection; +using System; +using System.Collections.Generic; +using System.Reflection; namespace BUTR.CrashReport.Models; /// /// /// -public record StacktraceEntry +public sealed record StacktraceEntry { /// /// @@ -17,7 +19,7 @@ public record StacktraceEntry /// /// /// - public required MethodEntrySimple? OriginalMethod { get; set; } + public required MethodEntry? OriginalMethod { get; set; } /// /// The module that holds the method. Can be null. @@ -33,47 +35,29 @@ public record StacktraceEntry /// /// /// - public required int? ILOffset { get; set; } + public required int ILOffset { get; set; } /// /// /// /// - public required int? NativeOffset { get; set; } + public required int NativeOffset { get; set; } /// - /// - /// - /// - public required string StackFrameDescription { get; set; } - - /// - /// - /// - /// - public required string[] NativeInstructions { get; set; } - - /// - /// - /// - /// - public required string[] ILInstructions { get; set; } - - /// - /// + /// /// - /// - public required string[] CSharpILMixedInstructions { get; set; } + /// + public required IntPtr NativeCodePtr { get; set; } /// - /// + /// /// - /// - public required string[] CSharpInstructions { get; set; } + /// + public required string StackFrameDescription { get; set; } /// /// /// /// - public required MethodEntry[] PatchMethods { get; set; } + public required MethodRuntimePatchEntry[] PatchMethods { get; set; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Utils/Anonymizer.cs b/src/BUTR.CrashReport/Utils/Anonymizer.cs index 477b7ae..47552eb 100644 --- a/src/BUTR.CrashReport/Utils/Anonymizer.cs +++ b/src/BUTR.CrashReport/Utils/Anonymizer.cs @@ -21,6 +21,8 @@ public void Add(string? path, string name) private static string NormalizePath(this string path) => path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + private static char[] ColonSeparator = [':']; + // TODO: Use store metadata to get the game install path? private static readonly List AnonymizationPathsSimple = new() { @@ -86,22 +88,25 @@ public static string AnonymizePath(string path) var normalizedPath = path.NormalizePath(); var entries = SplitWithIndex(normalizedPath, Path.DirectorySeparatorChar); - foreach (var kv in AnonymizationPaths) + for (var i = 0; i < AnonymizationPaths.Count; i++) { - if (string.IsNullOrEmpty(kv.Key)) continue; - if (TryAnonymizePath(normalizedPath, kv.Key!, kv.Value, out var anonymizedPath) && !string.IsNullOrEmpty(anonymizedPath)) + var (key, value) = AnonymizationPaths[i]; + if (string.IsNullOrEmpty(key)) continue; + if (TryAnonymizePath(normalizedPath, key!, value, out var anonymizedPath) && !string.IsNullOrEmpty(anonymizedPath)) return anonymizedPath!; } - foreach (var kv in AnonymizationPathsMultiColon) + for (var i = 0; i < AnonymizationPathsMultiColon.Count; i++) { - if (string.IsNullOrEmpty(kv.Key)) continue; - if (TryAnonymizePathsColonSeparated(normalizedPath, kv.Key!, kv.Value, out var anonymizedPath) && !string.IsNullOrEmpty(anonymizedPath)) + var (key, value) = AnonymizationPathsMultiColon[i]; + if (string.IsNullOrEmpty(key)) continue; + if (TryAnonymizePathsColonSeparated(normalizedPath, key!, value, out var anonymizedPath) && !string.IsNullOrEmpty(anonymizedPath)) return anonymizedPath!; } - foreach (var simplePath in AnonymizationPathsSimple) + for (var i = 0; i < AnonymizationPathsSimple.Count; i++) { + var simplePath = AnonymizationPathsSimple[i]; if (entries.FirstOrDefault(x => x.Substring.Equals(simplePath, StringComparison.OrdinalIgnoreCase)) is { } entrySimple) return path.Substring(entrySimple.Index); } @@ -123,7 +128,7 @@ private static bool TryAnonymizePath(string normalizedPath, string variablePath, private static bool TryAnonymizePathsColonSeparated(string normalizedPath, string variablePaths, string replacePath, out string? anonymizedPath) { - var variablePathsSplit = variablePaths.Split(':'); + var variablePathsSplit = variablePaths.Split(ColonSeparator); if (variablePathsSplit.Length == 0) variablePathsSplit = [variablePaths]; for (var i = 0; i < variablePathsSplit.Length; i++) { diff --git a/src/BUTR.CrashReport/Utils/CrashReportModelUtils.cs b/src/BUTR.CrashReport/Utils/CrashReportModelUtils.cs index 495fec7..06d01a4 100644 --- a/src/BUTR.CrashReport/Utils/CrashReportModelUtils.cs +++ b/src/BUTR.CrashReport/Utils/CrashReportModelUtils.cs @@ -1,4 +1,4 @@ -using BUTR.CrashReport.Extensions; +using BUTR.CrashReport.Decompilers.Utils; using BUTR.CrashReport.Interfaces; using BUTR.CrashReport.Models; @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Security.Cryptography; namespace BUTR.CrashReport.Utils; @@ -25,7 +24,7 @@ static ExceptionModel GetRecursiveExceptionInternal(IReadOnlyCollection y.Id.Name == ex.Source); - return new ExceptionModel + return new() { SourceAssemblyId = assembly?.Id, SourceModuleId = assembly?.ModuleId, @@ -34,7 +33,7 @@ static ExceptionModel GetRecursiveExceptionInternal(IReadOnlyCollection(), + AdditionalMetadata = [], }; } @@ -44,59 +43,70 @@ static ExceptionModel GetRecursiveExceptionInternal(IReadOnlyCollection /// Returns the /// - public static List GetEnhancedStacktrace(CrashReportInfo crashReport, IReadOnlyCollection assemblies) + public static List GetEnhancedStacktrace(CrashReportInfo crashReport, IReadOnlyCollection assemblies, IAssemblyUtilities assemblyUtilities, IHttpUtilities httpUtilities) { var enhancedStacktraceFrameModels = new List(); foreach (var stacktrace in crashReport.Stacktrace.GroupBy(x => x.StackFrameDescription)) { foreach (var entry in stacktrace) { - var methods = new List(entry.PatchMethods.Length); - foreach (var patchMethod in entry.PatchMethods) + var patches = entry.PatchMethods.Select(patchMethod => { - var patchAssemblyName = entry.Method.DeclaringType?.Assembly.GetName(); - var patchAssembly = patchAssemblyName is not null ? assemblies.FirstOrDefault(x => x.Id.Equals(patchAssemblyName)) : null; - var methodSimple = new MethodSimpleModel + var patchDecompiled = MethodDecompiler.DecompileMethod(patchMethod.Patch.Patch, null, false, assemblyUtilities.GetAssemblyStream, assemblyUtilities.GetPdbStream, httpUtilities.GetStringFromUrl); + return new MethodRuntimePatchModel { - AssemblyId = patchAssembly?.Id, + Provider = patchMethod.Patch.PatchProvider, + Type = patchMethod.Patch.PatchType, + AssemblyId = patchMethod.AssemblyId, ModuleId = patchMethod.ModuleInfo?.Id, LoaderPluginId = patchMethod.LoaderPluginInfo?.Id, MethodDeclaredTypeName = patchMethod.Method.DeclaringType?.FullName, MethodName = patchMethod.Method.Name, - MethodFullDescription = patchMethod.Method.FullDescription(), + MethodFullDescription = patchMethod.Method.FullDescription().ToString(), MethodParameters = patchMethod.Method.GetParameters().Select(x => x.ParameterType.FullName ?? string.Empty).ToArray(), - ILInstructions = patchMethod.ILInstructions, - CSharpILMixedInstructions = patchMethod.CSharpILMixedInstructions, - CSharpInstructions = patchMethod.CSharpInstructions, - AdditionalMetadata = Array.Empty(), - }; - methods.Add(patchMethod switch - { - /* - MethodEntryHarmony meh => methodSimple with + ILInstructions = new() { - AdditionalMetadata = methodSimple.AdditionalMetadata.Append(new MetadataModel { Key = "HarmonyPatchType", Value = meh.Patch.Type.ToString() }).ToArray() + Instructions = patchDecompiled.IL.Code, + Highlight = null, }, - */ - _ => methodSimple - }); - } + ILMixedInstructions = new() + { + Instructions = patchDecompiled.ILMixed.Code, + Highlight = null, + }, + CSharpInstructions = new() + { + Instructions = patchDecompiled.CSharp.Code, + Highlight = null, + }, + AdditionalMetadata = [], + }; + }).ToList(); - var executingAssemblyName = entry.Method.DeclaringType?.Assembly.GetName(); - var executingAssembly = executingAssemblyName is not null ? assemblies.FirstOrDefault(x => x.Id.Equals(executingAssemblyName)) : null; + var executingAssemblyName = entry.Method.Module.Assembly.GetName(); + var executingAssembly = assemblies.FirstOrDefault(x => x.Id.Equals(executingAssemblyName)); - var originalAssemblyName = entry.OriginalMethod?.Method.DeclaringType?.Assembly.GetName(); + var originalAssemblyName = entry.OriginalMethod?.Method.Module.Assembly.GetName(); var originalAssembly = originalAssemblyName is not null ? assemblies.FirstOrDefault(x => x.Id.Equals(originalAssemblyName)) : null; // Do not reverse engineer copyrighted or flagged original assemblies - static bool IsProtected(AssemblyModel? assembly) => assembly is not null && - ((assembly.Type & AssemblyType.GameCore) != 0 || - (assembly.Type & AssemblyType.GameModule) != 0 || - (assembly.Type & AssemblyType.ProtectedFromDisassembly) != 0); + static bool IsProtected(AssemblyModel? assembly) + { + if (assembly is null) return false; + + if ((assembly.Type & AssemblyType.ProtectedFromDisassembly) != 0) return true; + + if ((assembly.Type & AssemblyType.AllowedDisassembly) != 0) return false; + + return (assembly.Type & AssemblyType.GameCore) != 0 || (assembly.Type & AssemblyType.GameModule) != 0; + } var skipDisassemblyForOriginal = IsProtected(originalAssembly); var skipDisassemblyForExecuting = IsProtected(originalAssembly) || IsProtected(executingAssembly); + var nativeInstructions = MethodDecompiler.DecompileNativeCode(entry.NativeCodePtr, entry.NativeOffset); + var originalDecompiled = entry.OriginalMethod != null ? MethodDecompiler.DecompileMethod(entry.OriginalMethod.Method, null, skipDisassemblyForOriginal, assemblyUtilities.GetAssemblyStream, assemblyUtilities.GetPdbStream, httpUtilities.GetStringFromUrl) : null; + var executingDecompiled = MethodDecompiler.DecompileMethod(entry.Method, entry.ILOffset, skipDisassemblyForExecuting, assemblyUtilities.GetAssemblyStream, assemblyUtilities.GetPdbStream, httpUtilities.GetStringFromUrl); enhancedStacktraceFrameModels.Add(new() { FrameDescription = entry.StackFrameDescription, @@ -107,13 +117,53 @@ static bool IsProtected(AssemblyModel? assembly) => assembly is not null && LoaderPluginId = entry.LoaderPluginInfo?.Id, MethodDeclaredTypeName = entry.Method.DeclaringType?.FullName, MethodName = entry.Method.Name, - MethodFullDescription = entry.Method.FullDescription(), + MethodFullDescription = entry.Method.FullDescription().ToString(), MethodParameters = entry.Method.GetParameters().Select(x => x.ParameterType.FullName ?? string.Empty).ToArray(), - NativeInstructions = entry.NativeInstructions, - ILInstructions = entry.ILInstructions, - CSharpILMixedInstructions = skipDisassemblyForExecuting ? [] : entry.CSharpILMixedInstructions, - CSharpInstructions = skipDisassemblyForExecuting ? [] : entry.CSharpInstructions, - AdditionalMetadata = Array.Empty(), + NativeInstructions = new() + { + Instructions = nativeInstructions.Code, + Highlight = nativeInstructions.Highlight is null ? null : new() + { + StartLine = nativeInstructions.Highlight.StartLine, + StartColumn = nativeInstructions.Highlight.StartColumn, + EndLine = nativeInstructions.Highlight.EndLine, + EndColumn = nativeInstructions.Highlight.EndColumn, + } + }, + ILInstructions = new() + { + Instructions = executingDecompiled.IL.Code, + Highlight = executingDecompiled.IL.Highlight is null ? null : new() + { + StartLine = executingDecompiled.IL.Highlight.StartLine, + StartColumn = executingDecompiled.IL.Highlight.StartColumn, + EndLine = executingDecompiled.IL.Highlight.EndLine, + EndColumn = executingDecompiled.IL.Highlight.EndColumn, + }, + }, + ILMixedInstructions = new() + { + Instructions = executingDecompiled.ILMixed.Code, + Highlight = executingDecompiled.ILMixed.Highlight is null ? null : new() + { + StartLine = executingDecompiled.ILMixed.Highlight.StartLine, + StartColumn = executingDecompiled.ILMixed.Highlight.StartColumn, + EndLine = executingDecompiled.ILMixed.Highlight.EndLine, + EndColumn = executingDecompiled.ILMixed.Highlight.EndColumn, + }, + }, + CSharpInstructions = new() + { + Instructions = executingDecompiled.CSharp.Code, + Highlight = executingDecompiled.CSharp.Highlight is null ? null : new() + { + StartLine = executingDecompiled.CSharp.Highlight.StartLine, + StartColumn = executingDecompiled.CSharp.Highlight.StartColumn, + EndLine = executingDecompiled.CSharp.Highlight.EndLine, + EndColumn = executingDecompiled.CSharp.Highlight.EndColumn, + }, + }, + AdditionalMetadata = [], }, OriginalMethod = entry.OriginalMethod is not null ? new() { @@ -122,17 +172,29 @@ static bool IsProtected(AssemblyModel? assembly) => assembly is not null && LoaderPluginId = entry.OriginalMethod.LoaderPluginInfo?.Id, MethodDeclaredTypeName = entry.OriginalMethod.Method.DeclaringType?.FullName, MethodName = entry.OriginalMethod.Method.Name, - MethodFullDescription = entry.OriginalMethod.Method.FullDescription(), + MethodFullDescription = entry.OriginalMethod.Method.FullDescription().ToString(), MethodParameters = entry.OriginalMethod.Method.GetParameters().Select(x => x.ParameterType.FullName ?? string.Empty).ToArray(), - ILInstructions = entry.OriginalMethod.ILInstructions, - CSharpILMixedInstructions = skipDisassemblyForOriginal ? [] : entry.OriginalMethod.CSharpILMixedInstructions, - CSharpInstructions = skipDisassemblyForOriginal ? [] : entry.OriginalMethod.CSharpInstructions, - AdditionalMetadata = Array.Empty() + ILInstructions = originalDecompiled is null ? null : new() + { + Instructions = originalDecompiled.IL.Code, + Highlight = null, + }, + ILMixedInstructions = originalDecompiled is null ? null : new() + { + Instructions = originalDecompiled.ILMixed.Code, + Highlight = null, + }, + CSharpInstructions = originalDecompiled is null ? null : new() + { + Instructions = originalDecompiled.CSharp.Code, + Highlight = null, + }, + AdditionalMetadata = [], } : null, - PatchMethods = methods, + PatchMethods = patches, ILOffset = entry.ILOffset, NativeOffset = entry.NativeOffset, - AdditionalMetadata = Array.Empty(), + AdditionalMetadata = [], }); } } @@ -142,32 +204,34 @@ static bool IsProtected(AssemblyModel? assembly) => assembly is not null && /// /// Returns the /// - public static List GetInvolvedModules(CrashReportInfo crashReport) + public static List GetInvolvedModules(List enhancedStacktrace) { var involvedModels = new List(); - foreach (var stacktraces in crashReport.FilteredStacktrace.GroupBy(m => m.ModuleInfo)) + foreach (var stacktraces in enhancedStacktrace.Where(x => !string.IsNullOrEmpty(x.ExecutingMethod.ModuleId)).GroupBy(m => m.ExecutingMethod.ModuleId!)) { - if (stacktraces.Key is { } module) + foreach (var stacktrace in stacktraces) { involvedModels.Add(new() { - ModuleOrLoaderPluginId = module.Id, - EnhancedStacktraceFrameName = stacktraces.Last().StackFrameDescription, - AdditionalMetadata = Array.Empty(), + ModuleOrLoaderPluginId = stacktraces.Key, + EnhancedStacktraceFrameName = stacktrace.FrameDescription, + Type = InvolvedModuleOrPluginType.Direct, + AdditionalMetadata = [], }); } } - foreach (var stacktrace in crashReport.FilteredStacktrace) + foreach (var stacktrace in enhancedStacktrace) { - foreach (var patch in stacktrace.PatchMethods) + foreach (var patch in stacktrace.PatchMethods.Where(x => !string.IsNullOrEmpty(x.ModuleId))) { - if (patch.ModuleInfo is null) continue; + if (involvedModels.Any(x => x.EnhancedStacktraceFrameName == stacktrace.FrameDescription)) continue; involvedModels.Add(new() { - ModuleOrLoaderPluginId = patch.ModuleInfo.Id, - EnhancedStacktraceFrameName = stacktrace.StackFrameDescription, - AdditionalMetadata = Array.Empty(), + ModuleOrLoaderPluginId = patch.ModuleId!, + EnhancedStacktraceFrameName = stacktrace.FrameDescription, + Type = InvolvedModuleOrPluginType.Patch, + AdditionalMetadata = [], }); } } @@ -177,32 +241,35 @@ public static List GetInvolvedModules(CrashReportIn /// /// Returns the /// - public static List GetInvolvedPlugins(CrashReportInfo crashReport) + public static List GetInvolvedPlugins(List enhancedStacktrace) { var involvedPluginModels = new List(); - foreach (var stacktraces in crashReport.FilteredStacktrace.GroupBy(m => m.LoaderPluginInfo)) + foreach (var stacktraces in enhancedStacktrace.GroupBy(m => m.ExecutingMethod.LoaderPluginId)) { - if (stacktraces.Key is { } loaderPlugin) + if (stacktraces.Key is { } loaderPluginId && !string.IsNullOrEmpty(loaderPluginId)) { involvedPluginModels.Add(new() { - ModuleOrLoaderPluginId = loaderPlugin.Id, - EnhancedStacktraceFrameName = stacktraces.Last().StackFrameDescription, - AdditionalMetadata = Array.Empty(), + ModuleOrLoaderPluginId = loaderPluginId, + EnhancedStacktraceFrameName = stacktraces.Last().FrameDescription, + Type = InvolvedModuleOrPluginType.Direct, + AdditionalMetadata = [], }); } } - foreach (var stacktrace in crashReport.FilteredStacktrace) + foreach (var stacktrace in enhancedStacktrace) { foreach (var patch in stacktrace.PatchMethods) { - if (patch.LoaderPluginInfo is null) continue; + if (string.IsNullOrEmpty(patch.LoaderPluginId)) continue; + if (involvedPluginModels.Any(x => x.EnhancedStacktraceFrameName == stacktrace.FrameDescription)) continue; involvedPluginModels.Add(new() { - ModuleOrLoaderPluginId = patch.LoaderPluginInfo.Id, - EnhancedStacktraceFrameName = stacktrace.StackFrameDescription, - AdditionalMetadata = Array.Empty(), + ModuleOrLoaderPluginId = patch.LoaderPluginId!, + EnhancedStacktraceFrameName = stacktrace.FrameDescription, + Type = InvolvedModuleOrPluginType.Patch, + AdditionalMetadata = [], }); } } @@ -239,11 +306,8 @@ static bool IsProtectedFromDisassembly(Assembly assembly) var assemblyModels = new List(crashReport.AvailableAssemblies.Count); var systemAssemblyDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location); - foreach (var kv in crashReport.AvailableAssemblies) + foreach (var (assemblyName, assembly) in crashReport.AvailableAssemblies) { - var assemblyName = kv.Key; - var assembly = kv.Value; - var type = AssemblyType.Unclassified; // TODO: On unity the system folder is the unity root folder. @@ -281,170 +345,80 @@ static bool IsProtectedFromDisassembly(Assembly assembly) Hash = assembly.IsDynamic || string.IsNullOrWhiteSpace(assembly.Location) || !File.Exists(assembly.Location) ? "NONE" : CrashReportUtils.CalculateMD5(assembly.Location), AnonymizedPath = assembly.IsDynamic ? "DYNAMIC" : string.IsNullOrWhiteSpace(assembly.Location) ? "EMPTY" : !File.Exists(assembly.Location) ? "MISSING" : anonymizedPath, Type = type, - ImportedTypeReferences = (type & AssemblyType.System) == 0 - ? crashReport.ImportedTypeReferences.TryGetValue(assemblyName, out var values) ? values.Select(x => new AssemblyImportedTypeReferenceModel - { - Namespace = x.Namespace, - Name = x.Name, - FullName = x.FullName, - }).ToArray() : [] - : [], - ImportedAssemblyReferences = (type & AssemblyType.System) == 0 - ? assembly.GetReferencedAssemblies().Select(AssemblyImportedReferenceModelExtensions.Create).ToArray() - : [], - AdditionalMetadata = Array.Empty(), + AdditionalMetadata = [], }); } return assemblyModels; } - - /* - /// - /// Returns the - /// - public static List GetHarmonyPatches(CrashReportInfo crashReport, IReadOnlyCollection assemblies, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - var builder = new List(crashReport.LoadedHarmonyPatches.Count); - - static void AppendPatches(ICollection builder, HarmonyPatchType type, IEnumerable patches, IReadOnlyCollection assemblies, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - foreach (var patch in patches) - { - var assemblyId = patch.PatchMethod.DeclaringType?.Assembly.GetName() is { } asmName ? AssemblyIdModel.FromAssembly(asmName) : null; - var module = moduleProvider.GetModuleByType(patch.PatchMethod.DeclaringType); - var loaderPlugin = loaderPluginProvider.GetLoaderPluginByType(patch.PatchMethod.DeclaringType); - - builder.Add(new() - { - Type = type, - AssemblyId = assemblyId, - ModuleId = module?.Id, - LoaderPluginId = loaderPlugin?.Id, - Owner = patch.Owner, - Namespace = $"{patch.PatchMethod.DeclaringType!.FullName}.{patch.PatchMethod.Name}", - Index = patch.Index, - Priority = patch.Priority, - Before = patch.Before, - After = patch.After, - AdditionalMetadata = Array.Empty(), - }); - } - } - - foreach (var kv in crashReport.LoadedHarmonyPatches) - { - var originalMethod = kv.Key; - var patches = kv.Value; - - var patchBuilder = new List(patches.Prefixes.Count + patches.Postfixes.Count + patches.Finalizers.Count + patches.Transpilers.Count); - - AppendPatches(patchBuilder, HarmonyPatchType.Prefix, patches.Prefixes, assemblies, moduleProvider, loaderPluginProvider); - AppendPatches(patchBuilder, HarmonyPatchType.Postfix, patches.Postfixes, assemblies, moduleProvider, loaderPluginProvider); - AppendPatches(patchBuilder, HarmonyPatchType.Finalizer, patches.Finalizers, assemblies, moduleProvider, loaderPluginProvider); - AppendPatches(patchBuilder, HarmonyPatchType.Transpiler, patches.Transpilers, assemblies, moduleProvider, loaderPluginProvider); - - if (patchBuilder.Count > 0) - { - builder.Add(new() - { - OriginalMethodDeclaredTypeName = originalMethod.DeclaringType?.FullName, - OriginalMethodName = originalMethod.Name, - Patches = patchBuilder, - AdditionalMetadata = Array.Empty(), - }); - } - } - - return builder; - } /// - /// Returns the + /// Returns the /// - public static List GetMonoModDetours(CrashReportInfo crashReport, IReadOnlyCollection assemblies, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) + public static List GetRuntimePatches(CrashReportInfo crashReport, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) { - var builder = new List(crashReport.LoadedMonoModPatches.Count); + var builder = new List(crashReport.LoadedManagedRuntimePatches.Count); - static void AppendPatches(ICollection builder, MonoModPatchModelType type, IEnumerable patches, IReadOnlyCollection assemblies, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) + foreach (var (originalMethod, patches) in crashReport.LoadedManagedRuntimePatches) { + var patchesBuilder = new List(patches.Count); foreach (var patch in patches) { - var assemblyId = patch.Method.DeclaringType?.Assembly.GetName() is { } asmName ? AssemblyIdModel.FromAssembly(asmName) : null; - var module = moduleProvider.GetModuleByType(patch.Method.DeclaringType); - var loaderPlugin = loaderPluginProvider.GetLoaderPluginByType(patch.Method.DeclaringType); + var method = patch.Patch; + if (method.DeclaringType is not { } declaringType) + continue; - builder.Add(new() + var assemblyId = AssemblyIdModel.FromAssembly(patch.Patch.Module.Assembly.GetName()); + var moduleInfo = moduleProvider.GetModuleByType(declaringType); + var loaderPluginInfo = loaderPluginProvider.GetLoaderPluginByType(declaringType); + + //if (moduleInfo is not null || loaderPluginInfo is not null) + patchesBuilder.Add(new() { - Id = patch.Id, - Namespace = $"{patch.Method.DeclaringType!.FullName}.{patch.Method.Name}", - Type = type, - IsActive = false, + ModuleId = moduleInfo?.Id, + LoaderPluginId = loaderPluginInfo?.Id, AssemblyId = assemblyId, - ModuleId = module?.Id, - LoaderPluginId = loaderPlugin?.Id, - Index = patch.Index, - MaxIndex = patch.MaxIndex, - GlobalIndex = patch.GlobalIndex, - Priority = patch.Priority, - SubPriority = patch.SubPriority, - Before = patch.Before, - After = patch.After, - AdditionalMetadata = Array.Empty(), + Provider = patch.PatchProvider, + Type = patch.PatchType, + FullName = $"{method.DeclaringType.FullName}.{method.Name}", + AdditionalMetadata = patch.AdditionalMetadata, }); } - } - - foreach (var kv in crashReport.LoadedMonoModPatches) - { - var originalMethod = kv.Key; - var patches = kv.Value; - - var detoursBuilder = new List(patches.Detours.Count); - - AppendPatches(detoursBuilder, MonoModPatchModelType.Detour, patches.Detours, assemblies, moduleProvider, loaderPluginProvider); - AppendPatches(detoursBuilder, MonoModPatchModelType.ILHook, patches.ILHooks, assemblies, moduleProvider, loaderPluginProvider); - if (detoursBuilder.Count > 0) + if (patchesBuilder.Count > 0) { builder.Add(new() { OriginalMethodDeclaredTypeName = originalMethod.DeclaringType?.FullName, OriginalMethodName = originalMethod.Name, - Detours = detoursBuilder, - AdditionalMetadata = Array.Empty(), + Patches = patchesBuilder, + AdditionalMetadata = [], }); } } - return builder; - } - */ - - public static List GetRuntimePatches(CrashReportInfo crashReport, IReadOnlyCollection assemblies, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - var builder = new List(crashReport.LoadedRuntimePatches.Count); - - foreach (var kv in crashReport.LoadedRuntimePatches) + foreach (var (originalMethodPtr, patches) in crashReport.LoadedNativeRuntimePatches) { - var originalMethod = kv.Key; - var patches = kv.Value; - - var patchesBuilder = new List(crashReport.LoadedRuntimePatches.Count); + var patchesBuilder = new List(patches.Count); foreach (var patch in patches) { - var assemblyId = patch.Patch.DeclaringType?.Assembly.GetName() is { } asmName ? AssemblyIdModel.FromAssembly(asmName) : null; - var module = moduleProvider.GetModuleByType(patch.Patch.DeclaringType); - var loaderPlugin = loaderPluginProvider.GetLoaderPluginByType(patch.Patch.DeclaringType); + var method = patch.Patch; + if (method.DeclaringType is not { } declaringType) + continue; + + var assemblyId = AssemblyIdModel.FromAssembly(patch.Patch.Module.Assembly.GetName()); + var moduleInfo = moduleProvider.GetModuleByType(declaringType); + var loaderPluginInfo = loaderPluginProvider.GetLoaderPluginByType(declaringType); + //if (moduleInfo is not null || loaderPluginInfo is not null) patchesBuilder.Add(new() { + ModuleId = moduleInfo?.Id, + LoaderPluginId = loaderPluginInfo?.Id, AssemblyId = assemblyId, - ModuleId = module?.Id, - LoaderPluginId = loaderPlugin?.Id, Provider = patch.PatchProvider, Type = patch.PatchType, - FullName = patch.Patch.DeclaringType is not null ? $"{patch.Patch.DeclaringType.FullName}.{patch.Patch.Name}" : patch.Patch.Name, + FullName = $"{method.DeclaringType.FullName}.{method.Name}", AdditionalMetadata = patch.AdditionalMetadata, }); } @@ -453,10 +427,10 @@ public static List GetRuntimePatches(CrashReportInfo crashR { builder.Add(new() { - OriginalMethodDeclaredTypeName = originalMethod.DeclaringType?.FullName, - OriginalMethodName = originalMethod.Name, + OriginalMethodDeclaredTypeName = null, + OriginalMethodName = originalMethodPtr.ToString(), Patches = patchesBuilder, - AdditionalMetadata = Array.Empty(), + AdditionalMetadata = [], }); } } diff --git a/src/BUTR.CrashReport/Utils/CrashReportUtils.cs b/src/BUTR.CrashReport/Utils/CrashReportUtils.cs index 73c4511..e3e5b3a 100644 --- a/src/BUTR.CrashReport/Utils/CrashReportUtils.cs +++ b/src/BUTR.CrashReport/Utils/CrashReportUtils.cs @@ -9,8 +9,6 @@ using System.Reflection; using System.Security.Cryptography; -using static BUTR.CrashReport.Decompilers.Utils.MethodDecompiler; - namespace BUTR.CrashReport.Utils; /// @@ -18,99 +16,8 @@ namespace BUTR.CrashReport.Utils; /// public static class CrashReportUtils { - /// - /// - /// - public record StackframePatchData - { - /// - /// - /// - public required MethodBase? OriginalMethod { get; set; } - - /// - /// - /// - public required MethodInfo? ExecutingMethod { get; set; } - - /// - /// - /// - public required List Patches { get; set; } - - public required int ILOffset { get; set; } - - public required int NativeILOffset { get; set; } - - public required IntPtr NativeCodePtr { get; set; } - - /// - /// Deconstructs the object. - /// - public void Deconstruct(out MethodBase? original, out MethodInfo? replacement, out List patches, out int ilOffset, out int nativeILOffset, out IntPtr nativeCodePtr) - { - original = OriginalMethod; - replacement = ExecutingMethod; - patches = Patches; - ilOffset = ILOffset; - nativeILOffset = NativeILOffset; - nativeCodePtr = NativeCodePtr; - } - } - - /* - /// - /// Gets all involved modules in the exception stacktrace. - /// - public static IEnumerable<(MonoModPatch, IModuleInfo?, ILoaderPluginInfo?)> GetMonoModPatchMethods(MonoModPatches? patches, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - if (patches is null) - yield break; - - var patchMethods = patches.Detours.OrderBy(t => t.Priority) - .Concat(patches.ILHooks.OrderBy(t => t.Priority)); - - foreach (var patch in patchMethods) - { - var method = patch.Method; - if (method.DeclaringType is not { } declaringType) - continue; - - var moduleInfo = moduleProvider.GetModuleByType(declaringType); - var loaderPluginInfo = loaderPluginProvider.GetLoaderPluginByType(declaringType); - - if (moduleInfo is not null || loaderPluginInfo is not null) - yield return (patch, moduleInfo, loaderPluginInfo); - } - } - - /// - /// Gets all involved modules in the exception stacktrace. - /// - public static IEnumerable<(HarmonyPatch, IModuleInfo?, ILoaderPluginInfo?)> GetHarmonyPatchMethods(HarmonyPatches? patches, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - if (patches is null) - yield break; - - var patchMethods = patches.Prefixes.OrderBy(t => t.Priority) - .Concat(patches.Postfixes.OrderBy(t => t.Priority)) - .Concat(patches.Transpilers.OrderBy(t => t.Priority)) - .Concat(patches.Finalizers.OrderBy(t => t.Priority)); - - foreach (var patch in patchMethods) - { - var method = patch.PatchMethod; - if (method.DeclaringType is not { } declaringType) - continue; - - var moduleInfo = moduleProvider.GetModuleByType(declaringType); - var loaderPluginInfo = loaderPluginProvider.GetLoaderPluginByType(declaringType); - - if (moduleInfo is not null || loaderPluginInfo is not null) - yield return (patch, moduleInfo, loaderPluginInfo); - } - } - */ + private static readonly char[] ParenthesisSeparator = ['(']; + private static readonly string[] PathSeparator = ["_Patch"]; /// /// Gets the module info if the method is from a mod. @@ -131,8 +38,8 @@ public void Deconstruct(out MethodBase? original, out MethodInfo? replacement, o // This is not possible if (method.DeclaringType is null && method.Name.Contains('.')) { - var methodName = method.Name.Split('(')[0]; - var patchPostfix = methodName.Split(["_Patch"], StringSplitOptions.None); + var methodName = method.Name.Split(ParenthesisSeparator)[0]; + var patchPostfix = methodName.Split(PathSeparator, StringSplitOptions.None); if (!patchPostfix.Last().All(char.IsDigit)) return null; @@ -174,8 +81,8 @@ public void Deconstruct(out MethodBase? original, out MethodInfo? replacement, o // This is not possible if (method.DeclaringType is null && method.Name.Contains('.')) { - var methodName = method.Name.Split('(')[0]; - var patchPostfix = methodName.Split(["_Patch"], StringSplitOptions.None); + var methodName = method.Name.Split(ParenthesisSeparator)[0]; + var patchPostfix = methodName.Split(PathSeparator, StringSplitOptions.None); if (!patchPostfix.Last().All(char.IsDigit)) return null; @@ -197,91 +104,15 @@ public void Deconstruct(out MethodBase? original, out MethodInfo? replacement, o return null; } - /* - /// - /// Gets the Harmony data from the stackframe. - /// - public static StackframePatchData GetPatchData(StackFrame frame, IPatchProvider patchProvider, IMonoModProvider monoModProvider, IHarmonyProvider harmonyProvider, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - var patches = new List(); - - var executingMethodMonoMod = monoModProvider.GetExecutingMethod(frame); - var originalMethodMonoMod = monoModProvider.GetOriginalMethod(frame); - var ilOffsetMonoMod = frame.GetILOffset(); - var nativeILOffsetMonoMod = frame.GetNativeOffset(); - var nativeCodePtrMonoMod = executingMethodMonoMod is not null ? monoModProvider.GetNativeMethodBody(executingMethodMonoMod) : IntPtr.Zero; - var monoModPatches = monoModProvider.GetPatchInfo(frame); - foreach (var (patch, moduleInfo, loaderPluginInfo) in GetMonoModPatchMethods(monoModPatches, moduleProvider, loaderPluginProvider)) - { - patches.Add(new MethodEntryMonoMod - { - Patch = patch, - Method = patch.Method, - ModuleInfo = moduleInfo, - LoaderPluginInfo = loaderPluginInfo, - ILInstructions = DecompileILCode(patch.Method), - CSharpILMixedInstructions = DecompileILWithCSharpCode(patch.Method), - CSharpInstructions = DecompileCSharpCode(patch.Method), - }); - } - - var executingMethodHarmony = harmonyProvider.GetExecutingMethod(frame); - var originalMethodHarmony = harmonyProvider.GetOriginalMethod(frame); - var ilOffsetHarmony = frame.GetILOffset(); - var nativeILOffsetHarmony = frame.GetNativeOffset(); - var nativeCodePtrHarmony = executingMethodHarmony is not null ? harmonyProvider.GetNativeMethodBody(executingMethodHarmony) : IntPtr.Zero; - var harmonyPatches = harmonyProvider.GetPatchInfo(frame); - foreach (var (patch, moduleInfo, loaderPluginInfo) in GetHarmonyPatchMethods(harmonyPatches, moduleProvider, loaderPluginProvider)) - { - patches.Add(new MethodEntryHarmony - { - Patch = patch, - Method = patch.PatchMethod, - ModuleInfo = moduleInfo, - LoaderPluginInfo = loaderPluginInfo, - ILInstructions = DecompileILCode(patch.PatchMethod), - CSharpILMixedInstructions = DecompileILWithCSharpCode(patch.PatchMethod), - CSharpInstructions = DecompileCSharpCode(patch.PatchMethod), - }); - } - - return new() - { - OriginalMethod = originalMethodMonoMod ?? originalMethodHarmony, - ExecutingMethod = executingMethodMonoMod ?? executingMethodHarmony, - Patches = patches, - }; - } - */ - - public static IEnumerable<(RuntimePatch, IModuleInfo?, ILoaderPluginInfo?)> GetPatchMethods(IList patches, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider) - { - if (patches is null) - yield break; - - foreach (var patch in patches) - { - var method = patch.Patch; - if (method.DeclaringType is not { } declaringType) - continue; - - var moduleInfo = moduleProvider.GetModuleByType(declaringType); - var loaderPluginInfo = loaderPluginProvider.GetLoaderPluginByType(declaringType); - - if (moduleInfo is not null || loaderPluginInfo is not null) - yield return (patch, moduleInfo, loaderPluginInfo); - } - } - /// /// Gets all involved modules in the exception stacktrace. /// - public static IEnumerable GetAllInvolvedModules(Exception ex, ICollection assemblies, IAssemblyUtilities assemblyUtilities, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider, IPatchProvider patchProvider/*, IMonoModProvider monoModProvider, IHarmonyProvider harmonyProvider*/) + public static IEnumerable GetEnhancedStacktrace(Exception ex, ICollection assemblies, IAssemblyUtilities assemblyUtilities, IModuleProvider moduleProvider, ILoaderPluginProvider loaderPluginProvider, IRuntimePatchProvider runtimePatchProvider) { var inner = ex.InnerException; if (inner is not null) { - foreach (var modInfo in GetAllInvolvedModules(inner, assemblies, assemblyUtilities, moduleProvider, loaderPluginProvider, patchProvider/*, monoModProvider, harmonyProvider*/)) + foreach (var modInfo in GetEnhancedStacktrace(inner, assemblies, assemblyUtilities, moduleProvider, loaderPluginProvider, runtimePatchProvider)) yield return modInfo; } @@ -290,56 +121,59 @@ public static IEnumerable GetAllInvolvedModules(Exception ex, I { if (!frame.HasMethod()) continue; - //var (originalMethod, executingMethod, patches, ilOffset, nativeILOffset, nativeCodePtr) = GetPatchData(frame, patchProvider/*, monoModProvider, harmonyProvider*/, moduleProvider, loaderPluginProvider); - if (patchProvider.GetPatches(frame) is not { } data) continue; - - var (originalMethod, executingMethod, patches, ilOffset, nativeILOffset, nativeCodePtr) = data; - + var method = frame.GetMethod(); + var ilOffset = frame.GetILOffset(); + var nativeOffset = frame.GetNativeOffset(); + + if (runtimePatchProvider.GetPatchInfo(method) is not { } data) continue; + var (originalMethod, executingMethod, patches, nativeCodePtr) = data; + yield return new() { Method = executingMethod!, OriginalMethod = originalMethod is not null && originalMethod != executingMethod ? new() { Method = originalMethod, + AssemblyId = AssemblyIdModel.FromAssembly(originalMethod.Module.Assembly.GetName()), ModuleInfo = GetModuleInfoIfMod(originalMethod, assemblies, assemblyUtilities, moduleProvider), LoaderPluginInfo = GetLoaderPluginIfMod(originalMethod, assemblies, assemblyUtilities, loaderPluginProvider), - ILInstructions = DecompileILCode(originalMethod), - CSharpILMixedInstructions = DecompileILWithCSharpCode(originalMethod), - CSharpInstructions = DecompileCSharpCode(originalMethod), } : null, ModuleInfo = GetModuleInfoIfMod(executingMethod, assemblies, assemblyUtilities, moduleProvider), LoaderPluginInfo = GetLoaderPluginIfMod(executingMethod, assemblies, assemblyUtilities, loaderPluginProvider), - ILOffset = ilOffset != StackFrame.OFFSET_UNKNOWN ? ilOffset : null, - NativeOffset = nativeILOffset != StackFrame.OFFSET_UNKNOWN ? nativeILOffset : null, + ILOffset = ilOffset, + NativeOffset = nativeOffset, + NativeCodePtr = nativeCodePtr, StackFrameDescription = frame.ToString(), - NativeInstructions = DecompileNativeCode(nativeCodePtr, nativeILOffset), - ILInstructions = DecompileILCode(executingMethod), - CSharpILMixedInstructions = DecompileILWithCSharpCode(executingMethod), - CSharpInstructions = DecompileCSharpCode(executingMethod), - PatchMethods = GetPatchMethods(patches, moduleProvider, loaderPluginProvider).Select(x => + PatchMethods = patches.Select(patch => { - var (patch, moduleInfo, loaderPluginInfo) = x; - return new MethodEntryRuntimePatch + var assemblyId = AssemblyIdModel.FromAssembly(patch.Patch.Module.Assembly.GetName()); + var moduleInfo = moduleProvider.GetModuleByType(patch.Patch.DeclaringType); + var loaderPluginInfo = loaderPluginProvider.GetLoaderPluginByType(patch.Patch.DeclaringType); + return new MethodRuntimePatchEntry { Patch = patch, - Method = patch.Original, + Method = patch.Patch, + AssemblyId = assemblyId, ModuleInfo = moduleInfo, LoaderPluginInfo = loaderPluginInfo, - ILInstructions = DecompileILCode(patch.Patch), - CSharpILMixedInstructions = DecompileILWithCSharpCode(patch.Patch), - CSharpInstructions = DecompileCSharpCode(patch.Patch), }; - }).ToArray(), + }).ToArray(), }; } } + /// + /// Get the MD5 hash of a file. + /// public static string CalculateMD5(string filename) { using var stream = File.OpenRead(filename); return CalculateMD5(stream); } + /// + /// Get the MD5 hash of a stream. + /// public static string CalculateMD5(Stream stream) { using var md5 = MD5.Create(); diff --git a/src/BUTR.CrashReport/Utils/HarmonyUtils.cs b/src/BUTR.CrashReport/Utils/HarmonyUtils.cs deleted file mode 100644 index d7ebc9b..0000000 --- a/src/BUTR.CrashReport/Utils/HarmonyUtils.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace BUTR.CrashReport.Utils; - -internal static class HarmonyUtils -{ - public static Type? GetReturnedType(MethodBase? methodOrConstructor) => methodOrConstructor switch - { - ConstructorInfo => typeof(void), - MethodInfo method => method.ReturnType, - _ => null - }; - - public static string Join(this IEnumerable enumeration, Func? converter = null, string delimiter = ", ") - { - converter ??= t => t!.ToString(); - return enumeration.Aggregate("", (prev, curr) => prev + (prev.Length > 0 ? delimiter : "") + converter(curr)); - } - - public static string FullDescription(this Type? type) - { - if (type is null) - return "null"; - - var ns = type.Namespace; - if (string.IsNullOrEmpty(ns) is false) ns += "."; - var result = ns + type.Name; - - if (type.IsGenericType) - { - result += "<"; - var subTypes = type.GetGenericArguments(); - for (var i = 0; i < subTypes.Length; i++) - { - if (!result.EndsWith("<", StringComparison.Ordinal)) - result += ", "; - result += subTypes[i].FullDescription(); - } - result += ">"; - } - return result; - } - - public static string FullDescription(this MethodBase? member) - { - if (member is null) return "null"; - var returnType = GetReturnedType(member); - - var result = new StringBuilder(); - if (member.IsStatic) _ = result.Append("static "); - if (member.IsAbstract) _ = result.Append("abstract "); - if (member.IsVirtual) _ = result.Append("virtual "); - _ = result.Append($"{returnType.FullDescription()} "); - if (member.DeclaringType is not null) - _ = result.Append($"{member.DeclaringType.FullDescription()}::"); - var parameterString = member.GetParameters().Join(p => $"{p.ParameterType.FullDescription()} {p.Name}"); - _ = result.Append($"{member.Name}({parameterString})"); - return result.ToString(); - } -} \ No newline at end of file diff --git a/src/BUTR.CrashReport/Utils/NativeModuleUtils.cs b/src/BUTR.CrashReport/Utils/NativeModuleUtils.cs index 4fa34f6..8afd824 100644 --- a/src/BUTR.CrashReport/Utils/NativeModuleUtils.cs +++ b/src/BUTR.CrashReport/Utils/NativeModuleUtils.cs @@ -27,11 +27,13 @@ public static List CollectModules(Process process, IPathAnonymizer { using var fs = File.OpenRead(x.FileName); - var signature = new byte[4]; - _ = fs.Read(signature, 0, signature.Length); - fs.Seek(0, SeekOrigin.Begin); + Span signature = stackalloc byte[4]; + signature[0] = (byte) fs.ReadByte(); + signature[1] = (byte) fs.ReadByte(); + signature[2] = (byte) fs.ReadByte(); + signature[3] = (byte) fs.ReadByte(); - if (signature.AsSpan(0, PEHeader.Length).SequenceEqual(PEHeader)) + if (signature.Slice(0, PEHeader.Length).SequenceEqual(PEHeader)) { using var reader = new PEReader(fs, PEStreamOptions.LeaveOpen); if (reader.HasMetadata) return null; @@ -47,8 +49,7 @@ public static List CollectModules(Process process, IPathAnonymizer var version = x.FileVersionInfo.FileVersion ?? x.FileVersionInfo.ProductVersion; var path = x.FileName; - var anonymizedPath = string.Empty; - if (!pathAnonymizer.TryHandlePath(path, out anonymizedPath)) + if (!pathAnonymizer.TryHandlePath(path, out var anonymizedPath)) anonymizedPath = Anonymizer.AnonymizePath(path); return new NativeModule(x.ModuleName, anonymizedPath, version, arch, (uint) fs.Length, x.BaseAddress, (uint) x.ModuleMemorySize, hash); @@ -59,9 +60,9 @@ public static List CollectModules(Process process, IPathAnonymizer } }).OfType().ToList(); - private static NativeAssemblyArchitectureType GetArchitecture(byte[] signature, Stream stream) + private static NativeAssemblyArchitectureType GetArchitecture(Span signature, Stream stream) { - if (signature.AsSpan(0, PEHeader.Length).SequenceEqual(PEHeader)) + if (signature.Slice(0, PEHeader.Length).SequenceEqual(PEHeader)) { using var reader = new PEReader(stream, PEStreamOptions.LeaveOpen); return reader.PEHeaders.CoffHeader.Machine switch @@ -73,7 +74,8 @@ private static NativeAssemblyArchitectureType GetArchitecture(byte[] signature, _ => NativeAssemblyArchitectureType.Unknown, }; } - if (signature.AsSpan(0, ElfMagic.Length).SequenceEqual(ElfMagic)) + + if (signature.Slice(0, ElfMagic.Length).SequenceEqual(ElfMagic)) { using var elf = ELFReader.Load(stream, false); return elf.Machine switch @@ -85,7 +87,8 @@ private static NativeAssemblyArchitectureType GetArchitecture(byte[] signature, _ => NativeAssemblyArchitectureType.Unknown, }; } - if (signature.AsSpan(0, MachOMagic.Length).SequenceEqual(MachOMagic) || signature.AsSpan(0, MachOMagic64.Length).SequenceEqual(MachOMagic64)) + + if (signature.Slice(0, MachOMagic.Length).SequenceEqual(MachOMagic) || signature.Slice(0, MachOMagic64.Length).SequenceEqual(MachOMagic64)) { var reader = MachOReader.Load(stream, false); return reader.Machine switch @@ -97,6 +100,7 @@ private static NativeAssemblyArchitectureType GetArchitecture(byte[] signature, _ => NativeAssemblyArchitectureType.Unknown, }; } + return NativeAssemblyArchitectureType.Unknown; } } \ No newline at end of file diff --git a/src/BUTR.CrashReport/Utils/TypeUtils.cs b/src/BUTR.CrashReport/Utils/TypeUtils.cs new file mode 100644 index 0000000..85295ce --- /dev/null +++ b/src/BUTR.CrashReport/Utils/TypeUtils.cs @@ -0,0 +1,72 @@ +using System; +using System.Reflection; +using System.Text; + +namespace BUTR.CrashReport.Utils; + +internal static class TypeUtils +{ + public static Type? GetReturnedType(MethodBase? methodOrConstructor) => methodOrConstructor switch + { + ConstructorInfo => typeof(void), + MethodInfo method => method.ReturnType, + _ => null, + }; + + public static StringBuilder FullDescription(this Type? type) + { + var sb = new StringBuilder(); + if (type is null) + { + sb.Append("null"); + return sb; + } + + if (!string.IsNullOrEmpty(type.Namespace)) + sb.Append(type.Namespace).Append('.'); + sb.Append(type.Name); + if (type.IsGenericType) + { + sb.Append('<'); + var subTypes = type.GetGenericArguments(); + for (var i = 0; i < subTypes.Length; i++) + { + if (sb[sb.Length - 1] != '<') + sb.Append(", "); + sb.Append(FullDescription(subTypes[i])); + } + sb.Append('>'); + } + return sb; + } + + public static StringBuilder FullDescription(this MethodBase? member) + { + var sb = new StringBuilder(); + if (member is null) + { + sb.Append("null"); + return sb; + } + + var returnType = GetReturnedType(member); + + var result = new StringBuilder(); + if (member.IsStatic) _ = result.Append("static "); + if (member.IsAbstract) _ = result.Append("abstract "); + if (member.IsVirtual) _ = result.Append("virtual "); + result.Append(FullDescription(returnType)).Append(' '); + if (member.DeclaringType is not null) + result.Append(FullDescription(member.DeclaringType)).Append("::"); + result.Append(member.Name).Append('('); + var parameters = member.GetParameters(); + for (var i = 0; i < parameters.Length; i++) + { + var parameter = parameters[i]; + result.Append(FullDescription(parameter.ParameterType)).Append(' ').Append(parameter.Name); + if (i < parameters.Length - 1) _ = result.Append(", "); + } + result.Append(')'); + return result; + } +} \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 78d3a96..adad186 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,5 @@ - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 583df8e..9555f39 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,5 +1,5 @@ - + diff --git a/src/ImGuiColorTextEditNet/ColorPalette.cs b/src/ImGuiColorTextEditNet/ColorPalette.cs new file mode 100644 index 0000000..6d69206 --- /dev/null +++ b/src/ImGuiColorTextEditNet/ColorPalette.cs @@ -0,0 +1,95 @@ +using BUTR.CrashReport.ImGui.Utils; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace ImGuiColorTextEditNet; + +public class ColorPalette +{ + private static readonly Dictionary> _perTypeNameToColorIndices = new(); + private static readonly Dictionary> _perTypeColorIndices = new(); + + private static readonly Dictionary> _colorIndices = new(); + + public static Span GetAll<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] TColorPalette>() where TColorPalette : ColorPalette + { + if (_colorIndices.TryGetValue(typeof(TColorPalette), out var colorIndices)) + return colorIndices.AsSpan(); + + var unorderedColorIndices = new List(); + var type = typeof(TColorPalette); +#pragma warning disable IL2059 + RuntimeHelpers.RunClassConstructor(typeof(TColorPalette).TypeHandle); +#pragma warning restore IL2059 + while (type != null) + { + if (_perTypeColorIndices.TryGetValue(type, out var perTypeColorIndices)) + { + foreach (var colorPalette in perTypeColorIndices) + unorderedColorIndices.Add(colorPalette); + } + type = type.BaseType; + } + + _colorIndices[typeof(TColorPalette)] = colorIndices = unorderedColorIndices.OrderBy(x => x.ColorIndex).ToList(); + return colorIndices.AsSpan(); + } + + public static implicit operator ushort(ColorPalette colorPalette) => colorPalette.ColorIndex; + + public static ColorPalette Default { get; } = new(nameof(Default)); + public static ColorPalette Background { get; } = new(nameof(Background)); + public static ColorPalette Cursor { get; } = new(nameof(Cursor)); + public static ColorPalette Selection { get; } = new(nameof(Selection)); + public static ColorPalette ExecutingLine { get; } = new(nameof(ExecutingLine)); + public static ColorPalette LineNumber { get; } = new(nameof(LineNumber)); + public static ColorPalette CurrentLineFill { get; } = new(nameof(CurrentLineFill)); + public static ColorPalette CurrentLineFillInactive { get; } = new(nameof(CurrentLineFillInactive)); + public static ColorPalette CurrentLineEdge { get; } = new(nameof(CurrentLineEdge)); + public static ColorPalette ErrorMarker { get; } = new(nameof(ErrorMarker)); + public static ColorPalette ErrorText { get; } = new(nameof(ErrorText)); + + + public ushort ColorIndex { get; } + + protected ColorPalette(string uniqueName) + { + if (!_perTypeNameToColorIndices.TryGetValue(GetType(), out var perTypeNameToColorIndices)) + _perTypeNameToColorIndices[GetType()] = perTypeNameToColorIndices = new(); + + int offset = perTypeNameToColorIndices.IndexOf(uniqueName); + if (offset == -1) + { + perTypeNameToColorIndices.Add(uniqueName); + offset = perTypeNameToColorIndices.Count - 1; + } + + int maxBaseColorIndex = 0; + if (_perTypeColorIndices.TryGetValue(GetType().BaseType ?? typeof(object), out var basePerTypeColorIndices)) + { + maxBaseColorIndex = basePerTypeColorIndices.Max(x => x.ColorIndex) + 1; + } + + ColorIndex = (ushort) (maxBaseColorIndex + offset); + + if (!_perTypeColorIndices.TryGetValue(GetType(), out var perTypeColorIndices)) + _perTypeColorIndices[GetType()] = perTypeColorIndices = []; + perTypeColorIndices.Add(this); + } + protected ColorPalette(ushort colorIndex) + { + ColorIndex = colorIndex; + + if (!_perTypeColorIndices.TryGetValue(GetType(), out var perTypeColorIndices)) + _perTypeColorIndices[GetType()] = perTypeColorIndices = []; + perTypeColorIndices.Add(this); + } + + public override int GetHashCode() => ColorIndex.GetHashCode(); + + public override bool Equals(object? obj) => obj is ColorPalette colorPalette && ColorIndex == colorPalette.ColorIndex; +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/ColorPaletteIndex.cs b/src/ImGuiColorTextEditNet/ColorPaletteIndex.cs new file mode 100644 index 0000000..005bcd4 --- /dev/null +++ b/src/ImGuiColorTextEditNet/ColorPaletteIndex.cs @@ -0,0 +1,51 @@ +using BUTR.CrashReport.ImGui.Utils; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace ImGuiColorTextEditNet; + +public class ColorPaletteIndexCreator +{ + public List<(ushort, Vector4)> Indices { get; init; } = new(); + + public Vector4 this[ColorPalette key] { set => Indices.Add((key.ColorIndex, value)); } + +} + +public class ColorPaletteIndex +{ + private readonly List _indices = new(); + + public int Length => _indices.Count; + + public ref readonly Vector4 this[ushort key] => ref IndicesAsSpan()[key]; + + public Vector4 this[ColorPalette key] + { + get => this[key.ColorIndex]; + set => _indices.Insert(key.ColorIndex, value); + } + + public Span IndicesAsSpan() => _indices.AsSpan(); + + public ColorPaletteIndex With(ColorPaletteIndexCreator colorPaletteIndex) + { + var maxValue = colorPaletteIndex.Indices.Max(x => x.Item1) + 1; + + var copy = new ColorPaletteIndex { _indices = { Capacity = maxValue } }; + + for (var i = 0; i < maxValue; i++) + copy._indices.Add(Vector4.Zero); + + for (var i = 0; i < _indices.Count; i++) + copy._indices[i] = _indices[i]; + + foreach (var (index, color) in colorPaletteIndex.Indices.OrderBy(x => x.Item1)) + copy._indices[index] = color; + + return copy; + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Coordinates.cs b/src/ImGuiColorTextEditNet/Coordinates.cs new file mode 100644 index 0000000..802d79f --- /dev/null +++ b/src/ImGuiColorTextEditNet/Coordinates.cs @@ -0,0 +1,47 @@ +using System; + +namespace ImGuiColorTextEditNet; + +// Represents a character coordinate from the user's point of view, +// i.e. consider a uniform grid (assuming fixed-width font) on the +// screen as it is rendered, and each cell has its own coordinate, starting from 0. +// Tabs are counted as between 1 and mTabSize empty spaces, depending on +// how many spaces are necessary to reach the next tab stop. +// For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4, +// because it is rendered as " ABC" on the screen. +internal struct Coordinates : IEquatable +{ + public int Line; + public int Column; + + public Coordinates() { Line = 0; Column = 0; } + public Coordinates(int line, int column) + { + //Util.Assert(line >= 0); + //Util.Assert(column >= 0); + Line = line; + Column = column; + } + + public static Coordinates Invalid => new() { Line = -1, Column = -1 }; + + public static bool operator ==(Coordinates x, Coordinates y) => x.Line == y.Line && x.Column == y.Column; + public static bool operator !=(Coordinates x, Coordinates y) => x.Line != y.Line || x.Column != y.Column; + public static bool operator <(Coordinates x, Coordinates y) => x.Line != y.Line ? x.Line < y.Line : x.Column < y.Column; + public static bool operator >(Coordinates x, Coordinates y) => x.Line != y.Line ? x.Line > y.Line : x.Column > y.Column; + public static bool operator <=(Coordinates x, Coordinates y) => x.Line != y.Line ? x.Line < y.Line : x.Column <= y.Column; + public static bool operator >=(Coordinates x, Coordinates y) => x.Line != y.Line ? x.Line > y.Line : x.Column >= y.Column; + + public bool Equals(Coordinates other) => Line == other.Line && Column == other.Column; + public override bool Equals(object? obj) => obj is Coordinates other && Equals(other); + + public override int GetHashCode() + { + unchecked + { + return (Line * 397) ^ Column; + } + } + + public override string ToString() => $"{Line}:{Column}"; +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Editor/ITextEditorRenderer.cs b/src/ImGuiColorTextEditNet/Editor/ITextEditorRenderer.cs new file mode 100644 index 0000000..5e26025 --- /dev/null +++ b/src/ImGuiColorTextEditNet/Editor/ITextEditorRenderer.cs @@ -0,0 +1,13 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; + +namespace ImGuiColorTextEditNet.Editor; + +internal interface ITextEditorRenderer +{ + int PageSize { get; } + void ScreenPosToCoordinates(ref readonly Vector2 position, out Coordinates coordinates); + void Render(ReadOnlySpan title, ref readonly Vector2 size); + void SetPalette<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] TColorPalette>(ColorPaletteIndex colorPaletteIndex) where TColorPalette : ColorPalette; +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Editor/TextEditorMovement.cs b/src/ImGuiColorTextEditNet/Editor/TextEditorMovement.cs new file mode 100644 index 0000000..18ddc27 --- /dev/null +++ b/src/ImGuiColorTextEditNet/Editor/TextEditorMovement.cs @@ -0,0 +1,267 @@ +using System; + +namespace ImGuiColorTextEditNet.Editor; + +internal class TextEditorMovement +{ + private readonly TextEditorSelection _selection; + private readonly TextEditorText _text; + + internal TextEditorMovement(TextEditorSelection selection, TextEditorText text) + { + _selection = selection ?? throw new ArgumentNullException(nameof(selection)); + _text = text ?? throw new ArgumentNullException(nameof(text)); + } + + public void MoveUp(int amount = 1, bool isSelecting = false) + { + var oldPos = _selection.Cursor; + var newPos = _selection.Cursor; + newPos.Line = Math.Max(0, _selection.Cursor.Line - amount); + + if (oldPos == newPos) + return; + + _selection.Cursor = newPos; + if (isSelecting) + { + if (oldPos == _selection.InteractiveStart) + _selection.InteractiveStart = _selection.Cursor; + else if (oldPos == _selection.InteractiveEnd) + _selection.InteractiveEnd = _selection.Cursor; + else + { + _selection.InteractiveStart = _selection.Cursor; + _selection.InteractiveEnd = oldPos; + } + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd); + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveDown(int amount = 1, bool isSelecting = false) + { + //Util.Assert(_selection.Cursor.Column >= 0); + var oldPos = _selection.Cursor; + var newPos = _selection.Cursor; + newPos.Line = Math.Max(0, Math.Min(_text.LineCount - 1, _selection.Cursor.Line + amount)); + + if (newPos == oldPos) + return; + + _selection.Cursor = newPos; + + if (isSelecting) + { + if (oldPos == _selection.InteractiveEnd) + _selection.InteractiveEnd = _selection.Cursor; + else if (oldPos == _selection.InteractiveStart) + _selection.InteractiveStart = _selection.Cursor; + else + { + _selection.InteractiveStart = oldPos; + _selection.InteractiveEnd = _selection.Cursor; + } + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd); + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveLeft(int amount = 1, bool isSelecting = false, bool isWordMode = false) + { + if (_text.LineCount == 0) + return; + + var oldPos = _selection.Cursor; + _selection.GetActualCursorCoordinates(out _selection.Cursor); + var line = _selection.Cursor.Line; + var cindex = _text.GetCharacterIndex(in _selection.Cursor); + + while (amount-- > 0) + { + if (cindex == 0) + { + if (line > 0) + { + --line; + cindex = _text.LineCount > line ? _text.GetLine(line).Length : 0; + } + } + else + --cindex; + + _selection.Cursor = new(line, _text.GetCharacterColumn(line, cindex)); + if (isWordMode) + { + _text.FindWordStart(in _selection.Cursor, out _selection.Cursor); + cindex = _text.GetCharacterIndex(in _selection.Cursor); + } + } + + _selection.Cursor = new(line, _text.GetCharacterColumn(line, cindex)); + + //Util.Assert(_selection.Cursor.Column >= 0); + if (isSelecting) + { + if (oldPos == _selection.InteractiveStart) + _selection.InteractiveStart = _selection.Cursor; + else if (oldPos == _selection.InteractiveEnd) + _selection.InteractiveEnd = _selection.Cursor; + else + { + _selection.InteractiveStart = _selection.Cursor; + _selection.InteractiveEnd = oldPos; + } + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd, isSelecting && isWordMode ? SelectionMode.Word : SelectionMode.Normal); + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveRight(int amount = 1, bool isSelecting = false, bool isWordMode = false) + { + var oldPos = _selection.Cursor; + + if (_text.LineCount == 0 || oldPos.Line >= _text.LineCount) + return; + + var cindex = _text.GetCharacterIndex(in _selection.Cursor); + while (amount-- > 0) + { + var lindex = _selection.Cursor.Line; + var line = _text.GetLine(lindex); + if (cindex >= line.Length) + { + if (_selection.Cursor.Line < _text.LineCount - 1) + _selection.Cursor = new(Math.Max(0, Math.Min(_text.LineCount - 1, _selection.Cursor.Line + 1)), 0); + else + return; + } + else + { + cindex++; + _selection.Cursor = new(lindex, _text.GetCharacterColumn(lindex, cindex)); + if (isWordMode) + _text.FindNextWord(in _selection.Cursor, out _selection.Cursor); + } + } + + if (isSelecting) + { + if (oldPos == _selection.InteractiveEnd) + _text.SanitizeCoordinates(in _selection.Cursor, out _selection.InteractiveEnd); + else if (oldPos == _selection.InteractiveStart) + _selection.InteractiveStart = _selection.Cursor; + else + { + _selection.InteractiveStart = oldPos; + _selection.InteractiveEnd = _selection.Cursor; + } + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd, isSelecting && isWordMode ? SelectionMode.Word : SelectionMode.Normal); + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveToStartOfFile(bool isSelecting = false) + { + var oldPos = _selection.Cursor; + _selection.Cursor = new(0, 0); + + if (_selection.Cursor == oldPos) + return; + + if (isSelecting) + { + _selection.InteractiveEnd = oldPos; + _selection.InteractiveStart = _selection.Cursor; + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd); + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveToEndOfFile(bool isSelecting = false) + { + var oldPos = _selection.Cursor; + var newPos = new Coordinates(_text.LineCount - 1, 0); + _selection.Cursor = newPos; + + if (isSelecting) + { + _selection.InteractiveStart = oldPos; + _selection.InteractiveEnd = newPos; + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = newPos; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd); + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveToStartOfLine(bool isSelecting = false) + { + var oldPos = _selection.Cursor; + _selection.Cursor = new(_selection.Cursor.Line, 0); + + if (_selection.Cursor != oldPos) + { + if (isSelecting) + { + if (oldPos == _selection.InteractiveStart) + _selection.InteractiveStart = _selection.Cursor; + else if (oldPos == _selection.InteractiveEnd) + _selection.InteractiveEnd = _selection.Cursor; + else + { + _selection.InteractiveStart = _selection.Cursor; + _selection.InteractiveEnd = oldPos; + } + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd); + } + + _text.PendingScrollRequest = _selection.Cursor.Line; + } + + public void MoveToEndOfLine(bool isSelecting = false) + { + var oldPos = _selection.Cursor; + _selection.Cursor = new(_selection.Cursor.Line, _text.GetLineMaxColumn(oldPos.Line)); + + if (_selection.Cursor == oldPos) + return; + + if (isSelecting) + { + if (oldPos == _selection.InteractiveEnd) + _selection.InteractiveEnd = _selection.Cursor; + else if (oldPos == _selection.InteractiveStart) + _selection.InteractiveStart = _selection.Cursor; + else + { + _selection.InteractiveStart = oldPos; + _selection.InteractiveEnd = _selection.Cursor; + } + } + else + _selection.InteractiveStart = _selection.InteractiveEnd = _selection.Cursor; + + _selection.Select(in _selection.InteractiveStart, in _selection.InteractiveEnd); + _text.PendingScrollRequest = _selection.Cursor.Line; + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Editor/TextEditorRenderer.cs b/src/ImGuiColorTextEditNet/Editor/TextEditorRenderer.cs new file mode 100644 index 0000000..8190f35 --- /dev/null +++ b/src/ImGuiColorTextEditNet/Editor/TextEditorRenderer.cs @@ -0,0 +1,528 @@ +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Structures; + +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace ImGuiColorTextEditNet.Editor; + +internal class TextEditorRenderer +{ + protected const float LineSpacing = 1.0f; + protected const int LeftMargin = 10; + protected const int CursorBlinkPeriodMs = 800; + + protected static readonly Vector4 MagentaVec4 = new(1.0f, 1.0f, 1.0f, 1.0f); + protected static readonly uint MagentaUInt = 0xff00ffff; +} + +internal class TextEditorRenderer : TextEditorRenderer, ITextEditorRenderer + where TImDrawListRef : IImDrawList + + where TImGuiStyleRef : IImGuiStyle + where TColorsRangeAccessorRef : IRangeAccessor + + where TImGuiListClipperRef : IImGuiListClipper +{ + private readonly IImGui _imGui; + private readonly IImGuiWithImDrawList _imGuiWithImDrawList; + private readonly IImGuiWithImGuiStyle _imGuiWithImGuiStyle; + private readonly IImGuiWithImGuiListClipper _imGuiWithImGuiListClipper; + private readonly TextEditorSelection _selection; + private readonly TextEditorText _text; + private readonly StringBuilder _lineBuffer = new(); + private readonly List _palette = new(); + + private Vector2 _charAdvance; + private DateTime _startTime = DateTime.UtcNow; + private float _textStart = 20.0f; // position (in pixels) where a code line starts relative to the left of the TextEditor. + private uint[]? _uintPalette; + private ColorPaletteIndex? _vec4Palette; + private bool _paletteDirty; + private float _lastAlpha; + + + public ColorPaletteIndex PaletteIndex => _vec4Palette!; + public uint[] Palette + { + get => _palette.ToArray(); + set + { + _palette.Clear(); + _palette.AddRange(value); + _paletteDirty = true; + } + } + + public int PageSize + { + get + { + var height = _imGui.GetWindowHeight() - 20.0f; + return (int) Math.Floor(height / _charAdvance.Y); + } + } + + public ITextEditorKeyboardInput? KeyboardInput { get; init; } + public ITextEditorMouseInput? MouseInput { get; init; } + + private float fontSize; + private float spaceSize; + + internal TextEditorRenderer( + IImGui imGui, + IImGuiWithImDrawList imGuiWithImDrawList, + IImGuiWithImGuiStyle imGuiWithImGuiStyle, + IImGuiWithImGuiListClipper imGuiWithImGuiListClipper, + ITextEditor editor) + { + _selection = editor.Selection; + _text = editor.Text; + Palette = []; + _imGui = imGui; + _imGuiWithImDrawList = imGuiWithImDrawList; + _imGuiWithImGuiStyle = imGuiWithImGuiStyle; + _imGuiWithImGuiListClipper = imGuiWithImGuiListClipper; + } + + private uint ColorUInt(ushort index) => _uintPalette == null || index >= _uintPalette.Length + ? MagentaUInt + : _uintPalette[index]; + + private uint ColorUInt(ColorPalette colorPalette) => _uintPalette == null || colorPalette.ColorIndex >= _uintPalette.Length + ? MagentaUInt + : _uintPalette[colorPalette.ColorIndex]; + + private ref readonly Vector4 ColorVec(ColorPalette colorPalette) => ref _vec4Palette == null || colorPalette.ColorIndex > _vec4Palette.Length + ? ref MagentaVec4 + : ref _vec4Palette[colorPalette.ColorIndex]; + + public void SetPalette<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] TColorPalette>(ColorPaletteIndex colorPaletteIndex) where TColorPalette : ColorPalette + { + var values = ColorPalette.GetAll(); + if (values.Length != colorPaletteIndex.Length) + throw new ArgumentException("Palette must contain all values of the enum."); + + _palette.Clear(); + for (var index = 0; index < values.Length; index++) + _palette.Add(_imGui.ColorConvertFloat4ToU32(in colorPaletteIndex[values[index].ColorIndex])); + + _paletteDirty = true; + } + + private bool _isInitialized; + public void Initialize() + { + _imGui.CalcTextSize('#', out var fontSizeV2); + fontSize = fontSizeV2.X; + + _imGui.CalcTextSize(' ', out var spaceSizeV2); + spaceSize = spaceSizeV2.X; + } + + public void Render(ReadOnlySpan title, ref readonly Vector2 size) + { + if (!_isInitialized) + { + Initialize(); + _isInitialized = true; + } + + Vector4 background; + if (_vec4Palette == null) + _imGui.ColorConvertU32ToFloat4(_palette[ColorPalette.Background], out background); + else + background = ColorVec(ColorPalette.Background); + var itemSpacing = new Vector2(0.0f, 0.0f); + + _imGui.PushStyleColor(ImGuiCol.ChildBg, in background); + _imGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, in itemSpacing); + + _imGui.BeginChild(title, in size, ImGuiChildFlags.AutoResizeY, ImGuiWindowFlags.HorizontalScrollbar | + ImGuiWindowFlags.AlwaysHorizontalScrollbar | + ImGuiWindowFlags.NoMove); + + KeyboardInput?.HandleKeyboardInputs(); + + MouseInput?.HandleMouseInputs(); + + RenderInner(); + + if (_text.PendingScrollRequest != null) + { + if (_text.PendingScrollRequest.Value < _text.LineCount) + EnsurePositionVisible(new Coordinates(_text.PendingScrollRequest.Value, 0)); + _imGui.SetWindowFocus(); + _text.PendingScrollRequest = null; + } + + _imGui.EndChild(); + + _imGui.PopStyleVar(); + _imGui.PopStyleColor(); + } + + private void RenderInner() + { + /* Compute _charAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ + _charAdvance = new Vector2(fontSize, _imGui.GetTextLineHeightWithSpacing() * LineSpacing); + + _imGuiWithImGuiStyle.GetStyle(out var style); + var alpha = style.Alpha; + if (Math.Abs(_lastAlpha - alpha) > float.Epsilon) + { + _paletteDirty = true; + _lastAlpha = alpha; + } + + /* Update palette with the current alpha from style */ + if (_paletteDirty) + { + _uintPalette = _palette.ToArray(); + var vec4Palette = new Vector4[_palette.Count]; + + for (var i = 0; i < Palette.Length; ++i) + { + _imGui.ColorConvertU32ToFloat4(_palette[i], out var color); + color.W *= alpha; + vec4Palette[i] = color; + _uintPalette[i] = _imGui.ColorConvertFloat4ToU32(in color); + } + + _vec4Palette = new ColorPaletteIndex().With(new ColorPaletteIndexCreator + { + Indices = vec4Palette.Select((x, i) => ((ushort) i, x)).ToList(), + }); + + _paletteDirty = false; + } + + //Util.Assert(_lineBuffer.Length == 0); + + var longest = _textStart; + _imGuiWithImDrawList.GetWindowDrawList(out var drawList); + _imGui.GetCursorScreenPos(out var cursorScreenPos); + var scrollX = _imGui.GetScrollX(); + var globalLineMax = _text.LineCount; + + // Deduce _textStart by evaluating _lines size (global lineMax) plus two spaces as text width + using var buf = GetLineNumberCache(globalLineMax, out var bufLength); + _imGui.CalcTextSize(buf.Memory.Span.Slice(0, bufLength), out var bufSize); + _textStart = bufSize.X + LeftMargin + spaceSize; + + if (globalLineMax != 0) + { + _imGuiWithImGuiListClipper.CreateImGuiListClipper(out var clipper); + using var _ = clipper; + + clipper.Begin(globalLineMax, _imGui.GetTextLineHeightWithSpacing()); + while (clipper.Step()) + { + RenderWithClipper(in clipper, in drawList, in cursorScreenPos, scrollX, ref longest, spaceSize); + } + clipper.End(); + } + + // Expand the window size to fit the longest line + var vec2 = new Vector2(longest + 2, 0f); + _imGui.Dummy(in vec2); + } + + private void RenderWithClipper(ref readonly TImGuiListClipperRef clipper, ref readonly TImDrawListRef drawList, ref readonly Vector2 cursorScreenPos, float scrollX, ref float longestLineLength, float spaceSize) + { + for (var lineIdx = clipper.DisplayStart; lineIdx < clipper.DisplayEnd; ++lineIdx) + { + var lineStartScreenPos = cursorScreenPos with { Y = cursorScreenPos.Y + lineIdx * _charAdvance.Y }; + var textScreenPos = lineStartScreenPos with { X = lineStartScreenPos.X + _textStart }; + + var line = _text.GetLine(lineIdx); + var lineLength = _textStart + TextDistanceToLineStart(new(lineIdx, _text.GetLineMaxColumn(lineIdx))); + longestLineLength = Math.Max(lineLength, longestLineLength); + + var lineStartCoord = new Coordinates(lineIdx, 0); + var lineEndCoord = new Coordinates(lineIdx, _text.GetLineMaxColumn(lineIdx)); + + // Draw selection for the current line + var sstart = float.NegativeInfinity; + var ssend = float.NegativeInfinity; + + //Util.Assert(_selection.Start <= _selection.End); + if (_selection.Start <= lineEndCoord) + sstart = _selection.Start > lineStartCoord ? TextDistanceToLineStart(in _selection.State.Start) : 0.0f; + if (_selection.End > lineStartCoord) + ssend = TextDistanceToLineStart(in _selection.End < lineEndCoord ? ref _selection.State.End : ref lineEndCoord); + + if (_selection.End.Line > lineIdx) + ssend += _charAdvance.X; + + if (!float.IsNegativeInfinity(sstart) && !float.IsNegativeInfinity(ssend) && sstart < ssend) + { + var vstart = lineStartScreenPos with { X = lineStartScreenPos.X + _textStart + sstart }; + var vend = new Vector2(lineStartScreenPos.X + _textStart + ssend, lineStartScreenPos.Y + _charAdvance.Y); + drawList.AddRectFilled(in vstart, in vend, ColorUInt(ColorPalette.Selection), 1.0f); + } + + var start = lineStartScreenPos with { X = lineStartScreenPos.X + scrollX }; + + if (_text.ExceptionLines.Contains(lineIdx + 1)) + { + /* + var y = new Coordinates(lineIdx, 0); + while (_text.IsOnWordBoundary(y)) + y = y with { Column = y.Column + 1 }; + y = y with { Column = y.Column - 1 }; + var tstart = TextDistanceToLineStart(y); + */ + var tstart = 0; + var estart = start with { X = start.X + _textStart + tstart }; + var end = new Vector2( + lineStartScreenPos.X + lineLength + 2.0f * scrollX, + lineStartScreenPos.Y + _charAdvance.Y); + + drawList.AddRectFilled(in estart, in end, ColorUInt(ColorPalette.ErrorMarker), 1f); + + if (_imGui.IsMouseHoveringRect(in lineStartScreenPos, in end)) + { + _imGui.BeginTooltip(); + _imGui.PushStyleColor(ImGuiCol.Text, in ColorVec(ColorPalette.ErrorText)); + _imGui.Text("Exception"u8); + _imGui.PopStyleColor(); + _imGui.EndTooltip(); + } + } + + // Draw line number (right aligned) + using var buf = GetLineNumberCache(lineIdx + 1, out var bufLength); + + _imGui.CalcTextSize(buf.Memory.Span.Slice(0, bufLength), out var bufSize); + var lineNoWidth = bufSize.X; + var pos = lineStartScreenPos with { X = lineStartScreenPos.X + _textStart - lineNoWidth }; + drawList.AddText(in pos, ColorUInt(ColorPalette.LineNumber), buf.Memory.Span.Slice(0, bufLength)); + + if (_selection.Cursor.Line == lineIdx) + { + var focused = _imGui.IsWindowFocused(); + + // Highlight the current line (where the cursor is) + if (!_selection.HasSelection) + { + var sstart2 = start with { X = start.X + _textStart }; + var end = new Vector2(start.X + lineLength - scrollX, start.Y + _charAdvance.Y); + drawList.AddRectFilled(in sstart2, in end, ColorUInt(focused ? ColorPalette.CurrentLineFill : ColorPalette.CurrentLineFillInactive), 1.0f); + + drawList.AddRect(in start, in end, ColorUInt(ColorPalette.CurrentLineEdge), 1.0f); + } + + // Render the cursor + if (focused) + { + var timeEnd = DateTime.UtcNow; + var elapsed = timeEnd - _startTime; + if (elapsed.Milliseconds > CursorBlinkPeriodMs / 2) + { + var width = 1.0f; + //var cindex = _text.GetCharacterIndex(_selection.Cursor); + var cx = TextDistanceToLineStart(in _selection.Cursor); + + var cstart = lineStartScreenPos with { X = textScreenPos.X + cx }; + var cend = new Vector2(textScreenPos.X + cx + width, lineStartScreenPos.Y + _charAdvance.Y); + drawList.AddRectFilled(in cstart, in cend, ColorUInt(ColorPalette.Cursor), 1.0f); + if (elapsed.Milliseconds > CursorBlinkPeriodMs) + _startTime = timeEnd; + } + } + } + + // Render colorized text + var prevColor = line.Length == 0 ? ColorUInt(ColorPalette.Default) : ColorUInt(line[0].ColorIndex); + var bufferOffset = new Vector2(); + + for (var i = 0; i < line.Length;) + { + var glyph = line[i]; + var color = ColorUInt(glyph.ColorIndex); + + if ((color != prevColor || glyph.Char is '\t' or ' ') && _lineBuffer.Length != 0) + { + var newOffset = new Vector2(textScreenPos.X + bufferOffset.X, textScreenPos.Y + bufferOffset.Y); + DrawText(in drawList, in newOffset, prevColor, _lineBuffer, out var textSize); + bufferOffset.X += textSize.X; + _lineBuffer.Clear(); + } + prevColor = color; + + if (glyph.Char == '\t') + { + bufferOffset.X = (1.0f + (float) Math.Floor((1.0f + bufferOffset.X) / (_text.TabSize * spaceSize))) * (_text.TabSize * spaceSize); + ++i; + } + else if (glyph.Char == ' ') + { + bufferOffset.X += spaceSize; + i++; + } + else + { + _lineBuffer.Append(line[i++].Char); + } + } + + if (_lineBuffer.Length != 0) + { + var newOffset = new Vector2(textScreenPos.X + bufferOffset.X, textScreenPos.Y + bufferOffset.Y); + DrawText(in drawList, in newOffset, prevColor, _lineBuffer, out _); + _lineBuffer.Clear(); + } + } + } + + private void DrawText(ref readonly TImDrawListRef drawList, ref readonly Vector2 offset, uint color, StringBuilder sb, out Vector2 textSize) + { + IMemoryOwner? tempMemory = null; + if (sb.Length > 1024) + tempMemory = MemoryPool.Shared.Rent(sb.Length); + + try + { + var temp = sb.Length > 1024 + ? tempMemory!.Memory.Span.Slice(0, sb.Length) + : stackalloc char[sb.Length]; + + var i = 0; + + foreach (var chunk in sb.GetChunks()) + { + chunk.Span.CopyTo(temp.Slice(i)); + i += chunk.Length; + } + + drawList.AddText(in offset, color, temp); + _imGui.CalcTextSize(temp, out textSize); + } + finally + { + tempMemory?.Dispose(); + } + } + + private float TextDistanceToLineStart(ref readonly Coordinates position) + { + var line = _text.GetLine(position.Line); + var distance = 0.0f; + + var colIndex = _text.GetCharacterIndex(in position); + for (var i = 0; i < line.Length && i < colIndex;) + { + var c = line[i].Char; + _imGui.CalcTextSize(c, out var charSizeV2); + distance = c == '\t' + ? (1.0f + (float) Math.Floor((1.0f + distance) / (_text.TabSize * spaceSize))) * (_text.TabSize * spaceSize) + //: distance + _charWidthCache.Get(c); + : distance + charSizeV2.X; + + i++; + } + + return distance; + } + + private void EnsurePositionVisible(ref readonly Coordinates pos) + { + var scrollX = _imGui.GetScrollX(); + var scrollY = _imGui.GetScrollY(); + + var height = _imGui.GetWindowHeight(); + var width = _imGui.GetWindowWidth(); + + var top = 1 + (int) Math.Ceiling(scrollY / _charAdvance.Y); + var bottom = (int) Math.Ceiling((scrollY + height) / _charAdvance.Y); + + var left = (int) Math.Ceiling(scrollX / _charAdvance.X); + var right = (int) Math.Ceiling((scrollX + width) / _charAdvance.X); + + var len = TextDistanceToLineStart(in pos); + + if (pos.Line < top) + _imGui.SetScrollY(Math.Max(0.0f, (pos.Line - 1) * _charAdvance.Y)); + if (pos.Line > bottom - 4) + _imGui.SetScrollY(Math.Max(0.0f, (pos.Line + 4) * _charAdvance.Y - height)); + if (len + _textStart < left + 4) + _imGui.SetScrollX(Math.Max(0.0f, len + _textStart - 4)); + if (len + _textStart > right - 4) + _imGui.SetScrollX(Math.Max(0.0f, len + _textStart + 4 - width)); + } + + public void ScreenPosToCoordinates(ref readonly Vector2 position, out Coordinates coordinates) + { + _imGui.GetCursorScreenPos(out var origin); + var local = new Vector2(position.X - origin.X, position.Y - origin.Y); + + var lineCount = _text.LineCount; + var lineNo = Math.Max(0, (int) Math.Floor(local.Y / _charAdvance.Y)); + var columnCoord = 0; + + if (lineNo >= lineCount) + { + coordinates = new Coordinates(lineNo, columnCoord); + _text.SanitizeCoordinates(in coordinates, out coordinates); + return; + } + + var line = _text.GetLine(lineNo); + + var columnIndex = 0; + var columnX = 0.0f; + + while (columnIndex < line.Length) + { + float columnWidth; + + if (line[columnIndex].Char == '\t') + { + var oldX = columnX; + var newColumnX = (1.0f + (float) Math.Floor((1.0f + columnX) / (_text.TabSize * spaceSize))) * (_text.TabSize * spaceSize); + columnWidth = newColumnX - oldX; + if (_textStart + columnX + columnWidth * 0.5f > local.X) + break; + + columnX = newColumnX; + columnCoord = columnCoord / _text.TabSize * _text.TabSize + _text.TabSize; + columnIndex++; + } + else + { + _imGui.CalcTextSize(line[columnIndex++].Char, out var charSizeV2); + columnWidth = charSizeV2.X; + // TODO: + //columnWidth = _charWidthCache.Get(line[columnIndex++].Char); + if (_textStart + columnX + columnWidth * 0.5f > local.X) + break; + + columnX += columnWidth; + columnCoord++; + } + } + + coordinates = new Coordinates(lineNo, columnCoord); + _text.SanitizeCoordinates(in coordinates, out coordinates); + } + + private static IMemoryOwner GetLineNumberCache(int lineIdx, out int length) + { + // We support up to 9999 lines + var buffer = MemoryPool.Shared.Rent(6); + var bufferUtf8 = buffer.Memory.Span; + Utf8Formatter.TryFormat(lineIdx, bufferUtf8, out length); + bufferUtf8[length++] = (byte) ' '; + bufferUtf8[length++] = 0; + return buffer; + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Editor/TextEditorSelection.cs b/src/ImGuiColorTextEditNet/Editor/TextEditorSelection.cs new file mode 100644 index 0000000..df438fc --- /dev/null +++ b/src/ImGuiColorTextEditNet/Editor/TextEditorSelection.cs @@ -0,0 +1,83 @@ +using System; +using System.Buffers; + +namespace ImGuiColorTextEditNet.Editor; + +internal class TextEditorSelection +{ + private readonly TextEditorText _text; + private SelectionState _state; + + internal SelectionMode Mode = SelectionMode.Normal; + internal Coordinates InteractiveStart; + internal Coordinates InteractiveEnd; + + internal TextEditorSelection(TextEditorText text) + { + _text = text ?? throw new ArgumentNullException(nameof(text)); + } + + public IMemoryOwner GetSelectedText() => _text.GetText(in _state.Start, in _state.End); + internal void GetActualCursorCoordinates(out Coordinates cursorCoordinates) => _text.SanitizeCoordinates(in Cursor, out cursorCoordinates); + + internal ref SelectionState State => ref _state; + + public ref Coordinates Cursor => ref _state.Cursor; + + public Coordinates Start + { + get => _state.Start; + set + { + _text.SanitizeCoordinates(in value, out _state.Start); + if (_state.Start > _state.End) + (_state.Start, _state.End) = (_state.End, _state.Start); + } + } + + public Coordinates End + { + get => _state.End; + set + { + _text.SanitizeCoordinates(in value, out _state.End); + if (_state.Start > _state.End) + (_state.Start, _state.End) = (_state.End, _state.Start); + } + } + + public void SelectAll() => Select(new(0, 0), new(_text.LineCount, 0)); + public bool HasSelection => End > Start; + + public void Select(ref readonly Coordinates start, ref readonly Coordinates end, SelectionMode mode = SelectionMode.Normal) + { + _text.SanitizeCoordinates(in start, out _state.Start); + _text.SanitizeCoordinates(in end, out _state.End); + + switch (mode) + { + case SelectionMode.Normal: + break; + + case SelectionMode.Word: + { + _text.FindWordStart(in _state.Start, out _state.Start); + _text.SanitizeCoordinates(in _state.Start, out _state.Start); + if (!_text.IsOnWordBoundary(in _state.End)) + { + _text.FindWordStart(in _state.End, out _state.End); + _text.FindWordEnd(in _state.End, out _state.End); + _text.SanitizeCoordinates(in _state.End, out _state.End); + } + break; + } + + case SelectionMode.Line: + { + Start = new(Start.Line, 0); + End = new(End.Line, _text.GetLineMaxColumn(End.Line)); + break; + } + } + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Editor/TextEditorText.cs b/src/ImGuiColorTextEditNet/Editor/TextEditorText.cs new file mode 100644 index 0000000..e5701c2 --- /dev/null +++ b/src/ImGuiColorTextEditNet/Editor/TextEditorText.cs @@ -0,0 +1,338 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Text; + +namespace ImGuiColorTextEditNet.Editor; + +internal class TextEditorText +{ + private readonly List _lines = []; + private int _tabSize = 4; + + public int LineCount => _lines.Count; + public int? PendingScrollRequest { get; set; } + + public int TabSize { get => _tabSize; set => _tabSize = Math.Max(0, Math.Min(32, value)); } + + public HashSet ExceptionLines { get; } = new(); + + public ReadOnlySpan GetLine(int index) => _lines[index].AsSpan(); + + public IMemoryOwner GetText(ref readonly Coordinates startPos, ref readonly Coordinates endPos) + { + var lstart = startPos.Line; + var lend = endPos.Line; + var istart = GetCharacterIndex(in startPos); + var iend = GetCharacterIndex(in endPos); + var s = 0; + + for (var i = lstart; i < lend; i++) + s += _lines[i].Glyphs.Count; + if (lstart == lend) + s += iend - istart; + + var j = 0; + var buffer = MemoryPool.Shared.Rent(s + s / 8); + while (istart < iend || lstart < lend) + { + if (lstart >= _lines.Count) + break; + + var line = _lines[lstart].AsSpan(); + if (istart < line.Length) + { + buffer.Memory.Span[j++] = line[istart].Char; + istart++; + } + else + { + istart = 0; + ++lstart; + if (lstart < _lines.Count) + { + Environment.NewLine.AsSpan().CopyTo(buffer.Memory.Span.Slice(j, Environment.NewLine.Length)); + j += Environment.NewLine.Length; + } + } + } + + return buffer; + } + + public string GetWordAt(ref readonly Coordinates position) + { + FindWordStart(in position, out var start); + FindWordEnd(in position, out var end); + + var sb = new StringBuilder(); + + var istart = GetCharacterIndex(in start); + var iend = GetCharacterIndex(in end); + + var line = _lines[position.Line].AsSpan(); + for (var it = istart; it < iend; ++it) + sb.Append(line[it].Char); + + return sb.ToString(); + } + + public int GetCharacterIndex(ref readonly Coordinates position) + { + if (position.Line >= _lines.Count) + return -1; + + var line = _lines[position.Line].AsSpan(); + var c = 0; + var i = 0; + + for (; i < line.Length && c < position.Column;) + { + if (line[i].Char == '\t') + c = c / _tabSize * _tabSize + _tabSize; + else + c++; + i++; + } + + return i; + } + + public int GetCharacterColumn(int lineNumber, int columnNumber) + { + if (lineNumber >= _lines.Count) + return 0; + + var line = _lines[lineNumber].AsSpan(); + var col = 0; + var i = 0; + + while (i < columnNumber && i < line.Length) + { + var c = line[i].Char; + i++; + if (c == '\t') + col = col / _tabSize * _tabSize + _tabSize; + else + col++; + } + + return col; + } + + public int GetLineMaxColumn(int lineNumber) + { + if (lineNumber >= _lines.Count) + return 0; + + var line = _lines[lineNumber].AsSpan(); + var col = 0; + + for (var i = 0; i < line.Length;) + { + var c = line[i].Char; + if (c == '\t') + col = col / _tabSize * _tabSize + _tabSize; + else + col++; + i++; + } + + return col; + } + + public bool IsOnWordBoundary(ref readonly Coordinates position) + { + if (position.Line >= _lines.Count || position.Column == 0) + return true; + + var line = _lines[position.Line].AsSpan(); + var cindex = GetCharacterIndex(in position); + if (cindex >= line.Length) + return true; + + return line[cindex].ColorIndex != line[cindex - 1].ColorIndex; + } + + public void SanitizeCoordinates(ref readonly Coordinates value, out Coordinates sanitizedValue) + { + var line = value.Line; + var column = value.Column; + if (line >= _lines.Count) + { + if (_lines.Count == 0) + { + line = 0; + column = 0; + } + else + { + line = _lines.Count - 1; + column = GetLineMaxColumn(line); + } + } + else + { + column = _lines.Count == 0 ? 0 : Math.Min(column, GetLineMaxColumn(line)); + } + + sanitizedValue = new(line, column); + } + + public void FindWordStart(ref readonly Coordinates position, out Coordinates wordStart) + { + if (position.Line >= _lines.Count) + { + wordStart = position; + return; + } + + var line = _lines[position.Line].AsSpan(); + var cindex = GetCharacterIndex(in position); + + if (cindex >= line.Length) + { + wordStart = position; + return; + } + + while (cindex > 0 && char.IsWhiteSpace(line[cindex].Char)) + --cindex; + + var cstart = line[cindex].ColorIndex; + while (cindex > 0) + { + var c = line[cindex].Char; + if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx + { + if (c <= 32 && char.IsWhiteSpace(c)) + { + cindex++; + break; + } + if (cstart != line[cindex - 1].ColorIndex) + break; + } + --cindex; + } + + wordStart = new(position.Line, GetCharacterColumn(position.Line, cindex)); + } + + public void FindWordEnd(ref readonly Coordinates position, out Coordinates wordEnd) + { + if (position.Line >= _lines.Count) + { + wordEnd = position; + return; + } + + var line = _lines[position.Line].AsSpan(); + var cindex = GetCharacterIndex(in position); + + if (cindex >= line.Length) + { + wordEnd = position; + return; + } + + var prevspace = char.IsWhiteSpace(line[cindex].Char); + var cstart = line[cindex].ColorIndex; + while (cindex < line.Length) + { + var c = line[cindex].Char; + if (cstart != line[cindex].ColorIndex) + break; + + if (prevspace != char.IsWhiteSpace(c)) + { + if (char.IsWhiteSpace(c)) + while (cindex < line.Length && char.IsWhiteSpace(line[cindex].Char)) + ++cindex; + break; + } + cindex++; + } + wordEnd = new(position.Line, GetCharacterColumn(position.Line, cindex)); + } + + public void FindNextWord(ref readonly Coordinates from, out Coordinates nextWord) + { + var at = from; + if (at.Line >= _lines.Count) + { + nextWord = at; + return; + } + + // skip to the next non-word character + var isword = false; + var skip = false; + var cindex = GetCharacterIndex(in from); + { + var line = _lines[at.Line].AsSpan(); + if (cindex < line.Length) + { + isword = char.IsLetterOrDigit(line[cindex].Char); + skip = isword; + } + } + + while (!isword || skip) + { + if (at.Line >= _lines.Count) + { + var l = Math.Max(0, _lines.Count - 1); + nextWord = new(l, GetLineMaxColumn(l)); + return; + } + + var line = _lines[at.Line].AsSpan(); + if (cindex < line.Length) + { + isword = char.IsLetterOrDigit(line[cindex].Char); + + if (isword && !skip) + { + nextWord = new(at.Line, GetCharacterColumn(at.Line, cindex)); + return; + } + + if (!isword) + skip = false; + + cindex++; + } + else + { + cindex = 0; + ++at.Line; + skip = false; + isword = false; + } + } + + nextWord = at; + } + + public void AddGlyphs(Span glyphs) + { + Line line; + if (_lines.Count == 0) + _lines.Add(line = Line.Empty); + else + line = _lines[_lines.Count - 1]; + + for (var i = 0; i < glyphs.Length; i++) + { + if (glyphs[i].Char == '\n') + { + _lines.Add(line = Line.Empty); + } + else + { + line.Glyphs.Add(glyphs[i]); + } + } + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/Glyph.cs b/src/ImGuiColorTextEditNet/Glyph.cs new file mode 100644 index 0000000..bdf9c3d --- /dev/null +++ b/src/ImGuiColorTextEditNet/Glyph.cs @@ -0,0 +1,13 @@ +namespace ImGuiColorTextEditNet; + +public readonly struct Glyph +{ + public readonly char Char; + public readonly ushort ColorIndex; + + public Glyph(char aChar, ushort colorIndex) + { + Char = aChar; + ColorIndex = colorIndex; + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/GlyphBuilder.cs b/src/ImGuiColorTextEditNet/GlyphBuilder.cs new file mode 100644 index 0000000..cc8a293 --- /dev/null +++ b/src/ImGuiColorTextEditNet/GlyphBuilder.cs @@ -0,0 +1,55 @@ +using BUTR.CrashReport.ImGui.Utils; + +using System; +using System.Collections.Generic; + +namespace ImGuiColorTextEditNet; + +public class GlyphBuilder +{ + private readonly List _glyphs = new(); + + public void Append(char c, ColorPalette colorPalette) => _glyphs.Add(new Glyph(c, colorPalette.ColorIndex)); + + public void Append(char c, int count, ColorPalette colorPalette) + { + for (var i = 0; i < count; i++) + _glyphs.Add(new Glyph(c, colorPalette.ColorIndex)); + } + public void Append(ReadOnlySpan s, ColorPalette colorPalette) + { + foreach (var c in s) + _glyphs.Add(new Glyph(c, colorPalette.ColorIndex)); + } + public void Append(string s, ColorPalette colorPalette) + { + foreach (var c in s) + _glyphs.Add(new Glyph(c, colorPalette.ColorIndex)); + } + + public void Append(char c) => _glyphs.Add(new Glyph(c, 0)); + + public void Append(char c, int count) + { + for (var i = 0; i < count; i++) + _glyphs.Add(new Glyph(c, 0)); + } + + public void Append(ReadOnlySpan s) + { + foreach (var c in s) + _glyphs.Add(new Glyph(c, 0)); + } + + public void Append(string s) + { + foreach (var c in s) + _glyphs.Add(new Glyph(c, 0)); + } + + public void AppendLine() => _glyphs.Add(new Glyph('\n', 0)); + + public Span AsSpan() => _glyphs.AsSpan(); + + public void Clear() => _glyphs.Clear(); +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/ITextEditor.cs b/src/ImGuiColorTextEditNet/ITextEditor.cs new file mode 100644 index 0000000..e0802a5 --- /dev/null +++ b/src/ImGuiColorTextEditNet/ITextEditor.cs @@ -0,0 +1,11 @@ +using ImGuiColorTextEditNet.Editor; + +namespace ImGuiColorTextEditNet; + +internal interface ITextEditor +{ + TextEditorText Text { get; } + TextEditorSelection Selection { get; } + TextEditorMovement Movement { get; } + ITextEditorRenderer Renderer { get; } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/ITextEditorKeyboardInput.cs b/src/ImGuiColorTextEditNet/ITextEditorKeyboardInput.cs new file mode 100644 index 0000000..793152f --- /dev/null +++ b/src/ImGuiColorTextEditNet/ITextEditorKeyboardInput.cs @@ -0,0 +1,6 @@ +namespace ImGuiColorTextEditNet; + +internal interface ITextEditorKeyboardInput +{ + void HandleKeyboardInputs(); +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/ITextEditorMouseInput.cs b/src/ImGuiColorTextEditNet/ITextEditorMouseInput.cs new file mode 100644 index 0000000..2cbd0e7 --- /dev/null +++ b/src/ImGuiColorTextEditNet/ITextEditorMouseInput.cs @@ -0,0 +1,6 @@ +namespace ImGuiColorTextEditNet; + +internal interface ITextEditorMouseInput +{ + void HandleMouseInputs(); +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/ImGuiColorTextEditNet.csproj b/src/ImGuiColorTextEditNet/ImGuiColorTextEditNet.csproj new file mode 100644 index 0000000..16d56a5 --- /dev/null +++ b/src/ImGuiColorTextEditNet/ImGuiColorTextEditNet.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0;net6.0;net8.0;net9.0 + latest + enable + Cam Sinclair, BalazsJako + + A multi-line ImGui text editor control supporting syntax highlighting + 2023 Cam Sinclair + ImGuiColorTextEditNet + MIT + https://github.com/csinkers/ImGuiColorTextEditNet + imgui + https://github.com/csinkers/ImGuiColorTextEditNet + Git + 0.1.6 + + $(DefineConstants);FeatureMemory;FeatureValueTuple; + + + + + + + + + + + diff --git a/src/ImGuiColorTextEditNet/Line.cs b/src/ImGuiColorTextEditNet/Line.cs new file mode 100644 index 0000000..54bf2e7 --- /dev/null +++ b/src/ImGuiColorTextEditNet/Line.cs @@ -0,0 +1,27 @@ +using BUTR.CrashReport.ImGui.Utils; + +using System; +using System.Collections.Generic; + +namespace ImGuiColorTextEditNet; + +internal sealed class Line +{ + public static Line Empty => new(ReadOnlySpan.Empty); + + public List Glyphs { get; init; } + + public Line(List glyphs) + { + Glyphs = glyphs ?? throw new ArgumentNullException(nameof(glyphs)); + } + + public Line(ReadOnlySpan text) + { + Glyphs = new(); + for (var i = 0; i < text.Length; i++) + Glyphs.Add(new Glyph(text[i], 0)); + } + + public Span AsSpan() => Glyphs.AsSpan(); +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/SelectionMode.cs b/src/ImGuiColorTextEditNet/SelectionMode.cs new file mode 100644 index 0000000..ad22d03 --- /dev/null +++ b/src/ImGuiColorTextEditNet/SelectionMode.cs @@ -0,0 +1,8 @@ +namespace ImGuiColorTextEditNet; + +internal enum SelectionMode +{ + Normal, + Word, + Line, +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/SelectionState.cs b/src/ImGuiColorTextEditNet/SelectionState.cs new file mode 100644 index 0000000..bdcc564 --- /dev/null +++ b/src/ImGuiColorTextEditNet/SelectionState.cs @@ -0,0 +1,10 @@ +namespace ImGuiColorTextEditNet; + +internal struct SelectionState +{ + public Coordinates Start; + public Coordinates End; + public Coordinates Cursor; + + public override string ToString() => $"SEL [{Start}-{End}] CUR {Cursor}"; +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/StandardKeyboardInput.cs b/src/ImGuiColorTextEditNet/StandardKeyboardInput.cs new file mode 100644 index 0000000..1dc64a1 --- /dev/null +++ b/src/ImGuiColorTextEditNet/StandardKeyboardInput.cs @@ -0,0 +1,58 @@ +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Extensions; +using BUTR.CrashReport.ImGui.Structures; + +using System; + +namespace ImGuiColorTextEditNet; + +internal class StandardKeyboardInput : ITextEditorKeyboardInput + where TImGuiIORef : IImGuiIO +{ + private readonly IImGui _imGui; + private readonly IImGuiWithImGuiIO _imGuiWithImGuiIO; + private readonly ITextEditor _editor; + public StandardKeyboardInput(IImGui imGui, IImGuiWithImGuiIO imGuiWithImGuiIO, ITextEditor editor) + { + _imGui = imGui; + _imGuiWithImGuiIO = imGuiWithImGuiIO; + _editor = editor ?? throw new ArgumentNullException(nameof(editor)); + } + + public void HandleKeyboardInputs() + { + if (!_imGui.IsWindowFocused()) + return; + + _imGuiWithImGuiIO.GetIO(out var io); + var shift = _imGui.IsKeyDown(ImGuiKey.LeftShift) || _imGui.IsKeyDown(ImGuiKey.RightShift); + var ctrl = io.ConfigMacOSXBehaviors + ? _imGui.IsKeyDown(ImGuiKey.LeftSuper) || _imGui.IsKeyDown(ImGuiKey.RightSuper) + : _imGui.IsKeyDown(ImGuiKey.LeftCtrl) || _imGui.IsKeyDown(ImGuiKey.RightCtrl); + + io.WantCaptureKeyboard = true; + io.WantTextInput = true; + + switch (ctrl, shift) + { + case (false, _) when _imGui.IsKeyPressed(ImGuiKey.UpArrow): _editor.Movement.MoveUp(1, shift); break; + case (false, _) when _imGui.IsKeyPressed(ImGuiKey.DownArrow): _editor.Movement.MoveDown(1, shift); break; + case (_, _) when _imGui.IsKeyPressed(ImGuiKey.LeftArrow): _editor.Movement.MoveLeft(1, shift, ctrl); break; + case (_, _) when _imGui.IsKeyPressed(ImGuiKey.RightArrow): _editor.Movement.MoveRight(1, shift, ctrl); break; + case (_, _) when _imGui.IsKeyPressed(ImGuiKey.PageUp): _editor.Movement.MoveUp(_editor.Renderer.PageSize - 4, shift); break; + case (_, _) when _imGui.IsKeyPressed(ImGuiKey.PageDown): _editor.Movement.MoveDown(_editor.Renderer.PageSize - 4, shift); break; + case (true, _) when _imGui.IsKeyPressed(ImGuiKey.Home): _editor.Movement.MoveToStartOfFile(shift); break; + case (true, _) when _imGui.IsKeyPressed(ImGuiKey.End): _editor.Movement.MoveToEndOfFile(shift); break; + case (false, _) when _imGui.IsKeyPressed(ImGuiKey.Home): _editor.Movement.MoveToStartOfLine(shift); break; + case (false, _) when _imGui.IsKeyPressed(ImGuiKey.End): _editor.Movement.MoveToEndOfLine(shift); break; + case (true, false) when _imGui.IsKeyPressed(ImGuiKey.A): _editor.Selection.SelectAll(); break; + case (true, false) when _imGui.IsKeyPressed(ImGuiKey.C): + { + using var memory = _editor.Selection.GetSelectedText(); + _imGui.SetClipboardText(memory.Memory.Span); + break; + } + } + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/StandardMouseInput.cs b/src/ImGuiColorTextEditNet/StandardMouseInput.cs new file mode 100644 index 0000000..4c868bb --- /dev/null +++ b/src/ImGuiColorTextEditNet/StandardMouseInput.cs @@ -0,0 +1,82 @@ +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; + +using System; + +namespace ImGuiColorTextEditNet; + +internal class StandardMouseInput : ITextEditorMouseInput + where TImGuiIORef : IImGuiIO +{ + private readonly IImGui _imGui; + private readonly IImGuiWithImGuiIO _imGuiWithImGuiIO; + private readonly ITextEditor _editor; + + public StandardMouseInput(IImGui imGui, IImGuiWithImGuiIO imGuiWithImGuiIO, ITextEditor editor) + { + _imGui = imGui; + _imGuiWithImGuiIO = imGuiWithImGuiIO; + _editor = editor ?? throw new ArgumentNullException(nameof(editor)); + } + + public void HandleMouseInputs() + { + _imGuiWithImGuiIO.GetIO(out var io); + var shift = io.KeyShift; + var ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + var alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; + + if (!_imGui.IsWindowHovered()) + return; + + _imGui.SetMouseCursor(ImGuiMouseCursor.TextInput); + + if (shift || alt) + return; + + var click = _imGui.IsMouseClicked(0); + var doubleClick = _imGui.IsMouseDoubleClicked(0); + + /* Left mouse button double click */ + if (doubleClick) + { + if (!ctrl) + { + _imGui.GetMousePos(out var mousePos); + _editor.Renderer.ScreenPosToCoordinates(in mousePos, out _editor.Selection.Cursor); + _editor.Selection.InteractiveStart = _editor.Selection.Cursor; + _editor.Selection.InteractiveEnd = _editor.Selection.Cursor; + _editor.Selection.Mode = _editor.Selection.Mode == SelectionMode.Line ? SelectionMode.Normal : SelectionMode.Word; + _editor.Selection.Select(in _editor.Selection.InteractiveStart, in _editor.Selection.InteractiveEnd, _editor.Selection.Mode); + } + } + else if (click) /* Left mouse button click */ + { + _imGui.GetMousePos(out var mousePos); + _editor.Renderer.ScreenPosToCoordinates(in mousePos, out _editor.Selection.Cursor); + _editor.Selection.InteractiveStart = _editor.Selection.Cursor; + _editor.Selection.InteractiveEnd = _editor.Selection.Cursor; + _editor.Selection.Mode = ctrl ? SelectionMode.Word : SelectionMode.Normal; + _editor.Selection.Select(in _editor.Selection.Cursor, in _editor.Selection.Cursor, _editor.Selection.Mode); + } + else if (_imGui.IsMouseDragging(0) && _imGui.IsMouseDown(0)) // Mouse left button dragging (=> update selection) + { + io.WantCaptureMouse = true; + _imGui.GetMousePos(out var mousePos); + _editor.Renderer.ScreenPosToCoordinates(in mousePos, out _editor.Selection.Cursor); + if (_editor.Selection.Cursor < _editor.Selection.InteractiveStart) + { + _editor.Selection.InteractiveEnd = _editor.Selection.InteractiveStart; + _editor.Selection.Select(in _editor.Selection.Cursor, in _editor.Selection.InteractiveStart, _editor.Selection.Mode); + } + else + { + _editor.Selection.InteractiveEnd = _editor.Selection.Cursor; + _editor.Selection.Select(in _editor.Selection.InteractiveStart, in _editor.Selection.Cursor, _editor.Selection.Mode); + } + //_editor.Selection.InteractiveEnd = _editor.Selection.Cursor; + //_editor.Selection.Select(in _editor.Selection.InteractiveStart, in _editor.Selection.InteractiveEnd, _editor.Selection.Mode); + } + } +} \ No newline at end of file diff --git a/src/ImGuiColorTextEditNet/TextEditor.cs b/src/ImGuiColorTextEditNet/TextEditor.cs new file mode 100644 index 0000000..cf7b04c --- /dev/null +++ b/src/ImGuiColorTextEditNet/TextEditor.cs @@ -0,0 +1,75 @@ +using BUTR.CrashReport.ImGui; +using BUTR.CrashReport.ImGui.Enums; +using BUTR.CrashReport.ImGui.Structures; +using BUTR.CrashReport.Memory.Utils; + +using ImGuiColorTextEditNet.Editor; + +using System; +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Text; + +namespace ImGuiColorTextEditNet; + +public class TextEditor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] TColorPalette, TImGuiIORef, TImDrawListRef, TImGuiStyleRef, TColorsRangeAccessorRef, TImGuiListClipperRef> : ITextEditor + where TColorPalette : ColorPalette + + where TImGuiIORef : IImGuiIO + + where TImDrawListRef : IImDrawList + + where TImGuiStyleRef : IImGuiStyle + where TColorsRangeAccessorRef : IRangeAccessor + where TImGuiListClipperRef : IImGuiListClipper +{ + internal TextEditorText Text { get; } + TextEditorText ITextEditor.Text => Text; + + internal TextEditorSelection Selection { get; } + TextEditorSelection ITextEditor.Selection => Selection; + + internal ITextEditorRenderer Renderer { get; } + ITextEditorRenderer ITextEditor.Renderer => Renderer; + internal TextEditorMovement Movement { get; } + TextEditorMovement ITextEditor.Movement => Movement; + + public TextEditor( + IImGui imGui, + IImGuiWithImGuiIO imGuiWithImGuiIO, + IImGuiWithImDrawList imGuiWithImDrawList, + IImGuiWithImGuiStyle imGuiWithImGuiStyle, + IImGuiWithImGuiListClipper imGuiWithImGuiListClipper) + { + Text = new TextEditorText(); + Selection = new TextEditorSelection(Text); + Movement = new TextEditorMovement(Selection, Text); + Renderer = new TextEditorRenderer(imGui, imGuiWithImDrawList, imGuiWithImGuiStyle, imGuiWithImGuiListClipper, this) + { + KeyboardInput = new StandardKeyboardInput(imGui, imGuiWithImGuiIO, this), + MouseInput = new StandardMouseInput(imGui, imGuiWithImGuiIO, this) + }; + } + + public void Render(string utf16Title, in Vector2 size = new()) + { + var utf8ByteCount = Encoding.UTF8.GetMaxByteCount(utf16Title.Length) + 1; + var tempMemory = utf8ByteCount > 2048 ? MemoryPool.Shared.Rent(utf8ByteCount) : null; + var utf8 = utf8ByteCount <= 2048 ? stackalloc byte[utf8ByteCount] : tempMemory!.Memory.Span; + var length = Utf8Utils.Utf16ToUtf8(utf16Title, utf8); + utf8[length] = 0; + + Renderer.Render(utf8, in size); + } + + public void Render(ReadOnlySpan utf8Title, in Vector2 size = new()) => Renderer.Render(utf8Title, in size); + + public void AddGlyphs(Span glyphs) => Text.AddGlyphs(glyphs); + + public void AddExceptionLine(int line) => Text.ExceptionLines.Add(line); + + public void SetPalette(ColorPaletteIndex colorPaletteIndex) => Renderer.SetPalette(colorPaletteIndex); + + public GlyphBuilder CreateGlyphBuilder() => new(); +} \ No newline at end of file diff --git a/src/nuget.config b/src/nuget.config index 776bd66..446ce05 100644 --- a/src/nuget.config +++ b/src/nuget.config @@ -2,9 +2,10 @@ - + +