From 55d40032b068d9d9a189c80141a3330fd0c8b90b 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 1/3] 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 9af198c9..ab279650 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -86,6 +86,7 @@ set(SWA_UI_CXX_SOURCES "ui/achievement_menu.cpp" "ui/achievement_overlay.cpp" "ui/installer_wizard.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 fd1ffc4c..489be1fe 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -1066,6 +1067,7 @@ static void CreateImGuiBackend() AchievementMenu::Init(); AchievementOverlay::Init(); + MessageWindow::Init(); OptionsMenu::Init(); InstallerWizard::Init(); @@ -1741,6 +1743,7 @@ static void DrawImGui() OptionsMenu::Draw(); AchievementOverlay::Draw(); InstallerWizard::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 cb008c80eb92e40f1d41992602477fb8be6f9350 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 02:28:19 +0000 Subject: [PATCH 2/3] Merge branch 'bin2c' into options-menu --- CMakeLists.txt | 5 +- UnleashedRecomp/CMakeLists.txt | 33 +++++++++++ UnleashedRecomp/res/.gitignore | 1 + UnleashedRecomp/ui/window.h | 8 +-- tools/CMakeLists.txt | 1 + tools/file_to_c/CMakeLists.txt | 8 +++ tools/file_to_c/file_to_c.cpp | 105 +++++++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 6 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 0c605965..853ef904 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("UnleashedRecompLib") diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index ab279650..4edbfbed 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 @@ -302,3 +328,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 96464122..9e48300b 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include @@ -47,11 +47,11 @@ public: { if (isNight) { - SetIcon((void*)g_iconNight, g_iconNight_size); + SetIcon(g_game_icon_night, sizeof(g_game_icon_night)); } else { - SetIcon((void*)g_icon, g_icon_size); + SetIcon(g_game_icon, sizeof(g_game_icon)); } } 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..08975574 --- /dev/null +++ b/tools/file_to_c/CMakeLists.txt @@ -0,0 +1,8 @@ +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") 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 f939c70438a7bd7c26d64cd5c924fc5f8cf772b9 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Tue, 3 Dec 2024 03:12:23 +0000 Subject: [PATCH 3/3] Update embedded resources --- UnleashedRecomp/CMakeLists.txt | 26 +++++++--------- UnleashedRecomp/ui/installer_wizard.cpp | 40 ++++++++++++------------- UnleashedRecomp/ui/message_window.cpp | 4 +-- UnleashedRecompResources | 2 +- thirdparty/PowerRecomp | 2 +- 5 files changed, 34 insertions(+), 40 deletions(-) diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 4edbfbed..00aa2ad3 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -49,10 +49,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" ) @@ -134,18 +130,6 @@ set(SWA_INSTALL_CXX_SOURCES "install/hashes/update.cpp" ) -set(INSTALLER_IMAGES_DIR "../UnleashedRecompResources/images/installer") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_001.dds" HEADER_FILE "res/install_001_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install001DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_002.dds" HEADER_FILE "res/install_002_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install002DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_003.dds" HEADER_FILE "res/install_003_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install003DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_004.dds" HEADER_FILE "res/install_004_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install004DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_005.dds" HEADER_FILE "res/install_005_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install005DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_006.dds" HEADER_FILE "res/install_006_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install006DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_007.dds" HEADER_FILE "res/install_007_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install007DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/install_008.dds" HEADER_FILE "res/install_008_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_install008DDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/miles_electric_icon.dds" HEADER_FILE "res/miles_electric_icon_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_milesElectricIconDDS") -BIN2H(SOURCE_FILE "${INSTALLER_IMAGES_DIR}/arrow_circle.dds" HEADER_FILE "res/arrow_circle_dds.h" ARRAY_TYPE "unsigned char" VARIABLE_NAME "g_arrowCircleDDS") - set(LIBMSPACK_PATH ${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack) set(LIBMSPACK_C_SOURCES @@ -332,6 +316,16 @@ 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/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_arrow_circle") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_001") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_002") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_003.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_003.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_003") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_004.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_004.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_004") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_005.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_005.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_005") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_006.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_006.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_006") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_007.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_007.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_007") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_install_008") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/miles_electric_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/miles_electric_icon.dds" ARRAY_TYPE "unsigned char" ARRAY_NAME "g_miles_electric_icon") 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/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index 55029ddf..1183496d 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -8,16 +8,16 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include static constexpr double SCANLINES_ANIMATION_TIME = 0.0; static constexpr double SCANLINES_ANIMATION_DURATION = 15.0; @@ -979,16 +979,16 @@ void InstallerWizard::Init() g_seuratFont = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 26.0f * FONT_SCALE); g_dfsogeistdFont = io.Fonts->AddFontFromFileTTF("DFSoGeiStd-W7.otf", 48.0f * FONT_SCALE); g_newRodinFont = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); - g_installTextures[0] = LoadTexture(g_install001DDS, g_install001DDS_size); - g_installTextures[1] = LoadTexture(g_install002DDS, g_install002DDS_size); - g_installTextures[2] = LoadTexture(g_install003DDS, g_install003DDS_size); - g_installTextures[3] = LoadTexture(g_install004DDS, g_install004DDS_size); - g_installTextures[4] = LoadTexture(g_install005DDS, g_install005DDS_size); - g_installTextures[5] = LoadTexture(g_install006DDS, g_install006DDS_size); - g_installTextures[6] = LoadTexture(g_install007DDS, g_install007DDS_size); - g_installTextures[7] = LoadTexture(g_install008DDS, g_install008DDS_size); - g_milesElectricIcon = LoadTexture(g_milesElectricIconDDS, g_milesElectricIconDDS_size); - g_arrowCircle = LoadTexture(g_arrowCircleDDS, g_arrowCircleDDS_size); + g_installTextures[0] = LoadTexture(g_install_001, sizeof(g_install_001)); + g_installTextures[1] = LoadTexture(g_install_002, sizeof(g_install_002)); + g_installTextures[2] = LoadTexture(g_install_003, sizeof(g_install_003)); + g_installTextures[3] = LoadTexture(g_install_004, sizeof(g_install_004)); + g_installTextures[4] = LoadTexture(g_install_005, sizeof(g_install_005)); + g_installTextures[5] = LoadTexture(g_install_006, sizeof(g_install_006)); + g_installTextures[6] = LoadTexture(g_install_007, sizeof(g_install_007)); + g_installTextures[7] = LoadTexture(g_install_008, sizeof(g_install_008)); + g_milesElectricIcon = LoadTexture(g_miles_electric_icon, sizeof(g_miles_electric_icon)); + g_arrowCircle = LoadTexture(g_arrow_circle, sizeof(g_arrow_circle)); } void InstallerWizard::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() diff --git a/UnleashedRecompResources b/UnleashedRecompResources index 79a2a18c..b04e3bdc 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit 79a2a18cc8013dd6d76d8300f703f19f6e0ec484 +Subproject commit b04e3bdcd9e76a0717cec69822f4eb01cd97d5c6 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