This requires moving `set_root_movie` into `UpdateContext`.
Now, we preload the entire movieclip immediately - Flash Player
does this regardless of the size of the SWF.
The 'Loader::load_complete' is delayed to the end of the frame
(which is when the root class is constructed for the loaded clip).
When handling dynamic properties, avmplus will always try to
parse the string key name as a uint. If it succeeds, then the
key will be stored internally as a integer (via Atom), which is
observable by property iteration. The intention appears to have
been to support `obj[25] = someVal`, but it causes `obj["25"]`
and `obj[25]` to map to the same key (though iterating over the
object's keys will always produce a `number`).
Currently in tests (input.json) it is possible to trigger Ctrl-V using:
{ "type": "TextControl", "code": "Paste" },
But there is no way of populating the clipboard.
This patch adds AutomatedEvent::SetClipboardText, so the clipboard
may be populated before pasting:
{ "type": "SetClipboardText", "text": "<value>" },
{ "type": "TextControl", "code": "Paste" },
Methods `onSetFocus` & `onKillFocus` are invoked when focus is changed
for `TextField`, `Button`, and `MovieClip`.
Multiple SWFs use these methods to listen to a focus change,
e.g. in order to implement placeholders for text fields.
We now validate the passed in profile, and return the selected profile
from 'Context3D.profile'. We don't yet alter the available
registers/textures based on the profile.
This is pretty straightforward, except for the fact that Flash
completely ignores the provided commands when the 'data' vector
is empty (if 'data' has even a single entry, then Flash will validate
that all of the commands have the correct amount of data to run).
One SWF that I tested relies on this behavior.
Casting the character to u8 and back to char caused some non-ASCII
non-control characters to be treated as control characters.
For instance the letter "ą" (U+0105) after casting to u8 and back
became ENQ (U+0005) which is a control character.
Some other letters worked, for instance the letter "ł" (U+0142)
became "B" (U+0042) and was not classified as a control character.
The test edittext_input was added to verify this behavior.
Our asc.jar doesn't seem to apply a version suffix to namespaces for
interface method definitions. This was causing these methods to
get marked as VM_INTERNAL when we loaded playerglobals, preventing SWF
from invoking these methods through the interface (e.g. having a
variable of type `IEventDispatcher`, and calling `dispatchEvent` on it)
This builds on our existing playerglobal versioning support
to add in AIR versioning. We closely follow the avmplus implementation:
* When an SWF is loaded, we chose either a FlashPlayer or AIR
APIVersion for its SWF version, based on our configured player runtime.
* When loading playerglobals, we look at the player runtime. In AIR
mode, we map FlashPlayer-versioned definitions to the closest AIR
version. This ensures that all runtime APIVersions are in the
same series (either AIR or FlashPlayer). In FlashPlayer mode,
all AIR-versioned definitions get mapped to VM_INTERNAL, hiding
them from user code.
Part of our existing api versioning code was implemented incorrectly.
Within playerglobals, we need to treat all unmarked namespaces as
VM_INTERNAL - this allows things like playerglobal script
initializer "initproperty" opcodes to see any VM_INTERNAL AIR
definitions (when we run under FlashPlayer mode). Previously, we
were using AllVersions, which would result in those VM_INTERNAL
definitions being hidden from other playerglobal code, which is
not correct.
Using this support, I've added a stub for the AIR-only
'flash.net.DatagramSocket'. I've also extended the test framework
with a new 'player_options.runtime' config option, which can be
set to "AIR" or "FlashPlayer" to configure the test runtime mode.
I've also added two new tests:
* 'air_hidden_lookup' runs under the FlashPlayer runtime, and verifies
that a list of classes (currently just "DatagramSocket" are
inacessible).
* 'air_datagram_socket', which uses `player_options.runtime = "AIR"`
to construct an instance of `flash.net.DatagramSocket`. We can
extend this test once we implement more of `DatagramSocket`
With this commit, we have all of the needed infrastructure to start
implementing and testing AIR-only classes and methods.
After some testing, and looking at OpenFL, I believe I've
determined the correct behavior for AGAL sampling:
Each time a Context3D.setProgram or Context3D.setSamplerStateAt
call is made, the sampler config for the used texture slot(s)
is updated with the new wrapping/filter behavior. For setProgram,
this comes from all of the 'tex' opcodes used within the program.
However, when the 'ignoresampler' flag is set in a 'tex' opcode,
the setProgram call does *not* override the existing sampler config.
As a result, that program will sample with the behavior determined
by the most recent setSamplerStateAt or setProgram call involving
the used texture slot(s).
Previously, we were always overriding the opcode sampler config
with the values from Context3D.setSamplerStateAt. However, I didn't
realize that the order of the calls matter, so none of my tests ended
up observing the effect of 'ignoresampler'.
We now need to process AGAL bytecode twice - a quick initial
parse to determine the sampler configs (which need to be updated
when we call 'setProgram'), and a second time when to build the
Naga module (which needs to wait until we have the vertex attributes
available, which can be changed by ActionScript after setting
the program).
This was causing the `Object.prototype.toString` to throw error 1050,
instead of returning `[object Array]`, which was causing quite a few avmplus test failures.
This makes them pass on aarch64 (for example, Android),
it looks nicer (no secret robot math), and more digits
are not necessary anyway. Also fix up formatting a bit.
This more closely aligns our code with the corresponding avmplus code.
A user-supplied index of '0' is special-cased, and we correctly
resume iteration when a public index mismatch is detected.
This ensures that this flag is set regardless of whether the
object is constructed by the timeline or from ActionScript
(it was previously only set when constructed by the timeline).
* core: Fix MorphShape inaccuracy on complex paths
It's inaccurate to interpolate the moveTo/lineTo deltas individually
because on complex paths they're often small integers, which won't
interpolate smoothly.
Instead, interpolate absolute positions.
Avmplus constructs a full `QName`, and uses the normal
Multiname matching logic. This would be a large refactor,
so I've just modified the existing method to properly
handle multiple namespaces.
I've also included a closely related fix - we should only treat
a multiname with the literal local name "@foo" as an attribute
when the namespace is the empty public namespace. We were incorrectly
reparsing multinames that contained multiple namespaces.
An Avm2 button appears to run a 'nested frame' during construction -
the same type of 'nested frame' performed by a goto. This will
run framescripts for all objects in the display hierarchy (including
orphans), resulting in an unusual child->parent framescript execution
order that DeathVsMonstars_decrypted.swf depends on.
This allows us to hide things like `MovieClip.isPlaying` from older
SWFs, which may define an `isPlaying` method without marking it
as an override.
This has the largest impact on public namespaces. There is no longer
just one public namespace - we now have a public namespace per API
version. Most callsites should use `Avm2.find_public_namespace()`,
which determines the public namespace based on the root SWF API version.
This ensures that explicit property access in Ruffle (e.g. calling
"toString") are able to see things defined in the user SWF.
This requires several changes other:
* A new `ApiVersion` enum is introduced, storing all of the api versions
defined in avmplus
* NamespaceData::Namespace now holds an `ApiVersion`. When we read a
namespace from a user-defined ABC file, we use the `ApiVersion` from
the root movie clip. This matches Flash Player's behavior - when a
child SWF is loaded through `Loader`, its API version gets overwritten
with the parent's API version, which affects definition visibility and
'override' requirements in the child SWF. Note that 'behavior changes'
in methods like `gotoAndPlay` are unaffected.
* The `PartialEq` impl for `Namespace` has been removed - each call site must now choose
to compare namespaces either by an exact version match, or by
'compatible' versions (a `<=` check) with `Namespace::matches_ns`
* `PropertyMap` now uses `Namespace::matches_ns` to check for a
compatible version when looking up a definition.
* When compiling our playerglobal, we pass in the `-apiversioning` flag,
which causes asc.jar to convert `[API]` metadata into a special
Unicode 'version mark' suffix in namespace URLs. We parse this
when loading namespaces to determine the API version to use for
playerglobal definitions (unmarked definitions use the lowest possible
version).
Unfortunately, this makes ffdec unable to decompile our
playerglobal.swc
I've going to file an upstream issue
When a UTF-16 BOM is present, ByteArray.toString automatically
strips it out, and treats the remaining data as a UTF-16 string
with the specified endianness.
* avm2: Use RawTable to implement 'public index' iteration
This makes our implementation more closely aligned with avmplus.
In particular, it's now possible to delete keys from an object
while iterating without disturbing the iteration order (as long
as those keys were already produced by the iterator).
This is based on @Bale001's work on RawTable-based iteration
A few tests had their output changed (they depend on the exact
object iteration order, and don't neccessarily match Flash Player
exactly).
* Use Cell to store index fields
* Remove outdated comment
This is our first non-rgba texture format (it uses Bc3RgbaUnorm).
ATF files store these textures in a very convoluted way - fortunately,
the 'dds2atf' tool is open-source, which allowed me to figure out
how to decode the texture back to a DXT5/DXT1 texture.
Some SWFs rely on catching the error.
In order to reproduce Flash's error messages, we now store a `QName`
in `FunctionObject`, which is set when we make a bound method.
Normally, Flash Player will ignores frames for a movie clip with
a symbol class that doesn't extend `MovieClip` (e.g. it extends
`Sprite`). However, the root movie appears to have frame run
unconditionally, even if it only extends `Sprite`. This can
be observed by adding a Graphics child in the second frame only -
the child will flicker in and out as the player switches between
frames, but only for the root movie clip.
Seedling relies on this behavior - it has `DoAbc2` tags in the second
frame, and has a main `Preloader` class that extends `Sprite`.
We are not interested in testing the timing of buffer loads (since that will vary based on hardware), we just need to know that we got a buffer load event.
Furthermore all our seeks are in-buffer so multiple buffer full events shouldn't happen in Ruffle.
We previously ran these tags during preloading - however,
they are actually run as part of frame execution. This is observable
by ActionScript - a SWF can load in a class from a stop()'d MoveClip,
and then advance the clip to a frame with a SymbolClass referencing
the loaded class.
This fixes 'who_killed_travolta', and unblocks DeathvsMonstars and
NeoPets Lost City Lanes once some additional button fixes are merged.
This isn't 100% correct (as shown by the disabled test), but it brings
us closer to what Flash Player does.
* avm2: Add error 1087
* avm2: Add deep_copy method to XmlObject
Changes existing AS3 copy method to use this instead
* avm2: Add a few utility methods to E4XNode
* avm2: Change XML.replace impl to use new utility methods
* avm2: allow setting XML/XMLList as a element
* avm2: Fix `xml_namespaced_property` test failure
* chore: fmt
* chore: Appease clippy
* avm2: Add test for XML/XMLList as element
* avm2/tests: Implement XML.setName; add a test
* avm2: Basic support for QNames in XML.setName (no namespace support yet)
* avm2: Reorder order of Attribute/Element/PI checks in XML.setName
* avm2: Throw error #1117 when the name passed to XML.setName is not a valid XML name
SetTarget currently sets the target clip to the base clip if the
target passed to tellTarget() is an undefined object. This causes
goto's to run on the base clip in Ruffle, when Adobe Flash Player
does not run the goto's at all.
Fixes#12389 and #12390
`Loader` override `removeChild` and `removeChildAt` to try to prevent
user code from removing the loaded content child. However, this can be
bypassed by calling `otherContainer.addChild(loader.content)`, which
actually removes the child from the loader. Stealth Hunter 2 relies
on this behavior.
To make this work, `Loader.content` needs to go through
`contentLoaderInfo`, instead of relying on a child being present.
avm1_unload_movie has been adapted to only enter the unloaded state with
one frame delay if the MovieClip is a root MovieClip. The unloaded state
is now immediately entered for non-root MovieClips.
This fixes the regressions #12254 and #12265 which got introduced
because of this delay.
However, in Flash Player, even non-root MovieClips enter the unloaded
state one frame after the unloadMovie command has been read. Ruffle is
probably replacing a MovieClip differently to Flash, therefore
introducing these regressions when trying to emulate that delay.
Documentation explaining this all has been added to avm1_unload_movie.
Therefore, the test movieclip_library_state_values has been added. It
tests the default state and the unloaded state of a (non-root) child
MovieClip that's loaded from the library. It is marked as known_failure
because Ruffle currently doesn't implement the delay before entering the
unloaded state for non-root MovieClips.