diff --git a/CHANGELOG.md b/CHANGELOG.md index c52a740..be5c897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # CHANGELOG -### `1.6.0-SS0.2` `2022-08-21` `LATEST` +### `1.6.0-SS1.0` `2022-08-26` `LATEST` +- Level 3 save and restore +- Change save/restore key combo to include both left triggers (`L` and `ZL`). This is so that you don't accidentally save or restore during a super launch +- Enable logging (except for debug logging) in ship builds + +### `1.6.0-SS0.2` `2022-08-21` - Save and restore equipped weapon/bow/shield durability and equipped arrow count - Durability/count will only restore if you have the same item equipped as when you saved - Otherwise you will see a warning message, but the ones that do match will restore fine. diff --git a/Justfile b/Justfile index 547a135..4ffd5b1 100644 --- a/Justfile +++ b/Justfile @@ -47,15 +47,14 @@ release EXTRA_DEFINES="": clean (_make EXTRA_DEFINES) mkdir -p release/atmosphere/contents/01007EF00011E000/romfs/System mkdir -p release/atmosphere/exefs_patches/botwsavs mkdir -p release/botwsavs - cp build/botwsavs.nso release/atmosphere/contents/01007EF00011E000/exefs/subsdk9 - cp build/app.npdm release/atmosphere/contents/01007EF00011E000/exefs/main.npdm - cp build/8E9978D50BDD20B4C8395A106C27FFDE.ips release/atmosphere/exefs_patches/botwsavs/8E9978D50BDD20B4C8395A106C27FFDE.ips + cp build{{EXTRA_DEFINES}}/botwsavs.nso release/atmosphere/contents/01007EF00011E000/exefs/subsdk9 + cp build{{EXTRA_DEFINES}}/app.npdm release/atmosphere/contents/01007EF00011E000/exefs/main.npdm + cp build{{EXTRA_DEFINES}}/8E9978D50BDD20B4C8395A106C27FFDE.ips release/atmosphere/exefs_patches/botwsavs/8E9978D50BDD20B4C8395A106C27FFDE.ips cp README.md release cp CHANGELOG.md release - cp standalone_ftp.py release/ftp.py echo "" > release/botwsavs/latest.txt echo -n {{VERSION_TEXT}} > release/atmosphere/contents/01007EF00011E000/romfs/System/Version.txt - cd release && zip -r ../save-state-{{VERSION_TEXT}}.zip * + cd release && zip -r ../save-state-{{VERSION_TEXT}}{{EXTRA_DEFINES}}.zip * # Build gold rush configuration and package release-gold-rush: (release "-DGOLD_RUSH") diff --git a/README.md b/README.md index e00f598..39265f9 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,14 @@ Save state: Active Mode ### Active Mode Combos You can use these key combos in the Active Mode. -- `PLUS + Dpad Left`: Save state to memory -- `PLUS + R3 + Dpad Left`: Save state to `/botwsavs/latest.txt` on sd card -- `PLUS + Dpad Right`: Restore state from memory -- `PLUS + R3 + Dpad Right`: Restore state from `/botwsavs/restore.txt` on sd card +- `ZL + L + PLUS + Dpad Left`: Save state to memory +- `ZL + L + PLUS + R3 + Dpad Left`: Save state to `/botwsavs/latest.txt` on sd card +- `ZL + L + PLUS + Dpad Right`: Restore state from memory +- `ZL + L + PLUS + R3 + Dpad Right`: Restore state from `/botwsavs/restore.txt` on sd card -Note that for save to/restore from file, you need to hold `PLUS + R3` before pressing the D pad. Otherwise it would trigger save to/restore from memory instead +Note that for save to/restore from file, you can't press `R3` last. Otherwise you would trigger save to/restore from memory instead. I usually prefer holding `Triggers + Dpad + R3`, then hit `PLUS` to trigger. + +Also note that the mod has a logging function that will block reading the `/botwsavs` folder when the game is running, if you read it from a ftp client like FileZilla. If you use the [script](#save-state-transfer-script) it will be fine. If you try to restore without saving, or without the right file on sd card. You will see a message like this ``` @@ -164,7 +166,7 @@ Release workflow: - `just release` to build for release configuration and produce the release zip - `just release-gold-rush` to build for gold rush configuration - `just publish` to tag the current commit with latest version -- `just draft-release` to generate release notes +- Upload the built release zip to GitHub manually - `just clean` ### Linking BOTW diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index 022b00f..f2e64cd 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -36,20 +36,21 @@ bool Controller::TryGetController() { } bool Controller::ShouldSaveState() { - return mpController->isHoldAll(Key::Plus | Key::DpadLeft) && !mpController->isHold(Key::RStick); + return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadLeft) && + !mpController->isHold(Key::RStick); } bool Controller::ShouldRestoreState() { - return mpController->isHoldAll(Key::Plus | Key::DpadRight) && + return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadRight) && !mpController->isHold(Key::RStick); } bool Controller::ShouldSaveStateToFile() { - return mpController->isHoldAll(Key::Plus | Key::DpadLeft | Key::RStick); + return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadLeft | Key::RStick); } bool Controller::ShouldRestoreStateFromFile() { - return mpController->isHoldAll(Key::Plus | Key::DpadRight | Key::RStick); + return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadRight | Key::RStick); } bool Controller::ShouldSwitchMode() { diff --git a/src/core/State.cpp b/src/core/State.cpp index a1e36bf..e608536 100644 --- a/src/core/State.cpp +++ b/src/core/State.cpp @@ -36,9 +36,4 @@ bool State::WriteToGame(u32 level) { return mError == Error::None; } -void State::ReadLevel3() { /*TODO*/ -} -void State::WriteLevel3() { /*TODO*/ -} - } // namespace botwsavs::core diff --git a/src/core/State.hpp b/src/core/State.hpp index 2905d25..a3f82b1 100644 --- a/src/core/State.hpp +++ b/src/core/State.hpp @@ -57,6 +57,30 @@ class State { void WriteLevel2(); // Level 3: All +public: + f32 mTimeOfDayPaused; + f32 mTimeOfDayUnpaused; + f32 mBloodMoonTimer; + f32 mTemperatureDamageTimer; + f32 mFlameTimer; + f32 mGaleTimer; + f32 mFuryTimer; + f32 mProtectionTimer; + f32 mGraceTimer; + u32 mAbilityUses[3]; + f32 mMasterSwordCooldown; + + f32 mSpeedPotionTimer1; + f32 mSpeedPotionTimer2; + f32 mSpeedPotionTimer3; + f32 mAttackPotionTimer; + f32 mDefensePotionTimer; + f32 mHeatResistPotionTimer; + f32 mColdResistPotionTimer; + f32 mFlameResistPotionTimer; + f32 mShockResistPotionTimer; + f32 mStealthPotionTimer; + private: void ReadLevel3(); void WriteLevel3(); diff --git a/src/core/StateLevel3.cpp b/src/core/StateLevel3.cpp new file mode 100644 index 0000000..87188cf --- /dev/null +++ b/src/core/StateLevel3.cpp @@ -0,0 +1,56 @@ +#include "State.hpp" +#include "StateMacros.hpp" +#include "fs/Logger.hpp" +#include "mem/GamePtr.h" + +namespace botwsavs::core { + +void State::ReadLevel3() { + GameReadEz(TimeOfDayPaused); + GameReadEz(TimeOfDayUnpaused); + GameReadEz(BloodMoonTimer); + GameReadEz(TemperatureDamageTimer); + GameReadEz(FlameTimer); + GameReadEz(GaleTimer); + GameReadEz(FuryTimer); + GameReadEz(ProtectionTimer); + GameReadEz(GraceTimer); + GameReadArrayEz(AbilityUses, 3); + GameReadEz(MasterSwordCooldown); + GameReadEz(SpeedPotionTimer1); + GameReadEz(SpeedPotionTimer2); + GameReadEz(SpeedPotionTimer3); + GameReadEz(AttackPotionTimer); + GameReadEz(DefensePotionTimer); + GameReadEz(HeatResistPotionTimer); + GameReadEz(ColdResistPotionTimer); + GameReadEz(FlameResistPotionTimer); + GameReadEz(ShockResistPotionTimer); + GameReadEz(StealthPotionTimer); +} + +void State::WriteLevel3() { + GameWriteEz(TimeOfDayPaused); + GameWriteEz(TimeOfDayUnpaused); + GameWriteEz(BloodMoonTimer); + GameWriteEz(TemperatureDamageTimer); + GameWriteEz(FlameTimer); + GameWriteEz(GaleTimer); + GameWriteEz(FuryTimer); + GameWriteEz(ProtectionTimer); + GameWriteEz(GraceTimer); + GameWriteArrayEz(AbilityUses, 3); + GameWriteEz(MasterSwordCooldown); + GameWriteEz(SpeedPotionTimer1); + GameWriteEz(SpeedPotionTimer2); + GameWriteEz(SpeedPotionTimer3); + GameWriteEz(AttackPotionTimer); + GameWriteEz(DefensePotionTimer); + GameWriteEz(HeatResistPotionTimer); + GameWriteEz(ColdResistPotionTimer); + GameWriteEz(FlameResistPotionTimer); + GameWriteEz(ShockResistPotionTimer); + GameWriteEz(StealthPotionTimer); +} + +} // namespace botwsavs::core diff --git a/src/core/Worker.cpp b/src/core/Worker.cpp index 22a670b..3aea100 100644 --- a/src/core/Worker.cpp +++ b/src/core/Worker.cpp @@ -2,7 +2,8 @@ #include "Worker.hpp" #include "fs/Logger.hpp" -#include "fs/SaveFile.hpp" +#include "fs/StateSaveFile.hpp" +#include "fs/WorkerSaveFile.hpp" #include "ui/OverlayString.hpp" namespace botwsavs::core { @@ -10,8 +11,8 @@ namespace botwsavs::core { bool Worker::Init() { info("Init worker config from file"); - fs::SaveFile workerTxt("sd:/botwsavs/worker.txt"); - bool result = workerTxt.LoadWorker(*this); + fs::WorkerSaveFile workerTxt(*this); + bool result = workerTxt.Load(); if (!result) { warn("File operation failed. Cannot init worker"); @@ -121,8 +122,8 @@ void Worker::ExecuteSaveToFile() { warnf("State read gives error 0x%x, but continuing to write file anyway", tempState.GetError()); } - fs::SaveFile latestTxt("sd:/botwsavs/latest.txt"); - bool result = latestTxt.Save(tempState); + fs::StateSaveFile latestTxt("sd:/botwsavs/latest.txt", tempState); + bool result = latestTxt.Save(); if (!result) { ui::ShowError(); @@ -141,9 +142,15 @@ void Worker::ExecuteRestore() { return; } + if (mState.mLevel == 0) { + error("Restore failed because state level is 0 (nothing stored)"); + ui::ShowCantDoThatRightNow(); + return; + } + if (mState.mLevel < mLevel) { error("Restore failed because state level is less than setting level"); - ui::ShowLevelError(); + ui::ShowLevelError(mState.mLevel); return; } @@ -167,14 +174,14 @@ void Worker::ExecuteRestoreFromFile() { } State tempState; - fs::SaveFile restoreTxt("sd:/botwsavs/restore.txt"); + fs::StateSaveFile restoreTxt("sd:/botwsavs/restore.txt", tempState); if (!restoreTxt.Exists()) { error("Restore failed because restore.txt does not exist"); ui::ShowCantDoThatRightNow(); return; } - bool result = restoreTxt.Load(tempState); + bool result = restoreTxt.Load(); if (!result) { ui::ShowError(); error("File operation failed"); @@ -182,7 +189,7 @@ void Worker::ExecuteRestoreFromFile() { } if (tempState.mLevel < mLevel) { error("Restore failed because state level is less than setting level"); - ui::ShowLevelError(); + ui::ShowLevelError(tempState.mLevel); return; } @@ -218,8 +225,8 @@ void Worker::DisplayStateError(State& state) { void Worker::SaveWorker() { info("Saving worker config to file"); - fs::SaveFile workerTxt("sd:/botwsavs/worker.txt"); - bool result = workerTxt.SaveWorker(*this); + fs::WorkerSaveFile workerTxt(*this); + bool result = workerTxt.Save(); if (!result) { warn("File operation failed. Cannot save worker"); diff --git a/src/fs/ConfigFile.cpp b/src/fs/ConfigFile.cpp new file mode 100644 index 0000000..9f1c726 --- /dev/null +++ b/src/fs/ConfigFile.cpp @@ -0,0 +1,120 @@ +#include "ConfigFile.hpp" +#include +#include "Logger.hpp" +//#include "util/StringBuffer.hpp" + +namespace botwsavs::fs { + +bool ConfigFile::Save() { + if (!mFile.Open()) { + return false; + } + + if (!mFile.Clear()) { + mFile.Close(); + return false; + } + mSuccess = true; + mMode = Mode::Save; + SaveInternal(); + mFile.Close(); + return mSuccess; +} + +bool ConfigFile::Load() { + if (!mFile.Open()) { + return false; + } + mSuccess = true; + mMode = Mode::Load; + mBuffer.Clear(); + LoadInternal(); + mFile.Close(); + return mSuccess; +} + +bool ConfigFile::ReadLine(u32* outLineLength) { + u32 lineLength; + if (!mBuffer.IndexOf('\n', 0, &lineLength)) { + // no new line character found, try reading more + s64 readSize = mFile.Read(mBuffer); + if (readSize < 0) { + error("Reached end of file"); + return false; + } + + if (!mBuffer.IndexOf('\n', 0, &lineLength)) { + // still no new line, probably error + error("Line is too long and cannot fit in buffer"); + return false; + } + } + + mBuffer.SafeSet(lineLength, '\0'); + // find a comment character + u32 commentIndex; + if (mBuffer.IndexOf('#', 0, &commentIndex)) { + // remove comment + mBuffer.SafeSet(commentIndex, '\0'); + } + + *outLineLength = lineLength; + return true; +} + +bool ConfigFile::WriteIntegerInternal(const char* fieldName, u64 value) { + if (mMode == Mode::Load) { + error("Cannot write in load mode"); + return false; + } + mBuffer.Clear(); + mBuffer.SafeAppendF("0x%016x", value); + mBuffer.SafeAppendF("# %s\n", fieldName); + return mFile.Write(mBuffer); +} + +bool ConfigFile::ReadIntegerInternal(u64* outValue) { + if (mMode == Mode::Save) { + error("Cannot read in save mode"); + return false; + } + u32 lineLength; + if (!ReadLine(&lineLength)) { + return false; + } + + // Convert to hex + u64 value = strtol(mBuffer.Content(), nullptr, 0); + mBuffer.SafeDeleteFront(lineLength + 1); + *outValue = value; + return true; +} + +bool ConfigFile::WriteStringInternal(const char* fieldName, const char* string) { + if (mMode == Mode::Load) { + error("Cannot write in load mode"); + return false; + } + mBuffer.Clear(); + mBuffer.SafeAppendF("%s", string); + mBuffer.SafeAppendF("# %s\n", fieldName); + return mFile.Write(mBuffer); +} + +bool ConfigFile::ReadStringInternal(char* outString, const u32 bufferLength) { + if (mMode == Mode::Save) { + error("Cannot read in save mode"); + return false; + } + u32 lineLength; + if (!ReadLine(&lineLength)) { + return false; + } + strncpy(outString, mBuffer.Content(), bufferLength); + mBuffer.SafeDeleteFront(lineLength + 1); + outString[bufferLength - 1] = '\0'; + + return true; +} + +} // namespace botwsavs::fs diff --git a/src/fs/ConfigFile.hpp b/src/fs/ConfigFile.hpp new file mode 100644 index 0000000..28b0cd8 --- /dev/null +++ b/src/fs/ConfigFile.hpp @@ -0,0 +1,150 @@ +#pragma once +#include "File.hpp" +#include "util/StringBuffer.hpp" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +namespace botwsavs { + +namespace fs { + +class ConfigFile { +public: + ConfigFile(const char* path) : mFile(path) {} + ~ConfigFile() = default; + + bool Exists() { return mFile.Exists(); } + + bool Save(); + bool Load(); + +protected: + virtual void SaveInternal(); + virtual void LoadInternal(); + + void WriteInteger(const char* fieldName, const u64 value) { + if (mSuccess) { + mSuccess = WriteIntegerInternal(fieldName, value); + } + } + + void ReadInteger(u64* outValue) { + if (mSuccess) { + mSuccess = ReadIntegerInternal(outValue); + } + } + + void WriteString(const char* fieldName, const char* string) { + if (mSuccess) { + mSuccess = WriteStringInternal(fieldName, string); + } + } + + void ReadString(char* outString, const u32 maxLength) { + if (mSuccess) { + mSuccess = ReadStringInternal(outString, maxLength); + } + } + + void ReadInteger(u32* outValue) { + u64 iValue; + if (!ReadIntegerInternal(&iValue)) { + mSuccess = false; + return; + } + + u32 truncated = (u32)(iValue); + *outValue = truncated; + } + + void WriteFloat(const char* fieldName, const f32 value) { + WriteInteger(fieldName, reinterpret_cast(value)); + } + + void ReadFloat(f32* value) { + u32 iValue; + ReadInteger(&iValue); + if (!mSuccess) { + return; + } + *value = reinterpret_cast(iValue); + } + + template + void WriteIntegerArray(const char* fieldName, const T* array, u32 size) { + WriteInteger(fieldName, array[0]); + if (!mSuccess) { + return; + } + for (u32 i = 1; i < size; i++) { + WriteInteger(" ---", array[i]); + if (!mSuccess) { + return; + } + } + } + + template + void ReadIntegerArray(T* outArray, u32 size) { + ReadInteger(outArray); + if (!mSuccess) { + return; + } + for (u32 i = 1; i < size; i++) { + ReadInteger(outArray + i); + if (!mSuccess) { + return; + } + } + } + + void WriteFloatArray(const char* fieldName, const f32* array, u32 size) { + WriteFloat(fieldName, array[0]); + if (!mSuccess) { + return; + } + for (u32 i = 1; i < size; i++) { + WriteFloat(" ---", array[i]); + if (!mSuccess) { + return; + } + } + } + + void ReadFloatArray(f32* outArray, u32 size) { + ReadFloat(outArray); + if (!mSuccess) { + return; + } + for (u32 i = 1; i < size; i++) { + ReadFloat(outArray + i); + if (!mSuccess) { + return; + } + } + } + +private: + // In save mode all read functions will be disabled, vise versa + enum class Mode { Save, Load }; + + Mode mMode = Mode::Save; + bool mSuccess = true; + File mFile; + FileBuffer mBuffer; + + bool ReadLine(u32* outLineLength); + + bool WriteIntegerInternal(const char* fieldName, const u64 value); + + bool ReadIntegerInternal(u64* outValue); + + bool WriteStringInternal(const char* fieldName, const char* string); + + bool ReadStringInternal(char* outString, const u32 maxLength); +}; + +} // namespace fs + +} // namespace botwsavs +#pragma GCC diagnostic pop diff --git a/src/fs/SaveFile.cpp b/src/fs/SaveFile.cpp deleted file mode 100644 index 6edd0bf..0000000 --- a/src/fs/SaveFile.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "SaveFile.hpp" -#include -#include "util/StringBuffer.hpp" - -namespace botwsavs::fs { - -bool SaveFile::ReadLine(FileBuffer& buffer, u32* outLineLength) { - u32 lineLength; - if (!buffer.IndexOf('\n', 0, &lineLength)) { - // no new line character found, try reading more - s64 readSize = mFile.Read(buffer); - if (readSize < 0) { - return false; - } - - if (!buffer.IndexOf('\n', 0, &lineLength)) { - // still no new line, probably error - return false; - } - } - - buffer.SafeSet(lineLength, '\0'); - // find a comment character - u32 commentIndex; - if (buffer.IndexOf('#', 0, &commentIndex)) { - // remove comment - buffer.SafeSet(commentIndex, '\0'); - } - - *outLineLength = lineLength; - return true; -} - -bool SaveFile::WriteInteger(FileBuffer& buffer, const char* fieldName, u64 value) { - buffer.Clear(); - buffer.SafeAppendF("0x%016x", value); - buffer.SafeAppendF("# %s\n", fieldName); - return mFile.Write(buffer); -} - -bool SaveFile::ReadInteger(FileBuffer& buffer, u64* outValue) { - u32 lineLength; - if (!ReadLine(buffer, &lineLength)) { - return false; - } - - // Convert to hex - u64 value = strtol(buffer.Content(), nullptr, 0); - buffer.SafeDeleteFront(lineLength + 1); - *outValue = value; - return true; -} - -bool SaveFile::WriteString(FileBuffer& buffer, const char* fieldName, const char* string, - const u32 bufferLength) { - buffer.Clear(); - buffer.SafeAppendF("%s", string); - buffer.SafeAppendF("# %s\n", fieldName); - return mFile.Write(buffer); -} - -bool SaveFile::ReadString(FileBuffer& buffer, char* outString, const u32 bufferLength) { - u32 lineLength; - if (!ReadLine(buffer, &lineLength)) { - return false; - } - strncpy(outString, buffer.Content(), bufferLength); - buffer.SafeDeleteFront(lineLength + 1); - outString[bufferLength - 1] = '\0'; - - return true; -} - -} // namespace botwsavs::fs diff --git a/src/fs/SaveFile.hpp b/src/fs/SaveFile.hpp deleted file mode 100644 index f6fcc2d..0000000 --- a/src/fs/SaveFile.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include "File.hpp" -#define SAVE_FILE_VERSION 2 - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -namespace botwsavs { -namespace core { -class State; -class Worker; -} // namespace core -namespace fs { - -class SaveFile { -public: - SaveFile(const char* path) : mFile(path) {} - ~SaveFile() = default; - - bool Save(const core::State& state); - bool Load(core::State& state); - bool SaveWorker(const core::Worker& worker); - bool LoadWorker(core::Worker& worker); - bool Exists() { return mFile.Exists(); } - -private: - File mFile; - - bool ReadVersion1(FileBuffer& buffer, core::State& state); - bool ReadVersion2(FileBuffer& buffer, core::State& state); - - bool ReadLine(FileBuffer& buffer, u32* outLineLength); - - bool WriteInteger(FileBuffer& buffer, const char* fieldName, const u64 value); - - bool ReadInteger(FileBuffer& buffer, u64* outValue); - - bool WriteString(FileBuffer& buffer, const char* fieldName, const char* string, - const u32 maxLength); - - bool ReadString(FileBuffer& buffer, char* outString, const u32 maxLength); - - bool ReadInteger(FileBuffer& buffer, u32* outValue) { - u64 iValue; - if (!ReadInteger(buffer, &iValue)) { - return false; - } - - u32 truncated = (u32)(iValue); - *outValue = truncated; - return true; - } - - bool WriteFloat(FileBuffer& buffer, const char* fieldName, const f32 value) { - return WriteInteger(buffer, fieldName, reinterpret_cast(value)); - } - - bool ReadFloat(FileBuffer& buffer, f32* value) { - u32 iValue; - if (!ReadInteger(buffer, &iValue)) { - return false; - } - *value = reinterpret_cast(iValue); - return true; - } - - template - bool WriteIntegerArray(FileBuffer& buffer, const char* fieldName, const T* array, u32 size) { - if (!WriteInteger(buffer, fieldName, array[0])) { - return false; - } - for (u32 i = 1; i < size; i++) { - if (!WriteInteger(buffer, " ---", array[i])) { - return false; - } - } - return true; - } - template - bool ReadIntegerArray(FileBuffer& buffer, T* outArray, u32 size) { - if (!ReadInteger(buffer, outArray)) { - return false; - } - for (u32 i = 1; i < size; i++) { - if (!ReadInteger(buffer, outArray + i)) { - return false; - } - } - return true; - } - - bool WriteFloatArray(FileBuffer& buffer, const char* fieldName, const f32* array, u32 size) { - if (!WriteFloat(buffer, fieldName, array[0])) { - return false; - } - for (u32 i = 1; i < size; i++) { - if (!WriteFloat(buffer, " ---", array[i])) { - return false; - } - } - return true; - } - - bool ReadFloatArray(FileBuffer& buffer, f32* outArray, u32 size) { - if (!ReadFloat(buffer, outArray)) { - return false; - } - for (u32 i = 1; i < size; i++) { - if (!ReadFloat(buffer, outArray + i)) { - return false; - } - } - return true; - } -}; - -} // namespace fs - -} // namespace botwsavs -#pragma GCC diagnostic pop diff --git a/src/fs/SaveFileMacros.hpp b/src/fs/SaveFileMacros.hpp deleted file mode 100644 index 0bf8973..0000000 --- a/src/fs/SaveFileMacros.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#define SaveFileWrite(TYPE, MEMBER) result = result && Write##TYPE(buffer, #MEMBER, state.MEMBER); -#define SaveFileWriteArray(TYPE, MEMBER, SIZE) \ - result = result && Write##TYPE##Array(buffer, #MEMBER, state.MEMBER, SIZE); -#define SaveFileWriteString(MEMBER, SIZE) \ - result = result && WriteString(buffer, #MEMBER, state.MEMBER, SIZE) -#define SaveFileWriteNamedValue(TYPE, MEMBER, SIZE) \ - result = result && WriteString(buffer, "Name of value below", state.MEMBER.GetName(), SIZE); \ - result = result && Write##TYPE(buffer, #MEMBER, state.MEMBER.GetValue()) - -#define SaveFileRead(TYPE, MEMBER) result = result && Read##TYPE(buffer, &state.MEMBER); -#define SaveFileReadArray(TYPE, MEMBER, SIZE) \ - result = result && Read##TYPE##Array(buffer, state.MEMBER, SIZE); -#define SaveFileReadString(MEMBER, SIZE) result = result && ReadString(buffer, state.MEMBER, SIZE) -#define SaveFileReadNamedValue(TYPE, MEMBER, SIZE) \ - result = result && ReadString(buffer, state.MEMBER.Name(), SIZE); \ - result = result && Read##TYPE(buffer, state.MEMBER.GetValuePtr()); diff --git a/src/fs/SaveFileState.cpp b/src/fs/SaveFileState.cpp deleted file mode 100644 index 09b06dd..0000000 --- a/src/fs/SaveFileState.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include "core/State.hpp" -#include "util/StringBuffer.hpp" - -#include "Logger.hpp" -#include "SaveFile.hpp" -#include "SaveFileMacros.hpp" - -namespace botwsavs::fs { - -bool SaveFile::Load(core::State& state) { - bool result = mFile.Open(); - if (!result) { - return false; - } - - FileBuffer buffer; - u32 version; - result = ReadInteger(buffer, &version); - if (!result) { - return false; - } - switch (version) { - case 1: - result = ReadVersion1(buffer, state); - break; - case 2: - result = ReadVersion2(buffer, state); - break; - } - - mFile.Close(); - return result; -} - -bool SaveFile::Save(const core::State& state) { - bool result = mFile.Open(); - if (!result) { - return false; - } - - result = mFile.Clear(); - if (!result) { - mFile.Close(); - return false; - } - - FileBuffer buffer; - result = WriteInteger(buffer, "version", SAVE_FILE_VERSION); - SaveFileWrite(Integer, mLevel); - // Level 1 - SaveFileWrite(Integer, mHealth); - SaveFileWrite(Float, mStamina); - SaveFileWriteArray(Float, mHavokPosition, 3); - SaveFileWriteArray(Float, mMainPositionMatrix, 12); - SaveFileWriteArray(Float, mCameraPanMatrix, 12); - SaveFileWrite(Float, mCameraZoom); - SaveFileWrite(Float, mCameraTilt); - // Level 2 - SaveFileWriteNamedValue(Integer, mMenuEquippedArrow, 64); - SaveFileWriteNamedValue(Integer, mMenuEquippedWeapon, 64); - SaveFileWriteNamedValue(Integer, mMenuEquippedBow, 64); - SaveFileWriteNamedValue(Integer, mMenuEquippedShield, 64); - SaveFileWriteNamedValue(Integer, mOverworldEquippedWeapon, 64); - SaveFileWriteNamedValue(Integer, mOverworldEquippedBow, 64); - SaveFileWriteNamedValue(Integer, mOverworldEquippedShield, 64); - // Level 3 - // TODO - mFile.Close(); - return result; -} - -bool SaveFile::ReadVersion1(FileBuffer& buffer, core::State& state) { - bool result = true; - SaveFileRead(Integer, mLevel); - SaveFileRead(Integer, mHealth); - SaveFileRead(Float, mStamina); - SaveFileReadArray(Float, mHavokPosition, 3); - SaveFileReadArray(Float, mMainPositionMatrix, 12); - SaveFileReadArray(Float, mCameraPanMatrix, 12); - SaveFileRead(Float, mCameraZoom); - SaveFileRead(Float, mCameraTilt); - - return result; -} - -bool SaveFile::ReadVersion2(FileBuffer& buffer, core::State& state) { - bool result = ReadVersion1(buffer, state); - SaveFileReadNamedValue(Integer, mMenuEquippedArrow, 64); - SaveFileReadNamedValue(Integer, mMenuEquippedWeapon, 64); - SaveFileReadNamedValue(Integer, mMenuEquippedBow, 64); - SaveFileReadNamedValue(Integer, mMenuEquippedShield, 64); - SaveFileReadNamedValue(Integer, mOverworldEquippedWeapon, 64); - SaveFileReadNamedValue(Integer, mOverworldEquippedBow, 64); - SaveFileReadNamedValue(Integer, mOverworldEquippedShield, 64); - - return result; -} - -} // namespace botwsavs::fs diff --git a/src/fs/SaveFileWorker.cpp b/src/fs/SaveFileWorker.cpp deleted file mode 100644 index e82ec8a..0000000 --- a/src/fs/SaveFileWorker.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "core/Worker.hpp" - -#include "util/StringBuffer.hpp" - -#include "File.hpp" -#include "SaveFile.hpp" - -namespace botwsavs::fs { - -bool SaveFile::SaveWorker(const core::Worker& worker) { - bool result = mFile.Open(); - if (!result) { - return false; - } - - result = mFile.Clear(); - if (!result) { - mFile.Close(); - return false; - } - - FileBuffer buffer; - result = result && WriteInteger(buffer, "level", worker.mLevel); - - mFile.Close(); - return result; -} - -bool SaveFile::LoadWorker(core::Worker& worker) { - bool result = mFile.Open(); - if (!result) { - return false; - } - - FileBuffer buffer; - result = ReadInteger(buffer, &worker.mLevel); - - mFile.Close(); - return result; -} - -} // namespace botwsavs::fs diff --git a/src/fs/StateSaveFile.cpp b/src/fs/StateSaveFile.cpp new file mode 100644 index 0000000..577d07d --- /dev/null +++ b/src/fs/StateSaveFile.cpp @@ -0,0 +1,117 @@ +#include "StateSaveFile.hpp" +#include "core/State.hpp" +#include "fs/Logger.hpp" + +namespace botwsavs::fs { +void StateSaveFile::LoadInternal() { + u32 version = 0; + ReadInteger(&version); + + switch (version) { + case 1: + ReadVersion1(); + break; + case 2: + ReadVersion2(); + break; + case 3: + ReadVersion3(); + break; + default: + errorf("Bad version: %d", version); + } +} + +void StateSaveFile::SaveInternal() { + WriteInteger("version", STATE_SAVE_FILE_VERSION); + StateFileWrite(Integer, mLevel); + // Level 1 + StateFileWrite(Integer, mHealth); + StateFileWrite(Float, mStamina); + StateFileWriteArray(Float, mHavokPosition, 3); + StateFileWriteArray(Float, mMainPositionMatrix, 12); + StateFileWriteArray(Float, mCameraPanMatrix, 12); + StateFileWrite(Float, mCameraZoom); + StateFileWrite(Float, mCameraTilt); + // Level 2 + StateFileWriteNamedValue(Integer, mMenuEquippedArrow); + StateFileWriteNamedValue(Integer, mMenuEquippedWeapon); + StateFileWriteNamedValue(Integer, mMenuEquippedBow); + StateFileWriteNamedValue(Integer, mMenuEquippedShield); + StateFileWriteNamedValue(Integer, mOverworldEquippedWeapon); + StateFileWriteNamedValue(Integer, mOverworldEquippedBow); + StateFileWriteNamedValue(Integer, mOverworldEquippedShield); + // Level 3 + StateFileWrite(Float, mTimeOfDayPaused); + StateFileWrite(Float, mTimeOfDayUnpaused); + StateFileWrite(Float, mBloodMoonTimer); + StateFileWrite(Float, mTemperatureDamageTimer); + StateFileWrite(Float, mFlameTimer); + StateFileWrite(Float, mGaleTimer); + StateFileWrite(Float, mFuryTimer); + StateFileWrite(Float, mProtectionTimer); + StateFileWrite(Float, mGraceTimer); + StateFileWriteArray(Integer, mAbilityUses, 3); + StateFileWrite(Float, mMasterSwordCooldown); + StateFileWrite(Float, mSpeedPotionTimer1); + StateFileWrite(Float, mSpeedPotionTimer2); + StateFileWrite(Float, mSpeedPotionTimer3); + StateFileWrite(Float, mAttackPotionTimer); + StateFileWrite(Float, mDefensePotionTimer); + StateFileWrite(Float, mHeatResistPotionTimer); + StateFileWrite(Float, mColdResistPotionTimer); + StateFileWrite(Float, mFlameResistPotionTimer); + StateFileWrite(Float, mShockResistPotionTimer); + StateFileWrite(Float, mStealthPotionTimer); +} + +void StateSaveFile::ReadVersion1() { + StateFileRead(Integer, mLevel); + StateFileRead(Integer, mHealth); + StateFileRead(Float, mStamina); + StateFileReadArray(Float, mHavokPosition, 3); + StateFileReadArray(Float, mMainPositionMatrix, 12); + StateFileReadArray(Float, mCameraPanMatrix, 12); + StateFileRead(Float, mCameraZoom); + StateFileRead(Float, mCameraTilt); +} + +void StateSaveFile::ReadVersion2() { + ReadVersion1(); + StateFileReadNamedValue(Integer, mMenuEquippedArrow, 64); + StateFileReadNamedValue(Integer, mMenuEquippedWeapon, 64); + StateFileReadNamedValue(Integer, mMenuEquippedBow, 64); + StateFileReadNamedValue(Integer, mMenuEquippedShield, 64); + StateFileReadNamedValue(Integer, mOverworldEquippedWeapon, 64); + StateFileReadNamedValue(Integer, mOverworldEquippedBow, 64); + StateFileReadNamedValue(Integer, mOverworldEquippedShield, 64); +} + +void StateSaveFile::ReadVersion3() { + ReadVersion2(); + StateFileRead(Float, mTimeOfDayPaused); + StateFileRead(Float, mTimeOfDayUnpaused); + StateFileRead(Float, mBloodMoonTimer); + StateFileRead(Float, mTimeOfDayPaused); + StateFileRead(Float, mTimeOfDayUnpaused); + StateFileRead(Float, mBloodMoonTimer); + StateFileRead(Float, mTemperatureDamageTimer); + StateFileRead(Float, mFlameTimer); + StateFileRead(Float, mGaleTimer); + StateFileRead(Float, mFuryTimer); + StateFileRead(Float, mProtectionTimer); + StateFileRead(Float, mGraceTimer); + StateFileReadArray(Integer, mAbilityUses, 3); + StateFileRead(Float, mMasterSwordCooldown); + StateFileRead(Float, mSpeedPotionTimer1); + StateFileRead(Float, mSpeedPotionTimer2); + StateFileRead(Float, mSpeedPotionTimer3); + StateFileRead(Float, mAttackPotionTimer); + StateFileRead(Float, mDefensePotionTimer); + StateFileRead(Float, mHeatResistPotionTimer); + StateFileRead(Float, mColdResistPotionTimer); + StateFileRead(Float, mFlameResistPotionTimer); + StateFileRead(Float, mShockResistPotionTimer); + StateFileRead(Float, mStealthPotionTimer); +} +} // namespace botwsavs::fs diff --git a/src/fs/StateSaveFile.hpp b/src/fs/StateSaveFile.hpp new file mode 100644 index 0000000..63f9ead --- /dev/null +++ b/src/fs/StateSaveFile.hpp @@ -0,0 +1,45 @@ +#pragma once +#include "ConfigFile.hpp" + +namespace botwsavs { + +namespace core { +class State; +} + +namespace fs { + +class StateSaveFile : public ConfigFile { +public: + StateSaveFile(const char* path, core::State& state) : ConfigFile(path), mState(state){}; + ~StateSaveFile() = default; + +protected: + void SaveInternal() override; + void LoadInternal() override; + +private: + void ReadVersion1(); + void ReadVersion2(); + void ReadVersion3(); + + core::State& mState; +}; + +} // namespace fs +} // namespace botwsavs + +#define STATE_SAVE_FILE_VERSION 3 +#define StateFileWrite(TYPE, MEMBER) Write##TYPE(#MEMBER, mState.MEMBER) +#define StateFileWriteArray(TYPE, MEMBER, SIZE) Write##TYPE##Array(#MEMBER, mState.MEMBER, SIZE) +#define StateFileWriteString(MEMBER, SIZE) WriteString(#MEMBER, mState.MEMBER, SIZE) +#define StateFileWriteNamedValue(TYPE, MEMBER) \ + WriteString("Name of value below", mState.MEMBER.GetName()); \ + Write##TYPE(#MEMBER, mState.MEMBER.GetValue()) + +#define StateFileRead(TYPE, MEMBER) Read##TYPE(&mState.MEMBER) +#define StateFileReadArray(TYPE, MEMBER, SIZE) Read##TYPE##Array(mState.MEMBER, SIZE); +#define StateFileReadString(MEMBER, SIZE) ReadString(mState.MEMBER, SIZE) +#define StateFileReadNamedValue(TYPE, MEMBER, SIZE) \ + ReadString(mState.MEMBER.Name(), SIZE); \ + Read##TYPE(mState.MEMBER.GetValuePtr()); diff --git a/src/fs/WorkerSaveFile.cpp b/src/fs/WorkerSaveFile.cpp new file mode 100644 index 0000000..49b978e --- /dev/null +++ b/src/fs/WorkerSaveFile.cpp @@ -0,0 +1,13 @@ +#include "WorkerSaveFile.hpp" +#include "core/Worker.hpp" + +namespace botwsavs::fs { +void WorkerSaveFile::LoadInternal() { + ReadInteger(&mWorker.mLevel); +} + +void WorkerSaveFile::SaveInternal() { + WriteInteger("level", mWorker.mLevel); +} + +} // namespace botwsavs::fs diff --git a/src/fs/WorkerSaveFile.hpp b/src/fs/WorkerSaveFile.hpp new file mode 100644 index 0000000..789ba35 --- /dev/null +++ b/src/fs/WorkerSaveFile.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "ConfigFile.hpp" + +namespace botwsavs { + +namespace core { +class Worker; +} + +namespace fs { + +class WorkerSaveFile : public ConfigFile { +public: + WorkerSaveFile(core::Worker& worker) : ConfigFile("sd:/botwsavs/worker.txt"), mWorker(worker){}; + ~WorkerSaveFile() = default; + +protected: + void SaveInternal() override; + void LoadInternal() override; + + core::Worker& mWorker; +}; + +} // namespace fs +} // namespace botwsavs diff --git a/src/mem/GamePtr.h b/src/mem/GamePtr.h index c3a8384..a9227d3 100644 --- a/src/mem/GamePtr.h +++ b/src/mem/GamePtr.h @@ -55,7 +55,7 @@ GamePtrDefine(ksys::act::BaseProc, OverworldShieldActor, main[0x2CC4768][0xc0][- // Time GamePtrDefine(f32, TimeOfDayPaused, main[0x2CC5FE0][0xBE0][0x30][0xF8] + 0x18) // [[[[main+2CC5FE0]+BE0]+30]+F8]+18 GamePtrDefine(f32, TimeOfDayUnpaused, main[0x2CD18C8][0x17630] + 0x468) // [[main+2CD18C8]+17630]+468 -GamePtrDefine(f32, BloodMoonTimer, main[0x2CD18D0][0x39][0x398] + 0x2D0) // [[[main+2CD18D0]+30]+398]+2D0 +GamePtrDefine(f32, BloodMoonTimer, main[0x2CD18D0][0x30][0x398] + 0x2D0) // [[[main+2CD18D0]+30]+398]+2D0 // Climate Damage GamePtrDefine(f32, TemperatureDamageTimer, main[0x2CA1140][0x60] + 0x268C) // [[main+2CA1140]+60]+268C @@ -80,7 +80,7 @@ GamePtrDefine(f32, DefensePotionTimer, main[0x2CC4768][0xC0] + 0x12E8) // [[main GamePtrDefine(f32, HeatResistPotionTimer, main[0x2CC4768][0xC0] + 0x12F4) // [[main+2CC4768]+C0]+12F4 GamePtrDefine(f32, ColdResistPotionTimer, main[0x2CC4768][0xC0] + 0x1300) // [[main+2CC4768]+C0]+1300 GamePtrDefine(f32, FlameResistPotionTimer, main[0x2CC4768][0xC0] + 0x130C) // [[main+2CC4768]+C0]+130C -GamePtrDefine(f32, ShockResistPotinoTimer, main[0x2CC4768][0xC0] + 0x1324) // [[main+2CC4768]+C0]+1324 -GamePtrDefine(f32, StealthPotinoTimer, main[0x2CC4768][0xC0] + 0x133C) // [[main+2CC4768]+C0]+133C +GamePtrDefine(f32, ShockResistPotionTimer, main[0x2CC4768][0xC0] + 0x1324) // [[main+2CC4768]+C0]+1324 +GamePtrDefine(f32, StealthPotionTimer, main[0x2CC4768][0xC0] + 0x133C) // [[main+2CC4768]+C0]+133C } // namespace botwsavs::core::ptr diff --git a/src/mem/MemPtr.hpp b/src/mem/MemPtr.hpp index 86cd317..342aa5a 100644 --- a/src/mem/MemPtr.hpp +++ b/src/mem/MemPtr.hpp @@ -24,8 +24,9 @@ class MemPtr { MemPtr& operator+(s64 offset) { return Add(offset); } // Deferring and storing the value as pointer MemPtr& Deref() { - if (!mPtr) { + if (!PtrLooksSafe(mPtr)) { mError = true; + error("MemPtr deref failed"); return *this; } char** pp = reinterpret_cast(mPtr); diff --git a/src/mem/SafePtr.hpp b/src/mem/SafePtr.hpp index 6b282da..553c00b 100644 --- a/src/mem/SafePtr.hpp +++ b/src/mem/SafePtr.hpp @@ -5,6 +5,21 @@ namespace botwsavs::mem { +static bool PtrLooksSafe(void* p) { + u64 raw = reinterpret_cast(p); + + if (raw > 0xFFFFFFFFFF || (raw >> 32 == 0)) { + warnf("Pointer %p does not look safe", p); + return false; + } + if ((raw & 3) != 0) { + warnf("Pointer %p does not look safe", p); + return false; + } + + return true; +} + template class SafePtr { public: @@ -72,20 +87,7 @@ class SafePtr { } private: - bool LooksSafe() const { - u64 raw = reinterpret_cast(mPtr); - - if (raw > 0xFFFFFFFFFF || (raw >> 32 == 0)) { - warnf("Pointer %p does not look safe", mPtr); - return false; - } - if ((raw & 3) != 0) { - warnf("Pointer %p does not look safe", mPtr); - return false; - } - - return true; - } + bool LooksSafe() const { return PtrLooksSafe(mPtr); } private: T* mPtr; diff --git a/src/types.h b/src/types.h index 88af936..33f87d4 100644 --- a/src/types.h +++ b/src/types.h @@ -36,23 +36,16 @@ typedef u32 uint; typedef char16_t wchar; // log functions -// only debug builds contain log because FTP has problem when the log file is not closed #ifdef DEBUG #define debug(mes) botwsavs::fs::Logger::Instance().Debug(mes) #define debugf(mes, value) botwsavs::fs::Logger::Instance().DebugF(mes, value) +#else +#define debug(mes) +#define debugf(mes) +#endif #define info(mes) botwsavs::fs::Logger::Instance().Info(mes) #define warn(mes) botwsavs::fs::Logger::Instance().Warn(mes) #define error(mes) botwsavs::fs::Logger::Instance().Error(mes) #define infof(mes, value) botwsavs::fs::Logger::Instance().InfoF(mes, value) #define warnf(mes, value) botwsavs::fs::Logger::Instance().WarnF(mes, value) #define errorf(mes, value) botwsavs::fs::Logger::Instance().ErrorF(mes, value) -#else -#define debug(mes) -#define debugf(mes) -#define info(mes) -#define warn(mes) -#define error(mes) -#define infof(mes, value) -#define warnf(mes, value) -#define errorf(mes, value) -#endif diff --git a/src/ui/OverlayString.hpp b/src/ui/OverlayString.hpp index 4a4ddbb..cc50cc3 100644 --- a/src/ui/OverlayString.hpp +++ b/src/ui/OverlayString.hpp @@ -48,8 +48,8 @@ inline void ShowError() { ShowOverridenMessage("Save state error!"); } -inline void ShowLevelError() { - ShowOverridenMessage("You need to lower the setting level to restore!"); +inline void ShowLevelError(u32 stateLevel) { + ShowFormattedMessage("You need to lower the setting level to %d to restore!", stateLevel); } } // namespace botwsavs::ui diff --git a/standalone_ftp.py b/standalone_ftp.py index ddb7162..e0503ab 100644 --- a/standalone_ftp.py +++ b/standalone_ftp.py @@ -96,6 +96,6 @@ def add_slash_if_need(path): sys.exit(-1) ip = YOUR_IP for i, arg in enumerate(sys.argv): - if arg[i] == "-ip": - ip = arg[i+1] + if sys.argv[i] == "-ip": + ip = sys.argv[i+1] run(ip, sys.argv[1], sys.argv[2])