Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
storage.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Twilight
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15*/
16
22
23#include "./storage.h"
24#include "../qmlownership.h"
28#include "../savefile.h"
29#include "../savefiletoolset.h"
30#include "../savefileiterator.h"
31
33{
34 items = new ItemStorageBox(false, 50); // Max 50 items, Mark this is storage
35
36 for(var8 i = 0; i < maxPokemonStorageSets; i++)
38
39 load(saveFile);
40}
41
43{
44 items->deleteLater();
45
46 for(var8 i = 0; i < maxPokemonStorageSets; i++)
47 pokemon[i]->deleteLater();
48}
49
51{
52 return maxPokemonBoxes;
53}
54
56{
57 bool curSetB = ind >= setMaxBoxes;
58 var8 cur = ind; // Get the current box
59 if(curSetB)
60 cur -= setMaxBoxes; // Offset if it's in set b
61
62 return qmlCppOwned(pokemon[curSetB ? 1 : 0]->boxes[cur]);
63}
64
66{
67 if(!boxesFormatted) {
68 if(boxAt(curBox)->pokemonCount() < boxAt(curBox)->pokemonMax())
69 return boxAt(curBox);
70 else
71 return nullptr;
72 }
73
74 for(int i = 0; i < boxCount(); i++) {
75 if(boxAt(i)->pokemonCount() < boxAt(i)->pokemonMax())
76 return boxAt(curBox);;
77 }
78
79 return nullptr;
80}
81
83{
84 auto storage = freeSpace();
85 if(storage == nullptr)
86 return false;
87
88 storage->pokemon.append(pokemon);
89 storage->pokemonChanged();
90 return true;
91}
92
93void Storage::load(SaveFile* saveFile)
94{
95 reset();
96
97 if(saveFile == nullptr)
98 return;
99
100 auto toolset = saveFile->toolset;
101
102 // Now Pokemon operates off a cached version of a box, so when switching boxes
103 // it saves the box from the cache to storage and then loads the new box into
104 // cache. All changes work off of the cache and only get saved back to storage
105 // when switching boxes. It's imperative we replicate this.
106
107 // Current Box, last bit represents whether the other boxes are formatted or
108 // not. If they aren't formatted then don't load them as they contain garbage
109 // information
110 curBox = (toolset->getByte(0x284C) & 0b01111111);
111
112 // Graceful degradation on a corrupt/garbage save: the current-box index must be a
113 // real box (0 .. 2*setMaxBoxes-1 == 0..11). A malformed save (e.g. all-0xFF -> 127)
114 // would otherwise flow into loadSpecific()/boxAt() and index boxes[] out of bounds
115 // -> crash. Clamp an out-of-range value to 0. A valid save is always in range, so
116 // this is a no-op for real saves and byte-fidelity is unaffected.
117 if(curBox >= setMaxBoxes * 2)
118 curBox = 0;
119
121
122 boxesFormatted = toolset->getBit(0x284C, 0x1, 7);
124
125 // Load Items and Boxes 1-12 in 2 sets of 1-6
126 items->load(saveFile, 0x27E6);
127
128 // Determine how to play out current box loading
129 // We only want to load all but current box
130 bool curSetB = curBox >= setMaxBoxes; // Which set is the current box in
131 var8 cur = curBox; // Get the current box
132 if(curSetB)
133 cur -= setMaxBoxes; // Offset if it's in set b
134
135 // Load all of these and skip current box in correct set only if the boxes
136 // have been formatted
137 if(boxesFormatted) {
138 pokemon[0]->load(saveFile, 0x4000, curSetB ? -1 : cur);
139 pokemon[1]->load(saveFile, 0x6000, curSetB ? cur : -1);
140 }
141
142 // Load cached box to current box data regardless of boxes being formatted
143 // or not
144 pokemon[curSetB ? 1 : 0]->loadSpecific(saveFile, 0x30C0, cur);
145}
146
147void Storage::save(SaveFile* saveFile)
148{
149 auto toolset = saveFile->toolset;
150
151 // Save current box details
152 toolset->setByte(0x284C, curBox);
153 toolset->setBit(0x284C, 1, 7, boxesFormatted);
154
155 items->save(saveFile, 0x27E6);
156
157 bool curSetB = curBox >= setMaxBoxes; // Which set is the current box in
158 var8 cur = curBox; // Get the current box
159 if(curSetB)
160 cur -= setMaxBoxes; // Offset if it's in set b
161
162 // Load all of these and skip current box in correct set only if the boxes
163 // have been formatted
164 if(boxesFormatted) {
165 pokemon[0]->save(saveFile, 0x4000, curSetB ? -1 : cur);
166 pokemon[1]->save(saveFile, 0x6000, curSetB ? cur : -1);
167 }
168
169 // Load cached box to current box data regardless of boxes being formatted
170 // or not
171 pokemon[curSetB ? 1 : 0]->saveSpecific(saveFile, 0x30C0, cur);
172}
173
175{
176 curBox = 0;
178
179 boxesFormatted = false;
181
182 items->reset();
183
184 for(var8 i = 0; i < maxPokemonStorageSets; i++)
185 pokemon[i]->reset();
186}
187
189{
190 reset();
192 randomizePokemon(basics);
193}
194
196{
197 for(var8 i = 0; i < maxPokemonStorageSets; i++)
198 pokemon[i]->randomize(basics);
199}
200
202{
203 items->randomize();
204}
A container of Items – either the trainer's bag or a PC item box.
The trainer's headline values: name, ID, money, coins, badges, starter.
A single Pokemon record – the most property-rich object in the tree.
Definition pokemonbox.h:213
Holds contents of a single Pokemon storage box.
Holds contents of a single box set, basically a row or array of boxes each holding Pokemon.
void setByte(var16 addr, var8 val)
Simply sets a byte.
One loaded save: the raw 32 KB bytes, their expanded object tree, and the tools that move between the...
Definition savefile.h:46
SaveFileToolset * toolset
Tools to operate directly on the raw sav file data.
Definition savefile.h:117
int boxCount()
Total boxes across both sets (12).
Definition storage.cpp:50
Storage(SaveFile *saveFile=nullptr)
< The PC item box.
Definition storage.cpp:32
ItemStorageBox * items
Definition storage.h:89
int curBox
Definition storage.h:90
void randomizeItems()
Randomize just the PC items.
Definition storage.cpp:201
PokemonStorageSet * pokemon[maxPokemonStorageSets]
The two box sets (6 boxes each).
Definition storage.h:96
void reset()
Blank the whole PC.
Definition storage.cpp:174
virtual ~Storage()
Definition storage.cpp:42
void randomize(PlayerBasics *basics)
Randomize items and Pokemon.
Definition storage.cpp:188
void curBoxChanged()
PokemonStorageBox * freeSpace()
First box with room, or null if all full.
Definition storage.cpp:65
void save(SaveFile *saveFile)
Flatten the PC back to the save.
Definition storage.cpp:147
void load(SaveFile *saveFile=nullptr)
Expand the PC (items + both box sets) from the save.
Definition storage.cpp:93
void randomizePokemon(PlayerBasics *basics)
Randomize just the boxed Pokemon.
Definition storage.cpp:195
void boxesFormattedChanged()
PokemonStorageBox * boxAt(int ind)
Box ind in the flattened 0..11 space (GC-protected return).
Definition storage.cpp:55
bool boxesFormatted
Definition storage.h:91
bool depositPokemon(PokemonBox *pokemon)
Put pokemon in the first box with space.
Definition storage.cpp:82
var8e var8
Everyday 8-bit alias. Exact (not "fastest") to dodge the pointer-width bug noted above.
Definition types.h:124
constexpr var8 setMaxBoxes
Boxes per storage set (a save has two sets = 12 boxes).
qmlCppOwned() – protect Q_INVOKABLE QObject returns from QML's GC.
static T * qmlCppOwned(T *obj)
Hand QML CppOwnership of a C++-owned QObject returned from a Q_INVOKABLE.
constexpr var8 maxPokemonBoxes
Total PC boxes (12).
Definition storage.h:35
constexpr var8 maxPokemonStorageSets
PC Pokemon-box banks (2 sets).
Definition storage.h:34