Merge branch 'options-menu-and-installer' of https://github.com/hedge-dev/UnleashedRecomp into options-menu-and-installer

This commit is contained in:
Dario 2024-12-03 09:23:16 -03:00
commit 7e701ca516
16 changed files with 620 additions and 46 deletions

View file

@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 3.20)
include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(SWA_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty) set(SWA_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty)
set(SWA_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
@ -14,9 +15,9 @@ endif()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_subdirectory(${SWA_THIRDPARTY_ROOT}) 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. # Include sub-projects.
add_subdirectory("UnleashedRecompLib") add_subdirectory("UnleashedRecompLib")

View file

@ -3,6 +3,32 @@ set(TARGET_NAME "SWA")
option(SWA_XAUDIO2 "Use XAudio2 for audio playback" OFF) 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( add_compile_options(
/fp:strict /fp:strict
-march=sandybridge -march=sandybridge
@ -23,10 +49,6 @@ add_compile_definitions(
_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # Microsoft wtf? _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # Microsoft wtf?
_CRT_SECURE_NO_WARNINGS) _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 set(SWA_PRECOMPILED_HEADERS
"stdafx.h" "stdafx.h"
) )
@ -86,6 +108,7 @@ set(SWA_UI_CXX_SOURCES
"ui/achievement_menu.cpp" "ui/achievement_menu.cpp"
"ui/achievement_overlay.cpp" "ui/achievement_overlay.cpp"
"ui/installer_wizard.cpp" "ui/installer_wizard.cpp"
"ui/message_window.cpp"
"ui/options_menu.cpp" "ui/options_menu.cpp"
"ui/sdl_listener.cpp" "ui/sdl_listener.cpp"
"ui/window.cpp" "ui/window.cpp"
@ -107,18 +130,6 @@ set(SWA_INSTALL_CXX_SOURCES
"install/hashes/update.cpp" "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_PATH ${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack)
set(LIBMSPACK_C_SOURCES set(LIBMSPACK_C_SOURCES
@ -301,3 +312,20 @@ generate_aggregate_header(
"${CMAKE_CURRENT_SOURCE_DIR}/api" "${CMAKE_CURRENT_SOURCE_DIR}/api"
"${CMAKE_CURRENT_SOURCE_DIR}/api/SWA.h" "${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/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")

View file

@ -11,6 +11,7 @@
#include <shader/shader_cache.h> #include <shader/shader_cache.h>
#include <ui/achievement_menu.h> #include <ui/achievement_menu.h>
#include <ui/achievement_overlay.h> #include <ui/achievement_overlay.h>
#include <ui/message_window.h>
#include <ui/options_menu.h> #include <ui/options_menu.h>
#include <ui/installer_wizard.h> #include <ui/installer_wizard.h>
@ -1066,6 +1067,7 @@ static void CreateImGuiBackend()
AchievementMenu::Init(); AchievementMenu::Init();
AchievementOverlay::Init(); AchievementOverlay::Init();
MessageWindow::Init();
OptionsMenu::Init(); OptionsMenu::Init();
InstallerWizard::Init(); InstallerWizard::Init();
@ -1741,6 +1743,7 @@ static void DrawImGui()
OptionsMenu::Draw(); OptionsMenu::Draw();
AchievementOverlay::Draw(); AchievementOverlay::Draw();
InstallerWizard::Draw(); InstallerWizard::Draw();
MessageWindow::Draw();
ImGui::Render(); ImGui::Render();

View file

@ -1,2 +1,3 @@
![Ww][Ii][Nn]32/ ![Ww][Ii][Nn]32/
*.c
*.h *.h

View file

@ -14,6 +14,7 @@
#include <string> #include <string>
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <span>
#include <xbox.h> #include <xbox.h>
#include <xxhash.h> #include <xxhash.h>
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>

View file

@ -3,6 +3,12 @@
#include <gpu/imgui_common.h> #include <gpu/imgui_common.h>
#include <app.h> #include <app.h>
#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<std::unique_ptr<ImGuiCallbackData>> g_callbackData; static std::vector<std::unique_ptr<ImGuiCallbackData>> g_callbackData;
static uint32_t g_callbackDataIndex = 0; static uint32_t g_callbackDataIndex = 0;
@ -150,7 +156,80 @@ static void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2&
DrawTextWithOutline<float>(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text, radius, shadowColour); DrawTextWithOutline<float>(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<std::string> 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<std::string> Split(const char* str, char delimiter)
{
std::vector<std::string> 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<std::string> 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<void(const char*, ImVec2)> 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)) 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))

View file

@ -8,16 +8,16 @@
#include <ui/imgui_utils.h> #include <ui/imgui_utils.h>
#include <ui/window.h> #include <ui/window.h>
#include <res/install_001_dds.h> #include <res/images/installer/install_001.dds.h>
#include <res/install_002_dds.h> #include <res/images/installer/install_002.dds.h>
#include <res/install_003_dds.h> #include <res/images/installer/install_003.dds.h>
#include <res/install_004_dds.h> #include <res/images/installer/install_004.dds.h>
#include <res/install_005_dds.h> #include <res/images/installer/install_005.dds.h>
#include <res/install_006_dds.h> #include <res/images/installer/install_006.dds.h>
#include <res/install_007_dds.h> #include <res/images/installer/install_007.dds.h>
#include <res/install_008_dds.h> #include <res/images/installer/install_008.dds.h>
#include <res/miles_electric_icon_dds.h> #include <res/images/installer/miles_electric_icon.dds.h>
#include <res/arrow_circle_dds.h> #include <res/images/installer/arrow_circle.dds.h>
static constexpr double SCANLINES_ANIMATION_TIME = 0.0; static constexpr double SCANLINES_ANIMATION_TIME = 0.0;
static constexpr double SCANLINES_ANIMATION_DURATION = 15.0; static constexpr double SCANLINES_ANIMATION_DURATION = 15.0;
@ -999,16 +999,16 @@ void InstallerWizard::Init()
g_seuratFont = io.Fonts->AddFontFromFileTTF("FOT-SeuratPro-M.otf", 26.0f * FONT_SCALE); 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_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_newRodinFont = io.Fonts->AddFontFromFileTTF("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE);
g_installTextures[0] = LoadTexture(g_install001DDS, g_install001DDS_size); g_installTextures[0] = LoadTexture(g_install_001, sizeof(g_install_001));
g_installTextures[1] = LoadTexture(g_install002DDS, g_install002DDS_size); g_installTextures[1] = LoadTexture(g_install_002, sizeof(g_install_002));
g_installTextures[2] = LoadTexture(g_install003DDS, g_install003DDS_size); g_installTextures[2] = LoadTexture(g_install_003, sizeof(g_install_003));
g_installTextures[3] = LoadTexture(g_install004DDS, g_install004DDS_size); g_installTextures[3] = LoadTexture(g_install_004, sizeof(g_install_004));
g_installTextures[4] = LoadTexture(g_install005DDS, g_install005DDS_size); g_installTextures[4] = LoadTexture(g_install_005, sizeof(g_install_005));
g_installTextures[5] = LoadTexture(g_install006DDS, g_install006DDS_size); g_installTextures[5] = LoadTexture(g_install_006, sizeof(g_install_006));
g_installTextures[6] = LoadTexture(g_install007DDS, g_install007DDS_size); g_installTextures[6] = LoadTexture(g_install_007, sizeof(g_install_007));
g_installTextures[7] = LoadTexture(g_install008DDS, g_install008DDS_size); g_installTextures[7] = LoadTexture(g_install_008, sizeof(g_install_008));
g_milesElectricIcon = LoadTexture(g_milesElectricIconDDS, g_milesElectricIconDDS_size); g_milesElectricIcon = LoadTexture(g_miles_electric_icon, sizeof(g_miles_electric_icon));
g_arrowCircle = LoadTexture(g_arrowCircleDDS, g_arrowCircleDDS_size); g_arrowCircle = LoadTexture(g_arrow_circle, sizeof(g_arrow_circle));
} }
void InstallerWizard::Draw() void InstallerWizard::Draw()

View file

@ -0,0 +1,335 @@
#include "message_window.h"
#include "imgui_utils.h"
#include <api/SWA.h>
#include <gpu/video.h>
#include <exports.h>
#include <res/images/pause.dds.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<GuestTexture> g_upSelectionCursor;
std::string g_text;
int g_result;
std::vector<std::string> 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(g_pause, sizeof(g_pause));
}
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<std::string> 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");
}

View file

@ -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<std::string> buttons = {}, int defaultButtonIndex = 0);
static void Close();
};

