From f967268632dc7186fdf13d499b8088e651928bd6 Mon Sep 17 00:00:00 2001 From: Pheenoh Date: Fri, 27 Sep 2024 19:27:02 -0600 Subject: [PATCH] 1.1.0 (#86) * memfile fix attempt * memfile fix * update pr branch * readd crash address for pr tests * fix other refs * copy pasta * more granular actor pos/angle control * simplify menu closing logic, add proc id to actor list * migrate checkers to their own menu * minor cleanup * more actor view optimizations * skip startup logos * rm extra mappings in tools menu * lfc checker wip * use new docker image(s) * tell dme about the env it's running in * crash test * fix: Crash test at the end of main * removed redundant declaration * rm crash test * actor view address, update button combo checks for pause/delete * skip wii intros * rm extra testing * remove counters from save manager * use fpcEx_Search * very basic sound test menu * add set scent option in pause menu * rm debug logging * ee checker * golden bugs menu * allow setting of charlo donation * add leever range viewer * add funraising flags, sort rupee flags into their own menu * sort pause menu into sub menus * sort tools menu into sub menus * intro cs flag WIP * fix actor spawner * add coro/rusl td * update hundo saves, add stage info display * updating (simplifying) docker config * added wii BiTE saves * Update README.md Removed comment about the old patcher using a lot of memory. * account for dScnLogo_c size difference on PAL * rm wii fade on warning menu * use same class name for dScnLogo_c * add missing enum * keep fade function * rm intro cs flag feature for now * fix bombs * update release automation * fixed wii nand writing --------- Co-authored-by: kipcode66 Co-authored-by: TakaRikka Co-authored-by: kipcode66 --- .devcontainer/Dockerfile | 23 +- .devcontainer/devcontainer.json | 45 +- .github/workflows/multi-region-tests.yml | 6 +- .github/workflows/tpgz-release.yml | 7 + CMakeLists.txt | 2 +- README.md | 4 +- RomHack.toml.in | 4 +- external/gcn_c/include/nand.h | 4 + external/gcn_c/include/storage.h | 48 +- .../include/JSystem/JAudio2/JAISound.h | 27 +- .../include/SSystem/SComponent/c_phase.h | 22 + .../libtp_c/include/Z2AudioLib/Z2AudioMgr.h | 23 +- .../libtp_c/include/d/com/d_com_inf_game.h | 36 + external/libtp_c/include/d/d_stage.h | 2 + .../libtp_c/include/d/meter/d_meter2_info.h | 3 +- external/libtp_c/include/d/s/d_s_logo.h | 152 ++ external/libtp_c/include/d/save/d_save.h | 4 + .../libtp_c/include/f_op/f_op_actor_mng.h | 3 + external/libtp_c/include/f_op/f_op_scene.h | 22 + .../libtp_c/include/f_op/f_op_scene_tag.h | 12 + .../libtp_c/include/f_pc/f_pc_create_req.h | 31 + .../libtp_c/include/f_pc/f_pc_create_tag.h | 14 + external/libtp_c/include/f_pc/f_pc_executor.h | 19 + external/libtp_c/include/f_pc/f_pc_layer.h | 3 + .../libtp_c/include/f_pc/f_pc_layer_iter.h | 21 + .../libtp_c/include/f_pc/f_pc_line_iter.h | 9 + external/libtp_c/include/f_pc/f_pc_manager.h | 39 +- external/libtp_c/include/f_pc/f_pc_node.h | 19 + .../libtp_c/include/f_pc/f_pc_stdcreate_req.h | 22 + external/libtp_c/include/m_Do/m_Do_audio.h | 11 +- external/libtp_c/include/utils.h | 9 + external/libtp_c/src/utils.cpp | 21 +- external/misc/any.py | 34 +- external/misc/anyb.py | 517 +++-- external/misc/dat2qlogs.py | 28 + external/misc/gci2qlogs.py | 28 + external/misc/hundo.py | 523 ++--- external/misc/meta.py | 49 + external/misc/nandpack.py | 1788 +++++++++++++++++ modules/boot/include/gz_flags.h | 2 + modules/boot/include/menus/menu.h | 12 + modules/boot/include/modules.h | 2 + modules/boot/include/pos_settings.h | 3 +- modules/boot/include/practice.h | 2 - modules/boot/include/save_manager.h | 3 - modules/boot/include/save_specials.h | 5 + modules/boot/include/scene.h | 1 + modules/boot/include/settings.h | 5 + modules/boot/include/tools.h | 29 +- modules/boot/include/trigger_view.h | 1 + modules/boot/include/utils/lines.h | 2 +- modules/boot/include/utils/link.h | 1 + modules/boot/src/global_data.cpp | 1 + modules/boot/src/gz_flags.cpp | 8 +- modules/boot/src/menus/menu.cpp | 28 +- modules/boot/src/modules.cpp | 8 + modules/boot/src/save_manager.cpp | 1 - modules/boot/src/save_specials.cpp | 27 +- modules/boot/src/utils/card.cpp | 39 +- modules/boot/src/utils/hook.cpp | 64 +- modules/boot/src/utils/link.cpp | 59 + modules/features/ee_checker/CMakeLists.txt | 5 + .../features/ee_checker/include/ee_checker.h | 5 + modules/features/ee_checker/include/main.h | 6 + .../features/ee_checker/src/ee_checker.cpp | 108 + modules/features/ee_checker/src/main.cpp | 15 + modules/features/lfc_checker/CMakeLists.txt | 5 + .../lfc_checker/include/lfc_checker.h | 5 + modules/features/lfc_checker/include/main.h | 6 + .../features/lfc_checker/src/lfc_checker.cpp | 213 ++ modules/features/lfc_checker/src/main.cpp | 15 + .../trigger_view/src/trigger_view.cpp | 28 + modules/init/src/main.cpp | 4 + .../menu_actor_list/include/actor_list_menu.h | 4 +- .../menu_actor_list/src/actor_list_menu.cpp | 189 +- .../menu_actor_spawn/src/actor_spawn_menu.cpp | 18 +- .../include/any_bite_saves_menu.h | 61 + .../src/any_bite_saves_menu.cpp | 62 +- .../menu_any_saves/include/any_saves_menu.h | 5 - .../include/dungeon_flags_menu.h | 8 +- .../src/dungeon_flags_menu.cpp | 78 + modules/menus/menu_equipment/CMakeLists.txt | 5 + .../menu_equipment/include/equipment_menu.h | 45 + modules/menus/menu_equipment/include/main.h | 6 + .../menu_equipment/src/equipment_menu.cpp | 351 ++++ modules/menus/menu_equipment/src/main.cpp | 56 + modules/menus/menu_flags/include/flags_menu.h | 7 +- modules/menus/menu_flags/src/flags_menu.cpp | 4 + .../include/general_flags_menu.h | 14 +- .../src/general_flags_menu.cpp | 38 +- modules/menus/menu_golden_bugs/CMakeLists.txt | 5 + .../include/golden_bugs_menu.h | 72 + modules/menus/menu_golden_bugs/include/main.h | 6 + .../menu_golden_bugs/src/golden_bugs_menu.cpp | 730 +++++++ modules/menus/menu_golden_bugs/src/main.cpp | 56 + .../menus/menu_hidden_skills/CMakeLists.txt | 5 + .../include/hidden_skills_menu.h | 35 + .../menus/menu_hidden_skills/include/main.h | 6 + .../src/hidden_skills_menu.cpp | 68 + modules/menus/menu_hidden_skills/src/main.cpp | 56 + .../include/hundo_saves_menu.h | 15 +- .../menu_hundo_saves/src/hundo_saves_menu.cpp | 22 +- .../menu_item_wheel/include/item_wheel_menu.h | 4 +- .../menu_item_wheel/src/item_wheel_menu.cpp | 12 +- .../menu_memfiles/include/memfiles_menu.h | 3 +- modules/menus/menu_memfiles/src/main.cpp | 14 +- .../menus/menu_memfiles/src/memfiles_menu.cpp | 17 +- modules/menus/menu_pause/include/pause_menu.h | 47 +- modules/menus/menu_pause/src/pause_menu.cpp | 397 +--- .../src/position_settings_menu.cpp | 3 + .../menu_practice/include/practice_menu.h | 2 +- .../menus/menu_practice/src/practice_menu.cpp | 4 - modules/menus/menu_rupee_flags/CMakeLists.txt | 5 + modules/menus/menu_rupee_flags/include/main.h | 6 + .../include/rupee_flags_menu.h | 32 + modules/menus/menu_rupee_flags/src/main.cpp | 56 + .../menu_rupee_flags/src/rupee_flags_menu.cpp | 109 + modules/menus/menu_scene/src/scene_menu.cpp | 4 + modules/menus/menu_sound_test/CMakeLists.txt | 5 + modules/menus/menu_sound_test/include/main.h | 6 + .../menu_sound_test/include/sound_test_menu.h | 11 + modules/menus/menu_sound_test/src/main.cpp | 50 + .../menu_sound_test/src/sound_test_menu.cpp | 64 + modules/menus/menu_tools/include/tools_menu.h | 35 +- modules/menus/menu_tools/src/main.cpp | 10 +- modules/menus/menu_tools/src/tools_menu.cpp | 304 +-- .../menus/menu_tools_checkers/CMakeLists.txt | 5 + .../menus/menu_tools_checkers/include/main.h | 6 + .../include/tools_checkers_menu.h | 42 + .../menus/menu_tools_checkers/src/main.cpp | 50 + .../src/tools_checkers_menu.cpp | 111 + .../menu_tools_controller/CMakeLists.txt | 5 + .../menu_tools_controller/include/main.h | 6 + .../include/tools_controller_menu.h | 21 + .../menus/menu_tools_controller/src/main.cpp | 50 + .../src/tools_controller_menu.cpp | 38 + modules/menus/menu_tools_link/CMakeLists.txt | 5 + modules/menus/menu_tools_link/include/main.h | 6 + .../menu_tools_link/include/tools_link_menu.h | 45 + modules/menus/menu_tools_link/src/main.cpp | 56 + .../menu_tools_link/src/tools_link_menu.cpp | 163 ++ modules/menus/menu_tools_scene/CMakeLists.txt | 5 + modules/menus/menu_tools_scene/include/main.h | 6 + .../include/tools_scene_menu.h | 38 + modules/menus/menu_tools_scene/src/main.cpp | 50 + .../menu_tools_scene/src/tools_scene_menu.cpp | 112 ++ .../menus/menu_tools_timers/CMakeLists.txt | 5 + .../menus/menu_tools_timers/include/main.h | 6 + .../include/tools_timers_menu.h | 33 + modules/menus/menu_tools_timers/src/main.cpp | 50 + .../src/tools_timers_menu.cpp | 118 ++ .../src/trigger_view_menu.cpp | 2 + modules/menus/menu_warp/src/main.cpp | 3 +- res/map/GCN_NTSCJ.lst | 5 + res/map/GCN_NTSCU.lst | 5 + res/map/GCN_PAL.lst | 5 + res/map/WII_NTSCJ.lst | 5 + res/map/WII_NTSCU_10.lst | 5 + res/map/WII_NTSCU_12.lst | 5 + res/map/WII_PAL.lst | 5 + res/save_files/any.bin | Bin 4080 -> 4080 bytes res/save_files/any_bite.bin | Bin 3680 -> 3680 bytes res/save_files/hundo.bin | Bin 6480 -> 6880 bytes res/save_files/hundo/aeralfos_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/ag.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/argorok.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/blizzeta.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/boss_bug.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/camp.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/cits_1.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/cits_2.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/cits_poe_cycle.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/coo.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/coo_10.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/coo_20.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/coo_30.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/coo_40.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/corotd.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/dangoro.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/darkhammer.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/death_sword_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/dot_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/early_ele.bin | Bin 0 -> 2700 bytes res/save_files/hundo/early_platform.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/eldin_collection.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/fan_tower.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/fyrus.bin | Bin 0 -> 2700 bytes res/save_files/hundo/gm.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/gorge_arc.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/grove_boost.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/grove_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/hotspring.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/hv_archery.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/ice_puzzle.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/iza_1_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/iza_2.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/kb1.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/kb2.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/lakebed_bk_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/lanayru_twilight.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/lh_cave.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/mdh tower.bin | Bin 0 -> 2700 bytes res/save_files/hundo/mdh.bin | Bin 0 -> 2700 bytes res/save_files/hundo/morpheel.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/nf_bomb_boost.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/palace_1.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/palace_2.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/poe_1_skip.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/post_ag.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/post_mdh.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/post_tot.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/silver_rupee.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/spr.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/spr_2.bin | Bin 0 -> 2700 bytes res/save_files/hundo/spr_bk.bin | Bin 0 -> 2700 bytes res/save_files/hundo/spr_bk_lja.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/stallord.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/star_1.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/star_2.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/tot.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/tot_darknut.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/tot_early_hp.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/tot_early_poe.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/tot_statue_throws.bin | Bin 2700 -> 2700 bytes res/save_files/hundo/waterfall_sidehop.bin | Bin 0 -> 2700 bytes res/save_files/hundo/zant.bin | Bin 2700 -> 2700 bytes res/save_files_wii/any.bin | Bin 4160 -> 4160 bytes .../any/{argarok.bin => argorok.bin} | Bin res/save_files_wii/any_bite.bin | Bin 0 -> 4000 bytes res/save_files_wii/any_bite/ag.bin | Bin 0 -> 2700 bytes .../any_bite/arbiters_grounds.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/arealfos.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/argorok.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/beast_ganon.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/before_kb1.bin | Bin 0 -> 2707 bytes .../any_bite/bombhouse_skip.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/boss_bug.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/bulblin_camp.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/cits.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/cits_2.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/cits_tower.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/dark_hammer.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/darknut.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/death_sword.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/deku_toad.bin | Bin 0 -> 2707 bytes .../any_bite/early_boss_key.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/early_city.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/earlypf.bin | Bin 0 -> 2707 bytes .../any_bite/eldin_twilight.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/ems.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/enter_lakebed.bin | Bin 0 -> 2707 bytes .../any_bite/epona_oob_to_flight_by_fowl.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/fan_tower.bin | Bin 0 -> 2700 bytes .../any_bite/faron_twilight.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/freezard_skip.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/goats.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/hc.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/horseback.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/hugo.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/iza.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/kb1.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/lakebed_1.bin | Bin 0 -> 2707 bytes .../any_bite/lanayru_twilight.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/mdh_bridge.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/mdh_tower.bin | Bin 0 -> 2707 bytes .../any_bite/messenger_skip.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/morpheel.bin | Bin 0 -> 2707 bytes .../any_bite/ordon_gate_clip.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/plumm_oob.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/poe_1_skip.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/pot1.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/pot2.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/purple_mist.bin | Bin 0 -> 2700 bytes res/save_files_wii/any_bite/seam_clip.bin | Bin 0 -> 2707 bytes .../any_bite/snowpeak_ruins_mbbb.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/stallord.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/stupidroom.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/towerclimb.bin | Bin 0 -> 2707 bytes .../any_bite/waterfall_sidehop.bin | Bin 0 -> 2707 bytes res/save_files_wii/any_bite/zant.bin | Bin 0 -> 2700 bytes 280 files changed, 7144 insertions(+), 1507 deletions(-) create mode 100644 external/libtp_c/include/SSystem/SComponent/c_phase.h create mode 100644 external/libtp_c/include/d/s/d_s_logo.h create mode 100644 external/libtp_c/include/f_op/f_op_scene.h create mode 100644 external/libtp_c/include/f_op/f_op_scene_tag.h create mode 100644 external/libtp_c/include/f_pc/f_pc_create_req.h create mode 100644 external/libtp_c/include/f_pc/f_pc_create_tag.h create mode 100644 external/libtp_c/include/f_pc/f_pc_executor.h create mode 100644 external/libtp_c/include/f_pc/f_pc_layer_iter.h create mode 100644 external/libtp_c/include/f_pc/f_pc_line_iter.h create mode 100644 external/libtp_c/include/f_pc/f_pc_node.h create mode 100644 external/libtp_c/include/f_pc/f_pc_stdcreate_req.h create mode 100755 external/misc/dat2qlogs.py create mode 100755 external/misc/gci2qlogs.py create mode 100644 external/misc/meta.py create mode 100755 external/misc/nandpack.py create mode 100644 modules/features/ee_checker/CMakeLists.txt create mode 100644 modules/features/ee_checker/include/ee_checker.h create mode 100644 modules/features/ee_checker/include/main.h create mode 100644 modules/features/ee_checker/src/ee_checker.cpp create mode 100644 modules/features/ee_checker/src/main.cpp create mode 100644 modules/features/lfc_checker/CMakeLists.txt create mode 100644 modules/features/lfc_checker/include/lfc_checker.h create mode 100644 modules/features/lfc_checker/include/main.h create mode 100644 modules/features/lfc_checker/src/lfc_checker.cpp create mode 100644 modules/features/lfc_checker/src/main.cpp create mode 100644 modules/menus/menu_equipment/CMakeLists.txt create mode 100644 modules/menus/menu_equipment/include/equipment_menu.h create mode 100644 modules/menus/menu_equipment/include/main.h create mode 100644 modules/menus/menu_equipment/src/equipment_menu.cpp create mode 100644 modules/menus/menu_equipment/src/main.cpp create mode 100644 modules/menus/menu_golden_bugs/CMakeLists.txt create mode 100644 modules/menus/menu_golden_bugs/include/golden_bugs_menu.h create mode 100644 modules/menus/menu_golden_bugs/include/main.h create mode 100644 modules/menus/menu_golden_bugs/src/golden_bugs_menu.cpp create mode 100644 modules/menus/menu_golden_bugs/src/main.cpp create mode 100644 modules/menus/menu_hidden_skills/CMakeLists.txt create mode 100644 modules/menus/menu_hidden_skills/include/hidden_skills_menu.h create mode 100644 modules/menus/menu_hidden_skills/include/main.h create mode 100644 modules/menus/menu_hidden_skills/src/hidden_skills_menu.cpp create mode 100644 modules/menus/menu_hidden_skills/src/main.cpp create mode 100644 modules/menus/menu_rupee_flags/CMakeLists.txt create mode 100644 modules/menus/menu_rupee_flags/include/main.h create mode 100644 modules/menus/menu_rupee_flags/include/rupee_flags_menu.h create mode 100644 modules/menus/menu_rupee_flags/src/main.cpp create mode 100644 modules/menus/menu_rupee_flags/src/rupee_flags_menu.cpp create mode 100644 modules/menus/menu_sound_test/CMakeLists.txt create mode 100644 modules/menus/menu_sound_test/include/main.h create mode 100644 modules/menus/menu_sound_test/include/sound_test_menu.h create mode 100644 modules/menus/menu_sound_test/src/main.cpp create mode 100644 modules/menus/menu_sound_test/src/sound_test_menu.cpp create mode 100644 modules/menus/menu_tools_checkers/CMakeLists.txt create mode 100644 modules/menus/menu_tools_checkers/include/main.h create mode 100644 modules/menus/menu_tools_checkers/include/tools_checkers_menu.h create mode 100644 modules/menus/menu_tools_checkers/src/main.cpp create mode 100644 modules/menus/menu_tools_checkers/src/tools_checkers_menu.cpp create mode 100644 modules/menus/menu_tools_controller/CMakeLists.txt create mode 100644 modules/menus/menu_tools_controller/include/main.h create mode 100644 modules/menus/menu_tools_controller/include/tools_controller_menu.h create mode 100644 modules/menus/menu_tools_controller/src/main.cpp create mode 100644 modules/menus/menu_tools_controller/src/tools_controller_menu.cpp create mode 100644 modules/menus/menu_tools_link/CMakeLists.txt create mode 100644 modules/menus/menu_tools_link/include/main.h create mode 100644 modules/menus/menu_tools_link/include/tools_link_menu.h create mode 100644 modules/menus/menu_tools_link/src/main.cpp create mode 100644 modules/menus/menu_tools_link/src/tools_link_menu.cpp create mode 100644 modules/menus/menu_tools_scene/CMakeLists.txt create mode 100644 modules/menus/menu_tools_scene/include/main.h create mode 100644 modules/menus/menu_tools_scene/include/tools_scene_menu.h create mode 100644 modules/menus/menu_tools_scene/src/main.cpp create mode 100644 modules/menus/menu_tools_scene/src/tools_scene_menu.cpp create mode 100644 modules/menus/menu_tools_timers/CMakeLists.txt create mode 100644 modules/menus/menu_tools_timers/include/main.h create mode 100644 modules/menus/menu_tools_timers/include/tools_timers_menu.h create mode 100644 modules/menus/menu_tools_timers/src/main.cpp create mode 100644 modules/menus/menu_tools_timers/src/tools_timers_menu.cpp create mode 100644 res/save_files/hundo/early_ele.bin create mode 100644 res/save_files/hundo/fyrus.bin create mode 100644 res/save_files/hundo/mdh tower.bin create mode 100644 res/save_files/hundo/mdh.bin create mode 100644 res/save_files/hundo/spr_2.bin create mode 100644 res/save_files/hundo/spr_bk.bin create mode 100644 res/save_files/hundo/waterfall_sidehop.bin rename res/save_files_wii/any/{argarok.bin => argorok.bin} (100%) create mode 100644 res/save_files_wii/any_bite.bin create mode 100644 res/save_files_wii/any_bite/ag.bin create mode 100644 res/save_files_wii/any_bite/arbiters_grounds.bin create mode 100644 res/save_files_wii/any_bite/arealfos.bin create mode 100644 res/save_files_wii/any_bite/argorok.bin create mode 100644 res/save_files_wii/any_bite/beast_ganon.bin create mode 100644 res/save_files_wii/any_bite/before_kb1.bin create mode 100644 res/save_files_wii/any_bite/bombhouse_skip.bin create mode 100644 res/save_files_wii/any_bite/boss_bug.bin create mode 100644 res/save_files_wii/any_bite/bulblin_camp.bin create mode 100644 res/save_files_wii/any_bite/cits.bin create mode 100644 res/save_files_wii/any_bite/cits_2.bin create mode 100644 res/save_files_wii/any_bite/cits_tower.bin create mode 100644 res/save_files_wii/any_bite/dark_hammer.bin create mode 100644 res/save_files_wii/any_bite/darknut.bin create mode 100644 res/save_files_wii/any_bite/death_sword.bin create mode 100644 res/save_files_wii/any_bite/deku_toad.bin create mode 100644 res/save_files_wii/any_bite/early_boss_key.bin create mode 100644 res/save_files_wii/any_bite/early_city.bin create mode 100644 res/save_files_wii/any_bite/earlypf.bin create mode 100644 res/save_files_wii/any_bite/eldin_twilight.bin create mode 100644 res/save_files_wii/any_bite/ems.bin create mode 100644 res/save_files_wii/any_bite/enter_lakebed.bin create mode 100644 res/save_files_wii/any_bite/epona_oob_to_flight_by_fowl.bin create mode 100644 res/save_files_wii/any_bite/fan_tower.bin create mode 100644 res/save_files_wii/any_bite/faron_twilight.bin create mode 100644 res/save_files_wii/any_bite/freezard_skip.bin create mode 100644 res/save_files_wii/any_bite/goats.bin create mode 100644 res/save_files_wii/any_bite/hc.bin create mode 100644 res/save_files_wii/any_bite/horseback.bin create mode 100644 res/save_files_wii/any_bite/hugo.bin create mode 100644 res/save_files_wii/any_bite/iza.bin create mode 100644 res/save_files_wii/any_bite/kb1.bin create mode 100644 res/save_files_wii/any_bite/lakebed_1.bin create mode 100644 res/save_files_wii/any_bite/lanayru_twilight.bin create mode 100644 res/save_files_wii/any_bite/mdh_bridge.bin create mode 100644 res/save_files_wii/any_bite/mdh_tower.bin create mode 100644 res/save_files_wii/any_bite/messenger_skip.bin create mode 100644 res/save_files_wii/any_bite/morpheel.bin create mode 100644 res/save_files_wii/any_bite/ordon_gate_clip.bin create mode 100644 res/save_files_wii/any_bite/plumm_oob.bin create mode 100644 res/save_files_wii/any_bite/poe_1_skip.bin create mode 100644 res/save_files_wii/any_bite/pot1.bin create mode 100644 res/save_files_wii/any_bite/pot2.bin create mode 100644 res/save_files_wii/any_bite/purple_mist.bin create mode 100644 res/save_files_wii/any_bite/seam_clip.bin create mode 100644 res/save_files_wii/any_bite/snowpeak_ruins_mbbb.bin create mode 100644 res/save_files_wii/any_bite/stallord.bin create mode 100644 res/save_files_wii/any_bite/stupidroom.bin create mode 100644 res/save_files_wii/any_bite/towerclimb.bin create mode 100644 res/save_files_wii/any_bite/waterfall_sidehop.bin create mode 100644 res/save_files_wii/any_bite/zant.bin diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e9c4915a..4904518e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,19 +1,15 @@ # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/ubuntu/.devcontainer/base.Dockerfile # [Choice] Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon): ubuntu-22.04, ubuntu-20.04, ubuntu-18.04 -ARG VARIANT="jammy" -FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} -USER vscode +FROM devkitpro/devkitppc # [Optional] Uncomment this section to install additional OS packages. # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ # && apt-get -y install --no-install-recommends -RUN sudo dpkg --add-architecture i386 +#RUN sudo dpkg --add-architecture i386 RUN sudo apt-get update && sudo apt-get install -y build-essential -RUN sudo apt-get install -y python3-pip -RUN sudo apt-get install -y libarchive-tools -RUN sudo apt-get install -y clang-format-10 +RUN sudo apt-get install -y libarchive-tools RUN sudo apt-get install -y cmake # RUN sudo apt-get install -y wine64 # RUN sudo apt-get install -y wine @@ -21,19 +17,6 @@ RUN sudo apt-get install -y ninja-build RUN sudo apt-get upgrade -y RUN sudo apt-get install -y libtinfo5 -RUN python3 -m pip install Pillow - -# Install DevkitPPC -RUN sudo ln -sf /proc/self/mounts /etc/mtab -WORKDIR /tmp -COPY install-devkitpro-pacman install-devkitpro-pacman -RUN sudo chmod +x ./install-devkitpro-pacman -RUN sudo ./install-devkitpro-pacman -RUN sudo dkp-pacman -Syu --noconfirm -WORKDIR /etc -RUN sudo ln -sf /proc/self/mounts mtab -RUN sudo dkp-pacman -S --noconfirm gamecube-dev wii-dev - # # Install the patchers # WORKDIR /workspaces/romhack/wii # RUN curl -L https://github.com/zsrtp/romhack-compiler/releases/download/v0.1.1-r2/romhack-v0.1.1-r2-linux-x64-musl.zip | bsdtar -xvf - diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9bd539da..aadd334b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,32 +1,43 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/ubuntu { - "name": "Ubuntu", + "name": "TGPZ", "build": { - "dockerfile": "Dockerfile", - // Update 'VARIANT' to pick an Ubuntu version: jammy / ubuntu-22.04, focal / ubuntu-20.04, bionic /ubuntu-18.04 - // Use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon. - "args": { "VARIANT": "ubuntu-20.04" } + "dockerfile": "Dockerfile" }, - "extensions": [ - "ms-vscode.cpptools-extension-pack", - "ms-vscode.makefile-tools", - "ms-python.python", - "ms-vscode.hexeditor", - "twxs.cmake", - "Gruntfuggly.todo-tree", - "bungcip.better-toml", - "mhutchie.git-graph" - ], + "features": { + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12.5" + } + }, + + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools-extension-pack", + "ms-vscode.makefile-tools", + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.vscode-pylance", + "ms-vscode.hexeditor", + "twxs.cmake", + "Gruntfuggly.todo-tree", + "bungcip.better-toml", + "mhutchie.git-graph" + ] + } + }, + + "onCreateCommand": "python3 -m pip install Pillow pycryptodome", // Use 'forwardPorts' to make a list of ports inside the container available locally. "runArgs": [ "--network=host" - ], + ] // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "uname -a", // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "vscode" + //"remoteUser": "vscode" } diff --git a/.github/workflows/multi-region-tests.yml b/.github/workflows/multi-region-tests.yml index 042fea45..b97a5561 100644 --- a/.github/workflows/multi-region-tests.yml +++ b/.github/workflows/multi-region-tests.yml @@ -2,7 +2,7 @@ name: Multi-Region Tests on: pull_request: - branches: [ master ] + branches: [ main ] jobs: build: @@ -53,7 +53,7 @@ jobs: dolphin_memory: "0x8044A6C0" iso_name: tpgz.iso container: - image: ghcr.io/pheenoh/zsrtp-tpgz:${{ matrix.container }} + image: ghcr.io/zsrtp/zsrtp-tpgz:${{ matrix.container }} options: --shm-size=128m --ulimit core=-1 --privileged=true name: ${{ matrix.platform }}-${{ matrix.region }} Test steps: @@ -70,4 +70,4 @@ jobs: run: | dolphin-emu-nogui --platform=headless --video_backend=Null -e build/${{ matrix.iso_name }} & sleep 15 - /dme -c ${{ matrix.dolphin_memory }} + dme -d dolphin-emu-nog -a ${{ matrix.dolphin_memory }} -q 0 --platform offscreen diff --git a/.github/workflows/tpgz-release.yml b/.github/workflows/tpgz-release.yml index 8bbb420c..2adc8e3c 100644 --- a/.github/workflows/tpgz-release.yml +++ b/.github/workflows/tpgz-release.yml @@ -20,6 +20,13 @@ jobs: with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ${{ secrets.AWS_REGION }} + - name: Download existing meta.json + run: aws s3 cp s3://${{ secrets.AWS_S3_BUCKET }}/patches/meta.json meta.json + - name: Update meta.json + run: | + python3 external/misc/meta.py + - name: Upload updated meta.json to S3 + run: aws s3 cp meta.json s3://${{ secrets.AWS_S3_BUCKET }}/patches/meta.json --acl public-read - name: Sync directory to S3 run: | aws s3 sync patches/ s3://${{ secrets.AWS_S3_BUCKET }}/patches/ --acl public-read diff --git a/CMakeLists.txt b/CMakeLists.txt index 155868de..29ac53a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,7 @@ include(cmake/relmapper.cmake) include(cmake/fonts.cmake) project(TPGZ - VERSION 1.0.0 + VERSION 1.1.0 DESCRIPTION "Twilight Princess speedrunning practice and testing tool" HOMEPAGE_URL "tpgz.io" LANGUAGES C CXX ASM) diff --git a/README.md b/README.md index 967829a0..3009f8fe 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,7 @@ A *Twilight Princess* ROM hack for speedrun practice and research. ## Download ### Web -Patch your TP (GameCube) rom on the web using our website [tpgz.io](https://tpgz.io). -- This uses a lot of memory, so it requires a decent computer. -- The Wii version is currently not supported for web patching. Please follow the offline patching guide. +Patch your TP (GameCube/Wii) rom on the web using our website [tpgz.io](https://tpgz.io). ### Offline To patch your rom offline, please follow our [guide](./docs/CreatingAnIso.md). diff --git a/RomHack.toml.in b/RomHack.toml.in index a6d3e6a0..675cb76f 100644 --- a/RomHack.toml.in +++ b/RomHack.toml.in @@ -25,8 +25,8 @@ iso = "@TPGZ_CFG_BLD_ISO@" "tpgz/save_files/any" = "../@TPGZ_CFG_SAVE_ANY_PATH@/any" # any% bite saves -"tpgz/save_files/any_bite.bin" = "../res/save_files/any_bite.bin" -"tpgz/save_files/any_bite" = "../res/save_files/any_bite" +"tpgz/save_files/any_bite.bin" = "../@TPGZ_CFG_SAVE_ANY_PATH@/any_bite.bin" +"tpgz/save_files/any_bite" = "../@TPGZ_CFG_SAVE_ANY_PATH@/any_bite" # 100% saves "tpgz/save_files/hundo.bin" = "../res/save_files/hundo.bin" diff --git a/external/gcn_c/include/nand.h b/external/gcn_c/include/nand.h index d5cac321..109d6323 100644 --- a/external/gcn_c/include/nand.h +++ b/external/gcn_c/include/nand.h @@ -10,6 +10,10 @@ #define NAND_OPEN_WRITE 0x02 #define NAND_OPEN_RW (NAND_OPEN_READ | NAND_OPEN_WRITE) +#define NAND_SEEK_BEG 0 +#define NAND_SEEK_CUR 1 +#define NAND_SEEK_END 2 + typedef struct NANDInfo { uint8_t unk[0x90]; } NANDInfo; diff --git a/external/gcn_c/include/storage.h b/external/gcn_c/include/storage.h index 477dcfed..7642c4e7 100644 --- a/external/gcn_c/include/storage.h +++ b/external/gcn_c/include/storage.h @@ -10,6 +10,9 @@ #define OPEN_MODE_READ #define OPEN_MODE_WRITE #define OPEN_MODE_RW +#define STORAGE_SEEK_BEG 0 +#define STORAGE_SEEK_CUR 1 +#define STORAGE_SEEK_END 2 #else // WII_PLATFORM #include "nand.h" #define STORAGE_FILENAME_MAX NAND_FILENAME_MAX @@ -17,6 +20,9 @@ #define OPEN_MODE_READ NAND_OPEN_READ #define OPEN_MODE_WRITE NAND_OPEN_WRITE #define OPEN_MODE_RW NAND_OPEN_RW +#define STORAGE_SEEK_BEG NAND_SEEK_BEG +#define STORAGE_SEEK_CUR NAND_SEEK_CUR +#define STORAGE_SEEK_END NAND_SEEK_END #endif // WII_PLATFORM enum StorageError { @@ -45,6 +51,7 @@ typedef struct Storage { int32_t result; char file_name_buffer[STORAGE_FILENAME_MAX * 2]; uint32_t result_size; + uint32_t position; } Storage; #ifndef WII_PLATFORM @@ -52,10 +59,26 @@ typedef struct Storage { #define StorageDelete(ch, fileName) CARDDelete(ch, fileName) #define StorageOpen(ch, fileName, fileInfo, mode) CARDOpen(ch, fileName, fileInfo) #define StorageClose(fileInfo) CARDClose(fileInfo) -#define StorageRead(storage, data, length, offset) \ - ({ (storage).result = CARDRead(&(storage).info, data, length, offset); }) -#define StorageWrite(storage, data, length, offset) \ - ({ (storage).result = CARDWrite(&(storage).info, data, length, offset); }) +#define StorageRead(storage, data, length) \ + ({ (storage).result = CARDRead(&(storage).info, data, length, (storage).position); (storage).position += length; (storage).result; }) +#define StorageWrite(storage, data, length) \ + ({ (storage).result = CARDWrite(&(storage).info, data, length, (storage).position); (storage).position += length; (storage).result; }) +inline int32_t StorageSeek(Storage* storage, int32_t offset, int32_t whence) { + switch (whence) { + case STORAGE_SEEK_BEG: + storage->position = (uint32_t)offset; + break; + case STORAGE_SEEK_CUR: + storage->position += offset; + break; + case STORAGE_SEEK_END: + storage->position = storage->info.length + offset; + break; + default: + return IoError; + } + return storage->position; +} #else // WII_PLATFORM #define StorageCreate(ch, fileName, size, fileBuffer) \ ({ \ @@ -66,20 +89,17 @@ typedef struct Storage { #define StorageDelete(ch, fileName) NANDDelete(fileName) #define StorageOpen(ch, fileName, fileInfo, mode) NANDOpen(fileName, fileInfo, mode) #define StorageClose(fileInfo) NANDClose(fileInfo) -#define StorageRead(storage, data, length, offset) \ +#define StorageRead(storage, data, length) \ ({ \ - (storage).result = NANDSeek(&(storage).info, offset, 0); \ - if ((storage).result == Ready) { \ - (storage).result_size = NANDRead(&(storage).info, data, length); \ - } \ + (storage).result_size = NANDRead(&(storage).info, data, length); \ + (storage).result = Ready; \ }) -#define StorageWrite(storage, data, length, offset) \ +#define StorageWrite(storage, data, length) \ ({ \ - (storage).result = NANDSeek(&(storage).info, offset, 0); \ - if ((storage).result == Ready) { \ - (storage).result_size = NANDWrite(&(storage).info, data, length); \ - } \ + (storage).result_size = NANDWrite(&(storage).info, data, length); \ + (storage).result = Ready; \ }) +#define StorageSeek(storage, offset, whence) NANDSeek(&(*(storage)).info, offset, whence) #endif // WII_PLATFORM #endif // __STORAGE_H__ \ No newline at end of file diff --git a/external/libtp_c/include/JSystem/JAudio2/JAISound.h b/external/libtp_c/include/JSystem/JAudio2/JAISound.h index a8c7fe43..6ff6c0f1 100644 --- a/external/libtp_c/include/JSystem/JAudio2/JAISound.h +++ b/external/libtp_c/include/JSystem/JAudio2/JAISound.h @@ -8,12 +8,31 @@ class JAISoundID { public: - operator u32() const { return this->mId; } - JAISoundID(u32 pId) { mId = pId; }; + operator u32() const { return this->mId.mFullId; } + + JAISoundID(u32 pId) { mId.mFullId = pId; }; + + JAISoundID(JAISoundID const& other) { mId = other.mId; }; + JAISoundID() {} -private: - u32 mId; + bool isAnonymous() { return mId.mFullId == 0xffffffff; } + void setAnonymous() { mId.mFullId = -1; } + + union { + u32 mFullId; + struct { + u8 b0; + u8 b1; + u8 b2; + u8 b3; + } mBytes; + struct { + u16 mSoundType; + u16 mShortId; + } mAdvancedId; // Debug doesn't have an inline for referencing the short ID so I assume + // it's similar to this + } mId; }; struct JASTrack {}; diff --git a/external/libtp_c/include/SSystem/SComponent/c_phase.h b/external/libtp_c/include/SSystem/SComponent/c_phase.h new file mode 100644 index 00000000..917f85ac --- /dev/null +++ b/external/libtp_c/include/SSystem/SComponent/c_phase.h @@ -0,0 +1,22 @@ +#ifndef C_PHASE_H +#define C_PHASE_H + +#include "../../dolphin/types.h" + +typedef int (*cPhs__Handler)(void*); + +enum cPhs__Step { + /* 0x0 */ cPhs_INIT_e, + /* 0x1 */ cPhs_LOADING_e, + /* 0x2 */ cPhs_NEXT_e, + /* 0x3 */ cPhs_UNK3_e, // appears to be an alternate error code, unsure how it differs + /* 0x4 */ cPhs_COMPLEATE_e, + /* 0x5 */ cPhs_ERROR_e, +}; + +typedef struct request_of_phase_process_class { + cPhs__Handler* mpHandlerTable; + int id; +} request_of_phase_process_class; + +#endif \ No newline at end of file diff --git a/external/libtp_c/include/Z2AudioLib/Z2AudioMgr.h b/external/libtp_c/include/Z2AudioLib/Z2AudioMgr.h index 4d93ef79..053c0bab 100644 --- a/external/libtp_c/include/Z2AudioLib/Z2AudioMgr.h +++ b/external/libtp_c/include/Z2AudioLib/Z2AudioMgr.h @@ -12,6 +12,7 @@ #include "Z2SoundStarter.h" #include "Z2SpeechMgr2.h" #include "Z2StatusMgr.h" +#include "../defines.h" #ifdef GCN_PLATFORM class Z2AudioMgr { @@ -62,8 +63,24 @@ class Z2AudioMgr { }; #endif -/* inline Z2AudioMgr* Z2GetAudioMgr() { - return Z2AudioMgr::getInterface(); -} */ +#ifdef WII_PLATFORM +#define mAudioMgrPtr Z2SeMgr__mAudioMgrPtr +#else +#define mAudioMgrPtr mAudioMgrPtr__10Z2AudioMgr +#endif + +extern "C" Z2AudioMgr* mAudioMgrPtr; + +inline Z2AudioMgr* Z2GetAudioMgr() { + return mAudioMgrPtr; +} + +LIBTP_DEFINE_FUNC(seStart__7Z2SeMgrF10JAISoundIDPC3VecUlScffffUc, + Z2SeMgr__seStart_JAISoundID__Vec_const____unsigned_long__signed_char__float__float__float__float__unsigned_char_, + bool, Z2SeMgr__seStart, (Z2SeMgr*, JAISoundID, Vec const*, u32, s8, f32, f32, f32, f32, u8)) + +LIBTP_DEFINE_FUNC(seStop__7Z2SeMgrF10JAISoundIDUl, + Z2SeMgr__seStop_JAISoundID__unsigned_long_, + bool, Z2SeMgr__seStop, (Z2SeMgr*, JAISoundID, u32)) #endif /* Z2AUDIOMGR_H */ \ No newline at end of file diff --git a/external/libtp_c/include/d/com/d_com_inf_game.h b/external/libtp_c/include/d/com/d_com_inf_game.h index 57e4c683..65c5d715 100644 --- a/external/libtp_c/include/d/com/d_com_inf_game.h +++ b/external/libtp_c/include/d/com/d_com_inf_game.h @@ -344,6 +344,9 @@ extern TitleScreenInfo l_fpcNdRq_Queue; LIBTP_DEFINE_FUNC(getLayerNo_common_common__14dComIfG_play_cFPCcii, dComIfG_play_c__getLayerNo_common_common_char_const____int__int_, int, tp_getLayerNo, (const char* stageName, int roomId, int layerOverride)) +LIBTP_DEFINE_FUNC(getLayerNo__14dComIfG_play_cFi, dComIfG_play_c__getLayerNo_int_, + int, dComIfG_play_c__getLayerNo, (int)) + inline dBgS* dComIfG_Bgsp() { return &g_dComIfG_gameInfo.play.mDBgS; } @@ -432,6 +435,9 @@ inline void dComIfGs_setLife(u16 amount) { inline void dComIfGs_setMaxLife(u8 max) { g_dComIfG_gameInfo.info.getPlayer().getPlayerStatusA().setMaxLife(max); } +inline void dComIfGs_setCollectSmell(u8 smell_id) { + g_dComIfG_gameInfo.info.getPlayer().getPlayerStatusA().setSelectEquip(3, smell_id); +} /* inline void dComIfGs_onDungeonItemBossKey(void) { g_dComIfG_gameInfo.info.getMemory().getBit().onDungeonItemBossKey(); @@ -619,6 +625,10 @@ inline void dComIfGs_offSwitch(int i_no, int i_roomNo) { dSv_info_c__offSwitch(&g_dComIfG_gameInfo.info, i_no, i_roomNo); } +inline BOOL dComIfGs_isSwitch(int i_no, int i_roomNo) { + return dSv_info_c__isSwitch(&g_dComIfG_gameInfo.info, i_no, i_roomNo); +} + inline void dComIfGs_putSave(int i_stageNo) { tp_putSave(&g_dComIfG_gameInfo.info, i_stageNo); } @@ -700,6 +710,18 @@ inline void dComIfGs_offEventBit(u16 flag) { dSv_event_c__offEventBit(&g_dComIfG_gameInfo.info.mSavedata.mEvent, flag); } +inline bool dComIfGs_isTmpBit(u16 flag) { + return dSv_event_c__isEventBit(&g_dComIfG_gameInfo.info.mTmp, flag); +} + +inline void dComIfGs_onTmpBit(u16 flag) { + dSv_event_c__onEventBit(&g_dComIfG_gameInfo.info.mTmp, flag); +} + +inline void dComIfGs_offTmpBit(u16 flag) { + dSv_event_c__offEventBit(&g_dComIfG_gameInfo.info.mTmp, flag); +} + inline void dComIfGs_setKeyNum(u8 num) { g_dComIfG_gameInfo.info.getMemory().getBit().setKeyNum(num); } @@ -753,6 +775,20 @@ LIBTP_DEFINE_FUNC(dComIfGs_onOneZoneSwitch__Fii, dComIfGs_onOneZoneSwitch_int__i LIBTP_DEFINE_FUNC(dComIfGs_onZoneSwitch__Fii, dComIfGs_onZoneSwitch_int__int_, void, dComIfGs_onZoneSwitch, (int, int)) +LIBTP_DEFINE_FUNC(setEventReg__11dSv_event_cFUsUc, dSv_event_c__setEventReg_unsigned_short__unsigned_char_, + void, dSv_event_c__setEventReg, (dSv_event_c*, u16, u8)) + +LIBTP_DEFINE_FUNC(getEventReg__11dSv_event_cCFUs, dSv_event_c__getEventReg_unsigned_short__const, + u8, dSv_event_c__getEventReg, (dSv_event_c*, u16)) + +inline void dComIfGs_setEventReg(u16 flag, u8 value) { + dSv_event_c__setEventReg(&g_dComIfG_gameInfo.info.getSavedata().getEvent(), flag, value); +} + +inline u8 dComIfGs_getEventReg(u16 flag) { + return dSv_event_c__getEventReg(&g_dComIfG_gameInfo.info.getSavedata().getEvent(), flag); +} + class dComIfAc_gameInfo { public: u8 field_0x0[4]; diff --git a/external/libtp_c/include/d/d_stage.h b/external/libtp_c/include/d/d_stage.h index 915c55d3..6d033a7b 100644 --- a/external/libtp_c/include/d/d_stage.h +++ b/external/libtp_c/include/d/d_stage.h @@ -256,4 +256,6 @@ extern dStage_nextStage_c__set_t dStage_nextStage_c__set; #endif extern dStage_roomStatus_c dStage_roomControl_c__mStatus[64]; +extern "C" s8 dStage_roomControl_c__mStayNo; + #endif /* D_D_STAGE_H */ diff --git a/external/libtp_c/include/d/meter/d_meter2_info.h b/external/libtp_c/include/d/meter/d_meter2_info.h index 1372d032..64f2a3de 100644 --- a/external/libtp_c/include/d/meter/d_meter2_info.h +++ b/external/libtp_c/include/d/meter/d_meter2_info.h @@ -25,6 +25,7 @@ class dMeter2Info_c { u8& getDirectUseItem() { return mDirectUseItem; } dMeterMap_c* getMeterMapClass() { return mMeterMap; } void offUseButton(int pButton) { mUseButton &= ~(u16)pButton; } + void offMenuInForce(int flag) { mMenuInForce &= ~(1 << flag); } /* 0x00 */ void* vtable; /* 0x04 */ u8 unk4[4]; @@ -56,7 +57,7 @@ class dMeter2Info_c { /* 0x90 */ u32 mTempBits; /* 0x94 */ s16 mMsgKeyWaitTimer; /* 0x96 */ u16 mHorseLifeCount; - /* 0x98 */ u16 unk152; + /* 0x98 */ u16 mMenuInForce; /* 0x9A */ u16 mHotSpringTimer[4]; /* 0xA2 */ u16 mSub2DStatus; /* 0xA4 */ u16 mFloatingFlowID; diff --git a/external/libtp_c/include/d/s/d_s_logo.h b/external/libtp_c/include/d/s/d_s_logo.h new file mode 100644 index 00000000..09660602 --- /dev/null +++ b/external/libtp_c/include/d/s/d_s_logo.h @@ -0,0 +1,152 @@ +#ifndef D_D_S_LOGO_H +#define D_D_S_LOGO_H + +#include "../../f_op/f_op_scene.h" + +class mDoDvdThd_mountXArchive_c; +class mDoDvdThd_toMainRam_c; +class dDlst_2D_c; +class ResTIMG; +class JKRExpHeap; +class JKRHeap; + + +#if defined(WII_PLATFORM) || defined(GCN_PAL) +class dScnLogo_c { +public: + enum { + /* 0x0 */ EXEC_WARNING_IN, + /* 0x1 */ EXEC_WARNING_DISP, + /* 0x2 */ EXEC_WARNING_OUT, + /* 0x3 */ EXEC_NINTENDO_IN, + /* 0x4 */ EXEC_NINTENDO_OUT, + /* 0x5 */ EXEC_DOLBY_IN, + /* 0x6 */ EXEC_DOLBY_OUT, + /* 0x7 */ EXEC_DOLBY_OUT2, + /* 0x8 */ EXEC_PROG_IN, + /* 0x9 */ EXEC_PROG_SEL, + /* 0xA */ EXEC_PROG_OUT, + /* 0xB */ EXEC_PROG_SET, + /* 0xC */ EXEC_PROG_SET2, + /* 0xD */ EXEC_PROG_CHANGE, + /* 0xE */ EXEC_DVD_WAIT, + /* 0xF */ EXEC_SCENE_CHANGE, + }; + + /* 0x000*/ u8 field_0x00[0x20c]; + /* 0x20C */ u8 mExecCommand; + /* 0x20D */ u8 field_0x209; + /* 0x20E */ u8 field_0x20a; + /* 0x20F */ u8 field_0x20b; + /* 0x210 */ u16 mTimer; +}; +#else +class dScnLogo_c : public scene_class { +public: + enum { + /* 0x0 */ EXEC_WARNING_IN, + /* 0x1 */ EXEC_WARNING_DISP, + /* 0x2 */ EXEC_WARNING_OUT, + /* 0x3 */ EXEC_NINTENDO_IN, + /* 0x4 */ EXEC_NINTENDO_OUT, + /* 0x5 */ EXEC_DOLBY_IN, + /* 0x6 */ EXEC_DOLBY_OUT, + /* 0x7 */ EXEC_DOLBY_OUT2, + /* 0x8 */ EXEC_PROG_IN, + /* 0x9 */ EXEC_PROG_SEL, + /* 0xA */ EXEC_PROG_OUT, + /* 0xB */ EXEC_PROG_SET, + /* 0xC */ EXEC_PROG_SET2, + /* 0xD */ EXEC_PROG_CHANGE, + /* 0xE */ EXEC_DVD_WAIT, + /* 0xF */ EXEC_SCENE_CHANGE, + }; + + dScnLogo_c() {} + /* 802560B4 */ void preLoad_dyl_create(); + /* 802560F8 */ void preLoad_dyl_remove(); + /* 8025611C */ bool preLoad_dyl(); + /* 80256198 */ void checkProgSelect(); + /* 80256210 */ int draw(); + /* 80256264 */ void progInDraw(); + /* 8025631C */ void progSelDraw(); + /* 802568E0 */ void progOutDraw(); + /* 80256A3C */ void progSetDraw(); + /* 80256AC0 */ void progSet2Draw(); + /* 80256B3C */ void progChangeDraw(); + /* 80256BF4 */ void warningInDraw(); + /* 80256C68 */ void warningDispDraw(); + /* 80256DC4 */ void warningOutDraw(); + /* 80256E48 */ void nintendoInDraw(); + /* 80256ECC */ void nintendoOutDraw(); + /* 80256F50 */ void dolbyInDraw(); + /* 80256FD4 */ void dolbyOutDraw(); + /* 80257058 */ void dolbyOutDraw2(); + /* 80257070 */ void dvdWaitDraw(); + /* 80257284 */ void nextSceneChange(); + /* 802572B8 */ ~dScnLogo_c(); + /* 80257AE0 */ int create(); + /* 80257C64 */ void logoInitGC(); + /* 80257FEC */ void dvdDataLoad(); + /* 802584D8 */ void setProgressiveMode(u8); + /* 802584FC */ u8 getProgressiveMode(); + /* 80258520 */ bool isProgressiveMode(); + /* 8025854C */ void setRenderMode(); + +public: + /* 0x1C4 */ request_of_phase_process_class field_0x1c4; + /* 0x1CC */ mDoDvdThd_toMainRam_c* sceneCommand; + /* 0x1D0 */ JKRExpHeap* field_0x1d0; + /* 0x1D4 */ JKRExpHeap* field_0x1d4; + /* 0x1D8 */ JKRHeap* mpHeap; + /* 0x1DC */ dDlst_2D_c* mWarning; + /* 0x1E0 */ dDlst_2D_c* mWarningStart; + /* 0x1E4 */ dDlst_2D_c* mNintendoLogo; + /* 0x1E8 */ dDlst_2D_c* mDolbyLogo; + /* 0x1EC */ dDlst_2D_c* mProgressiveChoice; + /* 0x1F0 */ dDlst_2D_c* mProgressiveYes; + /* 0x1F4 */ dDlst_2D_c* mProgressiveNo; + /* 0x1F8 */ dDlst_2D_c* mProgressiveSel; + /* 0x1FC */ request_of_phase_process_class* m_preLoad_dylPhase; + /* 0x200 */ ResTIMG* mProgressivePro; + /* 0x204 */ ResTIMG* mProgressiveInter; + /* 0x208 */ u8 mExecCommand; + /* 0x209 */ u8 field_0x209; + /* 0x20A */ u8 field_0x20a; + /* 0x20B */ u8 field_0x20b; + /* 0x20C */ u16 mTimer; + /* 0x20E */ u16 field_0x20e; + /* 0x210 */ u16 field_0x210; + /* 0x212 */ u16 field_0x212; + /* 0x214 */ u16 field_0x214; + /* 0x218 */ u32 field_0x218; + /* 0x21C */ void* buffer; + /* 0x220 */ mDoDvdThd_mountXArchive_c* mpField0Command; + /* 0x224 */ mDoDvdThd_mountXArchive_c* mpAlAnmCommand; + /* 0x228 */ u8 field_0x228[4]; + /* 0x22C */ mDoDvdThd_mountXArchive_c* mpFmapResCommand; + /* 0x230 */ mDoDvdThd_mountXArchive_c* mpDmapResCommand; + /* 0x234 */ mDoDvdThd_mountXArchive_c* mpCollectResCommand; + /* 0x238 */ u8 field_0x238[4]; + /* 0x23C */ mDoDvdThd_mountXArchive_c* mpItemIconCommand; + /* 0x240 */ mDoDvdThd_mountXArchive_c* mpRingResCommand; + /* 0x244 */ u8 field_0x244[4]; + /* 0x248 */ mDoDvdThd_mountXArchive_c* mpPlayerNameCommand; + /* 0x24C */ mDoDvdThd_mountXArchive_c* mpItemInfResCommand; + /* 0x250 */ mDoDvdThd_mountXArchive_c* mpButtonCommand; + /* 0x254 */ u8 field_0x254[4]; + /* 0x258 */ mDoDvdThd_mountXArchive_c* mpCardIconCommand; + /* 0x25C */ mDoDvdThd_mountXArchive_c* mpBmgResCommand; + /* 0x260 */ mDoDvdThd_mountXArchive_c* mpMsgComCommand; + /* 0x264 */ mDoDvdThd_mountXArchive_c* mpMsgResCommand[7]; + /* 0x280 */ u8 field_0x280[0x10]; + /* 0x290 */ mDoDvdThd_mountXArchive_c* mpFontResCommand; + /* 0x294 */ mDoDvdThd_mountXArchive_c* mpMain2DCommand; + /* 0x298 */ mDoDvdThd_mountXArchive_c* mpRubyResCommand; + /* 0x29C */ mDoDvdThd_toMainRam_c* mParticleCommand; + /* 0x2A0 */ mDoDvdThd_toMainRam_c* mItemTableCommand; + /* 0x2A4 */ mDoDvdThd_toMainRam_c* mEnemyItemCommand; +}; +#endif + +#endif \ No newline at end of file diff --git a/external/libtp_c/include/d/save/d_save.h b/external/libtp_c/include/d/save/d_save.h index 6f9f08c7..703a5637 100644 --- a/external/libtp_c/include/d/save/d_save.h +++ b/external/libtp_c/include/d/save/d_save.h @@ -783,6 +783,7 @@ class dSv_info_c { dSv_memory_c& getMemory() { return mMemory; } dSv_zone_c* getZones() { return mZone; } dSv_player_c& getPlayer() { return mSavedata.getPlayer(); } + dSv_event_c& getEvent() { return mTmp; } dSv_event_c& getTmp() { return mTmp; } /* 0x000 */ dSv_save_c mSavedata; @@ -850,6 +851,9 @@ LIBTP_DEFINE_FUNC(onSwitch__10dSv_info_cFii, dSv_info_c__onSwitch_int__int_, LIBTP_DEFINE_FUNC(offSwitch__10dSv_info_cFii, dSv_info_c__offSwitch_int__int_, void, dSv_info_c__offSwitch, (void* addr, int i_no, int i_roomNo)) +LIBTP_DEFINE_FUNC(isSwitch__10dSv_info_cCFii, dSv_info_c__isSwitch_int__int__const, + BOOL, dSv_info_c__isSwitch, (void* addr, int i_no, int i_roomNo)) + LIBTP_DEFINE_FUNC(isSwitch__12dSv_memBit_cCFi, dSv_memBit_c__isSwitch_int__const, bool, dSv_memBit_c__isSwitch, (void* addr, int i_no)) diff --git a/external/libtp_c/include/f_op/f_op_actor_mng.h b/external/libtp_c/include/f_op/f_op_actor_mng.h index 2c01efdf..38f3018f 100644 --- a/external/libtp_c/include/f_op/f_op_actor_mng.h +++ b/external/libtp_c/include/f_op/f_op_actor_mng.h @@ -272,6 +272,9 @@ LIBTP_DEFINE_FUNC(fopAcM_seenActorAngleY__FPC10fopAc_ac_cPC10fopAc_ac_c, fopAcM_ LIBTP_DEFINE_FUNC(gndCheck__11fopAcM_gc_cFPC4cXyz, fopAcM_gc_c__gndCheck_cXyz_const___, bool, fopAcM_gc_c__gndCheck, (const cXyz*)) +LIBTP_DEFINE_FUNC(fopAcM_CreateAppend__Fv, fopAcM_CreateAppend_void_, + fopAcM_prm_class*, fopAcM_CreateAppend, (void)) + #define tp_fopAcM_gc_c__mGroundY_addr 0x80450cd0 #define tp_fopAcM_gc_c__mGroundY (*(f32*)(tp_fopAcM_gc_c__mGroundY_addr)) diff --git a/external/libtp_c/include/f_op/f_op_scene.h b/external/libtp_c/include/f_op/f_op_scene.h new file mode 100644 index 00000000..eda0a9a6 --- /dev/null +++ b/external/libtp_c/include/f_op/f_op_scene.h @@ -0,0 +1,22 @@ +#ifndef F_F_OP_SCENE_H_ +#define F_F_OP_SCENE_H_ + +#include "../f_op/f_op_scene_tag.h" + +struct request_of_phase_process_class; +class mDoDvdThd_command_c; + +typedef struct scene_process_profile_definition { + /* 0x00 */ node_process_profile_definition mBase; + /* 0x20 */ process_method_class* mpMtd; // Subclass methods + /* 0x24 */ u32 field_0x24; // padding? +} scene_process_profile_definition; + +class scene_class { +public: + /* 0x000 */ process_node_class mBase; + /* 0x1AC */ process_method_class * mpMtd; + /* 0x1B0 */ scene_tag_class mScnTg; +}; + +#endif \ No newline at end of file diff --git a/external/libtp_c/include/f_op/f_op_scene_tag.h b/external/libtp_c/include/f_op/f_op_scene_tag.h new file mode 100644 index 00000000..d99b933c --- /dev/null +++ b/external/libtp_c/include/f_op/f_op_scene_tag.h @@ -0,0 +1,12 @@ +#ifndef F_OP_SCENE_TAG_H +#define F_OP_SCENE_TAG_H + +#include "../f_pc/f_pc_node.h" +#include "../SSystem/SComponent/c_phase.h" + +class scene_tag_class { +public: + u8 field_0x00[0x14]; +}; + +#endif \ No newline at end of file diff --git a/external/libtp_c/include/f_pc/f_pc_create_req.h b/external/libtp_c/include/f_pc/f_pc_create_req.h new file mode 100644 index 00000000..383c0aab --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_create_req.h @@ -0,0 +1,31 @@ + +#ifndef F_PC_CREATE_REQ_H_ +#define F_PC_CREATE_REQ_H_ + +#include "../SSystem/SComponent/c_phase.h" +#include "f_pc_create_tag.h" +#include "f_pc_method.h" +#include "f_pc_method_tag.h" + +typedef struct base_process_class base_process_class; +typedef struct layer_class layer_class; + +typedef struct create_request_method_class { + cPhs__Handler mpHandler; + process_method_func mpCancel; + process_method_func mpDelete; +} create_request_method_class; + +typedef struct create_request { + create_tag mBase; + s8 mbIsCreating; + s8 mbIsCancelling; + process_method_tag_class mMtdTg; + create_request_method_class* mpCtRqMtd; + void* mpUnk1; + s32 mBsPcId; + struct base_process_class* mpRes; + layer_class* mpLayer; +} create_request; // Size: 0x48 + +#endif diff --git a/external/libtp_c/include/f_pc/f_pc_create_tag.h b/external/libtp_c/include/f_pc/f_pc_create_tag.h new file mode 100644 index 00000000..2ddc7ed6 --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_create_tag.h @@ -0,0 +1,14 @@ + +#ifndef F_PC_CREATE_TAG_H_ +#define F_PC_CREATE_TAG_H_ + +#include "../SSystem/SComponent/c_list.h" +#include "../SSystem/SComponent/c_tag.h" + +typedef struct create_tag { + create_tag_class mBase; +} create_tag; + +extern "C" node_list_class g_fpcCtTg_Queue; + +#endif diff --git a/external/libtp_c/include/f_pc/f_pc_executor.h b/external/libtp_c/include/f_pc/f_pc_executor.h new file mode 100644 index 00000000..c24d192f --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_executor.h @@ -0,0 +1,19 @@ + +#ifndef F_PC_EXECUTOR_H_ +#define F_PC_EXECUTOR_H_ + +#include "f_pc_layer_iter.h" +#include "f_pc_line_iter.h" + +typedef struct base_process_class base_process_class; + +// base_process_class* fpcEx_Search(fpcLyIt_JudgeFunc pFunc, void* pUserData); +base_process_class* fpcEx_SearchByID(unsigned int id); +BOOL fpcEx_IsExist(unsigned int id); +s32 fpcEx_ToLineQ(base_process_class* pProc); +s32 fpcEx_ExecuteQTo(base_process_class* pProc); +s32 fpcEx_Execute(base_process_class* pProc); +s32 fpcEx_ToExecuteQ(base_process_class* pProc); +void fpcEx_Handler(fpcLnIt_QueueFunc pFunc); + +#endif diff --git a/external/libtp_c/include/f_pc/f_pc_layer.h b/external/libtp_c/include/f_pc/f_pc_layer.h index 43cab5d4..3a387b4a 100644 --- a/external/libtp_c/include/f_pc/f_pc_layer.h +++ b/external/libtp_c/include/f_pc/f_pc_layer.h @@ -21,4 +21,7 @@ typedef struct layer_class { } counts; } layer_class; +LIBTP_DEFINE_FUNC(fpcLy_CurrentLayer__Fv, fpcLy_CurrentLayer_void_, + layer_class*, fpcLy_CurrentLayer, (void)) + #endif diff --git a/external/libtp_c/include/f_pc/f_pc_layer_iter.h b/external/libtp_c/include/f_pc/f_pc_layer_iter.h new file mode 100644 index 00000000..f0cdfbb1 --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_layer_iter.h @@ -0,0 +1,21 @@ +#ifndef F_PC_LAYER_ITER_H_ +#define F_PC_LAYER_ITER_H_ + +#include "../dolphin/types.h" + +typedef struct layer_class layer_class; + +typedef struct layer_iter { + void* mpFunc; + void* mpUserData; +} layer_iter; + +typedef int (*fpcLyIt_OnlyHereFunc)(void*, void*); +typedef void* (*fpcLyIt_JudgeFunc)(void*, void*); + +s32 fpcLyIt_OnlyHere(layer_class* pLayer, fpcLyIt_OnlyHereFunc pFunc, void* pUserData); +s32 fpcLyIt_OnlyHereLY(layer_class* pLayer, fpcLyIt_OnlyHereFunc pFunc, void* pUserData); +void* fpcLyIt_Judge(layer_class* pLayer, fpcLyIt_JudgeFunc pFunc, void* pUserData); +void* fpcLyIt_AllJudge(fpcLyIt_JudgeFunc pFunc, void* pUserData); + +#endif diff --git a/external/libtp_c/include/f_pc/f_pc_line_iter.h b/external/libtp_c/include/f_pc/f_pc_line_iter.h new file mode 100644 index 00000000..97a2fe57 --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_line_iter.h @@ -0,0 +1,9 @@ + +#ifndef F_PC_LINE_ITER_H_ +#define F_PC_LINE_ITER_H_ + +typedef int (*fpcLnIt_QueueFunc)(void*, void*); + +void fpcLnIt_Queue(fpcLnIt_QueueFunc pFunc); + +#endif diff --git a/external/libtp_c/include/f_pc/f_pc_manager.h b/external/libtp_c/include/f_pc/f_pc_manager.h index cb179c4d..25b7d836 100644 --- a/external/libtp_c/include/f_pc/f_pc_manager.h +++ b/external/libtp_c/include/f_pc/f_pc_manager.h @@ -2,10 +2,10 @@ #define F_PC_MANAGER_H_ #include "f_pc_leaf.h" +#include "f_pc_executor.h" +#include "f_pc_node.h" -enum { - fpcM_ERROR_PROCESS_ID_e = -1, -}; +LIBTP_DEFINE_FUNC(fpcEx_Search__FPFPvPv_PvPv, fpcEx_Search_void______void____void_____void___, base_process_class*, fpcEx_Search, (fpcLyIt_JudgeFunc pFunc, void* pUserData)) typedef int (*FastCreateReqFunc)(void*); typedef void (*fpcM_ManagementFunc)(void); @@ -37,4 +37,37 @@ inline void* fpcM_GetAppend(const void* proc) { return ((base_process_class*)proc)->mpUserData; } +enum { + fpcM_ERROR_PROCESS_ID_e = 0xFFFFFFFF +}; + +typedef int (*FastCreateReqFunc)(void*); +typedef void (*fpcM_ManagementFunc)(void); +typedef int (*fpcM_DrawIteraterFunc)(void*, void*); + +inline process_profile_definition* fpcM_GetProfile(void* proc) { + return (process_profile_definition*)((base_process_class*)proc)->mpProf; +} + +inline BOOL fpcM_IsExecuting(unsigned int id) { + return fpcEx_IsExist(id); +} + +inline void* fpcM_LyJudge(process_node_class* i_node, fpcLyIt_JudgeFunc i_func, void* i_data) { + return fpcLyIt_Judge(&i_node->mLayer, i_func, i_data); +} + +void fpcM_Draw(void* pProc); +s32 fpcM_DrawIterater(fpcM_DrawIteraterFunc pFunc); +s32 fpcM_Execute(void* pProc); +s32 fpcM_Delete(void* pProc); +BOOL fpcM_IsCreating(unsigned int pID); +void fpcM_Management(fpcM_ManagementFunc pFunc1, fpcM_ManagementFunc pFunc2); +void fpcM_Init(); +base_process_class* fpcM_FastCreate(s16 pProcTypeID, FastCreateReqFunc param_2, void* param_3, + void* pData); +s32 fpcM_IsPause(void* pProc, u8 param_2); +void fpcM_PauseEnable(void* pProc, u8 param_2); +void fpcM_PauseDisable(void* pProc, u8 param_2); + #endif diff --git a/external/libtp_c/include/f_pc/f_pc_node.h b/external/libtp_c/include/f_pc/f_pc_node.h new file mode 100644 index 00000000..82d28495 --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_node.h @@ -0,0 +1,19 @@ +#ifndef F_PC_NODE_H_ +#define F_PC_NODE_H_ + +#include "../f_pc/f_pc_base.h" + +typedef struct process_node_class { + /* 0x00 */ base_process_class mBase; + /* 0xB8 */ nodedraw_method_class* mpNodeMtd; + /* 0xBC */ layer_class mLayer; + /* 0xE8 */ node_list_class mLayerNodeLists[16]; + /* 0x1A8 */ s8 mUnk0; +} process_node_class; + +typedef struct node_process_profile_definition { + /* 0x00 */ process_profile_definition mBase; + /* 0x1C */ process_method_class* sub_method; // Subclass methods +} node_process_profile_definition; + +#endif \ No newline at end of file diff --git a/external/libtp_c/include/f_pc/f_pc_stdcreate_req.h b/external/libtp_c/include/f_pc/f_pc_stdcreate_req.h new file mode 100644 index 00000000..5b3dba1d --- /dev/null +++ b/external/libtp_c/include/f_pc/f_pc_stdcreate_req.h @@ -0,0 +1,22 @@ +#ifndef F_PC_STDCREATE_H_ +#define F_PC_STDCREATE_H_ + +#include "f_pc_create_req.h" + +typedef struct layer_class layer_class; + +typedef int (*stdCreateFunc)(void*, void*); + +typedef struct standard_create_request_class { + /* 0x00 */ create_request mBase; + /* 0x48 */ request_of_phase_process_class unk_0x48; + /* 0x50 */ s16 mLoadID; + /* 0x54 */ void* unk_0x54; + /* 0x58 */ stdCreateFunc unk_0x58; + /* 0x5C */ void* unk_0x5C; +} standard_create_request_class; + +LIBTP_DEFINE_FUNC(fpcSCtRq_Request__FP11layer_classsPFPvPv_iPvPv, fpcSCtRq_Request_layer_class____short__int_____void____void_____void____void___, + int, fpcSCtRq_Request, (layer_class*, s16, stdCreateFunc, void*, void*)) + +#endif \ No newline at end of file diff --git a/external/libtp_c/include/m_Do/m_Do_audio.h b/external/libtp_c/include/m_Do/m_Do_audio.h index 90c680a8..1de771d6 100644 --- a/external/libtp_c/include/m_Do/m_Do_audio.h +++ b/external/libtp_c/include/m_Do/m_Do_audio.h @@ -13,11 +13,12 @@ extern "C" { extern mDoAud_zelAudio_c g_mDoAud_zelAudio; } -// Functions -#ifdef GCN_PLATFORM -typedef void (*mDoAud_seStartLevel_t)(u32, Vec const*, u32, s8); -#define mDoAud_seStartLevel ((mDoAud_seStartLevel_t)mDoAud_seStartLevel_addr) +inline void mDoAud_seStart(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_reverb) { + Z2SeMgr__seStart(&Z2GetAudioMgr()->mSeMgr, i_sfxID, i_sePos, param_2, i_reverb, 1.0f, 1.0f, -1.0f, -1.0f, 0); +} -#endif +inline void mDoAud_seStop(u32 i_sfxID, u32 param_2) { + Z2SeMgr__seStop(&Z2GetAudioMgr()->mSeMgr, i_sfxID, param_2); +} #endif /* M_DO_M_DO_AUDIO_H */ diff --git a/external/libtp_c/include/utils.h b/external/libtp_c/include/utils.h index 3fdcd332..02d745da 100644 --- a/external/libtp_c/include/utils.h +++ b/external/libtp_c/include/utils.h @@ -7,6 +7,15 @@ // Toggles save event flags void setEventFlag(u16 flag); +// Toggles temp event flags +void setTempEventFlag(u16 flag); + +// Toggles equipment items +void setItemFirstBit(u8 item); + +// Toggle dungeon switches +void setDungeonSwitch(int pFlag, int i_roomNo); + // Set Savefile spawn info void setReturnPlace(const char* stage, s8 room, u8 spawn); diff --git a/external/libtp_c/src/utils.cpp b/external/libtp_c/src/utils.cpp index 6901da63..ccf46d8c 100644 --- a/external/libtp_c/src/utils.cpp +++ b/external/libtp_c/src/utils.cpp @@ -4,11 +4,22 @@ // Toggles save event flags KEEP_FUNC void setEventFlag(u16 flag) { - if (dComIfGs_isEventBit(flag)) { - dComIfGs_offEventBit(flag); - } else { - dComIfGs_onEventBit(flag); - } + dComIfGs_isEventBit(flag) ? dComIfGs_offEventBit(flag) : dComIfGs_onEventBit(flag); +} + +// Toggles temp event flags +KEEP_FUNC void setTempEventFlag(u16 flag) { + dComIfGs_isTmpBit(flag) ? dComIfGs_offTmpBit(flag) : dComIfGs_onTmpBit(flag); +} + +// Toggles equipment items +KEEP_FUNC void setItemFirstBit(u8 item) { + dComIfGs_isItemFirstBit(item) ? dComIfGs_offItemFirstBit(item) : dComIfGs_onItemFirstBit(item); +} + +// Toggle dungeon switches +KEEP_FUNC void setDungeonSwitch(int pFlag, int i_roomNo) { + dComIfGs_isSwitch(pFlag, i_roomNo) ? dComIfGs_offSwitch(pFlag, i_roomNo) : dComIfGs_onSwitch(pFlag, i_roomNo); } // Set Savefile spawn info diff --git a/external/misc/any.py b/external/misc/any.py index 294e39c1..adf2f7d4 100644 --- a/external/misc/any.py +++ b/external/misc/any.py @@ -138,7 +138,7 @@ def main(args=None): "arealfos", "cits_2", "cits_tower", - "argarok", + "argorok", "palace_1", "palace_2", "early_platform", @@ -153,14 +153,20 @@ def main(args=None): any_p = [{**copy.deepcopy(default_entry), "id": i, "filename": name} for i, name in enumerate(file_names)] - file_dict = {e: i for i, e in enumerate(file_names)} + file_dict = {} + for i, e in enumerate(file_names): + if not e in file_dict: + file_dict[e] = [i] + else: + file_dict[e].append(i) - def update_entry(filename, data): - if filename in file_names: - any_p[file_dict[filename]] = {**any_p[file_dict[filename]], **data} + def update_entry(filename, data, n = 1): + count = sum(1 for entry in any_p if entry["filename"] == filename) + if n <= count and n > 0: + any_p[file_dict[filename][n - 1]] = {**any_p[file_dict[filename][n - 1]], **data} # ordon gate clip - update_entry("ordon_gate_clip", { + update_entry("ordon_gate_clip", n = 1, data = { 'requirements': Requirements.POS | Requirements.CAM, 'pos': (827.450012, 216.490097, -4533.90625), 'angle': 498, @@ -172,12 +178,16 @@ def update_entry(filename, data): }) if args.platform is Platform.GCN: - any_p[1]["requirements"] = Requirements.POS | Requirements.CAM - any_p[1]["pos"] = (466.622467, 319.770752, -11651.3867) - any_p[1]["angle"] = 52540 - any_p[1]["cam"]["pos"] = (735.525391, 524.418701, -11576.4746) - any_p[1]["cam"]["target"] = (465.674622, 421.052704, -11651.0684) - any_p[1]["counter"] = 10 + update_entry("ordon_gate_clip", n = 2, data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (466.622467, 319.770752, -11651.3867), + 'angle': 52540, + 'cam': { + 'pos': (735.525391, 524.418701, -11576.4746), + 'target': (465.674622, 421.052704, -11651.0684) + }, + 'counter': 10 + }) # back in time update_entry("bit", { diff --git a/external/misc/anyb.py b/external/misc/anyb.py index 1fb2ff16..2f6801da 100644 --- a/external/misc/anyb.py +++ b/external/misc/anyb.py @@ -1,171 +1,350 @@ +import copy +import sys +import argparse import struct +from enum import IntEnum, unique -REQ_POS = 1 -REQ_CAM = 2 - -default_entry = { - "requirements": 0, - "pos": (0.0,0.0,0.0), - "angle": 0, - "cam": {"pos":(0,0,0), "target": (0,0,0)}, - "counter": 0, -} - -# order matters -file_names = [ - "ordon_gate_clip", - "ordon_gate_clip", - "goats", - "hugo", - "faron_twilight", - "ems", - "purple_mist", - "forest_bit", - "forest_escape", - "lanayru_gate_clip", - "pillar_clip", - "lakebed_1", - "deku_toad", - "karg", - "kb1", - "eldin_twilight", - "lanayru_twilight", - "waterfall_sidehop", - "iza", - "spr_warp", - "spr", - "darkhammer", - "lakebed_bk_skip", - "onebomb", - "mdh_tower", - "mdh_bridge", - "camp", - "ag", - "poe_1_skip", - "death_sword_skip", - "stallord", - "stallord", - "silver_rupee", - "cits_early", - "cits_1", - "aeralfos_skip", - "fan_tower", - "argorok", - "palace_1", - "palace_2", - "early_platform", - "zant", - "hc", - "hc_tower", - "beast_ganon", - "horseback_ganon", -] - -anyb_p = [{**default_entry, "id": i, "filename": file_names[i]} for i in range(46)] - -# ordon gate clip -anyb_p[0]["requirements"] = REQ_POS | REQ_CAM -anyb_p[0]["pos"] = (827.450012, 216.490097, -4533.90625) -anyb_p[0]["angle"] = 498 -anyb_p[0]["cam"]["pos"] = (833.467468, 477.604675, -4241.97266) -anyb_p[0]["cam"]["target"] = (827.497559, 329.622986, -4532.90723) -anyb_p[0]["counter"] = 10 - -# back in time -anyb_p[1]["requirements"] = REQ_POS | REQ_CAM -anyb_p[1]["pos"] = (466.622467, 319.770752, -11651.3867) -anyb_p[1]["angle"] = 52540 -anyb_p[1]["cam"]["pos"] = (735.525391, 524.418701, -11576.4746) -anyb_p[1]["cam"]["target"] = (465.674622, 421.052704, -11651.0684) -anyb_p[1]["counter"] = 10 - -# hugo -anyb_p[3]["requirements"] = REQ_POS | REQ_CAM -anyb_p[3]["pos"] = (701.797302, 85.5212784, -5299.6123) -anyb_p[3]["angle"] = 63622 -anyb_p[3]["cam"]["pos"] = (735.525391, 524.418701, -11576.4746) -anyb_p[3]["cam"]["target"] = (465.674622, 421.052704, -11651.0684) - -# purple mist -anyb_p[6]["requirements"] = REQ_POS -anyb_p[6]["pos"] = (-23524.6152, 250.0, -16220.166) -anyb_p[6]["angle"] = 40758 -anyb_p[6]["counter"] = 30 - -# forest escape -anyb_p[8]["requirements"] = REQ_POS | REQ_CAM -anyb_p[8]["pos"] = (-12433.6016, -235.969193, -17103.998) -anyb_p[8]["angle"] = 29553 -anyb_p[8]["cam"]["pos"] = (-12552.8252, -53.5801048, -16729.5313) -anyb_p[8]["cam"]["target"] = (-12433.2979, -106.667023, -17104.9512) -anyb_p[8]["counter"] = 30 - -# lanayru gate clip -anyb_p[9]["requirements"] = REQ_POS | REQ_CAM -anyb_p[9]["pos"] = (-63026.2852, -9065.92578, 71680.3438) -anyb_p[9]["angle"] = 44248 -anyb_p[9]["cam"]["pos"] = (-62655.8125, -8900.91309, 71903.6328) -anyb_p[9]["cam"]["target"] = (-63064.2148, -8969.97656, 71661.0781) -anyb_p[9]["counter"] = 15 - -# eldin twilight -anyb_p[15]["requirements"] = REQ_POS | REQ_CAM -anyb_p[15]["pos"] = (455.088379, -150.0, 11516.7227) -anyb_p[15]["angle"] = 6058 -anyb_p[15]["cam"]["pos"] = (219.367218, -20.1253014, 11157.582) -anyb_p[15]["cam"]["target"] = (482.515137, -39.9999771, 11558.5283) -anyb_p[15]["counter"] = 10 - -# iza -anyb_p[18]["requirements"] = REQ_POS -anyb_p[18]["pos"] = (5979.97217, 150.0, -2748.34155) -anyb_p[18]["angle"] = 10114 - -# snowpeak messenger skip -anyb_p[19]["requirements"] = REQ_POS | REQ_CAM -anyb_p[19]["pos"] = (-9294.87988, 980.0, -11712.3838) -anyb_p[19]["angle"] = 346 -anyb_p[19]["cam"]["pos"] = (-9309.65137, 1280.4469, -12130.7695) -anyb_p[19]["cam"]["target"] = (-9294.2207, 1180.0, -11692.3945) -anyb_p[19]["counter"] = 10 - -# spr -anyb_p[20]["requirements"] = REQ_POS -anyb_p[20]["pos"] = (0.0, -150.0, 6000.0) -anyb_p[20]["angle"] = 33768 - -# bk skip -anyb_p[22]["requirements"] = REQ_POS | REQ_CAM -anyb_p[22]["pos"] = (71.9835968, 1500.00, 2839.01587) -anyb_p[22]["angle"] = 32767 -anyb_p[22]["cam"]["pos"] = (71.9835968, 1719.93542, 2969.04565) -anyb_p[22]["cam"]["target"] = (71.9835968, 1660.0, 2839.01587) -anyb_p[22]["counter"] = 30 - -# morpheel -anyb_p[23]["requirements"] = REQ_POS | REQ_CAM -anyb_p[23]["pos"] = (-1193.0, -23999.00, -770.0) -anyb_p[23]["angle"] = 10754 -anyb_p[23]["counter"] = 20 - -# poe 1 skip -anyb_p[28]["requirements"] = REQ_POS | REQ_CAM -anyb_p[28]["pos"] = (-2046.97168, 0.0, -587.304871) -anyb_p[28]["angle"] = 49030 -anyb_p[28]["cam"]["pos"] = (-1779.00293, 213.707397, -584.686768) -anyb_p[28]["cam"]["target"] = (-2047.97168, 130.16568, -587.317139) -anyb_p[28]["counter"] = 10 - -file = open("any_bite.bin", "wb") - -for entry in anyb_p: - print(entry) - file.write(entry["requirements"].to_bytes(1, "big", signed=False)) - file.write(int(0).to_bytes(1, "big", signed=False)) # padding - file.write(entry["angle"].to_bytes(2, "big", signed=False)) - file.write(struct.pack('>fff', *entry["pos"])) - file.write(struct.pack('>fff', *entry["cam"]["pos"])) - file.write(struct.pack('>fff', *entry["cam"]["target"])) - file.write(entry["counter"].to_bytes(4, "big", signed=False)) - file.write(struct.pack(">32s", entry["filename"].encode("ascii"))) - file.write(int(0).to_bytes(4, "big", signed=False)) # padding +@unique +class Platform(IntEnum): + GCN = 0 + WII = 1 + + +class Requirements(IntEnum): + POS = 1 + CAM = 2 + +def main(args=None): + parser = argparse.ArgumentParser( + sys.argv[0], description="A tool to generate the metadata file for the any% BiTE save files.") + parser.add_argument( + "-p", "--platform", type=str.upper, choices=[e.name for e in Platform], default=Platform.GCN.name, help="The platform to generate for.") + args = parser.parse_args() + + args.platform = Platform[args.platform] + + default_entry = { + "requirements": 0, + "pos": (0.0,0.0,0.0), + "angle": 0, + "cam": {"pos":(0,0,0), "target": (0,0,0)}, + "counter": 0, + } + + # order matters + file_names = [ + "ordon_gate_clip", + "ordon_gate_clip", + "goats", + "hugo", + "faron_twilight", + "ems", + "purple_mist", + "forest_bit", + "forest_escape", + "lanayru_gate_clip", + "pillar_clip", + "lakebed_1", + "deku_toad", + "karg", + "kb1", + "eldin_twilight", + "lanayru_twilight", + "waterfall_sidehop", + "iza", + "spr_warp", + "spr", + "darkhammer", + "lakebed_bk_skip", + "onebomb", + "mdh_tower", + "mdh_bridge", + "camp", + "ag", + "poe_1_skip", + "death_sword_skip", + "stallord", + "stallord", + "silver_rupee", + "cits_early", + "cits_1", + "aeralfos_skip", + "fan_tower", + "argorok", + "palace_1", + "palace_2", + "early_platform", + "zant", + "hc", + "hc_tower", + "beast_ganon", + "horseback_ganon", + ] + + if args.platform is Platform.WII: + # order matters + file_names = [ + "ordon_gate_clip", + "ordon_gate_clip", + "seam_clip", + "goats", + "hugo", + "faron_twilight", + "ems", + "purple_mist", + "kb1", + "eldin_twilight", + "bombhouse_skip", + "epona_oob_to_flight_by_fowl", + "lanayru_twilight", + "waterfall_sidehop", + "boss_bug", + "iza", + "plumm_oob", + "enter_lakebed", + "lakebed_1", + "deku_toad", + "morpheel", + "mdh_tower", + "mdh_bridge", + "messenger_skip", + "snowpeak_ruins_mbbb", + "freezard_skip", + "dark_hammer", + "bulblin_camp", + "ag", + "poe_1_skip", + "early_boss_key", + "death_sword", + "stallord", + "stallord", + "early_city", + "cits", + "arealfos", + "cits_2", + "fan_tower", + "argorok", + "pot1", + "stupidroom", + "pot2", + "earlypf", + "zant", + "hc", + "darknut", + "towerclimb", + "beast_ganon", + "horseback", + ] + + anyb_p = [{**copy.deepcopy(default_entry), "id": i, "filename": file_names[i]} for i in range(len(file_names))] + + file_dict = {} + for i, e in enumerate(file_names): + if not e in file_dict: + file_dict[e] = [i] + else: + file_dict[e].append(i) + + def update_entry(filename, data, n = 1): + count = sum(1 for entry in anyb_p if entry["filename"] == filename) + if n <= count and n > 0: + anyb_p[file_dict[filename][n - 1]] = {**anyb_p[file_dict[filename][n - 1]], **data} + + # ordon gate clip + update_entry("ordon_gate_clip", n = 1, data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (827.450012, 216.490097, -4533.90625), + 'angle': 498, + 'cam': {'pos': (833.467468, 477.604675, -4241.97266), 'target': (827.497559, 329.622986, -4532.90723)}, + 'counter': 10, + }) + + # back in time + update_entry("ordon_gate_clip", n = 2, data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (466.622467, 319.770752, -11651.3867), + 'angle': 52540, + 'cam': {'pos': (735.525391, 524.418701, -11576.4746), 'target': (465.674622, 421.052704, -11651.0684)}, + 'counter': 10, + }) + + # hugo + update_entry("hugo", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (701.797302, 85.5212784, -5299.6123), + 'angle': 63622, + 'cam': {'pos': (735.525391, 524.418701, -11576.4746), 'target': (465.674622, 421.052704, -11651.0684)}, + }) + + # purple mist + update_entry("purple_mist", data = { + 'requirements': Requirements.POS, + 'pos': (-23524.6152, 250.0, -16220.166), + 'angle': 40758, + 'counter': 30, + }) + + # king bulblin 1 + if args.platform is Platform.WII: + update_entry("kb1", data = { + 'requirements': Requirements.POS, + 'pos': (-9717.6035, 337.0316, 97.9661), + 'angle': 16384, + 'counter': 30, + }) + + # boss bug + if args.platform is Platform.WII: + update_entry("boss_bug", data = { + 'requirements': Requirements.POS, + 'pos': (-87517.1562, -18789.2812, 38927.0820), + 'angle': 41851, + 'counter': 30, + }) + + # plumm oob + if args.platform is Platform.WII: + update_entry("plumm_oob", data = { + 'requirements': Requirements.POS, + 'pos': (-104271.3750, -18470.0, 52661.7812), + 'angle': 45103, + 'counter': 30, + }) + + # mdh tower + if args.platform is Platform.WII: + update_entry("mdh_tower", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (25362.3184, -3028.7673, 10060.8379), + 'angle': 29327, + 'counter': 30, + }) + + # mdh bridge + if args.platform is Platform.WII: + update_entry("mdh_bridge", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (13050.0, 9825.0, 36202.0), + 'angle': 32768, + 'counter': 30, + }) + + # freezard skip + if args.platform is Platform.WII: + update_entry("freezard_skip", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-1125.0, 0.0, -1275.0), + 'angle': 32768, + 'counter': 30, + }) + + # dark hammer + if args.platform is Platform.WII: + update_entry("dark_hammer", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (0.7448, 0.0, 1330.9711), + 'angle': 32768, + 'counter': 20, + }) + + # forest escape + update_entry("forest_escape", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-12433.6016, -235.969193, -17103.998), + 'angle': 29553, + 'cam': {'pos': (-12552.8252, -53.5801048, -16729.5313), 'target': (-12433.2979, -106.667023, -17104.9512)}, + 'counter': 30, + }) + + # lanayru gate clip + update_entry("lanayru_gate_clip", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-63026.2852, -9065.92578, 71680.3438), + 'angle': 44248, + 'cam': {'pos': (-62655.8125, -8900.91309, 71903.6328), 'target': (-63064.2148, -8969.97656, 71661.0781)}, + 'counter': 15, + }) + + # eldin twilight + update_entry("eldin_twilight", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (455.088379, -150.0, 11516.7227), + 'angle': 6058, + 'cam': {'pos': (219.367218, -20.1253014, 11157.582), 'target': (482.515137, -39.9999771, 11558.5283)}, + 'counter': 10, + }) + + # waterfall sidehop + if args.platform is Platform.WII: + update_entry("waterfall_sidehop", data = { + 'requirements': Requirements.POS, + 'pos': (1169.5876, 12.6414, -1114.5820), + 'angle': 0, + 'counter': 10, + }) + + # iza + update_entry("iza", data = { + 'requirements': Requirements.POS, + 'pos': (5979.97217, 150.0, -2748.34155), + 'angle': 10114, + }) + + # snowpeak messenger skip + update_entry("spr_warp", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-9294.87988, 980.0, -11712.3838), + 'angle': 346, + 'cam': {'pos': (-9309.65137, 1280.4469, -12130.7695), 'target': (-9294.2207, 1180.0, -11692.3945)}, + 'counter': 10, + }) + + # spr + update_entry("spr", data = { + 'requirements': Requirements.POS, + 'pos': (0.0, 150.0, 6000.0), + 'angle': 33768, + }) + + # bk skip + update_entry("lakebed_bk_skip", data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (71.9835968, 1500.0, 2839.01587), + 'angle': 32767, + 'cam': {'pos': (71.9835968, 1719.93542, 2969.04565), 'target': (71.9835968, 1660.0, 2839.01587)}, + 'counter': 30, + }) + + # morpheel + update_entry('onebomb', data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-1193.0, -23999.00, -770.0), + 'angle': 10754, + 'counter': 20, + }) + + # poe 1 skip + update_entry('poe_1_skip', data = { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-2046.97168, 0.0, -587.304871), + 'angle': 49030, + 'cam': {'pos': (-1779.00293, 213.707397, -584.686768), 'target': (-2047.97168, 130.16568, -587.317139)}, + 'counter': 10, + }) + + file = open("any_bite.bin", "wb") + + for entry in anyb_p: + print(entry) + file.write(entry["requirements"].to_bytes(1, "big", signed=False)) + file.write(int(0).to_bytes(1, "big", signed=False)) # padding + file.write(entry["angle"].to_bytes(2, "big", signed=False)) + file.write(struct.pack('>fff', *entry["pos"])) + file.write(struct.pack('>fff', *entry["cam"]["pos"])) + file.write(struct.pack('>fff', *entry["cam"]["target"])) + file.write(entry["counter"].to_bytes(4, "big", signed=False)) + file.write(struct.pack(">32s", entry["filename"].encode("ascii"))) + file.write(int(0).to_bytes(4, "big", signed=False)) # padding + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/external/misc/dat2qlogs.py b/external/misc/dat2qlogs.py new file mode 100755 index 00000000..e04f1e13 --- /dev/null +++ b/external/misc/dat2qlogs.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import argparse, os, re, struct, sys + +def main(): + parser = argparse.ArgumentParser(description="Parses out quest logs from a Twilight Princess savefiles") + parser.add_argument("wsd", help="A valid twilight princess save data file extracted from a savefile (data.bin)") + wsdbytes = None + args = parser.parse_args() + file_in = args.wsd + with open(file_in,"rb") as wsdfile: + wsdbytes = bytearray(wsdfile.read()) + + qlog1 = wsdbytes[0x0008:0x0A9B] + qlog2 = wsdbytes[0x0A9C:0x152F] + qlog3 = wsdbytes[0x1530:0x1FC3] + + with open("qlog1.bin", "wb") as outfile: + outfile.write(qlog1) + + with open("qlog2.bin", "wb") as outfile: + outfile.write(qlog2) + + with open("qlog3.bin", "wb") as outfile: + outfile.write(qlog3) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/external/misc/gci2qlogs.py b/external/misc/gci2qlogs.py new file mode 100755 index 00000000..681c9b1e --- /dev/null +++ b/external/misc/gci2qlogs.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import argparse, os, re, struct, sys + +def main(): + parser = argparse.ArgumentParser(description="Parses out quest logs from a Twilight Princess GCI") + parser.add_argument("gci", help="A valid twilight princess GCI file") + gcibytes = None + args = parser.parse_args() + file_in = args.gci + with open(file_in,"rb") as gcifile: + gcibytes = bytearray(gcifile.read()) + + qlog1 = gcibytes[0x4048:0x4AD4] + qlog2 = gcibytes[0x4ADC:0x5568] + qlog3 = gcibytes[0x5570:0x5FFC] + + with open("qlog1.bin", "w") as outfile: + outfile.write(qlog1) + + with open("qlog2.bin", "w") as outfile: + outfile.write(qlog2) + + with open("qlog3.bin", "w") as outfile: + outfile.write(qlog3) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/external/misc/hundo.py b/external/misc/hundo.py index 968651bb..fcc379c5 100644 --- a/external/misc/hundo.py +++ b/external/misc/hundo.py @@ -1,235 +1,292 @@ +#!/usr/bin/python3 + +""" +Generates the metadata file for the 100% save files. +""" + +import sys +import argparse +import copy +from enum import IntEnum, unique import struct -REQ_POS = 1 -REQ_CAM = 2 - -default_entry = { - "requirements": 0, - "pos": (0.0,0.0,0.0), - "angle": 0, - "cam": {"pos":(0,0,0), "target": (0,0,0)}, - "counter": 0, -} - -# order matters -file_names = [ - "goats", - "ordon_gate_clip", - "goats_2", - "faron_twilight", - "ems", - "purple_mist", - "forest_bit", - "forest_2", - "ookless", - "eldin_twilight", - "bomb_house_skip", - "lanayru_gate_clip", - "pillar_clip", - "lakebed_1", - "deku_toad", - "karg", - "lanayru_twilight", - "boss_bug", - "kb2", - "kb2", - "corotd", - "gm", - "dangoro", - "post_gm", - "lakebed_bk_skip", - "morpheel", - "mdh_tower", - "mdh_bridge", - "post_mdh", - "star_1", - "kb1", # new - "iza_1_skip", - "lh_cave", - "camp", - "ag", - "poe_1_skip", - "death_sword_skip", - "stallord", - "post_ag", - "spr", - "darkhammer", - "spr_superjump", - "spr_bk_lja", - "spr_bk_lja", - "blizzeta", - "nf_bomb_boost", - "grove_skip", - "grove_boost", - "tot", - "tot_early_poe", - "tot_statue_throws", - "tot_early_hp", - "tot_darknut", - "dot_skip", - "post_tot", - "hotspring", - "gorge_arc", # new - "ice_puzzle", - "hv_archery", - "cits_1", - "aeralfos_skip", - "cits_2", - "cits_poe_cycle", - "fan_tower", - "argorok", - "star_2", - "palace_1", - "palace_2", - "early_platform", - "zant", - "coo", - "coo_10", - "coo_20", - "coo_30", - "coo_40", - "cats", - "hc", - "hc_darknut", - "hc_tower", - "beast_ganon", - "horseback_ganon", -] - -hundo_p = [{**default_entry, "id": i, "filename": file_names[i]} for i in range(81)] - -# ordon gate clip -hundo_p[1]["requirements"] = REQ_POS | REQ_CAM -hundo_p[1]["cam"]["target"] = 827.497559, 329.622986, -4532.90723 -hundo_p[1]["cam"]["pos"] = 833.467468, 477.604675, -4241.97266 -hundo_p[1]["angle"] = 498 -hundo_p[1]["pos"] = 827.450012, 216.490097, -4533.90625 -hundo_p[1]["counter"] = 10 - -# purple mist -hundo_p[5]["requirements"] = REQ_POS -hundo_p[5]["angle"] = 40758 -hundo_p[5]["pos"] = -23524.6152, 250.0, -16220.166 -hundo_p[5]["counter"] = 30 - -# lanayru gate clip -hundo_p[11]["requirements"] = REQ_POS | REQ_CAM -hundo_p[11]["cam"]["target"] = -63064.2148, -8969.97656, 71661.0781 -hundo_p[11]["cam"]["pos"] = -62655.8125, -8900.91309, 71903.6328 -hundo_p[11]["angle"] = 44248 -hundo_p[11]["pos"] = -63026.2852, -9065.92578, 71680.3438 -hundo_p[11]["counter"] = 15 - -# boss bug -hundo_p[17]["requirements"] = REQ_POS -hundo_p[17]["angle"] = 21504 -hundo_p[17]["pos"] = -89100.00, -18811.2363, 39410.00 - -# kb2 -hundo_p[18]["requirements"] = REQ_POS | REQ_CAM -hundo_p[18]["cam"]["target"] = -92098.1797, -5398.54883, 22599.9102 -hundo_p[18]["cam"]["pos"] = -92795.1328, -5302.87988, 22505.3359 -hundo_p[18]["angle"] = 14957 -hundo_p[18]["counter"] = 30 - -# coro td -hundo_p[20]["requirements"] = REQ_POS -hundo_p[20]["angle"] = 27714 -hundo_p[20]["pos"] = -13715.0712, 0.00, -14238.0654 - -# lakebed bk skip -hundo_p[24]["requirements"] = REQ_POS | REQ_CAM -hundo_p[24]["cam"]["target"] = 71.9835968, 1660.0, 2839.01587 -hundo_p[24]["cam"]["pos"] = 71.9835968, 1719.93542, 2969.04565 -hundo_p[24]["angle"] = 32767 -hundo_p[24]["pos"] = 71.9835968, 1500.00, 2839.01587 -hundo_p[24]["counter"] = 30 - -# mdh tower -hundo_p[26]["requirements"] = REQ_POS | REQ_CAM -hundo_p[26]["cam"]["target"] = 25256.7285, -2919.95215, 2839.01587 -hundo_p[26]["cam"]["pos"] = 10193.6064, 25254.7852, -2874.2627 -hundo_p[26]["angle"] = 32025 -hundo_p[26]["pos"] = 25254.6875, -3031.50854, 10222.1445 -hundo_p[26]["counter"] = 15 - -# poe 1 skip -hundo_p[35]["requirements"] = REQ_POS | REQ_CAM -hundo_p[35]["cam"]["target"] = -2047.97168, 130.16568, -587.317139 -hundo_p[35]["cam"]["pos"] = -1779.00293, 213.707397, -584.686768 -hundo_p[35]["angle"] = 49030 -hundo_p[35]["pos"] = -2046.97168, 0.0, -587.304871 -hundo_p[35]["counter"] = 10 - -# spr superjump -hundo_p[41]["requirements"] = REQ_POS | REQ_CAM -hundo_p[41]["cam"]["target"] = 1529.35425, 466.16306, 3684.08252 -hundo_p[41]["cam"]["pos"] = 1765.20581, 691.830688, 3662.42749 -hundo_p[41]["angle"] = 50120 -hundo_p[41]["pos"] = 1530.35, 359.56, 3683.99 -hundo_p[41]["counter"] = 30 - -# spr bk lja -hundo_p[42]["requirements"] = REQ_POS -hundo_p[42]["angle"] = 32887 -hundo_p[42]["pos"] = -2171.19, 973.96, -2384.89 -hundo_p[42]["counter"] = 30 - -# grove skip -hundo_p[46]["requirements"] = REQ_POS -hundo_p[46]["cam"]["target"] = -9965.82617, 2176.59863, 4084.57056 -hundo_p[46]["cam"]["pos"] = -10415.2363, 2212.92139, 4370.72852 -hundo_p[46]["angle"] = 22306 -hundo_p[46]["pos"] = -9966.6689, 2000.0, 4085.1082 -hundo_p[46]["counter"] = 30 - -# grove boost -hundo_p[47]["requirements"] = REQ_POS -hundo_p[47]["cam"]["target"] = -1765.32605, 1180.38452, 4303.98584 -hundo_p[47]["cam"]["pos"] = -1368.4314, 1347.8916, 4057.84863 -hundo_p[47]["angle"] = 54947 -hundo_p[47]["pos"] = -1764.4763, 1000.0, 4303.4585 -hundo_p[47]["counter"] = 30 - -# dot skip -hundo_p[53]["requirements"] = REQ_POS | REQ_CAM -hundo_p[53]["cam"]["target"] = 1361.59766, -33.1954155, -1090.47632 -hundo_p[53]["cam"]["pos"] = 1396.36316, 9.51973343, -719.644531 -hundo_p[53]["angle"] = 33673 -hundo_p[53]["pos"] = 1361.68408, -143.56076, -1089.4801 - -# hv archery -hundo_p[58]["requirements"] = REQ_POS -hundo_p[58]["angle"] = 64520 -hundo_p[58]["pos"] = 3125.57, -62.16, -9360.22 -hundo_p[58]["counter"] = 30 - -# city 1 -hundo_p[59]["requirements"] = REQ_POS | REQ_CAM -hundo_p[59]["cam"]["target"] = 1313.54285, -234.203003, 5545.16846 -hundo_p[59]["cam"]["pos"] = 1027.53259, -108.096123, 5605.23047 -hundo_p[59]["angle"] = 16384 -hundo_p[59]["pos"] = 1309.60645, -240.0, 5533.43848 -hundo_p[59]["counter"] = 10 - -# cats -hundo_p[75]["requirements"] = REQ_POS | REQ_CAM -hundo_p[75]["cam"]["target"] = 5309.32373, 160.1, -3581.83423 -hundo_p[75]["cam"]["pos"] = 4893.25391, 160.117676, -3524.51245 -hundo_p[75]["angle"] = 17282 -hundo_p[75]["pos"] = 5238.59, 0.00, -3575.74 -hundo_p[75]["counter"] = 30 - -file = open("hundo.bin", "wb") - -for entry in hundo_p: - print(entry) - file.write(entry["requirements"].to_bytes(1, "big", signed=False)) - file.write(int(0).to_bytes(1, "big", signed=False)) # padding - file.write(entry["angle"].to_bytes(2, "big", signed=False)) - file.write(struct.pack('>fff', *entry["pos"])) - file.write(struct.pack('>fff', *entry["cam"]["pos"])) - file.write(struct.pack('>fff', *entry["cam"]["target"])) - file.write(entry["counter"].to_bytes(4, "big", signed=False)) - file.write(struct.pack(">32s", entry["filename"].encode("ascii"))) - file.write(int(0).to_bytes(4, "big", signed=False)) # padding + +@unique +class Platform(IntEnum): + GCN = 0 + WII = 1 + + +class Requirements(IntEnum): + POS = 1 + CAM = 2 + + +def main(args=None): + parser = argparse.ArgumentParser( + sys.argv[0], description="A tool to generate the metadata file for the 100% save files.") + parser.add_argument( + "-p", "--platform", type=str.upper, choices=[e.name for e in Platform], default=Platform.GCN.name, help="The platform to generate for.") + args = parser.parse_args() + + args.platform = Platform[args.platform] + + default_entry = { + "requirements": 0, + "pos": (0.0, 0.0, 0.0), + "angle": 0, + "cam": {"pos": (0, 0, 0), "target": (0, 0, 0)}, + "counter": 0, + } + + # order matters + file_names = [ + "goats", + "ordon_gate_clip", + "goats_2", + "faron_twilight", + "ems", + "purple_mist", + "forest_bit", + "forest_2", + "ookless", + "eldin_twilight", + "bomb_house_skip", + "lanayru_gate_clip", + "pillar_clip", + "lakebed_1", + "deku_toad", + "karg", + "lanayru_twilight", + "corotd", + "early_ele", + "gm", + "dangoro", + "fyrus", + "waterfall_sidehop", + "boss_bug", + "kb2", + "kb2", + "eldin_collection", + "lakebed_bk_skip", + "morpheel", + "mdh_tower", + "mdh_bridge", + "post_mdh", + "star_1", + "kb1", + "iza_1_skip", + "lh_cave", + "camp", + "ag", + "poe_1_skip", + "death_sword_skip", + "stallord", + "post_ag", + "spr", + "darkhammer", + "spr_2", + "spr_bk_lja", + "spr_bk", + "blizzeta", + "nf_bomb_boost", + "grove_skip", + "grove_boost", + "tot", + "tot_early_poe", + "tot_statue_throws", + "tot_early_hp", + "tot_darknut", + "dot_skip", + "post_tot", + "hotspring", + "gorge_arc", + "silver_rupee", + "ice_puzzle", + "iza_2", + "hv_archery", + "cits_1", + "aeralfos_skip", + "cits_2", + "cits_poe_cycle", + "fan_tower", + "argorok", + "star_2", + "palace_1", + "palace_2", + "early_platform", + "zant", + "coo", + "coo_10", + "coo_20", + "coo_30", + "coo_40", + "cats", + "hc", + "hc_darknut", + "hc_tower", + "beast_ganon", + "horseback_ganon", + ] + + hundo_p = [{**copy.deepcopy(default_entry), "id": i, "filename": name} + for i, name in enumerate(file_names)] + + file_dict = {e: i for i, e in enumerate(file_names)} + + def update_entry(filename, data): + if filename in file_names: + hundo_p[file_dict[filename]] = {**hundo_p[file_dict[filename]], **data} + + update_entry("ordon_gate_clip", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (827.497559, 329.622986, -4532.90723), + 'angle': 498, + 'cam': { + 'pos': (833.467468, 477.604675, -4241.97266), + 'target': (827.497559, 329.622986, -4532.90723) + }, + 'counter': 10 + }) + + update_entry("purple_mist", { + 'requirements': Requirements.POS, + 'pos': (-23524.6152, 250.0, -16220.166), + 'angle': 40758, + 'counter': 30 + }) + + update_entry("lanayru_gate_clip", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-63026.2852, -9065.92578, 71680.3438), + 'angle': 44248, + 'cam': { + 'pos': (-62655.8125, -8900.91309, 71903.6328), + 'target': (-63064.2148, -8969.97656, 71661.0781) + }, + 'counter': 15 + }) + + update_entry("early_ele", { + 'requirements': Requirements.POS, + 'pos': (1197.00, -355.55, -5468.84), + 'angle': 45137, + }) + + update_entry("boss_bug", { + 'requirements': Requirements.POS, + 'pos': (-89100.00, -18811.2363, 39410.00), + 'angle': 21504, + }) + + update_entry("kb2", { + 'requirements': Requirements.POS | Requirements.CAM, + 'angle': 14957, + 'cam': { + 'pos': (-92795.1328, -5302.87988, 22505.3359), + 'target': (-92098.1797, -5398.54883, 22599.9102) + }, + 'counter': 30 + }) + + update_entry("corotd", { + 'requirements': Requirements.POS, + 'pos': (-13715.0712, 0.00, -14238.0654), + 'angle': 27714, + }) + + update_entry("lakebed_bk_skip", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (71.9835968, 1500.00, 2839.01587), + 'angle': 32767, + 'cam': { + 'pos': (71.9835968, 1719.93542, 2969.04565), + 'target': (71.9835968, 1660.0, 2839.01587) + }, + 'counter': 30 + }) + + update_entry("mdh_tower", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (25254.6875, -3031.50854, 10222.1445), + 'angle': 32025, + 'cam': { + 'pos': (10193.6064, 25254.7852, -2874.2627), + 'target': (25256.7285, -2919.95215, 2839.01587) + }, + 'counter': 15 + }) + + update_entry("poe_1_skip", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-2046.97168, 0.0, -587.304871), + 'angle': 49030, + 'cam': { + 'pos': (-1779.00293, 213.707397, -584.686768), + 'target': (-2047.97168, 130.16568, -587.317139) + }, + 'counter': 10 + }) + + update_entry("spr_bk_lja", { + 'requirements': Requirements.POS, + 'pos': (-2171.19, 973.96, -2384.89), + 'angle': 32887, + }) + + update_entry("grove_skip", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (-9966.6689, 2000.0, 4085.1082), + 'angle': 22306, + 'cam': { + 'pos': (-10415.2363, 2212.92139, 4370.72852), + 'target': (-9965.82617, 2176.59863, 4084.57056) + }, + 'counter': 30 + }) + + update_entry("hv_archery", { + 'requirements': Requirements.POS, + 'pos': (3125.57, -62.16, -9360.22), + 'angle': 64520, + }) + + update_entry("cits_1", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (1309.60645, -240.0, 5533.43848), + 'angle': 16384, + 'cam': { + 'pos': (1027.53259, -108.096123, 5605.23047), + 'target': (1313.54285, -234.203003, 5545.16846) + }, + 'counter': 10 + }) + + update_entry("cats", { + 'requirements': Requirements.POS | Requirements.CAM, + 'pos': (5238.59, 0.00, -3575.74), + 'angle': 17282, + 'cam': { + 'pos': (4893.25391, 160.117676, -3524.51245), + 'target': (5309.32373, 160.1, -3581.83423) + }, + 'counter': 30 + }) + + file = open("hundo.bin", "wb") + + for entry in hundo_p: + print(entry) + file.write(entry["requirements"].to_bytes(1, "big", signed=False)) + file.write(int(0).to_bytes(1, "big", signed=False)) # padding + file.write(entry["angle"].to_bytes(2, "big", signed=False)) + file.write(struct.pack('>fff', *entry["pos"])) + file.write(struct.pack('>fff', *entry["cam"]["pos"])) + file.write(struct.pack('>fff', *entry["cam"]["target"])) + file.write(entry["counter"].to_bytes(4, "big", signed=False)) + file.write(struct.pack(">32s", entry["filename"].encode("ascii"))) + file.write(int(0).to_bytes(4, "big", signed=False)) # padding + + +if __name__ == "__main__": + main(sys.argv) diff --git a/external/misc/meta.py b/external/misc/meta.py new file mode 100644 index 00000000..b2efdee6 --- /dev/null +++ b/external/misc/meta.py @@ -0,0 +1,49 @@ +import json +import os +import re + +# Load the meta.json file +with open('meta.json', 'r') as f: + meta_data = json.load(f) + +# Extract current patch files from the 'patches/' directory +patch_files = [f for f in os.listdir('patches') if os.path.isfile(os.path.join('patches', f))] + +# Define a regex pattern to extract SemVer from filenames (assuming the format contains semver) +semver_pattern = r'(\d+\.\d+\.\d+)(.*)' + +# Function to extract the SemVer and the part after it from a patch file name +def extract_semver_and_suffix(filename): + match = re.search(semver_pattern, filename) + if match: + semver = match.group(1) + suffix = match.group(2).lstrip('-').replace('-', '_').replace('.patch','') # Remove leading hyphen and replace hyphens with underscores + return semver, suffix + return None, None + +# Extract SemVer and suffix for all patch files +patch_versions = {extract_semver_and_suffix(patch) for patch in patch_files if extract_semver_and_suffix(patch)[0]} + +# Check if the SemVer exists in the meta.json data +def patch_exists_in_meta(semver): + return any(item['name'] == semver for item in meta_data) + +# Add new patches to meta.json if not already present +for semver, suffix in patch_versions: + if semver and not patch_exists_in_meta(semver): + # Find all patches with this version + version_patches = [p for p in patch_files if semver in p] + for patch in version_patches: + new_patch_entry = { + 'name': semver, + 'path': f'patches/{patch}', + 'version': suffix # Use the converted suffix + } + meta_data.append(new_patch_entry) + print(f"Added new patch version {semver}: {patch}") + +# Write updated meta.json back to disk +with open('meta.json', 'w') as f: + json.dump(meta_data, f, indent=2) + +print("meta.json has been updated.") \ No newline at end of file diff --git a/external/misc/nandpack.py b/external/misc/nandpack.py new file mode 100755 index 00000000..13dc0822 --- /dev/null +++ b/external/misc/nandpack.py @@ -0,0 +1,1788 @@ +#!/usr/bin/python3 + +"""Tool to pack/unpack Wii saves & inject REL modules.""" + +# Copyright 2021 kipcode66 & Seeky +# Based off of Dolphin Emulator Project https://github.com/dolphin-emu/dolphin.git +# Copyright 2010 Dolphin Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Based off of tachtig/twintig http://git.infradead.org/?p=users/segher/wii.git +# See also: https://wiibrew.org/wiki/Segher%27s_Wii.git +# Copyright 2007,2008 Segher Boessenkool +# Licensed under the terms of the GNU GPL, version 2 +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +# The binary data loaded in the save files is taken from the REL loader code from PistonMiner. +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) +# Modifications made by Zephiles + +from functools import reduce +import logging +import sys +import os +import argparse +import struct +import ctypes +from io import FileIO +from enum import IntEnum +from typing import Any, Dict, List +import hashlib +import secrets +import json +import base64 +from datetime import datetime, timezone +import re +from Crypto.Cipher import AES + +# +----------------------+ +# | Constants definition | +# +----------------------+ + +# Program Constants/global variables +VERSION = "0.2.2" + +# Constants +BLOCK_SZ = 0x40 +BNR_SZ = 0x60a0 +ICON_SZ = 0x1200 +FULL_BNR_MIN = 0x72a0 # BNR_SZ + 1*ICON_SZ +FULL_BNR_MAX = 0xF0A0 # BNR_SZ + 8*ICON_SZ +BK_LISTED_SZ = 0x70 +SIG_SZ = 0x40 +FULL_CERT_SZ = 0x3C0 + +DEFAULT_DEVICE_ID = 0x0403AC68 + +OS_BUS_CLOCK = 243000000 + +# Crypto values +SD_KEY = bytes([0xab, 0x01, 0xb9, 0xd8, + 0xe1, 0x62, 0x2b, 0x08, + 0xaf, 0xba, 0xd8, 0x4d, + 0xbf, 0xc2, 0xa5, 0x5d]) +SD_INITIAL_IV = bytes([0x21, 0x67, 0x12, 0xE6, + 0xAA, 0x1F, 0x68, 0x9F, + 0x95, 0xC5, 0xA2, 0x23, + 0x24, 0xDC, 0x6A, 0x98]) +MD5_BLANKER = bytes([0x0E, 0x65, 0x37, 0x81, + 0x99, 0xBE, 0x45, 0x17, + 0xAB, 0x06, 0xEC, 0x22, + 0x45, 0x1A, 0x57, 0x93]) +NG_ID = 0x0403AC68 +CA_ID = 1 +MS_ID = 2 + +DEFAULT_KEY_ID = 0x6AAB8C59 + +SYSTEM_MENU = 0x0000000100000002 + +DEFAULT_SIGNATURE = bytes([ + # R + 0x00, 0xD8, 0x81, 0x63, 0xB2, 0x00, 0x6B, 0x0B, 0x54, 0x82, + 0x88, 0x63, 0x81, 0x1C, 0x00, 0x71, 0x12, 0xED, 0xB7, 0xFD, + 0x21, 0xAB, 0x0E, 0x50, 0x0E, 0x1F, 0xBF, 0x78, 0xAD, 0x37, + # S + 0x00, 0x71, 0x8D, 0x82, 0x41, 0xEE, 0x45, 0x11, 0xC7, 0x3B, + 0xAC, 0x08, 0xB6, 0x83, 0xDC, 0x05, 0xB8, 0xA8, 0x90, 0x1F, + 0xA8, 0x2A, 0x0E, 0x4E, 0x76, 0xEF, 0x44, 0x72, 0x99, 0xF8 +]) + +DEFAULT_PRIVATE_KEY = bytes([0x00, 0xAB, 0xEE, 0xC1, 0xDD, 0xB4, + 0xA6, 0x16, 0x6B, 0x70, 0xFD, 0x7E, + 0x56, 0x67, 0x70, 0x57, 0x55, 0x27, + 0x38, 0xA3, 0x26, 0xC5, 0x46, 0x16, + 0xF7, 0x62, 0xC9, 0xED, 0x73, 0xF2]) + +# DEFAULT_PUBLIC_KEY = ec.privToPub(commondefs.DEFAULT_PRIVATE_KEY) +DEFAULT_PUBLIC_KEY = bytes([0x01, 0x04, 0x0b, 0xe0, 0x46, 0xea, + 0x95, 0x19, 0xf2, 0x85, 0x9b, 0x0d, + 0x94, 0x29, 0xa2, 0xc6, 0x91, 0x80, + 0x15, 0x89, 0x8f, 0x2e, 0xba, 0x20, + 0xcf, 0xfd, 0xb3, 0x16, 0x4f, 0x0c, + 0x01, 0x38, 0xc5, 0xd2, 0x2f, 0xc1, + 0xe9, 0xee, 0x17, 0x6c, 0x2d, 0x8f, + 0xa4, 0x74, 0xb0, 0xe9, 0x38, 0x66, + 0x6e, 0x60, 0xcf, 0x06, 0xd5, 0x08, + 0x7a, 0xc2, 0x4f, 0x01, 0x39, 0x79]) + +SQUARE = bytes([0x00, 0x01, 0x04, 0x05, + 0x10, 0x11, 0x14, 0x15, + 0x40, 0x41, 0x44, 0x45, + 0x50, 0x51, 0x54, 0x55]) + +SIGNATURE_END_MAGIC = 0x2f536969 + +# ACE data + +TIDS = {0x00010000525a4445: "us0", + 0x00010000525a4450: "eu", 0x00010000525a444a: "jp"} + +VERSIONS = {"us0": 0x00010000525a4445, "us2": 0x00010000525a4445, + "eu": 0x00010000525a4450, "jp": 0x00010000525a444a} + +GAME_INFO_PTR = { + # NA 1.0 + "us0": 0x80492928, + # NA 1.2 + "us2": 0x80479F30, + # PAL + "eu": 0x8047A828, + # JP + "jp": 0x80477DB0, +} + +BIN_DATA_INIT = { + # NA 1.0 + "us0": base64.b64decode("PICASYiEOEAchAqUOIQCCDyggEZgpYPgfYUiFH2JA6ZOgAQg"), + # NA 1.2 + "us2": base64.b64decode("PICASIiErkgchAqUOIQCCDyggERgpfngfYUiFH2JA6ZOgAQg"), + # PAL + "eu": base64.b64decode("PICASIiEt0AchAqUOIQCCDyggEVgpQLgfYUiFH2JA6ZOgAQg"), + # JP + "jp": base64.b64decode("PICASIiEjMgchAqUOIQCCDyggERgpdhgfYUiFH2JA6ZOgAQg"), +} + +BIN_DATA_MAIN = { + '1': { + # NA 1.0 + "us0": base64.b64decode("PYCANGGMovR9iAOmToAAITxggDRgY8V0SAAABXyIAqY4h" + "ABQSAABSYhtsmAsAwAAQYIAIIBtu0g4gAAFOKAAAT2AgC" + "phjPzcfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyp+H2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMYPYCAPGGMCRR9iAOmT" + "oAAIX+j63g4gAMYSAAA0ZOtv5A4fQMYkG2/lDxggDRgY3" + "f0Y6QA/EgAAJ09gIA0YYzFeH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuEduB8fxt4P8CAAGPDW/Q8gICwOKADG" + "EuMB+Vjw1v0OIADGEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CANGBjxXQ8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDRjpWGIfKgDpk6AACFjo2JsfGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2yZJ" + "Qh/1B8CAKmkAEAtL9BAAhINEUZfHobeD/ggAA7wAAAO6E" + "AIGPjXwR/pOt4OKAAAUg2wVUsAwAAQIIA5H+j63g4gAAA" + "OKAAAkg2ur0sAwAAQYAAzDuDAB9XnAA0f6PreDiAAAA4o" + "AAASDa6nSwDAABBgACsgG2zZH+E43g4oAAgSC3RbXx7G3" + "h/o+t4f2TbeH+F43hINrixLAMAAECBAFyAbbNsgJsAIIC" + "7AERILdFBfHwbeEg0RGF/Y9t4f4TjeEg0UXUsAwABQIIA" + "GEg0RF2Tn1S4k39UvIPbADRIAAAof2PbeEg0U51INERBg" + "G2zbH+E43hILdeFgG2zZH9k23hILdd5f6PreEg2wlEsHg" + "AAQYIADH/IA6ZOgAAhf0PTeEg0RB27QQAIgAEAtHwIA6Y" + "4IQCwSAAnSA=="), # 6D6F642E72656C00 + # NA 1.2 + "us2": base64.b64decode("PYCAM2GMTMR9iAOmToAAITxggDNgY29ESAAABXyIAqY4h" + "ABQSAABSYhtseAsAwAAQYIAIIBtuug4gAAFOKAAAT2AgC" + "phjPbwfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyqsH2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMYPYCAOmGMs0x9iAOmT" + "oAAIX+j63g4gAMYSAAA0ZOtvwg4fQMYkG2/DDxggDNgYy" + "HEY6QA/EgAAJ09gIAzYYxvSH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuDILB8fxt4P8CAAGPDW/Q8gICwOKADG" + "EuKsh1jw1v0OIADGEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CAM2Bjb0Q8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDNjpQtYfKgDpk6AACFjoww8fGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2x5J" + "Qh/1B8CAKmkAEAtL9BAAhIMu7pfHobeD/ggAA7wAAAO6E" + "AIGPjXwR/pOt4OKAAAUg1a40sAwAAQIIA5H+j63g4gAAA" + "OKAAAkg1ZPUsAwAAQYAAzDuDAB9XnAA0f6PreDiAAAA4o" + "AAASDVk1SwDAABBgACsgG2y5H+E43g4oAAgSCx7PXx7G3" + "h/o+t4f2TbeH+F43hINWLpLAMAAECBAFyAbbLsgJsAIIC" + "7AERILHsRfHwbeEgy7jF/Y9t4f4TjeEgy+0UsAwABQIIA" + "GEgy7i2Tn1S4k39UvIPbADRIAAAof2PbeEgy/W1IMu4Rg" + "G2y7H+E43hILIFVgG2y5H9k23hILIFJf6PreEg1bIksHg" + "AAQYIADH/IA6ZOgAAhf0PTeEgy7e27QQAIgAEAtHwIA6Y" + "4IQCwSAAnSA=="), # 6D6F642E72656C00 + # PAL + "eu": base64.b64decode("PYCAM2GMUPR9iAOmToAAITxggDNgY3N0SAAABXyIAqY4h" + "ABQSAABSYhtsKAsAwAAQYIAIIBtuag4gAAFOKAAAT2AgC" + "phjPsgfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyqoH2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMYPYCAOmGMt9B9iAOmT" + "oAAIX+j63g4gAMYSAAA0ZOtvcg4fQMYkG29zDxggDNgYy" + "X0Y6QA/EgAAJ09gIAzYYxzeH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuDJOB8fxt4P8CAAGPDW/Q8gICwOKADG" + "EuKtqFjw1v0OIADGEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CAM2Bjc3Q8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDNjpQ+IfKgDpk6AACFjoxBsfGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2wpJ" + "Qh/1B8CAKmkAEAtL9BAAhIMvMZfHobeD/ggAA7wAAAO6E" + "AIGPjXwR/pOt4OKAAAUg1b70sAwAAQIIA5H+j63g4gAAA" + "OKAAAkg1aSUsAwAAQYAAzDuDAB9XnAA0f6PreDiAAAA4o" + "AAASDVpBSwDAABBgACsgG2xpH+E43g4oAAgSCx/bXx7G3" + "h/o+t4f2TbeH+F43hINWcZLAMAAECBAFyAbbGsgJsAIIC" + "7AERILH9BfHwbeEgy8mF/Y9t4f4TjeEgy/3UsAwABQIIA" + "GEgy8l2Tn1S4k39UvIPbADRIAAAof2PbeEgzAZ1IMvJBg" + "G2xrH+E43hILIWFgG2xpH9k23hILIV5f6PreEg1cLksHg" + "AAQYIADH/IA6ZOgAAhf0PTeEgy8h27QQAIgAEAtHwIA6Y" + "4IQCwSAAnSA=="), # 6D6F642E72656C00 + # JP + "jp": base64.b64decode("PYCAM2GMZ+R9iAOmToAAITxggDNgY4pkSAAABXyIAqY4h" + "ABQSAABSYhtseAsAwAAQYIAIIBtutg4gAAFOKAAAT2AgC" + "thjBIQfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyrLH2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMYPYCAOmGMzgR9iAOmT" + "oAAIX+j63g4gAMYSAAA0ZOtvvg4fQMYkG2+/DxggDNgYz" + "zkY6QA/EgAAJ09gIAzYYyKaH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuDO9B8fxt4P8CAAGPDW/Q8gICwOKADG" + "EuKzNVjw1v0OIADGEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CAM2BjimQ8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDNjpSZ4fKgDpk6AACFjoydcfGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2x5J" + "Qh/1B8CAKmkAEAtL9BAAhIMwoJfHobeD/ggAA7wAAAO6E" + "AIGPjXwR/pOt4OKAAAUg1hkUsAwAAQIIA5H+j63g4gAAA" + "OKAAAkg1f60sAwAAQYAAzDuDAB9XnAA0f6PreDiAAAA4o" + "AAASDV/jSwDAABBgACsgG2y5H+E43g4oAAgSCyWXXx7G3" + "h/o+t4f2TbeH+F43hINX2hLAMAAECBAFyAbbLsgJsAIIC" + "7AERILJYxfHwbeEgzCVF/Y9t4f4TjeEgzFmUsAwABQIIA" + "GEgzCU2Tn1S4k39UvIPbADRIAAAof2PbeEgzGI1IMwkxg" + "G2y7H+E43hILJx1gG2y5H9k23hILJxpf6PreEg1h0EsHg" + "AAQYIADH/IA6ZOgAAhf0PTeEgzCQ27QQAIgAEAtHwIA6Y" + "4IQCwSAAnSA=="), # 6D6F642E72656C00 + }, + '2': { + # NA 1.0 + "us0": base64.b64decode("PYCANGGMovR9iAOmToAAITxggDRgY8V0SAAABXyIAqY4h" + "ABQSAABSYhtsmAsAwAAQYIAIIBtu0g4gAAFOKAAAT2AgC" + "phjPzcfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyp+H2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMsPYCAPGGMCRR9iAOmT" + "oAAIX+j63g4gAMsSAAA0ZOtv5A4fQMskG2/lDxggDRgY3" + "f0Y6QA/EgAAJ09gIA0YYzFeH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuEduB8fxt4P8CAAGPDW/Q8gICwOKADL" + "EuMB+Vjw1v0OIADLEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CANGBjxXQ8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDRjpWGIfKgDpk6AACFjo2JsfGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2yZJ" + "Qh/1B8CAKmkAEAtL9BAAhINEUZfHobeD/ggAA7wAAAO6E" + "AIGPjXxh/pOt4OKAAAUg2wVUsAwAAQIIA+H+j63g4gAAA" + "OKAAAkg2ur0sAwAAQYAA4DuDAB9XnAA0f6PreDiAAAA4o" + "AAASDa6nSwDAABBgADAgG2zZH+E43g4oAAgSC3RbXx7G3" + "h/o+t4f2TbeH+F43hINrixLAMAAECBAHCAbbNsgJsAIIC" + "7AERILdFBfHwbeEg0RGF/Y9t4f4TjeEg0UX0sAwABQIIA" + "LEg0RF2AuwBIfLsoUIBts2R/ZNt4SC3ZfZOfVLiTf1S8g" + "9sANEgAACh/Y9t4SDRTiUg0RC2AbbNsf4TjeEgt13GAbb" + "Nkf2TbeEgt12V/o+t4SDbCPSweAABBggAMf8gDpk6AACF" + "/Q9N4SDRECbtBAAiAAQC0fAgDpjghALBIACc0"), + # NA 1.2 + "us2": base64.b64decode("PYCAM2GMTMR9iAOmToAAITxggDNgY29ESAAABXyIAqY4h" + "ABQSAABSYhtseAsAwAAQYIAIIBtuug4gAAFOKAAAT2AgC" + "phjPbwfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyqsH2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMsPYCAOmGMs0x9iAOmT" + "oAAIX+j63g4gAMsSAAA0ZOtvwg4fQMskG2/DDxggDNgYy" + "HEY6QA/EgAAJ09gIAzYYxvSH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuDILB8fxt4P8CAAGPDW/Q8gICwOKADL" + "EuKsh1jw1v0OIADLEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CAM2Bjb0Q8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDNjpQtYfKgDpk6AACFjoww8fGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2x5J" + "Qh/1B8CAKmkAEAtL9BAAhIMu7pfHobeD/ggAA7wAAAO6E" + "AIGPjXxh/pOt4OKAAAUg1a40sAwAAQIIA+H+j63g4gAAA" + "OKAAAkg1ZPUsAwAAQYAA4DuDAB9XnAA0f6PreDiAAAA4o" + "AAASDVk1SwDAABBgADAgG2y5H+E43g4oAAgSCx7PXx7G3" + "h/o+t4f2TbeH+F43hINWLpLAMAAECBAHCAbbLsgJsAIIC" + "7AERILHsRfHwbeEgy7jF/Y9t4f4TjeEgy+00sAwABQIIA" + "LEgy7i2AuwBIfLsoUIBtsuR/ZNt4SCyDTZOfVLiTf1S8g" + "9sANEgAACh/Y9t4SDL9WUgy7f2AbbLsf4TjeEgsgUGAbb" + "Lkf2TbeEgsgTV/o+t4SDVsdSweAABBggAMf8gDpk6AACF" + "/Q9N4SDLt2btBAAiAAQC0fAgDpjghALBIACc0"), + # PAL + "eu": base64.b64decode("PYCAM2GMUPR9iAOmToAAITxggDNgY3N0SAAABXyIAqY4h" + "ABQSAABSYhtsKAsAwAAQYIAIIBtuag4gAAFOKAAAT2AgC" + "phjPsgfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyqoH2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMsPYCAOmGMt9B9iAOmT" + "oAAIX+j63g4gAMsSAAA0ZOtvcg4fQMskG29zDxggDNgYy" + "X0Y6QA/EgAAJ09gIAzYYxzeH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuDJOB8fxt4P8CAAGPDW/Q8gICwOKADL" + "EuKtqFjw1v0OIADLEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CAM2Bjc3Q8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDNjpQ+IfKgDpk6AACFjoxBsfGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2wpJ" + "Qh/1B8CAKmkAEAtL9BAAhIMvMZfHobeD/ggAA7wAAAO6E" + "AIGPjXxh/pOt4OKAAAUg1b70sAwAAQIIA+H+j63g4gAAA" + "OKAAAkg1aSUsAwAAQYAA4DuDAB9XnAA0f6PreDiAAAA4o" + "AAASDVpBSwDAABBgADAgG2xpH+E43g4oAAgSCx/bXx7G3" + "h/o+t4f2TbeH+F43hINWcZLAMAAECBAHCAbbGsgJsAIIC" + "7AERILH9BfHwbeEgy8mF/Y9t4f4TjeEgy/30sAwABQIIA" + "LEgy8l2AuwBIfLsoUIBtsaR/ZNt4SCyHfZOfVLiTf1S8g" + "9sANEgAACh/Y9t4SDMBiUgy8i2AbbGsf4TjeEgshXGAbb" + "Gkf2TbeEgshWV/o+t4SDVwpSweAABBggAMf8gDpk6AACF" + "/Q9N4SDLyCbtBAAiAAQC0fAgDpjghALBIACc0"), + # JP + "jp": base64.b64decode("PYCAM2GMZ+R9iAOmToAAITxggDNgY4pkSAAABXyIAqY4h" + "ABQSAABSYhtseAsAwAAQYIAIIBtutg4gAAFOKAAAT2AgC" + "thjBIQfYgDpk6AACE4YAAAPICAADigAAA9gIABYYyrLH2" + "JA6ZOgAQglCH/4HwIAqaQAQAkv6EACHx+G3h8nyN4P6CA" + "sH+j63hIAAAFfIgCpjiE/3A4oAMsPYCAOmGMzgR9iAOmT" + "oAAIX+j63g4gAMsSAAA0ZOtvvg4fQMskG2+/DxggDNgYz" + "zkY6QA/EgAAJ09gIAzYYyKaH2JA6Z/w/N4f+T7eLuhAAi" + "AAQAkfAgDpjghACBOgAQgfH8beDxggTNgYzqcPICAsGCE" + "ARxIAABdf+P7eEuDO9B8fxt4P8CAAGPDW/Q8gICwOKADL" + "EuKzNVjw1v0OIADLEgAAEk8YIAAYGOGRGPEXchIAAAhPG" + "CAM2BjimQ8gIAAYIRcYEgAAA1/4/t4SIM5OHyDIFBUhAG" + "6PKBIAHylI3iQowAAOIAABJQh/+B8CAKmkAEAJL+hAAh8" + "fxt4fJ4jeD+ggDNjpSZ4fKgDpk6AACFjoydcfGgDpn/j+" + "3h/xPN4ToAAIbuhAAiAAQAkfAgDpjghACBOgAAgkG2x5J" + "Qh/1B8CAKmkAEAtL9BAAhIMwoJfHobeD/ggAA7wAAAO6E" + "AIGPjXxh/pOt4OKAAAUg1hkUsAwAAQIIA+H+j63g4gAAA" + "OKAAAkg1f60sAwAAQYAA4DuDAB9XnAA0f6PreDiAAAA4o" + "AAASDV/jSwDAABBgADAgG2y5H+E43g4oAAgSCyWXXx7G3" + "h/o+t4f2TbeH+F43hINX2hLAMAAECBAHCAbbLsgJsAIIC" + "7AERILJYxfHwbeEgzCVF/Y9t4f4TjeEgzFm0sAwABQIIA" + "LEgzCU2AuwBIfLsoUIBtsuR/ZNt4SCyebZOfVLiTf1S8g" + "9sANEgAACh/Y9t4SDMYeUgzCR2AbbLsf4TjeEgsnGGAbb" + "Lkf2TbeEgsnFV/o+t4SDWHLSweAABBggAMf8gDpk6AACF" + "/Q9N4SDMI+btBAAiAAQC0fAgDpjghALBIACc0"), + } +} + +# +------------------+ +# | Common functions | +# +------------------+ + + +def alignUp(value: int, size: int): + """Aligns and address to the given size. + + :param value: The address to align up. + :type value: int + :param size: The size to which we align the address. + :type size: int + + :return The aligned address. + :rtype int + """ + return value + (size - value % size) % size + + +def sha1(data: bytes) -> bytes: + hasher = hashlib.sha1() + hasher.update(data) + return hasher.digest() + + +def md5(data: bytes) -> bytes: + hasher = hashlib.md5() + hasher.update(data) + return hasher.digest() + + +def aes_cbc_decrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + return AES.new(key, AES.MODE_CBC, iv).decrypt(data) + + +def aes_cbc_encrypt(key: bytes, iv: bytes, data: bytes) -> bytes: + return AES.new(key, AES.MODE_CBC, iv).encrypt(data) + +# +-------------------+ +# | Class definitions | +# +-------------------+ + +# Classes used to generate a new signature +# (Algorithm from the Dolphin Emulator project) + + +class Elt: + pass + + +class Elt: + __slots__ = ('data',) + + def __init__(self, data: bytes = bytes(30)): + self.data = bytes(data[0:min(30, len(data))]) + \ + bytes(max(0, 30 - len(data))) + + def is_zero(self): + return all(d == 0 for d in self.data) + + def mulX(self): + new_data = bytearray(self.data) + carry = new_data[0] & 1 + + # x = (self.data[29] << 1) & 0xFF + # xs = itertools.chain([0], ((y << 1) & 0xFF for y in self.data[1:-1])) + # new_data[:-1] = list((x ^ (y >> 7)) & 0xFF for x, y in zip(xs, self.data[1:])) + x = 0 + for i in range(29): + y = new_data[i + 1] + new_data[i] = (x ^ (y >> 7)) & 0xFF + x = (y << 1) & 0xFF + + new_data[29] = (x ^ carry) & 0xFF + new_data[20] ^= (carry << 2) & 0xFF + self.data = bytes(new_data) + + def square(self): + # wide = bytearray(y for x in ( + # (SQUARE[x >> 4], SQUARE[x & 0xF]) for x in self.data) for y in x) + wide = bytearray(60) + for i in range(30): + wide[2 * i] = SQUARE[self.data[i] >> 4] + wide[2 * i + 1] = SQUARE[self.data[i] & 0xF] + + for i in range(30): + x = wide[i] + wide[i + 19] ^= x >> 7 + wide[i + 20] ^= (x << 1) & 0xFF + wide[i + 29] ^= x >> 1 + wide[i + 30] ^= (x << 7) & 0xFF + x = wide[30] & ~1 + wide[49] ^= x >> 7 + wide[50] ^= (x << 1) & 0xFF + wide[59] ^= x >> 1 + wide[30] &= 1 + return Elt(wide[30:]) + + def ItohTsujii(self, b, j): + t = Elt(self.data) + for _ in range(j): + t = t.square() + return t * b + + def inv(self): + t = self.ItohTsujii(self, 1) + s = t.ItohTsujii(self, 1) + t = s.ItohTsujii(s, 3) + s = t.ItohTsujii(self, 1) + t = s.ItohTsujii(s, 7) + s = t.ItohTsujii(t, 14) + t = s.ItohTsujii(self, 1) + s = t.ItohTsujii(t, 29) + t = s.ItohTsujii(s, 58) + s = t.ItohTsujii(t, 116) + return s.square() + + def __add__(self, other): + if not isinstance(other, Elt): + raise TypeError( + f"Cannot add {self.__class__.__name__} " + f"with {other.__class__.__name__}") + return Elt(bytes(a ^ b for a, b in zip(self.data, other.data))) + + def __truediv__(self, other): + if not isinstance(other, Elt): + raise TypeError( + f"Cannot divide {self.__class__.__name__} " + f"with {other.__class__.__name__}") + return self * other.inv() + + def __mul__(self, other: Elt): + if not isinstance(other, Elt): + raise TypeError( + f"Cannot multiply {self.__class__.__name__} " + f"with {other.__class__.__name__}") + d = Elt() + i = 0 + mask = 1 + for _ in range(233): + d.mulX() + if (self.data[i] & mask) != 0: + d = d + other + mask >>= 1 + if mask == 0: + mask = 0x80 + i += 1 + return d + + +class Point: + __slots__ = ('x', 'y',) + + def __init__(self, x: Elt = Elt(), y: Elt = Elt()): + self.x = Elt(x.data) + self.y = Elt(y.data) + + def is_zero(self): + return self.x.is_zero() and self.y.is_zero() + + def double(self): + r = Point() + if self.x.is_zero(): + return r + s = (self.y / self.x) + # print(s.data.hex()) + s = s + self.x + r.x = s.square() + s + r_x_data = bytearray(r.x.data) + r_x_data[29] ^= 1 + r.x.data = bytes(r_x_data) + r.y = s * r.x + r.x + self.x.square() + return r + + def __add__(self, other): + if not isinstance(other, Point): + raise TypeError( + f"Cannot add {self.__class__.__name__} " + f"with {other.__class__.__name__}") + if self.is_zero(): + return Point(other.x, other.y) + if other.is_zero(): + return Point(self.x, self.y) + u = self.x + other.x + if u.is_zero(): + u = self.y + other.y + if u.is_zero(): + return self.double() + return Point() + s = (self.y + other.y) / u + t = s.square() + s + other.x + t_data = bytearray(t.data) + t_data[29] ^= 1 + t.data = bytes(t_data) + rx = t + self.x + ry = s * t + self.y + rx + return Point(rx, ry) + + def __mul__(self, other): + if not isinstance(other, bytes | bytearray | memoryview): + raise TypeError( + f"Point only multiplies with bytes, " + f"got {other.__class__.__name__}") + d = Point() + for i in range(30): + mask = 0x80 + while mask != 0: + d = d.double() + if (other[i] & mask) != 0: + d = d + self + mask >>= 1 + return d + + +EC_G = Point(Elt(bytes([0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, + 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1, + 0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, + 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, + 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b])), + Elt(bytes([0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, + 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, + 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef, + 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, + 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52]))) + +EC_N = bytes([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xe9, 0x74, 0xe7, 0x2f, + 0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7]) + + +class SignatureType(IntEnum): + RSA4096 = 0x00010000 + RSA2048 = 0x00010001 + ECC = 0x00010002 + + +class PublicKeyType(IntEnum): + RSA4096 = 0x00000000 + RSA2048 = 0x00000001 + ECC = 0x00000002 + + +class SignatureECC: + __slots__ = ('type', 'sig', 'fill', 'issuer',) + PACK_FORMAT = struct.Struct('>I60s64s64s') + + def __init__(self, type: SignatureType, sig, fill, issuer): + self.type = type + self.sig = sig + self.fill = fill + self.issuer = issuer + + def pack(self): + return SignatureECC.PACK_FORMAT.pack(self.type.value, self.sig, self.fill, self.issuer) + + +class CertHeader: + __slots__ = ('public_key_type', 'name', 'id',) + PACK_FORMAT = struct.Struct('>I64sI') + + def __init__(self, public_key_type: PublicKeyType, name, id): + self.public_key_type = public_key_type + self.name = name + self.id = id + + def pack(self): + return CertHeader.PACK_FORMAT.pack(self.public_key_type.value, self.name, self.id) + + +class CertECC: + __slots__ = ("signature", "header", "public_key", "padding",) + + def __init__(self, signature: SignatureECC, header: CertHeader, public_key: bytes, padding: bytes): + self.signature = signature + self.header = header + self.public_key = public_key + self.padding = padding + + def pack(self): + return self.signature.pack() + self.header.pack() + self.public_key + self.padding + +# +---------------------+ +# | Signature functions | +# +---------------------+ + + +def priv_to_pub(key: bytes): + data = EC_G * bytes(key) + return data.x.data + data.y.data + + +def make_blank_ecc_cert(issuer, name, public_key: bytes, key_id): + return CertECC(SignatureECC(SignatureType.ECC, bytes(60), bytes(0x40), bytes(issuer, 'utf-8')[0:min(0x3F, len(issuer))] + b'\0'), CertHeader(PublicKeyType.ECC, bytes(name, 'utf-8')[0:min(0x3F, len(name))] + b'\0', key_id), public_key, bytes(60)) + + +def get_device_certificate(device_id=DEFAULT_DEVICE_ID): + name = f"NG{device_id:08x}" + cert = make_blank_ecc_cert( + f"Root-CA{CA_ID:08x}-MS{MS_ID:08x}", name, DEFAULT_PUBLIC_KEY, DEFAULT_KEY_ID) + cert.signature.sig = DEFAULT_SIGNATURE + return cert + + +def bn_sub_modulus(a, N, n): + a = bytearray(a) + c = 0 + for i in range(n - 1, -1, -1): + dig = N[i] + c + c = a[i] < dig + a[i] = (a[i] - dig) & 0xFF + return a + + +def bn_add(a: bytes, b: bytes, N, n): + d = bytearray(n) + c = 0 + for i in range(n - 1, -1, -1): + dig = a[i] + b[i] + c + c = (dig >= 0x100) + d[i] = (dig) & 0xFF + + if c: + d = bn_sub_modulus(d, N, n) + if d >= N: + d = bn_sub_modulus(d, N, n) + return d + + +def bn_mul(a, b, N, n): + d = bytearray(n) + for i in range(n): + mask = 0x80 + while mask != 0: + d = bn_add(d, d, N, n) + if (a[i] & mask) != 0: + d = bn_add(d, b, N, n) + mask >>= 1 + return d + + +def bn_exp(a, N, n, e, en): + t = bytearray(512) + d = bytearray(n) + d[n - 1] = 1 + for i in range(en): + mask = 0x80 + while mask != 0: + t = bn_mul(d, d, N, n) + if (e[i] & mask) != 0: + d = bn_mul(t, a, N, n) + else: + d[:] = t[:n] + mask >>= 1 + return d + + +def bn_inv(a, N, n): + d = bytearray(n) + t = bytearray(512) + s = bytearray(512) + t[0:n] = N + s[n - 1] = 2 + t = bn_sub_modulus(t, s, n) + d = bn_exp(a, N, n, t, n) + return d + + +def sign2(key: bytes, hash: bytes): + e = bytearray(30) + e[10:] = hash + m = bytearray(secrets.token_bytes(30)) + m[0] &= 1 + while m >= EC_N: + m = bytearray(secrets.token_bytes(30)) + m[0] &= 1 + r = (EC_G * m).x + if r.data >= EC_N: + r.data = bytes(bn_sub_modulus(r.data, EC_N, 30)) + + kk = bytearray(30) + kk[:] = key + if kk >= EC_N: + kk = bn_sub_modulus(kk, EC_N, 30) + s = Elt(bn_mul(r.data, kk, EC_N, 30)) + kk = bn_add(s.data, e, EC_N, 30) + minv = bn_inv(m, EC_N, 30) + s.data = bytes(bn_mul(minv, kk, EC_N, 30)) + + signature = bytearray(60) + signature[:30] = r.data + signature[30:] = s.data + return bytes(signature) + + +def sign(title_id, data, device_id=DEFAULT_DEVICE_ID): + hash = bytearray(20) + ap_priv = bytearray(30) + ap_priv[0x1d] = 1 + + logging.debug(f"Signing for device #{device_id:08x}") + # In practice, we can reduce the encryption time by using a + # pre-calculated "cert", but it's not a significant amount of time + signer = f"Root-CA{CA_ID:08x}-MS{MS_ID:08x}-NG{device_id:08x}" + name = f"AP{title_id:016x}" + logging.debug("Creating certificate...") + cert = make_blank_ecc_cert(signer, name, priv_to_pub(ap_priv), 0) + cert_packed = cert.pack() + hash = sha1(cert_packed[0x80:]) + cert.signature.sig = sign2(DEFAULT_PRIVATE_KEY, hash) + cert = cert.pack() + + logging.debug("Signing data...") + hash = sha1(data) + signature = sign2(ap_priv, hash) + logging.debug("Signed") + + return (signature, cert) + + +def verify_signature(public_key, signature, hash): + r = signature[:30] + s = signature[30:] + + s_inv = bn_inv(s, EC_N, 30) + e = bytearray(30) + e[10:] = hash[:20] + + w1 = bn_mul(e, s_inv, EC_N, 30) + w2 = bn_mul(r, s_inv, EC_N, 30) + + public_key_point = Point(Elt(public_key[:30]), Elt(public_key[30:])) + r1 = EC_G * w1 + public_key_point * w2 + rx = r1.x.data + if rx >= EC_N: + rx = bn_sub_modulus(rx, EC_N, 30) + + return rx == r + + +class Header: + """The encryption header. + + It contains the necessary data for the save file's encryption and + signature. It wraps the archive that contains the game's saved + files. + """ + + __slots__ = ("tid", "banner_size", "permissions", + "unk1", "md5", "unk2", "banner",) + PACK_FORMAT = struct.Struct('>QIBB16sH') + PACK_SIZE = PACK_FORMAT.size + FULL_BNR_MAX + + def __init__(self, tid: int, banner_size: int, permissions: int, unk1: int, md5: bytes, unk2: int, banner=bytes()): + self.tid = tid + self.banner_size = banner_size + self.permissions = permissions + self.unk1 = unk1 + self.md5 = md5 + self.unk2 = unk2 + self.banner = banner + + def __repr__(self) -> str: + return (f"Header(tid=0x{self.tid:016x}, " + f"banner_size=0x{self.banner_size:08x}, " + f"permissions=0x{self.permissions:02x}, " + f"unk1=0x{self.unk1:02x}, " + f"md5=0x{self.md5.hex()}, " + f"unk2={self.unk2:04x}, " + f"banner={self.banner})") + + def __str__(self) -> str: + return (f"Header(tid=0x{self.tid:016x}, " + f"banner_size=0x{self.banner_size:08x}, " + f"permissions=0x{self.permissions:02x}, " + f"unk1=0x{self.unk1:02x}, " + f"md5=0x{self.md5.hex()}, " + f"unk2={self.unk2:04x}, " + f"banner={self.banner.__class__.__name__}({len(self.banner)}))") + + def updateBanner(self, banner: bytes): + """Updates the headers and sets the new banner. + + :param banner: The banner file + :type banner: bytes + """ + new_size = len(banner) + if (new_size < FULL_BNR_MIN) or (new_size > FULL_BNR_MAX) or (((new_size - BNR_SZ) % ICON_SZ) != 0): + logging.error(f"Invalid banner size {new_size:04x}") + self.banner_size = new_size + self.banner = banner + self.md5 = md5(self.to_bytes()) + + @staticmethod + def generate(banner: bytes, game_version: str): + """Generates a new header from a given banner for the given version + of 'The Legend of Zelda: Twilight Princess'. + """ + hdr = Header(VERSIONS[game_version], len( + banner), 0x34, 0, MD5_BLANKER, 0, banner) + hdr.md5 = md5(hdr.to_bytes()) + return hdr + + @staticmethod + def unpack(buffer: bytes): + data = aes_cbc_decrypt(SD_KEY, SD_INITIAL_IV, + buffer[0:Header.PACK_SIZE]) + hdr = Header( + *Header.PACK_FORMAT.unpack(data[0:Header.PACK_FORMAT.size])) + hdr.banner = data[Header.PACK_FORMAT.size:][0:hdr.banner_size] + if (hdr.banner_size < FULL_BNR_MIN) or (hdr.banner_size > FULL_BNR_MAX) or (((hdr.banner_size - BNR_SZ) % ICON_SZ) != 0): + logging.error( + "Warning: Not a Wii save or read failure for " + f"banner size 0x{hdr.banner_size:04x} ({hdr.banner_size})") + return + hdr_md5 = hdr.md5 + hdr.md5 = MD5_BLANKER + md5_calc = md5(hdr.to_bytes()) + hdr.md5 = hdr_md5 + if md5_calc != hdr_md5: + logging.error( + f"[Header] MD5 mismatch: {hdr_md5.hex()} != {md5_calc.hex()}") + return + return hdr + + @staticmethod + def from_file(reader: FileIO): + reader.seek(0) + return Header.unpack(reader.read(Header.PACK_SIZE)) + + def to_bytes(self): + data1 = Header.PACK_FORMAT.pack( + self.tid, self.banner_size, self.permissions, self.unk1, self.md5, self.unk2) + data2 = self.banner + if len(data2) < FULL_BNR_MAX: + data2 += bytes(FULL_BNR_MAX - self.banner_size) + return data1 + data2 + + def pack(self): + self.md5 = MD5_BLANKER + self.md5 = md5(self.to_bytes()) + return aes_cbc_encrypt(SD_KEY, SD_INITIAL_IV, self.to_bytes()) + + +class BkHeader: + """The archive header. + + This header contains the data related to the archival of + the files saved by the game. + """ + + __slots__ = ("size", "magic", "ngid", "number_of_files", "size_of_files", + "unk1", "unk2", "total_size", "unk3", "tid", "mac_address", + "padding",) + PACK_FORMAT = struct.Struct('>8I64sQ6s18s') + MAGIC = 0x426B0001 + + def __init__(self, size, magic, ngid, number_of_files, size_of_files, unk1, unk2, total_size, unk3, tid, mac_address, padding): + self.size = size + self.magic = magic + self.ngid = ngid + self.number_of_files = number_of_files + self.size_of_files = size_of_files + self.unk1 = unk1 + self.unk2 = unk2 + self.total_size = total_size + self.unk3 = unk3 + self.tid = tid + self.mac_address = mac_address + self.padding = padding + + def __repr__(self) -> str: + return f"BkHeader(size=0x{self.size:08x}, magic=0x{self.magic:08x}, ngid=0x{self.ngid:08x}, number_of_files={self.number_of_files}, size_of_files={self.size_of_files}, unk1={self.unk1}, unk2={self.unk2}, total_size={self.total_size}, unk3={self.unk3}, tid=0x{self.tid:016x}, mac_address={self.mac_address}, padding={self.padding})" + + def __str__(self) -> str: + return f"BkHeader(size=0x{self.size:08x}, magic=0x{self.magic:08x}, ngid=0x{self.ngid:08x}, number_of_files={self.number_of_files}, size_of_files={self.size_of_files}, unk1={self.unk1}, unk2={self.unk2}, total_size={self.total_size}, unk3={self.unk3.__class__.__name__}({len(self.unk3)}), tid=0x{self.tid:016x}, mac_address={self.mac_address.hex()}, padding={self.padding.__class__.__name__}({len(self.padding)}))" + + @staticmethod + def generate(files: list, game_version: str): + size_of_files = sum(len(file) for file in files) + bkh = BkHeader(BK_LISTED_SZ, BkHeader.MAGIC, DEFAULT_DEVICE_ID, len(files), size_of_files, 0, 0, + size_of_files + FULL_CERT_SZ, bytes(0x40), VERSIONS[game_version], bytes(6), bytes(0x12)) + return bkh + + @staticmethod + def unpack(buffer): + hdr = BkHeader(*BkHeader.PACK_FORMAT.unpack(buffer)) + if hdr.size != BK_LISTED_SZ: + logging.error( + f"[BkHeader] Invalid header size: {BK_LISTED_SZ} != {hdr.size}") + return + if hdr.magic != BkHeader.MAGIC: + logging.error( + f"[BkHeader] Magic mismatch: {BkHeader.MAGIC:08x} != {hdr.magic:08x}") + return + if hdr.size_of_files + FULL_CERT_SZ != hdr.total_size: + logging.error( + f"[BkHeader] Invalid files size: {hdr.size_of_files + FULL_CERT_SZ} != {hdr.total_size}") + return + return hdr + + @staticmethod + def from_file(reader: FileIO): + reader.seek(Header.PACK_SIZE) + data = reader.read(BkHeader.PACK_FORMAT.size) + return BkHeader.unpack(data) + + def to_bytes(self): + return BkHeader.PACK_FORMAT.pack(self.size, self.magic, self.ngid, self.number_of_files, self.size_of_files, self.unk1, self.unk2, self.total_size, self.unk3, self.tid, self.mac_address, self.padding) + + def pack(self): + return self.to_bytes() + + +class NodeType(IntEnum): + FILE = 1 + DIR = 2 + + +class FileHDR: + __slots__ = ("magic", "size", "permissions", "attrib", + "node_type", "name", "padding", "iv", "unk",) + PACK_FORMAT = struct.Struct('>II3B64s5s16s32s') + MAGIC = 0x03adf17e + + def __init__(self, magic, size, permissions, attrib, node_type: NodeType, name, padding, iv, unk): + self.magic = magic + self.size = size + self.permissions = permissions + self.attrib = attrib + self.node_type = NodeType(node_type) + self.name = name + self.padding = padding + self.iv = iv + self.unk = unk + + def __str__(self) -> str: + return f"FileHDR(magic=0x{self.magic:08x}, size=0x{self.size:08x}, permissions=0x{self.permissions:02x}, attrib=0x{self.attrib:02x}, node_type={self.node_type.name[:1].upper() + self.node_type.name.lower()[1:]}, name={self.name}, padding=bytes({len(self.padding)}), iv={self.iv.hex()}, unk={self.unk})" + + @staticmethod + def unpack(buffer): + file_hdr = FileHDR(*FileHDR.PACK_FORMAT.unpack(buffer)) + if file_hdr.magic != FileHDR.MAGIC: + logging.error( + f"[FileHDR] magic field mismatch: {FileHDR.MAGIC:08x} != {file_hdr.magic:08x}") + return + return file_hdr + + @staticmethod + def from_save_file(save): + if len(save.path) >= 0x40: + logging.error( + f"Error: file name {save.path!a} is too long (64 characters max)") + return + file_hdr = FileHDR(FileHDR.MAGIC, len(save.data) if save.node_type == 1 else 0, save.mode, + save.attributes, save.node_type, bytes(save.path, 'utf-8')[:min(0x40, len(save.path))] + bytes(max(0, 0x40-len(save.path))), bytes(5), SD_INITIAL_IV, bytes(0x20)) + return file_hdr + + def pack(self): + return FileHDR.PACK_FORMAT.pack(self.magic, self.size, self.permissions, self.attrib, self.node_type, self.name, self.padding, self.iv, self.unk) + + +class SaveNode: + def __init__(self, mode=0, attributes=0, node_type: NodeType = NodeType.DIR, path="", data=None): + self.mode = mode + self.attributes = attributes + self.node_type = NodeType(node_type) + self.path = path + self.data = data + + def __repr__(self) -> str: + data = f", data={self.data}" if self.data is not None else "" + return f"SaveNode(path={repr(self.path)}, node_type={self.node_type}, mode={self.mode}, attributes={self.attributes}{data})" + + def __str__(self) -> str: + data = f", data={self.data.__class__.__name__}({len(self.data)})" if self.data is not None else "" + return f"SaveNode(path=\"{self.path}\", node_type={self.node_type.name[:1].upper() + self.node_type.name.lower()[1:]}, mode={self.mode}, attributes={self.attributes}{data})" + + @staticmethod + def unpack(reader: FileIO): + data = reader.read(FileHDR.PACK_FORMAT.size) + file_hdr = FileHDR.unpack(data) + if file_hdr is None: + return + file_data = None + if file_hdr.node_type == NodeType.FILE: + size = file_hdr.size + rounded_size = alignUp(size, BLOCK_SZ) + file_data = reader.read(rounded_size) + file_data = aes_cbc_decrypt(SD_KEY, file_hdr.iv, file_data)[0:size] + return SaveNode(file_hdr.permissions, file_hdr.attrib, file_hdr.node_type, + file_hdr.name.split(b'\x00')[0].decode('utf-8'), file_data) + + @staticmethod + def unpack_all(reader: FileIO, bkh: BkHeader): + files: List[SaveNode] = [] + reader.seek(Header.PACK_SIZE + BkHeader.PACK_FORMAT.size) + for i in range(0, bkh.number_of_files): + logging.debug(f"[SaveNode] Unpacking file #{i}") + file = SaveNode.unpack(reader) + if not file is None: + files.append(file) + else: + logging.error(f"[SaveNode] Error while unpacking file #{i}") + return + return files + + def pack(self): + file_hdr = FileHDR.from_save_file(self) + if file_hdr is None: + return + data_enc = bytes(0) + if not self.data is None and len(self.data) > 0: + data_aligned = self.data + \ + bytes(alignUp(len(self.data), BLOCK_SZ) - len(self.data)) + data_enc = aes_cbc_encrypt(SD_KEY, file_hdr.iv, data_aligned) + return file_hdr.pack() + data_enc + + def metadata(self): + return {'path': self.path, 'mode': self.mode, 'attributes': self.attributes, 'type': self.node_type} + + @staticmethod + def pack_all(files): + data = bytes() + for file in files: + data = data + file.pack() + return data + + def __len__(self): + return FileHDR.PACK_FORMAT.size + alignUp(len(self.data), BLOCK_SZ) if self.node_type == 1 else 0 + + +class SaveBin: + def __init__(self, header: Header, bkheader: BkHeader, files: List[SaveNode]): + self.header = header + self.bkheader = bkheader + self.files = files + + def __repr__(self) -> str: + return f"SaveBin(header={repr(self.header)}, bkheader={repr(self.bkheader)}, files=[{', '.join([repr(file) for file in self.files])}])" + + def __str__(self) -> str: + return f"SaveBin(header={str(self.header)}, bkheader={str(self.bkheader)}, files=[{', '.join([str(file) for file in self.files])}])" + + @staticmethod + def from_file(reader: FileIO): + header = Header.from_file(reader) + if header is None: + logging.error("[SaveBin] Could not parse Header") + return + bkheader = BkHeader.from_file(reader) + if bkheader is None: + logging.error("[SaveBin] Could not parse BkHeader") + return + files = SaveNode.unpack_all(reader, bkheader) + if files is None: + logging.error("[SaveBin] Could not parse files") + return + return SaveBin(header, bkheader, files) + + @staticmethod + def generate(banner: FileIO, game_version: str): + files = [] + bkheader = BkHeader.generate(files, game_version) + header = Header.generate(banner.read(), game_version) + return SaveBin(header, bkheader, files) + + def to_file(self, writer: FileIO): + writer.write(self.header.pack()) + data1 = self.bkheader.pack() + SaveNode.pack_all(self.files) + writer.write(data1) + data_sha1 = sha1(data1) + ap_sig, ap_cert = sign(SYSTEM_MENU, data_sha1, self.bkheader.ngid) + writer.write(ap_sig + struct.pack('>I', SIGNATURE_END_MAGIC) + + get_device_certificate(self.bkheader.ngid).pack() + ap_cert) + + def _update_bk_files(self): + self.bkheader.number_of_files = len(self.files) + self.bkheader.size_of_files = sum(len(file) for file in self.files) + self.bkheader.total_size = self.bkheader.size_of_files + FULL_CERT_SZ + + def add_file(self, path, data, mode=0x3f, attributes=0, node_type=NodeType.FILE): + indices = [i for i, f in enumerate( + self.files) if f.path == path] + indices.sort(reverse=True) + # Remove all duplicate occurences + n_idx = len(indices) + if n_idx > 0: + logging.debug( + f"{n_idx} file{'s' if n_idx > 1 else ''} already exist with the same name. Removing them...") + for idx in indices: + self.files.pop(idx) + # Split the path into component dirs, and fold it to get each directory's full path. + def fold(acc, x): + l = [] + l.extend(acc) + l.append(acc[-1] + '/' + x) + return l + dirs = path.split('/')[:-1] + if len(dirs) > 1: + dirs = reduce(fold, dirs[1:], [dirs[0]]) + # Checking for all the parent directories if they exist + for d in dirs: + # For each parent directory, find it in the list... + f = next((f for f in self.files if f.path == d), None) + if not f is None: + # ... if found, skip to the next directory... + if f.node_type != NodeType.DIR: + raise RuntimeError(f"'{d}' already exists and is not a directory") + logging.debug(f"'{d}' found") + continue + else: + # ... else create directory. + logging.debug(f"'{d}' not found") + self.add_file(d, None, mode, attributes, NodeType.DIR) + logging.debug(f"Adding '{path}'...") + self.files.append(SaveNode(mode, attributes, node_type, path, data)) + self._sort_files() + self._update_bk_files() + + def _sort_files(self): + self.files.sort(key=lambda x: x.path) + pass + + def rm_file(self, path): + indices = [i for i, f in enumerate( + self.files) if f.path == path] + indices.sort(reverse=True) + # Remove all occurences + n_idx = len(indices) + if n_idx > 0: + # If it is a directory, remove all the children recursively + for f in [self.files[idx] for idx in indices]: + if f.node_type == NodeType.DIR: + for sub_path in [x.path for x in self.files if x.path.startswith(f.path + '/')]: + self.rm_file(sub_path) + logging.debug(f"removing '{path}'...") + for idx in indices: + self.files.pop(idx) + self._sort_files() + self._update_bk_files() + + def config(self): + return {'tid': self.header.tid, 'permissions': self.header.permissions, 'ngid': self.bkheader.ngid, 'mac_address': struct.unpack('>Q', bytes(2) + self.bkheader.mac_address)[0], 'files': [file.metadata() for file in self.files]} + +# +------------------------------+ +# | Save file patching functions | +# +------------------------------+ + + +def find_zeldaTp_idx(save_bin: SaveBin): + idx = -1 + for i in range(len(save_bin.files)): + file = save_bin.files[i] + if file.path == "zeldaTp.dat": + idx = i + return idx if idx > -1 else None + + +def update_checksum(data: bytes, fileNumber: int): + data = bytearray(data) + + offsetFile0 = (fileNumber - 1) * 0xA94 + 0x8 + offsetFile1 = offsetFile0 + 0x2000 + + def patchFilesU32(offset: int, value: int): + data[offsetFile0 + offset:offsetFile0 + + offset + 4] = struct.pack('>I', value) + data[offsetFile1 + offset:offsetFile1 + + offset + 4] = struct.pack('>I', value) + + dataFieldSize = 0xA8C + + dataFieldSum = 0 + for i in range(dataFieldSize): + dataFieldSum = ctypes.c_uint32( + dataFieldSum + data[offsetFile0 + i]).value + + # Patch in checksums. + patchFilesU32(0xA8C, dataFieldSum) + patchFilesU32( + 0xA90, ctypes.c_uint32(-(dataFieldSum + dataFieldSize)).value) + + return bytes(data) + + +def patch_file(data: bytes, fileNumber: int, version: str, rel_name: str = "mod.rel", bin_data_version='1', file_name=None): + data = bytearray(data) + + offsetFile0 = (fileNumber - 1) * 0xA94 + 0x8 + offsetFile1 = offsetFile0 + 0x2000 + + def patchFilesU16(offset: int, value: int): + data[offsetFile0 + offset:offsetFile0 + + offset + 2] = struct.pack('>H', value) + data[offsetFile1 + offset:offsetFile1 + + offset + 2] = struct.pack('>H', value) + + def patchFilesU32(offset: int, value: int): + data[offsetFile0 + offset:offsetFile0 + + offset + 4] = struct.pack('>I', value) + data[offsetFile1 + offset:offsetFile1 + + offset + 4] = struct.pack('>I', value) + + def patchFilesU64(offset: int, value: int): + data[offsetFile0 + offset:offsetFile0 + + offset + 8] = struct.pack('>Q', value) + data[offsetFile1 + offset:offsetFile1 + + offset + 8] = struct.pack('>Q', value) + + def patchFilesS64(offset: int, value: int): + data[offsetFile0 + offset:offsetFile0 + + offset + 8] = struct.pack('>q', value) + data[offsetFile1 + offset:offsetFile1 + + offset + 8] = struct.pack('>q', value) + + def patchFilesBytes(offset: int, value: bytes): + data[offsetFile0 + offset:offsetFile0 + offset + len(value)] = value + data[offsetFile1 + offset:offsetFile1 + offset + len(value)] = value + + # Set the last save time to the current date (as a kind of build date) + ticks = (datetime.now(timezone.utc).replace(tzinfo=None) - datetime(2000, 1, 1) + ).total_seconds() * (OS_BUS_CLOCK / 4) + patchFilesS64(0x28, int(ticks)) + + # Write the new file name (Link's name). + if file_name is None: + file_name = b'REL Loader v' + bytes(bin_data_version, 'utf-8') + else: + file_name = bytes(file_name, 'utf-8') + patchFilesBytes(0x1B4, file_name + b'\0') + + # Overwrite the next stage string with a bunch of filler 3s. + patchFilesBytes(0x58, b"3" * 0x12) + + # Write the pointer to the pointer to the init ASM function. + patchFilesU32(0x6A, GAME_INFO_PTR[version] + 0x1CC - 0xBC) + + # Write the pointer to the init ASM function. + patchFilesU32(0x1CC, GAME_INFO_PTR[version] + 0x1E4) + + # Write the init ASM function. + patchFilesBytes(0x1E4, BIN_DATA_INIT[version]) + + # Write the main ASM function. + patchFilesBytes( + 0x1E4 + len(BIN_DATA_INIT[version]), BIN_DATA_MAIN[bin_data_version][version] + bytes(rel_name, 'utf-8') + b'\x00') + + return update_checksum(data, fileNumber) + + +def generate_zeldaTp(save_bin: SaveBin): + data = bytes(0x4000) + for i in range(1, 4): + data = update_checksum(data, i) + save_bin.add_file("zeldaTp.dat", data, 0x34, 0, NodeType.FILE) + +# +-------------+ +# | Main script | +# +-------------+ + +def main(): + + # Function definitions used in the argument parser + # (shouldn't be defined in the root scope, since there + # is no need to make them available when imported as library) + + def parseFileNumber(string): + val = int(string) + if val < 1 or val > 3: + raise argparse.ArgumentTypeError( + "File number can only have integer values between 1 and 3") + return val + + + def parseFileName(string): + if len(string) > 12: + raise argparse.ArgumentTypeError( + f"File name is too long (12 characters max; got {len(string)})") + return string + + + def parseSaveFileName(string): + if len(string) > 31: + raise argparse.ArgumentTypeError( + f"File path is too long (31 characters max; got {len(string)})") + return string + + + def directoryPathParser(string): + if os.path.exists(string): + if os.path.isfile(string): + raise argparse.ArgumentTypeError( + "The output has to be an existing directory or a new directory; Got a file") + return string + + + def filePathParser(string): + if os.path.exists(string): + if not os.path.isfile(string): + raise argparse.ArgumentTypeError( + "The output has to be a file; Got a directory") + return string + + file_map_parser_re = re.compile("^([^\\:]*?)\\:([^\\:]*?)$") + def fileMapParser(string): + m = file_map_parser_re.match(string) + if not m: + raise argparse.ArgumentTypeError(f'"{string}" is not a valid mapping') + if len(m.group(2)) > 31: + raise argparse.ArgumentTypeError( + f"File path is too long (31 characters max; got {len(m.group(2))})") + return string + + class NameMapping: + def __init__(self, old, new): + self.old = old + self.new = new + + @staticmethod + def parse(string): + m = file_map_parser_re.match(string) + return NameMapping(m.group(1), m.group(2)) + + # Functions only used in the main function + # (shouldn't be defined in the root scope, since there + # is no need to make them available when imported as library) + + def updateMetaData(save_bin: SaveBin, meta: Dict[str, Any]): + save_bin.header.tid = meta["tid"] if "tid" in meta else save_bin.header.tid + save_bin.header.permissions = meta["permissions"] if "permissions" in meta else save_bin.header.permissions + save_bin.bkheader.ngid = meta["ngid"] if "ngid" in meta else save_bin.bkheader.ngid + save_bin.bkheader.mac_address = struct.pack(">Q", meta["mac_address"])[ + 2:] if "mac_address" in meta else save_bin.bkheader.mac_address + if "files" in meta: + for i in range(len(save_bin.files)): + try: + idx = [file["path"] + for file in meta["files"]].index(save_bin.files[i].path) + save_bin.files[i].attributes = meta["files"][idx]["attributes"] if "attributes" in meta["files"][idx] else save_bin.files[i].attributes + save_bin.files[i].mode = meta["files"][idx]["mode"] if "mode" in meta["files"][idx] else save_bin.files[i].mode + save_bin.files[i].node_type = meta["files"][idx]["type"] if "type" in meta["files"][idx] else save_bin.files[i].node_type + except ValueError as err: + continue + + + parser = argparse.ArgumentParser( + sys.argv[0], description="Tool to pack/unpack Wii saves & inject REL modules.") + parser.add_argument("-v", "--verbose", action="count", + help="increase verbosity of the output", default=1) + parser.add_argument("-q", "--quiet", action="store_true", + help="prevents output to the console", default=False) + parser.add_argument("-V", "--version", action="version", version=VERSION) + subparsers = parser.add_subparsers( + dest="command", metavar="command", help="Available commands are: generate, inject, patch, unpack, pack, meta, banner, files") + # Generate + gen_parser = subparsers.add_parser( + "generate", description="Generate a new save file", help="Generate a new save file") + gen_parser.add_argument("-i", "--index", action="append", type=parseFileNumber, + help="file number to inject the custom rel into (1 to 3)") + gen_parser.add_argument("-n", "--name", type=parseFileName, + help="overwrite the name of the internal REL file that will be loaded", default="mod.rel") + gen_parser.add_argument("rel", type=argparse.FileType( + 'rb'), help="Path to the REL module to pack") + gen_parser.add_argument("banner", type=argparse.FileType( + 'rb'), help="Path to the banner of the game") + gen_parser.add_argument( + "out", type=str, help="Path where to write the generated save file") + gen_parser.add_argument("-g", "--game-version", choices=[ + "us0", "us2", "eu", "jp"], help="Version to generate the save for", required=True) + gen_parser.add_argument("-m", "--meta", type=argparse.FileType("r"), + help="Metadata to use for the save file") + gen_parser.add_argument("--get-meta", type=argparse.FileType('w'), + help="Extract the medatada of the generated save") + gen_parser.add_argument("-l", "--loader-version", choices=[ + '1', '2'], help="Choose which version of the loader to put in the save file", default='1') + gen_parser.add_argument("-f", "--file-name", type=parseSaveFileName, + help="The player name in the save file (31 character max)", default=None) + # Inject + inj_parser = subparsers.add_parser( + "inject", description="Injects into an existing save file", help="Injects into an existing save file") + inj_parser.add_argument("-i", "--index", action="append", type=parseFileNumber, + help="file number to inject the custom rel into (1 to 3)") + inj_parser.add_argument("-n", "--name", type=parseFileName, + help="overwrite the name of the internal REL file that will be loaded", default="mod.rel") + inj_parser.add_argument("rel", type=argparse.FileType( + 'rb'), help="Path to the REL module to pack") + inj_parser.add_argument("save", type=argparse.FileType( + 'rb'), help="Save file to inject into") + inj_parser.add_argument( + "out", type=str, help="Path where to write the generated save file") + inj_parser.add_argument( + "-g", "--game-version", choices=["us0", "us2", "eu", "jp"], help="Version of the save file") + inj_parser.add_argument("-m", "--meta", type=argparse.FileType("r"), + help="Overwrite the metadata using the provided file") + inj_parser.add_argument("--get-meta", type=argparse.FileType('w'), + help="Extract the medatada of the generated save") + inj_parser.add_argument( + "-b", "--banner", type=argparse.FileType("rb"), help="Overwrite the banner") + inj_parser.add_argument("-l", "--loader-version", choices=[ + '1', '2'], help="Choose which version of the loader to put in the save file", default='1') + inj_parser.add_argument("-f", "--file-name", type=parseSaveFileName, + help="The player name in the save file (31 character max)", default=None) + # Patch + patch_parser = subparsers.add_parser( + "patch", description="Patches a zeldaTp.dat file", help="Patches a zeldaTp.dat file") + patch_parser.add_argument("-i", "--index", action="append", type=parseFileNumber, + help="file number to inject the custom rel into (1 to 3)") + patch_parser.add_argument("-n", "--name", type=parseFileName, + help="overwrite the name of the internal REL file that will be loaded", default="mod.rel") + patch_parser.add_argument("file", type=argparse.FileType( + 'rb'), help="The zeldaTp.dat file to patch") + patch_parser.add_argument( + "out", type=str, help="Where to write the patched file") + patch_parser.add_argument("-g", "--game-version", choices=[ + "us0", "us2", "eu", "jp"], help="Version to generate the save for", required=True) + patch_parser.add_argument("-l", "--loader-version", choices=[ + '1', '2'], help="Choose which version of the loader to put in the save file", default='2') + patch_parser.add_argument("-f", "--file-name", type=parseSaveFileName, + help="The player name in the save file (31 character max)", default=None) + # Unpack + unpack_parser = subparsers.add_parser( + "unpack", description="Unpacks a save into a directory", help="Unpacks a save into a directory") + unpack_parser.add_argument("save", type=argparse.FileType( + 'rb'), help="Path to the save file to unpack") + unpack_parser.add_argument("out_dir", type=directoryPathParser, + help="Path to the directory to unpack the save into") + unpack_parser.add_argument("--get-meta", type=argparse.FileType('w'), + help="Extract the medatada of the generated save") + # Pack + pack_parser = subparsers.add_parser( + "pack", description="Packs a folder into a new save file", help="Packs data files into a new save file") + pack_parser.add_argument("out_path", type=argparse.FileType( + 'wb'), help="Where to write the packed save") + pack_parser.add_argument("header", type=argparse.FileType( + 'rb'), help='Path to "header.bin"') + pack_parser.add_argument("bkheader", type=argparse.FileType( + 'rb'), help='Path to "bkheader.bin"') + pack_parser.add_argument("files", metavar="file", type=argparse.FileType( + 'rb'), nargs="*", help="Any file you want to pack in the save") + pack_parser.add_argument( + "-m", "--meta", type=argparse.FileType("r"), help="Metadata to use for the save file") + pack_parser.add_argument("--get-meta", type=argparse.FileType('w'), + help="Extract the medatada of the generated save") + # Meta + meta_parser = subparsers.add_parser( + "meta", description="Extract metadata from a save file", help="Extract metadata from a save file") + meta_parser.add_argument("save", type=argparse.FileType( + 'rb'), help="Save file to extract the metadata from") + meta_parser.add_argument("out", type=argparse.FileType( + 'w'), help="Path the the file where to write the metadata (JSON format)") + # Banner + banner_parser = subparsers.add_parser( + "banner", description="Extract banner from save file", help="Extract banner from save file") + banner_parser.add_argument("save", type=argparse.FileType( + 'rb'), help="Path to the save file to unpack") + banner_parser.add_argument("out", type=filePathParser, + help="Path to the file to store the banner into") + # Files + files_parser = subparsers.add_parser( + "files", description="Operations on the file inside the save.", help="Operations on the file inside the save.") + files_subparser = files_parser.add_subparsers( + dest="files_cmd", metavar="CMD", help="Available commands are: add, list, rm", required=True) + # Files; Add + files_add_parser = files_subparser.add_parser( + "add", description="Adds file(s) to a save", help="Adds file(s) to a save") + files_add_parser.add_argument( + "save", type=argparse.FileType('rb'), metavar="", help="Path to the save file") + files_add_parser.add_argument("-o", "--output", nargs='?', type=argparse.FileType( + 'wb'), default=None, help="Path to where to store the result (default: overwrites 'save')") + files_add_parser.add_argument( + "-M", "--map", action="extend", nargs='*', type=fileMapParser, help="Map a file name to an other one") + files_add_parser.add_argument("file", type=argparse.FileType( + 'rb'), nargs="+", metavar="", help="Path to the file(s) to add to the save") + # Files; List + files_list_parser = files_subparser.add_parser( + "list", description="Lists the file(s) within a given save", help="Lists the files within a given save") + files_list_parser.add_argument("save", type=argparse.FileType( + 'rb'), metavar="", help="Path to the save file") + # Files; Remove + files_rm_parser = files_subparser.add_parser( + "rm", description="Removes the file(s) within the save", help="Removes the file(s) within the save") + files_rm_parser.add_argument("save", type=argparse.FileType( + 'rb'), metavar="", help="Path to the save file") + files_rm_parser.add_argument("-o", "--output", nargs='?', type=argparse.FileType( + 'wb'), default=None, help="Path to where to store the result (default: overwrites 'save')") + files_rm_parser.add_argument( + "file", type=str, nargs="+", metavar="", help="Name of the file(s) to remove from the save") + # Help + help_parser = subparsers.add_parser( + "help", description="Get a help guide for a command", help="Get a help guide for a command") + help_parser.add_argument("cmd", choices=[ + "generate", "inject", "pack", "unpack", "patch", "meta", "banner", "files", "help"], help="A command you need help with", nargs='?') + + args = parser.parse_args() + + # Phase 1: Extract the simple options and check if it's only looking for a help text. + + if not "command" in args or args.command is None: + parser.print_help() + sys.exit(0) + + parsers = {"generate": gen_parser, "inject": inj_parser, "patch": patch_parser, + "pack": pack_parser, "unpack": unpack_parser, "help": help_parser, "meta": meta_parser, "banner": banner_parser, "files": files_parser} + + if args.command == "help": + if not args.cmd is None: + parsers[args.cmd].print_help() + else: + parser.print_help() + sys.exit(0) + + verbosity = args.verbose if not args.quiet else 0 + + LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] + loglevel = LEVELS[min(max(4 - verbosity, 0), 4)] + + numeric_level = getattr(logging, loglevel.upper(), logging.CRITICAL) + logging.basicConfig( + level=numeric_level, format="[%(levelname)s]\t[%(asctime)s]\t%(pathname)s:%(lineno)d %(funcName)s: %(message)s") + + mappings = {} + if args.command == "files" and args.files_cmd == "add": + if args.map is None: + args.map = [] + for m in args.map: + mapping = NameMapping.parse(m) + logging.info(f"parsed mapping '{mapping.old}' to '{mapping.new}'") + mappings[mapping.old] = mapping.new + + # Phase 2: Load files and data/options + + # Phase 2.1: Load and/or generate the Save File (if needed) + save_bin = None + if args.command in ["inject", "unpack", "meta", "banner", "files"]: + # We need to fetch the save_bin from a provided file + with args.save as save: + if args.command == "inject": + prefix_len = len(os.path.commonprefix( + [args.rel.name, save.name])) + logging.info( + f"injecting '{'.../' if prefix_len > 0 else ''}{args.rel.name[prefix_len:]}' into '{'.../' if prefix_len > 0 else ''}{args.save.name[prefix_len:]}'") + save_bin = SaveBin.from_file(save) + if save_bin is None: + logging.error("Could not parse save file") + sys.exit(1) + logging.info("Loaded SaveBin") + logging.debug(f"{save_bin}") + elif args.command == "generate": + # We need to generate a new save_bin + logging.info("Generating new save file") + with args.banner as banner: + save_bin = SaveBin.generate(banner, args.game_version) + generate_zeldaTp(save_bin) + logging.debug(f"{save_bin}") + elif args.command in ["pack", "patch"]: + # We don't need any save file + pass + else: + logging.error(f'Error: command not supported {args.command!a}') + sys.exit(1) + + # Phase 2.2: Make sure we have a valid file index to put the loader into (if needed) + + if args.command in ["inject", "generate", "patch"]: + # We need to make sure we have the index of the file to patch the loader into. Default is file 3 + if args.index is None: + logging.info("No file index provided, defaulting to 3") + args.index = [3] + + # Phase 2.3: Load and process the rel file (if needed) + + rel_bin = None + if args.command in ["inject", "generate"]: + # Load the rel file + with args.rel as rel: + rel_bin = rel.read() + + # Phase 3: Execute the command and save the results + + if args.command in ["inject", "generate"]: + # Inject the rel into the save_bin + args.out = open(args.out, 'wb') + if args.game_version is None: + if not save_bin.header.tid in TIDS.keys(): + logging.error( + "Error: Game version unrecognized, please provide the --game-version option.") + sys.exit(1) + args.game_version = TIDS[save_bin.header.tid] + logging.info( + f'Game ID: {save_bin.header.tid >> 32:08x}-{struct.pack(">I", save_bin.header.tid & 0xffffffff).decode()}') + save_bin.add_file(args.name, rel_bin) + zeldaTp_idx = find_zeldaTp_idx(save_bin) + if not zeldaTp_idx is None: + for file_idx in args.index: + save_bin.files[zeldaTp_idx].data = bytes( + patch_file(save_bin.files[zeldaTp_idx].data, file_idx, args.game_version, args.name, args.loader_version, args.file_name)) + else: + logging.error( + 'Error: no "zeldaTp.dat" file in the save archive') + sys.exit(1) + + if "meta" in args and not args.meta is None: + meta = json.load(args.meta) + args.meta.close() + updateMetaData(save_bin, meta) + + if not args.get_meta is None: + json.dump(save_bin.config(), args.get_meta, + sort_keys=True, indent=4) + args.get_meta.close() + + logging.debug(f"Produced SaveBin: {save_bin}") + + save_bin.to_file(args.out) + elif args.command == "unpack": + path = os.path.realpath(args.out_dir) + if not os.path.exists(path): + os.mkdir(path) + header_file = open(args.out_dir + os.path.sep + "header.bin", 'wb') + header_file.write(save_bin.header.pack()) + header_file.close() + bkheader_file = open(args.out_dir + os.path.sep + "bkheader.bin", 'wb') + bkheader_file.write(save_bin.bkheader.pack()) + bkheader_file.close() + for file in save_bin.files: + file_handle = open(args.out_dir + os.path.sep + file.path, 'wb') + file_handle.write(file.data) + file_handle.close() + + if not args.get_meta is None: + json.dump(save_bin.config(), args.get_meta, + sort_keys=True, indent=4) + args.get_meta.close() + elif args.command == "pack": + header = Header.unpack(args.header.read()) + bkheader = BkHeader.unpack(args.bkheader.read()) + args.header.close() + args.bkheader.close() + save_bin = SaveBin(header, bkheader, []) + save_bin._update_bk_files() + for file in args.files: + save_bin.add_file(os.path.basename(file.name), + file.read(), node_type=NodeType.FILE) + file.close() + + if "meta" in args and not args.meta is None: + meta = json.load(args.meta) + args.meta.close() + updateMetaData(save_bin, meta) + + if not args.get_meta is None: + json.dump(save_bin.config(), args.get_meta, + sort_keys=True, indent=4) + args.get_meta.close() + + logging.debug(f"Generated SaveBin: {save_bin}") + # Recalculate the checksums + zeldaTp_idx = find_zeldaTp_idx(save_bin) + if zeldaTp_idx is not None: + data = save_bin.files[zeldaTp_idx].data + for i in range(1, 4): + data = update_checksum(data, i) + save_bin.files[zeldaTp_idx].data = data + save_bin.to_file(args.out_path) + elif args.command == "patch": + if args.game_version is None: + logging.error( + "Error: Game version required, please provide it through the --game-version option.") + sys.exit(1) + zeldaTp_data = args.file.read() + args.file.close() + if len(zeldaTp_data) != 0x4000: + logging.error( + f"Error: wrong file size (0x{len(zeldaTp_data):08x}; expected 0x4000)") + sys.exit(1) + for file_idx in args.index: + zeldaTp_data = bytes(patch_file( + zeldaTp_data, file_idx, args.game_version, args.name, args.loader_version, args.file_name)) + out = open(args.out, 'wb') + out.write(zeldaTp_data) + out.close() + elif args.command == "banner": + out = open(args.out, 'wb') + out.write(save_bin.header.banner) + out.close() + elif args.command == "meta": + json.dump(save_bin.config(), args.out, sort_keys=True, indent=4) + elif args.command == "files": + if args.files_cmd == "add": + file_path = args.save.name + for file in args.file: + file_name = file.name + logging.info(f"Loading '{file_name}'...") + with file as f: + file_data = f.read() + logging.debug(f"Adding '{file_name}'...") + if file_name in mappings.keys(): + file_name = mappings.get(file_name) + logging.debug(f"(Mapped to '{file_name}')") + save_bin.add_file(file_name, file_data) + if args.output is None: + logging.info( + f"No output file provided, overwritting input save") + logging.debug(f"({file_path})") + with open(file_path, 'wb') as f: + save_bin.to_file(f) + else: + with args.output as out: + save_bin.to_file(out) + elif args.files_cmd == "list": + print("id\ttype\tmode\tattr\tpath") + for i, file in enumerate(save_bin.files): + print( + f"#{i:04d}\t{file.node_type.name[:1].upper() + file.node_type.name.lower()[1:]}\t{file.mode}\t{file.attributes}\t{file.path}") + elif args.files_cmd == "rm": + file_path = args.save.name + for file in args.file: + save_bin.rm_file(file) + if args.output is None: + logging.info( + f"No output file provided, overwritting input save") + logging.debug(f"({file_path})") + with open(file_path, 'wb') as f: + save_bin.to_file(f) + else: + with args.output as out: + save_bin.to_file(out) + # TODO Add a command to edit a file's properties. + else: + raise ValueError( + f"'{args.files_cmd}' is not a recognized 'files' command.") + + +if __name__ == "__main__": + main() diff --git a/modules/boot/include/gz_flags.h b/modules/boot/include/gz_flags.h index 4ba3e4a0..a17ec2e7 100644 --- a/modules/boot/include/gz_flags.h +++ b/modules/boot/include/gz_flags.h @@ -32,6 +32,8 @@ enum GZFlags { GZFLG_FREEZE_TIME, GZFLG_DISABLE_BGM, GZFLG_DISABLE_SFX, + GZFLG_LFC, + GZFLG_EE, }; struct GZFlag { diff --git a/modules/boot/include/menus/menu.h b/modules/boot/include/menus/menu.h index f7e872a4..d8bc5e9e 100644 --- a/modules/boot/include/menus/menu.h +++ b/modules/boot/include/menus/menu.h @@ -38,12 +38,17 @@ enum MenuIndex { MN_GENERAL_FLAGS_INDEX, MN_DUNGEON_FLAGS_INDEX, MN_PORTAL_FLAGS_INDEX, + MN_RUPEE_FLAGS_INDEX, MN_FLAG_RECORDS_INDEX, MN_FLAG_LOG_INDEX, // Inventory menu's sub menus MN_ITEM_WHELL_INDEX, MN_PAUSE_INDEX, MN_AMOUNTS_INDEX, + // Pause menu's sub menus + MN_EQUIPMENT_INDEX, + MN_GOLDEN_BUGS_INDEX, + MN_HIDDEN_SKILLS_INDEX, // Memory menu's sub menus MN_WATCHES_INDEX, MN_MEMORY_EDITOR_INDEX, @@ -61,10 +66,17 @@ enum MenuIndex { MN_COLLISION_VIEW_INDEX, MN_PROJECTION_VIEW_INDEX, MN_TRIGGER_VIEW_INDEX, + MN_SOUND_TEST_INDEX, // Setting menu's sub menus MN_POS_SETTINGS_INDEX, MN_CREDITS_INDEX, MN_COMBO_INDEX, + // Tools menu's sub menus + MN_TOOLS_CHECKERS_INDEX, + MN_TOOLS_CONTROLLER_INDEX, + MN_TOOLS_LINK_INDEX, + MN_TOOLS_SCENE_INDEX, + MN_TOOLS_TIMERS_INDEX, // This entry is used only to get a count of the number of valid entries. MN_COUNT diff --git a/modules/boot/include/modules.h b/modules/boot/include/modules.h index 1c0f7b0e..179ea2d5 100644 --- a/modules/boot/include/modules.h +++ b/modules/boot/include/modules.h @@ -22,6 +22,8 @@ bool umd_active(); bool bit_active(); #endif bool corotd_active(); +bool lfc_active(); +bool ee_active(); bool mash_checker_active(); bool gorge_active(); bool rollcheck_active(); diff --git a/modules/boot/include/pos_settings.h b/modules/boot/include/pos_settings.h index d0b12069..35a54898 100644 --- a/modules/boot/include/pos_settings.h +++ b/modules/boot/include/pos_settings.h @@ -3,11 +3,12 @@ #include "settings.h" #include "libtp_c/include/dolphin/mtx/vec.h" -#define SPRITES_AMNT 10 +#define SPRITES_AMNT 11 enum SpritesIndex { MENU_INDEX, VIEWER_INDEX, DEBUG_INFO_INDEX, + STAGE_INFO_INDEX, TIMER_SPR_INDEX, LOAD_TIMER_SPR_INDEX, IGT_TIMER_SPR_INDEX, diff --git a/modules/boot/include/practice.h b/modules/boot/include/practice.h index d9a2e613..c8583883 100644 --- a/modules/boot/include/practice.h +++ b/modules/boot/include/practice.h @@ -4,9 +4,7 @@ enum { ANY_INDEX, -#ifdef GCN_PLATFORM ANY_BITE_INDEX, -#endif HUNDO_INDEX, AD_INDEX, #ifdef GCN_PLATFORM diff --git a/modules/boot/include/save_manager.h b/modules/boot/include/save_manager.h index 0eec1fc9..0674d84a 100644 --- a/modules/boot/include/save_manager.h +++ b/modules/boot/include/save_manager.h @@ -81,12 +81,9 @@ class SaveManager { AreaReload mAreaReloadOpts; special* mSpecials; bool loading_initiated = false; - int repeat_count = 0; - bool repeat_during = false; static bool s_injectSave; static bool s_injectMemfile; - static s8 s_applyAfterTimer; public: static void injectSave(void* buffer); diff --git a/modules/boot/include/save_specials.h b/modules/boot/include/save_specials.h index 68f9caac..ec412588 100644 --- a/modules/boot/include/save_specials.h +++ b/modules/boot/include/save_specials.h @@ -14,6 +14,11 @@ void SaveMngSpecial_PurpleMist(); void SaveMngSpecial_KargOoB(); void SaveMngSpecial_WaterfallSidehop(); +void SaveMngSpecial_EarlyEle(); + +void SaveMngSpecial_HorseSpawn(); +void SaveMngSpecial_EldinCollection(); + void SaveMngSpecial_KB2Skip(); void SaveMngSpecial_Escort(); void SaveMngSpecial_EscortKeys(); diff --git a/modules/boot/include/scene.h b/modules/boot/include/scene.h index 111e6bf0..24cabadb 100644 --- a/modules/boot/include/scene.h +++ b/modules/boot/include/scene.h @@ -17,6 +17,7 @@ enum SceneIndex { COLLISION_VIEW_INDEX, PROJECTION_VIEW_INDEX, TRIGGER_VIEW_INDEX, + SOUND_TEST_INDEX, SCENE_MENU_MAX }; diff --git a/modules/boot/include/settings.h b/modules/boot/include/settings.h index 9e344a38..1abe511f 100644 --- a/modules/boot/include/settings.h +++ b/modules/boot/include/settings.h @@ -103,6 +103,11 @@ enum GZSettingID : uint32_t { STNG_CMD_BIT, // Wii only, but we reserve the id anyway STNG_CMD_GORGE_VOID, STNG_CMD_MOON_JUMP, + // New Adds + STNG_TOOLS_LFC, + STNG_TOOLS_ELEVATOR_ESCAPE, + STNG_TOOLS_STAGE_INFO, + STNG_SPRITES_STAGE_INFO, }; struct GZSettingEntry { diff --git a/modules/boot/include/tools.h b/modules/boot/include/tools.h index d4333a50..5b0d6b79 100644 --- a/modules/boot/include/tools.h +++ b/modules/boot/include/tools.h @@ -1,30 +1,11 @@ #pragma once enum ToolsIndex { - RELOAD_AREA_INDEX, - FRAME_ADVANCE_INDEX, - FAST_BONK_INDEX, - FAST_MOVEMENT_INDEX, - GORGE_INDEX, -#ifdef WII_PLATFORM - BIT_INDEX, -#endif - COROTD_INDEX, - UMD_INDEX, - INPUT_VIEWER_INDEX, - LINK_DEBUG_INDEX, - HEAP_DEBUG_INDEX, - SAND_INDEX, - ROLL_INDEX, - MASH_CHECKER_INDEX, - TELEPORT_INDEX, - TURBO_MODE_INDEX, - TIMER_INDEX, - LOAD_TIMER_INDEX, - IGT_TIMER_INDEX, - FREE_CAM_INDEX, - MOVE_LINK_INDEX, - TUNIC_COLOR_INDEX, + CHECKERS_INDEX, + CONTROLLER_INDEX, + LINK_INDEX, + SCENE_INDEX, + TIMERS_INDEX, // Entry used as a counter TOOLS_COUNT diff --git a/modules/boot/include/trigger_view.h b/modules/boot/include/trigger_view.h index e6da4840..4ce22548 100644 --- a/modules/boot/include/trigger_view.h +++ b/modules/boot/include/trigger_view.h @@ -13,6 +13,7 @@ enum TriggerViewIndex { VIEW_SWITCH_AREAS, VIEW_TRANSFORM_DISTS, VIEW_TW_GATES, + VIEW_LEEVER_RANGE, TRIGGER_VIEW_MAX, }; diff --git a/modules/boot/include/utils/lines.h b/modules/boot/include/utils/lines.h index 0a9ae932..a3d104fe 100644 --- a/modules/boot/include/utils/lines.h +++ b/modules/boot/include/utils/lines.h @@ -17,7 +17,7 @@ struct ListMember { struct Line { char line[50]; - const uint32_t idx; + uint32_t idx; char description[MAX_DESCRIPTION_LENGTH]; bool toggleable = false; bool (*active)(); diff --git a/modules/boot/include/utils/link.h b/modules/boot/include/utils/link.h index 29f6a440..127c583a 100644 --- a/modules/boot/include/utils/link.h +++ b/modules/boot/include/utils/link.h @@ -2,5 +2,6 @@ #include "font.h" void GZ_displayLinkInfo(); +void GZ_displayStageInfo(); void GZ_setTunicColor(); \ No newline at end of file diff --git a/modules/boot/src/global_data.cpp b/modules/boot/src/global_data.cpp index 51018a17..5b77a095 100644 --- a/modules/boot/src/global_data.cpp +++ b/modules/boot/src/global_data.cpp @@ -23,6 +23,7 @@ KEEP_VAR TriggerViewItem g_triggerViewFlags[TRIGGER_VIEW_MAX] = { {VIEW_TRANSFORM_DISTS, false}, {VIEW_ATTN_DISTS, false}, {VIEW_MIST_AVOID, false}, + {VIEW_LEEVER_RANGE, false}, }; KEEP_VAR ProjectionLine g_ljaProjectionLine; diff --git a/modules/boot/src/gz_flags.cpp b/modules/boot/src/gz_flags.cpp index 3dc8228e..65c33369 100644 --- a/modules/boot/src/gz_flags.cpp +++ b/modules/boot/src/gz_flags.cpp @@ -118,14 +118,12 @@ void GZ_execute(int phase) { } } - // Timer set after dScnPly__phase_4, delay until objects are fully loaded - if (!fopScnRq.isLoading && SaveManager::s_applyAfterTimer > 0) { - SaveManager::s_applyAfterTimer--; - } else if (SaveManager::s_applyAfterTimer == 0) { + // Check for post load callback and run it once link is valid + if (!fopScnRq.isLoading && dComIfGp_getPlayer()) { if (gSaveManager.mPracticeFileOpts.inject_options_after_load) { gSaveManager.mPracticeFileOpts.inject_options_after_load(); + gSaveManager.mPracticeFileOpts.inject_options_after_load = nullptr; } - SaveManager::s_applyAfterTimer = -1; } // normally oxygen doesn't get set until going to the file select screen diff --git a/modules/boot/src/menus/menu.cpp b/modules/boot/src/menus/menu.cpp index 0dcde5ed..882e4427 100644 --- a/modules/boot/src/menus/menu.cpp +++ b/modules/boot/src/menus/menu.cpp @@ -3,18 +3,22 @@ // This array must correspond to the order of the MenuIndex enum. const char* g_menuPaths[MN_COUNT] = { - "main", "cheats", "flags", - "inventory", "memory", "practice", - "scene", "settings", "tools", - "warp", "general_flags", "dungeon_flags", - "portal_flags", "flag_records", "flag_log", - "item_wheel", "pause", "amounts", - "watches", "memory_editor", "memfiles", - "any_saves", "any_bite_saves", "hundo_saves", - "ad_saves", "nosq_saves", "glitchless_saves", - "actor_spawn", "actor_list", "collision_view", - "projection_view", "trigger_view", "pos_settings", - "credits", "combo" + "main", "cheats", "flags", + "inventory", "memory", "practice", + "scene", "settings", "tools", + "warp", "general_flags", "dungeon_flags", + "portal_flags", "rupee_flags", "flag_records", + "flag_log", "item_wheel", "pause", + "amounts", "equipment", "golden_bugs", + "hidden_skills", + "watches", "memory_editor", "memfiles", + "any_saves", "any_bite_saves", "hundo_saves", + "ad_saves", "nosq_saves", "glitchless_saves", + "actor_spawn", "actor_list", "collision_view", + "projection_view", "trigger_view", "sound_test", + "pos_settings", "credits", "combo", + "tools_checkers", "tools_controller", "tools_link", + "tools_scene", "tools_timers" }; KEEP_FUNC Menu::Menu(Cursor& cursor) : cursor(cursor) {} \ No newline at end of file diff --git a/modules/boot/src/modules.cpp b/modules/boot/src/modules.cpp index 75f042b9..c4151795 100644 --- a/modules/boot/src/modules.cpp +++ b/modules/boot/src/modules.cpp @@ -72,6 +72,14 @@ KEEP_FUNC bool corotd_active() { return GZStng_getData(STNG_TOOLS_COROTD, false); } +KEEP_FUNC bool lfc_active() { + return GZStng_getData(STNG_TOOLS_LFC, false); +} + +KEEP_FUNC bool ee_active() { + return GZStng_getData(STNG_TOOLS_ELEVATOR_ESCAPE, false); +} + KEEP_FUNC bool mash_checker_active() { return GZStng_getData(STNG_TOOLS_MASH_CHECKER, false); } diff --git a/modules/boot/src/save_manager.cpp b/modules/boot/src/save_manager.cpp index ca916304..7a8c380d 100644 --- a/modules/boot/src/save_manager.cpp +++ b/modules/boot/src/save_manager.cpp @@ -20,7 +20,6 @@ SaveManager gSaveManager; KEEP_VAR bool SaveManager::s_injectSave = false; KEEP_VAR bool SaveManager::s_injectMemfile = false; -KEEP_VAR s8 SaveManager::s_applyAfterTimer = -1; void SaveManager::injectSave(void* buffer) { memcpy(&g_dComIfG_gameInfo, buffer, 0x9F8); diff --git a/modules/boot/src/save_specials.cpp b/modules/boot/src/save_specials.cpp index f187bab1..8735f7fd 100644 --- a/modules/boot/src/save_specials.cpp +++ b/modules/boot/src/save_specials.cpp @@ -110,6 +110,21 @@ KEEP_FUNC void SaveMngSpecial_WaterfallSidehop() { g_dComIfG_gameInfo.info.mRestart.mLastSpeedF = 10.0f; // link spawns swimming forward } +KEEP_FUNC void SaveMngSpecial_EarlyEle() { + gSaveManager.injectDefault_during(); + dComIfGs_onTmpBit(0x0002); +} + +KEEP_FUNC void SaveMngSpecial_HorseSpawn() { + gSaveManager.injectDefault_during(); + g_dComIfG_gameInfo.info.mRestart.mLastMode = 1; // spawn on epona +} + +KEEP_FUNC void SaveMngSpecial_EldinCollection() { + SaveMngSpecial_HorseSpawn(); + g_dComIfG_gameInfo.info.mRestart.mLastSpeedF = 42.0f; +} + KEEP_FUNC void SaveMngSpecial_KB2Skip() { gSaveManager.injectDefault_during(); setNextStageLayer(3); @@ -182,12 +197,11 @@ KEEP_FUNC void SaveMngSpecial_Stallord() { } KEEP_FUNC void SaveMngSpecial_Stallord2() { - gSaveManager.mPracticeFileOpts.inject_options_after_counter = 20; - daB_DS_c* stallord = (daB_DS_c*)fopAcM_SearchByName(PROC_B_DS); + // create the phase 2 version of stallord fopAcM_create(PROC_B_DS, fopAcM_GetParam(stallord) | 2, &stallord->current.pos, - fopAcM_GetRoomNo(stallord), nullptr, nullptr, -1); + fopAcM_GetRoomNo(stallord), nullptr, nullptr, -1); fopAcM_delete(stallord); // delete phase 1 stallord daObjLv4Wall_c* rwall = (daObjLv4Wall_c*)fopAcM_SearchByName(PROC_Obj_Lv4RailWall); @@ -199,9 +213,6 @@ KEEP_FUNC void SaveMngSpecial_Stallord2() { } KEEP_FUNC void SaveMngSpecial_Stallord2_init() { - gSaveManager.repeat_during = true; - gSaveManager.repeat_count = 120; - gSaveManager.injectDefault_during(); g_dComIfG_gameInfo.info.mZone[0].mBit.mSwitch[0] |= 0x300000; // turn off intro cs, start fight setNextStagePoint(1); // spawn at in front of stally @@ -235,8 +246,8 @@ KEEP_FUNC void SaveMngSpecial_HugoArchery() { KEEP_FUNC void SaveMngSpecial_CityPoeCycle() { gSaveManager.injectDefault_during(); - gSaveManager.setSaveAngle(71); - gSaveManager.setSavePosition(-14005.31f, 3000.0f, -15854.05f); + gSaveManager.setSaveAngle(0); + gSaveManager.setSavePosition(-13990.0f, 3000.0f, -16200.0f); gSaveManager.setLinkInfo(); } diff --git a/modules/boot/src/utils/card.cpp b/modules/boot/src/utils/card.cpp index 67da2e20..4fa39441 100644 --- a/modules/boot/src/utils/card.cpp +++ b/modules/boot/src/utils/card.cpp @@ -36,18 +36,27 @@ int32_t GZ_storageWrite(Storage* storage, void* data, int32_t size, int32_t offs int32_t read_bytes = 0; while (result == Ready && size > 0) { - StorageRead(*storage, buf, sector_size, (offset & ~(sector_size - 1))); - if (result != Ready) { + OSReport("GZ_storageWrite: Writing 0x%x bytes to offset 0x%x\n", MIN(size, sector_size), offset); + StorageSeek(storage, (offset & ~(sector_size - 1)), STORAGE_SEEK_BEG); + StorageRead(*storage, buf, sector_size); + if (storage->result != Ready) { + result = IoError; break; } int32_t rem_size = sector_size - (offset & (sector_size - 1)); memcpy(buf + (offset & (sector_size - 1)), (void*)((uint32_t)data + read_bytes), MIN(rem_size, size)); - StorageWrite(*storage, buf, sector_size, (offset & ~(sector_size - 1))); + StorageSeek(storage, (offset & ~(sector_size - 1)), STORAGE_SEEK_BEG); + StorageWrite(*storage, buf, sector_size); + if (storage->result != Ready) { + result = IoError; + break; + } read_bytes += MIN(rem_size, size); size -= rem_size; offset += rem_size; } + OSReport("GZ_storageWrite: Done Writing\n"); delete[] buf; return result; } @@ -62,7 +71,8 @@ int32_t GZ_storageRead(Storage* storage, void* data, int32_t size, int32_t offse int32_t read_bytes = 0; while (result == Ready && size > 0) { - StorageRead(*storage, buf, sector_size, (offset & ~(sector_size - 1))); + StorageSeek(storage, (offset & ~(sector_size - 1)), STORAGE_SEEK_BEG); + StorageRead(*storage, buf, sector_size); if (result != Ready) { break; } @@ -244,7 +254,11 @@ KEEP_FUNC void GZ_storeMemfile(Storage& storage) { posData.cam.target = matrixInfo.matrix_info->target; posData.cam.pos = matrixInfo.matrix_info->pos; posData.angle = dComIfGp_getPlayer()->shape_angle.y; - uint32_t file_size = (uint32_t)(ceil((double)sizeof(dSv_info_c) / (double)storage.sector_size) * + OSReport("GZ_storeMemfile: position: {%f, %f, %f}\n", posData.link.x, posData.link.y, posData.link.z); + OSReport("GZ_storeMemfile: angle: %f\n", posData.angle); + OSReport("GZ_storeMemfile: cam target: {%f, %f, %f}\n", posData.cam.target.x, posData.cam.target.y, posData.cam.target.z); + OSReport("GZ_storeMemfile: cam pos: {%f, %f, %f}\n", posData.cam.pos.x, posData.cam.pos.y, posData.cam.pos.z); + uint32_t file_size = (uint32_t)(ceil((double)(sizeof(dSv_info_c) + 1 + sizeof(PositionData)) / (double)storage.sector_size) * storage.sector_size); storage.result = StorageDelete(0, storage.file_name_buffer); @@ -258,10 +272,13 @@ KEEP_FUNC void GZ_storeMemfile(Storage& storage) { setReturnPlace(g_dComIfG_gameInfo.play.mStartStage.mStage, g_dComIfG_gameInfo.play.mEvent.field_0x12c, 0); - storage.result = GZ_storageWrite(&storage, &g_dComIfG_gameInfo, sizeof(dSv_info_c), 0, + uint8_t* data = new (-32) uint8_t[sizeof(dSv_info_c) + 1 + sizeof(PositionData)]; + memcpy(data, &g_dComIfG_gameInfo, sizeof(dSv_info_c)); + memcpy(&data[sizeof(dSv_info_c) + 1], &posData, sizeof(PositionData)); + storage.result = GZ_storageWrite(&storage, data, sizeof(dSv_info_c) + 1 + sizeof(PositionData), 0, storage.sector_size); - storage.result = GZ_storageWrite(&storage, &posData, sizeof(posData), - sizeof(dSv_info_c) + 1, storage.sector_size); + OSReport("GZ_storeMemfile: data write result: %d\n", storage.result); + delete[] data; if (storage.result == Ready) { FIFOQueue::push("saved memfile!", Queue); } else { @@ -332,7 +349,13 @@ KEEP_FUNC void GZ_loadMemfile(Storage& storage) { storage.result = StorageOpen(0, storage.file_name_buffer, &storage.info, OPEN_MODE_RW); if (storage.result == Ready) { PositionData posData; + OSReport("GZ_loadMemfile: reading position data at 0x%x\n", sizeof(dSv_info_c) + 1); storage.result = GZ_readMemfile(&storage, posData, storage.sector_size); + OSReport("GZ_loadMemfile: result: %d\n", storage.result); + OSReport("GZ_loadMemfile: position: {%f, %f, %f}\n", posData.link.x, posData.link.y, posData.link.z); + OSReport("GZ_loadMemfile: angle: %f\n", posData.angle); + OSReport("GZ_loadMemfile: cam target: {%f, %f, %f}\n", posData.cam.target.x, posData.cam.target.y, posData.cam.target.z); + OSReport("GZ_loadMemfile: cam pos: {%f, %f, %f}\n", posData.cam.pos.x, posData.cam.pos.y, posData.cam.pos.z); if (storage.result == Ready) { FIFOQueue::push("loaded memfile!", Queue); SaveManager::injectDefault_before(); diff --git a/modules/boot/src/utils/hook.cpp b/modules/boot/src/utils/hook.cpp index 397ac53b..5779829e 100644 --- a/modules/boot/src/utils/hook.cpp +++ b/modules/boot/src/utils/hook.cpp @@ -18,6 +18,7 @@ #include "collision_view.h" #include "features/projection_view/include/projection_view.h" #include "libtp_c/include/m_Do/m_Do_printf.h" +#include "libtp_c/include/d/s/d_s_logo.h" #define HOOK_DEF(rettype, name, params) \ typedef rettype(*tp_##name##_t) params; \ @@ -51,7 +52,6 @@ HOOK_DEF(void, dCcS__draw, (dCcS*)); HOOK_DEF(void, BeforeOfPaint, (void)); HOOK_DEF(void, dCcS__MoveAfterCheck, (dCcS*)); - HOOK_DEF(int, dScnPly__phase_1, (void*)); HOOK_DEF(int, dScnPly__phase_4, (void*)); @@ -59,6 +59,14 @@ HOOK_DEF(void, dBgS_Acch__CrrPos, (dBgS_Acch*, dBgS&)); HOOK_DEF(void, daAlink_c__setCutJumpSpeed, (daAlink_c*, int)); HOOK_DEF(void, daAlink_c__posMove, (daAlink_c*)); +#ifdef WII_PLATFORM +HOOK_DEF(void, dScnLogo_c__create, (dScnLogo_c*)); +HOOK_DEF(void, dScnLogo_c__dvdWaitDraw, (dScnLogo_c*)); +HOOK_DEF(int, mDoGph_gInf_c__startFadeOut, (int)); +#else +HOOK_DEF(void, dScnLogo_c__warningInDraw, (dScnLogo_c*)); +#endif + namespace Hook { void gameLoopHook(void) { game_loop(); @@ -74,9 +82,9 @@ void drawHook(void* p1) { #ifdef PR_TEST void myExceptionCallbackHook(void) { ExceptionCallbackTrampoline(); - gzCrashAddr = 1; - DCFlushRange((void*)(&gzCrashAddr), sizeof(gzCrashAddr)); - ICInvalidateRange((void*)(&gzCrashAddr), sizeof(gzCrashAddr)); + gzCrashReport = 1; + DCFlushRange((void*)(&gzCrashReport), sizeof(gzCrashReport)); + ICInvalidateRange((void*)(&gzCrashReport), sizeof(gzCrashReport)); } #endif // PR_TEST @@ -151,6 +159,8 @@ void onSwitchHook(void* addr, int pFlag, int i_roomNo) { if (g_flagLogEnabled) { if (pFlag < 0x80) { snprintf(buf, sizeof(buf), "%s[%d] : %d | ON", "Memory Switch", tmp >> 5, tmp & 0x1F); + OSReport("pFlag: %d\n", pFlag); + OSReport("roomNo: %d\n", i_roomNo); } else if (pFlag < 0xC0) { tmp -= 0x80; snprintf(buf, sizeof(buf), "%s[%d] : %d | ON", "Dan Switch", tmp >> 5, tmp & 0x1F); @@ -189,10 +199,6 @@ int endSaveInjectHook(void* i_scene) { int rt = dScnPly__phase_4Trampoline(i_scene); if (SaveManager::s_injectSave || SaveManager::s_injectMemfile) { - if (gSaveManager.mPracticeFileOpts.inject_options_after_load) { - SaveManager::s_applyAfterTimer = 5; - } - SaveManager::s_injectSave = false; SaveManager::s_injectMemfile = false; } @@ -228,6 +234,28 @@ void daAlink_c__setCutJumpSpeedHook(daAlink_c* i_this, int i_air) { daAlink_c__setCutJumpSpeedTrampoline(i_this, i_air); } +#ifdef WII_PLATFORM +// Skip intro logos (Wii) +void dScnLogo_c__create(dScnLogo_c* i_this) { + dScnLogo_c__createTrampoline(i_this); + i_this->mExecCommand = dScnLogo_c::EXEC_DVD_WAIT; +} + +int mDoGph_gInf_c__startFadeOut(int i) { + return mDoGph_gInf_c__startFadeOutTrampoline(i); +} + +void dScnLogo_c__dvdWaitDraw(dScnLogo_c* i_this) { + dScnLogo_c__dvdWaitDrawTrampoline(i_this); +} + +#else +// Skip intro logos (GCN) +void dScnLogo_c__warningInDraw(dScnLogo_c* i_this) { + i_this->mExecCommand = dScnLogo_c::EXEC_DVD_WAIT; +} +#endif + void setupLJAProjectionLine(daAlink_c* i_this) { bool got_it = false; @@ -392,12 +420,16 @@ void daAlink_c__posMoveHook(daAlink_c* i_this) { #define f_myExceptionCallback myExceptionCallback_unsigned #define f_dScnPly__phase_1 phase_1_dScnPly_c___ #define f_dScnPly__phase_4 phase_4_dScnPly_c___ +#define f_dScnLogo_c__warningInDraw dScnLogo_c__warningInDraw_void_ #define f_dCcS__Draw dCcS__Draw_void_ #define f_dScnPly_BeforeOfPaint mDoGph_BeforeOfDraw_void_ #define f_dCcS__MoveAfterCheck dCcS__MoveAfterCheck_void_ #define f_dBgS_Acch__CrrPos dBgS_Acch__CrrPos_dBgS___ #define f_daAlink_c__setCutJumpSpeed daAlink_c__setCutJumpSpeed_int_ #define f_daAlink_c__posMove daAlink_c__posMove_void_ +#define f_dScnLogo_c__create dScnLogo_c__create_void_ +#define f_dScnLogo_c__dvdWaitDraw dScnLogo_c__dvdWaitDraw_void_ +#define f_mDoGph_gInf_c__startFadeOut mDoGph_gInf_c__startFadeOut_int_ #else #define draw_console draw__17JUTConsoleManagerCFv #define f_fapGm_Execute fapGm_Execute__Fv @@ -412,6 +444,7 @@ void daAlink_c__posMoveHook(daAlink_c* i_this) { #define f_myExceptionCallback myExceptionCallback__FUsP9OSContextUlUl #define f_dScnPly__phase_1 phase_1__FP9dScnPly_c #define f_dScnPly__phase_4 phase_4__FP9dScnPly_c +#define f_dScnLogo_c__warningInDraw warningInDraw__10dScnLogo_cFv #define f_dCcS__Draw Draw__4dCcSFv #define f_dScnPly_BeforeOfPaint dScnPly_BeforeOfPaint__Fv #define f_dCcS__MoveAfterCheck MoveAfterCheck__4dCcSFv @@ -441,6 +474,13 @@ void f_dCcS__MoveAfterCheck(dCcS*); void f_dBgS_Acch__CrrPos(dBgS_Acch*, dBgS&); void f_daAlink_c__setCutJumpSpeed(daAlink_c*, int); void f_daAlink_c__posMove(daAlink_c*); +#ifdef WII_PLATFORM +void f_dScnLogo_c__create(dScnLogo_c*); +void f_dScnLogo_c__dvdWaitDraw(dScnLogo_c*); +void f_mDoGph_gInf_c__startFadeOut(int); +#else +void f_dScnLogo_c__warningInDraw(dScnLogo_c*); +#endif } KEEP_FUNC void applyHooks() { @@ -468,6 +508,14 @@ KEEP_FUNC void applyHooks() { APPLY_HOOK(daAlink_c__setCutJumpSpeed, &f_daAlink_c__setCutJumpSpeed, daAlink_c__setCutJumpSpeedHook); APPLY_HOOK(daAlink_c__posMove, &f_daAlink_c__posMove, daAlink_c__posMoveHook); +#ifdef WII_PLATFORM + APPLY_HOOK(dScnLogo_c__create, &f_dScnLogo_c__create, dScnLogo_c__create); + APPLY_HOOK(dScnLogo_c__dvdWaitDraw, &f_dScnLogo_c__dvdWaitDraw, dScnLogo_c__dvdWaitDraw); + APPLY_HOOK(mDoGph_gInf_c__startFadeOut, &f_mDoGph_gInf_c__startFadeOut, mDoGph_gInf_c__startFadeOut); +#else +APPLY_HOOK(dScnLogo_c__warningInDraw, &f_dScnLogo_c__warningInDraw, dScnLogo_c__warningInDraw); +#endif + #ifdef PR_TEST APPLY_HOOK(ExceptionCallback, &f_myExceptionCallback, myExceptionCallbackHook); #endif diff --git a/modules/boot/src/utils/link.cpp b/modules/boot/src/utils/link.cpp index 9b494596..51371e80 100644 --- a/modules/boot/src/utils/link.cpp +++ b/modules/boot/src/utils/link.cpp @@ -24,6 +24,7 @@ KEEP_FUNC void GZ_displayLinkInfo() { char link_x[22]; char link_y[22]; char link_z[22]; + char link_action[22]; snprintf(link_angle, sizeof(link_angle), "angle: %d", (uint16_t)dComIfGp_getPlayer()->shape_angle.y); @@ -32,6 +33,8 @@ KEEP_FUNC void GZ_displayLinkInfo() { snprintf(link_x, sizeof(link_x), "x-pos: %.4f", dComIfGp_getPlayer()->current.pos.x); snprintf(link_y, sizeof(link_y), "y-pos: %.4f", dComIfGp_getPlayer()->current.pos.y); snprintf(link_z, sizeof(link_z), "z-pos: %.4f", dComIfGp_getPlayer()->current.pos.z); + snprintf(link_action, sizeof(link_action), "action: %d", dComIfGp_getPlayer()->mActionID); + Font::GZ_drawStr(link_angle, spriteOffset.x, spriteOffset.y + 20.0f, 0xFFFFFFFF, @@ -51,6 +54,9 @@ KEEP_FUNC void GZ_displayLinkInfo() { Font::GZ_drawStr(link_z, spriteOffset.x, spriteOffset.y + 120.0f, 0xFFFFFFFF, GZ_checkDropShadows()); + Font::GZ_drawStr(link_action, spriteOffset.x, + spriteOffset.y + 140.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); } else { Font::GZ_drawStr("angle: n/a", spriteOffset.x, spriteOffset.y + 20.0f, 0xFFFFFFFF, @@ -70,9 +76,62 @@ KEEP_FUNC void GZ_displayLinkInfo() { Font::GZ_drawStr("z-pos: n/a", spriteOffset.x, spriteOffset.y + 120.0f, 0xFFFFFFFF, GZ_checkDropShadows()); + Font::GZ_drawStr("action: n/a", spriteOffset.x, + spriteOffset.y + 140.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); } } +KEEP_FUNC void GZ_displayStageInfo() { + if (!GZStng_getData(STNG_TOOLS_STAGE_INFO, false)) { + return; + } + + Vec2 spriteOffset = GZ_getSpriteOffset(STNG_SPRITES_STAGE_INFO); + + char cur_stage[15]; + char cur_room[10]; + char cur_point[11]; + char cur_layer[10]; + + snprintf(cur_stage, sizeof(cur_stage), "Stage: %s", g_dComIfG_gameInfo.play.mStartStage.mStage); + snprintf(cur_room, sizeof(cur_room), "Room: %d", dStage_roomControl_c__mStayNo); + snprintf(cur_point, sizeof(cur_point), "Point: %d", g_dComIfG_gameInfo.play.mStartStage.mPoint); + snprintf(cur_layer, sizeof(cur_layer), "Layer: %d", dComIfG_play_c__getLayerNo(0)); + + Font::GZ_drawStr(cur_stage, spriteOffset.x, + spriteOffset.y + 20.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); + Font::GZ_drawStr(cur_room, spriteOffset.x, + spriteOffset.y + 40.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); + Font::GZ_drawStr(cur_point, spriteOffset.x, + spriteOffset.y + 60.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); + Font::GZ_drawStr(cur_layer, spriteOffset.x, + spriteOffset.y + 80.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); + + + char save_stage[20]; + char save_room[15]; + char save_point[16]; + + snprintf(save_stage, sizeof(save_stage), "Save Stage: %s", g_dComIfG_gameInfo.info.getPlayer().getPlayerReturnPlace().mName); + snprintf(save_room, sizeof(save_room), "Save Room: %d", g_dComIfG_gameInfo.info.getPlayer().getPlayerReturnPlace().mRoomNo); + snprintf(save_point, sizeof(save_point), "Save Point: %d", g_dComIfG_gameInfo.info.getPlayer().getPlayerReturnPlace().mPlayerStatus); + + Font::GZ_drawStr(save_stage, spriteOffset.x + 150.0f, + spriteOffset.y + 20.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); + Font::GZ_drawStr(save_room, spriteOffset.x + 150.0f, + spriteOffset.y + 40.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); + Font::GZ_drawStr(save_point, spriteOffset.x + 150.0f, + spriteOffset.y + 60.0f, 0xFFFFFFFF, + GZ_checkDropShadows()); +} + KEEP_FUNC void GZ_setTunicColor() { static int16_t cycle_r = 0; static int16_t cycle_g = 0; diff --git a/modules/features/ee_checker/CMakeLists.txt b/modules/features/ee_checker/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/features/ee_checker/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/features/ee_checker/include/ee_checker.h b/modules/features/ee_checker/include/ee_checker.h new file mode 100644 index 00000000..c2c7ac00 --- /dev/null +++ b/modules/features/ee_checker/include/ee_checker.h @@ -0,0 +1,5 @@ +#pragma once + +namespace EEChecker { +void execute(); +} // namespace EEChecker \ No newline at end of file diff --git a/modules/features/ee_checker/include/main.h b/modules/features/ee_checker/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/features/ee_checker/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/features/ee_checker/src/ee_checker.cpp b/modules/features/ee_checker/src/ee_checker.cpp new file mode 100644 index 00000000..5e702494 --- /dev/null +++ b/modules/features/ee_checker/src/ee_checker.cpp @@ -0,0 +1,108 @@ +#include "ee_checker.h" +#include "controller.h" +#include "fifo_queue.h" +#include +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/f_op/f_op_actor_mng.h" +#include "libtp_c/include/m_Do/m_Do_printf.h" +#include "libtp_c/include/d/d_procname.h" +#include "libtp_c/include/SSystem/SComponent/c_counter.h" + +u16 previous_action; // Tracks the previous action for adjusting the frame timing window, because some actions like land dive cut the beginning of the roll short +bool game_paused; // Whether the game is paused or not. Used to determine if the roll start frame should be incremented. +char msg_buffer[20]; // Buffer for the message to be printed. +s32 early_roll_frame; +s32 late_roll_frame; +s32 target_frame; +const u8 metamorphose_anm_length = 56; // length of the metamorphose animation in frames +s32 metamorphose_start_frame; // frame that the metamorphose animation starts on + +void checkRollFrame(daAlink_c* link) { + switch (link->mActionID) { + case daAlink_c::PROC_TALK: + previous_action = daAlink_c::PROC_TALK; + break; + case daAlink_c::PROC_METAMORPHOSE: + if (previous_action == daAlink_c::PROC_TALK) { + metamorphose_start_frame = cCt_getFrameCount(); + } + +#if DEBUG + OSReport("previous action: %d\n", previous_action); + OSReport("metamorphose start frame: %d\n", metamorphose_start_frame); + OSReport("Current metamorphose frame: %d\n", (cCt_getFrameCount() - metamorphose_start_frame)); +#endif + + if (GZ_getButtonPressed(A) && !GZ_getButtonHold(A)) { + early_roll_frame = cCt_getFrameCount(); + snprintf(msg_buffer, sizeof(msg_buffer), "early by %df", metamorphose_anm_length - (cCt_getFrameCount() - metamorphose_start_frame)); + FIFOQueue::push(msg_buffer, Queue, 0x0000CC00); + } + + previous_action = daAlink_c::PROC_METAMORPHOSE; + break; + + case daAlink_c::PROC_WAIT: + late_roll_frame = 0; + target_frame = cCt_getFrameCount(); + break; + case daAlink_c::PROC_MOVE: + case daAlink_c::PROC_WAIT_TURN: + case daAlink_c::PROC_MOVE_TURN: + if (GZ_getButtonPressed(A) && !GZ_getButtonHold(A)) { + late_roll_frame = cCt_getFrameCount(); + } + break; + case daAlink_c::PROC_FRONT_ROLL: +#if DEBUG + OSReport("Front roll, last action: %d\n", previous_action); + OSReport("Early roll frame: %d\n", early_roll_frame); + OSReport("Late roll frame: %d\n", late_roll_frame); +#endif + if (late_roll_frame == 0 && previous_action != daAlink_c::PROC_FRONT_ROLL) { + FIFOQueue::push("<3", Queue, 0x00CC0000); + } else if (previous_action != daAlink_c::PROC_FRONT_ROLL) { + snprintf(msg_buffer, sizeof(msg_buffer), "late by %df", late_roll_frame - target_frame); + FIFOQueue::push(msg_buffer, Queue, 0xCC000000); + } + + previous_action = daAlink_c::PROC_FRONT_ROLL; + break; + default: + early_roll_frame = 0; + late_roll_frame = 0; + } +} + +KEEP_FUNC void EEChecker::execute() { + // Retrieve player pointer + daAlink_c* link = dComIfGp_getPlayer(); + + // Early return if player pointer is invalid + if (!link) { + return; + } + + if (!daAlink_c__checkStageName("R_SP110")) { +#if DEBUG + OSReport("Player is not in goron elder room\n"); +#endif + return; + } + + if (link->current.pos.x > -1400.0f || link->current.pos.x < -1600.0f) { +#if DEBUG + OSReport("Player is not in the correct x position\n"); + return; +#endif + } + + if (link->current.pos.z < 4000.0f || link->current.pos.z > 4400.0f) { +#if DEBUG + OSReport("Player is not in the correct z position\n"); + return; +#endif + } + + checkRollFrame(link); +} \ No newline at end of file diff --git a/modules/features/ee_checker/src/main.cpp b/modules/features/ee_checker/src/main.cpp new file mode 100644 index 00000000..5ca0ec16 --- /dev/null +++ b/modules/features/ee_checker/src/main.cpp @@ -0,0 +1,15 @@ +#include +#include "gz_flags.h" +#include "ee_checker.h" + +namespace tpgz::modules { +void main() { + GZFlg_addFlag( + new GZFlag{GZFLG_EE, ACTIVE_FUNC(STNG_TOOLS_ELEVATOR_ESCAPE), GAME_LOOP, EEChecker::execute}); +} +void exit() { + auto* flg = GZFlg_removeFlag(GZFLG_EE); + delete flg; +} + +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/features/lfc_checker/CMakeLists.txt b/modules/features/lfc_checker/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/features/lfc_checker/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/features/lfc_checker/include/lfc_checker.h b/modules/features/lfc_checker/include/lfc_checker.h new file mode 100644 index 00000000..462c1b03 --- /dev/null +++ b/modules/features/lfc_checker/include/lfc_checker.h @@ -0,0 +1,5 @@ +#pragma once + +namespace LFCChecker { +void execute(); +} // namespace LFCChecker \ No newline at end of file diff --git a/modules/features/lfc_checker/include/main.h b/modules/features/lfc_checker/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/features/lfc_checker/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/features/lfc_checker/src/lfc_checker.cpp b/modules/features/lfc_checker/src/lfc_checker.cpp new file mode 100644 index 00000000..4746432e --- /dev/null +++ b/modules/features/lfc_checker/src/lfc_checker.cpp @@ -0,0 +1,213 @@ +#include "lfc_checker.h" +#include "controller.h" +#include "fifo_queue.h" +#include +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/f_op/f_op_actor_mng.h" +#include "libtp_c/include/m_Do/m_Do_printf.h" +#include "libtp_c/include/d/d_procname.h" +#include "libtp_c/include/SSystem/SComponent/c_counter.h" + +enum Phase { + LADDER_START, + LADDER_DURING, + LADDER_DURING_2, + LADDER_DROP, + LADDER_DROP_2, + LADDER_REGRAB +}; + +u8 phase = Phase::LADDER_START; +int frameDelta = 0; +s32 drop_1_start_frame; // The frame the first drop started on. +s32 drop_2_start_frame; // The frame the second drop started on. +bool game_paused; + +static void* searchLadderFreezard(void* i_actor, void* i_data) { + const float LADDER_FREEZARD_Y_POS = 950.0f; + + if (fopAcM_GetName(i_actor) == PROC_E_FB && static_cast(i_actor)->current.pos.y == LADDER_FREEZARD_Y_POS) { + return i_actor; + } + + return NULL; +} + +void checkOnLadder(daAlink_c* link) { + u16 current_action = link->mActionID; + + if (current_action == daAlink_c::PROC_LADDER_UP_START || + current_action == daAlink_c::PROC_LADDER_UP_END || + current_action == daAlink_c::PROC_LADDER_DOWN_START || + current_action == daAlink_c::PROC_LADDER_DOWN_END || + current_action == daAlink_c::PROC_LADDER_MOVE) { + if (phase == Phase::LADDER_START) { + phase = Phase::LADDER_DURING; + } else if (phase == Phase::LADDER_DROP) { + phase = Phase::LADDER_DURING_2; + } else { + phase = Phase::LADDER_REGRAB; + } + } else if (phase != Phase::LADDER_DROP) { + phase = Phase::LADDER_START; + } +} + +void checkFirstDrop(daAlink_c* link) { + if (link->mActionID == daAlink_c::PROC_FALL) { + if (link->current.pos.y < 247.0f) { + char msg[15]; + sprintf(msg, "off by -%f", 247.0f - link->current.pos.y); + FIFOQueue::push(msg, Queue, 0xFFFF0000); + phase = Phase::LADDER_START; + return; + } + + if (link->current.pos.y >= 247.0f && link->current.pos.y < 249.8f) { + FIFOQueue::push("Got it (1/2)", Queue, 0x00FF0000); + phase = Phase::LADDER_DROP; + drop_1_start_frame = cCt_getFrameCount(); + return; + } + + if (link->current.pos.y >= 249.8f) { + char msg[15]; + sprintf(msg, "off by +%f", link->current.pos.y - 249.8f); + FIFOQueue::push(msg, Queue, 0xFF000000); + phase = Phase::LADDER_START; + return; + } + } +} + +void checkSecondDrop(daAlink_c* link) { + if (link->mActionID == daAlink_c::PROC_FALL) { + drop_2_start_frame = cCt_getFrameCount(); + s32 delta = drop_2_start_frame - drop_1_start_frame; +#if DEBUG + OSReport("delta: %d\n", delta); +#endif + + + if (delta < 260 && delta >= 255) { + char msg[15]; + sprintf(msg, "early by %df", 260 - delta); + FIFOQueue::push(msg, Queue, 0x00FF0000); + phase = Phase::LADDER_DROP_2; + return; + } + + if (delta >= 260 && delta < 268) { + FIFOQueue::push("Got it (2/2)", Queue, 0x00FF0000); + phase = Phase::LADDER_REGRAB; + return; + } + + if (delta >= 268 && delta < 273) { + char msg[15]; + sprintf(msg, "late by %df", delta - 267); + FIFOQueue::push(msg, Queue, 0x00FF0000); + phase = Phase::LADDER_DROP_2; + return; + } + + if (delta >= 273) { + phase = Phase::LADDER_START; + return; + } + + } +} + +void framePauseUpdate() { + if (g_dComIfG_gameInfo.play.mPauseFlag) { +#if DEBUG + OSReport("game paused, incrementing start frame!\n"); +#endif + + drop_1_start_frame += 1; + game_paused = true; + } else { + if (game_paused) { + game_paused = false; + drop_1_start_frame++; + } + } +} + +KEEP_FUNC void LFCChecker::execute() { + // Retrieve player pointer + daAlink_c* link = dComIfGp_getPlayer(); + + // Early return if player pointer is invalid + if (!link) { + return; + } + + if (dComIfGp_getEvent().mHalt == true) { +#if DEBUG + OSReport("In event\n"); +#endif + return; + } + + if (dComIfGs_getTransformStatus() != STATUS_HUMAN) { +#if DEBUG + OSReport("Player is not in human form\n"); +#endif + return; + } + + if (!daAlink_c__checkStageName("D_MN11")) { +#if DEBUG + OSReport("Player is not in Snowpeak Ruins\n"); +#endif + return; + } + + if (fopAcM_GetRoomNo(link) != 4) { +#if DEBUG + OSReport("Player is not in courtyard\n"); +#endif + return; + } + + fopAc_ac_c* ladder_freezard = (fopAc_ac_c*)fpcEx_Search(searchLadderFreezard, nullptr); + + if (!ladder_freezard) { +#if DEBUG + OSReport("Ladder Freezard not found\n"); +#endif + return; + } + + switch(phase) { + case Phase::LADDER_START: + OSReport("Checking for link on ladder 1\n"); + checkOnLadder(link); + break; + case Phase::LADDER_DURING: + OSReport("Checking for first drop\n"); + checkFirstDrop(link); + break; + case Phase::LADDER_DROP: + OSReport("Checking for link on ladder 2\n"); + checkOnLadder(link); + framePauseUpdate(); + break; + case Phase::LADDER_DURING_2: + OSReport("Checking for second drop\n"); + framePauseUpdate(); + checkSecondDrop(link); + break; + case Phase::LADDER_DROP_2: + OSReport("Checking for link on ladder 3\n"); + checkOnLadder(link); + break; + case Phase::LADDER_REGRAB: + OSReport("Finished!\n"); + default: + phase = Phase::LADDER_START; + break; + } +} \ No newline at end of file diff --git a/modules/features/lfc_checker/src/main.cpp b/modules/features/lfc_checker/src/main.cpp new file mode 100644 index 00000000..bf81595c --- /dev/null +++ b/modules/features/lfc_checker/src/main.cpp @@ -0,0 +1,15 @@ +#include +#include "gz_flags.h" +#include "lfc_checker.h" + +namespace tpgz::modules { +void main() { + GZFlg_addFlag( + new GZFlag{GZFLG_LFC, ACTIVE_FUNC(STNG_TOOLS_LFC), GAME_LOOP, LFCChecker::execute}); +} +void exit() { + auto* flg = GZFlg_removeFlag(GZFLG_LFC); + delete flg; +} + +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/features/trigger_view/src/trigger_view.cpp b/modules/features/trigger_view/src/trigger_view.cpp index 487e6d3f..b86b1d61 100644 --- a/modules/features/trigger_view/src/trigger_view.cpp +++ b/modules/features/trigger_view/src/trigger_view.cpp @@ -368,6 +368,30 @@ void drawPurpleMistAvoid(fopAc_ac_c* actor) { dDbVw_drawCubeXlu(tag->mTargetAvoidPos, cubeSize, cubeAngle, targetColor); } +void drawLeeverData(fopAc_ac_c* actor) { + struct e_rb_class : public fopAc_ac_c { + u8 data[0xA66 - 0x568]; + /* 0xA66 */ u8 mIsChild; + /* 0xA67 */ u8 field_0xa67; + /* 0xA68 */ u8 mAppearRange; + /* 0xA69 */ u8 mDisappearRange; + }; + e_rb_class* leever = (e_rb_class*)actor; + + if (!leever->mIsChild) { + GXColor color = {0xFF, 0x00, 0x00, g_geometryOpacity}; + GXColor color2 = {0x00, 0x00, 0xFF, g_geometryOpacity}; + + cXyz pos(leever->current.pos); + if (pos.y < dComIfGp_getPlayer()->mLinkAcch.GetGroundH()) { + pos.y = dComIfGp_getPlayer()->mLinkAcch.GetGroundH() + 100.0f; + } + + dDbVw_drawCircleXlu(pos, leever->mAppearRange * 100.0f, color, 1, 20); + dDbVw_drawCircleXlu(pos, leever->mDisappearRange * 100.0f, color2, 1, 20); + } +} + KEEP_FUNC void execute() { if (g_triggerViewFlags[VIEW_LOAD_ZONES].active) { searchActorForCallback(PROC_SCENE_EXIT, drawSceneExit); @@ -413,5 +437,9 @@ KEEP_FUNC void execute() { if (g_triggerViewFlags[VIEW_MIST_AVOID].active) { searchActorForCallback(PROC_KYTAG08, drawPurpleMistAvoid); } + + if (g_triggerViewFlags[VIEW_LEEVER_RANGE].active) { + searchActorForCallback(PROC_E_RB, drawLeeverData); + } } } // namespace TriggerViewer \ No newline at end of file diff --git a/modules/init/src/main.cpp b/modules/init/src/main.cpp index fa0aa878..89e1cf5c 100644 --- a/modules/init/src/main.cpp +++ b/modules/init/src/main.cpp @@ -45,6 +45,7 @@ void main() { g_drawListener->addListener(GZ_renderMenuTitle); g_drawListener->addListener(GZ_renderFifoQueue); g_drawListener->addListener(GZ_displayLinkInfo); + g_drawListener->addListener(GZ_displayStageInfo); g_drawListener->addListener(GZ_drawHeapInfo); g_drawListener->addListener(Timer::drawTimer); g_drawListener->addListener(Timer::drawLoadTimer); @@ -67,6 +68,8 @@ void main() { g_modules.push_back(new Module{bit_active, "/tpgz/rels/features/bit.rel"}); #endif g_modules.push_back(new Module{corotd_active, "/tpgz/rels/features/corotd.rel"}); + g_modules.push_back(new Module{lfc_active, "/tpgz/rels/features/lfc_checker.rel"}); + g_modules.push_back(new Module{ee_active, "/tpgz/rels/features/ee_checker.rel"}); g_modules.push_back(new Module{mash_checker_active, "/tpgz/rels/features/mash_checker.rel"}); g_modules.push_back(new Module{gorge_active, "/tpgz/rels/features/gorge.rel"}); g_modules.push_back(new Module{rollcheck_active, "/tpgz/rels/features/rollcheck.rel"}); @@ -120,6 +123,7 @@ void GZ_PosSettings_initDefaults() { #endif GZStng_add(STNG_SPRITES_MENU, new Vec2{25.f, 60.f}, sizeof(Vec2)); GZStng_add(STNG_SPRITES_DEBUG_INFO, new Vec2{450.0f, 200.f}, sizeof(Vec2)); + GZStng_add(STNG_SPRITES_STAGE_INFO, new Vec2{145.0f, 350.f}, sizeof(Vec2)); GZStng_add(STNG_SPRITES_TIMER_SPR, new Vec2{450.0f, 420.f}, sizeof(Vec2)); GZStng_add(STNG_SPRITES_LOAD_TIMER_SPR, new Vec2{450.0f, 30.f}, sizeof(Vec2)); GZStng_add(STNG_SPRITES_IGT_TIMER_SPR, new Vec2{35.0f, 30.f}, sizeof(Vec2)); diff --git a/modules/menus/menu_actor_list/include/actor_list_menu.h b/modules/menus/menu_actor_list/include/actor_list_menu.h index 57c94b60..67242279 100644 --- a/modules/menus/menu_actor_list/include/actor_list_menu.h +++ b/modules/menus/menu_actor_list/include/actor_list_menu.h @@ -22,6 +22,8 @@ enum { ACTOR_ANGLE_X_INDEX, ACTOR_ANGLE_Y_INDEX, ACTOR_ANGLE_Z_INDEX, + ACTOR_ADDRESS_INDEX, + ACTOR_PROC_INDEX, ACTOR_PARAMS_INDEX, ACTOR_LIST_LINE_COUNT, @@ -36,7 +38,7 @@ class ActorListMenu : public Menu { private: void updateActorData(); template - void updateValue(T* value, f32 smallChange, f32 largeChange, bool increase, bool largeIncrement); + void updateValue(T*, bool); void loadActorName(); void checkAndCloseMenu(); void checkAndRestoreMenu(); diff --git a/modules/menus/menu_actor_list/src/actor_list_menu.cpp b/modules/menus/menu_actor_list/src/actor_list_menu.cpp index bf506391..8b1501b5 100644 --- a/modules/menus/menu_actor_list/src/actor_list_menu.cpp +++ b/modules/menus/menu_actor_list/src/actor_list_menu.cpp @@ -19,20 +19,26 @@ #ifdef GCN_PLATFORM #define CONTROLLER_RIGHT GZPad::DPAD_RIGHT #define CONTROLLER_LEFT GZPad::DPAD_LEFT -#define CONTROLLER_Z GZPad::Z #define CONTROLLER_A GZPad::A -#define MEM_SWITCH_BTN GZPad::Y -#define CONTROL_TEXT "Y" +#define MEM_SWITCH_BTN GZPad::Z +#define SLOW_INC_BTN GZPad::X +#define FAST_INC_BTN GZPad::Y +#define MEM_TEXT "Z" +#define SLOW_INC_TEXT "X" +#define FAST_INC_TEXT "Y" #define DELETE_TEXT "START" #define DELETE_BUTTON GZPad::START #endif #ifdef WII_PLATFORM #define CONTROLLER_RIGHT GZPad::DPAD_RIGHT #define CONTROLLER_LEFT GZPad::DPAD_LEFT -#define CONTROLLER_Z GZPad::Z #define CONTROLLER_A GZPad::A -#define MEM_SWITCH_BTN GZPad::C -#define CONTROL_TEXT "C" +#define MEM_SWITCH_BTN GZPad::ONE +#define SLOW_INC_BTN GZPad::Z +#define FAST_INC_BTN GZPad::C +#define MEM_TEXT "1" +#define SLOW_INC_TEXT "Z" +#define FAST_INC_TEXT "C" #define DELETE_TEXT "PLUS" #define DELETE_BUTTON GZPad::PLUS #endif @@ -42,99 +48,12 @@ */ procBinData l_procData; -void ring_close_proc_wrapper(void* arg) { - dMw_c__ring_close_proc(arg); -} - -void collect_close_proc_wrapper(void* arg) { - dMw_c__collect_close_proc(arg); -} - -void dmap_close_proc_wrapper(void* arg) { - dMw_c__dmap_close_proc(arg); -} - -void fmap_close_proc_wrapper(void* arg) { - dMw_c__fmap_close_proc(arg); -} - -void collect_save_close_proc_wrapper(void* arg) { - dMw_c__collect_save_close_proc(arg); -} - -void collect_option_close_proc_wrapper(void* arg) { - dMw_c__collect_option_close_proc(arg); -} - -void collect_letter_close_proc_wrapper(void* arg) { - dMw_c__collect_letter_close_proc(arg); -} - -void collect_fishing_close_proc_wrapper(void* arg) { - dMw_c__collect_fishing_close_proc(arg); -} - -void collect_skill_close_proc_wrapper(void* arg) { - dMw_c__collect_skill_close_proc(arg); -} - -void collect_insect_close_proc_wrapper(void* arg) { - dMw_c__collect_insect_close_proc(arg); -} - -void insect_close_proc_wrapper(void* arg) { - dMw_c__insect_close_proc(arg); -} - -typedef void (*procFunc)(void*); -procFunc move_proc[] = { - NULL, - NULL, - ring_close_proc_wrapper, - NULL, - NULL, - collect_close_proc_wrapper, - NULL, - NULL, - fmap_close_proc_wrapper, - NULL, - NULL, - dmap_close_proc_wrapper, - NULL, - NULL, - collect_save_close_proc_wrapper, - NULL, - NULL, - collect_option_close_proc_wrapper, - NULL, - NULL, - collect_letter_close_proc_wrapper, - NULL, - NULL, - collect_fishing_close_proc_wrapper, - NULL, - NULL, - collect_skill_close_proc_wrapper, - NULL, - NULL, - collect_insect_close_proc_wrapper, - NULL, - NULL, - NULL, - insect_close_proc_wrapper -}; - /** * @brief Checks and closes any menu that's currently open. * * @details This function is used to close any menu that's currently open. * It stores the current menu status and then closes the menu by setting the status to none and running the currently open menu's closer function via move_proc function table. * - * @note Currently the function won't catch if the menu status is in opening or closing status (two statuses during menu transitions). It will only catch the "move" status. - * We may have to account for this later. - * - * @note The move_proc function table already exists in code. We can probably just map directly to it in the future instead of defining our own. - * */ void ActorListMenu::checkAndCloseMenu() { if (g_meter2_info.mMenuWindowClass) { @@ -142,22 +61,14 @@ void ActorListMenu::checkAndCloseMenu() { case dMw_c::NO_MENU: g_dComIfG_gameInfo.play.mPauseFlag = false; break; - case dMw_c::RING_MOVE: - case dMw_c::COLLECT_MOVE: - case dMw_c::DMAP_MOVE: - case dMw_c::FMAP_MOVE: - case dMw_c::SAVE_MOVE: - case dMw_c::OPTIONS_MOVE: - case dMw_c::LETTER_MOVE: - case dMw_c::FISHING_MOVE: - case dMw_c::SKILL_MOVE: - case dMw_c::INSECT_MOVE: - case dMw_c::INSECT_AGITHA_MOVE: + default: l_menuStatus = g_meter2_info.mMenuWindowClass->mMenuStatus; l_windowStatus = g_meter2_info.mWindowStatus; + g_meter2_info.offMenuInForce(g_meter2_info.mMenuWindowClass->mMenuStatus); + g_meter2_info.mMenuWindowClass->mMenuStatus = dMw_c::NO_MENU; g_meter2_info.mWindowStatus = dMw_c::NO_MENU; - move_proc[g_meter2_info.mMenuWindowClass->mMenuStatus](g_meter2_info.mMenuWindowClass); g_dComIfG_gameInfo.play.mPauseFlag = false; + break; } } } @@ -175,23 +86,8 @@ void ActorListMenu::checkAndCloseMenu() { void ActorListMenu::checkAndRestoreMenu() { if (l_menuStatus != dMw_c::NO_MENU) { g_dComIfG_gameInfo.play.mPauseFlag = true; - - switch (l_menuStatus) { - case dMw_c::RING_MOVE: - case dMw_c::COLLECT_MOVE: - case dMw_c::FMAP_MOVE: - case dMw_c::SAVE_MOVE: - case dMw_c::OPTIONS_MOVE: - case dMw_c::LETTER_MOVE: - case dMw_c::FISHING_MOVE: - case dMw_c::SKILL_MOVE: - case dMw_c::INSECT_MOVE: - case dMw_c::INSECT_AGITHA_MOVE: - case dMw_c::DMAP_MOVE: - g_meter2_info.mWindowStatus = l_windowStatus; - g_meter2_info.mMenuWindowClass->mMenuStatus = l_menuStatus; - break; - } + g_meter2_info.mWindowStatus = l_windowStatus; + g_meter2_info.mMenuWindowClass->mMenuStatus = l_menuStatus; } } @@ -199,13 +95,15 @@ KEEP_FUNC ActorListMenu::ActorListMenu(Cursor& cursor, ActorListData& data) : Menu(cursor), l_index(data.l_index), lines{ - {"", ACTOR_NAME_INDEX, "Z+A: freeze actor, Z+" DELETE_TEXT ": delete actor, " CONTROL_TEXT " view memory", false}, - {"", ACTOR_POSITION_X_INDEX, "dpad: +/-10.0, Z+dpad: +/-100.0", false}, - {"", ACTOR_POSITION_Y_INDEX, "dpad: +/-10.0, Z+dpad: +/-100.0", false}, - {"", ACTOR_POSITION_Z_INDEX, "dpad: +/-10.0, Z+dpad: +/-100.0", false}, - {"", ACTOR_ANGLE_X_INDEX, "dpad: +/-10, Z+dpad: +/-100", false}, - {"", ACTOR_ANGLE_Y_INDEX, "dpad: +/-10, Z+dpad: +/-100", false}, - {"", ACTOR_ANGLE_Z_INDEX, "dpad: +/-10, Z+dpad: +/-100", false}, + {"", ACTOR_NAME_INDEX, "A: freeze actor, " DELETE_TEXT ": delete actor, " MEM_TEXT " view memory", false}, + {"", ACTOR_POSITION_X_INDEX, "dpad: +/-100.0, " SLOW_INC_TEXT "+dpad: +/-1.0, " FAST_INC_TEXT "+dpad: +/-1000.0", false}, + {"", ACTOR_POSITION_Y_INDEX, "dpad: +/-100.0, " SLOW_INC_TEXT "+dpad: +/-1.0, " FAST_INC_TEXT "+dpad: +/-1000.0", false}, + {"", ACTOR_POSITION_Z_INDEX, "dpad: +/-100.0, " SLOW_INC_TEXT "+dpad: +/-1.0, " FAST_INC_TEXT "+dpad: +/-1000.0", false}, + {"", ACTOR_ANGLE_X_INDEX, "dpad: +/-100, " SLOW_INC_TEXT "+dpad: +/-1, " FAST_INC_TEXT "+dpad: +/-1000", false}, + {"", ACTOR_ANGLE_Y_INDEX, "dpad: +/-100, " SLOW_INC_TEXT "+dpad: +/-1, " FAST_INC_TEXT "+dpad: +/-1000", false}, + {"", ACTOR_ANGLE_Z_INDEX, "dpad: +/-100, " SLOW_INC_TEXT "+dpad: +/-1, " FAST_INC_TEXT "+dpad: +/-1000", false}, + {"", ACTOR_ADDRESS_INDEX, "current actor address", false}, + {"", ACTOR_PROC_INDEX, "current actor proc id", false}, {"", ACTOR_PARAMS_INDEX, "current actor parameters", false}, } { // store camera position and target @@ -228,15 +126,18 @@ ActorListMenu::~ActorListMenu() { matrixInfo.matrix_info->pos = l_cameraPos; matrixInfo.matrix_info->target = l_cameraTarget; + // restore evt manager camera play & HUD dComIfGp_getEventManager().mCameraPlay = 0; g_drawHIO.mHUDAlpha = 1.0f; - } template -void ActorListMenu::updateValue(T* value, f32 smallChange, f32 largeChange, bool increase, bool largeIncrement) { +void ActorListMenu::updateValue(T* value, bool increase) { if (value != NULL) { - *value += (increase ? 1 : -1) * (largeIncrement ? largeChange : smallChange); + f32 change; + GZ_getButtonPressed(FAST_INC_BTN) ? change = 1000.0f : GZ_getButtonPressed(SLOW_INC_BTN) ? change = 1.0f : change = 100.0f; + + *value += (increase ? 1 : -1) * change; } } @@ -277,10 +178,7 @@ void ActorListMenu::draw() { bool rightPressed = GZ_getButtonRepeat(CONTROLLER_RIGHT,1); bool leftPressed = GZ_getButtonRepeat(CONTROLLER_LEFT,1); - bool zPressed = GZ_getButtonPressed(CONTROLLER_Z); - - f32 smallPosChange = 10.0f, largePosChange = 100.0f; - int smallAngleChange = 100, largeAngleChange = 1000; + switch (cursor.y) { case ACTOR_NAME_INDEX: @@ -302,7 +200,7 @@ void ActorListMenu::draw() { loadActorName(); } - if (GZ_getButtonPressed(CONTROLLER_Z) && GZ_getButtonPressed(DELETE_BUTTON)) { + if (GZ_getButtonRepeat(DELETE_BUTTON)) { if (g_currentActor) { if (g_currentActor->mBase.mProcName != PROC_ALINK) { fopAcM_delete(g_currentActor); @@ -310,7 +208,7 @@ void ActorListMenu::draw() { } } - if (GZ_getButtonPressed(CONTROLLER_Z) && GZ_getButtonPressed(CONTROLLER_A)) { + if (GZ_getButtonRepeat(CONTROLLER_A)) { if (g_currentActor) { g_currentActor->mBase.mPauseFlag = !g_currentActor->mBase.mPauseFlag; } @@ -328,34 +226,35 @@ void ActorListMenu::draw() { break; case ACTOR_POSITION_X_INDEX: if (rightPressed || leftPressed) { - updateValue(&g_currentActor->current.pos.x, smallPosChange, largePosChange, rightPressed, zPressed); + updateValue(&g_currentActor->current.pos.x, rightPressed); } break; case ACTOR_POSITION_Y_INDEX: if (rightPressed || leftPressed) { - updateValue(&g_currentActor->current.pos.y, smallPosChange, largePosChange, rightPressed, zPressed); + updateValue(&g_currentActor->current.pos.y, rightPressed); } break; case ACTOR_POSITION_Z_INDEX: if (rightPressed || leftPressed) { - updateValue(&g_currentActor->current.pos.z, smallPosChange, largePosChange, rightPressed, zPressed); + updateValue(&g_currentActor->current.pos.z, rightPressed); } break; case ACTOR_ANGLE_X_INDEX: if (rightPressed || leftPressed) { - updateValue(&g_currentActor->shape_angle.x, smallAngleChange, largeAngleChange, rightPressed, zPressed); + updateValue(&g_currentActor->shape_angle.x, rightPressed); } break; case ACTOR_ANGLE_Y_INDEX: if (rightPressed || leftPressed) { - updateValue(&g_currentActor->shape_angle.y, smallAngleChange, largeAngleChange, rightPressed, zPressed); + updateValue(&g_currentActor->shape_angle.y, rightPressed); } break; case ACTOR_ANGLE_Z_INDEX: if (rightPressed || leftPressed) { - updateValue(&g_currentActor->shape_angle.z, smallAngleChange, largeAngleChange, rightPressed, zPressed); + updateValue(&g_currentActor->shape_angle.z, rightPressed); } break; + case ACTOR_PROC_INDEX: case ACTOR_PARAMS_INDEX: // allowing arbitrary updates here causes frequent crashes. removing for now. break; @@ -369,6 +268,8 @@ void ActorListMenu::draw() { lines[ACTOR_ANGLE_X_INDEX].printf("rot-x: <0x%04X>", static_cast(g_currentActor->shape_angle.x)); lines[ACTOR_ANGLE_Y_INDEX].printf("rot-y: <0x%04X>", static_cast(g_currentActor->shape_angle.y)); lines[ACTOR_ANGLE_Z_INDEX].printf("rot-z: <0x%04X>", static_cast(g_currentActor->shape_angle.z)); + lines[ACTOR_ADDRESS_INDEX].printf("addr: 0x%08X", g_currentActor); + lines[ACTOR_PROC_INDEX].printf("proc id: %d", g_currentActor->mBase.mProcName); lines[ACTOR_PARAMS_INDEX].printf("params: 0x%08X", g_currentActor->mBase.mParameters); } diff --git a/modules/menus/menu_actor_spawn/src/actor_spawn_menu.cpp b/modules/menus/menu_actor_spawn/src/actor_spawn_menu.cpp index dc1be7de..a625e936 100644 --- a/modules/menus/menu_actor_spawn/src/actor_spawn_menu.cpp +++ b/modules/menus/menu_actor_spawn/src/actor_spawn_menu.cpp @@ -3,6 +3,7 @@ #include "settings.h" #include "libtp_c/include/d/com/d_com_inf_game.h" #include "libtp_c/include/f_op/f_op_actor_mng.h" +#include "libtp_c/include/f_pc/f_pc_stdcreate_req.h" #include "libtp_c/include/m_Do/m_Do_printf.h" #include "gz_flags.h" #include "pos_settings.h" @@ -57,10 +58,19 @@ KEEP_FUNC ActorSpawnMenu::ActorSpawnMenu(ActorSpawnData& data) ActorSpawnMenu::~ActorSpawnMenu() {} -void actorFastCreateAtLink(short id, uint32_t parameters, int8_t subtype) { - fopAcM_create(id, parameters, &dComIfGp_getPlayer()->current.pos, - dComIfGp_getPlayer()->current.roomNo, &dComIfGp_getPlayer()->current.angle, - nullptr, subtype); +void actorFastCreateAtLink(s16 id, u32 parameters, s8 subtype) { + fopAcM_prm_class* appen = fopAcM_CreateAppend(); + if (appen != NULL) { + appen->mParameter = parameters; + appen->mPos = dComIfGp_getPlayer()->current.pos; + appen->mAngle = dComIfGp_getPlayer()->current.angle; + appen->mEnemyNo = 0xFFFF; + appen->mSubtype = subtype; + appen->mRoomNo = dComIfGp_getPlayer()->current.roomNo; + + layer_class* curLayer = fpcLy_CurrentLayer(); + fpcSCtRq_Request(curLayer, id, nullptr, nullptr, appen); + } } void ActorSpawnMenu::loadActorName(s16& i_procName) { diff --git a/modules/menus/menu_any_bite_saves/include/any_bite_saves_menu.h b/modules/menus/menu_any_bite_saves/include/any_bite_saves_menu.h index aecd0681..25b8a816 100644 --- a/modules/menus/menu_any_bite_saves/include/any_bite_saves_menu.h +++ b/modules/menus/menu_any_bite_saves/include/any_bite_saves_menu.h @@ -1,5 +1,6 @@ #include "menus/menu.h" +#ifdef GCN_PLATFORM #define ANY_BITE_SPECIALS_AMNT 18 enum AnyBiTEPracticeIndex { @@ -53,6 +54,66 @@ enum AnyBiTEPracticeIndex { // Entry used as a count of entries ANY_BITE_SAVES_COUNT }; +#endif +#ifdef WII_PLATFORM +#define ANY_BITE_SPECIALS_AMNT 13 + +enum AnyBiTEPracticeIndex { + BITE_ORDON_GATE_CLIP_INDEX, + BITE_BACK_IN_TIME_INDEX, + BITE_SEAM_CLIP_INDEX, + BITE_GOATS_INDEX, + BITE_HUGO_INDEX, + BITE_FARON_TWILIGHT_INDEX, + BITE_EMS_INDEX, + BITE_MIST_INDEX, + BITE_KB1_INDEX, + BITE_ELDIN_TWILIGHT_INDEX, + BITE_BOMBHOUSE_SKIP_INDEX, + BITE_EPONA_OOB_INDEX, + BITE_LANAYRU_TWILIGHT_INDEX, + BITE_WATERFALL_SIDEHOP_INDEX, + BITE_BOSS_BUG_INDEX, + BITE_IZA_INDEX, + BITE_PLUMM_OOB_INDEX, + BITE_ENTER_LAKEBED_INDEX, + BITE_LAKEBED_1_INDEX, + BITE_TOAD_INDEX, + BITE_ONEBOMB_INDEX, + BITE_MDH_TOWER_INDEX, + BITE_MDH_BRIDGE_INDEX, + BITE_MESSENGER_SKIP_INDEX, + BITE_SPR_MBBB_INDEX, + BITE_FREEZARD_SKIP_INDEX, + BITE_DARK_HAMMER_INDEX, + BITE_BULBLIN_CAMP_INDEX, + BITE_AG_INDEX, + BITE_POE_1_SKIP_INDEX, + BITE_EARLY_BOSS_KEY_INDEX, + BITE_DSS_INDEX, + BITE_STALLORD_INDEX, + BITE_STALLORD2_INDEX, + BITE_CITS_EARLY_INDEX, + BITE_CITS_1_INDEX, + BITE_AERALFOS_SKIP_INDEX, + BITE_CITS_2_INDEX, + BITE_FAN_TOWER_INDEX, + BITE_ARGOROK_INDEX, + BITE_PALACE_1_INDEX, + BITE_STUPID_ROOM_INDEX, + BITE_PALACE_2_INDEX, + BITE_EARLY_PLATFORM_INDEX, + BITE_ZANT_INDEX, + BITE_HC_INDEX, + BITE_DARKNUT_INDEX, + BITE_HC_TOWER_INDEX, + BITE_BEAST_GANON_INDEX, + BITE_HORSEBACK_GANON_INDEX, + + // Entry used as a count of entries + ANY_BITE_SAVES_COUNT +}; +#endif class AnyBiTESavesMenu : public Menu { public: diff --git a/modules/menus/menu_any_bite_saves/src/any_bite_saves_menu.cpp b/modules/menus/menu_any_bite_saves/src/any_bite_saves_menu.cpp index 50aee8c9..7c7dc5b6 100644 --- a/modules/menus/menu_any_bite_saves/src/any_bite_saves_menu.cpp +++ b/modules/menus/menu_any_bite_saves/src/any_bite_saves_menu.cpp @@ -7,6 +7,7 @@ KEEP_FUNC AnyBiTESavesMenu::AnyBiTESavesMenu(Cursor& cursor) : Menu(cursor), lines{ +#ifdef GCN_PLATFORM {"ordon gate clip", BITE_ORDON_GATE_CLIP_INDEX, "Gate Clip outside Ordon Spring"}, {"back in time", BITE_BACK_IN_TIME_INDEX, "Back in Time off the Ordon Spring bridge"}, {"goats", BITE_GOATS_INDEX, "Goat herding 2"}, @@ -54,6 +55,59 @@ KEEP_FUNC AnyBiTESavesMenu::AnyBiTESavesMenu(Cursor& cursor) {"final tower climb", BITE_HC_TOWER_INDEX, "The tower climb before Ganondorf"}, {"beast ganon", BITE_BEAST_GANON_INDEX, "The Beast Ganon fight"}, {"horseback ganon", BITE_HORSEBACK_GANON_INDEX, "The Horseback Ganondorf fight"}, +#endif +#ifdef WII_PLATFORM + {"ordon gate clip", BITE_ORDON_GATE_CLIP_INDEX, "Gate Clip outside Ordon Spring"}, + {"back in time", BITE_BACK_IN_TIME_INDEX, "Back in Time off the Ordon Spring bridge"}, + {"seam clip", BITE_SEAM_CLIP_INDEX, "Seam Clip in Hyrule Field South"}, + {"goats", BITE_GOATS_INDEX, "Goat herding 2"}, + {"sword and shield skip", BITE_HUGO_INDEX, "Hangin' with Hugo"}, + {"faron twilight", BITE_FARON_TWILIGHT_INDEX, "Faron Twilight tears"}, + {"early master sword", BITE_EMS_INDEX, "Super Jump to Sacred Grove"}, + {"purple mist", BITE_MIST_INDEX, "Purple mist in Faron Woods (post-EMS)"}, + {"king bulblin 1", BITE_KB1_INDEX, "King Bulblin 1 fight"}, + {"eldin twilight", BITE_ELDIN_TWILIGHT_INDEX, "Eldin Twilight tears"}, + {"bombhouse skip", BITE_BOMBHOUSE_SKIP_INDEX, "Bombhouse skip in Kakariko"}, + {"epona oob", BITE_EPONA_OOB_INDEX, "Epona OoB in Eldin Province"}, + {"lanayru twilight", BITE_LANAYRU_TWILIGHT_INDEX, "Lanayru Twilight tears"}, + {"waterfall sidehop", BITE_WATERFALL_SIDEHOP_INDEX, "Waterfall sidehop after Rutela skip"}, + {"boss bug", BITE_BOSS_BUG_INDEX, "Boss Bug in Lake Hylia"}, + {"iza", BITE_IZA_INDEX, "Steal Iza's bomb bag"}, + {"plumm oob", BITE_PLUMM_OOB_INDEX, "Plumm OoB in Zora's Domain"}, + {"enter lakebed", BITE_ENTER_LAKEBED_INDEX, "Enter Lakebed Temple"}, + {"lakebed 1", BITE_LAKEBED_1_INDEX, "The 1st Lakebed Temple segment"}, + {"deku toad", BITE_TOAD_INDEX, "Lakebed Temple miniboss"}, + {"onebomb morpheel", BITE_ONEBOMB_INDEX, "Morpheel fight (no Zora Armor)"}, + {"mdh tower", BITE_MDH_TOWER_INDEX, "MDH tower climb"}, + {"mdh bridge", BITE_MDH_BRIDGE_INDEX, "MDH castle rooftops"}, + {"messenger skip", BITE_MESSENGER_SKIP_INDEX, "LJA to skip the Snowpeak messengers"}, + {"snowpeak mbbb", BITE_SPR_MBBB_INDEX, "Snowpeak Ruins miniboss"}, + {"freezard skip", BITE_FREEZARD_SKIP_INDEX, "Freezard skip in Snowpeak Ruins"}, + {"darkhammer", BITE_DARK_HAMMER_INDEX, "Snowpeak Ruins miniboss"}, + {"bulblin camp", BITE_BULBLIN_CAMP_INDEX, "The camp before Arbiter's Grounds"}, + {"arbiter's grounds", BITE_AG_INDEX, "The Arbiter's Grounds segment"}, + {"poe 1 skip", BITE_POE_1_SKIP_INDEX, "The pillar jump in Arbiter's Grounds"}, + {"early boss key", BITE_EARLY_BOSS_KEY_INDEX, "Early Boss Key in Arbiter's Grounds"}, + {"death sword", BITE_DSS_INDEX, "Arbiter's Grounds miniboss"}, + {"stallord", BITE_STALLORD_INDEX, "Arbiter's Grounds boss"}, + {"stallord 2", BITE_STALLORD2_INDEX, "Stallord 2nd phase"}, + {"city early", BITE_CITS_EARLY_INDEX, "Clip to the cannon early"}, + {"city 1", BITE_CITS_1_INDEX, "The 1st City in the Sky segment"}, + {"aeralfos skip", BITE_AERALFOS_SKIP_INDEX, "City in the Sky miniboss"}, + {"city 2", BITE_CITS_2_INDEX, "The 2nd City in the Sky segment"}, + {"fan tower", BITE_FAN_TOWER_INDEX, "Final fan room in City"}, + {"argorok", BITE_ARGOROK_INDEX, "City in the Sky boss"}, + {"palace sol 1", BITE_PALACE_1_INDEX, "The 1st Sol of Palace of Twilight"}, + {"palace sol 2", BITE_STUPID_ROOM_INDEX, "The 2nd Sol of Palace of Twilight"}, + {"palace 2", BITE_PALACE_2_INDEX, "After light sword"}, + {"early platform", BITE_EARLY_PLATFORM_INDEX, "Early platform in Palace of Twilight"}, + {"zant", BITE_ZANT_INDEX, "Palace of Twilight boss"}, + {"hyrule castle", BITE_HC_INDEX, "The Hyrule Castle segment"}, + {"darknut", BITE_DARKNUT_INDEX, "The Darknut fight in Hyrule Castle"}, + {"final tower climb", BITE_HC_TOWER_INDEX, "The tower climb before Ganondorf"}, + {"beast ganon", BITE_BEAST_GANON_INDEX, "The Beast Ganon fight"}, + {"horseback ganon", BITE_HORSEBACK_GANON_INDEX, "The Horseback Ganondorf fight"}, +#endif } {} AnyBiTESavesMenu::~AnyBiTESavesMenu() {} @@ -68,14 +122,16 @@ void AnyBiTESavesMenu::draw() { special AnySpecials[] = { special(BITE_ORDON_GATE_CLIP_INDEX, nullptr, SaveMngSpecial_OrdonRock), special(BITE_HUGO_INDEX, SaveMngSpecial_Hugo, SaveMngSpecial_SpawnHugo), +#ifdef GCN_PLATFORM special(BITE_KARG_INDEX, SaveMngSpecial_KargOoB, nullptr), special(BITE_LAKEBED_BK_SKIP_INDEX, SaveMngSpecial_LakebedBKSkip, nullptr), - special(BITE_ONEBOMB_INDEX, nullptr, SaveMngSpecial_Morpheel), - special(BITE_STALLORD_INDEX, SaveMngSpecial_Stallord, nullptr), - special(BITE_STALLORD2_INDEX, SaveMngSpecial_Stallord2_init, SaveMngSpecial_Stallord2), special(BITE_FRST_ESCAPE_INDEX, SaveMngSpecial_BossFlags, nullptr), special(BITE_LANAYRU_GATE_CLIP_INDEX, SaveMngSpecial_BossFlags, nullptr), special(BITE_PILLAR_CLIP_INDEX, SaveMngSpecial_BossFlags, nullptr), +#endif + special(BITE_ONEBOMB_INDEX, nullptr, SaveMngSpecial_Morpheel), + special(BITE_STALLORD_INDEX, SaveMngSpecial_Stallord, nullptr), + special(BITE_STALLORD2_INDEX, SaveMngSpecial_Stallord2_init, SaveMngSpecial_Stallord2), special(BITE_LAKEBED_1_INDEX, SaveMngSpecial_BossFlags, nullptr), special(BITE_WATERFALL_SIDEHOP_INDEX, SaveMngSpecial_WaterfallSidehop, nullptr), special(BITE_DARK_HAMMER_INDEX, SaveMngSpecial_BossFlags, SaveMngSpecial_Darkhammer), diff --git a/modules/menus/menu_any_saves/include/any_saves_menu.h b/modules/menus/menu_any_saves/include/any_saves_menu.h index 38a85225..30641fce 100644 --- a/modules/menus/menu_any_saves/include/any_saves_menu.h +++ b/modules/menus/menu_any_saves/include/any_saves_menu.h @@ -127,10 +127,5 @@ class AnySavesMenu : public Menu { virtual void draw(); private: -#ifdef GCN_PLATFORM Line lines[ANY_SAVES_COUNT]; -#endif -#ifdef WII_PLATFORM - Line lines[ANY_SAVES_COUNT]; -#endif }; \ No newline at end of file diff --git a/modules/menus/menu_dungeon_flags/include/dungeon_flags_menu.h b/modules/menus/menu_dungeon_flags/include/dungeon_flags_menu.h index 5873df4d..ef21a018 100644 --- a/modules/menus/menu_dungeon_flags/include/dungeon_flags_menu.h +++ b/modules/menus/menu_dungeon_flags/include/dungeon_flags_menu.h @@ -3,6 +3,7 @@ struct DungeonFlagsData { bool init_once = false; + // bool l_introFlag; bool l_mapFlag; bool l_compassFlag; bool l_bosskeyFlag; @@ -15,12 +16,15 @@ struct DungeonFlagsData { enum DungeonFlagsIndex { SELECT_DUNGEON_INDEX, SMALL_KEY_FLAG_INDEX, + // INTRO_CS_FLAG_INDEX, MAP_FLAG_INDEX, COMPASS_FLAG_INDEX, BOSS_KEY_FLAG_INDEX, DEFEAT_MINIBOSS_FLAG_INDEX, DEFEAT_BOSS_FLAG_INDEX, - CLEAR_DUNGEON_FLAGS_INDEX + CLEAR_DUNGEON_FLAGS_INDEX, + + DUNGEON_FLAGS_COUNT }; extern DungeonFlagsData* dungeonFlagsData; @@ -33,5 +37,5 @@ class DungeonFlagsMenu : public Menu { private: - Line lines[8]; + Line lines[DUNGEON_FLAGS_COUNT]; }; diff --git a/modules/menus/menu_dungeon_flags/src/dungeon_flags_menu.cpp b/modules/menus/menu_dungeon_flags/src/dungeon_flags_menu.cpp index ec48a652..8afacc6f 100644 --- a/modules/menus/menu_dungeon_flags/src/dungeon_flags_menu.cpp +++ b/modules/menus/menu_dungeon_flags/src/dungeon_flags_menu.cpp @@ -4,6 +4,7 @@ #include "gz_flags.h" #include "rels/include/defines.h" #include "menus/utils/menu_mgr.h" +#include "libtp_c/include/utils.h" #define MAX_DUNGEON_OPTIONS 9 @@ -15,6 +16,8 @@ KEEP_FUNC DungeonFlagsMenu::DungeonFlagsMenu(Cursor& cursor) {"dungeon:", SELECT_DUNGEON_INDEX, "Selected dungeon flags", false, nullptr, MAX_DUNGEON_OPTIONS}, {"small keys", SMALL_KEY_FLAG_INDEX, "Selected dungeon small keys", false, nullptr, 5}, + // {"intro cutscene", INTRO_CS_FLAG_INDEX, "Toggle selected dungeon intro cutscene", true, + // [](){return dungeonFlagsData->l_introFlag;}}, {"have map", MAP_FLAG_INDEX, "Give selected dungeon map", true, [](){return dungeonFlagsData->l_mapFlag;}}, {"have compass", COMPASS_FLAG_INDEX, "Give selected dungeon compass", true, [](){return dungeonFlagsData->l_compassFlag;}}, @@ -41,6 +44,20 @@ void setSaveDungeonItem(int32_t stage, int32_t flag) { } } +#include "libtp_c/include/m_Do/m_Do_printf.h" // OSReport + +bool getDungeonMemSwitch(int32_t stage, int32_t flag) { + return dSv_memBit_c__isSwitch(&dComIfGs_getSavedata().mSave[stage].mBit, flag); +} + +void setDungeonMemSwitch(int32_t stage, int32_t flag) { + if (getDungeonMemSwitch(stage, flag)) { + dSv_memBit_c__onSwitch(&dComIfGs_getSavedata().mSave[stage].mBit, flag); + } else { + dSv_memBit_c__offSwitch(&dComIfGs_getSavedata().mSave[stage].mBit, flag); + } +} + uint8_t getSaveDungeonKeys(int32_t stage) { return dComIfGs_getSavedata().mSave[stage].mBit.getKeyNum(); } @@ -123,6 +140,36 @@ void DungeonFlagsMenu::draw() { } // update flags + // switch (area_id) { + // case dSv_memory_c::FOREST_TEMPLE: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 105); + // break; + // case dSv_memory_c::GORON_MINES: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 85); + // break; + // case dSv_memory_c::LAKEBED: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 120); + // break; + // case dSv_memory_c::ARBITERS: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 124); + // break; + // case dSv_memory_c::SNOWPEAK_RUINS: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 120); + // break; + // case dSv_memory_c::TEMPLE_OF_TIME: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 21); + // break; + // case dSv_memory_c::CITY: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 111); + // break; + // case dSv_memory_c::PALACE: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 1); + // break; + // case dSv_memory_c::HYRULE_CASTLE: + // dungeonFlagsData->l_introFlag = getDungeonMemSwitch(area_id, 98); + // break; + // } + dungeonFlagsData->l_mapFlag = getSaveDungeonItem(area_id, dSv_memBit_c::MAP); dungeonFlagsData->l_compassFlag = getSaveDungeonItem(area_id, dSv_memBit_c::COMPASS); dungeonFlagsData->l_bosskeyFlag = getSaveDungeonItem(area_id, dSv_memBit_c::BOSS_KEY); @@ -131,6 +178,37 @@ void DungeonFlagsMenu::draw() { if (GZ_getButtonTrig(SELECTION_BUTTON)) { switch (cursor.y) { + // case INTRO_CS_FLAG_INDEX: + // switch (area_id) { + // case dSv_memory_c::FOREST_TEMPLE: + // setDungeonMemSwitch(area_id, 105); + // break; + // case dSv_memory_c::GORON_MINES: + // setDungeonMemSwitch(area_id, 85); + // break; + // case dSv_memory_c::LAKEBED: + // setDungeonMemSwitch(area_id, 120); + // break; + // case dSv_memory_c::ARBITERS: + // setDungeonMemSwitch(area_id, 124); + // break; + // case dSv_memory_c::SNOWPEAK_RUINS: + // setDungeonMemSwitch(area_id, 120); + // break; + // case dSv_memory_c::TEMPLE_OF_TIME: + // setDungeonMemSwitch(area_id, 21); + // break; + // case dSv_memory_c::CITY: + // setDungeonMemSwitch(area_id, 111); + // break; + // case dSv_memory_c::PALACE: + // setDungeonMemSwitch(area_id, 1); + // break; + // case dSv_memory_c::HYRULE_CASTLE: + // setDungeonMemSwitch(area_id, 98); + // break; + // } + // break; case MAP_FLAG_INDEX: setSaveDungeonItem(area_id, dSv_memBit_c::MAP); break; diff --git a/modules/menus/menu_equipment/CMakeLists.txt b/modules/menus/menu_equipment/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_equipment/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_equipment/include/equipment_menu.h b/modules/menus/menu_equipment/include/equipment_menu.h new file mode 100644 index 00000000..44172cde --- /dev/null +++ b/modules/menus/menu_equipment/include/equipment_menu.h @@ -0,0 +1,45 @@ +#include "menus/menu.h" + +struct EquipmentData { + uint8_t l_ordonSword_idx; + uint8_t l_masterSword_idx; + uint8_t l_woodShield_idx; + uint8_t l_hyShield_idx; + uint8_t l_tunic_idx; + uint8_t l_zoraArmor_idx; + uint8_t l_magicArmor_idx; + uint8_t l_bombCap_idx; + uint8_t l_wallet_idx; + uint8_t l_arrowCap_idx; +}; + +enum EquipmentIndex { + ORDON_SWORD_INDEX, + MASTER_SWORD_INDEX, + WOOD_SHIELD_INDEX, + HYLIAN_SHIELD_INDEX, + HERO_TUNIC_INDEX, + ZORA_ARMOR_INDEX, + MAGIC_ARMOR_INDEX, + BOMB_CAPACITY_INDEX, + WALLET_INDEX, + ARROW_CAPACITY_INDEX, + + EQUIPMENT_INDEX_COUNT +}; + +extern EquipmentData* equipmentData; + +class EquipmentMenu : public Menu { +public: + EquipmentMenu(Cursor&); + virtual ~EquipmentMenu(); + virtual void draw(); + +private: + Line lines[EQUIPMENT_INDEX_COUNT]; + + void resetIndex(); + void getEquipment(); + void setEquipment(); +}; \ No newline at end of file diff --git a/modules/menus/menu_equipment/include/main.h b/modules/menus/menu_equipment/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_equipment/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_equipment/src/equipment_menu.cpp b/modules/menus/menu_equipment/src/equipment_menu.cpp new file mode 100644 index 00000000..f4c33d99 --- /dev/null +++ b/modules/menus/menu_equipment/src/equipment_menu.cpp @@ -0,0 +1,351 @@ +#include "menus/menu_equipment/include/equipment_menu.h" +#include +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/utils.h" +#include "gz_flags.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +#define MAX_ORDON_SWORD_OPT 3 +#define MAX_MASTER_SWORD_OPT 3 +#define MAX_WOOD_SHIELD_OPT 3 +#define MAX_HYLIAN_SHIELD_OPT 2 +#define MAX_HERO_TUNIC_OPT 2 +#define MAX_ZORA_ARMOR_OPT 2 +#define MAX_MAGIC_ARMOR_OPT 2 +#define MAX_BOMB_CAPACITY_OPT 2 +#define MAX_WALLET_OPT 3 +#define MAX_ARROW_CAPACITY_OPT 3 + +KEEP_VAR EquipmentData* equipmentData; + +KEEP_FUNC EquipmentMenu::EquipmentMenu(Cursor& cursor) + : Menu(cursor), + lines{{"ordon sword:", ORDON_SWORD_INDEX, "Wooden Sword / Ordon Sword", false, nullptr, + MAX_ORDON_SWORD_OPT}, + {"master sword:", MASTER_SWORD_INDEX, "Master Sword / Light Sword", false, nullptr, + MAX_MASTER_SWORD_OPT}, + {"wooden shield:", WOOD_SHIELD_INDEX, "Ordon Shield / Wooden Shield", false, nullptr, + MAX_WOOD_SHIELD_OPT}, + {"hylian shield:", HYLIAN_SHIELD_INDEX, "Hylian Shield", false, nullptr, + MAX_HYLIAN_SHIELD_OPT}, + {"hero's tunic:", HERO_TUNIC_INDEX, "Hero's Tunic", false, nullptr, MAX_HERO_TUNIC_OPT}, + {"zora armor:", ZORA_ARMOR_INDEX, "Zora Armor", false, nullptr, MAX_ZORA_ARMOR_OPT}, + {"magic armor:", MAGIC_ARMOR_INDEX, "Magic Armor", false, nullptr, MAX_MAGIC_ARMOR_OPT}, + {"bomb capacity:", BOMB_CAPACITY_INDEX, "Bomb Bag Capacity", false, nullptr, + MAX_BOMB_CAPACITY_OPT}, + {"wallet upgrade:", WALLET_INDEX, "Wallet Capacity", false, nullptr, MAX_WALLET_OPT}, + {"arrow capacity:", ARROW_CAPACITY_INDEX, "Arrow Quiver Capacity", false, nullptr, + MAX_ARROW_CAPACITY_OPT}} {} + +EquipmentMenu::~EquipmentMenu() {} + +void EquipmentMenu::resetIndex() { + equipmentData->l_ordonSword_idx = 0; + equipmentData->l_masterSword_idx = 0; + equipmentData->l_woodShield_idx = 0; + equipmentData->l_hyShield_idx = 0; + equipmentData->l_tunic_idx = 0; + equipmentData->l_zoraArmor_idx = 0; + equipmentData->l_magicArmor_idx = 0; + equipmentData->l_bombCap_idx = 0; + equipmentData->l_wallet_idx = 0; + equipmentData->l_arrowCap_idx = 0; +} + +void EquipmentMenu::getEquipment() { + if (dComIfGs_isItemFirstBit(SWORD)) { + equipmentData->l_ordonSword_idx = 2; + } else if (dComIfGs_isItemFirstBit(WOOD_STICK)) { + equipmentData->l_ordonSword_idx = 1; + } + + if (dComIfGs_isItemFirstBit(LIGHT_SWORD)) { + equipmentData->l_masterSword_idx = 2; + } else if (dComIfGs_isItemFirstBit(MASTER_SWORD)) { + equipmentData->l_masterSword_idx = 1; + } + + if (dComIfGs_isItemFirstBit(SHIELD)) { + equipmentData->l_woodShield_idx = 2; + } else if (dComIfGs_isItemFirstBit(WOOD_SHIELD)) { + equipmentData->l_woodShield_idx = 1; + } + + if (dComIfGs_isItemFirstBit(HYLIA_SHIELD)) { + equipmentData->l_hyShield_idx = 1; + } + + if (dComIfGs_isItemFirstBit(WEAR_KOKIRI)) { + equipmentData->l_tunic_idx = 1; + } + + if (dComIfGs_isItemFirstBit(WEAR_ZORA)) { + equipmentData->l_zoraArmor_idx = 1; + } + + if (dComIfGs_isItemFirstBit(ARMOR)) { + equipmentData->l_magicArmor_idx = 1; + } + + if (dComIfGs_isItemFirstBit(BOMB_BAG_LV2)) { + equipmentData->l_bombCap_idx = 1; + } + + switch (dComIfGs_getWalletSize()) { + case 1: + equipmentData->l_wallet_idx = 1; + break; + case 2: + equipmentData->l_wallet_idx = 2; + break; + } + + if (dComIfGs_getArrowMax() == 100) { + equipmentData->l_arrowCap_idx = 2; + } else if (dComIfGs_getArrowMax() == 60) { + equipmentData->l_arrowCap_idx = 1; + } +} + +void EquipmentMenu::setEquipment() { + switch (equipmentData->l_ordonSword_idx) { + case 0: + dComIfGs_offItemFirstBit(WOOD_STICK); + dComIfGs_offItemFirstBit(SWORD); + break; + case 1: + dComIfGs_onItemFirstBit(WOOD_STICK); + dComIfGs_offItemFirstBit(SWORD); + break; + case 2: + dComIfGs_onItemFirstBit(SWORD); + dComIfGs_offItemFirstBit(WOOD_STICK); + break; + } + + switch (equipmentData->l_masterSword_idx) { + case 0: + dComIfGs_offItemFirstBit(MASTER_SWORD); + dComIfGs_offItemFirstBit(LIGHT_SWORD); + break; + case 1: + dComIfGs_onItemFirstBit(MASTER_SWORD); + dComIfGs_offItemFirstBit(LIGHT_SWORD); + break; + case 2: + dComIfGs_onItemFirstBit(LIGHT_SWORD); + break; + } + + switch (equipmentData->l_woodShield_idx) { + case 0: + dComIfGs_offItemFirstBit(SHIELD); + dComIfGs_offItemFirstBit(WOOD_SHIELD); + break; + case 1: + dComIfGs_onItemFirstBit(WOOD_SHIELD); + break; + case 2: + dComIfGs_onItemFirstBit(SHIELD); + break; + } + + switch (equipmentData->l_hyShield_idx) { + case 0: + dComIfGs_offItemFirstBit(HYLIA_SHIELD); + break; + case 1: + dComIfGs_onItemFirstBit(HYLIA_SHIELD); + break; + } + + switch (equipmentData->l_tunic_idx) { + case 0: + dComIfGs_offItemFirstBit(WEAR_KOKIRI); + break; + case 1: + dComIfGs_onItemFirstBit(WEAR_KOKIRI); + break; + } + + switch (equipmentData->l_zoraArmor_idx) { + case 0: + dComIfGs_offItemFirstBit(WEAR_ZORA); + break; + case 1: + dComIfGs_onItemFirstBit(WEAR_ZORA); + break; + } + + switch (equipmentData->l_magicArmor_idx) { + case 0: + dComIfGs_offItemFirstBit(ARMOR); + break; + case 1: + dComIfGs_onItemFirstBit(ARMOR); + break; + } + + switch (equipmentData->l_bombCap_idx) { + case 0: + dComIfGs_offItemFirstBit(BOMB_BAG_LV2); + break; + case 1: + dComIfGs_onItemFirstBit(BOMB_BAG_LV2); + break; + } + + switch (equipmentData->l_wallet_idx) { + case 0: + dComIfGs_setWalletSize(WALLET); + break; + case 1: + dComIfGs_setWalletSize(BIG_WALLET); + break; + case 2: + dComIfGs_setWalletSize(GIANT_WALLET); + break; + } + + switch (equipmentData->l_arrowCap_idx) { + case 0: + dComIfGs_setArrowMax(30); + break; + case 1: + dComIfGs_setArrowMax(60); + break; + case 2: + dComIfGs_setArrowMax(100); + break; + } +} + +void EquipmentMenu::draw() { + static bool init = false; + cursor.setMode(Cursor::MODE_LIST); + + if (!init) { + getEquipment(); + init = true; + } + + if (GZ_getButtonTrig(BACK_BUTTON)) { + init = false; + g_menuMgr->pop(); + resetIndex(); + return; + } + + ListMember ordonSword_opt[3] = {"none", "wooden sword", "ordon sword"}; + ListMember masterSword_opt[3] = {"none", "master sword", "light sword"}; + ListMember woodShield_opt[3] = {"none", "ordon shield", "wooden shield"}; + ListMember hyShield_opt[2] = {"none", "hylian shield"}; + ListMember tunic_opt[2] = {"none", "hero's tunic"}; + ListMember zoraArmor_opt[2] = {"none", "zora armor"}; + ListMember magicArmor_opt[2] = {"none", "magic armor"}; + ListMember bombCap_opt[2] = {"30/15/10", "60/30/20"}; + ListMember wallet_opt[3] = {"300 Rupees", "600 Rupees", "1000 Rupees"}; + ListMember arrowCap_opt[3] = {"30 Arrows", "60 Arrows", "100 Arrows"}; + + switch (cursor.y) { + case ORDON_SWORD_INDEX: + cursor.x = equipmentData->l_ordonSword_idx; + cursor.move(MAX_ORDON_SWORD_OPT, MENU_LINE_NUM); + + if (cursor.y == ORDON_SWORD_INDEX) { + equipmentData->l_ordonSword_idx = cursor.x; + } + break; + case MASTER_SWORD_INDEX: + cursor.x = equipmentData->l_masterSword_idx; + cursor.move(MAX_MASTER_SWORD_OPT, MENU_LINE_NUM); + + if (cursor.y == MASTER_SWORD_INDEX) { + equipmentData->l_masterSword_idx = cursor.x; + } + break; + case WOOD_SHIELD_INDEX: + cursor.x = equipmentData->l_woodShield_idx; + cursor.move(MAX_WOOD_SHIELD_OPT, MENU_LINE_NUM); + + if (cursor.y == WOOD_SHIELD_INDEX) { + equipmentData->l_woodShield_idx = cursor.x; + } + break; + case HYLIAN_SHIELD_INDEX: + cursor.x = equipmentData->l_hyShield_idx; + cursor.move(MAX_HYLIAN_SHIELD_OPT, MENU_LINE_NUM); + + if (cursor.y == HYLIAN_SHIELD_INDEX) { + equipmentData->l_hyShield_idx = cursor.x; + } + break; + case HERO_TUNIC_INDEX: + cursor.x = equipmentData->l_tunic_idx; + cursor.move(MAX_HERO_TUNIC_OPT, MENU_LINE_NUM); + + if (cursor.y == HERO_TUNIC_INDEX) { + equipmentData->l_tunic_idx = cursor.x; + } + break; + case ZORA_ARMOR_INDEX: + cursor.x = equipmentData->l_zoraArmor_idx; + cursor.move(MAX_ZORA_ARMOR_OPT, MENU_LINE_NUM); + + if (cursor.y == ZORA_ARMOR_INDEX) { + equipmentData->l_zoraArmor_idx = cursor.x; + } + break; + case MAGIC_ARMOR_INDEX: + cursor.x = equipmentData->l_magicArmor_idx; + cursor.move(MAX_MAGIC_ARMOR_OPT, MENU_LINE_NUM); + + if (cursor.y == MAGIC_ARMOR_INDEX) { + equipmentData->l_magicArmor_idx = cursor.x; + } + break; + case BOMB_CAPACITY_INDEX: + cursor.x = equipmentData->l_bombCap_idx; + cursor.move(MAX_BOMB_CAPACITY_OPT, MENU_LINE_NUM); + + if (cursor.y == BOMB_CAPACITY_INDEX) { + equipmentData->l_bombCap_idx = cursor.x; + } + break; + case WALLET_INDEX: + cursor.x = equipmentData->l_wallet_idx; + cursor.move(MAX_WALLET_OPT, MENU_LINE_NUM); + + if (cursor.y == WALLET_INDEX) { + equipmentData->l_wallet_idx = cursor.x; + } + break; + case ARROW_CAPACITY_INDEX: + cursor.x = equipmentData->l_arrowCap_idx; + cursor.move(MAX_ARROW_CAPACITY_OPT, MENU_LINE_NUM); + + if (cursor.y == ARROW_CAPACITY_INDEX) { + equipmentData->l_arrowCap_idx = cursor.x; + } + break; + default: + cursor.move(0, MENU_LINE_NUM); + break; + } + + setEquipment(); + + lines[ORDON_SWORD_INDEX].printf(" <%s>", ordonSword_opt[equipmentData->l_ordonSword_idx].member); + lines[MASTER_SWORD_INDEX].printf(" <%s>", masterSword_opt[equipmentData->l_masterSword_idx].member); + lines[WOOD_SHIELD_INDEX].printf(" <%s>", woodShield_opt[equipmentData->l_woodShield_idx].member); + lines[HYLIAN_SHIELD_INDEX].printf(" <%s>", hyShield_opt[equipmentData->l_hyShield_idx].member); + lines[HERO_TUNIC_INDEX].printf(" <%s>", tunic_opt[equipmentData->l_tunic_idx].member); + lines[ZORA_ARMOR_INDEX].printf(" <%s>", zoraArmor_opt[equipmentData->l_zoraArmor_idx].member); + lines[MAGIC_ARMOR_INDEX].printf(" <%s>", magicArmor_opt[equipmentData->l_magicArmor_idx].member); + lines[BOMB_CAPACITY_INDEX].printf(" <%s>", bombCap_opt[equipmentData->l_bombCap_idx].member); + lines[WALLET_INDEX].printf(" <%s>", wallet_opt[equipmentData->l_wallet_idx].member); + lines[ARROW_CAPACITY_INDEX].printf(" <%s>", arrowCap_opt[equipmentData->l_arrowCap_idx].member); + + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_equipment/src/main.cpp b/modules/menus/menu_equipment/src/main.cpp new file mode 100644 index 00000000..ed0a6b41 --- /dev/null +++ b/modules/menus/menu_equipment/src/main.cpp @@ -0,0 +1,56 @@ +#include +#include "menus/menu_equipment/include/equipment_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +EquipmentMenu* l_menu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + g_menuMgr->setPersistentData(new EquipmentData); + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + equipmentData = g_menuMgr->getPersistentData(); + l_menu = new EquipmentMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_menu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_menu; +} + +void onDelete() { + auto data = g_menuMgr->getPersistentData(); + delete data; + g_menuMgr->setPersistentData(nullptr); +} diff --git a/modules/menus/menu_flags/include/flags_menu.h b/modules/menus/menu_flags/include/flags_menu.h index f80942f1..10869a45 100644 --- a/modules/menus/menu_flags/include/flags_menu.h +++ b/modules/menus/menu_flags/include/flags_menu.h @@ -5,8 +5,11 @@ enum FlagsIndex { GENERAL_FLAGS_INDEX, DUNGEON_FLAGS_INDEX, PORTAL_FLAGS_INDEX, + RUPEE_FLAGS_INDEX, FLAG_RECORDS_INDEX, - FLAG_LOG_INDEX + FLAG_LOG_INDEX, + + FLAGS_COUNT }; class FlagsMenu : public Menu { @@ -16,5 +19,5 @@ class FlagsMenu : public Menu { virtual void draw(); private: - Line lines[5]; + Line lines[FLAGS_COUNT]; }; diff --git a/modules/menus/menu_flags/src/flags_menu.cpp b/modules/menus/menu_flags/src/flags_menu.cpp index 4f589fc0..d578ab43 100644 --- a/modules/menus/menu_flags/src/flags_menu.cpp +++ b/modules/menus/menu_flags/src/flags_menu.cpp @@ -8,6 +8,7 @@ KEEP_FUNC FlagsMenu::FlagsMenu(Cursor& cursor) {"general flags", GENERAL_FLAGS_INDEX, "General flags", false}, {"dungeon flags", DUNGEON_FLAGS_INDEX, "Dungeon flags", false}, {"portal flags", PORTAL_FLAGS_INDEX, "Warp portal flags", false}, + {"rupee flags", RUPEE_FLAGS_INDEX, "Rupee related flags", false}, {"flag records", FLAG_RECORDS_INDEX, "Edit flag records", false}, {"flag log", FLAG_LOG_INDEX, "Toggle the flag log", false}, } {} @@ -31,6 +32,9 @@ void FlagsMenu::draw() { case PORTAL_FLAGS_INDEX: g_menuMgr->push(MN_PORTAL_FLAGS_INDEX); return; + case RUPEE_FLAGS_INDEX: + g_menuMgr->push(MN_RUPEE_FLAGS_INDEX); + return; case FLAG_RECORDS_INDEX: g_menuMgr->push(MN_FLAG_RECORDS_INDEX); return; diff --git a/modules/menus/menu_general_flags/include/general_flags_menu.h b/modules/menus/menu_general_flags/include/general_flags_menu.h index 0e3f3438..76deb2bb 100644 --- a/modules/menus/menu_general_flags/include/general_flags_menu.h +++ b/modules/menus/menu_general_flags/include/general_flags_menu.h @@ -3,12 +3,14 @@ struct GeneralFlagsData { bool l_bossFlag; - bool l_rupeeFlag; + bool l_coroTD; bool l_midnaCharge; bool l_transformWarp; bool l_midnaZ; + bool l_ruslTD; bool l_eponaStolen; bool l_eponaTamed; + bool l_maloMartCT; bool l_mapWarping; bool l_midnaHealed; bool l_midnaRide; @@ -17,16 +19,20 @@ struct GeneralFlagsData { enum GeneralFlagsIndex { BOSS_FLAG_INDEX, - RUPEE_CS_FLAG_INDEX, + CORO_TD_INDEX, EPONA_STOLEN_INDEX, EPONA_TAMED_INDEX, + MALO_MART_CT_INDEX, MAP_WARPING_INDEX, MIDNA_CHARGE_INDEX, MIDNA_HEALTHY, MIDNA_ON_BACK, MIDNA_Z_INDEX, + RUSL_TD_INDEX, TRANSFORM_WARP_INDEX, - WOLF_SENSE_INDEX + WOLF_SENSE_INDEX, + + GENERAL_FLAGS_COUNT }; extern GeneralFlagsData* generalFlagsData; @@ -38,5 +44,5 @@ class GeneralFlagsMenu : public Menu { virtual void draw(); private: - Line lines[11]; + Line lines[GENERAL_FLAGS_COUNT]; }; diff --git a/modules/menus/menu_general_flags/src/general_flags_menu.cpp b/modules/menus/menu_general_flags/src/general_flags_menu.cpp index a54732da..22ee4cce 100644 --- a/modules/menus/menu_general_flags/src/general_flags_menu.cpp +++ b/modules/menus/menu_general_flags/src/general_flags_menu.cpp @@ -11,12 +11,14 @@ KEEP_FUNC GeneralFlagsMenu::GeneralFlagsMenu(Cursor& cursor) : Menu(cursor), lines{ {"boss flag", BOSS_FLAG_INDEX, "Sets the boss flag value", true, [](){return generalFlagsData->l_bossFlag;}}, - {"rupee cutscenes", RUPEE_CS_FLAG_INDEX, "Toggle rupee cutscenes being enabled", true, - [](){return generalFlagsData->l_rupeeFlag;}}, + {"coro td", CORO_TD_INDEX, "Toggle temporary flag for Coro text displacement", true, + [](){return generalFlagsData->l_coroTD;}}, {"epona stolen", EPONA_STOLEN_INDEX, "Toggle flag for Epona being stolen", true, [](){return generalFlagsData->l_eponaStolen;}}, {"epona tamed", EPONA_TAMED_INDEX, "Toggle flag for Epona being tamed", true, [](){return generalFlagsData->l_eponaTamed;}}, + {"malo mart in castle town", MALO_MART_CT_INDEX, "Toggle flag for Malo Mart being open in Castle Town", true, + [](){return generalFlagsData->l_maloMartCT;}}, {"map warping", MAP_WARPING_INDEX, "Toggle flag for map warping", true, [](){return generalFlagsData->l_mapWarping;}}, {"midna charge", MIDNA_CHARGE_INDEX, "Toggle flag for Midna charge", true, [](){return generalFlagsData->l_midnaCharge;}}, @@ -26,6 +28,8 @@ KEEP_FUNC GeneralFlagsMenu::GeneralFlagsMenu(Cursor& cursor) true, [](){return generalFlagsData->l_midnaRide;}}, {"midna available", MIDNA_Z_INDEX, "Toggle flag for being able to call Midna", true, [](){return generalFlagsData->l_midnaZ;}}, + {"rusl td", RUSL_TD_INDEX, "Toggle temporary flag for Rusl text displacement", true, + [](){return generalFlagsData->l_ruslTD;}}, {"transform/warp", TRANSFORM_WARP_INDEX, "Toggle flag for transforming/warping", true, [](){return generalFlagsData->l_transformWarp;}}, {"wolf sense", WOLF_SENSE_INDEX, "Toggle flag for wolf sense", true, [](){return generalFlagsData->l_wolfSense;}}, @@ -40,22 +44,19 @@ void GeneralFlagsMenu::draw() { // update flags generalFlagsData->l_bossFlag = bossFlags > 0; + generalFlagsData->l_coroTD = dComIfGs_isTmpBit(0x0002); + generalFlagsData->l_ruslTD = dComIfGs_isTmpBit(0x0006); generalFlagsData->l_midnaCharge = dComIfGs_isEventBit(0x0501); generalFlagsData->l_transformWarp = dComIfGs_isEventBit(0x0D04); generalFlagsData->l_midnaZ = dComIfGs_isEventBit(0x0C10); generalFlagsData->l_eponaStolen = dComIfGs_isEventBit(0x0580); generalFlagsData->l_eponaTamed = dComIfGs_isEventBit(0x0601); + generalFlagsData->l_maloMartCT = dComIfGs_isEventBit(0x2210); generalFlagsData->l_mapWarping = dComIfGs_isEventBit(0x0604); generalFlagsData->l_midnaHealed = dComIfGs_isEventBit(0x1E08); generalFlagsData->l_midnaRide = dComIfGs_isTransformLV(3); generalFlagsData->l_wolfSense = dComIfGs_isEventBit(0x4308); - - for (int i = BLUE_RUPEE; i <= SILVER_RUPEE; i++) { - if (dComIfGs_isItemFirstBit(i)) { - generalFlagsData->l_rupeeFlag = true; - break; - } - } + if (GZ_getButtonTrig(BACK_BUTTON)) { g_menuMgr->pop(); @@ -71,17 +72,8 @@ void GeneralFlagsMenu::draw() { bossFlags = 255; } break; - case RUPEE_CS_FLAG_INDEX: - if (generalFlagsData->l_rupeeFlag) { - for (int i = BLUE_RUPEE; i <= SILVER_RUPEE; i++) { - dComIfGs_offItemFirstBit(i); - } - generalFlagsData->l_rupeeFlag = false; - } else { - for (int i = BLUE_RUPEE; i <= SILVER_RUPEE; i++) { - dComIfGs_onItemFirstBit(i); - } - } + case CORO_TD_INDEX: + setTempEventFlag(0x0002); break; case EPONA_STOLEN_INDEX: setEventFlag(0x0580); @@ -89,6 +81,9 @@ void GeneralFlagsMenu::draw() { case EPONA_TAMED_INDEX: setEventFlag(0x0601); break; + case MALO_MART_CT_INDEX: + setEventFlag(0x2210); + break; case MAP_WARPING_INDEX: setEventFlag(0x0604); break; @@ -105,6 +100,9 @@ void GeneralFlagsMenu::draw() { case MIDNA_Z_INDEX: setEventFlag(0x0C10); break; + case RUSL_TD_INDEX: + setTempEventFlag(0x0006); + break; case TRANSFORM_WARP_INDEX: setEventFlag(0x0D04); break; diff --git a/modules/menus/menu_golden_bugs/CMakeLists.txt b/modules/menus/menu_golden_bugs/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_golden_bugs/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_golden_bugs/include/golden_bugs_menu.h b/modules/menus/menu_golden_bugs/include/golden_bugs_menu.h new file mode 100644 index 00000000..038fd5c8 --- /dev/null +++ b/modules/menus/menu_golden_bugs/include/golden_bugs_menu.h @@ -0,0 +1,72 @@ +#include "menus/menu.h" + +struct GoldenBugData { + uint8_t l_mAntIdx; + uint8_t l_fAntIdx; + uint8_t l_mBeetleIdx; + uint8_t l_fBeetleIdx; + uint8_t l_mButterflyIdx; + uint8_t l_fButterflyIdx; + uint8_t l_mDayflyIdx; + uint8_t l_fDayflyIdx; + uint8_t l_mDragonflyIdx; + uint8_t l_fDragonflyIdx; + uint8_t l_mGrasshopperIdx; + uint8_t l_fGrasshopperIdx; + uint8_t l_mLadybugIdx; + uint8_t l_fLadybugIdx; + uint8_t l_mMantisIdx; + uint8_t l_fMantisIdx; + uint8_t l_mPhasmidIdx; + uint8_t l_fPhasmidIdx; + uint8_t l_mPillBugIdx; + uint8_t l_fPillBugIdx; + uint8_t l_mSnailIdx; + uint8_t l_fSnailIdx; + uint8_t l_mStagBeetleIdx; + uint8_t l_fStagBeetleIdx; +}; + +enum BugIndex { + M_ANT_INDEX, + F_ANT_INDEX, + M_BEETLE_INDEX, + F_BEETLE_INDEX, + M_BUTTERFLY_INDEX, + F_BUTTERFLY_INDEX, + M_DAYFLY_INDEX, + F_DAYFLY_INDEX, + M_DRAGONFLY_INDEX, + F_DRAGONFLY_INDEX, + M_GRASSHOPPER_INDEX, + F_GRASSHOPPER_INDEX, + M_LADYBUG_INDEX, + F_LADYBUG_INDEX, + M_MANTIS_INDEX, + F_MANTIS_INDEX, + M_PHASMID_INDEX, + F_PHASMID_INDEX, + M_PILL_BUG_INDEX, + F_PILL_BUG_INDEX, + M_SNAIL_INDEX, + F_SNAIL_INDEX, + M_STAG_BEETLE_INDEX, + F_STAG_BEETLE_INDEX, + + GB_INDEX_COUNT +}; + +extern GoldenBugData* gbData; + +class GoldenBugMenu : public Menu { +public: + GoldenBugMenu(Cursor&); + virtual ~GoldenBugMenu(); + virtual void draw(); + +private: + uint8_t getBugIdx(u8 bug, u16 flag); + void setBugIdx(u8 bug, u8 idx, u16 flag); + void setBugs(); + Line lines[GB_INDEX_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_golden_bugs/include/main.h b/modules/menus/menu_golden_bugs/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_golden_bugs/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_golden_bugs/src/golden_bugs_menu.cpp b/modules/menus/menu_golden_bugs/src/golden_bugs_menu.cpp new file mode 100644 index 00000000..a54d7ccc --- /dev/null +++ b/modules/menus/menu_golden_bugs/src/golden_bugs_menu.cpp @@ -0,0 +1,730 @@ +#include "menus/menu_golden_bugs/include/golden_bugs_menu.h" +#include +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/utils.h" +#include "gz_flags.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +KEEP_VAR GoldenBugData* gbData; + +#define MAX_GB_OPT 3 + +KEEP_FUNC GoldenBugMenu::GoldenBugMenu(Cursor& cursor) + : Menu(cursor), + lines{ + {"Ant (M):", M_ANT_INDEX, "Male Ant", false, nullptr, MAX_GB_OPT}, + {"Ant (F):", F_ANT_INDEX, "Female Ant", false, nullptr, MAX_GB_OPT}, + {"Beetle (M:", M_BEETLE_INDEX, "Male Beetle", false, nullptr, MAX_GB_OPT}, + {"Beetle (F):", F_BEETLE_INDEX, "Female Beetle", false, nullptr, MAX_GB_OPT}, + {"Butterfly (M):", M_BUTTERFLY_INDEX, "Male Butterfly", false, nullptr, MAX_GB_OPT}, + {"Butterfly (F):", F_BUTTERFLY_INDEX, "Female Butterfly", false, nullptr, MAX_GB_OPT}, + {"Dayfly (M):", M_DAYFLY_INDEX, "Male Dayfly", false, nullptr, MAX_GB_OPT}, + {"Dayfly (F):", F_DAYFLY_INDEX, "Female Dayfly", false, nullptr, MAX_GB_OPT}, + {"Dragonfly (M):", M_DRAGONFLY_INDEX, "Male Dragonfly", false, nullptr, MAX_GB_OPT}, + {"Dragonfly (F):", F_DRAGONFLY_INDEX, "Female Dragonfly", false, nullptr, MAX_GB_OPT}, + {"Grasshopper (M):", M_GRASSHOPPER_INDEX, "Male Grasshopper", false, nullptr, MAX_GB_OPT}, + {"Grasshopper (F):", F_GRASSHOPPER_INDEX, "Female Grasshopper", false, nullptr, MAX_GB_OPT}, + {"Ladybug (M):", M_LADYBUG_INDEX, "Male Ladybug", false, nullptr, MAX_GB_OPT}, + {"Ladybug (F):", F_LADYBUG_INDEX, "Female Ladybug", false, nullptr, MAX_GB_OPT}, + {"Mantis (M):", M_MANTIS_INDEX, "Male Mantis", false, nullptr, MAX_GB_OPT}, + {"Mantis (F):", F_MANTIS_INDEX, "Female Mantis", false, nullptr, MAX_GB_OPT}, + {"Phasmid (M):", M_PHASMID_INDEX, "Male Phasmid", false, nullptr, MAX_GB_OPT}, + {"Phasmid (F):", F_PHASMID_INDEX, "Female Phasmid", false, nullptr, MAX_GB_OPT}, + {"Pill Bug (M):", M_PILL_BUG_INDEX, "Male Pill Bug", false, nullptr, MAX_GB_OPT}, + {"Pill Bug (F):", F_PILL_BUG_INDEX, "Female Pill Bug", false, nullptr, MAX_GB_OPT}, + {"Snail (M):", M_SNAIL_INDEX, "Male Snail", false, nullptr, MAX_GB_OPT}, + {"Snail (F):", F_SNAIL_INDEX, "Female Snail", false, nullptr, MAX_GB_OPT}, + {"Stag Beetle (M):", M_STAG_BEETLE_INDEX, "Male Stag Beetle", false, nullptr, MAX_GB_OPT}, + {"Stag Beetle (F):", F_STAG_BEETLE_INDEX, "Female Stag Beetle", false, nullptr, MAX_GB_OPT}, + } { + + } + +GoldenBugMenu::~GoldenBugMenu() {} + +void GoldenBugMenu::setBugs() { + switch (gbData->l_mAntIdx) { + case 0: + dComIfGs_offItemFirstBit(M_ANT); + dComIfGs_offEventBit(0x3301); + break; + case 1: + dComIfGs_onItemFirstBit(M_ANT); + dComIfGs_offEventBit(0x3301); + break; + case 2: + dComIfGs_onItemFirstBit(M_ANT); + dComIfGs_onEventBit(0x3301); + break; + } + + switch (gbData->l_fAntIdx) { + case 0: + dComIfGs_offItemFirstBit(F_ANT); + dComIfGs_offEventBit(0x3480); + break; + case 1: + dComIfGs_onItemFirstBit(F_ANT); + dComIfGs_offEventBit(0x3480); + break; + case 2: + dComIfGs_onItemFirstBit(F_ANT); + dComIfGs_onEventBit(0x3480); + break; + } + + switch (gbData->l_mBeetleIdx) { + case 0: + dComIfGs_offItemFirstBit(M_BEETLE); + dComIfGs_offEventBit(0x3110); + break; + case 1: + dComIfGs_onItemFirstBit(M_BEETLE); + dComIfGs_offEventBit(0x3110); + break; + case 2: + dComIfGs_onItemFirstBit(M_BEETLE); + dComIfGs_onEventBit(0x3110); + break; + } + + switch (gbData->l_fBeetleIdx) { + case 0: + dComIfGs_offItemFirstBit(F_BEETLE); + dComIfGs_offEventBit(0x3108); + break; + case 1: + dComIfGs_onItemFirstBit(F_BEETLE); + dComIfGs_offEventBit(0x3108); + break; + case 2: + dComIfGs_onItemFirstBit(F_BEETLE); + dComIfGs_onEventBit(0x3108); + break; + } + + switch (gbData->l_mButterflyIdx) { + case 0: + dComIfGs_offItemFirstBit(M_BUTTERFLY); + dComIfGs_offEventBit(0x3104); + break; + case 1: + dComIfGs_onItemFirstBit(M_BUTTERFLY); + dComIfGs_offEventBit(0x3104); + break; + case 2: + dComIfGs_onItemFirstBit(M_BUTTERFLY); + dComIfGs_onEventBit(0x3104); + break; + } + + switch (gbData->l_fButterflyIdx) { + case 0: + dComIfGs_offItemFirstBit(F_BUTTERFLY); + dComIfGs_offEventBit(0x3102); + break; + case 1: + dComIfGs_onItemFirstBit(F_BUTTERFLY); + dComIfGs_offEventBit(0x3102); + break; + case 2: + dComIfGs_onItemFirstBit(F_BUTTERFLY); + dComIfGs_onEventBit(0x3102); + break; + } + + switch (gbData->l_mDayflyIdx) { + case 0: + dComIfGs_offItemFirstBit(M_MAYFLY); + dComIfGs_offEventBit(0x3440); + break; + case 1: + dComIfGs_onItemFirstBit(M_MAYFLY); + dComIfGs_offEventBit(0x3440); + break; + case 2: + dComIfGs_onItemFirstBit(M_MAYFLY); + dComIfGs_onEventBit(0x3440); + break; + } + + switch (gbData->l_fDayflyIdx) { + case 0: + dComIfGs_offItemFirstBit(F_MAYFLY); + dComIfGs_offEventBit(0x3420); + break; + case 1: + dComIfGs_onItemFirstBit(F_MAYFLY); + dComIfGs_offEventBit(0x3420); + break; + case 2: + dComIfGs_onItemFirstBit(F_MAYFLY); + dComIfGs_onEventBit(0x3420); + break; + } + + switch (gbData->l_mDragonflyIdx) { + case 0: + dComIfGs_offItemFirstBit(M_DRAGONFLY); + dComIfGs_offEventBit(0x3304); + break; + case 1: + dComIfGs_onItemFirstBit(M_DRAGONFLY); + dComIfGs_offEventBit(0x3304); + break; + case 2: + dComIfGs_onItemFirstBit(M_DRAGONFLY); + dComIfGs_onEventBit(0x3304); + break; + } + + switch (gbData->l_fDragonflyIdx) { + case 0: + dComIfGs_offItemFirstBit(F_DRAGONFLY); + dComIfGs_offEventBit(0x3302); + break; + case 1: + dComIfGs_onItemFirstBit(F_DRAGONFLY); + dComIfGs_offEventBit(0x3302); + break; + case 2: + dComIfGs_onItemFirstBit(F_DRAGONFLY); + dComIfGs_onEventBit(0x3302); + break; + } + + switch (gbData->l_mGrasshopperIdx) { + case 0: + dComIfGs_offItemFirstBit(M_GRASSHOPPER); + dComIfGs_offEventBit(0x3240); + break; + case 1: + dComIfGs_onItemFirstBit(M_GRASSHOPPER); + dComIfGs_offEventBit(0x3240); + break; + case 2: + dComIfGs_onItemFirstBit(M_GRASSHOPPER); + dComIfGs_onEventBit(0x3240); + break; + } + + switch (gbData->l_fGrasshopperIdx) { + case 0: + dComIfGs_offItemFirstBit(F_GRASSHOPPER); + dComIfGs_offEventBit(0x3220); + break; + case 1: + dComIfGs_onItemFirstBit(F_GRASSHOPPER); + dComIfGs_offEventBit(0x3220); + break; + case 2: + dComIfGs_onItemFirstBit(F_GRASSHOPPER); + dComIfGs_onEventBit(0x3220); + break; + } + + switch (gbData->l_mLadybugIdx) { + case 0: + dComIfGs_offItemFirstBit(M_LADYBUG); + dComIfGs_offEventBit(0x3340); + break; + case 1: + dComIfGs_onItemFirstBit(M_LADYBUG); + dComIfGs_offEventBit(0x3340); + break; + case 2: + dComIfGs_onItemFirstBit(M_LADYBUG); + dComIfGs_onEventBit(0x3340); + break; + } + + switch (gbData->l_fLadybugIdx) { + case 0: + dComIfGs_offItemFirstBit(F_LADYBUG); + dComIfGs_offEventBit(0x3320); + break; + case 1: + dComIfGs_onItemFirstBit(F_LADYBUG); + dComIfGs_offEventBit(0x3320); + break; + case 2: + dComIfGs_onItemFirstBit(F_LADYBUG); + dComIfGs_onEventBit(0x3320); + break; + } + + switch (gbData->l_mMantisIdx) { + case 0: + dComIfGs_offItemFirstBit(M_MANTIS); + dComIfGs_offEventBit(0x3201); + break; + case 1: + dComIfGs_onItemFirstBit(M_MANTIS); + dComIfGs_offEventBit(0x3201); + break; + case 2: + dComIfGs_onItemFirstBit(M_MANTIS); + dComIfGs_onEventBit(0x3201); + break; + } + + switch (gbData->l_fMantisIdx) { + case 0: + dComIfGs_offItemFirstBit(F_MANTIS); + dComIfGs_offEventBit(0x3380); + break; + case 1: + dComIfGs_onItemFirstBit(F_MANTIS); + dComIfGs_offEventBit(0x3380); + break; + case 2: + dComIfGs_onItemFirstBit(F_MANTIS); + dComIfGs_onEventBit(0x3380); + break; + } + + switch (gbData->l_mPhasmidIdx) { + case 0: + dComIfGs_offItemFirstBit(M_NANAFUSHI); + dComIfGs_offEventBit(0x3210); + break; + case 1: + dComIfGs_onItemFirstBit(M_NANAFUSHI); + dComIfGs_offEventBit(0x3210); + break; + case 2: + dComIfGs_onItemFirstBit(M_NANAFUSHI); + dComIfGs_onEventBit(0x3210); + break; + } + + switch (gbData->l_fPhasmidIdx) { + case 0: + dComIfGs_offItemFirstBit(F_NANAFUSHI); + dComIfGs_offEventBit(0x3208); + break; + case 1: + dComIfGs_onItemFirstBit(F_NANAFUSHI); + dComIfGs_offEventBit(0x3208); + break; + case 2: + dComIfGs_onItemFirstBit(F_NANAFUSHI); + dComIfGs_onEventBit(0x3208); + break; + } + + switch (gbData->l_mPillBugIdx) { + case 0: + dComIfGs_offItemFirstBit(M_DANGOMUSHI); + dComIfGs_offEventBit(0x3204); + break; + case 1: + dComIfGs_onItemFirstBit(M_DANGOMUSHI); + dComIfGs_offEventBit(0x3204); + break; + case 2: + dComIfGs_onItemFirstBit(M_DANGOMUSHI); + dComIfGs_onEventBit(0x3204); + break; + } + + switch (gbData->l_fPillBugIdx) { + case 0: + dComIfGs_offItemFirstBit(F_DANGOMUSHI); + dComIfGs_offEventBit(0x3202); + break; + case 1: + dComIfGs_onItemFirstBit(F_DANGOMUSHI); + dComIfGs_offEventBit(0x3202); + break; + case 2: + dComIfGs_onItemFirstBit(F_DANGOMUSHI); + dComIfGs_onEventBit(0x3202); + break; + } + + switch (gbData->l_mSnailIdx) { + case 0: + dComIfGs_offItemFirstBit(M_SNAIL); + dComIfGs_offEventBit(0x3310); + break; + case 1: + dComIfGs_onItemFirstBit(M_SNAIL); + dComIfGs_offEventBit(0x3310); + break; + case 2: + dComIfGs_onItemFirstBit(M_SNAIL); + dComIfGs_onEventBit(0x3310); + break; + } + + switch (gbData->l_fSnailIdx) { + case 0: + dComIfGs_offItemFirstBit(F_SNAIL); + dComIfGs_offEventBit(0x3308); + break; + case 1: + dComIfGs_onItemFirstBit(F_SNAIL); + dComIfGs_offEventBit(0x3308); + break; + case 2: + dComIfGs_onItemFirstBit(F_SNAIL); + dComIfGs_onEventBit(0x3308); + break; + } + + switch (gbData->l_mStagBeetleIdx) { + case 0: + dComIfGs_offItemFirstBit(M_STAG_BEETLE); + dComIfGs_offEventBit(0x3101); + break; + case 1: + dComIfGs_onItemFirstBit(M_STAG_BEETLE); + dComIfGs_offEventBit(0x3101); + break; + case 2: + dComIfGs_onItemFirstBit(M_STAG_BEETLE); + dComIfGs_onEventBit(0x3101); + break; + } + + switch (gbData->l_fStagBeetleIdx) { + case 0: + dComIfGs_offItemFirstBit(F_STAG_BEETLE); + dComIfGs_offEventBit(0x3280); + break; + case 1: + dComIfGs_onItemFirstBit(F_STAG_BEETLE); + dComIfGs_offEventBit(0x3280); + break; + case 2: + dComIfGs_onItemFirstBit(F_STAG_BEETLE); + dComIfGs_onEventBit(0x3280); + break; + } +} + +u8 GoldenBugMenu::getBugIdx(u8 bug, u16 flag) { + if (dComIfGs_isEventBit(flag)) { + return 2; + } + + if (dComIfGs_isItemFirstBit(bug)) { + return 1; + } + + return 0; +} + +void GoldenBugMenu::draw() { + cursor.setMode(Cursor::MODE_LIST); + + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + // update gb flags + gbData->l_mAntIdx = getBugIdx(M_ANT,0x3301); + gbData->l_fAntIdx = getBugIdx(F_ANT,0x3480); + gbData->l_mBeetleIdx = getBugIdx(M_BEETLE,0x3110); + gbData->l_fBeetleIdx = getBugIdx(F_BEETLE,0x3108); + gbData->l_mButterflyIdx = getBugIdx(M_BUTTERFLY,0x3104); + gbData->l_fButterflyIdx = getBugIdx(F_BUTTERFLY,0x3102); + gbData->l_mDayflyIdx = getBugIdx(M_MAYFLY,0x3440); + gbData->l_fDayflyIdx = getBugIdx(F_MAYFLY,0x3420); + gbData->l_mDragonflyIdx = getBugIdx(M_DRAGONFLY,0x3304); + gbData->l_fDragonflyIdx = getBugIdx(F_DRAGONFLY,0x3302); + gbData->l_mGrasshopperIdx = getBugIdx(M_GRASSHOPPER,0x3240); + gbData->l_fGrasshopperIdx = getBugIdx(F_GRASSHOPPER,0x3220); + gbData->l_mLadybugIdx = getBugIdx(M_LADYBUG,0x3340); + gbData->l_fLadybugIdx = getBugIdx(F_LADYBUG,0x3320); + gbData->l_mMantisIdx = getBugIdx(M_MANTIS,0x3201); + gbData->l_fMantisIdx = getBugIdx(F_MANTIS,0x3380); + gbData->l_mPhasmidIdx = getBugIdx(M_NANAFUSHI,0x3210); + gbData->l_fPhasmidIdx = getBugIdx(F_NANAFUSHI,0x3208); + gbData->l_mPillBugIdx = getBugIdx(M_DANGOMUSHI,0x3204); + gbData->l_fPillBugIdx = getBugIdx(F_DANGOMUSHI,0x3202); + gbData->l_mSnailIdx = getBugIdx(M_SNAIL,0x3310); + gbData->l_fSnailIdx = getBugIdx(F_SNAIL,0x3308); + gbData->l_mStagBeetleIdx = getBugIdx(M_STAG_BEETLE,0x3101); + gbData->l_fStagBeetleIdx = getBugIdx(F_STAG_BEETLE,0x3280); + + ListMember mAnt_opt[3] = {"none", "Ant (M)", "Ant (M) (turned in)"}; + ListMember fAnt_opt[3] = {"none", "Ant (F)", "Ant (F) (turned in)"}; + ListMember mBeetle_opt[3] = {"none", "Beetle (M)", "Beetle (M) (turned in)"}; + ListMember fBeetle_opt[3] = {"none", "Beetle (F)", "Beetle (F) (turned in)"}; + ListMember mButterfly_opt[3] = {"none", "Butterfly (M)", "Butterfly (M) (turned in)"}; + ListMember fButterfly_opt[3] = {"none", "Butterfly (F)", "Butterfly (F) (turned in)"}; + ListMember mDayfly_opt[3] = {"none", "Dayfly (M)", "Dayfly (M) (turned in)"}; + ListMember fDayfly_opt[3] = {"none", "Dayfly (F)", "Dayfly (F) (turned in)"}; + ListMember mDragonfly_opt[3] = {"none", "Dragonfly (M)", "Dragonfly (M) (turned in)"}; + ListMember fDragonfly_opt[3] = {"none", "Dragonfly (F)", "Dragonfly (F) (turned in)"}; + ListMember mGrasshopper_opt[3] = {"none", "Grasshopper (M)", "Grasshopper (M) (turned in)"}; + ListMember fGrasshopper_opt[3] = {"none", "Grasshopper (F)", "Grasshopper (F) (turned in)"}; + ListMember mLadybug_opt[3] = {"none", "Ladybug (M)", "Ladybug (M) (turned in)"}; + ListMember fLadybug_opt[3] = {"none", "Ladybug (F)", "Ladybug (F) (turned in)"}; + ListMember mMantis_opt[3] = {"none", "Mantis (M)", "Mantis (M) (turned in)"}; + ListMember fMantis_opt[3] = {"none", "Mantis (F)", "Mantis (F) (turned in)"}; + ListMember mPhasmid_opt[3] = {"none", "Phasmid (M)", "Phasmid (M) (turned in)"}; + ListMember fPhasmid_opt[3] = {"none", "Phasmid (F)", "Phasmid (F) (turned in)"}; + ListMember mPillBug_opt[3] = {"none", "Pill Bug (M)", "Pill Bug (M) (turned in)"}; + ListMember fPillBug_opt[3] = {"none", "Pill Bug (F)", "Pill Bug (F) (turned in)"}; + ListMember mSnail_opt[3] = {"none", "Snail (M)", "Snail (M) (turned in)"}; + ListMember fSnail_opt[3] = {"none", "Snail (F)", "Snail (F) (turned in)"}; + ListMember mStagBeetle_opt[3] = {"none", "Stag Beetle (M)", "Stag Beetle (M) (turned in)"}; + ListMember fStagBeetle_opt[3] = {"none", "Stag Beetle (F)", "Stag Beetle (F) (turned in)"}; + + switch (cursor.y) { + case M_ANT_INDEX: + cursor.x = gbData->l_mAntIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_ANT_INDEX) { + gbData->l_mAntIdx = cursor.x; + } + + break; + case F_ANT_INDEX: + cursor.x = gbData->l_fAntIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_ANT_INDEX) { + gbData->l_fAntIdx = cursor.x; + } + + break; + case M_BEETLE_INDEX: + cursor.x = gbData->l_mBeetleIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_BEETLE_INDEX) { + gbData->l_mBeetleIdx = cursor.x; + } + + break; + case F_BEETLE_INDEX: + cursor.x = gbData->l_fBeetleIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_BEETLE_INDEX) { + gbData->l_fBeetleIdx = cursor.x; + } + + break; + case M_BUTTERFLY_INDEX: + cursor.x = gbData->l_mButterflyIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_BUTTERFLY_INDEX) { + gbData->l_mButterflyIdx = cursor.x; + } + + break; + case F_BUTTERFLY_INDEX: + cursor.x = gbData->l_fButterflyIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_BUTTERFLY_INDEX) { + gbData->l_fButterflyIdx = cursor.x; + } + + break; + case M_DAYFLY_INDEX: + cursor.x = gbData->l_mDayflyIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_DAYFLY_INDEX) { + gbData->l_mDayflyIdx = cursor.x; + } + + break; + case F_DAYFLY_INDEX: + cursor.x = gbData->l_fDayflyIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_DAYFLY_INDEX) { + gbData->l_fDayflyIdx = cursor.x; + } + + break; + case M_DRAGONFLY_INDEX: + cursor.x = gbData->l_mDragonflyIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_DRAGONFLY_INDEX) { + gbData->l_mDragonflyIdx = cursor.x; + } + + break; + case F_DRAGONFLY_INDEX: + cursor.x = gbData->l_fDragonflyIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_DRAGONFLY_INDEX) { + gbData->l_fDragonflyIdx = cursor.x; + } + + break; + case M_GRASSHOPPER_INDEX: + cursor.x = gbData->l_mGrasshopperIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_GRASSHOPPER_INDEX) { + gbData->l_mGrasshopperIdx = cursor.x; + } + + break; + case F_GRASSHOPPER_INDEX: + cursor.x = gbData->l_fGrasshopperIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_GRASSHOPPER_INDEX) { + gbData->l_fGrasshopperIdx = cursor.x; + } + + break; + case M_LADYBUG_INDEX: + cursor.x = gbData->l_mLadybugIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_LADYBUG_INDEX) { + gbData->l_mLadybugIdx = cursor.x; + } + + break; + case F_LADYBUG_INDEX: + cursor.x = gbData->l_fLadybugIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_LADYBUG_INDEX) { + gbData->l_fLadybugIdx = cursor.x; + } + + break; + case M_MANTIS_INDEX: + cursor.x = gbData->l_mMantisIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_MANTIS_INDEX) { + gbData->l_mMantisIdx = cursor.x; + } + + break; + case F_MANTIS_INDEX: + cursor.x = gbData->l_fMantisIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_MANTIS_INDEX) { + gbData->l_fMantisIdx = cursor.x; + } + + break; + case M_PHASMID_INDEX: + cursor.x = gbData->l_mPhasmidIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_PHASMID_INDEX) { + gbData->l_mPhasmidIdx = cursor.x; + } + + break; + case F_PHASMID_INDEX: + cursor.x = gbData->l_fPhasmidIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_PHASMID_INDEX) { + gbData->l_fPhasmidIdx = cursor.x; + } + + break; + case M_PILL_BUG_INDEX: + cursor.x = gbData->l_mPillBugIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_PILL_BUG_INDEX) { + gbData->l_mPillBugIdx = cursor.x; + } + + break; + case F_PILL_BUG_INDEX: + cursor.x = gbData->l_fPillBugIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_PILL_BUG_INDEX) { + gbData->l_fPillBugIdx = cursor.x; + } + + break; + case M_SNAIL_INDEX: + cursor.x = gbData->l_mSnailIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_SNAIL_INDEX) { + gbData->l_mSnailIdx = cursor.x; + } + + break; + case F_SNAIL_INDEX: + cursor.x = gbData->l_fSnailIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_SNAIL_INDEX) { + gbData->l_fSnailIdx = cursor.x; + } + + break; + case M_STAG_BEETLE_INDEX: + cursor.x = gbData->l_mStagBeetleIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == M_STAG_BEETLE_INDEX) { + gbData->l_mStagBeetleIdx = cursor.x; + } + + break; + case F_STAG_BEETLE_INDEX: + cursor.x = gbData->l_fStagBeetleIdx; + cursor.move(MAX_GB_OPT, MENU_LINE_NUM); + + if (cursor.y == F_STAG_BEETLE_INDEX) { + gbData->l_fStagBeetleIdx = cursor.x; + } + + break; + default: + cursor.move(0, MENU_LINE_NUM); + break; + } + + setBugs(); + + lines[M_ANT_INDEX].printf(" <%s>", mAnt_opt[gbData->l_mAntIdx].member); + lines[F_ANT_INDEX].printf(" <%s>", fAnt_opt[gbData->l_fAntIdx].member); + lines[M_BEETLE_INDEX].printf(" <%s>", mBeetle_opt[gbData->l_mBeetleIdx].member); + lines[F_BEETLE_INDEX].printf(" <%s>", fBeetle_opt[gbData->l_fBeetleIdx].member); + lines[M_BUTTERFLY_INDEX].printf(" <%s>", mButterfly_opt[gbData->l_mButterflyIdx].member); + lines[F_BUTTERFLY_INDEX].printf(" <%s>", fButterfly_opt[gbData->l_fButterflyIdx].member); + lines[M_DAYFLY_INDEX].printf(" <%s>", mDayfly_opt[gbData->l_mDayflyIdx].member); + lines[F_DAYFLY_INDEX].printf(" <%s>", fDayfly_opt[gbData->l_fDayflyIdx].member); + lines[M_DRAGONFLY_INDEX].printf(" <%s>", mDragonfly_opt[gbData->l_mDragonflyIdx].member); + lines[F_DRAGONFLY_INDEX].printf(" <%s>", fDragonfly_opt[gbData->l_fDragonflyIdx].member); + lines[M_GRASSHOPPER_INDEX].printf(" <%s>", mGrasshopper_opt[gbData->l_mGrasshopperIdx].member); + lines[F_GRASSHOPPER_INDEX].printf(" <%s>", fGrasshopper_opt[gbData->l_fGrasshopperIdx].member); + lines[M_LADYBUG_INDEX].printf(" <%s>", mLadybug_opt[gbData->l_mLadybugIdx].member); + lines[F_LADYBUG_INDEX].printf(" <%s>", fLadybug_opt[gbData->l_fLadybugIdx].member); + lines[M_MANTIS_INDEX].printf(" <%s>", mMantis_opt[gbData->l_mMantisIdx].member); + lines[F_MANTIS_INDEX].printf(" <%s>", fMantis_opt[gbData->l_fMantisIdx].member); + lines[M_PHASMID_INDEX].printf(" <%s>", mPhasmid_opt[gbData->l_mPhasmidIdx].member); + lines[F_PHASMID_INDEX].printf(" <%s>", fPhasmid_opt[gbData->l_fPhasmidIdx].member); + lines[M_PILL_BUG_INDEX].printf(" <%s>", mPillBug_opt[gbData->l_mPillBugIdx].member); + lines[F_PILL_BUG_INDEX].printf(" <%s>", fPillBug_opt[gbData->l_fPillBugIdx].member); + lines[M_SNAIL_INDEX].printf(" <%s>", mSnail_opt[gbData->l_mSnailIdx].member); + lines[F_SNAIL_INDEX].printf(" <%s>", fSnail_opt[gbData->l_fSnailIdx].member); + lines[M_STAG_BEETLE_INDEX].printf(" <%s>", mStagBeetle_opt[gbData->l_mStagBeetleIdx].member); + lines[F_STAG_BEETLE_INDEX].printf(" <%s>", fStagBeetle_opt[gbData->l_fStagBeetleIdx].member); + + + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} \ No newline at end of file diff --git a/modules/menus/menu_golden_bugs/src/main.cpp b/modules/menus/menu_golden_bugs/src/main.cpp new file mode 100644 index 00000000..31b88873 --- /dev/null +++ b/modules/menus/menu_golden_bugs/src/main.cpp @@ -0,0 +1,56 @@ +#include +#include "menus/menu_golden_bugs/include/golden_bugs_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +GoldenBugMenu* l_goldenBugMenu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + g_menuMgr->setPersistentData(new GoldenBugData); + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + gbData = g_menuMgr->getPersistentData(); + l_goldenBugMenu = new GoldenBugMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_goldenBugMenu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_goldenBugMenu; +} + +void onDelete() { + auto data = g_menuMgr->getPersistentData(); + delete data; + g_menuMgr->setPersistentData(nullptr); +} diff --git a/modules/menus/menu_hidden_skills/CMakeLists.txt b/modules/menus/menu_hidden_skills/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_hidden_skills/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_hidden_skills/include/hidden_skills_menu.h b/modules/menus/menu_hidden_skills/include/hidden_skills_menu.h new file mode 100644 index 00000000..cdc5b3a8 --- /dev/null +++ b/modules/menus/menu_hidden_skills/include/hidden_skills_menu.h @@ -0,0 +1,35 @@ +#include "menus/menu.h" + +struct HiddenSkillsData { + bool l_ebFlag; + bool l_sbFlag; + bool l_bsFlag; + bool l_hsFlag; + bool l_mdFlag; + bool l_jsFlag; + bool l_gsFlag; +}; + +enum HiddenSkillsIndex { + ENDING_BLOW_INDEX, + SHIELD_BASH_INDEX, + BACKSLICE_INDEX, + HELM_SPLITTER_INDEX, + MORTAL_DRAW_INDEX, + JUMP_STRIKE_INDEX, + GREAT_SPIN_INDEX, + + HIDDEN_SKILLS_INDEX_COUNT +}; + +extern HiddenSkillsData* hiddenSkillsData; + +class HiddenSkillsMenu : public Menu { +public: + HiddenSkillsMenu(Cursor&); + virtual ~HiddenSkillsMenu(); + virtual void draw(); + +private: + Line lines[HIDDEN_SKILLS_INDEX_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_hidden_skills/include/main.h b/modules/menus/menu_hidden_skills/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_hidden_skills/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_hidden_skills/src/hidden_skills_menu.cpp b/modules/menus/menu_hidden_skills/src/hidden_skills_menu.cpp new file mode 100644 index 00000000..fd4b75a6 --- /dev/null +++ b/modules/menus/menu_hidden_skills/src/hidden_skills_menu.cpp @@ -0,0 +1,68 @@ +#include "menus/menu_hidden_skills/include/hidden_skills_menu.h" +#include +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/utils.h" +#include "gz_flags.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +KEEP_VAR HiddenSkillsData* hiddenSkillsData; + +KEEP_FUNC HiddenSkillsMenu::HiddenSkillsMenu(Cursor& cursor) + : Menu(cursor), + lines{{"ending blow:", ENDING_BLOW_INDEX, "Ending Blow", true, [](){return hiddenSkillsData->l_ebFlag;}}, + {"shield bash:", SHIELD_BASH_INDEX, "Shield Bash", true, [](){return hiddenSkillsData->l_sbFlag;}}, + {"backslice:", BACKSLICE_INDEX, "Backslice", true, [](){return hiddenSkillsData->l_bsFlag;}}, + {"helm splitter:", HELM_SPLITTER_INDEX, "Helm Splitter", true, [](){return hiddenSkillsData->l_hsFlag;}}, + {"mortal draw:", MORTAL_DRAW_INDEX, "Mortal Draw", true, [](){return hiddenSkillsData->l_mdFlag;}}, + {"jump strike:", JUMP_STRIKE_INDEX, "Jump Strike", true, [](){return hiddenSkillsData->l_jsFlag;}}, + {"greatspin:", GREAT_SPIN_INDEX, "Greatspin", true, [](){return hiddenSkillsData->l_gsFlag;}}} {} + +HiddenSkillsMenu::~HiddenSkillsMenu() {} + +void HiddenSkillsMenu::draw() { + cursor.setMode(Cursor::MODE_LIST); + + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + // update hidden skill flags + hiddenSkillsData->l_ebFlag = dComIfGs_isEventBit(0x2904); + hiddenSkillsData->l_sbFlag = dComIfGs_isEventBit(0x2908); + hiddenSkillsData->l_bsFlag = dComIfGs_isEventBit(0x2902); + hiddenSkillsData->l_hsFlag = dComIfGs_isEventBit(0x2901); + hiddenSkillsData->l_mdFlag = dComIfGs_isEventBit(0x2A80); + hiddenSkillsData->l_jsFlag = dComIfGs_isEventBit(0x2A40); + hiddenSkillsData->l_gsFlag = dComIfGs_isEventBit(0x2A20); + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + switch (cursor.y) { + case ENDING_BLOW_INDEX: + setEventFlag(0x2904); + break; + case SHIELD_BASH_INDEX: + setEventFlag(0x2908); + break; + case BACKSLICE_INDEX: + setEventFlag(0x2902); + break; + case HELM_SPLITTER_INDEX: + setEventFlag(0x2901); + break; + case MORTAL_DRAW_INDEX: + setEventFlag(0x2A80); + break; + case JUMP_STRIKE_INDEX: + setEventFlag(0x2A40); + break; + case GREAT_SPIN_INDEX: + setEventFlag(0x2A20); + break; + } + } + + cursor.move(0, MENU_LINE_NUM); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_hidden_skills/src/main.cpp b/modules/menus/menu_hidden_skills/src/main.cpp new file mode 100644 index 00000000..aa517183 --- /dev/null +++ b/modules/menus/menu_hidden_skills/src/main.cpp @@ -0,0 +1,56 @@ +#include +#include "menus/menu_hidden_skills/include/hidden_skills_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +HiddenSkillsMenu* l_menu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + g_menuMgr->setPersistentData(new HiddenSkillsData); + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + hiddenSkillsData = g_menuMgr->getPersistentData(); + l_menu = new HiddenSkillsMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_menu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_menu; +} + +void onDelete() { + auto data = g_menuMgr->getPersistentData(); + delete data; + g_menuMgr->setPersistentData(nullptr); +} diff --git a/modules/menus/menu_hundo_saves/include/hundo_saves_menu.h b/modules/menus/menu_hundo_saves/include/hundo_saves_menu.h index f60a3e3b..5c20b5d7 100644 --- a/modules/menus/menu_hundo_saves/include/hundo_saves_menu.h +++ b/modules/menus/menu_hundo_saves/include/hundo_saves_menu.h @@ -22,13 +22,16 @@ enum HundoPracticeIndex { HND_TOAD_INDEX, HND_KARG_INDEX, HND_LANAYRU_TWILIGHT_INDEX, - HND_BOSS_BUG_INDEX, - HND_KB_2_INDEX, - HND_ESCORT_INDEX, HND_COROTD_INDEX, + HND_EARLY_ELE_INDEX, HND_GM_INDEX, HND_DANGORO_INDEX, - HND_POST_GM_INDEX, + HND_FYRUS_INDEX, + HND_WATERFALL_SIDEHOP_INDEX, + HND_BOSS_BUG_INDEX, + HND_KB_2_INDEX, + HND_ESCORT_INDEX, + HND_ELDIN_COLLECT_INDEX, HND_LAKEBED_BK_SKIP_INDEX, HND_MORPHEEL_INDEX, HND_MDH_TOWER_INDEX, @@ -46,7 +49,7 @@ enum HundoPracticeIndex { HND_POST_AG_INDEX, HND_SPR_INDEX, HND_DARK_HAMMER_INDEX, - HND_SPR_SUPERJUMP_INDEX, + HND_SPR_2_INDEX, HND_SPR_BK_LJA_INDEX, HND_SPR_BK_ROOM_INDEX, HND_BLIZZETA_INDEX, @@ -62,7 +65,9 @@ enum HundoPracticeIndex { HND_POST_TOT_INDEX, HND_HOTSPRING_INDEX, HND_GORGE_ARC_INDEX, + HND_SILVER_RUPEE_INDEX, HND_PUZZLE_INDEX, + HND_IZA2_INDEX, HND_ARCHERY_INDEX, HND_CITY_1_INDEX, HND_AERALFOS_INDEX, diff --git a/modules/menus/menu_hundo_saves/src/hundo_saves_menu.cpp b/modules/menus/menu_hundo_saves/src/hundo_saves_menu.cpp index 5b7acee8..91204add 100644 --- a/modules/menus/menu_hundo_saves/src/hundo_saves_menu.cpp +++ b/modules/menus/menu_hundo_saves/src/hundo_saves_menu.cpp @@ -24,13 +24,16 @@ KEEP_FUNC HundoSavesMenu::HundoSavesMenu(Cursor& cursor) {"deku toad", HND_TOAD_INDEX, "Lakebed Temple miniboss"}, {"kargorok flight", HND_KARG_INDEX, "Clip OoB with trumpet bird"}, {"lanayru twilight", HND_LANAYRU_TWILIGHT_INDEX, "Lanayru Twilight tears"}, - {"boss bug", HND_BOSS_BUG_INDEX, "Lanayru Twilight boss bug"}, - {"kb2 skip", HND_KB_2_INDEX, "Clip to skip King Bulblin 2"}, - {"wagon escort", HND_ESCORT_INDEX, "Telma wagon escort"}, {"coro td", HND_COROTD_INDEX, "Text Displacement with Coro"}, + {"early ele", HND_EARLY_ELE_INDEX, "OoB LJA to Death Mountain Elevator"}, {"goron mines", HND_GM_INDEX, "The Goron Mines segment"}, {"dangoro", HND_DANGORO_INDEX, "Goron Mines miniboss"}, - {"post gm", HND_POST_GM_INDEX, "The segment after Goron Mines"}, + {"fyrus", HND_FYRUS_INDEX, "Goron Mines boss"}, + {"waterfall sidehop", HND_WATERFALL_SIDEHOP_INDEX, "Waterfall sidehop after Rutela skip"}, + {"boss bug", HND_BOSS_BUG_INDEX, "Lanayru Twilight boss bug"}, + {"kb2 skip", HND_KB_2_INDEX, "Clip to skip King Bulblin 2"}, + {"wagon escort", HND_ESCORT_INDEX, "Telma wagon escort"}, + {"eldin collection", HND_ELDIN_COLLECT_INDEX, "Eldin Field collection segment"}, {"lakebed bk skip", HND_LAKEBED_BK_SKIP_INDEX, "Boss Key skip in Lakebed Temple"}, {"morpheel", HND_MORPHEEL_INDEX, "Lakebed Temple boss"}, {"mdh tower", HND_MDH_TOWER_INDEX, "MDH tower climb"}, @@ -48,7 +51,7 @@ KEEP_FUNC HundoSavesMenu::HundoSavesMenu(Cursor& cursor) {"post ag", HND_POST_AG_INDEX, "Collection cycle after Arbiter's"}, {"snowpeak", HND_SPR_INDEX, "The Snowpeak Ruins segment"}, {"darkhammer", HND_DARK_HAMMER_INDEX, "Snowpeak Ruins miniboss"}, - {"spr superjump", HND_SPR_SUPERJUMP_INDEX, "SPR Super Jump to 2nd floor"}, + {"spr 2", HND_SPR_2_INDEX, "The 2nd half of Snowpeak Ruins"}, {"spr bk lja", HND_SPR_BK_LJA_INDEX, "LJA to Snowpeak Boss Key room"}, {"spr bk room", HND_SPR_BK_ROOM_INDEX, "Snowpeak Boss Key room"}, {"blizzeta", HND_BLIZZETA_INDEX, "Snowpeak Ruins boss"}, @@ -64,7 +67,9 @@ KEEP_FUNC HundoSavesMenu::HundoSavesMenu(Cursor& cursor) {"post tot", HND_POST_TOT_INDEX, "Collection cycle after Temple of Time"}, {"hotspring minigame", HND_HOTSPRING_INDEX, "Goron hotspring water minigame"}, {"gorge arc", HND_GORGE_ARC_INDEX, "Gorge / South Faron collection"}, + {"silver rupee", HND_SILVER_RUPEE_INDEX, "Getting the Kakariko Silver Rupee"}, {"ice puzzle", HND_PUZZLE_INDEX, "The Ice Puzzle segment"}, + {"iza 2", HND_IZA2_INDEX, "Iza River Ride minigame"}, {"hugo archery", HND_ARCHERY_INDEX, "Archery in Hidden Village"}, {"city 1", HND_CITY_1_INDEX, "The 1st City in the Sky segment"}, {"aeralfos skip", HND_AERALFOS_INDEX, "the city in the sky miniboss"}, @@ -94,13 +99,16 @@ KEEP_FUNC HundoSavesMenu::HundoSavesMenu(Cursor& cursor) HundoSavesMenu::~HundoSavesMenu() {} void HundoSavesMenu::draw() { - special HundoSpecials[HND_SPECIALS_AMNT] = { + special HundoSpecials[] = { special(HND_GOATS_1_INDEX, SaveMngSpecial_Goats1, nullptr), special(HND_MIST_INDEX, SaveMngSpecial_PurpleMist, nullptr), special(HND_KARG_INDEX, SaveMngSpecial_KargOoB, nullptr), + special(HND_EARLY_ELE_INDEX, SaveMngSpecial_EarlyEle, nullptr), special(HND_KB_2_INDEX, SaveMngSpecial_KB2Skip, nullptr), special(HND_ESCORT_INDEX, SaveMngSpecial_Escort, SaveMngSpecial_EscortKeys), + special(HND_ELDIN_COLLECT_INDEX, SaveMngSpecial_EldinCollection, nullptr), special(HND_DANGORO_INDEX, nullptr, SaveMngSpecial_Dangoro), + special(HND_WATERFALL_SIDEHOP_INDEX, SaveMngSpecial_WaterfallSidehop, nullptr), special(HND_LAKEBED_BK_SKIP_INDEX, SaveMngSpecial_LakebedBKSkip, nullptr), special(HND_MORPHEEL_INDEX, nullptr, SaveMngSpecial_Morpheel), special(HND_IZA_1_SKIP_INDEX, SaveMngSpecial_Iza1Skip, nullptr), @@ -128,7 +136,7 @@ void HundoSavesMenu::draw() { } if (GZ_getButtonTrig(SELECTION_BUTTON)) { - SaveManager::triggerLoad(cursor.y, "hundo", HundoSpecials, HND_SPECIALS_AMNT); + SaveManager::triggerLoad(cursor.y, "hundo", HundoSpecials, ARRAY_COUNT(HundoSpecials)); g_menuMgr->hide(); } diff --git a/modules/menus/menu_item_wheel/include/item_wheel_menu.h b/modules/menus/menu_item_wheel/include/item_wheel_menu.h index 70d666d3..1538dd75 100644 --- a/modules/menus/menu_item_wheel/include/item_wheel_menu.h +++ b/modules/menus/menu_item_wheel/include/item_wheel_menu.h @@ -4,7 +4,7 @@ #define ITEM_WHEEL_SLOTS 24 struct ItemWheelData { - int l_listIdx; + unsigned int l_listIdx; }; struct ItemLookup { @@ -21,7 +21,7 @@ class ItemWheelMenu : public Menu { void fixSpecialItems(int i); private: - int& l_listIdx; + unsigned int& l_listIdx; Line lines[ITEM_WHEEL_SLOTS]; }; \ No newline at end of file diff --git a/modules/menus/menu_item_wheel/src/item_wheel_menu.cpp b/modules/menus/menu_item_wheel/src/item_wheel_menu.cpp index febb8c10..2115e272 100644 --- a/modules/menus/menu_item_wheel/src/item_wheel_menu.cpp +++ b/modules/menus/menu_item_wheel/src/item_wheel_menu.cpp @@ -8,7 +8,7 @@ #include "menus/utils/menu_mgr.h" #define ITEM_WHEEL_SLOTS 24 -#define MAX_ITEMS 58 +#define MAX_ITEMS ARRAY_COUNT(l_lookupTbl) #ifdef GCN_PLATFORM #define DEFAULT_BTN_TXT "Z" @@ -90,11 +90,11 @@ const uint8_t l_defaultItems[ITEM_WHEEL_SLOTS] = { BOOMERANG, KANTERA, SPINNER, HVY_BOOTS, BOW, HAWK_EYE, IRONBALL, NO_ITEM, COPY_ROD, HOOKSHOT, W_HOOKSHOT, EMPTY_BOTTLE, EMPTY_BOTTLE, EMPTY_BOTTLE, EMPTY_BOTTLE, - BOMB_BAG_LV1, BOMB_BAG_LV1, BOMB_BAG_LV1, DUNGEON_EXIT, RAFRELS_MEMO, + NORMAL_BOMB, WATER_BOMB, POKE_BOMB, DUNGEON_EXIT, RAFRELS_MEMO, FISHING_ROD_1, HORSE_FLUTE, ANCIENT_DOCUMENT, PACHINKO, }; -const ItemLookup l_lookupTbl[MAX_ITEMS] = { +const ItemLookup l_lookupTbl[] = { {NO_ITEM, "n/a"}, {ANCIENT_DOCUMENT, "ancient sky book (empty)"}, {ANCIENT_DOCUMENT2, "ancient sky book (filled)"}, @@ -157,7 +157,7 @@ const ItemLookup l_lookupTbl[MAX_ITEMS] = { void ItemWheelMenu::updateListIdx() { uint8_t item_id = dComIfGs_getSavedata().getPlayer().getItem().mItems[cursor.y]; - for (int i = 0; i < MAX_ITEMS; i++) { + for (unsigned int i = 0; i < MAX_ITEMS; i++) { if (item_id == l_validItems[i]) { l_listIdx = i; } @@ -193,14 +193,14 @@ void ItemWheelMenu::draw() { for (size_t slot_no = 0; slot_no < MENU_LINE_NUM; slot_no++) { int item_id = dComIfGs_getItem(slot_no, false); - for (int j = 0; j < MAX_ITEMS; j++) { + for (unsigned int j = 0; j < MAX_ITEMS; j++) { if (l_lookupTbl[j].item_id == item_id) { lines[slot_no].printf(" <%s>", item_id != NO_ITEM ? l_lookupTbl[j].name : "n/a"); } if (l_lookupTbl[j].item_id == l_defaultItems[slot_no]) { snprintf(lines[slot_no].description, sizeof(lines[slot_no].description), - "Slot %d default: %s. Press " DEFAULT_BTN_TXT " to set to default; " RESET_BTN_TXT " to reset.", + "Slot %d default: %s. " DEFAULT_BTN_TXT ": set default; " RESET_BTN_TXT ": reset.", slot_no, l_lookupTbl[j].name); } else { continue; diff --git a/modules/menus/menu_memfiles/include/memfiles_menu.h b/modules/menus/menu_memfiles/include/memfiles_menu.h index c5f269d1..a7518fda 100644 --- a/modules/menus/menu_memfiles/include/memfiles_menu.h +++ b/modules/menus/menu_memfiles/include/memfiles_menu.h @@ -8,12 +8,13 @@ #define MEMFILE_DELETE_INDEX 3 struct MemfilesData { + Cursor cursor; uint8_t l_fileNo = 1; }; class MemfilesMenu : public Menu { public: - MemfilesMenu(Cursor&, MemfilesData&); + MemfilesMenu(MemfilesData&); virtual ~MemfilesMenu(); virtual void draw(); diff --git a/modules/menus/menu_memfiles/src/main.cpp b/modules/menus/menu_memfiles/src/main.cpp index 3008489c..5a7a3e9b 100644 --- a/modules/menus/menu_memfiles/src/main.cpp +++ b/modules/menus/menu_memfiles/src/main.cpp @@ -28,15 +28,13 @@ void exit() { } // namespace tpgz::modules void onCreate() { - g_menuMgr->setPersistentData(new MemfilesData()); - if (!g_menuMgr->getPermanentData()) { - g_menuMgr->setPermanentData(new Cursor); + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new MemfilesData); } } void onLoad() { - l_menu = new MemfilesMenu(*g_menuMgr->getPermanentData(), - *g_menuMgr->getPersistentData()); + l_menu = new MemfilesMenu(*g_menuMgr->getPermanentData()); g_drawListener->addListener(onDraw); } @@ -49,8 +47,4 @@ void onUnload() { delete l_menu; } -void onDelete() { - auto data = g_menuMgr->getPersistentData(); - delete data; - g_menuMgr->setPersistentData(nullptr); -} +void onDelete() {} diff --git a/modules/menus/menu_memfiles/src/memfiles_menu.cpp b/modules/menus/menu_memfiles/src/memfiles_menu.cpp index 11711e94..6f759b4b 100644 --- a/modules/menus/menu_memfiles/src/memfiles_menu.cpp +++ b/modules/menus/menu_memfiles/src/memfiles_menu.cpp @@ -11,17 +11,12 @@ #define MAX_SAVE_SLOTS 20 -PositionData memfile_posdata; - -KEEP_FUNC MemfilesMenu::MemfilesMenu(Cursor& cursor, MemfilesData& data) - : Menu(cursor), - l_fileNo(data.l_fileNo), lines{ - {"file slot:", MEMFILE_SLOT_INDEX, "Select memfile slot"}, - {"save", MEMFILE_SAVE_INDEX, "Save memfile to slot", false}, - {"load", MEMFILE_LOAD_INDEX, "Load memfile from slot", false}, - {"delete", MEMFILE_DELETE_INDEX, "Delete memfile from slot", - false}, - } {} +KEEP_FUNC MemfilesMenu::MemfilesMenu(MemfilesData& data) + : Menu(data.cursor), l_fileNo(data.l_fileNo), lines{ + {"file slot:", MEMFILE_SLOT_INDEX, "Select memfile slot"}, + {"save", MEMFILE_SAVE_INDEX, "Save memfile to slot", false}, + {"load", MEMFILE_LOAD_INDEX, "Load memfile from slot", false}, + {"delete", MEMFILE_DELETE_INDEX, "Delete memfile from slot", false}} {} MemfilesMenu::~MemfilesMenu() {} diff --git a/modules/menus/menu_pause/include/pause_menu.h b/modules/menus/menu_pause/include/pause_menu.h index 1d6543fc..403334b1 100644 --- a/modules/menus/menu_pause/include/pause_menu.h +++ b/modules/menus/menu_pause/include/pause_menu.h @@ -1,43 +1,16 @@ #include "menus/menu.h" struct PauseData { - uint8_t l_ordonSword_idx; - uint8_t l_masterSword_idx; - uint8_t l_woodShield_idx; - uint8_t l_hyShield_idx; - uint8_t l_tunic_idx; - uint8_t l_zoraArmor_idx; - uint8_t l_magicArmor_idx; - uint8_t l_bombCap_idx; - uint8_t l_wallet_idx; - uint8_t l_arrowCap_idx; - bool l_ebFlag; - bool l_sbFlag; - bool l_bsFlag; - bool l_hsFlag; - bool l_mdFlag; - bool l_jsFlag; - bool l_gsFlag; + uint8_t l_scent_idx; }; enum PauseIndex { - ORDON_SWORD_INDEX, - MASTER_SWORD_INDEX, - WOOD_SHIELD_INDEX, - HYLIAN_SHIELD_INDEX, - HERO_TUNIC_INDEX, - ZORA_ARMOR_INDEX, - MAGIC_ARMOR_INDEX, - BOMB_CAPACITY_INDEX, - WALLET_INDEX, - ARROW_CAPACITY_INDEX, - ENDING_BLOW_INDEX, - SHIELD_BASH_INDEX, - BACKSLICE_INDEX, - HELM_SPLITTER_INDEX, - MORTAL_DRAW_INDEX, - JUMP_STRIKE_INDEX, - GREAT_SPIN_INDEX + EQUIPMENT_INDEX, + GOLDEN_BUG_INDEX, + HIDDEN_SKILL_INDEX, + SCENT_INDEX, + + PAUSE_INDEX_COUNT }; extern PauseData* pauseData; @@ -49,9 +22,5 @@ class PauseMenu : public Menu { virtual void draw(); private: - Line lines[17]; - - void resetIndex(); - void getEquipment(); - void setEquipment(); + Line lines[PAUSE_INDEX_COUNT]; }; \ No newline at end of file diff --git a/modules/menus/menu_pause/src/pause_menu.cpp b/modules/menus/menu_pause/src/pause_menu.cpp index 4f04ef9d..78e2594b 100644 --- a/modules/menus/menu_pause/src/pause_menu.cpp +++ b/modules/menus/menu_pause/src/pause_menu.cpp @@ -6,389 +6,98 @@ #include "rels/include/defines.h" #include "menus/utils/menu_mgr.h" -#define MAX_ORDON_SWORD_OPT 3 -#define MAX_MASTER_SWORD_OPT 3 -#define MAX_WOOD_SHIELD_OPT 3 -#define MAX_HYLIAN_SHIELD_OPT 2 -#define MAX_HERO_TUNIC_OPT 2 -#define MAX_ZORA_ARMOR_OPT 2 -#define MAX_MAGIC_ARMOR_OPT 2 -#define MAX_BOMB_CAPACITY_OPT 2 -#define MAX_WALLET_OPT 3 -#define MAX_ARROW_CAPACITY_OPT 3 -#define MAX_HIDDEN_SKILL_OPT 2 +#define MAX_SCENT_OPT 6 KEEP_VAR PauseData* pauseData; KEEP_FUNC PauseMenu::PauseMenu(Cursor& cursor) : Menu(cursor), - lines{{"ordon sword:", ORDON_SWORD_INDEX, "Wooden Sword / Ordon Sword", false, nullptr, - MAX_ORDON_SWORD_OPT}, - {"master sword:", MASTER_SWORD_INDEX, "Master Sword / Light Sword", false, nullptr, - MAX_MASTER_SWORD_OPT}, - {"wooden shield:", WOOD_SHIELD_INDEX, "Ordon Shield / Wooden Shield", false, nullptr, - MAX_WOOD_SHIELD_OPT}, - {"hylian shield:", HYLIAN_SHIELD_INDEX, "Hylian Shield", false, nullptr, - MAX_HYLIAN_SHIELD_OPT}, - {"hero's tunic:", HERO_TUNIC_INDEX, "Hero's Tunic", false, nullptr, MAX_HERO_TUNIC_OPT}, - {"zora armor:", ZORA_ARMOR_INDEX, "Zora Armor", false, nullptr, MAX_ZORA_ARMOR_OPT}, - {"magic armor:", MAGIC_ARMOR_INDEX, "Magic Armor", false, nullptr, MAX_MAGIC_ARMOR_OPT}, - {"bomb capacity:", BOMB_CAPACITY_INDEX, "Bomb Bag Capacity", false, nullptr, - MAX_BOMB_CAPACITY_OPT}, - {"wallet upgrade:", WALLET_INDEX, "Wallet Capacity", false, nullptr, MAX_WALLET_OPT}, - {"arrow capacity:", ARROW_CAPACITY_INDEX, "Arrow Quiver Capacity", false, nullptr, - MAX_ARROW_CAPACITY_OPT}, - {"ending blow:", ENDING_BLOW_INDEX, "Ending Blow", true, [](){return pauseData->l_ebFlag;}}, - {"shield bash:", SHIELD_BASH_INDEX, "Shield Bash", true, [](){return pauseData->l_sbFlag;}}, - {"backslice:", BACKSLICE_INDEX, "Backslice", true, [](){return pauseData->l_bsFlag;}}, - {"helm splitter:", HELM_SPLITTER_INDEX, "Helm Splitter", true, [](){return pauseData->l_hsFlag;}}, - {"mortal draw:", MORTAL_DRAW_INDEX, "Mortal Draw", true, [](){return pauseData->l_mdFlag;}}, - {"jump strike:", JUMP_STRIKE_INDEX, "Jump Strike", true, [](){return pauseData->l_jsFlag;}}, - {"greatspin:", GREAT_SPIN_INDEX, "Greatspin", true, [](){return pauseData->l_gsFlag;}}} {} + lines{{"equipment", EQUIPMENT_INDEX, "Equipment", false, nullptr}, + {"golden bugs", GOLDEN_BUG_INDEX, "Golden Bugs", false, nullptr}, + {"hidden skills", HIDDEN_SKILL_INDEX, "Hidden Skills", false, nullptr}, + {"scent:", SCENT_INDEX, "Current Scent", false, nullptr}} {} PauseMenu::~PauseMenu() {} -void PauseMenu::resetIndex() { - pauseData->l_ordonSword_idx = 0; - pauseData->l_masterSword_idx = 0; - pauseData->l_woodShield_idx = 0; - pauseData->l_hyShield_idx = 0; - pauseData->l_tunic_idx = 0; - pauseData->l_zoraArmor_idx = 0; - pauseData->l_magicArmor_idx = 0; - pauseData->l_bombCap_idx = 0; - pauseData->l_wallet_idx = 0; - pauseData->l_arrowCap_idx = 0; -} - -void PauseMenu::getEquipment() { - if (dComIfGs_isItemFirstBit(SWORD)) { - pauseData->l_ordonSword_idx = 2; - } else if (dComIfGs_isItemFirstBit(WOOD_STICK)) { - pauseData->l_ordonSword_idx = 1; - } - - if (dComIfGs_isItemFirstBit(LIGHT_SWORD)) { - pauseData->l_masterSword_idx = 2; - } else if (dComIfGs_isItemFirstBit(MASTER_SWORD)) { - pauseData->l_masterSword_idx = 1; - } - - if (dComIfGs_isItemFirstBit(SHIELD)) { - pauseData->l_woodShield_idx = 2; - } else if (dComIfGs_isItemFirstBit(WOOD_SHIELD)) { - pauseData->l_woodShield_idx = 1; - } - - if (dComIfGs_isItemFirstBit(HYLIA_SHIELD)) { - pauseData->l_hyShield_idx = 1; - } - - if (dComIfGs_isItemFirstBit(WEAR_KOKIRI)) { - pauseData->l_tunic_idx = 1; - } - - if (dComIfGs_isItemFirstBit(WEAR_ZORA)) { - pauseData->l_zoraArmor_idx = 1; - } - - if (dComIfGs_isItemFirstBit(ARMOR)) { - pauseData->l_magicArmor_idx = 1; - } - - if (dComIfGs_isItemFirstBit(BOMB_BAG_LV2)) { - pauseData->l_bombCap_idx = 1; - } - - switch (dComIfGs_getWalletSize()) { - case 1: - pauseData->l_wallet_idx = 1; - break; - case 2: - pauseData->l_wallet_idx = 2; - break; - } - - if (dComIfGs_getArrowMax() == 100) { - pauseData->l_arrowCap_idx = 2; - } else if (dComIfGs_getArrowMax() == 60) { - pauseData->l_arrowCap_idx = 1; - } -} - -void PauseMenu::setEquipment() { - switch (pauseData->l_ordonSword_idx) { - case 0: - dComIfGs_offItemFirstBit(WOOD_STICK); - dComIfGs_offItemFirstBit(SWORD); - break; - case 1: - dComIfGs_onItemFirstBit(WOOD_STICK); - dComIfGs_offItemFirstBit(SWORD); - break; - case 2: - dComIfGs_onItemFirstBit(SWORD); - dComIfGs_offItemFirstBit(WOOD_STICK); - break; - } - - switch (pauseData->l_masterSword_idx) { - case 0: - dComIfGs_offItemFirstBit(MASTER_SWORD); - dComIfGs_offItemFirstBit(LIGHT_SWORD); - break; - case 1: - dComIfGs_onItemFirstBit(MASTER_SWORD); - dComIfGs_offItemFirstBit(LIGHT_SWORD); - break; - case 2: - dComIfGs_onItemFirstBit(LIGHT_SWORD); - break; - } - - switch (pauseData->l_woodShield_idx) { - case 0: - dComIfGs_offItemFirstBit(SHIELD); - dComIfGs_offItemFirstBit(WOOD_SHIELD); - break; - case 1: - dComIfGs_onItemFirstBit(WOOD_SHIELD); - break; - case 2: - dComIfGs_onItemFirstBit(SHIELD); - break; - } - - switch (pauseData->l_hyShield_idx) { - case 0: - dComIfGs_offItemFirstBit(HYLIA_SHIELD); - break; - case 1: - dComIfGs_onItemFirstBit(HYLIA_SHIELD); - break; - } - - switch (pauseData->l_tunic_idx) { - case 0: - dComIfGs_offItemFirstBit(WEAR_KOKIRI); - break; - case 1: - dComIfGs_onItemFirstBit(WEAR_KOKIRI); - break; - } - - switch (pauseData->l_zoraArmor_idx) { - case 0: - dComIfGs_offItemFirstBit(WEAR_ZORA); - break; - case 1: - dComIfGs_onItemFirstBit(WEAR_ZORA); - break; - } - - switch (pauseData->l_magicArmor_idx) { - case 0: - dComIfGs_offItemFirstBit(ARMOR); - break; - case 1: - dComIfGs_onItemFirstBit(ARMOR); - break; - } +void PauseMenu::draw() { + cursor.setMode(Cursor::MODE_LIST); - switch (pauseData->l_bombCap_idx) { - case 0: - dComIfGs_offItemFirstBit(BOMB_BAG_LV2); + switch (dComIfGs_getCollectSmell()) { + case 180: + pauseData->l_scent_idx = 1; break; - case 1: - dComIfGs_onItemFirstBit(BOMB_BAG_LV2); - break; - } - - switch (pauseData->l_wallet_idx) { - case 0: - dComIfGs_setWalletSize(WALLET); + case 176: + pauseData->l_scent_idx = 2; break; - case 1: - dComIfGs_setWalletSize(BIG_WALLET); + case 178: + pauseData->l_scent_idx = 3; break; - case 2: - dComIfGs_setWalletSize(GIANT_WALLET); + case 179: + pauseData->l_scent_idx = 4; break; - } - - switch (pauseData->l_arrowCap_idx) { - case 0: - dComIfGs_setArrowMax(30); + case 181: + pauseData->l_scent_idx = 5; break; - case 1: - dComIfGs_setArrowMax(60); - break; - case 2: - dComIfGs_setArrowMax(100); - break; - } -} - -void PauseMenu::draw() { - static bool init = false; - cursor.setMode(Cursor::MODE_LIST); + default: + pauseData->l_scent_idx = 0; - if (!init) { - getEquipment(); - init = true; } if (GZ_getButtonTrig(BACK_BUTTON)) { - init = false; g_menuMgr->pop(); - resetIndex(); return; } - // update hidden skill flags - pauseData->l_ebFlag = dComIfGs_isEventBit(0x2904); - pauseData->l_sbFlag = dComIfGs_isEventBit(0x2908); - pauseData->l_bsFlag = dComIfGs_isEventBit(0x2902); - pauseData->l_hsFlag = dComIfGs_isEventBit(0x2901); - pauseData->l_mdFlag = dComIfGs_isEventBit(0x2A80); - pauseData->l_jsFlag = dComIfGs_isEventBit(0x2A40); - pauseData->l_gsFlag = dComIfGs_isEventBit(0x2A20); - - ListMember ordonSword_opt[3] = {"none", "wooden sword", "ordon sword"}; - ListMember masterSword_opt[3] = {"none", "master sword", "light sword"}; - ListMember woodShield_opt[3] = {"none", "ordon shield", "wooden shield"}; - ListMember hyShield_opt[2] = {"none", "hylian shield"}; - ListMember tunic_opt[2] = {"none", "hero's tunic"}; - ListMember zoraArmor_opt[2] = {"none", "zora armor"}; - ListMember magicArmor_opt[2] = {"none", "magic armor"}; - ListMember bombCap_opt[2] = {"30/15/10", "60/30/20"}; - ListMember wallet_opt[3] = {"300 Rupees", "600 Rupees", "1000 Rupees"}; - ListMember arrowCap_opt[3] = {"30 Arrows", "60 Arrows", "100 Arrows"}; + ListMember scent_opt[6] = {"none", "youths' scent", "scent of ilia", "poe scent", "reekfish scent", "medicine scent"}; switch (cursor.y) { - case ORDON_SWORD_INDEX: - cursor.x = pauseData->l_ordonSword_idx; - cursor.move(MAX_ORDON_SWORD_OPT, MENU_LINE_NUM); + case SCENT_INDEX: + cursor.x = pauseData->l_scent_idx; + cursor.move(MAX_SCENT_OPT, MENU_LINE_NUM); - if (cursor.y == ORDON_SWORD_INDEX) { - pauseData->l_ordonSword_idx = cursor.x; + if (cursor.y == SCENT_INDEX) { + pauseData->l_scent_idx = cursor.x; } break; - case MASTER_SWORD_INDEX: - cursor.x = pauseData->l_masterSword_idx; - cursor.move(MAX_MASTER_SWORD_OPT, MENU_LINE_NUM); - - if (cursor.y == MASTER_SWORD_INDEX) { - pauseData->l_masterSword_idx = cursor.x; - } - break; - case WOOD_SHIELD_INDEX: - cursor.x = pauseData->l_woodShield_idx; - cursor.move(MAX_WOOD_SHIELD_OPT, MENU_LINE_NUM); - - if (cursor.y == WOOD_SHIELD_INDEX) { - pauseData->l_woodShield_idx = cursor.x; - } - break; - case HYLIAN_SHIELD_INDEX: - cursor.x = pauseData->l_hyShield_idx; - cursor.move(MAX_HYLIAN_SHIELD_OPT, MENU_LINE_NUM); - - if (cursor.y == HYLIAN_SHIELD_INDEX) { - pauseData->l_hyShield_idx = cursor.x; - } + default: + cursor.move(0, MENU_LINE_NUM); break; - case HERO_TUNIC_INDEX: - cursor.x = pauseData->l_tunic_idx; - cursor.move(MAX_HERO_TUNIC_OPT, MENU_LINE_NUM); + } - if (cursor.y == HERO_TUNIC_INDEX) { - pauseData->l_tunic_idx = cursor.x; + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + switch (cursor.y) { + case EQUIPMENT_INDEX: + g_menuMgr->push(MN_EQUIPMENT_INDEX); + return; + case GOLDEN_BUG_INDEX: + g_menuMgr->push(MN_GOLDEN_BUGS_INDEX); + return; + case HIDDEN_SKILL_INDEX: + g_menuMgr->push(MN_HIDDEN_SKILLS_INDEX); } - break; - case ZORA_ARMOR_INDEX: - cursor.x = pauseData->l_zoraArmor_idx; - cursor.move(MAX_ZORA_ARMOR_OPT, MENU_LINE_NUM); + } - if (cursor.y == ZORA_ARMOR_INDEX) { - pauseData->l_zoraArmor_idx = cursor.x; - } + switch (pauseData->l_scent_idx) { + case 1: + dComIfGs_setCollectSmell(180); break; - case MAGIC_ARMOR_INDEX: - cursor.x = pauseData->l_magicArmor_idx; - cursor.move(MAX_MAGIC_ARMOR_OPT, MENU_LINE_NUM); - - if (cursor.y == MAGIC_ARMOR_INDEX) { - pauseData->l_magicArmor_idx = cursor.x; - } + case 2: + dComIfGs_setCollectSmell(176); break; - case BOMB_CAPACITY_INDEX: - cursor.x = pauseData->l_bombCap_idx; - cursor.move(MAX_BOMB_CAPACITY_OPT, MENU_LINE_NUM); - - if (cursor.y == BOMB_CAPACITY_INDEX) { - pauseData->l_bombCap_idx = cursor.x; - } + case 3: + dComIfGs_setCollectSmell(178); break; - case WALLET_INDEX: - cursor.x = pauseData->l_wallet_idx; - cursor.move(MAX_WALLET_OPT, MENU_LINE_NUM); - - if (cursor.y == WALLET_INDEX) { - pauseData->l_wallet_idx = cursor.x; - } + case 4: + dComIfGs_setCollectSmell(179); break; - case ARROW_CAPACITY_INDEX: - cursor.x = pauseData->l_arrowCap_idx; - cursor.move(MAX_ARROW_CAPACITY_OPT, MENU_LINE_NUM); - - if (cursor.y == ARROW_CAPACITY_INDEX) { - pauseData->l_arrowCap_idx = cursor.x; - } + case 5: + dComIfGs_setCollectSmell(181); break; default: - cursor.move(0, MENU_LINE_NUM); - break; + dComIfGs_setCollectSmell(255); } - if (GZ_getButtonTrig(SELECTION_BUTTON)) { - switch (cursor.y) { - case ENDING_BLOW_INDEX: - setEventFlag(0x2904); - break; - case SHIELD_BASH_INDEX: - setEventFlag(0x2908); - break; - case BACKSLICE_INDEX: - setEventFlag(0x2902); - break; - case HELM_SPLITTER_INDEX: - setEventFlag(0x2901); - break; - case MORTAL_DRAW_INDEX: - setEventFlag(0x2A80); - break; - case JUMP_STRIKE_INDEX: - setEventFlag(0x2A40); - break; - case GREAT_SPIN_INDEX: - setEventFlag(0x2A20); - break; - } - } - - setEquipment(); - - lines[ORDON_SWORD_INDEX].printf(" <%s>", ordonSword_opt[pauseData->l_ordonSword_idx].member); - lines[MASTER_SWORD_INDEX].printf(" <%s>", masterSword_opt[pauseData->l_masterSword_idx].member); - lines[WOOD_SHIELD_INDEX].printf(" <%s>", woodShield_opt[pauseData->l_woodShield_idx].member); - lines[HYLIAN_SHIELD_INDEX].printf(" <%s>", hyShield_opt[pauseData->l_hyShield_idx].member); - lines[HERO_TUNIC_INDEX].printf(" <%s>", tunic_opt[pauseData->l_tunic_idx].member); - lines[ZORA_ARMOR_INDEX].printf(" <%s>", zoraArmor_opt[pauseData->l_zoraArmor_idx].member); - lines[MAGIC_ARMOR_INDEX].printf(" <%s>", magicArmor_opt[pauseData->l_magicArmor_idx].member); - lines[BOMB_CAPACITY_INDEX].printf(" <%s>", bombCap_opt[pauseData->l_bombCap_idx].member); - lines[WALLET_INDEX].printf(" <%s>", wallet_opt[pauseData->l_wallet_idx].member); - lines[ARROW_CAPACITY_INDEX].printf(" <%s>", arrowCap_opt[pauseData->l_arrowCap_idx].member); + lines[SCENT_INDEX].printf(" <%s>", scent_opt[pauseData->l_scent_idx].member); GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); } diff --git a/modules/menus/menu_pos_settings/src/position_settings_menu.cpp b/modules/menus/menu_pos_settings/src/position_settings_menu.cpp index f942aaa0..249bf25d 100644 --- a/modules/menus/menu_pos_settings/src/position_settings_menu.cpp +++ b/modules/menus/menu_pos_settings/src/position_settings_menu.cpp @@ -18,6 +18,8 @@ KEEP_FUNC PosSettingsMenu::PosSettingsMenu(Cursor& cursor, PosSettingsData& data {"input viewer", SpritesIndex::VIEWER_INDEX, "Change input viewer position", false}, {"link debug info", SpritesIndex::DEBUG_INFO_INDEX, "Change link debug info position", false}, + {"stage info", SpritesIndex::STAGE_INFO_INDEX, "Change link stage info position", + false}, {"timer", SpritesIndex::TIMER_SPR_INDEX, "Change timer position", false}, {"load timer", SpritesIndex::LOAD_TIMER_SPR_INDEX, "Change load timer position", false}, {"igt timer", SpritesIndex::IGT_TIMER_SPR_INDEX, "Change IGT timer position", false}, @@ -48,6 +50,7 @@ GZSettingID l_mapping[] = { STNG_SPRITES_MENU, STNG_SPRITES_INPUT_VIEWER, STNG_SPRITES_DEBUG_INFO, + STNG_SPRITES_STAGE_INFO, STNG_SPRITES_TIMER_SPR, STNG_SPRITES_LOAD_TIMER_SPR, STNG_SPRITES_IGT_TIMER_SPR, diff --git a/modules/menus/menu_practice/include/practice_menu.h b/modules/menus/menu_practice/include/practice_menu.h index c66a8f65..0a9c14bb 100644 --- a/modules/menus/menu_practice/include/practice_menu.h +++ b/modules/menus/menu_practice/include/practice_menu.h @@ -6,7 +6,7 @@ #ifdef GCN_PLATFORM #define PRACTICE_MENU_NUM 6 #elif defined WII_PLATFORM -#define PRACTICE_MENU_NUM 4 +#define PRACTICE_MENU_NUM 5 #endif class PracticeMenu : public Menu { diff --git a/modules/menus/menu_practice/src/practice_menu.cpp b/modules/menus/menu_practice/src/practice_menu.cpp index 15f00994..f1cd0992 100644 --- a/modules/menus/menu_practice/src/practice_menu.cpp +++ b/modules/menus/menu_practice/src/practice_menu.cpp @@ -6,9 +6,7 @@ KEEP_FUNC PracticeMenu::PracticeMenu(Cursor& cursor) : Menu(cursor), lines{ {"any%", ANY_INDEX, "Any% practice saves", false}, -#ifdef GCN_PLATFORM {"any% BiTE", ANY_BITE_INDEX, "Any% (BiTE route) practice saves", false}, -#endif {"100%", HUNDO_INDEX, "100% practice saves", false}, {"all dungeons", AD_INDEX, "All Dungeons practice saves", false}, #ifdef GCN_PLATFORM @@ -30,11 +28,9 @@ void PracticeMenu::draw() { case ANY_INDEX: g_menuMgr->push(MN_ANY_SAVES_INDEX); return; -#ifdef GCN_PLATFORM case ANY_BITE_INDEX: g_menuMgr->push(MN_ANY_BITE_SAVES_INDEX); return; -#endif case HUNDO_INDEX: g_menuMgr->push(MN_HUNDO_SAVES_INDEX); return; diff --git a/modules/menus/menu_rupee_flags/CMakeLists.txt b/modules/menus/menu_rupee_flags/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_rupee_flags/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_rupee_flags/include/main.h b/modules/menus/menu_rupee_flags/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_rupee_flags/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_rupee_flags/include/rupee_flags_menu.h b/modules/menus/menu_rupee_flags/include/rupee_flags_menu.h new file mode 100644 index 00000000..800fd1fb --- /dev/null +++ b/modules/menus/menu_rupee_flags/include/rupee_flags_menu.h @@ -0,0 +1,32 @@ +#pragma once +#include "menus/menu.h" + +struct RupeeFlagsData { + u16 l_donationAmount; + u16 l_fundraisingAmount; + bool l_fundraising1; + bool l_fundraising2; + bool l_rupeeFlag; +}; + +enum GeneralFlagsIndex { + DONATION_AMT_INDEX, + FUNDRAISING_AMT_INDEX, + FUNDRAISING_1_INDEX, + FUNDRAISING_2_INDEX, + RUPEE_CS_FLAG_INDEX, + + RUPEE_FLAGS_COUNT +}; + +extern RupeeFlagsData* rupeeFlagsData; + +class RupeeFlagsMenu : public Menu { +public: + RupeeFlagsMenu(Cursor&); + virtual ~RupeeFlagsMenu(); + virtual void draw(); + +private: + Line lines[RUPEE_FLAGS_COUNT]; +}; diff --git a/modules/menus/menu_rupee_flags/src/main.cpp b/modules/menus/menu_rupee_flags/src/main.cpp new file mode 100644 index 00000000..679cc18b --- /dev/null +++ b/modules/menus/menu_rupee_flags/src/main.cpp @@ -0,0 +1,56 @@ +#include +#include "menus/menu_rupee_flags/include/rupee_flags_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +RupeeFlagsMenu* l_menu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + g_menuMgr->setPersistentData(new RupeeFlagsData); + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + rupeeFlagsData = g_menuMgr->getPersistentData(); + l_menu = new RupeeFlagsMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_menu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_menu; +} + +void onDelete() { + auto data = g_menuMgr->getPersistentData(); + delete data; + g_menuMgr->setPersistentData(nullptr); +} diff --git a/modules/menus/menu_rupee_flags/src/rupee_flags_menu.cpp b/modules/menus/menu_rupee_flags/src/rupee_flags_menu.cpp new file mode 100644 index 00000000..b47acae1 --- /dev/null +++ b/modules/menus/menu_rupee_flags/src/rupee_flags_menu.cpp @@ -0,0 +1,109 @@ +#include "menus/menu_rupee_flags/include/rupee_flags_menu.h" +#include "gz_flags.h" +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/utils.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +KEEP_VAR RupeeFlagsData* rupeeFlagsData; + +KEEP_FUNC RupeeFlagsMenu::RupeeFlagsMenu(Cursor& cursor) + : Menu(cursor), + lines{ + {"donation amount:", DONATION_AMT_INDEX, "Sets the amount of rupees donated to Charlo"}, + {"fundraising amount:", FUNDRAISING_AMT_INDEX, "Sets the current fundraising amount"}, + {"fundraising 1", FUNDRAISING_1_INDEX, "Toggle flag for first fundraising being complete", true, + [](){return rupeeFlagsData->l_fundraising1;}}, + {"fundraising 2", FUNDRAISING_2_INDEX, "Toggle flag for second fundraising being complete", true, + [](){return rupeeFlagsData->l_fundraising2;}}, + {"rupee cutscenes", RUPEE_CS_FLAG_INDEX, "Toggle rupee cutscenes being enabled", true, + [](){return rupeeFlagsData->l_rupeeFlag;}}, + } {} + +RupeeFlagsMenu::~RupeeFlagsMenu() {} + +void RupeeFlagsMenu::draw() { + cursor.setMode(Cursor::MODE_LIST); + + if (!rupeeFlagsData) { + return; + } + + // update flags + rupeeFlagsData->l_fundraising1 = dComIfGs_isEventBit(0x2e20); + rupeeFlagsData->l_fundraising2 = dComIfGs_isEventBit(0x0f10); + + // update donation amount + u8 donation_high_bits = dComIfGs_getEventReg(0xf7ff); + u8 donation_low_bits = dComIfGs_getEventReg(0xf8ff); + + rupeeFlagsData->l_donationAmount = donation_high_bits << 8 | donation_low_bits; + + // update fundraising amount + u8 fund_high_bits = dComIfGs_getEventReg(0xf9ff); + u8 fund_low_bits = dComIfGs_getEventReg(0xfaff); + + rupeeFlagsData->l_fundraisingAmount = fund_high_bits << 8 | fund_low_bits; + + for (int i = BLUE_RUPEE; i <= SILVER_RUPEE; i++) { + if (dComIfGs_isItemFirstBit(i)) { + rupeeFlagsData->l_rupeeFlag = true; + break; + } + } + + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + switch (cursor.y) { + case DONATION_AMT_INDEX: + Cursor::moveList(rupeeFlagsData->l_donationAmount); + + if (rupeeFlagsData->l_donationAmount > 1000) + rupeeFlagsData->l_donationAmount = 1000; + + dComIfGs_setEventReg(0xf7ff, (rupeeFlagsData->l_donationAmount >> 8) & 0xFF); + dComIfGs_setEventReg(0xf8ff, rupeeFlagsData->l_donationAmount & 0xFF); + break; + case FUNDRAISING_AMT_INDEX: + Cursor::moveList(rupeeFlagsData->l_fundraisingAmount); + + if (rupeeFlagsData->l_fundraisingAmount > 2000) + rupeeFlagsData->l_fundraisingAmount = 2000; + + dComIfGs_setEventReg(0xf9ff, (rupeeFlagsData->l_fundraisingAmount >> 8) & 0xFF); + dComIfGs_setEventReg(0xfaff, rupeeFlagsData->l_fundraisingAmount & 0xFF); + break; + } + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + switch (cursor.y) { + case FUNDRAISING_1_INDEX: + setEventFlag(0x2e20); + break; + case FUNDRAISING_2_INDEX: + setEventFlag(0x0f10); + break; + case RUPEE_CS_FLAG_INDEX: + if (rupeeFlagsData->l_rupeeFlag) { + for (int i = BLUE_RUPEE; i <= SILVER_RUPEE; i++) { + dComIfGs_offItemFirstBit(i); + } + rupeeFlagsData->l_rupeeFlag = false; + } else { + for (int i = BLUE_RUPEE; i <= SILVER_RUPEE; i++) { + dComIfGs_onItemFirstBit(i); + } + } + break; + } + } + + lines[DONATION_AMT_INDEX].printf(" <%d>", rupeeFlagsData->l_donationAmount); + lines[FUNDRAISING_AMT_INDEX].printf(" <%d>", rupeeFlagsData->l_fundraisingAmount); + + cursor.move(0, MENU_LINE_NUM); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_scene/src/scene_menu.cpp b/modules/menus/menu_scene/src/scene_menu.cpp index 9f7fc36c..72d9c06e 100644 --- a/modules/menus/menu_scene/src/scene_menu.cpp +++ b/modules/menus/menu_scene/src/scene_menu.cpp @@ -36,6 +36,7 @@ KEEP_FUNC SceneMenu::SceneMenu(Cursor& cursor) {"collision viewer", COLLISION_VIEW_INDEX, "Change Collision Viewer settings", false}, {"projection viewer", PROJECTION_VIEW_INDEX, "Change Projection Viewer settings", false}, {"trigger viewer", TRIGGER_VIEW_INDEX, "Change Trigger Viewer settings", false}, + {"sound test", SOUND_TEST_INDEX, "Play a specified sound effect", false}, } {} SceneMenu::~SceneMenu() {} @@ -95,6 +96,9 @@ void SceneMenu::draw() { case TRIGGER_VIEW_INDEX: g_menuMgr->push(MN_TRIGGER_VIEW_INDEX); return; + case SOUND_TEST_INDEX: + g_menuMgr->push(MN_SOUND_TEST_INDEX); + return; } } diff --git a/modules/menus/menu_sound_test/CMakeLists.txt b/modules/menus/menu_sound_test/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_sound_test/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_sound_test/include/main.h b/modules/menus/menu_sound_test/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_sound_test/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_sound_test/include/sound_test_menu.h b/modules/menus/menu_sound_test/include/sound_test_menu.h new file mode 100644 index 00000000..19453bfb --- /dev/null +++ b/modules/menus/menu_sound_test/include/sound_test_menu.h @@ -0,0 +1,11 @@ +#include "menus/menu.h" + +class SoundTestMenu : public Menu { +public: + SoundTestMenu(Cursor&); + virtual ~SoundTestMenu(); + virtual void draw(); + +private: + Line lines[4]; +}; diff --git a/modules/menus/menu_sound_test/src/main.cpp b/modules/menus/menu_sound_test/src/main.cpp new file mode 100644 index 00000000..ffeba234 --- /dev/null +++ b/modules/menus/menu_sound_test/src/main.cpp @@ -0,0 +1,50 @@ +#include +#include "menus/menu_sound_test/include/sound_test_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +SoundTestMenu* l_menu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + l_menu = new SoundTestMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_menu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_menu; +} + +void onDelete() {} diff --git a/modules/menus/menu_sound_test/src/sound_test_menu.cpp b/modules/menus/menu_sound_test/src/sound_test_menu.cpp new file mode 100644 index 00000000..636880f9 --- /dev/null +++ b/modules/menus/menu_sound_test/src/sound_test_menu.cpp @@ -0,0 +1,64 @@ +#include "menus/menu_sound_test/include/sound_test_menu.h" +#include +#include "settings.h" +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "libtp_c/include/f_op/f_op_actor_mng.h" +#include "libtp_c/include/m_Do/m_Do_printf.h" +#include "libtp_c/include/m_Do/m_Do_audio.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +KEEP_FUNC SoundTestMenu::SoundTestMenu(Cursor& cursor) + : Menu(cursor), lines{ + {"category id:", 0, "Select Sound Category ID", false}, + {"sound id:", 1, "Select Sound Effect ID", false}, + {"play", 2, "Play Sound Effect", false}, + {"stop", 3, "Stop Sound Effect", false}, + } {} + +SoundTestMenu::~SoundTestMenu() {} + +void SoundTestMenu::draw() { + cursor.setMode(Cursor::MODE_LIST); + static u32 l_categoryID = 0; + static u32 l_soundID = 0; + + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + switch (cursor.y) { + case 0: + Cursor::moveList(l_categoryID); + if (l_categoryID > 9) { + l_categoryID = 0; + } + break; + case 1: + Cursor::moveList(l_soundID); + if (l_soundID > 0xFFFF) { + l_soundID = 0; + } + break; + } + + u32 composite_id = (l_categoryID << 0x10) | l_soundID; + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + switch (cursor.y) { + case 2: + mDoAud_seStart(composite_id, nullptr, 0, 0); + break; + case 3: + mDoAud_seStop(composite_id, 0); + break; + } + } + + lines[0].printf(" <%d>", l_categoryID); + lines[1].printf(" <%d>", l_soundID); + + cursor.move(0, MENU_LINE_NUM); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} \ No newline at end of file diff --git a/modules/menus/menu_tools/include/tools_menu.h b/modules/menus/menu_tools/include/tools_menu.h index 2043c905..34d27c12 100644 --- a/modules/menus/menu_tools/include/tools_menu.h +++ b/modules/menus/menu_tools/include/tools_menu.h @@ -3,45 +3,12 @@ #include "menus/menu.h" #include "tools.h" -#ifdef GCN_PLATFORM -#define FRAME_ADVANCE_TEXT "R + D-Pad Up" -#define FRAME_PAUSE_TEXT "R" -#define GORGE_VOID_TEXT "L+Z" -#define STORE_POSITION_TEXT "D-PAD up + R" -#define LOAD_POSITION_TEXT "D-PAD down + R" -#define RELOAD_AREA_TEXT "L+R+A+Start" -#define TIMER_TOGGLE_TEXT "Z+A" -#define TIMER_RESET_TEXT "Z+B" -#define FREE_CAM_TEXT "Z+B+A" -#define MOVE_LINK_TEXT "L+R+Y" -#endif - -#ifdef WII_PLATFORM -#define FRAME_ADVANCE_TEXT "Z+C+Plus+Minus" -#define FRAME_PAUSE_TEXT "2" -#define GORGE_VOID_TEXT "Z+C+A+1" -#define BACK_IN_TIME_TEXT "Z+C+A+2" -#define STORE_POSITION_TEXT "Z+C+1" -#define LOAD_POSITION_TEXT "Z+C+2" -#define RELOAD_AREA_TEXT "Z+C+B+2" -#define TIMER_TOGGLE_TEXT "Z+C+A+B" -#define TIMER_RESET_TEXT "Z+C+B+1" -#define FREE_CAM_TEXT "Z+C+B+Minus" -#define MOVE_LINK_TEXT "Z+C+B+Plus" -#endif - -struct ToolsData { - uint8_t l_tunicCol_idx; -}; - class ToolsMenu : public Menu { public: - ToolsMenu(Cursor&, ToolsData&); + ToolsMenu(Cursor&); virtual ~ToolsMenu(); virtual void draw(); private: - uint8_t& l_tunicCol_idx; - Line lines[TOOLS_COUNT]; }; \ No newline at end of file diff --git a/modules/menus/menu_tools/src/main.cpp b/modules/menus/menu_tools/src/main.cpp index 2eb088f6..9f2b0b2e 100644 --- a/modules/menus/menu_tools/src/main.cpp +++ b/modules/menus/menu_tools/src/main.cpp @@ -28,15 +28,13 @@ void exit() { } // namespace tpgz::modules void onCreate() { - g_menuMgr->setPersistentData(new ToolsData()); if (!g_menuMgr->getPermanentData()) { g_menuMgr->setPermanentData(new Cursor); } } void onLoad() { - l_toolsMenu = new ToolsMenu(*g_menuMgr->getPermanentData(), - *g_menuMgr->getPersistentData()); + l_toolsMenu = new ToolsMenu(*g_menuMgr->getPermanentData()); g_drawListener->addListener(onDraw); } @@ -49,8 +47,4 @@ void onUnload() { delete l_toolsMenu; } -void onDelete() { - auto data = g_menuMgr->getPersistentData(); - delete data; - g_menuMgr->setPersistentData(nullptr); -} +void onDelete() {} diff --git a/modules/menus/menu_tools/src/tools_menu.cpp b/modules/menus/menu_tools/src/tools_menu.cpp index 51f77ff0..7ccbc7c7 100644 --- a/modules/menus/menu_tools/src/tools_menu.cpp +++ b/modules/menus/menu_tools/src/tools_menu.cpp @@ -1,299 +1,43 @@ #include "menus/menu_tools/include/tools_menu.h" -#include -#include "commands.h" -#include "global_data.h" -#include "timer.h" -#include "libtp_c/include/d/com/d_com_inf_game.h" -#include "gz_flags.h" -#include "rels/include/defines.h" -#include "rels/include/defines.h" #include "menus/utils/menu_mgr.h" -#ifdef GCN_PLATFORM -#define FREE_CAM_MOVEMENT_TEXT "Stick/L/R" -#define FREE_CAM_VIEW_TEXT "C-stick" -#define MOVE_LINK_MOVEMENT_TEXT "Stick/C" -#define MOVE_LINK_ANGLE_TEXT "C-left/right" -#define PREVIOUS_TUNIC_COLOR GZPad::Y -#define PREVIOUS_TUNIC_COLOR_TEXT "Y" -#define NEXT_TUNIC_COLOR GZPad::X -#define NEXT_TUNIC_COLOR_TEXT "X" -#endif -#ifdef WII_PLATFORM -#define FREE_CAM_MOVEMENT_TEXT "Stick+DPad" -#define FREE_CAM_VIEW_TEXT "C+Stick" -#define MOVE_LINK_MOVEMENT_TEXT "Stick" -#define MOVE_LINK_ANGLE_TEXT "C+Stick" -#define PREVIOUS_TUNIC_COLOR GZPad::TWO -#define PREVIOUS_TUNIC_COLOR_TEXT "TWO" -#define NEXT_TUNIC_COLOR GZPad::ONE -#define NEXT_TUNIC_COLOR_TEXT "ONE" -#endif - -#define MAX_TUNIC_COLORS 7 - -const char l_descTemplates[TOOLS_COUNT][100] = { - "use %s to reload current area", - "use %s to pause, %s to frame advance", - "reduces bonk animation significantly", - "link's movement is much faster", - "use %s to warp to kakariko gorge", -#ifdef WII_PLATFORM - "use %s to warp to ordon bridge", -#endif - "show frame info when doing coro td", - "practice snowpeak universal map delay timing", - "show current inputs", - "show Link's position, angle, and speed", - "show Heap size info", - "link won't sink in sand", - "frame counter for chaining rolls", - "display A/B button mashing speeds", - "%s to set, %s to load", - "simulates turbo controller inputs", - "frame timer: %s to start/stop, %s to reset", - "loading zone timer: %s to reset", - "In-game time timer: %s to start/stop, %s to reset", - FREE_CAM_TEXT " to activate, " FREE_CAM_MOVEMENT_TEXT " to move, " FREE_CAM_VIEW_TEXT - " to view, Z to speed", - MOVE_LINK_TEXT " to activate. " MOVE_LINK_MOVEMENT_TEXT " to move, " MOVE_LINK_ANGLE_TEXT - " to change angle", - "changes link's tunic color. " NEXT_TUNIC_COLOR_TEXT "/" PREVIOUS_TUNIC_COLOR_TEXT - " to cycle through colors", -}; - -KEEP_FUNC ToolsMenu::ToolsMenu(Cursor& cursor, ToolsData& data) - : Menu(cursor), l_tunicCol_idx(data.l_tunicCol_idx), - lines{{"area reload", RELOAD_AREA_INDEX, "use " RELOAD_AREA_TEXT " to reload current area", - true, ACTIVE_FUNC(STNG_TOOLS_RELOAD_AREA)}, - {"frame advance", FRAME_ADVANCE_INDEX, "use " FRAME_ADVANCE_TEXT " to pause, " FRAME_PAUSE_TEXT " to frame advance", - true, ACTIVE_FUNC(STNG_TOOLS_FRAME_ADVANCE)}, - {"fast bonk recovery", FAST_BONK_INDEX, "reduces bonk animation significantly", true, - ACTIVE_FUNC(STNG_TOOLS_FAST_BONK)}, - {"fast movement", FAST_MOVEMENT_INDEX, "link's movement is much faster", true, - ACTIVE_FUNC(STNG_TOOLS_FAST_MOVEMENT)}, - {"gorge checker", GORGE_INDEX, "use " GORGE_VOID_TEXT " to warp to kakariko gorge", - true, ACTIVE_FUNC(STNG_TOOLS_GORGE)}, -#ifdef WII_PLATFORM - {"bit checker", BIT_INDEX, "use " BACK_IN_TIME_TEXT " to warp to ordon bridge", true, - ACTIVE_FUNC(STNG_TOOLS_BIT)}, -#endif - {"coro td checker", COROTD_INDEX, "show frame info when doing coro td", true, - ACTIVE_FUNC(STNG_TOOLS_COROTD)}, - {"umd checker", UMD_INDEX, "practice snowpeak universal map delay timing", true, - ACTIVE_FUNC(STNG_TOOLS_UMD)}, - {"input viewer", INPUT_VIEWER_INDEX, "show current inputs", true, - ACTIVE_FUNC(STNG_TOOLS_INPUT_VIEWER)}, - {"link debug info", LINK_DEBUG_INDEX, "show Link's position, angle, and speed", true, - ACTIVE_FUNC(STNG_TOOLS_LINK_DEBUG)}, - {"heap debug info", HEAP_DEBUG_INDEX, "show Heap size info", true, - ACTIVE_FUNC(STNG_TOOLS_HEAP_DEBUG)}, - {"no sinking in sand", SAND_INDEX, "link won't sink in sand", true, - ACTIVE_FUNC(STNG_TOOLS_SAND)}, - {"roll checker", ROLL_INDEX, "frame counter for chaining rolls", true, - ACTIVE_FUNC(STNG_TOOLS_ROLL)}, - {"mash checker", MASH_CHECKER_INDEX, "display A/B button mashing speeds", true, - ACTIVE_FUNC(STNG_TOOLS_MASH_CHECKER)}, - {"teleport", TELEPORT_INDEX, - STORE_POSITION_TEXT " to set, " LOAD_POSITION_TEXT " to load", true, - ACTIVE_FUNC(STNG_TOOLS_TELEPORT)}, - {"turbo mode", TURBO_MODE_INDEX, "simulates turbo controller inputs", true, - ACTIVE_FUNC(STNG_TOOLS_TURBO_MODE)}, - {"timer", TIMER_INDEX, - "frame timer: " TIMER_TOGGLE_TEXT " to start/stop, " TIMER_RESET_TEXT " to reset", - true, ACTIVE_FUNC(STNG_TOOLS_TIMER)}, - {"load timer", LOAD_TIMER_INDEX, "loading zone timer: " TIMER_RESET_TEXT " to reset", - true, ACTIVE_FUNC(STNG_TOOLS_LOAD_TIMER)}, - {"igt timer", IGT_TIMER_INDEX, - "In-game time timer: " TIMER_TOGGLE_TEXT " to start/stop, " TIMER_RESET_TEXT - " to reset", - true, ACTIVE_FUNC(STNG_TOOLS_IGT_TIMER)}, - {"free cam", FREE_CAM_INDEX, - FREE_CAM_TEXT " to activate, " FREE_CAM_MOVEMENT_TEXT " to move, " FREE_CAM_VIEW_TEXT - " to view, Z to speed", - true, ACTIVE_FUNC(STNG_TOOLS_FREE_CAM)}, - {"move link", MOVE_LINK_INDEX, - MOVE_LINK_TEXT " to activate. " MOVE_LINK_MOVEMENT_TEXT - " to move, " MOVE_LINK_ANGLE_TEXT " to change angle", - true, ACTIVE_FUNC(STNG_TOOLS_MOVE_LINK)}, - {"link tunic color:", TUNIC_COLOR_INDEX, - "changes link's tunic color. " NEXT_TUNIC_COLOR_TEXT "/" PREVIOUS_TUNIC_COLOR_TEXT - " to cycle through colors", - false, nullptr, MAX_TUNIC_COLORS}} { +KEEP_FUNC ToolsMenu::ToolsMenu(Cursor& cursor) + : Menu(cursor), + lines{{"checkers", CHECKERS_INDEX, "Various checker tools", false}, + {"controller", CONTROLLER_INDEX, "Controller related tools", false}, + {"link", LINK_INDEX, "Link related tools", false}, + {"scene", SCENE_INDEX, "Scene related tools", false}, + {"timers", TIMERS_INDEX, "Various timer tools", false}} { } ToolsMenu::~ToolsMenu() {} -GZSettingID l_mapping[] = { - STNG_TOOLS_RELOAD_AREA, STNG_TOOLS_FRAME_ADVANCE, STNG_TOOLS_FAST_BONK, - STNG_TOOLS_FAST_MOVEMENT, STNG_TOOLS_GORGE, -#ifdef WII_PLATFORM - STNG_TOOLS_BIT, -#endif - STNG_TOOLS_COROTD, STNG_TOOLS_UMD, STNG_TOOLS_INPUT_VIEWER, - STNG_TOOLS_LINK_DEBUG, STNG_TOOLS_HEAP_DEBUG, STNG_TOOLS_SAND, - STNG_TOOLS_ROLL, STNG_TOOLS_MASH_CHECKER, STNG_TOOLS_TELEPORT, - STNG_TOOLS_TURBO_MODE, STNG_TOOLS_TIMER, STNG_TOOLS_LOAD_TIMER, - STNG_TOOLS_IGT_TIMER, STNG_TOOLS_FREE_CAM, STNG_TOOLS_MOVE_LINK, -}; - -#define set_active(id, status) \ - ({ \ - auto* stng = GZStng_get(id); \ - if (stng) \ - *(bool*)stng->data = status; \ - }) - void ToolsMenu::draw() { - l_tunicCol_idx = g_tunic_color; - if (GZ_getButtonTrig(BACK_BUTTON)) { g_menuMgr->pop(); return; } - ListMember tunicCol_opt[MAX_TUNIC_COLORS] = {"green", "blue", "red", "orange", - "yellow", "white", "cycle"}; - - if (cursor.y == TUNIC_COLOR_INDEX) { - cursor.x = l_tunicCol_idx; - cursor.move(MAX_TUNIC_COLORS, MENU_LINE_NUM); - - if (GZ_getButtonRepeat(NEXT_TUNIC_COLOR)) { - l_tunicCol_idx++; - - if (l_tunicCol_idx >= MAX_TUNIC_COLORS) - l_tunicCol_idx = 0; - } - - if (GZ_getButtonRepeat(PREVIOUS_TUNIC_COLOR)) { - l_tunicCol_idx--; - - if (l_tunicCol_idx >= MAX_TUNIC_COLORS) - l_tunicCol_idx = MAX_TUNIC_COLORS - 1; - } - - g_tunic_color = l_tunicCol_idx; - } else { - cursor.move(0, MENU_LINE_NUM); - } - if (GZ_getButtonTrig(SELECTION_BUTTON)) { - GZSettingEntry* stng = nullptr; - if (cursor.y < TOOLS_COUNT && cursor.y != TUNIC_COLOR_INDEX) { - stng = GZStng_get(l_mapping[cursor.y]); - if (!stng) { - stng = new GZSettingEntry{l_mapping[cursor.y], sizeof(bool), new bool}; - g_settings.push_back(stng); - } + switch (cursor.y) { + case CHECKERS_INDEX: + g_menuMgr->push(MN_TOOLS_CHECKERS_INDEX); + return; + case CONTROLLER_INDEX: + g_menuMgr->push(MN_TOOLS_CONTROLLER_INDEX); + return; + case LINK_INDEX: + g_menuMgr->push(MN_TOOLS_LINK_INDEX); + return; + case SCENE_INDEX: + g_menuMgr->push(MN_TOOLS_SCENE_INDEX); + return; + case TIMERS_INDEX: + g_menuMgr->push(MN_TOOLS_TIMERS_INDEX); + return; } - if (stng) { - *(bool*)stng->data = !*(bool*)stng->data; - if (*(bool*)stng->data) { - switch (cursor.y) { - case TIMER_INDEX: - set_active(STNG_TOOLS_LOAD_TIMER, false); - set_active(STNG_TOOLS_IGT_TIMER, false); - break; - case LOAD_TIMER_INDEX: - set_active(STNG_TOOLS_TIMER, false); - set_active(STNG_TOOLS_IGT_TIMER, false); - break; - case IGT_TIMER_INDEX: - set_active(STNG_TOOLS_TIMER, false); - set_active(STNG_TOOLS_LOAD_TIMER, false); - break; - } - } - } - } - - char buf[100]; - switch (cursor.y) { - case RELOAD_AREA_INDEX: { - uint16_t combo = GZStng_getData(STNG_CMD_RELOAD_AREA, RELOAD_AREA_BUTTONS); - char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; - GZCmd_comboToStr(combo, comboStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); - delete[] comboStr; - break; - } - case FRAME_ADVANCE_INDEX: { - uint16_t comboPause = - GZStng_getData(STNG_CMD_FRAME_PAUSE, FRAME_PAUSE_BUTTONS); - char* comboPauseStr = new char[GZCmd_getComboLen(comboPause) + 1]; - GZCmd_comboToStr(comboPause, comboPauseStr); - uint16_t comboAdvance = - GZStng_getData(STNG_CMD_FRAME_ADVANCE, FRAME_ADVANCE_BUTTONS); - char* comboAdvanceStr = new char[GZCmd_getComboLen(comboAdvance) + 1]; - GZCmd_comboToStr(comboAdvance, comboAdvanceStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboPauseStr, comboAdvanceStr); - delete[] comboAdvanceStr; - delete[] comboPauseStr; - break; - } - case GORGE_INDEX: { - uint16_t combo = GZStng_getData(STNG_CMD_GORGE_VOID, GORGE_VOID_BUTTONS); - char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; - GZCmd_comboToStr(combo, comboStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); - delete[] comboStr; - break; - } -#ifdef WII_PLATFORM - case BIT_INDEX: { - uint16_t combo = GZStng_getData(STNG_CMD_BIT, BACK_IN_TIME_BUTTONS); - char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; - GZCmd_comboToStr(combo, comboStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); - delete[] comboStr; - break; - } -#endif - case TELEPORT_INDEX: { - uint16_t comboPause = - GZStng_getData(STNG_CMD_STORE_POSITION, STORE_POSITION_BUTTONS); - char* comboPauseStr = new char[GZCmd_getComboLen(comboPause) + 1]; - GZCmd_comboToStr(comboPause, comboPauseStr); - uint16_t comboAdvance = - GZStng_getData(STNG_CMD_LOAD_POSITION, LOAD_POSITION_BUTTONS); - char* comboAdvanceStr = new char[GZCmd_getComboLen(comboAdvance) + 1]; - GZCmd_comboToStr(comboAdvance, comboAdvanceStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboPauseStr, comboAdvanceStr); - delete[] comboAdvanceStr; - delete[] comboPauseStr; - break; - } - case IGT_TIMER_INDEX: // fallthrough - case TIMER_INDEX: { - uint16_t comboPause = - GZStng_getData(STNG_CMD_TIMER_TOGGLE, TIMER_TOGGLE_BUTTONS); - char* comboPauseStr = new char[GZCmd_getComboLen(comboPause) + 1]; - GZCmd_comboToStr(comboPause, comboPauseStr); - uint16_t comboAdvance = - GZStng_getData(STNG_CMD_TIMER_RESET, TIMER_RESET_BUTTONS); - char* comboAdvanceStr = new char[GZCmd_getComboLen(comboAdvance) + 1]; - GZCmd_comboToStr(comboAdvance, comboAdvanceStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboPauseStr, comboAdvanceStr); - delete[] comboAdvanceStr; - delete[] comboPauseStr; - break; - } - case LOAD_TIMER_INDEX: { - uint16_t combo = GZStng_getData(STNG_CMD_TIMER_RESET, TIMER_RESET_BUTTONS); - char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; - GZCmd_comboToStr(combo, comboStr); - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); - delete[] comboStr; - break; - } - default: { - snprintf(buf, sizeof(buf), l_descTemplates[cursor.y]); - break; - } } - strncpy(lines[cursor.y].description, buf, sizeof(lines[cursor.y].description)); - lines[TUNIC_COLOR_INDEX].printf(" <%s>", tunicCol_opt[l_tunicCol_idx].member); + cursor.move(0, MENU_LINE_NUM); GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); } diff --git a/modules/menus/menu_tools_checkers/CMakeLists.txt b/modules/menus/menu_tools_checkers/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_tools_checkers/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_tools_checkers/include/main.h b/modules/menus/menu_tools_checkers/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_tools_checkers/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_tools_checkers/include/tools_checkers_menu.h b/modules/menus/menu_tools_checkers/include/tools_checkers_menu.h new file mode 100644 index 00000000..71fe3f50 --- /dev/null +++ b/modules/menus/menu_tools_checkers/include/tools_checkers_menu.h @@ -0,0 +1,42 @@ +#pragma once + +#include "menus/menu.h" + +#ifdef GCN_PLATFORM +#define GORGE_VOID_TEXT "L+Z" +#endif + +#ifdef WII_PLATFORM +#define GORGE_VOID_TEXT "Z+C+A+1" +#define BACK_IN_TIME_TEXT "Z+C+A+2" +#endif + +enum CheckersIndex { + +#ifdef WII_PLATFORM + BIT_INDEX, +#endif + COROTD_INDEX, + ELEVATOR_ESCAPE_INDEX, + GORGE_INDEX, + LFC_INDEX, + MASH_CHECKER_INDEX, + ROLL_INDEX, + UMD_INDEX, + + CHECKERS_COUNT, +}; + +struct CheckersData { + Cursor cursor; +}; + +class CheckersMenu : public Menu { +public: + CheckersMenu(CheckersData&); + virtual ~CheckersMenu(); + virtual void draw(); + +private: + Line lines[CHECKERS_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_tools_checkers/src/main.cpp b/modules/menus/menu_tools_checkers/src/main.cpp new file mode 100644 index 00000000..17c3f6dc --- /dev/null +++ b/modules/menus/menu_tools_checkers/src/main.cpp @@ -0,0 +1,50 @@ +#include +#include "menus/menu_tools_checkers/include/tools_checkers_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +CheckersMenu* l_checkersMenu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new CheckersData); + } +} + +void onLoad() { + l_checkersMenu = new CheckersMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_checkersMenu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_checkersMenu; +} + +void onDelete() {} \ No newline at end of file diff --git a/modules/menus/menu_tools_checkers/src/tools_checkers_menu.cpp b/modules/menus/menu_tools_checkers/src/tools_checkers_menu.cpp new file mode 100644 index 00000000..b4255bb1 --- /dev/null +++ b/modules/menus/menu_tools_checkers/src/tools_checkers_menu.cpp @@ -0,0 +1,111 @@ +#include "menus/menu_tools_checkers/include/tools_checkers_menu.h" +#include "menus/utils/menu_mgr.h" + +const char l_descTemplates[CHECKERS_COUNT][100] = { +#ifdef WII_PLATFORM + "use %s to warp to ordon bridge", +#endif + "show frame info when doing coro td", + "show frame info when doing elevator escape", + "use %s to warp to kakariko gorge", + "ladder freezard cancel checker", + "display A/B button mashing speeds", + "frame counter for chaining rolls", + "practice snowpeak universal map delay timing", +}; + +KEEP_FUNC CheckersMenu::CheckersMenu(CheckersData& data) + : Menu(data.cursor), lines{ +#ifdef WII_PLATFORM + {"bit", BIT_INDEX, "use " BACK_IN_TIME_TEXT " to warp to ordon bridge", true, + ACTIVE_FUNC(STNG_TOOLS_BIT)}, +#endif + {"coro td", COROTD_INDEX, "show frame info when doing coro td", true, + ACTIVE_FUNC(STNG_TOOLS_COROTD)}, + {"elevator escape", ELEVATOR_ESCAPE_INDEX, "show frame info when doing elevator escape", + true, ACTIVE_FUNC(STNG_TOOLS_ELEVATOR_ESCAPE)}, + {"gorge void", GORGE_INDEX, "use " GORGE_VOID_TEXT " to warp to kakariko gorge", + true, ACTIVE_FUNC(STNG_TOOLS_GORGE)}, + {"ladder freezard cancel", LFC_INDEX, "ladder freezard cancel checker", + true, ACTIVE_FUNC(STNG_TOOLS_LFC)}, + {"a/b mash rate", MASH_CHECKER_INDEX, "display A/B button mashing speeds", true, + ACTIVE_FUNC(STNG_TOOLS_MASH_CHECKER)}, + {"rolling", ROLL_INDEX, "frame counter for chaining rolls", true, + ACTIVE_FUNC(STNG_TOOLS_ROLL)}, + {"universal map delay", UMD_INDEX, "practice snowpeak universal map delay timing", true, + ACTIVE_FUNC(STNG_TOOLS_UMD)}} { +} + +CheckersMenu::~CheckersMenu() {} + +GZSettingID l_mapping[] = { +#ifdef WII_PLATFORM + STNG_TOOLS_BIT, +#endif + STNG_TOOLS_COROTD, + STNG_TOOLS_ELEVATOR_ESCAPE, + STNG_TOOLS_GORGE, + STNG_TOOLS_LFC, + STNG_TOOLS_MASH_CHECKER, + STNG_TOOLS_ROLL, + STNG_TOOLS_UMD, +}; + +#define set_active(id, status) \ + ({ \ + auto* stng = GZStng_get(id); \ + if (stng) \ + *(bool*)stng->data = status; \ + }) + +void CheckersMenu::draw() { + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + cursor.move(0, MENU_LINE_NUM); + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + GZSettingEntry* stng = nullptr; + + stng = GZStng_get(l_mapping[cursor.y]); + + if (!stng) { + stng = new GZSettingEntry{l_mapping[cursor.y], sizeof(bool), new bool}; + g_settings.push_back(stng); + } + + if (stng) + *(bool*)stng->data = !*(bool*)stng->data; + } + + char buf[100]; + switch (cursor.y) { + case GORGE_INDEX: { + uint16_t combo = GZStng_getData(STNG_CMD_GORGE_VOID, GORGE_VOID_BUTTONS); + char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; + GZCmd_comboToStr(combo, comboStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); + delete[] comboStr; + break; + } +#ifdef WII_PLATFORM + case BIT_INDEX: { + uint16_t combo = GZStng_getData(STNG_CMD_BIT, BACK_IN_TIME_BUTTONS); + char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; + GZCmd_comboToStr(combo, comboStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); + delete[] comboStr; + break; + } +#endif + default: { + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y]); + break; + } + } + + strncpy(lines[cursor.y].description, buf, sizeof(lines[cursor.y].description)); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_tools_controller/CMakeLists.txt b/modules/menus/menu_tools_controller/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_tools_controller/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_tools_controller/include/main.h b/modules/menus/menu_tools_controller/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_tools_controller/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_tools_controller/include/tools_controller_menu.h b/modules/menus/menu_tools_controller/include/tools_controller_menu.h new file mode 100644 index 00000000..5ba687d8 --- /dev/null +++ b/modules/menus/menu_tools_controller/include/tools_controller_menu.h @@ -0,0 +1,21 @@ +#pragma once + +#include "menus/menu.h" +#include "tools.h" + +enum ToolsControllerIndex { + INPUT_VIEWER_INDEX, + TURBO_MODE_INDEX, + + TOOLS_CONTROLLER_COUNT +}; + +class ToolsControllerMenu : public Menu { +public: + ToolsControllerMenu(Cursor&); + virtual ~ToolsControllerMenu(); + virtual void draw(); + +private: + Line lines[TOOLS_CONTROLLER_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_tools_controller/src/main.cpp b/modules/menus/menu_tools_controller/src/main.cpp new file mode 100644 index 00000000..01087973 --- /dev/null +++ b/modules/menus/menu_tools_controller/src/main.cpp @@ -0,0 +1,50 @@ +#include +#include "menus/menu_tools_controller/include/tools_controller_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +ToolsControllerMenu* l_toolsControllerMenu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + l_toolsControllerMenu = new ToolsControllerMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_toolsControllerMenu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_toolsControllerMenu; +} + +void onDelete() {} diff --git a/modules/menus/menu_tools_controller/src/tools_controller_menu.cpp b/modules/menus/menu_tools_controller/src/tools_controller_menu.cpp new file mode 100644 index 00000000..2c74eeb8 --- /dev/null +++ b/modules/menus/menu_tools_controller/src/tools_controller_menu.cpp @@ -0,0 +1,38 @@ +#include "menus/menu_tools_controller/include/tools_controller_menu.h" +#include "menus/utils/menu_mgr.h" + +KEEP_FUNC ToolsControllerMenu::ToolsControllerMenu(Cursor& cursor) + : Menu(cursor), + lines{{"input viewer", INPUT_VIEWER_INDEX, "show current inputs", true, + ACTIVE_FUNC(STNG_TOOLS_INPUT_VIEWER)}, + {"turbo mode", TURBO_MODE_INDEX, "simulates turbo controller inputs", true, + ACTIVE_FUNC(STNG_TOOLS_TURBO_MODE)}} { +} + +ToolsControllerMenu::~ToolsControllerMenu() {} + +GZSettingID l_mapping[] = {STNG_TOOLS_INPUT_VIEWER, STNG_TOOLS_TURBO_MODE}; + +void ToolsControllerMenu::draw() { + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + GZSettingEntry* stng = nullptr; + + stng = GZStng_get(l_mapping[cursor.y]); + + if (!stng) { + stng = new GZSettingEntry{l_mapping[cursor.y], sizeof(bool), new bool}; + g_settings.push_back(stng); + } + + if (stng) + *(bool*)stng->data = !*(bool*)stng->data; + } + + cursor.move(0, MENU_LINE_NUM); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_tools_link/CMakeLists.txt b/modules/menus/menu_tools_link/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_tools_link/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_tools_link/include/main.h b/modules/menus/menu_tools_link/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_tools_link/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_tools_link/include/tools_link_menu.h b/modules/menus/menu_tools_link/include/tools_link_menu.h new file mode 100644 index 00000000..e2ac8e6c --- /dev/null +++ b/modules/menus/menu_tools_link/include/tools_link_menu.h @@ -0,0 +1,45 @@ +#pragma once + +#include "menus/menu.h" +#include "tools.h" + +#ifdef GCN_PLATFORM +#define STORE_POSITION_TEXT "D-PAD up + R" +#define LOAD_POSITION_TEXT "D-PAD down + R" +#define MOVE_LINK_TEXT "L+R+Y" +#endif + +#ifdef WII_PLATFORM +#define STORE_POSITION_TEXT "Z+C+1" +#define LOAD_POSITION_TEXT "Z+C+2" +#define MOVE_LINK_TEXT "Z+C+B+Plus" +#endif + +enum ToolsLinkIndex { + FAST_BONK_INDEX, + FAST_MOVEMENT_INDEX, + LINK_DEBUG_INDEX, + LINK_STAGE_INFO_INDEX, + SAND_INDEX, + TELEPORT_INDEX, + MOVE_LINK_INDEX, + TUNIC_COLOR_INDEX, + + TOOLS_LINK_COUNT +}; + +struct ToolsLinkData { + uint8_t l_tunicCol_idx; +}; + +class ToolsLinkMenu : public Menu { +public: + ToolsLinkMenu(Cursor&, ToolsLinkData&); + virtual ~ToolsLinkMenu(); + virtual void draw(); + +private: + uint8_t& l_tunicCol_idx; + + Line lines[TOOLS_LINK_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_tools_link/src/main.cpp b/modules/menus/menu_tools_link/src/main.cpp new file mode 100644 index 00000000..1d410189 --- /dev/null +++ b/modules/menus/menu_tools_link/src/main.cpp @@ -0,0 +1,56 @@ +#include +#include "menus/menu_tools_link/include/tools_link_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +ToolsLinkMenu* l_toolsLinkMenu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + g_menuMgr->setPersistentData(new ToolsLinkData()); + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + l_toolsLinkMenu = new ToolsLinkMenu(*g_menuMgr->getPermanentData(), + *g_menuMgr->getPersistentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_toolsLinkMenu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_toolsLinkMenu; +} + +void onDelete() { + auto data = g_menuMgr->getPersistentData(); + delete data; + g_menuMgr->setPersistentData(nullptr); +} diff --git a/modules/menus/menu_tools_link/src/tools_link_menu.cpp b/modules/menus/menu_tools_link/src/tools_link_menu.cpp new file mode 100644 index 00000000..0e4a9147 --- /dev/null +++ b/modules/menus/menu_tools_link/src/tools_link_menu.cpp @@ -0,0 +1,163 @@ +#include "menus/menu_tools_link/include/tools_link_menu.h" +#include +#include "commands.h" +#include "global_data.h" +#include "timer.h" +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "gz_flags.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +#ifdef GCN_PLATFORM +#define MOVE_LINK_MOVEMENT_TEXT "Stick/C" +#define MOVE_LINK_ANGLE_TEXT "C-left/right" +#define PREVIOUS_TUNIC_COLOR GZPad::Y +#define PREVIOUS_TUNIC_COLOR_TEXT "Y" +#define NEXT_TUNIC_COLOR GZPad::X +#define NEXT_TUNIC_COLOR_TEXT "X" +#endif +#ifdef WII_PLATFORM +#define MOVE_LINK_MOVEMENT_TEXT "Stick" +#define MOVE_LINK_ANGLE_TEXT "C+Stick" +#define PREVIOUS_TUNIC_COLOR GZPad::TWO +#define PREVIOUS_TUNIC_COLOR_TEXT "TWO" +#define NEXT_TUNIC_COLOR GZPad::ONE +#define NEXT_TUNIC_COLOR_TEXT "ONE" +#endif + +#define MAX_TUNIC_COLORS 7 + +const char l_descTemplates[TOOLS_LINK_COUNT][100] = { + "reduces bonk animation significantly", + "link's movement is much faster", + "show Link's position, angle, and speed", + "show Link's current stage info", + "link won't sink in sand", + "%s to set, %s to load", + MOVE_LINK_TEXT " to activate. " MOVE_LINK_MOVEMENT_TEXT " to move, " MOVE_LINK_ANGLE_TEXT + " to change angle", + "changes link's tunic color. " NEXT_TUNIC_COLOR_TEXT "/" PREVIOUS_TUNIC_COLOR_TEXT + " to cycle through colors", +}; + +KEEP_FUNC ToolsLinkMenu::ToolsLinkMenu(Cursor& cursor, ToolsLinkData& data) + : Menu(cursor), l_tunicCol_idx(data.l_tunicCol_idx), + lines{{"fast bonk recovery", FAST_BONK_INDEX, "reduces bonk animation significantly", true, + ACTIVE_FUNC(STNG_TOOLS_FAST_BONK)}, + {"fast movement", FAST_MOVEMENT_INDEX, "link's movement is much faster", true, + ACTIVE_FUNC(STNG_TOOLS_FAST_MOVEMENT)}, + {"link debug info", LINK_DEBUG_INDEX, "show Link's position, angle, and speed", true, + ACTIVE_FUNC(STNG_TOOLS_LINK_DEBUG)}, + {"stage info", LINK_STAGE_INFO_INDEX, "show Link's current stage info", true, + ACTIVE_FUNC(STNG_TOOLS_STAGE_INFO)}, + {"no sinking in sand", SAND_INDEX, "link won't sink in sand", true, + ACTIVE_FUNC(STNG_TOOLS_SAND)}, + {"teleport", TELEPORT_INDEX, + STORE_POSITION_TEXT " to set, " LOAD_POSITION_TEXT " to load", true, + ACTIVE_FUNC(STNG_TOOLS_TELEPORT)}, + {"move link", MOVE_LINK_INDEX, + MOVE_LINK_TEXT " to activate. " MOVE_LINK_MOVEMENT_TEXT + " to move, " MOVE_LINK_ANGLE_TEXT " to change angle", + true, ACTIVE_FUNC(STNG_TOOLS_MOVE_LINK)}, + {"link tunic color:", TUNIC_COLOR_INDEX, + "changes link's tunic color. " NEXT_TUNIC_COLOR_TEXT "/" PREVIOUS_TUNIC_COLOR_TEXT + " to cycle through colors", + false, nullptr, MAX_TUNIC_COLORS}} { +} + +ToolsLinkMenu::~ToolsLinkMenu() {} + +GZSettingID l_mapping[] = { + STNG_TOOLS_FAST_BONK, + STNG_TOOLS_FAST_MOVEMENT, + STNG_TOOLS_LINK_DEBUG, + STNG_TOOLS_STAGE_INFO, + STNG_TOOLS_SAND, + STNG_TOOLS_TELEPORT, + STNG_TOOLS_MOVE_LINK, +}; + +#define set_active(id, status) \ + ({ \ + auto* stng = GZStng_get(id); \ + if (stng) \ + *(bool*)stng->data = status; \ + }) + +void ToolsLinkMenu::draw() { + l_tunicCol_idx = g_tunic_color; + + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + ListMember tunicCol_opt[MAX_TUNIC_COLORS] = {"green", "blue", "red", "orange", + "yellow", "white", "cycle"}; + + if (cursor.y == TUNIC_COLOR_INDEX) { + cursor.x = l_tunicCol_idx; + cursor.move(MAX_TUNIC_COLORS, MENU_LINE_NUM); + + if (GZ_getButtonRepeat(NEXT_TUNIC_COLOR)) { + l_tunicCol_idx++; + + if (l_tunicCol_idx >= MAX_TUNIC_COLORS) + l_tunicCol_idx = 0; + } + + if (GZ_getButtonRepeat(PREVIOUS_TUNIC_COLOR)) { + l_tunicCol_idx--; + + if (l_tunicCol_idx >= MAX_TUNIC_COLORS) + l_tunicCol_idx = MAX_TUNIC_COLORS - 1; + } + + g_tunic_color = l_tunicCol_idx; + } else { + cursor.move(0, MENU_LINE_NUM); + } + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + + GZSettingEntry* stng = nullptr; + + stng = GZStng_get(l_mapping[cursor.y]); + + if (!stng) { + stng = new GZSettingEntry{l_mapping[cursor.y], sizeof(bool), new bool}; + g_settings.push_back(stng); + } + + if (stng) + *(bool*)stng->data = !*(bool*)stng->data; + } + + char buf[100]; + + switch (cursor.y) { + case TELEPORT_INDEX: { + uint16_t comboPause = + GZStng_getData(STNG_CMD_STORE_POSITION, STORE_POSITION_BUTTONS); + char* comboPauseStr = new char[GZCmd_getComboLen(comboPause) + 1]; + GZCmd_comboToStr(comboPause, comboPauseStr); + uint16_t comboAdvance = + GZStng_getData(STNG_CMD_LOAD_POSITION, LOAD_POSITION_BUTTONS); + char* comboAdvanceStr = new char[GZCmd_getComboLen(comboAdvance) + 1]; + GZCmd_comboToStr(comboAdvance, comboAdvanceStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboPauseStr, comboAdvanceStr); + delete[] comboAdvanceStr; + delete[] comboPauseStr; + break; + } + default: { + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y]); + break; + } + } + + strncpy(lines[cursor.y].description, buf, sizeof(lines[cursor.y].description)); + + lines[TUNIC_COLOR_INDEX].printf(" <%s>", tunicCol_opt[l_tunicCol_idx].member); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_tools_scene/CMakeLists.txt b/modules/menus/menu_tools_scene/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_tools_scene/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_tools_scene/include/main.h b/modules/menus/menu_tools_scene/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_tools_scene/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_tools_scene/include/tools_scene_menu.h b/modules/menus/menu_tools_scene/include/tools_scene_menu.h new file mode 100644 index 00000000..1891b87b --- /dev/null +++ b/modules/menus/menu_tools_scene/include/tools_scene_menu.h @@ -0,0 +1,38 @@ +#pragma once + +#include "menus/menu.h" +#include "tools.h" + +#ifdef GCN_PLATFORM +#define FRAME_ADVANCE_TEXT "R + D-Pad Up" +#define FRAME_PAUSE_TEXT "R" +#define RELOAD_AREA_TEXT "L+R+A+Start" +#define FREE_CAM_TEXT "Z+B+A" +#endif + +#ifdef WII_PLATFORM +#define FRAME_ADVANCE_TEXT "Z+C+Plus+Minus" +#define FRAME_PAUSE_TEXT "2" +#define RELOAD_AREA_TEXT "Z+C+B+2" +#define FREE_CAM_TEXT "Z+C+B+Minus" +#endif + +enum ToolsSceneIndex { + AREA_RELOAD_INDEX, + FRAME_ADVANCE_INDEX, + FREE_CAM_INDEX, + HEAP_DEBUG_INDEX, + + // Entry used as a counter + TOOLS_SCENE_COUNT +}; + +class ToolsSceneMenu : public Menu { +public: + ToolsSceneMenu(Cursor&); + virtual ~ToolsSceneMenu(); + virtual void draw(); + +private: + Line lines[TOOLS_SCENE_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_tools_scene/src/main.cpp b/modules/menus/menu_tools_scene/src/main.cpp new file mode 100644 index 00000000..1308021e --- /dev/null +++ b/modules/menus/menu_tools_scene/src/main.cpp @@ -0,0 +1,50 @@ +#include +#include "menus/menu_tools_scene/include/tools_scene_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +ToolsSceneMenu* l_toolsSceneMenu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + l_toolsSceneMenu = new ToolsSceneMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_toolsSceneMenu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_toolsSceneMenu; +} + +void onDelete() {} diff --git a/modules/menus/menu_tools_scene/src/tools_scene_menu.cpp b/modules/menus/menu_tools_scene/src/tools_scene_menu.cpp new file mode 100644 index 00000000..9cac7a34 --- /dev/null +++ b/modules/menus/menu_tools_scene/src/tools_scene_menu.cpp @@ -0,0 +1,112 @@ +#include "menus/menu_tools_scene/include/tools_scene_menu.h" +#include +#include "commands.h" +#include "global_data.h" +#include "timer.h" +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "gz_flags.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +#ifdef GCN_PLATFORM +#define FREE_CAM_MOVEMENT_TEXT "Stick/L/R" +#define FREE_CAM_VIEW_TEXT "C-stick" +#endif +#ifdef WII_PLATFORM +#define FREE_CAM_MOVEMENT_TEXT "Stick+DPad" +#define FREE_CAM_VIEW_TEXT "C+Stick" +#endif + +#define MAX_TUNIC_COLORS 7 + +const char l_descTemplates[TOOLS_SCENE_COUNT][100] = { + "use %s to reload current area", + "use %s to pause, %s to frame advance", + FREE_CAM_TEXT " to activate, " FREE_CAM_MOVEMENT_TEXT " to move, " FREE_CAM_VIEW_TEXT " to view, Z to speed", + "show Heap size info", +}; + +KEEP_FUNC ToolsSceneMenu::ToolsSceneMenu(Cursor& cursor) + : Menu(cursor), + lines{{"area reload", AREA_RELOAD_INDEX, "use " RELOAD_AREA_TEXT " to reload current area", + true, ACTIVE_FUNC(STNG_TOOLS_RELOAD_AREA)}, + {"frame advance", FRAME_ADVANCE_INDEX, "use " FRAME_ADVANCE_TEXT " to pause, " FRAME_PAUSE_TEXT " to frame advance", + true, ACTIVE_FUNC(STNG_TOOLS_FRAME_ADVANCE)}, + {"free cam", FREE_CAM_INDEX, + FREE_CAM_TEXT " to activate, " FREE_CAM_MOVEMENT_TEXT " to move, " FREE_CAM_VIEW_TEXT + " to view, Z to speed", + true, ACTIVE_FUNC(STNG_TOOLS_FREE_CAM)}, + {"heap debug info", HEAP_DEBUG_INDEX, "show Heap size info", true, + ACTIVE_FUNC(STNG_TOOLS_HEAP_DEBUG)}} {} + +ToolsSceneMenu::~ToolsSceneMenu() {} + +GZSettingID l_mapping[] = { + STNG_TOOLS_RELOAD_AREA, + STNG_TOOLS_FRAME_ADVANCE, + STNG_TOOLS_FREE_CAM, + STNG_TOOLS_HEAP_DEBUG, +}; + +#define set_active(id, status) \ + ({ \ + auto* stng = GZStng_get(id); \ + if (stng) \ + *(bool*)stng->data = status; \ + }) + +void ToolsSceneMenu::draw() { + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + GZSettingEntry* stng = nullptr; + + stng = GZStng_get(l_mapping[cursor.y]); + if (!stng) { + stng = new GZSettingEntry{l_mapping[cursor.y], sizeof(bool), new bool}; + g_settings.push_back(stng); + } + + if (stng) + *(bool*)stng->data = !*(bool*)stng->data; + } + + char buf[100]; + + switch (cursor.y) { + case AREA_RELOAD_INDEX: { + uint16_t combo = GZStng_getData(STNG_CMD_RELOAD_AREA, RELOAD_AREA_BUTTONS); + char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; + GZCmd_comboToStr(combo, comboStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); + delete[] comboStr; + break; + } + case FRAME_ADVANCE_INDEX: { + uint16_t comboPause = + GZStng_getData(STNG_CMD_FRAME_PAUSE, FRAME_PAUSE_BUTTONS); + char* comboPauseStr = new char[GZCmd_getComboLen(comboPause) + 1]; + GZCmd_comboToStr(comboPause, comboPauseStr); + uint16_t comboAdvance = + GZStng_getData(STNG_CMD_FRAME_ADVANCE, FRAME_ADVANCE_BUTTONS); + char* comboAdvanceStr = new char[GZCmd_getComboLen(comboAdvance) + 1]; + GZCmd_comboToStr(comboAdvance, comboAdvanceStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboPauseStr, comboAdvanceStr); + delete[] comboAdvanceStr; + delete[] comboPauseStr; + break; + } + default: { + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y]); + break; + } + } + + strncpy(lines[cursor.y].description, buf, sizeof(lines[cursor.y].description)); + + cursor.move(0, MENU_LINE_NUM); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_tools_timers/CMakeLists.txt b/modules/menus/menu_tools_timers/CMakeLists.txt new file mode 100644 index 00000000..24e35f3c --- /dev/null +++ b/modules/menus/menu_tools_timers/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE asms CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.s") +list(APPEND srcs ${asms}) +get_filename_component(rel_name ${CMAKE_CURRENT_SOURCE_DIR} NAME) +tpgz_add_module(${rel_name} "${srcs}" "${CMAKE_CURRENT_SOURCE_DIR}/include") \ No newline at end of file diff --git a/modules/menus/menu_tools_timers/include/main.h b/modules/menus/menu_tools_timers/include/main.h new file mode 100644 index 00000000..1935f7ae --- /dev/null +++ b/modules/menus/menu_tools_timers/include/main.h @@ -0,0 +1,6 @@ +#pragma once + +namespace tpgz::modules { +void main(); +void exit(); +} // namespace tpgz::modules \ No newline at end of file diff --git a/modules/menus/menu_tools_timers/include/tools_timers_menu.h b/modules/menus/menu_tools_timers/include/tools_timers_menu.h new file mode 100644 index 00000000..6111f900 --- /dev/null +++ b/modules/menus/menu_tools_timers/include/tools_timers_menu.h @@ -0,0 +1,33 @@ +#pragma once + +#include "menus/menu.h" +#include "tools.h" + +#ifdef GCN_PLATFORM +#define TIMER_TOGGLE_TEXT "Z+A" +#define TIMER_RESET_TEXT "Z+B" +#endif + +#ifdef WII_PLATFORM +#define TIMER_TOGGLE_TEXT "Z+C+A+B" +#define TIMER_RESET_TEXT "Z+C+B+1" +#endif + +enum ToolsTimersIndex { + TIMER_INDEX, + LOAD_TIMER_INDEX, + IGT_TIMER_INDEX, + + TOOLS_TIMERS_COUNT +}; + +class ToolsTimersMenu : public Menu { +public: + ToolsTimersMenu(Cursor&); + virtual ~ToolsTimersMenu(); + virtual void draw(); + +private: + + Line lines[TOOLS_TIMERS_COUNT]; +}; \ No newline at end of file diff --git a/modules/menus/menu_tools_timers/src/main.cpp b/modules/menus/menu_tools_timers/src/main.cpp new file mode 100644 index 00000000..d171cd8e --- /dev/null +++ b/modules/menus/menu_tools_timers/src/main.cpp @@ -0,0 +1,50 @@ +#include +#include "menus/menu_tools_timers/include/tools_timers_menu.h" +#include "events/draw_listener.h" +#include "menus/utils/menu_mgr.h" +#include "utils/draw.h" + +void onCreate(); +void onLoad(); +void onDraw(); +void onUnload(); +void onDelete(); + +ToolsTimersMenu* l_toolsTimersMenu; + +namespace tpgz::modules { +void main() { + g_menuMgr->setCreateHook(onCreate); + g_menuMgr->setLoadHook(onLoad); + g_menuMgr->setUnloadHook(onUnload); + g_menuMgr->setDeleteHook(onDelete); +} +void exit() { + g_menuMgr->setCreateHook(nullptr); + g_menuMgr->setLoadHook(nullptr); + g_menuMgr->setUnloadHook(nullptr); + g_menuMgr->setDeleteHook(nullptr); +} +} // namespace tpgz::modules + +void onCreate() { + if (!g_menuMgr->getPermanentData()) { + g_menuMgr->setPermanentData(new Cursor); + } +} + +void onLoad() { + l_toolsTimersMenu = new ToolsTimersMenu(*g_menuMgr->getPermanentData()); + g_drawListener->addListener(onDraw); +} + +void onDraw() { + l_toolsTimersMenu->draw(); +} + +void onUnload() { + g_drawListener->removeListener(onDraw); + delete l_toolsTimersMenu; +} + +void onDelete() {} diff --git a/modules/menus/menu_tools_timers/src/tools_timers_menu.cpp b/modules/menus/menu_tools_timers/src/tools_timers_menu.cpp new file mode 100644 index 00000000..c990e7b0 --- /dev/null +++ b/modules/menus/menu_tools_timers/src/tools_timers_menu.cpp @@ -0,0 +1,118 @@ +#include "menus/menu_tools_timers/include/tools_timers_menu.h" +#include +#include "commands.h" +#include "global_data.h" +#include "timer.h" +#include "libtp_c/include/d/com/d_com_inf_game.h" +#include "gz_flags.h" +#include "rels/include/defines.h" +#include "menus/utils/menu_mgr.h" + +const char l_descTemplates[TOOLS_COUNT][100] = { + "frame timer: %s to start/stop, %s to reset", + "loading zone timer: %s to reset", + "In-game time timer: %s to start/stop, %s to reset", +}; + +KEEP_FUNC ToolsTimersMenu::ToolsTimersMenu(Cursor& cursor) + : Menu(cursor), + lines{{"timer", TIMER_INDEX, + "frame timer: " TIMER_TOGGLE_TEXT " to start/stop, " TIMER_RESET_TEXT " to reset", + true, ACTIVE_FUNC(STNG_TOOLS_TIMER)}, + {"load timer", LOAD_TIMER_INDEX, "loading zone timer: " TIMER_RESET_TEXT " to reset", + true, ACTIVE_FUNC(STNG_TOOLS_LOAD_TIMER)}, + {"igt timer", IGT_TIMER_INDEX, + "In-game time timer: " TIMER_TOGGLE_TEXT " to start/stop, " TIMER_RESET_TEXT + " to reset", + true, ACTIVE_FUNC(STNG_TOOLS_IGT_TIMER)}} { +} + +ToolsTimersMenu::~ToolsTimersMenu() {} + +GZSettingID l_mapping[] = { + STNG_TOOLS_TIMER, + STNG_TOOLS_LOAD_TIMER, + STNG_TOOLS_IGT_TIMER +}; + +#define set_active(id, status) \ + ({ \ + auto* stng = GZStng_get(id); \ + if (stng) \ + *(bool*)stng->data = status; \ + }) + +void ToolsTimersMenu::draw() { + if (GZ_getButtonTrig(BACK_BUTTON)) { + g_menuMgr->pop(); + return; + } + + if (GZ_getButtonTrig(SELECTION_BUTTON)) { + GZSettingEntry* stng = nullptr; + + stng = GZStng_get(l_mapping[cursor.y]); + + if (!stng) { + stng = new GZSettingEntry{l_mapping[cursor.y], sizeof(bool), new bool}; + g_settings.push_back(stng); + } + + if (stng) { + *(bool*)stng->data = !*(bool*)stng->data; + + if (*(bool*)stng->data) { + switch (cursor.y) { + case TIMER_INDEX: + set_active(STNG_TOOLS_LOAD_TIMER, false); + set_active(STNG_TOOLS_IGT_TIMER, false); + break; + case LOAD_TIMER_INDEX: + set_active(STNG_TOOLS_TIMER, false); + set_active(STNG_TOOLS_IGT_TIMER, false); + break; + case IGT_TIMER_INDEX: + set_active(STNG_TOOLS_TIMER, false); + set_active(STNG_TOOLS_LOAD_TIMER, false); + break; + } + } + } + } + + char buf[100]; + switch (cursor.y) { + case IGT_TIMER_INDEX: // fallthrough + case TIMER_INDEX: { + uint16_t comboPause = + GZStng_getData(STNG_CMD_TIMER_TOGGLE, TIMER_TOGGLE_BUTTONS); + char* comboPauseStr = new char[GZCmd_getComboLen(comboPause) + 1]; + GZCmd_comboToStr(comboPause, comboPauseStr); + uint16_t comboAdvance = + GZStng_getData(STNG_CMD_TIMER_RESET, TIMER_RESET_BUTTONS); + char* comboAdvanceStr = new char[GZCmd_getComboLen(comboAdvance) + 1]; + GZCmd_comboToStr(comboAdvance, comboAdvanceStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboPauseStr, comboAdvanceStr); + delete[] comboAdvanceStr; + delete[] comboPauseStr; + break; + } + case LOAD_TIMER_INDEX: { + uint16_t combo = GZStng_getData(STNG_CMD_TIMER_RESET, TIMER_RESET_BUTTONS); + char* comboStr = new char[GZCmd_getComboLen(combo) + 1]; + GZCmd_comboToStr(combo, comboStr); + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y], comboStr); + delete[] comboStr; + break; + } + default: { + snprintf(buf, sizeof(buf), l_descTemplates[cursor.y]); + break; + } + } + + strncpy(lines[cursor.y].description, buf, sizeof(lines[cursor.y].description)); + + cursor.move(0, MENU_LINE_NUM); + GZ_drawMenuLines(lines, cursor.y, MENU_LINE_NUM); +} diff --git a/modules/menus/menu_trigger_view/src/trigger_view_menu.cpp b/modules/menus/menu_trigger_view/src/trigger_view_menu.cpp index f9a36d51..ae7a6c43 100644 --- a/modules/menus/menu_trigger_view/src/trigger_view_menu.cpp +++ b/modules/menus/menu_trigger_view/src/trigger_view_menu.cpp @@ -31,6 +31,8 @@ KEEP_FUNC TriggerViewMenu::TriggerViewMenu(Cursor& cursor) [](){return g_triggerViewFlags[VIEW_TRANSFORM_DISTS].active;}}, {"twilight gates", VIEW_TW_GATES, "view twilight gate triggers", true, [](){return g_triggerViewFlags[VIEW_TW_GATES].active;}}, + {"leever ranges", VIEW_LEEVER_RANGE, "view leever ranges", true, + [](){return g_triggerViewFlags[VIEW_LEEVER_RANGE].active;}}, {"opacity:", TRIGGER_VIEW_MAX, "the opacity of drawn geometry"}, } {} diff --git a/modules/menus/menu_warp/src/main.cpp b/modules/menus/menu_warp/src/main.cpp index 32298206..81807c5e 100644 --- a/modules/menus/menu_warp/src/main.cpp +++ b/modules/menus/menu_warp/src/main.cpp @@ -3,7 +3,6 @@ #include "events/draw_listener.h" #include "menus/utils/menu_mgr.h" #include "utils/draw.h" -#include "rels/include/cxx.h" void onCreate(); void onLoad(); @@ -48,4 +47,4 @@ void onUnload() { delete l_menu; } -void onDelete() {} +void onDelete() {} \ No newline at end of file diff --git a/res/map/GCN_NTSCJ.lst b/res/map/GCN_NTSCJ.lst index e8dd3859..92bbf971 100644 --- a/res/map/GCN_NTSCJ.lst +++ b/res/map/GCN_NTSCJ.lst @@ -402,6 +402,11 @@ 8044b265:sPauseTimer +// Z2AudioLib +8044b4a8:mAudioMgrPtr__10Z2AudioMgr + +8044aea4:dStage_roomControl_c__mStayNo + CC008000:wgPipe 800056c0:version_check__Fv diff --git a/res/map/GCN_NTSCU.lst b/res/map/GCN_NTSCU.lst index 6c789d32..bfc63220 100644 --- a/res/map/GCN_NTSCU.lst +++ b/res/map/GCN_NTSCU.lst @@ -185,6 +185,11 @@ 80451125:sPauseTimer +// Z2AudioLib +80451368:mAudioMgrPtr__10Z2AudioMgr + +80450d64:dStage_roomControl_c__mStayNo + CC008000:wgPipe 800056c0:version_check__Fv diff --git a/res/map/GCN_PAL.lst b/res/map/GCN_PAL.lst index cd0f4a3e..9c4e0c46 100644 --- a/res/map/GCN_PAL.lst +++ b/res/map/GCN_PAL.lst @@ -409,6 +409,11 @@ 804530ED:sPauseTimer +// Z2AudioLib +80453330:mAudioMgrPtr__10Z2AudioMgr + +80452d24:dStage_roomControl_c__mStayNo + CC008000:wgPipe 800056c0:version_check__Fv diff --git a/res/map/WII_NTSCJ.lst b/res/map/WII_NTSCJ.lst index dde11f4f..0373fd9e 100644 --- a/res/map/WII_NTSCJ.lst +++ b/res/map/WII_NTSCJ.lst @@ -258,6 +258,11 @@ 8051eca5:sPauseTimer +// Z2AudioLib +8051eef8:Z2SeMgr__mAudioMgrPtr + +8051e81C:dStage_roomControl_c__mStayNo + CC008000:wgPipe 80006920:SECallback_int__int_ diff --git a/res/map/WII_NTSCU_10.lst b/res/map/WII_NTSCU_10.lst index 6f2db620..99011d30 100644 --- a/res/map/WII_NTSCU_10.lst +++ b/res/map/WII_NTSCU_10.lst @@ -524,6 +524,11 @@ 8053ae1d:sPauseTimer +// Z2AudioLib +8053b068:Z2SeMgr__mAudioMgrPtr + +8053a994:dStage_roomControl_c__mStayNo + CC008000:wgPipe 80006920:SECallback_int__int_ diff --git a/res/map/WII_NTSCU_12.lst b/res/map/WII_NTSCU_12.lst index efe47798..0d43cd11 100644 --- a/res/map/WII_NTSCU_12.lst +++ b/res/map/WII_NTSCU_12.lst @@ -327,6 +327,11 @@ 80520e35:sPauseTimer +// Z2AudioLib +80521088:Z2SeMgr__mAudioMgrPtr + +8052099C:dStage_roomControl_c__mStayNo + CC008000:wgPipe 80006920:SECallback_int__int_ diff --git a/res/map/WII_PAL.lst b/res/map/WII_PAL.lst index 8f5c2b71..9e41ef07 100644 --- a/res/map/WII_PAL.lst +++ b/res/map/WII_PAL.lst @@ -257,6 +257,11 @@ 805215f5:sPauseTimer +// Z2AudioLib +80521848:Z2SeMgr__mAudioMgrPtr + +8052115C:dStage_roomControl_c__mStayNo + CC008000:wgPipe 80006920:SECallback_int__int_ diff --git a/res/save_files/any.bin b/res/save_files/any.bin index 23f969894154360f934c7676b60ee500dd40558f..bdb33e5566999db554c7f3a9533d8250e2ec2e70 100644 GIT binary patch delta 53 zcmV-50LuUHAMhV60{{W?L{8kzLs)%x#f`5(L{MGpL+;IH#e|Q@L{8t>L!{5X#f_&z L0000Au`I+7q`Mg2 delta 11 Scmew$|3QA@1f7jKNB99E3k4hi diff --git a/res/save_files/any_bite.bin b/res/save_files/any_bite.bin index 976069cf196e81a3a0fcfacf6257be0f7a8e834d..bb255d098fd36ccb425e210fba9ad81ef2a06520 100644 GIT binary patch delta 624 zcmaDL^FT&}nSt?>i{G8I&JnfcM|;;hxCF$$c7As@?PyE?2^YWnH=LK8-*>ckxzj`q z3ketVhYMVorOq5PdHBrv#kDBsOKtm)nen(ytgx7Dz$i2EjEMp+B@952QJS7VS&>m6 zs8)7z4x@*!#h`}|iCKD&hFlSaJf<5qoRY2mHVA*V!1$@58Y`aB{30gdIncTo| z%xvS7$r5Y^?9O5g46eHyCQg)PE-om7Izevo9ySvRr~dD=T(13n?`k<=H<*3Cfx*?V zz;QAMyT!yw(vzdvA)&H{9b)w#2+fk5m|Fn%OCrcyN|O(8B!Hc1!wFH_gGTFcL7fk! z&p>GzZm>9WT4LVh_3VaVGxk8lCiCz>^u$2vH4vJmDlxAFZhuBHlqt^yWI~LPo&1Iu JqSb~^1prWN+1LO8 delta 459 zcmaDL^FT&`nSt?>i{G8I&JnfcM|;;hOuoP&Gf~F`Ox1uW>4~RJK@=-PMrnHf#EAwV z&g5)HupkRVYHsnwiHegSFbaUhxtYM?lcS;ZYAF31N_#Uy`s9cnl; zLw3^Si(E>RH?V*Vm0?NENy(gCzzDHfn-yY0HH2ni$gD~Px!?mUSn*^THXV+`Vhjwf zyBiolPGVswE-0Ef(Gcvs-E0ue-0VD3ULm7yvzuLSOZjO2+E YjX;(&Gh`$K%>c6}zvhMLwdPX=0G0EhCjbBd diff --git a/res/save_files/hundo.bin b/res/save_files/hundo.bin index 8cf7668ac7ccf9edd494cfa70bb4729c1dacdb41..19dc998d8a6c0aa2ba59a0e2378590df250a780c 100644 GIT binary patch delta 1143 zcmYLITSyd97~cE3I@;`7y5;DuvJ{n2t6LTrl)JMfY^D$0z}lvDRyRjy+g+_J&<7u) z3_JY=5k2@|6k%wT7#UriwFg5J2_K}(O9V+pL`4)e=bX_7hVT5}`LEwOXR7S&26${K zH8;vaDv6K_)mlp4%=-H$A1ddrR|RSheDr^M5mmZ-p9gCG^XT{{n6hwa!iNDkJwc<` zl7uIgVmPm75h{RD{RXnl7^qvlh65B$qSO!rw|+fw3K@OQ@zkTKbXM;5?LG3#@%DWc zX=c@j?PtHAfy+ix59WBSw4@Fb6kZ4pZuTwJ>LaBLWj#QQ26ROs!U;5y2KSCTx z77we}4b=rM_3577PDhoS@9(L+O%xK+iLfY%TD%hkt4qz}R)0G%R#M0uJ&|a`v*8My z{g{vv6H!5u!pT^J*d)gh%7t1pi!N|*%Iv|>sE;u;ABJtYCD3PPi^hkyAm!RfW^izN zVEoRzeC5X7hTu@|wAx@1al?sSELNm79FZkSjHF_6D?&@5%gQ>ddVgFAy!!K*-+X?Q ztS7s4yf?lLZdj=>zpXxJ&=qV6DmyPI<^8^(QrD(z?5reCJ2I%#)eNfIyoQxFVv{~? zqyd{*x$cSzhTXhF~>|u z9V#;}i}1$g#d3j^Me9D$kYR{zOvV#h&LS94b5hwbjb0XmJ6pX(9E7rovqfdYhnqG3 z0+^>XROWcRM%~_$biw^wfnPcu-&r@BE_v{k&#ZaKpT0DcE*PodGi(?6&gx<;c4Ig%<&1r`z=!4p3y0F{xclgcEIX zaY4dm@Y}waBi1312_1AohtD18I-2!#)?2|3uAU9_d&AF;l>g*!Rqw}AMq;TXY_-v6 zGVdTI4Urg{#OfA8PcC7MQK1!fWl+USmlDpDObM!%c(y{Apd4_^Xwwo!Q-{!+S~yx` zM3xtiq&|;0npG}b20|WA!x5`|!GchJH)ah@5iQJxmoi(dvg#Yi#uja=5ayh;aOrZ- EKXq2F-2eap delta 934 zcmZ8fJxmi}7=Dkt>(61^gQ5t9D8!2cF_dDb8f!v{LldKki6#wc&Qc1Na+kJXq%jUg zqcMt~?}(e5ji_<6CJrE&pn)zfbYO5}lEJ|)-hJ=K;w{hjJ@51WJm0V4OsZ9Z2Lclc@lOd=D4OL0W0W+ao(*1ytu+s9zWj6d za^E5FS{bi_yIwv;NAVUmdtxpv13wSrRn-keu%;SJ=SLX|CC0c4iyq$42ewtIW*uG{ zMBB%U;VU0mI`Gj@3BMRl;GVz{jq|+Ym_~KCiSxnyY&K^uH^z@3RLVeW29LP7$F(fzoovU$0t5+txe7(G1NC0a3tBv zqMa|_UUYg%y{HGt(1jowN(8C+p1_fip-XUMcr)_pZR#(;s7~jcQT;T(usaAwzDC?a&{H9Osk* zGv)#&{nVjBqzaX)S!PX2&YYljb%AX{6_(9~s`yt5JCBI j@uMs5oqXuhGR)@0wz)^lc8UFT&Pl)6!hV{d(Qo_%rCkdW diff --git a/res/save_files/hundo/aeralfos_skip.bin b/res/save_files/hundo/aeralfos_skip.bin index 4428ca7108bd7ce6a245140919cedbef705e0ef4..3c947626b17cda10edc7748c33e9cc7d1ce85859 100644 GIT binary patch delta 623 zcmeAX?Gfc=2x2g2U z&iXfroglGBkXS27Ov7gKfy95z|Nkc%Ffja21iI25!ZiYMjX_*Zi{f^Om>o!mJ%|A7 zKyV+`b1@uBe9WVOOg!Of_@DTc{{u4d48(noN<8QXiai7ok3hs@5b*>=JOvTYK*Vzp zae$-$f8s%oR|Scn07yK<@q<6{Fvlx~L|}k>G9(`1(Bn@$%JHNiQ3xdc?Xks?#P5$S g&L#c;v3`PBzd)?tAl4rc>o14}_K6-yhd$V20A3d6+yDRo delta 623 zcmeAX?Gfc=2x4$xU z3=GYaPc!*3PMR#mtjH*~*@oGX(V3Bf;RLgeQ7a3O0Rne`SU}j2fq{wPzrg{91q=*| z3=AurSwM0i0FnpU4+I8I27YYE7#TJnU3|d`SRuz&>-!+@p0Yne+4E z$sOYLK*oEwWX=z+ab-pzy3yw2fkyq$V2zD7pH~3!8k^7O85lmFw*d(@+I%sD1~~LKzSrNCUmgumEVD0>lyD?*N?uaYTH;=VLJsJTJQl eEeHcL&b!4txa7*ba2tq5b;K7Ppz93?IRXImHoL9> diff --git a/res/save_files/hundo/ag.bin b/res/save_files/hundo/ag.bin index 2905cb766748e5a2d29f5694201a89ca3cc55f4d..4bc1b658ae523efd20a9cd9a47407180fbca0631 100644 GIT binary patch delta 188 zcmeAX?Gfc-Fknz&%nwO0FPO+B&%eUA*5T#8f86X03{D+Nb0%8o@?MwT>7vuXaLi-N z;+7*5i%K}VPiiqZFd8rkO?p|5_!}6Q1SZRI=raaP_T!kvIBjwvbIfK}PIpEC`pq}h delta 188 zcmeAX?Gfc-Fknz&%neCU?4HOa&!6+nz^q!WnVX$~!Kp{bbE1VV?;{-s7o7%%W1ehr z&7UV0m2ftn)M9X8G-MK<_nAJfswWD`2z;;|IgzYTR2r14%C0F|NJ?K*d z`Uy6Drz@HmLz!l2G%!mrFbGaQ&E&`UXtETuBBRh|8)ipFXF&#r6U;nDy)2AigBk7s zDFI=i!yJLU|>+>U|0e515hpoq)aIhL>f66oZ~yjD7g6mizpK#571`O&9B+i z8HMKkaoE%#;E;cSK}evghp(*4Zn6r80+)gSP*Vm2gF?V$KaN?9(~SVo4QC9jr+stu+udHbgNF8tdI0Bvy%rFxZOV zpM;3z$-;u8gM-?!F+LFrNr@9|422e&AVZCbpjMynltDbn`{nz-bI*O}4k<&56E_SvATYw#B66fQA z6H&0Bg6X*{$d9<%5Ik%l|^MIM}SBf~NuP4|* zQzNw0*VMP33#Nz~Wxjo}n{?>phv(sEh2DO4$u#CkOM8KbYQYp|BkF$1%l2f$Qk2CQ zFe4oa4$6k0S};E6E@Lqh^niw!fERcblc`5VeH;?R;ORizo@dUiCMzb!jXx{dFP6j1 zHC~InOFJXCG`P_B+J3Lstu}Ix)$0Vu4P*JF8s}qdM!hXvO|dx*@JV31i*zT|ns^eZ z=^_m_Dh3QMMz^Kb#8b*1P&P=JFRW_fkEH7&0O-P4xOLM8$sQ-$6zX}rnVq7ERziK^ zpIu5>F(FDe&sLOeXuU!$!79SR#}NWx_IHg-{`Ad{`GB`D#))X@v>C z;SitrD#T8p17ealJH!+(CxwgJ-%FuMv0e&&78|5~kormL7pWSl-=yj4bzW@lh<+Hm3QM2qQ+j1w=2 zadn^7a&TbOX5f>Y_+d9|hk>?q!{iN&yX6@`KmmyBobv$SAbghS`x(P>_M) z1T&9NFAJjpNDo8Hm-=kzSjoynNVP@xJ+q(EDjdl4XUU5JR9?d*B% zAMCzgPDui&UmZG(Xba31In7KSJ1*9BRt;)SM!^LJLc!NDXbc>VzcYy}5 zc??@yoVQ!u6m=uijZ;??;8FaPg+M=mz%qc^A1x=XAoXYi6F*%IEouqhi(kD=h8;8= zd4Ca#ISheU&bz<3CXN5c`K;Al5H1NOAx5|&TodAis4^>XTe%4${R`Yto}IX>+yJJL t?kQd;?klk%x=0_*N^K9HOzi}pO)I@ndZqM6>7CLCrSF$`Y2qV%egU%?(Aoe1 diff --git a/res/save_files/hundo/boss_bug.bin b/res/save_files/hundo/boss_bug.bin index 980611b335e9895bba92313f55621181a78ad3b4..c7df28836e0721b0b1cd05899c23f0049ecc37c3 100644 GIT binary patch delta 401 zcmX|3%_{_P7=GTJVQ0VCUAuPDsPWa7!j4w0Bok{B`3OZWRu(5GA}J;%5wiXK0lTHx zi_{(|PWGs9Q4TmN2Pcvmzo9(!KJW8>JkM@$H|Rk%WJE`k9d{7Kv`4X@^tZepI?L0M zXsFOy`g(m_h$cQ4SHAO!$&n#VYX+rP>-xJ)j<`89Yjrbh_L^p&Lli)vfXnUwUH%gQ z0oa-YJgr=-DkqK-sGxf22&LZ?5}1>+MK!Pcdk^f4G$QswOIl9*xY?dI&I2V=)e$8g z_;d0MF4hW9=&9TalSs9=(y@NK&?&GgYO+(-8^cCz{?ax~mA)OFKSRoND^JqJks~Kc zAX%|?c+5A7^R}|V-$D1c!Oay-e8J65i`!X^J;v*Ptiga6ZK~1HpBbV$afYJ}E}&2anon AzyJUM delta 697 zcmeAX?Gfc=kYW&ESR9g|JAr{&XdF ztpLOzzz1Y8GB7v*4SXxZ$T*pcaUm;XdgpT0$r~6A85t%&Vk|RUYxwutq3Rcl-f^2g7uBMixaCh64@^^_PmBm?kSS>a#O$7O-GwQroT28ISCH3|ij z{WvBta~x2a{E(x(emh8tV+EMp0VY?1$(>+w6`0%wCRc;W-C(jH;(GxnN5csfAx@47 zEKQCIAW<-Au(?ywXtNB&1_mtygInevtz*fYpAS#&5C_ul-I6&!xW<(kf#^n?#q|k| z`b!po6gJu{^#EcYo6qMN7(So3fru^2XtY_R(&+i9qQRB30EkO~>>Q~20HFE^poKCZ zK9J4;QVW3Q9e_AuWev~?5J$uZd_ETQ;IfO*f-oR`-Yw?AC0FK!+uUNz9$W&t0@)E~ KfQDWm59V;eqvokO_iFMwXXrasd{dE0d zr4LKobRWJ+{xGp9mXUel1u@3%iO-L)##cCoubI4oakm@;2q*w?-SY|tp8LT#5`G%QA@%HD9gaOT)?8C$)bsofkBF+f!TtAL16M}CO^hUlcksy85uU) rFkfTgVVdJ$(7@op-@w2mFjY{Tx-$X*)x}Fu#{Qv*|L;*?uoNwCyHv1-XvokO_dBizQw9w`KQE}$5 z(uXB(dfFz*Arp&Y85t*D5Mykf`1}ZKs@>Mg<&!rs?v~?Y00IRdu6zD~!TbMrcg7Y? z6@~*5h4r6`gP0~OGHOZq0A(2%w+mP>G#NB8GB8LnG%#B*Ff>d)&E&^8X|fcvA|uOY r8|G^)Jj`<(3>p|5_!}6Q7$(bc=ra~f_T!kvcw%xPbIfK}PIpECMj1KV diff --git a/res/save_files/hundo/cits_1.bin b/res/save_files/hundo/cits_1.bin index 8c5bf47aaa64d46513a3d0bf38f9f9068f7eace9..9111a4b1eba4d732256ffc1ea8f912513ef6daac 100644 GIT binary patch delta 611 zcmeAX?Gfc-2x4$xUDMojW!<-H0pl_YizXnyaI^V*nB?E!0`FJ4M?!j=EDgfZfW!~xY6LsSpdW( zK=v7k+~*BI^*ewT%7FMl8t7ey1wiu@AddKc2j~QdBjN)-AB%b5dD%s1K^Ty6-Yw?A VC0FK!+dwp`BfjVWU2j0h5df$&xmW-I delta 305 zcmeAX?Gfc-2x4$xUOaIy&QO+4vl-0Tbt&L#{D49q|b1jo9O*uts8aG?HU{pZg?%np+s88sxGb3m#r*H5tNJ6+Ml7|JwDqk&n1 zfkANcX(m6$N0X(P6&Zy#+b}yaItwx|oM7fL>SbXB8_aMANC^lVGB7YP{8w;bSir!b z$ic7z>R_N;3|N^Wh%|CC@MAm1$hi3cOAxcrygv?`8U!5j4=@M`RQ2$cRoP8e;ZWdG w5CE#oU|>)PnC!M4c4%Yz`Kq1t}eTv9|t! zVoe`ix>Rt`!9ncc5X5I~Vw){@`@VC}<=kQau-}a= z62Qgl&LfbFg1hx_Px9r3&CYi_n}AeP@hlv$>hqlI;mrHF`spv{%ZpbFnSJhs%P5To zCerEFQqMr)PQU$_(|@Q?FFJHbSNeteMic`rJQ{>>+^bb^&RD-D2HV@9Dc_Xuy;)mA zO^Wb=)J?wB_2F6kX}NDeT(XS^^U{9cfm*g@#He~u3W>dwaVaTM0+^MKMu+UC;QDGF z@9qz>% diff --git a/res/save_files/hundo/cits_poe_cycle.bin b/res/save_files/hundo/cits_poe_cycle.bin index 3585b4adde2d80e34ab19ba44c8edcd65dbdbb7b..f6102f8dd133014433f5ab7e60ca1e1ede19281a 100644 GIT binary patch delta 639 zcmeAX?Gfc-2xf3#Uf%1&!2;s9VR<6YDhZgfK*wo zpJ3B>x}u3Olxdbm1G5AJgW%-TOn!`yCQC6ZG74?BVRmG67Gz*J!OUaS%fbjYnBfkP z5)cMDjEUjDf&;?>1_nhAh80jh0Oex9$`V1Ok&}TR+c8GL%?DV5n1$y3aoE%#;E;cS zK}evghp(*4Zn6r80+)gSP-O-KgF?V$KaN?9(KH}S)6 z)+cJqe@~vgfpNDyAILNX21eGp=MNaX|9_8XY|&IVItF){o%IKZ%gfkBahVTCgbNDc%*@(c`#Ai~JWz>n=1qrm0^EJ4gd^Zz((Y7lV9 zKfoZwP_>4ytU`XW3Woxhq5uO2Lk7^r1(W?aW-*?aT*w^5&3NSi1A_)L1Ea`;$%(9! NCKs?aPYz&I2LLQCTE_qY diff --git a/res/save_files/hundo/coo.bin b/res/save_files/hundo/coo.bin index 9cce9e34cf2afed73adde546d39edc28eb6c2bf1..08a187d7ba4961eb0421ce27c0f08c2f22e7e241 100644 GIT binary patch delta 443 zcmeAX?Gg23NM!I~U=B%;TgV{5we>&C|NsB>J+-#}XJBFg0!9!40W17M=Fhms#LdpY z;M~6RAOkZ*`1slSyrYMNJ>2e|{Bl=pqMw-fkr}$OE?-ir-BaTyZvW;M9~@w4WWvBO zvARf%Q<@8;O96;evB17*-hYQp4+I?YA8-f>)b&hO;85W*5CCe+ l00uz7WIv8sjMF9;GRJadZ)0H4U}j+CQ`sEIY0b#U000J7a4Y}- delta 443 zcmeAX?Gg23NM!I~U;+2;$~-H zaBdI%!N3X;K7NL$?daY!AKb16oPHQL(N9d=<)M3u>jagv$L6m%^efCQJ~+V8$drL) zVs()io2VQ}mjV!{$nnd`$#F3-a&C|NsB>J+-#}XJBGL1uOi*uUKDZ;$~-HaBkmu zkbxN@ef(^F-qAzC9&UF}ez_|)(N9eL$P8UsmoF*R?y2z;w|{es4-POiGGSntSRKO1 zKkl7f7!H5U0rT%gM=cF)(s7PG)9Y!OHm|s)Kj(2FBg;AQeC*jI4FfA24|T ze;&`+!l}Y=p#Ee1=g&dR4wD@jH6)#LK#DBaPq67bUE#zS$}~%(fmwoqL2&YECO^hU zlcksy8HG06Fgr5V3oRu7#O%VtFZ21WD@={`5s#g6Z8McHteU>85mP|9ZDTe zEU<5y_upaD0|AHp2OL5Ibv=Cb|NlQ=U|>DK{r3m|mC1S>Vq69SKm#*?!4xprk7E|& aw8@3cv0T~P7#K8|85sFgHV1IpFaiLHuYTtM delta 485 zcmeAX?Gg23NM!I~hzdzy(`FFl+WMd6|NsB`o;q9qGcYj#0V9ZjfZV|Kv04wDx!D;Q zoZCZxFt9>|kDuXbJG%GG2e+#Mrys^m^b-?zdFY^vH2?w{R(r74-POiGG$Z3bAjRL}k3_x%GH#oqsfPq1gfnkL+3&=na z0Le2bC4xvJCxdhR#~42_7$|_a5G64T46Y44A3*{P->VP*`EPuK_dgdCL;ZmdKoLd; zpgb~|?9V7J$mjqRsuG&R%^;z=umQ;4tirm3kx9T}@;$Z~CML$oHteU>Sr}7z9ZDTe zEU<5y|KDNL0|AHp2OL5Sb!+(Q|Nno$z`%Nd`|l6_3zPLY#JCIv7&sU*fFW5h*^gru bkvI+AsnDTD|NsB>J+-#}XJBFg0!9!40W18Xqr2|j;bv!G za9-2Ez`zUchJ7#O)3Co?myVC5}eIqAjZ4UD_xK`MYs7+LF{ zKVb0w|2&?tg;Ry$K>f%1&!2;s9VR<6YDhZgfD~D-pJ3B>y26Pulxdbm1G5AJgW%-T zOn!`yCQC6ZG74?BVRmG!7i3^K!OUaS%L4V$9iT=5VM7L>H~%X*Ff3qTP~>1(0reA5 zE(VAhloCOtk(0qW{$q?E84MIaTow=kl6O78^AXJcUVZq_KjR;N|8p@h)F1p06k~+w z2g`zq$^MMuf{YG8c9qZ^ZUza}g$)c0TANi^cQ7&u|CoG_Er#j;|H(G&rzIH}Q+ORp z9ZoE;Z<_btVbcQvhx`W|LIQO?lNC5rxC{hd7l6P delta 471 zcmeAX?Gg23NM!I~)Coyo(`FFl+xnm7|NsB`o;q9qGcYj#0V9ZjfZV|CT0gk{akDcp zIIn47U|@v^A3wv>c69HV4{lcjPCtyB=qD!b^3XlSb%M&-WAj%W`W5CD9~@w4WXixY zu{wm2apDCT@f0>uIgnljAWo6vmy?s@VqoNEoy^R*f|c`>+5Nc58yI)X^Ffp_verF+ zz~KG=dpu){rV7J>`j7RWi-VXPCOa}}NIK_$6j|<{VAFTH!ih1IX_i6*vjhV}^W@V^ zevFeQOED`lify)Gc4VvvdgKJNj!`QM)JJ!K8U=(68GzpWZ*YKN0Rw{~1H%eu7Lb7; z0Fq}=N(7NcP6p@rk1>8=Fi-$-AxdHx7+f28K7s@ozE>aq^WXRe?|&{PhWY~^fFg_x zKzU>^*`HBdkkJ7sR3$Wrn?XW#VFNG#Hmk7iU}O@on0${dhN+2hvJLxbNfyQwUWZbL z6ASE{=Kput^gzHN{{e>(L*1Im3LGk2h5`&63>iSJ1(W?aW-*?aT*w^D)gQpXpux<* MDDq%)0H+Nj0MaFREC2ui diff --git a/res/save_files/hundo/coo_30.bin b/res/save_files/hundo/coo_30.bin index 37ac36b69b934c07f6f4027c355bc88ddc4f1338..5c78ebb36298e8ede798c58ed393bb2338e63b77 100644 GIT binary patch delta 489 zcmeAX?Gg23NM!I~Vhu@D|NsB>J+-#}XJBFg0!9!40W19CX3yDvgPWa! z!Ff#s0|PTe`1slSyrYMNJ>2e|{Bl=pqMw-fkr}$OE?-ir-BaTyZvW;M9~@w4WWvBO zu{wm&f8qrh@o-LFE|6XYAWo6vmy?s@VqoNEoXpI)f>mJq?wqHSH!$v&2dMxmVPvg) z{(!;z|MPgp7ETq01N9&4KYtElc9`tQs3GZ`15#wUeu7Qk=?W*tP^MWL4a^b@41$wS zGx;$-nk>bv$SAbghS`y^UXX#|1T&9OFALO1cYqoNgbf*h-u$oNz_5UUL6L)D1=LSK zxfmd3P)Y=mMotFj_>VDuWH3+waalkFNZ$1T&qpx(d-dTz|BQe9{m;e3P=D}0P>d0x zA1n(dCi^pr3o<$Y*;PVwxEUl=7d9|3Xm3_w-NDEt{A2PxwkQ^RV8~ClVLz?Tz?j18 zQ0j1Efqm1w{|=iT2sq?F;1Ck1>*1^a|Nj941M30qzd!h|P1fTO<1!Ec8kqqMsDR0S f9J3gwO)g}P<;vd1z@Wj*z{sbvIgrzuk&yuaTC##U delta 489 zcmeAX?Gg23NM!I~d=Qerrp+M8xAi~E|NsB>J$1JJXJBFg0!9!40l9%Y=Gj-+ac69HV4{lcjPCtyB=qD!b^3XlSb%M&-WAj%W`W5CD9~@w4WXixY zu{wm2apDCT@dP$eIgnljAWo6vmy?s@VqoNEoy^R*f|XnJY>DjT4UD_x`5;OdS?iuZ zVDSF`J)W^eQ-$F`{m1&x#X(FClN}i~B%O0WiY)g}u<1Kp;lvoqG)tj@S%QI~dGcu{ zKgLOurI-~N#Wve8J2KV-J#vCs$EcMB>Z3bAjRL}k3_x%GH#oqsfPq1gfnkL+3&=na z0Le2bC4xvJCxdhR#~42_7$|_a5G64T46Y44A3*{P->VP*`EPuK_dgdCL;ZmdKoLd; zpgb~|?9V7J$mjqRsuG&R%^;z=umKnVn^jnMFfs{POuok!#iGF9#5mc8{j@p@V+yZB zsl$l{_D%EuJ8XI&;E?}-Lx`bn4PX8L{||rxe}Mb%5B@8Y^*F@13^= diff --git a/res/save_files/hundo/coo_40.bin b/res/save_files/hundo/coo_40.bin index 6272d91552ca9873b042f433a42f2f0c4b2e1bfd..060ca3fe39e0d232c1fccf23bd76a7ce22a994b9 100644 GIT binary patch delta 596 zcmeAX?Gg23NMvwh4h%_D|NsB>J+-#}XJBGL1uOiL(chJ7#O)3K|%}+{QUndC-XASbv&LJk?0Pi#=GDFImhp+zs{|5{VtOvON{@}kpi-AKRk&9u+T=p!SoQ2}3=A5~42*m#KuIuolh|SK=YL`Y1LM@h7NAJt NRL=T0n{zqk83DHIoUH%= delta 595 zcmY*WT}TvB6h3EW)}7sLcgA9KTVi&$3^fF&j7o}{GK$Eil!P9Nc4&oK`qK~u!{}2cL4ejt8-V|lemx&wn0q;Y>+t8$#l?bS z)I{$GZyfrT?LX2rZO-g=dRnmCncT#A=)BpO?i3{1?^Bs&6-iN)FkmF$Qvi}gcakVR z2t#?s)pjrj$#cvhkQCRZ#QYMKaRXmGI7WY*!=Z*+C|b)_x!l2#1K3c1{*qtQGJALL zZMW|rc5>sY%_jhEGR+l)eeW)l58T%EnF{l-lOaDyl6;-GR@VLPY+?rQkdv4oOMEj^ zQ7aftqFw1wc{_~&@3}SG=L8n`_{dCNw+-0ksH(#RKpMy+A%O;&7B*L}Hm`iA;U2Jx z7Ar(J!z6FfP-hRL{YeZ`Z#UQgIcv|-E=K)@D(Wnvi&wrn>ORSXr-jylg%}~Ps(QuD zah|elp6J2Z6SO9je0(n-0L(`9UcEH2fAAsIpLw!=)nf|{jK(ZVJ%B(l@@qwUmjqGn z3d5+Q0e4!!VKxRd(EQK2z|MS)7~(jVNH>4BF5eGO&lERmELnkT4r{B_v;oJ=A;y?v Mq3dqpawvtr0b_)lLjV8( diff --git a/res/save_files/hundo/corotd.bin b/res/save_files/hundo/corotd.bin index ff1ac3258b9bd992ceb5f610f85ca880358e0bdf..58b3641d2c2b9a880a548cc02758e3c66e7ffd8a 100644 GIT binary patch delta 557 zcmY+9ODKd<7>2*||Hd5CWQJxIBF1GgQe$RZnuW|1QOczTS>Z$0%0fx`l?hQAr`cJM z#ByqE)wu60*jeEplq{^=`JHbN&f@8P-_!e@^DQ_RoEFFj2P}8?2T!AxqVQ&GUg1E& zl&S(W3|_#`Invg*4tdDaJX5;1J>Jw8?(q420JgO91^_aga1`%_!zI&jb4!n`rEh#S z@+LYFRvAF_q)`qv3!mC6##pt9Z|D)fh_krck&PuTYop`mG1s>U6)0wAVnYBSnJ9`V zW?s%#?Jm&D6p>WAMSsRw;$EFsgqsy9=k1B*%r2JgfEYI(-5S`TC&Gkbu3#&of)^{$ zk!a%6 delta 583 zcmeAX?Gfc-kY*5JWC}^p_L;~fADlE{LvGqmZ)SD|2B+;_+ze1~wEqCZq2$0HuI~jH zPOWx}4-POiums9MSQZQnETRl<%bqv6Fgq|Dd~EfMSs5&2Xvo08Jh3L2*_nZ9;w2Ym z0S5NTADQANmou8Mvi&`Boq6&G#toV>e2GdLJZ>d0%M^f&y5|oVJmj~tGqPx^FdT4T zsQ*;##57rvQH$3BD9gaOS-^s!NzG~U0j4lP0R{#phG{@i1CSI0Lj%x`lbb&_+YX&ha!_g!Dc@WF2?%pAnhD0 zz~l}vxe`q71e2@4q zh;Yl?qjfBq^Yh`!9pXUxy<0Np2iLeVBM{wav$#H?QGdw-kitfrr5-@+WAphu1HWcJ%)sDWHa7w$eY|1m zmSY?adhYGYi}zN!#Rmr%8W}P$@VmtO`WcuwF)$d5G91->dC+B#ip9fYlfp zFaS-GW0+VU#Kl# v4qGTAp9sTLj{o0v8W;o^B3c(8bbpH zW@ZLChKcn-j7$?RxiAYbuuuNT6gRn?(S(&(O6H5|+voo@2sxTaIV5t98?8G!#kx`4+0VvDBxLLr0p-IhY@&TqWK>-E^CWdK1 zQ3H?^149GQj+2`|Fn2RC@&L8SF@5#hJclimk#7paRF41Ob-pkNFhsR7PyWxY&G=xl zHisgULcwM~4lc&}?I7(OE5PIqFu4*;?gW#oz~nA4xf)FF29pI5-wQZ78cwJPadJ#x zX>wEmiGo3c&7F!yn`Iz2BZzRz+@p0Yne+4E$sOWA`n_8+=LgrgG9wV(XtTIJp;3Ry z0+7N+o24E=>|^u!JOjh$^EME%MH!7Yi&PptA5}ECauxt_36PxwRUZIU9|5#b2E+%_ z89-_Q(7Xc>N35&?IsxK{_<+yHVjf&}5n2!iq|dv>Jh$uq&7@Rg-I6KiogOPb+Vk>9&SuF+! zMgs=EiSPHYHneV^B{z8k<8FBd5KsW(y5|oVy#GHBXKdkAVK@+1SpTUwh}mJXBBQ2+ z4^Wzcak+p+Lz9IQBLkBZM+3731B1Zi(@cJhk0whoD>4dgwqbT~4`*!ARAD#}S6Kh4IEcw%vLd6V zgbz@ffpNQl1w)g86C(qY6hi~E1p`CFwP93<~a^V4Ga$X2N;AHCd+Z?GZswt~f z=CNgQ%aMsiv5d?UFNkq=pVVS-U^HOjpZIp|5_!}6Q1SZRI=raaP_T!kvIBjwvbIfK}PIpEC{JS?$ delta 196 zcmeAX?Gfc=Fk(<+oF0-OHH$%jb0V({f6g~+&8atBx!D;QoO*OTCtB$8KGI=u(P>~f z=E)Y<{CQ$gEFh{F0$#X(Gy6&bZ8e1NhHjN1h)7@7>47#SF(7#f%@7#JERpJwu7oHSXA wS&>m}vkmh#79Qp~4h9Vj4*U%aObnCdIP@6{Ci`*BVmvXqkU3_vE2ldn00wV2m;e9( diff --git a/res/save_files/hundo/dot_skip.bin b/res/save_files/hundo/dot_skip.bin index cf75e14713af3f2a699512f6e35f5097ade06b07..dd025237daaaec9fb830c95b351a56585556644a 100644 GIT binary patch delta 603 zcmY+A-AhzK7{=c@vum^N#yPtpsEDrBg)XS;8hI`jJqlh~5fVfo(fb#)B0&(er%>#} zkY!jeBBGnf%g9S_)~^VnF6$<6+q1eLU2G+_eophcP&zQbd7fwHov)etnR*KbA#U_t zy&3aCn7>sjV>O!dm%ilNv!8>f0EU*X*JSL@;E(NJ!{<}*u6b`Qle!%|c=9pkI4l-u z31`a9?5sS^Uie>)J-)jw->}72pcd8rUMUYFs&tJ_2F^k5#(4VY#3*-U8|!GesHcIw zwrux-J;cCh5-zs^EcwCcJX2E!Q%?(8bBA!ESukCYO}vdo7Lt6B^@IW5^HAcck5!j_ z)q_>n17LuPI`C58MZi4&w}(%ne`=xEzixz#=T5=}=T;6PmTVE-?25cBp0PPO&d20% zpf3F#AIA3;y1=qHsdvC^KxdCAyHD8^W$j6s!$YnV#sL)SlLx9cLAIA{hdnV}&HAZg zfN)gt-xg|N8NE>SIMT(Ct8l|CAIB!y(n;iJb@q)=Ab5l#VS}(qC=qgER^gJ^N})wp zSQZr%SHu>e{zSl4(PiSAD5fw%_P2MzUd2D2{Ru0cRy(A2N$ru^Cv`yT=p|iL(oH!0 E3$Gd8n*aa+ delta 304 zcmeAX?GY7VaAYuHatuijV`kvz*z})eqJSiS&JW!qpQRRZvokO_9lrQ-qJ=K^hbL^V zCIt+~WMdR37L;=TX+Epv;J|3ZE+;qfgAAj}WM0O3tP88zud7bpz_?qUj{yi2fVl4Y z0|xK^-@_SOG*uW5#1+&7}_UImSR?96x(dW?8s=y$iQ%dS;wfA1;_w_J3y=;Y{XTUI!;faE}+WpW~G>ti^x!0* zt=J*9h2o*EGqXEs5)AZ`7Ssp(=Dqj5_vX#p*|&%y zgpT2{<8OeFEvfV1A=^eJJ?Y!i+urd&(|{8bT{8+^W{>BWbb7dFs{P^O)sv$`1Mx&0 z2uLX&1GF7jUYx_y^tUfP*{3%@y>gHvfR~s+q4(_Vi zx2z~>Ul^Cde*2VdpKj1VBH<>#Tdz|!=OPZ68V9zC9G1lyr)F3nJeyPmv-o400n;=a zK{O5tA}F5OFg&PtthM5vs)?JbBL4a5A)i2p(-Y}3uT*w2TVYZ-A>!=h9M|20!Yi=; zm&fi#?>*}TJnwS`nFWH4!meno)xOTGJi{wiRL^|LDhd!tx*?r$6sD&>PBc6er&7ASeONgGJQUa8@W9|KPZbSu9Ddu3EG!y~?`MF990r zg1^*73qUW`Yc8vE)@q|#=P#?9g>gr>R}U~47`Z`6gzRE1jQ=Du4fHZ)u=syj1W?{{ zbunZDZI-va1u(|=Xbr~T8SxVS9+(7OHo(WQj4m~9ovcGIWHw`a^0u%U+x+c&{05@z BlGgwL literal 0 HcmV?d00001 diff --git a/res/save_files/hundo/early_platform.bin b/res/save_files/hundo/early_platform.bin index 236c3290b0ba9b33dddf085bc7005ac2e195536a..dd39aebbf0468a8a26f126e003793c4a22a0e650 100644 GIT binary patch delta 381 zcmeAX?Gfc-h+%MGU=B%;TR4$RUUh|^?<`H(Y;JZ22w;X#$IsU19X%xM;db}rm%CyU z{ls}6JWF$(u56nH!Fj@{@J&aH!$v&2WwYgU}UX( z{(!;z|MPgp7ETq01N9&4KYtElc9`tQs3GZ`15#zVeu7Qk=?W*tP^MWL4a^b@41$wS zGx;$-nk>bv$SAbghS`y^UXX#|1T&9OFAF2sV1_$DNt$^yintkH7ypm>B90{s)Ue zRDqcwVzNJ@_-0|&FHDjQj48Yhr4A<+*f-7l@385CfJ6QR4k3ZMp2-RvDqIQzK*bph i3pvFR zEFd`$0Le2bC4xvJCxdhR#~42_7$|_a5G64T42}&vA3*{P-zyIN`K@q+_df>{L;Zmd zU@^EsU>?I{e@5}m!mMAIBv}|!cpXX|PAsr*n*ZNn(*psA{0AID40UTJD{!cADGD%f lFk}EdUNG5@V;19y$%V|ZT>SwI3>wS~j3N&<2XNXj0sx`@WJ&-4 diff --git a/res/save_files/hundo/eldin_collection.bin b/res/save_files/hundo/eldin_collection.bin index 61df282a4d01fa93c705ebc6fa71118c15ca8d1b..cc31d7f8b638f354e9cc7fc55c256f0ca6dc16a2 100644 GIT binary patch delta 647 zcmZ9I-%FEG7{|Y7uhDs9wIz(C!rpQhbL5=!WFqj zK#=ib*F`}E5`xfOv`m8tx~P9(x#>V8Ln5`z(|I=5uXFJ^-yi2WaLz<%BIKY2hcVN4 zIrR|%Q>g}}RC{YwDg3{lob6eAxPH&r4VB3fj;$^Pd#7_W*Xhz;SF0%RQW0o!88y^Dp>H?NkyM7-c5K-7@ zK9v9bVprmNZm`pv5zkvPBeetis1`RXMf9l0-D1=XqhP~XA5I(IQ4wvjbqnzzm~?N) z(n4zqBM1a%-}x>HA!ox4AB^Zm+gAj0{bPnWAPzoias|W{(;l(i`xbnoe1q)=e0ujR z*(Q3ub22ZAUQu3kA_mv>wMuv5+lpL>DdIVrGM!br%h@f?#yE?d^a`|NC`EBVk^ZYx z%_i7(vTaQckJqwOJdt8lC;r&QJ(#1C(jb*l#e^H#&*(b&#qr@mP zmKe*73Zoz=6}^%xS<-(7nv2cs;U|*^gr)2*erggH$CfN|HB6ce&#)36Qp%Fs5HQ*1zBBk{ph5DWJAhl`{$-#|M zQ6aU4r5<_^z4fM;V?ox~AEnrnM+-Gc8>l@*Dz<9sI^R~X4$L#}`_6pxmknh@GIWCm z(0y^=^PJq&GOo8CYK67#Czs>K=>5V&PF1()3o*z8b zRUHiWRSH)zvOZGdo#i zcj^kQI{xBu;?eA_yR64G7RgAv00QQ$OJ_xMkJQYRd_wAEsQ|z`$r~GXGiPJ?BnE%* z2!IQ0RfA^$%^b%6URcca(z5+XjGc(;_0uenx7>=$yJKpGX-#{T@oQHt zQ2gm^!_9Qk40Bah{i5zNfO^i<`51d*UXywa_R#`}NWdDxv@Nw2d=RbKm;pA5L&D>z z$5_FKl-;519%Ye)Y2k0lwlNA|qquX&rj3z3O7>v<#{JDKNfjLgr{bS28Zs2Hge5|SVDS+f-|*!Dqz}LXU-#i#z6>b7{|w+H rZ}Z_4FAtzX)89++Nn9(%=Ww0W4^lr#{UTK(wL!{xNgGuhb{_TzrBLd) delta 634 zcmeAX?Gfc-2w`wwU6`;nWVwHWP2cH?CdN>vSqcrz5)2H@ zlTS1GF;1E+#jMCEw%LZ+kK zfXN+TawVAD2_{#8$z5P_HJIEDCJQ3I7jSYkoKO+sE zus_{0_h=nU=KOqka))?5kn!Fvne&5dT$vGwZnXJ$pi%!bSYxBj=M_M_#^&>R28Pe) zZ9syJHXlv^aZ96@!Hou2&H^AV0kY3PG+Eb^hbVp!5c z6hR=xN(NdHK7?5sY=zcCBvcPQSS|Dv1Qjbtf!)r`-SIEiAh3cuFz1~4&d;2CcJ>S` z2RmezR&RL$lEvVZwI;(bU^aCZuC3TrT`EZcxlNbLg!wVOc6(UX)@^NDdG-E@ysDkW z*{*DWcu!T~4(IwD0M}9&TJ!#aXE5i(Q1jV6=exO2wi7Uu1Hiw1Z@XuJg4flMPJu^Z zI7l{RTE5}UWhHrxefxQ7tBnG~*k-jzKoae-aW)Q4G7KRAZ3h4rZA4vFks&Px6-WkX zkYw(03*d0XI~r<8oG zFZuHBTiV1G{g`0{g&p*_ypKKE<0EHk>41H4%?dTnV;6RnDTIQS8Wl|np)EjcW`9PpHwo<; z>yYg8rUvA}E^CiP29lJ@Flfc4;f+GVW+$KyS_{xbakc!+@}K_l4v0B`lbn_dK&_BWsu@gRo@nI9JQ z&hPc3^;hG5%rrN$(D`SaO6_n_HpVk4C{T8akVLYKCQ*DO>a1F$MnMJhebBJy>|%sk zO+fOYjxVTEjiWWe2@z!$f<`($dohk-K3=ASe@>Sn)?fEKjwdhuvyZvM0J)p~7`1PQoh?$J7y%=!87aVEK-q+8-1jt|%WjLZ8x7sy@v*D;$U&0)TiAAx@-V96=FS#%a zFtAVl$P_oZoY91p{XsR$A zaA2taRP4kwS&>nT*8wQYz_?k!f}u&xY4QQ4FhKzZ1}27SKv4sb6azy8(2kRvKQMPQ zG4cSl$T5BO+dPLYl#y==!&Hv{-*vt)2rxvoGEe@`uFd#hvNnezlS08}KMpR&`pU!( zgFpWh8yFa;Cbj@gN}S4BTbbAi5^DsBwSvSnY$hK_{Kx$NeACG~opCHyR5bHOH^#{cI3u1wNq6gBV4wy?9 zVUL6weopk0;Mtgdbnk;BZZH4++^;;bD3&pO;sr6T(`U6D92m7Y<@hIl*v+~tRHHI} z@&?A;@?bL*fMnhC2Mpf-pNBKHaH=pIh%2oBR2;6L7fK5ZQMH3?f z(=3h#W(x)efyt+t{1_iimSR?96xwXV?8s;+$iQ%dna8M?g%PBMf#D7i3kVxB0A2fE z!GU1`1A`(5!wP1Q6d1%zPGoJ}Y|MI}Nr-QbgHeNkL;e8a=?CZ%iN=NESdB3;mIB1 z^+3jZw`9%_u5o2XAiB}!HlJ4j@fw@Y=NTA2pSJ-CHrjkR0mLnhUIsTB zTsaGXxCF>P1Cjf@0jPcl&_WpyA4q2asRcmu6d;cHeh26Th$G?yJ|ByD;Cb0aXh9f| aao#QF!6jGbh1)ONe|k8an4n85tN(FzXn#vH%$%a0iG5gbf*h zuKjOtfMEdxgCYaN3TGCO90=T*oXFa`*_ib{lMw$L2crf7hx`K!LJUP~_`)jWC#!HM xa48Bfa4;kQ-B>W$k7E|&iOGe`G2Dz-4lpoiFf%ZUJOCOnX>tK;^W*?VbpW9jT3G-9 diff --git a/res/save_files/hundo/grove_boost.bin b/res/save_files/hundo/grove_boost.bin index ee7ae70f17a3b6155c6f0038a818e31af1793842..191248da3659c1436db151bdaa9b20301d9e9932 100644 GIT binary patch delta 263 zcmeAX?Gfc+uxHR?G7m{G2xAbO$Ro?Y!uRCTy&=-v>uMyO1QeuYB@MCYI4d+Py8Ums5qIIaUSce=C5=0CU0QeEzbY~3P4=< z`~ida|L5V1Eu1P02jU9rKNSZtJ4{w&)Rgc6N;5Dn7qDn(vT$N#V3Oi!V76dj5SV+T=p!n9Z)7?u-CF6hT1% delta 263 zcmeAX?Gfc+uxBt}vJXj+NM+!k$Ro?2^R2nc#-p8^oq@q=uZ7k`3k{(YjG_!jrGgf^ zsJ#5?+HOY|&I1Cta(1G5DKL&N0L zOu9;L9~k(7(i|W)KR^Tu5MXGZG+BySkx^{34YMPoAR`0A31%ImRu&)w1nx`@z+Sg@c#cjoUuhyh2cP4 zVg0A#ASQ>&ij0~PK0s*(#_a+Y3{3`3j0{Xt3=PZ{3=9pEPc!K%xqV>Z2TF5*)cgPu zC_sRrebQtpW<^G^%{I)AjDm~|3@4a%j9OWM3=p_8c_PcA%?7NGn0T1yI2biBIOHE- c5Mr1t$Dz+yFxih|7UPM@h0HOVT{+zu0q=!CA^-pY diff --git a/res/save_files/hundo/hotspring.bin b/res/save_files/hundo/hotspring.bin index 65189f047e5c3d5765cca43eab19a99ec739f859..f617918ce9c93a9aec3e9fdd854c00189502fc26 100644 GIT binary patch delta 291 zcmeAX?Gfc=@M5rFU7JrKhs z?2%Bz&xw9BxVq13IXEzCamw*e{IHvK>k?yA&dD1Xcgus7D*(y5=MNaX|343BY~fU4 zI1pD@|EV~L*wS~ TjC?9U113!_U~Qfpz^D!Yf7nKu delta 597 zcmY+9PiPZS5XQf^+th8^niy@6q9%o^iAYV-%L>J8DIWYo4kCE4NXMHXm{JtIxT|eY zp%CIyl;XjYc<>%jZ*ec#N$yy>3lo^G6k z37j2^>X3S=d(ZyMtxGqq7H2!R0V8&z9UA;9e|Zwh7Oqu~HxGWCd{D|g3{Ji~WLe^9 z``&$}wfVryudxH?-RELHww>qW>fLNqa7wP96I024co)X)Zxd%U%N-WU-KSL>9WTNr zZl^T?trw22&T!nfT29<|d-XmMog31JMOpxh`r%BWO$n|g^};*TP!Y^#-~kC8 zVpm0V{c6(P)P^JnVuq$vLNgt9lkRbLi?ae}i6PgcKf08Z0wk%svYR%;c93n-oVc@@ z9pjBG!{7L4t9B3zRO^awMZL?Bv>aO7iWRnv|4}Kb>@%avs4?n{WyT7l!SIZtq*umj zp2B>>g3$`mYhx9(12_RH8v`MFW7P8$;r92MS*G=x`JP&=ez5w<>KCgGR=-*KKN;YS IBmT$!0#v`<&j0`b diff --git a/res/save_files/hundo/hv_archery.bin b/res/save_files/hundo/hv_archery.bin index 9cb78df81466448235e3ac6444b7b90146c92fcb..3fe53c990c6cbeeb75bf76ca1abc509b461742bc 100644 GIT binary patch delta 319 zcmeAX?Gfc-2x4$xatTS0TR4$R-gAYY@S+3t#oX)+49*Un8Vt+~U~v3wecsVS!X9pS zPky;8<`y3uU}$8>z`zS(Spe0DGPs3T&T-{gz;HZW`S`t}iA7Z`Dx5N06F=-`JufuP zfN}B$#@+H@!xR`8S?iuZVDSF`Jf5+IQ-$F`{m1%G#X-ytlN}i~B%O0Wsw~$}u<7fr zXkuhwnx)aeEWyAaIQcY_ALFCRQp}2sLYr-v9T^P;85mA5^BDEAFoF$cxC5jFgn(;MgMmRIV6q>_EXHX-4Kdt|R}L^RXfQJ{@~KQtWSum*fVFvY0HZnp*Q-OA_l`kUFU8Pne z*}C}ROFs6{L*S!CH&YapUMeWq!}LxE;=}SGt<2j!+e3EX`|h;bUSJ&o_zHs}dw_ce-W826!9PfKn&6>EQnNjKZXIC^AX2?_ndr?%g7OlF=d$Gp0 z^gjw?cDBq|VVH~}W0kSSC@~CW%%bV^OS diff --git a/res/save_files/hundo/ice_puzzle.bin b/res/save_files/hundo/ice_puzzle.bin index 1634230be0987e614b10b03fc860ad939dd97b04..42df711454f72528057a0f2ee001f9900d67d5c8 100644 GIT binary patch delta 610 zcmeAX?Gfc-2xPEhiU>)NTR4$RUUh{Zm;W`p9By_724_~eD-6sGU~v3wecsVS!X9pS zPky;8HqlRl$86ydX9kAjUJhNmUrj8EW!ygTf((larwsSR54%}Uym+zU?BorMyXC>U z6&M&<>z+Sg@c#cip0R~fh2cQ`$NEpjLCg-59T_zwopV5{ESFEP>Fcg&Vq{>NtT41Ed6m4H+1i82&3bFf3qT zP~>1(0d+J`E@pBebJ}JR)_+Vwe0Lm-8U!5j4=@M`RQ2$MRoP8e;ZWdG5CAGmU|>)P znC!-b68q5rgd@4X$FxU+zloR zBEA=Jax|P!5#r>Sz|!QX01^d*2Aew-jW%BrfE=)2+%orQ9ZTl?e0Xw)cs-Eu-YuE) zgKJ!w5r}TI`FNmF|1(%)qs`|PK)lB0^LYk_&*yDGf{ivGP5^OBqnE*r23O7kAT9y2 z&p_lpZvd*_0klvC#0SzDKxzTdJOzj&zTW{l0pf`GfX~Nb9(Z1M5n2!iWSn=4d2q>< TdEqt?jp~RmIzZPO5OM?nasarr delta 304 zcmeAX?Gfc-2xM?ziU>(y)1JsBubT6t-gl}`8#g-xgEQx$sSKln4N02v@~2Z#lP4H+1i82%d^U|7Jw zpvb_m!kGmm2Lg8{7c!@97GeFzB*cHm!KgvNA^!k_5JS}(zOV}U$toNQT#5n=91ICS pHx^9x?-CWf+6oTW5HRyH$3BV=kkFOsZm zSXd}an=u9pWoOU4;nidz3&!8)9TvECe|_I~&sX=vU2&HKJW$I*!{Z%c)lntx^-u1< zY}OSQ0d%gmOsGm-MKb_%!TFW6d$tx1^>mHw43>0{4GqY0t2!HEE2@z$490S5P&;5p zl|ZN3x?$rQdQGE;#q70^^vjXQnV_K6u*UK}>lN6pq(tYHc?RI-Q9%KavxO`Q!q_z0x(eq@cme8Clg!9HYhWTKUoJge1z}Dugy3hg}Sr)FCtlI)Wloy;VQL!5}1g!o*{qn?i}ckm7j}Pk0Wnglw97L_Fo$DJ&)Xq_-() eeAbmq%g5x#kuh9AIcFv3cV2BdnHeQEK-lZ(!Ul$HxE!3P4=<`~idK|L^XM zEt)C}2OJFRKNSZtO;%*olJEh_GB9ozuwZC1XkuhwkYZ?HwqRgrn0%Vak8#pwDdrz6 zJj`<(fQlUW85o!tCd+Z?GZswtGWTd5OXmE1 zcyfn0kbdu$%=y7JuFMETH`;uxKhUWE8KjP((dP3CAYNnh`8)%|=kqoYu@5I2Z9cR# zdKuhkaOErj;u0YH3{?FFp!yv^3uQojAe{lE768ptfH>m&9iS5+j))KVd@Sa{Wf!3Z iVL~IRm4M~okMS;)m|hAH%i5S z(HfR|(1Z45k3x^R#F!GL=&2`Zp-s{TLJn2LR;{k{wt{sIzxlrJ%^T)TKAMmEFbYGu zbYY@%9w8KbvEhYG;6;1m^x;E5+SR!N8P2MHFt03?BWH%z-hW*?GBkdEMAJ3E=O|4F zV^LJor{*hZpnBGLP;3>hXFKBVn5VE^bS-PKPkq*FRy^)prh>U{HrH{^ZjQ4VcN``2 zv-xFuR5q+ZmN=TeOsD!@JxM)&c;hyM^gNN1lR(H?mNKkn9h19QcrhgpGA#tWlfC+* z4(42pRj~yl;$aeO)xb-jOToh58ANT&x+YI9MqIWO_OBXxE@)gWAso!+{TH+SR$Ng% zYET@T0MrpHqda3zMZ=iMR!TrZ2J^>7)md$RuXoSpIj}iCBN9I6HrxC@Ww$81Ls=qa zmH2Pjws|jLbMdZ$eXEl_N%ll?`tE+#PaOjUr{kZknI4pR-K!o%!<)1DW?=guw#YXB z$NH?uHV8FBozNg`61E6ULP?pm`5UD*#pwfhS=kNnw@M3KeDN@NMd=RkC8aUN!&LrO hPgeL&J^7LElKM{S2dST=_DKCA(y(`H~`o5(BUne*@J-nES~-0Tbt&Rj{h46F=baQqBU+tIye zKDb>CIQ=lrEj~EF(8!R1ffvFuV_;zrWjO9E@$rZ=1H*BzDDyo}Cl*z)7_y1VP5iK% z_59R{l@XISFz%M;1DU44z{pzn`~ida|L^gPEt)C}2kJl8e<}`Qa+vJMs3GZ`15#wU zeS%G2cSRE;1Ji7U24)KehUUqqnfw?hO_pL-WE9(M!|cdt$jHEOf?3C?l?BKEfjdAf zAPjUE6T^Rl0}Klo7!(;8RyebOwEmiGo3c&7F!yn=c7K4%jblnR~R3 zC3Aj0Jh?->9>{p_mdyFVHLlDEL^s-eJkY5B8LY9<=JN_5USsq5JOjh$^EM#CMw<^O zfVid6%iulyD?*N?uaYTH;=VLJs iJTJQlEeHcL&b!4txa7*ba2tq5b;K7Ppz93?IRXH45xsl> diff --git a/res/save_files/hundo/kb1.bin b/res/save_files/hundo/kb1.bin index 1300c7008f89663bc9137dcff422d8d6199a6f63..5ce46d7df63ebb1acf572ecde7f2e0ba45e6b353 100644 GIT binary patch delta 254 zcmeAX?Gfc=&|y$uyd08XSI5A>GLcs%V1;k;-oBld-0Tbt&Or=ta7=sxgVVz~f1FM* zFgP%{#Rmr%8W;g(5iC)LV_X6koUihqbeit#2Ujz(GK?{C;sr6r?upNjuzKC;OS?3A z1LJNvu*nKQvhMi<2G9S`-5FarRTvI9Fw}o44q~3H$fzaZ15(4dT)?8C$)bsofkBF+ zf!TtAL16M}CO^hUo28iVu*m+`{la3>7TwCsD9$LgM52+AfiZ>Cp~RtMfdT{5WMK{~ d#(>Fw9J3gwO*UkXne56bJUN!-`eX;@SOAyMM(h9p delta 557 zcmeAX?Gfc=&|y$uyb_Y2CC9+RFp*a#AUCh`#FtZVxY-#Poc>i+GeChm`&+idQyUnL z71+LBz~B}i9AIc<2$Tb}42*y*QHG;?%54rUU|?|lm+yJp6e43eu`-O2apDCr#^#C7 zkFXl$PHFx=c?08aIX;lV3P4=<`~idK|L^XMEt)C}2OJpcKNSZtO;%*olJEh_GB9oz zuwZC1XkuhwkYZ?HwqRgrn0%Vak8#pwDdsyYvfp*Tuo$#Ow=y$|GfFLyXk=tyOyP7W zap+i}z`#6Nn8S*(V6q>_EXEU)4VhymyK)NGze(&c`13!pfq`*qVhhlM#HpM>u}+X! zBS@?jB&K0A`9R`7=Kuc_4Hy{yCjxzH58(oR`ajVa#MQJYZik53fppk|2(S(W_YoJv zp~S~L3dltL6P||uiBI`IAQR6(+~=sogMOgcLlE%@L_7u&Pe8;|5b+E|JO>d6IQsu5 z9^`mckSG9h!Xb_y{E3G-UNIyJg1ARG^!O8xay%(W6asO-JvOsAlKB0x#ks^EkAbY8 bAl5Gs>o`7-k!Ue(5D`Fe$Vgu_rAZocgQ|uw?GZJ zVB3+%u6ckeN-3AJI1gp^w=TZBGshYL+LCK;!?L}})-}BD`)CZc2%i?lLp_JX{w6R;ebQN=6$aBj#op*xX8#7fi2mkbA2X z*y;_Io08V#(?OTYPmc;?91q0l(rS=@+`#oQwx0*|OtQ9s2^s174JPGb#ZINh(2V}! z*j5$H*SfB<#6i}LI%NXnq#*T6a@q|{QrneqPt(9_iT2&Birficj__c!_<-r;2sUN# zl_wD0VWoo~W{j$$^5^39>`DX4Q!U{$I!@|1rsFdm@9Fql$NM@?=@{!Q#ROiSt+fjL zFgLK$z-vl6ZcG@!&6^bvFK?lZfp?Fj%1dm^A3Smr$-?NFvqBaK! zv6w-BAQp1~#hh5K6=oA+A(?3REyzJ3hTe~SZh3wJ=O^(({TE|AgY`6?=PrvVFlXscM!l{ zQ;N+Ei<4WA&9x6C550=dSbB_|;66~&qdOuvU4+Foql#VPxDY3-CLk@YP0mWTL3twq zNDKjNmN$U6`hsSuWUfh@{?IhJwtH=1SGCJ% zzgx<}(QU6S#N62d84Y6Jhx--YM!KwIS`ESdo&CUPl_8??)8-GnY--~j#5)}{Usqg( zh%rcW_JFN6lD}x&&jTdHzcz+s^;PR$Uba{Ud^)IbF>WF)WQ*?636QHqyQ%5;5Hn8D zvl$?OYa36VqO5d}mMQ9@*BO0ZeF9Azp#VUDia**ska=0LkZ s{b4&>r1>awqW_T#EwGhb=p$Q2^%K=ERKHQJq56Z$_!2*MbRpLN0&M;fE&u=k diff --git a/res/save_files/hundo/lakebed_bk_skip.bin b/res/save_files/hundo/lakebed_bk_skip.bin index df4a73bbac466a95ae61707b8fc19031abc02e7a..e5af54067dcb134f8a28ad32e3c308a3095ab47a 100644 GIT binary patch delta 221 zcmeAX?Gfc+P-Bo`*cg&vf0==KB9E-<3Sa-6ZLU$w>2)xbblLp@~J7ss;c5tN!=;|KFRFg_(g-T!f#SN9aE?U|?`y zo_IlqNm6X$yS=RD%N_U4p1gr^w;b381t3}X`~idK|K;wCEu1P02OJpcKNUMMPgZ2q zlJEhkVO%a?(a>b)#K^!P!O_5M!N4G}`81O|D|8I9^Y|&I&i9Pvd5=z0S}jsO53K8@P| delta 463 zcmY+9!Ab&A6o$_kHE&{CmYY^dn+&osC0s=I1g?U1(TN^FXwg!IqyoWY5LyU5K@iNY zm6@%go*|u4!8Wz1tjyb-5z5ZuyZ`?memJ)fECf}Ug&C-9?kt^xBOvWr4lhax_Tcbg zZYlHOr~8SLsw#OX?dH%|GUMphUX*R^0OPLM>xI{;U1AhbYvP$N;6_(y4`9RUDvB0D zRY6_9QW_3S>6Ni_oOYrB$Xe^)N$V-$!OR^Ut9?mXz%m_LODd0B*$HPk&hnfstZ7vm zIHV*MKvEPSIkYQmV{F5T{i9*F&W$9)Zv5G%2Wn(^=SGn+y;(^u|MMu?Y&-w)ShTVR zqscHBCZok@Gdhf_C`!5(-84<|2{qC4(~al?O|h+uh@WnSnWg~SH)AzXr*|XqKt01! WAFMuEeX;7Z`etSSB*K+t#^48xZoBvZ diff --git a/res/save_files/hundo/lh_cave.bin b/res/save_files/hundo/lh_cave.bin index e8e85558daf4ba6479ed8b7c226737e0c3fc9acc..81ad8502513bf113a7b622e8a3e54ea6e9aa95f6 100644 GIT binary patch delta 173 zcmeAX?Gfc-&|^?x%neDftDDFr&%eSqe|4-Y6E`~pgY&HY2PRtR@r3yuYhA%P@G#AB04j3eXJB9wm@LPk W&loV-k7E|&w8@3cF`HdE-5CK0DKYB+ delta 173 zcmeAX?Gfc-&|^?w%neDvM`7B@QsgY%rgu89_UJZ%-nTGueR7ahMR z{di(g31job=SNtrU-;IjOy0n_TaJ$b2o!+0?)d`-@BiQ38Cx_}7!G(8)_*DvVw$YT zs3qY8lx1MtE?~jXWYEOOz#zrYz-+<5&@lNllON-x%~H%iSa_J{H~7heGmku5Jv(!Q85|kkdHqE@Zv!cAU6}%XIhxxww{eIuae6ut2 z7Uc-ySzBk-06>^&;XP%H!8BqyF-|tNM_mt$EY}JoqFVR#x7jg@SDwp#*XwIKPBe!r z!W?fEQK%9wvJ-|;=AT^xjLA2TZ@e}&;r&pY2mrB$_R;ET3ZvpuF-3Jp!B_HI$z+4^)cT^X#I&S#m>Wc~Ylv4%C_X`Yy*GaJmOSn>8H$4jKlSk{l zVE%p6w|0y29gtT}-kZc(Pd4YVqB`*o2$Om8osjc{F3EqgEPYG?Xe#$y&5%dnmRJO2p-Wj@e z`ldpBq+lYS@{dHfTkqHDco2?ZombNNo{NK_T2%>E;yljh&upiPyxb5RC!K_o6d_B& m$6&JtY`Fbo`ep}SRrkcZH@m7`J@PIF{wKqZ`j4f*1AhS5d#jrO literal 0 HcmV?d00001 diff --git a/res/save_files/hundo/mdh.bin b/res/save_files/hundo/mdh.bin new file mode 100644 index 0000000000000000000000000000000000000000..02ec5dd263d5d0d34a957a4463ddbe0b8d7026ca GIT binary patch literal 2700 zcmds3NlX+$6#X?lN)MpZAO=J-4#Wcxml+H?MuCokAd7%vf|>{ww}T0gVDuo7f`F(O z4~j7{8V)4tMe*VV55pF2deDOhmyuyf2u7nQi$Ynyn;I5D;lM%b@az5e{{K_IrmMPP z6FA{XQEA#s0K$#oPDtb&=8gw>hf8*Zq=WQGpZHb`6I@1>o12`OZ zhr{1c9D4O#{*9at_Oa#X}ZUQnn{Q{)lQ3JxgarWtEqe&!MDF2#5yBap6xj z3Rcwe+f#3^zw>A-faPtCMw$S^f{~YFVgs)hSZHSdjmeCQr|arFdijag0OJTd#uN!=;Hr2!x8=L4Cf&bAS}I#hqDJgCTkzt6cC zLhfrGR-3vkq74?;k?um6n)ioUeHcEkG`@{sL7NNwb^KsPd7`!&cYjWAzG& zB+<=LOuQsc+VpceYjT364om4`&+)-f&AJ2>pTJS#nk`i!BFqJrVk;3MlUJmOa~6nu zbII+SKd=loT-EKqlgm)}x02+Q7sN-#On!)7asA|!0coUlLZFSd>LGd+xf&Z&6Ma4J50(o8oA-EHV5cq$uAE)_G<&=?uhZci|@Q-7#)rs)l3oc7s2&ouq$ z4$L;b>w^1$rU#85O)r{0H2r7>(0JABoVKZh6;%AiK-<+(JMB;hfrg>mscx~;F15FU mhNJu4@9Cr?e$R6{if9bcIHDhjej=JcWc0BGGsI_zXzLec@IF}p literal 0 HcmV?d00001 diff --git a/res/save_files/hundo/morpheel.bin b/res/save_files/hundo/morpheel.bin index caadb940fbd2e17e5ca1bdee12cc0fca78dbe3e7..c7f53f5f36acca3c1dd0f6a9343327619d5acea8 100644 GIT binary patch delta 202 zcmeAX?Gfc-P-l=}*cg&ve|aL8yy^sG?P0kBhzGI4lAYrfyn{v#*-bGBRAjX*vJR~CT=>4 delta 202 zcmeAX?Gfc-P-hTfcoLGJ?K6=}UNz^dN9e&l8qDkr49@vgn;4+Lo&7D_;i(M_#|ms; zFJPGHC&9Bm?xgtUX5+`q9 z+%3-l0t!G}_xu5a_y6bNj4hlh3pv9-F*{6FWYm=K0ZKD4E*G$9XtHo(WMGow zXkfNrU=Wymnn_oQ?E?cpP?`gzrUFEu00D;fN0X(P6&Zy#+b}ya3JNkXoM7fL>SbYs zD7!OxBFm!92CR>mc$nrm7&S0Bi2{=RIp1nOaVHjXvokO_yS;og(LzW3=>ED5 zM; z`uUyeeU8Z+7VDuoP2;qdU83V)Z_q0xy{0? zCm1IyvrBCL%qGGp$-tPx>rm=&Vu5|ry#EfH8U!5jA8-f>)b&hO;85XG5CEEz!N8yp gFxih|7UQ(Zh0L*B+1nTxG?*C}`BXLsaM~~e0D4zo2><{9 delta 359 zcmeAX?Gfc-h+^K3BLl+;W*wte z79axz?f|iXFwlui4F3%dFf3qTP-I|O;miV(0|AgcgHj@hG;%UH$A66R!{h@j(v$0$ zEjA0Yo?x8p#wf7)HJc2hBnx8-uS2QBi3Rpe^Zz?+Y7lV9f50KcP`75X0*4Bhq5uO2 kLk7?%1(W?aW-*?aT*w^D)gQpXpux<*DDq%)0H+Nj0I2$6S^xk5 diff --git a/res/save_files/hundo/palace_2.bin b/res/save_files/hundo/palace_2.bin index 36d69e49f609d0a1b3ad019e96023c0a8e7ba3c2..b45082a1c52377ab6ce3f82ef68e97c1ccfa3fa2 100644 GIT binary patch delta 345 zcmeAX?Gfc-h+*(xUtBJbDQhQhVB9Sa)~Ue2$XfUO0fYDd z=kbg!oGJ_l>OaYFxin&L((}1q{?#r1e?Co6;6zyOtUl^m?an(1Sg+n@?(57 zS&CVaQE0Obvm>LkAOph*W*(zn7Dlka40nK(fUqG00~5o41qX%&3=E1K3@f1S1`aiB=TIl2rjJxIeKw1?T7+LF{KVb0w z|2>|uMN@_0K>f%1&&5GZ4wD@jH6)#LK#DB)Pq67bUE#zS$}~%%fmwoqp?UIYCO^hW zlcksy8O1i+Fgr3jGcquoVAe5eWdSlk;0_QA2pcjmFfsf$IKZ%gfkBahVTCgbNDc%* z@(fCeAkxUm;2i%k#t)MZut@TMuQ>GQv%(GD|3Hll3?G2b(B3S}dV-O0`{p}r+KiGc zj48Yhr4A<+*f-7p@385CfJ6QR4k3oRHIo%MRJarc7&sU*fc`3&?8h;S@xt<8 diff --git a/res/save_files/hundo/poe_1_skip.bin b/res/save_files/hundo/poe_1_skip.bin index 28ff8132f76fc4469cb39af97a819bda0c6f7d17..26850f22d1fe8cb5c42d7cc6efff31571de90a40 100644 GIT binary patch delta 220 zcmeAX?Gd$LFl3Np%nwO0e7}b^t=;YS-^m*o zcgukdQ2>&4&mS;&|9|ez*uts8a3G?v{!?)f^JGOvEeRix8ph=U77a}nO+c%qI2xEO z7#IX5pJwu7d^A~#S&>m_vkmh#79OTK4h9Vj4*U%aOahbTIP@6MgRZ+ delta 220 zcmeAX?Gd$LFl10+Yz#?|=VK7y-1Hv=3^lbj{byigKn1z+d$x6?oaSa{U~uZu@tkO( z%lk-&!9}Nm;g}~|T=VCNMX`*66EBEyHlNgDa9}iK5}x>e4{NslY%SKw8yI)X@qvs} z0OGpm4;Z}ve|KkW(NtkL5K&nFsW^yfvLd6Fgbz@bfpNQl1w)fT6VQGsh6ZK}28M>o zr1AhYp6T@UV4t>Ug$$lKO7*9+tWRBVF%IVGs E04)hUF#rGn diff --git a/res/save_files/hundo/post_ag.bin b/res/save_files/hundo/post_ag.bin index 7166409c54ade8223e75e669f155172a5013f43d..367fc6b423f8e826fc3d48dfad5f864cb77f908c 100644 GIT binary patch delta 230 zcmeAX?Gfc=FlW$VEC@+3TEoC3Fp*b=e}(Vds}A8Y-0Tbt&T|_WCR*r*-#@%uZAfN!mbjOy&e+1K!f+s>u>MnV5c6b3MlA^+pezI9asi8mCW|IU1_mjP24)Ke27$?^nfw?Z zO_pL-Ue$$lKO7^h7xWRBVF%IVGs0JKm)UjP6A delta 232 zcmeAX?Gfc=FlW$VoF0-OHH(3Xb0V({f6h0D*p|jG-0Tbt&U0r>m}sFZ_Fx8^tCmsA zvG$Ax1_rnI-~dA-QwEla)!CfQC$$(H7_FHECcfXpS{^xbR`=u$jJxId7=S;vlBUii}zkK0sLp#_a+Y3{3`2j0_A?3=PZ{3=9pEPc!*3 zPMR#mtjH(Ez@fk(BPQT*5U8|aaw2>GWCKQt%^#R2Gcig74H4ZuldY1Ghk1^JK?8#W fe**&(!(=%Qea3>xejKwHPfRXkj@j(W>COlMj?_S1 diff --git a/res/save_files/hundo/post_mdh.bin b/res/save_files/hundo/post_mdh.bin index 5f3f76c962b86b13f6e24c22c9a798fc2916aeb3..724227f1534385ec480d760fa6086d10f01a4ced 100644 GIT binary patch delta 480 zcmeAX?Gfc=&|y$u*cg&vSI5A>IFVOIb%k$qYQ>^XZgvI+r!5(!3{c?Cx%<`OsSOOr z`OAuHyeIn2W0aiub|0(LDg7_ClQ%H#mIJF(0FrgjA24|Sf9}rM!l}Y=z=5IuQ*jXU zWJN|T2_KLe#^nMQ4NZ1Vj0_A?91YAC3=9I3Pc!*3KH4nB{DXy&X|gbf6;pt~)^7)Cd~fyvciayOVPi1=Q>$URJwlmYR9 zbOw-G05nel;)w5efKGroB0k{rv6u&!U4#~d0qOH@F%K@eGB4cb7Gw6{63`V$j`*Sj KbiDx~M*ske*_e$0 delta 177 zcmeAX?Gfc=&|y$ucoLGJCC9+XFp*bAHRo$@u$hiIH#-A^(-tNn1}Jc6f6I1wY6HWu z0^8RM7$*A7W0alvb|0(e^?ZKL$r~7V%khB}Dgbfa^9Kx`|G&F4wrHv_9B^Q$|5O~r zG+B{ROTq^z%fPr@z=EO4%!!eKL5iV)*@A(gVe)AvKgLO$rI>%PFfvaT=CEQa5SSdm VZamq6IdbxCj@rqwEZ2eTSO7SjFgE}I diff --git a/res/save_files/hundo/post_tot.bin b/res/save_files/hundo/post_tot.bin index 5cae0b369fe67f2d6bfb4114291339a942361d28..6d036f62e63441c7aff3b5852c89f8e052ae5433 100644 GIT binary patch delta 607 zcmY+AO-mb56o${8F?v%+GDch|HchNhKR{!Yb{3*j!EUsr6v2feJ-@(65CowaMX^#v z!bKNFq&w}RP;gUmlUkK-><=hHH4_bpi&({Po!(>6>VbLYeczdL?#FoHc%cR_z)+sG zzpc!I{FwGIXa40zr*EJr7OdA6_YV(R>cg;Ba=sc1_vK!GSg$lqR^|5Tv2+Q0Eb`c_ z>+_d(|2OfKEjTzm>~yuPdOAX=bAAKg; A!vFvP delta 607 zcmeAX?Gfc+aAUAwatuijV`ku=$Ro?1^TV3?wb}iN1|~d?={v(*t}kG4Jy)(hv7nUu zPxDzV2M0zgHaWS8A7mI+Ci61RV_m1}xxrxa2FBg;d<;OK0K|3AA24|T{~pfRqN&1g zAg-|fQ*jWJ!(>H9O?4ljGy~&y0SksE11ClXCMkvnW(x*}1}2a=5O9Eq?$48NG07>p zePG~+3H<EB}%!5m=%nP@<#h5*~1at+GBfjVWU2j0h F5diXLtuO!p diff --git a/res/save_files/hundo/silver_rupee.bin b/res/save_files/hundo/silver_rupee.bin index 69f5de20203e70b6a4352d1b669c257c84e0574a..60c988bcd60a77fbe67939d2102def5b1d077e82 100644 GIT binary patch delta 541 zcmeAX?Gfc=@Mo}LR1QgyTgbp5Fp*crVukO|-|zl^=4NMLa0;rP$H2@02FK6V=N&yH z?BRCzj+cUVrHyn!)~k!i9#laD;uDg_2c*1G2p z7`*>Kk7sP*RAD$!_p$y{aS*e^WJg8~4W}HCD$C^pHhs+&O^ggovpE`=Er6~Ex)=<& zK;-GqlW#F;iT{8IA&~+M?T^GKOEar+hzU3_0wo$IC$jfX4q%j+{DIk+kz=w7i=Vy* zM1#WO1_eO|h7-&@M!hT`wLowOhy{d!UT0$Xui(J2fPq1gWAZ|lVtogo1Pn9)8THm5 z%wc>W&EWtf{y)q6G*Q8Z{{h2_&Caa#j656{8F)a782>Zy0DUJr`88VvBjaQj_T3>z z#TliR%xGj~U`*k4D0Mioz_y9+j)PHyfJ6QP1|fl}9=@2kAFaM5STW(kU55% W@yY=P1`TEgMn09zft=Qij0^xx7=Wz+ delta 516 zcmY*VOK1~O6g_u7nbG_vwV|=C@ihibi{LbFDpnZ@D3~Ix7A+{6fTAEoLn-(HGl?#` zsMPQZ3I(a?N(6-pZUaSH6&HdF(JlglB7(%YP*>xf>^yKUhkMSupEnnui-(cNC>ACQ zz2AUXJ={NX(R}#p>Zhkc1IR62Z$m)`hs^oal^U=bpLykWA3Sqxd^kJIN{88d5U3Xq zQ@Q>mm1>em-93+Ow<{13LWO}eBJft;4ml3((4v`otDn*`O!syDhj<;5&!|L@Ta~e7 z#GfTNZhUPb@BKbOw+7Q_oGw?|(?=C~fCe*#F_y9Ug*A$sc8#Dc!c|AW$*(gu`svM2 z`B53_Dfs+9Sg6(v*{5zdyDW!Ta%It&m*bR>Ep<1qk=BmvGTqJ_p}GTX`JjZdFE!uG zSm&@d@#P9U;a~|EfPA1$bBAP1m=4R{mr}bf_D7p|yqwc36bimWi0dE^A--XY{2rKM zzbW&^^U2RW)cfdORR^67J2TGO(!N@B-O0L^bE$zY>)Jx(_?2W6&>O+Ot>~QuOzWJ9 z?wAc_N~2kz3LC=W_80i;E;9)vWKo}u^qtIke@gyb#+odvr-Jmp0c3cQo%Zs?hP_1i E2cL_25C8xG diff --git a/res/save_files/hundo/spr.bin b/res/save_files/hundo/spr.bin index 0221d0d995824e7e3192bff803db86190e097bbe..b4ca73e5e358286a5a986e80d21fbbaf5c4b00c5 100644 GIT binary patch delta 179 zcmeAX?Gfc+FlW$Wycm*Tw1(mTL>^iG6~1fM)g3><&CbB!wBf?pi5Alt*(Y8Q zYD)M3r5PBP3s^KXSvWB=FiCMVFk3J%2uwcB=4&iGOmiHJ8W+T=p!n9Z)7?u-D(s5Hj_ delta 179 zcmeAX?Gfc+FlW$VycCikHH(2^B9AP8&Nm-7rStE&*%=s|R@I)GXfd6UdEx~z&gQdP z3=WLe3<4A1?_q8Fzv)llbv$SAhihWQ!`5Az%cqXq_t f`~wU^43p(J^cf2#`*F--JTbYDIcBpfr#mA6Kc+No diff --git a/res/save_files/hundo/spr_2.bin b/res/save_files/hundo/spr_2.bin new file mode 100644 index 0000000000000000000000000000000000000000..c39b191d473c106eb5852d11178419277b1c3697 GIT binary patch literal 2700 zcmds2TS!z<6kTU#8tw6V ztboXJavJo=jN+NRkn+ zDWG&sbJFNSD!%TypHEx>sebZ6qu(^Y*YEca26NgQO=3J-kR+cD()a}7^vOl!~uR+@@)6UvQd&l#5jmX#}f5WLRv1ROak~KQ%}Au$(FinhAj>Q^%=CJ+_A#Hu$%d zxk3KAs_mdQ&6~jKP@l{FcQ+&JP`r*pWZ&4eZaHZb{Xr6;cfb^xC*KKa+i|i8@Z64< z^GhwCRB4a5?>?f&AW z|2N~z{t~%eKu*+;{?+;WN&h{!aTrGw&d+_UEttJ*my>0(?B>Q8{Yk9$=$rN3O#wKL z>mdVrbsRp`p(ws>4ueiVp4?%04`e}6D->j;QP^S^qt`Qr05q`#8niN%cHR3m74%<_ d^oSmPv)2Cb(Sck5dmIPG%DNuNFa6hZ{{YK}%DDgl literal 0 HcmV?d00001 diff --git a/res/save_files/hundo/spr_bk.bin b/res/save_files/hundo/spr_bk.bin new file mode 100644 index 0000000000000000000000000000000000000000..b9c291beb284d73ba031fa1fce0dabba4968f867 GIT binary patch literal 2700 zcmd5;ZETZO6h61D-R)Y|x7~1)jqz^!gAoI5)mY-fUgsuF_!_uGR06wEA;x5@nGqAs z`gUOs=g0h-S)wsBSt5(51cUy7AKjcmf8a;r4^2etI#xrLKoEq%w(B|X+Y9UiGJl|) zq~|{8Ip^H(d-}p^sC3-Z*c90h0rx@al2V4NRveVTV5aQbaOmUXzYKWu0BR5Xa10!_ zdpL6_Thp~zs~>+cv8(LC9S=2xL#xqMw`0S`a2SDiF&usMsn3pX@x$82tsRMJD<*t5 zFgLUTq}D&uRd?8g`bfOOw6a2!<&>z5*!}en$XY4uLz|7@Z{Bj&^RLxFaZ= zhSv0!zW3=W0LSs9#`*vP`NS)kS|$_dyG_}uz2%D+S_zkKKGnQ=A~U^g>VxENDgRP_ zNd8Vo2Ls{r-+%kKd3fpTA`(#-NNs;6ai(>P(?G|&6nzQczSnk`v-|*W zeOMF(Uct(4)ePF$xe;?x)c((L{Fj8ut>ZT;(oBQ){ujXTZgQm6Q<&5Uu53-vYU1>>K;;cBvJ1s zch_waHN6j%7}fzxRGz96%Fe~9O8~#+;#L2uRV7u~cjn^kyOA?4H|*Rd3md`vz=w!E zdeLgZ>9^ZUr|g#hXusminb!hpzUf?mvac1d_&f&drELZp18fjeY z4NXd60_zcbkP$te#JG5wxFm5M#4WFWJ0+%_Qi>3O6!_s&W1Cn~q_~UBs%UGc&8?vf z`mUL6j^x9T$oTZD)u_*u;)MV7)oP5)>`ayJM(z(%Bcw8y7@JmIwKF$90#KY28a9u3_=2vi119@%%wn82xsW+# Jvn!`NBLIsaI;H>s delta 210 zcmeAX?Gfc-uw>9-G7m|Rnl+J2onn7UV z`#r3kx<`7pP2Rw`Tb_>r2o!+0?)d`-@BiP!8Cx_}7!Jf0)_*DvVsec&IkaC(m7-R diff --git a/res/save_files/hundo/stallord.bin b/res/save_files/hundo/stallord.bin index c59e76a8f1abe3ebaf8f348c421949a3a68828fe..51c788026f53a4498fe0c8b682a38bcc3ca21b40 100644 GIT binary patch delta 183 zcmeAX?Gfc=Fk;YPEC@+3TEoC3Fp*b=eTDCA5w>^pCmNXYUYFkKqSL@|%wx;qmLn63 zN;tbuYB4x48Zhxse7}b^?@HjZ`pFv@cgrz=fC3QLJ%7O9{r|Z;V+*GW!-0sx`cK6{ z%##%vwIqCivJ8yN1uPnxESeY@7^FBFm@OC>1SX$m@?(57S&CVaQE0Ob^EDP8ra2A< h4Ga$a4M6uymgCT844CZ4F^h59~f=E)Y<{CQ$g z31{<3Ed~chYbJq-@At5lJY%AW#6}y5|oVy#If9XKc|_VK@*`SpTUw zh-tDSqn3mZP?mvlyMP5llR*DK4ZaTKaN?9Cngs%$82`xbY}zr$C)&S diff --git a/res/save_files/hundo/star_1.bin b/res/save_files/hundo/star_1.bin index 6b36e7ac9573d242af86e5805bd9933c0202b807..04f1b97f01ab8f020c1ee3a74624231be55d0573 100644 GIT binary patch delta 507 zcmY+9T_}T57{{Np*T;FWHYAKRMIrety-8@bmVC72MoAtQ%AKrSNcIw;7Om40}7QiLrHVnu{w?^Fpb<5P% zbci5+O1h3s06I1UbbV_JX)9^1;#v9bI%%Sdke>L}Xbyx>%d`K5HV`N>~Gl)Pie<}`QnykpECE){5_nAjQzYY{9_LF!?l-ALFFW zQp`VC7?~#vb67DI2uu!OH=gXk969+m2QTA^$%f1^lU+H5>$ii9;8+1BcYw*2U~(sz zTm>d~fyvciayOVPi1=Q>$URJwlmYR9bOw-G05nel;)w5efKGroB0k{rv6u&! mU4#~d0qOH@F%K@eGB4cb7Gw6{63`V$j`*SjbiDx~M*sjZ0->w` diff --git a/res/save_files/hundo/star_2.bin b/res/save_files/hundo/star_2.bin index 51aef0265766b0ea3f5fca0dcfa82a7f71e2d329..c2a968fa33f79a5d4021ecff3d855f185138c047 100644 GIT binary patch delta 316 zcmeAX?Gfc-h+^tj7qakiN^?#8u!m*RKW5{}8yI)X zgN;yNU}UX({(!;z|MPgp7ETq01N9&4KYtElc9`tQs3GZ`15#zVeu7Qk=?W*tP^MWL z4a^b@41$wSGx;$-nk>bv$SAbghS`zPS&)I@1T&9OFAF2sV1_$DNWX&tv<4zfx%7Xq^-kDxA@=yLn9*wj)~QUEV^u>auYx7VF@`^UpjdM z<8FCAkQoXLjI4FfA24|T{~piSqN&1gp#Ee1=i(qHhslnN8j{XAAVrq@C)o6zu5e-u zWtyeXz%0SQ&^-AxlON-x$x_UUjAEN@m>n6N85tN(FzXn#vH%$%a0iG5gbf)Om>B*W z9AH?$z@W&$u)>)IBnJW@c?P9K5NYIOaE|{N0l(=L4c5-1MJiqJSiS&JV@ruVuE}>r9t4_C1p~Fz%M;V*mmLAg+7< zfWiC!_i)A*O%;X%afS7tii4OOCMz;(O85Y!85p+m#!Ki`3 gA^!k_5W{3S4t>Ug$$lKO7*9+tWRBVF%IVGs0PUSXCjbBd diff --git a/res/save_files/hundo/tot_darknut.bin b/res/save_files/hundo/tot_darknut.bin index ef001abcfa1ffdaec09219fcaa061859458c86bb..77f78c9168a7271ab2a0904c4e52d8105d9ceebf 100644 GIT binary patch delta 288 zcmeAX?Gfc+aAYuIG7m}6Vqp-R$Ro?Y!uQ&ph8IV<*%=s|4o0#~w9w`L_~o;!Nddz# zncqhz7Q{0$PP`z>{kQw9mV*PMHm98M#1Arz%9D8+=dmuSQt-Gpc?08ac?J+r0OGpm z4;Z}vKM!YY;Z$Kb5La0LsW^z)VX`8lri2ernt^e-fJH--g%cwKlN3h-vjqc#z~s|R zx=L&x82EwG93V9nAOZylFtk6KEXAzID74vz*^yC2kb&U@GmlX(3nN6?9U!G3Y{0<4 z#IRezfnfoV$FX@KOCd84(;Nq*1_p=x0}Mg}ljS({83QK!am-?zHo1^FX0j`%@Z?<9 Nw#fyo&65onwE>9jN7w)W delta 587 zcmY+9&r4KM6vxkf*B{sB=^K?1ksN1Y7ESnLnk3$0$Txz4BrZf~Bh}dly+L$W^DM;z zVR&8KM2P(d!7N$`F{y{c$bm~3V%^{bEH&lKfbw${@Esv@eNly~5ce|VS8$74{qm&yK`p3p2AW%152wG8ZC z(1fRnVFp?kINStq8i;Zt&?sXhA>pC@WQI>453 zULAwWO)7gq*&JmHlwD290-kW+M|Fpf*8zskY?AB^vT<{I@hrPV6GMd4iT}2E3D(im zBj=GV>5h+kM&&&A$(Ao-C#SMqLXqGRN`yVaKA}t~h@6if#DR@LdcwM>8n_`209(m! sihct>ijs|OWPf`%%}qS?%&%DWl=?&Jh}1Eu8mYgePJhx*BLjq!e@qP8fB*mh diff --git a/res/save_files/hundo/tot_early_hp.bin b/res/save_files/hundo/tot_early_hp.bin index e02dadbfa51db9a6c2306e60928e7aef12740ca8..8cb980b7a36227f89c1c28cc73720b3b444a77af 100644 GIT binary patch delta 244 zcmeAX?Gfc+a9}WCG7m}6Vqp-R$Ro?Y!uJyEqTTbk*%=s|4o0#~w9w`L_~o;!Nddz# zncqhz7L;=T?LMpJ;J~QLDJMShgAAk6WM0O3tPAq`uN|JefpNDy0|+PpaozI=4Br2r zhcmWtsxTagE3E%i9K`G}S&>mw!UrhLz_?t%qM^yciIIUxilc$qf`LI`@@XbrCAJR? z{6J|AkeUh*fdT{=+8<4pVpe1n+HAw@$jBkcz;J?@$EbI6D@!jk57Qh6qXq_t`~wU^ b0+Z!9^ce#t`*F--oHn_TIcBpfr#mA6DCa*x delta 244 zcmeAX?Gfc+a9}WCatKKfV`ku=$Ro?2^FwRe%5Boz>%3DREAl6AVB9Uw#{dKhKwS6y0fYDd z@8OIsnkozj;tK0O6$ddnOjcynl<)ycGcax!uwZC1aAIU&l459JwqRgrn0%T^SIO-I z13yrj1El5$h(G}X4DFL9OED`lify)Gc4XvWWMDYKtYg%=xs|1tnTL6fgHZ#6L;e8< cA%@9v9Qup}ll?emF`k%Q$Q-lTmD8OO0JXzEE&u=k diff --git a/res/save_files/hundo/tot_early_poe.bin b/res/save_files/hundo/tot_early_poe.bin index 9f78760af14562a69a0911d61acc5d3ca07336c4..1cba50b5bfa1379a9dbe7b8544b984e2848b0000 100644 GIT binary patch delta 255 zcmeAX?Gfc+uxHR?G7m}6Vqp-R$Ro?Y!uQOyHf>jKb_NEggOO|#Ep)j*e);TbQowLb z=J(Nw1*P17yU%JlI528*%1KZBAj7CQnU`@M>s+VW9gin(VB9Uw00Ig?T=)C|gZKaE z;fyVuDhvnW3hO@=2QfQLR%Fza@BvCQFfJFcXlSxF;%H#DU|#422A$jn8i44av^ifW>-#kMgTzOK<5Ae delta 255 zcmeAX?Gfc+uxBt}vJXiRV`ku=$Ro?2^FvLtPoj;Roq@sW@Wq!CEp)j*JYjP+DPTAz z8>2X}pp^Sh^I0ti2S!UiIk|}+WEhnu^D@q3oz-2K%rbcc<8FCA1|U!X;=1P#7`*>~ z4`*!ARAD#}S6Kh4IEcw%vLd6Vgbz@ffpNQl1w)g86C(qY6hi~E1p`CFWg1PTyfXrDA$idm6SY_kosBO?bR1H%bs9i!IGtt@U#jO;+;<)ue#o zn9T2^6AR)Q87E#4<^J1!R?ESGQJqsxa^eRWMy1KTjPqFMEzdZTJ$VD;Zg~a}Pyph( z=MNaX|343BY~fU4I1pD@|EV~L*bv$SAbghS`ykLy&>t1T&9O@8(vPUS=MqISxh* h3=a7R7=#2S%W>#422A$jn8i44av^ifW>-#kMgTcvKq>$L delta 250 zcmeAX?Gfc=uxBt}atKKfV`kuIn8+)`pYuaQec6GHOcr0Hqliw+mP>G#NNCGB8OoG%#B*Ff>d) z&7`a3_5tV~1_ln0njatn1qd*-Pns;ntjH*~*@oGXk%N(e;RLgeQS0VbmR@Ea<~a^V i4Ga$X2N;AHCd+Z?GZswtRjx#nc0xPJq`0jVkch7zIp7-8) z4>BMbcGfnOyavg_49m^RW(>mZPNhpNz2cRmP+?fV{osYH%b?ef@6zT%l0FLR0`>#nEo}fDUE(E$KSA>njhtcsfXl9JF#) zR*GA4vy-Qct!-xq89QXPNI;Staq)>pa2a?C0q8spuyHr%PA3O$3E%{h4H_hwK6e3B z)uyUyED(vOph>WNW<`-8#*D5PMnzmWE~28+1H(^Ht)*vZ$gJ|_rslAa&dOMIrvp#QmZS36|R!H`irH;st z`mFsH8Ax&}#sGDVKT+IxvF)6L)((1-c(i<|{I|cn0-_IyPA=Y?!mxeYOuroa0Gh}X zeM0zwdCU5Hb2{&+rin4&F2{u*VH~M41TAq7#B)ZD%^M>~HjpEvtQ{8nU3=DQ^;h#& zuk`cCdaoaHRowx-GMm|@g93FOgd~xz-ejGBBpR*SSxxsk!TdgGyyo~|1WHXn@^YRG zpft$fu)83t&O*4TgDbdxHE+X4dkKdAB$FY|*Z4a&K;HPmFEdRqxfU4Jf%MXgT%^6$ zwrSdhF%QN%Ft%gwWglIV7^A$1jPf6!64T8`w*cL&qSj6`R*V@X2u4QG9gT%S>W_D> zgt2&?(Q(`7l`w(sWGIaE@YqL$Q3OB27{WNh1i~bOPwrv#sXWy}@k>hwJ1ATA4t0Li-^;Q0t9gT)}Kz)TP^ z*`HB-voPxyCP@ay6kdl?hZ76zo96v@*z`caA^!n~kU(9}WCactE(HOg;tU1`g@DO^ d9J3gwO)g}P<;vd1z@Wj*z{sbvIe^oK5dc3tVOszI delta 376 zcmeAX?Gfc-h+*(xU$}D~5!q&+f73<0f%=d2pNoT-940$5YDhZgfD~EopJ3B>y26Pulxdbi1G5AJL-XX*On!`$ zCQC6ZGKy`sVRmG!XJlYF!K`D{$^vA7z#SkK5H@6BU}E@haDZU}1A`(1!wP2>kQ@kr zb6hK^vk{AXC*9M-CAOVK&)rbE4SH8jfpNom1{=f&Y7~CK* zk72SuqxfcF)-OzwEQ~3<4y6t!7T7n<|L?Hrfq+B)0}dgEx;2v(I8?Y41sFIOGJqa0 fnC!Mbpz!ccY*p9>m};E(^gG58@?TJtLS*($$%y=P-1=@4c_T ze(%-m>Lf^f%5HhlFYP{9Dj&JJA-iqgztUj8TJAV{FS}*nxpeN)t@4qJO#pxtp$f5J z0XZ1v4c+FZ(Z3|`jX!`<8XG>tL|*_$G`oRuii@^p6!tyci#3Jr{{R& zxcEljA>6_+GQ*XH$AuvUf*ckGezPaRm(J``Hnr(;>RtK#srQ2C1)5tR5p;FIDx$m| zPDwZPg67l}I;Jx=qn;Lk1n6q?Z`@xkwFBTEw0p)Rsv0x4OKk%~Q)l7g5?}-H=RRwd zE_F<6>K!~U;w#F_1aK8fg1J4?n?u`r=Kzpf@#(F!cmI?-XTa&)l3hEb|2BTS`_s_Z z+{%;lxpxX1iXLbFWMax1aT$iJ7_vPg^$&NzHn9*SM1~H5YNg0fZi6Z*8N=PieQh8M zwNY2tF$vC=J-+*6*4SPN+0eEpsv zAY&V@X$JKWIMoATi{}p-sEB2()Dsxu`;7@k?~)LL8nc3nyjci}Lt8*?5^8H03!66y z=+T_N;B0wqOLK1iiaj-whA%UwqH%d8Oov-*u9M(*klXYDqUVMPQCHIVEYvcV^G9+_i}mKyKqB@#}SYJ3vh?(~KR z(!t7Wc=D(B@<3YJc|GklEc)yX4W*?i3tPR0r!5>#*#yI7qU2R6$Ni5HHQHch3KntO zxWhsn6^tAWvJyYHh5FP3Fo)0jxXAd8(y3~!5r7t6-tOX8CBn7xxN^y0YIgn zZT0&NCVAD4tnh>Q1h}>Q1cC%1!TZB<P z_kaT=D6DTP`~W~~*^EnBaS3CvS_bY*2kR<@oGgIKTLG)?o=612*EKv>ofU)2XQ~_P zPI}z?xYO5oqSoWasIA}&jqI;%n0@==opVwFHOAuwk^zSSsv<7%J(S_G{oX5C&2oEq zQWh2<*RU!qi1}3XtH>c+;HB!uiVaijA8Je@YldmZ%=Dj<{?S|RU_2Qg~iS40W_I_T7jhpVW6}*t0O%dro=k$fb7* ztY!!I%X-Y!Xb6slqesbu;t~kS&_36C+N3+zIg;CXPaxnr!kgl$LVEFh4iTix)KsIv zl)fHKg6;s^1jsy~MJsEP?8Gzx;NmY*s;@Ocn~*S>uGRChs+(hd{rTC0Jo8KW zm|E)l-xo||sKBV*AlbpecRw)NvCYLtl$YA@A2;<5Bp((C$tv}~p3dvV+cc$yp98As ze#BZwmDLPZZnjS{FN zlo1gTJ(ctlbO9mgVS{cKA(Dh$H8v5Y%hF9Fbo$S^sJj~`L{QX+zxl3b_M0wQN9-ilss<79t|s61$BN zgUjtCmS=lonqtI=>ZTI0`NoX!Ai2g!I`d_imK=S)JfF{(Ma-#~L4|}_PNb?TVVQrH zv~=to*z)!&no=sZ?bx2w+|pe;cWg?Ej5=78{M&%hUY@yN9VNU@*2Rnxd za?BVXlQwHTut(~dE8f?XeT|PehtJjS7viIg8N#}aZ|72Iu)T3JzwXW5QB%qBG&@b=V@ggCSRB)_I6ej0Wrb&0J z(?b(R?BAKN=ogZ|d;a9sZow6+FLI@D?@Yys-}W5g9Evl2uiii!F-0%s_%D-uvC0xD znWW)rl~#em#@(LsBl0J3mSyT+6V@3^UGmZOifCNt5cO+GG7P?fuV4^-2A{wOkO1$% z8!!O+K_BP^aqtRsgO}hrcnZ3}W6%j6fez3P9)bs;4cu$a*Sw23kwmwMK}&d%sNzK< zIXn%+Yv2@$vwb@S?$p>AUAaM|jcDkkkaJo$~A;I1XySNh@|9b{JGx{#Mu} l(w5Sk?@u(XAzU~aOi_rTuLd3{4I{3AuvvwXDNn>m;yY|{2($nI literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/arealfos.bin b/res/save_files_wii/any_bite/arealfos.bin new file mode 100644 index 0000000000000000000000000000000000000000..cddceb9afcea5791ea0dc74310a5687150b28867 GIT binary patch literal 2700 zcmeHJO-NKx6#nje&zbQDeU4LE!t|^l1)-TN3|jccMw`H)iL|&M+!diwcnrGv|EgeCOUd_wO+9 zKn1IB^!|VZInyNBS6Vh=Vzms640jI|AIf$DM-C9l?1YJxD1x_#zSKAuaqCP?!|9WL zUpZ01hT~yhMHNuK1yfV!_D=R*K76!pZg6?p%JClr6m$U9a~gu9I-a=)r*(UjpN2bm z0eUDw##m@3B!S=niJ0@C(GAP(5m_8S})omyxPe4qU)uiTHcndVX=kIh(jXUPBU-Q7#Z z3s3IHhs8OHhj?A&bj-(aYqNfY@0Ec~#V5~`Vv#kTbZn{X&qifiW8<3>ldaACbX|b=r z<-SE)vcMV6jX=J&)p>MxS=P=?dcsVAi?X5l2 zp~Al0A|6Anr7e{WRnnlyF*?0`UDvE&B*~{iC7knxePPEWqsy#uS3F&g|4aHEh#wa3 zm6nL7C*opsyjzB-4_G4jtX00~s)C=1^J0EsMN1WaSv=3a8*wLTK7}nD^1en+GD>f~ z&-RxWY+F(ZQIVCFVbbGICynXIbvUuEb*MiQ`rGrT6lqM5YqpgbirRW!k?FVH-G)KN zVERq{4t(^M^!*0(Jyv4{Gq~N+@euu|2_N*6e+B KGcWI7t?ef;5wx2C literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/argorok.bin b/res/save_files_wii/any_bite/argorok.bin new file mode 100644 index 0000000000000000000000000000000000000000..8c73f70ffba6207e3258a61b320a7d11214b475c GIT binary patch literal 2700 zcmeHJPe>F|82{ePw7aabj_X#IFpVXoAhc_RhYr5ARktuHGlGINf*>R_Dm@4~yA&1( zdXPF53i?Zw7V`^1cgW+yWij6d;5ED-uq_3 z2P%-d2JJgYAiGVJJw?SsCajd+q36SQ-}PlU0S@lNA`yVGwncwJ_SiQ6H`K(a)TqzPrLq8C9TBb*)MsDOw2KnY|Ck7-TI(lpHt zL{>y{6PT6@T?U8cC7r3r&qFjtAJ(V<0JP7=;n4KlN%E+u7~&K<78h$JyZD=y+|PajFZFU<6b_YB$>SU@ zP#uY>24e|dcuJg~2G`2Gh*ju}!fgOYsfdj>x_%Rq)K!PmLEvMJShGLopT?&(FWR#M}C;~6JJ0Uhw@hB)57z&!Y` zR@njTVS#Mb?WxO~GAoMD|34VM(&=Lb^0dK4Tx)_cSY^mE(=`LX19C8(s=$?o&Ab$b>N+@Zq SSWK{yHF+P~$jkdzYx@C~5VwE; literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/beast_ganon.bin b/res/save_files_wii/any_bite/beast_ganon.bin new file mode 100644 index 0000000000000000000000000000000000000000..f37d9462b5d8b6e88f58a60e603f2b625aecd228 GIT binary patch literal 2700 zcmd^AOGuPa6#o84<1{|zPfaah8monZ;R|}KyyL5pOtgd+K^j63lGy_#B>fdbg-93T zBBDi$Hc?1e5=E(%wUFAh6dW^g5uqTYmG*b;z4JRhigY1F9%jztJLlg2oco`9e#F9n zR#p+;0@dwuSVU=ssShozq?_HhijLH`hlc^VZ6=_|fP#U2xj92$N{7NKZ&+~!<;P1h z(lg1GSAMiO{g4mH+>Yl5pI*uHXJx(aee>Ofh{@OwMMndW|H+HIM+|+@_lB8Vl|jqH zcmmAt%eSl?O6f4q>jGz-Tn<%(;+Dn|t}vce^Es$mpU95qs3S_~U<$TMjdPF3?eTbA zP{MTyMmnva2E#BwW&T?v&;J@Fr4*g5u3DB{e$Dyn)60-WgN}gzP7&v)!AX|MNHd$c z-7i9@4VKcvE~^nxcPz!Yt~{>4#ARaXRJHHh_*ZS=c2k{Y&2UVRGAerKe#Y$k_Db_f zY}DADqzFBhd`t!a4U+*Bj!&LYdQ(ynU}&6~Ii}jqb;{q=Cw6ecaD=w%=@02r(*tmm z=3I$O+dylx*$p@jNZVW$7Oli}0eh&Czy?=&|6M>;ua{AF1y$1>oi%qKSTQc&pyPA) zfXH~{?|IW5^YN(NFrpDlr{CPu6z?U;=`H;`os6qZVKL7JRHMGOb=aTK5tYa>6bUX^ zI1JSbT?^2Q-^Q3%fK4I)pEYj^_hlBRzjF^MV>-N`*3A>~wEvd=h-snE*1w$pnKidy zxh|q{K+ZGy{BZ7%_V4GV1lj$rzc${FPMp>=@R;?^NY2~Ncqse$tvEEa(@9v C?aZ|R literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/before_kb1.bin b/res/save_files_wii/any_bite/before_kb1.bin new file mode 100644 index 0000000000000000000000000000000000000000..5c0883f5810059d869c10e32f7e65a7ecaaa5793 GIT binary patch literal 2707 zcmZSJXW(Jr4N34g%<%s|Gvj|C(AU)Y|DORJEIY%y;^R>TMn(n(XA^|vk-rBRTw2$D za%2C%P#WqM9~@w4U<{Oouq=QqQHEnB3Or6C3=ButR7x+Gg~>26F|r}dL1%#+@ZaOV zE673U0OCw^QLvkt85o*?IvEv!1Pm}i7!EK#n3j{{my?s@Vqj!ufr-QYEC3V%dULP= zmYz@LU}yPc=4GQe&b1&vFA+rq&H-uwp*SE3)(ez?c-R5RY5;{5!vTnILB2+Szw-a< zfjmis2!zGBt-2V*k`8>IN>V8VVBtz7;|Lf7D*gWlDu7f;uz+fF zp$3KqAl1O&Py!@?5|H`fBiw_PkG%yB=Ls*s!TnwTNho*EbaZ#NSb#%?{+p1S#G7QX&av&411CRq8 zJpQ|a9E1*-flkE+fLb^i7@Axe7#J0iOl5chJ7#NvZAmWS+P(L#; z2mnoCMo~Q|IE~#}2}?jO@yX1~MsbmAL4IB$iU^zoj4lw01Cl@*3?LqM0J0mJKmrHA zN`T1#8T^(1Uk~I`#i^=kH0bho!40Y|Nm!@7z7BP2hj}-!sj`Y z8+M8?BrZ|pVF20b01}4*2L>i~MivDgs4x>uoLHK1p@0HIg920^Gpgf(hHx+lK;s=` z;)VktauhHyFfsLn0K>s8`$PYPf@SOe3v)RhVtZ|W5F9plMnh->gaE7@fE97DyayA9 z1pz;V;Q=ZKKmaDs1>wT^P%fN*4k`dMA4a3AgYj|D5B$Iq9&-B`fThF$0+_#PAYKHQZGz@il-BEi7O c0hWOij30m!5Z!RG5y4=*a$rRGlg6F|0D@eH#{d8T literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/boss_bug.bin b/res/save_files_wii/any_bite/boss_bug.bin new file mode 100644 index 0000000000000000000000000000000000000000..8e201da28105ace906f06280eb279c2cdf30f5f5 GIT binary patch literal 2707 zcmZSJXW(Jb3rTR6XZZi08HoS?*Vokf|DOR27$FphTy`xY!PSn9kr^QbVjVxL`Sj?) zYmeRTOK{&Ub&C%UFf=k`VBiI_3{8P5L>XMew!S+i#PH_W?XGJkb0IQD42&#{a;Rn@ z^FR)8@c8cvau7OTW?)1Y0`q}dfUau-QjCg7X7NGT4oE@_403Y(a&mH942;YyFmb4# z85jfv_!-z?qJxEAb}|1|+S^Sa=lEpiWurLDwIDw)5k&;f0mdK*#Q{km4F(VoI{?`Y zps->%09L{v0AjSe9dgx&bB*a}qy<;Q=ZKKmaDs1>wT^P<3$rKd3y+d>9QC29?oZ zzylM&PTO~a#T^&~891=3ppz)f-?Xxh*5;$Sy9LPoAG)G}L7qW^g&AnKPy<5)kZNFb zC~;_DP+(wy6c9iXmN38uz+I^TELuS#5-f~p@uK(vta9WLj8_hfJWtcY$GuDb|4#=1 DL8p;B literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/bulblin_camp.bin b/res/save_files_wii/any_bite/bulblin_camp.bin new file mode 100644 index 0000000000000000000000000000000000000000..1e63d35fdd967287bded53bba1a71cb52d7b7f34 GIT binary patch literal 2707 zcmeH}TSyd97{|Y}Gj5uu+p#bko>ElFG#{D&d~~XJ*bWyGjNT1og4M`L1*3J9GA&UGh;j zeOp&+c2S04ZUI@ipil_1gxN}7FTFp~&5X{BX++C2Pw!HR`#lTZ-MjrHQrR~<^VQzU zEo)bWibF&*EGtw>#B!;^y>i8!dC?1%Ma@q)4%sy&L}{!=jWSWo2Fxl^A;dBv%4J(( zk9EbxouLuSQC>n*n3fV9nt_KdvPQJq&$dT5nLjP@9KS!u@Apq9rs;N2C!uER@puT+ z{JZ4R!^Xk7kJr(ZUcIAXXHs(&yBiy}CPm^7<|IFxaaGf^Cv2jGACh%ptAsq#j>~wy zI7sB06Aw5ByScwfnt7{->X(j1V%qrm)_p>Jj@Pk1>$`I$G}7C#jo`SSNV`P zRZYu4KUGBrYS`f=7pdQ*gTqa0G?n~C&91Rda*>HixhA_MojmSD{AxI1+ZJ9z4y^Rl zxb1tW?G&9Qaxb=hKCz6&Z#5?-|K^Cj`~edS?xLuRaZb!@H_WH*J3w-e5zQlym*lE? zZ}3@$^VNDb_5G?MlRPeibgdt&$Er-jinprlfBgsFfU{Vnww&5ODy|+a_Xng3=YSf+~1%*m_ql~zZp@(WYy99OBb4$CrWX7Vo8j#HMZv2=cNJ}EbuQ#)DkCowhe zpOpMo>Q9fK+}vHbV*O>V=a=N^8lLSv%sCWiX0zE&1~J_%=J=IKK3HXmlyow1v`TBX zo;U*S+CbF)C6U>GO4#FY>U@uG(nZHcmuRaY$vF59#=r>p3ci405CxyW2QUcUg8}dk zM8F%+2VR4h;5m2(o`A>T5qJoCK@aE#UEsmVT*Ft!i6o{&7+S&$#5!IO%i_VpbFkm? z;CzDzbsrB>5ib}ixBZL~=yF~#Ix00mTBp4@08W5JKujj|Cu@S&p9=W+dE zZr1zj`I&Mq+BM6kbu0+GLSqNnox&mrYH(pZs~k6lp;B`3m_q?I zC7!B35a%Y7gJ4Id%+;H+3hixh1t7&dN1)GhbFgJS0BZr9patex7q7x*>)MaiRn?FC zyBlxziJbJZp_DJ)L8Icgf3BNpP)1&BKy^SaUj4Ry-*OkuG+yApThWO6Qga>++SHcj z_L1rp{;WLGL5+b1WPoaOfh+~_?qzG5%TW5__W$g!U4hum0x7nF_2^l8Yhijma8zR- zFotDMpCiZoX8X&#nBB^6%~ngbXAac(wB}WX84ltou!Q=Bqguc)_a3 zbhGMA3`*q35`_iuKjm9Mir>2Y?fglL>%%CD^98!BHXoKHT-$L`*KyLBc2?^EH@=s( z4LWG+jK&J!GQ-gYTk0wVmjcwyVTH`%-4nfZ$RAgM$@5yVnc!bW_9ifQdlBB@jtH&; z>*W{G$a}k4gm;q8N4iU-E7*29z<)|YaJ=k-b6sam%xaK ziyzWqh=(#=i4e}z1s}DKC&EV($C4rFN8NjZ4+J5CFu^FnM}jeefZ`W?Q2E@*>Gtv= z<-46fQ9c9DCUICPw)3Y-xREjLdcuM$5VO+ZXF#Wl{pbnq`GY3fAA#?!_iH?vJEBK zSxYgMU$(9|YX#txQ{mC0ZHxLEDpnPgzHa;4&ug-m11NAD!24Q*{M!`ny4w1w7?q8~ z6Bq$n=<%9n9#Se`ybdv7;;>0FkW}HsAfb$v{{TqjE@#I$Dv-i$pafEd#nA0eb-UdT zAhIHYlfaBvpsFh1GAEdjys6IKQY1x+Dy#O#k@Hp`tlAwH`QhLeu-{HZwi`^uGAUZR zpZP&vg2y0MM&_C)0Hm3*F=mrHwTGFWy?&_5^JVa(JaWl6W}0CZ<3e8YvvWQB)4=I+ zy)!N4Rm;3&B@J^_6aXB58-lfiZ?}+J8JQ5Ga3UNom8|4%YSKKp1A4SPToewKVG#Hzh;IMJ+QDGpDM?Okp+r~Q0<@_5HQtZF`}E=a@O_un+8Yp9t##wTE>%rp|FbahK= zZB1Q&;f%(V*(?S{962O`Drum|A$)t2Db;w;h~~$zGFUFBIIGy!M~DI%bFHh(?EfREAXbfjArVjtjQ{^jxqQ@v=!KUjDIU*I3D496LE)<9Lr_8^`+`tG9ltPPFTf7rGMd0mbjpSd@#N z_oB&g?xX1N^Wv-QTHIHj7=C!Remd$QUwPuNcV|rw(}r_z(;2`oEEL08S&vV{>hJ(S zcwn`NRkRw;iao}h?Vt4xElv1bM*DrFK7jQX@c+`8k2-*g&8GO_x}jZLBxM^#EHanpVYvVz%1W3R*l76x2ZwRIC-R2SGPQse+J# z$D4va_~MCpBbe5DJQRKLQ81d+f<9O*cr|tW|Cwx))bl|>#GmAw@4qJh%+CB-C;X;-=PfSQJ~hv2k-=akWib8+o2(K*0^6&l*G=rKn{($lYc# z#MMSw#0R8*oY)7TjUF&$2)njtJJGRA-ZWN!Oup0mPeykO(K`^wp2@RwzT*AkL+jm} zB}LCqk1sSz(C>ILd%F{`Albc=T`4Pvgai9}dzWgS^A|^Us`v)9+jDVIn5yR&k8_w{ z`La`8if6fEMnawjbFf|M7PK_OaR48kU=&wkE(F<#qW}}}Ab}Ilb^PUA(+)4k66-ei z#9EuqToA+j)$P95kv3WO)1McQcdVMCCV&-$65RdfOVN=wjwBE9+iE&QqXolmw`ta% zT`isUqhdv4$p#B0N*D^3W`m(8@aYvhjxCMRtpEGWE+b!ML%6|rpD_b$zz$X;UnlAEz>RQu@6Y`{Qg0@*Hu0Lvy9)H-y6M_>gnZ~ICVBMN_OaK zki0&02MV)Ryz4Qg_q|bLX#~8AWLTHL%l{);_ao;weBQdg;rcD6KRth5k!}a7Vt1)) zSi4pmI{tQWzw6@QGG~W#5W@Js>{PnoOp=Wiz*gxPf-Q9nf^7nIa~Nf?>^@_Z3*&q6 zRl@kH6{`vUWn@p{gO``)2g7N>U0}!fesuCq2nccERz* zC!*9!Qt>q^!po|*w?5A8fzXy-zz&=tHZ(31-R6Q{?^+szLFe%E{jyqMGub4q~UGgD1GNb0*}RR@2cta`*h Z5c^2%6R|#GX=44vy+l`n-Zm&qLPQd?tHvgxbXhD>3HS7$b6MSG6DfkCKK3{BUH&sWbLN~OE5*_J zvMRZkB8kN-unlSHibBEGJ<*u4Y5()caH70>3&ozkw6VtzhdhNNNzreP7gp{pab>!Q zV)HAvZFjlyh&CotPiM`|p3->QTDIqOlTX#SvWSII#lk$no>+ifq!fq+Dfw!9RBh2j zn)Q_$3B4ybF`|}OB8DaSEl%@fW()J@$YzVT*=$k7%#xOrNWoy$EEWr4n}3#d57bW{ z{CEe8l8S5U4hJl*;7EO4bwI`+U{B&#f!5fbwqY3(-Xe5vRYDfw`)xi&X(BSM_9t|Q z-9OPF+~ggfYjfv3KJ)yQ=0-*N;@7be_1(GQn(6O2z#mxSs!jGaC05jBLvw0;l!`p# z<*aqfH5!$vX{w>rY!sK-v)k+vnMlT_OreHEkY&+n-*N|Q@DSpWiwMqZv4z^s(?z1N z&05Su%l^u&cKi+*2W}#-iE&OWVcFYs5akN-TCyaNIMsx^e3e~)wT886meWR)1?MK2 z?PytqY*)wV(=2EFf6Y%n0plqfpPcdi7l-wko;2Vch4WVxJL+;UDvsk zl9eo(@!tup#L;mkACue3PE3-;8-Z)WcrnBTLZX=+SF7-nB(~`G6k3N@Q~Yx+8*~Tn z`OH2^>DX&h+R8~V559ppFaxH+S1<*<;1l=&Ccy+42V=kk-hmPD7Q6;8!7vyCgWwq$ z0R5m3JO#brNmruXn!||%vW^?QfTt>DJk=M&o#~h1zvs?%cJ7p{+({WcbtYenBeT%w z^VHc{(G#pW{MAWt4%C7RdhROxY*4Jnhu~)jUszY7!`r!=$V@AkBoV{9+PO0`8+i$2 MYbwzyFBISOcfbk+vj6}9 literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/darknut.bin b/res/save_files_wii/any_bite/darknut.bin new file mode 100644 index 0000000000000000000000000000000000000000..65f81ba7693bf5399d4b047c509557910a26dbe3 GIT binary patch literal 2707 zcmd^BYitZr6h3!n)a>eZ$0|zMj>nG@No~C%k+@y$T8T=%B{r(5kb2f5M4ISqNt8%z z2oZuMD)o$zc=pFq$`8>5Km4I9el!tDH(AjN?Kt<|nXYY#`XLcccFuQR_slu>H47fd z0cowL16%($#;DB)z>`6I~9lwyR0m|krhA#1x1G8!;)sR zx;8V$$dP7~fVhP69VbyB05!z`5_W@x8px2rWuU~vbdx+Dm&fCA02v?)OS)p15~!*Q z*ydkG#L*?a--$(~&8^tD4!k(a78O-)+E`{OsfaDJ-wNbb_o7XTae564j*5v=ncPjC zBaLcYykXr2Qy@31wlXn$e%tzj&+odV&QqZshLPxwBQ1RIT=BLy??4vh1h|b3VUnxqd3*mgs%4rbCXP*urbU|AN2D6%?7djWfWALxwwt!IuN%gSpRk1qwx5% zsL`OhAroiceM29rAu@V}ixE~R6GQz6HQcwURonMI4P+h6$YwSOa?aTRP;DB>QVds* zP^xibDCz&Xq>W>~Nxq5CTyMx*04*7c`TIrFW(knay0LFfjF$N(X_j*OxYdkdx|vWC*+K)}RiSA@{p`fM;CEdEc4Uci%_+UgV$7 zpIXgBRE@>;x}38uTbA(M?mfEBN+5I_t%ZF2V$%E=`)(?HV`%6?!Vo++He|UD2pfM{a-S;LEa;~MQ@crViXc*sAZcj8Gt{sZl<}bI$i&kvUXBcv>i5!9{ zd|b#`QH#%k83{a2iRKy17;>(z$7fY&#PSY(;n@g2=TQD2d=+7gKZftiWjbOQf#uUg w4I3J;0#QS0Nuntjdr|0)EJ1Ac2V>8Rq%$`erq-}F9cx#ARRfSAxkgvZH#%h*Qvd(} literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/death_sword.bin b/res/save_files_wii/any_bite/death_sword.bin new file mode 100644 index 0000000000000000000000000000000000000000..5e378b9885e3810d09a9062538dbcce113a4ad7e GIT binary patch literal 2707 zcmeH|c}Nsd9LIleb~KODXWBmSi+)PBIqE9qUb=YKa6^cU{cUQ zL`3vQRP;~K0YcCpD|AOmBoSR2CekfdsEEG4Z)V=wu2De*75&)H`# z4;6-pMub<+ofj%7C7O~&;b&Klh4VRGkJL4AvTH)cG%87h4`y!5XS6q?xne!^V={wgFH7iRv9fe2mqx7W>^f7wT@RU&Al^GFEHmxcNTh zEmgCtqm?3U9p$Wc`e|ylXm?k|LXS%Rq-Gb`QPRl5q_l7SC54RS!T9;;pzT;#giKf| ziE+m_sO}IQAxfU+_cx;9$Py_jWa4U-R<_Q*x;%L7D5XgIyqPDF?Fxo%@y&E}SH$>%=gwJv>-gjJOVp Nohq~}Eg%Nod;uAL4MhL| literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/deku_toad.bin b/res/save_files_wii/any_bite/deku_toad.bin new file mode 100644 index 0000000000000000000000000000000000000000..6dd6dc33bf6fe03be272548591d12b38d6408e5a GIT binary patch literal 2707 zcmeH|TSyd97{|XeJE^I;o2+CP)?H{91S!`F^vTKlgN2e6m1q(~k*HwdLy<=jN(xMf z=plM4>m}#{LJz_AP3??Sth-jQFM?4Y`Ql~iARoIc&WX;L(Q7ZFaD`g#dS7}tE z*2(4qi%iC-*)4XF6w>5S3vWvxTikr?rf=Sk#ll`FbODvwMm3M+roW`dIDVIg16L@Z zFwTjk#ylqWTNA|!*%T6|ntF$?a{lQQMCE^3VmlxYOFnXn*`JKdcCCg{Cf)-=WIoYw z!@gM;_Jd|#t}nG2Cg~^3!0&Z_XDF`GN=k^VuyjTHk-0^&<=l(1I2K*(^`ralfUEDk z&M8A(qgwFYsU5_jIMa@5oSejr7X+^IhY?yJ+@%w*RuM^8S;V=BT2~-c`R`rWkZ_2p$NcSUm80sMu*K)S{pj z5fSl8#TUT?M8Ss$-Xf(~O7Uu?O0ifC9#oNb{Lk)A+N7d_2r7Q$H{Y4bH#55v3Q!gc zELk&kE~V!)pMXrBRC-a6Bg|EDJoD;G)&30Kmq9eU{$7nr+34qCLNO%^R zrb$@lpCxBnxAd;>yNITYc^fxxN@}ih>z2)HlOhQRTOdEHFv{JK;x5yM#p|UXb4tjR zc0vY5x>-g0`-dfEX zzaW6Tt!no6bWyasXE|$_eww;$s(rV8k*<Kno_!tYPAf*FvcRzUA=mXa^vu0OgL)4_aEGIu~$R<;>$k4lRdMs*NPO5HL z!GH62?SOY!q+U7o{;0V6UHN`MqPPbfQF(Qi%J=FL_sgJHujW@;U8%}1_3E-couXzm zIRTqSCBLQmLCQgfy?^U{p(r_@GR;VL&B)MqBWj)BE&7Gz@18%owHt87(zDzs+EHle z_-%g!=TMy4JM4XA5i{&^?)*!r2SZLEOQd9yg{xIsV+}U!_LQ~7aU4s^|A?&o=Y_S! zQrEbDr6HPDXrggBNd~|d@EP=jkKhA%2jbujcnx~NE6@X8f^P5}bb@E#33voLKs#sy z4?!zv0S~}^a2MQZF0cYKIFZEmh(SwuiCDr*26K3*^aSh|JT%ebAtS&;RK`pCBd+h8 z0v+KcpFT-Vkh+Y=d%z)34UTxRQ?M(*JkQ?%yG+_rnhT2K2bK{ooHS+_#L(A19xAUu Q+yE7B6-JhsBL@4v0_J=R8vpGBx4bis!LrG+k(O#eBvv)gK74+Rqa?D_xiznnAQIdkS; zNClB(ZLP3>2QXMoD$1(NtUD@9mgYN`jurQ>jW+{0ubh1iL@Vf@2!QKk$48HO9FA=B zloxNx$zF}AEr@cn*8`a6fvY&w>Aclk=YD#yYtJIB1~W#e2XMcYz;%y8m#uw>f~$OE zQFabaH!}jXz~NR^r>eRbE!E34DwDx#1d^<~5et;D^6du$S;yHijshfc43xk^4KZ!E zC)@4zI3Pwr3(f&Gj6f7cz-`VlA**^MPHsm?$YYe)z}G9t_5xaNz{;DZYQChZ=9Jf+M=<~IYZz>5SlFeN*C0|vvH zt;o)^Z@kIxy>?rj8|QdzdY|9N8NZyFH=*EQRm(xNK`NfUuTxndp=C0N!*8JsD(1Pp zeFm|vwqd9sttn|Si$NjRT*ZTEkU%g7@$IEhD)FEa&HqP#?F7Ov3;VAo`lqMI^#`-> z1Em1_02e(UH^~pzMf%G~IIpj-TfL>~epa|nyBlFAYTgxbTws3$3DGEi>rI;fZA`Nn z`JglUxomn|F@5Tn8GYZsO#Xr3-(Ek4qTG+JdNe~8#M% z&6R literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/earlypf.bin b/res/save_files_wii/any_bite/earlypf.bin new file mode 100644 index 0000000000000000000000000000000000000000..fccfcc7e9490974b5e23454b99be4e005da1bb92 GIT binary patch literal 2707 zcmd^BYe*DP6h3!n(%IG3X)`pmb*!KVL9H1TL|v_24KwT=B$^n7J?()8LC#8$QQ%S( zK~QUCSP?{l_VCBr`$s5LP(PwJ`XND}u+iJ5bMM`8+^tB0DC)3t&UYU7&N=5kX2A_9 zK$flXv;){Ahk-O@!Q@K@#M7Cw;u+yrZT7@i$K9K6g13*iRQgVqm61?bTRdmpQ-DJ60yK!2Jb+=aCw8vE8cTo{<$DdI08=Vtd2M5ikt4w- z0ZA3gAWlL+02)dFB&-GrHIT7BPL#N1ff#qYQ{8U214vTTC@Y5PR#g?^%CrXlIrMmZ z@bdwDeZs=(EgQjuT{g*Evvo_Qsl-Zb0sECBXWd{V%A|<&2BwEZqf~|msDHFkk2_-2 zfFTM<+wL6g!77peb*zs@is199Xz-#ZsP@4EL_=~vTIUYydhO4HC!&6qts zVJLdpvy$wc)dFD(^^wRD$#f1;l4f!Y3N(sE*}gYK0X4z0Ukq7TAUO<9L`u9}=ql`M zfTP&z8A8{;33HJ&Y}i<4r#HUw{>=ul{FM|`>|CFX=sFOWNLc@Q-Z=pIENTp>E=a@f zyDsbL8cIe_a4`Z3Wze5EqNCHM?ylW;zbKN-n- z(4Djqa`F2>bHnT_q415Np$iE^@UFBW(;N^04Io1%*6pdW*qlC!n;`Z(!p8pdL>`6W zYvg}GlvZX|u5f;q^Q)Yn7LDue&@PY zeAfE2R=?Mmo(ke~8u4r4s}5lNF?`b(<8Joj~WXxUtSq?y)UB;K5uL-LWo&W#< literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/eldin_twilight.bin b/res/save_files_wii/any_bite/eldin_twilight.bin new file mode 100644 index 0000000000000000000000000000000000000000..642edb3fbec21746b4305d5b633d1b30ad6e6b40 GIT binary patch literal 2707 zcmZSJXW(Jr3rTR=&G7#}GZ6p(udk`||33p5FhVF0x$KO;py%m@j7UNt;Uh{7Ad10t zxdD)Liw_PkG_XWsu`-A<9G4b5>eAW3aO}>OYu_$F)L1aEF){LibYce}2RL~AcLg~J z9WXO6q6>lfKrKMmHGwEaBvaWSYzHJE1_n7femOZgE(S(s7MM8H&kPI#KoMq`=-{CZ zyv@J<06E7eGcOy(S*`{7d5I_@a1Jm)KqwAK0%2`i zzaGevgz;fCg9!57n7>YoOg+TxmqSOV98}XaR z!5{#QcaXXb2k@&NNwSdWPjmoAI1|GMCdP&a21X%B05puGa2TEDHuEHc^ISKU&W^_i{ZrR;a7^s23)~&BVwCHUcXFa=?F& z|E?ehp#z9B(M7>-W`-EXr~skCBol<~fF#7gAScH!Cnv|nz`)1?6NmblfdS+!bbrDW z4Ir&F_Za^rko`WHdD$rLa4pEsOGFWYbAbK;p*SE3^Ag0v4nQRhO(1m#Aifm;De?I=k{C8HP!vFuD(bbLeM?+vV1O{aYz{&w=S;+t` zAK~Q#%+dT1h6kt|00Edj2yc|6RtV5HTtTJ(e@6w7<0ZgxD%8Nx0CYkFgF^vG5EnqW zl7j(URR}OJa-b?^Y(V9Y7#`!51L%I;4iey40Va2V$(3MoCzxCXCU=3!)nIZrm@J6+ zUckxGa6(0hlVbu)lcNGi6bu@i?k#BK&2j*8z%k*Lxku|*GUw;RlRLzL^n15t&JV6} zWkw*nkvDz8BnDHkzDC~64j}Ha`Fx&%;q!SLkYFQk+6o{}X_Sfn(Ga|70T3?%vfn`D zGA97l&j8w|0pbJc2|x;Do&v-TxqE;vfVd$(;PbJV2bW!h7K8!m^KLN@F1a!<+ySRCf3EXOBO8FcK*p%}9Q8sO1}(HH)`E4|T2Erv1e&|a#&l|% zYTo6Noy*khF1v!4OP7mUIxLYqYZqd7!VC6tEbN6smywxkW%K0>S|vHw@%wcgxI$5d zaZW7l-&^J?IgLM1ro1xp$|-gYGA>JUTSmF~4hWI`NtWyQcAn$M?0z}F)OMJppCSXl z*M(hXT&E3`7?&aIhzqWuv(nF7>G|WgrW3BQ?-r*lb(8ACv(q|>Lvf~`)H(TxSsw_r z^2IPGaH{m-(<&kvDqD0eBG(@^GylJa4TqO5`uri)=sKtvXAh8I9()ILUO7h5eofxA=Ha_3|L)^1w{7<*(ZgUCaZsvqBT3J>k`9a1qplOLpu!?0isR q`^RA0|D8nL>Hg@2{e+9ZhN&ts)OC&r^YRf_LB1Gc$y})s`|%TY5dD7u literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/epona_oob_to_flight_by_fowl.bin b/res/save_files_wii/any_bite/epona_oob_to_flight_by_fowl.bin new file mode 100644 index 0000000000000000000000000000000000000000..7344b995198734ef9c297351873d14d186517899 GIT binary patch literal 2707 zcmZSJXW(H_4@q#AXZZi08HoS?*Vokf|DORJEW6mFy8B=&BQpbovrYLDn6z8Nyi8ZW zDJ$LCzaP~5?-m~%U}$6rl!UPu8AKV5InI-DX;RlX`u*I=gQvhU29^vA%#3_6Q!r_e z0~|d5yMi2q4wxAj(S^W#pcZ}xh9)4zsEA}D8-(qEB*efVC&w=*C&$IW$jky0hx=JT z0O&ZF=wP9jUCe)#_I4AuNa2XZBmguqP3ZPmpfhIHWj9LJxJ|1tla*I54l|7W;5#{1xK>^1y; zzB9RDrwCgjlY&fu2vFVuu7(N>#*O$5;b0Je#yd#eh6DIjk0e=0^eZ|5BbFkb#3zqbSl3^EX9?;L%KL^HHq^W&i)7D?nAH1Pde3ZlMN-1|ZeI=upDYz@Wgu w04X4VBrIWo4S>5+0a&zxL?jp((c*=%0jzT55sX(3j66@%!pBWN|Nk!n0F(rVJG$_VeD}o1G#Udy)T15|qb{hgJ zDAbFm;id4CHL zSOL)8yYm)6VA3LNZfc&ipc*H~9uDL`d|qA$uxpF!eAH+PrF}e(W2)ysXCl4>S+Zwu zCcX_xXf;et9e(!Y%84%>UB%t(s)$K!1<+tfOoikaht#oAFy*WSS*ZY?Zf&=$HY}-w z>N-P$5b!A~u#i+LBqm@0xjq1PS7b&FzY-K2IEyWljASepip639V5%mK8dx?1UDpBI zTx>{nKYmVqDd}u~_SDge^7dp0vi;chJYbC0gdM4H$u>D!%p92u=*K}PUdG4zxnca& zy0XD0?9Sj>qS5`c$6LP_zp8ULMlM)ZsT{|q#o4)?`1bO0(YzN4zRMXKIwQCd+=&^Q zc?;>{%psO<+5`m-!=+M};y8bEWE1*!Xfrrgj#E`{@tDI8y3b8DQE<6rxCkL+{%X6_ zb-0*=>i|AF!3a~xN)cue{N_e7@j|6z(Q~=|4IDRIflmrE@z#W8)#k5@H@G+~&%m}@Yo;Q5b%@`2cG* c=DwZ=#TutkXf;nU1ut!|*M+6s;J@kaC*K*OzyJUM literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/faron_twilight.bin b/res/save_files_wii/any_bite/faron_twilight.bin new file mode 100644 index 0000000000000000000000000000000000000000..8e1923d98215a9478e74dee0f802446093b16460 GIT binary patch literal 2700 zcmZSJXW(IA3`q!bU|{?Y0s0y`|Nk?90V9M0ky$fuyD+3UA&G$Hj{H5q;L^JGlNq@f8Ud;lm9qy#{0ICv}nw;srmgbP3!u=wW? zIKaU8^N|zN-^NA;h7HW<>PGpaAut*OgE9nQSs0dQp=Bnx5I}Y?KZN5^jVwq3N1&~iAzy-e$upWRop#UU^3m{y{0jVkk7#KNlDIQ_sj8_hz`*=G@ zfMW%i+yN$6g2|m=aut}|1t!5&I>&A>yCC9w0VhYp2^AqujtMMHjtU@AFley3Q_*Pi zB>~6*$Anwv9<5`^oSzR*?hpsk@7$9YFhJKztyb0i+fH%~OE5;rkt+ q3m|TY5BPj6=D}qbp#@<;`n+4rgG;W=3%7x2R5yIl0lM9QkQ)GT(aQAz literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/freezard_skip.bin b/res/save_files_wii/any_bite/freezard_skip.bin new file mode 100644 index 0000000000000000000000000000000000000000..759aaa82652559024f729816cad512ae5d76451b GIT binary patch literal 2707 zcmeH{TS!zv7{|ZaJ*n$$w`C>6Sa%~ABq>*f?8#%S=7WWqWf6EGh$PX%qK6_!F;o)D zi0C1DO6euK67;rFVG<&eh;-A~M3gRKiAuPq|D22NE`~lN6!o#c`7ZN6b7pqt3^~X~ zTS_bC9wfPi;@AEgFki zpQ%yMJ^6_dH6;)+@n*vkwLoUhD5FH=bXuKGXDl(Zq!lGn@UUt&n~ku{Kg+uM>n4tT zzK=;sMb)*(!X}r0ysow?tg?_`P2yLM)>xjlU>OqLBy@gNf@2VB^QlS$k!gKsO~mZc znR?+<_I$0$n+pWZ^EVn#E6O)fQ;iL)XXmzermyWVf9y!CGPyPsJJ2U<0-|$cBUBg| zD`U;uJ849whN=1zv(dRi&+f5nWFi@hGA;E;4B0|<`>sDygFnHFT1;eJ%Wc$hnXVE= zZPRkTwCt~}YTNIYvEe3~G%?PJ*;l+wH&LDtZzP+Y#ID*O@O3WzXbow@tfY-28}>~y zyC7yUvT)ilRD=J`D*gldW|8s98Q+glH(o8i4_G1Y0o9ED*oa)$E!;0bz26w$@Wz#D zud;r7XJ$MUVgCQ^o2EZ%O5Bfka zcnNyIi;g6hBbO5iWF0@efTt;?JS`Z{JsH;#f8?G`F7A;W+(Vf>ZN{hNmTY(*Pn(?; zo?wm9Z_a{CpaxvgYc~<+fFeCVia1lm!a9=NlkNKn7tR_cNyIR(R_@8lL0tklnhGtW I%}Q|kCt+6uSpWb4 literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/goats.bin b/res/save_files_wii/any_bite/goats.bin new file mode 100644 index 0000000000000000000000000000000000000000..4c501d099f9da782df985660aa946b2454fe529d GIT binary patch literal 2700 zcmZSJXW(IA2uTPEW%&R9KO+$T*VoYb|DORJOqumBeOtCZNZ45wA$d%+&eAO_f#KNl zMtRXtxA@=y0|SsOf+fmuGbC}MJQ{Bm+~Tnr41EHH5-{V<_HMGM?!HJ%P~kxyn`Hj2w!3-a?4QAFSz zU`T*a9FW8kC`@p5#4_N92mnp}`N)asZv#;424-UQkE$CDfzc2c&=7!UVd7ljQAeD* zQB`Dy0EzKIrY$Ji;8sEza6XDEoE%`;&%hvnQK67%3QpsPoCM>Q1L$u4lh|SK=YL`Y z1LM@h7NBI})Cr7#5<5X+jUcgBkeG(e97Y8U>ykVBQAzRiH~^{kclTe4gV9L@_#@ko`JZ}QHclrK(U7);t_~=3?iO@ zh^HXp8HjieA`Wo$|4%%~@v0yZ6!(dTIDYUa9_Dz(kSGY^9^ugAPdv);q##iU#QpZz o;z;86#}?-je}Gs&L9AaO)^8B&4~X>_!~*+652ORstW5j|05D1O#sB~S literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/hc.bin b/res/save_files_wii/any_bite/hc.bin new file mode 100644 index 0000000000000000000000000000000000000000..17a6e3f64f657feb391466405205c8ba479ff861 GIT binary patch literal 2707 zcmd^BU1$_n6h3!n8fQ1z>~xJLlDHYG4_XAXt1$!wZ??&H3rUH8RcJ7Vl$J!R@rU#w zU}j?=Q3@^tgPSMz(*WzIza70155FWo;6 z{vEq_{z60O+nNohpN8tU)%pW|0C#15&4(rB7?r&Q22UjKR)ztFg6EE1eZ*t@8-b{B zRL(M2vIJMPzm`JuVuhGdx36%dK_*$FdoN4M>06VFnAgjPI*>_GDU_`X=&)Wu<1~$SB`C zSNs#h#~aOCdAW~!jiTB-^ivsgbSwekCu2KFXZZz4P-su3wkp#3nv(R8vk){W7WLAq zrUz@FXQj1k@iZ1lO@JGbHrpGR1;PdJA zlT()K5VYSB_gQs1R3bxy|BobgsfsP-`H^$YGv;#)X99O#eMg*gZEFPqMfpaYVGJV)#4+Cs)FzKcA6$jfzTtg8v^)!pn(MYN@#pz80bR65NxCa*=iR= zO#`4nC3b7^A~aa+iX_ENkog^9WB>C+{)o@6k^ceF`j}b!f%8k8-{Smx&Tn&mne!hx zZ`n1`;?&GvR(YLTRPCrRn3a)UqLA!7jZXrp; z=_sQp*nEb_;qw8^K;%$gn;NQ1+^_V;K0-Mhs!RM{rS$YOnVCa#4(6`EDhD7-1J?BD E3(-CqX8-^I literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/horseback.bin b/res/save_files_wii/any_bite/horseback.bin new file mode 100644 index 0000000000000000000000000000000000000000..3ca6b794ba3ca45e1e7045c051f47a0aa6358509 GIT binary patch literal 2707 zcmds3OK22H82+ofO?oDoOgl!+5_fDL2VbBQ(M1SYGbV8sRAPLB!6*@zxITiuyS1Ha<1(6Ik7P=0m?v_OXx+A~;cIlsrniZ9y(0m{o?03h>X1o9z4VPP11WQ`X z>#F3y#hat3K>X5p4@m+DK+{qHwL^@D7HHU?5K4lQ2n+`O!C=q}Bu|sxeBH7c7={7Z z<~Js!=*;o6Q?RIg;2dNBYFxxfmMgIZ(@P78X1^ZBD2x z#5i?w_Pj&a_&+r{MNUA(q}Y`5v0HR7a$NtFgv%ww&SP^Ua~}J9$uj`cO~vs?CGu0kG0@QUc6jdKkmcP8jd$dEwnRkoIF2*$ zeb~d``IEO$!(c8IOzaj9Pz?%Zk3%n)p zg23AX>(>s{dvx}2YQUq%wWhGiy+T-|w=L$|LeUWWrJ}BLMt7|z-F38eJnH_6T2HEc z{oWF;W4>S7j=(%|U(8q6ir1miG`>#ho>K0N`8K!XwJA1X-5^_Wx(%;0$Y0A?T^#dI z;Pa+&8+9BNAJ3FEZ0p1dWDPZy>F(;}UuA)|)u?+as*`_~tEaAWoms<{e5~F0Tn#|R J{c8>Pz5_m+8p;3w literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/hugo.bin b/res/save_files_wii/any_bite/hugo.bin new file mode 100644 index 0000000000000000000000000000000000000000..a24065814bf87ff31db9f0458fc4b2da64f699ca GIT binary patch literal 2700 zcmZSJXW(IA3`qzIW%&P}nejgm=xgZw|IYvhj1US$PMOXAQelM_NW{6eIUOc(T=$nTN5HgI6jBGGdFlmqj z{(Jm)1vv;EK%9v#3U)Iy#3V)q2#riSAafYxZ6p9AJEbP#lm1MI8u0JnR5sGJ%K#U?mI!AT}KQmH%H4QVk^2#kinAPWIl7KY_na3KJ6BTO9TTz&|{qYfr8 zO4B3+(8HZ3mcR`KmHxjN6+nDY5e_CBfK&qmLjgz-7eKg@15#B8FfekUDrRf|@*Pmc QMg))X%7GE#Pa1m?0NY+?Z2$lO literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/iza.bin b/res/save_files_wii/any_bite/iza.bin new file mode 100644 index 0000000000000000000000000000000000000000..2aad0d01bb131e5a6c16301bec52758eaec4c1f5 GIT binary patch literal 2707 zcmd^>TS!zv7{|XkdrC9YO;)lCdx3UAka8`;o;>CSAB2poh@ccHibMm89*P_zsHGkf zBYKE>Df1<$9-`j1Kq^E;6p3ycCX#entWXhm`p-FMj=K5aiHN|e^VnwE~j2umU=+I~AjbwBIO9%_HmH(1)Awfyag(gQmy z!g*n$6_&LHX_@r2t*5g6QI)6kW=qMcgJw^76EU56)hxeiS%6U{N@PKa;v$w9GJOnT zL}#T6%$PCFvW*!bBCj{y>-DA(bLlGXtl8~$6PEdRNo#lg$f3^W+CWu({kTikd0pmbN2PKG6tgwG*%n_V84LG}rF{k>ps%A24v>8pSlmIWhY>|4YkU zB&YEQ%2e+Gb>%d>1{GH&rCUasd=E%b{VA5~_;#M-$L)SKztVP?l%J{sbL)bL88>Jp zCC{Dlo1$vn3-!2#o_}`T=irK6ZJd0xMt9-a8Jsv2XU1`Zlb`T^Knhy*#V~!RN-ezJpN^17E-=Fakb;VK4*+ z!F$jT-hnsZ6?h5yKriS4-JlC}f)4NkJa5kQdkQ#_1V?KUg_iK3sN}(TDi7schy8(v z*86!#_wW$q^59spw z!&g~6oV;j=hYbSIzp!_p!u)sH>R^}gS~8JXjqP0nAxzL(FZ z;3*+q3%OA7sE86gcvYMfv!^q r7?2kP#Zey&nLxf0LDRykRUi={Is1ap3O;hSh!>HNTf2y=Em)}^9IkkT literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/lakebed_1.bin b/res/save_files_wii/any_bite/lakebed_1.bin new file mode 100644 index 0000000000000000000000000000000000000000..db632e183ff006378c1a667410031886d44e63d4 GIT binary patch literal 2707 zcmd^=T}TvB6vxkRF?tSoRw`DG+(*~TZ%v3Q&|F0VWt169Pa7O8hQ9&Fj6vveg(-XvnGCWFqN-EKEwoBx+|^*4+i`f?XVY2`=i zkH!^O+}KcG8y9gLG(`NWafLR;+OQ6ri(XOKOd+?h9hu2ATHvg80=B`rPc{j2edV{h zZBvn$I&-zP*)YC4d2Gn+8{Fs{p-)1$*w7P(Q%+Vii|uud)G zP1{vDxIoSBu*;;7E;luQS|Yhu&c<$sXYGYp=!HU;keOp;^My=WB01La`*j?+LQ#cr zPAu)8w)wX+SRxHsZe_~jlCPX%H$cW^KU|hkCcXngUFa;*SPw)eb zgDCh4K7%pv5sZQn5CQMO5O@dPfLGup7z6|01?UHTpcgy?Pr&2Obf2ey6A5s%CShm+ z4;WQE5KG~~>?^Q8@Zd%t4{9DBq#PcYD6;%jTcC@0U~*Dug0v>QJ_Rm-I&jgBU4xwm s%5DEJZ2P~Hs59LkJ->(W;;&$uMhtbG<-y!M#5Is7=2$Xy!iY`(0)4^#;s5{u literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/lanayru_twilight.bin b/res/save_files_wii/any_bite/lanayru_twilight.bin new file mode 100644 index 0000000000000000000000000000000000000000..ab8aa060a1018853a913ac3fedf4269ecd354862 GIT binary patch literal 2707 zcmd^1vykt{%O6D_hJdvg-AOGb=GQ1Dr4 z!X=m{U`FVa-Gy50P7`e~TR&@1O4Zd`Fhkez@;QSy?Sd`}?}f z6f3}n$X^$FSfiN>*5PxxSClYP2#YRhxu7_MaCwPjxa{t!W71i%ZKiiqHkEhgt`D3P z;+vJnlEyyXie<*)2lzc-b+;q5vcrdQ-O4cnuba7I2Tj@GC>@)N&u$a{U0wxw78S>b z6GeZ{9-njOf=V=xKdj@x9h7!3&WU-=75t+sUQov~|HZ0%K;7i(+f%x#pvH9CB}M*;#1`ZNUBV zG`IwM!Igq`1AY@|FT{u7H%MQ$L4P=XaVO!!cQH*PhPux4Xk!y}4K$fqvY)vsgzFbP C=kq53 literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/mdh_bridge.bin b/res/save_files_wii/any_bite/mdh_bridge.bin new file mode 100644 index 0000000000000000000000000000000000000000..e33dd428df6199d1d63b13e046c258dd8136d016 GIT binary patch literal 2707 zcmeH{T}V@57{{M?JEk*pYfLhXxk(!gq?|-qHy+8E7hafI5kVFqA03|{hqJ)Ip;jj-g91Z zP&Tcottabf!nuVLU@k2w|OekWWz1VtK(>}%O*ogk_T=7r!ckbf19oh9J*RpB{)>T<^YG_K|Fsg823~0Y}9dt4lAoOX zeq>&@Z#A4soCCVZ^~^bOv@UkOM562R{?bO%vF8@b#Qe6jQ_rikm=exyBJ*j=n*WLv zvvn!f$uc>&jDNQM)%Q>Cw;iFjxtA;XN}giIv+eD~VK`F{s+?TJ6jfY-V?w?dA`~Tt zxNx)zFIQm+`yy)HVWZ-o(`+y}cjlAZ6|HldN$c23f?4nl%z!EI1$+jRAPnAvw_pOi z0pnl{guqKM0$zY;;0YK8Ltqd*1_PiU^npjEdq&5BNQE&z{fOFB@CHN(vDjM&BUnG1X jT{-UXsV(?&DUB%#G4yqUdy7kuS3rqTCG__+Ei&^1c>@3p literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/mdh_tower.bin b/res/save_files_wii/any_bite/mdh_tower.bin new file mode 100644 index 0000000000000000000000000000000000000000..829ba4ebd1ddad2f389fd1e76b0be94b89f4bfb0 GIT binary patch literal 2707 zcmeH{Ur19?9LIlWyQVX9YfLhXIi(E*DJN0(7k;B5($F-K+xMFq9Q~j5$T_XnIu`uifOUkzUSVZ{ShDbP!!dVeSZJW@80|S?VNkb zK{i@dTUYjp$ZBC4WOYHIrjahpFhchZU%PmDpwp5XO9}5`f5F6qd&A=uquGmJ9_dK&J3Bl7jNy*-hy*Lc^GSs`cQz2YO$fLf{fL_=D+t`F)O z%3>yFl|h^(KcV5a6g(6yL?zSkROU>J=E&)^I-SmRVrWAbg)}*>&1R!{W5zE0U0&~C z)6|~nTX?)uwXbo1LL0sVO^x*lnP`BS#BV3G9M2e7MG0>cb$-1B#~@xpxmpY2)zl%*KU5-ky&qYt*CZjO*IOLj!%wJ z<@n@A*1rBKjj7Z!(_CYgi%D8`lbt6MsTj(%&?RYPTNKOR@W=dQJR5bKr5>V1IKV=< zuKx~YY=54L4L4E1#5gBrH%3ch%siqpQJhaUJBeMj-{#9)k)t&tR{qN${trYpi{vXO z-yfNm?OP3}QhW!5$n|t1jK-0=XdI5L%lk_kNk^YsC=(0Y(k?x((o%~1ZWEbLQ^vwq zB$=&CvQC!Cxn=yb?XSLnV!!POwJm*I$yIU`GoEd4Cl14zdPwEuBBrR~3VbHy!4RP+ zF~o&WtMIZF7Pl{=HZNdQ{BxQO`RC7ka)+XIZ8vEh+ei=s-@zORg0J8Um;nLs0lWuO z;2oF*6JQ*?2BY8=cmbY)5iksfz*8^?20%Y}0v>}$-B~V2DJK$OYjymv0`AsoxjUS} zJ%tzGzu}&hF78nr+(Sj&9V|Cu%Np2n?w*|$mLRRE&yRsKpaGnV)GwxUq1^iI|Gyc+PD5{R}x2q&KnWKHxv9`TiorLTH-s^rj~a!;;@ zC`GkAd6aBOqrUDPhoAHuxb<{nP2K1wJwx%VD*T*SODsSxRf@%elp=LJp^g|#Mq*~& ziO}RFM${Td#IS^rq-vJT?1`Zm+3jY#-JV3uC}}~76iimdVzCg~Tv^gP)Hq%D`5qP} zl^tz37PYwIYYEV{oi1w;?BM1w`j2$EnmeS;^YA z-Jo%qnrE7J8};NeHM`5Ml7VD2W%$*UM6$$(<9EDaTeI*IQeZ`#h{y^HwRh7MqWG;^ zlt(82a$DPer;H6Zkl(;KCuUuCn@*xaQM{fkRua2vz0cP;f=6pqd(8iFMZbaIW|98M z>EDkY*B5Wrj8fbKs_6No7(ZASx?cjpy#9P$>sPAoX9w$+Pz9Z8LZ;QxU#amTB`+~$ zdEx5im4&MGzC(fEZ2alxM=!SxuCn(gmolXc$%y~9wh@QoOg<@hkb{^ciy62k^n)Q> zQKE?hSF5nnB^Ggeidv^%tN7D+Hn+AB%02!4PCFb8JAcQ6C|;0yQ! zroj}L1QWmqK7cXs9=rvw!6+C3!{8+t0)t=xya4^+c~_dlR=|k_au6@HfV-3m?g}Jx zcXki#kKDb%!QGOLyD5je=881mlm}hJUGwuo6Qm{K%~@~>)Pu{x*mco3nz|A5;3f+gS&I{5tl%|R)vw#P9-q=6Xs3>p#T5? literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/morpheel.bin b/res/save_files_wii/any_bite/morpheel.bin new file mode 100644 index 0000000000000000000000000000000000000000..35e62f024c3e8b2797b6b740fab7e15dd0a499e5 GIT binary patch literal 2707 zcmeH|TS!zv7{|ZaJ*B3(+p>~fSa+daFr-{5&?k?%ULGuzEQvsqAc{l{3m=LcMJOpS zA)<$ zXAapYg*H|0E6gCWS(7x_=FBWjqlMNzbmi*Cst8LUDy;B4mBYP#nWGOM426n^)05vG zE3VmD?$7ZPtqjy`-;wPvAj(Oj-k$2VM^lYdlHB~fHBZm+ZzWR5CGYa97X_%LT9GJ7 z3+U|;y+xB`C1#VIIi7;Vh}r-{38tkQkxH%=rbaH8&EJQH=uBfr0zIL8scEn=fSB(~4H)FyoV%{c%pq@f@5t}xXp|zm1#uQ=! z>p9*e!gUp2>k1;FsCDLYYqO?(GxON6zK*y2ll>k0`F&?g>I5%)AbL6e;}P=XdD3N~QR-Go0xGxpp}0N{RU>0ZXB&bNmtmMeG$2yu%7wv zHEb}raMEY?Dq6=Li*~At1T$b7M8G8Y0ltF?5C&hsCom2^f-x`(Lf}0Z2JgTd@Cv*H zLtqfR00W>O^nqvK33%L<=62?DA_2BmCkQX#UagXQqbb~%bqVnY?%UwzKE=s>l+C@9 z0i&O^1wO#NQ&YkdtTo~F32+Y7f%7r#8sc0~7V95G9Q$?>b)|X2XLl1`{AEm0h@r02 T+?SIJT>-h`8pEbLwP@rg*+&2$ literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/ordon_gate_clip.bin b/res/save_files_wii/any_bite/ordon_gate_clip.bin new file mode 100644 index 0000000000000000000000000000000000000000..3ab14454cf3c1f485bebe2a9872860e19f455085 GIT binary patch literal 2700 zcmZSJXW(Igfd5dS2cn^32x`i#w~fmm`+@|V+Y!P?#g_haE@faiCZ?k+8tN7w9AIc* z!oa|YV2LsuTsY;v^IE0wqf+`JKl))ZjEsy-2y@U`AP2x4L@bSN20p%=9KW2L92c?@ zOg|&b4m_LLzU|*TkTZNT^RiK#<64lPmxv+)=P&}}0*K;(0zeuC%s}Cdaz;a7Gz3Oc z2*65T7(J5wKI&I!2ndXZ1mz*Xc;x`PA=^O$94o-&4lubAOzs4etH9(gFu58`?gouU}yV5FhaQSj>aVEOGs2v7{|YJ=cZ=nOfr%^xXwT`AV@hcdj+2#O$6zVmdR_EWc`5fLS5RWI>KJD#Jz0 zt?XotAt?B)RQ{wHGi}=>Bk6gD;Wi8-otR5ksm_{-JRT2WnSYmbK5G~|`0>6hs`5zv z(HX^+H8#}O&WI!&Y>52qMGI|-y~*c-o$ig}u<|GBOM7Y(AesizLTBe!qzW*C?tn z&WUCHd&^uTr|}2MR98k_InAj-#Z^ge+o+K50V%3K-F6eclb7)0PQRL8X(vp|&rpH6 zb#c2DH)#c>&7JX^qH5g>^|*zee`ejSaK(;WoO1Qmx(m4z~rRV1ZhcqbsAg*b>NZ{y8*ihR671) o*v@Y!QAf5vdSNHw;xA#kP7HOO70SyDcjj#d?k05*Bi$X#3Qyw%QLBR#rsNgCL5e2NiuVbd*4) zpa&5Z(Wi>O1U*0q`mjOo2$4*pmxhKZm5UW>q0@ivLw8ptf`XzB`#a~%Ip@yK+<9E` zk%zvPYz$8$r<=J1WJW29>Tj+0Rn3X_sX<#sQ_tsHTtqSw@e{&{<~hI`gNQp45K7+wb>h5K~k$sFEnPz>vMy$nU>hV# zf3Bz?a!gE&NuSk~?U8oU@~+Z(eXRpZ|GCQjLVS`jLs+x1cP@u|>T5Ui8@{Yf4sCL= z4>{c|AS!Qfp=fLS8g_8b1!~c$qI=H@EZk2CyUI?HgLF*FG17zSzsyxPx&{%xeyx|9BR8MDt|T z6!LgUuB!JspP*qCq&x#$I#QIeI7y@`d^)rlO3<0)acIc4VD(s3*NjwULcxFccfA2? zvE*H8h<$$Sxc$HKd_bbO2Mn?E87Xb6Zt#8?u<~~QwzaQRyB)OZGA)~8=GGbkoyI4h zQsoF?XJMl)wjRh$&RfyZ&BoDFBWCP*kC^-g!Qb6~@(^#MDOO$LYVO`is)GOa9^@Q` zGkvdKLmDwvFXH&hBwwtuL`phoxLT!`qq2nCQ*NTXIL@+6{cFM+W2sXcM012s{Ay zk7aAVxtvI%Tf|@`JYSUX{DHAN5IhUNjR&S`JfQk`fC_njPsD7yX2M2zes8a|1gRYJ z;s7`bO2G*$b{>8hEV0^K;TK9@%CYRc_~F&~B2XGrRbm)d4G$EB5m!Ojtb%6Ftzw|- EJB;87(*OVf literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/pot1.bin b/res/save_files_wii/any_bite/pot1.bin new file mode 100644 index 0000000000000000000000000000000000000000..890cfe1535952628b3a8e6b07b21ff57061287e8 GIT binary patch literal 2707 zcmd^BT}TvB6h3!n(phVD+KdWo8!IA1$hD$^pi$h-hYb4!A{=hn{k!pqD^lBa6`KoO@@Sc2n#niaP9^^PTUUd*|FUckV0% zKn1qEEO-mRBc)ggP^f$vQaJ{l1!RFp2Lfq+P)Xp>7_~LYo z>rT#|4^b$5_hA*=H}4!ob=Vt=EtQ<$FIsk*9fy2P$3=Mt8lr%juI*TRlhk#!qQ}mW~aZOuYW^r|92Ih{s>9<9qLcZVxjIV17}sF?8LT0C|)( z2~ zpn zMXv91eTnOPTvxB|srD+y(p|v}6$(p_mkwY+>k28Ih4R{@xAH&xv);^y|)3?3}uXX15HD8QnXCio=Mf_33 zDjPBXINom>`Dh!_(0rPxVM{YsAZjQrj|s`-K~S?IWDv9z zMG({~6;=dMU|Ie!?e(LQ9zP^{Z1kf)feRa1luhT}JL9&iJs_f}!_GP1`OcX;=bpK9 z?}8grfvhO?Tn4a74g+QC!iwx1vnYH1Q-D10Li9z>41gi9J9ezEJyrnC&+!^YE|!$X>e|K> z6Gwtg0+KA8uQX`~0#H`~AmMCK&;uEgI0j1GvOr9`-N|mZ+W};lNy>_0g;iCBxM@aj z{B!8R!)Mvm`1pjy6_uO7gI%`7TeY>a%yMEawt)SLQL}C^5!0lI^g8CZi$ixWCpgBK>*f1z$VoJ-uD~%JtTiq?b+Fn8Fl{ zQ!8ip>k#C>dcB&|&prhq3VR}vWs(*ANlO~ZQOMOOPRcgeEefa!QT{@E6bmGWz==qS z=Y_Ar-a0sftsp+C=D%>ZhRR5g)-2mP2E+q_ildbfuuCXK@rzn4FlDtfh>ja?GZ{f zPWq$qZzF9oi%rj+c0c(!c?qCJQ~CIbVNTTU?D!Pcx}64FA4G7`%>Nk?bMlgDt?$R; zt==RHkdONS7c1_F^Ub{e{pE!jx5l@uwWV5qhM6bUhUrAjye^I@43GUvr2~-2A|HE% zg>kX*n}*3};mJ0blnzFIhqLDWos?e+{qFH&i@SlYv9ej0$5p4v5sGGMa($KSb6j8Jx@t{tm0i*AOmx|mfLxcOF_w#-`#k7(Zlvh&_2R2& zn{un%9_c*PFbZvtuiPH?7T07l>vwJl9)y|vKEJb~0k8dYBltN*y5=&{?_A%E*E;{0 zweR)iO+ma)B7P%c6#>jYiqD(Ge6#^HY&=cWu%!(v5H*w(Ms5{_9xZSMSE23l6@?yp YrN$O!V{6!$h_$OfssV^{&-nc23!)ngoB#j- literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/purple_mist.bin b/res/save_files_wii/any_bite/purple_mist.bin new file mode 100644 index 0000000000000000000000000000000000000000..5b6e86a9fcf9a05f09eaf0c158136ffea504d30c GIT binary patch literal 2700 zcmeHJKWGzS6n|eY)*}S2777*R47ga3Ce zE9b{ZYeQ+V)WA`#$u|{TfOF4M&ks+>BZ?m@)m+NYy!c+&yH2;p3YRaO9ZDaUNWd}_ z+tWvjb8lYf59aHOr%pe9S_vaYm+JPn_$#0QJ*r1gP`d?XwyS>422q!+R7ru*Ac*CB z48gW7+qUCGMl6&XJ(C=W>=3HE=URCA<@P6(bLQIk^_H?miZ{l&MQJ+H2Y#0X!HW0`$PS4_Ajby$z%GWGbokkM_Ks_D$-Eyj`!{ zovx>I|IP{Qk`oB$fXqtY8uPI^AHp)NfQ(`ez`}JhG&{D9ClH?NHg5TktuX!da1t5) zF(vozb;yy6L%Ev*5(vHzNQl~2l`>ip=qF`H6XXSPTK)gVQ=i@TUaNdRUDfQ6n&-O` zxR~xmWRSaMA^VS23P(@WqBYoaWBwLVnr6z;%7A6S3SbrBnzNeUnrlVIv*!h~V(~k3jd&m2 qk~wJcd$U~RUbq`xro=zJ%tx+(ZG!y*`wdnFtAY7_3}QtJQ2zsz@&>*D literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/seam_clip.bin b/res/save_files_wii/any_bite/seam_clip.bin new file mode 100644 index 0000000000000000000000000000000000000000..ef1f19fa4c4cae6e880a9f18bb218a37c98a7426 GIT binary patch literal 2707 zcmZSJXW(IAW&nZzP@u1&^Z!3s1esWN`ev3^w;4#t*@Wd6OzN1OoYP^~X;R19w!iT( za*GcRFf_1WU|<2Wj0_nV*hCqQSv7bcx^v#**p#0_jW#efEKH0%FjFvTkON>2B9;bf zVZt;CyNH||znq*L7Xt$$3rqo~pJ6hCgJxV-et#3lDL$Eb*(gqOEy&MHL=k~=7=bKQd^5^4xgToovwnm59Ej8_hf@DQMq_v7~c H|33`?4}xNE literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/snowpeak_ruins_mbbb.bin b/res/save_files_wii/any_bite/snowpeak_ruins_mbbb.bin new file mode 100644 index 0000000000000000000000000000000000000000..068541fab2994c17183f5bbd0888c063731a62c7 GIT binary patch literal 2707 zcmeH{TS!zv7{|ZabMmgc$x4Q??nW*MQmzQvlgCi=!NSb42)qzPl4xPkLy@BxDG6mo z^bkFj^b%bOdfT8d2@^>~D$_6#rORT5O1Y>1oXcLM(1(PgKKA$B<~ws{cD^%YCmU@n zuU2{}+A5I>*_584sW3^7U8lQOAzSQDGgM%d<`C0+duQwKiY z!=mJp+WJFbiz__bP+t?42?W@a_*Fui+tW9!poF)GIJxDKnR^cVfEadhSwJx7t@y}^C;9b1uv-@PV zeXm75Q$d0S@D0p^S@0FifN9_ZpTGw&1t!4+7zbnE9T)*`!5i=j41qx~0A7NA&fZp!3ovjut_oef*S)8^)c zB}jAR>yzL-r~{Xb+;#Z5pu~s|!p{`Gkj^BRuVXhZds@Y0nHbj9#@$)D$jcyCuYzWD IQT5OK0M7peC;$Ke literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/stallord.bin b/res/save_files_wii/any_bite/stallord.bin new file mode 100644 index 0000000000000000000000000000000000000000..1f07d30f0e949a4fd620c42ec39ce5e0776ea603 GIT binary patch literal 2707 zcmeH|Sx8h-7{|YJ@6EOJHjQK$<276VcoM++}RWs33%jKIV76bZyLBEinK+=wD?RD-0tbhGiTyTQo6J zyi6?Fal@KI#E6a+5wT@vfAC15B&%@pux5$#zg7!%3^EN|@%KB~5Mn z-|hNv3xiVD?AccyF}PI+_U|i;h=d(1f%xn|D^^2@HBB8BKPLQ;SwgC?!!nU>)DTGv z!U5Z0%~ch`Oj|#+*FV-fDNkIgJz^Li!*#6NJUiEYqpb})`5kvsnWQaT=SE(4YO4Et zDA?Oy%#JU=LOnXwj8trpo#ZcSc9WeaiF8a#`rcRKNR63_-v~|FmW4$~h7}hbw|tfA z&d@oc80#DnpH}8#V!<>K*G2R9h_(G0I>s;2u*5hg=CX#*e?E&0BEP6wM5>F#uDWjX z`5JaXNGE8F(}h_ZJFn%gq*fii`ZTB}C8dd&eez5*kyJ?|Mef1wv7oM*!CJHZ-^}a^ z*qcSpm2>Wo6L)@H+z&_)?|>;zJ|W7t>%#BLq@8#AceL}QI)1iYmuTxKRnTAtbebD^ zOXX8E8yPk}WNM=H$b3{c)7{ePp&v$^dH=BJHVE+lLIx{vgT3DD20q#2`UuMbIEn zib@D-l?*F_DEgP!{!Hjcs2+OCjUIXl6gIL5ozA^?X4la|dx!!LJLi1oJ7?~kd*pM! z*8|`zC@ox+&m zy&hM@oETvMa;x?*h3V@D_T_*7I3f(63m(z+Xe^KPv$b>8Gt_siT)Uf+^r1tUTan~G5#?B4eh^7H?@q)F9>&^tor&;qs zc0meu-*rPvRnSs-hcBa5QYj23&gy<>lWS|5UIfxwlF}&$B}_9l9b}sVq7cEiM^sf| zWH288JyI6Y*ivuKtK|3O1Ar1w<>TitVs@H050rqYvGrkH-0?f6A|l451;d*A$K=hP zBy$kJbAX8%cZ~6kyx95DZ^X^`p;8=pawq_4B8i`#3eC+Nq+gOLxYfwpFizhh><@_4MLFpX z)0ddO%k*WY?=gLa>HADqZ5^tzOImN1%PzHw4fzUXnP}ekp`de*iVj;Z{)+C!J>~Z3 z S*fRrbmnW(Li1O!VlDLjP4{8P7*eZhH&63@2!Ib!4XfTF|CAQj7B|Zeq z-54;6;G!tfQeCA=+M@Ua{^nsdn)abIQu|OJG%G%&4}~mjG0~cg=iWP$%_fRL5JV3< z-}%nZ-aY63%|Z~0fvnm1VGsBfm&Za`@ag;u7L1nD)#v|t>gsh*1SsGA!(Au*Yv+H- z+uxt6jUKQ3=K1W+%F5zzt-3gzgO1SJs|4Elq? zpa)2SCcR$WvKbhL0odkSCd6nweG7X7*NXaWUqJ|G*^x-YH`{6*BVXeb@XrQBJ{w$Q zn-m#q;dV--fWi5&3{JC}arLGSJ8VKOHErj#WM$viNM z@7`O-t`-$OJZY9z6=9q@IlFq(5PQ_Sp0qAK1!)S++3YIS364{f6XYm_O^Qt^FZzlO zhAZbkm&$R;v^4mT>7)J9HegQ+JAzZaMA~djT7VScgw5i9dUH(lZFb1zucf%)3S<<* z7?6N0n7z908F>78)=V%0P=vD|xX7lMsJZ-&i;>VNr}2rCJ8mdOQ{$fBW5ovwOSliJ z1qvttLoq>9)3|zsQd2nNx%kVOIhV&4g%|$ee?T4rn7LFOe?rQe5sv<*&Syi51_mw9 zx6ph;-e)UBq<|wHaexSenRlMSDxb=@4_XDj>p<)VRa-mf$MHM;`z49tJs`x1dq&xI z-thg>W5=EG9qnAHj=jLnlY7H9a%y?E;1nk1pHlSzczNXiKB-}Pe*Cs2{r=072YB9< z%^XL_#u0ox)aiRXxv!J+rpM2(=@y#S+IFT*-%+Be_}hV2#&`*Y?x1Z@hVKVjmga8> zU2F^!LnxSnccp?b#{&sw0u*b+zCH0MnLKw!hTgnf$Knr&-p<|n6@fnq zyejZ#f!757BJjGvhR^#O+&cShcEGJCw3c#{dxfy@gJj&dg`y$$OSHaYURRwv+j*#M z3hKUSojViR&{)QG-1lYjAbc#o7x&e-;j@2P7LQZ5dl`4eeVg0y*%UARVt}nanZ)Nu z=$Qo*@vR&2byA^@tI@I0K>hzxx^~5Ev^K01R Q#oCQm)c~Y)mNj(mIX|HvNdN!< literal 0 HcmV?d00001 diff --git a/res/save_files_wii/any_bite/waterfall_sidehop.bin b/res/save_files_wii/any_bite/waterfall_sidehop.bin new file mode 100644 index 0000000000000000000000000000000000000000..a8671b5bbf5f0afcf3b4064e2f8c446e4fc0162c GIT binary patch literal 2707 zcmd^>Ur19?9LIlWyZ*^cmuz4Z+XOcfjNB?%FLfg|AAB&fB8p0)D3Tde^ia^zmmKzx z5IsdtA-x3UL(q$?hguOLl8ne|#F9v?hFMd>oxbPZovYhJ>}8PkW1sW;d(Zuydv?!8 z)l^9vJCE=7kPvnt#O8)ZAt+yl;@f8jren;Wn~04zOg()PPqrp%SHHX1dTd`uAQ&K8 zV_1PLD0We7q`vQ2|Id*<;h9^*o6O8MB8|Co=UVQ`2J|-3A{)vVJF)zb;o}l?*9XPw zmd#aV+WN8(k=tGEcDpNx*)lJ|LNa4tywiJ0 zi0@V%OBm1bejq&-Il&)0tGaC74INJ8bycG?4yUw?l>|$r(?ryl(I3-iPe}Z_(xZ~qPC~_&8l-io#g7>Q*qTnwQ01; zb3ls9SD3ElXX`9Klg+F7mClCc8C{6%h!|Bmt)=op>WfyR)?KU{6np;MzUAvL_TS^= zp<0dM-Zh*!6leMcos$>8%fBMoeOto3N=JqtI*I?B1#$><5&74 z(Ci*cBNTFIQGZEVinf+YDcZCe=p~lWAGLwDze=%JYN?=*!vj+>aeZgz_AZwojXy-u zm)qz4oVjOaXWl#1!GgAJS9lgd=mC%9(anvgJS>!hr)F1e@3>S|kxwwC&(Eb1d*{T@ ziHa=VdM?qqwY|CNF-pZcpKfbER9B3GlDX?TSoz+0nn&ubw{PYGVH2@nz}*L1L;a^@l9AXdpe(DYHsPH;3(D{XK_ zD=bJj91MrU0kA4V#mY=CZ^5!G_{%H|_{R;_^YFDJKM)PJrMtJMgigHg)!r9NA_a%G zKzw!*iro;RmdVL-lEP2R5;_L+Wp0%>2xt$?j|rQd9(Y6OmCsD~Mt{Bai$3?UyVvt_ zA|{31kZ0#a^UTz{UC!CM+M9=0R~vPdrKH=t<;wh_BTPj)!f_sQqp@<4Q={Krk>lJWI>^QJo%6;*3sg-}PkpFHWVut^KK zKu^l#!gMQ^kBw{AK>z-Wt@XpTD@6=jBuc1)rP(m_486T&oZB+!7W02@+3Q6v($;h- zILU4rut7&RChKYbvUGJ;+A{+av%jysJLd&n$Kbf?XZ{731^-*fzbxqa9q_N7ANMyV z-z{6I4@mLzfd#(bRsH;peBPhm*M3X&{l62+V9JAGtKn2aE%iYHI{@l@K)u0=-IJ;utj@NB8H-x6n&7{T>}q=9 z^5h>7^QZ`$XCxkzcvj+P62FpoT;kUf`(Bvot2CVp>q3=g%1B0S5tZV!d^h6O?B?Xi z^OEQudt|b!GWX53PY0XvbY=>){c4j*$J6%5|rx{v-On2H}%VkrMOSio>2UVxTy5w&x~0GFM`uk?o|X X5*?Y#asAL~p-VaJt|so*y*T^<6pbNP literal 0 HcmV?d00001