Compare commits

...

77 Commits

Author SHA1 Message Date
Irastris 1fea4f02ed Fix camera bouncing with interp enabled 2026-04-24 14:26:56 -04:00
Luke Street 35ea13c53a Update aurora 2026-04-24 12:25:15 -06:00
PJB3005 5fab665f21 Don't allocate giant unused stacks for movie player
How to save 800 KiB of commit charge easy
2026-04-24 15:25:14 +02:00
madeline 109f0a50e5 fix sun song again 2026-04-24 03:37:35 -07:00
Irastris 746910c59f Register interp callback for d_a_obj_item
Fixes rupee color changes
2026-04-24 05:13:31 -04:00
CraftyBoss ae4806ae4f Merge remote-tracking branch 'origin/main' 2026-04-23 23:40:36 -07:00
CraftyBoss 1c00e2cdde fix unsafe cstr usage in flag editor, remove stray backslash 2026-04-23 23:40:20 -07:00
Luke Street 78301a8a83 Restore half-size in drawDepth2 2026-04-24 00:39:20 -06:00
CraftyBoss ca798049b3 Merge remote-tracking branch 'origin/main' 2026-04-23 23:31:24 -07:00
CraftyBoss daf4b1dfeb update aurora, add pre launch option to determine save file type (gci or raw) 2026-04-23 23:31:07 -07:00
TakaRikka 5bdd31a5af Merge pull request #517 from TwilitRealm/midna-eye
Clamp max LOD for Wolf Link and Midna eyes
2026-04-23 22:43:16 -07:00
Luke Street 3cb5e5172b Add Adult Link eye LOD fix 2026-04-23 23:42:15 -06:00
TakaRikka d625c7ab0c fix save editor clothes change crash 2026-04-23 20:26:50 -07:00
Luke Street c2045391c8 Fix a couple issues for Xcode projects (#513)
* Link separate JSystem libraries due to control.cpp

This allows the Xcode generator to work, otherwise
it breaks on the duplicate control.o files within
the game_debug library.

* Fix __memcpy define on GCC

* Try LINK_GROUP:RESCAN for GNU ld

* Combine dusk/game_base/game_debug targets

* Fix compile defs syntax
2026-04-23 21:05:38 -06:00
TakaRikka f04a0ffcf1 forgot pic oops 2026-04-23 15:12:40 -07:00
TakaRikka dfdac1c1cd update setup instructions 2026-04-23 15:11:51 -07:00
TakaRikka f4b361ab02 Merge pull request #510 from TwilitRealm/feature/thinner-map-lines
Thinner map lines at higher resolution
2026-04-23 14:36:02 -07:00
TakaRikka 5f3d3012ff Merge pull request #509 from TwilitRealm/fix/ring_menu_cursor_interp
Frame interp: interpolate ring menu cursor along arc
2026-04-23 14:09:35 -07:00
TakaRikka 0897ef1132 Merge pull request #508 from TwilitRealm/fix/wolf-dive-fix
Fix Sun Song check
2026-04-23 14:08:44 -07:00
TakaRikka 8b89dbcbf0 Merge pull request #506 from TwilitRealm/instant-text-responsiveness
Instant Text: Fix holding B in the middle of a textbox
2026-04-23 14:08:09 -07:00
TakaRikka f24e944ab0 Merge pull request #492 from TwilitRealm/feature/gamepad-color
Gamepad Colors for DS4/DS5
2026-04-23 14:07:43 -07:00
MelonSpeedruns 23130d5a57 Thinner map lines at higher resolution
Affects both the Map Menu and the Mini-Map.
2026-04-23 15:08:34 -04:00
Luke Street 1e6e1976e3 Clamp max LOD for Wolf Link and Midna eyes 2026-04-23 13:07:42 -06:00
MelonSpeedruns 4453316bb0 Separated gamepad color into its own files 2026-04-23 14:25:50 -04:00
Pheenoh 14aeefb813 Frame interp: interpolate ring menu cursor along arc 2026-04-23 08:38:47 -06:00
MelonSpeedruns c991c7c407 Fixes sun songing underwater 2026-04-23 09:42:02 -04:00
madeline 251c6e7aec more precise link debug info 2026-04-23 06:28:16 -07:00
madeline cf2357fff2 Merge branch 'main' of https://github.com/TakaRikka/dusk 2026-04-23 03:58:38 -07:00
madeline 06e6b0d47e fix safety bit on interp fixes #507 2026-04-23 03:58:35 -07:00
gymnast86 a83b4186af fix holding B in the middle of text 2026-04-23 03:25:19 -07:00
TakaRikka dc5de83436 Merge pull request #503 from TwilitRealm/feature/rotate-link-doll
Add ability to rotate link on the collection screen
2026-04-22 23:36:55 -07:00
TakaRikka 77425b4b25 Merge pull request #500 from TwilitRealm/fix/interp_dselect_cursor_ring_menu
Frame Interp: dselect_cursor_c in d_menu_ring
2026-04-22 23:31:16 -07:00
Phillip Stephens 5a109313cb Minor tooltip cleanup 2026-04-22 23:02:11 -07:00
Phillip Stephens d8a7927602 Clarify tooltip 2026-04-22 22:59:56 -07:00
Phillip Stephens cccddee106 Add ability to rotate link on the collection screen 2026-04-22 22:58:38 -07:00
madeline 6c252c6d26 disallow breaking share state by overlapping loads 2026-04-22 22:37:21 -07:00
madeline b06c1911c6 Merge branch 'main' of https://github.com/TakaRikka/dusk 2026-04-22 22:33:59 -07:00
madeline 7c9e99220a better impl of insta state shares 2026-04-22 22:33:55 -07:00
madeline bfd8b9f453 make state share loads basically instant 2026-04-22 22:27:14 -07:00
Pheenoh 53e8662335 frame interp: dselect_cursor_c in d_menu_ring 2026-04-22 21:57:36 -06:00
Luke Street ae54f024cd Update aurora 2026-04-22 21:48:58 -06:00
TakaRikka 9aa391c5bf Merge pull request #499 from TwilitRealm/fix/dmap_frame_interp
Frame interp: Fix dmap scroll arrows
2026-04-22 17:41:56 -07:00
TakaRikka ec48249934 Merge pull request #495 from TwilitRealm/limiter
Limiter: Use SDL_GetTicksNS instead of std::chrono
2026-04-22 17:40:47 -07:00
Pheenoh 6a8f3516f9 Frame interp: Fix dmap scroll arrows 2026-04-22 18:35:07 -06:00
TakaRikka 79d4835784 Merge pull request #496 from TwilitRealm/feat/esc_fs
Press esc to exit full screen
2026-04-22 17:00:54 -07:00
TakaRikka b1a4783e38 Merge pull request #494 from TwilitRealm/fix/wide-fused-shadows
Widescreenified Fused Shadow Animations for all first 3 bosses
2026-04-22 16:59:45 -07:00
Pheenoh 0038afa392 Press esc to exit full screen 2026-04-22 17:51:16 -06:00
Luke Street 5fcffa0b4f Use SDL_GetTicksNS instead of std::chrono 2026-04-22 17:18:18 -06:00
MelonSpeedruns 9e9d11ae89 Widescreenified Fused Shadow Animations for all first 3 bosses 2026-04-22 18:47:51 -04:00
TakaRikka 97bd84725c Merge pull request #491 from TwilitRealm/gyro-roll
Incorporate roll into gyro horizontal aiming
2026-04-22 14:45:57 -07:00
Captain Kitty Cat 19c86b1b73 Items Don't Despawn (#488)
* Indefinite Item Drops

* Preserve Disappear Effect when Disabled

* Changed to "Items Don't Despawn". Under "Cheats"

* SetItemTooltip for description
2026-04-22 12:27:54 -06:00
Luke Street ca247095da Reset gravity baseline on aim start 2026-04-22 12:20:44 -06:00
MelonSpeedruns c350b7b8ed fix building on linux 2026-04-22 13:52:14 -04:00
Luke Street ac3d3314c4 Incorporate roll into gyro horizontal aiming 2026-04-22 11:47:40 -06:00
MelonSpeedruns f916a48db0 optimized code and removed bugs 2026-04-22 13:36:06 -04:00
MelonSpeedruns 6963a4b554 Merge remote-tracking branch 'origin/main' into feature/gamepad-color
# Conflicts:
#	src/f_ap/f_ap_game.cpp
2026-04-22 13:01:03 -04:00
MelonSpeedruns 4466bf4e12 wip 2026-04-22 13:00:35 -04:00
madeline c4d01b82a6 get rid of old logs 2026-04-22 03:16:13 -07:00
madeline 42e8d9ab9d name fix 2026-04-22 02:50:49 -07:00
madeline 9c562ff740 state packs and partial states 2026-04-22 02:44:58 -07:00
madeline 1787de517c properly set oxygen in share states 2026-04-22 01:55:23 -07:00
madeline 832e567620 better share states 2026-04-22 01:50:17 -07:00
Luke Street 319efbe662 Reset game clock with over 250ms frame gap 2026-04-22 00:30:11 -06:00
Luke Street 6f34bb050a Call J3DModel::diff in mDoExt_modelEntryDL when interpolating
Fixes #355
2026-04-22 00:17:52 -06:00
Irastris a2a56122e2 Gyro: Hawk Aiming 2026-04-22 01:46:10 -04:00
Irastris 58f2679def Rollgoal: Gyro & Mirror Mode Fixes 2026-04-22 01:41:35 -04:00
Luke Street 396ea02fe5 Fix flowers with interpolation on (#486) 2026-04-21 23:33:03 -06:00
TakaRikka 8100ddb990 Merge pull request #485 from TwilitRealm/frame-pacing-2
Rework interpolation pacing; interpolate every frame
2026-04-21 22:05:33 -07:00
Luke Street d78c46a628 Rework interpolation pacing; interpolate every frame 2026-04-21 22:45:18 -06:00
Phillip Stephens 1e93657ab5 Update aurora for fix 2026-04-21 19:25:28 -07:00
madeline 366e47245e make sun song play the correct notes 2026-04-21 19:23:53 -07:00
madeline 4c53ba91be Merge branch 'main' of https://github.com/TakaRikka/dusk 2026-04-21 18:59:23 -07:00
madeline cf080523cb dsp oscillator channels fixes #471 fixes #131 2026-04-21 18:59:11 -07:00
Luke Street a15d0af139 Better fix for mirror crashes 2026-04-21 17:39:53 -06:00
Luke Street d99205ecc6 Update aurora 2026-04-21 17:12:28 -06:00
TakaRikka dd3a61d84c fix dmap background 2026-04-21 15:13:55 -07:00
MelonSpeedruns 31c5397ae3 Gamepad Color Implementation 2026-04-15 14:36:20 -04:00
55 changed files with 1471 additions and 286 deletions
+54 -25
View File
@@ -90,6 +90,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Folder-based instead of target-based organization
# in Visual Studio and Xcode generators
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "_cmake")
if (CMAKE_SYSTEM_NAME STREQUAL Linux) if (CMAKE_SYSTEM_NAME STREQUAL Linux)
set(DAWN_USE_WAYLAND ON CACHE BOOL "Enable support for Wayland surface" FORCE) set(DAWN_USE_WAYLAND ON CACHE BOOL "Enable support for Wayland surface" FORCE)
endif () endif ()
@@ -281,7 +286,7 @@ set(DUSK_TP_VERSION ${DUSK_GAME_NAME}${DUSK_GAME_VERSION})
message(STATUS "dusk: Game Version: ${DUSK_TP_VERSION}") message(STATUS "dusk: Game Version: ${DUSK_TP_VERSION}")
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${JSYSTEM_FILES} ${JSYSTEM_DEBUG_FILES} ${REL_FILES}) source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
source_group("dusk" FILES ${DUSK_FILES}) source_group("dusk" FILES ${DUSK_FILES})
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0
@@ -374,41 +379,65 @@ endif ()
# game_debug is for game code files that we know work when compiled with DEBUG=1 # game_debug is for game code files that we know work when compiled with DEBUG=1
# Of course, if building a release build, this distinction is irrelevant # Of course, if building a release build, this distinction is irrelevant
add_library(game_debug OBJECT ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES} set(GAME_DEBUG_FILES
${SSYSTEM_FILES}
src/dusk/audio/DuskAudioSystem.cpp src/dusk/audio/DuskAudioSystem.cpp
src/dusk/audio/JASCriticalSection.cpp src/dusk/audio/JASCriticalSection.cpp
src/dusk/audio/DuskDsp.cpp src/dusk/audio/DuskDsp.cpp
src/dusk/audio/Adpcm.cpp src/dusk/audio/Adpcm.cpp
src/dusk/audio/DspStub.cpp src/dusk/audio/DspStub.cpp
src/dusk/imgui/ImGuiAudio.cpp) src/dusk/imgui/ImGuiAudio.cpp
)
set_source_files_properties(
${GAME_DEBUG_FILES}
PROPERTIES
COMPILE_DEFINITIONS "$<$<CONFIG:Debug>:DEBUG=1>;$<$<CONFIG:Debug>:PARTIAL_DEBUG=1>"
)
# game_base is for all other game code files # game_base is for all other game code files
add_library(game_base OBJECT ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${JSYSTEM_FILES} ${REL_FILES} ${DUSK_FILES} ${DOLPHIN_FILES}) set(GAME_BASE_FILES
${DOLZEL_FILES}
${Z2AUDIOLIB_FILES}
${REL_FILES}
${DUSK_FILES}
${DOLPHIN_FILES}
)
set_source_files_properties(
${GAME_BASE_FILES}
PROPERTIES
COMPILE_DEFINITIONS "NDEBUG=1;NDEBUG_DEFINED=1;DEBUG_DEFINED=0;$<$<CONFIG:Debug>:PARTIAL_DEBUG=1>"
)
target_compile_definitions(game_debug PRIVATE ${GAME_COMPILE_DEFS} $<$<CONFIG:Debug>:DEBUG=1> $<$<CONFIG:Debug>:PARTIAL_DEBUG=1>) foreach(jsystem_lib IN LISTS JSYSTEM_LIBRARIES)
target_compile_definitions(game_base PRIVATE ${GAME_COMPILE_DEFS} NDEBUG=1 NDEBUG_DEFINED=1 DEBUG_DEFINED=0 $<$<CONFIG:Debug>:PARTIAL_DEBUG=1>) target_compile_definitions(${jsystem_lib} PRIVATE
${GAME_COMPILE_DEFS}
$<$<CONFIG:Debug>:DEBUG=1>
$<$<CONFIG:Debug>:PARTIAL_DEBUG=1>
)
target_include_directories(${jsystem_lib} PRIVATE ${GAME_INCLUDE_DIRS})
target_link_libraries(${jsystem_lib} PRIVATE ${GAME_LIBS})
set_target_properties(${jsystem_lib} PROPERTIES FOLDER "JSystem")
endforeach()
# only apply PCH to game_base since not all headers are necessarily validated with DEBUG=1 set(JSYSTEM_LINK_LIBRARIES ${JSYSTEM_LIBRARIES})
target_precompile_headers(game_base PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>") if (CMAKE_CXX_LINK_GROUP_USING_RESCAN_SUPPORTED OR CMAKE_LINK_GROUP_USING_RESCAN_SUPPORTED)
# GNU ld resolves static archives in a single left-to-right pass. The split
target_include_directories(game_debug PRIVATE ${GAME_INCLUDE_DIRS}) # JSystem libraries reference each other, so they need a RESCAN group there.
target_include_directories(game_base PRIVATE ${GAME_INCLUDE_DIRS}) set(JSYSTEM_LINK_LIBRARIES "$<LINK_GROUP:RESCAN,${JSYSTEM_LIBRARIES}>")
# This implicitly pulls in the library include directories even though no
# linking actually takes place for object libraries
target_link_libraries(game_debug PRIVATE ${GAME_LIBS})
target_link_libraries(game_base PRIVATE ${GAME_LIBS})
if(ANDROID)
add_library(dusk SHARED src/dusk/main.cpp)
set_target_properties(dusk PROPERTIES OUTPUT_NAME main)
else ()
add_executable(dusk src/dusk/main.cpp)
endif () endif ()
target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0) set(DUSK_FILES src/dusk/main.cpp ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
target_include_directories(dusk PRIVATE include) if(ANDROID)
target_link_libraries(dusk PRIVATE game_base game_debug aurora::main) add_library(dusk SHARED ${DUSK_FILES})
set_target_properties(dusk PROPERTIES OUTPUT_NAME main)
else ()
add_executable(dusk ${DUSK_FILES})
endif ()
target_compile_definitions(dusk PRIVATE ${GAME_COMPILE_DEFS})
target_include_directories(dusk PRIVATE ${GAME_INCLUDE_DIRS})
target_link_libraries(dusk PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBRARIES})
target_precompile_headers(dusk 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(dusk crashpad_handler)
endif () endif ()
+6 -15
View File
@@ -10,28 +10,19 @@
First make sure your dump of the game is clean and supported by Dusk. You can do this by checking the sha1 hash of your dump against this list of supported versions. First make sure your dump of the game is clean and supported by Dusk. You can do this by checking the sha1 hash of your dump against this list of supported versions.
| Version | sha1 hash | | Version | sha1 hash |
| ------------ | ---------------------------------------- | |--------------| ---------------------------------------- |
| GameCube USA | 75edd3ddff41f125d1b4ce1a40378f1b565519e7 | | GameCube USA | 75edd3ddff41f125d1b4ce1a40378f1b565519e7 |
| GameCube PAL | 2601822a488eeb86fb89db16ca8f29c2c953e1ca |
### 2. Download [Dusk](https://github.com/TwilitRealm/dusk/releases) ### 2. Download [Dusk](https://github.com/TwilitRealm/dusk/releases)
### 3. Setup the game ### 3. Setup the game
#### Windows
- Extract the zip folder - Extract the zip folder
- Place your dump of the game into the same folder where you extracted to - Launch Dusk
- Launch `dusk.exe` - Select Options, then set the ISO Path to your supported game dump
- Press Start Game to play!
#### macOS ![DuskOptions](assets/dusk_options.png)
- TODO
#### Linux
- TODO
#### iOS
- TODO
#### android
- TODO
# 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 Dusk from source, please read the [build instructions](docs/building.md).
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+1 -1
+84 -3
View File
@@ -314,7 +314,7 @@ set(SSYSTEM_FILES
src/SSystem/SStandard/s_basic.cpp src/SSystem/SStandard/s_basic.cpp
) )
set(JSYSTEM_DEBUG_FILES add_library(JSystem_JParticle STATIC
libs/JSystem/src/JParticle/JPAResourceManager.cpp libs/JSystem/src/JParticle/JPAResourceManager.cpp
libs/JSystem/src/JParticle/JPAResource.cpp libs/JSystem/src/JParticle/JPAResource.cpp
libs/JSystem/src/JParticle/JPABaseShape.cpp libs/JSystem/src/JParticle/JPABaseShape.cpp
@@ -330,10 +330,19 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JParticle/JPAEmitter.cpp libs/JSystem/src/JParticle/JPAEmitter.cpp
libs/JSystem/src/JParticle/JPAParticle.cpp libs/JSystem/src/JParticle/JPAParticle.cpp
libs/JSystem/src/JParticle/JPAMath.cpp libs/JSystem/src/JParticle/JPAMath.cpp
)
add_library(JSystem_JFramework STATIC
libs/JSystem/src/JFramework/JFWSystem.cpp libs/JSystem/src/JFramework/JFWSystem.cpp
libs/JSystem/src/JFramework/JFWDisplay.cpp libs/JSystem/src/JFramework/JFWDisplay.cpp
)
add_library(JSystem_J3DU STATIC
libs/JSystem/src/J3DU/J3DUClipper.cpp libs/JSystem/src/J3DU/J3DUClipper.cpp
libs/JSystem/src/J3DU/J3DUDL.cpp libs/JSystem/src/J3DU/J3DUDL.cpp
)
add_library(JSystem_JKernel STATIC
libs/JSystem/src/JKernel/JKRHeap.cpp libs/JSystem/src/JKernel/JKRHeap.cpp
libs/JSystem/src/JKernel/JKRExpHeap.cpp libs/JSystem/src/JKernel/JKRExpHeap.cpp
libs/JSystem/src/JKernel/JKRSolidHeap.cpp libs/JSystem/src/JKernel/JKRSolidHeap.cpp
@@ -359,14 +368,23 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JKernel/JKRDvdRipper.cpp libs/JSystem/src/JKernel/JKRDvdRipper.cpp
libs/JSystem/src/JKernel/JKRDvdAramRipper.cpp libs/JSystem/src/JKernel/JKRDvdAramRipper.cpp
libs/JSystem/src/JKernel/JKRDecomp.cpp libs/JSystem/src/JKernel/JKRDecomp.cpp
)
add_library(JSystem_JMath STATIC
libs/JSystem/src/JMath/JMath.cpp libs/JSystem/src/JMath/JMath.cpp
libs/JSystem/src/JMath/random.cpp libs/JSystem/src/JMath/random.cpp
libs/JSystem/src/JMath/JMATrigonometric.cpp libs/JSystem/src/JMath/JMATrigonometric.cpp
)
add_library(JSystem_JSupport STATIC
libs/JSystem/src/JSupport/JSUList.cpp libs/JSystem/src/JSupport/JSUList.cpp
libs/JSystem/src/JSupport/JSUInputStream.cpp libs/JSystem/src/JSupport/JSUInputStream.cpp
libs/JSystem/src/JSupport/JSUOutputStream.cpp libs/JSystem/src/JSupport/JSUOutputStream.cpp
libs/JSystem/src/JSupport/JSUMemoryStream.cpp libs/JSystem/src/JSupport/JSUMemoryStream.cpp
libs/JSystem/src/JSupport/JSUFileStream.cpp libs/JSystem/src/JSupport/JSUFileStream.cpp
)
add_library(JSystem_JUtility STATIC
libs/JSystem/src/JUtility/JUTCacheFont.cpp libs/JSystem/src/JUtility/JUTCacheFont.cpp
libs/JSystem/src/JUtility/JUTResource.cpp libs/JSystem/src/JUtility/JUTResource.cpp
libs/JSystem/src/JUtility/JUTTexture.cpp libs/JSystem/src/JUtility/JUTTexture.cpp
@@ -387,6 +405,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JUtility/JUTConsole.cpp libs/JSystem/src/JUtility/JUTConsole.cpp
libs/JSystem/src/JUtility/JUTDirectFile.cpp libs/JSystem/src/JUtility/JUTDirectFile.cpp
libs/JSystem/src/JUtility/JUTFontData_Ascfont_fix12.cpp libs/JSystem/src/JUtility/JUTFontData_Ascfont_fix12.cpp
)
add_library(JSystem_JStage STATIC
libs/JSystem/src/JStage/JSGActor.cpp libs/JSystem/src/JStage/JSGActor.cpp
libs/JSystem/src/JStage/JSGAmbientLight.cpp libs/JSystem/src/JStage/JSGAmbientLight.cpp
libs/JSystem/src/JStage/JSGCamera.cpp libs/JSystem/src/JStage/JSGCamera.cpp
@@ -394,6 +415,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JStage/JSGLight.cpp libs/JSystem/src/JStage/JSGLight.cpp
libs/JSystem/src/JStage/JSGObject.cpp libs/JSystem/src/JStage/JSGObject.cpp
libs/JSystem/src/JStage/JSGSystem.cpp libs/JSystem/src/JStage/JSGSystem.cpp
)
add_library(JSystem_J2DGraph STATIC
libs/JSystem/src/J2DGraph/J2DGrafContext.cpp libs/JSystem/src/J2DGraph/J2DGrafContext.cpp
libs/JSystem/src/J2DGraph/J2DOrthoGraph.cpp libs/JSystem/src/J2DGraph/J2DOrthoGraph.cpp
libs/JSystem/src/J2DGraph/J2DTevs.cpp libs/JSystem/src/J2DGraph/J2DTevs.cpp
@@ -412,6 +436,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/J2DGraph/J2DAnmLoader.cpp libs/JSystem/src/J2DGraph/J2DAnmLoader.cpp
libs/JSystem/src/J2DGraph/J2DAnimation.cpp libs/JSystem/src/J2DGraph/J2DAnimation.cpp
libs/JSystem/src/J2DGraph/J2DManage.cpp libs/JSystem/src/J2DGraph/J2DManage.cpp
)
add_library(JSystem_J3DGraphBase STATIC
libs/JSystem/src/J3DGraphBase/J3DGD.cpp libs/JSystem/src/J3DGraphBase/J3DGD.cpp
libs/JSystem/src/J3DGraphBase/J3DSys.cpp libs/JSystem/src/J3DGraphBase/J3DSys.cpp
libs/JSystem/src/J3DGraphBase/J3DVertex.cpp libs/JSystem/src/J3DGraphBase/J3DVertex.cpp
@@ -426,6 +453,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/J3DGraphBase/J3DTevs.cpp libs/JSystem/src/J3DGraphBase/J3DTevs.cpp
libs/JSystem/src/J3DGraphBase/J3DDrawBuffer.cpp libs/JSystem/src/J3DGraphBase/J3DDrawBuffer.cpp
libs/JSystem/src/J3DGraphBase/J3DStruct.cpp libs/JSystem/src/J3DGraphBase/J3DStruct.cpp
)
add_library(JSystem_J3DGraphAnimator STATIC
libs/JSystem/src/J3DGraphAnimator/J3DShapeTable.cpp libs/JSystem/src/J3DGraphAnimator/J3DShapeTable.cpp
libs/JSystem/src/J3DGraphAnimator/J3DJointTree.cpp libs/JSystem/src/J3DGraphAnimator/J3DJointTree.cpp
libs/JSystem/src/J3DGraphAnimator/J3DModelData.cpp libs/JSystem/src/J3DGraphAnimator/J3DModelData.cpp
@@ -437,6 +467,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/J3DGraphAnimator/J3DCluster.cpp libs/JSystem/src/J3DGraphAnimator/J3DCluster.cpp
libs/JSystem/src/J3DGraphAnimator/J3DJoint.cpp libs/JSystem/src/J3DGraphAnimator/J3DJoint.cpp
libs/JSystem/src/J3DGraphAnimator/J3DMaterialAttach.cpp libs/JSystem/src/J3DGraphAnimator/J3DMaterialAttach.cpp
)
add_library(JSystem_J3DGraphLoader STATIC
libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp
libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp
libs/JSystem/src/J3DGraphLoader/J3DClusterLoader.cpp libs/JSystem/src/J3DGraphLoader/J3DClusterLoader.cpp
@@ -445,6 +478,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/J3DGraphLoader/J3DJointFactory.cpp libs/JSystem/src/J3DGraphLoader/J3DJointFactory.cpp
libs/JSystem/src/J3DGraphLoader/J3DShapeFactory.cpp libs/JSystem/src/J3DGraphLoader/J3DShapeFactory.cpp
libs/JSystem/src/J3DGraphLoader/J3DAnmLoader.cpp libs/JSystem/src/J3DGraphLoader/J3DAnmLoader.cpp
)
add_library(JSystem_JStudio STATIC
libs/JSystem/src/JStudio/JStudio/ctb.cpp libs/JSystem/src/JStudio/JStudio/ctb.cpp
libs/JSystem/src/JStudio/JStudio/ctb-data.cpp libs/JSystem/src/JStudio/JStudio/ctb-data.cpp
libs/JSystem/src/JStudio/JStudio/functionvalue.cpp libs/JSystem/src/JStudio/JStudio/functionvalue.cpp
@@ -459,6 +495,9 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JStudio/JStudio/stb.cpp libs/JSystem/src/JStudio/JStudio/stb.cpp
libs/JSystem/src/JStudio/JStudio/stb-data-parse.cpp libs/JSystem/src/JStudio/JStudio/stb-data-parse.cpp
libs/JSystem/src/JStudio/JStudio/stb-data.cpp libs/JSystem/src/JStudio/JStudio/stb-data.cpp
)
add_library(JSystem_JStudio_JStage STATIC
libs/JSystem/src/JStudio/JStudio_JStage/control.cpp libs/JSystem/src/JStudio/JStudio_JStage/control.cpp
libs/JSystem/src/JStudio/JStudio_JStage/object.cpp libs/JSystem/src/JStudio/JStudio_JStage/object.cpp
libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp
@@ -466,10 +505,19 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JStudio/JStudio_JStage/object-camera.cpp libs/JSystem/src/JStudio/JStudio_JStage/object-camera.cpp
libs/JSystem/src/JStudio/JStudio_JStage/object-fog.cpp libs/JSystem/src/JStudio/JStudio_JStage/object-fog.cpp
libs/JSystem/src/JStudio/JStudio_JStage/object-light.cpp libs/JSystem/src/JStudio/JStudio_JStage/object-light.cpp
)
add_library(JSystem_JStudio_JAudio2 STATIC
libs/JSystem/src/JStudio/JStudio_JAudio2/control.cpp libs/JSystem/src/JStudio/JStudio_JAudio2/control.cpp
libs/JSystem/src/JStudio/JStudio_JAudio2/object-sound.cpp libs/JSystem/src/JStudio/JStudio_JAudio2/object-sound.cpp
)
add_library(JSystem_JStudio_JParticle STATIC
libs/JSystem/src/JStudio/JStudio_JParticle/control.cpp libs/JSystem/src/JStudio/JStudio_JParticle/control.cpp
libs/JSystem/src/JStudio/JStudio_JParticle/object-particle.cpp libs/JSystem/src/JStudio/JStudio_JParticle/object-particle.cpp
)
add_library(JSystem_JAudio2 STATIC
libs/JSystem/src/JAudio2/JASCalc.cpp libs/JSystem/src/JAudio2/JASCalc.cpp
libs/JSystem/src/JAudio2/JASTaskThread.cpp libs/JSystem/src/JAudio2/JASTaskThread.cpp
libs/JSystem/src/JAudio2/JASDvdThread.cpp libs/JSystem/src/JAudio2/JASDvdThread.cpp
@@ -534,22 +582,34 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JAudio2/JAUSoundAnimator.cpp libs/JSystem/src/JAudio2/JAUSoundAnimator.cpp
libs/JSystem/src/JAudio2/JAUSoundTable.cpp libs/JSystem/src/JAudio2/JAUSoundTable.cpp
libs/JSystem/src/JAudio2/JAUStreamFileTable.cpp libs/JSystem/src/JAudio2/JAUStreamFileTable.cpp
)
add_library(JSystem_JMessage STATIC
libs/JSystem/src/JMessage/control.cpp libs/JSystem/src/JMessage/control.cpp
libs/JSystem/src/JMessage/data.cpp libs/JSystem/src/JMessage/data.cpp
libs/JSystem/src/JMessage/processor.cpp libs/JSystem/src/JMessage/processor.cpp
libs/JSystem/src/JMessage/resource.cpp libs/JSystem/src/JMessage/resource.cpp
libs/JSystem/src/JMessage/locale.cpp libs/JSystem/src/JMessage/locale.cpp
)
add_library(JSystem_JGadget STATIC
libs/JSystem/src/JGadget/binary.cpp libs/JSystem/src/JGadget/binary.cpp
libs/JSystem/src/JGadget/define.cpp libs/JSystem/src/JGadget/define.cpp
libs/JSystem/src/JGadget/linklist.cpp libs/JSystem/src/JGadget/linklist.cpp
libs/JSystem/src/JGadget/search.cpp libs/JSystem/src/JGadget/search.cpp
libs/JSystem/src/JGadget/std-vector.cpp libs/JSystem/src/JGadget/std-vector.cpp
)
add_library(JSystem_JAHostIO STATIC
libs/JSystem/src/JAHostIO/JAHFrameNode.cpp libs/JSystem/src/JAHostIO/JAHFrameNode.cpp
libs/JSystem/src/JAHostIO/JAHioMessage.cpp libs/JSystem/src/JAHostIO/JAHioMessage.cpp
libs/JSystem/src/JAHostIO/JAHioMgr.cpp libs/JSystem/src/JAHostIO/JAHioMgr.cpp
libs/JSystem/src/JAHostIO/JAHioNode.cpp libs/JSystem/src/JAHostIO/JAHioNode.cpp
libs/JSystem/src/JAHostIO/JAHioUtil.cpp libs/JSystem/src/JAHostIO/JAHioUtil.cpp
libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp
)
add_library(JSystem_JHostIO STATIC
libs/JSystem/src/JHostIO/JORFile.cpp libs/JSystem/src/JHostIO/JORFile.cpp
libs/JSystem/src/JHostIO/JORHostInfo.cpp libs/JSystem/src/JHostIO/JORHostInfo.cpp
libs/JSystem/src/JHostIO/JORMessageBox.cpp libs/JSystem/src/JHostIO/JORMessageBox.cpp
@@ -559,7 +619,28 @@ set(JSYSTEM_DEBUG_FILES
libs/JSystem/src/JHostIO/JHIMccBuf.cpp libs/JSystem/src/JHostIO/JHIMccBuf.cpp
) )
set(JSYSTEM_FILES set(JSYSTEM_LIBRARIES
JSystem_JParticle
JSystem_JFramework
JSystem_J3DU
JSystem_JKernel
JSystem_JMath
JSystem_JSupport
JSystem_JUtility
JSystem_JStage
JSystem_J2DGraph
JSystem_J3DGraphBase
JSystem_J3DGraphAnimator
JSystem_J3DGraphLoader
JSystem_JStudio
JSystem_JStudio_JStage
JSystem_JStudio_JAudio2
JSystem_JStudio_JParticle
JSystem_JAudio2
JSystem_JMessage
JSystem_JGadget
JSystem_JAHostIO
JSystem_JHostIO
) )
set(REL_FILES set(REL_FILES
@@ -1341,13 +1422,13 @@ set(DUSK_FILES
src/dusk/crash_reporting.cpp src/dusk/crash_reporting.cpp
src/dusk/endian.cpp src/dusk/endian.cpp
src/dusk/extras.c src/dusk/extras.c
src/dusk/extras.cpp
src/dusk/file_select.cpp src/dusk/file_select.cpp
src/dusk/file_select.hpp src/dusk/file_select.hpp
src/dusk/frame_interpolation.cpp src/dusk/frame_interpolation.cpp
src/dusk/game_clock.cpp src/dusk/game_clock.cpp
src/dusk/globals.cpp src/dusk/globals.cpp
src/dusk/gyro.cpp src/dusk/gyro.cpp
src/dusk/gamepad_color.cpp
src/dusk/io.cpp src/dusk/io.cpp
src/dusk/layout.cpp src/dusk/layout.cpp
src/dusk/logging.cpp src/dusk/logging.cpp
+4
View File
@@ -103,6 +103,10 @@ public:
field_0xd98 = param_1; field_0xd98 = param_1;
} }
#if TARGET_PC
void resetScrollArrowMask() { field_0xdda = 0; }
#endif
/* 0xC98 */ JKRExpHeap* mpHeap; /* 0xC98 */ JKRExpHeap* mpHeap;
/* 0xC9C */ JKRExpHeap* mpTalkHeap; /* 0xC9C */ JKRExpHeap* mpTalkHeap;
/* 0xCA0 */ STControl* mpStick; /* 0xCA0 */ STControl* mpStick;
+9
View File
@@ -206,6 +206,15 @@ private:
/* 0x6D3 */ u8 field_0x6d3; /* 0x6D3 */ u8 field_0x6d3;
#if TARGET_PC #if TARGET_PC
f32 mSelectItemSlideElapsed[4]; f32 mSelectItemSlideElapsed[4];
f32 mCursorInterpPrevX;
f32 mCursorInterpPrevY;
f32 mCursorInterpCurrX;
f32 mCursorInterpCurrY;
s16 mCursorInterpPrevAngle;
s16 mCursorInterpCurrAngle;
bool mCursorInterpPrevAngular;
bool mCursorInterpCurrAngular;
bool mCursorInterpInit;
#endif #endif
}; };
+3
View File
@@ -48,6 +48,9 @@ public:
} }
#ifdef TARGET_PC #ifdef TARGET_PC
f32 getPositionX() const { return mPositionX; }
f32 getPositionY() const { return mPositionY; }
void refreshAspectScale(); void refreshAspectScale();
#endif #endif
+1
View File
@@ -16,6 +16,7 @@ void ensure_initialized();
void begin_record(); void begin_record();
void end_record(); void end_record();
void begin_sim_tick();
void begin_frame(bool enabled, bool is_sim_frame, float step); void begin_frame(bool enabled, bool is_sim_frame, float step);
void interpolate(); void interpolate();
float get_interpolation_step(); float get_interpolation_step();
+6 -13
View File
@@ -1,13 +1,8 @@
#ifndef DUSK_GAME_CLOCK_H #pragma once
#define DUSK_GAME_CLOCK_H
#include <stddef.h> namespace dusk::game_clock {
namespace dusk {
namespace game_clock {
void ensure_initialized(); void ensure_initialized();
void reset_accumulator();
void reset_frame_timer(); void reset_frame_timer();
constexpr float sim_pace() { return 1.0f / 30.0f; } constexpr float sim_pace() { return 1.0f / 30.0f; }
@@ -18,16 +13,14 @@ constexpr float ui_initial_dt() { return 1.0f / 60.0f; }
struct MainLoopPacer { struct MainLoopPacer {
float presentation_dt_seconds; float presentation_dt_seconds;
bool is_interpolating; bool is_interpolating;
bool do_sim_tick; int sim_ticks_to_run;
float interpolation_step;
float sim_pace; float sim_pace;
}; };
MainLoopPacer advance_main_loop(); MainLoopPacer advance_main_loop();
void commit_sim_tick();
float sample_interpolation_step();
float consume_interval(const void* consumer); float consume_interval(const void* consumer);
} // namespace game_clock } // namespace dusk::game_clock
} // namespace dusk
#endif // DUSK_GAME_CLOCK_H
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#ifndef GAMEPAD_COLOR_H
#define GAMEPAD_COLOR_H
void handleGamepadColor();
#endif
+5
View File
@@ -69,6 +69,8 @@ struct UserSettings {
ConfigVar<bool> invertCameraXAxis; ConfigVar<bool> invertCameraXAxis;
ConfigVar<bool> disableMainHUD; ConfigVar<bool> disableMainHUD;
ConfigVar<bool> pauseOnFocusLost; ConfigVar<bool> pauseOnFocusLost;
ConfigVar<bool> enableLinkDollRotation;
// Graphics // Graphics
ConfigVar<BloomMode> bloomMode; ConfigVar<BloomMode> bloomMode;
@@ -101,6 +103,7 @@ struct UserSettings {
ConfigVar<bool> infiniteOil; ConfigVar<bool> infiniteOil;
ConfigVar<bool> infiniteOxygen; ConfigVar<bool> infiniteOxygen;
ConfigVar<bool> infiniteRupees; ConfigVar<bool> infiniteRupees;
ConfigVar<bool> enableIndefiniteItemDrops;
ConfigVar<bool> moonJump; ConfigVar<bool> moonJump;
ConfigVar<bool> superClawshot; ConfigVar<bool> superClawshot;
ConfigVar<bool> alwaysGreatspin; ConfigVar<bool> alwaysGreatspin;
@@ -124,6 +127,7 @@ struct UserSettings {
ConfigVar<bool> wasPresetChosen; ConfigVar<bool> wasPresetChosen;
ConfigVar<bool> enableCrashReporting; ConfigVar<bool> enableCrashReporting;
ConfigVar<bool> duskMenuOpen; ConfigVar<bool> duskMenuOpen;
ConfigVar<int> cardFileType;
} backend; } backend;
}; };
@@ -148,6 +152,7 @@ struct TransientSettings {
CollisionViewSettings collisionView; CollisionViewSettings collisionView;
bool skipFrameRateLimit; bool skipFrameRateLimit;
bool moveLinkActive; bool moveLinkActive;
bool stateShareLoadActive;
}; };
TransientSettings& getTransientSettings(); TransientSettings& getTransientSettings();
+25 -25
View File
@@ -1,9 +1,10 @@
#ifndef DUSK_TIME_H #ifndef DUSK_TIME_H
#define DUSK_TIME_H #define DUSK_TIME_H
#include <chrono>
#include <numeric>
#include <array> #include <array>
#include <numeric>
#include "SDL3/SDL_timer.h"
#ifdef _WIN32 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
@@ -15,28 +16,26 @@
#include <Windows.h> #include <Windows.h>
#include <shellapi.h> #include <shellapi.h>
#include <intrin.h> #include <intrin.h>
#else
#include "SDL3/SDL_timer.h"
#endif #endif
class Limiter { class Limiter {
using delta_clock = std::chrono::high_resolution_clock;
using duration_t = std::chrono::nanoseconds;
public: public:
void Reset() { m_oldTime = delta_clock::now(); } using duration_t = Uint64;
void Reset() { m_oldTime = SDL_GetTicksNS(); }
void Sleep(duration_t targetFrameTime) { void Sleep(duration_t targetFrameTime) {
if (targetFrameTime.count() == 0) { if (targetFrameTime == 0) {
return; return;
} }
auto start = delta_clock::now(); const Uint64 start = SDL_GetTicksNS();
duration_t adjustedSleepTime = SleepTime(targetFrameTime); duration_t adjustedSleepTime = SleepTime(targetFrameTime);
if (adjustedSleepTime.count() > 0) { if (adjustedSleepTime > 0) {
NanoSleep(adjustedSleepTime); NanoSleep(adjustedSleepTime);
duration_t overslept = TimeSince(start) - adjustedSleepTime; const duration_t elapsed = TimeSince(start);
if (overslept < duration_t{targetFrameTime}) { const duration_t overslept = elapsed > adjustedSleepTime ? elapsed - adjustedSleepTime : 0;
if (overslept < targetFrameTime) {
m_overheadTimes[m_overheadTimeIdx] = overslept; m_overheadTimes[m_overheadTimeIdx] = overslept;
m_overheadTimeIdx = (m_overheadTimeIdx + 1) % m_overheadTimes.size(); m_overheadTimeIdx = (m_overheadTimeIdx + 1) % m_overheadTimes.size();
} }
@@ -45,23 +44,23 @@ public:
} }
duration_t SleepTime(duration_t targetFrameTime) { duration_t SleepTime(duration_t targetFrameTime) {
const auto sleepTime = duration_t{targetFrameTime} - TimeSince(m_oldTime); const duration_t elapsed = TimeSince(m_oldTime);
m_overhead = std::accumulate(m_overheadTimes.begin(), m_overheadTimes.end(), duration_t{}) / m_overheadTimes.size(); const duration_t sleepTime = elapsed < targetFrameTime ? targetFrameTime - elapsed : 0;
m_overhead = std::accumulate(m_overheadTimes.begin(), m_overheadTimes.end(), duration_t{0}) /
m_overheadTimes.size();
if (sleepTime > m_overhead) { if (sleepTime > m_overhead) {
return sleepTime - m_overhead; return sleepTime - m_overhead;
} }
return duration_t{0}; return 0;
} }
private: private:
delta_clock::time_point m_oldTime; Uint64 m_oldTime = 0;
std::array<duration_t, 4> m_overheadTimes{}; std::array<duration_t, 4> m_overheadTimes{};
size_t m_overheadTimeIdx = 0; size_t m_overheadTimeIdx = 0;
duration_t m_overhead = duration_t{0}; duration_t m_overhead = 0;
duration_t TimeSince(delta_clock::time_point start) { duration_t TimeSince(Uint64 start) const { return SDL_GetTicksNS() - start; }
return std::chrono::duration_cast<duration_t>(delta_clock::now() - start);
}
#if _WIN32 #if _WIN32
void NanoSleep(const duration_t duration) { void NanoSleep(const duration_t duration) {
@@ -85,9 +84,10 @@ private:
LARGE_INTEGER start, current; LARGE_INTEGER start, current;
QueryPerformanceCounter(&start); QueryPerformanceCounter(&start);
LONGLONG ticksToWait = static_cast<LONGLONG>(duration.count() * countPerNs); const LONGLONG ticksToWait = static_cast<LONGLONG>(duration * countPerNs);
if (DWORD ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); ms > 1) { const Uint64 ms = duration / 1'000'000ULL;
::Sleep(ms - 1); if (ms > 1) {
::Sleep(static_cast<DWORD>(ms - 1));
} }
do { do {
QueryPerformanceCounter(&current); QueryPerformanceCounter(&current);
@@ -99,7 +99,7 @@ private:
} while (current.QuadPart - start.QuadPart < ticksToWait); } while (current.QuadPart - start.QuadPart < ticksToWait);
} }
#else #else
void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration.count()); } void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration); }
#endif #endif
}; };
+11 -1
View File
@@ -73,6 +73,9 @@
#endif #endif
#ifndef __MWERKS__ #ifndef __MWERKS__
#ifdef __cplusplus
extern "C" {
#endif
// Silence clangd errors about MWCC PPC intrinsics by declaring them here. // Silence clangd errors about MWCC PPC intrinsics by declaring them here.
extern int __cntlzw(unsigned int); extern int __cntlzw(unsigned int);
extern int __rlwimi(int, int, int, int, int); extern int __rlwimi(int, int, int, int, int);
@@ -80,7 +83,14 @@ extern void __dcbf(void*, int);
extern void __dcbz(void*, int); extern void __dcbz(void*, int);
extern void __sync(); extern void __sync();
extern int __abs(int); extern int __abs(int);
void* __memcpy(void*, const void*, int); #if defined(__has_builtin) && __has_builtin(__builtin_memcpy)
#define __memcpy __builtin_memcpy
#else
#define __memcpy memcpy
#endif
#ifdef __cplusplus
}
#endif
#endif #endif
#ifndef M_PI #ifndef M_PI
@@ -263,6 +263,9 @@ public:
/* 0x9C */ u8 field_0x9c[4]; /* 0x9C */ u8 field_0x9c[4];
/* 0xA0 */ OSTime mResetHoldStartTime; /* 0xA0 */ OSTime mResetHoldStartTime;
/* 0xA8 */ u8 field_0xa8; /* 0xA8 */ u8 field_0xa8;
#if TARGET_PC
u32 mResetHoldFrameCount;
#endif
}; };
/** /**
+12 -4
View File
@@ -18,6 +18,7 @@
#include "dusk/logging.h" #include "dusk/logging.h"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/time.h" #include "dusk/time.h"
#include "f_op/f_op_overlap_mng.h"
#include "SDL3/SDL_timer.h" #include "SDL3/SDL_timer.h"
#include "tracy/Tracy.hpp" #include "tracy/Tracy.hpp"
@@ -368,11 +369,11 @@ constexpr auto FRAME_PERIOD = std::chrono::duration_cast<std::chrono::nanosecond
std::chrono::duration<double>(1001.0 / 30000.0)); std::chrono::duration<double>(1001.0 / 30000.0));
constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2; constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2;
static void waitPrecise(Limiter& limiter, Uint64 targetNs) { static void waitPrecise(Limiter& limiter, Limiter::duration_t targetNs) {
const auto sleepTime = limiter.SleepTime(std::chrono::nanoseconds(targetNs)); const auto sleepTime = limiter.SleepTime(targetNs);
dusk::frameUsagePct = dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime.count()) / static_cast<float>(targetNs)); 100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
limiter.Sleep(std::chrono::nanoseconds(targetNs)); limiter.Sleep(targetNs);
} }
#endif #endif
@@ -385,6 +386,13 @@ static void waitForTick(u32 p1, u16 p2) {
if (dusk::getTransientSettings().skipFrameRateLimit) { if (dusk::getTransientSettings().skipFrameRateLimit) {
p1 = OS_TIMER_CLOCK / 120; p1 = OS_TIMER_CLOCK / 120;
} }
#if TARGET_PC
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
return;
}
#endif
ZoneScopedC(tracy::Color::DimGray); ZoneScopedC(tracy::Color::DimGray);
#endif #endif
+11
View File
@@ -64,6 +64,9 @@ BOOL JUTGamePad::init() {
void JUTGamePad::clear() { void JUTGamePad::clear() {
mButtonReset.mReset = false; mButtonReset.mReset = false;
field_0xa8 = 1; field_0xa8 = 1;
#if TARGET_PC
mResetHoldFrameCount = 0;
#endif
} }
PADStatus JUTGamePad::mPadStatus[4]; PADStatus JUTGamePad::mPadStatus[4];
@@ -219,11 +222,19 @@ void JUTGamePad::update() {
mButtonReset.mReset = false; mButtonReset.mReset = false;
} else if (!JUTGamePad::C3ButtonReset::sResetOccurred) { } else if (!JUTGamePad::C3ButtonReset::sResetOccurred) {
if (mButtonReset.mReset == true) { if (mButtonReset.mReset == true) {
#if TARGET_PC
checkResetCallback(++mResetHoldFrameCount * (OS_TIMER_CLOCK / 30));
#else
OSTime hold_time = OSGetTime() - mResetHoldStartTime; OSTime hold_time = OSGetTime() - mResetHoldStartTime;
checkResetCallback(hold_time); checkResetCallback(hold_time);
#endif
} else { } else {
mButtonReset.mReset = true; mButtonReset.mReset = true;
#if TARGET_PC
mResetHoldFrameCount = 0;
#else
mResetHoldStartTime = OSGetTime(); mResetHoldStartTime = OSGetTime();
#endif
} }
} }
+5 -5
View File
@@ -117,8 +117,8 @@ static Z2WolfHowlLine sNewSong3[9] = {
#if TARGET_PC #if TARGET_PC
static Z2WolfHowlLine sHowlTimeSong[6] = { static Z2WolfHowlLine sHowlTimeSong[6] = {
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40}, {HOWL_LINE_MID, 15}, {HOWL_LINE_LOW, 15}, {HOWL_LINE_HIGH, 30},
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40}, {HOWL_LINE_MID, 15}, {HOWL_LINE_LOW, 15}, {HOWL_LINE_HIGH, 30},
}; };
#endif #endif
@@ -368,9 +368,9 @@ void Z2WolfHowlMgr::setCorrectData(s8 curveID, Z2WolfHowlData* data) {
break; break;
#if TARGET_PC #if TARGET_PC
case Z2WOLFHOWL_TIMESONG: case Z2WOLFHOWL_TIMESONG:
cPitchUp = 1.259906f; cPitchUp = 1.3348f;
cPitchCenter = 0.94387f; cPitchCenter = 0.8909f;
cPitchDown = 0.840885f; cPitchDown = 0.7937f;
break; break;
#endif #endif
default: default:
+8 -2
View File
@@ -41,7 +41,7 @@ void daAlink_c::handleWolfHowl() {
return; return;
} }
bool canTransform = false; bool canHowl = false;
if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) && !checkMagneBootsOn()) { if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) && !checkMagneBootsOn()) {
if (!checkForestOldCentury()) { if (!checkForestOldCentury()) {
@@ -52,12 +52,17 @@ void daAlink_c::handleWolfHowl() {
(checkEventRun() || getMidnaActor()->checkMetamorphoseEnable()) && (checkEventRun() || getMidnaActor()->checkMetamorphoseEnable()) &&
(checkModeFlg(4) || dComIfGp_checkPlayerStatus0(0, 0x10)))) (checkModeFlg(4) || dComIfGp_checkPlayerStatus0(0, 0x10))))
{ {
canTransform = true; canHowl = true;
} }
} }
} }
} }
if (!canHowl) {
Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0);
return;
}
getWolfHowlMgrP()->setCorrectCurve(9); getWolfHowlMgrP()->setCorrectCurve(9);
procWolfHowlDemoInit(); procWolfHowlDemoInit();
} }
@@ -154,6 +159,7 @@ bool daAlink_c::checkGyroAimContext() {
case PROC_BOW_SUBJECT: case PROC_BOW_SUBJECT:
case PROC_BOOMERANG_SUBJECT: case PROC_BOOMERANG_SUBJECT:
case PROC_COPY_ROD_SUBJECT: case PROC_COPY_ROD_SUBJECT:
case PROC_HAWK_SUBJECT:
case PROC_HOOKSHOT_SUBJECT: case PROC_HOOKSHOT_SUBJECT:
case PROC_SWIM_HOOKSHOT_SUBJECT: case PROC_SWIM_HOOKSHOT_SUBJECT:
case PROC_HORSE_BOW_SUBJECT: case PROC_HORSE_BOW_SUBJECT:
+54
View File
@@ -149,6 +149,23 @@ void daAlink_c::changeWolf() {
mpLinkModel = initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 14)), 0x20200); mpLinkModel = initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 14)), 0x20200);
#ifdef TARGET_PC
// Update Wolf Link's eye maxLOD to prevent the eyes from disappearing
{
J3DTexture* tex = mpLinkModel->getModelData()->getTexture();
JUTNameTab* nametable = mpLinkModel->getModelData()->getTextureName();
if (tex != nullptr && nametable != nullptr) {
for (u16 i = 0; i < tex->getNum(); i++) {
const char* tex_name = nametable->getName(i);
if (tex_name != NULL && strcmp(tex_name, "wl_eyeball") == 0) {
ResTIMG* timg = tex->getResTIMG(i);
timg->maxLOD = 0;
}
}
}
}
#endif
J3DModelData* chainModelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 15)); J3DModelData* chainModelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 15));
for (u16 i = 0; i < 4; i++) { for (u16 i = 0; i < 4; i++) {
mpWlChainModels[i] = initModel(chainModelData, 0); mpWlChainModels[i] = initModel(chainModelData, 0);
@@ -162,6 +179,23 @@ void daAlink_c::changeWolf() {
mpWlMidnaHairModel = mpWlMidnaHairModel =
initModelEnv(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 11)), 0x1000000); initModelEnv(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 11)), 0x1000000);
#ifdef TARGET_PC
// Update Midna's eye maxLOD to prevent the eyes from disappearing
{
J3DTexture* tex = mpWlMidnaModel->getModelData()->getTexture();
JUTNameTab* nametable = mpWlMidnaModel->getModelData()->getTextureName();
if (tex != nullptr && nametable != nullptr) {
for (u16 i = 0; i < tex->getNum(); i++) {
const char* tex_name = nametable->getName(i);
if (tex_name != NULL && strcmp(tex_name, "midona_eyeball") == 0) {
ResTIMG* timg = tex->getResTIMG(i);
timg->maxLOD = 0;
}
}
}
}
#endif
mpDMidnaBrk = static_cast<J3DAnmTevRegKey*>(dComIfG_getObjectRes(l_wArcName, 18)); mpDMidnaBrk = static_cast<J3DAnmTevRegKey*>(dComIfG_getObjectRes(l_wArcName, 18));
mpDMidnaBrk->searchUpdateMaterialID(mpWlMidnaModel->getModelData()); mpDMidnaBrk->searchUpdateMaterialID(mpWlMidnaModel->getModelData());
mpWlMidnaModel->getModelData()->entryTevRegAnimator(mpDMidnaBrk); mpWlMidnaModel->getModelData()->entryTevRegAnimator(mpDMidnaBrk);
@@ -342,6 +376,26 @@ void daAlink_c::changeLink(int param_0) {
initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(mArcName, "zl_face.bmd")), 0x20200); initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(mArcName, "zl_face.bmd")), 0x20200);
} }
#ifdef TARGET_PC
// Update Adult Link's eye maxLOD to prevent the eyes from disappearing
{
J3DTexture* tex = mpLinkFaceModel->getModelData()->getTexture();
JUTNameTab* nametable = mpLinkFaceModel->getModelData()->getTextureName();
if (tex != nullptr && nametable != nullptr) {
for (u16 i = 0; i < tex->getNum(); i++) {
const char* tex_name = nametable->getName(i);
if (tex_name != nullptr &&
(strcmp(tex_name, "al_eyeball") == 0 || strcmp(tex_name, "highlight02") == 0 ||
strcmp(tex_name, "eye_kage01") == 0))
{
ResTIMG* timg = tex->getResTIMG(i);
timg->maxLOD = 0;
}
}
}
}
#endif
modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(mArcName, "al_bootsH.bmd")); modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(mArcName, "al_bootsH.bmd"));
u16 i; u16 i;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
+9 -1
View File
@@ -2059,7 +2059,15 @@ static void demo_camera(b_bq_class* i_this) {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
static u16 g_e_i[] = {0x83EB, 0x83EC, 0x83ED, 0x83EE, 0x83EF}; static u16 g_e_i[] = {0x83EB, 0x83EC, 0x83ED, 0x83EE, 0x83EF};
dComIfGp_particle_set(g_e_i[i], &pos, NULL, NULL); #if TARGET_PC
if (i == 0) {
static const cXyz effWideScale = {mDoGph_gInf_c::getAspect(), 1.0f, 1.0f};
dComIfGp_particle_set(g_e_i[i], &pos, NULL, &effWideScale);
} else
#endif
{
dComIfGp_particle_set(g_e_i[i], &pos, NULL, NULL);
}
} }
i_this->mSound.startCreatureSound(Z2SE_EN_BOSS_CONVERGE, 0, 0); i_this->mSound.startCreatureSound(Z2SE_EN_BOSS_CONVERGE, 0, 0);
+10 -1
View File
@@ -2725,7 +2725,16 @@ static void demo_camera(b_ob_class* i_this) {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
static u16 ex_eff[] = {dPa_RM(ID_ZI_S_OI_CONVERGE_FILTER), dPa_RM(ID_ZI_S_OI_CONVERGE_FILTEROUT), dPa_RM(ID_ZI_S_OI_CONVERGE_HIDE), dPa_RM(ID_ZI_S_OI_CONVERGE_POLYGON_A), dPa_RM(ID_ZI_S_OI_CONVERGE_POLYGON_B)}; static u16 ex_eff[] = {dPa_RM(ID_ZI_S_OI_CONVERGE_FILTER), dPa_RM(ID_ZI_S_OI_CONVERGE_FILTEROUT), dPa_RM(ID_ZI_S_OI_CONVERGE_HIDE), dPa_RM(ID_ZI_S_OI_CONVERGE_POLYGON_A), dPa_RM(ID_ZI_S_OI_CONVERGE_POLYGON_B)};
dComIfGp_particle_set(ex_eff[i], &room_pos, NULL, &sc);
#if TARGET_PC
if (i == 0) {
static const cXyz effWideScale = {mDoGph_gInf_c::getAspect() * 10.0f, 10.0f, 10.0f};
dComIfGp_particle_set(ex_eff[i], &room_pos, NULL, &effWideScale);
} else
#endif
{
dComIfGp_particle_set(ex_eff[i], &room_pos, NULL, &sc);
}
} }
i_this->mDemoCamEye.set(-4820.0f, -18600.0f, -510.0f); i_this->mDemoCamEye.set(-4820.0f, -18600.0f, -510.0f);
+10 -1
View File
@@ -1677,7 +1677,16 @@ static void demo_camera(e_fm_class* i_this) {
cXyz spBC(0.0f, 0.0f, 0.0f); cXyz spBC(0.0f, 0.0f, 0.0f);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
static u16 g_e_i[] = {0x847B, 0x847C, 0x847D, 0x847E}; static u16 g_e_i[] = {0x847B, 0x847C, 0x847D, 0x847E};
dComIfGp_particle_set(g_e_i[i], &spBC, NULL, NULL);
#if TARGET_PC
if (i == 0) {
static const cXyz effWideScale = {mDoGph_gInf_c::getAspect(), 1.0f, 1.0f};
dComIfGp_particle_set(g_e_i[i], &spBC, NULL, &effWideScale);
} else
#endif
{
dComIfGp_particle_set(g_e_i[i], &spBC, NULL, NULL);
}
} }
i_this->mDemoCamFovy = 55.0f + NREG_F(10); i_this->mDemoCamFovy = 55.0f + NREG_F(10);
+10 -10
View File
@@ -761,6 +761,11 @@ static void koro2_game(fshop_class* i_this) {
sp5C.x = mDoCPd_c::getStickX3D(PAD_1); sp5C.x = mDoCPd_c::getStickX3D(PAD_1);
sp5C.y = 0.0f; sp5C.y = 0.0f;
sp5C.z = mDoCPd_c::getStickY(PAD_1); sp5C.z = mDoCPd_c::getStickY(PAD_1);
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
sp5C.x = -sp5C.x;
}
#endif
MtxPosition(&sp5C, &sp68); MtxPosition(&sp5C, &sp68);
f32 reg_f31 = sp68.x; f32 reg_f31 = sp68.x;
@@ -782,20 +787,15 @@ static void koro2_game(fshop_class* i_this) {
reg_f30 = 0.0f; reg_f30 = 0.0f;
} }
s16 gyro_ax = 0;
s16 gyro_az = 0;
#if TARGET_PC #if TARGET_PC
if (dusk::getSettings().game.enableGyroRollgoal) { if (dusk::getSettings().game.enableGyroRollgoal) {
s16 rg_add_x; dusk::gyro::rollgoalTableOffset(gyro_ax, gyro_az);
s16 rg_add_z;
dusk::gyro::rollgoalTableOffset(rg_add_x, rg_add_z);
s16 tgt_x = static_cast<s16>(reg_f30 * (-6000.0f + JREG_F(7))) + rg_add_x;
s16 tgt_z = static_cast<s16>(reg_f31 * (-6000.0f + JREG_F(8))) + rg_add_z;
cLib_addCalcAngleS2(&i_this->field_0x4020.x, tgt_x, 4, 0x200);
cLib_addCalcAngleS2(&i_this->field_0x4020.z, tgt_z, 4, 0x200);
} }
#else
cLib_addCalcAngleS2(&i_this->field_0x4020.x, reg_f30 * (-6000.0f + JREG_F(7)), 4, 0x200);
cLib_addCalcAngleS2(&i_this->field_0x4020.z, reg_f31 * (-6000.0f + JREG_F(8)), 4, 0x200);
#endif #endif
cLib_addCalcAngleS2(&i_this->field_0x4020.x, reg_f30 * (-6000.0f + JREG_F(7)) + gyro_ax, 4, 0x200);
cLib_addCalcAngleS2(&i_this->field_0x4020.z, reg_f31 * (-6000.0f + JREG_F(8)) + gyro_az, 4, 0x200);
} }
#if TARGET_PC #if TARGET_PC
if (i_this->field_0x4010 != 2) { if (i_this->field_0x4010 != 2) {
+4
View File
@@ -30,6 +30,10 @@ static char* l_arcName = "Mirror";
static char* l_arcName2 = "MR-Table"; static char* l_arcName2 = "MR-Table";
dMirror_packet_c::dMirror_packet_c() { dMirror_packet_c::dMirror_packet_c() {
#ifdef TARGET_PC
GXInitTexObj(&mTexObj, nullptr, 0, 0, static_cast<GXTexFmt>(-1), GX_MAX_TEXWRAPMODE,
GX_MAX_TEXWRAPMODE, GX_FALSE);
#endif
reset(); reset();
} }
+3 -3
View File
@@ -2855,7 +2855,7 @@ void* daMP_Reader(void*) {
#endif #endif
} }
static u8 daMP_ReadThreadStack[0x2000]; static u8 daMP_ReadThreadStack[DUSK_IF_ELSE(8, 0x2000)];
#if TARGET_PC #if TARGET_PC
static BOOL VideoThreadCancelled; static BOOL VideoThreadCancelled;
@@ -2880,7 +2880,7 @@ static BOOL daMP_CreateReadThread(s32 param_0) {
static OSThread daMP_VideoDecodeThread; static OSThread daMP_VideoDecodeThread;
static u8 daMP_VideoDecodeThreadStack[0x64000]; static u8 daMP_VideoDecodeThreadStack[DUSK_IF_ELSE(8, 0x64000)];
static OSMessageQueue daMP_FreeTextureSetQueue; static OSMessageQueue daMP_FreeTextureSetQueue;
@@ -3132,7 +3132,7 @@ static BOOL AudioThreadCancelled;
static OSThread daMP_AudioDecodeThread; static OSThread daMP_AudioDecodeThread;
static u8 daMP_AudioDecodeThreadStack[0x64000]; static u8 daMP_AudioDecodeThreadStack[DUSK_IF_ELSE(8, 0x64000)];
static OSMessageQueue daMP_FreeAudioBufferQueue; static OSMessageQueue daMP_FreeAudioBufferQueue;
+43 -1
View File
@@ -16,6 +16,10 @@
#include "f_op/f_op_camera_mng.h" #include "f_op/f_op_camera_mng.h"
#include "m_Do/m_Do_mtx.h" #include "m_Do/m_Do_mtx.h"
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
static f32 Reflect(cXyz* i_vec, cBgS_PolyInfo const& i_polyinfo, f32 i_scale) { static f32 Reflect(cXyz* i_vec, cBgS_PolyInfo const& i_polyinfo, f32 i_scale) {
cM3dGPla plane; cM3dGPla plane;
@@ -31,6 +35,27 @@ static f32 Reflect(cXyz* i_vec, cBgS_PolyInfo const& i_polyinfo, f32 i_scale) {
return 0.0f; return 0.0f;
} }
#if TARGET_PC
static void d_a_obj_item_interp_callback(bool isSimFrame, void* pUserWork) {
daItem_c* item = static_cast<daItem_c*>(pUserWork);
if (item == NULL || item->mpModel == NULL || !item->chkDraw()) {
return;
}
item->setTevStr();
if (item->mpBrkAnm != NULL) {
s8 tevFrm = item->getTevFrm();
if (tevFrm != -1) {
item->mpBrkAnm->entry(item->mpModel->getModelData(), tevFrm);
} else {
item->mpBrkAnm->entry(item->mpModel->getModelData());
}
}
if (item->chkFlag(4)) {
fopAcM_setEffectMtx(item, item->mpModel->getModelData());
}
}
#endif
const daItemBase_data& daItemBase_c::getData() { const daItemBase_data& daItemBase_c::getData() {
return m_data; return m_data;
} }
@@ -353,6 +378,10 @@ int daItem_c::_daItem_draw() {
return 1; return 1;
} }
#if TARGET_PC
dusk::frame_interp::add_interpolation_callback(&d_a_obj_item_interp_callback, this);
#endif
if (chkDraw()) { if (chkDraw()) {
return DrawBase(); return DrawBase();
} }
@@ -390,6 +419,9 @@ void daItem_c::procMainNormal() {
cLib_chaseF(&scale.z, mItemScale.z, step_z); cLib_chaseF(&scale.z, mItemScale.z, step_z);
} }
#if TARGET_PC
if (!dusk::getSettings().game.enableIndefiniteItemDrops) {
#endif
if (mWaitTimer == 0) { if (mWaitTimer == 0) {
if (mDisappearTimer == 0) { if (mDisappearTimer == 0) {
deleteItem(); deleteItem();
@@ -399,6 +431,9 @@ void daItem_c::procMainNormal() {
changeDraw(); changeDraw();
} }
} }
#if TARGET_PC
}
#endif
mCcCyl.SetC(current.pos); mCcCyl.SetC(current.pos);
dComIfG_Ccsp()->Set(&mCcCyl); dComIfG_Ccsp()->Set(&mCcCyl);
@@ -1058,9 +1093,16 @@ int daItem_c::CountTimer() {
if (checkCountTimer()) { if (checkCountTimer()) {
if (mWaitTimer > 0) { if (mWaitTimer > 0) {
mWaitTimer--; mWaitTimer--;
} else if (mDisappearTimer > 0) { }
#if TARGET_PC
else if (!dusk::getSettings().game.enableIndefiniteItemDrops && mDisappearTimer > 0) {
mDisappearTimer--; mDisappearTimer--;
} }
#else
else if (mDisappearTimer > 0) {
mDisappearTimer--;
}
#endif
} }
cLib_calcTimer<u8>(&mBoomWindTgTimer); cLib_calcTimer<u8>(&mBoomWindTgTimer);
+6 -9
View File
@@ -699,8 +699,8 @@ void dFlower_packet_c::draw() {
if (!cLib_checkBit<u8>(sp44->m_state, 4) && !cLib_checkBit<u8>(sp44->m_state, 0x40)) { if (!cLib_checkBit<u8>(sp44->m_state, 4) && !cLib_checkBit<u8>(sp44->m_state, 0x40)) {
#ifdef TARGET_PC #ifdef TARGET_PC
Mtx flower_mtx; Mtx flower_mtx;
if (dusk::frame_interp::lookup_replacement(reinterpret_cast<const void*>(&sp44->m_modelMtx), flower_mtx)) { if (dusk::frame_interp::lookup_replacement(&sp44->m_modelMtx, flower_mtx)) {
cMtx_concat(j3dSys.getViewMtx(), flower_mtx, flower_mtx);
GXLoadPosMtxImm(flower_mtx, 0); GXLoadPosMtxImm(flower_mtx, 0);
} else } else
#endif #endif
@@ -854,21 +854,18 @@ void dFlower_packet_c::draw() {
if (!cLib_checkBit<u8>(sp34->m_state, 4) && cLib_checkBit<u8>(sp34->m_state, 0x40)) { if (!cLib_checkBit<u8>(sp34->m_state, 4) && cLib_checkBit<u8>(sp34->m_state, 0x40)) {
#ifdef TARGET_PC #ifdef TARGET_PC
Mtx flower_mtx; Mtx flower_mtx;
if (dusk::frame_interp::lookup_replacement(reinterpret_cast<const void*>(&sp34->m_modelMtx), flower_mtx)) { if (dusk::frame_interp::lookup_replacement(&sp34->m_modelMtx, flower_mtx)) {
cMtx_concat(j3dSys.getViewMtx(), flower_mtx, flower_mtx); cMtx_concat(j3dSys.getViewMtx(), flower_mtx, flower_mtx);
GXLoadPosMtxImm(flower_mtx, 0); GXLoadPosMtxImm(flower_mtx, 0);
} else { } else
#endif #endif
{
GXLoadPosMtxImm(sp34->m_modelMtx, 0); GXLoadPosMtxImm(sp34->m_modelMtx, 0);
#ifdef TARGET_PC
} }
#endif
GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0); GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0);
#if TARGET_PC #if TARGET_PC
GXLoadTexObj(&mTexObj_l_J_Ohana01_64128_0419TEX, GX_TEXMAP0); GXLoadTexObj(&mTexObj_l_J_Ohana01_64128_0419TEX, GX_TEXMAP0);
#endif #endif
if (!cLib_checkBit<u8>(sp34->m_state, 8)) { if (!cLib_checkBit<u8>(sp34->m_state, 8)) {
if (!cLib_checkBit<u8>(sp34->m_state, 0x10)) { if (!cLib_checkBit<u8>(sp34->m_state, 0x10)) {
GXCallDisplayList(mp_Jhana01DL, m_Jhana01DL_size); GXCallDisplayList(mp_Jhana01DL, m_Jhana01DL_size);
@@ -995,7 +992,7 @@ void dFlower_packet_c::update() {
mDoMtx_stack_c::scaleM(temp_f31, temp_f31, temp_f31); mDoMtx_stack_c::scaleM(temp_f31, temp_f31, temp_f31);
cMtx_concat(j3dSys.getViewMtx(), temp_r28, data_p->m_modelMtx); cMtx_concat(j3dSys.getViewMtx(), temp_r28, data_p->m_modelMtx);
#ifdef TARGET_PC #ifdef TARGET_PC
dusk::frame_interp::record_final_mtx(mDoMtx_stack_c::get(), data_p->m_modelMtx); dusk::frame_interp::record_final_mtx(temp_r28, data_p->m_modelMtx);
#endif #endif
} }
} }
+7
View File
@@ -237,6 +237,13 @@ void dDrawPath_c::rendering(dDrawPath_c::line_class const* p_line) {
if (isDrawType(p_line->field_0x0)) { if (isDrawType(p_line->field_0x0)) {
int width = getLineWidth(p_line->field_0x1); int width = getLineWidth(p_line->field_0x1);
#if TARGET_PC
f32 height = JUTVideo::getManager()->getRenderHeight() / 448.0f;
if (height > 1.0f) {
width /= 2;
}
#endif
if (width > 0 && p_line->mDataNum >= 2) { if (width > 0 && p_line->mDataNum >= 2) {
GXSetLineWidth(width, GX_TO_ZERO); GXSetLineWidth(width, GX_TO_ZERO);
GXSetTevColor(GX_TEVREG0, *getLineColor(p_line->field_0x0 & 0x3F, p_line->field_0x1)); GXSetTevColor(GX_TEVREG0, *getLineColor(p_line->field_0x0 & 0x3F, p_line->field_0x1));
+7
View File
@@ -2399,6 +2399,13 @@ void dMenu_Collect3D_c::_move(u8 param_0, u8 param_1) {
posZ = 550.0f; posZ = 550.0f;
} }
toItem3Dpos(linkPos.x, posY, posZ, &itemPos); toItem3Dpos(linkPos.x, posY, posZ, &itemPos);
#if TARGET_PC
if (dusk::getSettings().game.enableLinkDollRotation) {
const f32 angle = mDoCPd_c::getSubStickX3D(PAD_1) * 2048;
ANGLE_ADD(mLinkAngle, angle);
} else
#endif
if (param_0 == 0 && param_1 == 0) { if (param_0 == 0 && param_1 == 0) {
f32 temp = 450.0f; f32 temp = 450.0f;
ANGLE_ADD(mLinkAngle, temp); ANGLE_ADD(mLinkAngle, temp);
+42 -1
View File
@@ -21,6 +21,7 @@
#include "d/d_msg_string.h" #include "d/d_msg_string.h"
#include "d/d_meter_haihai.h" #include "d/d_meter_haihai.h"
#include "d/d_menu_window.h" #include "d/d_menu_window.h"
#include "dusk/settings.h"
#include "f_op/f_op_msg_mng.h" #include "f_op/f_op_msg_mng.h"
#include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_graphic.h"
#include <cstring> #include <cstring>
@@ -945,9 +946,15 @@ void dMenu_DmapBg_c::draw() {
mpMeterHaihai->drawHaihai(field_0xdda, mpMeterHaihai->drawHaihai(field_0xdda,
x1 + (local_224.x + local_218.x) / 2, x1 + (local_224.x + local_218.x) / 2,
y1 + (local_224.y + local_218.y) / 2, y1 + (local_224.y + local_218.y) / 2,
-35.0f + (local_224.x - local_218.x), -35.0f + (local_224.x - local_218.x),
-35.0f + (local_224.y - local_218.y)); -35.0f + (local_224.y - local_218.y));
#if TARGET_PC
if (!dusk::getSettings().game.enableFrameInterpolation) {
field_0xdda = 0;
}
#else
field_0xdda = 0; field_0xdda = 0;
#endif
} }
dMenu_Dmap_c::myclass->drawFloorScreenTop(mFloorScreen, field_0xd94, field_0xd98, grafContext); dMenu_Dmap_c::myclass->drawFloorScreenTop(mFloorScreen, field_0xd94, field_0xd98, grafContext);
@@ -984,7 +991,36 @@ void dMenu_DmapBg_c::update() {
JUT_ASSERT(2323, mpBackTexture != NULL); JUT_ASSERT(2323, mpBackTexture != NULL);
void* spec = mpArchive->getResource("spec/spec.dat"); void* spec = mpArchive->getResource("spec/spec.dat");
#if TARGET_PC
struct dmap_spec {
/* 0x00 */ BE(f32) field_0x0;
/* 0x04 */ BE(f32) field_0x4;
/* 0x08 */ BE(f32) field_0x8;
/* 0x0C */ u8 field_0xc;
/* 0x0D */ u8 field_0xd;
/* 0x0E */ u8 field_0xe;
/* 0x0F */ u8 field_0xf;
/* 0x10 */ u8 field_0x10;
/* 0x11 */ u8 field_0x11;
/* 0x12 */ u8 field_0x12;
/* 0x13 */ u8 field_0x13;
};
dmap_spec* dspec = (dmap_spec*)spec;
field_0xd80 = dspec->field_0x0;
field_0xd84 = dspec->field_0x4;
field_0xd88 = dspec->field_0x8;
field_0xd8c = dspec->field_0xc;
field_0xd8d = dspec->field_0xd;
field_0xd8e = dspec->field_0xe;
field_0xd8f = dspec->field_0xf;
field_0xd90 = dspec->field_0x10;
field_0xd91 = dspec->field_0x11;
field_0xd92 = dspec->field_0x12;
field_0xd93 = dspec->field_0x13;
#else
memcpy(&field_0xd80, spec, 20); memcpy(&field_0xd80, spec, 20);
#endif
} }
} }
@@ -2545,6 +2581,11 @@ void dMenu_Dmap_c::zoomIn_proc() {
} }
void dMenu_Dmap_c::zoomOut_init_proc() { void dMenu_Dmap_c::zoomOut_init_proc() {
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation) {
mpDrawBg->resetScrollArrowMask();
}
#endif
Z2GetAudioMgr()->seStart(Z2SE_SY_MAP_ZOOMOUT, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); Z2GetAudioMgr()->seStart(Z2SE_SY_MAP_ZOOMOUT, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0);
mMapCtrl->initZoomOut(10); mMapCtrl->initZoomOut(10);
mpDrawBg->iconScaleAnmInit(1.0f, 0.0f, 10); mpDrawBg->iconScaleAnmInit(1.0f, 0.0f, 10);
+75
View File
@@ -185,6 +185,17 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i
field_0x682 = 0xc000; field_0x682 = 0xc000;
break; break;
} }
#if TARGET_PC
mCursorInterpPrevX = 0.0f;
mCursorInterpPrevY = 0.0f;
mCursorInterpCurrX = 0.0f;
mCursorInterpCurrY = 0.0f;
mCursorInterpPrevAngle = 0;
mCursorInterpCurrAngle = 0;
mCursorInterpPrevAngular = false;
mCursorInterpCurrAngular = false;
mCursorInterpInit = false;
#endif
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
field_0x674[i] = 0; field_0x674[i] = 0;
#if TARGET_PC #if TARGET_PC
@@ -631,7 +642,71 @@ void dMenu_Ring_c::_draw() {
} else { } else {
drawSelectItem(); drawSelectItem();
drawItem2(); drawItem2();
#if TARGET_PC
f32 simX = 0.0f;
f32 simY = 0.0f;
bool restoreSimPos = false;
if (dusk::frame_interp::is_enabled() && mAlphaRate >= 1.0f) {
simX = mpDrawCursor->getPositionX();
simY = mpDrawCursor->getPositionY();
const bool isAngular = (mStatus == STATUS_MOVE) && !mDirectSelectActive;
if (dusk::frame_interp::get_ui_tick_pending()) {
mCursorInterpPrevX = mCursorInterpCurrX;
mCursorInterpPrevY = mCursorInterpCurrY;
mCursorInterpPrevAngle = mCursorInterpCurrAngle;
mCursorInterpPrevAngular = mCursorInterpCurrAngular;
mCursorInterpCurrX = simX;
mCursorInterpCurrY = simY;
mCursorInterpCurrAngle = field_0x66e;
mCursorInterpCurrAngular = isAngular;
// reset prev = curr for first render pass or
// when angle modes prev/curr differ
// to prevent arrival jitter
if (!mCursorInterpInit ||
mCursorInterpPrevAngular != mCursorInterpCurrAngular) {
mCursorInterpPrevX = mCursorInterpCurrX;
mCursorInterpPrevY = mCursorInterpCurrY;
mCursorInterpPrevAngle = mCursorInterpCurrAngle;
mCursorInterpPrevAngular = mCursorInterpCurrAngular;
mCursorInterpInit = true;
}
}
if (mCursorInterpInit) {
const f32 step = dusk::frame_interp::get_interpolation_step();
if (mCursorInterpPrevAngular && mCursorInterpCurrAngular) {
const s16 delta = mCursorInterpCurrAngle - mCursorInterpPrevAngle;
const s16 lerpedAngle = mCursorInterpPrevAngle + (s16)(delta * step);
// yoinked from stick_move_proc()
const f32 x = g_ringHIO.mItemRingPosX + FB_WIDTH_BASE / 2 +
mRingRadiusH * cM_ssin(lerpedAngle);
const f32 y = g_ringHIO.mItemRingPosY + FB_HEIGHT_BASE / 2 +
mRingRadiusV * cM_scos(lerpedAngle);
mpDrawCursor->setPos(x, y);
} else {
mpDrawCursor->setPos(
mCursorInterpPrevX + (mCursorInterpCurrX - mCursorInterpPrevX) * step,
mCursorInterpPrevY + (mCursorInterpCurrY - mCursorInterpPrevY) * step
);
}
restoreSimPos = true;
}
} else {
mCursorInterpInit = false;
}
#endif
mpDrawCursor->draw(); mpDrawCursor->draw();
#if TARGET_PC
// prevents offsetting at destination on the next frame
// since stick_wait_proc doesn't call setPos and we clobbered mPositionX/Y
if (restoreSimPos) {
mpDrawCursor->setPos(simX, simY);
}
#endif
mpItemExplain->trans(mCenterPosX, mCenterPosY); mpItemExplain->trans(mCenterPosX, mCenterPosY);
mpItemExplain->draw((J2DOrthoGraph*)grafPort); mpItemExplain->draw((J2DOrthoGraph*)grafPort);
drawFlag0(); drawFlag0();
+7
View File
@@ -1987,6 +1987,13 @@ bool jmessage_tSequenceProcessor::do_isReady() {
} }
#endif #endif
#if TARGET_PC
if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) {
field_0xb2 = 1;
pReference->setSendTimer(0);
}
#endif
if (dComIfGp_checkMesgBgm()) { if (dComIfGp_checkMesgBgm()) {
bool isItemMusicPlaying = true; bool isItemMusicPlaying = true;
if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET && if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET &&
+132 -10
View File
@@ -5,14 +5,15 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cmath>
#include <cstdio> #include <cstdio>
#include <span> #include <span>
#include "Adpcm.hpp" #include "Adpcm.hpp"
#include "freeverb/revmodel.hpp" #include "freeverb/revmodel.hpp"
#include "JSystem/JAudio2/JASDriverIF.h"
#include "dusk/audio/DuskAudioSystem.h" #include "dusk/audio/DuskAudioSystem.h"
#include "dusk/endian.h" #include "dusk/endian.h"
#include "dusk/logging.h"
#include "global.h" #include "global.h"
#include "tracy/Tracy.hpp" #include "tracy/Tracy.hpp"
@@ -95,6 +96,13 @@ static void RenderChannel(
ChannelAuxData& channelAux, ChannelAuxData& channelAux,
OutputSubframe& subframe); OutputSubframe& subframe);
static void RenderOutputChannel(
const JASDsp::TChannel& sourceChannel,
ChannelAuxData& aux,
OutputChannel outputChannel,
const std::span<f32> inputSamples,
OutputSubframe& fullOutputSubframe);
/** /**
* Converts a pitch value on a DSP channel to a sample rate. * Converts a pitch value on a DSP channel to a sample rate.
*/ */
@@ -117,6 +125,8 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) {
aux.resamplePos = 0.0; aux.resamplePos = 0.0;
aux.resamplePrev = 0; aux.resamplePrev = 0;
aux.oscPhase = 0;
aux.prev_lp_out = 0.0f; aux.prev_lp_out = 0.0f;
aux.prev_lp_in = 0.0f; aux.prev_lp_in = 0.0f;
@@ -141,6 +151,119 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) {
} }
} }
enum class OscType : u16 {
SQUARE_WAVE_PW_50 = 0,
SAW_WAVE = 1,
SQUARE_WAVE_PW_25 = 3,
TRIANGLE_WAVE = 4,
// idk what 5 and 6 are
SINE_WAVE = 7,
// idk what 8 and 9 are
SINE_WAVE_VAR_STEP = 10,
EVOLVING_HARMONIC = 11,
EVOLVING_RAMP = 12,
};
static s16 gEvolvingHarmonic[64];
static void GenerateEvolvingHarmonic() {
static bool initialized = false;
if (!initialized) {
gEvolvingHarmonic[62] = 8191;
gEvolvingHarmonic[63] = 16383;
initialized = true;
}
u32 prev2 = (u32)gEvolvingHarmonic[62];
u32 prev1 = (u32)gEvolvingHarmonic[63];
for (int i = 0; i < 64; i += 2) {
u32 cur = (u32)gEvolvingHarmonic[i];
gEvolvingHarmonic[i] = (s16)((s32)(prev2 * prev1 - (cur << 16)) >> 16);
prev2 = prev1;
prev1 = cur;
cur = (u32)gEvolvingHarmonic[i + 1];
gEvolvingHarmonic[i + 1] = (s16)((s32)(2u * (prev2 * prev1 + (cur << 16))) >> 16);
prev2 = prev1;
prev1 = cur;
}
}
static void RenderOscChannel(
JASDsp::TChannel& channel,
ChannelAuxData& channelAux,
OutputSubframe& subframe) {
if (channel.mResetFlag)
ResetChannel(channel, channelAux);
const u32 pitch = channel.mPitch;
DspSubframe buf = {};
const auto oscType = static_cast<OscType>(channel.mBytesPerBlock);
switch (oscType) {
case OscType::SQUARE_WAVE_PW_50: {
std::generate(buf.begin(), buf.end(), [&] {
f32 s = channelAux.oscPhase < 0x8000u ? 0.5f : -0.5f;
channelAux.oscPhase += pitch >> 1;
return s;
});
break;
}
case OscType::SQUARE_WAVE_PW_25: {
std::generate(buf.begin(), buf.end(), [&] {
f32 s = channelAux.oscPhase < 0x4000u ? 0.5f : -0.5f;
channelAux.oscPhase += pitch >> 1;
return s;
});
break;
}
case OscType::SAW_WAVE:
case OscType::EVOLVING_RAMP: {
std::generate(buf.begin(), buf.end(), [&] {
f32 s = (f32)(s16)channelAux.oscPhase / 32768.0f;
channelAux.oscPhase += pitch >> 1;
return s;
});
break;
}
case OscType::SINE_WAVE:
case OscType::SINE_WAVE_VAR_STEP: {
std::generate(buf.begin(), buf.end(), [&] {
f32 s = sinf((f32)channelAux.oscPhase * (2.0f * M_PI / 65536.0f)) * 0.5f;
channelAux.oscPhase += pitch >> 1;
return s;
});
break;
}
case OscType::TRIANGLE_WAVE: {
std::generate(buf.begin(), buf.end(), [&] {
f32 s = 0.5f - fabsf((f32)(s16)channelAux.oscPhase / 32768.0f);
channelAux.oscPhase += pitch >> 1;
return s;
});
break;
}
case OscType::EVOLVING_HARMONIC: {
std::generate(buf.begin(), buf.end(), [&] {
f32 s = gEvolvingHarmonic[channelAux.oscPhase >> 10] / 32768.0f;
channelAux.oscPhase += pitch >> 1;
return s;
});
break;
}
default:
DuskLog.error("RenderOscChannel: unimplemented oscillator type {}", channel.mBytesPerBlock);
break;
}
auto samples = std::span(buf).subspan(0, DSP_SUBFRAME_SIZE);
RenderOutputChannel(channel, channelAux, OutputChannel::LEFT, samples, subframe);
RenderOutputChannel(channel, channelAux, OutputChannel::RIGHT, samples, subframe);
}
void dusk::audio::DspRender(OutputSubframe& subframe) { void dusk::audio::DspRender(OutputSubframe& subframe) {
ZoneScoped; ZoneScoped;
if (DumpAudio != sDumpWasActive) { if (DumpAudio != sDumpWasActive) {
@@ -152,6 +275,8 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
} }
} }
GenerateEvolvingHarmonic();
std::span channels(JASDsp::CH_BUF, DSP_CHANNELS); std::span channels(JASDsp::CH_BUF, DSP_CHANNELS);
DspSubframe reverbInputL = {}; DspSubframe reverbInputL = {};
@@ -174,17 +299,14 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
channel.mIsFinished = true; channel.mIsFinished = true;
continue; continue;
} }
else if (channel.mWaveAramAddress == 0) {
// I think these are oscillator channels? Not backed by audio.
// No idea how to implement these yet, so skip them.
channel.mIsFinished = true;
continue;
}
ValidateChannel(channel);
OutputSubframe channelSubframe = {}; OutputSubframe channelSubframe = {};
RenderChannel(channel, channelAux, channelSubframe); if (channel.mWaveAramAddress == 0) {
RenderOscChannel(channel, channelAux, channelSubframe);
} else {
ValidateChannel(channel);
RenderChannel(channel, channelAux, channelSubframe);
}
if (EnableReverb) { if (EnableReverb) {
// scale the input to the reverb rather than using wet/dry on the output. // scale the input to the reverb rather than using wet/dry on the output.
+3
View File
@@ -53,6 +53,9 @@ namespace dusk::audio {
// last consumed sample from decodeBuf // last consumed sample from decodeBuf
s16 resamplePrev; s16 resamplePrev;
// phase of oscillator channels
u16 oscPhase;
// low pass previous state // low pass previous state
f32 prev_lp_out; // out[n-1] f32 prev_lp_out; // out[n-1]
f32 prev_lp_in; // in[n-1] f32 prev_lp_in; // in[n-1]
+21 -5
View File
@@ -3,6 +3,27 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
void __dcbz(void* addr, int offset) {
// Gekko cache lines are 32 bytes.
// dcbz usually requires addr to be 32-byte aligned.
memset((char*)addr + offset, 0, 32);
}
int __cntlzw(unsigned int val) {
if (val == 0) return 32; // PowerPC returns 32 if the input is 0
#ifdef _MSC_VER
unsigned long idx;
_BitScanReverse(&idx, val);
return 31 - (int)idx;
#else
return __builtin_clz(val);
#endif
}
#ifndef _MSC_VER #ifndef _MSC_VER
int stricmp(const char* str1, const char* str2) { int stricmp(const char* str1, const char* str2) {
char a_var; char a_var;
@@ -48,11 +69,6 @@ int strnicmp(const char* str1, const char* str2, int n) {
} }
#endif #endif
void *_memcpy(void* dest, void const* src, int n) {
return memcpy(dest, src, n);
}
void DCZeroRange(void* addr, uint32_t nBytes) { void DCZeroRange(void* addr, uint32_t nBytes) {
#if defined(_MSC_VER) || TARGET_ANDROID #if defined(_MSC_VER) || TARGET_ANDROID
memset(addr, 0, nBytes); memset(addr, 0, nBytes);
-27
View File
@@ -1,27 +0,0 @@
// C++ Mangled version of extras.c
#include <cstring>
#include <cstdint>
#ifdef _MSC_VER
#include <intrin.h>
#endif
void *__memcpy(void* dest, void const* src, int n) {
return memcpy(dest, src, n);
}
void __dcbz(void* addr, int offset) {
// Gekko cache lines are 32 bytes.
// dcbz usually requires addr to be 32-byte aligned.
memset((char*)addr + offset, 0, 32);
}
int __cntlzw(unsigned int val) {
if (val == 0) return 32; // PowerPC returns 32 if the input is 0
#ifdef _MSC_VER
unsigned long idx;
_BitScanReverse(&idx, val);
return 31 - (int)idx;
#else
return __builtin_clz(val);
#endif
}
+36 -16
View File
@@ -127,14 +127,20 @@ void ensure_initialized() {
s_initialized = true; s_initialized = true;
} }
void begin_sim_tick() {
ensure_initialized();
if (!g_enabled) {
return;
}
s_interpolationCallBackWork.clear();
s_cam_prev = std::move(s_cam_curr);
}
void begin_frame(bool enabled, bool is_sim_frame, float step) { void begin_frame(bool enabled, bool is_sim_frame, float step) {
g_enabled = enabled; g_enabled = enabled;
g_is_sim_frame = is_sim_frame; g_is_sim_frame = is_sim_frame;
g_step = std::clamp(step, 0.0f, 1.0f); g_step = std::clamp(step, 0.0f, 1.0f);
if (is_sim_frame) {
s_interpolationCallBackWork.clear();
s_cam_prev = std::move(s_cam_curr);
}
} }
bool is_enabled() { bool is_enabled() {
@@ -286,12 +292,20 @@ void interp_view(::view_class* view) {
return; return;
const f32 step = get_interpolation_step(); const f32 step = get_interpolation_step();
const bool is_cam_curr_authoritative = g_is_sim_frame && step <= 0.0f;
cXyz eye; cXyz eye;
cXyz center; cXyz center;
cXyz up; cXyz up;
lerp_xyz(&eye, s_cam_prev.eye, s_cam_curr.eye, step); if (is_cam_curr_authoritative) {
lerp_xyz(&center, s_cam_prev.center, s_cam_curr.center, step); eye = s_cam_curr.eye;
lerp_xyz(&up, s_cam_prev.up, s_cam_curr.up, step); center = s_cam_curr.center;
up = s_cam_curr.up;
} else {
lerp_xyz(&eye, s_cam_prev.eye, s_cam_curr.eye, step);
lerp_xyz(&center, s_cam_prev.center, s_cam_curr.center, step);
lerp_xyz(&up, s_cam_prev.up, s_cam_curr.up, step);
}
if (!up.normalizeRS()) { if (!up.normalizeRS()) {
up = s_cam_curr.up; up = s_cam_curr.up;
up.normalizeRS(); up.normalizeRS();
@@ -300,19 +314,25 @@ void interp_view(::view_class* view) {
view->lookat.eye = eye; view->lookat.eye = eye;
view->lookat.center = center; view->lookat.center = center;
view->lookat.up = up; view->lookat.up = up;
view->bank = lerp_bank(s_cam_prev.bank, s_cam_curr.bank, step); if (is_cam_curr_authoritative) {
view->fovy = s_cam_prev.fovy + (s_cam_curr.fovy - s_cam_prev.fovy) * step; view->bank = s_cam_curr.bank;
view->aspect = s_cam_prev.aspect + (s_cam_curr.aspect - s_cam_prev.aspect) * step; view->fovy = s_cam_curr.fovy;
view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step; view->aspect = s_cam_curr.aspect;
view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step; view->near_ = s_cam_curr.near_;
view->far_ = s_cam_curr.far_;
} else {
view->bank = lerp_bank(s_cam_prev.bank, s_cam_curr.bank, step);
view->fovy = s_cam_prev.fovy + (s_cam_curr.fovy - s_cam_prev.fovy) * step;
view->aspect = s_cam_prev.aspect + (s_cam_curr.aspect - s_cam_prev.aspect) * step;
view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step;
view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step;
}
// FRAME INTERP TODO: It might be better if I rewired the game to not clear this flag until the // FRAME INTERP TODO: It might be better if I rewired the game to not clear this flag until the
// next sim frame, but I don't care enough to right now // next sim frame, but I don't care enough to right now
#if WIDESCREEN_SUPPORT #if WIDESCREEN_SUPPORT
if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && step >= 0.5f ? const f32 wide_step = is_cam_curr_authoritative ? 1.0f : step;
s_cam_curr.wideZoom : if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && wide_step >= 0.5f ? s_cam_curr.wideZoom : s_cam_prev.wideZoom) {
s_cam_prev.wideZoom)
{
mDoGph_gInf_c::onWideZoom(); mDoGph_gInf_c::onWideZoom();
} }
#endif #endif
+44 -23
View File
@@ -5,62 +5,84 @@
#include <cmath> #include <cmath>
#include <unordered_map> #include <unordered_map>
namespace dusk { namespace dusk::game_clock {
namespace game_clock {
using clock = std::chrono::steady_clock; using clock = std::chrono::steady_clock;
bool s_initialized = false; bool s_initialized = false;
clock::time_point s_previous_sample{}; clock::time_point s_previous_sample{};
float s_sim_accumulator = 0.0f; clock::time_point s_current_snapshot_time{};
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample; std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
constexpr clock::duration kSimPeriodDuration =
std::chrono::duration_cast<clock::duration>(std::chrono::duration<float>(sim_pace()));
constexpr clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
constexpr int kMaxSimTicksPerFrame = 2;
void ensure_initialized() { void ensure_initialized() {
if (s_initialized) { if (s_initialized) {
return; return;
} }
s_previous_sample = clock::now(); s_previous_sample = clock::now();
s_sim_accumulator = sim_pace(); s_current_snapshot_time = s_previous_sample;
s_initialized = true; s_initialized = true;
} }
void reset_accumulator() {
ensure_initialized();
s_sim_accumulator = fmodf(s_sim_accumulator, sim_pace());
}
void reset_frame_timer() { void reset_frame_timer() {
s_previous_sample = clock::now(); s_previous_sample = clock::now();
s_sim_accumulator = 0.0f; s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
} }
MainLoopPacer advance_main_loop() { MainLoopPacer advance_main_loop() {
ensure_initialized(); ensure_initialized();
const clock::time_point now = clock::now(); const clock::time_point now = clock::now();
const float presentation_dt = std::chrono::duration<float>(now - s_previous_sample).count(); const clock::duration frame_gap = now - s_previous_sample;
const float presentation_dt = std::chrono::duration<float>(frame_gap).count();
s_previous_sample = now; s_previous_sample = now;
s_sim_accumulator += presentation_dt;
MainLoopPacer out{}; MainLoopPacer out{};
out.presentation_dt_seconds = presentation_dt; out.presentation_dt_seconds = presentation_dt;
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit; const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation &&
!dusk::getTransientSettings().skipFrameRateLimit;
out.is_interpolating = should_interpolate; out.is_interpolating = should_interpolate;
out.sim_pace = sim_pace(); out.sim_pace = sim_pace();
if (!should_interpolate) { if (!should_interpolate) {
s_sim_accumulator = 0.0f; s_current_snapshot_time = now;
out.do_sim_tick = true; out.sim_ticks_to_run = 1;
out.interpolation_step = 0.0f;
return out;
} else {
out.do_sim_tick = s_sim_accumulator >= sim_pace();
out.interpolation_step = out.do_sim_tick ? 0.0f : s_sim_accumulator / sim_pace();
return out; return out;
} }
if (frame_gap > kAbnormalGapResetThreshold) {
s_current_snapshot_time = now - kSimPeriodDuration;
out.sim_ticks_to_run = 0;
return out;
}
int sim_ticks_to_run = 0;
clock::time_point projected_snapshot_time = s_current_snapshot_time;
const clock::time_point render_time = now - kSimPeriodDuration;
while (sim_ticks_to_run < kMaxSimTicksPerFrame && projected_snapshot_time < render_time) {
projected_snapshot_time += kSimPeriodDuration;
sim_ticks_to_run++;
}
out.sim_ticks_to_run = sim_ticks_to_run;
return out;
}
void commit_sim_tick() {
ensure_initialized();
s_current_snapshot_time += kSimPeriodDuration;
}
float sample_interpolation_step() {
ensure_initialized();
const float step =
std::chrono::duration<float>(clock::now() - s_current_snapshot_time).count() / sim_pace();
return std::clamp(step, 0.0f, 1.0f);
} }
float consume_interval(const void* consumer) { float consume_interval(const void* consumer) {
@@ -78,5 +100,4 @@ float consume_interval(const void* consumer) {
return dt; return dt;
} }
} // namespace game_clock } // namespace dusk::game_clock
} // namespace dusk
+106
View File
@@ -0,0 +1,106 @@
#include <cmath>
#include <SSystem/SComponent/c_xyz.h>
#include <d/d_com_inf_game.h>
#include <d/actor/d_a_player.h>
#include <d/actor/d_a_alink.h>
#include <dusk/gamepad_color.h>
cXyz currentGamepadColor = {0, 0, 0};
cXyz finalGamepadColor = {0, 0, 0};
cXyz additionalGamepadColor = {0, 0, 0};
float lerpSpeed = 0.0f;
const cXyz duskColor = {50, 50, -50};
const cXyz noColor = {0, 0, 0};
cXyz LerpColor(cXyz a, cXyz b, float t) {
return {std::lerp(a.x, b.x, t), std::lerp(a.y, b.y, t), std::lerp(a.z, b.z, t)};
}
void FadeLED(cXyz newColor, float speed) {
finalGamepadColor = newColor;
lerpSpeed = speed / 30.0f;
}
void SetLED(cXyz newColor) {
currentGamepadColor = newColor;
finalGamepadColor = newColor;
}
void SetGamepadAdditionalColor(cXyz addColor) {
additionalGamepadColor.x = addColor.x;
additionalGamepadColor.y = addColor.y;
additionalGamepadColor.z = addColor.z;
}
void handleGamepadColor() {
bool setColor = false;
fopAc_ac_c* zhint = dComIfGp_att_getZHint();
if (zhint != NULL) {
FadeLED({50, 50, 175}, 2.0f);
setColor = true;
}
daPy_py_c* player = daPy_getPlayerActorClass();
daAlink_c* link = daAlink_getAlinkActorClass();
if (link != nullptr && !setColor) {
if (link->checkWolf()) {
FadeLED({115, 115, 75}, 5.0f);
setColor = true;
} else {
switch (dComIfGs_getSelectEquipClothes()) {
case dItemNo_WEAR_KOKIRI_e:
FadeLED({0, 100, 0}, 5.0f);
setColor = true;
break;
case dItemNo_WEAR_ZORA_e:
FadeLED({0, 0, 100}, 5.0f);
setColor = true;
break;
case dItemNo_ARMOR_e:
if (link->checkMagicArmorHeavy()) {
FadeLED({5, 100, 100}, 5.0f);
} else {
FadeLED({100, 0, 5}, 5.0f);
}
setColor = true;
break;
case dItemNo_WEAR_CASUAL_e:
FadeLED({235, 230, 115}, 5.0f);
setColor = true;
break;
}
}
}
if (dKy_darkworld_check()) {
SetGamepadAdditionalColor(duskColor);
} else {
SetGamepadAdditionalColor(noColor);
}
f32 finalRed = finalGamepadColor.x + additionalGamepadColor.x;
f32 finalGreen = finalGamepadColor.y + additionalGamepadColor.y;
f32 finalBlue = finalGamepadColor.z + additionalGamepadColor.z;
if (finalRed > 255)
finalRed = 255;
if (finalRed < 0)
finalRed = 0;
if (finalGreen > 255)
finalGreen = 255;
if (finalGreen < 0)
finalGreen = 0;
if (finalBlue > 255)
finalBlue = 255;
if (finalBlue < 0)
finalBlue = 0;
currentGamepadColor = LerpColor(currentGamepadColor, cXyz{finalRed, finalGreen, finalBlue}, lerpSpeed);
PADSetColor(PAD_CHAN0, (u8)currentGamepadColor.x, (u8)currentGamepadColor.y, (u8)currentGamepadColor.z);
}
+85 -5
View File
@@ -1,16 +1,29 @@
#include "dusk/gyro.h" #include "dusk/gyro.h"
#include "d/actor/d_a_alink.h" #include "d/actor/d_a_alink.h"
#include <cmath>
namespace dusk::gyro { namespace dusk::gyro {
namespace { namespace {
constexpr s32 kRollgoalTableMaxOffset = 12000; constexpr s32 kRollgoalTableMaxOffset = 6500;
constexpr float kGyroEmaAlphaMin = 0.05f; constexpr float kGyroEmaAlphaMin = 0.05f;
constexpr float kGyroEmaAlphaMax = 1.0f; constexpr float kGyroEmaAlphaMax = 1.0f;
// Smooth gravity separately so the yaw/roll blend doesn't twitch with raw accel noise.
constexpr float kGravityEmaAlpha = 0.1f;
constexpr float kMinGravityProjection = 0.2f;
// Let roll contribute more strongly as the pad approaches an upright posture.
constexpr float kRollAimBoostMax = 2.0f;
bool s_sensor_enabled = false; bool s_sensor_enabled = false;
bool s_accel_enabled = false;
bool s_was_aiming = false;
bool s_have_gravity_baseline = false;
float s_smooth_gx = 0.0f; float s_smooth_gx = 0.0f;
float s_smooth_gy = 0.0f; float s_smooth_gy = 0.0f;
float s_smooth_gz = 0.0f; float s_smooth_gz = 0.0f;
float s_gravity_y = 0.0f;
float s_gravity_z = 0.0f;
float s_baseline_gravity_y = 0.0f;
float s_baseline_gravity_z = 0.0f;
float s_yaw_rad = 0.0f; float s_yaw_rad = 0.0f;
float s_pitch_rad = 0.0f; float s_pitch_rad = 0.0f;
float s_roll_rad = 0.0f; float s_roll_rad = 0.0f;
@@ -19,6 +32,10 @@ s32 s_rollgoal_az = 0;
void reset_filter_state() { void reset_filter_state() {
s_smooth_gx = s_smooth_gy = s_smooth_gz = 0.0f; s_smooth_gx = s_smooth_gy = s_smooth_gz = 0.0f;
s_gravity_y = s_gravity_z = 0.0f;
s_baseline_gravity_y = s_baseline_gravity_z = 0.0f;
s_was_aiming = false;
s_have_gravity_baseline = false;
s_yaw_rad = s_pitch_rad = s_roll_rad = 0.0f; s_yaw_rad = s_pitch_rad = s_roll_rad = 0.0f;
s_rollgoal_ax = s_rollgoal_az = 0; s_rollgoal_ax = s_rollgoal_az = 0;
} }
@@ -49,15 +66,30 @@ bool queryGyroAimContext() {
} }
void read(float dt) { void read(float dt) {
if (!s_sensor_keep_alive && !queryGyroAimContext()) { const bool aim_active = queryGyroAimContext();
const bool aim_just_started = aim_active && !s_was_aiming;
const bool aim_just_ended = !aim_active && s_was_aiming;
s_was_aiming = aim_active;
if (!s_sensor_keep_alive && !aim_active) {
if (s_sensor_enabled) { if (s_sensor_enabled) {
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE); PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE);
s_sensor_enabled = false; s_sensor_enabled = false;
} }
if (s_accel_enabled) {
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_ACCEL, FALSE);
s_accel_enabled = false;
}
reset_filter_state(); reset_filter_state();
return; return;
} }
if (aim_just_started || aim_just_ended) {
s_gravity_y = s_gravity_z = 0.0f;
s_baseline_gravity_y = s_baseline_gravity_z = 0.0f;
s_have_gravity_baseline = false;
}
if (!s_sensor_enabled) { if (!s_sensor_enabled) {
if (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) { if (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) {
return; return;
@@ -68,6 +100,13 @@ void read(float dt) {
s_sensor_enabled = true; s_sensor_enabled = true;
} }
if (!s_accel_enabled && PADHasSensor(PAD_CHAN0, PAD_SENSOR_ACCEL) &&
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_ACCEL, TRUE))
{
// We only need accel for the gravity-aware yaw/roll mix.
s_accel_enabled = true;
}
f32 gyro[3]; f32 gyro[3];
if (!PADGetSensorData(PAD_CHAN0, PAD_SENSOR_GYRO, gyro, 3)) { if (!PADGetSensorData(PAD_CHAN0, PAD_SENSOR_GYRO, gyro, 3)) {
return; return;
@@ -80,9 +119,50 @@ void read(float dt) {
s_smooth_gy += smooth_alpha * (gyro[1] - s_smooth_gy); s_smooth_gy += smooth_alpha * (gyro[1] - s_smooth_gy);
s_smooth_gz += smooth_alpha * (gyro[2] - s_smooth_gz); s_smooth_gz += smooth_alpha * (gyro[2] - s_smooth_gz);
s_pitch_rad = -apply_deadband(s_smooth_gx, deadband) * dt * dusk::getSettings().game.gyroSensitivityX; const float pitch_rate = apply_deadband(s_smooth_gx, deadband);
s_yaw_rad = apply_deadband(s_smooth_gy, deadband) * dt * dusk::getSettings().game.gyroSensitivityY; const float yaw_rate = apply_deadband(s_smooth_gy, deadband);
s_roll_rad = apply_deadband(s_smooth_gz, deadband) * dt * dusk::getSettings().game.gyroSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X const float roll_rate = apply_deadband(s_smooth_gz, deadband);
s_pitch_rad = -pitch_rate * dt * dusk::getSettings().game.gyroSensitivityX;
s_roll_rad = roll_rate * dt * dusk::getSettings().game.gyroSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X
float horizontal_rate = yaw_rate;
if (aim_active && s_accel_enabled) {
f32 accel[3];
if (PADGetSensorData(PAD_CHAN0, PAD_SENSOR_ACCEL, accel, 3)) {
if (!s_have_gravity_baseline) {
s_gravity_y = accel[1];
s_gravity_z = accel[2];
} else {
s_gravity_y += kGravityEmaAlpha * (accel[1] - s_gravity_y);
s_gravity_z += kGravityEmaAlpha * (accel[2] - s_gravity_z);
}
// Compare the current gravity projection against the gravity vector from
// aim start so the user's resting hold angle becomes the neutral baseline.
const float gravity_yz_len = std::sqrt((s_gravity_y * s_gravity_y) + (s_gravity_z * s_gravity_z));
if (gravity_yz_len >= kMinGravityProjection) {
const float current_gravity_y = s_gravity_y / gravity_yz_len;
const float current_gravity_z = s_gravity_z / gravity_yz_len;
if (!s_have_gravity_baseline) {
s_baseline_gravity_y = current_gravity_y;
s_baseline_gravity_z = current_gravity_z;
s_have_gravity_baseline = true;
}
const float yaw_weight =
(s_baseline_gravity_y * current_gravity_y) + (s_baseline_gravity_z * current_gravity_z);
const float roll_weight =
(s_baseline_gravity_y * current_gravity_z) - (s_baseline_gravity_z * current_gravity_y);
const float roll_mix = std::fabs(roll_weight);
const float roll_boost = 1.0f + (roll_mix * (kRollAimBoostMax - 1.0f));
horizontal_rate = (yaw_rate * yaw_weight) + (roll_rate * roll_weight * roll_boost);
}
}
}
s_yaw_rad = horizontal_rate * dt * dusk::getSettings().game.gyroSensitivityY;
s_pitch_rad = dusk::getSettings().game.gyroInvertPitch ? -s_pitch_rad : s_pitch_rad; s_pitch_rad = dusk::getSettings().game.gyroInvertPitch ? -s_pitch_rad : s_pitch_rad;
s_yaw_rad = dusk::getSettings().game.gyroInvertYaw ? -s_yaw_rad : s_yaw_rad; s_yaw_rad = dusk::getSettings().game.gyroInvertYaw ? -s_yaw_rad : s_yaw_rad;
+4
View File
@@ -305,6 +305,10 @@ namespace dusk {
ImGuiMenuGame::ToggleFullscreen(); ImGuiMenuGame::ToggleFullscreen();
} }
if (ImGui::IsKeyPressed(ImGuiKey_Escape) && getSettings().video.enableFullscreen) {
ImGuiMenuGame::ToggleFullscreen();
}
if (!dusk::IsGameLaunched) { if (!dusk::IsGameLaunched) {
m_preLaunchWindow.draw(); m_preLaunchWindow.draw();
} }
+7
View File
@@ -191,6 +191,11 @@ namespace dusk {
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n" ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
"the first released version."); "the first released version.");
} }
config::ImGuiCheckbox("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables rotating Link in the collection menu with the C-Stick");
}
ImGui::SeparatorText("Difficulty"); ImGui::SeparatorText("Difficulty");
@@ -288,6 +293,8 @@ namespace dusk {
config::ImGuiCheckbox("Infinite Oil", getSettings().game.infiniteOil); config::ImGuiCheckbox("Infinite Oil", getSettings().game.infiniteOil);
config::ImGuiCheckbox("Infinite Oxygen", getSettings().game.infiniteOxygen); config::ImGuiCheckbox("Infinite Oxygen", getSettings().game.infiniteOxygen);
config::ImGuiCheckbox("Infinite Rupees", getSettings().game.infiniteRupees); config::ImGuiCheckbox("Infinite Rupees", getSettings().game.infiniteRupees);
config::ImGuiCheckbox("Items Don't Despawn", getSettings().game.enableIndefiniteItemDrops);
ImGui::SetItemTooltip("Items Don't Despawn Unless You Load A Different Room In Which Case They Do But Even Under Some Circumstances They Don't, It Is Quite Rare Though");
ImGui::SeparatorText("Abilities"); ImGui::SeparatorText("Abilities");
config::ImGuiCheckbox("Moon Jump (R+A)", getSettings().game.moonJump); config::ImGuiCheckbox("Moon Jump (R+A)", getSettings().game.moonJump);
+4 -4
View File
@@ -210,7 +210,7 @@ namespace dusk {
ImGui::Text("Link"); ImGui::Text("Link");
ImGuiStringViewText( ImGuiStringViewText(
player != nullptr player != nullptr
? fmt::format("Position: {: .2f}, {: .2f}, {: .2f}\n", player->current.pos.x, player->current.pos.y, player->current.pos.z) ? fmt::format("Position: {: .4f}, {: .4f}, {: .4f}\n", player->current.pos.x, player->current.pos.y, player->current.pos.z)
: "Position: ?, ?, ?\n" : "Position: ?, ?, ?\n"
); );
@@ -222,7 +222,7 @@ namespace dusk {
ImGuiStringViewText( ImGuiStringViewText(
player != nullptr player != nullptr
? fmt::format("Speed: {0}\n", player->speedF) ? fmt::format("Speed: {: .4f}\n", player->speedF)
: "Speed: ?\n" : "Speed: ?\n"
); );
@@ -230,7 +230,7 @@ namespace dusk {
ImGui::Text("Epona"); ImGui::Text("Epona");
ImGuiStringViewText( ImGuiStringViewText(
horse != nullptr horse != nullptr
? fmt::format("Position: {: .2f}, {: .2f}, {: .2f}\n", horse->current.pos.x, horse->current.pos.y, horse->current.pos.z) ? fmt::format("Position: {: .4f}, {: .4f}, {: .4f}\n", horse->current.pos.x, horse->current.pos.y, horse->current.pos.z)
: "Position: ?, ?, ?\n" : "Position: ?, ?, ?\n"
); );
@@ -242,7 +242,7 @@ namespace dusk {
ImGuiStringViewText( ImGuiStringViewText(
horse != nullptr horse != nullptr
? fmt::format("Speed: {0}\n", horse->speedF) ? fmt::format("Speed: {: .4f}\n", horse->speedF)
: "Speed: ?\n" : "Speed: ?\n"
); );
+28
View File
@@ -46,6 +46,17 @@ static std::string ShowIsoInvalidError(const iso::ValidationError code) {
} }
} }
static std::string_view card_type_name(CARDFileType type) {
switch (type) {
case CARD_GCIFOLDER:
return "GCI Folder"sv;
case CARD_RAWIMAGE:
return "Card Image"sv;
default:
return ""sv;
}
}
void fileDialogCallback(void* userdata, const char* path, const char* error) { void fileDialogCallback(void* userdata, const char* path, const char* error) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata); auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
if (error != nullptr) { if (error != nullptr) {
@@ -216,6 +227,23 @@ void ImGuiPreLaunchWindow::drawOptions() {
if (configuredBackendId != m_initialGraphicsBackend) { if (configuredBackendId != m_initialGraphicsBackend) {
ImGui::TextDisabled("Restart Required"); ImGui::TextDisabled("Restart Required");
} }
auto curFileType = (CARDFileType)getSettings().backend.cardFileType.getValue();
if (ImGui::BeginCombo("Save File Type", card_type_name(curFileType).data())) {
if (ImGui::Selectable("GCI Folder", curFileType == CARD_GCIFOLDER)) {
getSettings().backend.cardFileType.setValue(CARD_GCIFOLDER);
config::Save();
}
if (ImGui::Selectable("Card Image", curFileType == CARD_RAWIMAGE)) {
getSettings().backend.cardFileType.setValue(CARD_RAWIMAGE);
config::Save();
}
ImGui::EndCombo();
}
ImGui::EndChild(); ImGui::EndChild();
} }
+12 -10
View File
@@ -10,6 +10,7 @@
#include "d/d_item_data.h" #include "d/d_item_data.h"
#include "d/d_meter2_info.h" #include "d/d_meter2_info.h"
#include "d/d_save.h" #include "d/d_save.h"
#include "d/actor/d_a_player.h"
#include <map> #include <map>
@@ -579,20 +580,21 @@ namespace dusk {
if (ImGui::BeginCombo("Clothes", itemMap.find(statusA.mSelectEquip[0])->second.m_name.c_str())) { if (ImGui::BeginCombo("Clothes", itemMap.find(statusA.mSelectEquip[0])->second.m_name.c_str())) {
if (ImGui::Selectable("None")) {
statusA.mSelectEquip[0] = dItemNo_NONE_e;
}
if (ImGui::Selectable("Ordon Clothes")) { if (ImGui::Selectable("Ordon Clothes")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_CASUAL_e; dMeter2Info_setCloth(dItemNo_WEAR_CASUAL_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
} }
if (ImGui::Selectable("Hero's Clothes")) { if (ImGui::Selectable("Hero's Clothes")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_KOKIRI_e; dMeter2Info_setCloth(dItemNo_WEAR_KOKIRI_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
} }
if (ImGui::Selectable("Zora Armor")) { if (ImGui::Selectable("Zora Armor")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_ZORA_e; dMeter2Info_setCloth(dItemNo_WEAR_ZORA_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
} }
if (ImGui::Selectable("Magic Armor")) { if (ImGui::Selectable("Magic Armor")) {
statusA.mSelectEquip[0] = dItemNo_ARMOR_e; dMeter2Info_setCloth(dItemNo_ARMOR_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
@@ -1489,11 +1491,11 @@ namespace dusk {
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text(e.flagName.c_str()); ImGuiStringViewText(e.flagName);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text(e.location.c_str()); ImGuiStringViewText(e.location);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text(e.description.c_str()); ImGuiStringViewText(e.description);
} }
ImGui::EndTable(); ImGui::EndTable();
} }
+323 -30
View File
@@ -5,14 +5,24 @@
#include "imgui.h" #include "imgui.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "absl/strings/escaping.h" #include "absl/strings/escaping.h"
#include "nlohmann/json.hpp"
#include "d/d_com_inf_game.h" #include "d/d_com_inf_game.h"
#include "dusk/main.h" #include "dusk/main.h"
#include "dusk/io.hpp"
#include "dusk/logging.h"
#include "dusk/settings.h"
#include "f_op/f_op_overlap_mng.h"
#include "../file_select.hpp"
#include "aurora/lib/window.hpp"
#include <unordered_set>
#include <zstd.h> #include <zstd.h>
namespace dusk { namespace dusk {
using json = nlohmann::json;
#pragma pack(push, 1) #pragma pack(push, 1)
struct StateSharePacket { struct StateSharePacket {
char stageName[8]; char stageName[8];
@@ -23,9 +33,65 @@ struct StateSharePacket {
}; };
#pragma pack(pop) #pragma pack(pop)
static constexpr size_t PACKET_TOTAL = sizeof(StateSharePacket) + sizeof(dSv_info_c); static constexpr size_t PACKET_TOTAL = sizeof(StateSharePacket) + sizeof(dSv_info_c);
static constexpr size_t PACKET_SAVE_ONLY = sizeof(StateSharePacket) + sizeof(dSv_save_c);
static constexpr auto STATES_FILENAME = "states.json";
void ImGuiStateShare::copyState() { static bool ValidateEncodedState(const std::string&);
void ImGuiStateShare::onMergeFileSelected(void* userdata, const char* path, const char* /*error*/) {
auto* self = static_cast<ImGuiStateShare*>(userdata);
if (path != nullptr) {
self->m_pendingMergePath = path;
}
}
static std::string GetStatesFilePath() {
return (dusk::ConfigPath / STATES_FILENAME).string();
}
void ImGuiStateShare::loadStatesFile() {
m_loaded = true;
const std::filesystem::path filePath = dusk::ConfigPath / STATES_FILENAME;
if (!std::filesystem::exists(filePath)) {
return;
}
try {
const std::string pathStr = filePath.string();
auto data = io::FileStream::ReadAllBytes(pathStr.c_str());
auto j = json::parse(data);
if (!j.is_array()) {
return;
}
for (const auto& entry : j) {
if (!entry.contains("name") || !entry.contains("data")) {
continue;
}
SavedStateEntry s;
s.name = entry["name"].get<std::string>();
s.encoded = entry["data"].get<std::string>();
m_states.push_back(std::move(s));
}
} catch (const std::exception& e) {
m_statusMsg = fmt::format("Failed to load states: {}", e.what());
}
}
void ImGuiStateShare::saveStatesFile() {
json j = json::array();
for (const auto& s : m_states) {
j.push_back(json{{"name", s.name}, {"data", s.encoded}});
}
try {
io::FileStream::WriteAllText(GetStatesFilePath().c_str(), j.dump(2));
} catch (const std::exception& e) {
m_statusMsg = fmt::format("Failed to save states: {}", e.what());
}
}
std::string ImGuiStateShare::encodeCurrentState() {
StateSharePacket pkt = {}; StateSharePacket pkt = {};
strncpy(pkt.stageName, dComIfGp_getStartStageName(), 7); strncpy(pkt.stageName, dComIfGp_getStartStageName(), 7);
pkt.roomNo = dComIfGp_getStartStageRoomNo(); pkt.roomNo = dComIfGp_getStartStageRoomNo();
@@ -40,26 +106,25 @@ void ImGuiStateShare::copyState() {
std::string compressed(bound, '\0'); std::string compressed(bound, '\0');
compressed.resize(ZSTD_compress(compressed.data(), bound, raw.data(), raw.size(), 1)); compressed.resize(ZSTD_compress(compressed.data(), bound, raw.data(), raw.size(), 1));
std::string encoded = absl::Base64Escape(compressed); return absl::Base64Escape(compressed);
ImGui::SetClipboardText(encoded.c_str());
m_statusMsg = "Copied to clipboard.";
} }
bool ImGuiStateShare::pasteState() { bool ImGuiStateShare::applyEncodedState(const std::string& encoded, const std::string& name) {
const char* clip = ImGui::GetClipboardText();
if (!clip || clip[0] == '\0') {
m_statusMsg = "Clipboard is empty.";
return false;
}
std::string decoded; std::string decoded;
if (!absl::Base64Unescape(clip, &decoded)) { if (!absl::Base64Unescape(encoded, &decoded)) {
m_statusMsg = "Invalid base64."; m_statusMsg = "Invalid base64.";
return false; return false;
} }
unsigned long long dSize = ZSTD_getFrameContentSize(decoded.data(), decoded.size()); unsigned long long dSize = ZSTD_getFrameContentSize(decoded.data(), decoded.size());
if (dSize == ZSTD_CONTENTSIZE_ERROR || dSize == ZSTD_CONTENTSIZE_UNKNOWN || dSize < PACKET_TOTAL) { if (dSize == ZSTD_CONTENTSIZE_ERROR || dSize == ZSTD_CONTENTSIZE_UNKNOWN) {
m_statusMsg = "Not a valid state string.";
return false;
}
const bool isFull = (dSize == PACKET_TOTAL);
const bool isPartial = (dSize == PACKET_SAVE_ONLY);
if (!isFull && !isPartial) {
m_statusMsg = "Not a valid state string."; m_statusMsg = "Not a valid state string.";
return false; return false;
} }
@@ -75,45 +140,272 @@ bool ImGuiStateShare::pasteState() {
memcpy(&pkt, raw.data(), sizeof(pkt)); memcpy(&pkt, raw.data(), sizeof(pkt));
pkt.stageName[7] = '\0'; pkt.stageName[7] = '\0';
memcpy(&g_dComIfG_gameInfo.info, raw.data() + sizeof(pkt), sizeof(dSv_info_c)); if (isFull) {
memcpy(&g_dComIfG_gameInfo.info, raw.data() + sizeof(pkt), sizeof(dSv_info_c));
m_pendingInfo = g_dComIfG_gameInfo.info;
m_pendingSavedata.reset();
} else {
memcpy(&g_dComIfG_gameInfo.info.mSavedata, raw.data() + sizeof(pkt), sizeof(dSv_save_c));
m_pendingSavedata = g_dComIfG_gameInfo.info.mSavedata;
m_pendingInfo.reset();
}
s16 spawnPoint = pkt.startPoint == -4 ? -1 : pkt.startPoint; s16 spawnPoint = pkt.startPoint == -4 ? -1 : pkt.startPoint;
if (spawnPoint == -1) { if (spawnPoint == -1) {
dComIfGs_setRestartRoomParam(pkt.roomNo & 0x3F); dComIfGs_setRestartRoomParam(pkt.roomNo & 0x3F);
} }
dComIfGp_setNextStage(pkt.stageName, spawnPoint, pkt.roomNo, pkt.layer); dusk::getTransientSettings().stateShareLoadActive = true;
m_pendingInfo = g_dComIfG_gameInfo.info; m_stateSharePeekSeen = false;
dComIfGp_setNextStage(pkt.stageName, spawnPoint, pkt.roomNo, pkt.layer, 0.0f, 0, 1, 0, 0, 1, 3);
m_statusMsg = fmt::format("Warping to {} room {} layer {}.", pkt.stageName, (int)pkt.roomNo, (int)pkt.layer); if (name.empty()) {
m_statusMsg = fmt::format("{} room {} layer {}.", pkt.stageName, (int)pkt.roomNo, (int)pkt.layer);
} else {
m_statusMsg = fmt::format("{}: {} room {} layer {}.", name, pkt.stageName, (int)pkt.roomNo, (int)pkt.layer);
}
return true; return true;
} }
void ImGuiStateShare::tickPendingApply() { void ImGuiStateShare::tickPendingApply() {
if (!m_pendingInfo.has_value() || dComIfGp_isEnableNextStage()) if (!m_pendingInfo.has_value() && !m_pendingSavedata.has_value()) {
return; return;
g_dComIfG_gameInfo.info = *m_pendingInfo; }
m_pendingInfo.reset(); if (dComIfGp_isEnableNextStage()) {
return;
}
if (m_pendingInfo.has_value()) {
g_dComIfG_gameInfo.info = *m_pendingInfo;
m_pendingInfo.reset();
} else {
g_dComIfG_gameInfo.info.mSavedata = *m_pendingSavedata;
m_pendingSavedata.reset();
}
dComIfGp_offOxygenShowFlag();
dComIfGp_setMaxOxygen(600);
dComIfGp_setOxygen(600);
}
static bool ValidateEncodedState(const std::string& encoded) {
std::string decoded;
if (!absl::Base64Unescape(encoded, &decoded)) {
return false;
}
unsigned long long dSize = ZSTD_getFrameContentSize(decoded.data(), decoded.size());
return dSize == PACKET_TOTAL || dSize == PACKET_SAVE_ONLY;
}
void ImGuiStateShare::mergeFromFile(const std::string& path) {
try {
auto data = io::FileStream::ReadAllBytes(path.c_str());
auto j = json::parse(data);
if (!j.is_array()) {
m_statusMsg = "File does not contain a JSON array.";
return;
}
std::unordered_set<std::string> existingNames;
for (const auto& s : m_states) {
existingNames.insert(s.name);
}
int added = 0;
int skipped = 0;
for (const auto& entry : j) {
if (!entry.contains("name") || !entry.contains("data")) {
++skipped;
continue;
}
const std::string name = entry["name"].get<std::string>();
const std::string encoded = entry["data"].get<std::string>();
if (!ValidateEncodedState(encoded)) {
++skipped;
continue;
}
if (existingNames.count(name)) {
++skipped;
continue;
}
SavedStateEntry s;
s.name = name;
s.encoded = encoded;
existingNames.insert(s.name);
m_states.push_back(std::move(s));
++added;
}
if (added > 0) {
saveStatesFile();
}
m_statusMsg = fmt::format("Merged: {} added, {} skipped.", added, skipped);
} catch (const std::exception& e) {
m_statusMsg = fmt::format("Failed to load file: {}", e.what());
}
} }
void ImGuiStateShare::draw(bool& open) { void ImGuiStateShare::draw(bool& open) {
if (dusk::IsGameLaunched) if (dusk::IsGameLaunched) {
tickPendingApply(); tickPendingApply();
if (dusk::getTransientSettings().stateShareLoadActive) {
if (fopOvlpM_IsPeek()) {
m_stateSharePeekSeen = true;
} else if (m_stateSharePeekSeen) {
dusk::getTransientSettings().stateShareLoadActive = false;
m_stateSharePeekSeen = false;
}
}
}
if (!open) if (!m_loaded) {
loadStatesFile();
}
if (!m_pendingMergePath.empty()) {
mergeFromFile(m_pendingMergePath);
m_pendingMergePath.clear();
}
if (!open) {
return; return;
}
if (!ImGui::Begin("State Share", &open, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) { ImGui::SetNextWindowSizeConstraints(ImVec2(400, 0), ImVec2(FLT_MAX, FLT_MAX));
if (!ImGui::Begin("State Manager", &open, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
ImGui::End(); ImGui::End();
return; return;
} }
if (!dusk::IsGameLaunched) ImGui::BeginDisabled(); const bool gameRunning = dusk::IsGameLaunched;
if (ImGui::Button("Copy State")) copyState(); const bool loadInProgress = dusk::getTransientSettings().stateShareLoadActive;
const float rowH = ImGui::GetTextLineHeightWithSpacing();
const float listH = rowH * 8 + ImGui::GetStyle().FramePadding.y * 2;
ImGui::BeginChild("##states", ImVec2(0, listH), true);
if (m_states.empty()) {
ImGui::TextDisabled("No saved states. Save or import one below.");
}
int toDelete = -1;
for (int i = 0; i < (int)m_states.size(); ++i) {
ImGui::PushID(i);
if (m_renamingIndex == i) {
ImGui::SetNextItemWidth(150);
bool done = ImGui::InputText("##rename", m_renameBuffer, sizeof(m_renameBuffer),
ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll);
if (done) {
if (m_renameBuffer[0] != '\0') {
m_states[i].name = m_renameBuffer;
}
m_renamingIndex = -1;
saveStatesFile();
} else if (ImGui::IsItemDeactivated()) {
m_renamingIndex = -1;
}
} else {
ImGui::Selectable(m_states[i].name.c_str(), false, ImGuiSelectableFlags_None, ImVec2(150, 0));
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Double-click to rename");
if (ImGui::IsMouseDoubleClicked(0)) {
m_renamingIndex = i;
strncpy(m_renameBuffer, m_states[i].name.c_str(), sizeof(m_renameBuffer) - 1);
m_renameBuffer[sizeof(m_renameBuffer) - 1] = '\0';
ImGui::SetKeyboardFocusHere(-1);
}
}
}
ImGui::SameLine();
if (!gameRunning || loadInProgress) { ImGui::BeginDisabled(); }
if (ImGui::Button("Load")) {
applyEncodedState(m_states[i].encoded, m_states[i].name);
}
if (!gameRunning || loadInProgress) { ImGui::EndDisabled(); }
ImGui::SameLine();
if (ImGui::Button("Copy")) {
ImGui::SetClipboardText(m_states[i].encoded.c_str());
m_statusMsg = fmt::format("'{}' copied to clipboard.", m_states[i].name);
}
ImGui::SameLine();
if (ImGui::Button("Del")) {
toDelete = i;
}
ImGui::PopID();
}
if (toDelete >= 0) {
if (m_renamingIndex == toDelete) { m_renamingIndex = -1; }
m_states.erase(m_states.begin() + toDelete);
saveStatesFile();
}
ImGui::EndChild();
// Toolbar
if (!gameRunning) { ImGui::BeginDisabled(); }
if (ImGui::Button("Save")) {
SavedStateEntry entry;
entry.name = fmt::format("State {}", m_states.size() + 1);
entry.encoded = encodeCurrentState();
m_states.push_back(std::move(entry));
saveStatesFile();
m_statusMsg = fmt::format("Saved as '{}'.", m_states.back().name);
}
if (!gameRunning) { ImGui::EndDisabled(); }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Import State")) pasteState(); if (ImGui::Button("Import Clipboard")) {
if (!dusk::IsGameLaunched) ImGui::EndDisabled(); const char* clip = ImGui::GetClipboardText();
if (!clip || clip[0] == '\0') {
m_statusMsg = "Clipboard is empty.";
} else {
std::string clipStr = clip;
if (!ValidateEncodedState(clipStr)) {
m_statusMsg = "Clipboard does not contain a valid state.";
} else {
SavedStateEntry entry;
entry.name = fmt::format("Imported {}", m_states.size() + 1);
entry.encoded = std::move(clipStr);
m_states.push_back(std::move(entry));
saveStatesFile();
m_statusMsg = fmt::format("Imported as '{}'.", m_states.back().name);
}
}
}
ImGui::SameLine();
if (ImGui::Button("Load Pack")) {
static constexpr SDL_DialogFileFilter filter = {"State pack", "json"};
ShowFileSelect(&onMergeFileSelected, this, aurora::window::get_sdl_window(), &filter, 1, nullptr, false);
}
if (!m_states.empty()) {
ImGui::SameLine();
if (ImGui::Button("Clear All")) {
ImGui::OpenPopup("##clearall");
}
if (ImGui::BeginPopup("##clearall")) {
ImGui::Text("Delete all saved states?");
ImGui::Spacing();
if (ImGui::Button("Yes, clear all")) {
m_states.clear();
m_renamingIndex = -1;
saveStatesFile();
m_statusMsg = "All states cleared.";
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
if (!m_statusMsg.empty()) { if (!m_statusMsg.empty()) {
ImGui::Spacing(); ImGui::Spacing();
@@ -125,8 +417,9 @@ void ImGuiStateShare::draw(bool& open) {
} }
void ImGuiMenuTools::ShowStateShare() { void ImGuiMenuTools::ShowStateShare() {
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare)) if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare)) {
return; return;
}
m_stateShare.draw(m_showStateShare); m_stateShare.draw(m_showStateShare);
} }
+29 -11
View File
@@ -4,21 +4,39 @@
#include "d/d_save.h" #include "d/d_save.h"
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector>
namespace dusk { namespace dusk {
class ImGuiStateShare {
public:
void draw(bool& open);
private: struct SavedStateEntry {
void copyState(); std::string name;
bool pasteState(); std::string encoded;
void tickPendingApply(); };
class ImGuiStateShare {
public:
void draw(bool& open);
private:
std::string encodeCurrentState();
bool applyEncodedState(const std::string& encoded, const std::string& name = {});
void tickPendingApply();
void loadStatesFile();
void saveStatesFile();
void mergeFromFile(const std::string& path);
static void onMergeFileSelected(void* userdata, const char* path, const char* error);
std::vector<SavedStateEntry> m_states;
std::string m_statusMsg;
std::optional<dSv_info_c> m_pendingInfo;
std::optional<dSv_save_c> m_pendingSavedata;
int m_renamingIndex = -1;
char m_renameBuffer[128] = {};
bool m_loaded = false;
bool m_stateSharePeekSeen = false;
std::string m_pendingMergePath;
};
std::string m_statusMsg;
std::optional<dSv_info_c> m_pendingInfo;
};
} }
#endif #endif
+7 -1
View File
@@ -43,6 +43,7 @@ UserSettings g_userSettings = {
.invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraXAxis {"game.invertCameraXAxis", false},
.disableMainHUD {"game.disableMainHUD", false}, .disableMainHUD {"game.disableMainHUD", false},
.pauseOnFocusLost {"game.pauseOnFocusLost", false}, .pauseOnFocusLost {"game.pauseOnFocusLost", false},
.enableLinkDollRotation = {"game.enableLinkDollRotation", false },
// Graphics // Graphics
.bloomMode {"game.bloomMode", BloomMode::Classic}, .bloomMode {"game.bloomMode", BloomMode::Classic},
@@ -75,6 +76,7 @@ UserSettings g_userSettings = {
.infiniteOil{"game.infiniteOil", false}, .infiniteOil{"game.infiniteOil", false},
.infiniteOxygen{"game.infiniteOxygen", false}, .infiniteOxygen{"game.infiniteOxygen", false},
.infiniteRupees{"game.infiniteRupees", false}, .infiniteRupees{"game.infiniteRupees", false},
.enableIndefiniteItemDrops {"game.enableIndefiniteItemDrops", false},
.moonJump{"game.moonJump", false}, .moonJump{"game.moonJump", false},
.superClawshot{"game.superClawshot", false}, .superClawshot{"game.superClawshot", false},
.alwaysGreatspin{"game.alwaysGreatspin", false}, .alwaysGreatspin{"game.alwaysGreatspin", false},
@@ -97,7 +99,8 @@ UserSettings g_userSettings = {
.showPipelineCompilation {"backend.showPipelineCompilation", false}, .showPipelineCompilation {"backend.showPipelineCompilation", false},
.wasPresetChosen {"backend.wasPresetChosen", false}, .wasPresetChosen {"backend.wasPresetChosen", false},
.enableCrashReporting {"backend.enableCrashReporting", true}, .enableCrashReporting {"backend.enableCrashReporting", true},
.duskMenuOpen {"backend.duskMenuOpen", false} .duskMenuOpen {"backend.duskMenuOpen", false},
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)}
} }
}; };
@@ -149,6 +152,7 @@ void registerSettings() {
Register(g_userSettings.game.canTransformAnywhere); Register(g_userSettings.game.canTransformAnywhere);
Register(g_userSettings.game.freeMagicArmor); Register(g_userSettings.game.freeMagicArmor);
Register(g_userSettings.game.restoreWiiGlitches); Register(g_userSettings.game.restoreWiiGlitches);
Register(g_userSettings.game.enableLinkDollRotation);
Register(g_userSettings.game.noMissClimbing); Register(g_userSettings.game.noMissClimbing);
Register(g_userSettings.game.noLowHpSound); Register(g_userSettings.game.noLowHpSound);
Register(g_userSettings.game.midnasLamentNonStop); Register(g_userSettings.game.midnasLamentNonStop);
@@ -160,6 +164,7 @@ void registerSettings() {
Register(g_userSettings.game.infiniteOil); Register(g_userSettings.game.infiniteOil);
Register(g_userSettings.game.infiniteOxygen); Register(g_userSettings.game.infiniteOxygen);
Register(g_userSettings.game.infiniteRupees); Register(g_userSettings.game.infiniteRupees);
Register(g_userSettings.game.enableIndefiniteItemDrops);
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);
@@ -181,6 +186,7 @@ void registerSettings() {
Register(g_userSettings.backend.wasPresetChosen); Register(g_userSettings.backend.wasPresetChosen);
Register(g_userSettings.backend.enableCrashReporting); Register(g_userSettings.backend.enableCrashReporting);
Register(g_userSettings.backend.duskMenuOpen); Register(g_userSettings.backend.duskMenuOpen);
Register(g_userSettings.backend.cardFileType);
} }
// Transient settings // Transient settings
+3
View File
@@ -23,6 +23,7 @@
#include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_graphic.h"
#include "m_Do/m_Do_main.h" #include "m_Do/m_Do_main.h"
#include "tracy/Tracy.hpp" #include "tracy/Tracy.hpp"
#include <dusk/gamepad_color.h>
fapGm_HIO_c::fapGm_HIO_c() { fapGm_HIO_c::fapGm_HIO_c() {
mUsingHostIO = true; mUsingHostIO = true;
@@ -734,6 +735,8 @@ static void fapGm_AfterRecord() {
} }
static void duskExecute() { static void duskExecute() {
handleGamepadColor();
if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigX(PAD_1)) { if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigX(PAD_1)) {
if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) { if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) {
dynamic_cast<daAlink_c*>(link)->handleWolfHowl(); dynamic_cast<daAlink_c*>(link)->handleWolfHowl();
+2
View File
@@ -77,6 +77,8 @@ static OSThread MemCardThread;
void mDoMemCd_Ctrl_c::ThdInit() { void mDoMemCd_Ctrl_c::ThdInit() {
#if !PLATFORM_SHIELD #if !PLATFORM_SHIELD
CARDSetLoadType((CARDFileType)dusk::getSettings().backend.cardFileType.getValue());
CARDInit(DUSK_GAME_NAME, DUSK_GAME_VERSION); CARDInit(DUSK_GAME_NAME, DUSK_GAME_VERSION);
#endif #endif
+6 -1
View File
@@ -351,8 +351,13 @@ void mDoExt_modelUpdateDL(J3DModel* i_model) {
void mDoExt_modelEntryDL(J3DModel* i_model) { void mDoExt_modelEntryDL(J3DModel* i_model) {
#if TARGET_PC #if TARGET_PC
if (!dusk::frame_interp::is_sim_frame()) if (!dusk::frame_interp::is_sim_frame()) {
// FRAME INTERP NOTE: This fixes issue #355 where some lights would flicker.
// This is likely better solved by updating J3DMaterial::needsInterpCallBack,
// but it's unclear what exactly needs to be added.
i_model->diff();
return; return;
}
#endif #endif
modelMtxErrorCheck(i_model); modelMtxErrorCheck(i_model);
-7
View File
@@ -1029,15 +1029,8 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_
GX_FALSE, 0); GX_FALSE, 0);
} }
#if TARGET_PC
// use full size for pc for higher quality background elements
u16 halfWidth = width;
u16 halfHeight = height;
#else
u16 halfWidth = width >> 1; u16 halfWidth = width >> 1;
u16 halfHeight = height >> 1; u16 halfHeight = height >> 1;
#endif
GXRenderModeObj* sp24 = JUTGetVideoManager()->getRenderMode(); GXRenderModeObj* sp24 = JUTGetVideoManager()->getRenderMode();
GXSetCopyFilter(GX_FALSE, NULL, GX_TRUE, sp24->vfilter); GXSetCopyFilter(GX_FALSE, NULL, GX_TRUE, sp24->vfilter);
GXSetTexCopySrc(x_orig, y_orig_pos, width, height); GXSetTexCopySrc(x_orig, y_orig_pos, width, height);
+18 -15
View File
@@ -242,8 +242,6 @@ void main01(void) {
continue; continue;
} }
const dusk::game_clock::MainLoopPacer pacing = dusk::game_clock::advance_main_loop();
VIWaitForRetrace(); VIWaitForRetrace();
dusk::lastFrameAuroraStats = *aurora_get_stats(); dusk::lastFrameAuroraStats = *aurora_get_stats();
@@ -254,28 +252,33 @@ void main01(void) {
mDoGph_gInf_c::updateRenderSize(); mDoGph_gInf_c::updateRenderSize();
dusk::frame_interp::begin_frame(pacing.is_interpolating, pacing.do_sim_tick, pacing.interpolation_step); const auto pacing = dusk::game_clock::advance_main_loop();
if (pacing.is_interpolating) { if (pacing.is_interpolating) {
if (pacing.do_sim_tick) { if (pacing.sim_ticks_to_run > 0) {
dusk::frame_interp::begin_frame(true, true, 0.0f);
dusk::frame_interp::set_ui_tick_pending(true); dusk::frame_interp::set_ui_tick_pending(true);
mDoCPd_c::read(); for (int sim_tick = 0; sim_tick < pacing.sim_ticks_to_run; ++sim_tick) {
DuskDebugPad(); dusk::frame_interp::begin_sim_tick();
dusk::gyro::read(pacing.sim_pace); mDoCPd_c::read();
fapGm_Execute(); DuskDebugPad();
mDoAud_Execute(); dusk::gyro::read(pacing.sim_pace);
dusk::game_clock::reset_accumulator(); fapGm_Execute();
mDoAud_Execute();
dusk::game_clock::commit_sim_tick();
}
} }
dusk::frame_interp::begin_frame(true, false,
dusk::game_clock::sample_interpolation_step());
dusk::frame_interp::interpolate(); dusk::frame_interp::interpolate();
dusk::frame_interp::begin_presentation_camera(); dusk::frame_interp::begin_presentation_camera();
if (!pacing.do_sim_tick) { // run draw functions for anything specially marked to handle interp
// run draw functions for anything specially marked to handle interp on non-sim fpcM_DrawIterater((fpcM_DrawIteraterFunc)fpcM_Draw);
// ticks
fpcM_DrawIterater((fpcM_DrawIteraterFunc)fpcM_Draw);
}
cAPIGph_Painter(); cAPIGph_Painter();
dusk::frame_interp::end_presentation_camera(); dusk::frame_interp::end_presentation_camera();
dusk::frame_interp::set_ui_tick_pending(false); dusk::frame_interp::set_ui_tick_pending(false);
} else { } else {
dusk::frame_interp::begin_frame(false, true, 0.0f);
dusk::frame_interp::set_ui_tick_pending(true); dusk::frame_interp::set_ui_tick_pending(true);
// Game Inputs // Game Inputs
+58
View File
@@ -0,0 +1,58 @@
"""
Convert a folder of TPGZ saves to a states.json
Usage:
python saves_to_states_json.py path/to/saves [prefix]
Requirements:
pip install zstandard
"""
import base64
import json
import struct
import sys
import zstandard
from pathlib import Path
SAVE_C_SIZE = 0x958
PACKET_FORMAT = "<8sbbh"
RETURN_PLACE_OFF = 0x058
NAME_OFF = RETURN_PLACE_OFF + 0x00
ROOM_OFF = RETURN_PLACE_OFF + 0x09
SPAWN_POINT_OFF = RETURN_PLACE_OFF + 0x08
folder = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parent
out_path = folder / "states.json"
if len(sys.argv) > 2:
prefix = sys.argv[2]
else:
prefix = None
cctx = zstandard.ZstdCompressor(level=1)
states = []
for bin_path in sorted(folder.glob("*.bin")):
raw = bin_path.read_bytes()
save_c = raw[:SAVE_C_SIZE]
if len(save_c) < SAVE_C_SIZE:
print(f" skip {bin_path.name}: too small ({len(save_c)} bytes)")
continue
stage_name = save_c[NAME_OFF:NAME_OFF + 8]
room_no = struct.unpack_from("b", save_c, ROOM_OFF)[0]
spawn_point = struct.unpack_from("B", save_c, SPAWN_POINT_OFF)[0]
pkt = struct.pack(PACKET_FORMAT, stage_name, room_no, -1, spawn_point)
payload = pkt + save_c
encoded = base64.b64encode(cctx.compress(payload)).decode("ascii")
stage_str = stage_name.rstrip(b"\x00").decode("ascii", errors="replace")
print(f" {bin_path.stem:30s} stage={stage_str!r} room={room_no} point={spawn_point}")
states.append({"name": f"({prefix}) {bin_path.stem}" if prefix else bin_path.stem, "data": encoded})
out_path.write_text(json.dumps(states, indent=2))
print(f"\nWrote {len(states)} states to {out_path}")