From 3dcdd4aaade237eb9444857f2e47e3242d352161 Mon Sep 17 00:00:00 2001 From: Michael Zhao <44533763+Pistonight@users.noreply.github.com> Date: Sat, 7 Sep 2024 20:03:43 -0700 Subject: [PATCH] Restore Inventory (#15) * pmdm * clean up * clang-format --- .clang-format | 74 +----- .clangd | 2 +- .github/workflows/main.yml | 46 ++-- Megaton.toml | 11 +- README.md | 52 +++- Taskfile.yml | 31 ++- libs/botw-symbols | 2 +- libs/nnheaders | 2 +- link.ld | 7 - scripts/lftp-upload.sh | 4 +- src/core/controller.cpp | 92 ++++--- src/core/controller.hpp | 32 ++- src/core/reporter.cpp | 2 +- src/core/reporter.hpp | 9 +- src/core/state.cpp | 347 +++++++++---------------- src/core/state.hpp | 46 ++-- src/core/version.hpp | 5 +- src/core/worker.cpp | 97 ++++--- src/core/worker.hpp | 5 +- src/impl/botw/PauseMenuDataMgrImpl.cpp | 26 -- src/impl/hack/pmdm.cpp | 23 -- src/impl/hack/pmdm.hpp | 41 --- src/impl/hack/sead_list.cpp | 24 -- src/impl/hack/sead_list.hpp | 15 -- src/impl/raw_ptr.hpp | 89 +++---- src/impl/sead/SafeStringImpl.cpp | 47 ---- src/main.cpp | 14 +- src/util/data_reader.cpp | 67 ----- src/util/data_reader.hpp | 105 -------- src/util/data_writer.cpp | 33 --- src/util/data_writer.hpp | 87 ------- src/util/file.cpp | 114 -------- src/util/file.hpp | 41 --- src/util/mem.h | 22 -- src/util/message.cpp | 160 ------------ src/util/message.hpp | 105 -------- src/util/named.h | 3 - src/util/named_value.hpp | 38 --- src/util/safe_memory.cpp | 18 -- src/util/safe_memory.hpp | 125 --------- src/util/scoped_lock.hpp | 24 -- src/util/string.hpp | 116 --------- src/util/time.cpp | 27 -- src/util/time.hpp | 11 - 44 files changed, 435 insertions(+), 1806 deletions(-) delete mode 100644 link.ld delete mode 100644 src/impl/botw/PauseMenuDataMgrImpl.cpp delete mode 100644 src/impl/hack/pmdm.cpp delete mode 100644 src/impl/hack/pmdm.hpp delete mode 100644 src/impl/hack/sead_list.cpp delete mode 100644 src/impl/hack/sead_list.hpp delete mode 100644 src/impl/sead/SafeStringImpl.cpp delete mode 100644 src/util/data_reader.cpp delete mode 100644 src/util/data_reader.hpp delete mode 100644 src/util/data_writer.cpp delete mode 100644 src/util/data_writer.hpp delete mode 100644 src/util/file.cpp delete mode 100644 src/util/file.hpp delete mode 100644 src/util/mem.h delete mode 100644 src/util/message.cpp delete mode 100644 src/util/message.hpp delete mode 100644 src/util/named.h delete mode 100644 src/util/named_value.hpp delete mode 100644 src/util/safe_memory.cpp delete mode 100644 src/util/safe_memory.hpp delete mode 100644 src/util/scoped_lock.hpp delete mode 100644 src/util/string.hpp delete mode 100644 src/util/time.cpp delete mode 100644 src/util/time.hpp diff --git a/.clang-format b/.clang-format index 641238e..c837f18 100644 --- a/.clang-format +++ b/.clang-format @@ -1,75 +1,3 @@ ---- -Language: Cpp +IndentWidth: 4 AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: Never -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: true -BinPackParameters: true -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -ColumnLimit: 100 -CommentPragmas: '^ (IWYU pragma:|NOLINT)' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ForEachMacros: [] -IncludeCategories: - - Regex: '^<[Ww]indows\.h>$' - Priority: 1 - - Regex: '^<' - Priority: 2 - - Regex: '^"' - Priority: 3 -IndentCaseLabels: false -IndentWidth: 4 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 4 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: c++17 -TabWidth: 4 -UseTab: Never -WhitespaceSensitiveMacros: ["SEAD_ENUM", "SEAD_ENUM_EX", "SEAD_ENUM_EX_VALUES"] -... diff --git a/.clangd b/.clangd index f55113f..6efdad6 100644 --- a/.clangd +++ b/.clangd @@ -1,6 +1,6 @@ CompileFlags: Compiler: /usr/bin/g++ - CompilationDatabase: ./target/megaton/none/ + CompilationDatabase: ./target/megaton/debug/ Remove: [ -march=*, -mtune=*, -mtp=* ] Diagnostics: Suppress: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d74f239..dc582af 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,21 +1,25 @@ -# name: Main -# -# on: -# push: -# branches: -# - main -# pull_request: -# branches: -# - main -# -# jobs: -# check: -# name: Check -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v4 -# - uses: arduino/setup-task@v2 -# with: -# version: 3.x -# repo-token: ${{ secrets.GITHUB_TOKEN }} -# - run: task check # installing clang-format 18 is too annoying +name: Main + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + recursive: true + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - uses: DoozyX/clang-format-lint-action@v0.18 + with: + source: 'src' + clangFormatVersion: 18 diff --git a/Megaton.toml b/Megaton.toml index 358cd8c..b1b7e02 100644 --- a/Megaton.toml +++ b/Megaton.toml @@ -7,13 +7,15 @@ title-id = 0x01007ef00011e000 entry = "exl_module_init" sources = [ "src", - "libs/exlaunch/source" + "libs/exlaunch/source", + "libs/botw-symbols/src", ] includes = [ "src", "libs/botw/src", "libs/botw/lib/agl/include", "libs/botw/lib/gsys/include", + "libs/botw-symbols/src", "libs/exlaunch/source", "libs/sead/include", "libs/nnheaders/include", @@ -21,7 +23,7 @@ includes = [ ldscripts = [ "libs/exlaunch/misc/link.ld", "libs/botw-symbols/ld/ld160.ld", - "link.ld", + "libs/botw-symbols/ld/toolkit160.ld", ] [build.flags] @@ -48,6 +50,11 @@ cxx = [ "-Wno-invalid-offsetof", ] +[build.profiles.debug.flags] +c = [ + "-DBOTWTOOLKIT_TCP_SEND", +] + [check] ignore = [ ".data", diff --git a/README.md b/README.md index b56f68f..068de8f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # botw-save-state -A BOTW 1.6.0 Switch save state mod for speedrun practices +![Build Badge](https://img.shields.io/github/check-runs/Pistonight/botw-save-state/main) +![Issue Badge](https://img.shields.io/github/issues/Pistonight/botw-save-state) +![Downloads Badge](https://img.shields.io/github/downloads/Pistonight/botw-save-state/total) + +A BOTW 1.6.0 Switch save-state mod for speedrun practices ## Install **Migration from 1.x - See [here](./migrate-1.x.md)** @@ -47,6 +51,8 @@ Restored state from memory **New in 2.0**: As long as you don't release all the keys, the mod will keep resetting Link's position, allowing for a more accurate restore while in ragdoll state. For example, with the default restore combo, I typically release the other 3 keys and hold `Plus` for slightly longer until position is fully restored. +If you see errors, please see trouble shooting below. + ### Settings Hold all triggers (`ZL + L + ZR + R`) and the whistle button (`Dpad Down`) for 3 seconds to enter setting mode. You should see the setting menu pop up in the top-right corner. Use the dpad to navigate the menu and `A` to select. Use `B` to go back a level or exit the settings. @@ -65,7 +71,8 @@ Since 2.0, you can now toggle individually what you want to save/restore. Open t **Recommended (TL;DR)**: - Keep `Timers` enabled as they don't have negative effects -- If you don't need to track durability during practice (i.e. Any%), you can disable `Overworld Durability` and `Inventory` to avoid accidental durability transfer +- If you don't need to track inventory during practice, you can disable `Inventory` to avoid issues. + - Having it on can still have benefits, for example you don't need to swap equipments after restoring for Any% **Full List**: 1. `Restore Message` @@ -89,13 +96,13 @@ Since 2.0, you can now toggle individually what you want to save/restore. Open t - Flame Resist - Shock Resist - Stealth -3. `Overworld Durability` - - When enabled, the name and durability of equipped Weapon, Bow and Shield will be saved. - - When restoring, the durability of equipped Weapon/Bow/Shield will only be restored if the same item by name is equipped (not necessarily the same one). - - Note that the inventory durability is not restored unless you also enable `Inventory` -4. `Inventory` enables: - - Equipped durability of Weapon, Bow, Shield and Arrow (similar to `Overworld Durability`, only restored if the same item is equipped) +3. `Inventory` enables: + - (Since v2.1) All your items and their states in the inventory, including but not limited to: + - Number of offset slots for IST + - GameData will be synced + - Equipped items in the overworld will be synced if a quick menu is open. If not, they will be synced the next time you open the quick menu. - Number of offset slots for IST + - (Since v2.1) This is a stored in its own field, so you can edit this value and break slots easily like in The following will always be enabled, and you cannot turn them off: - Current Health @@ -118,6 +125,35 @@ You can use this [python script](./scripts/ftp.py) to transfer save state files (This has nothing to do in game, just makes managing the save state files generated by the mod easier) +## Troubleshooting & FAQ + +#### Q: The mod is not showing in SimpleModManager +When you download the release `zip`, there are 2 folders `mods` and `botwsavs`. Make sure to +copy these to the **root** of the SD card, and overwrite existing files. Do NOT put them in any subfolders. + +#### Q: I installed but it's not working in game +Please check the following: +- The title screen should show the version as `1.6.0-SSx.y` where `x.y` is the version of the mod. +- When you load a save, after walking around for a few seconds, there will be a welcome message displayed on the top-right corner. + +If you have these working, then the mod is active, and you likely are not pressing the key combos correctly. Make sure to only +press the keys set. For example, if your binding is `A+B`, then pressing `A+B+Y` will not work. + +#### Q: Restore is showing error +Try restarting the game. The mod uses raw memory access to save/restore the state and has protection against invalid memory access. +There is a small chance that when the game boots, the values are not in the usual location. + +#### Q: I'm stuck after restoring +Don't restore in water or on a ladder. + +#### Q: Crash +Please open an issue and upload the crash report found in `/atmosphere/crash_reports/.log`, where `` is a number. +The greatest number is the most recent crash. + +#### Q: Will there be support for TOTK? +Making a mod like this requires an insane amount of research, and I was lucky to have Bloom do most of it +for BOTW. I don't really have the time and energy to think about TOTK right now. + ## Developer **This section is intended for developers** diff --git a/Taskfile.yml b/Taskfile.yml index 8a6a02c..a9a4704 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,42 +1,46 @@ version: '3' env: - CONSOLE_ADDR: 192.168.0.161:5000 # can be overriden in .env + # can be overriden in .env + CONSOLE_ADDR: 192.168.0.161:5000 + DEBUG_ADDR: 192.168.0.161:5001 dotenv: [".env"] +includes: + symbols: + aliases: [sym] + taskfile: libs/botw-symbols + dir: libs/botw-symbols + tasks: build: aliases: [b] desc: Build the mod cmds: - - megaton + - megaton -p debug check: desc: Check code for errors cmds: - - clang-format -n -Werror src/**/*.cpp - - clang-format -n -Werror src/**/*.hpp - - clang-format -n -Werror src/**/*.h + - clang-format -n -Werror $(find src -name '*.cpp' -o -name '*.hpp') fix: desc: Fix errors in code cmds: - - clang-format -i src/**/*.cpp - - clang-format -i src/**/*.hpp - - clang-format -i src/**/*.h + - clang-format -i $(find src -name '*.cpp' -o -name '*.hpp') package: desc: Package the build for release - deps: [build] vars: - VERSION: "SS2.0" + VERSION: "SS2.1" MOD_DIR: "target/package/mods/The Legend of Zelda - Breath of the Wild/Save State/contents/01007EF00011E000" cmds: + - megaton -p none - rm -rf target/package/mods - mkdir -p "{{.MOD_DIR}}/exefs" - mkdir -p "{{.MOD_DIR}}/romfs/System" - cp target/megaton/none/main.npdm "{{.MOD_DIR}}/exefs/main.npdm" - - cp target/megaton/none/make/botwsavs.nso "{{.MOD_DIR}}/exefs/subsdk9" + - cp target/megaton/none/botwsavs.nso "{{.MOD_DIR}}/exefs/subsdk9" - mkdir -p target/package/botwsavs - echo "" > target/package/botwsavs/latest.txt - echo -n "1.6.0-{{.VERSION}}" > "{{.MOD_DIR}}/romfs/System/Version.txt" @@ -54,5 +58,6 @@ tasks: - mkdir -p target/crash_reports/dumps - lftp $CONSOLE_ADDR < scripts/lftp-download.sh - - + debug: + cmds: + - python libs/botw-symbols/tcp-client.py $DEBUG_ADDR diff --git a/libs/botw-symbols b/libs/botw-symbols index e54f1b4..ad47183 160000 --- a/libs/botw-symbols +++ b/libs/botw-symbols @@ -1 +1 @@ -Subproject commit e54f1b4c5ada4ce5c53241b96fd5d67917dea6a6 +Subproject commit ad47183887ac78cabe138e2169d6bc3e4e8c017c diff --git a/libs/nnheaders b/libs/nnheaders index 1cc8049..28c5efe 160000 --- a/libs/nnheaders +++ b/libs/nnheaders @@ -1 +1 @@ -Subproject commit 1cc804966b3a45f46677b482c0a7338bd315a45c +Subproject commit 28c5efe2952462ed4c1ff3a4c8b153b12536ffe5 diff --git a/link.ld b/link.ld deleted file mode 100644 index 47dbc44..0000000 --- a/link.ld +++ /dev/null @@ -1,7 +0,0 @@ - -main_memory = 0x0 - 0x3483000; -ksys_ui_showInfoOverlayWithString = 0x1238680 - 0x3483000; -ScreenMessageTipsRuntime_doShowMessageTip = 0x0119C750 - 0x3483000; -ksys_ui_ScreenMgr_sInstance = 0x2CC2490 - 0x3483000; -/* botw_get_message_string = 0x123DEA0 - 0x3483000; */ -ksys_ui_sRuntimeTips = 0x02CBA3B0 - 0x3483000; diff --git a/scripts/lftp-upload.sh b/scripts/lftp-upload.sh index a86091a..76bde3d 100644 --- a/scripts/lftp-upload.sh +++ b/scripts/lftp-upload.sh @@ -1,6 +1,6 @@ mkdir -p -f /atmosphere/contents/01007EF00011E000/exefs cd /atmosphere/contents/01007EF00011E000/exefs -mput -e target/megaton/none/botwsavs.nso -mput -e target/megaton/none/main.npdm +mput -e target/megaton/debug/botwsavs.nso +mput -e target/megaton/debug/main.npdm rm -f subsdk9 mv botwsavs.nso subsdk9 diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 2a50d89..d391523 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -1,9 +1,11 @@ #include #include +#include +#include +#include +#include #include "core/controller.hpp" -#include "util/message.hpp" -#include "util/named.h" namespace botw::savs { @@ -25,14 +27,14 @@ bool Controller::initialize() { return true; } -void Controller::save_key_bindings(DataWriter& w) const { +void Controller::save_key_bindings(io::DataWriter& w) const { w.write_integer(_named(m_key_save)); w.write_integer(_named(m_key_save_file)); w.write_integer(_named(m_key_restore)); w.write_integer(_named(m_key_restore_file)); } -void Controller::load_key_bindings(DataReader& r) { +void Controller::load_key_bindings(io::DataReader& r) { u32 key_save = 0; u32 key_save_file = 0; u32 key_restore = 0; @@ -98,9 +100,9 @@ bool Controller::finish_configure_key(Key new_key) { if (m_configure_hold_counter < 30) { m_configure_hold_counter++; if (m_configure_hold_counter % 10 == 0) { - StringBuffer<120> buffer; + mem::StringBuffer<120> buffer; get_key_string(new_key, buffer); - msg::show_infof("Keep holding %s", buffer.content()); + msg::info::printf("Keep holding %s", buffer.content()); } return false; } @@ -133,9 +135,7 @@ static const char* get_enabled_text(bool value) { return value ? "Enabled" : "Disabled"; } -static const char* get_toggle_text(bool value) { - return value ? "On" : "Off"; -} +static const char* get_toggle_text(bool value) { return value ? "On" : "Off"; } Command Controller::update_setting_mode() { m_tick_since_last_menu_input++; @@ -210,11 +210,12 @@ Command Controller::update_setting_mode() { } Key new_key = get_hold_keys(); if (finish_configure_key(new_key)) { - StringBuffer<16> key_name; + mem::StringBuffer<16> key_name; get_key_name(key_name, m_key_being_configured); - StringBuffer<120> buffer; + mem::StringBuffer<120> buffer; get_key_string(new_key, buffer); - msg::show_widgetf("Changed %s key\n to: %s", key_name.content(), buffer.content()); + msg::widget::printf("Changed %s key\n to: %s", + key_name.content(), buffer.content()); m_key_being_configured = nullptr; m_tick_since_last_menu_input = 0; @@ -259,22 +260,21 @@ Command Controller::update_setting_mode() { m_tick_since_last_menu_input = 0; switch (m_menu_current_option) { case 0: - msg::show_widget( - "Restore Message\n\nWhen disabled, there will be no\nmessage shown when " - "executing a\nstate restore. Useful for grinding\nBTT segments."); + msg::widget::print("Restore Message\n\nWhen disabled, there " + "will be no\nmessage shown when " + "executing a\nstate restore. Useful for " + "grinding\nBTT segments."); break; case 1: - msg::show_widget("Timers\n\nEnable save/restore TOD, Bloodmoon,\nChampion Ability, " - "Master Sword,\nPotion, and Weather Damage Timers."); + msg::widget::print( + "Timers\n\nEnable save/restore TOD, Bloodmoon,\nChampion " + "Ability, " + "Master Sword,\nPotion, and Weather Damage Timers."); break; case 2: - msg::show_widget("Overworld Durability\n\nEnable save/restore durability of " - "the\nequipped Weapon/Bow/Shield in the\noverworld if the same " - "item by name\nis equipped when restoring."); - break; - case 3: - msg::show_widget("Inventory\n\nEnable save/restore the inventory\nstate. See " - "GitHub for more details."); + msg::widget::print( + "Inventory\n\nEnable save/restore the inventory\nstate. " + "Equipments will be synced."); break; } } @@ -283,22 +283,18 @@ Command Controller::update_setting_mode() { switch (m_menu_current_option) { case 0: value = !m_config->m_show_restore_message; - msg::show_infof("Restore Message: %s", get_enabled_text(value)); + msg::info::printf("Restore Message: %s", + get_enabled_text(value)); m_config->m_show_restore_message = value; break; case 1: value = !m_config->m_enable_timers; - msg::show_infof("Timers: %s", get_enabled_text(value)); + msg::info::printf("Timers: %s", get_enabled_text(value)); m_config->m_enable_timers = value; break; case 2: - value = !m_config->m_enable_overworld_durability; - msg::show_infof("Overworld Durability: %s", get_enabled_text(value)); - m_config->m_enable_overworld_durability = value; - break; - case 3: value = !m_config->m_enable_inventory; - msg::show_infof("Inventory: %s", get_enabled_text(value)); + msg::info::printf("Inventory: %s", get_enabled_text(value)); m_config->m_enable_inventory = value; break; } @@ -364,23 +360,23 @@ void Controller::refresh_menu() { case Mode::SettingStateOption: m_menu_title.copy("Change State Options"); m_menu_subtitle.copy("[A] Toggle; [X] Explain"); - m_menu_options_count = 4; + m_menu_options_count = 3; m_menu_options[0].clear(); - m_menu_options[0].appendf("[%s] Restore Message", - get_toggle_text(m_config->m_show_restore_message)); + m_menu_options[0].appendf( + "[%s] Restore Message", + get_toggle_text(m_config->m_show_restore_message)); m_menu_options[1].clear(); - m_menu_options[1].appendf("[%s] Timers", get_toggle_text(m_config->m_enable_timers)); + m_menu_options[1].appendf("[%s] Timers", + get_toggle_text(m_config->m_enable_timers)); m_menu_options[2].clear(); - m_menu_options[2].appendf("[%s] Overworld Durability", - get_toggle_text(m_config->m_enable_overworld_durability)); - m_menu_options[3].clear(); - m_menu_options[3].appendf("[%s] Inventory", get_toggle_text(m_config->m_enable_inventory)); + m_menu_options[2].appendf( + "[%s] Inventory", get_toggle_text(m_config->m_enable_inventory)); break; } if (m_menu_current_option >= m_menu_options_count) { m_menu_current_option = 0; } - StringBuffer buffer; + mem::StringBuffer buffer; buffer.appendf("%s\n%s", m_menu_title.content(), m_menu_subtitle.content()); for (u32 i = 0; i < m_menu_options_count; i++) { buffer.append("\n"); @@ -391,20 +387,20 @@ void Controller::refresh_menu() { } buffer.appendf("%s", m_menu_options[i].content()); } - msg::show_widget(buffer.content()); + msg::widget::print(buffer.content()); } void Controller::show_configuring_key_message() { if (!m_key_being_configured) { return; } - StringBuffer<16> key_name; + mem::StringBuffer<16> key_name; get_key_name(key_name, m_key_being_configured); - StringBuffer<50> current_combo; + mem::StringBuffer<50> current_combo; get_key_string(*m_key_being_configured, current_combo); - msg::show_widgetf( - "Changing: %s\nCurrent:\n%s\nHold new combo for 3 seconds.\nHold ZL to cancel", - key_name.content(), current_combo.content()); + msg::widget::printf("Changing: %s\nCurrent:\n%s\nHold new combo for 3 " + "seconds.\nHold ZL to cancel", + key_name.content(), current_combo.content()); } -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/controller.hpp b/src/core/controller.hpp index 17bc0f6..d9cd657 100644 --- a/src/core/controller.hpp +++ b/src/core/controller.hpp @@ -1,11 +1,11 @@ #pragma once #include +#include +#include +#include #include "core/state.hpp" -#include "util/data_reader.hpp" -#include "util/data_writer.hpp" -#include "util/string.hpp" namespace sead { struct Controller; @@ -40,12 +40,13 @@ inline constexpr Key operator|(Key a, Key b) { return static_cast(static_cast(a) | static_cast(b)); } -constexpr Key KEY_SETTINGS = Key::DpadDown | Key::ZL | Key::ZR | Key::L | Key::R; +constexpr Key KEY_SETTINGS = + Key::DpadDown | Key::ZL | Key::ZR | Key::L | Key::R; constexpr Key KEY_INCREASE_LEVEL = Key::R; constexpr Key KEY_DECREASE_LEVEL = Key::L; template -void get_key_string(u32 keys, StringBuffer& out_buffer) { +void get_key_string(u32 keys, mem::StringBuffer& out_buffer) { out_buffer.clear(); if (keys == 0) { return; @@ -115,7 +116,8 @@ enum class Command { }; constexpr size_t MENU_BUFFER_LEN = 40; -constexpr u32 MENU_REFRESH_TICKS = 60; // refresh menu after this time if there is no input +constexpr u32 MENU_REFRESH_TICKS = + 60; // refresh menu after this time if there is no input constexpr u32 MENU_INPUT_INTERVAL = 3; class Controller { @@ -130,8 +132,8 @@ class Controller { bool initialize(); Command update(); - void save_key_bindings(DataWriter& writer) const; - void load_key_bindings(DataReader& reader); + void save_key_bindings(io::DataWriter& writer) const; + void load_key_bindings(io::DataReader& reader); Mode get_mode() const { return m_mode; } @@ -153,9 +155,9 @@ class Controller { bool m_restore_fired = false; u32 m_tick_since_last_menu_input = MENU_REFRESH_TICKS + 1; - StringBuffer m_menu_title; - StringBuffer m_menu_subtitle; - StringBuffer m_menu_options[4]; + mem::StringBuffer m_menu_title; + mem::StringBuffer m_menu_subtitle; + mem::StringBuffer m_menu_options[4]; u32 m_menu_options_count = 0; u32 m_menu_current_option = 0; bool m_menu_showing_explain_message = false; @@ -167,10 +169,12 @@ class Controller { m_holding_keys = Key::None; m_hold_counter = 0; } - bool is_configuring_key() const { return m_key_being_configured != nullptr; } + bool is_configuring_key() const { + return m_key_being_configured != nullptr; + } void start_configure_key(Key* key); bool finish_configure_key(Key new_key); - void get_key_name(StringBuffer<16>& out_buffer, Key* key) const { + void get_key_name(mem::StringBuffer<16>& out_buffer, Key* key) const { out_buffer.clear(); if (key == &m_key_save) { out_buffer.append("SaveToMem"); @@ -188,4 +192,4 @@ class Controller { void refresh_menu(); void show_configuring_key_message(); }; -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 6bec06b..428dd74 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -17,4 +17,4 @@ void Reporter::report(const char* field, bool success) { } return; } -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/reporter.hpp b/src/core/reporter.hpp index 4e423c9..1c0bee8 100644 --- a/src/core/reporter.hpp +++ b/src/core/reporter.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace botw::savs { @@ -15,8 +15,7 @@ class Reporter { bool has_error() const { return m_error_count > 0; } void mark_error() { m_mark_error = m_error_count; } bool has_more_errors() const { return m_mark_error < m_error_count; } - template - void append_fields_to(StringBuffer& out) const { + template void append_fields_to(mem::StringBuffer& out) const { u32 max = std::min(m_error_count, 5u); for (u32 i = 0; i < max; i++) { out.appendf("--%s", m_fields[i].content()); @@ -29,7 +28,7 @@ class Reporter { private: u32 m_error_count = 0; u32 m_mark_error = 0; - StringBuffer<36> m_fields[5]; + mem::StringBuffer<36> m_fields[5]; }; -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/state.cpp b/src/core/state.cpp index 8daa9b7..a6aa4de 100644 --- a/src/core/state.cpp +++ b/src/core/state.cpp @@ -1,101 +1,51 @@ -#include "core/state.hpp" #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include "core/state.hpp" #include "core/version.hpp" -#include "impl/hack/pmdm.hpp" -#include "impl/hack/sead_list.hpp" #include "impl/raw_ptr.hpp" -#include "util/data_reader.hpp" -#include "util/data_writer.hpp" -#include "util/named.h" -#include "util/named_value.hpp" namespace botw::savs { -void StateConfig::save_config(DataWriter& w) const { +void StateConfig::save_config(io::DataWriter& w) const { w.write_bool(_named(m_show_restore_message)); w.write_bool(_named(m_enable_timers)); - w.write_bool(_named(m_enable_overworld_durability)); + // was overworld durability in v5 + bool unused = false; + w.write_bool(_named(unused)); w.write_bool(_named(m_enable_inventory)); } -void StateConfig::read_config(DataReader& r) { +void StateConfig::read_config(io::DataReader& r) { r.read_bool(&m_show_restore_message); r.read_bool(&m_enable_timers); - r.read_bool(&m_enable_overworld_durability); + // was overworld durability in v5 + bool unused; + r.read_bool(&unused); r.read_bool(&m_enable_inventory); } -void read_inventory_equipment_durability(const char* name, const uking::ui::PouchItem* item, - NamedValue& value, Reporter& r) { - if (ptr_looks_safe(item)) { - value.set(item->getName(), item->getValue()); - } else { - value.clear_name(); - r.report(name, false); - } -} - -void write_inventory_equipment_durability(const char* name, uking::ui::PouchItem* item, - const NamedValue& value, Reporter& r) { - if (ptr_looks_safe(item)) { - if (value.name_matches(item->getName())) { - hack::PouchItem hack_item(item); - hack_item.set_value(value.value()); - } - } else { - r.report(name, false); - } -} - -void read_overworld_equipment_durability(const char* name, - const safe_ptr& actor, - const safe_ptr& durability, - NamedValue& value, Reporter& r) { - ksys::act::BaseProc* safe_actor = nullptr; - // if actor is not found, it's not an error - // just means the item is not equipped - if (actor.take_ptr(&safe_actor) && safe_actor) { - u32 safe_durability; - if (durability.get(&safe_durability)) { - value.set(safe_actor->getName(), safe_durability); - return; - } - // if actor is found but not durability, it is an error - r.report(name, false); - } - value.clear_name(); -} - -void write_overworld_equipment_durability(const char* name, - const safe_ptr& actor, - const safe_ptr& durability, - const NamedValue& value, Reporter& r) { - ksys::act::BaseProc* safe_actor = nullptr; - // if actor is not found, it's not an error - // just means the item is not equipped - if (actor.take_ptr(&safe_actor) && safe_actor) { - // if name doesn't match, not an error - // means the equipment was changed - if (value.name_matches(safe_actor->getName())) { - if (!durability.set(value.value())) { - r.report(name, false); - } - } - } -} - void State::read_from_game(Reporter& r, const StateConfig& config) { + tcp::sendf("reading state from game\n"); // essentials m_stored_essentials = false; r.report("Health", raw_ptr::health().get(&m_health)); r.report("Stamina", raw_ptr::stamina().get(&m_stamina)); - r.report("HavokPosition", raw_ptr::havok_position().get_array(m_havok_position, 3)); - r.report("MainPositionMatrix", - raw_ptr::main_position_matrix().get_array(m_main_position_matrix, 12)); - r.report("CamPanMatrix", raw_ptr::camera_pan_matrix().get_array(m_camera_pan_matrix, 12)); + r.report("HavokPosition", + raw_ptr::havok_position().get_array(m_havok_position, 3)); + r.report("MainPositionMatrix", raw_ptr::main_position_matrix().get_array( + m_main_position_matrix, 12)); + r.report("CamPanMatrix", + raw_ptr::camera_pan_matrix().get_array(m_camera_pan_matrix, 12)); r.report("CamZoom", raw_ptr::camera_zoom().get(&m_camera_zoom)); r.report("CamTilt", raw_ptr::camera_tilt().get(&m_camera_tilt)); if (!r.has_error()) { @@ -107,84 +57,56 @@ void State::read_from_game(Reporter& r, const StateConfig& config) { m_stored_timers = false; if (config.m_enable_timers) { r.report("TODPaused", raw_ptr::time_of_day_paused().get(&m_tod_paused)); - r.report("TODUnpause", raw_ptr::time_of_day_unpaused().get(&m_tod_unpaused)); + r.report("TODUnpause", + raw_ptr::time_of_day_unpaused().get(&m_tod_unpaused)); r.report("BMTimer", raw_ptr::blood_moon().get(&m_blood_moon_timer)); - r.report("TempDmg", raw_ptr::temperature_damage_timer().get(&m_temperature_damage_timer)); + r.report("TempDmg", raw_ptr::temperature_damage_timer().get( + &m_temperature_damage_timer)); r.report("FlameDmg", raw_ptr::flame_timer().get(&m_flame_timer)); r.report("Gale", raw_ptr::gale_timer().get(&m_gale_timer)); r.report("Fury", raw_ptr::fury_timer().get(&m_fury_timer)); - r.report("DarukP", raw_ptr::protection_timer().get(&m_protection_timer)); + r.report("DarukP", + raw_ptr::protection_timer().get(&m_protection_timer)); r.report("Grace", raw_ptr::grace_timer().get(&m_grace_timer)); - r.report("ChampUse", raw_ptr::ability_uses().get_array(m_ability_uses, 3)); - r.report("MS", raw_ptr::master_sword_cooldown().get(&m_master_sword_cooldown)); - r.report("Speed1", raw_ptr::speed_potion_timer1().get(&m_speed_potion_timer_1)); - r.report("Speed2", raw_ptr::speed_potion_timer2().get(&m_speed_potion_timer_2)); - r.report("Speed3", raw_ptr::speed_potion_timer3().get(&m_speed_potion_timer_3)); - r.report("AtkTime", raw_ptr::attack_potion_timer().get(&m_attack_potion_timer)); - r.report("DefTime", raw_ptr::defense_potion_timer().get(&m_defense_potion_timer)); - r.report("HeatResT", raw_ptr::heat_resist_potion_timer().get(&m_heat_resist_potion_timer)); - r.report("ColdResT", raw_ptr::cold_resist_potion_timer().get(&m_cold_resist_potion_timer)); - r.report("FireResT", - raw_ptr::flame_resist_potion_timer().get(&m_flame_resist_potion_timer)); - r.report("ElecResT", - raw_ptr::shock_resist_potion_timer().get(&m_shock_resist_potion_timer)); - r.report("StealthT", raw_ptr::stealth_potion_timer().get(&m_stealth_potion_timer)); + r.report("ChampUse", + raw_ptr::ability_uses().get_array(m_ability_uses, 3)); + r.report("MS", raw_ptr::master_sword_cooldown().get( + &m_master_sword_cooldown)); + r.report("Speed1", + raw_ptr::speed_potion_timer1().get(&m_speed_potion_timer_1)); + r.report("Speed2", + raw_ptr::speed_potion_timer2().get(&m_speed_potion_timer_2)); + r.report("Speed3", + raw_ptr::speed_potion_timer3().get(&m_speed_potion_timer_3)); + r.report("AtkTime", + raw_ptr::attack_potion_timer().get(&m_attack_potion_timer)); + r.report("DefTime", + raw_ptr::defense_potion_timer().get(&m_defense_potion_timer)); + r.report("HeatResT", raw_ptr::heat_resist_potion_timer().get( + &m_heat_resist_potion_timer)); + r.report("ColdResT", raw_ptr::cold_resist_potion_timer().get( + &m_cold_resist_potion_timer)); + r.report("FireResT", raw_ptr::flame_resist_potion_timer().get( + &m_flame_resist_potion_timer)); + r.report("ElecResT", raw_ptr::shock_resist_potion_timer().get( + &m_shock_resist_potion_timer)); + r.report("StealthT", + raw_ptr::stealth_potion_timer().get(&m_stealth_potion_timer)); if (!r.has_more_errors()) { m_stored_timers = true; } } - // durability - r.mark_error(); - m_stored_overworld_durability = false; - if (config.m_enable_overworld_durability) { - read_overworld_equipment_durability("OvWpnDura", raw_ptr::overworld_weapon_actor(), - raw_ptr::overworld_weapon_durability(), - m_overworld_equipped_weapon_durability, r); - - read_overworld_equipment_durability("OvBowDura", raw_ptr::overworld_bow_actor(), - raw_ptr::overworld_bow_durability(), - m_overworld_equipped_bow_durability, r); - - read_overworld_equipment_durability("OvShdDura", raw_ptr::overworld_shield_actor(), - raw_ptr::overworld_shield_durability(), - m_overworld_equipped_shield_durability, r); - if (!r.has_more_errors()) { - m_stored_overworld_durability = true; - } - } - // inventory r.mark_error(); m_stored_inventory = false; if (config.m_enable_inventory) { - auto* pmdm = uking::ui::PauseMenuDataMgr::instance(); - if (!pmdm) { + toolkit::PmdmAccess pmdm; + if (pmdm.is_nullptr()) { r.report("PMDM", false); } else { - pmdm->updateEquippedItemArray(); - hack::Pmdm hack_pmdm(pmdm); - - // save arrow, weapon, bow, shield - auto* arrow = hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Arrow); - read_inventory_equipment_durability("ArrowCnt", arrow, m_menu_equipped_arrow_count, r); - - auto* weapon = hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Sword); - read_inventory_equipment_durability("InvWpnDura", weapon, - m_menu_equipped_weapon_durability, r); - - auto* bow = hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Bow); - read_inventory_equipment_durability("InvBowDura", bow, m_menu_equipped_bow_durability, - r); - - auto* shield = hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Shield); - read_inventory_equipment_durability("InvShdDura", shield, - m_menu_equipped_shield_durability, r); - - auto& items = hack_pmdm.get_items(); - s32 count = items.size(); - s32 real_count = hack::get_sead_list_real_size(&items); - m_num_inventory_count_offset = real_count - count; + r.report("PmdmState", m_pmdm_state.read_from(pmdm)); + m_num_inventory_count_offset = pmdm.get_offset_slots(); } if (!r.has_more_errors()) { m_stored_inventory = true; @@ -192,15 +114,19 @@ void State::read_from_game(Reporter& r, const StateConfig& config) { } } -void State::write_to_game(Reporter& r, const StateConfig& config, bool hold) const { +void State::write_to_game(Reporter& r, const StateConfig& config, + bool hold) const { // essentials if (m_stored_essentials) { r.report("Health", raw_ptr::health().set(m_health)); r.report("Stamina", raw_ptr::stamina().set(m_stamina)); - r.report("HavokPosition", raw_ptr::havok_position().set_array(m_havok_position, 3)); + r.report("HavokPosition", + raw_ptr::havok_position().set_array(m_havok_position, 3)); r.report("MainPositionMatrix", - raw_ptr::main_position_matrix().set_array(m_main_position_matrix, 12)); - r.report("CamPanMatrix", raw_ptr::camera_pan_matrix().set_array(m_camera_pan_matrix, 12)); + raw_ptr::main_position_matrix().set_array( + m_main_position_matrix, 12)); + r.report("CamPanMatrix", raw_ptr::camera_pan_matrix().set_array( + m_camera_pan_matrix, 12)); r.report("CamZoom", raw_ptr::camera_zoom().set(m_camera_zoom)); r.report("CamTilt", raw_ptr::camera_tilt().set(m_camera_tilt)); @@ -230,81 +156,64 @@ void State::write_to_game(Reporter& r, const StateConfig& config, bool hold) con // timers if (m_stored_timers && config.m_enable_timers) { r.report("TODPaused", raw_ptr::time_of_day_paused().set(m_tod_paused)); - r.report("TODUnpause", raw_ptr::time_of_day_unpaused().set(m_tod_unpaused)); + r.report("TODUnpause", + raw_ptr::time_of_day_unpaused().set(m_tod_unpaused)); r.report("BMTimer", raw_ptr::blood_moon().set(m_blood_moon_timer)); - r.report("TempDmg", raw_ptr::temperature_damage_timer().set(m_temperature_damage_timer)); + r.report("TempDmg", raw_ptr::temperature_damage_timer().set( + m_temperature_damage_timer)); r.report("FlameDmg", raw_ptr::flame_timer().set(m_flame_timer)); r.report("Gale", raw_ptr::gale_timer().set(m_gale_timer)); r.report("Fury", raw_ptr::fury_timer().set(m_fury_timer)); r.report("DarukP", raw_ptr::protection_timer().set(m_protection_timer)); r.report("Grace", raw_ptr::grace_timer().set(m_grace_timer)); - r.report("ChampUse", raw_ptr::ability_uses().set_array(m_ability_uses, 3)); - r.report("MS", raw_ptr::master_sword_cooldown().set(m_master_sword_cooldown)); - r.report("Speed1", raw_ptr::speed_potion_timer1().set(m_speed_potion_timer_1)); - r.report("Speed2", raw_ptr::speed_potion_timer2().set(m_speed_potion_timer_2)); - r.report("Speed3", raw_ptr::speed_potion_timer3().set(m_speed_potion_timer_3)); - r.report("AtkTime", raw_ptr::attack_potion_timer().set(m_attack_potion_timer)); - r.report("DefTime", raw_ptr::defense_potion_timer().set(m_defense_potion_timer)); - r.report("HeatResT", raw_ptr::heat_resist_potion_timer().set(m_heat_resist_potion_timer)); - r.report("ColdResT", raw_ptr::cold_resist_potion_timer().set(m_cold_resist_potion_timer)); - r.report("FireResT", raw_ptr::flame_resist_potion_timer().set(m_flame_resist_potion_timer)); - r.report("ElecResT", raw_ptr::shock_resist_potion_timer().set(m_shock_resist_potion_timer)); - r.report("StealthT", raw_ptr::stealth_potion_timer().set(m_stealth_potion_timer)); - } - - // durability - if (m_stored_overworld_durability && config.m_enable_overworld_durability) { - write_overworld_equipment_durability("OvWpnDura", raw_ptr::overworld_weapon_actor(), - raw_ptr::overworld_weapon_durability(), - m_overworld_equipped_weapon_durability, r); - - write_overworld_equipment_durability("OvBowDura", raw_ptr::overworld_bow_actor(), - raw_ptr::overworld_bow_durability(), - m_overworld_equipped_bow_durability, r); - - write_overworld_equipment_durability("OvShdDura", raw_ptr::overworld_shield_actor(), - raw_ptr::overworld_shield_durability(), - m_overworld_equipped_shield_durability, r); + r.report("ChampUse", + raw_ptr::ability_uses().set_array(m_ability_uses, 3)); + r.report("MS", + raw_ptr::master_sword_cooldown().set(m_master_sword_cooldown)); + r.report("Speed1", + raw_ptr::speed_potion_timer1().set(m_speed_potion_timer_1)); + r.report("Speed2", + raw_ptr::speed_potion_timer2().set(m_speed_potion_timer_2)); + r.report("Speed3", + raw_ptr::speed_potion_timer3().set(m_speed_potion_timer_3)); + r.report("AtkTime", + raw_ptr::attack_potion_timer().set(m_attack_potion_timer)); + r.report("DefTime", + raw_ptr::defense_potion_timer().set(m_defense_potion_timer)); + r.report("HeatResT", raw_ptr::heat_resist_potion_timer().set( + m_heat_resist_potion_timer)); + r.report("ColdResT", raw_ptr::cold_resist_potion_timer().set( + m_cold_resist_potion_timer)); + r.report("FireResT", raw_ptr::flame_resist_potion_timer().set( + m_flame_resist_potion_timer)); + r.report("ElecResT", raw_ptr::shock_resist_potion_timer().set( + m_shock_resist_potion_timer)); + r.report("StealthT", + raw_ptr::stealth_potion_timer().set(m_stealth_potion_timer)); } // inventory - // Note inventory is only restored while not holding - if (hold && m_stored_inventory && config.m_enable_inventory) { - auto* pmdm = uking::ui::PauseMenuDataMgr::instance(); - if (!pmdm) { + if (!hold && m_stored_inventory && config.m_enable_inventory) { + toolkit::PmdmAccess pmdm; + if (pmdm.is_nullptr()) { r.report("PMDM", false); } else { - pmdm->updateEquippedItemArray(); - hack::Pmdm hack_pmdm(pmdm); - - write_inventory_equipment_durability( - "ArrowCnt", hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Arrow), - m_menu_equipped_arrow_count, r); - - write_inventory_equipment_durability( - "InvWpnDura", hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Sword), - m_menu_equipped_weapon_durability, r); - - write_inventory_equipment_durability( - "InvBowDura", hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Bow), - m_menu_equipped_bow_durability, r); - - write_inventory_equipment_durability( - "InvShdDura", hack_pmdm.get_equipped_item(uking::ui::PouchItemType::Shield), - m_menu_equipped_shield_durability, r); - - auto& items = hack_pmdm.get_items(); - s32 real_size = hack::get_sead_list_real_size(&items); - s32 size = real_size - m_num_inventory_count_offset; - hack::set_sead_list_m_count(&items, size); + m_pmdm_state.write_to(pmdm, true); + pmdm.set_offset_slots(m_num_inventory_count_offset); + int status = toolkit::equipment::sync_with_pmdm(); + if (status == -1) { + r.report("SyncPMDM", false); + } else if (status == 1) { + r.report("SyncNotPaused", false); + } } } } -StateFileResult State::read_from_file(DataReader& r) { +StateFileResult State::read_from_file(io::DataReader& r) { u32 version = 0; r.read_integer(&version); - if (version <= vLegacy) { + if (version <= Version::vLegacy) { return StateFileResult::UnsupportedVersion; } if (version > Version::vLatest) { @@ -312,8 +221,14 @@ StateFileResult State::read_from_file(DataReader& r) { } r.read_bool(&m_stored_essentials); r.read_bool(&m_stored_timers); - r.read_bool(&m_stored_overworld_durability); + // was overworld durability in v5 + bool unused; + r.read_bool(&unused); r.read_bool(&m_stored_inventory); + if (version < Version::v6) { + // due to incompatibility with inventory state in v5, turn it off + m_stored_inventory = false; + } r.read_integer(&m_health); r.read_float(&m_stamina); @@ -345,15 +260,12 @@ StateFileResult State::read_from_file(DataReader& r) { r.read_float(&m_shock_resist_potion_timer); r.read_float(&m_stealth_potion_timer); - r.read_named_integer(m_overworld_equipped_weapon_durability); - r.read_named_integer(m_overworld_equipped_bow_durability); - r.read_named_integer(m_overworld_equipped_shield_durability); + if (version >= Version::v6) { + // read inventory + r.read_integer(&m_num_inventory_count_offset); + m_pmdm_state.read_from_file(r); + } - r.read_named_integer(m_menu_equipped_arrow_count); - r.read_named_integer(m_menu_equipped_weapon_durability); - r.read_named_integer(m_menu_equipped_bow_durability); - r.read_named_integer(m_menu_equipped_shield_durability); - r.read_integer(&m_num_inventory_count_offset); if (!r.is_successful()) { clear(); return StateFileResult::IOError; @@ -361,12 +273,16 @@ StateFileResult State::read_from_file(DataReader& r) { return StateFileResult::Ok; } -StateFileResult State::write_to_file(DataWriter& w) const { +StateFileResult State::write_to_file(io::DataWriter& w) const { + tcp::sendf("writing state to file - version %d\n", Version::vLatest); w.write_integer("version", Version::vLatest); + tcp::sendf("-- essentials\n"); w.write_bool(_named(m_stored_essentials)); w.write_bool(_named(m_stored_timers)); - w.write_bool(_named(m_stored_overworld_durability)); + // was overworld durability in v5 + bool unused = false; + w.write_bool(_named(unused)); w.write_bool(_named(m_stored_inventory)); w.write_integer(_named(m_health)); @@ -377,6 +293,7 @@ StateFileResult State::write_to_file(DataWriter& w) const { w.write_float(_named(m_camera_zoom)); w.write_float(_named(m_camera_tilt)); + tcp::sendf("-- timers\n"); w.write_float(_named(m_tod_paused)); w.write_float(_named(m_tod_unpaused)); w.write_float(_named(m_blood_moon_timer)); @@ -399,15 +316,9 @@ StateFileResult State::write_to_file(DataWriter& w) const { w.write_float(_named(m_shock_resist_potion_timer)); w.write_float(_named(m_stealth_potion_timer)); - w.write_named_integer(_named(m_overworld_equipped_weapon_durability)); - w.write_named_integer(_named(m_overworld_equipped_bow_durability)); - w.write_named_integer(_named(m_overworld_equipped_shield_durability)); - - w.write_named_integer(_named(m_menu_equipped_arrow_count)); - w.write_named_integer(_named(m_menu_equipped_weapon_durability)); - w.write_named_integer(_named(m_menu_equipped_bow_durability)); - w.write_named_integer(_named(m_menu_equipped_shield_durability)); + tcp::sendf("-- inventory\n"); w.write_integer(_named(m_num_inventory_count_offset)); + m_pmdm_state.write_to_file(w); if (!w.is_successful()) { return StateFileResult::IOError; @@ -415,4 +326,4 @@ StateFileResult State::write_to_file(DataWriter& w) const { return StateFileResult::Ok; } -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/state.hpp b/src/core/state.hpp index 3b09e8a..857d2e2 100644 --- a/src/core/state.hpp +++ b/src/core/state.hpp @@ -1,9 +1,11 @@ #pragma once #include +#include +#include +#include + #include "core/reporter.hpp" -#include "util/data_reader.hpp" -#include "util/data_writer.hpp" namespace botw::savs { @@ -16,33 +18,32 @@ enum class StateFileResult { class StateConfig { public: - void save_config(DataWriter& w) const; - void read_config(DataReader& r); + void save_config(io::DataWriter& w) const; + void read_config(io::DataReader& r); // show info message when restoring bool m_show_restore_message = true; - // enable TOD, bloodmoon, ability, master sword, potion, and climate damage timers + // enable TOD, bloodmoon, ability, master sword, potion, and climate damage + // timers bool m_enable_timers = true; - // enable restoring overworld equipment durability, if the same equipment is equipped - bool m_enable_overworld_durability = false; - // enable restoring inventory state + // enable restoring inventory state, including full pmdm and num offset + // slots bool m_enable_inventory = false; }; class State { public: void read_from_game(Reporter& reporter, const StateConfig& config); - void write_to_game(Reporter& reporter, const StateConfig& config, bool hold) const; - StateFileResult read_from_file(DataReader& reader); - StateFileResult write_to_file(DataWriter& writer) const; + void write_to_game(Reporter& reporter, const StateConfig& config, + bool hold) const; + StateFileResult read_from_file(io::DataReader& reader); + StateFileResult write_to_file(io::DataWriter& writer) const; bool is_stored() const { - return m_stored_essentials || m_stored_timers || m_stored_overworld_durability || - m_stored_inventory; + return m_stored_essentials || m_stored_timers || m_stored_inventory; } void clear() { m_stored_essentials = false; m_stored_timers = false; - m_stored_overworld_durability = false; m_stored_inventory = false; } @@ -50,7 +51,6 @@ class State { // config bool m_stored_essentials = false; bool m_stored_timers = false; - bool m_stored_overworld_durability = false; bool m_stored_inventory = false; // always enabled u32 m_health; @@ -91,17 +91,13 @@ class State { float m_shock_resist_potion_timer; float m_stealth_potion_timer; - // durability - NamedValue m_overworld_equipped_weapon_durability{0}; - NamedValue m_overworld_equipped_bow_durability{0}; - NamedValue m_overworld_equipped_shield_durability{0}; + // inventory - // inventory (TODO - save and restore entire PMDM - NamedValue m_menu_equipped_arrow_count{0}; - NamedValue m_menu_equipped_weapon_durability{0}; - NamedValue m_menu_equipped_bow_durability{0}; - NamedValue m_menu_equipped_shield_durability{0}; + // even though the pmdm state covers this + // this field still exists to allow people to change the state manually + // to easily break slots s32 m_num_inventory_count_offset; + toolkit::PmdmSaveState m_pmdm_state; }; -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/version.hpp b/src/core/version.hpp index 8290824..2ea6aac 100644 --- a/src/core/version.hpp +++ b/src/core/version.hpp @@ -2,8 +2,9 @@ namespace botw::savs { enum Version { - vLegacy = 4, // 1-4 are no longer supported + vLegacy = 4, // no longer supported v5 = 5, - vLatest = v5 + v6 = 6, + vLatest = v6 }; } diff --git a/src/core/worker.cpp b/src/core/worker.cpp index ef734af..b1dd89a 100644 --- a/src/core/worker.cpp +++ b/src/core/worker.cpp @@ -3,31 +3,38 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "core/reporter.hpp" #include "core/version.hpp" #include "core/worker.hpp" #include "impl/raw_ptr.hpp" -#include "util/data_reader.hpp" -#include "util/data_writer.hpp" -#include "util/mem.h" -#include "util/message.hpp" + +extern "C" { +void* memalign(size_t alignment, size_t size); +} namespace botw::savs { static nn::os::ThreadType s_thread; void worker_main(void*) { - Worker worker; + auto worker = mem::make_unique(); while (true) { - if (worker.initialize()) { + // we need to trap the thread here if alloc fails + if (worker && worker->initialize()) { break; } nn::os::YieldThread(); nn::os::SleepThread(nn::TimeSpan::FromSeconds(5)); } while (true) { - worker.do_work(); + worker->do_work(); nn::os::YieldThread(); nn::os::SleepThread(nn::TimeSpan::FromNanoSeconds(100000000)); } @@ -37,8 +44,8 @@ void start_worker_thread() { const u64 STACK_SIZE = 0x80000; void* thread_stack = memalign(0x1000, STACK_SIZE); - nn::Result result = - nn::os::CreateThread(&s_thread, worker_main, nullptr, thread_stack, STACK_SIZE, 16); + nn::Result result = nn::os::CreateThread(&s_thread, worker_main, nullptr, + thread_stack, STACK_SIZE, 16); if (result.IsFailure()) { return; } @@ -68,16 +75,17 @@ void Worker::do_work() { // we don't report here to reduce spam // restore every 2 frames so the game has a chance to update nn::os::YieldThread(); - nn::os::SleepThread(nn::TimeSpan::FromNanoSeconds(60 * 1000 * 1000)); + nn::os::SleepThread( + nn::TimeSpan::FromNanoSeconds(60 * 1000 * 1000)); } break; case Command::RestoreDone: if (m_last_restored_ok) { if (m_config.m_show_restore_message) { if (m_last_restored_is_from_memory) { - msg::show_info("Restored state from memory"); + msg::info::print("Restored state from memory"); } else { - msg::show_info("Restored state from file"); + msg::info::print("Restored state from file"); } } m_last_restored_ok = false; @@ -98,15 +106,16 @@ void Worker::do_work() { } void Worker::save_options() const { - DataWriter writer(OPTION_FILE_PATH); + io::DataWriter writer(OPTION_FILE_PATH); writer.write_integer("version", Version::vLatest); m_config.save_config(writer); m_controller.save_key_bindings(writer); + writer.flush(); } void Worker::load_options() { - DataReader reader(OPTION_FILE_PATH); + io::DataReader reader(OPTION_FILE_PATH); u32 version = 0; reader.read_integer(&version); @@ -123,45 +132,54 @@ void Worker::load_options() { } static void report_fail_read(Reporter& reporter) { - StringBuffer<280> report; + mem::StringBuffer<280> report; report.append("Error! Failed to read game memory\n"); reporter.append_fields_to(report); - msg::show_widget(report.content()); + msg::widget::print(report.content()); } static void report_fail_write(Reporter& reporter) { - StringBuffer<280> report; + mem::StringBuffer<280> report; report.append("Error! Failed to write game memory\n"); reporter.append_fields_to(report); - msg::show_widget(report.content()); + msg::widget::print(report.content()); } void Worker::execute_save() { + tcp::sendf("save to memory\n"); Reporter reporter; m_memory_state.read_from_game(reporter, m_config); if (reporter.has_error()) { report_fail_read(reporter); } - msg::show_info("Saved state to memory"); + msg::info::print("Saved state to memory"); } void Worker::execute_save_file() { - State temp_state; + tcp::sendf("save to file\n"); + mem::unique_ptr temp_state = mem::make_unique(); + if (!temp_state) { + msg::widget::print("Error!\nFailed to allocate memory for state"); + return; + } + Reporter reporter; - temp_state.read_from_game(reporter, m_config); + temp_state->read_from_game(reporter, m_config); if (reporter.has_error()) { report_fail_read(reporter); return; } - DataWriter writer(STATE_FILE_PATH); - StateFileResult result = temp_state.write_to_file(writer); + tcp::sendf("writing latest.txt\n"); + io::DataWriter writer(STATE_FILE_PATH); + StateFileResult result = temp_state->write_to_file(writer); + writer.flush(); if (result != StateFileResult::Ok || !writer.is_successful()) { - msg::show_widget("Error!\nIO Error while writing state file"); + msg::widget::print("Error!\nIO Error while writing state file"); return; } - msg::show_info("Saved state to file"); + msg::info::print("Saved state to file"); } void Worker::execute_restore() { @@ -180,30 +198,31 @@ void Worker::execute_restore() { void Worker::execute_restore_file() { m_last_restored_ok = false; - File file(STATE_FILE_PATH); + io::File file(RESTORE_FILE_PATH); if (!file.exists()) { - msg::show_info("No restore.txt found!"); + msg::info::print("No restore.txt found!"); return; } - DataReader reader(STATE_FILE_PATH); + io::DataReader reader(RESTORE_FILE_PATH); StateFileResult result = m_last_restored_file.read_from_file(reader); switch (result) { case StateFileResult::UnsupportedVersion: - msg::show_widget("The state file restore.txt\nhas an unsupported version."); + msg::widget::print( + "The state file restore.txt\nhas an unsupported version."); return; case StateFileResult::InvalidVersion: - msg::show_widget( - "The state file restore.txt\nhas an invalid version. It could\nbe corrupted."); + msg::widget::print("The state file restore.txt\nhas an invalid " + "version. It could\nbe corrupted."); return; case StateFileResult::IOError: - msg::show_widget("IO Error reading restore.txt!"); + msg::widget::print("IO Error reading restore.txt!"); return; case StateFileResult::Ok: break; } if (!reader.is_successful()) { - msg::show_widget("IO Error reading restore.txt!"); + msg::widget::print("IO Error reading restore.txt!"); return; } @@ -219,7 +238,7 @@ void Worker::execute_restore_file() { } bool Worker::show_active_mode_message() const { - return msg::show_widgetf("\ + return msg::widget::printf("\ Settings exited.\n\ You can use the save state now\n\ Hold All Triggers + Dpad Down\n\ @@ -231,7 +250,7 @@ void Worker::welcome() { return; } // probe to see if it's probably safe to deref havok position - if (!ksys_ui_ScreenMgr_sInstance) { + if (!msg::widget::is_ready()) { m_pos_diff_ticks = 0; return; } @@ -261,10 +280,12 @@ void Worker::welcome() { } m_pos_diff_ticks++; if (m_pos_diff_ticks >= 4) { - bool showed = msg::show_widget("Save State is active\n\nHold All Triggers + Dpad Down\nto " - "open Settings, or see README\non GitHub for more info."); + bool showed = msg::widget::print( + "Save State is active\n\nHold All Triggers + Dpad Down\nto " + "open Settings, or see README\non GitHub for more info."); if (showed) { m_showed_welcome = true; + tcp::start_server(5001); return; } } @@ -273,4 +294,4 @@ void Worker::welcome() { m_player_pos[2] = new_pos[2]; } -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/core/worker.hpp b/src/core/worker.hpp index ab017a4..b236be3 100644 --- a/src/core/worker.hpp +++ b/src/core/worker.hpp @@ -1,13 +1,14 @@ #pragma once -#include #include "core/controller.hpp" #include "core/state.hpp" +#include namespace botw::savs { constexpr const char* OPTION_FILE_PATH = "sd:/botwsavs/option.txt"; constexpr const char* STATE_FILE_PATH = "sd:/botwsavs/latest.txt"; +constexpr const char* RESTORE_FILE_PATH = "sd:/botwsavs/restore.txt"; void worker_main(void*); @@ -59,4 +60,4 @@ class Worker { void welcome(); }; -} // namespace botw::savs +} // namespace botw::savs diff --git a/src/impl/botw/PauseMenuDataMgrImpl.cpp b/src/impl/botw/PauseMenuDataMgrImpl.cpp deleted file mode 100644 index 6f30162..0000000 --- a/src/impl/botw/PauseMenuDataMgrImpl.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#define private public -#include -#undef private -#include - -#include "util/scoped_lock.hpp" - -namespace uking::ui { - -void PauseMenuDataMgr::updateEquippedItemArray() { - // offset of array: 0x44E70 - mEquippedWeapons.fill({}); - - // since sead::CriticalSection is inlined in 1.6 - // our own scoped lock implementation is provided - botw::savs::ScopedLock lock(&mCritSection.mCriticalSectionInner); - // const auto lock = sead::makeScopedLock(mCritSection); - for (auto& item : getItems()) { - if (item.getType() > PouchItemType::Shield) - break; - if (item.isEquipped()) - mEquippedWeapons[u32(item.getType())] = &item; - } -} - -} // namespace uking::ui diff --git a/src/impl/hack/pmdm.cpp b/src/impl/hack/pmdm.cpp deleted file mode 100644 index d8ae026..0000000 --- a/src/impl/hack/pmdm.cpp +++ /dev/null @@ -1,23 +0,0 @@ - -// Need private field access -#define private public -#include -#undef private - -#include "impl/hack/pmdm.hpp" - -namespace botw::savs::hack { - -uking::ui::PouchItem* Pmdm::get_equipped_item(uking::ui::PouchItemType t) { - return m_pmdm->mEquippedWeapons[(int)t]; -} - -sead::OffsetList& Pmdm::get_items() { - return m_pmdm->mItemLists.list1; -} - -void PouchItem::set_value(u32 value) { - m_item->mValue = value; -} - -} diff --git a/src/impl/hack/pmdm.hpp b/src/impl/hack/pmdm.hpp deleted file mode 100644 index 3719bc0..0000000 --- a/src/impl/hack/pmdm.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -namespace sead { -template -class OffsetList; -} - -namespace uking::ui { -class PauseMenuDataMgr; -class PouchItem; -enum class PouchItemType; -} - -namespace botw::savs::hack { - -class Pmdm { - -public: - Pmdm(uking::ui::PauseMenuDataMgr* pmdm) : m_pmdm(pmdm) {} - - uking::ui::PouchItem* get_equipped_item(uking::ui::PouchItemType t); - sead::OffsetList& get_items(); - -private: - uking::ui::PauseMenuDataMgr* m_pmdm; -}; - -class PouchItem { -public: - PouchItem(uking::ui::PouchItem* item) : m_item(item) {} - - void set_value(u32 value); - -private: - uking::ui::PouchItem* m_item; -}; - - -} diff --git a/src/impl/hack/sead_list.cpp b/src/impl/hack/sead_list.cpp deleted file mode 100644 index 76f6401..0000000 --- a/src/impl/hack/sead_list.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Need private field access -#define private public -#define protected public -#include -#undef private -#undef protected - -namespace botw::savs::hack { - -void set_sead_list_m_count(sead::ListImpl* list, s32 count) { - list->mCount = count; -} - -s32 get_sead_list_real_size(sead::ListImpl* list) { - s32 size = 0; - sead::ListNode* ptr = list->mStartEnd.mNext; - while(ptr && ptr != &list->mStartEnd){ - ptr = ptr->mNext; - size++; - } - return size; -} - -} diff --git a/src/impl/hack/sead_list.hpp b/src/impl/hack/sead_list.hpp deleted file mode 100644 index dc8ba05..0000000 --- a/src/impl/hack/sead_list.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace sead { -class ListImpl; -} - -namespace botw::savs::hack { - -void set_sead_list_m_count(sead::ListImpl* list, s32 count); - -s32 get_sead_list_real_size(sead::ListImpl* list); - -} diff --git a/src/impl/raw_ptr.hpp b/src/impl/raw_ptr.hpp index 3fef6fe..d4ea222 100644 --- a/src/impl/raw_ptr.hpp +++ b/src/impl/raw_ptr.hpp @@ -6,11 +6,10 @@ */ #pragma once #include -#include "util/safe_memory.hpp" +#include +#include -extern "C" void* main_memory; - -#define _main (mem_ptr(&main_memory)) +#define _main (mem::mem_ptr(botw::mem::main_module())) namespace ksys::act { struct BaseProc; @@ -22,146 +21,142 @@ struct PouchItem; namespace botw::savs::raw_ptr { // Health and Stam -inline safe_ptr health() { - return _main[0x2CA1A78][0x80] + 0x848; -} -inline safe_ptr stamina() { - return _main[0x2c9fd70][0x38] + 0x2ac; -} +inline mem::safe_ptr health() { return _main[0x2CA1A78][0x80] + 0x848; } +inline mem::safe_ptr stamina() { return _main[0x2c9fd70][0x38] + 0x2ac; } // Max Stam Amount (unused): [[main+2c9fd70]+38]+2b0 // Runes // Set Bomb cool down to 360 in float (43350000) // Set Stasis cool down to 1 in float (3F800000) -inline safe_ptr round_bomb_cooldown() { +inline mem::safe_ptr round_bomb_cooldown() { return _main[0x2CA3AD8][0x30] + 0x80; } -inline safe_ptr square_bomb_cooldown() { +inline mem::safe_ptr square_bomb_cooldown() { return _main[0x2CA3AD8][0x30] + 0x98; } -inline safe_ptr stasis_cooldown() { +inline mem::safe_ptr stasis_cooldown() { return _main[0x2CA3AD8][0x30] + 0x144; } // Position and Camera -inline safe_ptr havok_position() { +inline mem::safe_ptr havok_position() { return _main[0x2D37AF0][0xF0][0xD28] + 0x5E0; } -inline safe_ptr main_position_matrix() { +inline mem::safe_ptr main_position_matrix() { return _main[0x2CA1140][0x60] + 0x398; } -inline safe_ptr camera_pan_matrix() { +inline mem::safe_ptr camera_pan_matrix() { return _main[0x2CE1090][0x60] + 0x20; } -inline safe_ptr camera_zoom() { +inline mem::safe_ptr camera_zoom() { return _main[0x2CA5520][0x20][0x558] + 0xD48; } -inline safe_ptr camera_tilt() { +inline mem::safe_ptr camera_tilt() { return _main[0x2CA5520][0x20][0x558] + 0xD14; } // Fall Damage (need to figure out how to make FDC work) -inline safe_ptr fall_damage_y() { +inline mem::safe_ptr fall_damage_y() { return _main[0x2CA1140][0x60] + 0x20D0; } // Overworld Durability -inline safe_ptr overworld_weapon_actor() { +inline mem::safe_ptr overworld_weapon_actor() { return _main[0x2CC4768][0xc0][-0xA0][0x48]; } -inline safe_ptr overworld_bow_actor() { +inline mem::safe_ptr overworld_bow_actor() { return _main[0x2CC4768][0xc0][-0x70][0x48]; } -inline safe_ptr overworld_shield_actor() { +inline mem::safe_ptr overworld_shield_actor() { return _main[0x2CC4768][0xc0][-0x88][0x48]; } -inline safe_ptr overworld_weapon_durability() { +inline mem::safe_ptr overworld_weapon_durability() { return _main[0x2CC4768][0xc0][-0xA0][0x48] + 0xD4C; } -inline safe_ptr overworld_bow_durability() { +inline mem::safe_ptr overworld_bow_durability() { return _main[0x2CC4768][0xc0][-0x70][0x48] + 0xD4C; } -inline safe_ptr overworld_shield_durability() { +inline mem::safe_ptr overworld_shield_durability() { return _main[0x2CC4768][0xc0][-0x88][0x48] + 0xD4C; } // Time -inline safe_ptr time_of_day_paused() { +inline mem::safe_ptr time_of_day_paused() { return _main[0x2CC5FE0][0xBE0][0x30][0xF8] + 0x18; } -inline safe_ptr time_of_day_unpaused() { +inline mem::safe_ptr time_of_day_unpaused() { return _main[0x2CD18C8][0x17630] + 0x468; } -inline safe_ptr blood_moon() { +inline mem::safe_ptr blood_moon() { return _main[0x2CD18D0][0x30][0x398] + 0x2D0; } // Climate Damage -inline safe_ptr temperature_damage_timer() { +inline mem::safe_ptr temperature_damage_timer() { return _main[0x2CA1140][0x60] + 0x268C; } -inline safe_ptr flame_timer() { +inline mem::safe_ptr flame_timer() { return _main[0x2CA3A68][0x1438][0xB0] + 0x258; } // Champion Abilities -inline safe_ptr gale_timer() { +inline mem::safe_ptr gale_timer() { return _main[0x2CA1140][0x60] + 0x1DF4; } -inline safe_ptr fury_timer() { +inline mem::safe_ptr fury_timer() { return _main[0x2CA1140][0x60] + 0x1E00; } -inline safe_ptr protection_timer() { +inline mem::safe_ptr protection_timer() { return _main[0x2CA1140][0x60] + 0x1E0C; } -inline safe_ptr grace_timer() { +inline mem::safe_ptr grace_timer() { return _main[0x2CA1140][0x60] + 0x1E18; } -inline safe_ptr ability_uses() { +inline mem::safe_ptr ability_uses() { return _main[0x2CA1A78][0x80] + 0x1CC8; } // Master Sword -inline safe_ptr master_sword_cooldown() { +inline mem::safe_ptr master_sword_cooldown() { return _main[0x2CA1140][0x60] + 0x1E24; } // Potion -inline safe_ptr speed_potion_timer1() { +inline mem::safe_ptr speed_potion_timer1() { return _main[0x2CC4768][0xC0] + 0x12A0; } -inline safe_ptr speed_potion_timer2() { +inline mem::safe_ptr speed_potion_timer2() { return _main[0x2CC4768][0xC0] + 0x12AC; } -inline safe_ptr speed_potion_timer3() { +inline mem::safe_ptr speed_potion_timer3() { return _main[0x2CC4768][0xC0] + 0x12B8; } -inline safe_ptr attack_potion_timer() { +inline mem::safe_ptr attack_potion_timer() { return _main[0x2CC4768][0xC0] + 0x12DC; } -inline safe_ptr defense_potion_timer() { +inline mem::safe_ptr defense_potion_timer() { return _main[0x2CC4768][0xC0] + 0x12E8; } -inline safe_ptr heat_resist_potion_timer() { +inline mem::safe_ptr heat_resist_potion_timer() { return _main[0x2CC4768][0xC0] + 0x12F4; } -inline safe_ptr cold_resist_potion_timer() { +inline mem::safe_ptr cold_resist_potion_timer() { return _main[0x2CC4768][0xC0] + 0x1300; } -inline safe_ptr flame_resist_potion_timer() { +inline mem::safe_ptr flame_resist_potion_timer() { return _main[0x2CC4768][0xC0] + 0x130C; } -inline safe_ptr shock_resist_potion_timer() { +inline mem::safe_ptr shock_resist_potion_timer() { return _main[0x2CC4768][0xC0] + 0x1324; } -inline safe_ptr stealth_potion_timer() { +inline mem::safe_ptr stealth_potion_timer() { return _main[0x2CC4768][0xC0] + 0x133C; } -} // namespace botw::savs::raw_ptr +} // namespace botw::savs::raw_ptr #undef _main diff --git a/src/impl/sead/SafeStringImpl.cpp b/src/impl/sead/SafeStringImpl.cpp deleted file mode 100644 index 11efa55..0000000 --- a/src/impl/sead/SafeStringImpl.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include - -namespace sead { -template <> -const char SafeStringBase::cNullChar = '\0'; - -template <> -const char SafeStringBase::cLineBreakChar = '\n'; - -template <> -const SafeStringBase SafeStringBase::cEmptyString(""); - -template <> -const char16 SafeStringBase::cNullChar = 0; - -template <> -const char16 SafeStringBase::cLineBreakChar = static_cast('\n'); - -template <> -void BufferedSafeStringBase::assureTerminationImpl_() const { - const_cast(this)->getMutableStringTop_()[mBufferSize - 1] = - this->cNullChar; -} -template <> -SafeStringBase& SafeStringBase::operator=(const SafeStringBase& other) = default; - -template <> -SafeStringBase& -SafeStringBase::operator=(const SafeStringBase& other) = default; - -template <> -BufferedSafeStringBase& -BufferedSafeStringBase::operator=(const SafeStringBase& other) -{ - copy(other); - return *this; -} - -template <> -BufferedSafeStringBase& -BufferedSafeStringBase::operator=(const SafeStringBase& other) -{ - copy(other); - return *this; -} - -} // namespace sead diff --git a/src/main.cpp b/src/main.cpp index c36085b..1ca4352 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,22 @@ #include #include +#include +#include +#include +#include #include "core/worker.hpp" -#include "util/message.hpp" extern "C" void exl_main(void* x0, void* x1) { exl::hook::Initialize(); nn::fs::MountSdCardForDebug("sd"); - botw::savs::msg::install_hooks(); + botw::msg::info::init(); + botw::msg::widget::init(); + botw::toolkit::equipment::init(); + botw::tcp::init(); botw::savs::start_worker_thread(); } -extern "C" NORETURN void exl_exception_entry() { - EXL_ABORT(0x420); -} +extern "C" NORETURN void exl_exception_entry() { EXL_ABORT(0x420); } diff --git a/src/util/data_reader.cpp b/src/util/data_reader.cpp deleted file mode 100644 index 5662503..0000000 --- a/src/util/data_reader.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include - -#include "util/data_reader.hpp" - -namespace botw::savs { - -DataReader::DataReader(const char* path) : m_file(path) { - if (!m_file.open()) { - m_success = false; - return; - } -} - -bool DataReader::do_read_line(u32* out_line_length) { - u32 line_length; - if (!m_buffer.index_of('\n', 0, &line_length)) { - // no new line character found, try reading more - s64 readSize = m_file.read(m_buffer); - if (readSize < 0) { - return false; - } - - if (!m_buffer.index_of('\n', 0, &line_length)) { - // still no new line, probably error - return false; - } - } - - m_buffer.set(line_length, '\0'); - // find a comment character - u32 comment_index; - if (m_buffer.index_of('#', 0, &comment_index)) { - // remove comment - m_buffer.set(comment_index, '\0'); - } - - // including the comment length because we need to delete it later - *out_line_length = line_length; - return true; -} - -bool DataReader::do_read_integer(u64* out_value) { - u32 line_length; - if (!do_read_line(&line_length)) { - return false; - } - - // Convert to hex - u64 value = strtol(m_buffer.content(), nullptr, 0); - m_buffer.delete_front(line_length + 1); - *out_value = value; - return true; -} - -bool DataReader::do_read_string(char* out_string, const u32 buffer_length) { - u32 line_length; - if (!do_read_line(&line_length)) { - return false; - } - strncpy(out_string, m_buffer.content(), buffer_length); - m_buffer.delete_front(line_length + 1); - out_string[buffer_length - 1] = '\0'; - - return true; -} - -} // namespace botw::savs diff --git a/src/util/data_reader.hpp b/src/util/data_reader.hpp deleted file mode 100644 index 3923f7d..0000000 --- a/src/util/data_reader.hpp +++ /dev/null @@ -1,105 +0,0 @@ - -#pragma once - -#include "util/file.hpp" -#include "util/named_value.hpp" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" - -namespace botw::savs { -class DataReader { -public: - DataReader(const char* path); - ~DataReader() = default; - - bool is_successful() const { return m_success; } - - void read_bool(bool* out) { - u64 i_value; - read_integer(&i_value); - if (!m_success) { - return; - } - *out = i_value != 0; - } - - void read_integer(u64* out) { - if (m_success) { - m_success = do_read_integer(out); - } - } - - void read_integer(u32* out) { - u64 i_value; - if (!do_read_integer(&i_value)) { - m_success = false; - return; - } - - u32 truncated = (u32)(i_value); - *out = truncated; - } - - void read_integer(s32* out) { read_integer(reinterpret_cast(out)); } - - void read_string(char* out_string, const u32 max_length) { - if (m_success) { - m_success = do_read_string(out_string, max_length); - } - } - void read_float(f32* value) { - u32 i_value; - read_integer(&i_value); - if (!m_success) { - return; - } - *value = reinterpret_cast(i_value); - } - template - void read_integer_array(T* out_array, u32 size) { - read_integer(out_array); - if (!m_success) { - return; - } - for (u32 i = 1; i < size; i++) { - read_integer(out_array + i); - if (!m_success) { - return; - } - } - } - void read_float_array(f32* out_array, u32 size) { - read_float(out_array); - if (!m_success) { - return; - } - for (u32 i = 1; i < size; i++) { - read_float(out_array + i); - if (!m_success) { - return; - } - } - } - template - void read_named_integer(NamedValue& value) { - read_string(value.name(), L); - read_integer(value.value_ptr()); - } - template - void read_named_float(const NamedValue& value) { - read_string(value.name(), L); - read_float(value.value_ptr()); - } - -private: - bool m_success = true; - File m_file; - FileBuffer m_buffer; - - bool do_read_line(u32* out_line_length); - bool do_read_integer(u64* out_value); - bool do_read_string(char* out_string, const u32 max_length); -}; -} // namespace botw::savs -#pragma GCC diagnostic pop diff --git a/src/util/data_writer.cpp b/src/util/data_writer.cpp deleted file mode 100644 index b065168..0000000 --- a/src/util/data_writer.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include "util/data_writer.hpp" - -namespace botw::savs { - -DataWriter::DataWriter(const char* path) : m_file(path) { - if (!m_file.exists()) { - if (!m_file.create()) { - m_success = false; - return; - } - } - if (!m_file.open()) { - m_success = false; - } -} - -bool DataWriter::do_write_number(const char* field_name, u64 value) { - m_buffer.clear(); - m_buffer.appendf("0x%016x", value); - m_buffer.appendf("# %s\n", field_name); - return m_file.write(m_buffer); -} - -bool DataWriter::do_write_string(const char* field_name, const char* string) { - m_buffer.clear(); - m_buffer.appendf("%s", string); - m_buffer.appendf("# %s\n", field_name); - return m_file.write(m_buffer); -} - -} // namespace botw::savs diff --git a/src/util/data_writer.hpp b/src/util/data_writer.hpp deleted file mode 100644 index b70ad9a..0000000 --- a/src/util/data_writer.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "util/file.hpp" -#include "util/named_value.hpp" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" - -namespace botw::savs { -class DataWriter { -public: - DataWriter(const char* path); - ~DataWriter() = default; - - bool is_successful() const { return m_success; } - - void write_bool(const char* field_name, const bool value) { - if (m_success) { - m_success = do_write_number(field_name, value ? 1 : 0); - } - } - - void write_integer(const char* field_name, const u64 value) { - if (m_success) { - m_success = do_write_number(field_name, value); - } - } - - void write_string(const char* field_name, const char* string) { - if (m_success) { - m_success = do_write_string(field_name, string); - } - } - - void write_float(const char* fieldName, const f32 value) { - write_integer(fieldName, reinterpret_cast(value)); - } - - template - void write_integer_array(const char* field_name, const T* array, u32 size) { - write_integer(field_name, array[0]); - if (!m_success) { - return; - } - for (u32 i = 1; i < size; i++) { - write_integer(" ---", array[i]); - if (!m_success) { - return; - } - } - } - void write_float_array(const char* field_name, const f32* array, u32 size) { - write_float(field_name, array[0]); - if (!m_success) { - return; - } - for (u32 i = 1; i < size; i++) { - write_float(" ---", array[i]); - if (!m_success) { - return; - } - } - } - - template - void write_named_integer(const char* field_name, const NamedValue& value) { - write_string("Name of value below", value.name()); - write_integer(field_name, value.value()); - } - - template - void write_named_float(const char* field_name, const NamedValue& value) { - write_string("Name of value below", value.name()); - write_float(field_name, value.value()); - } - -private: - bool m_success = true; - File m_file; - FileBuffer m_buffer; - - bool do_write_number(const char* field_name, const u64 value); - bool do_write_string(const char* field_name, const char* string); -}; - -} // namespace botw::savs -#pragma GCC diagnostic pop diff --git a/src/util/file.cpp b/src/util/file.cpp deleted file mode 100644 index 099cb9e..0000000 --- a/src/util/file.cpp +++ /dev/null @@ -1,114 +0,0 @@ - -#include "util/file.hpp" - -namespace botw::savs { - -File::File(const char* path) { - m_path = path; -} - -File::~File() { - if (m_open) { - close(); - } -} - -bool File::exists() { - nn::fs::DirectoryEntryType type; - nn::Result result = nn::fs::GetEntryType(&type, m_path); - - if (result.IsFailure()) { - return false; - } - - return type != nn::fs::DirectoryEntryType_Directory; -} - -bool File::create() { - if (exists()) { - return false; - } - - nn::Result result = nn::fs::CreateFile(m_path, 0); - - return result.IsSuccess(); -} - -bool File::open() { - if (m_open) { - return false; - } - - if (!exists()) { - return false; - } - - nn::Result result = - nn::fs::OpenFile(&m_handle, m_path, nn::fs::OpenMode_ReadWrite | nn::fs::OpenMode_Append); - - m_open = result.IsSuccess(); - return m_open; -} - -bool File::close() { - if (!m_open) { - return false; - } - nn::fs::CloseFile(m_handle); - m_open = false; - return true; -} - -bool File::clear() { - if (!m_open) { - return false; - } - nn::Result result = nn::fs::SetFileSize(m_handle, 0); - m_offset = 0; - - return result.IsSuccess(); -} - -bool File::write(const FileBuffer& buffer) { - if (!m_open) { - return false; - } - nn::Result result = nn::fs::SetFileSize(m_handle, m_offset + buffer.len()); - if (result.IsFailure()) { - return false; - } - - result = nn::fs::WriteFile(m_handle, m_offset, buffer.content(), buffer.len(), - nn::fs::WriteOption::CreateOption(nn::fs::WriteOptionFlag_Flush)); - if (result.IsFailure()) { - return false; - } - - m_offset += buffer.len(); - - return true; -} -s64 File::read(FileBuffer& buffer) { - if (!m_open) { - return -1; - } - - u64 readSize = 0; - - nn::Result result = - nn::fs::ReadFile(&readSize, m_handle, m_offset, buffer.last(), 1024 - buffer.len()); - if (result.IsFailure()) { - return -1; - } - - if (readSize == 0) { - return -1; - } - - buffer.increase_length(readSize); - m_offset += readSize; - - return static_cast(readSize); -} - -} // namespace botw::savs diff --git a/src/util/file.hpp b/src/util/file.hpp deleted file mode 100644 index f606b4b..0000000 --- a/src/util/file.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include - -#include "util/string.hpp" - -namespace botw::savs { - -using FileBuffer = StringBuffer<1024>; - -class File { -public: - File(const char* path); - ~File(); - - // Check if file exists - bool exists(); - // Try creating a new file - bool create(); - // Open the file for read and write - bool open(); - // Close the file - bool close(); - // Set file to empty - bool clear(); - // Write to file - bool write(const FileBuffer& buffer); - // Read into buffer - s64 read(FileBuffer& buffer); - - const char* path() const { return m_path; } - bool is_opened() const { return m_open; } - -private: - const char* m_path = nullptr; - bool m_open = false; - nn::fs::FileHandle m_handle; - u64 m_offset = 0; -}; - -} // namespace botw::savs diff --git a/src/util/mem.h b/src/util/mem.h deleted file mode 100644 index 3a2dcad..0000000 --- a/src/util/mem.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file mem.h - * @brief Memory functions. - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void* memset(void* src, int val, uint64_t num); -void* memcpy(void* dest, void const* src, uint64_t count); -void* memmove(void* dest, const void* src, uint64_t count); -void* memalign(size_t alignment, size_t size); - -#ifdef __cplusplus -} -#endif diff --git a/src/util/message.cpp b/src/util/message.cpp deleted file mode 100644 index 2580aae..0000000 --- a/src/util/message.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "util/message.hpp" -#include -#include -#include -#include -#include -#include -#include "util/string.hpp" - -namespace botw::savs::msg { - -// can display around 40 chars without truncation in US and EU region languages -constexpr size_t INFO_BUFFER_LEN = 40; -constexpr size_t WIDGET_BUFFER_LEN = 280; -constexpr const char* WIDGET_MSG_ID = "0025"; - -static WStringBuffer s_info; -static u32 s_info_idx = 0; -static WStringBuffer s_widget; -static u32 s_widget_idx = 0; - -// Hooking the message loader function -HOOK_DEFINE_TRAMPOLINE(KsysUiGetMessageHook){ - static bool Callback(sead::SafeString * file, sead::SafeString* msg_id, - WideString* out){if (*file == "LayoutMsg/MainScreen_00"){ - // handle info overlay - // 0028 - The Master Sword has returned to the forest - // 0061 - In this demo version, you can't advance any farther - if (*msg_id == "0061" || *msg_id == "0028"){// output the previously set message - out->content = s_info.content(); -; -out->length = s_info.len(); -return 0; -} // namespace botw::savs::msg -} -else if (*file == "LayoutMsg/MessageTipsRunTime_00") { - // handle widgets - if (*msg_id == WIDGET_MSG_ID) { - out->content = s_widget.content(); - out->length = s_widget.len(); - return 0; - } -} -// original function for other cases -return Orig(file, msg_id, out); -} -} -; - -// Hooking the initialization of message tip -// to override the "Wolf Link" text and flag -HOOK_DEFINE_TRAMPOLINE(KsysUiInitMessageTipsRuntimeHook){static void Callback(){Orig(); -ksys_ui_sRuntimeTips[0x0E].m_label = WIDGET_MSG_ID; -// note that this will not work for 1.5.0 -// 1.6.0 added a message for VR mode without a flag, -// so they added a check for skipping the flag check when -// the flag is empty -ksys_ui_sRuntimeTips[0x0E].m_flag = ""; -} -} -; - -HOOK_DEFINE_TRAMPOLINE(ScreenMessageTipsRuntimeShowMessageTipHook){ - static void Callback(void* this_, u32 idx, bool){ - if (idx == 0x17 || idx == 0x0E){u32 set_idx = idx == 0x17 ? 0x0E : 0x17; -(reinterpret_cast(this_))[0x365C] = false; -(reinterpret_cast(this_))[0xD96] = set_idx; -(reinterpret_cast(this_))[0xD98] = set_idx; -nn::os::YieldThread(); -nn::os::SleepThread(nn::TimeSpan::FromNanoSeconds(100 * 1000 * 1000)); -} -return Orig(this_, idx, true); -} -} -; - -/* namespace inst = exl::armv8::inst; */ - -void install_hooks() { - KsysUiGetMessageHook::InstallAtOffset(0x123DEA0); - KsysUiInitMessageTipsRuntimeHook::InstallAtOffset(0x20950); - ScreenMessageTipsRuntimeShowMessageTipHook::InstallAtOffset(0x119C750); - - // patch ScreenMessageTipsRuntime::doShowMessageTip(eui::Screen* this, idx, bool) - // 0x119C750 in 1.6.0 - /* exl::patch::CodePatcher patcher { 0x0119C788 }; */ - // patching out checking if previous message is the same - /* patcher.WriteInst(inst::Nop()); */ - /* patcher.WriteInst(inst::Nop()); */ - /* patcher.WriteInst(inst::Nop()); */ - /* patcher.WriteInst(inst::Nop()); */ - /* patcher.WriteInst(inst::Nop()); */ -} - -void show_info(const char* message) { - s_info.copy_from(message); - // cycle the message index so the game would clear the previous one - s_info_idx = s_info_idx == 0x21 ? 0x2A : 0x21; - ksys_ui_showInfoOverlayWithString(s_info_idx, &sead::SafeStringBase::cEmptyString); -} - -void show_infof(const char* format, ...) { - char result[INFO_BUFFER_LEN + 1]; - result[INFO_BUFFER_LEN - 1] = '\0'; - va_list args; - va_start(args, format); - int size = vsnprintf(result, INFO_BUFFER_LEN, format, args); - va_end(args); - if (size <= 0) { - return; - } - result[size] = '\0'; - show_info(result); -} - -bool show_widget(const char* message) { - if (!ksys_ui_ScreenMgr_sInstance) { - return false; - } - // idk what this is - void** screens = ksys_ui_ScreenMgr_sInstance->screens; - if (ksys_ui_ScreenMgr_sInstance->num_screens > 0x29) { - screens += 0x29; - } - void* screen = *screens; - // should be a sead::DynamicCast here - if (!screen) { - return false; - } - s_widget.copy_from(message); - // cycle the message index so the game would clear the previous one - /* (reinterpret_cast(screen))[0x365C] = false; */ - /* (reinterpret_cast(screen))[0xD96] = s_widget_idx; */ - /* (reinterpret_cast(screen))[0xD98] = s_widget_idx; */ - // ^ totally safe don't look - // let screen mgr delete the previous message - /* nn::os::YieldThread(); */ - /* nn::os::SleepThread(nn::TimeSpan::FromNanoSeconds(1000 * 1000 * 1000)); */ - s_widget_idx = s_widget_idx == 0x17 ? 0x0E : 0x17; - ScreenMessageTipsRuntime_doShowMessageTip(screen, s_widget_idx, true); - // yield thread to let the screen manager pick up the new message quicker - /* nn::os::YieldThread(); */ - return true; -} - -bool show_widgetf(const char* format, ...) { - char result[WIDGET_BUFFER_LEN + 1]; - result[WIDGET_BUFFER_LEN - 1] = '\0'; - va_list args; - va_start(args, format); - int size = vsnprintf(result, WIDGET_BUFFER_LEN, format, args); - va_end(args); - if (size <= 0) { - return false; - } - result[size] = '\0'; - return show_widget(result); -} - -} // namespace botwsavs::ui diff --git a/src/util/message.hpp b/src/util/message.hpp deleted file mode 100644 index a069c11..0000000 --- a/src/util/message.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/** - * This is a patch for BOTW 1.6.0 to show custom messages on the screen - * This overrides: - * - The info overlay message (such as "Your Pot Lid is badly damaged") - * - The tip widget message (such as "The area is very cold ...") - * - * The basic idea is to hook into the function that loads the actual message - * to load the overridden message instead. Then call the functions to display - * the message. - * - */ -#pragma once -#include -#include -#include -#include - -struct WideString { // This is probably a eui::MessageString - char16_t* content = nullptr; - u64 length = 0; -}; - -struct RuntimeTip { - sead::SafeString m_label; - sead::SafeString m_flag; -}; -static_assert(sizeof(RuntimeTip) == 0x20); - -struct ScreenMgr { - void* vtable; - char disposer[0x20]; - u32 num_screens; - void** screens; -}; -static_assert(offsetof(ScreenMgr, num_screens) == 0x28); -static_assert(offsetof(ScreenMgr, screens) == 0x30); - -extern "C" { -// BOTW Symbols -// These can be replaced with the decomp headers once they are available - -// 0x0123DEA0 (1.6.0) -// 0x00AA248C (1.5.0) -// Load msg_id from file and put it in out -// file is -// - "LayoutMsg/MessageTipsRunTime_00" for widgets -// - "LayoutMsg/MainScreen_00" for info overlay -// -// msg_id is like "0001" -// return 0 for success -/* u64 ksys_ui_getMessage( */ -/* sead::SafeString* file, sead::SafeString* msg_id, WideString* out); */ - -// 0x01238680 (1.6.0) -// 0x00A95924 (1.5.0) -void* ksys_ui_showInfoOverlayWithString(u64 idx, const sead::SafeString* info); - -// 0x0119C750 (1.6.0) -void ScreenMessageTipsRuntime_doShowMessageTip(void* this_, u32 idx, bool); - -// 0x00020950 (1.6.0) -// 0x00A261CC (1.5.0) -/* void ksys_ui_initMessageTipsRuntime(); */ - -// 0x02CBA3A8 (1.6.0) -// 0x025EFC08 (1.5.0) -/* extern u32 ksys_ui_sRuntimeTipsNum; */ - -// 0x02CBA3B0 (1.6.0) -// 0x025EFC10 (1.5.0) -extern RuntimeTip* ksys_ui_sRuntimeTips; - -// 0x02CC2490 (1.6.0) -// 0x025FCC68 (1.5.0) -extern ScreenMgr* ksys_ui_ScreenMgr_sInstance; -} - -namespace botw::savs::msg { - -/** - * Initialize the hook system to override the messages - */ -void install_hooks(); - -/** - * Show a custom info message on the screen - */ -void show_info(const char* message); - -/** - * Show a custom formatted info message on the screen - */ -void show_infof(const char* format, ...); - -/** - * Show a custom widget message on the top-right corner of the screen - */ -bool show_widget(const char* message); - -/** - * Show a custom formatted widget message on the top-right corner of the screen - */ -bool show_widgetf(const char* message, ...); - -} // namespace botw::savs::msg diff --git a/src/util/named.h b/src/util/named.h deleted file mode 100644 index fc49231..0000000 --- a/src/util/named.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define _named(x) #x, x diff --git a/src/util/named_value.hpp b/src/util/named_value.hpp deleted file mode 100644 index 007ea58..0000000 --- a/src/util/named_value.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include -#include - -namespace botw::savs { - -template -class NamedValue { -public: - NamedValue(T initial) : m_value(initial) { m_name[0] = '\0'; } - - void set_name(const sead::FixedSafeString& string) { - strncpy(m_name, string.cstr(), L); - m_name[L - 1] = '\0'; - } - - bool name_matches(const sead::FixedSafeString& string) const { - return strncmp(m_name, string.cstr(), L) == 0; - } - - void clear_name() { m_name[0] = '\0'; } - bool is_name_empty() const { return m_name[0] == '\0'; } - const char* name() const { return m_name; } - char* name() { return m_name; } - T value() const { return m_value; } - T* value_ptr() { return &m_value; } - void set_value(T value) { m_value = value; } - - void set(const sead::FixedSafeString& string, T value) { - set_name(string); - set_value(value); - } - -private: - char m_name[L]; - T m_value; -}; -} // namespace botw::savs diff --git a/src/util/safe_memory.cpp b/src/util/safe_memory.cpp deleted file mode 100644 index 41be818..0000000 --- a/src/util/safe_memory.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -namespace botw::savs { - -bool ptr_looks_safe(const void* ptr) { - u64 raw = reinterpret_cast(ptr); - - if (raw > 0xFFFFFFFFFF || (raw >> 32 == 0)) { - return false; - } - if ((raw & 3) != 0) { - return false; - } - - return true; -} - -} // namespace botw::savs diff --git a/src/util/safe_memory.hpp b/src/util/safe_memory.hpp deleted file mode 100644 index 30f8377..0000000 --- a/src/util/safe_memory.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include - -namespace botw::savs { - -/** - * Check if the value looks like a pointer - */ -bool ptr_looks_safe(const void* ptr); - -class mem_ptr { -public: - // Create pointer to main - mem_ptr(void** ptr) { m_ptr = reinterpret_cast(ptr); } - // Copy constructor - mem_ptr(const mem_ptr& other) { - m_ptr = other.m_ptr; - m_error = other.m_error; - } - // Offset - mem_ptr& add(s64 offset) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" - m_ptr += offset; -#pragma GCC diagnostic pop - return *this; - } - // p+x is the same as p.add(xxx) - mem_ptr& operator+(s64 offset) { return add(offset); } - // Deferring and storing the value as pointer - mem_ptr& deref() { - if (m_error || !ptr_looks_safe(m_ptr)) { - m_error = true; - return *this; - } - char** pp = reinterpret_cast(m_ptr); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" - m_ptr = *pp; -#pragma GCC diagnostic pop - return *this; - } - // p[x] is the same as p.add(xxx).deref() - mem_ptr& operator[](s64 offset) { return add(offset).deref(); } - -public: - char* m_ptr; - bool m_error = false; -}; - -template -class safe_ptr { -public: - safe_ptr(T* ptr) : m_ptr(ptr) {} - safe_ptr(const safe_ptr& other) : m_ptr(other.m_ptr) {} - safe_ptr(const mem_ptr& p) { m_ptr = p.m_error ? nullptr : reinterpret_cast(p.m_ptr); } - - bool take_ptr(T** out) const { - if (!looks_safe()) { - return false; - } - *out = m_ptr; - return true; - } - - bool get(T* out) const { - if (!looks_safe()) { - return false; - } - *out = *m_ptr; - return true; - } - - bool set(T value) const { - if (!looks_safe()) { - return false; - } - *m_ptr = value; - return true; - } - - bool get(T* out, u32 i) const { - if (!looks_safe()) { - return false; - } - *out = m_ptr[i]; - return true; - } - - bool set(T value, u32 i) const { - if (!looks_safe()) { - return false; - } - m_ptr[i] = value; - return true; - } - - bool get_array(T* out, u32 len) const { - if (!looks_safe()) { - return false; - } - for (u32 i = 0; i < len; i++) { - out[i] = m_ptr[i]; - } - return true; - } - - bool set_array(const T* array, u32 len) const { - if (!looks_safe()) { - return false; - } - for (u32 i = 0; i < len; i++) { - m_ptr[i] = array[i]; - } - return true; - } - - bool looks_safe() const { return ptr_looks_safe(m_ptr); } - -private: - T* m_ptr; -}; - -} // namespace botw::savs diff --git a/src/util/scoped_lock.hpp b/src/util/scoped_lock.hpp deleted file mode 100644 index 0d599fe..0000000 --- a/src/util/scoped_lock.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include - -namespace botw::savs { -class ScopedLock { -public: - ScopedLock(nn::os::MutexType* mutex) : m_mutex(mutex), m_locked(false) { - if (m_mutex) { - nn::os::LockMutex(m_mutex); - m_locked = true; - } - } - ~ScopedLock() { - if (m_locked) { - nn::os::UnlockMutex(m_mutex); - } - } - -private: - nn::os::MutexType* m_mutex; - bool m_locked; -}; - -}; // namespace botw::savs diff --git a/src/util/string.hpp b/src/util/string.hpp deleted file mode 100644 index ad0f845..0000000 --- a/src/util/string.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace botw::savs { - -template -class StringBufferBase { -public: - StringBufferBase() { clear(); } - void clear() { - m_len = 0; - ensure_termination(); - } - const T* content() const { return m_content; } - T* content() { return m_content; } - T* last() { return m_content + m_len; } - u32 len() const { return m_len; } // may include null byte in the middle - - void increase_length(u32 size) { - if (size > 0) { - m_len += size; - if (m_len > L) { - m_len = L; - } - } - ensure_termination(); - } - - void append(const T* text) { - strncpy(last(), text, L - m_len); - increase_length(strlen(text)); - } - - void copy(const T* text) { - clear(); - append(text); - } - - void ensure_termination() { m_content[m_len] = '\0'; } - - bool index_of(T search, u32 from, u32* outIndex) { - for (u32 i = from; i < m_len; i++) { - if (m_content[i] == search) { - *outIndex = i; - return true; - } - } - return false; - } - - void set(u32 i, T c) { - if (i < m_len) { - m_content[i] = c; - } - } - - void delete_front(u32 size) { - if (size > m_len) { - clear(); - return; - } - for (u32 i = 0; i < m_len - size; i++) { - m_content[i] = m_content[i + size]; - } - m_len -= size; - ensure_termination(); - } - - void delete_end(u32 size) { - if (m_len <= size) { - m_len = 0; - } else { - m_len -= size; - } - ensure_termination(); - } - -protected: - T m_content[L + 1]; - u32 m_len; -}; - -template -class StringBuffer : public StringBufferBase { -public: - void appendf(const char* format, ...) { - va_list args; - va_start(args, format); - int size = vsnprintf(this->last(), L - this->m_len, format, args); - va_end(args); - this->increase_length(size); - } -}; - -template -class WStringBuffer : public StringBufferBase { -public: - void copy_from(const char* text) { - for (u32 i = 0; i < L; i++) { - this->m_content[i] = text[i]; - if (text[i] == '\0') { - this->m_len = i; - return; - } - } - this->m_len = L; - this->ensure_termination(); - } -}; - -} // namespace botw::savs diff --git a/src/util/time.cpp b/src/util/time.cpp deleted file mode 100644 index b3a9e62..0000000 --- a/src/util/time.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include - -#include "util/time.hpp" - -namespace botw::savs { - -void get_current_time(TimeBuffer& out) { - out.clear(); - if (!nn::time::IsInitialized()) { - out.append("0000-00-00 00:00:00"); - return; - } - nn::time::PosixTime posixTime; - nn::time::CalendarAdditionalInfo calendarAddtionalInfo; - nn::time::CalendarTime calendarTime; - nn::time::StandardUserSystemClock::GetCurrentTime(&posixTime); - nn::time::ToCalendarTime(&calendarTime, &calendarAddtionalInfo, posixTime); - - out.appendf("%04hu-", calendarTime.year); - out.appendf("%02hu-", calendarTime.month); - out.appendf("%02hu ", calendarTime.day); - out.appendf("%02hu:", calendarTime.hour); - out.appendf("%02hu:", calendarTime.minute); - out.appendf("%02hu", calendarTime.second); -} - -} // namespace botw::savs diff --git a/src/util/time.hpp b/src/util/time.hpp deleted file mode 100644 index b1ce316..0000000 --- a/src/util/time.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "util/string.hpp" - -namespace botw::savs { -using TimeBuffer = StringBuffer<20>; -/** - * Get current time as string, format YYYY-MM-DD HH:MM:SS - */ -void get_current_time(TimeBuffer& outBuffer); -} // namespace botw::savs