From f53c83cfa4213fe35dd4a33fec0f8d6b7673a6ee Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Sat, 11 Mar 2023 23:31:09 +0100 Subject: [PATCH] Partly implement XML child and elements This makes #3294 (rollercoaster-creator-2) fully playable. Missing is any (*) matching for child()/elements() and the existing attributes() method. Also missing is support for number indexes with child(). --- core/src/avm2/globals/XML.as | 14 +++++- core/src/avm2/globals/xml.rs | 83 +++++++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/core/src/avm2/globals/XML.as b/core/src/avm2/globals/XML.as index da9618af1..2f553ad4e 100644 --- a/core/src/avm2/globals/XML.as +++ b/core/src/avm2/globals/XML.as @@ -10,7 +10,9 @@ package { AS3 native function name():Object; AS3 native function localName():Object; AS3 native function toXMLString():String; + AS3 native function child(name:Object):XMLList; AS3 native function children():XMLList; + AS3 native function elements(name:*):XMLList; AS3 native function attributes():XMLList; AS3 native function attribute(name:*):XMLList; @@ -36,11 +38,21 @@ package { return self.AS3::toXMLString(); }; - prototype.children = function():String { + prototype.child = function(name:Object):XMLList { + var self:XML = this; + return self.AS3::child(name); + }; + + prototype.children = function():XMLList { var self:XML = this; return self.AS3::children(); }; + prototype.elements = function(name:*):XMLList { + var self:XML = this; + return self.AS3::elements(name); + } + prototype.toString = function():String { if (this === prototype) { return ""; diff --git a/core/src/avm2/globals/xml.rs b/core/src/avm2/globals/xml.rs index f789aa2a1..621897a44 100644 --- a/core/src/avm2/globals/xml.rs +++ b/core/src/avm2/globals/xml.rs @@ -88,6 +88,50 @@ pub fn to_xml_string<'gc>( Ok(Value::String(node.xml_to_xml_string(activation)?)) } +fn name_to_multiname<'gc>( + activation: &mut Activation<'_, 'gc>, + name: &Value<'gc>, +) -> Result, Error<'gc>> { + Ok(match name { + Value::String(s) => Multiname::new(activation.avm2().public_namespace, *s), + Value::Object(o) => { + if let Some(qname) = o.as_qname_object() { + qname.name().clone() + } else { + Multiname::new( + activation.avm2().public_namespace, + name.coerce_to_string(activation)?, + ) + } + } + _ => Multiname::new( + activation.avm2().public_namespace, + name.coerce_to_string(activation)?, + ), + }) +} + +pub fn child<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let xml = this.unwrap().as_xml_object().unwrap(); + let multiname = name_to_multiname(activation, &args[0])?; + // FIXME: Support numerical indexes. + let children = if let E4XNodeKind::Element { children, .. } = &*xml.node().kind() { + children + .iter() + .filter(|node| node.matches_name(&multiname)) + .map(|node| E4XOrXml::E4X(*node)) + .collect() + } else { + Vec::new() + }; + + Ok(XmlListObject::new(activation, children, Some(xml.into())).into()) +} + pub fn children<'gc>( activation: &mut Activation<'_, 'gc>, this: Option>, @@ -103,6 +147,25 @@ pub fn children<'gc>( Ok(XmlListObject::new(activation, children, Some(xml.into())).into()) } +pub fn elements<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let xml = this.unwrap().as_xml_object().unwrap(); + let children = if let E4XNodeKind::Element { children, .. } = &*xml.node().kind() { + children + .iter() + .filter(|node| matches!(&*node.kind(), E4XNodeKind::Element { .. })) + .map(|node| E4XOrXml::E4X(*node)) + .collect() + } else { + Vec::new() + }; + + Ok(XmlListObject::new(activation, children, Some(xml.into())).into()) +} + pub fn attributes<'gc>( activation: &mut Activation<'_, 'gc>, this: Option>, @@ -126,25 +189,7 @@ pub fn attribute<'gc>( ) -> Result, Error<'gc>> { let this = this.unwrap(); let xml = this.as_xml_object().unwrap(); - let name = args[0]; - let multiname = match name { - Value::String(s) => Multiname::new(activation.avm2().public_namespace, s), - Value::Object(o) => { - if let Some(qname) = o.as_qname_object() { - qname.name().clone() - } else { - Multiname::new( - activation.avm2().public_namespace, - name.coerce_to_string(activation)?, - ) - } - } - _ => Multiname::new( - activation.avm2().public_namespace, - name.coerce_to_string(activation)?, - ), - }; - + let multiname = name_to_multiname(activation, &args[0])?; let attribute = if let E4XNodeKind::Element { attributes, .. } = &*xml.node().kind() { attributes .iter()