avm2: The stage's loader info object allows access to `url`, `bytes`, and `parameters`.

This commit is contained in:
David Wendt 2022-01-15 18:59:48 -05:00 committed by Mike Welsh
parent b181debff6
commit f041cced72
1 changed files with 72 additions and 61 deletions

View File

@ -8,7 +8,7 @@ use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{loaderinfo_allocator, DomainObject, LoaderStream, Object, TObject}; use crate::avm2::object::{loaderinfo_allocator, DomainObject, LoaderStream, Object, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{AvmString, Error}; use crate::avm2::{AvmString, Error};
use crate::display_object::TDisplayObject; use crate::display_object::{TDisplayObject, TDisplayObjectContainer};
use gc_arena::{GcCell, MutationContext}; use gc_arena::{GcCell, MutationContext};
use swf::{write_swf, Compression}; use swf::{write_swf, Compression};
@ -242,14 +242,18 @@ pub fn url<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(loader_stream) = this.as_loader_stream() { if let Some(loader_stream) = this.as_loader_stream() {
match &*loader_stream { let root = match &*loader_stream {
LoaderStream::Stage => { LoaderStream::Stage => activation
return Err("Error: The stage's loader info does not have a URL".into()) .context
} .stage
LoaderStream::Swf(root, _) => { .child_by_index(0)
let url = root.url().unwrap_or(""); .and_then(|c| c.movie()),
return Ok(AvmString::new_utf8(activation.context.gc_context, url).into()); LoaderStream::Swf(root, _) => Some(root.clone()),
} };
if let Some(root) = root {
let url = root.url().unwrap_or("");
return Ok(AvmString::new_utf8(activation.context.gc_context, url).into());
} }
} }
} }
@ -287,44 +291,47 @@ pub fn bytes<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(loader_stream) = this.as_loader_stream() { if let Some(loader_stream) = this.as_loader_stream() {
match &*loader_stream { let root = match &*loader_stream {
LoaderStream::Stage => { LoaderStream::Stage => activation
return Err("Error: The stage's loader info does not have a bytestream".into()) .context
} .stage
LoaderStream::Swf(root, _) => { .child_by_index(0)
let ba_class = activation.context.avm2.classes().bytearray; .and_then(|c| c.movie()),
LoaderStream::Swf(root, _) => Some(root.clone()),
};
let ba = ba_class.construct(activation, &[])?; if let Some(root) = root {
let mut ba_write = ba.as_bytearray_mut(activation.context.gc_context).unwrap(); let ba_class = activation.context.avm2.classes().bytearray;
// First, write a fake header corresponding to an let ba = ba_class.construct(activation, &[])?;
// uncompressed SWF let mut ba_write = ba.as_bytearray_mut(activation.context.gc_context).unwrap();
let mut header = root.header().swf_header().clone();
header.compression = Compression::None;
write_swf(&header, &[], &mut *ba_write).unwrap(); // First, write a fake header corresponding to an
// uncompressed SWF
let mut header = root.header().swf_header().clone();
header.compression = Compression::None;
// `swf` always writes an implicit end tag, let's cut that write_swf(&header, &[], &mut *ba_write).unwrap();
// 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.len() - 2;
ba_write.set_position(correct_header_length);
ba_write.write_bytes(root.data())?;
// `swf` wrote the wrong length (since we wrote the data // `swf` always writes an implicit end tag, let's cut that
// ourselves), so we need to overwrite it ourselves. // off. We scroll back 2 bytes before writing the actual
ba_write.set_position(4); // datastream as it is guaranteed to at least be as long as
ba_write.set_endian(Endian::Little); // the implicit end tag we want to get rid of.
ba_write let correct_header_length = ba_write.len() - 2;
.write_unsigned_int((root.data().len() + correct_header_length) as u32)?; ba_write.set_position(correct_header_length);
ba_write.write_bytes(root.data())?;
// Finally, reset the array to the correct state. // `swf` wrote the wrong length (since we wrote the data
ba_write.set_position(0); // ourselves), so we need to overwrite it ourselves.
ba_write.set_endian(Endian::Big); ba_write.set_position(4);
ba_write.set_endian(Endian::Little);
ba_write.write_unsigned_int((root.data().len() + correct_header_length) as u32)?;
return Ok(ba.into()); // Finally, reset the array to the correct state.
} ba_write.set_position(0);
ba_write.set_endian(Endian::Big);
return Ok(ba.into());
} }
} }
} }
@ -365,30 +372,34 @@ pub fn parameters<'gc>(
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(this) = this { if let Some(this) = this {
if let Some(loader_stream) = this.as_loader_stream() { if let Some(loader_stream) = this.as_loader_stream() {
match &*loader_stream { let root = match &*loader_stream {
LoaderStream::Stage => { LoaderStream::Stage => activation
return Err("Error: The stage's loader info does not have parameters".into()) .context
} .stage
LoaderStream::Swf(root, _) => { .child_by_index(0)
let mut params_obj = activation .and_then(|c| c.movie()),
.avm2() LoaderStream::Swf(root, _) => Some(root.clone()),
.classes() };
.object
.construct(activation, &[])?;
let parameters = root.parameters();
for (k, v) in parameters.iter() { if let Some(root) = root {
let avm_k = AvmString::new_utf8(activation.context.gc_context, k); let mut params_obj = activation
let avm_v = AvmString::new_utf8(activation.context.gc_context, v); .avm2()
params_obj.set_property( .classes()
&QName::new(Namespace::public(), avm_k).into(), .object
avm_v.into(), .construct(activation, &[])?;
activation, let parameters = root.parameters();
)?;
}
return Ok(params_obj.into()); for (k, v) in parameters.iter() {
let avm_k = AvmString::new_utf8(activation.context.gc_context, k);
let avm_v = AvmString::new_utf8(activation.context.gc_context, v);
params_obj.set_property(
&QName::new(Namespace::public(), avm_k).into(),
avm_v.into(),
activation,
)?;
} }
return Ok(params_obj.into());
} }
} }
} }