use crate::internal::as2_pcode::{Action, Actions, CatchTarget, FunctionParam, PushValue};
use crate::program::{CompiledProgram, SwfOptions};
use byteorder::{LittleEndian, WriteBytesExt};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Result, Write};
use swf::write::SwfWriteExt;
use swf::{CharacterId, ExportedAsset, FileAttributes, Fixed8, Sprite, SwfStr, Tag, Twips};
pub(crate) fn pcode_to_swf(
program: &CompiledProgram,
swf_options: &SwfOptions,
) -> swf::error::Result<Vec<u8>> {
let mut initializers = vec![];
for actions in &program.custom_pcodes {
let mut result = ActionEncoder::new();
result.write_actions(actions)?;
result.write_small_action(OpCode::End)?;
result.patch_labels()?;
initializers.push(result.output);
}
if let Some(initializer) = &program.initializer {
let mut result = ActionEncoder::new();
result.write_actions(initializer)?;
result.write_small_action(OpCode::End)?;
result.patch_labels()?;
initializers.push(result.output);
}
let mut modules = vec![];
for (name, actions) in &program.extra_modules {
let mut result = ActionEncoder::new();
result.write_actions(actions)?;
result.write_small_action(OpCode::End)?;
result.patch_labels()?;
modules.push((format!("__Packages.{}", name), result.output));
}
let mut file_attributes = FileAttributes::empty();
file_attributes.set(
FileAttributes::USE_NETWORK_SANDBOX,
swf_options.use_network_sandbox,
);
let mut tags = vec![
Tag::FileAttributes(file_attributes),
Tag::SetBackgroundColor(swf::Color::WHITE),
];
for initializer in &initializers {
tags.push(Tag::DoAction(initializer))
}
if (modules.len() + 1) > CharacterId::MAX as usize {
return Err(swf::error::Error::from(io::Error::from(
EncoderError::TooManyModules,
)));
}
for (i, (name, module)) in modules.iter().enumerate() {
let id = (i + 1) as CharacterId;
tags.push(Tag::DefineSprite(Sprite {
id,
num_frames: 0,
tags: vec![Tag::End],
}));
tags.push(Tag::ExportAssets(vec![ExportedAsset {
id,
name: SwfStr::from_utf8_str(name),
}]));
tags.push(Tag::DoInitAction {
id,
action_data: module,
})
}
tags.push(Tag::ShowFrame);
let mut swf = Vec::new();
swf::write::write_swf(
&swf::Header {
compression: swf::Compression::None,
version: program.compile_options.swf_version,
stage_size: swf::Rectangle {
x_min: Twips::from_pixels(swf_options.stage_x_min),
x_max: Twips::from_pixels(swf_options.stage_x_max),
y_min: Twips::from_pixels(swf_options.stage_y_min),
y_max: Twips::from_pixels(swf_options.stage_y_max),
},
frame_rate: Fixed8::from_f32(swf_options.frame_rate),
num_frames: 1,
},
&tags,
&mut swf,
)?;
Ok(swf)
}
struct PendingLabelWrite<'a> {
label: &'a str,
offset_pos: usize,
}
struct ActionEncoder<'a> {
output: Vec<u8>,
pending_label_writes: Vec<PendingLabelWrite<'a>>,
label_positions: HashMap<&'a str, usize>,
}
impl SwfWriteExt for ActionEncoder<'_> {
#[inline]
fn write_u8(&mut self, n: u8) -> Result<()> {
self.output.write_u8(n)
}
#[inline]
fn write_u16(&mut self, n: u16) -> Result<()> {
self.output.write_u16::<LittleEndian>(n)
}
#[inline]
fn write_u32(&mut self, n: u32) -> Result<()> {
self.output.write_u32::<LittleEndian>(n)
}
#[inline]
fn write_u64(&mut self, n: u64) -> Result<()> {
self.output.write_u64::<LittleEndian>(n)
}
#[inline]
fn write_i8(&mut self, n: i8) -> Result<()> {
self.output.write_i8(n)
}
#[inline]
fn write_i16(&mut self, n: i16) -> Result<()> {
self.output.write_i16::<LittleEndian>(n)
}
#[inline]
fn write_i32(&mut self, n: i32) -> Result<()> {
self.output.write_i32::<LittleEndian>(n)
}
#[inline]
fn write_f32(&mut self, n: f32) -> Result<()> {
self.output.write_f32::<LittleEndian>(n)
}
#[inline]
fn write_f64(&mut self, n: f64) -> Result<()> {
self.output.write_f64::<LittleEndian>(n)
}
fn write_string(&mut self, s: &'_ SwfStr) -> Result<()> {
let bytes = s.as_bytes();
if bytes.contains(&0) {
return Err(EncoderError::NulByteInString.into());
}
self.output.write_all(bytes)?;
self.write_u8(0)
}
}
#[derive(Debug)]
enum EncoderError {
TooManyModules,
NulByteInString,
JumpOffsetOverflow(isize),
ActionLenOverflow(usize),
BlockLenOverflow(usize),
}
impl fmt::Display for EncoderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TooManyModules => write!(f, "too many modules"),
Self::NulByteInString => write!(f, "invalid NUL byte in string"),
Self::JumpOffsetOverflow(off) => {
write!(f, "jump offset ({off} bytes) cannot fit in i16")
}
Self::ActionLenOverflow(size) => {
write!(f, "action length ({size} bytes) cannot fit in u16")
}
Self::BlockLenOverflow(size) => {
write!(f, "block length ({size} bytes) cannot fit in u16")
}
}
}
}
impl std::error::Error for EncoderError {}
impl From<EncoderError> for io::Error {
fn from(e: EncoderError) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
impl<'a> ActionEncoder<'a> {
fn new() -> Self {
Self {
output: Vec::new(),
pending_label_writes: Vec::new(),
label_positions: HashMap::new(),
}
}
fn patch_labels(&mut self) -> Result<()> {
for write in &self.pending_label_writes {
if let Some(position) = self.label_positions.get(write.label) {
let offset = *position as isize - (write.offset_pos + 2) as isize;
let offset =
i16::try_from(offset).map_err(|_| EncoderError::JumpOffsetOverflow(offset))?;
self.output[write.offset_pos..write.offset_pos + 2]
.copy_from_slice(&offset.to_le_bytes());
}
}
Ok(())
}
#[inline]
fn write_f64_me(&mut self, n: f64) -> Result<()> {
self.write_u64(n.to_bits().rotate_left(32))
}
fn write_actions(&mut self, actions: &'a Actions) -> Result<()> {
let mut labels_per_pos: IndexMap<usize, Vec<&str>> = IndexMap::new();
for (label, &pos) in actions.label_positions.iter() {
labels_per_pos.entry(pos).or_default().push(label);
}
for (i, action) in actions.actions.iter().enumerate() {
if let Some(labels) = labels_per_pos.get(&i) {
for label in labels {
self.label_positions.insert(label, self.output.len());
}
}
self.write_action(action)?;
}
if let Some(labels) = labels_per_pos.get(&actions.actions.len()) {
for label in labels {
self.label_positions.insert(label, self.output.len());
}
}
Ok(())
}
fn write_action(&mut self, action: &'a Action) -> Result<()> {
match action {
Action::Add => self.write_small_action(OpCode::Add),
Action::Add2 => self.write_small_action(OpCode::Add2),
Action::And => self.write_small_action(OpCode::And),
Action::AsciiToChar => self.write_small_action(OpCode::AsciiToChar),
Action::BitAnd => self.write_small_action(OpCode::BitAnd),
Action::BitLShift => self.write_small_action(OpCode::BitLShift),
Action::BitOr => self.write_small_action(OpCode::BitOr),
Action::BitRShift => self.write_small_action(OpCode::BitRShift),
Action::BitURShift => self.write_small_action(OpCode::BitURShift),
Action::BitXor => self.write_small_action(OpCode::BitXor),
Action::Call => self.write_small_action(OpCode::Call),
Action::CallFunction => self.write_small_action(OpCode::CallFunction),
Action::CallMethod => self.write_small_action(OpCode::CallMethod),
Action::CastOp => self.write_small_action(OpCode::CastOp),
Action::CharToAscii => self.write_small_action(OpCode::CharToAscii),
Action::CloneSprite => self.write_small_action(OpCode::CloneSprite),
Action::ConstantPool(action) => self.write_constant_pool(action),
Action::Decrement => self.write_small_action(OpCode::Decrement),
Action::DefineFunction {
name,
params,
actions,
} => self.write_define_function(name, params, actions),
Action::DefineFunction2 {
name,
params,
actions,
register_count,
preload_this,
suppress_this,
preload_arguments,
suppress_arguments,
preload_super,
suppress_super,
preload_root,
preload_parent,
preload_global,
} => self.write_define_function_2(
name,
params,
actions,
*register_count,
*preload_this,
*suppress_this,
*preload_arguments,
*suppress_arguments,
*preload_super,
*suppress_super,
*preload_root,
*preload_parent,
*preload_global,
),
Action::DefineLocal => self.write_small_action(OpCode::DefineLocal),
Action::DefineLocal2 => self.write_small_action(OpCode::DefineLocal2),
Action::Divide => self.write_small_action(OpCode::Divide),
Action::Delete => self.write_small_action(OpCode::Delete),
Action::Delete2 => self.write_small_action(OpCode::Delete2),
Action::End => self.write_small_action(OpCode::End),
Action::EndDrag => self.write_small_action(OpCode::EndDrag),
Action::Enumerate => self.write_small_action(OpCode::Enumerate),
Action::Enumerate2 => self.write_small_action(OpCode::Enumerate2),
Action::Equals => self.write_small_action(OpCode::Equals),
Action::Equals2 => self.write_small_action(OpCode::Equals2),
Action::Extends => self.write_small_action(OpCode::Extends),
Action::GetMember => self.write_small_action(OpCode::GetMember),
Action::GetProperty => self.write_small_action(OpCode::GetProperty),
Action::GetTime => self.write_small_action(OpCode::GetTime),
Action::GetUrl { url, target } => self.write_get_url(url, target),
Action::GetUrl2 {
load_variables,
load_target,
method,
} => self.write_get_url_2(*load_variables, *load_target, *method),
Action::GetVariable => self.write_small_action(OpCode::GetVariable),
Action::GotoFrame(frame) => self.write_goto_frame(*frame),
Action::GotoFrame2 { scene_bias, play } => self.write_goto_frame_2(*scene_bias, *play),
Action::GotoLabel(label) => self.write_goto_label(label),
Action::Greater => self.write_small_action(OpCode::Greater),
Action::If(action) => self.write_if(action),
Action::ImplementsOp => self.write_small_action(OpCode::ImplementsOp),
Action::Increment => self.write_small_action(OpCode::Increment),
Action::InitArray => self.write_small_action(OpCode::InitArray),
Action::InitObject => self.write_small_action(OpCode::InitObject),
Action::InstanceOf => self.write_small_action(OpCode::InstanceOf),
Action::Jump(action) => self.write_jump(action),
Action::Less => self.write_small_action(OpCode::Less),
Action::Less2 => self.write_small_action(OpCode::Less2),
Action::MBAsciiToChar => self.write_small_action(OpCode::MBAsciiToChar),
Action::MBCharToAscii => self.write_small_action(OpCode::MBCharToAscii),
Action::MBStringExtract => self.write_small_action(OpCode::MBStringExtract),
Action::MBStringLength => self.write_small_action(OpCode::MBStringLength),
Action::Modulo => self.write_small_action(OpCode::Modulo),
Action::Multiply => self.write_small_action(OpCode::Multiply),
Action::NewMethod => self.write_small_action(OpCode::NewMethod),
Action::NewObject => self.write_small_action(OpCode::NewObject),
Action::NextFrame => self.write_small_action(OpCode::NextFrame),
Action::Not => self.write_small_action(OpCode::Not),
Action::Or => self.write_small_action(OpCode::Or),
Action::Play => self.write_small_action(OpCode::Play),
Action::Pop => self.write_small_action(OpCode::Pop),
Action::PrevFrame => self.write_small_action(OpCode::PreviousFrame),
Action::Push(action) => self.write_push(action),
Action::PushDuplicate => self.write_small_action(OpCode::PushDuplicate),
Action::RandomNumber => self.write_small_action(OpCode::RandomNumber),
Action::RemoveSprite => self.write_small_action(OpCode::RemoveSprite),
Action::Return => self.write_small_action(OpCode::Return),
Action::SetMember => self.write_small_action(OpCode::SetMember),
Action::SetProperty => self.write_small_action(OpCode::SetProperty),
Action::SetTarget(action) => self.write_set_target(action),
Action::SetTarget2 => self.write_small_action(OpCode::SetTarget2),
Action::SetVariable => self.write_small_action(OpCode::SetVariable),
Action::StackSwap => self.write_small_action(OpCode::StackSwap),
Action::StartDrag => self.write_small_action(OpCode::StartDrag),
Action::Stop => self.write_small_action(OpCode::Stop),
Action::StopSounds => self.write_small_action(OpCode::StopSounds),
Action::StoreRegister(action) => self.write_store_register(*action),
Action::StrictEquals => self.write_small_action(OpCode::StrictEquals),
Action::StringAdd => self.write_small_action(OpCode::StringAdd),
Action::StringEquals => self.write_small_action(OpCode::StringEquals),
Action::StringExtract => self.write_small_action(OpCode::StringExtract),
Action::StringGreater => self.write_small_action(OpCode::StringGreater),
Action::StringLength => self.write_small_action(OpCode::StringLength),
Action::StringLess => self.write_small_action(OpCode::StringLess),
Action::Subtract => self.write_small_action(OpCode::Subtract),
Action::TargetPath => self.write_small_action(OpCode::TargetPath),
Action::Throw => self.write_small_action(OpCode::Throw),
Action::ToggleQuality => self.write_small_action(OpCode::ToggleQuality),
Action::ToInteger => self.write_small_action(OpCode::ToInteger),
Action::ToNumber => self.write_small_action(OpCode::ToNumber),
Action::ToString => self.write_small_action(OpCode::ToString),
Action::Trace => self.write_small_action(OpCode::Trace),
Action::Try {
try_body,
catch_body,
finally_body,
} => self.write_try(try_body, catch_body, finally_body),
Action::TypeOf => self.write_small_action(OpCode::TypeOf),
Action::WaitForFrame { frame, skip_count } => {
self.write_wait_for_frame(*frame, *skip_count)
}
Action::WaitForFrame2 { skip_count } => self.write_wait_for_frame_2(*skip_count),
Action::With(actions) => self.write_with(actions),
}
}
fn write_action_header(&mut self, opcode: OpCode, length: usize) -> Result<()> {
self.write_opcode_and_length(opcode as u8, length)
}
fn write_opcode_and_length(&mut self, opcode: u8, length: usize) -> Result<()> {
self.write_u8(opcode)?;
assert!(
opcode >= 0x80 || length == 0,
"Opcodes less than 0x80 must have length 0"
);
if opcode >= 0x80 {
let len = u16::try_from(length).map_err(|_| EncoderError::ActionLenOverflow(length))?;
self.write_u16(len)?;
}
Ok(())
}
fn write_small_action(&mut self, opcode: OpCode) -> Result<()> {
self.write_action_header(opcode, 0)
}
fn write_goto_frame(&mut self, frame: u16) -> Result<()> {
self.write_action_header(OpCode::GotoFrame, 2)?;
self.write_u16(frame)?;
Ok(())
}
fn write_goto_frame_2(&mut self, scene_bias: u16, play: bool) -> Result<()> {
if scene_bias != 0 {
self.write_action_header(OpCode::GotoFrame2, 3)?;
self.write_u8(if play { 0b11 } else { 0b10 })?;
self.write_u16(scene_bias)?;
} else {
self.write_action_header(OpCode::GotoFrame2, 1)?;
self.write_u8(if play { 0b01 } else { 0b00 })?;
}
Ok(())
}
fn write_goto_label(&mut self, label: &'a str) -> Result<()> {
self.write_action_header(OpCode::GotoLabel, label.len() + 1)?;
self.write_string(SwfStr::from_utf8_str(label))?;
Ok(())
}
fn write_constant_pool(&mut self, strings: &'a [String]) -> Result<()> {
let len = strings
.iter()
.fold(2usize, |sum, c| sum.saturating_add(c.len() + 1));
self.write_action_header(OpCode::ConstantPool, len)?;
self.write_u16(strings.len() as u16)?;
for string in strings {
self.write_string(SwfStr::from_utf8_str(string))?;
}
Ok(())
}
fn write_block_length_at(
&mut self,
at: usize,
block_start: usize,
block_end: usize,
) -> Result<()> {
let len = block_end - block_start;
let len = u16::try_from(len).map_err(|_| EncoderError::BlockLenOverflow(len))?;
self.output[at..(at + 2)].copy_from_slice(&len.to_le_bytes());
Ok(())
}
fn write_define_function(
&mut self,
name: &str,
params: &'a [String],
actions: &'a Actions,
) -> Result<()> {
let len = params.iter().fold(name.len() + 1 + 2 + 2, |sum, p| {
sum.saturating_add(p.len() + 1)
});
self.write_action_header(OpCode::DefineFunction, len)?;
self.write_string(SwfStr::from_utf8_str(name))?;
self.write_u16(params.len() as u16)?;
for param in params {
self.write_string(SwfStr::from_utf8_str(param))?;
}
let length_offset = self.output.len();
self.write_u16(0)?;
let length_before_function = self.output.len();
self.write_actions(actions)?;
let length_after_function = self.output.len();
self.write_block_length_at(length_offset, length_before_function, length_after_function)?;
Ok(())
}
#[expect(clippy::too_many_arguments)]
fn write_define_function_2(
&mut self,
name: &str,
params: &'a [FunctionParam],
actions: &'a Actions,
register_count: u8,
preload_this: bool,
suppress_this: bool,
preload_arguments: bool,
suppress_arguments: bool,
preload_super: bool,
suppress_super: bool,
preload_root: bool,
preload_parent: bool,
preload_global: bool,
) -> Result<()> {
let len = params.iter().fold(name.len() + 1 + 3 + 4, |sum, p| {
sum.saturating_add(p.name.len() + 2)
});
self.write_action_header(OpCode::DefineFunction2, len)?;
self.write_string(SwfStr::from_utf8_str(name))?;
self.write_u16(params.len() as u16)?;
self.write_u8(register_count)?;
let mut flags = 0;
if preload_this {
flags |= 1 << 0;
}
if suppress_this {
flags |= 1 << 1;
}
if preload_arguments {
flags |= 1 << 2;
}
if suppress_arguments {
flags |= 1 << 3;
}
if preload_super {
flags |= 1 << 4;
}
if suppress_super {
flags |= 1 << 5;
}
if preload_root {
flags |= 1 << 6;
}
if preload_parent {
flags |= 1 << 7;
}
if preload_global {
flags |= 1 << 8;
}
self.write_u16(flags)?;
for param in params {
self.write_u8(param.register)?;
self.write_string(SwfStr::from_utf8_str(¶m.name))?;
}
let length_offset = self.output.len();
self.write_u16(0)?;
let length_before_function = self.output.len();
self.write_actions(actions)?;
let length_after_function = self.output.len();
self.write_block_length_at(length_offset, length_before_function, length_after_function)?;
Ok(())
}
fn write_with(&mut self, actions: &'a Actions) -> Result<()> {
self.write_action_header(OpCode::With, 2)?;
let length_offset = self.output.len();
self.write_u16(0)?;
let length_before = self.output.len();
self.write_actions(actions)?;
let length_after = self.output.len();
self.write_block_length_at(length_offset, length_before, length_after)?;
Ok(())
}
fn write_try(
&mut self,
try_body: &'a Actions,
catch_body: &'a Option<(CatchTarget, Actions)>,
finally_body: &'a Option<Actions>,
) -> Result<()> {
let len = 7 + if let Some((CatchTarget::Variable(name), _)) = catch_body {
name.len() + 1
} else {
1
};
self.write_action_header(OpCode::Try, len)?;
let mut flags = 0;
if catch_body.is_some() {
flags |= 1 << 0;
}
if finally_body.is_some() {
flags |= 1 << 1;
}
if matches!(catch_body, Some((CatchTarget::Register(_), _))) {
flags |= 1 << 2;
}
self.write_u8(flags)?;
let try_offset = self.output.len();
self.write_u16(0)?; let catch_offset = self.output.len();
self.write_u16(0)?; let finally_offset = self.output.len();
self.write_u16(0)?;
match catch_body {
Some((CatchTarget::Variable(name), _)) => {
self.write_string(SwfStr::from_utf8_str(name))?
}
Some((CatchTarget::Register(i), _)) => self.write_u8(*i)?,
None => self.write_u8(0)?,
}
let len_before_try = self.output.len();
self.write_actions(try_body)?;
let len_before_catch = self.output.len();
if let Some((_, catch_body)) = catch_body {
self.write_actions(catch_body)?;
}
let len_before_finally = self.output.len();
if let Some(finally_body) = finally_body {
self.write_actions(finally_body)?;
}
let len_at_end = self.output.len();
self.write_block_length_at(try_offset, len_before_try, len_before_catch)?;
self.write_block_length_at(catch_offset, len_before_catch, len_before_finally)?;
self.write_block_length_at(finally_offset, len_before_finally, len_at_end)?;
Ok(())
}
fn write_if(&mut self, label: &'a str) -> Result<()> {
self.write_action_header(OpCode::If, 2)?;
self.pending_label_writes.push(PendingLabelWrite {
label,
offset_pos: self.output.len(),
});
self.write_i16(0)?;
Ok(())
}
fn write_jump(&mut self, label: &'a str) -> Result<()> {
self.write_action_header(OpCode::Jump, 2)?;
self.pending_label_writes.push(PendingLabelWrite {
label,
offset_pos: self.output.len(),
});
self.write_i16(0)?;
Ok(())
}
fn write_push(&mut self, values: &'a [PushValue]) -> Result<()> {
let len = values
.iter()
.map(|v| match v {
PushValue::String(string) => string.len() + 2,
PushValue::Null | PushValue::Undefined => 1,
PushValue::Register(_) | PushValue::False | PushValue::True => 2,
PushValue::Float(_) => 9,
PushValue::Integer(_) => 5,
PushValue::Constant(v) => {
if *v < 256 {
2
} else {
3
}
}
})
.sum();
self.write_action_header(OpCode::Push, len)?;
for value in values {
self.write_push_value(value)?;
}
Ok(())
}
fn write_push_value(&mut self, value: &PushValue) -> Result<()> {
match value {
PushValue::String(string) => {
self.write_u8(0)?;
self.write_string(SwfStr::from_utf8_str(string))?;
}
PushValue::Float(v) => {
self.write_u8(6)?;
self.write_f64_me(*v)?;
}
PushValue::Null => {
self.write_u8(2)?;
}
PushValue::Undefined => {
self.write_u8(3)?;
}
PushValue::Register(v) => {
self.write_u8(4)?;
self.write_u8(*v)?;
}
PushValue::True => {
self.write_u8(5)?;
self.write_u8(1)?;
}
PushValue::False => {
self.write_u8(5)?;
self.write_u8(0)?;
}
PushValue::Integer(v) => {
self.write_u8(7)?;
self.write_i32(*v)?;
}
PushValue::Constant(v) => {
if *v < 256 {
self.write_u8(8)?;
self.write_u8(*v as u8)?;
} else {
self.write_u8(9)?;
self.write_u16(*v)?;
}
}
};
Ok(())
}
fn write_set_target(&mut self, target: &str) -> Result<()> {
self.write_action_header(OpCode::SetTarget, target.len() + 1)?;
self.write_string(SwfStr::from_utf8_str(target))?;
Ok(())
}
fn write_store_register(&mut self, register: u8) -> Result<()> {
self.write_action_header(OpCode::StoreRegister, 1)?;
self.write_u8(register)?;
Ok(())
}
fn write_get_url(&mut self, url: &str, target: &str) -> Result<()> {
self.write_action_header(OpCode::GetUrl, url.len() + target.len() + 2)?;
self.write_string(SwfStr::from_utf8_str(url))?;
self.write_string(SwfStr::from_utf8_str(target))?;
Ok(())
}
fn write_get_url_2(
&mut self,
load_variables: bool,
load_target: bool,
method: u8,
) -> Result<()> {
self.write_action_header(OpCode::GetUrl2, 1)?;
let mut flags = 0;
flags |= method & 0x3;
flags |= (load_target as u8) << 6;
flags |= (load_variables as u8) << 7;
self.write_u8(flags)?;
Ok(())
}
fn write_wait_for_frame(&mut self, frame: u16, num_actions_to_skip: u8) -> Result<()> {
self.write_action_header(OpCode::WaitForFrame, 3)?;
self.write_u16(frame)?;
self.write_u8(num_actions_to_skip)?;
Ok(())
}
fn write_wait_for_frame_2(&mut self, num_actions_to_skip: u8) -> Result<()> {
self.write_action_header(OpCode::WaitForFrame2, 1)?;
self.write_u8(num_actions_to_skip)?;
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpCode {
End = 0x00,
NextFrame = 0x04,
PreviousFrame = 0x05,
Play = 0x06,
Stop = 0x07,
ToggleQuality = 0x08,
StopSounds = 0x09,
Add = 0x0A,
Subtract = 0x0B,
Multiply = 0x0C,
Divide = 0x0D,
Equals = 0x0E,
Less = 0x0F,
And = 0x10,
Or = 0x11,
Not = 0x12,
StringEquals = 0x13,
StringLength = 0x14,
StringExtract = 0x15,
Pop = 0x17,
ToInteger = 0x18,
GetVariable = 0x1C,
SetVariable = 0x1D,
SetTarget2 = 0x20,
StringAdd = 0x21,
GetProperty = 0x22,
SetProperty = 0x23,
CloneSprite = 0x24,
RemoveSprite = 0x25,
Trace = 0x26,
StartDrag = 0x27,
EndDrag = 0x28,
StringLess = 0x29,
Throw = 0x2A,
CastOp = 0x2B,
ImplementsOp = 0x2C,
RandomNumber = 0x30,
MBStringLength = 0x31,
CharToAscii = 0x32,
AsciiToChar = 0x33,
GetTime = 0x34,
MBStringExtract = 0x35,
MBCharToAscii = 0x36,
MBAsciiToChar = 0x37,
Delete = 0x3A,
Delete2 = 0x3B,
DefineLocal = 0x3C,
CallFunction = 0x3D,
Return = 0x3E,
Modulo = 0x3F,
NewObject = 0x40,
DefineLocal2 = 0x41,
InitArray = 0x42,
InitObject = 0x43,
TypeOf = 0x44,
TargetPath = 0x45,
Enumerate = 0x46,
Add2 = 0x47,
Less2 = 0x48,
Equals2 = 0x49,
ToNumber = 0x4A,
ToString = 0x4B,
PushDuplicate = 0x4C,
StackSwap = 0x4D,
GetMember = 0x4E,
SetMember = 0x4F,
Increment = 0x50,
Decrement = 0x51,
CallMethod = 0x52,
NewMethod = 0x53,
InstanceOf = 0x54,
Enumerate2 = 0x55,
BitAnd = 0x60,
BitOr = 0x61,
BitXor = 0x62,
BitLShift = 0x63,
BitRShift = 0x64,
BitURShift = 0x65,
StrictEquals = 0x66,
Greater = 0x67,
StringGreater = 0x68,
Extends = 0x69,
GotoFrame = 0x81,
GetUrl = 0x83,
StoreRegister = 0x87,
ConstantPool = 0x88,
WaitForFrame = 0x8A,
SetTarget = 0x8B,
GotoLabel = 0x8C,
WaitForFrame2 = 0x8D,
DefineFunction2 = 0x8E,
Try = 0x8F,
With = 0x94,
Push = 0x96,
Jump = 0x99,
GetUrl2 = 0x9A,
DefineFunction = 0x9B,
If = 0x9D,
Call = 0x9E,
GotoFrame2 = 0x9F,
}