diff --git a/core/src/avm2/globals/flash/net/Socket.as b/core/src/avm2/globals/flash/net/Socket.as index 5177db4ef..01a1df46f 100644 --- a/core/src/avm2/globals/flash/net/Socket.as +++ b/core/src/avm2/globals/flash/net/Socket.as @@ -140,13 +140,8 @@ package flash.net { stub_method("flash.net.Socket", "writeBoolean"); } - public function writeByte(value:int):void { - stub_method("flash.net.Socket", "writeByte"); - } - - public function writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0):void { - stub_method("flash.net.Socket", "writeBytes"); - } + public native function writeByte(value:int):void; + public native function writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0):void; public function writeDouble(value:Number):void { stub_method("flash.net.Socket", "writeDouble"); diff --git a/core/src/avm2/globals/flash/net/socket.rs b/core/src/avm2/globals/flash/net/socket.rs index 07759226a..6e4068f08 100644 --- a/core/src/avm2/globals/flash/net/socket.rs +++ b/core/src/avm2/globals/flash/net/socket.rs @@ -102,3 +102,61 @@ pub fn get_connected<'gc>( Ok(Value::Bool(is_connected)) } + +pub fn write_byte<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + if let Some(socket) = this.as_socket() { + let byte = args + .get(0) + .cloned() + .unwrap_or(Value::Undefined) + .coerce_to_i32(activation)?; + socket.write_bytes(&[byte as u8]); + } + + Ok(Value::Undefined) +} + +pub fn write_bytes<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + if let Some(socket) = this.as_socket() { + let bytearray = args + .get(0) + .unwrap_or(&Value::Undefined) + .coerce_to_object(activation)?; + let offset = args + .get(1) + .unwrap_or(&Value::Integer(0)) + .coerce_to_u32(activation)? as usize; + let length = args + .get(2) + .unwrap_or(&Value::Integer(0)) + .coerce_to_u32(activation)? as usize; + + let ba_read = bytearray + .as_bytearray() + .ok_or("ArgumentError: Parameter must be a bytearray")?; + + let to_write = ba_read + .read_at( + // If length is 0, lets read the remaining bytes of ByteArray from the supplied offset + if length != 0 { + length + } else { + ba_read.len().saturating_sub(offset) + }, + offset, + ) + .map_err(|e| e.to_avm(activation))?; + + socket.write_bytes(to_write); + } + + Ok(Value::Undefined) +} diff --git a/core/src/avm2/object/socket_object.rs b/core/src/avm2/object/socket_object.rs index 7321933b3..a45beab39 100644 --- a/core/src/avm2/object/socket_object.rs +++ b/core/src/avm2/object/socket_object.rs @@ -7,7 +7,7 @@ use crate::socket::SocketHandle; use gc_arena::barrier::unlock; use gc_arena::{lock::RefLock, Collect, Gc}; use gc_arena::{GcWeak, Mutation}; -use std::cell::{Cell, Ref, RefMut}; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; /// A class instance allocator that allocates ShaderData objects. @@ -24,6 +24,7 @@ pub fn socket_allocator<'gc>( // Default endianness is Big. endian: Cell::new(Endian::Big), handle: Cell::new(None), + write_buffer: RefCell::new(vec![]), }, )) .into()) @@ -75,6 +76,10 @@ impl<'gc> SocketObject<'gc> { pub fn set_handle(&self, handle: SocketHandle) -> Option { self.0.handle.replace(Some(handle)) } + + pub fn write_bytes(&self, bytes: &[u8]) { + self.0.write_buffer.borrow_mut().extend_from_slice(bytes) + } } #[derive(Collect)] @@ -85,6 +90,7 @@ pub struct SocketObjectData<'gc> { #[collect(require_static)] handle: Cell>, endian: Cell, + write_buffer: RefCell>, } impl fmt::Debug for SocketObject<'_> {