diff --git a/Cargo.lock b/Cargo.lock index 3bbb5fcca..1c5e41eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4263,6 +4263,7 @@ dependencies = [ "egui", "egui_extras", "encoding_rs", + "enum-map", "enumset", "flash-lso", "flate2", diff --git a/core/Cargo.toml b/core/Cargo.toml index 26031bf07..52e4bfd6d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -58,6 +58,7 @@ flv-rs = { path = "../flv" } async-channel = "2.1.1" jpegxr = { git = "https://github.com/ruffle-rs/jpegxr", branch = "ruffle", optional = true } image = { version = "0.24.7", default-features = false, features = ["tiff", "dxt"] } +enum-map = "2.7.3" [target.'cfg(not(target_family = "wasm"))'.dependencies.futures] version = "0.3.29" diff --git a/core/src/avm2.rs b/core/src/avm2.rs index 6c0a1552f..5a17db01b 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -11,6 +11,7 @@ use crate::context::{GcContext, UpdateContext}; use crate::display_object::{DisplayObject, DisplayObjectWeak, TDisplayObject}; use crate::string::AvmString; use crate::tag_utils::SwfMovie; +use crate::PlayerRuntime; use fnv::FnvHashMap; use gc_arena::{Collect, GcCell, Mutation}; @@ -92,6 +93,10 @@ pub struct Avm2<'gc> { /// The Flash Player version we're emulating. player_version: u8, + /// The player runtime we're emulating + #[collect(require_static)] + pub player_runtime: PlayerRuntime, + /// Values currently present on the operand stack. stack: Vec>, @@ -180,7 +185,11 @@ pub struct Avm2<'gc> { impl<'gc> Avm2<'gc> { /// Construct a new AVM interpreter. - pub fn new(context: &mut GcContext<'_, 'gc>, player_version: u8) -> Self { + pub fn new( + context: &mut GcContext<'_, 'gc>, + player_version: u8, + player_runtime: PlayerRuntime, + ) -> Self { let playerglobals_domain = Domain::uninitialized_domain(context.gc_context, None); let stage_domain = Domain::uninitialized_domain(context.gc_context, Some(playerglobals_domain)); @@ -191,6 +200,7 @@ impl<'gc> Avm2<'gc> { Self { player_version, + player_runtime, stack: Vec::new(), scope_stack: Vec::new(), call_stack: GcCell::new(context.gc_context, CallStack::new()), diff --git a/core/src/avm2/api_version.rs b/core/src/avm2/api_version.rs index 4b82f0874..74c08ed02 100644 --- a/core/src/avm2/api_version.rs +++ b/core/src/avm2/api_version.rs @@ -1,7 +1,10 @@ +use std::sync::OnceLock; + +use crate::PlayerRuntime; +use enum_map::{enum_map, Enum, EnumMap}; + // Based on https://github.com/adobe/avmplus/blob/master/core/api-versions.h -// FIXME - if we ever add in AIR emulation, then implement the 'version series' logic. -// For now, it's fine to just compare Flash player version against air versions directly. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, FromPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, FromPrimitive, Enum)] #[allow(non_camel_case_types)] pub enum ApiVersion { AllVersions = 0, @@ -59,37 +62,142 @@ pub enum ApiVersion { VM_INTERNAL = 52, } +static TRANSFER_TABLE: OnceLock> = OnceLock::new(); + impl ApiVersion { - pub fn from_swf_version(val: u8) -> Option { - match val { + pub fn to_valid_playerglobals_version(self, runtime: PlayerRuntime) -> ApiVersion { + // This maps an ApiVersion from our playerglobals SWF to the closest valid version, + // based on the active runtime. + // + // If our runtime is AIR, then we leave ApiVersion::AIR_* unchanged, and map + // ApiVersion::FP_* to the closest AIR version. This has the effect of exposing + // API versioned with an AIR-specific version, and also exposing all of the normal FP + // APIs that were included in that AIR release. + // + // If our runtime is FlashPlayer, then we leave ApiVersion::FP_* unchanged, and + // map ApiVersion::AIR_* to VM_INTERNAL. This hides all AIR-specific APIs when + // running in FlashPlayer. + // + // See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/api-versions.cpp#L63 + let active_series = TRANSFER_TABLE.get_or_init(|| { + enum_map! { + ApiVersion::AllVersions => (ApiVersion::AllVersions, ApiVersion::AllVersions), + ApiVersion::AIR_1_0 => (ApiVersion::AIR_1_0, ApiVersion::VM_INTERNAL), + ApiVersion::FP_10_0 => (ApiVersion::AIR_1_5, ApiVersion::FP_10_0), + ApiVersion::AIR_1_5 => (ApiVersion::AIR_1_5, ApiVersion::VM_INTERNAL), + ApiVersion::AIR_1_5_1 => (ApiVersion::AIR_1_5_1, ApiVersion::VM_INTERNAL), + ApiVersion::FP_10_0_32 => (ApiVersion::AIR_1_5_2, ApiVersion::FP_10_0_32), + ApiVersion::AIR_1_5_2 => (ApiVersion::AIR_1_5_2, ApiVersion::VM_INTERNAL), + ApiVersion::FP_10_1 => (ApiVersion::AIR_2_0, ApiVersion::FP_10_1), + ApiVersion::AIR_2_0 => (ApiVersion::AIR_2_0, ApiVersion::VM_INTERNAL), + ApiVersion::AIR_2_5 => (ApiVersion::AIR_2_5, ApiVersion::VM_INTERNAL), + ApiVersion::FP_10_2 => (ApiVersion::AIR_2_6, ApiVersion::FP_10_2), + ApiVersion::AIR_2_6 => (ApiVersion::AIR_2_6, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_12 => (ApiVersion::SWF_12, ApiVersion::SWF_12), + ApiVersion::AIR_2_7 => (ApiVersion::AIR_2_7, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_13 => (ApiVersion::AIR_3_0, ApiVersion::SWF_13), + ApiVersion::AIR_3_0 => (ApiVersion::AIR_3_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_14 => (ApiVersion::AIR_3_1, ApiVersion::SWF_14), + ApiVersion::AIR_3_1 => (ApiVersion::AIR_3_1, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_15 => (ApiVersion::AIR_3_2, ApiVersion::SWF_15), + ApiVersion::AIR_3_2 => (ApiVersion::AIR_3_2, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_16 => (ApiVersion::AIR_3_3, ApiVersion::SWF_16), + ApiVersion::AIR_3_3 => (ApiVersion::AIR_3_3, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_17 => (ApiVersion::AIR_3_4, ApiVersion::SWF_17), + ApiVersion::AIR_3_4 => (ApiVersion::AIR_3_4, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_18 => (ApiVersion::AIR_3_5, ApiVersion::SWF_18), + ApiVersion::AIR_3_5 => (ApiVersion::AIR_3_5, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_19 => (ApiVersion::AIR_3_6, ApiVersion::SWF_19), + ApiVersion::AIR_3_6 => (ApiVersion::AIR_3_6, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_20 => (ApiVersion::AIR_3_7, ApiVersion::SWF_20), + ApiVersion::AIR_3_7 => (ApiVersion::AIR_3_7, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_21 => (ApiVersion::AIR_3_8, ApiVersion::SWF_21), + ApiVersion::AIR_3_8 => (ApiVersion::AIR_3_8, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_22 => (ApiVersion::AIR_3_9, ApiVersion::SWF_22), + ApiVersion::AIR_3_9 => (ApiVersion::AIR_3_9, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_23 => (ApiVersion::AIR_4_0, ApiVersion::SWF_23), + ApiVersion::AIR_4_0 => (ApiVersion::AIR_4_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_24 => (ApiVersion::AIR_13_0, ApiVersion::SWF_24), + ApiVersion::AIR_13_0 => (ApiVersion::AIR_13_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_25 => (ApiVersion::AIR_14_0, ApiVersion::SWF_25), + ApiVersion::AIR_14_0 => (ApiVersion::AIR_14_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_26 => (ApiVersion::AIR_15_0, ApiVersion::SWF_26), + ApiVersion::AIR_15_0 => (ApiVersion::AIR_15_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_27 => (ApiVersion::AIR_16_0, ApiVersion::SWF_27), + ApiVersion::AIR_16_0 => (ApiVersion::AIR_16_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_28 => (ApiVersion::AIR_17_0, ApiVersion::SWF_28), + ApiVersion::AIR_17_0 => (ApiVersion::AIR_17_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_29 => (ApiVersion::AIR_18_0, ApiVersion::SWF_29), + ApiVersion::AIR_18_0 => (ApiVersion::AIR_18_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_30 => (ApiVersion::AIR_19_0, ApiVersion::SWF_30), + ApiVersion::AIR_19_0 => (ApiVersion::AIR_19_0, ApiVersion::VM_INTERNAL), + ApiVersion::SWF_31 => (ApiVersion::AIR_20_0, ApiVersion::SWF_31), + ApiVersion::AIR_20_0 => (ApiVersion::AIR_20_0, ApiVersion::VM_INTERNAL), + ApiVersion::VM_INTERNAL => (ApiVersion::VM_INTERNAL, ApiVersion::VM_INTERNAL), + } + })[self]; + + match runtime { + PlayerRuntime::AIR => active_series.0, + PlayerRuntime::FlashPlayer => active_series.1, + } + } + + pub fn from_swf_version(val: u8, runtime: PlayerRuntime) -> Option { + // Based on this table: https://github.com/ruffle-rs/ruffle/wiki/SWF-version-chart + match (val, runtime) { // There's no specific entry for SWF 9 in avmplus, // so map it to the lowest entry. - 9 => Some(ApiVersion::AllVersions), - 10 => Some(ApiVersion::FP_10_1), - 11 => Some(ApiVersion::FP_10_2), - 12 => Some(ApiVersion::SWF_12), - 13 => Some(ApiVersion::SWF_13), - 14 => Some(ApiVersion::SWF_14), - 15 => Some(ApiVersion::SWF_15), - 16 => Some(ApiVersion::SWF_16), - 17 => Some(ApiVersion::SWF_17), - 18 => Some(ApiVersion::SWF_18), - 19 => Some(ApiVersion::SWF_19), - 20 => Some(ApiVersion::SWF_20), - 21 => Some(ApiVersion::SWF_21), - 22 => Some(ApiVersion::SWF_22), - 23 => Some(ApiVersion::SWF_23), - 24 => Some(ApiVersion::SWF_24), - 25 => Some(ApiVersion::SWF_25), - 26 => Some(ApiVersion::SWF_26), - 27 => Some(ApiVersion::SWF_27), - 28 => Some(ApiVersion::SWF_28), - 29 => Some(ApiVersion::SWF_29), - 30 => Some(ApiVersion::SWF_30), - 31 => Some(ApiVersion::SWF_31), + (9, _) => Some(ApiVersion::AllVersions), + (10, PlayerRuntime::FlashPlayer) => Some(ApiVersion::FP_10_1), + (10, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_0), + (11, PlayerRuntime::FlashPlayer) => Some(ApiVersion::FP_10_2), + (11, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_6), + (12, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_12), + (12, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_7), + (13, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_13), + (13, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_0), + (14, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_14), + (14, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_1), + (15, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_15), + (15, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_2), + (16, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_16), + (16, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_3), + (17, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_17), + (17, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_4), + (18, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_18), + (18, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_5), + (19, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_19), + (19, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_6), + (20, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_20), + (20, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_7), + (21, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_21), + (21, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_8), + (22, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_22), + (22, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_9), + (23, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_23), + (23, PlayerRuntime::AIR) => Some(ApiVersion::AIR_4_0), + (24, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_24), + (24, PlayerRuntime::AIR) => Some(ApiVersion::AIR_13_0), + (25, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_25), + (25, PlayerRuntime::AIR) => Some(ApiVersion::AIR_14_0), + (26, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_26), + (26, PlayerRuntime::AIR) => Some(ApiVersion::AIR_15_0), + (27, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_27), + (27, PlayerRuntime::AIR) => Some(ApiVersion::AIR_16_0), + (28, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_28), + (28, PlayerRuntime::AIR) => Some(ApiVersion::AIR_17_0), + (29, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_29), + (29, PlayerRuntime::AIR) => Some(ApiVersion::AIR_18_0), + (30, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_30), + (30, PlayerRuntime::AIR) => Some(ApiVersion::AIR_19_0), + (31, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_31), + (31, PlayerRuntime::AIR) => Some(ApiVersion::AIR_20_0), + // We haven't yet created entries from higher versions - just map them // to the highest non-VM_INTERNAL version. - _ if val > 31 => Some(ApiVersion::SWF_31), + (32.., PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_31), + (32.., PlayerRuntime::AIR) => Some(ApiVersion::AIR_20_0), _ => None, } } diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 013666d02..8353512d1 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -702,7 +702,7 @@ fn load_playerglobal<'gc>( // Lookup with the highest version, so we we see all defined classes here let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, &mut activation.borrow_gc()); let name = QName::new(ns, $class_name); - let class_object = activation.domain().get_defined_value(activation, name)?; + let class_object = activation.domain().get_defined_value(activation, name).unwrap_or_else(|e| panic!("Failed to lookup {name:?}: {e:?}")); let class_object = class_object.as_object().unwrap().as_class_object().unwrap(); let sc = activation.avm2().system_classes.as_mut().unwrap(); sc.$field = class_object; diff --git a/core/src/avm2/globals/flash/net/DatagramSocket.as b/core/src/avm2/globals/flash/net/DatagramSocket.as new file mode 100644 index 000000000..215101cba --- /dev/null +++ b/core/src/avm2/globals/flash/net/DatagramSocket.as @@ -0,0 +1,6 @@ +package flash.net { + [API("668")] // AIR 2.0 + public class DatagramSocket { + + } +} \ No newline at end of file diff --git a/core/src/avm2/globals/globals.as b/core/src/avm2/globals/globals.as index 8861ecc7a..fb085fe7d 100644 --- a/core/src/avm2/globals/globals.as +++ b/core/src/avm2/globals/globals.as @@ -254,6 +254,7 @@ include "flash/media/VideoStreamSettings.as" include "flash/external/ExternalInterface.as" include "flash/net.as" +include "flash/net/DatagramSocket.as" include "flash/net/FileFilter.as" include "flash/net/FileReference.as" include "flash/net/FileReferenceList.as" diff --git a/core/src/avm2/namespace.rs b/core/src/avm2/namespace.rs index eae8b727f..e4821ca4f 100644 --- a/core/src/avm2/namespace.rs +++ b/core/src/avm2/namespace.rs @@ -107,9 +107,38 @@ impl<'gc> Namespace<'gc> { ))); } - // FIXME - what other versioned urls are there? - let is_versioned_url = |url: AvmAtom<'gc>| url.as_wstr().is_empty(); - + // FIXME - AvmCore gets this from an external source. I'm not exactly sure + // what the contents it, but it's probably all 'flash.*', 'air.*', etc. namespaces + // This is only ever used when parsing our playerglobals, so we just treat everything + // as versioned for now. As a result, any intra-playerglobal *references* that lack + // an explicit version marker will be treated as ApiVersion::VM_INTERNAL. + // The only exceptions are the 'AS3' ("http://adobe.com/AS3/2006/builtin") + // and "flash_proxy" (b"http://www.adobe.com/2006/actionscript/flash/proxy") namespaces. + // These are used by user code, and are not given version markers in playerglobals + // by the ASC compiler. As a result, we do not treat them as versioned, so that + // references from within playerglobals will use ApiVersion::AllVersions; + // + // For example, consider the AIR-only class `flash.net.DatagramSocket`. The class + // definition has version marker corresponding to an AIR-only version - when running + // the Flash Player runtime, we will map this to VM_INTERNAL in `ApiVersion::to_valid_playerglobals_version` + // (which hides it from user code). However, the playerglobal will still try to initialize this class via: + // + // ``` + // initproperty QName(PackageNamespace("flash.net"),"DatagramSocket") + // ``` + // + // This is a namespace without a version marker (the compiler only ever generates version + // markers in definitions, not references). As a result, we will treat this as a VM_INTERNAL + // which will allow `initproperty` to see the `flash.net.DatagramSocket` class definition, + // even when running as the FlashPlayer (not AIR) runtime. + // + // Outside of playerglobals, we'll tag all namespaces with a version based on the SWF version. + // This is always less than VM_INTERNAL, so AIR-only classes will be correctly hidden outside + // of playerglobals when using the FlashPlayer runtime. + let is_versioned_url = |url: AvmAtom<'gc>| { + url.as_wstr() != b"http://adobe.com/AS3/2006/builtin" + && url.as_wstr() != b"http://www.adobe.com/2006/actionscript/flash/proxy" + }; let is_public = matches!( abc_namespace, AbcNamespace::Namespace(_) | AbcNamespace::Package(_) @@ -137,6 +166,11 @@ impl<'gc> Namespace<'gc> { if !has_version_mark && is_public && is_versioned_url(namespace_name) { api_version = ApiVersion::VM_INTERNAL; } + // In avmplus, this conversion is done later in in 'getValidApiVersion' + // However, there's no reason to hold on to invalid API versions for the + // current active series (player runtime), so let's just do the conversion immediately. + api_version = + api_version.to_valid_playerglobals_version(context.avm2.player_runtime); } else if is_public { api_version = translation_unit.api_version(context.avm2); }; diff --git a/core/src/avm2/script.rs b/core/src/avm2/script.rs index e1d891b57..8951313f0 100644 --- a/core/src/avm2/script.rs +++ b/core/src/avm2/script.rs @@ -16,6 +16,7 @@ use crate::avm2::{Avm2, Error}; use crate::context::{GcContext, UpdateContext}; use crate::string::{AvmAtom, AvmString}; use crate::tag_utils::SwfMovie; +use crate::PlayerRuntime; use gc_arena::{Collect, Gc, GcCell, Mutation}; use std::cell::Ref; use std::mem::drop; @@ -135,7 +136,10 @@ impl<'gc> TranslationUnit<'gc> { pub fn api_version(self, avm2: &Avm2<'gc>) -> ApiVersion { if self.domain().is_playerglobals_domain(avm2) { // FIXME: get this from the player version we're emulating - ApiVersion::SWF_31 + match avm2.player_runtime { + PlayerRuntime::FlashPlayer => ApiVersion::SWF_31, + PlayerRuntime::AIR => ApiVersion::AIR_20_0, + } } else { avm2.root_api_version } diff --git a/core/src/player.rs b/core/src/player.rs index 25e54dd01..ccaa41c13 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -57,6 +57,7 @@ use ruffle_render::commands::CommandList; use ruffle_render::quality::StageQuality; use ruffle_render::transform::TransformStack; use ruffle_video::backend::VideoBackend; +use serde::Deserialize; use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::ops::DerefMut; @@ -392,8 +393,11 @@ impl Player { self.mutate_with_update_context(|context| { if context.swf.is_action_script_3() { - context.avm2.root_api_version = ApiVersion::from_swf_version(context.swf.version()) - .unwrap_or_else(|| panic!("Unknown SWF version {}", context.swf.version())); + context.avm2.root_api_version = ApiVersion::from_swf_version( + context.swf.version(), + context.avm2.player_runtime, + ) + .unwrap_or_else(|| panic!("Unknown SWF version {}", context.swf.version())); } context.stage.set_movie_size( @@ -2432,6 +2436,7 @@ impl PlayerBuilder { fn create_gc_root<'gc>( gc_context: &'gc gc_arena::Mutation<'gc>, player_version: u8, + player_runtime: PlayerRuntime, fullscreen: bool, fake_movie: Arc, external_interface_providers: Vec>, @@ -2452,7 +2457,7 @@ impl PlayerBuilder { audio_manager: AudioManager::new(), action_queue: ActionQueue::new(), avm1: Avm1::new(&mut init, player_version), - avm2: Avm2::new(&mut init, player_version), + avm2: Avm2::new(&mut init, player_version, player_runtime), interner, current_context_menu: None, drag_object: None, @@ -2572,6 +2577,7 @@ impl PlayerBuilder { Self::create_gc_root( gc_context, player_version, + self.player_runtime, self.fullscreen, fake_movie.clone(), self.external_interface_providers, @@ -2682,7 +2688,7 @@ fn run_mouse_pick<'gc>( } #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] -#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Deserialize)] pub enum PlayerRuntime { #[default] FlashPlayer, diff --git a/desktop/src/player.rs b/desktop/src/player.rs index 9b66e4dbb..53ebe3f49 100644 --- a/desktop/src/player.rs +++ b/desktop/src/player.rs @@ -166,6 +166,7 @@ impl ActivePlayer { .with_spoofed_url(opt.spoof_url.clone().map(|url| url.to_string())) .with_page_url(opt.spoof_url.clone().map(|url| url.to_string())) .with_player_version(Some(opt.player_version)) + .with_player_runtime(opt.player_runtime) .with_frame_rate(opt.frame_rate); let player = builder.build(); diff --git a/tests/README.md b/tests/README.md index 7807e037c..fe263c3a3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -40,6 +40,7 @@ viewport_dimensions = { width = 100, height = 100, scale_factor = 1 } # The size with_renderer = { optional = false, sample_count = 4, exclude_warp = false } # If this test requires a renderer to run. Optional will enable the renderer where available. with_audio = false # If this test requires an audio backend to run. with_video = false # If this test requires a video decoder backend to run. +runtime = "AIR" # The runtime to emulate ("FlashPlayer" or "AIR"). Defaults to "FlashPlayer" # A list of image comparisons to perform during the test. This block is repeatable infinitely, as long as each name is unique. # The comparison part of a test is optional and only runs when `imgtests` feature is enabled diff --git a/tests/framework/src/options.rs b/tests/framework/src/options.rs index 956837ac7..3aaa19dbd 100644 --- a/tests/framework/src/options.rs +++ b/tests/framework/src/options.rs @@ -7,7 +7,7 @@ use approx::assert_relative_eq; use image::ImageOutputFormat; use regex::Regex; use ruffle_core::tag_utils::SwfMovie; -use ruffle_core::{PlayerBuilder, ViewportDimensions}; +use ruffle_core::{PlayerBuilder, PlayerRuntime, ViewportDimensions}; use ruffle_render::backend::RenderBackend; use ruffle_render::quality::StageQuality; use serde::Deserialize; @@ -135,6 +135,7 @@ pub struct PlayerOptions { with_renderer: Option, with_audio: bool, with_video: bool, + runtime: PlayerRuntime, } impl PlayerOptions { @@ -157,6 +158,8 @@ impl PlayerOptions { player_builder = player_builder.with_audio(TestAudioBackend::default()); } + player_builder = player_builder.with_player_runtime(self.runtime); + #[cfg(feature = "ruffle_video_software")] if self.with_video { use ruffle_video_software::backend::SoftwareVideoBackend; diff --git a/tests/tests/swfs/avm2/air_datagram_socket/Test.as b/tests/tests/swfs/avm2/air_datagram_socket/Test.as new file mode 100755 index 000000000..462418d40 --- /dev/null +++ b/tests/tests/swfs/avm2/air_datagram_socket/Test.as @@ -0,0 +1,18 @@ +package { + + import flash.display.MovieClip; + + + public class Test extends MovieClip { + + + public function Test() { + } + } + +} + +import flash.net.DatagramSocket; +var socket = new DatagramSocket(); +// TODO - test functionally once we implement it in Ruffle +trace("Socket: " + socket); \ No newline at end of file diff --git a/tests/tests/swfs/avm2/air_datagram_socket/output.txt b/tests/tests/swfs/avm2/air_datagram_socket/output.txt new file mode 100644 index 000000000..8db90f2c8 --- /dev/null +++ b/tests/tests/swfs/avm2/air_datagram_socket/output.txt @@ -0,0 +1 @@ +Socket: [object DatagramSocket] diff --git a/tests/tests/swfs/avm2/air_datagram_socket/test.fla b/tests/tests/swfs/avm2/air_datagram_socket/test.fla new file mode 100755 index 000000000..393eece5c Binary files /dev/null and b/tests/tests/swfs/avm2/air_datagram_socket/test.fla differ diff --git a/tests/tests/swfs/avm2/air_datagram_socket/test.swf b/tests/tests/swfs/avm2/air_datagram_socket/test.swf new file mode 100755 index 000000000..6ce54b971 Binary files /dev/null and b/tests/tests/swfs/avm2/air_datagram_socket/test.swf differ diff --git a/tests/tests/swfs/avm2/air_datagram_socket/test.toml b/tests/tests/swfs/avm2/air_datagram_socket/test.toml new file mode 100644 index 000000000..cfdb67e51 --- /dev/null +++ b/tests/tests/swfs/avm2/air_datagram_socket/test.toml @@ -0,0 +1,4 @@ +num_ticks = 1 + +[player_options] +runtime = "AIR" \ No newline at end of file diff --git a/tests/tests/swfs/avm2/air_hidden_lookup/Test.as b/tests/tests/swfs/avm2/air_hidden_lookup/Test.as new file mode 100755 index 000000000..12bf9e5fb --- /dev/null +++ b/tests/tests/swfs/avm2/air_hidden_lookup/Test.as @@ -0,0 +1,26 @@ +package { + + import flash.display.MovieClip; + + + public class Test extends MovieClip { + + + public function Test() { + } + } + +} + +import flash.utils.getDefinitionByName; + +const HIDDEN_CLASSES: Array = ["flash.net.DatagramSocket"]; + +for each (var klass in HIDDEN_CLASSES) { + try { + var obj = getDefinitionByName(klass); + trace("ERROR: " + klass + " is accessible from Flash Player"); + } catch (e) { + trace(klass + " is inaccessible from Flash Player"); + } +} \ No newline at end of file diff --git a/tests/tests/swfs/avm2/air_hidden_lookup/output.txt b/tests/tests/swfs/avm2/air_hidden_lookup/output.txt new file mode 100644 index 000000000..7a3ab4c3f --- /dev/null +++ b/tests/tests/swfs/avm2/air_hidden_lookup/output.txt @@ -0,0 +1 @@ +flash.net.DatagramSocket is inaccessible from Flash Player diff --git a/tests/tests/swfs/avm2/air_hidden_lookup/test.fla b/tests/tests/swfs/avm2/air_hidden_lookup/test.fla new file mode 100755 index 000000000..03cd82dde Binary files /dev/null and b/tests/tests/swfs/avm2/air_hidden_lookup/test.fla differ diff --git a/tests/tests/swfs/avm2/air_hidden_lookup/test.swf b/tests/tests/swfs/avm2/air_hidden_lookup/test.swf new file mode 100755 index 000000000..2b5a81008 Binary files /dev/null and b/tests/tests/swfs/avm2/air_hidden_lookup/test.swf differ diff --git a/tests/tests/swfs/avm2/air_hidden_lookup/test.toml b/tests/tests/swfs/avm2/air_hidden_lookup/test.toml new file mode 100644 index 000000000..67f15e863 --- /dev/null +++ b/tests/tests/swfs/avm2/air_hidden_lookup/test.toml @@ -0,0 +1 @@ +num_ticks = 1 \ No newline at end of file