Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b6f1fbf074 | |||
| 7a77d48954 | |||
| 4ee0d8ed4b | |||
| ce554a107d | |||
| 9ce1ab7d5a | |||
| 8830760d34 | |||
| 9abe89f47f | |||
| 3e62c1e96e | |||
| d9bbea300d | |||
| 1a951511be | |||
| ab0efb7a3b | |||
| 5fdc3c7a54 | |||
| 2978ae145d | |||
| e93773757f | |||
| 6be742b15f | |||
| 861efaa053 | |||
| 8e41e0195e | |||
| e9359a92d7 | |||
| 0c78376ba8 | |||
| 8c001f7968 | |||
| ef43b94370 | |||
| 76efa02beb | |||
| aeeb1ccdd2 | |||
| 93c7d0d64d | |||
| 1b76b2650c | |||
| 45196886b0 | |||
| 80af15c95b | |||
| 4c5e3b933e | |||
| 5eddcb9653 | |||
| 6dd50c955c | |||
| 4db65b9845 | |||
| ede6827369 | |||
| 2c9b20841d | |||
| 2b9ed729a3 | |||
| f39195c5e0 | |||
| a0f42c0c80 | |||
| d1e9d5af2f | |||
| 61b2e6ce4d | |||
| e73244bca5 | |||
| a4f25ecb28 | |||
| 39b546b81f | |||
| 1bd4585994 | |||
| c896bb39ea | |||
| 3366613354 | |||
| 79b1f4ab4d | |||
| 157f4f9df2 | |||
| 9d5d8dd13a | |||
| b0f1fbee1c | |||
| 40e3f7d057 | |||
| 764fc0b96f | |||
| d6cbc9b6d5 | |||
| b10211c4c2 |
@@ -67,9 +67,9 @@ jobs:
|
|||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-linux-${{matrix.preset}}-${{matrix.artifact_arch}}
|
name: dusklight-${{env.DUSK_VERSION}}-linux-${{matrix.preset}}-${{matrix.artifact_arch}}
|
||||||
path: |
|
path: |
|
||||||
build/install/Dusk-*.AppImage
|
build/install/Dusklight-*.AppImage
|
||||||
build/install/debug.tar.*
|
build/install/debug.tar.*
|
||||||
|
|
||||||
build-apple:
|
build-apple:
|
||||||
@@ -135,9 +135,9 @@ jobs:
|
|||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-${{matrix.artifact_name}}
|
name: dusklight-${{env.DUSK_VERSION}}-${{matrix.artifact_name}}
|
||||||
path: |
|
path: |
|
||||||
build/install/Dusk.app
|
build/install/Dusklight.app
|
||||||
build/install/debug.tar.*
|
build/install/debug.tar.*
|
||||||
|
|
||||||
build-android:
|
build-android:
|
||||||
@@ -192,7 +192,7 @@ jobs:
|
|||||||
run: cmake --preset ${{matrix.preset}}
|
run: cmake --preset ${{matrix.preset}}
|
||||||
|
|
||||||
- name: Build native library
|
- name: Build native library
|
||||||
run: cmake --build --preset ${{matrix.preset}} --target dusk
|
run: cmake --build --preset ${{matrix.preset}} --target dusklight
|
||||||
|
|
||||||
- name: Stage stripped JNI library
|
- name: Stage stripped JNI library
|
||||||
run: ANDROID_STAGE_ABIS="${{matrix.abi}}" platforms/android/scripts/stage-jni-libs.sh
|
run: ANDROID_STAGE_ABIS="${{matrix.abi}}" platforms/android/scripts/stage-jni-libs.sh
|
||||||
@@ -204,7 +204,7 @@ jobs:
|
|||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-android-${{matrix.artifact_arch}}
|
name: dusklight-${{env.DUSK_VERSION}}-android-${{matrix.artifact_arch}}
|
||||||
path: platforms/android/app/build/outputs/apk/release/app-${{matrix.abi}}-release-unsigned.apk
|
path: platforms/android/app/build/outputs/apk/release/app-${{matrix.abi}}-release-unsigned.apk
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
@@ -266,7 +266,7 @@ jobs:
|
|||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dusk-${{env.DUSK_VERSION}}-win32-msvc-${{matrix.artifact_arch}}
|
name: dusklight-${{env.DUSK_VERSION}}-win32-msvc-${{matrix.artifact_arch}}
|
||||||
path: |
|
path: |
|
||||||
build/install/*.exe
|
build/install/*.exe
|
||||||
build/install/*.dll
|
build/install/*.dll
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ compile_commands.json
|
|||||||
# MacOS
|
# MacOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# direnv / nix
|
||||||
|
.direnv/
|
||||||
|
.envrc
|
||||||
|
|
||||||
# ISOs
|
# ISOs
|
||||||
*.iso
|
*.iso
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "(gdb) Launch Dusk MSVC",
|
"name": "(gdb) Launch Dusklight MSVC",
|
||||||
"type": "cppvsdbg",
|
"type": "cppvsdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${command:cmake.launchTargetPath}",
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cmake.buildDirectory": "${workspaceFolder}/build/dusk/${buildType}/${variant:tp_version}",
|
"cmake.buildDirectory": "${workspaceFolder}/build/dusklight/${buildType}/${variant:tp_version}",
|
||||||
"cmake.generator": "Ninja",
|
"cmake.generator": "Ninja",
|
||||||
"cmake.configureSettings": {
|
"cmake.configureSettings": {
|
||||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||||
|
|||||||
@@ -5,8 +5,19 @@ if (NOT CMAKE_BUILD_TYPE)
|
|||||||
"Build type options: Debug Release RelWithDebInfo MinSizeRel" FORCE)
|
"Build type options: Debug Release RelWithDebInfo MinSizeRel" FORCE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# obtain revision info from git
|
set(DUSK_VERSION_OVERRIDE "" CACHE STRING "Override version string (skips git detection and format validation)")
|
||||||
find_package(Git)
|
|
||||||
|
if (DUSK_VERSION_OVERRIDE)
|
||||||
|
set(DUSK_WC_DESCRIBE "${DUSK_VERSION_OVERRIDE}")
|
||||||
|
set(DUSK_VERSION_STRING "0.0.0.0")
|
||||||
|
set(DUSK_SHORT_VERSION_STRING "0.0.0")
|
||||||
|
set(DUSK_WC_REVISION "")
|
||||||
|
set(DUSK_WC_BRANCH "")
|
||||||
|
set(DUSK_WC_DATE "")
|
||||||
|
message(STATUS "Dusklight version overridden to ${DUSK_WC_DESCRIBE}")
|
||||||
|
else ()
|
||||||
|
# obtain revision info from git
|
||||||
|
find_package(Git)
|
||||||
if (GIT_FOUND)
|
if (GIT_FOUND)
|
||||||
# make sure version information gets re-run when the current Git HEAD changes
|
# make sure version information gets re-run when the current Git HEAD changes
|
||||||
execute_process(WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --git-path HEAD
|
execute_process(WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --git-path HEAD
|
||||||
@@ -63,13 +74,15 @@ else ()
|
|||||||
set(DUSK_SHORT_VERSION_STRING "0.0.0")
|
set(DUSK_SHORT_VERSION_STRING "0.0.0")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Add version information to CI environment variables
|
# Add version information to CI environment variables
|
||||||
if(DEFINED ENV{GITHUB_ENV})
|
if(DEFINED ENV{GITHUB_ENV})
|
||||||
file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n")
|
file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Dusk version set to ${DUSK_WC_DESCRIBE}")
|
message(STATUS "Dusklight version set to ${DUSK_WC_DESCRIBE}")
|
||||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
project(dusk LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING})
|
project(dusklight LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING})
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
enable_language(OBJC OBJCXX)
|
enable_language(OBJC OBJCXX)
|
||||||
endif ()
|
endif ()
|
||||||
@@ -112,11 +125,6 @@ option(DUSK_BUILD_WARNINGS "Enable compiler warnings (off by default)")
|
|||||||
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
|
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
|
||||||
option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON)
|
option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON)
|
||||||
option(DUSK_ENABLE_UPDATE_CHECKER "Enable update checking support" ON)
|
option(DUSK_ENABLE_UPDATE_CHECKER "Enable update checking support" ON)
|
||||||
|
|
||||||
if(ANDROID)
|
|
||||||
set(DUSK_MOVIE_SUPPORT OFF)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
|
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
|
||||||
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
|
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
|
||||||
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
|
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
|
||||||
@@ -134,9 +142,9 @@ endif ()
|
|||||||
if (DUSK_MOVIE_SUPPORT)
|
if (DUSK_MOVIE_SUPPORT)
|
||||||
find_package(libjpeg-turbo 3.0 CONFIG QUIET)
|
find_package(libjpeg-turbo 3.0 CONFIG QUIET)
|
||||||
if (libjpeg-turbo_FOUND)
|
if (libjpeg-turbo_FOUND)
|
||||||
message(STATUS "dusk: Using system libjpeg-turbo")
|
message(STATUS "dusklight: Using system libjpeg-turbo")
|
||||||
else ()
|
else ()
|
||||||
message(STATUS "dusk: Fetching libjpeg-turbo")
|
message(STATUS "dusklight: Fetching libjpeg-turbo")
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
set(_jpeg_install_dir ${CMAKE_BINARY_DIR}/libjpeg-turbo-install)
|
set(_jpeg_install_dir ${CMAKE_BINARY_DIR}/libjpeg-turbo-install)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -155,6 +163,8 @@ if (DUSK_MOVIE_SUPPORT)
|
|||||||
list(APPEND _jpeg_cmake_args -DCMAKE_TOOLCHAIN_FILE=${_jpeg_toolchain_file})
|
list(APPEND _jpeg_cmake_args -DCMAKE_TOOLCHAIN_FILE=${_jpeg_toolchain_file})
|
||||||
endif ()
|
endif ()
|
||||||
set(_jpeg_passthrough_vars
|
set(_jpeg_passthrough_vars
|
||||||
|
ANDROID_ABI
|
||||||
|
ANDROID_PLATFORM
|
||||||
CMAKE_BUILD_TYPE
|
CMAKE_BUILD_TYPE
|
||||||
CMAKE_C_COMPILER
|
CMAKE_C_COMPILER
|
||||||
CMAKE_C_COMPILER_LAUNCHER
|
CMAKE_C_COMPILER_LAUNCHER
|
||||||
@@ -226,13 +236,13 @@ endif ()
|
|||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
# Declare all dependencies first so CMake can download them in parallel
|
# Declare all dependencies first so CMake can download them in parallel
|
||||||
message(STATUS "dusk: Fetching cxxopts")
|
message(STATUS "dusklight: Fetching cxxopts")
|
||||||
FetchContent_Declare(cxxopts
|
FetchContent_Declare(cxxopts
|
||||||
URL https://github.com/jarro2783/cxxopts/archive/refs/tags/v3.3.1.tar.gz
|
URL https://github.com/jarro2783/cxxopts/archive/refs/tags/v3.3.1.tar.gz
|
||||||
URL_HASH SHA256=3bfc70542c521d4b55a46429d808178916a579b28d048bd8c727ee76c39e2072
|
URL_HASH SHA256=3bfc70542c521d4b55a46429d808178916a579b28d048bd8c727ee76c39e2072
|
||||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||||
)
|
)
|
||||||
message(STATUS "dusk: Fetching nlohmann/json")
|
message(STATUS "dusklight: Fetching nlohmann/json")
|
||||||
FetchContent_Declare(json
|
FetchContent_Declare(json
|
||||||
URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
|
URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
|
||||||
URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa
|
URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa
|
||||||
@@ -241,7 +251,7 @@ FetchContent_Declare(json
|
|||||||
FetchContent_MakeAvailable(cxxopts json)
|
FetchContent_MakeAvailable(cxxopts json)
|
||||||
|
|
||||||
if (DUSK_ENABLE_SENTRY_NATIVE)
|
if (DUSK_ENABLE_SENTRY_NATIVE)
|
||||||
message(STATUS "dusk: Fetching sentry-native")
|
message(STATUS "dusklight: Fetching sentry-native")
|
||||||
set(SENTRY_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
set(SENTRY_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
set(SENTRY_BACKEND crashpad CACHE STRING "" FORCE)
|
set(SENTRY_BACKEND crashpad CACHE STRING "" FORCE)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -286,15 +296,15 @@ include(files.cmake)
|
|||||||
|
|
||||||
# TODO: version handling for res includes
|
# TODO: version handling for res includes
|
||||||
|
|
||||||
set(DUSK_BUNDLE_NAME Dusk)
|
set(DUSK_BUNDLE_NAME Dusklight)
|
||||||
set(DUSK_BUNDLE_IDENTIFIER dev.twilitrealm.dusk)
|
set(DUSK_BUNDLE_IDENTIFIER dev.twilitrealm.dusk)
|
||||||
set(DUSK_COMPANY_NAME "Twilit Realm")
|
set(DUSK_COMPANY_NAME "Twilit Realm")
|
||||||
set(DUSK_FILE_DESCRIPTION "Dusk")
|
set(DUSK_FILE_DESCRIPTION "Dusklight")
|
||||||
set(DUSK_PRODUCT_NAME "Dusk")
|
set(DUSK_PRODUCT_NAME "Dusklight")
|
||||||
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
|
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
|
||||||
|
|
||||||
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
|
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
|
||||||
source_group("dusk" FILES ${DUSK_FILES} ${DUSK_HTTP_BACKEND_FILES})
|
source_group("dusklight" FILES ${DUSK_FILES} ${DUSK_HTTP_BACKEND_FILES})
|
||||||
|
|
||||||
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1)
|
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1)
|
||||||
|
|
||||||
@@ -331,30 +341,30 @@ if (DUSK_ENABLE_UPDATE_CHECKER)
|
|||||||
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/winhttp.cpp)
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/winhttp.cpp)
|
||||||
list(APPEND GAME_LIBS winhttp)
|
list(APPEND GAME_LIBS winhttp)
|
||||||
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_WINHTTP=1)
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_WINHTTP=1)
|
||||||
message(STATUS "dusk: Enabled update checker (WinHTTP)")
|
message(STATUS "dusklight: Enabled update checker (WinHTTP)")
|
||||||
elseif (ANDROID)
|
elseif (ANDROID)
|
||||||
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/android.cpp)
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/android.cpp)
|
||||||
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_ANDROID=1)
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_ANDROID=1)
|
||||||
message(STATUS "dusk: Enabled update checker (Android)")
|
message(STATUS "dusklight: Enabled update checker (Android)")
|
||||||
elseif (APPLE)
|
elseif (APPLE)
|
||||||
find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED)
|
find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED)
|
||||||
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/url_session.mm)
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/url_session.mm)
|
||||||
set_source_files_properties(src/dusk/http/url_session.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
set_source_files_properties(src/dusk/http/url_session.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
list(APPEND GAME_LIBS ${FOUNDATION_FRAMEWORK})
|
list(APPEND GAME_LIBS ${FOUNDATION_FRAMEWORK})
|
||||||
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_URLSESSION=1)
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_URLSESSION=1)
|
||||||
message(STATUS "dusk: Enabled update checker (NSURLSession)")
|
message(STATUS "dusklight: Enabled update checker (NSURLSession)")
|
||||||
elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||||
find_package(CURL QUIET OPTIONAL_COMPONENTS HTTPS SSL)
|
find_package(CURL QUIET OPTIONAL_COMPONENTS HTTPS SSL)
|
||||||
if (CURL_FOUND AND CURL_HTTPS_FOUND AND CURL_SSL_FOUND)
|
if (CURL_FOUND AND CURL_HTTPS_FOUND AND CURL_SSL_FOUND)
|
||||||
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/curl.cpp)
|
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/curl.cpp)
|
||||||
list(APPEND GAME_LIBS CURL::libcurl)
|
list(APPEND GAME_LIBS CURL::libcurl)
|
||||||
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_LIBCURL=1)
|
list(APPEND GAME_COMPILE_DEFS DUSK_HTTP_BACKEND_LIBCURL=1)
|
||||||
message(STATUS "dusk: Enabled update checker (libcurl)")
|
message(STATUS "dusklight: Enabled update checker (libcurl)")
|
||||||
else ()
|
else ()
|
||||||
message(STATUS "dusk: Disabled update checker (libcurl + HTTPS/SSL not found)")
|
message(STATUS "dusklight: Disabled update checker (libcurl + HTTPS/SSL not found)")
|
||||||
endif ()
|
endif ()
|
||||||
else ()
|
else ()
|
||||||
message(STATUS "dusk: Disabled update checker (unsupported platform)")
|
message(STATUS "dusklight: Disabled update checker (unsupported platform)")
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
list(APPEND DUSK_FILES ${DUSK_HTTP_BACKEND_SOURCE})
|
list(APPEND DUSK_FILES ${DUSK_HTTP_BACKEND_SOURCE})
|
||||||
@@ -432,22 +442,22 @@ endif ()
|
|||||||
|
|
||||||
set(DUSK_FILES src/dusk/main.cpp ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
|
set(DUSK_FILES src/dusk/main.cpp ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
add_library(dusk SHARED ${DUSK_FILES})
|
add_library(dusklight SHARED ${DUSK_FILES})
|
||||||
set_target_properties(dusk PROPERTIES OUTPUT_NAME main)
|
set_target_properties(dusklight PROPERTIES OUTPUT_NAME main)
|
||||||
else ()
|
else ()
|
||||||
add_executable(dusk ${DUSK_FILES})
|
add_executable(dusklight ${DUSK_FILES})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_compile_definitions(dusk PRIVATE ${GAME_COMPILE_DEFS})
|
target_compile_definitions(dusklight PRIVATE ${GAME_COMPILE_DEFS})
|
||||||
target_include_directories(dusk PRIVATE ${GAME_INCLUDE_DIRS})
|
target_include_directories(dusklight PRIVATE ${GAME_INCLUDE_DIRS})
|
||||||
target_link_libraries(dusk PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBRARIES})
|
target_link_libraries(dusklight PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBRARIES})
|
||||||
target_precompile_headers(dusk PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
|
target_precompile_headers(dusklight PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
|
||||||
if (TARGET crashpad_handler)
|
if (TARGET crashpad_handler)
|
||||||
add_dependencies(dusk crashpad_handler)
|
add_dependencies(dusklight crashpad_handler)
|
||||||
add_custom_command(TARGET dusk POST_BUILD
|
add_custom_command(TARGET dusklight POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
"$<TARGET_FILE:crashpad_handler>"
|
"$<TARGET_FILE:crashpad_handler>"
|
||||||
"$<TARGET_FILE_DIR:dusk>"
|
"$<TARGET_FILE_DIR:dusklight>"
|
||||||
COMMENT "Copying crashpad handler"
|
COMMENT "Copying crashpad handler"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
@@ -455,14 +465,14 @@ endif ()
|
|||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
# SDLActivity loads SDL_main via dlsym on Android. Since aurora::main is a static
|
# SDLActivity loads SDL_main via dlsym on Android. Since aurora::main is a static
|
||||||
# archive, force an undefined reference so the linker keeps the SDL_main object.
|
# archive, force an undefined reference so the linker keeps the SDL_main object.
|
||||||
target_link_options(dusk PRIVATE "-Wl,-u,SDL_main")
|
target_link_options(dusklight PRIVATE "-Wl,-u,SDL_main")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
add_custom_command(TARGET dusk POST_BUILD
|
add_custom_command(TARGET dusklight POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
"${CMAKE_SOURCE_DIR}/res"
|
"${CMAKE_SOURCE_DIR}/res"
|
||||||
"$<TARGET_FILE_DIR:dusk>/res"
|
"$<TARGET_FILE_DIR:dusklight>/res"
|
||||||
COMMENT "Copying resources"
|
COMMENT "Copying resources"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
@@ -470,9 +480,9 @@ endif ()
|
|||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(DUSK_WINDOWS_RESOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/platforms/windows)
|
set(DUSK_WINDOWS_RESOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/platforms/windows)
|
||||||
set(DUSK_WINDOWS_ICON_PNG ${CMAKE_CURRENT_SOURCE_DIR}/res/icon.png)
|
set(DUSK_WINDOWS_ICON_PNG ${CMAKE_CURRENT_SOURCE_DIR}/res/icon.png)
|
||||||
set(DUSK_WINDOWS_ICON_ICO ${CMAKE_CURRENT_BINARY_DIR}/dusk.ico)
|
set(DUSK_WINDOWS_ICON_ICO ${CMAKE_CURRENT_BINARY_DIR}/dusklight.ico)
|
||||||
set(DUSK_WINDOWS_RC ${CMAKE_CURRENT_BINARY_DIR}/dusk.rc)
|
set(DUSK_WINDOWS_RC ${CMAKE_CURRENT_BINARY_DIR}/dusklight.rc)
|
||||||
set(DUSK_WINDOWS_MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/dusk.manifest)
|
set(DUSK_WINDOWS_MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/dusklight.manifest)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${DUSK_WINDOWS_ICON_ICO}
|
OUTPUT ${DUSK_WINDOWS_ICON_ICO}
|
||||||
@@ -485,14 +495,14 @@ if (WIN32)
|
|||||||
COMMENT "Generating Windows icon"
|
COMMENT "Generating Windows icon"
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusk.manifest.in ${DUSK_WINDOWS_MANIFEST} @ONLY)
|
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusklight.manifest.in ${DUSK_WINDOWS_MANIFEST} @ONLY)
|
||||||
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusk.rc.in ${DUSK_WINDOWS_RC} @ONLY)
|
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusklight.rc.in ${DUSK_WINDOWS_RC} @ONLY)
|
||||||
|
|
||||||
target_sources(dusk PRIVATE ${DUSK_WINDOWS_ICON_ICO} ${DUSK_WINDOWS_RC})
|
target_sources(dusklight PRIVATE ${DUSK_WINDOWS_ICON_ICO} ${DUSK_WINDOWS_RC})
|
||||||
set_target_properties(dusk PROPERTIES WIN32_EXECUTABLE TRUE)
|
set_target_properties(dusklight PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_options(dusk PRIVATE /MANIFEST:NO)
|
target_link_options(dusklight PRIVATE /MANIFEST:NO)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -508,10 +518,10 @@ if (APPLE)
|
|||||||
file(GLOB_RECURSE DUSK_RESOURCE_FILES
|
file(GLOB_RECURSE DUSK_RESOURCE_FILES
|
||||||
"${DUSK_RESOURCE_DIR}/Assets.car"
|
"${DUSK_RESOURCE_DIR}/Assets.car"
|
||||||
"${DUSK_RESOURCE_DIR}/Base.lproj/*"
|
"${DUSK_RESOURCE_DIR}/Base.lproj/*"
|
||||||
"${DUSK_RESOURCE_DIR}/Dusk.icns")
|
"${DUSK_RESOURCE_DIR}/Dusklight.icns")
|
||||||
file(GLOB_RECURSE DUSK_APP_RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/*")
|
file(GLOB_RECURSE DUSK_APP_RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/*")
|
||||||
target_sources(dusk PRIVATE ${DUSK_RESOURCE_FILES})
|
target_sources(dusklight PRIVATE ${DUSK_RESOURCE_FILES})
|
||||||
target_sources(dusk PRIVATE ${DUSK_APP_RESOURCE_FILES})
|
target_sources(dusklight PRIVATE ${DUSK_APP_RESOURCE_FILES})
|
||||||
foreach (FILE ${DUSK_RESOURCE_FILES})
|
foreach (FILE ${DUSK_RESOURCE_FILES})
|
||||||
file(RELATIVE_PATH NEW_FILE "${DUSK_RESOURCE_DIR}" ${FILE})
|
file(RELATIVE_PATH NEW_FILE "${DUSK_RESOURCE_DIR}" ${FILE})
|
||||||
get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
|
get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
|
||||||
@@ -523,29 +533,36 @@ if (APPLE)
|
|||||||
set_property(SOURCE ${FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${NEW_FILE_PATH}")
|
set_property(SOURCE ${FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${NEW_FILE_PATH}")
|
||||||
endforeach ()
|
endforeach ()
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
dusk PROPERTIES
|
dusklight PROPERTIES
|
||||||
MACOSX_BUNDLE TRUE
|
MACOSX_BUNDLE TRUE
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME ${DUSK_BUNDLE_NAME}
|
MACOSX_BUNDLE_BUNDLE_NAME ${DUSK_BUNDLE_NAME}
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER ${DUSK_BUNDLE_IDENTIFIER}
|
MACOSX_BUNDLE_GUI_IDENTIFIER ${DUSK_BUNDLE_IDENTIFIER}
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${DUSK_VERSION_STRING}
|
MACOSX_BUNDLE_BUNDLE_VERSION ${DUSK_VERSION_STRING}
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${DUSK_SHORT_VERSION_STRING}
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${DUSK_SHORT_VERSION_STRING}
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${DUSK_INFO_PLIST}
|
MACOSX_BUNDLE_INFO_PLIST ${DUSK_INFO_PLIST}
|
||||||
OUTPUT_NAME Dusk
|
OUTPUT_NAME Dusklight
|
||||||
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES"
|
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES"
|
||||||
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "YES"
|
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "YES"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (APPLE AND NOT IOS AND NOT TVOS)
|
||||||
|
find_library(APPKIT_FRAMEWORK AppKit REQUIRED)
|
||||||
|
target_sources(dusklight PRIVATE src/dusk/file_select_macos.mm)
|
||||||
|
set_source_files_properties(src/dusk/file_select_macos.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
target_link_libraries(dusklight PRIVATE ${APPKIT_FRAMEWORK})
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (IOS)
|
if (IOS)
|
||||||
find_library(UIKIT_FRAMEWORK UIKit REQUIRED)
|
find_library(UIKIT_FRAMEWORK UIKit REQUIRED)
|
||||||
find_library(UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK UniformTypeIdentifiers REQUIRED)
|
find_library(UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK UniformTypeIdentifiers REQUIRED)
|
||||||
target_sources(dusk PRIVATE src/dusk/ios/FileSelectDialog.m)
|
target_sources(dusklight PRIVATE src/dusk/ios/FileSelectDialog.m)
|
||||||
set_source_files_properties(src/dusk/ios/FileSelectDialog.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
set_source_files_properties(src/dusk/ios/FileSelectDialog.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
target_link_libraries(dusk PRIVATE ${UIKIT_FRAMEWORK} ${UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK})
|
target_link_libraries(dusklight PRIVATE ${UIKIT_FRAMEWORK} ${UNIFORM_TYPE_IDENTIFIERS_FRAMEWORK})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
include(extern/aurora/cmake/AuroraCopyRuntimeDLLs.cmake)
|
include(extern/aurora/cmake/AuroraCopyRuntimeDLLs.cmake)
|
||||||
aurora_copy_runtime_dlls(dusk)
|
aurora_copy_runtime_dlls(dusklight)
|
||||||
|
|
||||||
if (DUSK_SELECTED_OPT)
|
if (DUSK_SELECTED_OPT)
|
||||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
@@ -583,13 +600,13 @@ function(get_target_prefix target result_var)
|
|||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
endfunction()
|
endfunction()
|
||||||
list(APPEND BINARY_TARGETS dusk)
|
list(APPEND BINARY_TARGETS dusklight)
|
||||||
set(EXTRA_TARGETS "")
|
set(EXTRA_TARGETS "")
|
||||||
if (TARGET crashpad_handler)
|
if (TARGET crashpad_handler)
|
||||||
list(APPEND EXTRA_TARGETS crashpad_handler)
|
list(APPEND EXTRA_TARGETS crashpad_handler)
|
||||||
endif ()
|
endif ()
|
||||||
install(TARGETS ${BINARY_TARGETS} ${EXTRA_TARGETS} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
install(TARGETS ${BINARY_TARGETS} ${EXTRA_TARGETS} DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||||
aurora_install_runtime_dlls(dusk ${CMAKE_INSTALL_PREFIX})
|
aurora_install_runtime_dlls(dusklight ${CMAKE_INSTALL_PREFIX})
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res DESTINATION ${CMAKE_INSTALL_PREFIX})
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@@ -249,22 +249,11 @@
|
|||||||
"type": "BOOL",
|
"type": "BOOL",
|
||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
"CMAKE_DISABLE_FIND_PACKAGE_BZip2": {
|
"CMAKE_DISABLE_FIND_PACKAGE_PkgConfig": {
|
||||||
"type": "BOOL",
|
"type": "BOOL",
|
||||||
"value": true
|
"value": true
|
||||||
},
|
},
|
||||||
"CMAKE_DISABLE_FIND_PACKAGE_LibLZMA": {
|
"CMAKE_IGNORE_PREFIX_PATH": "/opt/homebrew"
|
||||||
"type": "BOOL",
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
"CMAKE_DISABLE_FIND_PACKAGE_zstd": {
|
|
||||||
"type": "BOOL",
|
|
||||||
"value": true
|
|
||||||
},
|
|
||||||
"CMAKE_DISABLE_FIND_PACKAGE_Freetype": {
|
|
||||||
"type": "BOOL",
|
|
||||||
"value": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"vendor": {
|
"vendor": {
|
||||||
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
"microsoft.com/VisualStudioSettings/CMake/1.0": {
|
||||||
@@ -329,7 +318,11 @@
|
|||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
|
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
|
||||||
"CMAKE_TOOLCHAIN_FILE": "$env{ANDROID_HOME}/ndk/$env{ANDROID_NDK_VERSION}/build/cmake/android.toolchain.cmake",
|
"CMAKE_TOOLCHAIN_FILE": "$env{ANDROID_HOME}/ndk/$env{ANDROID_NDK_VERSION}/build/cmake/android.toolchain.cmake",
|
||||||
"ANDROID_PLATFORM": "android-28"
|
"ANDROID_PLATFORM": "android-28",
|
||||||
|
"BUILD_SHARED_LIBS": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -416,7 +409,7 @@
|
|||||||
},
|
},
|
||||||
"CMAKE_OSX_DEPLOYMENT_TARGET": "11.0",
|
"CMAKE_OSX_DEPLOYMENT_TARGET": "11.0",
|
||||||
"CMAKE_IGNORE_PREFIX_PATH": "/opt/homebrew",
|
"CMAKE_IGNORE_PREFIX_PATH": "/opt/homebrew",
|
||||||
"DUSK_MOVIE_SUPPORT": {
|
"BUILD_SHARED_LIBS": {
|
||||||
"type": "BOOL",
|
"type": "BOOL",
|
||||||
"value": false
|
"value": false
|
||||||
}
|
}
|
||||||
@@ -449,11 +442,7 @@
|
|||||||
],
|
],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
|
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
|
||||||
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
|
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
|
||||||
"DUSK_MOVIE_SUPPORT": {
|
|
||||||
"type": "BOOL",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -463,11 +452,7 @@
|
|||||||
],
|
],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
|
"CMAKE_C_COMPILER_LAUNCHER": "sccache",
|
||||||
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache",
|
"CMAKE_CXX_COMPILER_LAUNCHER": "sccache"
|
||||||
"DUSK_MOVIE_SUPPORT": {
|
|
||||||
"type": "BOOL",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -551,7 +536,7 @@
|
|||||||
"description": "iOS release build with debug info",
|
"description": "iOS release build with debug info",
|
||||||
"displayName": "iOS RelWithDebInfo",
|
"displayName": "iOS RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -560,7 +545,7 @@
|
|||||||
"description": "tvOS release build with debug info",
|
"description": "tvOS release build with debug info",
|
||||||
"displayName": "tvOS RelWithDebInfo",
|
"displayName": "tvOS RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -569,7 +554,7 @@
|
|||||||
"description": "Android arm64-v8a release build with debug info",
|
"description": "Android arm64-v8a release build with debug info",
|
||||||
"displayName": "Android arm64-v8a RelWithDebInfo",
|
"displayName": "Android arm64-v8a RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -578,7 +563,7 @@
|
|||||||
"description": "Android x86_64 release build with debug info",
|
"description": "Android x86_64 release build with debug info",
|
||||||
"displayName": "Android x86_64 RelWithDebInfo",
|
"displayName": "Android x86_64 RelWithDebInfo",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -587,7 +572,7 @@
|
|||||||
"description": "(Internal) Android CI arm64-v8a",
|
"description": "(Internal) Android CI arm64-v8a",
|
||||||
"displayName": "(Internal) Android CI arm64-v8a",
|
"displayName": "(Internal) Android CI arm64-v8a",
|
||||||
"targets": [
|
"targets": [
|
||||||
"dusk"
|
"dusklight"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="res/logo-mascot.png" alt="Logo" width="640">
|
<img src="res/logo.png" alt="Logo" width="640">
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://twilitrealm.dev">Official Website</a>
|
<a href="https://twilitrealm.dev">Official Website</a>
|
||||||
•
|
•
|
||||||
<a href="https://discord.gg/dusktp">Discord</a>
|
<a href="https://discord.gg/6NpMhefCK9">Discord</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
Dusk is a reverse-engineered reimplementation of Twilight Princess.
|
Dusklight is a reverse-engineered reimplementation of Twilight Princess.
|
||||||
|
|
||||||
It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience.
|
It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience.
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Dusk does *not* provide any copyrighted assets. You must provide your own copy of the original game.
|
> Dusklight does *not* provide any copyrighted assets. You must provide your own copy of the original game.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> At a minimum, Dusk requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelyhood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them.
|
||||||
|
|
||||||
### 1. Verify your dump
|
### 1. Verify your dump
|
||||||
|
|
||||||
First, make sure your dump of the game is clean and supported by Dusk. You can do this by checking the SHA-1 hash of your dump against this list of supported versions:
|
First, make sure your dump of the game is clean and supported by Dusklight. You can do this by checking the SHA-1 hash of your dump against this list of supported versions:
|
||||||
|
|
||||||
| Version | SHA-1 hash |
|
| Version | SHA-1 hash |
|
||||||
|--------------| ------------------------------------------ |
|
|--------------| ------------------------------------------ |
|
||||||
@@ -33,12 +33,12 @@ First, make sure your dump of the game is clean and supported by Dusk. You can d
|
|||||||
|
|
||||||
*Support for other versions of the game is planned in the future.
|
*Support for other versions of the game is planned in the future.
|
||||||
|
|
||||||
### 2. Download [Dusk](https://github.com/TwilitRealm/dusk/releases)
|
### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases)
|
||||||
|
|
||||||
### 3. Setup the game
|
### 3. Setup the game
|
||||||
**Windows / macOS / Linux**
|
**Windows / macOS / Linux**
|
||||||
- Extract the .zip file
|
- Extract the .zip file
|
||||||
- Launch Dusk
|
- Launch Dusklight
|
||||||
- Press **Select Disc Image** and provide the path to your supported game dump
|
- Press **Select Disc Image** and provide the path to your supported game dump
|
||||||
- Press **Play**!
|
- Press **Play**!
|
||||||
|
|
||||||
@@ -46,20 +46,20 @@ First, make sure your dump of the game is clean and supported by Dusk. You can d
|
|||||||
- Follow the [iOS setup guide](docs/ios-install-altstore.md)
|
- Follow the [iOS setup guide](docs/ios-install-altstore.md)
|
||||||
|
|
||||||
**Android**
|
**Android**
|
||||||
- Install the Dusk apk
|
- Install the Dusklight APK
|
||||||
- Launch Dusk
|
- Launch Dusklight
|
||||||
- Press **Select Disc Image** and provide the path to your supported game dump
|
- Press **Select Disc Image** and provide the path to your supported game dump
|
||||||
- Press **Play**!
|
- Press **Play**!
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
If you'd like to build Dusk from source, please read the [build instructions](docs/building.md).
|
If you'd like to build Dusklight from source, please read the [build instructions](docs/building.md).
|
||||||
|
|
||||||
Pull requests are welcomed! Note that we do not accept contributions that are primarily AI-generated and will close your PR if we suspect as much. Please also see the [code conventions](docs/code-conventions.md).
|
Pull requests are welcomed! Note that we do not accept contributions that are primarily AI-generated and will close your PR if we suspect as much. Please also see the [code conventions](docs/code-conventions.md).
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors).
|
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusklight/graphs/contributors).
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ for install_path in build/install/*; do
|
|||||||
cp -r "$install_path" build/appdir/usr/bin
|
cp -r "$install_path" build/appdir/usr/bin
|
||||||
done
|
done
|
||||||
cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor
|
cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor
|
||||||
cp platforms/freedesktop/dusk.desktop build/appdir/usr/share/applications
|
cp platforms/freedesktop/dusklight.desktop build/appdir/usr/share/applications
|
||||||
|
|
||||||
cd build/install
|
cd build/install
|
||||||
VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \
|
VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
sudo dnf groupinstall "Development Tools" "Development Libraries"
|
sudo dnf groupinstall "Development Tools" "Development Libraries"
|
||||||
```
|
```
|
||||||
#### Setup
|
#### Setup
|
||||||
Clone and initialize the Dusk repository
|
Clone and initialize the Dusklight repository
|
||||||
```sh
|
```sh
|
||||||
git clone --recursive https://github.com/TwilitRealm/dusk.git
|
git clone --recursive https://github.com/TwilitRealm/dusklight.git
|
||||||
cd dusk
|
cd dusklight
|
||||||
git pull
|
git pull
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
```
|
```
|
||||||
@@ -93,6 +93,6 @@ Alternate presets available:
|
|||||||
#### Running
|
#### Running
|
||||||
Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
|
Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ, WIA, WBFS, CISO, GCZ
|
||||||
```sh
|
```sh
|
||||||
build/{preset}/dusk /path/to/game.rvz
|
build/{preset}/dusklight/path/to/game.rvz
|
||||||
```
|
```
|
||||||
If no path is specified, Dusk defaults to `game.iso` in the current working directory.
|
If no path is specified, Dusklight defaults to `game.iso` in the current working directory.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Installing Dusk on iOS via AltStore
|
# Installing Dusklight on iOS via AltStore
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Mac with Homebrew installed
|
- Mac with Homebrew installed
|
||||||
- iPhone connected via USB
|
- iPhone connected via USB
|
||||||
- Dusk IPA file (download the latest `Dusk-vX.X.X-ios-arm64.ipa` from the [releases page](https://github.com/TwilitRealm/dusk/releases))
|
- Dusklight IPA file (download the latest `Dusklight-vX.X.X-ios-arm64.ipa` from the [releases page](https://github.com/TwilitRealm/dusk/releases))
|
||||||
- Game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL)
|
- Game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL)
|
||||||
|
|
||||||
## 1. Install AltServer
|
## 1. Install AltServer
|
||||||
|
|||||||
@@ -1411,6 +1411,7 @@ set(DOLPHIN_FILES
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(DUSK_FILES
|
set(DUSK_FILES
|
||||||
|
include/dusk/action_bindings.h
|
||||||
include/dusk/endian_gx.hpp
|
include/dusk/endian_gx.hpp
|
||||||
include/dusk/config.hpp
|
include/dusk/config.hpp
|
||||||
include/dusk/dvd_asset.hpp
|
include/dusk/dvd_asset.hpp
|
||||||
@@ -1420,6 +1421,8 @@ set(DUSK_FILES
|
|||||||
src/dusk/asserts.cpp
|
src/dusk/asserts.cpp
|
||||||
src/dusk/config.cpp
|
src/dusk/config.cpp
|
||||||
src/dusk/crash_reporting.cpp
|
src/dusk/crash_reporting.cpp
|
||||||
|
src/dusk/data.cpp
|
||||||
|
src/dusk/data.hpp
|
||||||
src/dusk/endian.cpp
|
src/dusk/endian.cpp
|
||||||
src/dusk/extras.c
|
src/dusk/extras.c
|
||||||
src/dusk/file_select.cpp
|
src/dusk/file_select.cpp
|
||||||
@@ -1435,6 +1438,7 @@ set(DUSK_FILES
|
|||||||
src/dusk/layout.cpp
|
src/dusk/layout.cpp
|
||||||
src/dusk/logging.cpp
|
src/dusk/logging.cpp
|
||||||
src/dusk/settings.cpp
|
src/dusk/settings.cpp
|
||||||
|
src/dusk/speedrun.cpp
|
||||||
src/dusk/stubs.cpp
|
src/dusk/stubs.cpp
|
||||||
src/dusk/update_check.cpp
|
src/dusk/update_check.cpp
|
||||||
src/dusk/update_check.hpp
|
src/dusk/update_check.hpp
|
||||||
@@ -1444,8 +1448,6 @@ set(DUSK_FILES
|
|||||||
src/dusk/imgui/ImGuiConsole.cpp
|
src/dusk/imgui/ImGuiConsole.cpp
|
||||||
src/dusk/imgui/ImGuiEngine.cpp
|
src/dusk/imgui/ImGuiEngine.cpp
|
||||||
src/dusk/imgui/ImGuiEngine.hpp
|
src/dusk/imgui/ImGuiEngine.hpp
|
||||||
src/dusk/imgui/ImGuiMenuGame.cpp
|
|
||||||
src/dusk/imgui/ImGuiMenuGame.hpp
|
|
||||||
src/dusk/imgui/ImGuiBloomWindow.cpp
|
src/dusk/imgui/ImGuiBloomWindow.cpp
|
||||||
src/dusk/imgui/ImGuiBloomWindow.hpp
|
src/dusk/imgui/ImGuiBloomWindow.hpp
|
||||||
src/dusk/imgui/ImGuiMenuTools.cpp
|
src/dusk/imgui/ImGuiMenuTools.cpp
|
||||||
@@ -1456,7 +1458,6 @@ set(DUSK_FILES
|
|||||||
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
||||||
src/dusk/imgui/ImGuiControllerOverlay.cpp
|
src/dusk/imgui/ImGuiControllerOverlay.cpp
|
||||||
src/dusk/imgui/ImGuiStubLog.cpp
|
src/dusk/imgui/ImGuiStubLog.cpp
|
||||||
src/dusk/imgui/ImGuiMapLoader.cpp
|
|
||||||
src/dusk/imgui/ImGuiSaveEditor.cpp
|
src/dusk/imgui/ImGuiSaveEditor.cpp
|
||||||
src/dusk/imgui/ImGuiStateShare.hpp
|
src/dusk/imgui/ImGuiStateShare.hpp
|
||||||
src/dusk/imgui/ImGuiStateShare.cpp
|
src/dusk/imgui/ImGuiStateShare.cpp
|
||||||
@@ -1507,6 +1508,8 @@ set(DUSK_FILES
|
|||||||
src/dusk/ui/tab_bar.hpp
|
src/dusk/ui/tab_bar.hpp
|
||||||
src/dusk/ui/ui.cpp
|
src/dusk/ui/ui.cpp
|
||||||
src/dusk/ui/ui.hpp
|
src/dusk/ui/ui.hpp
|
||||||
|
src/dusk/ui/warp.cpp
|
||||||
|
src/dusk/ui/warp.hpp
|
||||||
src/dusk/ui/window.cpp
|
src/dusk/ui/window.cpp
|
||||||
src/dusk/ui/window.hpp
|
src/dusk/ui/window.hpp
|
||||||
src/dusk/achievements.cpp
|
src/dusk/achievements.cpp
|
||||||
@@ -1521,6 +1524,7 @@ set(DUSK_FILES
|
|||||||
src/dusk/discord.hpp
|
src/dusk/discord.hpp
|
||||||
src/dusk/discord_presence.cpp
|
src/dusk/discord_presence.cpp
|
||||||
src/dusk/version.cpp
|
src/dusk/version.cpp
|
||||||
|
src/dusk/action_bindings.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DUSK_HTTP_BACKEND_FILES
|
set(DUSK_HTTP_BACKEND_FILES
|
||||||
|
|||||||
@@ -4,101 +4,225 @@
|
|||||||
};
|
};
|
||||||
outputs = { self, nixpkgs }:
|
outputs = { self, nixpkgs }:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
supportedSystems = [
|
||||||
|
"x86_64-linux"
|
||||||
# Dependencies that are not packaged in nixpkgs:
|
"aarch64-linux"
|
||||||
aurora-src = pkgs.fetchFromGitHub {
|
"x86_64-darwin"
|
||||||
owner = "encounter";
|
"aarch64-darwin"
|
||||||
repo = "aurora";
|
];
|
||||||
rev = "63606a43265a3bc18dafd500ab4d7a2108f109e6";
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
hash = "sha256-xBvnAwGwNzav67Ac6oUz7RqDUwqgL2bsME3OOMn8Tqw=";
|
pkgsFor = system: import nixpkgs { inherit system; };
|
||||||
};
|
|
||||||
dawn-src = pkgs.fetchzip {
|
# Dependencies that are not packaged in nixpkgs (used by the Linux package build):
|
||||||
url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz";
|
buildSources = pkgs: {
|
||||||
hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs=";
|
aurora-src = pkgs.fetchFromGitHub {
|
||||||
stripRoot = false;
|
owner = "encounter";
|
||||||
};
|
repo = "aurora";
|
||||||
nod-src = pkgs.fetchzip {
|
rev = "63606a43265a3bc18dafd500ab4d7a2108f109e6";
|
||||||
url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz";
|
hash = "sha256-xBvnAwGwNzav67Ac6oUz7RqDUwqgL2bsME3OOMn8Tqw=";
|
||||||
hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE=";
|
};
|
||||||
stripRoot = false;
|
dawn-src = pkgs.fetchzip {
|
||||||
};
|
url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz";
|
||||||
# The version of imgui on nixpkgs does not map cleanly.
|
hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs=";
|
||||||
imgui-src = pkgs.fetchFromGitHub {
|
stripRoot = false;
|
||||||
owner = "ocornut";
|
};
|
||||||
repo = "imgui";
|
nod-src = pkgs.fetchzip {
|
||||||
rev = "v1.91.9b-docking";
|
url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz";
|
||||||
hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ=";
|
hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE=";
|
||||||
};
|
stripRoot = false;
|
||||||
sqlite-src = pkgs.fetchzip {
|
};
|
||||||
url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip";
|
# The version of imgui on nixpkgs does not map cleanly.
|
||||||
hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM=";
|
imgui-src = pkgs.fetchFromGitHub {
|
||||||
};
|
owner = "ocornut";
|
||||||
rmlui-src = pkgs.fetchzip {
|
repo = "imgui";
|
||||||
url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz";
|
rev = "v1.91.9b-docking";
|
||||||
hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg=";
|
hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ=";
|
||||||
};
|
};
|
||||||
# Dusk Actual
|
sqlite-src = pkgs.fetchzip {
|
||||||
dusk = pkgs.stdenv.mkDerivation {
|
url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip";
|
||||||
name = "dusk";
|
hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM=";
|
||||||
src = ./.;
|
};
|
||||||
postUnpack = ''
|
rmlui-src = pkgs.fetchzip {
|
||||||
mkdir -p $sourceRoot/extern/aurora
|
url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz";
|
||||||
cp -r ${aurora-src}/. $sourceRoot/extern/aurora/
|
hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg=";
|
||||||
chmod -R u+w $sourceRoot/extern/aurora
|
};
|
||||||
sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt
|
|
||||||
'';
|
|
||||||
# Remove last line to re-enable tests
|
|
||||||
cmakeFlags = [
|
|
||||||
"-DFETCHCONTENT_FULLY_DISCONNECTED=ON"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${dawn-src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}"
|
|
||||||
"-DAURORA_SDL3_PROVIDER=system"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${nod-src}"
|
|
||||||
"-DAURORA_NOD_PROVIDER=package"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${sqlite-src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_IMGUI=${imgui-src}"
|
|
||||||
"-DFETCHCONTENT_SOURCE_DIR_RMLUI=${rmlui-src}"
|
|
||||||
"-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it.
|
|
||||||
];
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
cp dusk $out/bin/dusk
|
|
||||||
cp -r ./res $out/bin/res
|
|
||||||
'';
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.cmake
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.wayland
|
|
||||||
];
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.libGL
|
|
||||||
pkgs.libX11
|
|
||||||
pkgs.libXcursor
|
|
||||||
pkgs.libxi
|
|
||||||
pkgs.libxcb
|
|
||||||
pkgs.libxrandr
|
|
||||||
pkgs.libxscrnsaver
|
|
||||||
pkgs.libxtst
|
|
||||||
pkgs.libjpeg8
|
|
||||||
pkgs.libxkbcommon
|
|
||||||
pkgs.libglvnd
|
|
||||||
pkgs.cxxopts
|
|
||||||
pkgs.abseil-cpp
|
|
||||||
pkgs.sdl3
|
|
||||||
pkgs.fmt
|
|
||||||
pkgs.tracy
|
|
||||||
pkgs.freetype
|
|
||||||
pkgs.zstd
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Dusklight Actual (Linux x86_64 only — relies on prebuilt dawn/nod binaries)
|
||||||
|
mkDusklight = pkgs:
|
||||||
|
let srcs = buildSources pkgs;
|
||||||
|
versionSuffix = if self ? shortRev && self.shortRev != null
|
||||||
|
then "nix-${self.shortRev}"
|
||||||
|
else "nix-dirty";
|
||||||
|
in
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "dusklight";
|
||||||
|
src = ./.;
|
||||||
|
postUnpack = ''
|
||||||
|
mkdir -p $sourceRoot/extern/aurora
|
||||||
|
cp -r ${srcs.aurora-src}/. $sourceRoot/extern/aurora/
|
||||||
|
chmod -R u+w $sourceRoot/extern/aurora
|
||||||
|
sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt
|
||||||
|
'';
|
||||||
|
# Remove last line to re-enable tests
|
||||||
|
cmakeFlags = [
|
||||||
|
"-DDUSK_VERSION_OVERRIDE=${versionSuffix}"
|
||||||
|
"-DFETCHCONTENT_FULLY_DISCONNECTED=ON"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${srcs.dawn-src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}"
|
||||||
|
"-DAURORA_SDL3_PROVIDER=system"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${srcs.nod-src}"
|
||||||
|
"-DAURORA_NOD_PROVIDER=package"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${srcs.sqlite-src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_IMGUI=${srcs.imgui-src}"
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_RMLUI=${srcs.rmlui-src}"
|
||||||
|
"-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it.
|
||||||
|
];
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp dusklight $out/bin/dusklight
|
||||||
|
cp -r ./res $out/bin/res
|
||||||
|
|
||||||
|
mkdir -p $out/share/applications
|
||||||
|
cp $src/platforms/freedesktop/dusklight.desktop $out/share/applications/dusklight.desktop
|
||||||
|
|
||||||
|
for size in 16 32 48 64 128 256 512 1024; do
|
||||||
|
install -Dm644 $src/platforms/freedesktop/''${size}x''${size}/apps/dusklight.png \
|
||||||
|
$out/share/icons/hicolor/''${size}x''${size}/apps/dusklight.png
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.cmake
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.wayland
|
||||||
|
];
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.libGL
|
||||||
|
pkgs.libX11
|
||||||
|
pkgs.libXcursor
|
||||||
|
pkgs.libxi
|
||||||
|
pkgs.libxcb
|
||||||
|
pkgs.libxrandr
|
||||||
|
pkgs.libxscrnsaver
|
||||||
|
pkgs.libxtst
|
||||||
|
pkgs.libjpeg8
|
||||||
|
pkgs.libxkbcommon
|
||||||
|
pkgs.libglvnd
|
||||||
|
pkgs.cxxopts
|
||||||
|
pkgs.abseil-cpp
|
||||||
|
pkgs.sdl3
|
||||||
|
pkgs.fmt
|
||||||
|
pkgs.tracy
|
||||||
|
pkgs.freetype
|
||||||
|
pkgs.zstd
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Tooling common to every supported host (Linux and macOS).
|
||||||
|
commonDevTools = pkgs: [
|
||||||
|
pkgs.cmake
|
||||||
|
pkgs.ninja
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.git
|
||||||
|
pkgs.python3
|
||||||
|
pkgs.python3Packages.markupsafe
|
||||||
|
pkgs.rustc
|
||||||
|
pkgs.cargo
|
||||||
|
pkgs.sccache
|
||||||
|
];
|
||||||
|
|
||||||
|
# Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml
|
||||||
|
# so the cmake presets resolve the same set of headers as CI.
|
||||||
|
linuxDevDeps = pkgs: [
|
||||||
|
# Compilers / linkers
|
||||||
|
pkgs.clang
|
||||||
|
pkgs.lld
|
||||||
|
# C/C++ utilities
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.openssl
|
||||||
|
pkgs.zlib
|
||||||
|
pkgs.libpng
|
||||||
|
pkgs.libjpeg_turbo
|
||||||
|
pkgs.freetype
|
||||||
|
pkgs.zstd
|
||||||
|
pkgs.fmt
|
||||||
|
pkgs.tracy
|
||||||
|
pkgs.cxxopts
|
||||||
|
pkgs.abseil-cpp
|
||||||
|
pkgs.sdl3
|
||||||
|
pkgs.ncurses
|
||||||
|
pkgs.libunwind
|
||||||
|
pkgs.libusb1
|
||||||
|
pkgs.fuse
|
||||||
|
# Wayland / display server
|
||||||
|
pkgs.wayland
|
||||||
|
pkgs.wayland-protocols
|
||||||
|
pkgs.libxkbcommon
|
||||||
|
pkgs.libdecor
|
||||||
|
# OpenGL / Vulkan
|
||||||
|
pkgs.libGL
|
||||||
|
pkgs.libGLU
|
||||||
|
pkgs.libglvnd
|
||||||
|
pkgs.vulkan-headers
|
||||||
|
pkgs.vulkan-loader
|
||||||
|
# X11
|
||||||
|
pkgs.libX11
|
||||||
|
pkgs.libxcb
|
||||||
|
pkgs.libXcursor
|
||||||
|
pkgs.libxi
|
||||||
|
pkgs.libxrandr
|
||||||
|
pkgs.libxscrnsaver
|
||||||
|
pkgs.libxtst
|
||||||
|
pkgs.libxinerama
|
||||||
|
# Audio
|
||||||
|
pkgs.alsa-lib
|
||||||
|
pkgs.libpulseaudio
|
||||||
|
pkgs.pipewire
|
||||||
|
# System integration
|
||||||
|
pkgs.dbus
|
||||||
|
pkgs.udev
|
||||||
|
pkgs.gtk3
|
||||||
|
];
|
||||||
|
|
||||||
|
# On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up
|
||||||
|
# Apple Clang and the Xcode SDK directly, matching the macOS CI workflow.
|
||||||
|
mkDarwinShell = pkgs:
|
||||||
|
pkgs.mkShellNoCC {
|
||||||
|
packages = commonDevTools pkgs;
|
||||||
|
shellHook = ''
|
||||||
|
echo "Dusklight dev shell (macOS)"
|
||||||
|
echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK."
|
||||||
|
echo "Configure: cmake --preset macos-default-relwithdebinfo"
|
||||||
|
echo "Build: cmake --build --preset macos-default-relwithdebinfo"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mkLinuxShell = pkgs:
|
||||||
|
pkgs.mkShell {
|
||||||
|
packages = (commonDevTools pkgs) ++ (linuxDevDeps pkgs);
|
||||||
|
shellHook = ''
|
||||||
|
echo "Dusklight dev shell (Linux)"
|
||||||
|
echo "Configure: cmake --preset linux-default-relwithdebinfo"
|
||||||
|
echo " cmake --preset linux-clang-relwithdebinfo"
|
||||||
|
echo "Build: cmake --build --preset <preset>"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mkDevShell = pkgs:
|
||||||
|
if pkgs.stdenv.isDarwin
|
||||||
|
then mkDarwinShell pkgs
|
||||||
|
else mkLinuxShell pkgs;
|
||||||
in {
|
in {
|
||||||
packages.x86_64-linux.default = dusk;
|
packages.x86_64-linux.default = mkDusklight (pkgsFor "x86_64-linux");
|
||||||
|
|
||||||
|
devShells = forAllSystems (system: {
|
||||||
|
default = mkDevShell (pkgsFor system);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,11 @@ public:
|
|||||||
/* 0x108 */ int mSkipTimer;
|
/* 0x108 */ int mSkipTimer;
|
||||||
/* 0x10C */ int mSkipParameter;
|
/* 0x10C */ int mSkipParameter;
|
||||||
/* 0x110 */ BOOL mIsSkipFade;
|
/* 0x110 */ BOOL mIsSkipFade;
|
||||||
|
#if TARGET_PC
|
||||||
|
/* 0x114 */ char mSkipEventName[21];
|
||||||
|
#else
|
||||||
/* 0x114 */ char mSkipEventName[20];
|
/* 0x114 */ char mSkipEventName[20];
|
||||||
|
#endif
|
||||||
/* 0x128 */ u8 mCompulsory;
|
/* 0x128 */ u8 mCompulsory;
|
||||||
/* 0x129 */ bool mRoomInfoSet;
|
/* 0x129 */ bool mRoomInfoSet;
|
||||||
/* 0x12C */ int mRoomNo;
|
/* 0x12C */ int mRoomNo;
|
||||||
|
|||||||
@@ -223,6 +223,9 @@ private:
|
|||||||
/* 0x8F */ u8 field_0x8f;
|
/* 0x8F */ u8 field_0x8f;
|
||||||
/* 0x90 */ u8 field_0x90;
|
/* 0x90 */ u8 field_0x90;
|
||||||
/* 0x91 */ u8 field_0x91;
|
/* 0x91 */ u8 field_0x91;
|
||||||
|
#if TARGET_PC
|
||||||
|
bool previousMirror;
|
||||||
|
#endif
|
||||||
}; // Size: 0x94
|
}; // Size: 0x94
|
||||||
|
|
||||||
class dMap_HIO_list_c : public dMpath_HIO_n::hioList_c {
|
class dMap_HIO_list_c : public dMpath_HIO_n::hioList_c {
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "dusk/config_var.hpp"
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
|
||||||
|
enum class ActionBinds {
|
||||||
|
FIRST_PERSON_CAMERA,
|
||||||
|
CALL_MIDNA,
|
||||||
|
OPEN_DUSKLIGHT_MENU,
|
||||||
|
TURBO_SPEED_BUTTON,
|
||||||
|
COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActionBindData {
|
||||||
|
std::array<config::ActionBindConfigVar, 4>* configVars{};
|
||||||
|
std::string actionName{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActionBindPressData {
|
||||||
|
bool pressedCurFrame{false};
|
||||||
|
bool pressedPrevFrame{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
using ActionBindsMap = std::unordered_map<ActionBinds, ActionBindData>;
|
||||||
|
|
||||||
|
ActionBindsMap& getActionBinds();
|
||||||
|
|
||||||
|
bool isActionBound(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
void updateActionBindings();
|
||||||
|
|
||||||
|
bool getActionBindTrig(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
bool getActionBindHold(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
bool getActionBindHoldAnyPort(ActionBinds action);
|
||||||
|
|
||||||
|
int getActionBindButton(ActionBinds action, u32 port);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,12 @@ namespace dusk {
|
|||||||
*
|
*
|
||||||
* This gets used for file paths and such, and cannot be changed!
|
* This gets used for file paths and such, and cannot be changed!
|
||||||
*/
|
*/
|
||||||
constexpr auto AppName = "Dusk";
|
constexpr auto AppName = "Dusklight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous AppName to migrate data from.
|
||||||
|
*/
|
||||||
|
constexpr auto LegacyAppName = "Dusk";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief The internal organization name for the game.
|
* \brief The internal organization name for the game.
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <dolphin/types.h>
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
namespace dusk::audio {
|
namespace dusk::audio {
|
||||||
|
|
||||||
|
// Converts a 0-1 volume to a linear amplitude multiplier.
|
||||||
|
// The curve is -4 dB per 10% step: 100% = 0 dB, 90% = -4 dB, ..., 0% = -inf dB
|
||||||
|
inline f32 MasterVolumeToLinear(f32 v) {
|
||||||
|
if (v <= 0.0f) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
return std::pow(10.0f, (v - 1.0f) * 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the audio system and start playing audio.
|
* Initialize the audio system and start playing audio.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ void enterAutoSave();
|
|||||||
void autoSaving();
|
void autoSaving();
|
||||||
void waitingForWrite();
|
void waitingForWrite();
|
||||||
void endAutoSave();
|
void endAutoSave();
|
||||||
|
void toggleAutoSave(bool enabled);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef DUSK_CONFIG_HPP
|
#ifndef DUSK_CONFIG_HPP
|
||||||
#define DUSK_CONFIG_HPP
|
#define DUSK_CONFIG_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
#include "config_var.hpp"
|
#include "config_var.hpp"
|
||||||
@@ -111,6 +112,18 @@ void Save();
|
|||||||
*/
|
*/
|
||||||
ConfigVarBase* GetConfigVar(std::string_view name);
|
ConfigVarBase* GetConfigVar(std::string_view name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resets all custom action bindings for a specific port to nothing
|
||||||
|
*
|
||||||
|
* @param port The port to be cleared of action bindings
|
||||||
|
*/
|
||||||
|
void ClearAllActionBindings(int port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Call a function on every registered CVar.
|
||||||
|
*/
|
||||||
|
void EnumerateRegistered(std::function<void(ConfigVarBase&)> callback);
|
||||||
|
|
||||||
template <ConfigValue T>
|
template <ConfigValue T>
|
||||||
const ConfigImplBase* GetConfigImpl() {
|
const ConfigImplBase* GetConfigImpl() {
|
||||||
static ConfigImpl<T> config;
|
static ConfigImpl<T> config;
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ enum class ConfigVarLayer : u8 {
|
|||||||
* Will not get saved to config.
|
* Will not get saved to config.
|
||||||
*/
|
*/
|
||||||
Override,
|
Override,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CVar is temporarily overridden by speedrun mode.
|
||||||
|
* Will not get saved to config. Cleared when speedrun mode is disabled.
|
||||||
|
* Lower priority than Override, so launch args still win.
|
||||||
|
*/
|
||||||
|
Speedrun,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigImplBase;
|
class ConfigImplBase;
|
||||||
@@ -113,6 +120,12 @@ public:
|
|||||||
* This is necessary to make it legal to access.
|
* This is necessary to make it legal to access.
|
||||||
*/
|
*/
|
||||||
void markRegistered();
|
void markRegistered();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a speedrun-mode override if one is active on this CVar.
|
||||||
|
* Safe to call on any CVar, no-op if not at the Speedrun layer.
|
||||||
|
*/
|
||||||
|
virtual void clearSpeedrunOverride() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -162,6 +175,7 @@ class ConfigVar : public ConfigVarBase {
|
|||||||
T defaultValue;
|
T defaultValue;
|
||||||
T value;
|
T value;
|
||||||
T overrideValue;
|
T overrideValue;
|
||||||
|
ConfigVarLayer priorLayer = ConfigVarLayer::Default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -189,6 +203,7 @@ public:
|
|||||||
case ConfigVarLayer::Value:
|
case ConfigVarLayer::Value:
|
||||||
return value;
|
return value;
|
||||||
case ConfigVarLayer::Override:
|
case ConfigVarLayer::Override:
|
||||||
|
case ConfigVarLayer::Speedrun:
|
||||||
return overrideValue;
|
return overrideValue;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
@@ -239,8 +254,54 @@ public:
|
|||||||
overrideValue = std::move(newValue);
|
overrideValue = std::move(newValue);
|
||||||
layer = ConfigVarLayer::Override;
|
layer = ConfigVarLayer::Override;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Give a CVar a speedrun-mode override value.
|
||||||
|
*
|
||||||
|
* Lower priority than a launch-arg override. Cleared when speedrun mode is disabled.
|
||||||
|
* The overridden value will not get saved to config.
|
||||||
|
*
|
||||||
|
* @param newValue The new value the CVar will get.
|
||||||
|
*/
|
||||||
|
void setSpeedrunValue(T newValue) {
|
||||||
|
checkRegistered();
|
||||||
|
if (layer != ConfigVarLayer::Override) {
|
||||||
|
priorLayer = layer;
|
||||||
|
overrideValue = std::move(newValue);
|
||||||
|
layer = ConfigVarLayer::Speedrun;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearOverride() {
|
||||||
|
checkRegistered();
|
||||||
|
if (layer == ConfigVarLayer::Override) {
|
||||||
|
overrideValue = {};
|
||||||
|
layer = ConfigVarLayer::Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearSpeedrunOverride() override {
|
||||||
|
checkRegistered();
|
||||||
|
if (layer == ConfigVarLayer::Speedrun) {
|
||||||
|
overrideValue = {};
|
||||||
|
layer = priorLayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the user-persisted value, ignoring any temporary overrides.
|
||||||
|
*
|
||||||
|
* Used by Save() to write the correct value even when a speedrun override is active.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr const T& getValueForSave() const noexcept {
|
||||||
|
checkRegistered();
|
||||||
|
const ConfigVarLayer effectiveLayer = (layer == ConfigVarLayer::Speedrun) ? priorLayer : layer;
|
||||||
|
return effectiveLayer == ConfigVarLayer::Default ? defaultValue : value;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ActionBindConfigVar = ConfigVar<int>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DUSK_CONFIG_VAR_HPP
|
#endif // DUSK_CONFIG_VAR_HPP
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ constexpr const char* SHOW_DEBUG_OVERLAY = "F3";
|
|||||||
constexpr const char* SHOW_HEAP_VIEWER = "F4";
|
constexpr const char* SHOW_HEAP_VIEWER = "F4";
|
||||||
constexpr const char* SHOW_PLAYER_INFO = "F5";
|
constexpr const char* SHOW_PLAYER_INFO = "F5";
|
||||||
constexpr const char* SHOW_SAVE_EDITOR = "F6";
|
constexpr const char* SHOW_SAVE_EDITOR = "F6";
|
||||||
constexpr const char* SHOW_MAP_LOADER = "F7";
|
|
||||||
constexpr const char* SHOW_STATE_SHARE = "F8";
|
constexpr const char* SHOW_STATE_SHARE = "F8";
|
||||||
constexpr const char* SHOW_DEBUG_CAMERA = "F9";
|
constexpr const char* SHOW_DEBUG_CAMERA = "F9";
|
||||||
constexpr const char* SHOW_AUDIO_DEBUG = "F10";
|
constexpr const char* SHOW_AUDIO_DEBUG = "F10";
|
||||||
|
|||||||
@@ -1,36 +1,26 @@
|
|||||||
#ifndef DUSK_MAIN_H
|
#ifndef DUSK_MAIN_H
|
||||||
#define DUSK_MAIN_H
|
#define DUSK_MAIN_H
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#include <TargetConditionals.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#if defined(_WIN32) || \
|
|
||||||
(defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \
|
|
||||||
(defined(__linux__) && !defined(__ANDROID__))
|
|
||||||
#define DUSK_CAN_OPEN_DATA_FOLDER 1
|
|
||||||
#else
|
|
||||||
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
extern bool IsRunning;
|
|
||||||
extern bool IsShuttingDown;
|
|
||||||
extern bool IsGameLaunched;
|
|
||||||
extern bool RestartRequested;
|
|
||||||
extern std::filesystem::path ConfigPath;
|
|
||||||
|
|
||||||
#if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \
|
extern bool IsRunning;
|
||||||
|
extern bool IsShuttingDown;
|
||||||
|
extern bool IsGameLaunched;
|
||||||
|
extern bool RestartRequested;
|
||||||
|
extern std::filesystem::path ConfigPath;
|
||||||
|
extern std::filesystem::path CachePath;
|
||||||
|
|
||||||
|
#if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \
|
||||||
(defined(TARGET_OS_TV) && TARGET_OS_TV)
|
(defined(TARGET_OS_TV) && TARGET_OS_TV)
|
||||||
inline constexpr bool SupportsProcessRestart = false;
|
inline constexpr bool SupportsProcessRestart = false;
|
||||||
#else
|
#else
|
||||||
inline constexpr bool SupportsProcessRestart = true;
|
inline constexpr bool SupportsProcessRestart = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void RequestRestart() noexcept;
|
void RequestRestart() noexcept;
|
||||||
bool OpenDataFolder();
|
|
||||||
}
|
} // namespace dusk
|
||||||
|
|
||||||
#endif // DUSK_MAIN_H
|
#endif // DUSK_MAIN_H
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef DUSK_CONFIG_H
|
#ifndef DUSK_CONFIG_H
|
||||||
#define DUSK_CONFIG_H
|
#define DUSK_CONFIG_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "dusk/config_var.hpp"
|
#include "dusk/config_var.hpp"
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
@@ -115,6 +117,7 @@ struct UserSettings {
|
|||||||
ConfigVar<bool> enableLinkDollRotation;
|
ConfigVar<bool> enableLinkDollRotation;
|
||||||
ConfigVar<bool> enableAchievementToasts;
|
ConfigVar<bool> enableAchievementToasts;
|
||||||
ConfigVar<bool> enableControllerToasts;
|
ConfigVar<bool> enableControllerToasts;
|
||||||
|
ConfigVar<bool> enableDiscordPresence;
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
ConfigVar<BloomMode> bloomMode;
|
ConfigVar<BloomMode> bloomMode;
|
||||||
@@ -125,6 +128,7 @@ struct UserSettings {
|
|||||||
ConfigVar<int> shadowResolutionMultiplier;
|
ConfigVar<int> shadowResolutionMultiplier;
|
||||||
ConfigVar<bool> enableDepthOfField;
|
ConfigVar<bool> enableDepthOfField;
|
||||||
ConfigVar<bool> enableMapBackground;
|
ConfigVar<bool> enableMapBackground;
|
||||||
|
ConfigVar<bool> disableCutscenePillarboxing;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
ConfigVar<bool> noLowHpSound;
|
ConfigVar<bool> noLowHpSound;
|
||||||
@@ -154,6 +158,7 @@ struct UserSettings {
|
|||||||
// Cheats
|
// Cheats
|
||||||
ConfigVar<bool> infiniteHearts;
|
ConfigVar<bool> infiniteHearts;
|
||||||
ConfigVar<bool> infiniteArrows;
|
ConfigVar<bool> infiniteArrows;
|
||||||
|
ConfigVar<bool> infiniteSeeds;
|
||||||
ConfigVar<bool> infiniteBombs;
|
ConfigVar<bool> infiniteBombs;
|
||||||
ConfigVar<bool> infiniteOil;
|
ConfigVar<bool> infiniteOil;
|
||||||
ConfigVar<bool> infiniteOxygen;
|
ConfigVar<bool> infiniteOxygen;
|
||||||
@@ -164,19 +169,27 @@ struct UserSettings {
|
|||||||
ConfigVar<bool> alwaysGreatspin;
|
ConfigVar<bool> alwaysGreatspin;
|
||||||
ConfigVar<bool> enableFastIronBoots;
|
ConfigVar<bool> enableFastIronBoots;
|
||||||
ConfigVar<bool> canTransformAnywhere;
|
ConfigVar<bool> canTransformAnywhere;
|
||||||
|
ConfigVar<bool> fastRoll;
|
||||||
ConfigVar<bool> fastSpinner;
|
ConfigVar<bool> fastSpinner;
|
||||||
ConfigVar<bool> freeMagicArmor;
|
ConfigVar<bool> magicArmorNoDrain;
|
||||||
|
ConfigVar<bool> magicArmorNoDamageLoss;
|
||||||
|
ConfigVar<bool> magicArmorNoHeavy;
|
||||||
|
ConfigVar<bool> invincibleEnemies;
|
||||||
|
|
||||||
// Technical
|
// Technical
|
||||||
ConfigVar<bool> restoreWiiGlitches;
|
ConfigVar<bool> restoreWiiGlitches;
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
ConfigVar<bool> enableTurboKeybind;
|
ConfigVar<bool> enableTurboKeybind;
|
||||||
|
ConfigVar<bool> enableResetKeybind;
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
ConfigVar<bool> speedrunMode;
|
ConfigVar<bool> speedrunMode;
|
||||||
ConfigVar<bool> liveSplitEnabled;
|
ConfigVar<bool> liveSplitEnabled;
|
||||||
|
ConfigVar<bool> showSpeedrunRTATimer;
|
||||||
ConfigVar<bool> recordingMode;
|
ConfigVar<bool> recordingMode;
|
||||||
|
ConfigVar<bool> showInputViewer;
|
||||||
|
ConfigVar<bool> showInputViewerGyro;
|
||||||
} game;
|
} game;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -190,6 +203,14 @@ struct UserSettings {
|
|||||||
ConfigVar<int> cardFileType;
|
ConfigVar<int> cardFileType;
|
||||||
ConfigVar<bool> enableAdvancedSettings;
|
ConfigVar<bool> enableAdvancedSettings;
|
||||||
} backend;
|
} backend;
|
||||||
|
|
||||||
|
// Arrays of size 4 for 4 ports
|
||||||
|
struct {
|
||||||
|
std::array<ActionBindConfigVar, 4> firstPersonCamera;
|
||||||
|
std::array<ActionBindConfigVar, 4> callMidna;
|
||||||
|
std::array<ActionBindConfigVar, 4> openDusklightMenu;
|
||||||
|
std::array<ActionBindConfigVar, 4> turboSpeedButton;
|
||||||
|
} actionBindings;
|
||||||
};
|
};
|
||||||
|
|
||||||
UserSettings& getSettings();
|
UserSettings& getSettings();
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <aurora/aurora.h>
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
|
||||||
|
struct SpeedrunInfo {
|
||||||
|
void startRun() {
|
||||||
|
m_isRunStarted = true;
|
||||||
|
m_startTimestamp = OSGetTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopRun() {
|
||||||
|
m_isRunStarted = false;
|
||||||
|
m_endTimestamp = OSGetTime() - m_startTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
m_isRunStarted = false;
|
||||||
|
m_startTimestamp = 0;
|
||||||
|
m_endTimestamp = 0;
|
||||||
|
m_isPauseIGT = false;
|
||||||
|
m_loadStartTimestamp = 0;
|
||||||
|
m_totalLoadTime = 0;
|
||||||
|
m_igtTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_isRunStarted = false;
|
||||||
|
OSTime m_startTimestamp = 0;
|
||||||
|
OSTime m_endTimestamp = 0;
|
||||||
|
|
||||||
|
bool m_isPauseIGT = false;
|
||||||
|
OSTime m_loadStartTimestamp = 0;
|
||||||
|
OSTime m_totalLoadTime = 0;
|
||||||
|
OSTime m_igtTimer = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SpeedrunInfo m_speedrunInfo;
|
||||||
|
|
||||||
|
void resetForSpeedrunMode();
|
||||||
|
|
||||||
|
} // namespace dusk
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "os_report.h"
|
#include "os_report.h"
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
u32 JUTGamePad::CRumble::sChannelMask[4] = {
|
u32 JUTGamePad::CRumble::sChannelMask[4] = {
|
||||||
PAD_CHAN0_BIT,
|
PAD_CHAN0_BIT,
|
||||||
PAD_CHAN1_BIT,
|
PAD_CHAN1_BIT,
|
||||||
@@ -85,6 +89,9 @@ u32 JUTGamePad::sRumbleSupported;
|
|||||||
|
|
||||||
u32 JUTGamePad::read() {
|
u32 JUTGamePad::read() {
|
||||||
sRumbleSupported = PADRead(mPadStatus);
|
sRumbleSupported = PADRead(mPadStatus);
|
||||||
|
#if TARGET_PC
|
||||||
|
dusk::updateActionBindings();
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (sClampMode) {
|
switch (sClampMode) {
|
||||||
case EClampStick:
|
case EClampStick:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Android Shell
|
# Android Shell
|
||||||
|
|
||||||
This directory contains a minimal SDLActivity-based Android app wrapper for Dusk.
|
This directory contains a minimal SDLActivity-based Android app wrapper for Dusklight.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def duskRepoDir = rootProject.projectDir.parentFile.parentFile
|
def duskRepoDir = rootProject.projectDir.parentFile.parentFile
|
||||||
def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusk').get().asFile
|
def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusklight').get().asFile
|
||||||
def syncDuskAssets = tasks.register('syncDuskAssets', Sync) {
|
def syncDuskAssets = tasks.register('syncDuskAssets', Sync) {
|
||||||
from(new File(duskRepoDir, 'res')) {
|
from(new File(duskRepoDir, 'res')) {
|
||||||
into 'res'
|
into 'res'
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package dev.twilitrealm.dusk;
|
package dev.twilitrealm.dusk;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -22,6 +26,13 @@ import java.util.List;
|
|||||||
|
|
||||||
public class DuskActivity extends SDLActivity {
|
public class DuskActivity extends SDLActivity {
|
||||||
private static final String TAG = "DuskActivity";
|
private static final String TAG = "DuskActivity";
|
||||||
|
private static final int FOLDER_DIALOG_REQUEST_CODE = 0x4455;
|
||||||
|
private static final String EXTERNAL_STORAGE_AUTHORITY =
|
||||||
|
"com.android.externalstorage.documents";
|
||||||
|
|
||||||
|
private long folderDialogUserdata = 0;
|
||||||
|
|
||||||
|
private static native void nativeFolderDialogResult(long userdata, String path, String error);
|
||||||
|
|
||||||
private static String[] splitArgs(String raw) {
|
private static String[] splitArgs(String raw) {
|
||||||
List<String> out = new ArrayList<>();
|
List<String> out = new ArrayList<>();
|
||||||
@@ -147,9 +158,154 @@ public class DuskActivity extends SDLActivity {
|
|||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
persistUriPermissions(data);
|
persistUriPermissions(data);
|
||||||
}
|
}
|
||||||
|
if (requestCode == FOLDER_DIALOG_REQUEST_CODE) {
|
||||||
|
finishFolderDialog(resultCode, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean showFolderDialog(long userdata) {
|
||||||
|
if (userdata == 0 || folderDialogUserdata != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
folderDialogUserdata = userdata;
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
|
||||||
|
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivityForResult(intent, FOLDER_DIALOG_REQUEST_CODE);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.w(TAG, "Unable to open folder dialog.", e);
|
||||||
|
finishFolderDialog(Activity.RESULT_CANCELED, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishFolderDialog(int resultCode, Intent data) {
|
||||||
|
long userdata = folderDialogUserdata;
|
||||||
|
folderDialogUserdata = 0;
|
||||||
|
if (userdata == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode == RESULT_OK && data != null && data.getData() != null) {
|
||||||
|
String path = getRealPathForUri(data.getData());
|
||||||
|
if (path != null && !path.isEmpty()) {
|
||||||
|
nativeFolderDialogResult(userdata, path, null);
|
||||||
|
} else {
|
||||||
|
nativeFolderDialogResult(
|
||||||
|
userdata, null, "Selected folder is not available as a filesystem path");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeFolderDialogResult(userdata, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRealPathForUri(Uri uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String scheme = uri.getScheme();
|
||||||
|
if ("file".equals(scheme)) {
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"content".equals(scheme) ||
|
||||||
|
!EXTERNAL_STORAGE_AUTHORITY.equals(uri.getAuthority()) ||
|
||||||
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getExternalStoragePathForDocumentId(getExternalStorageDocumentId(uri));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.w(TAG, "Unable to resolve URI: " + uri, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExternalStorageDocumentId(Uri uri) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isTreeDocumentUri(uri)) {
|
||||||
|
return DocumentsContract.getTreeDocumentId(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DocumentsContract.getDocumentId(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTreeDocumentUri(Uri uri) {
|
||||||
|
List<String> segments = uri.getPathSegments();
|
||||||
|
return segments.size() >= 2 && "tree".equals(segments.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getExternalStoragePathForDocumentId(String documentId) {
|
||||||
|
if (documentId == null || documentId.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (documentId.startsWith("raw:")) {
|
||||||
|
return documentId.substring("raw:".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = documentId.split(":", 2);
|
||||||
|
String volumeId = parts[0];
|
||||||
|
String relativePath = parts.length > 1 ? parts[1] : "";
|
||||||
|
|
||||||
|
File root = getExternalStorageRoot(volumeId);
|
||||||
|
if (root == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return relativePath.isEmpty()
|
||||||
|
? root.getAbsolutePath()
|
||||||
|
: new File(root, relativePath).getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getExternalStorageRoot(String volumeId) {
|
||||||
|
if ("primary".equalsIgnoreCase(volumeId)) {
|
||||||
|
return Environment.getExternalStorageDirectory();
|
||||||
|
}
|
||||||
|
if ("home".equalsIgnoreCase(volumeId)) {
|
||||||
|
return new File(
|
||||||
|
Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOCUMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] externalFilesDirs = getExternalFilesDirs(null);
|
||||||
|
if (externalFilesDirs != null) {
|
||||||
|
for (File externalFilesDir : externalFilesDirs) {
|
||||||
|
File root = getStorageRootForExternalFilesDir(externalFilesDir);
|
||||||
|
if (root != null && volumeId.equalsIgnoreCase(root.getName())) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File fallback = new File("/storage", volumeId);
|
||||||
|
return fallback.exists() ? fallback : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getStorageRootForExternalFilesDir(File externalFilesDir) {
|
||||||
|
if (externalFilesDir == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = externalFilesDir.getAbsolutePath();
|
||||||
|
int androidDir = path.indexOf("/Android/");
|
||||||
|
if (androidDir <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File(path.substring(0, androidDir));
|
||||||
|
}
|
||||||
|
|
||||||
private void persistUriPermissions(Intent data) {
|
private void persistUriPermissions(Intent data) {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,15 +14,22 @@ import android.provider.DocumentsContract.Root;
|
|||||||
import android.provider.DocumentsProvider;
|
import android.provider.DocumentsProvider;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class DuskDocumentsProvider extends DocumentsProvider {
|
public class DuskDocumentsProvider extends DocumentsProvider {
|
||||||
public static final String AUTHORITY = "dev.twilitrealm.dusk.documents";
|
public static final String AUTHORITY = "dev.twilitrealm.dusk.documents";
|
||||||
|
|
||||||
private static final String ROOT_ID = "dusk";
|
private static final String ROOT_ID = "dusk";
|
||||||
private static final String ROOT_DOCUMENT_ID = "root";
|
private static final String ROOT_DOCUMENT_ID = "root";
|
||||||
|
private static final String LOCATION_DESCRIPTOR_NAME = "data_location.json";
|
||||||
private static final String DIRECTORY_MIME_TYPE = Document.MIME_TYPE_DIR;
|
private static final String DIRECTORY_MIME_TYPE = Document.MIME_TYPE_DIR;
|
||||||
|
|
||||||
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
|
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
|
||||||
@@ -46,13 +53,19 @@ public class DuskDocumentsProvider extends DocumentsProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
ensureUserDirectories();
|
if (!isCustomDataPathEnabled()) {
|
||||||
|
ensureUserDirectories();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
|
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
|
||||||
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
|
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
|
||||||
|
if (isCustomDataPathEnabled()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
final File root = getRootDirectory();
|
final File root = getRootDirectory();
|
||||||
final MatrixCursor.RowBuilder row = result.newRow();
|
final MatrixCursor.RowBuilder row = result.newRow();
|
||||||
|
|
||||||
@@ -222,9 +235,14 @@ public class DuskDocumentsProvider extends DocumentsProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private File getRootDirectory() throws FileNotFoundException {
|
private File getRootDirectory() throws FileNotFoundException {
|
||||||
|
if (isCustomDataPathEnabled()) {
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"Dusk DocumentsProvider is disabled while a custom data path is configured");
|
||||||
|
}
|
||||||
|
|
||||||
final File root = getContext().getFilesDir();
|
final File root = getContext().getFilesDir();
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
throw new FileNotFoundException("Dusk files directory is unavailable");
|
throw new FileNotFoundException("Dusklight files directory is unavailable");
|
||||||
}
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@@ -241,7 +259,7 @@ public class DuskDocumentsProvider extends DocumentsProvider {
|
|||||||
final String relativePath = documentId.substring(ROOT_DOCUMENT_ID.length() + 1);
|
final String relativePath = documentId.substring(ROOT_DOCUMENT_ID.length() + 1);
|
||||||
final File file = new File(root, relativePath);
|
final File file = new File(root, relativePath);
|
||||||
if (!isInside(root, file)) {
|
if (!isInside(root, file)) {
|
||||||
throw new FileNotFoundException("Document escapes Dusk files directory: " + documentId);
|
throw new FileNotFoundException("Document escapes Dusklight files directory: " + documentId);
|
||||||
}
|
}
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
throw new FileNotFoundException("Document does not exist: " + documentId);
|
throw new FileNotFoundException("Document does not exist: " + documentId);
|
||||||
@@ -255,7 +273,7 @@ public class DuskDocumentsProvider extends DocumentsProvider {
|
|||||||
return ROOT_DOCUMENT_ID;
|
return ROOT_DOCUMENT_ID;
|
||||||
}
|
}
|
||||||
if (!isInside(root, file)) {
|
if (!isInside(root, file)) {
|
||||||
throw new FileNotFoundException("File escapes Dusk files directory: " + file);
|
throw new FileNotFoundException("File escapes Dusklight files directory: " + file);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String rootPath = canonicalPath(root);
|
final String rootPath = canonicalPath(root);
|
||||||
@@ -273,6 +291,42 @@ public class DuskDocumentsProvider extends DocumentsProvider {
|
|||||||
new File(root, "EUR/Card A").mkdirs();
|
new File(root, "EUR/Card A").mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCustomDataPathEnabled() {
|
||||||
|
if (getContext() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File filesDir = getContext().getFilesDir();
|
||||||
|
if (filesDir == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File descriptor = new File(filesDir, LOCATION_DESCRIPTOR_NAME);
|
||||||
|
if (!descriptor.isFile()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final JSONObject json = new JSONObject(readText(descriptor));
|
||||||
|
return "custom".equals(json.optString("mode", "default"));
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readText(File file) throws IOException {
|
||||||
|
try (FileInputStream input = new FileInputStream(file);
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream())
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = input.read(buffer)) != -1) {
|
||||||
|
output.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
return output.toString(StandardCharsets.UTF_8.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String[] resolveRootProjection(String[] projection) {
|
private static String[] resolveRootProjection(String[] projection) {
|
||||||
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
|
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Dusk</string>
|
<string name="app_name">Dusklight</string>
|
||||||
<string name="documents_provider_root_name">Dusk Data</string>
|
<string name="documents_provider_root_name">Dusklight Data</string>
|
||||||
<string name="documents_provider_summary">Saves, texture packs, settings, and logs</string>
|
<string name="documents_provider_summary">Saves, texture packs, settings, and logs</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "dusk-android"
|
rootProject.name = "dusklight-android"
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 928 KiB After Width: | Height: | Size: 928 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 1014 B After Width: | Height: | Size: 1014 B |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
@@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=Dusk
|
|
||||||
GenericName=Dusk
|
|
||||||
Comment=The Legend of Zelda: Twilight Princess
|
|
||||||
Exec=dusk
|
|
||||||
Icon=dusk
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Game;
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=Dusklight
|
||||||
|
GenericName=Dusklight
|
||||||
|
Comment=PC port of a classic adventure game
|
||||||
|
Exec=dusklight
|
||||||
|
Icon=dusklight
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Game;
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>Dusk</string>
|
<string>Dusklight</string>
|
||||||
<key>CFBundleIconName</key>
|
<key>CFBundleIconName</key>
|
||||||
<string>Dusk</string>
|
<string>Dusk</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ BEGIN
|
|||||||
VALUE "CompanyName", "@DUSK_COMPANY_NAME@\0"
|
VALUE "CompanyName", "@DUSK_COMPANY_NAME@\0"
|
||||||
VALUE "FileDescription", "@DUSK_FILE_DESCRIPTION@\0"
|
VALUE "FileDescription", "@DUSK_FILE_DESCRIPTION@\0"
|
||||||
VALUE "FileVersion", "@DUSK_VERSION_STRING@\0"
|
VALUE "FileVersion", "@DUSK_VERSION_STRING@\0"
|
||||||
VALUE "InternalName", "dusk\0"
|
VALUE "InternalName", "dusklight\0"
|
||||||
VALUE "LegalCopyright", "@DUSK_COPYRIGHT@\0"
|
VALUE "LegalCopyright", "@DUSK_COPYRIGHT@\0"
|
||||||
VALUE "OriginalFilename", "dusk.exe\0"
|
VALUE "OriginalFilename", "dusklight.exe\0"
|
||||||
VALUE "ProductName", "@DUSK_PRODUCT_NAME@\0"
|
VALUE "ProductName", "@DUSK_PRODUCT_NAME@\0"
|
||||||
VALUE "ProductVersion", "@DUSK_VERSION_STRING@\0"
|
VALUE "ProductVersion", "@DUSK_VERSION_STRING@\0"
|
||||||
END
|
END
|
||||||
|
Before Width: | Height: | Size: 457 KiB |
|
Before Width: | Height: | Size: 340 KiB After Width: | Height: | Size: 642 KiB |
@@ -201,6 +201,37 @@ fps {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speedrun-timer {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: rgba(0, 0, 0, 65%);
|
||||||
|
padding: 2dp 4dp;
|
||||||
|
pointer-events: none;
|
||||||
|
font-family: "Noto Mono";
|
||||||
|
font-size: 16dp;
|
||||||
|
color: #ffffff;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
speedrun-timer[open] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
speedrun-rta {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
speedrun-rta[open] {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
speedrun-igt {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
fps[open] {
|
fps[open] {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ menu {
|
|||||||
right: auto;
|
right: auto;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
/* Scale based on a reference screen width, 428/1216 */
|
/* Scale based on a reference screen width, 856/1216 */
|
||||||
width: 35.230264vw;
|
width: 70.394736vw;
|
||||||
min-width: 428dp;
|
min-width: 428dp;
|
||||||
max-width: 856dp;
|
max-width: 50vw;
|
||||||
height: auto;
|
height: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -83,9 +83,8 @@ body.mirrored menu {
|
|||||||
hero {
|
hero {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8dp;
|
gap: 4dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.mirrored hero {
|
body.mirrored hero {
|
||||||
@@ -96,19 +95,19 @@ hero img {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eyebrow {
|
eyebrow {
|
||||||
font-family: "Alegreya SC";
|
font-family: "Alegreya SC";
|
||||||
font-size: 32dp;
|
font-size: 32dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1216dp) {
|
@media (min-width: 1216dp) {
|
||||||
.eyebrow {
|
eyebrow {
|
||||||
/* Same logic as .menu, 32/1216 */
|
/* Same logic as .menu, 32/1216 */
|
||||||
font-size: 2.631579vw;
|
font-size: 2.631579vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.eyebrow span {
|
eyebrow span {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +436,7 @@ body.animate-in .intro-item {
|
|||||||
decorator: horizontal-gradient(#FEE685FF #FEE68500);
|
decorator: horizontal-gradient(#FEE685FF #FEE68500);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eyebrow {
|
eyebrow {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ window content pane:last-of-type > div {
|
|||||||
line-height: 1.625;
|
line-height: 1.625;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-folder-current {
|
||||||
|
display: block;
|
||||||
|
font-size: 16dp;
|
||||||
|
color: rgba(224, 219, 200, 65%);
|
||||||
|
}
|
||||||
|
|
||||||
window content pane > spacer {
|
window content pane > spacer {
|
||||||
display: block;
|
display: block;
|
||||||
/* Completes the 24dp bottom inset after the pane's 8dp gap. */
|
/* Completes the 24dp bottom inset after the pane's 8dp gap. */
|
||||||
@@ -199,6 +205,11 @@ button:not(:disabled):active {
|
|||||||
box-shadow: #C2A42D 0 0 0 2dp;
|
box-shadow: #C2A42D 0 0 0 2dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
opacity: 0.35;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
button.modal-btn {
|
button.modal-btn {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -51,10 +51,13 @@
|
|||||||
#include "d/actor/d_a_ni.h"
|
#include "d/actor/d_a_ni.h"
|
||||||
#include "d/d_s_play.h"
|
#include "d/d_s_play.h"
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
#include "dusk/frame_interpolation.h"
|
#include "dusk/frame_interpolation.h"
|
||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
#include "res/Object/Alink.h"
|
#include "res/Object/Alink.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#endif
|
||||||
|
|
||||||
static int daAlink_Create(fopAc_ac_c* i_this);
|
static int daAlink_Create(fopAc_ac_c* i_this);
|
||||||
static int daAlink_Delete(daAlink_c* i_this);
|
static int daAlink_Delete(daAlink_c* i_this);
|
||||||
@@ -9363,6 +9366,12 @@ BOOL daAlink_c::spActionTrigger() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOL daAlink_c::midnaTalkTrigger() const {
|
BOOL daAlink_c::midnaTalkTrigger() const {
|
||||||
|
#if TARGET_PC
|
||||||
|
// If we have a custom bind for Midna, check that instead
|
||||||
|
if (dusk::isActionBound(dusk::ActionBinds::CALL_MIDNA, 0)) {
|
||||||
|
return dusk::getActionBindTrig(dusk::ActionBinds::CALL_MIDNA, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return mItemTrigger & BTN_Z;
|
return mItemTrigger & BTN_Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12729,7 +12738,7 @@ void daAlink_c::setMagicArmorBrk(int i_status) {
|
|||||||
|
|
||||||
BOOL daAlink_c::checkMagicArmorHeavy() const {
|
BOOL daAlink_c::checkMagicArmorHeavy() const {
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
return checkMagicArmorWearAbility() && (dComIfGs_getRupee() == 0 && !dusk::getSettings().game.freeMagicArmor);
|
return checkMagicArmorWearAbility() && (dComIfGs_getRupee() == 0 && !dusk::getSettings().game.magicArmorNoHeavy);
|
||||||
#else
|
#else
|
||||||
return checkMagicArmorWearAbility() && dComIfGs_getRupee() == 0;
|
return checkMagicArmorWearAbility() && dComIfGs_getRupee() == 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -16114,6 +16123,9 @@ int daAlink_c::procSlideLand() {
|
|||||||
|
|
||||||
int daAlink_c::procFrontRollInit() {
|
int daAlink_c::procFrontRollInit() {
|
||||||
BOOL is_guard_anime = checkUpperGuardAnime();
|
BOOL is_guard_anime = checkUpperGuardAnime();
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
const f32 fastRollMultiplier = dusk::getSettings().game.fastRoll ? 2.0f : 1.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mProcID == PROC_FRONT_ROLL && mDemo.getDemoMode() == daPy_demo_c::DEMO_FRONT_ROLL_e) {
|
if (mProcID == PROC_FRONT_ROLL && mDemo.getDemoMode() == daPy_demo_c::DEMO_FRONT_ROLL_e) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -16129,10 +16141,16 @@ int daAlink_c::procFrontRollInit() {
|
|||||||
roll_anm_speed = mpHIO->mFrontRoll.m.mRollAnm.mStartFrame;
|
roll_anm_speed = mpHIO->mFrontRoll.m.mRollAnm.mStartFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSingleAnime(ANM_FRONT_ROLL, mpHIO->mFrontRoll.m.mRollAnm.mSpeed, roll_anm_speed,
|
setSingleAnime(ANM_FRONT_ROLL,
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
mpHIO->mFrontRoll.m.mRollAnm.mSpeed * fastRollMultiplier,
|
||||||
|
#else
|
||||||
|
mpHIO->mFrontRoll.m.mRollAnm.mSpeed,
|
||||||
|
#endif
|
||||||
|
roll_anm_speed,
|
||||||
mpHIO->mFrontRoll.m.mRollAnm.mEndFrame,
|
mpHIO->mFrontRoll.m.mRollAnm.mEndFrame,
|
||||||
mpHIO->mFrontRoll.m.mRollAnm.mInterpolation);
|
mpHIO->mFrontRoll.m.mRollAnm.mInterpolation);
|
||||||
|
|
||||||
mNormalSpeed = speedF * mpHIO->mFrontRoll.m.mSpeedRate + mpHIO->mFrontRoll.m.mInitSpeed;
|
mNormalSpeed = speedF * mpHIO->mFrontRoll.m.mSpeedRate + mpHIO->mFrontRoll.m.mInitSpeed;
|
||||||
|
|
||||||
f32 max_speed = mpHIO->mFrontRoll.m.mInitSpeed + mpHIO->mMove.m.mMaxSpeed * mpHIO->mFrontRoll.m.mSpeedRate;
|
f32 max_speed = mpHIO->mFrontRoll.m.mInitSpeed + mpHIO->mMove.m.mMaxSpeed * mpHIO->mFrontRoll.m.mSpeedRate;
|
||||||
@@ -16145,11 +16163,20 @@ int daAlink_c::procFrontRollInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (checkNoResetFlg0(FLG0_WATER_IN_MOVE)) {
|
if (checkNoResetFlg0(FLG0_WATER_IN_MOVE)) {
|
||||||
mNormalSpeed *= mpHIO->mItem.mIronBoots.m.mWaterVelocityX;
|
#if TARGET_PC
|
||||||
|
if (!(dusk::getSettings().game.enableFastIronBoots))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
mNormalSpeed *= mpHIO->mItem.mIronBoots.m.mWaterVelocityX;
|
||||||
|
}
|
||||||
} else if (checkHeavyStateOn(TRUE, TRUE)) {
|
} else if (checkHeavyStateOn(TRUE, TRUE)) {
|
||||||
mNormalSpeed *= mHeavySpeedMultiplier;
|
mNormalSpeed *= mHeavySpeedMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
mNormalSpeed *= fastRollMultiplier;
|
||||||
|
#endif
|
||||||
|
|
||||||
current.angle.y = shape_angle.y;
|
current.angle.y = shape_angle.y;
|
||||||
voiceStart(Z2SE_AL_V_BACKTEN);
|
voiceStart(Z2SE_AL_V_BACKTEN);
|
||||||
mProcVar2.field_0x300c = 0;
|
mProcVar2.field_0x300c = 0;
|
||||||
@@ -16280,8 +16307,13 @@ int daAlink_c::procFrontRollCrashInit() {
|
|||||||
speed.y = mpHIO->mFrontRoll.m.mCrashSpeedV;
|
speed.y = mpHIO->mFrontRoll.m.mCrashSpeedV;
|
||||||
|
|
||||||
if (checkNoResetFlg0(FLG0_WATER_IN_MOVE)) {
|
if (checkNoResetFlg0(FLG0_WATER_IN_MOVE)) {
|
||||||
mNormalSpeed *= mpHIO->mItem.mIronBoots.m.mWaterVelocityX;
|
#if TARGET_PC
|
||||||
speed.y *= mpHIO->mItem.mIronBoots.m.mWaterVelocityY;
|
if (!(dusk::getSettings().game.enableFastIronBoots))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
mNormalSpeed *= mpHIO->mItem.mIronBoots.m.mWaterVelocityX;
|
||||||
|
speed.y *= mpHIO->mItem.mIronBoots.m.mWaterVelocityY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ANGLE_ADD_2(current.angle.y, 0x8000);
|
ANGLE_ADD_2(current.angle.y, 0x8000);
|
||||||
@@ -16375,6 +16407,9 @@ int daAlink_c::procFrontRollSuccess() {
|
|||||||
|
|
||||||
int daAlink_c::procSideRollInit(int param_0) {
|
int daAlink_c::procSideRollInit(int param_0) {
|
||||||
BOOL is_prev_guardAnm = checkUpperGuardAnime();
|
BOOL is_prev_guardAnm = checkUpperGuardAnime();
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
const f32 fastRollMultiplier = dusk::getSettings().game.fastRoll ? 2.0f : 1.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!commonProcInitNotSameProc(PROC_SIDE_ROLL)) {
|
if (!commonProcInitNotSameProc(PROC_SIDE_ROLL)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -16391,17 +16426,30 @@ int daAlink_c::procSideRollInit(int param_0) {
|
|||||||
current.angle.y = shape_angle.y + -0x4000;
|
current.angle.y = shape_angle.y + -0x4000;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSingleAnime(anmID, mpHIO->mGuard.mTurnMove.m.mSideRollAnmSpeed,
|
setSingleAnime(anmID,
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
mpHIO->mGuard.mTurnMove.m.mSideRollAnmSpeed * fastRollMultiplier,
|
||||||
|
#else
|
||||||
|
mpHIO->mGuard.mTurnMove.m.mSideRollAnmSpeed,
|
||||||
|
#endif
|
||||||
mpHIO->mGuard.mTurnMove.m.mTurnAnm.mStartFrame,
|
mpHIO->mGuard.mTurnMove.m.mTurnAnm.mStartFrame,
|
||||||
mpHIO->mGuard.mTurnMove.m.mTurnAnm.mEndFrame,
|
mpHIO->mGuard.mTurnMove.m.mTurnAnm.mEndFrame,
|
||||||
mpHIO->mGuard.mTurnMove.m.mTurnAnm.mInterpolation);
|
mpHIO->mGuard.mTurnMove.m.mTurnAnm.mInterpolation);
|
||||||
mNormalSpeed = mpHIO->mGuard.mTurnMove.m.mSideRollSpeed;
|
mNormalSpeed = mpHIO->mGuard.mTurnMove.m.mSideRollSpeed;
|
||||||
|
|
||||||
if (checkNoResetFlg0(FLG0_WATER_IN_MOVE)) {
|
if (checkNoResetFlg0(FLG0_WATER_IN_MOVE)) {
|
||||||
mNormalSpeed *= mpHIO->mItem.mIronBoots.m.mWaterVelocityX;
|
#if TARGET_PC
|
||||||
|
if (!(dusk::getSettings().game.enableFastIronBoots))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
mNormalSpeed *= mpHIO->mItem.mIronBoots.m.mWaterVelocityX;
|
||||||
|
}
|
||||||
} else if (checkHeavyStateOn(TRUE, TRUE)) {
|
} else if (checkHeavyStateOn(TRUE, TRUE)) {
|
||||||
mNormalSpeed *= mHeavySpeedMultiplier;
|
mNormalSpeed *= mHeavySpeedMultiplier;
|
||||||
}
|
}
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
mNormalSpeed *= fastRollMultiplier;
|
||||||
|
#endif
|
||||||
|
|
||||||
setFootEffectProcType(0);
|
setFootEffectProcType(0);
|
||||||
field_0x2f9d = 4;
|
field_0x2f9d = 4;
|
||||||
@@ -18663,7 +18711,7 @@ int daAlink_c::execute() {
|
|||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
// This handles rupee drain and transitions between rupees/no rupees
|
// This handles rupee drain and transitions between rupees/no rupees
|
||||||
// We can skip all of that if the magic armor doesn't use rupees
|
// We can skip all of that if the magic armor doesn't use rupees
|
||||||
if (!dusk::getSettings().game.freeMagicArmor && checkMagicArmorWearAbility() && mClothesChangeWaitTimer == 0) {
|
if (!dusk::getSettings().game.magicArmorNoDrain && checkMagicArmorWearAbility() && mClothesChangeWaitTimer == 0) {
|
||||||
#else
|
#else
|
||||||
if (checkMagicArmorWearAbility() && mClothesChangeWaitTimer == 0) {
|
if (checkMagicArmorWearAbility() && mClothesChangeWaitTimer == 0) {
|
||||||
#endif
|
#endif
|
||||||
@@ -18676,7 +18724,8 @@ int daAlink_c::execute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dComIfGs_getRupee() == 0 && field_0x2fd7 != 0) {
|
if (dComIfGs_getRupee() == 0 && field_0x2fd7 != 0 IF_DUSK( && !dusk::getSettings().game.magicArmorNoHeavy))
|
||||||
|
{
|
||||||
setMagicArmorBrk(0);
|
setMagicArmorBrk(0);
|
||||||
seStartOnlyReverb(Z2SE_AL_M_ARMER_TURNOFF);
|
seStartOnlyReverb(Z2SE_AL_M_ARMER_TURNOFF);
|
||||||
mZ2Link.setLinkState(5);
|
mZ2Link.setLinkState(5);
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ int daAlink_c::setDamagePoint(int i_dmgAmount, BOOL i_checkZoraMag, BOOL i_setDm
|
|||||||
|
|
||||||
if (checkMagicArmorNoDamage()) {
|
if (checkMagicArmorNoDamage()) {
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
if(dusk::getSettings().game.freeMagicArmor) {
|
if(dusk::getSettings().game.magicArmorNoDamageLoss) {
|
||||||
i_dmgAmount = 0;
|
i_dmgAmount = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
|
#include "dusk/speedrun.h"
|
||||||
|
|
||||||
BOOL daAlink_c::checkEventRun() const {
|
BOOL daAlink_c::checkEventRun() const {
|
||||||
return dComIfGp_event_runCheck() || checkPlayerDemoMode();
|
return dComIfGp_event_runCheck() || checkPlayerDemoMode();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
#include "dusk/gyro.h"
|
#include "dusk/gyro.h"
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool daAlink_c::checkNoSubjectModeCamera() {
|
bool daAlink_c::checkNoSubjectModeCamera() {
|
||||||
@@ -144,8 +145,8 @@ BOOL daAlink_c::setBodyAngleToCamera() {
|
|||||||
f32 gy_pitch = 0.f;
|
f32 gy_pitch = 0.f;
|
||||||
dusk::gyro::getAimDeltas(gy_yaw, gy_pitch);
|
dusk::gyro::getAimDeltas(gy_yaw, gy_pitch);
|
||||||
|
|
||||||
shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale * (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f));
|
shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale);
|
||||||
sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale * (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f));
|
sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale);
|
||||||
|
|
||||||
if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) {
|
if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) {
|
||||||
sp8 = mBodyAngle.x;
|
sp8 = mBodyAngle.x;
|
||||||
@@ -192,7 +193,9 @@ BOOL daAlink_c::subjectCancelTrigger() {
|
|||||||
BOOL daAlink_c::checkSubjectEnd(BOOL i_isPlaySe) {
|
BOOL daAlink_c::checkSubjectEnd(BOOL i_isPlaySe) {
|
||||||
setDoStatus(BUTTON_STATUS_BACK);
|
setDoStatus(BUTTON_STATUS_BACK);
|
||||||
|
|
||||||
if (checkEventRun() || checkEquipAnime() || doTrigger() || checkSetItemTrigger(dItemNo_HAWK_EYE_e) || subjectCancelTrigger() || checkEndResetFlg0(ERFLG0_FORCE_SUBJECT_CANCEL) || dComIfGp_checkCameraAttentionStatus(field_0x317c, 0x2000)) {
|
// Allow pressing the first person binding to also leave first person
|
||||||
|
if (IF_DUSK(dusk::getActionBindTrig(dusk::ActionBinds::FIRST_PERSON_CAMERA, 0)) ||
|
||||||
|
checkEventRun() || checkEquipAnime() || doTrigger() || checkSetItemTrigger(dItemNo_HAWK_EYE_e) || subjectCancelTrigger() || checkEndResetFlg0(ERFLG0_FORCE_SUBJECT_CANCEL) || dComIfGp_checkCameraAttentionStatus(field_0x317c, 0x2000)) {
|
||||||
if (i_isPlaySe) {
|
if (i_isPlaySe) {
|
||||||
seStartSystem(Z2SE_SUBJ_VIEW_OUT);
|
seStartSystem(Z2SE_SUBJ_VIEW_OUT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,12 @@ int daAlink_c::loadModelDVD() {
|
|||||||
mpWlMidnaHairModel = NULL;
|
mpWlMidnaHairModel = NULL;
|
||||||
|
|
||||||
if (!checkNoResetFlg2(FLG2_UNK_280000)) {
|
if (!checkNoResetFlg2(FLG2_UNK_280000)) {
|
||||||
dComIfG_resDelete(&mPhaseReq, mArcName);
|
if (!dComIfG_resDelete(&mPhaseReq, mArcName)) {
|
||||||
|
#if TARGET_PC
|
||||||
|
// resDelete no-ops if load was in-progress; force-unregister before freeAll
|
||||||
|
dComIfG_deleteObjectResMain(mArcName);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
cPhs_Reset(&mPhaseReq);
|
cPhs_Reset(&mPhaseReq);
|
||||||
mpArcHeap->freeAll();
|
mpArcHeap->freeAll();
|
||||||
|
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ void daAlink_c::changeLink(int param_0) {
|
|||||||
initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_mArcName, "al_hands.bmd")), 0);
|
initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_mArcName, "al_hands.bmd")), 0);
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
if (dComIfGs_getRupee() != 0 || dusk::getSettings().game.freeMagicArmor)
|
if (dComIfGs_getRupee() != 0 || dusk::getSettings().game.magicArmorNoHeavy)
|
||||||
#else
|
#else
|
||||||
if (dComIfGs_getRupee() != 0)
|
if (dComIfGs_getRupee() != 0)
|
||||||
#endif
|
#endif
|
||||||
@@ -458,7 +458,7 @@ void daAlink_c::changeLink(int param_0) {
|
|||||||
field_0x06f0 = field_0x064C->getMaterialNodePointer(2)->getShape();
|
field_0x06f0 = field_0x064C->getMaterialNodePointer(2)->getShape();
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
if (dComIfGs_getRupee() != 0 || dusk::getSettings().game.freeMagicArmor) {
|
if (dComIfGs_getRupee() != 0 || dusk::getSettings().game.magicArmorNoHeavy) {
|
||||||
#else
|
#else
|
||||||
if (dComIfGs_getRupee() != 0) {
|
if (dComIfGs_getRupee() != 0) {
|
||||||
#endif
|
#endif
|
||||||
@@ -8723,6 +8723,12 @@ int daAlink_c::procWolfCargoCarry() {
|
|||||||
return checkNextActionWolf(0);
|
return checkNextActionWolf(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
if (field_0x280c.getActor() == NULL) {
|
||||||
|
return checkNextActionWolf(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
mDoMtx_stack_c::copy(((e_yc_class*)field_0x280c.getActor())->getLegR3Mtx());
|
mDoMtx_stack_c::copy(((e_yc_class*)field_0x280c.getActor())->getLegR3Mtx());
|
||||||
mDoMtx_stack_c::transM(-9.0f, -7.0f, -30.0f);
|
mDoMtx_stack_c::transM(-9.0f, -7.0f, -30.0f);
|
||||||
mDoMtx_stack_c::multVecZero(¤t.pos);
|
mDoMtx_stack_c::multVecZero(¤t.pos);
|
||||||
|
|||||||
@@ -11,7 +11,9 @@
|
|||||||
#include "d/d_msg_string.h"
|
#include "d/d_msg_string.h"
|
||||||
#include "dusk/livesplit.h"
|
#include "dusk/livesplit.h"
|
||||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||||
|
#include "dusk/speedrun.h"
|
||||||
#include "m_Do/m_Do_controller_pad.h"
|
#include "m_Do/m_Do_controller_pad.h"
|
||||||
|
#include <dusk/autosave.h>
|
||||||
|
|
||||||
dBrightCheck_c::dBrightCheck_c(JKRArchive* i_archive) {
|
dBrightCheck_c::dBrightCheck_c(JKRArchive* i_archive) {
|
||||||
mArchive = i_archive;
|
mArchive = i_archive;
|
||||||
@@ -146,10 +148,12 @@ void dBrightCheck_c::modeMove() {
|
|||||||
if (dusk::getSettings().game.speedrunMode && !dusk::getSettings().game.hideTvSettingsScreen) {
|
if (dusk::getSettings().game.speedrunMode && !dusk::getSettings().game.hideTvSettingsScreen) {
|
||||||
// start a new run if a run isn't already in progress
|
// start a new run if a run isn't already in progress
|
||||||
if (!dusk::m_speedrunInfo.m_isRunStarted) {
|
if (!dusk::m_speedrunInfo.m_isRunStarted) {
|
||||||
dusk::ImGuiMenuGame::resetForSpeedrunMode();
|
dusk::resetForSpeedrunMode();
|
||||||
dusk::m_speedrunInfo.startRun();
|
dusk::m_speedrunInfo.startRun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAutoSave(true);
|
||||||
#endif
|
#endif
|
||||||
mCompleteCheck = true;
|
mCompleteCheck = true;
|
||||||
mMode = MODE_WAIT_e;
|
mMode = MODE_WAIT_e;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
#include "dusk/frame_interpolation.h"
|
#include "dusk/frame_interpolation.h"
|
||||||
#include "dusk/logging.h"
|
#include "dusk/logging.h"
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -838,6 +839,12 @@ void dCamera_c::updatePad() {
|
|||||||
mTrigB = mDoCPd_c::getTrigB(mPadID) ? true : false;
|
mTrigB = mDoCPd_c::getTrigB(mPadID) ? true : false;
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
|
// If our custom action binding is triggered, and we're not already in first person, go into first person
|
||||||
|
if (dusk::getActionBindTrig(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID) && mGear != -1) {
|
||||||
|
setComStat(0x1000);
|
||||||
|
mGear = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (mCamParam.mManualMode) {
|
if (mCamParam.mManualMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -877,7 +884,8 @@ void dCamera_c::updatePad() {
|
|||||||
|
|
||||||
if (mPadInfo.mCStick.mLastPosY < -mCamSetup.mCStick.SwTHH()) {
|
if (mPadInfo.mCStick.mLastPosY < -mCamSetup.mCStick.SwTHH()) {
|
||||||
if (mCStickYState != -1) {
|
if (mCStickYState != -1) {
|
||||||
if (mGear == -1 && mCurMode == 4) {
|
// Don't use regular first person trigger if custom mapping is set
|
||||||
|
if (mGear == -1 && mCurMode == 4 IF_DUSK(&& !dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID))) {
|
||||||
mGear = 0;
|
mGear = 0;
|
||||||
setComStat(0x2000);
|
setComStat(0x2000);
|
||||||
} else if (mGear == 0 && sp6C) {
|
} else if (mGear == 0 && sp6C) {
|
||||||
@@ -888,7 +896,8 @@ void dCamera_c::updatePad() {
|
|||||||
mCStickYState = -1;
|
mCStickYState = -1;
|
||||||
} else if (mPadInfo.mCStick.mLastPosY > mCamSetup.mCStick.SwTHH()) {
|
} else if (mPadInfo.mCStick.mLastPosY > mCamSetup.mCStick.SwTHH()) {
|
||||||
if (mCStickYState != 1) {
|
if (mCStickYState != 1) {
|
||||||
if (mGear == 0 && sp6B) {
|
// Don't use regular first person trigger if custom mapping is set
|
||||||
|
if (mGear == 0 && sp6B IF_DUSK(&& !dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID))) {
|
||||||
setComStat(0x1000);
|
setComStat(0x1000);
|
||||||
} else if (mGear == 1) {
|
} else if (mGear == 1) {
|
||||||
mGear = 0;
|
mGear = 0;
|
||||||
@@ -7649,9 +7658,10 @@ bool dCamera_c::freeCamera() {
|
|||||||
f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY);
|
f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY);
|
||||||
|
|
||||||
// If we aren't in manual cam mode, don't trigger it if the player tries to hit C-up
|
// If we aren't in manual cam mode, don't trigger it if the player tries to hit C-up
|
||||||
// for first person
|
// for first person unless they have first person bound to a custom binding
|
||||||
if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY < 0 ||
|
if ((dusk::isActionBound(dusk::ActionBinds::FIRST_PERSON_CAMERA, mPadID) && mPadInfo.mCStick.mLastPosY != 0) ||
|
||||||
(mCamParam.mManualMode == 1 && mPadInfo.mCStick.mLastPosY != 0)) {
|
mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY < 0 || (mCamParam.mManualMode == 1 && mPadInfo.mCStick.mLastPosY != 0))
|
||||||
|
{
|
||||||
mCamParam.mManualMode = 1;
|
mCamParam.mManualMode = 1;
|
||||||
camMovement = camMovement.normalize();
|
camMovement = camMovement.normalize();
|
||||||
camMovement.y *= dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f;
|
camMovement.y *= dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f;
|
||||||
@@ -11234,6 +11244,62 @@ cXyz dCamera_c::Center() {
|
|||||||
return mCenter + mShake.field_0x24;
|
return mCenter + mShake.field_0x24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
f32 get_target_trim_height(camera_process_class* i_this) {
|
||||||
|
const auto camera = &i_this->mCamera;
|
||||||
|
if (camera->mCurState != 2) {
|
||||||
|
switch (camera->mTrimSize) {
|
||||||
|
case 0:
|
||||||
|
case 4:
|
||||||
|
return 0.0f;
|
||||||
|
case 1:
|
||||||
|
return camera->mCamSetup.VistaTrimHeight();
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
return camera->mCamSetup.CinemaScopeTrimHeight();
|
||||||
|
default:
|
||||||
|
return camera->mTrimHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return camera->mTrimHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void widezoom_correction(camera_process_class* i_this, float trim_height) {
|
||||||
|
camera_class* camera = (camera_class*)i_this;
|
||||||
|
dDlst_window_c* window = get_window(camera);
|
||||||
|
view_port_class* viewport = window->getViewPort();
|
||||||
|
|
||||||
|
auto trim_width = 0.0f;
|
||||||
|
|
||||||
|
if (mDoGph_gInf_c::isWideZoom()) {
|
||||||
|
const auto target_ar = FB_WIDTH_BASE / (FB_HEIGHT_BASE - trim_height * 2.0f);
|
||||||
|
const auto target_ar_real =
|
||||||
|
FB_WIDTH_BASE / (FB_HEIGHT_BASE - get_target_trim_height(i_this) * 2.0f);
|
||||||
|
const auto current_ar = camera->view.aspect;
|
||||||
|
|
||||||
|
if (current_ar < target_ar) {
|
||||||
|
trim_height = FB_HEIGHT_BASE / 2.0f * (1.0f - current_ar / target_ar);
|
||||||
|
} else {
|
||||||
|
trim_height = 0.0f;
|
||||||
|
trim_width = FB_WIDTH_BASE / 2.0f * (1.0f - target_ar_real / current_ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dusk::frame_interp::is_sim_frame()) {
|
||||||
|
constexpr auto base_ar =
|
||||||
|
static_cast<f32>(FB_WIDTH_BASE) / static_cast<f32>(FB_HEIGHT_BASE);
|
||||||
|
const auto ar_corr = base_ar / std::min(current_ar, target_ar_real);
|
||||||
|
camera->view.fovy =
|
||||||
|
MTXRadToDeg(2.0f * atanf(tanf(MTXDegToRad(camera->view.fovy) * 0.5f) * ar_corr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_width *= viewport->width / FB_WIDTH_BASE;
|
||||||
|
trim_height *= viewport->height / FB_HEIGHT_BASE;
|
||||||
|
window->setScissor(trim_width, trim_height, viewport->width - trim_width * 2.0f,
|
||||||
|
viewport->height - trim_height * 2.0f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int camera_execute(camera_process_class* i_this) {
|
static int camera_execute(camera_process_class* i_this) {
|
||||||
preparation(i_this);
|
preparation(i_this);
|
||||||
|
|
||||||
@@ -11254,6 +11320,28 @@ static int camera_execute(camera_process_class* i_this) {
|
|||||||
store(i_this);
|
store(i_this);
|
||||||
|
|
||||||
#ifdef TARGET_PC
|
#ifdef TARGET_PC
|
||||||
|
widezoom_correction(i_this, i_this->mCamera.TrimHeight());
|
||||||
|
|
||||||
|
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||||
|
dusk::frame_interp::add_interpolation_callback([](bool _, void* pUserWork) {
|
||||||
|
const auto i_this = static_cast<camera_process_class*>(pUserWork);
|
||||||
|
const auto camera = &i_this->mCamera;
|
||||||
|
|
||||||
|
const auto trim_size = camera->mTrimSize;
|
||||||
|
|
||||||
|
if (camera->mCurState != 2 && trim_size >= 0 && trim_size <= 3) {
|
||||||
|
// derive trim height at previous tick using current camera state
|
||||||
|
const auto target = get_target_trim_height(i_this);
|
||||||
|
const auto step = dusk::frame_interp::get_interpolation_step();
|
||||||
|
const auto cur = camera->TrimHeight();
|
||||||
|
const auto prev = (4.0f * cur - target) / 3.0f;
|
||||||
|
const auto trim_height = prev + (cur - prev) * step;
|
||||||
|
|
||||||
|
widezoom_correction(i_this, trim_height);
|
||||||
|
}
|
||||||
|
}, i_this);
|
||||||
|
}
|
||||||
|
|
||||||
// record new camera for our sim frame
|
// record new camera for our sim frame
|
||||||
dusk::frame_interp::record_camera(i_this, get_camera_id(i_this));
|
dusk::frame_interp::record_camera(i_this, get_camera_id(i_this));
|
||||||
// interpolate the view now so that this sim frame's view matrix matches what
|
// interpolate the view now so that this sim frame's view matrix matches what
|
||||||
@@ -11265,26 +11353,6 @@ static int camera_execute(camera_process_class* i_this) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TARGET_PC
|
|
||||||
void set_ar_corrected_trim(dDlst_window_c* window, float trim_height) {
|
|
||||||
const auto viewport = window->getViewPort();
|
|
||||||
|
|
||||||
if (mDoGph_gInf_c::isWideZoom()) {
|
|
||||||
const auto target_ar = FB_WIDTH / (FB_HEIGHT - trim_height * 2.0f);
|
|
||||||
const auto current_ar = mDoGph_gInf_c::m_safeWidthF / mDoGph_gInf_c::m_safeHeightF;
|
|
||||||
|
|
||||||
if (current_ar < target_ar) {
|
|
||||||
trim_height = FB_HEIGHT / 2.0f * (1.0f - current_ar / target_ar);
|
|
||||||
} else {
|
|
||||||
trim_height = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trim_height *= viewport->height / FB_HEIGHT;
|
|
||||||
window->setScissor(0.0f, trim_height, viewport->width, viewport->height - trim_height * 2.0f);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int camera_draw(camera_process_class* i_this) {
|
static int camera_draw(camera_process_class* i_this) {
|
||||||
camera_class* a_this = (camera_class*)i_this;
|
camera_class* a_this = (camera_class*)i_this;
|
||||||
dCamera_c* body = &i_this->mCamera;
|
dCamera_c* body = &i_this->mCamera;
|
||||||
@@ -11337,42 +11405,8 @@ static int camera_draw(camera_process_class* i_this) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if TARGET_PC
|
#if !TARGET_PC
|
||||||
set_ar_corrected_trim(window, body->TrimHeight());
|
// trim handling moved to camera_execute for PC
|
||||||
|
|
||||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
|
||||||
dusk::frame_interp::add_interpolation_callback([](bool _, void* pUserWork) {
|
|
||||||
const auto i_this = static_cast<camera_process_class*>(pUserWork);
|
|
||||||
const auto camera = &i_this->mCamera;
|
|
||||||
|
|
||||||
const auto trim_size = camera->mTrimSize;
|
|
||||||
|
|
||||||
if (camera->mCurState != 2 && trim_size >= 0 && trim_size <= 3) {
|
|
||||||
// derive trim height at previous tick using current camera state
|
|
||||||
f32 target;
|
|
||||||
switch (trim_size) {
|
|
||||||
case 0:
|
|
||||||
target = 0.0f;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
target = camera->mCamSetup.VistaTrimHeight();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
target = camera->mCamSetup.CinemaScopeTrimHeight();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto step = dusk::frame_interp::get_interpolation_step();
|
|
||||||
const auto cur = camera->TrimHeight();
|
|
||||||
const auto prev = (4.0f * cur - target) / 3.0f;
|
|
||||||
const auto trim_height = prev + (cur - prev) * step;
|
|
||||||
|
|
||||||
set_ar_corrected_trim(get_window((camera_class*)i_this), trim_height);
|
|
||||||
}
|
|
||||||
}, i_this);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int trim_height = body->TrimHeight();
|
int trim_height = body->TrimHeight();
|
||||||
|
|
||||||
window->setScissor(0.0f, trim_height, FB_WIDTH, FB_HEIGHT - trim_height * 2.0f);
|
window->setScissor(0.0f, trim_height, FB_WIDTH, FB_HEIGHT - trim_height * 2.0f);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "f_op/f_op_actor_mng.h"
|
#include "f_op/f_op_actor_mng.h"
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
#include "dusk/achievements.h"
|
#include "dusk/achievements.h"
|
||||||
|
#include "dusk/settings.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int plCutLRC[58] = {
|
static int plCutLRC[58] = {
|
||||||
@@ -429,6 +430,13 @@ fopAc_ac_c* cc_at_check(fopAc_ac_c* i_enemy, dCcU_AtInfo* i_AtInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_PC
|
||||||
|
if (dusk::getSettings().game.invincibleEnemies &&
|
||||||
|
fopAcM_GetGroup(i_enemy) == fopAc_ENEMY_e) {
|
||||||
|
i_AtInfo->mAttackPower = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (i_AtInfo->mAttackPower != 0) {
|
if (i_AtInfo->mAttackPower != 0) {
|
||||||
i_enemy->health -= i_AtInfo->mAttackPower;
|
i_enemy->health -= i_AtInfo->mAttackPower;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3882,7 +3882,11 @@ bool dCamera_c::hintTalkEvCamera() {
|
|||||||
|
|
||||||
cSAngle acStack_1fc(20.0f);
|
cSAngle acStack_1fc(20.0f);
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
|
#if AVOID_UB
|
||||||
|
for (j = 0; j < 10; j++) {
|
||||||
|
#else
|
||||||
for (j = 0; j < 12; j++) {
|
for (j = 0; j < 12; j++) {
|
||||||
|
#endif
|
||||||
cSAngle acStack_200(local_b0[j] * fVar22);
|
cSAngle acStack_200(local_b0[j] * fVar22);
|
||||||
hintTalk->mDirection.U(acStack_1f8 + acStack_200);
|
hintTalk->mDirection.U(acStack_1f8 + acStack_200);
|
||||||
hintTalk->mDirection.V(((hintTalk->field_0x28.V() * acStack_200.Cos()) * 0.2f) + acStack_1fc);
|
hintTalk->mDirection.V(((hintTalk->field_0x28.V() * acStack_200.Cos()) * 0.2f) + acStack_1fc);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "dusk/imgui/ImGuiBloomWindow.hpp"
|
#include "dusk/imgui/ImGuiBloomWindow.hpp"
|
||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
#include "dusk/frame_interpolation.h"
|
#include "dusk/frame_interpolation.h"
|
||||||
|
#include "dusk/game_clock.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void GxXFog_set();
|
static void GxXFog_set();
|
||||||
@@ -2268,6 +2269,7 @@ void dKy_calc_color_set(GXColorS10* out_color_p, color_RGB_class* color_a_start_
|
|||||||
color_b_start_p->b, color_b_end_p->b, blend_ratio, add_col.b, scale);
|
color_b_start_p->b, color_b_end_p->b, blend_ratio, add_col.b, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dScnKy_env_light_c::setLight() {
|
void dScnKy_env_light_c::setLight() {
|
||||||
f32 color_ratio;
|
f32 color_ratio;
|
||||||
|
|
||||||
@@ -2513,7 +2515,14 @@ void dScnKy_env_light_c::setLight() {
|
|||||||
static s16 S_fuwan_sin;
|
static s16 S_fuwan_sin;
|
||||||
|
|
||||||
f32 sin = cM_ssin(S_fuwan_sin);
|
f32 sin = cM_ssin(S_fuwan_sin);
|
||||||
S_fuwan_sin += (s16)cM_rndF(2000.0f) + 500;
|
|
||||||
|
#if TARGET_PC
|
||||||
|
const f32 deltaTime = dusk::game_clock::consume_interval(this);
|
||||||
|
const f32 timeScale = deltaTime / dusk::game_clock::period_for_original_frames(1.0f);
|
||||||
|
S_fuwan_sin += (s16)((cM_rndF(2000.0f) + 500) * timeScale);
|
||||||
|
#else
|
||||||
|
S_fuwan_sin += (s16)cM_rndF(2000.0f) + 500;
|
||||||
|
#endif
|
||||||
|
|
||||||
blure_size += (u8)(sin * (0.2f * blure_size));
|
blure_size += (u8)(sin * (0.2f * blure_size));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ u8 STControl::checkTrigger() {
|
|||||||
field_0x22 = -field_0x24;
|
field_0x22 = -field_0x24;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if !TARGET_PC
|
||||||
if (!(mDirectionTrig & 3)) {
|
if (!(mDirectionTrig & 3)) {
|
||||||
Xinit();
|
Xinit();
|
||||||
}
|
}
|
||||||
@@ -160,10 +160,35 @@ u8 STControl::checkTrigger() {
|
|||||||
if (!(mDirectionTrig & 0xC)) {
|
if (!(mDirectionTrig & 0xC)) {
|
||||||
Yinit();
|
Yinit();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
mDirectionTrig = 0;
|
mDirectionTrig = 0;
|
||||||
|
#if !TARGET_PC
|
||||||
Xinit();
|
Xinit();
|
||||||
Yinit();
|
Yinit();
|
||||||
|
#endif
|
||||||
|
#if TARGET_PC
|
||||||
|
if (mDoCPd_c::getHoldLeft(PAD_1)) {
|
||||||
|
mDirectionTrig |= TRIG_LEFT;
|
||||||
|
}
|
||||||
|
if (mDoCPd_c::getHoldRight(PAD_1)) {
|
||||||
|
mDirectionTrig |= TRIG_RIGHT;
|
||||||
|
}
|
||||||
|
if (mDoCPd_c::getHoldUp(PAD_1)) {
|
||||||
|
mDirectionTrig |= TRIG_UP;
|
||||||
|
}
|
||||||
|
if (mDoCPd_c::getHoldDown(PAD_1)) {
|
||||||
|
mDirectionTrig |= TRIG_DOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(mDirectionTrig & 3)) {
|
||||||
|
Xinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(mDirectionTrig & 0xC)) {
|
||||||
|
Yinit();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((field_0x0d & mDirectionTrig & 3) && field_0x0e > 0) {
|
if ((field_0x0d & mDirectionTrig & 3) && field_0x0e > 0) {
|
||||||
|
|||||||
@@ -1141,6 +1141,9 @@ dMap_c::dMap_c(int width, int height, int param_2, int param_3) {
|
|||||||
field_0x91 = 0;
|
field_0x91 = 0;
|
||||||
m_mySelfPointer = this;
|
m_mySelfPointer = this;
|
||||||
#endif
|
#endif
|
||||||
|
#if TARGET_PC
|
||||||
|
previousMirror = dusk::getSettings().game.enableMirrorMode;
|
||||||
|
#endif
|
||||||
|
|
||||||
m_res = JKR_NEW_ARGS (0x20) dMap_prm_res_s;
|
m_res = JKR_NEW_ARGS (0x20) dMap_prm_res_s;
|
||||||
JUT_ASSERT(2559, m_res != NULL);
|
JUT_ASSERT(2559, m_res != NULL);
|
||||||
@@ -1579,6 +1582,17 @@ bool dMap_c::isDrawRoomIcon(int param_0, int param_1) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dMap_c::_move(f32 i_centerX, f32 i_centerZ, int i_roomNo, f32 param_3) {
|
void dMap_c::_move(f32 i_centerX, f32 i_centerZ, int i_roomNo, f32 param_3) {
|
||||||
|
#if TARGET_PC
|
||||||
|
bool currentMirror = dusk::getSettings().game.enableMirrorMode;
|
||||||
|
if (currentMirror != previousMirror) {
|
||||||
|
previousMirror = currentMirror;
|
||||||
|
if (currentMirror) {
|
||||||
|
mCenterX -= 2.0f * mPackX;
|
||||||
|
} else {
|
||||||
|
mCenterX += 2.0f * mPackX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (mStayRoomNo == -1) {
|
if (mStayRoomNo == -1) {
|
||||||
mStayRoomNo = i_roomNo;
|
mStayRoomNo = i_roomNo;
|
||||||
field_0x80 = mStayRoomNo;
|
field_0x80 = mStayRoomNo;
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ bool dMenu_Fishing_c::isSync() {
|
|||||||
void dMenu_Fishing_c::init() {
|
void dMenu_Fishing_c::init() {
|
||||||
#if TARGET_PC || VERSION == VERSION_GCN_PAL
|
#if TARGET_PC || VERSION == VERSION_GCN_PAL
|
||||||
BOOL isEnglish = FALSE;
|
BOOL isEnglish = FALSE;
|
||||||
if (dusk::version::isRegionUsa() || (dusk::version::isRegionPal() && dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH)) {
|
if (dusk::version::isRegionPal() && dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
|
||||||
isEnglish = TRUE;
|
isEnglish = TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "d/d_s_name.h"
|
#include "d/d_s_name.h"
|
||||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||||
#include "dusk/memory.h"
|
#include "dusk/memory.h"
|
||||||
|
#include "dusk/speedrun.h"
|
||||||
#include "dusk/settings.h"
|
#include "dusk/settings.h"
|
||||||
#include "f_op/f_op_overlap_mng.h"
|
#include "f_op/f_op_overlap_mng.h"
|
||||||
#include "f_op/f_op_scene_mng.h"
|
#include "f_op/f_op_scene_mng.h"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include "m_Do/m_Do_machine.h"
|
#include "m_Do/m_Do_machine.h"
|
||||||
#include "m_Do/m_Do_main.h"
|
#include "m_Do/m_Do_main.h"
|
||||||
#include "m_Do/m_Do_mtx.h"
|
#include "m_Do/m_Do_mtx.h"
|
||||||
|
#include <dusk/autosave.h>
|
||||||
|
|
||||||
#if TARGET_PC
|
#if TARGET_PC
|
||||||
#define SHOW_TV_SETTINGS_SCREEN (this->mShowTvSettingsScreen)
|
#define SHOW_TV_SETTINGS_SCREEN (this->mShowTvSettingsScreen)
|
||||||
@@ -418,10 +420,12 @@ void dScnName_c::changeGameScene() {
|
|||||||
if (dusk::getSettings().game.speedrunMode && dusk::getSettings().game.hideTvSettingsScreen) {
|
if (dusk::getSettings().game.speedrunMode && dusk::getSettings().game.hideTvSettingsScreen) {
|
||||||
// start a new run on file load if a run isn't already in progress
|
// start a new run on file load if a run isn't already in progress
|
||||||
if (!dusk::m_speedrunInfo.m_isRunStarted) {
|
if (!dusk::m_speedrunInfo.m_isRunStarted) {
|
||||||
dusk::ImGuiMenuGame::resetForSpeedrunMode();
|
dusk::resetForSpeedrunMode();
|
||||||
dusk::m_speedrunInfo.startRun();
|
dusk::m_speedrunInfo.startRun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAutoSave(true);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1042,6 +1042,10 @@ static BOOL heapSizeCheck() {
|
|||||||
|
|
||||||
bool dScnPly_c::resetGame() {
|
bool dScnPly_c::resetGame() {
|
||||||
if (fpcM_GetName(this) == fpcNm_OPENING_SCENE_e) {
|
if (fpcM_GetName(this) == fpcNm_OPENING_SCENE_e) {
|
||||||
|
#if TARGET_PC
|
||||||
|
toggleAutoSave(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!dStage_roomControl_c::resetArchiveBank(0)) {
|
if (!dStage_roomControl_c::resetArchiveBank(0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ static std::string FormatToString(const char* msg, va_list list) {
|
|||||||
size *= 2;
|
size *= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (!str.empty() && str[str.size()-1] == '\n') {
|
||||||
|
str.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -692,6 +692,12 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prevent stuff like https://github.com/TwilitRealm/dusklight/issues/949
|
||||||
|
if (link->getDemoMode() != 0) {
|
||||||
|
inJump = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!inJump) {
|
if (!inJump) {
|
||||||
if (link->mProcID == daAlink_c::PROC_CUT_JUMP) {
|
if (link->mProcID == daAlink_c::PROC_CUT_JUMP) {
|
||||||
inJump = true;
|
inJump = true;
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#include "dusk/action_bindings.h"
|
||||||
|
|
||||||
|
#include "aurora/lib/input.hpp"
|
||||||
|
#include "dusk/settings.h"
|
||||||
|
#include "dusk/ui/ui.hpp"
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
|
||||||
|
static std::array<std::array<ActionBindPressData, static_cast<int>(ActionBinds::COUNT)>, PAD_CHANMAX> actionPressData{};
|
||||||
|
|
||||||
|
ActionBindsMap& getActionBinds() {
|
||||||
|
static ActionBindsMap actionBinds = {
|
||||||
|
{ActionBinds::FIRST_PERSON_CAMERA, {&getSettings().actionBindings.firstPersonCamera, "First Person Camera"}},
|
||||||
|
{ActionBinds::CALL_MIDNA, {&getSettings().actionBindings.callMidna, "Call Midna"}},
|
||||||
|
{ActionBinds::OPEN_DUSKLIGHT_MENU, {&getSettings().actionBindings.openDusklightMenu, "Open Dusklight Menu"}},
|
||||||
|
{ActionBinds::TURBO_SPEED_BUTTON, {&getSettings().actionBindings.turboSpeedButton, "Turbo Speed Button"}},
|
||||||
|
};
|
||||||
|
return actionBinds;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActionBound(ActionBinds action, u32 port) {
|
||||||
|
auto& actionBinds = getActionBinds();
|
||||||
|
// Check to make sure action is properly bound
|
||||||
|
if (!actionBinds.contains(action)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getActionBindButton(action, port) != PAD_NATIVE_BUTTON_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateActionBindings() {
|
||||||
|
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
|
||||||
|
// Move the current press to the previous frame
|
||||||
|
for (auto& pressData : actionPressData[port]) {
|
||||||
|
pressData.pressedPrevFrame = pressData.pressedCurFrame;
|
||||||
|
pressData.pressedCurFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update current frame with whether action button is pressed
|
||||||
|
for (auto& [action, boundAction] : getActionBinds()) {
|
||||||
|
// If the action isn't bound, or if documents are visible and the action isn't
|
||||||
|
// opening the dusklight menu, don't update. Otherwise, we may accidentally
|
||||||
|
// perform actions while the dusklight menu is open.
|
||||||
|
if (!isActionBound(action, port) ||
|
||||||
|
(ui::any_document_visible() && action != ActionBinds::OPEN_DUSKLIGHT_MENU)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int button = boundAction.configVars->at(port);
|
||||||
|
|
||||||
|
// If keyboard is active for this port
|
||||||
|
u32 count = 0;
|
||||||
|
if (PADGetKeyButtonBindings(port, &count) != nullptr) {
|
||||||
|
int numKeys = 0;
|
||||||
|
const bool* kbState = SDL_GetKeyboardState(&numKeys);
|
||||||
|
if (kbState[button]) {
|
||||||
|
actionPressData[port][static_cast<int>(action)].pressedCurFrame = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If controller is active
|
||||||
|
auto controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller) {
|
||||||
|
if (SDL_GetGamepadButton(controller->m_controller, static_cast<SDL_GamepadButton>(button))) {
|
||||||
|
actionPressData[port][static_cast<int>(action)].pressedCurFrame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getActionBindTrig(ActionBinds action, u32 port) {
|
||||||
|
return isActionBound(action, port) &&
|
||||||
|
actionPressData[port][static_cast<int>(action)].pressedCurFrame &&
|
||||||
|
!actionPressData[port][static_cast<int>(action)].pressedPrevFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getActionBindHold(ActionBinds action, u32 port) {
|
||||||
|
return isActionBound(action, port) &&
|
||||||
|
actionPressData[port][static_cast<int>(action)].pressedCurFrame &&
|
||||||
|
actionPressData[port][static_cast<int>(action)].pressedPrevFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getActionBindHoldAnyPort(ActionBinds action) {
|
||||||
|
for (u32 port = 0; port < PAD_CHANMAX; ++port) {
|
||||||
|
if (getActionBindHold(action, port)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getActionBindButton(ActionBinds action, u32 port) {
|
||||||
|
return (*getActionBinds()[action].configVars)[port];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "dusk/ui/ui.hpp"
|
#include "dusk/ui/ui.hpp"
|
||||||
#include "imgui/ImGuiConsole.hpp"
|
#include "imgui/ImGuiConsole.hpp"
|
||||||
|
|
||||||
|
bool shouldAutoSave = false;
|
||||||
u8 mSaveBuffer[QUEST_LOG_SIZE * 3];
|
u8 mSaveBuffer[QUEST_LOG_SIZE * 3];
|
||||||
u8 mAutoSaveProc = 0;
|
u8 mAutoSaveProc = 0;
|
||||||
int autoSaveWriteState = 0;
|
int autoSaveWriteState = 0;
|
||||||
@@ -14,7 +15,7 @@ static AutoSaveFuncs AutoSaveFuncsProc[] = {
|
|||||||
void noAutoSave() {}
|
void noAutoSave() {}
|
||||||
|
|
||||||
void triggerAutoSave() {
|
void triggerAutoSave() {
|
||||||
if (dusk::getSettings().game.autoSave && mAutoSaveProc == 0 &&
|
if (dusk::getSettings().game.autoSave && shouldAutoSave && mAutoSaveProc == 0 &&
|
||||||
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0)
|
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0)
|
||||||
{
|
{
|
||||||
mAutoSaveProc = 1;
|
mAutoSaveProc = 1;
|
||||||
@@ -25,8 +26,12 @@ void updateAutoSave() {
|
|||||||
(AutoSaveFuncsProc[mAutoSaveProc])();
|
(AutoSaveFuncsProc[mAutoSaveProc])();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeAutoSave() {
|
bool writeAutoSave() {
|
||||||
int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo());
|
stage_stag_info_class* stagInfo = dComIfGp_getStageStagInfo();
|
||||||
|
if (stagInfo == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int stageNo = dStage_stagInfo_GetSaveTbl(stagInfo);
|
||||||
|
|
||||||
dComIfGs_putSave(stageNo);
|
dComIfGs_putSave(stageNo);
|
||||||
dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum());
|
dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum());
|
||||||
@@ -39,6 +44,7 @@ void writeAutoSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void autoSaving() {
|
void autoSaving() {
|
||||||
@@ -47,8 +53,9 @@ void autoSaving() {
|
|||||||
if (cardState == 2) {
|
if (cardState == 2) {
|
||||||
mAutoSaveProc = 1;
|
mAutoSaveProc = 1;
|
||||||
} else if (cardState == 1) {
|
} else if (cardState == 1) {
|
||||||
writeAutoSave();
|
if (writeAutoSave()) {
|
||||||
mAutoSaveProc = 3;
|
mAutoSaveProc = 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,4 +96,8 @@ void endAutoSave() {
|
|||||||
.duration = std::chrono::milliseconds(1500),
|
.duration = std::chrono::milliseconds(1500),
|
||||||
});
|
});
|
||||||
mAutoSaveProc = 0;
|
mAutoSaveProc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleAutoSave(bool enabled) {
|
||||||
|
shouldAutoSave = enabled;
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "dusk/main.h"
|
#include "dusk/main.h"
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
|
|
||||||
using namespace dusk::config;
|
using namespace dusk::config;
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) {
|
|||||||
|
|
||||||
template<ConfigValue T>
|
template<ConfigValue T>
|
||||||
nlohmann::json ConfigImpl<T>::dumpToJson(const ConfigVar<T>& cVar) {
|
nlohmann::json ConfigImpl<T>::dumpToJson(const ConfigVar<T>& cVar) {
|
||||||
return cVar.getValue();
|
return cVar.getValueForSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<ConfigValue T> requires std::is_integral_v<T> && std::is_signed_v<T>
|
template<ConfigValue T> requires std::is_integral_v<T> && std::is_signed_v<T>
|
||||||
@@ -248,7 +249,8 @@ void dusk::config::Save() {
|
|||||||
json j;
|
json j;
|
||||||
|
|
||||||
for (const auto& pair : RegisteredConfigVars) {
|
for (const auto& pair : RegisteredConfigVars) {
|
||||||
if (pair.second->getLayer() == ConfigVarLayer::Value) {
|
const auto layer = pair.second->getLayer();
|
||||||
|
if (layer == ConfigVarLayer::Value || layer == ConfigVarLayer::Speedrun) {
|
||||||
j[pair.first] = pair.second->getImpl()->dumpToJson(*pair.second);
|
j[pair.first] = pair.second->getImpl()->dumpToJson(*pair.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,6 +258,13 @@ void dusk::config::Save() {
|
|||||||
io::FileStream::WriteAllText(reinterpret_cast<const char*>(configJsonPath.c_str()), j.dump(4));
|
io::FileStream::WriteAllText(reinterpret_cast<const char*>(configJsonPath.c_str()), j.dump(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dusk::config::ClearAllActionBindings(int port) {
|
||||||
|
for (auto& actionBinding : getActionBinds() | std::views::values) {
|
||||||
|
actionBinding.configVars->at(port).setValue(PAD_NATIVE_BUTTON_INVALID);
|
||||||
|
}
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
|
ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
|
||||||
const auto configVar = RegisteredConfigVars.find(name);
|
const auto configVar = RegisteredConfigVars.find(name);
|
||||||
if (configVar != RegisteredConfigVars.end()) {
|
if (configVar != RegisteredConfigVars.end()) {
|
||||||
@@ -264,3 +273,9 @@ ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
|
|||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dusk::config::EnumerateRegistered(std::function<void(ConfigVarBase&)> callback) {
|
||||||
|
for (auto& pair : RegisteredConfigVars) {
|
||||||
|
callback(*pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ std::string release_name() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path sentry_database_path() {
|
std::filesystem::path sentry_database_path() {
|
||||||
return dusk::ConfigPath / "sentry";
|
return dusk::CachePath / "sentry";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path log_attachment_path() {
|
std::filesystem::path log_attachment_path() {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || \
|
||||||
|
(defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST) || \
|
||||||
|
(defined(__linux__) && !defined(__ANDROID__))
|
||||||
|
#define DUSK_CAN_OPEN_DATA_FOLDER 1
|
||||||
|
#else
|
||||||
|
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || defined(__ANDROID__)
|
||||||
|
#define DUSK_CAN_CHANGE_DATA_FOLDER 0
|
||||||
|
#else
|
||||||
|
#define DUSK_CAN_CHANGE_DATA_FOLDER 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace dusk::data {
|
||||||
|
|
||||||
|
struct Paths {
|
||||||
|
std::filesystem::path userPath;
|
||||||
|
std::filesystem::path cachePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
Paths initialize_data();
|
||||||
|
std::filesystem::path configured_data_path();
|
||||||
|
std::filesystem::path cache_path();
|
||||||
|
bool open_data_path();
|
||||||
|
bool set_custom_data_path(const char* path, std::string* errorOut);
|
||||||
|
bool set_custom_data_path(const std::filesystem::path& path, std::string* errorOut);
|
||||||
|
bool set_portable_data_path();
|
||||||
|
bool reset_data_path();
|
||||||
|
bool is_default_data_path();
|
||||||
|
bool is_data_path_restart_pending();
|
||||||
|
|
||||||
|
} // namespace dusk::data
|
||||||
@@ -81,7 +81,7 @@ void update_presence() {
|
|||||||
rpc::Presence presence{};
|
rpc::Presence presence{};
|
||||||
presence.startTimestamp = g_startTime;
|
presence.startTimestamp = g_startTime;
|
||||||
presence.largeImageKey = "icon";
|
presence.largeImageKey = "icon";
|
||||||
presence.largeImageText = "Dusk";
|
presence.largeImageText = "Dusklight";
|
||||||
|
|
||||||
if (IsGameLaunched) {
|
if (IsGameLaunched) {
|
||||||
const char* stageName = dComIfGp_getLastPlayStageName();
|
const char* stageName = dComIfGp_getLastPlayStageName();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL_dialog.h>
|
#include <SDL3/SDL_dialog.h>
|
||||||
#include <SDL3/SDL_error.h>
|
#include <SDL3/SDL_error.h>
|
||||||
|
#include <SDL3/SDL_init.h>
|
||||||
#include <SDL3/SDL_stdinc.h>
|
#include <SDL3/SDL_stdinc.h>
|
||||||
|
|
||||||
#if defined(__ANDROID__) || defined(ANDROID)
|
#if defined(__ANDROID__) || defined(ANDROID)
|
||||||
@@ -16,6 +17,12 @@
|
|||||||
#include <TargetConditionals.h>
|
#include <TargetConditionals.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_MACCATALYST
|
||||||
|
#define USE_MACOS_FOLDER_DIALOG 1
|
||||||
|
#else
|
||||||
|
#define USE_MACOS_FOLDER_DIALOG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
#if defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
||||||
#define USE_IOS_DIALOG 1
|
#define USE_IOS_DIALOG 1
|
||||||
#include "ios/FileSelectDialog.h"
|
#include "ios/FileSelectDialog.h"
|
||||||
@@ -23,6 +30,13 @@
|
|||||||
#define USE_IOS_DIALOG 0
|
#define USE_IOS_DIALOG 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if USE_MACOS_FOLDER_DIALOG
|
||||||
|
namespace dusk {
|
||||||
|
bool ShowMacOSFolderSelect(
|
||||||
|
FileCallback callback, void* userdata, SDL_Window* window, const char* default_location);
|
||||||
|
} // namespace dusk
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -32,6 +46,10 @@ std::string fallback_display_name(std::string_view path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string pathString(path);
|
std::string pathString(path);
|
||||||
|
while (pathString.size() > 1 && (pathString.back() == '/' || pathString.back() == '\\')) {
|
||||||
|
pathString.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
const std::size_t slash = pathString.find_last_of("/\\");
|
const std::size_t slash = pathString.find_last_of("/\\");
|
||||||
if (slash == std::string::npos || slash + 1 >= pathString.size()) {
|
if (slash == std::string::npos || slash + 1 >= pathString.size()) {
|
||||||
return pathString;
|
return pathString;
|
||||||
@@ -98,8 +116,7 @@ std::string android_display_name(std::string_view path) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* displayName =
|
auto* displayName = static_cast<jstring>(env->CallObjectMethod(activity, getDisplayName, uri));
|
||||||
static_cast<jstring>(env->CallObjectMethod(activity, getDisplayName, uri));
|
|
||||||
env->DeleteLocalRef(uri);
|
env->DeleteLocalRef(uri);
|
||||||
env->DeleteLocalRef(activity);
|
env->DeleteLocalRef(activity);
|
||||||
if (displayName == nullptr || clear_pending_exception(env)) {
|
if (displayName == nullptr || clear_pending_exception(env)) {
|
||||||
@@ -110,6 +127,76 @@ std::string android_display_name(std::string_view path) {
|
|||||||
env->DeleteLocalRef(displayName);
|
env->DeleteLocalRef(displayName);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AndroidFolderDialogState {
|
||||||
|
FileCallback callback;
|
||||||
|
void* userdata;
|
||||||
|
std::string path;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
void onAndroidFolderDialogFinished(void* userdata) {
|
||||||
|
std::unique_ptr<AndroidFolderDialogState> state(
|
||||||
|
static_cast<AndroidFolderDialogState*>(userdata));
|
||||||
|
|
||||||
|
const char* path = state->path.empty() ? nullptr : state->path.c_str();
|
||||||
|
const char* error = state->error.empty() ? nullptr : state->error.c_str();
|
||||||
|
state->callback(state->userdata, path, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool show_android_folder_select(AndroidFolderDialogState* state) {
|
||||||
|
auto* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
|
||||||
|
if (env == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject activity = static_cast<jobject>(SDL_GetAndroidActivity());
|
||||||
|
if (activity == nullptr || clear_pending_exception(env)) {
|
||||||
|
if (activity != nullptr) {
|
||||||
|
env->DeleteLocalRef(activity);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass activityClass = env->GetObjectClass(activity);
|
||||||
|
if (activityClass == nullptr || clear_pending_exception(env)) {
|
||||||
|
env->DeleteLocalRef(activity);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID showFolderDialog =
|
||||||
|
env->GetMethodID(activityClass, "showFolderDialog", "(J)Z");
|
||||||
|
env->DeleteLocalRef(activityClass);
|
||||||
|
if (showFolderDialog == nullptr || clear_pending_exception(env)) {
|
||||||
|
env->DeleteLocalRef(activity);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jboolean shown = env->CallBooleanMethod(
|
||||||
|
activity, showFolderDialog, reinterpret_cast<jlong>(state));
|
||||||
|
env->DeleteLocalRef(activity);
|
||||||
|
if (clear_pending_exception(env)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shown == JNI_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
Java_dev_twilitrealm_dusk_DuskActivity_nativeFolderDialogResult(
|
||||||
|
JNIEnv* env, jclass, jlong userdata, jstring path, jstring error) {
|
||||||
|
auto* state = reinterpret_cast<AndroidFolderDialogState*>(userdata);
|
||||||
|
if (state == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->path = to_string(env, path);
|
||||||
|
state->error = to_string(env, error);
|
||||||
|
|
||||||
|
if (!SDL_RunOnMainThread(&onAndroidFolderDialogFinished, state, false)) {
|
||||||
|
onAndroidFolderDialogFinished(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_IOS_DIALOG
|
#if USE_IOS_DIALOG
|
||||||
@@ -159,8 +246,8 @@ void onSDLDialogFinished(void* userdata, const char* const* filelist, [[maybe_un
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
|
void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
|
||||||
const SDL_DialogFileFilter* filters, int nfilters, const char* default_location,
|
const SDL_DialogFileFilter* filters, int nfilters, const char* default_location,
|
||||||
bool allow_many) {
|
bool allow_many) {
|
||||||
if (callback == nullptr) {
|
if (callback == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -171,14 +258,45 @@ void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
|
|||||||
state->userdata = userdata;
|
state->userdata = userdata;
|
||||||
|
|
||||||
Dusk_iOS_ShowFileSelect(&onIOSDialogFinished, state.release(), window, filters, nfilters,
|
Dusk_iOS_ShowFileSelect(&onIOSDialogFinished, state.release(), window, filters, nfilters,
|
||||||
default_location, allow_many);
|
default_location, allow_many);
|
||||||
#else
|
#else
|
||||||
auto state = std::make_unique<SDLDialogCallbackState>();
|
auto state = std::make_unique<SDLDialogCallbackState>();
|
||||||
state->callback = callback;
|
state->callback = callback;
|
||||||
state->userdata = userdata;
|
state->userdata = userdata;
|
||||||
|
|
||||||
SDL_ShowOpenFileDialog(&onSDLDialogFinished, state.release(), window, filters, nfilters,
|
SDL_ShowOpenFileDialog(&onSDLDialogFinished, state.release(), window, filters, nfilters,
|
||||||
default_location, allow_many);
|
default_location, allow_many);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowFolderSelect(
|
||||||
|
FileCallback callback, void* userdata, SDL_Window* window, const char* default_location) {
|
||||||
|
if (callback == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_IOS_DIALOG
|
||||||
|
callback(userdata, nullptr, "Folder selection is not supported on this platform");
|
||||||
|
#elif USE_MACOS_FOLDER_DIALOG
|
||||||
|
ShowMacOSFolderSelect(callback, userdata, window, default_location);
|
||||||
|
#elif defined(__ANDROID__) || defined(ANDROID)
|
||||||
|
auto state = std::make_unique<AndroidFolderDialogState>();
|
||||||
|
state->callback = callback;
|
||||||
|
state->userdata = userdata;
|
||||||
|
|
||||||
|
if (show_android_folder_select(state.get())) {
|
||||||
|
state.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(userdata, nullptr, "Folder selection is not supported on this platform");
|
||||||
|
#else
|
||||||
|
auto state = std::make_unique<SDLDialogCallbackState>();
|
||||||
|
state->callback = callback;
|
||||||
|
state->userdata = userdata;
|
||||||
|
|
||||||
|
SDL_ShowOpenFolderDialog(
|
||||||
|
&onSDLDialogFinished, state.release(), window, default_location, false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ using FileCallback = void (*)(void* userdata, const char* path, const char* erro
|
|||||||
void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
|
void ShowFileSelect(FileCallback callback, void* userdata, SDL_Window* window,
|
||||||
const SDL_DialogFileFilter* filters, int nfilters, const char* default_location,
|
const SDL_DialogFileFilter* filters, int nfilters, const char* default_location,
|
||||||
bool allow_many);
|
bool allow_many);
|
||||||
|
void ShowFolderSelect(
|
||||||
|
FileCallback callback, void* userdata, SDL_Window* window, const char* default_location);
|
||||||
|
|
||||||
std::string display_name_for_path(std::string_view path);
|
std::string display_name_for_path(std::string_view path);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
#include "file_select.hpp"
|
||||||
|
|
||||||
|
#include <SDL3/SDL_properties.h>
|
||||||
|
#include <SDL3/SDL_video.h>
|
||||||
|
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct MacOSFolderDialogState {
|
||||||
|
FileCallback callback;
|
||||||
|
void* userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
void finish_folder_dialog(MacOSFolderDialogState* state, NSURL* url, const char* error) {
|
||||||
|
if (state == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != nullptr) {
|
||||||
|
state->callback(state->userdata, nullptr, error);
|
||||||
|
delete state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == nil) {
|
||||||
|
state->callback(state->userdata, nullptr, nullptr);
|
||||||
|
delete state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->callback(state->userdata, [[url path] UTF8String], nullptr);
|
||||||
|
delete state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure_default_location(NSOpenPanel* panel, const char* defaultLocation) {
|
||||||
|
if (panel == nil || defaultLocation == nullptr || defaultLocation[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* path = [NSString stringWithUTF8String:defaultLocation];
|
||||||
|
if (path == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isDirectory = NO;
|
||||||
|
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||||
|
NSURL* url = [NSURL fileURLWithPath:path];
|
||||||
|
if ([fileManager fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory) {
|
||||||
|
[panel setDirectoryURL:url];
|
||||||
|
} else {
|
||||||
|
[panel setDirectoryURL:[url URLByDeletingLastPathComponent]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSWindow* window_for_sdl_window(SDL_Window* window) {
|
||||||
|
if (window == nullptr) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto props = SDL_GetWindowProperties(window);
|
||||||
|
return (__bridge NSWindow*)SDL_GetPointerProperty(
|
||||||
|
props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool ShowMacOSFolderSelect(
|
||||||
|
FileCallback callback, void* userdata, SDL_Window* window, const char* defaultLocation) {
|
||||||
|
if (callback == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* state = new MacOSFolderDialogState{
|
||||||
|
.callback = callback,
|
||||||
|
.userdata = userdata,
|
||||||
|
};
|
||||||
|
|
||||||
|
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
||||||
|
[panel setCanChooseFiles:NO];
|
||||||
|
[panel setCanChooseDirectories:YES];
|
||||||
|
[panel setAllowsMultipleSelection:NO];
|
||||||
|
[panel setCanCreateDirectories:YES];
|
||||||
|
configure_default_location(panel, defaultLocation);
|
||||||
|
|
||||||
|
NSWindow* modalWindow = window_for_sdl_window(window);
|
||||||
|
if (modalWindow != nil) {
|
||||||
|
[panel beginSheetModalForWindow:modalWindow
|
||||||
|
completionHandler:^(NSModalResponse result) {
|
||||||
|
finish_folder_dialog(
|
||||||
|
state, result == NSModalResponseOK ? [panel URL] : nil, nullptr);
|
||||||
|
}];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NSModalResponse result = [panel runModal];
|
||||||
|
finish_folder_dialog(state, result == NSModalResponseOK ? [panel URL] : nil, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk
|
||||||
@@ -13,8 +13,10 @@
|
|||||||
#include "ImGuiEngine.hpp"
|
#include "ImGuiEngine.hpp"
|
||||||
#include "JSystem/JUtility/JUTGamePad.h"
|
#include "JSystem/JUtility/JUTGamePad.h"
|
||||||
#include "SDL3/SDL_mouse.h"
|
#include "SDL3/SDL_mouse.h"
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
#include "dusk/audio/DuskAudioSystem.h"
|
#include "dusk/audio/DuskAudioSystem.h"
|
||||||
#include "dusk/config.hpp"
|
#include "dusk/config.hpp"
|
||||||
|
#include "dusk/data.hpp"
|
||||||
#include "dusk/dusk.h"
|
#include "dusk/dusk.h"
|
||||||
#include "dusk/frame_interpolation.h"
|
#include "dusk/frame_interpolation.h"
|
||||||
#include "dusk/livesplit.h"
|
#include "dusk/livesplit.h"
|
||||||
@@ -238,7 +240,8 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiConsole::UpdateSettings() {
|
void ImGuiConsole::UpdateSettings() {
|
||||||
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab);
|
getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind &&
|
||||||
|
(ImGui::IsKeyDown(ImGuiKey_Tab) || getActionBindHoldAnyPort(ActionBinds::TURBO_SPEED_BUTTON));
|
||||||
|
|
||||||
if (dusk::frame_interp::get_ui_tick_pending() && mDoMain::developmentMode == 1 && (mDoCPd_c::getHold(PAD_1) & (PAD_TRIGGER_R | PAD_TRIGGER_L)) == (PAD_TRIGGER_R | PAD_TRIGGER_L) && mDoCPd_c::getTrigY(PAD_1)) {
|
if (dusk::frame_interp::get_ui_tick_pending() && mDoMain::developmentMode == 1 && (mDoCPd_c::getHold(PAD_1) & (PAD_TRIGGER_R | PAD_TRIGGER_L)) == (PAD_TRIGGER_R | PAD_TRIGGER_L) && mDoCPd_c::getTrigY(PAD_1)) {
|
||||||
getTransientSettings().moveLinkActive = !getTransientSettings().moveLinkActive;
|
getTransientSettings().moveLinkActive = !getTransientSettings().moveLinkActive;
|
||||||
@@ -259,6 +262,12 @@ namespace dusk {
|
|||||||
config::Save();
|
config::Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getSettings().game.enableResetKeybind && ImGui::GetIO().KeyCtrl &&
|
||||||
|
ImGui::IsKeyPressed(ImGuiKey_R) && !fpcM_SearchByName(fpcNm_LOGO_SCENE_e))
|
||||||
|
{
|
||||||
|
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::GetIO().KeyShift && ImGui::IsKeyPressed(ImGuiKey_F1)) {
|
if (ImGui::GetIO().KeyShift && ImGui::IsKeyPressed(ImGuiKey_F1)) {
|
||||||
if (getSettings().backend.enableAdvancedSettings) {
|
if (getSettings().backend.enableAdvancedSettings) {
|
||||||
m_isHidden = !m_isHidden;
|
m_isHidden = !m_isHidden;
|
||||||
@@ -273,7 +282,6 @@ namespace dusk {
|
|||||||
// so make the window bg fully transparent temporarily
|
// so make the window bg fully transparent temporarily
|
||||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||||
if (showMenu && ImGui::BeginMainMenuBar()) {
|
if (showMenu && ImGui::BeginMainMenuBar()) {
|
||||||
m_menuGame.draw();
|
|
||||||
m_menuTools.draw();
|
m_menuTools.draw();
|
||||||
|
|
||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
@@ -282,7 +290,7 @@ namespace dusk {
|
|||||||
|
|
||||||
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
||||||
m_isLaunchInitialized = true;
|
m_isLaunchInitialized = true;
|
||||||
if (getSettings().game.liveSplitEnabled) {
|
if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) {
|
||||||
dusk::speedrun::connectLiveSplit();
|
dusk::speedrun::connectLiveSplit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,13 +317,13 @@ namespace dusk {
|
|||||||
ImGui::Image(ImGuiEngine::duskLogo, ImVec2{width, iconSize});
|
ImGui::Image(ImGuiEngine::duskLogo, ImVec2{width, iconSize});
|
||||||
} else {
|
} else {
|
||||||
ImGui::PushFont(ImGuiEngine::fontExtraLarge);
|
ImGui::PushFont(ImGuiEngine::fontExtraLarge);
|
||||||
ImGuiTextCenter("Dusk");
|
ImGuiTextCenter("Dusklight");
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
ImGui::PushFont(ImGuiEngine::fontLarge);
|
ImGui::PushFont(ImGuiEngine::fontLarge);
|
||||||
ImGuiTextCenter("Failed to initialize any graphics backend.");
|
ImGuiTextCenter("Failed to initialize any graphics backend.");
|
||||||
ImGuiTextCenter("\nYour system may be misconfigured, or your hardware may not support the required versions of any of the available backends.");
|
ImGuiTextCenter("\nYour system may be misconfigured, or your hardware may not support the required versions of any of the available backends.");
|
||||||
ImGuiTextCenter("\nA clean reinstall of Dusk may help. For further assistance, please visit #tech-support on the Twilit Realm Discord server.");
|
ImGuiTextCenter("\nA clean reinstall of Dusklight may help. For further assistance, please visit #tech-support on the Twilit Realm Discord server.");
|
||||||
const auto& style = ImGui::GetStyle();
|
const auto& style = ImGui::GetStyle();
|
||||||
const auto retrySize = ImGui::CalcTextSize("Retry (Auto backend)");
|
const auto retrySize = ImGui::CalcTextSize("Retry (Auto backend)");
|
||||||
const auto quitSize = ImGui::CalcTextSize("Quit");
|
const auto quitSize = ImGui::CalcTextSize("Quit");
|
||||||
@@ -341,7 +349,7 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
#if DUSK_CAN_OPEN_DATA_FOLDER
|
#if DUSK_CAN_OPEN_DATA_FOLDER
|
||||||
if (ImGui::Button("Open Data Folder")) {
|
if (ImGui::Button("Open Data Folder")) {
|
||||||
OpenDataFolder();
|
data::open_data_path();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
#endif
|
#endif
|
||||||
@@ -353,15 +361,6 @@ namespace dusk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_menuTools.ShowInputViewer();
|
m_menuTools.ShowInputViewer();
|
||||||
m_menuGame.drawSpeedrunTimerOverlay();
|
|
||||||
|
|
||||||
if (getSettings().game.liveSplitEnabled) {
|
|
||||||
dusk::speedrun::updateLiveSplit();
|
|
||||||
if (dusk::speedrun::consumeConnectedEvent())
|
|
||||||
AddToast("LiveSplit connected");
|
|
||||||
else if (dusk::speedrun::consumeDisconnectedEvent())
|
|
||||||
AddToast("LiveSplit disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dusk::IsGameLaunched && !dusk::getSettings().game.speedrunMode) {
|
if (dusk::IsGameLaunched && !dusk::getSettings().game.speedrunMode) {
|
||||||
m_menuTools.ShowDebugOverlay();
|
m_menuTools.ShowDebugOverlay();
|
||||||
@@ -369,7 +368,6 @@ namespace dusk {
|
|||||||
m_menuTools.ShowProcessManager();
|
m_menuTools.ShowProcessManager();
|
||||||
m_menuTools.ShowHeapOverlay();
|
m_menuTools.ShowHeapOverlay();
|
||||||
m_menuTools.ShowStubLog();
|
m_menuTools.ShowStubLog();
|
||||||
m_menuTools.ShowMapLoader();
|
|
||||||
m_menuTools.ShowBloomWindow();
|
m_menuTools.ShowBloomWindow();
|
||||||
m_menuTools.ShowPlayerInfo();
|
m_menuTools.ShowPlayerInfo();
|
||||||
m_menuTools.ShowAudioDebug();
|
m_menuTools.ShowAudioDebug();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include <aurora/aurora.h>
|
#include <aurora/aurora.h>
|
||||||
|
|
||||||
#include "ImGuiMenuGame.hpp"
|
|
||||||
#include "ImGuiMenuTools.hpp"
|
#include "ImGuiMenuTools.hpp"
|
||||||
#include "dusk/main.h"
|
#include "dusk/main.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
@@ -44,8 +43,6 @@ private:
|
|||||||
ImVec2 m_dragScrollLastMousePos = {};
|
ImVec2 m_dragScrollLastMousePos = {};
|
||||||
std::deque<Toast> m_toasts;
|
std::deque<Toast> m_toasts;
|
||||||
|
|
||||||
ImGuiMenuGame m_menuGame;
|
|
||||||
|
|
||||||
// Keep always last
|
// Keep always last
|
||||||
ImGuiMenuTools m_menuTools;
|
ImGuiMenuTools m_menuTools;
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include "ImGuiConsole.hpp"
|
#include "ImGuiConsole.hpp"
|
||||||
|
#include "dusk/settings.h"
|
||||||
|
|
||||||
#include <dolphin/pad.h>
|
#include <dolphin/pad.h>
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
void ImGuiMenuTools::ShowInputViewer() {
|
void ImGuiMenuTools::ShowInputViewer() {
|
||||||
if (!m_showInputViewer) {
|
if (!getSettings().game.showInputViewer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,10 +260,10 @@ namespace dusk {
|
|||||||
size.y = 130 * scale;
|
size.y = 130 * scale;
|
||||||
ImGui::Dummy(size);
|
ImGui::Dummy(size);
|
||||||
|
|
||||||
if (PADHasSensor(PAD_1, PAD_SENSOR_GYRO) == TRUE) {
|
if (getSettings().game.showInputViewerGyro)
|
||||||
|
{
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Checkbox("Gyro Values", &m_showInputViewerGyro);
|
{
|
||||||
if (m_showInputViewerGyro) {
|
|
||||||
ImGui::TextUnformatted("Gyro");
|
ImGui::TextUnformatted("Gyro");
|
||||||
|
|
||||||
constexpr float kBarScale = 4.0f;
|
constexpr float kBarScale = 4.0f;
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ void ImGuiEngine_AddTextures() {
|
|||||||
ImGuiEngine::orgIcon = AddTexture("org-icon.png");
|
ImGuiEngine::orgIcon = AddTexture("org-icon.png");
|
||||||
}
|
}
|
||||||
if (ImGuiEngine::duskLogo == 0) {
|
if (ImGuiEngine::duskLogo == 0) {
|
||||||
ImGuiEngine::duskLogo = AddTexture("logo-mascot.png");
|
ImGuiEngine::duskLogo = AddTexture("logo.png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace dusk
|
} // namespace dusk
|
||||||
|
|||||||
@@ -1,149 +0,0 @@
|
|||||||
#include "d/d_com_inf_game.h"
|
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
#include <imgui_internal.h>
|
|
||||||
#include "ImGuiConsole.hpp"
|
|
||||||
#include "ImGuiMenuTools.hpp"
|
|
||||||
#include "dusk/map_loader_definitions.h"
|
|
||||||
#include "fmt/format.h"
|
|
||||||
|
|
||||||
namespace dusk {
|
|
||||||
void ImGuiMenuTools::ShowMapLoader() {
|
|
||||||
if (!getSettings().backend.enableAdvancedSettings ||
|
|
||||||
!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showMapLoader))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize |
|
|
||||||
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
|
|
||||||
|
|
||||||
// ImGui::SetNextWindowBgAlpha(0.65f);
|
|
||||||
|
|
||||||
if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) {
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Checkbox("Show Internal Names", &m_mapLoaderInfo.showInternalNames);
|
|
||||||
|
|
||||||
const char* previewRegion = "None";
|
|
||||||
if (m_mapLoaderInfo.regionIdx != -1) {
|
|
||||||
previewRegion = gameRegions[m_mapLoaderInfo.regionIdx].regionName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginCombo("Select Region", previewRegion)) {
|
|
||||||
int idx = 0;
|
|
||||||
for (const auto& region : gameRegions) {
|
|
||||||
if (ImGui::Selectable(region.regionName)) {
|
|
||||||
if (m_mapLoaderInfo.regionIdx != idx) {
|
|
||||||
m_mapLoaderInfo.mapIdx = 0;
|
|
||||||
m_mapLoaderInfo.roomNoIdx = 0;
|
|
||||||
m_mapLoaderInfo.pointNoIdx = 0;
|
|
||||||
}
|
|
||||||
m_mapLoaderInfo.regionIdx = idx;
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_mapLoaderInfo.regionIdx != -1) {
|
|
||||||
const auto& region = gameRegions[m_mapLoaderInfo.regionIdx];
|
|
||||||
|
|
||||||
std::string previewMap = "None";
|
|
||||||
if (m_mapLoaderInfo.mapIdx != -1) {
|
|
||||||
const auto& map = region.maps[m_mapLoaderInfo.mapIdx];
|
|
||||||
previewMap = m_mapLoaderInfo.showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginCombo("Select Map", previewMap.data())) {
|
|
||||||
int prevMapIdx = m_mapLoaderInfo.mapIdx;
|
|
||||||
for (int i = 0; i < region.maps.size(); ++i) {
|
|
||||||
const auto& map = region.maps[i];
|
|
||||||
std::string label = m_mapLoaderInfo.showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName;
|
|
||||||
if (ImGui::Selectable(label.data())) {
|
|
||||||
m_mapLoaderInfo.mapIdx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
if (m_mapLoaderInfo.mapIdx != prevMapIdx) {
|
|
||||||
m_mapLoaderInfo.roomNoIdx = 0;
|
|
||||||
m_mapLoaderInfo.pointNoIdx = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ImGui::Text("No region selected.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_mapLoaderInfo.regionIdx != -1 && m_mapLoaderInfo.mapIdx != -1) {
|
|
||||||
const auto& region = gameRegions[m_mapLoaderInfo.regionIdx];
|
|
||||||
const auto& map = region.maps[m_mapLoaderInfo.mapIdx];
|
|
||||||
const auto& room = map.mapRooms[m_mapLoaderInfo.roomNoIdx];
|
|
||||||
|
|
||||||
if (map.mapRooms.size() > 1) {
|
|
||||||
ImGui::Text("Selected Room: %2d", room.roomNo);
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("-###RoomNoIdxDec")) {
|
|
||||||
m_mapLoaderInfo.roomNoIdx--;
|
|
||||||
if (m_mapLoaderInfo.roomNoIdx < 0) {
|
|
||||||
m_mapLoaderInfo.roomNoIdx = map.mapRooms.size() - 1;
|
|
||||||
}
|
|
||||||
m_mapLoaderInfo.pointNoIdx = 0;
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("+###RoomNoIdxInc")) {
|
|
||||||
m_mapLoaderInfo.roomNoIdx++;
|
|
||||||
if (m_mapLoaderInfo.roomNoIdx >= map.mapRooms.size()) {
|
|
||||||
m_mapLoaderInfo.roomNoIdx = 0;
|
|
||||||
}
|
|
||||||
m_mapLoaderInfo.pointNoIdx = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int MAX_LAYER = 14;
|
|
||||||
|
|
||||||
ImGui::Text("Selected Layer: %3d", m_mapLoaderInfo.layer);
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("-###layerDec")) {
|
|
||||||
m_mapLoaderInfo.layer--;
|
|
||||||
if (m_mapLoaderInfo.layer < -1) {
|
|
||||||
m_mapLoaderInfo.layer = MAX_LAYER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("+###layerInc")) {
|
|
||||||
m_mapLoaderInfo.layer++;
|
|
||||||
if (m_mapLoaderInfo.layer > MAX_LAYER) {
|
|
||||||
m_mapLoaderInfo.layer = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room.roomPoints.size() > 1) {
|
|
||||||
ImGui::Text("Selected Point: %3d", room.roomPoints[m_mapLoaderInfo.pointNoIdx]);
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("-###PointNoIdxDec")) {
|
|
||||||
m_mapLoaderInfo.pointNoIdx--;
|
|
||||||
if (m_mapLoaderInfo.pointNoIdx < 0) {
|
|
||||||
m_mapLoaderInfo.pointNoIdx = room.roomPoints.size() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("+###PointNoIdxInc")) {
|
|
||||||
m_mapLoaderInfo.pointNoIdx++;
|
|
||||||
if (m_mapLoaderInfo.pointNoIdx >= room.roomPoints.size()) {
|
|
||||||
m_mapLoaderInfo.pointNoIdx = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Warp")) {
|
|
||||||
dComIfGp_setNextStage(map.mapFile, room.roomPoints[m_mapLoaderInfo.pointNoIdx], room.roomNo, m_mapLoaderInfo.layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dusk
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#include "fmt/format.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
|
|
||||||
#include "ImGuiConsole.hpp"
|
|
||||||
#include "ImGuiConfig.hpp"
|
|
||||||
|
|
||||||
#include "dusk/main.h"
|
|
||||||
#include "m_Do/m_Do_main.h"
|
|
||||||
|
|
||||||
namespace dusk {
|
|
||||||
ImGuiMenuGame::ImGuiMenuGame() {}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::draw() {}
|
|
||||||
|
|
||||||
static std::string GetFormattedTime(OSTime ticks) {
|
|
||||||
OSCalendarTime time;
|
|
||||||
OSTicksToCalendarTime(ticks, &time);
|
|
||||||
|
|
||||||
return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", time.hour, time.min, time.sec, time.msec);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiMenuGame::resetForSpeedrunMode() {
|
|
||||||
// reset settings that should be off for speedrun mode
|
|
||||||
mDoMain::developmentMode = -1;
|
|
||||||
|
|
||||||
getSettings().game.damageMultiplier.setValue(1);
|
|
||||||
getSettings().game.instantDeath.setValue(false);
|
|
||||||
getSettings().game.noHeartDrops.setValue(false);
|
|
||||||
|
|
||||||
getSettings().game.infiniteHearts.setValue(false);
|
|
||||||
getSettings().game.infiniteArrows.setValue(false);
|
|
||||||
getSettings().game.infiniteBombs.setValue(false);
|
|
||||||
getSettings().game.infiniteOil.setValue(false);
|
|
||||||
getSettings().game.infiniteOxygen.setValue(false);
|
|
||||||
getSettings().game.infiniteRupees.setValue(false);
|
|
||||||
getSettings().game.enableIndefiniteItemDrops.setValue(false);
|
|
||||||
|
|
||||||
getSettings().game.moonJump.setValue(false);
|
|
||||||
getSettings().game.superClawshot.setValue(false);
|
|
||||||
getSettings().game.alwaysGreatspin.setValue(false);
|
|
||||||
getSettings().game.enableFastIronBoots.setValue(false);
|
|
||||||
getSettings().game.canTransformAnywhere.setValue(false);
|
|
||||||
getSettings().game.fastSpinner.setValue(false);
|
|
||||||
getSettings().game.freeMagicArmor.setValue(false);
|
|
||||||
|
|
||||||
getSettings().game.enableTurboKeybind.setValue(false);
|
|
||||||
getSettings().game.debugFlyCam.setValue(false);
|
|
||||||
getSettings().game.autoSave.setValue(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeedrunInfo m_speedrunInfo;
|
|
||||||
|
|
||||||
void ImGuiMenuGame::drawSpeedrunTimerOverlay() {
|
|
||||||
if (!getSettings().game.speedrunMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// L+R+A+Start to reset timer
|
|
||||||
if (mDoCPd_c::getHoldL(PAD_1) && mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getHoldA(PAD_1) && mDoCPd_c::getTrigZ(PAD_1)) {
|
|
||||||
m_speedrunInfo.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// L+R+A+Z to manually stop timer
|
|
||||||
if (mDoCPd_c::getHoldL(PAD_1) && mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getHoldA(PAD_1) && mDoCPd_c::getTrigY(PAD_1)) {
|
|
||||||
if (m_speedrunInfo.m_isRunStarted) {
|
|
||||||
m_speedrunInfo.m_endTimestamp = OSGetTime() - m_speedrunInfo.m_startTimestamp;
|
|
||||||
m_speedrunInfo.m_isRunStarted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SetNextWindowBgAlpha(0.65f);
|
|
||||||
ImGuiWindowFlags flags =
|
|
||||||
ImGuiWindowFlags_NoResize
|
|
||||||
| ImGuiWindowFlags_NoDocking
|
|
||||||
| ImGuiWindowFlags_NoTitleBar
|
|
||||||
| ImGuiWindowFlags_NoScrollbar;
|
|
||||||
|
|
||||||
if (ImGui::Begin("##SpeedrunTimerWindow", nullptr, flags)) {
|
|
||||||
OSTime elapsedTime = 0;
|
|
||||||
if (m_speedrunInfo.m_isRunStarted) {
|
|
||||||
elapsedTime = OSGetTime() - m_speedrunInfo.m_startTimestamp;
|
|
||||||
} else if (m_speedrunInfo.m_endTimestamp != 0) {
|
|
||||||
elapsedTime = m_speedrunInfo.m_endTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("RTA");
|
|
||||||
ImGui::SameLine(60.0f);
|
|
||||||
ImGuiStringViewText(GetFormattedTime(elapsedTime));
|
|
||||||
|
|
||||||
if (!m_speedrunInfo.m_isPauseIGT) {
|
|
||||||
m_speedrunInfo.m_igtTimer = elapsedTime - m_speedrunInfo.m_totalLoadTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("IGT");
|
|
||||||
ImGui::SameLine(60.0f);
|
|
||||||
ImGuiStringViewText(GetFormattedTime(m_speedrunInfo.m_igtTimer));
|
|
||||||
}
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#ifndef DUSK_IMGUI_MENUGAME_HPP
|
|
||||||
#define DUSK_IMGUI_MENUGAME_HPP
|
|
||||||
|
|
||||||
#include <aurora/aurora.h>
|
|
||||||
#include <pad.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
|
|
||||||
namespace dusk {
|
|
||||||
struct SpeedrunInfo {
|
|
||||||
void startRun() {
|
|
||||||
m_isRunStarted = true;
|
|
||||||
m_startTimestamp = OSGetTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
void stopRun() {
|
|
||||||
m_isRunStarted = false;
|
|
||||||
m_endTimestamp = OSGetTime() - m_startTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
m_isRunStarted = false;
|
|
||||||
m_startTimestamp = 0;
|
|
||||||
m_endTimestamp = 0;
|
|
||||||
m_isPauseIGT = false;
|
|
||||||
m_loadStartTimestamp = 0;
|
|
||||||
m_totalLoadTime = 0;
|
|
||||||
m_igtTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool m_isRunStarted = false;
|
|
||||||
OSTime m_startTimestamp = 0;
|
|
||||||
OSTime m_endTimestamp = 0;
|
|
||||||
|
|
||||||
bool m_isPauseIGT = false;
|
|
||||||
OSTime m_loadStartTimestamp = 0;
|
|
||||||
OSTime m_totalLoadTime = 0;
|
|
||||||
OSTime m_igtTimer = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern SpeedrunInfo m_speedrunInfo;
|
|
||||||
|
|
||||||
class ImGuiMenuGame {
|
|
||||||
public:
|
|
||||||
ImGuiMenuGame();
|
|
||||||
void draw();
|
|
||||||
|
|
||||||
void drawSpeedrunTimerOverlay();
|
|
||||||
|
|
||||||
static void resetForSpeedrunMode();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_showTimerWindow = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DUSK_IMGUI_MENUGAME_HPP
|
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "d/actor/d_a_alink.h"
|
#include "d/actor/d_a_alink.h"
|
||||||
#include "d/actor/d_a_horse.h"
|
#include "d/actor/d_a_horse.h"
|
||||||
#include "d/d_com_inf_game.h"
|
#include "d/d_com_inf_game.h"
|
||||||
|
#include "dusk/data.hpp"
|
||||||
#include "dusk/dusk.h"
|
#include "dusk/dusk.h"
|
||||||
#include "dusk/main.h"
|
#include "dusk/main.h"
|
||||||
#include "m_Do/m_Do_main.h"
|
#include "m_Do/m_Do_main.h"
|
||||||
@@ -39,7 +40,6 @@ namespace dusk {
|
|||||||
ImGui::BeginDisabled(getSettings().game.speedrunMode);
|
ImGui::BeginDisabled(getSettings().game.speedrunMode);
|
||||||
|
|
||||||
ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor);
|
ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor);
|
||||||
ImGui::MenuItem("Map Loader", hotkeys::SHOW_MAP_LOADER, &m_showMapLoader);
|
|
||||||
ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare);
|
ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare);
|
||||||
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
@@ -48,13 +48,10 @@ namespace dusk {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Checkbox("Show Input Viewer", &m_showInputViewer);
|
|
||||||
|
|
||||||
#if DUSK_CAN_OPEN_DATA_FOLDER
|
#if DUSK_CAN_OPEN_DATA_FOLDER
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::MenuItem("Open Data Folder")) {
|
if (ImGui::MenuItem("Open Data Folder")) {
|
||||||
OpenDataFolder();
|
data::open_data_path();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace dusk {
|
|||||||
void ShowProcessManager();
|
void ShowProcessManager();
|
||||||
void ShowHeapOverlay();
|
void ShowHeapOverlay();
|
||||||
void ShowStubLog();
|
void ShowStubLog();
|
||||||
void ShowMapLoader();
|
|
||||||
void ShowBloomWindow();
|
void ShowBloomWindow();
|
||||||
void ShowPlayerInfo();
|
void ShowPlayerInfo();
|
||||||
void ShowAudioDebug();
|
void ShowAudioDebug();
|
||||||
@@ -43,22 +42,9 @@ namespace dusk {
|
|||||||
|
|
||||||
bool m_showStubLog = false;
|
bool m_showStubLog = false;
|
||||||
|
|
||||||
bool m_showMapLoader = false;
|
|
||||||
|
|
||||||
bool m_showBloomWindow = false;
|
bool m_showBloomWindow = false;
|
||||||
|
|
||||||
bool m_showAudioDebug = false;
|
bool m_showAudioDebug = false;
|
||||||
struct {
|
|
||||||
int mapIdx = -1;
|
|
||||||
int regionIdx = -1;
|
|
||||||
int roomNoIdx = 0;
|
|
||||||
int pointNoIdx = 0;
|
|
||||||
int roomNo = -1;
|
|
||||||
int pointNo = -1;
|
|
||||||
int spawnId = 0;
|
|
||||||
int layer = -1;
|
|
||||||
bool showInternalNames = false;
|
|
||||||
} m_mapLoaderInfo;
|
|
||||||
|
|
||||||
bool m_showPlayerInfo = false;
|
bool m_showPlayerInfo = false;
|
||||||
int m_playerInfoOverlayCorner = 1; // top-right
|
int m_playerInfoOverlayCorner = 1; // top-right
|
||||||
@@ -69,8 +55,6 @@ namespace dusk {
|
|||||||
bool m_showStateShare = false;
|
bool m_showStateShare = false;
|
||||||
ImGuiStateShare m_stateShare;
|
ImGuiStateShare m_stateShare;
|
||||||
|
|
||||||
bool m_showInputViewer = false;
|
|
||||||
bool m_showInputViewerGyro = false;
|
|
||||||
bool m_showActorSpawner = false;
|
bool m_showActorSpawner = false;
|
||||||
int m_inputOverlayCorner = 3;
|
int m_inputOverlayCorner = 3;
|
||||||
std::string m_controllerName;
|
std::string m_controllerName;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
|
#include <dusk/autosave.h>
|
||||||
|
|
||||||
namespace dusk {
|
namespace dusk {
|
||||||
|
|
||||||
@@ -135,6 +136,8 @@ bool ImGuiStateShare::applyEncodedState(const std::string& encoded, const std::s
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAutoSave(false);
|
||||||
|
|
||||||
StateSharePacket pkt;
|
StateSharePacket pkt;
|
||||||
memcpy(&pkt, raw.data(), sizeof(pkt));
|
memcpy(&pkt, raw.data(), sizeof(pkt));
|
||||||
pkt.stageName[7] = '\0';
|
pkt.stageName[7] = '\0';
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ using namespace dusk::io;
|
|||||||
#else
|
#else
|
||||||
#define MODE(val) val
|
#define MODE(val) val
|
||||||
#endif
|
#endif
|
||||||
|
#define _SH_DENYNO 0
|
||||||
|
#define _SH_DENYWR 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static FILE* ThrowIfNotOpen(const FileStream& file) {
|
static FILE* ThrowIfNotOpen(const FileStream& file) {
|
||||||
@@ -31,19 +33,19 @@ static FILE* ThrowIfNotOpen(const FileStream& file) {
|
|||||||
throw std::system_error(std::make_error_code(static_cast<std::errc>(code)));
|
throw std::system_error(std::make_error_code(static_cast<std::errc>(code)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static FILE* OpenCore(const std::filesystem::path& path, const MODE_TYPE* mode) {
|
static FILE* OpenCore(const std::filesystem::path& path, const MODE_TYPE* mode, int shareFlag) {
|
||||||
FILE* file;
|
FILE* file;
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
|
errno = 0;
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
static_assert(std::is_same_v<std::filesystem::path::value_type, wchar_t>);
|
static_assert(std::is_same_v<std::filesystem::path::value_type, wchar_t>);
|
||||||
err = _wfopen_s(&file, path.c_str(), mode);
|
file = _wfsopen(path.c_str(), mode, shareFlag);
|
||||||
#else
|
#else
|
||||||
errno = 0;
|
|
||||||
static_assert(std::is_same_v<std::filesystem::path::value_type, char>);
|
static_assert(std::is_same_v<std::filesystem::path::value_type, char>);
|
||||||
file = fopen(path.c_str(), mode);
|
file = fopen(path.c_str(), mode);
|
||||||
err = errno;
|
|
||||||
#endif
|
#endif
|
||||||
|
err = errno;
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
ThrowForError(err);
|
ThrowForError(err);
|
||||||
@@ -52,8 +54,8 @@ static FILE* OpenCore(const std::filesystem::path& path, const MODE_TYPE* mode)
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FILE* OpenCore(const char* path, const MODE_TYPE* mode) {
|
static FILE* OpenCore(const char* path, const MODE_TYPE* mode, int shareFlag) {
|
||||||
return OpenCore(reinterpret_cast<const char8_t*>(path), mode);
|
return OpenCore(reinterpret_cast<const char8_t*>(path), mode, shareFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream::FileStream() noexcept : file(nullptr) {
|
FileStream::FileStream() noexcept : file(nullptr) {
|
||||||
@@ -76,19 +78,19 @@ FileStream::~FileStream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileStream FileStream::OpenRead(const char* utf8Path) {
|
FileStream FileStream::OpenRead(const char* utf8Path) {
|
||||||
return FileStream(OpenCore(utf8Path, MODE("rb")));
|
return FileStream(OpenCore(utf8Path, MODE("rb"), _SH_DENYWR));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream FileStream::OpenRead(const std::filesystem::path& path) {
|
FileStream FileStream::OpenRead(const std::filesystem::path& path) {
|
||||||
return FileStream(OpenCore(path, MODE("rb")));
|
return FileStream(OpenCore(path, MODE("rb"), _SH_DENYWR));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream FileStream::Create(const char* utf8Path) {
|
FileStream FileStream::Create(const char* utf8Path) {
|
||||||
return FileStream(OpenCore(utf8Path, MODE("wb")));
|
return FileStream(OpenCore(utf8Path, MODE("wb"), _SH_DENYWR));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream FileStream::Create(const std::filesystem::path& path) {
|
FileStream FileStream::Create(const std::filesystem::path& path) {
|
||||||
return FileStream(OpenCore(path, MODE("wb")));
|
return FileStream(OpenCore(path, MODE("wb"), _SH_DENYWR));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> FileStream::ReadFull() {
|
std::vector<u8> FileStream::ReadFull() {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ static void RunOnMainThread(void (^block)(void))
|
|||||||
|
|
||||||
static NSError *MakeError(NSString *message)
|
static NSError *MakeError(NSString *message)
|
||||||
{
|
{
|
||||||
return [NSError errorWithDomain:@"org.twilitrealm.dusk.file-select"
|
return [NSError errorWithDomain:@"dev.twilitrealm.dusk.file-select"
|
||||||
code:1
|
code:1
|
||||||
userInfo:@{NSLocalizedDescriptionKey: message}];
|
userInfo:@{NSLocalizedDescriptionKey: message}];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,45 @@
|
|||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
using socket_t = SOCKET;
|
using socket_t = SOCKET;
|
||||||
static void closeSocket(socket_t s) { closesocket(s); }
|
static void closeSocket(socket_t s) {
|
||||||
|
LINGER li{1, 0};
|
||||||
|
setsockopt(s, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char*>(&li), sizeof(li));
|
||||||
|
closesocket(s);
|
||||||
|
}
|
||||||
|
static int socketError(socket_t s) {
|
||||||
|
int err = 0; int len = sizeof(err);
|
||||||
|
getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &len);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
static constexpr int kSendFlags = 0;
|
||||||
#else
|
#else
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
using socket_t = int;
|
using socket_t = int;
|
||||||
static void closeSocket(socket_t s) { close(s); }
|
static void closeSocket(socket_t s) {
|
||||||
|
struct linger li{1, 0};
|
||||||
|
setsockopt(s, SOL_SOCKET, SO_LINGER, &li, sizeof(li));
|
||||||
|
close(s);
|
||||||
|
}
|
||||||
|
static int socketError(socket_t s) {
|
||||||
|
int err = 0; socklen_t len = sizeof(err);
|
||||||
|
getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
#ifndef INVALID_SOCKET
|
#ifndef INVALID_SOCKET
|
||||||
#define INVALID_SOCKET -1
|
#define INVALID_SOCKET -1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
static constexpr int kSendFlags = 0;
|
||||||
|
#else
|
||||||
|
static constexpr int kSendFlags = MSG_NOSIGNAL;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -24,12 +50,17 @@
|
|||||||
namespace dusk::speedrun {
|
namespace dusk::speedrun {
|
||||||
|
|
||||||
static bool running = false;
|
static bool running = false;
|
||||||
|
static bool startPending = false;
|
||||||
static uint64_t frameCount = 0;
|
static uint64_t frameCount = 0;
|
||||||
static socket_t sock = INVALID_SOCKET;
|
static socket_t sock = INVALID_SOCKET;
|
||||||
static bool wasLoading = false;
|
static bool wasLoading = false;
|
||||||
static bool connected = false;
|
static bool connected = false;
|
||||||
static bool connectPending = false;
|
static bool connectPending = false;
|
||||||
static bool disconnectPending = false;
|
static bool disconnectPending = false;
|
||||||
|
static uint32_t idleProbeCounter = 0;
|
||||||
|
static uint32_t reconnectCounter = 0;
|
||||||
|
static char storedHost[64] = "127.0.0.1";
|
||||||
|
static int storedPort = 16834;
|
||||||
|
|
||||||
static void sendCmd(const char* cmd) {
|
static void sendCmd(const char* cmd) {
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
@@ -37,18 +68,20 @@ static void sendCmd(const char* cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char msg[64];
|
char msg[64];
|
||||||
int len = snprintf(msg, sizeof(msg), "%s\r\n", cmd);
|
const int len = snprintf(msg, sizeof(msg), "%s\r\n", cmd);
|
||||||
|
if (len <= 0 || len >= static_cast<int>(sizeof(msg))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (send(sock, msg, len, 0) >= 0) {
|
if (send(sock, msg, len, kSendFlags) >= 0) {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
connected = connectPending = true;
|
connected = connectPending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
int err = WSAGetLastError();
|
const int err = WSAGetLastError();
|
||||||
if (err == WSAEWOULDBLOCK || err == WSAENOTCONN) {
|
if (err == WSAEWOULDBLOCK || err == WSAENOTCONN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -58,10 +91,13 @@ static void sendCmd(const char* cmd) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (connected) disconnectPending = true;
|
if (connected) {
|
||||||
|
disconnectPending = true;
|
||||||
|
}
|
||||||
closeSocket(sock);
|
closeSocket(sock);
|
||||||
sock = INVALID_SOCKET;
|
sock = INVALID_SOCKET;
|
||||||
connected = connectPending = false;
|
connected = connectPending = false;
|
||||||
|
reconnectCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t getFrameCount() {
|
uint64_t getFrameCount() {
|
||||||
@@ -89,57 +125,93 @@ void start() {
|
|||||||
if (running) {
|
if (running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
|
startPending = true;
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
wasLoading = false;
|
wasLoading = false;
|
||||||
sendCmd("initgametime");
|
|
||||||
sendCmd("reset");
|
|
||||||
sendCmd("starttimer");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
running = false;
|
running = false;
|
||||||
|
startPending = false;
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
wasLoading = false;
|
wasLoading = false;
|
||||||
sendCmd("reset");
|
sendCmd("reset");
|
||||||
}
|
}
|
||||||
|
|
||||||
void connectLiveSplit(const char* host, int port) {
|
static void reconnect() {
|
||||||
#if _WIN32
|
|
||||||
WSADATA wd{}; WSAStartup(MAKEWORD(2, 2), &wd);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sock != INVALID_SOCKET) {
|
if (sock != INVALID_SOCKET) {
|
||||||
closeSocket(sock); sock = INVALID_SOCKET;
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
connected = connectPending = false;
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
u_long nb = 1;
|
u_long nb = 1;
|
||||||
ioctlsocket(sock, FIONBIO, &nb);
|
if (ioctlsocket(sock, FIONBIO, &nb) != 0) {
|
||||||
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
return;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
|
const int fl = fcntl(sock, F_GETFL, 0);
|
||||||
|
if (fl < 0 || fcntl(sock, F_SETFL, fl | O_NONBLOCK) < 0) {
|
||||||
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sockaddr_in addr{}; addr.sin_family = AF_INET;
|
#if defined(__APPLE__)
|
||||||
addr.sin_port = htons((uint16_t)port);
|
{
|
||||||
inet_pton(AF_INET, host, &addr.sin_addr);
|
int opt = 1;
|
||||||
connect(sock, (sockaddr*)&addr, sizeof(addr));
|
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt));
|
||||||
sendCmd("initgametime");
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sockaddr_in addr{};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(static_cast<uint16_t>(storedPort));
|
||||||
|
if (inet_pton(AF_INET, storedHost, &addr.sin_addr) != 1) {
|
||||||
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int cr = connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
|
||||||
|
#if _WIN32
|
||||||
|
const bool connectPending_ = cr < 0 && WSAGetLastError() == WSAEWOULDBLOCK;
|
||||||
|
#else
|
||||||
|
const bool connectPending_ = cr < 0 && errno == EINPROGRESS;
|
||||||
|
#endif
|
||||||
|
if (cr != 0 && !connectPending_) {
|
||||||
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void connectLiveSplit(const char* host, int port) {
|
||||||
|
#if _WIN32
|
||||||
|
WSADATA wd{};
|
||||||
|
WSAStartup(MAKEWORD(2, 2), &wd);
|
||||||
|
#endif
|
||||||
|
snprintf(storedHost, sizeof(storedHost), "%s", host);
|
||||||
|
storedPort = port;
|
||||||
|
reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnectLiveSplit() {
|
void disconnectLiveSplit() {
|
||||||
if (sock != INVALID_SOCKET) {
|
if (sock != INVALID_SOCKET) {
|
||||||
closeSocket(sock);
|
closeSocket(sock);
|
||||||
sock = INVALID_SOCKET;
|
sock = INVALID_SOCKET;
|
||||||
connected = false;
|
|
||||||
}
|
}
|
||||||
|
connected = connectPending = disconnectPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool consumeConnectedEvent() { bool v = connectPending; connectPending = false; return v; }
|
bool consumeConnectedEvent() { bool v = connectPending; connectPending = false; return v; }
|
||||||
@@ -147,29 +219,76 @@ bool consumeDisconnectedEvent() { bool v = disconnectPending; disconnectPending
|
|||||||
|
|
||||||
void updateLiveSplit() {
|
void updateLiveSplit() {
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
|
if ((reconnectCounter++ % 30) == 0) {
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
|
fd_set writefds, errorfds;
|
||||||
|
FD_ZERO(&writefds);
|
||||||
|
FD_ZERO(&errorfds);
|
||||||
|
FD_SET(sock, &writefds);
|
||||||
|
FD_SET(sock, &errorfds);
|
||||||
|
timeval tv{0, 0};
|
||||||
|
#if _WIN32
|
||||||
|
const int r = select(0, nullptr, &writefds, &errorfds, &tv);
|
||||||
|
#else
|
||||||
|
const int r = select(sock + 1, nullptr, &writefds, &errorfds, &tv);
|
||||||
|
#endif
|
||||||
|
if (r < 0 || FD_ISSET(sock, &errorfds) || socketError(sock) != 0) {
|
||||||
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
reconnectCounter = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!FD_ISSET(sock, &writefds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
sendCmd("initgametime");
|
sendCmd("initgametime");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (startPending) {
|
||||||
|
startPending = false;
|
||||||
|
sendCmd("initgametime");
|
||||||
|
sendCmd("reset");
|
||||||
|
sendCmd("starttimer");
|
||||||
|
}
|
||||||
|
|
||||||
if (!running) {
|
if (!running) {
|
||||||
|
if ((idleProbeCounter++ % 60) == 0) {
|
||||||
|
char buf;
|
||||||
|
const int r = recv(sock, &buf, 1, 0);
|
||||||
|
if (r == 0
|
||||||
|
#if _WIN32
|
||||||
|
|| (r < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
|
||||||
|
#else
|
||||||
|
|| (r < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
if (connected) {
|
||||||
|
disconnectPending = true;
|
||||||
|
}
|
||||||
|
closeSocket(sock);
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
connected = connectPending = false;
|
||||||
|
reconnectCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t totalMs = frameCount * 1000 / 30;
|
const uint64_t totalMs = frameCount * 1000 / 30;
|
||||||
const uint64_t totalSec = totalMs / 1000;
|
const uint64_t totalSec = totalMs / 1000;
|
||||||
char cmd[32];
|
char cmd[32];
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), "setgametime %u:%02u:%02u.%03u",
|
snprintf(cmd, sizeof(cmd), "setgametime %u:%02u:%02u.%03u",
|
||||||
(uint32_t)(totalSec / 3600),
|
static_cast<uint32_t>(totalSec / 3600),
|
||||||
(uint32_t)((totalSec / 60) % 60),
|
static_cast<uint32_t>((totalSec / 60) % 60),
|
||||||
(uint32_t)(totalSec % 60),
|
static_cast<uint32_t>(totalSec % 60),
|
||||||
(uint32_t)(totalMs % 1000)
|
static_cast<uint32_t>(totalMs % 1000)
|
||||||
);
|
);
|
||||||
|
|
||||||
sendCmd(cmd);
|
sendCmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ std::string MakeTimestampedLogName() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::array<char, 32> buffer{};
|
std::array<char, 32> buffer{};
|
||||||
std::strftime(buffer.data(), buffer.size(), "dusk-%Y%m%d-%H%M%S.log", &localTime);
|
std::strftime(buffer.data(), buffer.size(), "dusklight-%Y%m%d-%H%M%S.log", &localTime);
|
||||||
return buffer.data();
|
return buffer.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +109,16 @@ void WriteLogLine(FILE* out, const char* levelStr, const char* module, const cha
|
|||||||
std::fputc('\n', out);
|
std::fputc('\n', out);
|
||||||
std::fflush(out);
|
std::fflush(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WriteLogLineToFile(
|
||||||
|
const char* levelStr, const char* module, const char* message, unsigned int len) {
|
||||||
|
if (g_logStateAlive.load(std::memory_order_acquire)) {
|
||||||
|
std::lock_guard lock(g_logState.mutex);
|
||||||
|
if (g_logState.file != nullptr) {
|
||||||
|
WriteLogLine(g_logState.file, levelStr, module, message, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static bool IsForStubLog(const char* message) {
|
static bool IsForStubLog(const char* message) {
|
||||||
@@ -132,6 +142,11 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (module == nullptr) {
|
||||||
|
module = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* levelStr = LogLevelString(level);
|
||||||
int android_log_level = 0;
|
int android_log_level = 0;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LOG_DEBUG:
|
case LOG_DEBUG:
|
||||||
@@ -151,12 +166,14 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream msgStream(message);
|
std::stringstream msgStream(std::string(message, len));
|
||||||
std::string segment;
|
std::string segment;
|
||||||
while(std::getline(msgStream, segment)) {
|
while(std::getline(msgStream, segment)) {
|
||||||
__android_log_print(android_log_level, module, "%s\n", segment.c_str());
|
__android_log_print(android_log_level, module, "%s\n", segment.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WriteLogLineToFile(levelStr, module, message, len);
|
||||||
|
|
||||||
if (level == LOG_FATAL) {
|
if (level == LOG_FATAL) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
@@ -177,13 +194,7 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
|||||||
const char* levelStr = LogLevelString(level);
|
const char* levelStr = LogLevelString(level);
|
||||||
FILE* out = LogStreamForLevel(level);
|
FILE* out = LogStreamForLevel(level);
|
||||||
WriteLogLine(out, levelStr, module, message, len);
|
WriteLogLine(out, levelStr, module, message, len);
|
||||||
|
WriteLogLineToFile(levelStr, module, message, len);
|
||||||
if (g_logStateAlive.load(std::memory_order_acquire)) {
|
|
||||||
std::lock_guard lock(g_logState.mutex);
|
|
||||||
if (g_logState.file != nullptr) {
|
|
||||||
WriteLogLine(g_logState.file, levelStr, module, message, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level == LOG_FATAL) {
|
if (level == LOG_FATAL) {
|
||||||
abort();
|
abort();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ bool RestartProcess(int argc, char* argv[]) {
|
|||||||
if (!CreateProcessW(nullptr, commandLine.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr,
|
if (!CreateProcessW(nullptr, commandLine.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr,
|
||||||
&startupInfo, &processInfo))
|
&startupInfo, &processInfo))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to restart Dusk: CreateProcessW error %lu\n", GetLastError());
|
fprintf(stderr, "Failed to restart Dusklight: CreateProcessW error %lu\n", GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ bool RestartProcess(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (executablePath.empty()) {
|
if (executablePath.empty()) {
|
||||||
fprintf(stderr, "Failed to restart Dusk: unable to resolve executable path\n");
|
fprintf(stderr, "Failed to restart Dusklight: unable to resolve executable path\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ bool RestartProcess(int argc, char* argv[]) {
|
|||||||
execArgv.push_back(nullptr);
|
execArgv.push_back(nullptr);
|
||||||
|
|
||||||
execv(executablePath.c_str(), execArgv.data());
|
execv(executablePath.c_str(), execArgv.data());
|
||||||
fprintf(stderr, "Failed to restart Dusk: execv failed: %s\n", std::strerror(errno));
|
fprintf(stderr, "Failed to restart Dusklight: execv failed: %s\n", std::strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ UserSettings g_userSettings = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
.audio = {
|
.audio = {
|
||||||
.masterVolume {"audio.masterVolume", 80},
|
.masterVolume {"audio.masterVolume", 60},
|
||||||
.mainMusicVolume {"audio.mainMusicVolume", 100},
|
.mainMusicVolume {"audio.mainMusicVolume", 100},
|
||||||
.subMusicVolume {"audio.subMusicVolume", 100},
|
.subMusicVolume {"audio.subMusicVolume", 100},
|
||||||
.soundEffectsVolume {"audio.soundEffectsVolume", 100},
|
.soundEffectsVolume {"audio.soundEffectsVolume", 100},
|
||||||
@@ -52,6 +52,7 @@ UserSettings g_userSettings = {
|
|||||||
.enableLinkDollRotation {"game.enableLinkDollRotation", false},
|
.enableLinkDollRotation {"game.enableLinkDollRotation", false},
|
||||||
.enableAchievementToasts {"game.enableAchievementToasts", true},
|
.enableAchievementToasts {"game.enableAchievementToasts", true},
|
||||||
.enableControllerToasts {"game.enableControllerToasts", true},
|
.enableControllerToasts {"game.enableControllerToasts", true},
|
||||||
|
.enableDiscordPresence {"game.enableDiscordPresence", true},
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
.bloomMode {"game.bloomMode", BloomMode::Dusk},
|
.bloomMode {"game.bloomMode", BloomMode::Dusk},
|
||||||
@@ -62,6 +63,7 @@ UserSettings g_userSettings = {
|
|||||||
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
||||||
.enableDepthOfField {"game.enableDepthOfField", true},
|
.enableDepthOfField {"game.enableDepthOfField", true},
|
||||||
.enableMapBackground {"game.enableMapBackground", true},
|
.enableMapBackground {"game.enableMapBackground", true},
|
||||||
|
.disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false},
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
.noLowHpSound {"game.noLowHpSound", false},
|
.noLowHpSound {"game.noLowHpSound", false},
|
||||||
@@ -91,6 +93,7 @@ UserSettings g_userSettings = {
|
|||||||
// Cheats
|
// Cheats
|
||||||
.infiniteHearts {"game.infiniteHearts", false},
|
.infiniteHearts {"game.infiniteHearts", false},
|
||||||
.infiniteArrows {"game.infiniteArrows", false},
|
.infiniteArrows {"game.infiniteArrows", false},
|
||||||
|
.infiniteSeeds {"game.infiniteSeeds", false},
|
||||||
.infiniteBombs {"game.infiniteBombs", false},
|
.infiniteBombs {"game.infiniteBombs", false},
|
||||||
.infiniteOil {"game.infiniteOil", false},
|
.infiniteOil {"game.infiniteOil", false},
|
||||||
.infiniteOxygen {"game.infiniteOxygen", false},
|
.infiniteOxygen {"game.infiniteOxygen", false},
|
||||||
@@ -101,19 +104,27 @@ UserSettings g_userSettings = {
|
|||||||
.alwaysGreatspin {"game.alwaysGreatspin", false},
|
.alwaysGreatspin {"game.alwaysGreatspin", false},
|
||||||
.enableFastIronBoots {"game.enableFastIronBoots", false},
|
.enableFastIronBoots {"game.enableFastIronBoots", false},
|
||||||
.canTransformAnywhere {"game.canTransformAnywhere", false},
|
.canTransformAnywhere {"game.canTransformAnywhere", false},
|
||||||
|
.fastRoll {"game.fastRoll", false},
|
||||||
.fastSpinner {"game.fastSpinner", false},
|
.fastSpinner {"game.fastSpinner", false},
|
||||||
.freeMagicArmor {"game.freeMagicArmor", false},
|
.magicArmorNoDrain {"game.magicArmorNoDrain", false},
|
||||||
|
.magicArmorNoDamageLoss {"game.magicArmorNoDamageLoss", false},
|
||||||
|
.magicArmorNoHeavy {"game.magicArmorNoHeavy", false},
|
||||||
|
.invincibleEnemies {"game.invincibleEnemies", false},
|
||||||
|
|
||||||
// Technical
|
// Technical
|
||||||
.restoreWiiGlitches {"game.restoreWiiGlitches", false},
|
.restoreWiiGlitches {"game.restoreWiiGlitches", false},
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
.enableTurboKeybind {"game.enableTurboKeybind", false},
|
.enableTurboKeybind {"game.enableTurboKeybind", false},
|
||||||
|
.enableResetKeybind {"game.enableResetKeybind", false},
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
.speedrunMode {"game.speedrunMode", false},
|
.speedrunMode {"game.speedrunMode", false},
|
||||||
.liveSplitEnabled {"game.liveSplitEnabled", false},
|
.liveSplitEnabled {"game.liveSplitEnabled", false},
|
||||||
.recordingMode {"game.recordingMode", false}
|
.showSpeedrunRTATimer {"game.showSpeedrunRTATimer", true},
|
||||||
|
.recordingMode {"game.recordingMode", false},
|
||||||
|
.showInputViewer {"game.showInputViewer", false},
|
||||||
|
.showInputViewerGyro {"game.showInputViewerGyro", false}
|
||||||
},
|
},
|
||||||
|
|
||||||
.backend = {
|
.backend = {
|
||||||
@@ -126,6 +137,34 @@ UserSettings g_userSettings = {
|
|||||||
.checkForUpdates {"backend.checkForUpdates", true},
|
.checkForUpdates {"backend.checkForUpdates", true},
|
||||||
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)},
|
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)},
|
||||||
.enableAdvancedSettings {"backend.enableAdvancedSettings", false},
|
.enableAdvancedSettings {"backend.enableAdvancedSettings", false},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Not sure if there's a better way to declare this
|
||||||
|
.actionBindings = {
|
||||||
|
.firstPersonCamera {
|
||||||
|
ActionBindConfigVar{"actionBindings.firstPersonCamera_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.firstPersonCamera_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.firstPersonCamera_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.firstPersonCamera_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
},
|
||||||
|
.callMidna {
|
||||||
|
ActionBindConfigVar{"actionBindings.callMidna_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.callMidna_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.callMidna_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.callMidna_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
},
|
||||||
|
.openDusklightMenu {
|
||||||
|
ActionBindConfigVar{"actionBindings.openDusklightMenu_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.openDusklightMenu_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.openDusklightMenu_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.openDusklightMenu_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
},
|
||||||
|
.turboSpeedButton {
|
||||||
|
ActionBindConfigVar{"actionBindings.turboButton_port0", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.turboButton_port1", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.turboButton_port2", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
ActionBindConfigVar{"actionBindings.turboButton_port3", PAD_NATIVE_BUTTON_INVALID},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,6 +216,7 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.game.freeCameraSensitivity);
|
Register(g_userSettings.game.freeCameraSensitivity);
|
||||||
Register(g_userSettings.game.minimalHUD);
|
Register(g_userSettings.game.minimalHUD);
|
||||||
Register(g_userSettings.game.pauseOnFocusLost);
|
Register(g_userSettings.game.pauseOnFocusLost);
|
||||||
|
Register(g_userSettings.game.enableDiscordPresence);
|
||||||
Register(g_userSettings.game.bloomMode);
|
Register(g_userSettings.game.bloomMode);
|
||||||
Register(g_userSettings.game.bloomMultiplier);
|
Register(g_userSettings.game.bloomMultiplier);
|
||||||
Register(g_userSettings.game.disableWaterRefraction);
|
Register(g_userSettings.game.disableWaterRefraction);
|
||||||
@@ -184,9 +224,13 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.game.shadowResolutionMultiplier);
|
Register(g_userSettings.game.shadowResolutionMultiplier);
|
||||||
Register(g_userSettings.game.enableDepthOfField);
|
Register(g_userSettings.game.enableDepthOfField);
|
||||||
Register(g_userSettings.game.enableMapBackground);
|
Register(g_userSettings.game.enableMapBackground);
|
||||||
|
Register(g_userSettings.game.disableCutscenePillarboxing);
|
||||||
Register(g_userSettings.game.enableFastIronBoots);
|
Register(g_userSettings.game.enableFastIronBoots);
|
||||||
Register(g_userSettings.game.canTransformAnywhere);
|
Register(g_userSettings.game.canTransformAnywhere);
|
||||||
Register(g_userSettings.game.freeMagicArmor);
|
Register(g_userSettings.game.fastRoll);
|
||||||
|
Register(g_userSettings.game.magicArmorNoDrain);
|
||||||
|
Register(g_userSettings.game.magicArmorNoDamageLoss);
|
||||||
|
Register(g_userSettings.game.magicArmorNoHeavy);
|
||||||
Register(g_userSettings.game.restoreWiiGlitches);
|
Register(g_userSettings.game.restoreWiiGlitches);
|
||||||
Register(g_userSettings.game.enableLinkDollRotation);
|
Register(g_userSettings.game.enableLinkDollRotation);
|
||||||
Register(g_userSettings.game.enableAchievementToasts);
|
Register(g_userSettings.game.enableAchievementToasts);
|
||||||
@@ -195,12 +239,17 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.game.noLowHpSound);
|
Register(g_userSettings.game.noLowHpSound);
|
||||||
Register(g_userSettings.game.midnasLamentNonStop);
|
Register(g_userSettings.game.midnasLamentNonStop);
|
||||||
Register(g_userSettings.game.enableTurboKeybind);
|
Register(g_userSettings.game.enableTurboKeybind);
|
||||||
|
Register(g_userSettings.game.enableResetKeybind);
|
||||||
Register(g_userSettings.game.speedrunMode);
|
Register(g_userSettings.game.speedrunMode);
|
||||||
Register(g_userSettings.game.liveSplitEnabled);
|
Register(g_userSettings.game.liveSplitEnabled);
|
||||||
|
Register(g_userSettings.game.showSpeedrunRTATimer);
|
||||||
Register(g_userSettings.game.recordingMode);
|
Register(g_userSettings.game.recordingMode);
|
||||||
|
Register(g_userSettings.game.showInputViewer);
|
||||||
|
Register(g_userSettings.game.showInputViewerGyro);
|
||||||
Register(g_userSettings.game.fastSpinner);
|
Register(g_userSettings.game.fastSpinner);
|
||||||
Register(g_userSettings.game.infiniteHearts);
|
Register(g_userSettings.game.infiniteHearts);
|
||||||
Register(g_userSettings.game.infiniteArrows);
|
Register(g_userSettings.game.infiniteArrows);
|
||||||
|
Register(g_userSettings.game.infiniteSeeds);
|
||||||
Register(g_userSettings.game.infiniteBombs);
|
Register(g_userSettings.game.infiniteBombs);
|
||||||
Register(g_userSettings.game.infiniteOil);
|
Register(g_userSettings.game.infiniteOil);
|
||||||
Register(g_userSettings.game.infiniteOxygen);
|
Register(g_userSettings.game.infiniteOxygen);
|
||||||
@@ -209,6 +258,7 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.game.moonJump);
|
Register(g_userSettings.game.moonJump);
|
||||||
Register(g_userSettings.game.superClawshot);
|
Register(g_userSettings.game.superClawshot);
|
||||||
Register(g_userSettings.game.alwaysGreatspin);
|
Register(g_userSettings.game.alwaysGreatspin);
|
||||||
|
Register(g_userSettings.game.invincibleEnemies);
|
||||||
Register(g_userSettings.game.enableFrameInterpolation);
|
Register(g_userSettings.game.enableFrameInterpolation);
|
||||||
Register(g_userSettings.game.gyroMode);
|
Register(g_userSettings.game.gyroMode);
|
||||||
Register(g_userSettings.game.enableGyroAim);
|
Register(g_userSettings.game.enableGyroAim);
|
||||||
@@ -234,6 +284,23 @@ void registerSettings() {
|
|||||||
Register(g_userSettings.backend.checkForUpdates);
|
Register(g_userSettings.backend.checkForUpdates);
|
||||||
Register(g_userSettings.backend.cardFileType);
|
Register(g_userSettings.backend.cardFileType);
|
||||||
Register(g_userSettings.backend.enableAdvancedSettings);
|
Register(g_userSettings.backend.enableAdvancedSettings);
|
||||||
|
|
||||||
|
Register(g_userSettings.actionBindings.firstPersonCamera[0]);
|
||||||
|
Register(g_userSettings.actionBindings.firstPersonCamera[1]);
|
||||||
|
Register(g_userSettings.actionBindings.firstPersonCamera[2]);
|
||||||
|
Register(g_userSettings.actionBindings.firstPersonCamera[3]);
|
||||||
|
Register(g_userSettings.actionBindings.callMidna[0]);
|
||||||
|
Register(g_userSettings.actionBindings.callMidna[1]);
|
||||||
|
Register(g_userSettings.actionBindings.callMidna[2]);
|
||||||
|
Register(g_userSettings.actionBindings.callMidna[3]);
|
||||||
|
Register(g_userSettings.actionBindings.openDusklightMenu[0]);
|
||||||
|
Register(g_userSettings.actionBindings.openDusklightMenu[1]);
|
||||||
|
Register(g_userSettings.actionBindings.openDusklightMenu[2]);
|
||||||
|
Register(g_userSettings.actionBindings.openDusklightMenu[3]);
|
||||||
|
Register(g_userSettings.actionBindings.turboSpeedButton[0]);
|
||||||
|
Register(g_userSettings.actionBindings.turboSpeedButton[1]);
|
||||||
|
Register(g_userSettings.actionBindings.turboSpeedButton[2]);
|
||||||
|
Register(g_userSettings.actionBindings.turboSpeedButton[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transient settings
|
// Transient settings
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "dusk/speedrun.h"
|
||||||
|
#include "dusk/settings.h"
|
||||||
|
#include "m_Do/m_Do_main.h"
|
||||||
|
#include <aurora/aurora.h>
|
||||||
|
|
||||||
|
namespace dusk {
|
||||||
|
|
||||||
|
SpeedrunInfo m_speedrunInfo;
|
||||||
|
|
||||||
|
void resetForSpeedrunMode() {
|
||||||
|
mDoMain::developmentMode = -1;
|
||||||
|
|
||||||
|
getSettings().game.enableTurboKeybind.setSpeedrunValue(false);
|
||||||
|
|
||||||
|
getSettings().game.damageMultiplier.setSpeedrunValue(1);
|
||||||
|
getSettings().game.instantDeath.setSpeedrunValue(false);
|
||||||
|
getSettings().game.noHeartDrops.setSpeedrunValue(false);
|
||||||
|
getSettings().game.autoSave.setSpeedrunValue(false);
|
||||||
|
getSettings().game.sunsSong.setSpeedrunValue(false);
|
||||||
|
|
||||||
|
getSettings().game.infiniteHearts.setSpeedrunValue(false);
|
||||||
|
getSettings().game.infiniteArrows.setSpeedrunValue(false);
|
||||||
|
getSettings().game.infiniteSeeds.setSpeedrunValue(false);
|
||||||
|
getSettings().game.infiniteBombs.setSpeedrunValue(false);
|
||||||
|
getSettings().game.infiniteOil.setSpeedrunValue(false);
|
||||||
|
getSettings().game.infiniteOxygen.setSpeedrunValue(false);
|
||||||
|
getSettings().game.infiniteRupees.setSpeedrunValue(false);
|
||||||
|
getSettings().game.enableIndefiniteItemDrops.setSpeedrunValue(false);
|
||||||
|
getSettings().game.moonJump.setSpeedrunValue(false);
|
||||||
|
getSettings().game.superClawshot.setSpeedrunValue(false);
|
||||||
|
getSettings().game.alwaysGreatspin.setSpeedrunValue(false);
|
||||||
|
getSettings().game.enableFastIronBoots.setSpeedrunValue(false);
|
||||||
|
getSettings().game.canTransformAnywhere.setSpeedrunValue(false);
|
||||||
|
getSettings().game.fastRoll.setSpeedrunValue(false);
|
||||||
|
getSettings().game.fastSpinner.setSpeedrunValue(false);
|
||||||
|
getSettings().game.magicArmorNoDrain.setSpeedrunValue(false);
|
||||||
|
getSettings().game.magicArmorNoDamageLoss.setSpeedrunValue(false);
|
||||||
|
getSettings().game.magicArmorNoHeavy.setSpeedrunValue(false);
|
||||||
|
getSettings().game.invincibleEnemies.setSpeedrunValue(false);
|
||||||
|
|
||||||
|
getSettings().game.pauseOnFocusLost.setSpeedrunValue(false);
|
||||||
|
aurora_set_pause_on_focus_lost(false);
|
||||||
|
|
||||||
|
getSettings().backend.enableAdvancedSettings.setSpeedrunValue(false);
|
||||||
|
getSettings().game.recordingMode.setSpeedrunValue(false);
|
||||||
|
getSettings().game.debugFlyCam.setSpeedrunValue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk
|
||||||
@@ -51,6 +51,9 @@ void Button::update_props(Props props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ControlledButton::update() {
|
void ControlledButton::update() {
|
||||||
|
if (mIsDisabled) {
|
||||||
|
set_disabled(mIsDisabled());
|
||||||
|
}
|
||||||
if (mIsSelected) {
|
if (mIsSelected) {
|
||||||
set_selected(mIsSelected());
|
set_selected(mIsSelected());
|
||||||
}
|
}
|
||||||
@@ -64,4 +67,11 @@ bool ControlledButton::selected() const {
|
|||||||
return Button::selected();
|
return Button::selected();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dusk::ui
|
bool ControlledButton::disabled() const {
|
||||||
|
if (mIsDisabled) {
|
||||||
|
return mIsDisabled();
|
||||||
|
}
|
||||||
|
return Button::disabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dusk::ui
|
||||||
|
|||||||
@@ -32,17 +32,21 @@ public:
|
|||||||
struct Props {
|
struct Props {
|
||||||
Rml::String text;
|
Rml::String text;
|
||||||
std::function<bool()> isSelected;
|
std::function<bool()> isSelected;
|
||||||
|
std::function<bool()> isDisabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
ControlledButton(Rml::Element* parent, Props props, const Rml::String& tagName = "button")
|
ControlledButton(Rml::Element* parent, Props props, const Rml::String& tagName = "button")
|
||||||
: Button(parent, {std::move(props.text)}, tagName),
|
: Button(parent, {std::move(props.text)}, tagName),
|
||||||
mIsSelected(std::move(props.isSelected)) {}
|
mIsSelected(std::move(props.isSelected)),
|
||||||
|
mIsDisabled(std::move(props.isDisabled)) {}
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
bool selected() const override;
|
bool selected() const override;
|
||||||
|
bool disabled() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<bool()> mIsSelected;
|
std::function<bool()> mIsSelected;
|
||||||
|
std::function<bool()> mIsDisabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dusk::ui
|
} // namespace dusk::ui
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dusk/action_bindings.h"
|
||||||
|
#include "dusk/config.hpp"
|
||||||
|
|
||||||
namespace dusk::ui {
|
namespace dusk::ui {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -108,68 +111,6 @@ const std::vector<ButtonNames> kGamepadButtonNames = {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped) {
|
|
||||||
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
|
|
||||||
return "Not bound";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
|
|
||||||
if (gamepad != nullptr) {
|
|
||||||
switch (SDL_GetGamepadButtonLabel(gamepad, button)) {
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_A:
|
|
||||||
return "A";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_B:
|
|
||||||
return "B";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_X:
|
|
||||||
return "X";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_Y:
|
|
||||||
return "Y";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
|
|
||||||
return "Cross";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
|
|
||||||
return "Circle";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
|
|
||||||
return "Triangle";
|
|
||||||
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
|
|
||||||
return "Square";
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SDL_GamepadType type =
|
|
||||||
gamepad != nullptr ? SDL_GetGamepadType(gamepad) : SDL_GAMEPAD_TYPE_UNKNOWN;
|
|
||||||
for (const auto& buttonNames : kGamepadButtonNames) {
|
|
||||||
if (buttonNames.button != button) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& name : buttonNames.names) {
|
|
||||||
if (name.type == type) {
|
|
||||||
return name.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (button) {
|
|
||||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
|
||||||
return "D-pad left";
|
|
||||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
|
||||||
return "D-pad right";
|
|
||||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
|
||||||
return "D-pad up";
|
|
||||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
|
||||||
return "D-pad down";
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const char* name = PADGetNativeButtonName(buttonUntyped)) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
Rml::String native_axis_name(const PADAxisMapping& mapping, SDL_Gamepad* gamepad) {
|
Rml::String native_axis_name(const PADAxisMapping& mapping, SDL_Gamepad* gamepad) {
|
||||||
if (mapping.nativeAxis.nativeAxis != -1) {
|
if (mapping.nativeAxis.nativeAxis != -1) {
|
||||||
Rml::String value = PADGetNativeAxisName(mapping.nativeAxis);
|
Rml::String value = PADGetNativeAxisName(mapping.nativeAxis);
|
||||||
@@ -301,7 +242,11 @@ int rumble_raw_to_percent(u16 raw) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ControllerConfigWindow::ControllerConfigWindow() {
|
ControllerConfigWindow::ControllerConfigWindow(bool prelaunch) {
|
||||||
|
if (prelaunch) {
|
||||||
|
mSuppressNavFallback = true;
|
||||||
|
}
|
||||||
|
|
||||||
listen(
|
listen(
|
||||||
Rml::EventId::Keydown,
|
Rml::EventId::Keydown,
|
||||||
[this](Rml::Event& event) {
|
[this](Rml::Event& event) {
|
||||||
@@ -363,6 +308,7 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
|
|||||||
addPageButton(Page::Triggers, "Triggers", [] { return Rml::String(">"); }, [] { return false; });
|
addPageButton(Page::Triggers, "Triggers", [] { return Rml::String(">"); }, [] { return false; });
|
||||||
addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); }, [] { return false; });
|
addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); }, [] { return false; });
|
||||||
addPageButton(Page::Rumble, "Rumble", [] { return Rml::String(">"); }, [port] { return !PADSupportsRumbleIntensity(static_cast<u32>(port)); });
|
addPageButton(Page::Rumble, "Rumble", [] { return Rml::String(">"); }, [port] { return !PADSupportsRumbleIntensity(static_cast<u32>(port)); });
|
||||||
|
addPageButton(Page::Actions, "Custom Action Bindings", [] {return Rml::String(">"); }, [] { return false; });
|
||||||
|
|
||||||
leftPane.add_section("Options");
|
leftPane.add_section("Options");
|
||||||
leftPane.register_control(leftPane.add_child<BoolButton>(BoolButton::Props{
|
leftPane.register_control(leftPane.add_child<BoolButton>(BoolButton::Props{
|
||||||
@@ -424,6 +370,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
|||||||
PADClearPort(port);
|
PADClearPort(port);
|
||||||
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
|
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
|
||||||
PADSerializeMappings();
|
PADSerializeMappings();
|
||||||
|
ClearAllActionBindings(port);
|
||||||
});
|
});
|
||||||
|
|
||||||
pane.add_button({
|
pane.add_button({
|
||||||
@@ -436,6 +383,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
|||||||
PADClearPort(port);
|
PADClearPort(port);
|
||||||
PADSetKeyboardActive(static_cast<u32>(port), TRUE);
|
PADSetKeyboardActive(static_cast<u32>(port), TRUE);
|
||||||
PADSerializeMappings();
|
PADSerializeMappings();
|
||||||
|
ClearAllActionBindings(port);
|
||||||
});
|
});
|
||||||
|
|
||||||
const u32 controllerCount = PADCount();
|
const u32 controllerCount = PADCount();
|
||||||
@@ -457,6 +405,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
|||||||
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
|
PADSetKeyboardActive(static_cast<u32>(port), FALSE);
|
||||||
PADSetPortForIndex(i, port);
|
PADSetPortForIndex(i, port);
|
||||||
PADSerializeMappings();
|
PADSerializeMappings();
|
||||||
|
ClearAllActionBindings(port);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -942,6 +891,77 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
|
|||||||
pane.add_text("Configure your desired rumble intensities, then run a test to check how they feel.");
|
pane.add_text("Configure your desired rumble intensities, then run a test to check how they feel.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Page::Actions: {
|
||||||
|
if (keyboard_active(port)) {
|
||||||
|
auto addActionBinding = [&](auto actionBind, const std::string& key) {
|
||||||
|
pane.add_select_button(
|
||||||
|
{
|
||||||
|
.key = key,
|
||||||
|
.getValue =
|
||||||
|
[this, actionBind] {
|
||||||
|
if (mPendingActionBinding == actionBind) {
|
||||||
|
return pending_key_label();
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyboard_key_name(actionBind->getValue());
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.on_pressed([this, port, actionBind] {
|
||||||
|
cancel_pending_binding();
|
||||||
|
mPendingPort = port;
|
||||||
|
mPendingBindingArmed = false;
|
||||||
|
mPendingActionBinding = actionBind;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
pane.add_section("Custom Action Bindings");
|
||||||
|
pane.add_text("A key bound to any action here will REPLACE the default control for"
|
||||||
|
" that action. Only bind buttons here that aren't used anywhere else.");
|
||||||
|
for (auto& [configVars, actionName] : getActionBinds() | std::views::values) {
|
||||||
|
addActionBinding(&configVars->at(port), actionName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 buttonCount = 0;
|
||||||
|
PADButtonMapping* mappings = PADGetButtonMappings(port, &buttonCount);
|
||||||
|
if (mappings == nullptr) {
|
||||||
|
pane.add_text("No controller selected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Gamepad* gamepad = gamepad_for_port(port);
|
||||||
|
pane.add_section("Custom Action Bindings");
|
||||||
|
pane.add_text("A button bound to any action here will REPLACE the default control for"
|
||||||
|
" that action. Only bind buttons here that aren't used anywhere else. The glyphs"
|
||||||
|
" shown for in game actions will not change. This is not recommended for "
|
||||||
|
" regular Gamecube controllers.");
|
||||||
|
auto addActionBinding = [&](auto actionBind, const std::string& key) {
|
||||||
|
pane.add_select_button({
|
||||||
|
.key = key,
|
||||||
|
.getValue =
|
||||||
|
[this, gamepad, actionBind] {
|
||||||
|
if (mPendingActionBinding == actionBind) {
|
||||||
|
return pending_button_label();
|
||||||
|
}
|
||||||
|
|
||||||
|
return native_button_name(
|
||||||
|
gamepad, actionBind->getValue());
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.on_pressed([this, port, actionBind] {
|
||||||
|
cancel_pending_binding();
|
||||||
|
mPendingPort = port;
|
||||||
|
mPendingBindingArmed = false;
|
||||||
|
mPendingActionBinding = actionBind;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& [configVars, actionName] : getActionBinds() | std::views::values) {
|
||||||
|
addActionBinding(&configVars->at(port), actionName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,12 +1036,31 @@ void ControllerConfigWindow::poll_pending_binding() {
|
|||||||
mPendingAxisMapping->nativeButton = nativeButton;
|
mPendingAxisMapping->nativeButton = nativeButton;
|
||||||
finish_pending_binding(completedPort);
|
finish_pending_binding(completedPort);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPendingActionBinding != nullptr) {
|
||||||
|
int button{};
|
||||||
|
if (keyboard_active(mPendingPort)) {
|
||||||
|
button = keyboard_key_pressed();
|
||||||
|
} else {
|
||||||
|
button = PADGetNativeButtonPressed(mPendingPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button != -1) {
|
||||||
|
const int completedPort = mPendingPort;
|
||||||
|
mPendingActionBinding->setValue(button);
|
||||||
|
config::Save();
|
||||||
|
finish_pending_binding(completedPort);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControllerConfigWindow::finish_pending_binding(int completedPort) {
|
void ControllerConfigWindow::finish_pending_binding(int completedPort) {
|
||||||
mPendingButtonMapping = nullptr;
|
mPendingButtonMapping = nullptr;
|
||||||
mPendingAxisMapping = nullptr;
|
mPendingAxisMapping = nullptr;
|
||||||
|
mPendingActionBinding = nullptr;
|
||||||
mPendingPort = -1;
|
mPendingPort = -1;
|
||||||
mPendingBindingArmed = false;
|
mPendingBindingArmed = false;
|
||||||
mSuppressNavigationUntilNeutral = true;
|
mSuppressNavigationUntilNeutral = true;
|
||||||
@@ -1031,7 +1070,7 @@ void ControllerConfigWindow::finish_pending_binding(int completedPort) {
|
|||||||
|
|
||||||
void ControllerConfigWindow::unmap_pending_binding() {
|
void ControllerConfigWindow::unmap_pending_binding() {
|
||||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
||||||
mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
mPendingActionBinding == nullptr && mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1044,6 +1083,9 @@ void ControllerConfigWindow::unmap_pending_binding() {
|
|||||||
mPendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
|
mPendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE};
|
||||||
mPendingAxisMapping->nativeButton = -1;
|
mPendingAxisMapping->nativeButton = -1;
|
||||||
finish_pending_binding(completedPort);
|
finish_pending_binding(completedPort);
|
||||||
|
} else if (mPendingActionBinding != nullptr) {
|
||||||
|
mPendingActionBinding->setValue(PAD_NATIVE_BUTTON_INVALID);
|
||||||
|
finish_pending_binding(completedPort);
|
||||||
} else if (mPendingKeyButton >= 0) {
|
} else if (mPendingKeyButton >= 0) {
|
||||||
PADSetKeyButtonBinding(static_cast<u32>(completedPort),
|
PADSetKeyButtonBinding(static_cast<u32>(completedPort),
|
||||||
{PAD_KEY_INVALID, static_cast<PADButton>(mPendingKeyButton)});
|
{PAD_KEY_INVALID, static_cast<PADButton>(mPendingKeyButton)});
|
||||||
@@ -1057,7 +1099,7 @@ void ControllerConfigWindow::unmap_pending_binding() {
|
|||||||
|
|
||||||
bool ControllerConfigWindow::capture_active() const {
|
bool ControllerConfigWindow::capture_active() const {
|
||||||
return mPendingButtonMapping != nullptr || mPendingAxisMapping != nullptr ||
|
return mPendingButtonMapping != nullptr || mPendingAxisMapping != nullptr ||
|
||||||
mPendingKeyButton >= 0 || mPendingKeyAxis >= 0;
|
mPendingActionBinding != nullptr || mPendingKeyButton >= 0 || mPendingKeyAxis >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ControllerConfigWindow::pending_input_neutral() const {
|
bool ControllerConfigWindow::pending_input_neutral() const {
|
||||||
@@ -1076,13 +1118,14 @@ Rml::String ControllerConfigWindow::pending_axis_label() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ControllerConfigWindow::cancel_pending_binding() {
|
void ControllerConfigWindow::cancel_pending_binding() {
|
||||||
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr &&
|
if (mPendingButtonMapping == nullptr && mPendingAxisMapping == nullptr && mPendingActionBinding == nullptr &&
|
||||||
!mSuppressNavigationUntilNeutral && mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
!mSuppressNavigationUntilNeutral && mPendingKeyButton < 0 && mPendingKeyAxis < 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mPendingButtonMapping = nullptr;
|
mPendingButtonMapping = nullptr;
|
||||||
mPendingAxisMapping = nullptr;
|
mPendingAxisMapping = nullptr;
|
||||||
|
mPendingActionBinding = nullptr;
|
||||||
mPendingKeyButton = -1;
|
mPendingKeyButton = -1;
|
||||||
mPendingKeyAxis = -1;
|
mPendingKeyAxis = -1;
|
||||||
mPendingPort = -1;
|
mPendingPort = -1;
|
||||||
@@ -1114,4 +1157,66 @@ void ControllerConfigWindow::stop_rumble_test() {
|
|||||||
mRumbleTestPort = -1;
|
mRumbleTestPort = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rml::String native_button_name(SDL_Gamepad* gamepad, u32 buttonUntyped) {
|
||||||
|
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
|
||||||
|
return "Not bound";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
|
||||||
|
if (gamepad != nullptr) {
|
||||||
|
switch (SDL_GetGamepadButtonLabel(gamepad, button)) {
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_A:
|
||||||
|
return "A";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_B:
|
||||||
|
return "B";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_X:
|
||||||
|
return "X";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_Y:
|
||||||
|
return "Y";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
|
||||||
|
return "Cross";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
|
||||||
|
return "Circle";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
|
||||||
|
return "Triangle";
|
||||||
|
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
|
||||||
|
return "Square";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SDL_GamepadType type =
|
||||||
|
gamepad != nullptr ? SDL_GetGamepadType(gamepad) : SDL_GAMEPAD_TYPE_UNKNOWN;
|
||||||
|
for (const auto& buttonNames : kGamepadButtonNames) {
|
||||||
|
if (buttonNames.button != button) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& name : buttonNames.names) {
|
||||||
|
if (name.type == type) {
|
||||||
|
return name.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (button) {
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||||
|
return "D-pad left";
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||||
|
return "D-pad right";
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||||
|
return "D-pad up";
|
||||||
|
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||||
|
return "D-pad down";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const char* name = PADGetNativeButtonName(buttonUntyped)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dusk::ui
|
} // namespace dusk::ui
|
||||||
|
|||||||