diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 105c008..4b8de37 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,10 +36,13 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 - # Create the NuGet package in the folder from the environment variable NuGetDirectory - - run: dotnet pack src/MindControl/MindControl.csproj --configuration Release --output ${{ env.NuGetDirectory }} + # Create the NuGet packages in the folder from the environment variable NuGetDirectory + - name: Create NuGet packages + run: | + dotnet pack src/MindControl/MindControl.csproj --configuration Release --output ${{ env.NuGetDirectory }} + dotnet pack src/MindControl.Code/MindControl.Code.csproj --configuration Release --output ${{ env.NuGetDirectory }} - # Publish the NuGet package as an artifact, so they can be used in the following jobs + # Publish the NuGet packages as an artifact, so they can be used in the following jobs - uses: actions/upload-artifact@v4 with: name: nuget @@ -77,16 +80,26 @@ jobs: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 - - # Build the InjectedLibrary C++ project that is required by the test project. + - name: Restore NuGet packages + run: dotnet restore + + # Build the InjectedLibrary C++ project in both x64 and x86 configurations - name: Setup MSBuild uses: microsoft/setup-msbuild@v2 - - name: Build the C++ injection test DLL - run: msbuild test/MindControl.Test.InjectedLibrary/MindControl.Test.InjectedLibrary.vcxproj /p:Configuration=Release /p:Platform=x64 /p:OutDir=../MindControl.Test/bin/Release/ - + - name: Build the C++ injection test DLL x64 + run: msbuild test/MindControl.Test.InjectedLibrary/MindControl.Test.InjectedLibrary.vcxproj /p:Configuration=Release /p:Platform=x64 /p:OutDir=../MindControl.Test/bin/Release/InjectedLibrary/x64/ + - name: Build the C++ injection test DLL x86 + run: msbuild test/MindControl.Test.InjectedLibrary/MindControl.Test.InjectedLibrary.vcxproj /p:Configuration=Release /p:Platform=x86 /p:OutDir=../MindControl.Test/bin/Release/InjectedLibrary/x86/ + + # Build the test target project in x64 and x86 + - name: Build the test target project x64 + run: dotnet publish test/MindControl.Test.TargetApp/MindControl.Test.TargetApp.csproj --configuration Release --runtime win-x64 --self-contained true --output ${{ github.workspace }}/test/MindControl.Test/bin/Release/TargetApp/x64/ + - name: Build the test target project x86 + run: dotnet publish test/MindControl.Test.TargetApp/MindControl.Test.TargetApp.csproj --configuration Release --runtime win-x86 --self-contained true --output ${{ github.workspace }}/test/MindControl.Test/bin/Release/TargetApp/x86/ + # Run the tests - name: Run tests - run: dotnet test test/MindControl.Test/MindControl.Test.csproj --configuration Release + run: dotnet test test/MindControl.Test/MindControl.Test.csproj --configuration Release --logger "console;verbosity=detailed" deploy: # Publish only when creating a GitHub Release @@ -110,4 +123,26 @@ jobs: run: | foreach($file in (Get-ChildItem "${{ env.NuGetDirectory }}" -Recurse -Include *.nupkg)) { dotnet nuget push $file --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate - } \ No newline at end of file + } + publish_docs: + if: github.event_name == 'release' + runs-on: windows-latest + needs: [run_test] + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Build documentation + run: docfx docs/docfx.json + + - name: Publish to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/_site + publish_branch: gh-pages \ No newline at end of file diff --git a/.gitignore b/.gitignore index 888bf30..4c61720 100644 --- a/.gitignore +++ b/.gitignore @@ -365,3 +365,7 @@ MigrationBackup/ FodyWeavers.xsd /CommandCenter.App/.config /CommandCenter.App/Plugins/ + +# docfx +docs/_site/ +docs/api/ \ No newline at end of file diff --git a/MindControl.sln b/MindControl.sln index 9ede659..25e89f7 100644 --- a/MindControl.sln +++ b/MindControl.sln @@ -11,30 +11,111 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MindControl.Test.TargetApp" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MindControl.Test.InjectedLibrary", "test\MindControl.Test.InjectedLibrary\MindControl.Test.InjectedLibrary.vcxproj", "{AA8C9AF6-7C31-43D1-A519-DBB1C3252127}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindControl.Benchmark", "test\MindControl.Benchmark\MindControl.Benchmark.csproj", "{5C1B693D-D176-41B2-A47A-E78E098171CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindControl.Code", "src\MindControl.Code\MindControl.Code.csproj", "{BF166555-9220-42C6-A93E-EC9FBA9AC38C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{751C861A-AA2D-454F-8910-E27040CDEF6B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{1D218910-68CD-4D94-B928-A0ADE5A600D5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{0923ADDB-5517-4ABF-A38E-CC3776C85A71}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindControl.Samples.SrDemoConsoleApp", "samples\MindControl.Samples.SrDemoConsoleApp\MindControl.Samples.SrDemoConsoleApp.csproj", "{B7E8F1B0-F5D2-4943-B780-CB1C0C248824}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindControl.Samples.SrDemoWpfApp", "samples\MindControl.Samples.SrDemoWpfApp\MindControl.Samples.SrDemoWpfApp.csproj", "{09A4F714-3F5A-4CD8-94D9-902BBDE5A090}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindControl.Samples.SlimeRancherDemo", "samples\MindControl.Samples.SlimeRancherDemo\MindControl.Samples.SlimeRancherDemo.csproj", "{8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindControl.Samples.SrDemoBlazorApp", "samples\MindControl.Samples.SrDemoBlazorApp\MindControl.Samples.SrDemoBlazorApp.csproj", "{125FFA82-F622-4981-8D9F-C5FB7CE87D56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A13C976F-5866-48C4-904E-2C1960220FAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A13C976F-5866-48C4-904E-2C1960220FAC}.Debug|Any CPU.Build.0 = Debug|Any CPU {A13C976F-5866-48C4-904E-2C1960220FAC}.Release|Any CPU.ActiveCfg = Release|Any CPU {A13C976F-5866-48C4-904E-2C1960220FAC}.Release|Any CPU.Build.0 = Release|Any CPU + {A13C976F-5866-48C4-904E-2C1960220FAC}.Debug|x86.ActiveCfg = Debug|Any CPU {2BE5C902-70EC-4DBE-B782-FB0623518F96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2BE5C902-70EC-4DBE-B782-FB0623518F96}.Debug|Any CPU.Build.0 = Debug|Any CPU {2BE5C902-70EC-4DBE-B782-FB0623518F96}.Release|Any CPU.ActiveCfg = Release|Any CPU {2BE5C902-70EC-4DBE-B782-FB0623518F96}.Release|Any CPU.Build.0 = Release|Any CPU + {2BE5C902-70EC-4DBE-B782-FB0623518F96}.Debug|x86.ActiveCfg = Debug|Any CPU {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Debug|Any CPU.Build.0 = Debug|Any CPU {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Release|Any CPU.Build.0 = Release|Any CPU + {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Debug|x86.ActiveCfg = Debug|x86 + {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Debug|x86.Build.0 = Debug|x86 + {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Release|x86.ActiveCfg = Release|x86 + {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F}.Release|x86.Build.0 = Release|x86 {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Debug|Any CPU.ActiveCfg = Debug|x64 {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Debug|Any CPU.Build.0 = Debug|x64 - {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Release|Any CPU.ActiveCfg = Release|Win32 - {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Release|Any CPU.Build.0 = Release|Win32 + {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Release|Any CPU.ActiveCfg = Release|x64 + {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Release|Any CPU.Build.0 = Release|x64 + {AA8C9AF6-7C31-43D1-A519-DBB1C3252127}.Debug|x86.ActiveCfg = Debug|Win32 + {5C1B693D-D176-41B2-A47A-E78E098171CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C1B693D-D176-41B2-A47A-E78E098171CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C1B693D-D176-41B2-A47A-E78E098171CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C1B693D-D176-41B2-A47A-E78E098171CB}.Release|Any CPU.Build.0 = Release|Any CPU + {5C1B693D-D176-41B2-A47A-E78E098171CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {BF166555-9220-42C6-A93E-EC9FBA9AC38C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF166555-9220-42C6-A93E-EC9FBA9AC38C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF166555-9220-42C6-A93E-EC9FBA9AC38C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF166555-9220-42C6-A93E-EC9FBA9AC38C}.Release|Any CPU.Build.0 = Release|Any CPU + {BF166555-9220-42C6-A93E-EC9FBA9AC38C}.Debug|x86.ActiveCfg = Debug|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Release|Any CPU.Build.0 = Release|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Debug|x86.ActiveCfg = Debug|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Debug|x86.Build.0 = Debug|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Release|x86.ActiveCfg = Release|Any CPU + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824}.Release|x86.Build.0 = Release|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Release|Any CPU.Build.0 = Release|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Debug|x86.ActiveCfg = Debug|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Debug|x86.Build.0 = Debug|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Release|x86.ActiveCfg = Release|Any CPU + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090}.Release|x86.Build.0 = Release|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Release|Any CPU.Build.0 = Release|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Debug|x86.Build.0 = Debug|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Release|x86.ActiveCfg = Release|Any CPU + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35}.Release|x86.Build.0 = Release|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Release|Any CPU.Build.0 = Release|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Debug|x86.ActiveCfg = Debug|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Debug|x86.Build.0 = Debug|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Release|x86.ActiveCfg = Release|Any CPU + {125FFA82-F622-4981-8D9F-C5FB7CE87D56}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5C1B693D-D176-41B2-A47A-E78E098171CB} = {751C861A-AA2D-454F-8910-E27040CDEF6B} + {2BE5C902-70EC-4DBE-B782-FB0623518F96} = {751C861A-AA2D-454F-8910-E27040CDEF6B} + {AA8C9AF6-7C31-43D1-A519-DBB1C3252127} = {751C861A-AA2D-454F-8910-E27040CDEF6B} + {9FE5EF8E-1230-42DB-A6B2-0C2633D32A1F} = {751C861A-AA2D-454F-8910-E27040CDEF6B} + {A13C976F-5866-48C4-904E-2C1960220FAC} = {1D218910-68CD-4D94-B928-A0ADE5A600D5} + {BF166555-9220-42C6-A93E-EC9FBA9AC38C} = {1D218910-68CD-4D94-B928-A0ADE5A600D5} + {B7E8F1B0-F5D2-4943-B780-CB1C0C248824} = {0923ADDB-5517-4ABF-A38E-CC3776C85A71} + {09A4F714-3F5A-4CD8-94D9-902BBDE5A090} = {0923ADDB-5517-4ABF-A38E-CC3776C85A71} + {8E00D032-5E48-4DA3-B2BF-C3DC3C57EA35} = {0923ADDB-5517-4ABF-A38E-CC3776C85A71} + {125FFA82-F622-4981-8D9F-C5FB7CE87D56} = {0923ADDB-5517-4ABF-A38E-CC3776C85A71} + EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 8617fbb..2d663ae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # MindControl +[![NuGet Version](https://img.shields.io/nuget/v/mindcontrol?style=for-the-badge)](https://www.nuget.org/packages/mindcontrol)[![Downloads](https://img.shields.io/nuget/dt/mindcontrol?style=for-the-badge)](https://www.nuget.org/packages/mindcontrol)[![License](https://img.shields.io/github/license/Doublevil/mind-control?style=for-the-badge)](https://github.com/Doublevil/mind-control/blob/main/LICENSE) + MindControl is a .net hacking library for Windows that allows you to manipulate a game or any other process and its internal memory. > **DO NOT use this library to cheat in online competitive games. Cheaters ruins the fun for everyone. If you ignore this warning, I will do my best to shut down your project.** @@ -9,58 +11,39 @@ MindControl is a .net hacking library for Windows that allows you to manipulate Here is a quick example to get you started. ```csharp -var myGame = ProcessMemory.OpenProcess("mygame.exe"); // A process with this name must be running +var myGame = ProcessMemory.OpenProcess("mygame").Result; // A process with this name must be running var hpAddress = new PointerPath("mygame.exe+1D005A70,1C,8"); // See the docs for how to determine these // Read values -int currentHp = myGame.ReadInt(hpAddress); +var currentHp = myGame.Read(hpAddress); Console.WriteLine($"You have {currentHp}HP"); // Example output: "You have 50HP" // Write values -myGame.WriteInt(hpAddress, 9999); +myGame.Write(hpAddress, 9999); // Find the first occurrence of a pattern in memory, with wildcard bytes UIntPtr targetAddress = myGame.FindBytes("4D 79 ?? ?? ?? ?? ?? ?? 56 61 6C 75 65") .FirstOrDefault(); + +// ... And many more features ``` -See [the documentation](doc/GetStarted.md) for more information. +See [the documentation](https://doublevil.github.io/mind-control/guide/introduction.html) to get started, whether you already dabble in memory hacking or are completely new to it. ## Features - Attach to any process easily by name or PID - Address memory either through simple pointer addresses, or through dynamic pointer paths (e.g. `mygame.exe+1D005A70,1C,8`) -- Read a memory address as a byte array, boolean, or any basic number types -- Read a memory address as a string as simply as possible, or as complex as you need -- Write byte arrays, booleans and basic number types at any memory address +- Read and write byte arrays, booleans, numbers of any kind, strings and structures - Inject DLLs to execute arbitrary code in the target process -- Search for byte patterns in the target process memory +- Search for byte sequences or patterns in the target process memory +- Manage memory allocations manually or automatically, to store data or code in the target process +- Insert assembly code with hooks +- Replace or remove existing code +- Start threads in the target process - Designed for performance and simplicity of use - Unit tested and made with care ## Comparison with other libraries -MindControl is a small library that focuses on the most common use cases for hacking. - -It is not as feature-rich as the most used .net hacking library, [memory.dll](https://github.com/erfg12/memory.dll/), but it aims to be easier to use, have comparable performance, and most importantly to be more reliable and maintainable. - -If you are considering MindControl but unsure if it has the features you need, here is a comparison table. - -| Feature | MindControl | memory.dll -|-----------------------------|--- |--- | -| **Handle pointer paths** | ✔️ | ✔️ | -| **Read primitive types** | ✔️ | ✔️ | -| **Write primitive types** | ✔️ | ✔️ | -| **Read strings** | ✔️ | ✔️ | -| **Write strings** | ❌ | ✔️ | -| **Array of bytes scanning** | ✔️ | ✔️ | -| **Inject DLLs** | ✔️ | ✔️ | -| **State watchers** | ✔️ | ❌ | -| **Create code caves** | ❌ | ✔️ | -| **Bind to UI elements** | ❌ | ✔️ | -| **Freeze values** | ❌ | ✔️ | -| **Set focus to process** | ❌ | ✔️ | -| **Load from .ini file** | ❌ | ✔️ | -| **Suspend process** | ❌ | ✔️ | -| **Dump process memory** | ❌ | ✔️ | -| **Manipulate threads** | ❌ | ✔️ | +MindControl has a lot in common with other .net hacking libraries, the most popular one being [memory.dll](https://github.com/erfg12/memory.dll/). While the latter focuses on practicality and has some bias towards a specific use-case (game trainers), MindControl primarily aims to be more generic, reliable and maintainable, generally easier to understand and to use, and with similar or better performance. diff --git a/doc/FindBytes.md b/doc/FindBytes.md deleted file mode 100644 index 110168d..0000000 --- a/doc/FindBytes.md +++ /dev/null @@ -1,123 +0,0 @@ -# Finding patterns in memory with FindBytes - -MindControl allows you to search for byte patterns in the memory of your target process. This can be useful to find the location of a value you want to hack, or to find a function you want to hook. - -```csharp -var myGame = ProcessMemory.OpenProcess("mygame"); -UIntPtr targetAddress = myGame.FindBytes("4D 79 ?? ?? ?? ?? ?? ?? 56 61 6C 75 65") - .FirstOrDefault(); -``` - -This is also called "AoB scanning" (where AoB stands for "Array of bytes"). - -## Pattern syntax - -The pattern you provide to `FindBytes` is a string of hexadecimal bytes, optionally separated by spaces. Each byte can be a specific value (e.g. `4D`), or a wildcard (e.g. `??`). The wildcard will match any byte. - -You can also use partial wildcards, for example `4?` or `?D` to match any byte that starts with `4` or ends with `D`. - -For better performances, avoid starting your pattern with a wildcard. - -## Search range - -By default, `FindBytes` will search the entire memory of the target process. This can be slow, especially if the process has a lot of memory. - -You can limit the search to a specific range of memory by using the `range` optional parameter. - -The example below demonstrates how to search only in the range of a specific module. - -```csharp -var myGame = ProcessMemory.OpenProcess("mygame"); - -// Get the range for the module that contains the game executable -// Instead of "mygame.exe", you can use the name of any module loaded in the process (usually a DLL) -var myModuleRange = myGame.Modules.FirstOrDefault(m => m.Name == "mygame.exe"); -List results = myGame.FindBytes("4D 79 ?? ?? 65", range: myModuleRange) - .ToList(); -``` - -You can also specify a custom range by creating a `MemoryRange` instance: - -```csharp - // Both of the following ranges can be used to search from 0x5000 to 0x5FFF included -var rangeWithStartAndEnd = new MemoryRange(0x5000, 0x5FFF); -var rangeWithStartAndSize = MemoryRange.FromStartAndSize(0x5000, 0x1000); -``` - -To optimize the performance of your search, you should always specify a range. Usually, you can figure out at least the module that contains the value you are looking for, but the more specific you can be, the faster the search will be. - -## Search parameters - -The `FindBytes` method takes an optional `FindBytesSetting` parameter that allows you to specify what kind of memory you want to search, and limit the number of results. - -Depending on what kind of data you are looking for, you can specify constraints on the kind of memory you want to search. For example, you can search only executable memory, or only memory that is writable. - -Here is an example of how to search for a pattern in the executable code of your target process: - -```csharp -var searchSettings = new FindBytesSettings -{ - SearchExecutable = true, - SearchWritable = false -}; -List codeResults = myGame.FindBytes("4D 79 ?? ?? 65", settings: searchSettings) - .ToList(); -``` - -Specifying these settings has multiple benefits: -- It will make the search faster, as it will skip memory that is not relevant to your search. -- It will make the search more reliable, as it will avoid returning results that are not useful to you. - -## Using the results - -The `FindBytes` method returns an `IEnumerable` that contains the addresses of the memory locations where the pattern was found. You can then use these addresses to read or write memory, or to hook functions. - -Here is an example of how to read a value at the first address found, with an offset: - -```csharp -var myGame = ProcessMemory.OpenProcess("mygame"); -UIntPtr targetAddress = myGame.FindBytes("4D 79 28 2A ?? ?? ?? ?? 75 65") - .FirstOrDefault(); - -// The line below reads the actual value of the wildcard part of the pattern, skipping the first 4 bytes -int value = myGame.ReadInt(targetAddress + 0x4); -``` - -When using the results of `FindBytes`, be careful of multiple enumerations. If you need to use the results multiple times, consider storing them in a list: - -```csharp -var results = myGame.FindBytes("4D 79 ?? ?? 65").ToList(); -``` - -If you only need the first result, you can use `FirstOrDefault` as shown in the examples above. This will ensure that the search stops as soon as a match is found, which can speed up the execution dramatically, depending on your use case. - -## Preventing hangs - -If you are searching for a pattern in a large memory range, the search can take a long time. - -If you are running this code in a UI application, your application might become unresponsive during the search. - -To prevent this, you should use the asynchronous variant `FindByesAsync`, which returns an `IAsyncEnumerable`. - -Here is an example of how to prevent your application from hanging during the search: - -```csharp -private async Task OnButtonClick() -{ - var results = myGame.FindBytesAsync("4D 79 ?? ?? 65"); - await foreach (UIntPtr result in results) - { - // Do something with the result - } -} -``` - -If you want to only get the first result, or work with an array or a list of results, you can use the `FirstOrDefaultAsync`, `ToArrayAsync`, or `ToListAsync` extension methods from the `System.Linq.Async` package. - -```csharp -private async Task OnButtonClick() -{ - UIntPtr result = await myGame.FindBytesAsync("4D 79 ?? ?? 65").FirstOrDefaultAsync(); - // Do something with the result -} -``` diff --git a/doc/GetStarted.md b/doc/GetStarted.md deleted file mode 100644 index 37b2dd8..0000000 --- a/doc/GetStarted.md +++ /dev/null @@ -1,129 +0,0 @@ -# Get started using MindControl - -> **DO NOT use this library to cheat in online competitive games. Cheaters ruins the fun for everyone. If you ignore this warning, I will do my best to shut down your project.** - -This documentation aims to provide you with basic directions on how to get started hacking a process with the MindControl library. - -## Basic principle - -Let's go through the basics of hacking a health value in an imaginary game. - -```csharp -// Open a process named "mygame" that is currently running -var myGame = ProcessMemory.OpenProcess("mygame"); - -// Build a path that points to the health value or anything else you want to hack -// To determine these, use tools like Cheat Engine (keep reading the docs for more info) -var hpAddress = new PointerPath("mygame.exe+1D005A70,1C,8"); - -// Read the health value. In this example, it's an integer, but it can be any basic type. -int currentHp = myGame.ReadInt(hpAddress); -Console.WriteLine($"You have {currentHp}HP"); // Example output: "You have 50HP" - -// Overwrite the health value of the game so that our health goes to 9999. -myGame.WriteInt(hpAddress, 9999); -``` - -This code should give you an idea of how the library works and how to build basic hacks. - -## Figuring out pointer paths - -The key in the example shown above is the pointer path, that allows us to reference the address of the health value in the internal memory of our imaginary game. - -The pointer path to use will be different for each case, and also need to be stable in order to work every time. To figure out those stable pointers, you will need to experiment with tools such as [Cheat Engine](https://www.cheatengine.org), or use pointers that others have figured out through this process. - -This documentation will not go through the process of finding stable pointer paths. Cheat Engine has tutorials, and there are lots of video resources out there that you can check out. - -Once you've figured out your stable pointer, just transcribe it into the PointerPath expression syntax and you're ready to hack! Here's a quick example of transcribing a pointer from Cheat Engine: - -![An example Cheat Engine pointer](res/CheatEngineExamplePointer.png) - -Just go from the bottom up, and separate every field with a `,`. - -The result: `"UnityPlayer.dll"+017CD668,1F0,1E8,4E0,DB8,B8,D0,8,68`. - -Stable pointers usually start with a module name, in this case `"UnityPlayer.dll"`, but you don't have to use one. Check out the [pointer path doc](PointerPath.md) if you need more info. - -## Gotchas and common pitfalls - -Hacking is hard and comes with a lot of surprises and hurdles. Take your time, make sure you understand what you're doing, and you'll get there eventually. - -Here are common reasons for your programs to fail: -- Your hacking program may need to be run as an administrator. This can be required to manipulate memory, disable protections, and other internal operations that MindControl does. -- You are getting/setting wrong values because your pointer path is not stable. Double-check with Cheat Engine that your pointer does work in the same circumstances. -- You cannot read string properly. Check out the [Manipulating strings](ManipulatingStrings.md) documentation. - -## Finding patterns in memory - -MindControl allows you to search for byte patterns in the memory of your target process. This can be useful to find the location of a value you want to hack, or to find a function you want to hook. - -```csharp -var myGame = ProcessMemory.OpenProcess("mygame"); -UIntPtr targetAddress = myGame.FindBytes("4D 79 ?? ?? ?? ?? ?? ?? 56 61 6C 75 65") - .FirstOrDefault(); -``` - -Find out more about this on the [FindBytes](FindBytes.md) documentation. - -## Handle process exit and restart - -The `ProcessMemory` class has a `ProcessDetached` event that you can use to react to your target process exiting or crashing. Note that it will also fire when disposing the instance. - -```csharp -var myGame = ProcessMemory.OpenProcess("mygame"); -myGame.ProcessDetached += (_, _) => { Console.WriteLine("Target process is detached."); } -``` - -However, a `ProcessMemory` instance that has been detached **cannot be reattached**. - -If you want to handle your target process exiting and potentially restarting (or starting after your hacking program), use the `ProcessTracker` class. - -```csharp -var tracker = new ProcessTracker("mygame"); -tracker.Attached += (_, _) => { Console.WriteLine("Target is attached."); } -tracker.Detached += (_, _) => { Console.WriteLine("Target is detached."); } - -var myGame = tracker.GetProcessMemory(); -``` - -The `GetProcessMemory` method will return an attached `ProcessMemory` instance, or null if the target process is not running. It will automatically handle target process restarts by creating a new `ProcessMemory` instance when the existing one has been detached. - -Just make sure to use the freshest instance from the tracker in your reading/writing methods, and not a stored `ProcessMemory` variable: - -```csharp -public MyHackingClass() -{ - Tracker = new ProcessTracker("mygame"); -} -public void Update() - => DoSomeHacking(Tracker.GetProcessMemory()); // ✓ This will handle restarts -``` - -VS - -```csharp -public MyHackingClass() -{ - var tracker = new ProcessTracker("mygame"); - MyGame = tracker.GetProcessMemory(); -} -public void Update() - => DoSomeHacking(MyGame); // ✖ This will NOT handle restarts. -``` - -## Inject libraries - -MindControl allows you to inject DLLs into the target process, which can be used to execute arbitrary code in the target process. - -```csharp -var myGame = ProcessMemory.OpenProcess("mygame"); -myGame.InjectLibrary("myhack.dll"); -``` - -For example, you can use named pipes to communicate between your hacking program and the target process (send commands to the game or receive data from it). - -## Tracking a process in real-time - -The `StateWatcher` class gives you a convenient way to access the internal data of your target process, with automatic refreshes. - -Check the [StateWatcher](StateWatcher.md) documentation to learn how to set it up. diff --git a/doc/ManipulatingStrings.md b/doc/ManipulatingStrings.md deleted file mode 100644 index 364f74c..0000000 --- a/doc/ManipulatingStrings.md +++ /dev/null @@ -1,47 +0,0 @@ -# Manipulating strings - -Strings are more complicated than the basic boolean and numeric types: they usually are object instances with their own memory allocation, they have a length, an encoding, and there is no globally consistent way to handle them. - -To take a couple of examples, programs using the .net framework will have strings stored with a 4-byte prefix indicating their length, and use the UTF-16 encoding and a \0 terminator, while programs made with Rust will likely use UTF-8 with no terminator and a 2-byte length prefix. - -## Reading strings - -In addition to the usual parameters, the `ReadString` method takes a max length and a `StringSettings` instance parameters. - -## Using the right StringSettings - -Once you have located the string you want to read in memory, you might find that calling the `ReadString` method with the default parameters works just as you would expect. - -If the result doesn't look right, read through the "Problems and solutions" section of this guide, experiment with various presets (e.g. `StringSettings.DefaultUtf8`) or try to figure out exactly what settings to use. - -To build your own `StringSettings` instance, just use the constructor and supply the following parameters: -- `encoding`: this should almost always be either `System.Text.Encoding.UTF8` or `System.Text.Encoding.Unicode` (which is UTF-16). You will recognize the latter when inspecting your target string if each character seems to take two bytes (usually, the second byte will be `00`). -- `isNullTerminated`: this specifies whether your strings are terminated with a `\0` character (the value 0, which is also called `null`). Some languages or frameworks read strings character-by-character until they encounter this terminator. Others rely on a length prefix. And sometimes you will have both. You can usually set this to `true` if you have a `00` at the end of your string, but it's usually better to set it to `false` if you have a length prefix (see the following parameter). -- `prefixSettings`: this is an instance of `StringLengthPrefixSettings`, that specifies if your string is length-prefixed, and if so, how to read that prefix. A length prefix is a number that comes right before the first character of your string and that is equal to the number of characters or bytes in your string. If your string doesn't seem to have one, set this parameter to `null`. Otherwise, you can build an instance with the given parameters: - - `prefixSize`: set this to the number of bytes of the length prefix. It will usually be either `2` or `4`. Check how long the prefix seems to be on your string with a memory inspector. - - `lengthUnit`: when reading a string, the number in the prefix will be multiplied by this parameter to obtain the number of bytes to read. So if the length prefix is the number of bytes, specify `1`. If the prefix reads 5 but your string is 10-bytes-long, specify `2`. If you omit this parameter or set it to `null`, it will be determined automatically. - -**Here are a few additional considerations:** -- The `maxSizeInBytes` parameter in `ReadString` is always applied, no matter what `StringSettings` you specify. It also has a default value, so be aware of that if you need to read a string that might be long. -- If your `StringSettings` specify a length prefix, your pointer path needs to point to the start of the prefix, not the first character of the string. If you ignore this, you will be getting results where the first characters are missing and the string length might not be right. - -### Problems and solutions - -- **The result I get from `ReadString` looks like a bunch of garbled, random characters** - - You are probably not using the right encoding. Try using `StringSettings.Default` and then `StringSettings.DefaultUtf8`. One of the two should either work, or leave you with more minor issues. - - If the solution above does not work, check your pointer path. - - -- **The result I get from `ReadString` is missing a few characters at the start** - - If your string has a length prefix, make sure that your pointer path points at the start of the length prefix, not at the first character of the string. Typically, your pointer path will end with `,C` when it should end with `,8`. - - If your string does not have a length prefix, make sure you specify a `StringSettings` instance with a null `PrefixSettings`. - - -- **The result I get from `ReadString` is longer than expected** - - Check if your string is length-prefixed, and make sure to use appropriate `StringSettings`, with a matching `StringLengthPrefixSettings` instance. - - If your string isn't length-prefixed, use a `StringSettings` that has `IsNullTerminated` set to `true`. - - -- **The result I get from `ReadString` cuts at about half the length** - - If you are using a `StringSettings` instance with a length prefix specification, try setting the `LengthUnit` of the `StringLengthPrefixSettings` instance to `2`. - - Check the `maxLength` parameter that you supply to `ReadString` (and remember that its value is in bytes, not characters). \ No newline at end of file diff --git a/doc/PointerPath.md b/doc/PointerPath.md deleted file mode 100644 index 51349b6..0000000 --- a/doc/PointerPath.md +++ /dev/null @@ -1,86 +0,0 @@ -# PointerPath expressions - -This guide will give you a comprehensive look at how to use the `PointerPath` class and its expressions. - -The goal of a pointer path is to detail a path in memory that you can follow to get to the address of a targeted value, by reading a series of pointers. They are used as an alternative to static addresses, that are usually not stable (meaning they do not always point to the right value in memory). - -Read the "Quick look" section below to learn about pretty much everything you need to know. There is usually no need to go further than that in most scenarios. - -## Quick look - -Here is an example of a PointerPath expression: - -`"mygame.exe"+1D5A10,1C,8` - -Let's decompose it: -- `"mygame.exe"` - -This is a module name. Most of the time, you should work with module names to get stable pointers. Modules are files loaded in memory at a particular address. This gives us a base address to start with. - -- `+1D5A10` - -This is the base module offset. It applies to the address of the module. In this case, our first pointer is at the address of the `"mygame.exe"` module, plus 1923600 (1D5A10) bytes. - -- `,1C` - -This is the offset to the second pointer in the path. You can tell by the `,` which separates each pointer to follow. In this case, the second pointer is 28 (1C) bytes after following the first one. - -- `,8` - -This is the offset to the third and final pointer in the path. The last pointer is 8 bytes after following the second one. - -### Evaluating the pointer path - -Evaluating a pointer path is the process of translating it to a static memory address, that we can read from or write to. Pointer paths are evaluated every time they are used. - -When evaluating our example pointer path, this is what happens: -1. The address of the `"mygame.exe"` module is determined through the process' module list -2. The first offset `1D5A10` is added to the address of the module -3. A pointer is read from the memory at the resulting address. -4. The second offset `1C` is added to the pointer read at the previous step. -5. A pointer is read from the memory at the resulting address. -6. The third offset `8` is added to the pointer read at the previous step. - -This gives us our final address that we can read from or write to. - -## Full syntax guide - -This section is here for a more comprehensive syntax breakdown. You should be able to handle most cases with the information in the previous section, but keep reading if you want to learn more advanced use cases. - -### Structure - -A PointerPath expression is basically comprised of a starting address, and a series of offsets. Each part is separated with a `,`. - -#### Starting address syntax - -An expression will always start with a starting address, that provides a first address to start with. It cannot contain any additional starting addresses. - -The starting address uses the same syntax as an offset, except it can also start with a module name. - -Typically, a module name looks like `"mygame.exe"`, but here are some other valid module names: `"mygame.data"`, `mygame.exe`, `mygame`, `my game`, `"1F8D"`. - -This highlights a few things: -- A module name does not necessarily start and end with `"` (unless it's otherwise a valid hexadecimal number as in that last example). These `"` characters are trimmed during evaluation to find out the actual module name. -- The module name may have any extension, or no extension at all. -- The module name may contain spaces. - -In addition to the module name, a starting address may, like any offset, have any number of static offsets chained together with `+` or `-` operators. Here are some valid examples of a starting address: `"mygame.exe"+0F`, `mygame.exe-0F`, `mygame+1C-4+2`. See the "Offset syntax" section below for more info. - -It shall be noted that a starting address does not necessarily start with a module name. It may also be any static address. Here is a couple of examples of full, valid PointerPath expressions with no module name: `1F016644,13,A0`, `1F016644+13,A0`. - -#### Offset syntax - -Any number of offset expressions can follow the starting address expression. - -An offset expression is comprised of at least one hexadecimal number. It can be added together with others, through a series of algorithmic `+` or `-` operators. - -Additionally, an offset expression can start with a `-` sign to indicate a negative offset. - -Here are a few examples of valid offsets and what they evaluate to: -- `2A` evaluates to `2A` -- `2A+4` evaluates to `2E` -- `2A-3` evaluates to `27` -- `2A+4-4+2` evaluates to `2C` -- `-2A` evaluates to `-2A` - -Two operators cannot be chained, and an offset cannot end with an operator. \ No newline at end of file diff --git a/doc/StateWatcher.md b/doc/StateWatcher.md deleted file mode 100644 index ea4e717..0000000 --- a/doc/StateWatcher.md +++ /dev/null @@ -1,49 +0,0 @@ -# StateWatcher - -The `StateWatcher` abstract class allows you to easily track values in real-time. This is a great fit if you're trying to display up-to-date values from a game on a user interface like a WPF or Blazor application for example. - -The way it works is that it periodically updates its `LatestState` property automatically, which works well in data binding scenarios, but also has events that you can subscribe to, in order to directly receive each and every state update if you need to. - -To use it, you have define a class that inherits it, and implement the `ReadState` method. - -Here is an example: - -```csharp -public record MyGameState(int HealthPoints, string PlayerName); - -public class MyGameWatcher : StateWatcher -{ - private ProcessMemory _myGame; - - public MyGameWatcher() - : base(30) // This makes it refresh 30 times per second - { - // We build a new ProcessMemory instance here, but we could also - // have an existing instance passed as a constructor parameter. - _myGame = ProcessMemory.OpenProcessByName("mygame.exe"); - } - - protected override MyGameState ReadState() - { - // This method will be automatically called 30 times per second. - // It will update the LatestState property of this instance. - int hp = _myGame.ReadInt("mygame.exe+1F16C,24,8"); - string name = _myGame.ReadString("mygame.exe+1F16C,24,1A,8"); - return new MyGameState(hp, name); - } -} -``` - -And then this is how you would use the `MyGameWatcher` class: - -```csharp -var watcher = new MyGameWatcher(); - -// Set this up if you need to react each time your state is updated. -// Alternatively, you can just use watcher.LatestState whenever you need it. -watcher.StateUpdated += (_, args) => Console.WriteLine( - $"{args.State.PlayerName} has {args.State.HealthPoints} health points."); - -// Don't forget to start the automatic updates -watcher.Start(); -``` diff --git a/doc/res/CheatEngineExamplePointer.png b/doc/res/CheatEngineExamplePointer.png deleted file mode 100644 index 009f45e..0000000 Binary files a/doc/res/CheatEngineExamplePointer.png and /dev/null differ diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 0000000..5dca868 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,51 @@ +{ + "metadata": [ + { + "src": [ + { + "src": "../src/MindControl", + "files": [ + "**/*.csproj" + ] + }, + { + "src": "../src/MindControl.Code", + "files": [ + "**/*.csproj" + ] + } + ], + "dest": "api" + } + ], + "build": { + "content": [ + { + "files": [ + "**/*.{md,yml}" + ], + "exclude": [ + "_site/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "output": "_site", + "template": [ + "default", + "modern" + ], + "globalMetadata": { + "_appName": "MindControl", + "_appTitle": "MindControl", + "_enableSearch": true, + "pdf": false + } + } +} \ No newline at end of file diff --git a/docs/guide/guide-requirements.md b/docs/guide/guide-requirements.md new file mode 100644 index 0000000..308d236 --- /dev/null +++ b/docs/guide/guide-requirements.md @@ -0,0 +1,29 @@ +# Requirements + +This section details what you are going to need to follow this guide. If you are already familiar with memory hacking and just want to learn about MindControl features, you can skip over to the [Project setup](#todo) section. + +## Grab the test game + +Throughout the guide, we are going to always use the same target program, so that you can follow along by repeating the same steps. + +That target program is going to be the demo of a game called **Slime Rancher**, from the independent studio **Monomi Park**. It's a fun first-person adventure game about capturing wild monsters called slimes and making money with resources harvested from them. It's available on Steam. You can download the demo for free [on the Steam store page](https://store.steampowered.com/app/433340/Slime_Rancher/). **Make sure to use the free demo version**, even if you have the full game, so that the exact same steps shown in this guide work for you. + +The demo is a little bit outdated compared to the full game, is limited in time and doesn't save your progress, but fits our needs perfectly and is still enjoyable. + +> As an aside, if you happen to enjoy the demo, consider buying the full game to have some fun and support the team behind it. Also check out the sequel, [Slime Rancher 2](https://store.steampowered.com/app/1657630/Slime_Rancher_2/). + +## Install Cheat Engine + +Cheat Engine is a popular memory hacking tool that allows you to inspect and manipulate the memory of a running process. It's a great tool to learn about memory hacking, and it's also useful to complement MindControl, as it provides a set of tools to inspect memory, and lots more. + +You can download the latest Cheat Engine installer from its [official website](https://www.cheatengine.org/). The installer is safe to use, but has bundled software offers during the installation process. Make sure to decline these if you don't want them. + +## Setting up the game + +Before continuing further in the guide, make sure you boot up the game, start a new game, and clear the tutorial to understand how the game works. It should only take a couple of minutes, at most. + +When you're done, in the game settings, you can turn off tutorials for the next times you'll boot up the game. It's also recommended to set the game in windowed mode, as we will be switching between the game and Cheat Engine a lot. + +## Next step + +The next section will be about setting up Cheat Engine and exploring basic memory concepts. diff --git a/docs/guide/hacking-basics/basic-memory-concepts.md b/docs/guide/hacking-basics/basic-memory-concepts.md new file mode 100644 index 0000000..a8791ac --- /dev/null +++ b/docs/guide/hacking-basics/basic-memory-concepts.md @@ -0,0 +1,61 @@ +# Basic memory concepts + +This section is intended to cover some fundamental concepts about memory hacking. If you are already familiar with memory hacking, you can skip ahead to the [Project setup](#todo) section. + +## Memory and structures + +As previously stated, a program is loaded into memory when it runs. This means that all the data it needs to operate is stored in memory, as a series of numeric values. These numeric values can be just numbers, like a character's health points value, but when grouped together, they can be text, pictures, audio, code instructions, or anything else. The way the data is organized is defined by how the program is written and compiled. + +![A memory viewer example](../../images/ce-memoryviewer-small.png)
+*An example of a small portion of memory shown in a memory viewer.* + +A structure is a set of related variables that are grouped together in a set. Each variable in the structure is called a member. Structures can be nested, meaning that a member of a structure can be another structure. If you think about it in terms of object-oriented programming, a structure is like a class, and a member is like a field. + +To give a few examples, in a game, you could have a structure that represents the player's character. This structure could have members for the player's health, position, speed, and so on. You could imagine other structures that represent the game world, weapons, enemies, animations, etc. + +![An example of structures](../../images/il-structures.png)
+*An extremely simplified example of structures in a game. This is not an accurate representation of any game, just an example.* + +All the structures that are relevant to the state of the game are stored in memory, and the game itself uses that memory to operate. If we manage to identify these values, we can observe and manipulate them. For example, if you were to change the player's health value, you would see the player's health bar change instantly (in most cases). Unless it specifically has cheat protection, the game would have no clue that you changed the value, as it would just read the health value from memory like it does at every frame to update the health bar accordingly. + +The trouble is that **we don't know** where in memory these structures are stored, how they are arranged, and most often, what structures are defined. This is where tools like Cheat Engine come in handy. + +## Attaching to the process with Cheat Engine + +If we start the Slime Rancher demo, and then open Cheat Engine, we can attach Cheat Engine to the game process. This will allow us to inspect the memory of the game. + +![The attach window in Cheat Engine](../../images/ce-attach.png)
+*Attaching Cheat Engine to the game process: click the "Select a process to open" button in the top-left action icon row, and then pick the process running the game.* + +> [!NOTE] +> This is not the case for Slime Rancher, but sometimes, your target can have multiple processes. The correct one to pick is usually the one with the highest memory usage (check in the Task Manager). You can also use the "Windows" tab in the Cheat Engine process list to find the process handling the main window, which is the correct one in most cases. + +## Memory viewer and addresses + +Clicking the "Memory View" button above the bottom panel will open the memory viewer window. This window allows you to visualize both the memory and the decoded instructions of the game code. + +![The memory viewer window in Cheat Engine](../../images/ce-memoryviewer.png)
+*The Memory viewer window: The top-most panel, highlighted in green, shows the disassembled (decoded) instructions of the game code. The bottom panel, highlighted in orange and blue, shows the memory contents.* + +Let's focus on the left part of the memory viewer panel for now (the part highlighted in orange in the screenshot shown above). It shows the memory as a series of hexadecimal numbers that range from 00 to FF (255). And as you can see, there are lots and lots of these numbers. You can keep scrolling pretty much forever. This is because each of these numbers is a byte, and this game, like most 3D games, has well over a gigabyte of memory, which means over a billion bytes. + +On the leftmost part of the panel, you will notice hexadecimal numbers ranging from 00000000 to 7FFFFFFF. These are the **memory addresses**. Each byte in memory has an address. If we think of the process memory as an immense locker room, the memory addresses are the locker numbers, and the bytes are what we store in the lockers. The address on the left of the panel is the address of the leftmost byte in the matching row. If you select a byte by clicking on it, you will see the address of that byte in the bottom-left corner of the window. + +Memory addresses are a key concept in memory hacking. Whenever we want to read or write to a specific memory location, we need to know its address. This is why memory hacking tools like Cheat Engine will always show the addresses of everything you inspect. + +## Values and types + +So, each byte that you can see in the memory viewer represents a value between 0 and 255. But what if we want to store a bigger number, like 123456? We can't do that with a single byte. + +Well, if you group multiple adjacent bytes together, you can store bigger numbers. For example, if you group four bytes together, you can store a number between 0 and 4294967295. + +This notion of grouping bytes together to store more than just a small value is called a **data type**. A data type is a way to define how many bytes are used to store a value, and how that value is interpreted. For example, a short integer is a data type that uses two bytes to store a whole number. A floating-point number (or "float") is a data type that uses four bytes to store a decimal number. A string is a data type that uses multiple bytes to store a sequence of characters that form a text. + +![An illustration showing the same bytes interpreted into different types](../../images/il-datatypes.gif)
+*An illustration showing the same bytes interpreted into different types.* + +If you select a byte in the memory viewer, below the memory panel, Cheat Engine will display the base-10 value of the byte, but also its value when grouped together with the following bytes, interpreted as some of the most used data types. This is useful when trying to understand if the byte is the start of a field with a bigger type. + +## Next step + +Now that we have covered some basic memory concepts, the next section will be about searching for specific values in memory using Cheat Engine. diff --git a/docs/guide/hacking-basics/code-manipulation.md b/docs/guide/hacking-basics/code-manipulation.md new file mode 100644 index 0000000..561e188 --- /dev/null +++ b/docs/guide/hacking-basics/code-manipulation.md @@ -0,0 +1,135 @@ +# Code manipulation + +In this section, we are going to manipulate the code instructions of our target program. This is a slightly more advanced topic, but we will keep things simple. + +## Code instructions in memory + +As stated in the previous section, a program is loaded into memory when it runs. This means that the code that the program executes is stored in memory, as a series of instructions. + +We have seen before that these Assembly instructions are represented with a short mnemonic, followed by a list of operands. For example, the instruction `MOV EAX, 0x12345678` moves the value `0x12345678` into the `EAX` register. + +But these instructions are not stored as text in memory. They are stored as binary values, where each value represents a different instruction. The CPU reads these values and executes the corresponding instruction. These numeric values are called **opcodes**. + +For example, the `ADD EAX, 0x11BB22CC` instruction, which means "add the value `0x11BB22CC` to the `EAX` register", is written as `05 CC 22 BB 11` in memory. The `05` opcode tells the CPU that this is an `ADD` instruction with `EAX` as the first operand, and the following bytes are the value, in little-endian (in reverse). + +This means that, if we rewrite instructions in the memory of a running process, we can change the behavior of the program. + +## Locating code instructions + +Even when you know assembly, reading and understanding the whole code of a program would take ages, because there are lots of instructions, which are all very basic, and not very informative by themselves. So, if we want to modify parts of the code, we need to know where to look. + +Let's take a quick example. In the Slime Rancher demo, running uses up your stamina, and so you cannot run forever. The first thing that we are going to do is to modify the behaviour of the game so that running does not consume stamina anymore. To do that, we need to find the code that decreases the stamina value when running. But we don't know what this code looks like (what registers it uses, what values it compares, etc.). + +To find this code, we first need to find out the address of the stamina value in memory. We've already covered how to do so in the "[Finding values with Cheat Engine](./finding-values.md)" section of this guide. Here are some more hints: +- Like most values that are displayed in gauges or bars in games, the stamina value is a float +- You can sprint and then pause the game to freeze the value while you are scanning for it +- The number you see in-game is rounded, so it's better to use the `Value between...` scan type instead of exact values. For example, if you see 72, search for values between 71 and 73. + +> **Remark**: Once you have found the stamina value, notice how it relates to the coin count. They are very close together. It's likely that they are part of the same structure. You can even duplicate the coin count pointer path and replace the `+80` offset with a `+74` offset to get a stable pointer path to the stamina value. + +> [!TIP] +> If you cannot find the stamina value and still want to go further, just subtract 0xC from the coin count address. + +Once we have the address of the stamina value, we are going to use a tool that we've already covered before: the "Find out what writes to this address" feature of Cheat Engine. This feature will show us the code that writes to the stamina value in memory. This is likely to lead to the code that decreases the stamina value when running. + +![Using the "Find out what writes to this address" menu in Cheat Engine](../../images/ce-stamina-find-writes.png)
+ +This opens a window you should already be familiar with, showing the instructions that write to the stamina address. If you go back to the game, without even sprinting, you should see an instruction being called over and over again (probably each frame). This should be `movss [rax+74],xmm5`. However, we will disregard this instruction, because, when we start sprinting, we see another entry appear in the list. Because that second one only appears when we sprint, we can suppose that it's the instruction that decreases the stamina value when you are sprinting, and so that's the one we are looking for. + +So that second instruction should be `movss [rsi+74],xmm5`. Select this one and click the "Show disassembler" button. This will open the memory view window and show you the disassembled code around this instruction. + +![The disassembled code around the instruction that writes to the stamina value](../../images/ce-memoryviewer-stamina-instructions.png)
+*The first line highlighted in blue is the instruction that writes to the stamina address. The next lines are the instructions that are executed afterward.* + +## Removing code instructions + +Now that we have located the instruction, let's try modifying it. Our goal is to prevent stamina from decreasing. In theory, this can be done by just removing the instruction that writes to the stamina value. If nothing writes there, stamina won't go down. + +In practice, the convenient way to do that is to replace the target instruction with `NOP` instructions. `NOP` is an assembly instruction that does nothing. It's just a placeholder that takes up space in the code. Its bytecode is `0x90`, so it only takes up one byte. To fully replace our instruction, we need to replace the 5 bytes taken by the original `movss` instruction with 5 `NOP` instructions. + +This procedure is actually pretty common, and thus Cheat Engine has a tool for it. Just right-click the instruction you want to replace, and select "Replace with code that does nothing". + +![Replacing an instruction with NOPs in Cheat Engine](../../images/ce-stamina-instruction-nop.png)
+*Once you click "Replace with code that does nothing", you should see five NOP instructions instead of the original `movss` instruction.* + +Now, go back to the game and start sprinting. You should see that your stamina does not decrease anymore, and you can sprint forever. Congratulations, you've successfully manipulated the code of the game! + +## Injecting code instructions + +Deactivating instructions was simple enough. But let's say that instead of disabling a behaviour, we want to modify something. For example, let's say we want to multiply the coins we gain by selling plorts by 10. This is a more complex scenario, because we need to add instructions instead of just removing them. + +The problem with adding new instructions is that we cannot insert them in the middle of the code. The code is already written in memory, and we cannot just shift it around. If we were to overwrite the existing opcodes, we would write over the next instructions, and the program would probably crash. + +The best way to insert code is through a **trampoline hook**, which, as dumb as it sounds, actually makes sense when you know how it works. The idea is to: +- Write the new instructions in an unused memory location +- Replace the target instruction with a `JMP` instruction that jumps (redirects) to the new instructions +- At the end of the new instructions, add a `JMP` instruction that jumps back to the original code + +This normally involves a lot of work (finding or allocating a memory location, writing the new instructions, writing the trampoline with instructions that depend on the distance to the new code, etc.), but Cheat Engine has a tool that simplifies this process. + +For now, let's find the instruction that adds the coins when selling plorts, using the same technique as before: use "Find out what writes to this address" on the coin count address (because we now have a pointer path, Cheat Engine will ask if we want to use the pointer or the address. Pick the "Find what writes the address pointed at by this pointer" option), and look for the instruction that writes to the coin count when you sell plorts. + +The instruction should be `mov [rax+80],ecx`. It writes the value of the `ecx` register to the coin count address. This means that `ecx` holds the new coin count value. If we use the "Show disassembler" button, we should see the instructions around this one. The previous instruction in particular is `add ecx,edi`, which adds the value of the `edi` register to the `ecx` register. This is the instruction that adds the coins when selling plorts. + +When reaching the `add` instruction, the `edi` register should hold the value of the plort price. This is the value that we want to multiply by 10. So, we need to insert an instruction that multiplies the `edi` register by 10 before the `add` instruction. + +To perform the code injection in Cheat Engine, select the `add` instruction, and then in the Tools menu, pick "Auto Assemble". + +![Opening the Auto Assemble tool in Cheat Engine](../../images/ce-auto-assemble.png)
+ +This will open a window where you can write the new instructions. In that new window, in the "Template" menu, pick "Code injection". This will fill the window with the necessary code to perform the injection. You should see something like this: + +``` +alloc(newmem,2048,1E59C2074A5) +label(returnhere) +label(originalcode) +label(exit) + +newmem: //this is allocated memory, you have read,write,execute access +//place your code here + +originalcode: +add ecx,edi +mov [rax+00000080],ecx + +exit: +jmp returnhere + +1E59C2074A5: +jmp newmem +nop 3 +returnhere: +``` + +> [!NOTE] +> The addresses may be different in your case. + +This code does exactly what we explained before. It allocates a new memory location, writes the new instructions there, and then replaces the target instruction with a `JMP` instruction that jumps to the new instructions. At the end of the new instructions, there is a `JMP` instruction that jumps back to the original code. + +Now, we need to modify the `newmem` block to multiply the `edi` register by 10. The instruction to do that is `imul edi,edi,A` (meaning: multiply `edi` by A (or 10 in base-10) and place the result in `edi`). So, replace the `//place your code here` comment with this instruction, and click on Execute. + +![Injecting code to multiply the plort price by 10 in Cheat Engine](../../images/ce-code-injection.png)
+ +Once you've done that, Cheat Engine will ask you if you want to see the new code in the memory viewer. Click "Yes", and you should see the new instructions in memory. + +You should have 4 instructions: +```assembly +imul edi,edi,0A +add ecx,edi +mov [rax+80],ecx +jmp 1E59C2074AD +``` + +> The jmp instruction at the end is the one that jumps back to the original code. The exact address may be different in your case. + +You should also see a bunch of `add [rax],al` after the `jmp` instruction, but these are just there because the new memory location is filled with 0 bytes by default, and `00 00` can be interpreted as an `add` instruction. They are never executed, because the `jmp` instruction jumps back to the original code. + +If you highlight the `jmp` instruction and press Spacebar, the memory viewer will "follow" the jump and go to the original code. Scroll a little bit up, and you will see that the original `add ecx,edi` and the following `mov [rax+80],ecx` instructions have been replaced with a `jmp` instruction that jumps to the new code, and a `nop` instruction that does nothing and is only there for padding, to make sure the next instructions are aligned correctly, just like they were before. + +Note that the `add` and `mov` instructions that were replaced by the new `jmp` are now in the new memory location after our `imul` instruction. They have been automatically moved there by Cheat Engine to make room for the `jmp` instruction in the original code location. + +Now, go back to the game and try to sell a plort. It should give you 10 times more coins than displayed on the market panel. Congratulations, you've successfully injected code into the game! + +## Next part + +This is the end of the "hacking basics" tutorial part of this guide. The next chapter will now cover how to make programs that use the MindControl library to interact with the memory of a running process. This will allow you to build your own memory hacking tools, and automate some of the processes we've just done manually. diff --git a/docs/guide/hacking-basics/finding-values.md b/docs/guide/hacking-basics/finding-values.md new file mode 100644 index 0000000..a4cc153 --- /dev/null +++ b/docs/guide/hacking-basics/finding-values.md @@ -0,0 +1,104 @@ +# Finding values with Cheat Engine + +When hacking a game, the first step is to find the values you want to manipulate. This can be anything from the player's health, to the number of coins, to the position of an enemy. In this section, we will cover how to find these values using Cheat Engine. + +> [!TIP] +> Make sure the Slime Rancher demo is started, and Cheat Engine is running and attached to the process before proceeding. Start a new game to make sure you have the same initial values as we do. + +## Memory scanning + +### Coin count + +For our first example, we will attempt to modify the number of coins we currently have. The game has a counter shown in the bottom-left corner of the screen. Let's find the memory address that stores this value. + +![The coin count counter](../../images/sr-coincount.png)
+*This screenshot of the Slime Rancher demo shows the coin counter above the health bar. The value we want to get is going to be 250 (the initial money we get when starting a new game).* + +Now that we have a value to search for, the first step is to identify the type of value we are looking for. In this case, 250 is a small number that would fit into a single byte, but it's likely that the game uses a bigger data type to store this value, because it's a counter that can go up to thousands and more. + +Determining the type of the value we are looking for can be a little bit tricky, so here are some general guidelines: +- If the value can be either "on" or "off", like a switch, it's likely a boolean. Booleans are usually stored as a single byte, that can be either 0 (*off*, known as "false") or 1 (*on*, known as "true"). +- If the value can only be a round number, like 3 or -1407, it's likely an integer. The most commonly used type to store integers is a 4-byte integer. If it can be extremely large (in the scale of billions or more), it will probably be an 8-byte integer. You may also find 2-byte integers or just single bytes to store this kind of values, but this is less common, even when the value is always small. +- If the value can be a decimal number, like 3.14 or -100.5, it's likely a float. You may also find 8-byte floats, more commonly called "doubles", used when the values may be particularly big or precise (meaning they have many decimal places). Floats are also frequently used for percentages, or values that can be computed with fractions like damage values or health values. If your value is displayed in a gauge or a bar, it's likely a float, even if the numbers are shown as rounded numbers in the UI. +- If the value is a text, like "Hello", it's a string. Strings are sequences of characters. They are a bit more complex, and out of scope for this basics guide. + +In our case, the coin count is an integer for sure, because we cannot have a fraction of a coin, so it wouldn't make sense to use float or double. Looking for a **4-byte integer** is a good guess, because it's the most commonly used type for integers. + +Next, we are going to perform a **first scan**. This is the most basic type of scan, and it's the one you will use most often. It will search the memory for all the values that match the type and value you specify. + +In our case, we are looking for a 4-byte integer, with the value of 250. Here's how to do it: +- In the main window of Cheat Engine, locate the scan panel on the right +- In the "Value" field, type `250` +- In the "Scan type" dropdown, select `Exact value` +- In the "Value type" dropdown, select `4 Bytes` +- Click the "First scan" button + +![Screenshot of the scan panel](../../images/ce-coinscan.png)
+ +What this does is search the whole memory of the game for any groups of 4 bytes that have the value 250 when read as an integer. + +After the scan is complete, the left panel will fill up with a list of memory addresses that match the value you searched for. For each result, Cheat Engine will show the address, the current value at that address, and the previous/first value at that address (more on that later). + +Now, because there are so many bytes in memory (remember, there's more than a billion), the value `250` that we are searching for is inevitably going to show up thousands of times, and not only for our coin counter. Each row in the results panel is a different memory address of a group of 4-bytes that represents an integer value of `250`. + +![Screenshot of the results panel showing multiple thousands of hits](../../images/ce-coinscan-firstresults.png)
+*We have more than 7000 results for the value 250. This is expected.* + +These thousands of results are too many to look through manually. We need to narrow down the search. To do this, we need to make the game change the value, and then filter out the memory addresses that did not change to the expected new value. + +If you went through the game's tutorial (as you should have!), you know how to gain some money. Just collect a plort, shove it into the plort market, and you should obtain some more coins. In my case, I got 9 coins, so I have 259 coins. + +Now we can go back to Cheat Engine, enter the value `259` (or however many coins you have now), and hit **next scan**. Remember that "Next scan" is used to filter the results of a previous scan, based on the new value you specify, while "New Scan" or "First scan" will perform an entirely new search. + +In the results list: +- The **Address** column shows the memory address where the matching value was found. +- The **Value** column shows the current value at the address, updated in real-time. When the value changes, it is highlighted in red. +- The **Previous** column shows the value the last time we clicked the "Next scan" button. +- The **First** column shows the value at the time of the first scan. In our case, it should be "250" for all results. + +After the second scan, you should have a much smaller list of results. If you still have some results that are changing (highlighted in red) despite your coin count staying the same, you can perform additional scans without changing the value to filter them out, or remove them from the list manually. + +For this particular value, we should be able to narrow it down to 3 values pretty quickly with only a couple of scans, but it's not uncommon to have to repeat the operation (make the game change the value & scan for the new expected value) a few more times to get to a manageable results count. + +![Screenshot of the narrowed down results](../../images/ce-coinscan-results-narrowed.png)
+*The addresses you obtain can be different from the ones shown in this screenshot. This is expected, we will cover this some more later.* + +In the results list, you can double-click on an address to add it to the bottom panel. You can also select multiple results and click the little red arrow on the bottom-right of the results panel. The bottom panel is called the **address list**, and it's where you can keep track of the addresses you are interested in. + +Once you have added all of your narrowed-down results to the address list, we have to find out which of the three is the correct one. The most straightforward way to do this is to change the value and see if the coin count changes in the game. To do this, double-click on the value in the address list, enter a different value (e.g. `9999`), confirm, and check back in-game. + +In my case, modifying the second result seems to have no effect in-game, modifying the third one seems to instantly revert it back to 259, but modifying the first one does the trick. The coin count in the game is now 9999. + +![Screenshot of coin count with a value of 9999](../../images/sr-coincount-hacked.png)
+ +Congratulations! You have successfully found and modified a value in memory, and the game now believes you have 9999 coins. In fact, unless the game specifically guards against memory hacking (which Slime Rancher and most single-player-only games do not), it has no reason to get suspicious about our sudden fortune, and so it will let us use these coins however we want, no matter how ridiculously high we set the value. + +> [!WARNING] +> _Reminder:_ what we are doing here is fine because it's a single-player game, with no competitive aspect. **Using memory hacking for cheating in online games or to gain any kind of advantage (no matter how small) in a competitive space is wrong and will get you banned**. Always respect the rules of the games you play. Keep it fair and don't ruin the fun for others. **If you ignore this, I will do my best to shut down your project.** + +Editing values in the memory of your target is the core of memory hacking. Libraries like MindControl will help you automate the writing process, but you will still have to manually find the addresses. + +### Bonus training: hacking your health points + +As a bonus, try to find and modify your health points using the same technique. Here are a few hints to help you get started: +- You can lose health points by colliding with aggressive slimes in some areas of the game. +- Despite the game showing integral values for health points, remember that most games use floats to store values that show up in gauges or bars. +- Setting your health above 100 will revert it back to 100. This is a common behavior in games to prevent players from healing above the maximum health. Try setting it to a distinct value below 100 to see if it works. + +### Bonus bonus training: track a global statistic + +As a second bonus, try to find out the memory address that holds one of the following global stats (they don't reset when you start a new game): +- Total number of splorts sold +- Total money made from splorts +- Total number of chicken fed to slimes + +> [!TIP] +> These stats are tracked in the achievement menu, but the challenge is to find them without looking. You can look in the achievement menu once you've found them to verify that you have the correct numbers. + +A few hints: +- You probably haven't counted how many times you have done these things until now. In these cases, you can use the "Unknown initial value" scan type to get started. +- Once you have an initial scan, you can filter out unwanted addresses by increasing these numbers, and then using the "Increased value by..." scan type, specifying a value that represents by how much you have increased the counter since the last scan. For example, for the splorts sold counter, if you've sold 2 additional splorts since the last scan, specify 2. This will only keep addresses that have increased by that amount since the last scan. + +## Next part + +In the next section, we will point out a big issue with the technique we just used, and find out how to solve it using pointers. diff --git a/docs/guide/hacking-basics/stable-address.md b/docs/guide/hacking-basics/stable-address.md new file mode 100644 index 0000000..e2e5891 --- /dev/null +++ b/docs/guide/hacking-basics/stable-address.md @@ -0,0 +1,175 @@ +# Stable addresses + +In the previous section, we learned how pointers can help us pinpoint the location of a target value in memory, no matter how much the memory layout changes. We also learned that the pointer path has to start with a stable address if we want our hack to be reliable. This section will cover techniques to find stable addresses and follow pointer paths to get to the values we want to read or write. + +## What is a stable address? + +To explain stable addresses, we first need to cover modules. A module is a file that contains code and data that is loaded into memory when a program runs. In typical cases, you have an EXE (Executable) file, which contains the entry point of the program (first code executed), and then a bunch of DLL (Dynamic Link Library) files, which contain additional code that the EXE can reference. When a module is loaded into memory, it is assigned a base address, which is the memory address where the module starts. + +A stable address is the address of a value that is always located at the same offset, after the starting address to its parent module. The notation is usually `module_name + offset`, where `module_name` is the name of the module, and `offset` is a fixed value that represents the distance from the module's base address. For example, `Game.dll + 12C0` means "the address `12C0` bytes after the start of the `Game.dll` module". + +## Why are stable addresses important? + +Stable addresses are crucial for building memory hacking programs that are robust and reliable. If we can find a stable address that holds a pointer to a structure that we are interested in, we can follow it to get to the values we want to read or write, no matter how much the game changes things in memory. + +Without stable pointers, we would have to find addresses from scratch every time we start the game or change the game state in a way that moves things around in memory. + +## How to find stable pointer paths + +There are several ways to find pointer paths that start with a stable address. Fortunately, because this is a very common task in memory hacking, Cheat Engine provides a few tools to help us with that. This guide will only demonstrate some of the easiest techniques, but there are many ways to find them, that may or may not work depending on the target process. + +For the examples, we will use the coin count value from the Slime Rancher demo that we have found in a previous section. Start by attaching Cheat Engine to the game process and finding the address of the coin count value as described in the "[Finding values with Cheat Engine](./finding-values.md)" section. + +### Going backwards with the debugger + +Let's start with the most reliable way to find a stable address, but also one of the most tedious ones. Starting with the address of the coin count value, we will use the "Find out what writes to this address" feature of Cheat Engine to find the code that writes to the coin count value, and inspect it to see what the offset of the coin count member is within the structure that holds it. Then, we will look for a pointer to that structure, and so on, until we find a stable address. + +Start by right-clicking the coin count address in Cheat Engine from the address list, and selecting "Find out what writes to this address". Confirm that you want to attach the debugger. This will open the watcher window and add an entry whenever the game writes to the address. + +![Cheat Engine: Find out what writes to this address](../../images/ce-find-writes.png)
+ +Now, sell a plort in the game to cause it to write a new coin count value. You should see an entry appear in the watcher window. Click it, and then click the "More information" button on the right. This opens another window with extra info. + +![Cheat Engine: Finding code that writes to the coin count](../../images/ce-write-trace.png)
+ +So let's explain what we just did here. In the first window, we have a list of all instructions that have been writing a value at the coin count address. If the same instruction writes to that address multiple times, it will appear only once, but the counter on the leftmost column will show how many times it did. + +We didn't really explain what instructions are. Basically, they are the lines of code that the game is executing. The instructions are stored in memory, and the CPU reads them one by one to execute the program. + +The instructions that we are seeing in the memory of a process are in a format called **assembly language**. This is a low-level language, very close to the machine code that the CPU executes. Each instruction is represented by a mnemonic (a short word that represents the operation), followed by some parameters that tell the CPU what to do. For example, the `MOV` instruction moves a value from one place to another, and the `ADD` instruction adds two values together. + +![An example of assembly code](../../images/ce-assembly-example.png)
+*An example of a few assembly instructions, from the "Extra info" window of Cheat Engine.* + +Developers very rarely write in assembly language directly, but instead write code in a high-level language that is then transformed (through compilation or interpretation) into assembly. With memory hacking, we always have to work with assembly code, which is much harder to read and understand than the original code. + +> [!NOTE] +> This tutorial is not going to cover assembly language, because this is a much larger topic. We will only cover the basic elements required for our specific case. However, it's a good idea to learn more about it if you want to become proficient in memory hacking. You can find many resources online to learn assembly. + +Back to our example, the "Extra info" window shows us the assembly code that wrote to the coin count address. The exact instruction that wrote to the address is highlighted in red: + +```asm +mov [rax+00000080],ecx +``` + +It's a `MOV` instruction, which means that we are copying a value from one place to another. The `MOV` instruction takes two arguments: the destination and the source. In this case, the destination is `[rax+00000080]`, and the source is `ecx`. So that means it is copying the value of the `ecx` register to the address `[rax+00000080]`. If you don't know what registers are, to simplify, you can think of them as a limited set of variables that the CPU can use to store values temporarily. + +What's interesting here is that the instruction specifies an offset of `80` (in hexadecimal) bytes from the `rax` register. This typically means that `rax` stores the address of a structure that contains the coin count value at an offset of `+80` bytes. Looking at the bottom part of the "Extra info" window, we can see a dump of the values in CPU registers at the time the instruction was executed. Take note of the value of the `rax` register, as this is the address of the parent structure holding the coin count. In my case, it's `168698616C0`, but yours will be different. Once you've noted it down, and also noted down the offset (`+80`), you can close both windows. + +Now, let's take a look back at the pointer path example from the previous section: + +![An illustration showing a pointer path with a stable address](../../images/il-pointerchain.png)
+ +For now, we have only found the last offset of the pointer path. For the next step, we need to find memory values that hold the address of the parent structure. You know how to do that already: use the main window of Cheat Engine to scan for the value of the `rax` register. + +![Scanning for a pointer value in Cheat Engine](../../images/ce-scan-for-pointer.png)
+*Scanning for the value of the `rax` register in Cheat Engine. Replace the searched value with the value of `rax` you have noted before. Don't forget to pick `8 Bytes` in the value type dropdown (addresses are 8-byte integers in 64-bit programs), and to check the `Hex` checkbox to let you input a hexadecimal number.* + +This scan will give you a list of pointers that hold the address of the parent structure. If you look at the pointer path diagram from before, we need to keep finding offsets and parent structure addresses until we reach a stable address. So, now, we can repeat the steps we just did with the coin count address, but using the addresses we just found with the scan. + +For each scan result: +- Use "Find out what accesses this address" (or press Ctrl+F5) to find the instruction that reads the pointer. We have used "writes" before, but finding instructions that _read_ the pointer is more reliable after the first step. +- Mess around in the game until instructions appear in the watcher window. If it never does, give up on this pointer and try another one. +- Look at the assembly code to find the offset of the next structure in the same way we did before, and take note of the address of the parent structure, and the offset (sometimes there is no offset, in that case it's just `+0`). +- Scan for the parent structure address in Cheat Engine to find the next pointers. +- If the scan brings up stable addresses (they appear in green), you can stop there. If not, repeat the whole process until it does. + +Be careful with this process, as it's easy to get lost or to fall into a loop of pointers that reference each others. If you are stuck, try to go back a few steps and try another path. + +> [!TIP] +> Try doing at least a couple of steps of this process to make sure you understand how it works, but **don't feel like you have to go all the way to the end**. It's a very time-consuming process, and there are easier ways to find stable addresses (keep reading to find out!). + +![Scanning results with a stable address Cheat Engine](../../images/ce-scan-stable-pointer.png)
+*Repeating these steps will (hopefully) eventually lead you to a stable address. The last address in the list on this screenshot, highlighted in green, is stable.* + +Once you have a stable address to start with, click the "Add Address Manually" button on the right edge of the Cheat Engine main window, pick the target value type (in our case, `4 Bytes`), check "Pointer", and start filling in the fields. The bottommost field should be the stable address you found, and then the fields above it should be the offsets. Click "Add Offset" as needed to add more offset fields. Once you are done, the value shown next to the address field should be the value of the coin count. + +![Adding a pointer manually with Cheat Engine](../../images/ce-add-pointer.png)
+*An example pointer path added manually in Cheat Engine. Note that the `+80` offset we found earlier is the first one when read from top to bottom.* + +Now that we've done all this work, we finally have a stable pointer path that we can use to read or write the coin count value, no matter how much the game changes things in memory. Hopefully. In fact, there is no guarantee that the particular path we have found will work no matter what we do in the game. Maybe one of the structures we traverse is not guaranteed to hold a pointer to the next structure in all cases. Or maybe it does and everything works out just fine. To make sure, we have to test it out in the game, restart the game and test it again, and so on, to make sure that the path is indeed stable. If it's not, we have to go back in the process steps and find other paths. + +So all in all, this process is methodical and rather reliable, but it's also very time-consuming and requires a lot of manual work. It's a good idea to try it out at least once to understand how it works, but let's find out about another method that is much faster and easier. + +### Using the pointer scanner + +The pointer scanner is a tool in Cheat Engine that can automatically find pointer paths for you. It's not always reliable, but it's often a very efficient way to find stable addresses. + +So let's start over (restart the game if you have to) and find the coin count value again, as we did in the "[Finding values with Cheat Engine](./finding-values.md)" section. + +Start by right-clicking the address of the coin count value in Cheat Engine, and selecting "Pointer scan for this address". + +![The "Pointer scan for this address" option in Cheat Engine](../../images/ce-pointer-scan-open.png)
+ +This will open the pointer scanner option window. Most settings are fine as they are, but the most important one is the "max level" setting. This is the maximum number of pointers that the scanner will follow. The higher the number, the longer the scan will take, but the more likely it is to find a stable address. + +Generally, you should start with a low number for the max level, like 2 or 3, and increase it if you don't find any stable addresses. However, Slime Rancher is a Unity game, which is known to have a lot of pointer layers, so we are going to use a max level of 5. + +> [!NOTE] +> Increasing the max level will make the scan **exponentially** slower. To give you an idea, a max level of 7 may take a few seconds, a max level of 8 may take several minutes, and a max level of 9 may take days. + +In addition to the max level setting, I also recommend you check the "Pointers must end with specific offsets", and add the `+80` final offset that we have found earlier through the "Find out what writes to this address" method. This will make the scan faster and more reliable, as it will make sure pointer paths end with the offset we know is correct. + +![The pointer scanner options in Cheat Engine](../../images/ce-pointer-scan-options.png)
+ +Once you have set up the options, click "OK" to start the scan. Cheat Engine will ask you to save a `.ptr` file. Try to keep these organized, they are quite useful if you want to go back after making a mistake, or stop and then resume your hacking project later on. + +> [!TIP] +> A recommended way to keep your `.ptr` files organized is to create a folder for each game you are hacking, and then create a subfolder for each hack you are working on. Then, name your `.ptr` files with a descriptive name, a max level indication, and an index (we will see why in a moment). For this first one, I am going to use `D:\Hacking\SlimeRancherDemo\CoinCount\coincount-l5-001.ptr`. + +The scan may take a while, so be patient. When it's done, you will see a list of results. + +![The initial pointer scanner results in Cheat Engine](../../images/ce-pointer-scan-initial-results.png)
+ +This window shows all the pointer paths that the scanner has found. The number of results is displayed above the table (in my screenshot, it's 984, but yours will probably be slightly different). The "Points to" column shows the final address and its value. These should all have a value corresponding to your current coin count for now. + +> [!NOTE] +> The values shown in the "Points to" column are **not** refreshed in real time. They are refreshed whenever you select the row. + +So, in the current state of the game, all of these stable pointer paths lead to our coin count. But that doesn't mean they will always do. We have to test them out to make sure they are indeed stable. The best way to do that is to mess with the game state, and perform an additional scan to filter out results that no longer point to our coin count. + +The first thing we can do is to go back to the menu and start a new game. This should shuffle things around in memory, but the paths that are stable should still point to the coin count. When you've done that, go back to the pointer scan results window, and in the "Pointer scanner" menu, select "Rescan memory - Removes pointers not pointing to the right address". + +The rescan window that opens allows you to specify what you want to filter out. The easiest scan we can do is to pick "Value to find" and enter the expected value we should be pointing to. In our case, because we just started a new game, we are looking for the value `250` (the starting coin count). + +![The pointer scanner rescan options in Cheat Engine](../../images/ce-pointer-scan-rescan.png)
+ +Click "OK" to start the rescan. This will effectively filter out paths that no longer point to an address with a value of `250`. + +> [!TIP] +> Every time you rescan, you are asked to save a new `.ptr` file. As stated before, this is useful to go back to a previous state. Following the convention from before, I am going to save this one as `D:\Hacking\SlimeRancherDemo\CoinCount\coincount-l5-002.ptr`. + +When the rescan is done, your list should have been trimmed significantly. + +![The trimmed pointer scanner rescan results in Cheat Engine](../../images/ce-pointer-scan-results-2.png)
+*We now have 257 results, down from 984. Your numbers may be different.* + +We can trim the list further by repeating the process of messing with the game state, and then rescanning. For example, you can sell a plort to change the coin count, and then rescan for the new value. This will filter out paths that don't point to the new coin count value. + +One of the best ways to mess with game state is to close the game and start it back up. This will shuffle things around in memory a lot, which means that paths that still hold after multiple reboots are very likely to be reliable. + +> [!NOTE] +> After you restart the game, don't forget to **re-attach the process in Cheat Engine**! You don't have to close any window, everything will update automatically. + +After a few iterations of this process, you should have a list of stable pointer paths that you can use to read or write the coin count value. + +There is no way to know for sure if any of these will always hold no matter what you do in the game. Just keep iterating until you are confident enough that the paths are stable. + +If all of your results get filtered out, it can mean one of the following: +- You have made a mistake in the rescan options. Make sure you are looking for the right value, and that the process is attached. You can always load a previous `.ptr` file to go back to a previous state without having to restart from scratch. +- There are no stable pointer paths with the specified max level. Start all over again with an increased max level (in the Slime Rancher demo, you should be able to find stable paths with a max level of 5). +- There are no stable pointer paths for your value at all. This can happen for example when one of the target structures may be at various indexes in a list, or if the game has cheat protections. + +![The final pointer scanner results in Cheat Engine](../../images/ce-pointer-scan-results-final.png)
+*In this case, we quickly reach a list of 9 paths that are seemingly stable.* + +When you are confident in your results, pick any of the paths that you have found (it's a good idea to pick shorter ones, but in our case, they all have 5 offsets). Double-click any result to add the pointer path to the address list in the main window of Cheat Engine. + +You now have a stable way to read or write the coin count value, no matter how much the game changes things in memory. Congratulations! + +> [!NOTE] +> Cheat Engine allows you to save your address list and more as a `.ct` file. This is useful to keep your progress, and to share it with others. You can find the save option in the "File" menu. + +## Next part + +We have now covered the basics of finding stable addresses and following pointer paths to get to the values we want to read or write. In the next section, we will learn another key concept in memory hacking: modifying the game code. diff --git a/docs/guide/hacking-basics/unstable-address.md b/docs/guide/hacking-basics/unstable-address.md new file mode 100644 index 0000000..07d2559 --- /dev/null +++ b/docs/guide/hacking-basics/unstable-address.md @@ -0,0 +1,85 @@ +# Unstable addresses + +In the previous section, we learned how to find a memory address that stores a specific value. We found the address that stores the coin count in the Slime Rancher demo, and we changed it to a different value. This is a very basic form of memory hacking, but it's already quite powerful. However, there is a catch. + +## The issue + +With the address of the coin count still in the address list in Cheat Engine, try to go back to the main menu of the game, and then start a new game. You will notice that the value at this address doesn't change. It's stuck with the same old value, and won't reflect the coin count in our new game. + +Worse, if you exit the demo (close it entirely) and then start it back up and load into a new game (don't forget to re-attach to the game in Cheat Engine every time you do that!), the address will either be invalid (the value will show up as `??`), or it will hold a value that doesn't have anything to do with the coin count. + +If you try to find the address again using the same technique we used in the previous section, you will notice that it's not the same as before. It's a different address. + +**Why is that?** + +Programs manipulate structures in memory all the time. They allocate memory for variables, use it, and then deallocate it. They move things around, they change the size of structures, they create new structures, and they destroy them. This is all part of the normal operation of a program. + +When a program does this, the memory addresses that we found before can be discarded or reused for other purposes. The address that holds the coin count is now different because the memory of the game has been shuffled around when we started a new game or booted up the game again. This is why we call these addresses **unstable**. + +To solve this issue, we need to understand two key concepts: **structures** and **pointers**. + +## Understanding structures + +A structure is a set of variables that are grouped together under a single name. Each variable in the structure is called a member. If you think about it in terms of object-oriented programming, a structure is like a class, and a member is like a field. Members are always organized in the same order across different structures of the same type, and will always be of the same length. + +Let's take some examples and define the following two structures (these are simplified examples, not actual structures in Slime Rancher or any other game): + +*The `Player` structure* + +| Field | Type | Offset | +|--------------|------------|--------| +| healthPoints | float | 0x08 | +| weaponHeld | pointer | 0x10 | +| coinCount | 4-byte int | 0x18 | + +*The `Weapon` structure* + +| Field | Type | Offset | +|-----------|------------|--------| +| cost | 4-byte int | 0x08 | +| damage | 4-byte int | 0x0C | +| swingTime | float | 0x10 | + +In these tables, the `Offset` column tells us where the field is located within the structure. For example, if we know the address of a `Player` structure, we can find the `coinCount` field by adding `0x18` (hexadecimal notation for the number `24`) to the address, no matter where the `Player` structure itself is located. Doing a little bit of math, if we know our `Player` structure starts at the address `182F359202C`, the `coinCount` member will be at the address `182F3592034`. + +Now, you'll notice that the `weaponHeld` member is a pointer, which is the second concept we need to understand to solve our issue. + +## Understanding pointers + +### What is a pointer? + +A pointer is simply a variable that holds the address of another variable. It's like a note that says "the value you are looking for is at this address". In our examples, the `weaponHeld` member of the `Player` structure is a pointer to a `Weapon` structure. This means that the `weaponHeld` member holds the address of a `Weapon` structure, and not the `Weapon` structure itself. + +Looking at the offsets and value types, we can see that a `Weapon` structure is 24 bytes long. Regardless of that, the `Player` structure can have a `weaponHeld` member that is always 8 bytes long, because it only holds the address of the `Weapon` structure. The `Weapon` structure itself can be located anywhere else in memory. + +### How do pointers solve our issue? + +Let's take a look back at our "weapon held" example. Say we want to track our current weapon damage. We have found that the address of the `Weapon` structure for the weapon we are currently holding is `182F3593120`. Using the offset, we know that the damage value is at `182F359312C`. Now imagine that, in game, we equip a different weapon. + +What happens in that case is that the game will update the value of the `weaponHeld` member in our `Player` structure to hold the address of the new weapon structure (this can also be called "pointing to" the new weapon structure). The previously held `Weapon` structure will not be touched. + +When we look at our `182F359312C` address, we still see the same damage value as before, because it still is a field within the old `Weapon` structure. Even worse, if it doesn't need the old weapon anymore, maybe the game will discard the old structure, and the address may become invalid, or be reused to hold different data. + +![An illustration describing the scenario when observing a fixed address](../../images/il-pointers-firstscenario.gif)
+*We observe the same address, regardless of the change, so we still get the same value of 5 after the weapon changes.* + +Now, let's say that instead of having a fixed `182F359312C` address that we watch, we first read the address of the `weaponHeld` member in the `Player` structure, to know where the weapon structure is stored. Then, we can add the `damage` member offset (`+C`) to that address, and we will always get the correct damage value, no matter how many times the game moves things around in memory. + +![An illustration describing the scenario when using pointers](../../images/il-pointers-secondscenario.gif)
+*We observe the pointer to the equipped weapon, and follow it with an added +0C offset to get to the damage value. This time, we get the correct value of 17 after the weapon changes.* + +The thing is, the `Player` structure itself can move around, especially if we do things like going back to the menu and loading back in, or even worse, close the game and start it back again. So, following the same logic, we have to find a pointer within a different structure that holds the address of the `Player` structure. Maybe there's a `GameManager` structure that has a `player` pointer member. And then maybe there's a `Game` structure that has a `gameManager` pointer member. And maybe that `GameManager` structure is always located at the same address, relative to a module (more on that later). + +If we can figure out this whole chain, we can follow the pointer path: +> `Game` structure address + `gameManager` member offset → `GameManager` structure address + `player` member offset → `Player` structure address + `weaponHeld` member offset → weapon structure address + `damage` member offset + +And that pointer path would always be stable, meaning that no matter how much the game moves things around in memory, and no matter how many times we restart the game, we can always get to the `damage` member of the weapon we are holding. + +![An illustration showing a pointer path with a stable address](../../images/il-pointerchain.png)
+*We observe the stable address `game.exe + 1FC08` (we'll learn more about these in the next section) that starts a sequence of pointers, that we follow with known offsets added each time, to finally get to the damage value of the weapon held by the player.* + +The key to these pointer paths is to find the first address in the chain that never moves around. This is called a **stable address**. + +## Next part + +In the next section, we will learn techniques to find stable addresses and follow pointer paths to get to the values we want to read or write. This will allow us to build memory hacking programs that are robust and reliable, even when the game moves things around in memory. diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md new file mode 100644 index 0000000..140f4ed --- /dev/null +++ b/docs/guide/introduction.md @@ -0,0 +1,26 @@ +# Introduction + +This guide is intended to demonstrate how to use the `MindControl` .net library to interact with the memory of a running process. However, to make sure your understanding of the basics of memory hacking is aligned with the library's design, we will first cover some fundamental concepts. If you are new to memory hacking, this guide will help you get started on your journey. If you are already familiar with process hacking, you can skip ahead to the [Project setup](./project-setup/creating-mc-project.md) section. + +## What is memory hacking and what can we do with it? + +Memory hacking is basically manipulating the internal values used by a process that is running on your system, while it's running. + +Every program that runs on your system is loaded into memory. This memory stores everything the program needs to run, including the values of variables, the code that is being executed, and the data that is being processed. By changing these values, you can manipulate the behavior of the program. + +Usually, each program minds its own business and has a separate set of memory. However, using system functions, you can read and write to the memory of any process running on your system. A memory hacking program will make use of these functions to access the memory of its target program. + +This technique is often used in gaming, to build all sorts of tools, such as game trainers, cheats, bots, overlays, and automation tools. Mods can also be built using memory hacking, although they typically also require other skills. It can also be used in general-purpose software, for debugging, reverse engineering, security testing, and more. + +> [!WARNING] +> Just a reminder before you keep going: **using memory hacking for cheating in online games or to gain any kind of advantage (no matter how small) in a competitive space is wrong and will get you banned**. Always respect the rules of the games you play. Keep it fair and don't ruin the fun for others. **If you ignore this, I will do my best to shut down your project.** + +## What MindControl is about + +MindControl is a .net library that provides a set of tools to interact with the memory of a running process. It allows you to easily build memory hacking programs that read and write values, search for patterns, inject code, and more. It's designed to be simple to use, reliable, and efficient. + +As stated before, operating systems like Windows provide functions to do that already. However, these functions are low-level, complex to understand and cumbersome to use. MindControl provides an additional layer on top of that, that considerably simplifies the process. + +## Next step + +The next section will be about gathering everything you need to follow this guide. \ No newline at end of file diff --git a/docs/guide/mcfeatures/allocations.md b/docs/guide/mcfeatures/allocations.md new file mode 100644 index 0000000..ca7752b --- /dev/null +++ b/docs/guide/mcfeatures/allocations.md @@ -0,0 +1,107 @@ +# Storing data in memory + +This section explains how to allocate memory and store data in the memory of the target process using MindControl. + +## What is a memory allocation? + +A memory allocation is a block of memory that is reserved in the target process's address space. This memory space can be used to store data, such as structures, arrays, strings, pictures, or even code. + +For example, if you want to replace a texture in a game, you have to: +1. Allocate memory in the target process to store the new texture data. +2. Write the texture data to the allocated memory. +3. Overwrite pointers to use the new texture data from the allocated memory instead of the normal one. + +MindControl provides three features for working with memory allocations: allocations, reservations, and storage. + +## Storage + +For most use cases, you don't need to allocate memory manually. Instead, you can use the `ProcessMemory` class to store data in the target process's memory. This is done using the `Store` method, which takes a value and returns a `MemoryReservation` object that represents a reservation on allocated memory (more on that later). You can then get the address of the reservation and reference it wherever you need to. + +```csharp +// Store an integer value in the target process memory. This could be any other data type that you can write. +var reservationResult = processMemory.Store(42); +if (reservationResult.IsSuccess) +{ + // We can then use the Address property of the MemoryReservation + // In this example, we read the value back from the target process memory, but you would typically write a pointer + // to this address somewhere so that the process uses it. + MemoryReservation reservation = reservationResult.Value; + int value = processMemory.Read(reservation.Address).ValueOrDefault(); + Console.WriteLine($"Stored value: {value}"); // Output: Stored value: 42 +} +``` + +When calling `Store`, under the hood, MindControl will: +- Allocate a chunk of memory in the target process that is large enough to hold the data you want to store, but usually bigger than that for various reasons +- Keep track of the allocated memory in a `MemoryAllocation` object to maybe reuse it later +- Reserve a portion of the `MemoryAllocation` just big enough to hold the data you want to store, essentially creating a `MemoryReservation` object +- Write the data at the address of that reservation +- Return the `MemoryReservation` object + +If we call `Store` again with some data that is small enough to fit in the same `MemoryAllocation`, MindControl will reuse the same allocation, and create a new `MemoryReservation` on it, for your new data. This is done to avoid unnecessary memory allocations and to optimize memory usage. + +```csharp +var reservation1 = processMemory.Store(42).Value; +var reservation2 = processMemory.Store(64).Value; +// Only one memory allocation is issued, but two different reservations are created. +// Usually, you don't need to worry about this. +``` + +The advantage of using `Store` is that it abstracts away the details of memory allocation and management, allowing you to focus on the data you want to store rather than the underlying memory operations. You don't have to worry about where the memory is allocated, how much space is reserved, about the system page size, data alignment, or even about accidentally overwriting the memory you allocated. + +> [!NOTE] +> Disposing the `MemoryReservation` object will automatically free the memory reserved for your data, so that it can be reused to store other data later. If your program dynamically stores more and more data, you have to make sure to dispose of the `MemoryReservation` objects when you no longer need them, to avoid memory leaks in the target process. + +The two other features, allocation and reservation, are more low-level and give you more control over the memory management process. They're usually not needed, but there are cases where you might want to use them. + +## Allocations + +If you need to allocate memory manually, you can use the `Allocate` method of the `ProcessMemory` class. This method allows you to allocate a block of memory in the target process's address space. This method returns a `MemoryAllocation` object that represents the allocated memory. + +```csharp +// Allocate a block of memory of at least 1024 bytes in the target process, to store data (not code) +MemoryAllocation allocation = processMemory.Allocate(1024, forExecutableCode: false).Result; +// The actual allocation size may be larger than 1024 bytes, depending on the system page size and other factors. +``` + +Note that the second parameter, `forExecutableCode`, specifies whether the allocated memory should be executable or not. If you plan to write code to this memory, you should set this parameter to `true`. Otherwise, you can set it to `false` to avoid performance overhead and potential security risks. + +There are also two optional parameters that you can use to provide memory range limits for the allocation, and/or to specify that the memory allocation should be made as close as possible to a specific address. This is most useful when performing code injection and other advanced memory manipulation techniques. + +> [!NOTE] +> Like the reservations, disposing `MemoryAllocation` instances will free the memory allocation in the target process. If your program creates allocations dynamically, make sure to dispose of them when you no longer need them, to avoid memory leaks in the target process. + +With the returned `MemoryAllocation` object, you can perform reservations, which is the topic of the next section. + +## Reservations + +If an allocation is a physical block of memory in the target process, a reservation is a logical portion of that allocation that you explicitly mark as used. + +Basically, reservations are another layer on top of allocations that allow you to manage your allocations more easily. When you make a reservation, you are locking in a portion of the allocated memory, and that makes sure that future reservations will not overlap with it. + +There are two ways to create a reservation: using the `ReserveRange` method of the `MemoryAllocation` class, or using the `Reserve` method directly on the `ProcessMemory` class. + +### Using a MemoryAllocation + +```csharp +MemoryAllocation allocation = processMemory.Allocate(1024, forExecutableCode: false).Result; +// Reserve 256 bytes of that allocation +MemoryReservation reservation = allocation.ReserveRange(256).Result; +// You can now use the reservation to write data +processMemory.Write(reservation.Address, 42); +``` + +### Using ProcessMemory + +The `Reserve` method is a more convenient way to create a reservation without having to manage the allocation yourself. It will browse existing allocations to find one that satisfies the required size, create one if none exists, and then make a reservation in it. + +```csharp +// Reserve 256 bytes of memory in the target process, without manually creating a new allocation +MemoryReservation reservation = processMemory.Reserve(256, requireExecutable: false).Result; +// You can now use the reservation to write data +processMemory.Write(reservation.Address, 42); +``` + +## Conclusion + +To recapitulate, in most cases, you can use the `Store` method to store data in the target process's memory without worrying about allocations and reservations. If you need more control over memory management, you can use the `Reserve` method to create reservations directly, or, if you need even more control, you can use `Allocate` to manage allocations yourself. diff --git a/docs/guide/mcfeatures/attaching.md b/docs/guide/mcfeatures/attaching.md new file mode 100644 index 0000000..aa00b19 --- /dev/null +++ b/docs/guide/mcfeatures/attaching.md @@ -0,0 +1,75 @@ +# Attaching to a process + +Manipulating the memory of a running process with MindControl requires attaching to that process to get a `ProcessMemory` instance. In this section, we will cover how to attach to a process using the `MindControl` library. + +## The Process Tracker + +In most cases, the best way to attach to a process is to use the `ProcessTracker` class. This class has a very simple API: you specify the name of the process you want to attach to, and then you can get a `ProcessMemory` instance whenever you want to perform a memory manipulation operation. If the process is not running at the time of the call, it will return `null`. + +Here is an example of how to use a `ProcessTracker` in a class: + +```csharp +public class SlimeRancherProcess +{ + // Keep an instance of the ProcessTracker in your class and give it the name of the target process. + private readonly ProcessTracker _processTracker = new("SlimeRancher"); + + public int? GetCoinCount() + { + // Attempt to get the ProcessMemory instance for the target process + // If the process is not running, this will return null + var process = _processTracker.GetProcessMemory(); + if (process == null) + return null; + + // Use the ProcessMemory instance to read a value from the target process + var coinCountResult = process.Read("UnityPlayer.dll+0168EEA0,8,100,28,20,80"); + return coinCountResult.ValueOr(0); + } +} +``` + +The `ProcessTracker` class allows you to easily attach to your target process, without having to worry about: +- The order in which the processes are started: if your program starts first, `GetProcessMemory` will just return `null` until the target process is started. +- The process being closed: if the target process is closed, `GetProcessMemory` will return `null` after the process is closed. +- The process being restarted: if the target process is restarted, `GetProcessMemory` will return a new `ProcessMemory` instance for the new process. You don't have to care about this. + +## Attaching directly through the `ProcessMemory` class + +There are cases where you might want to attach to a process directly without using the `ProcessTracker`. For example: +- You know the process ID of the target process and want to attach to it directly. +- You are building a "one-shot" tool that attaches to a process, performs some operations, and then exits. +- You want to attach to multiple processes with the same name. + +For these cases, you can use the `ProcessMemory` class directly. Here are examples of how to do this: + +### Attaching to a process by name + +```csharp +using MindControl; + +var result = ProcessMemory.OpenProcessByName("MyTargetProcess"); // Replace with the actual process name +result.ThrowOnFailure(); // Throws an exception if the process could not be opened +var processMemory = result.Value; +``` + +### Attaching to a process by PID (process ID) + +```csharp +using MindControl; + +var result = ProcessMemory.OpenProcessById(1234); // Replace with the actual process ID +result.ThrowOnFailure(); // Throws an exception if the process could not be opened +var processMemory = result.Value; +``` + +### Attaching to a process with a System.Diagnostics.Process instance + +```csharp +using MindControl; + +var process = System.Diagnostics.Process.GetProcessById(1234); // You can use other methods to get the Process instance +var result = ProcessMemory.OpenProcess(process); +result.ThrowOnFailure(); // Throws an exception if the process could not be opened +var processMemory = result.Value; +``` \ No newline at end of file diff --git a/docs/guide/mcfeatures/code-manipulation.md b/docs/guide/mcfeatures/code-manipulation.md new file mode 100644 index 0000000..2f3b809 --- /dev/null +++ b/docs/guide/mcfeatures/code-manipulation.md @@ -0,0 +1,169 @@ +# Manipulating code + +This section is a bit more advanced and will explain how to manipulate code in the target process using MindControl. + +> [!NOTE] +> This section requires the `MindControl.Code` package to be installed in addition to the `MindControl` package. + +## What is code manipulation? + +Code manipulation refers to the ability to modify the executable code of a running process. This can include removing or changing instructions, injecting new code, or redirecting execution flow. Code manipulation is often used in game hacking, reverse engineering, and debugging scenarios. + +Executable code is stored in the process memory, just like any other data, under the form of instruction bytes called opcodes. These opcodes are executed by the CPU to perform various operations, such as arithmetic calculations, memory access, and control flow changes (jumping to a different instruction, often depending on various conditions). + +Manipulating code can be challenging, because messing up a single bit in an instruction often leads to crashes or unexpected behavior. Injecting new code without modifying the existing code is especially difficult, because you cannot just insert new instructions in the middle of existing code without breaking the flow of execution. + +## Opcodes + +Opcodes are the machine-level instructions that the CPU executes. Each opcode corresponds to a specific operation, usually very basic, such as adding two numbers, jumping to a different instruction, or calling a function. Opcodes are represented as byte sequences in memory, and they can vary depending on the CPU architecture (e.g., x86, x64). + +When using a memory hacking tool such as Cheat Engine, you can view the disassembled code, which shows the opcodes in a human-readable format (assembly language). This allows you to see what the code is doing and how it is structured, but it is still very complex to understand, because meaningful operations (like "fire a bullet") are made of thousands of opcodes, each performing a basic operation that has no obvious meaning by itself. + +Here are some examples of common opcodes in x64 architecture: +```assembly +mov eax, 1 ; B8 01 00 00 00 - Move the value 1 into the EAX register. +add eax, 2 ; 05 02 00 00 00 - Add the value 2 to the EAX register. +jmp 0x12345678 ; (Bytes depend on multiple factors) - Jump to the instruction at address 0x12345678. +call 0x12345678 ; (Bytes depend on multiple factors) - Call the function at address 0x12345678 +nop ; 90 - No operation, does nothing. +``` + +Note that the opcodes are not always the same length, and some operations can take different forms depending on the operands used. For example, the `jmp` and `call` instructions can have different byte sequences depending on whether they use absolute addresses, relative offsets, or other addressing modes. + +> [!NOTE] +> When diving into code manipulation, it is very advisable to learn the basics of assembly language. This is outside the scope of this guide, but there are many resources available online to help you get started. + +## Removing code instructions + +The easiest code manipulation operation is to remove code instructions. This can be done by overwriting the instructions with `NOP` (No Operation) instructions, which effectively make the code do nothing. + +Fortunately, `NOP` instructions are only one byte long, so you can replace any instruction with a number of `NOP` instructions without changing the size of the code. For example, removing a `mov eax, 1` instruction would be as simple as writing 5 bytes of `0x90` (the opcode for `NOP`) at the address of the instruction. + +MindControl provides easy-to-use methods to remove code instructions. The `DisableCodeAt` method disables a number of instructions at a specific address by overwriting them with `NOP` instructions. + +```csharp +// Disable 5 instructions, starting at the address "mygame.exe+0168EEA0" +CodeChange codeRemoval = processMemory.DisableCodeAt("mygame.exe+0168EEA0", 5); + +// Disposing the CodeChange object will restore the original code +codeRemoval.Dispose(); +``` + +> [!NOTE] +> It is important to consider that removing code instructions can lead to unexpected behavior, especially if the removed instruction is part of a larger control flow structure (like a loop or a conditional statement). If you don't know what the removed instructions do, chances are you will cause a crash. + +## Injecting code + +Injecting code is a more advanced operation that allows you to add new instructions to the target process. This can be used to implement custom functionalities or to modify existing behavior. + +Because we cannot just insert new instructions in the middle of existing code and shift everything around, this is usually done through a hook. A hook is a technique that intercepts the execution flow of the target process and redirects it to your custom code, usually redirecting it back to the original code afterward. + +The steps are the following: +1. Allocate executable memory in the target process to store the new code. Ideally, the code is located near the original code, to optimize performance. +2. Write the new code to the allocated memory. +3. Overwrite the original code at the address of the target instruction with a jump instruction that redirects execution to the new code. + +Typically, the code you write at step 2 will end with a jump instruction that redirects execution back to the original code, so that the original code can continue executing after your custom code has run. This is often referred to as a "trampoline". (So now you know how to build trampoline hooks.) + +If you want to inject code without replacing functionality, the code you write at step 2 may start with whatever instructions end up being replaced by the jump instruction at step 3. + +There is a big issue with that though. The code you write will often use registers and set CPU flags, meaning that, when you redirect execution back to the original code, the state of the CPU will not be what the original code expects. This often leads to crashes or unexpected behavior. To protect against this, you need to save the state of the CPU before executing your custom code, and restore it afterward. This is often done by pushing the registers onto the stack at the start of your custom code, and popping them back at the end. + +We won't go into the details of how to write assembly code for this, but we will see how to do it using MindControl. + +Whatever you want to achieve, MindControl provides three ways to inject code. + +### Using InsertCodeAt + +The `InsertCodeAt` method of `ProcessMemory` allows you to inject code at a specific instruction in the target process. It takes either the address of a pointer path to the address of an instruction, and the code to inject. In this variant, the code is going to be executed before the original instruction, and the original instruction will be executed afterward. No instructions are removed, the original code is preserved entirely. + +The code you provide is either a byte array, or a `Iced.Intel.Assembler` object that contains some code ready to be assembled. The `Iced.Intel.Assembler` class is part of the Iced project, which is a library for disassembling and assembling x86/x64 code. You can use it to line up assembly code in your .net project that you can then inject through MindControl. + +```csharp +// Create an assembler and write some code to it +var assembler = new Assembler(64); +assembler.mov(rcx, value); +// ... + +// Insert the code at the address "mygame.exe+0168EEA0" +CodeChange codeInjection = processMemory.InsertCodeAt("mygame.exe+0168EEA0", assembler).Value; + +// Disposing the CodeChange object restores the original code and frees the memory reservation where code was written +codeInjection.Dispose(); +``` + +### Using ReplaceCodeAt + +Similarly, the `ReplaceCodeAt` method allows you to replace one or more instructions with your own code. This is useful when you want to modify the behavior of existing code. + +The differences with `InsertCodeAt` are: +- You can specify the number of instructions to replace, and the code you provide will be executed **instead of** the original instructions. +- If your code is shorter or equal in size to the original instructions, the original code will simply be overwritten. If your code is longer, a hook will be performed. You don't have to worry about that. + +```csharp +// Create an assembler and write some code to it +var assembler = new Assembler(64); +assembler.mov(rcx, value); +// ... + +// Replace 3 instructions, starting at the instruction at address "mygame.exe+0168EEA0", with the code we just prepared +CodeChange codeInjection = processMemory.ReplaceCodeAt("mygame.exe+0168EEA0", 3, assembler).Value; + +// Disposing the CodeChange object restores the original code and frees the memory reservation where code was written +// (in cases where a hook was necessary). +codeInjection.Dispose(); +``` + +### Using Hook + +Finally, the `Hook` method allows you to specify what kind of hook to perform, through a `HookOptions` object. This method provides slightly more control over the hook, but it's almost always possible to achieve the same result using either `InsertCodeAt` or `ReplaceCodeAt`. + +```csharp +// Create an assembler and write some code to it +var assembler = new Assembler(64); +assembler.mov(rcx, value); +// ... + +// Create hook options to specify the type of hook to perform +HookOptions hookOptions = new HookOptions(HookExecutionMode.ExecuteInjectedCodeFirst); + +// Hook the instruction at address "mygame.exe+0168EEA0" with the code we just prepared +CodeChange codeInjection = processMemory.Hook("mygame.exe+0168EEA0", assembler, hookOptions).Value; + +// Disposing the CodeChange object restores the original code and frees the memory reservation where code was written +codeInjection.Dispose(); +``` + +> [!NOTE] +> It's generally discouraged to use the `Hook` method directly, as it is more complex and less intuitive than the other two methods. The `InsertCodeAt` and `ReplaceCodeAt` methods are usually better and easier to read. + +### Code isolation + +As we have previously touched, injecting code can lead to unexpected behavior if the injected code does not properly handle the CPU state. To make sure that your injected code does not interfere with the original code, you have to save and restore the CPU registers and flags before and after executing your custom code. + +You can do this manually by pushing the registers onto the stack at the start of your custom code, and popping them back at the end. However, MindControl provides a more convenient way to do this through an additional parameter in both `InsertCodeAt` and `ReplaceCodeAt`. + +This parameter is an array of `HookRegister`, an enumeration that you can use to list the registers that your code uses. When performing the code manipulation, MindControl will automatically save and restore the state of these registers. + +Additionally, if your injected code is very complex or if you want to make sure that it does not interfere with the original code, you can use one of the pre-made arrays available through the `HookRegisters` static class. For example, `HookRegisters.AllCommonRegisters` is a pre-made array of all commonly used registers, and using it in a code manipulation operation will almost always guarantee that your code does not interfere with the original code. + +In performance-critical scenarios, you should try to list only the registers that your code actually uses, to avoid the overhead of saving and restoring unnecessary registers. + +#### Example using a few registers + +```csharp +// Save and restore the state of the RCX and RBX registers, and the CPU flags +CodeChange codeInjection = processMemory.InsertCodeAt("mygame.exe+0168EEA0", assembler, + HookRegister.RcxEcx, HookRegister.RbxEbx, HookRegister.Flags).Value; +``` + +#### Example using all common registers + +```csharp +// Using all common registers is a catch-all solution, but runs slower +CodeChange codeInjection = processMemory.InsertCodeAt("mygame.exe+0168EEA0", assembler, + HookRegisters.AllCommonRegisters).Value; +``` + +> [!NOTE] +> In the `HookRegister` enumeration, x64 and x86 versions of the same register are grouped together, because MindControl is not compiled against a specific architecture. For example, `HookRegister.RcxEcx` refers to the x64 `RCX` register if your target process is x64, or the x86 `ECX` register if your target process is x86. \ No newline at end of file diff --git a/docs/guide/mcfeatures/dll-injection.md b/docs/guide/mcfeatures/dll-injection.md new file mode 100644 index 0000000..76b40f7 --- /dev/null +++ b/docs/guide/mcfeatures/dll-injection.md @@ -0,0 +1,45 @@ +# Injecting a DLL + +A common technique in process hacking is to inject a DLL into the target process. This allows you to run custom code within the context of the target process, which can be useful for various purposes, such as modifying the behavior of the application, hooking functions, or even creating a user interface. + +## Creating a DLL for injection + +To create a DLL for injection, you can write a simple C++ program with an `APIENTRY` function that will be called when the DLL is loaded. Here's a basic example: + +```c++ +#include +#include + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + // This code will run when the DLL is injected into the target process + // In this example, we will just show a messagebox, but you can replace this with your own code + MessageBoxA(NULL, "Injected library attached", "DLL Injection", MB_OK | MB_ICONINFORMATION); + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} +``` + +Compile this code into a DLL using your preferred C++ compiler. Make sure to set the output type to "Dynamic Link Library" (DLL), and use the appropriate bitness (x86 or x64) that matches the target process you want to inject into. Then, you can add this DLL to your MindControl project. + +> [!NOTE] +> When referencing the DLL in your MindControl project, just add it as a content file, and not a reference. Make sure that the DLL is copied to the output directory of your project. + +## Injecting the DLL using MindControl + +Once you have your DLL ready, you can inject it into the target process using the `ProcessMemory.InjectLibrary` method. + +```csharp +// Pay attention to the result of the method call, as there are many reasons why the injection might fail +processMemory.InjectLibrary("path_to_your_dll.dll").ThrowIfFailure(); +``` + +This method will attempt to inject the specified DLL into the target process. If the injection is successful, the code in the `DllMain` function of your DLL will be executed before the method returns. For example, if you used the code provided above, you should see the message "Injected library attached" in a message box when the DLL is injected successfully. \ No newline at end of file diff --git a/docs/guide/mcfeatures/freezing.md b/docs/guide/mcfeatures/freezing.md new file mode 100644 index 0000000..de86a88 --- /dev/null +++ b/docs/guide/mcfeatures/freezing.md @@ -0,0 +1,20 @@ +# Freezing values in memory + +This section covers a technique often called "freezing". The idea is to lock the value of a variable in memory so that it does not change, even as the game tries to modify it. This can be useful for debugging or, let's be real, to make cheats in games. + +In MindControl, this is done through memory anchors. An anchor is like a persisting reference to a specific variable in memory, of a specific type, and it allows you to perform various operations on that specific target. + +```csharp +var anchor = processMemory.GetAnchor(); + +// Freeze that variable to the value 1234567 +var freezer = anchor.Freeze(1234567); + +// To unfreeze the variable, dispose the freezer +freezer.Dispose(); +``` + +When you freeze a variable, MindControl will continuously write the specified value to the target memory location, effectively locking it in place. This means that even if the game changes the value, it will be very quickly overwritten with the frozen value you chose. + +> [!NOTE] +> Freezing is resource-intensive, as it requires continuous writes to memory. Be careful not to overuse it, and make sure you dispose freezers that you no longer need. diff --git a/docs/guide/mcfeatures/functions.md b/docs/guide/mcfeatures/functions.md new file mode 100644 index 0000000..fefc6a7 --- /dev/null +++ b/docs/guide/mcfeatures/functions.md @@ -0,0 +1,108 @@ +# Executing remote functions + +This section covers how to execute functions in your target process. This can allow you to perform actions in the target process from your program, or to call your own functions after injecting a DLL into the target process. + +You can execute functions with the method `RunThread`. There are two main ways to call this method: + +## With an address or pointer path + +You can call a function by providing its address or a pointer path to it. This is useful when you know the exact location of the function in memory. + +```csharp +var result = processMemory.RunThread("UnityPlayer.dll+0168EEA0,8,100,28,20,80"); +result.ThrowOnFailure(); // Throws an exception if the function could not be executed + +// Wait for the function to complete, with a timeout of 10 seconds +result.Value.WaitForCompletion(TimeSpan.FromSeconds(10)); + +// Dispose the result when done to ensure resources are released +result.Dispose(); +``` + +## With a module and function name + +You can also call a function by providing the module name and the function name. This is useful when you want to call a function in a specific module without needing to know its address. This uses the module's export table to find the function, and thus requires the module to explicitly export the function. + +In the following example, we call the `ExitProcess` function from the Windows kernel library `kernel32.dll`. This module is loaded in every Windows process, so you can use its functions without having to inject a DLL. + +```csharp +var result = processMemory.RunThread("kernel32.dll", "ExitProcess"); +result.ThrowOnFailure(); // Throws an exception if the function could not be executed + +// Wait for the function to complete, with a timeout of 10 seconds +result.Value.WaitForCompletion(TimeSpan.FromSeconds(10)); + +// Dispose the result when done to ensure resources are released +result.Dispose(); +``` + +## Function parameters + +You can also pass parameters to the function you are calling. This is an advanced feature that requires you to understand how the target function expects its parameters to be passed. In MindControl, parameters are passed through a single pointer. You can arrange your parameters in a structure, store them in memory (e.g. using `Store`), and then pass the pointer to that memory as the parameter. + +```csharp +struct MyFunctionParams { public int Param1; public float Param2;} +var myParams = new MyFunctionParams { Param1 = 42, Param2 = 3.14f }; +var paramsPointer = processMemory.Store(myParams).Value.Address; +var result = processMemory.RunThread("myprocess.exe+019BAEA1", paramsPointer); +result.Value.WaitForCompletion(TimeSpan.FromSeconds(10)); +result.Dispose(); +``` + +However, this will only work for a specific argument passing convention. What this does is store the pointer in the RCX register (or EBX for 32-bit processes) before calling the function. + +If the function is not designed to accept parameters in this way, you will need to use a different approach, such as writing a wrapper function that prepares the parameters and calls the target function (also called a trampoline). + +Here is an example where we call the `GetCurrentDirectoryW` function from `kernel32.dll`, which expects two parameters: a buffer size and a pointer to a buffer where the current directory will be stored. + +> [!NOTE] +> The following example is complex and requires some understanding of assembly code and registers. For most use cases, you won't need to perform trampoline calls. Don't worry about this unless you need it. + +```csharp +// Define the structure that holds the parameters for GetCurrentDirectoryW +// The attribute prevents the compiler from adding padding bytes, ensuring the structure is packed tightly in memory. +[StructLayout(LayoutKind.Sequential, Pack = 1)] +struct GetCurrentDirectoryWArgs { public uint BufferSize; public ulong BufferAddress; } + +// Reserve memory for the buffer where the current directory will be stored +var bufferReservation = processMemory.Reserve(2048, false).Value; + +// Create an instance of the structure and store it in memory +var args = new GetCurrentDirectoryWArgs { BufferSize = 2048, BufferAddress = bufferReservation.Address }; +var argsReservation = processMemory.Store(args).Value; + +// Retrieve the address of the GetCurrentDirectoryW function +var kernel32Module = processMemory.GetModule("kernel32.dll"); +var functionAddress = kernel32Module!.ReadExportTable().Value["GetCurrentDirectoryW"]; + +// Using Iced.Intel, prepare the trampoline code to call GetCurrentDirectoryW +// Make sure to use "using static Iced.Intel.AssemblerRegisters;" to use the registers in a readable way +var assembler = new Assembler(64); +// In x64, the function uses the fastcall calling convention, i.e. RCX and RDX are used for the two arguments. +// When the thread is created, the thread parameter is in RCX. In this case, our parameter is going to be the +// address of a GetCurrentDirectoryWArgs struct holding the parameters we want. +assembler.mov(rax, rcx); // Move the address of the GetCurrentDirectoryWArgs struct to RAX, to free up RCX +assembler.mov(ecx, __dword_ptr[rax]); // Move the buffer size (first argument) to ECX/RCX +assembler.mov(rdx, __[rax+4]); // Move the buffer address (second argument) to RDX +assembler.call(functionAddress.ToUInt64()); // Call GetCurrentDirectoryW +assembler.ret(); + +// Store the trampoline code in the target process memory +// The nearAddress parameter is used to favor allocations close to the kernel32.dll module, for more efficient jumps +// This code requires the MindControl.Code package +var codeReservation = processMemory.StoreCode(assembler, nearAddress: kernel32Module.GetRange().Start).Value; + +// Now we can run the trampoline code in a new thread, passing the address of the GetCurrentDirectoryWArgs struct +var threadResult = processMemory.RunThread(codeReservation.Address, argsReservation.Address); +threadResult.Value.WaitForCompletion(TimeSpan.FromSeconds(10)).ThrowOnFailure(); + +// Read the resulting string from the allocated buffer +var resultingString = processMemory.ReadRawString(bufferReservation.Address, Encoding.Unicode, 512).Value; +Console.WriteLine($"Current Directory: {resultingString}"); // This should print the directory of the target process + +// Dispose everything to ensure resources are released +bufferReservation.Dispose(); +argsReservation.Dispose(); +codeReservation.Dispose(); +threadResult.Dispose(); +``` diff --git a/docs/guide/mcfeatures/monitoring.md b/docs/guide/mcfeatures/monitoring.md new file mode 100644 index 0000000..619757f --- /dev/null +++ b/docs/guide/mcfeatures/monitoring.md @@ -0,0 +1,27 @@ +# Monitoring value changes in memory + +This section covers how to monitor changes to a variable in memory. This can be useful if you need your application to react to changes in your target process, without having to set up timers or other polling mechanisms yourself. + +You can monitor changes to a variable in memory through anchors. An anchor is a persistent reference to a specific variable in memory, of a specific type, and it allows you to perform various operations on that specific target. + +```csharp +var anchor = processMemory.GetAnchor(); +var watcher = anchor.Watch(TimeSpan.FromMilliseconds(100)); // Read every 100 milliseconds +watcher.ValueChanged += (_, args) => +{ + Console.WriteLine($"Value changed from {args.PreviousValue} to {args.NewValue}."); +}; +watcher.ValueLost += (_, args) => +{ + Console.WriteLine($"Value lost. Last known value is {args.LastKnownValue}."); +}; +watcher.ValueReacquired += (_, args) => +{ + Console.WriteLine($"Value no longer lost. The new value is {args.NewValue}."); +}; + +// ... + +// To stop watching the value, dispose the watcher +watcher.Dispose(); +``` diff --git a/docs/guide/mcfeatures/pointer-paths.md b/docs/guide/mcfeatures/pointer-paths.md new file mode 100644 index 0000000..232e9d2 --- /dev/null +++ b/docs/guide/mcfeatures/pointer-paths.md @@ -0,0 +1,91 @@ +# Understanding pointer paths + +Most methods in the `ProcessMemory` class that accept an address also accept a pointer path. A pointer path is a string that describes a sequence of memory addresses and offsets, allowing you to navigate through complex data structures in the target process's memory. More importantly, they allow you to get to the value you want reliably, no matter how the target process shuffles its memory around. + +They are usually represented by a string called a pointer path expression. Here is an example of a pointer path expression: + +`UnityPlayer.dll+0168EEA0,8,100,28,20,80` + +In this example, the pointer path expression consists of: +- `UnityPlayer.dll+0168EEA0`: This is the base address of the module (DLL) in the target process's memory, with an offset of `0168EEA0` bytes. +- `8,100,28,20,80`: These are offsets that will be applied to the base address to navigate through the memory structure. Each number represents an offset in bytes from the previous address. + +This expression is the equivalent of the following pointer in Cheat Engine: + +![Pointer in Cheat Engine](../../images/ce-pointer-example.png)
+*To convert a pointer in Cheat Engine to a pointer path expression, read it from bottom to top, separating every field with a comma.* + +Pointer paths don't have to be based on modules, they can also be based on pointers. For example, if you have a base pointer address of `0x4A018C30A`, you can create a pointer path like this: + +`0x4A018C30A,8,100,28,20,80` + +You can also use `+` and `-` operators within pointer paths, e.g.: + +`UnityPlayer.dll+0168EEA0,100+8C,28-4,20,80` + +## Building pointer paths + +Pointer paths can be built in a number of ways: + +### Using the expression constructor + +```csharp +PointerPath myPath = new PointerPath("UnityPlayer.dll+0168EEA0,8,100,28,20,80"); +``` + +### Using PointerPath.TryParse + +Use this method when your pointer path is coming from an external source, such as user input or a configuration file. It will return `null` if the parsing fails, allowing you to handle errors gracefully. + +```csharp +var path = PointerPath.TryParse("UnityPlayer.dll+0168EEA0,8,100,28,20,80"); +if (path != null) +{ + // Successfully parsed the pointer path +} +else +{ + // Failed to parse the pointer path +} +``` + +### Using implicit string conversion + +This is a convenient way to create a `PointerPath` from a string without using the constructor directly. The string will be parsed automatically. + +```csharp +PointerPath myPath = "UnityPlayer.dll+0168EEA0,8,100,28,20,80"; +``` + +This can also be used with any method that accepts a `PointerPath` as an argument: + +```csharp +var someValue = processMemory.Read("UnityPlayer.dll+0168EEA0,8,100,28,20,80").ValueOrDefault(); +``` + +> [!NOTE] +> When using pointer string expressions, it's more performant to store and reuse your `PointerPath` instances instead of creating new ones every time you need to read or write memory. This avoids the overhead of parsing the string each time. + +### Using the module constructor + +This is more performant than the expression constructor, as it does not require parsing the string. + +```csharp +PointerPath myPath = new PointerPath("UnityPlayer.dll", 0x168EEA0, 0x8, 0x100, 0x28, 0x20, 0x80); +``` + +### Using the pointer constructor + +This is a variant of the module constructor that allows you to specify the base address as a pointer when your path is not based on a module. + +```csharp +PointerPath myPath = new PointerPath(0x4A018C30A, 0x8, 0x100, 0x28, 0x20, 0x80); +``` + +## Evaluating a pointer path + +When using a pointer path for memory manipulations, behind the scenes, MindControl will start by evaluating the pointer path to get the target address out of it. In some cases, you may want to do just that and nothing else. For this, you can use the `EvaluateMemoryAddress` method. + +```csharp +UIntPtr address = processMemory.EvaluateMemoryAddress("UnityPlayer.dll+0168EEA0,8,100,28,20,80").ValueOrDefault(); +``` diff --git a/docs/guide/mcfeatures/reading.md b/docs/guide/mcfeatures/reading.md new file mode 100644 index 0000000..2008f3e --- /dev/null +++ b/docs/guide/mcfeatures/reading.md @@ -0,0 +1,94 @@ +# Reading memory + +This section will explain how to read data from the target process using MindControl. + +> [!NOTE] +> Reading string values is a more complex topic and is covered in the [Manipulating string](./strings.md) section of this guide. + +## Reading numeric values + +To read numeric values from the target process, you can use the `Read` method of the `ProcessMemory` class. This method takes either an address or a pointer path, and returns a result containing either the read value in the asked type, or a failure in case the read operation failed. + +Most numeric types are supported, including `int`, `float`, `double`, `long`, `bool`, and others. The read operation will attempt to read the value from the specified address or pointer path in the target process's memory. + +Here are some examples of how to read numeric values from the target process: + +### Reading an integer value from a pointer path + +```csharp +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +// Read an integer value from the specified address. Default to 0 if the read fails. +int health = processMemory.Read("GameAssembly.dll+12345678").ValueOr(0); +``` + +### Reading a float value from an address + +```csharp +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +// Read a float value from the specified address. Default to 0.0f if the read fails. +float speed = processMemory.Read(0x1A2B3C4).ValueOr(0.0f); +``` + +## Reading arbitrary structures + +When you need to read multiple values in the same structure, you can define a struct that represents the data structure you want to read, and then use the same `Read` method to read the entire structure at once. This is more performant than reading each field individually, especially when using pointer paths. + +In most cases, you won't need all the fields of the structure, so you can define only the fields you are interested in. To handle these cases, you can use `[FieldOffset]` attributes to specify the offset of each field in the structure. This allows you to define a structure that only contains the fields you need, while still being able to read the entire structure in a single read operation. + +Here are some examples: + +### Reading a custom unmarked structure + +```csharp +// Define a structure that represents the data you want to read +// Fields must be in the same order as they are in memory +struct PlayerStats +{ + public int Health; + public float Speed; + public long Score; +} + +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +PlayerStats playerStats = processMemory.Read("GameAssembly.dll+12345678").ValueOrDefault(); +``` + +### Reading a custom structure with field offsets + +```csharp +using System.Runtime.InteropServices; +// Define a structure with explicit field offsets +// This allows you to read only the fields you are interested in, even if they are not contiguous in memory. +[StructLayout(LayoutKind.Explicit)] // This is required for field offsets to be respected +struct PlayerStats +{ + [FieldOffset(0x00)] public int Health; + [FieldOffset(0x0A)] public float Speed; + [FieldOffset(0xF0)] public long Score; +} + +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +PlayerStats playerStats = processMemory.Read("GameAssembly.dll+12345678").ValueOrDefault(); +// Remarks: even though only 3 fields are defined, the structure is 0xF8 bytes long, because it covers the whole memory +// area from 0x00 to the highest field offset plus its length. Don't use this approach if your fields are too far apart. +``` + +## Reading raw bytes + +Sometimes, you may want to read raw bytes, without interpreting them as a specific type. You can use the `ReadBytes` method of the `ProcessMemory` class to read a specified number of bytes from a given address or pointer path. + +```csharp +// Read 16 bytes from the specified address in the target process +byte[] rawData = processMemory.ReadBytes("GameAssembly.dll+12345678", 16).ValueOr([]); +``` + +There is also a `ReadBytesPartial` method variant that takes a `byte[]` array as a parameter, and populates it with the read data. Contrary to `ReadBytes`, this method will not fail if only some bytes could be read, and will return the number of bytes that were actually read. + +```csharp +// Read up to 2048 bytes into a buffer, and get the number of bytes actually read +byte[] buffer = new byte[2048]; +int bytesRead = processMemory.ReadBytesPartial("GameAssembly.dll+12345678", buffer, 2048).ValueOr(0); +``` + +> [!NOTE] +> `ReadBytesPartial` is only useful in particular cases, when reading large batches of an unreliable memory area. Most of the time, `ReadBytes` is simpler to use and preferable. diff --git a/docs/guide/mcfeatures/results.md b/docs/guide/mcfeatures/results.md new file mode 100644 index 0000000..a2ba21e --- /dev/null +++ b/docs/guide/mcfeatures/results.md @@ -0,0 +1,79 @@ +# Understanding Results + +Most methods in `MindControl` return a `Result` or `Result` object, which encapsulates the success or failure of the operation, along with any relevant data or error messages. This allows you to handle errors gracefully and provides a consistent way to check the outcome of operations. + +For example, when you read a value from a process, you can check if the operation was successful and retrieve the value if it was: + +```csharp +Result readResult = processMemory.Read("GameAssembly.dll+12345678"); +``` + +Instead of directly returning an `int`, and throwing an exception or returning `0` on failure, we get a `Result`. You can then check if the read operation was successful using the `IsSuccess` property: + +```csharp +if (readResult.IsSuccess) +{ + int value = readResult.Value; + // Use the value as needed +} +else +{ + // Handle the error + Console.WriteLine($"Error reading value: {readResult.Failure.Message}"); +} +``` + +This pattern is used throughout the `MindControl` library, allowing you to handle errors in a consistent way without relying on exceptions for control flow. + +Even though relying on exceptions is generally discouraged, because they are slower and tend to make the code harder to read, you can use the `ThrowOnFailure()` method to throw an exception if the operation failed, which can be useful in scenarios where you want to enforce error handling: + +```csharp +readResult.ThrowOnFailure(); // Throws an exception if the read operation failed +int value = readResult.Value; // After the exception check, you can safely use the value +``` + +> [!NOTE] +> Accessing the `Value` property of an unsuccessful `Result` will also throw an exception. + +If you prefer to discard errors and just use default values, you can use the `ValueOrDefault()` method, which returns the value if the operation was successful, or a default value (like `0` for numeric types) if it failed: + +```csharp +int value = processMemory.Read("GameAssembly.dll+12345678").ValueOrDefault(); +// value will be 0 if the read operation failed +``` + +Alternatively, you can use `ValueOr()` to provide a custom default value: + +```csharp +int value = processMemory.Read("GameAssembly.dll+12345678").ValueOr(42); +// value will be 42 if the read operation failed +``` + +## The Failure object + +When an operation fails, the `Result` object contains a `Failure` property that provides detailed information about the error. The `Failure` base class itself contains only a `Message` property, that describes the error, but methods usually return a more specific type of `Failure` that provides additional context. + +```csharp +// Ask the user for a pointer path +string pointerPath = Console.ReadLine(); + +// Use the pointer path to read a value +var readResult = processMemory.Read(pointerPath); +if (!readResult.IsSuccess) +{ + // Handle the error in a different way based on the specific failure type + // In this example, we just print a different message for each failure type, but the idea is that you can perform + // different actions based on the type of failure if you need to. + readResult.Failure switch + { + BaseModuleNotFoundFailure f => Console.WriteLine($"The module you entered ({f.ModuleName}) is invalid!"), + DetachedProcessFailure _ => Console.WriteLine($"The process has exited."), + IncompatiblePointerPathBitnessFailure _ => Console.WriteLine("The pointer path you entered is not compatible with the 32-bit target process!"), + _ => Console.WriteLine($"An unexpected error occurred: {readResult.Failure}") + }; + return; +} + +int value = readResult.Value; +// (Use the value as needed) +``` diff --git a/docs/guide/mcfeatures/searching.md b/docs/guide/mcfeatures/searching.md new file mode 100644 index 0000000..e185ac0 --- /dev/null +++ b/docs/guide/mcfeatures/searching.md @@ -0,0 +1,52 @@ +# Searching for byte sequences or patterns + +This section explains how to search through the memory of the target process for specific byte sequences or byte patterns. This type of search is sometimes called "AoB (Array of Bytes) scanning", and it's useful to find specific data arrangements in memory, especially in cases where pointer paths fail or are harder to pull off. + +Let's jump right into an example to make things clearer. + +```csharp +IEnumerable results = processMemory.SearchBytes("90 A8 00 00 ?? 42 A8"); +``` + +In this example, we are searching for a specific byte pattern in the target process's memory. The pattern consists of some set hexadecimal byte values, and the `??` is a wildcard that matches any byte value at that position. The search will return all addresses where this pattern is found. + +So this example would match, for instance, `90 A8 00 00 01 42 A8` or `90 A8 00 00 FF 42 A8`, but not `90 A8 00 01 42 A8`. + +Now, this example searches the entire memory of the target process, which is usually very slow, especially if your target process uses up a lot of memory. For modern 3D games, this can easily take over a minute. Let's dive into how to make this search more efficient. + +> [!NOTE] +> Do not start your patterns with a wildcard (`??`). Even though this is supported, it will slow down the search significantly, and has no practical purpose. You can remove leading wildcards from your patterns to achieve the same result without the performance hit. + +## Restricting the search range + +The second parameter of the `SearchBytes` method allows you to specify a range of memory addresses to search in. This can significantly speed up the search process, especially if you know where the data you're looking for is likely to be located. + +```csharp +MemoryRange range = new MemoryRange(0x10000000, 0x20000000); +IEnumerable results = processMemory.SearchBytes("90 A8 00 00 ?? 42 A8", range); +``` + +The typical way to use this parameter is to get a specific module in the target process, and then search only within that module's memory range. Here's how: + +```csharp +RemoteModule module = processMemory.GetModule("GameAssembly.dll") ?? throw new Exception("Module not found"); +IEnumerable results = processMemory.SearchBytes("90 A8 00 00 ?? 42 A8", module.GetRange()); +``` + +## Specifying settings to filter out invalid results + +Another way to both speed up the search *and* filter out unwanted results is to use the third `FindBytesSettings` parameter. This allows you to specify additional criteria for the search, to ignore certain ranges of memory depending on their properties, or to specify a maximum number of results. + +```csharp +RemoteModule module = processMemory.GetModule("GameAssembly.dll") ?? throw new Exception("Module not found"); +var settings = new FindBytesSettings +{ + SearchReadable = true, // Only search in readable memory + SearchWritable = null, // Don't care if the memory is writable or not + SearchExecutable = false, // Ignore executable memory, because we are looking for data, not code + MaxResultCount = 10 // Limit the number of results to 10 +}; +IEnumerable results = processMemory.SearchBytes("90 A8 00 00 ?? 42 A8", range, settings); +``` + +Using these settings can greatly improve the performance of your searches, but keep in mind that they are still extremely slow compared to pointer paths or direct memory reads. Use them only when you believe this is the best way to find the data you are looking for. diff --git a/docs/guide/mcfeatures/streams.md b/docs/guide/mcfeatures/streams.md new file mode 100644 index 0000000..29dfc23 --- /dev/null +++ b/docs/guide/mcfeatures/streams.md @@ -0,0 +1,26 @@ +# Using memory streams + +For various use cases, you may need to read or write memory using streams. For these situations, MindControl provides a `ProcessMemoryStream` class, instantiated through a `ProcessMemory` instance. + +This can be useful to read or write large amounts of data in a more efficient way, or more generally to work with memory as if it were a file. + +```csharp +// Get a stream that starts at a specific address in the target process +ProcessMemoryStream stream = processMemory.GetMemoryStream(0x123456789); + +// Read from the stream +byte[] buffer = new byte[1024]; +int bytesRead = stream.Read(buffer, 0, buffer.Length); + +// Write to the stream +stream.Write(buffer, 0, bytesRead); + +// Seek to a specific position in the stream (here, go 8 bytes after the initial address 0x123456789) +stream.Seek(8, SeekOrigin.Begin); + +// Dispose the stream when done +stream.Dispose(); +``` + +> [!NOTE] +> Internally, the stream uses `ReadPartial` so that it will still read as much as possible upon reaching an unreadable section of memory. When failing to read a single byte, the `Read` method will return 0. \ No newline at end of file diff --git a/docs/guide/mcfeatures/strings.md b/docs/guide/mcfeatures/strings.md new file mode 100644 index 0000000..ccafde0 --- /dev/null +++ b/docs/guide/mcfeatures/strings.md @@ -0,0 +1,180 @@ +# Manipulating strings + +Reading and writing strings in a process's memory is more complex than numeric values and requires understanding how strings are represented in memory. In this section, we will cover how strings are stored in memory, and how to manipulate them with `MindControl`. + +## Understanding string representation in memory + +Intuitively, strings are sequences of characters, but in memory, they are represented as a sequence of bytes. In some way or another, these bytes represent the characters in the string. But because their length is variable, **they are almost always referenced by a pointer**. For example, a `Player` structure could have a `Name` field that holds a pointer to the start of the string in memory. + +Now, once you locate a string, the key is then to understand how to translate its bytes into characters and vice versa. + +### Encodings + +The thing is, there are many ways to represent strings in memory, and it mostly comes down to what is called the "encoding". The encoding defines how characters are mapped to bytes. Some common encodings are: +- **ASCII**: Uses 1 byte per character, supports only the first 128 characters (English letters, digits, and some symbols). +- **UTF-8**: Uses 1 to 4 bytes per character, supports all Unicode characters, like symbols, chinese characters, emojis, etc. It is the most common encoding used in modern applications. +- **UTF-16**: Uses 2 bytes per character, also supports all Unicode characters, and is commonly used in Windows applications. + +You may also see other encodings like UTF-32, ISO-8859-1, or others. It all depends on the application and how it was developed. And in a single application, you may find different encodings used for different use cases, because some encodings are more efficient for certain types of data. For example, you may decide to use UTF-16 for a player name, to handle any language, but still use ASCII for internal item identifiers, because you don't need more than the English alphabet, and it uses up less memory. + +Let's take a look at some examples of how the same string is represented in memory with different encodings. + +#### Example: "Hello, World!" in different encodings + +| Encoding | Bytes in Hexadecimal | +| --- | --- | +| ASCII | 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 | +| UTF-8 | 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 | +| UTF-16 | 48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 57 00 6F 00 72 00 6C 00 64 00 21 00 | + +#### Example 2: "こんにちは" (Hello in Japanese) in different encodings + +| Encoding | Bytes in Hexadecimal | +| --- |----------------------------------------------| +| ASCII | (This string cannot be represented in ASCII) | +| UTF-8 | E3 81 93 E3 82 93 E3 81 AB E3 81 A1 E3 81 AF | +| UTF-16 | 53 30 93 30 6B 30 61 30 6F 30 | + +We can see several interesting things here: +- The ASCII and UTF-8 representations of "Hello, World!" are the same, because all characters in this string are part of the ASCII character set, and UTF-8 is designed to be backward compatible with ASCII. +- The Japanese string cannot be represented in ASCII, because it contains characters that are not part of the ASCII character set. However, it can be represented in both UTF-8 and UTF-16. +- UTF-16 always uses 2 bytes per character, which is kind of wasteful for ASCII-compatible strings like "Hello, World!" (you can see every second bit is a zero), but it ends up using way less space for the Japanese string. This is because it _always_ uses 2 bytes per character, while UTF-8 has variable-length characters, meaning it has to dedicate extra bits to indicate one way or another how many bytes are used for each character in the string. + +So, after locating a string in memory, we need to find out what encoding is used to represent it. + +### Knowing where the string stops + +When reading a string from memory, we also need to know where it ends. Because bytes in memory are not delimited and keep going on pretty much forever, it's important to know when to stop reading. And to do that, there are 2 main ways that programming languages and libraries use: +- **Null-terminators**: This is a special byte (`00`) or group of bytes (`00 00`) appended after the final character that indicates the end of the string. When reading a string, you keep reading bytes until you encounter a null terminator. This is common in C-style strings and is used in many programming languages. +- **Length prefix**: This is a byte or group of bytes before the first character of the string, that indicates how many bytes or characters follow it. When reading a string, you read the length first, and then read that many bytes. This is common in stacks like .net and Java. + +Sometimes, both techniques are used together, for compatibility. + +#### Example 1: Reading a string with a null terminator + +Let's say we have a string "Hello, World!" stored in memory as UTF-8, and we know it is null-terminated. The bytes in memory would look like this: + +``` +48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 00 +``` + +When reading this string, we would know to stop reading as soon as we encounter the null terminator `00`. The resulting string would be "Hello, World!". + +#### Example 2: Reading a string with a length prefix + +Now, let's say we have the same string "Hello, World!" stored in memory as UTF-8, but this time we have identified that it has a length prefix of 2 bytes. The bytes in memory would look like this: + +``` +0D 00 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 +``` + +When reading this string, knowing that it has a length prefix of 2 bytes, we would start by reading the length prefix `0D 00`. This is the number 13, meaning that the string is 13 bytes long. Then we would know to read only the next 13 bytes, resulting in the string "Hello, World!". + +### Type handles + +In some cases, especially in managed languages and frameworks like .net, strings, like any other object instance, start with a type handle. This is a pointer to a metadata structure that describes the type of the object (in this case, a string). This type handle is used for various purposes by the runtime. + +We don't care about this handle, but, when reading a string, we need to know if it exists and how long it is, so we can skip it. And when writing, we actually need to know the full handle, so we can write it properly, the way it would have been written by the runtime. + +#### An example of a typical .net string + +The .net standard for strings has all the things we've discussed so far, which makes it ideal for a final example. So let's take a look at a "Hello, World!" string in .net: + +``` +C0 12 34 56 78 9A BC DE 0D 00 00 00 48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 57 00 6F 00 72 00 6C 00 64 00 21 00 00 00 +``` + +This string is represented in memory as follows: +- `C0 12 34 56 78 9A BC DE`: This is the .net type handle, which is a pointer to the metadata structure that describes the string type. This is an example, the actual handle will be different in every application, and even in every run of the same application. +- `0D 00 00 00`: This is the length prefix. In .net, length prefixes are 4 bytes long, and indicate the number of characters. This one reads as 13 (0x0D) characters long. +- `48 00 65 00 6C 00 6C 00 6F 00 2C 00 20 00 57 00 6F 00 72 00 6C 00 64 00 21 00`: These are the UTF-16 encoded characters of the string "Hello, World!", with each character taking up two bytes. +- `00 00`: This is the null terminator, which indicates the end of the string. Because we are using UTF-16, the null terminator is represented as two bytes and not just one. We technically don't need it, because we have the length prefix, but .net still has this terminator for compatibility with other systems that expect it. + +## Reading strings with MindControl + +There are several options to read strings in MindControl, depending on your needs and preferences. + +### Using ReadStringPointer + +The `ReadStringPointer` method of the `ProcessMemory` class is the most versatile way to read strings from memory. It takes an address or a pointer path to the pointer to the string you want to read, along with a `StringSettings` object that defines how the string is represented in memory. This method will read the pointer, then read the string from the address it points to, and return the string value. + +Let's take an example to read the string from the previous example (a standard .net string): + +```csharp +var stringSettings = new StringSettings( + encoding: System.Text.Encoding.UTF8, + isNullTerminated: true, + lengthPrefix: new LengthPrefix(4, StringLengthUnit.Characters), // 4 byte length, counting characters (not bytes) + typePrefix: new byte[] { 0xC0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE }); + +string? result = processMemory.ReadStringPointer("GameAssembly.dll+12345678", stringSettings).ValueOrDefault(); +``` + +In this example, we specify the encoding as UTF-8, the string is null-terminated, and it has a 4-byte length prefix that counts characters. We also provide the type handle as a byte array. The method will read the pointer at the specified address, then read the string from the address it points to, and return the string value. + +> [!NOTE] +> As stated before, this method takes the address of a pointer to the string, **not** the address of the start of the string itself. + +### Using ReadRawString + +An alternative to `ReadStringPointer` is the `ReadRawString` method, which reads a string directly from the address or pointer path to its first character byte, without needing to provide a pointer to the string. This method is useful when you don't have a pointer to the string, or if you prefer to read the string directly from its starting address. + +Instead of a `StringSettings` object, this method takes an `Encoding` parameter to specify how the string is encoded, a max length that indicates when to stop, and a boolean to indicate if the string is null-terminated. + +A matching example for the previous string would look like this: + +```csharp +// Note the 'C' offset at the end of the pointer path to skip the type handle (8 bytes) and the length prefix (4 bytes) +string? result = processMemory.ReadRawString("GameAssembly.dll+12345678,C", + Encoding.UTF8, + maxLength: 100, + isNullTerminated: true).ValueOrDefault(); +``` + +> [!NOTE] +> ReadRawString stops reading when it reaches the specified maximum length, or when it encounters a null terminator, whichever comes first. If you specify no null terminator, it will always read up to the maximum length. + +### Determining the string settings automatically + +If you don't know the string settings, you can use observation and trial and error to determine them. However, `MindControl` provides a method that helps you pinpoint the string settings automatically. The `FindStringSettings` method takes an address or pointer path to a pointer to a string, and the expected string value, and returns a `StringSettings` object that matches the string representation in memory. + +It may not be ideal for all cases, especially because it requires you to know a string value that you expect to find in memory, but it can be useful especially when dealing with strings with a type handle that changes every time the application is run. + +```csharp +// Determine the string settings automatically thanks to a known "Hello, World!" string somewhere in memory +var expectedString = "Hello, World!"; +StringSettings stringSettings = processMemory.FindStringSettings("GameAssembly.dll+12345678", expectedString) + .ValueOrDefault(); + +// Now you can use the string settings to read another string that you don't know +string? anotherString = processMemory.ReadStringPointer("GameAssembly.dll+ABCDEF012", stringSettings).ValueOrDefault(); +``` + +## Writing strings with MindControl + +By design, MindControl does not have a method that directly writes strings to memory. This is because it would make it too easy for users to make mistakes and either corrupt memory, or bloat memory with hundreds of thousands of strings that are never erased. + +This is because, unless you are certain that your new string is smaller than the original string you want to replace, you cannot simply overwrite the bytes of the original string with the bytes of your new string. If the new string is longer, you would end up writing past the end of the original string, which could corrupt memory and lead to crashes or unexpected behavior. + +Instead, you can write strings to memory by chaining multiple operations: +- (Optional) Use `FindStringSettings` on a known string to determine the string settings for the string you want to write. +- Use `StoreString` to store the value of the string somewhere in an allocated space in memory. +- Use `Write` to overwrite the pointer to the string with the address of your newly stored string. + +Here is a concrete example of exactly that: + +```csharp +// Determine the string settings automatically thanks to a known "Hello, World!" string +var expectedString = "Hello, World!"; +StringSettings stringSettings = processMemory.FindStringSettings("GameAssembly.dll+12345678", expectedString) + .ValueOrDefault(); + +// Store the new string in memory using the determined string settings +MemoryReservation reservation = processMemory.StoreString("New String Value", stringSettings).ValueOrDefault(); + +// Write the pointer to the new string in the target process memory +processMemory.Write("GameAssembly.dll+12345678", reservation.Address).ThrowOnFailure(); +``` + +> [!NOTE] +> Make sure to dispose of the `MemoryReservation` object when you no longer need it, to avoid memory leaks in the target process. If you constantly write new strings in this way without disposing of the old reservations, you will quickly end up consuming gigabytes of memory in the target process, which will lead to performance issues and crashes. diff --git a/docs/guide/mcfeatures/writing.md b/docs/guide/mcfeatures/writing.md new file mode 100644 index 0000000..fc5d2cc --- /dev/null +++ b/docs/guide/mcfeatures/writing.md @@ -0,0 +1,85 @@ +# Writing to memory + +This section will guide you through writing to memory in a target process using `MindControl`. + +> [!NOTE] +> Writing string values is a more complex topic and is covered in the [Manipulating string](./strings.md) section of this guide. + +## Writing numeric values + +To write numeric values to the target process, you can use the `Write` method of the `ProcessMemory` class. This method takes either an address or a pointer path, and the value to write, and returns a result indicating whether the write operation was successful. + +Here are some examples of how to write numeric values to the target process: + +### Writing an integer value to a pointer path + +```csharp +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +bool success = processMemory.Write("GameAssembly.dll+12345678", 100).IsSuccess; +``` + +### Writing a float value to an address + +```csharp +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +bool success = processMemory.Write(0x1A2B3C4, 3.14f).IsSuccess; +``` + +## Writing arbitrary structures + +Just like when reading, you can define a struct that represents the data structure you want to write, and then use the `Write` method to write the entire structure at once. This is more efficient than writing each field individually. + +Here are some examples: + +### Writing a custom unmarked structure + +```csharp +// Define a structure that represents the data you want to write +// Fields must be in the same order as they are in memory +struct PlayerStats +{ + public int Health; + public float Speed; + public long Score; +} +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +bool success = processMemory.Write("GameAssembly.dll+12345678", new PlayerStats { Health = 100, Speed = 5.0f, Score = 1000 }).IsSuccess; +``` + +### Writing a custom structure with field offsets + +```csharp +using System.Runtime.InteropServices; +// See the previous section for more details on how to define structures with field offsets. +[StructLayout(LayoutKind.Explicit)] +struct PlayerStats +{ + [FieldOffset(0x00)] public int Health; + [FieldOffset(0x0A)] public float Speed; + [FieldOffset(0xF0)] public long Score; +} +var processMemory = MindControl.ProcessMemory.OpenProcessByName("MyTargetProcess").Value; +bool success = processMemory.Write("GameAssembly.dll+12345678", new PlayerStats { Health = 100, Speed = 5.0f, Score = 1000 }).IsSuccess; +``` + +## Writing raw bytes + +If you need to write raw bytes to a specific address or pointer path, you can use the `WriteBytes` method. This method takes a byte array and writes it to the specified location in the target process. + +```csharp +byte[] dataToWrite = new byte[] { 0x90, 0x90, 0x90 }; +bool success = processMemory.WriteBytes("GameAssembly.dll+12345678", dataToWrite).IsSuccess; +``` + +## Memory protection strategies + +All writing methods in `MindControl` have an additional parameter that allows you to specify how to handle memory protection. There are three options: +- `MemoryProtectionStrategy.Ignore`: No protection removal. The write will fail if the memory is protected, but if you know it isn't, this is the most performant option. +- `MemoryProtectionStrategy.Remove`: Removes the memory protection before writing. Memory protection will not be restored after the write operation, which may cause issues in some cases, but may be more performant than restoring it. +- `MemoryProtectionStrategy.RemoveAndRestore`: Temporarily removes the memory protection to allow writing, then restores it. This is the safest, but least performant option. + +The default strategy is `MemoryProtectionStrategy.RemoveAndRestore`, for best compatibility. You can change this by passing a different strategy to the `Write` method. + +```csharp +bool success = processMemory.Write("GameAssembly.dll+12345678", 100, MemoryProtectionStrategy.Ignore).IsSuccess; +``` \ No newline at end of file diff --git a/docs/guide/project-setup/blazor-setup.md b/docs/guide/project-setup/blazor-setup.md new file mode 100644 index 0000000..6a3c64c --- /dev/null +++ b/docs/guide/project-setup/blazor-setup.md @@ -0,0 +1,171 @@ +# Setting up MindControl in a Blazor (web) application + +This section will guide you through setting up a new Blazor application project that uses the `MindControl` library to interact with the memory of a target process. We will use the Slime Rancher demo (see the [requirements page](../guide-requirements.md) for more info) as our target process, but feel free to use any other target. + +## Define the scope of your project + +Before we start, let's define what our project should do. In our example, we are going to set up a very simple page, with an editable field for the player's current coin count, with real-time synchronization. + +## Create a new Blazor project + +First, we need to create a new Blazor application project. There are two types of Blazor applications: Blazor Server and Blazor WebAssembly. Explaining the differences is beyond the scope of this guide, but Blazor server is perfect for our purposes, because it has a built-in mechanism to constantly stream updates from the server app to the browser. + +Open a command line interface and navigate to the directory where you want to create the project. Then, run the following command to create your project from scratch: + +```bash +dotnet new blazor -n MyMindControlWebProject --empty +cd MyMindControlWebProject +``` + +> [!NOTE] +> We are using the `dotnet` command-line tool here, but feel free to use your IDE of choice if you prefer. + +## Add the MindControl library to your project + +Next, we need to add the `MindControl` library to our project. Run the following command to add the library to your project: + +```bash +dotnet add package MindControl +``` + +This will reference the latest stable version of the `MindControl` library in your project using NuGet. + +## Develop your memory hacking features in a new class + +It's a good idea to separate your memory hacking features from the rest of your application. This way, you can keep your code organized and easy to maintain. Let's create a new class called `SlimeRancherDemo` in a new file called `SlimeRancherDemo.cs`. This class will be our entry point for interacting with the target process, and will define methods for every memory hack our program is able to do. + +```csharp +using MindControl; + +namespace MyMindControlWebProject; + +public class SlimeRancherDemo +{ + // We have determined the memory path to the coin count value in the target process using Cheat Engine. See the tutorials in this guide to learn how to do this. + private readonly PointerPath _coinCountPath = "UnityPlayer.dll+0168EEA0,8,100,28,20,80"; + + // Use the name of your target process here. + private readonly ProcessTracker _processTracker = new("SlimeRancher"); + + public int? GetCoinCount() + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return null; // The target process is not running + + // Try to read the coin count value from the target process + var coinCountResult = process.Read(_coinCountPath); + if (!coinCountResult.IsSuccess) + { + // The coin count value could not be read (maybe we are in the main menu) + // Check coinCountResult.Failure for more information + return null; + } + + return coinCountResult.Value; + } + + public bool SetCoinCount(int newCoinCount) + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return false; // The target process is not running + + // Try to write the new coin count value to the target process, and return true if successful + var writeResult = process.Write(_coinCountPath, newCoinCount); + return writeResult.IsSuccess; + } +} +``` + +> [!NOTE] +> In the next chapter of this guide, we will explain how to use the classes and methods provided by `MindControl`. For now, we are just focusing on getting a basic project set up. + +As you can see, in our case, we defined two methods: `GetCoinCount` and `SetCoinCount`. + +## Register the service in the Program.cs file + +Now that we have our memory hacking features defined, we need to register the `SlimeRancherDemo` service in the `Program.cs` file so that we can use it in our Blazor components. + +Open the `Program.cs` file and add the following code right before the `builder.Build()` line: + +```csharp +// Register the SlimeRancherDemoService as a singleton, so it can be used by multiple pages and components. +builder.Services.AddSingleton(); +``` + +## Implementing the page + +Now that we have our memory hacking features defined, let's work on the page itself. In Blazor, pages are defined in `.razor` files. The template already has a home page in `Components/Pages/Home.razor`, so let's edit that file to make our page. + +```html +@page "/" + +@* Specifying the render mode as InteractiveServer allows us to use server-side rendering with interactive components, +which is crucial in our case because the client (the browser) cannot directly access the Slime Rancher game state. *@ +@rendermode InteractiveServer + +@* We can access the SlimeRancherDemo service using injection because we have registered it in the Program.cs file *@ +@inject SlimeRancherDemo _slimeRancherDemo + +Home + +
+ + +
+ +@code { + public int _coinCount; + + private readonly System.Timers.Timer _stateUpdateTimer = new System.Timers.Timer(TimeSpan.FromMilliseconds(100)); + protected override void OnInitialized() + { + _stateUpdateTimer.Elapsed += OnStateUpdateTimerElapsed; + _stateUpdateTimer.Start(); + } + + /// Called when the timer ticks, every 100ms. + private void OnStateUpdateTimerElapsed(object? sender, System.Timers.ElapsedEventArgs e) + { + _coinCount = _slimeRancherDemo.GetCoinCount() ?? 0; + + // Update the UI with StateHasChanged. We need to do that manually when new data comes from events or timers. + InvokeAsync(StateHasChanged); + } + + /// Called when the coin input value is modified manually. + private void OnCoinsModified(ChangeEventArgs e) + { + // Make sure the input value is a valid number + if (!int.TryParse(e.Value?.ToString(), out int coins)) + return; + + _slimeRancherDemo.SetCoinCount(coins); + } +} +``` + +This code defines a simple page with an input field for the coin count. The input field is bound to the `_coinCount` variable, which is updated every 100 milliseconds by the timer we set up in the `OnInitialized` method. + +When the user modifies the input field, the `OnCoinsModified` method is called (through the `oninput` event), which updates the coin count in the game. + +At this stage, the program should be complete and functional. You can run it using `dotnet run` from the project directory and try it out. + +> [!NOTE] +> You may need to manually access the page in your browser by navigating to the link output by the application after running it, which should look like `http://localhost:5273`. + +![Our example Blazor application](../../images/ex-blazor-app-simple.png) + +> Try spending or earning coins in the game, and see how the coin count updates in real-time in your application. You can also change the coin count value in the input field, and it will be reflected in the game. + +## Going further + +Now, to learn how to use classes and methods provided by the `MindControl` library, check out the [next chapter](../mcfeatures/attaching.md) of this guide. + +Alternatively, if you want to see a more advanced example of a Blazor application using the `MindControl` library, check out the [MindControl Blazor app sample](https://github.com/Doublevil/mind-control/tree/main/samples/MindControl.Samples.SrDemoBlazorApp) in the MindControl repository. Here is a breakdown of the features in the sample: +- Fully automated process tracking (process detection with no need for user input, supports the game closing and restarting). +- Editable sliders and input fields, synchronized in real-time with the game, for the player's health, stamina, and coin count values. +- A button to toggle infinite stamina. + +![An example of the MindControl Blazor app sample](../../images/ex-blazor-app-full.png) diff --git a/docs/guide/project-setup/console-setup.md b/docs/guide/project-setup/console-setup.md new file mode 100644 index 0000000..a5e275d --- /dev/null +++ b/docs/guide/project-setup/console-setup.md @@ -0,0 +1,130 @@ +# Setting up MindControl in a console application + +This section will guide you through setting up a new console application project that uses the `MindControl` library to interact with the memory of a target process. We will use the Slime Rancher demo (see the [requirements page](../guide-requirements.md) for more info) as our target process, but feel free to use any other target. + +## Define the scope of your project + +Before we start, let's define what our project should do. In our example, we are going to set up a very simple program that reads the player's current coin count in our target game, and then after pressing a key, we will increment the coin count by 1000. + +## Create a new console application project + +First, we need to create a new console application project. Open a command line interface and navigate to the directory where you want to create the project. Then, run the following command to create your project from scratch: + +```bash +dotnet new console -n MyMindControlProject +cd MyMindControlProject +``` + +> [!NOTE] +> We are using the `dotnet` command-line tool here, but feel free to use your IDE of choice if you prefer. + +## Add the MindControl library to your project + +Next, we need to add the `MindControl` library to our project. Run the following command to add the library to your project: + +```bash +dotnet add package MindControl +``` + +This will reference the latest stable version of the `MindControl` library in your project using NuGet. + +## Develop your memory hacking features in a new class + +It's a good idea to separate your memory hacking features from the rest of your application. This way, you can keep your code organized and easy to maintain. Let's create a new class called `SlimeRancherDemo` in a new file called `SlimeRancherDemo.cs`. This class will be our entry point for interacting with the target process, and will define methods for every memory hack our program is able to do. + +```csharp +using MindControl; + +namespace MyMindControlProject; + +public class SlimeRancherDemo +{ + // We have determined the memory path to the coin count value in the target process using Cheat Engine. See the tutorials in this guide to learn how to do this. + private readonly PointerPath _coinCountPath = "UnityPlayer.dll+0168EEA0,8,100,28,20,80"; + + // Use the name of your target process here. + private readonly ProcessTracker _processTracker = new("SlimeRancher"); + + public int? GetCoinCount() + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return null; // The target process is not running + + // Try to read the coin count value from the target process + var coinCountResult = process.Read(_coinCountPath); + if (!coinCountResult.IsSuccess) + { + // The coin count value could not be read (maybe we are in the main menu) + // Check coinCountResult.Failure for more information + return null; + } + + return coinCountResult.Value; + } + + public bool SetCoinCount(int newCoinCount) + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return false; // The target process is not running + + // Try to write the new coin count value to the target process, and return true if successful + var writeResult = process.Write(_coinCountPath, newCoinCount); + return writeResult.IsSuccess; + } +} +``` + +> [!NOTE] +> In the next chapter of this guide, we will explain how to use the classes and methods provided by `MindControl`. For now, we are just focusing on getting a basic project set up. + +As you can see, in our case, we defined two methods: `GetCoinCount` and `SetCoinCount`. + +## Implement the main program logic + +Now that we have our memory hacking features defined, let's implement the main program logic in the `Program.cs` file. This is where we will interact with the `SlimeRancherDemo` class we just created. + +```csharp +using MyMindControlProject; + +var slimeRancher = new SlimeRancherDemo(); +Console.WriteLine("Press any key to read the current coin count."); +Console.ReadKey(true); +int? coinCount = slimeRancher.GetCoinCount(); +if (coinCount == null) +{ + Console.WriteLine("Could not read the coin count. Make sure the game is running, and a new game is started."); + return; +} + +Console.WriteLine($"Current coin count: {coinCount}"); +Console.WriteLine("Press any key to add 1000 coins."); +Console.ReadKey(true); + +bool isWriteSuccessful = slimeRancher.SetCoinCount(coinCount.Value + 1000); +if (!isWriteSuccessful) +{ + Console.WriteLine("Could not write the new coin count. Make sure the game is running, and a new game is started."); + return; +} +Console.WriteLine("Coin count updated successfully."); +``` + +This code will read the current coin count from the target process, display it to the user, and then increment the coin count by 1000. Feel free to modify this code to fit your own project. + +As of this stage, the program should be complete and functional. You can run it using `dotnet run` from the project directory and try it out. + +![Our example console application](../../images/ex-console-app-simple.png) + +## Going further + +Now, to learn how to use classes and methods provided by the `MindControl` library, check out the [next chapter](../mcfeatures/attaching.md) of this guide. + +Alternatively, if you want to see a more advanced example of a console application using the `MindControl` library, check out the [MindControl console app sample](https://github.com/Doublevil/mind-control/tree/main/samples/MindControl.Samples.SrDemoConsoleApp) in the MindControl repository. Here is a breakdown of the features in the sample: +- Fully automatic process tracking (process detection with no need for user input, supports the game closing and restarting, etc). +- A live view of the player's current coin count, health, and stamina values, automatically updated multiple times per second. +- Simple user input to change the player's coin count, health, and stamina values. +- A command to toggle infinite stamina. + +![An example of the MindControl console app sample](../../images/ex-console-app-full.png) diff --git a/docs/guide/project-setup/creating-mc-project.md b/docs/guide/project-setup/creating-mc-project.md new file mode 100644 index 0000000..614a8d2 --- /dev/null +++ b/docs/guide/project-setup/creating-mc-project.md @@ -0,0 +1,43 @@ +# Creating a MindControl project + +In this chapter, we are going to implement a simple example program that uses the `MindControl` library to interact with the memory of our example target process, the Slime Rancher demo. + +## Requirements + +Before we start, make sure the .net SDK is installed on your system. You can download it from the [official Microsoft .net website](https://dotnet.microsoft.com/en-us/download/dotnet). Pick the latest LTS (Long-Term Support) version. Alternatively, you can use Visual Studio or your .net IDE of choice, which should come with the .net SDK. + +To make sure everyone can follow equally, this guide will not assume any particular IDE and use the `dotnet` command-line tool included in the SDK, instead of IDE-specific instructions. + +This guide assumes you have a basic understanding of C# programming. If you are new to C#, you can learn in the [official Microsoft C# portal](https://docs.microsoft.com/en-us/dotnet/csharp/). + +## What do you want to build? + +This guide will show an implementation for multiple different scenarios. It's not necessary to read through them all, just pick the one you are most interested in building. + +### Console application + +Console apps are the simplest to develop, but also the most limited choice in terms of interface. + +![An example console application running MindControl code](../../images/ex-console-app-full.png) + +**[Follow the Console application implementation guide](./console-setup.md)** + +### WPF (desktop) application + +WPF apps provide a rich desktop interface that can be customized to your liking. + +![An example WPF application running MindControl code](../../images/ex-wpf-app-full.png) + +**[Follow the WPF application implementation guide](./wpf-setup.md)** + +### Blazor (web) application + +Blazor server apps are web applications. They can be accessed through a web browser, and provide a rich interface that can also be reached from other devices, as long as the server app runs on the same machine as the target process. + +![An example Blazor application running MindControl code](../../images/ex-blazor-app-full.png) + +**[Follow the Blazor application implementation guide](./blazor-setup.md)** + +### Want to build something that isn't listed here? + +If you prefer to build a WinForms app, an ASP .net Core web app, a Windows service, or any other type of .net application, you can still follow the general guidelines in [the Console application implementation guide](./console-setup.md). You should be able to adapt the code to your preferred type of application relatively easily. diff --git a/docs/guide/project-setup/wpf-setup.md b/docs/guide/project-setup/wpf-setup.md new file mode 100644 index 0000000..5afcabe --- /dev/null +++ b/docs/guide/project-setup/wpf-setup.md @@ -0,0 +1,206 @@ +# Setting up MindControl in a WPF application + +This section will guide you through setting up a new WPF application project that uses the `MindControl` library to interact with the memory of a target process. We will use the Slime Rancher demo (see the [requirements page](../guide-requirements.md) for more info) as our target process, but feel free to use any other target. + +## Define the scope of your project + +Before we start, let's define what our project should do. In our example, we are going to set up a very simple window, with an editable field for the player's current coin count, with real-time synchronization. + +## Create a new WPF application project + +First, we need to create a new console application project. Open a command line interface and navigate to the directory where you want to create the project. Then, run the following command to create your project from scratch: + +```bash +dotnet new wpf -n MyMindControlWpfProject +cd MyMindControlWpfProject +``` + +> [!NOTE] +> We are using the `dotnet` command-line tool here, but feel free to use your IDE of choice if you prefer. + +## Add the MindControl library to your project + +Next, we need to add the `MindControl` library to our project. Run the following command to add the library to your project: + +```bash +dotnet add package MindControl +``` + +This will reference the latest stable version of the `MindControl` library in your project using NuGet. + +## Develop your memory hacking features in a new class + +It's a good idea to separate your memory hacking features from the rest of your application. This way, you can keep your code organized and easy to maintain. Let's create a new class called `SlimeRancherDemo` in a new file called `SlimeRancherDemo.cs`. This class will be our entry point for interacting with the target process, and will define methods for every memory hack our program is able to do. + +```csharp +using MindControl; + +namespace MyMindControlWpfProject; + +public class SlimeRancherDemo +{ + // We have determined the memory path to the coin count value in the target process using Cheat Engine. See the tutorials in this guide to learn how to do this. + private readonly PointerPath _coinCountPath = "UnityPlayer.dll+0168EEA0,8,100,28,20,80"; + + // Use the name of your target process here. + private readonly ProcessTracker _processTracker = new("SlimeRancher"); + + public int? GetCoinCount() + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return null; // The target process is not running + + // Try to read the coin count value from the target process + var coinCountResult = process.Read(_coinCountPath); + if (!coinCountResult.IsSuccess) + { + // The coin count value could not be read (maybe we are in the main menu) + // Check coinCountResult.Failure for more information + return null; + } + + return coinCountResult.Value; + } + + public bool SetCoinCount(int newCoinCount) + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return false; // The target process is not running + + // Try to write the new coin count value to the target process, and return true if successful + var writeResult = process.Write(_coinCountPath, newCoinCount); + return writeResult.IsSuccess; + } +} +``` + +> [!NOTE] +> In the next chapter of this guide, we will explain how to use the classes and methods provided by `MindControl`. For now, we are just focusing on getting a basic project set up. + +As you can see, in our case, we defined two methods: `GetCoinCount` and `SetCoinCount`. + +## Implementing the application logic + +Now that we have our memory hacking features defined, let's work on the application logic. We are going to use the MVVM (Model-View-ViewModel) pattern to separate our application logic from the user interface. This is a very common pattern in WPF applications, that helps keep the code organized and maintainable. + +So let's make a ViewModel that will hold our coin count and update it in real-time. Create a new file called `MainViewModel.cs` in the project directory, and add the following code: + +```csharp +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace MyMindControlWpfProject; + +public class MainViewModel : INotifyPropertyChanged +{ + private readonly SlimeRancherDemo _slimeRancherDemo = new(); + + private int _coinCount; + public int CoinCount + { + get => _coinCount; + set => SetProperty(ref _coinCount, value); + } + + // We set up a timer to update the displayed game values every 100 milliseconds. + private readonly System.Timers.Timer _stateUpdateTimer = new(TimeSpan.FromMilliseconds(100)); + + public MainViewModel() + { + _stateUpdateTimer.Elapsed += OnStateUpdateTimerTick; + _stateUpdateTimer.Start(); + } + + /// Called from the view when the user inputs a new value for the coin count. + public void OnCoinCountInput() => _slimeRancherDemo.SetCoinCount(CoinCount); + + /// Called every time the refresh timer ticks. + private void OnStateUpdateTimerTick(object? sender, EventArgs e) + { + CoinCount = _slimeRancherDemo.GetCoinCount() ?? 0; + } + + // The code below is boilerplate code for implementing INotifyPropertyChanged. + // Feel free to move that to a base class if you want to reuse it in other view models. + public event PropertyChangedEventHandler? PropertyChanged; + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null!) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetProperty(ref T field, T value, [CallerMemberName] string propertyName = null!) + { + if (Equals(field, value)) + return false; + + field = value; + OnPropertyChanged(propertyName); + return true; + } +} +``` + +This is the code that will handle our main window logic. It will update the coin count every 100 milliseconds, and it will also allow the user to input a new coin count value that will be reflected instantly in-game. + +## Setting up the user interface + +Now, open the `MainWindow.xaml` file and set up the user interface. Here is a simple example of how you can do that: + +```xml + + + + + + +``` + +And finally, edit the `MainWindow.xaml.cs` file to set the DataContext to our `MainViewModel` and define the `OnCoinCountInput` method: + +```csharp +using System.Windows; +using System.Windows.Controls; + +namespace MyMindControlWpfProject; + +public partial class MainWindow : Window +{ + private readonly MainViewModel _viewModel; + + public MainWindow() + { + InitializeComponent(); + _viewModel = new MainViewModel(); + DataContext = _viewModel; + } + + private void OnCoinCountInput(object sender, TextChangedEventArgs e) + { + _viewModel.OnCoinCountInput(); + } +} +``` + +As of this stage, the program should be complete and functional. You can run it using `dotnet run` from the project directory and try it out. + +![Our example WPF application](../../images/ex-wpf-app-simple.png) + +> Try spending or earning coins in the game, and see how the coin count updates in real-time in your application. You can also change the coin count value in the input field, and it will be reflected in the game. + +## Going further + +Now, to learn how to use classes and methods provided by the `MindControl` library, check out the [next chapter](../mcfeatures/attaching.md) of this guide. + +Alternatively, if you want to see a more advanced example of a WPF application using the `MindControl` library, check out the [MindControl WPF app sample](https://github.com/Doublevil/mind-control/tree/main/samples/MindControl.Samples.SrDemoWpfApp) in the MindControl repository. Here is a breakdown of the features in the sample: +- Fully automated process tracking (process detection with no need for user input, supports the game closing and restarting, tracks when in the main menu). +- Editable sliders and input fields, synchronized in real-time with the game, for the player's health, stamina, and coin count values. +- A button to toggle infinite stamina. + +![An example of the MindControl WPF app sample](../../images/ex-wpf-app-full.png) diff --git a/docs/guide/toc.yml b/docs/guide/toc.yml new file mode 100644 index 0000000..1ea11e8 --- /dev/null +++ b/docs/guide/toc.yml @@ -0,0 +1,54 @@ +- name: Introduction + href: introduction.md +- name: Basic hacking concepts + items: + - name: Basic memory concepts + href: hacking-basics/basic-memory-concepts.md + - name: Finding values with Cheat Engine + href: hacking-basics/finding-values.md + - name: Unstable addresses + href: hacking-basics/unstable-address.md + - name: Stable addresses + href: hacking-basics/stable-address.md + - name: Code manipulation + href: hacking-basics/code-manipulation.md +- name: Setting up a MindControl project + items: + - name: Creating a MindControl project + href: project-setup/creating-mc-project.md + - name: Console app setup + href: project-setup/console-setup.md + - name: WPF (desktop) app setup + href: project-setup/wpf-setup.md + - name: Blazor (web) app setup + href: project-setup/blazor-setup.md +- name: MindControl features + items: + - name: Attaching to a process + href: mcfeatures/attaching.md + - name: Reading memory + href: mcfeatures/reading.md + - name: Writing memory + href: mcfeatures/writing.md + - name: Pointer paths + href: mcfeatures/pointer-paths.md + - name: Results & error handling + href: mcfeatures/results.md + - name: Allocating & storing data + href: mcfeatures/allocations.md + - name: Manipulating strings + href: mcfeatures/strings.md + - name: Searching memory + href: mcfeatures/searching.md + - name: Freezing values + href: mcfeatures/freezing.md + - name: Monitoring value changes + href: mcfeatures/monitoring.md + - name: Injecting DLLs + href: mcfeatures/dll-injection.md + - name: Code manipulation + href: mcfeatures/code-manipulation.md + - name: Calling functions + href: mcfeatures/functions.md + - name: Streaming memory + href: mcfeatures/streams.md \ No newline at end of file diff --git a/docs/images/ce-add-pointer.png b/docs/images/ce-add-pointer.png new file mode 100644 index 0000000..8b9deff Binary files /dev/null and b/docs/images/ce-add-pointer.png differ diff --git a/docs/images/ce-assembly-example.png b/docs/images/ce-assembly-example.png new file mode 100644 index 0000000..2d22bf4 Binary files /dev/null and b/docs/images/ce-assembly-example.png differ diff --git a/docs/images/ce-attach.png b/docs/images/ce-attach.png new file mode 100644 index 0000000..c60b011 Binary files /dev/null and b/docs/images/ce-attach.png differ diff --git a/docs/images/ce-auto-assemble.png b/docs/images/ce-auto-assemble.png new file mode 100644 index 0000000..8a81130 Binary files /dev/null and b/docs/images/ce-auto-assemble.png differ diff --git a/docs/images/ce-code-injection.png b/docs/images/ce-code-injection.png new file mode 100644 index 0000000..a844efb Binary files /dev/null and b/docs/images/ce-code-injection.png differ diff --git a/docs/images/ce-coinscan-firstresults.png b/docs/images/ce-coinscan-firstresults.png new file mode 100644 index 0000000..1d0fb87 Binary files /dev/null and b/docs/images/ce-coinscan-firstresults.png differ diff --git a/docs/images/ce-coinscan-results-narrowed.png b/docs/images/ce-coinscan-results-narrowed.png new file mode 100644 index 0000000..53a7a11 Binary files /dev/null and b/docs/images/ce-coinscan-results-narrowed.png differ diff --git a/docs/images/ce-coinscan.png b/docs/images/ce-coinscan.png new file mode 100644 index 0000000..6240f6e Binary files /dev/null and b/docs/images/ce-coinscan.png differ diff --git a/docs/images/ce-find-writes.png b/docs/images/ce-find-writes.png new file mode 100644 index 0000000..d85ed02 Binary files /dev/null and b/docs/images/ce-find-writes.png differ diff --git a/docs/images/ce-memoryviewer-small.png b/docs/images/ce-memoryviewer-small.png new file mode 100644 index 0000000..50e9109 Binary files /dev/null and b/docs/images/ce-memoryviewer-small.png differ diff --git a/docs/images/ce-memoryviewer-stamina-instructions.png b/docs/images/ce-memoryviewer-stamina-instructions.png new file mode 100644 index 0000000..c09e919 Binary files /dev/null and b/docs/images/ce-memoryviewer-stamina-instructions.png differ diff --git a/docs/images/ce-memoryviewer.png b/docs/images/ce-memoryviewer.png new file mode 100644 index 0000000..defee27 Binary files /dev/null and b/docs/images/ce-memoryviewer.png differ diff --git a/docs/images/ce-pointer-example.png b/docs/images/ce-pointer-example.png new file mode 100644 index 0000000..27c4a85 Binary files /dev/null and b/docs/images/ce-pointer-example.png differ diff --git a/docs/images/ce-pointer-scan-initial-results.png b/docs/images/ce-pointer-scan-initial-results.png new file mode 100644 index 0000000..9406560 Binary files /dev/null and b/docs/images/ce-pointer-scan-initial-results.png differ diff --git a/docs/images/ce-pointer-scan-open.png b/docs/images/ce-pointer-scan-open.png new file mode 100644 index 0000000..d8ca302 Binary files /dev/null and b/docs/images/ce-pointer-scan-open.png differ diff --git a/docs/images/ce-pointer-scan-options.png b/docs/images/ce-pointer-scan-options.png new file mode 100644 index 0000000..a7085eb Binary files /dev/null and b/docs/images/ce-pointer-scan-options.png differ diff --git a/docs/images/ce-pointer-scan-rescan.png b/docs/images/ce-pointer-scan-rescan.png new file mode 100644 index 0000000..88e1f1c Binary files /dev/null and b/docs/images/ce-pointer-scan-rescan.png differ diff --git a/docs/images/ce-pointer-scan-results-2.png b/docs/images/ce-pointer-scan-results-2.png new file mode 100644 index 0000000..fad4045 Binary files /dev/null and b/docs/images/ce-pointer-scan-results-2.png differ diff --git a/docs/images/ce-pointer-scan-results-final.png b/docs/images/ce-pointer-scan-results-final.png new file mode 100644 index 0000000..b039374 Binary files /dev/null and b/docs/images/ce-pointer-scan-results-final.png differ diff --git a/docs/images/ce-scan-for-pointer.png b/docs/images/ce-scan-for-pointer.png new file mode 100644 index 0000000..2b594b0 Binary files /dev/null and b/docs/images/ce-scan-for-pointer.png differ diff --git a/docs/images/ce-scan-stable-pointer.png b/docs/images/ce-scan-stable-pointer.png new file mode 100644 index 0000000..03e71ea Binary files /dev/null and b/docs/images/ce-scan-stable-pointer.png differ diff --git a/docs/images/ce-stamina-find-writes.png b/docs/images/ce-stamina-find-writes.png new file mode 100644 index 0000000..c261954 Binary files /dev/null and b/docs/images/ce-stamina-find-writes.png differ diff --git a/docs/images/ce-stamina-instruction-nop.png b/docs/images/ce-stamina-instruction-nop.png new file mode 100644 index 0000000..5bf66c6 Binary files /dev/null and b/docs/images/ce-stamina-instruction-nop.png differ diff --git a/docs/images/ce-write-trace.png b/docs/images/ce-write-trace.png new file mode 100644 index 0000000..6f84525 Binary files /dev/null and b/docs/images/ce-write-trace.png differ diff --git a/docs/images/ex-blazor-app-full.png b/docs/images/ex-blazor-app-full.png new file mode 100644 index 0000000..f0b202c Binary files /dev/null and b/docs/images/ex-blazor-app-full.png differ diff --git a/docs/images/ex-blazor-app-simple.png b/docs/images/ex-blazor-app-simple.png new file mode 100644 index 0000000..50851cb Binary files /dev/null and b/docs/images/ex-blazor-app-simple.png differ diff --git a/docs/images/ex-console-app-full.png b/docs/images/ex-console-app-full.png new file mode 100644 index 0000000..87e6aa8 Binary files /dev/null and b/docs/images/ex-console-app-full.png differ diff --git a/docs/images/ex-console-app-simple.png b/docs/images/ex-console-app-simple.png new file mode 100644 index 0000000..f2b51ab Binary files /dev/null and b/docs/images/ex-console-app-simple.png differ diff --git a/docs/images/ex-console-app.png b/docs/images/ex-console-app.png new file mode 100644 index 0000000..43ac27e Binary files /dev/null and b/docs/images/ex-console-app.png differ diff --git a/docs/images/ex-wpf-app-full.png b/docs/images/ex-wpf-app-full.png new file mode 100644 index 0000000..a536daa Binary files /dev/null and b/docs/images/ex-wpf-app-full.png differ diff --git a/docs/images/ex-wpf-app-simple.png b/docs/images/ex-wpf-app-simple.png new file mode 100644 index 0000000..89ba6f9 Binary files /dev/null and b/docs/images/ex-wpf-app-simple.png differ diff --git a/docs/images/il-datatypes.gif b/docs/images/il-datatypes.gif new file mode 100644 index 0000000..34b319e Binary files /dev/null and b/docs/images/il-datatypes.gif differ diff --git a/docs/images/il-pointerchain.png b/docs/images/il-pointerchain.png new file mode 100644 index 0000000..6b1c630 Binary files /dev/null and b/docs/images/il-pointerchain.png differ diff --git a/docs/images/il-pointers-firstscenario.gif b/docs/images/il-pointers-firstscenario.gif new file mode 100644 index 0000000..67e14bd Binary files /dev/null and b/docs/images/il-pointers-firstscenario.gif differ diff --git a/docs/images/il-pointers-secondscenario.gif b/docs/images/il-pointers-secondscenario.gif new file mode 100644 index 0000000..7a7fff6 Binary files /dev/null and b/docs/images/il-pointers-secondscenario.gif differ diff --git a/docs/images/il-structures.png b/docs/images/il-structures.png new file mode 100644 index 0000000..42d53f9 Binary files /dev/null and b/docs/images/il-structures.png differ diff --git a/docs/images/sr-coincount-hacked.png b/docs/images/sr-coincount-hacked.png new file mode 100644 index 0000000..4dcd1c7 Binary files /dev/null and b/docs/images/sr-coincount-hacked.png differ diff --git a/docs/images/sr-coincount.png b/docs/images/sr-coincount.png new file mode 100644 index 0000000..4d44e15 Binary files /dev/null and b/docs/images/sr-coincount.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..d51dc0a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,5 @@ +# MindControl Documentation + +This is the documentation for MindControl, a .net library for memory manipulation and process hacking. It provides a set of features to manipulate memory in other processes, making it useful for game hacking, debugging, and reverse engineering. + +Start with the [Introduction](./guide/introduction.md) and navigate through the pages to learn about the features and how to use them. The documentation also includes a tutorial to hacking in general, if you are new. \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..65a1c7b --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,4 @@ +- name: Guide + href: guide/ +- name: API Reference + href: api/ \ No newline at end of file diff --git a/res/icon_code.png b/res/icon_code.png new file mode 100644 index 0000000..539294b Binary files /dev/null and b/res/icon_code.png differ diff --git a/res/icon_code.svg b/res/icon_code.svg new file mode 100644 index 0000000..7ec7e1f --- /dev/null +++ b/res/icon_code.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + diff --git a/samples/MindControl.Samples.SlimeRancherDemo/GameState.cs b/samples/MindControl.Samples.SlimeRancherDemo/GameState.cs new file mode 100644 index 0000000..7cb4f68 --- /dev/null +++ b/samples/MindControl.Samples.SlimeRancherDemo/GameState.cs @@ -0,0 +1,49 @@ +using System.Runtime.InteropServices; + +namespace MindControl.Samples.SlimeRancherDemo; + +/// Enumerates the potential states that the game can have. +public enum GameRunState +{ + /// The game is not running. + NotRunning, + /// The game is running but no game has been started. + InMenu, + /// The game is running and a game has been loaded. + InGame +} + +/// Represents the current state of the Slime Rancher demo. +/// The current state of the game. +/// The current player state. +public record GameState(GameRunState State, PlayerState? Player); + +/// Represents the current state of the player in the Slime Rancher demo. +/// +/// We are using the StructLayout attribute with the "Explicit" value here, which allows us to specify the offset of +/// each field in the structure. This lets us to mimic the actual structure of the game's player state in memory, which +/// means we can read and write the player state directly from/to memory in a single operation. +/// +[StructLayout(LayoutKind.Explicit)] +public record struct PlayerState +{ + /// Current stamina of the player. + [FieldOffset(0x00)] + public float CurrentStamina; + + /// Current health of the player. + [FieldOffset(0x04)] + public float CurrentHealth; + + /// Current coin count of the player. + [FieldOffset(0x0C)] + public int CoinCount; + + /// Maximum health value, considering any upgrades. + [FieldOffset(0x40)] + public int MaxHealth; + + /// Maximum stamina value, considering any upgrades. + [FieldOffset(0x44)] + public int MaxStamina; +} \ No newline at end of file diff --git a/samples/MindControl.Samples.SlimeRancherDemo/MindControl.Samples.SlimeRancherDemo.csproj b/samples/MindControl.Samples.SlimeRancherDemo/MindControl.Samples.SlimeRancherDemo.csproj new file mode 100644 index 0000000..58117b0 --- /dev/null +++ b/samples/MindControl.Samples.SlimeRancherDemo/MindControl.Samples.SlimeRancherDemo.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/samples/MindControl.Samples.SlimeRancherDemo/SlimeRancherDemo.cs b/samples/MindControl.Samples.SlimeRancherDemo/SlimeRancherDemo.cs new file mode 100644 index 0000000..59ba981 --- /dev/null +++ b/samples/MindControl.Samples.SlimeRancherDemo/SlimeRancherDemo.cs @@ -0,0 +1,57 @@ +namespace MindControl.Samples.SlimeRancherDemo; + +/// +/// Represents the tracked game. Provides methods built on top of the MindControl classes to manipulate the game. +/// +public class SlimeRancherDemo +{ + private readonly PointerPath _playerStatePath = "UnityPlayer.dll+0168EEA0,8,100,28,20,74"; + private readonly ProcessTracker _processTracker = new("SlimeRancher"); + + private IDisposable? _infiniteStaminaFreezer; + + public bool IsInfinityStaminaEnabled => _infiniteStaminaFreezer != null; + + /// Gets the current game state. + public GameState GetGameState() + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return new GameState(GameRunState.NotRunning, Player: null); + + var playerStateResult = process.Read(_playerStatePath); + if (!playerStateResult.IsSuccess) + return new GameState(GameRunState.InMenu, Player: null); + + return new GameState(GameRunState.InGame, playerStateResult.Value); + } + + /// Sets the player state. + /// The new player state. + public bool SetPlayerState(PlayerState newState) + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return false; + + return process.Write(_playerStatePath, newState).IsSuccess; + } + + /// Freezes the stamina value to 100, or unfreezes it if it's already frozen. + public bool ToggleInfiniteStamina() + { + var process = _processTracker.GetProcessMemory(); + if (process == null) + return false; + + if (_infiniteStaminaFreezer != null) + { + _infiniteStaminaFreezer.Dispose(); + _infiniteStaminaFreezer = null; + } + else + _infiniteStaminaFreezer = process.GetAnchor(_playerStatePath).Freeze(100); + + return true; + } +} \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/App.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/App.razor new file mode 100644 index 0000000..0c194f0 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/App.razor @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Components/GameStateComponent.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Components/GameStateComponent.razor new file mode 100644 index 0000000..d1d7e9a --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Components/GameStateComponent.razor @@ -0,0 +1,68 @@ +@using MindControl.Samples.SlimeRancherDemo +@inject SlimeRancherDemoService SlimeRancherService + +@switch (CurrentGameState.State) +{ + case GameRunState.NotRunning: +

The game is not running.

+ break; + case GameRunState.InMenu: +

The game is running, but no game is currently loaded.

+ break; + case GameRunState.InGame: + default: +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + break; +} + +@code { + private GameState CurrentGameState => SlimeRancherService.CurrentGameState; + private PlayerState? PlayerState => SlimeRancherService.CurrentGameState.Player; + + protected override void OnInitialized() => SlimeRancherService.GameStateUpdated += OnGameStateUpdated; + private void OnGameStateUpdated(object? sender, GameState e) => InvokeAsync(StateHasChanged); + + private void OnStaminaModified(ChangeEventArgs e) + { + if (!int.TryParse(e.Value?.ToString(), out int stamina)) + return; + + var newPlayerState = PlayerState.GetValueOrDefault() with { CurrentStamina = stamina }; + SlimeRancherService.SetPlayerState(newPlayerState); + } + + private void OnHealthModified(ChangeEventArgs e) + { + if (!int.TryParse(e.Value?.ToString(), out int health)) + return; + + var newPlayerState = PlayerState.GetValueOrDefault() with { CurrentHealth = health }; + SlimeRancherService.SetPlayerState(newPlayerState); + } + + private void OnCoinsModified(ChangeEventArgs e) + { + if (!int.TryParse(e.Value?.ToString(), out int coins)) + return; + + var newPlayerState = PlayerState.GetValueOrDefault() with { CoinCount = coins }; + SlimeRancherService.SetPlayerState(newPlayerState); + } +} \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/MainLayout.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..7ff1bf0 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/MainLayout.razor @@ -0,0 +1,19 @@ +@inherits LayoutComponentBase + +
+ + +
+
+ @Body +
+
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
\ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/MainLayout.razor.css b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/MainLayout.razor.css new file mode 100644 index 0000000..038baf1 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/MainLayout.razor.css @@ -0,0 +1,96 @@ +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; + } + + .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; + } + + .top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row { + justify-content: space-between; + } + + .top-row ::deep a, .top-row ::deep .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row.auth ::deep a:first-child { + flex: 1; + text-align: right; + width: 0; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/NavMenu.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/NavMenu.razor new file mode 100644 index 0000000..f4dd088 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/NavMenu.razor @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/NavMenu.razor.css b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/NavMenu.razor.css new file mode 100644 index 0000000..4e15395 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Layout/NavMenu.razor.css @@ -0,0 +1,105 @@ +.navbar-toggler { + appearance: none; + cursor: pointer; + width: 3.5rem; + height: 2.5rem; + color: white; + position: absolute; + top: 0.5rem; + right: 1rem; + border: 1px solid rgba(255, 255, 255, 0.1); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); +} + +.navbar-toggler:checked { + background-color: rgba(255, 255, 255, 0.5); +} + +.top-row { + height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.bi { + display: inline-block; + position: relative; + width: 1.25rem; + height: 1.25rem; + margin-right: 0.75rem; + top: -1px; + background-size: cover; +} + +.bi-house-door-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); +} + +.bi-plus-square-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); +} + +.bi-list-nested-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep .nav-link { + color: #d7d7d7; + background: none; + border: none; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + width: 100%; + } + +.nav-item ::deep a.active { + background-color: rgba(255,255,255,0.37); + color: white; +} + +.nav-item ::deep .nav-link:hover { + background-color: rgba(255,255,255,0.1); + color: white; +} + +.nav-scrollable { + display: none; +} + +.navbar-toggler:checked ~ .nav-scrollable { + display: block; +} + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .nav-scrollable { + /* Never collapse the sidebar for wide screens */ + display: block; + + /* Allow sidebar to scroll for tall menus */ + height: calc(100vh - 3.5rem); + overflow-y: auto; + } +} diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Pages/Error.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Pages/Error.razor new file mode 100644 index 0000000..9d7c6be --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; + +} \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Pages/Home.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Pages/Home.razor new file mode 100644 index 0000000..aa13c71 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Pages/Home.razor @@ -0,0 +1,15 @@ +@page "/" +@using MindControl.Samples.SrDemoBlazorApp.Components.Components + +Slime Rancher Demo MindControl Blazor App + +

Slime Rancher Demo MindControl Blazor App

+ +

+ This application is a demo for the MindControl library.
+ Start the free Slime Rancher demo to see and edit the game state.
+ Slime Rancher belongs to Monomi Park. They are not responsible for this software. +

+ +

Game state

+ \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/Routes.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Routes.razor new file mode 100644 index 0000000..ae94e9e --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Components/_Imports.razor b/samples/MindControl.Samples.SrDemoBlazorApp/Components/_Imports.razor new file mode 100644 index 0000000..f04aa6b --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Components/_Imports.razor @@ -0,0 +1,10 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using MindControl.Samples.SrDemoBlazorApp +@using MindControl.Samples.SrDemoBlazorApp.Components \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/MindControl.Samples.SrDemoBlazorApp.csproj b/samples/MindControl.Samples.SrDemoBlazorApp/MindControl.Samples.SrDemoBlazorApp.csproj new file mode 100644 index 0000000..23f110d --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/MindControl.Samples.SrDemoBlazorApp.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Program.cs b/samples/MindControl.Samples.SrDemoBlazorApp/Program.cs new file mode 100644 index 0000000..3350ec2 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Program.cs @@ -0,0 +1,30 @@ +using MindControl.Samples.SrDemoBlazorApp; +using MindControl.Samples.SrDemoBlazorApp.Components; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services + // Register the SlimeRancherDemoService as a singleton, so it can be used by multiple pages and components. + .AddSingleton() + .AddRazorComponents() + .AddInteractiveServerComponents(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); +app.UseAntiforgery(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + +app.Run(); \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/Properties/launchSettings.json b/samples/MindControl.Samples.SrDemoBlazorApp/Properties/launchSettings.json new file mode 100644 index 0000000..0bae6c8 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:44869", + "sslPort": 44395 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5170", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7280;http://localhost:5170", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/SlimeRancherDemoService.cs b/samples/MindControl.Samples.SrDemoBlazorApp/SlimeRancherDemoService.cs new file mode 100644 index 0000000..b837464 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/SlimeRancherDemoService.cs @@ -0,0 +1,40 @@ +using System.Timers; +using MindControl.Samples.SlimeRancherDemo; + +namespace MindControl.Samples.SrDemoBlazorApp; + +/// +/// A service built on top of the class, that periodically refreshes the game state. +/// This service is designed to be instanciated as a singleton and then used by multiple pages & components in the app. +/// +public class SlimeRancherDemoService +{ + private readonly SlimeRancherDemo.SlimeRancherDemo _slimeRancher; + private readonly System.Timers.Timer _stateUpdateTimer; + + /// Gets the current game state, refreshed periodically. + public GameState CurrentGameState { get; private set; } = new(GameRunState.NotRunning, null); + public event EventHandler? GameStateUpdated; + + public SlimeRancherDemoService() + { + _slimeRancher = new SlimeRancherDemo.SlimeRancherDemo(); + _stateUpdateTimer = new System.Timers.Timer(TimeSpan.FromMilliseconds(100)); + _stateUpdateTimer.Elapsed += OnStateUpdateTimerTick; + _stateUpdateTimer.Start(); + } + + /// Callback for the timer. Refreshes the game state. + private void OnStateUpdateTimerTick(object? sender, ElapsedEventArgs e) + { + CurrentGameState = _slimeRancher.GetGameState(); + GameStateUpdated?.Invoke(this, CurrentGameState); + } + + /// Writes the given player state to memory. + /// The player state to write. + public bool SetPlayerState(PlayerState playerState) => _slimeRancher.SetPlayerState(playerState); + + /// Enables or disables infinite stamina. + public void ToggleInfiniteStamina() => _slimeRancher.ToggleInfiniteStamina(); +} diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/appsettings.Development.json b/samples/MindControl.Samples.SrDemoBlazorApp/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/appsettings.json b/samples/MindControl.Samples.SrDemoBlazorApp/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/app.css b/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/app.css new file mode 100644 index 0000000..72d1e34 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/app.css @@ -0,0 +1,71 @@ +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +a, .btn-link { + color: #006bb7; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; +} + +.content { + padding-top: 1.1rem; +} + +h1:focus { + outline: none; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid #e50000; +} + +.validation-message { + color: #e50000; +} + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +.darker-border-checkbox.form-check-input { + border-color: #929292; +} + +.field-container { + display: flex; + flex-direction: column; + gap: 10px; +} + +.field { + display: flex; + align-items: center; + gap: 10px; +} + +.field label { + width: 100px; +} + +.field input { + width: 130px; +} \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/bootstrap/bootstrap.min.css b/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/bootstrap/bootstrap.min.css new file mode 100644 index 0000000..02ae65b --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/bootstrap/bootstrap.min.css @@ -0,0 +1,7 @@ +@charset "UTF-8";/*! + * Bootstrap v5.1.0 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-rgb:33,37,41;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-sm .offcanvas-bottom,.navbar-expand-sm .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-md .offcanvas-bottom,.navbar-expand-md .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-lg .offcanvas-bottom,.navbar-expand-lg .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xl .offcanvas-bottom,.navbar-expand-xl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xxl .offcanvas-bottom,.navbar-expand-xxl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand .offcanvas-bottom,.navbar-expand .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{-webkit-animation:placeholder-glow 2s ease-in-out infinite;animation:placeholder-glow 2s ease-in-out infinite}@-webkit-keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;-webkit-animation:placeholder-wave 2s linear infinite;animation:placeholder-wave 2s linear infinite}@-webkit-keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/bootstrap/bootstrap.min.css.map b/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/bootstrap/bootstrap.min.css.map new file mode 100644 index 0000000..afcd9e3 --- /dev/null +++ b/samples/MindControl.Samples.SrDemoBlazorApp/wwwroot/bootstrap/bootstrap.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","../../scss/mixins/_border-radius.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/_tables.scss","../../scss/mixins/_table-variants.scss","../../scss/forms/_labels.scss","../../scss/forms/_form-text.scss","../../scss/forms/_form-control.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_gradients.scss","../../scss/forms/_form-select.scss","../../scss/forms/_form-check.scss","../../scss/forms/_form-range.scss","../../scss/forms/_floating-labels.scss","../../scss/forms/_input-group.scss","../../scss/mixins/_forms.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/_button-group.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_accordion.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/mixins/_backdrop.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/_offcanvas.scss","../../scss/_placeholders.scss","../../scss/helpers/_colored-links.scss","../../scss/helpers/_ratio.scss","../../scss/helpers/_position.scss","../../scss/helpers/_stacks.scss","../../scss/helpers/_visually-hidden.scss","../../scss/mixins/_visually-hidden.scss","../../scss/helpers/_stretched-link.scss","../../scss/helpers/_text-truncation.scss","../../scss/mixins/_text-truncate.scss","../../scss/helpers/_vr.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap v5.1.0 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n\n// scss-docs-start import-stack\n// Configuration\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"utilities\";\n\n// Layout & components\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"containers\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"accordion\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"alert\";\n@import \"progress\";\n@import \"list-group\";\n@import \"close\";\n@import \"toasts\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"spinners\";\n@import \"offcanvas\";\n@import \"placeholders\";\n\n// Helpers\n@import \"helpers\";\n\n// Utilities\n@import \"utilities/api\";\n// scss-docs-end import-stack\n",":root {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$variable-prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$variable-prefix}#{$color}-rgb: #{$value};\n }\n\n --#{$variable-prefix}white-rgb: #{to-rgb($white)};\n --#{$variable-prefix}black-rgb: #{to-rgb($black)};\n --#{$variable-prefix}body-rgb: #{to-rgb($body-color)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$variable-prefix}gradient: #{$gradient};\n\n // Root and body\n // stylelint-disable custom-property-empty-line-before\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$variable-prefix}root-font-size: #{$font-size-root};\n }\n --#{$variable-prefix}body-font-family: #{$font-family-base};\n --#{$variable-prefix}body-font-size: #{$font-size-base};\n --#{$variable-prefix}body-font-weight: #{$font-weight-base};\n --#{$variable-prefix}body-line-height: #{$line-height-base};\n --#{$variable-prefix}body-color: #{$body-color};\n @if $body-text-align != null {\n --#{$variable-prefix}body-text-align: #{$body-text-align};\n }\n --#{$variable-prefix}body-bg: #{$body-bg};\n // scss-docs-end root-body-variables\n // stylelint-enable custom-property-empty-line-before\n}\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n font-size: var(--#{$variable-prefix}-root-font-size);\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$variable-prefix}body-font-family);\n @include font-size(var(--#{$variable-prefix}body-font-size));\n font-weight: var(--#{$variable-prefix}body-font-weight);\n line-height: var(--#{$variable-prefix}body-line-height);\n color: var(--#{$variable-prefix}body-color);\n text-align: var(--#{$variable-prefix}body-text-align);\n background-color: var(--#{$variable-prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n background-color: currentColor;\n border: 0;\n opacity: $hr-opacity;\n}\n\nhr:not([size]) {\n height: $hr-height; // 2\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-bs-original-title] { // 1\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n text-decoration-skip-ink: none; // 4\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n\n &:hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n direction: ltr #{\"/* rtl:ignore */\"};\n unicode-bidi: bidi-override;\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-`