Skip to main content

fallout_core/fallout1/
header.rs

1use std::io::{self, Read, Seek};
2
3use crate::reader::BigEndianReader;
4
5use super::types::{HEADER_PADDING, PREVIEW_SIZE, SIGNATURE};
6
7#[derive(Debug)]
8pub struct SaveHeader {
9    pub character_name: String,
10    pub description: String,
11    pub version_major: i16,
12    pub version_minor: i16,
13    pub version_release: u8,
14    pub file_day: i16,
15    pub file_month: i16,
16    pub file_year: i16,
17    pub file_time: i32,
18    pub game_month: i16,
19    pub game_day: i16,
20    pub game_year: i16,
21    pub game_time: u32,
22    pub elevation: i16,
23    pub map: i16,
24    pub map_filename: String,
25}
26
27impl SaveHeader {
28    pub fn parse<R: Read + Seek>(r: &mut BigEndianReader<R>) -> io::Result<Self> {
29        // Signature: 24 bytes, validate first 18
30        let sig_bytes = r.read_bytes(24)?;
31        if &sig_bytes[..SIGNATURE.len()] != SIGNATURE {
32            return Err(io::Error::new(
33                io::ErrorKind::InvalidData,
34                "invalid save file signature",
35            ));
36        }
37
38        let version_minor = r.read_i16()?;
39        let version_major = r.read_i16()?;
40        let version_release = r.read_u8()?;
41
42        if version_minor != 1 || version_major != 1 || version_release != b'R' {
43            return Err(io::Error::new(
44                io::ErrorKind::InvalidData,
45                format!(
46                    "unsupported version: {}.{}{}",
47                    version_major, version_minor, version_release as char
48                ),
49            ));
50        }
51
52        let character_name = r.read_fixed_string(32)?;
53        let description = r.read_fixed_string(30)?;
54
55        // Note: written in order day, month, year (see SaveHeader() in loadsave.cc)
56        let file_day = r.read_i16()?;
57        let file_month = r.read_i16()?;
58        let file_year = r.read_i16()?;
59        let file_time = r.read_i32()?;
60
61        let game_month = r.read_i16()?;
62        let game_day = r.read_i16()?;
63        let game_year = r.read_i16()?;
64        let game_time = r.read_u32()?;
65
66        let elevation = r.read_i16()?;
67        let map = r.read_i16()?;
68        let map_filename = r.read_fixed_string(16)?;
69
70        // Skip thumbnail and padding
71        r.skip((PREVIEW_SIZE + HEADER_PADDING) as u64)?;
72
73        Ok(Self {
74            character_name,
75            description,
76            version_major,
77            version_minor,
78            version_release,
79            file_day,
80            file_month,
81            file_year,
82            file_time,
83            game_month,
84            game_day,
85            game_year,
86            game_time,
87            elevation,
88            map,
89            map_filename,
90        })
91    }
92}