View file

@ -2,7 +2,7 @@
#include <api/SWA.h> #include <api/SWA.h>
struct OptionsMenu class OptionsMenu
{ {
public: public:
inline static bool s_isVisible = false; inline static bool s_isVisible = false;

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <res/icon.h> #include <res/images/game_icon.bmp.h>
#include <res/icon_night.h> #include <res/images/game_icon_night.bmp.h>
#include <ui/window_events.h> #include <ui/window_events.h>
#include <user/config.h> #include <user/config.h>
@ -47,11 +47,11 @@ public:
{ {
if (isNight) if (isNight)
{ {
SetIcon((void*)g_iconNight, g_iconNight_size); SetIcon(g_game_icon_night, sizeof(g_game_icon_night));
} }
else else
{ {
SetIcon((void*)g_icon, g_icon_size); SetIcon(g_game_icon, sizeof(g_game_icon));
} }
} }

@ -1 +1 @@
Subproject commit 79a2a18cc8013dd6d76d8300f703f19f6e0ec484 Subproject commit b04e3bdcd9e76a0717cec69822f4eb01cd97d5c6

@ -1 +1 @@
Subproject commit 675b482ec4852b873590fb999d24b426bade2b3a Subproject commit ea3e60cb0d618ec8d24cc745d72b1ac39cc722cc

1
tools/CMakeLists.txt Normal file
View file

@ -0,0 +1 @@
add_subdirectory(${SWA_TOOLS_ROOT}/file_to_c)

View file

@ -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")

View file

@ -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 <filesystem>
#include <fstream>
#include <cstdio>
#include <vector>
std::vector<char> read_file(const char* path) {
std::ifstream input_file{path, std::ios::binary};
std::vector<char> 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<char> 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";
}
}