If a child named 'foo' is removed by the timeline (without
having been previous added/removed from the timeline by ActionScript),
then the 'foo' field in the parent will be set to null. This occurs
even if the 'foo' field in the parent is not currently set to
the child 'foo' (e.g. 'this.foo = someOtherObject' was executed by
ActionScript).
The Adobe Animate compiler rejects a subclass that contains
a non-private field with the same name as a field in an ancestor
class (e.g. 'pub var foo:String' in both the subclass and superclass).
Unfortunately, Flash Player accepts this code, and creates a distinct
field for each class (even though they have the same namespace and
name). I suspect that this is caused by the optimizer replacing the
field accesses with internal slot accesses.
I've added an ignored test demonstrating this behavior - getting Ruffle
to reproduce it will be tricky.
When we run a 'goto', a weird "nested frame" gets triggered.
Previously, we were only calling `construct_frame` on the target
MovieClip as part of this "nested frame". However, Flash Player
seems to treat this (in some ways) like a normal frame - *all*
objects on the Stage (and orphans) have `construct_frame` called.
In particular, `gotoAndStop`/`gotoAndPlay` is called during
an "enterFrame" event handler, then unrelated objects on the Stage
will have their children constructed during the execution of
`gotoAndStop`/`gotoAndPlay`. The same logic holds for frame scripts.
This fixes a bug in Steamlands, which relies on children on the main
timeline being constructed immediately following a call to `gotoAndStop`
on an orphan (originally triggered from an "enterFrame" handler).
QName can contain characters like '.' and '<' as part of the
package or class name (though this requires editing the SWF
or using a nonstandard compiler). This broke our attempt to parse
generic type paramters by looking for '.<'
Instead, our 'Vector' special casing now operates on the unparsed
'AVMString' name, instead of attempting to construct a 'QName'.
This means that we don't need to handle generic paramters at
all for obfuscated names (which will never start with '__AS3__'
or 'Vector.<')
This fixes a bug in Red Ball 4v3, which has an obfuscated class
'!D.<H'
If we have two PlaceObject tags in the same frame with the same depth,
only the first one actually places an object. The second one is ignored
(Flash Player logs a warning).
We now parse PixelBender bytecode, and populate the parameters
from the bytecode on `ShaderData`. This is enough to progress
Steamlands, which needs to access dynamically set properties
on `ShaderData`
Bytecode execution is not implemented yet.
When we create a DisplayObject from ActionScript, we should always
run `construct_frame` on it, regardless of what frame phase we
are currently in.
This fixes a regression in Fancy Pants World 4 Part 1, where entering
the first door produced an error.
Flash Player performs `x + width` and `y + height` as floating
point operations before `round_to_even`. This affects the extent
covered by a `Rectangle` in various BitmapData methods, as the sum
of two values might be large enough to be rounded up to a larger
value (when rounding `x` and `width` individually would have
produced a smaller overall extent).
These are directly set on the underlying navigator's HTTP
request type, and get printed out in our test navigator backend.
No validation of the header names is performed - on web, this will be
enforced by the browser.
When we iterate over a render list (in order to call
`enter_frame`, `construct_frame, etc.) we need to be sure
that we iterate over all of the original `DisplayObjects`
in the list, even if the list is modified during iteration
(e.g. some ActionScript code calls `parent.addChildAt`).
Previously, `RenderIter` would repeatedly call `child_by_index`
on the original `DisplayObjectContainer`, up to the original
child count. If new DisplayObjects were inserted into the list
during iteration, we could miss some of the original DisplayObjects
in the list (as they are now at a greater position in the list).
To solve this, we now store the render list as an `Rc<Vec>`,
and use `Rc::make_mut` to modify it. See the comments
for more details.
With SWF version < 30, Context3D.configureBackBuffer throws
an error with a less informative message when the width/height
are out of range. Additionally, it seems to special case
the case width=0, height=0, antiAlias=0. enableDepthAndStencil=false,
and does *not* throw an error. This is relied on by Sniper Team.
The Flash Player 'Matrix3D.recompose' method throws exceptions under
certain circumstances with the "quaternion" orientation style.
I haven't yet figured this out yet, so I've marked that case as a stub.
All of the implementations are based on the OpenFL code, with some
tweaks to match Flash Player's behavior.
TextFields have a very unusual behavior - if they are selectable
and have `was_static`, they *block* the dispatch of mouse events when
they're targeted (not even the Stage will receive the event).
This only occurs when the TextField is actually targeted
(which requires mouseEnabled=true). With mouseEnabled=false,
the event will be dispatched with an ancestor as the target,
following the usual logic.
Also, TextFields now properly propagate mouse picks to
their parent if mouseEnabled=false. Previously, setting
mouseEnabled=false for a TextField made all mouse picks
cause a miss on it. This was the cause of the Turbo Kids
regression.
Fixes#10245
This can actually affect runtime behavior - if the return type is
declared as 'int', then an instance of a custom class will get
coerced to 0 when returned by the function.
'Plants vs Zombies Demo' relies on this - it has a function which
incorrectly returns an object instead of an array index, but the
value gets silently coerced to 0 under Flash Player.
This is based on URLLoader, and doesn't actualy 'stream' data -
it all becomes available once the request finishes. However, this
is good enough to get Sniper Team working.
We now run all of the completion logic (including adding
the new DisplayObject as a child) in `Loader::movie_loader_complete`.
Previously, some of this logic was run from `Loader::preload_tick`,
which meant that loaded images did not have the logic run.
Also, `BitmapData` and `Bitmap` instances (with corresponding AVM2
objects) are now properly constructed for loaded images.
In a previous PR, I introduced an optimization that used
`copy_texture_to_texture` to copy directly from a BitmapData GPU
texture to a Stage3D GPU texture.
Unfortunately, this optimization is incorrect. A BitmapData GPU
texture can be modified at any time by normal AVM2 code - in
particular, in might be modified before we submit the encoded
`copy_texture_to_texture` command. This shows up in Sniper Team,
which re-uses BitmapData objects for multiple distinct textures.
The previous 'optimization' resulted in the wrong BitmapData contents
getting uploaded to a texture (since it was changed before the copy
command was submitted).
When multisampling is enabled, we should create a new multisampled texture,
and use the existing texture as the resolve buffer. We also need to
call `update_has_depth_texture` to keep our pipeline aware of whether
or not we currently have a depth buffer attached.
Makes progress on #10641 (it has a stack overflow after
this PR, due to an unrelated issue).
wgpu requires buffer copy sizes and offsets to be 4-byte aligned.
Unfortunately, ActionScript can perform 2-byte aligned uploads
into an IndexBuffer3D.
To support this, we now keep a copy of the IndexBuffer3D on the CPU.
When performing an upload to the buffer, we round the offset down
and the size up to the nearest 4-byte aligned value. The cpu buffer
is used to fill out the write with existing data, so that we don't
corrupt the contents of the GPU buffer.
To avoid introducing a new RefCell, I've changed IndexBuffer3D
to use a `Box` instead of an `Rc` to store the trait object.
This allows us to pass a mutable reference down to the backend.
Early class construction is tricky - `Object` defines properties
that need to get copied into subclass instance vtables, but `Class`
defines `prototype`, which needs to be copied into the *class* vtable
of `Object`.
To accomplish this, I've split out instance vtable initialization
into a separate `init_instance_vtable`. We call
`object_class.init_instance_vtable` before
`class_class.init_instance_vtable`, but do things in the opposite
order for `into_finished_class` (`class_class.into_finished_class` is
called before `object_class.into_finished_class`)
It's possible to have a DefineSprite tag with multiple frames,
but with a corresponding SymbolClass that directly extends
`Sprite` (and therefore does *not* extend `MovieClip`). When this
happens, Flash Player stops after the first frame.
Doing `super.someNonGetter` gives you back a function object.
We were previously attempting to call normal methods as though
they were getters. Additionally, we were failing to properly
get the property from the superclass vtable.
If a SWF contains multiple DefineFont tags with the same
font name (but different font IDs), the first tag will win
when a font is looked up by *name*. This affects the behavior
of EditText objects, which can have embedded HTML like
`<font face="MyFontName">` which performs a font lookup by name.
This fixes Fancy Pants World 4 Part 3, which contains two
DefineFont3 tags with the name FancyFont. The second font is
missing many glyphs, so using it causes us to be unable to
render the squiggle and life count text.
When we receieve a nonzero 'antiAlias' parameter, we create
create a non-multisampled resolve buffer to use with WGPU.
Several tests were already requesting antialiasing, so their
output images are now anti-aliased without any changes to
the tests themselves.
If you use a `Loader` to load an SWF containing a class that shadows
an already-defined class, the class definition from the Loader SWF
will be ignoredin favor of the already-defined class. This commit
applies this log to symbol classes as well - the symbol registry for the class
should continue to point to the existing MovieClip in the parent.
This results in the child SWF instantiating the class from the parent
SWF when the child places the affected movie clip on the timeline.
This fixes a bug in Fancy Pants World 4 Part 3, where the sub-level
SWF was replacing the symbol class entry for the parent 'shipInteract'
class with the dummy clip provided in the sub-level SWF (instead
of continuing to use the correct clip from the parent SWF).
Previously, we were scaling down the source image to fit into
the smaller sourceRect, instead of cropping at the original scale.
This broke the background textures in Fancy Pants World 4 Part 2,
as the scaled-down output image resulted in a smaller rectangle
being returned from 'getColorBoundsRect'
We now crop the image by properly constructing the UV-coordinate
transformation matrix. We were also using the wrong value for the
'destPoint' y coordinate, which I fixed.
This slightly changes the image output of two tests - the new images
now more closely match the Flash output.
Calling `Hash::write_bytes` isn't guaranteed to be equivalent to a
sequence of `Hash::write_u8`.
Additionally, make sure the hash is truly prefix-free by hashing the
length first.
This was a leftover from before we started usiung vec4 everywhere
for compatibility with AGAL. There are a few specific opcodes that
don't need extension, but it doesn't depend on the destination
register.
In the process, I fixed a bug where we were clearing the depth
and stencil buffers with the incorrect value.
This makes Fancy Pants World 4 Part 1 playable to completion
(though there are still some rendering issues that need
to be fixed).
This factors out the early-resolution logic I added in `op_coerce`,
making it useable during paramter resolution as well. This lets
a static initializer reference the containing class in parameter
types, even though the ClassObject hasn't yet been initialized.
We were missing the initial 'set_skip_next_enter_frame(true)'
call, and we weren't properly clearing it in `enter_frame`.
Loaders appear to have the same behavior as MovieClips.
This makes us correctly run the first framescript for the loaded
SWF.
Despite not being MovieClips, Loader instances appear to get
the same kind of orphan handling - you can instantiate a
Loader and call 'Loader.load' without ever adding it
to a parent, and the loader will still run.
I've changed the movie code to work with a new `DisplayObjectWeak`
enum. Currently, this just supports `MovieClip` and `Loader`,
but it can easily be extended if we ever need other weak display
objects.
This also fixes a bug where we were adding the loaded MovieClip
as a child of the Loader slightly too early.
This includes the 'GetDescendants' opcode, which is used by the
the 'xml..elementName' syntax. The 'XMLList.toXMLString()
impl makes it much easier to write tests for this.
We only support values that are neither XML nor XMLList,
since we can't yet properly stringify those.
Attempting to modify an existing attribute throws an error.
* Bump bitflags to 2.0.0
* Sprinkle Clone, Copy, Eq, PartialEq, and Debug derives where needed
* Call `bits` on bitflags, as it is now a method
* Switch from `from_bits_truncate` to `from_bits_retain` on bitflags where needed
* Bump h263-rs for the bitflags 2.0.0 dependency
As part of porting to bitflags 2.0.0, see:
https://kodraus.github.io/rust/2022/10/07/bitflags2.html#upgrading-to-2x
The XML call handler is implemented as 'new XML(arg)',
so we get all of the related string coercions for free.
Our various native tables are starting to get somewhat wasteful -
if we add any more, we might want to consider a more compact
representation.
When we skip running a frame for a MovieCilp, we skip all
of its children as well. However, this skip 'counts' as
a skip for any children that already wanted to skip their next
frame. For example, say we create three objects in ActionScript,
and arrange them like 'obj1 -> obj2 -> obj3'.
The first 'obj1.enter_frame' call will not run a new frame
for any of the objects, but next time, 'obj1.enter_frame'
will run a new frame for all of the objects.
This fixes jacksmith, which was missing a frame1-framescript
due to 'enter_frame' getting incorrectly run for a deeply
nested child.
If you call 'BitmapData.dispose()', any Bitmap objects using it will
continue to report the original 'width' and 'height' values to
ActionScript. The values only refresh if you explicitly do
'Bitmap.bitmapData = bitmapDataObject' (including with the same
object).
Fancy Pants Adventure World 4 relies on this - it calls
BitmapData.dispose(), and then uses the width and height from
a previously-constructed Bitmap object.
When a DisplayObject is removed from its parent by a RemoveTag, it still runs its framescript for the current frame (but with 'this.parent == null'). It then stops executing entirely, unlike ActionScript-removed orphans, which continue to execute indefinitely.
Additionally, objects created by ActionScript during a frame skip their next 'enterFrame' logic (but still receive an enterFrame event). This results in the currentFrame lagging one frame behind objects that were placed by the timeline during the same frame.
The combination of these two changes lets us greatly simplify frame lifecycle handling for orphan movies. Most of the orphan stages were unencessary, and the remaining ones run in the same phase as the normal Stage-descendant objects.