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()); ::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) >> script_offsets: count!(le_u32, script_count as usize) >>
48 _padding: take!((0x10 - ((script_count * 4) & 0xF)) & 0xF) >> 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}