avm2: Implement read/writeObject()

This commit is contained in:
sleepycatcoding 2023-07-17 17:52:20 +03:00 committed by Nathan Adams
parent ea3d6e2a5c
commit 6c3b6e5109
2 changed files with 76 additions and 12 deletions

View File

@ -5,7 +5,6 @@ package flash.net {
import flash.utils.IDataInput; import flash.utils.IDataInput;
import flash.utils.IDataOutput; import flash.utils.IDataOutput;
import __ruffle__.stub_method;
import __ruffle__.stub_getter; import __ruffle__.stub_getter;
[Ruffle(InstanceAllocator)] [Ruffle(InstanceAllocator)]
@ -60,12 +59,7 @@ package flash.net {
public native function readFloat():Number; public native function readFloat():Number;
public native function readInt():int; public native function readInt():int;
public native function readMultiByte(length:uint, charSet:String):String; public native function readMultiByte(length:uint, charSet:String):String;
public native function readObject():*;
public function readObject():* {
stub_method("flash.net.Socket", "readObject");
return null;
}
public native function readShort():int; public native function readShort():int;
public native function readUnsignedByte():uint; public native function readUnsignedByte():uint;
public native function readUnsignedInt():uint; public native function readUnsignedInt():uint;
@ -80,11 +74,7 @@ package flash.net {
public native function writeFloat(value:Number):void; public native function writeFloat(value:Number):void;
public native function writeInt(value:int):void; public native function writeInt(value:int):void;
public native function writeMultiByte(value:String, charSet:String):void; public native function writeMultiByte(value:String, charSet:String):void;
public native function writeObject(value:*):void;
public function writeObject(value:*):void {
stub_method("flash.net.Socket", "writeObject");
}
public native function writeShort(value:int):void; public native function writeShort(value:int):void;
public native function writeUnsignedInt(value:uint):void; public native function writeUnsignedInt(value:uint):void;
public native function writeUTF(value:String):void; public native function writeUTF(value:String):void;

View File

@ -7,6 +7,9 @@ use crate::avm2::{Activation, Error, Object, TObject, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use encoding_rs::Encoding; use encoding_rs::Encoding;
use encoding_rs::UTF_8; use encoding_rs::UTF_8;
use flash_lso::amf0::read::AMF0Decoder;
use flash_lso::amf3::read::AMF3Decoder;
use flash_lso::types::{AMFVersion, Element};
pub fn connect<'gc>( pub fn connect<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -292,6 +295,45 @@ pub fn read_multi_byte<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
pub fn read_object<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(socket) = this.as_socket() {
let mut bytes = socket.read_buffer();
let (bytes_left, value) = match socket.object_encoding() {
ObjectEncoding::Amf0 => {
let mut decoder = AMF0Decoder::default();
let (extra, amf) = decoder
.parse_single_element(&bytes)
.map_err(|_| "Error: Invalid object")?;
(
extra.len(),
crate::avm2::amf::deserialize_value(activation, &amf)?,
)
}
ObjectEncoding::Amf3 => {
let mut decoder = AMF3Decoder::default();
let (extra, amf) = decoder
.parse_single_element(&bytes)
.map_err(|_| "Error: Invalid object")?;
(
extra.len(),
crate::avm2::amf::deserialize_value(activation, &amf)?,
)
}
};
let len = bytes.len();
let _ = bytes.drain(..(len - bytes_left));
return Ok(value);
}
Ok(Value::Undefined)
}
pub fn read_short<'gc>( pub fn read_short<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
@ -504,6 +546,38 @@ pub fn write_multi_byte<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
pub fn write_object<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(socket) = this.as_socket() {
let obj = args.get_value(0);
let amf_version = match socket.object_encoding() {
ObjectEncoding::Amf0 => AMFVersion::AMF0,
ObjectEncoding::Amf3 => AMFVersion::AMF3,
};
if let Some(amf) = crate::avm2::amf::serialize_value(activation, obj, amf_version) {
let element = Element::new("", amf);
let mut lso = flash_lso::types::Lso::new(vec![element], "", amf_version);
let bytes = flash_lso::write::write_to_bytes(&mut lso)
.map_err(|_| "Failed to serialize object")?;
// This is kind of hacky: We need to strip out the header and any padding so that we only write
// the value. In the future, there should be a method to do this in the flash_lso crate.
let element_padding = match amf_version {
AMFVersion::AMF0 => 8,
AMFVersion::AMF3 => 7,
};
socket.write_bytes(
&bytes[flash_lso::write::header_length(&lso.header) + element_padding
..bytes.len() - 1],
);
}
}
Ok(Value::Undefined)
}
pub fn write_short<'gc>( pub fn write_short<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,