avm2: ByteArray updates
This commit is contained in:
parent
e010775099
commit
1ee61cca57
|
@ -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",
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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>(
|
||||||
¤t_bytes[position..]
|
¤t_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)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.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());
|
|
@ -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
|
||||||
7
|
// ba1.writeDouble(6.6);
|
||||||
7
|
// ba1.position = 3;
|
||||||
TesTe
|
// ba1.bytesAvailable;
|
||||||
29556
|
5
|
||||||
|
// ba1.writeMultiByte("次 滋 治 爾 璽 痔 磁 示 而 耳 自 蒔 辞 汐 鹿 ", "shift-jis");
|
||||||
|
// ba1.readMultiByte(6, "shift-jis")
|
||||||
次 滋
|
次 滋
|
||||||
THIS IS A TEST UTF STRING
|
3
|
||||||
25
|
5
|
||||||
ZTHIS IS A TEST UTF STRING
|
-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