@@ -5,7 +5,7 @@ use crate::display_object::TDisplayObject;
55use crate :: string:: AvmString ;
66use flash_lso:: amf0:: read:: AMF0Decoder ;
77use flash_lso:: amf0:: writer:: { Amf0Writer , CacheKey , ObjWriter } ;
8- use flash_lso:: types:: { Lso , ObjectId , Reference , Value as AmfValue } ;
8+ use flash_lso:: types:: { Element , Lso , ObjectId , Reference , Value as AmfValue } ;
99use gc_arena:: { Collect , Gc } ;
1010use ruffle_macros:: istr;
1111use std:: borrow:: Cow ;
@@ -86,13 +86,56 @@ pub fn serialize<'gc>(activation: &mut Activation<'_, 'gc>, value: Value<'gc>) -
8686 Value :: Number ( number) => AmfValue :: Number ( number) ,
8787 Value :: String ( string) => AmfValue :: String ( string. to_string ( ) ) ,
8888 Value :: Object ( object) => {
89- let lso = new_lso ( activation, "root" , object) ;
90- AmfValue :: Object ( ObjectId :: INVALID , lso. into_iter ( ) . collect ( ) , None )
89+ if let NativeObject :: Array ( _) = object. native ( ) {
90+ // AS1/AS2 `Array` values are serialized using the AMF0 array
91+ // markers so that Flash Remoting / LocalConnection endpoints
92+ // round-trip them as arrays rather than as anonymous objects
93+ // with `"0"`/`"1"`/... keys. Callers that wire the result
94+ // onto a path Flash sends as `StrictArray` (NetConnection.call
95+ // / LocalConnection.send argument trees, #16381) post-process
96+ // with `crate::avm2::amf::promote_dense_ecma_to_strict_array`.
97+ serialize_array ( activation, object)
98+ } else {
99+ let lso = new_lso ( activation, "root" , object) ;
100+ AmfValue :: Object ( ObjectId :: INVALID , lso. into_iter ( ) . collect ( ) , None )
101+ }
91102 }
92103 Value :: MovieClip ( _) => AmfValue :: Undefined ,
93104 }
94105}
95106
107+ /// Serialize an AS1/AS2 `Array` into the AMF0 array form. Dense indices fill
108+ /// the first slot of the returned `ECMAArray`; non-numeric or out-of-range
109+ /// keys go into the associative part. The wrapping `ECMAArray` matches what
110+ /// flash-lso's `ObjWriter::array`/`ArrayWriter::commit` produces for nested
111+ /// arrays inside `recursive_serialize`, so behavior is uniform between
112+ /// top-level and nested `Array` serialization.
113+ fn serialize_array < ' gc > ( activation : & mut Activation < ' _ , ' gc > , array : Object < ' gc > ) -> AmfValue {
114+ let length = array. length ( activation) . unwrap_or ( 0 ) . max ( 0 ) as u32 ;
115+ let mut dense: Vec < std:: rc:: Rc < AmfValue > > = ( 0 ..length)
116+ . map ( |_| std:: rc:: Rc :: new ( AmfValue :: Undefined ) )
117+ . collect ( ) ;
118+ let mut associative: Vec < Element > = Vec :: new ( ) ;
119+ // Reversed to match Flash Player enumeration order, mirroring
120+ // `recursive_serialize`.
121+ for key in array. get_keys ( activation, false ) . into_iter ( ) . rev ( ) {
122+ let key_str = key. to_utf8_lossy ( ) ;
123+ let value = array
124+ . get ( key, activation)
125+ . map ( |v| serialize ( activation, v) )
126+ . unwrap_or ( AmfValue :: Undefined ) ;
127+ match key_str. parse :: < u32 > ( ) {
128+ Ok ( idx) if idx < length => {
129+ dense[ idx as usize ] = std:: rc:: Rc :: new ( value) ;
130+ }
131+ _ => {
132+ associative. push ( Element :: new ( key_str. to_string ( ) , std:: rc:: Rc :: new ( value) ) ) ;
133+ }
134+ }
135+ }
136+ AmfValue :: ECMAArray ( ObjectId :: INVALID , dense, associative, length)
137+ }
138+
96139/// Serialize an Object and any children to a JSON object
97140fn recursive_serialize < ' gc > (
98141 activation : & mut Activation < ' _ , ' gc > ,
0 commit comments