Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion core/common/src/tag_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ pub struct SwfMovie {
/// something else, like an loaded image, filler movie, or error state.
is_movie: bool,

/// Whether this movie should be interpreted as AVM1, regardless of what the
/// header declares.
force_avm1: bool,

/// Security sandbox type enforced for this movie.
///
/// It absolutely cannot be changed after constructing
Expand All @@ -63,6 +67,7 @@ impl SwfMovie {
encoding: swf::UTF_8,
compressed_len: 0,
is_movie: false,
force_avm1: false,
sandbox_type,
}
}
Expand Down Expand Up @@ -90,6 +95,7 @@ impl SwfMovie {
parameters: Vec::new(),
encoding: swf::UTF_8,
is_movie: false,
force_avm1: false,
sandbox_type,
}
}
Expand All @@ -115,6 +121,7 @@ impl SwfMovie {
parameters: Vec::new(),
encoding: swf::UTF_8,
is_movie: false,
force_avm1: false,
sandbox_type,
}
}
Expand All @@ -138,6 +145,7 @@ impl SwfMovie {
encoding: swf::UTF_8,
compressed_len: 0,
is_movie: false,
force_avm1: false,
sandbox_type,
}
}
Expand All @@ -161,6 +169,7 @@ impl SwfMovie {
encoding,
compressed_len,
is_movie: true,
force_avm1: false,
sandbox_type,
};
movie.append_parameters_from_url();
Expand All @@ -180,6 +189,7 @@ impl SwfMovie {
encoding: swf::UTF_8,
compressed_len: length,
is_movie: false,
force_avm1: false,
sandbox_type,
};
movie.append_parameters_from_url();
Expand Down Expand Up @@ -242,6 +252,10 @@ impl SwfMovie {
self.url = url;
}

pub fn set_force_avm1(&mut self) {
self.force_avm1 = true;
}

