avm2: ByteArray updates
This commit is contained in:
parent
e010775099
commit
1ee61cca57
|
@ -3143,6 +3143,7 @@ dependencies = [
|
|||
"jpeg-decoder",
|
||||
"json",
|
||||
"log",
|
||||
"lzma-rs",
|
||||
"minimp3",
|
||||
"nellymoser-rs",
|
||||
"num-derive",
|
||||
|
|
|
@ -38,6 +38,7 @@ nellymoser-rs = { git = "https://github.com/ruffle-rs/nellymoser", branch = "mai
|
|||
regress = "0.3"
|
||||
flash-lso = { git = "https://github.com/ruffle-rs/rust-flash-lso", rev = "e39a8abc897289696672858e30bbc9e43b1c98ac" }
|
||||
json = "0.12.4"
|
||||
lzma-rs = {version = "0.2.0", optional = true }
|
||||
|
||||
[dependencies.jpeg-decoder]
|
||||
version = "0.1.22"
|
||||
|
@ -50,6 +51,6 @@ env_logger = "0.8.4"
|
|||
|
||||
[features]
|
||||
default = ["minimp3", "serde"]
|
||||
lzma = ["swf/lzma"]
|
||||
lzma = ["lzma-rs", "swf/lzma"]
|
||||
wasm-bindgen = [ "instant/wasm-bindgen" ]
|
||||
avm_debug = []
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::context::UpdateContext;
|
|||
use crate::swf::extensions::ReadSwfExt;
|
||||
use gc_arena::{Gc, GcCell, MutationContext};
|
||||
use smallvec::SmallVec;
|
||||
use std::convert::TryInto;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use swf::avm2::read::Reader;
|
||||
use swf::avm2::types::{
|
||||
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
|
||||
.as_bytearray_mut(self.context.gc_context)
|
||||
.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)
|
||||
}
|
||||
|
@ -2587,11 +2586,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.as_bytearray_mut(self.context.gc_context)
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
|
||||
if address < 0 || (address as usize + 1) >= 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)
|
||||
}
|
||||
|
@ -2606,11 +2603,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.as_bytearray_mut(self.context.gc_context)
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
|
||||
if address < 0 || (address as usize + 3) >= 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)
|
||||
}
|
||||
|
@ -2625,11 +2620,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.as_bytearray_mut(self.context.gc_context)
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
|
||||
if address < 0 || (address as usize + 3) >= 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)
|
||||
}
|
||||
|
@ -2644,11 +2637,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.as_bytearray_mut(self.context.gc_context)
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
|
||||
if address < 0 || (address as usize + 7) >= 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)
|
||||
}
|
||||
|
@ -2664,7 +2655,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let val = dm.get(address);
|
||||
|
||||
if let Some(val) = val {
|
||||
self.context.avm2.push(Value::Integer(val as i32));
|
||||
self.context.avm2.push(val);
|
||||
} else {
|
||||
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
|
||||
.as_bytearray()
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
let val = dm.get_range(address..address + 2);
|
||||
|
||||
if let Some(val) = val {
|
||||
self.context.avm2.push(Value::Integer(
|
||||
u16::from_le_bytes(val.try_into().unwrap()) as i32
|
||||
));
|
||||
} else {
|
||||
return Err("RangeError: The specified range is invalid".into());
|
||||
}
|
||||
let val = dm.read_at(2, address)?;
|
||||
self.context
|
||||
.avm2
|
||||
.push(u16::from_le_bytes(val.try_into().unwrap()));
|
||||
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
@ -2701,16 +2687,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let dm = dm
|
||||
.as_bytearray()
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
let val = dm.get_range(address..address + 4);
|
||||
|
||||
if let Some(val) = val {
|
||||
self.context
|
||||
.avm2
|
||||
.push(Value::Integer(i32::from_le_bytes(val.try_into().unwrap())));
|
||||
} else {
|
||||
return Err("RangeError: The specified range is invalid".into());
|
||||
}
|
||||
|
||||
let val = dm.read_at(4, address)?;
|
||||
self.context
|
||||
.avm2
|
||||
.push(i32::from_le_bytes(val.try_into().unwrap()));
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
||||
|
@ -2722,15 +2702,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let dm = dm
|
||||
.as_bytearray()
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
let val = dm.get_range(address..address + 4);
|
||||
|
||||
if let Some(val) = val {
|
||||
self.context.avm2.push(Value::Number(
|
||||
f32::from_le_bytes(val.try_into().unwrap()) as f64
|
||||
));
|
||||
} else {
|
||||
return Err("RangeError: The specified range is invalid".into());
|
||||
}
|
||||
let val = dm.read_at(4, address)?;
|
||||
self.context
|
||||
.avm2
|
||||
.push(f32::from_le_bytes(val.try_into().unwrap()));
|
||||
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
@ -2743,16 +2718,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
let dm = dm
|
||||
.as_bytearray()
|
||||
.ok_or_else(|| "Unable to get bytearray storage".to_string())?;
|
||||
let val = dm.get_range(address..address + 8);
|
||||
|
||||
if let Some(val) = val {
|
||||
self.context
|
||||
.avm2
|
||||
.push(Value::Number(f64::from_le_bytes(val.try_into().unwrap())));
|
||||
} else {
|
||||
return Err("RangeError: The specified range is invalid".into());
|
||||
}
|
||||
|
||||
let val = dm.read_at(8, address)?;
|
||||
self.context
|
||||
.avm2
|
||||
.push(f64::from_le_bytes(val.try_into().unwrap()));
|
||||
Ok(FrameControl::Continue)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,13 @@ use crate::avm2::Error;
|
|||
use flate2::read::*;
|
||||
use flate2::Compression;
|
||||
use gc_arena::Collect;
|
||||
use std::cell::Cell;
|
||||
use std::cmp;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::io;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::io::prelude::*;
|
||||
use std::ops::Range;
|
||||
use std::io::{self, Read, SeekFrom};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, Collect, Debug)]
|
||||
#[collect(no_drop)]
|
||||
|
@ -15,16 +17,47 @@ pub enum Endian {
|
|||
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)]
|
||||
#[collect(no_drop)]
|
||||
pub struct ByteArrayStorage {
|
||||
/// Underlying ByteArray
|
||||
bytes: Vec<u8>,
|
||||
|
||||
// The current position to read/write from
|
||||
position: usize,
|
||||
/// The current position to read/write from
|
||||
position: Cell<usize>,
|
||||
|
||||
/// This represents what endian to use while reading data.
|
||||
/// This represents what endian to use while reading/writing data.
|
||||
endian: Endian,
|
||||
}
|
||||
|
||||
|
@ -33,272 +66,156 @@ impl ByteArrayStorage {
|
|||
pub fn new() -> ByteArrayStorage {
|
||||
ByteArrayStorage {
|
||||
bytes: Vec::new(),
|
||||
position: 0,
|
||||
position: Cell::new(0),
|
||||
endian: Endian::Big,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a byte at next position in the bytearray
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
let bytes_len = self.bytes.len();
|
||||
// Allocate space for the byte
|
||||
self.position += 1;
|
||||
if self.position > bytes_len {
|
||||
self.bytes.resize(self.position, 0);
|
||||
}
|
||||
self.bytes[self.position - 1] = byte;
|
||||
/// Write bytes at the next position in the ByteArray, growing if needed.
|
||||
#[inline]
|
||||
pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Error> {
|
||||
self.write_at(buf, self.position.get())?;
|
||||
self.position.set(self.position.get() + buf.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write bytes at next position in bytearray (This function is similar to whats in std::io::Cursor)
|
||||
pub fn write_bytes(&mut self, buf: &[u8]) {
|
||||
// Make sure the internal buffer is as least as big as where we
|
||||
// currently are
|
||||
let len = self.bytes.len();
|
||||
if len < self.position {
|
||||
// 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();
|
||||
/// Reads any amount of bytes from the current position in the ByteArray
|
||||
#[inline]
|
||||
pub fn read_bytes(&self, amnt: usize) -> Result<&[u8], Error> {
|
||||
let bytes = self.read_at(amnt, self.position.get())?;
|
||||
self.position.set(self.position.get() + amnt);
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
// Write bytes at an offset, ignoring the current position
|
||||
pub fn write_bytes_at(&mut self, buf: &[u8], offset: usize) {
|
||||
// Make sure the internal buffer is as least as big as where we
|
||||
// currently are
|
||||
let len = self.bytes.len();
|
||||
if len < offset {
|
||||
// use `resize` so that the zero filling is as efficient as possible
|
||||
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);
|
||||
}
|
||||
/// Reads any amount of bytes at any offset in the ByteArray
|
||||
#[inline]
|
||||
pub fn read_at(&self, amnt: usize, offset: usize) -> Result<&[u8], Error> {
|
||||
self.bytes
|
||||
.get(offset..)
|
||||
.and_then(|bytes| bytes.get(..amnt))
|
||||
.ok_or_else(|| "EOFError: Reached EOF".into())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.bytes.clear();
|
||||
// According to docs, this is where the bytearray should free resources
|
||||
self.bytes.shrink_to_fit();
|
||||
self.position = 0;
|
||||
/// Write bytes at any offset in the ByteArray
|
||||
/// Will automatically grow the ByteArray to fit the new buffer
|
||||
pub fn write_at(&mut self, buf: &[u8], offset: usize) -> Result<(), Error> {
|
||||
let new_len = offset
|
||||
.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
|
||||
pub fn zlib_compress(&mut self) -> io::Result<Vec<u8>> {
|
||||
/// Write bytes at any offset in the ByteArray
|
||||
/// 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 compresser = ZlibEncoder::new(&*self.bytes, Compression::fast());
|
||||
compresser.read_to_end(&mut buffer)?;
|
||||
match algorithm {
|
||||
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)
|
||||
}
|
||||
|
||||
// Returns the bytearray compressed with deflate
|
||||
pub fn deflate_compress(&mut self) -> io::Result<Vec<u8>> {
|
||||
/// Decompress the ByteArray into a temporary buffer
|
||||
pub fn decompress(&mut self, algorithm: CompressionAlgorithm) -> Result<Vec<u8>, Error> {
|
||||
let mut buffer = Vec::new();
|
||||
let mut compresser = DeflateEncoder::new(&*self.bytes, Compression::fast());
|
||||
compresser.read_to_end(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
// Returns the bytearray decompressed with zlib
|
||||
pub fn zlib_decompress(&mut self) -> io::Result<Vec<u8>> {
|
||||
let mut buffer = Vec::new();
|
||||
let mut compresser = ZlibDecoder::new(&*self.bytes);
|
||||
compresser.read_to_end(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
// Returns the bytearray decompressed with deflate
|
||||
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());
|
||||
match algorithm {
|
||||
CompressionAlgorithm::Zlib => {
|
||||
let mut compresser = ZlibDecoder::new(&*self.bytes);
|
||||
compresser.read_to_end(&mut buffer)?;
|
||||
}
|
||||
CompressionAlgorithm::Deflate => {
|
||||
let mut compresser = DeflateDecoder::new(&*self.bytes);
|
||||
compresser.read_to_end(&mut buffer)?;
|
||||
}
|
||||
#[cfg(feature = "lzma")]
|
||||
CompressionAlgorithm::Lzma => lzma_rs::lzma_decompress(&mut &*self.bytes, &mut buffer)?,
|
||||
#[cfg(not(feature = "lzma"))]
|
||||
CompressionAlgorithm::Lzma => {
|
||||
return Err("Ruffle was not compiled with LZMA support".into())
|
||||
}
|
||||
}
|
||||
let val = Ok(&self.bytes[self.position..self.position + amnt]);
|
||||
self.position += amnt;
|
||||
val
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn read_utf(&mut self) -> Result<String, Error> {
|
||||
pub fn read_utf(&self) -> Result<String, Error> {
|
||||
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())
|
||||
}
|
||||
|
||||
// Reads a i16 from the buffer
|
||||
pub fn read_short(&mut self) -> Result<i16, Error> {
|
||||
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()),
|
||||
})
|
||||
pub fn write_boolean(&mut self, val: bool) -> Result<(), Error> {
|
||||
self.write_bytes(&[val as u8; 1])
|
||||
}
|
||||
|
||||
// Reads a u16 from the buffer
|
||||
pub fn read_unsigned_short(&mut self) -> Result<u16, Error> {
|
||||
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);
|
||||
pub fn read_boolean(&self) -> Result<bool, Error> {
|
||||
Ok(self.read_bytes(1)? != [0])
|
||||
}
|
||||
|
||||
// Writes a UTF String into the buffer, with its length as a prefix
|
||||
pub fn write_utf(&mut self, utf_string: &str) -> Result<(), Error> {
|
||||
if let Ok(str_size) = u16::try_from(utf_string.len()) {
|
||||
self.write_unsigned_short(str_size);
|
||||
self.write_bytes(utf_string.as_bytes());
|
||||
self.write_unsigned_short(str_size)?;
|
||||
self.write_bytes(utf_string.as_bytes())
|
||||
} 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> {
|
||||
self.bytes.get(item).copied()
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.bytes.clear();
|
||||
self.position.set(0)
|
||||
}
|
||||
|
||||
pub fn get_range(&self, item: Range<usize>) -> Option<&[u8]> {
|
||||
self.bytes.get(item)
|
||||
#[inline]
|
||||
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) {
|
||||
if self.bytes.len() < (item + 1) {
|
||||
if self.len() < (item + 1) {
|
||||
self.bytes.resize(item + 1, 0)
|
||||
}
|
||||
|
||||
|
@ -311,38 +228,52 @@ impl ByteArrayStorage {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bytes(&self) -> &Vec<u8> {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.bytes.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position(&self) -> usize {
|
||||
self.position
|
||||
self.position.get()
|
||||
}
|
||||
|
||||
pub fn set_position(&mut self, pos: usize) {
|
||||
self.position = pos;
|
||||
#[inline]
|
||||
pub fn set_position(&self, pos: usize) {
|
||||
self.position.set(pos);
|
||||
}
|
||||
|
||||
pub fn add_position(&mut self, amnt: usize) {
|
||||
self.position += amnt;
|
||||
#[inline]
|
||||
pub fn add_position(&self, amnt: usize) {
|
||||
self.position.set(self.position.get() + amnt);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn endian(&self) -> &Endian {
|
||||
&self.endian
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_endian(&mut self, new_endian: Endian) {
|
||||
self.endian = new_endian;
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.bytes.len()
|
||||
#[inline]
|
||||
pub fn bytes_available(&self) -> usize {
|
||||
self.len().saturating_sub(self.position.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for ByteArrayStorage {
|
||||
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())
|
||||
}
|
||||
|
@ -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 {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
|
|
@ -598,6 +598,14 @@ pub fn load_player_globals<'gc>(
|
|||
script,
|
||||
)?;
|
||||
|
||||
class(
|
||||
activation,
|
||||
flash::utils::compression_algorithm::create_class(mc),
|
||||
implicit_deriver,
|
||||
domain,
|
||||
script,
|
||||
)?;
|
||||
|
||||
function(
|
||||
mc,
|
||||
"flash.utils",
|
||||
|
|
|
@ -307,15 +307,16 @@ pub fn bytes<'gc>(
|
|||
// off. We scroll back 2 bytes before writing the actual
|
||||
// datastream as it is guaranteed to at least be as long as
|
||||
// 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.write_bytes(root.data());
|
||||
ba_write.write_bytes(root.data())?;
|
||||
|
||||
// `swf` wrote the wrong length (since we wrote the data
|
||||
// ourselves), so we need to overwrite it ourselves.
|
||||
ba_write.set_position(4);
|
||||
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.
|
||||
ba_write.set_position(0);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use crate::avm2::{Activation, Error, Object, Value};
|
||||
|
||||
pub mod bytearray;
|
||||
pub mod compression_algorithm;
|
||||
pub mod endian;
|
||||
|
||||
/// Implements `flash.utils.getTimer`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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::method::{Method, NativeMethod};
|
||||
use crate::avm2::names::{Namespace, QName};
|
||||
|
@ -10,6 +10,7 @@ use crate::avm2::Error;
|
|||
use encoding_rs::Encoding;
|
||||
use encoding_rs::UTF_8;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Implements `flash.utils.ByteArray`'s instance constructor.
|
||||
pub fn instance_init<'gc>(
|
||||
|
@ -46,7 +47,7 @@ pub fn write_byte<'gc>(
|
|||
.cloned()
|
||||
.unwrap_or(Value::Undefined)
|
||||
.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]
|
||||
} else {
|
||||
&combining_bytes[offset..]
|
||||
});
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +133,7 @@ pub fn read_bytes<'gc>(
|
|||
¤t_bytes[position..]
|
||||
};
|
||||
merging_offset = to_write.len();
|
||||
merging_storage.write_bytes_at(to_write, offset);
|
||||
merging_storage.write_at(to_write, offset)?;
|
||||
} else {
|
||||
return Err("ArgumentError: Parameter must be a bytearray".into());
|
||||
}
|
||||
|
@ -167,7 +168,7 @@ pub fn read_utf<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -181,8 +182,7 @@ pub fn to_string<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
if let Some(bytearray) = this.as_bytearray() {
|
||||
let bytes = bytearray.bytes();
|
||||
let (new_string, _, _) = UTF_8.decode(bytes);
|
||||
let (new_string, _, _) = UTF_8.decode(bytearray.bytes());
|
||||
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(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
|
||||
bytearray.clear();
|
||||
bytearray.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ pub fn set_position<'gc>(
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Integer(0))
|
||||
|
@ -243,13 +244,7 @@ pub fn bytes_available<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
if let Some(bytearray) = this.as_bytearray() {
|
||||
return Ok(Value::Unsigned(
|
||||
if bytearray.position() > bytearray.bytes().len() {
|
||||
0
|
||||
} else {
|
||||
(bytearray.bytes().len() - bytearray.position()) as u32
|
||||
},
|
||||
));
|
||||
return Ok(Value::Unsigned(bytearray.bytes_available() as u32));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +258,7 @@ pub fn length<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
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>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -343,12 +338,12 @@ pub fn read_short<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_unsigned_short<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -357,12 +352,12 @@ pub fn read_unsigned_short<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_double<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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()?));
|
||||
}
|
||||
}
|
||||
|
@ -371,12 +366,12 @@ pub fn read_double<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_float<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -385,12 +380,12 @@ pub fn read_float<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_int<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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()?));
|
||||
}
|
||||
}
|
||||
|
@ -399,12 +394,12 @@ pub fn read_int<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_unsigned_int<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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()?));
|
||||
}
|
||||
}
|
||||
|
@ -413,12 +408,12 @@ pub fn read_unsigned_int<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_boolean<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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()?));
|
||||
}
|
||||
}
|
||||
|
@ -427,12 +422,12 @@ pub fn read_boolean<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_byte<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -446,14 +441,14 @@ pub fn read_utf_bytes<'gc>(
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.coerce_to_u32(activation)?;
|
||||
return Ok(AvmString::new(
|
||||
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());
|
||||
}
|
||||
|
@ -463,12 +458,12 @@ pub fn read_utf_bytes<'gc>(
|
|||
}
|
||||
|
||||
pub fn read_unsigned_byte<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +482,7 @@ pub fn write_float<'gc>(
|
|||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.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)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.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(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
|
||||
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)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.coerce_to_i32(activation)?;
|
||||
bytearray.write_int(num);
|
||||
bytearray.write_int(num)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,7 +551,7 @@ pub fn write_unsigned_int<'gc>(
|
|||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.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)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.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)?;
|
||||
let encoder = Encoding::for_label(charset_label.as_bytes()).unwrap_or(UTF_8);
|
||||
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>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
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
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
|
@ -620,7 +615,7 @@ pub fn read_multibyte<'gc>(
|
|||
.get(1)
|
||||
.unwrap_or(&"UTF-8".into())
|
||||
.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 (decoded_str, _, _) = encoder.decode(bytes);
|
||||
return Ok(AvmString::new(activation.context.gc_context, decoded_str).into());
|
||||
|
@ -641,7 +636,7 @@ pub fn write_utf_bytes<'gc>(
|
|||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.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> {
|
||||
if let Some(this) = this {
|
||||
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 compressed = match string.as_str() {
|
||||
"zlib" => bytearray.zlib_compress(),
|
||||
"deflate" => bytearray.deflate_compress(),
|
||||
&_ => return Ok(Value::Undefined),
|
||||
};
|
||||
if let Ok(buffer) = compressed {
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer);
|
||||
}
|
||||
}
|
||||
let algorithm = args
|
||||
.get(0)
|
||||
.unwrap_or(&"zlib".into())
|
||||
.coerce_to_string(activation)?;
|
||||
let buffer = bytearray.compress(CompressionAlgorithm::from_str(algorithm.as_str())?)?;
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -679,17 +670,14 @@ pub fn uncompress<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
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 compressed = match string.as_str() {
|
||||
"zlib" => bytearray.zlib_decompress(),
|
||||
"deflate" => bytearray.deflate_decompress(),
|
||||
&_ => return Ok(Value::Undefined),
|
||||
};
|
||||
if let Ok(buffer) = compressed {
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer);
|
||||
}
|
||||
}
|
||||
let algorithm = args
|
||||
.get(0)
|
||||
.unwrap_or(&"zlib".into())
|
||||
.coerce_to_string(activation)?;
|
||||
let buffer =
|
||||
bytearray.decompress(CompressionAlgorithm::from_str(algorithm.as_str())?)?;
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -703,10 +691,9 @@ pub fn deflate<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
|
||||
if let Ok(buffer) = bytearray.deflate_compress() {
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer);
|
||||
}
|
||||
let buffer = bytearray.compress(CompressionAlgorithm::Deflate)?;
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -720,10 +707,9 @@ pub fn inflate<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
if let Some(mut bytearray) = this.as_bytearray_mut(activation.context.gc_context) {
|
||||
if let Ok(buffer) = bytearray.deflate_decompress() {
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer);
|
||||
}
|
||||
let buffer = bytearray.decompress(CompressionAlgorithm::Deflate)?;
|
||||
bytearray.clear();
|
||||
bytearray.write_bytes(&buffer)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -6,42 +6,83 @@ package {
|
|||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Endian;
|
||||
import flash.utils.CompressionAlgorithm;
|
||||
|
||||
var test = new ByteArray();
|
||||
test.writeUTFBytes("HELLO TEST");
|
||||
test.position = 0;
|
||||
trace(test.readByte());
|
||||
trace(test.readByte());
|
||||
trace(test.readByte());
|
||||
trace(test.readByte());
|
||||
trace(test.readFloat());
|
||||
test.position -= 4;
|
||||
test.endian = Endian.LITTLE_ENDIAN;
|
||||
trace(test.readFloat());
|
||||
test.clear();
|
||||
test.writeUTFBytes("Test");
|
||||
test.position = 0;
|
||||
trace(test.readUTFBytes(4));
|
||||
test.position = 3;
|
||||
test.writeBytes(test);
|
||||
trace(test.toString());
|
||||
test.position = 0;
|
||||
var test2 = new ByteArray();
|
||||
test.readBytes(test2);
|
||||
trace(test2.toString());
|
||||
trace(test2.position);
|
||||
trace(test.position);
|
||||
trace(test2.bytesAvailable);
|
||||
trace(test2.readMultiByte(5, "shift-jis"));
|
||||
trace(test2.readShort());
|
||||
test2.clear();
|
||||
test2.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis");
|
||||
test2.position = 0;
|
||||
trace(test2.readMultiByte(6, "shift-jis"));
|
||||
test2.clear();
|
||||
test2.writeUTF("THIS IS A TEST UTF STRING");
|
||||
test2.position = 0;
|
||||
trace(test2.readUTF());
|
||||
trace(test2[1]);
|
||||
test2[0] = 90;
|
||||
trace(test2.toString());
|
||||
var ba1 = new ByteArray();
|
||||
var ba2 = new ByteArray();
|
||||
ba1.writeUTFBytes("test data");
|
||||
ba2.writeUTFBytes("more data");
|
||||
ba1.writeBytes(ba2, 3, 2);
|
||||
trace("// ba1.writeBytes(ba2, 3, 2);");
|
||||
trace(ba1);
|
||||
ba1.position = 0;
|
||||
ba1.readBytes(ba2, 2, 3);
|
||||
trace("// ba1.readBytes(ba2, 2, 3);");
|
||||
trace(ba2);
|
||||
ba1.position = 0;
|
||||
ba1.position = 100;
|
||||
ba1.writeUnsignedInt(2);
|
||||
trace("// ba1.position = 100;");
|
||||
trace("// ba1.writeUnsignedInt(2);");
|
||||
trace(ba1.length);
|
||||
ba1.clear();
|
||||
trace("// ba1.clear();");
|
||||
trace(ba1.length);
|
||||
ba1.writeDouble(6.6);
|
||||
ba1.position = 3;
|
||||
trace("// ba1.writeDouble(6.6);");
|
||||
trace("// ba1.position = 3;")
|
||||
trace("// ba1.bytesAvailable;")
|
||||
trace(ba1.bytesAvailable);
|
||||
ba1.clear();
|
||||
ba1.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis");
|
||||
trace("// ba1.writeMultiByte(\"次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 \", \"shift-jis\");");
|
||||
ba1.position = 0;
|
||||
trace("// ba1.readMultiByte(6, \"shift-jis\")");
|
||||
trace(ba1.readMultiByte(6, "shift-jis"));
|
||||
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.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());
|
|
@ -1,18 +1,36 @@
|
|||
72
|
||||
69
|
||||
76
|
||||
76
|
||||
2689877248
|
||||
3394.019287109375
|
||||
Test
|
||||
TesTest
|
||||
TesTest
|
||||
0
|
||||
7
|
||||
7
|
||||
TesTe
|
||||
29556
|
||||
次 滋
|
||||
THIS IS A TEST UTF STRING
|
||||
25
|
||||
ZTHIS IS A TEST UTF STRING
|
||||
// ba1.writeBytes(ba2, 3, 2);
|
||||
test datae
|
||||
// ba1.readBytes(ba2, 2, 3);
|
||||
motesdata
|
||||
// ba1.position = 100;
|
||||
// ba1.writeUnsignedInt(2);
|
||||
104
|
||||
// ba1.clear();
|
||||
0
|
||||
// ba1.writeDouble(6.6);
|
||||
// ba1.position = 3;
|
||||
// ba1.bytesAvailable;
|
||||
5
|
||||
// ba1.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis");
|
||||
// ba1.readMultiByte(6, "shift-jis")
|
||||
次 滋
|
||||
3
|
||||
5
|
||||
-10
|
||||
20
|
||||
40
|
||||
22
|
||||
false
|
||||
true
|
||||
true
|
||||
100
|
||||
255
|
||||
64
|
||||
64
|
||||
false
|
||||
false
|
||||
true
|
||||
5120
|
||||
0
|
||||
255
|
||||
-2560
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue