1use crate::error::SaveError;
4use crate::offsets::{active, general, save, stored};
5use crate::{ActivePokemon, PmdString, StoredPokemon};
6use arrayvec::ArrayVec;
7use bitvec::bitarr;
8use bitvec::field::BitField;
9use bitvec::order::Lsb0;
10use bitvec::slice::BitSlice;
11use bitvec::view::BitView;
12use std::fs;
13use std::ops::Range;
14use std::path::Path;
15
16const MIN_SAVE_LEN: usize = 0x20000;
18
19fn checksum(data: &[u8], data_range: Range<usize>) -> [u8; 4] {
20 (data[data_range]
21 .chunks(4)
22 .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) .fold(0u64, |acc, u| acc + u as u64) as u32)
24 .to_le_bytes()
25}
26
27fn load_save_slice(data: &[u8], active_save_block: ActiveSaveBlock, range: Range<usize>) -> &[u8] {
28 &data[range.start + active_save_block as usize..range.end + active_save_block as usize]
29}
30
31fn store_save_slice(
32 data: &mut [u8],
33 active_save_block: ActiveSaveBlock,
34 range: Range<usize>,
35 value: &[u8],
36) {
37 data[range.start + active_save_block as usize..range.end + active_save_block as usize]
38 .copy_from_slice(value);
39}
40
41fn load_save_bits(
42 data: &BitSlice<u8, Lsb0>,
43 active_save_block: ActiveSaveBlock,
44 range: Range<usize>,
45) -> &BitSlice<u8, Lsb0> {
46 &data[range.start + active_save_block as usize * 8..range.end + active_save_block as usize * 8]
47}
48
49fn store_save_bits(
50 data: &mut BitSlice<u8, Lsb0>,
51 active_save_block: ActiveSaveBlock,
52 range: Range<usize>,
53 value: &BitSlice<u8, Lsb0>,
54) {
55 data[range.start + active_save_block as usize * 8..range.end + active_save_block as usize * 8]
56 .copy_from_bitslice(value);
57}
58
59#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62#[repr(usize)]
63pub enum ActiveSaveBlock {
64 Primary = save::PRIMARY_SAVE.start,
65 Backup = save::BACKUP_SAVE.start,
66}
67
68#[derive(Debug)]
70pub struct General {
71 pub team_name: PmdString,
72 pub held_money: u32,
73 pub sp_episode_held_money: u32,
74 pub stored_money: u32,
75 pub number_of_adventures: i32,
76 pub explorer_rank: u32,
77}
78
79impl General {
80 fn load(data: &[u8], active_save_block: ActiveSaveBlock) -> Self {
81 let team_name = load_save_slice(data, active_save_block, general::TEAM_NAME);
82 let held_money = load_save_bits(
83 data.view_bits(),
84 active_save_block,
85 general::HELD_MONEY_BITS,
86 );
87 let sp_episode_held_money = load_save_bits(
88 data.view_bits(),
89 active_save_block,
90 general::SP_EPISODE_HELD_MONEY_BITS,
91 );
92 let stored_money = load_save_bits(
93 data.view_bits(),
94 active_save_block,
95 general::STORED_MONEY_BITS,
96 );
97 let number_of_adventures =
98 load_save_slice(data, active_save_block, general::NUMBER_OF_ADVENTURERS)
99 .try_into()
100 .unwrap();
101 let explorer_rank = load_save_slice(data, active_save_block, general::EXPLORER_RANK)
102 .try_into()
103 .unwrap();
104
105 Self {
106 team_name: PmdString::from(team_name),
107 held_money: held_money.load_le(),
108 sp_episode_held_money: sp_episode_held_money.load_le(),
109 stored_money: stored_money.load_le(),
110 number_of_adventures: i32::from_le_bytes(number_of_adventures),
111 explorer_rank: u32::from_le_bytes(explorer_rank),
112 }
113 }
114
115 fn save(&self, data: &mut [u8], active_save_block: ActiveSaveBlock) {
116 store_save_slice(
117 data,
118 active_save_block,
119 general::TEAM_NAME,
120 self.team_name.to_save_bytes().as_slice(),
121 );
122
123 store_save_bits(
124 data.view_bits_mut(),
125 active_save_block,
126 general::HELD_MONEY_BITS,
127 &self.held_money.to_le_bytes().view_bits::<Lsb0>()[0..24],
128 );
129 store_save_bits(
130 data.view_bits_mut(),
131 active_save_block,
132 general::SP_EPISODE_HELD_MONEY_BITS,
133 &self.sp_episode_held_money.to_le_bytes().view_bits::<Lsb0>()[0..24],
134 );
135 store_save_bits(
136 data.view_bits_mut(),
137 active_save_block,
138 general::STORED_MONEY_BITS,
139 &self.stored_money.to_le_bytes().view_bits::<Lsb0>()[0..24],
140 );
141 store_save_slice(
142 data,
143 active_save_block,
144 general::NUMBER_OF_ADVENTURERS,
145 &self.number_of_adventures.to_le_bytes(),
146 );
147 store_save_slice(
148 data,
149 active_save_block,
150 general::EXPLORER_RANK,
151 &self.explorer_rank.to_le_bytes(),
152 );
153 }
154}
155
156#[derive(Debug)]
160pub struct SkySave {
161 pub data: Vec<u8>,
162 pub active_save_block: ActiveSaveBlock,
163 pub quicksave_valid: bool,
164
165 pub general: General,
166 pub stored_pokemon: ArrayVec<StoredPokemon, 720>,
167 pub active_pokemon: ArrayVec<ActivePokemon, 4>,
168}
169
170impl SkySave {
171 pub fn from_slice<S: AsRef<[u8]>>(data: S) -> Result<Self, SaveError> {
182 let data = data.as_ref();
183
184 if data.len() < MIN_SAVE_LEN {
185 return Err(SaveError::InvalidSize);
186 }
187
188 let pri_read: [u8; 4] = data[save::PRIMARY_READ_CHECKSUM].try_into().unwrap(); let backup_read: [u8; 4] = data[save::BACKUP_READ_CHECKSUM].try_into().unwrap(); let quick_read: [u8; 4] = data[save::QUICKSAVE_READ_CHECKSUM].try_into().unwrap(); let pri_sum = checksum(data, save::PRIMARY_CHECKSUM);
193 let backup_sum = checksum(data, save::BACKUP_CHECKSUM);
194 let quick_sum = checksum(data, save::QUICKSAVE_CHECKSUM);
195
196 let pri_matches = pri_sum == pri_read;
197 let backup_matches = backup_sum == backup_read;
198 let quick_matches = quick_sum == quick_read;
199
200 if !pri_matches && !backup_matches {
201 return Err(SaveError::InvalidChecksum {
202 pri_expected: pri_read,
203 pri_found: pri_sum,
204 bak_expected: backup_read,
205 bak_found: backup_sum,
206 });
207 }
208
209 let active_save_block = if pri_matches {
210 ActiveSaveBlock::Primary
211 } else {
212 ActiveSaveBlock::Backup
213 };
214
215 let general = General::load(data, active_save_block);
216 let bits = load_save_bits(data.view_bits(), active_save_block, stored::STORED_PKM_BITS);
217
218 let stored_pokemon: ArrayVec<StoredPokemon, 720> = bits
219 .chunks(stored::STORED_PKM_BIT_LEN)
220 .map(StoredPokemon::from_bitslice)
221 .collect();
222
223 let bits = load_save_bits(data.view_bits(), active_save_block, active::ACTIVE_PKM_BITS);
224 let active_pokemon: ArrayVec<ActivePokemon, 4> = bits
225 .chunks(active::ACTIVE_PKM_BIT_LEN)
226 .map(ActivePokemon::from_bitslice)
227 .collect();
228
229 Ok(SkySave {
230 data: data.to_vec(),
231 active_save_block,
232 quicksave_valid: quick_matches,
233 general,
234 stored_pokemon,
235 active_pokemon,
236 })
237 }
238
239 pub fn open<P: AsRef<Path>>(filename: P) -> Result<Self, SaveError> {
241 let data = fs::read(filename).map_err(SaveError::Io)?;
242 Self::from_slice(&data)
243 }
244
245 pub fn fix_checksums(&mut self) {
248 let pri_sum = checksum(&self.data, save::PRIMARY_CHECKSUM);
249 let backup_sum = checksum(&self.data, save::BACKUP_CHECKSUM);
250 let quick_sum = checksum(&self.data, save::QUICKSAVE_CHECKSUM);
251
252 self.data[save::PRIMARY_READ_CHECKSUM].copy_from_slice(&pri_sum);
253 self.data[save::BACKUP_READ_CHECKSUM].copy_from_slice(&backup_sum);
254 self.data[save::QUICKSAVE_READ_CHECKSUM].copy_from_slice(&quick_sum);
255 }
256
257 pub fn save<P: AsRef<Path>>(&mut self, filename: P) -> Result<(), SaveError> {
259 let active_range = match self.active_save_block {
260 ActiveSaveBlock::Primary => save::PRIMARY_SAVE,
261 ActiveSaveBlock::Backup => save::BACKUP_SAVE,
262 };
263
264 let backup = match self.active_save_block {
265 ActiveSaveBlock::Primary => save::BACKUP_SAVE.start,
266 ActiveSaveBlock::Backup => save::PRIMARY_SAVE.start,
267 };
268
269 self.general.save(&mut self.data, self.active_save_block);
270
271 let stored = self
273 .stored_pokemon
274 .iter()
275 .map(StoredPokemon::to_bits)
276 .enumerate()
277 .fold(
278 bitarr![u8, Lsb0; 0; stored::STORED_PKM_BIT_LEN * stored::STORED_PKM_COUNT],
279 |mut acc, (idx, a)| {
280 acc[idx * stored::STORED_PKM_BIT_LEN..(idx + 1) * stored::STORED_PKM_BIT_LEN]
281 .copy_from_bitslice(&a[0..stored::STORED_PKM_BIT_LEN]);
282 acc
283 },
284 );
285 store_save_bits(
286 self.data.view_bits_mut(),
287 self.active_save_block,
288 stored::STORED_PKM_BITS,
289 stored.as_bitslice(),
290 );
291
292 let active = self
293 .active_pokemon
294 .iter()
295 .map(ActivePokemon::to_bits)
296 .enumerate()
297 .fold(
298 bitarr![u8, Lsb0; 0; active::ACTIVE_PKM_BIT_LEN * active::ACTIVE_PKM_COUNT],
299 |mut acc, (idx, a)| {
300 acc[idx * active::ACTIVE_PKM_BIT_LEN..(idx + 1) * active::ACTIVE_PKM_BIT_LEN]
301 .copy_from_bitslice(&a[0..active::ACTIVE_PKM_BIT_LEN]);
302 acc
303 },
304 );
305 store_save_bits(
306 self.data.view_bits_mut(),
307 self.active_save_block,
308 active::ACTIVE_PKM_BITS,
309 active.as_bitslice(),
310 );
311
312 self.data.copy_within(active_range, backup);
313 self.fix_checksums();
314
315 fs::write(filename, &self.data).map_err(SaveError::Io)
316 }
317}