Skip to content

Commit c2041f5

Browse files
authored
Level 3 (#7)
* level 3 * change key binding * clang format
1 parent 5c3bb70 commit c2041f5

25 files changed

+620
-418
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# CHANGELOG
22

3-
### `1.6.0-SS0.2` `2022-08-21` `LATEST`
3+
### `1.6.0-SS1.0` `2022-08-26` `LATEST`
4+
- Level 3 save and restore
5+
- 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
6+
- Enable logging (except for debug logging) in ship builds
7+
8+
### `1.6.0-SS0.2` `2022-08-21`
49
- Save and restore equipped weapon/bow/shield durability and equipped arrow count
510
- Durability/count will only restore if you have the same item equipped as when you saved
611
- Otherwise you will see a warning message, but the ones that do match will restore fine.

Justfile

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,14 @@ release EXTRA_DEFINES="": clean (_make EXTRA_DEFINES)
4747
mkdir -p release/atmosphere/contents/01007EF00011E000/romfs/System
4848
mkdir -p release/atmosphere/exefs_patches/botwsavs
4949
mkdir -p release/botwsavs
50-
cp build/botwsavs.nso release/atmosphere/contents/01007EF00011E000/exefs/subsdk9
51-
cp build/app.npdm release/atmosphere/contents/01007EF00011E000/exefs/main.npdm
52-
cp build/8E9978D50BDD20B4C8395A106C27FFDE.ips release/atmosphere/exefs_patches/botwsavs/8E9978D50BDD20B4C8395A106C27FFDE.ips
50+
cp build{{EXTRA_DEFINES}}/botwsavs.nso release/atmosphere/contents/01007EF00011E000/exefs/subsdk9
51+
cp build{{EXTRA_DEFINES}}/app.npdm release/atmosphere/contents/01007EF00011E000/exefs/main.npdm
52+
cp build{{EXTRA_DEFINES}}/8E9978D50BDD20B4C8395A106C27FFDE.ips release/atmosphere/exefs_patches/botwsavs/8E9978D50BDD20B4C8395A106C27FFDE.ips
5353
cp README.md release
5454
cp CHANGELOG.md release
55-
cp standalone_ftp.py release/ftp.py
5655
echo "" > release/botwsavs/latest.txt
5756
echo -n {{VERSION_TEXT}} > release/atmosphere/contents/01007EF00011E000/romfs/System/Version.txt
58-
cd release && zip -r ../save-state-{{VERSION_TEXT}}.zip *
57+
cd release && zip -r ../save-state-{{VERSION_TEXT}}{{EXTRA_DEFINES}}.zip *
5958

6059
# Build gold rush configuration and package
6160
release-gold-rush: (release "-DGOLD_RUSH")

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ Save state: Active Mode
3030
### Active Mode Combos
3131
You can use these key combos in the Active Mode.
3232

33-
- `PLUS + Dpad Left`: Save state to memory
34-
- `PLUS + R3 + Dpad Left`: Save state to `/botwsavs/latest.txt` on sd card
35-
- `PLUS + Dpad Right`: Restore state from memory
36-
- `PLUS + R3 + Dpad Right`: Restore state from `/botwsavs/restore.txt` on sd card
33+
- `ZL + L + PLUS + Dpad Left`: Save state to memory
34+
- `ZL + L + PLUS + R3 + Dpad Left`: Save state to `/botwsavs/latest.txt` on sd card
35+
- `ZL + L + PLUS + Dpad Right`: Restore state from memory
36+
- `ZL + L + PLUS + R3 + Dpad Right`: Restore state from `/botwsavs/restore.txt` on sd card
3737

38-
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
38+
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.
39+
40+
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.
3941

4042
If you try to restore without saving, or without the right file on sd card. You will see a message like this
4143
```
@@ -164,7 +166,7 @@ Release workflow:
164166
- `just release` to build for release configuration and produce the release zip
165167
- `just release-gold-rush` to build for gold rush configuration
166168
- `just publish` to tag the current commit with latest version
167-
- `just draft-release` to generate release notes
169+
- Upload the built release zip to GitHub manually
168170
- `just clean`
169171

170172
### Linking BOTW

src/core/Controller.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,21 @@ bool Controller::TryGetController() {
3636
}
3737

3838
bool Controller::ShouldSaveState() {
39-
return mpController->isHoldAll(Key::Plus | Key::DpadLeft) && !mpController->isHold(Key::RStick);
39+
return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadLeft) &&
40+
!mpController->isHold(Key::RStick);
4041
}
4142

4243
bool Controller::ShouldRestoreState() {
43-
return mpController->isHoldAll(Key::Plus | Key::DpadRight) &&
44+
return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadRight) &&
4445
!mpController->isHold(Key::RStick);
4546
}
4647

4748
bool Controller::ShouldSaveStateToFile() {
48-
return mpController->isHoldAll(Key::Plus | Key::DpadLeft | Key::RStick);
49+
return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadLeft | Key::RStick);
4950
}
5051

5152
bool Controller::ShouldRestoreStateFromFile() {
52-
return mpController->isHoldAll(Key::Plus | Key::DpadRight | Key::RStick);
53+
return mpController->isHoldAll(Key::ZL | Key::L | Key::Plus | Key::DpadRight | Key::RStick);
5354
}
5455

5556
bool Controller::ShouldSwitchMode() {

src/core/State.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,4 @@ bool State::WriteToGame(u32 level) {
3636
return mError == Error::None;
3737
}
3838

39-
void State::ReadLevel3() { /*TODO*/
40-
}
41-
void State::WriteLevel3() { /*TODO*/
42-
}
43-
4439
} // namespace botwsavs::core

src/core/State.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,30 @@ class State {
5757
void WriteLevel2();
5858

5959
// Level 3: All
60+
public:
61+
f32 mTimeOfDayPaused;
62+
f32 mTimeOfDayUnpaused;
63+
f32 mBloodMoonTimer;
64+
f32 mTemperatureDamageTimer;
65+
f32 mFlameTimer;
66+
f32 mGaleTimer;
67+
f32 mFuryTimer;
68+
f32 mProtectionTimer;
69+
f32 mGraceTimer;
70+
u32 mAbilityUses[3];
71+
f32 mMasterSwordCooldown;
72+
73+
f32 mSpeedPotionTimer1;
74+
f32 mSpeedPotionTimer2;
75+
f32 mSpeedPotionTimer3;
76+
f32 mAttackPotionTimer;
77+
f32 mDefensePotionTimer;
78+
f32 mHeatResistPotionTimer;
79+
f32 mColdResistPotionTimer;
80+
f32 mFlameResistPotionTimer;
81+
f32 mShockResistPotionTimer;
82+
f32 mStealthPotionTimer;
83+
6084
private:
6185
void ReadLevel3();
6286
void WriteLevel3();

src/core/StateLevel3.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "State.hpp"
2+
#include "StateMacros.hpp"
3+
#include "fs/Logger.hpp"
4+
#include "mem/GamePtr.h"
5+
6+
namespace botwsavs::core {
7+
8+
void State::ReadLevel3() {
9+
GameReadEz(TimeOfDayPaused);
10+
GameReadEz(TimeOfDayUnpaused);
11+
GameReadEz(BloodMoonTimer);
12+
GameReadEz(TemperatureDamageTimer);
13+
GameReadEz(FlameTimer);
14+
GameReadEz(GaleTimer);
15+
GameReadEz(FuryTimer);
16+
GameReadEz(ProtectionTimer);
17+
GameReadEz(GraceTimer);
18+
GameReadArrayEz(AbilityUses, 3);
19+
GameReadEz(MasterSwordCooldown);
20+
GameReadEz(SpeedPotionTimer1);
21+
GameReadEz(SpeedPotionTimer2);
22+
GameReadEz(SpeedPotionTimer3);
23+
GameReadEz(AttackPotionTimer);
24+
GameReadEz(DefensePotionTimer);
25+
GameReadEz(HeatResistPotionTimer);
26+
GameReadEz(ColdResistPotionTimer);
27+
GameReadEz(FlameResistPotionTimer);
28+
GameReadEz(ShockResistPotionTimer);
29+
GameReadEz(StealthPotionTimer);
30+
}
31+
32+
void State::WriteLevel3() {
33+
GameWriteEz(TimeOfDayPaused);
34+
GameWriteEz(TimeOfDayUnpaused);
35+
GameWriteEz(BloodMoonTimer);
36+
GameWriteEz(TemperatureDamageTimer);
37+
GameWriteEz(FlameTimer);
38+
GameWriteEz(GaleTimer);
39+
GameWriteEz(FuryTimer);
40+
GameWriteEz(ProtectionTimer);
41+
GameWriteEz(GraceTimer);
42+
GameWriteArrayEz(AbilityUses, 3);
43+
GameWriteEz(MasterSwordCooldown);
44+
GameWriteEz(SpeedPotionTimer1);
45+
GameWriteEz(SpeedPotionTimer2);
46+
GameWriteEz(SpeedPotionTimer3);
47+
GameWriteEz(AttackPotionTimer);
48+
GameWriteEz(DefensePotionTimer);
49+
GameWriteEz(HeatResistPotionTimer);
50+
GameWriteEz(ColdResistPotionTimer);
51+
GameWriteEz(FlameResistPotionTimer);
52+
GameWriteEz(ShockResistPotionTimer);
53+
GameWriteEz(StealthPotionTimer);
54+
}
55+
56+
} // namespace botwsavs::core

src/core/Worker.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
#include "Worker.hpp"
44
#include "fs/Logger.hpp"
5-
#include "fs/SaveFile.hpp"
5+
#include "fs/StateSaveFile.hpp"
6+
#include "fs/WorkerSaveFile.hpp"
67
#include "ui/OverlayString.hpp"
78

89
namespace botwsavs::core {
910

1011
bool Worker::Init() {
1112
info("Init worker config from file");
1213

13-
fs::SaveFile workerTxt("sd:/botwsavs/worker.txt");
14-
bool result = workerTxt.LoadWorker(*this);
14+
fs::WorkerSaveFile workerTxt(*this);
15+
bool result = workerTxt.Load();
1516

1617
if (!result) {
1718
warn("File operation failed. Cannot init worker");
@@ -121,8 +122,8 @@ void Worker::ExecuteSaveToFile() {
121122
warnf("State read gives error 0x%x, but continuing to write file anyway",
122123
tempState.GetError());
123124
}
124-
fs::SaveFile latestTxt("sd:/botwsavs/latest.txt");
125-
bool result = latestTxt.Save(tempState);
125+
fs::StateSaveFile latestTxt("sd:/botwsavs/latest.txt", tempState);
126+
bool result = latestTxt.Save();
126127

127128
if (!result) {
128129
ui::ShowError();
@@ -141,9 +142,15 @@ void Worker::ExecuteRestore() {
141142
return;
142143
}
143144

145+
if (mState.mLevel == 0) {
146+
error("Restore failed because state level is 0 (nothing stored)");
147+
ui::ShowCantDoThatRightNow();
148+
return;
149+
}
150+
144151
if (mState.mLevel < mLevel) {
145152
error("Restore failed because state level is less than setting level");
146-
ui::ShowLevelError();
153+
ui::ShowLevelError(mState.mLevel);
147154
return;
148155
}
149156

@@ -167,22 +174,22 @@ void Worker::ExecuteRestoreFromFile() {
167174
}
168175

169176
State tempState;
170-
fs::SaveFile restoreTxt("sd:/botwsavs/restore.txt");
177+
fs::StateSaveFile restoreTxt("sd:/botwsavs/restore.txt", tempState);
171178
if (!restoreTxt.Exists()) {
172179
error("Restore failed because restore.txt does not exist");
173180
ui::ShowCantDoThatRightNow();
174181
return;
175182
}
176183

177-
bool result = restoreTxt.Load(tempState);
184+
bool result = restoreTxt.Load();
178185
if (!result) {
179186
ui::ShowError();
180187
error("File operation failed");
181188
return;
182189
}
183190
if (tempState.mLevel < mLevel) {
184191
error("Restore failed because state level is less than setting level");
185-
ui::ShowLevelError();
192+
ui::ShowLevelError(tempState.mLevel);
186193
return;
187194
}
188195

@@ -218,8 +225,8 @@ void Worker::DisplayStateError(State& state) {
218225
void Worker::SaveWorker() {
219226
info("Saving worker config to file");
220227

221-
fs::SaveFile workerTxt("sd:/botwsavs/worker.txt");
222-
bool result = workerTxt.SaveWorker(*this);
228+
fs::WorkerSaveFile workerTxt(*this);
229+
bool result = workerTxt.Save();
223230

224231
if (!result) {
225232
warn("File operation failed. Cannot save worker");

src/fs/ConfigFile.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "ConfigFile.hpp"
2+
#include <stdlib.h>
3+
#include "Logger.hpp"
4+
//#include "util/StringBuffer.hpp"
5+
6+
namespace botwsavs::fs {
7+
8+
bool ConfigFile::Save() {
9+
if (!mFile.Open()) {
10+
return false;
11+
}
12+
13+
if (!mFile.Clear()) {
14+
mFile.Close();
15+
return false;
16+
}
17+
mSuccess = true;
18+
mMode = Mode::Save;
19+
SaveInternal();
20+
mFile.Close();
21+
return mSuccess;
22+
}
23+
24+
bool ConfigFile::Load() {
25+
if (!mFile.Open()) {
26+
return false;
27+
}
28+
mSuccess = true;
29+
mMode = Mode::Load;
30+
mBuffer.Clear();
31+
LoadInternal();
32+
mFile.Close();
33+
return mSuccess;
34+
}
35+
36+
bool ConfigFile::ReadLine(u32* outLineLength) {
37+
u32 lineLength;
38+
if (!mBuffer.IndexOf('\n', 0, &lineLength)) {
39+
// no new line character found, try reading more
40+
s64 readSize = mFile.Read(mBuffer);
41+
if (readSize < 0) {
42+
error("Reached end of file");
43+
return false;
44+
}
45+
46+
if (!mBuffer.IndexOf('\n', 0, &lineLength)) {
47+
// still no new line, probably error
48+
error("Line is too long and cannot fit in buffer");
49+
return false;
50+
}
51+
}
52+
53+
mBuffer.SafeSet(lineLength, '\0');
54+
// find a comment character
55+
u32 commentIndex;
56+
if (mBuffer.IndexOf('#', 0, &commentIndex)) {
57+
// remove comment
58+
mBuffer.SafeSet(commentIndex, '\0');
59+
}
60+
61+
*outLineLength = lineLength;
62+
return true;
63+
}
64+
65+
bool ConfigFile::WriteIntegerInternal(const char* fieldName, u64 value) {
66+
if (mMode == Mode::Load) {
67+
error("Cannot write in load mode");
68+
return false;
69+
}
70+
mBuffer.Clear();
71+
mBuffer.SafeAppendF("0x%016x", value);
72+
mBuffer.SafeAppendF("# %s\n", fieldName);
73+
return mFile.Write(mBuffer);
74+
}
75+
76+
bool ConfigFile::ReadIntegerInternal(u64* outValue) {
77+
if (mMode == Mode::Save) {
78+
error("Cannot read in save mode");
79+
return false;
80+
}
81+
u32 lineLength;
82+
if (!ReadLine(&lineLength)) {
83+
return false;
84+
}
85+
86+
// Convert to hex
87+
u64 value = strtol(mBuffer.Content(), nullptr, 0);
88+
mBuffer.SafeDeleteFront(lineLength + 1);
89+
*outValue = value;
90+
return true;
91+
}
92+
93+
bool ConfigFile::WriteStringInternal(const char* fieldName, const char* string) {
94+
if (mMode == Mode::Load) {
95+
error("Cannot write in load mode");
96+
return false;
97+
}
98+
mBuffer.Clear();
99+
mBuffer.SafeAppendF("%s", string);
100+
mBuffer.SafeAppendF("# %s\n", fieldName);
101+
return mFile.Write(mBuffer);
102+
}
103+
104+
bool ConfigFile::ReadStringInternal(char* outString, const u32 bufferLength) {
105+
if (mMode == Mode::Save) {
106+
error("Cannot read in save mode");
107+
return false;
108+
}
109+
u32 lineLength;
110+
if (!ReadLine(&lineLength)) {
111+
return false;
112+
}
113+
strncpy(outString, mBuffer.Content(), bufferLength);
114+
mBuffer.SafeDeleteFront(lineLength + 1);
115+
outString[bufferLength - 1] = '\0';
116+
117+
return true;
118+
}
119+
120+
} // namespace botwsavs::fs

0 commit comments

Comments
 (0)