From 0bd2953ce2b23405a272283c10b334667200a9a0 Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Tue, 24 Mar 2026 13:13:17 -0300 Subject: [PATCH 01/23] Add Submodule: RecompFrontEnd --- .gitmodules | 3 +++ lib/RecompFrontend | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/RecompFrontend diff --git a/.gitmodules b/.gitmodules index acfb6c7..208c5b3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "lib/rt64"] path = lib/rt64 url = https://github.com/rt64/rt64 +[submodule "lib/RecompFrontend"] + path = lib/RecompFrontend + url = https://github.com/N64Recomp/RecompFrontend.git diff --git a/lib/RecompFrontend b/lib/RecompFrontend new file mode 160000 index 0000000..b3b7ebb --- /dev/null +++ b/lib/RecompFrontend @@ -0,0 +1 @@ +Subproject commit b3b7ebb4ec1a8a763c0191486f1b3329f9499a48 From d1d512b5f783acb0255a4990a0bf6eff5c4c8999 Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Wed, 25 Mar 2026 00:26:09 -0300 Subject: [PATCH 02/23] Submodule Update: N64ModernRuntime --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 9ae9dbb..0bb76b0 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 9ae9dbbe41cafcd6f425135c726aebe80777d682 +Subproject commit 0bb76b0fc7078cdec37ec3d220af4aaf8212c611 From 5887e584fc3fe1d944f111b89048915eb82e1815 Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Wed, 25 Mar 2026 00:30:57 -0300 Subject: [PATCH 03/23] Submodule update: N64ModernRuntime: config_option_enhancements_rebase --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index 0bb76b0..a857af0 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit 0bb76b0fc7078cdec37ec3d220af4aaf8212c611 +Subproject commit a857af008dbb9cd0d55ce0f3132c50cc6a9a5c4c From 9b60c31a25251df290e021b824f1e82d6a11ec5c Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Wed, 25 Mar 2026 00:36:21 -0300 Subject: [PATCH 04/23] Submodule Update: N64ModernRuntime --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index a857af0..ca568b6 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit a857af008dbb9cd0d55ce0f3132c50cc6a9a5c4c +Subproject commit ca568b6ad79b9029d14077f0c3ffa757727c5559 From 58a4b54490291be0dac4ea65527208543321b16f Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Wed, 25 Mar 2026 00:40:25 -0300 Subject: [PATCH 05/23] Submodule Update: RT64 --- lib/rt64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rt64 b/lib/rt64 index aa047b8..f0d8c9f 160000 --- a/lib/rt64 +++ b/lib/rt64 @@ -1 +1 @@ -Subproject commit aa047b8158034552466175b8e8554988caa18976 +Subproject commit f0d8c9f29b579a143744f76c222899dc82c12e81 From 7d9415e6793226e0a410abd003c175abfbbbf033 Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Thu, 26 Mar 2026 19:45:26 -0300 Subject: [PATCH 06/23] Submodule Update: N64ModernRuntime (sonicdcer/NewFrontEnd) --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index ca568b6..c840160 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit ca568b6ad79b9029d14077f0c3ffa757727c5559 +Subproject commit c8401602b5bf7cb91785033763421e1ced7e07eb From 9bc9f6f8e5520347159d646058f24e4d44dee03b Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Thu, 26 Mar 2026 20:31:12 -0300 Subject: [PATCH 07/23] Submodule Update: N64ModernRuntime (fork: sonicdcer/NewFrontEnd) --- lib/N64ModernRuntime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime index c840160..c515776 160000 --- a/lib/N64ModernRuntime +++ b/lib/N64ModernRuntime @@ -1 +1 @@ -Subproject commit c8401602b5bf7cb91785033763421e1ced7e07eb +Subproject commit c515776f9238b4c1e9bd7ff6c7740b1ab1a3106b From e8d755b88dc1750ad585113ade050d84b8340794 Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Thu, 26 Mar 2026 21:01:59 -0300 Subject: [PATCH 08/23] Submodule Update: RecompFrontend (fork: sonicdcer/RecompFrontend) --- lib/RecompFrontend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/RecompFrontend b/lib/RecompFrontend index b3b7ebb..460a19b 160000 --- a/lib/RecompFrontend +++ b/lib/RecompFrontend @@ -1 +1 @@ -Subproject commit b3b7ebb4ec1a8a763c0191486f1b3329f9499a48 +Subproject commit 460a19b5e2999133ba955fb8b8bf94a35266c565 From b0a5e40ca08825c150367a5325d8617a5859c9d5 Mon Sep 17 00:00:00 2001 From: Sonic Dreamcaster Date: Thu, 26 Mar 2026 21:12:24 -0300 Subject: [PATCH 09/23] Use new Frontend --- CMakeLists.txt | 105 +- assets/recomp.rcss | 2513 +---------------------- icons/Arrow.svg | 5 + icons/Cont.svg | 3 + icons/Keyboard.svg | 3 + icons/Plus.svg | 12 + icons/Quit.svg | 11 + icons/RecordBorder.svg | 3 + icons/Reset.svg | 5 + icons/Trash.svg | 8 + icons/VizMap/ButtonLarge.svg | 5 + icons/VizMap/ButtonMedium.svg | 4 + icons/VizMap/ButtonSmall.svg | 5 + icons/VizMap/DPad.svg | 10 + icons/VizMap/DPadArrow.svg | 7 + icons/VizMap/Map.svg | 4 + icons/VizMap/Shield.svg | 19 + icons/VizMap/Target.svg | 46 + icons/X.svg | 5 + include/zelda_config.h | 69 + include/zelda_debug.h | 5 + include/zelda_launcher.h | 11 + patches/recompui_event_structs.h | 19 +- shaders/InterfacePS.hlsl | 11 - shaders/InterfaceVS.hlsl | 23 - src/game/config.cpp | 651 ++---- src/game/recomp_api.cpp | 32 +- src/game/recomp_data_api.cpp | 2 +- src/main/launcher_animation.cpp | 13 + src/main/main.cpp | 101 +- src/main/theme.cpp | 106 + src/main/theme.h | 6 + src/ui/core/ui_context.cpp | 725 ------- src/ui/core/ui_context.h | 70 - src/ui/core/ui_resource.h | 24 - src/ui/elements/ui_button.cpp | 114 - src/ui/elements/ui_button.h | 33 - src/ui/elements/ui_clickable.cpp | 77 - src/ui/elements/ui_clickable.h | 23 - src/ui/elements/ui_container.cpp | 13 - src/ui/elements/ui_container.h | 14 - src/ui/elements/ui_element.cpp | 516 ----- src/ui/elements/ui_element.h | 106 - src/ui/elements/ui_image.cpp | 11 - src/ui/elements/ui_image.h | 14 - src/ui/elements/ui_label.cpp | 43 - src/ui/elements/ui_label.h | 22 - src/ui/elements/ui_radio.cpp | 268 --- src/ui/elements/ui_radio.h | 58 - src/ui/elements/ui_scroll_container.cpp | 27 - src/ui/elements/ui_scroll_container.h | 19 - src/ui/elements/ui_slider.cpp | 243 --- src/ui/elements/ui_slider.h | 58 - src/ui/elements/ui_span.cpp | 15 - src/ui/elements/ui_span.h | 16 - src/ui/elements/ui_style.cpp | 612 ------ src/ui/elements/ui_style.h | 108 - src/ui/elements/ui_text_input.cpp | 61 - src/ui/elements/ui_text_input.h | 23 - src/ui/elements/ui_toggle.cpp | 161 -- src/ui/elements/ui_toggle.h | 38 - src/ui/elements/ui_types.h | 282 --- src/ui/ui_api.cpp | 1031 ---------- src/ui/ui_api_events.cpp | 118 -- src/ui/ui_api_images.cpp | 131 -- src/ui/ui_api_images.h | 10 - src/ui/ui_color_hack.cpp | 172 -- src/ui/ui_config.cpp | 1088 ---------- src/ui/ui_config_sub_menu.cpp | 269 --- src/ui/ui_config_sub_menu.h | 117 -- src/ui/ui_elements.cpp | 42 - src/ui/ui_elements.h | 16 - src/ui/ui_helpers.h | 154 -- src/ui/ui_launcher.cpp | 127 -- src/ui/ui_mod_details_panel.cpp | 153 -- src/ui/ui_mod_details_panel.h | 52 - src/ui/ui_mod_installer.cpp | 343 ---- src/ui/ui_mod_installer.h | 42 - src/ui/ui_mod_menu.cpp | 799 ------- src/ui/ui_mod_menu.h | 130 -- src/ui/ui_prompt.cpp | 360 ---- src/ui/ui_renderer.cpp | 720 ------- src/ui/ui_renderer.h | 38 - src/ui/ui_rml_hacks.cpp | 106 - src/ui/ui_rml_hacks.hpp | 13 - src/ui/ui_state.cpp | 1012 --------- src/ui/ui_utils.cpp | 20 - src/ui/ui_utils.h | 11 - src/ui/util/bem.h | 12 - src/ui/util/hsv.cpp | 145 -- src/ui/util/hsv.h | 77 - 91 files changed, 702 insertions(+), 14222 deletions(-) create mode 100644 icons/Arrow.svg create mode 100644 icons/Cont.svg create mode 100644 icons/Keyboard.svg create mode 100644 icons/Plus.svg create mode 100644 icons/Quit.svg create mode 100644 icons/RecordBorder.svg create mode 100644 icons/Reset.svg create mode 100644 icons/Trash.svg create mode 100644 icons/VizMap/ButtonLarge.svg create mode 100644 icons/VizMap/ButtonMedium.svg create mode 100644 icons/VizMap/ButtonSmall.svg create mode 100644 icons/VizMap/DPad.svg create mode 100644 icons/VizMap/DPadArrow.svg create mode 100644 icons/VizMap/Map.svg create mode 100644 icons/VizMap/Shield.svg create mode 100644 icons/VizMap/Target.svg create mode 100644 icons/X.svg create mode 100644 include/zelda_launcher.h delete mode 100644 shaders/InterfacePS.hlsl delete mode 100644 shaders/InterfaceVS.hlsl create mode 100644 src/main/launcher_animation.cpp create mode 100644 src/main/theme.cpp create mode 100644 src/main/theme.h delete mode 100644 src/ui/core/ui_context.cpp delete mode 100644 src/ui/core/ui_context.h delete mode 100644 src/ui/core/ui_resource.h delete mode 100644 src/ui/elements/ui_button.cpp delete mode 100644 src/ui/elements/ui_button.h delete mode 100644 src/ui/elements/ui_clickable.cpp delete mode 100644 src/ui/elements/ui_clickable.h delete mode 100644 src/ui/elements/ui_container.cpp delete mode 100644 src/ui/elements/ui_container.h delete mode 100644 src/ui/elements/ui_element.cpp delete mode 100644 src/ui/elements/ui_element.h delete mode 100644 src/ui/elements/ui_image.cpp delete mode 100644 src/ui/elements/ui_image.h delete mode 100644 src/ui/elements/ui_label.cpp delete mode 100644 src/ui/elements/ui_label.h delete mode 100644 src/ui/elements/ui_radio.cpp delete mode 100644 src/ui/elements/ui_radio.h delete mode 100644 src/ui/elements/ui_scroll_container.cpp delete mode 100644 src/ui/elements/ui_scroll_container.h delete mode 100644 src/ui/elements/ui_slider.cpp delete mode 100644 src/ui/elements/ui_slider.h delete mode 100644 src/ui/elements/ui_span.cpp delete mode 100644 src/ui/elements/ui_span.h delete mode 100644 src/ui/elements/ui_style.cpp delete mode 100644 src/ui/elements/ui_style.h delete mode 100644 src/ui/elements/ui_text_input.cpp delete mode 100644 src/ui/elements/ui_text_input.h delete mode 100644 src/ui/elements/ui_toggle.cpp delete mode 100644 src/ui/elements/ui_toggle.h delete mode 100644 src/ui/elements/ui_types.h delete mode 100644 src/ui/ui_api.cpp delete mode 100644 src/ui/ui_api_events.cpp delete mode 100644 src/ui/ui_api_images.cpp delete mode 100644 src/ui/ui_api_images.h delete mode 100644 src/ui/ui_color_hack.cpp delete mode 100644 src/ui/ui_config.cpp delete mode 100644 src/ui/ui_config_sub_menu.cpp delete mode 100644 src/ui/ui_config_sub_menu.h delete mode 100644 src/ui/ui_elements.cpp delete mode 100644 src/ui/ui_elements.h delete mode 100644 src/ui/ui_helpers.h delete mode 100644 src/ui/ui_launcher.cpp delete mode 100644 src/ui/ui_mod_details_panel.cpp delete mode 100644 src/ui/ui_mod_details_panel.h delete mode 100644 src/ui/ui_mod_installer.cpp delete mode 100644 src/ui/ui_mod_installer.h delete mode 100644 src/ui/ui_mod_menu.cpp delete mode 100644 src/ui/ui_mod_menu.h delete mode 100644 src/ui/ui_prompt.cpp delete mode 100644 src/ui/ui_renderer.cpp delete mode 100644 src/ui/ui_renderer.h delete mode 100644 src/ui/ui_rml_hacks.cpp delete mode 100644 src/ui/ui_rml_hacks.hpp delete mode 100644 src/ui/ui_state.cpp delete mode 100644 src/ui/ui_utils.cpp delete mode 100644 src/ui/ui_utils.h delete mode 100644 src/ui/util/bem.h delete mode 100644 src/ui/util/hsv.cpp delete mode 100644 src/ui/util/hsv.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cbcc0f4..435ac45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,15 +51,7 @@ endif() add_subdirectory(${CMAKE_SOURCE_DIR}/lib/rt64 ${CMAKE_BINARY_DIR}/rt64) -# set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}") set(BUILD_SHARED_LIBS OFF) -SET(LUNASVG_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) -add_subdirectory(${CMAKE_SOURCE_DIR}/lib/lunasvg) -# set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}") -SET(RMLUI_SVG_PLUGIN ON CACHE BOOL "" FORCE) -SET(RMLUI_TESTS_ENABLED OFF CACHE BOOL "" FORCE) -add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RmlUi) -target_compile_definitions(rmlui_core PRIVATE LUNASVG_BUILD_STATIC) add_subdirectory(${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime) @@ -148,14 +140,13 @@ add_custom_command(OUTPUT add_executable(Starfox64Recompiled) set (SOURCES + ${CMAKE_SOURCE_DIR}/src/main/launcher_animation.cpp ${CMAKE_SOURCE_DIR}/src/main/main.cpp ${CMAKE_SOURCE_DIR}/src/main/support.cpp ${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp ${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp - ${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp + ${CMAKE_SOURCE_DIR}/src/main/theme.cpp - ${CMAKE_SOURCE_DIR}/src/game/input.cpp - ${CMAKE_SOURCE_DIR}/src/game/controls.cpp ${CMAKE_SOURCE_DIR}/src/game/config.cpp ${CMAKE_SOURCE_DIR}/src/game/scene_table.cpp ${CMAKE_SOURCE_DIR}/src/game/debug.cpp @@ -165,68 +156,35 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/game/recomp_data_api.cpp ${CMAKE_SOURCE_DIR}/src/game/rom_decompression.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_state.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_prompt.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_config_sub_menu.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_mod_details_panel.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_mod_installer.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_mod_menu.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_api.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_api_events.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_api_images.cpp - ${CMAKE_SOURCE_DIR}/src/ui/ui_utils.cpp - ${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp - ${CMAKE_SOURCE_DIR}/src/ui/core/ui_context.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_clickable.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_radio.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_scroll_container.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_slider.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_span.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_style.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_text_input.cpp - ${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp - ${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp # ${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp - - ${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp ) -if (APPLE) - list(APPEND SOURCES ${CMAKE_SOURCE_DIR}/src/main/support_apple.mm) -endif() - target_include_directories(Starfox64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/N64Recomp/include - ${CMAKE_SOURCE_DIR}/lib/concurrentqueue - ${CMAKE_SOURCE_DIR}/lib/GamepadMotionHelpers - ${CMAKE_SOURCE_DIR}/lib/RmlUi/Include - ${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends + ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/thirdparty/sse2neon ${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib ${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/hlslpp/include ${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/dxc/inc ${CMAKE_SOURCE_DIR}/lib/rt64/src ${CMAKE_SOURCE_DIR}/lib/rt64/src/rhi ${CMAKE_SOURCE_DIR}/lib/rt64/src/render - ${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include + ${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompinput/include + ${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompui/include + ${CMAKE_SOURCE_DIR}/lib/RecompFrontend/recompui/src ${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/nativefiledialog-extended/src/include ${CMAKE_SOURCE_DIR}/lib/SlotMap - ${CMAKE_BINARY_DIR}/shaders ${CMAKE_CURRENT_BINARY_DIR} ) +# Generate icon_bytes.c from the app icon PNG. +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h + COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h + DEPENDS ${CMAKE_SOURCE_DIR}/icons/512.png +) +target_sources(Starfox64Recompiled PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c) + if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") target_compile_options(Starfox64Recompiled PRIVATE -march=nehalem @@ -313,15 +271,9 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(SDL2 REQUIRED) find_package(X11 REQUIRED) + add_compile_definitions("PLUME_SDL_VULKAN_ENABLED") add_compile_definitions("RT64_SDL_WINDOW_VULKAN") - # Generate icon_bytes.c from the app icon PNG. - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h - COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h - DEPENDS ${CMAKE_SOURCE_DIR}/icons/512.png - ) - target_sources(Starfox64Recompiled PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c) - message(STATUS "SDL2_FOUND = ${SDL2_FOUND}") message(STATUS "SDL2_INCLUDE_DIRS = ${SDL2_INCLUDE_DIRS}") @@ -351,18 +303,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") target_link_libraries(Starfox64Recompiled PRIVATE "-latomic -static-libstdc++" ${CMAKE_DL_LIBS} Threads::Threads) endif() -target_link_libraries(Starfox64Recompiled PRIVATE - PatchesLib - RecompiledFuncs - librecomp - ultramodern - rt64 - RmlUi::Core - RmlUi::Debugger - nfd - lunasvg -) - # TODO fix the rt64 CMake script so that this doesn't need to be duplicated here # For DXC set (DXC_COMMON_OPTS "-I${PROJECT_SOURCE_DIR}/src") @@ -395,8 +335,21 @@ else() endif() endif() -build_vertex_shader(Starfox64Recompiled "shaders/InterfaceVS.hlsl" "shaders/InterfaceVS.hlsl") -build_pixel_shader(Starfox64Recompiled "shaders/InterfacePS.hlsl" "shaders/InterfacePS.hlsl") +set(RECOMP_FRONTEND_N64MODERNRUNTIME_PATH ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime) +set(RECOMP_FRONTEND_RT64_PATH ${CMAKE_SOURCE_DIR}/lib/rt64) + +add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RecompFrontend) + +target_link_libraries(Starfox64Recompiled PRIVATE + PatchesLib + RecompiledFuncs + recompui + recompinput + librecomp + ultramodern + rt64 + nfd +) # Embed all .nrm files in the "mods" directory file(GLOB NRM_FILES "${CMAKE_SOURCE_DIR}/mods/*.nrm") diff --git a/assets/recomp.rcss b/assets/recomp.rcss index 551e60d..1ebcae5 100644 --- a/assets/recomp.rcss +++ b/assets/recomp.rcss @@ -1,2512 +1,5 @@ -/* stylelint-disable color-no-hex, color-hex-length */ -h1 { - font-size: 68dp; - letter-spacing: 4.76dp; - line-height: 68dp; - font-style: normal; - font-weight: 700; -} - -h2 { - font-size: 52dp; - letter-spacing: 3.64dp; - line-height: 52dp; - font-style: normal; - font-weight: 700; -} - -h3, .tab { - font-size: 36dp; - letter-spacing: 2.52dp; - line-height: 36dp; - font-style: normal; - font-weight: 700; -} - -.label-lg, .menu-list-item__label, .button--large { - font-size: 36dp; - letter-spacing: 3.96dp; - line-height: 36dp; - font-style: normal; - font-weight: 700; -} - -.label-md, .config-debug-option__label, .button, .config-option__title, .config-option--hz .config-option__title, .config-group__title, .control-option__label { - font-size: 28dp; - letter-spacing: 3.08dp; - line-height: 28dp; - font-style: normal; - font-weight: 700; -} - -.label-sm, .config-debug__select-wrapper .config-debug__select-label, .subtitle-title__disclaimer, .config-option__range-label, .config-option-range__label, .config-option__radio-tabs .config-option__tab-label, -.config-option__list .config-option__tab-label, .centered-page__controls > label { - font-size: 20dp; - letter-spacing: 2.8dp; - line-height: 20dp; - font-style: normal; - font-weight: 700; - text-transform: uppercase; -} - -.config-option__details { - font-size: 18dp; - letter-spacing: 2.52dp; - line-height: 18dp; - font-style: normal; - font-weight: 400; -} - -body, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .config-description__contents, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config__wrapper p { - font-size: 20dp; - letter-spacing: 0dp; - line-height: 20dp; - font-style: normal; - font-weight: 400; -} - -.toggle__icon { - font-family: promptfont; - font-size: 56dp; - font-style: normal; - font-weight: 400; - line-height: 56dp; -} - -.prompt-font { - font-family: promptfont; - font-size: 40dp; - font-style: normal; - font-weight: 400; - line-height: 40dp; -} - -.prompt-font-sm, .input-viz__mappings div { - font-family: promptfont; - font-size: 32dp; - font-style: normal; - font-weight: 400; - line-height: 32dp; -} - /* -@include trans-colors; + In order for rml to parse the base style sheet and combine with this one, any style needs to at least be present. + If you add styles to this file later, you can clear the current contents of this file. */ -/* -@include trans-colors-opa; -*/ -/* -@include trans-colors-svg; -*/ -/* -@include trans-colors-border; -*/ -.nav-vert, .nav-dir, .nav-all, .config-debug__select-wrapper select selectbox option, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .toggle, .subtitle-title:not(:disabled, [disabled]), .menu-list-item:not(:disabled, [disabled]), .icon-button:not([disabled]), .button:not([disabled]), .button, .config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config-option__radio-tabs input.radio, -.config-option__list input.radio, .config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox, .tab, .control-option__binding:not([disabled]) { - nav-up: auto; - nav-down: auto; -} - -.nav-horiz, .nav-dir, .nav-all, .config-debug__select-wrapper select selectbox option, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .toggle, .subtitle-title:not(:disabled, [disabled]), .menu-list-item:not(:disabled, [disabled]), .icon-button:not([disabled]), .button:not([disabled]), .button, .config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config-option__radio-tabs input.radio, -.config-option__list input.radio, .config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox, .tab, .control-option__binding:not([disabled]) { - nav-right: auto; - nav-left: auto; -} - -.nav-foc, .nav-all, .config-debug__select-wrapper select selectbox option, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .toggle, .subtitle-title:not(:disabled, [disabled]), .menu-list-item:not(:disabled, [disabled]), .icon-button:not([disabled]), .button:not([disabled]), .button, .config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config-option__radio-tabs input.radio, -.config-option__list input.radio, .config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox, .tab, .control-option__binding:not([disabled]) { - focus: auto; - tab-index: auto; -} - -/* -@include set-color(COLOR); -*/ -/* stylelint-disable color-no-hex, color-hex-length */ -h1 { - font-size: 68dp; - letter-spacing: 4.76dp; - line-height: 68dp; - font-style: normal; - font-weight: 700; -} - -h2 { - font-size: 52dp; - letter-spacing: 3.64dp; - line-height: 52dp; - font-style: normal; - font-weight: 700; -} - -h3, .tab { - font-size: 36dp; - letter-spacing: 2.52dp; - line-height: 36dp; - font-style: normal; - font-weight: 700; -} - -.label-lg, .menu-list-item__label, .button--large { - font-size: 36dp; - letter-spacing: 3.96dp; - line-height: 36dp; - font-style: normal; - font-weight: 700; -} - -.label-md, .config-debug-option__label, .button, .config-option__title, .config-option--hz .config-option__title, .config-group__title, .control-option__label { - font-size: 28dp; - letter-spacing: 3.08dp; - line-height: 28dp; - font-style: normal; - font-weight: 700; -} - -.label-sm, .config-debug__select-wrapper .config-debug__select-label, .subtitle-title__disclaimer, .config-option__range-label, .config-option-range__label, .config-option__radio-tabs .config-option__tab-label, -.config-option__list .config-option__tab-label, .centered-page__controls > label { - font-size: 20dp; - letter-spacing: 2.8dp; - line-height: 20dp; - font-style: normal; - font-weight: 700; - text-transform: uppercase; -} - -.config-option__details { - font-size: 18dp; - letter-spacing: 2.52dp; - line-height: 18dp; - font-style: normal; - font-weight: 400; -} - -body, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .config-description__contents, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config__wrapper p { - font-size: 20dp; - letter-spacing: 0dp; - line-height: 20dp; - font-style: normal; - font-weight: 400; -} - -.toggle__icon { - font-family: promptfont; - font-size: 56dp; - font-style: normal; - font-weight: 400; - line-height: 56dp; -} - -.prompt-font { - font-family: promptfont; - font-size: 40dp; - font-style: normal; - font-weight: 400; - line-height: 40dp; -} - -.prompt-font-sm, .input-viz__mappings div { - font-family: promptfont; - font-size: 32dp; - font-style: normal; - font-weight: 400; - line-height: 32dp; -} - -/* -@include trans-colors; -*/ -/* -@include trans-colors-opa; -*/ -/* -@include trans-colors-svg; -*/ -/* -@include trans-colors-border; -*/ -.nav-vert, .nav-dir, .nav-all, .config-debug__select-wrapper select selectbox option, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .toggle, .subtitle-title:not(:disabled, [disabled]), .menu-list-item:not(:disabled, [disabled]), .icon-button:not([disabled]), .button:not([disabled]), .button, .config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config-option__radio-tabs input.radio, -.config-option__list input.radio, .config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox, .tab, .control-option__binding:not([disabled]) { - nav-up: auto; - nav-down: auto; -} - -.nav-horiz, .nav-dir, .nav-all, .config-debug__select-wrapper select selectbox option, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .toggle, .subtitle-title:not(:disabled, [disabled]), .menu-list-item:not(:disabled, [disabled]), .icon-button:not([disabled]), .button:not([disabled]), .button, .config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config-option__radio-tabs input.radio, -.config-option__list input.radio, .config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox, .tab, .control-option__binding:not([disabled]) { - nav-right: auto; - nav-left: auto; -} - -.nav-foc, .nav-all, .config-debug__select-wrapper select selectbox option, .config-debug__select-wrapper select, .config-debug__select-wrapper input, .toggle, .subtitle-title:not(:disabled, [disabled]), .menu-list-item:not(:disabled, [disabled]), .icon-button:not([disabled]), .button:not([disabled]), .button, .config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option, .config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper, .config-option__radio-tabs input.radio, -.config-option__list input.radio, .config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox, .tab, .control-option__binding:not([disabled]) { - focus: auto; - tab-index: auto; -} - -/* -@include set-color(COLOR); -*/ -/* stylelint-disable color-no-hex */ -/* stylelint-disable selector-max-id */ -* { - box-sizing: border-box; -} - -hr { - display: block; - padding: 1.5dp; - background: #08070D; -} - -body { - color: #fff; - font-family: "latolatin"; - font-size: 20dp; - font-style: normal; - font-weight: normal; -} - -/* div { - focus:none; - tab-index:none; -} */ -div#window { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border-color: rgba(255, 255, 255, 0.2); - background-color: #121018; -} - -div#content { - z-index: 2; - width: auto; - height: 100%; - overflow: hidden auto; - text-align: center; -} - -p { - text-align: left; -} - -input.submit { - margin-left: 0; -} - -input.text, -input.password { - box-sizing: border-box; - height: 31dp; - padding: 11dp 10dp 0; - text-align: left; - cursor: text; -} - -textarea { - padding: 14dp 12dp 10dp; - text-align: left; - cursor: text; -} - -input.text, -input.password, -select, -textarea { - /* color: #333; */ - /* font-size: 13dp */ - height: auto; -} - -table input.text { - box-sizing: border-box; - width: 100%; - height: auto; - /* height: 18dp; */ - margin: 0; - border-width: 1.1dp; - border-color: #000; - background-color: #fff; - font-size: 15dp; - /* padding: 0 5dp; */ - line-height: 1; - decorator: none; - /* vertical-align: center; */ -} - -input.radio { - flex: 0; - width: 0dp; - nav-up: auto; - nav-right: auto; - nav-down: auto; - nav-left: auto; - tab-index: auto; - focus: auto; -} - -input.checkbox { - width: 20dp; - height: 20dp; - nav-up: auto; - nav-right: auto; - nav-down: auto; - nav-left: auto; - tab-index: auto; - focus: auto; -} - -scrollbarvertical, scrollbarhorizontal { - margin: 0; - border: 0; -} -scrollbarvertical slidertrack, scrollbarhorizontal slidertrack { - background: #DABAF7; - opacity: 0.05; -} -scrollbarvertical sliderbar, scrollbarhorizontal sliderbar { - border-radius: 5dp; - background: #DABAF7; - opacity: 0.1; -} -scrollbarvertical sliderbar:hover:not(:active), scrollbarhorizontal sliderbar:hover:not(:active) { - opacity: 0.2; -} -scrollbarvertical sliderbar:active, scrollbarhorizontal sliderbar:active { - opacity: 0.3; -} -scrollbarvertical sliderarrowdec, scrollbarvertical sliderarrowinc, scrollbarhorizontal sliderarrowdec, scrollbarhorizontal sliderarrowinc { - width: 0; - height: 0; -} - -scrollbarvertical { - width: 12dp; -} -scrollbarvertical slidertrack { - width: 12dp; -} -scrollbarvertical sliderbar { - width: 12dp; -} - -scrollbarhorizontal { - height: 12dp; -} -scrollbarhorizontal slidertrack { - height: 12dp; -} -scrollbarhorizontal sliderbar { - height: 12dp; -} - -.centered-page { - display: flex; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 64dp; - background-color: rgba(255, 255, 255, 0.1); -} - -.centered-page__modal { - display: flex; - position: relative; - flex: 1 1 100%; - flex-direction: column; - width: 100%; - max-width: 1692.4444444444dp; - height: 100%; - margin: auto; - border-width: 1.1dp; - border-radius: 16dp; - border-color: rgba(255, 255, 255, 0.2); - background: rgba(8, 7, 13, 0.9); -} -.centered-page__modal > .tabs { - display: flex; - position: relative; - flex: 1 1 100%; - flex-direction: column; - width: 100%; - max-width: 1692.4444444444dp; - height: 100%; - margin: auto; -} -.centered-page__modal panels { - flex: 1 1 100%; -} - -.centered-page__controls { - display: flex; - position: absolute; - bottom: 24dp; - flex-direction: row; - align-items: center; - justify-content: center; - width: 100%; - max-width: 1692.4444444444dp; - height: auto; - margin: 0 auto; -} -.centered-page__controls > label { - display: inline-block; - align-items: center; - justify-content: space-between; - width: auto; - height: 24dp; -} -.centered-page__controls > label:not(:last-child) { - margin-right: 40dp; -} -.centered-page__controls > label > span:first-child { - margin-right: 4dp; -} - -.control-option { - color: #CCCCCC; - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - display: flex; - position: relative; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 100%; - height: auto; - padding: 4dp 16dp 4dp 20dp; - border-radius: 8dp; - background-color: rgba(0, 0, 0, 0); -} -.control-option svg { - image-color: #CCCCCC; -} -.control-option svg { - transition: image-color 0.05s linear-in-out, background-color 0.05s linear-in-out; -} -.control-option:focus-visible:not(:disabled, [disabled]), .control-option:hover:not(:disabled, [disabled]) { - color: #F2F2F2; - background-color: rgba(190, 184, 219, 0.1); -} -.control-option:focus-visible:not(:disabled, [disabled]) svg, .control-option:hover:not(:disabled, [disabled]) svg { - image-color: #F2F2F2; -} -.control-option:disabled, .control-option[disabled] { - opacity: 0.5; -} -[cur-binding-slot="0"] .control-option--active .control-option__binding[bind-slot="0"] { - border-color: #F86039; -} -[cur-binding-slot="0"] .control-option--active .control-option__binding[bind-slot="0"] .control-option__binding-icon { - opacity: 0; -} -[cur-binding-slot="0"] .control-option--active .control-option__binding[bind-slot="0"] .control-option__binding-recording { - opacity: 1; -} -[cur-binding-slot="1"] .control-option--active .control-option__binding[bind-slot="1"] { - border-color: #F86039; -} -[cur-binding-slot="1"] .control-option--active .control-option__binding[bind-slot="1"] .control-option__binding-icon { - opacity: 0; -} -[cur-binding-slot="1"] .control-option--active .control-option__binding[bind-slot="1"] .control-option__binding-recording { - opacity: 1; -} -.control-option .icon-button { - flex: 1 1 auto; -} - -.control-option__label { - flex: 2 1 300dp; - height: auto; - white-space: nowrap; -} - -.control-option__bindings { - display: flex; - position: relative; - flex: 2 1 400dp; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 100%; - height: 56dp; - padding: 0 12dp 0 4dp; -} - -.control-option__binding { - color: #CCCCCC; - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out, border-color 0.05s linear-in-out; - display: flex; - position: relative; - flex: 1 1 100%; - align-items: center; - justify-content: center; - width: 100%; - height: 56dp; - margin: 0 4dp; - padding: 8dp; - border-width: 1.1dp; - border-radius: 8dp; - border-color: rgba(190, 184, 219, 0.1); - background-color: rgba(190, 184, 219, 0.1); -} -.control-option__binding svg { - image-color: #CCCCCC; -} -.control-option__binding svg { - transition: image-color 0.05s linear-in-out, background-color 0.05s linear-in-out; -} -.control-option__binding:focus, .control-option__binding:hover { - color: #F2F2F2; - border-color: #F2F2F2; - background-color: rgba(255, 255, 255, 0.1); -} -.control-option__binding:focus svg, .control-option__binding:hover svg { - image-color: #F2F2F2; -} -.control-option__binding:active { - color: rgb(244.6, 244.6, 244.6); -} -.control-option__binding:active svg { - image-color: rgb(244.6, 244.6, 244.6); -} -.control-option__binding:disabled, .control-option__binding[disabled] { - color: #CCCCCC; - opacity: 0.5; -} -.control-option__binding:disabled svg, .control-option__binding[disabled] svg { - image-color: #CCCCCC; -} -.control-option__binding:not([disabled]) { - cursor: pointer; -} - -.control-option__binding-icon { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - opacity: 1; -} - -@keyframes control-option__binding-recording-scale { - 0% { - transform: scale(1); - } - 50% { - transform: scale(0.85); - } - 100% { - transform: scale(1); - } -} -.control-option__binding-recording { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - display: flex; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - align-items: center; - justify-content: center; - opacity: 0; -} -.control-option__binding-recording .control-option__binding-circle { - width: 24dp; - height: 24dp; - animation: 1.5s sine-in-out infinite control-option__binding-recording-scale; - border-radius: 24dp; - background-color: #F86039; -} -.control-option__binding-recording .control-option__binding-edge { - position: absolute; - top: 50%; - left: 50%; - width: 36dp; - height: 36dp; - transform: translate(-50%, -50%); -} -.control-option__binding-recording .control-option__binding-edge > svg.control-option__binding-edge-svg { - width: 36dp; - height: 36dp; - image-color: #F86039; -} - -/* - Example: - -
Graphics
-
-
-*/ -.tabs tabs { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; -} - -.tab { - display: block; - position: relative; - margin: 0; - padding: 20dp 24dp; - opacity: 0.9; - background-color: rgba(0, 0, 0, 0); - color: rgba(255, 255, 255, 0.6); -} -.tab:selected { - color: #F2F2F2; -} -.tab:selected .tab__indicator { - background-color: rgba(255, 255, 255, 0.6); -} -.tab:selected:hover { - cursor: default; -} -.rmlui-window:not([mouse-active]) .tab:focus { - transition: none; - animation: blue-pulse 0.75s infinite; -} -.rmlui-window:not([mouse-active]) .tab:focus:selected .tab__indicator { - animation: blue-pulse-background 0.75s infinite; -} -.tab:focus, .tab:hover { - opacity: 1; - color: #F2F2F2; - cursor: pointer; -} - -.tab__indicator { - position: absolute; - right: 0; - bottom: 2dp; - left: 0; - height: 2dp; - background-color: rgba(0, 0, 0, 0); -} - -.config__icon-buttons { - display: flex; - position: absolute; - top: 8dp; - right: 0dp; - flex-direction: row; - align-items: center; - justify-content: flex-end; - width: auto; -} -.config__icon-buttons .icon-button { - margin: 0 8dp; -} - -.config__form { - border-top-width: 1.1dp; - border-top-color: rgba(255, 255, 255, 0.1); - display: flex; - flex: 1 1 100%; - flex-direction: column; - justify-content: space-between; - width: 100%; - height: 100%; - border-bottom-right-radius: 16dp; - border-bottom-left-radius: 16dp; -} - -.config__wrapper { - flex: 1 1 100%; - width: auto; - height: auto; - padding: 16dp; - border-radius: 0dp; - border-bottom-right-radius: 16dp; - border-bottom-left-radius: 16dp; - background-color: rgba(0, 0, 0, 0.35); - text-align: left; -} -.config__wrapper p { - padding: 16dp; - line-height: 28dp; - white-space: pre-line; -} -.config__wrapper p b { - color: #B97DF2; -} -.config__wrapper p i { - color: #E9CD35; - font-style: normal; -} - -.config__hz-wrapper { - display: flex; - flex: 1 1 100%; - flex-direction: row; - width: 100%; - height: 100%; - border-radius: 0dp; - text-align: left; -} - -.config__header, .config__footer { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - background-color: rgba(0, 0, 0, 0.35); -} - -.config__header { - border-bottom-width: 1.1dp; - border-bottom-color: rgba(255, 255, 255, 0.1); - padding: 12dp 20dp; -} - -.config__footer { - border-top-width: 1.1dp; - border-top-color: rgba(255, 255, 255, 0.1); - padding: 20dp 20dp; - border-bottom-right-radius: 16dp; - border-bottom-left-radius: 16dp; -} - -.config__header-left { - display: flex; - flex: 1 1 auto; - flex-direction: row; -} -.config__header-left > :not(:first-child) { - margin-left: 8dp; -} - -.config__row { - display: flex; - flex-direction: row; -} - -.config-group { - position: relative; -} -.config-group--scrollable { - flex: 1 1 100%; - width: auto; - height: auto; - padding: 0 0 0 16dp; -} -.config-group--scrollable .config-group__wrapper { - max-height: 100%; - overflow-y: auto; -} -.config-group__title { - color: #B97DF2; -} -.config-group__title--hidden { - display: none; -} -.config-group__wrapper { - padding: 16dp 0; -} - -.config-option { - display: flex; - flex: 1; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - margin: 16dp 0dp 24dp; -} -.config-option--hz { - flex-direction: row-reverse; - align-items: center; - margin-top: 4dp; - margin-bottom: 4dp; -} -.config-option--hz .config-option__title { - flex: 1 1 100%; -} -.config-option--hz .config-option__list { - flex: 1 1 auto; - width: auto; -} -.config-option--hz:first-child { - margin-top: 0; -} -.config-option--hz:last-child { - margin-bottom: 0; -} - -.config-option__title { - padding: 0 12dp; -} - -.config-option__radio-tabs, -.config-option__list { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; - width: 100%; - height: auto; - padding: 0; -} -.config-option__radio-tabs input:first-of-type, -.config-option__list input:first-of-type { - nav-left: none; -} -.config-option__radio-tabs input:last-of-type, -.config-option__list input:last-of-type { - nav-right: none; -} -.config-option__radio-tabs .config-option__tab-label, -.config-option__list .config-option__tab-label { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - display: block; - position: relative; - height: auto; - margin: 4dp 12dp 0; - padding: 8dp 0; - color: rgba(255, 255, 255, 0.6); - tab-index: none; -} -.config-option__radio-tabs .config-option__tab-label:hover, -.config-option__list .config-option__tab-label:hover { - color: #F2F2F2; - cursor: pointer; -} -.config-option__radio-tabs .config-option__checkbox-wrapper, -.config-option__list .config-option__checkbox-wrapper { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - width: 32dp; - height: 32dp; - margin: 4dp 12dp 0; - border-radius: 8dp; - opacity: 0.5; - background-color: rgba(190, 184, 219, 0.1); - cursor: pointer; -} -.config-option__radio-tabs .config-option__checkbox-wrapper:hover, -.config-option__list .config-option__checkbox-wrapper:hover { - opacity: 1; -} -.config-option__radio-tabs .config-option__checkbox-wrapper[checked], -.config-option__list .config-option__checkbox-wrapper[checked] { - background-color: #3333FF; -} -.config-option__radio-tabs .config-option__checkbox, -.config-option__list .config-option__checkbox { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - visibility: visible; - width: 0; - height: 0; -} -.config-option__radio-tabs input.radio, -.config-option__list input.radio { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - visibility: visible; - width: 0; - height: 0; -} -.config-option__radio-tabs input.radio:not(:disabled):checked + .config-option__tab-label, -.config-option__list input.radio:not(:disabled):checked + .config-option__tab-label { - border-bottom: 1dp; - border-color: #F2F2F2; - color: #F2F2F2; -} -.config-option__radio-tabs input.radio:not(:disabled):checked + .config-option__tab-label:hover, -.config-option__list input.radio:not(:disabled):checked + .config-option__tab-label:hover { - cursor: default; -} -.rmlui-window:not([mouse-active]) .config-option__radio-tabs input.radio:not(:disabled):focus + .config-option__tab-label, -.rmlui-window:not([mouse-active]) .config-option__list input.radio:not(:disabled):focus + .config-option__tab-label { - transition: none; - animation: blue-pulse 0.75s infinite; - border-color: #17D6E8; - color: #17D6E8; -} -.config-option__radio-tabs input.radio:not(:disabled):focus + .config-option__tab-label, .config-option__radio-tabs input.radio:not(:disabled):hover + .config-option__tab-label, -.config-option__list input.radio:not(:disabled):focus + .config-option__tab-label, -.config-option__list input.radio:not(:disabled):hover + .config-option__tab-label { - color: #F2F2F2; -} -.config-option__radio-tabs input.radio:disabled + .config-option__tab-label, -.config-option__list input.radio:disabled + .config-option__tab-label { - opacity: 0.5; -} -.config-option__radio-tabs input.radio:disabled + .config-option__tab-label:hover, -.config-option__list input.radio:disabled + .config-option__tab-label:hover { - cursor: default; -} -.config-option__radio-tabs input.range slidertrack, -.config-option__list input.range slidertrack { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - height: 2dp; - margin-top: 8dp; - background-color: rgba(255, 255, 255, 0.2); -} -.config-option__radio-tabs input.range sliderbar, -.config-option__list input.range sliderbar { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - width: 16dp; - height: 16dp; - margin-top: 1dp; - margin-right: -8dp; - margin-left: -8dp; - transition: background-color 0.05s linear-in-out; - border-radius: 8dp; - background-color: #CCCCCC; -} -.rmlui-window:not([mouse-active]) .config-option__radio-tabs input.range sliderbar:focus, -.rmlui-window:not([mouse-active]) .config-option__list input.range sliderbar:focus { - border-width: 1.1dp; - border-color: #3333FF; - animation: blue-pulse-background 0.75s infinite; -} -.config-option__radio-tabs input.range sliderbar:hover, -.config-option__list input.range sliderbar:hover { - background-color: #F2F2F2; - cursor: pointer; -} -.config-option__radio-tabs input.range sliderbar:active, -.config-option__radio-tabs input.range slidertrack:active + sliderbar, -.config-option__list input.range sliderbar:active, -.config-option__list input.range slidertrack:active + sliderbar { - background-color: #17D6E8; -} -.config-option__radio-tabs input.range sliderarrowdec, -.config-option__radio-tabs input.range sliderarrowinc, -.config-option__list input.range sliderarrowdec, -.config-option__list input.range sliderarrowinc { - display: none; -} - -.config-option__details { - height: 18dp; - margin: 14dp 12dp 0; - color: #B97DF2; -} - -.config-option-color { - width: 100%; - max-width: 360dp; - height: auto; - margin-top: 4dp; - margin-left: 12dp; - padding: 0; -} -.config-option-color__preview-wrapper { - display: flex; - flex-direction: row; - width: 100%; - height: 72dp; -} -.config-option-color__preview-block { - display: block; - width: 88dp; - height: 100%; - border-width: 1.1dp; - border-radius: 16dp; - border-color: rgba(255, 255, 255, 0.2); -} -.config-option-color__hsv-wrapper { - display: flex; - flex: 1 1 100%; - flex-direction: column; - width: auto; - height: auto; - padding-left: 8dp; -} -.config-option-color__hsv-wrapper .config-option-range { - flex: 1 1 auto; -} -.config-option-color__hsv-wrapper .config-option-range label { - min-width: 72dp; -} -.config-option-color__hsv-wrapper .config-option-range input { - flex: 1 1 auto; -} - -.config-option-range { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; - width: 100%; - max-width: 360dp; - height: auto; - margin-top: 4dp; - padding: 0; -} -.config-option-range__label { - display: block; - width: 56dp; - margin: 0 12dp; - margin-right: 16dp; - padding: 0; - color: #F2F2F2; - tab-index: none; -} -.config-option-range__range-input { - flex: 1; -} -.config-option-range__range-input slidertrack { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - height: 2dp; - margin-top: 8dp; - background-color: rgba(255, 255, 255, 0.2); -} -.config-option-range__range-input sliderbar { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - width: 16dp; - height: 16dp; - margin-top: 1dp; - margin-right: -8dp; - margin-left: -8dp; - transition: background-color 0.05s linear-in-out; - border-radius: 8dp; - background-color: #CCCCCC; -} -.rmlui-window:not([mouse-active]) .config-option-range__range-input sliderbar:focus { - border-width: 1.1dp; - border-color: #3333FF; - animation: blue-pulse-background 0.75s infinite; -} -.config-option-range__range-input sliderbar:hover { - background-color: #F2F2F2; - cursor: pointer; -} -.config-option-range__range-input sliderbar:active, -.config-option-range__range-input slidertrack:active + sliderbar { - background-color: #17D6E8; -} -.config-option-range__range-input sliderarrowdec, -.config-option-range__range-input sliderarrowinc { - display: none; -} - -.config-option__range-wrapper { - max-width: 360dp; - margin-top: 4dp; -} - -.config-option__range-label { - display: block; - width: 56dp; - margin: 0 12dp; - margin-right: 16dp; - padding: 0; - color: #F2F2F2; - tab-index: none; -} - -.config-option-dropdown, .config-option-textfield { - display: flex; - position: relative; - flex: 1 1 100%; - flex-direction: row; - align-items: center; - justify-content: flex-start; - width: auto; - height: auto; - padding: 8dp 24dp 8dp 12dp; -} -.config-option-dropdown__select, .config-option-textfield__select { - display: block; - height: 48dp; - padding: 14dp; - cursor: pointer; -} -.config-option-dropdown__wrapper, .config-option-textfield__wrapper { - display: flex; - align-items: center; - justify-content: flex-start; - width: 100%; - height: auto; - padding: 2dp 0 12dp; - cursor: text; -} -.config-option-dropdown__wrapper input, .config-option-textfield__wrapper input { - width: 100%; - height: auto; - vertical-align: middle; -} -.config-option-dropdown__select, .config-option-dropdown__wrapper, .config-option-textfield__select, .config-option-textfield__wrapper { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out, border-color 0.05s linear-in-out; - border-width: 1.1dp; - border-color: rgba(255, 255, 255, 0.5); - position: relative; - box-sizing: border-box; - flex: 1 1 100%; - width: auto; - border-radius: 12dp; - background-color: rgba(255, 255, 255, 0.05); -} -.config-option-dropdown__select svg, .config-option-dropdown__wrapper svg, .config-option-textfield__select svg, .config-option-textfield__wrapper svg { - transition: image-color 0.05s linear-in-out, background-color 0.05s linear-in-out; -} -.config-option-dropdown__select:hover, .config-option-dropdown__select:focus, .config-option-dropdown__wrapper:hover, .config-option-dropdown__wrapper:focus, .config-option-textfield__select:hover, .config-option-textfield__select:focus, .config-option-textfield__wrapper:hover, .config-option-textfield__wrapper:focus { - border-width: 1.1dp; - border-color: rgba(255, 255, 255, 0.8); - background-color: rgba(255, 255, 255, 0.2); -} -.config-option-dropdown__select selectvalue, .config-option-dropdown__wrapper selectvalue, .config-option-textfield__select selectvalue, .config-option-textfield__wrapper selectvalue { - display: inline; - height: auto; - margin: auto 0; -} -.config-option-dropdown__select selectbox, .config-option-dropdown__wrapper selectbox, .config-option-textfield__select selectbox, .config-option-textfield__wrapper selectbox { - border-width: 1.1dp; - border-color: rgba(255, 255, 255, 0.2); - margin-top: 2dp; - padding: 4dp 0; - border-radius: 12dp; - background-color: #191622; -} -.config-option-dropdown__select selectbox option, .config-option-dropdown__wrapper selectbox option, .config-option-textfield__select selectbox option, .config-option-textfield__wrapper selectbox option { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - padding: 8dp 12dp; - background-color: rgba(0, 0, 0, 0); - color: #CCCCCC; - font-weight: 400; -} -.config-option-dropdown__select selectbox option:hover, .config-option-dropdown__select selectbox option:focus, .config-option-dropdown__wrapper selectbox option:hover, .config-option-dropdown__wrapper selectbox option:focus, .config-option-textfield__select selectbox option:hover, .config-option-textfield__select selectbox option:focus, .config-option-textfield__wrapper selectbox option:hover, .config-option-textfield__wrapper selectbox option:focus { - background-color: rgba(255, 255, 255, 0.2); -} -.config-option-dropdown__select selectbox option:hover:not(:checked), .config-option-dropdown__wrapper selectbox option:hover:not(:checked), .config-option-textfield__select selectbox option:hover:not(:checked), .config-option-textfield__wrapper selectbox option:hover:not(:checked) { - cursor: pointer; -} -.config-option-dropdown__select selectbox option:checked, .config-option-dropdown__wrapper selectbox option:checked, .config-option-textfield__select selectbox option:checked, .config-option-textfield__wrapper selectbox option:checked { - background-color: rgba(255, 255, 255, 0.05); - color: #FFFFFF; -} - -.config-description { - flex: 1 1 100%; - width: auto; - height: auto; - padding: 16dp; - border-radius: 0dp; - border-bottom-right-radius: 16dp; - border-bottom-left-radius: 16dp; - background-color: rgba(0, 0, 0, 0.35); - text-align: left; -} -.config-description__contents { - padding: 16dp; - line-height: 28dp; - white-space: pre-line; -} -.config-description__contents b { - color: #B97DF2; -} -.config-description__contents i { - color: #E9CD35; - font-style: normal; -} - -.input-config { - padding: 0; -} - -.input-config__horizontal-split { - display: flex; - position: relative; - flex-direction: row; - height: 100%; -} - -.input-config__mappings { - display: block; - flex: 1 1 auto; - min-width: 640dp; - height: 100%; -} - -.input-config__mappings-scroll { - display: block; - width: 100%; - max-height: 100%; - overflow-y: auto; -} - -.input-config__mappings-wrapper { - padding: 8dp; -} - -.input-config__visual-wrapper { - display: block; - flex: 1 1 100%; - width: auto; - max-width: 1040.4444444444dp; - height: auto; - max-height: 780.3333333333dp; - margin: auto 0; -} - -.input-config__visual-aspect { - position: relative; - width: 100%; - margin: auto 0; - padding-bottom: 75%; - background-color: rgba(0, 0, 0, 0.35); -} - -.input-config__visual { - display: flex; - position: absolute; - top: 16dp; - right: 16dp; - bottom: 16dp; - left: 16dp; - flex-direction: column; - border-radius: 108dp; - background-color: rgba(255, 255, 255, 0.05); -} - -.input-config__visual-half { - display: flex; - position: relative; - flex: 1 1 100%; - flex-direction: row; - padding: 6%; -} -.input-config__visual-half--bottom { - align-items: flex-end; - justify-content: space-between; -} - -.input-config__visual-quarter-left { - display: flex; - flex: 1 1 50%; - align-items: flex-start; - justify-content: flex-start; - width: auto; -} - -.input-config__visual-quarter-right { - display: flex; - flex: 1 1 100%; - align-items: flex-start; - justify-content: flex-end; -} - -.input-config__visual-stick-wrapper { - display: flex; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - align-items: center; - justify-content: center; -} - -.input-viz { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - display: flex; - position: relative; - align-items: center; - justify-content: center; -} -.input-viz > svg:not(.input-viz__dpad-arrow) { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; -} -[cur-input=NONE] .input-viz[visual-input] { - opacity: 1; -} - -.input-viz[visual-input~=A] { - opacity: 0.25; -} -[cur-input=A] .input-viz[visual-input~=A] { - opacity: 1; -} - -.input-viz[visual-input~=B] { - opacity: 0.25; -} -[cur-input=B] .input-viz[visual-input~=B] { - opacity: 1; -} - -.input-viz[visual-input~=Z] { - opacity: 0.25; -} -[cur-input=Z] .input-viz[visual-input~=Z] { - opacity: 1; -} - -.input-viz[visual-input~=START] { - opacity: 0.25; -} -[cur-input=START] .input-viz[visual-input~=START] { - opacity: 1; -} - -.input-viz[visual-input~=DPAD_UP] { - opacity: 0.25; -} -[cur-input=DPAD_UP] .input-viz[visual-input~=DPAD_UP] { - opacity: 1; -} - -.input-viz[visual-input~=DPAD_DOWN] { - opacity: 0.25; -} -[cur-input=DPAD_DOWN] .input-viz[visual-input~=DPAD_DOWN] { - opacity: 1; -} - -.input-viz[visual-input~=DPAD_LEFT] { - opacity: 0.25; -} -[cur-input=DPAD_LEFT] .input-viz[visual-input~=DPAD_LEFT] { - opacity: 1; -} - -.input-viz[visual-input~=DPAD_RIGHT] { - opacity: 0.25; -} -[cur-input=DPAD_RIGHT] .input-viz[visual-input~=DPAD_RIGHT] { - opacity: 1; -} - -.input-viz[visual-input~=L] { - opacity: 0.25; -} -[cur-input=L] .input-viz[visual-input~=L] { - opacity: 1; -} - -.input-viz[visual-input~=R] { - opacity: 0.25; -} -[cur-input=R] .input-viz[visual-input~=R] { - opacity: 1; -} - -.input-viz[visual-input~=C_UP] { - opacity: 0.25; -} -[cur-input=C_UP] .input-viz[visual-input~=C_UP] { - opacity: 1; -} - -.input-viz[visual-input~=C_DOWN] { - opacity: 0.25; -} -[cur-input=C_DOWN] .input-viz[visual-input~=C_DOWN] { - opacity: 1; -} - -.input-viz[visual-input~=C_LEFT] { - opacity: 0.25; -} -[cur-input=C_LEFT] .input-viz[visual-input~=C_LEFT] { - opacity: 1; -} - -.input-viz[visual-input~=C_RIGHT] { - opacity: 0.25; -} -[cur-input=C_RIGHT] .input-viz[visual-input~=C_RIGHT] { - opacity: 1; -} - -.input-viz[visual-input~=X_AXIS_NEG] { - opacity: 0.25; -} -[cur-input=X_AXIS_NEG] .input-viz[visual-input~=X_AXIS_NEG] { - opacity: 1; -} - -.input-viz[visual-input~=X_AXIS_POS] { - opacity: 0.25; -} -[cur-input=X_AXIS_POS] .input-viz[visual-input~=X_AXIS_POS] { - opacity: 1; -} - -.input-viz[visual-input~=Y_AXIS_NEG] { - opacity: 0.25; -} -[cur-input=Y_AXIS_NEG] .input-viz[visual-input~=Y_AXIS_NEG] { - opacity: 1; -} - -.input-viz[visual-input~=Y_AXIS_POS] { - opacity: 0.25; -} -[cur-input=Y_AXIS_POS] .input-viz[visual-input~=Y_AXIS_POS] { - opacity: 1; -} - -.input-viz__button { - color: #F2F2F2; -} -.input-viz__button svg { - image-color: #F2F2F2; -} -.input-viz__button--sm { - width: 64dp; - height: 64dp; -} -.input-viz__button--sm > svg { - width: 64dp; - height: 64dp; -} -.input-viz__button--md { - width: 76dp; - height: 76dp; -} -.input-viz__button--md > svg { - width: 76dp; - height: 76dp; -} -.input-viz__button--lg { - width: 84dp; - height: 84dp; -} -.input-viz__button--lg > svg { - width: 84dp; - height: 84dp; -} -.input-viz__button--C svg { - image-color: #E9CD35; -} -.input-viz__button--A { - margin-top: auto; -} -.input-viz__button--A svg { - image-color: #3333FF; -} -.input-viz__button--B svg { - image-color: #45D043; -} -.input-viz__button--Start svg { - image-color: #F86039; -} - -.input-viz__Z { - width: 136dp; - height: 136dp; -} -.input-viz__Z svg { - image-color: #E9CD35; -} -.input-viz__Z > svg { - width: 136dp; - height: 136dp; -} - -.input-viz.input-viz__dpad { - width: 192dp; - height: 192dp; - position: relative; -} -.input-viz.input-viz__dpad svg { - image-color: #F2F2F2; -} -.input-viz.input-viz__dpad > svg { - width: 192dp; - height: 192dp; -} - -.input-config__visual-stick { - display: flex; - position: relative; - align-items: center; - justify-content: center; - width: 200dp; - height: 200dp; - border-radius: 100dp; - background-color: rgba(255, 255, 255, 0.05); -} - -.input-viz__dpad-split, -.input-viz__stick-split { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - display: flex; - width: 100%; - height: 100%; -} -.input-viz__dpad-split--vertical, -.input-viz__stick-split--vertical { - flex-direction: column; - align-items: center; - justify-content: space-between; -} -.input-viz__dpad-split--horizontal, -.input-viz__stick-split--horizontal { - flex-direction: row; - align-items: center; - justify-content: space-between; -} -.input-viz__dpad-split > div, -.input-viz__stick-split > div { - display: flex; - flex: 1 1 100%; - flex-direction: row; - align-items: center; - justify-content: center; -} - -.input-viz__dpad-split > div { - width: 64dp; - height: 64dp; -} - -.input-viz__stick-split > div { - width: 66.6666666667dp; - height: 66.6666666667dp; -} - -.input-viz__dpad-arrow { - position: absolute; - width: 60dp; - height: 60dp; -} -.input-viz__dpad-arrow--up { - top: 4dp; - margin: 0 auto; -} -.input-viz__dpad-arrow--down { - bottom: 4dp; - margin: 0 auto; - transform: rotate(180deg); -} -.input-viz__dpad-arrow--left { - left: 4dp; - margin: auto 0; - transform: rotate(-90deg); -} -.input-viz__dpad-arrow--right { - right: 4dp; - margin: auto 0; - transform: rotate(90deg); -} - -.input-viz__R { - width: 96dp; - height: 96dp; -} -.input-viz__R svg { - image-color: #FFFFFF; -} -.input-viz__R > svg { - width: 96dp; - height: 96dp; -} - -.input-viz__L { - width: 136dp; - height: 136dp; -} -.input-viz__L svg { - image-color: #17D6E8; -} -.input-viz__L > svg { - width: 136dp; - height: 136dp; -} - -.input-config__c-buttons { - position: relative; - width: 208dp; - height: 132dp; -} -.input-config__c-buttons-lr, .input-config__c-buttons-du { - display: flex; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; -} -.input-config__c-buttons-lr { - flex-direction: row; - align-items: flex-start; - justify-content: space-between; -} -.input-config__c-buttons-du { - flex-direction: column-reverse; - align-items: center; - justify-content: space-between; -} -.input-config__c-buttons .input-viz[visual-input=C_UP] { - margin-top: -32dp; -} - -.input-config__main-buttons { - display: flex; - position: relative; - flex-direction: row; - justify-content: space-between; - width: 268dp; - min-width: 1dp; - height: 128dp; - margin-right: 10dp; -} - -.button { - border-color: rgba(185, 125, 242, 0.8); - background-color: rgba(185, 125, 242, 0.05); - color: #CCCCCC; - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - display: block; - width: auto; - height: auto; - padding: 23dp; - border-width: 1.1dp; - border-radius: 12dp; -} -.button:focus, .button:hover { - border-color: #B97DF2; - background-color: rgba(185, 125, 242, 0.3); - color: #F2F2F2; -} -.button:disabled, .button[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button:active { - background-color: rgba(185, 125, 242, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button--primary { - border-color: rgba(185, 125, 242, 0.8); - background-color: rgba(185, 125, 242, 0.05); - color: #CCCCCC; -} -.button--primary:focus, .button--primary:hover { - border-color: #B97DF2; - background-color: rgba(185, 125, 242, 0.3); - color: #F2F2F2; -} -.button--primary:disabled, .button--primary[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button--primary:active { - background-color: rgba(185, 125, 242, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button--secondary { - border-color: rgba(23, 214, 232, 0.8); - background-color: rgba(23, 214, 232, 0.05); - color: #CCCCCC; -} -.button--secondary:focus, .button--secondary:hover { - border-color: #17D6E8; - background-color: rgba(23, 214, 232, 0.3); - color: #F2F2F2; -} -.button--secondary:disabled, .button--secondary[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button--secondary:active { - background-color: rgba(23, 214, 232, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button--tertiary { - border-color: rgba(242, 242, 242, 0.8); - background-color: rgba(242, 242, 242, 0.05); - color: #CCCCCC; -} -.button--tertiary:focus, .button--tertiary:hover { - border-color: #F2F2F2; - background-color: rgba(242, 242, 242, 0.3); - color: #F2F2F2; -} -.button--tertiary:disabled, .button--tertiary[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button--tertiary:active { - background-color: rgba(242, 242, 242, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button--success { - border-color: rgba(69, 208, 67, 0.8); - background-color: rgba(69, 208, 67, 0.05); - color: #CCCCCC; -} -.button--success:focus, .button--success:hover { - border-color: #45D043; - background-color: rgba(69, 208, 67, 0.3); - color: #F2F2F2; -} -.button--success:disabled, .button--success[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button--success:active { - background-color: rgba(69, 208, 67, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button--error { - border-color: rgba(248, 96, 57, 0.8); - background-color: rgba(248, 96, 57, 0.05); - color: #CCCCCC; -} -.button--error:focus, .button--error:hover { - border-color: #F86039; - background-color: rgba(248, 96, 57, 0.3); - color: #F2F2F2; -} -.button--error:disabled, .button--error[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button--error:active { - background-color: rgba(248, 96, 57, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button--warning { - border-color: rgba(233, 205, 53, 0.8); - background-color: rgba(233, 205, 53, 0.05); - color: #CCCCCC; -} -.button--warning:focus, .button--warning:hover { - border-color: #E9CD35; - background-color: rgba(233, 205, 53, 0.3); - color: #F2F2F2; -} -.button--warning:disabled, .button--warning[disabled] { - color: rgba(255, 255, 255, 0.6); -} -.button--warning:active { - background-color: rgba(233, 205, 53, 0.2); - color: rgb(244.6, 244.6, 244.6); -} -.button:not([disabled]) { - cursor: pointer; -} -.button:disabled, .button[disabled] { - opacity: 0.5; -} -.button__label { - width: auto; - height: auto; -} - -/* - -*/ -.icon-button { - color: #CCCCCC; - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out, border-color 0.05s linear-in-out; - display: flex; - align-items: center; - justify-content: center; - width: 53.8dp; - min-width: 53.8dp; - max-width: 53.8dp; - height: 53.8dp; - min-height: 53.8dp; - max-height: 53.8dp; - border-width: 1.1dp; - border-radius: 26.9dp; - border-color: rgba(0, 0, 0, 0); - background-color: rgba(0, 0, 0, 0); -} -.icon-button svg { - image-color: #CCCCCC; -} -.icon-button svg { - transition: image-color 0.05s linear-in-out, background-color 0.05s linear-in-out; -} -.icon-button:focus, .icon-button:hover { - color: #F2F2F2; - background-color: rgba(255, 255, 255, 0.2); -} -.icon-button:focus svg, .icon-button:hover svg { - image-color: #F2F2F2; -} -.icon-button:active { - color: rgb(244.6, 244.6, 244.6); - background-color: rgba(255, 255, 255, 0.1); -} -.icon-button:active svg { - image-color: rgb(244.6, 244.6, 244.6); -} -.icon-button:disabled, .icon-button[disabled] { - color: #CCCCCC; - opacity: 0.5; -} -.icon-button:disabled svg, .icon-button[disabled] svg { - image-color: #CCCCCC; -} -.icon-button:not([disabled]) { - cursor: pointer; -} -.icon-button svg { - width: 32dp; - height: 32dp; -} -.icon-button--primary { - border-color: rgba(185, 125, 242, 0.8); - background-color: rgba(185, 125, 242, 0.05); -} -.icon-button--primary:focus, .icon-button--primary:hover { - border-color: #B97DF2; - background-color: rgba(185, 125, 242, 0.3); -} -.icon-button--primary:active { - background-color: rgba(185, 125, 242, 0.2); -} -.icon-button--secondary { - border-color: rgba(23, 214, 232, 0.8); - background-color: rgba(23, 214, 232, 0.05); -} -.icon-button--secondary:focus, .icon-button--secondary:hover { - border-color: #17D6E8; - background-color: rgba(23, 214, 232, 0.3); -} -.icon-button--secondary:active { - background-color: rgba(23, 214, 232, 0.2); -} -.icon-button--tertiary { - border-color: rgba(242, 242, 242, 0.8); - background-color: rgba(242, 242, 242, 0.05); -} -.icon-button--tertiary:focus, .icon-button--tertiary:hover { - border-color: #F2F2F2; - background-color: rgba(242, 242, 242, 0.3); -} -.icon-button--tertiary:active { - background-color: rgba(242, 242, 242, 0.2); -} -.icon-button--success { - border-color: rgba(69, 208, 67, 0.8); - background-color: rgba(69, 208, 67, 0.05); -} -.icon-button--success:focus, .icon-button--success:hover { - border-color: #45D043; - background-color: rgba(69, 208, 67, 0.3); -} -.icon-button--success:active { - background-color: rgba(69, 208, 67, 0.2); -} -.icon-button--error { - border-color: rgba(248, 96, 57, 0.8); - background-color: rgba(248, 96, 57, 0.05); -} -.icon-button--error:focus, .icon-button--error:hover { - border-color: #F86039; - background-color: rgba(248, 96, 57, 0.3); -} -.icon-button--error:active { - background-color: rgba(248, 96, 57, 0.2); -} -.icon-button--warning { - border-color: rgba(233, 205, 53, 0.8); - background-color: rgba(233, 205, 53, 0.05); -} -.icon-button--warning:focus, .icon-button--warning:hover { - border-color: #E9CD35; - background-color: rgba(233, 205, 53, 0.3); -} -.icon-button--warning:active { - background-color: rgba(233, 205, 53, 0.2); -} - -.launcher { - display: block; - position: relative; - flex-direction: row; - justify-content: space-between; - width: 100%; - height: 100%; - background-color: #08070D; -} - -.launcher__vertical-split { - display: flex; - position: absolute; - top: 0; - right: 50%; - bottom: 0; - left: 0; - flex-direction: column; - align-items: flex-start; - justify-content: space-between; -} -.launcher__vertical-split--right { - right: 0; - left: 50%; - align-items: flex-end; -} - -@keyframes slide-mm-bg-over { - 0% { - transform: translateX(100dp); - } - 100% { - transform: translateX(0dp); - } -} -.launcher__background-wrapper { - display: flex; - position: absolute; - top: -55vw; - right: -100%; - bottom: -50vw; - left: -70vw; - align-items: center; - justify-content: flex-start; - transform: translateX(0dp); - animation: 25s cubic-out 1 slide-mm-bg-over; -} - -@keyframes fade-mm-in { - 0% { - opacity: 0; - } - 100% { - opacity: 0.1; - } -} -.launcher__background-mm { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: auto; - height: 100%; - animation: 2.5s cubic-in-out 1 fade-mm-in; - opacity: 0.15; -} - -.launcher__title-quadrant { - flex: 1 1 auto; - width: auto; - height: auto; - padding-top: 96dp; - padding-left: 96dp; -} -.launcher__title-quadrant--right { - padding-right: 96dp; - padding-left: 0; -} - -.launcher__content-quadrant { - display: flex; - position: relative; - flex: 1 1 100%; - flex-direction: column; - align-items: flex-start; - justify-content: flex-end; - width: 100%; - height: auto; - padding: 32dp; -} - -/* - Example layout: - - - Variants: - .menu-list-item--right (align to right side) - - Optional: - -
Coming Soon™
- -*/ -.menu-list-item { - color: #CCCCCC; - display: flex; - flex-direction: row; - align-items: center; - width: 100%; - height: auto; - padding: 16dp; - border-radius: 8dp; - background-color: rgba(0, 0, 0, 0); - cursor: pointer; -} -.menu-list-item svg { - image-color: #CCCCCC; -} -.menu-list-item--right { - flex-direction: row-reverse; - align-content: flex-end; -} -.menu-list-item--right .menu-list-item__bullet { - margin-left: 12dp; - opacity: 1; -} -.menu-list-item--right.menu-list-item:focus:not(:disabled, [disabled]), .menu-list-item--right.menu-list-item:hover:not(:disabled, [disabled]) { - decorator: horizontal-gradient(#7A2AC600 #DABAF714); -} -.menu-list-item:focus:not(:disabled, [disabled]), .menu-list-item:hover:not(:disabled, [disabled]) { - color: #B97DF2; - decorator: horizontal-gradient(#7A2AC614 #DABAF700); -} -.menu-list-item:focus:not(:disabled, [disabled]) svg, .menu-list-item:hover:not(:disabled, [disabled]) svg { - image-color: #B97DF2; -} -.menu-list-item:focus:not(:disabled, [disabled]) .menu-list-item__bullet, .menu-list-item:hover:not(:disabled, [disabled]) .menu-list-item__bullet { - opacity: 1; -} -.menu-list-item:disabled, .menu-list-item[disabled] { - opacity: 0.5; - tab-index: none; - cursor: default; -} - -.menu-list-item__bullet { - margin-right: 12dp; - opacity: 0; -} - -/* - Example layout: - - - - Variants: - .subtitle-title--right (align to right side) - - Optional: - -
Coming Soon™
- -*/ -.subtitle-title { - display: block; - position: relative; - flex-direction: column; - align-content: flex-start; - align-items: flex-start; - width: auto; - height: auto; - padding: 0; - background-color: rgba(0, 0, 0, 0); - color: #CCCCCC; - text-align: left; - cursor: pointer; -} -.subtitle-title--right { - align-content: flex-end; -} -.subtitle-title--right, .subtitle-title--right > * { - text-align: right; -} -.subtitle-title[selected] { - color: #F2F2F2; - cursor: default; -} -.subtitle-title:focus:not(:disabled, [disabled]), .subtitle-title:hover:not(:disabled, [disabled], [selected]) { - color: #B97DF2; -} -.subtitle-title:disabled, .subtitle-title[disabled] { - opacity: 0.5; - cursor: default; - tab-index: none; -} -.subtitle-title h3 { - margin-bottom: 6dp; -} -.subtitle-title h1 { - margin-top: 6dp; -} -.subtitle-title__disclaimer { - margin-top: 16dp; -} - -.toggle { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - display: flex; - position: relative; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 162dp; - height: 72dp; - border-radius: 36dp; - opacity: 0.9; - background: rgba(0, 0, 0, 0); - cursor: pointer; -} -.toggle:hover, .toggle:focus-visible, .toggle:focus { - opacity: 1; - background-color: rgba(23, 214, 232, 0.3); -} -.toggle:active { - opacity: 1; - background-color: rgba(23, 214, 232, 0.05); -} -.toggle .toggle__border { - position: absolute; - top: 1.1dp; - right: 1.1dp; - bottom: 1.1dp; - left: 1.1dp; - border-width: 1.1dp; - border-color: #A2EFF6; - display: block; - border-radius: 36dp; -} -.toggle .toggle__floater { - position: absolute; - top: 50%; - left: 4dp; - width: 80dp; - height: 64dp; - transform: translateY(-50%); - border-radius: 32dp; - background: #25A1AD; -} -.toggle--checked .toggle__floater { - left: 78dp; -} -.toggle--checked .toggle__icon.toggle__icon--left { - opacity: 0.9; - color: #A2EFF6; -} -.toggle--checked .toggle__icon.toggle__icon--right { - opacity: 1; - color: #F2F2F2; -} - -.toggle__icons { - display: flex; - position: absolute; - top: 50%; - right: 16dp; - left: 16dp; - align-items: center; - justify-content: space-between; - height: 56dp; - transform: translateY(-50%); -} - -.toggle__icon { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - display: flex; - align-items: center; - justify-content: center; - width: 56dp; - height: 56dp; - color: #F2F2F2; -} -.toggle__icon--right { - opacity: 1; - color: #A2EFF6; -} - -.bottom-left { - display: flex; - position: absolute; - bottom: 4dp; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; - width: 100%; - max-width: 1692.4444444444dp; - height: auto; - margin: 0 4dp; -} - -.prompt__overlay { - background-color: rgba(190, 184, 219, 0.1); - pointer-events: auto; -} -.prompt__overlay, .prompt__content-wrapper { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; -} -.prompt__content-wrapper { - display: flex; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - align-items: center; - justify-content: center; -} -.prompt__content { - display: flex; - position: relative; - flex: 1 1 100%; - flex-direction: column; - width: 100%; - max-width: 700dp; - height: auto; - margin: auto; - border-width: 1.1dp; - border-radius: 16dp; - border-color: rgba(255, 255, 255, 0.2); - background: rgba(8, 7, 13, 0.9); -} -.prompt__content h3, .prompt__content p { - margin: 24dp; -} -.prompt__content p { - margin-top: 0; -} -.prompt__controls { - display: flex; - flex-direction: row; - justify-content: center; - padding: 24dp 12dp; - border-top-width: 1.1dp; - border-top-color: rgba(255, 255, 255, 0.1); -} -.prompt__controls .button { - min-width: 233.3333333333dp; - margin: 0 12dp; - text-align: center; - nav-up: none; - nav-down: none; -} - -.config-debug { - display: block; - position: relative; - width: 100%; - max-height: 100%; - padding: 8dp; -} - -.config-debug__scroll { - display: block; - position: relative; - width: 100%; - max-height: 100%; - overflow-y: auto; -} - -.config-debug-option { - color: #CCCCCC; - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out; - border-bottom-width: 1.1dp; - border-bottom-color: rgba(255, 255, 255, 0.1); - display: block; - position: relative; - flex-direction: column; - width: 100%; - height: auto; - padding: 12dp 4dp; - background-color: rgba(0, 0, 0, 0); -} -.config-debug-option svg { - image-color: #CCCCCC; -} -.config-debug-option svg { - transition: image-color 0.05s linear-in-out, background-color 0.05s linear-in-out; -} -.config-debug-option:focus:not(:disabled, [disabled]), .config-debug-option:focus-visible:not(:disabled, [disabled]), .config-debug-option:hover:not(:disabled, [disabled]) { - color: #F2F2F2; - background-color: rgba(190, 184, 219, 0.1); -} -.config-debug-option:focus:not(:disabled, [disabled]) svg, .config-debug-option:focus-visible:not(:disabled, [disabled]) svg, .config-debug-option:hover:not(:disabled, [disabled]) svg { - image-color: #F2F2F2; -} -.config-debug-option:disabled, .config-debug-option[disabled] { - opacity: 0.5; -} -.config-debug-option .icon-button { - margin-left: 8dp; -} - -.config-debug__option-split { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; -} - -.config-debug-option__label { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - padding: 4dp 16dp 12dp; - width: auto; - height: auto; - white-space: nowrap; -} - -.config-debug__option-controls { - display: block; - position: relative; - flex: 1 1 auto; - height: auto; - width: auto; - max-width: 800dp; - padding: 0 12dp; -} - -.config-debug__option-trigger { - flex: 1 1 auto; -} - -.config-debug__select-wrapper { - display: flex; - position: relative; - flex-direction: row; - align-items: center; - justify-content: flex-start; - flex: 1 1 100%; - width: auto; - max-width: 800dp; - height: auto; - padding: 4dp; -} -.config-debug__select-wrapper .config-debug__select-label { - padding-right: 16dp; - flex: auto; - width: 196dp; -} -.config-debug__select-wrapper .config-debug__select-label > div { - display: inline; - width: auto; - height: auto; -} -.config-debug__select-wrapper input { - display: block; - position: relative; - box-sizing: border-box; - padding: 0; - flex: 1 1 100%; - width: auto; - height: 20dp; - margin: auto 0; -} -.config-debug__select-wrapper select { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out, opacity 0.05s linear-in-out, border-color 0.05s linear-in-out; - border-width: 1.1dp; - border-color: rgba(255, 255, 255, 0.5); - display: block; - position: relative; - box-sizing: border-box; - padding: 0; - flex: 1 1 100%; - width: auto; - height: 48dp; - border-radius: 12dp; - background-color: rgba(255, 255, 255, 0.05); - cursor: pointer; - align-items: center; - justify-content: flex-start; - padding: 14dp; -} -.config-debug__select-wrapper select svg { - transition: image-color 0.05s linear-in-out, background-color 0.05s linear-in-out; -} -.config-debug__select-wrapper select:hover, .config-debug__select-wrapper select:focus { - border-width: 1.1dp; - border-color: rgba(255, 255, 255, 0.8); - background-color: rgba(255, 255, 255, 0.2); -} -.config-debug__select-wrapper select selectvalue { - display: inline; - margin: auto 0; - height: auto; -} -.config-debug__select-wrapper select selectbox { - border-width: 1.1dp; - border-color: rgba(255, 255, 255, 0.8); - background-color: #191622; - padding: 4dp 0; - margin-top: 2dp; - border-radius: 12dp; -} -.config-debug__select-wrapper select selectbox option { - transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; - padding: 8dp 12dp; - background-color: rgba(0, 0, 0, 0); - color: #CCCCCC; - font-weight: 400; -} -.config-debug__select-wrapper select selectbox option:hover, .config-debug__select-wrapper select selectbox option:focus { - background-color: rgba(255, 255, 255, 0.2); -} -.config-debug__select-wrapper select selectbox option:hover:not(:checked) { - cursor: pointer; -} -.config-debug__select-wrapper select selectbox option:checked { - color: #FFFFFF; - background-color: rgba(255, 255, 255, 0.05); -} - -body { - box-sizing: border-box; - color: #F2F2F2; - font-family: "latolatin"; -} - -.rmlui-window { - opacity: 1; -} -.rmlui-window--hidden { - opacity: 0; -} -.rmlui-window:not([mouse-active]) { - pointer-events: none; -} - -*, *:before, *:after { - box-sizing: border-box; -} - -button { - background-color: #7A2AC6; -} - -@keyframes blue-pulse { - 0% { - color: #17D6E8; - } - 50% { - color: #A2EFF6; - } - 100% { - color: #17D6E8; - } -} -@keyframes blue-pulse-with-border { - 0% { - border-color: #17D6E8; - color: #17D6E8; - } - 50% { - border-color: #A2EFF6; - color: #A2EFF6; - } - 100% { - border-color: #17D6E8; - color: #17D6E8; - } -} -@keyframes blue-pulse-background { - 0% { - background-color: #17D6E8; - } - 50% { - background-color: #A2EFF6; - } - 100% { - background-color: #17D6E8; - } -} +.rml-needs-something-in-this-file {} \ No newline at end of file diff --git a/icons/Arrow.svg b/icons/Arrow.svg new file mode 100644 index 0000000..f1c5219 --- /dev/null +++ b/icons/Arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/Cont.svg b/icons/Cont.svg new file mode 100644 index 0000000..5959613 --- /dev/null +++ b/icons/Cont.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/Keyboard.svg b/icons/Keyboard.svg new file mode 100644 index 0000000..880ceb0 --- /dev/null +++ b/icons/Keyboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/Plus.svg b/icons/Plus.svg new file mode 100644 index 0000000..ca00c8f --- /dev/null +++ b/icons/Plus.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/icons/Quit.svg b/icons/Quit.svg new file mode 100644 index 0000000..1713672 --- /dev/null +++ b/icons/Quit.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/icons/RecordBorder.svg b/icons/RecordBorder.svg new file mode 100644 index 0000000..28d8eac --- /dev/null +++ b/icons/RecordBorder.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/Reset.svg b/icons/Reset.svg new file mode 100644 index 0000000..90dba9b --- /dev/null +++ b/icons/Reset.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/icons/Trash.svg b/icons/Trash.svg new file mode 100644 index 0000000..6bb569e --- /dev/null +++ b/icons/Trash.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/icons/VizMap/ButtonLarge.svg b/icons/VizMap/ButtonLarge.svg new file mode 100644 index 0000000..b6b9210 --- /dev/null +++ b/icons/VizMap/ButtonLarge.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/icons/VizMap/ButtonMedium.svg b/icons/VizMap/ButtonMedium.svg new file mode 100644 index 0000000..263cf48 --- /dev/null +++ b/icons/VizMap/ButtonMedium.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/VizMap/ButtonSmall.svg b/icons/VizMap/ButtonSmall.svg new file mode 100644 index 0000000..4874ced --- /dev/null +++ b/icons/VizMap/ButtonSmall.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/icons/VizMap/DPad.svg b/icons/VizMap/DPad.svg new file mode 100644 index 0000000..20a0517 --- /dev/null +++ b/icons/VizMap/DPad.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/icons/VizMap/DPadArrow.svg b/icons/VizMap/DPadArrow.svg new file mode 100644 index 0000000..3fbc085 --- /dev/null +++ b/icons/VizMap/DPadArrow.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/icons/VizMap/Map.svg b/icons/VizMap/Map.svg new file mode 100644 index 0000000..9a5fde4 --- /dev/null +++ b/icons/VizMap/Map.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/VizMap/Shield.svg b/icons/VizMap/Shield.svg new file mode 100644 index 0000000..933bb07 --- /dev/null +++ b/icons/VizMap/Shield.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/icons/VizMap/Target.svg b/icons/VizMap/Target.svg new file mode 100644 index 0000000..f24a64e --- /dev/null +++ b/icons/VizMap/Target.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/icons/X.svg b/icons/X.svg new file mode 100644 index 0000000..8b35dbd --- /dev/null +++ b/icons/X.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/include/zelda_config.h b/include/zelda_config.h index 0078d22..9c21169 100644 --- a/include/zelda_config.h +++ b/include/zelda_config.h @@ -6,6 +6,73 @@ #include "ultramodern/config.hpp" #include "recomp_input.h" +#if 1 +namespace zelda64 { + inline const std::u8string program_id = u8"Zelda64Recompiled"; + inline const std::string program_name = "Zelda 64: Recompiled"; + + namespace configkeys { + namespace general { + inline const std::string debug_mode = "debug_mode"; + inline const std::string targeting_mode = "targeting_mode"; + inline const std::string autosave_mode = "autosave_mode"; + inline const std::string camera_invert_mode = "camera_invert_mode"; + inline const std::string analog_cam_mode = "analog_cam_mode"; + inline const std::string analog_camera_invert_mode = "analog_camera_invert_mode"; + } + + namespace sound { + inline const std::string bgm_volume = "bgm_volume"; + inline const std::string low_health_beeps = "low_health_beeps"; + } + + namespace graphics { + } + } + + // TODO: Move loading configs to the runtime once we have a way to allow per-project customization. + void init_config(); + + bool get_debug_mode_enabled(); + + enum class AutosaveMode { + Off, + On, + }; + + AutosaveMode get_autosave_mode(); + + enum class TargetingMode { + Switch, + Hold + }; + + TargetingMode get_targeting_mode(); + + enum class CameraInvertMode { + InvertNone, + InvertX, + InvertY, + InvertBoth + }; + + CameraInvertMode get_camera_invert_mode(); + CameraInvertMode get_analog_camera_invert_mode(); + + enum class AnalogCamMode { + Off, + On, + }; + + AnalogCamMode get_analog_cam_mode(); + + enum class LowHealthBeepsMode { + Off, + On, + }; +}; + +#else namespace zelda64 { constexpr std::u8string_view program_id = u8"Starfox64Recompiled"; constexpr std::string_view program_name = "Starfox 64: Recompiled"; @@ -98,3 +165,5 @@ namespace zelda64 { }; #endif + +#endif diff --git a/include/zelda_debug.h b/include/zelda_debug.h index 4c0743e..26552f4 100644 --- a/include/zelda_debug.h +++ b/include/zelda_debug.h @@ -5,6 +5,11 @@ #include namespace zelda64 { + namespace debug { + inline const std::string tab_id = "debug"; + inline const std::string tab_name = "Debug"; + } + struct SceneWarps { int index; std::string name; diff --git a/include/zelda_launcher.h b/include/zelda_launcher.h new file mode 100644 index 0000000..f88f7ee --- /dev/null +++ b/include/zelda_launcher.h @@ -0,0 +1,11 @@ +#ifndef __ZELDA_LAUNCHER_H__ +#define __ZELDA_LAUNCHER_H__ + +#include "recompui/recompui.h" + +namespace zelda64 { + void launcher_animation_setup(recompui::LauncherMenu *menu); + void launcher_animation_update(recompui::LauncherMenu *menu); +} + +#endif diff --git a/patches/recompui_event_structs.h b/patches/recompui_event_structs.h index 914160d..5495081 100644 --- a/patches/recompui_event_structs.h +++ b/patches/recompui_event_structs.h @@ -1,7 +1,7 @@ #ifndef __UI_FUNCS_H__ #define __UI_FUNCS_H__ -// These two enums must be kept in sync with src/ui/elements/ui_types.h! +// These three enums must be kept in sync with src/ui/elements/ui_types.h! typedef enum { UI_EVENT_NONE, UI_EVENT_CLICK, @@ -11,6 +11,9 @@ typedef enum { UI_EVENT_DRAG, UI_EVENT_RESERVED1, // Would be UI_EVENT_TEXT but text events aren't usable in mods currently UI_EVENT_UPDATE, + UI_EVENT_NAVIGATE, + UI_EVENT_MOUSE_BUTTON, + UI_EVENT_MENU_ACTION, UI_EVENT_COUNT } RecompuiEventType; @@ -21,6 +24,16 @@ typedef enum { UI_DRAG_END } RecompuiDragPhase; +typedef enum { + UI_MENU_ACTION_NONE, + UI_MENU_ACTION_ACCEPT, + UI_MENU_ACTION_APPLY, + UI_MENU_ACTION_BACK, + UI_MENU_ACTION_TOGGLE, + UI_MENU_ACTION_TAB_LEFT, + UI_MENU_ACTION_TAB_RIGHT +} RecompuiMenuAction; + typedef struct { RecompuiEventType type; union { @@ -46,6 +59,10 @@ typedef struct { float y; RecompuiDragPhase phase; } drag; + + struct { + RecompuiMenuAction action; + } menu_action; } data; } RecompuiEventData; diff --git a/shaders/InterfacePS.hlsl b/shaders/InterfacePS.hlsl deleted file mode 100644 index b8ab340..0000000 --- a/shaders/InterfacePS.hlsl +++ /dev/null @@ -1,11 +0,0 @@ -SamplerState gSampler : register(s1, space0); -Texture2D gTexture : register(t2, space1); - -void PSMain( - in float4 iColor : COLOR, - in float2 iUV : TEXCOORD, - out float4 oColor : SV_TARGET -) -{ - oColor = gTexture.SampleLevel(gSampler, iUV, 0) * iColor; -} diff --git a/shaders/InterfaceVS.hlsl b/shaders/InterfaceVS.hlsl deleted file mode 100644 index 05fd649..0000000 --- a/shaders/InterfaceVS.hlsl +++ /dev/null @@ -1,23 +0,0 @@ -struct Input { - float4x4 transform; - float2 translation; -}; - -[[vk::push_constant]] -ConstantBuffer gInput : register(b0, space0); - -void VSMain( - in float2 iPosition : POSITION, - in float4 iColor : COLOR, - in float2 iUV : TEXCOORD, - out float4 oColor : COLOR, - out float2 oUV : TEXCOORD, - out float4 oPosition : SV_Position -) -{ - float2 translatedPos = iPosition + gInput.translation; - oPosition = mul(gInput.transform, float4(translatedPos, 0, 1)); - - oColor = iColor; - oUV = iUV; -} diff --git a/src/game/config.cpp b/src/game/config.cpp index ad4ec86..a96c4a0 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -1,10 +1,14 @@ #include "zelda_config.h" -#include "recomp_input.h" +#include "zelda_debug.h" +#include "recompui/recompui.h" +#include "recompui/config.h" +#include "recompinput/recompinput.h" #include "zelda_sound.h" -#include "zelda_render.h" #include "zelda_support.h" #include "ultramodern/config.hpp" #include "librecomp/files.hpp" +#include "librecomp/config.hpp" +#include "util/file.h" #include #include #include @@ -18,519 +22,256 @@ #include "apple/rt64_apple.h" #endif -constexpr std::u8string_view general_filename = u8"general.json"; -constexpr std::u8string_view graphics_filename = u8"graphics.json"; -constexpr std::u8string_view controls_filename = u8"controls.json"; -constexpr std::u8string_view sound_filename = u8"sound.json"; - -constexpr auto res_default = ultramodern::renderer::Resolution::Auto; -constexpr auto hr_default = ultramodern::renderer::HUDRatioMode::Clamp16x9; -constexpr auto api_default = ultramodern::renderer::GraphicsApi::Auto; -constexpr auto ar_default = ultramodern::renderer::AspectRatio::Expand; -constexpr auto msaa_default = ultramodern::renderer::Antialiasing::MSAA2X; -constexpr auto rr_default = ultramodern::renderer::RefreshRate::Display; -constexpr auto hpfb_default = ultramodern::renderer::HighPrecisionFramebuffer::Auto; -constexpr int ds_default = 1; -constexpr int rr_manual_default = 60; -constexpr bool developer_mode_default = false; - -static bool is_steam_deck = false; - -ultramodern::renderer::WindowMode wm_default() { - return is_steam_deck ? ultramodern::renderer::WindowMode::Fullscreen : ultramodern::renderer::WindowMode::Windowed; +bool get_general_config_bool_value(const std::string& option_id) { + return std::get(recompui::config::get_general_config().get_option_value(option_id)); } -#ifdef __gnu_linux__ -void detect_steam_deck() { - // Check if the board vendor is Valve. - std::ifstream board_vendor_file("/sys/devices/virtual/dmi/id/board_vendor"); - std::string line; - if (std::getline(board_vendor_file, line).good() && line == "Valve") { - is_steam_deck = true; - return; - } - - // Check if the SteamDeck variable is set to 1. - const char* steam_deck_env = getenv("SteamDeck"); - if (steam_deck_env != nullptr && std::string{steam_deck_env} == "1") { - is_steam_deck = true; - return; - } - - is_steam_deck = false; - return; +template +T get_general_config_enum_value(const std::string& option_id) { + return static_cast(std::get(recompui::config::get_general_config().get_option_value(option_id))); } -#else -void detect_steam_deck() { is_steam_deck = false; } -#endif -template -T from_or_default(const json& j, const std::string& key, T default_value) { - T ret; - auto find_it = j.find(key); - if (find_it != j.end()) { - find_it->get_to(ret); - } - else { - ret = default_value; - } - - return ret; +template +T get_general_config_number_value(const std::string& option_id) { + return static_cast(std::get(recompui::config::get_general_config().get_option_value(option_id))); } -template -void call_if_key_exists(void (*func)(T), const json& j, const std::string& key) { - auto find_it = j.find(key); - if (find_it != j.end()) { - T val; - find_it->get_to(val); - func(val); - } +template +T get_graphics_config_enum_value(const std::string& option_id) { + return static_cast(std::get(recompui::config::get_graphics_config().get_option_value(option_id))); } -namespace ultramodern { - void to_json(json& j, const renderer::GraphicsConfig& config) { - j = json{ - {"res_option", config.res_option}, - {"wm_option", config.wm_option}, - {"hr_option", config.hr_option}, - {"api_option", config.api_option}, - {"ds_option", config.ds_option}, - {"ar_option", config.ar_option}, - {"msaa_option", config.msaa_option}, - {"rr_option", config.rr_option}, - {"hpfb_option", config.hpfb_option}, - {"rr_manual_value", config.rr_manual_value}, - {"developer_mode", config.developer_mode}, - }; - } - - void from_json(const json& j, renderer::GraphicsConfig& config) { - config.res_option = from_or_default(j, "res_option", res_default); - config.wm_option = from_or_default(j, "wm_option", wm_default()); - config.hr_option = from_or_default(j, "hr_option", hr_default); - config.api_option = from_or_default(j, "api_option", api_default); - config.ds_option = from_or_default(j, "ds_option", ds_default); - config.ar_option = from_or_default(j, "ar_option", ar_default); - config.msaa_option = from_or_default(j, "msaa_option", msaa_default); - config.rr_option = from_or_default(j, "rr_option", rr_default); - config.hpfb_option = from_or_default(j, "hpfb_option", hpfb_default); - config.rr_manual_value = from_or_default(j, "rr_manual_value", rr_manual_default); - config.developer_mode = from_or_default(j, "developer_mode", developer_mode_default); - } +template +T get_graphics_config_number_value(const std::string& option_id) { + return static_cast(std::get(recompui::config::get_graphics_config().get_option_value(option_id))); } -namespace recomp { - void to_json(json& j, const InputField& field) { - j = json{ {"input_type", field.input_type}, {"input_id", field.input_id} }; - } - - void from_json(const json& j, InputField& field) { - j.at("input_type").get_to(field.input_type); - j.at("input_id").get_to(field.input_id); - } +template +T get_sound_config_enum_value(const std::string& option_id) { + return static_cast(std::get(recompui::config::get_sound_config().get_option_value(option_id))); } -std::filesystem::path zelda64::get_app_folder_path() { - // directly check for portable.txt (windows and native linux binary) - if (std::filesystem::exists("portable.txt")) { - return std::filesystem::current_path(); - } - -#if defined(__APPLE__) - // Check for portable file in the directory containing the app bundle. - const auto app_bundle_path = zelda64::get_bundle_directory().parent_path(); - if (std::filesystem::exists(app_bundle_path / "portable.txt")) { - return app_bundle_path; - } -#endif - - std::filesystem::path recomp_dir{}; - -#if defined(_WIN32) - // Deduce local app data path. - PWSTR known_path = NULL; - HRESULT result = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &known_path); - if (result == S_OK) { - recomp_dir = std::filesystem::path{known_path} / zelda64::program_id; - } - - CoTaskMemFree(known_path); -#elif defined(__linux__) || defined(__APPLE__) - // check for APP_FOLDER_PATH env var - if (getenv("APP_FOLDER_PATH") != nullptr) { - return std::filesystem::path{getenv("APP_FOLDER_PATH")}; - } - -#if defined(__APPLE__) - const auto supportdir = zelda64::get_application_support_directory(); - if (supportdir) { - return *supportdir / zelda64::program_id; - } -#endif - - const char *homedir; - - if ((homedir = getenv("HOME")) == nullptr) { - #if defined(__linux__) - homedir = getpwuid(getuid())->pw_dir; - #elif defined(__APPLE__) - homedir = GetHomeDirectory(); - #endif - } - - if (homedir != nullptr) { - recomp_dir = std::filesystem::path{homedir} / (std::u8string{u8".config/"} + std::u8string{zelda64::program_id}); - } -#endif - - return recomp_dir; +template +T get_sound_config_number_value(const std::string& option_id) { + return static_cast(std::get(recompui::config::get_sound_config().get_option_value(option_id))); } -bool read_json(std::ifstream input_file, nlohmann::json& json_out) { - if (!input_file.good()) { - return false; - } +static void add_general_options(recomp::config::Config &config) { + using EnumOptionVector = const std::vector; - try { - input_file >> json_out; - } - catch (nlohmann::json::parse_error&) { - return false; - } - return true; -} - -bool read_json_with_backups(const std::filesystem::path& path, nlohmann::json& json_out) { - // Try reading and parsing the base file. - if (read_json(std::ifstream{path}, json_out)) { - return true; - } - - // Try reading and parsing the backup file. - if (read_json(recomp::open_input_backup_file(path), json_out)) { - return true; - } - - // Both reads failed. - return false; -} - -bool save_json_with_backups(const std::filesystem::path& path, const nlohmann::json& json_data) { - { - std::ofstream output_file = recomp::open_output_file_with_backup(path); - if (!output_file.good()) { - return false; - } + // debug_mode + config.add_bool_option( + zelda64::configkeys::general::debug_mode, + "Debug Mode", + "Controls whether the debug menu is visible", + false, + true + ); - output_file << std::setw(4) << json_data; - } - return recomp::finalize_output_file_with_backup(path); -} + // targeting_mode + static EnumOptionVector targeting_mode_options = { + {zelda64::TargetingMode::Switch, "Switch"}, + {zelda64::TargetingMode::Hold, "Hold"}, + }; + config.add_enum_option( + zelda64::configkeys::general::targeting_mode, + "Targeting Mode", + "Controls how targeting enemies and objects works. Switch will start or stop targeting each time the target button is pressed. Hold will start when the target button is pressed and stop when the button is released.", + targeting_mode_options, + zelda64::TargetingMode::Switch + ); -bool save_general_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - - zelda64::to_json(config_json["targeting_mode"], zelda64::get_targeting_mode()); - recomp::to_json(config_json["background_input_mode"], recomp::get_background_input_mode()); - config_json["rumble_strength"] = recomp::get_rumble_strength(); - config_json["gyro_sensitivity"] = recomp::get_gyro_sensitivity(); - config_json["mouse_sensitivity"] = recomp::get_mouse_sensitivity(); - config_json["joystick_deadzone"] = recomp::get_joystick_deadzone(); - config_json["film_grain_mode"] = zelda64::get_film_grain_mode(); - config_json["radio_comm_box_mode"] = zelda64::get_radio_comm_box_mode(); - config_json["invert_y_axis_mode"] = zelda64::get_invert_y_axis_mode(); - config_json["analog_camera_invert_mode"] = zelda64::get_analog_camera_invert_mode(); - config_json["debug_mode"] = zelda64::get_debug_mode_enabled(); - - return save_json_with_backups(path, config_json); -} + // autosave_mode + static EnumOptionVector autosave_mode_options = { + {zelda64::AutosaveMode::Off, "Off"}, + {zelda64::AutosaveMode::On, "On"}, + }; + config.add_enum_option( + zelda64::configkeys::general::autosave_mode, + "Autosaving", + "Turns on autosaving and prevents owl saves from being deleted on load. Autosaves act as owl saves and take up the same slot as they do.\n" + "\n" + "Loading an autosave will place you in Clock Town or at the entrance of the current dungeon if you were in one.\n" + "\n" + "\n" + "If autosaving is disabled, existing autosaves will be deleted when loaded.", + autosave_mode_options, + zelda64::AutosaveMode::On + ); -void set_general_settings_from_json(const nlohmann::json& config_json) { - zelda64::set_targeting_mode(from_or_default(config_json, "targeting_mode", zelda64::TargetingMode::Switch)); - recomp::set_background_input_mode(from_or_default(config_json, "background_input_mode", recomp::BackgroundInputMode::On)); - recomp::set_rumble_strength(from_or_default(config_json, "rumble_strength", 50)); - recomp::set_gyro_sensitivity(from_or_default(config_json, "gyro_sensitivity", 50)); - recomp::set_mouse_sensitivity(from_or_default(config_json, "mouse_sensitivity", is_steam_deck ? 50 : 0)); - recomp::set_joystick_deadzone(from_or_default(config_json, "joystick_deadzone", 0)); - zelda64::set_film_grain_mode(from_or_default(config_json, "film_grain_mode", zelda64::FilmGrainMode::On)); - zelda64::set_radio_comm_box_mode(from_or_default(config_json, "radio_comm_box_mode", zelda64::RadioBoxMode::Original)); - zelda64::set_invert_y_axis_mode(from_or_default(config_json, "invert_y_axis_mode", zelda64::AimInvertMode::On)); - zelda64::set_analog_camera_invert_mode(from_or_default(config_json, "analog_camera_invert_mode", zelda64::AimInvertMode::On)); - zelda64::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false)); -} + // camera_invert_mode + static EnumOptionVector camera_invert_mode_options = { + {zelda64::CameraInvertMode::InvertNone, "None"}, + {zelda64::CameraInvertMode::InvertX, "Invert X"}, + {zelda64::CameraInvertMode::InvertY, "Invert Y"}, + {zelda64::CameraInvertMode::InvertBoth, "Invert Both"}, + }; + config.add_enum_option( + zelda64::configkeys::general::camera_invert_mode, + "Aiming Camera Mode", + "Inverts the camera controls for first-person aiming. Invert Y is the default and matches the original game.", + camera_invert_mode_options, + zelda64::CameraInvertMode::InvertY + ); -bool load_general_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - if (!read_json_with_backups(path, config_json)) { - return false; - } + // analog_cam_mode + static EnumOptionVector analog_cam_mode_options = { + {zelda64::AnalogCamMode::Off, "Off"}, + {zelda64::AnalogCamMode::On, "On"}, + }; + config.add_enum_option( + zelda64::configkeys::general::analog_cam_mode, + "Analog Camera", + "Enables an analog \"free\" camera similar to later entries in the series that's mapped to the right analog stick on the controller.\n" + "\n" + "When you move the right stick, the camera will enter free mode and stop centering behind Link. Press the Target button at any time to go back into the normal camera mode. The camera will also return to normal mode after a cutscene plays or when you move between areas.\n" + "\n" + "This option also enables right stick control while looking and aiming.", + analog_cam_mode_options, + zelda64::AnalogCamMode::Off + ); - set_general_settings_from_json(config_json); - return true; + // analog_camera_invert_mode + static EnumOptionVector analog_camera_invert_mode_options = { + {zelda64::CameraInvertMode::InvertNone, "None"}, + {zelda64::CameraInvertMode::InvertX, "Invert X"}, + {zelda64::CameraInvertMode::InvertY, "Invert Y"}, + {zelda64::CameraInvertMode::InvertBoth, "Invert Both"}, + }; + config.add_enum_option( + zelda64::configkeys::general::analog_camera_invert_mode, + "Analog Camera Mode", + "Inverts the camera controls for the analog camera if it's enabled. None is the default.", + analog_camera_invert_mode_options, + zelda64::CameraInvertMode::InvertNone + ); } -void assign_mapping(recomp::InputDevice device, recomp::GameInput input, const std::vector& value) { - for (size_t binding_index = 0; binding_index < std::min(value.size(), recomp::bindings_per_input); binding_index++) { - recomp::set_input_binding(input, binding_index, device, value[binding_index]); - } -}; - -// same as assign_mapping, except will clear unassigned bindings if not in value -void assign_mapping_complete(recomp::InputDevice device, recomp::GameInput input, const std::vector& value) { - for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { - if (binding_index >= value.size()) { - recomp::set_input_binding(input, binding_index, device, recomp::InputField{}); - } else { - recomp::set_input_binding(input, binding_index, device, value[binding_index]); - } - } -}; - -void assign_all_mappings(recomp::InputDevice device, const recomp::DefaultN64Mappings& values) { - assign_mapping_complete(device, recomp::GameInput::A, values.a); - assign_mapping_complete(device, recomp::GameInput::B, values.b); - assign_mapping_complete(device, recomp::GameInput::Z, values.z); - assign_mapping_complete(device, recomp::GameInput::START, values.start); - assign_mapping_complete(device, recomp::GameInput::DPAD_UP, values.dpad_up); - assign_mapping_complete(device, recomp::GameInput::DPAD_DOWN, values.dpad_down); - assign_mapping_complete(device, recomp::GameInput::DPAD_LEFT, values.dpad_left); - assign_mapping_complete(device, recomp::GameInput::DPAD_RIGHT, values.dpad_right); - assign_mapping_complete(device, recomp::GameInput::L, values.l); - assign_mapping_complete(device, recomp::GameInput::R, values.r); - assign_mapping_complete(device, recomp::GameInput::C_UP, values.c_up); - assign_mapping_complete(device, recomp::GameInput::C_DOWN, values.c_down); - assign_mapping_complete(device, recomp::GameInput::C_LEFT, values.c_left); - assign_mapping_complete(device, recomp::GameInput::C_RIGHT, values.c_right); - - assign_mapping_complete(device, recomp::GameInput::X_AXIS_NEG, values.analog_left); - assign_mapping_complete(device, recomp::GameInput::X_AXIS_POS, values.analog_right); - assign_mapping_complete(device, recomp::GameInput::Y_AXIS_NEG, values.analog_down); - assign_mapping_complete(device, recomp::GameInput::Y_AXIS_POS, values.analog_up); - - assign_mapping_complete(device, recomp::GameInput::TOGGLE_MENU, values.toggle_menu); - assign_mapping_complete(device, recomp::GameInput::ACCEPT_MENU, values.accept_menu); - assign_mapping_complete(device, recomp::GameInput::APPLY_MENU, values.apply_menu); -}; - -void zelda64::reset_input_bindings() { - assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); - assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); +bool zelda64::get_debug_mode_enabled() { + return get_general_config_bool_value(zelda64::configkeys::general::debug_mode); } -void zelda64::reset_cont_input_bindings() { - assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); +zelda64::AutosaveMode zelda64::get_autosave_mode() { + return get_general_config_enum_value(zelda64::configkeys::general::autosave_mode); } -void zelda64::reset_kb_input_bindings() { - assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); +zelda64::TargetingMode zelda64::get_targeting_mode() { + return get_general_config_enum_value(zelda64::configkeys::general::targeting_mode); } -void zelda64::reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input) { - assign_mapping_complete( - device, - input, - recomp::get_default_mapping_for_input( - device == recomp::InputDevice::Keyboard ? - recomp::default_n64_keyboard_mappings : - recomp::default_n64_controller_mappings, - input - ) - ); +zelda64::CameraInvertMode zelda64::get_camera_invert_mode() { + return get_general_config_enum_value(zelda64::configkeys::general::camera_invert_mode); } -void reset_graphics_options() { - ultramodern::renderer::GraphicsConfig new_config{}; - new_config.res_option = res_default; - new_config.wm_option = wm_default(); - new_config.hr_option = hr_default; - new_config.ds_option = ds_default; - new_config.ar_option = ar_default; - new_config.msaa_option = msaa_default; - new_config.rr_option = rr_default; - new_config.hpfb_option = hpfb_default; - new_config.rr_manual_value = rr_manual_default; - new_config.developer_mode = developer_mode_default; - ultramodern::renderer::set_graphics_config(new_config); +zelda64::AnalogCamMode zelda64::get_analog_cam_mode() { + return get_general_config_enum_value(zelda64::configkeys::general::analog_cam_mode); } -bool save_graphics_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - ultramodern::to_json(config_json, ultramodern::renderer::get_graphics_config()); - return save_json_with_backups(path, config_json); +zelda64::CameraInvertMode zelda64::get_analog_camera_invert_mode() { + return get_general_config_enum_value(zelda64::configkeys::general::analog_camera_invert_mode); } -bool load_graphics_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - if (!read_json_with_backups(path, config_json)) { - return false; - } +static void add_graphics_options(recomp::config::Config &config) { + using EnumOptionVector = const std::vector; - ultramodern::renderer::GraphicsConfig new_config{}; - ultramodern::from_json(config_json, new_config); - ultramodern::renderer::set_graphics_config(new_config); - return true; + // Nothing to do here. } -void add_input_bindings(nlohmann::json& out, recomp::GameInput input, recomp::InputDevice device) { - const std::string& input_name = recomp::get_input_enum_name(input); - nlohmann::json& out_array = out[input_name]; - out_array = nlohmann::json::array(); - for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { - out_array[binding_index] = recomp::get_input_binding(input, binding_index, device); - } -}; - -bool save_controls_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - - config_json["keyboard"] = {}; - config_json["controller"] = {}; - - for (size_t i = 0; i < recomp::get_num_inputs(); i++) { - recomp::GameInput cur_input = static_cast(i); +static void add_sound_options(recomp::config::Config &config) { + using EnumOptionVector = const std::vector; - add_input_bindings(config_json["keyboard"], cur_input, recomp::InputDevice::Keyboard); - add_input_bindings(config_json["controller"], cur_input, recomp::InputDevice::Controller); - } + // bgm_volume + config.add_percent_number_option( + zelda64::configkeys::sound::bgm_volume, + "Background Music Volume", + "Controls the overall volume of background music.", + 100.0f + ); - return save_json_with_backups(path, config_json); + // low_health_beeps + static EnumOptionVector low_health_beeps_mode_options = { + {zelda64::LowHealthBeepsMode::Off, "Off"}, + {zelda64::LowHealthBeepsMode::On, "On"}, + }; + config.add_enum_option( + zelda64::configkeys::sound::low_health_beeps, + "Low Health Beeps", + "Toggles whether or not the low-health beeping sound plays.", + low_health_beeps_mode_options, + zelda64::LowHealthBeepsMode::On + ); } -bool load_input_device_from_json(const nlohmann::json& config_json, recomp::InputDevice device, const std::string& key) { - // Check if the json object for the given key exists. - auto find_it = config_json.find(key); - if (find_it == config_json.end()) { - return false; - } - - const nlohmann::json& mappings_json = *find_it; - - for (size_t i = 0; i < recomp::get_num_inputs(); i++) { - recomp::GameInput cur_input = static_cast(i); - const std::string& input_name = recomp::get_input_enum_name(cur_input); - - // Check if the json object for the given input exists and that it's an array. - auto find_input_it = mappings_json.find(input_name); - if (find_input_it == mappings_json.end() || !find_input_it->is_array()) { - assign_mapping( - device, - cur_input, - recomp::get_default_mapping_for_input( - device == recomp::InputDevice::Keyboard ? - recomp::default_n64_keyboard_mappings : - recomp::default_n64_controller_mappings, - cur_input - ) - ); - continue; - } - const nlohmann::json& input_json = *find_input_it; - - // Deserialize all the bindings from the json array (up to the max number of bindings per input). - for (size_t binding_index = 0; binding_index < std::min(recomp::bindings_per_input, input_json.size()); binding_index++) { - recomp::InputField cur_field{}; - recomp::from_json(input_json[binding_index], cur_field); - recomp::set_input_binding(cur_input, binding_index, device, cur_field); - } - } - - return true; +int zelda64::get_bgm_volume() { + return get_sound_config_number_value(zelda64::configkeys::sound::bgm_volume); } -bool load_controls_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - if (!read_json_with_backups(path, config_json)) { - return false; - } - - if (!load_input_device_from_json(config_json, recomp::InputDevice::Keyboard, "keyboard")) { - assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); - } - - if (!load_input_device_from_json(config_json, recomp::InputDevice::Controller, "controller")) { - assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); - } - return true; +bool zelda64::get_low_health_beeps_enabled() { + return get_sound_config_enum_value(zelda64::configkeys::sound::low_health_beeps) == zelda64::LowHealthBeepsMode::On; } -bool save_sound_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - - config_json["main_volume"] = zelda64::get_main_volume(); - config_json["bgm_volume"] = zelda64::get_bgm_volume(); - config_json["sfx_volume"] = zelda64::get_sfx_volume(); - config_json["voice_volume"] = zelda64::get_voice_volume(); - config_json["low_health_beeps"] = zelda64::get_low_health_beeps_enabled(); - - return save_json_with_backups(path, config_json); +static void set_control_defaults() { + // Nothing to do here. } -bool load_sound_config(const std::filesystem::path& path) { - nlohmann::json config_json{}; - if (!read_json_with_backups(path, config_json)) { - return false; - } - - zelda64::reset_sound_settings(); - call_if_key_exists(zelda64::set_main_volume, config_json, "main_volume"); - call_if_key_exists(zelda64::set_bgm_volume, config_json, "bgm_volume"); - call_if_key_exists(zelda64::set_sfx_volume, config_json, "sfx_volume"); - call_if_key_exists(zelda64::set_voice_volume, config_json, "voice_volume"); - call_if_key_exists(zelda64::set_low_health_beeps_enabled, config_json, "low_health_beeps"); - return true; +static void set_control_descriptions() { + // TODO descriptions + recompinput::set_game_input_description(recompinput::GameInput::Y_AXIS_POS, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::Y_AXIS_NEG, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::X_AXIS_NEG, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::X_AXIS_POS, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::A, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::B, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::Z, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::L, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::R, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::START, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::C_UP, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::C_DOWN, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::C_LEFT, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::C_RIGHT, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::DPAD_UP, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::DPAD_DOWN, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::DPAD_LEFT, "TODO description"); + recompinput::set_game_input_description(recompinput::GameInput::DPAD_RIGHT, "TODO description"); } -void zelda64::load_config() { - detect_steam_deck(); - - std::filesystem::path recomp_dir = zelda64::get_app_folder_path(); - std::filesystem::path general_path = recomp_dir / general_filename; - std::filesystem::path graphics_path = recomp_dir / graphics_filename; - std::filesystem::path controls_path = recomp_dir / controls_filename; - std::filesystem::path sound_path = recomp_dir / sound_filename; +void zelda64::init_config() { + std::filesystem::path recomp_dir = recompui::file::get_app_folder_path(); if (!recomp_dir.empty()) { std::filesystem::create_directories(recomp_dir); } - // TODO error handling for failing to save config files after resetting them. + recompui::config::GeneralTabOptions general_options{}; + general_options.has_rumble_strength = true; + general_options.has_gyro_sensitivity = true; + general_options.has_mouse_sensitivity = true; - if (!load_general_config(general_path)) { - // Set the general settings from an empty json to use defaults. - set_general_settings_from_json({}); - save_general_config(general_path); - } + auto &general_config = recompui::config::create_general_tab(general_options); + add_general_options(general_config); - if (!load_graphics_config(graphics_path)) { - reset_graphics_options(); - save_graphics_config(graphics_path); - } - - if (!load_controls_config(controls_path)) { - zelda64::reset_input_bindings(); - save_controls_config(controls_path); - } + set_control_defaults(); + set_control_descriptions(); + recompui::config::create_controls_tab(); - if (!load_sound_config(sound_path)) { - zelda64::reset_sound_settings(); - save_sound_config(sound_path); - } -} + auto &graphics_config = recompui::config::create_graphics_tab(); + add_graphics_options(graphics_config); -void zelda64::save_config() { - std::filesystem::path recomp_dir = zelda64::get_app_folder_path(); + auto &sound_config = recompui::config::create_sound_tab(); + add_sound_options(sound_config); - if (recomp_dir.empty()) { - return; - } + recompui::config::create_mods_tab(); - std::filesystem::create_directories(recomp_dir); + recompui::config::create_tab( + zelda64::debug::tab_name, + zelda64::debug::tab_id, + [](recompui::ContextId context, recompui::Element* parent) { + // TODO implement debug tab + } + ); - // TODO error handling for failing to save config files. + recompui::config::finalize(); - save_general_config(recomp_dir / general_filename); - save_graphics_config(recomp_dir / graphics_filename); - save_controls_config(recomp_dir / controls_filename); - save_sound_config(recomp_dir / sound_filename); + recompui::config::set_tab_visible(zelda64::debug::tab_id, get_debug_mode_enabled()); } diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp index 5777a04..d53d277 100644 --- a/src/game/recomp_api.cpp +++ b/src/game/recomp_api.cpp @@ -3,9 +3,9 @@ #include "recomp.h" #include "librecomp/overlays.hpp" #include "zelda_config.h" -#include "recomp_input.h" -#include "recomp_ui.h" -#include "zelda_render.h" +#include "recompinput/recompinput.h" +#include "recompui/recompui.h" +#include "recompui/renderer.h" #include "zelda_sound.h" #include "librecomp/helpers.hpp" // #include "../patches/input.h" @@ -15,7 +15,7 @@ #include "ultramodern/config.hpp" extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) { - recomp::poll_inputs(); + recompinput::poll_inputs(); } extern "C" void sqrtf_recomp(uint8_t* rdram, recomp_context* ctx) { @@ -48,14 +48,14 @@ extern "C" void recomp_get_gyro_deltas(uint8_t* rdram, recomp_context* ctx) { float* x_out = _arg<0, float*>(rdram, ctx); float* y_out = _arg<1, float*>(rdram, ctx); - recomp::get_gyro_deltas(x_out, y_out); + recompinput::get_gyro_deltas(0, x_out, y_out); } extern "C" void recomp_get_mouse_deltas(uint8_t* rdram, recomp_context* ctx) { float* x_out = _arg<0, float*>(rdram, ctx); float* y_out = _arg<1, float*>(rdram, ctx); - recomp::get_mouse_deltas(x_out, y_out); + recompinput::get_mouse_deltas(x_out, y_out); } extern "C" void recomp_powf(uint8_t* rdram, recomp_context* ctx) { @@ -107,6 +107,9 @@ extern "C" void recomp_get_bgm_volume(uint8_t* rdram, recomp_context* ctx) { _return(ctx, zelda64::get_bgm_volume() / 100.0f); } +int zelda64::get_sfx_volume() {} +int zelda64::get_voice_volume() {} + extern "C" void recomp_get_sfx_volume(uint8_t* rdram, recomp_context* ctx) { _return(ctx, zelda64::get_sfx_volume() / 100.0f); } @@ -124,7 +127,7 @@ extern "C" void recomp_time_us(uint8_t* rdram, recomp_context* ctx) { } extern "C" void recomp_get_film_grain_enabled(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, static_cast(zelda64::get_film_grain_mode() == zelda64::FilmGrainMode::On)); + // _return(ctx, static_cast(zelda64::get_film_grain_mode() == zelda64::FilmGrainMode::On)); } extern "C" void recomp_load_overlays(uint8_t * rdram, recomp_context * ctx) { @@ -136,7 +139,7 @@ extern "C" void recomp_load_overlays(uint8_t * rdram, recomp_context * ctx) { } extern "C" void recomp_high_precision_fb_enabled(uint8_t * rdram, recomp_context * ctx) { - _return(ctx, static_cast(zelda64::renderer::RT64HighPrecisionFBEnabled())); + _return(ctx, static_cast(recompui::renderer::RT64HighPrecisionFBEnabled())); } extern "C" void recomp_get_resolution_scale(uint8_t* rdram, recomp_context* ctx) { @@ -147,28 +150,28 @@ extern "C" void recomp_get_inverted_axes(uint8_t* rdram, recomp_context* ctx) { s32* x_out = _arg<0, s32*>(rdram, ctx); s32* y_out = _arg<1, s32*>(rdram, ctx); - zelda64::RadioBoxMode mode = zelda64::get_radio_comm_box_mode(); + // zelda64::RadioBoxMode mode = zelda64::get_radio_comm_box_mode(); // *x_out = (mode == zelda64::AimInvertMode::InvertX || mode == zelda64::AimInvertMode::InvertBoth); // *y_out = (mode == zelda64::AimInvertMode::InvertY || mode == zelda64::AimInvertMode::InvertBoth); } extern "C" void recomp_get_radio_comm_box_mode(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, zelda64::get_radio_comm_box_mode() == zelda64::RadioBoxMode::Expand); + // _return(ctx, zelda64::get_radio_comm_box_mode() == zelda64::RadioBoxMode::Expand); } extern "C" void recomp_get_analog_inverted_axes(uint8_t* rdram, recomp_context* ctx) { s32* x_out = _arg<0, s32*>(rdram, ctx); s32* y_out = _arg<1, s32*>(rdram, ctx); - zelda64::AimInvertMode mode = zelda64::get_analog_camera_invert_mode(); + // zelda64::AimInvertMode mode = zelda64::get_analog_camera_invert_mode(); // *x_out = (mode == zelda64::AimInvertMode::InvertX || mode == zelda64::AimInvertMode::InvertBoth); // *y_out = (mode == zelda64::AimInvertMode::InvertY || mode == zelda64::AimInvertMode::InvertBoth); } extern "C" void recomp_get_invert_y_axis_mode(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, zelda64::get_invert_y_axis_mode() == zelda64::AimInvertMode::On); + // _return(ctx, zelda64::get_invert_y_axis_mode() == zelda64::AimInvertMode::On); } extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) { @@ -180,7 +183,8 @@ extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) { float x, y; - recomp::get_right_analog(&x, &y); + // TODO: Use controller number. + recompinput::get_right_analog(0, &x, &y); float magnitude = sqrtf(x * x + y * y); @@ -200,5 +204,5 @@ extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) { extern "C" void recomp_set_right_analog_suppressed(uint8_t* rdram, recomp_context* ctx) { s32 suppressed = _arg<0, s32>(rdram, ctx); - recomp::set_right_analog_suppressed(suppressed); + recompinput::set_right_analog_suppressed(suppressed); } diff --git a/src/game/recomp_data_api.cpp b/src/game/recomp_data_api.cpp index 2557abf..6aa5621 100644 --- a/src/game/recomp_data_api.cpp +++ b/src/game/recomp_data_api.cpp @@ -5,7 +5,7 @@ #include "slot_map.h" #include "recomp_data.h" -#include "recomp_ui.h" +#include "recompui/recompui.h" #include "librecomp/helpers.hpp" #include "librecomp/overlays.hpp" #include "librecomp/addresses.hpp" diff --git a/src/main/launcher_animation.cpp b/src/main/launcher_animation.cpp new file mode 100644 index 0000000..6fb7194 --- /dev/null +++ b/src/main/launcher_animation.cpp @@ -0,0 +1,13 @@ +#include "zelda_launcher.h" +#include + +void zelda64::launcher_animation_setup(recompui::LauncherMenu *menu) { + auto context = recompui::get_current_context(); + recompui::Element *background_container = menu->get_background_container(); + background_container->set_background_color({ 0x00, 0x00, 0x00, 0xFF }); + + (void)context; +} + +void zelda64::launcher_animation_update(recompui::LauncherMenu *menu) { +} diff --git a/src/main/main.cpp b/src/main/main.cpp index 58c42b1..829fb16 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -27,15 +27,22 @@ #undef Always #endif -#include "recomp_ui.h" -#include "recomp_input.h" +#include "recompui/recompui.h" +#include "recompui/program_config.h" +#include "recompui/renderer.h" +#include "recompui/config.h" +#include "util/file.h" +#include "recompinput/input_events.h" +#include "recompinput/recompinput.h" +#include "recompinput/profiles.h" #include "zelda_config.h" #include "zelda_sound.h" -#include "zelda_render.h" #include "zelda_support.h" #include "zelda_game.h" +#include "zelda_launcher.h" #include "recomp_data.h" #include "ovl_patches.hpp" +#include "theme.h" #include "librecomp/game.hpp" #include "librecomp/mods.hpp" #include "librecomp/helpers.hpp" @@ -83,9 +90,23 @@ ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() { return {}; } -#if defined(__gnu_linux__) +ultramodern::input::connected_device_info_t get_connected_device_info(int controller_num) { + if (recompinput::players::is_single_player_mode() || recompinput::players::get_player_is_assigned(controller_num)) { + return ultramodern::input::connected_device_info_t{ + .connected_device = ultramodern::input::Device::Controller, + .connected_pak = ultramodern::input::Pak::RumblePak, + }; + } + + return ultramodern::input::connected_device_info_t{ + .connected_device = ultramodern::input::Device::None, + .connected_pak = ultramodern::input::Pak::None, + }; +} + #include "icon_bytes.h" +#if defined(__gnu_linux__) bool SetImageAsIcon(const char* filename, SDL_Window* window) { // Read data int width, height, bytesPerPixel; @@ -144,16 +165,6 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t:: window = SDL_CreateWindow("Starfox 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, flags); -#if defined(__linux__) - SetImageAsIcon("icons/512.png", window); - if (ultramodern::renderer::get_graphics_config().wm_option == - ultramodern::renderer::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on - // Linux - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); - } else { - SDL_SetWindowFullscreen(window, 0); - } -#endif if (window == nullptr) { exit_error("Failed to create window: %s\n", SDL_GetError()); @@ -163,6 +174,10 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t:: SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo); +#if defined(__linux__) + SetImageAsIcon("icons/512.png", window); +#endif + #if defined(_WIN32) return ultramodern::renderer::WindowHandle{ wmInfo.info.win.window, GetCurrentThreadId() }; #elif defined(__linux__) || defined(__ANDROID__) @@ -176,7 +191,7 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t:: } void update_gfx(void*) { - recomp::handle_events(); + recompinput::handle_events(); } static SDL_AudioCVT audio_convert; @@ -220,7 +235,8 @@ void queue_samples(int16_t* audio_data, size_t sample_count) { // Convert the audio from 16-bit values to floats and swap the audio channels into the // swap buffer to correct for the address xor caused by endianness handling. - float cur_main_volume = zelda64::get_main_volume() / 100.0f; // Get the current main volume, normalized to 0.0-1.0. + float cur_main_volume = + recompui::config::sound::get_main_volume() / 100.0f; // Get the current main volume, normalized to 0.0-1.0. for (size_t i = 0; i < sample_count; i += input_channels) { swap_buffer[i + 0 + duplicated_input_frames * input_channels] = audio_data[i + 1] * (1.0f / 32768.0f) * cur_main_volume; @@ -358,9 +374,11 @@ std::vector supported_games = { { .rom_hash = 0x163fd3fc3813f54eULL, .internal_name = "STARFOX64", + .display_name = "Starfox 64", .game_id = u8"sf64.n64.us.1.1", .mod_game_id = "sf64", .save_type = recomp::SaveType::Eep4k, + .thumbnail_bytes = std::span(icon_bytes), .is_enabled = true, .decompression_routine = zelda64::decompress_sf64, .has_compressed_code = true, @@ -545,7 +563,7 @@ void release_preload(PreloadContext& context) { context = {}; } -#else +#elif defined(__linux__) || defined(APPLE) struct PreloadContext {}; @@ -560,15 +578,15 @@ void release_preload(PreloadContext& context) { #endif void enable_texture_pack(recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) { - zelda64::renderer::enable_texture_pack(context, mod); + recompui::renderer::enable_texture_pack(context, mod); } void disable_texture_pack(recomp::mods::ModContext&, const recomp::mods::ModHandle& mod) { - zelda64::renderer::disable_texture_pack(mod); + recompui::renderer::disable_texture_pack(mod); } void reorder_texture_pack(recomp::mods::ModContext&) { - zelda64::renderer::trigger_texture_pack_update(); + recompui::renderer::trigger_texture_pack_update(); } #define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name) @@ -642,6 +660,13 @@ int main(int argc, char** argv) { std::filesystem::current_path("/var/data", ec); #endif + // Initialize native file dialogs. + NFD_Init(); + + // Initialize program settings. + recompui::programconfig::set_program_name(zelda64::program_name); + recompui::programconfig::set_program_id(zelda64::program_id); + // Initialize SDL audio and set the output frequency. SDL_InitSubSystem(SDL_INIT_AUDIO); reset_audio(48000); @@ -652,7 +677,11 @@ int main(int argc, char** argv) { fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError()); } - recomp::register_config_path(zelda64::get_app_folder_path()); + // Register fonts. + recompui::register_primary_font("ChiaroNormal.otf", "Chiaro"); + recompui::register_extra_font("ChiaroBold.otf"); + + recomp::register_config_path(recompui::file::get_app_folder_path()); // Register supported games and patches for (const auto& game : supported_games) { @@ -681,15 +710,26 @@ int main(int argc, char** argv) { zelda64::register_overlays(); zelda64::register_patches(); + // recomputil::init_extended_actor_data(); - zelda64::load_config(); + + recompinput::players::set_single_player_mode(true); + + zelda64::init_config(); recomp::rsp::callbacks_t rsp_callbacks{ .get_rsp_microcode = get_rsp_microcode, }; ultramodern::renderer::callbacks_t renderer_callbacks{ - .create_render_context = zelda64::renderer::create_render_context, + .create_render_context = + [](uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode) { + auto presentation_mode = ultramodern::renderer::PresentationMode::PresentEarly; + std::unique_ptr render_context = + recompui::renderer::create_render_context(rdram, window_handle, presentation_mode, developer_mode); + render_context->set_post_blend_negative_dither_noise(true); + return std::unique_ptr(std::move(render_context)); + }, }; ultramodern::gfx_callbacks_t gfx_callbacks{ @@ -705,15 +745,15 @@ int main(int argc, char** argv) { }; ultramodern::input::callbacks_t input_callbacks{ - .poll_input = recomp::poll_inputs, - .get_input = recomp::get_n64_input, - .set_rumble = recomp::set_rumble, - .get_connected_device_info = recomp::get_connected_device_info, + .poll_input = recompinput::poll_inputs, + .get_input = recompinput::profiles::get_n64_input, + .set_rumble = recompinput::set_rumble, + .get_connected_device_info = get_connected_device_info, }; ultramodern::events::callbacks_t thread_callbacks{ - .vi_callback = recomp::update_rumble, - .gfx_init_callback = recompui::update_supported_options, + .vi_callback = recompinput::update_rumble, + .gfx_init_callback = nullptr, }; ultramodern::error_handling::callbacks_t error_handling_callbacks{ @@ -753,6 +793,9 @@ int main(int argc, char** argv) { recomp::start(cfg); + // recomp::start(project_version, {}, rsp_callbacks, renderer_callbacks, audio_callbacks, input_callbacks, + // gfx_callbacks, thread_callbacks, error_handling_callbacks, threads_callbacks); + NFD_Quit(); if (preloaded) { diff --git a/src/main/theme.cpp b/src/main/theme.cpp new file mode 100644 index 0000000..1091b14 --- /dev/null +++ b/src/main/theme.cpp @@ -0,0 +1,106 @@ +#include "elements/ui_theme.h" +#include "theme.h" + +void recomptheme::set_custom_theme() { + using namespace recompui; + + theme::set_theme_color(theme::color::Background1, Color{8, 7, 13, 255}); + theme::set_theme_color(theme::color::Background2, Color{18, 16, 24, 255}); + theme::set_theme_color(theme::color::Background3, Color{25, 22, 34, 255}); + theme::set_theme_color(theme::color::BGOverlay, Color{190, 184, 219, 25}); // actually the overlay for the modal + theme::set_theme_color(theme::color::ModalOverlay, Color{8, 7, 13, 229}); // actually the background color of the modal itself + theme::set_theme_color(theme::color::BGShadow, Color{0, 0, 0, 89}); + theme::set_theme_color(theme::color::BGShadow2, Color{8, 7, 13, 184}); + theme::set_theme_color(theme::color::Text, Color{242, 242, 242, 255}); + theme::set_theme_color(theme::color::TextActive, Color{245, 245, 245, 255}); + theme::set_theme_color(theme::color::TextDim, Color{204, 204, 204, 255}); + theme::set_theme_color(theme::color::TextInactive, Color{255, 255, 255, 153}); + theme::set_theme_color(theme::color::TextA5, Color{242, 242, 242, 13}); + theme::set_theme_color(theme::color::TextA20, Color{242, 242, 242, 51}); + theme::set_theme_color(theme::color::TextA30, Color{242, 242, 242, 77}); + theme::set_theme_color(theme::color::TextA50, Color{242, 242, 242, 128}); + theme::set_theme_color(theme::color::TextA80, Color{242, 242, 242, 204}); + theme::set_theme_color(theme::color::Primary, Color{0xB9, 0x7D, 0xF2, 255}); + theme::set_theme_color(theme::color::PrimaryL, Color{0xDA, 0xBA, 0xF7, 255}); + theme::set_theme_color(theme::color::PrimaryD, Color{0x7A, 0x2A, 0xC6, 255}); + theme::set_theme_color(theme::color::PrimaryA5, Color{185, 125, 242, 13}); + theme::set_theme_color(theme::color::PrimaryA20, Color{185, 125, 242, 51}); + theme::set_theme_color(theme::color::PrimaryA30, Color{185, 125, 242, 77}); + theme::set_theme_color(theme::color::PrimaryA50, Color{185, 125, 242, 128}); + theme::set_theme_color(theme::color::PrimaryA80, Color{185, 125, 242, 204}); + theme::set_theme_color(theme::color::Secondary, Color{0x17, 0xD6, 0xE8, 255}); + theme::set_theme_color(theme::color::SecondaryL, Color{0xA2, 0xEF, 0xF6, 255}); + theme::set_theme_color(theme::color::SecondaryD, Color{0x25, 0xA1, 0xAD, 255}); + theme::set_theme_color(theme::color::SecondaryA5, Color{23, 214, 232, 13}); + theme::set_theme_color(theme::color::SecondaryA20, Color{23, 214, 232, 51}); + theme::set_theme_color(theme::color::SecondaryA30, Color{23, 214, 232, 77}); + theme::set_theme_color(theme::color::SecondaryA50, Color{23, 214, 232, 128}); + theme::set_theme_color(theme::color::SecondaryA80, Color{23, 214, 232, 204}); + theme::set_theme_color(theme::color::Warning, Color{0xE9, 0xCD, 0x35, 255}); + theme::set_theme_color(theme::color::WarningL, Color{0xF9, 0xE5, 0x7C, 255}); + theme::set_theme_color(theme::color::WarningD, Color{0xC5, 0xAA, 0x16, 255}); + theme::set_theme_color(theme::color::WarningA5, Color{233, 205, 53, 13}); + theme::set_theme_color(theme::color::WarningA20, Color{233, 205, 53, 51}); + theme::set_theme_color(theme::color::WarningA30, Color{233, 205, 53, 77}); + theme::set_theme_color(theme::color::WarningA50, Color{233, 205, 53, 128}); + theme::set_theme_color(theme::color::WarningA80, Color{233, 205, 53, 204}); + theme::set_theme_color(theme::color::Danger, Color{0xF8, 0x60, 0x39, 255}); + theme::set_theme_color(theme::color::DangerL, Color{0xFE, 0x86, 0x67, 255}); + theme::set_theme_color(theme::color::DangerD, Color{0xB2, 0x39, 0x19, 255}); + theme::set_theme_color(theme::color::DangerA5, Color{248, 96, 57, 13}); + theme::set_theme_color(theme::color::DangerA20, Color{248, 96, 57, 51}); + theme::set_theme_color(theme::color::DangerA30, Color{248, 96, 57, 77}); + theme::set_theme_color(theme::color::DangerA50, Color{248, 96, 57, 128}); + theme::set_theme_color(theme::color::DangerA80, Color{248, 96, 57, 204}); + theme::set_theme_color(theme::color::Success, Color{0x45, 0xD0, 0x43, 255}); + theme::set_theme_color(theme::color::SuccessL, Color{0xAA, 0xEA, 0xA9, 255}); + theme::set_theme_color(theme::color::SuccessD, Color{0x2C, 0xA7, 0x2A, 255}); + theme::set_theme_color(theme::color::SuccessA5, Color{69, 208, 67, 13}); + theme::set_theme_color(theme::color::SuccessA20, Color{69, 208, 67, 51}); + theme::set_theme_color(theme::color::SuccessA30, Color{69, 208, 67, 77}); + theme::set_theme_color(theme::color::SuccessA50, Color{69, 208, 67, 128}); + theme::set_theme_color(theme::color::SuccessA80, Color{69, 208, 67, 204}); + theme::set_theme_color(theme::color::Border, Color{255, 255, 255, 51}); + theme::set_theme_color(theme::color::BorderSoft, Color{255, 255, 255, 26}); + theme::set_theme_color(theme::color::BorderHard, Color{255, 255, 255, 77}); + theme::set_theme_color(theme::color::BorderSolid, Color{255, 255, 255, 153}); + theme::set_theme_color(theme::color::Transparent, Color{0, 0, 0, 0}); + theme::set_theme_color(theme::color::A, Color{51, 51, 255, 255}); + theme::set_theme_color(theme::color::AL, Color{178, 178, 255, 255}); + theme::set_theme_color(theme::color::AD, Color{32, 32, 172, 255}); + theme::set_theme_color(theme::color::AA5, Color{51, 51, 255, 13}); + theme::set_theme_color(theme::color::AA20, Color{51, 51, 255, 51}); + theme::set_theme_color(theme::color::AA30, Color{51, 51, 255, 77}); + theme::set_theme_color(theme::color::AA50, Color{51, 51, 255, 128}); + theme::set_theme_color(theme::color::AA80, Color{51, 51, 255, 204}); + theme::set_theme_color(theme::color::White, Color{255, 255, 255, 255}); + theme::set_theme_color(theme::color::WhiteA5, Color{255, 255, 255, 13}); + theme::set_theme_color(theme::color::WhiteA20, Color{255, 255, 255, 51}); + theme::set_theme_color(theme::color::WhiteA30, Color{255, 255, 255, 77}); + theme::set_theme_color(theme::color::WhiteA50, Color{255, 255, 255, 128}); + theme::set_theme_color(theme::color::WhiteA80, Color{255, 255, 255, 204}); + theme::set_theme_color(theme::color::BW05, Color{13, 13, 13, 255}); + theme::set_theme_color(theme::color::BW10, Color{26, 26, 26, 255}); + theme::set_theme_color(theme::color::BW25, Color{64, 64, 64, 255}); + theme::set_theme_color(theme::color::BW50, Color{128, 128, 128, 255}); + theme::set_theme_color(theme::color::BW75, Color{191, 191, 191, 255}); + theme::set_theme_color(theme::color::BW90, Color{229, 229, 229, 255}); + + theme::border::radius_sm = 8.0f; + theme::border::radius_md = 12.0f; + theme::border::radius_lg = 16.0f; + + const uint32_t header_weight = 700; + const uint32_t label_weight = 700; + const uint32_t base_weight = 400; + theme::set_typography_preset(theme::Typography::Header1, 64.0f + 4.0f, 0.07f, header_weight); + theme::set_typography_preset(theme::Typography::Header2, 48.0f + 4.0f, 0.07f, header_weight); + theme::set_typography_preset(theme::Typography::Header3, 32.0f + 4.0f, 0.07f, header_weight); + theme::set_typography_preset(theme::Typography::LabelLG, 32.0f + 4.0f, 0.11f, label_weight); + theme::set_typography_preset(theme::Typography::LabelMD, 24.0f + 4.0f, 0.11f, label_weight); + theme::set_typography_preset(theme::Typography::LabelSM, 16.0f + 4.0f, 0.14f, label_weight); + theme::set_typography_preset(theme::Typography::LabelXS, 14.0f + 4.0f, 0.14f, base_weight); + theme::set_typography_preset(theme::Typography::Body, 16.0f + 4.0f, 0, base_weight); +}; + + diff --git a/src/main/theme.h b/src/main/theme.h new file mode 100644 index 0000000..deaa66b --- /dev/null +++ b/src/main/theme.h @@ -0,0 +1,6 @@ +#pragma once + +namespace recomptheme { + // Applies custom themes. recompui::apply_color_hack MUST be called after. + void set_custom_theme(); +} // namespace recomptheme diff --git a/src/ui/core/ui_context.cpp b/src/ui/core/ui_context.cpp deleted file mode 100644 index cff69fc..0000000 --- a/src/ui/core/ui_context.cpp +++ /dev/null @@ -1,725 +0,0 @@ -#include -#include -#include -#include - -#include "slot_map.h" -#include "RmlUi/Core/StreamMemory.h" - -#include "ultramodern/error_handling.hpp" -#include "recomp_ui.h" -#include "ui_context.h" -#include "../elements/ui_element.h" - -// Hash implementations for ContextId and ResourceId. -template <> -struct std::hash { - std::size_t operator()(const recompui::ContextId& id) const { - return std::hash()(id.slot_id); - } -}; - -template <> -struct std::hash { - std::size_t operator()(const recompui::ResourceId& id) const { - return std::hash()(id.slot_id); - } -}; - -using resource_slotmap = dod::slot_map32>; - -namespace recompui { - struct Context { - std::mutex context_lock; - resource_slotmap resources; - Rml::ElementDocument* document; - Element root_element; - Element* autofocus_element = nullptr; - std::vector loose_elements; - std::unordered_set to_update; - std::vector> to_set_text; - bool captures_input = true; - bool captures_mouse = true; - Context(Rml::ElementDocument* document) : document(document), root_element(document) {} - }; -} // namespace recompui - -using context_slotmap = dod::slot_map32; - -static struct { - std::recursive_mutex all_contexts_lock; - context_slotmap all_contexts; - std::unordered_set opened_contexts; - std::unordered_map documents_to_contexts; - Rml::SharedPtr style_sheet; -} context_state; - -thread_local recompui::Context* opened_context = nullptr; -thread_local recompui::ContextId opened_context_id = recompui::ContextId::null(); - -enum class ContextErrorType { - OpenWithoutClose, - OpenInvalidContext, - CloseWithoutOpen, - CloseWrongContext, - DestroyInvalidContext, - GetContextWithoutOpen, - AddResourceWithoutOpen, - AddResourceToWrongContext, - UpdateElementWithoutContext, - UpdateElementInWrongContext, - SetTextElementWithoutContext, - SetTextElementInWrongContext, - GetResourceWithoutOpen, - GetResourceFailed, - DestroyResourceWithoutOpen, - DestroyResourceInWrongContext, - DestroyResourceNotFound, - GetDocumentInvalidContext, - GetAutofocusInvalidContext, - SetAutofocusInvalidContext, - InternalError, -}; - -enum class SlotTag : uint8_t { - Style = 0, - Element = 1, -}; - -void context_error(recompui::ContextId id, ContextErrorType type) { - (void)id; - - const char* error_message = ""; - - switch (type) { - case ContextErrorType::OpenWithoutClose: - error_message = "Attempted to open a UI context without closing another UI context"; - break; - case ContextErrorType::OpenInvalidContext: - error_message = "Attempted to open an invalid UI context"; - break; - case ContextErrorType::CloseWithoutOpen: - error_message = "Attempted to close a UI context without one being open"; - break; - case ContextErrorType::CloseWrongContext: - error_message = "Attempted to close a different UI context than the one that's open"; - break; - case ContextErrorType::DestroyInvalidContext: - error_message = "Attempted to destroy an invalid UI element"; - break; - case ContextErrorType::GetContextWithoutOpen: - error_message = "Attempted to get the current UI context with no UI context open"; - break; - case ContextErrorType::AddResourceWithoutOpen: - error_message = "Attempted to create a UI resource with no open UI context"; - break; - case ContextErrorType::AddResourceToWrongContext: - error_message = "Attempted to create a UI resource in a different UI context than the one that's open"; - break; - case ContextErrorType::UpdateElementWithoutContext: - error_message = "Attempted to update a UI element with no open UI context"; - break; - case ContextErrorType::UpdateElementInWrongContext: - error_message = "Attempted to update a UI element in a different UI context than the one that's open"; - break; - case ContextErrorType::SetTextElementWithoutContext: - error_message = "Attempted to set the text of a UI element with no open UI context"; - break; - case ContextErrorType::SetTextElementInWrongContext: - error_message = "Attempted to set the text of a UI element in a different UI context than the one that's open"; - break; - case ContextErrorType::GetResourceWithoutOpen: - error_message = "Attempted to get a UI resource with no open UI context"; - break; - case ContextErrorType::GetResourceFailed: - error_message = "Failed to get a UI resource from the current open UI context"; - break; - case ContextErrorType::DestroyResourceWithoutOpen: - error_message = "Attempted to destroy a UI resource with no open UI context"; - break; - case ContextErrorType::DestroyResourceInWrongContext: - error_message = "Attempted to destroy a UI resource in a different UI context than the one that's open"; - break; - case ContextErrorType::DestroyResourceNotFound: - error_message = "Attempted to destroy a UI resource that doesn't exist in the current context"; - break; - case ContextErrorType::GetDocumentInvalidContext: - error_message = "Attempted to get the document of an invalid UI context"; - break; - case ContextErrorType::GetAutofocusInvalidContext: - error_message = "Attempted to get the autofocus element of an invalid UI context"; - break; - case ContextErrorType::SetAutofocusInvalidContext: - error_message = "Attempted to set the autofocus element of an invalid UI context"; - break; - case ContextErrorType::InternalError: - error_message = "Internal error in UI context"; - break; - default: - error_message = "Unknown UI context error"; - break; - } - - // This assumes the error is coming from a mod, as it's unlikely that an end user will see a UI context error - // in the base recomp. - recompui::message_box((std::string{"Fatal error in mod - "} + error_message + ".").c_str()); - assert(false); - ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__); -} - -recompui::ContextId create_context_impl(Rml::ElementDocument* document) { - static Rml::ElementDocument dummy_document{""}; - bool add_to_dict = true; - - if (document == nullptr) { - document = &dummy_document; - add_to_dict = false; - } - - recompui::ContextId ret; - { - std::lock_guard lock{ context_state.all_contexts_lock }; - ret = { context_state.all_contexts.emplace(document).raw }; - - if (add_to_dict) { - context_state.documents_to_contexts.emplace(document, ret); - } - } - - return ret; -} - -void recompui::init_styling(const std::filesystem::path& rcss_file) { - std::string style{}; - { - std::ifstream style_stream{rcss_file}; - style_stream.seekg(0, std::ios::end); - style.resize(style_stream.tellg()); - style_stream.seekg(0, std::ios::beg); - - style_stream.read(style.data(), style.size()); - } - std::unique_ptr rml_stream = std::make_unique(reinterpret_cast(style.data()), style.size()); - rml_stream->SetSourceURL(rcss_file.filename().string()); - context_state.style_sheet = Rml::Factory::InstanceStyleSheetStream(rml_stream.get()); -} - -recompui::ContextId recompui::create_context(const std::filesystem::path& path) { - ContextId new_context = create_context_impl(nullptr); - - auto workingdir = std::filesystem::current_path(); - - new_context.open(); - Rml::ElementDocument* doc = recompui::load_document(path.string()); - opened_context->document = doc; - opened_context->root_element.base = doc; - new_context.close(); - - { - std::lock_guard lock{ context_state.all_contexts_lock }; - context_state.documents_to_contexts.emplace(doc, new_context); - } - - return new_context; -} - -recompui::ContextId recompui::create_context(Rml::ElementDocument* document) { - assert(document != nullptr); - - return create_context_impl(document); -} - -recompui::ContextId recompui::create_context() { - Rml::ElementDocument* doc = create_empty_document(); - doc->SetStyleSheetContainer(context_state.style_sheet); - ContextId ret = create_context_impl(doc); - Element* root = ret.get_root_element(); - // Mark the root element as not being a shim, as that's only needed for elements that were parented to Rml ones manually. - root->shim = false; - - ret.open(); - root->set_width(100.0f, Unit::Percent); - root->set_height(100.0f, Unit::Percent); - root->set_display(Display::Flex); - ret.close(); - - doc->Hide(); - - return ret; -} - -void recompui::destroy_context(ContextId id) { - bool existed = false; - - // TODO prevent deletion of a context while its mutex is in use. Second lock on the context's mutex before popping - // from the slotmap? - - // Check if the provided id exists. - { - std::lock_guard lock{ context_state.all_contexts_lock }; - // Check if the target context is currently open. - existed = context_state.all_contexts.has_key(context_slotmap::key{ id.slot_id }); - } - - - // Raise an error if the context didn't exist. - if (!existed) { - context_error(id, ContextErrorType::DestroyInvalidContext); - } - - id.open(); - id.clear_children(); - id.close(); - - // Delete the provided id. - { - std::lock_guard lock{ context_state.all_contexts_lock }; - context_state.all_contexts.erase(context_slotmap::key{ id.slot_id }); - } -} - -void recompui::destroy_all_contexts() { - recompui::hide_all_contexts(); - - std::lock_guard lock{ context_state.all_contexts_lock }; - - // TODO prevent deletion of a context while its mutex is in use. Second lock on the context's mutex before popping - // from the slotmap - - std::vector keys{}; - for (const auto& [key, item] : context_state.all_contexts.items()) { - keys.push_back(key); - } - - for (auto key : keys) { - Context* ctx = context_state.all_contexts.get(key); - - std::lock_guard context_lock{ ctx->context_lock }; - opened_context = ctx; - opened_context_id = ContextId{ key }; - - opened_context_id.clear_children(); - - opened_context = nullptr; - opened_context_id = ContextId::null(); - } - - context_state.all_contexts.reset(); - context_state.documents_to_contexts.clear(); -} - -void recompui::ContextId::open() { - // Ensure no other context is opened by this thread already. - if (opened_context_id != ContextId::null()) { - context_error(*this, ContextErrorType::OpenWithoutClose); - } - - // Get the context with this id. - Context* ctx; - { - std::lock_guard lock{ context_state.all_contexts_lock }; - ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id }); - // If the context was found, add it to the opened contexts. - if (ctx != nullptr) { - context_state.opened_contexts.emplace(*this); - } - } - - // Check if the context exists. - if (ctx == nullptr) { - context_error(*this, ContextErrorType::OpenInvalidContext); - } - - // Take ownership of the target context. - ctx->context_lock.lock(); - opened_context = ctx; - opened_context_id = *this; -} - -bool recompui::ContextId::open_if_not_already() { - if (opened_context_id == *this) { - return false; - } - - open(); - return true; -} - -void recompui::ContextId::close() { - // Ensure a context is currently opened by this thread. - if (opened_context_id == ContextId::null()) { - context_error(*this, ContextErrorType::CloseWithoutOpen); - } - - // Check that the context that was specified is the same one that's currently open. - if (*this != opened_context_id) { - context_error(*this, ContextErrorType::CloseWrongContext); - } - - // Release ownership of the target context. - opened_context->context_lock.unlock(); - opened_context = nullptr; - opened_context_id = ContextId::null(); - - // Remove this context from the opened contexts. - { - std::lock_guard lock{ context_state.all_contexts_lock }; - context_state.opened_contexts.erase(*this); - } -} - -recompui::ContextId recompui::try_close_current_context() { - if (opened_context_id != ContextId::null()) { - ContextId prev_context = opened_context_id; - opened_context_id.close(); - return prev_context; - } - return ContextId::null(); -} - -void recompui::ContextId::process_updates() { - // Ensure a context is currently opened by this thread. - if (opened_context_id == ContextId::null()) { - context_error(*this, ContextErrorType::InternalError); - } - - // Check that the context that was specified is the same one that's currently open. - if (*this != opened_context_id) { - context_error(*this, ContextErrorType::InternalError); - } - - // Move the current update set into a local variable. This clears the update set - // and allows it to be used to queue updates from any element callbacks. - std::unordered_set to_update = std::move(opened_context->to_update); - - Event update_event = Event::update_event(); - - for (auto cur_resource_id : to_update) { - resource_slotmap::key cur_key{ cur_resource_id.slot_id }; - - // Ignore any resources that aren't elements. - if (cur_key.get_tag() != static_cast(SlotTag::Element)) { - // Assert to catch errors of queueing other resource types for update. - // This isn't an actual error, so there's no issue with continuing in release builds. - assert(false); - continue; - } - - // Get the resource being updaten from the context. - std::unique_ptr - - - - - - - - - -
-
-
- - -
General
-
-
- -