├── pch.cpp ├── BetterChat.rc ├── .gitignore ├── plugins ├── BetterChat.dll ├── BetterChat.exp ├── BetterChat.lib └── BetterChat.pdb ├── fmt ├── include │ └── fmt │ │ ├── posix.h │ │ ├── locale.h │ │ ├── ostream.h │ │ ├── ranges.h │ │ ├── os.h │ │ └── color.h └── src │ ├── format.cc │ └── os.cc ├── vcpkg.json ├── BetterChat.vcxproj.user ├── version.h ├── IMGUI ├── imgui_timeline.h ├── imgui_additions.cpp ├── imgui_additions.h ├── imgui_searchablecombo.h ├── imgui_impl_dx11.h ├── imgui_stdlib.h ├── imgui_impl_win32.h ├── imgui_rangeslider.h ├── imgui_stdlib.cpp ├── imgui_timeline.cpp ├── imconfig.h ├── imgui_searchablecombo.cpp ├── imgui_impl_win32.cpp └── imstb_rectpack.h ├── update_version.ps1 ├── pch.h ├── resource.h ├── BetterChat.sln ├── LICENSE ├── BakkesMod.props ├── logging.h ├── README.md ├── .github └── workflows │ └── build.yml ├── BetterChat.vcxproj.filters ├── BetterChat.vcxproj ├── BetterChat.h └── BetterChatGUI.cpp /pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /BetterChat.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JulienML/BetterChat/HEAD/BetterChat.rc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .vscode/ 3 | build/ 4 | vcpkg_installed/ 5 | 6 | README BakkesPlugins.md -------------------------------------------------------------------------------- /plugins/BetterChat.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JulienML/BetterChat/HEAD/plugins/BetterChat.dll -------------------------------------------------------------------------------- /plugins/BetterChat.exp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JulienML/BetterChat/HEAD/plugins/BetterChat.exp -------------------------------------------------------------------------------- /plugins/BetterChat.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JulienML/BetterChat/HEAD/plugins/BetterChat.lib -------------------------------------------------------------------------------- /plugins/BetterChat.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JulienML/BetterChat/HEAD/plugins/BetterChat.pdb -------------------------------------------------------------------------------- /fmt/include/fmt/posix.h: -------------------------------------------------------------------------------- 1 | #include "os.h" 2 | #warning "fmt/posix.h is deprecated; use fmt/os.h instead" 3 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", 3 | "dependencies": [ 4 | "nlohmann-json" 5 | ] 6 | } -------------------------------------------------------------------------------- /BetterChat.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define VERSION_MAJOR 3 3 | #define VERSION_MINOR 1 4 | #define VERSION_PATCH 0 5 | #define VERSION_BUILD 530 6 | 7 | #define stringify(a) stringify_(a) 8 | #define stringify_(a) #a 9 | 10 | -------------------------------------------------------------------------------- /IMGUI/imgui_timeline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace ImGui { 3 | 4 | bool BeginTimeline(const char* str_id, float max_time); 5 | bool TimelineEvent(const char* str_id, float times[2]); 6 | void EndTimeline(float current_time = -1); 7 | 8 | } 9 | 10 | -------------------------------------------------------------------------------- /update_version.ps1: -------------------------------------------------------------------------------- 1 | (Get-Content -path $args[0] -Raw) | 2 | ForEach-Object { 3 | $defstr="#define VERSION_BUILD "; 4 | $regex="$defstr(?\d*)"; 5 | if($_ -match $regex) { 6 | $_ = $_ -replace $regex,"$($defstr)$(([int]$matches["BuildVersion"])+1)" 7 | } 8 | $_ 9 | } | 10 | Out-File $args[0] -encoding ascii -nonewline -------------------------------------------------------------------------------- /pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define _CRT_SECURE_NO_WARNINGS 5 | #include "bakkesmod/plugin/bakkesmodplugin.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "IMGUI/imgui.h" 13 | #include "IMGUI/imgui_stdlib.h" 14 | #include "IMGUI/imgui_searchablecombo.h" 15 | #include "IMGUI/imgui_rangeslider.h" 16 | 17 | #include "logging.h" -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by TemplateChanges.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /IMGUI/imgui_additions.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "imgui_additions.h" 3 | 4 | namespace ImGui { 5 | bool Combo(const char* label, int* currIndex, std::vector& values) 6 | { 7 | if (values.empty()) { return false; } 8 | return Combo(label, currIndex, vector_getter, 9 | static_cast(&values), values.size()); 10 | } 11 | 12 | bool ListBox(const char* label, int* currIndex, std::vector& values) 13 | { 14 | if (values.empty()) { return false; } 15 | return ListBox(label, currIndex, vector_getter, 16 | static_cast(&values), values.size()); 17 | } 18 | } -------------------------------------------------------------------------------- /IMGUI/imgui_additions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "imgui.h" 5 | #include "imgui_internal.h" 6 | 7 | namespace ImGui 8 | { 9 | static auto vector_getter = [](void* vec, int idx, const char** out_text) 10 | { 11 | auto& vector = *static_cast*>(vec); 12 | if (idx < 0 || idx >= static_cast(vector.size())) { return false; } 13 | *out_text = vector.at(idx).c_str(); 14 | return true; 15 | }; 16 | bool Combo(const char* label, int* currIndex, std::vector& values); 17 | bool ListBox(const char* label, int* currIndex, std::vector& values); 18 | } -------------------------------------------------------------------------------- /IMGUI/imgui_searchablecombo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "imgui.h" 3 | 4 | #include // isprint 5 | #include // vector<> 6 | #include // string 7 | #include // transform 8 | 9 | namespace ImGui 10 | { 11 | IMGUI_API bool BeginSearchableCombo(const char* label, const char* preview_value, char* input, int input_size, const char* input_preview_value, ImGuiComboFlags flags = 0); 12 | IMGUI_API void EndSearchableCombo(); 13 | IMGUI_API bool SearchableCombo(const char* label, int* current_item, std::vector items, const char* default_preview_text, const char* input_preview_value, int popup_max_height_in_items = -1); 14 | } // namespace ImGui -------------------------------------------------------------------------------- /BetterChat.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BetterChat", "BetterChat.vcxproj", "{4D053DE5-3F47-432D-BE43-BDD54680D990}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Release|x64 = Release|x64 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {4D053DE5-3F47-432D-BE43-BDD54680D990}.Release|x64.ActiveCfg = Release|x64 14 | {4D053DE5-3F47-432D-BE43-BDD54680D990}.Release|x64.Build.0 = Release|x64 15 | EndGlobalSection 16 | GlobalSection(SolutionProperties) = preSolution 17 | HideSolutionNode = FALSE 18 | EndGlobalSection 19 | GlobalSection(ExtensibilityGlobals) = postSolution 20 | SolutionGuid = {3056B556-37ED-439C-880E-56BECECA3365} 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 JulienML 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /BakkesMod.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(registry:HKEY_CURRENT_USER\Software\BakkesMod\AppPath@BakkesModPath) 6 | 7 | 8 | 9 | $(BakkesModPath)\bakkesmodsdk\include;%(AdditionalIncludeDirectories) 10 | 11 | 12 | $(BakkesModPath)\bakkesmodsdk\lib;%(AdditionalLibraryDirectories) 13 | pluginsdk.lib;%(AdditionalDependencies) 14 | 15 | 16 | "$(BakkesModPath)\bakkesmodsdk\bakkesmod-patch.exe" "$(TargetPath)" 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /IMGUI/imgui_impl_dx11.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer for DirectX11 2 | // This needs to be used along with a Platform Binding (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. 7 | 8 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 9 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 10 | // https://github.com/ocornut/imgui 11 | 12 | #pragma once 13 | 14 | struct ID3D11Device; 15 | struct ID3D11DeviceContext; 16 | 17 | IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); 18 | IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); 19 | IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); 20 | IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); 21 | 22 | // Use if you want to reset your rendering device without losing ImGui state. 23 | IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); 24 | IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); -------------------------------------------------------------------------------- /IMGUI/imgui_stdlib.h: -------------------------------------------------------------------------------- 1 | // dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) 2 | // This is also an example of how you may wrap your own similar types. 3 | 4 | // Compatibility: 5 | // - std::string support is only guaranteed to work from C++11. 6 | // If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture) 7 | 8 | // Changelog: 9 | // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | namespace ImGui 16 | { 17 | // ImGui::InputText() with std::string 18 | // Because text input needs dynamic resizing, we need to setup a callback to grow the capacity 19 | IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); 20 | IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); 21 | IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); 22 | } 23 | -------------------------------------------------------------------------------- /IMGUI/imgui_impl_win32.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | 4 | // Implemented features: 5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) 6 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 7 | // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). 8 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | 10 | #pragma once 11 | 12 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); 13 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); 14 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); 15 | 16 | // Configuration: Disable gamepad support or linking with xinput.lib 17 | #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 18 | #define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT 19 | 20 | // Handler for Win32 messages, update mouse/keyboard data. 21 | // You may or not need this for your implementation, but it can serve as reference for handling inputs. 22 | // Intentionally commented out to avoid dragging dependencies on types. You can COPY this line into your .cpp code instead. 23 | #if 0 24 | IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 25 | #endif -------------------------------------------------------------------------------- /IMGUI/imgui_rangeslider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "imgui.h" 3 | namespace ImGui 4 | { 5 | IMGUI_API bool RangeSliderScalar(const char* label, ImGuiDataType data_type, void* p_data1, void* p_data2, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f); 6 | IMGUI_API bool RangeSliderScalarN(const char* label, ImGuiDataType data_type, void* p_data1, void* p_data2, int components, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f); 7 | IMGUI_API bool RangeSliderFloat(const char* label, float* v1, float* v2, float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders 8 | IMGUI_API bool RangeSliderFloat2(const char* label, float v1[2], float v2[2], float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 9 | IMGUI_API bool RangeSliderFloat3(const char* label, float v1[3], float v2[3], float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 10 | IMGUI_API bool RangeSliderFloat4(const char* label, float v1[4], float v2[4], float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 11 | IMGUI_API bool RangeSliderAngle(const char* label, float* v_rad1, float* v_rad2, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "(%d, %d) deg"); 12 | IMGUI_API bool RangeSliderInt(const char* label, int* v1, int* v2, int v_min, int v_max, const char* format = "(%d, %d)"); 13 | IMGUI_API bool RangeSliderInt2(const char* label, int v1[2], int v2[2], int v_min, int v_max, const char* format = "(%d, %d)"); 14 | IMGUI_API bool RangeSliderInt3(const char* label, int v1[3], int v2[3], int v_min, int v_max, const char* format = "(%d, %d)"); 15 | IMGUI_API bool RangeSliderInt4(const char* label, int v1[4], int v2[4], int v_min, int v_max, const char* format = "(%d, %d)"); 16 | IMGUI_API bool RangeVSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data1, void* p_data2, const void* p_min, const void* p_max, const char* format = NULL, float power = 1.0f); 17 | IMGUI_API bool RangeVSliderFloat(const char* label, const ImVec2& size, float* v1, float* v2, float v_min, float v_max, const char* format = "(%.3f, %.3f)", float power = 1.0f); 18 | IMGUI_API bool RangeVSliderInt(const char* label, const ImVec2& size, int* v1, int* v2, int v_min, int v_max, const char* format = "(%d, %d)"); 19 | 20 | } // namespace ImGui -------------------------------------------------------------------------------- /logging.h: -------------------------------------------------------------------------------- 1 | // ReSharper disable CppNonExplicitConvertingConstructor 2 | #pragma once 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bakkesmod/wrappers/cvarmanagerwrapper.h" 9 | 10 | extern std::shared_ptr _globalCvarManager; 11 | constexpr bool DEBUG_LOG = false; 12 | 13 | 14 | struct FormatString 15 | { 16 | std::string_view str; 17 | std::source_location loc{}; 18 | 19 | FormatString(const char* str, const std::source_location& loc = std::source_location::current()) : str(str), loc(loc) 20 | { 21 | } 22 | 23 | FormatString(const std::string&& str, const std::source_location& loc = std::source_location::current()) : str(str), loc(loc) 24 | { 25 | } 26 | 27 | [[nodiscard]] std::string GetLocation() const 28 | { 29 | return std::format("[{} ({}:{})]", loc.function_name(), loc.file_name(), loc.line()); 30 | } 31 | }; 32 | 33 | struct FormatWstring 34 | { 35 | std::wstring_view str; 36 | std::source_location loc{}; 37 | 38 | FormatWstring(const wchar_t* str, const std::source_location& loc = std::source_location::current()) : str(str), loc(loc) 39 | { 40 | } 41 | 42 | FormatWstring(const std::wstring&& str, const std::source_location& loc = std::source_location::current()) : str(str), loc(loc) 43 | { 44 | } 45 | 46 | [[nodiscard]] std::wstring GetLocation() const 47 | { 48 | auto basic_string = std::format("[{} ({}:{})]", loc.function_name(), loc.file_name(), loc.line()); 49 | return std::wstring(basic_string.begin(), basic_string.end()); 50 | } 51 | }; 52 | 53 | 54 | template 55 | void LOG(std::string_view format_str, Args&&... args) 56 | { 57 | _globalCvarManager->log(std::vformat(format_str, std::make_format_args(args...))); 58 | } 59 | 60 | template 61 | void LOG(std::wstring_view format_str, Args&&... args) 62 | { 63 | _globalCvarManager->log(std::vformat(format_str, std::make_wformat_args(args...))); 64 | } 65 | 66 | 67 | template 68 | void DEBUGLOG(const FormatString& format_str, Args&&... args) 69 | { 70 | if constexpr (DEBUG_LOG) 71 | { 72 | auto text = std::vformat(format_str.str, std::make_format_args(args...)); 73 | auto location = format_str.GetLocation(); 74 | _globalCvarManager->log(std::format("{} {}", text, location)); 75 | } 76 | } 77 | 78 | template 79 | void DEBUGLOG(const FormatWstring& format_str, Args&&... args) 80 | { 81 | if constexpr (DEBUG_LOG) 82 | { 83 | auto text = std::vformat(format_str.str, std::make_wformat_args(args...)); 84 | auto location = format_str.GetLocation(); 85 | _globalCvarManager->log(std::format(L"{} {}", text, location)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BetterChat [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 2 | 3 | **BetterChat** is a plugin that works with **BakkesMod**, allowing you to choose which quickchats you want to disallow at which points in the game (after a goal, after a save, etc.). There is also an anti-spam function. At the end of the game, you get the toxicity score of each player (i.e. a recap of the number of blocked messages for each player). 4 | 5 | In this way, you can make the usual toxic Rocket League chat more respectful and focus on your gameplay. 6 | 7 | **If you have any suggestions to improve the plugin, don't hesitate to create an [Issue](https://github.com/JulienML/BetterChat/issues) or a [Pull Request](https://github.com/JulienML/BetterChat/pulls)!** 8 | 9 | # Table of contents 10 | 11 | * [Installation](#installation) 12 | * [Configuration](#configuration) 13 | * [Anti-Spam](#anti-spam) 14 | * [Chat Filter](#chat-filter) 15 | 16 | # Installation 17 | 18 | - Install [BakkesMod](https://www.bakkesmod.com/download.php). 19 | - Launch Rocket League and make sure BakkesMod is working. 20 | - Install [BetterChat](https://bakkesplugins.com/plugins/view/416) plugin. 21 | 22 | You are ready to use the plugin :) 23 | 24 | # Configuration 25 | 26 | You can configure the plugin easily by pressing F2 -> Plugins -> BetterChat Plugin. 27 | 28 | Here, you can create as many configurations as you want and assign them to the different gamemodes: 29 | 30 | ![Menu1](https://github.com/user-attachments/assets/184db644-d443-443c-9ee7-929d249f9867) 31 | 32 | Each configuration is composed of two features: an Anti-Spam and a Message Filter. 33 | 34 | ## Anti-Spam 35 | 36 | | Without Anti-Spam | With Anti-Spam | 37 | | :-------------: |:-------------:| 38 | | ![Gif1](https://user-images.githubusercontent.com/108280870/297385991-b60ccd7b-1461-4352-8a42-d5d43518d679.gif) | ![Gif2](https://user-images.githubusercontent.com/108280870/297386150-fafc56e7-0464-4811-8598-7d1d8c21db53.gif) | 39 | 40 | In the Anti-Spam section, you can set the delay between two identical quickchat from the same player. 41 | 42 | ## Message Filter 43 | 44 | | Without the Message Filter | With the Message Filter | 45 | | :-------------: |:-------------:| 46 | | ![Gif3](https://user-images.githubusercontent.com/108280870/297386506-b0152ce6-dd2c-41ab-b528-f302037bab8c.gif) | ![Gif4](https://user-images.githubusercontent.com/108280870/297386316-dc14da4c-5fab-49f0-ae75-6e4aec44fe33.gif) | 47 | 48 | In the Message Filter section, you can allow/disallow the quickchat you want at the moment you want by pressing the corresponding button: 49 | 50 | ![Menu3](https://github.com/JulienML/BetterChat/assets/108280870/12da1a67-6017-4221-8529-1a4e5d4140c8) 51 | 52 | At the bottom, you can configure the position of the toxicity scores at the end of the game: 53 | 54 | ![Menu5](https://github.com/JulienML/BetterChat/assets/108280870/4fdb965d-8cb5-4e34-a509-7903a4f9eba5) 55 | -------------------------------------------------------------------------------- /fmt/src/format.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | #include "pch.h" 8 | #include "fmt/format-inl.h" 9 | 10 | FMT_BEGIN_NAMESPACE 11 | namespace detail { 12 | 13 | template 14 | int format_float(char* buf, std::size_t size, const char* format, int precision, 15 | T value) { 16 | #ifdef FMT_FUZZ 17 | if (precision > 100000) 18 | throw std::runtime_error( 19 | "fuzz mode - avoid large allocation inside snprintf"); 20 | #endif 21 | // Suppress the warning about nonliteral format string. 22 | int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; 23 | return precision < 0 ? snprintf_ptr(buf, size, format, value) 24 | : snprintf_ptr(buf, size, format, precision, value); 25 | } 26 | } // namespace detail 27 | 28 | template struct FMT_INSTANTIATION_DEF_API detail::basic_data; 29 | 30 | // Workaround a bug in MSVC2013 that prevents instantiation of format_float. 31 | int (*instantiate_format_float)(double, int, detail::float_specs, 32 | detail::buffer&) = detail::format_float; 33 | 34 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 35 | template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); 36 | template FMT_API std::locale detail::locale_ref::get() const; 37 | #endif 38 | 39 | // Explicit instantiations for char. 40 | 41 | template FMT_API std::string detail::grouping_impl(locale_ref); 42 | template FMT_API char detail::thousands_sep_impl(locale_ref); 43 | template FMT_API char detail::decimal_point_impl(locale_ref); 44 | 45 | template FMT_API void detail::buffer::append(const char*, const char*); 46 | 47 | template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to( 48 | detail::buffer&, string_view, 49 | basic_format_args); 50 | 51 | template FMT_API int detail::snprintf_float(double, int, detail::float_specs, 52 | detail::buffer&); 53 | template FMT_API int detail::snprintf_float(long double, int, 54 | detail::float_specs, 55 | detail::buffer&); 56 | template FMT_API int detail::format_float(double, int, detail::float_specs, 57 | detail::buffer&); 58 | template FMT_API int detail::format_float(long double, int, detail::float_specs, 59 | detail::buffer&); 60 | 61 | // Explicit instantiations for wchar_t. 62 | 63 | template FMT_API std::string detail::grouping_impl(locale_ref); 64 | template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); 65 | template FMT_API wchar_t detail::decimal_point_impl(locale_ref); 66 | 67 | template FMT_API void detail::buffer::append(const wchar_t*, 68 | const wchar_t*); 69 | FMT_END_NAMESPACE 70 | -------------------------------------------------------------------------------- /fmt/include/fmt/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::locale support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_LOCALE_H_ 9 | #define FMT_LOCALE_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | namespace detail { 18 | template 19 | typename buffer_context::iterator vformat_to( 20 | const std::locale& loc, buffer& buf, 21 | basic_string_view format_str, 22 | basic_format_args>> args) { 23 | using af = arg_formatter::iterator, Char>; 24 | return vformat_to(std::back_inserter(buf), to_string_view(format_str), 25 | args, detail::locale_ref(loc)); 26 | } 27 | 28 | template 29 | std::basic_string vformat( 30 | const std::locale& loc, basic_string_view format_str, 31 | basic_format_args>> args) { 32 | basic_memory_buffer buffer; 33 | detail::vformat_to(loc, buffer, format_str, args); 34 | return fmt::to_string(buffer); 35 | } 36 | } // namespace detail 37 | 38 | template > 39 | inline std::basic_string vformat( 40 | const std::locale& loc, const S& format_str, 41 | basic_format_args>> args) { 42 | return detail::vformat(loc, to_string_view(format_str), args); 43 | } 44 | 45 | template > 46 | inline std::basic_string format(const std::locale& loc, 47 | const S& format_str, Args&&... args) { 48 | return detail::vformat( 49 | loc, to_string_view(format_str), 50 | detail::make_args_checked(format_str, args...)); 51 | } 52 | 53 | template ::value, char_t>> 56 | inline OutputIt vformat_to( 57 | OutputIt out, const std::locale& loc, const S& format_str, 58 | format_args_t, Char> args) { 59 | using af = detail::arg_formatter; 60 | return vformat_to(out, to_string_view(format_str), args, 61 | detail::locale_ref(loc)); 62 | } 63 | 64 | template ::value&& 66 | detail::is_string::value)> 67 | inline OutputIt format_to(OutputIt out, const std::locale& loc, 68 | const S& format_str, Args&&... args) { 69 | detail::check_format_string(format_str); 70 | using context = format_context_t>; 71 | format_arg_store as{args...}; 72 | return vformat_to(out, loc, to_string_view(format_str), 73 | basic_format_args(as)); 74 | } 75 | 76 | FMT_END_NAMESPACE 77 | 78 | #endif // FMT_LOCALE_H_ 79 | -------------------------------------------------------------------------------- /IMGUI/imgui_stdlib.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.) 2 | // This is also an example of how you may wrap your own similar types. 3 | 4 | // Compatibility: 5 | // - std::string support is only guaranteed to work from C++11. 6 | // If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture) 7 | 8 | // Changelog: 9 | // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string 10 | #include "pch.h" 11 | #include "imgui.h" 12 | #include "imgui_stdlib.h" 13 | 14 | struct InputTextCallback_UserData 15 | { 16 | std::string* Str; 17 | ImGuiInputTextCallback ChainCallback; 18 | void* ChainCallbackUserData; 19 | }; 20 | 21 | static int InputTextCallback(ImGuiInputTextCallbackData* data) 22 | { 23 | InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData; 24 | if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) 25 | { 26 | // Resize string callback 27 | // If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want. 28 | std::string* str = user_data->Str; 29 | IM_ASSERT(data->Buf == str->c_str()); 30 | str->resize(data->BufTextLen); 31 | data->Buf = (char*)str->c_str(); 32 | } 33 | else if (user_data->ChainCallback) 34 | { 35 | // Forward to user callback, if any 36 | data->UserData = user_data->ChainCallbackUserData; 37 | return user_data->ChainCallback(data); 38 | } 39 | return 0; 40 | } 41 | 42 | bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) 43 | { 44 | IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); 45 | flags |= ImGuiInputTextFlags_CallbackResize; 46 | 47 | InputTextCallback_UserData cb_user_data; 48 | cb_user_data.Str = str; 49 | cb_user_data.ChainCallback = callback; 50 | cb_user_data.ChainCallbackUserData = user_data; 51 | return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); 52 | } 53 | 54 | bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) 55 | { 56 | IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); 57 | flags |= ImGuiInputTextFlags_CallbackResize; 58 | 59 | InputTextCallback_UserData cb_user_data; 60 | cb_user_data.Str = str; 61 | cb_user_data.ChainCallback = callback; 62 | cb_user_data.ChainCallbackUserData = user_data; 63 | return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data); 64 | } 65 | 66 | bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) 67 | { 68 | IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); 69 | flags |= ImGuiInputTextFlags_CallbackResize; 70 | 71 | InputTextCallback_UserData cb_user_data; 72 | cb_user_data.Str = str; 73 | cb_user_data.ChainCallback = callback; 74 | cb_user_data.ChainCallbackUserData = user_data; 75 | return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); 76 | } 77 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build BakkesMod Plugin 2 | 3 | on: 4 | push: 5 | branches: [ main, master ] 6 | pull_request: 7 | branches: [ main, master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | 18 | - name: Checkout BakkesMod SDK 19 | uses: actions/checkout@v4 20 | with: 21 | repository: bakkesmodorg/BakkesModSDK 22 | path: bakkesmod\bakkesmodsdk 23 | 24 | 25 | - name: Check for vcpkg manifest 26 | id: vcpkg_check 27 | shell: pwsh 28 | run: | 29 | $manifest = Test-Path "$env:GITHUB_WORKSPACE/vcpkg.json" 30 | Write-Host "vcpkg manifest present: $manifest" 31 | echo "vcpkg_manifest=$manifest" | Out-File -FilePath $env:GITHUB_OUTPUT -Append 32 | 33 | - name: Checkout vcpkg 34 | if: steps.vcpkg_check.outputs.vcpkg_manifest == 'True' 35 | uses: actions/checkout@v4 36 | with: 37 | repository: microsoft/vcpkg 38 | path: vcpkg 39 | 40 | - name: Bootstrap vcpkg 41 | if: steps.vcpkg_check.outputs.vcpkg_manifest == 'True' 42 | run: | 43 | .\vcpkg\bootstrap-vcpkg.bat 44 | 45 | - name: Integrate vcpkg 46 | if: steps.vcpkg_check.outputs.vcpkg_manifest == 'True' 47 | run: | 48 | .\vcpkg\vcpkg.exe integrate install 49 | 50 | 51 | - name: Setup MSBuild 52 | uses: microsoft/setup-msbuild@v2 53 | 54 | - name: Find solution file 55 | id: find_sln 56 | shell: pwsh 57 | run: | 58 | $sln = Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.sln -Recurse | Select-Object -First 1 59 | if ($null -eq $sln) { Write-Error 'No .sln file found!'; exit 1 } 60 | Write-Host "sln path: $($sln.FullName)" 61 | echo "SOLUTION_PATH=$($sln.FullName)" | Out-File -FilePath $env:GITHUB_ENV -Append 62 | 63 | - name: Build 64 | run: msbuild /m /p:Configuration=Release /p:BakkesModPath=$env:GITHUB_WORKSPACE\bakkesmod /p:PostBuildEventUseInBuild=false $env:SOLUTION_PATH 65 | 66 | 67 | - name: Prepare build artifacts 68 | run: | 69 | # Create artifacts/plugins directory 70 | New-Item -ItemType Directory -Force -Path "artifacts/plugins" 71 | 72 | if (Test-Path "plugins") { 73 | Get-ChildItem -Path "plugins" -Include "*.dll", "*.pdb" -Recurse | ForEach-Object { 74 | Copy-Item $_.FullName -Destination "artifacts/plugins" -Force 75 | } 76 | } 77 | 78 | if (Test-Path "data") { 79 | Copy-Item -Path "data" -Destination "artifacts/data" -Recurse -Force 80 | } 81 | 82 | Write-Host "Artifacts contents:" 83 | Get-ChildItem -Path "artifacts" -Recurse 84 | 85 | - name: Create release archive 86 | run: | 87 | # Create zip file with timestamp 88 | $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" 89 | $zipName = "${{ github.event.repository.name }}-$timestamp.zip" 90 | 91 | if (Test-Path "artifacts") { 92 | Compress-Archive -Path "artifacts\*" -DestinationPath $zipName 93 | echo "RELEASE_ZIP=$zipName" >> $env:GITHUB_ENV 94 | } else { 95 | Write-Error "No artifacts found to archive" 96 | exit 1 97 | } 98 | 99 | - name: Upload build artifacts 100 | uses: actions/upload-artifact@v4 101 | with: 102 | name: ${{ github.event.repository.name }} 103 | path: ${{ env.RELEASE_ZIP }} 104 | retention-days: 30 -------------------------------------------------------------------------------- /IMGUI/imgui_timeline.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "imgui_timeline.h" 3 | // cpp 4 | #include "imgui.h" 5 | #include "imgui_internal.h" 6 | // https://github.com/ocornut/imgui/issues/76 7 | 8 | namespace ImGui { 9 | 10 | static float s_max_timeline_value; 11 | 12 | 13 | bool BeginTimeline(const char* str_id, float max_time) 14 | { 15 | s_max_timeline_value = max_time; 16 | return BeginChild(str_id); 17 | } 18 | 19 | 20 | static const float TIMELINE_RADIUS = 6; 21 | 22 | 23 | bool TimelineEvent(const char* str_id, float values[2]) 24 | { 25 | ImGuiWindow* win = GetCurrentWindow(); 26 | const ImU32 inactive_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Button]); 27 | const ImU32 active_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_ButtonHovered]); 28 | const ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); 29 | bool changed = false; 30 | ImVec2 cursor_pos = win->DC.CursorPos; 31 | 32 | // @r-lyeh { 33 | Button(str_id, ImVec2(120, 0)); // @todo: enable/disable track channel here 34 | SameLine(); 35 | cursor_pos += ImVec2(0, GetTextLineHeightWithSpacing() / 3); 36 | // } 37 | 38 | for (int i = 0; i < 2; ++i) 39 | { 40 | ImVec2 pos = cursor_pos; 41 | pos.x += win->Size.x * values[i] / s_max_timeline_value + TIMELINE_RADIUS; 42 | pos.y += TIMELINE_RADIUS; 43 | 44 | SetCursorScreenPos(pos - ImVec2(TIMELINE_RADIUS, TIMELINE_RADIUS)); 45 | PushID(i); 46 | InvisibleButton(str_id, ImVec2(2 * TIMELINE_RADIUS, 2 * TIMELINE_RADIUS)); 47 | if (IsItemActive() || IsItemHovered()) 48 | { 49 | ImGui::SetTooltip("%f", values[i]); 50 | ImVec2 a(pos.x, GetWindowContentRegionMin().y + win->Pos.y + win->Scroll.y); 51 | ImVec2 b(pos.x, GetWindowContentRegionMax().y + win->Pos.y + win->Scroll.y); 52 | win->DrawList->AddLine(a, b, line_color); 53 | } 54 | if (IsItemActive() && IsMouseDragging(0)) 55 | { 56 | values[i] += GetIO().MouseDelta.x / win->Size.x * s_max_timeline_value; 57 | changed = true; 58 | } 59 | PopID(); 60 | win->DrawList->AddCircleFilled( 61 | pos, TIMELINE_RADIUS, IsItemActive() || IsItemHovered() ? active_color : inactive_color); 62 | } 63 | 64 | ImVec2 start = cursor_pos; 65 | start.x += win->Size.x * values[0] / s_max_timeline_value + 2 * TIMELINE_RADIUS; 66 | start.y += TIMELINE_RADIUS * 0.5f; 67 | ImVec2 end = start + ImVec2(win->Size.x * (values[1] - values[0]) / s_max_timeline_value - 2 * TIMELINE_RADIUS, 68 | TIMELINE_RADIUS); 69 | 70 | PushID(-1); 71 | SetCursorScreenPos(start); 72 | InvisibleButton(str_id, end - start); 73 | if (IsItemActive() && IsMouseDragging(0)) 74 | { 75 | values[0] += GetIO().MouseDelta.x / win->Size.x * s_max_timeline_value; 76 | values[1] += GetIO().MouseDelta.x / win->Size.x * s_max_timeline_value; 77 | changed = true; 78 | } 79 | PopID(); 80 | 81 | SetCursorScreenPos(cursor_pos + ImVec2(0, GetTextLineHeightWithSpacing())); 82 | 83 | win->DrawList->AddRectFilled(start, end, IsItemActive() || IsItemHovered() ? active_color : inactive_color); 84 | 85 | if (values[0] > values[1]) 86 | { 87 | float tmp = values[0]; 88 | values[0] = values[1]; 89 | values[1] = tmp; 90 | } 91 | if (values[1] > s_max_timeline_value) values[1] = s_max_timeline_value; 92 | if (values[0] < 0) values[0] = 0; 93 | return changed; 94 | } 95 | 96 | 97 | void EndTimeline(float t) 98 | { 99 | ImGuiWindow* win = GetCurrentWindow(); 100 | 101 | // @r-lyeh { 102 | if (t >= 0) { 103 | if (t > s_max_timeline_value) t = s_max_timeline_value; t /= s_max_timeline_value; 104 | const ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_SeparatorActive]); 105 | ImVec2 a(win->Pos.x + GetWindowContentRegionMin().x + t * GetWindowContentRegionWidth(), GetWindowContentRegionMin().y + win->Pos.y + win->Scroll.y); 106 | ImVec2 b(win->Pos.x + GetWindowContentRegionMin().x + t * GetWindowContentRegionWidth(), GetWindowContentRegionMax().y + win->Pos.y + win->Scroll.y); 107 | win->DrawList->AddLine(a, b, line_color); 108 | } 109 | // } 110 | 111 | ImU32 color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Button]); 112 | ImU32 line_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Border]); 113 | ImU32 text_color = ColorConvertFloat4ToU32(GImGui->Style.Colors[ImGuiCol_Text]); 114 | float rounding = GImGui->Style.ScrollbarRounding; 115 | ImVec2 start(GetWindowContentRegionMin().x + win->Pos.x, 116 | GetWindowContentRegionMax().y - GetTextLineHeightWithSpacing() + win->Pos.y + win->Scroll.y); 117 | ImVec2 end = GetWindowContentRegionMax() + win->Pos + ImVec2(0, win->Scroll.y); 118 | 119 | win->DrawList->AddRectFilled(start, end, color, rounding); 120 | 121 | const int LINE_COUNT = 5; 122 | const ImVec2 text_offset(0, GetTextLineHeightWithSpacing()); 123 | for (int i = 0; i <= LINE_COUNT; ++i) 124 | { 125 | ImVec2 a = GetWindowContentRegionMin() + win->Pos; // @r-lyeh: - ImVec2(TIMELINE_RADIUS, 0); 126 | a.x += i * (GetWindowContentRegionWidth() - 1) / LINE_COUNT; // @r-lyeh: -1 127 | ImVec2 b = a; 128 | b.y = start.y; 129 | win->DrawList->AddLine(a, b, line_color); 130 | char tmp[256]; 131 | ImFormatString(tmp, sizeof(tmp), "%.2f", i * s_max_timeline_value / LINE_COUNT); 132 | win->DrawList->AddText(b, text_color, tmp); 133 | } 134 | 135 | EndChild(); 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /BetterChat.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {396e4dda-4fe2-4588-9756-a364a57aa8c1} 18 | 19 | 20 | {ae0c99c9-78de-41f1-8c8d-e6eb860af833} 21 | 22 | 23 | {d46c4711-0bdc-484c-bb4c-5526d80f7f5e} 24 | 25 | 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | imgui\implementation 35 | 36 | 37 | imgui\implementation 38 | 39 | 40 | imgui\implementation 41 | 42 | 43 | imgui\implementation 44 | 45 | 46 | imgui\implementation 47 | 48 | 49 | imgui\implementation 50 | 51 | 52 | imgui\implementation 53 | 54 | 55 | imgui\implementation 56 | 57 | 58 | imgui\implementation 59 | 60 | 61 | imgui\implementation 62 | 63 | 64 | imgui\implementation 65 | 66 | 67 | Source Files 68 | 69 | 70 | Source Files 71 | 72 | 73 | Source Files 74 | 75 | 76 | Source Files 77 | 78 | 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | imgui\headers 88 | 89 | 90 | imgui\headers 91 | 92 | 93 | imgui\headers 94 | 95 | 96 | imgui\headers 97 | 98 | 99 | imgui\headers 100 | 101 | 102 | imgui\headers 103 | 104 | 105 | imgui\headers 106 | 107 | 108 | imgui\headers 109 | 110 | 111 | imgui\headers 112 | 113 | 114 | imgui\headers 115 | 116 | 117 | imgui\headers 118 | 119 | 120 | imgui\headers 121 | 122 | 123 | imgui\headers 124 | 125 | 126 | Header Files 127 | 128 | 129 | Header Files 130 | 131 | 132 | Header Files 133 | 134 | 135 | 136 | 137 | Resource Files 138 | 139 | 140 | -------------------------------------------------------------------------------- /fmt/include/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | template class basic_printf_parse_context; 18 | template class basic_printf_context; 19 | 20 | namespace detail { 21 | 22 | template class formatbuf : public std::basic_streambuf { 23 | private: 24 | using int_type = typename std::basic_streambuf::int_type; 25 | using traits_type = typename std::basic_streambuf::traits_type; 26 | 27 | buffer& buffer_; 28 | 29 | public: 30 | formatbuf(buffer& buf) : buffer_(buf) {} 31 | 32 | protected: 33 | // The put-area is actually always empty. This makes the implementation 34 | // simpler and has the advantage that the streambuf and the buffer are always 35 | // in sync and sputc never writes into uninitialized memory. The obvious 36 | // disadvantage is that each call to sputc always results in a (virtual) call 37 | // to overflow. There is no disadvantage here for sputn since this always 38 | // results in a call to xsputn. 39 | 40 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 41 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 42 | buffer_.push_back(static_cast(ch)); 43 | return ch; 44 | } 45 | 46 | std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { 47 | buffer_.append(s, s + count); 48 | return count; 49 | } 50 | }; 51 | 52 | template struct test_stream : std::basic_ostream { 53 | private: 54 | // Hide all operator<< from std::basic_ostream. 55 | void_t<> operator<<(null<>); 56 | void_t<> operator<<(const Char*); 57 | 58 | template ::value && 59 | !std::is_enum::value)> 60 | void_t<> operator<<(T); 61 | }; 62 | 63 | // Checks if T has a user-defined operator<< (e.g. not a member of 64 | // std::ostream). 65 | template class is_streamable { 66 | private: 67 | template 68 | static bool_constant&>() 69 | << std::declval()), 70 | void_t<>>::value> 71 | test(int); 72 | 73 | template static std::false_type test(...); 74 | 75 | using result = decltype(test(0)); 76 | 77 | public: 78 | static const bool value = result::value; 79 | }; 80 | 81 | // Write the content of buf to os. 82 | template 83 | void write_buffer(std::basic_ostream& os, buffer& buf) { 84 | const Char* buf_data = buf.data(); 85 | using unsigned_streamsize = std::make_unsigned::type; 86 | unsigned_streamsize size = buf.size(); 87 | unsigned_streamsize max_size = to_unsigned(max_value()); 88 | do { 89 | unsigned_streamsize n = size <= max_size ? size : max_size; 90 | os.write(buf_data, static_cast(n)); 91 | buf_data += n; 92 | size -= n; 93 | } while (size != 0); 94 | } 95 | 96 | template 97 | void format_value(buffer& buf, const T& value, 98 | locale_ref loc = locale_ref()) { 99 | formatbuf format_buf(buf); 100 | std::basic_ostream output(&format_buf); 101 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 102 | if (loc) output.imbue(loc.get()); 103 | #endif 104 | output << value; 105 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 106 | buf.resize(buf.size()); 107 | } 108 | 109 | // Formats an object of type T that has an overloaded ostream operator<<. 110 | template 111 | struct fallback_formatter::value>> 112 | : private formatter, Char> { 113 | FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) 114 | -> decltype(ctx.begin()) { 115 | return formatter, Char>::parse(ctx); 116 | } 117 | template >::value)> 120 | auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { 121 | return ctx.begin(); 122 | } 123 | 124 | template 125 | auto format(const T& value, basic_format_context& ctx) 126 | -> OutputIt { 127 | basic_memory_buffer buffer; 128 | format_value(buffer, value, ctx.locale()); 129 | basic_string_view str(buffer.data(), buffer.size()); 130 | return formatter, Char>::format(str, ctx); 131 | } 132 | template 133 | auto format(const T& value, basic_printf_context& ctx) 134 | -> OutputIt { 135 | basic_memory_buffer buffer; 136 | format_value(buffer, value, ctx.locale()); 137 | return std::copy(buffer.begin(), buffer.end(), ctx.out()); 138 | } 139 | }; 140 | } // namespace detail 141 | 142 | template 143 | void vprint(std::basic_ostream& os, basic_string_view format_str, 144 | basic_format_args>> args) { 145 | basic_memory_buffer buffer; 146 | detail::vformat_to(buffer, format_str, args); 147 | detail::write_buffer(os, buffer); 148 | } 149 | 150 | /** 151 | \rst 152 | Prints formatted data to the stream *os*. 153 | 154 | **Example**:: 155 | 156 | fmt::print(cerr, "Don't {}!", "panic"); 157 | \endrst 158 | */ 159 | template ::value, char_t>> 161 | void print(std::basic_ostream& os, const S& format_str, Args&&... args) { 162 | vprint(os, to_string_view(format_str), 163 | detail::make_args_checked(format_str, args...)); 164 | } 165 | FMT_END_NAMESPACE 166 | 167 | #endif // FMT_OSTREAM_H_ 168 | -------------------------------------------------------------------------------- /BetterChat.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Release 6 | x64 7 | 8 | 9 | 10 | 16.0 11 | {4d053de5-3f47-432d-be43-bdd54680d990} 12 | BetterChat 13 | 10.0 14 | 15 | 16 | 17 | DynamicLibrary 18 | true 19 | v142 20 | Unicode 21 | 22 | 23 | DynamicLibrary 24 | false 25 | v143 26 | true 27 | Unicode 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | true 45 | $(SolutionDir)plugins\ 46 | $(SolutionDir)build\.intermediates\$(Configuration)\ 47 | 48 | 49 | false 50 | $(SolutionDir)plugins\ 51 | $(SolutionDir)build\.intermediates\$(Configuration)\ 52 | 53 | 54 | true 55 | 56 | 57 | true 58 | 59 | 60 | 61 | Level3 62 | true 63 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 64 | true 65 | Use 66 | pch.h 67 | MultiThreadedDebug 68 | 69 | 70 | Console 71 | true 72 | 73 | 74 | 75 | 76 | Level3 77 | true 78 | true 79 | true 80 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 81 | true 82 | Use 83 | pch.h 84 | MultiThreaded 85 | stdcpp20 86 | true 87 | $(ProjectDir)fmt\include;%(AdditionalIncludeDirectories) 88 | 89 | 90 | Console 91 | true 92 | true 93 | true 94 | 95 | 96 | powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | Create 116 | Create 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /IMGUI/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h) 7 | // B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" 8 | // If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include 9 | // the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 10 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 11 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 12 | //----------------------------------------------------------------------------- 13 | 14 | #pragma once 15 | 16 | //---- Define assertion handler. Defaults to calling assert(). 17 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 18 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 19 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 20 | 21 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 22 | // Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 23 | //#define IMGUI_API __declspec( dllexport ) 24 | //#define IMGUI_API __declspec( dllimport ) 25 | 26 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 27 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 28 | 29 | //---- Disable all of Dear ImGui or don't implement standard windows. 30 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 31 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 32 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 33 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty. 34 | 35 | //---- Don't implement some functions to reduce linkage requirements. 36 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. 37 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. 38 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 39 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 40 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 41 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 42 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 43 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 44 | 45 | //---- Include imgui_user.h at the end of imgui.h as a convenience 46 | //#define IMGUI_INCLUDE_IMGUI_USER_H 47 | 48 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 49 | //#define IMGUI_USE_BGRA_PACKED_COLOR 50 | 51 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 52 | // By default the embedded implementations are declared static and not available outside of imgui cpp files. 53 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 54 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 55 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 56 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 57 | 58 | //---- Unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined, use the much faster STB sprintf library implementation of vsnprintf instead of the one from the default C library. 59 | // Note that stb_sprintf.h is meant to be provided by the user and available in the include path at compile time. Also, the compatibility checks of the arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 60 | // #define IMGUI_USE_STB_SPRINTF 61 | 62 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 63 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 64 | /* 65 | #define IM_VEC2_CLASS_EXTRA \ 66 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 67 | operator MyVec2() const { return MyVec2(x,y); } 68 | 69 | #define IM_VEC4_CLASS_EXTRA \ 70 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 71 | operator MyVec4() const { return MyVec4(x,y,z,w); } 72 | */ 73 | 74 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 75 | // Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bit indices). 76 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 77 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 78 | //#define ImDrawIdx unsigned int 79 | 80 | //---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly) 81 | //struct ImDrawList; 82 | //struct ImDrawCmd; 83 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 84 | //#define ImDrawCallback MyImDrawCallback 85 | 86 | //---- Debug Tools: Macro to break in Debugger 87 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 88 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 89 | //#define IM_DEBUG_BREAK __debugbreak() 90 | 91 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 92 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 93 | // This adds a small runtime cost which is why it is not enabled by default. 94 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 95 | 96 | //---- Debug Tools: Enable slower asserts 97 | //#define IMGUI_DEBUG_PARANOID 98 | 99 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 100 | /* 101 | namespace ImGui 102 | { 103 | void MyFunction(const char* name, const MyMatrix44& v); 104 | } 105 | */ 106 | 107 | #define IMGUI_DEFINE_MATH_OPERATORS -------------------------------------------------------------------------------- /BetterChat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bakkesmod/plugin/bakkesmodplugin.h" 4 | #include "bakkesmod/plugin/pluginwindow.h" 5 | #include "bakkesmod/plugin/PluginSettingsWindow.h" 6 | 7 | #include "version.h" 8 | 9 | #include 10 | #include 11 | #include 12 | constexpr auto plugin_version = stringify(VERSION_MAJOR) "." stringify(VERSION_MINOR) "." stringify(VERSION_PATCH); 13 | 14 | class BetterChat: public BakkesMod::Plugin::BakkesModPlugin, public BakkesMod::Plugin::PluginSettingsWindow 15 | { 16 | // Quickchats id <=> Quickchats texts 17 | std::map idQuickchats = { 18 | {"Group1Message1", "I got it!"}, // Je l'ai ! 19 | {"Group1Message2", "Need boost!"}, // Besoin de turbo ! 20 | {"Group1Message3", "Take the shot!"}, // Prends-le ! 21 | {"Group1Message4", "Defending."}, // Je d�fends. 22 | {"Group1Message5", "Go for it!"}, // Vas-y ! 23 | {"Group1Message6", "Centering!"}, // Centre ! 24 | {"Group1Message7", "All yours."}, // Il est pour toi. 25 | {"Group1Message8", "In position."}, // En position. 26 | {"Group1Message9", "Incoming!"}, // En approche ! 27 | {"Group1Message10", "Faking."}, // La feinte. 28 | {"Group1Message11", "Bumping!"}, // Impact ! 29 | {"Group1Message12", "On your left!"}, // Sur ta gauche ! 30 | {"Group1Message13", "On your right!"}, // Sur ta droite ! 31 | {"Group1Message14", "Passing!"}, // La passe ! 32 | {"Group1Message15", "Rotating Up!"}, // Je monte ! 33 | {"Group1Message16", "Rotating back!"}, // Je recule ! 34 | {"Group1Message17", "You have time!"}, // Tu as le temps ! 35 | 36 | {"Group2Message1", "Nice shot!"}, // Beau tir ! 37 | {"Group2Message2", "Great pass!"}, // Belle passe ! 38 | {"Group2Message3", "Thanks!"}, // Merci ! 39 | {"Group2Message4", "What a save!"}, // Quel arr�t ! 40 | {"Group2Message5", "Nice one!"}, // Bien vu ! 41 | {"Group2Message6", "What a play!"}, // Quelle intensit� ! 42 | {"Group2Message7", "Great clear!"}, // Beau d�gagement ! 43 | {"Group2Message8", "Nice block!"}, // Super blocage ! 44 | {"Group2Message9", "Nice bump!"}, // Bel impact ! 45 | {"Group2Message10", "Nice demo!"}, // Jolie d�mo ! 46 | {"Group2Message11", "We got this."}, // On assure ! 47 | 48 | {"Group3Message1", "OMG!"}, // Oh mon dieu ! 49 | {"Group3Message2", "Noooo!"}, // Noooon ! 50 | {"Group3Message3", "Wow!"}, // Wow ! 51 | {"Group3Message4", "Close one..."}, // C'�tait pas loin... 52 | {"Group3Message5", "No way!"}, // Pas possible ! 53 | {"Group3Message6", "Holy cow!"}, // S�rieux ?! 54 | {"Group3Message7", "Whew."}, // Waouh. 55 | {"Group3Message8", "Siiiick!"}, // Truc de ouf ! 56 | {"Group3Message9", "Calculated."}, // C'est pr�vu. 57 | {"Group3Message10", "Savage!"}, // Sauvage ! 58 | {"Group3Message11", "Okay."}, // Ok. 59 | {"Group3Message12", "Yes!"}, // Oui ! 60 | 61 | {"Group4Message1", "$#@%!"}, // $#@%! 62 | {"Group4Message2", "No problem."}, // Pas de probl�mes. 63 | {"Group4Message3", "Whoops..."}, // Oups... 64 | {"Group4Message4", "Sorry!"}, // D�sol� ! 65 | {"Group4Message5", "My bad..."}, // Pardon... 66 | {"Group4Message6", "Oops!"}, // Oups ! 67 | {"Group4Message7", "My fault."}, // Ma faute. 68 | 69 | {"Group5Message1", "gg"}, // gg 70 | {"Group5Message2", "Well played."}, // Bien jou�. 71 | {"Group5Message3", "That was fun!"}, // C'�tait cool ! 72 | {"Group5Message4", "Rematch!"}, // On remet �a ! 73 | {"Group5Message5", "One. More. Game."}, // Encore. Une. Partie. 74 | {"Group5Message6", "What a game!"}, // Quelle partie ! 75 | {"Group5Message7", "Nice moves!"}, // Super d�placements ! 76 | {"Group5Message8", "Everybody dance!"}, // Que tout le monde dance ! 77 | {"Group5Message9", "Party Up?"}, // On groupe ? 78 | 79 | {"Group6Message4", "This is Rocket League!"}, // �a c'est Rocket League ! 80 | }; 81 | 82 | // Structs 83 | struct BetterChatParams { 84 | bool antispam; 85 | bool chatfilter; 86 | int antispam_delay; 87 | bool nowrittenmsg; 88 | bool writtenmsgastoxic; 89 | int aftersavetime; 90 | bool owngoal; 91 | bool unwanted_pass; 92 | bool toxicityscores; 93 | }; 94 | 95 | struct StatTickerParams { 96 | uintptr_t Receiver; 97 | uintptr_t Victim; 98 | uintptr_t StatEvent; 99 | }; 100 | 101 | struct FHUDChatMessage 102 | { 103 | void* PRI; 104 | void* Team; 105 | wchar_t* PlayerName; 106 | uint8_t PlayerNamePadding[0x8]; 107 | wchar_t* Message; 108 | uint8_t MessagePadding[0x8]; 109 | uint8_t ChatChannel; 110 | unsigned long bPreset : 1; 111 | }; 112 | 113 | struct FString 114 | { 115 | public: 116 | using ElementType = const wchar_t; 117 | using ElementPointer = ElementType*; 118 | 119 | private: 120 | ElementPointer ArrayData; 121 | int32_t ArrayCount; 122 | int32_t ArrayMax; 123 | 124 | public: 125 | FString() 126 | { 127 | ArrayData = nullptr; 128 | ArrayCount = 0; 129 | ArrayMax = 0; 130 | } 131 | 132 | FString(ElementPointer other) 133 | { 134 | ArrayData = nullptr; 135 | ArrayCount = 0; 136 | ArrayMax = 0; 137 | 138 | ArrayMax = ArrayCount = *other ? (wcslen(other) + 1) : 0; 139 | 140 | if (ArrayCount > 0) 141 | { 142 | ArrayData = other; 143 | } 144 | } 145 | 146 | ~FString() {} 147 | 148 | public: 149 | std::string ToString() const 150 | { 151 | if (!IsValid()) 152 | { 153 | std::wstring wideStr(ArrayData); 154 | std::string str(wideStr.begin(), wideStr.end()); 155 | return str; 156 | } 157 | 158 | return std::string("null"); 159 | } 160 | 161 | bool IsValid() const 162 | { 163 | return !ArrayData; 164 | } 165 | 166 | FString operator=(ElementPointer other) 167 | { 168 | if (ArrayData != other) 169 | { 170 | ArrayMax = ArrayCount = *other ? (wcslen(other) + 1) : 0; 171 | 172 | if (ArrayCount > 0) 173 | { 174 | ArrayData = other; 175 | } 176 | } 177 | 178 | return *this; 179 | } 180 | 181 | bool operator==(const FString& other) 182 | { 183 | return (!wcscmp(ArrayData, other.ArrayData)); 184 | } 185 | }; 186 | 187 | FString FS(const std::string& s) { 188 | wchar_t* p = new wchar_t[s.size() + 1]; 189 | for (std::string::size_type i = 0; i < s.size(); ++i) 190 | p[i] = s[i]; 191 | 192 | p[s.size()] = '\0'; 193 | return FString(p); 194 | } 195 | 196 | struct FSceNpOnlineId 197 | { 198 | uint64_t Data[0x2]; // 0x0000 (0x0010) [0x0000000000000000] 199 | uint8_t Term; // 0x0010 (0x0001) [0x0000000000000000] 200 | uint8_t Dummy[0x3]; // 0x0011 (0x0003) [0x0000000000000000] 201 | }; 202 | 203 | struct FSceNpId 204 | { 205 | struct FSceNpOnlineId Handle; // 0x0000 (0x0018) [0x0000000000000002] (CPF_Const) 206 | uint64_t Opt; // 0x0018 (0x0008) [0x0000000000000002] (CPF_Const) 207 | uint64_t Reserved; // 0x0020 (0x0008) [0x0000000000000002] (CPF_Const) 208 | }; 209 | 210 | struct FUniqueNetId 211 | { 212 | uint64_t Uid; // 0x0000 (0x0008) [0x0000000000000000] 213 | struct FSceNpId NpId; // 0x0008 (0x0028) [0x0000000000000000] 214 | struct FString EpicAccountId; // 0x0030 (0x0010) [0x0000000000400000] (CPF_NeedCtorLink) 215 | uint8_t Platform; // 0x0040 (0x0001) [0x0000000000000000] 216 | uint8_t SplitscreenID; // 0x0041 (0x0001) [0x0000000000000000] 217 | }; 218 | 219 | struct FGFxChatMessage { 220 | int32_t Team; 221 | class FString PlayerName; 222 | class FString Message; 223 | uint8_t ChatChannel; 224 | bool bLocalPlayer : 1; 225 | struct FUniqueNetId SenderID; 226 | uint8_t MessageType; 227 | class FString TimeStamp; 228 | }; 229 | 230 | //Functions 231 | 232 | void resetWhitelist(); 233 | 234 | // -- JSON functions -- 235 | void jsonFileExists(); 236 | void createConfigInJson(std::string configName); 237 | void deleteConfigInJson(std::string configName); 238 | std::list getConfigsListInJson(); 239 | std::map getConfigByGamemodeInJson(); 240 | void editConfigByGamemodeInJson(std::string gamemode, std::string config); 241 | std::map readMapInJson(std::string config, std::string category); 242 | void toggleQuickchatInJson(std::string config, std::string category, std::string idMsg); 243 | BetterChatParams getParamsInJson(std::string config); 244 | void editParamInJson(std::string config, std::string param, std::variant value); 245 | 246 | virtual void onLoad(); 247 | virtual void onUnload(); 248 | 249 | void handleMsg(bool cancel, std::string playerName); 250 | 251 | void onNewGame(); 252 | void setConfig(); 253 | void refreshConfig(); 254 | void gameBegin(); 255 | void gameEnd(); 256 | void chatMessageEvent(ActorWrapper caller, void* args); 257 | void onStatTickerMessage(ServerWrapper caller, void* args); 258 | void hitBall(CarWrapper car, void* params); 259 | void addKickoffMessages(); 260 | void onTimerUpdate(); 261 | void onGoal(); 262 | void onOvertimeStarted(); 263 | 264 | void gameDestroyed(); 265 | void ShowToxicityScores(CanvasWrapper canvas); 266 | 267 | // Interface 268 | void RenderSettings() override; 269 | std::string GetPluginName() override; 270 | void SetImGuiContext(uintptr_t ctx) override; 271 | }; -------------------------------------------------------------------------------- /IMGUI/imgui_searchablecombo.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "imgui_searchablecombo.h" 3 | #include "imgui_internal.h" 4 | 5 | static float CalcMaxPopupHeightFromItemCount(int items_count) 6 | { 7 | ImGuiContext& g = *GImGui; 8 | if (items_count <= 0) 9 | return FLT_MAX; 10 | return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); 11 | } 12 | 13 | /* Modified version of BeginCombo from imgui.cpp at line 9172, 14 | * to include a input field to be able to filter the combo values. */ 15 | bool ImGui::BeginSearchableCombo(const char* label, const char* preview_value, char* input, int input_size, const char* input_preview_value, ImGuiComboFlags flags) 16 | { 17 | // Always consume the SetNextWindowSizeConstraint() call in our early return paths 18 | ImGuiContext& g = *GImGui; 19 | bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0; 20 | g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; 21 | 22 | ImGuiWindow* window = GetCurrentWindow(); 23 | if (window->SkipItems) 24 | return false; 25 | 26 | IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together 27 | 28 | const ImGuiStyle& style = g.Style; 29 | const ImGuiID id = window->GetID(label); 30 | 31 | const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); 32 | const ImVec2 label_size = CalcTextSize(label, NULL, true); 33 | const float expected_w = CalcItemWidth(); 34 | const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w; 35 | const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); 36 | const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 37 | 38 | bool hovered, held; 39 | bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); 40 | bool popup_open = IsPopupOpen(id); 41 | 42 | const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 43 | const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); 44 | 45 | if (popup_open || (pressed || g.NavActivateId == id)) 46 | { 47 | ImGuiID activeId = g.ActiveId; 48 | if (popup_open) 49 | g.ActiveId = id; 50 | else 51 | g.NavInputId = id; 52 | InputTextEx(label, input_preview_value, input, input_size, frame_bb.Max - frame_bb.Min, ImGuiInputTextFlags_NoUndoRedo); 53 | g.ActiveId = activeId; 54 | if (!(flags & ImGuiComboFlags_NoArrowButton)) 55 | { 56 | ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 57 | ImU32 text_col = GetColorU32(ImGuiCol_Text); 58 | window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); 59 | if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) 60 | RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); 61 | } 62 | } 63 | else 64 | { 65 | ItemSize(total_bb, style.FramePadding.y); 66 | if (!ItemAdd(total_bb, id, &frame_bb)) 67 | return false; 68 | } 69 | 70 | if ((pressed || g.NavActivateId == id) && !popup_open) 71 | { 72 | if (window->DC.NavLayerCurrent == 0) 73 | window->NavLastIds[0] = id; 74 | OpenPopupEx(id); 75 | popup_open = true; 76 | } 77 | 78 | if (!popup_open) 79 | { 80 | RenderNavHighlight(frame_bb, id); 81 | if (!(flags & ImGuiComboFlags_NoPreview)) 82 | window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Left); 83 | if (!(flags & ImGuiComboFlags_NoArrowButton)) 84 | { 85 | ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 86 | ImU32 text_col = GetColorU32(ImGuiCol_Text); 87 | window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); 88 | if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) 89 | RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); 90 | } 91 | RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); 92 | 93 | if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) 94 | RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f)); 95 | if (label_size.x > 0) 96 | RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 97 | 98 | return false; 99 | } 100 | 101 | if (has_window_size_constraint) 102 | { 103 | g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; 104 | g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); 105 | } 106 | else 107 | { 108 | if ((flags & ImGuiComboFlags_HeightMask_) == 0) 109 | flags |= ImGuiComboFlags_HeightRegular; 110 | IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one 111 | int popup_max_height_in_items = -1; 112 | if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; 113 | else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; 114 | else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; 115 | SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); 116 | } 117 | 118 | char name[16]; 119 | ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth 120 | 121 | // Peak into expected window size so we can position it 122 | if (ImGuiWindow* popup_window = FindWindowByName(name)) 123 | if (popup_window->WasActive) 124 | { 125 | ImVec2 size_expected = CalcWindowExpectedSize(popup_window); 126 | if (flags & ImGuiComboFlags_PopupAlignLeft) 127 | popup_window->AutoPosLastDirection = ImGuiDir_Left; 128 | ImRect r_outer = GetWindowAllowedExtentRect(popup_window); 129 | ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); 130 | SetNextWindowPos(pos); 131 | } 132 | 133 | // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() 134 | ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; 135 | 136 | // Horizontally align ourselves with the framed text 137 | PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); 138 | bool ret = Begin(name, NULL, window_flags); 139 | PopStyleVar(); 140 | if (!ret) 141 | { 142 | EndPopup(); 143 | IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above 144 | return false; 145 | } 146 | 147 | return true; 148 | } 149 | 150 | 151 | // Just so you can end your BeginSearchableCombo with EndSearchableCombo. 152 | void ImGui::EndSearchableCombo() 153 | { 154 | EndCombo(); 155 | } 156 | 157 | 158 | /* Modified version of Combo from imgui.cpp at line 9343, 159 | * to include a input field to be able to filter the combo values. */ 160 | bool ImGui::SearchableCombo(const char* label, int* current_item, std::vector items, const char* default_preview_text, const char* input_preview_value, int popup_max_height_in_items) 161 | { 162 | ImGuiContext& g = *GImGui; 163 | 164 | const char* preview_text = NULL; 165 | if (*current_item >= (int)items.size()) 166 | *current_item = 0; 167 | if (*current_item >= 0 && *current_item < (int)items.size()) 168 | preview_text = items[*current_item].c_str(); 169 | else 170 | preview_text = default_preview_text; 171 | 172 | // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. 173 | if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) 174 | SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); 175 | 176 | const int input_size = 64; 177 | char input_buffer[input_size] = ""; 178 | if (!BeginSearchableCombo(label, preview_text, input_buffer, input_size, input_preview_value, ImGuiComboFlags_None)) 179 | return false; 180 | 181 | // Display items 182 | // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) 183 | int matched_items = 0; 184 | bool value_changed = false; 185 | for (int i = 0; i < (int)items.size(); i++) 186 | { 187 | char buffer[input_size] = ""; 188 | ImStrncpy(buffer, input_buffer, input_size); 189 | std::string input(buffer); 190 | std::string item = items[i]; 191 | 192 | std::transform(item.begin(), item.end(), item.begin(), 193 | [](unsigned char c) { return (unsigned char)std::tolower(c); }); 194 | std::transform(input.begin(), input.end(), input.begin(), 195 | [](unsigned char c) { return (unsigned char)std::tolower(c); }); 196 | 197 | if (item.find(input, 0) == std::string::npos) 198 | continue; 199 | 200 | matched_items++; 201 | PushID((void*)(intptr_t)i); 202 | const bool item_selected = (i == *current_item); 203 | const char* item_text = items[i].c_str(); 204 | if (Selectable(item_text, item_selected)) 205 | { 206 | value_changed = true; 207 | *current_item = i; 208 | } 209 | if (item_selected) 210 | SetItemDefaultFocus(); 211 | PopID(); 212 | } 213 | if (matched_items == 0) 214 | ImGui::Selectable("No maps found", false, ImGuiSelectableFlags_Disabled); 215 | 216 | EndSearchableCombo(); 217 | 218 | return value_changed; 219 | } -------------------------------------------------------------------------------- /fmt/src/os.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | // Disable bogus MSVC warnings. 9 | #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) 10 | # define _CRT_SECURE_NO_WARNINGS 11 | #endif 12 | #include "pch.h" 13 | #include "fmt/os.h" 14 | 15 | #include 16 | 17 | #if FMT_USE_FCNTL 18 | # include 19 | # include 20 | 21 | # ifndef _WIN32 22 | # include 23 | # else 24 | # ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | # endif 27 | # include 28 | # include 29 | 30 | # define O_CREAT _O_CREAT 31 | # define O_TRUNC _O_TRUNC 32 | 33 | # ifndef S_IRUSR 34 | # define S_IRUSR _S_IREAD 35 | # endif 36 | 37 | # ifndef S_IWUSR 38 | # define S_IWUSR _S_IWRITE 39 | # endif 40 | 41 | # ifdef __MINGW32__ 42 | # define _SH_DENYNO 0x40 43 | # endif 44 | # endif // _WIN32 45 | #endif // FMT_USE_FCNTL 46 | 47 | #ifdef _WIN32 48 | # include 49 | #endif 50 | 51 | #ifdef fileno 52 | # undef fileno 53 | #endif 54 | 55 | namespace { 56 | #ifdef _WIN32 57 | // Return type of read and write functions. 58 | using RWResult = int; 59 | 60 | // On Windows the count argument to read and write is unsigned, so convert 61 | // it from size_t preventing integer overflow. 62 | inline unsigned convert_rwcount(std::size_t count) { 63 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 64 | } 65 | #else 66 | // Return type of read and write functions. 67 | using RWResult = ssize_t; 68 | 69 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 70 | #endif 71 | } // namespace 72 | 73 | FMT_BEGIN_NAMESPACE 74 | 75 | #ifdef _WIN32 76 | detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { 77 | if (int error_code = convert(s)) { 78 | FMT_THROW(windows_error(error_code, 79 | "cannot convert string from UTF-16 to UTF-8")); 80 | } 81 | } 82 | 83 | int detail::utf16_to_utf8::convert(wstring_view s) { 84 | if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; 85 | int s_size = static_cast(s.size()); 86 | if (s_size == 0) { 87 | // WideCharToMultiByte does not support zero length, handle separately. 88 | buffer_.resize(1); 89 | buffer_[0] = 0; 90 | return 0; 91 | } 92 | 93 | int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, 94 | nullptr, nullptr); 95 | if (length == 0) return GetLastError(); 96 | buffer_.resize(length + 1); 97 | length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], 98 | length, nullptr, nullptr); 99 | if (length == 0) return GetLastError(); 100 | buffer_[length] = 0; 101 | return 0; 102 | } 103 | 104 | void windows_error::init(int err_code, string_view format_str, 105 | format_args args) { 106 | error_code_ = err_code; 107 | memory_buffer buffer; 108 | detail::format_windows_error(buffer, err_code, vformat(format_str, args)); 109 | std::runtime_error& base = *this; 110 | base = std::runtime_error(to_string(buffer)); 111 | } 112 | 113 | void detail::format_windows_error(detail::buffer& out, int error_code, 114 | string_view message) FMT_NOEXCEPT { 115 | FMT_TRY { 116 | wmemory_buffer buf; 117 | buf.resize(inline_buffer_size); 118 | for (;;) { 119 | wchar_t* system_message = &buf[0]; 120 | int result = FormatMessageW( 121 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, 122 | error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, 123 | static_cast(buf.size()), nullptr); 124 | if (result != 0) { 125 | utf16_to_utf8 utf8_message; 126 | if (utf8_message.convert(system_message) == ERROR_SUCCESS) { 127 | format_to(std::back_inserter(out), "{}: {}", message, utf8_message); 128 | return; 129 | } 130 | break; 131 | } 132 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 133 | break; // Can't get error message, report error code instead. 134 | buf.resize(buf.size() * 2); 135 | } 136 | } 137 | FMT_CATCH(...) {} 138 | format_error_code(out, error_code, message); 139 | } 140 | 141 | void report_windows_error(int error_code, 142 | fmt::string_view message) FMT_NOEXCEPT { 143 | report_error(detail::format_windows_error, error_code, message); 144 | } 145 | #endif // _WIN32 146 | 147 | buffered_file::~buffered_file() FMT_NOEXCEPT { 148 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 149 | report_system_error(errno, "cannot close file"); 150 | } 151 | 152 | buffered_file::buffered_file(cstring_view filename, cstring_view mode) { 153 | FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 154 | nullptr); 155 | if (!file_) 156 | FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); 157 | } 158 | 159 | void buffered_file::close() { 160 | if (!file_) return; 161 | int result = FMT_SYSTEM(fclose(file_)); 162 | file_ = nullptr; 163 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 164 | } 165 | 166 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 167 | #define FMT_ARGS 168 | 169 | int buffered_file::fileno() const { 170 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 171 | if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); 172 | return fd; 173 | } 174 | 175 | #if FMT_USE_FCNTL 176 | file::file(cstring_view path, int oflag) { 177 | int mode = S_IRUSR | S_IWUSR; 178 | # if defined(_WIN32) && !defined(__MINGW32__) 179 | fd_ = -1; 180 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 181 | # else 182 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 183 | # endif 184 | if (fd_ == -1) 185 | FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); 186 | } 187 | 188 | file::~file() FMT_NOEXCEPT { 189 | // Don't retry close in case of EINTR! 190 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 191 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 192 | report_system_error(errno, "cannot close file"); 193 | } 194 | 195 | void file::close() { 196 | if (fd_ == -1) return; 197 | // Don't retry close in case of EINTR! 198 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 199 | int result = FMT_POSIX_CALL(close(fd_)); 200 | fd_ = -1; 201 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 202 | } 203 | 204 | long long file::size() const { 205 | # ifdef _WIN32 206 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 207 | // is less than 0x0500 as is the case with some default MinGW builds. 208 | // Both functions support large file sizes. 209 | DWORD size_upper = 0; 210 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 211 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 212 | if (size_lower == INVALID_FILE_SIZE) { 213 | DWORD error = GetLastError(); 214 | if (error != NO_ERROR) 215 | FMT_THROW(windows_error(GetLastError(), "cannot get file size")); 216 | } 217 | unsigned long long long_size = size_upper; 218 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 219 | # else 220 | using Stat = struct stat; 221 | Stat file_stat = Stat(); 222 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 223 | FMT_THROW(system_error(errno, "cannot get file attributes")); 224 | static_assert(sizeof(long long) >= sizeof(file_stat.st_size), 225 | "return type of file::size is not large enough"); 226 | return file_stat.st_size; 227 | # endif 228 | } 229 | 230 | std::size_t file::read(void* buffer, std::size_t count) { 231 | RWResult result = 0; 232 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 233 | if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); 234 | return detail::to_unsigned(result); 235 | } 236 | 237 | std::size_t file::write(const void* buffer, std::size_t count) { 238 | RWResult result = 0; 239 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 240 | if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); 241 | return detail::to_unsigned(result); 242 | } 243 | 244 | file file::dup(int fd) { 245 | // Don't retry as dup doesn't return EINTR. 246 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 247 | int new_fd = FMT_POSIX_CALL(dup(fd)); 248 | if (new_fd == -1) 249 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); 250 | return file(new_fd); 251 | } 252 | 253 | void file::dup2(int fd) { 254 | int result = 0; 255 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 256 | if (result == -1) { 257 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", 258 | fd_, fd)); 259 | } 260 | } 261 | 262 | void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT { 263 | int result = 0; 264 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 265 | if (result == -1) ec = error_code(errno); 266 | } 267 | 268 | void file::pipe(file& read_end, file& write_end) { 269 | // Close the descriptors first to make sure that assignments don't throw 270 | // and there are no leaks. 271 | read_end.close(); 272 | write_end.close(); 273 | int fds[2] = {}; 274 | # ifdef _WIN32 275 | // Make the default pipe capacity same as on Linux 2.6.11+. 276 | enum { DEFAULT_CAPACITY = 65536 }; 277 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 278 | # else 279 | // Don't retry as the pipe function doesn't return EINTR. 280 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 281 | int result = FMT_POSIX_CALL(pipe(fds)); 282 | # endif 283 | if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); 284 | // The following assignments don't throw because read_fd and write_fd 285 | // are closed. 286 | read_end = file(fds[0]); 287 | write_end = file(fds[1]); 288 | } 289 | 290 | buffered_file file::fdopen(const char* mode) { 291 | // Don't retry as fdopen doesn't return EINTR. 292 | #if defined(__MINGW32__) && defined(_POSIX_) 293 | FILE* f = ::fdopen(fd_, mode); 294 | #else 295 | FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); 296 | #endif 297 | if (!f) 298 | FMT_THROW( 299 | system_error(errno, "cannot associate stream with file descriptor")); 300 | buffered_file bf(f); 301 | fd_ = -1; 302 | return bf; 303 | } 304 | 305 | long getpagesize() { 306 | # ifdef _WIN32 307 | SYSTEM_INFO si; 308 | GetSystemInfo(&si); 309 | return si.dwPageSize; 310 | # else 311 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 312 | if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); 313 | return size; 314 | # endif 315 | } 316 | #endif // FMT_USE_FCNTL 317 | FMT_END_NAMESPACE 318 | -------------------------------------------------------------------------------- /fmt/include/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental range support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | // 8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) 9 | // All Rights Reserved 10 | // {fmt} support for ranges, containers and types tuple interface. 11 | 12 | #ifndef FMT_RANGES_H_ 13 | #define FMT_RANGES_H_ 14 | 15 | #include 16 | #include 17 | 18 | #include "format.h" 19 | 20 | // output only up to N items from the range. 21 | #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT 22 | # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 23 | #endif 24 | 25 | FMT_BEGIN_NAMESPACE 26 | 27 | template struct formatting_base { 28 | template 29 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 30 | return ctx.begin(); 31 | } 32 | }; 33 | 34 | template 35 | struct formatting_range : formatting_base { 36 | static FMT_CONSTEXPR_DECL const size_t range_length_limit = 37 | FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the 38 | // range. 39 | Char prefix; 40 | Char delimiter; 41 | Char postfix; 42 | formatting_range() : prefix('{'), delimiter(','), postfix('}') {} 43 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 44 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 45 | }; 46 | 47 | template 48 | struct formatting_tuple : formatting_base { 49 | Char prefix; 50 | Char delimiter; 51 | Char postfix; 52 | formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} 53 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 54 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 55 | }; 56 | 57 | namespace detail { 58 | 59 | template 60 | OutputIterator copy(const RangeT& range, OutputIterator out) { 61 | for (auto it = range.begin(), end = range.end(); it != end; ++it) 62 | *out++ = *it; 63 | return out; 64 | } 65 | 66 | template 67 | OutputIterator copy(const char* str, OutputIterator out) { 68 | while (*str) *out++ = *str++; 69 | return out; 70 | } 71 | 72 | template 73 | OutputIterator copy(char ch, OutputIterator out) { 74 | *out++ = ch; 75 | return out; 76 | } 77 | 78 | /// Return true value if T has std::string interface, like std::string_view. 79 | template class is_like_std_string { 80 | template 81 | static auto check(U* p) 82 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); 83 | template static void check(...); 84 | 85 | public: 86 | static FMT_CONSTEXPR_DECL const bool value = 87 | is_string::value || !std::is_void(nullptr))>::value; 88 | }; 89 | 90 | template 91 | struct is_like_std_string> : std::true_type {}; 92 | 93 | template struct conditional_helper {}; 94 | 95 | template struct is_range_ : std::false_type {}; 96 | 97 | #if !FMT_MSC_VER || FMT_MSC_VER > 1800 98 | template 99 | struct is_range_< 100 | T, conditional_t().begin()), 102 | decltype(std::declval().end())>, 103 | void>> : std::true_type {}; 104 | #endif 105 | 106 | /// tuple_size and tuple_element check. 107 | template class is_tuple_like_ { 108 | template 109 | static auto check(U* p) -> decltype(std::tuple_size::value, int()); 110 | template static void check(...); 111 | 112 | public: 113 | static FMT_CONSTEXPR_DECL const bool value = 114 | !std::is_void(nullptr))>::value; 115 | }; 116 | 117 | // Check for integer_sequence 118 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 119 | template 120 | using integer_sequence = std::integer_sequence; 121 | template using index_sequence = std::index_sequence; 122 | template using make_index_sequence = std::make_index_sequence; 123 | #else 124 | template struct integer_sequence { 125 | using value_type = T; 126 | 127 | static FMT_CONSTEXPR size_t size() { return sizeof...(N); } 128 | }; 129 | 130 | template using index_sequence = integer_sequence; 131 | 132 | template 133 | struct make_integer_sequence : make_integer_sequence {}; 134 | template 135 | struct make_integer_sequence : integer_sequence {}; 136 | 137 | template 138 | using make_index_sequence = make_integer_sequence; 139 | #endif 140 | 141 | template 142 | void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { 143 | using std::get; 144 | // using free function get(T) now. 145 | const int _[] = {0, ((void)f(get(tup)), 0)...}; 146 | (void)_; // blocks warnings 147 | } 148 | 149 | template 150 | FMT_CONSTEXPR make_index_sequence::value> get_indexes( 151 | T const&) { 152 | return {}; 153 | } 154 | 155 | template void for_each(Tuple&& tup, F&& f) { 156 | const auto indexes = get_indexes(tup); 157 | for_each(indexes, std::forward(tup), std::forward(f)); 158 | } 159 | 160 | template ::type>::value)> 162 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 163 | return add_space ? " {}" : "{}"; 164 | } 165 | 166 | template ::type>::value)> 168 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 169 | return add_space ? " \"{}\"" : "\"{}\""; 170 | } 171 | 172 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { 173 | return add_space ? " \"{}\"" : "\"{}\""; 174 | } 175 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { 176 | return add_space ? L" \"{}\"" : L"\"{}\""; 177 | } 178 | 179 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { 180 | return add_space ? " '{}'" : "'{}'"; 181 | } 182 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { 183 | return add_space ? L" '{}'" : L"'{}'"; 184 | } 185 | 186 | } // namespace detail 187 | 188 | template struct is_tuple_like { 189 | static FMT_CONSTEXPR_DECL const bool value = 190 | detail::is_tuple_like_::value && !detail::is_range_::value; 191 | }; 192 | 193 | template 194 | struct formatter::value>> { 195 | private: 196 | // C++11 generic lambda for format() 197 | template struct format_each { 198 | template void operator()(const T& v) { 199 | if (i > 0) { 200 | if (formatting.add_prepostfix_space) { 201 | *out++ = ' '; 202 | } 203 | out = detail::copy(formatting.delimiter, out); 204 | } 205 | out = format_to(out, 206 | detail::format_str_quoted( 207 | (formatting.add_delimiter_spaces && i > 0), v), 208 | v); 209 | ++i; 210 | } 211 | 212 | formatting_tuple& formatting; 213 | size_t& i; 214 | typename std::add_lvalue_reference().out())>::type out; 216 | }; 217 | 218 | public: 219 | formatting_tuple formatting; 220 | 221 | template 222 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 223 | return formatting.parse(ctx); 224 | } 225 | 226 | template 227 | auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { 228 | auto out = ctx.out(); 229 | size_t i = 0; 230 | detail::copy(formatting.prefix, out); 231 | 232 | detail::for_each(values, format_each{formatting, i, out}); 233 | if (formatting.add_prepostfix_space) { 234 | *out++ = ' '; 235 | } 236 | detail::copy(formatting.postfix, out); 237 | 238 | return ctx.out(); 239 | } 240 | }; 241 | 242 | template struct is_range { 243 | static FMT_CONSTEXPR_DECL const bool value = 244 | detail::is_range_::value && !detail::is_like_std_string::value && 245 | !std::is_convertible>::value && 246 | !std::is_constructible, T>::value; 247 | }; 248 | 249 | template 250 | struct formatter::value>> { 252 | formatting_range formatting; 253 | 254 | template 255 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 256 | return formatting.parse(ctx); 257 | } 258 | 259 | template 260 | typename FormatContext::iterator format(const RangeT& values, 261 | FormatContext& ctx) { 262 | auto out = detail::copy(formatting.prefix, ctx.out()); 263 | size_t i = 0; 264 | auto it = values.begin(); 265 | auto end = values.end(); 266 | for (; it != end; ++it) { 267 | if (i > 0) { 268 | if (formatting.add_prepostfix_space) *out++ = ' '; 269 | out = detail::copy(formatting.delimiter, out); 270 | } 271 | out = format_to(out, 272 | detail::format_str_quoted( 273 | (formatting.add_delimiter_spaces && i > 0), *it), 274 | *it); 275 | if (++i > formatting.range_length_limit) { 276 | out = format_to(out, " ... "); 277 | break; 278 | } 279 | } 280 | if (formatting.add_prepostfix_space) *out++ = ' '; 281 | return detail::copy(formatting.postfix, out); 282 | } 283 | }; 284 | 285 | template struct tuple_arg_join : detail::view { 286 | const std::tuple& tuple; 287 | basic_string_view sep; 288 | 289 | tuple_arg_join(const std::tuple& t, basic_string_view s) 290 | : tuple{t}, sep{s} {} 291 | }; 292 | 293 | template 294 | struct formatter, Char> { 295 | template 296 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 297 | return ctx.begin(); 298 | } 299 | 300 | template 301 | typename FormatContext::iterator format( 302 | const tuple_arg_join& value, FormatContext& ctx) { 303 | return format(value, ctx, detail::make_index_sequence{}); 304 | } 305 | 306 | private: 307 | template 308 | typename FormatContext::iterator format( 309 | const tuple_arg_join& value, FormatContext& ctx, 310 | detail::index_sequence) { 311 | return format_args(value, ctx, std::get(value.tuple)...); 312 | } 313 | 314 | template 315 | typename FormatContext::iterator format_args( 316 | const tuple_arg_join&, FormatContext& ctx) { 317 | // NOTE: for compilers that support C++17, this empty function instantiation 318 | // can be replaced with a constexpr branch in the variadic overload. 319 | return ctx.out(); 320 | } 321 | 322 | template 323 | typename FormatContext::iterator format_args( 324 | const tuple_arg_join& value, FormatContext& ctx, 325 | const Arg& arg, const Args&... args) { 326 | using base = formatter::type, Char>; 327 | auto out = ctx.out(); 328 | out = base{}.format(arg, ctx); 329 | if (sizeof...(Args) > 0) { 330 | out = std::copy(value.sep.begin(), value.sep.end(), out); 331 | ctx.advance_to(out); 332 | return format_args(value, ctx, args...); 333 | } 334 | return out; 335 | } 336 | }; 337 | 338 | /** 339 | \rst 340 | Returns an object that formats `tuple` with elements separated by `sep`. 341 | 342 | **Example**:: 343 | 344 | std::tuple t = {1, 'a'}; 345 | fmt::print("{}", fmt::join(t, ", ")); 346 | // Output: "1, a" 347 | \endrst 348 | */ 349 | template 350 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 351 | string_view sep) { 352 | return {tuple, sep}; 353 | } 354 | 355 | template 356 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 357 | wstring_view sep) { 358 | return {tuple, sep}; 359 | } 360 | 361 | /** 362 | \rst 363 | Returns an object that formats `initializer_list` with elements separated by 364 | `sep`. 365 | 366 | **Example**:: 367 | 368 | fmt::print("{}", fmt::join({1, 2, 3}, ", ")); 369 | // Output: "1, 2, 3" 370 | \endrst 371 | */ 372 | template 373 | arg_join join(std::initializer_list list, 374 | string_view sep) { 375 | return join(std::begin(list), std::end(list), sep); 376 | } 377 | 378 | template 379 | arg_join join(std::initializer_list list, 380 | wstring_view sep) { 381 | return join(std::begin(list), std::end(list), sep); 382 | } 383 | 384 | FMT_END_NAMESPACE 385 | 386 | #endif // FMT_RANGES_H_ 387 | -------------------------------------------------------------------------------- /fmt/include/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #if defined(__MINGW32__) || defined(__CYGWIN__) 12 | // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. 13 | # undef __STRICT_ANSI__ 14 | #endif 15 | 16 | #include 17 | #include // for locale_t 18 | #include 19 | #include 20 | #include // for strtod_l 21 | 22 | #if defined __APPLE__ || defined(__FreeBSD__) 23 | # include // for LC_NUMERIC_MASK on OS X 24 | #endif 25 | 26 | #include "format.h" 27 | 28 | // UWP doesn't provide _pipe. 29 | #if FMT_HAS_INCLUDE("winapifamily.h") 30 | # include 31 | #endif 32 | #if FMT_HAS_INCLUDE("fcntl.h") && \ 33 | (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 34 | # include // for O_RDONLY 35 | # define FMT_USE_FCNTL 1 36 | #else 37 | # define FMT_USE_FCNTL 0 38 | #endif 39 | 40 | #ifndef FMT_POSIX 41 | # if defined(_WIN32) && !defined(__MINGW32__) 42 | // Fix warnings about deprecated symbols. 43 | # define FMT_POSIX(call) _##call 44 | # else 45 | # define FMT_POSIX(call) call 46 | # endif 47 | #endif 48 | 49 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 50 | #ifdef FMT_SYSTEM 51 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 52 | #else 53 | # define FMT_SYSTEM(call) ::call 54 | # ifdef _WIN32 55 | // Fix warnings about deprecated symbols. 56 | # define FMT_POSIX_CALL(call) ::_##call 57 | # else 58 | # define FMT_POSIX_CALL(call) ::call 59 | # endif 60 | #endif 61 | 62 | // Retries the expression while it evaluates to error_result and errno 63 | // equals to EINTR. 64 | #ifndef _WIN32 65 | # define FMT_RETRY_VAL(result, expression, error_result) \ 66 | do { \ 67 | (result) = (expression); \ 68 | } while ((result) == (error_result) && errno == EINTR) 69 | #else 70 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 71 | #endif 72 | 73 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 74 | 75 | FMT_BEGIN_NAMESPACE 76 | 77 | /** 78 | \rst 79 | A reference to a null-terminated string. It can be constructed from a C 80 | string or ``std::string``. 81 | 82 | You can use one of the following type aliases for common character types: 83 | 84 | +---------------+-----------------------------+ 85 | | Type | Definition | 86 | +===============+=============================+ 87 | | cstring_view | basic_cstring_view | 88 | +---------------+-----------------------------+ 89 | | wcstring_view | basic_cstring_view | 90 | +---------------+-----------------------------+ 91 | 92 | This class is most useful as a parameter type to allow passing 93 | different types of strings to a function, for example:: 94 | 95 | template 96 | std::string format(cstring_view format_str, const Args & ... args); 97 | 98 | format("{}", 42); 99 | format(std::string("{}"), 42); 100 | \endrst 101 | */ 102 | template class basic_cstring_view { 103 | private: 104 | const Char* data_; 105 | 106 | public: 107 | /** Constructs a string reference object from a C string. */ 108 | basic_cstring_view(const Char* s) : data_(s) {} 109 | 110 | /** 111 | \rst 112 | Constructs a string reference from an ``std::string`` object. 113 | \endrst 114 | */ 115 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 116 | 117 | /** Returns the pointer to a C string. */ 118 | const Char* c_str() const { return data_; } 119 | }; 120 | 121 | using cstring_view = basic_cstring_view; 122 | using wcstring_view = basic_cstring_view; 123 | 124 | // An error code. 125 | class error_code { 126 | private: 127 | int value_; 128 | 129 | public: 130 | explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} 131 | 132 | int get() const FMT_NOEXCEPT { return value_; } 133 | }; 134 | 135 | #ifdef _WIN32 136 | namespace detail { 137 | // A converter from UTF-16 to UTF-8. 138 | // It is only provided for Windows since other systems support UTF-8 natively. 139 | class utf16_to_utf8 { 140 | private: 141 | memory_buffer buffer_; 142 | 143 | public: 144 | utf16_to_utf8() {} 145 | FMT_API explicit utf16_to_utf8(wstring_view s); 146 | operator string_view() const { return string_view(&buffer_[0], size()); } 147 | size_t size() const { return buffer_.size() - 1; } 148 | const char* c_str() const { return &buffer_[0]; } 149 | std::string str() const { return std::string(&buffer_[0], size()); } 150 | 151 | // Performs conversion returning a system error code instead of 152 | // throwing exception on conversion error. This method may still throw 153 | // in case of memory allocation error. 154 | FMT_API int convert(wstring_view s); 155 | }; 156 | 157 | FMT_API void format_windows_error(buffer& out, int error_code, 158 | string_view message) FMT_NOEXCEPT; 159 | } // namespace detail 160 | 161 | /** A Windows error. */ 162 | class windows_error : public system_error { 163 | private: 164 | FMT_API void init(int error_code, string_view format_str, format_args args); 165 | 166 | public: 167 | /** 168 | \rst 169 | Constructs a :class:`fmt::windows_error` object with the description 170 | of the form 171 | 172 | .. parsed-literal:: 173 | **: ** 174 | 175 | where ** is the formatted message and ** is the 176 | system message corresponding to the error code. 177 | *error_code* is a Windows error code as given by ``GetLastError``. 178 | If *error_code* is not a valid error code such as -1, the system message 179 | will look like "error -1". 180 | 181 | **Example**:: 182 | 183 | // This throws a windows_error with the description 184 | // cannot open file 'madeup': The system cannot find the file specified. 185 | // or similar (system message may vary). 186 | const char *filename = "madeup"; 187 | LPOFSTRUCT of = LPOFSTRUCT(); 188 | HFILE file = OpenFile(filename, &of, OF_READ); 189 | if (file == HFILE_ERROR) { 190 | throw fmt::windows_error(GetLastError(), 191 | "cannot open file '{}'", filename); 192 | } 193 | \endrst 194 | */ 195 | template 196 | windows_error(int error_code, string_view message, const Args&... args) { 197 | init(error_code, message, make_format_args(args...)); 198 | } 199 | }; 200 | 201 | // Reports a Windows error without throwing an exception. 202 | // Can be used to report errors from destructors. 203 | FMT_API void report_windows_error(int error_code, 204 | string_view message) FMT_NOEXCEPT; 205 | #endif // _WIN32 206 | 207 | // A buffered file. 208 | class buffered_file { 209 | private: 210 | FILE* file_; 211 | 212 | friend class file; 213 | 214 | explicit buffered_file(FILE* f) : file_(f) {} 215 | 216 | public: 217 | buffered_file(const buffered_file&) = delete; 218 | void operator=(const buffered_file&) = delete; 219 | 220 | // Constructs a buffered_file object which doesn't represent any file. 221 | buffered_file() FMT_NOEXCEPT : file_(nullptr) {} 222 | 223 | // Destroys the object closing the file it represents if any. 224 | FMT_API ~buffered_file() FMT_NOEXCEPT; 225 | 226 | public: 227 | buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { 228 | other.file_ = nullptr; 229 | } 230 | 231 | buffered_file& operator=(buffered_file&& other) { 232 | close(); 233 | file_ = other.file_; 234 | other.file_ = nullptr; 235 | return *this; 236 | } 237 | 238 | // Opens a file. 239 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 240 | 241 | // Closes the file. 242 | FMT_API void close(); 243 | 244 | // Returns the pointer to a FILE object representing this file. 245 | FILE* get() const FMT_NOEXCEPT { return file_; } 246 | 247 | // We place parentheses around fileno to workaround a bug in some versions 248 | // of MinGW that define fileno as a macro. 249 | FMT_API int(fileno)() const; 250 | 251 | void vprint(string_view format_str, format_args args) { 252 | fmt::vprint(file_, format_str, args); 253 | } 254 | 255 | template 256 | inline void print(string_view format_str, const Args&... args) { 257 | vprint(format_str, make_format_args(args...)); 258 | } 259 | }; 260 | 261 | #if FMT_USE_FCNTL 262 | // A file. Closed file is represented by a file object with descriptor -1. 263 | // Methods that are not declared with FMT_NOEXCEPT may throw 264 | // fmt::system_error in case of failure. Note that some errors such as 265 | // closing the file multiple times will cause a crash on Windows rather 266 | // than an exception. You can get standard behavior by overriding the 267 | // invalid parameter handler with _set_invalid_parameter_handler. 268 | class file { 269 | private: 270 | int fd_; // File descriptor. 271 | 272 | // Constructs a file object with a given descriptor. 273 | explicit file(int fd) : fd_(fd) {} 274 | 275 | public: 276 | // Possible values for the oflag argument to the constructor. 277 | enum { 278 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 279 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 280 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. 281 | CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist. 282 | }; 283 | 284 | // Constructs a file object which doesn't represent any file. 285 | file() FMT_NOEXCEPT : fd_(-1) {} 286 | 287 | // Opens a file and constructs a file object representing this file. 288 | FMT_API file(cstring_view path, int oflag); 289 | 290 | public: 291 | file(const file&) = delete; 292 | void operator=(const file&) = delete; 293 | 294 | file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } 295 | 296 | file& operator=(file&& other) FMT_NOEXCEPT { 297 | close(); 298 | fd_ = other.fd_; 299 | other.fd_ = -1; 300 | return *this; 301 | } 302 | 303 | // Destroys the object closing the file it represents if any. 304 | FMT_API ~file() FMT_NOEXCEPT; 305 | 306 | // Returns the file descriptor. 307 | int descriptor() const FMT_NOEXCEPT { return fd_; } 308 | 309 | // Closes the file. 310 | FMT_API void close(); 311 | 312 | // Returns the file size. The size has signed type for consistency with 313 | // stat::st_size. 314 | FMT_API long long size() const; 315 | 316 | // Attempts to read count bytes from the file into the specified buffer. 317 | FMT_API size_t read(void* buffer, size_t count); 318 | 319 | // Attempts to write count bytes from the specified buffer to the file. 320 | FMT_API size_t write(const void* buffer, size_t count); 321 | 322 | // Duplicates a file descriptor with the dup function and returns 323 | // the duplicate as a file object. 324 | FMT_API static file dup(int fd); 325 | 326 | // Makes fd be the copy of this file descriptor, closing fd first if 327 | // necessary. 328 | FMT_API void dup2(int fd); 329 | 330 | // Makes fd be the copy of this file descriptor, closing fd first if 331 | // necessary. 332 | FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; 333 | 334 | // Creates a pipe setting up read_end and write_end file objects for reading 335 | // and writing respectively. 336 | FMT_API static void pipe(file& read_end, file& write_end); 337 | 338 | // Creates a buffered_file object associated with this file and detaches 339 | // this file object from the file. 340 | FMT_API buffered_file fdopen(const char* mode); 341 | }; 342 | 343 | // Returns the memory page size. 344 | long getpagesize(); 345 | 346 | class direct_buffered_file; 347 | 348 | template 349 | void print(direct_buffered_file& f, const S& format_str, 350 | const Args&... args); 351 | 352 | // A buffered file with a direct buffer access and no synchronization. 353 | class direct_buffered_file { 354 | private: 355 | file file_; 356 | 357 | enum { buffer_size = 4096 }; 358 | char buffer_[buffer_size]; 359 | int pos_; 360 | 361 | void flush() { 362 | if (pos_ == 0) return; 363 | file_.write(buffer_, pos_); 364 | pos_ = 0; 365 | } 366 | 367 | int free_capacity() const { return buffer_size - pos_; } 368 | 369 | public: 370 | direct_buffered_file(cstring_view path, int oflag) 371 | : file_(path, oflag), pos_(0) {} 372 | 373 | ~direct_buffered_file() { 374 | flush(); 375 | } 376 | 377 | void close() { 378 | flush(); 379 | file_.close(); 380 | } 381 | 382 | template 383 | friend void print(direct_buffered_file& f, const S& format_str, 384 | const Args&... args) { 385 | // We could avoid double buffering. 386 | auto buf = fmt::memory_buffer(); 387 | fmt::format_to(std::back_inserter(buf), format_str, args...); 388 | auto remaining_pos = 0; 389 | auto remaining_size = buf.size(); 390 | while (remaining_size > detail::to_unsigned(f.free_capacity())) { 391 | auto size = f.free_capacity(); 392 | memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size); 393 | f.pos_ += size; 394 | f.flush(); 395 | remaining_pos += size; 396 | remaining_size -= size; 397 | } 398 | memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size); 399 | f.pos_ += static_cast(remaining_size); 400 | } 401 | }; 402 | #endif // FMT_USE_FCNTL 403 | 404 | #ifdef FMT_LOCALE 405 | // A "C" numeric locale. 406 | class locale { 407 | private: 408 | # ifdef _WIN32 409 | using locale_t = _locale_t; 410 | 411 | static void freelocale(locale_t loc) { _free_locale(loc); } 412 | 413 | static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { 414 | return _strtod_l(nptr, endptr, loc); 415 | } 416 | # endif 417 | 418 | locale_t locale_; 419 | 420 | public: 421 | using type = locale_t; 422 | locale(const locale&) = delete; 423 | void operator=(const locale&) = delete; 424 | 425 | locale() { 426 | # ifndef _WIN32 427 | locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); 428 | # else 429 | locale_ = _create_locale(LC_NUMERIC, "C"); 430 | # endif 431 | if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); 432 | } 433 | ~locale() { freelocale(locale_); } 434 | 435 | type get() const { return locale_; } 436 | 437 | // Converts string to floating-point number and advances str past the end 438 | // of the parsed input. 439 | double strtod(const char*& str) const { 440 | char* end = nullptr; 441 | double result = strtod_l(str, &end, locale_); 442 | str = end; 443 | return result; 444 | } 445 | }; 446 | using Locale FMT_DEPRECATED_ALIAS = locale; 447 | #endif // FMT_LOCALE 448 | FMT_END_NAMESPACE 449 | 450 | #endif // FMT_OS_H_ 451 | -------------------------------------------------------------------------------- /BetterChatGUI.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "BetterChat.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | using json = nlohmann::json; 13 | 14 | bool newConfigButtonClicked = false; 15 | string newConfigName = ""; 16 | 17 | //Plugin Settings Window code here 18 | 19 | void BetterChat::SetImGuiContext(uintptr_t ctx) { 20 | ImGui::SetCurrentContext(reinterpret_cast(ctx)); 21 | } 22 | 23 | std::string BetterChat::GetPluginName() { 24 | return "BetterChat Plugin"; 25 | } 26 | 27 | // Render the plugin settings here 28 | // This will show up in bakkesmod when the plugin is loaded at 29 | // f2 -> plugins -> BetterChat 30 | void BetterChat::RenderSettings() { 31 | CVarWrapper enabledCvar = cvarManager->getCvar("betterchat_enabled"); 32 | if (!enabledCvar) { return; } 33 | bool enabled = enabledCvar.getBoolValue(); 34 | 35 | CVarWrapper toxicityScoreXCvar = cvarManager->getCvar("betterchat_score_X"); 36 | if (!toxicityScoreXCvar) { return; } 37 | int toxicityScoreX = toxicityScoreXCvar.getIntValue(); 38 | 39 | CVarWrapper toxicityScoreYCvar = cvarManager->getCvar("betterchat_score_Y"); 40 | if (!toxicityScoreYCvar) { return; } 41 | int toxicityScoreY = toxicityScoreYCvar.getIntValue(); 42 | 43 | list categories = { "quickchats" , "default", "beforeKickoff", "afterAlliedGoal", "afterEnemyGoal", "afterPass", "afterSave" }; 44 | 45 | // On/Off Plugin Button 46 | if (ImGui::Checkbox("Enable plugin", &enabled)) { 47 | enabledCvar.setValue(enabled); 48 | } 49 | if (ImGui::IsItemHovered()) { 50 | ImGui::SetTooltip("Enable/Disable BetterChat Plugin"); 51 | } 52 | 53 | if (enabled) 54 | { 55 | ImGui::Separator(); 56 | 57 | // Configuration by gamemode 58 | ImGui::Text("Gamemode configuration:"); 59 | ImGui::Text(""); 60 | 61 | list configsSet = getConfigsListInJson(); 62 | const char** configsArray = new const char* [configsSet.size()]; 63 | configsArray[0] = "Default config"; 64 | int i = 1; 65 | for (const string& config : configsSet) { 66 | if (config != "Default config") { 67 | configsArray[i] = config.c_str(); 68 | i++; 69 | } 70 | } 71 | 72 | map configByGamemode = getConfigByGamemodeInJson(); 73 | map configIndexByGamemode; 74 | for (auto& pair : configByGamemode) { 75 | configIndexByGamemode[pair.first] = 0; 76 | for (int i = 1; i < configsSet.size(); i++) { 77 | if (pair.second == configsArray[i]) { 78 | configIndexByGamemode[pair.first] = i; 79 | break; 80 | } 81 | } 82 | } 83 | 84 | // Configurations combo for each gamemode 85 | ImGui::BeginGroup(); 86 | ImGui::Columns(2, nullptr, false); 87 | ImGui::SetColumnWidth(-1, 100); 88 | float textOffset; 89 | 90 | for (auto& pair : configIndexByGamemode) { 91 | ImGui::PushID(pair.first.c_str()); 92 | textOffset = (ImGui::GetColumnWidth() - ImGui::CalcTextSize(pair.first.c_str()).x) * 0.5f; 93 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + textOffset - 6); 94 | ImGui::Text(pair.first.c_str()); 95 | ImGui::NextColumn(); 96 | ImGui::PushItemWidth(300); 97 | if (ImGui::Combo("", &pair.second, configsArray, configsSet.size())) { 98 | editConfigByGamemodeInJson(pair.first, configsArray[pair.second]); 99 | LOG("'" + pair.first + "' gamemode is now using '" + configsArray[pair.second] + "' config."); 100 | if (gameWrapper->IsInOnlineGame()) { 101 | refreshConfig(); 102 | resetWhitelist(); 103 | } 104 | } 105 | ImGui::PopItemWidth(); 106 | ImGui::PopID(); 107 | ImGui::NextColumn(); 108 | } 109 | ImGui::Columns(1, nullptr); 110 | ImGui::Dummy(ImVec2(0, 10)); 111 | ImGui::EndGroup(); 112 | 113 | ImGui::SameLine(0, 600); 114 | 115 | // New config button 116 | ImGui::BeginGroup(); 117 | ImGui::Text("\n"); 118 | if (ImGui::Button("New config", ImVec2(175, 60))) { 119 | newConfigButtonClicked = !newConfigButtonClicked; 120 | } 121 | if (newConfigButtonClicked) { 122 | ImGui::PushItemWidth(175); 123 | ImGui::Text("Config name:"); 124 | ImGui::InputText("", &newConfigName); 125 | ImGui::PopItemWidth(); 126 | ImGui::SameLine(); 127 | if (ImGui::Button("Create")) { 128 | if (newConfigName != "" && newConfigName != "ConfigByGamemode" && find(configsSet.begin(), configsSet.end(), newConfigName) == configsSet.end()) { 129 | createConfigInJson(newConfigName); 130 | LOG("'" + newConfigName + "' config has been created."); 131 | newConfigButtonClicked = false; 132 | newConfigName = ""; 133 | } 134 | else { 135 | ImGui::OpenPopup("Error"); 136 | } 137 | } 138 | } 139 | ImGui::EndGroup(); 140 | 141 | ImGui::Separator(); 142 | 143 | ImGui::Text("\n"); 144 | 145 | // Configurations tabs 146 | if (ImGui::BeginTabBar("tab_bar")) { 147 | for (const string& config : configsSet) { 148 | 149 | if (ImGui::BeginTabItem(config.c_str())) { 150 | ImGui::PushID(config.c_str()); 151 | 152 | BetterChatParams pluginParams = getParamsInJson(config); 153 | 154 | map defaultCheck = readMapInJson(config, "default"); 155 | map beforeKickoffCheck = readMapInJson(config, "beforeKickoff"); 156 | map afterAlliedGoalCheck = readMapInJson(config, "afterAlliedGoal"); 157 | map afterEnemyGoalCheck = readMapInJson(config, "afterEnemyGoal"); 158 | map afterPassCheck = readMapInJson(config, "afterPass"); 159 | map afterSaveCheck = readMapInJson(config, "afterSave"); 160 | 161 | map> maps = { {"default", defaultCheck}, {"beforeKickoff", beforeKickoffCheck}, { "afterAlliedGoal", afterAlliedGoalCheck }, {"afterEnemyGoal", afterEnemyGoalCheck}, {"afterPass", afterPassCheck}, {"afterSave", afterSaveCheck} }; 162 | 163 | // AntiSpam button 164 | if (ImGui::Checkbox("AntiSpam", &pluginParams.antispam)) { 165 | editParamInJson(config, "antispam", pluginParams.antispam); 166 | } 167 | if (ImGui::IsItemHovered()) { 168 | ImGui::SetTooltip("Enable/Disable AntiSpam"); 169 | } 170 | 171 | // AntiSpam delay slider 172 | if (pluginParams.antispam) { 173 | ImGui::PushItemWidth(500); 174 | if (ImGui::SliderInt("Delay", &pluginParams.antispam_delay, 0, 10)) { 175 | editParamInJson(config, "antispam_delay", pluginParams.antispam_delay); 176 | } 177 | ImGui::PopItemWidth(); 178 | if (ImGui::IsItemHovered()) { 179 | std::string hoverText = "Delay between two similar messages : " + std::to_string(pluginParams.antispam_delay) + " seconds"; 180 | ImGui::SetTooltip(hoverText.c_str()); 181 | } 182 | } 183 | else { 184 | ImGui::Text("\n\n"); 185 | } 186 | 187 | // Message Filter Button 188 | ImGui::Text("\n"); 189 | if (ImGui::Checkbox("Message Filter", &pluginParams.chatfilter)) { 190 | editParamInJson(config, "chatfilter", pluginParams.chatfilter); 191 | } 192 | if (ImGui::IsItemHovered()) { 193 | ImGui::SetTooltip("Enable/Disable Message Filter"); 194 | } 195 | 196 | // Message Filter Table 197 | if (pluginParams.chatfilter) { 198 | ImGui::Text("\n"); 199 | 200 | // Menu Bar 201 | ImGui::BeginChild("Config Table", ImVec2(775, 450), true, ImGuiWindowFlags_MenuBar); 202 | if (ImGui::BeginMenuBar()) 203 | { 204 | ImGui::Text("Configuration"); 205 | ImGui::EndMenuBar(); 206 | } 207 | 208 | // Headers child 209 | ImGui::BeginChild("Headers", ImVec2(0, 35), false); 210 | 211 | ImGui::Columns(7, nullptr); 212 | 213 | float headerOffsetX; 214 | float headerOffsetY = ImGui::GetTextLineHeight() * 0.5f + 2; 215 | 216 | ImGui::SetColumnWidth(-1, 150); 217 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("Quickchats").x) * 0.5f; 218 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 219 | ImGui::SetCursorPosY(ImGui::GetCursorPosY() + headerOffsetY); 220 | ImGui::Text("Quickchats"); 221 | ImGui::NextColumn(); 222 | ImGui::SetColumnWidth(-1, 100); 223 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("Default").x) * 0.5f; 224 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 225 | ImGui::SetCursorPosY(ImGui::GetCursorPosY() + headerOffsetY); 226 | ImGui::Text("Default"); 227 | ImGui::NextColumn(); 228 | ImGui::SetColumnWidth(-1, 100); 229 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("Before kickoff").x) * 0.5f; 230 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 231 | ImGui::SetCursorPosY(ImGui::GetCursorPosY() + headerOffsetY); 232 | ImGui::Text("Before kickoff"); 233 | ImGui::NextColumn(); 234 | ImGui::SetColumnWidth(-1, 100); 235 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("After an allied").x) * 0.5f; 236 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 237 | ImGui::Text("After an allied"); 238 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("goal").x) * 0.5f; 239 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 240 | ImGui::Text("goal"); 241 | ImGui::NextColumn(); 242 | ImGui::SetColumnWidth(-1, 100); 243 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("After an enemy").x) * 0.5f; 244 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 245 | ImGui::Text("After an enemy"); 246 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("goal").x) * 0.5f; 247 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 248 | ImGui::Text("goal"); 249 | ImGui::NextColumn(); 250 | ImGui::SetColumnWidth(-1, 100); 251 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("After an assist").x) * 0.5f; 252 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 253 | ImGui::SetCursorPosY(ImGui::GetCursorPosY() + headerOffsetY); 254 | ImGui::Text("After an assist"); 255 | ImGui::NextColumn(); 256 | ImGui::SetColumnWidth(-1, 100); 257 | headerOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize("After a save").x) * 0.5f; 258 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + headerOffsetX - 6); 259 | ImGui::SetCursorPosY(ImGui::GetCursorPosY() + headerOffsetY); 260 | ImGui::Text("After a save"); 261 | 262 | ImGui::Columns(1, nullptr); 263 | ImGui::EndChild(); 264 | 265 | ImGui::Separator(); 266 | ImGui::Dummy(ImVec2(0, 3)); 267 | 268 | // Table content 269 | 270 | ImGui::BeginChild("TableContent", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar); 271 | ImGui::Columns(7, nullptr); 272 | 273 | for (const auto& chat : idQuickchats) { 274 | for (const string column : categories) { 275 | ImGui::PushID(column.c_str()); 276 | if (column == "quickchats") { 277 | ImGui::SetColumnWidth(-1, 150); 278 | float textOffsetX = (ImGui::GetColumnWidth() - ImGui::CalcTextSize(chat.second.c_str()).x) * 0.5f; 279 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + textOffsetX - 6); 280 | ImGui::Text(chat.second.c_str()); 281 | } 282 | else { 283 | ImGui::SetColumnWidth(-1, 100); 284 | const string msg = chat.first; 285 | map map = maps[column]; 286 | bool check = map[msg]; 287 | string btn = check ? "Allowed" : "Forbidden"; 288 | if (check) { 289 | ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.8f, 0.2f, 1.0f)); 290 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.9f, 0.3f, 1.0f)); 291 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.7f, 0.1f, 1.0f)); 292 | } 293 | else { 294 | ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f)); 295 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f)); 296 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.7f, 0.1f, 0.1f, 1.0f)); 297 | } 298 | 299 | ImGui::PushID(msg.c_str()); 300 | float btnOffsetX = (ImGui::GetColumnWidth() - 80) * 0.5f; 301 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + btnOffsetX - 6); 302 | if (ImGui::Button(btn.c_str(), ImVec2(80, 18))) { 303 | toggleQuickchatInJson(config, column, msg); 304 | if (gameWrapper->IsInOnlineGame()) { 305 | resetWhitelist(); 306 | } 307 | } 308 | ImGui::PopID(); 309 | ImGui::PopStyleColor(3); 310 | } 311 | ImGui::PopID(); 312 | ImGui::NextColumn(); 313 | } 314 | } 315 | ImGui::Columns(1, nullptr); 316 | ImGui::EndChild(); 317 | 318 | ImGui::EndChild(); 319 | 320 | // Message filter options 321 | ImGui::Text("\nMessage filter options:"); 322 | if (ImGui::Checkbox("Block written messages", &pluginParams.nowrittenmsg)) { 323 | editParamInJson(config, "nowrittenmsg", pluginParams.nowrittenmsg); 324 | } 325 | if (ImGui::Checkbox("Count written messages in the toxicity scores", &pluginParams.writtenmsgastoxic)) { 326 | editParamInJson(config, "writtenmsgastoxic", pluginParams.writtenmsgastoxic); 327 | } 328 | if (ImGui::IsItemHovered()) { 329 | ImGui::SetTooltip("If enabled, written messages will be considered as toxic if 'Block written messages' option is enabled, and as normal if not."); 330 | } 331 | ImGui::PushItemWidth(500); 332 | if (ImGui::SliderInt("Time during which 'after a save' messages are allowed after a save.", &pluginParams.aftersavetime, 0, 20)) { 333 | editParamInJson(config, "aftersavetime", pluginParams.aftersavetime); 334 | } 335 | ImGui::PopItemWidth(); 336 | if (ImGui::Checkbox("Do not count a goal if it is an owngoal", &pluginParams.owngoal)) { 337 | editParamInJson(config, "owngoal", pluginParams.owngoal); 338 | } 339 | if (ImGui::Checkbox("Do not count a pass if an opponent touch it", &pluginParams.unwanted_pass)) { 340 | editParamInJson(config, "unwanted_pass", pluginParams.unwanted_pass); 341 | } 342 | } 343 | 344 | // Toxicity scores 345 | if (pluginParams.antispam || pluginParams.chatfilter) { 346 | ImGui::Text("\n"); 347 | if (ImGui::Checkbox("Toxicity scores", &pluginParams.toxicityscores)) { 348 | editParamInJson(config, "toxicityscores", pluginParams.toxicityscores); 349 | } 350 | if (ImGui::IsItemHovered()) { 351 | ImGui::SetTooltip("Enable/Disable Toxicity Scores (at the end of the game)"); 352 | } 353 | } 354 | 355 | // Delete config button 356 | if (config != "Default config") { 357 | ImGui::Text("\n"); 358 | ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f)); 359 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f)); 360 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.7f, 0.1f, 0.1f, 1.0f)); 361 | if (ImGui::Button("Delete config")) { 362 | for (auto& pair : configByGamemode) { 363 | if (pair.second == config) { 364 | editConfigByGamemodeInJson(pair.first, "Default config"); 365 | LOG("'" + pair.first + "' gamemode is now using 'Default config' config."); 366 | } 367 | } 368 | if (gameWrapper->IsInOnlineGame()) { 369 | refreshConfig(); 370 | resetWhitelist(); 371 | } 372 | deleteConfigInJson(config); 373 | LOG("'" + config + "' config has been deleted."); 374 | } 375 | ImGui::PopStyleColor(3); 376 | } 377 | ImGui::EndTabItem(); 378 | ImGui::PopID(); 379 | } 380 | } 381 | ImGui::EndTabBar(); 382 | } 383 | ImGui::Separator(); 384 | 385 | // Toxicity scores position 386 | ImGui::Text("Toxicity scores options:"); 387 | if (ImGui::SliderInt("X", &toxicityScoreX, 0, 1920)) { 388 | toxicityScoreXCvar.setValue(toxicityScoreX); 389 | } 390 | 391 | if (ImGui::SliderInt("Y", &toxicityScoreY, 0, 1080)) { 392 | toxicityScoreYCvar.setValue(toxicityScoreY); 393 | } 394 | } 395 | 396 | ImGui::Text("\n\n"); 397 | 398 | ImGui::Separator(); 399 | 400 | ImGui::Text("Plugin version: %s", plugin_version); 401 | 402 | if (ImGui::BeginPopupModal("Error", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { 403 | ImGui::Text("This config name is invalid\nor already exists."); 404 | if (ImGui::Button("OK", ImVec2(50, 0))) { ImGui::CloseCurrentPopup(); } 405 | ImGui::EndPopup(); 406 | } 407 | } -------------------------------------------------------------------------------- /IMGUI/imgui_impl_win32.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | // dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) 3 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) 7 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 8 | // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). 9 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 10 | 11 | #include "imgui.h" 12 | #include "imgui_impl_win32.h" 13 | #ifndef WIN32_LEAN_AND_MEAN 14 | #define WIN32_LEAN_AND_MEAN 15 | #endif 16 | #include 17 | #include 18 | 19 | // Using XInput library for gamepad (with recent Windows SDK this may leads to executables which won't run on Windows 7) 20 | #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 21 | #include 22 | #else 23 | #define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT 24 | #endif 25 | #if defined(_MSC_VER) && !defined(IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT) 26 | #pragma comment(lib, "xinput") 27 | //#pragma comment(lib, "Xinput9_1_0") 28 | #endif 29 | 30 | // CHANGELOG 31 | // (minor and older changes stripped away, please see git history for details) 32 | // 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT. 33 | // 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor. 34 | // 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter(). 35 | // 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent. 36 | // 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages. 37 | // 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application). 38 | // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. 39 | // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. 40 | // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). 41 | // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. 42 | // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. 43 | // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). 44 | // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. 45 | // 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). 46 | // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. 47 | // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. 48 | // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. 49 | // 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. 50 | // 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. 51 | // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. 52 | // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. 53 | 54 | // Win32 Data 55 | static HWND g_hWnd = NULL; 56 | static INT64 g_Time = 0; 57 | static INT64 g_TicksPerSecond = 0; 58 | static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; 59 | static bool g_HasGamepad = false; 60 | static bool g_WantUpdateHasGamepad = true; 61 | 62 | // Functions 63 | bool ImGui_ImplWin32_Init(void* hwnd) 64 | { 65 | if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond)) 66 | return false; 67 | if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time)) 68 | return false; 69 | 70 | // Setup back-end capabilities flags 71 | g_hWnd = (HWND)hwnd; 72 | ImGuiIO& io = ImGui::GetIO(); 73 | io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) 74 | io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 75 | io.BackendPlatformName = "imgui_impl_win32"; 76 | io.ImeWindowHandle = hwnd; 77 | 78 | // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. 79 | io.KeyMap[ImGuiKey_Tab] = VK_TAB; 80 | io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; 81 | io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; 82 | io.KeyMap[ImGuiKey_UpArrow] = VK_UP; 83 | io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; 84 | io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; 85 | io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; 86 | io.KeyMap[ImGuiKey_Home] = VK_HOME; 87 | io.KeyMap[ImGuiKey_End] = VK_END; 88 | io.KeyMap[ImGuiKey_Insert] = VK_INSERT; 89 | io.KeyMap[ImGuiKey_Delete] = VK_DELETE; 90 | io.KeyMap[ImGuiKey_Backspace] = VK_BACK; 91 | io.KeyMap[ImGuiKey_Space] = VK_SPACE; 92 | io.KeyMap[ImGuiKey_Enter] = VK_RETURN; 93 | io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; 94 | io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN; 95 | io.KeyMap[ImGuiKey_A] = 'A'; 96 | io.KeyMap[ImGuiKey_C] = 'C'; 97 | io.KeyMap[ImGuiKey_V] = 'V'; 98 | io.KeyMap[ImGuiKey_X] = 'X'; 99 | io.KeyMap[ImGuiKey_Y] = 'Y'; 100 | io.KeyMap[ImGuiKey_Z] = 'Z'; 101 | 102 | return true; 103 | } 104 | 105 | void ImGui_ImplWin32_Shutdown() 106 | { 107 | g_hWnd = (HWND)0; 108 | } 109 | 110 | static bool ImGui_ImplWin32_UpdateMouseCursor() 111 | { 112 | ImGuiIO& io = ImGui::GetIO(); 113 | if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) 114 | return false; 115 | 116 | ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); 117 | if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) 118 | { 119 | // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor 120 | ::SetCursor(NULL); 121 | } 122 | else 123 | { 124 | // Show OS mouse cursor 125 | LPTSTR win32_cursor = IDC_ARROW; 126 | switch (imgui_cursor) 127 | { 128 | case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; 129 | case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; 130 | case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; 131 | case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; 132 | case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; 133 | case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; 134 | case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; 135 | case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; 136 | case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; 137 | } 138 | ::SetCursor(::LoadCursor(NULL, win32_cursor)); 139 | } 140 | return true; 141 | } 142 | 143 | static void ImGui_ImplWin32_UpdateMousePos() 144 | { 145 | ImGuiIO& io = ImGui::GetIO(); 146 | 147 | // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) 148 | if (io.WantSetMousePos) 149 | { 150 | POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; 151 | ::ClientToScreen(g_hWnd, &pos); 152 | ::SetCursorPos(pos.x, pos.y); 153 | } 154 | 155 | // Set mouse position 156 | io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 157 | POINT pos; 158 | if (HWND active_window = ::GetForegroundWindow()) 159 | if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) 160 | if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) 161 | io.MousePos = ImVec2((float)pos.x, (float)pos.y); 162 | } 163 | 164 | // Gamepad navigation mapping 165 | static void ImGui_ImplWin32_UpdateGamepads() 166 | { 167 | #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 168 | ImGuiIO& io = ImGui::GetIO(); 169 | memset(io.NavInputs, 0, sizeof(io.NavInputs)); 170 | if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) 171 | return; 172 | 173 | // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. 174 | // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. 175 | if (g_WantUpdateHasGamepad) 176 | { 177 | XINPUT_CAPABILITIES caps; 178 | g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); 179 | g_WantUpdateHasGamepad = false; 180 | } 181 | 182 | XINPUT_STATE xinput_state; 183 | io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; 184 | if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) 185 | { 186 | const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; 187 | io.BackendFlags |= ImGuiBackendFlags_HasGamepad; 188 | 189 | #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } 190 | #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } 191 | MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A 192 | MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B 193 | MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X 194 | MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y 195 | MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left 196 | MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right 197 | MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up 198 | MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down 199 | MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB 200 | MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB 201 | MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB 202 | MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB 203 | MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); 204 | MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); 205 | MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); 206 | MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); 207 | #undef MAP_BUTTON 208 | #undef MAP_ANALOG 209 | } 210 | #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 211 | } 212 | 213 | void ImGui_ImplWin32_NewFrame() 214 | { 215 | ImGuiIO& io = ImGui::GetIO(); 216 | IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); 217 | 218 | // Setup display size (every frame to accommodate for window resizing) 219 | RECT rect; 220 | ::GetClientRect(g_hWnd, &rect); 221 | io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); 222 | 223 | // Setup time step 224 | INT64 current_time; 225 | ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); 226 | io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; 227 | g_Time = current_time; 228 | 229 | // Read keyboard modifiers inputs 230 | io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; 231 | io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; 232 | io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; 233 | io.KeySuper = false; 234 | // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. 235 | 236 | // Update OS mouse position 237 | ImGui_ImplWin32_UpdateMousePos(); 238 | 239 | // Update OS mouse cursor with the cursor requested by imgui 240 | ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); 241 | if (g_LastMouseCursor != mouse_cursor) 242 | { 243 | g_LastMouseCursor = mouse_cursor; 244 | ImGui_ImplWin32_UpdateMouseCursor(); 245 | } 246 | 247 | // Update game controllers (if enabled and available) 248 | ImGui_ImplWin32_UpdateGamepads(); 249 | } 250 | 251 | // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. 252 | #ifndef WM_MOUSEHWHEEL 253 | #define WM_MOUSEHWHEEL 0x020E 254 | #endif 255 | #ifndef DBT_DEVNODES_CHANGED 256 | #define DBT_DEVNODES_CHANGED 0x0007 257 | #endif 258 | 259 | // Process Win32 mouse/keyboard inputs. 260 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. 261 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. 262 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. 263 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. 264 | // PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds. 265 | // PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. 266 | IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 267 | { 268 | if (ImGui::GetCurrentContext() == NULL) 269 | return 0; 270 | 271 | ImGuiIO& io = ImGui::GetIO(); 272 | switch (msg) 273 | { 274 | case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: 275 | case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: 276 | case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: 277 | case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: 278 | { 279 | int button = 0; 280 | if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } 281 | if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } 282 | if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } 283 | if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } 284 | if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) 285 | ::SetCapture(hwnd); 286 | io.MouseDown[button] = true; 287 | return 0; 288 | } 289 | case WM_LBUTTONUP: 290 | case WM_RBUTTONUP: 291 | case WM_MBUTTONUP: 292 | case WM_XBUTTONUP: 293 | { 294 | int button = 0; 295 | if (msg == WM_LBUTTONUP) { button = 0; } 296 | if (msg == WM_RBUTTONUP) { button = 1; } 297 | if (msg == WM_MBUTTONUP) { button = 2; } 298 | if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } 299 | io.MouseDown[button] = false; 300 | if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) 301 | ::ReleaseCapture(); 302 | return 0; 303 | } 304 | case WM_MOUSEWHEEL: 305 | io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; 306 | return 0; 307 | case WM_MOUSEHWHEEL: 308 | io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; 309 | return 0; 310 | case WM_KEYDOWN: 311 | case WM_SYSKEYDOWN: 312 | if (wParam < 256) 313 | io.KeysDown[wParam] = 1; 314 | return 0; 315 | case WM_KEYUP: 316 | case WM_SYSKEYUP: 317 | if (wParam < 256) 318 | io.KeysDown[wParam] = 0; 319 | return 0; 320 | case WM_CHAR: 321 | // You can also use ToAscii()+GetKeyboardState() to retrieve characters. 322 | io.AddInputCharacter((unsigned int)wParam); 323 | return 0; 324 | case WM_SETCURSOR: 325 | if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) 326 | return 1; 327 | return 0; 328 | case WM_DEVICECHANGE: 329 | if ((UINT)wParam == DBT_DEVNODES_CHANGED) 330 | g_WantUpdateHasGamepad = true; 331 | return 0; 332 | } 333 | return 0; 334 | } 335 | -------------------------------------------------------------------------------- /IMGUI/imstb_rectpack.h: -------------------------------------------------------------------------------- 1 | // [DEAR IMGUI] 2 | // This is a slightly modified version of stb_rect_pack.h 1.00. 3 | // Those changes would need to be pushed into nothings/stb: 4 | // - Added STBRP__CDECL 5 | // Grep for [DEAR IMGUI] to find the changes. 6 | 7 | // stb_rect_pack.h - v1.00 - public domain - rectangle packing 8 | // Sean Barrett 2014 9 | // 10 | // Useful for e.g. packing rectangular textures into an atlas. 11 | // Does not do rotation. 12 | // 13 | // Not necessarily the awesomest packing method, but better than 14 | // the totally naive one in stb_truetype (which is primarily what 15 | // this is meant to replace). 16 | // 17 | // Has only had a few tests run, may have issues. 18 | // 19 | // More docs to come. 20 | // 21 | // No memory allocations; uses qsort() and assert() from stdlib. 22 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 23 | // 24 | // This library currently uses the Skyline Bottom-Left algorithm. 25 | // 26 | // Please note: better rectangle packers are welcome! Please 27 | // implement them to the same API, but with a different init 28 | // function. 29 | // 30 | // Credits 31 | // 32 | // Library 33 | // Sean Barrett 34 | // Minor features 35 | // Martins Mozeiko 36 | // github:IntellectualKitty 37 | // 38 | // Bugfixes / warning fixes 39 | // Jeremy Jaussaud 40 | // Fabian Giesen 41 | // 42 | // Version history: 43 | // 44 | // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles 45 | // 0.99 (2019-02-07) warning fixes 46 | // 0.11 (2017-03-03) return packing success/fail result 47 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 48 | // 0.09 (2016-08-27) fix compiler warnings 49 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 50 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 51 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 52 | // 0.05: added STBRP_ASSERT to allow replacing assert 53 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 54 | // 0.01: initial release 55 | // 56 | // LICENSE 57 | // 58 | // See end of file for license information. 59 | 60 | ////////////////////////////////////////////////////////////////////////////// 61 | // 62 | // INCLUDE SECTION 63 | // 64 | 65 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 66 | #define STB_INCLUDE_STB_RECT_PACK_H 67 | 68 | #define STB_RECT_PACK_VERSION 1 69 | 70 | #ifdef STBRP_STATIC 71 | #define STBRP_DEF static 72 | #else 73 | #define STBRP_DEF extern 74 | #endif 75 | 76 | #ifdef __cplusplus 77 | extern "C" { 78 | #endif 79 | 80 | typedef struct stbrp_context stbrp_context; 81 | typedef struct stbrp_node stbrp_node; 82 | typedef struct stbrp_rect stbrp_rect; 83 | 84 | #ifdef STBRP_LARGE_RECTS 85 | typedef int stbrp_coord; 86 | #else 87 | typedef unsigned short stbrp_coord; 88 | #endif 89 | 90 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 91 | // Assign packed locations to rectangles. The rectangles are of type 92 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 93 | // are 'num_rects' many of them. 94 | // 95 | // Rectangles which are successfully packed have the 'was_packed' flag 96 | // set to a non-zero value and 'x' and 'y' store the minimum location 97 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 98 | // if you imagine y increasing downwards). Rectangles which do not fit 99 | // have the 'was_packed' flag set to 0. 100 | // 101 | // You should not try to access the 'rects' array from another thread 102 | // while this function is running, as the function temporarily reorders 103 | // the array while it executes. 104 | // 105 | // To pack into another rectangle, you need to call stbrp_init_target 106 | // again. To continue packing into the same rectangle, you can call 107 | // this function again. Calling this multiple times with multiple rect 108 | // arrays will probably produce worse packing results than calling it 109 | // a single time with the full rectangle array, but the option is 110 | // available. 111 | // 112 | // The function returns 1 if all of the rectangles were successfully 113 | // packed and 0 otherwise. 114 | 115 | struct stbrp_rect 116 | { 117 | // reserved for your use: 118 | int id; 119 | 120 | // input: 121 | stbrp_coord w, h; 122 | 123 | // output: 124 | stbrp_coord x, y; 125 | int was_packed; // non-zero if valid packing 126 | 127 | }; // 16 bytes, nominally 128 | 129 | 130 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 131 | // Initialize a rectangle packer to: 132 | // pack a rectangle that is 'width' by 'height' in dimensions 133 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 134 | // 135 | // You must call this function every time you start packing into a new target. 136 | // 137 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 138 | // the following stbrp_pack_rects() call (or calls), but can be freed after 139 | // the call (or calls) finish. 140 | // 141 | // Note: to guarantee best results, either: 142 | // 1. make sure 'num_nodes' >= 'width' 143 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 144 | // 145 | // If you don't do either of the above things, widths will be quantized to multiples 146 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 147 | // 148 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 149 | // may run out of temporary storage and be unable to pack some rectangles. 150 | 151 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 152 | // Optionally call this function after init but before doing any packing to 153 | // change the handling of the out-of-temp-memory scenario, described above. 154 | // If you call init again, this will be reset to the default (false). 155 | 156 | 157 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 158 | // Optionally select which packing heuristic the library should use. Different 159 | // heuristics will produce better/worse results for different data sets. 160 | // If you call init again, this will be reset to the default. 161 | 162 | enum 163 | { 164 | STBRP_HEURISTIC_Skyline_default=0, 165 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 166 | STBRP_HEURISTIC_Skyline_BF_sortHeight 167 | }; 168 | 169 | 170 | ////////////////////////////////////////////////////////////////////////////// 171 | // 172 | // the details of the following structures don't matter to you, but they must 173 | // be visible so you can handle the memory allocations for them 174 | 175 | struct stbrp_node 176 | { 177 | stbrp_coord x,y; 178 | stbrp_node *next; 179 | }; 180 | 181 | struct stbrp_context 182 | { 183 | int width; 184 | int height; 185 | int align; 186 | int init_mode; 187 | int heuristic; 188 | int num_nodes; 189 | stbrp_node *active_head; 190 | stbrp_node *free_head; 191 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 192 | }; 193 | 194 | #ifdef __cplusplus 195 | } 196 | #endif 197 | 198 | #endif 199 | 200 | ////////////////////////////////////////////////////////////////////////////// 201 | // 202 | // IMPLEMENTATION SECTION 203 | // 204 | 205 | #ifdef STB_RECT_PACK_IMPLEMENTATION 206 | #ifndef STBRP_SORT 207 | #include 208 | #define STBRP_SORT qsort 209 | #endif 210 | 211 | #ifndef STBRP_ASSERT 212 | #include 213 | #define STBRP_ASSERT assert 214 | #endif 215 | 216 | // [DEAR IMGUI] Added STBRP__CDECL 217 | #ifdef _MSC_VER 218 | #define STBRP__NOTUSED(v) (void)(v) 219 | #define STBRP__CDECL __cdecl 220 | #else 221 | #define STBRP__NOTUSED(v) (void)sizeof(v) 222 | #define STBRP__CDECL 223 | #endif 224 | 225 | enum 226 | { 227 | STBRP__INIT_skyline = 1 228 | }; 229 | 230 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 231 | { 232 | switch (context->init_mode) { 233 | case STBRP__INIT_skyline: 234 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 235 | context->heuristic = heuristic; 236 | break; 237 | default: 238 | STBRP_ASSERT(0); 239 | } 240 | } 241 | 242 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 243 | { 244 | if (allow_out_of_mem) 245 | // if it's ok to run out of memory, then don't bother aligning them; 246 | // this gives better packing, but may fail due to OOM (even though 247 | // the rectangles easily fit). @TODO a smarter approach would be to only 248 | // quantize once we've hit OOM, then we could get rid of this parameter. 249 | context->align = 1; 250 | else { 251 | // if it's not ok to run out of memory, then quantize the widths 252 | // so that num_nodes is always enough nodes. 253 | // 254 | // I.e. num_nodes * align >= width 255 | // align >= width / num_nodes 256 | // align = ceil(width/num_nodes) 257 | 258 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 259 | } 260 | } 261 | 262 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 263 | { 264 | int i; 265 | #ifndef STBRP_LARGE_RECTS 266 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 267 | #endif 268 | 269 | for (i=0; i < num_nodes-1; ++i) 270 | nodes[i].next = &nodes[i+1]; 271 | nodes[i].next = NULL; 272 | context->init_mode = STBRP__INIT_skyline; 273 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 274 | context->free_head = &nodes[0]; 275 | context->active_head = &context->extra[0]; 276 | context->width = width; 277 | context->height = height; 278 | context->num_nodes = num_nodes; 279 | stbrp_setup_allow_out_of_mem(context, 0); 280 | 281 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 282 | context->extra[0].x = 0; 283 | context->extra[0].y = 0; 284 | context->extra[0].next = &context->extra[1]; 285 | context->extra[1].x = (stbrp_coord) width; 286 | #ifdef STBRP_LARGE_RECTS 287 | context->extra[1].y = (1<<30); 288 | #else 289 | context->extra[1].y = 65535; 290 | #endif 291 | context->extra[1].next = NULL; 292 | } 293 | 294 | // find minimum y position if it starts at x1 295 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 296 | { 297 | stbrp_node *node = first; 298 | int x1 = x0 + width; 299 | int min_y, visited_width, waste_area; 300 | 301 | STBRP__NOTUSED(c); 302 | 303 | STBRP_ASSERT(first->x <= x0); 304 | 305 | #if 0 306 | // skip in case we're past the node 307 | while (node->next->x <= x0) 308 | ++node; 309 | #else 310 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 311 | #endif 312 | 313 | STBRP_ASSERT(node->x <= x0); 314 | 315 | min_y = 0; 316 | waste_area = 0; 317 | visited_width = 0; 318 | while (node->x < x1) { 319 | if (node->y > min_y) { 320 | // raise min_y higher. 321 | // we've accounted for all waste up to min_y, 322 | // but we'll now add more waste for everything we've visted 323 | waste_area += visited_width * (node->y - min_y); 324 | min_y = node->y; 325 | // the first time through, visited_width might be reduced 326 | if (node->x < x0) 327 | visited_width += node->next->x - x0; 328 | else 329 | visited_width += node->next->x - node->x; 330 | } else { 331 | // add waste area 332 | int under_width = node->next->x - node->x; 333 | if (under_width + visited_width > width) 334 | under_width = width - visited_width; 335 | waste_area += under_width * (min_y - node->y); 336 | visited_width += under_width; 337 | } 338 | node = node->next; 339 | } 340 | 341 | *pwaste = waste_area; 342 | return min_y; 343 | } 344 | 345 | typedef struct 346 | { 347 | int x,y; 348 | stbrp_node **prev_link; 349 | } stbrp__findresult; 350 | 351 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 352 | { 353 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 354 | stbrp__findresult fr; 355 | stbrp_node **prev, *node, *tail, **best = NULL; 356 | 357 | // align to multiple of c->align 358 | width = (width + c->align - 1); 359 | width -= width % c->align; 360 | STBRP_ASSERT(width % c->align == 0); 361 | 362 | // if it can't possibly fit, bail immediately 363 | if (width > c->width || height > c->height) { 364 | fr.prev_link = NULL; 365 | fr.x = fr.y = 0; 366 | return fr; 367 | } 368 | 369 | node = c->active_head; 370 | prev = &c->active_head; 371 | while (node->x + width <= c->width) { 372 | int y,waste; 373 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 374 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 375 | // bottom left 376 | if (y < best_y) { 377 | best_y = y; 378 | best = prev; 379 | } 380 | } else { 381 | // best-fit 382 | if (y + height <= c->height) { 383 | // can only use it if it first vertically 384 | if (y < best_y || (y == best_y && waste < best_waste)) { 385 | best_y = y; 386 | best_waste = waste; 387 | best = prev; 388 | } 389 | } 390 | } 391 | prev = &node->next; 392 | node = node->next; 393 | } 394 | 395 | best_x = (best == NULL) ? 0 : (*best)->x; 396 | 397 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 398 | // 399 | // e.g, if fitting 400 | // 401 | // ____________________ 402 | // |____________________| 403 | // 404 | // into 405 | // 406 | // | | 407 | // | ____________| 408 | // |____________| 409 | // 410 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 411 | // 412 | // This makes BF take about 2x the time 413 | 414 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 415 | tail = c->active_head; 416 | node = c->active_head; 417 | prev = &c->active_head; 418 | // find first node that's admissible 419 | while (tail->x < width) 420 | tail = tail->next; 421 | while (tail) { 422 | int xpos = tail->x - width; 423 | int y,waste; 424 | STBRP_ASSERT(xpos >= 0); 425 | // find the left position that matches this 426 | while (node->next->x <= xpos) { 427 | prev = &node->next; 428 | node = node->next; 429 | } 430 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 431 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 432 | if (y + height <= c->height) { 433 | if (y <= best_y) { 434 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 435 | best_x = xpos; 436 | STBRP_ASSERT(y <= best_y); 437 | best_y = y; 438 | best_waste = waste; 439 | best = prev; 440 | } 441 | } 442 | } 443 | tail = tail->next; 444 | } 445 | } 446 | 447 | fr.prev_link = best; 448 | fr.x = best_x; 449 | fr.y = best_y; 450 | return fr; 451 | } 452 | 453 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 454 | { 455 | // find best position according to heuristic 456 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 457 | stbrp_node *node, *cur; 458 | 459 | // bail if: 460 | // 1. it failed 461 | // 2. the best node doesn't fit (we don't always check this) 462 | // 3. we're out of memory 463 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 464 | res.prev_link = NULL; 465 | return res; 466 | } 467 | 468 | // on success, create new node 469 | node = context->free_head; 470 | node->x = (stbrp_coord) res.x; 471 | node->y = (stbrp_coord) (res.y + height); 472 | 473 | context->free_head = node->next; 474 | 475 | // insert the new node into the right starting point, and 476 | // let 'cur' point to the remaining nodes needing to be 477 | // stiched back in 478 | 479 | cur = *res.prev_link; 480 | if (cur->x < res.x) { 481 | // preserve the existing one, so start testing with the next one 482 | stbrp_node *next = cur->next; 483 | cur->next = node; 484 | cur = next; 485 | } else { 486 | *res.prev_link = node; 487 | } 488 | 489 | // from here, traverse cur and free the nodes, until we get to one 490 | // that shouldn't be freed 491 | while (cur->next && cur->next->x <= res.x + width) { 492 | stbrp_node *next = cur->next; 493 | // move the current node to the free list 494 | cur->next = context->free_head; 495 | context->free_head = cur; 496 | cur = next; 497 | } 498 | 499 | // stitch the list back in 500 | node->next = cur; 501 | 502 | if (cur->x < res.x + width) 503 | cur->x = (stbrp_coord) (res.x + width); 504 | 505 | #ifdef _DEBUG 506 | cur = context->active_head; 507 | while (cur->x < context->width) { 508 | STBRP_ASSERT(cur->x < cur->next->x); 509 | cur = cur->next; 510 | } 511 | STBRP_ASSERT(cur->next == NULL); 512 | 513 | { 514 | int count=0; 515 | cur = context->active_head; 516 | while (cur) { 517 | cur = cur->next; 518 | ++count; 519 | } 520 | cur = context->free_head; 521 | while (cur) { 522 | cur = cur->next; 523 | ++count; 524 | } 525 | STBRP_ASSERT(count == context->num_nodes+2); 526 | } 527 | #endif 528 | 529 | return res; 530 | } 531 | 532 | // [DEAR IMGUI] Added STBRP__CDECL 533 | static int STBRP__CDECL rect_height_compare(const void *a, const void *b) 534 | { 535 | const stbrp_rect *p = (const stbrp_rect *) a; 536 | const stbrp_rect *q = (const stbrp_rect *) b; 537 | if (p->h > q->h) 538 | return -1; 539 | if (p->h < q->h) 540 | return 1; 541 | return (p->w > q->w) ? -1 : (p->w < q->w); 542 | } 543 | 544 | // [DEAR IMGUI] Added STBRP__CDECL 545 | static int STBRP__CDECL rect_original_order(const void *a, const void *b) 546 | { 547 | const stbrp_rect *p = (const stbrp_rect *) a; 548 | const stbrp_rect *q = (const stbrp_rect *) b; 549 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 550 | } 551 | 552 | #ifdef STBRP_LARGE_RECTS 553 | #define STBRP__MAXVAL 0xffffffff 554 | #else 555 | #define STBRP__MAXVAL 0xffff 556 | #endif 557 | 558 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 559 | { 560 | int i, all_rects_packed = 1; 561 | 562 | // we use the 'was_packed' field internally to allow sorting/unsorting 563 | for (i=0; i < num_rects; ++i) { 564 | rects[i].was_packed = i; 565 | } 566 | 567 | // sort according to heuristic 568 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 569 | 570 | for (i=0; i < num_rects; ++i) { 571 | if (rects[i].w == 0 || rects[i].h == 0) { 572 | rects[i].x = rects[i].y = 0; // empty rect needs no space 573 | } else { 574 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 575 | if (fr.prev_link) { 576 | rects[i].x = (stbrp_coord) fr.x; 577 | rects[i].y = (stbrp_coord) fr.y; 578 | } else { 579 | rects[i].x = rects[i].y = STBRP__MAXVAL; 580 | } 581 | } 582 | } 583 | 584 | // unsort 585 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 586 | 587 | // set was_packed flags and all_rects_packed status 588 | for (i=0; i < num_rects; ++i) { 589 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 590 | if (!rects[i].was_packed) 591 | all_rects_packed = 0; 592 | } 593 | 594 | // return the all_rects_packed status 595 | return all_rects_packed; 596 | } 597 | #endif 598 | 599 | /* 600 | ------------------------------------------------------------------------------ 601 | This software is available under 2 licenses -- choose whichever you prefer. 602 | ------------------------------------------------------------------------------ 603 | ALTERNATIVE A - MIT License 604 | Copyright (c) 2017 Sean Barrett 605 | Permission is hereby granted, free of charge, to any person obtaining a copy of 606 | this software and associated documentation files (the "Software"), to deal in 607 | the Software without restriction, including without limitation the rights to 608 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 609 | of the Software, and to permit persons to whom the Software is furnished to do 610 | so, subject to the following conditions: 611 | The above copyright notice and this permission notice shall be included in all 612 | copies or substantial portions of the Software. 613 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 614 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 615 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 616 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 617 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 618 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 619 | SOFTWARE. 620 | ------------------------------------------------------------------------------ 621 | ALTERNATIVE B - Public Domain (www.unlicense.org) 622 | This is free and unencumbered software released into the public domain. 623 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 624 | software, either in source code form or as a compiled binary, for any purpose, 625 | commercial or non-commercial, and by any means. 626 | In jurisdictions that recognize copyright laws, the author or authors of this 627 | software dedicate any and all copyright interest in the software to the public 628 | domain. We make this dedication for the benefit of the public at large and to 629 | the detriment of our heirs and successors. We intend this dedication to be an 630 | overt act of relinquishment in perpetuity of all present and future rights to 631 | this software under copyright law. 632 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 633 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 634 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 635 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 636 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 637 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 638 | ------------------------------------------------------------------------------ 639 | */ 640 | -------------------------------------------------------------------------------- /fmt/include/fmt/color.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - color support 2 | // 3 | // Copyright (c) 2018 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COLOR_H_ 9 | #define FMT_COLOR_H_ 10 | 11 | #include "format.h" 12 | 13 | FMT_BEGIN_NAMESPACE 14 | 15 | enum class color : uint32_t { 16 | alice_blue = 0xF0F8FF, // rgb(240,248,255) 17 | antique_white = 0xFAEBD7, // rgb(250,235,215) 18 | aqua = 0x00FFFF, // rgb(0,255,255) 19 | aquamarine = 0x7FFFD4, // rgb(127,255,212) 20 | azure = 0xF0FFFF, // rgb(240,255,255) 21 | beige = 0xF5F5DC, // rgb(245,245,220) 22 | bisque = 0xFFE4C4, // rgb(255,228,196) 23 | black = 0x000000, // rgb(0,0,0) 24 | blanched_almond = 0xFFEBCD, // rgb(255,235,205) 25 | blue = 0x0000FF, // rgb(0,0,255) 26 | blue_violet = 0x8A2BE2, // rgb(138,43,226) 27 | brown = 0xA52A2A, // rgb(165,42,42) 28 | burly_wood = 0xDEB887, // rgb(222,184,135) 29 | cadet_blue = 0x5F9EA0, // rgb(95,158,160) 30 | chartreuse = 0x7FFF00, // rgb(127,255,0) 31 | chocolate = 0xD2691E, // rgb(210,105,30) 32 | coral = 0xFF7F50, // rgb(255,127,80) 33 | cornflower_blue = 0x6495ED, // rgb(100,149,237) 34 | cornsilk = 0xFFF8DC, // rgb(255,248,220) 35 | crimson = 0xDC143C, // rgb(220,20,60) 36 | cyan = 0x00FFFF, // rgb(0,255,255) 37 | dark_blue = 0x00008B, // rgb(0,0,139) 38 | dark_cyan = 0x008B8B, // rgb(0,139,139) 39 | dark_golden_rod = 0xB8860B, // rgb(184,134,11) 40 | dark_gray = 0xA9A9A9, // rgb(169,169,169) 41 | dark_green = 0x006400, // rgb(0,100,0) 42 | dark_khaki = 0xBDB76B, // rgb(189,183,107) 43 | dark_magenta = 0x8B008B, // rgb(139,0,139) 44 | dark_olive_green = 0x556B2F, // rgb(85,107,47) 45 | dark_orange = 0xFF8C00, // rgb(255,140,0) 46 | dark_orchid = 0x9932CC, // rgb(153,50,204) 47 | dark_red = 0x8B0000, // rgb(139,0,0) 48 | dark_salmon = 0xE9967A, // rgb(233,150,122) 49 | dark_sea_green = 0x8FBC8F, // rgb(143,188,143) 50 | dark_slate_blue = 0x483D8B, // rgb(72,61,139) 51 | dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) 52 | dark_turquoise = 0x00CED1, // rgb(0,206,209) 53 | dark_violet = 0x9400D3, // rgb(148,0,211) 54 | deep_pink = 0xFF1493, // rgb(255,20,147) 55 | deep_sky_blue = 0x00BFFF, // rgb(0,191,255) 56 | dim_gray = 0x696969, // rgb(105,105,105) 57 | dodger_blue = 0x1E90FF, // rgb(30,144,255) 58 | fire_brick = 0xB22222, // rgb(178,34,34) 59 | floral_white = 0xFFFAF0, // rgb(255,250,240) 60 | forest_green = 0x228B22, // rgb(34,139,34) 61 | fuchsia = 0xFF00FF, // rgb(255,0,255) 62 | gainsboro = 0xDCDCDC, // rgb(220,220,220) 63 | ghost_white = 0xF8F8FF, // rgb(248,248,255) 64 | gold = 0xFFD700, // rgb(255,215,0) 65 | golden_rod = 0xDAA520, // rgb(218,165,32) 66 | gray = 0x808080, // rgb(128,128,128) 67 | green = 0x008000, // rgb(0,128,0) 68 | green_yellow = 0xADFF2F, // rgb(173,255,47) 69 | honey_dew = 0xF0FFF0, // rgb(240,255,240) 70 | hot_pink = 0xFF69B4, // rgb(255,105,180) 71 | indian_red = 0xCD5C5C, // rgb(205,92,92) 72 | indigo = 0x4B0082, // rgb(75,0,130) 73 | ivory = 0xFFFFF0, // rgb(255,255,240) 74 | khaki = 0xF0E68C, // rgb(240,230,140) 75 | lavender = 0xE6E6FA, // rgb(230,230,250) 76 | lavender_blush = 0xFFF0F5, // rgb(255,240,245) 77 | lawn_green = 0x7CFC00, // rgb(124,252,0) 78 | lemon_chiffon = 0xFFFACD, // rgb(255,250,205) 79 | light_blue = 0xADD8E6, // rgb(173,216,230) 80 | light_coral = 0xF08080, // rgb(240,128,128) 81 | light_cyan = 0xE0FFFF, // rgb(224,255,255) 82 | light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) 83 | light_gray = 0xD3D3D3, // rgb(211,211,211) 84 | light_green = 0x90EE90, // rgb(144,238,144) 85 | light_pink = 0xFFB6C1, // rgb(255,182,193) 86 | light_salmon = 0xFFA07A, // rgb(255,160,122) 87 | light_sea_green = 0x20B2AA, // rgb(32,178,170) 88 | light_sky_blue = 0x87CEFA, // rgb(135,206,250) 89 | light_slate_gray = 0x778899, // rgb(119,136,153) 90 | light_steel_blue = 0xB0C4DE, // rgb(176,196,222) 91 | light_yellow = 0xFFFFE0, // rgb(255,255,224) 92 | lime = 0x00FF00, // rgb(0,255,0) 93 | lime_green = 0x32CD32, // rgb(50,205,50) 94 | linen = 0xFAF0E6, // rgb(250,240,230) 95 | magenta = 0xFF00FF, // rgb(255,0,255) 96 | maroon = 0x800000, // rgb(128,0,0) 97 | medium_aquamarine = 0x66CDAA, // rgb(102,205,170) 98 | medium_blue = 0x0000CD, // rgb(0,0,205) 99 | medium_orchid = 0xBA55D3, // rgb(186,85,211) 100 | medium_purple = 0x9370DB, // rgb(147,112,219) 101 | medium_sea_green = 0x3CB371, // rgb(60,179,113) 102 | medium_slate_blue = 0x7B68EE, // rgb(123,104,238) 103 | medium_spring_green = 0x00FA9A, // rgb(0,250,154) 104 | medium_turquoise = 0x48D1CC, // rgb(72,209,204) 105 | medium_violet_red = 0xC71585, // rgb(199,21,133) 106 | midnight_blue = 0x191970, // rgb(25,25,112) 107 | mint_cream = 0xF5FFFA, // rgb(245,255,250) 108 | misty_rose = 0xFFE4E1, // rgb(255,228,225) 109 | moccasin = 0xFFE4B5, // rgb(255,228,181) 110 | navajo_white = 0xFFDEAD, // rgb(255,222,173) 111 | navy = 0x000080, // rgb(0,0,128) 112 | old_lace = 0xFDF5E6, // rgb(253,245,230) 113 | olive = 0x808000, // rgb(128,128,0) 114 | olive_drab = 0x6B8E23, // rgb(107,142,35) 115 | orange = 0xFFA500, // rgb(255,165,0) 116 | orange_red = 0xFF4500, // rgb(255,69,0) 117 | orchid = 0xDA70D6, // rgb(218,112,214) 118 | pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) 119 | pale_green = 0x98FB98, // rgb(152,251,152) 120 | pale_turquoise = 0xAFEEEE, // rgb(175,238,238) 121 | pale_violet_red = 0xDB7093, // rgb(219,112,147) 122 | papaya_whip = 0xFFEFD5, // rgb(255,239,213) 123 | peach_puff = 0xFFDAB9, // rgb(255,218,185) 124 | peru = 0xCD853F, // rgb(205,133,63) 125 | pink = 0xFFC0CB, // rgb(255,192,203) 126 | plum = 0xDDA0DD, // rgb(221,160,221) 127 | powder_blue = 0xB0E0E6, // rgb(176,224,230) 128 | purple = 0x800080, // rgb(128,0,128) 129 | rebecca_purple = 0x663399, // rgb(102,51,153) 130 | red = 0xFF0000, // rgb(255,0,0) 131 | rosy_brown = 0xBC8F8F, // rgb(188,143,143) 132 | royal_blue = 0x4169E1, // rgb(65,105,225) 133 | saddle_brown = 0x8B4513, // rgb(139,69,19) 134 | salmon = 0xFA8072, // rgb(250,128,114) 135 | sandy_brown = 0xF4A460, // rgb(244,164,96) 136 | sea_green = 0x2E8B57, // rgb(46,139,87) 137 | sea_shell = 0xFFF5EE, // rgb(255,245,238) 138 | sienna = 0xA0522D, // rgb(160,82,45) 139 | silver = 0xC0C0C0, // rgb(192,192,192) 140 | sky_blue = 0x87CEEB, // rgb(135,206,235) 141 | slate_blue = 0x6A5ACD, // rgb(106,90,205) 142 | slate_gray = 0x708090, // rgb(112,128,144) 143 | snow = 0xFFFAFA, // rgb(255,250,250) 144 | spring_green = 0x00FF7F, // rgb(0,255,127) 145 | steel_blue = 0x4682B4, // rgb(70,130,180) 146 | tan = 0xD2B48C, // rgb(210,180,140) 147 | teal = 0x008080, // rgb(0,128,128) 148 | thistle = 0xD8BFD8, // rgb(216,191,216) 149 | tomato = 0xFF6347, // rgb(255,99,71) 150 | turquoise = 0x40E0D0, // rgb(64,224,208) 151 | violet = 0xEE82EE, // rgb(238,130,238) 152 | wheat = 0xF5DEB3, // rgb(245,222,179) 153 | white = 0xFFFFFF, // rgb(255,255,255) 154 | white_smoke = 0xF5F5F5, // rgb(245,245,245) 155 | yellow = 0xFFFF00, // rgb(255,255,0) 156 | yellow_green = 0x9ACD32 // rgb(154,205,50) 157 | }; // enum class color 158 | 159 | enum class terminal_color : uint8_t { 160 | black = 30, 161 | red, 162 | green, 163 | yellow, 164 | blue, 165 | magenta, 166 | cyan, 167 | white, 168 | bright_black = 90, 169 | bright_red, 170 | bright_green, 171 | bright_yellow, 172 | bright_blue, 173 | bright_magenta, 174 | bright_cyan, 175 | bright_white 176 | }; 177 | 178 | enum class emphasis : uint8_t { 179 | bold = 1, 180 | italic = 1 << 1, 181 | underline = 1 << 2, 182 | strikethrough = 1 << 3 183 | }; 184 | 185 | // rgb is a struct for red, green and blue colors. 186 | // Using the name "rgb" makes some editors show the color in a tooltip. 187 | struct rgb { 188 | FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} 189 | FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} 190 | FMT_CONSTEXPR rgb(uint32_t hex) 191 | : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} 192 | FMT_CONSTEXPR rgb(color hex) 193 | : r((uint32_t(hex) >> 16) & 0xFF), 194 | g((uint32_t(hex) >> 8) & 0xFF), 195 | b(uint32_t(hex) & 0xFF) {} 196 | uint8_t r; 197 | uint8_t g; 198 | uint8_t b; 199 | }; 200 | 201 | namespace detail { 202 | 203 | // color is a struct of either a rgb color or a terminal color. 204 | struct color_type { 205 | FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} 206 | FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), 207 | value{} { 208 | value.rgb_color = static_cast(rgb_color); 209 | } 210 | FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { 211 | value.rgb_color = (static_cast(rgb_color.r) << 16) | 212 | (static_cast(rgb_color.g) << 8) | rgb_color.b; 213 | } 214 | FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), 215 | value{} { 216 | value.term_color = static_cast(term_color); 217 | } 218 | bool is_rgb; 219 | union color_union { 220 | uint8_t term_color; 221 | uint32_t rgb_color; 222 | } value; 223 | }; 224 | } // namespace detail 225 | 226 | // Experimental text formatting support. 227 | class text_style { 228 | public: 229 | FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT 230 | : set_foreground_color(), 231 | set_background_color(), 232 | ems(em) {} 233 | 234 | FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { 235 | if (!set_foreground_color) { 236 | set_foreground_color = rhs.set_foreground_color; 237 | foreground_color = rhs.foreground_color; 238 | } else if (rhs.set_foreground_color) { 239 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 240 | FMT_THROW(format_error("can't OR a terminal color")); 241 | foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; 242 | } 243 | 244 | if (!set_background_color) { 245 | set_background_color = rhs.set_background_color; 246 | background_color = rhs.background_color; 247 | } else if (rhs.set_background_color) { 248 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 249 | FMT_THROW(format_error("can't OR a terminal color")); 250 | background_color.value.rgb_color |= rhs.background_color.value.rgb_color; 251 | } 252 | 253 | ems = static_cast(static_cast(ems) | 254 | static_cast(rhs.ems)); 255 | return *this; 256 | } 257 | 258 | friend FMT_CONSTEXPR text_style operator|(text_style lhs, 259 | const text_style& rhs) { 260 | return lhs |= rhs; 261 | } 262 | 263 | FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { 264 | if (!set_foreground_color) { 265 | set_foreground_color = rhs.set_foreground_color; 266 | foreground_color = rhs.foreground_color; 267 | } else if (rhs.set_foreground_color) { 268 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 269 | FMT_THROW(format_error("can't AND a terminal color")); 270 | foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; 271 | } 272 | 273 | if (!set_background_color) { 274 | set_background_color = rhs.set_background_color; 275 | background_color = rhs.background_color; 276 | } else if (rhs.set_background_color) { 277 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 278 | FMT_THROW(format_error("can't AND a terminal color")); 279 | background_color.value.rgb_color &= rhs.background_color.value.rgb_color; 280 | } 281 | 282 | ems = static_cast(static_cast(ems) & 283 | static_cast(rhs.ems)); 284 | return *this; 285 | } 286 | 287 | friend FMT_CONSTEXPR text_style operator&(text_style lhs, 288 | const text_style& rhs) { 289 | return lhs &= rhs; 290 | } 291 | 292 | FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { 293 | return set_foreground_color; 294 | } 295 | FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { 296 | return set_background_color; 297 | } 298 | FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { 299 | return static_cast(ems) != 0; 300 | } 301 | FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { 302 | FMT_ASSERT(has_foreground(), "no foreground specified for this style"); 303 | return foreground_color; 304 | } 305 | FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { 306 | FMT_ASSERT(has_background(), "no background specified for this style"); 307 | return background_color; 308 | } 309 | FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { 310 | FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); 311 | return ems; 312 | } 313 | 314 | private: 315 | FMT_CONSTEXPR text_style(bool is_foreground, 316 | detail::color_type text_color) FMT_NOEXCEPT 317 | : set_foreground_color(), 318 | set_background_color(), 319 | ems() { 320 | if (is_foreground) { 321 | foreground_color = text_color; 322 | set_foreground_color = true; 323 | } else { 324 | background_color = text_color; 325 | set_background_color = true; 326 | } 327 | } 328 | 329 | friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) 330 | FMT_NOEXCEPT; 331 | friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) 332 | FMT_NOEXCEPT; 333 | 334 | detail::color_type foreground_color; 335 | detail::color_type background_color; 336 | bool set_foreground_color; 337 | bool set_background_color; 338 | emphasis ems; 339 | }; 340 | 341 | FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { 342 | return text_style(/*is_foreground=*/true, foreground); 343 | } 344 | 345 | FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { 346 | return text_style(/*is_foreground=*/false, background); 347 | } 348 | 349 | FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { 350 | return text_style(lhs) | rhs; 351 | } 352 | 353 | namespace detail { 354 | 355 | template struct ansi_color_escape { 356 | FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, 357 | const char* esc) FMT_NOEXCEPT { 358 | // If we have a terminal color, we need to output another escape code 359 | // sequence. 360 | if (!text_color.is_rgb) { 361 | bool is_background = esc == detail::data::background_color; 362 | uint32_t value = text_color.value.term_color; 363 | // Background ASCII codes are the same as the foreground ones but with 364 | // 10 more. 365 | if (is_background) value += 10u; 366 | 367 | size_t index = 0; 368 | buffer[index++] = static_cast('\x1b'); 369 | buffer[index++] = static_cast('['); 370 | 371 | if (value >= 100u) { 372 | buffer[index++] = static_cast('1'); 373 | value %= 100u; 374 | } 375 | buffer[index++] = static_cast('0' + value / 10u); 376 | buffer[index++] = static_cast('0' + value % 10u); 377 | 378 | buffer[index++] = static_cast('m'); 379 | buffer[index++] = static_cast('\0'); 380 | return; 381 | } 382 | 383 | for (int i = 0; i < 7; i++) { 384 | buffer[i] = static_cast(esc[i]); 385 | } 386 | rgb color(text_color.value.rgb_color); 387 | to_esc(color.r, buffer + 7, ';'); 388 | to_esc(color.g, buffer + 11, ';'); 389 | to_esc(color.b, buffer + 15, 'm'); 390 | buffer[19] = static_cast(0); 391 | } 392 | FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { 393 | uint8_t em_codes[4] = {}; 394 | uint8_t em_bits = static_cast(em); 395 | if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; 396 | if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; 397 | if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; 398 | if (em_bits & static_cast(emphasis::strikethrough)) 399 | em_codes[3] = 9; 400 | 401 | size_t index = 0; 402 | for (int i = 0; i < 4; ++i) { 403 | if (!em_codes[i]) continue; 404 | buffer[index++] = static_cast('\x1b'); 405 | buffer[index++] = static_cast('['); 406 | buffer[index++] = static_cast('0' + em_codes[i]); 407 | buffer[index++] = static_cast('m'); 408 | } 409 | buffer[index++] = static_cast(0); 410 | } 411 | FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } 412 | 413 | FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } 414 | FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { 415 | return buffer + std::char_traits::length(buffer); 416 | } 417 | 418 | private: 419 | Char buffer[7u + 3u * 4u + 1u]; 420 | 421 | static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, 422 | char delimiter) FMT_NOEXCEPT { 423 | out[0] = static_cast('0' + c / 100); 424 | out[1] = static_cast('0' + c / 10 % 10); 425 | out[2] = static_cast('0' + c % 10); 426 | out[3] = static_cast(delimiter); 427 | } 428 | }; 429 | 430 | template 431 | FMT_CONSTEXPR ansi_color_escape make_foreground_color( 432 | detail::color_type foreground) FMT_NOEXCEPT { 433 | return ansi_color_escape(foreground, detail::data::foreground_color); 434 | } 435 | 436 | template 437 | FMT_CONSTEXPR ansi_color_escape make_background_color( 438 | detail::color_type background) FMT_NOEXCEPT { 439 | return ansi_color_escape(background, detail::data::background_color); 440 | } 441 | 442 | template 443 | FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { 444 | return ansi_color_escape(em); 445 | } 446 | 447 | template 448 | inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { 449 | std::fputs(chars, stream); 450 | } 451 | 452 | template <> 453 | inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { 454 | std::fputws(chars, stream); 455 | } 456 | 457 | template inline void reset_color(FILE* stream) FMT_NOEXCEPT { 458 | fputs(detail::data::reset_color, stream); 459 | } 460 | 461 | template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { 462 | fputs(detail::data::wreset_color, stream); 463 | } 464 | 465 | template 466 | inline void reset_color(basic_memory_buffer& buffer) FMT_NOEXCEPT { 467 | const char* begin = data::reset_color; 468 | const char* end = begin + sizeof(data::reset_color) - 1; 469 | buffer.append(begin, end); 470 | } 471 | 472 | template 473 | void vformat_to(basic_memory_buffer& buf, const text_style& ts, 474 | basic_string_view format_str, 475 | basic_format_args> args) { 476 | bool has_style = false; 477 | if (ts.has_emphasis()) { 478 | has_style = true; 479 | auto emphasis = detail::make_emphasis(ts.get_emphasis()); 480 | buf.append(emphasis.begin(), emphasis.end()); 481 | } 482 | if (ts.has_foreground()) { 483 | has_style = true; 484 | auto foreground = detail::make_foreground_color(ts.get_foreground()); 485 | buf.append(foreground.begin(), foreground.end()); 486 | } 487 | if (ts.has_background()) { 488 | has_style = true; 489 | auto background = detail::make_background_color(ts.get_background()); 490 | buf.append(background.begin(), background.end()); 491 | } 492 | detail::vformat_to(buf, format_str, args); 493 | if (has_style) detail::reset_color(buf); 494 | } 495 | } // namespace detail 496 | 497 | template > 498 | void vprint(std::FILE* f, const text_style& ts, const S& format, 499 | basic_format_args> args) { 500 | basic_memory_buffer buf; 501 | detail::vformat_to(buf, ts, to_string_view(format), args); 502 | buf.push_back(Char(0)); 503 | detail::fputs(buf.data(), f); 504 | } 505 | 506 | /** 507 | Formats a string and prints it to the specified file stream using ANSI 508 | escape sequences to specify text formatting. 509 | Example: 510 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 511 | "Elapsed time: {0:.2f} seconds", 1.23); 512 | */ 513 | template ::value)> 515 | void print(std::FILE* f, const text_style& ts, const S& format_str, 516 | const Args&... args) { 517 | detail::check_format_string(format_str); 518 | using context = buffer_context>; 519 | format_arg_store as{args...}; 520 | vprint(f, ts, format_str, basic_format_args(as)); 521 | } 522 | 523 | /** 524 | Formats a string and prints it to stdout using ANSI escape sequences to 525 | specify text formatting. 526 | Example: 527 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 528 | "Elapsed time: {0:.2f} seconds", 1.23); 529 | */ 530 | template ::value)> 532 | void print(const text_style& ts, const S& format_str, const Args&... args) { 533 | return print(stdout, ts, format_str, args...); 534 | } 535 | 536 | template > 537 | inline std::basic_string vformat( 538 | const text_style& ts, const S& format_str, 539 | basic_format_args>> args) { 540 | basic_memory_buffer buf; 541 | detail::vformat_to(buf, ts, to_string_view(format_str), args); 542 | return fmt::to_string(buf); 543 | } 544 | 545 | /** 546 | \rst 547 | Formats arguments and returns the result as a string using ANSI 548 | escape sequences to specify text formatting. 549 | 550 | **Example**:: 551 | 552 | #include 553 | std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), 554 | "The answer is {}", 42); 555 | \endrst 556 | */ 557 | template > 558 | inline std::basic_string format(const text_style& ts, const S& format_str, 559 | const Args&... args) { 560 | return vformat(ts, to_string_view(format_str), 561 | detail::make_args_checked(format_str, args...)); 562 | } 563 | 564 | FMT_END_NAMESPACE 565 | 566 | #endif // FMT_COLOR_H_ 567 | --------------------------------------------------------------------------------