avm2: ByteArray updates

This commit is contained in:
EmperorBale 2021-06-22 01:26:27 -07:00 committed by GitHub
parent e010775099
commit 1ee61cca57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 518 additions and 439 deletions

1
Cargo.lock generated
View File

@ -3143,6 +3143,7 @@ dependencies = [
"jpeg-decoder", "jpeg-decoder",
"json", "json",
"log", "log",
"lzma-rs",
"minimp3", "minimp3",
"nellymoser-rs", "nellymoser-rs",
"num-derive", "num-derive",

View File

@ -38,6 +38,7 @@ nellymoser-rs = { git = "https://github.com/ruffle-rs/nellymoser", branch = "mai
regress = "0.3" regress = "0.3"
flash-lso = { git = "https://github.com/ruffle-rs/rust-flash-lso", rev = "e39a8abc897289696672858e30bbc9e43b1c98ac" } flash-lso = { git = "https://github.com/ruffle-rs/rust-flash-lso", rev = "e39a8abc897289696672858e30bbc9e43b1c98ac" }
json = "0.12.4" json = "0.12.4"
lzma-rs = {version = "0.2.0", optional = true }
[dependencies.jpeg-decoder] [dependencies.jpeg-decoder]
version = "0.1.22" version = "0.1.22"
@ -50,6 +51,6 @@ env_logger = "0.8.4"
[features] [features]
default = ["minimp3", "serde"] default = ["minimp3", "serde"]
lzma = ["swf/lzma"] lzma = ["lzma-rs", "swf/lzma"]
wasm-bindgen = [ "instant/wasm-bindgen" ] wasm-bindgen = [ "instant/wasm-bindgen" ]
avm_debug = [] avm_debug = []

View File

@ -18,7 +18,7 @@ use crate::context::UpdateContext;
use crate::swf::extensions::ReadSwfExt; use crate::swf::extensions::ReadSwfExt;
use gc_arena::{Gc, GcCell, MutationContext}; use gc_arena::{Gc, GcCell, MutationContext};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::convert::TryInto; use std::convert::{TryFrom, TryInto};
use swf::avm2::read::Reader; use swf::avm2::read::Reader;
use swf::avm2::types::{ use swf::avm2::types::{
Class as AbcClass, Index, Method as AbcMethod, Multiname as AbcMultiname, Class as AbcClass, Index, Method as AbcMethod, Multiname as AbcMultiname,
@ -2568,11 +2568,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let mut dm = dm let mut dm = dm
.as_bytearray_mut(self.context.gc_context) .as_bytearray_mut(self.context.gc_context)
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
if address < 0 || address as usize >= dm.len() {
return Err("RangeError: The specified range is invalid".into());
}
dm.write_bytes_at(&val.to_le_bytes(), address as usize); let address =
usize::try_from(address).map_err(|_| "RangeError: The specified range is invalid")?;
dm.write_at_nongrowing(&val.to_le_bytes(), address)?;
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2587,11 +2586,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
.as_bytearray_mut(self.context.gc_context) .as_bytearray_mut(self.context.gc_context)
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
if address < 0 || (address as usize + 1) >= dm.len() { let address =
return Err("RangeError: The specified range is invalid".into()); usize::try_from(address).map_err(|_| "RangeError: The specified range is invalid")?;
} dm.write_at_nongrowing(&val.to_le_bytes(), address)?;
dm.write_bytes_at(&val.to_le_bytes(), address as usize);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2606,11 +2603,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
.as_bytearray_mut(self.context.gc_context) .as_bytearray_mut(self.context.gc_context)
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
if address < 0 || (address as usize + 3) >= dm.len() { let address =
return Err("RangeError: The specified range is invalid".into()); usize::try_from(address).map_err(|_| "RangeError: The specified range is invalid")?;
} dm.write_at_nongrowing(&val.to_le_bytes(), address)?;
dm.write_bytes_at(&val.to_le_bytes(), address as usize);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2625,11 +2620,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
.as_bytearray_mut(self.context.gc_context) .as_bytearray_mut(self.context.gc_context)
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
if address < 0 || (address as usize + 3) >= dm.len() { let address =
return Err("RangeError: The specified range is invalid".into()); usize::try_from(address).map_err(|_| "RangeError: The specified range is invalid")?;
} dm.write_at_nongrowing(&val.to_le_bytes(), address)?;
dm.write_bytes_at(&val.to_le_bytes(), address as usize);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2644,11 +2637,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
.as_bytearray_mut(self.context.gc_context) .as_bytearray_mut(self.context.gc_context)
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
if address < 0 || (address as usize + 7) >= dm.len() { let address =
return Err("RangeError: The specified range is invalid".into()); usize::try_from(address).map_err(|_| "RangeError: The specified range is invalid")?;
} dm.write_at_nongrowing(&val.to_le_bytes(), address)?;
dm.write_bytes_at(&val.to_le_bytes(), address as usize);
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2664,7 +2655,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let val = dm.get(address); let val = dm.get(address);
if let Some(val) = val { if let Some(val) = val {
self.context.avm2.push(Value::Integer(val as i32)); self.context.avm2.push(val);
} else { } else {
return Err("RangeError: The specified range is invalid".into()); return Err("RangeError: The specified range is invalid".into());
} }
@ -2680,15 +2671,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let dm = dm let dm = dm
.as_bytearray() .as_bytearray()
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
let val = dm.get_range(address..address + 2); let val = dm.read_at(2, address)?;
self.context
if let Some(val) = val { .avm2
self.context.avm2.push(Value::Integer( .push(u16::from_le_bytes(val.try_into().unwrap()));
u16::from_le_bytes(val.try_into().unwrap()) as i32
));
} else {
return Err("RangeError: The specified range is invalid".into());
}
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2701,16 +2687,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let dm = dm let dm = dm
.as_bytearray() .as_bytearray()
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
let val = dm.get_range(address..address + 4); let val = dm.read_at(4, address)?;
self.context
if let Some(val) = val { .avm2
self.context .push(i32::from_le_bytes(val.try_into().unwrap()));
.avm2
.push(Value::Integer(i32::from_le_bytes(val.try_into().unwrap())));
} else {
return Err("RangeError: The specified range is invalid".into());
}
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2722,15 +2702,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let dm = dm let dm = dm
.as_bytearray() .as_bytearray()
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
let val = dm.get_range(address..address + 4); let val = dm.read_at(4, address)?;
self.context
if let Some(val) = val { .avm2
self.context.avm2.push(Value::Number( .push(f32::from_le_bytes(val.try_into().unwrap()));
f32::from_le_bytes(val.try_into().unwrap()) as f64
));
} else {
return Err("RangeError: The specified range is invalid".into());
}
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
@ -2743,16 +2718,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
let dm = dm let dm = dm
.as_bytearray() .as_bytearray()
.ok_or_else(|| "Unable to get bytearray storage".to_string())?; .ok_or_else(|| "Unable to get bytearray storage".to_string())?;
let val = dm.get_range(address..address + 8); let val = dm.read_at(8, address)?;
self.context
if let Some(val) = val { .avm2
self.context .push(f64::from_le_bytes(val.try_into().unwrap()));
.avm2
.push(Value::Number(f64::from_le_bytes(val.try_into().unwrap())));
} else {
return Err("RangeError: The specified range is invalid".into());
}
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }

View File

@ -2,11 +2,13 @@ use crate::avm2::Error;
use flate2::read::*; use flate2::read::*;
use flate2::Compression; use flate2::Compression;
use gc_arena::Collect; use gc_arena::Collect;
use std::cell::Cell;
use std::cmp; use std::cmp;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io; use std::fmt::{self, Display, Formatter};
use std::io::prelude::*; use std::io::prelude::*;
use std::ops::Range; use std::io::{self, Read, SeekFrom};
use std::str::FromStr;
#[derive(Clone, Collect, Debug)] #[derive(Clone, Collect, Debug)]
#[collect(no_drop)] #[collect(no_drop)]
@ -15,16 +17,47 @@ pub enum Endian {
Little, Little,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CompressionAlgorithm {
Zlib,
Deflate,
Lzma,
}
impl Display for CompressionAlgorithm {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = match *self {
CompressionAlgorithm::Zlib => "zlib",
CompressionAlgorithm::Deflate => "deflate",
CompressionAlgorithm::Lzma => "lzma",
};
f.write_str(s)
}
}
impl FromStr for CompressionAlgorithm {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"zlib" => CompressionAlgorithm::Zlib,
"deflate" => CompressionAlgorithm::Deflate,
"lzma" => CompressionAlgorithm::Lzma,
_ => return Err("Unknown compression algorithm".into()),
})
}
}
#[derive(Clone, Collect, Debug)] #[derive(Clone, Collect, Debug)]
#[collect(no_drop)] #[collect(no_drop)]
pub struct ByteArrayStorage { pub struct ByteArrayStorage {
/// Underlying ByteArray /// Underlying ByteArray
bytes: Vec<u8>, bytes: Vec<u8>,
// The current position to read/write from /// The current position to read/write from
position: usize, position: Cell<usize>,
/// This represents what endian to use while reading data. /// This represents what endian to use while reading/writing data.
endian: Endian, endian: Endian,
} }
@ -33,272 +66,156 @@ impl ByteArrayStorage {
pub fn new() -> ByteArrayStorage { pub fn new() -> ByteArrayStorage {
ByteArrayStorage { ByteArrayStorage {
bytes: Vec::new(), bytes: Vec::new(),
position: 0, position: Cell::new(0),
endian: Endian::Big, endian: Endian::Big,
} }
} }
/// Write a byte at next position in the bytearray /// Write bytes at the next position in the ByteArray, growing if needed.
pub fn write_byte(&mut self, byte: u8) { #[inline]
let bytes_len = self.bytes.len(); pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Error> {
// Allocate space for the byte self.write_at(buf, self.position.get())?;
self.position += 1; self.position.set(self.position.get() + buf.len());
if self.position > bytes_len { Ok(())
self.bytes.resize(self.position, 0);
}
self.bytes[self.position - 1] = byte;
} }
/// Write bytes at next position in bytearray (This function is similar to whats in std::io::Cursor) /// Reads any amount of bytes from the current position in the ByteArray
pub fn write_bytes(&mut self, buf: &[u8]) { #[inline]
// Make sure the internal buffer is as least as big as where we pub fn read_bytes(&self, amnt: usize) -> Result<&[u8], Error> {
// currently are let bytes = self.read_at(amnt, self.position.get())?;
let len = self.bytes.len(); self.position.set(self.position.get() + amnt);
if len < self.position { Ok(bytes)
// use `resize` so that the zero filling is as efficient as possible
self.bytes.resize(self.position, 0);
}
// Figure out what bytes will be used to overwrite what's currently
// there (left), and what will be appended on the end (right)
{
let space = self.bytes.len() - self.position;
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
self.bytes[self.position..self.position + left.len()].copy_from_slice(left);
self.bytes.extend_from_slice(right);
}
// Bump us forward
self.position += buf.len();
} }
// Write bytes at an offset, ignoring the current position /// Reads any amount of bytes at any offset in the ByteArray
pub fn write_bytes_at(&mut self, buf: &[u8], offset: usize) { #[inline]
// Make sure the internal buffer is as least as big as where we pub fn read_at(&self, amnt: usize, offset: usize) -> Result<&[u8], Error> {
// currently are self.bytes
let len = self.bytes.len(); .get(offset..)
if len < offset { .and_then(|bytes| bytes.get(..amnt))
// use `resize` so that the zero filling is as efficient as possible .ok_or_else(|| "EOFError: Reached EOF".into())
self.bytes.resize(offset, 0);
}
// Figure out what bytes will be used to overwrite what's currently
// there (left), and what will be appended on the end (right)
{
let space = self.bytes.len() - offset;
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
self.bytes[offset..offset + left.len()].copy_from_slice(left);
self.bytes.extend_from_slice(right);
}
} }
pub fn clear(&mut self) { /// Write bytes at any offset in the ByteArray
self.bytes.clear(); /// Will automatically grow the ByteArray to fit the new buffer
// According to docs, this is where the bytearray should free resources pub fn write_at(&mut self, buf: &[u8], offset: usize) -> Result<(), Error> {
self.bytes.shrink_to_fit(); let new_len = offset
self.position = 0; .checked_add(buf.len())
.ok_or("RangeError: Cannot overflow usize")?;
if self.len() < new_len {
self.set_length(new_len);
}
// SAFETY:
// The storage is garunteed to be at least the size of new_len because we just resized it.
unsafe {
self.bytes
.get_unchecked_mut(offset..new_len)
.copy_from_slice(buf)
}
Ok(())
} }
// Returns the bytearray compressed with zlib /// Write bytes at any offset in the ByteArray
pub fn zlib_compress(&mut self) -> io::Result<Vec<u8>> { /// Will return an error if the new buffer does not fit the ByteArray
pub fn write_at_nongrowing(&mut self, buf: &[u8], offset: usize) -> Result<(), Error> {
self.bytes
.get_mut(offset..)
.and_then(|bytes| bytes.get_mut(..buf.len()))
.ok_or("RangeError: The specified range is invalid")?
.copy_from_slice(buf);
Ok(())
}
/// Compress the ByteArray into a temporary buffer
pub fn compress(&mut self, algorithm: CompressionAlgorithm) -> Result<Vec<u8>, Error> {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let mut compresser = ZlibEncoder::new(&*self.bytes, Compression::fast()); match algorithm {
compresser.read_to_end(&mut buffer)?; CompressionAlgorithm::Zlib => {
let mut compresser = ZlibEncoder::new(&*self.bytes, Compression::fast());
compresser.read_to_end(&mut buffer)?;
}
CompressionAlgorithm::Deflate => {
let mut compresser = DeflateEncoder::new(&*self.bytes, Compression::fast());
compresser.read_to_end(&mut buffer)?;
}
#[cfg(feature = "lzma")]
CompressionAlgorithm::Lzma => lzma_rs::lzma_compress(&mut &*self.bytes, &mut buffer)?,
#[cfg(not(feature = "lzma"))]
CompressionAlgorithm::Lzma => {
return Err("Ruffle was not compiled with LZMA support".into())
}
}
Ok(buffer) Ok(buffer)
} }
// Returns the bytearray compressed with deflate /// Decompress the ByteArray into a temporary buffer
pub fn deflate_compress(&mut self) -> io::Result<Vec<u8>> { pub fn decompress(&mut self, algorithm: CompressionAlgorithm) -> Result<Vec<u8>, Error> {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let mut compresser = DeflateEncoder::new(&*self.bytes, Compression::fast()); match algorithm {
compresser.read_to_end(&mut buffer)?; CompressionAlgorithm::Zlib => {
Ok(buffer) let mut compresser = ZlibDecoder::new(&*self.bytes);
} compresser.read_to_end(&mut buffer)?;
}
// Returns the bytearray decompressed with zlib CompressionAlgorithm::Deflate => {
pub fn zlib_decompress(&mut self) -> io::Result<Vec<u8>> { let mut compresser = DeflateDecoder::new(&*self.bytes);
let mut buffer = Vec::new(); compresser.read_to_end(&mut buffer)?;
let mut compresser = ZlibDecoder::new(&*self.bytes); }
compresser.read_to_end(&mut buffer)?; #[cfg(feature = "lzma")]
Ok(buffer) CompressionAlgorithm::Lzma => lzma_rs::lzma_decompress(&mut &*self.bytes, &mut buffer)?,
} #[cfg(not(feature = "lzma"))]
CompressionAlgorithm::Lzma => {
// Returns the bytearray decompressed with deflate return Err("Ruffle was not compiled with LZMA support".into())
pub fn deflate_decompress(&mut self) -> io::Result<Vec<u8>> { }
let mut buffer = Vec::new();
let mut compresser = DeflateDecoder::new(&*self.bytes);
compresser.read_to_end(&mut buffer)?;
Ok(buffer)
}
/// Set a new length for the bytearray
pub fn set_length(&mut self, new_len: usize) {
self.bytes.resize(new_len, 0);
}
// Reads exactly an amount of data
pub fn read_exact(&mut self, amnt: usize) -> Result<&[u8], Error> {
if self.position + amnt > self.bytes.len() {
return Err("EOFError: Reached EOF".into());
} }
let val = Ok(&self.bytes[self.position..self.position + amnt]); Ok(buffer)
self.position += amnt;
val
} }
pub fn read_utf(&mut self) -> Result<String, Error> { pub fn read_utf(&self) -> Result<String, Error> {
let len = self.read_unsigned_short()?; let len = self.read_unsigned_short()?;
let val = String::from_utf8_lossy(self.read_exact(len as usize)?); let val = String::from_utf8_lossy(self.read_bytes(len.into())?);
Ok(val.into_owned()) Ok(val.into_owned())
} }
// Reads a i16 from the buffer pub fn write_boolean(&mut self, val: bool) -> Result<(), Error> {
pub fn read_short(&mut self) -> Result<i16, Error> { self.write_bytes(&[val as u8; 1])
Ok(match self.endian {
Endian::Big => i16::from_be_bytes(self.read_exact(2)?.try_into().unwrap()),
Endian::Little => i16::from_le_bytes(self.read_exact(2)?.try_into().unwrap()),
})
} }
// Reads a u16 from the buffer pub fn read_boolean(&self) -> Result<bool, Error> {
pub fn read_unsigned_short(&mut self) -> Result<u16, Error> { Ok(self.read_bytes(1)? != [0])
Ok(match self.endian {
Endian::Big => u16::from_be_bytes(self.read_exact(2)?.try_into().unwrap()),
Endian::Little => u16::from_le_bytes(self.read_exact(2)?.try_into().unwrap()),
})
}
// Reads a f64 from the buffer
pub fn read_double(&mut self) -> Result<f64, Error> {
Ok(match self.endian {
Endian::Big => f64::from_be_bytes(self.read_exact(8)?.try_into().unwrap()),
Endian::Little => f64::from_le_bytes(self.read_exact(8)?.try_into().unwrap()),
})
}
// Reads a f32 from the buffer
pub fn read_float(&mut self) -> Result<f32, Error> {
Ok(match self.endian {
Endian::Big => f32::from_be_bytes(self.read_exact(4)?.try_into().unwrap()),
Endian::Little => f32::from_le_bytes(self.read_exact(4)?.try_into().unwrap()),
})
}
// Reads a i32 from the buffer
pub fn read_int(&mut self) -> Result<i32, Error> {
Ok(match self.endian {
Endian::Big => i32::from_be_bytes(self.read_exact(4)?.try_into().unwrap()),
Endian::Little => i32::from_le_bytes(self.read_exact(4)?.try_into().unwrap()),
})
}
// Reads a u32 from the buffer
pub fn read_unsigned_int(&mut self) -> Result<u32, Error> {
Ok(match self.endian {
Endian::Big => u32::from_be_bytes(self.read_exact(4)?.try_into().unwrap()),
Endian::Little => u32::from_le_bytes(self.read_exact(4)?.try_into().unwrap()),
})
}
// Reads byte from buffer, returns false if zero, otherwise true
pub fn read_boolean(&mut self) -> Result<bool, Error> {
Ok(*self.read_exact(1)?.first().unwrap() != 0)
}
// Reads a i8 from the buffer
pub fn read_byte(&mut self) -> Result<i8, Error> {
Ok(match self.endian {
Endian::Big => i8::from_be_bytes(self.read_exact(1)?.try_into().unwrap()),
Endian::Little => i8::from_le_bytes(self.read_exact(1)?.try_into().unwrap()),
})
}
// Reads a u8 from the buffer
pub fn read_unsigned_byte(&mut self) -> Result<u8, Error> {
Ok(match self.endian {
Endian::Big => u8::from_be_bytes(self.read_exact(1)?.try_into().unwrap()),
Endian::Little => u8::from_le_bytes(self.read_exact(1)?.try_into().unwrap()),
})
}
// Writes a f32 to the buffer
pub fn write_float(&mut self, val: f32) {
let float_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&float_bytes);
}
// Writes a f64 to the buffer
pub fn write_double(&mut self, val: f64) {
let double_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&double_bytes);
}
// Writes a 1 byte to the buffer, either 1 or 0
pub fn write_boolean(&mut self, val: bool) {
self.write_bytes(&[val as u8; 1]);
}
// Writes a i32 to the buffer
pub fn write_int(&mut self, val: i32) {
let int_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&int_bytes);
}
// Writes a u32 to the buffer
pub fn write_unsigned_int(&mut self, val: u32) {
let uint_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&uint_bytes);
}
// Writes a i16 to the buffer
pub fn write_short(&mut self, val: i16) {
let short_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&short_bytes);
}
// Writes a u16 to the buffer
pub fn write_unsigned_short(&mut self, val: u16) {
let ushort_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&ushort_bytes);
} }
// Writes a UTF String into the buffer, with its length as a prefix // Writes a UTF String into the buffer, with its length as a prefix
pub fn write_utf(&mut self, utf_string: &str) -> Result<(), Error> { pub fn write_utf(&mut self, utf_string: &str) -> Result<(), Error> {
if let Ok(str_size) = u16::try_from(utf_string.len()) { if let Ok(str_size) = u16::try_from(utf_string.len()) {
self.write_unsigned_short(str_size); self.write_unsigned_short(str_size)?;
self.write_bytes(utf_string.as_bytes()); self.write_bytes(utf_string.as_bytes())
} else { } else {
return Err("RangeError: UTF String length must fit into a short".into()); Err("RangeError: UTF String length must fit into a short".into())
} }
Ok(())
} }
pub fn get(&self, item: usize) -> Option<u8> { #[inline]
self.bytes.get(item).copied() pub fn clear(&mut self) {
self.bytes.clear();
self.position.set(0)
} }
pub fn get_range(&self, item: Range<usize>) -> Option<&[u8]> { #[inline]
self.bytes.get(item) pub fn shrink_to_fit(&mut self) {
self.bytes.shrink_to_fit()
}
#[inline]
pub fn set_length(&mut self, new_len: usize) {
self.bytes.resize(new_len, 0);
}
pub fn get(&self, pos: usize) -> Option<u8> {
self.bytes.get(pos).copied()
} }
pub fn set(&mut self, item: usize, value: u8) { pub fn set(&mut self, item: usize, value: u8) {
if self.bytes.len() < (item + 1) { if self.len() < (item + 1) {
self.bytes.resize(item + 1, 0) self.bytes.resize(item + 1, 0)
} }
@ -311,38 +228,52 @@ impl ByteArrayStorage {
} }
} }
#[inline]
pub fn bytes(&self) -> &Vec<u8> { pub fn bytes(&self) -> &Vec<u8> {
&self.bytes &self.bytes
} }
#[inline]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[inline]
pub fn position(&self) -> usize { pub fn position(&self) -> usize {
self.position self.position.get()
} }
pub fn set_position(&mut self, pos: usize) { #[inline]
self.position = pos; pub fn set_position(&self, pos: usize) {
self.position.set(pos);
} }
pub fn add_position(&mut self, amnt: usize) { #[inline]
self.position += amnt; pub fn add_position(&self, amnt: usize) {
self.position.set(self.position.get() + amnt);
} }
#[inline]
pub fn endian(&self) -> &Endian { pub fn endian(&self) -> &Endian {
&self.endian &self.endian
} }
#[inline]
pub fn set_endian(&mut self, new_endian: Endian) { pub fn set_endian(&mut self, new_endian: Endian) {
self.endian = new_endian; self.endian = new_endian;
} }
pub fn len(&self) -> usize { #[inline]
self.bytes.len() pub fn bytes_available(&self) -> usize {
self.len().saturating_sub(self.position.get())
} }
} }
impl Write for ByteArrayStorage { impl Write for ByteArrayStorage {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.write_bytes(buf); self.write_bytes(buf).map_err(|_| {
io::Error::new(io::ErrorKind::Other, "Failed to write to ByteArrayStorage")
})?;
Ok(buf.len()) Ok(buf.len())
} }
@ -352,6 +283,82 @@ impl Write for ByteArrayStorage {
} }
} }
impl Read for ByteArrayStorage {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let bytes = self
.read_bytes(cmp::min(buf.len(), self.bytes_available()))
.map_err(|_| {
io::Error::new(io::ErrorKind::Other, "Failed to read from ByteArrayStorage")
})?;
buf[..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
}
}
impl Seek for ByteArrayStorage {
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
let (base_pos, offset) = match style {
SeekFrom::Start(n) => {
self.position.set(n as usize);
return Ok(n);
}
SeekFrom::End(n) => (self.len(), n),
SeekFrom::Current(n) => (self.position.get(), n),
};
let new_pos = if offset >= 0 {
base_pos.checked_add(offset as usize)
} else {
base_pos.checked_sub((offset.wrapping_neg()) as usize)
};
match new_pos {
Some(n) => {
self.position.set(n);
Ok(n as u64)
}
None => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid seek to a negative or overflowing position",
)),
}
}
}
macro_rules! impl_write{
($($method_name:ident $data_type:ty ), *)
=>
{
impl ByteArrayStorage {
$( pub fn $method_name (&mut self, val: $data_type) -> Result<(), Error> {
let val_bytes = match self.endian {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
self.write_bytes(&val_bytes)
} )*
}
}
}
macro_rules! impl_read{
($($method_name:ident $size:expr; $data_type:ty ), *)
=>
{
impl ByteArrayStorage {
$( pub fn $method_name (&self) -> Result<$data_type, Error> {
Ok(match self.endian {
Endian::Big => <$data_type>::from_be_bytes(self.read_bytes($size)?.try_into().unwrap()),
Endian::Little => <$data_type>::from_le_bytes(self.read_bytes($size)?.try_into().unwrap())
})
} )*
}
}
}
impl_write!(write_float f32, write_double f64, write_int i32, write_unsigned_int u32, write_short i16, write_unsigned_short u16);
impl_read!(read_float 4; f32, read_double 8; f64, read_int 4; i32, read_unsigned_int 4; u32, read_short 2; i16, read_unsigned_short 2; u16, read_byte 1; i8, read_unsigned_byte 1; u8);
impl Default for ByteArrayStorage { impl Default for ByteArrayStorage {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()

View File

@ -598,6 +598,14 @@ pub fn load_player_globals<'gc>(
script, script,
)?; )?;
class(
activation,
flash::utils::compression_algorithm::create_class(mc),
implicit_deriver,
domain,
script,
)?;
function( function(
mc, mc,
"flash.utils", "flash.utils",

View File

@ -307,15 +307,16 @@ pub fn bytes<'gc>(
// off. We scroll back 2 bytes before writing the actual // off. We scroll back 2 bytes before writing the actual
// datastream as it is guaranteed to at least be as long as // datastream as it is guaranteed to at least be as long as
// the implicit end tag we want to get rid of. // the implicit end tag we want to get rid of.
let correct_header_length = ba_write.bytes().len() - 2; let correct_header_length = ba_write.len() - 2;
ba_write.set_position(correct_header_length); ba_write.set_position(correct_header_length);
ba_write.write_bytes(root.data()); ba_write.write_bytes(root.data())?;
// `swf` wrote the wrong length (since we wrote the data // `swf` wrote the wrong length (since we wrote the data
// ourselves), so we need to overwrite it ourselves. // ourselves), so we need to overwrite it ourselves.
ba_write.set_position(4); ba_write.set_position(4);
ba_write.set_endian(Endian::Little); ba_write.set_endian(Endian::Little);
ba_write.write_unsigned_int((root.data().len() + correct_header_length) as u32); ba_write
.write_unsigned_int((root.data().len() + correct_header_length) as u32)?;
// Finally, reset the array to the correct state. // Finally, reset the array to the correct state.
ba_write.set_position(0); ba_write.set_position(0);

View File

@ -3,6 +3,7 @@
use crate::avm2::{Activation, Error, Object, Value}; use crate::avm2::{Activation, Error, Object, Value};
pub mod bytearray; pub mod bytearray;
pub mod compression_algorithm;
pub mod endian; pub mod endian;
/// Implements `flash.utils.getTimer` /// Implements `flash.utils.getTimer`

View File

@ -1,5 +1,5 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::bytearray::Endian; use crate::avm2::bytearray::{CompressionAlgorithm, Endian};
use crate::avm2::class::{Class, ClassAttributes}; use crate::avm2::class::{Class, ClassAttributes};
use crate::avm2::method::{Method, NativeMethod}; use crate::avm2::method::{Method, NativeMethod};
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
@ -10,6 +10,7 @@ use crate::avm2::Error;
use encoding_rs::Encoding; use encoding_rs::Encoding;
use encoding_rs::UTF_8; use encoding_rs::UTF_8;
use gc_arena::{GcCell, MutationContext}; use gc_arena::{GcCell, MutationContext};
use std::str::FromStr;
/// Implements `flash.utils.ByteArray`'s instance constructor. /// Implements `flash.utils.ByteArray`'s instance constructor.
pub fn instance_init<'gc>( pub fn instance_init<'gc>(
@ -46,7 +47,7 @@ pub fn write_byte<'gc>(
.cloned() .cloned()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
bytearray.write_byte(byte as u8); bytearray.write_bytes(&[byte as u8])?;
} }
} }
@ -85,7 +86,7 @@ pub fn write_bytes<'gc>(
&combining_bytes[offset..length + offset] &combining_bytes[offset..length + offset]
} else { } else {
&combining_bytes[offset..] &combining_bytes[offset..]
}); })?;
} }
} }
} }
@ -132,7 +133,7 @@ pub fn read_bytes<'gc>(
&current_bytes[position..] &current_bytes[position..]
}; };
merging_offset = to_write.len(); merging_offset = to_write.len();
merging_storage.write_bytes_at(to_write, offset); merging_storage.write_at(to_write, offset)?;
} else { } else {
return Err("ArgumentError: Parameter must be a bytearray".into()); return Err("ArgumentError: Parameter must be a bytearray".into());
} }
@ -167,7 +168,7 @@ pub fn read_utf<'gc>(
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(AvmString::new(activation.context.gc_context, bytearray.read_utf()?).into()); return Ok(AvmString::new(activation.context.gc_context, bytearray.read_utf()?).into());
} }
} }
@ -181,8 +182,7 @@ pub fn to_string<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(bytearray) = this.as_bytearray() { if let Some(bytearray) = this.as_bytearray() {
let bytes = bytearray.bytes(); let (new_string, _, _) = UTF_8.decode(bytearray.bytes());
let (new_string, _, _) = UTF_8.decode(bytes);
return Ok(AvmString::new(activation.context.gc_context, new_string).into()); return Ok(AvmString::new(activation.context.gc_context, new_string).into());
} }
} }
@ -198,6 +198,7 @@ pub fn clear<'gc>(
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
bytearray.clear(); bytearray.clear();
bytearray.shrink_to_fit();
} }
} }
@ -224,7 +225,7 @@ pub fn set_position<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
let num = args let num = args
.get(0) .get(0)
.unwrap_or(&Value::Integer(0)) .unwrap_or(&Value::Integer(0))
@ -243,13 +244,7 @@ pub fn bytes_available<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(bytearray) = this.as_bytearray() { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Unsigned( return Ok(Value::Unsigned(bytearray.bytes_available() as u32));
if bytearray.position() > bytearray.bytes().len() {
0
} else {
(bytearray.bytes().len() - bytearray.position()) as u32
},
));
} }
} }
@ -263,7 +258,7 @@ pub fn length<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(bytearray) = this.as_bytearray() { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Unsigned(bytearray.bytes().len() as u32)); return Ok(Value::Unsigned(bytearray.len() as u32));
} }
} }
@ -329,12 +324,12 @@ pub fn set_endian<'gc>(
} }
pub fn read_short<'gc>( pub fn read_short<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Integer(bytearray.read_short()? as i32)); return Ok(Value::Integer(bytearray.read_short()? as i32));
} }
} }
@ -343,12 +338,12 @@ pub fn read_short<'gc>(
} }
pub fn read_unsigned_short<'gc>( pub fn read_unsigned_short<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Unsigned(bytearray.read_unsigned_short()? as u32)); return Ok(Value::Unsigned(bytearray.read_unsigned_short()? as u32));
} }
} }
@ -357,12 +352,12 @@ pub fn read_unsigned_short<'gc>(
} }
pub fn read_double<'gc>( pub fn read_double<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Number(bytearray.read_double()?)); return Ok(Value::Number(bytearray.read_double()?));
} }
} }
@ -371,12 +366,12 @@ pub fn read_double<'gc>(
} }
pub fn read_float<'gc>( pub fn read_float<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Number(bytearray.read_float()? as f64)); return Ok(Value::Number(bytearray.read_float()? as f64));
} }
} }
@ -385,12 +380,12 @@ pub fn read_float<'gc>(
} }
pub fn read_int<'gc>( pub fn read_int<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Integer(bytearray.read_int()?)); return Ok(Value::Integer(bytearray.read_int()?));
} }
} }
@ -399,12 +394,12 @@ pub fn read_int<'gc>(
} }
pub fn read_unsigned_int<'gc>( pub fn read_unsigned_int<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Unsigned(bytearray.read_unsigned_int()?)); return Ok(Value::Unsigned(bytearray.read_unsigned_int()?));
} }
} }
@ -413,12 +408,12 @@ pub fn read_unsigned_int<'gc>(
} }
pub fn read_boolean<'gc>( pub fn read_boolean<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Bool(bytearray.read_boolean()?)); return Ok(Value::Bool(bytearray.read_boolean()?));
} }
} }
@ -427,12 +422,12 @@ pub fn read_boolean<'gc>(
} }
pub fn read_byte<'gc>( pub fn read_byte<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Integer(bytearray.read_byte()? as i32)); return Ok(Value::Integer(bytearray.read_byte()? as i32));
} }
} }
@ -446,14 +441,14 @@ pub fn read_utf_bytes<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
let len = args let len = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_u32(activation)?; .coerce_to_u32(activation)?;
return Ok(AvmString::new( return Ok(AvmString::new(
activation.context.gc_context, activation.context.gc_context,
String::from_utf8_lossy(bytearray.read_exact(len as usize)?), String::from_utf8_lossy(bytearray.read_bytes(len as usize)?),
) )
.into()); .into());
} }
@ -463,12 +458,12 @@ pub fn read_utf_bytes<'gc>(
} }
pub fn read_unsigned_byte<'gc>( pub fn read_unsigned_byte<'gc>(
activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
return Ok(Value::Unsigned(bytearray.read_unsigned_byte()? as u32)); return Ok(Value::Unsigned(bytearray.read_unsigned_byte()? as u32));
} }
} }
@ -487,7 +482,7 @@ pub fn write_float<'gc>(
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_number(activation)?; .coerce_to_number(activation)?;
bytearray.write_float(num as f32); bytearray.write_float(num as f32)?;
} }
} }
@ -505,7 +500,7 @@ pub fn write_double<'gc>(
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_number(activation)?; .coerce_to_number(activation)?;
bytearray.write_double(num); bytearray.write_double(num)?;
} }
} }
@ -520,7 +515,7 @@ pub fn write_boolean<'gc>(
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
let num = args.get(0).unwrap_or(&Value::Undefined).coerce_to_boolean(); let num = args.get(0).unwrap_or(&Value::Undefined).coerce_to_boolean();
bytearray.write_boolean(num); bytearray.write_boolean(num)?;
} }
} }
@ -538,7 +533,7 @@ pub fn write_int<'gc>(
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
bytearray.write_int(num); bytearray.write_int(num)?;
} }
} }
@ -556,7 +551,7 @@ pub fn write_unsigned_int<'gc>(
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_u32(activation)?; .coerce_to_u32(activation)?;
bytearray.write_unsigned_int(num); bytearray.write_unsigned_int(num)?;
} }
} }
@ -574,7 +569,7 @@ pub fn write_short<'gc>(
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
bytearray.write_short(num as i16); bytearray.write_short(num as i16)?;
} }
} }
@ -598,7 +593,7 @@ pub fn write_multibyte<'gc>(
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
let encoder = Encoding::for_label(charset_label.as_bytes()).unwrap_or(UTF_8); let encoder = Encoding::for_label(charset_label.as_bytes()).unwrap_or(UTF_8);
let (encoded_bytes, _, _) = encoder.encode(string.as_str()); let (encoded_bytes, _, _) = encoder.encode(string.as_str());
bytearray.write_bytes(&encoded_bytes.into_owned()); bytearray.write_bytes(&encoded_bytes.into_owned())?;
} }
} }
@ -611,7 +606,7 @@ pub fn read_multibyte<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(bytearray) = this.as_bytearray() {
let len = args let len = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
@ -620,7 +615,7 @@ pub fn read_multibyte<'gc>(
.get(1) .get(1)
.unwrap_or(&"UTF-8".into()) .unwrap_or(&"UTF-8".into())
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
let bytes = bytearray.read_exact(len as usize)?; let bytes = bytearray.read_bytes(len as usize)?;
let encoder = Encoding::for_label(charset_label.as_bytes()).unwrap_or(UTF_8); let encoder = Encoding::for_label(charset_label.as_bytes()).unwrap_or(UTF_8);
let (decoded_str, _, _) = encoder.decode(bytes); let (decoded_str, _, _) = encoder.decode(bytes);
return Ok(AvmString::new(activation.context.gc_context, decoded_str).into()); return Ok(AvmString::new(activation.context.gc_context, decoded_str).into());
@ -641,7 +636,7 @@ pub fn write_utf_bytes<'gc>(
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_string(activation)?; .coerce_to_string(activation)?;
bytearray.write_bytes(string.as_bytes()); bytearray.write_bytes(string.as_bytes())?;
} }
} }
@ -655,17 +650,13 @@ pub fn compress<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
if let Value::String(string) = args.get(0).unwrap_or(&Value::Undefined) { let algorithm = args
let compressed = match string.as_str() { .get(0)
"zlib" => bytearray.zlib_compress(), .unwrap_or(&"zlib".into())
"deflate" => bytearray.deflate_compress(), .coerce_to_string(activation)?;
&_ => return Ok(Value::Undefined), let buffer = bytearray.compress(CompressionAlgorithm::from_str(algorithm.as_str())?)?;
}; bytearray.clear();
if let Ok(buffer) = compressed { bytearray.write_bytes(&buffer)?;
bytearray.clear();
bytearray.write_bytes(&buffer);
}
}
} }
} }
@ -679,17 +670,14 @@ pub fn uncompress<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
if let Value::String(string) = args.get(0).unwrap_or(&Value::Undefined) { let algorithm = args
let compressed = match string.as_str() { .get(0)
"zlib" => bytearray.zlib_decompress(), .unwrap_or(&"zlib".into())
"deflate" => bytearray.deflate_decompress(), .coerce_to_string(activation)?;
&_ => return Ok(Value::Undefined), let buffer =
}; bytearray.decompress(CompressionAlgorithm::from_str(algorithm.as_str())?)?;
if let Ok(buffer) = compressed { bytearray.clear();
bytearray.clear(); bytearray.write_bytes(&buffer)?;
bytearray.write_bytes(&buffer);
}
}
} }
} }
@ -703,10 +691,9 @@ pub fn deflate<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
if let Ok(buffer) = bytearray.deflate_compress() { let buffer = bytearray.compress(CompressionAlgorithm::Deflate)?;
bytearray.clear(); bytearray.clear();
bytearray.write_bytes(&buffer); bytearray.write_bytes(&buffer)?;
}
} }
} }
@ -720,10 +707,9 @@ pub fn inflate<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) { if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
if let Ok(buffer) = bytearray.deflate_decompress() { let buffer = bytearray.decompress(CompressionAlgorithm::Deflate)?;
bytearray.clear(); bytearray.clear();
bytearray.write_bytes(&buffer); bytearray.write_bytes(&buffer)?;
}
} }
} }