/// Get the URL that triggered the fetch of this SWF.
pub fn loader_url(&self) -> Option<&str> {
self.loader_url.as_deref()
Expand All @@ -263,10 +277,20 @@ impl SwfMovie {
self.header.uncompressed_len()
}

pub fn is_action_script_3(&self) -> bool {
/// Whether the SWF's FileAttributes tag declares the SWF to be AVM2.
pub fn is_declared_action_script_3(&self) -> bool {
self.header.is_action_script_3()
}

/// Whether this `SwfMovie` should be interpreted as AVM2.
///
/// This usually is the same as `is_declared_action_script_3`, but will
/// return false if this is an AVM2 movie loaded by AVM1 (which we mark by
/// setting `force_avm1` to true).
pub fn is_action_script_3(&self) -> bool {
self.header.is_action_script_3() && !self.force_avm1
}

pub fn stage_size(&self) -> &Rectangle<Twips> {
self.header.stage_size()
}
Expand Down
26 changes: 16 additions & 10 deletions core/src/display_object/movie_clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@
let mut target = self;
loop {
let shared = target.0.shared.get();
if shared.movie().is_action_script_3() {
if shared.movie().is_declared_action_script_3() {
tracing::warn!("DoInitAction tag in AVM2 movie");
return Ok(());
}
Expand Down Expand Up @@ -652,10 +652,8 @@
context: &mut UpdateContext<'gc>,
reader: &mut SwfStream<'_>,
) -> Result<Option<Script<'gc>>, Error> {
if !context.root_swf.is_action_script_3() {
tracing::warn!("DoABC tag with non-AVM2 root");
return Ok(None);
}
// This is unreachable if this `MovieClip`'s movie is AVM1, as
// `preload_bytecode_tag` returns early if the movie is AVM1

let data = reader.read_slice_to_end();
if !data.is_empty() {
Expand Down Expand Up @@ -696,10 +694,8 @@
context: &mut UpdateContext<'gc>,
reader: &mut SwfStream<'_>,
) -> Result<Option<Script<'gc>>, Error> {
if !context.root_swf.is_action_script_3() {
tracing::warn!("DoABC2 tag with non-AVM2 root");
return Ok(None);
}
// This is unreachable if this `MovieClip`'s movie is AVM1, as
// `preload_bytecode_tag` returns early if the movie is AVM1

let do_abc = reader.read_do_abc_2()?;
if !do_abc.data.is_empty() {
Expand Down Expand Up @@ -4116,6 +4112,16 @@
tag_code: TagCode,
reader: &mut SwfStream<'a>,
) -> Result<(), Error> {
if !self.movie().is_action_script_3() {
match tag_code {
TagCode::DoAbc => tracing::warn!("DoABC tag in AVM1 movie"),

Check warning on line 4117 in core/src/display_object/movie_clip.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (4117)
TagCode::DoAbc2 => tracing::warn!("DoABC2 tag in AVM1 movie"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we warn when !is_action_script_3 or !is_declared_action_script_3?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go with !is_action_script_3, as if AVM1 loads an AVM2 movie the tags will possibly unexpectedly not be executed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but then the warnings are a bit misleading? They suggest it's an AVM1 movie despite the fact it's declared as AVM2.

_ => unreachable!(),

Check warning on line 4119 in core/src/display_object/movie_clip.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (4119)
}

return Ok(());
}

let cur_frame = self.preload_progress.cur_preload_frame.get() - 1;
let abc = match tag_code {
TagCode::DoAbc | TagCode::DoAbc2 => {
Expand Down Expand Up @@ -4165,7 +4171,7 @@
reader: &mut SwfStream<'a>,
tag_len: usize,
) -> Result<(), Error> {
if self.movie().is_action_script_3() {
if self.movie().is_declared_action_script_3() {
tracing::warn!("DoAction tag in AVM2 movie");
return Ok(());
}
Expand Down
10 changes: 9 additions & 1 deletion core/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1578,7 +1578,15 @@ impl<'gc> MovieLoader<'gc> {

let movie = match sniffed_type {
ContentType::Swf => {
Arc::new(SwfMovie::from_data(data, url.clone(), loader_url.clone())?)
let mut movie = SwfMovie::from_data(data, url.clone(), loader_url.clone())?;

if matches!(vm_data, MovieLoaderVMData::Avm1 { .. }) {
// If AVM1 loads a SWF, that SWF is always interpreted as
// AVM1, regardless of what it declares in its header.
movie.set_force_avm1();
}

Arc::new(movie)
}
ContentType::Gif | ContentType::Jpeg | ContentType::Png => {
Arc::new(SwfMovie::from_loaded_image(url.clone(), length))
Expand Down
11 changes: 11 additions & 0 deletions tests/tests/swfs/mixed_avm/avm1_doabc/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
trace("AVM1 shouldn't run DoABCs, so this shouldn't get printed");
}

package {
public class Test {
public function Test() {
super();
}
}
}
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm1_doabc/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This trace is printed from a DoAction
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm1_doabc/test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trace("This trace is printed from a DoAction");
Binary file added tests/tests/swfs/mixed_avm/avm1_doabc/test.swf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm1_doabc/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trace("Hello from inner SWF - this shouldn't print");
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trace("Hello from inner SWF (DoInitAction) - this shouldn't print");
4 changes: 4 additions & 0 deletions tests/tests/swfs/mixed_avm/avm1_loads_avm2_doaction/outer.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
trace("Hello from outer SWF");
var clip = this.createEmptyMovieClip("clip", 0);
loadMovie("avm2.swf", clip);
trace("Outer SWF: Called loadMovie");
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello from outer SWF
Outer SWF: Called loadMovie
Binary file not shown.
3 changes: 3 additions & 0 deletions tests/tests/swfs/mixed_avm/avm1_sprite_sc_ignored/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
frame 1
frame 2
frame 3
Binary file not shown.
11 changes: 11 additions & 0 deletions tests/tests/swfs/mixed_avm/avm2_doaction/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
trace("This trace is printed from a DoABC");
}

package {
public class Test {
public function Test() {
super();
}
}
}
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_doaction/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This trace is printed from a DoABC
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_doaction/test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trace("AVM2 shouldn't run DoActions, so this shouldn't get printed");
Binary file added tests/tests/swfs/mixed_avm/avm2_doaction/test.swf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_doaction/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 1
11 changes: 11 additions & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_doabc/Loaded.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
trace("This shouldn\'t be traced, as the SWF is AVM1");
}

package {
public class Loaded {
public function Loaded() {
super();
}
}
}
15 changes: 15 additions & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_doabc/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package {
import flash.display.Loader;
import flash.display.MovieClip;
import flash.net.URLRequest;

public class Test extends MovieClip {
public function Test() {
var l:Loader = new Loader();
l.load(new URLRequest("avm1.swf"));
addChild(l);

trace("Finished constructor of class of root MovieClip");
}
}
}
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_doabc/frame1.as
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trace("Hello from DoAction tag on frame 1 of avm1.swf");
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_doabc/frame2.as
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trace("Hello from DoAction tag on frame 2 of avm1.swf");
4 changes: 4 additions & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_doabc/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Finished constructor of class of root MovieClip
Hello from DoAction tag on frame 1 of avm1.swf
Hello from DoAction tag on frame 2 of avm1.swf
Hello from DoAction tag on frame 1 of avm1.swf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_doabc/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 4
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Outer avm2 SWF: Called load
AVM1 SWF: Hello from AVM1
AVM1 SWF: Called loadMovie
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 4
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_v10/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 3
1 change: 1 addition & 0 deletions tests/tests/swfs/mixed_avm/avm2_loads_avm1_v9/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_frames = 3
Loading