Mercurial > touhou
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 {