diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 9ec264b9fc1f4..6b8da2c17248f 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -30,7 +30,6 @@ pub use flv::FlvValueAvm1Ext; pub use function::{Executable, ExecutionReason}; pub use globals::context_menu::make_context_menu_state; pub use globals::sound::start as start_sound; -pub use globals::system::SystemProperties; pub use object::array_object::ArrayObject; pub use object::script_object::ScriptObject; pub use object::stage_object::StageObject; diff --git a/core/src/avm1/globals/system.rs b/core/src/avm1/globals/system.rs index aaf0e3759ad41..3f63cf1a73bda 100644 --- a/core/src/avm1/globals/system.rs +++ b/core/src/avm1/globals/system.rs @@ -2,13 +2,9 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; use crate::avm1::object::Object; use crate::avm1::property_decl::{define_properties_on, Declaration}; -use crate::avm1::runtime::Avm1; use crate::avm1::{ScriptObject, Value}; use crate::avm1_stub; -use crate::context::UpdateContext; use crate::string::StringContext; -use bitflags::bitflags; -use core::fmt; const OBJECT_DECLS: &[Declaration] = declare_properties! { "exactSettings" => property(get_exact_settings, set_exact_settings); @@ -19,188 +15,6 @@ const OBJECT_DECLS: &[Declaration] = declare_properties! { "onStatus" => method(on_status); }; -/// Available cpu architectures -#[allow(dead_code)] -pub enum CpuArchitecture { - PowerPc, - X86, - Sparc, - Arm, -} - -impl fmt::Display for CpuArchitecture { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - CpuArchitecture::PowerPc => "PowerPC", - CpuArchitecture::X86 => "x86", - CpuArchitecture::Sparc => "SPARC", - CpuArchitecture::Arm => "ARM", - }) - } -} - -/// The available host operating systems -#[allow(dead_code)] -pub enum OperatingSystem { - WindowsXp, - Windows2k, - WindowsNt, - Windows98, - Windows95, - WindowsCe, - WindowsUnknown, - Linux, - MacOs, -} - -impl fmt::Display for OperatingSystem { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(match self { - OperatingSystem::WindowsXp => "Windows XP", - OperatingSystem::Windows2k => "Windows 2000", - OperatingSystem::WindowsNt => "Windows NT", - OperatingSystem::Windows98 => "Windows 98/ME", - OperatingSystem::Windows95 => "Windows 95", - OperatingSystem::WindowsCe => "Windows CE", - OperatingSystem::WindowsUnknown => "Windows", - OperatingSystem::Linux => "Linux", - OperatingSystem::MacOs => "MacOS", - }) - } -} - -/// The available player manufacturers -#[allow(dead_code)] -pub enum Manufacturer { - Windows, - Macintosh, - Linux, - Other(String), -} - -impl Manufacturer { - pub fn get_manufacturer_string(&self, version: u8) -> String { - let os_part = match self { - Manufacturer::Windows => "Windows", - Manufacturer::Macintosh => "Macintosh", - Manufacturer::Linux => "Linux", - Manufacturer::Other(name) => name.as_str(), - }; - - if version <= 8 { - format!("Macromedia {os_part}") - } else { - format!("Adobe {os_part}") - } - } - - pub fn get_platform_name(&self) -> &str { - match self { - Manufacturer::Windows => "WIN", - Manufacturer::Macintosh => "MAC", - Manufacturer::Linux => "LNX", - _ => "", - } - } -} - -/// The language of the host os -#[allow(dead_code)] -pub enum Language { - Czech, - Danish, - Dutch, - English, - Finnish, - French, - German, - Hungarian, - Italian, - Japanese, - Korean, - Norwegian, - Unknown, - Polish, - Portuguese, - Russian, - SimplifiedChinese, - Spanish, - Swedish, - TraditionalChinese, - Turkish, -} - -impl Language { - pub fn get_language_code(&self, player_version: u8) -> &str { - match self { - Language::Czech => "cs", - Language::Danish => "da", - Language::Dutch => "nl", - Language::English => { - if player_version < 7 { - "en-US" - } else { - "en" - } - } - Language::Finnish => "fi", - Language::French => "fr", - Language::German => "de", - Language::Hungarian => "hu", - Language::Italian => "it", - Language::Japanese => "ja", - Language::Korean => "ko", - Language::Norwegian => "no", - Language::Unknown => "xu", - Language::Polish => "pl", - Language::Portuguese => "pt", - Language::Russian => "ru", - Language::SimplifiedChinese => "zh-CN", - Language::Spanish => "es", - Language::Swedish => "sv", - Language::TraditionalChinese => "zh-TW", - Language::Turkish => "tr", - } - } -} - -/// The supported colors of the screen -#[allow(dead_code)] -pub enum ScreenColor { - Color, - Gray, - BlackWhite, -} - -impl fmt::Display for ScreenColor { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(match self { - ScreenColor::Color => "color", - ScreenColor::Gray => "gray", - ScreenColor::BlackWhite => "bw", - }) - } -} -/// The type of the player -#[allow(dead_code)] -pub enum PlayerType { - StandAlone, - External, - PlugIn, - ActiveX, -} - -impl fmt::Display for PlayerType { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(match self { - PlayerType::StandAlone => "StandAlone", - PlayerType::External => "External", - PlayerType::PlugIn => "PlugIn", - PlayerType::ActiveX => "ActiveX", - }) - } -} - #[derive(Debug, Copy, Clone, FromPrimitive)] enum SettingsPanel { Privacy = 0, @@ -215,198 +29,6 @@ impl SettingsPanel { } } -bitflags! { - pub struct SystemCapabilities: u32 { - const AV_HARDWARE = 1 << 0; - const ACCESSIBILITY = 1 << 1; - const AUDIO = 1 << 2; - const AUDIO_ENCODER = 1 << 3; - const EMBEDDED_VIDEO = 1 << 4; - const IME = 1 << 5; - const MP3 = 1 << 6; - const PRINTING = 1 << 7; - const SCREEN_BROADCAST = 1 << 8; - const SCREEN_PLAYBACK = 1 << 9; - const STREAMING_AUDIO = 1 << 10; - const STREAMING_VIDEO = 1 << 11; - const VIDEO_ENCODER = 1 << 12; - const DEBUGGER = 1 << 13; - const LOCAL_FILE_READ = 1 << 14; - const PROCESS_64_BIT = 1 << 15; - const PROCESS_32_BIT = 1 << 16; - const ACROBAT_EMBEDDED = 1 << 17; - const TLS = 1 << 18; - const WINDOW_LESS = 1 << 19; - } -} - -/// The properties modified by 'System' -pub struct SystemProperties { - /// If true then settings should be saved and read from the exact same domain of the player - /// If false then they should be saved to the super domain - pub exact_settings: bool, - /// If true, the system codepage should be used for text files - /// If false, UTF-8 should be used for SWF version >= 6 and ISO Latin-1 for SWF version <= 5 - pub use_codepage: bool, - /// The capabilities of the player - pub capabilities: SystemCapabilities, - /// The type of the player - pub player_type: PlayerType, - /// The type of screen available to the player - pub screen_color: ScreenColor, - /// The aspect ratio of the screens pixels - pub pixel_aspect_ratio: f32, - /// The dpi of the screen - pub dpi: f32, - /// The language of the host os - pub language: Language, - /// The manufacturer of the player - pub manufacturer: Manufacturer, - /// The os of the host - pub os: OperatingSystem, - /// The cpu architecture of the platform - pub cpu_architecture: CpuArchitecture, - /// The highest supported h264 decoder level - pub idc_level: String, -} - -impl Default for SystemProperties { - fn default() -> Self { - Self::new() - } -} - -impl SystemProperties { - pub fn new() -> Self { - SystemProperties { - //TODO: default to true on fp>=7, false <= 6 - exact_settings: true, - //TODO: default to false on fp>=7, true <= 6 - use_codepage: false, - capabilities: SystemCapabilities::empty(), - player_type: PlayerType::StandAlone, - screen_color: ScreenColor::Color, - // TODO: note for fp <7 this should be the locale and the ui lang for >= 7, on windows - language: Language::English, - // source: https://web.archive.org/web/20230611050355/https://flylib.com/books/en/4.13.1.272/1/ - pixel_aspect_ratio: 1_f32, - // source: https://tracker.adobe.com/#/view/FP-3949775 - dpi: 72_f32, - manufacturer: Manufacturer::Linux, - os: OperatingSystem::Linux, - cpu_architecture: CpuArchitecture::X86, - idc_level: "5.1".into(), - } - } - pub fn get_version_string(&self, avm: &mut Avm1) -> String { - format!( - "{} {},0,0,0", - self.manufacturer.get_platform_name(), - avm.player_version() - ) - } - - pub fn has_capability(&self, cap: SystemCapabilities) -> bool { - self.capabilities.contains(cap) - } - - fn encode_capability(&self, cap: SystemCapabilities) -> &str { - if self.has_capability(cap) { - "t" - } else { - "f" - } - } - - fn encode_not_capability(&self, cap: SystemCapabilities) -> &str { - if self.has_capability(cap) { - "f" - } else { - "t" - } - } - - fn encode_string(&self, s: &str) -> String { - percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).to_string() - } - - pub fn get_server_string(&self, context: &UpdateContext) -> String { - let viewport_dimensions = context.renderer.viewport_dimensions(); - url::form_urlencoded::Serializer::new(String::new()) - .append_pair("A", self.encode_capability(SystemCapabilities::AUDIO)) - .append_pair( - "SA", - self.encode_capability(SystemCapabilities::STREAMING_AUDIO), - ) - .append_pair( - "SV", - self.encode_capability(SystemCapabilities::STREAMING_VIDEO), - ) - .append_pair( - "EV", - self.encode_capability(SystemCapabilities::EMBEDDED_VIDEO), - ) - .append_pair("MP3", self.encode_capability(SystemCapabilities::MP3)) - .append_pair( - "AE", - self.encode_capability(SystemCapabilities::AUDIO_ENCODER), - ) - .append_pair( - "VE", - self.encode_capability(SystemCapabilities::VIDEO_ENCODER), - ) - .append_pair( - "ACC", - self.encode_not_capability(SystemCapabilities::ACCESSIBILITY), - ) - .append_pair("PR", self.encode_capability(SystemCapabilities::PRINTING)) - .append_pair( - "SP", - self.encode_capability(SystemCapabilities::SCREEN_PLAYBACK), - ) - .append_pair( - "SB", - self.encode_capability(SystemCapabilities::SCREEN_BROADCAST), - ) - .append_pair("DEB", self.encode_capability(SystemCapabilities::DEBUGGER)) - .append_pair( - "M", - &self.encode_string( - self.manufacturer - .get_manufacturer_string(context.avm1.player_version()) - .as_str(), - ), - ) - .append_pair( - "R", - &format!( - "{}x{}", - viewport_dimensions.width, viewport_dimensions.height - ), - ) - .append_pair("COL", &self.screen_color.to_string()) - .append_pair("AR", &self.pixel_aspect_ratio.to_string()) - .append_pair("OS", &self.encode_string(&self.os.to_string())) - .append_pair( - "L", - self.language - .get_language_code(context.avm1.player_version()), - ) - .append_pair("IME", self.encode_capability(SystemCapabilities::IME)) - .append_pair("PT", &self.player_type.to_string()) - .append_pair( - "AVD", - self.encode_not_capability(SystemCapabilities::AV_HARDWARE), - ) - .append_pair( - "LFD", - self.encode_not_capability(SystemCapabilities::LOCAL_FILE_READ), - ) - .append_pair("DP", &self.dpi.to_string()) - .finish() - } -} - pub fn set_clipboard<'gc>( activation: &mut Activation<'_, 'gc>, _this: Object<'gc>, diff --git a/core/src/avm1/globals/system_capabilities.rs b/core/src/avm1/globals/system_capabilities.rs index a492eeb512e3b..8a8d881de3414 100644 --- a/core/src/avm1/globals/system_capabilities.rs +++ b/core/src/avm1/globals/system_capabilities.rs @@ -1,10 +1,10 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; -use crate::avm1::globals::system::SystemCapabilities; use crate::avm1::object::Object; use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::{ScriptObject, Value}; use crate::string::{AvmString, StringContext}; +use crate::system_properties::SystemCapabilities; const OBJECT_DECLS: &[Declaration] = declare_properties! { "supports64BitProcesses" => property(get_has_64_bit_support); @@ -132,7 +132,7 @@ pub fn get_language<'gc>( .context .system .language - .get_language_code(activation.context.avm1.player_version()), + .get_language_code(activation.context.player_version), ) .into()) } @@ -186,7 +186,7 @@ pub fn get_manufacturer<'gc>( .context .system .manufacturer - .get_manufacturer_string(activation.context.avm1.player_version()), + .get_manufacturer_string(activation.context.player_version), ) .into()) } @@ -209,7 +209,7 @@ pub fn get_version<'gc>( activation .context .system - .get_version_string(activation.context.avm1), + .get_version_string(activation.context.player_version), ) .into()) } diff --git a/core/src/avm2/globals/flash/system/Capabilities.as b/core/src/avm2/globals/flash/system/Capabilities.as index 96fe83290b3b6..e6febebf06f1b 100644 --- a/core/src/avm2/globals/flash/system/Capabilities.as +++ b/core/src/avm2/globals/flash/system/Capabilities.as @@ -8,14 +8,11 @@ package flash.system { public native static function get screenResolutionY():Number; public native static function get pixelAspectRatio():Number; public native static function get screenDPI():Number; + public native static function get language(): String; public static function get manufacturer(): String { stub_getter("flash.system.Capabilities", "manufacturer"); return "Adobe Windows" } - public static function get language(): String { - stub_getter("flash.system.Capabilities", "language"); - return "en" - } public static function get isDebugger(): Boolean { return false } diff --git a/core/src/avm2/globals/flash/system/capabilities.rs b/core/src/avm2/globals/flash/system/capabilities.rs index ca98426d3b37a..f5a989cd8a22f 100644 --- a/core/src/avm2/globals/flash/system/capabilities.rs +++ b/core/src/avm2/globals/flash/system/capabilities.rs @@ -113,3 +113,17 @@ pub fn get_screen_dpi<'gc>( // source: https://tracker.adobe.com/#/view/FP-3949775 Ok(72.into()) } + +/// Implements `flash.system.Capabilities.language` +pub fn get_language<'gc>( + activation: &mut Activation<'_, 'gc>, + _this: Value<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let language = activation + .context + .system + .language + .get_language_code(activation.context.player_version); + Ok(AvmString::new_utf8(activation.gc(), language).into()) +} diff --git a/core/src/context.rs b/core/src/context.rs index acae440afdab3..d72b3aff71fe6 100644 --- a/core/src/context.rs +++ b/core/src/context.rs @@ -5,7 +5,6 @@ use crate::avm1::ActivationIdentifier; use crate::avm1::Attribute; use crate::avm1::Avm1; use crate::avm1::ScriptObject; -use crate::avm1::SystemProperties; use crate::avm1::TObject; use crate::avm1::{Object as Avm1Object, Value as Avm1Value}; use crate::avm2::api_version::ApiVersion; @@ -38,6 +37,7 @@ use crate::streams::StreamManager; use crate::string::HasStringContext; use crate::string::{AvmString, StringContext}; use crate::stub::StubCollection; +use crate::system_properties::SystemProperties; use crate::tag_utils::{SwfMovie, SwfSlice}; use crate::timer::Timers; use crate::vminterface::Instantiator; @@ -419,7 +419,7 @@ impl<'gc> UpdateContext<'gc> { let version_string = activation .context .system - .get_version_string(activation.context.avm1); + .get_version_string(activation.context.player_version); object.define_value( activation.gc(), AvmString::new_ascii_static(activation.gc(), b"$version"), diff --git a/core/src/lib.rs b/core/src/lib.rs index c37bd6fbae7cf..ea260cfc88edc 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -48,6 +48,7 @@ pub mod sandbox; pub mod socket; mod streams; pub mod string; +mod system_properties; pub mod tag_utils; pub mod timer; mod types; diff --git a/core/src/player.rs b/core/src/player.rs index c74f5a33c512c..874f76480a53d 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -1,7 +1,6 @@ use crate::avm1::Attribute; use crate::avm1::Avm1; use crate::avm1::Object; -use crate::avm1::SystemProperties; use crate::avm1::VariableDumper; use crate::avm1::{Activation, ActivationIdentifier}; use crate::avm1::{TObject, Value}; @@ -45,6 +44,7 @@ use crate::socket::Sockets; use crate::streams::StreamManager; use crate::string::{AvmStringInterner, StringContext}; use crate::stub::StubCollection; +use crate::system_properties::SystemProperties; use crate::tag_utils::SwfMovie; use crate::timer::Timers; use crate::vminterface::Instantiator; @@ -2826,6 +2826,7 @@ impl PlayerBuilder { .unwrap_or_else(|| Box::new(null::NullVideoBackend::new())); let player_version = self.player_version.unwrap_or(NEWEST_PLAYER_VERSION); + let language = ui.language(); // Instantiate the player. let fake_movie = Arc::new(SwfMovie::empty(player_version)); @@ -2867,7 +2868,7 @@ impl PlayerBuilder { // Misc. state rng: SmallRng::seed_from_u64(get_current_date_time().timestamp_millis() as u64), - system: SystemProperties::new(), + system: SystemProperties::new(language), page_url: self.page_url.clone(), transform_stack: TransformStack::new(), instance_counter: 0, diff --git a/core/src/system_properties.rs b/core/src/system_properties.rs new file mode 100644 index 0000000000000..bd743a275e569 --- /dev/null +++ b/core/src/system_properties.rs @@ -0,0 +1,402 @@ +use crate::context::UpdateContext; +use bitflags::bitflags; +use core::fmt; +use fluent_templates::{langid, LanguageIdentifier}; + +/// Available cpu architectures +#[allow(dead_code)] +pub enum CpuArchitecture { + PowerPc, + X86, + Sparc, + Arm, +} + +impl fmt::Display for CpuArchitecture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + CpuArchitecture::PowerPc => "PowerPC", + CpuArchitecture::X86 => "x86", + CpuArchitecture::Sparc => "SPARC", + CpuArchitecture::Arm => "ARM", + }) + } +} + +/// The available host operating systems +#[allow(dead_code)] +pub enum OperatingSystem { + WindowsXp, + Windows2k, + WindowsNt, + Windows98, + Windows95, + WindowsCe, + WindowsUnknown, + Linux, + MacOs, +} + +impl fmt::Display for OperatingSystem { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(match self { + OperatingSystem::WindowsXp => "Windows XP", + OperatingSystem::Windows2k => "Windows 2000", + OperatingSystem::WindowsNt => "Windows NT", + OperatingSystem::Windows98 => "Windows 98/ME", + OperatingSystem::Windows95 => "Windows 95", + OperatingSystem::WindowsCe => "Windows CE", + OperatingSystem::WindowsUnknown => "Windows", + OperatingSystem::Linux => "Linux", + OperatingSystem::MacOs => "MacOS", + }) + } +} + +/// The available player manufacturers +#[allow(dead_code)] +pub enum Manufacturer { + Windows, + Macintosh, + Linux, + Other(String), +} + +impl Manufacturer { + pub fn get_manufacturer_string(&self, version: u8) -> String { + let os_part = match self { + Manufacturer::Windows => "Windows", + Manufacturer::Macintosh => "Macintosh", + Manufacturer::Linux => "Linux", + Manufacturer::Other(name) => name.as_str(), + }; + + if version <= 8 { + format!("Macromedia {os_part}") + } else { + format!("Adobe {os_part}") + } + } + + pub fn get_platform_name(&self) -> &str { + match self { + Manufacturer::Windows => "WIN", + Manufacturer::Macintosh => "MAC", + Manufacturer::Linux => "LNX", + _ => "", + } + } +} + +/// The language of the host os +pub enum Language { + Czech, + Danish, + Dutch, + English, + Finnish, + French, + German, + Hungarian, + Italian, + Japanese, + Korean, + Norwegian, + Unknown, + Polish, + Portuguese, + Russian, + SimplifiedChinese, + Spanish, + Swedish, + TraditionalChinese, + Turkish, +} + +impl Language { + pub fn get_language_code(&self, player_version: u8) -> &str { + match self { + Language::Czech => "cs", + Language::Danish => "da", + Language::Dutch => "nl", + Language::English => { + if player_version < 7 { + "en-US" + } else { + "en" + } + } + Language::Finnish => "fi", + Language::French => "fr", + Language::German => "de", + Language::Hungarian => "hu", + Language::Italian => "it", + Language::Japanese => "ja", + Language::Korean => "ko", + Language::Norwegian => "no", + Language::Unknown => "xu", + Language::Polish => "pl", + Language::Portuguese => "pt", + Language::Russian => "ru", + Language::SimplifiedChinese => "zh-CN", + Language::Spanish => "es", + Language::Swedish => "sv", + Language::TraditionalChinese => "zh-TW", + Language::Turkish => "tr", + } + } +} + +impl From for Language { + fn from(li: LanguageIdentifier) -> Self { + match li.language.as_str() { + "da" => Language::Danish, + "nl" => Language::Dutch, + "en" => Language::English, + "fi" => Language::Finnish, + "fr" => Language::French, + "de" => Language::German, + "hu" => Language::Hungarian, + "it" => Language::Italian, + "ja" => Language::Japanese, + "ko" => Language::Korean, + "no" => Language::Norwegian, + "und" => Language::Unknown, + "pl" => Language::Polish, + "pt" => Language::Portuguese, + "ru" => Language::Russian, + "zh" => { + if li == langid!("zh-TW") { + Language::TraditionalChinese + } else { + Language::SimplifiedChinese + } + } + "es" => Language::Spanish, + "sv" => Language::Swedish, + "tr" => Language::Turkish, + // Fallback to English instead of Unknown for better compatibility. + _ => Language::English, + } + } +} + +/// The supported colors of the screen +#[allow(dead_code)] +pub enum ScreenColor { + Color, + Gray, + BlackWhite, +} + +impl fmt::Display for ScreenColor { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(match self { + ScreenColor::Color => "color", + ScreenColor::Gray => "gray", + ScreenColor::BlackWhite => "bw", + }) + } +} +/// The type of the player +#[allow(dead_code)] +pub enum PlayerType { + StandAlone, + External, + PlugIn, + ActiveX, +} + +impl fmt::Display for PlayerType { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(match self { + PlayerType::StandAlone => "StandAlone", + PlayerType::External => "External", + PlayerType::PlugIn => "PlugIn", + PlayerType::ActiveX => "ActiveX", + }) + } +} + +bitflags! { + pub struct SystemCapabilities: u32 { + const AV_HARDWARE = 1 << 0; + const ACCESSIBILITY = 1 << 1; + const AUDIO = 1 << 2; + const AUDIO_ENCODER = 1 << 3; + const EMBEDDED_VIDEO = 1 << 4; + const IME = 1 << 5; + const MP3 = 1 << 6; + const PRINTING = 1 << 7; + const SCREEN_BROADCAST = 1 << 8; + const SCREEN_PLAYBACK = 1 << 9; + const STREAMING_AUDIO = 1 << 10; + const STREAMING_VIDEO = 1 << 11; + const VIDEO_ENCODER = 1 << 12; + const DEBUGGER = 1 << 13; + const LOCAL_FILE_READ = 1 << 14; + const PROCESS_64_BIT = 1 << 15; + const PROCESS_32_BIT = 1 << 16; + const ACROBAT_EMBEDDED = 1 << 17; + const TLS = 1 << 18; + const WINDOW_LESS = 1 << 19; + } +} + +/// The properties modified by 'System' +pub struct SystemProperties { + /// If true then settings should be saved and read from the exact same domain of the player + /// If false then they should be saved to the super domain + pub exact_settings: bool, + /// If true, the system codepage should be used for text files + /// If false, UTF-8 should be used for SWF version >= 6 and ISO Latin-1 for SWF version <= 5 + pub use_codepage: bool, + /// The capabilities of the player + pub capabilities: SystemCapabilities, + /// The type of the player + pub player_type: PlayerType, + /// The type of screen available to the player + pub screen_color: ScreenColor, + /// The aspect ratio of the screens pixels + pub pixel_aspect_ratio: f32, + /// The dpi of the screen + pub dpi: f32, + /// The language of the host os + pub language: Language, + /// The manufacturer of the player + pub manufacturer: Manufacturer, + /// The os of the host + pub os: OperatingSystem, + /// The cpu architecture of the platform + pub cpu_architecture: CpuArchitecture, + /// The highest supported h264 decoder level + pub idc_level: String, +} + +impl SystemProperties { + pub fn new(language: LanguageIdentifier) -> Self { + SystemProperties { + //TODO: default to true on fp>=7, false <= 6 + exact_settings: true, + //TODO: default to false on fp>=7, true <= 6 + use_codepage: false, + capabilities: SystemCapabilities::empty(), + player_type: PlayerType::StandAlone, + screen_color: ScreenColor::Color, + // TODO: note for fp <7 this should be the locale and the ui lang for >= 7, on windows + language: language.into(), + // source: https://web.archive.org/web/20230611050355/https://flylib.com/books/en/4.13.1.272/1/ + pixel_aspect_ratio: 1_f32, + // source: https://tracker.adobe.com/#/view/FP-3949775 + dpi: 72_f32, + manufacturer: Manufacturer::Linux, + os: OperatingSystem::Linux, + cpu_architecture: CpuArchitecture::X86, + idc_level: "5.1".into(), + } + } + + pub fn get_version_string(&self, player_version: u8) -> String { + format!( + "{} {},0,0,0", + self.manufacturer.get_platform_name(), + player_version + ) + } + + pub fn has_capability(&self, cap: SystemCapabilities) -> bool { + self.capabilities.contains(cap) + } + + fn encode_capability(&self, cap: SystemCapabilities) -> &str { + if self.has_capability(cap) { + "t" + } else { + "f" + } + } + + fn encode_not_capability(&self, cap: SystemCapabilities) -> &str { + if self.has_capability(cap) { + "f" + } else { + "t" + } + } + + fn encode_string(&self, s: &str) -> String { + percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).to_string() + } + + pub fn get_server_string(&self, context: &UpdateContext) -> String { + let viewport_dimensions = context.renderer.viewport_dimensions(); + url::form_urlencoded::Serializer::new(String::new()) + .append_pair("A", self.encode_capability(SystemCapabilities::AUDIO)) + .append_pair( + "SA", + self.encode_capability(SystemCapabilities::STREAMING_AUDIO), + ) + .append_pair( + "SV", + self.encode_capability(SystemCapabilities::STREAMING_VIDEO), + ) + .append_pair( + "EV", + self.encode_capability(SystemCapabilities::EMBEDDED_VIDEO), + ) + .append_pair("MP3", self.encode_capability(SystemCapabilities::MP3)) + .append_pair( + "AE", + self.encode_capability(SystemCapabilities::AUDIO_ENCODER), + ) + .append_pair( + "VE", + self.encode_capability(SystemCapabilities::VIDEO_ENCODER), + ) + .append_pair( + "ACC", + self.encode_not_capability(SystemCapabilities::ACCESSIBILITY), + ) + .append_pair("PR", self.encode_capability(SystemCapabilities::PRINTING)) + .append_pair( + "SP", + self.encode_capability(SystemCapabilities::SCREEN_PLAYBACK), + ) + .append_pair( + "SB", + self.encode_capability(SystemCapabilities::SCREEN_BROADCAST), + ) + .append_pair("DEB", self.encode_capability(SystemCapabilities::DEBUGGER)) + .append_pair( + "M", + &self.encode_string( + self.manufacturer + .get_manufacturer_string(context.player_version) + .as_str(), + ), + ) + .append_pair( + "R", + &format!( + "{}x{}", + viewport_dimensions.width, viewport_dimensions.height + ), + ) + .append_pair("COL", &self.screen_color.to_string()) + .append_pair("AR", &self.pixel_aspect_ratio.to_string()) + .append_pair("OS", &self.encode_string(&self.os.to_string())) + .append_pair("L", self.language.get_language_code(context.player_version)) + .append_pair("IME", self.encode_capability(SystemCapabilities::IME)) + .append_pair("PT", &self.player_type.to_string()) + .append_pair( + "AVD", + self.encode_not_capability(SystemCapabilities::AV_HARDWARE), + ) + .append_pair( + "LFD", + self.encode_not_capability(SystemCapabilities::LOCAL_FILE_READ), + ) + .append_pair("DP", &self.dpi.to_string()) + .finish() + } +}