1use crate::avm1::{opcode::OpCode, types::*};
2use crate::error::{Error, Result};
3use crate::extensions::ReadSwfExt;
4use std::num::NonZeroU8;
5
6pub struct Reader<'a> {
7 input: &'a [u8],
8 #[allow(dead_code)]
9 version: u8,
10}
11
12impl<'a> ReadSwfExt<'a> for Reader<'a> {
13 #[inline(always)]
14 fn as_mut_slice(&mut self) -> &mut &'a [u8] {
15 &mut self.input
16 }
17
18 #[inline(always)]
19 fn as_slice(&self) -> &'a [u8] {
20 self.input
21 }
22}
23
24impl<'a> Reader<'a> {
25 #[inline]
26 pub const fn new(input: &'a [u8], version: u8) -> Self {
27 Self { input, version }
28 }
29
30 #[inline]
31 pub fn seek(&mut self, data: &'a [u8], jump_offset: i16) {
32 ReadSwfExt::seek(self, data, jump_offset as isize)
33 }
34
35 #[inline]
36 pub const fn get_ref(&self) -> &'a [u8] {
37 self.input
38 }
39
40 #[inline]
41 pub fn get_mut(&mut self) -> &mut &'a [u8] {
42 &mut self.input
43 }
44
45 #[inline]
46 fn read_f64_me(&mut self) -> Result<f64> {
47 Ok(f64::from_bits(self.read_u64()?.rotate_left(32)))
50 }
51
52 #[inline]
53 pub fn read_action(&mut self) -> Result<Action<'a>> {
54 let (opcode, mut length) = self.read_opcode_and_length()?;
55 let start = self.input;
56
57 let action = self.read_op(opcode, &mut length);
58
59 if let Err(e) = action {
60 return Err(Error::avm1_parse_error_with_source(opcode, e));
61 }
62
63 let end_pos = (start.as_ptr() as usize + length) as *const u8;
65 if self.input.as_ptr() != end_pos {
66 self.input = &start[length.min(start.len())..];
69 log::warn!("Length mismatch in AVM1 action: {}", OpCode::format(opcode));
70 }
71
72 action
73 }
74
75 pub fn read_opcode_and_length(&mut self) -> Result<(u8, usize)> {
76 let opcode = self.read_u8()?;
77 let length = if opcode >= 0x80 {
78 self.read_u16()?.into()
79 } else {
80 0
81 };
82 Ok((opcode, length))
83 }
84
85 #[inline]
91 fn read_op(&mut self, opcode: u8, length: &mut usize) -> Result<Action<'a>> {
92 let action = if let Some(op) = OpCode::from_u8(opcode) {
93 match op {
94 OpCode::Add => Action::Add,
95 OpCode::Add2 => Action::Add2,
96 OpCode::And => Action::And,
97 OpCode::AsciiToChar => Action::AsciiToChar,
98 OpCode::BitAnd => Action::BitAnd,
99 OpCode::BitLShift => Action::BitLShift,
100 OpCode::BitOr => Action::BitOr,
101 OpCode::BitRShift => Action::BitRShift,
102 OpCode::BitURShift => Action::BitURShift,
103 OpCode::BitXor => Action::BitXor,
104 OpCode::Call => Action::Call,
105 OpCode::CallFunction => Action::CallFunction,
106 OpCode::CallMethod => Action::CallMethod,
107 OpCode::CastOp => Action::CastOp,
108 OpCode::CharToAscii => Action::CharToAscii,
109 OpCode::CloneSprite => Action::CloneSprite,
110 OpCode::ConstantPool => Action::ConstantPool(self.read_constant_pool()?),
111 OpCode::Decrement => Action::Decrement,
112 OpCode::DefineFunction => {
113 Action::DefineFunction(self.read_define_function(length)?)
114 }
115 OpCode::DefineFunction2 => {
116 Action::DefineFunction2(self.read_define_function_2(length)?)
117 }
118 OpCode::DefineLocal => Action::DefineLocal,
119 OpCode::DefineLocal2 => Action::DefineLocal2,
120 OpCode::Delete => Action::Delete,
121 OpCode::Delete2 => Action::Delete2,
122 OpCode::Divide => Action::Divide,
123 OpCode::EndDrag => Action::EndDrag,
124 OpCode::Enumerate => Action::Enumerate,
125 OpCode::Enumerate2 => Action::Enumerate2,
126 OpCode::End => Action::End,
127 OpCode::Equals => Action::Equals,
128 OpCode::Equals2 => Action::Equals2,
129 OpCode::Extends => Action::Extends,
130 OpCode::GetMember => Action::GetMember,
131 OpCode::GetProperty => Action::GetProperty,
132 OpCode::GetTime => Action::GetTime,
133 OpCode::GetUrl => Action::GetUrl(self.read_get_url()?),
134 OpCode::GetUrl2 => Action::GetUrl2(self.read_get_url_2()?),
135 OpCode::GetVariable => Action::GetVariable,
136 OpCode::GotoFrame => Action::GotoFrame(self.read_goto_frame()?),
137 OpCode::GotoFrame2 => Action::GotoFrame2(self.read_goto_frame_2()?),
138 OpCode::GotoLabel => Action::GotoLabel(self.read_goto_label()?),
139 OpCode::Greater => Action::Greater,
140 OpCode::If => Action::If(self.read_if()?),
141 OpCode::ImplementsOp => Action::ImplementsOp,
142 OpCode::Increment => Action::Increment,
143 OpCode::InitArray => Action::InitArray,
144 OpCode::InitObject => Action::InitObject,
145 OpCode::InstanceOf => Action::InstanceOf,
146 OpCode::Jump => Action::Jump(self.read_jump()?),
147 OpCode::Less => Action::Less,
148 OpCode::Less2 => Action::Less2,
149 OpCode::MBAsciiToChar => Action::MBAsciiToChar,
150 OpCode::MBCharToAscii => Action::MBCharToAscii,
151 OpCode::MBStringExtract => Action::MBStringExtract,
152 OpCode::MBStringLength => Action::MBStringLength,
153 OpCode::Modulo => Action::Modulo,
154 OpCode::Multiply => Action::Multiply,
155 OpCode::NewMethod => Action::NewMethod,
156 OpCode::NewObject => Action::NewObject,
157 OpCode::NextFrame => Action::NextFrame,
158 OpCode::Not => Action::Not,
159 OpCode::Or => Action::Or,
160 OpCode::Play => Action::Play,
161 OpCode::Pop => Action::Pop,
162 OpCode::PreviousFrame => Action::PreviousFrame,
163 OpCode::Push => Action::Push(self.read_push(*length)?),
164 OpCode::PushDuplicate => Action::PushDuplicate,
165 OpCode::RandomNumber => Action::RandomNumber,
166 OpCode::RemoveSprite => Action::RemoveSprite,
167 OpCode::Return => Action::Return,
168 OpCode::SetMember => Action::SetMember,
169 OpCode::SetProperty => Action::SetProperty,
170 OpCode::SetTarget => Action::SetTarget(self.read_set_target()?),
171 OpCode::SetTarget2 => Action::SetTarget2,
172 OpCode::SetVariable => Action::SetVariable,
173 OpCode::StackSwap => Action::StackSwap,
174 OpCode::StartDrag => Action::StartDrag,
175 OpCode::Stop => Action::Stop,
176 OpCode::StopSounds => Action::StopSounds,
177 OpCode::StoreRegister => Action::StoreRegister(self.read_store_register()?),
178 OpCode::StrictEquals => Action::StrictEquals,
179 OpCode::StringAdd => Action::StringAdd,
180 OpCode::StringEquals => Action::StringEquals,
181 OpCode::StringExtract => Action::StringExtract,
182 OpCode::StringGreater => Action::StringGreater,
183 OpCode::StringLength => Action::StringLength,
184 OpCode::StringLess => Action::StringLess,
185 OpCode::Subtract => Action::Subtract,
186 OpCode::TargetPath => Action::TargetPath,
187 OpCode::Throw => Action::Throw,
188 OpCode::ToggleQuality => Action::ToggleQuality,
189 OpCode::ToInteger => Action::ToInteger,
190 OpCode::ToNumber => Action::ToNumber,
191 OpCode::ToString => Action::ToString,
192 OpCode::Trace => Action::Trace,
193 OpCode::Try => Action::Try(self.read_try(length)?),
194 OpCode::TypeOf => Action::TypeOf,
195 OpCode::WaitForFrame => Action::WaitForFrame(self.read_wait_for_frame()?),
196 OpCode::WaitForFrame2 => Action::WaitForFrame2(self.read_wait_for_frame_2()?),
197 OpCode::With => Action::With(self.read_with(length)?),
198 }
199 } else {
200 Action::Unknown(self.read_unknown_action(opcode, *length)?)
201 };
202
203 Ok(action)
204 }
205
206 fn read_constant_pool(&mut self) -> Result<ConstantPool<'a>> {
207 let count = self.read_u16()?;
208 let mut strings = Vec::with_capacity(count as usize);
209 for _ in 0..count {
210 let string = self.read_str();
211 if let Ok(string) = string {
212 strings.push(string);
213 }
214 }
215 Ok(ConstantPool { strings })
216 }
217
218 fn read_define_function(&mut self, action_length: &mut usize) -> Result<DefineFunction<'a>> {
219 let name = self.read_str()?;
220 let num_params = self.read_u16()?;
221 let mut params = Vec::with_capacity(num_params as usize);
222 for _ in 0..num_params {
223 params.push(self.read_str()?);
224 }
225 let code_length: usize = (self.read_u16()?).into();
227 *action_length += code_length;
228 Ok(DefineFunction {
229 name,
230 params,
231 actions: self.read_slice(code_length)?,
232 })
233 }
234
235 fn read_define_function_2(&mut self, action_length: &mut usize) -> Result<DefineFunction2<'a>> {
236 let name = self.read_str()?;
237 let num_params = self.read_u16()?;
238 let register_count = self.read_u8()?;
239 let flags = FunctionFlags::from_bits_truncate(self.read_u16()?);
240 let mut params = Vec::with_capacity(num_params as usize);
241 for _ in 0..num_params {
242 params.push(FunctionParam {
243 register_index: NonZeroU8::new(self.read_u8()?),
244 name: self.read_str()?,
245 });
246 }
247 let code_length: usize = (self.read_u16()?).into();
249 *action_length += code_length;
250 Ok(DefineFunction2 {
251 name,
252 params,
253 register_count,
254 flags,
255 actions: self.read_slice(code_length)?,
256 })
257 }
258
259 fn read_get_url(&mut self) -> Result<GetUrl<'a>> {
260 Ok(GetUrl {
261 url: self.read_str()?,
262 target: self.read_str()?,
263 })
264 }
265
266 fn read_get_url_2(&mut self) -> Result<GetUrl2> {
267 let mut flags = GetUrlFlags::from_bits_truncate(self.read_u8()?);
268 if flags.contains(GetUrlFlags::METHOD_MASK) {
269 log::warn!("Invalid SendVarsMethod in GetUrl2, defaulting to None");
271 flags.set(GetUrlFlags::METHOD_MASK, false);
272 }
273 Ok(GetUrl2(flags))
274 }
275
276 fn read_goto_frame(&mut self) -> Result<GotoFrame> {
277 Ok(GotoFrame {
278 frame: self.read_u16()?,
279 })
280 }
281
282 fn read_goto_frame_2(&mut self) -> Result<GotoFrame2> {
283 let flags = self.read_u8()?;
284 Ok(GotoFrame2 {
285 set_playing: flags & 0b1 != 0,
286 scene_offset: if flags & 0b10 != 0 {
287 self.read_u16()?
288 } else {
289 0
290 },
291 })
292 }
293
294 fn read_goto_label(&mut self) -> Result<GotoLabel<'a>> {
295 Ok(GotoLabel {
296 label: self.read_str()?,
297 })
298 }
299
300 fn read_if(&mut self) -> Result<If> {
301 Ok(If {
302 offset: self.read_i16()?,
303 })
304 }
305
306 fn read_jump(&mut self) -> Result<Jump> {
307 Ok(Jump {
308 offset: self.read_i16()?,
309 })
310 }
311
312 fn read_push(&mut self, length: usize) -> Result<Push<'a>> {
313 let end_pos = (self.input.as_ptr() as usize + length) as *const u8;
315 let mut values = Vec::with_capacity(4);
316 while self.input.as_ptr() < end_pos {
317 let value = match self.read_u8()? {
318 0 => Value::Str(self.read_str()?),
319 1 => Value::Float(self.read_f32()?),
320 2 => Value::Null,
321 3 => Value::Undefined,
322 4 => Value::Register(self.read_u8()?),
323 5 => Value::Bool(self.read_u8()? != 0),
324 6 => Value::Double(self.read_f64_me()?),
325 7 => Value::Int(self.read_i32()?),
326 8 => Value::ConstantPool(self.read_u8()?.into()),
327 9 => Value::ConstantPool(self.read_u16()?),
328 type_ => {
329 log::warn!("Invalid value type in ActionPush: {}", type_);
335 continue;
336 }
337 };
338 values.push(value);
339 }
340 Ok(Push { values })
341 }
342
343 fn read_set_target(&mut self) -> Result<SetTarget<'a>> {
344 Ok(SetTarget {
345 target: self.read_str()?,
346 })
347 }
348
349 fn read_store_register(&mut self) -> Result<StoreRegister> {
350 Ok(StoreRegister {
351 register: self.read_u8()?,
352 })
353 }
354
355 fn read_try(&mut self, length: &mut usize) -> Result<Try<'a>> {
356 if *length < 7 {
358 return Ok(Try {
359 try_body: &[],
360 catch_body: None,
361 finally_body: None,
362 });
363 }
364
365 let flags = TryFlags::from_bits_truncate(self.read_u8()?);
366 let try_size: usize = self.read_u16()?.into();
367 let catch_size: usize = self.read_u16()?.into();
368 let finally_size: usize = self.read_u16()?.into();
369 *length += try_size + catch_size + finally_size;
370 let catch_var = if flags.contains(TryFlags::CATCH_IN_REGISTER) {
371 CatchVar::Register(self.read_u8()?)
372 } else {
373 CatchVar::Var(self.read_str()?)
374 };
375 let try_body = self.read_slice(try_size)?;
376 let catch_body = self.read_slice(catch_size)?;
377 let finally_body = self.read_slice(finally_size)?;
378 Ok(Try {
379 try_body,
380 catch_body: if flags.contains(TryFlags::CATCH_BLOCK) {
381 Some((catch_var, catch_body))
382 } else {
383 None
384 },
385 finally_body: if flags.contains(TryFlags::FINALLY_BLOCK) {
386 Some(finally_body)
387 } else {
388 None
389 },
390 })
391 }
392
393 fn read_wait_for_frame(&mut self) -> Result<WaitForFrame> {
394 Ok(WaitForFrame {
395 frame: self.read_u16()?,
396 num_actions_to_skip: self.read_u8()?,
397 })
398 }
399
400 fn read_wait_for_frame_2(&mut self) -> Result<WaitForFrame2> {
401 Ok(WaitForFrame2 {
402 num_actions_to_skip: self.read_u8()?,
403 })
404 }
405
406 fn read_with(&mut self, action_length: &mut usize) -> Result<With<'a>> {
407 let code_length: usize = (self.read_u16()?).into();
408 *action_length += code_length;
409 Ok(With {
410 actions: self.read_slice(code_length)?,
411 })
412 }
413
414 fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result<Unknown<'a>> {
415 Ok(Unknown {
416 opcode,
417 data: self.read_slice(length)?,
418 })
419 }
420}
421
422#[cfg(test)]
423pub mod tests {
424 use super::*;
425 use crate::string::{SwfStr, WINDOWS_1252};
426 use crate::test_data;
427
428 #[test]
429 fn read_action() {
430 for (swf_version, expected_action, action_bytes) in test_data::avm1_tests() {
431 let mut reader = Reader::new(&action_bytes[..], swf_version);
432 let parsed_action = reader.read_action().unwrap();
433 assert_eq!(
434 parsed_action, expected_action,
435 "Incorrectly parsed action.\nRead:\n{parsed_action:?}\n\nExpected:\n{expected_action:?}",
436 );
437 }
438 }
439
440 #[test]
442 fn read_parse_error() {
443 let action_bytes = [0xff, 0xff, 0xff, 0x00, 0x00];
444 let mut reader = Reader::new(&action_bytes[..], 5);
445 match reader.read_action() {
446 Err(crate::error::Error::Avm1ParseError { .. }) => (),
447 result => {
448 panic!("Expected Avm1ParseError, got {result:?}");
449 }
450 }
451 }
452
453 #[test]
454 fn read_define_function() {
455 let action_bytes = [
457 0x9b, 0x08, 0x00, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x96, 0x06, 0x00,
458 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26, 0x00,
459 ];
460 let mut reader = Reader::new(&action_bytes[..], 5);
461 let action = reader.read_action().unwrap();
462 assert_eq!(
463 action,
464 Action::DefineFunction(DefineFunction {
465 name: SwfStr::from_str_with_encoding("foo", WINDOWS_1252).unwrap(),
466 params: vec![],
467 actions: &[0x96, 0x06, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26],
468 })
469 );
470
471 if let Action::DefineFunction(DefineFunction { actions, .. }) = action {
472 let mut reader = Reader::new(actions, 5);
473 let action = reader.read_action().unwrap();
474 assert_eq!(
475 action,
476 Action::Push(Push {
477 values: vec![Value::Str(
478 SwfStr::from_str_with_encoding("test", WINDOWS_1252).unwrap()
479 )]
480 })
481 );
482 }
483 }
484
485 #[test]
486 fn read_push() {
487 let action_bytes = [
490 OpCode::Push as u8,
491 3,
492 0,
493 2, 10, 3, 3, ];
498 let mut reader = Reader::new(&action_bytes, 5);
499 let action = reader.read_action().unwrap();
500 assert_eq!(
501 action,
502 Action::Push(Push {
503 values: vec![Value::Null, Value::Undefined]
504 })
505 );
506 }
507
508 #[test]
509 fn read_length_mismatch() {
510 let action_bytes = [
511 OpCode::ConstantPool as u8,
512 5,
513 0,
514 1,
515 0,
516 b'a',
517 0,
518 OpCode::Add as u8,
519 OpCode::Subtract as u8,
520 ];
521 let mut reader = Reader::new(&action_bytes[..], 5);
522
523 let action = reader.read_action().unwrap();
524 assert_eq!(
525 action,
526 Action::ConstantPool(ConstantPool {
527 strings: vec!["a".into()]
528 })
529 );
530
531 let action = reader.read_action().unwrap();
532 assert_eq!(action, Action::Subtract);
533 }
534}