Skip to content

Commit

Permalink
Level 3 (#7)
Browse files Browse the repository at this point in the history
* level 3

* change key binding

* clang format
  • Loading branch information
Pistonight authored Aug 26, 2022
1 parent 5c3bb70 commit c2041f5
Show file tree
Hide file tree
Showing 25 changed files with 620 additions and 418 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
9 changes: 4 additions & 5 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions src/core/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
5 changes: 0 additions & 5 deletions src/core/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,4 @@ bool State::WriteToGame(u32 level) {
return mError == Error::None;
}

void State::ReadLevel3() { /*TODO*/
}
void State::WriteLevel3() { /*TODO*/
}

} // namespace botwsavs::core
24 changes: 24 additions & 0 deletions src/core/State.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
56 changes: 56 additions & 0 deletions src/core/StateLevel3.cpp
Original file line number Diff line number Diff line change
@@ -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
29 changes: 18 additions & 11 deletions src/core/Worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

#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 {

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");
Expand Down Expand Up @@ -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();
Expand All @@ -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;
}

Expand All @@ -167,22 +174,22 @@ 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");
return;
}
if (tempState.mLevel < mLevel) {
error("Restore failed because state level is less than setting level");
ui::ShowLevelError();
ui::ShowLevelError(tempState.mLevel);
return;
}

Expand Down Expand Up @@ -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");
Expand Down
120 changes: 120 additions & 0 deletions src/fs/ConfigFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "ConfigFile.hpp"
#include <stdlib.h>
#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
Loading

0 comments on commit c2041f5

Please sign in to comment.