Mercurial > touhou
comparison src/th06/anm0.rs @ 639:a8e0219162b6
Implement AnmRunner.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Thu, 04 Jul 2019 15:21:46 +0200 |
parents | a806f28e94fc |
children | 01849ffd0180 |
comparison
equal
deleted
inserted
replaced
638:a806f28e94fc | 639:a8e0219162b6 |
---|---|
1 //! ANM0 animation format support. | 1 //! ANM0 animation format support. |
2 | 2 |
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_f32}, | 6 number::complete::{le_u8, le_u16, le_u32, le_i32, le_f32}, |
7 }; | 7 }; |
8 use std::collections::HashMap; | 8 use std::collections::HashMap; |
9 | 9 |
10 /// Coordinates of a sprite into the image. | 10 /// Coordinates of a sprite into the image. |
11 #[derive(Debug, Clone)] | 11 #[derive(Debug, Clone)] |
12 pub struct Sprite { | 12 pub struct Sprite { |
13 index: u32, | 13 /// Index inside the anm0. |
14 x: f32, | 14 pub index: u32, |
15 y: f32, | 15 |
16 width: f32, | 16 /// X coordinate in the sprite sheet. |
17 height: f32, | 17 pub x: f32, |
18 | |
19 /// Y coordinate in the sprite sheet. | |
20 pub y: f32, | |
21 | |
22 /// Width of the sprite. | |
23 pub width: f32, | |
24 | |
25 /// Height of the sprite. | |
26 pub height: f32, | |
18 } | 27 } |
19 | 28 |
20 /// A single instruction, part of a `Script`. | 29 /// A single instruction, part of a `Script`. |
21 #[derive(Debug, Clone)] | 30 #[derive(Debug, Clone)] |
22 struct Instruction { | 31 pub struct Call { |
23 time: u16, | 32 /// Time at which this instruction will be called. |
24 opcode: u8, | 33 pub time: u16, |
25 args: Vec<Arg>, | 34 |
35 /// The instruction to call. | |
36 pub instr: Instruction, | |
26 } | 37 } |
27 | 38 |
28 /// Script driving an animation. | 39 /// Script driving an animation. |
29 #[derive(Debug, Clone)] | 40 #[derive(Debug, Clone)] |
30 pub struct Script { | 41 pub struct Script { |
31 instructions: Vec<Instruction>, | 42 /// List of instructions in this script. |
32 interrupts: HashMap<u32, u8> | 43 pub instructions: Vec<Call>, |
44 | |
45 /// List of interrupts in this script. | |
46 pub interrupts: HashMap<i32, u8> | |
33 } | 47 } |
34 | 48 |
35 /// Main struct of the ANM0 animation format. | 49 /// Main struct of the ANM0 animation format. |
36 #[derive(Debug, Clone)] | 50 #[derive(Debug, Clone)] |
37 pub struct Anm0 { | 51 pub struct Anm0 { |
87 width, | 101 width, |
88 height, | 102 height, |
89 })) | 103 })) |
90 } | 104 } |
91 | 105 |
92 #[derive(Debug, Clone, Copy)] | |
93 enum Arg { | |
94 U8(u8), | |
95 U32(u32), | |
96 F32(f32), | |
97 Ptr(u8), | |
98 } | |
99 | |
100 fn parse_u8_arg(i: &[u8]) -> IResult<&[u8], Arg> { | |
101 let (i, value) = le_u8(i)?; | |
102 Ok((i, Arg::U8(value))) | |
103 } | |
104 | |
105 fn parse_u32_arg(i: &[u8]) -> IResult<&[u8], Arg> { | |
106 let (i, value) = le_u32(i)?; | |
107 Ok((i, Arg::U32(value))) | |
108 } | |
109 | |
110 fn parse_f32_arg(i: &[u8]) -> IResult<&[u8], Arg> { | |
111 let (i, value) = le_f32(i)?; | |
112 Ok((i, Arg::F32(value))) | |
113 } | |
114 | |
115 macro_rules! declare_anm_instructions { | 106 macro_rules! declare_anm_instructions { |
116 ($($opcode:tt => fn $name:ident($($arg:ident: $arg_type:ty),*)),*,) => { | 107 ($($opcode:tt => fn $name:ident($($arg:ident: $arg_type:ident),*)),*,) => { |
117 fn parse_instruction_args(input: &[u8], opcode: u8) -> IResult<&[u8], Vec<Arg>> { | 108 /// Available instructions in an `Anm0`. |
118 let mut args = vec![]; | 109 #[allow(missing_docs)] |
110 #[derive(Debug, Clone, Copy)] | |
111 pub enum Instruction { | |
112 $( | |
113 $name($($arg_type),*) | |
114 ),* | |
115 } | |
116 | |
117 fn parse_instruction_args(input: &[u8], opcode: u8) -> IResult<&[u8], Instruction> { | |
119 let mut i = &input[..]; | 118 let mut i = &input[..]; |
120 match opcode { | 119 let instr = match opcode { |
121 $( | 120 $( |
122 $opcode => { | 121 $opcode => { |
123 $( | 122 $( |
124 let (i2, data) = match stringify!($arg_type) { | 123 let (i2, $arg) = concat_idents!(le_, $arg_type)(i)?; |
125 "u8" => parse_u8_arg(i), | |
126 "u32" => parse_u32_arg(i), | |
127 "f32" => parse_f32_arg(i), | |
128 "x8" => parse_u8_arg(i), | |
129 _ => unreachable!(), | |
130 }?; | |
131 i = i2; | 124 i = i2; |
132 if stringify!($arg_type) != "x8" { | |
133 args.push(data); | |
134 } | |
135 )* | 125 )* |
126 Instruction::$name($($arg),*) | |
136 } | 127 } |
137 )* | 128 )* |
138 _ => unreachable!() | 129 _ => unreachable!() |
139 } | 130 }; |
140 Ok((i, args)) | 131 Ok((i, instr)) |
141 } | 132 } |
142 }; | 133 }; |
143 } | 134 } |
144 | 135 |
145 declare_anm_instructions!{ | 136 declare_anm_instructions!{ |
146 0 => fn delete(), | 137 0 => fn Delete(), |
147 1 => fn set_sprite(sprite_number: u32), | 138 1 => fn LoadSprite(sprite_number: u32), |
148 2 => fn set_scale(sx: f32, sy: f32), | 139 2 => fn SetScale(sx: f32, sy: f32), |
149 3 => fn set_alpha(alpha: u32), | 140 3 => fn SetAlpha(alpha: u32), |
150 4 => fn set_color(red: u8, green: u8, blue: u8, XXX: x8), | 141 4 => fn SetColor(red: u8, green: u8, blue: u8/*, XXX: x8*/), |
151 5 => fn jump(instruction: u32), | 142 5 => fn Jump(instruction: u32), |
152 7 => fn toggle_mirrored(), | 143 7 => fn ToggleMirrored(), |
153 9 => fn set_3d_rotations(x: f32, y: f32, z: f32), | 144 9 => fn SetRotations3d(x: f32, y: f32, z: f32), |
154 10 => fn set_3d_rotations_speed(x: f32, y: f32, z: f32), | 145 10 => fn SetRotationsSpeed3d(x: f32, y: f32, z: f32), |
155 11 => fn set_scale_speed(sx: f32, sy: f32), | 146 11 => fn SetScaleSpeed(sx: f32, sy: f32), |
156 12 => fn fade(alpha: u32, duration: u32), | 147 12 => fn Fade(alpha: u32, duration: u32), |
157 13 => fn set_blendmode_add(), | 148 13 => fn SetBlendmodeAdd(), |
158 14 => fn set_blendmode_alphablend(), | 149 14 => fn SetBlendmodeAlphablend(), |
159 15 => fn keep_still(), | 150 15 => fn KeepStill(), |
160 16 => fn set_random_sprite(min_index: u32, amplitude: u32), | 151 16 => fn LoadRandomSprite(min_index: u32, amplitude: u32), |
161 17 => fn set_3d_translation(x: f32, y: f32, z: f32), | 152 17 => fn Move(x: f32, y: f32, z: f32), |
162 18 => fn move_to_linear(x: f32, y: f32, z: f32, duration: u32), | 153 18 => fn MoveToLinear(x: f32, y: f32, z: f32, duration: u32), |
163 19 => fn move_to_decel(x: f32, y: f32, z: f32, duration: u32), | 154 19 => fn MoveToDecel(x: f32, y: f32, z: f32, duration: u32), |
164 20 => fn move_to_accel(x: f32, y: f32, z: f32, duration: u32), | 155 20 => fn MoveToAccel(x: f32, y: f32, z: f32, duration: u32), |
165 21 => fn wait(), | 156 21 => fn Wait(), |
166 22 => fn interrupt_label(label: u32), | 157 22 => fn InterruptLabel(label: i32), |
167 23 => fn set_corner_relative_placement(), | 158 23 => fn SetCornerRelativePlacement(), |
168 24 => fn wait_ex(), | 159 24 => fn WaitEx(), |
169 25 => fn set_allow_offset(allow: u32), // TODO: better name | 160 25 => fn SetAllowOffset(allow: u32), // TODO: better name |
170 26 => fn set_automatic_orientation(automatic: u32), | 161 26 => fn SetAutomaticOrientation(automatic: u32), |
171 27 => fn shift_texture_x(dx: f32), | 162 27 => fn ShiftTextureX(dx: f32), |
172 28 => fn shift_texture_y(dy: f32), | 163 28 => fn ShiftTextureY(dy: f32), |
173 29 => fn set_visible(visible: u32), | 164 29 => fn SetVisible(visible: u32), |
174 30 => fn scale_in(sx: f32, sy: f32, duration: u32), | 165 30 => fn ScaleIn(sx: f32, sy: f32, duration: u32), |
175 31 => fn TODO(TODO: u32), | 166 31 => fn Todo(todo: u32), |
176 } | 167 } |
177 | 168 |
178 fn parse_anm0(input: &[u8]) -> IResult<&[u8], Vec<Anm0>> { | 169 fn parse_anm0(input: &[u8]) -> IResult<&[u8], Vec<Anm0>> { |
179 let mut list = vec![]; | 170 let mut list = vec![]; |
180 let start_offset = 0; | 171 let start_offset = 0; |
241 instruction_offsets.push(tell - (start_offset + offset)); | 232 instruction_offsets.push(tell - (start_offset + offset)); |
242 let (i2, time) = le_u16(i)?; | 233 let (i2, time) = le_u16(i)?; |
243 let (i2, opcode) = le_u8(i2)?; | 234 let (i2, opcode) = le_u8(i2)?; |
244 // TODO: maybe check against the size of parsed data? | 235 // TODO: maybe check against the size of parsed data? |
245 let (i2, _size) = le_u8(i2)?; | 236 let (i2, _size) = le_u8(i2)?; |
246 let (i2, args) = parse_instruction_args(i2, opcode)?; | 237 let (i2, instr) = parse_instruction_args(i2, opcode)?; |
247 instructions.push(Instruction { time, opcode, args }); | 238 instructions.push(Call { time, instr }); |
248 i = i2; | 239 i = i2; |
249 if opcode == 0 { | 240 if opcode == 0 { |
250 break; | 241 break; |
251 } | 242 } |
252 } | 243 } |
253 let mut interrupts = HashMap::new(); | 244 let mut interrupts = HashMap::new(); |
254 let mut j = 0; | 245 let mut j = 0; |
255 for Instruction { time: _, opcode, args } in &mut instructions { | 246 for Call { time: _, instr } in &mut instructions { |
256 match opcode { | 247 match instr { |
257 5 => { | 248 Instruction::Jump(ref mut offset) => { |
258 let offset = match args[0] { | 249 let result = instruction_offsets.binary_search(&(*offset as usize)); |
259 Arg::U32(offset) => offset as usize, | 250 match result { |
260 _ => panic!("Wrong argument type for jump!"), | 251 Ok(ptr) => *offset = ptr as u32, |
261 }; | |
262 let result = instruction_offsets.binary_search(&offset); | |
263 let ptr = match result { | |
264 Ok(ptr) => ptr as u8, | |
265 // TODO: make that a recoverable error instead. | 252 // TODO: make that a recoverable error instead. |
266 Err(ptr) => panic!("Instruction offset not found for pointer: {}", ptr), | 253 Err(ptr) => panic!("Instruction offset not found for pointer: {}", ptr), |
267 }; | 254 } |
268 args[0] = Arg::Ptr(ptr); | |
269 } | 255 } |
270 22 => { | 256 Instruction::InterruptLabel(interrupt) => { |
271 // TODO: maybe also remove this instruction, as the label is already | 257 interrupts.insert(*interrupt, j + 1); |
272 // present. | |
273 if let Arg::U32(interrupt) = args[0] { | |
274 interrupts.insert(interrupt, j + 1); | |
275 } else { | |
276 panic!("Wrong argument type for interrupt!"); | |
277 } | |
278 } | 258 } |
279 _ => () | 259 _ => () |
280 } | 260 } |
281 j += 1; | 261 j += 1; |
282 } | 262 } |