├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── about.md ├── changelog.md ├── cmake └── CPM.cmake ├── logo.png ├── mod.json ├── resources └── Muli.ttf └── src ├── animationhandler.cpp ├── animationhandler.hpp ├── colorfade.cpp ├── colorfade.hpp ├── errorcodes.cpp ├── errorcodes.hpp ├── gui.cpp ├── gui.hpp ├── main.cpp ├── manager.cpp ├── manager.hpp ├── mhcolor.cpp ├── mhcolor.hpp ├── mhinfo.cpp ├── mhinfo.hpp ├── mhmenu.cpp ├── mhmenu.hpp ├── savedata.cpp └── savedata.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Macos be like 35 | **/.DS_Store 36 | 37 | # Cache files for Sublime Text 38 | *.tmlanguage.cache 39 | *.tmPreferences.cache 40 | *.stTheme.cache 41 | 42 | # Ignore build folders 43 | **/build 44 | # Ignore platform specific build folders 45 | build-*/ 46 | 47 | # Workspace files are user-specific 48 | *.sublime-workspace 49 | 50 | # ILY vscode 51 | **/.vscode 52 | 53 | # Local History for Visual Studio Code 54 | .history/ 55 | 56 | # clangd 57 | .cache/ 58 | 59 | # Visual Studio 60 | .vs/ 61 | 62 | # CLion 63 | .idea/ 64 | /cmake-build-*/ 65 | 66 | # Only include logo png 67 | logo.pdn -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/Sig"] 2 | path = lib/Sig 3 | url = https://github.com/HoShiMin/Sig 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | set(CMAKE_CXX_STANDARD 20) 3 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 4 | set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") 5 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 6 | 7 | project(MegaHack-Recolor VERSION 1.0.0) 8 | 9 | # Set up the mod binary 10 | file(GLOB SOURCE_FILES src/*.cpp) 11 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES}) 12 | 13 | if(NOT DEFINED ENV{GEODE_SDK}) 14 | message(FATAL_ERROR "Unable to find Geode SDK! Please define GEODE_SDK environment variable to point to Geode") 15 | else() 16 | message(STATUS "Found Geode: $ENV{GEODE_SDK}") 17 | endif() 18 | 19 | target_include_directories(${PROJECT_NAME} PRIVATE 20 | lib/Sig/include/Sig 21 | ) 22 | 23 | if(NOT COMMAND CPMAddPackage) 24 | include(cmake/CPM.cmake) 25 | endif() 26 | 27 | CPMAddPackage("gh:matcool/gd-imgui-cocos#09a9555") 28 | 29 | target_link_libraries(${PROJECT_NAME} imgui-cocos) 30 | 31 | add_subdirectory($ENV{GEODE_SDK} ${CMAKE_CURRENT_BINARY_DIR}/geode) 32 | 33 | # Set up dependencies, resources, and link Geode. 34 | setup_geode_mod(${PROJECT_NAME}) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ikszyon 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mega Hack Recolor 2 | 3 | Lets you customize MH's menu color to any color of your liking! 4 | 5 | ## Features 6 | 7 | - Set MH's color to any color you want 8 | - Forward compatibility (sigscanning) 9 | - Rainbow menu animation 10 | 11 | ## Planned Features 12 | 13 | - Fully customizable color transitions (system in place already; still need to add the customizability) 14 | 15 | ### Special Thanks 16 | 17 | - KontrollFreek (Mod Icon) -------------------------------------------------------------------------------- /about.md: -------------------------------------------------------------------------------- 1 | # Mega Hack Recolor 2 | 3 | Lets you customize MH's menu color to any color of your liking! 4 | 5 | ## Features 6 | 7 | - Set MH's color to any color you want 8 | - Forward compatibility (sigscanning) 9 | - Rainbow menu animation 10 | 11 | ## Planned Features 12 | 13 | - Fully customizable color transitions (system in place already; still need to add the customizability) 14 | 15 | ### Special Thanks 16 | 17 | - KontrollFreek (Mod Icon) -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Geode v3.0.0 2 | - Ported to Geode 3 | 4 | # Compatibility v2.1.0 5 | - sigscans for the function offsets now which means version independence yippeeee 6 | 7 | # Hotfix v2.0.2 8 | - Adjusted Offsets for v7.1.1-GM2 9 | 10 | # Hotfix v2.0.1 11 | - Adjusted Offsets for v7.1.1-GM1 12 | 13 | # Release v2.0.0 14 | - can now be loaded through extensions 15 | - runtime color change (switched from patches to hooks) 16 | - rainbow menu animation 17 | - customizable Saturation and Value 18 | - adjustable cycle speed 19 | - readable JSON config file (MHRecolor/config.json) 20 | - no more crashes (hopefully) 21 | 22 | # QOL v1.2 23 | - the mod now only checks if extensions\MegaHack-Recolor.dll exists or not 24 | 25 | # Bugfix v1.1 26 | - Fixed bug which caused colors that had 0x00 - 0x09 as their g or b values to not save correctly 27 | - Better quickldr check 28 | 29 | # Initial Release v1.0 30 | h 31 | -------------------------------------------------------------------------------- /cmake/CPM.cmake: -------------------------------------------------------------------------------- 1 | set(CPM_DOWNLOAD_VERSION 0.35.4) 2 | 3 | if(CPM_SOURCE_CACHE) 4 | # Expand relative path. This is important if the provided path contains a tilde (~) 5 | get_filename_component(CPM_SOURCE_CACHE ${CPM_SOURCE_CACHE} ABSOLUTE) 6 | set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 7 | elseif(DEFINED ENV{CPM_SOURCE_CACHE}) 8 | set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 9 | else() 10 | set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 11 | endif() 12 | 13 | if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) 14 | message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") 15 | file(DOWNLOAD 16 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake 17 | ${CPM_DOWNLOAD_LOCATION} 18 | ) 19 | endif() 20 | 21 | include(${CPM_DOWNLOAD_LOCATION}) -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ikszyon/MegaHack-Recolor/ab57baacf61b719922f5470ee4157dff7ecf8362/logo.png -------------------------------------------------------------------------------- /mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "geode": "3.7.1", 3 | "gd": { 4 | "win": "2.206" 5 | }, 6 | 7 | "id": "ikszyon.mhrecolor", 8 | "name": "Mega Hack Recolor", 9 | "version": "v3.0.0", 10 | "developer": "Ikszyon", 11 | "description": "Lets you customize Mega Hack's default color", 12 | "tags": ["customization", "enhancement", "interface"], 13 | "repository": "https://github.com/Ikszyon/MegaHack-Recolor", 14 | 15 | "resources": { 16 | "files": [ 17 | "resources/Muli.ttf" 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /resources/Muli.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ikszyon/MegaHack-Recolor/ab57baacf61b719922f5470ee4157dff7ecf8362/resources/Muli.ttf -------------------------------------------------------------------------------- /src/animationhandler.cpp: -------------------------------------------------------------------------------- 1 | #include "animationhandler.hpp" 2 | 3 | namespace MHRecolor { 4 | AnimationData::AnimationData(std::string AnimationName, std::vector> AnimationVector) 5 | : AnimationName(AnimationName), AnimationVector(AnimationVector) {} 6 | 7 | template 8 | struct Overload : Types... { 9 | using Types::operator()...; 10 | }; 11 | void AnimationData::Play(MHMenu& MHMenuToPlay, GUI& GUIToPlay) { 12 | for(auto& CurrentFade : AnimationVector) { 13 | std::visit(Overload{ 14 | [&](FadeHSV& Animation) { 15 | Animation.Play(MHMenuToPlay, GUIToPlay); 16 | }, 17 | [&](FadeRGB& Animation) { 18 | Animation.Play(MHMenuToPlay, GUIToPlay); 19 | }}, 20 | CurrentFade); 21 | } 22 | } 23 | 24 | AnimationData& AnimationData::AddStep(std::variant Step) { 25 | AnimationVector.push_back(Step); 26 | return *this; 27 | } 28 | 29 | std::string& AnimationData::GetName() { 30 | return AnimationName; 31 | } 32 | } -------------------------------------------------------------------------------- /src/animationhandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "colorfade.hpp" 9 | 10 | namespace MHRecolor { 11 | class AnimationData { 12 | private: 13 | std::string AnimationName; 14 | std::vector> AnimationVector; 15 | 16 | public: 17 | AnimationData(std::string AnimationName, std::vector> AnimationVector = {}); 18 | 19 | void Play(MHMenu& MHMenuToPlay, GUI& GUIToPlay); 20 | 21 | AnimationData& AddStep(std::variant Step); 22 | 23 | std::string& GetName(); 24 | }; 25 | } -------------------------------------------------------------------------------- /src/colorfade.cpp: -------------------------------------------------------------------------------- 1 | #include "colorfade.hpp" 2 | 3 | namespace MHRecolor { 4 | FadeRGB::FadeRGB(MHColor Color1, MHColor Color2, int DurationMS) 5 | : m_Color1(Color1), m_Color2(Color2), m_Duration(DurationMS) { 6 | m_DiffR = m_Color2.r - m_Color1.r; 7 | m_DiffG = m_Color2.g - m_Color1.g; 8 | m_DiffB = m_Color2.b - m_Color1.b; 9 | 10 | // absolute value of differences 11 | int AbsDiffR = abs(m_DiffR); 12 | int AbsDiffG = abs(m_DiffG); 13 | int AbsDiffB = abs(m_DiffB); 14 | 15 | m_DiffMax = std::max({AbsDiffR, AbsDiffG, AbsDiffB}); 16 | 17 | if(!m_DiffMax) { 18 | return; 19 | } 20 | 21 | m_IncR = static_cast(m_DiffR) / m_DiffMax; 22 | m_IncG = static_cast(m_DiffG) / m_DiffMax; 23 | m_IncB = static_cast(m_DiffB) / m_DiffMax; 24 | } 25 | 26 | void FadeRGB::Play(MHMenu& MHMenuToPlay, GUI& GUIToPlay) { 27 | if(!m_DiffMax) { 28 | return; 29 | } 30 | 31 | MHColor CurrentColor; 32 | 33 | float SleepDuration = m_Duration < m_DiffMax ? 1.f : m_Duration / m_DiffMax; 34 | unsigned char ValueR = m_Color1.r; 35 | unsigned char ValueG = m_Color1.g; 36 | unsigned char ValueB = m_Color1.b; 37 | 38 | for(int i = 0; i < (m_DiffMax - 1); i++) { 39 | if(!MHMenuToPlay.IsOpen() || !GUIToPlay.GetRainbowEnabled()) { 40 | return; 41 | } 42 | 43 | ValueR += m_IncR; 44 | ValueG += m_IncG; 45 | ValueB += m_IncB; 46 | 47 | CurrentColor = {static_cast(ValueR), static_cast(ValueG), static_cast(ValueB)}; 48 | 49 | MHMenuToPlay.SetColor(CurrentColor); 50 | GUIToPlay.SetGUIColor(CurrentColor); 51 | 52 | std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(SleepDuration))); 53 | } 54 | } 55 | 56 | FadeHSV::FadeHSV(MHColorHSV Color1, MHColorHSV Color2, int DurationMS) 57 | : m_Color1(Color1), m_Color2(Color2), m_Duration(DurationMS) { 58 | m_DiffH = m_Color2.H - m_Color1.H; 59 | m_DiffS = m_Color2.S - m_Color1.S; 60 | m_DiffV = m_Color2.V - m_Color1.V; 61 | 62 | // absolute value of differences 63 | float AbsDiffH = abs(m_DiffH); 64 | float AbsDiffS = abs(m_DiffS); 65 | float AbsDiffV = abs(m_DiffV); 66 | 67 | m_DiffMax = std::max({AbsDiffH, AbsDiffS, AbsDiffV}); 68 | 69 | if(!m_DiffMax) { 70 | return; 71 | } 72 | 73 | m_IncH = m_DiffH / m_DiffMax; 74 | m_IncS = m_DiffS / m_DiffMax; 75 | m_IncV = m_DiffV / m_DiffMax; 76 | } 77 | 78 | void FadeHSV::Play(MHMenu& MHMenuToPlay, GUI& GUIToPlay) { 79 | if(!m_DiffMax) { 80 | return; 81 | } 82 | 83 | MHColor CurrentColor; 84 | float SleepDuration = m_Duration < m_DiffMax ? m_DiffMax : m_Duration / m_DiffMax; 85 | float ValueH = m_Color1.H; 86 | float ValueS = m_Color1.S; 87 | float ValueV = m_Color1.V; 88 | 89 | for(float i = 0.f; i < (m_DiffMax - 1.f); i += 1.f) { 90 | if(!MHMenuToPlay.IsOpen() || !GUIToPlay.GetRainbowEnabled()) { 91 | return; 92 | } 93 | 94 | ValueH += m_IncH; 95 | ValueS += m_IncS; 96 | ValueV += m_IncV; 97 | 98 | CurrentColor = HSVToRGB({ValueH, ValueS, ValueV}); 99 | 100 | MHMenuToPlay.SetColor(CurrentColor); 101 | GUIToPlay.SetGUIColor(CurrentColor); 102 | 103 | std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(SleepDuration))); 104 | } 105 | } 106 | 107 | MHColor FadeRGB::GetLastColor() { 108 | return m_Color2; 109 | } 110 | 111 | MHColorHSV FadeHSV::GetLastColor() { 112 | return m_Color2; 113 | } 114 | } -------------------------------------------------------------------------------- /src/colorfade.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mhcolor.hpp" 4 | #include "mhmenu.hpp" 5 | #include "gui.hpp" 6 | 7 | namespace MHRecolor { 8 | class FadeRGB { 9 | private: 10 | MHColor m_Color1; 11 | MHColor m_Color2; 12 | 13 | int m_DiffR; 14 | int m_DiffG; 15 | int m_DiffB; 16 | int m_DiffMax; // == number of animation steps 17 | 18 | float m_IncR; 19 | float m_IncG; 20 | float m_IncB; 21 | 22 | int m_Duration; 23 | 24 | public: 25 | FadeRGB(MHColor Color1, MHColor Color2, int DurationMS); 26 | 27 | void Play(MHMenu& MHMenuToPlay, GUI& GUIToPlay); 28 | 29 | MHColor GetLastColor(); 30 | }; 31 | 32 | class FadeHSV { 33 | private: 34 | MHColorHSV m_Color1; 35 | MHColorHSV m_Color2; 36 | 37 | float m_DiffH; 38 | float m_DiffS; 39 | float m_DiffV; 40 | float m_DiffMax; // == number of animation steps 41 | 42 | float m_IncH; 43 | float m_IncS; 44 | float m_IncV; 45 | 46 | int m_Duration; 47 | 48 | public: 49 | FadeHSV(MHColorHSV Color1, MHColorHSV Color2, int DurationMS); 50 | 51 | void Play(MHMenu& MHMenuToPlay, GUI& GUIToPlay); 52 | 53 | MHColorHSV GetLastColor(); 54 | }; 55 | } -------------------------------------------------------------------------------- /src/errorcodes.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "errorcodes.hpp" 4 | 5 | namespace MHRecolor { 6 | Errorcode operator|(Errorcode A, Errorcode B) { 7 | return static_cast(static_cast(A) | static_cast(B)); 8 | } 9 | 10 | Errorcode& operator|=(Errorcode& A, Errorcode B) { 11 | return (A = (A | B)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/errorcodes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace MHRecolor { 4 | enum Errorcode : int { 5 | None = 0 << 0, 6 | MHNotFound = 1 << 0, 7 | MHDLLInfoError = 1 << 1, 8 | MHSigsNotFound = 1 << 2, 9 | MHVarsNotFound = 1 << 3, 10 | MHInfoIsNullptr = 1 << 4 11 | }; 12 | 13 | Errorcode operator|(Errorcode A, Errorcode B); 14 | Errorcode& operator|=(Errorcode& A, Errorcode B); 15 | } -------------------------------------------------------------------------------- /src/gui.cpp: -------------------------------------------------------------------------------- 1 | #include "gui.hpp" 2 | 3 | #define ColToImVec(R, G, B, A) ImVec4(R / 255.f, G / 255.f, B / 255.f, A / 255.f) 4 | #define ImVecToRGB(Vec) MHColor(Vec.x * 255.f, Vec[1] * 255.f, Vec[2] * 255.f) 5 | 6 | namespace MHRecolor { 7 | bool GUI::IsInitialized = 0; 8 | float GUI::DesiredColor[3] = {1.f, 1.f, 1.f}; 9 | ImVec4* GUI::GUIColors = 0; 10 | bool GUI::RainbowEnabled = 0; 11 | 12 | Errorcode GUI::Init() { 13 | if(!IsInitialized) { 14 | ImGuiCocos::get() 15 | .setup([&] { 16 | auto* Font = ImGui::GetIO().Fonts->AddFontFromFileTTF((Mod::get()->getResourcesDir() / "Muli.ttf").string().c_str(), 22.0f); 17 | 18 | auto &Style = ImGui::GetStyle(); 19 | Style.WindowPadding = ImVec2(5, 4); 20 | Style.WindowRounding = 0.f; 21 | Style.FramePadding = ImVec2(8, 4); 22 | Style.FrameRounding = 0.f; 23 | Style.PopupRounding = 0.f; 24 | Style.ItemSpacing = ImVec2(12, 3); 25 | Style.ItemInnerSpacing = ImVec2(8, 6); 26 | Style.WindowTitleAlign = ImVec2(0.0f, 0.5f); 27 | Style.IndentSpacing = 25.0f; 28 | Style.ScrollbarSize = 15.0f; 29 | Style.ScrollbarRounding = 9.0f; 30 | Style.GrabMinSize = 5.0f; 31 | Style.GrabRounding = 3.0f; 32 | Style.WindowBorderSize = 0.f; 33 | Style.WindowMinSize = ImVec2(32, 32); 34 | Style.DisplayWindowPadding = ImVec2(0, 0); 35 | Style.WindowMenuButtonPosition = ImGuiDir_Left; 36 | 37 | GUIColors = Style.Colors; 38 | 39 | GUIColors[ImGuiCol_Text] = ColToImVec(0xEB, 0xEB, 0xE3, 0xFF); 40 | GUIColors[ImGuiCol_TextDisabled] = ColToImVec(0xEB, 0xEB, 0xE3, 0x7F); 41 | 42 | GUIColors[ImGuiCol_WindowBg] = ColToImVec(0x2A, 0x2A, 0x2A, 0xFF); 43 | 44 | GUIColors[ImGuiCol_TitleBg] = 45 | GUIColors[ImGuiCol_TitleBgActive] = 46 | GUIColors[ImGuiCol_TitleBgCollapsed] = 47 | ColToImVec(0xAD, 0x62, 0xEE, 0xFF); 48 | 49 | GUIColors[ImGuiCol_Button] = ColToImVec(0x2A, 0x2A, 0x2A, 0xFF); 50 | GUIColors[ImGuiCol_ButtonHovered] = ColToImVec(0x1B, 0x1B, 0x1B, 0xFF); 51 | GUIColors[ImGuiCol_ButtonActive] = ColToImVec(0x0F, 0x0F, 0x0F, 0xFF); 52 | 53 | GUIColors[ImGuiCol_FrameBg] = ColToImVec(0x1B, 0x1B, 0x1B, 0xFF); 54 | GUIColors[ImGuiCol_FrameBgHovered] = ColToImVec(0x33, 0x33, 0x33, 0xFF); 55 | GUIColors[ImGuiCol_FrameBgActive] = ColToImVec(0x1E, 0x1E, 0x1E, 0xFF); 56 | 57 | GUIColors[ImGuiCol_ScrollbarBg] = ColToImVec(0x1B, 0x1B, 0x1B, 0xFF); 58 | GUIColors[ImGuiCol_ScrollbarGrab] = ColToImVec(0x64, 0x64, 0x64, 0xFF); 59 | GUIColors[ImGuiCol_ScrollbarGrabHovered] = ColToImVec(0x33, 0x33, 0x33, 0xFF); 60 | GUIColors[ImGuiCol_ScrollbarGrabActive] = ColToImVec(0x1E, 0x1E, 0x1E, 0xFF); 61 | 62 | GUIColors[ImGuiCol_CheckMark] = ColToImVec(0xAD, 0x62, 0xEE, 0xFF); 63 | GUIColors[ImGuiCol_SliderGrab] = ColToImVec(0x0F, 0x0F, 0x0F, 0xFF); 64 | GUIColors[ImGuiCol_SliderGrabActive] = ColToImVec(0xAD, 0x62, 0xEE, 0xFF); 65 | 66 | GUIColors[ImGuiCol_Border] = ColToImVec(0x00, 0x00, 0x00, 0x00); 67 | GUIColors[ImGuiCol_BorderShadow] = ColToImVec(0x00, 0x00, 0x00, 0x00); 68 | 69 | GUIColors[ImGuiCol_PopupBg] = ColToImVec(0x2A, 0x2A, 0x2A, 0xFF); 70 | GUIColors[ImGuiCol_Header] = ColToImVec(0xAD, 0x62, 0xEE, 0xFF); 71 | GUIColors[ImGuiCol_HeaderHovered] = ColToImVec(0xAD, 0x62, 0xEE, 0xFF); 72 | GUIColors[ImGuiCol_HeaderActive] = ColToImVec(0xAD, 0x62, 0xEE, 0xFF); 73 | }) 74 | .draw([&] { 75 | ImGui::Begin("MH Recolor"); 76 | ImGui::ColorEdit3("Menu Color", DesiredColor); 77 | ImGui::Checkbox("Rainbow", &RainbowEnabled); 78 | ImGui::End(); 79 | }) 80 | .setVisible(0); 81 | } 82 | IsInitialized = 1; 83 | return Errorcode::None; 84 | } 85 | 86 | bool GUI::SetVisible(bool SetVisibility) { 87 | if(LastVisibility != SetVisibility) { 88 | ImGuiCocos::get().setVisible(SetVisibility); 89 | LastVisibility = SetVisibility; 90 | } 91 | return SetVisibility; 92 | } 93 | 94 | void GUI::SetGUIColor(MHColor Color) { 95 | GUIColors[ImGuiCol_CheckMark] = 96 | GUIColors[ImGuiCol_SliderGrabActive] = 97 | GUIColors[ImGuiCol_HeaderHovered] = 98 | GUIColors[ImGuiCol_HeaderActive] = 99 | GUIColors[ImGuiCol_TitleBg] = 100 | GUIColors[ImGuiCol_TitleBgActive] = 101 | GUIColors[ImGuiCol_TitleBgCollapsed] = 102 | ColToImVec(Color.r, Color.g, Color.b, 0xFF); 103 | } 104 | 105 | MHColor GUI::GetDesiredColor() { 106 | return { 107 | static_cast(std::round(DesiredColor[0] * 255.f)), 108 | static_cast(std::round(DesiredColor[1] * 255.f)), 109 | static_cast(std::round(DesiredColor[2] * 255.f)) 110 | }; 111 | } 112 | 113 | void GUI::SetDesiredColor(MHColor Color) { 114 | DesiredColor[0] = static_cast(Color.r / 255.f); 115 | DesiredColor[1] = static_cast(Color.g / 255.f); 116 | DesiredColor[2] = static_cast(Color.b / 255.f); 117 | } 118 | 119 | bool GUI::GetRainbowEnabled() { 120 | return RainbowEnabled; 121 | } 122 | 123 | void GUI::SetRainbowEnabled(bool Bool) { 124 | RainbowEnabled = Bool; 125 | } 126 | } -------------------------------------------------------------------------------- /src/gui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace geode::prelude; 5 | 6 | #include 7 | 8 | #include "mhcolor.hpp" 9 | #include "errorcodes.hpp" 10 | 11 | namespace MHRecolor { 12 | class GUI { 13 | private: 14 | static bool IsInitialized; 15 | static float DesiredColor[3]; 16 | static ImVec4* GUIColors; 17 | static bool RainbowEnabled; 18 | 19 | 20 | bool LastVisibility = 0; 21 | 22 | public: 23 | GUI() = default; 24 | 25 | Errorcode Init(); 26 | 27 | bool SetVisible(bool SetVisibility); 28 | 29 | void SetGUIColor(MHColor Color); 30 | 31 | MHColor GetDesiredColor(); 32 | 33 | void SetDesiredColor(MHColor Color); 34 | 35 | bool GetRainbowEnabled(); 36 | 37 | void SetRainbowEnabled(bool Bool); 38 | }; 39 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace geode::prelude; 3 | 4 | #include "manager.hpp" 5 | 6 | $on_mod(Loaded) { 7 | MHRecolor::Manager::Init(); 8 | MHRecolor::Manager::LoadSave(); 9 | MHRecolor::Manager::StartGUIThread(); 10 | } 11 | 12 | $on_mod(DataSaved) { 13 | MHRecolor::Manager::SaveSave(); 14 | } -------------------------------------------------------------------------------- /src/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "manager.hpp" 2 | 3 | namespace MHRecolor{ 4 | MHInfo Manager::Info; 5 | MHMenu Manager::Menu; 6 | GUI Manager::MHRCGUI; 7 | Errorcode Manager::Errors; 8 | 9 | std::vector Manager::Animations; 10 | 11 | Errorcode Manager::Init() { 12 | Errors |= Info.Init(); 13 | if(Errors != Errorcode::None) { 14 | ShowErrorMessage(); 15 | return Errors; 16 | } 17 | Errors |= Menu.Init(Info); 18 | if(Errors != Errorcode::None) { 19 | ShowErrorMessage(); 20 | return Errors; 21 | } 22 | Errors |= MHRCGUI.Init(); 23 | if(Errors != Errorcode::None) { 24 | ShowErrorMessage(); 25 | return Errors; 26 | } 27 | 28 | return Errorcode::None; 29 | } 30 | 31 | void Manager::ShowErrorMessage() { 32 | std::string ErrorMessage = "The following errors have occured:\n"; 33 | 34 | if(Errors & Errorcode::MHNotFound) { 35 | ErrorMessage += "- MH couldn't be found (Check if MH is installed)\n"; 36 | } 37 | if(Errors & Errorcode::MHDLLInfoError) { 38 | ErrorMessage += "- Couldn't retrieve MH's DLL info\n"; 39 | } 40 | if(Errors & Errorcode::MHSigsNotFound) { 41 | ErrorMessage += "- Couldn't find all function signatures (Wait for update)\n"; 42 | } 43 | if(Errors & Errorcode::MHVarsNotFound) { 44 | ErrorMessage += "- Couldn't find all important variables (Wait for Update) \n"; 45 | } 46 | if(Errors & Errorcode::MHInfoIsNullptr) { 47 | ErrorMessage += "- Something went really wrong (DM me)\n"; 48 | } 49 | 50 | auto Alert = geode::createQuickPopup( 51 | "MH Recolor", 52 | ErrorMessage, 53 | "OK", nullptr, 54 | [&](auto This, bool) { 55 | SceneManager::get()->forget(This); 56 | } 57 | ); 58 | SceneManager::get()->keepAcrossScenes(Alert); 59 | } 60 | 61 | void Manager::LoadSave() { 62 | if(Errors == Errorcode::None) { 63 | SaveData Data = Mod::get()->getSavedValue("Data"); 64 | 65 | MHRCGUI.SetDesiredColor(Data.MenuColor); 66 | MHRCGUI.SetRainbowEnabled(Data.RainbowEnabled); 67 | } 68 | } 69 | 70 | void Manager::SaveSave() { 71 | if(Errors == Errorcode::None) { 72 | SaveData Data(MHRCGUI.GetDesiredColor(), MHRCGUI.GetRainbowEnabled()); 73 | 74 | Mod::get()->setSavedValue("Data", Data); 75 | } 76 | } 77 | 78 | bool Manager::AddAnimation(AnimationData& Animation) { 79 | for(auto& CurrentAnimation : Animations) { 80 | if(CurrentAnimation.GetName() == Animation.GetName()) { 81 | return 0; 82 | } 83 | } 84 | Animations.push_back(Animation); 85 | return 1; 86 | } 87 | 88 | void Manager::SetColorDesired() { 89 | MHColor DesiredColor(MHRCGUI.GetDesiredColor()); 90 | Menu.SetColor(DesiredColor); 91 | MHRCGUI.SetGUIColor(DesiredColor); 92 | } 93 | 94 | void Manager::PlayAnimation(std::string AnimationName) { 95 | for(auto& CurrentAnimation : Animations) { 96 | if(CurrentAnimation.GetName() == AnimationName) { 97 | CurrentAnimation.Play(Menu, MHRCGUI); 98 | } 99 | } 100 | } 101 | 102 | void Manager::UpdateMenu() { 103 | if(MHRCGUI.SetVisible(Menu.IsOpen())) { 104 | MHColor DesiredColor(MHRCGUI.GetDesiredColor()); 105 | if(MHRCGUI.GetRainbowEnabled()) { 106 | PlayAnimation("Rainbow"); 107 | } else { 108 | SetColorDesired(); 109 | } 110 | } 111 | } 112 | 113 | void Manager::GUIThread() { 114 | AnimationData RainbowAnim("Rainbow"); 115 | RainbowAnim.AddStep(FadeHSV({0.f, 90.f, 90.f}, {360.f, 90.f, 90.f}, 1750)); 116 | 117 | AddAnimation(RainbowAnim); 118 | 119 | while(1) { 120 | UpdateMenu(); 121 | } 122 | } 123 | 124 | void Manager::StartGUIThread() { 125 | if(Errors == Errorcode::None){ 126 | std::thread(GUIThread).detach(); 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "animationhandler.hpp" 6 | #include "colorfade.hpp" 7 | #include "gui.hpp" 8 | #include "mhcolor.hpp" 9 | #include "mhinfo.hpp" 10 | #include "mhmenu.hpp" 11 | #include "savedata.hpp" 12 | 13 | namespace MHRecolor { 14 | class Manager { 15 | private: 16 | static MHInfo Info; 17 | static MHMenu Menu; 18 | static GUI MHRCGUI; 19 | static Errorcode Errors; 20 | 21 | static std::vector Animations; 22 | 23 | public: 24 | static Errorcode Init(); 25 | static void ShowErrorMessage(); 26 | 27 | static void LoadSave(); 28 | 29 | static void SaveSave(); // 🔥 🔥 🔥 30 | 31 | static bool AddAnimation(AnimationData& Animation); 32 | static void SetColorDesired(); 33 | static void PlayAnimation(std::string AnimationName); 34 | static void UpdateMenu(); 35 | 36 | private: 37 | static void GUIThread(); 38 | 39 | public: 40 | static void StartGUIThread(); 41 | }; 42 | } -------------------------------------------------------------------------------- /src/mhcolor.cpp: -------------------------------------------------------------------------------- 1 | #include "mhcolor.hpp" 2 | 3 | namespace MHRecolor { 4 | MHColor& MHColor::operator*=(MHColor& OtherColor) { 5 | float OriginalColor[3] = {r / 255.f, g / 255.f, b / 255.f}; 6 | float OverlayColor[3] = {OtherColor.r / 255.f, OtherColor.g / 255.f, OtherColor.b / 255.f}; 7 | 8 | r = static_cast(255.f * (OriginalColor[0] * OverlayColor[0])); 9 | g = static_cast(255.f * (OriginalColor[1] * OverlayColor[1])); 10 | b = static_cast(255.f * (OriginalColor[2] * OverlayColor[2])); 11 | 12 | return *this; 13 | } 14 | 15 | MHColor operator*(MHColor MainColor, MHColor& OtherColor) { 16 | return MainColor *= OtherColor; 17 | } 18 | 19 | MHColorHSV::MHColorHSV(float H, float S, float V) 20 | : H(H), S(S), V(V) { 21 | } 22 | 23 | MHColor::MHColor(unsigned char r, unsigned char g, unsigned char b) 24 | : r(r), g(g), b(b) { 25 | } 26 | 27 | MHColorHSV::MHColorHSV(std::initializer_list InitList) { 28 | if(InitList.size() != 3) { 29 | *this = MHColorHSV(); 30 | } else { 31 | *this = MHColorHSV(InitList.begin()[0], InitList.begin()[1], InitList.begin()[2]); 32 | } 33 | } 34 | 35 | MHColor::MHColor(std::initializer_list InitList) { 36 | if(InitList.size() != 3) { 37 | *this = MHColor(); 38 | } else { 39 | // fuck you microsoft how difficult is it to add one operator 40 | *this = MHColor(InitList.begin()[0], InitList.begin()[1], InitList.begin()[2]); 41 | } 42 | } 43 | 44 | MHColor HSVToRGB(MHColorHSV Color) { 45 | float H = Color.H; 46 | float S = Color.S; 47 | float V = Color.V; 48 | 49 | if((H > 360) || (H < 0) || (S > 100) || (S < 0) || (V > 100) || (V < 0)) { 50 | return {0, 0, 0}; 51 | } 52 | 53 | float s = S / 100; 54 | float v = V / 100; 55 | float C = s * v; 56 | float X = C * (1 - abs(fmod(H / 60.0, 2) - 1)); 57 | float m = v - C; 58 | float _r, _g, _b; 59 | 60 | if(H >= 0 && H < 60) { 61 | _r = C, _g = X, _b = 0; 62 | } else if(H >= 60 && H < 120) { 63 | _r = X, _g = C, _b = 0; 64 | } else if(H >= 120 && H < 180) { 65 | _r = 0, _g = C, _b = X; 66 | } else if(H >= 180 && H < 240) { 67 | _r = 0, _g = X, _b = C; 68 | } else if(H >= 240 && H < 300) { 69 | _r = X, _g = 0, _b = C; 70 | } else { 71 | _r = C, _g = 0, _b = X; 72 | } 73 | unsigned char R = (_r + m) * 255; 74 | unsigned char G = (_g + m) * 255; 75 | unsigned char B = (_b + m) * 255; 76 | 77 | return {R, G, B}; 78 | } 79 | 80 | MHColorHSV RGBToHSV(MHColor In) { 81 | MHColorHSV Out; 82 | float Min, Max, Delta; 83 | 84 | Min = In.r < In.g ? In.r : In.g; 85 | Min = Min < In.b ? Min : In.b; 86 | 87 | Max = In.r > In.g ? In.r : In.g; 88 | Max = Max > In.b ? Max : In.b; 89 | 90 | Out.V = (Max / 255.f) * 100.f; 91 | Delta = Max - Min; 92 | if(Delta < 0.00001f) { 93 | Out.S = 0; 94 | Out.H = 0; 95 | return Out; 96 | } 97 | if(Max > 0.f) { 98 | Out.S = (Delta / Max) * 100; 99 | } else { 100 | Out.S = 0.0; 101 | Out.H = NAN; 102 | return Out; 103 | } 104 | if(In.r >= Max) 105 | Out.H = (In.g - In.b) / Delta; 106 | else if(In.g >= Max) 107 | Out.H = 2.f + (In.b - In.r) / Delta; 108 | else 109 | Out.H = 4.f + (In.r - In.g) / Delta; 110 | 111 | Out.H *= 60.f; 112 | 113 | if(Out.H < 0.f) 114 | Out.H += 360.f; 115 | 116 | return Out; 117 | } 118 | } -------------------------------------------------------------------------------- /src/mhcolor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace MHRecolor { 7 | enum ColorType : int { 8 | RGB = 1 << 0, 9 | HSV = 1 << 1 10 | }; 11 | 12 | struct MHColorHSV { 13 | float H, S ,V; 14 | 15 | MHColorHSV(float H = 0.f, float S = 0.f, float V = 0.f); 16 | MHColorHSV(std::initializer_list InitList); 17 | }; 18 | 19 | 20 | struct MHColor { 21 | unsigned char r, g, b; 22 | 23 | MHColor& operator*=(MHColor& OtherColor); 24 | 25 | MHColor(unsigned char r = 0, unsigned char g = 0, unsigned char b = 0); 26 | MHColor(std::initializer_list InitList); 27 | }; 28 | 29 | MHColor operator*(MHColor MainColor, MHColor& OtherColor); 30 | 31 | MHColor HSVToRGB(MHColorHSV Color); 32 | 33 | MHColorHSV RGBToHSV(MHColor Color); 34 | } -------------------------------------------------------------------------------- /src/mhinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "mhinfo.hpp" 2 | 3 | /* 4 | Not needed anymore but I'll leave it here in case I need it again 5 | 6 | Ik this is hacky asf but I didn't feel like dealing with mutexes 7 | #define WAIT_UNTIL_TRUE(Condition) \ 8 | do { \ 9 | for(volatile bool B; B = !(Condition);); \ 10 | } while(0) 11 | */ 12 | 13 | namespace MHRecolor { 14 | template 15 | T MHInfo::GetPtrFromRIPDepMOV(uintptr_t InstructionAddress, uintptr_t InstructionSize) { 16 | uintptr_t RIP = InstructionAddress + InstructionSize; 17 | uintptr_t Offset = *reinterpret_cast(InstructionAddress + InstructionSize - 0x04); 18 | 19 | return reinterpret_cast(RIP + Offset); 20 | } 21 | 22 | bool MH__MenuIsOpen = 0; 23 | void MH__SetMenuVisible(uintptr_t RCX, float XMM1) { 24 | *(float*)(RCX + 0x18C) = XMM1; // | movss [rcx+Geode.s_addressStack+14],xmm1 25 | MH__MenuIsOpen = *(bool*)(RCX + 0x18F); // + Lowest byte of XMM1 - Actually a float but its only a non-zero bool when open as well 26 | return; // | ret 27 | } 28 | 29 | Errorcode MHInfo::Init() { 30 | MHBase = reinterpret_cast(GetModuleHandle("absolllute.megahack.dll")); 31 | 32 | if(MHBase == NULL) { 33 | return Errorcode::MHNotFound; 34 | } 35 | 36 | MODULEINFO MHDLLInfo = {0}; 37 | if(!GetModuleInformation(GetCurrentProcess(), reinterpret_cast(MHBase), &MHDLLInfo, sizeof(MHDLLInfo))) { 38 | return Errorcode::MHDLLInfoError; 39 | } 40 | 41 | MHDrawGUI_o = (uintptr_t)Sig::find(reinterpret_cast(MHBase), MHDLLInfo.SizeOfImage, "0F B6 41 23 0F 57 D2"); 42 | MHRandomAssFunc_o = (uintptr_t)Sig::find(reinterpret_cast(MHBase), MHDLLInfo.SizeOfImage, "F3 0F 11 89 ? ? ? ? C3"); 43 | 44 | if(!MHDrawGUI_o || !MHRandomAssFunc_o) { 45 | return Errorcode::MHSigsNotFound; 46 | } 47 | 48 | MHMenuColor = GetPtrFromRIPDepMOV(MHDrawGUI_o + 0x0F, 0x06); 49 | MHMenuColorT = GetPtrFromRIPDepMOV(MHDrawGUI_o + 0x4D, 0x06); 50 | MHMenuIsOpen = &MH__MenuIsOpen; 51 | 52 | if(!MHMenuColor || !MHMenuColorT || !MHMenuIsOpen) { 53 | return Errorcode::MHVarsNotFound; 54 | } 55 | 56 | auto Result = Mod::get()->hook( 57 | reinterpret_cast(MHRandomAssFunc_o), 58 | &MH__SetMenuVisible, 59 | "MH::SetMenuVisible", 60 | tulip::hook::TulipConvention::Fastcall); 61 | 62 | return Errorcode::None; 63 | } 64 | 65 | MHColor MHInfo::GetMenuColor() { 66 | return *MHMenuColor; 67 | } 68 | 69 | MHColor MHInfo::GetMenuColorT() { 70 | return *MHMenuColorT; 71 | } 72 | 73 | void MHInfo::SetMenuColor(MHColor Color) { 74 | *MHMenuColor = Color; 75 | } 76 | 77 | void MHInfo::SetMenuColorT(MHColor Color) { 78 | *MHMenuColorT = Color; 79 | } 80 | 81 | bool MHInfo::GetMenuState() { 82 | return *MHMenuIsOpen; 83 | } 84 | 85 | bool MHInfo::InitSuccess() { 86 | return Success; 87 | } 88 | } -------------------------------------------------------------------------------- /src/mhinfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "mhcolor.hpp" 9 | #include "errorcodes.hpp" 10 | 11 | #include 12 | using namespace geode::prelude; 13 | 14 | namespace MHRecolor { 15 | class MHInfo { 16 | public: 17 | uintptr_t MHBase = 0; 18 | 19 | uintptr_t MHDrawGUI_o = 0; 20 | uintptr_t MHRandomAssFunc_o = 0; 21 | 22 | MHColor* MHMenuColor = 0; 23 | MHColor* MHMenuColorT = 0; 24 | bool* MHMenuIsOpen = 0; 25 | 26 | bool Success = 0; 27 | 28 | private: 29 | // Gets Final Pointer from an RIP dependant MOV instruction as type T 30 | template 31 | T GetPtrFromRIPDepMOV(uintptr_t InstructionAddress, uintptr_t InstructionSize); 32 | 33 | public: 34 | MHInfo() = default; 35 | 36 | Errorcode Init(); 37 | 38 | MHColor GetMenuColor(); 39 | 40 | MHColor GetMenuColorT(); 41 | 42 | void SetMenuColor(MHColor Color); 43 | 44 | void SetMenuColorT(MHColor Color); 45 | 46 | bool GetMenuState(); 47 | 48 | bool InitSuccess(); 49 | }; 50 | } -------------------------------------------------------------------------------- /src/mhmenu.cpp: -------------------------------------------------------------------------------- 1 | #include "mhmenu.hpp" 2 | 3 | namespace MHRecolor { 4 | Errorcode MHMenu::Init(MHInfo& InitInfo) { 5 | Info = &InitInfo; 6 | if(Info == nullptr) { 7 | return Errorcode::MHInfoIsNullptr; 8 | } 9 | return Errorcode::None; 10 | } 11 | 12 | void MHMenu::SetColor(MHColor Color) { 13 | Info->SetMenuColor(Color); 14 | Info->SetMenuColorT(Color * OverlayColor); 15 | } 16 | 17 | bool MHMenu::IsOpen() { 18 | return Info->GetMenuState(); 19 | } 20 | } -------------------------------------------------------------------------------- /src/mhmenu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mhinfo.hpp" 4 | #include "mhcolor.hpp" 5 | #include "errorcodes.hpp" 6 | 7 | namespace MHRecolor { 8 | class MHMenu { 9 | private: 10 | MHInfo* Info = nullptr; 11 | 12 | // Color that gets overlayed onto options that are disabled when searching 13 | MHColor OverlayColor = {0x80, 0x80, 0x80}; 14 | 15 | public: 16 | MHMenu() = default; 17 | 18 | Errorcode Init(MHInfo& Info); 19 | 20 | void SetColor(MHColor Color); 21 | 22 | bool IsOpen(); 23 | }; 24 | } -------------------------------------------------------------------------------- /src/savedata.cpp: -------------------------------------------------------------------------------- 1 | #include "savedata.hpp" 2 | 3 | bool matjson::Serialize::is_json(matjson::Value const& Value) { 4 | return (Value.contains("MenuColor") && 5 | Value.contains("RainbowEnabled") && 6 | Value["MenuColor"]["R"].is_number() && 7 | Value["MenuColor"]["G"].is_number() && 8 | Value["MenuColor"]["B"].is_number() && 9 | Value["RainbowEnabled"].is_bool()); 10 | } 11 | 12 | MHRecolor::SaveData matjson::Serialize::from_json(matjson::Value const& Value) { 13 | return MHRecolor::SaveData( 14 | {static_cast(Value["MenuColor"]["R"].as_int()), 15 | static_cast(Value["MenuColor"]["G"].as_int()), 16 | static_cast(Value["MenuColor"]["B"].as_int())}, 17 | Value["RainbowEnabled"].as_bool()); 18 | } 19 | 20 | matjson::Value matjson::Serialize::to_json(MHRecolor::SaveData const& Value) { 21 | auto Obj = matjson::Object(); 22 | Obj["MenuColor"]["R"] = static_cast(Value.MenuColor.r); 23 | Obj["MenuColor"]["G"] = static_cast(Value.MenuColor.g); 24 | Obj["MenuColor"]["B"] = static_cast(Value.MenuColor.b); 25 | Obj["RainbowEnabled"] = Value.RainbowEnabled; 26 | return Obj; 27 | } -------------------------------------------------------------------------------- /src/savedata.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using namespace geode::prelude; 5 | 6 | #include "mhcolor.hpp" 7 | 8 | namespace MHRecolor { 9 | struct SaveData { 10 | MHColor MenuColor = {0x00, 0x00, 0x00}; 11 | bool RainbowEnabled = 0; 12 | 13 | SaveData() = default; 14 | SaveData(MHColor MenuColor, bool RainbowEnabled) 15 | : MenuColor(MenuColor), RainbowEnabled(RainbowEnabled) {} 16 | }; 17 | } 18 | 19 | template<> 20 | struct matjson::Serialize { 21 | static bool is_json(matjson::Value const& Value); 22 | static MHRecolor::SaveData from_json(matjson::Value const& Value); 23 | static matjson::Value to_json(MHRecolor::SaveData const& Value); 24 | }; 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | struct MyCustomSaveData { 41 | int x; 42 | int y; 43 | }; 44 | 45 | template<> 46 | struct matjson::Serialize { 47 | static bool is_json(matjson::Value const& Value) { 48 | return Value.is_object(); 49 | } 50 | 51 | static MyCustomSaveData from_json(matjson::Value const& value) { 52 | return MyCustomSaveData { 53 | .x = value["x"].as_int(), 54 | .y = value["y"].as_int() 55 | }; 56 | } 57 | 58 | static matjson::Value to_json(MyCustomSaveData const& value) { 59 | auto obj = matjson::Object(); 60 | obj["x"] = value.x; 61 | obj["y"] = value.y; 62 | return obj; 63 | } 64 | }; --------------------------------------------------------------------------------