1use crate::crs::{Reader, Writer};
5use crate::error::{format_err, Error, Result};
6use crate::stream;
7
8pub struct Save {
10 pub metadata: Vec<u8>,
12 pub state: Vec<u8>,
14}
15
16#[derive(Debug, Clone, PartialEq)]
21pub struct SaveMetadata {
22 pub name: String,
24 pub width: i32,
26 pub height: i32,
28 pub image: Vec<u8>,
30 pub value: i32,
32 pub mods: Vec<SavedMod>,
34 pub wide: bool,
36}
37
38#[derive(Debug, Clone, PartialEq)]
40pub struct SavedMod {
41 pub name: String,
43 pub file: String,
45 pub values: [u32; 3],
47}
48
49impl SaveMetadata {
50 pub fn parse(data: &[u8]) -> Result<SaveMetadata> {
52 let mut r = Reader::new(data);
53 let mut meta = r.block(|r, version| {
54 let name = r.string()?;
55 let width = r.i32()?;
56 let height = r.i32()?;
57 let image = r
58 .bytes(width.max(0) as usize * height.max(0) as usize * 4)?
59 .to_vec();
60 let (value, mods) = if version > 2 {
61 let value = r.i32()?;
62 let mods = (0..r.count()?)
63 .map(|_| {
64 Ok(SavedMod {
65 name: r.string()?,
66 file: r.string()?,
67 values: [r.u32()?, r.u32()?, r.u32()?],
68 })
69 })
70 .collect::<Result<_>>()?;
71 (value, mods)
72 } else {
73 (0, Vec::new())
74 };
75 Ok(SaveMetadata {
76 name,
77 width,
78 height,
79 image,
80 value,
81 mods,
82 wide: false,
83 })
84 })?;
85 meta.wide = r.wide_seen;
86 Ok(meta)
87 }
88
89 pub fn write(&self) -> Vec<u8> {
91 let mut w = Writer::new();
92 w.wide_strings = self.wide;
93 let at = w.begin_block(3);
94 w.string(&self.name);
95 w.i32(self.width);
96 w.i32(self.height);
97 w.bytes(&self.image);
98 w.i32(self.value);
99 w.count(self.mods.len());
100 for m in &self.mods {
101 w.string(&m.name);
102 w.string(&m.file);
103 m.values.iter().for_each(|&v| w.u32(v));
104 }
105 w.end_block(at);
106 w.data
107 }
108}
109
110fn header_check(table: &[u8], blocks: &[&[u8]]) -> u32 {
114 let step = |c: u32, data: &[u8]| !crate::crc::crc32_raw(c, data);
115 let mut c = step(!0, table);
116 for b in blocks {
117 for piece in b.chunks(stream::BLOCK_SIZE) {
118 c = step(c, piece);
119 }
120 }
121 c
122}
123
124#[derive(Debug, Clone)]
128pub struct LoopHeader {
129 pub climate: String,
131 pub start_condition: String,
133 pub terrain: String,
135 pub seed: u32,
137 pub size_index: i32,
139}
140
141#[derive(Debug, Clone)]
145pub struct MapHeights {
146 pub width: usize,
148 pub heights: Vec<i16>,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub struct MapCell {
157 pub objects: [bool; 2],
160 pub external: u32,
163 pub flags: [u8; 9],
166 pub value: u16,
169 pub bytes: [u8; 6],
172}
173
174impl MapCell {
175 pub fn cell_type(&self) -> u8 {
177 self.bytes[1]
178 }
179}
180
181#[derive(Debug, Clone)]
186pub struct MapCells {
187 pub size: usize,
189 pub cells: Vec<MapCell>,
191 pub trailer: [i16; 9],
194}
195
196fn u32_at(b: &[u8], at: usize) -> Result<u32> {
197 b.get(at..at + 4)
198 .map(|s| u32::from_le_bytes(s.try_into().unwrap()))
199 .ok_or(Error::Truncated { at, wanted: 4 })
200}
201
202#[derive(Debug, Clone, PartialEq)]
205pub struct SavedEvent {
206 pub color: u32,
208 pub text: String,
210 pub kind: i32,
212 pub severity: i32,
214 pub cell: i32,
216 pub category: i32,
218 pub icon: i32,
220}
221
222#[derive(Debug, Clone)]
225pub struct SavedEventLog {
226 pub notify: [bool; 6],
228 pub entries: Vec<SavedEvent>,
230}
231
232impl Save {
233 pub fn parse(data: &[u8]) -> Result<Save> {
235 let (meta, state) = Save::payloads(data)?;
236 Ok(Save {
237 metadata: stream::decode(meta)?,
238 state: stream::decode(state)?,
239 })
240 }
241
242 pub fn read_metadata(data: &[u8]) -> Result<SaveMetadata> {
248 SaveMetadata::parse(&stream::decode(Save::payloads(data)?.0)?)
249 }
250
251 fn payloads(data: &[u8]) -> Result<(&[u8], &[u8])> {
253 if data.len() < 2 {
254 return Err(Error::Truncated { at: 0, wanted: 2 });
255 }
256 let version = u16::from_le_bytes([data[0], data[1]]);
257 let (first, second) = match version {
261 1 => ((0x08, 0x0c), (0x14, 0x18)),
262 2 => ((0x10, 0x14), (0x1c, 0x20)),
263 v => return Err(Error::Unsupported(format!("save container version {v}"))),
264 };
265 let payload = |(o, s): (usize, usize)| -> Result<&[u8]> {
266 let (off, size) = (u32_at(data, o)? as usize, u32_at(data, s)? as usize);
267 data.get(off..off + size).ok_or(Error::Truncated {
268 at: off,
269 wanted: size,
270 })
271 };
272 let (meta, state) = (payload(first)?, payload(second)?);
273 if state.as_ptr_range().end != data.as_ptr_range().end {
274 return format_err("game-state payload does not end at EOF");
275 }
276 if version == 2 && u32_at(data, 4)? == 0x8080_8080 {
277 let count = u16::from_le_bytes([data[2], data[3]]) as usize;
278 let table = data.get(12..12 + 12 * count).ok_or(Error::Truncated {
279 at: 12,
280 wanted: 12 * count,
281 })?;
282 if header_check(table, &[meta, state]) != u32_at(data, 8)? {
283 return format_err("save check value does not match");
284 }
285 }
286 Ok((meta, state))
287 }
288
289 pub fn metadata(&self) -> Result<SaveMetadata> {
291 SaveMetadata::parse(&self.metadata)
292 }
293
294 pub fn write(metadata: &SaveMetadata, state: &[u8]) -> Vec<u8> {
297 let meta = stream::encode(&metadata.write());
298 let state = stream::encode(state);
299 const HEADER: usize = 12 + 2 * 12;
300 let mut table = Vec::new();
301 for (id, off, size) in [
302 (0, HEADER, meta.len()),
303 (1, HEADER + meta.len(), state.len()),
304 ] {
305 for v in [id as u32, off as u32, size as u32] {
306 table.extend_from_slice(&v.to_le_bytes());
307 }
308 }
309 let mut out = Vec::with_capacity(HEADER + meta.len() + state.len());
310 out.extend_from_slice(&2u16.to_le_bytes());
311 out.extend_from_slice(&2u16.to_le_bytes());
312 out.extend_from_slice(&0x8080_8080u32.to_le_bytes());
313 out.extend_from_slice(&header_check(&table, &[&meta, &state]).to_le_bytes());
314 out.extend_from_slice(&table);
315 out.extend_from_slice(&meta);
316 out.extend_from_slice(&state);
317 out
318 }
319
320 pub fn event_log(&self) -> Option<SavedEventLog> {
325 let d = &self.state;
326 let marker = 0xaaaa_aaaau32.to_le_bytes();
327 let mut at = 0;
328 while let Some(p) = d[at..].windows(4).position(|w| w == marker) {
329 let start = at + p;
330 at = start + 4;
331 if let Some(log) = parse_event_block(&d[start..]) {
332 return Some(log);
333 }
334 }
335 None
336 }
337
338 pub fn references(&self) -> Result<Vec<(u32, String)>> {
340 let mut r = Reader::new(&self.state);
341 r.block(|r, _| {
342 r.block(|r, _| {
343 let n = r.count()?;
344 (0..n).map(|_| Ok((r.u32()?, r.string()?))).collect()
345 })
346 .map(|refs| {
347 skip_rest(r);
349 refs
350 })
351 })
352 }
353
354 pub fn loop_header(&self) -> Result<LoopHeader> {
356 let refs = self.references()?;
357 let name = |id: u32| {
358 refs.iter()
359 .find(|(i, _)| *i == id)
360 .map(|(_, p)| p.clone())
361 .unwrap_or_default()
362 };
363 let mut r = Reader::new(&self.state);
364 r.block(|r, _| {
365 r.skip_block()?; r.skip_block()?; let (climate, start, terrain) = (r.u32()?, r.u32()?, r.u32()?);
368 let seed = r.u32()?;
369 let size_index = r.i32()?;
370 skip_rest(r);
371 Ok(LoopHeader {
372 climate: name(climate),
373 start_condition: name(start),
374 terrain: name(terrain),
375 seed,
376 size_index,
377 })
378 })
379 }
380
381 pub fn map_heights(&self) -> Result<MapHeights> {
383 for n in [256i32, 384, 512] {
384 let pat: Vec<u8> = [2 * n, n, 32, 33, 2 * n + 1, 33 * 33]
385 .iter()
386 .flat_map(|v| v.to_le_bytes())
387 .collect();
388 if let Some(at) = self
389 .state
390 .windows(pat.len())
391 .position(|w| w == pat.as_slice())
392 {
393 let count = u32_at(&self.state, at + 24)? as usize;
394 let width = (2 * n + 1) as usize;
395 if count != width * width {
396 return format_err("map height count does not match its dimensions");
397 }
398 let start = at + 28;
399 let bytes = self
400 .state
401 .get(start..start + 2 * count)
402 .ok_or(Error::Truncated {
403 at: start,
404 wanted: 2 * count,
405 })?;
406 let heights = bytes
407 .chunks_exact(2)
408 .map(|c| i16::from_le_bytes([c[0], c[1]]))
409 .collect();
410 return Ok(MapHeights { width, heights });
411 }
412 }
413 format_err("no map block found")
414 }
415}
416
417impl Save {
418 pub fn map_cells(&self) -> Result<MapCells> {
421 let n = (self.map_heights()?.width - 1) / 2;
422 let pat: Vec<u8> = [0xaaaa_aaaau32, 0, 4, n as u32, (n * n) as u32]
423 .iter()
424 .flat_map(|v| v.to_le_bytes())
425 .collect();
426 let at = self
427 .state
428 .windows(pat.len())
429 .position(|w| w[..4] == pat[..4] && w[8..] == pat[8..])
430 .ok_or_else(|| Error::Format("no map cell block found".into()))?;
431 let mut r = Reader::new(&self.state[at + 20..]);
432 let ptr = |r: &mut Reader| -> Result<bool> {
433 let set = r.u8()? != 0;
434 if set {
435 r.bytes(12)?;
436 }
437 Ok(set)
438 };
439 let mut cells = Vec::with_capacity(n * n);
440 for _ in 0..n * n {
441 let objects = [ptr(&mut r)?, ptr(&mut r)?];
442 let external = r.u32()?;
443 let flags: [u8; 9] = r.bytes(9)?.try_into().unwrap();
444 let value = u16::from_le_bytes(r.bytes(2)?.try_into().unwrap());
445 let bytes: [u8; 6] = r.bytes(6)?.try_into().unwrap();
446 cells.push(MapCell {
447 objects,
448 external,
449 flags,
450 value,
451 bytes,
452 });
453 }
454 let mut trailer = [0i16; 9];
455 for t in &mut trailer {
456 *t = i16::from_le_bytes(r.bytes(2)?.try_into().unwrap());
457 }
458 if r.u32()? != 0xbbbb_bbbb {
459 return format_err("map cell block does not end with its terminator");
460 }
461 Ok(MapCells {
462 size: n,
463 cells,
464 trailer,
465 })
466 }
467}
468
469fn skip_rest(r: &mut Reader) {
470 let rest = r.remaining();
471 let _ = r.bytes(rest);
472}
473
474fn parse_event_block(b: &[u8]) -> Option<SavedEventLog> {
475 let u32_at = |o: usize| {
476 b.get(o..o + 4)
477 .map(|s| u32::from_le_bytes(s.try_into().unwrap()))
478 };
479 let size = u32_at(4)? as usize;
481 if size < 14 || u32_at(8 + size)? != 0xbbbb_bbbb || u32_at(8)? != 1 {
482 return None;
483 }
484 let body = &b[8..8 + size];
485 if body[4..10].iter().any(|&v| v > 1) {
486 return None;
487 }
488 let notify: [bool; 6] = std::array::from_fn(|i| body[4 + i] == 1);
489 let mut o = 10;
490 let rd = |o: &mut usize| -> Option<u32> {
491 let v = body.get(*o..*o + 4)?;
492 *o += 4;
493 Some(u32::from_le_bytes(v.try_into().unwrap()))
494 };
495 let count = rd(&mut o)? as usize;
496 if count > 100_000 {
497 return None;
498 }
499 let mut entries = Vec::with_capacity(count);
500 for _ in 0..count {
501 let color = rd(&mut o)?;
502 let len = rd(&mut o)?;
503 let text = if len & 0x8000_0000 != 0 {
506 let n = (len & 0x7fff_ffff) as usize;
507 let text = body.get(o..o + n.checked_sub(1)?)?;
508 o += n;
509 String::from_utf8_lossy(text).into_owned()
510 } else {
511 let n = len as usize;
512 let units: Vec<u16> = body
513 .get(o..o + 2 * n.checked_sub(1)?)?
514 .chunks_exact(2)
515 .map(|c| u16::from_le_bytes([c[0], c[1]]))
516 .collect();
517 o += 2 * n;
518 String::from_utf16_lossy(&units)
519 };
520 let mut i = || rd(&mut o).map(|v| v as i32);
521 entries.push(SavedEvent {
522 color,
523 text,
524 kind: i()?,
525 severity: i()?,
526 cell: i()?,
527 category: i()?,
528 icon: i()?,
529 });
530 }
531 (o == body.len()).then_some(SavedEventLog { notify, entries })
532}