changeset 714:fcc8f736c746

ecl: Simplify parsing.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Mon, 23 Sep 2019 00:06:02 +0200
parents 258f4aebf3fc
children 2b2376811f46
files src/th06/ecl.rs
diffstat 1 files changed, 63 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/src/th06/ecl.rs
+++ b/src/th06/ecl.rs
@@ -4,7 +4,9 @@ use nom::{
     IResult,
     number::complete::{le_u8, le_u16, le_u32, le_i16, le_i32, le_f32},
     sequence::tuple,
-    multi::many_m_n,
+    multi::{count, many0},
+    error::ErrorKind,
+    Err,
 };
 use encoding_rs::SHIFT_JIS;
 use bitflags::bitflags;
@@ -146,10 +148,10 @@ macro_rules! declare_main_instructions {
 }
 
 /// Parse a SHIFT_JIS byte string of length 34 into a String.
+#[allow(non_snake_case)]
 pub fn le_String(i: &[u8]) -> IResult<&[u8], String> {
-    assert_eq!(i.len(), 34);
     let data = i.splitn(2, |c| *c == b'\0').nth(0).unwrap();
-    let (string, encoding, replaced) = SHIFT_JIS.decode(data);
+    let (string, _encoding, _replaced) = SHIFT_JIS.decode(data);
     Ok((&i[34..], string.into_owned()))
 }
 
@@ -301,12 +303,11 @@ declare_sub_instructions!{
     115 => fn SetTimeout(timeout: i32),
     116 => fn SetTimeoutCallback(sub: i32),
     117 => fn SetTouchable(touchable: i32),
-    118 => fn DropParticles(anim: i32, number: u32, r: u8, g: u8, b: u8, UNUSED: u8),
+    118 => fn DropParticles(anim: i32, number: u32, r: u8, g: u8, b: u8, a: u8),
     119 => fn DropBonus(number: i32),
     120 => fn SetAutomaticOrientation(automatic: i32),
     121 => fn CallSpecialFunction(function: i32, argument: i32),
-    // TODO: Found in stage 3 then 5 onward.
-    122 => fn UNK_ins122(TODO: i32),
+    122 => fn SetSpecialFunctionCallback(function: i32),
     123 => fn SkipFrames(frames: i32),
     124 => fn DropSpecificBonus(type_: i32),
     // TODO: Found in stage 3.
@@ -326,39 +327,69 @@ declare_sub_instructions!{
     135 => fn EnableSpellcardBonus(UNKNOW: i32),
 }
 
+fn parse_sub_instruction(input: &[u8]) -> IResult<&[u8], CallSub> {
+    let i = &input[..];
+    let (i, (time, opcode)) = tuple((le_i32, le_u16))(i)?;
+    if time == -1 || opcode == 0xffff {
+        return Err(Err::Error((i, ErrorKind::Eof)));
+    }
+
+    let (i, (size, rank_mask, param_mask)) = tuple((le_u16, le_u16, le_u16))(i)?;
+    let rank_mask = Rank::from_bits(rank_mask).unwrap();
+    let (i, instr) = parse_sub_instruction_args(i, opcode)?;
+    assert_eq!(input.len() - i.len(), size as usize);
+    let call = CallSub { time, rank_mask, param_mask, instr };
+    Ok((i, call))
+}
+
+fn parse_sub(i: &[u8]) -> IResult<&[u8], Sub> {
+    let (i, instructions) = many0(parse_sub_instruction)(i)?;
+    let sub = Sub { instructions };
+    Ok((i, sub))
+}
+
+fn parse_main_instruction(input: &[u8]) -> IResult<&[u8], CallMain> {
+    let i = &input[..];
+    let (i, (time, sub)) = tuple((le_u16, le_u16))(i)?;
+    if time == 0xffff && sub == 4 {
+        return Err(Err::Error((i, ErrorKind::Eof)));
+    }
+
+    let (i, (opcode, size)) = tuple((le_u16, le_u16))(i)?;
+    let size = size as usize;
+    let (i, instr) = parse_main_instruction_args(i, opcode)?;
+    assert_eq!(input.len() - i.len(), size as usize);
+    let call = CallMain { time, sub, instr };
+    Ok((i, call))
+}
+
+fn parse_main(i: &[u8]) -> IResult<&[u8], Main> {
+    let (i, instructions) = many0(parse_main_instruction)(i)?;
+    let main = Main { instructions };
+    Ok((i, main))
+}
+
 fn parse_ecl(input: &[u8]) -> IResult<&[u8], Ecl> {
     let i = input;
 
-    let (i, sub_count) = le_u16(i)?;
+    let (i, (sub_count, main_count)) = tuple((le_u16, le_u16))(i)?;
     let sub_count = sub_count as usize;
-    let (i, main_count) = le_u16(i)?;
-    assert_eq!(main_count, 0);
 
-    let (i, main_offsets) = many_m_n(3, 3, le_u32)(i)?;
-    let (_, sub_offsets) = many_m_n(sub_count, sub_count, le_u32)(i)?;
+    if main_count != 0 {
+        // TODO: use a better error.
+        return Err(Err::Error((i, ErrorKind::Eof)));
+    }
+
+    let (_, (main_offsets, sub_offsets)) = tuple((
+        count(le_u32, 3),
+        count(le_u32, sub_count),
+    ))(i)?;
 
     // Read all subs.
     let mut subs = Vec::new();
     for offset in sub_offsets.into_iter().map(|offset| offset as usize) {
-        let mut i = &input[offset..];
-        let mut instructions = Vec::new();
-        loop {
-            let (i2, (time, opcode)) = tuple((le_i32, le_u16))(i)?;
-            if time == -1 || opcode == 0xffff {
-                break;
-            }
-
-            let (i2, (size, rank_mask, param_mask)) = tuple((le_u16, le_u16, le_u16))(i2)?;
-            let size = size as usize;
-            let rank_mask = Rank::from_bits(rank_mask).unwrap();
-            // FIXME: this - 12 can trigger a panic, fuzz it!
-            let data = &i2[..size - 12];
-            let (data, instr) = parse_sub_instruction_args(data, opcode)?;
-            assert_eq!(data.len(), 0);
-            instructions.push(CallSub { time, rank_mask, param_mask, instr });
-            i = &i[size..];
-        }
-        subs.push(Sub { instructions });
+        let (_, sub) = parse_sub(&input[offset..])?;
+        subs.push(sub);
     }
 
     // Read all mains (always a single one atm).
@@ -367,25 +398,8 @@ fn parse_ecl(input: &[u8]) -> IResult<&[
         if offset == 0 {
             break;
         }
-
-        let mut i = &input[offset..];
-        let mut instructions = Vec::new();
-        loop {
-            let (i2, (time, sub)) = tuple((le_u16, le_u16))(i)?;
-            if time == 0xffff && sub == 4 {
-                break;
-            }
-
-            let (i2, (opcode, size)) = tuple((le_u16, le_u16))(i2)?;
-            let size = size as usize;
-            // FIXME: this - 8 can trigger a panic, fuzz it!
-            let data = &i2[..size - 8];
-            let (data, instr) = parse_main_instruction_args(data, opcode)?;
-            assert_eq!(data.len(), 0);
-            instructions.push(CallMain { time, sub, instr });
-            i = &i[size..];
-        }
-        mains.push(Main { instructions });
+        let (_, main) = parse_main(&input[offset..])?;
+        mains.push(main);
     }
 
     let ecl = Ecl {