From a570d9643326999e83dbc65832ddf3e85edb1bf0 Mon Sep 17 00:00:00 2001 From: Callum Thomson Date: Thu, 18 Mar 2021 19:03:04 +0000 Subject: [PATCH] avm2: Implement flash.geom.Point (#3681) --- core/src/avm2/globals.rs | 17 + core/src/avm2/globals/flash.rs | 1 + core/src/avm2/globals/flash/geom.rs | 3 + core/src/avm2/globals/flash/geom/point.rs | 399 ++++++++++++++++++++++ core/tests/regression_tests.rs | 1 + core/tests/swfs/avm2/point/Test.as | 210 ++++++++++++ core/tests/swfs/avm2/point/output.txt | 133 ++++++++ core/tests/swfs/avm2/point/test.fla | Bin 0 -> 4506 bytes core/tests/swfs/avm2/point/test.swf | Bin 0 -> 2072 bytes 9 files changed, 764 insertions(+) create mode 100644 core/src/avm2/globals/flash/geom.rs create mode 100644 core/src/avm2/globals/flash/geom/point.rs create mode 100644 core/tests/swfs/avm2/point/Test.as create mode 100644 core/tests/swfs/avm2/point/output.txt create mode 100644 core/tests/swfs/avm2/point/test.fla create mode 100644 core/tests/swfs/avm2/point/test.swf diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 9f704e9cf..6b3307d61 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -103,6 +103,7 @@ pub struct SystemPrototypes<'gc> { pub xml_list: Object<'gc>, pub display_object: Object<'gc>, pub shape: Object<'gc>, + pub point: Object<'gc>, } impl<'gc> SystemPrototypes<'gc> { @@ -141,6 +142,7 @@ impl<'gc> SystemPrototypes<'gc> { xml_list: empty, display_object: empty, shape: empty, + point: empty, } } } @@ -662,6 +664,21 @@ pub fn load_player_globals<'gc>( script, )?; + // package `flash.geom` + activation + .context + .avm2 + .system_prototypes + .as_mut() + .unwrap() + .point = class( + activation, + flash::geom::point::create_class(mc), + implicit_deriver, + domain, + script, + )?; + // package `flash.media` activation .context diff --git a/core/src/avm2/globals/flash.rs b/core/src/avm2/globals/flash.rs index 2ace30441..467ff3ac2 100644 --- a/core/src/avm2/globals/flash.rs +++ b/core/src/avm2/globals/flash.rs @@ -2,6 +2,7 @@ pub mod display; pub mod events; +pub mod geom; pub mod media; pub mod system; pub mod utils; diff --git a/core/src/avm2/globals/flash/geom.rs b/core/src/avm2/globals/flash/geom.rs new file mode 100644 index 000000000..425abb1db --- /dev/null +++ b/core/src/avm2/globals/flash/geom.rs @@ -0,0 +1,3 @@ +//! `flash.geom` namespace + +pub mod point; diff --git a/core/src/avm2/globals/flash/geom/point.rs b/core/src/avm2/globals/flash/geom/point.rs new file mode 100644 index 000000000..9537304c9 --- /dev/null +++ b/core/src/avm2/globals/flash/geom/point.rs @@ -0,0 +1,399 @@ +//! `flash.geom.Point` builtin/prototype + +use crate::avm1::AvmString; +use crate::avm2::class::{Class, ClassAttributes}; +use crate::avm2::method::Method; +use crate::avm2::traits::Trait; +use crate::avm2::{Activation, Error, Namespace, Object, QName, TObject, Value}; +use gc_arena::{GcCell, MutationContext}; + +fn create_point<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + coords: (f64, f64), +) -> Result, Error> { + let proto = activation.context.avm2.prototypes().point; + let args = [Value::Number(coords.0), Value::Number(coords.1)]; + let new_point = proto.construct(activation, &args)?; + instance_init(activation, Some(new_point), &args)?; + + Ok(new_point.into()) +} + +/// Implements `flash.geom.Point`'s instance constructor. +pub fn instance_init<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + let _ = set_to(activation, this, args)?; + Ok(Value::Undefined) +} + +fn coords<'gc>( + this: &mut Object<'gc>, + activation: &mut Activation<'_, 'gc, '_>, +) -> Result<(f64, f64), Error> { + let x = this + .get_property(*this, &QName::new(Namespace::public(), "x"), activation)? + .coerce_to_number(activation)?; + let y = this + .get_property(*this, &QName::new(Namespace::public(), "y"), activation)? + .coerce_to_number(activation)?; + Ok((x, y)) +} + +fn set_coords<'gc>( + this: &mut Object<'gc>, + activation: &mut Activation<'_, 'gc, '_>, + value: (f64, f64), +) -> Result<(), Error> { + this.set_property( + *this, + &QName::new(Namespace::public(), "x"), + value.0.into(), + activation, + )?; + this.set_property( + *this, + &QName::new(Namespace::public(), "y"), + value.1.into(), + activation, + )?; + Ok(()) +} + +/// Implements `flash.geom.Point`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + Ok(Value::Undefined) +} + +/// Implements the `length` property +pub fn length<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + let (x, y) = coords(&mut this, activation)?; + + return Ok((x * x + y * y).sqrt().into()); + } + + Ok(Value::Undefined) +} + +/// Implements `add` +pub fn add<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + if let Some(other) = args.get(0) { + let mut other_obj = other.coerce_to_object(activation)?; + let (our_x, our_y) = coords(&mut this, activation)?; + let (their_x, their_y) = coords(&mut other_obj, activation)?; + + return create_point(activation, (our_x + their_x, our_y + their_y)); + } + } + + Ok(Value::Undefined) +} + +/// Implements `clone` +pub fn clone<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + let (our_x, our_y) = coords(&mut this, activation)?; + + return create_point(activation, (our_x, our_y)); + } + + Ok(Value::Undefined) +} + +/// Implements `copyFrom` +pub fn copy_from<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + if let Some(other) = args.get(0) { + let mut other_obj = other.coerce_to_object(activation)?; + let (their_x, their_y) = coords(&mut other_obj, activation)?; + + set_coords(&mut this, activation, (their_x, their_y))?; + } + } + + Ok(Value::Undefined) +} + +/// Implements `distance` +pub fn distance<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(first) = args.get(0) { + let mut first_object = first.coerce_to_object(activation)?; + if let Some(second) = args.get(1) { + let mut second_obj = second.coerce_to_object(activation)?; + let (our_x, our_y) = coords(&mut first_object, activation)?; + let (their_x, their_y) = coords(&mut second_obj, activation)?; + + return Ok(((our_x - their_x).powf(2.0) + (our_y - their_y).powf(2.0)) + .sqrt() + .into()); + } + } + + Ok(Value::Undefined) +} + +/// Implements `equals` +#[allow(clippy::float_cmp)] +pub fn equals<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + if let Some(other) = args.get(0) { + let mut other_obj = other.coerce_to_object(activation)?; + + let (our_x, our_y) = coords(&mut this, activation)?; + let (their_x, their_y) = coords(&mut other_obj, activation)?; + + return Ok((our_x == their_x && our_y == their_y).into()); + } + } + + Ok(Value::Undefined) +} + +/// Implements `interpolate` +pub fn interpolate<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if args.len() < 3 { + return create_point(activation, (f64::NAN, f64::NAN)); + } + + let (a_x, a_y) = coords( + &mut args.get(0).unwrap().coerce_to_object(activation)?, + activation, + )?; + let (b_x, b_y) = coords( + &mut args.get(1).unwrap().coerce_to_object(activation)?, + activation, + )?; + let f = args.get(2).unwrap().coerce_to_number(activation)?; + + let result = (b_x - (b_x - a_x) * f, b_y - (b_y - a_y) * f); + create_point(activation, result) +} + +/// Implements `normalize` +pub fn normalize<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + let thickness = args + .get(0) + .unwrap_or(&0.into()) + .coerce_to_number(activation)?; + + let length = length(activation, Some(this), args)?.coerce_to_number(activation)?; + + if length > 0.0 { + let inv_d = thickness / length; + + let (old_x, old_y) = coords(&mut this, activation)?; + set_coords(&mut this, activation, (old_x * inv_d, old_y * inv_d))?; + } + } + + Ok(Value::Undefined) +} + +/// Implements `offset` +pub fn offset<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + let (x, y) = coords(&mut this, activation)?; + + let dx = args + .get(0) + .unwrap_or(&0.into()) + .coerce_to_number(activation)?; + let dy = args + .get(1) + .unwrap_or(&0.into()) + .coerce_to_number(activation)?; + + set_coords(&mut this, activation, (x + dx, y + dy))?; + } + + Ok(Value::Undefined) +} + +/// Implements `polar` +pub fn polar<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + let length = args + .get(0) + .unwrap_or(&Value::Undefined) + .coerce_to_number(activation)?; + let angle = args + .get(1) + .unwrap_or(&Value::Undefined) + .coerce_to_number(activation)?; + + create_point(activation, (length * angle.cos(), length * angle.sin())) +} + +/// Implements `setTo` +pub fn set_to<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + let x = args + .get(0) + .unwrap_or(&0.into()) + .coerce_to_number(activation)?; + let y = args + .get(1) + .unwrap_or(&0.into()) + .coerce_to_number(activation)?; + + set_coords(&mut this, activation, (x, y))?; + } + + Ok(Value::Undefined) +} + +/// Implements `subtract` +pub fn subtract<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + if let Some(other) = args.get(0) { + let mut other_obj = other.coerce_to_object(activation)?; + let (our_x, our_y) = coords(&mut this, activation)?; + let (their_x, their_y) = coords(&mut other_obj, activation)?; + + return create_point(activation, (our_x - their_x, our_y - their_y)); + } + } + + Ok(Value::Undefined) +} + +/// Implements `toString` +pub fn to_string<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mut this) = this { + let (x, y) = coords(&mut this, activation)?; + return Ok( + AvmString::new(activation.context.gc_context, format!("(x={}, y={})", x, y)).into(), + ); + } + + Ok(Value::Undefined) +} + +/// Construct `Point`'s class. +pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { + let class = Class::new( + QName::new(Namespace::package("flash.geom"), "Point"), + Some(QName::new(Namespace::public(), "Object").into()), + Method::from_builtin(instance_init), + Method::from_builtin(class_init), + mc, + ); + + let mut write = class.write(mc); + write.set_attributes(ClassAttributes::SEALED); + + write.define_instance_trait(Trait::from_getter( + QName::new(Namespace::public(), "length"), + Method::from_builtin(length), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "add"), + Method::from_builtin(add), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "clone"), + Method::from_builtin(clone), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "copyFrom"), + Method::from_builtin(copy_from), + )); + write.define_class_trait(Trait::from_method( + QName::new(Namespace::public(), "distance"), + Method::from_builtin(distance), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "equals"), + Method::from_builtin(equals), + )); + write.define_class_trait(Trait::from_method( + QName::new(Namespace::public(), "interpolate"), + Method::from_builtin(interpolate), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "normalize"), + Method::from_builtin(normalize), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "offset"), + Method::from_builtin(offset), + )); + write.define_class_trait(Trait::from_method( + QName::new(Namespace::public(), "polar"), + Method::from_builtin(polar), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "setTo"), + Method::from_builtin(set_to), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "subtract"), + Method::from_builtin(subtract), + )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public(), "toString"), + Method::from_builtin(to_string), + )); + + class +} diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index c3edf1482..803d7458a 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -532,6 +532,7 @@ swf_tests! { (as3_regexp_constr, "avm2/regexp_constr", 1), (as3_regexp_test, "avm2/regexp_test", 1), (as3_regexp_exec, "avm2/regexp_exec", 1), + (as3_point, "avm2/point", 1), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/core/tests/swfs/avm2/point/Test.as b/core/tests/swfs/avm2/point/Test.as new file mode 100644 index 000000000..a44e20273 --- /dev/null +++ b/core/tests/swfs/avm2/point/Test.as @@ -0,0 +1,210 @@ +package { + public class Test { + } +} + +import flash.geom.Point; + +trace("/// Constructors"); +trace("// new Point()"); +trace(new Point()); +trace(""); + +trace("// new Point(1)"); +trace(new Point(1)); +trace(""); + +trace("// new Point(1, 2)"); +trace(new Point(1, 2)); +trace(""); + +trace("// new Point({}, 2)"); +var temp = {}; +trace(new Point(temp, 2)); +trace(""); +trace(""); + +trace("/// Add"); + +var point2 = new Point(); +trace("// point.add(new Point(1, 2))"); +trace(point2.add(new Point(1, 2))); +trace(""); + +trace("// point"); +trace(point2); +trace(""); +trace(""); + +trace("/// Subtract"); + +var point3 = new Point(); +trace("// point.subtract(new Point(1, 2))"); +trace(point3.subtract(new Point(1, 2))); +trace(""); + +trace("// point"); +trace(point3); +trace(""); +trace(""); + +trace("/// Distance"); + +trace("// Point.distance(new Point(), new Point())"); +trace(Point.distance(new Point(), new Point())); +trace(""); + +trace("// Point.distance(new Point(-100, 200), new Point(100, 200))"); +trace(Point.distance(new Point(-100, 200), new Point(100, 200))); +trace(""); + +trace("/// Equals"); + +var point4 = new Point(); +trace("// point.equals(new Point(1, 2))"); +trace(point4.equals(new Point(1, 2))); +trace(""); + + +trace("// point.equals(point)"); +trace(point4.equals(point4)); +trace(""); + +trace("// point"); +trace(point4); +trace(""); +trace(""); + + +trace("/// Clone"); + +var point5 = new Point(1, 2); +var clone = point5.clone(); +trace("// point"); +trace(point5); +trace(""); + +trace("// clone"); +trace(clone); +trace(""); + +trace("// point === clone"); +trace(point5 === clone); +trace(""); + +trace("// point.equals(clone)"); +trace(point5.equals(clone)); +trace(""); +trace(""); + +trace("/// Interpolate"); +trace("// Point.interpolate(new Point(-100, -200), new Point(100, 200), -1)"); +trace(Point.interpolate(new Point(-100, -200), new Point(100, 200), -1)); +trace(""); + +trace("// Point.interpolate(new Point(-100, -200), new Point(100, 200), 0)"); +trace(Point.interpolate(new Point(-100, -200), new Point(100, 200), 0)); +trace(""); + +trace("// Point.interpolate(new Point(-100, -200), new Point(100, 200), 0.5)"); +trace(Point.interpolate(new Point(-100, -200), new Point(100, 200), 0.5)); +trace(""); + +trace("// Point.interpolate(new Point(-100, -200), new Point(100, 200), 1)"); +trace(Point.interpolate(new Point(-100, -200), new Point(100, 200), 1)); +trace(""); + +trace("// Point.interpolate(new Point(-100, -200), new Point(100, 200), 2)"); +trace(Point.interpolate(new Point(-100, -200), new Point(100, 200), 2)); +trace(""); + +trace("/// length"); +trace("new Point().length"); +trace(new Point().length); +trace(""); + +trace("new Point(100, 0).length"); +trace(new Point(100, 0).length); +trace(""); + +trace("new Point(0, -200).length"); +trace(new Point(0, -200).length); +trace(""); +trace(""); + +trace("/// Normalize"); +trace("// new Point() normalize(10)"); +var point6 = new Point(); +point6.normalize(10); +trace(point6); +trace(""); + +trace("// new Point() normalize(-5)"); +var point7 = new Point(); +point7.normalize(-5); +trace(point7); +trace(""); + +trace("// new Point(100, 200) normalize(10)"); +var point8 = new Point(100, 200); +point8.normalize(10); +trace(point8); +trace(""); + +trace("// new Point(100, 200) normalize(-5)"); +var point9 = new Point(100, 200); +point9.normalize(-5); +trace(point9); +trace(""); + +trace("// new Point(-200, 100) normalize(10)"); +var point10 = new Point(-200, 100); +point10.normalize(10); +trace(point10); +trace(""); + +trace("// new Point(-200, 100) normalize(-5)"); +var point11 = new Point(-200, 100); +point11.normalize(-5); +trace(point11); +trace(""); + +trace("// new Point(undefined, 100) normalize(1)"); +var point14 = new Point(undefined, 100); +point14.normalize(1); +trace(point14); +trace(""); +trace(""); + +trace("// new Point(100, null) normalize(1)"); +var point15 = new Point(100, null); +point15.normalize(1); +trace(point15); +trace(""); +trace(""); + +trace("/// Offset"); +var point16 = new Point(); +trace("// point = new Point()"); +trace(point16); +trace(""); + +point16.offset(100, 200); +trace("// point.offset(100, 200)"); +trace(point16); +trace(""); + +point16.offset(-1000, -2000); +trace("// point.offset(-1000, -2000)"); +trace(point16); +trace(""); + + +trace("/// polar"); +trace("// Point.polar(5, Math.atan(3/4))"); +trace(Point.polar(5, Math.atan(3/4))); +trace(""); + +trace("// Point.polar(0, Math.atan(3/4))"); +trace(Point.polar(0, Math.atan(3/4))); +trace(""); diff --git a/core/tests/swfs/avm2/point/output.txt b/core/tests/swfs/avm2/point/output.txt new file mode 100644 index 000000000..54c8c8881 --- /dev/null +++ b/core/tests/swfs/avm2/point/output.txt @@ -0,0 +1,133 @@ +/// Constructors +// new Point() +(x=0, y=0) + +// new Point(1) +(x=1, y=0) + +// new Point(1, 2) +(x=1, y=2) + +// new Point({}, 2) +(x=NaN, y=2) + + +/// Add +// point.add(new Point(1, 2)) +(x=1, y=2) + +// point +(x=0, y=0) + + +/// Subtract +// point.subtract(new Point(1, 2)) +(x=-1, y=-2) + +// point +(x=0, y=0) + + +/// Distance +// Point.distance(new Point(), new Point()) +0 + +// Point.distance(new Point(-100, 200), new Point(100, 200)) +200 + +/// Equals +// point.equals(new Point(1, 2)) +false + +// point.equals(point) +true + +// point +(x=0, y=0) + + +/// Clone +// point +(x=1, y=2) + +// clone +(x=1, y=2) + +// point === clone +false + +// point.equals(clone) +true + + +/// Interpolate +// Point.interpolate(new Point(-100, -200), new Point(100, 200), -1) +(x=300, y=600) + +// Point.interpolate(new Point(-100, -200), new Point(100, 200), 0) +(x=100, y=200) + +// Point.interpolate(new Point(-100, -200), new Point(100, 200), 0.5) +(x=0, y=0) + +// Point.interpolate(new Point(-100, -200), new Point(100, 200), 1) +(x=-100, y=-200) + +// Point.interpolate(new Point(-100, -200), new Point(100, 200), 2) +(x=-300, y=-600) + +/// length +new Point().length +0 + +new Point(100, 0).length +100 + +new Point(0, -200).length +200 + + +/// Normalize +// new Point() normalize(10) +(x=0, y=0) + +// new Point() normalize(-5) +(x=0, y=0) + +// new Point(100, 200) normalize(10) +(x=4.47213595499958, y=8.94427190999916) + +// new Point(100, 200) normalize(-5) +(x=-2.23606797749979, y=-4.47213595499958) + +// new Point(-200, 100) normalize(10) +(x=-8.94427190999916, y=4.47213595499958) + +// new Point(-200, 100) normalize(-5) +(x=4.47213595499958, y=-2.23606797749979) + +// new Point(undefined, 100) normalize(1) +(x=NaN, y=100) + + +// new Point(100, null) normalize(1) +(x=1, y=0) + + +/// Offset +// point = new Point() +(x=0, y=0) + +// point.offset(100, 200) +(x=100, y=200) + +// point.offset(-1000, -2000) +(x=-900, y=-1800) + +/// polar +// Point.polar(5, Math.atan(3/4)) +(x=4, y=3) + +// Point.polar(0, Math.atan(3/4)) +(x=0, y=0) + diff --git a/core/tests/swfs/avm2/point/test.fla b/core/tests/swfs/avm2/point/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..7a2d53915bcd62a8b1822591c97d570ce92e4df0 GIT binary patch literal 4506 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 literal 0 HcmV?d00001 diff --git a/core/tests/swfs/avm2/point/test.swf b/core/tests/swfs/avm2/point/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..d3347d3cae29402e70cadac09a24d8333e57b0e2 GIT binary patch literal 2072 zcmV+z2y{r{=WwZ(>{?pIYGuih)=tv`WeN=Ea1BQX3R5V`+INS9&eu(eAfga@{%C*5MeeN5CmaH+}heY(6Qbt zXXM#gxiD9IYVzvp?3wH7TPveC%nQZiPZ!g|>)j#H5@9iKR?K8Z5MJ-9M==1u4j~1t zC^)~rpDYT}C9`g&vFW#cz7Dh2&(&`h>p5d%btLDgPn>8>(P!0+yxEaXr;{_)O5Lih=dEh39){zJc{BMa ziU*V+TZQXpb=BvDA}#rh5^FskN{%RHVV~cGnKEdrcW=9rbkqDBE7s`DqE#WfY)flqKoUxcqY~%w{pD^*-<{gGmde# zRJV*u-W&vWRCukONRv?+YF-T`z$FjvW*W|@Dh#NqEp9ic1abJe8|y~7o^)qzI>$Ta z>}T=#ojT`0k(|qi_Yp3Nb3?_rGC4gx%|biOPE?7Y`CP>^YirfAVVSe;IhD49J5Fo( zEzb*b870)@ow@IVsxo)~HB#fs{m&%xf2c9Su6)_7T(?$YP4`ubh5W4Rxx=jTeD8LG zkJQMtW8DkY+Nx15J!9@`eTI`2o`8CaRJpfocwCWNpKE>+t+nngf=c}@BAhj>=8jVT zg$imZEvofO!7Redx3IIIBDu-COtP|GF1N~^3$sux)=ewE{QxyDkH6|6edHVtj1MKx8`et7fY->tXnIUhym;)zW8YmJ>JCRA-;`3# zo6=NcV+_MJX)24<)J;6{Rtc6ceRR_=CuUl*~w7qV$kQ>K3KAC?!OxN0fR+X^%+B zg0xqZ_KDIWD3>UCoRYUwqS2sEy$1CzQ*WMn3zU84hyv^J-vj?%#v&0V5y0aO_yiyE z5lnalQS2Z9e*7e&*x;v@AYl{42EV3+Hf-9l31bssA5-v)ViUur6Pqq<{KNyxM+E}C zXHz{3qZ8uwg*MgAbHeRD`>zB-qF*GQe}p$x2#8v@E~>h?B=oHiI1Xs>p#uwXVoLx? zgrFAJ67vZGIRrt1&<=1IA&gm>%qN_1gy*9GGD3{yWpO?s;&CTebOZDw#I>Hwg5kFl zsMP(gvWF{sIqU^^0HIIob*5xcdq2dEVC;Yv*Qq;Hc8}voa_b?0qX>slh9L7)KZhd# zg9wVYmz59LMlF94EMA8ejzBq_&@(fn=Y8*eJ$MLltTJ3=TMsGu%1{ zFpiK#nJzW*XLIs132sw1=p;wSKp1lSziJ-G=W9kOaFLNjWOd*&6ClHG6%D(pR zt*uDQ;>ugu!yXh}=l&AFG{Td+*@TAeDQ;T@cpE}x7h9S%Y*lW%0dNwbw%cgjR_C^L zfKvz?yV=Bs?IyR~0yvHE48R$LXB*Fb(Rq&F$%XF*m_>Nbd9n+#0QH#WJ%o#%N18)= zFL7joI|(LzpDTU8OCLbWB7G3)EYgRF*3a(bHv7XwZD| zRoC}Dm%fkmDAEszmcmm`Vu4QkheYpW1%8ADI#Kdt*Y=uAKS6p7>8C_XvkTN^{|uuS zG5T|KcOm`4_570PUC!DrsG|KV3}3?VuU+YHTAYDQFBhpo*KcOxm`}BPram3j;dH!#2 zJ_H**AySa-#;`dDLlM|{bFlN0c_Jvb2f{uNgyYU8t&QD&hfQQ*v*=qG6KxT)uw4^r za8oq}+^WY_eL~fbsrqqMpHy`({4J3__FM7XHtim;ob24w_zJn}*Pw6;GXDWwspH<{ C-ur3* literal 0 HcmV?d00001