Add separate libraries for each loaded movie.

This commit is contained in:
David Wendt 2019-11-13 21:41:38 -05:00
parent e0c0779bd0
commit 5ce499d11e
14 changed files with 322 additions and 105 deletions

7
Cargo.lock generated
View File

@ -1577,6 +1577,7 @@ dependencies = [
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"swf 0.1.2", "swf 0.1.2",
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"weak-table 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -2242,6 +2243,11 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "weak-table"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.34" version = "0.3.34"
@ -2642,6 +2648,7 @@ dependencies = [
"checksum wayland-protocols 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" "checksum wayland-protocols 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9"
"checksum wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" "checksum wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d"
"checksum wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" "checksum wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4"
"checksum weak-table 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a5862bb244c852a56c6f3c39668ff181271bda44513ef30d2073a3eedd9898d"
"checksum web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ba09295448c0b93bc87d2769614d371a924749e5e6c87e4c1df8b2416b49b775" "checksum web-sys 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ba09295448c0b93bc87d2769614d371a924749e5e6c87e4c1df8b2416b49b775"
"checksum webbrowser 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "97d468a911faaaeb783693b004e1c62e0063e646b0afae5c146cd144e566e66d" "checksum webbrowser 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "97d468a911faaaeb783693b004e1c62e0063e646b0afae5c146cd144e566e66d"
"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" "checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"

View File

@ -22,6 +22,7 @@ num_enum = "0.4.2"
quick-xml = "0.17.2" quick-xml = "0.17.2"
downcast-rs = "1.1.1" downcast-rs = "1.1.1"
url = "2.1.0" url = "2.1.0"
weak-table = "0.2.3"
[dependencies.jpeg-decoder] [dependencies.jpeg-decoder]
version = "0.1.18" version = "0.1.18"

View File

@ -1645,7 +1645,7 @@ impl<'gc> Avm1<'gc> {
let that = avm.unroot_object(slot).as_display_object().unwrap(); let that = avm.unroot_object(slot).as_display_object().unwrap();
let mut mc = that.as_movie_clip().unwrap(); let mut mc = that.as_movie_clip().unwrap();
mc.replace_with_movie(uc.gc_context, movie); mc.replace_with_movie(uc.gc_context, movie.clone());
let mut morph_shapes = fnv::FnvHashMap::default(); let mut morph_shapes = fnv::FnvHashMap::default();
mc.preload(uc, &mut morph_shapes); mc.preload(uc, &mut morph_shapes);
@ -1653,7 +1653,9 @@ impl<'gc> Avm1<'gc> {
// Finalize morph shapes. // Finalize morph shapes.
for (id, static_data) in morph_shapes { for (id, static_data) in morph_shapes {
let morph_shape = MorphShape::new(uc.gc_context, static_data); let morph_shape = MorphShape::new(uc.gc_context, static_data);
uc.library.register_character( uc.library
.library_for_movie_mut(movie.clone())
.register_character(
id, id,
crate::character::Character::MorphShape(morph_shape), crate::character::Character::MorphShape(morph_shape),
); );
@ -1738,7 +1740,7 @@ impl<'gc> Avm1<'gc> {
let that = avm.unroot_object(slot).as_display_object().unwrap(); let that = avm.unroot_object(slot).as_display_object().unwrap();
let mut mc = that.as_movie_clip().unwrap(); let mut mc = that.as_movie_clip().unwrap();
mc.replace_with_movie(uc.gc_context, movie); mc.replace_with_movie(uc.gc_context, movie.clone());
let mut morph_shapes = fnv::FnvHashMap::default(); let mut morph_shapes = fnv::FnvHashMap::default();
mc.preload(uc, &mut morph_shapes); mc.preload(uc, &mut morph_shapes);
@ -1746,7 +1748,9 @@ impl<'gc> Avm1<'gc> {
// Finalize morph shapes. // Finalize morph shapes.
for (id, static_data) in morph_shapes { for (id, static_data) in morph_shapes {
let morph_shape = MorphShape::new(uc.gc_context, static_data); let morph_shape = MorphShape::new(uc.gc_context, static_data);
uc.library.register_character( uc.library
.library_for_movie_mut(movie.clone())
.register_character(
id, id,
crate::character::Character::MorphShape(morph_shape), crate::character::Character::MorphShape(morph_shape),
); );

View File

@ -246,11 +246,15 @@ fn attach_movie<'gc>(
if depth < 0 || depth > AVM_MAX_DEPTH { if depth < 0 || depth > AVM_MAX_DEPTH {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined.into());
} }
if let Ok(mut new_clip) = context.library.instantiate_by_export_name(
&export_name, if let Ok(mut new_clip) = context
context.gc_context, .library
&avm.prototypes, .library_for_movie(movie_clip.movie().unwrap())
) { .ok_or_else(|| "Movie is missing!".into())
.and_then(|l| {
l.instantiate_by_export_name(&export_name, context.gc_context, &avm.prototypes)
})
{
// Set name and attach to parent. // Set name and attach to parent.
new_clip.set_name(context.gc_context, &new_instance_name); new_clip.set_name(context.gc_context, &new_instance_name);
movie_clip.add_child_from_avm(context, new_clip, depth); movie_clip.add_child_from_avm(context, new_clip, depth);
@ -310,6 +314,7 @@ fn create_text_field<'gc>(
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
let movie = avm.base_clip().movie().unwrap();
let instance_name = args let instance_name = args
.get(0) .get(0)
.cloned() .cloned()
@ -341,7 +346,8 @@ fn create_text_field<'gc>(
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.as_number(avm, context)?; .as_number(avm, context)?;
let mut text_field: DisplayObject<'gc> = EditText::new(context, x, y, width, height).into(); let mut text_field: DisplayObject<'gc> =
EditText::new(context, movie, x, y, width, height).into();
text_field.post_instantiation(context.gc_context, text_field, avm.prototypes().text_field); text_field.post_instantiation(context.gc_context, text_field, avm.prototypes().text_field);
text_field.set_name(context.gc_context, &instance_name); text_field.set_name(context.gc_context, &instance_name);
movie_clip.add_child_from_avm(context, text_field, depth as Depth); movie_clip.add_child_from_avm(context, text_field, depth as Depth);
@ -385,10 +391,12 @@ pub fn duplicate_movie_clip<'gc>(
if depth < 0 || depth > AVM_MAX_DEPTH { if depth < 0 || depth > AVM_MAX_DEPTH {
return Ok(Value::Undefined.into()); return Ok(Value::Undefined.into());
} }
if let Ok(mut new_clip) =
context if let Ok(mut new_clip) = context
.library .library
.instantiate_by_id(movie_clip.id(), context.gc_context, &avm.prototypes) .library_for_movie(movie_clip.movie().unwrap())
.ok_or_else(|| "Movie is missing!".into())
.and_then(|l| l.instantiate_by_id(movie_clip.id(), context.gc_context, &avm.prototypes))
{ {
// Set name and attach to parent. // Set name and attach to parent.
new_clip.set_name(context.gc_context, &new_instance_name); new_clip.set_name(context.gc_context, &new_instance_name);

View File

@ -6,6 +6,7 @@ use crate::avm1::property::Attribute::*;
use crate::avm1::return_value::ReturnValue; use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Error, Object, SoundObject, TObject, UpdateContext, Value}; use crate::avm1::{Avm1, Error, Object, SoundObject, TObject, UpdateContext, Value};
use crate::character::Character; use crate::character::Character;
use crate::display_object::TDisplayObject;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `Sound` /// Implements `Sound`
@ -167,7 +168,13 @@ fn attach_sound<'gc>(
let name = args.get(0).unwrap_or(&Value::Undefined); let name = args.get(0).unwrap_or(&Value::Undefined);
if let Some(sound_object) = this.as_sound_object() { if let Some(sound_object) = this.as_sound_object() {
let name = name.clone().coerce_to_string(avm, context)?; let name = name.clone().coerce_to_string(avm, context)?;
if let Some(Character::Sound(sound)) = context.library.get_character_by_export_name(&name) { let movie = sound_object.owner().and_then(|o| o.movie());
if let Some(movie) = movie {
if let Some(Character::Sound(sound)) = context
.library
.library_for_movie_mut(movie)
.get_character_by_export_name(&name)
{
sound_object.set_sound(context.gc_context, Some(*sound)); sound_object.set_sound(context.gc_context, Some(*sound));
sound_object.set_duration( sound_object.set_duration(
context.gc_context, context.gc_context,
@ -177,6 +184,12 @@ fn attach_sound<'gc>(
} else { } else {
log::warn!("Sound.attachSound: Sound '{}' not found", name); log::warn!("Sound.attachSound: Sound '{}' not found", name);
} }
} else {
log::warn!(
"Sound.attachSound: Cannot attach Sound '{}' without a library to reference",
name
);
}
} else { } else {
log::warn!("Sound.attachSound: this is not a Sound"); log::warn!("Sound.attachSound: this is not a Sound");
} }
@ -395,14 +408,24 @@ fn stop<'gc>(
if let Some(name) = args.get(0) { if let Some(name) = args.get(0) {
// Usage 1: Stop all instances of a particular sound, using the name parameter. // Usage 1: Stop all instances of a particular sound, using the name parameter.
let name = name.clone().coerce_to_string(avm, context)?; let name = name.clone().coerce_to_string(avm, context)?;
if let Some(Character::Sound(sound)) = let movie = sound.owner().and_then(|o| o.movie());
context.library.get_character_by_export_name(&name) if let Some(movie) = movie {
if let Some(Character::Sound(sound)) = context
.library
.library_for_movie_mut(movie)
.get_character_by_export_name(&name)
{ {
// Stop all sounds with the given name. // Stop all sounds with the given name.
context.audio.stop_sounds_with_handle(*sound); context.audio.stop_sounds_with_handle(*sound);
} else { } else {
log::warn!("Sound.stop: Sound '{}' not found", name); log::warn!("Sound.stop: Sound '{}' not found", name);
} }
} else {
log::warn!(
"Sound.stop: Cannot stop Sound '{}' without a library to reference",
name
)
}
} else if let Some(_owner) = sound.owner() { } else if let Some(_owner) = sound.owner() {
// Usage 2: Stop all sound running within a given clip. // Usage 2: Stop all sound running within a given clip.
// TODO: We just stop the last played sound for now. // TODO: We just stop the last played sound for now.

View File

@ -604,7 +604,7 @@ mod tests {
b: 0, b: 0,
a: 0, a: 0,
}, },
library: &mut Library::new(), library: &mut Library::default(),
navigator: &mut NullNavigatorBackend::new(), navigator: &mut NullNavigatorBackend::new(),
renderer: &mut NullRenderer::new(), renderer: &mut NullRenderer::new(),
system_prototypes: avm.prototypes().clone(), system_prototypes: avm.prototypes().clone(),

View File

@ -43,7 +43,7 @@ where
b: 0, b: 0,
a: 0, a: 0,
}, },
library: &mut Library::new(), library: &mut Library::default(),
navigator: &mut NullNavigatorBackend::new(), navigator: &mut NullNavigatorBackend::new(),
renderer: &mut NullRenderer::new(), renderer: &mut NullRenderer::new(),
system_prototypes: avm.prototypes().clone(), system_prototypes: avm.prototypes().clone(),

View File

@ -2,12 +2,14 @@ use crate::avm1::{Object, Value};
use crate::context::{RenderContext, UpdateContext}; use crate::context::{RenderContext, UpdateContext};
use crate::player::NEWEST_PLAYER_VERSION; use crate::player::NEWEST_PLAYER_VERSION;
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::SwfMovie;
use crate::transform::Transform; use crate::transform::Transform;
use enumset::{EnumSet, EnumSetType}; use enumset::{EnumSet, EnumSetType};
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
use ruffle_macros::enum_trait_object; use ruffle_macros::enum_trait_object;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc;
mod bitmap; mod bitmap;
mod button; mod button;
@ -345,6 +347,10 @@ impl<'gc> DisplayObjectBase<'gc> {
.map(|p| p.swf_version()) .map(|p| p.swf_version())
.unwrap_or(NEWEST_PLAYER_VERSION) .unwrap_or(NEWEST_PLAYER_VERSION)
} }
fn movie(&self) -> Option<Arc<SwfMovie>> {
self.parent.and_then(|p| p.movie())
}
} }
#[enum_trait_object( #[enum_trait_object(
@ -725,7 +731,7 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug {
.clip_actions .clip_actions
.iter() .iter()
.cloned() .cloned()
.map(|a| ClipAction::from_action_and_movie(a, clip.movie())) .map(|a| ClipAction::from_action_and_movie(a, clip.movie().unwrap()))
.collect(), .collect(),
); );
} }
@ -784,6 +790,11 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug {
.unwrap_or(NEWEST_PLAYER_VERSION) .unwrap_or(NEWEST_PLAYER_VERSION)
} }
/// Return the SWF that defines this display object.
fn movie(&self) -> Option<Arc<SwfMovie>> {
self.parent().and_then(|p| p.movie())
}
fn instantiate(&self, gc_context: MutationContext<'gc, '_>) -> DisplayObject<'gc>; fn instantiate(&self, gc_context: MutationContext<'gc, '_>) -> DisplayObject<'gc>;
fn as_ptr(&self) -> *const DisplayObjectPtr; fn as_ptr(&self) -> *const DisplayObjectPtr;

View File

@ -3,10 +3,11 @@ use crate::context::{ActionType, RenderContext, UpdateContext};
use crate::display_object::{DisplayObjectBase, TDisplayObject}; use crate::display_object::{DisplayObjectBase, TDisplayObject};
use crate::events::{ButtonEvent, ButtonEventResult, ButtonKeyCode}; use crate::events::{ButtonEvent, ButtonEventResult, ButtonKeyCode};
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::SwfSlice; use crate::tag_utils::{SwfMovie, SwfSlice};
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::Arc;
#[derive(Clone, Debug, Collect, Copy)] #[derive(Clone, Debug, Collect, Copy)]
#[collect(no_drop)] #[collect(no_drop)]
@ -47,6 +48,7 @@ impl<'gc> Button<'gc> {
} }
let static_data = ButtonStatic { let static_data = ButtonStatic {
swf: source_movie.movie.clone(),
id: button.id, id: button.id,
records: button.records.clone(), records: button.records.clone(),
actions, actions,
@ -120,6 +122,10 @@ impl<'gc> TDisplayObject<'gc> for Button<'gc> {
self.0.read().static_data.read().id self.0.read().static_data.read().id
} }
fn movie(&self) -> Option<Arc<SwfMovie>> {
Some(self.0.read().static_data.read().swf.clone())
}
fn post_instantiation( fn post_instantiation(
&mut self, &mut self,
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
@ -223,11 +229,11 @@ impl<'gc> ButtonData<'gc> {
self.children.clear(); self.children.clear();
for record in &self.static_data.read().records { for record in &self.static_data.read().records {
if record.states.contains(&swf_state) { if record.states.contains(&swf_state) {
if let Ok(mut child) = context.library.instantiate_by_id( if let Ok(mut child) = context
record.id, .library
context.gc_context, .library_for_movie_mut(self.movie())
&context.system_prototypes, .instantiate_by_id(record.id, context.gc_context, &context.system_prototypes)
) { {
child.set_parent(context.gc_context, Some(self_display_object)); child.set_parent(context.gc_context, Some(self_display_object));
child.set_matrix(context.gc_context, &record.matrix.clone().into()); child.set_matrix(context.gc_context, &record.matrix.clone().into());
child.set_color_transform( child.set_color_transform(
@ -253,7 +259,10 @@ impl<'gc> ButtonData<'gc> {
for record in &self.static_data.read().records { for record in &self.static_data.read().records {
if record.states.contains(&swf::ButtonState::HitTest) { if record.states.contains(&swf::ButtonState::HitTest) {
match context.library.instantiate_by_id( match context
.library
.library_for_movie_mut(self.static_data.read().swf.clone())
.instantiate_by_id(
record.id, record.id,
context.gc_context, context.gc_context,
&context.system_prototypes, &context.system_prototypes,
@ -335,7 +344,11 @@ impl<'gc> ButtonData<'gc> {
sound: Option<&swf::ButtonSound>, sound: Option<&swf::ButtonSound>,
) { ) {
if let Some((id, sound_info)) = sound { if let Some((id, sound_info)) = sound {
if let Some(sound_handle) = context.library.get_sound(*id) { if let Some(sound_handle) = context
.library
.library_for_movie_mut(self.movie())
.get_sound(*id)
{
context.audio.start_sound(sound_handle, sound_info); context.audio.start_sound(sound_handle, sound_info);
} }
} }
@ -368,6 +381,10 @@ impl<'gc> ButtonData<'gc> {
} }
handled handled
} }
fn movie(&self) -> Arc<SwfMovie> {
self.static_data.read().swf.clone()
}
} }
unsafe impl<'gc> gc_arena::Collect for ButtonData<'gc> { unsafe impl<'gc> gc_arena::Collect for ButtonData<'gc> {
@ -410,6 +427,7 @@ enum ButtonTracking {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct ButtonStatic { struct ButtonStatic {
swf: Arc<SwfMovie>,
id: CharacterId, id: CharacterId,
records: Vec<swf::ButtonRecord>, records: Vec<swf::ButtonRecord>,
actions: Vec<ButtonAction>, actions: Vec<ButtonAction>,

View File

@ -6,8 +6,10 @@ use crate::display_object::{DisplayObjectBase, TDisplayObject};
use crate::font::{Glyph, TextFormat}; use crate::font::{Glyph, TextFormat};
use crate::library::Library; use crate::library::Library;
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::SwfMovie;
use crate::transform::Transform; use crate::transform::Transform;
use gc_arena::{Collect, Gc, GcCell, MutationContext}; use gc_arena::{Collect, Gc, GcCell, MutationContext};
use std::sync::Arc;
/// A dynamic text field. /// A dynamic text field.
/// The text in this text field can be changed dynamically. /// The text in this text field can be changed dynamically.
@ -51,7 +53,11 @@ pub struct EditTextData<'gc> {
impl<'gc> EditText<'gc> { impl<'gc> EditText<'gc> {
/// Creates a new `EditText` from an SWF `DefineEditText` tag. /// Creates a new `EditText` from an SWF `DefineEditText` tag.
pub fn from_swf_tag(context: &mut UpdateContext<'_, 'gc, '_>, swf_tag: swf::EditText) -> Self { pub fn from_swf_tag(
context: &mut UpdateContext<'_, 'gc, '_>,
swf_movie: Arc<SwfMovie>,
swf_tag: swf::EditText,
) -> Self {
let is_multiline = swf_tag.is_multiline; let is_multiline = swf_tag.is_multiline;
let is_word_wrap = swf_tag.is_word_wrap; let is_word_wrap = swf_tag.is_word_wrap;
@ -84,7 +90,13 @@ impl<'gc> EditText<'gc> {
base: Default::default(), base: Default::default(),
text, text,
new_format: TextFormat::default(), new_format: TextFormat::default(),
static_data: gc_arena::Gc::allocate(context.gc_context, EditTextStatic(swf_tag)), static_data: gc_arena::Gc::allocate(
context.gc_context,
EditTextStatic {
swf: swf_movie,
text: swf_tag,
},
),
is_multiline, is_multiline,
is_word_wrap, is_word_wrap,
object: None, object: None,
@ -96,6 +108,7 @@ impl<'gc> EditText<'gc> {
/// Create a new, dynamic `EditText`. /// Create a new, dynamic `EditText`.
pub fn new( pub fn new(
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
swf_movie: Arc<SwfMovie>,
x: f64, x: f64,
y: f64, y: f64,
width: f64, width: f64,
@ -140,7 +153,7 @@ impl<'gc> EditText<'gc> {
is_device_font: false, is_device_font: false,
}; };
Self::from_swf_tag(context, swf_tag) Self::from_swf_tag(context, swf_movie, swf_tag)
} }
// TODO: This needs to strip away HTML // TODO: This needs to strip away HTML
@ -191,11 +204,15 @@ impl<'gc> EditText<'gc> {
/// `DisplayObject`. /// `DisplayObject`.
pub fn text_transform(self) -> Transform { pub fn text_transform(self) -> Transform {
let edit_text = self.0.read(); let edit_text = self.0.read();
let static_data = &edit_text.static_data.0; let static_data = &edit_text.static_data;
// TODO: Many of these properties should change be instance members instead // TODO: Many of these properties should change be instance members instead
// of static data, because they can be altered via ActionScript. // of static data, because they can be altered via ActionScript.
let color = static_data.color.as_ref().unwrap_or_else(|| &swf::Color { let color = static_data
.text
.color
.as_ref()
.unwrap_or_else(|| &swf::Color {
r: 0, r: 0,
g: 0, g: 0,
b: 0, b: 0,
@ -208,7 +225,7 @@ impl<'gc> EditText<'gc> {
transform.color_transform.b_mult = f32::from(color.b) / 255.0; transform.color_transform.b_mult = f32::from(color.b) / 255.0;
transform.color_transform.a_mult = f32::from(color.a) / 255.0; transform.color_transform.a_mult = f32::from(color.a) / 255.0;
if let Some(layout) = &static_data.layout { if let Some(layout) = &static_data.text.layout {
transform.matrix.tx += layout.left_margin.get() as f32; transform.matrix.tx += layout.left_margin.get() as f32;
transform.matrix.tx += layout.indent.get() as f32; transform.matrix.tx += layout.indent.get() as f32;
transform.matrix.ty -= layout.leading.get() as f32; transform.matrix.ty -= layout.leading.get() as f32;
@ -224,11 +241,11 @@ impl<'gc> EditText<'gc> {
/// and returns the adjusted transform. /// and returns the adjusted transform.
pub fn newline(self, height: f32, mut transform: Transform) -> Transform { pub fn newline(self, height: f32, mut transform: Transform) -> Transform {
let edit_text = self.0.read(); let edit_text = self.0.read();
let static_data = &edit_text.static_data.0; let static_data = &edit_text.static_data;
transform.matrix.tx = 0.0; transform.matrix.tx = 0.0;
transform.matrix.ty += height * Twips::TWIPS_PER_PIXEL as f32; transform.matrix.ty += height * Twips::TWIPS_PER_PIXEL as f32;
if let Some(layout) = &static_data.layout { if let Some(layout) = &static_data.text.layout {
transform.matrix.tx += layout.left_margin.get() as f32; transform.matrix.tx += layout.left_margin.get() as f32;
transform.matrix.tx += layout.indent.get() as f32; transform.matrix.tx += layout.indent.get() as f32;
transform.matrix.ty += layout.leading.get() as f32; transform.matrix.ty += layout.leading.get() as f32;
@ -239,11 +256,11 @@ impl<'gc> EditText<'gc> {
pub fn line_width(self) -> f32 { pub fn line_width(self) -> f32 {
let edit_text = self.0.read(); let edit_text = self.0.read();
let static_data = &edit_text.static_data.0; let static_data = &edit_text.static_data;
let mut base_width = self.width() as f32; let mut base_width = self.width() as f32;
if let Some(layout) = &static_data.layout { if let Some(layout) = &static_data.text.layout {
base_width -= layout.left_margin.to_pixels() as f32; base_width -= layout.left_margin.to_pixels() as f32;
base_width -= layout.indent.to_pixels() as f32; base_width -= layout.indent.to_pixels() as f32;
base_width -= layout.right_margin.to_pixels() as f32; base_width -= layout.right_margin.to_pixels() as f32;
@ -273,18 +290,26 @@ impl<'gc> EditText<'gc> {
/// calculating them is a relatively expensive operation. /// calculating them is a relatively expensive operation.
fn line_breaks(self, library: &Library<'gc>) -> Vec<usize> { fn line_breaks(self, library: &Library<'gc>) -> Vec<usize> {
let edit_text = self.0.read(); let edit_text = self.0.read();
let static_data = &edit_text.static_data.0; let static_data = &edit_text.static_data;
let font_id = static_data.font_id.unwrap_or(0); let font_id = static_data.text.font_id.unwrap_or(0);
if edit_text.is_multiline { if edit_text.is_multiline {
if let Some(font) = library if let Some(font) = library
.library_for_movie(self.movie().unwrap())
.unwrap()
.get_font(font_id) .get_font(font_id)
.filter(|font| font.has_glyphs()) .filter(|font| font.has_glyphs())
.or_else(|| library.device_font()) .or_else(|| {
library
.library_for_movie(self.movie().unwrap())
.unwrap()
.device_font()
})
{ {
let mut breakpoints = vec![]; let mut breakpoints = vec![];
let mut break_base = 0; let mut break_base = 0;
let height = static_data let height = static_data
.text
.height .height
.map(|v| v.to_pixels() as f32) .map(|v| v.to_pixels() as f32)
.unwrap_or_else(|| font.scale()); .unwrap_or_else(|| font.scale());
@ -343,16 +368,24 @@ impl<'gc> EditText<'gc> {
let breakpoints = self.line_breaks_cached(context.gc_context, context.library); let breakpoints = self.line_breaks_cached(context.gc_context, context.library);
let edit_text = self.0.read(); let edit_text = self.0.read();
let static_data = &edit_text.static_data.0; let static_data = &edit_text.static_data;
let font_id = static_data.font_id.unwrap_or(0); let font_id = static_data.text.font_id.unwrap_or(0);
let mut size: (f32, f32) = (0.0, 0.0); let mut size: (f32, f32) = (0.0, 0.0);
if let Some(font) = context if let Some(font) = context
.library .library
.library_for_movie(self.movie().unwrap())
.unwrap()
.get_font(font_id) .get_font(font_id)
.filter(|font| font.has_glyphs()) .filter(|font| font.has_glyphs())
.or_else(|| context.library.device_font()) .or_else(|| {
context
.library
.library_for_movie(self.movie().unwrap())
.unwrap()
.device_font()
})
{ {
let mut start = 0; let mut start = 0;
let mut chunks = vec![]; let mut chunks = vec![];
@ -364,6 +397,7 @@ impl<'gc> EditText<'gc> {
chunks.push(&edit_text.text[start..]); chunks.push(&edit_text.text[start..]);
let height = static_data let height = static_data
.text
.height .height
.map(|v| v.to_pixels() as f32) .map(|v| v.to_pixels() as f32)
.unwrap_or_else(|| font.scale()); .unwrap_or_else(|| font.scale());
@ -372,7 +406,7 @@ impl<'gc> EditText<'gc> {
let chunk_size = font.measure(chunk, height); let chunk_size = font.measure(chunk, height);
size.0 = size.0.max(chunk_size.0); size.0 = size.0.max(chunk_size.0);
if let Some(layout) = &static_data.layout { if let Some(layout) = &static_data.text.layout {
size.1 += layout.leading.to_pixels() as f32; size.1 += layout.leading.to_pixels() as f32;
} }
size.1 += chunk_size.1; size.1 += chunk_size.1;
@ -387,7 +421,11 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
impl_display_object!(base); impl_display_object!(base);
fn id(&self) -> CharacterId { fn id(&self) -> CharacterId {
self.0.read().static_data.0.id self.0.read().static_data.text.id
}
fn movie(&self) -> Option<Arc<SwfMovie>> {
Some(self.0.read().static_data.swf.clone())
} }
fn run_frame(&mut self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
@ -424,7 +462,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
} }
fn self_bounds(&self) -> BoundingBox { fn self_bounds(&self) -> BoundingBox {
self.0.read().static_data.0.bounds.clone().into() self.0.read().static_data.text.bounds.clone().into()
} }
fn render(&self, context: &mut RenderContext<'_, 'gc>) { fn render(&self, context: &mut RenderContext<'_, 'gc>) {
@ -433,20 +471,24 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
let mut text_transform = self.text_transform(); let mut text_transform = self.text_transform();
let edit_text = self.0.read(); let edit_text = self.0.read();
let static_data = &edit_text.static_data.0; let static_data = &edit_text.static_data;
let font_id = static_data.font_id.unwrap_or(0); let font_id = static_data.text.font_id.unwrap_or(0);
// If the font can't be found or has no glyph information, use the "device font" instead. // If the font can't be found or has no glyph information, use the "device font" instead.
// We're cheating a bit and not actually rendering text using the OS/web. // We're cheating a bit and not actually rendering text using the OS/web.
// Instead, we embed an SWF version of Noto Sans to use as the "device font", and render // Instead, we embed an SWF version of Noto Sans to use as the "device font", and render
// it the same as any other SWF outline text. // it the same as any other SWF outline text.
if let Some(font) = context let library = context
.library .library
.library_for_movie(edit_text.static_data.swf.clone())
.unwrap();
if let Some(font) = library
.get_font(font_id) .get_font(font_id)
.filter(|font| font.has_glyphs()) .filter(|font| font.has_glyphs())
.or_else(|| context.library.device_font()) .or_else(|| library.device_font())
{ {
let height = static_data let height = static_data
.text
.height .height
.map(|v| v.to_pixels() as f32) .map(|v| v.to_pixels() as f32)
.unwrap_or_else(|| font.scale()); .unwrap_or_else(|| font.scale());
@ -503,7 +545,10 @@ unsafe impl<'gc> gc_arena::Collect for EditTextData<'gc> {
/// Static data shared between all instances of a text object. /// Static data shared between all instances of a text object.
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct EditTextStatic(swf::EditText); struct EditTextStatic {
swf: Arc<SwfMovie>,
text: swf::EditText,
}
unsafe impl<'gc> gc_arena::Collect for EditTextStatic { unsafe impl<'gc> gc_arena::Collect for EditTextStatic {
#[inline] #[inline]

View File

@ -301,10 +301,6 @@ impl<'gc> MovieClip<'gc> {
actions.into_iter() actions.into_iter()
} }
pub fn movie(self) -> Arc<SwfMovie> {
self.0.read().movie()
}
} }
impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
@ -314,6 +310,10 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
self.0.read().id() self.0.read().id()
} }
fn movie(&self) -> Option<Arc<SwfMovie>> {
Some(self.0.read().movie())
}
fn run_frame(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) { fn run_frame(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) {
// Children must run first. // Children must run first.
for mut child in self.children() { for mut child in self.children() {
@ -595,9 +595,9 @@ impl<'gc> MovieClipData<'gc> {
place_object: &swf::PlaceObject, place_object: &swf::PlaceObject,
copy_previous_properties: bool, copy_previous_properties: bool,
) -> Option<DisplayObject<'gc>> { ) -> Option<DisplayObject<'gc>> {
if let Ok(mut child) = if let Ok(mut child) = context
context
.library .library
.library_for_movie_mut(self.movie())
.instantiate_by_id(id, context.gc_context, &context.system_prototypes) .instantiate_by_id(id, context.gc_context, &context.system_prototypes)
{ {
// Remove previous child from children list, // Remove previous child from children list,
@ -1095,6 +1095,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(define_bits_lossless.id, Character::Bitmap(bitmap)); .register_character(define_bits_lossless.id, Character::Bitmap(bitmap));
Ok(()) Ok(())
} }
@ -1125,6 +1126,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
let graphic = Graphic::from_swf_tag(context, &swf_shape); let graphic = Graphic::from_swf_tag(context, &swf_shape);
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(swf_shape.id, Character::Graphic(graphic)); .register_character(swf_shape.id, Character::Graphic(graphic));
Ok(()) Ok(())
} }
@ -1232,10 +1234,14 @@ impl<'gc, 'a> MovieClipData<'gc> {
.get_mut() .get_mut()
.take(data_len as u64) .take(data_len as u64)
.read_to_end(&mut jpeg_data)?; .read_to_end(&mut jpeg_data)?;
let bitmap_info = let bitmap_info = context.renderer.register_bitmap_jpeg(
id,
&jpeg_data,
context context
.renderer .library
.register_bitmap_jpeg(id, &jpeg_data, context.library.jpeg_tables()); .library_for_movie_mut(self.movie())
.jpeg_tables(),
);
let bitmap = crate::display_object::Bitmap::new( let bitmap = crate::display_object::Bitmap::new(
context, context,
id, id,
@ -1245,6 +1251,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(id, Character::Bitmap(bitmap)); .register_character(id, Character::Bitmap(bitmap));
Ok(()) Ok(())
} }
@ -1274,6 +1281,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(id, Character::Bitmap(bitmap)); .register_character(id, Character::Bitmap(bitmap));
Ok(()) Ok(())
} }
@ -1311,6 +1319,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(id, Character::Bitmap(bitmap)); .register_character(id, Character::Bitmap(bitmap));
Ok(()) Ok(())
} }
@ -1349,6 +1358,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(id, Character::Bitmap(bitmap)); .register_character(id, Character::Bitmap(bitmap));
Ok(()) Ok(())
} }
@ -1368,6 +1378,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(swf_button.id, Character::Button(button)); .register_character(swf_button.id, Character::Button(button));
Ok(()) Ok(())
} }
@ -1387,6 +1398,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
); );
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(swf_button.id, Character::Button(button)); .register_character(swf_button.id, Character::Button(button));
Ok(()) Ok(())
} }
@ -1399,7 +1411,11 @@ impl<'gc, 'a> MovieClipData<'gc> {
tag_len: usize, tag_len: usize,
) -> DecodeResult { ) -> DecodeResult {
let button_colors = reader.read_define_button_cxform(tag_len)?; let button_colors = reader.read_define_button_cxform(tag_len)?;
if let Some(button) = context.library.get_character_by_id(button_colors.id) { if let Some(button) = context
.library
.library_for_movie_mut(self.movie())
.get_character_by_id(button_colors.id)
{
if let Character::Button(button) = button { if let Character::Button(button) = button {
button.set_colors(context.gc_context, &button_colors.color_transforms[..]); button.set_colors(context.gc_context, &button_colors.color_transforms[..]);
} else { } else {
@ -1424,7 +1440,11 @@ impl<'gc, 'a> MovieClipData<'gc> {
reader: &mut SwfStream<&'a [u8]>, reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult { ) -> DecodeResult {
let button_sounds = reader.read_define_button_sound()?; let button_sounds = reader.read_define_button_sound()?;
if let Some(button) = context.library.get_character_by_id(button_sounds.id) { if let Some(button) = context
.library
.library_for_movie_mut(self.movie())
.get_character_by_id(button_sounds.id)
{
if let Character::Button(button) = button { if let Character::Button(button) = button {
button.set_sounds(context.gc_context, button_sounds); button.set_sounds(context.gc_context, button_sounds);
} else { } else {
@ -1450,9 +1470,10 @@ impl<'gc, 'a> MovieClipData<'gc> {
reader: &mut SwfStream<&'a [u8]>, reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult { ) -> DecodeResult {
let swf_edit_text = reader.read_define_edit_text()?; let swf_edit_text = reader.read_define_edit_text()?;
let edit_text = EditText::from_swf_tag(context, swf_edit_text); let edit_text = EditText::from_swf_tag(context, self.movie(), swf_edit_text);
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(edit_text.id(), Character::EditText(edit_text)); .register_character(edit_text.id(), Character::EditText(edit_text));
Ok(()) Ok(())
} }
@ -1491,6 +1512,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap();
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(font.id, Character::Font(font_object)); .register_character(font.id, Character::Font(font_object));
Ok(()) Ok(())
} }
@ -1505,6 +1527,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap();
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(font.id, Character::Font(font_object)); .register_character(font.id, Character::Font(font_object));
Ok(()) Ok(())
} }
@ -1519,6 +1542,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap();
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(font.id, Character::Font(font_object)); .register_character(font.id, Character::Font(font_object));
Ok(()) Ok(())
@ -1541,6 +1565,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
let handle = context.audio.register_sound(&sound).unwrap(); let handle = context.audio.register_sound(&sound).unwrap();
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(sound.id, Character::Sound(handle)); .register_character(sound.id, Character::Sound(handle));
Ok(()) Ok(())
} }
@ -1573,6 +1598,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(id, Character::MovieClip(movie_clip)); .register_character(id, Character::MovieClip(movie_clip));
Ok(()) Ok(())
@ -1586,9 +1612,10 @@ impl<'gc, 'a> MovieClipData<'gc> {
version: u8, version: u8,
) -> DecodeResult { ) -> DecodeResult {
let text = reader.read_define_text(version)?; let text = reader.read_define_text(version)?;
let text_object = Text::from_swf_tag(context, &text); let text_object = Text::from_swf_tag(context, self.movie(), &text);
context context
.library .library
.library_for_movie_mut(self.movie())
.register_character(text.id, Character::Text(text_object)); .register_character(text.id, Character::Text(text_object));
Ok(()) Ok(())
} }
@ -1601,7 +1628,10 @@ impl<'gc, 'a> MovieClipData<'gc> {
) -> DecodeResult { ) -> DecodeResult {
let exports = reader.read_export_assets()?; let exports = reader.read_export_assets()?;
for export in exports { for export in exports {
context.library.register_export(export.id, &export.name); context
.library
.library_for_movie_mut(self.movie())
.register_export(export.id, &export.name);
} }
Ok(()) Ok(())
} }
@ -1642,7 +1672,10 @@ impl<'gc, 'a> MovieClipData<'gc> {
.get_mut() .get_mut()
.take(tag_len as u64) .take(tag_len as u64)
.read_to_end(&mut jpeg_data)?; .read_to_end(&mut jpeg_data)?;
context.library.set_jpeg_tables(jpeg_data); context
.library
.library_for_movie_mut(self.movie())
.set_jpeg_tables(jpeg_data);
Ok(()) Ok(())
} }
@ -1852,7 +1885,11 @@ impl<'gc, 'a> MovieClipData<'gc> {
reader: &mut SwfStream<&'a [u8]>, reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult { ) -> DecodeResult {
let start_sound = reader.read_start_sound_1()?; let start_sound = reader.read_start_sound_1()?;
if let Some(handle) = context.library.get_sound(start_sound.id) { if let Some(handle) = context
.library
.library_for_movie_mut(self.movie())
.get_sound(start_sound.id)
{
use swf::SoundEvent; use swf::SoundEvent;
// The sound event type is controlled by the "Sync" setting in the Flash IDE. // The sound event type is controlled by the "Sync" setting in the Flash IDE.
match start_sound.sound_info.event { match start_sound.sound_info.event {

View File

@ -1,8 +1,10 @@
use crate::context::{RenderContext, UpdateContext}; use crate::context::{RenderContext, UpdateContext};
use crate::display_object::{DisplayObjectBase, TDisplayObject}; use crate::display_object::{DisplayObjectBase, TDisplayObject};
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::SwfMovie;
use crate::transform::Transform; use crate::transform::Transform;
use gc_arena::{Collect, GcCell}; use gc_arena::{Collect, GcCell};
use std::sync::Arc;
#[derive(Clone, Debug, Collect, Copy)] #[derive(Clone, Debug, Collect, Copy)]
#[collect(no_drop)] #[collect(no_drop)]
@ -15,7 +17,11 @@ pub struct TextData<'gc> {
} }
impl<'gc> Text<'gc> { impl<'gc> Text<'gc> {
pub fn from_swf_tag(context: &mut UpdateContext<'_, 'gc, '_>, tag: &swf::Text) -> Self { pub fn from_swf_tag(
context: &mut UpdateContext<'_, 'gc, '_>,
swf: Arc<SwfMovie>,
tag: &swf::Text,
) -> Self {
Text(GcCell::allocate( Text(GcCell::allocate(
context.gc_context, context.gc_context,
TextData { TextData {
@ -23,6 +29,7 @@ impl<'gc> Text<'gc> {
static_data: gc_arena::Gc::allocate( static_data: gc_arena::Gc::allocate(
context.gc_context, context.gc_context,
TextStatic { TextStatic {
swf,
id: tag.id, id: tag.id,
text_transform: tag.matrix.clone().into(), text_transform: tag.matrix.clone().into(),
text_blocks: tag.records.clone(), text_blocks: tag.records.clone(),
@ -40,6 +47,10 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
self.0.read().static_data.id self.0.read().static_data.id
} }
fn movie(&self) -> Option<Arc<SwfMovie>> {
Some(self.0.read().static_data.swf.clone())
}
fn run_frame(&mut self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
// Noop // Noop
} }
@ -71,7 +82,12 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
color = block.color.as_ref().unwrap_or(&color).clone(); color = block.color.as_ref().unwrap_or(&color).clone();
font_id = block.font_id.unwrap_or(font_id); font_id = block.font_id.unwrap_or(font_id);
height = block.height.map(|h| h.get() as f32).unwrap_or(height); height = block.height.map(|h| h.get() as f32).unwrap_or(height);
if let Some(font) = context.library.get_font(font_id) { if let Some(font) = context
.library
.library_for_movie(self.movie().unwrap())
.unwrap()
.get_font(font_id)
{
let scale = height / font.scale(); let scale = height / font.scale();
transform.matrix.a = scale; transform.matrix.a = scale;
transform.matrix.d = scale; transform.matrix.d = scale;
@ -108,6 +124,7 @@ unsafe impl<'gc> gc_arena::Collect for TextData<'gc> {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct TextStatic { struct TextStatic {
swf: Arc<SwfMovie>,
id: CharacterId, id: CharacterId,
text_transform: Matrix, text_transform: Matrix,
text_blocks: Vec<swf::TextRecord>, text_blocks: Vec<swf::TextRecord>,

View File

@ -5,20 +5,24 @@ use crate::character::Character;
use crate::display_object::TDisplayObject; use crate::display_object::TDisplayObject;
use crate::font::Font; use crate::font::Font;
use crate::prelude::*; use crate::prelude::*;
use crate::tag_utils::SwfMovie;
use gc_arena::MutationContext; use gc_arena::MutationContext;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Weak};
use swf::CharacterId; use swf::CharacterId;
use weak_table::PtrWeakKeyHashMap;
pub struct Library<'gc> { /// Symbol library for a single given SWF.
pub struct MovieLibrary<'gc> {
characters: HashMap<CharacterId, Character<'gc>>, characters: HashMap<CharacterId, Character<'gc>>,
export_characters: HashMap<String, Character<'gc>>, export_characters: HashMap<String, Character<'gc>>,
jpeg_tables: Option<Vec<u8>>, jpeg_tables: Option<Vec<u8>>,
device_font: Option<Font<'gc>>, device_font: Option<Font<'gc>>,
} }
impl<'gc> Library<'gc> { impl<'gc> MovieLibrary<'gc> {
pub fn new() -> Self { pub fn new() -> Self {
Library { MovieLibrary {
characters: HashMap::new(), characters: HashMap::new(),
export_characters: HashMap::new(), export_characters: HashMap::new(),
jpeg_tables: None, jpeg_tables: None,
@ -181,7 +185,7 @@ impl<'gc> Library<'gc> {
} }
} }
unsafe impl<'gc> gc_arena::Collect for Library<'gc> { unsafe impl<'gc> gc_arena::Collect for MovieLibrary<'gc> {
#[inline] #[inline]
fn trace(&self, cc: gc_arena::CollectionContext) { fn trace(&self, cc: gc_arena::CollectionContext) {
for character in self.characters.values() { for character in self.characters.values() {
@ -191,8 +195,46 @@ unsafe impl<'gc> gc_arena::Collect for Library<'gc> {
} }
} }
impl Default for Library<'_> { impl Default for MovieLibrary<'_> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
/// Symbol library for multiple movies.
pub struct Library<'gc> {
/// All the movie libraries.
movie_libraries: PtrWeakKeyHashMap<Weak<SwfMovie>, MovieLibrary<'gc>>,
}
unsafe impl<'gc> gc_arena::Collect for Library<'gc> {
#[inline]
fn trace(&self, cc: gc_arena::CollectionContext) {
for (_, val) in self.movie_libraries.iter() {
val.trace(cc);
}
}
}
impl<'gc> Library<'gc> {
pub fn library_for_movie(&self, movie: Arc<SwfMovie>) -> Option<&MovieLibrary<'gc>> {
self.movie_libraries.get(&movie)
}
pub fn library_for_movie_mut(&mut self, movie: Arc<SwfMovie>) -> &mut MovieLibrary<'gc> {
if !self.movie_libraries.contains_key(&movie) {
self.movie_libraries
.insert(movie.clone(), MovieLibrary::default());
};
self.movie_libraries.get_mut(&movie).unwrap()
}
}
impl<'gc> Default for Library<'gc> {
fn default() -> Self {
Self {
movie_libraries: PtrWeakKeyHashMap::new(),
}
}
}

View File

@ -174,8 +174,11 @@ impl Player {
} }
}; };
let mut library = Library::new(); let mut library = Library::default();
library.set_device_font(device_font); library
.library_for_movie_mut(movie.clone())
.set_device_font(device_font);
GcRoot(GcCell::allocate( GcRoot(GcCell::allocate(
gc_context, gc_context,
GcRootData { GcRootData {
@ -521,6 +524,7 @@ impl Player {
let morph_shape = MorphShape::new(context.gc_context, static_data); let morph_shape = MorphShape::new(context.gc_context, static_data);
context context
.library .library
.library_for_movie_mut(root.as_movie_clip().unwrap().movie().unwrap())
.register_character(id, crate::character::Character::MorphShape(morph_shape)); .register_character(id, crate::character::Character::MorphShape(morph_shape));
} }
}); });