Mercurial > touhou
comparison src/th06/anm0.rs @ 713:258f4aebf3fc
anm0: Parse only a single anm0 at once and use many0 for more, to simplify parsing.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 17 Sep 2019 17:36:02 +0200 |
parents | 84af5bedbde4 |
children | fc937d93a57c |
comparison
equal
deleted
inserted
replaced
712:13fd434d5d1b | 713:258f4aebf3fc |
---|---|
3 use nom::{ | 3 use nom::{ |
4 IResult, | 4 IResult, |
5 bytes::complete::{tag, take_while_m_n}, | 5 bytes::complete::{tag, take_while_m_n}, |
6 number::complete::{le_u8, le_u16, le_u32, le_i32, le_f32}, | 6 number::complete::{le_u8, le_u16, le_u32, le_i32, le_f32}, |
7 sequence::tuple, | 7 sequence::tuple, |
8 multi::many_m_n, | 8 multi::{many_m_n, many0}, |
9 }; | 9 }; |
10 use std::collections::HashMap; | 10 use std::collections::HashMap; |
11 | 11 |
12 /// Coordinates of a sprite into the image. | 12 /// Coordinates of a sprite into the image. |
13 #[derive(Debug, Clone)] | 13 #[derive(Debug, Clone)] |
71 } | 71 } |
72 | 72 |
73 impl Anm0 { | 73 impl Anm0 { |
74 /// Parse a slice of bytes into an `Anm0` struct. | 74 /// Parse a slice of bytes into an `Anm0` struct. |
75 pub fn from_slice(data: &[u8]) -> IResult<&[u8], Vec<Anm0>> { | 75 pub fn from_slice(data: &[u8]) -> IResult<&[u8], Vec<Anm0>> { |
76 parse_anm0(data) | 76 many0(parse_anm0)(data) |
77 } | 77 } |
78 | 78 |
79 /// TODO | 79 /// TODO |
80 pub fn inv_size(&self) -> (f32, f32) { | 80 pub fn inv_size(&self) -> (f32, f32) { |
81 let (x, y) = self.size; | 81 let (x, y) = self.size; |
165 29 => fn SetVisible(visible: u32), | 165 29 => fn SetVisible(visible: u32), |
166 30 => fn ScaleIn(sx: f32, sy: f32, duration: u32), | 166 30 => fn ScaleIn(sx: f32, sy: f32, duration: u32), |
167 31 => fn Todo(todo: u32), | 167 31 => fn Todo(todo: u32), |
168 } | 168 } |
169 | 169 |
170 fn parse_anm0(input: &[u8]) -> IResult<&[u8], Vec<Anm0>> { | 170 fn parse_anm0(input: &[u8]) -> IResult<&[u8], Anm0> { |
171 let mut list = vec![]; | 171 let (i, (num_sprites, num_scripts, _, width, height, format, _unknown1, |
172 let start_offset = 0; | 172 first_name_offset, _unused, second_name_offset, version, _unknown2, |
173 loop { | 173 _texture_offset, has_data, _next_offset, unknown3)) = |
174 let i = &input[start_offset..]; | 174 tuple((le_u32, le_u32, tag(b"\0\0\0\0"), le_u32, le_u32, le_u32, le_u32, le_u32, |
175 let (mut i, (num_sprites, num_scripts, _, width, height, format, _unknown1, | 175 le_u32, le_u32, le_u32, le_u32, le_u32, le_u32, le_u32, le_u32))(input)?; |
176 first_name_offset, _unused, second_name_offset, version, _unknown2, | 176 |
177 _texture_offset, has_data, _next_offset, unknown3)) = | 177 assert_eq!(version, 0); |
178 tuple((le_u32, le_u32, tag(b"\0\0\0\0"), le_u32, le_u32, le_u32, le_u32, le_u32, | 178 assert_eq!(unknown3, 0); |
179 le_u32, le_u32, le_u32, le_u32, le_u32, le_u32, le_u32, le_u32))(i)?; | 179 assert_eq!(has_data, 0); |
180 | 180 let num_sprites = num_sprites as usize; |
181 assert_eq!(version, 0); | 181 let num_scripts = num_scripts as usize; |
182 assert_eq!(unknown3, 0); | 182 |
183 assert_eq!(has_data, 0); | 183 let (i, sprite_offsets) = many_m_n(num_sprites, num_sprites, le_u32)(i)?; |
184 let num_sprites = num_sprites as usize; | 184 let (_, script_offsets) = many_m_n(num_scripts, num_scripts, tuple((le_u32, le_u32)))(i)?; |
185 let num_scripts = num_scripts as usize; | 185 |
186 | 186 let png_filename = if first_name_offset > 0 { |
187 let (i, sprite_offsets) = many_m_n(num_sprites, num_sprites, le_u32)(i)?; | 187 if input.len() < first_name_offset as usize { |
188 let (_, script_offsets) = many_m_n(num_scripts, num_scripts, tuple((le_u32, le_u32)))(i)?; | 188 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); |
189 | 189 } |
190 let png_filename = if first_name_offset > 0 { | 190 let i = &input[first_name_offset as usize..]; |
191 if input.len() < start_offset + first_name_offset as usize { | 191 let (_, name) = parse_name(i)?; |
192 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | 192 name |
193 } else { | |
194 String::new() | |
195 }; | |
196 | |
197 let alpha_filename = if second_name_offset > 0 { | |
198 if input.len() < second_name_offset as usize { | |
199 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | |
200 } | |
201 let i = &input[second_name_offset as usize..]; | |
202 let (_, name) = parse_name(i)?; | |
203 Some(name) | |
204 } else { | |
205 None | |
206 }; | |
207 | |
208 let mut sprites = vec![]; | |
209 let mut i = &input[..]; | |
210 for offset in sprite_offsets.into_iter().map(|x| x as usize) { | |
211 if input.len() < offset { | |
212 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | |
213 } | |
214 i = &input[offset..]; | |
215 let (_, sprite) = parse_sprite(i)?; | |
216 sprites.push(sprite); | |
217 } | |
218 | |
219 let mut scripts = HashMap::new(); | |
220 for (index, offset) in script_offsets.into_iter().map(|(index, offset)| (index as u8, offset as usize)) { | |
221 if input.len() < offset { | |
222 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | |
223 } | |
224 i = &input[offset..]; | |
225 let mut instruction_offsets = vec![]; | |
226 | |
227 let mut instructions = vec![]; | |
228 loop { | |
229 let tell = input.len() - i.len(); | |
230 instruction_offsets.push(tell - offset); | |
231 // TODO: maybe check against the size of parsed data? | |
232 let (i2, (time, opcode, _size)) = tuple((le_u16, le_u8, le_u8))(i)?; | |
233 let (i2, instr) = parse_instruction_args(i2, opcode)?; | |
234 instructions.push(Call { time, instr }); | |
235 i = i2; | |
236 if opcode == 0 { | |
237 break; | |
193 } | 238 } |
194 let i = &input[start_offset + first_name_offset as usize..]; | 239 } |
195 let (_, name) = parse_name(i)?; | 240 let mut interrupts = HashMap::new(); |
196 name | 241 let mut j = 0; |
197 } else { | 242 for Call { time: _, instr } in &mut instructions { |
198 String::new() | 243 match instr { |
199 }; | 244 Instruction::Jump(ref mut offset) => { |
200 | 245 let result = instruction_offsets.binary_search(&(*offset as usize)); |
201 let alpha_filename = if second_name_offset > 0 { | 246 match result { |
202 if input.len() < start_offset + second_name_offset as usize { | 247 Ok(ptr) => *offset = ptr as u32, |
203 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | 248 Err(ptr) => { |
204 } | 249 // XXX: use a more specific error instead. |
205 let i = &input[start_offset + second_name_offset as usize..]; | 250 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); |
206 let (_, name) = parse_name(i)?; | 251 //println!("Instruction offset not found for pointer: {}", ptr); |
207 Some(name) | |
208 } else { | |
209 None | |
210 }; | |
211 | |
212 let mut sprites = vec![]; | |
213 let mut i; | |
214 for offset in sprite_offsets.into_iter().map(|x| x as usize) { | |
215 if input.len() < start_offset + offset { | |
216 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | |
217 } | |
218 i = &input[start_offset + offset..]; | |
219 let (_, sprite) = parse_sprite(i)?; | |
220 sprites.push(sprite); | |
221 } | |
222 | |
223 let mut scripts = HashMap::new(); | |
224 for (index, offset) in script_offsets.into_iter().map(|(index, offset)| (index as u8, offset as usize)) { | |
225 if input.len() < start_offset + offset { | |
226 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | |
227 } | |
228 i = &input[start_offset + offset..]; | |
229 let mut instruction_offsets = vec![]; | |
230 | |
231 let mut instructions = vec![]; | |
232 loop { | |
233 let tell = input.len() - i.len(); | |
234 instruction_offsets.push(tell - (start_offset + offset)); | |
235 // TODO: maybe check against the size of parsed data? | |
236 let (i2, (time, opcode, _size)) = tuple((le_u16, le_u8, le_u8))(i)?; | |
237 let (i2, instr) = parse_instruction_args(i2, opcode)?; | |
238 instructions.push(Call { time, instr }); | |
239 i = i2; | |
240 if opcode == 0 { | |
241 break; | |
242 } | |
243 } | |
244 let mut interrupts = HashMap::new(); | |
245 let mut j = 0; | |
246 for Call { time: _, instr } in &mut instructions { | |
247 match instr { | |
248 Instruction::Jump(ref mut offset) => { | |
249 let result = instruction_offsets.binary_search(&(*offset as usize)); | |
250 match result { | |
251 Ok(ptr) => *offset = ptr as u32, | |
252 Err(ptr) => { | |
253 // XXX: use a more specific error instead. | |
254 return Err(nom::Err::Failure((input, nom::error::ErrorKind::Eof))); | |
255 //println!("Instruction offset not found for pointer: {}", ptr); | |
256 } | |
257 } | 252 } |
258 } | 253 } |
259 Instruction::InterruptLabel(interrupt) => { | |
260 interrupts.insert(*interrupt, j + 1); | |
261 } | |
262 _ => () | |
263 } | 254 } |
264 j += 1; | 255 Instruction::InterruptLabel(interrupt) => { |
256 interrupts.insert(*interrupt, j + 1); | |
257 } | |
258 _ => () | |
265 } | 259 } |
266 scripts.insert(index, Script { | 260 j += 1; |
267 instructions, | 261 } |
268 interrupts, | 262 scripts.insert(index, Script { |
269 }); | 263 instructions, |
270 } | 264 interrupts, |
271 | 265 }); |
272 let anm0 = Anm0 { | 266 } |
273 size: (width, height), | 267 |
274 format, | 268 let anm0 = Anm0 { |
275 png_filename, | 269 size: (width, height), |
276 alpha_filename, | 270 format, |
277 sprites, | 271 png_filename, |
278 scripts, | 272 alpha_filename, |
279 }; | 273 sprites, |
280 list.push(anm0); | 274 scripts, |
281 break; | 275 }; |
282 } | 276 Ok((i, anm0)) |
283 Ok((b"", list)) | |
284 } | 277 } |
285 | 278 |
286 #[cfg(test)] | 279 #[cfg(test)] |
287 mod tests { | 280 mod tests { |
288 use super::*; | 281 use super::*; |