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
use std::io::{Read,Seek};

use crate::nom::{
    IResult,
    all_consuming,
    line_ending,
    many0,
    many1,
    tag,
    opt,
};

use crate::{
    Decode,
    Error,
    ErrorKind,
    ScriptField,
    Gtg,
    ScriptReference,
    Tng,
    ScriptExpression,
};

impl Decode for Gtg {
    fn decode<Source>(source: &mut Source) -> Result<Gtg, Error> where
        Source: Read + Seek
    {
        let mut input = Vec::new();
        source.read_to_end(&mut input)?;
        let (_, gtg) = all_consuming(Gtg::decode_gtg)(&input)?;
        Ok(gtg)
    }
}

impl Gtg {
    pub fn decode_gtg(input: &[u8]) -> IResult<&[u8], Gtg, Error> {
        let (input, maps) = many1(Self::decode_gtg_map)(input)?;
        Ok((input, Gtg { maps: maps }))
    }

    pub fn decode_gtg_map(input: &[u8]) -> IResult<&[u8], Tng, Error> {
        let (input, _start) = Self::decode_gtg_field_named("NEWMAP")(input)?;
        let (input, tng) = Tng::decode_tng(input)?;
        let (input, _end) = Self::decode_gtg_field_named("ENDMAP")(input)?;
        Ok((input, tng))
    }

    /// This is a variation of `fable::script::decode_tagged_field` because "NEWMAP" and "ENDMAP" don't use semicolons.
    pub fn decode_gtg_field_named(name: &'static str) -> impl Fn(&[u8]) -> IResult<&[u8], ScriptField, Error> {
        move |input: &[u8]| {
            let (input, _line_ending) = many0(line_ending)(input)?;
            let (input, reference) = ScriptReference::decode_reference(input)?;
            let (input, _space) = opt(tag(" "))(input)?;
            let (input, expression) = ScriptExpression::decode_expression(input)?;
            let (input, _line_ending) = many1(line_ending)(input)?;

           let field_name = match reference {
                ScriptReference::Name(x) => x,
                ScriptReference::ScriptAccessor(_) => return Err(nom::Err::Error(Error::Fable(ErrorKind::InvalidInstruction))),
            };

            if field_name != name {
                return Err(nom::Err::Error(Error::Fable(ErrorKind::InvalidTagName)));
            }

            Ok(
                (
                    input,
                    ScriptField {
                        reference: ScriptReference::Name(field_name),
                        value: Box::new(expression),
                    }
                )
            )
        }
    }
}