Compare commits

...

104 Commits

Author SHA1 Message Date
TakaRikka 2b65a696dd Merge pull request #556 from TwilitRealm/fix/zant_fog
Frame Interp: Fix Zant Fog Spin
2026-04-25 21:31:30 -07:00
Pheenoh 02924549be fix idle howl blinking 2026-04-25 21:21:22 -07:00
Pheenoh 34ec362876 fix zant fog spin and culling 2026-04-25 21:37:11 -06:00
MelonSpeedruns ba58d2486e Free Camera (#542)
* freecam wip

* added manual mode

* fix freecam collision

* made freecam into its own function

* Added settings & Radius modification when camer is higher up

---------

Co-authored-by: MelonSpeedruns <melonspeedruns@stratobox.net>
2026-04-25 21:35:43 -06:00
Luke Street f7b5350d60 Update README.md 2026-04-25 20:42:02 -06:00
Howard Luck 9ddfcc38c9 fix interp on ooks rope (#553) 2026-04-25 20:37:36 -06:00
TakaRikka cee66a6fd5 Merge pull request #554 from TwilitRealm/achievements
achievements
2026-04-25 19:25:50 -07:00
madeline 574b6197a2 achievements 2026-04-25 18:23:12 -07:00
Howard Luck 6fc8628144 fix gdorf horseback reins + demo cuts (#551) 2026-04-25 20:44:38 -04:00
Irastris ca9330b412 Register interp callback for d_a_obj_lv8OptiLift
Fixes disappearing platforms
2026-04-25 20:40:29 -04:00
Luke Street 1b792eb964 Update aurora 2026-04-25 16:18:16 -06:00
Howard Luck 0e5ea0d3b1 fix d_meter_button flashing (#549) 2026-04-25 15:48:45 -06:00
Pieter-Jan Briers 977ad16806 GCN PAL support (#533)
* Basic PAL ISO & language support

Probably still needs much more work

* Add language selector to pre-launch

* Store DVDDiskID in a global

Can use this later for things

* More version system API improvements

* d_name mostly region switching fully

JPN doesn't work yet cuz it'll be a nightmare, probably.

* More version switching support

* Mark GCN PAL as supported ROM

* Fix remaining REL assets to have PAL offsets

* d_a_mg_fish PAL

* d_a_mg_fshop PAL

* isRegionUsa helper

* d_menu_fishing PAL

* d_msg_class PAL

* m_Do_MemCardRWmng PAL

* Update CARDInit call & remove DUSK_TP_VERSION

* Fix Ganon cape

Missed this one.

* Remove tp_version from Sentry

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-04-25 15:46:12 -06:00
Irastris 41420bc71c Chu Fix 2: Electric Boogaloo 2026-04-25 17:08:22 -04:00
Irastris 5ec5f8864a Register interp callback for d_a_e_sm2
Fixes chu color changes
2026-04-25 16:59:48 -04:00
MelonSpeedruns 60e8836968 Tears Collect during cutscenes (#539)
TPHD has tears collectable just as fast during cutscenes, this changes our implementation to reflect that.

Co-authored-by: MelonSpeedruns <melonspeedruns@stratobox.net>
2026-04-25 14:59:30 -06:00
MelonSpeedruns e755148f16 Fix some UBs (#544)
Co-authored-by: MelonSpeedruns <melonspeedruns@stratobox.net>
2026-04-25 14:59:04 -06:00
Luke Street 4e264d6a22 Fix Zbuffer texture binding
Resolves #511
2026-04-24 22:53:48 -06:00
Irastris c8e89a0f99 Fix grass cutting when interp is enabled 2026-04-25 00:31:40 -04:00
Luke Street c61e32cd4f Update aurora for GXPeekZ
Resolves #281
Resolves #468
2026-04-24 22:10:56 -06:00
Irastris 274bf61b3e Fix Link's hands detaching when interp is enabled 2026-04-24 22:56:47 -04:00
TakaRikka 05b2a5cbe9 Merge pull request #530 from TwilitRealm/fix-event-flag-constant-sorting
Implement custom sorting when sorting event flags by flag on/off
2026-04-24 15:23:49 -07:00
TakaRikka aa838c8003 Merge pull request #532 from TwilitRealm/feature/even-faster-tears
Even faster tears to match TPHD
2026-04-24 15:14:10 -07:00
MelonSpeedruns d5aabe9f9e Even faster tears to match TPHD 2026-04-24 18:06:02 -04:00
roeming 171dbdd0a1 Implement custom sorting when sorting event flags by flag on/off 2026-04-24 16:50:31 -04:00
Luke Street b2c09d56d6 Remove unnecessary logic from dMirror_packet_c::entryModel 2026-04-24 13:34:51 -06:00
Luke Street f0e8379fa5 Fix UaF in d_a_mirror
Resolves #505
2026-04-24 13:19:09 -06:00
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
104 changed files with 3722 additions and 572 deletions
+56 -33
View File
@@ -90,6 +90,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE 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)
set(DAWN_USE_WAYLAND ON CACHE BOOL "Enable support for Wayland surface" FORCE)
endif ()
@@ -275,22 +280,16 @@ set(DUSK_COMPANY_NAME "Twilit Realm")
set(DUSK_FILE_DESCRIPTION "Dusk")
set(DUSK_PRODUCT_NAME "Dusk")
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
set(DUSK_GAME_NAME "GZ2E")
set(DUSK_GAME_VERSION "01")
set(DUSK_TP_VERSION ${DUSK_GAME_NAME}${DUSK_GAME_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})
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0
DUSK_TP_VERSION="${DUSK_TP_VERSION}" DUSK_GAME_NAME="${DUSK_GAME_NAME}" DUSK_GAME_VERSION="${DUSK_GAME_VERSION}")
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0)
set(GAME_INCLUDE_DIRS
include
src
assets/${DUSK_TP_VERSION}
assets/GZ2E01 # TODO: make this dynamic if needed?
libs/JSystem/include
libs
extern/aurora/include/dolphin
@@ -374,41 +373,65 @@ endif ()
# 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
add_library(game_debug OBJECT ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES}
set(GAME_DEBUG_FILES
${SSYSTEM_FILES}
src/dusk/audio/DuskAudioSystem.cpp
src/dusk/audio/JASCriticalSection.cpp
src/dusk/audio/DuskDsp.cpp
src/dusk/audio/Adpcm.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
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>)
target_compile_definitions(game_base PRIVATE ${GAME_COMPILE_DEFS} NDEBUG=1 NDEBUG_DEFINED=1 DEBUG_DEFINED=0 $<$<CONFIG:Debug>:PARTIAL_DEBUG=1>)
foreach(jsystem_lib IN LISTS JSYSTEM_LIBRARIES)
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
target_precompile_headers(game_base PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
target_include_directories(game_debug PRIVATE ${GAME_INCLUDE_DIRS})
target_include_directories(game_base PRIVATE ${GAME_INCLUDE_DIRS})
# 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)
set(JSYSTEM_LINK_LIBRARIES ${JSYSTEM_LIBRARIES})
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
# JSystem libraries reference each other, so they need a RESCAN group there.
set(JSYSTEM_LINK_LIBRARIES "$<LINK_GROUP:RESCAN,${JSYSTEM_LIBRARIES}>")
endif ()
target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0)
target_include_directories(dusk PRIVATE include)
target_link_libraries(dusk PRIVATE game_base game_debug aurora::main)
set(DUSK_FILES src/dusk/main.cpp ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
if(ANDROID)
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)
add_dependencies(dusk crashpad_handler)
endif ()
+8 -36
View File
@@ -4,56 +4,28 @@
- ### **[Discord](https://discord.gg/QACynxeyna)**
# Setup
**⚠️Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
**⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
### 1. Verify your ROM dump
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 |
| ------------ | ---------------------------------------- |
|--------------| ---------------------------------------- |
| GameCube USA | 75edd3ddff41f125d1b4ce1a40378f1b565519e7 |
| GameCube PAL | 2601822a488eeb86fb89db16ca8f29c2c953e1ca |
### 2. Download [Dusk](https://github.com/TwilitRealm/dusk/releases)
### 3. Setup the game
#### Windows
- Extract the zip folder
- Place your dump of the game into the same folder where you extracted to
- Launch `dusk.exe`
- Launch Dusk
- Select Options, then set the ISO Path to your supported game dump
- Press Start Game to play!
#### macOS
- TODO
#### Linux
- TODO
#### iOS
- TODO
#### android
- TODO
![Dusk options](assets/dusk_options.png)
# Building
If you'd like to build Dusk from source, please read the [build instructions](docs/building.md).
# Credits
- Taka
- encounter
- Antidote
- caseif
- CraftyBoss
- crowell
- dooplecks
- gymnast86
- Irastris
- kipcode66
- Lars
- LunarSoap
- Maddie
- MelonSpeedruns
- Pheenoh
- PJB
- Roeming
- YunataSavior
Special thanks to the TP Decomp team, the GC/Wii Decomp community, the Aurora developers, and the TP speedrunning community.
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors).
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

-10
View File
@@ -13,13 +13,3 @@ buildType:
short: RelWithDebInfo
long: Optimized, with debug symbols
buildType: RelWithDebInfo
tp_version:
default: GZ2E01
description: TP Version
choices:
GZ2E01:
short: GZ2E01
long: GZ2E01
settings:
DUSK_TP_VERSION: GZ2E01
+1 -1
+88 -3
View File
@@ -314,7 +314,7 @@ set(SSYSTEM_FILES
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/JPAResource.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/JPAParticle.cpp
libs/JSystem/src/JParticle/JPAMath.cpp
)
add_library(JSystem_JFramework STATIC
libs/JSystem/src/JFramework/JFWSystem.cpp
libs/JSystem/src/JFramework/JFWDisplay.cpp
)
add_library(JSystem_J3DU STATIC
libs/JSystem/src/J3DU/J3DUClipper.cpp
libs/JSystem/src/J3DU/J3DUDL.cpp
)
add_library(JSystem_JKernel STATIC
libs/JSystem/src/JKernel/JKRHeap.cpp
libs/JSystem/src/JKernel/JKRExpHeap.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/JKRDvdAramRipper.cpp
libs/JSystem/src/JKernel/JKRDecomp.cpp
)
add_library(JSystem_JMath STATIC
libs/JSystem/src/JMath/JMath.cpp
libs/JSystem/src/JMath/random.cpp
libs/JSystem/src/JMath/JMATrigonometric.cpp
)
add_library(JSystem_JSupport STATIC
libs/JSystem/src/JSupport/JSUList.cpp
libs/JSystem/src/JSupport/JSUInputStream.cpp
libs/JSystem/src/JSupport/JSUOutputStream.cpp
libs/JSystem/src/JSupport/JSUMemoryStream.cpp
libs/JSystem/src/JSupport/JSUFileStream.cpp
)
add_library(JSystem_JUtility STATIC
libs/JSystem/src/JUtility/JUTCacheFont.cpp
libs/JSystem/src/JUtility/JUTResource.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/JUTDirectFile.cpp
libs/JSystem/src/JUtility/JUTFontData_Ascfont_fix12.cpp
)
add_library(JSystem_JStage STATIC
libs/JSystem/src/JStage/JSGActor.cpp
libs/JSystem/src/JStage/JSGAmbientLight.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/JSGObject.cpp
libs/JSystem/src/JStage/JSGSystem.cpp
)
add_library(JSystem_J2DGraph STATIC
libs/JSystem/src/J2DGraph/J2DGrafContext.cpp
libs/JSystem/src/J2DGraph/J2DOrthoGraph.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/J2DAnimation.cpp
libs/JSystem/src/J2DGraph/J2DManage.cpp
)
add_library(JSystem_J3DGraphBase STATIC
libs/JSystem/src/J3DGraphBase/J3DGD.cpp
libs/JSystem/src/J3DGraphBase/J3DSys.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/J3DDrawBuffer.cpp
libs/JSystem/src/J3DGraphBase/J3DStruct.cpp
)
add_library(JSystem_J3DGraphAnimator STATIC
libs/JSystem/src/J3DGraphAnimator/J3DShapeTable.cpp
libs/JSystem/src/J3DGraphAnimator/J3DJointTree.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/J3DJoint.cpp
libs/JSystem/src/J3DGraphAnimator/J3DMaterialAttach.cpp
)
add_library(JSystem_J3DGraphLoader STATIC
libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp
libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.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/J3DShapeFactory.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-data.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-data-parse.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/object.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-fog.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/object-sound.cpp
)
add_library(JSystem_JStudio_JParticle STATIC
libs/JSystem/src/JStudio/JStudio_JParticle/control.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/JASTaskThread.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/JAUSoundTable.cpp
libs/JSystem/src/JAudio2/JAUStreamFileTable.cpp
)
add_library(JSystem_JMessage STATIC
libs/JSystem/src/JMessage/control.cpp
libs/JSystem/src/JMessage/data.cpp
libs/JSystem/src/JMessage/processor.cpp
libs/JSystem/src/JMessage/resource.cpp
libs/JSystem/src/JMessage/locale.cpp
)
add_library(JSystem_JGadget STATIC
libs/JSystem/src/JGadget/binary.cpp
libs/JSystem/src/JGadget/define.cpp
libs/JSystem/src/JGadget/linklist.cpp
libs/JSystem/src/JGadget/search.cpp
libs/JSystem/src/JGadget/std-vector.cpp
)
add_library(JSystem_JAHostIO STATIC
libs/JSystem/src/JAHostIO/JAHFrameNode.cpp
libs/JSystem/src/JAHostIO/JAHioMessage.cpp
libs/JSystem/src/JAHostIO/JAHioMgr.cpp
libs/JSystem/src/JAHostIO/JAHioNode.cpp
libs/JSystem/src/JAHostIO/JAHioUtil.cpp
libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp
)
add_library(JSystem_JHostIO STATIC
libs/JSystem/src/JHostIO/JORFile.cpp
libs/JSystem/src/JHostIO/JORHostInfo.cpp
libs/JSystem/src/JHostIO/JORMessageBox.cpp
@@ -559,7 +619,28 @@ set(JSYSTEM_DEBUG_FILES
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
@@ -1341,13 +1422,13 @@ set(DUSK_FILES
src/dusk/crash_reporting.cpp
src/dusk/endian.cpp
src/dusk/extras.c
src/dusk/extras.cpp
src/dusk/file_select.cpp
src/dusk/file_select.hpp
src/dusk/frame_interpolation.cpp
src/dusk/game_clock.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
src/dusk/gamepad_color.cpp
src/dusk/io.cpp
src/dusk/layout.cpp
src/dusk/logging.cpp
@@ -1379,10 +1460,14 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
src/dusk/imgui/ImGuiAchievements.hpp
src/dusk/imgui/ImGuiAchievements.cpp
src/dusk/achievements.cpp
src/dusk/iso_validate.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
src/dusk/OSMutex.cpp
src/dusk/discord_presence.cpp
src/dusk/version.cpp
)
+9
View File
@@ -188,6 +188,15 @@ public:
/* 0x273C */ f32 mKankyoBlend;
/* 0x2740 */ u8 field_0x2740;
/* 0x2744 */ dMsgFlow_c mMsgFlow;
#if TARGET_PC
cXyz mReinsInterpPrev[2][16];
cXyz mReinsInterpCurr[2][16];
cXyz mReinsTexInterpPrev[2];
cXyz mReinsTexInterpCurr[2];
bool mReinsInterpPrevValid;
bool mReinsInterpCurrValid;
s8 mDemoCamSyncTicks;
#endif
};
STATIC_ASSERT(sizeof(b_gnd_class) == 0x2790);
+6
View File
@@ -44,6 +44,12 @@ public:
/* 0x88C */ u8 field_0x88C[0x8C8 - 0x88C];
/* 0x8C8 */ s8 field_0x8c8;
/* 0x8C9 */ u8 mInitHIO;
#if TARGET_PC
cXyz mRopeInterpPrev[16];
cXyz mRopeInterpCurr[16];
bool mRopeInterpPrevValid;
bool mRopeInterpCurrValid;
#endif
};
STATIC_ASSERT(sizeof(e_mb_class) == 0x8cc);
+6
View File
@@ -143,6 +143,12 @@ public:
/* 0x20 */ JORFile mFile;
#endif
#if TARGET_PC
/* 0x24 */ u8 mManualMode;
/* 0x25 */ f32 freeXAngle;
/* 0x29 */ f32 freeYAngle;
#endif
u32 Id(s32 i_style) { return mCamStyleData[i_style].field_0x0; }
int Algorythmn(s32 i_style) { return mCamStyleData[i_style].field_0x4; }
int Algorythmn() { return mCurrentStyle->field_0x4; }
+5
View File
@@ -273,6 +273,8 @@ public:
/* 0xA4 */ f32 field_0xa4;
/* 0xA8 */ int field_0xa8;
/* 0xAC */ f32 field_0xac;
f32 xAngle;
f32 yAngle;
};
struct LockOnData {
@@ -1024,6 +1026,9 @@ public:
bool colosseumCamera(s32);
bool test1Camera(s32);
bool test2Camera(s32);
#if TARGET_PC
bool freeCamera();
#endif
bool towerCamera(s32);
bool hookshotCamera(s32);
bool railCamera(s32);
+4
View File
@@ -103,6 +103,10 @@ public:
field_0xd98 = param_1;
}
#if TARGET_PC
void resetScrollArrowMask() { field_0xdda = 0; }
#endif
/* 0xC98 */ JKRExpHeap* mpHeap;
/* 0xC9C */ JKRExpHeap* mpTalkHeap;
/* 0xCA0 */ STControl* mpStick;
+9
View File
@@ -206,6 +206,15 @@ private:
/* 0x6D3 */ u8 field_0x6d3;
#if TARGET_PC
f32 mSelectItemSlideElapsed[4];
f32 mCursorInterpPrevX;
f32 mCursorInterpPrevY;
f32 mCursorInterpCurrX;
f32 mCursorInterpCurrY;
s16 mCursorInterpPrevAngle;
s16 mCursorInterpCurrAngle;
bool mCursorInterpPrevAngular;
bool mCursorInterpCurrAngular;
bool mCursorInterpInit;
#endif
};
+5
View File
@@ -343,6 +343,11 @@ public:
/* 0x624 */ f32 mMidonaPosX;
/* 0x628 */ f32 mMidonaPosY;
/* 0x62C */ f32 mMidonaScale;
#ifdef TARGET_PC
bool mWasListen[2];
bool mWasRepeat[2];
#endif
};
#endif /* D_METER_D_METER_BUTTON_H */
+1 -1
View File
@@ -89,7 +89,7 @@ public:
void MojiSelectAnm3();
int mojiChange(u8);
void selectMojiSet();
#if REGION_JPN
#if TARGET_PC || REGION_JPN
int checkDakuon(int, u8);
int setDakuon(int, u8);
#endif
+2 -2
View File
@@ -79,7 +79,7 @@ public:
bool isProgressiveMode();
void setRenderMode();
#if VERSION == VERSION_GCN_PAL || PLATFORM_WII || PLATFORM_SHIELD
#if TARGET_PC || VERSION == VERSION_GCN_PAL || PLATFORM_WII || PLATFORM_SHIELD
u8 getPalLanguage();
#endif
@@ -149,7 +149,7 @@ public:
/* 0x200 */ dDlst_2D_c* mNvLogo;
/* 0x204 */ dDlst_2D_c* mMocImg;
#endif
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
/* 0x1FC */ mDoDvdThd_mountArchive_c* mpPalLogoResCommand;
#endif
/* 0x1FC */ request_of_phase_process_class* m_preLoad_dylPhase;
+3
View File
@@ -48,6 +48,9 @@ public:
}
#ifdef TARGET_PC
f32 getPositionX() const { return mPositionX; }
f32 getPositionY() const { return mPositionY; }
void refreshAspectScale();
#endif
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#include <cstdint>
#include <functional>
#include <queue>
#include <string>
#include <vector>
#include "nlohmann/json.hpp"
namespace dusk {
enum class AchievementCategory : uint8_t {
Story,
Collection,
Challenge,
Minigame,
Glitched
};
struct Achievement {
const char* key;
const char* name;
const char* description;
AchievementCategory category;
bool isCounter;
int32_t goal;
int32_t progress;
bool unlocked;
};
// Responsible for updating a.progress.
// Use extra for any per-achievement state that must survive across frames or sessions, extra is saved
using AchievementCheckFn = std::function<void(Achievement& a, nlohmann::json& extra)>;
class AchievementSystem {
public:
static AchievementSystem& get();
void load();
void save();
void tick();
void clearAll();
std::vector<Achievement> getAchievements() const;
bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); }
std::string consumePendingUnlock();
private:
struct Entry {
Achievement achievement;
AchievementCheckFn check;
nlohmann::json extra;
};
AchievementSystem();
static std::vector<Entry> makeEntries();
void processEntry(Entry& e);
std::vector<Entry> m_entries;
bool m_loaded = false;
bool m_dirty = false;
std::queue<std::string> m_pendingUnlocks;
};
} // namespace dusk
+12 -3
View File
@@ -1,22 +1,31 @@
#pragma once
#include "dolphin/types.h"
#include "version.hpp"
namespace dusk {
struct OffsetVersion {
version::GameVersion mGameVersion;
u32 mOffset;
constexpr OffsetVersion(const version::GameVersion gameVersion, const u32 offset)
: mGameVersion(gameVersion), mOffset(offset) {}
};
/**
* Load bytes from the main DOL by GameCube virtual address
*/
bool LoadDolAsset(void* dst, u32 virtualAddress, s32 size);
bool LoadDolAsset(void* dst, std::initializer_list<OffsetVersion> virtualAddress, s32 size);
/**
* Load bytes from a REL file in the ISO filesystem, dst must be 32-byte aligned
*/
bool LoadRelAsset(void* dst, const char* dvdPath, s32 offset, s32 size);
bool LoadRelAsset(void* dst, const char* dvdPath, std::initializer_list<OffsetVersion> offset, s32 size);
/**
* Load bytes from a REL inside RELS.arc
*/
bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, s32 offset, s32 size);
bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, std::initializer_list<OffsetVersion> offset, s32 size);
} // namespace dusk
+1
View File
@@ -16,6 +16,7 @@ void ensure_initialized();
void begin_record();
void end_record();
void begin_sim_tick();
void begin_frame(bool enabled, bool is_sim_frame, float step);
void interpolate();
float get_interpolation_step();
+6 -13
View File
@@ -1,13 +1,8 @@
#ifndef DUSK_GAME_CLOCK_H
#define DUSK_GAME_CLOCK_H
#pragma once
#include <stddef.h>
namespace dusk {
namespace game_clock {
namespace dusk::game_clock {
void ensure_initialized();
void reset_accumulator();
void reset_frame_timer();
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 {
float presentation_dt_seconds;
bool is_interpolating;
bool do_sim_tick;
float interpolation_step;
int sim_ticks_to_run;
float sim_pace;
};
MainLoopPacer advance_main_loop();
void commit_sim_tick();
float sample_interpolation_step();
float consume_interval(const void* consumer);
} // namespace game_clock
} // namespace dusk
#endif // DUSK_GAME_CLOCK_H
} // namespace dusk::game_clock
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#ifndef GAMEPAD_COLOR_H
#define GAMEPAD_COLOR_H
void handleGamepadColor();
#endif
+26 -1
View File
@@ -13,12 +13,26 @@ enum class BloomMode : int {
Dusk = 2,
};
enum class GameLanguage : u8 {
English = OS_LANGUAGE_ENGLISH,
German = OS_LANGUAGE_GERMAN,
French = OS_LANGUAGE_FRENCH,
Spanish = OS_LANGUAGE_SPANISH,
Italian = OS_LANGUAGE_ITALIAN,
};
namespace config {
template <>
struct ConfigEnumRange<BloomMode> {
static constexpr auto min = BloomMode::Off;
static constexpr auto max = BloomMode::Dusk;
};
template <>
struct ConfigEnumRange<GameLanguage> {
static constexpr auto min = GameLanguage::English;
static constexpr auto max = GameLanguage::Italian;
};
}
// Persistent user settings
@@ -46,6 +60,8 @@ struct UserSettings {
// Game settings
struct {
ConfigVar<GameLanguage> language;
// QoL
ConfigVar<bool> enableQuickTransform;
ConfigVar<bool> hideTvSettingsScreen;
@@ -66,9 +82,11 @@ struct UserSettings {
// Preferences
ConfigVar<bool> enableMirrorMode;
ConfigVar<bool> invertCameraXAxis;
ConfigVar<bool> disableMainHUD;
ConfigVar<bool> pauseOnFocusLost;
ConfigVar<bool> enableLinkDollRotation;
ConfigVar<bool> enableAchievementNotifications;
// Graphics
ConfigVar<BloomMode> bloomMode;
@@ -93,6 +111,10 @@ struct UserSettings {
ConfigVar<float> gyroDeadband;
ConfigVar<bool> gyroInvertPitch;
ConfigVar<bool> gyroInvertYaw;
ConfigVar<bool> freeCamera;
ConfigVar<bool> invertCameraXAxis;
ConfigVar<bool> invertCameraYAxis;
ConfigVar<float> freeCameraSensitivity;
// Cheats
ConfigVar<bool> infiniteHearts;
@@ -101,6 +123,7 @@ struct UserSettings {
ConfigVar<bool> infiniteOil;
ConfigVar<bool> infiniteOxygen;
ConfigVar<bool> infiniteRupees;
ConfigVar<bool> enableIndefiniteItemDrops;
ConfigVar<bool> moonJump;
ConfigVar<bool> superClawshot;
ConfigVar<bool> alwaysGreatspin;
@@ -124,6 +147,7 @@ struct UserSettings {
ConfigVar<bool> wasPresetChosen;
ConfigVar<bool> enableCrashReporting;
ConfigVar<bool> duskMenuOpen;
ConfigVar<int> cardFileType;
} backend;
};
@@ -148,6 +172,7 @@ struct TransientSettings {
CollisionViewSettings collisionView;
bool skipFrameRateLimit;
bool moveLinkActive;
bool stateShareLoadActive;
};
TransientSettings& getTransientSettings();
+25 -25
View File
@@ -1,9 +1,10 @@
#ifndef DUSK_TIME_H
#define DUSK_TIME_H
#include <chrono>
#include <numeric>
#include <array>
#include <numeric>
#include "SDL3/SDL_timer.h"
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@@ -15,28 +16,26 @@
#include <Windows.h>
#include <shellapi.h>
#include <intrin.h>
#else
#include "SDL3/SDL_timer.h"
#endif
class Limiter {
using delta_clock = std::chrono::high_resolution_clock;
using duration_t = std::chrono::nanoseconds;
public:
void Reset() { m_oldTime = delta_clock::now(); }
using duration_t = Uint64;
void Reset() { m_oldTime = SDL_GetTicksNS(); }
void Sleep(duration_t targetFrameTime) {
if (targetFrameTime.count() == 0) {
if (targetFrameTime == 0) {
return;
}
auto start = delta_clock::now();
const Uint64 start = SDL_GetTicksNS();
duration_t adjustedSleepTime = SleepTime(targetFrameTime);
if (adjustedSleepTime.count() > 0) {
if (adjustedSleepTime > 0) {
NanoSleep(adjustedSleepTime);
duration_t overslept = TimeSince(start) - adjustedSleepTime;
if (overslept < duration_t{targetFrameTime}) {
const duration_t elapsed = TimeSince(start);
const duration_t overslept = elapsed > adjustedSleepTime ? elapsed - adjustedSleepTime : 0;
if (overslept < targetFrameTime) {
m_overheadTimes[m_overheadTimeIdx] = overslept;
m_overheadTimeIdx = (m_overheadTimeIdx + 1) % m_overheadTimes.size();
}
@@ -45,23 +44,23 @@ public:
}
duration_t SleepTime(duration_t targetFrameTime) {
const auto sleepTime = duration_t{targetFrameTime} - TimeSince(m_oldTime);
m_overhead = std::accumulate(m_overheadTimes.begin(), m_overheadTimes.end(), duration_t{}) / m_overheadTimes.size();
const duration_t elapsed = TimeSince(m_oldTime);
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) {
return sleepTime - m_overhead;
}
return duration_t{0};
return 0;
}
private:
delta_clock::time_point m_oldTime;
Uint64 m_oldTime = 0;
std::array<duration_t, 4> m_overheadTimes{};
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) {
return std::chrono::duration_cast<duration_t>(delta_clock::now() - start);
}
duration_t TimeSince(Uint64 start) const { return SDL_GetTicksNS() - start; }
#if _WIN32
void NanoSleep(const duration_t duration) {
@@ -85,9 +84,10 @@ private:
LARGE_INTEGER start, current;
QueryPerformanceCounter(&start);
LONGLONG ticksToWait = static_cast<LONGLONG>(duration.count() * countPerNs);
if (DWORD ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); ms > 1) {
::Sleep(ms - 1);
const LONGLONG ticksToWait = static_cast<LONGLONG>(duration * countPerNs);
const Uint64 ms = duration / 1'000'000ULL;
if (ms > 1) {
::Sleep(static_cast<DWORD>(ms - 1));
}
do {
QueryPerformanceCounter(&current);
@@ -99,7 +99,7 @@ private:
} while (current.QuadPart - start.QuadPart < ticksToWait);
}
#else
void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration.count()); }
void NanoSleep(const duration_t duration) { SDL_DelayPrecise(duration); }
#endif
};
+67
View File
@@ -0,0 +1,67 @@
#ifndef DUSK_VERSION_HPP
#define DUSK_VERSION_HPP
/**
* Functionality for switching game behavior based on the loaded game version (e.g. PAL/JPN, GC/Wii)
*/
namespace dusk::version {
enum class GameVersion : u8 {
GcnUsa = VERSION_GCN_USA,
GcnPal = VERSION_GCN_PAL,
GcnJpn = VERSION_GCN_JPN,
WiiUsaRev0 = VERSION_WII_USA_R0,
WiiUsa = VERSION_WII_USA_R2,
WiiPal = VERSION_WII_PAL,
WiiJpn = VERSION_WII_JPN,
WiiKor = VERSION_WII_KOR,
};
bool isGcn();
bool isWii();
bool isPalOrAtLeastWiiR2();
bool isRegionPal();
bool isRegionJpn();
bool isRegionUsa();
GameVersion getGameVersion();
const DVDDiskID& getDiskID();
void init();
template<typename T>
struct VersionOption {
GameVersion mVersion;
T mValue;
constexpr VersionOption(GameVersion version, T value) : mVersion(version), mValue(value) {}
};
template<typename T>
const T& versionSelect(const std::initializer_list<VersionOption<T>> options) {
const auto version = getGameVersion();
for (const auto& opt : options) {
if (opt.mVersion == version) {
return opt.mValue;
}
}
// Unable to find value.
abort();
}
template<typename T>
const T& versionSelect(const std::initializer_list<VersionOption<T>> options, const T& defaultValue) {
const auto version = getGameVersion();
for (const auto& opt : options) {
if (opt.mVersion == version) {
return opt.mValue;
}
}
return defaultValue;
}
} // namespace dusk::version
#endif // DUSK_VERSION_HPP
+15 -1
View File
@@ -73,6 +73,9 @@
#endif
#ifndef __MWERKS__
#ifdef __cplusplus
extern "C" {
#endif
// Silence clangd errors about MWCC PPC intrinsics by declaring them here.
extern int __cntlzw(unsigned 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 __sync();
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
#ifndef M_PI
@@ -220,12 +230,16 @@ using std::isnan;
// Some basic macros that are more convenient than putting down #if blocks for one-line changes.
#if TARGET_PC
#define IF_DUSK(statement) statement
#define IF_DUSK_BLOCK(cond) if (cond) {
#define IF_DUSK_BLOCK_END }
#define IF_DUSK_ARG(expr) , expr
#define IF_NOT_DUSK(statement)
#define DUSK_IF_ELSE(dusk, orig) dusk
#else
#define IF_DUSK(statement)
#define IF_DUSK_ARG(expr)
#define IF_DUSK_BLOCK(cond)
#define IF_DUSK_BLOCK_END
#define IF_NOT_DUSK(statement) statement
#define DUSK_IF_ELSE(dusk, orig) orig
#endif
@@ -263,6 +263,9 @@ public:
/* 0x9C */ u8 field_0x9c[4];
/* 0xA0 */ OSTime mResetHoldStartTime;
/* 0xA8 */ u8 field_0xa8;
#if TARGET_PC
u32 mResetHoldFrameCount;
#endif
};
/**
+12 -4
View File
@@ -18,6 +18,7 @@
#include "dusk/logging.h"
#include "dusk/settings.h"
#include "dusk/time.h"
#include "f_op/f_op_overlap_mng.h"
#include "SDL3/SDL_timer.h"
#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));
constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2;
static void waitPrecise(Limiter& limiter, Uint64 targetNs) {
const auto sleepTime = limiter.SleepTime(std::chrono::nanoseconds(targetNs));
static void waitPrecise(Limiter& limiter, Limiter::duration_t targetNs) {
const auto sleepTime = limiter.SleepTime(targetNs);
dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime.count()) / static_cast<float>(targetNs));
limiter.Sleep(std::chrono::nanoseconds(targetNs));
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
limiter.Sleep(targetNs);
}
#endif
@@ -385,6 +386,13 @@ static void waitForTick(u32 p1, u16 p2) {
if (dusk::getTransientSettings().skipFrameRateLimit) {
p1 = OS_TIMER_CLOCK / 120;
}
#if TARGET_PC
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
return;
}
#endif
ZoneScopedC(tracy::Color::DimGray);
#endif
+11
View File
@@ -64,6 +64,9 @@ BOOL JUTGamePad::init() {
void JUTGamePad::clear() {
mButtonReset.mReset = false;
field_0xa8 = 1;
#if TARGET_PC
mResetHoldFrameCount = 0;
#endif
}
PADStatus JUTGamePad::mPadStatus[4];
@@ -219,11 +222,19 @@ void JUTGamePad::update() {
mButtonReset.mReset = false;
} else if (!JUTGamePad::C3ButtonReset::sResetOccurred) {
if (mButtonReset.mReset == true) {
#if TARGET_PC
checkResetCallback(++mResetHoldFrameCount * (OS_TIMER_CLOCK / 30));
#else
OSTime hold_time = OSGetTime() - mResetHoldStartTime;
checkResetCallback(hold_time);
#endif
} else {
mButtonReset.mReset = true;
#if TARGET_PC
mResetHoldFrameCount = 0;
#else
mResetHoldStartTime = OSGetTime();
#endif
}
}
+5 -5
View File
@@ -117,8 +117,8 @@ static Z2WolfHowlLine sNewSong3[9] = {
#if TARGET_PC
static Z2WolfHowlLine sHowlTimeSong[6] = {
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40},
{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, 15}, {HOWL_LINE_LOW, 15}, {HOWL_LINE_HIGH, 30},
};
#endif
@@ -368,9 +368,9 @@ void Z2WolfHowlMgr::setCorrectData(s8 curveID, Z2WolfHowlData* data) {
break;
#if TARGET_PC
case Z2WOLFHOWL_TIMESONG:
cPitchUp = 1.259906f;
cPitchCenter = 0.94387f;
cPitchDown = 0.840885f;
cPitchUp = 1.3348f;
cPitchCenter = 0.8909f;
cPitchDown = 0.7937f;
break;
#endif
default:
+16
View File
@@ -5877,6 +5877,11 @@ void daAlink_c::setItemMatrix(int param_0) {
modelCalc(mSheathModel);
int var_r26;
#if AVOID_UB
var_r26 = 0;
#endif
if (!checkNoResetFlg3(FLG3_UNK_4000000)) {
if (mEquipItem == 0x103 || param_0 != 0) {
mSwordModel->setBaseTRMtx(mpLinkModel->getAnmMtx(mLeftItemJntNo));
@@ -18936,11 +18941,20 @@ void daAlink_c::setDrawHand() {
mpLinkHandModel->setBaseTRMtx(mpLinkModel->getBaseTRMtx());
mpLinkHandModel->calc();
#if TARGET_PC
// FRAME INTERP NOTE: Always set these, otherwise the hands occasionally zip to origin.
// Doing it regardless of interpolation being active seems harmless.
mpLinkHandModel->setAnmMtx(1, mpLinkModel->getAnmMtx(9));
mpLinkHandModel->setAnmMtx(2, mpLinkModel->getAnmMtx(0xE));
#endif
if (var_r30 == 0xFE || var_r30 == 0xFB) {
field_0x06d0 = field_0x06d8;
} else {
field_0x06d0 = mpLinkHandModel->getModelData()->getMaterialNodePointer(var_r30)->getShape();
#if !TARGET_PC
mpLinkHandModel->setAnmMtx(1, mpLinkModel->getAnmMtx(9));
#endif
}
if (var_r30 == 0xFB) {
@@ -18959,7 +18973,9 @@ void daAlink_c::setDrawHand() {
field_0x06d4 = field_0x06dc;
} else {
field_0x06d4 = mpLinkHandModel->getModelData()->getMaterialNodePointer(var_r29)->getShape();
#if !TARGET_PC
mpLinkHandModel->setAnmMtx(2, mpLinkModel->getAnmMtx(0xE));
#endif
}
if (var_r29 == 0xFB) {
+8 -2
View File
@@ -41,7 +41,7 @@ void daAlink_c::handleWolfHowl() {
return;
}
bool canTransform = false;
bool canHowl = false;
if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) && !checkMagneBootsOn()) {
if (!checkForestOldCentury()) {
@@ -52,12 +52,17 @@ void daAlink_c::handleWolfHowl() {
(checkEventRun() || getMidnaActor()->checkMetamorphoseEnable()) &&
(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);
procWolfHowlDemoInit();
}
@@ -154,6 +159,7 @@ bool daAlink_c::checkGyroAimContext() {
case PROC_BOW_SUBJECT:
case PROC_BOOMERANG_SUBJECT:
case PROC_COPY_ROD_SUBJECT:
case PROC_HAWK_SUBJECT:
case PROC_HOOKSHOT_SUBJECT:
case PROC_SWIM_HOOKSHOT_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);
#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));
for (u16 i = 0; i < 4; i++) {
mpWlChainModels[i] = initModel(chainModelData, 0);
@@ -162,6 +179,23 @@ void daAlink_c::changeWolf() {
mpWlMidnaHairModel =
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->searchUpdateMaterialID(mpWlMidnaModel->getModelData());
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);
}
#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"));
u16 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++) {
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);
+60
View File
@@ -17,6 +17,9 @@
#include "Z2AudioLib/Z2Instances.h"
#include "dusk/frame_interpolation.h"
#include "dusk/settings.h"
class daB_GND_HIO_c : public JORReflexible {
public:
daB_GND_HIO_c();
@@ -279,6 +282,30 @@ static int h_nodeCallBack(J3DJoint* i_joint, int param_2) {
return 1;
}
#if TARGET_PC
static void b_gnd_rein_interp_callback(bool isSimFrame, void* pUserWork) {
b_gnd_class* i_this = (b_gnd_class*)pUserWork;
if (!i_this->mReinsInterpPrevValid || !i_this->mReinsInterpCurrValid) {
return;
}
const f32 alpha = dusk::frame_interp::get_interpolation_step();
for (int r = 0; r < 2; r++) {
cXyz* dst = i_this->mHorseReins[r].getPos(0);
for (int i = 0; i < 16; i++) {
const cXyz& p0 = i_this->mReinsInterpPrev[r][i];
const cXyz& p1 = i_this->mReinsInterpCurr[r][i];
dst[i] = p0 + (p1 - p0) * alpha;
}
}
cXyz* dst = i_this->field_0x21e8.getPos(0);
for (int i = 0; i < 2; i++) {
const cXyz& p0 = i_this->mReinsTexInterpPrev[i];
const cXyz& p1 = i_this->mReinsTexInterpCurr[i];
dst[i] = p0 + (p1 - p0) * alpha;
}
}
#endif
static int daB_GND_Draw(b_gnd_class* i_this) {
fopAc_ac_c* a_this = (fopAc_ac_c*)i_this;
@@ -366,6 +393,21 @@ static int daB_GND_Draw(b_gnd_class* i_this) {
i_this->field_0x21e8.update(2, l_color, &a_this->tevStr);
dComIfGd_set3DlineMat(&i_this->field_0x21e8);
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation) {
if (i_this->mReinsInterpCurrValid) {
memcpy(i_this->mReinsInterpPrev, i_this->mReinsInterpCurr, sizeof(i_this->mReinsInterpCurr));
memcpy(i_this->mReinsTexInterpPrev, i_this->mReinsTexInterpCurr, sizeof(i_this->mReinsTexInterpCurr));
i_this->mReinsInterpPrevValid = true;
}
for (int r = 0; r < 2; r++) {
memcpy(i_this->mReinsInterpCurr[r], i_this->mHorseReins[r].getPos(0), 16 * sizeof(cXyz));
}
memcpy(i_this->mReinsTexInterpCurr, i_this->field_0x21e8.getPos(0), 2 * sizeof(cXyz));
i_this->mReinsInterpCurrValid = true;
dusk::frame_interp::add_interpolation_callback(&b_gnd_rein_interp_callback, i_this);
}
#endif
}
return 1;
@@ -1189,10 +1231,16 @@ static void b_gnd_h_end(b_gnd_class* i_this) {
if (i_this->mDemoCamMode < 32) {
i_this->mDemoCamMode = 32;
#if TARGET_PC
i_this->mDemoCamSyncTicks = 2;
#endif
} else {
i_this->mDemoCamMode = 34;
i_this->mDemoCamTimer = 0;
i_this->mMoveMode = 2;
#if TARGET_PC
i_this->mDemoCamSyncTicks = 2;
#endif
}
}
break;
@@ -2887,6 +2935,9 @@ static void demo_camera(b_gnd_class* i_this) {
cXyz spF0;
s8 sp8 = false;
#if TARGET_PC
const s16 entry_demo_cam_mode = i_this->mDemoCamMode;
#endif
switch (i_this->mDemoCamMode) {
case 0:
break;
@@ -3725,6 +3776,15 @@ static void demo_camera(b_gnd_class* i_this) {
i_this->mDemoCamTimer = 10000;
}
}
#if TARGET_PC
if (entry_demo_cam_mode != i_this->mDemoCamMode) {
i_this->mDemoCamSyncTicks = 2;
}
if (i_this->mDemoCamSyncTicks > 0) {
dusk::frame_interp::request_presentation_sync();
i_this->mDemoCamSyncTicks--;
}
#endif
}
static void anm_se_set(b_gnd_class* i_this) {
+10 -1
View File
@@ -2725,7 +2725,16 @@ static void demo_camera(b_ob_class* i_this) {
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)};
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);
+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);
for (int i = 0; i < 4; i++) {
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);
+29
View File
@@ -12,6 +12,8 @@
#include "d/d_bomb.h"
#include "c/c_damagereaction.h"
#include "Z2AudioLib/Z2Instances.h"
#include "dusk/frame_interpolation.h"
#include "dusk/settings.h"
#define ACTION_STANDBY 0
#define ACTION_WALK1 1
@@ -63,6 +65,22 @@ static void anm_init(e_mb_class* i_this, int i_anmID, f32 i_morf, u8 i_attr, f32
i_this->mAnm = i_anmID;
}
#if TARGET_PC
static void e_mb_rope_interp_callback(bool isSimFrame, void* pUserWork) {
e_mb_class* i_this = (e_mb_class*)pUserWork;
if (!i_this->mRopeInterpPrevValid || !i_this->mRopeInterpCurrValid) {
return;
}
const f32 alpha = dusk::frame_interp::get_interpolation_step();
cXyz* dst = i_this->mRopeMat.getPos(0);
for (int i = 0; i < 16; i++) {
const cXyz& p0 = i_this->mRopeInterpPrev[i];
const cXyz& p1 = i_this->mRopeInterpCurr[i];
dst[i] = p0 + (p1 - p0) * alpha;
}
}
#endif
static int daE_MB_Draw(e_mb_class* i_this) {
fopAc_ac_c* a_this = (fopAc_ac_c*)i_this;
@@ -86,6 +104,17 @@ static int daE_MB_Draw(e_mb_class* i_this) {
static GXColor l_color = {0x14, 0x0F, 0x00, 0xFF};
i_this->mRopeMat.update(16, l_color, &a_this->tevStr);
dComIfGd_set3DlineMat(&i_this->mRopeMat);
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation) {
if (i_this->mRopeInterpCurrValid) {
memcpy(i_this->mRopeInterpPrev, i_this->mRopeInterpCurr, sizeof(i_this->mRopeInterpCurr));
i_this->mRopeInterpPrevValid = true;
}
memcpy(i_this->mRopeInterpCurr, i_this->mRopeMat.getPos(0), 16 * sizeof(cXyz));
i_this->mRopeInterpCurrValid = true;
dusk::frame_interp::add_interpolation_callback(&e_mb_rope_interp_callback, i_this);
}
#endif
return 1;
}
+58
View File
@@ -14,6 +14,10 @@
#include "f_op/f_op_camera_mng.h"
#include <cstring>
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
class daE_SM2_HIO_c : public fOpAcm_HIO_entry_c {
public:
daE_SM2_HIO_c();
@@ -76,8 +80,62 @@ static int nodeCallBack(J3DJoint* i_joint, int param_1) {
return 1;
}
#if TARGET_PC
static void daE_SM2_interp_callback(bool isSimFrame, void* pUserWork) {
e_sm2_class* i_this = static_cast<e_sm2_class*>(pUserWork);
if (i_this == NULL) {
return;
}
fopAc_ac_c* actor = (fopAc_ac_c*)&i_this->enemy;
g_env_light.settingTevStruct(0, &actor->current.pos, &actor->tevStr);
if (!i_this->isPiece) {
if (i_this->modelMorf == NULL) {
return;
}
J3DModel* model = i_this->modelMorf->getModel();
if (model == NULL) {
return;
}
g_env_light.setLightTevColorType_MAJI(model, &actor->tevStr);
J3DMaterial* material = model->getModelData()->getMaterialNodePointer(0);
material->getTevKColor(1)->r = i_this->color_R;
material->getTevKColor(1)->g = i_this->color_G;
material->getTevKColor(1)->b = i_this->color_B;
material->getTevKColor(1)->a = 217.0f * i_this->color_alpha;
if (i_this->pbtk != NULL) {
i_this->pbtk->entry(model->getModelData());
}
} else {
if (i_this->pieceModelMorf == NULL) {
return;
}
J3DModel* model = i_this->pieceModelMorf->getModel();
if (model == NULL) {
return;
}
J3DMaterial* material = model->getModelData()->getMaterialNodePointer(0);
material->getTevKColor(1)->r = i_this->color_R;
material->getTevKColor(1)->g = i_this->color_G;
material->getTevKColor(1)->b = i_this->color_B;
material->getTevKColor(1)->a = 217.0f * i_this->color_alpha;
g_env_light.setLightTevColorType_MAJI(model, &actor->tevStr);
}
}
#endif
static int daE_SM2_Draw(e_sm2_class* i_this) {
fopAc_ac_c* actor = (fopAc_ac_c*)&i_this->enemy;
#if TARGET_PC
dusk::frame_interp::add_interpolation_callback(&daE_SM2_interp_callback, i_this);
#endif
g_env_light.settingTevStruct(0, &actor->current.pos, &actor->tevStr);
J3DModel* model;
+3 -3
View File
@@ -1069,7 +1069,7 @@ void daE_YM_c::executeDown() {
if (mAcch.ChkGroundHit()) {
if (mFlyType != 1) {
#if TARGET_PC
bckSet(6, 0, 0.0f, dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0 ? 2.0f : 1.0f);
bckSet(6, 0, 0.0f, dusk::getSettings().game.fastTears ? 2.0f : 1.0f);
#else
bckSet(6, 0, 0.0f, 1.0f);
#endif
@@ -1093,7 +1093,7 @@ void daE_YM_c::executeDown() {
mSound.startCreatureSound(Z2SE_CM_BODYFALL_WATER_M, 0, -1);
mSound.startCreatureSound(Z2SE_EN_YM_MOGAKU, 0, -1);
#if TARGET_PC
bckSet(6, 0, 0.0f, dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0 ? 2.0f : 1.0f);
bckSet(6, 0, 0.0f, dusk::getSettings().game.fastTears ? 2.0f : 1.0f);
#else
bckSet(6, 0, 0.0f, 1.0f);
#endif
@@ -1115,7 +1115,7 @@ void daE_YM_c::executeDown() {
|| dComIfG_Bgsp().GetGroundCode(gnd_chk) == 5)
{
#if TARGET_PC
bckSet(6, 0, 0.0f, dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0 ? 2.0f : 1.0f);
bckSet(6, 0, 0.0f, dusk::getSettings().game.fastTears ? 2.0f : 1.0f);
#else
bckSet(6, 0, 0.0f, 1.0f);
#endif
+9 -4
View File
@@ -13,9 +13,12 @@
#if TARGET_PC
#include "dusk/dvd_asset.hpp"
#include "dusk/frame_interpolation.h"
static u8* l_Egnd_mantTEX_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0x1C00, 0x4000), true); return buf; }
static u8* l_Egnd_mantTEX_U_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0x5C00, 0x4000), true); return buf; }
static u8* l_Egnd_mantPAL_get() { alignas(32) static u8 buf[0x60]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0x9C00, 0x60), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_Egnd_mantTEX_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x1C00}, {GameVersion::GcnPal, 0x1C00}}, 0x4000), true); return buf; }
static u8* l_Egnd_mantTEX_U_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x5C00}, {GameVersion::GcnPal, 0x5C00}}, 0x4000), true); return buf; }
static u8* l_Egnd_mantPAL_get() { alignas(32) static u8 buf[0x60]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0x9C00}, {GameVersion::GcnPal, 0x9C00}}, 0x60), true); return buf; }
#define l_Egnd_mantTEX (l_Egnd_mantTEX_get())
#define l_Egnd_mantTEX_U (l_Egnd_mantTEX_U_get())
#define l_Egnd_mantPAL (l_Egnd_mantPAL_get())
@@ -251,7 +254,9 @@ static u32 l_texCoord[338] = {
};
#if TARGET_PC
static u8* l_Egnd_mantDL_get() { alignas(32) static u8 buf[0x3EC]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0xA9A0, 0x3EC), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_Egnd_mantDL_get() { alignas(32) static u8 buf[0x3EC]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", {{GameVersion::GcnUsa, 0xA9A0}, {GameVersion::GcnPal, 0xA9A0}}, 0x3EC), true); return buf; }
#define l_Egnd_mantDL (l_Egnd_mantDL_get())
#else
#include "assets/l_Egnd_mantDL.h"
+25 -2
View File
@@ -22,6 +22,7 @@
#include "d/d_s_play.h"
#include "d/d_vibration.h"
#include "f_op/f_op_kankyo_mng.h"
#include "dusk/version.hpp"
#include <cstring>
#define ANM_MG_FISH_MOUTH_CLOSE 4
@@ -3230,7 +3231,17 @@ static int daMg_Fish_Execute(mg_fish_class* i_this) {
daPy_py_c* player = daPy_getPlayerActorClass();
#if VERSION == VERSION_GCN_JPN
#if TARGET_PC
if (dusk::version::isRegionJpn()) {
lit_1008 = 0;
} else if (dusk::version::isRegionPal()) {
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
lit_1008 = 2;
} else {
lit_1008 = 0;
}
}
#elif VERSION == VERSION_GCN_JPN
lit_1008 = 0;
#elif VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
@@ -3843,7 +3854,19 @@ static int daMg_Fish_Create(fopAc_ac_c* i_this) {
a_this->mResName = "O_gD_bott";
}
#if VERSION == VERSION_GCN_JPN
#if TARGET_PC
if (dusk::version::isRegionJpn()) {
lit_1008 = 0;
} else if (dusk::version::isRegionPal()) {
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
lit_1008 = 2;
} else {
lit_1008 = 0;
}
} else {
lit_1008 = 1;
}
#elif VERSION == VERSION_GCN_JPN
lit_1008 = 0;
#elif VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
+27 -15
View File
@@ -5,14 +5,15 @@
#include "d/dolzel_rel.h" // IWYU pragma: keep
#include "Z2AudioLib/Z2Instances.h"
#include "d/actor/d_a_mg_fish.h"
#include "d/actor/d_a_mg_fshop.h"
#include "d/actor/d_a_npc_henna.h"
#include "d/actor/d_a_mg_fish.h"
#include "d/actor/d_a_player.h"
#include "f_op/f_op_camera_mng.h"
#include "d/d_timer.h"
#include "d/d_s_play.h"
#include "Z2AudioLib/Z2Instances.h"
#include "d/d_timer.h"
#include "dusk/version.hpp"
#include "f_op/f_op_camera_mng.h"
#if TARGET_PC
#include "dusk/gyro.h"
@@ -761,6 +762,11 @@ static void koro2_game(fshop_class* i_this) {
sp5C.x = mDoCPd_c::getStickX3D(PAD_1);
sp5C.y = 0.0f;
sp5C.z = mDoCPd_c::getStickY(PAD_1);
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
sp5C.x = -sp5C.x;
}
#endif
MtxPosition(&sp5C, &sp68);
f32 reg_f31 = sp68.x;
@@ -782,20 +788,15 @@ static void koro2_game(fshop_class* i_this) {
reg_f30 = 0.0f;
}
s16 gyro_ax = 0;
s16 gyro_az = 0;
#if TARGET_PC
if (dusk::getSettings().game.enableGyroRollgoal) {
s16 rg_add_x;
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);
dusk::gyro::rollgoalTableOffset(gyro_ax, gyro_az);
}
#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
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 (i_this->field_0x4010 != 2) {
@@ -1742,7 +1743,18 @@ static int daFshop_Create(fopAc_ac_c* actor) {
fopAcM_createChild(fpcNm_FSHOP_e, fopAcM_GetID(actor), 0xFFFFFF23, &actor->current.pos, fopAcM_GetRoomNo(actor), NULL, NULL, -1, NULL);
u8 sp10;
#if VERSION == VERSION_GCN_PAL || VERSION == VERSION_WII_PAL
#if TARGET_PC
if (dusk::version::isRegionPal()) {
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
sp10 = 2;
} else {
sp10 = 0;
}
} else {
sp10 = 1;
}
#elif VERSION == VERSION_GCN_PAL || VERSION == VERSION_WII_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
sp10 = 2;
} else {
+28 -2
View File
@@ -25,6 +25,8 @@
#include <cmath>
#include <cstring>
#include "dusk/version.hpp"
class dmg_rod_HIO_c : public JORReflexible {
public:
dmg_rod_HIO_c();
@@ -5734,10 +5736,22 @@ static void play_camera_u(dmg_rod_class* i_this) {
static int dmg_rod_Execute(dmg_rod_class* i_this) {
fopAc_ac_c* actor = &i_this->actor;
#if TARGET_PC
if (dusk::version::isPalOrAtLeastWiiR2()) {
if (dComIfGs_getPalLanguage() == 0) {
data_804BBBD4 = 2;
} else {
data_804BBBD4 = 0;
}
} else if (dusk::version::isRegionJpn()) {
data_804BBBD4 = 0;
} else {
data_804BBBD4 = 1;
}
//TODO: It seems possible that dComIfGs_getPalLanguage returns a constant value for non-PAL
// versions (causing the first block to be elided), and it's also possible that the value
// being compared against is an enum value with per-version definitions.
#if VERSION == VERSION_SHIELD_DEBUG
#elif VERSION == VERSION_SHIELD_DEBUG
if (dComIfGs_getPalLanguage() == 1) {
data_804BBBD4 = 2;
} else {
@@ -6303,7 +6317,19 @@ static int dmg_rod_Create(fopAc_ac_c* i_this) {
heap_size = 0xC9A0;
}
#if VERSION == VERSION_SHIELD_DEBUG
#if TARGET_PC
if (dusk::version::isPalOrAtLeastWiiR2()) {
if (dComIfGs_getPalLanguage() == 0) {
data_804BBBD4 = 2;
} else {
data_804BBBD4 = 0;
}
} else if (dusk::version::isRegionJpn()) {
data_804BBBD4 = 0;
} else {
data_804BBBD4 = 1;
}
#elif VERSION == VERSION_SHIELD_DEBUG
if (dComIfGs_getPalLanguage() == 1) {
data_804BBBD4 = 2;
} else {
+11 -7
View File
@@ -30,6 +30,10 @@ static char* l_arcName = "Mirror";
static char* l_arcName2 = "MR-Table";
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();
}
@@ -80,13 +84,6 @@ void dMirror_packet_c::calcMinMax() {
}
int dMirror_packet_c::entryModel(J3DModel* i_model) {
#if TARGET_PC
if (mbReset) {
mModelCount = 0;
mbReset = false;
}
#endif
if (mModelCount >= 0x40) {
return 0;
}
@@ -595,6 +592,13 @@ int daMirror_c::execute() {
return 1;
}
#if TARGET_PC
if (mPacket.mbReset) {
mPacket.mModelCount = 0;
mPacket.mbReset = false;
}
#endif
daPy_py_c* player = daPy_getLinkPlayerActorClass();
JUT_ASSERT(0, player != NULL);
+3 -3
View File
@@ -2855,7 +2855,7 @@ void* daMP_Reader(void*) {
#endif
}
static u8 daMP_ReadThreadStack[0x2000];
static u8 daMP_ReadThreadStack[DUSK_IF_ELSE(8, 0x2000)];
#if TARGET_PC
static BOOL VideoThreadCancelled;
@@ -2880,7 +2880,7 @@ static BOOL daMP_CreateReadThread(s32 param_0) {
static OSThread daMP_VideoDecodeThread;
static u8 daMP_VideoDecodeThreadStack[0x64000];
static u8 daMP_VideoDecodeThreadStack[DUSK_IF_ELSE(8, 0x64000)];
static OSMessageQueue daMP_FreeTextureSetQueue;
@@ -3132,7 +3132,7 @@ static BOOL AudioThreadCancelled;
static OSThread daMP_AudioDecodeThread;
static u8 daMP_AudioDecodeThreadStack[0x64000];
static u8 daMP_AudioDecodeThreadStack[DUSK_IF_ELSE(8, 0x64000)];
static OSMessageQueue daMP_FreeAudioBufferQueue;
+4 -4
View File
@@ -265,8 +265,8 @@ int daObjDrop_c::modeParentWait() {
mModeAction = 1;
#if TARGET_PC
mModeTimer = dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0 ? 20 : 40;
if (dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0) {
mModeTimer = dusk::getSettings().game.fastTears ? 0 : 40;
if (dusk::getSettings().game.fastTears) {
current.pos.y += 100.0f;
} else {
current.pos.y += 300.0f;
@@ -285,7 +285,7 @@ int daObjDrop_c::modeParentWait() {
case 2:
createBodyEffect();
#if TARGET_PC
mModeTimer = dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0 ? 5 : 45;
mModeTimer = dusk::getSettings().game.fastTears ? 0 : 45;
#else
mModeTimer = 45;
#endif
@@ -331,7 +331,7 @@ int daObjDrop_c::modeWait() {
case 2:
case 50:
#if TARGET_PC
if (dusk::getSettings().game.fastTears && dComIfGp_event_getMode() == 0) {
if (dusk::getSettings().game.fastTears) {
f32 player_dist = current.pos.abs(daPy_getPlayerActorClass()->current.pos);
f32 home_dist = current.pos.abs(home.pos);
+43 -1
View File
@@ -16,6 +16,10 @@
#include "f_op/f_op_camera_mng.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) {
cM3dGPla plane;
@@ -31,6 +35,27 @@ static f32 Reflect(cXyz* i_vec, cBgS_PolyInfo const& i_polyinfo, f32 i_scale) {
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() {
return m_data;
}
@@ -353,6 +378,10 @@ int daItem_c::_daItem_draw() {
return 1;
}
#if TARGET_PC
dusk::frame_interp::add_interpolation_callback(&d_a_obj_item_interp_callback, this);
#endif
if (chkDraw()) {
return DrawBase();
}
@@ -390,6 +419,9 @@ void daItem_c::procMainNormal() {
cLib_chaseF(&scale.z, mItemScale.z, step_z);
}
#if TARGET_PC
if (!dusk::getSettings().game.enableIndefiniteItemDrops) {
#endif
if (mWaitTimer == 0) {
if (mDisappearTimer == 0) {
deleteItem();
@@ -399,6 +431,9 @@ void daItem_c::procMainNormal() {
changeDraw();
}
}
#if TARGET_PC
}
#endif
mCcCyl.SetC(current.pos);
dComIfG_Ccsp()->Set(&mCcCyl);
@@ -1058,9 +1093,16 @@ int daItem_c::CountTimer() {
if (checkCountTimer()) {
if (mWaitTimer > 0) {
mWaitTimer--;
} else if (mDisappearTimer > 0) {
}
#if TARGET_PC
else if (!dusk::getSettings().game.enableIndefiniteItemDrops && mDisappearTimer > 0) {
mDisappearTimer--;
}
#else
else if (mDisappearTimer > 0) {
mDisappearTimer--;
}
#endif
}
cLib_calcTimer<u8>(&mBoomWindTgTimer);
+41
View File
@@ -11,6 +11,10 @@
#include "d/d_com_inf_game.h"
#include "d/d_path.h"
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#endif
daOptiLift_HIO_c::daOptiLift_HIO_c() {
mStopDisappearTime = 30;
mStartMoveTime = 30;
@@ -412,7 +416,44 @@ void daOptiLift_c::setNextPoint() {
mCurrentPoint = next_point;
}
#if TARGET_PC
static void daOptiLift_interp_callback(bool isSimFrame, void* pUserWork) {
daOptiLift_c* lift = static_cast<daOptiLift_c*>(pUserWork);
if (lift == NULL || lift->mpModel == NULL) {
return;
}
g_env_light.settingTevStruct(0x10, &lift->current.pos, &lift->tevStr);
g_env_light.setLightTevColorType_MAJI(lift->mpModel, &lift->tevStr);
J3DModelData* modelData = lift->mpModel->getModelData();
J3DMaterial* materialp = modelData->getMaterialNodePointer(0);
if (materialp->getTexGenBlock()->getTexMtx(1) != NULL) {
J3DTexMtxInfo* mtx_info = &materialp->getTexGenBlock()->getTexMtx(1)->getTexMtxInfo();
if (mtx_info != NULL) {
Mtx m;
C_MTXLightOrtho(m, 100.0f, -100.0f, -100.0f, 100.0f, 1.0f, 1.0f, 0.0f, 0.0f);
mDoMtx_stack_c::XrotS(0x4000);
mDoMtx_stack_c::transM(-lift->current.pos.x, -lift->current.pos.y, -lift->current.pos.z);
cMtx_concat(m, mDoMtx_stack_c::get(), mtx_info->mEffectMtx);
}
}
lift->mBtk.entry(modelData);
J3DGXColor* color = materialp->getTevKColor(1);
color->r = l_HIO.mColorR;
color->g = l_HIO.mColorG;
color->b = l_HIO.mColorB;
}
#endif
int daOptiLift_c::Draw() {
#if TARGET_PC
dusk::frame_interp::add_interpolation_callback(&daOptiLift_interp_callback, this);
#endif
g_env_light.settingTevStruct(0x10, &current.pos, &tevStr);
g_env_light.setLightTevColorType_MAJI(mpModel, &tevStr);
+6 -7
View File
@@ -369,18 +369,17 @@ JKRHeap* daPy_anmHeap_c::setAnimeHeap() {
#if !PLATFORM_WII
#if TARGET_PC
#include "dusk/dvd_asset.hpp"
using GameVersion = dusk::version::GameVersion;
static const u8* l_sightDL_get() {
static u8 buf[0x89];
static bool _ = (
dusk::LoadDolAsset(
buf,
#if VERSION == VERSION_GCN_PAL
0x803BBDA0,
#elif VERSION == VERSION_GCN_JPN
0x803B4220,
#elif VERSION == VERSION_GCN_USA
0x803BA0C0,
#endif
{
{GameVersion::GcnUsa, 0x803BA0C0},
{GameVersion::GcnPal, 0x803BBDA0},
{GameVersion::GcnJpn, 0x803B4220}
},
0x89
),
true
+17 -13
View File
@@ -1,21 +1,22 @@
#include "d/dolzel_rel.h" // IWYU pragma: keep
#include "JSystem/J2DGraph/J2DScreen.h"
#include "JSystem/J2DGraph/J2DTextBox.h"
#include "JSystem/JKernel/JKRExpHeap.h"
#include "JSystem/JKernel/JKRMemArchive.h"
#include "d/actor/d_a_title.h"
#include "d/d_com_inf_game.h"
#include "d/d_demo.h"
#include "d/d_menu_collect.h"
#include "d/d_pane_class_alpha.h"
#include "d/d_s_logo.h"
#include "d/d_s_play.h"
#include "d/d_demo.h"
#include "d/d_pane_class_alpha.h"
#include "d/d_menu_collect.h"
#include "dusk/version.hpp"
#include "f_op/f_op_msg_mng.h"
#include "f_op/f_op_overlap_mng.h"
#include "f_op/f_op_scene_mng.h"
#include "m_Do/m_Do_Reset.h"
#include "m_Do/m_Do_controller_pad.h"
#include "d/d_com_inf_game.h"
#include "JSystem/JKernel/JKRExpHeap.h"
#include "f_op/f_op_overlap_mng.h"
#include "f_op/f_op_msg_mng.h"
#include "f_op/f_op_scene_mng.h"
#include "JSystem/J2DGraph/J2DScreen.h"
#include "JSystem/JKernel/JKRMemArchive.h"
#include "JSystem/J2DGraph/J2DTextBox.h"
#include "m_Do/m_Do_graphic.h"
#ifdef TARGET_PC
@@ -49,7 +50,10 @@ static u8 const lit_3772[12] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC
using namespace dusk::version;
#define l_arcName versionSelect<const char*>({{GameVersion::GcnPal, "TitlePal"}}, "Title")
#elif VERSION == VERSION_GCN_PAL
static char const l_arcName[] = "TitlePal";
#else
static char const l_arcName[] = "Title";
@@ -59,7 +63,7 @@ daTit_HIO_c::daTit_HIO_c() {
mPSScaleX = 1.0f;
mPSScaleY = 1.0f;
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
switch (OSGetLanguage()) {
case OS_LANGUAGE_ENGLISH:
case OS_LANGUAGE_GERMAN:
+27 -20
View File
@@ -17,7 +17,8 @@ const u16 l_J_Ohana00_64TEX__height = 63;
#if TARGET_PC
#include "dusk/dvd_asset.hpp"
static u8* l_J_Ohana00_64TEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x9060, 0x800), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_J_Ohana00_64TEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9060}, {GameVersion::GcnPal, 0x9060}}, 0x800), true); return buf; }
#define l_J_Ohana00_64TEX (l_J_Ohana00_64TEX_get())
#else
#include "assets/l_J_Ohana00_64TEX.h"
@@ -113,10 +114,12 @@ static u8 l_flowerTexCoord[] = {
0x3E, 0xA7, 0x72, 0xD6, 0xBD, 0x2F, 0x46, 0xAA};
#if TARGET_PC
static u8* l_J_hana00DL_get() { static u8 buf[0x150]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x9D20, 0x150), true); return buf; }
static u8* l_J_hana00_cDL_get() { static u8 buf[0xDE]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x9E80, 0xDE), true); return buf; }
static u8* l_matDL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x9F60, 0x99), true); return buf; }
static u8* l_matLight4DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xA000, 0x99), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_J_hana00DL_get() { static u8 buf[0x150]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9D20}, {GameVersion::GcnPal, 0x9D20}}, 0x150), true); return buf; }
static u8* l_J_hana00_cDL_get() { static u8 buf[0xDE]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9E80}, {GameVersion::GcnPal, 0x9E80}}, 0xDE), true); return buf; }
static u8* l_matDL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x9F60}, {GameVersion::GcnPal, 0x9F60}}, 0x99), true); return buf; }
static u8* l_matLight4DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xA000}, {GameVersion::GcnPal, 0xA000}}, 0x99), true); return buf; }
#define l_J_hana00DL (l_J_hana00DL_get())
#define l_J_hana00_cDL (l_J_hana00_cDL_get())
#define l_matDL (l_matDL_get())
@@ -142,7 +145,8 @@ const u16 l_J_Ohana01_64128_0419TEX__height = 127;
#endif
#if TARGET_PC
static u8* l_J_Ohana01_64128_0419TEX_get() { static u8 buf[0x1000]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xA0A0, 0x1000), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_J_Ohana01_64128_0419TEX_get() { static u8 buf[0x1000]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xA0A0}, {GameVersion::GcnPal, 0xA0A0}}, 0x1000), true); return buf; }
#define l_J_Ohana01_64128_0419TEX (l_J_Ohana01_64128_0419TEX_get())
#else
#include "assets/l_J_Ohana01_64128_0419TEX.h"
@@ -274,11 +278,13 @@ static u8 l_flowerTexCoord2[] = {
0x40, 0x1B, 0x7D, 0x52, 0x3F, 0x80, 0x3F, 0x79, 0x40, 0x1B, 0x7D, 0x52, 0x3F, 0x51, 0x10, 0x6F};
#if TARGET_PC
static u8* l_J_hana01DL_get() { static u8 buf[0x138]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xB7C0, 0x138), true); return buf; }
static u8* l_J_hana01_c_00DL_get() { static u8 buf[0xDE]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xB900, 0xDE), true); return buf; }
static u8* l_J_hana01_c_01DL_get() { static u8 buf[0x128]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xB9E0, 0x128), true); return buf; }
static u8* l_mat2DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xBB20, 0x99), true); return buf; }
static u8* l_mat2Light4DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0xBBC0, 0x99), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_J_hana01DL_get() { static u8 buf[0x138]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xB7C0}, {GameVersion::GcnPal, 0xB7C0}}, 0x138), true); return buf; }
static u8* l_J_hana01_c_00DL_get() { static u8 buf[0xDE]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xB900}, {GameVersion::GcnPal, 0xB900}}, 0xDE), true); return buf; }
static u8* l_J_hana01_c_01DL_get() { static u8 buf[0x128]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xB9E0}, {GameVersion::GcnPal, 0xB9E0}}, 0x128), true); return buf; }
static u8* l_mat2DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xBB20}, {GameVersion::GcnPal, 0xBB20}}, 0x99), true); return buf; }
static u8* l_mat2Light4DL_get() { static u8 buf[0x99]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0xBBC0}, {GameVersion::GcnPal, 0xBBC0}}, 0x99), true); return buf; }
#define l_J_hana01DL (l_J_hana01DL_get())
#define l_J_hana01_c_00DL (l_J_hana01_c_00DL_get())
#define l_J_hana01_c_01DL (l_J_hana01_c_01DL_get())
@@ -699,8 +705,8 @@ void dFlower_packet_c::draw() {
if (!cLib_checkBit<u8>(sp44->m_state, 4) && !cLib_checkBit<u8>(sp44->m_state, 0x40)) {
#ifdef TARGET_PC
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);
} else
#endif
@@ -789,6 +795,10 @@ void dFlower_packet_c::draw() {
GXColor sp28;
#if AVOID_UB
sp28 = {1, 1, 1, 1};
#endif
//u8 sp26, sp25, sp24;
GXColor sp24;
sp24.r = -0.4f * temp_r29->AmbCol.r * var_f29;
@@ -854,21 +864,18 @@ void dFlower_packet_c::draw() {
if (!cLib_checkBit<u8>(sp34->m_state, 4) && cLib_checkBit<u8>(sp34->m_state, 0x40)) {
#ifdef TARGET_PC
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);
GXLoadPosMtxImm(flower_mtx, 0);
} else {
} else
#endif
{
GXLoadPosMtxImm(sp34->m_modelMtx, 0);
#ifdef TARGET_PC
}
#endif
GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0);
#if TARGET_PC
GXLoadTexObj(&mTexObj_l_J_Ohana01_64128_0419TEX, GX_TEXMAP0);
#endif
if (!cLib_checkBit<u8>(sp34->m_state, 8)) {
if (!cLib_checkBit<u8>(sp34->m_state, 0x10)) {
GXCallDisplayList(mp_Jhana01DL, m_Jhana01DL_size);
@@ -995,7 +1002,7 @@ void dFlower_packet_c::update() {
mDoMtx_stack_c::scaleM(temp_f31, temp_f31, temp_f31);
cMtx_concat(j3dSys.getViewMtx(), temp_r28, data_p->m_modelMtx);
#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
}
}
+11 -8
View File
@@ -20,8 +20,9 @@ const u16 l_M_kusa05_RGBATEX__height = 31;
#if TARGET_PC
#include "dusk/dvd_asset.hpp"
static u8* l_M_kusa05_RGBATEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x7680, 0x800), true); return buf; }
static u8* l_M_Hijiki00TEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x7E80, 0x800), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_M_kusa05_RGBATEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x7680}, {GameVersion::GcnPal, 0x7680}}, 0x800), true); return buf; }
static u8* l_M_Hijiki00TEX_get() { static u8 buf[0x800]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x7E80}, {GameVersion::GcnPal, 0x7E80}}, 0x800), true); return buf; }
#define l_M_kusa05_RGBATEX (l_M_kusa05_RGBATEX_get())
#define l_M_Hijiki00TEX (l_M_Hijiki00TEX_get())
#else
@@ -113,12 +114,14 @@ static u8 l_texCoord[160] = {
};
#if TARGET_PC
static u8* l_M_Kusa_9qDL_get() { static u8 buf[0xCB]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x8B00, 0xCB), true); return buf; }
static u8* l_M_Kusa_9q_cDL_get() { static u8 buf[0xCB]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x8BE0, 0xCB), true); return buf; }
static u8* l_M_TenGusaDL_get() { static u8 buf[0xD4]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x8CC0, 0xD4), true); return buf; }
static u8* l_Tengusa_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x8DA0, 0xA8), true); return buf; }
static u8* l_kusa9q_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x8E60, 0xA8), true); return buf; }
static u8* l_kusa9q_l4_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", 0x8F20, 0xA8), true); return buf; }
using GameVersion = dusk::version::GameVersion;
static u8* l_M_Kusa_9qDL_get() { static u8 buf[0xCB]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8B00}, {GameVersion::GcnPal, 0x8B00}}, 0xCB), true); return buf; }
static u8* l_M_Kusa_9q_cDL_get() { static u8 buf[0xCB]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8BE0}, {GameVersion::GcnPal, 0x8BE0}}, 0xCB), true); return buf; }
static u8* l_M_TenGusaDL_get() { static u8 buf[0xD4]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8CC0}, {GameVersion::GcnPal, 0x8CC0}}, 0xD4), true); return buf; }
static u8* l_Tengusa_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8DA0}, {GameVersion::GcnPal, 0x8DA0}}, 0xA8), true); return buf; }
static u8* l_kusa9q_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8E60}, {GameVersion::GcnPal, 0x8E60}}, 0xA8), true); return buf; }
static u8* l_kusa9q_l4_matDL_get() { static u8 buf[0xA8]; static bool _ = (dusk::LoadArchivedRelAsset(buf, 'AMEM', "d_a_grass.rel", {{GameVersion::GcnUsa, 0x8F20}, {GameVersion::GcnPal, 0x8F20}}, 0xA8), true); return buf; }
#define l_M_Kusa_9qDL (l_M_Kusa_9qDL_get())
#define l_M_Kusa_9q_cDL (l_M_Kusa_9q_cDL_get())
#define l_M_TenGusaDL (l_M_TenGusaDL_get())
+66
View File
@@ -800,6 +800,10 @@ void dCamera_c::updatePad() {
}
mLockLActive = 1;
#if TARGET_PC
mCamParam.mManualMode = 0;
#endif
} else {
mLockLJustActivated = 0;
mLockLActive = 0;
@@ -833,6 +837,12 @@ void dCamera_c::updatePad() {
mHoldB = mDoCPd_c::getHoldB(mPadID) ? true : false;
mTrigB = mDoCPd_c::getTrigB(mPadID) ? true : false;
#if TARGET_PC
if (mCamParam.mManualMode) {
return;
}
#endif
bool sp6B = true;
bool sp6C = true;
int temp1;
@@ -1167,6 +1177,13 @@ bool dCamera_c::Run() {
}
} else {
sp0F = (this->*engine_tbl[mCamParam.Algorythmn(mCamStyle)])(mCamStyle);
#if TARGET_PC
if (mCamParam.Algorythmn(mCamStyle) != 1) {
mCamParam.mManualMode = 0;
}
#endif
field_0x170++;
field_0x160++;
mCurCamStyleTimer++;
@@ -3078,6 +3095,11 @@ bool dCamera_c::bumpCheck(u32 i_flags) {
} else {
field_0x968 *= mMonitor.field_0xc / 5.0f;
}
#if TARGET_PC
if (!dusk::getSettings().game.freeCamera || !mCamParam.mManualMode) {
#endif
f32 tmp = field_0x96c * (mIsWolf == 1 ? 30.0f : 30.0f);
center += vec3.norm() * (tmp * globe.V().Sin());
cSGlobe globe2(vec2 - center);
@@ -3091,6 +3113,10 @@ bool dCamera_c::bumpCheck(u32 i_flags) {
vec = lin_chk1.GetCross();
}
#if TARGET_PC
}
#endif
#if DEBUG
if (mCamSetup.CheckFlag(0x8000)) {
dDbVw_Report(20, 235, " U");
@@ -4604,6 +4630,11 @@ bool dCamera_c::chaseCamera(s32 param_0) {
sp110 = mViewCache.mDirection.R();
mViewCache.mDirection.R(mViewCache.mDirection.R() + (fVar55 - mViewCache.mDirection.R()) * chase->field_0x74);
#if TARGET_PC
freeCamera();
#endif
chase->field_0x64 = mViewCache.mCenter + mViewCache.mDirection.Xyz();
mViewCache.mEye = chase->field_0x64;
@@ -7444,6 +7475,41 @@ bool dCamera_c::test2Camera(s32 param_0) {
return false;
}
#if TARGET_PC
bool dCamera_c::freeCamera() {
if (!dusk::getSettings().game.freeCamera) {
mCamParam.mManualMode = 0;
return false;
}
cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f};
f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY);
if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY != 0) {
if (!mCamParam.mManualMode) {
mCamParam.mManualMode = 1;
mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree();
mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree();
}
camMovement = camMovement.normalize();
camMovement.x *= (dusk::getSettings().game.invertCameraXAxis ? 1.0f : -1.0f) * dusk::getSettings().game.freeCameraSensitivity * 4.0f;
camMovement.y *= (dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f) * dusk::getSettings().game.freeCameraSensitivity * 4.0f;
mCamParam.freeXAngle += camMovement.x * magnitude * dusk::getSettings().game.freeCameraSensitivity;
mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity;
}
if (mCamParam.mManualMode) {
mCamParam.freeYAngle = std::clamp(mCamParam.freeYAngle, -35.0f, 60.0f);
mViewCache.mDirection.mAzimuth = cSAngle(mCamParam.freeXAngle);
mViewCache.mDirection.mInclination = cSAngle(mCamParam.freeYAngle);
mViewCache.mDirection.mRadius = std::clamp(mCamParam.freeYAngle * 15.0f, 300.0f, 10000.0f);
}
return mCamParam.mManualMode;
}
#endif
bool dCamera_c::towerCamera(s32 param_0) {
cSAngle stack_444 = cSAngle(mCamSetup.ChargeLatitude());
f32 sp224 = mCamSetup.ChargeBRatio();
+14 -1
View File
@@ -14,6 +14,8 @@
#include <cstdio>
#include <cstring>
#include "dusk/version.hpp"
dFile_info_c::dFile_info_c(JKRArchive* i_archive, u8 param_1) {
mArchive = i_archive;
field_0x22 = param_1;
@@ -169,7 +171,18 @@ void dFile_info_c::setSaveDate(dSv_save_c* i_savedata) {
OSCalendarTime time;
OSTicksToCalendarTime(i_savedata->getPlayer().getPlayerStatusB().getDateIpl(), &time);
#if (VERSION == VERSION_GCN_JPN) || (VERSION == VERSION_WII_JPN)
#if TARGET_PC
if (dusk::version::isRegionJpn()) {
sprintf(mSaveDate, "%d.%02d.%02d %02d:%02d", time.year, time.mon + 1, time.mday,
time.hour, time.min);
} else if (dusk::version::isRegionPal() && dComIfGs_getPalLanguage() != dSv_player_config_c::LANGUAGE_ENGLISH) {
sprintf(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mday, time.mon + 1, time.year, time.hour,
time.min);
} else {
sprintf(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mon + 1, time.mday, time.year, time.hour,
time.min);
}
#elif (VERSION == VERSION_GCN_JPN) || (VERSION == VERSION_WII_JPN)
sprintf(mSaveDate, "%d.%02d.%02d %02d:%02d", time.year, time.mon + 1, time.mday,
time.hour, time.min);
#elif VERSION == VERSION_GCN_PAL
+28 -4
View File
@@ -5986,7 +5986,12 @@ static void dKyr_evil_draw2(Mtx drawMtx, u8** tex) {
TGXTexObj texobj;
dKyr_set_btitex(&texobj, (ResTIMG*)tex[1]);
rot += 0.7f;
#if TARGET_PC
if (dusk::frame_interp::get_ui_tick_pending())
#endif
{
rot += 0.7f;
}
MTXRotRad(rotMtx, 'Z', DEG_TO_RAD(rot));
MTXConcat(camMtx, rotMtx, camMtx);
@@ -6037,7 +6042,14 @@ static void dKyr_evil_draw2(Mtx drawMtx, u8** tex) {
sp34.y = 80.0f;
sp34.z = 80.0f;
mDoLib_project(&sp7C, &proj);
if (!(proj.x > -sp34.x) || !(proj.x < (FB_WIDTH + sp34.x)) ||
#if TARGET_PC
f32 cullMinX = mDoGph_gInf_c::getSafeMinXF() - sp34.x;
f32 cullMaxX = mDoGph_gInf_c::getSafeMinXF() + mDoGph_gInf_c::getSafeWidthF() + sp34.x;
#else
f32 cullMinX = -sp34.x;
f32 cullMaxX = FB_WIDTH + sp34.x;
#endif
if (!(proj.x > cullMinX) || !(proj.x < cullMaxX) ||
!(proj.y > -sp34.y) || !(proj.y < (458.0f + sp34.z)))
{
continue;
@@ -6208,7 +6220,12 @@ void dKyr_evil_draw(Mtx drawMtx, u8** tex) {
TGXTexObj texobj;
dKyr_set_btitex(&texobj, (ResTIMG*)tex[0]);
rot += 1.0f;
#if TARGET_PC
if (dusk::frame_interp::get_ui_tick_pending())
#endif
{
rot += 1.0f;
}
MTXRotRad(rotMtx, 'Z', DEG_TO_RAD(rot));
MTXConcat(camMtx, rotMtx, camMtx);
@@ -6262,7 +6279,14 @@ void dKyr_evil_draw(Mtx drawMtx, u8** tex) {
sp44.z = 80.0f;
mDoLib_project(&spA4, &proj);
if (!(proj.x > -sp44.x) || !(proj.x < (FB_WIDTH + sp44.x)) ||
#if TARGET_PC
f32 cullMinX = mDoGph_gInf_c::getSafeMinXF() - sp44.x;
f32 cullMaxX = mDoGph_gInf_c::getSafeMinXF() + mDoGph_gInf_c::getSafeWidthF() + sp44.x;
#else
f32 cullMinX = -sp44.x;
f32 cullMaxX = FB_WIDTH + sp44.x;
#endif
if (!(proj.x > cullMinX) || !(proj.x < cullMaxX) ||
!(proj.y > -sp44.y) || !(proj.y < (458.0f + sp44.z)))
{
continue;
+7
View File
@@ -237,6 +237,13 @@ void dDrawPath_c::rendering(dDrawPath_c::line_class const* p_line) {
if (isDrawType(p_line->field_0x0)) {
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) {
GXSetLineWidth(width, GX_TO_ZERO);
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;
}
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) {
f32 temp = 450.0f;
ANGLE_ADD(mLinkAngle, temp);
+42 -1
View File
@@ -21,6 +21,7 @@
#include "d/d_msg_string.h"
#include "d/d_meter_haihai.h"
#include "d/d_menu_window.h"
#include "dusk/settings.h"
#include "f_op/f_op_msg_mng.h"
#include "m_Do/m_Do_graphic.h"
#include <cstring>
@@ -945,9 +946,15 @@ void dMenu_DmapBg_c::draw() {
mpMeterHaihai->drawHaihai(field_0xdda,
x1 + (local_224.x + local_218.x) / 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));
#if TARGET_PC
if (!dusk::getSettings().game.enableFrameInterpolation) {
field_0xdda = 0;
}
#else
field_0xdda = 0;
#endif
}
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);
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);
#endif
}
}
@@ -2545,6 +2581,11 @@ void dMenu_Dmap_c::zoomIn_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);
mMapCtrl->initZoomOut(10);
mpDrawBg->iconScaleAnmInit(1.0f, 0.0f, 10);
+5 -3
View File
@@ -16,6 +16,8 @@
#include "m_Do/m_Do_graphic.h"
#include <cstring>
#include "dusk/version.hpp"
typedef void (dMenu_Fishing_c::*initFunc)();
initFunc map_init_process[] = {
&dMenu_Fishing_c::wait_init,
@@ -135,9 +137,9 @@ bool dMenu_Fishing_c::isSync() {
}
void dMenu_Fishing_c::init() {
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
BOOL isEnglish = FALSE;
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH) {
if (dusk::version::isRegionUsa() || (dusk::version::isRegionPal() && dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_ENGLISH)) {
isEnglish = TRUE;
}
#endif
@@ -145,7 +147,7 @@ void dMenu_Fishing_c::init() {
for (int i = 0; i < MAX_FINDABLE_FISHES; i++) {
if (dComIfGs_getFishNum(i) != 0) {
// Fish has been caught once, display it along with it's params
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (isEnglish) {
setFishParam(i, dComIfGs_getFishNum(i), dComIfGs_getFishSize(i) / 2.54f);
} else {
+75
View File
@@ -185,6 +185,17 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i
field_0x682 = 0xc000;
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++) {
field_0x674[i] = 0;
#if TARGET_PC
@@ -631,7 +642,71 @@ void dMenu_Ring_c::_draw() {
} else {
drawSelectItem();
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();
#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->draw((J2DOrthoGraph*)grafPort);
drawFlag0();
+15 -1
View File
@@ -368,8 +368,22 @@ void dMeterButton_c::draw() {
}
if (var_r3) {
#ifdef TARGET_PC
if (dusk::frame_interp::get_ui_tick_pending()) {
mWasListen[i] = var_r22;
mWasRepeat[i] = var_r23;
} else {
var_r22 = mWasListen[i];
var_r23 = mWasRepeat[i];
}
#endif
if (var_r22) {
if (field_0x2e8[i] == 18.0f) {
#ifdef TARGET_PC
if (field_0x2e8[i] == 18.0f && dusk::frame_interp::get_ui_tick_pending())
#else
if (field_0x2e8[i] == 18.0f)
#endif
{
mDoAud_seStart(Z2SE_SY_HINT_BUTTON_BLINK, NULL, 0, 0);
}
+14 -7
View File
@@ -307,7 +307,7 @@ static u8 getOutFontNumberType(int param_0) {
}
}
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
static void setPlayerName(char* i_player_name, u8 param_2) {
if (param_2 != 0) {
strcpy(i_player_name, dComIfGs_getPlayerName());
@@ -1485,7 +1485,7 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz
char buffer[40];
switch (i_tag & 0xFF00FFFF) {
case MSGTAG_PLAYER_GENITIV:
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_GERMAN) {
setPlayerName(buffer, 1);
} else {
@@ -1495,7 +1495,7 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz
push_word(buffer);
return true;
case MSGTAG_HORSE_GENITIV:
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_GERMAN) {
setHorseName(buffer, 1);
} else {
@@ -1987,6 +1987,13 @@ bool jmessage_tSequenceProcessor::do_isReady() {
}
#endif
#if TARGET_PC
if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) {
field_0xb2 = 1;
pReference->setSendTimer(0);
}
#endif
if (dComIfGp_checkMesgBgm()) {
bool isItemMusicPlaying = true;
if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET &&
@@ -4263,7 +4270,7 @@ bool jmessage_string_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u3
char buffer[40];
switch (i_tag & 0xFF00FFFF) {
case MSGTAG_PLAYER_GENITIV:
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_GERMAN) {
setPlayerName(buffer, 1);
} else {
@@ -4274,7 +4281,7 @@ bool jmessage_string_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u3
stack_pushCurrent(buffer);
break;
case MSGTAG_HORSE_GENITIV:
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_GERMAN) {
setHorseName(buffer, 1);
} else {
@@ -4871,7 +4878,7 @@ bool jmessage_string_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data,
char buffer[40];
switch (i_tag & 0xFF00FFFF) {
case MSGTAG_PLAYER_GENITIV:
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_GERMAN) {
setPlayerName(buffer, 1);
} else {
@@ -4882,7 +4889,7 @@ bool jmessage_string_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data,
push_word(buffer);
break;
case MSGTAG_HORSE_GENITIV:
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
if (dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_GERMAN) {
setHorseName(buffer, 1);
} else {
+27 -2
View File
@@ -24,9 +24,11 @@
#include "f_op/f_op_msg_mng.h"
#include <cstdio>
#include <cstring>
#include "JSystem/JKernel/JKRExpHeap.h"
#include "dusk/version.hpp"
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_lib.h"
#include "JSystem/JKernel/JKRExpHeap.h"
#if TARGET_PC
#include "dusk/settings.h"
@@ -1643,7 +1645,30 @@ void dMsgObject_c::readMessageGroupLocal(mDoDvdThd_mountXArchive_c** p_arcMount)
#else
#if TARGET_PC
// Original game UB
snprintf(arcName, sizeof(arcName), "/res/Msgus/bmgres%d.arc", msgGroup);
if (dusk::version::isRegionPal()) {
switch (dComIfGs_getPalLanguage()) {
case dSv_player_config_c::LANGUAGE_GERMAN:
snprintf(arcName, sizeof(arcName), "/res/Msgde/bmgres%d.arc", msgGroup);
break;
case dSv_player_config_c::LANGUAGE_FRENCH:
snprintf(arcName, sizeof(arcName), "/res/Msgfr/bmgres%d.arc", msgGroup);
break;
case dSv_player_config_c::LANGUAGE_SPANISH:
snprintf(arcName, sizeof(arcName), "/res/Msgsp/bmgres%d.arc", msgGroup);
break;
case dSv_player_config_c::LANGUAGE_ITALIAN:
snprintf(arcName, sizeof(arcName), "/res/Msgit/bmgres%d.arc", msgGroup);
break;
default:
snprintf(arcName, sizeof(arcName), "/res/Msguk/bmgres%d.arc", msgGroup);
}
} else if (dusk::version::isRegionJpn()) {
snprintf(arcName, sizeof(arcName), "/res/Msgjp/bmgres%d.arc", msgGroup);
} else {
snprintf(arcName, sizeof(arcName), "/res/Msgus/bmgres%d.arc", msgGroup);
}
#else
sprintf(arcName, "/res/Msgus/bmgres%d.arc", msgGroup);
#endif
+9 -3
View File
@@ -20,6 +20,7 @@
#include "Z2AudioLib/Z2WolfHowlMgr.h"
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#include "dusk/settings.h"
#endif
@@ -580,9 +581,14 @@ void dMsgScrnHowl_c::drawWave() {
f17 = local_60;
f18 = local_64;
} else {
field_0x2134++;
if (field_0x2134 > 30) {
field_0x2134 = 0;
#if TARGET_PC
if (dusk::frame_interp::get_ui_tick_pending())
#endif
{
field_0x2134++;
if (field_0x2134 > 30) {
field_0x2134 = 0;
}
}
if (field_0x2134 < 15) {
local_dc = field_0x2134 / 15.0f;
+2 -2
View File
@@ -334,11 +334,11 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) {
vals[1] = ((dMsgUnit_inf1_section_t*)pInfoBlock)->entries[i_type].endFrame;
#endif
#if REGION_PAL
#if TARGET_PC || REGION_PAL
if (i_value == 1 ||
(dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_FRENCH &&
i_value == 0)) {
#elif !REGION_USA
#elif !REGION_USA // Dusk TODO: What? This checks for Spanish when *not* PAL or USA?
if (i_value == 1 ||
(dComIfGs_getPalLanguage() == dSv_player_config_c::LANGUAGE_SPANISH &&
i_value == 0)) {
+300 -99
View File
@@ -8,9 +8,15 @@
#include "m_Do/m_Do_controller_pad.h"
#include <cstdio>
#include <cstring>
#include "JSystem/J2DGraph/J2DAnmLoader.h"
#include "dusk/version.hpp"
#include "f_op/f_op_msg_mng.h"
static bool isPalOrJpn() {
return dusk::version::isRegionPal() || dusk::version::isRegionJpn();
}
static const char* l_mojiHira[65] = {
"", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "",
@@ -66,15 +72,31 @@ static const char* l_mojiEisu[65] = {
"X", "k", "x", ",", "L", "Y", "l", "y", ".", "M", "Z", "m", "z", " ",
};
#if REGION_PAL
static char* l_mojiEisuPal_1[65] = {
#if TARGET_PC
// The game normally mutates this string list to fill in the real character codes.
// That can't work on a modern platform, so instead I've filled them out ahead of time.
static const char* l_mojiEisuPal_1[65] = {
"A", "N", "\xC0", "\xCF", "1", "B", "O", "\xC1", "\xD0", "2", "C", "P", "\xC2", "\xD1", "3", "D", "Q",
"\xC3", "\xD2", "4", "E", "R", "\xC4", "\xD3", "5", "F", "S", "\xC5", "\xD4", "6", "G", "T", "\xC6", "\xD5",
"7", "H", "U", "\xC7", "\xD6", "8", "I", "V", "\xC8", "\xD7", "9", "J", "W", "\xC9", "\xD8", "0", "K",
"X", "\xCA", "\xD9", ",", "L", "Y", "\xCB", "\xDA", ".", "M", "Z", "\xCC", "\xDB", " ",
};
static const char* l_mojiEisuPal_2[65] = {
"a", "n", "\xE0", "\xEF", "1", "b", "o", "\xE1", "\xF0", "2", "c", "p", "\xE2", "\xF1", "3", "d", "q",
"\xE3", "\xF2", "4", "e", "r", "\xE4", "\xF3", "5", "f", "s", "\xE5", "\xF4", "6", "g", "t", "\xE6",
"\xF5", "7", "h", "u", "\xE7", "\xF6", "8", "i", "v", "\xE8", "\xF7", "9", "j", "w", "\xE9", "\xF8", "0",
"k", "x", "\xEA", "\xF9", ",", "l", "y", "\xEB", "\xFA", ".", "m", "z", "\xEC", "\xFB", " ",
};
#elif REGION_PAL
static const char* l_mojiEisuPal_1[65] = {
"A", "N", "AA", "BB", "1", "B", "O", "CC", "DD", "2", "C", "P", "EE", "FF", "3", "D", "Q",
"GG", "HH", "4", "E", "R", "II", "JJ", "5", "F", "S", "KK", "LL", "6", "G", "T", "MM", "NN",
"7", "H", "U", "OO", "PP", "8", "I", "V", "QQ", "RR", "9", "J", "W", "SS", "TT", "0", "K",
"X", "UU", "VV", ",", "L", "Y", "WW", "XX", ".", "M", "Z", "YY", "ZZ", " ",
};
static char* l_mojiEisuPal_2[65] = {
static const char* l_mojiEisuPal_2[65] = {
"a", "n", "aa", "bb", "1", "b", "o", "cc", "dd", "2", "c", "p", "ee", "ff", "3", "d", "q",
"gg", "hh", "4", "e", "r", "ii", "jj", "5", "f", "s", "kk", "ll", "6", "g", "t", "mm",
"nn", "7", "h", "u", "oo", "pp", "8", "i", "v", "qq", "rr", "9", "j", "w", "ss", "tt", "0",
@@ -82,6 +104,16 @@ static char* l_mojiEisuPal_2[65] = {
};
#endif
#if TARGET_PC
// ' ' (full-width space)
#define SPACE_MAYBE_FULL (dusk::version::isRegionJpn() ? '\x81\x40' : ' ')
#elif REGION_JPN
// ' ' (full-width space)
#define SPACE_MAYBE_FULL '\x81\x40'
#else
#define SPACE_MAYBE_FULL ' '
#endif
static dNm_HIO_c g_nmHIO;
typedef void (dName_c::*selProcFunc)(void);
@@ -163,13 +195,18 @@ void dName_c::init() {
field_0x2ac = mSelProc;
field_0x2ad = mSelProc;
field_0x2ae = field_0x2ac;
#if REGION_PAL || REGION_JPN
#if TARGET_PC
mMojiSet = isPalOrJpn() ? MOJI_HIRA : MOJI_EIGO;
#elif REGION_PAL || REGION_JPN
mMojiSet = MOJI_HIRA;
#else
mMojiSet = MOJI_EIGO;
#endif
mPrevMojiSet = 255;
#if REGION_PAL || REGION_JPN
#if TARGET_PC
mSelMenu = isPalOrJpn() ? MENU_HIRA : MENU_END;
mPrevSelMenu = isPalOrJpn() ? MENU_HIRA : MENU_END;
#elif REGION_PAL || REGION_JPN
mSelMenu = MENU_HIRA;
mPrevSelMenu = MENU_HIRA;
#else
@@ -187,7 +224,8 @@ void dName_c::initial() {
mNextNameStr[0] = 0;
}
#if REGION_PAL || REGION_JPN
#if TARGET_PC || REGION_PAL || REGION_JPN
IF_DUSK_BLOCK(isPalOrJpn())
if (mSelProc == PROC_MOJI_SELECT) {
mMenuIcon[mMojiSet]->scale(g_nmHIO.mMenuScale, g_nmHIO.mMenuScale);
mMenuText[mMojiSet]->setWhite(JUtility::TColor(0xC8, 0xC8, 0xC8, 0xFF));
@@ -196,6 +234,7 @@ void dName_c::initial() {
mMenuText[mPrevMojiSet]->setWhite(JUtility::TColor(0x96, 0x96, 0x96, 0xFF));
}
}
IF_DUSK_BLOCK_END
#endif
}
@@ -230,8 +269,8 @@ void dName_c::_move() {
stick->checkTrigger();
(this->*SelProc[mSelProc])();
#if REGION_PAL || REGION_JPN
if (mDoCPd_c::getTrigY(PAD_1)) {
#if TARGET_PC || REGION_PAL || REGION_JPN
if (IF_DUSK(isPalOrJpn() &&) mDoCPd_c::getTrigY(PAD_1)) {
mDoAud_seStart(Z2SE_SY_DUMMY, 0, 0, 0);
mPrevMojiSet = mMojiSet;
mMojiSet++;
@@ -245,8 +284,8 @@ void dName_c::_move() {
mojiListChange();
} else {
#endif
#if REGION_JPN
if (mDoCPd_c::getTrigX(PAD_1)) {
#if TARGET_PC || REGION_JPN
if (IF_DUSK(dusk::version::isRegionJpn() &&) mDoCPd_c::getTrigX(PAD_1)) {
if (mCurPos != 0) {
if (mojiChange(mCurPos - 1) == 1) {
mDoAud_seStart(Z2SE_SY_DUMMY, 0, 0, 0);
@@ -283,12 +322,14 @@ void dName_c::_move() {
backSpace();
}
} else if (mDoCPd_c::getTrigStart(PAD_1)) {
#define EIGO_OR_END DUSK_IF_ELSE((dusk::version::isRegionPal() ? MENU_EIGO : MENU_END), MENU_END)
#if REGION_PAL
if ((mSelProc != PROC_MENU_SELECT || mSelMenu != MENU_EIGO) &&
(mSelProc == PROC_MENU_SELECT || mSelProc == PROC_MOJI_SELECT))
{
#else
if ((mSelProc != PROC_MENU_SELECT || mSelMenu != MENU_END) &&
if ((mSelProc != PROC_MENU_SELECT || mSelMenu != EIGO_OR_END) &&
(mSelProc == PROC_MENU_SELECT || mSelProc == PROC_MOJI_SELECT))
{
#endif
@@ -297,7 +338,7 @@ void dName_c::_move() {
#if REGION_PAL
mSelMenu = MENU_EIGO;
#else
mSelMenu = MENU_END;
mSelMenu = EIGO_OR_END;
#endif
switch (mSelProc) {
@@ -314,10 +355,10 @@ void dName_c::_move() {
}
}
}
#if REGION_JPN
#if TARGET_PC || REGION_JPN
}
#endif
#if REGION_PAL || REGION_JPN
#if TARGET_PC || REGION_PAL || REGION_JPN
}
#endif
@@ -327,10 +368,9 @@ void dName_c::_move() {
int dName_c::nameCheck() {
for (int i = 8, len = 7; i > 0; i--) {
#if REGION_JPN
// ' ' (full-width space)
if (mChrInfo[len].mCharacter != ' ' && mChrInfo[len].mCharacter != '\x81\x40') {
#else
if (mChrInfo[len].mCharacter != ' ') {
if (mChrInfo[len].mCharacter != ' ' IF_DUSK(&& (!dusk::version::isRegionJpn() || mChrInfo[len].mCharacter != '\x81\x40'))) {
#endif
return len + 1;
}
@@ -344,8 +384,8 @@ void dName_c::playNameSet(int nameLength) {
char* str = mInputStr;
for (int i = 0; i < nameLength; i++) {
#if REGION_JPN
if (mChrInfo[i].mMojiSet == 2) {
#if TARGET_PC || REGION_JPN
if (!dusk::version::isRegionJpn() || mChrInfo[i].mMojiSet == 2) {
*str = mChrInfo[i].mCharacter;
str += 1;
} else {
@@ -727,7 +767,34 @@ int dName_c::getMoji() {
int result = -1;
const char* moji;
#if REGION_PAL
#if TARGET_PC
if (dusk::version::isRegionPal()) {
switch (mMojiSet) {
case MOJI_HIRA:
moji = l_mojiEisuPal_1[mCharRow + mCharColumn * 5];
break;
case MOJI_KATA:
moji = l_mojiEisuPal_2[mCharRow + mCharColumn * 5];
break;
default:
abort();
}
} else {
switch (mMojiSet) {
case MOJI_HIRA:
moji = l_mojiHira[mCharRow + mCharColumn * 5];
break;
case MOJI_KATA:
moji = l_mojikata[mCharRow + mCharColumn * 5];
break;
case MOJI_EIGO:
moji = l_mojiEisu[mCharRow + mCharColumn * 5];
break;
default:
abort();
}
}
#elif REGION_PAL
switch (mMojiSet) {
case MOJI_HIRA:
moji = l_mojiEisuPal_1[mCharRow + mCharColumn * 5];
@@ -750,7 +817,17 @@ int dName_c::getMoji() {
}
#endif
#if REGION_JPN
#if TARGET_PC
if (dusk::version::isRegionJpn()) {
if (*(u8*)moji >> 4 == 0x8 || *(u8*)moji >> 4 == 0x9) {
result = *(u16*)moji;
} else {
result = *moji;
}
} else {
result = *moji;
}
#elif REGION_JPN
if (*(u8*)moji >> 4 == 0x8 || *(u8*)moji >> 4 == 0x9) {
result = *(u16*)moji;
} else {
@@ -763,6 +840,14 @@ int dName_c::getMoji() {
return result;
}
#if TARGET_PC
#define CHAR_TRUNC(val) (dusk::version::isRegionPal() ? val & 0xFF : val)
#elif REGION_PAL
#define CHAR_TRUNC(val) (val & 0xFF)
#else
#define CHAR_TRUNC(val) val
#endif
void dName_c::setMoji(int moji) {
if (mCurPos == 8 || nameCheck() == 8) {
mDoAud_seStart(Z2SE_SYS_ERROR, NULL, 0, 0);
@@ -771,24 +856,14 @@ void dName_c::setMoji(int moji) {
s32 notEmpty = false;
for (int i = mCurPos; i < 8; i++) {
#if REGION_JPN
// ' ' (full-width space)
if (mChrInfo[i].mCharacter != '\x81\x40') {
#else
if (mChrInfo[i].mCharacter != ' ') {
#endif
if (mChrInfo[i].mCharacter != SPACE_MAYBE_FULL) {
notEmpty = true;
break;
}
}
if (notEmpty) {
#if REGION_JPN
// ' ' (full-width space)
if (mChrInfo[7].mCharacter == '\x81\x40') {
#else
if (mChrInfo[7].mCharacter == ' ') {
#endif
if (mChrInfo[7].mCharacter == SPACE_MAYBE_FULL) {
for (int i = 6; i >= mCurPos; i--) {
mChrInfo[i + 1] = mChrInfo[i];
}
@@ -797,11 +872,7 @@ void dName_c::setMoji(int moji) {
mChrInfo[mCurPos].mRow = mCharRow;
mChrInfo[mCurPos].mMojiSet = mMojiSet;
mChrInfo[mCurPos].field_0x3 = 1;
#if REGION_PAL
mChrInfo[mCurPos].mCharacter = moji & 0xFF;
#else
mChrInfo[mCurPos].mCharacter = moji;
#endif
mChrInfo[mCurPos].mCharacter = CHAR_TRUNC(moji);
if (mCurPos != 8) {
mLastCurPos = mCurPos;
@@ -814,11 +885,7 @@ void dName_c::setMoji(int moji) {
mChrInfo[mCurPos].mRow = mCharRow;
mChrInfo[mCurPos].mMojiSet = mMojiSet;
mChrInfo[mCurPos].field_0x3 = 1;
#if REGION_PAL
mChrInfo[mCurPos].mCharacter = moji & 0xFF;
#else
mChrInfo[mCurPos].mCharacter = moji;
#endif
mChrInfo[mCurPos].mCharacter = CHAR_TRUNC(moji);
if (mCurPos != 8) {
mLastCurPos = mCurPos;
@@ -844,13 +911,8 @@ void dName_c::setNameText() {
"CR\x1b"
"CC[000000]\x1bGM[0]%c\x1bHM\x1b"
"CC[ffffff]\x1bGM[0]%c",
#if REGION_PAL
(u8)mChrInfo[i].mCharacter & 0xFF,
(u8)mChrInfo[i].mCharacter & 0xFF
#else
(u8)mChrInfo[i].mCharacter,
(u8)mChrInfo[i].mCharacter
#endif
CHAR_TRUNC((u8)mChrInfo[i].mCharacter),
CHAR_TRUNC((u8)mChrInfo[i].mCharacter)
);
#if REGION_JPN
} else {
@@ -889,7 +951,29 @@ void dName_c::nameCursorMove() {
void dName_c::selectCursorMove() {
int idx;
#if REGION_PAL
#if TARGET_PC
if (dusk::version::isRegionPal()) {
if (mCharColumn < 3) {
idx = 0;
} else if (mCharColumn < 6) {
idx = 1;
} else if (mCharColumn >= 6) {
idx = 2;
}
} else if (dusk::version::isRegionJpn()) {
if (mCharColumn < 3) {
idx = 0;
} else if (mCharColumn < 6) {
idx = 1;
} else if (mCharColumn < 8) {
idx = 2;
} else if (mCharColumn >= 8) {
idx = 3;
}
} else {
idx = 3;
}
#elif REGION_PAL
if (mCharColumn < 3) {
idx = 0;
} else if (mCharColumn < 6) {
@@ -930,7 +1014,36 @@ void dName_c::selectCursorMove() {
void dName_c::menuCursorPosSet() {
mPrevSelMenu = mSelMenu;
#if REGION_PAL
#if TARGET_PC
if (dusk::version::isRegionPal()) {
if (mCharColumn < 3) {
mSelMenu = MENU_HIRA;
} else if (mCharColumn < 6) {
mSelMenu = MENU_KATA;
} else if (mCharColumn >= 6) {
mSelMenu = MENU_EIGO;
}
} else if (dusk::version::isRegionJpn()) {
if (mCharColumn < 3) {
mSelMenu = MENU_HIRA;
return;
}
if (mCharColumn < 6) {
mSelMenu = MENU_KATA;
return;
}
if (mCharColumn < 8) {
mSelMenu = MENU_EIGO;
return;
}
if (mCharColumn >= 8) {
mSelMenu = MENU_END;
return;
}
} else {
mSelMenu = MENU_END;
}
#elif REGION_PAL
if (mCharColumn < 3) {
mSelMenu = MENU_HIRA;
} else if (mCharColumn < 6) {
@@ -961,28 +1074,28 @@ void dName_c::menuCursorPosSet() {
}
void dName_c::MenuSelect() {
#if REGION_PAL || REGION_JPN
if (stick->checkRightTrigger()) {
#if TARGET_PC || REGION_PAL || REGION_JPN
if (isPalOrJpn() && stick->checkRightTrigger()) {
mDoAud_seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0);
mPrevSelMenu = mSelMenu;
mSelMenu++;
#if REGION_PAL
if (mSelMenu > MENU_EIGO) {
#else
if (mSelMenu > MENU_END) {
if (mSelMenu > EIGO_OR_END) {
#endif
mSelMenu = MENU_HIRA;
}
MenuSelectAnmInit();
mSelProc = PROC_MENU_SEL_ANM;
} else if (stick->checkLeftTrigger()) {
} else if (isPalOrJpn() && stick->checkLeftTrigger()) {
mDoAud_seStart(Z2SE_SY_CURSOR_OPTION, NULL, 0, 0);
mPrevSelMenu = mSelMenu;
if (mSelMenu == MENU_HIRA) {
#if REGION_JPN
mSelMenu = MENU_END;
#else
mSelMenu = MENU_EIGO;
mSelMenu = dusk::version::isRegionJpn() ? MENU_END : MENU_EIGO;
#endif
} else {
mSelMenu--;
@@ -1009,7 +1122,7 @@ void dName_c::MenuSelect() {
#if REGION_PAL
if (mSelMenu == MENU_EIGO) {
#else
if (mSelMenu == MENU_END) {
if (mSelMenu == EIGO_OR_END) {
#endif
if (nameCheck() != 0) {
mDoAud_seStart(Z2SE_SY_NAME_OK, NULL, 0, 0);
@@ -1024,7 +1137,7 @@ void dName_c::MenuSelect() {
#if REGION_PAL
if (mSelMenu == MENU_EIGO) {
#else
if (mSelMenu == MENU_END) {
if (mSelMenu == EIGO_OR_END) {
#endif
if (nameCheck() != 0) {
mDoAud_seStart(Z2SE_SY_NAME_OK, NULL, 0, 0);
@@ -1067,9 +1180,11 @@ void dName_c::MenuSelectAnm2() {
if (canMove == true) {
if (prevMenu_i != mojiSet_i) {
mMenuText[prevMenu_i]->setWhite(JUtility::TColor(0x96, 0x96, 0x96, 0xFF));
#if REGION_PAL || REGION_JPN
#if TARGET_PC || REGION_PAL || REGION_JPN
IF_DUSK_BLOCK(isPalOrJpn())
mMenuIcon[mojiSet_i]->scale(g_nmHIO.mMenuScale, g_nmHIO.mMenuScale);
mMenuText[mojiSet_i]->setWhite(JUtility::TColor(0xC8, 0xC8, 0xC8, 0xFF));
IF_DUSK_BLOCK_END
#endif
}
selectCursorMove();
@@ -1081,6 +1196,11 @@ void dName_c::MenuSelectAnm2() {
void dName_c::MenuSelectAnm3() {}
void dName_c::menuAbtnSelect() {
#if TARGET_PC
if (dusk::version::isRegionPal() && mSelMenu == MENU_EIGO) {
goto pal_eigo;
}
#endif
switch (mSelMenu) {
case MENU_HIRA:
case MENU_KATA:
@@ -1098,6 +1218,7 @@ void dName_c::menuAbtnSelect() {
#else
case MENU_END:
#endif
IF_DUSK(pal_eigo:)
int nameNum = nameCheck();
if (nameNum != 0) {
playNameSet(nameNum);
@@ -1116,44 +1237,33 @@ void dName_c::backSpace() {
if (mCurPos != 0) {
mDoAud_seStart(Z2SE_SY_NAME_DELETE, NULL, 0, 0);
#if REGION_JPN
// ' ' (full-width space)
if (mCurPos == 8 && mChrInfo[7].mCharacter != '\x81\x40') {
#else
if (mCurPos == 8 && mChrInfo[7].mCharacter != ' ') {
#endif
if (mCurPos == 8 && mChrInfo[7].mCharacter != SPACE_MAYBE_FULL) {
mChrInfo[7].mColumn = 7;
mChrInfo[7].mRow = 1;
#if REGION_PAL || REGION_JPN
#if TARGET_PC
mChrInfo[7].mMojiSet = isPalOrJpn() ? MOJI_HIRA : MOJI_EIGO;
#elif REGION_PAL || REGION_JPN
mChrInfo[7].mMojiSet = MOJI_HIRA;
#else
mChrInfo[7].mMojiSet = MOJI_EIGO;
#endif
mChrInfo[7].field_0x3 = 1;
#if REGION_JPN
// ' ' (full-width space)
mChrInfo[7].mCharacter = '\x81\x40';
#else
mChrInfo[7].mCharacter = ' ';
#endif
mChrInfo[7].mCharacter = SPACE_MAYBE_FULL;
} else {
for (int i = mCurPos - 1; i < 7; i++) {
mChrInfo[i] = mChrInfo[i + 1];
}
mChrInfo[7].mColumn = 7;
mChrInfo[7].mRow = 1;
#if REGION_PAL || REGION_JPN
#if TARGET_PC
mChrInfo[7].mMojiSet = isPalOrJpn() ? MOJI_HIRA : MOJI_EIGO;
#elif REGION_PAL || REGION_JPN
mChrInfo[7].mMojiSet = MOJI_HIRA;
#else
mChrInfo[7].mMojiSet = MOJI_EIGO;
#endif
mChrInfo[7].field_0x3 = 1;
#if REGION_JPN
// ' ' (full-width space)
mChrInfo[7].mCharacter = '\x81\x40';
#else
mChrInfo[7].mCharacter = ' ';
#endif
mChrInfo[7].mCharacter = SPACE_MAYBE_FULL;
}
setNameText();
@@ -1164,7 +1274,31 @@ void dName_c::backSpace() {
}
void dName_c::mojiListChange() {
#if REGION_PAL
#if TARGET_PC
const char** mojiSet;
if (dusk::version::isRegionPal()) {
switch (mMojiSet) {
case MOJI_HIRA:
mojiSet = l_mojiEisuPal_1;
break;
case MOJI_KATA:
mojiSet = l_mojiEisuPal_2;
break;
}
} else {
switch (mMojiSet) {
case MOJI_HIRA:
mojiSet = l_mojiHira;
break;
case MOJI_KATA:
mojiSet = l_mojikata;
break;
case MOJI_EIGO:
mojiSet = l_mojiEisu;
break;
}
}
#elif REGION_PAL
char** mojiSet;
switch (mMojiSet) {
@@ -1212,7 +1346,8 @@ void dName_c::mojiListChange() {
strcpy(mMojiText[i], buf);
}
#if REGION_PAL || REGION_JPN
#if TARGET_PC || REGION_PAL || REGION_JPN
IF_DUSK_BLOCK(isPalOrJpn())
if (mSelProc == PROC_MOJI_SELECT) {
mMenuIcon[mMojiSet]->scale(g_nmHIO.mMenuScale, g_nmHIO.mMenuScale);
mMenuText[mMojiSet]->setWhite(JUtility::TColor(0xC8, 0xC8, 0xC8, 0xFF));
@@ -1221,6 +1356,7 @@ void dName_c::mojiListChange() {
mMenuText[mPrevMojiSet]->setWhite(JUtility::TColor(0x96, 0x96, 0x96, 0xFF));
}
}
IF_DUSK_BLOCK_END
#endif
}
@@ -1241,9 +1377,11 @@ void dName_c::menuCursorMove2() {
if (menu_i != mojiSet_i) {
mMenuIcon[menu_i]->scale(g_nmHIO.mMenuScale, g_nmHIO.mMenuScale);
mMenuText[menu_i]->setWhite(JUtility::TColor(0xC8, 0xC8, 0xC8, 0xFF));
#if REGION_PAL || REGION_JPN
#if TARGET_PC || REGION_PAL || REGION_JPN
IF_DUSK_BLOCK(isPalOrJpn())
mMenuIcon[mojiSet_i]->scale(1.0f, 1.0f);
mMenuText[mojiSet_i]->setWhite(JUtility::TColor(0x96, 0x96, 0x96, 0xFF));
IF_DUSK_BLOCK_END
#endif
}
@@ -1265,7 +1403,9 @@ void dName_c::selectCursorPosSet(int row) {
mCharColumn = 3;
break;
case MENU_EIGO:
#if REGION_PAL
#if TARGET_PC
mCharColumn = dusk::version::isRegionPal() ? 8 : 6;
#elif REGION_PAL
mCharColumn = 8;
#else
mCharColumn = 6;
@@ -1481,9 +1621,11 @@ void dName_c::screenSet() {
#endif
}
#if !(REGION_PAL || REGION_JPN)
#if TARGET_PC || !(REGION_PAL || REGION_JPN)
IF_DUSK_BLOCK(!isPalOrJpn())
mMenuIcon[0]->hide();
mMenuIcon[1]->hide();
IF_DUSK_BLOCK_END
#endif
mMojiPane = nameIn.NameInScr->search(MULTI_CHAR('moji_n'));
@@ -1501,25 +1643,28 @@ void dName_c::screenSet() {
((J2DTextBox*)nameTagPane[i])->setFont(nameIn.font);
((J2DTextBox*)nameTagPane[i])->setString(72, "");
((J2DTextBox*)nameTagPane[i])->setWhite(JUtility::TColor(0xC8, 0xC8, 0xC8, 0xFF));
#if REGION_PAL
#if TARGET_PC || REGION_PAL
IF_DUSK_BLOCK(dusk::version::isRegionPal())
((J2DTextBox*)nameTagPane[i])->resize(24.0f, 23.0f);
IF_DUSK_BLOCK_END
#endif
mNameText[i] = ((J2DTextBox*)nameTagPane[i])->getStringPtr();
}
#if REGION_PAL
#if REGION_PAL // DUSK version note: this code mutates strings. We just edit the table.
IF_DUSK_BLOCK(dusk::version::isRegionPal())
int idx = 2;
static u8 palMoji00[13] = {
static const u8 palMoji00[13] = {
0xC0, 0xC1, 0xC2, 0xC4, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE,
};
static u8 palMoji01[13] = {
static const u8 palMoji01[13] = {
0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD6, 0x8C, 0xD9, 0xDA, 0xDB, 0xDC, 0x2D,
};
static u8 palMoji10[13] = {
static const u8 palMoji10[13] = {
0xE0, 0xE1, 0xE2, 0xE4, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE,
};
static u8 palMoji11[13] = {
static const u8 palMoji11[13] = {
0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF6, 0x9C, 0xF9, 0xFA, 0xFB, 0xFC, 0xDF,
};
@@ -1536,6 +1681,7 @@ void dName_c::screenSet() {
l_mojiEisuPal_2[idx + 1][0] = palMoji11[i];
l_mojiEisuPal_2[idx + 1][1] = 0;
}
IF_DUSK_BLOCK_END
#endif
mCharColumn = 0;
@@ -1574,18 +1720,15 @@ void dName_c::displayInit() {
mNameCursor[i]->hide();
mChrInfo[i].mColumn = 7;
mChrInfo[i].mRow = 1;
#if REGION_PAL || REGION_JPN
#if TARGET_PC
mChrInfo[i].mMojiSet = isPalOrJpn() ? MOJI_HIRA : MOJI_EIGO;
#elif REGION_PAL || REGION_JPN
mChrInfo[i].mMojiSet = MOJI_HIRA;
#else
mChrInfo[i].mMojiSet = MOJI_EIGO;
#endif
mChrInfo[i].field_0x3 = 1;
#if REGION_JPN
// ' ' (full-width space)
mChrInfo[i].mCharacter = '\x81\x40';
#else
mChrInfo[i].mCharacter = ' ';
#endif
mChrInfo[i].mCharacter = SPACE_MAYBE_FULL;
}
mIsInputEnd = false;
@@ -1596,7 +1739,63 @@ void dName_c::NameStrSet() {
int i = 0;
while (*moji != 0) {
#if REGION_PAL
#if TARGET_PC
if (dusk::version::isRegionPal()) {
mChrInfo[i].mCharacter = static_cast<u8>(*moji);
for (int j = 0; j < 65; j++) {
if (mChrInfo[i].mCharacter == *(u8*)l_mojiEisuPal_1[j] ||
mChrInfo[i].mCharacter == *(u16*)l_mojiEisuPal_2[j])
{
mChrInfo[i].mColumn = j / 5;
mChrInfo[i].mRow = j % 5;
mChrInfo[i].mMojiSet = MOJI_HIRA;
break;
}
}
moji++;
i++;
} else {
if (*(u8*)moji >> 4 == 8 || *(u8*)moji >> 4 == 9) {
mChrInfo[i].mCharacter = *(u16*)moji;
for (int j = 0; j < 65; j++) {
if (mChrInfo[i].mCharacter == *(u16*)l_mojiHira[j] ||
mChrInfo[i].mCharacter == *(u16*)l_mojiHira2[j] ||
mChrInfo[i].mCharacter == *(u16*)l_mojiHira3[j])
{
mChrInfo[i].mColumn = j / 5;
mChrInfo[i].mRow = j % 5;
mChrInfo[i].mMojiSet = MOJI_HIRA;
break;
} else if (mChrInfo[i].mCharacter == *(u16*)l_mojikata[j] ||
mChrInfo[i].mCharacter == *(u16*)l_mojikata2[j] ||
mChrInfo[i].mCharacter == *(u16*)l_mojikata3[j])
{
mChrInfo[i].mColumn = j / 5;
mChrInfo[i].mRow = j % 5;
mChrInfo[i].mMojiSet = MOJI_KATA;
break;
}
}
moji += 2;
i++;
} else {
mChrInfo[i].mCharacter = *moji;
for (int j = 0; j < 65; j++) {
if (mChrInfo[i].mCharacter == *(u8*)l_mojiEisu[j]) {
mChrInfo[i].mColumn = j / 5;
mChrInfo[i].mRow = j % 5;
mChrInfo[i].mMojiSet = MOJI_EIGO;
break;
}
}
moji++;
i++;
}
}
#elif REGION_PAL
mChrInfo[i].mCharacter = static_cast<u8>(*moji);
for (int j = 0; j < 65; j++) {
@@ -1669,7 +1868,9 @@ s32 dName_c::getMenuPosIdx(u8 selPos) {
result = 1;
break;
case 2:
#if REGION_PAL
#if TARGET_PC
result = dusk::version::isRegionPal() ? 3 : 2;
#elif REGION_PAL
result = 3;
#else
result = 2;
+6 -1
View File
@@ -1972,7 +1972,12 @@ void dPa_light8PcallBack::draw(JPABaseEmitter* param_1, JPABaseParticle* param_2
JGeometry::TVec3<f32> local_154;
JGeometry::TVec3<f32> local_160;
JGeometry::TVec3<f32> local_16c;
dPa_setWindPower(param_2);
#if TARGET_PC
if (dusk::frame_interp::is_sim_frame())
#endif
{
dPa_setWindPower(param_2);
}
MTXIdentity(local_60);
MTXIdentity(auStack_90);
param_2->getBaseAxis(&local_10c);
-4
View File
@@ -102,11 +102,7 @@ static void setIndirectTex(J3DModelData* i_modelData) {
texture->setResTIMG(i, *mDoGph_gInf_c::getFrameBufferTimg());
}
if (memcmp(textureName, "Zbuffer", 8) == 0) {
#if !TARGET_PC
texture->setResTIMG(i, *mDoGph_gInf_c::getZbufferTimg());
#else
DuskLog.warn("Zbuffer texture binding not yet supported");
#endif
}
}
}
+173 -9
View File
@@ -24,6 +24,7 @@
#include "JSystem/JUtility/JUTConsole.h"
#include "dusk/logging.h"
#include "dusk/version.hpp"
#if !PLATFORM_GCN
#include <revolution/os.h>
@@ -44,7 +45,12 @@ struct homeBtnData {
};
#endif
#if VERSION == VERSION_SHIELD
#if TARGET_PC
using namespace dusk::version;
#define LOGO_ARC versionSelect<const char*>({{GameVersion::GcnJpn, "Logo"}, {GameVersion::GcnPal, "LogoPal"}}, "LogoUs")
#define MSG_PATH versionSelect<const char*>({{GameVersion::GcnJpn, "/res/Msgjp/bmgres.arc"}}, "/res/Msgus/bmgres.arc")
#elif VERSION == VERSION_SHIELD
#define LOGO_ARC "LogoUs"
#define MSG_PATH "/res/Msgcn/bmgres.arc"
#elif VERSION == VERSION_GCN_JPN
@@ -789,7 +795,13 @@ dScnLogo_c::~dScnLogo_c() {
JKR_DELETE(mProgressiveNo);
JKR_DELETE(mProgressiveSel);
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC
if (getGameVersion() == GameVersion::GcnPal) {
mpPalLogoResCommand->getArchive()->removeResourceAll();
mpPalLogoResCommand->getArchive()->unmount();
mpPalLogoResCommand->destroy();
}
#elif VERSION == VERSION_GCN_PAL
mpPalLogoResCommand->getArchive()->removeResourceAll();
mpPalLogoResCommand->getArchive()->unmount();
mpPalLogoResCommand->destroy();
@@ -951,7 +963,8 @@ static int phase_0(dScnLogo_c* i_this) {
JUT_ASSERT(1528, i_this->mLogo01Heap != NULL);
JKRHEAP_NAME(i_this->mLogo01Heap, "Logo01");
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
IF_DUSK_BLOCK(getGameVersion() == GameVersion::GcnPal)
switch (i_this->getPalLanguage()) {
case 1:
i_this->mpPalLogoResCommand = mDoDvdThd_mountArchive_c::create("/res/Layout/LogoPalGm.arc", 0, NULL);
@@ -970,6 +983,7 @@ static int phase_0(dScnLogo_c* i_this) {
i_this->mpPalLogoResCommand = mDoDvdThd_mountArchive_c::create("/res/Layout/LogoPalUk.arc", 0, NULL);
break;
}
IF_DUSK_BLOCK_END
#endif
#if PLATFORM_WII || PLATFORM_SHIELD
@@ -990,7 +1004,8 @@ static int phase_1(dScnLogo_c* i_this) {
}
#endif
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
IF_DUSK_BLOCK(getGameVersion() == GameVersion::GcnPal)
if (!mDoDvdThd::SyncWidthSound) {
return cPhs_INIT_e;
}
@@ -998,6 +1013,7 @@ static int phase_1(dScnLogo_c* i_this) {
if (!i_this->mpPalLogoResCommand->sync()) {
return cPhs_INIT_e;
}
IF_DUSK_BLOCK_END
#endif
int rt;
@@ -1237,7 +1253,113 @@ void dScnLogo_c::logoInitGC() {
ResTIMG* dolbyImg = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 3);
mDolbyLogo = JKR_NEW dDlst_2D_c(dolbyImg, 189, 150, 232, 112, 255);
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC
if (getGameVersion() == GameVersion::GcnPal) {
u8 language = getPalLanguage();
if (language >= 5) {
language = 0;
}
static const char* choice[] = {
"50_60_choice_eng.bti",
"50_60_choice_ger.bti",
"50_60_choice_fra.bti",
"50_60_choice_spa.bti",
"50_60_choice_ita.bti",
};
static const char* yes[] = {
"60_set_eng.bti",
"60_set_ger.bti",
"60_set_fra.bti",
"60_set_spa.bti",
"60_set_ita.bti",
};
static const char* no[] = {
"50_set_eng.bti",
"50_set_ger.bti",
"50_set_fra.bti",
"50_set_spa.bti",
"50_set_ita.bti",
};
static const char* prog[] = {
"progressive_pro.bti",
"progressive_pro_gm.bti",
"progressive_pro_fr.bti",
"progressive_pro_sp.bti",
"progressive_pro_it.bti",
};
static const char* intr[] = {
"progressive_inter.bti",
"progressive_inter_gm.bti",
"progressive_inter_fr.bti",
"progressive_inter_sp.bti",
"progressive_inter_it.bti",
};
static const char* warning[] = {
"warning.bti",
"warning_gm.bti",
"warning_fr.bti",
"warning_sp.bti",
"warning_it.bti",
};
static const char* warningPs[] = {
"warning_pstart.bti",
"warning_pstart_gm.bti",
"warning_pstart_fr.bti",
"warning_pstart_sp.bti",
"warning_pstart_it.bti",
};
ResTIMG* warningImg = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', warning[language]);
mWarning = JKR_NEW dDlst_2D_c(warningImg, 0, 0, FB_WIDTH, FB_HEIGHT, 255);
ResTIMG* warnStartImg = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', warningPs[language]);
mWarningStart = JKR_NEW dDlst_2D_c(warnStartImg, 0, 359, FB_WIDTH, 48, 255);
ResTIMG* progChoiceImg = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', choice[language]);
mProgressiveChoice = JKR_NEW dDlst_2D_c(progChoiceImg, 113, 143, 416, 210, 255);
ResTIMG* progYesImg = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', yes[language]);
mProgressiveYes = JKR_NEW dDlst_2D_c(progYesImg, 121, 352, 200, 72, 255);
mProgressiveYes->getPicture()->setWhite(JUtility::TColor(160, 160, 160, 255));
ResTIMG* progNoImg = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', no[language]);
mProgressiveNo = JKR_NEW dDlst_2D_c(progNoImg, 320, 352, 200, 72, 255);
mProgressiveNo->getPicture()->setWhite(JUtility::TColor(160, 160, 160, 255));
mProgressivePro = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', prog[language]);
mProgressiveInter = (ResTIMG*)mpPalLogoResCommand->getArchive()->getResource('DAT ', intr[language]);
mProgressiveSel = JKR_NEW dDlst_2D_c(mProgressivePro, 153, 309, 336, 88, 255);
} else {
ResTIMG* warningImg = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 10);
mWarning = JKR_NEW dDlst_2D_c(warningImg, 0, 0, FB_WIDTH, FB_HEIGHT, 255);
ResTIMG* warnStartImg = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 11);
mWarningStart = JKR_NEW dDlst_2D_c(warnStartImg, 0, 359, FB_WIDTH, 48, 255);
ResTIMG* progChoiceImg = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 5);
mProgressiveChoice = JKR_NEW dDlst_2D_c(progChoiceImg, 113, 281, 416, 72, 255);
ResTIMG* progYesImg = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 9);
mProgressiveYes = JKR_NEW dDlst_2D_c(progYesImg, 211, 372, 80, 32, 255);
mProgressiveYes->getPicture()->setWhite(JUtility::TColor(160, 160, 160, 255));
ResTIMG* progNoImg = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 7);
mProgressiveNo = JKR_NEW dDlst_2D_c(progNoImg, 350, 372, 80, 32, 255);
mProgressiveNo->getPicture()->setWhite(JUtility::TColor(160, 160, 160, 255));
mProgressivePro = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 8);
mProgressiveInter = (ResTIMG*)dComIfG_getObjectRes(LOGO_ARC, 6);
mProgressiveSel = JKR_NEW dDlst_2D_c(mProgressivePro, 153, 309, 336, 88, 255);
}
#elif VERSION == VERSION_GCN_PAL
u8 language = getPalLanguage();
if (language >= 5) {
language = 0;
@@ -1395,7 +1517,30 @@ void dScnLogo_c::dvdDataLoad() {
mpButtonCommand = aramMount(BUTTON_RES_PATH, mDoExt_getJ2dHeap());
mpCardIconCommand = aramMount(ICON_RES_PATH, mDoExt_getJ2dHeap());
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC
if (getGameVersion() == GameVersion::GcnPal) {
switch (getPalLanguage()) {
case 1:
mpBmgResCommand = onMemMount("/res/Msgde/bmgres.arc");
break;
case 2:
mpBmgResCommand = onMemMount("/res/Msgfr/bmgres.arc");
break;
case 3:
mpBmgResCommand = onMemMount("/res/Msgsp/bmgres.arc");
break;
case 4:
mpBmgResCommand = onMemMount("/res/Msgit/bmgres.arc");
break;
case 0:
default:
mpBmgResCommand = onMemMount("/res/Msguk/bmgres.arc");
break;
}
} else {
mpBmgResCommand = onMemMount(MSG_PATH);
}
#elif VERSION == VERSION_GCN_PAL
switch (getPalLanguage()) {
case 1:
mpBmgResCommand = onMemMount("/res/Msgde/bmgres.arc");
@@ -1435,7 +1580,10 @@ void dScnLogo_c::dvdDataLoad() {
mpMsgResCommand[1] = aramMount(MSG_RES1_PATH, mDoExt_getJ2dHeap());
mpMsgResCommand[2] = aramMount(MSG_RES2_PATH, mDoExt_getJ2dHeap());
mpMsgResCommand[3] = aramMount(MSG_RES3_PATH, mDoExt_getJ2dHeap());
#if VERSION == VERSION_GCN_JPN
#if TARGET_PC
const auto res4Path = versionSelect<const char*>({{GameVersion::GcnJpn, "/res/Layout/msgres04.arc"}}, "/res/Layout/msgres04F.arc");
mpMsgResCommand[4] = aramMount( res4Path, mDoExt_getJ2dHeap());
#elif VERSION == VERSION_GCN_JPN
mpMsgResCommand[4] = aramMount("/res/Layout/msgres04.arc", mDoExt_getJ2dHeap());
#else
mpMsgResCommand[4] = aramMount("/res/Layout/msgres04F.arc", mDoExt_getJ2dHeap());
@@ -1445,7 +1593,23 @@ void dScnLogo_c::dvdDataLoad() {
mpMain2DCommand = onMemMount(MAIN2D_PATH);
#if VERSION == VERSION_GCN_JPN
#if TARGET_PC
const auto fontResPath = versionSelect<const char*>(
{
{GameVersion::GcnJpn, "/res/Fontjp/fontres.arc"},
{GameVersion::GcnPal, "/res/Fonteu/fontres.arc"},
}, "/res/Fontus/fontres.arc");
const auto fontRubyPath = versionSelect<const char*>(
{
{GameVersion::GcnJpn, "/res/Fontjp/rubyres.arc"},
{GameVersion::GcnPal, "/res/Fonteu/rubyres.arc"},
}, "/res/Fontus/rubyres.arc");
// Note: GCN_JPN mounts this archive as tail instead of head.
// I'm guessing this is fine since we have more RAM.
mpFontResCommand = onMemMount(fontResPath);
mpRubyResCommand = onMemMount(fontRubyPath);
#elif VERSION == VERSION_GCN_JPN
mpFontResCommand = mDoDvdThd_mountXArchive_c::create("/res/Fontjp/fontres.arc", 1, JKRArchive::MOUNT_MEM, NULL);
mpRubyResCommand = onMemMount("/res/Fontjp/rubyres.arc");
#elif VERSION == VERSION_GCN_PAL
@@ -1545,7 +1709,7 @@ static int dScnLogo_IsDelete(dScnLogo_c* i_this) {
return 1;
}
#if VERSION == VERSION_GCN_PAL || PLATFORM_WII || PLATFORM_SHIELD
#if TARGET_PC || VERSION == VERSION_GCN_PAL || PLATFORM_WII || PLATFORM_SHIELD
u8 dScnLogo_c::getPalLanguage() {
u8 language;
+6 -2
View File
@@ -16,6 +16,8 @@
#include <cstdio>
#include <cstring>
#include "dusk/version.hpp"
#if PLATFORM_WII || PLATFORM_SHIELD
#include <revolution/sc.h>
#include <revolution/wpad.h>
@@ -1027,7 +1029,7 @@ void dSv_player_config_c::init() {
mAttentionType = 0;
mVibration = 1;
#if DEBUG
#if DEBUG // DUSK VERSION SUPPORT: This field isn't used, so we can ignore it.
mLanguage = SCGetLanguage();
#elif REGION_PAL || VERSION >= VERSION_WII_USA_R2
mLanguage = OSGetLanguage();
@@ -1072,7 +1074,8 @@ void dSv_player_config_c::setVibration(u8 i_status) {
}
u8 dSv_player_config_c::getPalLanguage() const {
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC || VERSION == VERSION_GCN_PAL
IF_DUSK_BLOCK(dusk::version::getGameVersion() == dusk::version::GameVersion::GcnPal)
switch (OSGetLanguage()) {
case 0:
return LANGUAGE_ENGLISH;
@@ -1085,6 +1088,7 @@ u8 dSv_player_config_c::getPalLanguage() const {
case 4:
return LANGUAGE_ITALIAN;
}
IF_DUSK_BLOCK_END
#elif VERSION >= VERSION_WII_USA_R0
switch (SCGetLanguage()) {
case 1:
+467
View File
@@ -0,0 +1,467 @@
#include "dusk/achievements.h"
#include "dusk/io.hpp"
#include "dusk/main.h"
#include "d/d_com_inf_game.h"
#include "d/d_meter2_info.h"
#include "d/actor/d_a_alink.h"
#include "d/actor/d_a_npc4.h"
#include "d/actor/d_a_player.h"
#include "d/d_demo.h"
#include "f_pc/f_pc_name.h"
#include <filesystem>
#include <algorithm>
namespace dusk {
using json = nlohmann::json;
static void checkGoatHerding(Achievement& a, int32_t threshMs) {
if (dMeter2Info_getMaxCount() != 20 || dMeter2Info_getNowCount() != 20) {
return;
}
const int32_t elapsed = dMeter2Info_getTimeMs();
if (elapsed > 0 && elapsed <= threshMs) {
a.progress = 1;
}
}
static constexpr auto ACHIEVEMENTS_FILENAME = "achievements.json";
std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
return {
{
{
"hero_of_twilight",
"Hero of Twilight",
"Deliver the finishing blow to Ganondorf.",
AchievementCategory::Story,
false, 0, 0, false
},
[](Achievement& a, json&) {
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link != nullptr && link->mProcID == daAlink_c::PROC_GANON_FINISH) {
a.progress = 1;
}
},
{}
},
{
{
"rollgoal_8",
"Rollgoal Novice",
"Complete the first 8 rollgoal stages.",
AchievementCategory::Minigame,
true, 8, 0, false
},
[](Achievement& a, json&) {
a.progress = std::min((int)dComIfGs_getEventReg(0xf63f), 8);
},
{}
},
{
{
"rollgoal_all",
"Lost Your Marbles",
"Complete all rollgoal stages.",
AchievementCategory::Minigame,
true, 64, 0, false
},
[](Achievement& a, json&) {
if (dComIfGs_isEventBit(dSv_event_flag_c::KORO2_ALLCLEAR)) {
a.progress = 64;
} else {
a.progress = dComIfGs_getEventReg(0xf63f);
}
},
{}
},
{
{
"goat_30s",
"Ranch Hand",
"Herd all 20 goats into the pen in under 30 seconds.",
AchievementCategory::Minigame,
false, 0, 0, false
},
[](Achievement& a, json&) {
checkGoatHerding(a, 30000);
},
{}
},
{
{
"goat_20s",
"Bane of Howard",
"Herd all 20 goats into the pen in under 20 seconds.",
AchievementCategory::Minigame,
false, 0, 0, false
},
[](Achievement& a, json&) {
checkGoatHerding(a, 20000);
},
{}
},
{
{
"goat_18s",
"King of the Ranch",
"Herd all 20 goats into the pen in under 18 seconds.",
AchievementCategory::Minigame,
false, 0, 0, false
},
[](Achievement& a, json&) {
checkGoatHerding(a, 18000);
},
{}
},
{
{
"cave_of_ordeals",
"Conqueror of Ordeals",
"Clear all 50 floors of the Cave of Ordeals.",
AchievementCategory::Challenge,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (daNpcF_chkEvtBit(0x1F9)) {
a.progress = 1;
}
},
{}
},
{
{
"cave_of_ordeals_heartless",
"Indomitable",
"Clear all 50 floors of the Cave of Ordeals with only 3 heart containers.",
AchievementCategory::Challenge,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() <= 15) {
a.progress = 1;
}
},
{}
},
{
{
"speedrun_12h",
"Been There Done That",
"Defeat Ganondorf with a total save file play time under 12 hours.",
AchievementCategory::Challenge,
false, 0, 0, false
},
[](Achievement& a, json&) {
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) {
return;
}
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
if (ticks / OS_TIMER_CLOCK < 12 * 3600) {
a.progress = 1;
}
},
{}
},
{
{
"speedrun_8h",
"Swift Blade",
"Defeat Ganondorf with a total save file play time under 6 hours.",
AchievementCategory::Challenge,
false, 0, 0, false
},
[](Achievement& a, json&) {
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) {
return;
}
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
if (ticks / OS_TIMER_CLOCK < 8 * 3600) {
a.progress = 1;
}
},
{}
},
{
{
"princess_of_bugs",
"The Princess of Bugs",
"Deliver all 24 golden bugs to Agitha.",
AchievementCategory::Collection,
true, 24, 0, false
},
[](Achievement& a, json&) {
a.progress = dComIfGs_checkGetInsectNum();
},
{}
},
{
{
"all_poes",
"Poe Collector",
"Collect all 60 Poe Souls.",
AchievementCategory::Collection,
true, 60, 0, false
},
[](Achievement& a, json&) {
a.progress = dComIfGs_getPohSpiritNum();
},
{}
},
{
{
"hylian_loach",
"Legendary Catch",
"Catch a Hylian Loach.",
AchievementCategory::Collection,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (dComIfGs_getFishNum(1) > 0) {
a.progress = 1;
}
},
{}
},
{
{
"all_fish",
"Gone Fishin'",
"Catch all 6 species of fish.",
AchievementCategory::Collection,
true, 6, 0, false
},
[](Achievement& a, json&) {
int nUniqueFish = 0;
for (int i = 0; i < 6; ++i) {
if (dComIfGs_getFishNum(i) != 0) {
nUniqueFish++;
}
}
a.progress = nUniqueFish;
},
{}
},
{
{
"a_big_heart",
"A Big Heart",
"Reach maximum health with all 20 heart containers.",
AchievementCategory::Collection,
true, 20, 0, false
},
[](Achievement& a, json&) {
a.progress = dComIfGs_getMaxLife() / 5;
},
{}
},
{
{
"back_in_time",
"Back in Time",
"Perform the Back in Time glitch to play on the title screen.",
AchievementCategory::Glitched,
false, 0, 0, false
},
[](Achievement& a, json&) {
static int titleNoDemoFrames = 0;
if (fopAcM_SearchByName(fpcNm_TITLE_e) == nullptr) {
titleNoDemoFrames = 0;
return;
}
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link != nullptr && dDemo_c::getMode() == 0) {
if (++titleNoDemoFrames >= 60) {
a.progress = 1;
}
} else {
titleNoDemoFrames = 0;
}
},
{}
},
{
{
"early_master_sword",
"Early Master Sword",
"Obtain the Master Sword before completing Midna's Desperate Hour.",
AchievementCategory::Glitched,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (dComIfGs_isCollectSword(COLLECT_MASTER_SWORD) && !dComIfGs_isEventBit(0x1E08)) {
a.progress = 1;
}
},
{}
},
{
{
"earliest_master_sword",
"Earliest Master Sword",
"Obtain the Master Sword before meeting Midna.",
AchievementCategory::Glitched,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (dComIfGs_isCollectSword(COLLECT_MASTER_SWORD) && !dComIfGs_isTransformLV(0)) {
a.progress = 1;
}
},
{}
},
{
{
"ultimate_delivery",
"The Ultimate Delivery",
"Have all 16 postman letters at the same time.",
AchievementCategory::Glitched,
true, 16, 0, false
},
[](Achievement& a, json&) {
a.progress = dMeter2Info_getRecieveLetterNum();
},
{}
},
{
{
"speedrun_4h",
"Hero of Time",
"Defeat Ganondorf with a total save file play time under 4 hours.",
AchievementCategory::Glitched,
false, 0, 0, false
},
[](Achievement& a, json&) {
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) {
return;
}
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
if (ticks / OS_TIMER_CLOCK < 4 * 3600) {
a.progress = 1;
}
},
{}
}
};
}
AchievementSystem::AchievementSystem() : m_entries(makeEntries()) {}
AchievementSystem& AchievementSystem::get() {
static AchievementSystem instance;
return instance;
}
std::string AchievementSystem::consumePendingUnlock() {
std::string msg = std::move(m_pendingUnlocks.front());
m_pendingUnlocks.pop();
return msg;
}
std::vector<Achievement> AchievementSystem::getAchievements() const {
std::vector<Achievement> result;
result.reserve(m_entries.size());
for (const auto& e : m_entries) {
result.push_back(e.achievement);
}
return result;
}
void AchievementSystem::load() {
m_loaded = true;
const auto filePath = dusk::ConfigPath / ACHIEVEMENTS_FILENAME;
if (!std::filesystem::exists(filePath)) {
return;
}
try {
auto data = io::FileStream::ReadAllBytes(filePath.string().c_str());
auto j = json::parse(data);
if (!j.is_object()) {
return;
}
for (auto& e : m_entries) {
if (!j.contains(e.achievement.key)) {
continue;
}
const auto& entry = j[e.achievement.key];
if (entry.contains("progress")) {
e.achievement.progress = entry["progress"].get<int32_t>();
}
if (entry.contains("unlocked")) {
e.achievement.unlocked = entry["unlocked"].get<bool>();
}
if (entry.contains("extra")) {
e.extra = entry["extra"];
}
}
} catch (const std::exception&) {}
}
void AchievementSystem::save() {
json j = json::object();
for (const auto& e : m_entries) {
json entry = {
{"progress", e.achievement.progress},
{"unlocked", e.achievement.unlocked},
};
if (!e.extra.is_null()) {
entry["extra"] = e.extra;
}
j[e.achievement.key] = std::move(entry);
}
try {
io::FileStream::WriteAllText(
(dusk::ConfigPath / ACHIEVEMENTS_FILENAME).string().c_str(),
j.dump(2)
);
} catch (const std::exception&) {}
}
void AchievementSystem::clearAll() {
m_entries = makeEntries();
save();
}
void AchievementSystem::processEntry(Entry& e) {
if (e.achievement.unlocked) {
return;
}
const int32_t prevProgress = e.achievement.progress;
e.check(e.achievement, e.extra);
const bool progressChanged = e.achievement.progress != prevProgress;
const bool nowUnlocked = e.achievement.isCounter ?
e.achievement.progress >= e.achievement.goal :
e.achievement.progress > 0;
if (nowUnlocked) {
e.achievement.progress = e.achievement.isCounter ? e.achievement.goal : 1;
e.achievement.unlocked = true;
m_pendingUnlocks.push(e.achievement.name);
m_dirty = true;
} else if (progressChanged) {
m_dirty = true;
}
}
void AchievementSystem::tick() {
if (!m_loaded) {
load();
}
if (!dusk::IsGameLaunched) {
return;
}
for (auto& e : m_entries) {
processEntry(e);
}
if (m_dirty) {
save();
m_dirty = false;
}
}
} // namespace dusk
+132 -10
View File
@@ -5,14 +5,15 @@
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <span>
#include "Adpcm.hpp"
#include "freeverb/revmodel.hpp"
#include "JSystem/JAudio2/JASDriverIF.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/endian.h"
#include "dusk/logging.h"
#include "global.h"
#include "tracy/Tracy.hpp"
@@ -95,6 +96,13 @@ static void RenderChannel(
ChannelAuxData& channelAux,
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.
*/
@@ -117,6 +125,8 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) {
aux.resamplePos = 0.0;
aux.resamplePrev = 0;
aux.oscPhase = 0;
aux.prev_lp_out = 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) {
ZoneScoped;
if (DumpAudio != sDumpWasActive) {
@@ -152,6 +275,8 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
}
}
GenerateEvolvingHarmonic();
std::span channels(JASDsp::CH_BUF, DSP_CHANNELS);
DspSubframe reverbInputL = {};
@@ -174,17 +299,14 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
channel.mIsFinished = true;
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 = {};
RenderChannel(channel, channelAux, channelSubframe);
if (channel.mWaveAramAddress == 0) {
RenderOscChannel(channel, channelAux, channelSubframe);
} else {
ValidateChannel(channel);
RenderChannel(channel, channelAux, channelSubframe);
}
if (EnableReverb) {
// 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
s16 resamplePrev;
// phase of oscillator channels
u16 oscPhase;
// low pass previous state
f32 prev_lp_out; // out[n-1]
f32 prev_lp_in; // in[n-1]
+1
View File
@@ -154,6 +154,7 @@ namespace dusk::config {
template class ConfigImpl<f64>;
template class ConfigImpl<std::string>;
template class ConfigImpl<dusk::BloomMode>;
template class ConfigImpl<dusk::GameLanguage>;
}
void dusk::config::Register(ConfigVarBase& configVar) {
-1
View File
@@ -167,7 +167,6 @@ void InitializeCrashReporting() {
sentry_set_tag("git_branch", DUSK_WC_BRANCH);
sentry_set_tag("build_type", DUSK_BUILD_TYPE);
sentry_set_tag("tp_version", DUSK_TP_VERSION);
g_sentryInitialized = true;
DuskLog.info("Initialized Sentry crash reporting");
+21 -7
View File
@@ -80,8 +80,19 @@ static s32 DolVaToFileOffset(u32 va) {
return -1;
}
bool LoadDolAsset(void* dst, u32 virtualAddress, s32 size) {
s32 fileOffset = DolVaToFileOffset(virtualAddress);
static u32 GetOffsetForVersion(std::initializer_list<OffsetVersion> version) {
const auto gameVersion = dusk::version::getGameVersion();
for (auto elem : version) {
if (elem.mGameVersion == gameVersion) {
return elem.mOffset;
}
}
DuskLog.fatal("Unable to find offset for this game version!");
}
bool LoadDolAsset(void* dst, std::initializer_list<OffsetVersion> virtualAddress, s32 size) {
s32 fileOffset = DolVaToFileOffset(GetOffsetForVersion(virtualAddress));
if (fileOffset < 0) {
return false;
}
@@ -95,13 +106,16 @@ bool LoadDolAsset(void* dst, u32 virtualAddress, s32 size) {
return true;
}
bool LoadRelAsset(void* dst, const char* dvdPath, s32 offset, s32 size) {
void* p = JKRDvdRipper::loadToMainRAM(dvdPath, (u8*)dst, EXPAND_SWITCH_UNKNOWN1, (u32)size, nullptr, JKRDvdRipper::ALLOC_DIRECTION_FORWARD, (u32)offset, nullptr, nullptr);
if (!p) DuskLog.fatal("dvd_asset: failed to load {} (offset={:#x} size={:#x})", dvdPath, offset, size);
bool LoadRelAsset(void* dst, const char* dvdPath, std::initializer_list<OffsetVersion> offset, s32 size) {
auto resOffset = GetOffsetForVersion(offset);
void* p = JKRDvdRipper::loadToMainRAM(dvdPath, (u8*)dst, EXPAND_SWITCH_UNKNOWN1, (u32)size, nullptr, JKRDvdRipper::ALLOC_DIRECTION_FORWARD, resOffset, nullptr, nullptr);
if (!p) DuskLog.fatal("dvd_asset: failed to load {} (offset={:#x} size={:#x})", dvdPath, resOffset, size);
return p != nullptr;
}
bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, s32 offset, s32 size) {
bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, std::initializer_list<OffsetVersion> offset, s32 size) {
auto resOffset = GetOffsetForVersion(offset);
// On TARGET_PC, cDyl_InitCallback skips DynamicModuleControl::initialize() due to static linking
// Mount RELS.arc on first use so sArchive is available
static bool s_mountAttempted = false;
@@ -118,7 +132,7 @@ bool LoadArchivedRelAsset(void* dst, u32 memType, const char* relFileName, s32 o
DuskLog.fatal("dvd_asset: {} not found in RELS archive", relFileName); return false;
}
std::memcpy(dst, rel + offset, size);
std::memcpy(dst, rel + resOffset, size);
return true;
}
+21 -5
View File
@@ -3,6 +3,27 @@
#include <string.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
int stricmp(const char* str1, const char* str2) {
char a_var;
@@ -48,11 +69,6 @@ int strnicmp(const char* str1, const char* str2, int n) {
}
#endif
void *_memcpy(void* dest, void const* src, int n) {
return memcpy(dest, src, n);
}
void DCZeroRange(void* addr, uint32_t nBytes) {
#if defined(_MSC_VER) || TARGET_ANDROID
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;
}
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) {
g_enabled = enabled;
g_is_sim_frame = is_sim_frame;
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() {
@@ -286,12 +292,20 @@ void interp_view(::view_class* view) {
return;
const f32 step = get_interpolation_step();
const bool is_cam_curr_authoritative = g_is_sim_frame && step <= 0.0f;
cXyz eye;
cXyz center;
cXyz up;
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 (is_cam_curr_authoritative) {
eye = s_cam_curr.eye;
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()) {
up = s_cam_curr.up;
up.normalizeRS();
@@ -300,19 +314,25 @@ void interp_view(::view_class* view) {
view->lookat.eye = eye;
view->lookat.center = center;
view->lookat.up = up;
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;
if (is_cam_curr_authoritative) {
view->bank = s_cam_curr.bank;
view->fovy = s_cam_curr.fovy;
view->aspect = s_cam_curr.aspect;
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
// next sim frame, but I don't care enough to right now
#if WIDESCREEN_SUPPORT
if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && step >= 0.5f ?
s_cam_curr.wideZoom :
s_cam_prev.wideZoom)
{
const f32 wide_step = is_cam_curr_authoritative ? 1.0f : step;
if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && wide_step >= 0.5f ? s_cam_curr.wideZoom : s_cam_prev.wideZoom) {
mDoGph_gInf_c::onWideZoom();
}
#endif
+44 -23
View File
@@ -5,62 +5,84 @@
#include <cmath>
#include <unordered_map>
namespace dusk {
namespace game_clock {
namespace dusk::game_clock {
using clock = std::chrono::steady_clock;
bool s_initialized = false;
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;
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() {
if (s_initialized) {
return;
}
s_previous_sample = clock::now();
s_sim_accumulator = sim_pace();
s_current_snapshot_time = s_previous_sample;
s_initialized = true;
}
void reset_accumulator() {
ensure_initialized();
s_sim_accumulator = fmodf(s_sim_accumulator, sim_pace());
}
void reset_frame_timer() {
s_previous_sample = clock::now();
s_sim_accumulator = 0.0f;
s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
}
MainLoopPacer advance_main_loop() {
ensure_initialized();
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_sim_accumulator += presentation_dt;
MainLoopPacer out{};
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.sim_pace = sim_pace();
if (!should_interpolate) {
s_sim_accumulator = 0.0f;
out.do_sim_tick = true;
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();
s_current_snapshot_time = now;
out.sim_ticks_to_run = 1;
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) {
@@ -78,5 +100,4 @@ float consume_interval(const void* consumer) {
return dt;
}
} // namespace game_clock
} // namespace dusk
} // namespace dusk::game_clock
+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 "d/actor/d_a_alink.h"
#include <cmath>
namespace dusk::gyro {
namespace {
constexpr s32 kRollgoalTableMaxOffset = 12000;
constexpr s32 kRollgoalTableMaxOffset = 6500;
constexpr float kGyroEmaAlphaMin = 0.05f;
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_accel_enabled = false;
bool s_was_aiming = false;
bool s_have_gravity_baseline = false;
float s_smooth_gx = 0.0f;
float s_smooth_gy = 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_pitch_rad = 0.0f;
float s_roll_rad = 0.0f;
@@ -19,6 +32,10 @@ s32 s_rollgoal_az = 0;
void reset_filter_state() {
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_rollgoal_ax = s_rollgoal_az = 0;
}
@@ -49,15 +66,30 @@ bool queryGyroAimContext() {
}
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) {
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE);
s_sensor_enabled = false;
}
if (s_accel_enabled) {
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_ACCEL, FALSE);
s_accel_enabled = false;
}
reset_filter_state();
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 (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) {
return;
@@ -68,6 +100,13 @@ void read(float dt) {
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];
if (!PADGetSensorData(PAD_CHAN0, PAD_SENSOR_GYRO, gyro, 3)) {
return;
@@ -80,9 +119,50 @@ void read(float dt) {
s_smooth_gy += smooth_alpha * (gyro[1] - s_smooth_gy);
s_smooth_gz += smooth_alpha * (gyro[2] - s_smooth_gz);
s_pitch_rad = -apply_deadband(s_smooth_gx, deadband) * dt * dusk::getSettings().game.gyroSensitivityX;
s_yaw_rad = apply_deadband(s_smooth_gy, deadband) * dt * dusk::getSettings().game.gyroSensitivityY;
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 pitch_rate = apply_deadband(s_smooth_gx, deadband);
const float yaw_rate = apply_deadband(s_smooth_gy, deadband);
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_yaw_rad = dusk::getSettings().game.gyroInvertYaw ? -s_yaw_rad : s_yaw_rad;
+228
View File
@@ -0,0 +1,228 @@
#include "ImGuiAchievements.hpp"
#include "ImGuiConfig.hpp"
#include "dusk/achievements.h"
#include "dusk/settings.h"
#include "fmt/format.h"
#include "imgui.h"
namespace dusk {
void ImGuiAchievements::notify(std::string name) {
if (m_notifyTimer <= 0.f) {
m_notifyName = std::move(name);
m_notifyTimer = NOTIFY_DURATION;
} else {
m_notifyQueue.push(std::move(name));
}
}
void ImGuiAchievements::showNotification() {
if (!getSettings().game.enableAchievementNotifications.getValue()) {
return;
}
if (m_notifyTimer <= 0.f) {
if (m_notifyQueue.empty()) {
return;
}
m_notifyName = std::move(m_notifyQueue.front());
m_notifyQueue.pop();
m_notifyTimer = NOTIFY_DURATION;
}
m_notifyTimer -= ImGui::GetIO().DeltaTime;
const float alpha = std::min({
m_notifyTimer / NOTIFY_FADE_TIME,
(NOTIFY_DURATION - m_notifyTimer) / NOTIFY_FADE_TIME,
1.0f
});
const ImGuiViewport* viewport = ImGui::GetMainViewport();
const float padding = 12.0f;
ImGui::SetNextWindowPos(
ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - padding, viewport->WorkPos.y + padding),
ImGuiCond_Always, ImVec2(1.0f, 0.0f)
);
ImGui::SetNextWindowBgAlpha(alpha * 0.92f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.06f, 0.01f, alpha * 0.92f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.1f, alpha));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, alpha));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(14.0f, 10.0f));
constexpr ImGuiWindowFlags flags =
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs;
if (ImGui::Begin("##achievement_notify", nullptr, flags)) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.82f, 0.1f, alpha));
ImGui::TextUnformatted("Achievement Unlocked!");
ImGui::PopStyleColor();
ImGui::Spacing();
ImGui::TextUnformatted(m_notifyName.c_str());
}
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(3);
}
void ImGuiAchievements::draw(bool& open) {
showNotification();
if (!open) {
return;
}
ImGui::SetNextWindowSizeConstraints(ImVec2(640, 200), ImVec2(800, 900));
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);
if (!ImGui::Begin(
"Achievements", &open,
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)
)
{
ImGui::End();
return;
}
const auto achievements = AchievementSystem::get().getAchievements();
int unlocked = 0;
for (const auto& a : achievements) {
if (a.unlocked) {
++unlocked;
}
}
ImGui::Text("%d / %d achievements unlocked", unlocked, (int)achievements.size());
ImGui::SameLine();
config::ImGuiCheckbox("Notifications", getSettings().game.enableAchievementNotifications);
ImGui::Separator();
static const struct {
AchievementCategory cat;
const char* label;
ImVec4 color;
} ACHIEVEMENT_CATEGORIES[] = {
{AchievementCategory::Story, "Story", ImVec4(1.0f, 0.82f, 0.1f, 1.0f)},
{AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)},
{AchievementCategory::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)},
{AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 1.0f)},
{AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)},
};
const float footerHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginTabBar("##achievement_tabs", ImGuiTabBarFlags_FittingPolicyScroll)) {
for (const auto& catInfo : ACHIEVEMENT_CATEGORIES) {
int catTotal = 0, catUnlocked = 0;
for (const auto& a : achievements) {
if (a.category == catInfo.cat) {
++catTotal;
if (a.unlocked) {
++catUnlocked;
}
}
}
if (catTotal == 0) {
continue;
}
const std::string tabLabel = fmt::format("{} ({}/{})", catInfo.label, catUnlocked, catTotal);
ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color);
const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str());
ImGui::PopStyleColor();
if (tabOpen) {
ImGui::BeginChild(
"##cat_list",
ImVec2(0, -footerHeight),
ImGuiChildFlags_None,
ImGuiWindowFlags_NoBackground
);
ImGui::Spacing();
for (const auto& a : achievements) {
if (a.category != catInfo.cat) {
continue;
}
ImGui::PushID(a.key);
ImGui::PushStyleColor(
ImGuiCol_Text,
a.unlocked ?
ImVec4(1.0f, 0.65f, 0.15f, 1.0f) :
ImGui::GetStyleColorVec4(ImGuiCol_Text)
);
ImGui::TextUnformatted(a.name);
ImGui::PopStyleColor();
const char* statusLabel = a.unlocked ? "[Unlocked]" : "[Locked]";
ImGui::SameLine(
ImGui::GetContentRegionMax().x -
ImGui::CalcTextSize(statusLabel).x
);
if (a.unlocked) {
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s", statusLabel);
} else {
ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1.0f), "%s", statusLabel);
}
ImGui::TextDisabled("%s", a.description);
if (a.isCounter) {
const float fraction = a.goal > 0 ? (float)(a.progress) / (float)(a.goal) : 1.0f;
const std::string overlay = fmt::format("{} / {}", a.progress, a.goal);
ImGui::PushStyleColor(
ImGuiCol_PlotHistogram,
a.unlocked ?
ImVec4(0.4f, 0.7f, 0.1f, 1.0f) :
ImVec4(0.2f, 0.45f, 0.8f, 1.0f)
);
ImGui::ProgressBar(fraction, ImVec2(-1.0f, 0.0f), overlay.c_str());
ImGui::PopStyleColor();
}
ImGui::Spacing();
ImGui::PopID();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
}
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button("Clear All Achievements")) {
ImGui::OpenPopup("##confirm_clear");
}
if (ImGui::BeginPopup("##confirm_clear")) {
ImGui::Text("Reset all achievement progress?");
ImGui::Spacing();
if (ImGui::Button("Yes, reset all")) {
AchievementSystem::get().clearAll();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::End();
}
} // namespace dusk
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <queue>
#include <string>
namespace dusk {
class ImGuiAchievements {
public:
void draw(bool& open);
void notify(std::string name);
private:
void showNotification();
std::string m_notifyName;
float m_notifyTimer = 0.f;
std::queue<std::string> m_notifyQueue;
static constexpr float NOTIFY_DURATION = 4.0f;
static constexpr float NOTIFY_FADE_TIME = 0.5f;
};
} // namespace dusk
+15
View File
@@ -14,6 +14,7 @@
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_mouse.h"
#include "aurora/lib/window.hpp"
#include "dusk/achievements.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/config.hpp"
#include "dusk/dusk.h"
@@ -295,6 +296,15 @@ namespace dusk {
UpdateSettings();
AchievementSystem::get().tick();
while (AchievementSystem::get().hasPendingUnlock()) {
if (getSettings().game.enableAchievementNotifications) {
m_menuTools.notifyAchievement(AchievementSystem::get().consumePendingUnlock());
} else {
AchievementSystem::get().consumePendingUnlock();
}
}
if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) &&
ImGui::IsKeyPressed(ImGuiKey_R))
{
@@ -305,6 +315,10 @@ namespace dusk {
ImGuiMenuGame::ToggleFullscreen();
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape) && getSettings().video.enableFullscreen) {
ImGuiMenuGame::ToggleFullscreen();
}
if (!dusk::IsGameLaunched) {
m_preLaunchWindow.draw();
}
@@ -370,6 +384,7 @@ namespace dusk {
m_menuTools.ShowSaveEditor();
}
m_menuTools.ShowStateShare();
m_menuTools.ShowAchievements();
DuskDebugPad(); // temporary, remove later
// Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds.
+2
View File
@@ -29,12 +29,14 @@ static void ApplyPresetHD() {
s.game.fastTears.setValue(true);
s.game.biggerWallets.setValue(true);
s.game.invertCameraXAxis.setValue(true);
s.game.freeCamera.setValue(true);
}
static void ApplyPresetDusk() {
ApplyPresetHD();
auto& s = getSettings();
s.game.enableAchievementNotifications.setValue(true);
s.game.enableQuickTransform.setValue(true);
s.game.instantSaves.setValue(true);
s.game.midnasLamentNonStop.setValue(true);
+17 -1
View File
@@ -191,6 +191,11 @@ namespace dusk {
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
"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");
@@ -288,6 +293,8 @@ namespace dusk {
config::ImGuiCheckbox("Infinite Oil", getSettings().game.infiniteOil);
config::ImGuiCheckbox("Infinite Oxygen", getSettings().game.infiniteOxygen);
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");
config::ImGuiCheckbox("Moon Jump (R+A)", getSettings().game.moonJump);
@@ -376,7 +383,15 @@ namespace dusk {
ImGui::SeparatorText("Camera");
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
config::ImGuiCheckbox("Free Camera", getSettings().game.freeCamera);
if (getSettings().game.freeCamera) {
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
config::ImGuiCheckbox("Invert Camera Y Axis", getSettings().game.invertCameraYAxis);
config::ImGuiSliderFloat("Free Camera Sensitivity", getSettings().game.freeCameraSensitivity, 0.5f, 2.0f, "%.1f");
} else {
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
}
ImGui::SeparatorText("Gyro");
@@ -437,6 +452,7 @@ namespace dusk {
void ImGuiMenuGame::drawInterfaceMenu() {
if (ImGui::BeginMenu("Interface")) {
config::ImGuiCheckbox("Achievement Notifications", getSettings().game.enableAchievementNotifications);
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
#if DUSK_ENABLE_SENTRY_NATIVE
+14 -4
View File
@@ -58,6 +58,8 @@ namespace dusk {
ImGui::EndDisabled();
}
ImGui::MenuItem("Achievements", nullptr, &m_showAchievements);
#if DUSK_CAN_OPEN_DATA_FOLDER
ImGui::Separator();
if (ImGui::MenuItem("Open Data Folder")) {
@@ -210,7 +212,7 @@ namespace dusk {
ImGui::Text("Link");
ImGuiStringViewText(
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"
);
@@ -222,7 +224,7 @@ namespace dusk {
ImGuiStringViewText(
player != nullptr
? fmt::format("Speed: {0}\n", player->speedF)
? fmt::format("Speed: {: .4f}\n", player->speedF)
: "Speed: ?\n"
);
@@ -230,7 +232,7 @@ namespace dusk {
ImGui::Text("Epona");
ImGuiStringViewText(
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"
);
@@ -242,7 +244,7 @@ namespace dusk {
ImGuiStringViewText(
horse != nullptr
? fmt::format("Speed: {0}\n", horse->speedF)
? fmt::format("Speed: {: .4f}\n", horse->speedF)
: "Speed: ?\n"
);
@@ -252,4 +254,12 @@ namespace dusk {
ImGui::End();
ImGui::PopFont();
}
void ImGuiMenuTools::ShowAchievements() {
m_achievementsWindow.draw(m_showAchievements);
}
void ImGuiMenuTools::notifyAchievement(std::string name) {
m_achievementsWindow.notify(std::move(name));
}
}
+6
View File
@@ -5,6 +5,7 @@
#include <string>
#include "imgui.h"
#include "ImGuiAchievements.hpp"
#include "ImGuiSaveEditor.hpp"
#include "ImGuiStateShare.hpp"
@@ -26,6 +27,8 @@ namespace dusk {
void ShowAudioDebug();
void ShowSaveEditor();
void ShowStateShare();
void ShowAchievements();
void notifyAchievement(std::string name);
private:
bool m_showDebugOverlay = false;
@@ -65,6 +68,9 @@ namespace dusk {
bool m_showStateShare = false;
ImGuiStateShare m_stateShare;
bool m_showAchievements = false;
ImGuiAchievements m_achievementsWindow;
};
}
+46
View File
@@ -22,6 +22,10 @@ typedef void (ImGuiPreLaunchWindow::*drawFunc)();
drawFunc drawTable[2] = {&ImGuiPreLaunchWindow::drawMainMenu, &ImGuiPreLaunchWindow::drawOptions};
static constexpr std::array<const char*, 5> skLanguageNames = {
"English", "German", "French", "Spanish", "Italian"
};
static constexpr std::array<SDL_DialogFileFilter, 2> skGameDiscFileFilters{{
{"Game Disc Images", "iso;gcm;ciso;gcz;nfs;rvz;wbfs;wia;tgc"},
{"All Files", "*"},
@@ -46,6 +50,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) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
if (error != nullptr) {
@@ -184,6 +199,20 @@ void ImGuiPreLaunchWindow::drawOptions() {
false);
}
// TODO: Only show if PAL disc selected?
// Language selection
auto selectedLanguage = getSettings().game.language.getValue();
if (ImGui::BeginCombo("Language", skLanguageNames[static_cast<u8>(selectedLanguage)])) {
for (u8 i = 0; i < skLanguageNames.size(); ++i) {
if (ImGui::Selectable(skLanguageNames[i])) {
getSettings().game.language.setValue(static_cast<GameLanguage>(i));
config::Save();
}
}
ImGui::EndCombo();
}
AuroraBackend configuredBackend = BACKEND_AUTO;
const std::string& configuredBackendId = getSettings().backend.graphicsBackend;
if (!try_parse_backend(configuredBackendId, configuredBackend)) {
@@ -216,6 +245,23 @@ void ImGuiPreLaunchWindow::drawOptions() {
if (configuredBackendId != m_initialGraphicsBackend) {
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();
}
+88 -30
View File
@@ -10,6 +10,7 @@
#include "d/d_item_data.h"
#include "d/d_meter2_info.h"
#include "d/d_save.h"
#include "d/actor/d_a_player.h"
#include <map>
@@ -579,20 +580,21 @@ namespace dusk {
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")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_CASUAL_e;
dMeter2Info_setCloth(dItemNo_WEAR_CASUAL_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
}
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")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_ZORA_e;
dMeter2Info_setCloth(dItemNo_WEAR_ZORA_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
}
if (ImGui::Selectable("Magic Armor")) {
statusA.mSelectEquip[0] = dItemNo_ARMOR_e;
dMeter2Info_setCloth(dItemNo_ARMOR_e, false);
daPy_getPlayerActorClass()->setClothesChange(0);
}
ImGui::EndCombo();
}
@@ -1345,6 +1347,51 @@ namespace dusk {
}
}
template <typename T>
concept FlagIter = requires(T t) {
++t;
--t;
t + 1;
t < t;
{ t->flagID } -> std::convertible_to<u16>;
};
template <typename T>
concept FlagTester = requires(T t, u16 flagID) {
{ t(flagID) } -> std::convertible_to<bool>;
};
static void sortByFlags(FlagIter auto begin, FlagIter auto end, FlagTester auto&& flagTester) {
if (begin == end) return;
FlagIter auto fullEnd = end;
// We want to find the location of where we can swap our `On` flags to.
// We're gonna put the `Off` bits first, and the `On` bits last. 0 < 1
// We can achieve this by skipping all the `On` bits at the end.
// backtrack until we find a bit that is off
while (begin < --end && flagTester(end->flagID)) {
// move the end pointer back while we find on bits
}
// end should now be pointing to a bit that is off
while (begin < end) {
// if there's a flag that's on
if (flagTester(begin->flagID)) {
// move it to the end
std::rotate(begin, begin + 1, fullEnd);
// move back the end of where we're checking
--end;
// begin will now point to the next piece of data
// because we've rotated the data >= begin to the left
} else {
// not on, check next flag
++begin;
}
}
}
void ImGuiSaveEditor::drawFlagsTab() {
if (ImGui::TreeNode("Current Region Flags")) {
dSv_memBit_c& membit = g_dComIfG_gameInfo.info.mMemory.mBit;
@@ -1444,30 +1491,41 @@ namespace dusk {
sort != nullptr && sort->SpecsCount > 0 &&
(sort->SpecsDirty || sort->Specs[0].ColumnIndex == COLUMN_FLAG))
{
auto column = sort->Specs->ColumnIndex;
const auto cmp = [&](const duskImguiEventFlagEntry& l,
const duskImguiEventFlagEntry& r) -> bool {
switch (column) {
case COLUMN_FLAG:
return (bool)event.isEventBit(l.flagID) <
(bool)event.isEventBit(r.flagID);
case COLUMN_NAME:
return l.flagName < r.flagName;
case COLUMN_LOC:
return l.location < r.location;
case COLUMN_DESC:
return l.description < r.description;
}
return false;
};
const auto column = sort->Specs[0].ColumnIndex;
const auto direction = sort->Specs[0].SortDirection;
if (direction == ImGuiSortDirection_Ascending) {
std::sort(std::begin(duskImguiEventFlags), std::end(duskImguiEventFlags), cmp);
// if we're sorting by flags, do special sort, regular sort is bad for sorting bools
// it can swap values that are the same, and that causes constant reordering
if (column == COLUMN_FLAG) {
const auto testEventFunc = [&event](u16 flag) -> bool { return event.isEventBit(flag); };
if (direction == ImGuiSortDirection_Ascending) {
sortByFlags(std::begin(duskImguiEventFlags),
std::end(duskImguiEventFlags), testEventFunc);
} else {
sortByFlags(std::rbegin(duskImguiEventFlags),
std::rend(duskImguiEventFlags), testEventFunc);
}
} else {
std::sort(std::rbegin(duskImguiEventFlags), std::rend(duskImguiEventFlags), cmp);
const auto cmp = [column](const duskImguiEventFlagEntry& l,
const duskImguiEventFlagEntry& r) -> bool {
switch (column) {
case COLUMN_NAME: return l.flagName < r.flagName;
case COLUMN_LOC: return l.location < r.location;
case COLUMN_DESC: return l.description < r.description;
default: return false;
}
};
if (direction == ImGuiSortDirection_Ascending) {
std::sort(std::begin(duskImguiEventFlags),
std::end(duskImguiEventFlags), cmp);
} else {
std::sort(std::rbegin(duskImguiEventFlags),
std::rend(duskImguiEventFlags), cmp);
}
}
sort->SpecsDirty = false;
}
@@ -1489,11 +1547,11 @@ namespace dusk {
}
ImGui::TableNextColumn();
ImGui::Text(e.flagName.c_str());
ImGuiStringViewText(e.flagName);
ImGui::TableNextColumn();
ImGui::Text(e.location.c_str());
ImGuiStringViewText(e.location);
ImGui::TableNextColumn();
ImGui::Text(e.description.c_str());
ImGuiStringViewText(e.description);
}
ImGui::EndTable();
}
+323 -30
View File
@@ -5,14 +5,24 @@
#include "imgui.h"
#include "fmt/format.h"
#include "absl/strings/escaping.h"
#include "nlohmann/json.hpp"
#include "d/d_com_inf_game.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>
namespace dusk {
using json = nlohmann::json;
#pragma pack(push, 1)
struct StateSharePacket {
char stageName[8];
@@ -23,9 +33,65 @@ struct StateSharePacket {
};
#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 = {};
strncpy(pkt.stageName, dComIfGp_getStartStageName(), 7);
pkt.roomNo = dComIfGp_getStartStageRoomNo();
@@ -40,26 +106,25 @@ void ImGuiStateShare::copyState() {
std::string compressed(bound, '\0');
compressed.resize(ZSTD_compress(compressed.data(), bound, raw.data(), raw.size(), 1));
std::string encoded = absl::Base64Escape(compressed);
ImGui::SetClipboardText(encoded.c_str());
m_statusMsg = "Copied to clipboard.";
return absl::Base64Escape(compressed);
}
bool ImGuiStateShare::pasteState() {
const char* clip = ImGui::GetClipboardText();
if (!clip || clip[0] == '\0') {
m_statusMsg = "Clipboard is empty.";
return false;
}
bool ImGuiStateShare::applyEncodedState(const std::string& encoded, const std::string& name) {
std::string decoded;
if (!absl::Base64Unescape(clip, &decoded)) {
if (!absl::Base64Unescape(encoded, &decoded)) {
m_statusMsg = "Invalid base64.";
return false;
}
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.";
return false;
}
@@ -75,45 +140,272 @@ bool ImGuiStateShare::pasteState() {
memcpy(&pkt, raw.data(), sizeof(pkt));
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;
if (spawnPoint == -1) {
dComIfGs_setRestartRoomParam(pkt.roomNo & 0x3F);
}
dComIfGp_setNextStage(pkt.stageName, spawnPoint, pkt.roomNo, pkt.layer);
m_pendingInfo = g_dComIfG_gameInfo.info;
dusk::getTransientSettings().stateShareLoadActive = true;
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;
}
void ImGuiStateShare::tickPendingApply() {
if (!m_pendingInfo.has_value() || dComIfGp_isEnableNextStage())
if (!m_pendingInfo.has_value() && !m_pendingSavedata.has_value()) {
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) {
if (dusk::IsGameLaunched)
if (dusk::IsGameLaunched) {
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;
}
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();
return;
}
if (!dusk::IsGameLaunched) ImGui::BeginDisabled();
if (ImGui::Button("Copy State")) copyState();
const bool gameRunning = dusk::IsGameLaunched;
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();
if (ImGui::Button("Import State")) pasteState();
if (!dusk::IsGameLaunched) ImGui::EndDisabled();
if (ImGui::Button("Import Clipboard")) {
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()) {
ImGui::Spacing();
@@ -125,8 +417,9 @@ void ImGuiStateShare::draw(bool& open) {
}
void ImGuiMenuTools::ShowStateShare() {
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare))
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare)) {
return;
}
m_stateShare.draw(m_showStateShare);
}
+29 -11
View File
@@ -4,21 +4,39 @@
#include "d/d_save.h"
#include <optional>
#include <string>
#include <vector>
namespace dusk {
class ImGuiStateShare {
public:
void draw(bool& open);
private:
void copyState();
bool pasteState();
void tickPendingApply();
struct SavedStateEntry {
std::string name;
std::string encoded;
};
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
+1
View File
@@ -19,6 +19,7 @@ constexpr const char* TP_GAME_IDS[] = {
constexpr const char* SUPPORTED_TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
"GZ2P01", // GCN PAL
};
template <size_t N>
+19 -2
View File
@@ -20,6 +20,8 @@ UserSettings g_userSettings = {
},
.game = {
.language { "game.language", GameLanguage::English },
// Quality of Life
.enableQuickTransform {"game.enableQuickTransform", false},
.hideTvSettingsScreen {"game.hideTvSettingsScreen", false},
@@ -40,9 +42,10 @@ UserSettings g_userSettings = {
// Preferences
.enableMirrorMode {"game.enableMirrorMode", false},
.invertCameraXAxis {"game.invertCameraXAxis", false},
.disableMainHUD {"game.disableMainHUD", false},
.pauseOnFocusLost {"game.pauseOnFocusLost", false},
.enableLinkDollRotation = {"game.enableLinkDollRotation", false },
.enableAchievementNotifications {"game.enableAchievementNotifications", false},
// Graphics
.bloomMode {"game.bloomMode", BloomMode::Classic},
@@ -67,6 +70,10 @@ UserSettings g_userSettings = {
.gyroDeadband {"game.gyroDeadband", 0.04f},
.gyroInvertPitch {"game.gyroInvertPitch", false},
.gyroInvertYaw {"game.gyroInvertYaw", false},
.freeCamera {"game.freeCamera", false},
.invertCameraXAxis {"game.invertCameraXAxis", false},
.invertCameraYAxis {"game.invertCameraYAxis", false},
.freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f},
// Cheats
.infiniteHearts {"game.infiniteHearts", false},
@@ -75,6 +82,7 @@ UserSettings g_userSettings = {
.infiniteOil{"game.infiniteOil", false},
.infiniteOxygen{"game.infiniteOxygen", false},
.infiniteRupees{"game.infiniteRupees", false},
.enableIndefiniteItemDrops {"game.enableIndefiniteItemDrops", false},
.moonJump{"game.moonJump", false},
.superClawshot{"game.superClawshot", false},
.alwaysGreatspin{"game.alwaysGreatspin", false},
@@ -97,7 +105,8 @@ UserSettings g_userSettings = {
.showPipelineCompilation {"backend.showPipelineCompilation", false},
.wasPresetChosen {"backend.wasPresetChosen", false},
.enableCrashReporting {"backend.enableCrashReporting", true},
.duskMenuOpen {"backend.duskMenuOpen", false}
.duskMenuOpen {"backend.duskMenuOpen", false},
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)}
}
};
@@ -120,6 +129,7 @@ void registerSettings() {
Register(g_userSettings.audio.enableReverb);
// Game
Register(g_userSettings.game.language);
Register(g_userSettings.game.enableQuickTransform);
Register(g_userSettings.game.hideTvSettingsScreen);
Register(g_userSettings.game.skipWarningScreen);
@@ -137,6 +147,8 @@ void registerSettings() {
Register(g_userSettings.game.sunsSong);
Register(g_userSettings.game.enableMirrorMode);
Register(g_userSettings.game.invertCameraXAxis);
Register(g_userSettings.game.invertCameraYAxis);
Register(g_userSettings.game.freeCameraSensitivity);
Register(g_userSettings.game.disableMainHUD);
Register(g_userSettings.game.pauseOnFocusLost);
Register(g_userSettings.game.bloomMode);
@@ -149,6 +161,8 @@ void registerSettings() {
Register(g_userSettings.game.canTransformAnywhere);
Register(g_userSettings.game.freeMagicArmor);
Register(g_userSettings.game.restoreWiiGlitches);
Register(g_userSettings.game.enableLinkDollRotation);
Register(g_userSettings.game.enableAchievementNotifications);
Register(g_userSettings.game.noMissClimbing);
Register(g_userSettings.game.noLowHpSound);
Register(g_userSettings.game.midnasLamentNonStop);
@@ -160,6 +174,7 @@ void registerSettings() {
Register(g_userSettings.game.infiniteOil);
Register(g_userSettings.game.infiniteOxygen);
Register(g_userSettings.game.infiniteRupees);
Register(g_userSettings.game.enableIndefiniteItemDrops);
Register(g_userSettings.game.moonJump);
Register(g_userSettings.game.superClawshot);
Register(g_userSettings.game.alwaysGreatspin);
@@ -173,6 +188,7 @@ void registerSettings() {
Register(g_userSettings.game.gyroSmoothing);
Register(g_userSettings.game.gyroInvertPitch);
Register(g_userSettings.game.gyroInvertYaw);
Register(g_userSettings.game.freeCamera);
Register(g_userSettings.backend.isoPath);
Register(g_userSettings.backend.graphicsBackend);
@@ -181,6 +197,7 @@ void registerSettings() {
Register(g_userSettings.backend.wasPresetChosen);
Register(g_userSettings.backend.enableCrashReporting);
Register(g_userSettings.backend.duskMenuOpen);
Register(g_userSettings.backend.cardFileType);
}
// Transient settings
-4
View File
@@ -1020,10 +1020,6 @@ void GXInitTexCacheRegion(GXTexRegion* region, GXBool is_32b_mipmap, u32 tmem_ev
// XXX, this should be some struct?
// GXRenderModeObj GXNtsc480IntDf;
//GXRenderModeObj GXNtsc480Int;
void GXPeekZ(u16 x, u16 y, u32* z) {
STUB_LOG();
*z = 0;
}
void GXReadXfRasMetric(u32* xf_wait_in, u32* xf_wait_out, u32* ras_busy, u32* clocks) {
STUB_LOG();
*xf_wait_in = 0;
+82
View File
@@ -0,0 +1,82 @@
#include "dusk/version.hpp"
#include "dusk/logging.h"
namespace dusk::version {
using namespace std::string_view_literals;
static bool versionInitialized;
static GameVersion gameVersion;
static DVDDiskID diskId;
void init() {
versionInitialized = true;
if (!DVDLowReadDiskID(&diskId, nullptr)) {
DuskLog.fatal("DVDLowReadDiskID failed to return instantly.");
}
std::string_view company(diskId.company, sizeof(diskId.company));
std::string_view game(diskId.gameName, sizeof(diskId.gameName));
if (company != "01"sv) {
DuskLog.fatal("Wrong company ID in disc: {}", company);
}
if (game == "GZ2E"sv) {
gameVersion = GameVersion::GcnUsa;
} else if (game == "GZ2P") {
gameVersion = GameVersion::GcnPal;
} else {
// TODO: Handle remaining valid versions.
DuskLog.fatal("Unknown/unsupported game version in disc: {}", game);
}
DuskLog.info("Loaded game disc is {}{}", game, company);
}
bool isGcn() {
return getGameVersion() == GameVersion::GcnUsa
|| getGameVersion() == GameVersion::GcnPal
|| getGameVersion() == GameVersion::GcnJpn;
}
bool isWii() {
return getGameVersion() == GameVersion::WiiUsaRev0
|| getGameVersion() == GameVersion::WiiUsa
|| getGameVersion() == GameVersion::WiiPal
|| getGameVersion() == GameVersion::WiiJpn
|| getGameVersion() == GameVersion::WiiKor;
}
bool isPalOrAtLeastWiiR2() {
return getGameVersion() == GameVersion::GcnPal || getGameVersion() >= GameVersion::WiiUsa;
}
bool isRegionJpn() {
return getGameVersion() == GameVersion::WiiJpn || getGameVersion() == GameVersion::GcnJpn;
}
bool isRegionPal() {
return getGameVersion() == GameVersion::WiiPal || getGameVersion() == GameVersion::GcnPal;
}
bool isRegionUsa() {
return getGameVersion() == GameVersion::WiiUsa || getGameVersion() == GameVersion::WiiUsaRev0
|| getGameVersion() == GameVersion::GcnUsa;
}
GameVersion getGameVersion() {
if (!versionInitialized) {
abort();
}
return gameVersion;
}
const DVDDiskID& getDiskID() {
return diskId;
}
} // namespace dusk::version
+3
View File
@@ -23,6 +23,7 @@
#include "m_Do/m_Do_graphic.h"
#include "m_Do/m_Do_main.h"
#include "tracy/Tracy.hpp"
#include <dusk/gamepad_color.h>
fapGm_HIO_c::fapGm_HIO_c() {
mUsingHostIO = true;
@@ -734,6 +735,8 @@ static void fapGm_AfterRecord() {
}
static void duskExecute() {
handleGamepadColor();
if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigX(PAD_1)) {
if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) {
dynamic_cast<daAlink_c*>(link)->handleWolfHowl();
+8 -1
View File
@@ -12,6 +12,7 @@
#include "os_report.h"
#include "dusk/os.h"
#include "dusk/main.h"
#include "dusk/version.hpp"
#if PLATFORM_WII || PLATFORM_SHIELD
#include <revolution/nand.h>
@@ -77,7 +78,13 @@ static OSThread MemCardThread;
void mDoMemCd_Ctrl_c::ThdInit() {
#if !PLATFORM_SHIELD
CARDInit(DUSK_GAME_NAME, DUSK_GAME_VERSION);
CARDSetLoadType((CARDFileType)dusk::getSettings().backend.cardFileType.getValue());
char version[5] = {};
char maker[3] = {};
std::memcpy(version, dusk::version::getDiskID().gameName, 4);
std::memcpy(maker, dusk::version::getDiskID().company, 2);
CARDInit(version, maker);
#endif
mCopyToPos = 0;
+35 -1
View File
@@ -11,6 +11,8 @@
#include <cstdio>
#include <cstring>
#include "dusk/version.hpp"
#if VERSION == VERSION_GCN_JPN
#define HEADER_TITLE "ゼルダの伝説 トワイライトプリンセス"
#define HEADER_COMMENT "%d月%d日のセーブデータです"
@@ -313,12 +315,44 @@ s32 mDoMemCdRWm_StoreBannerNAND(NANDFileInfo* file) {
#endif
static void mDoMemCdRWm_BuildHeader(mDoMemCdRWm_HeaderData* header) {
#if !TARGET_PC
snprintf(header->mTitle, sizeof(header->mTitle), HEADER_TITLE);
#endif
OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time);
#if VERSION == VERSION_GCN_PAL
#if TARGET_PC
if (dusk::version::isRegionPal()) {
snprintf(header->mTitle, sizeof(header->mTitle), HEADER_TITLE);
switch (dComIfGs_getPalLanguage()) {
case dSv_player_config_c::LANGUAGE_ENGLISH:
snprintf(header->mComment, sizeof(header->mComment), "%d/%d Save Data", time.mon + 1, time.mday);
break;
case dSv_player_config_c::LANGUAGE_GERMAN:
snprintf(header->mComment, sizeof(header->mComment), "%d/%d Spielstand", time.mday, time.mon + 1);
break;
case dSv_player_config_c::LANGUAGE_FRENCH:
snprintf(header->mComment, sizeof(header->mComment), "Donn%ces de jeu %d/%d", 0xE9, time.mday, time.mon + 1);
break;
case dSv_player_config_c::LANGUAGE_SPANISH:
snprintf(header->mComment, sizeof(header->mComment), "Datos guardados el %d/%d", time.mday, time.mon + 1);
break;
case dSv_player_config_c::LANGUAGE_ITALIAN:
snprintf(header->mComment, sizeof(header->mComment), "Dati salvati: %d/%d", time.mday, time.mon + 1);
break;
}
} else if (dusk::version::isRegionUsa()) {
snprintf(header->mTitle, sizeof(header->mTitle), HEADER_TITLE);
snprintf(header->mComment, sizeof(header->mComment), HEADER_COMMENT, time.mon + 1, time.mday);
} else {
// TODO JPN SHIFT-JIS
// snprintf(header->mTitle, sizeof(header->mTitle), "ゼルダの伝説 トワイライトプリンセス");
// snprintf(header->mComment, sizeof(header->mComment), "%d月%d日のセーブデータです", time.mon + 1, time.mday);
}
#elif VERSION == VERSION_GCN_PAL
switch (dComIfGs_getPalLanguage()) {
case dSv_player_config_c::LANGUAGE_ENGLISH:
snprintf(header->mComment, sizeof(header->mComment), "%d/%d Save Data", time.mon + 1, time.mday);

Some files were not shown because too many files have changed in this diff Show More