Compare commits

..

213 Commits

Author SHA1 Message Date
Tom Lube 9f0d89473a typo (cannot) 2026-08-24 18:59:28 -04:00
gymnast86 adaa52aa9d Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-24 02:20:36 -07:00
gymnast86 3dd9430576 implement goron shop checks 2026-08-24 02:20:28 -07:00
Luke Street 6facff1297 Implement foolish items 2026-08-23 23:46:07 -06:00
Luke Street ba324dfdf6 Merge remote-tracking branch 'origin/main' into rando-mod 2026-08-23 22:40:06 -06:00
Luke Street a775418c66 ItemService v2.1: Support display_item (#2344)
* ItemService: Support display_item

* Add goron_springwater_rush constant

* Commit f_op_actor_mng.h
2026-08-23 21:51:49 -06:00
Luke Street 48b5c2e8c1 Restore focus to re-enabled elements 2026-08-23 20:26:26 -06:00
Luke Street 705e7f058b Un-inline d_meter2_info functions 2026-08-23 20:26:26 -06:00
Luke Street e30e0fe062 Update symgen to v1.3.3 2026-08-23 20:26:26 -06:00
Luke Street c4f4712d0e Minor svc/flow.hpp updates 2026-08-23 20:26:26 -06:00
Luke Street 2d70c24b37 Improve mod hook compatibility (inlines) 2026-08-23 20:26:26 -06:00
Luke Street 72ccb14d35 Guard against null stag_info 2026-08-23 20:25:41 -06:00
Luke Street c53f3f774b Add Golden Wolf item checks 2026-08-23 20:25:41 -06:00
Luke Street 5a8622c6b2 Fix forced Poe item demo 2026-08-23 20:25:24 -06:00
Luke Street 29b86f938f Fix group lookup for custom flows 2026-08-23 20:22:12 -06:00
Luke Street e02638ac6d Fix custom flow entry nodes 2026-08-23 20:22:10 -06:00
Luke Street 622b782614 Excluded locations using virtualized list 2026-08-23 14:37:09 -06:00
Luke Street 9a4b1edfe5 Merge remote-tracking branch 'origin/main' into rando-mod 2026-08-23 14:37:04 -06:00
Luke Street 1db2a74192 UiService v2.1: Virtualized lists (#2342)
* UiService: Add `pane_add_list`

* Fix CSS scoping
2026-08-23 14:33:36 -06:00
gymnast86 595c621173 add fused shadow and mirror shard item icons 2026-08-23 00:42:34 -07:00
gymnast86 ba95fdd4ab fix arg index in hookPostReadItemTexture 2026-08-23 00:01:14 -07:00
Luke Street a39c272985 Restore focus to re-enabled elements 2026-08-23 00:09:44 -06:00
gymnast86 f3bbde4b7d Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-22 23:05:08 -07:00
TakaRikka 46a2ac2c46 Merge remote-tracking branch 'origin/rando-mod' into rando-mod 2026-08-22 23:01:39 -07:00
TakaRikka 2885c7d9d8 disable OK button while seed is generating 2026-08-22 23:01:09 -07:00
Luke Street 19b8f7ede0 Link dusklight_mod_api to generator 2026-08-23 00:00:10 -06:00
gymnast86 b138735cbe Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-22 22:50:44 -07:00
gymnast86 1a6775eab1 add support for JP iso 2026-08-22 22:50:06 -07:00
TakaRikka dd5da64332 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-22 22:48:00 -07:00
TakaRikka d281c34a65 Merge pull request #2338 from encounter/ui-v2
UiService v2: UiDialogAction is_disabled
2026-08-22 22:46:21 -07:00
Luke Street a6813841b8 Use full color extensions 2026-08-22 23:44:55 -06:00
Luke Street 39a84e67b7 Merge remote-tracking branch 'origin/main' into rando-mod 2026-08-22 23:36:24 -06:00
Luke Street 788700daa6 UiService v2: UiDialogAction is_disabled 2026-08-22 23:17:51 -06:00
Luke Street 3aa54ca170 UiService: Add is_selected for buttons (#2337) 2026-08-22 23:16:32 -06:00
TakaRikka 81dfe701e9 Merge pull request #2336 from encounter/full-color
Add full color extension to messages
2026-08-22 21:35:55 -07:00
Luke Street 16f2b954fa Add full color extension to messages 2026-08-22 21:55:14 -06:00
gymnast86 a8a7513710 recolor dungeon map screen A and B buttons 2026-08-22 16:14:05 -07:00
gymnast86 797d264a71 don't allow deleting seeds which are currently saved to files 2026-08-22 10:36:43 -07:00
TakaRikka c0293b1f52 redesign delete seeds option 2026-08-22 05:00:18 -07:00
TakaRikka a9ea8aa935 redesign preset load option 2026-08-22 03:56:34 -07:00
TakaRikka 7240c19566 preset options added 2026-08-22 02:41:07 -07:00
TakaRikka dbc56da290 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-22 00:21:56 -07:00
Luke Street 41d5148793 UiService: Dialog controls (#2332) 2026-08-21 23:31:35 -06:00
doop 14f9b6c762 Add setting for UI scale (#2326) 2026-08-21 23:29:43 -06:00
doop 44e02a9d53 Fix Clang builds crashing at launch (#2325)
Closes #2323.
2026-08-21 23:28:52 -06:00
Luke Street 63d431d058 Guard against null stag_info 2026-08-21 23:24:09 -06:00
Luke Street 87d96c2696 Fix progressive item lookup 2026-08-21 23:24:09 -06:00
Luke Street 8f5ecdba1c Fix Golden Wolf item persistence 2026-08-21 23:24:09 -06:00
Luke Street 42805210df Fix forced Poe item handoff 2026-08-21 23:24:09 -06:00
gymnast86 df522f1abb add hooks for custom text colors 2026-08-21 18:46:47 -07:00
gymnast86 c475d72110 fix line width calculation in breakLines 2026-08-21 18:30:48 -07:00
gymnast86 3606ec64e3 fix lag on play tab 2026-08-21 18:29:59 -07:00
gymnast86 1b51fa0112 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-21 15:53:02 -07:00
Luke Street 1e8d72c3ed Fix group lookup for custom flows 2026-08-21 16:50:09 -06:00
gymnast86 28479314c2 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-21 14:56:28 -07:00
Luke Street 89a8384c7c Fix custom flow entry nodes 2026-08-21 15:55:38 -06:00
gymnast86 d9cac5befa Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-21 14:53:46 -07:00
gymnast86 3cd1f46e7b add additional descirpiton info to Skip Midna's Desperate Hour 2026-08-21 14:52:58 -07:00
Luke Street 0b26cee96d Fix hookPreGetLayerNo not writing the layerNo 2026-08-21 15:46:29 -06:00
gymnast86 42bba9c9a8 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-21 13:17:23 -07:00
Luke Street 3869ac6423 Un-inline d_meter2_info functions 2026-08-21 14:14:12 -06:00
Luke Street 54bed771ee Update symgen to v1.3.3 2026-08-21 14:14:03 -06:00
gymnast86 83b10681c3 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-21 11:24:38 -07:00
Luke Street 641855e49e Fix randomizer seed serialization 2026-08-21 12:15:33 -06:00
Luke Street 9977e78758 Improve game function hook compatibility 2026-08-21 12:15:33 -06:00
gymnast86 9a62d8c86f Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-21 10:49:27 -07:00
Luke Street 2cee0db91f Fix randomizer seed generation and activation 2026-08-21 11:16:26 -06:00
Luke Street 932c3aaba7 Rework flow impl on top of FlowService 2026-08-21 01:43:22 -06:00
Luke Street 5d1dda7e94 Bundle randomizer mod 2026-08-21 01:07:26 -06:00
Luke Street 75ae2767e9 Rework to use updated ItemService 2026-08-21 00:54:00 -06:00
Luke Street 829b0de69c Merge origin/main into rando-mod
# Conflicts:
#	src/d/actor/d_a_b_ds.cpp
2026-08-21 00:52:11 -06:00
Luke Street 5f0f3d4ebe ItemService improvements (#2322)
* ItemService improvements

* Move Poe increment to item
2026-08-21 00:50:51 -06:00
gymnast86 283b3704bb Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-20 19:59:19 -07:00
gymnast86 6cc22e71a9 fix boomerang collision field model radius and height 2026-08-20 19:58:59 -07:00
gymnast86 54ecdfe4d3 fix hookPreObjLifeCalcScale 2026-08-20 19:53:41 -07:00
TakaRikka 618eb90392 layer handling fix 2026-08-20 19:09:33 -07:00
TakaRikka 72ea47b973 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-20 18:15:25 -07:00
gymnast86 172d06f38d Add Built-In Cosmetics Mod (#2291)
* template setup

* get most colors working

* add lantern, midna, and heart colors

* fix PaneCache naming conflict

* add UI colors

* fix pane cache again

* override colors for items on other models

* recolor heart piece models

* cleanup

* add support for ordon sword recoloring

* recolor epona's eyelids

* move option descriptions

* fix mod.json

* constexpr descriptions

* gx.h instead of dolphin/gx/GXstruct.h

* fix default midna hair color

* use TextureService instead of overwriting model file data on load

* update descriptions

* manual cleanup

* fix includes

* remove unneeded include and const qualifier

* UI: Add Popover, ColorInput & more; mod cleanup

* Make it a bundled mod

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-08-20 17:49:26 -06:00
doop 338995ebe0 Fix interpretation of needed in audio callback (#2312) 2026-08-20 17:18:07 -06:00
ZipoLabs b5724fb6fd Add "Button Fishing" casting when lure fishing (#2226) 2026-08-20 17:13:00 -06:00
TakaRikka 05a1fe4485 some cleanup 2026-08-20 03:16:26 -07:00
TakaRikka 6f06a33926 only register ui changes during game mode 2026-08-20 02:37:31 -07:00
gymnast86 4d5fc47252 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-20 01:38:17 -07:00
TakaRikka 97d742a96a forgot to include tex oops 2026-08-20 01:26:05 -07:00
TakaRikka 80c93957e9 Merge remote-tracking branch 'origin/rando-mod' into rando-mod 2026-08-20 01:23:10 -07:00
TakaRikka dc716329d8 rando logo tex replacement 2026-08-20 01:22:49 -07:00
gymnast86 62bf0fbeb1 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-20 01:19:27 -07:00
gymnast86 ce4682fd89 Z2SceneMgr hooks 2026-08-20 01:19:19 -07:00
jdflyer acc8cc2e6d Fix signature on dMenu_Ring_c__destructor, Add play menu sound 2026-08-20 00:50:30 -07:00
TakaRikka c86d6918d6 hook up game mode service 2026-08-20 00:22:01 -07:00
jdflyer 5a4e6f3254 Mod SDK: Arc Overlays (#2280)
* Mod SDK: Arc Overlays

* Use DVD Functions for Arc overlays

* Re-add Fetchcontent for json.hpp

* Fix build for actions

* Make sure buffer usues aligned length

* Add include so msvc compiles

* Arc Overlays fixes: remove _arc suffix, handle getting overlayed file size from arcs, copy data correctly, etc.

* Reload overlayed data during loading screens

* Rename sync function

* Free overlayed files on load instead of re-loading them

* Rework arc overlays & load overlay files into host memory

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-08-20 00:47:57 -06:00
TakaRikka 5d5c3d5c5f Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-19 22:52:32 -07:00
TakaRikka 386d2f964b Merge pull request #2230 from jdflyer/main
Mod SDK: Game Mode Service
2026-08-19 22:51:24 -07:00
TakaRikka 04b915d8c7 castle barrier / big key requirement settings 2026-08-19 22:50:28 -07:00
Luke Street fd4a5f0cfb Use carousel button on prelaunch; more cleanup 2026-08-19 22:58:43 -06:00
TakaRikka 33abcbfbe1 maybe fix 2026-08-19 20:44:50 -07:00
Luke Street 3ecd785ac2 Rework UI lifecycle, finish renaming & cleanup 2026-08-19 21:43:41 -06:00
Luke Street fd0e33460f Gamemode -> GameMode 2026-08-19 20:49:58 -06:00
TakaRikka 6f840b050e permalink copy/paste working 2026-08-19 19:49:26 -07:00
TakaRikka 88c6e28179 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-19 19:37:57 -07:00
TakaRikka f13f0319ec Merge pull request #2318 from TwilitRealm/uisvc-clipboard
ui service: clipboard setter/getter functions
2026-08-19 19:36:36 -07:00
TakaRikka 06735d1369 c++ wrappers 2026-08-19 19:14:07 -07:00
TakaRikka 8525b0b40c enc's suggestions 2026-08-19 19:06:53 -07:00
TakaRikka e40506dfeb actually set outLength 2026-08-19 18:17:07 -07:00
TakaRikka ff3dbfa8d5 couple fixes 2026-08-19 18:15:42 -07:00
TakaRikka 5456d98fde add clipboard setter/getter functions 2026-08-19 16:18:00 -07:00
jdflyer 2f0cb26ceb Small fixes 2026-08-19 00:19:11 -07:00
jdflyer 21bc055552 Merge with origin/main 2026-08-19 00:16:37 -07:00
TakaRikka 3a2dbf9fba fix new build issues 2026-08-18 23:47:53 -07:00
TakaRikka 2d1487aefd Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-18 23:39:05 -07:00
Luke Street f6b7448de3 FlowService & MessageService (#2313) 2026-08-19 00:32:23 -06:00
jdflyer edfb93440c Prelaunch UI Fixes. Create Prelaunch after mod init. 2026-08-18 21:04:41 -07:00
gymnast86 f4f564b1b1 rework phase_1 hooks 2026-08-18 17:56:39 -07:00
gymnast86 4ba25932de remove unecessary setLineUpItem check 2026-08-18 16:30:54 -07:00
gymnast86 d4868ea81a dEvt_control_c::skipper hook 2026-08-18 16:13:30 -07:00
gymnast86 cd6c82d0ff forgot uninstall 2026-08-18 11:49:46 -07:00
gymnast86 355ea73510 getCollectSmell hook 2026-08-18 11:21:46 -07:00
jdflyer c0d44d4a21 Gamemode service improvements. Disable achievements on custom gamemodes (for now) 2026-08-18 00:33:15 -07:00
TakaRikka f0f7031b3d ui: basic excluded locations tab 2026-08-18 00:24:15 -07:00
jdflyer 85c2ddd8ff Merge with origin/main 2026-08-17 22:52:28 -07:00
jdflyer de7f09a6a4 Entrance Rando Fixes 2026-08-17 20:05:39 -07:00
TakaRikka 7129605035 forgot sound effect 2026-08-17 18:44:08 -07:00
TakaRikka 0c09d71c0a ui: starting inventory implemented 2026-08-17 18:32:10 -07:00
TakaRikka 947fb5b5a5 fix demo item gives and add obj item hooks 2026-08-17 02:12:25 -07:00
gymnast86 4c673eb04b dMenu_Ring_c hooks 2026-08-17 01:54:00 -07:00
gymnast86 9090ee80ab fix building in debug on windows 2026-08-17 01:49:44 -07:00
jdflyer 8953532fe7 OnNewSaveSelectFunction fix (Don't show name anm) 2026-08-16 18:41:07 -07:00
gymnast86 f1dc848ea5 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-16 17:58:06 -07:00
TakaRikka 548c843a9b remove unneeded mode dialog 2026-08-16 14:53:03 -07:00
TakaRikka 34a753ed85 make save_slot_written update current slot id 2026-08-16 14:08:09 -07:00
TakaRikka 08e51f83f3 item func changes 2026-08-15 23:38:29 -07:00
TakaRikka b4ba0b5755 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-15 16:54:35 -07:00
jdflyer 7c4338d7b3 Merge with origin/rando-mod 2026-08-15 16:43:10 -07:00
jdflyer 85a1a2901c Improve Entrance Coupling Logic, Improve Entrance Shuffle Data, fix seed name 2026-08-15 16:42:46 -07:00
TakaRikka a13932e901 partial ms check fix 2026-08-15 15:32:09 -07:00
Irastris c880d46fb5 d_menu_option bugfix (#2294) 2026-08-15 11:58:24 -06:00
Irastris 27d27e6d94 Add support for playing Dusklight using Wii disc images (#2296)
* Add offsets for Wii USA R0/R2, JPN, and PAL
Also fix an oopsie where l_pos and l_normal's offsets were swapped

* Allow loading Wii discs

* Change GameVersion::GcnJpn checks to isRegionJpn()

* Change GameVersion::GcnPal checks to isRegionPal()

* Add platformSelect and regionSelect templates, simplify a ton of code

* Add *_get() macros to simplify REL loads

* Actually accept Wii USA Rev. 2 discs

* Show the Wii USA revision in prelaunch's disc status

* l_normal isn't in Wii RELs, so I'll just hardcode it

* Account for various BLO differences

Fix crash when opening the map on Wii USA Rev 0 and Wii PAL

* Use the streamed version of the file select music

* Update language selection

* Fix the d-pad/cross button text for Wii PAL
Resizing panes that are already 120.0f wide is harmless, so I didn't bother with a GameVersion check

* Fix brightness check discrepancy, use IF_DUSK_BLOCK macros when ideal

* Update docs and the issue template
2026-08-15 11:58:09 -06:00
Irastris ac45cac56e Fix Graphics Backend option value (#2295)
* Graphics backend setting bugfix

* Better solution
2026-08-15 11:52:54 -06:00
Luke Street 9d38af22ee GfxService v1.2 + fixes (#2305)
* GfxService v1.2 + fixes

* gfx::synchronize before mod_shutdown
2026-08-15 11:50:13 -06:00
gymnast86 7cac8284d7 Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-15 05:24:56 -07:00
TakaRikka aa3db8f949 more hooks 2026-08-15 04:57:14 -07:00
TakaRikka 9f7360b359 mg_rod hooks 2026-08-15 03:20:06 -07:00
TakaRikka d319bb1049 demo item / bosswarp hooks 2026-08-15 02:00:10 -07:00
TakaRikka 1f43e16e2d more hooks wip 2026-08-14 23:41:12 -07:00
TakaRikka 2a67d6800e Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-14 19:17:16 -07:00
TakaRikka 4cbb8762f6 update aurora 2026-08-14 19:16:49 -07:00
TakaRikka c722cb1be0 update aurora 2026-08-14 19:12:14 -07:00
TakaRikka c2dbdd385d Merge remote-tracking branch 'origin/rando-mod' into rando-mod 2026-08-14 18:57:21 -07:00
TakaRikka 9c52a059bd alink demo and uzr portal hooks 2026-08-14 18:56:55 -07:00
jdflyer 33a9db2e49 Finish Implementing Entrance Rando 2026-08-14 18:26:37 -07:00
jdflyer e5a4323056 Merge with origin/rando-mod 2026-08-14 12:57:26 -07:00
jdflyer 08cda98337 Basic Entrance Rando 2026-08-14 12:56:40 -07:00
TakaRikka b0d4c62903 more hooks wip 2026-08-14 03:26:49 -07:00
TakaRikka a2cd701203 ykm/ykw/fairy hooks 2026-08-13 23:32:21 -07:00
Luke Street 0f5a248401 Time revamp (#2304)
* Don't Use OSCalendarTime for Speedrun Timing

- Shouldn't rely on OSTicksToCalendarTime since it will be changed to handle time zone conversion, and that doesn't make sense on elapsed time

* Use OSGetSystemTime Extension

- Fixes desyncing issues with save file time and a couple other odd instances, particularly on mobile platforms that suspend apps

* Time revamp

* Update aurora

* Split IGT/RTA calculations

* Shift-Turbo to slow down

---------

Co-authored-by: SuperDude88 <82904174+SuperDude88@users.noreply.github.com>
2026-08-14 00:07:54 -06:00
matthewdavidrichardanderson 045fbd9b46 request presentation sync for all captures (#2293)
Co-authored-by: matthewdavidrichardanderson <matthewdavidrichardanderson@mfb.com>
2026-08-13 23:54:29 -06:00
doop 46da33be37 Use texture replacement API for HQ minimap textures (#2303) 2026-08-13 23:44:36 -06:00
TakaRikka 8143de6bbb more hooks 2026-08-13 20:10:44 -07:00
TakaRikka 162829a8cf hook up randomizerState 2026-08-13 16:28:42 -07:00
TakaRikka c65b8a42b8 add handful of missing hooks 2026-08-13 14:50:39 -07:00
TakaRikka abbcb5a6b9 location name overrides for item service 2026-08-13 03:41:39 -07:00
TakaRikka 8229879aab refactor seed load/save and add file info hook 2026-08-13 02:04:41 -07:00
TakaRikka 11055282ff hook up item check overriding 2026-08-12 19:42:57 -07:00
TakaRikka 839d8fdb63 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-08-12 16:35:16 -07:00
TakaRikka 008a18c142 Merge pull request #2292 from TwilitRealm/item-svc
initial item service implementation
2026-08-12 15:34:45 -07:00
TakaRikka 5b4def820a couple fixes 2026-08-12 15:02:33 -07:00
TakaRikka 635901d3bd update game service major version 2026-08-12 13:59:01 -07:00
TakaRikka 5abc956448 daTbox2 handling 2026-08-11 14:45:53 -07:00
TakaRikka 5830c41547 couple ui fixes 2026-08-10 20:33:25 -07:00
TakaRikka 0c3bbbaba0 standardize some check names 2026-08-10 12:35:08 -07:00
gymnast86 1e39c5e1be Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-10 06:21:38 -07:00
gymnast86 2f1375297a Merge branch 'main' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-10 06:13:43 -07:00
TakaRikka 71b2eedc82 initial item service implementation 2026-08-09 16:40:26 -07:00
jdflyer 315897e45a Gamemode service fixes 2026-08-08 17:12:38 -07:00
TakaRikka 25a736b9dc getLayerNo hook 2026-08-08 13:27:12 -07:00
TakaRikka a8089f1204 couple small fixes 2026-08-08 11:25:00 -07:00
TakaRikka 1f28fd1ca5 sky character blob and stage edit registration 2026-08-08 05:38:42 -07:00
TakaRikka e16c05211b seed selection + rando file setup working 2026-08-07 19:42:50 -07:00
jdflyer 8be95d3aa3 Allow Gamemodes to interrupt flow on file creation 2026-08-07 18:46:52 -07:00
jdflyer ce6850acd3 Fix small issues, update example in modding.md to show scoped hooks 2026-08-07 15:27:23 -07:00
jdflyer 4aa3b647a3 Merge with origin/main 2026-08-07 13:57:48 -07:00
TakaRikka a9767ad3e8 use persistent data directory 2026-08-07 11:35:50 -07:00
TakaRikka 2f4d8a06d8 remove unnecessary hooks 2026-08-07 11:15:36 -07:00
TakaRikka 769f83db25 add in hooks 2026-08-07 07:46:43 -07:00
TakaRikka 48cf860467 Merge branch 'main' of https://www.github.com/TwilitRealm/dusklight into rando-mod 2026-08-07 03:49:10 -07:00
TakaRikka cb564f7465 ui: initial file select gate implementation 2026-08-06 18:00:16 -07:00
TakaRikka cd2ed2843c ui: begin hooking up some menu controls 2026-08-06 03:49:26 -07:00
TakaRikka c853934a24 Merge branch 'main' of https://www.github.com/TwilitRealm/dusklight into rando-mod 2026-08-05 16:32:21 -07:00
TakaRikka 8c4ad273b8 ui: blocking in seed options and hints tabs 2026-08-05 16:02:40 -07:00
TakaRikka 2a0cd87246 ui: start implementing menu tabs and controls 2026-08-05 14:27:59 -07:00
TakaRikka 8d677bf517 ui: wip porting main ui 2026-08-05 04:43:55 -07:00
TakaRikka 3160d1c370 ui: config_store/seed_generation 2026-08-05 03:04:06 -07:00
TakaRikka 0ed984a863 use new log fmt wrappers 2026-08-05 00:43:23 -07:00
TakaRikka 027e23d2e7 include save/stage services 2026-08-05 00:19:28 -07:00
TakaRikka 105413360e Merge branch 'main' of https://www.github.com/TwilitRealm/dusklight into rando-mod 2026-08-05 00:13:24 -07:00
gymnast86 d82cb08353 another include 2026-08-04 08:47:00 -07:00
gymnast86 572e9633ab fix more includes 2026-08-04 08:21:23 -07:00
gymnast86 67a511a131 fix includes 2026-08-04 08:00:25 -07:00
gymnast86 0ff615f991 more code -> flow changes 2026-08-04 07:56:05 -07:00
gymnast86 2191b38c1d Merge branch 'main' of https://github.com/TwilitRealm/dusk into rando-mod 2026-08-04 07:13:08 -07:00
gymnast86 36d31e2bb8 update data for flow node usage 2026-08-03 14:15:35 -07:00
gymnast86 2b489e1a7d add logic tests executable 2026-08-02 01:48:58 -07:00
gymnast86 e93a720d8f update src files with hint stuff 2026-08-02 01:37:12 -07:00
gymnast86 66f6942dbb update generator with hint stuff 2026-08-02 01:25:06 -07:00
gymnast86 631cb99208 add standalone generator for easier generator testing 2026-08-02 00:58:52 -07:00
TakaRikka a53b363905 Merge branch 'main' of ssh://github.com/TwilitRealm/dusklight into rando-mod 2026-07-30 05:45:18 -07:00
TakaRikka fae9add351 generator / basic mod structure building 2026-07-27 22:33:03 -07:00
jdflyer f22bfcd9ba Add restart to prelaunch button to settings, remove gamemodeStub functions 2026-07-26 00:25:59 -07:00
jdflyer 9c2e0e9331 Fix typo on std::tolower 2026-07-20 23:22:46 -07:00
jdflyer ecbec503dc Merge with origin/main 2026-07-20 17:24:48 -07:00
jdflyer 91c6c5c137 Fix Issues with mod resetting, make gamemode ids lowercase, update modding.md 2026-07-20 17:18:23 -07:00
jdflyer 449a8408ab Correctly unregister gamemodes from the service 2026-07-19 20:01:46 -07:00
jdflyer 61a4ab3117 Mod SDK: Gamemode Service, Change Speedrun mode to a gamemode, Allow prelaunch to be restarted 2026-07-19 00:50:49 -07:00
552 changed files with 99662 additions and 6295 deletions
+10 -5
View File
@@ -68,13 +68,18 @@ body:
required: true
- type: dropdown
id: game-region
id: game-disc
attributes:
label: Game Region
description: The game region you are playing on
label: Game Disc
description: The game disc you are playing on
options:
- NTSC-U (North America)
- PAL (Europe)
- GameCube NTSC-U (North America)
- GameCube PAL (Europe)
- GameCube NTSC-J (Japan)
- Wii NTSC-U (North America) Revision 0
- Wii NTSC-U (North America) Revision 2
- Wii PAL (Europe)
- Wii NTSC-J (Japan)
validations:
required: true
+1 -1
View File
@@ -1,6 +1,6 @@
[submodule "extern/aurora"]
path = extern/aurora
url = https://github.com/Lurs/aurora.git
url = https://github.com/encounter/aurora.git
[submodule "extern/borealis"]
path = extern/borealis
url = https://github.com/encounter/borealis.git
+48 -5
View File
@@ -305,7 +305,7 @@ find_package(Threads REQUIRED)
set(GAME_COMPILE_DEFS DUSK_BUILDING_GAME=1)
set(GAME_LIBS aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd
aurora::card borealis::cli borealis::crash borealis::data borealis::disc borealis::discord borealis::file_select borealis::log borealis::presentation borealis::sentry borealis::update freeverb cxxopts::cxxopts absl::flat_hash_map nlohmann_json::nlohmann_json TracyClient fmt::fmt
Threads::Threads zstd::libzstd dusklight_game_headers ZLIB::ZLIB)
Threads::Threads zstd::libzstd dusklight_game_headers)
if (DUSK_HAS_FUNCHOOK)
list(APPEND GAME_LIBS funchook-static)
endif ()
@@ -411,7 +411,47 @@ if (DUSK_ENABLE_CODE_MODS)
setup_symbol_manifest(dusklight)
endif ()
# Hook reliability: guaranteed patchable entries on the game ABI surface, and no identical-code folding.
# Hook reliability: prevent auto-inlining, guarantee patchable function entries,
# and disable identical-code folding for game functions.
set(DUSK_GAME_ABI_INLINE_OPTIONS)
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND DUSK_GAME_ABI_INLINE_OPTIONS
$<$<COMPILE_LANGUAGE:CXX>:-finline-hint-functions>)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND DUSK_GAME_ABI_INLINE_OPTIONS
$<$<COMPILE_LANGUAGE:CXX>:-flive-patching=inline-clone>
$<$<COMPILE_LANGUAGE:CXX>:-fno-inline-functions>
$<$<COMPILE_LANGUAGE:CXX>:-fno-inline-small-functions>
$<$<COMPILE_LANGUAGE:CXX>:-fno-inline-functions-called-once>
$<$<COMPILE_LANGUAGE:CXX>:-fno-early-inlining>
$<$<COMPILE_LANGUAGE:CXX>:-fno-ipa-cp>
$<$<COMPILE_LANGUAGE:CXX>:-fno-ipa-cp-clone>
$<$<COMPILE_LANGUAGE:CXX>:-fno-ipa-sra>
$<$<COMPILE_LANGUAGE:CXX>:-fno-partial-inlining>)
endif ()
endif ()
if (DUSK_GAME_ABI_INLINE_OPTIONS)
set(_game_abi_files ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
list(FILTER _game_abi_files EXCLUDE REGEX "^src/dusk/")
set_property(SOURCE ${_game_abi_files} APPEND PROPERTY
COMPILE_OPTIONS ${DUSK_GAME_ABI_INLINE_OPTIONS})
foreach(jsystem_lib IN LISTS JSYSTEM_LIBRARIES)
target_compile_options(${jsystem_lib} PRIVATE ${DUSK_GAME_ABI_INLINE_OPTIONS})
endforeach()
foreach(_sdk_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
aurora_os aurora_pad aurora_si aurora_vi)
if (TARGET ${_sdk_lib})
get_target_property(_sdk_lib_imported ${_sdk_lib} IMPORTED)
if (NOT _sdk_lib_imported)
target_compile_options(${_sdk_lib} PRIVATE ${DUSK_GAME_ABI_INLINE_OPTIONS})
endif ()
endif ()
endforeach ()
endif ()
if (MSVC)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:/hotpatch>)
@@ -421,11 +461,11 @@ if (MSVC)
else ()
target_link_options(dusklight PRIVATE /FUNCTIONPADMIN /OPT:NOICF)
endif ()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "^(AppleClang|Clang)$")
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "^(AppleClang|Clang|GNU)$")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=2,1>)
else ()
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=10,5>)
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=10,4>)
endif ()
endif ()
@@ -519,6 +559,9 @@ if (DUSK_ENABLE_CODE_MODS AND CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR
add_subdirectory(mods/ao_mod)
add_subdirectory(mods/shadow_mod)
add_subdirectory(mods/window_demo)
add_subdirectory(mods/flow_demo)
add_subdirectory(mods/basic_cosmetics_mod)
add_subdirectory(mods/randomizer)
endif ()
if (APPLE)
+4 -1
View File
@@ -26,7 +26,10 @@ It aims to be as accurate as possible to the original while also providing new o
You must dump your own copy of the game. Please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to `.rvz` to save space.
Currently, only the GameCube releases are supported. Support for other versions of the game is planned in the future.
Dusklight currently supports all commercial discs except for Wii's Korean release.
> [!NOTE]
> Dusklight is based on the [Twilight Princess decompilation](https://github.com/zeldaret/tp), which is currently only matching for GameCube. As a result, even when playing Dusklight with a Wii disc, you will be presented with the GameCube version's HUD and certain other specificities.
### 2. Install Dusklight
+1 -1
View File
@@ -4,7 +4,7 @@ include_guard(GLOBAL)
get_filename_component(_game_root "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
# PARTIAL_DEBUG makes debug and release share one struct/vtable ABI so a mod binary loads into either
set(_game_compile_defs TARGET_PC=1 WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1 PARTIAL_DEBUG=1 DUSK_TPHD=1)
set(_game_compile_defs TARGET_PC=1 WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1 PARTIAL_DEBUG=1)
if (ANDROID)
list(APPEND _game_compile_defs TARGET_ANDROID=1)
endif ()
+9 -9
View File
@@ -2,7 +2,7 @@ include_guard(GLOBAL)
get_filename_component(_SYMBOL_MANIFEST_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
set(_SYMGEN_VERSION "1.3.2")
set(_SYMGEN_VERSION "1.3.3")
set(_SYMGEN_RELEASE_BASE_URL "https://github.com/encounter/symgen/releases/download/v${_SYMGEN_VERSION}")
set(SYMGEN_PATH "" CACHE FILEPATH "Path to a symgen executable; empty downloads the pinned release")
mark_as_advanced(SYMGEN_PATH)
@@ -15,32 +15,32 @@ function(symgen_host_asset out_name out_hash)
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
if (_host_processor MATCHES "^(arm64|aarch64)$")
set(_asset "symgen-macos-arm64")
set(_asset_hash "SHA256=0344838d1674df09c17c3eeddcf26eb89c333fdb85dfd78f68adc436070eccbe")
set(_asset_hash "SHA256=e8420df1160242c83bd0e5197efeeb25ceb9fecc69dbc82bdc4927476381e948")
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
set(_asset "symgen-macos-x86_64")
set(_asset_hash "SHA256=ae0674f4a1e9d0dedfa02d35939ac28cdd229276b331c1f507df5df809cbec7e")
set(_asset_hash "SHA256=e51d13bbf0e982d1519f56850ff3c7b5910ca5a7095dd5a82cd8e59750e2d013")
endif ()
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
if (_host_processor MATCHES "^(aarch64|arm64)$")
set(_asset "symgen-linux-aarch64")
set(_asset_hash "SHA256=af766de2bfaeb0a06f6d7bc17bb2510b4a9c40f44a56e49bc4a4b798a6223042")
set(_asset_hash "SHA256=331190dd21ecee12a52e857b7f90a8b3e0a114608173813f7df74a31cf783f70")
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
set(_asset "symgen-linux-x86_64")
set(_asset_hash "SHA256=ebd62fb9623acc942b6295609e2306f85a73043b0a8a117f2072b761dd08e68f")
set(_asset_hash "SHA256=d727e0ba43242f92a92865af1b69c47aa9d81a0242c4570ce53b90e1ecd15e84")
elseif (_host_processor MATCHES "^(i[3-6]86|x86)$")
set(_asset "symgen-linux-i686")
set(_asset_hash "SHA256=07780a4513fd29726578efc4ff2d88736b22f407ed821b32a68e35ff1e9af5f4")
set(_asset_hash "SHA256=0c9f94d36b7fa46d15a199c9d0f1a633b73eb7d9c02621a6a76a676b83a135c7")
endif ()
elseif (CMAKE_HOST_WIN32)
if (_host_processor MATCHES "^(arm64|aarch64)$")
set(_asset "symgen-windows-arm64.exe")
set(_asset_hash "SHA256=5bb22b4a4a9b5ad45646af411bfb09b8321a732ff3a077eb4c9de1feaef27d2b")
set(_asset_hash "SHA256=c699192957e2086ed68912ed39fa77858e1ae894476ab8b557711804cf2aa7cb")
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
set(_asset "symgen-windows-x86_64.exe")
set(_asset_hash "SHA256=1d1ac087f991a96932d108969e15998becfec643e88f3e7d182ca97ebfc6a46f")
set(_asset_hash "SHA256=6d56a45617c75065ab7a3192d09ca52ff735baf5637b0899f446138d46acc310")
elseif (_host_processor MATCHES "^(i[3-6]86|x86)$")
set(_asset "symgen-windows-x86.exe")
set(_asset_hash "SHA256=c113f4cd05f813efe2b1878dbfcf44d6302aee3974271736e0036430b0cced78")
set(_asset_hash "SHA256=6cccc3a8d29e7525a4d1ade1dc4d89327b7c3002a604ab1c7decb43eea6fd4ab")
endif ()
endif ()
+7 -1
View File
@@ -5,7 +5,13 @@
- A Windows, Linux, or macOS device
- iOS device connected to computer via USB
- Dusklight IPA file (download the latest `Dusklight-vX.X.X-ios-arm64.ipa` from the [releases page](https://github.com/TwilitRealm/dusklight/releases))
- Legally acquired game disc - `GZ2E01` (Gamecube USA) or `GZ2PE01` (Gamecube PAL)
- Legally acquired game disc:
- `GZ2E01` (GameCube USA)
- `GZ2P01` (GameCube PAL)
- `GZ2J01` (GameCube JPN)
- `RZDE01` (Wii USA Rev. 0 *or* Rev. 2)
- `RZDP01` (Wii PAL)
- `RZDJ01` (Wii JPN)
## 1. Install iloader
+213 -11
View File
@@ -262,14 +262,16 @@ Installs hooks on game functions and resolves symbols by name. You'll rarely cal
### OverlayService (`mods/svc/overlay.h`)
Registers DVD file overlays at runtime: the dynamic counterpart to the static `overlay/` directory (see
[Asset Overlays](#asset-overlays)). Overlay a disc path with a file from your bundle, or with a caller-owned buffer
[Asset Overlays](#asset-overlays)). Overlay a disc path with a file from your bundle, a file within an archive,
or with a caller-owned buffer
(copied on registration):
```cpp
IMPORT_SERVICE(OverlayService, svc_overlay);
OverlayHandle handle = 0;
svc_overlay->add_file(mod_ctx, "/res/Msgus.arc", "res/replacement.arc", &handle);
svc_overlay->add_file(mod_ctx, "/Movie/demo_movie98_00.thp", "res/replacement.thp", &handle); // Replaces the demo movie
svc_overlay->add_file(mod_ctx, "/res/Object/Kmdl/archive/bmwr/al.bmd", "res/link_model.bmd", &handle); // Replaces link's model
svc_overlay->add_buffer(mod_ctx, "/generated.txt", data, size, nullptr);
svc_overlay->remove(mod_ctx, handle);
```
@@ -278,6 +280,9 @@ svc_overlay->remove(mod_ctx, handle);
on the disc are added as new files. Changes are applied at the next frame boundary, and data the game already read
stays in memory until the file is re-read: sometimes a scene reload, and in the worst case, a full restart.
Dusklight reloads core archive files during scene transitions so modifications to Link, Midna or other globally-loaded
data get refreshed without a full restart.
See [Asset Overlays](#asset-overlays) for priority and conflict handling.
### TextureService (`mods/svc/texture.h`)
@@ -469,8 +474,8 @@ per-window RCSS. A non-`MOD_OK` result from `build`/`update` fails your mod, as
callback.
**Controls:** `pane_add_control` adds an input row described by a `UiControlDesc`: `UI_CONTROL_BUTTON`,
`UI_CONTROL_TOGGLE`, `UI_CONTROL_NUMBER`, `UI_CONTROL_STRING`, or `UI_CONTROL_SELECT`. Values bind with callbacks or
directly to a config var.
`UI_CONTROL_GROUP`, `UI_CONTROL_TOGGLE`, `UI_CONTROL_NUMBER`, `UI_CONTROL_STRING`, `UI_CONTROL_SELECT`, or
`UI_CONTROL_COLOR`. Values bind with callbacks or directly to a config var.
```cpp
UiControlDesc control = UI_CONTROL_DESC_INIT;
@@ -483,9 +488,56 @@ svc_ui->pane_add_control(mod_ctx, leftPane, &control, nullptr);
```
`UI_BINDING_CONFIG_VAR` wires persistence, change notifications, and the modified indicator automatically. The var
type must match the control: `TOGGLE` = bool, `NUMBER` and `SELECT` = int, `STRING` = string. Float vars are not
bindable; use callbacks and convert. `help_rml` and `SELECT` option lists render in a help pane, so `SELECT` controls
are only available inside window tabs.
type must match the control: `TOGGLE` = bool, `NUMBER` and `SELECT` = int, `STRING` and `COLOR` = string. Float vars
are not bindable; use callbacks and convert. `help_rml` and `SELECT` option lists render in a help pane, so `SELECT`
controls are only available inside window tabs.
`pane_add_group` adds a category button to a window tab's left pane. Focusing the button clears the paired right pane
and calls the group's build callback with that pane, which is useful for organizing related controls without adding
more tabs.
**Lists:** `pane_add_list` adds a scrollable virtualized list of items that can be efficiently updated and filtered.
Keys must be unique and remain stable across replacements.
```cpp
UiListHandle locationList = 0;
void replace_locations(std::string_view query) {
std::vector<UiListItem> items;
for (uint64_t i = 0; i < locations.size(); ++i) {
if (matches(locations[i], query)) {
UiListItem item = UI_LIST_ITEM_INIT;
item.key = i;
item.label = locations[i].c_str();
items.push_back(item);
}
}
svc_ui->list_set_items(mod_ctx, locationList, items.data(), items.size());
}
void set_filter(ModContext*, void*, const UiControlValue* value) {
replace_locations(value->string_value);
}
bool location_selected(ModContext*, UiListHandle, uint64_t key, void*) {
return selected_locations.contains(key);
}
UiControlDesc filter = UI_CONTROL_DESC_INIT;
filter.kind = UI_CONTROL_STRING;
filter.label = "Filter";
filter.get = get_filter;
filter.set = set_filter;
filter.string_set_mode = UI_STRING_SET_ON_CHANGE; /* invoke `set` while typing */
svc_ui->pane_add_control(mod_ctx, pane, &filter, nullptr);
UiListDesc list = UI_LIST_DESC_INIT;
/* items may be passed as a part of list creation, or set afterwards */
list.on_pressed = location_pressed;
list.is_selected = location_selected;
svc_ui->pane_add_list(mod_ctx, pane, &list, &locationList);
replace_locations(""); /* calls `list_set_items` */
```
**Windows:** `window_push` pushes a tabbed two-pane window onto the document stack and shows it. Each tab's `build`
receives the window handle plus fresh left and right pane handles on every activation. The optional per-tab `update`
@@ -507,10 +559,36 @@ svc_ui->window_push(mod_ctx, &desc, &window);
```
**Dialogs:** `dialog_push` shows a modal dialog. `variant` picks the style, `icon` optionally overrides the variant's
default icon, and actions become buttons. After an action's `on_pressed` returns, the dialog closes unless the action
sets `keep_open`. A `keep_open` action can close it later (or immediately) with `dialog_close`. Cancel fires
`on_dismiss` if present and always closes. `dialog_set_body`, `dialog_set_icon`, and `dialog_add_action` mutate a live
dialog.
default icon, and actions become buttons. The optional `build` callback allows you to add controls to a pane between
the body and actions. It uses the same text, progress, and control builders as panels.
```cpp
ModResult build_dialog(ModContext*, UiElementHandle pane, void*, ModError*) {
UiControlDesc input = UI_CONTROL_DESC_INIT;
input.kind = UI_CONTROL_STRING;
input.label = "Name";
input.get = get_name;
input.set = set_name;
return svc_ui->pane_add_control(mod_ctx, pane, &input, nullptr);
}
UiDialogAction action = UI_DIALOG_ACTION_INIT;
action.label = "Save";
action.on_pressed = save;
action.is_disabled = is_save_disabled;
UiDialogDesc dialog = UI_DIALOG_DESC_INIT;
dialog.title = "New Preset";
dialog.body_rml = "Choose a name for the preset.";
dialog.actions = &action;
dialog.action_count = 1;
dialog.build = build_dialog;
svc_ui->dialog_push(mod_ctx, &dialog, nullptr);
```
After an action's `on_pressed`, the dialog closes unless the action sets `keep_open`. It can then be closed later
(or immediately) with `dialog_close`. Cancel fires `on_dismiss` and always closes. `dialog_set_body` and
`dialog_set_icon` mutate a live dialog.
**Toasts:** `push_toast` enqueues a notification. Titles and bodies accept RML. The optional `type` is applied as an
RCSS class; `warning` uses the built-in warning appearance, and mods can define their own types. A duration of 0 uses
@@ -636,6 +714,125 @@ first in-game frame. Projection matrices match the renderer's WebGPU clip conven
Camera operators allow overriding the main camera. When an operator callback returns true, its values replace the camera
state for the current frame. Register and unregister using `register_camera_operator` / `unregister_camera_operator`.
### GameModeService (`mods/svc/game_mode.h`)
Allows a mod to register a game mode with callbacks for key gameplay and save lifecycle events. Registered game modes
appear in the prelaunch menu. Game modes may use a unique set of saves by configuring `save_name`; leave it empty to use
the vanilla `gczelda2` save.
```cpp
IMPORT_SERVICE(LogService, svc_log);
IMPORT_SERVICE(HookService, svc_hook);
IMPORT_SERVICE(GameModeService, svc_game_mode);
DEFINE_HOOK(fopAcM_createItem, CreateItem);
#define MY_GAME_MODE_ID "game-mode-id"
static HookAction my_function_hook(ModContext* ctx, void* args, void*, void*) {
// If we wish to have this hook only run while the gamemode is registered, we need to hook the function from the
// gamemode's onActivatedFunction, and uninstall the hook during the onDeactivatedFunction. Example below.
// Alternatively, check with `svc_game_mode->is_active(mod_ctx, MY_GAME_MODE_ID, &active) == MOD_OK && active`.
return HOOK_CONTINUE;
}
ModResult on_game_mode_activated(void*, ModError* outError) {
// Setup the gamemode, Add any hooks that are gamemode specific
// Overlay any files that are gamemode specific
ModResult result = mods::hook_add_pre<CreateItem>(svc_hook, my_function_hook);
if (result != MOD_OK) {
return mods::set_error(outError, result, "failed to install fopAcM_createItem hook");
}
return MOD_OK;
}
ModResult on_game_mode_deactivated(void*, ModError* outError) {
// Uninstall any hooks that are gamemode specific
// Remove any file overlays that are gamemode specific
ModResult result = mods::hook_uninstall<CreateItem>();
if (result != MOD_OK) {
return mods::set_error(outError, result, "failed to uninstall fopAcM_createItem hook");
}
return MOD_OK;
}
ModResult on_save_loaded(void*, ModError*) {
// This function will be invoked by the game as a save is loaded
return MOD_OK;
}
const GameModeDesc gameModeDesc = {
.struct_size = sizeof(GameModeDesc),
.game_mode_id = MY_GAME_MODE_ID,
.full_name = "My Game Mode",
.save_name = "my-unique-save",
.user_data = nullptr,
.on_activated = on_game_mode_activated,
.on_deactivated = on_game_mode_deactivated,
.on_save_loaded = on_save_loaded,
};
svc_game_mode->register_game_mode(mod_ctx, &gameModeDesc);
```
A game mode can also open UI for per-save settings when creating a new file. The state begins as
`GAME_MODE_STATE_PENDING` and remains valid until the mod selects `PROCEED` or `RETURN`.
```cpp
IMPORT_SERVICE(GameModeService, svc_game_mode);
IMPORT_SERVICE(UiService, svc_ui);
ModResult on_new_save_select(void*, GameModeNewSaveState* state, ModError* outError) {
static GameModeNewSaveState* newSaveState;
static UiWindowHandle windowHandle;
newSaveState = state;
UiTabDesc tabs[1]{};
tabs[0].struct_size = sizeof(UiTabDesc);
tabs[0].title = "Play";
tabs[0].build = [](ModContext* ctx, UiWindowHandle, UiElementHandle leftPane, UiElementHandle rightPane, void*, ModError*) {
UiControlDesc desc = UI_CONTROL_DESC_INIT;
desc.kind = UI_CONTROL_BUTTON;
desc.label = "Play";
desc.help_rml = "Play Button";
desc.on_pressed = [](ModContext* ctx, void* userdata) {
*newSaveState = GAME_MODE_STATE_PROCEED;
svc_ui->window_close(ctx, *static_cast<UiWindowHandle*>(userdata));
};
desc.user_data = &windowHandle;
svc_ui->pane_add_control(mod_ctx, leftPane, &desc, nullptr);
return MOD_OK;
};
UiWindowDesc desc = UI_WINDOW_DESC_INIT;
desc.tabs = tabs;
desc.tab_count = 1;
desc.on_closed = [](ModContext *, UiWindowHandle, void *userdata) {
// If closing the window through backing out, return to file select
if (*newSaveState == GAME_MODE_STATE_PENDING) {
*newSaveState = GAME_MODE_STATE_RETURN;
}
};
ModResult result = svc_ui->window_push(mod_ctx, &desc, &windowHandle);
if (result != MOD_OK) {
return mods::set_error(outError, result, "failed to open new-save settings");
}
return MOD_OK;
}
const GameModeDesc gameModeDesc = {
.struct_size = sizeof(GameModeDesc),
.game_mode_id = "my-game-mode-id",
.full_name = "My Game Mode",
.save_name = "my-unique-save",
.on_new_save_select = on_new_save_select,
};
svc_game_mode->register_game_mode(mod_ctx, &gameModeDesc);
```
---
## Hooking Game Functions
@@ -763,6 +960,11 @@ For reference parameters (e.g. `const cXyz& pos`), `arg_ref<cXyz>` yields a dire
Files placed under `overlay/` in the `.dusk` archive override game files at the corresponding path, equivalent to
replacing files in the .iso. This requires no code: an archive with just `mod.json` and `overlay/` is a complete mod.
To replace a file within an `.arc` archive, replace the archive suffix with a directory and place the replacement at
its path within the archive.
- `overlay/Audiores/Stream/menu_select.ast` replaces the main title's audio stream.
- `overlay/res/Layout/main2D/main2d/timg/midona64.bti` replaces Midna's UI icon inside `main2D.arc`.
Files placed under `textures/` register as texture replacements, and act just like the user's general
`texture_replacements/` directory: Dolphin-style naming, matched by texture hash
+1 -1
+22 -10
View File
@@ -1412,6 +1412,7 @@ set(DOLPHIN_FILES
set(DUSK_FILES
include/helpers/batch.hpp
include/helpers/bits.hpp
include/helpers/endian_gx.hpp
src/d/actor/d_a_alink_dusk.cpp
src/dusk/OSContext.cpp
@@ -1433,9 +1434,11 @@ set(DUSK_FILES
src/dusk/extras.c
src/dusk/frame_interpolation.cpp
src/dusk/game_clock.cpp
src/dusk/game_mode.cpp
src/dusk/gamepad_color.cpp
src/dusk/globals.cpp
src/dusk/gyro.cpp
src/dusk/hq_minimap.cpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiBloomWindow.cpp
src/dusk/imgui/ImGuiBloomWindow.hpp
@@ -1456,6 +1459,8 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiStubLog.cpp
src/dusk/io.cpp
src/dusk/iso_validate.cpp
src/dusk/language.cpp
src/dusk/language.hpp
src/dusk/layout.cpp
src/dusk/livesplit.cpp
src/dusk/logging.cpp
@@ -1472,6 +1477,9 @@ set(DUSK_FILES
src/dusk/mods/loader/native_module.hpp
src/dusk/mods/loader/prepatch.cpp
src/dusk/mods/loader/prepatch.hpp
src/dusk/mods/item.hpp
src/dusk/mods/item_checks.cpp
src/dusk/mods/item_gives.cpp
src/dusk/mods/log_buffer.cpp
src/dusk/mods/log_buffer.hpp
src/dusk/mods/manifest.cpp
@@ -1481,8 +1489,11 @@ set(DUSK_FILES
src/dusk/mods/svc/config.hpp
src/dusk/mods/svc/game.cpp
src/dusk/mods/svc/gfx.cpp
src/dusk/mods/svc/flow.cpp
src/dusk/mods/svc/hook.cpp
src/dusk/mods/svc/host.cpp
src/dusk/mods/svc/item.cpp
src/dusk/mods/svc/item.hpp
src/dusk/mods/svc/log.cpp
src/dusk/mods/svc/overlay.cpp
src/dusk/mods/svc/registry.cpp
@@ -1491,6 +1502,7 @@ set(DUSK_FILES
src/dusk/mods/svc/texture.cpp
src/dusk/mods/svc/ui.cpp
src/dusk/mods/svc/ui.hpp
src/dusk/mods/svc/game_mode.cpp
src/dusk/mods/svc/window.cpp
src/dusk/mods/svc/window.hpp
src/dusk/mods/svc/save.cpp
@@ -1513,6 +1525,8 @@ set(DUSK_FILES
src/dusk/ui/bool_button.hpp
src/dusk/ui/button.cpp
src/dusk/ui/button.hpp
src/dusk/ui/color_input.cpp
src/dusk/ui/color_input.hpp
src/dusk/ui/component.cpp
src/dusk/ui/component.hpp
src/dusk/ui/controller_config.cpp
@@ -1526,12 +1540,16 @@ set(DUSK_FILES
src/dusk/ui/event.hpp
src/dusk/ui/graphics_tuner.cpp
src/dusk/ui/graphics_tuner.hpp
src/dusk/ui/group_button.cpp
src/dusk/ui/group_button.hpp
src/dusk/ui/icon_provider.cpp
src/dusk/ui/icon_provider.hpp
src/dusk/ui/input.cpp
src/dusk/ui/input.hpp
src/dusk/ui/logs_window.cpp
src/dusk/ui/logs_window.hpp
src/dusk/ui/list.cpp
src/dusk/ui/list.hpp
src/dusk/ui/menu_bar.cpp
src/dusk/ui/menu_bar.hpp
src/dusk/ui/mod_texture_provider.cpp
@@ -1543,12 +1561,16 @@ set(DUSK_FILES
src/dusk/ui/mods_window.cpp
src/dusk/ui/mods_window.hpp
src/dusk/ui/nav_types.hpp
src/dusk/ui/nav_group.cpp
src/dusk/ui/nav_group.hpp
src/dusk/ui/number_button.cpp
src/dusk/ui/number_button.hpp
src/dusk/ui/overlay.cpp
src/dusk/ui/overlay.hpp
src/dusk/ui/pane.cpp
src/dusk/ui/pane.hpp
src/dusk/ui/popover.cpp
src/dusk/ui/popover.hpp
src/dusk/ui/prelaunch.cpp
src/dusk/ui/prelaunch.hpp
src/dusk/ui/preset.cpp
@@ -1581,14 +1603,4 @@ set(DUSK_FILES
src/helpers/endian.cpp
src/helpers/offset_ptr.cpp
src/helpers/string.cpp
src/dusk/tphd/TphdPack.hpp
src/dusk/tphd/TphdPack.cpp
src/dusk/tphd/GtxParser.hpp
src/dusk/tphd/GtxParser.cpp
src/dusk/tphd/AddrLib.hpp
src/dusk/tphd/AddrLib.cpp
src/dusk/tphd/HdAssetLayer.hpp
src/dusk/tphd/HdAssetLayer.cpp
src/dusk/tphd/LosTable.hpp
src/dusk/tphd/LosTable.cpp
)
+1 -5
View File
@@ -47,7 +47,7 @@ public:
s32 getBgmLoadStatus(u32 wave) { return getWaveLoadStatus(wave, 1); }
u8 getDemoSeWaveNum() { return loadedDemoWave; }
private:
// private:
/* 0x00 */ JAISoundID BGM_ID;
/* 0x04 */ int sceneNum;
/* 0x08 */ int timer;
@@ -158,10 +158,6 @@ enum Z2Scene {
/* 0x4E */ Z2SCENE_GROTTO_ROCK_2,
/* 0x4F */ Z2SCENE_GROTTO_POND,
/* 0x50 */ Z2SCENE_FARON_WOODS_CAVE,
#if TARGET_PC
/* 0x51 */ Z2SCENE_CAVE_OF_SHADOWS,
#endif
};
#endif /* Z2SCENEMGR_H */
+2 -2
View File
@@ -4560,8 +4560,6 @@ public:
void onIronBallChainInterpCallback();
f32 mLosStickValue;
static const int IRON_BALL_CHAIN_COUNT = 102;
cXyz mIBChainInterpPrevPos[IRON_BALL_CHAIN_COUNT];
cXyz mIBChainInterpCurrPos[IRON_BALL_CHAIN_COUNT];
@@ -8451,8 +8449,10 @@ struct daAlink_cutHorseParamTbl {
/* 0xA */ u8 m_cutType;
}; // Size: 0xC
#if !TARGET_PC
inline BOOL dComIfGs_isTransformLV(int i_no);
inline BOOL dComIfGs_isEventBit(const u16);
#endif
static fopAc_ac_c* daAlink_searchPortal(fopAc_ac_c* i_actor, void* i_data);
static fopAc_ac_c* daAlink_searchCanoe(fopAc_ac_c* i_actor, void* i_data);
+1 -1
View File
@@ -123,7 +123,7 @@ public:
bool checkFlag(u16 flag) { return field_0x68e & flag; }
void setAction(u8 action) { mAction = action; }
private:
// private:
/* 0x56C */ request_of_phase_process_class mPhase1;
/* 0x574 */ request_of_phase_process_class mPhase2;
/* 0x57C */ J3DModel* mModel1;
+3
View File
@@ -96,6 +96,9 @@ public:
/* 0xDE0 */ cXyz field_0xde0;
/* 0xDEC */ u8 field_0xdec[0xdf9 - 0xdec];
/* 0xDF9 */ u8 field_0xdf9;
#if TARGET_PC
bool mItemCheckHandled;
#endif
};
STATIC_ASSERT(sizeof(daE_HP_c) == 0xdfc);
+9
View File
@@ -32,6 +32,12 @@ struct daItemBase_data {
class daItemBase_c : public fopAc_ac_c {
public:
u8 getItemNo();
#if TARGET_PC
u8 getDisplayItemNo() const {
return mDisplayItemNo != 0xFF ? mDisplayItemNo : m_itemNo;
}
void setDisplayItemNo(u8 itemNo) { mDisplayItemNo = itemNo; }
#endif
void hide();
void show();
void changeDraw();
@@ -79,6 +85,9 @@ public:
/* 0x928 */ s16 m_get_timer;
/* 0x92A */ u8 m_itemNo;
/* 0x92B */ u8 field_0x92b;
#if TARGET_PC
u8 mDisplayItemNo = 0xFF;
#endif
};
#endif /* D_A_D_A_ITEMBASE_H */
+1 -1
View File
@@ -423,7 +423,7 @@ public:
static DUSK_GAME_DATA daMidna_texData_s const m_texDataTable[21];
static DUSK_GAME_DATA daMidna_anmData_s const m_anmDataTable[53];
private:
// private:
/* 0x568 */ request_of_phase_process_class mPhase;
/* 0x570 */ J3DModel* mpModel;
/* 0x574 */ J3DModel* mpShadowModel;
+1 -9
View File
@@ -18,24 +18,16 @@ public:
TGXTexObj& getTexObj() { return mTexObj; }
cXyz* getQuad() { return mQuad; }
#if TARGET_PC
// TP HD reflects across N floor quads (F_SP117 room 2 uses 6); GC uses 1.
static const int MAX_QUADS = 6;
#endif
/* 0x010 */ TGXTexObj mTexObj;
/* 0x030 */ u8 mModelCount;
/* 0x034 */ J3DModel* mModels[0x40];
/* 0x134 */ cXyz mQuad[DUSK_IF_ELSE(MAX_QUADS*4, 4)];
/* 0x134 */ cXyz mQuad[4];
/* 0x164 */ cXyz mMinVal;
/* 0x170 */ cXyz mMaxVal;
/* 0x17C */ cXyz mViewScale;
#if TARGET_PC
bool mbReset = false;
bool mbHadEntry = false;
cXyz mQuadBoxMin[MAX_QUADS];
cXyz mQuadBoxMax[MAX_QUADS];
int mQuadCount = 1;
#endif
};
+1 -1
View File
@@ -296,7 +296,7 @@ public:
static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mCutNameList[18];
static DUSK_GAME_DATA cutFunc DUSK_CONST mCutList[18];
private:
// private:
/* 0x0E40 */ NPC_FAIRY_HIO_CLASS* mHIO;
/* 0x0E44 */ dCcD_Cyl mCyl;
/* 0x0F80 */ u8 mType;
+1 -1
View File
@@ -287,7 +287,7 @@ public:
static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mCutNameList[10];
static DUSK_GAME_DATA cutFunc DUSK_CONST mCutList[10];
private:
// private:
/* 0x0E40 */ mDoExt_McaMorfSO* mFishModelMorf;
/* 0x0E44 */ mDoExt_McaMorfSO* mLeafModelMorf;
/* 0x0E48 */ NPC_YKM_HIO_CLASS* mpHIO;
+1 -1
View File
@@ -96,7 +96,7 @@ public:
static DUSK_GAME_DATA char DUSK_CONST* DUSK_CONST mEvtCutNameList[2];
static DUSK_GAME_DATA EventFn DUSK_CONST mEvtCutList[2];
private:
// private:
/* 0xB48 */ Z2Creature mCreatureSound;
/* 0xBD8 */ daNpcF_MatAnm_c* mpMatAnm;
/* 0xBDC */ daNpcF_Lookat_c mLookat;
+1 -1
View File
@@ -114,7 +114,7 @@ public:
void himoCalc();
void adjustShapeAngle() {}
private:
// private:
/* 0x0B48 */ Z2Creature mCreatureSound;
/* 0x0BD8 */ daNpcF_MatAnm_c* mpMatAnm;
/* 0x0BDC */ mDoExt_invisibleModel mInvisibleModel;
+1 -1
View File
@@ -60,7 +60,7 @@ public:
bool isFirst() { return fopAcM_GetParamBit(this, 0x1b, 1); }
void setAction(u8 action) { mAction = action; }
private:
// private:
/* 0x568 */ request_of_phase_process_class mPhaseReq;
/* 0x570 */ J3DModel* mpModel;
/* 0x574 */ mDoExt_btkAnm* mpBtkAnm[2];
+7 -1
View File
@@ -64,7 +64,7 @@ public:
current.pos = new_pos;
}
private:
// private:
/* 0x92C */ fpc_ProcID mItemId;
/* 0x930 */ int mCounter;
/* 0x934 */ u8 mStatus;
@@ -88,6 +88,12 @@ private:
/* 0x99C */ dPa_followEcallBack mEffect2;
/* 0x9B0 */ Z2SoundObjSimple mSound;
/* 0x9D0 */ u8 mIsHookCarry;
#if TARGET_PC
u8 mOriginalItemNo;
bool mItemOverridden;
bool mOverrideHover;
bool mGoldenWolfItem;
#endif
};
STATIC_ASSERT(sizeof(daObjLife_c) == 0x9d4);
+1 -1
View File
@@ -51,7 +51,7 @@ public:
static DUSK_GAME_DATA daObjMasterSword_Attr_c const mAttr;
static DUSK_GAME_DATA actionFunc ActionTable[];
private:
// private:
/* 0x568 */ J3DModel* mpModel;
/* 0x56C */ request_of_phase_process_class mPhase;
/* 0x574 */ mDoExt_btkAnm mBtk;
+4 -4
View File
@@ -142,12 +142,12 @@ namespace daObjSwpush {
int Mthd_Execute();
int Mthd_Draw();
static DUSK_GAME_DATA s16 const M_bmd[DUSK_IF_ELSE(4, 3)];
static DUSK_GAME_DATA s16 const M_dzb[DUSK_IF_ELSE(4, 3)];
static DUSK_GAME_DATA u32 const M_heap_size[DUSK_IF_ELSE(4, 3)];
static DUSK_GAME_DATA s16 const M_bmd[3];
static DUSK_GAME_DATA s16 const M_dzb[3];
static DUSK_GAME_DATA u32 const M_heap_size[3];
static DUSK_GAME_DATA Hio_c::Attr_c const M_attr[5];
static DUSK_GAME_DATA u8 const M_op_vtx[4];
static DUSK_GAME_DATA DUSK_CONST char* M_arcname[DUSK_IF_ELSE(4, 3)];
static DUSK_GAME_DATA DUSK_CONST char* M_arcname[3];
/* 0x568 */ request_of_phase_process_class mPhase;
/* 0x570 */ dBgWSv* mpBgW;
+2
View File
@@ -311,9 +311,11 @@ private:
class daMidna_c;
class daSpinner_c;
class daPy_py_c;
#if !TARGET_PC
inline daPy_py_c* dComIfGp_getLinkPlayer();
inline BOOL dComIfGs_isEventBit(const u16);
inline u32 dComIfGs_getLastSceneMode();
#endif
class daPy_py_c : public fopAc_ac_c {
public:
+3
View File
@@ -194,6 +194,9 @@ private:
/* 0x9FC */ u8 field_0x9fc;
/* 0x9FD */ u8 field_0x9fd;
/* 0xA00 */ Mtx mDrawMtx;
#if TARGET_PC
u8 mOriginalItemNo;
#endif
};
STATIC_ASSERT(sizeof(daTbox_c) == 0xA30);
+5 -1
View File
@@ -67,7 +67,7 @@ public:
u8 getModelType() { return fopAcM_GetParamBit(this, 8, 4); }
u8 getItemNo() { return fopAcM_GetParamBit(this, 0, 8); }
private:
// private:
/* 0x718 */ u8 mReturnRupee;
/* 0x71C */ request_of_phase_process_class mPhase;
/* 0x724 */ J3DModel* mpModel;
@@ -83,6 +83,10 @@ private:
/* 0xAC0 */ u8 field_0xAC0[0xAC4 - 0xAC0];
/* 0xAC4 */ int mStaffIdx;
/* 0xAC8 */ dBgW* mBoxBgW;
#if TARGET_PC
bool mParamsInit;
u8 mOriginalItemNo;
#endif
};
STATIC_ASSERT(sizeof(daTbox2_c) == 0xACC);
+4
View File
@@ -145,6 +145,10 @@ public:
/* 0x9BC */ u8 field_0x9bc[4];
/* 0x9C0 */ u8 field_0x9c0;
/* 0x9C1 */ u8 field_0x9c1;
#if TARGET_PC
u8 mOriginalItemNo;
bool mItemOverridden;
#endif
}; // Size: 0x9C4
#endif /* D_A_D_A_ITEM_STATIC_H */
+5
View File
@@ -1,10 +1,15 @@
#ifndef D_A_D_A_ITEMBASE_STATIC_H
#define D_A_D_A_ITEMBASE_STATIC_H
#include "dolphin/types.h"
class fopAc_ac_c;
int CheckFieldItemCreateHeap(fopAc_ac_c* actor);
int CheckItemCreateHeap(fopAc_ac_c* i_this);
#if TARGET_PC
const char* dItem_fieldModelArc(u8 itemNo);
#endif
#endif /* D_A_D_A_ITEMBASE_STATIC_H */
+7
View File
@@ -50,6 +50,9 @@ public:
csXyz* getRotateP();
cXyz* getPosP();
const char* getShopArcname();
#if TARGET_PC
const ResourceData& getResourceData() const;
#endif
u16 getHeapSize();
void CreateInit();
void set_mtx();
@@ -110,6 +113,10 @@ private:
/* 0x960 */ s16 mAngleX;
/* 0x962 */ s16 mAngleY;
/* 0x964 */ u8 mShopItemID;
#if TARGET_PC
ResourceData mOverrideData;
bool mItemOverridden;
#endif
};
int CheckShopItemCreateHeap(fopAc_ac_c* i_this);
File diff suppressed because it is too large Load Diff
+1 -6
View File
@@ -27,11 +27,6 @@ public:
void setSaveDate(dSv_save_c*);
void setPlayTime(dSv_save_c*);
void modeWait();
#if TARGET_PC
void fileSelectWide();
#endif
void modeMove();
void _draw();
@@ -42,7 +37,7 @@ public:
CPaneMgrAlpha* getNoDatBase() { return mNoDatBase; }
void draw() { _draw(); }
private:
// private:
/* 0x04 */ JKRArchive* mArchive;
/* 0x08 */ dDlst_FileInfo_c mFileInfo;
/* 0x1C */ u8 field_0x1c[4];
+13 -33
View File
@@ -9,42 +9,13 @@
#include "JSystem/J3DGraphLoader/J3DModelLoader.h"
#include "JSystem/J3DGraphLoader/J3DAnmLoader.h"
#if TARGET_PC
#include "mods/svc/game_mode.h"
#endif
class dFile_info_c;
class J2DPicture;
#if TARGET_PC
static PaneCache mSelDtPanes[] = {
{MULTI_CHAR('tate_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n2'), 0.0f, 0.0f, false},
{MULTI_CHAR('gray_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('b_base'), 0.0f, 0.0f, false},
{MULTI_CHAR('b_base1'), 0.0f, 0.0f, false},
};
static PaneCache fileSelPanes[] = {
{MULTI_CHAR('w_uzu00'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu01'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu02'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu03'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu04'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu05'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu06'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu07'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu08'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_uzu09'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msg'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msE'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_msR'), 0.0f, 0.0f, false},
{MULTI_CHAR('er_for0'), 0.0f, 0.0f, false},
{MULTI_CHAR('er_for1'), 0.0f, 0.0f, false},
};
#endif
class dDlst_FileSel_c : public dDlst_base_c {
public:
void draw();
@@ -420,6 +391,13 @@ public:
bool pointerMenuSelect();
bool pointerCopyDataToSelect();
bool pointerYesNoSelect(bool errorSelect);
void backToDataSelectMove() {
headerTxtSet(0x43, 1, 0);
fileRecScaleAnmInitSet2(0.0f, 1.0f);
nameMoveAnmInitSet(0xd29, 0xd1f);
modoruTxtDispAnmInit(0);
mDataSelProc = DATASELPROC_NAME_TO_DATA_SELECT_MOVE;
}
#endif
void _draw();
void errorMoveAnmInitSet(int, int);
@@ -733,6 +711,8 @@ public:
#endif
#ifdef TARGET_PC
dDlst_FileSelFade_c mFadeDlst;
bool mGameModeSaveStartBuildUi = true;
GameModeNewSaveState mGameModeNewSaveState = GAME_MODE_STATE_PENDING;
#endif
#if PLATFORM_WII || PLATFORM_SHIELD
+3 -5
View File
@@ -11,7 +11,9 @@ public:
static DUSK_GAME_DATA u8* mData;
};
void execItemGet(u8 item_id);
class fopAc_ac_c;
void execItemGet(
u8 item_id IF_DUSK_ARG(u32 item_give_tag = 0) IF_DUSK_ARG(fopAc_ac_c* giver = NULL));
void item_func_HEART();
void item_func_GREEN_RUPEE();
@@ -400,8 +402,4 @@ BOOL isBottleItem(u8 item_no);
u8 check_itemno(int i_itemNo);
BOOL isInsect(u8 i_itemNo);
#if TARGET_PC
void item_func_WALLET_LV4();
#endif
#endif /* D_D_ITEM_H */
+6 -12
View File
@@ -58,11 +58,7 @@ struct dItem_data {
static s16 getTexture(u8 index) { return item_resource[index].mTexture; }
#if TARGET_PC
static u8 getTexScale(u8 index);
#else
static u8 getTexScale(u8 index) { return item_resource[index].mTexScale; }
#endif
static u8 getTexScale (u8 index) { return item_resource[index].mTexScale; }
static const char* getFieldArc(u8 index) { return field_item_res[index].mFieldArc; }
@@ -146,7 +142,12 @@ enum {
/* 0x2F */ dItemNo_WEAR_KOKIRI_e,
/* 0x30 */ dItemNo_ARMOR_e,
/* 0x31 */ dItemNo_WEAR_ZORA_e,
#if TARGET_PC
/* 0x32 */ dItemNo_MAGIC_LV1_e,
dItemNo_SHADOW_CRYSTAL_e = dItemNo_MAGIC_LV1_e,
#else
/* 0x32 */ dItemNo_MAGIC_LV1_e,
#endif
/* 0x33 */ dItemNo_DUNGEON_EXIT_2_e,
/* 0x34 */ dItemNo_WALLET_LV1_e,
/* 0x35 */ dItemNo_WALLET_LV2_e,
@@ -354,11 +355,4 @@ enum {
/* 0xFF */ dItemNo_NONE_e,
};
#if TARGET_PC
// HD item mappings
enum {
dItemNo_WALLET_LV4_e = 0xDA,
};
#endif
#endif /* D_D_ITEM_DATA_H */
+1 -1
View File
@@ -30,7 +30,7 @@ public:
void drawSelf() { mpKanteraIcon->draw(); }
private:
// private:
/* 0x04 */ dDlst_KanteraIcon_c* mpKanteraIcon;
/* 0x08 */ CPaneMgr* mpParent;
/* 0x0C */ CPaneMgr* mpGauge;
+1 -1
View File
@@ -57,7 +57,7 @@ public:
return &mpTypeGroupData[i_typeGroupNo];
}
/* 0x0 */ fmpTresTypeGroupDataList_c mpTypeGroupData[DUSK_IF_ELSE(35, 17)];
/* 0x0 */ fmpTresTypeGroupDataList_c mpTypeGroupData[17];
};
class dMenu_Fmap_data_c {
+1 -44
View File
@@ -15,49 +15,6 @@ class dMenu_Fishing_c;
class dMenu_Skill_c;
class dMenu_Insect_c;
class dSelect_cursor_c;
#if TARGET_PC
struct PaneCache {
u64 tag;
f32 origTransX;
f32 origTransY;
bool cached;
};
static PaneCache mpScreenPanes[] = {
{MULTI_CHAR('sa_tex_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('op_tex_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('heart_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('wolf_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_0_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_1_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('item_2_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('fish_3_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('lett_4_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('maki_5_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('fuku_n2'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('tate_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n0'), 0.0f, 0.0f, false},
{MULTI_CHAR('ken_n1'), 0.0f, 0.0f, false},
{MULTI_CHAR('kabu_6n'), 0.0f, 0.0f, false},
{MULTI_CHAR('t_t00'), 0.0f, 0.0f, false},
{MULTI_CHAR('f_t00'), 0.0f, 0.0f, false},
{MULTI_CHAR('itemn_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('infotxtn'), 0.0f, 0.0f, false},
{MULTI_CHAR('sa_op_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('title_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('menu_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('w_er_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('center_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('info_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('lavel_n'), 0.0f, 0.0f, false},
{MULTI_CHAR('modelbgn'), 0.0f, 0.0f, false},
};
#endif
class dMenu_Collect2D_c;
class dMenu_Collect2DTop_c : public dDlst_base_c {
public:
@@ -172,7 +129,7 @@ public:
J2DPicture* getBlackTex() { return mpBlackTex; }
u8 getSubWindowOpenCheck() { return mSubWindowOpenCheck; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRExpHeap* mpSubHeap;
/* 0x00C */ void* field_0xc;
+1 -1
View File
@@ -43,7 +43,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -270,7 +270,7 @@ public:
static DUSK_GAME_DATA dMenu_Fmap_c* MyClass;
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRExpHeap* mpTalkHeap;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -60,7 +60,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x04 */ JKRExpHeap* mpHeap;
/* 0x08 */ JKRArchive* mpArchive;
/* 0x0C */ STControl* mpStick;
+1 -1
View File
@@ -64,7 +64,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -117,7 +117,7 @@ public:
return ((u8)mUseFlag & (u8)i_flag) != 0 ? true : false;
}
private:
// private:
/* 0x004 */ J2DScreen* mpBackScreen;
/* 0x008 */ J2DScreen* mpScreen;
/* 0x00C */ J2DScreen* mpClipScreen;
+1 -1
View File
@@ -84,7 +84,7 @@ public:
void drawFlag0() { mDrawFlag = 0; }
void setStatus(u8 i_status) { mStatus = i_status; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ STControl* mpStick;
/* 0x00C */ CSTControl* mpCStick;
+1 -1
View File
@@ -278,7 +278,7 @@ public:
u8 getEndStatus() { return mEndStatus; }
void setUseType(u8 type) { mUseType = type; }
private:
// private:
/* 0x0004 */ JKRArchive* mpArchive;
/* 0x0008 */ mDoDvdThd_mountArchive_c* mpMount;
/* 0x000C */ STControl* stick;
+1 -1
View File
@@ -58,7 +58,7 @@ public:
u8 getStatus() { return mStatus; }
private:
// private:
/* 0x004 */ JKRExpHeap* mpHeap;
/* 0x008 */ JKRArchive* mpArchive;
/* 0x00C */ STControl* mpStick;
+1 -1
View File
@@ -154,7 +154,7 @@ public:
}
#endif
private:
// private:
/* 0x004 */ item_params mItemParams[4];
/* 0x074 */ JKRExpHeap* heap;
/* 0x078 */ J2DScreen* mpScreen;
+146
View File
@@ -328,6 +328,150 @@ void dMeter2Info_onWide2D();
void dMeter2Info_offWide2D();
#endif
#if TARGET_PC
DUSK_NOINLINE void dMeter2Info_Initialize();
DUSK_NOINLINE dMeterMap_c* dMeter2Info_getMeterMapClass();
DUSK_NOINLINE dMw_c* dMeter2Info_getMenuWindowClass();
DUSK_NOINLINE void dMeter2Info_setWindowStatus(u8 i_status);
DUSK_NOINLINE CPaneMgr* dMeter2Info_getMeterItemPanePtr(s32 i_idx);
DUSK_NOINLINE void dMeter2Info_getString(
u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry);
DUSK_NOINLINE void dMeter2Info_getStringKanji(
u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry);
DUSK_NOINLINE void dMeter2Info_getStringKana(
u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry);
DUSK_NOINLINE f32 dMeter2Info_getStringLength(
JUTFont* i_font, f32 param_2, f32 param_3, char* i_string);
DUSK_NOINLINE f32 dMeter2Info_getStringLength(J2DTextBox* i_textbox, char* i_string);
DUSK_NOINLINE void dMeter2Info_setHotSpringTimer(u8 i_slotNo);
DUSK_NOINLINE u16 dMeter2Info_getOilGaugeBackUp();
DUSK_NOINLINE void dMeter2Info_setOilGaugeBackUp(u16 param_1);
DUSK_NOINLINE void dMeter2Info_setSaveStageName(const char* i_stageName);
DUSK_NOINLINE u8 dMeter2Info_getInsectSelectType();
DUSK_NOINLINE void dMeter2Info_setInsectSelectType(u8 i_type);
DUSK_NOINLINE u8 dMeter2Info_getWarpStatus();
DUSK_NOINLINE void dMeter2Info_setWarpStatus(u8 i_status);
DUSK_NOINLINE int dMeter2Info_readItemTexture(u8 i_itemNo, void* i_texBuf1, J2DPicture* i_pic1,
void* i_texBuf2, J2DPicture* i_pic2, void* i_texBuf3, J2DPicture* i_pic3, void* i_texBuf4,
J2DPicture* i_pic4, int param_9);
DUSK_NOINLINE void dMeter2Info_setItemColor(
u8 i_itemNo, J2DPicture* i_pic1, J2DPicture* i_pic2, J2DPicture* i_pic3, J2DPicture* i_pic4);
DUSK_NOINLINE u8 dMeter2Info_getWarpRoomNo();
DUSK_NOINLINE u8 dMeter2Info_getWarpPlayerNo();
DUSK_NOINLINE cXyz& dMeter2Info_getWarpPos();
DUSK_NOINLINE const char* dMeter2Info_getWarpStageName();
DUSK_NOINLINE void dMeter2Info_setNowCount(u8 i_count);
DUSK_NOINLINE void dMeter2Info_setMaxCount(u8 i_count);
DUSK_NOINLINE BOOL dMeter2Info_isDirectUseItem(int param_0);
DUSK_NOINLINE bool dMeter2Info_isUseButton(int i_buttonBit);
DUSK_NOINLINE void dMeter2Info_setMeterMapClass(dMeterMap_c* i_map);
DUSK_NOINLINE void dMeter2Info_decHotSpringTimer();
DUSK_NOINLINE void dMeter2Info_allUseButton();
DUSK_NOINLINE void dMeter2Info_offUseButton(int i_buttonBit);
DUSK_NOINLINE void dMeter2Info_resetGameStatus();
DUSK_NOINLINE void dMeter2Info_onGameStatus(int i_status);
DUSK_NOINLINE void dMeter2Info_setMapStatus(u8 i_status);
DUSK_NOINLINE u8 dMeter2Info_getMapStatus();
DUSK_NOINLINE void dMeter2Info_setPauseStatus(u8 i_status);
DUSK_NOINLINE void dMeter2Info_resetPauseStatus();
DUSK_NOINLINE u8 dMeter2Info_getPauseStatus();
DUSK_NOINLINE bool dMeter2Info_isGameStatus(int i_status);
DUSK_NOINLINE bool dMeter2Info_isTouchKeyCheck(int i_status);
DUSK_NOINLINE void dMeter2Info_setMapKeyDirection(u16 i_direction);
DUSK_NOINLINE bool dMeter2Info_isSub2DStatus(int i_flag);
DUSK_NOINLINE void dMeter2Info_offMenuInForce(int i_flag);
DUSK_NOINLINE bool dMeter2Info_isMenuInForce(int i_flag);
DUSK_NOINLINE void dMeter2Info_setMenuWindowClass(dMw_c* i_menu);
DUSK_NOINLINE void dMeter2Info_resetWarpStatus();
DUSK_NOINLINE u16 dMeter2Info_getMapKeyDirection();
DUSK_NOINLINE u8 dMeter2Info_getWindowStatus();
DUSK_NOINLINE void dMeter2Info_setMsgResource(void* i_res);
DUSK_NOINLINE void dMeter2Info_setStageMsgResource(void* i_res);
DUSK_NOINLINE void dMeter2Info_setMsgUnitResource(void* i_res);
DUSK_NOINLINE void* dMeter2Info_getMsgResource();
DUSK_NOINLINE void* dMeter2Info_getStageMsgResource();
DUSK_NOINLINE void* dMeter2Info_getMsgUnitResource();
DUSK_NOINLINE u8 dMeter2Info_getLightDropGetFlag(int i_no);
DUSK_NOINLINE s32 dMeter2Info_getMeterStringType();
DUSK_NOINLINE void dMeter2Info_setMeterClass(dMeter2_c* i_meter);
DUSK_NOINLINE void dMeter2Info_onLifeGaugeSE();
DUSK_NOINLINE void dMeter2Info_offLifeGaugeSE();
DUSK_NOINLINE u8 dMeter2Info_getLifeGaugeSE();
DUSK_NOINLINE const char* dMeter2Info_getSaveStageName();
DUSK_NOINLINE void dMeter2Info_onShopTalkFlag();
DUSK_NOINLINE void dMeter2Info_setLightDropGetFlag(int i_no, u8 i_flag);
DUSK_NOINLINE u8 dMeter2Info_getRentalBombBag();
DUSK_NOINLINE u8 dMeter2Info_getMiniGameItemSetFlag();
DUSK_NOINLINE void dMeter2Info_setMiniGameItem(u8 i_minigameFlag);
DUSK_NOINLINE void dMeter2Info_resetMiniGameItem(bool i_saveItem);
DUSK_NOINLINE void dMeter2Info_setTableMapRegionNo(u8 i_regionNo);
DUSK_NOINLINE void dMeter2Info_setGoldWolfMapType(u8 i_mapType);
DUSK_NOINLINE void dMeter2Info_changeWater(u8 i_slotNo);
DUSK_NOINLINE void dMeter2Info_warpInProc();
DUSK_NOINLINE void dMeter2Info_warpOutProc();
DUSK_NOINLINE dMeter2_c* dMeter2Info_getMeterClass();
DUSK_NOINLINE s16 dMeter2Info_getMsgKeyWaitTimer();
DUSK_NOINLINE void dMeter2Info_onDirectUseItem(int param_0);
DUSK_NOINLINE void dMeter2Info_setFloatingFlow(u16 i_flowID, s16 i_msgTimer, bool i_wakuVisible);
DUSK_NOINLINE u8 dMeter2Info_getGameOverType();
DUSK_NOINLINE void dMeter2Info_setGameOverType(u8 i_gameoverType);
DUSK_NOINLINE void dMeter2Info_setMsgKeyWaitTimer(s16 i_waitTimer);
DUSK_NOINLINE s32 dMeter2Info_getMsgTimeMs();
DUSK_NOINLINE s32 dMeter2Info_getTimeMs();
DUSK_NOINLINE void dMeter2Info_setMsgTimeMs(s32 i_msgTime);
DUSK_NOINLINE void dMeter2Info_setTimeMs(s32 i_time);
DUSK_NOINLINE u8 dMeter2Info_getNowCount();
DUSK_NOINLINE u8 dMeter2Info_getMaxCount();
DUSK_NOINLINE void dMeter2Info_setScopeZoomPointer(u8 param_0);
DUSK_NOINLINE void dMeter2Info_decMsgKeyWaitTimer();
DUSK_NOINLINE bool dMeter2Info_isFloatingMessageVisible();
DUSK_NOINLINE u8 dMeter2Info_getItemExplainWindowStatus();
DUSK_NOINLINE void dMeter2Info_setItemExplainWindowStatus(u8 i_value);
DUSK_NOINLINE void dMeter2Info_resetDirectUseItem();
DUSK_NOINLINE u16 dMeter2Info_getFloatingFlowID();
DUSK_NOINLINE u8 dMeter2Info_getCollectCursorPosX();
DUSK_NOINLINE u8 dMeter2Info_getCollectCursorPosY();
DUSK_NOINLINE void dMeter2Info_setCollectCursorPosXY(u8 x, u8 y);
DUSK_NOINLINE void dMeter2Info_onBlinkButton(int i_flag);
DUSK_NOINLINE bool dMeter2Info_isFloatingMessageWakuVisible();
DUSK_NOINLINE bool dMeter2Info_isBlinkButton(int i_flag);
DUSK_NOINLINE void dMeter2Info_resetBlinkButton();
DUSK_NOINLINE void dMeter2Info_resetFloatingMessage();
DUSK_NOINLINE s16 dMeter2Info_decFloatingMessageTimer();
DUSK_NOINLINE s16 dMeter2Info_getFloatingMessageTimer();
DUSK_NOINLINE u16 dMeter2Info_getFloatingMessageID();
DUSK_NOINLINE s16 dMeter2Info_getHorseLifeCount();
DUSK_NOINLINE void dMeter2Info_setMeterString(s32 i_string);
DUSK_NOINLINE void dMeter2Info_resetMeterString();
DUSK_NOINLINE void dMeter2Info_setMeterItemPanePtr(int i_no, CPaneMgr* i_pane);
DUSK_NOINLINE bool dMeter2Info_isShopTalkFlag();
DUSK_NOINLINE void dMeter2Info_setMapDrugFlag(bool i_flag);
DUSK_NOINLINE bool dMeter2Info_isTempBit(int i_bit);
DUSK_NOINLINE void dMeter2Info_offSub2DStatus(int i_bit);
DUSK_NOINLINE void dMeter2Info_onSub2DStatus(int i_bit);
DUSK_NOINLINE void dMeter2Info_set2DWidth(f32 i_width);
DUSK_NOINLINE void dMeter2Info_set2DHeight(f32 i_height);
DUSK_NOINLINE void dMeter2Info_set2DPosH(f32 i_posH);
DUSK_NOINLINE void dMeter2Info_set2DPosV(f32 i_posV);
DUSK_NOINLINE void dMeter2Info_offShopTalkFlag();
DUSK_NOINLINE void dMeter2Info_onUseButton(int i_button);
DUSK_NOINLINE f32 dMeter2Info_get2DWidth();
DUSK_NOINLINE f32 dMeter2Info_get2DHeight();
DUSK_NOINLINE void dMeter2Info_setWarpInfo(const char* i_stageName, const cXyz& i_position,
s16 i_angle, u8 i_roomNo, u8 param_4, u8 i_warpPlayerNo);
DUSK_NOINLINE u8 dMeter2Info_getTableMapRegionNo();
DUSK_NOINLINE u8 dMeter2Info_getGoldWolfMapType();
DUSK_NOINLINE bool dMeter2Info_isWindowAccept(int param_0);
DUSK_NOINLINE void dMeter2Info_setHorseLifeCount(s16 i_count);
DUSK_NOINLINE void dMeter2Info_offTempBit(int i_bit);
DUSK_NOINLINE void dMeter2Info_onTempBit(int i_bit);
DUSK_NOINLINE void dMeter2Info_setFloatingMessage(u16 i_msgID, s16 i_msgTimer, bool i_wakuVisible);
DUSK_NOINLINE void dMeter2Info_setMiniGameCount(s8 i_count);
DUSK_NOINLINE void dMeter2Info_onWindowAccept(int param_0);
DUSK_NOINLINE void dMeter2Info_offWindowAccept(int param_0);
DUSK_NOINLINE void dMeter2Info_onMenuInForce(int param_0);
#else
inline void dMeter2Info_Initialize() {
g_meter2_info.init();
}
@@ -865,4 +1009,6 @@ inline void dMeter2Info_onMenuInForce(int param_0) {
g_meter2_info.onMenuInForce(param_0);
}
#endif
#endif /* D_METER_D_METER2_INFO_H */
+47 -12
View File
@@ -27,18 +27,20 @@ public:
// Attributes
/* 0x04 */ BE(u16) message_id;
/* 0x06 */ BE(u16) event_label_id;
/* 0x08 */ u8 se_speaker;
/* 0x09 */ u8 fuki_kind;
/* 0x0A */ u8 output_type;
/* 0x0B */ u8 fuki_pos_type;
/* 0x0C */ u8 unk_0xc;
/* 0x0D */ u8 unk_0xd;
/* 0x0E */ u8 se_mood;
/* 0x0F */ u8 camera_id;
/* 0x10 */ u8 base_anm_id;
/* 0x11 */ u8 face_anm_id;
/* 0x12 */ BE(u16) unk_0x12;
/* 0x06 */ BE(u16) event_label_id; // saveBitLabels index set when the message displays
/* 0x08 */ u8 speaker; // Z2SpeechMgr2 voice bank ID
/* 0x09 */ u8 box_kind; // screen class, see dMsgObject_c::talkStartInit
/* 0x0A */ u8 draw_type; // text pacing, see jmessage_tSequenceProcessor::do_begin
/* 0x0B */ u8 box_position; // see dMsgObject_c::fukiPosCalc
/* 0x0C */ u8 item_no; // unused; legacy dItemNo, 0xFF = none
/* 0x0D */ u8 line_alignment; // 0 centered (JP only), 1 left; also copied to
// jmessage_tReference::mForm
/* 0x0E */ u8 speaker_mood; // grunt emotion index for the voice bank
/* 0x0F */ u8 camera_attr; // 1-10 talk-actor slot, >=11 talk-camera style
/* 0x10 */ u8 talk_anim; // NPC talk motion attribute
/* 0x11 */ u8 face_anim; // NPC talk face attribute
/* 0x12 */ u8 lines_per_page; // unused; runtime uses getLineMax()
/* 0x13 */ u8 _pad;
};
class JMSMesgInfo_c {
@@ -119,6 +121,25 @@ struct jmessage_tReference : public JMessage::TReference {
void setDemoFrame(u32 i_frame) { mDemoFrame = i_frame; }
void setTopColorType(u8 i_colorType) { mTopColorType = i_colorType; }
void setNowColorType(u8 i_colorType) { mNowColorType = i_colorType; }
#if TARGET_PC
void resetColors() {
mNowCCColor = 0xFFFFFFFF;
mNowGCColor = 0xFFFFFFFF;
mTopCCColor = 0xFFFFFFFF;
mTopGCColor = 0xFFFFFFFF;
mNowFullColor = false;
mTopFullColor = false;
}
void setFullColor(u32 ccColor, u32 gcColor) {
mNowCCColor = ccColor;
mNowGCColor = gcColor;
mNowFullColor = true;
}
void clearNowFullColor() { mNowFullColor = false; }
bool hasTopFullColor() { return mTopFullColor; }
u32 getTopCCColor() { return mTopCCColor; }
u32 getTopGCColor() { return mTopGCColor; }
#endif
void setTopTagScale(u16 i_tagScale) { mTopTagScale = i_tagScale; }
void setNowTagScale(u16 i_tagScale) { mNowTagScale = i_tagScale; }
void setRevoMessageID(u32 i_msgID) { mRevoMessageID = i_msgID; }
@@ -363,6 +384,14 @@ struct jmessage_tReference : public JMessage::TReference {
/* 0x1274 */ bool mSelectSetCancelFlag;
/* 0x1275 */ bool mBombNameUseFlag;
/* 0x1276 */ u8 mBatchColorFlag;
#if TARGET_PC
u32 mNowCCColor;
u32 mNowGCColor;
u32 mTopCCColor;
u32 mTopGCColor;
bool mNowFullColor;
bool mTopFullColor;
#endif
}; // Size: 0x1278
struct jmessage_tMeasureProcessor : public JMessage::TRenderingProcessor {
@@ -460,6 +489,9 @@ struct jmessage_tRenderingProcessor : public JMessage::TRenderingProcessor {
void do_selwidthcenter(int);
void do_heightcenter();
void do_color(u8);
#if TARGET_PC
void do_fullcolor(u32 ccColor, u32 gcColor);
#endif
void do_scale(f32);
void do_linedown(s16);
void do_transY(s16, bool);
@@ -638,6 +670,9 @@ struct jmessage_string_tRenderingProcessor : public JMessage::TRenderingProcesso
void do_rubystrcat(char*);
void do_outfont(u8);
void do_color(u8);
#if TARGET_PC
void do_fullcolor(u32 ccColor, u32 gcColor);
#endif
void do_scale(f32);
void do_linedown(s16);
void do_numset(s16);
+4 -13
View File
@@ -22,7 +22,7 @@ struct msg_class;
// all mesg_flow_node structs members might be wrong
struct mesg_flow_node {
/* 0x00 */ u8 type;
/* 0x01 */ u8 field_0x1;
/* 0x01 */ u8 subtype;
/* 0x02 */ BE(u16) msg_index;
/* 0x04 */ BE(u16) next_node_idx;
/* 0x06 */ BE(u16) unk_0x6;
@@ -30,7 +30,7 @@ struct mesg_flow_node {
struct mesg_flow_node_branch {
/* 0x00 */ u8 type;
/* 0x01 */ u8 field_0x1;
/* 0x01 */ u8 result_count;
/* 0x02 */ BE(u16) query_idx;
/* 0x04 */ BE(u16) param;
/* 0x06 */ BE(u16) next_node_idx;
@@ -172,15 +172,6 @@ public:
int event041(mesg_flow_node_event*, fopAc_ac_c*);
int event042(mesg_flow_node_event*, fopAc_ac_c*);
#if TARGET_PC
// HD additions
u16 query054(mesg_flow_node_branch*, fopAc_ac_c*, int);
u16 query055(mesg_flow_node_branch*, fopAc_ac_c*, int);
int event043(mesg_flow_node_event*, fopAc_ac_c*);
int event044(mesg_flow_node_event*, fopAc_ac_c*);
int event045(mesg_flow_node_event*, fopAc_ac_c*);
#endif
void initWord(fopAc_ac_c*, const char*, u8, int, fopAc_ac_c**);
#if DEBUG
@@ -194,8 +185,8 @@ public:
void setMsg(u32 msg) { mMsg = msg; }
bool checkEndFlow() { return (u32)field_0x26 == 1; }
static DUSK_GAME_DATA queryFunc mQueryList[DUSK_IF_ELSE(55, 53)];
static DUSK_GAME_DATA eventFunc mEventList[DUSK_IF_ELSE(46, 43)];
static DUSK_GAME_DATA queryFunc mQueryList[53];
static DUSK_GAME_DATA eventFunc mEventList[43];
private:
/* 0x04 */ u8* mFlow_p;
+2 -2
View File
@@ -280,13 +280,13 @@ public:
/* 0x150 */ f32 field_0x150;
/* 0x154 */ u32 mMessageID;
/* 0x158 */ u32 field_0x158;
/* 0x15C */ u32 field_0x15c;
/* 0x15C */ u32 mSelectMessageID; // message ID of the selection options message; 1000 = none
/* 0x160 */ int mIdx;
/* 0x164 */ u16 mNodeIdx;
/* 0x166 */ u16 field_0x166;
/* 0x168 */ u16 field_0x168;
/* 0x16A */ s16 field_0x16a;
/* 0x16C */ s16 field_0x16c;
/* 0x16C */ s16 mCurrentGroupID; // group whose BMG is parsed; -1 none, 0 common, 1-8 stage
/* 0x16E */ s16 field_0x16e;
/* 0x170 */ s16 mNowTalkFlowNo;
/* 0x172 */ s16 field_0x172;
+2 -2
View File
@@ -25,7 +25,7 @@ public:
void setTextBoxPtr(J2DTextBox* p_textBox) { mpTextBoxPtr = p_textBox; }
void resetType() { mType = 0x47; }
private:
// private:
/* 0x04 */ J2DTextBox* mpTextBoxPtr;
/* 0x08 */ f32 mPosX;
/* 0x0C */ f32 mPosY;
@@ -53,7 +53,7 @@ public:
void setRupeeColor(u8 color) { mRupeeColor = color; }
private:
// private:
/* 0x004 */ COutFontSet_c* mpOfs[35];
/* 0x090 */ J2DPicture* mpPane[70];
/* 0x1A8 */ f32 mAlphaRatio;
-28
View File
@@ -12,34 +12,6 @@ class J2DTextBox;
class JUTFont;
class STControl;
#if TARGET_PC
struct PaneCache {
u64 tag;
f32 origTransX;
f32 origTransY;
bool cached;
};
static PaneCache l_tagName[] = {
{MULTI_CHAR('m_00_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_00_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_01_3'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_01_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_02_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_2'), 0.0f, 0.0f, false},
{MULTI_CHAR('m03_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m03_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_04_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_1'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_05_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_05_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_06_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_0'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_07_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_07_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_08_4'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_09_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_09_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_10_3'), 0.0f, 0.0f, false},
{MULTI_CHAR('m_10_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m_11_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_0'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_2'), 0.0f, 0.0f, false},
{MULTI_CHAR('m12_3'), 0.0f, 0.0f, false}, {MULTI_CHAR('m12_4'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_2'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_1'), 0.0f, 0.0f, false}, {MULTI_CHAR('p_end_0'), 0.0f, 0.0f, false},
};
static PaneCache l_nameTagName[] = {
{MULTI_CHAR('name_00'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_01'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_02'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_03'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_04'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_05'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_06'), 0.0f, 0.0f, false}, {MULTI_CHAR('name_07'), 0.0f, 0.0f, false},
};
static PaneCache l_nameCurTagName[] = {
{MULTI_CHAR('s__n_00'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_01'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_02'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_03'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_04'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_05'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_06'), 0.0f, 0.0f, false}, {MULTI_CHAR('s__n_07'), 0.0f, 0.0f, false},
};
#endif
class dNm_HIO_c {
public:
dNm_HIO_c();
+1 -5
View File
@@ -1291,6 +1291,7 @@ public:
}
void set(const char*, s8, s16, s8, s8, u8);
void offEnable() { enabled = 0; }
void onEnable() { enabled = 1; }
BOOL isEnable() const { return enabled; }
s8 getWipe() const { return wipe; }
u8 getWipeSpeed() const { return wipe_speed; }
@@ -1416,11 +1417,6 @@ dStage_KeepDoorInfo* dStage_GetKeepDoorInfo();
dStage_KeepDoorInfo* dStage_GetRoomKeepDoorInfo();
void dStage_dt_c_fieldMapLoader(void* i_data, dStage_dt_c* i_stage);
#if TARGET_PC
// TP HD Cave of Shadows (D_SB11): reveal the los next-floor when a descent gate opens.
void dStage_showLOSNextFloor(int fromRoom);
#endif
#if DEBUG
void dStage_DebugDisp();
#endif
+5
View File
@@ -318,6 +318,11 @@ public:
/* 0x566 */ s8 field_0x566;
/* 0x567 */ s8 field_0x567;
#if TARGET_PC
u32 mItemGiveTag;
u8 mItemGiveOriginalNo;
#endif
#if !__MWERKS__
s8 actor_last_base_field;
#endif
+22 -13
View File
@@ -79,6 +79,10 @@ struct fopAcM_prm_class {
/* 0x1C */ fpc_ProcID parent_id;
/* 0x20 */ s8 argument;
/* 0x21 */ s8 room_no;
#if TARGET_PC
u32 mItemGiveTag;
u8 mItemGiveOriginalNo;
#endif
};
struct fopAcM_search4ev_prm {
@@ -516,8 +520,9 @@ s32 fopAcM_SearchByName(s16 i_procName, fopAc_ac_c** i_outActor);
fopAcM_prm_class* fopAcM_CreateAppend();
fopAcM_prm_class* createAppend(u16 i_setId, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
fpc_ProcID i_parentId);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
fpc_ProcID i_parentId IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
void fopAcM_Log(fopAc_ac_c const* i_actor, char const* i_message);
@@ -526,19 +531,22 @@ s32 fopAcM_delete(fopAc_ac_c* i_actor);
s32 fopAcM_delete(fpc_ProcID i_actorID);
fpc_ProcID fopAcM_create(s16 i_procName, u16 i_setId, u32 i_parameters, const cXyz* i_pos,
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc);
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
fpc_ProcID fopAcM_create(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
inline fpc_ProcID fopAcM_Create(s16 i_procName, createFunc i_createFunc, void* params) {
return fpcM_Create(i_procName, i_createFunc,params);
}
fopAc_ac_c* fopAcM_fastCreate(s16 i_procName, u32 i_parameters, const cXyz* i_pos, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument,
createFunc i_createFunc, void* i_createFuncData);
const csXyz* i_angle, const cXyz* i_scale, s8 i_argument, createFunc i_createFunc,
void* i_createFuncData IF_DUSK_ARG(u32 i_itemGiveTag = 0)
IF_DUSK_ARG(u8 i_itemOriginalNo = 0xFF));
fopAc_ac_c* fopAcM_fastCreate(const char* i_actorname, u32 i_parameters, const cXyz* i_pos,
int i_roomNo, const csXyz* i_angle, const cXyz* i_scale,
@@ -623,11 +631,11 @@ fopAc_ac_c* fopAcM_getItemEventPartner(const fopAc_ac_c*);
fopAc_ac_c* fopAcM_getEventPartner(const fopAc_ac_c*);
fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 param_2,
int i_itemBitNo, int i_roomNo, csXyz const* i_angle,
cXyz const* i_scale);
int i_itemBitNo, int i_roomNo, csXyz const* i_angle,
cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_itemBitNo,
int i_roomNo, csXyz const* i_angle, cXyz const* i_scale);
int i_roomNo, csXyz const* i_angle, cXyz const* i_scale IF_DUSK_ARG(u32 i_itemGiveTag = 0));
u8 fopAcM_getItemNoFromTableNo(u8 i_tableNo);
@@ -641,11 +649,12 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_tableNo, int i_it
bool i_createDirect);
fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitNo,
const csXyz* i_angle, int i_roomNo, const cXyz* scale, u8 param_7);
const csXyz* i_angle, int i_roomNo, const cXyz* scale,
u8 param_7 IF_DUSK_ARG(u32 i_itemGiveTag = 0));
fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF,
f32 i_speedY, int param_8);
const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY,
int param_8 IF_DUSK_ARG(const char* i_itemCheckName = NULL));
fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo,
const csXyz* i_angle, const cXyz* i_scale, int param_6,
+1 -1
View File
@@ -30,7 +30,7 @@ struct msg_class {
/* 0xDC */ fopAc_ac_c* talk_actor;
/* 0xE0 */ cXyz pos;
/* 0xEC */ u32 msg_idx;
/* 0xF0 */ u32 field_0xf0;
/* 0xF0 */ u32 select_msg_idx; // selection options message ID; 1000 = none
/* 0xF4 */ u32 field_0xf4;
/* 0xF8 */ u16 mode;
/* 0xFA */ u8 select_idx;
+3 -3
View File
@@ -19,7 +19,7 @@ struct fopMsg_prm_class {
/* 0x00 */ fopAc_ac_c* talk_actor;
/* 0x04 */ cXyz pos;
/* 0x10 */ u32 msg_idx;
/* 0x14 */ u32 field_0x14;
/* 0x14 */ u32 select_msg_idx; // selection options message ID; 1000 = none
/* 0x18 */ fpc_ProcID field_0x18;
}; // Size: 0x1C
@@ -46,8 +46,8 @@ void fopMsgM_setMessageID(fpc_ProcID msg_id);
void fopMsgM_destroyExpHeap(JKRExpHeap* i_heap);
f32 fopMsgM_valueIncrease(int param_0, int param_1, u8 i_type);
s32 fopMsgM_setStageLayer(void* i_process);
fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, fopAc_ac_c* i_talkActor, u32 param_2);
fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, u32 param_1);
fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, fopAc_ac_c* i_talkActor, u32 i_selectMsgIdx);
fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, u32 i_selectMsgIdx);
fpc_ProcID fopMsgM_messageSetDemo(u32 i_msgidx);
msg_class* fopMsgM_SearchByID(fpc_ProcID i_id);
TEXT_SPAN fopMsgM_messageGet(TEXT_SPAN i_stringBuf, u32 i_msgId);
+27
View File
@@ -123,6 +123,14 @@ inline int __builtin_clz(unsigned int v) {
#endif
#define DUSK_GAME_EXTERN extern DUSK_GAME_DATA
#if defined(_MSC_VER)
#define DUSK_NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#define DUSK_NOINLINE __attribute__((noinline))
#else
#define DUSK_NOINLINE
#endif
#define FAST_DIV(x, n) (x >> (n / 2))
#define SQUARE(x) ((x) * (x))
@@ -256,4 +264,23 @@ using std::isnan;
#define DUSK_CONST IF_DUSK(const)
#define DUSK_CONSTEXPR IF_DUSK(constexpr)
#if TARGET_PC && defined(DUSK_BUILDING_GAME)
#include "dusk/mods/item.hpp"
#define DUSK_ITEM_CHECK(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check_commit(name, (item_no), giver).itemNo
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) \
(::dusk::mods::item_check_commit(name, (item_no), giver).itemNo)
#define DUSK_ITEM_CHECK_PREVIEW(name, item_no, giver) \
(item_no) = ::dusk::mods::item_check(name, (item_no), giver)
#define DUSK_ITEM_CHECK_PREVIEW_EXPR(name, item_no, giver) \
(::dusk::mods::item_check(name, (item_no), giver))
#define DUSK_GIVE_TAG(name) IF_DUSK_ARG(::dusk::mods::item_give_tag(name))
#else
#define DUSK_ITEM_CHECK(name, item_no, giver)
#define DUSK_ITEM_CHECK_EXPR(name, item_no, giver) (item_no)
#define DUSK_ITEM_CHECK_PREVIEW(name, item_no, giver)
#define DUSK_ITEM_CHECK_PREVIEW_EXPR(name, item_no, giver) (item_no)
#define DUSK_GIVE_TAG(name)
#endif
#endif
+178
View File
@@ -0,0 +1,178 @@
#pragma once
#include <bit>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>
namespace dusk {
namespace detail {
template <size_t Size>
struct uint_of_size;
template <>
struct uint_of_size<1> {
using type = uint8_t;
};
template <>
struct uint_of_size<2> {
using type = uint16_t;
};
template <>
struct uint_of_size<4> {
using type = uint32_t;
};
template <>
struct uint_of_size<8> {
using type = uint64_t;
};
template <size_t Size>
using uint_of_size_t = uint_of_size<Size>::type;
template <typename T>
requires(std::is_trivially_copyable_v<T>)
T unaligned_load(const void* source) noexcept {
T value;
std::memcpy(&value, source, sizeof(value));
return value;
}
template <typename T>
requires(std::is_trivially_copyable_v<T>)
void unaligned_store(void* destination, T value) noexcept {
std::memcpy(destination, &value, sizeof(value));
}
} // namespace detail
template <typename T>
requires(std::is_unsigned_v<T>)
constexpr T bswap(T value) noexcept {
if constexpr (sizeof(T) == 1) {
return value;
} else if constexpr (sizeof(T) == 2) {
return static_cast<T>((value << 8) | (value >> 8));
} else if constexpr (sizeof(T) == 4) {
return static_cast<T>(((value & 0x000000ffU) << 24) | ((value & 0x0000ff00U) << 8) |
((value & 0x00ff0000U) >> 8) | ((value & 0xff000000U) >> 24));
} else {
static_assert(sizeof(T) == 8);
return static_cast<T>(
((value & 0x00000000000000ffULL) << 56) | ((value & 0x000000000000ff00ULL) << 40) |
((value & 0x0000000000ff0000ULL) << 24) | ((value & 0x00000000ff000000ULL) << 8) |
((value & 0x000000ff00000000ULL) >> 8) | ((value & 0x0000ff0000000000ULL) >> 24) |
((value & 0x00ff000000000000ULL) >> 40) | ((value & 0xff00000000000000ULL) >> 56));
}
}
/// Reads an unaligned integral value in the specified byte order.
template <typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
T read_bits(const void* source, std::endian endian = std::endian::big) noexcept {
using Bits = std::make_unsigned_t<T>;
Bits value = detail::unaligned_load<Bits>(source);
if constexpr (sizeof(Bits) > 1) {
if (endian != std::endian::native) {
value = bswap(value);
}
}
return std::bit_cast<T>(value);
}
template <typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
constexpr T read_bits(const uint8_t* source, std::endian endian = std::endian::big) noexcept {
if (!std::is_constant_evaluated()) {
return read_bits<T>(static_cast<const void*>(source), endian);
}
using Bits = std::make_unsigned_t<T>;
Bits value{};
if (endian == std::endian::big) {
for (size_t i = 0; i < sizeof(Bits); ++i) {
value = static_cast<Bits>((value << 8) | source[i]);
}
} else {
for (size_t i = 0; i < sizeof(Bits); ++i) {
value |= static_cast<Bits>(source[i]) << (i * 8);
}
}
return std::bit_cast<T>(value);
}
/// Reads an unaligned floating-point value in the specified byte order.
template <typename T>
requires(
std::is_floating_point_v<T> && requires { typename detail::uint_of_size_t<sizeof(T)>; })
T read_bits(const void* source, std::endian endian = std::endian::big) noexcept {
using Bits = detail::uint_of_size_t<sizeof(T)>;
return std::bit_cast<T>(read_bits<Bits>(source, endian));
}
template <typename T>
requires(
std::is_floating_point_v<T> && requires { typename detail::uint_of_size_t<sizeof(T)>; })
constexpr T read_bits(const uint8_t* source, std::endian endian = std::endian::big) noexcept {
using Bits = detail::uint_of_size_t<sizeof(T)>;
return std::bit_cast<T>(read_bits<Bits>(source, endian));
}
/// Writes an unaligned integral value in the specified byte order.
template <typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
void write_bits(void* destination, T value, std::endian endian = std::endian::big) noexcept {
using Bits = std::make_unsigned_t<T>;
Bits bits = std::bit_cast<Bits>(value);
if constexpr (sizeof(Bits) > 1) {
if (endian != std::endian::native) {
bits = bswap(bits);
}
}
detail::unaligned_store(destination, bits);
}
template <typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
constexpr void write_bits(
uint8_t* destination, T value, std::endian endian = std::endian::big) noexcept {
if (!std::is_constant_evaluated()) {
write_bits(static_cast<void*>(destination), value, endian);
return;
}
using Bits = std::make_unsigned_t<T>;
const Bits bits = std::bit_cast<Bits>(value);
if (endian == std::endian::big) {
for (size_t i = 0; i < sizeof(Bits); ++i) {
destination[sizeof(Bits) - i - 1] = static_cast<uint8_t>(bits >> (i * 8));
}
} else {
for (size_t i = 0; i < sizeof(Bits); ++i) {
destination[i] = static_cast<uint8_t>(bits >> (i * 8));
}
}
}
/// Writes an unaligned floating-point value in the specified byte order.
template <typename T>
requires(
std::is_floating_point_v<T> && requires { typename detail::uint_of_size_t<sizeof(T)>; })
void write_bits(void* destination, T value, std::endian endian = std::endian::big) noexcept {
using Bits = detail::uint_of_size_t<sizeof(T)>;
write_bits(destination, std::bit_cast<Bits>(value), endian);
}
template <typename T>
requires(
std::is_floating_point_v<T> && requires { typename detail::uint_of_size_t<sizeof(T)>; })
constexpr void write_bits(
uint8_t* destination, T value, std::endian endian = std::endian::big) noexcept {
using Bits = detail::uint_of_size_t<sizeof(T)>;
write_bits(destination, std::bit_cast<Bits>(value), endian);
}
} // namespace dusk
+20
View File
@@ -112,6 +112,11 @@ public:
mSerialNo = serial_no;
}
#ifdef TARGET_PC
void setFileName(const std::string& fileName);
const char* getFileName();
#endif
/* 0x0000 */ u8 mData[SAVEFILE_SIZE];
/* 0x1FBC */ u8 mChannel;
/* 0x1FBD */ u8 mCopyToPos;
@@ -124,6 +129,11 @@ public:
/* 0x1FEC */ s32 mNandState;
/* 0x1FF0 */ u64 mSerialNo;
/* 0x1FF8 */ u32 mDataVersion;
#ifdef TARGET_PC
bool mInitialized;
std::string mFileName;
#endif
}; // Size: 0x2000
STATIC_ASSERT(sizeof(mDoMemCd_Ctrl_c) == 8192);
@@ -230,4 +240,14 @@ inline s32 mDoMemCd_checkNANDFile() {
}
#endif
#ifdef TARGET_PC
inline void mDoMemCd_SetFileName(const std::string& fileName) {
g_mDoMemCd_control.setFileName(fileName);
}
inline const char* mDoMemCd_GetFileName() {
return g_mDoMemCd_control.getFileName();
}
#endif
#endif /* M_DO_M_DO_MEMCARD_H */
+1 -1
View File
@@ -68,7 +68,7 @@ public:
JKRMemArchive* getArchive() const { return mArchive; }
JKRHeap* getHeap() const { return mHeap; }
private:
// private:
/* 0x14 */ u8 mMountDirection;
/* 0x18 */ s32 mEntryNumber;
/* 0x1C */ JKRMemArchive* mArchive;
@@ -1761,16 +1761,6 @@ public:
virtual ~J3DIndBlockNull() {}
};
#ifdef DUSK_TPHD
struct PolygonOffset {
BE(f32) mFrontOffset;
BE(f32) mFrontScale;
BE(f32) mBackOffset;
BE(f32) mBackScale;
BE(f32) mClamp;
};
#endif
/**
* @ingroup jsystem-j3d
*
@@ -1803,10 +1793,6 @@ public:
virtual void setDither(u8 const*) {}
virtual void setDither(u8) {}
virtual u8 getDither() const { return 0; }
#ifdef DUSK_TPHD
virtual void setPolygonOffset(const PolygonOffset&) {}
virtual PolygonOffset* getPolygonOffset() { return NULL; }
#endif
virtual u32 getFogOffset() const { return 0; }
virtual void setFogOffset(u32) {}
virtual ~J3DPEBlock() {}
@@ -1970,25 +1956,12 @@ public:
virtual void setFogOffset(u32 fogOffset) { mFogOffset = fogOffset; }
virtual ~J3DPEBlockFull() {}
#ifdef DUSK_TPHD
virtual void setPolygonOffset(const PolygonOffset& offset) {
mPolygonOffset = offset;
}
virtual PolygonOffset* getPolygonOffset() {
return &mPolygonOffset;
}
#endif
/* 0x04 */ J3DFog mFog;
/* 0x30 */ J3DAlphaComp mAlphaComp;
/* 0x34 */ J3DBlend mBlend;
/* 0x38 */ J3DZMode mZMode;
/* 0x3A */ u8 mZCompLoc;
/* 0x3B */ u8 mDither;
#ifdef DUSK_TPHD
PolygonOffset mPolygonOffset;
#endif
/* 0x3C */ u32 mFogOffset;
}; // Size: 0x40
@@ -46,13 +46,6 @@ struct J3DMaterialInitData {
/* 0x14A */ BE(u16) mNBTScaleIdx;
}; // size 0x14C
#ifdef DUSK_TPHD
struct J3DMaterialInitData_MAT4 : public J3DMaterialInitData {
/* 0x14C */ BE(u16) mPolygonOffsetIdx;
}; // size 0x14E
#endif
/**
* @ingroup jsystem-j3d
*
@@ -150,29 +143,10 @@ public:
J3DNBTScale newNBTScale(int) const;
u16 getMaterialID(int idx) const { return mpMaterialID[idx]; }
#ifdef DUSK_TPHD
u8 getMaterialMode(int idx) const { return getMatInitData(idx)->mMaterialMode; }
#else
u8 getMaterialMode(int idx) const { return mpMaterialInitData[mpMaterialID[idx]].mMaterialMode; }
#endif
#if DUSK_TPHD
const PolygonOffset newPolygonOffset(int) const;
J3DMaterialInitData* getMatInitData(int idx) const {
static const u32 sInitDataSizes[] = {0, 0, 0x138, 0x14C, 0x14E};
return (J3DMaterialInitData*)((u8*)mpMaterialInitData + sInitDataSizes[mBlockType] * getMaterialID(idx));
}
u16 mBlockType;
#endif
/* 0x00 */ u16 mMaterialNum;
#if DUSK_TPHD
/* 0x04 */ void* mpMaterialInitData;
#else
/* 0x04 */ J3DMaterialInitData* mpMaterialInitData;
#endif
/* 0x08 */ BE(u16)* mpMaterialID;
/* 0x0C */ J3DIndInitData* mpIndInitData;
/* 0x10 */ GXColor* mpMatColor;
@@ -201,9 +175,6 @@ public:
/* 0x6C */ u8* mpZCompLoc;
/* 0x70 */ u8* mpDither;
/* 0x74 */ J3DNBTScaleInfo* mpNBTScaleInfo;
#ifdef DUSK_TPHD
PolygonOffset* mpPolygonOffsets;
#endif
/* 0x78 */ J3DDisplayListInit* mpDisplayListInit;
/* 0x7C */ J3DPatchingInfo* mpPatchingInfo;
/* 0x80 */ J3DCurrentMtxInfo* mpCurrentMtxInfo;
@@ -9,6 +9,7 @@
class J3DModelData;
class J3DMaterialTable;
struct J3DModelHierarchy;
class J3DVertexData;
#if TARGET_PC
#define OFFSET_PTR_V0 BE(u32)
@@ -169,12 +170,6 @@ struct J3DMaterialBlock_v21 : public J3DModelBlock {
/* 0x74 */ OFFSET_PTR_V0 mpNBTScaleInfo;
};
#ifdef DUSK_TPHD
struct J3DMaterialBlock_MAT4 : public J3DMaterialBlock {
/* 0x84 */ OFFSET_PTR_V0 mpPolygonOffsets;
};
#endif
/**
* @ingroup jsystem-j3d
*
@@ -6,11 +6,17 @@
#include "global.h"
#include "helpers/endian.h"
#if TARGET_PC
#include <atomic>
#include <string>
#include <unordered_map>
#endif
class JKRHeap;
/**
* @ingroup jsystem-jkernel
*
*
*/
struct SArcHeader {
/* 0x00 */ BE(u32) signature;
@@ -25,7 +31,7 @@ struct SArcHeader {
/**
* @ingroup jsystem-jkernel
*
*
*/
struct SArcDataInfo {
/* 0x00 */ BE(u32) num_nodes;
@@ -59,7 +65,7 @@ extern u32 sCurrentDirID__10JKRArchive; // JKRArchive::sCurrentDirID
/**
* @ingroup jsystem-jkernel
*
*
*/
class JKRArchive : public JKRFileLoader {
public:
@@ -192,9 +198,7 @@ public:
u32 countFile() const { return mArcInfoBlock->num_file_entries; }
s32 countDirectory() const { return mArcInfoBlock->num_nodes; }
u8 getMountMode() const { return mMountMode; }
bool isFileEntry(u32 param_0) const {
return getFileAttribute(param_0) & 1;
}
bool isFileEntry(u32 param_0) const { return getFileAttribute(param_0) & 1; }
public:
/* 0x00 */ // vtable
@@ -210,7 +214,36 @@ public:
/* 0x54 */ const char* mStringTable;
#if TARGET_PC
u32 getFileSize(SDIFileEntry* entry) const;
void* getOverlayData(SDIFileEntry* entry, u32* outSize);
bool getOverlayFileSize(SDIFileEntry* entry, u32* outSize) const;
static void notifyOverlayFilesChanged();
protected:
void** mFileData;
struct ArcOverlayResource {
u32 entryIndex;
u32 size;
u64 generation;
};
// Resource pointers remain valid until removed through the JKR resource APIs.
// That way, any pointers to old overlay data remain valid even when the overlay changed.
std::unordered_map<void*, ArcOverlayResource> mArcOverlayResources;
mutable std::unordered_map<u32, void*> mActiveArcOverlayResources;
mutable std::string mArcOverlaysPath;
mutable std::unordered_map<u32, std::string> mIdxToPathMap;
mutable bool mArcOverlaysPathResolved = false;
bool buildArcOverlaysPath() const;
void buildIndexToPathMap(u32 dirIndex, const std::string& currentPath) const;
bool getOverlayPath(SDIFileEntry* entry, std::string& path) const;
void* getActiveOverlayData(SDIFileEntry* entry, u32* outSize) const;
bool copyOverlayData(void* buffer, u32 bufferSize, SDIFileEntry* entry, u32* outSize);
bool getOverlayResourceSize(const void* data, u32* outSize) const;
bool removeOverlayResource(void* resource, bool freeResource);
void removeAllOverlayResources();
#endif
protected:
@@ -234,7 +267,7 @@ public:
} else if (attr & JKRARCHIVE_ATTR_YAZ0) {
return COMPRESSION_YAZ0;
} else {
return COMPRESSION_YAY0;
return COMPRESSION_YAY0;
}
}
@@ -243,6 +276,9 @@ public:
protected:
static DUSK_GAME_DATA u32 sCurrentDirID;
#if TARGET_PC
static std::atomic<u64> sArcOverlayGeneration;
#endif
};
inline JKRCompression JKRConvertAttrToCompressionType(int attr) {
@@ -261,8 +297,8 @@ inline bool JKRRemoveResource(void* resource, JKRFileLoader* fileLoader) {
return JKRFileLoader::removeResource(resource, fileLoader);
}
inline JKRArchive* JKRMountArchive(void* ptr, JKRHeap* heap,
JKRArchive::EMountDirection mountDirection) {
inline JKRArchive* JKRMountArchive(
void* ptr, JKRHeap* heap, JKRArchive::EMountDirection mountDirection) {
return JKRArchive::mount(ptr, heap, mountDirection);
}
@@ -7,6 +7,7 @@
#include <new>
#include <utility>
#include <cstdint>
#include <type_traits>
class JKRHeap;
typedef void (*JKRErrorHandler)(void*, u32, int);
@@ -174,10 +174,14 @@ struct TProcessor {
}
void on_character(int iCharacter) { do_character(iCharacter); }
#if TARGET_PC
const char* on_message_limited(u16 u16Index) const;
#else
const char* on_message_limited(u16 u16Index) const {
JUT_ASSERT(482, pResourceCache_!=NULL);
return pResourceCache_->getMessageText_messageIndex(u16Index);
}
#endif
bool on_setBegin_isReady_() const { return do_setBegin_isReady_(); }
@@ -195,6 +199,10 @@ struct TProcessor {
return 1;
}
#if TARGET_PC
void* getMessageEntry_messageCode(u16 u16Code, u16 u16Index) const;
const char* getMessageText_messageCode(u16 u16Code, u16 u16Index) const;
#else
void* getMessageEntry_messageCode(u16 u16Code, u16 u16Index) const {
const TResource* pResource = getResource_groupID(u16Code);
@@ -214,6 +222,7 @@ struct TProcessor {
return pResourceCache_->getMessageText_messageEntry(pEntry);
}
#endif
void stack_pushCurrent_(const char* pszText) {
oStack_.push(getCurrent());
@@ -7,9 +7,6 @@
#include "JSystem/J3DGraphBase/J3DMaterial.h"
#include "JSystem/JMath/JMath.h"
#include "m_Do/m_Do_mtx.h"
#if TARGET_PC
#include "dusk/settings.h"
#endif
void J3DMtxCalcJ3DSysInitBasic::init(Vec const& scale, Mtx const& mtx) {
J3DSys::mCurrentS = scale;
@@ -181,23 +178,7 @@ void J3DJoint::entryIn() {
matPacket->setMaterialAnmID(mesh->getMaterialAnm());
matPacket->setShapePacket(shapePacket);
bool isDrawModeOpaTexEdge = mesh->isDrawModeOpaTexEdge() == FALSE;
#if TARGET_PC
// TP HD J3DJoint::entryIn (FUN_02b57690):
J3DDrawBuffer* drawBuffer = j3dSys.getDrawBuffer(isDrawModeOpaTexEdge);
u8 r24;
if (dusk::tphd_active() && (mesh->mMaterialID & 0x80000000) != 0 &&
(mesh->getMaterialMode() & 0x20) != 0 &&
drawBuffer->getSortMode() == J3DDrawBufSortMode_Mat)
{
matPacket->drawClear();
shapePacket->drawClear();
r24 = drawBuffer->entryImm(matPacket, 1);
} else {
r24 = matPacket->entry(drawBuffer);
}
#else
u8 r24 = matPacket->entry(j3dSys.getDrawBuffer(isDrawModeOpaTexEdge));
#endif
if (r24) {
j3dSys.setMatPacket(matPacket);
J3DDrawBuffer::entryNum++;
@@ -230,13 +230,6 @@ void J3DMatPacket::draw() {
#endif
packet->getShape()->loadPreDrawSetting();
#if DUSK_TPHD
{
const auto* offs = mpMaterial->getPEBlock()->getPolygonOffset();
GX2SetPolygonOffset(offs->mFrontOffset, offs->mFrontScale, offs->mBackOffset, offs->mBackScale, offs->mClamp);
}
#endif
while (packet != NULL) {
if (packet->getDisplayListObj() != NULL) {
packet->getDisplayListObj()->callDL();
@@ -12,9 +12,6 @@
#include "dusk/logging.h"
J3DMaterialFactory::J3DMaterialFactory(J3DMaterialBlock const& i_block) {
#ifdef DUSK_TPHD
mBlockType = (i_block.mBlockType & 0xFF) - 0x30; // get number at last byte of block name
#endif
mMaterialNum = i_block.mMaterialNum;
mpMaterialInitData = JSUConvertOffsetToPtr<J3DMaterialInitData>(&i_block, i_block.mpMaterialInitData);
mpMaterialID = JSUConvertOffsetToPtr<BE(u16)>(&i_block, i_block.mpMaterialID);
@@ -49,11 +46,6 @@ J3DMaterialFactory::J3DMaterialFactory(J3DMaterialBlock const& i_block) {
mpZCompLoc = JSUConvertOffsetToPtr<u8>(&i_block, i_block.mpZCompLoc);
mpDither = JSUConvertOffsetToPtr<u8>(&i_block, i_block.mpDither);
mpNBTScaleInfo = JSUConvertOffsetToPtr<J3DNBTScaleInfo>(&i_block, i_block.mpNBTScaleInfo);
#ifdef DUSK_TPHD
if(mBlockType >= 4) {
mpPolygonOffsets = JSUConvertOffsetToPtr<PolygonOffset>(&i_block, reinterpret_cast<const J3DMaterialBlock_MAT4&>(i_block).mpPolygonOffsets);
}
#endif
mpDisplayListInit = NULL;
mpPatchingInfo = NULL;
mpCurrentMtxInfo = NULL;
@@ -82,11 +74,7 @@ u16 J3DMaterialFactory::countUniqueMaterials() {
}
u32 J3DMaterialFactory::countTexGens(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTexGenNumIdx != 0xff) {
return mpTexGenNum[mtl_init_data->mTexGenNumIdx];
}
@@ -94,11 +82,7 @@ u32 J3DMaterialFactory::countTexGens(int i_idx) const {
}
u32 J3DMaterialFactory::countStages(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
u32 count1 = 0;
u32 count2 = 0;
if (mtl_init_data->mTevStageNumIdx != 0xff) {
@@ -173,9 +157,6 @@ J3DMaterial* J3DMaterialFactory::createNormalMaterial(J3DMaterial* i_material, i
i_material->mPEBlock->setZMode(newZMode(i_idx));
i_material->mPEBlock->setZCompLoc(newZCompLoc(i_idx));
i_material->mPEBlock->setDither(newDither(i_idx));
#ifdef DUSK_TPHD
i_material->mPEBlock->setPolygonOffset(newPolygonOffset(i_idx));
#endif
i_material->mTevBlock->setTevStageNum(newTevStageNum(i_idx));
for (u8 i = 0; i < tex_num; i++) {
i_material->mTevBlock->setTexNo(i, newTexNo(i_idx, i));
@@ -184,11 +165,7 @@ J3DMaterial* J3DMaterialFactory::createNormalMaterial(J3DMaterial* i_material, i
i_material->mTevBlock->setTevOrder(i, newTevOrder(i_idx, i));
}
for (u8 i = 0; i < tev_stage_num_max; i++) {
#ifdef DUSK_TPHD
J3DMaterialInitData* material_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* material_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
i_material->mTevBlock->setTevStage(i, newTevStage(i_idx, i));
if (material_init_data->mTevSwapModeIdx[i] != 0xffff) {
i_material->mTevBlock->getTevStage(i)->setTexSel(
@@ -223,11 +200,7 @@ J3DMaterial* J3DMaterialFactory::createNormalMaterial(J3DMaterial* i_material, i
for (u8 i = 0; i < 8; i++) {
i_material->mTexGenBlock->setTexMtx(i, newTexMtx(i_idx, i));
}
#ifdef DUSK_TPHD
J3DMaterialInitData* material_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* material_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
J3DMaterialInitData* material_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
for (u8 i = 0; i < tev_stage_num_max; i++) {
if (material_init_data->mTevKColorSel[i] != 0xff) {
i_material->mTevBlock->setTevKColorSel(i, material_init_data->mTevKColorSel[i]);
@@ -300,11 +273,7 @@ J3DMaterial* J3DMaterialFactory::createPatchedMaterial(J3DMaterial* i_material,
i_material->mTevBlock->setTevColor(i, newTevColor(i_idx, i));
}
for (u8 i = 0; i < tev_stage_num; i++) {
#ifdef DUSK_TPHD
J3DMaterialInitData* material_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* material_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
i_material->mTevBlock->setTevStage(i, newTevStage(i_idx, i));
if (material_init_data->mTevSwapModeIdx[i] != 0xffff) {
i_material->mTevBlock->getTevStage(i)->setTexSel(
@@ -313,11 +282,7 @@ J3DMaterial* J3DMaterialFactory::createPatchedMaterial(J3DMaterial* i_material,
mpTevSwapModeInfo[material_init_data->mTevSwapModeIdx[i]].mRasSel);
}
}
#ifdef DUSK_TPHD
J3DMaterialInitData* init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
J3DMaterialInitData* init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
for (u8 i = 0; i < tev_stage_num; i++) {
if (init_data->mTevKColorSel[i] != 0xff) {
i_material->mTevBlock->setTevKColorSel(i, init_data->mTevKColorSel[i]);
@@ -458,11 +423,7 @@ u32 J3DMaterialFactory::calcSizeNormalMaterial(J3DMaterial* i_material, int i_id
size += J3DMaterial::calcSizeTevBlock((u16)tev_stage_num_max);
size += J3DMaterial::calcSizeIndBlock(ind_flag);
size += J3DMaterial::calcSizePEBlock(pe_flag, getMaterialMode(i_idx));
#ifdef DUSK_TPHD
J3DMaterialInitData* init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
for (u32 i = 0; i < 8; i++) {
if (init_data->mTexMtxIdx[i] != 0xffff) {
size += sizeof(J3DTexMtx);
@@ -483,11 +444,7 @@ u32 J3DMaterialFactory::calcSizePatchedMaterial(J3DMaterial* i_material, int i_i
size += sizeof(J3DTevBlockPatched);
size += J3DMaterial::calcSizeIndBlock(ind_flag);
size += J3DMaterial::calcSizePEBlock(0x10000000, getMaterialMode(i_idx));
#ifdef DUSK_TPHD
J3DMaterialInitData* init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
for (u32 i = 0; i < 8; i++) {
if (init_data->mTexMtxIdx[i] != 0xffff) {
size += sizeof(J3DTexMtx);
@@ -517,11 +474,7 @@ J3DGXColor J3DMaterialFactory::newMatColor(int i_idx, int i_no) const {
#else
J3DGXColor dflt = GXColor{0xff, 0xff, 0xff, 0xff};
#endif
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mMatColorIdx[i_no] != 0xffff) {
return (GXColor) mpMatColor[mtl_init_data->mMatColorIdx[i_no]];
} else {
@@ -530,11 +483,7 @@ J3DGXColor J3DMaterialFactory::newMatColor(int i_idx, int i_no) const {
}
const u8 J3DMaterialFactory::newColorChanNum(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mColorChanNumIdx != 0xff) {
return mpColorChanNum[mtl_init_data->mColorChanNumIdx];
} else {
@@ -544,11 +493,7 @@ const u8 J3DMaterialFactory::newColorChanNum(int i_idx) const {
J3DColorChan J3DMaterialFactory::newColorChan(int i_idx, int i_no) const {
u32 r29 = 0;
#ifdef DUSK_TPHD
J3DMaterialInitData* init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (init_data->mColorChanIdx[i_no] != 0xffff) {
return J3DColorChan(mpColorChanInfo[init_data->mColorChanIdx[i_no]]);
} else {
@@ -562,11 +507,7 @@ J3DGXColor J3DMaterialFactory::newAmbColor(int i_idx, int i_no) const {
#else
J3DGXColor dflt = GXColor{0x32, 0x32, 0x32, 0x32};
#endif
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mAmbColorIdx[i_no] != 0xffff) {
return (GXColor) mpAmbColor[mtl_init_data->mAmbColorIdx[i_no]];
} else {
@@ -576,11 +517,7 @@ J3DGXColor J3DMaterialFactory::newAmbColor(int i_idx, int i_no) const {
u32 J3DMaterialFactory::newTexGenNum(int i_idx) const {
u32 r30 = 0;
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTexGenNumIdx != 0xff) {
return mpTexGenNum[mtl_init_data->mTexGenNumIdx];
} else {
@@ -589,11 +526,7 @@ u32 J3DMaterialFactory::newTexGenNum(int i_idx) const {
}
J3DTexCoord J3DMaterialFactory::newTexCoord(int i_idx, int i_no) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTexCoordIdx[i_no] != 0xffff) {
return J3DTexCoord(mpTexCoordInfo[mtl_init_data->mTexCoordIdx[i_no]]);
} else {
@@ -603,11 +536,7 @@ J3DTexCoord J3DMaterialFactory::newTexCoord(int i_idx, int i_no) const {
J3DTexMtx* J3DMaterialFactory::newTexMtx(int i_idx, int i_no) const {
J3DTexMtx* tex_mtx = NULL;
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTexMtxIdx[i_no] != 0xffff) {
#if TARGET_LITTLE_ENDIAN
auto tex_mtx_info = mpTexMtxInfo[mtl_init_data->mTexMtxIdx[i_no]];
@@ -629,11 +558,7 @@ J3DTexMtx* J3DMaterialFactory::newTexMtx(int i_idx, int i_no) const {
u8 J3DMaterialFactory::newCullMode(int i_idx) const {
u32 r30 = 0;
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mCullModeIdx != 0xff) {
return mpCullMode[mtl_init_data->mCullModeIdx];
} else {
@@ -642,11 +567,7 @@ u8 J3DMaterialFactory::newCullMode(int i_idx) const {
}
u16 J3DMaterialFactory::newTexNo(int i_idx, int i_no) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTexNoIdx[i_no] != 0xffff) {
return mpTexNo[mtl_init_data->mTexNoIdx[i_no]];
} else {
@@ -655,11 +576,7 @@ u16 J3DMaterialFactory::newTexNo(int i_idx, int i_no) const {
}
J3DTevOrder J3DMaterialFactory::newTevOrder(int i_idx, int i_no) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTevOrderIdx[i_no] != 0xffff) {
return J3DTevOrder(mpTevOrderInfo[mtl_init_data->mTevOrderIdx[i_no]]);
} else {
@@ -670,11 +587,7 @@ J3DTevOrder J3DMaterialFactory::newTevOrder(int i_idx, int i_no) const {
J3DGXColorS10 J3DMaterialFactory::newTevColor(int i_idx, int i_no) const {
GXColorS10 _dflt = {0, 0, 0, 0};
J3DGXColorS10 dflt = _dflt;
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTevColorIdx[i_no] != 0xffff) {
return (GXColorS10) mpTevColor[mtl_init_data->mTevColorIdx[i_no]];
} else {
@@ -688,11 +601,7 @@ J3DGXColor J3DMaterialFactory::newTevKColor(int i_idx, int i_no) const {
#else
J3DGXColor dflt = GXColor{0xff, 0xff, 0xff, 0xff};
#endif
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTevKColorIdx[i_no] != 0xffff) {
return (GXColor) mpTevKColor[mtl_init_data->mTevKColorIdx[i_no]];
} else {
@@ -701,11 +610,7 @@ J3DGXColor J3DMaterialFactory::newTevKColor(int i_idx, int i_no) const {
}
const u8 J3DMaterialFactory::newTevStageNum(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTevStageNumIdx != 0xff) {
return mpTevStageNum[mtl_init_data->mTevStageNumIdx];
} else {
@@ -714,11 +619,7 @@ const u8 J3DMaterialFactory::newTevStageNum(int i_idx) const {
}
J3DTevStage J3DMaterialFactory::newTevStage(int i_idx, int i_no) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTevStageIdx[i_no] != 0xffff) {
return J3DTevStage(mpTevStageInfo[mtl_init_data->mTevStageIdx[i_no]]);
} else {
@@ -727,11 +628,7 @@ J3DTevStage J3DMaterialFactory::newTevStage(int i_idx, int i_no) const {
}
J3DTevSwapModeTable J3DMaterialFactory::newTevSwapModeTable(int i_idx, int i_no) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mTevSwapModeTableIdx[i_no] != 0xffff) {
return J3DTevSwapModeTable(mpTevSwapModeTableInfo[mtl_init_data->mTevSwapModeTableIdx[i_no]]);
} else {
@@ -792,11 +689,7 @@ J3DIndTexCoordScale J3DMaterialFactory::newIndTexCoordScale(int i_idx, int i_no)
J3DFog J3DMaterialFactory::newFog(int i_idx) const {
J3DFog fog;
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mFogIdx != 0xffff) {
#if TARGET_LITTLE_ENDIAN
J3DFogInfo fogInfo = mpFogInfo[mtl_init_data->mFogIdx];
@@ -816,11 +709,7 @@ J3DFog J3DMaterialFactory::newFog(int i_idx) const {
}
J3DAlphaComp J3DMaterialFactory::newAlphaComp(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mAlphaCompIdx != 0xffff) {
return J3DAlphaComp(mpAlphaCompInfo[mtl_init_data->mAlphaCompIdx]);
} else {
@@ -829,11 +718,7 @@ J3DAlphaComp J3DMaterialFactory::newAlphaComp(int i_idx) const {
}
J3DBlend J3DMaterialFactory::newBlend(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mBlendIdx != 0xffff) {
return J3DBlend(mpBlendInfo[mtl_init_data->mBlendIdx]);
} else {
@@ -843,11 +728,7 @@ J3DBlend J3DMaterialFactory::newBlend(int i_idx) const {
J3DZMode J3DMaterialFactory::newZMode(int i_idx) const {
u32 r29 = 0;
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mZModeIdx != 0xff) {
return J3DZMode(mpZModeInfo[mtl_init_data->mZModeIdx]);
} else {
@@ -856,11 +737,7 @@ J3DZMode J3DMaterialFactory::newZMode(int i_idx) const {
}
const u8 J3DMaterialFactory::newZCompLoc(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mZCompLocIdx != 0xff){
return mpZCompLoc[mtl_init_data->mZCompLocIdx];
} else {
@@ -869,11 +746,7 @@ const u8 J3DMaterialFactory::newZCompLoc(int i_idx) const {
}
const u8 J3DMaterialFactory::newDither(int i_idx) const {
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mDitherIdx != 0xff){
return mpDither[mtl_init_data->mDitherIdx];
} else {
@@ -881,24 +754,9 @@ const u8 J3DMaterialFactory::newDither(int i_idx) const {
}
}
#ifdef DUSK_TPHD
const PolygonOffset J3DMaterialFactory::newPolygonOffset(int i_idx) const {
J3DMaterialInitData_MAT4* mtl_init_data = (J3DMaterialInitData_MAT4*)getMatInitData(i_idx);
if (mBlockType >= 4 && mtl_init_data->mPolygonOffsetIdx != 0xffff){
return mpPolygonOffsets[mtl_init_data->mPolygonOffsetIdx];
} else {
return PolygonOffset{0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}
}
#endif
J3DNBTScale J3DMaterialFactory::newNBTScale(int i_idx) const {
J3DNBTScale dflt(j3dDefaultNBTScaleInfo);
#ifdef DUSK_TPHD
J3DMaterialInitData* mtl_init_data = getMatInitData(i_idx);
#else
J3DMaterialInitData* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]];
#endif
if (mtl_init_data->mNBTScaleIdx != 0xffff) {
return J3DNBTScale(mpNBTScaleInfo[mtl_init_data->mNBTScaleIdx]);
} else {
@@ -103,9 +103,6 @@ J3DModelData* J3DModelLoader::load(void const* i_data, u32 i_flags) {
readJoint((J3DJointBlock*)block);
break;
case 'MAT3':
#if DUSK_TPHD
case 'MAT4':
#endif
readMaterial((J3DMaterialBlock*)block, (s32)i_flags);
break;
case 'MAT2':
@@ -150,9 +147,6 @@ J3DMaterialTable* J3DModelLoader::loadMaterialTable(void const* i_data) {
for (u32 block_no = 0; block_no < data->mBlockNum; block_no++) {
switch (block->mBlockType) {
case 'MAT3':
#if DUSK_TPHD
case 'MAT4':
#endif
readMaterialTable((J3DMaterialBlock*)block, flags);
break;
case 'MAT2':
@@ -218,9 +212,6 @@ J3DModelData* J3DModelLoader::loadBinaryDisplayList(void const* i_data, u32 i_fl
modifyMaterial(i_flags);
break;
case 'MAT3':
#if DUSK_TPHD
case 'MAT4':
#endif
flags = 0x50100000;
flags |= (i_flags & 0x3000000);
mpMaterialBlock = (J3DMaterialBlock*)block;
@@ -317,22 +308,8 @@ static GXVtxAttrFmtList getFmt(GXVtxAttrFmtList* i_fmtList, GXAttr i_attr) {
return *i_fmtList;
}
}
#if DUSK_TPHD
// HD BMDs occasionally have vertex arrays without a format entry. Mirror
// the GC runtime: fall back to J3DSys::initGX defaults.
GXVtxAttrFmtList def{};
def.attr = i_attr;
def.frac = 0;
if (i_attr == GX_VA_POS) { def.cnt = GX_POS_XYZ; def.type = GX_F32; }
else if (i_attr == GX_VA_NRM) { def.cnt = GX_NRM_XYZ; def.type = GX_F32; }
else if (i_attr == GX_VA_NBT) { def.cnt = GX_NRM_NBT; def.type = GX_F32; }
else if (i_attr >= GX_VA_CLR0 &&
i_attr <= GX_VA_CLR1) { def.cnt = GX_CLR_RGBA; def.type = GX_RGBA8; }
else { def.cnt = GX_TEX_ST; def.type = GX_F32; }
return def;
#else
OSPanic(__FILE__, __LINE__, "Unable to find vertex attribute format!");
#endif
}
#endif
@@ -16,7 +16,7 @@ u16 J3DModelLoader::countMaterialNum(const void* stream) {
const J3DModelBlock* block = header->mBlocks;
for (int i = 0; i < header->mBlockNum; i++)
{
if (block->mBlockType == 'MAT3' || block->mBlockType == 'MAT4') {
if (block->mBlockType == 'MAT3') {
const J3DMaterialBlock* materialBlock = (const J3DMaterialBlock*)block;
return materialBlock->mMaterialNum;
}
@@ -45,7 +45,6 @@ u32 J3DModelLoader::calcLoadSize(void const* stream, u32 flags_) {
size += calcSizeJoint((const J3DJointBlock*)nextBlock);
break;
case 'MAT3':
case 'MAT4':
size += calcSizeMaterial((const J3DMaterialBlock*)nextBlock, flags);
break;
case 'SHP1':
@@ -87,7 +86,6 @@ u32 J3DModelLoader::calcLoadMaterialTableSize(const void* stream) {
for (u32 i = 0; i < header->mBlockNum; i++) {
switch (nextBlock->mBlockType) {
case 'MAT3':
case 'MAT4':
size += calcSizeMaterialTable((const J3DMaterialBlock*)nextBlock, flags);
break;
case 'TEX1':
@@ -137,8 +135,7 @@ u32 J3DModelLoader::calcLoadBinaryDisplayListSize(const void* stream, u32 flags)
case 'MDL3':
size += calcSizeMaterialDL((const J3DMaterialDLBlock*)nextBlock, flags);
break;
case 'MAT3':
case 'MAT4': {
case 'MAT3': {
u32 flags2 = (J3DMLF_21 | J3DMLF_Material_PE_Full | J3DMLF_Material_Color_LightOn);
flags2 |= (u32)flags & (J3DMLF_Material_UseIndirect | J3DMLF_26);
mpMaterialBlock = (const J3DMaterialBlock*)nextBlock;
+8 -1
View File
@@ -17,7 +17,7 @@ size_t JASResArcLoader::getResSize(JKRArchive const* i_archiveP, u16 i_resourceI
return 0;
}
return file->data_size;
return DUSK_IF_ELSE(i_archiveP->getFileSize(file), file->data_size);
}
size_t JASResArcLoader::getResMaxSize(JKRArchive const* i_archiveP) {
@@ -27,9 +27,16 @@ size_t JASResArcLoader::getResMaxSize(JKRArchive const* i_archiveP) {
for (index = 0; index < fileEntries; index++) {
JKRArchive::SDIFileEntry* file = i_archiveP->findIdxResource(index);
if (file) {
#if TARGET_PC
const u32 fileSize = i_archiveP->getFileSize(file);
if (maxSize < fileSize) {
maxSize = fileSize;
}
#else
if (maxSize < file->data_size) {
maxSize = file->data_size;
}
#endif
}
}
+7 -12
View File
@@ -38,7 +38,7 @@ void JFWDisplay::ctor_subroutine(bool enableAlpha) {
mTickRate = 0;
mCombinationRatio = 0.0f;
field_0x30 = 0;
field_0x2c = OSGetTick();
field_0x2c = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x34 = 0;
field_0x48 = 0;
field_0x4a = 0;
@@ -258,7 +258,7 @@ void JFWDisplay::beginRender() {
waitForTick(mTickRate, mFrameRate);
JUTVideo::getManager()->waitRetraceIfNeed();
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
field_0x30 = tick - field_0x2c;
field_0x2c = tick;
field_0x34 = field_0x2c - JUTVideo::getVideoLastTick();
@@ -371,7 +371,7 @@ constexpr auto FRAME_PERIOD = std::chrono::duration_cast<std::chrono::nanosecond
constexpr auto RETRACE_PERIOD = FRAME_PERIOD / 2;
static void waitPrecise(Limiter& limiter, Limiter::duration_t targetNs) {
const auto sleepTime = limiter.Sleep(targetNs);
const auto sleepTime = limiter.Sleep(targetNs);
dusk::frameUsagePct =
100.0f * (1.0f - static_cast<float>(sleepTime) / static_cast<float>(targetNs));
}
@@ -381,15 +381,12 @@ static void waitForTick(u32 p1, u16 p2) {
#if TARGET_PC
static Limiter limiter;
if (dusk::frame_interp::is_enabled() && !dusk::getTransientSettings().skipFrameRateLimit) {
dusk::frameUsagePct = 0.f;
return;
if (dusk::frame_interp::is_enabled() || dusk::getTransientSettings().turboMode) {
limiter.Reset();
dusk::frameUsagePct = 0.f;
return;
}
if (dusk::getTransientSettings().skipFrameRateLimit) {
p1 = OS_TIMER_CLOCK / 120;
}
if (fopOvlpM_IsPeek() && dusk::getTransientSettings().stateShareLoadActive) {
return;
}
@@ -399,7 +396,6 @@ static void waitForTick(u32 p1, u16 p2) {
if (p1 != 0) {
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>(OSTicksToMicroseconds(p1)) * 1000ULL);
#else
static OSTime nextTick = OSGetTime();
@@ -413,7 +409,6 @@ static void waitForTick(u32 p1, u16 p2) {
} else {
u32 uVar1 = (p2 == 0) ? 1 : p2;
#if TARGET_PC
static Limiter limiter;
waitPrecise(limiter, static_cast<Uint64>((RETRACE_PERIOD * uVar1).count()));
#else
static u32 nextCount = VIGetRetraceCount();
+20 -12
View File
@@ -11,11 +11,6 @@
#include <cstring>
#include "os_report.h"
#if DUSK_TPHD
#include "dusk/settings.h"
#include "dusk/tphd/HdAssetLayer.hpp"
#endif
JKRAramArchive::JKRAramArchive() {}
JKRAramArchive::JKRAramArchive(s32 entryNumber, JKRArchive::EMountDirection mountDirection)
@@ -201,6 +196,13 @@ cleanup:
void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) {
JUT_ASSERT(442, isMounted());
#if TARGET_PC
if (void* data = getOverlayData(pEntry, pOutSize); data != nullptr) {
return data;
}
#endif
u32 outSize;
u8* outBuf;
if (pOutSize == NULL) {
@@ -222,13 +224,6 @@ void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) {
if (compression == COMPRESSION_YAZ0) {
this->setExpandSize(pEntry, *pOutSize);
}
#if DUSK_TPHD
// ARAM mounts bypass register_mounted_hd_archive; hook the fresh per-file buffer here.
if (dusk::tphd_active() && mStringTable != NULL) {
dusk::tphd::register_copied_hd_resource(
mEntryNum, mStringTable + pEntry->getNameOffset(), outBuf, *pOutSize);
}
#endif
} else {
if (compression == COMPRESSION_YAZ0) {
*pOutSize = this->getExpandSize(pEntry);
@@ -243,6 +238,13 @@ void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) {
void* JKRAramArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* pEntry,
u32* resourceSize) {
JUT_ASSERT(515, isMounted());
#if TARGET_PC
if (copyOverlayData(buffer, bufferSize, pEntry, resourceSize)) {
return buffer;
}
#endif
u32 size = pEntry->data_size;
if (size > bufferSize) {
size = bufferSize;
@@ -349,6 +351,12 @@ u32 JKRAramArchive::getExpandedResSize(const void* ptr) const {
return this->getResSize(ptr);
}
#if TARGET_PC
if (u32 size; getOverlayResourceSize(ptr, &size)) {
return size;
}
#endif
JKRArchive::SDIFileEntry* entry = this->findPtrResource(ptr);
if (entry == NULL) {
return 0xFFFFFFFF;
+268
View File
@@ -7,6 +7,37 @@
#if TARGET_PC
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <limits>
#include <ranges>
#include <string_view>
#include "JSystem/JKernel/JKRDvdRipper.h"
#if _WIN32
#include <malloc.h>
#endif
std::atomic<u64> JKRArchive::sArcOverlayGeneration{0};
namespace {
void* alloc_overlay_buffer(u32 size) {
#if _WIN32
return _aligned_malloc(size, alignof(std::max_align_t));
#else
return std::malloc(size);
#endif
}
void free_overlay_buffer(void* data) {
#if _WIN32
_aligned_free(data);
#else
std::free(data);
#endif
}
} // namespace
#endif
DUSK_GAME_DATA u32 JKRArchive::sCurrentDirID;
@@ -42,6 +73,7 @@ JKRArchive::JKRArchive(s32 entryNumber, JKRArchive::EMountMode mountMode) {
JKRArchive::~JKRArchive() {
#if TARGET_PC
removeAllOverlayResources();
if (mFileData != nullptr) {
JKRHeap::getSystemHeap()->free(mFileData);
mFileData = nullptr;
@@ -270,4 +302,240 @@ void JKRArchive::initFileDataPointers() {
mFiles[i].index = i;
}
}
void JKRArchive::notifyOverlayFilesChanged() {
sArcOverlayGeneration.fetch_add(1, std::memory_order_release);
}
bool JKRArchive::buildArcOverlaysPath() const {
if (mArcOverlaysPathResolved) {
return !mArcOverlaysPath.empty();
}
mArcOverlaysPathResolved = true;
if (mEntryNum < 0) {
return false;
}
char pathBuffer[1024];
if (!DVDConvertEntrynumToPath(mEntryNum, pathBuffer, sizeof(pathBuffer))) {
return false;
}
std::string path{pathBuffer};
constexpr std::string_view extension{".arc"};
if (path.size() < extension.size() ||
path.compare(path.size() - extension.size(), extension.size(), extension) != 0)
{
return false;
}
path.resize(path.size() - extension.size());
path.push_back('/');
mArcOverlaysPath = std::move(path);
return true;
}
void JKRArchive::buildIndexToPathMap(u32 dirIndex, const std::string& currentPath) const {
const SDIDirEntry& dir = mNodes[dirIndex];
for (int i = 0; i < dir.num_entries; i++) {
const SDIFileEntry& entry = mFiles[dir.first_file_index + i];
std::string entryName{&mStringTable[entry.getNameOffset()]};
if (entryName == "." || entryName == "..") {
continue;
}
if (entry.isDirectory()) {
buildIndexToPathMap(entry.data_offset, currentPath + entryName + "/");
} else {
mIdxToPathMap[entry.index] = currentPath + entryName;
}
}
}
bool JKRArchive::getOverlayPath(SDIFileEntry* entry, std::string& path) const {
if (entry == nullptr || !buildArcOverlaysPath()) {
return false;
}
if (mIdxToPathMap.empty()) {
buildIndexToPathMap(0, std::string{&mStringTable[mNodes[0].name_offset]} + "/");
}
const auto pathIt = mIdxToPathMap.find(entry->index);
if (pathIt == mIdxToPathMap.end()) {
return false;
}
path = mArcOverlaysPath + pathIt->second;
return true;
}
void* JKRArchive::getActiveOverlayData(SDIFileEntry* entry, u32* outSize) const {
const auto activeIt = mActiveArcOverlayResources.find(entry->index);
if (activeIt == mActiveArcOverlayResources.end()) {
return nullptr;
}
const auto resourceIt = mArcOverlayResources.find(activeIt->second);
const u64 generation = sArcOverlayGeneration.load(std::memory_order_acquire);
if (resourceIt == mArcOverlayResources.end() || resourceIt->second.generation != generation) {
// Keep the allocation owned so raw pointers returned by earlier fetches remain valid.
mActiveArcOverlayResources.erase(activeIt);
return nullptr;
}
if (outSize != nullptr) {
*outSize = resourceIt->second.size;
}
return resourceIt->first;
}
void* JKRArchive::getOverlayData(SDIFileEntry* entry, u32* outSize) {
if (entry == nullptr) {
return nullptr;
}
if (void* data = getActiveOverlayData(entry, outSize)) {
return data;
}
std::string path;
if (!getOverlayPath(entry, path)) {
return nullptr;
}
constexpr u32 alignmentMask = 0x1f;
const u64 generation = sArcOverlayGeneration.load(std::memory_order_acquire);
DVDFileInfo fileInfo{};
if (!DVDOpen(path.c_str(), &fileInfo)) {
return nullptr;
}
const u32 logicalSize = fileInfo.length;
if (logicalSize > static_cast<u32>(std::numeric_limits<s32>::max()) - alignmentMask) {
DVDClose(&fileInfo);
return nullptr;
}
const u32 readSize = ALIGN_NEXT(logicalSize, 0x20);
const u32 allocationSize = readSize == 0 ? 1 : readSize;
void* data = alloc_overlay_buffer(allocationSize);
if (data == nullptr) {
DVDClose(&fileInfo);
return nullptr;
}
const s32 status = DVDReadPrio(&fileInfo, data, readSize, 0, 2);
DVDClose(&fileInfo);
if (status < DVD_RESULT_GOOD || static_cast<u32>(status) != logicalSize) {
free_overlay_buffer(data);
return nullptr;
}
mArcOverlayResources.emplace(data, ArcOverlayResource{
.entryIndex = entry->index,
.size = logicalSize,
.generation = generation,
});
mActiveArcOverlayResources[entry->index] = data;
if (outSize != nullptr) {
*outSize = logicalSize;
}
return data;
}
bool JKRArchive::copyOverlayData(void* buffer, u32 bufferSize, SDIFileEntry* entry, u32* outSize) {
u32 overlaySize;
const void* overlayData = getOverlayData(entry, &overlaySize);
if (overlayData == nullptr) {
return false;
}
const u32 copySize = overlaySize < bufferSize ? overlaySize : bufferSize;
if (copySize != 0) {
memcpy(buffer, overlayData, copySize);
}
if (outSize != nullptr) {
*outSize = copySize;
}
return true;
}
bool JKRArchive::getOverlayResourceSize(const void* data, u32* outSize) const {
const auto resourceIt = mArcOverlayResources.find(const_cast<void*>(data));
if (resourceIt == mArcOverlayResources.end()) {
return false;
}
if (outSize != nullptr) {
*outSize = resourceIt->second.size;
}
return true;
}
bool JKRArchive::getOverlayFileSize(SDIFileEntry* entry, u32* outSize) const {
if (entry == nullptr) {
return false;
}
u32 activeSize;
if (getActiveOverlayData(entry, &activeSize) != nullptr) {
if (outSize != nullptr) {
*outSize = activeSize;
}
return true;
}
std::string path;
if (!getOverlayPath(entry, path)) {
return false;
}
DVDFileInfo fileInfo{};
if (!DVDOpen(path.c_str(), &fileInfo)) {
return false;
}
if (outSize != nullptr) {
*outSize = fileInfo.length;
}
DVDClose(&fileInfo);
return true;
}
u32 JKRArchive::getFileSize(SDIFileEntry* entry) const {
u32 size;
if (getOverlayFileSize(entry, &size)) {
return size;
}
return entry != nullptr ? entry->getSize() : 0;
}
bool JKRArchive::removeOverlayResource(void* resource, bool freeResource) {
const auto resourceIt = mArcOverlayResources.find(resource);
if (resourceIt == mArcOverlayResources.end()) {
return false;
}
const auto activeIt = mActiveArcOverlayResources.find(resourceIt->second.entryIndex);
if (activeIt != mActiveArcOverlayResources.end() && activeIt->second == resource) {
mActiveArcOverlayResources.erase(activeIt);
}
if (freeResource) {
free_overlay_buffer(resource);
}
mArcOverlayResources.erase(resourceIt);
return true;
}
void JKRArchive::removeAllOverlayResources() {
mActiveArcOverlayResources.clear();
for (const auto& key : mArcOverlayResources | std::views::keys) {
free_overlay_buffer(key);
}
mArcOverlayResources.clear();
}
#endif
+22 -30
View File
@@ -9,24 +9,6 @@
#include "JSystem/JKernel/JKRMemArchive.h"
#include "JSystem/JUtility/JUTAssert.h"
#if DUSK_TPHD
#include "dusk/tphd/HdAssetLayer.hpp"
namespace {
void register_copied_hd_resource(
JKRArchive* archive, JKRArchive::SDIFileEntry* fileEntry, void* buffer, u32 resourceSize) {
if (archive == NULL || fileEntry == NULL || buffer == NULL || resourceSize == 0 ||
archive->mStringTable == NULL)
{
return;
}
dusk::tphd::register_copied_hd_resource(archive->mEntryNum,
archive->mStringTable + fileEntry->getNameOffset(), buffer, resourceSize);
}
} // namespace
#endif
JKRArchive* JKRArchive::check_mount_already(s32 entryNum, JKRHeap* heap) {
if (heap == NULL) {
heap = JKRGetCurrentHeap();
@@ -214,9 +196,6 @@ u32 JKRArchive::readResource(void* buffer, u32 bufferSize, u32 type, const char*
if (fileEntry) {
u32 resourceSize;
fetchResource(buffer, bufferSize, fileEntry, &resourceSize);
#if DUSK_TPHD
register_copied_hd_resource(this, fileEntry, buffer, resourceSize);
#endif
return resourceSize;
}
@@ -235,9 +214,6 @@ u32 JKRArchive::readResource(void* buffer, u32 bufferSize, const char* path) {
if (fileEntry) {
u32 resourceSize;
fetchResource(buffer, bufferSize, fileEntry, &resourceSize);
#if DUSK_TPHD
register_copied_hd_resource(this, fileEntry, buffer, resourceSize);
#endif
return resourceSize;
}
@@ -250,9 +226,6 @@ u32 JKRArchive::readIdxResource(void* buffer, u32 bufferSize, u32 index) {
if (fileEntry) {
u32 resourceSize;
fetchResource(buffer, bufferSize, fileEntry, &resourceSize);
#if DUSK_TPHD
register_copied_hd_resource(this, fileEntry, buffer, resourceSize);
#endif
return resourceSize;
}
@@ -265,9 +238,6 @@ u32 JKRArchive::readResource(void* buffer, u32 bufferSize, u16 id) {
if (fileEntry) {
u32 resourceSize;
fetchResource(buffer, bufferSize, fileEntry, &resourceSize);
#if DUSK_TPHD
register_copied_hd_resource(this, fileEntry, buffer, resourceSize);
#endif
return resourceSize;
}
@@ -276,6 +246,7 @@ u32 JKRArchive::readResource(void* buffer, u32 bufferSize, u16 id) {
void JKRArchive::removeResourceAll() {
if (mArcInfoBlock && mMountMode != MOUNT_MEM) {
IF_DUSK(removeAllOverlayResources();)
SDIFileEntry* fileEntry = mFiles;
for (int i = 0; i < mArcInfoBlock->num_file_entries; i++) {
if (JKAR_DATA(fileEntry)) {
@@ -289,6 +260,13 @@ void JKRArchive::removeResourceAll() {
bool JKRArchive::removeResource(void* resource) {
JUT_ASSERT(678, resource != NULL);
#if TARGET_PC
if (removeOverlayResource(resource, true)) {
return true;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (fileEntry == NULL)
return false;
@@ -300,6 +278,13 @@ bool JKRArchive::removeResource(void* resource) {
bool JKRArchive::detachResource(void* resource) {
JUT_ASSERT(707, resource != NULL);
#if TARGET_PC
if (removeOverlayResource(resource, false)) {
return true;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (fileEntry == NULL)
return false;
@@ -310,6 +295,13 @@ bool JKRArchive::detachResource(void* resource) {
u32 JKRArchive::getResSize(const void* resource) const {
JUT_ASSERT(732, resource != NULL);
#if TARGET_PC
if (u32 size; getOverlayResourceSize(resource, &size)) {
return size;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (fileEntry == NULL)
return -1;
@@ -226,6 +226,13 @@ bool JKRCompArchive::open(s32 entryNum) {
void* JKRCompArchive::fetchResource(SDIFileEntry *fileEntry, u32 *pSize) {
JUT_ASSERT(597, isMounted());
#if TARGET_PC
if (void* data = getOverlayData(fileEntry, pSize); data != nullptr) {
return data;
}
#endif
u32 ptrSize;
u32 size = fileEntry->data_size;
int compression = JKRConvertAttrToCompressionType(u8(fileEntry->type_flags_and_name_offset >> 0x18));
@@ -274,6 +281,13 @@ void *JKRCompArchive::fetchResource(void *data, u32 compressedSize, SDIFileEntry
{
u32 size = 0;
JUT_ASSERT(708, isMounted());
#if TARGET_PC
if (copyOverlayData(data, compressedSize, fileEntry, pSize)) {
return data;
}
#endif
u32 fileSize = fileEntry->data_size;
u32 alignedSize = ALIGN_NEXT(fileSize, 32);
u32 fileFlag = fileEntry->type_flags_and_name_offset >> 0x18;
@@ -319,6 +333,7 @@ void *JKRCompArchive::fetchResource(void *data, u32 compressedSize, SDIFileEntry
void JKRCompArchive::removeResourceAll() {
if (mArcInfoBlock != NULL && mMountMode != MOUNT_MEM) {
IF_DUSK(removeAllOverlayResources();)
SDIFileEntry* fileEntry = mFiles;
for (int i = 0; i < mArcInfoBlock->num_file_entries; i++) {
int tmp = fileEntry->type_flags_and_name_offset >> 0x18;
@@ -336,6 +351,12 @@ void JKRCompArchive::removeResourceAll() {
}
bool JKRCompArchive::removeResource(void* resource) {
#if TARGET_PC
if (removeOverlayResource(resource, true)) {
return true;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (!fileEntry)
return false;
@@ -356,6 +377,12 @@ u32 JKRCompArchive::getExpandedResSize(const void *resource) const
return getResSize(resource);
}
#if TARGET_PC
if (u32 size; getOverlayResourceSize(resource, &size)) {
return size;
}
#endif
SDIFileEntry *fileEntry = findPtrResource(resource);
if(!fileEntry) {
return 0xffffffff;
@@ -147,6 +147,13 @@ cleanup:
void* JKRDvdArchive::fetchResource(SDIFileEntry* fileEntry, u32* returnSize) {
JUT_ASSERT(428, isMounted());
#if TARGET_PC
if (void* data = getOverlayData(fileEntry, returnSize); data != nullptr) {
return data;
}
#endif
u32 tempReturnSize;
if (returnSize == NULL) {
returnSize = &tempReturnSize;
@@ -181,6 +188,13 @@ void* JKRDvdArchive::fetchResource(SDIFileEntry* fileEntry, u32* returnSize) {
void* JKRDvdArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* fileEntry,
u32* returnSize) {
JUT_ASSERT(504, isMounted());
#if TARGET_PC
if (copyOverlayData(buffer, bufferSize, fileEntry, returnSize)) {
return buffer;
}
#endif
u32 size = fileEntry->data_size;
JKRCompression fileCompression = JKRConvertAttrToCompressionType(u8(fileEntry->type_flags_and_name_offset >> 24));
@@ -344,6 +358,12 @@ u32 JKRDvdArchive::getExpandedResSize(const void* resource) const {
return getResSize(resource);
}
#if TARGET_PC
if (u32 size; getOverlayResourceSize(resource, &size)) {
return size;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (!fileEntry) {
return -1;
+28 -14
View File
@@ -10,10 +10,6 @@
#include <stdint.h>
#include "os_report.h"
#if DUSK_TPHD
#include "dusk/tphd/HdAssetLayer.hpp"
#endif
JKRMemArchive::JKRMemArchive(s32 entryNum, JKRArchive::EMountDirection mountDirection)
: JKRArchive(entryNum, MOUNT_MEM) {
mIsMounted = false;
@@ -71,13 +67,8 @@ bool JKRMemArchive::open(s32 entryNum, JKRArchive::EMountDirection mountDirectio
mIsOpen = false;
mMountDirection = mountDirection;
#ifdef TARGET_PC
u32 loadedSize = 0;
#endif
if (mMountDirection == JKRArchive::MOUNT_DIRECTION_HEAD) {
#ifndef TARGET_PC
u32 loadedSize;
#endif
mArcHeader = (SArcHeader *)JKRDvdToMainRam(
entryNum, NULL, EXPAND_SWITCH_UNKNOWN1, 0, mHeap, JKRDvdRipper::ALLOC_DIRECTION_FORWARD,
0, (int *)&mCompression, &loadedSize);
@@ -86,9 +77,7 @@ bool JKRMemArchive::open(s32 entryNum, JKRArchive::EMountDirection mountDirectio
}
}
else {
#ifndef TARGET_PC
u32 loadedSize;
#endif
mArcHeader = (SArcHeader *)JKRDvdToMainRam(
entryNum, NULL, EXPAND_SWITCH_UNKNOWN1, 0, mHeap,
JKRDvdRipper::ALLOC_DIRECTION_BACKWARD, 0, (int *)&mCompression, &loadedSize);
@@ -101,9 +90,6 @@ bool JKRMemArchive::open(s32 entryNum, JKRArchive::EMountDirection mountDirectio
mMountMode = UNKNOWN_MOUNT_MODE;
}
else {
#if DUSK_TPHD
dusk::tphd::register_mounted_hd_archive(entryNum, mArcHeader, loadedSize);
#endif
JUT_ASSERT(438, mArcHeader->signature == 'RARC');
mArcInfoBlock = (SArcDataInfo *)((u8 *)mArcHeader + mArcHeader->header_length);
mNodes = (SDIDirEntry *)((u8 *)&mArcInfoBlock->num_nodes + mArcInfoBlock->node_offset);
@@ -149,6 +135,13 @@ bool JKRMemArchive::open(void* buffer, u32 bufferSize, JKRMemBreakFlag flag) {
void* JKRMemArchive::fetchResource(SDIFileEntry* fileEntry, u32* resourceSize) {
JUT_ASSERT(555, isMounted());
#if TARGET_PC
if (void* data = getOverlayData(fileEntry, resourceSize); data != nullptr) {
return data;
}
#endif
if (!JKAR_DATA(fileEntry)) {
JKAR_DATA(fileEntry) = mArchiveData + fileEntry->data_offset;
}
@@ -163,6 +156,13 @@ void* JKRMemArchive::fetchResource(SDIFileEntry* fileEntry, u32* resourceSize) {
void* JKRMemArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* fileEntry,
u32* resourceSize) {
JUT_ASSERT(595, isMounted());
#if TARGET_PC
if (copyOverlayData(buffer, bufferSize, fileEntry, resourceSize)) {
return buffer;
}
#endif
u32 srcLength = fileEntry->data_size;
if (srcLength > bufferSize) {
srcLength = bufferSize;
@@ -187,6 +187,8 @@ void* JKRMemArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* f
void JKRMemArchive::removeResourceAll(void) {
JUT_ASSERT(642, isMounted());
IF_DUSK(removeAllOverlayResources();)
if (mArcInfoBlock == NULL)
return;
if (mMountMode == MOUNT_MEM)
@@ -206,6 +208,12 @@ void JKRMemArchive::removeResourceAll(void) {
bool JKRMemArchive::removeResource(void* resource) {
JUT_ASSERT(673, isMounted());
#if TARGET_PC
if (removeOverlayResource(resource, true)) {
return true;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (!fileEntry)
return false;
@@ -245,6 +253,12 @@ u32 JKRMemArchive::fetchResource_subroutine(u8* src, u32 srcLength, u8* dst, u32
}
u32 JKRMemArchive::getExpandedResSize(const void* resource) const {
#if TARGET_PC
if (u32 overlaySize; getOverlayResourceSize(resource, &overlaySize)) {
return overlaySize;
}
#endif
SDIFileEntry* fileEntry = findPtrResource(resource);
if (fileEntry == NULL)
return -1;
+53 -3
View File
@@ -7,6 +7,10 @@
#include "JSystem/JMessage/control.h"
#if TARGET_PC
#include "dusk/mods/svc/flow.hpp"
#endif
JMessage::TControl::TControl()
: pSequenceProcessor_(NULL),
pRenderingProcessor_(NULL),
@@ -19,9 +23,16 @@ JMessage::TControl::TControl()
pMessageText_current_(NULL)
{}
JMessage::TControl::~TControl() {}
JMessage::TControl::~TControl() {
#if TARGET_PC
dusk::flow::release_message_control(this);
#endif
}
void JMessage::TControl::reset() {
#if TARGET_PC
dusk::flow::release_message_control(this);
#endif
pEntry_ = NULL;
pMessageText_begin_ = NULL;
pszText_update_current_ = NULL;
@@ -78,18 +89,57 @@ int JMessage::TControl::setMessageID(u32 uMsgID, u32 param_1, bool* pbValid) {
}
bool JMessage::TControl::setMessageCode_inSequence_(JMessage::TProcessor const* pProcessor, u16 u16GroupID, u16 u16Index) {
#if TARGET_PC
const TResource* resource = NULL;
const void* resolvedEntry = NULL;
const char* resolvedText = NULL;
if (u16Index >= dusk::flow::kCustomMessageMin) {
if (!dusk::flow::custom_message_for_processor(
this, pProcessor, u16Index, resource, resolvedEntry, resolvedText))
{
return false;
}
const_cast<TProcessor*>(pProcessor)->setResourceCache(const_cast<TResource*>(resource));
} else {
resource = pProcessor->getResource_groupID(u16GroupID);
if (resource == NULL) {
return false;
}
const void* nativeEntry = resource->getMessageEntry_messageIndex(u16Index);
const char* nativeText =
nativeEntry != NULL ? resource->getMessageText_messageEntry(nativeEntry) : NULL;
resolvedEntry = nativeEntry;
resolvedText = nativeText;
dusk::flow::resolve_message_for_control(this, resource->oParse_THeader_.getRaw(), u16Index,
nativeEntry, nativeText, resolvedEntry, resolvedText);
}
pEntry_ = const_cast<void*>(resolvedEntry);
if (pEntry_ == NULL || resolvedText == NULL) {
return false;
}
#else
pEntry_ = pProcessor->getMessageEntry_messageCode(u16GroupID, u16Index);
if (pEntry_ == NULL) {
return false;
}
#endif
#if TARGET_PC
uMessageGroupID_ = resource->getGroupID();
pResourceCache_ = resource;
#else
uMessageGroupID_ = u16GroupID;
uMessageID_ = u16Index;
pResourceCache_ = pProcessor->getResourceCache();
#endif
uMessageID_ = u16Index;
JUT_ASSERT(155, pResourceCache_!=NULL);
#if TARGET_PC
pMessageText_begin_ = resolvedText;
#else
pMessageText_begin_ = pResourceCache_->getMessageText_messageEntry(pEntry_);
#endif
pMessageText_current_ = pMessageText_begin_;
oStack_renderingProcessor_.clear();
return true;
+89 -1
View File
@@ -5,15 +5,26 @@
#include "JSystem/JUtility/JUTAssert.h"
#include <cstdint>
#if TARGET_PC
#include "dusk/mods/svc/flow.hpp"
#endif
JMessage::TReference::~TReference() {}
const char* JMessage::TReference::do_word(u32 param_0) const {
return NULL;
}
JMessage::TProcessor::~TProcessor() {}
JMessage::TProcessor::~TProcessor() {
#if TARGET_PC
dusk::flow::release_message_processor(this);
#endif
}
void JMessage::TProcessor::reset() {
#if TARGET_PC
dusk::flow::release_message_processor(this);
#endif
on_resetStatus_(NULL);
do_reset();
}
@@ -44,6 +55,15 @@ const JMessage::TResource* JMessage::TProcessor::getResource_groupID(u16 u16Grou
}
u32 JMessage::TProcessor::toMessageCode_messageID(u32 uMsgID, u32 param_1, bool* pbValid) const {
#if TARGET_PC
u32 customCode = 0;
if (dusk::flow::message_code_for_id(this, uMsgID, customCode)) {
if (pbValid != NULL) {
*pbValid = true;
}
return customCode;
}
#endif
const TResource* pResourceCache = (const TResource*)getResourceCache();
if (pResourceCache != NULL) {
u16 u16Index = pResourceCache->toMessageIndex_messageID(uMsgID, param_1, pbValid);
@@ -73,6 +93,74 @@ u32 JMessage::TProcessor::toMessageCode_messageID(u32 uMsgID, u32 param_1, bool*
return 0xFFFFFFFF;
}
#if TARGET_PC
void* JMessage::TProcessor::getMessageEntry_messageCode(u16 groupId, u16 index) const {
if (index >= dusk::flow::kCustomMessageMin) {
const TResource* customResource = NULL;
const void* customEntry = NULL;
const char* customText = NULL;
if (!dusk::flow::custom_message_for_processor(
NULL, this, index, customResource, customEntry, customText))
{
return NULL;
}
const_cast<TProcessor*>(this)->setResourceCache(const_cast<TResource*>(customResource));
return const_cast<void*>(customEntry);
}
const TResource* resource = getResource_groupID(groupId);
if (resource == NULL) {
return NULL;
}
void* nativeEntry = resource->getMessageEntry_messageIndex(index);
const char* nativeText =
nativeEntry != NULL ? resource->getMessageText_messageEntry(nativeEntry) : NULL;
const void* resolvedEntry = nativeEntry;
const char* resolvedText = nativeText;
dusk::flow::resolve_message(this, resource->oParse_THeader_.getRaw(), index, nativeEntry,
nativeText, resolvedEntry, resolvedText);
return const_cast<void*>(resolvedEntry);
}
const char* JMessage::TProcessor::getMessageText_messageCode(u16 groupId, u16 index) const {
if (index >= dusk::flow::kCustomMessageMin) {
const TResource* customResource = NULL;
const void* customEntry = NULL;
const char* customText = NULL;
if (!dusk::flow::custom_message_for_processor(
NULL, this, index, customResource, customEntry, customText))
{
return NULL;
}
const_cast<TProcessor*>(this)->setResourceCache(const_cast<TResource*>(customResource));
return customText;
}
const TResource* resource = getResource_groupID(groupId);
if (resource == NULL) {
return NULL;
}
const void* nativeEntry = resource->getMessageEntry_messageIndex(index);
const char* nativeText =
nativeEntry != NULL ? resource->getMessageText_messageEntry(nativeEntry) : NULL;
const void* resolvedEntry = nativeEntry;
const char* resolvedText = nativeText;
dusk::flow::resolve_message(this, resource->oParse_THeader_.getRaw(), index, nativeEntry,
nativeText, resolvedEntry, resolvedText);
return resolvedText;
}
const char* JMessage::TProcessor::on_message_limited(u16 index) const {
JUT_ASSERT(482, pResourceCache_!=NULL);
const void* nativeEntry = pResourceCache_->getMessageEntry_messageIndex(index);
const char* nativeText =
nativeEntry != NULL ? pResourceCache_->getMessageText_messageEntry(nativeEntry) : NULL;
const void* resolvedEntry = nativeEntry;
const char* resolvedText = nativeText;
dusk::flow::resolve_message(this, pResourceCache_->oParse_THeader_.getRaw(), index,
nativeEntry, nativeText, resolvedEntry, resolvedText);
return resolvedText;
}
#endif
void JMessage::TProcessor::on_select_begin(char const* (*pfn)(JMessage::TProcessor*),
void const* pOffset, char const* pcBase, u32 uNumber) {
JUT_ASSERT(191, uNumber>0);
+1 -1
View File
@@ -71,7 +71,7 @@ void JUTResFont::initJoinedTexture() {
int pageCount = 0;
u32 pageNumCells = block.numRows * block.numColumns;
if (dusk::version::getGameVersion() == dusk::version::GameVersion::GcnJpn) {
if (dusk::version::isRegionJpn()) {
pageCount = 1;
if (pageNumCells > 0 && block.endCode > block.startCode) {
pageCount = (block.endCode - block.startCode + pageNumCells - 1) / pageNumCells;
+7 -2
View File
@@ -42,7 +42,7 @@ JUTVideo::JUTVideo(GXRenderModeObj const* param_0) {
mRetraceCount = VIGetRetraceCount();
field_0x10 = 1;
field_0x18 = 0;
sVideoLastTick = OSGetTick();
sVideoLastTick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = 670000;
mPreRetraceCallback = VISetPreRetraceCallback(preRetraceProc);
mPostRetraceCallback = VISetPostRetraceCallback(postRetraceProc);
@@ -68,8 +68,13 @@ void JUTVideo::preRetraceProc(u32 retrace_count) {
(*sManager->mPreCallback)(retrace_count);
}
OSTick tick = OSGetTick();
OSTick tick = DUSK_IF_ELSE(static_cast<OSTick>(OSGetNativeTime()), OSGetTick());
sVideoInterval = tick - sVideoLastTick;
#if TARGET_PC
if (sVideoInterval <= 0) {
sVideoInterval = 1;
}
#endif
sVideoLastTick = tick;
JUTXfb* xfb = JUTXfb::getManager();
+12 -10
View File
@@ -53,6 +53,7 @@ ResourceBuffer g_denoiseSource = RESOURCE_BUFFER_INIT;
ResourceBuffer g_compositeSource = RESOURCE_BUFFER_INIT;
GfxDeviceInfo g_deviceInfo = GFX_DEVICE_INFO_INIT;
GfxRenderTargetLayout g_sceneTargetLayout = GFX_RENDER_TARGET_LAYOUT_INIT;
WGPUComputePipeline g_preprocessPipeline = nullptr;
WGPUComputePipeline g_mip4Pipeline = nullptr;
WGPUComputePipeline g_gtaoPipeline = nullptr;
@@ -226,19 +227,17 @@ bool build_composite_pipeline(
.dstFactor = WGPUBlendFactor_One,
},
};
WGPUColorTargetState colorTarget = WGPU_COLOR_TARGET_STATE_INIT;
colorTarget.format = g_deviceInfo.color_format;
if (blend) {
colorTarget.blend = &blendState;
}
WGPUColorTargetState colorTargets[GFX_MAX_COLOR_ATTACHMENTS];
const uint32_t colorTargetCount = gfx_init_color_target_states(
&g_sceneTargetLayout, colorTargets, blend ? &blendState : nullptr, WGPUColorWriteMask_All);
WGPUFragmentState fragment = WGPU_FRAGMENT_STATE_INIT;
fragment.module = module;
fragment.entryPoint = {"fs_main", WGPU_STRLEN};
fragment.targetCount = 1;
fragment.targets = &colorTarget;
fragment.targetCount = colorTargetCount;
fragment.targets = colorTargets;
// Depth state must match the EFB pass despite never touching depth.
WGPUDepthStencilState depthStencil = WGPU_DEPTH_STENCIL_STATE_INIT;
depthStencil.format = g_deviceInfo.depth_format;
depthStencil.format = g_sceneTargetLayout.depth_stencil_format;
depthStencil.depthWriteEnabled = WGPUOptionalBool_False;
depthStencil.depthCompare = WGPUCompareFunction_Always;
@@ -248,7 +247,7 @@ bool build_composite_pipeline(
pipelineDesc.vertex.entryPoint = {"vs_main", WGPU_STRLEN};
pipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
pipelineDesc.depthStencil = &depthStencil;
pipelineDesc.multisample.count = g_deviceInfo.sample_count;
pipelineDesc.multisample.count = g_sceneTargetLayout.sample_count;
pipelineDesc.fragment = &fragment;
outPipeline = wgpuDeviceCreateRenderPipeline(g_deviceInfo.device, &pipelineDesc);
wgpuShaderModuleRelease(module);
@@ -504,7 +503,7 @@ void on_compute(
// Render worker thread: composite the AO over the scene (or show it, in debug view).
void on_draw(
ModContext*, const GfxDrawContext* ctx, const void* payload, size_t payloadSize, void*) {
if (payloadSize != sizeof(CompositePayload)) {
if (payloadSize != sizeof(CompositePayload) || ctx->layout.key != g_sceneTargetLayout.key) {
return;
}
CompositePayload data;
@@ -816,6 +815,9 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) {
if (svc_gfx->get_device_info(mod_ctx, &g_deviceInfo) != MOD_OK) {
return mods::set_error(error, MOD_ERROR, "failed to query device info");
}
if (svc_gfx->get_scene_target_layout(mod_ctx, &g_sceneTargetLayout) != MOD_OK) {
return mods::set_error(error, MOD_ERROR, "failed to query scene target layout");
}
if (!build_compute_pipeline("AO preprocess depth", g_preprocessSource, "preprocess_depth",
g_preprocessPipeline, g_preprocessLayout) ||
!build_compute_pipeline("AO downsample mip4", g_preprocessSource, "downsample_mip4",
+35
View File
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.25)
project(basic_cosmetics_mod CXX)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root")
option(DUSK_MOD_USE_FULL_TREE "Use full build instead of the minimal mod SDK" OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (DUSK_MOD_USE_FULL_TREE)
add_subdirectory("${DUSK_DIR}" dusk EXCLUDE_FROM_ALL)
else ()
add_subdirectory("${DUSK_DIR}/sdk" dusk-sdk EXCLUDE_FROM_ALL)
endif ()
endif ()
include(FetchContent)
message(STATUS "basic_cosmetics_mod: Fetching xxhash")
FetchContent_Declare(
xxhash
GIT_REPOSITORY https://github.com/Cyan4973/xxHash.git
GIT_TAG v0.8.3
)
FetchContent_MakeAvailable(xxhash)
set(COSMETIC_SOURCES src/color_utils.cpp src/midna_hair_color.cpp src/texture_utils.cpp src/hooks.cpp)
add_mod(basic_cosmetics_mod
FEATURES game fmt
SOURCES src/mod.cpp ${COSMETIC_SOURCES}
MOD_JSON mod.json
RES_DIR res
BUNDLE
)
target_link_libraries(basic_cosmetics_mod PRIVATE xxHash::xxhash)
+7
View File
@@ -0,0 +1,7 @@
{
"id": "dev.twilitrealm.basic_cosmetics",
"name": "Basic Cosmetics",
"version": "1.0.0",
"author": "Twilit Realm",
"description": "Change Basic Cosmetic Colors"
}
@@ -0,0 +1,88 @@
#include "color_utils.hpp"
uint8_t desaturate_rgb_565(uint16_t rgb565Val) {
const uint32_t r = (rgb565Val & 0xf800) >> 11;
const uint32_t g = (rgb565Val & 0x7e0) >> 5;
const uint32_t b = rgb565Val & 0x1f;
// Here we are doing a quicker (0.22 * r + 0.72 * g + 0.06 * b) which
// uses multiplies and shifts rather than division.
const uint32_t combined = 30480413 * r + 49085341 * g + 8312839 * b;
uint8_t shifted = (combined >> 24) & 0xff;
// Check if should round up shifted value.
if (shifted < 0xff && combined & 0x00800000) {
shifted += 1;
}
return shifted;
}
uint16_t blend_overlay_rgb_565(uint8_t grayVal, GXColor color) {
uint32_t rTimes255, gTimes255, bTimes255;
if (grayVal <= 0x7f) {
const uint32_t grayTimesTwo = 2 * grayVal;
rTimes255 = grayTimesTwo * color.r;
gTimes255 = grayTimesTwo * color.g;
bTimes255 = grayTimesTwo * color.b;
} else {
const uint32_t multiplier = 2 * (255 - grayVal);
rTimes255 = 255 * 255 - multiplier * (255 - color.r);
gTimes255 = 255 * 255 - multiplier * (255 - color.g);
bTimes255 = 255 * 255 - multiplier * (255 - color.b);
}
// Divide each by 255
const uint32_t r = (rTimes255 + 1 + (rTimes255 >> 8)) >> 8;
const uint32_t g = (gTimes255 + 1 + (gTimes255 >> 8)) >> 8;
const uint32_t b = (bTimes255 + 1 + (bTimes255 >> 8)) >> 8;
return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3);
}
// Helper function to perform overlay blending on a single 8-bit color channel
uint8_t blend_overlay_channel(uint8_t base, uint8_t blend) {
if (base < 128) {
return static_cast<uint8_t>((2 * base * blend) / 255);
}
return static_cast<uint8_t>(255 - (2 * (255 - base) * (255 - blend)) / 255);
}
bool is_valid_hex_color_str(std::string_view hexStr) {
return hexStr.find_first_not_of("0123456789ABCDEFabcdef") == std::string_view::npos &&
hexStr.length() == 6;
}
GXColor hex_color_str_to_gx_color(const std::string& hexColorStr) {
u8 r = std::stoi(hexColorStr.substr(0, 2), nullptr, 16);
u8 g = std::stoi(hexColorStr.substr(2, 2), nullptr, 16);
u8 b = std::stoi(hexColorStr.substr(4, 2), nullptr, 16);
return GXColor{r, g, b};
}
static f32 rainbowPhaseAngle = 0.f;
GXColor get_rainbow_rgb(f32 amplitude) {
f32 phase_rad = rainbowPhaseAngle * M_PI / 180.0f;
u8 r_val = (u8)(amplitude * (sinf(phase_rad) + 1.0f) + 0.5f);
u8 g_val = (u8)(amplitude * (sinf(phase_rad + 2.0f * M_PI / 3.0f) + 1.0f) + 0.5f);
u8 b_val = (u8)(amplitude * (sinf(phase_rad + 4.0f * M_PI / 3.0f) + 1.0f));
GXColor rgbColor;
rgbColor.r = r_val;
rgbColor.g = g_val;
rgbColor.b = b_val;
rgbColor.a = 0xff;
return rgbColor;
}
void update_rainbow_rgb(f32 increment) {
rainbowPhaseAngle += increment;
if (rainbowPhaseAngle >= 360.0f) {
rainbowPhaseAngle -= 360.0f;
}
}
@@ -0,0 +1,30 @@
#pragma once
/**
* File originally copied from console TPR with permission from isaac
* https://github.com/zsrtp/libtp_rel/blob/master/include/util/color_utils.h
*/
#include <gx.h>
#include <cstdint>
#include <string>
// Desaturates an RGB565 color to a u8 gray value (0xFF being white and 0x00
// being black).
uint8_t desaturate_rgb_565(uint16_t rgb565Val);
// Performs an "Overlay" blend of a u8 gray value and a pointer to a u8
// array of {r,g,b}. Returns the result as an RGB565.
uint16_t blend_overlay_rgb_565(uint8_t grayVal, GXColor color);
// Perform overlay blending on a single 8-bit color channel
uint8_t blend_overlay_channel(uint8_t base, uint8_t blend);
bool is_valid_hex_color_str(std::string_view hexStr);
GXColor hex_color_str_to_gx_color(const std::string& hexColorStr);
GXColor get_rainbow_rgb(f32 amplitude);
void update_rainbow_rgb(f32 increment);
+745
View File
@@ -0,0 +1,745 @@
#include "hooks.hpp"
#include "color_utils.hpp"
#include "midna_hair_color.hpp"
#include "mod.hpp"
#include "types.h"
#include "mods/svc/hook.hpp"
#include "mods/svc/log.hpp"
#include "SSystem/SComponent/c_phase.h"
#include "d/actor/d_a_alink.h"
#include "d/actor/d_a_midna.h"
#include "d/d_a_item_static.h"
#include "d/d_bright_check.h"
#include "d/d_file_sel_info.h"
#include "d/d_file_select.h"
#include "d/d_kankyo.h"
#include "d/d_kantera_icon_meter.h"
#include "d/d_menu_collect.h"
#include "d/d_menu_dmap.h"
#include "d/d_menu_fishing.h"
#include "d/d_menu_fmap2D.h"
#include "d/d_menu_insect.h"
#include "d/d_menu_letter.h"
#include "d/d_menu_option.h"
#include "d/d_menu_ring.h"
#include "d/d_menu_save.h"
#include "d/d_menu_skill.h"
#include "d/d_meter2.h"
#include "d/d_meter2_draw.h"
#include "d/d_meter2_info.h"
#include "d/d_meter_button.h"
#include "d/d_meter_hakusha.h"
#include "d/d_msg_object.h"
#include "d/d_msg_out_font.h"
#include "d/d_msg_scrn_base.h"
#include "d/d_pane_class.h"
// Lantern ambience color
DEFINE_HOOK(&dKy_WolfEyeLight_set, WolfEyeLightSet);
void wolf_eye_light_set_post(ModContext*, void*, void*, void*) {
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
dScnKy_env_light_c* kankyo = dKy_getEnvlight();
kankyo->field_0x0c18[0].mColor.r = lanternColor.r;
kankyo->field_0x0c18[0].mColor.g = lanternColor.g;
kankyo->field_0x0c18[0].mColor.b = lanternColor.b;
}
}
// Lantern Sphere color
DEFINE_HOOK(&daAlink_c::preKandelaarDraw, PreKandelaarDraw);
void pre_kandelaar_draw_post(ModContext*, void*, void*, void*) {
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
J3DMaterial* mat_p = daAlink_getAlinkActorClass()
->mpKanteraGlowModel->getModelData()
->getMaterialNodePointer(0);
J3DGXColorS10 color;
color.r = lanternColor.r;
color.g = lanternColor.g;
color.b = lanternColor.b;
color.a = 255;
mat_p->setTevColor(1, &color);
color.r = lanternColor.r;
color.g = lanternColor.g;
color.b = lanternColor.b;
mat_p->setTevColor(2, &color);
}
}
// Main Lantern Meter Color
DEFINE_HOOK(&CPaneMgr::setBlackWhite, CPaneMgrSetBlackWhite);
HookAction cpane_mgr_set_black_white_pre(ModContext*, void* args, void*, void*) {
// Check for magic meter
auto pane = mods::arg<CPaneMgr*>(args, 0);
if (dMeter2Info_getMeterClass() == NULL ||
pane != dMeter2Info_getMeterClass()->getMeterDrawPtr()->mpMagicMeter)
{
return HOOK_CONTINUE;
}
// If magic meter, check to see we're setting lantern colors
auto& black = mods::arg_ref<JUtility::TColor>(args, 1);
auto& white = mods::arg_ref<JUtility::TColor>(args, 2);
if (black != JUtility::TColor(255, 255, 140, 255) &&
white != JUtility::TColor(230, 170, 0, 255))
{
return HOOK_CONTINUE;
}
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
black = JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255);
white = JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255);
}
return HOOK_CONTINUE;
}
// Lantern Icon Meter Color
DEFINE_HOOK(&dKantera_icon_c::setNowGauge, KanteraIconSetNowGauge);
void kantera_icon_set_now_gauge_post(ModContext*, void* args, void*, void*) {
auto maybeLanternColor = get_config_var_color(get_cvars().lanternGlowColor, true);
if (maybeLanternColor.has_value()) {
auto lanternColor = maybeLanternColor.value();
auto kanteraIcon = mods::arg<dKantera_icon_c*>(args, 0);
kanteraIcon->mpGauge->setBlackWhite(
JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255),
JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255));
}
}
// Light Sword Effect Color
DEFINE_HOOK(&daAlink_c::setLightningSwordEffect, SetLightningSwordEffect);
void set_lightning_sword_effect_post(ModContext*, void* args, void*, void*) {
auto maybeGlowColor = get_config_var_color(get_cvars().lightSwordGlowColor, true);
if (maybeGlowColor.has_value()) {
auto glowColor = maybeGlowColor.value();
auto link = mods::arg<daAlink_c*>(args, 0);
// Check copied from inside the hooked function
if (link->mEquipItem == 0x103 && link->checkNoResetFlg3(daPy_py_c::FLG3_UNK_100000)) {
for (size_t i = 0; i < 3; i++) {
auto emitter = dComIfGp_particle_getEmitter(link->field_0x327c[i]);
if (emitter != NULL) {
emitter->setGlobalEnvColor(glowColor.r, glowColor.g, glowColor.b);
emitter->setGlobalPrmColor(glowColor.r, glowColor.g, glowColor.b);
}
}
}
}
}
void recolor_ui_button(ConfigVarHandle option, u64 tag, J2DScreen* screen) {
auto buttonColorStr = get_str_option(option, "");
if (is_valid_hex_color_str(buttonColorStr)) {
auto color = hex_color_str_to_gx_color(buttonColorStr);
auto element = static_cast<J2DPicture*>(screen->search(tag));
if (element != nullptr) {
element->setBlackWhite(
JUtility::TColor(0, 0, 0, 0), JUtility::TColor(color.r, color.g, color.b, 0xFF));
}
}
}
// Main gameplay UI. Top left hearts and top right buttons
DEFINE_HOOK(&dMeter2Draw_c::init, dMeter2Init);
void d_meter_2_init_post(ModContext*, void* args, void*, void*) {
auto dMeter2Draw = mods::arg<dMeter2Draw_c*>(args, 0);
auto screen = dMeter2Draw->getMainScreenPtr();
// Heart tags on main UI
static constexpr std::array kHeartTags = {MULTI_CHAR('hear_00'), MULTI_CHAR('hear_01'),
MULTI_CHAR('hear_02'), MULTI_CHAR('hear_03'), MULTI_CHAR('hear_04'), MULTI_CHAR('hear_05'),
MULTI_CHAR('hear_06'), MULTI_CHAR('hear_07'), MULTI_CHAR('hear_08'), MULTI_CHAR('hear_09'),
MULTI_CHAR('hear_10'), MULTI_CHAR('hear_11'), MULTI_CHAR('hear_12'), MULTI_CHAR('hear_13'),
MULTI_CHAR('hear_14'), MULTI_CHAR('hear_15'), MULTI_CHAR('hear_16'), MULTI_CHAR('hear_17'),
MULTI_CHAR('hear_18'), MULTI_CHAR('hear_19'), MULTI_CHAR('bigh_00'), MULTI_CHAR('bigh_01'),
MULTI_CHAR('bigh_02'), MULTI_CHAR('bigh_03')};
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
for (auto tag : kHeartTags) {
auto element = static_cast<J2DPicture*>(screen->search(tag));
if (element != nullptr) {
element->setBlackWhite(heartColor, JUtility::TColor(200, 200, 200, 255));
}
}
}
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
recolor_ui_button(get_cvars().xButtonColor, MULTI_CHAR('x_btn'), screen);
recolor_ui_button(get_cvars().yButtonColor, MULTI_CHAR('y_btn'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('zbtn'), screen);
}
// Hearts on the file select screen
DEFINE_HOOK(&dFile_info_c::setHeartCnt, FileInfoSetHeartCount);
void file_info_set_heart_count_post(ModContext*, void* args, void*, void*) {
auto fileInfo = mods::arg<dFile_info_c*>(args, 0);
// Heart tags on file select
static constexpr std::array kFileSelectHeartTags{
MULTI_CHAR('hear_20'),
MULTI_CHAR('hear_21'),
MULTI_CHAR('hear_22'),
MULTI_CHAR('hear_23'),
MULTI_CHAR('hear_24'),
MULTI_CHAR('hear_25'),
MULTI_CHAR('hear_26'),
MULTI_CHAR('hear_27'),
MULTI_CHAR('hear_28'),
MULTI_CHAR('hear_29'),
MULTI_CHAR('hear_30'),
MULTI_CHAR('hear_31'),
MULTI_CHAR('hear_32'),
MULTI_CHAR('hear_33'),
MULTI_CHAR('hear_34'),
MULTI_CHAR('hear_35'),
MULTI_CHAR('hear_36'),
MULTI_CHAR('hear_37'),
MULTI_CHAR('hear_38'),
MULTI_CHAR('hear_39'),
};
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
for (auto tag : kFileSelectHeartTags) {
auto element = static_cast<J2DPicture*>(fileInfo->mFileInfo.Scr->search(tag));
if (element != nullptr) {
element->setBlackWhite(heartColor, JUtility::TColor(200, 200, 200, 255));
}
}
}
}
// Buttons that appear contextually at the bottom of the screen during gameplay
// (Are X, Y ever used there?)
DEFINE_HOOK(&dMeterButton_c::screenInitButton, ScreenInitButton);
void screen_init_button_post(ModContext*, void* args, void*, void*) {
auto meterButton = mods::arg<dMeterButton_c*>(args, 0);
auto screen = meterButton->mpButtonScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
recolor_ui_button(get_cvars().xButtonColor, MULTI_CHAR('x_btn'), screen);
recolor_ui_button(get_cvars().yButtonColor, MULTI_CHAR('y_btn'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('zbtn'), screen);
}
// A and B buttons when saving a file
DEFINE_HOOK(&dMenu_save_c::screenSet, MenuSaveScreenSet);
void menu_save_screen_set_post(ModContext*, void* args, void*, void*) {
auto menuSave = mods::arg<dMenu_save_c*>(args, 0);
auto screen = menuSave->mSaveSel.Scr;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('wabtn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('wbbtn'), screen);
}
// A and B buttons when selecting a file
DEFINE_HOOK(&dFile_select_c::screenSet, FileSelectScreenSet);
void file_select_screen_set_post(ModContext*, void* args, void*, void*) {
auto fileSelect = mods::arg<dFile_select_c*>(args, 0);
auto screen = fileSelect->fileSel.Scr;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('wabtn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('wbbtn'), screen);
}
// A button on brightness check screen
DEFINE_HOOK(&dBrightCheck_c::screenSet, BrightCheckScreenSet);
void bright_check_screen_set_post(ModContext*, void* args, void*, void*) {
auto brightCheck = mods::arg<dBrightCheck_c*>(args, 0);
auto screen = brightCheck->mBrightCheck.Scr;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), screen);
}
// X and Y buttons on the item wheel screen
DEFINE_HOOK(&dMenu_Ring_c::_create, MenuRingCreate);
void menu_ring_create_post(ModContext*, void* args, void*, void*) {
auto menuRing = mods::arg<dMenu_Ring_c*>(args, 0);
auto screen = menuRing->mpScreen;
recolor_ui_button(get_cvars().xButtonColor, MULTI_CHAR('xbtn'), screen);
recolor_ui_button(get_cvars().yButtonColor, MULTI_CHAR('ybtn'), screen);
}
// A and B buttons on the main pause screen
DEFINE_HOOK(&dMenu_Collect2D_c::_create, MenuCollect2DCreate);
void menu_collect_2D_create_post(ModContext*, void* args, void*, void*) {
auto menuCollect2D = mods::arg<dMenu_Collect2D_c*>(args, 0);
auto screen = menuCollect2D->mpScreenIcon;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A and B buttons on the fishing journal screen
DEFINE_HOOK(&dMenu_Fishing_c::screenSetDoIcon, MenuFishingScreenSetDoIcon);
void menu_fishing_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuFishing = mods::arg<dMenu_Fishing_c*>(args, 0);
auto screen = menuFishing->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A and B buttons on the insect collection screen
DEFINE_HOOK(&dMenu_Insect_c::screenSetDoIcon, MenuInsectScreenSetDoIcon);
void menu_insect_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuInsect = mods::arg<dMenu_Insect_c*>(args, 0);
auto screen = menuInsect->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A and B buttons on the letters screen
DEFINE_HOOK(&dMenu_Letter_c::screenSetDoIcon, MenuLetterScreenSetDoIcon);
void menu_letter_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuLetter = mods::arg<dMenu_Letter_c*>(args, 0);
auto screen = menuLetter->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A, B, and Z buttons on option screen
DEFINE_HOOK(&dMenu_Option_c::_create, MenuOptionCreate);
void menu_option_create_post(ModContext*, void* args, void*, void*) {
auto menuOption = mods::arg<dMenu_Option_c*>(args, 0);
auto screen = menuOption->mpScreenIcon;
auto backScreen = menuOption->mpBackScreen;
auto tvScreen = menuOption->mpTVScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('g_zbtn'), backScreen);
// A Button on option's brightness check screen
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), tvScreen);
}
// A and B buttons on the hidden skills screen
DEFINE_HOOK(&dMenu_Skill_c::screenSetDoIcon, MenuSkillScreenSetDoIcon);
void menu_skill_screen_set_do_icon_post(ModContext*, void* args, void*, void*) {
auto menuSkill = mods::arg<dMenu_Skill_c*>(args, 0);
auto screen = menuSkill->mpIconScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A, B, and Z buttons on the field map screen
DEFINE_HOOK(&dMenu_Fmap_c::_create, MenuFMapCreate);
void menu_fmap_create_post(ModContext*, void* args, void*, void*) {
auto menuFMap = mods::arg<dMenu_Fmap_c*>(args, 0);
auto screen = menuFMap->mpDraw2DTop->mpTitleScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn1'), screen);
recolor_ui_button(get_cvars().zButtonColor, MULTI_CHAR('zbtn'), screen);
}
// A and B buttons on the dungeon map screen
DEFINE_HOOK_SYMBOL("dMenu_DmapBg_c::buttonIconScreenInit", void(dMenu_DmapBg_c*), MenuDMapButtonIconScreenInit);
void menu_dmap_button_icon_screen_init_post(ModContext*, void* args, void*, void*) {
auto menuDMap = mods::arg<dMenu_DmapBg_c*>(args, 0);
auto screen = menuDMap->mButtonScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn'), screen);
recolor_ui_button(get_cvars().bButtonColor, MULTI_CHAR('b_btn'), screen);
}
// A button on the howling screen
DEFINE_HOOK(&dMsgObject_c::talkStartInit, MsgObjectTalkStartInit);
void msg_object_talk_start_init_post(ModContext*, void* args, void*, void*) {
auto msgObject = mods::arg<dMsgObject_c*>(args, 0);
// If textbox kind is howling
if (msgObject->mFukiKind == 17) {
auto screen = msgObject->mpScrnDraw->mpScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('abtn'), screen);
}
}
// A button when on Epona
DEFINE_HOOK(&dMeterHakusha_c::_create, MeterHakushaCreate);
void meter_hakusha_create_post(ModContext*, void* args, void*, void*) {
auto meterHakusha = mods::arg<dMeterHakusha_c*>(args, 0);
auto screen = meterHakusha->mpButtonScreen;
recolor_ui_button(get_cvars().aButtonColor, MULTI_CHAR('a_btn1'), screen);
}
// A, B, X, and Y button icons in text
DEFINE_HOOK(&COutFont_c::createPane, OutFontCreatePane);
void out_font_create_pane_post(ModContext*, void* args, void*, void*) {
auto outFont = mods::arg<COutFont_c*>(args, 0);
auto paneArr = outFont->mpPane;
auto maybeAButtonColor = get_config_var_color(get_cvars().aButtonColor);
if (maybeAButtonColor.has_value()) {
auto aButtonColor = maybeAButtonColor.value();
paneArr[0]->setBlackWhite(JUtility::TColor(255, 255, 255, 0),
JUtility::TColor(aButtonColor.r, aButtonColor.g, aButtonColor.b, 255));
}
auto maybeBButtonColor = get_config_var_color(get_cvars().bButtonColor);
if (maybeBButtonColor.has_value()) {
auto bButtonColor = maybeBButtonColor.value();
paneArr[1]->setBlackWhite(JUtility::TColor(255, 255, 255, 0),
JUtility::TColor(bButtonColor.r, bButtonColor.g, bButtonColor.b, 255));
}
// Condition copied from hooked function
auto xyBlack = JUtility::TColor(255, 255, 255, 0);
if (outFont->field_0x242 == 1) {
xyBlack = JUtility::TColor(0, 0, 0, 0);
}
auto maybeXButtonColor = get_config_var_color(get_cvars().xButtonColor);
if (maybeXButtonColor.has_value()) {
auto xButtonColor = maybeXButtonColor.value();
paneArr[5]->setBlackWhite(
xyBlack, JUtility::TColor(xButtonColor.r, xButtonColor.g, xButtonColor.b, 255));
}
auto maybeYButtonColor = get_config_var_color(get_cvars().yButtonColor);
if (maybeYButtonColor.has_value()) {
auto yButtonColor = maybeYButtonColor.value();
paneArr[6]->setBlackWhite(
xyBlack, JUtility::TColor(yButtonColor.r, yButtonColor.g, yButtonColor.b, 255));
}
}
// Heart icon in text
DEFINE_HOOK(&COutFontSet_c::drawFont, OutFontSetDrawFont);
void out_font_set_draw_font_post(ModContext*, void* args, void*, void*) {
auto outFontSet = mods::arg<COutFontSet_c*>(args, 0);
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
u32 heartColor_u32 = heartColor.r << 24 | heartColor.g << 16 | heartColor.b << 8 | 0xFF;
if (outFontSet->getType() == 0x1B) { // Heart icon type
outFontSet->mColor = heartColor_u32;
}
}
}
// Heart Drop/Piece of Heart/Heart Container model color
DEFINE_HOOK(&daItemBase_c::CreateItemHeap, ItemBaseCreateItemHeap);
void item_base_create_item_heap_post(ModContext*, void* args, void*, void*) {
auto itemBase = mods::arg<daItemBase_c*>(args, 0);
if (itemBase == NULL) {
return;
}
auto itemNo = itemBase->m_itemNo;
if (itemNo == dItemNo_HEART_e || itemNo == dItemNo_UTAWA_HEART_e ||
itemNo == dItemNo_KAKERA_HEART_e)
{
auto maybeHeartColor = get_config_var_color(get_cvars().heartColor);
if (maybeHeartColor.has_value()) {
auto heartColor = maybeHeartColor.value();
auto heartColorS10 = GXColorS10(heartColor.r, heartColor.g, heartColor.b, heartColor.a);
// Edit inner heart material color for Piece of Heart / Heart Container
if (itemNo == dItemNo_UTAWA_HEART_e || itemNo == dItemNo_KAKERA_HEART_e) {
*itemBase->mpModel->getModelData()->getMaterialNodePointer(3)->getTevKColor(1) =
heartColor;
*itemBase->mpModel->getModelData()->getMaterialNodePointer(3)->getTevColor(1) =
heartColorS10;
}
const u8 heartColorRGB[3] = {heartColor.r, heartColor.g, heartColor.b};
u8** cRegTable =
reinterpret_cast<u8**>(&itemBase->mpBrkAnm->getBrkAnm()->mAnmCRegDataR);
u8** kRegTable =
reinterpret_cast<u8**>(&itemBase->mpBrkAnm->getBrkAnm()->mAnmKRegDataR);
for (int i = 0; i < 3; i++) {
u8* cReg = cRegTable[i];
u8* kReg = kRegTable[i];
auto curColor = heartColorRGB[i];
// Set heart drop inner heart color
cReg[0x3] = curColor;
cReg[0xB] = curColor;
// Set heart drop outer heart color
kReg[0x3] = curColor;
kReg[0xB] = curColor;
if (itemNo == dItemNo_KAKERA_HEART_e) {
cReg[0x13] = curColor;
cReg[0x1B] = curColor;
kReg[0x13] = curColor;
kReg[0x1B] = curColor;
}
if (itemNo == dItemNo_UTAWA_HEART_e) {
cReg[0x13] = curColor;
kReg[0x13] = curColor;
kReg[0x1B] = curColor;
kReg[0x23] = curColor;
kReg[0x2B] = curColor;
}
}
}
}
}
// Midna Hair Color
DEFINE_HOOK(&daMidna_c::create, MidnaCreate);
void midna_create_post(ModContext*, void* args, void* retval, void*) {
auto step = reinterpret_cast<int*>(retval);
// Don't set colors if midna isn't done loading
if (*step != cPhs_COMPLEATE_e) {
return;
}
auto midna = mods::arg<daMidna_c*>(args, 0);
midna->field_0x6e0 = g_currentMidnaHairColors.normalColor;
if (dKy_darkworld_check()) {
midna->field_0x6e8 = g_currentMidnaHairColors.normalKColor;
midna->field_0x6ec = g_currentMidnaHairColors.normalKColor2;
} else {
midna->field_0x6e8 = g_currentMidnaHairColors.lNormalKColor;
midna->field_0x6ec = g_currentMidnaHairColors.lNormalKColor2;
}
}
// Override Midna Hair Color Part 2
DEFINE_HOOK(&daMidna_c::setBodyPartMatrix, MidnaSetBodyPartMatrix);
static GXColorS10 midnaField0x6e0{};
static GXColor midnaField0x6e8{};
static GXColor midnaField0x6ec{};
// Copy the original values before setBodyPartMatrix runs
HookAction midna_set_body_part_matrix_pre(ModContext*, void* args, void* retval, void* userdata) {
auto midna = mods::arg<daMidna_c*>(args, 0);
midnaField0x6e0 = midna->field_0x6e0;
midnaField0x6e8 = midna->field_0x6e8;
midnaField0x6ec = midna->field_0x6ec;
return HOOK_CONTINUE;
}
void midna_set_body_part_matrix_post(ModContext*, void* args, void* retval, void* userdata) {
auto midna = mods::arg<daMidna_c*>(args, 0);
if (midna->mpHairhandBmd != NULL) {
// Restore the original values from before setBodyPartMatrix ran (this undoes the chase)
midna->field_0x6e0 = midnaField0x6e0;
midna->field_0x6e8 = midnaField0x6e8;
midna->field_0x6ec = midnaField0x6ec;
// Statement copied from inside function to determine colors
bool bigColors =
midna->checkStateFlg0(daMidna_c::FLG0_UNK_10000000) ||
midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_HAIR].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_S_TAKES].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_S_WAITS].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_S_PACKAWAY].mResID ||
midna->mBckHeap[2].getIdx() ==
daMidna_c::m_anmDataTable[daMidna_c::ANM_GRABST].mResID ||
midna->checkEndResetStateFlg0(daMidna_c::ERFLG0_UNK_40) ||
dComIfGp_checkPlayerStatus1(0, 0x800000);
GXColorS10 color{};
GXColor kcolor1{};
GXColor kcolor2{};
// Set our own colors
if (bigColors) {
kcolor1 = g_currentMidnaHairColors.bigKColor;
if (dKy_darkworld_check()) {
color = g_currentMidnaHairColors.bigColor;
kcolor2 = g_currentMidnaHairColors.normalKColor2;
} else {
color = g_currentMidnaHairColors.lBigColor;
kcolor2 = g_currentMidnaHairColors.lBigKColor2;
}
} else {
color = g_currentMidnaHairColors.normalColor;
if (dKy_darkworld_check()) {
kcolor1 = g_currentMidnaHairColors.normalKColor;
kcolor2 = g_currentMidnaHairColors.normalKColor2;
} else {
kcolor1 = g_currentMidnaHairColors.lNormalKColor;
kcolor2 = g_currentMidnaHairColors.lNormalKColor2;
}
}
// Reapply the chase that happens in the function
cLib_chaseS(&midna->field_0x6e0.r, color.r, 10);
cLib_chaseS(&midna->field_0x6e0.g, color.g, 10);
cLib_chaseS(&midna->field_0x6e0.b, color.b, 10);
cLib_chaseUC(&midna->field_0x6e8.r, kcolor1.r, 10);
cLib_chaseUC(&midna->field_0x6e8.g, kcolor1.g, 10);
cLib_chaseUC(&midna->field_0x6e8.b, kcolor1.b, 10);
cLib_chaseUC(&midna->field_0x6ec.r, kcolor2.r, 10);
cLib_chaseUC(&midna->field_0x6ec.g, kcolor2.g, 10);
cLib_chaseUC(&midna->field_0x6ec.b, kcolor2.b, 10);
}
}
// Midna Charge Ring Color
DEFINE_HOOK(&daAlink_c::setWolfLockDomeModel, SetWolfLockDomeModel);
void wolf_lock_dome_model_post(ModContext*, void*, void*, void*) {
auto domeRingColorStr = get_str_option(get_cvars().midnaChargeRingColor, "");
if (is_valid_hex_color_str(domeRingColorStr)) {
auto domeRingColor = hex_color_str_to_gx_color(domeRingColorStr);
const u8 domeWave1RGBA[3] = {domeRingColor.r, domeRingColor.g, domeRingColor.b};
const u8 domeWave2RGBA[3] = {domeRingColor.r, domeRingColor.g, domeRingColor.b};
u8** chromaRegisterTable =
reinterpret_cast<u8**>(&daAlink_getAlinkActorClass()->field_0x0724->mAnmCRegDataR);
for (int i = 0; i < 3; i++) {
u8* currentTable = chromaRegisterTable[i];
const u8 currentWave1Color = domeWave1RGBA[i];
const u8 currentWave2Color = domeWave2RGBA[i];
const u8 currentBaseColor = (currentWave1Color + currentWave2Color) / 2;
currentTable[0x3] = currentBaseColor; // Set Alpha for the ring base
currentTable[0x13] = currentWave1Color; // Set Alpha for ring wave 1
currentTable[0x23] = currentWave2Color; // Set Alpha for ring wave 2
currentTable[0xB] = currentBaseColor; // Set Alpha for darkworld ring base
currentTable[0x1B] = currentWave1Color; // Set Alpha for darkworld ring wave 1
currentTable[0x2B] = currentWave2Color; // Set Alpha for darkworld ring wave 2
}
}
}
#define ADD_POST_HOOK(defined_hook, function, original) \
result = mods::hook::add_post<defined_hook>(function); \
if (result != MOD_OK) { \
mods::log::debug( \
"failed to add post hook to" #original ", Result {}", static_cast<int>(result)); \
return result; \
}
#define ADD_PRE_HOOK(defined_hook, function, original) \
result = mods::hook::add_pre<defined_hook>(function); \
if (result != MOD_OK) { \
mods::log::debug( \
"failed to add pre hook to" #original ", Result {}", static_cast<int>(result)); \
return result; \
}
ModResult add_all_hooks() {
ModResult result{};
// Hooks for lantern glow
ADD_POST_HOOK(WolfEyeLightSet, wolf_eye_light_set_post, dKy_WolfEyeLight_set)
ADD_POST_HOOK(PreKandelaarDraw, pre_kandelaar_draw_post, daAlink_c::preKandelaarDraw)
// Hooks for lantern meter color
ADD_PRE_HOOK(CPaneMgrSetBlackWhite, cpane_mgr_set_black_white_pre, CPaneMgr::setBlackWhite)
ADD_POST_HOOK(
KanteraIconSetNowGauge, kantera_icon_set_now_gauge_post, dKantera_icon_c::setNowGauge)
// Hook for midna charge ring
ADD_POST_HOOK(SetWolfLockDomeModel, wolf_lock_dome_model_post, daAlink_c::setWolfLockDomeModel)
// Hook for light sword glow
ADD_POST_HOOK(SetLightningSwordEffect, set_lightning_sword_effect_post,
daAlink_c::setLightningSwordEffect)
// Hooks for Midna Hair Color
ADD_POST_HOOK(MidnaCreate, midna_create_post, daMidna_c::create)
ADD_PRE_HOOK(
MidnaSetBodyPartMatrix, midna_set_body_part_matrix_pre, daMidna_c::setBodyPartMatrix)
ADD_POST_HOOK(
MidnaSetBodyPartMatrix, midna_set_body_part_matrix_post, daMidna_c::setBodyPartMatrix)
// Hooks for UI colors
ADD_POST_HOOK(dMeter2Init, d_meter_2_init_post, dMeter2Draw_c::init)
ADD_POST_HOOK(FileInfoSetHeartCount, file_info_set_heart_count_post, dFile_info_c::setHeartCnt)
ADD_POST_HOOK(ScreenInitButton, screen_init_button_post, dMeterButton_c::screenInitButton)
ADD_POST_HOOK(MenuSaveScreenSet, menu_save_screen_set_post, dMenu_save_c::screenSet)
ADD_POST_HOOK(FileSelectScreenSet, file_select_screen_set_post, dFile_select_c::screenSet)
ADD_POST_HOOK(BrightCheckScreenSet, bright_check_screen_set_post, dBrightCheck_c::screenSet)
ADD_POST_HOOK(MenuRingCreate, menu_ring_create_post, dMenu_Ring_c::_create)
ADD_POST_HOOK(MenuCollect2DCreate, menu_collect_2D_create_post, dMenu_Collect2D_c::_create)
ADD_POST_HOOK(MenuFishingScreenSetDoIcon, menu_fishing_screen_set_do_icon_post,
dMenu_Fishing_c::screenSetDoIcon)
ADD_POST_HOOK(MenuInsectScreenSetDoIcon, menu_insect_screen_set_do_icon_post,
dMenu_Insect_c::screenSetDoIcon)
ADD_POST_HOOK(MenuLetterScreenSetDoIcon, menu_letter_screen_set_do_icon_post,
dMenu_Letter_c::screenSetDoIcon)
ADD_POST_HOOK(MenuOptionCreate, menu_option_create_post, dMenu_Option_c::_create)
ADD_POST_HOOK(MenuSkillScreenSetDoIcon, menu_skill_screen_set_do_icon_post,
dMenu_Skill_c::screenSetDoIcon)
ADD_POST_HOOK(OutFontCreatePane, out_font_create_pane_post, COutFont_c::createPane)
ADD_POST_HOOK(OutFontSetDrawFont, out_font_set_draw_font_post, COutFontSet_c::drawFont)
ADD_POST_HOOK(MenuFMapCreate, menu_fmap_create_post, dMenu_Fmap_c::_create)
ADD_POST_HOOK(MenuDMapButtonIconScreenInit, menu_dmap_button_icon_screen_init_post,
dMenu_DmapBg_c::buttonIconScreenInit)
ADD_POST_HOOK(
MsgObjectTalkStartInit, msg_object_talk_start_init_post, dMsgObject_c::talkStartInit)
ADD_POST_HOOK(MeterHakushaCreate, meter_hakusha_create_post, dMeterHakusha_c::_create)
// Heart Model Color
ADD_POST_HOOK(
ItemBaseCreateItemHeap, item_base_create_item_heap_post, daItemBase_c::CreateItemHeap)
return MOD_OK;
}
#define UNINSTALL_HOOK(defined_hook) mods::hook::uninstall<defined_hook>(svc_hook);
ModResult remove_all_hooks() {
UNINSTALL_HOOK(WolfEyeLightSet)
UNINSTALL_HOOK(PreKandelaarDraw)
UNINSTALL_HOOK(CPaneMgrSetBlackWhite)
UNINSTALL_HOOK(KanteraIconSetNowGauge)
UNINSTALL_HOOK(SetWolfLockDomeModel)
UNINSTALL_HOOK(SetLightningSwordEffect)
UNINSTALL_HOOK(MidnaCreate)
UNINSTALL_HOOK(MidnaSetBodyPartMatrix)
UNINSTALL_HOOK(MidnaSetBodyPartMatrix)
UNINSTALL_HOOK(dMeter2Init)
UNINSTALL_HOOK(FileInfoSetHeartCount)
UNINSTALL_HOOK(ScreenInitButton)
UNINSTALL_HOOK(MenuSaveScreenSet)
UNINSTALL_HOOK(FileSelectScreenSet)
UNINSTALL_HOOK(BrightCheckScreenSet)
UNINSTALL_HOOK(MenuRingCreate)
UNINSTALL_HOOK(MenuCollect2DCreate)
UNINSTALL_HOOK(MenuFishingScreenSetDoIcon)
UNINSTALL_HOOK(MenuInsectScreenSetDoIcon)
UNINSTALL_HOOK(MenuLetterScreenSetDoIcon)
UNINSTALL_HOOK(MenuOptionCreate)
UNINSTALL_HOOK(MenuSkillScreenSetDoIcon)
UNINSTALL_HOOK(OutFontCreatePane)
UNINSTALL_HOOK(OutFontSetDrawFont)
UNINSTALL_HOOK(MenuFMapCreate)
UNINSTALL_HOOK(MenuDMapButtonIconScreenInit)
UNINSTALL_HOOK(MsgObjectTalkStartInit)
UNINSTALL_HOOK(MeterHakushaCreate)
UNINSTALL_HOOK(ItemBaseCreateItemHeap)
return MOD_OK;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "mods/service.hpp"
ModResult add_all_hooks();
ModResult remove_all_hooks();
@@ -0,0 +1,115 @@
#include "midna_hair_color.hpp"
#include "mod.hpp"
#include "mods/svc/log.hpp"
#include <array>
namespace {
constexpr std::array<std::array<GXColor, 9>, 10> kMidnaHairColors = {{
// Default
{{
/*l_lNormalKColor*/ /*l_normalKColor*/ /*l_normalColor*/
{0xFF, 0xDC, 0x00}, {0xB4, 0x87, 0x00}, {0x50, 0x00, 0x00},
/*l_bigKColor*/ /*l_lBigColor*/ /*l_bigColor*/
{0x1E, 0x00, 0x00}, {0xFF, 0x78, 0x00}, {0xFF, 0x64, 0x78},
/*l_lNormalKColor2*//*l_normalKColor2*/ /*l_lBigKColor2*/
{0x00, 0xC3, 0xEB}, {0x00, 0xC3, 0xC3}, {0xAA, 0xFF, 0xC3}
}},
// Pink
{{
{0xF5, 0xCF, 0xF3}, {0xAD, 0x7F, 0x7F}, {0x1B, 0x00, 0x20},
{0x3C, 0x02, 0x58}, {0xE3, 0x72, 0xF2}, {0xE3, 0x5F, 0xF8},
{0xDD, 0x00, 0xEB}, {0xDD, 0x00, 0xC3}, {0xF6, 0x4C, 0xFF}
}},
// Red
{{
{0xE4, 0x65, 0x41}, {0xA1, 0x3E, 0x22}, {0x21, 0x00, 0x00},
{0x4F, 0x02, 0x01}, {0xF0, 0x3A, 0x25}, {0xF0, 0x30, 0x8C},
{0xEB, 0x00, 0x00}, {0xEB, 0x00, 0x00}, {0xFF, 0x4F, 0x3A}
}},
// Yellow
{{
{0x91, 0x83, 0x0E}, {0x66, 0x50, 0x07}, {0x0E, 0x0B, 0x00},
{0x24, 0x25, 0x02}, {0xCB, 0xB7, 0x00}, {0xCB, 0x99, 0x78},
{0xEB, 0xDE, 0x00}, {0xEB, 0xDE, 0x00}, {0xFF, 0xF8, 0xBF}
}},
// Green
{{
{0x35, 0x79, 0x53}, {0x25, 0x4A, 0x2B}, {0x00, 0x0E, 0x05},
{0x0A, 0x29, 0x10}, {0x00, 0xB6, 0x6F}, {0x00, 0x98, 0xB3},
{0x1F, 0xEB, 0x00}, {0x1F, 0xEB, 0x00}, {0x9A, 0xFF, 0x81}
}},
// Blue
{{
{0x00, 0x72, 0xFF}, {0x00, 0x46, 0x85}, {0x00, 0x08, 0x28},
{0x16, 0x1B, 0x5D}, {0x00, 0x60, 0xFF}, {0x00, 0x50, 0xFF},
{0x00, 0x48, 0xEB}, {0x00, 0x48, 0xC3}, {0x3A, 0x66, 0xFF}
}},
// Purple
{{
{0x6F, 0x34, 0xFF}, {0x4E, 0x20, 0x85}, {0x0D, 0x00, 0x34},
{0x15, 0x08, 0x79}, {0x62, 0x00, 0xFF}, {0x62, 0x00, 0xFF},
{0x7B, 0x00, 0xEB}, {0x7B, 0x00, 0xC3}, {0x94, 0x3E, 0xFF}
}},
// Brown
{{
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x1A, 0x05, 0x00},
{0x3C, 0x19, 0x0E}, {0x3F, 0x1D, 0x0B}, {0x3F, 0x18, 0x7E},
{0x3F, 0x1D, 0x0B}, {0x3F, 0x1D, 0x09}, {0x59, 0x32, 0x1E}
}},
// White
{{
{0xF0, 0xF1, 0xF1}, {0xA9, 0x94, 0x7E}, {0x09, 0x0B, 0x0C},
{0x22, 0x24, 0x24}, {0xFF, 0xFF, 0xFF}, {0xFF, 0xD5, 0xFF},
{0xEA, 0xEA, 0xEA}, {0xEA, 0xEA, 0xC2}, {0xF3, 0xF3, 0xF3}
}},
// Black
{{
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x0B, 0x0B, 0x0B},
{0x23, 0x23, 0x23}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x78},
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}
}}
}};
size_t get_midna_hair_color_index(ConfigVarHandle handle) {
const auto optionIndex = get_int_option(handle, 0);
if (optionIndex < 0 || optionIndex >= static_cast<int64_t>(kMidnaHairColors.size())) {
return 0;
}
return static_cast<size_t>(optionIndex);
}
}
MidnaHairColors g_currentMidnaHairColors = {
.normalColor = {0x50, 0x00, 0x00, 0x00},
.normalKColor = {0xB4, 0x87, 0x00, 0x00},
.normalKColor2 = {0x00, 0xC3, 0xC3, 0x00},
.bigColor = {0xFF, 0x64, 0x78, 0x00},
.bigKColor = {0x1E, 0x00, 0x00, 0x00},
.lNormalKColor = {0xFF, 0xDC, 0x00, 0x00},
.lNormalKColor2 = {0x00, 0xC3, 0xEB, 0x00},
.lBigColor = {0xFF, 0x78, 0x00, 0x00},
.lBigKColor2 = {0xAA, 0xFF, 0xC3, 0x00},
};
void set_all_midna_hair_colors() {
auto& g_cvars = get_cvars();
auto hairBaseColor = get_midna_hair_color_index(g_cvars.midnaHairBaseColor);
auto hairTipsColor = get_midna_hair_color_index(g_cvars.midnaHairTipsColor);
// Colors we have to convert to GXColorS10
auto& normalColor = kMidnaHairColors.at(hairBaseColor)[2];
auto& bigColor = kMidnaHairColors.at(hairBaseColor)[5];
auto& lBigColor = kMidnaHairColors.at(hairBaseColor)[4];
g_currentMidnaHairColors.normalColor = GXColorS10{normalColor.r, normalColor.g, normalColor.b};
g_currentMidnaHairColors.normalKColor = kMidnaHairColors.at(hairBaseColor)[1];
g_currentMidnaHairColors.normalKColor2 = kMidnaHairColors.at(hairTipsColor)[7];
g_currentMidnaHairColors.bigColor = GXColorS10{bigColor.r, bigColor.g, bigColor.b};
g_currentMidnaHairColors.bigKColor = kMidnaHairColors.at(hairBaseColor)[3];
g_currentMidnaHairColors.lNormalKColor = kMidnaHairColors.at(hairBaseColor)[0];
g_currentMidnaHairColors.lNormalKColor2 = kMidnaHairColors.at(hairTipsColor)[6];
g_currentMidnaHairColors.lBigColor = GXColorS10{lBigColor.r, lBigColor.g, lBigColor.b};
g_currentMidnaHairColors.lBigKColor2 = kMidnaHairColors.at(hairTipsColor)[8];
}
@@ -0,0 +1,19 @@
#pragma once
#include <gx.h>
struct MidnaHairColors {
GXColorS10 normalColor;
GXColor normalKColor;
GXColor normalKColor2;
GXColorS10 bigColor;
GXColor bigKColor;
GXColor lNormalKColor;
GXColor lNormalKColor2;
GXColorS10 lBigColor;
GXColor lBigKColor2;
};
extern MidnaHairColors g_currentMidnaHairColors;
void set_all_midna_hair_colors();
+653
View File
@@ -0,0 +1,653 @@
#include "mod.hpp"
#include "color_utils.hpp"
#include "hooks.hpp"
#include "midna_hair_color.hpp"
#include "texture_utils.hpp"
#include "mods/service.hpp"
#include "mods/svc/config.h"
#include "mods/svc/hook.hpp"
#include "mods/svc/log.h"
#include "mods/svc/log.hpp"
#include "mods/svc/ui.h"
#include "d/d_com_inf_game.h"
#include <xxhash.h>
#include <optional>
#include <ranges>
#include <string>
DEFINE_MOD();
IMPORT_SERVICE(LogService, svc_log);
IMPORT_SERVICE(ConfigService, svc_config);
IMPORT_SERVICE(HookService, svc_hook);
IMPORT_SERVICE(TextureService, svc_texture);
IMPORT_SERVICE(UiService, svc_ui);
static cvars g_cvars;
cvars& get_cvars() {
return g_cvars;
}
std::string get_str_option(ConfigVarHandle handle, const std::string& fallback) {
std::string value{};
size_t outLength{};
svc_config->get_string(mod_ctx, handle, NULL, 0, &outLength);
value.resize(outLength);
if (handle == 0 || svc_config->get_string(
mod_ctx, handle, value.data(), value.size() + 1, &outLength) != MOD_OK)
{
return fallback;
}
return value;
}
int64_t get_int_option(ConfigVarHandle handle, int64_t fallback) {
int64_t value = fallback;
if (handle == 0 || svc_config->get_int(mod_ctx, handle, &value) != MOD_OK) {
return fallback;
}
return value;
}
// Helper for getting configVar color
std::optional<GXColor> get_config_var_color(ConfigVarHandle handle, bool allowRainbow) {
auto colorStr = get_str_option(handle, "");
// Convert to lowercase
for (auto& c : colorStr) {
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
if (colorStr == "rainbow" && allowRainbow) {
auto color = get_rainbow_rgb(127.5f);
color.r /= 2;
color.g /= 2;
color.b /= 2;
return color;
}
if (is_valid_hex_color_str(colorStr)) {
return hex_color_str_to_gx_color(colorStr);
}
return std::nullopt;
}
namespace {
UiWindowHandle g_cosmeticsWindow = 0;
bool g_loadedAllBaseTextures = false;
constexpr uint32_t kTextureLoadRetryFrames = 30;
uint32_t g_textureLoadRetryCountdown = 0;
constexpr const char* kOverlayPresets[] = {
"ab706e",
"6382a0",
"94749a",
"ec8644",
"b9ab00",
"ec9fc8",
"505154",
"f8f7f4",
"91723e",
};
constexpr const char* kLightPresets[] = {
"ff0000",
"f68821",
"f6f321",
"00ff00",
"0000ff",
"8000ff",
"a0a0a0",
"30d0d0",
};
constexpr const char* kRainbowLightPresets[] = {
"rainbow",
"ff0000",
"f68821",
"f6f321",
"00ff00",
"0000ff",
"8000ff",
"a0a0a0",
};
constexpr const char* kAButtonPresets[] = {
"ff0000",
"ff5000",
"ffaf00",
"0080ff",
"0000ff",
"8000ff",
"5555ff",
"ff20ff",
};
constexpr const char* kBButtonPresets[] = {
"ffff40",
"ffa0ff",
"00e87b",
"00aaff",
"6078ff",
"000000",
"00f3ff",
};
constexpr const char* kXyButtonPresets[] = {
"ff0000",
"ff8200",
"f7df00",
"70ff00",
"00bd11",
"0000ff",
"800088",
"000000",
"ff00aa",
"00ffff",
};
constexpr const char* kZButtonPresets[] = {
"ff0000",
"ff8200",
"f7df00",
"70ff00",
"00bd11",
"800088",
"000000",
"00ffff",
};
constexpr const char* kChargeRingPresets[] = {
"ff9f9f",
"ff0000",
"ffff00",
"00ff00",
"0000ff",
"ff00ff",
"331900",
"feffff",
"000000",
};
ModResult register_str_option(
const char* name, const char* defaultValue, ConfigVarHandle& outHandle, ModError* error) {
ConfigVarDesc cvarDesc = CONFIG_VAR_DESC_INIT;
cvarDesc.name = name;
cvarDesc.type = CONFIG_VAR_STRING;
cvarDesc.default_string = defaultValue;
if (svc_config->register_var(mod_ctx, &cvarDesc, &outHandle) != MOD_OK) {
return mods::set_error(error, MOD_ERROR, "failed to register cosmetics option");
}
return MOD_OK;
}
ModResult register_int_option(
const char* name, int64_t defaultValue, ConfigVarHandle& outHandle, ModError* error) {
ConfigVarDesc cvarDesc = CONFIG_VAR_DESC_INIT;
cvarDesc.name = name;
cvarDesc.type = CONFIG_VAR_INT;
cvarDesc.default_int = defaultValue;
if (svc_config->register_var(mod_ctx, &cvarDesc, &outHandle) != MOD_OK) {
return mods::set_error(error, MOD_ERROR, "failed to register cosmetics option");
}
return MOD_OK;
}
void add_control(UiElementHandle pane, const UiControlDesc& desc) {
auto result = svc_ui->pane_add_control(mod_ctx, pane, &desc, nullptr);
if (result != MOD_OK) {
mods::log::debug("pane_add_control failed {}", static_cast<int>(result));
}
}
template <size_t N>
void add_cosmetic_option(
UiElementHandle pane, ConfigVarHandle cvar, const char* name, const char* const (&presets)[N]) {
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_COLOR;
control.label = name;
control.binding = UI_BINDING_CONFIG_VAR;
control.config_var = cvar;
control.color_presets = presets;
control.color_preset_count = N;
add_control(pane, control);
}
void add_midna_hair_option(UiElementHandle left, ConfigVarHandle cvar, const std::string& name) {
static const char* kMidnaHairOptions[] = {
"Default", "Pink", "Red", "Yellow", "Green", "Blue", "Purple", "Brown", "White", "Black"};
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_SELECT;
control.label = name.c_str();
control.help_rml = "Choose Midna's hair color.";
control.binding = UI_BINDING_CONFIG_VAR;
control.config_var = cvar;
control.options = kMidnaHairOptions;
control.option_count = 10;
add_control(left, control);
}
void add_group(
UiElementHandle groups, UiElementHandle colors, const char* label, UiGroupBuildFn build) {
UiGroupDesc group = UI_GROUP_DESC_INIT;
group.label = label;
group.build = build;
const auto result = svc_ui->pane_add_group(mod_ctx, groups, colors, &group, nullptr);
if (result != MOD_OK) {
mods::log::debug("pane_add_group failed {}", static_cast<int>(result));
}
}
ModResult build_hero_tunic_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Hero's Tunic");
add_cosmetic_option(pane, g_cvars.herosTunicCapColor, "Cap", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.herosTunicTorsoColor, "Body", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.herosTunicSkirtColor, "Skirt", kOverlayPresets);
return MOD_OK;
}
ModResult build_zora_armor_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Zora Armor");
add_cosmetic_option(pane, g_cvars.zoraArmorCapColor, "Cap", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorHelmetColor, "Helmet", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorTorsoColor, "Torso", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorScalesColor, "Scales", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.zoraArmorFlippersColor, "Flippers", kOverlayPresets);
return MOD_OK;
}
ModResult build_sword_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Swords");
add_cosmetic_option(pane, g_cvars.woodenSwordColor, "Wooden Sword", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.ordonSwordBladeColor, "Ordon Blade", kLightPresets);
add_cosmetic_option(pane, g_cvars.ordonSwordHandleColor, "Ordon Handle", kLightPresets);
add_cosmetic_option(pane, g_cvars.msBladeColor, "Master Sword Blade", kLightPresets);
add_cosmetic_option(pane, g_cvars.msHandleColor, "Master Sword Handle", kLightPresets);
add_cosmetic_option(
pane, g_cvars.lightSwordGlowColor, "Light Sword Glow", kRainbowLightPresets);
return MOD_OK;
}
ModResult build_equipment_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Equipment");
add_cosmetic_option(pane, g_cvars.lanternGlowColor, "Lantern Glow", kRainbowLightPresets);
add_cosmetic_option(pane, g_cvars.boomerangColor, "Gale Boomerang", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.ironBootsColor, "Iron Boots", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.spinnerColor, "Spinner", kOverlayPresets);
return MOD_OK;
}
ModResult build_button_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Buttons");
add_cosmetic_option(pane, g_cvars.aButtonColor, "A Button", kAButtonPresets);
add_cosmetic_option(pane, g_cvars.bButtonColor, "B Button", kBButtonPresets);
add_cosmetic_option(pane, g_cvars.xButtonColor, "X Button", kXyButtonPresets);
add_cosmetic_option(pane, g_cvars.yButtonColor, "Y Button", kXyButtonPresets);
add_cosmetic_option(pane, g_cvars.zButtonColor, "Z Button", kZButtonPresets);
return MOD_OK;
}
ModResult build_hud_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "HUD");
add_cosmetic_option(pane, g_cvars.heartColor, "Hearts", kBButtonPresets);
return MOD_OK;
}
ModResult build_link_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Link");
add_cosmetic_option(pane, g_cvars.linkHairColor, "Hair", kOverlayPresets);
return MOD_OK;
}
ModResult build_midna_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Midna");
add_cosmetic_option(pane, g_cvars.midnaChargeRingColor, "Charge Ring", kChargeRingPresets);
return MOD_OK;
}
ModResult build_companion_colors(ModContext*, UiElementHandle pane, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, pane, "Companions");
add_cosmetic_option(pane, g_cvars.wolfLinkColor, "Wolf Link", kOverlayPresets);
add_cosmetic_option(pane, g_cvars.eponaColor, "Epona", kOverlayPresets);
return MOD_OK;
}
ModResult build_equipment_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, left, "Color Groups");
add_group(left, right, "Hero's Tunic", build_hero_tunic_colors);
add_group(left, right, "Zora Armor", build_zora_armor_colors);
add_group(left, right, "Swords", build_sword_colors);
add_group(left, right, "Equipment", build_equipment_colors);
return MOD_OK;
}
ModResult build_ui_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, left, "Color Groups");
add_group(left, right, "Buttons", build_button_colors);
add_group(left, right, "HUD", build_hud_colors);
return MOD_OK;
}
ModResult build_misc_colors_tab(
ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) {
svc_ui->pane_add_section(mod_ctx, left, "Hair Presets");
add_midna_hair_option(left, g_cvars.midnaHairBaseColor, "Midna's Hair Base Color");
add_midna_hair_option(left, g_cvars.midnaHairTipsColor, "Midna's Hair Tips Color");
svc_ui->pane_add_section(mod_ctx, left, "Color Groups");
add_group(left, right, "Link", build_link_colors);
add_group(left, right, "Midna", build_midna_colors);
add_group(left, right, "Companions", build_companion_colors);
return MOD_OK;
}
void on_cosmetics_menu_window_closed(ModContext*, UiWindowHandle, void*) {
g_cosmeticsWindow = 0;
}
void on_open_cosmetics_menu(ModContext*, void*) {
if (g_cosmeticsWindow != 0) {
return;
}
UiTabDesc tabs[] = {UI_TAB_DESC_INIT, UI_TAB_DESC_INIT, UI_TAB_DESC_INIT};
tabs[0].title = "Equipment";
tabs[0].build = build_equipment_colors_tab;
tabs[1].title = "Interface";
tabs[1].build = build_ui_colors_tab;
tabs[2].title = "Characters";
tabs[2].build = build_misc_colors_tab;
UiWindowDesc desc = UI_WINDOW_DESC_INIT;
desc.tabs = tabs;
desc.tab_count = ARRAY_SIZE(tabs);
desc.on_closed = on_cosmetics_menu_window_closed;
if (svc_ui->window_push(mod_ctx, &desc, &g_cosmeticsWindow) != MOD_OK) {
svc_log->error(mod_ctx, "failed to open basic cosmetics window");
}
}
ModResult build_panel(ModContext*, UiElementHandle panel, void*, ModError*) {
UiControlDesc control = UI_CONTROL_DESC_INIT;
control.kind = UI_CONTROL_GROUP;
control.label = "Open Cosmetics Menu";
control.on_pressed = on_open_cosmetics_menu;
add_control(panel, control);
return MOD_OK;
}
} // namespace
void load_base_texture_data() {
// Go through each texture we can recolor and attempt to load and store
// the texture data for future recoloring
for (auto& replacements : get_texture_replacements() | std::views::values) {
for (auto& replacement : replacements) {
// If we've already loaded the texture data, don't load it again
if (replacement.loadedTextureData) {
continue;
}
// Avoid noisy resource lookups until the archive has finished loading.
auto* resInfo = dComIfG_getObjectResInfo(replacement.arc);
if (resInfo == nullptr || resInfo->getArchive() == nullptr) {
continue;
}
// Try to get the model this texture is part of. If we can't get it, try again later
auto model = static_cast<J3DModelData*>(
dComIfG_getObjectRes(replacement.arc, replacement.modelFileName));
if (model == nullptr) {
continue;
}
J3DTexture* tex = model->getTexture();
JUTNameTab* nametable = model->getTextureName();
if (tex != nullptr && nametable != nullptr) {
for (u16 i = 0; i < tex->getNum(); i++) {
const char* texName = nametable->getName(i);
if (texName != nullptr && std::strcmp(texName, replacement.textureName) == 0) {
// Once we've found the texture, set all our TextureKey and TextureData
// fields that we can set right now.
auto imageHeader = tex->getResTIMG(i);
auto& key = replacement.key;
auto& data = replacement.data;
key.kind = TEXTURE_KEY_SOURCE;
key.has_tlut = imageHeader->numColors > 0;
key.width = imageHeader->width;
key.height = imageHeader->height;
key.gx_format = imageHeader->format;
// Currently, no replaced textures have a tlut
key.tlut_hash = replacement.tlutHash;
// Calculate the size of the image data
const uint32_t mipCount =
imageHeader->mipmapEnabled ?
std::max<uint32_t>(imageHeader->mipmapCount, 1) :
1;
auto size = get_image_data_size(
imageHeader->format, imageHeader->width, imageHeader->height, mipCount);
replacement.baseTextureData.resize(size);
std::memcpy(
replacement.baseTextureData.data(), tex->getImgDataPtr(i), size);
// Source texture keys hash only the base mip level.
const auto baseMipSize = get_image_data_size(
imageHeader->format, imageHeader->width, imageHeader->height, 1);
auto textureHash =
XXH64(replacement.baseTextureData.data(), baseMipSize, 0);
replacement.key.texture_hash = textureHash;
mods::log::debug("Loaded base texture data for {}. size: {:X} hash: {:X}",
replacement.textureName, size, textureHash);
replacement.loadedTextureData = true;
data.width = imageHeader->width;
data.height = imageHeader->height;
data.mip_count = mipCount;
data.size = size;
data.gx_format = imageHeader->format;
break;
}
}
}
}
}
// If we've loaded all base textures for recoloring, then we don't need to call this function
// again
g_loadedAllBaseTextures = std::ranges::all_of(
get_texture_replacements() | std::views::values, [](auto& replacementList) {
return std::ranges::all_of(
replacementList, [](const TextureReplacementData& replacement) {
return replacement.loadedTextureData;
});
});
}
ModResult check_and_set_recolored_textures() {
for (auto& [configVar, replacements] : get_texture_replacements()) {
// If the configvar hasn't been set, don't continue
if (configVar == 0) {
continue;
}
auto maybeColor = get_config_var_color(configVar);
if (!maybeColor.has_value()) {
// An empty or invalid option means the original texture should be restored.
for (auto& replacement : replacements) {
if (replacement.handle != 0) {
auto result = svc_texture->unregister(mod_ctx, replacement.handle);
if (result != MOD_OK) {
mods::log::debug("Could not unregister replacement for {}. Result: {}",
replacement.textureName, static_cast<int>(result));
}
replacement.handle = 0;
}
replacement.curColor.reset();
}
continue;
}
auto color = maybeColor.value();
for (auto& replacement : replacements) {
// If we haven't loaded the base texture yet, don't try to recolor it
if (!replacement.loadedTextureData) {
continue;
}
// If our color hasn't changed, don't try to recolor
auto& curColor = replacement.curColor;
if (curColor == std::nullopt || curColor.value().r != color.r ||
curColor.value().g != color.g || curColor.value().b != color.b)
{
// Make a copy of the base texture data to recolor
auto newTexture = replacement.baseTextureData;
recolor_texture(replacement, color, newTexture);
TextureData newTextureData = replacement.data;
newTextureData.data = newTexture.data();
newTextureData.size = newTexture.size();
// Keep the current replacement active if registering the new one fails.
TextureReplacementHandle newHandle{};
auto result = svc_texture->register_data(
mod_ctx, &replacement.key, &newTextureData, &newHandle);
if (result != MOD_OK) {
mods::log::debug("Could not register_data for {}. Result: {}",
replacement.textureName, static_cast<int>(result));
} else {
mods::log::debug("Registered replacement for {}.", replacement.textureName,
static_cast<int>(result));
auto oldHandle = replacement.handle;
replacement.handle = newHandle;
curColor = color;
if (oldHandle != 0) {
result = svc_texture->unregister(mod_ctx, oldHandle);
if (result != MOD_OK) {
mods::log::debug(
"Could not unregister previous replacement for {}. Result: {}",
replacement.textureName, static_cast<int>(result));
}
}
}
}
}
}
return MOD_OK;
}
void unregister_all_texture_handles() {
for (auto& replacements : get_texture_replacements() | std::views::values) {
for (auto& replacement : replacements) {
if (replacement.handle != 0) {
svc_texture->unregister(mod_ctx, replacement.handle);
replacement.handle = 0;
}
replacement.curColor.reset();
}
}
}
#define REGISTER_COSMETIC_OPTION(option) \
result = register_str_option(#option, NULL, g_cvars.option, error); \
if (result != MOD_OK) { \
return result; \
}
extern "C" {
MOD_EXPORT ModResult mod_initialize(ModError* error) {
svc_log->info(mod_ctx, "basic_cosmetics_mod initialized");
ModResult result{};
REGISTER_COSMETIC_OPTION(herosTunicCapColor)
REGISTER_COSMETIC_OPTION(herosTunicTorsoColor)
REGISTER_COSMETIC_OPTION(herosTunicSkirtColor)
REGISTER_COSMETIC_OPTION(zoraArmorCapColor)
REGISTER_COSMETIC_OPTION(zoraArmorHelmetColor)
REGISTER_COSMETIC_OPTION(zoraArmorTorsoColor)
REGISTER_COSMETIC_OPTION(zoraArmorScalesColor)
REGISTER_COSMETIC_OPTION(zoraArmorFlippersColor)
REGISTER_COSMETIC_OPTION(lanternGlowColor)
REGISTER_COSMETIC_OPTION(woodenSwordColor)
REGISTER_COSMETIC_OPTION(ordonSwordBladeColor)
REGISTER_COSMETIC_OPTION(ordonSwordHandleColor)
REGISTER_COSMETIC_OPTION(msBladeColor)
REGISTER_COSMETIC_OPTION(msHandleColor)
REGISTER_COSMETIC_OPTION(lightSwordGlowColor)
REGISTER_COSMETIC_OPTION(boomerangColor)
REGISTER_COSMETIC_OPTION(ironBootsColor)
REGISTER_COSMETIC_OPTION(spinnerColor)
REGISTER_COSMETIC_OPTION(aButtonColor)
REGISTER_COSMETIC_OPTION(bButtonColor)
REGISTER_COSMETIC_OPTION(xButtonColor)
REGISTER_COSMETIC_OPTION(yButtonColor)
REGISTER_COSMETIC_OPTION(zButtonColor)
REGISTER_COSMETIC_OPTION(heartColor)
result = register_int_option("midnaHairBaseColor", 0, g_cvars.midnaHairBaseColor, error);
if (result != MOD_OK) {
return result;
}
result = register_int_option("midnaHairTipsColor", 0, g_cvars.midnaHairTipsColor, error);
if (result != MOD_OK) {
return result;
}
REGISTER_COSMETIC_OPTION(midnaChargeRingColor)
REGISTER_COSMETIC_OPTION(linkHairColor)
REGISTER_COSMETIC_OPTION(wolfLinkColor)
REGISTER_COSMETIC_OPTION(eponaColor)
UiModsPanelDesc panelDesc = UI_MODS_PANEL_DESC_INIT;
panelDesc.build = build_panel;
svc_ui->register_mods_panel(mod_ctx, &panelDesc);
// Add all our hooks
result = add_all_hooks();
if (result != MOD_OK) {
return result;
}
g_loadedAllBaseTextures = false;
g_textureLoadRetryCountdown = 0;
return MOD_OK;
}
MOD_EXPORT ModResult mod_update(ModError*) {
update_rainbow_rgb(1.0f);
set_all_midna_hair_colors();
if (!g_loadedAllBaseTextures) {
if (g_textureLoadRetryCountdown == 0) {
load_base_texture_data();
g_textureLoadRetryCountdown = kTextureLoadRetryFrames;
} else {
--g_textureLoadRetryCountdown;
}
}
check_and_set_recolored_textures();
return MOD_OK;
}
MOD_EXPORT ModResult mod_shutdown(ModError*) {
svc_log->info(mod_ctx, "basic_cosmetics_mod unloaded");
g_cosmeticsWindow = 0;
remove_all_hooks();
unregister_all_texture_handles();
get_texture_replacements().clear();
g_loadedAllBaseTextures = false;
g_textureLoadRetryCountdown = 0;
return MOD_OK;
}
}
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#include "mods/svc/config.h"
#include "mods/svc/texture.h"
#include <gx.h>
#include <optional>
#include <string>
#include <vector>
struct cvars {
ConfigVarHandle herosTunicCapColor = 0;
ConfigVarHandle herosTunicTorsoColor = 0;
ConfigVarHandle herosTunicSkirtColor = 0;
ConfigVarHandle zoraArmorCapColor = 0;
ConfigVarHandle zoraArmorHelmetColor = 0;
ConfigVarHandle zoraArmorTorsoColor = 0;
ConfigVarHandle zoraArmorScalesColor = 0;
ConfigVarHandle zoraArmorFlippersColor = 0;
ConfigVarHandle lanternGlowColor = 0;
ConfigVarHandle woodenSwordColor = 0;
ConfigVarHandle ordonSwordBladeColor = 0;
ConfigVarHandle ordonSwordHandleColor = 0;
ConfigVarHandle msBladeColor = 0;
ConfigVarHandle msHandleColor = 0;
ConfigVarHandle lightSwordGlowColor = 0;
ConfigVarHandle boomerangColor = 0;
ConfigVarHandle ironBootsColor = 0;
ConfigVarHandle spinnerColor = 0;
ConfigVarHandle heartColor = 0;
ConfigVarHandle aButtonColor = 0;
ConfigVarHandle bButtonColor = 0;
ConfigVarHandle xButtonColor = 0;
ConfigVarHandle yButtonColor = 0;
ConfigVarHandle zButtonColor = 0;
ConfigVarHandle midnaHairBaseColor = 0;
ConfigVarHandle midnaHairTipsColor = 0;
ConfigVarHandle midnaChargeRingColor = 0;
ConfigVarHandle linkHairColor = 0;
ConfigVarHandle wolfLinkColor = 0;
ConfigVarHandle eponaColor = 0;
};
struct TextureReplacementData {
const char* arc{};
const char* modelFileName{};
const char* textureName{};
uint64_t textureHash{};
uint64_t tlutHash{};
TextureKey key = TEXTURE_KEY_INIT;
TextureData data = TEXTURE_DATA_INIT;
TextureReplacementHandle handle{};
std::vector<u8> baseTextureData{};
bool loadedTextureData = false;
std::optional<GXColor> curColor{};
};
cvars& get_cvars();
std::string get_str_option(ConfigVarHandle handle, const std::string& fallback);
int64_t get_int_option(ConfigVarHandle handle, int64_t fallback);
std::optional<GXColor> get_config_var_color(ConfigVarHandle handle, bool allowRainbow = false);
@@ -0,0 +1,682 @@
#include "texture_utils.hpp"
#include "color_utils.hpp"
#include "mod.hpp"
#include "mods/svc/config.h"
#include "mods/svc/log.hpp"
#include "JSystem/J3DGraphLoader/J3DModelLoader.h"
#include "JSystem/JSupport/JSupport.h"
#include "JSystem/JUtility/JUTNameTab.h"
#include "d/actor/d_a_alink.h"
static void get_gx_tile_info(uint8_t format, uint32_t& tileWidth, uint32_t& tileHeight, uint32_t& tileSize) {
switch (format) {
case GX_TF_I8:
case GX_TF_IA4:
case GX_TF_C8:
tileWidth = 8; tileHeight = 4; tileSize = 32;
break;
case GX_TF_IA8:
case GX_TF_RGB565:
case GX_TF_RGB5A3:
case GX_TF_C14X2:
tileWidth = 4; tileHeight = 4; tileSize = 32;
break;
case GX_TF_RGBA8:
tileWidth = 4; tileHeight = 4; tileSize = 64;
break;
case GX_TF_I4:
case GX_TF_C4:
case GX_TF_CMPR:
default:
tileWidth = 8; tileHeight = 8; tileSize = 32;
break;
}
}
uint32_t get_image_data_size(uint32_t format, uint32_t width, uint32_t height, uint32_t mipmapCount) {
uint32_t tileWidth, tileHeight, tileSize;
get_gx_tile_info(format, tileWidth, tileHeight, tileSize);
uint32_t totalSize = 0;
for (uint8_t i = 0; i < mipmapCount; ++i) {
// Round dimensions up to nearest tile boundary
uint32_t paddedWidth = (width + tileWidth - 1) & ~(tileWidth - 1);
uint32_t paddedHeight = (height + tileHeight - 1) & ~(tileHeight - 1);
uint32_t tilesX = paddedWidth / tileWidth;
uint32_t tilesY = paddedHeight / tileHeight;
totalSize += tilesX * tilesY * tileSize;
// Downscale dimensions for next mipmap level
width = std::max(1u, width >> 1);
height = std::max(1u, height >> 1);
}
return totalSize;
}
// When left is greater than right
// 0b00 points to the left color
// 0b01 points to the right color
// 0b10 is closer to left color
// 0b11 is closer to right color
// When left is not greater than right
// 0b00 points to the left color
// 0b01 points to the right color
// 0b10 is midway between the colors
// 0b11 is transparent
// That means when maintaining the relative order, if we have to swap the colors:
// in the case of left being greater than right:
// 0b00 will swap to 0b01
// 0b01 will swap to 0b00
// 0b10 will swap to 0b11
// 0b11 will swap to 0b10
// So the left bit stays the same, and the right bit changes
// Can do xor (^) like 0b01010101 or 0x55 for each u16
// in the case of left not being greater than right:
// 0b00 will swap to 0b01
// 0b01 will swap to 0b00
// 0b10 will stay the same
// 0b11 will stay the same
// so if the left bit is a 0, the right bit will change
uint32_t swap_index_bits(bool leftIsGreater, uint32_t bits) {
if (leftIsGreater) {
return bits ^ 0x55555555;
}
const uint32_t mask = ((bits >> 1) & 0x55555555) ^ 0x55555555;
return bits ^ mask;
}
void recolor_cmpr_texture(const TextureReplacementData& replacementData, const GXColor color, std::vector<u8>& newTextureDataOut)
{
uint16_t recolors[0x100];
for (int32_t i = 0; i < 0x100; i++) {
recolors[i] = blend_overlay_rgb_565(i, color);
}
const uint8_t mipCount = (replacementData.data.mip_count > 0) ? replacementData.data.mip_count : 1;
uint32_t mipWidth = replacementData.key.width;
uint32_t mipHeight = replacementData.key.height;
uint8_t* currentAddr = newTextureDataOut.data();
for (uint8_t mip = 0; mip < mipCount; ++mip) {
// Round dimensions up to the nearest 8x8 tile boundary
const uint32_t roundedWidth = (mipWidth + 7) & ~7;
const uint32_t roundedHeight = (mipHeight + 7) & ~7;
const uint32_t numBlocks = (roundedWidth / 8) * (roundedHeight / 8);
const uint32_t iterations = numBlocks * 4; // 4 CMPR sub-blocks per 8x8 tile
for (uint32_t i = 0; i < iterations; i++) {
auto* rgb565Ptr = reinterpret_cast<BE<uint16_t>*>(currentAddr);
auto leftRgb565 = rgb565Ptr[0];
auto rightRgb565 = rgb565Ptr[1];
const bool leftIsGreater = leftRgb565 > rightRgb565;
const uint32_t leftGrayVal = desaturate_rgb_565(leftRgb565);
const uint32_t rightGrayVal = desaturate_rgb_565(rightRgb565);
uint16_t leftNewRgb565 = recolors[leftGrayVal];
uint16_t rightNewRgb565 = recolors[rightGrayVal];
bool needsBitSwap = false;
if (leftIsGreater) {
if (leftNewRgb565 == rightNewRgb565) {
// Need to make sure that subtracting 1 does not mess
// everything up. For example, 0x1000 - 1 => 0x0fff which is
// a completely different color.
if ((leftNewRgb565 & 0x1f) == 0) {
// If left value has 0 blue, we change its blue to 1.
leftNewRgb565 += 1;
}
rightNewRgb565 = leftNewRgb565 - 1;
}
else if (leftNewRgb565 < rightNewRgb565) {
needsBitSwap = true;
}
}
else if (leftNewRgb565 > rightNewRgb565) {
needsBitSwap = true;
}
if (needsBitSwap) {
// The left and right colors are swapping so that their values
// are relative in the same way. We need to update the bits
// referencing the palette entries to handle the swap.
const uint16_t temp = leftNewRgb565;
leftNewRgb565 = rightNewRgb565;
rightNewRgb565 = temp;
auto wordPtr = reinterpret_cast<BE<uint32_t>*>(currentAddr);
const uint32_t bits = wordPtr[1];
const uint32_t newBits = swap_index_bits(leftIsGreater, bits);
wordPtr[1] = newBits;
}
rgb565Ptr[0] = leftNewRgb565;
rgb565Ptr[1] = rightNewRgb565;
currentAddr += 8;
}
// Halve dimensions for the next mipmap level
mipWidth = std::max(1u, mipWidth >> 1);
mipHeight = std::max(1u, mipHeight >> 1);
}
}
void recolor_rgb5a3_texture(const TextureReplacementData& replacementData, const GXColor color, std::vector<u8>& newTextureDataOut)
{
// Precompute lookup tables for both RGB555 (opaque) and RGB444 (translucent) modes
uint16_t recolors_rgb555[0x100];
uint16_t recolors_rgb444[0x100];
for (int32_t i = 0; i < 0x100; i++) {
const uint8_t r = blend_overlay_channel(i, color.r);
const uint8_t g = blend_overlay_channel(i, color.g);
const uint8_t b = blend_overlay_channel(i, color.b);
// Pack as RGB555: Bit 15 set to 1 + 5 bits R, G, B
recolors_rgb555[i] = 0x8000 | ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
// Pack as RGB444: 4 bits R, G, B (Bit 15 remains 0)
recolors_rgb444[i] = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4);
}
const uint8_t mipCount =
replacementData.data.mip_count > 0 ? replacementData.data.mip_count : 1;
uint32_t mipWidth = replacementData.key.width;
uint32_t mipHeight = replacementData.key.height;
auto* pixelPtr = reinterpret_cast<BE<uint16_t>*>(newTextureDataOut.data());
for (uint8_t mip = 0; mip < mipCount; ++mip) {
const uint32_t roundedWidth = (mipWidth + 3) & ~3;
const uint32_t roundedHeight = (mipHeight + 3) & ~3;
const uint32_t totalPixels = roundedWidth * roundedHeight;
for (uint32_t i = 0; i < totalPixels; i++) {
const uint16_t rawPixel = pixelPtr[i];
// MSB determines if pixel is opaque or translucent
if (rawPixel & 0x8000) {
// Pixel is opaque
const uint8_t r5 = (rawPixel >> 10) & 0x1F;
const uint8_t g5 = (rawPixel >> 5) & 0x1F;
const uint8_t b5 = rawPixel & 0x1F;
// Expand 5-bit to 8-bit
const uint8_t r8 = (r5 << 3) | (r5 >> 2);
const uint8_t g8 = (g5 << 3) | (g5 >> 2);
const uint8_t b8 = (b5 << 3) | (b5 >> 2);
const uint8_t grayVal = static_cast<uint8_t>((r8 * 77 + g8 * 150 + b8 * 29) >> 8);
pixelPtr[i] = recolors_rgb555[grayVal];
} else {
// Pixel is translucent
const uint16_t alpha3 = rawPixel & 0x7000;
const uint8_t r4 = (rawPixel >> 8) & 0x0F;
const uint8_t g4 = (rawPixel >> 4) & 0x0F;
const uint8_t b4 = rawPixel & 0x0F;
// Expand 4-bit to 8-bit
const uint8_t r8 = (r4 << 4) | r4;
const uint8_t g8 = (g4 << 4) | g4;
const uint8_t b8 = (b4 << 4) | b4;
const uint8_t grayVal = static_cast<uint8_t>((r8 * 77 + g8 * 150 + b8 * 29) >> 8);
// Combine original alpha with recolored RGB444
pixelPtr[i] = alpha3 | recolors_rgb444[grayVal];
}
}
pixelPtr += totalPixels;
mipWidth = std::max(1u, mipWidth >> 1);
mipHeight = std::max(1u, mipHeight >> 1);
}
}
// Function to encode a single 4x4 sub-block (16 pixels) into an 8-byte CMPR block
static void encode_cmpr_sub_block(uint8_t* dst, const uint8_t pixels[16]) {
uint8_t min_val = 255;
uint8_t max_val = 0;
for (int i = 0; i < 16; ++i) {
if (pixels[i] < min_val) min_val = pixels[i];
if (pixels[i] > max_val) max_val = pixels[i];
}
auto intensity_to_rgb565 = [](uint8_t val) -> uint16_t {
uint16_t r5 = val >> 3;
uint16_t g6 = val >> 2;
uint16_t b5 = val >> 3;
return static_cast<uint16_t>((r5 << 11) | (g6 << 5) | b5);
};
uint16_t c0_565 = intensity_to_rgb565(max_val);
uint16_t c1_565 = intensity_to_rgb565(min_val);
uint32_t indices = 0;
if (max_val > min_val) {
// Enforce c0_565 > c1_565 in unsigned 16-bit representation to use 4-color mode
if (c0_565 == c1_565) {
if ((c0_565 & 0x001F) < 0x001F) {
c0_565 += 1;
} else {
c1_565 -= 1;
}
}
// Interpolated 8-bit intensity values for quantization
const int c0 = max_val;
const int c1 = min_val;
const int c2 = (2 * max_val + min_val) / 3;
const int c3 = (max_val + 2 * min_val) / 3;
// Map each pixel to the nearest palette entry
for (int i = 0; i < 16; ++i) {
const int p = pixels[i];
const int d0 = std::abs(p - c0);
const int d1 = std::abs(p - c1);
const int d2 = std::abs(p - c2);
const int d3 = std::abs(p - c3);
uint32_t best_idx = 0;
int min_d = d0;
if (d1 < min_d) { min_d = d1; best_idx = 1; }
if (d2 < min_d) { min_d = d2; best_idx = 2; }
if (d3 < min_d) { min_d = d3; best_idx = 3; }
indices |= (best_idx << (30 - (2 * i)));
}
}
// Account for big endian data expectation
dst[0] = static_cast<uint8_t>(c0_565 >> 8);
dst[1] = static_cast<uint8_t>(c0_565 & 0xFF);
dst[2] = static_cast<uint8_t>(c1_565 >> 8);
dst[3] = static_cast<uint8_t>(c1_565 & 0xFF);
dst[4] = static_cast<uint8_t>(indices >> 24);
dst[5] = static_cast<uint8_t>((indices >> 16) & 0xFF);
dst[6] = static_cast<uint8_t>((indices >> 8) & 0xFF);
dst[7] = static_cast<uint8_t>(indices & 0xFF);
}
bool convert_i8_to_cmpr(TextureReplacementData& replacementData, std::vector<u8>& cmprOut) {
if (cmprOut.empty()) {
return false;
}
const uint8_t mipCount = (replacementData.data.mip_count > 0) ? replacementData.data.mip_count : 1;
const auto expectedInputSize = get_image_data_size(
GX_TF_I8, replacementData.key.width, replacementData.key.height, mipCount);
if (cmprOut.size() < expectedInputSize) {
return false;
}
const auto sourceData = cmprOut;
std::vector<u8> convertedData(get_image_data_size(
GX_TF_CMPR, replacementData.key.width, replacementData.key.height, mipCount));
uint32_t mipWidth = replacementData.key.width;
uint32_t mipHeight = replacementData.key.height;
const uint8_t* readPtr = sourceData.data();
uint8_t* writePtr = convertedData.data();
for (uint8_t mip = 0; mip < mipCount; ++mip) {
const uint32_t paddedWidthI8 = (mipWidth + 7) & ~7;
const uint32_t paddedHeightI8 = (mipHeight + 3) & ~3;
const uint32_t tilesX_I8 = paddedWidthI8 / 8;
const uint32_t paddedWidthCMPR = (mipWidth + 7) & ~7;
const uint32_t paddedHeightCMPR = (mipHeight + 7) & ~7;
const uint32_t blocksX_CMPR = paddedWidthCMPR / 8;
const uint32_t blocksY_CMPR = paddedHeightCMPR / 8;
for (uint32_t by = 0; by < blocksY_CMPR; ++by) {
for (uint32_t bx = 0; bx < blocksX_CMPR; ++bx) {
uint8_t subBlockPixels[4][16]{};
for (uint32_t subBlockY = 0; subBlockY < 2; ++subBlockY) {
for (uint32_t subBlockX = 0; subBlockX < 2; ++subBlockX) {
const uint32_t subBlock = subBlockY * 2 + subBlockX;
for (uint32_t row = 0; row < 4; ++row) {
for (uint32_t col = 0; col < 4; ++col) {
const uint32_t x = bx * 8 + subBlockX * 4 + col;
const uint32_t y = by * 8 + subBlockY * 4 + row;
if (x >= mipWidth || y >= mipHeight) {
continue;
}
const uint32_t tile = (y / 4) * tilesX_I8 + (x / 8);
const uint32_t tileOffset = (y % 4) * 8 + (x % 8);
subBlockPixels[subBlock][row * 4 + col] =
readPtr[tile * 32 + tileOffset];
}
}
}
}
for (const auto& subBlockPixel : subBlockPixels) {
encode_cmpr_sub_block(writePtr, subBlockPixel);
writePtr += 8;
}
}
}
const uint32_t tilesY_I8 = paddedHeightI8 / 4;
readPtr += tilesX_I8 * tilesY_I8 * 32;
mipWidth = std::max(1u, mipWidth >> 1);
mipHeight = std::max(1u, mipHeight >> 1);
}
cmprOut.swap(convertedData);
// Update format
replacementData.data.gx_format = GX_TF_CMPR;
return true;
}
void recolor_texture(TextureReplacementData& replacementData, GXColor color, std::vector<u8>& newTextureDataOut) {
switch (replacementData.key.gx_format) {
case GX_TF_CMPR:
recolor_cmpr_texture(replacementData, color, newTextureDataOut);
break;
case GX_TF_RGB5A3:
recolor_rgb5a3_texture(replacementData, color, newTextureDataOut);
break;
case GX_TF_I8:
if (convert_i8_to_cmpr(replacementData, newTextureDataOut)) {
recolor_cmpr_texture(replacementData, color, newTextureDataOut);
} else {
mods::log::debug("Could not convert {} from i8 to cmpr", replacementData.textureName);
}
break;
default:
break;
}
}
std::unordered_map<ConfigVarHandle, std::list<TextureReplacementData>>& get_texture_replacements() {
static std::unordered_map<ConfigVarHandle, std::list<TextureReplacementData>> replacements{};
if (replacements.empty()) {
replacements = {
{get_cvars().herosTunicCapColor, {
{
.arc = "Kmdl",
.modelFileName = "al_head.bmd",
.textureName = "al_cap",
}
}},
{get_cvars().herosTunicTorsoColor, {
{
.arc = "Kmdl",
.modelFileName = "al.bmd",
.textureName = "al_upbody",
}
}},
{get_cvars().herosTunicSkirtColor, {
{
.arc = "Kmdl",
.modelFileName = "al.bmd",
.textureName = "al_lowbody",
}
}},
{get_cvars().zoraArmorCapColor, {
{
.arc = "Zmdl",
.modelFileName = "zl_head.bmd",
.textureName = "zl_cap",
}
}},
{get_cvars().zoraArmorHelmetColor, {
{
.arc = "Zmdl",
.modelFileName = "zl_head.bmd",
.textureName = "zl_helmet",
}
}},
{get_cvars().zoraArmorTorsoColor, {
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_armor",
},
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_armL",
}
}},
{get_cvars().zoraArmorScalesColor, {
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_body",
}
}},
{get_cvars().zoraArmorFlippersColor, {
{
.arc = "Zmdl",
.modelFileName = "zl.bmd",
.textureName = "zl_boots",
}
}},
{get_cvars().woodenSwordColor, {
{
.arc = "Bmdl", // Ordon Clothes Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "Kmdl", // Hero's Tunic Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "Zmdl", // Zora Armor Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "Mmdl", // Magic Armor Model
.modelFileName = "al_swb.bmd",
.textureName = "al_SWB",
},
{
.arc = "O_gD_SWB", // Get Item Model
.modelFileName = "o_gd_al_swb.bmd",
.textureName = "al_SWB",
}
}},
{get_cvars().ordonSwordHandleColor, {
{
.arc = "Alink",
.modelFileName = "al_swa.bmd",
.textureName = "al_SWgripA",
},
{
.arc = "O_gD_SWA", // Get Item Model
.modelFileName = "o_gd_al_swa.bmd",
.textureName = "al_SWgripA",
}
}},
{get_cvars().ordonSwordBladeColor, {
{
.arc = "Alink",
.modelFileName = "al_swa.bmd",
.textureName = "al_SWA",
}
}},
{get_cvars().msHandleColor, {
{
.arc = "Alink",
.modelFileName = "al_swm.bmd",
.textureName = "al_SWgripM",
}
}},
{get_cvars().msBladeColor, {
{
.arc = "Alink",
.modelFileName = "al_swm.bmd",
.textureName = "al_SWM",
}
}},
{get_cvars().boomerangColor, {
{
.arc = "Alink", // Boomerang in Link's hand
.modelFileName = "al_boom.bmd",
.textureName = "L_al_boom00",
},
{
.arc = "E_mk", // Boomerang in Ook's hand
.modelFileName = "bm.bmd",
.textureName = "L_al_boom00",
},
{
.arc = "E_mk", // Boomerang in Ook's hand
.modelFileName = "bm.bmd",
.textureName = "bm_boom",
},
{
.arc = "O_gD_boom", // Get Item Model
.modelFileName = "o_gd_boom.bmd",
.textureName = "L_al_boom00",
}
}},
{get_cvars().ironBootsColor, {
{
.arc = "Bmdl", // Ordon Clothes Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "Kmdl", // Hero's Tunic Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "Zmdl", // Zora Armor Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "Mmdl", // Magic Armor Model
.modelFileName = "al_bootsh.bmd",
.textureName = "al_bootsH",
},
{
.arc = "O_gD_boot", // Get Item Model
.modelFileName = "o_gd_al_bootsh.bmd",
.textureName = "al_bootsH",
}
}},
{get_cvars().spinnerColor, {
{
.arc = "Alink", // Spinner used by Link
.modelFileName = "al_sp.bmd",
.textureName = "al_SP",
},
{
.arc = "O_gD_SP", // Get Item Model
.modelFileName = "o_gd_al_sp.bmd",
.textureName = "al_SP",
}
}},
{get_cvars().linkHairColor, {
{
.arc = "Bmdl", // Ordon Clothes Model
.modelFileName = "bl_head.bmd",
.textureName = "bl_hair",
},
{
.arc = "Kmdl", // Hero's Tunic Model
.modelFileName = "al_head.bmd",
.textureName = "al_hair",
},
{
.arc = "Mmdl", // Magic Armor Model
.modelFileName = "ml_head.bmd",
.textureName = "al_hair",
}
}},
{get_cvars().wolfLinkColor, {
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_body",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.1",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.2",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.3",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.4",
},
{
.arc = "Wmdl",
.modelFileName = "wl.bmd",
.textureName = "wl_eye.5",
}
}},
{get_cvars().eponaColor, {
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_body",
},
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_eye.1",
},
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_eye.2",
},
{
.arc = "Horse",
.modelFileName = "hs.bmd",
.textureName = "hs_eye.3",
},
}},
};
}
return replacements;
}
@@ -0,0 +1,21 @@
#pragma once
/**
* File originally copied from console TPR with permission from isaac
* https://github.com/zsrtp/libtp_rel/blob/master/include/util/texture_utils.h
*/
#include "mod.hpp"
#include <gx.h>
#include <list>
#include <unordered_map>
uint32_t get_image_data_size(
uint32_t format, uint32_t width, uint32_t height, uint32_t mipmapCount);
void recolor_texture(
TextureReplacementData& replacementData, GXColor color, std::vector<u8>& newTextureDataOut);
std::unordered_map<ConfigVarHandle, std::list<TextureReplacementData>>& get_texture_replacements();

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