View File

@ -0,0 +1,46 @@
use crate::avm2::activation::Activation;
use crate::avm2::class::{Class, ClassAttributes};
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.utils.CompressionAlgorithm`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Implements `flash.utils.CompressionAlgorithm`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
let class = Class::new(
QName::new(Namespace::package("flash.utils"), "CompressionAlgorithm"),
None,
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
);
let mut write = class.write(mc);
write.set_attributes(ClassAttributes::FINAL | ClassAttributes::SEALED);
const CONSTANTS: &[(&str, &str)] =
&[("DEFLATE", "deflate"), ("LZMA", "lzma"), ("ZLIB", "zlib")];
write.define_public_constant_string_class_traits(CONSTANTS);
class
}

View File

@ -6,42 +6,83 @@ package {
import flash.utils.ByteArray; import flash.utils.ByteArray;
import flash.utils.Endian; import flash.utils.Endian;
import flash.utils.CompressionAlgorithm;
var test = new ByteArray(); var ba1 = new ByteArray();
test.writeUTFBytes("HELLO TEST"); var ba2 = new ByteArray();
test.position = 0; ba1.writeUTFBytes("test data");
trace(test.readByte()); ba2.writeUTFBytes("more data");
trace(test.readByte()); ba1.writeBytes(ba2, 3, 2);
trace(test.readByte()); trace("// ba1.writeBytes(ba2, 3, 2);");
trace(test.readByte()); trace(ba1);
trace(test.readFloat()); ba1.position = 0;
test.position -= 4; ba1.readBytes(ba2, 2, 3);
test.endian = Endian.LITTLE_ENDIAN; trace("// ba1.readBytes(ba2, 2, 3);");
trace(test.readFloat()); trace(ba2);
test.clear(); ba1.position = 0;
test.writeUTFBytes("Test"); ba1.position = 100;
test.position = 0; ba1.writeUnsignedInt(2);
trace(test.readUTFBytes(4)); trace("// ba1.position = 100;");
test.position = 3; trace("// ba1.writeUnsignedInt(2);");
test.writeBytes(test); trace(ba1.length);
trace(test.toString()); ba1.clear();
test.position = 0; trace("// ba1.clear();");
var test2 = new ByteArray(); trace(ba1.length);
test.readBytes(test2); ba1.writeDouble(6.6);
trace(test2.toString()); ba1.position = 3;
trace(test2.position); trace("// ba1.writeDouble(6.6);");
trace(test.position); trace("// ba1.position = 3;")
trace(test2.bytesAvailable); trace("// ba1.bytesAvailable;")
trace(test2.readMultiByte(5, "shift-jis")); trace(ba1.bytesAvailable);
trace(test2.readShort()); ba1.clear();
test2.clear(); ba1.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis");
test2.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis"); trace("// ba1.writeMultiByte(\"次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 \", \"shift-jis\");");
test2.position = 0; ba1.position = 0;
trace(test2.readMultiByte(6, "shift-jis")); trace("// ba1.readMultiByte(6, \"shift-jis\")");
test2.clear(); trace(ba1.readMultiByte(6, "shift-jis"));
test2.writeUTF("THIS IS A TEST UTF STRING"); ba1.clear();
test2.position = 0; ba1.writeFloat(3);
trace(test2.readUTF()); ba1.writeDouble(5);
trace(test2[1]); ba1.writeInt(-10);
test2[0] = 90; ba1.writeUnsignedInt(20);
trace(test2.toString()); ba1.writeShort(40);
ba1.writeShort(22);
ba1.writeBoolean(false);
ba1.writeBoolean(true);
ba1.writeBoolean(10);
ba1.writeByte(100);
ba1.writeByte(255);
ba1.position = 0;
trace(ba1.readFloat());
trace(ba1.readDouble());
trace(ba1.readInt());
trace(ba1.readUnsignedInt());
trace(ba1.readShort());
trace(ba1.readUnsignedShort());
trace(ba1.readBoolean());
trace(ba1.readBoolean());
trace(ba1.readBoolean());
trace(ba1.readByte());
trace(ba1.readUnsignedByte());
ba1.clear();
ba1.writeFloat(3);
ba1.writeDouble(5);
ba1.writeInt(-10);
ba1.writeUnsignedInt(20);
ba1.writeShort(40);
ba1.writeShort(22);
ba1.writeBoolean(false);
ba1.writeBoolean(true);
ba1.writeBoolean(10);
ba1.writeByte(100);
ba1.writeByte(255);
ba1.position = 0;
trace(ba1.readUnsignedByte());
trace(ba1.readByte());
trace(ba1.readBoolean());
trace(ba1.readBoolean());
trace(ba1.readBoolean());
trace(ba1.readUnsignedShort());
trace(ba1.readShort());
trace(ba1.readUnsignedInt());
trace(ba1.readInt());

View File

@ -1,18 +1,36 @@
72 // ba1.writeBytes(ba2, 3, 2);
69 test datae
76 // ba1.readBytes(ba2, 2, 3);
76 motesdata
2689877248 // ba1.position = 100;
3394.019287109375 // ba1.writeUnsignedInt(2);
Test 104
TesTest // ba1.clear();
TesTest 0
0 // ba1.writeDouble(6.6);
7 // ba1.position = 3;
7 // ba1.bytesAvailable;
TesTe 5
29556 // ba1.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis");
次 滋 // ba1.readMultiByte(6, "shift-jis")
THIS IS A TEST UTF STRING 次 滋
25 3
ZTHIS IS A TEST UTF STRING 5
-10
20
40
22
false
true
true
100
255
64
64
false
false
true
5120
0
255
-2560