msc/mscb_file/
parser.rs

1use nom::{be_u8, be_u16, be_u32, le_u32, IResult};
2use super::MscsbFile;
3use super::super::{Cmd, Command, Script};
4
5fn get_nom_position(input: &[u8], input_size: usize) -> IResult<&[u8], usize> {
6    let remaining = input.len();
7    do_parse!(input, (input_size - remaining))
8}
9
10fn take_script(input: &[u8], position: usize) -> IResult<&[u8], Script> {
11    do_parse!(
12        input,
13        commands: many0!(complete!(
14                do_parse!(
15                    pos: apply!(get_nom_position, input.len()) >>
16                    cmd: apply!(take_cmd, pos) >>
17                    (cmd)
18                ))) >>
19        size: apply!(get_nom_position, input.len()) >>
20        (Script {
21            bounds: (position as u32, (position + size) as u32),
22            commands
23        })
24    )
25}
26
27pub fn str_from_u8_nul_utf8(utf8_src: &[u8]) -> Result<&str, std::str::Utf8Error> {
28    let nul_range_end = utf8_src.iter()
29        .position(|&c| c == b'\0')
30        .unwrap_or(utf8_src.len()); // default to length if no `\0` present
31    ::std::str::from_utf8(&utf8_src[0..nul_range_end])
32}
33
34pub fn take_file(input: &[u8]) -> IResult<&[u8], MscsbFile> {
35    do_parse!(
36        input,
37        tag!(b"\xB2\xAC\xBC\xBA\xE6\x90\x32\x01\xFD\x02\x00\x00\x00\x00\x00\x00") >>
38        script_data_size: le_u32 >>
39        entrypoint: le_u32 >>
40        script_count: le_u32 >>
41        _unk: le_u32 >>
42        string_size: le_u32 >>
43        string_count: le_u32 >>
44        _padding: take!(8) >>
45        script_data: take!(script_data_size) >>
46        _padding: take!((0x10 - (script_data_size & 0xF)) & 0xF) >> // pad to 0x10
47        script_offsets: count!(le_u32, script_count as usize) >>
48        _padding: take!((0x10 - ((script_count * 4) & 0xF)) & 0xF) >> // pad to 0x10
49        strings: count!(take!(string_size), string_count as usize) >>
50        ({
51            let mut script_offsets = script_offsets.clone();
52            script_offsets.sort();
53            script_offsets.push(script_data_size);
54            let scripts =
55                (0..script_offsets.len() - 1)
56                .filter_map(|i| {
57                    Some(do_parse!(
58                        &script_data[script_offsets[i] as usize..script_offsets[i+1] as usize],
59                        script: complete!(apply!(take_script, script_offsets[i] as usize)) >>
60                        (script)
61                    ).unwrap().1)
62                })
63                .collect();
64            let strings =
65                strings
66                .iter()
67                .map(|s|
68                    String::from(
69                        str_from_u8_nul_utf8(s).unwrap_or("[UTF-8 Error]")
70                )).collect();
71            MscsbFile {
72                scripts,
73                strings,
74                entrypoint
75            }
76        })
77    )
78}
79
80fn take_cmd(input: &[u8], position: usize) -> IResult<&[u8], Command> {
81    do_parse!(
82        input,
83        cmd_num: be_u8 >>
84        cmd: switch!(value!(cmd_num & 0x7F, take!(0)),
85            0 => value!(Cmd::Nop, take!(0)) |
86            1 => value!(Cmd::Unk1, take!(0)) |
87            2 => do_parse!(
88                arg_count: be_u16 >>
89                var_count: be_u16 >>
90                (Cmd::Begin {
91                    arg_count,
92                    var_count
93                })
94            ) |
95            3 => value!(Cmd::End, take!(0)) |
96            4 => do_parse!(
97                loc: be_u32 >>
98                (Cmd::Jump {
99                    loc
100                })
101            ) |
102            5 => do_parse!(
103                loc: be_u32 >>
104                (Cmd::Jump5 {
105                    loc
106                })
107            ) |
108            6 => value!(Cmd::Return6, take!(0)) |
109            7 => value!(Cmd::Return7, take!(0)) |
110            8 => value!(Cmd::Return8, take!(0)) |
111            9 => value!(Cmd::Return9, take!(0)) |
112            0xA => do_parse!(
113                val: be_u32 >>
114                (Cmd::PushInt {
115                    val
116                })
117            ) |
118            0xB => do_parse!(
119                var_type: be_u8 >>
120                var_num: be_u16 >>
121                (Cmd::PushVar {
122                    var_type,
123                    var_num
124                })
125            ) |
126            0xC => value!(Cmd::ErrorC, take!(0)) |
127            0xD => do_parse!(
128                val: be_u16 >>
129                (Cmd::PushShort {
130                    val
131                })
132            ) |
133            0xE => value!(Cmd::AddI, take!(0)) |
134            0xF => value!(Cmd::SubI, take!(0)) |
135            0x10 => value!(Cmd::MultI, take!(0)) |
136            0x11 => value!(Cmd::DivI, take!(0)) |
137            0x12 => value!(Cmd::ModI, take!(0)) |
138            0x13 => value!(Cmd::NegI, take!(0)) |
139            0x14 => do_parse!(
140                var_type: be_u8 >>
141                var_num: be_u16 >>
142                (Cmd::IncI {
143                    var_type,
144                    var_num
145                })
146            ) |
147            0x15 => do_parse!(
148                var_type: be_u8 >>
149                var_num: be_u16 >>
150                (Cmd::DecI {
151                    var_type,
152                    var_num
153                })
154            ) |
155            0x16 => value!(Cmd::AndI, take!(0)) |
156            0x17 => value!(Cmd::OrI, take!(0)) |
157            0x18 => value!(Cmd::NotI, take!(0)) |
158            0x19 => value!(Cmd::XorI, take!(0)) |
159            0x1A => value!(Cmd::ShiftL, take!(0)) |
160            0x1B => value!(Cmd::ShiftR, take!(0)) |
161            0x1C => do_parse!(
162                var_type: be_u8 >>
163                var_num: be_u16 >>
164                (Cmd::SetVar {
165                    var_type,
166                    var_num
167                })
168            ) |
169            0x1D => do_parse!(
170                var_type: be_u8 >>
171                var_num: be_u16 >>
172                (Cmd::AddVarBy {
173                    var_type,
174                    var_num
175                })
176            ) |
177            0x1E => do_parse!(
178                var_type: be_u8 >>
179                var_num: be_u16 >>
180                (Cmd::SubVarBy {
181                    var_type,
182                    var_num
183                })
184            ) |
185            0x1F => do_parse!(
186                var_type: be_u8 >>
187                var_num: be_u16 >>
188                (Cmd::MultVarBy {
189                    var_type,
190                    var_num
191                })
192            ) |
193            0x20 => do_parse!(
194                var_type: be_u8 >>
195                var_num: be_u16 >>
196                (Cmd::DivVarBy {
197                    var_type,
198                    var_num
199                })
200            ) |
201            0x21 => do_parse!(
202                var_type: be_u8 >>
203                var_num: be_u16 >>
204                (Cmd::ModVarBy {
205                    var_type,
206                    var_num
207                })
208            ) |
209            0x22 => do_parse!(
210                var_type: be_u8 >>
211                var_num: be_u16 >>
212                (Cmd::AndVarBy {
213                    var_type,
214                    var_num
215                })
216            ) |
217            0x23 => do_parse!(
218                var_type: be_u8 >>
219                var_num: be_u16 >>
220                (Cmd::OrVarBy {
221                    var_type,
222                    var_num
223                })
224            ) |
225            0x24 => do_parse!(
226                var_type: be_u8 >>
227                var_num: be_u16 >>
228                (Cmd::XorVarBy {
229                    var_type,
230                    var_num
231                })
232            ) |
233            0x25 => value!(Cmd::Equals, take!(0)) |
234            0x26 => value!(Cmd::NotEquals, take!(0)) |
235            0x27 => value!(Cmd::LessThan, take!(0)) |
236            0x28 => value!(Cmd::LessOrEqual, take!(0)) |
237            0x29 => value!(Cmd::Greater, take!(0)) |
238            0x2A => value!(Cmd::GreaterOrEqual, take!(0)) |
239            0x2B => value!(Cmd::Not, take!(0)) |
240            0x2C => do_parse!(
241                arg_count: be_u8 >>
242                (Cmd::PrintF {
243                    arg_count
244                })
245            ) |
246            0x2D => do_parse!(
247                arg_count: be_u8 >>
248                sys_num: be_u8 >>
249                (Cmd::Sys {
250                    arg_count,
251                    sys_num
252                })
253            ) |
254            0x2E => do_parse!(
255                loc: be_u32 >>
256                (Cmd::Try {
257                    loc
258                })
259            ) |
260            0x2F => do_parse!(
261                arg_count: be_u8 >>
262                (Cmd::CallFunc {
263                    arg_count
264                })
265            ) |
266            0x30 => do_parse!(
267                arg_count: be_u8 >>
268                (Cmd::CallFunc2 {
269                    arg_count
270                })
271            ) |
272            0x31 => do_parse!(
273                arg_count: be_u8 >>
274                (Cmd::CallFunc3 {
275                    arg_count
276                })
277            ) |
278            0x32 => value!(Cmd::Push, take!(0)) |
279            0x33 => value!(Cmd::Pop, take!(0)) |
280            0x34 => do_parse!(
281                loc: be_u32 >>
282                (Cmd::If {
283                    loc
284                })
285            ) |
286            0x35 => do_parse!(
287                loc: be_u32 >>
288                (Cmd::IfNot {
289                    loc
290                })
291            ) |
292            0x36 => do_parse!(
293                loc: be_u32 >>
294                (Cmd::Else {
295                    loc
296                })
297            ) |
298            0x37 => value!(Cmd::Error37, take!(0)) |
299            0x38 => do_parse!(
300                stack_pos: be_u8 >>
301                (Cmd::IntToFloat {
302                    stack_pos
303                })
304            ) |
305            0x39 => do_parse!(
306                stack_pos: be_u8 >>
307                (Cmd::FloatToInt {
308                    stack_pos
309                })
310            ) |
311            0x3A => value!(Cmd::AddF, take!(0)) |
312            0x3B => value!(Cmd::SubF, take!(0)) |
313            0x3C => value!(Cmd::MultF, take!(0)) |
314            0x3D => value!(Cmd::DivF, take!(0)) |
315            0x3E => value!(Cmd::NegF, take!(0)) |
316            0x3F => do_parse!(
317                var_type: be_u8 >>
318                var_num: be_u16 >>
319                (Cmd::IncF {
320                    var_type,
321                    var_num
322                })
323            ) |
324            0x40 => do_parse!(
325                var_type: be_u8 >>
326                var_num: be_u16 >>
327                (Cmd::DecF {
328                    var_type,
329                    var_num
330                })
331            ) |
332            0x41 => do_parse!(
333                var_type: be_u8 >>
334                var_num: be_u16 >>
335                (Cmd::VarSetF {
336                    var_type,
337                    var_num
338                })
339            ) |
340            0x42 => do_parse!(
341                var_type: be_u8 >>
342                var_num: be_u16 >>
343                (Cmd::AddVarByF {
344                    var_type,
345                    var_num
346                })
347            ) |
348            0x43 => do_parse!(
349                var_type: be_u8 >>
350                var_num: be_u16 >>
351                (Cmd::SubVarByF {
352                    var_type,
353                    var_num
354                })
355            ) |
356            0x44 => do_parse!(
357                var_type: be_u8 >>
358                var_num: be_u16 >>
359                (Cmd::MultVarByF {
360                    var_type,
361                    var_num
362                })
363            ) |
364            0x45 => do_parse!(
365                var_type: be_u8 >>
366                var_num: be_u16 >>
367                (Cmd::DivVarByF {
368                    var_type,
369                    var_num
370                })
371            ) |
372            0x46 => value!(Cmd::EqualsF, take!(0)) |
373            0x47 => value!(Cmd::NotEqualsF, take!(0)) |
374            0x48 => value!(Cmd::LessThanF, take!(0)) |
375            0x49 => value!(Cmd::LessOrEqualF, take!(0)) |
376            0x4A => value!(Cmd::GreaterF, take!(0)) |
377            0x4B => value!(Cmd::GreaterOrEqualF, take!(0)) |
378            0x4C => value!(Cmd::Error4C, take!(0)) |
379            0x4D => value!(Cmd::Exit, take!(0))
380        ) >>
381        ({
382            Command {
383                position: position as u32,
384                push_bit: cmd_num & 0x80 == 0x80,
385                cmd
386            }
387        })
388    )
389}