1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use jomini::{BinaryDeserializer, FailedResolveStrategy, TextDeserializer, TextTape};

use crate::{Hoi4Error, flavor::Hoi4Flavor, models::Hoi4Save, tokens::TokenLookup};

/// Describes the format of the save before decoding
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Encoding {
    /// Binary save
    Binary,

    /// Plaintext save
    Plaintext,
}

/// Customize how a save is extracted
#[derive(Debug, Clone)]
pub struct Hoi4ExtractorBuilder {
    on_failed_resolve: FailedResolveStrategy,
}

impl Default for Hoi4ExtractorBuilder {
    fn default() -> Self {
        Hoi4ExtractorBuilder::new()
    }
}

impl Hoi4ExtractorBuilder {
    /// Create a new extractor with default values: extract zips into memory
    // and ignore unknown binary tokens
    pub fn new() -> Self {
        Hoi4ExtractorBuilder {
            on_failed_resolve: FailedResolveStrategy::Ignore,
        }
    }

    /// Set the behavior for when an unresolved binary token is encountered
    pub fn with_on_failed_resolve(mut self, strategy: FailedResolveStrategy) -> Self {
        self.on_failed_resolve = strategy;
        self
    }

    /// Extract all info from a save
    pub fn extract_save(&self, data: &[u8]) -> Result<(Hoi4Save, Encoding), Hoi4Error> {
        self.extract_save_as(data)
    }

    // todo, customize deserialize type
    fn extract_save_as(&self, data: &[u8]) -> Result<(Hoi4Save, Encoding), Hoi4Error> {
        let text_header = b"HOI4txt";
        let binary_header = b"HOI4bin";
        match data.get(..text_header.len()) {
            Some(x) if x == text_header => {
                let save_data = &data[text_header.len()..];
                let tape = TextTape::from_slice(save_data)?;
                let save = TextDeserializer::from_utf8_tape(&tape)?;
                Ok((save, Encoding::Plaintext))
            },
            Some(x) if x == binary_header => {
                let save_data = &data[binary_header.len()..];
                let save = BinaryDeserializer::builder_flavor(Hoi4Flavor).from_slice(save_data, &TokenLookup)?;
                Ok((save, Encoding::Binary))
            }
            _ => {
                return Err(Hoi4Error::new(crate::Hoi4ErrorKind::UnknownHeader))
            }
        }
    }
}

/// Logic container for extracting data from an Hoi4 save
#[derive(Debug, Clone)]
pub struct Hoi4Extractor {}

impl Hoi4Extractor {
    /// Create a customized container
    pub fn builder() -> Hoi4ExtractorBuilder {
        Hoi4ExtractorBuilder::new()
    }

    /// Extract all info from a save
    pub fn extract_save(&self, data: &[u8]) -> Result<(Hoi4Save, Encoding), Hoi4Error> {
        Self::builder().extract_save(data)
    }
}