From f0afe7b486f0c9acf17c17595464cd1d85d620bf Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:30:47 +0000 Subject: [PATCH 01/16] achievement_menu: update sprite animation --- UnleashedRecomp/ui/achievement_menu.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index 23325211..14e5d107 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -23,8 +23,6 @@ constexpr double CONTENT_CONTAINER_COMMON_MOTION_END = 12; constexpr double COUNTER_INTRO_FADE_START = 15; constexpr double COUNTER_INTRO_FADE_END = 16; -constexpr double COUNTER_SPRITE_FRAME_START = 0; -constexpr double COUNTER_SPRITE_FRAME_END = 30; constexpr double SELECTION_CONTAINER_BREATHE = 30; @@ -371,10 +369,16 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max) ImVec2 imageMin = { max.x - imageSize - imageMarginX, min.y - imageSize - imageMarginY }; ImVec2 imageMax = { imageMin.x + imageSize, imageMin.y + imageSize }; - auto frm = int32_t(floor(ImGui::GetTime() * 30.0f)) % 30; - auto w = 256.0f / 7680.0f; - auto uv0 = ImVec2(frm * w, 0); - auto uv1 = ImVec2((frm + 1) * w, 1); + constexpr auto columns = 8; + constexpr auto rows = 4; + constexpr auto spriteSize = 256.0f; + constexpr auto textureWidth = 2048.0f; + constexpr auto textureHeight = 1024.0f; + auto frameIndex = int32_t(floor(ImGui::GetTime() * 30.0f)) % 30; + auto columnIndex = frameIndex % columns; + auto rowIndex = frameIndex / columns; + auto uv0 = ImVec2(columnIndex * spriteSize / textureWidth, rowIndex * spriteSize / textureHeight); + auto uv1 = ImVec2((columnIndex + 1) * spriteSize / textureWidth, (rowIndex + 1) * spriteSize / textureHeight); drawList->AddImage(g_upTrophyIcon.get(), imageMin, imageMax, uv0, uv1, IM_COL32(255, 255, 255, 255 * alpha)); @@ -630,7 +634,7 @@ void AchievementMenu::Init() g_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE); size_t bufferSize = 0; - auto buffer = ReadAllBytes("achievements_trophy.png", bufferSize); + auto buffer = ReadAllBytes("trophy.dds", bufferSize); if (!bufferSize) return; From 12b808d1907a3c6fef52f8e067ad53da8a54f81b Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:31:49 +0000 Subject: [PATCH 02/16] Update resources submodule --- UnleashedRecompResources | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnleashedRecompResources b/UnleashedRecompResources index 7183c6e0..cc4733bc 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit 7183c6e0e90480fe0f2c270c87cea5cd5df7d5c3 +Subproject commit cc4733bcf42fb040efbf086b15f64e762753c6c0 From 635b0ebcd15083495eaad045776c3f410ae506c5 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:41:58 +0000 Subject: [PATCH 03/16] Move icon header generation to resources submodule --- CMakeLists.txt | 1 + UnleashedRecomp/CMakeLists.txt | 4 ---- UnleashedRecompResources | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c605965..7bee2f63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,5 +19,6 @@ project("UnleashedRecomp-ALL") include("thirdparty/PowerRecomp/cmake/bin2h.cmake") # Include sub-projects. +add_subdirectory("UnleashedRecompResources") add_subdirectory("UnleashedRecompLib") add_subdirectory("UnleashedRecomp") diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 5d8972f1..35bddb40 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -23,10 +23,6 @@ add_compile_definitions( _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # Microsoft wtf? _CRT_SECURE_NO_WARNINGS) -# Generate icon bitmap header for SDL surface. -BIN2H(SOURCE_FILE "../UnleashedRecompResources/images/game_icon.bmp" HEADER_FILE "res/icon.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_icon") -BIN2H(SOURCE_FILE "../UnleashedRecompResources/images/game_icon_night.bmp" HEADER_FILE "res/icon_night.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_iconNight") - set(SWA_PRECOMPILED_HEADERS "stdafx.h" ) diff --git a/UnleashedRecompResources b/UnleashedRecompResources index cc4733bc..48e641b5 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit cc4733bcf42fb040efbf086b15f64e762753c6c0 +Subproject commit 48e641b58a09c9ecfcf72c60d97bb1079deb5ea4 From b42b70dfece35652b0221d638b3b47e793c272af Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:42:28 +0000 Subject: [PATCH 04/16] window: update icon resources --- UnleashedRecomp/ui/window.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UnleashedRecomp/ui/window.h b/UnleashedRecomp/ui/window.h index bceb219d..d6647fc2 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include "../UnleashedRecompResources/images/game_icon.h" +#include "../UnleashedRecompResources/images/game_icon_night.h" #include #include @@ -46,11 +46,11 @@ public: { if (isNight) { - SetIcon((void*)g_iconNight, g_iconNight_size); + SetIcon((void*)g_res_game_icon_night, g_res_game_icon_night_size); } else { - SetIcon((void*)g_icon, g_icon_size); + SetIcon((void*)g_res_game_icon, g_res_game_icon_size); } } From 12cdb73023cc6c8248cc1c0bcc8353dce4881c4c Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 01:04:38 +0000 Subject: [PATCH 05/16] Added file_to_c --- CMakeLists.txt | 5 +- tools/CMakeLists.txt | 1 + tools/file_to_c/CMakeLists.txt | 27 +++++++++ tools/file_to_c/file_to_c.cpp | 105 +++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 tools/CMakeLists.txt create mode 100644 tools/file_to_c/CMakeLists.txt create mode 100644 tools/file_to_c/file_to_c.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bee2f63..06200e46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 3.20) include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) set(SWA_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty) +set(SWA_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools) set(CMAKE_CXX_STANDARD 23) set(BUILD_SHARED_LIBS OFF) @@ -14,9 +15,9 @@ endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") add_subdirectory(${SWA_THIRDPARTY_ROOT}) -project("UnleashedRecomp-ALL") +add_subdirectory(${SWA_TOOLS_ROOT}) -include("thirdparty/PowerRecomp/cmake/bin2h.cmake") +project("UnleashedRecomp-ALL") # Include sub-projects. add_subdirectory("UnleashedRecompResources") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..d732343c --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(${SWA_TOOLS_ROOT}/file_to_c) diff --git a/tools/file_to_c/CMakeLists.txt b/tools/file_to_c/CMakeLists.txt new file mode 100644 index 00000000..ddf3fa9d --- /dev/null +++ b/tools/file_to_c/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.20) + +include(CMakeParseArguments) + +project("file_to_c") +set(CMAKE_CXX_STANDARD 17) + +add_executable(file_to_c "file_to_c.cpp") + +function(BIN2C) + cmake_parse_arguments(BIN2C_ARGS "" "SOURCE_FILE;DEST_FILE;ARRAY_TYPE;ARRAY_NAME" "" ${ARGN}) + + if(NOT BIN2C_ARGS_SOURCE_FILE) + message(FATAL_ERROR "SOURCE_FILE not specified.") + endif() + + if(NOT BIN2C_ARGS_DEST_FILE) + set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}") + endif() + + add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c" + COMMAND file_to_c "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_ARRAY_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h" + DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" file_to_c + BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h" + COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..." + ) +endfunction() diff --git a/tools/file_to_c/file_to_c.cpp b/tools/file_to_c/file_to_c.cpp new file mode 100644 index 00000000..4d69e8c9 --- /dev/null +++ b/tools/file_to_c/file_to_c.cpp @@ -0,0 +1,105 @@ +/* + MIT License + + Copyright (c) 2024 RT64 Contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include +#include +#include +#include + +std::vector read_file(const char* path) { + std::ifstream input_file{path, std::ios::binary}; + std::vector ret{}; + + if (!input_file.good()) { + return ret; + } + + // Get the length of the file + input_file.seekg(0, std::ios::end); + ret.resize(input_file.tellg()); + + // Read the file contents into the vector + input_file.seekg(0, std::ios::beg); + input_file.read(ret.data(), ret.size()); + + return ret; +} + +void create_parent_if_needed(const char* path) { + std::filesystem::path parent_path = std::filesystem::path{path}.parent_path(); + if (!parent_path.empty()) { + std::filesystem::create_directories(parent_path); + } +} + +int main(int argc, const char** argv) { + if (argc != 6) { + printf("Usage: %s [input file] [array name] [array type] [output C file] [output C header]\n", argv[0]); + return EXIT_SUCCESS; + } + + const char* input_path = argv[1]; + const char* array_name = argv[2]; + const char* array_type = argv[3]; + const char* output_c_path = argv[4]; + const char* output_h_path = argv[5]; + + // Read the input file's contents + std::vector contents = read_file(input_path); + + if (contents.empty()) { + fprintf(stderr, "Failed to open file %s! (Or it's empty)\n", input_path); + return EXIT_FAILURE; + } + + // Create the output directories if they don't exist + create_parent_if_needed(output_c_path); + create_parent_if_needed(output_h_path); + + // Write the C file with the array + { + std::ofstream output_c_file{output_c_path}; + output_c_file << "extern " << array_type << " " << array_name << "[" << contents.size() << "];\n"; + output_c_file << array_type << " " << array_name << "[" << contents.size() << "] = {"; + + for (char x : contents) { + output_c_file << (int)x << ", "; + } + + output_c_file << "};\n"; + } + + // Write the header file with the extern array + { + std::ofstream output_h_file{output_h_path}; + output_h_file << + "#ifdef __cplusplus\n" + " extern \"C\" {\n" + "#endif\n" + "extern " << array_type << " " << array_name << "[" << contents.size() << "];\n" + "#ifdef __cplusplus\n" + " }\n" + "#endif\n"; + } +} From 903c94c8e3726d3185652914f16b69b435e29163 Mon Sep 17 00:00:00 2001 From: Dario Date: Mon, 2 Dec 2024 23:20:16 -0300 Subject: [PATCH 06/16] Fixes to conversion. --- CMakeLists.txt | 1 - UnleashedRecomp/CMakeLists.txt | 33 +++++++++++++++++++++++++++++++++ UnleashedRecomp/res/.gitignore | 1 + UnleashedRecomp/ui/window.h | 8 ++++---- tools/file_to_c/CMakeLists.txt | 19 ------------------- 5 files changed, 38 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06200e46..853ef904 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,5 @@ add_subdirectory(${SWA_TOOLS_ROOT}) project("UnleashedRecomp-ALL") # Include sub-projects. -add_subdirectory("UnleashedRecompResources") add_subdirectory("UnleashedRecompLib") add_subdirectory("UnleashedRecomp") diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 35bddb40..39545832 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -3,6 +3,32 @@ set(TARGET_NAME "SWA") option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF) +function(BIN2C) + cmake_parse_arguments(BIN2C_ARGS "" "TARGET_OBJ;SOURCE_FILE;DEST_FILE;ARRAY_TYPE;ARRAY_NAME" "" ${ARGN}) + + if(NOT BIN2C_ARGS_TARGET_OBJ) + message(FATAL_ERROR "TARGET_OBJ not specified.") + endif() + + if(NOT BIN2C_ARGS_SOURCE_FILE) + message(FATAL_ERROR "SOURCE_FILE not specified.") + endif() + + if(NOT BIN2C_ARGS_DEST_FILE) + set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}") + endif() + + add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c" + COMMAND file_to_c "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_ARRAY_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h" + DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" file_to_c + BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h" + COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..." + ) + + set_source_files_properties(${BIN2C_ARGS_DEST_FILE}.c PROPERTIES SKIP_PRECOMPILE_HEADERS ON) + target_sources(${BIN2C_ARGS_TARGET_OBJ} PRIVATE ${BIN2C_ARGS_DEST_FILE}.c) +endfunction() + add_compile_options( /fp:strict -march=sandybridge @@ -282,3 +308,10 @@ generate_aggregate_header( "${CMAKE_CURRENT_SOURCE_DIR}/api" "${CMAKE_CURRENT_SOURCE_DIR}/api/SWA.h" ) + +set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") +set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_trophy") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon_night") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/pause.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/pause.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_pause") diff --git a/UnleashedRecomp/res/.gitignore b/UnleashedRecomp/res/.gitignore index e3285599..435cdeaa 100644 --- a/UnleashedRecomp/res/.gitignore +++ b/UnleashedRecomp/res/.gitignore @@ -1,2 +1,3 @@ ![Ww][Ii][Nn]32/ +*.c *.h \ No newline at end of file diff --git a/UnleashedRecomp/ui/window.h b/UnleashedRecomp/ui/window.h index d6647fc2..61a0eb3b 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -1,7 +1,7 @@ #pragma once -#include "../UnleashedRecompResources/images/game_icon.h" -#include "../UnleashedRecompResources/images/game_icon_night.h" +#include +#include #include #include @@ -46,11 +46,11 @@ public: { if (isNight) { - SetIcon((void*)g_res_game_icon_night, g_res_game_icon_night_size); + SetIcon(g_game_icon_night, sizeof(g_game_icon_night)); } else { - SetIcon((void*)g_res_game_icon, g_res_game_icon_size); + SetIcon(g_game_icon, sizeof(g_game_icon)); } } diff --git a/tools/file_to_c/CMakeLists.txt b/tools/file_to_c/CMakeLists.txt index ddf3fa9d..08975574 100644 --- a/tools/file_to_c/CMakeLists.txt +++ b/tools/file_to_c/CMakeLists.txt @@ -6,22 +6,3 @@ project("file_to_c") set(CMAKE_CXX_STANDARD 17) add_executable(file_to_c "file_to_c.cpp") - -function(BIN2C) - cmake_parse_arguments(BIN2C_ARGS "" "SOURCE_FILE;DEST_FILE;ARRAY_TYPE;ARRAY_NAME" "" ${ARGN}) - - if(NOT BIN2C_ARGS_SOURCE_FILE) - message(FATAL_ERROR "SOURCE_FILE not specified.") - endif() - - if(NOT BIN2C_ARGS_DEST_FILE) - set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}") - endif() - - add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c" - COMMAND file_to_c "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_ARRAY_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h" - DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" file_to_c - BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h" - COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..." - ) -endfunction() From 4119b1763e57e1ee490ca70b37404fcd5a36fcde Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 02:24:12 +0000 Subject: [PATCH 07/16] Implemented message window --- UnleashedRecomp/CMakeLists.txt | 1 + UnleashedRecomp/gpu/video.cpp | 3 + UnleashedRecomp/stdafx.h | 1 + UnleashedRecomp/ui/imgui_utils.h | 81 ++++++- UnleashedRecomp/ui/message_window.cpp | 335 ++++++++++++++++++++++++++ UnleashedRecomp/ui/message_window.h | 12 + UnleashedRecomp/ui/options_menu.h | 2 +- 7 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 UnleashedRecomp/ui/message_window.cpp create mode 100644 UnleashedRecomp/ui/message_window.h diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 35bddb40..63483604 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -81,6 +81,7 @@ set(SWA_PATCHES_CXX_SOURCES set(SWA_UI_CXX_SOURCES "ui/achievement_menu.cpp" "ui/achievement_overlay.cpp" + "ui/message_window.cpp" "ui/options_menu.cpp" "ui/sdl_listener.cpp" "ui/window.cpp" diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index abed18dd..45325c22 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "imgui_snapshot.h" @@ -1065,6 +1066,7 @@ static void CreateImGuiBackend() AchievementMenu::Init(); AchievementOverlay::Init(); + MessageWindow::Init(); OptionsMenu::Init(); ImGui_ImplSDL2_InitForOther(Window::s_pWindow); @@ -1739,6 +1741,7 @@ static void DrawImGui() AchievementMenu::Draw(); OptionsMenu::Draw(); AchievementOverlay::Draw(); + MessageWindow::Draw(); ImGui::Render(); diff --git a/UnleashedRecomp/stdafx.h b/UnleashedRecomp/stdafx.h index bf524468..964e928a 100644 --- a/UnleashedRecomp/stdafx.h +++ b/UnleashedRecomp/stdafx.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/UnleashedRecomp/ui/imgui_utils.h b/UnleashedRecomp/ui/imgui_utils.h index b3a1bd92..728e5c57 100644 --- a/UnleashedRecomp/ui/imgui_utils.h +++ b/UnleashedRecomp/ui/imgui_utils.h @@ -3,6 +3,12 @@ #include #include +#define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \ + std::make_tuple(ImVec2((float)x / (float)textureWidth, (float)y / (float)textureHeight), \ + ImVec2(((float)x + (float)width) / (float)textureWidth, ((float)y + (float)height) / (float)textureHeight)) + +#define GET_UV_COORDS(tuple) std::get<0>(tuple), std::get<1>(tuple) + static std::vector> g_callbackData; static uint32_t g_callbackDataIndex = 0; @@ -150,7 +156,80 @@ static void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& DrawTextWithOutline(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text, radius, shadowColour); - drawList->AddText(font, fontSize, pos, colour, text); + drawList->AddText(font, fontSize, pos, colour, text, nullptr); +} + +static float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs) +{ + auto result = 0.0f; + + for (auto& str : strs) + result = std::max(result, font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()).x); + + return result; +} + +static std::vector Split(const char* str, char delimiter) +{ + std::vector result; + + if (!str) + return result; + + const char* start = str; + const char* current = str; + + while (*current) + { + if (*current == delimiter) + { + result.emplace_back(start, current - start); + start = current + 1; + } + + current++; + } + + result.emplace_back(start); + + return result; +} + +static ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, std::vector lines) +{ + auto x = 0.0f; + auto y = 0.0f; + + for (auto& str : lines) + { + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); + + x = std::max(x, textSize.x); + y += textSize.y + Scale(lineMargin); + } + + return { x, y }; +} + +static ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const char* text) +{ + return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, '\n')); +} + +static void DrawCentredParagraph(const ImFont* font, float fontSize, const ImVec2& centre, float lineMargin, const char* text, std::function drawMethod) +{ + auto lines = Split(text, '\n'); + auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines); + auto offsetY = 0.0f; + + for (auto& str : lines) + { + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); + + drawMethod(str.c_str(), ImVec2(/* X */ centre.x - textSize.x / 2, /* Y */ centre.y - paragraphSize.y / 2 + offsetY)); + + offsetY += textSize.y + Scale(lineMargin); + } } static void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset = 2.0f, float radius = 0.4f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)) diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp new file mode 100644 index 00000000..34c7bd49 --- /dev/null +++ b/UnleashedRecomp/ui/message_window.cpp @@ -0,0 +1,335 @@ +#include "message_window.h" +#include "imgui_utils.h" +#include +#include +#include +#include "../UnleashedRecompResources/images/pause.h" + +constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; +constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; +constexpr double OVERLAY_CONTAINER_INTRO_FADE_START = 5; +constexpr double OVERLAY_CONTAINER_INTRO_FADE_END = 9; +constexpr double OVERLAY_CONTAINER_OUTRO_FADE_START = 0; +constexpr double OVERLAY_CONTAINER_OUTRO_FADE_END = 4; + +static bool g_isAwaitingResult = false; +static bool g_isClosing = false; +static bool g_isControlsVisible = false; + +static int g_selectedRowIndex; +static int g_foregroundCount; + +static bool g_upWasHeld; +static bool g_downWasHeld; + +static double g_appearTime; +static double g_controlsAppearTime; + +static ImFont* g_fntSeurat; + +static std::unique_ptr g_upSelectionCursor; + +std::string g_text; +int g_result; +std::vector g_buttons; +int g_defaultButtonIndex; + +bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForeground = true) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + ImVec2 _min = { centre.x - max.x, centre.y - max.y }; + ImVec2 _max = { centre.x + max.x, centre.y + max.y }; + + // Expand/retract animation. + auto containerMotion = ComputeMotion(appearTime, OVERLAY_CONTAINER_COMMON_MOTION_START, OVERLAY_CONTAINER_COMMON_MOTION_END); + + if (g_isClosing) + { + _min.x = Hermite(_min.x, centre.x, containerMotion); + _max.x = Hermite(_max.x, centre.x, containerMotion); + _min.y = Hermite(_min.y, centre.y, containerMotion); + _max.y = Hermite(_max.y, centre.y, containerMotion); + } + else + { + _min.x = Hermite(centre.x, _min.x, containerMotion); + _max.x = Hermite(centre.x, _max.x, containerMotion); + _min.y = Hermite(centre.y, _min.y, containerMotion); + _max.y = Hermite(centre.y, _max.y, containerMotion); + } + + auto vertices = GetPauseContainerVertices(_min, _max); + + // Transparency fade animation. + auto colourMotion = g_isClosing + ? ComputeMotion(appearTime, OVERLAY_CONTAINER_OUTRO_FADE_START, OVERLAY_CONTAINER_OUTRO_FADE_END) + : ComputeMotion(appearTime, OVERLAY_CONTAINER_INTRO_FADE_START, OVERLAY_CONTAINER_INTRO_FADE_END); + + auto alpha = g_isClosing + ? Lerp(1, 0, colourMotion) + : Lerp(0, 1, colourMotion); + + if (!isForeground) + g_foregroundCount++; + + if (isForeground) + drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 223 * (g_foregroundCount ? 1 : alpha))); + + auto colShadow = IM_COL32(0, 0, 0, 156 * alpha); + auto colGradientTop = IM_COL32(197, 194, 197, 200 * alpha); + auto colGradientBottom = IM_COL32(115, 113, 115, 236 * alpha); + + // Draw vertices with gradient. + SetGradient(_min, _max, colGradientTop, colGradientBottom); + drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha)); + ResetGradient(); + + // Draw outline. + drawList->AddPolyline + ( + vertices.data(), + vertices.size(), + IM_COL32(247, 247, 247, 255 * alpha), + true, + Scale(2.5f) + ); + + // Offset vertices to draw 3D effect lines. + for (int i = 0; i < vertices.size(); i++) + { + vertices[i].x -= Scale(0.4f); + vertices[i].y -= Scale(0.2f); + } + + auto colLineTop = IM_COL32(165, 170, 165, 230 * alpha); + auto colLineBottom = IM_COL32(190, 190, 190, 230 * alpha); + auto lineThickness = Scale(1.0f); + + // Top left corner bottom to top left corner top. + drawList->AddLine(vertices[0], vertices[1], colLineTop, lineThickness * 0.5f); + + // Top left corner bottom to bottom left. + drawList->AddRectFilledMultiColor({ vertices[0].x - 0.2f, vertices[0].y }, { vertices[6].x + lineThickness - 0.2f, vertices[6].y }, colLineTop, colLineTop, colLineBottom, colLineBottom); + + // Top left corner top to top right. + drawList->AddLine(vertices[1], vertices[2], colLineTop, lineThickness); + + drawList->PushClipRect(_min, _max); + + return containerMotion >= 1.0f && !g_isClosing; +} + +void DrawButton(int rowIndex, float yOffset, float width, float height, std::string& text) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + auto clipRectMin = drawList->GetClipRectMin(); + auto clipRectMax = drawList->GetClipRectMax(); + + ImVec2 min = { clipRectMin.x + ((clipRectMax.x - clipRectMin.x) - width) / 2, clipRectMin.y + height * rowIndex + yOffset }; + ImVec2 max = { min.x + width, min.y + height }; + + bool isSelected = rowIndex == g_selectedRowIndex; + + if (isSelected) + { + static auto breatheStart = ImGui::GetTime(); + auto alpha = Lerp(1.0f, 0.75f, (sin((ImGui::GetTime() - breatheStart) * (2.0f * M_PI / (55.0f / 60.0f))) + 1.0f) / 2.0f); + auto colour = IM_COL32(255, 255, 255, 255 * alpha); + + auto width = Scale(11); + auto left = PIXELS_TO_UV_COORDS(128, 128, 0, 0, 11, 50); + auto centre = PIXELS_TO_UV_COORDS(128, 128, 11, 0, 8, 50); + auto right = PIXELS_TO_UV_COORDS(128, 128, 19, 0, 11, 50); + + drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + width, max.y }, GET_UV_COORDS(left), colour); + drawList->AddImage(g_upSelectionCursor.get(), { min.x + width, min.y }, { max.x - width, max.y }, GET_UV_COORDS(centre), colour); + drawList->AddImage(g_upSelectionCursor.get(), { max.x - width, min.y }, max, GET_UV_COORDS(right), colour); + } + + auto fontSize = Scale(28); + auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str()); + + DrawTextWithShadow + ( + g_fntSeurat, + fontSize, + { /* X */ min.x + ((max.x - min.x) - textSize.x) / 2, /* Y */ min.y + ((max.y - min.y) - textSize.y) / 2 }, + isSelected ? IM_COL32(255, 128, 0, 255) : IM_COL32(255, 255, 255, 255), + text.c_str() + ); +} + +static void ResetSelection() +{ + g_selectedRowIndex = g_defaultButtonIndex; + g_upWasHeld = false; + g_downWasHeld = false; +} + +void MessageWindow::Init() +{ + auto& io = ImGui::GetIO(); + + constexpr float FONT_SCALE = 2.0f; + + g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 28.0f * FONT_SCALE); + + g_upSelectionCursor = LoadTexture((uint8_t*)g_res_pause, g_res_pause_size); +} + +void MessageWindow::Draw() +{ + if (!s_isVisible) + return; + + auto pInputState = SWA::CInputState::GetInstance(); + auto drawList = ImGui::GetForegroundDrawList(); + auto& res = ImGui::GetIO().DisplaySize; + + ImVec2 centre = { res.x / 2, res.y / 2 }; + + auto fontSize = Scale(28); + auto textSize = MeasureCentredParagraph(g_fntSeurat, fontSize, 5, g_text.c_str()); + auto textMarginX = Scale(32); + auto textMarginY = Scale(40); + + if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible)) + { + DrawCentredParagraph + ( + g_fntSeurat, + fontSize, + { centre.x, centre.y + Scale(3) }, + 5, + g_text.c_str(), + + [=](const char* str, ImVec2 pos) + { + DrawTextWithShadow(g_fntSeurat, fontSize, pos, IM_COL32(255, 255, 255, 255), str); + } + ); + + drawList->PopClipRect(); + + if (g_buttons.size()) + { + auto itemWidth = std::max(Scale(162), Scale(CalcWidestTextSize(g_fntSeurat, fontSize, g_buttons))); + auto itemHeight = Scale(57); + auto windowMarginX = Scale(18); + auto windowMarginY = Scale(25); + + ImVec2 controlsMax = { /* X */ itemWidth / 2 + windowMarginX, /* Y */ itemHeight / 2 * g_buttons.size() + windowMarginY }; + + if (g_isControlsVisible && DrawContainer(g_controlsAppearTime, centre, controlsMax)) + { + auto rowCount = 0; + + for (auto& button : g_buttons) + DrawButton(rowCount++, windowMarginY, itemWidth, itemHeight, button); + + drawList->PopClipRect(); + + bool upIsHeld = pInputState->GetPadState().IsDown(SWA::eKeyState_DpadUp) || + pInputState->GetPadState().LeftStickVertical > 0.5f; + + bool downIsHeld = pInputState->GetPadState().IsDown(SWA::eKeyState_DpadDown) || + pInputState->GetPadState().LeftStickVertical < -0.5f; + + bool scrollUp = !g_upWasHeld && upIsHeld; + bool scrollDown = !g_downWasHeld && downIsHeld; + + if (scrollUp) + { + --g_selectedRowIndex; + if (g_selectedRowIndex < 0) + g_selectedRowIndex = rowCount - 1; + } + else if (scrollDown) + { + ++g_selectedRowIndex; + if (g_selectedRowIndex >= rowCount) + g_selectedRowIndex = 0; + } + + if (scrollUp || scrollDown) + Game_PlaySound("sys_actstg_pausecursor"); + + g_upWasHeld = upIsHeld; + g_downWasHeld = downIsHeld; + + if (pInputState->GetPadState().IsTapped(SWA::eKeyState_A)) + { + g_result = g_selectedRowIndex; + + Game_PlaySound("sys_actstg_pausedecide"); + MessageWindow::Close(); + } + else if (pInputState->GetPadState().IsTapped(SWA::eKeyState_B)) + { + g_result = -1; + + Game_PlaySound("sys_actstg_pausecansel"); + MessageWindow::Close(); + } + } + else + { + if (!g_isControlsVisible && pInputState->GetPadState().IsTapped(SWA::eKeyState_A)) + { + g_controlsAppearTime = ImGui::GetTime(); + g_isControlsVisible = true; + + Game_PlaySound("sys_actstg_pausewinopen"); + } + } + } + else + { + if (pInputState->GetPadState().IsTapped(SWA::eKeyState_A)) + MessageWindow::Close(); + } + } +} + +bool MessageWindow::Open(std::string text, int* result, std::span buttons, int defaultButtonIndex) +{ + if (!g_isAwaitingResult && *result == -1) + { + s_isVisible = true; + g_isClosing = false; + g_isControlsVisible = false; + g_foregroundCount = 0; + g_appearTime = ImGui::GetTime(); + g_controlsAppearTime = ImGui::GetTime(); + + g_text = text; + g_buttons = std::vector(buttons.begin(), buttons.end()); + g_defaultButtonIndex = defaultButtonIndex; + + ResetSelection(); + + Game_PlaySound("sys_actstg_pausewinopen"); + + g_isAwaitingResult = true; + } + + *result = g_result; + + return !g_isAwaitingResult; +} + +void MessageWindow::Close() +{ + if (!g_isClosing) + { + g_appearTime = ImGui::GetTime(); + g_controlsAppearTime = ImGui::GetTime(); + g_isClosing = true; + g_isControlsVisible = false; + g_isAwaitingResult = false; + } + + Game_PlaySound("sys_actstg_pausewinclose"); +} diff --git a/UnleashedRecomp/ui/message_window.h b/UnleashedRecomp/ui/message_window.h new file mode 100644 index 00000000..b1baca00 --- /dev/null +++ b/UnleashedRecomp/ui/message_window.h @@ -0,0 +1,12 @@ +#pragma once + +class MessageWindow +{ +public: + inline static bool s_isVisible = false; + + static void Init(); + static void Draw(); + static bool Open(std::string text, int* result, std::span buttons = {}, int defaultButtonIndex = 0); + static void Close(); +}; diff --git a/UnleashedRecomp/ui/options_menu.h b/UnleashedRecomp/ui/options_menu.h index 6ea404b5..85268806 100644 --- a/UnleashedRecomp/ui/options_menu.h +++ b/UnleashedRecomp/ui/options_menu.h @@ -2,7 +2,7 @@ #include -struct OptionsMenu +class OptionsMenu { public: inline static bool s_isVisible = false; From 8a709921025bce18bfebb6286e3af84cc239f4e0 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 02:25:01 +0000 Subject: [PATCH 08/16] achievement_menu: use selection cursor texture --- UnleashedRecomp/ui/achievement_menu.cpp | 34 +++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index 14e5d107..d50aff6b 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "../UnleashedRecompResources/images/pause.h" constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15; @@ -28,7 +29,7 @@ constexpr double SELECTION_CONTAINER_BREATHE = 30; static bool g_isClosing = false; -static double g_appearTime = 0; +static double g_appearTime; static std::vector> g_achievements; @@ -37,6 +38,7 @@ static ImFont* g_fntNewRodinDB; static ImFont* g_fntNewRodinUB; static std::unique_ptr g_upTrophyIcon; +static std::unique_ptr g_upSelectionCursor; static int g_firstVisibleRowIndex; static int g_selectedRowIndex; @@ -104,14 +106,33 @@ static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradi static void DrawSelectionContainer(ImVec2 min, ImVec2 max) { auto drawList = ImGui::GetForegroundDrawList(); - auto vertices = GetPauseContainerVertices(min, max, 10); static auto breatheStart = ImGui::GetTime(); auto alpha = Lerp(1.0f, 0.75f, (sin((ImGui::GetTime() - breatheStart) * (2.0f * M_PI / (55.0f / 60.0f))) + 1.0f) / 2.0f); + auto colour = IM_COL32(255, 255, 255, 255 * alpha); - SetGradient(min, max, IM_COL32(255, 246, 0, 129), IM_COL32(255, 194, 0, 118 * alpha)); - drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha)); - ResetGradient(); + auto commonWidth = Scale(11); + auto commonHeight = Scale(24); + + auto tl = PIXELS_TO_UV_COORDS(128, 128, 41, 0, 11, 24); + auto tc = PIXELS_TO_UV_COORDS(128, 128, 52, 0, 8, 24); + auto tr = PIXELS_TO_UV_COORDS(128, 128, 60, 0, 11, 24); + auto cl = PIXELS_TO_UV_COORDS(128, 128, 41, 24, 11, 2); + auto cc = PIXELS_TO_UV_COORDS(128, 128, 52, 24, 8, 2); + auto cr = PIXELS_TO_UV_COORDS(128, 128, 60, 24, 11, 2); + auto bl = PIXELS_TO_UV_COORDS(128, 128, 41, 26, 11, 24); + auto bc = PIXELS_TO_UV_COORDS(128, 128, 52, 26, 8, 24); + auto br = PIXELS_TO_UV_COORDS(128, 128, 60, 26, 11, 24); + + drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); + drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); + drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour); + drawList->AddImage(g_upSelectionCursor.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); + drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour); + drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour); + drawList->AddImage(g_upSelectionCursor.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour); + drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour); + drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour); } static void DrawHeaderContainer(const char* text) @@ -633,6 +654,9 @@ void AchievementMenu::Init() g_fntNewRodinDB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); g_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE); + g_upSelectionCursor = LoadTexture((uint8_t*)g_res_pause, g_res_pause_size); + + // TODO: embed this texture. size_t bufferSize = 0; auto buffer = ReadAllBytes("trophy.dds", bufferSize); From f4359585cc125c4aba887e999b33f3fe50ff432d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 02:48:04 +0000 Subject: [PATCH 09/16] Update embedded resources --- UnleashedRecomp/CMakeLists.txt | 1 + UnleashedRecomp/ui/achievement_menu.cpp | 15 ++++----------- UnleashedRecomp/ui/message_window.cpp | 4 ++-- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index eec6a19e..d0c0be9a 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -312,6 +312,7 @@ generate_aggregate_header( set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") + BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_trophy") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon_night") diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index d50aff6b..7a8aca53 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -8,7 +8,8 @@ #include #include #include -#include "../UnleashedRecompResources/images/pause.h" +#include +#include constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15; @@ -654,16 +655,8 @@ void AchievementMenu::Init() g_fntNewRodinDB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); g_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE); - g_upSelectionCursor = LoadTexture((uint8_t*)g_res_pause, g_res_pause_size); - - // TODO: embed this texture. - size_t bufferSize = 0; - auto buffer = ReadAllBytes("trophy.dds", bufferSize); - - if (!bufferSize) - return; - - g_upTrophyIcon = LoadTexture(buffer.get(), bufferSize); + g_upTrophyIcon = LoadTexture(g_trophy, sizeof(g_trophy)); + g_upSelectionCursor = LoadTexture(g_pause, sizeof(g_pause)); } void AchievementMenu::Draw() diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index 34c7bd49..c82a044b 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -3,7 +3,7 @@ #include #include #include -#include "../UnleashedRecompResources/images/pause.h" +#include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; @@ -176,7 +176,7 @@ void MessageWindow::Init() g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 28.0f * FONT_SCALE); - g_upSelectionCursor = LoadTexture((uint8_t*)g_res_pause, g_res_pause_size); + g_upSelectionCursor = LoadTexture(g_pause, sizeof(g_pause)); } void MessageWindow::Draw() From 4d8c74412ccb6a1a9dc22bfd80286c0a8b2ca939 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:08:58 +0000 Subject: [PATCH 10/16] Update embedded resources --- UnleashedRecomp/CMakeLists.txt | 3 ++- UnleashedRecomp/ui/achievement_menu.cpp | 22 +++++++++++----------- UnleashedRecomp/ui/message_window.cpp | 10 +++++----- UnleashedRecompResources | 2 +- thirdparty/PowerRecomp | 2 +- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index d0c0be9a..02b8aa9f 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -314,6 +314,7 @@ set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_trophy") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fade") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fill") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon_night") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/pause.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/pause.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_pause") diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index 7a8aca53..52f70af1 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15; @@ -115,15 +115,15 @@ static void DrawSelectionContainer(ImVec2 min, ImVec2 max) auto commonWidth = Scale(11); auto commonHeight = Scale(24); - auto tl = PIXELS_TO_UV_COORDS(128, 128, 41, 0, 11, 24); - auto tc = PIXELS_TO_UV_COORDS(128, 128, 52, 0, 8, 24); - auto tr = PIXELS_TO_UV_COORDS(128, 128, 60, 0, 11, 24); - auto cl = PIXELS_TO_UV_COORDS(128, 128, 41, 24, 11, 2); - auto cc = PIXELS_TO_UV_COORDS(128, 128, 52, 24, 8, 2); - auto cr = PIXELS_TO_UV_COORDS(128, 128, 60, 24, 11, 2); - auto bl = PIXELS_TO_UV_COORDS(128, 128, 41, 26, 11, 24); - auto bc = PIXELS_TO_UV_COORDS(128, 128, 52, 26, 8, 24); - auto br = PIXELS_TO_UV_COORDS(128, 128, 60, 26, 11, 24); + auto tl = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 24); + auto tc = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 24); + auto tr = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 24); + auto cl = PIXELS_TO_UV_COORDS(64, 64, 0, 24, 11, 2); + auto cc = PIXELS_TO_UV_COORDS(64, 64, 11, 24, 8, 2); + auto cr = PIXELS_TO_UV_COORDS(64, 64, 19, 24, 11, 2); + auto bl = PIXELS_TO_UV_COORDS(64, 64, 0, 26, 11, 24); + auto bc = PIXELS_TO_UV_COORDS(64, 64, 11, 26, 8, 24); + auto br = PIXELS_TO_UV_COORDS(64, 64, 19, 26, 11, 24); drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); @@ -656,7 +656,7 @@ void AchievementMenu::Init() g_fntNewRodinUB = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE); g_upTrophyIcon = LoadTexture(g_trophy, sizeof(g_trophy)); - g_upSelectionCursor = LoadTexture(g_pause, sizeof(g_pause)); + g_upSelectionCursor = LoadTexture(g_select_fill, sizeof(g_select_fill)); } void AchievementMenu::Draw() diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index c82a044b..9e3639b5 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; @@ -139,9 +139,9 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str auto colour = IM_COL32(255, 255, 255, 255 * alpha); auto width = Scale(11); - auto left = PIXELS_TO_UV_COORDS(128, 128, 0, 0, 11, 50); - auto centre = PIXELS_TO_UV_COORDS(128, 128, 11, 0, 8, 50); - auto right = PIXELS_TO_UV_COORDS(128, 128, 19, 0, 11, 50); + auto left = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 50); + auto centre = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 50); + auto right = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 50); drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + width, max.y }, GET_UV_COORDS(left), colour); drawList->AddImage(g_upSelectionCursor.get(), { min.x + width, min.y }, { max.x - width, max.y }, GET_UV_COORDS(centre), colour); @@ -176,7 +176,7 @@ void MessageWindow::Init() g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 28.0f * FONT_SCALE); - g_upSelectionCursor = LoadTexture(g_pause, sizeof(g_pause)); + g_upSelectionCursor = LoadTexture(g_select_fade, sizeof(g_select_fade)); } void MessageWindow::Draw() diff --git a/UnleashedRecompResources b/UnleashedRecompResources index 48e641b5..e8dc5618 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit 48e641b58a09c9ecfcf72c60d97bb1079deb5ea4 +Subproject commit e8dc561807e68b65ce55e32292912c356f3e56c8 diff --git a/thirdparty/PowerRecomp b/thirdparty/PowerRecomp index 675b482e..ea3e60cb 160000 --- a/thirdparty/PowerRecomp +++ b/thirdparty/PowerRecomp @@ -1 +1 @@ -Subproject commit 675b482ec4852b873590fb999d24b426bade2b3a +Subproject commit ea3e60cb0d618ec8d24cc745d72b1ac39cc722cc From 6c65e0914d1cda92a84e4aa7952913739974030e Mon Sep 17 00:00:00 2001 From: "Skyth (Asilkan)" <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:10:06 +0300 Subject: [PATCH 11/16] Implement miniaudio. (#15) --- UnleashedRecomp/CMakeLists.txt | 6 +- .../apu/driver/miniaudio_driver.cpp | 56 +++++++++++++ .../{sdl2_driver.h => miniaudio_driver.h} | 1 - UnleashedRecomp/apu/driver/sdl2_driver.cpp | 81 ------------------- UnleashedRecomp/cpu/guest_code.h | 2 +- UnleashedRecomp/cpu/guest_thread.cpp | 43 ++++++---- UnleashedRecomp/cpu/guest_thread.h | 9 +++ UnleashedRecomp/stdafx.h | 1 - vcpkg.json | 2 +- 9 files changed, 96 insertions(+), 105 deletions(-) create mode 100644 UnleashedRecomp/apu/driver/miniaudio_driver.cpp rename UnleashedRecomp/apu/driver/{sdl2_driver.h => miniaudio_driver.h} (67%) delete mode 100644 UnleashedRecomp/apu/driver/sdl2_driver.cpp diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 7cb86eca..26909056 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -64,7 +64,7 @@ set(SWA_APU_CXX_SOURCES if(SWA_XAUDIO2) list(APPEND SWA_APU_CXX_SOURCES "apu/driver/xaudio_driver.cpp") else() - list(APPEND SWA_APU_CXX_SOURCES "apu/driver/sdl2_driver.cpp") + list(APPEND SWA_APU_CXX_SOURCES "apu/driver/miniaudio_driver.cpp") endif() set(SWA_HID_CXX_SOURCES @@ -160,7 +160,7 @@ find_package(unofficial-concurrentqueue REQUIRED) find_package(imgui CONFIG REQUIRED) find_package(magic_enum CONFIG REQUIRED) find_package(unofficial-tiny-aes-c CONFIG REQUIRED) -find_path(READERWRITERQUEUE_INCLUDE_DIRS "readerwriterqueue/atomicops.h") +find_path(MINIAUDIO_INCLUDE_DIRS "miniaudio.h") file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) add_custom_command(TARGET UnleashedRecomp POST_BUILD @@ -208,7 +208,7 @@ target_include_directories(UnleashedRecomp PRIVATE ${LIBMSPACK_PATH} ${Stb_INCLUDE_DIR} ${SMOLV_SOURCE_DIR} - ${READERWRITERQUEUE_INCLUDE_DIRS} + ${MINIAUDIO_INCLUDE_DIRS} ) target_precompile_headers(UnleashedRecomp PUBLIC ${SWA_PRECOMPILED_HEADERS}) diff --git a/UnleashedRecomp/apu/driver/miniaudio_driver.cpp b/UnleashedRecomp/apu/driver/miniaudio_driver.cpp new file mode 100644 index 00000000..a5b6dd2b --- /dev/null +++ b/UnleashedRecomp/apu/driver/miniaudio_driver.cpp @@ -0,0 +1,56 @@ +#include "miniaudio_driver.h" +#include +#include +#include +#include + +#define MINIAUDIO_IMPLEMENTATION +#include + +static PPCFunc* g_clientCallback{}; +static DWORD g_clientCallbackParam{}; // pointer in guest memory +static ma_device g_audioDevice{}; +static std::unique_ptr g_audioCtx; +static uint32_t* g_audioOutput; + +static void AudioCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + if (g_audioCtx == nullptr) + g_audioCtx = std::make_unique(0); + + g_audioCtx->ppcContext.r3.u64 = g_clientCallbackParam; + g_audioOutput = reinterpret_cast(pOutput); + (*g_clientCallback)(g_audioCtx->ppcContext, reinterpret_cast(g_memory.base)); +} + +void XAudioInitializeSystem() +{ + ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.sampleRate = XAUDIO_SAMPLES_HZ; + deviceConfig.periodSizeInFrames = XAUDIO_NUM_SAMPLES; + deviceConfig.noPreSilencedOutputBuffer = true; + deviceConfig.dataCallback = AudioCallback; + deviceConfig.playback.format = ma_format_f32; + deviceConfig.playback.channels = XAUDIO_NUM_CHANNELS; + ma_device_init(nullptr, &deviceConfig, &g_audioDevice); +} + +void XAudioRegisterClient(PPCFunc* callback, uint32_t param) +{ + auto* pClientParam = static_cast(g_userHeap.Alloc(sizeof(param))); + ByteSwap(param); + *pClientParam = param; + g_clientCallbackParam = g_memory.MapVirtual(pClientParam); + g_clientCallback = callback; + + ma_device_start(&g_audioDevice); +} + +void XAudioSubmitFrame(void* samples) +{ + for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) + { + for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) + g_audioOutput[i * XAUDIO_NUM_CHANNELS + j] = std::byteswap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]); + } +} diff --git a/UnleashedRecomp/apu/driver/sdl2_driver.h b/UnleashedRecomp/apu/driver/miniaudio_driver.h similarity index 67% rename from UnleashedRecomp/apu/driver/sdl2_driver.h rename to UnleashedRecomp/apu/driver/miniaudio_driver.h index ab732be5..c8f99bf4 100644 --- a/UnleashedRecomp/apu/driver/sdl2_driver.h +++ b/UnleashedRecomp/apu/driver/miniaudio_driver.h @@ -1,3 +1,2 @@ #pragma once -#include #include diff --git a/UnleashedRecomp/apu/driver/sdl2_driver.cpp b/UnleashedRecomp/apu/driver/sdl2_driver.cpp deleted file mode 100644 index da4cba78..00000000 --- a/UnleashedRecomp/apu/driver/sdl2_driver.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "sdl2_driver.h" -#include -#include -#include -#include - -#define SDLAUDIO_DRIVER_KEY (uint32_t)('SDLA') - -static constexpr uint32_t AUDIO_FRAME_SIZE = XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS; - -static std::atomic g_clientCallback{}; -static DWORD g_clientCallbackParam{}; // pointer in guest memory - -static SDL_AudioDeviceID g_audioDevice{}; -static moodycamel::BlockingReaderWriterCircularBuffer> g_audioQueue(16); - -static void SDLAudioCallback(void*, uint8_t* frames, int len) -{ - std::array audioFrame; - if (g_audioQueue.try_dequeue(audioFrame)) - memcpy(frames, &audioFrame, sizeof(audioFrame)); - else - memset(frames, 0, len); -} - -static PPC_FUNC(DriverLoop) -{ - GuestThread::SetThreadName(GetCurrentThreadId(), "Audio Driver"); - - while (true) - { - if (!g_clientCallback) - continue; - - ctx.r3.u64 = g_clientCallbackParam; - GuestCode::Run((void*)g_clientCallback, &ctx); - } -} - -void XAudioInitializeSystem() -{ - SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback"); - SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "SWA"); - - auto err = SDL_InitSubSystem(SDL_INIT_AUDIO); - SDL_AudioSpec spec{}; - spec.freq = XAUDIO_SAMPLES_HZ; - spec.format = AUDIO_F32SYS; - spec.channels = XAUDIO_NUM_CHANNELS; - spec.samples = XAUDIO_NUM_SAMPLES; - spec.callback = SDLAudioCallback; - g_audioDevice = SDL_OpenAudioDevice(nullptr, false, &spec, &spec, 0); - assert(g_audioDevice); - - SDL_PauseAudioDevice(g_audioDevice, 0); - KeInsertHostFunction(SDLAUDIO_DRIVER_KEY, DriverLoop); - GuestThread::Start(SDLAUDIO_DRIVER_KEY, 0, 0, nullptr); -} - -void XAudioRegisterClient(PPCFunc* callback, uint32_t param) -{ - auto* pClientParam = static_cast(g_userHeap.Alloc(sizeof(param))); - ByteSwap(param); - *pClientParam = param; - g_clientCallbackParam = g_memory.MapVirtual(pClientParam); - - g_clientCallback = callback; -} - -void XAudioSubmitFrame(void* samples) -{ - std::array audioFrame; - - for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) - { - for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) - audioFrame[i * XAUDIO_NUM_CHANNELS + j] = std::byteswap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]); - } - - g_audioQueue.wait_enqueue(audioFrame); -} diff --git a/UnleashedRecomp/cpu/guest_code.h b/UnleashedRecomp/cpu/guest_code.h index 11a27080..4177b05e 100644 --- a/UnleashedRecomp/cpu/guest_code.h +++ b/UnleashedRecomp/cpu/guest_code.h @@ -4,7 +4,7 @@ struct GuestCode { - inline static void Run(void* hostAddress, PPCContext* ctx, void* baseAddress, void* callStack) + inline static void Run(void* hostAddress, PPCContext* ctx, void* baseAddress) { ctx->fpscr.loadFromHost(); reinterpret_cast(hostAddress)(*ctx, reinterpret_cast(baseAddress)); diff --git a/UnleashedRecomp/cpu/guest_thread.cpp b/UnleashedRecomp/cpu/guest_thread.cpp index c13f580f..21010ee1 100644 --- a/UnleashedRecomp/cpu/guest_thread.cpp +++ b/UnleashedRecomp/cpu/guest_thread.cpp @@ -11,24 +11,15 @@ constexpr size_t PCR_SIZE = 0xAB0; constexpr size_t TLS_SIZE = 0x100; constexpr size_t TEB_SIZE = 0x2E0; constexpr size_t STACK_SIZE = 0x40000; -constexpr size_t CALL_STACK_SIZE = 0x8000; -constexpr size_t TOTAL_SIZE = PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE + CALL_STACK_SIZE; +constexpr size_t TOTAL_SIZE = PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE; constexpr size_t TEB_OFFSET = PCR_SIZE + TLS_SIZE; -DWORD GuestThread::Start(uint32_t function) +GuestThreadContext::GuestThreadContext(uint32_t cpuNumber) { - const GuestThreadParameter parameter{ function }; - return Start(parameter); -} - -DWORD GuestThread::Start(const GuestThreadParameter& parameter) -{ - auto* thread = (uint8_t*)g_userHeap.Alloc(TOTAL_SIZE); - - const auto procMask = (uint8_t)(parameter.flags >> 24); - const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask); + assert(thread == nullptr); + thread = (uint8_t*)g_userHeap.Alloc(TOTAL_SIZE); memset(thread, 0, TOTAL_SIZE); *(uint32_t*)thread = std::byteswap(g_memory.MapVirtual(thread + PCR_SIZE)); // tls pointer @@ -38,18 +29,36 @@ DWORD GuestThread::Start(const GuestThreadParameter& parameter) *(uint32_t*)(thread + PCR_SIZE + 0x10) = 0xFFFFFFFF; // that one TLS entry that felt quirky *(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = std::byteswap(GetCurrentThreadId()); // thread id - PPCContext ppcContext{}; ppcContext.fn = (uint8_t*)g_codeCache.bucket; ppcContext.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer - ppcContext.r3.u64 = parameter.value; ppcContext.r13.u64 = g_memory.MapVirtual(thread); + assert(GetPPCContext() == nullptr); SetPPCContext(ppcContext); +} - GuestCode::Run(g_codeCache.Find(parameter.function), &ppcContext, g_memory.Translate(0), g_memory.Translate(ppcContext.r1.u32)); +GuestThreadContext::~GuestThreadContext() +{ g_userHeap.Free(thread); +} - return (DWORD)ppcContext.r3.u64; +DWORD GuestThread::Start(uint32_t function) +{ + const GuestThreadParameter parameter{ function }; + return Start(parameter); +} + +DWORD GuestThread::Start(const GuestThreadParameter& parameter) +{ + const auto procMask = (uint8_t)(parameter.flags >> 24); + const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask); + + GuestThreadContext ctx(cpuNumber); + ctx.ppcContext.r3.u64 = parameter.value; + + GuestCode::Run(g_codeCache.Find(parameter.function), &ctx.ppcContext, g_memory.Translate(0)); + + return (DWORD)ctx.ppcContext.r3.u64; } DWORD HostThreadStart(void* pParameter) diff --git a/UnleashedRecomp/cpu/guest_thread.h b/UnleashedRecomp/cpu/guest_thread.h index fd7f8ff7..7fb6e9b5 100644 --- a/UnleashedRecomp/cpu/guest_thread.h +++ b/UnleashedRecomp/cpu/guest_thread.h @@ -8,6 +8,15 @@ struct GuestThreadParameter uint32_t flags; }; +struct GuestThreadContext +{ + PPCContext ppcContext{}; + uint8_t* thread = nullptr; + + GuestThreadContext(uint32_t cpuNumber); + ~GuestThreadContext(); +}; + struct GuestThread { static DWORD Start(uint32_t function); diff --git a/UnleashedRecomp/stdafx.h b/UnleashedRecomp/stdafx.h index bf524468..8fd5bb22 100644 --- a/UnleashedRecomp/stdafx.h +++ b/UnleashedRecomp/stdafx.h @@ -32,7 +32,6 @@ #include #include #include -#include using Microsoft::WRL::ComPtr; diff --git a/vcpkg.json b/vcpkg.json index 5e135232..1f07072b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -22,6 +22,6 @@ "features": [ "sdl2-binding" ] }, "magic-enum", - "readerwriterqueue" + "miniaudio" ] } From fe4801b9ff06b65051dc70118b5f4038224db005 Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:44:58 +0300 Subject: [PATCH 12/16] Fix counter shared pointer getting created with the wrong function. --- UnleashedRecomp/gpu/video.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index a253eb1a..5d556819 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -4515,7 +4515,7 @@ static void EnqueueGraphicsPipelineCompilation(const PipelineState& pipelineStat if (shouldCompile) { if (databaseDataHolderPair.counter == nullptr && databaseDataHolderPair.holder.databaseData.get() != nullptr) - databaseDataHolderPair.counter = std::make_unique(std::move(databaseDataHolderPair.holder)); + databaseDataHolderPair.counter = std::make_shared(std::move(databaseDataHolderPair.holder)); PipelineStateQueueItem queueItem; queueItem.pipelineHash = hash; From 78fde84bd923675f4c809ed8a620388d83209682 Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:41:27 +0300 Subject: [PATCH 13/16] Fix Dark World cutscene getting frozen at the end. --- UnleashedRecomp/misc_impl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/UnleashedRecomp/misc_impl.cpp b/UnleashedRecomp/misc_impl.cpp index e9d391c3..451f001d 100644 --- a/UnleashedRecomp/misc_impl.cpp +++ b/UnleashedRecomp/misc_impl.cpp @@ -42,6 +42,7 @@ GUEST_FUNCTION_HOOK(sub_82BD4CA8, OutputDebugStringA); GUEST_FUNCTION_HOOK(sub_82BD4AC8, QueryPerformanceCounterImpl); GUEST_FUNCTION_HOOK(sub_831CD040, QueryPerformanceFrequencyImpl); +GUEST_FUNCTION_HOOK(sub_831CDAD0, GetTickCount); GUEST_FUNCTION_HOOK(sub_82BD4BC0, GlobalMemoryStatusImpl); From 95ef1a86ac893a13233fca4ddcf39204b668508e Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:16:39 +0000 Subject: [PATCH 14/16] Use textures for pause menu containers --- UnleashedRecomp/CMakeLists.txt | 1 + UnleashedRecomp/ui/achievement_menu.cpp | 107 ++++++++------------- UnleashedRecomp/ui/achievement_overlay.cpp | 58 +++-------- UnleashedRecomp/ui/imgui_utils.h | 65 +++++++++++-- UnleashedRecompResources | 2 +- 5 files changed, 108 insertions(+), 125 deletions(-) diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 02b8aa9f..1f71ba32 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -314,6 +314,7 @@ set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/achievements_menu/trophy.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/achievements_menu/trophy.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_trophy") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/general_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/general_window.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_general_window") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fade") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_select_fill") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_game_icon") diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index 52f70af1..152cfbde 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; @@ -40,6 +41,7 @@ static ImFont* g_fntNewRodinUB; static std::unique_ptr g_upTrophyIcon; static std::unique_ptr g_upSelectionCursor; +static std::unique_ptr g_upWindow; static int g_firstVisibleRowIndex; static int g_selectedRowIndex; @@ -64,42 +66,8 @@ static void ResetSelection() static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradientBottom, float alpha = 1, float cornerRadius = 25) { auto drawList = ImGui::GetForegroundDrawList(); - auto vertices = GetPauseContainerVertices(min, max, cornerRadius); - // TODO: add a drop shadow. - - SetGradient(min, max, gradientTop, gradientBottom); - drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha)); - ResetGradient(); - - drawList->AddPolyline(vertices.data(), vertices.size(), IM_COL32(247, 247, 247, 255 * alpha), true, Scale(2.5f)); - - for (int i = 0; i < vertices.size(); i++) - { - vertices[i].x -= Scale(0.4f); - vertices[i].y -= Scale(0.2f); - } - - auto colLineTop = IM_COL32(165, 170, 165, 230 * alpha); - auto colLineBottom = IM_COL32(190, 190, 190, 230 * alpha); - auto lineThickness = Scale(1); - - // Top left corner bottom to top left corner top. - drawList->AddLine(vertices[0], vertices[1], colLineTop, lineThickness * Scale(0.5f)); - - // Top left corner bottom to bottom left. - drawList->AddRectFilledMultiColor - ( - { /* X */ vertices[0].x - Scale(0.2f), /* Y */ vertices[0].y }, - { /* X */ vertices[6].x + lineThickness - Scale(0.2f), /* Y */ vertices[6].y }, - colLineTop, - colLineTop, - colLineBottom, - colLineBottom - ); - - // Top left corner top to top right. - drawList->AddLine(vertices[1], vertices[2], colLineTop, lineThickness); + DrawPauseContainer(g_upWindow, min, max, alpha); drawList->PushClipRect({ min.x, min.y + Scale(20) }, { max.x, max.y - Scale(5) }); } @@ -139,7 +107,7 @@ static void DrawSelectionContainer(ImVec2 min, ImVec2 max) static void DrawHeaderContainer(const char* text) { auto drawList = ImGui::GetForegroundDrawList(); - auto fontSize = Scale(26); + auto fontSize = Scale(24); auto textSize = g_fntNewRodinUB->CalcTextSizeA(fontSize, FLT_MAX, 0, text); auto cornerRadius = 23; auto textMarginX = Scale(16) + (Scale(cornerRadius) / 2); @@ -154,26 +122,25 @@ static void DrawHeaderContainer(const char* text) // Slide animation. auto containerMarginX = g_isClosing - ? Hermite(256, 156, containerMotion) - : Hermite(156, 256, containerMotion); + ? Hermite(251, 151, containerMotion) + : Hermite(151, 251, containerMotion); // Transparency fade animation. auto alpha = g_isClosing ? Lerp(1, 0, colourMotion) : Lerp(0, 1, colourMotion); - ImVec2 min = { Scale(containerMarginX), Scale(138) }; - ImVec2 max = { min.x + textMarginX * 2 + textSize.x, Scale(185) }; + ImVec2 min = { Scale(containerMarginX), Scale(136) }; + ImVec2 max = { min.x + textMarginX * 2 + textSize.x + Scale(5), Scale(196) }; - DrawContainer(min, max, IM_COL32(140, 142, 140, 201), IM_COL32(66, 65, 66, 234), alpha, cornerRadius); - drawList->PopClipRect(); + DrawPauseHeaderContainer(g_upWindow, min, max, alpha); // TODO: skew this text and apply bevel. DrawTextWithOutline ( g_fntNewRodinUB, fontSize, - { /* X */ min.x + textMarginX, /* Y */ min.y + ((max.y - min.y) - textSize.y) / 2 }, + { /* X */ min.x + textMarginX, /* Y */ min.y + ((max.y - min.y) - textSize.y) / 2 - Scale(5) }, IM_COL32(255, 255, 255, 255 * alpha), text, 3, @@ -188,9 +155,9 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMax = drawList->GetClipRectMax(); - auto itemWidth = Scale(708); + auto itemWidth = Scale(700); auto itemHeight = Scale(94); - auto itemMarginX = Scale(13); + auto itemMarginX = Scale(18); auto imageMarginX = Scale(25); auto imageMarginY = Scale(18); auto imageSize = Scale(60); @@ -430,20 +397,20 @@ static void DrawContentContainer() : ComputeMotion(g_appearTime, CONTENT_CONTAINER_COMMON_MOTION_START, CONTENT_CONTAINER_COMMON_MOTION_END); auto minX = g_isClosing - ? Hermite(256, 306, motion) - : Hermite(306, 256, motion); + ? Hermite(251, 301, motion) + : Hermite(301, 251, motion); auto minY = g_isClosing - ? Hermite(192, 209, motion) - : Hermite(209, 192, motion); + ? Hermite(189, 206, motion) + : Hermite(206, 189, motion); auto maxX = g_isClosing - ? Hermite(1026, 973, motion) - : Hermite(973, 1026, motion); + ? Hermite(1031, 978, motion) + : Hermite(978, 1031, motion); auto maxY = g_isClosing - ? Hermite(601, 569, motion) - : Hermite(569, 601, motion); + ? Hermite(604, 573, motion) + : Hermite(573, 604, motion); ImVec2 min = { Scale(minX), Scale(minY) }; ImVec2 max = { Scale(maxX), Scale(maxY) }; @@ -475,8 +442,8 @@ static void DrawContentContainer() // Draw separators. for (int i = 1; i <= 3; i++) { - auto lineMarginLeft = Scale(31); - auto lineMarginRight = Scale(46); + auto lineMarginLeft = Scale(35); + auto lineMarginRight = Scale(55); auto lineMarginY = Scale(2); ImVec2 lineMin = { clipRectMin.x + lineMarginLeft, clipRectMin.y + itemHeight * i + lineMarginY }; @@ -594,34 +561,35 @@ static void DrawContentContainer() // Draw scroll bar if (rowCount > visibleRowCount) { - float cornerRadius = Scale(25.0f); - float totalHeight = (clipRectMax.y - clipRectMin.y - cornerRadius) - Scale(3.0f); + float cornerRadius = Scale(25); + float totalHeight = (clipRectMax.y - clipRectMin.y - cornerRadius) - Scale(3); float heightRatio = float(visibleRowCount) / float(rowCount); float offsetRatio = float(g_firstVisibleRowIndex) / float(rowCount); - float offsetX = clipRectMax.x - Scale(31.0f); - float offsetY = offsetRatio * totalHeight + clipRectMin.y + Scale(4.0f); - float lineThickness = Scale(1.0f); - float innerMarginX = Scale(2.0f); - float outerMarginX = Scale(16.0f); + float offsetX = clipRectMax.x - Scale(39); + float offsetY = offsetRatio * totalHeight + clipRectMin.y + Scale(4); + float maxY = max.y - cornerRadius - Scale(3); + float lineThickness = Scale(1); + float innerMarginX = Scale(2); + float outerMarginX = Scale(24); // Outline drawList->AddRect ( { /* X */ offsetX - lineThickness, /* Y */ clipRectMin.y - lineThickness }, - { /* X */ clipRectMax.x - outerMarginX + lineThickness, /* Y */ max.y - cornerRadius + lineThickness }, + { /* X */ clipRectMax.x - outerMarginX + lineThickness, /* Y */ maxY + lineThickness }, IM_COL32(255, 255, 255, 155), - Scale(0.5f) + Scale(1) ); // Background drawList->AddRectFilledMultiColor ( { /* X */ offsetX, /* Y */ clipRectMin.y }, - { /* X */ clipRectMax.x - outerMarginX, /* Y */ max.y - cornerRadius }, - IM_COL32(82, 85, 82, 186), - IM_COL32(82, 85, 82, 186), - IM_COL32(74, 73, 74, 185), - IM_COL32(74, 73, 74, 185) + { /* X */ clipRectMax.x - outerMarginX, /* Y */ maxY }, + IM_COL32(123, 125, 123, 255), + IM_COL32(123, 125, 123, 255), + IM_COL32(97, 99, 97, 255), + IM_COL32(97, 99, 97, 255) ); // Scroll Bar Outline @@ -657,6 +625,7 @@ void AchievementMenu::Init() g_upTrophyIcon = LoadTexture(g_trophy, sizeof(g_trophy)); g_upSelectionCursor = LoadTexture(g_select_fill, sizeof(g_select_fill)); + g_upWindow = LoadTexture(g_general_window, sizeof(g_general_window)); } void AchievementMenu::Draw() diff --git a/UnleashedRecomp/ui/achievement_overlay.cpp b/UnleashedRecomp/ui/achievement_overlay.cpp index e6e4eee4..703fce46 100644 --- a/UnleashedRecomp/ui/achievement_overlay.cpp +++ b/UnleashedRecomp/ui/achievement_overlay.cpp @@ -8,6 +8,7 @@ #include #include #include +#include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; @@ -26,6 +27,8 @@ static Achievement g_achievement; static ImFont* g_fntSeurat; +static std::unique_ptr g_upWindow; + static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25) { auto drawList = ImGui::GetForegroundDrawList(); @@ -51,8 +54,6 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25) max.y = Hermite(centreY, max.y, containerMotion); } - auto vertices = GetPauseContainerVertices(min, max, cornerRadius); - // Transparency fade animation. auto colourMotion = g_isClosing ? ComputeMotion(g_appearTime, OVERLAY_CONTAINER_OUTRO_FADE_START, OVERLAY_CONTAINER_OUTRO_FADE_END) @@ -62,46 +63,7 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25) ? Hermite(1, 0, colourMotion) : Hermite(0, 1, colourMotion); - auto colShadow = IM_COL32(0, 0, 0, 156 * alpha); - auto colGradientTop = IM_COL32(197, 194, 197, 200 * alpha); - auto colGradientBottom = IM_COL32(115, 113, 115, 236 * alpha); - - // TODO: add a drop shadow. - - // Draw vertices with gradient. - SetGradient(min, max, colGradientTop, colGradientBottom); - drawList->AddConvexPolyFilled(vertices.data(), vertices.size(), IM_COL32(255, 255, 255, 255 * alpha)); - ResetGradient(); - - // Draw outline. - drawList->AddPolyline - ( - vertices.data(), - vertices.size(), - IM_COL32(247, 247, 247, 255 * alpha), - true, - Scale(2.5f) - ); - - // Offset vertices to draw 3D effect lines. - for (int i = 0; i < vertices.size(); i++) - { - vertices[i].x -= Scale(0.4f); - vertices[i].y -= Scale(0.2f); - } - - auto colLineTop = IM_COL32(165, 170, 165, 230 * alpha); - auto colLineBottom = IM_COL32(190, 190, 190, 230 * alpha); - auto lineThickness = Scale(1.0f); - - // Top left corner bottom to top left corner top. - drawList->AddLine(vertices[0], vertices[1], colLineTop, lineThickness * 0.5f); - - // Top left corner bottom to bottom left. - drawList->AddRectFilledMultiColor({ vertices[0].x - 0.2f, vertices[0].y }, { vertices[6].x + lineThickness - 0.2f, vertices[6].y }, colLineTop, colLineTop, colLineBottom, colLineBottom); - - // Top left corner top to top right. - drawList->AddLine(vertices[1], vertices[2], colLineTop, lineThickness); + DrawPauseContainer(g_upWindow, min, max, alpha); drawList->PushClipRect(min, max); @@ -115,6 +77,8 @@ void AchievementOverlay::Init() constexpr float FONT_SCALE = 2.0f; g_fntSeurat = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); + + g_upWindow = LoadTexture(g_general_window, sizeof(g_general_window)); } void AchievementOverlay::Draw() @@ -138,18 +102,18 @@ void AchievementOverlay::Draw() auto maxSize = std::max(headerSize.x, bodySize.x); // Calculate image margins. - auto imageMarginX = Scale(20); - auto imageMarginY = Scale(20); + auto imageMarginX = Scale(25); + auto imageMarginY = Scale(22.5f); auto imageSize = Scale(60); // Calculate text margins. - auto textMarginX = imageMarginX * 2 + imageSize; + auto textMarginX = imageMarginX * 2 + imageSize - Scale(5); auto textMarginY = imageMarginY + Scale(2); auto containerWidth = imageMarginX + textMarginX + maxSize; - ImVec2 min = { (res.x / 2) - (containerWidth / 2), Scale(50) }; - ImVec2 max = { min.x + containerWidth, min.y + Scale(100) }; + ImVec2 min = { (res.x / 2) - (containerWidth / 2), Scale(55) }; + ImVec2 max = { min.x + containerWidth, min.y + Scale(105) }; if (DrawContainer(min, max)) { diff --git a/UnleashedRecomp/ui/imgui_utils.h b/UnleashedRecomp/ui/imgui_utils.h index 728e5c57..50bbfafe 100644 --- a/UnleashedRecomp/ui/imgui_utils.h +++ b/UnleashedRecomp/ui/imgui_utils.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \ @@ -84,17 +85,65 @@ static std::vector GetPauseContainerVertices(ImVec2 min, ImVec2 max, flo return { - { min.x, min.y + cornerRadius }, - { min.x + cornerRadius, min.y }, - { max.x, min.y }, - { max.x, min.y + cornerRadius }, - { max.x, max.y - cornerRadius }, - { max.x - cornerRadius, max.y }, - { min.x, max.y }, - { min.x, max.y - cornerRadius } + { min.x, min.y + cornerRadius }, // 0 - TL Corner Bottom + { min.x + cornerRadius, min.y }, // 1 - TL Corner Top + { max.x, min.y }, // 2 - TR Corner Top + { max.x, min.y + cornerRadius }, // 3 - TR Corner Bottom + { max.x, max.y - cornerRadius }, // 4 - BR Corner Top + { max.x - cornerRadius, max.y }, // 5 - BR Corner Bottom + { min.x, max.y }, // 6 - BL Corner Bottom + { min.x, max.y - cornerRadius } // 7 - BL Corner Top }; } +static void DrawPauseContainer(std::unique_ptr& texture, ImVec2 min, ImVec2 max, float alpha = 1) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + auto commonWidth = Scale(35); + auto commonHeight = Scale(35); + auto bottomHeight = Scale(5); + + auto tl = PIXELS_TO_UV_COORDS(512, 512, 0, 0, 35, 35); + auto tc = PIXELS_TO_UV_COORDS(512, 512, 51, 0, 5, 35); + auto tr = PIXELS_TO_UV_COORDS(512, 512, 70, 0, 35, 35); + auto cl = PIXELS_TO_UV_COORDS(512, 512, 0, 35, 35, 235); + auto cc = PIXELS_TO_UV_COORDS(512, 512, 51, 35, 5, 235); + auto cr = PIXELS_TO_UV_COORDS(512, 512, 70, 35, 35, 235); + auto bl = PIXELS_TO_UV_COORDS(512, 512, 0, 270, 35, 40); + auto bc = PIXELS_TO_UV_COORDS(512, 512, 51, 270, 5, 40); + auto br = PIXELS_TO_UV_COORDS(512, 512, 70, 270, 35, 40); + + auto colour = IM_COL32(255, 255, 255, 255 * alpha); + + drawList->AddImage(texture.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); + drawList->AddImage(texture.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); + drawList->AddImage(texture.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour); + drawList->AddImage(texture.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); + drawList->AddImage(texture.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour); + drawList->AddImage(texture.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour); + drawList->AddImage(texture.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour); + drawList->AddImage(texture.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bc), colour); + drawList->AddImage(texture.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(br), colour); +} + +static void DrawPauseHeaderContainer(std::unique_ptr& texture, ImVec2 min, ImVec2 max, float alpha = 1) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + auto commonWidth = Scale(35); + + auto left = PIXELS_TO_UV_COORDS(512, 512, 0, 314, 35, 60); + auto centre = PIXELS_TO_UV_COORDS(512, 512, 51, 314, 5, 60); + auto right = PIXELS_TO_UV_COORDS(512, 512, 70, 314, 35, 60); + + auto colour = IM_COL32(255, 255, 255, 255 * alpha); + + drawList->AddImage(texture.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour); + drawList->AddImage(texture.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour); + drawList->AddImage(texture.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour); +} + static void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed) { auto drawList = ImGui::GetForegroundDrawList(); diff --git a/UnleashedRecompResources b/UnleashedRecompResources index e8dc5618..5b5ad279 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit e8dc561807e68b65ce55e32292912c356f3e56c8 +Subproject commit 5b5ad2794a2c78d50dc6a85e71954fb6b9e80ae2 From 2c2e37f6feec6f5fcb48ee3726dbf160d1bd652d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:24:08 +0000 Subject: [PATCH 15/16] Implemented immersive dark title bar --- UnleashedRecomp/ui/window.cpp | 4 ++++ UnleashedRecomp/ui/window.h | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/UnleashedRecomp/ui/window.cpp b/UnleashedRecomp/ui/window.cpp index 304b93ee..fa7abd17 100644 --- a/UnleashedRecomp/ui/window.cpp +++ b/UnleashedRecomp/ui/window.cpp @@ -162,6 +162,10 @@ void Window::Init() SDL_GetWindowWMInfo(s_pWindow, &info); s_handle = info.info.win.window; + + SetDarkTitleBar(true); + + SDL_ShowWindow(s_pWindow); } void Window::Update() diff --git a/UnleashedRecomp/ui/window.h b/UnleashedRecomp/ui/window.h index b560981e..1d6764e4 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -5,6 +5,12 @@ #include #include +#if _WIN32 +#include +#include +#pragma comment(lib, "dwmapi.lib") +#endif + #define DEFAULT_WIDTH 1280 #define DEFAULT_HEIGHT 720 @@ -70,6 +76,23 @@ public: SDL_SetWindowTitle(s_pWindow, title); } + static void SetDarkTitleBar(bool isEnabled) + { +#if _WIN32 + auto version = GetPlatformVersion(); + + if (version.Major < 10 || version.Build <= 17763) + return; + + auto flag = version.Build >= 18985 + ? DWMWA_USE_IMMERSIVE_DARK_MODE + : 19; // DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 + + const DWORD useImmersiveDarkMode = isEnabled; + DwmSetWindowAttribute(s_handle, flag, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode)); +#endif + } + static bool IsFullscreen() { return SDL_GetWindowFlags(s_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -139,7 +162,7 @@ public: static uint32_t GetWindowFlags() { - uint32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; if (Config::WindowState == EWindowState::Maximised) flags |= SDL_WINDOW_MAXIMIZED; From acd7d0358b6bdcb02923f102018aa3eaa80fee2d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:27:03 +0000 Subject: [PATCH 16/16] audio_patches: check if Windows major version is >=10 Just in case. --- UnleashedRecomp/patches/audio_patches.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnleashedRecomp/patches/audio_patches.cpp b/UnleashedRecomp/patches/audio_patches.cpp index 47ee2dfd..e42d37ab 100644 --- a/UnleashedRecomp/patches/audio_patches.cpp +++ b/UnleashedRecomp/patches/audio_patches.cpp @@ -62,7 +62,7 @@ bool AudioPatches::CanAttenuate() auto version = GetPlatformVersion(); - m_isAttenuationSupported = version.Major == 10 && version.Build >= 17763; + m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763; return m_isAttenuationSupported; #else