From 2bebbfad0bdf27799a5bf7006f5e30f4b01ed5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Wed, 27 Jan 2021 19:11:31 +0100 Subject: [PATCH] fixed some dup problems --- .../decompiler/flash/abc/avm2/AVM2Code.java | 17 +++-- .../instructions/InstructionDefinition.java | 54 +++++++++++++ .../abc/avm2/instructions/SetTypeIns.java | 49 ++++++++---- .../avm2/instructions/other/GetSlotIns.java | 27 +------ .../instructions/other/SetPropertyIns.java | 18 +++++ .../avm2/instructions/other/SetSlotIns.java | 30 +------- .../decompiler/flash/ActionScript3Test.java | 25 ++++++ .../flash/generators/AS3Generator.java | 3 +- .../custom/abc/custom-0/custom-0.main.abc | Bin 1925 -> 2815 bytes .../custom/abc/custom-0/custom-0.main.asasm | 5 ++ .../custom-0/tests/TestDoubleDup.class.asasm | 72 ++++++++++++++++++ .../custom-0/tests/TestDoubleDup.script.asasm | 29 +++++++ .../abc/custom-0/tests/TestDup.class.asasm | 71 +++++++++++++++++ .../abc/custom-0/tests/TestDup.script.asasm | 29 +++++++ .../abc/custom-0/tests/TestDup2.class.asasm | 67 ++++++++++++++++ .../abc/custom-0/tests/TestDup2.script.asasm | 29 +++++++ .../custom-0/tests/TestIncrement3.class.asasm | 67 ++++++++++++++++ .../tests/TestIncrement3.script.asasm | 29 +++++++ .../tests/TestSetSlotFindProperty.class.asasm | 70 +++++++++++++++++ .../TestSetSlotFindProperty.script.asasm | 29 +++++++ .../ffdec_lib/testdata/custom/bin/custom.swf | Bin 1456 -> 1884 bytes 21 files changed, 644 insertions(+), 76 deletions(-) create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestDoubleDup.class.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestDoubleDup.script.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestDup.class.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestDup.script.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestDup2.class.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestDup2.script.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestIncrement3.class.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestIncrement3.script.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestSetSlotFindProperty.class.asasm create mode 100644 libsrc/ffdec_lib/testdata/custom/abc/custom-0/tests/TestSetSlotFindProperty.script.asasm diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 98057d1dd..452371590 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -295,6 +295,7 @@ import com.jpexs.decompiler.graph.SimpleValue; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.TypeItem; import com.jpexs.decompiler.graph.model.BinaryOpItem; +import com.jpexs.decompiler.graph.model.DuplicateItem; import com.jpexs.decompiler.graph.model.ExitItem; import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.ScriptEndItem; @@ -1614,7 +1615,7 @@ public class AVM2Code implements Cloneable { AVM2Instruction insAfter = code.get(ip + 1); Set usages = setLocalPosToGetLocalPos.containsKey(ip) ? setLocalPosToGetLocalPos.get(ip) : new HashSet<>(); - if (!AVM2Item.mustStayIntact2(stack.peek()) && usages.size() == 1 && (usages.iterator().next().equals(ip + 1)) && (insAfter.definition instanceof GetLocalTypeIns) && (((GetLocalTypeIns) insAfter.definition).getRegisterId(insAfter) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins))) { + if (!(stack.peek().getNotCoercedNoDup() instanceof DuplicateItem) && !AVM2Item.mustStayIntact2(stack.peek()) && usages.size() == 1 && (usages.iterator().next().equals(ip + 1)) && (insAfter.definition instanceof GetLocalTypeIns) && (((GetLocalTypeIns) insAfter.definition).getRegisterId(insAfter) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins))) { ip += 2; continue iploop; } else { @@ -2097,14 +2098,14 @@ public class AVM2Code implements Cloneable { ins.operands[j] = updater.updateOperandOffset(target, ins.operands[j]); } }*/ //Faster, but not so universal - if (ins.definition instanceof IfTypeIns) { - long target = ins.getTargetAddress(); - try { - ins.operands[0] = updater.updateOperandOffset(ins.getAddress(), target, ins.operands[0]); - } catch (ConvertException cex) { - throw new ConvertException("Invalid offset (" + ins + ")", i); - } + if (ins.definition instanceof IfTypeIns) { + long target = ins.getTargetAddress(); + try { + ins.operands[0] = updater.updateOperandOffset(ins.getAddress(), target, ins.operands[0]); + } catch (ConvertException cex) { + throw new ConvertException("Invalid offset (" + ins + ")", i); } + } ins.setAddress(updater.updateInstructionOffset(ins.getAddress())); //Note: changing operands here does not change instruction byte length as offsets are always S24 (not variable length) } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java index b3bff26ae..0fcf056d7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java @@ -29,12 +29,20 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ClassAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewActivationAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ScriptAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThisAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; import com.jpexs.helpers.Reference; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitWithSlot; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; @@ -320,4 +328,50 @@ public abstract class InstructionDefinition implements Serializable { } return localData.code.adr2pos(src.getAddress()); } + + protected Multiname searchSlotName(int slotIndex, AVM2LocalData localData, GraphTargetItem obj) { + return searchSlotName(slotIndex, localData, obj, -1); + } + + private Multiname searchSlotName(int slotIndex, AVM2LocalData localData, GraphTargetItem obj, int multiNameIndex) { + if ((obj instanceof ExceptionAVM2Item) && (multiNameIndex == -1 || ((ExceptionAVM2Item) obj).exception.name_index == multiNameIndex)) { + return localData.getConstants().getMultiname(((ExceptionAVM2Item) obj).exception.name_index); + } else if ((obj instanceof ThisAVM2Item) || (obj instanceof ClassAVM2Item) || (obj instanceof ScriptAVM2Item)) { + List traits = localData.getScriptInfo().get(localData.scriptIndex).traits.traits; + for (int t = 0; t < traits.size(); t++) { + Trait trait = traits.get(t); + if (trait instanceof TraitWithSlot) { + if (multiNameIndex == -1 || trait.name_index == multiNameIndex) { + if (((TraitWithSlot) trait).getSlotIndex() == slotIndex) { + return trait.getName(localData.abc); + } + } + } + } + } else if (obj instanceof NewActivationAVM2Item) { + MethodBody body = localData.methodBody; + List traits = body.traits.traits; + for (int t = 0; t < traits.size(); t++) { + Trait trait = traits.get(t); + if (trait instanceof TraitWithSlot) { + if (multiNameIndex == -1 || trait.name_index == multiNameIndex) { + if (((TraitWithSlot) trait).getSlotIndex() == slotIndex) { + return trait.getName(localData.abc); + } + } + } + } + } else if (obj instanceof FindPropertyAVM2Item) { + FindPropertyAVM2Item findProp = (FindPropertyAVM2Item) obj; + + for (GraphTargetItem item : localData.scopeStack) { + Multiname ret = searchSlotName(slotIndex, localData, item, ((FullMultinameAVM2Item) findProp.propertyName).multinameIndex); + if (ret != null) { + return ret; + } + } + + } + return null; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/SetTypeIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/SetTypeIns.java index b40b62bcc..555bc02cc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/SetTypeIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/SetTypeIns.java @@ -48,26 +48,47 @@ public interface SetTypeIns { if (notCoercedValue instanceof DuplicateItem) { GraphTargetItem insideDup = notCoercedValue.value; - if (!AVM2Item.mustStayIntact1(insideDup.getNotCoerced())) { + if (!AVM2Item.mustStayIntact1(insideDup.getNotCoercedNoDup())) { if (!stack.isEmpty() && stack.peek() == insideDup) { stack.pop(); - if ((value instanceof CoerceAVM2Item) || (value instanceof ConvertAVM2Item)) { - value.value = insideDup; - } else { - value = insideDup; - } - result.value = value; - //GraphTargetItem result = new SetLocalAVM2Item(ins, localData.lineStartInstruction, regId, value); - - if (regId > -1 && AVM2Item.mustStayIntact2(insideDup.getNotCoerced())) { //hack + if ((insideDup instanceof DuplicateItem) && regId > -1) { + int numDups = 1; + while ((insideDup instanceof DuplicateItem) && !stack.isEmpty() && stack.peek() == insideDup.value) { + insideDup = insideDup.value; + stack.pop(); + numDups++; + } + if ((value instanceof CoerceAVM2Item) || (value instanceof ConvertAVM2Item)) { + value.value = insideDup; + } else { + value = insideDup; + } + result.value = value; output.add(result); - stack.push(new LocalRegAVM2Item(null, localData.lineStartInstruction, regId, value)); + for (int i = 0; i < numDups; i++) { + stack.push(new LocalRegAVM2Item(null, localData.lineStartInstruction, regId, value)); + } + return; + } else { + + if ((value instanceof CoerceAVM2Item) || (value instanceof ConvertAVM2Item)) { + value.value = insideDup; + } else { + value = insideDup; + } + + result.value = value; + + if (regId > -1 && AVM2Item.mustStayIntact2(insideDup.getNotCoerced())) { //hack + output.add(result); + stack.push(new LocalRegAVM2Item(null, localData.lineStartInstruction, regId, value)); + return; + } + + stack.push(result); return; } - - stack.push(result); - return; } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/GetSlotIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/GetSlotIns.java index a29e0f264..21c540ab7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/GetSlotIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/GetSlotIns.java @@ -50,32 +50,7 @@ public class GetSlotIns extends InstructionDefinition { int slotIndex = ins.operands[0]; GraphTargetItem objinreg = stack.pop(); //scope GraphTargetItem obj = objinreg.getThroughRegister(); - Multiname slotname = null; - if (obj instanceof ExceptionAVM2Item) { - slotname = localData.getConstants().getMultiname(((ExceptionAVM2Item) obj).exception.name_index); - } else if ((obj instanceof ThisAVM2Item) || (obj instanceof ClassAVM2Item) || (obj instanceof ScriptAVM2Item)) { - List traits = localData.getScriptInfo().get(localData.scriptIndex).traits.traits; - for (int t = 0; t < traits.size(); t++) { - Trait tr = traits.get(t); - if (tr instanceof TraitWithSlot) { - if (((TraitWithSlot) tr).getSlotIndex() == slotIndex) { - slotname = tr.getName(localData.abc); - } - } - } - } else if (obj instanceof NewActivationAVM2Item) { - MethodBody body = localData.methodBody; - List traits = body.traits.traits; - for (int t = 0; t < traits.size(); t++) { - Trait trait = traits.get(t); - if (trait instanceof TraitWithSlot) { - if (((TraitWithSlot) trait).getSlotIndex() == slotIndex) { - slotname = trait.getName(localData.abc); - } - } - - } - } + Multiname slotname = searchSlotName(slotIndex, localData, obj); stack.push(new GetSlotAVM2Item(ins, localData.lineStartInstruction, obj, objinreg, slotIndex, slotname)); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java index 633fc635c..f90e82681 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java @@ -161,6 +161,24 @@ public class SetPropertyIns extends InstructionDefinition implements SetTypeIns } } } + //assembled/TestIncrement3 + if ((value instanceof IncrementAVM2Item) || (value instanceof DecrementAVM2Item)) { + boolean isIncrement = (value instanceof IncrementAVM2Item); + if (value.value.getNotCoerced() instanceof GetPropertyAVM2Item) { + GetPropertyAVM2Item getProp = (GetPropertyAVM2Item) value.value.getNotCoerced(); + if (getProp.object instanceof DuplicateItem) { + if (getProp.object.value == obj) { + getProp.object = obj; + if (isIncrement) { + output.add(new PostIncrementAVM2Item(ins, localData.lineStartInstruction, getProp)); + } else { + output.add(new PostDecrementAVM2Item(ins, localData.lineStartInstruction, getProp)); + } + return; + } + } + } + } if (value instanceof LocalRegAVM2Item) { LocalRegAVM2Item valueLocalReg = (LocalRegAVM2Item) value; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java index 7ee425d7e..3fa95b917 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetSlotIns.java @@ -25,6 +25,8 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.SetTypeIns; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ClassAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.DecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FindPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.GetSlotAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.IncrementAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; @@ -66,36 +68,10 @@ public class SetSlotIns extends InstructionDefinition implements SetTypeIns { GraphTargetItem obj = stack.pop(); //scopeId GraphTargetItem objnoreg = obj; obj = obj.getThroughRegister(); - Multiname slotname = null; if (obj instanceof NewActivationAVM2Item) { ((NewActivationAVM2Item) obj).slots.put(slotIndex, value); } - - if (obj instanceof ExceptionAVM2Item) { - slotname = localData.getConstants().getMultiname(((ExceptionAVM2Item) obj).exception.name_index); - } else if ((obj instanceof ThisAVM2Item) || (obj instanceof ClassAVM2Item) || (obj instanceof ScriptAVM2Item)) { - List traits = localData.getScriptInfo().get(localData.scriptIndex).traits.traits; - for (int t = 0; t < traits.size(); t++) { - Trait tr = traits.get(t); - if (tr instanceof TraitWithSlot) { - if (((TraitWithSlot) tr).getSlotIndex() == slotIndex) { - slotname = tr.getName(localData.abc); - } - } - } - } else if (obj instanceof NewActivationAVM2Item) { - MethodBody body = localData.methodBody; - List traits = body.traits.traits; - for (int t = 0; t < traits.size(); t++) { - Trait trait = traits.get(t); - if (trait instanceof TraitWithSlot) { - if (((TraitWithSlot) trait).getSlotIndex() == slotIndex) { - slotname = trait.getName(localData.abc); - } - } - - } - } + Multiname slotname = searchSlotName(slotIndex, localData, obj); if (slotname != null) { if (value instanceof LocalRegAVM2Item) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java index d37b4a120..e2702a039 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -1573,6 +1573,19 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } + @Test + public void testAssembledDoubleDup() { + decompileMethod("assembled", "testDoubleDup", "var _loc10_:Rectangle = myprop(_loc5_);\r\n" + + "_loc10_.mymethod(-_loc10_.width,-_loc10_.height);\r\n", + false); + } + + @Test + public void testAssembledDup() { + decompileMethod("assembled", "testDup", "return 1 - (var _loc1_:Number = 1 - _loc1_ / _loc4_) * _loc1_;\r\n", + false); + } + @Test public void testAssembledDupAssignment() { decompileMethod("assembled", "testDupAssignment", "var _loc1_:int = 0;\r\n" @@ -1625,6 +1638,12 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } + @Test + public void testAssembledIncrement3() { + decompileMethod("assembled", "testIncrement3", "_loc1_.length--;\r\n", + false); + } + @Test public void testAssembledSetSlotDup() { decompileMethod("assembled", "testSetSlotDup", "var _loc5_:int = 5;\r\n" @@ -1633,6 +1652,12 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } + @Test + public void testAssembledSetSlotFindProperty() { + decompileMethod("assembled", "testSetSlotFindProperty", "return var myprop:int = 50;\r\n", + false); + } + @Test public void testOptionalParameters() { String methodName = "testOptionalParameters"; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java index 5b27b419f..8aa4d0ed3 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java @@ -38,6 +38,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -120,7 +121,7 @@ public class AS3Generator { useFile(s, new File("testdata/flashdevelop/bin/flashdevelop.swf"), "standard"); useFile(s, new File("testdata/custom/bin/custom.swf"), "assembled"); - try (PrintWriter pw = new PrintWriter("as3_teststub.java")) { + try (PrintWriter pw = new PrintWriter("as3_teststub.java", Charset.forName("UTF-8"))) { pw.println(s.toString()); } System.exit(0); diff --git a/libsrc/ffdec_lib/testdata/custom/abc/custom-0/custom-0.main.abc b/libsrc/ffdec_lib/testdata/custom/abc/custom-0/custom-0.main.abc index 1412e02c7e2d0c1a056428316e1c173387047ead..3ab0989cacea771a35c5d7be2be5ddb0957a54e3 100644 GIT binary patch literal 2815 zcmd6odvn`F5WsIwcRHQ?e#BOsC{O1lC$Vs#Kw2mvuXYB;4CxI1r(oGuB13FvY`IMS z>qlsZPXK%&r%cO7fNxRm?GPn;yF>Pjqly2uMT=0w>uzT zI_)l^$IslZuby4?+rArn;tu?y-mr1$9u2QpU=6Rd+ixXxs~ijl?TfBp_z~$j>GhAD zrkB2>ANJgS(`^O3$K7V%mBDct@Xo4)&Aq@pbN#c+o-dN;W$>+e((bmt>i4ePzJE>H z-L_9U*WH2IxxNxkUEZeS`d+U^eX(bd59GewFF4b)#t)8;j*q^3QvL4i$-(Jy?5I7s zx^%9`HOYmn!(P`H#c}(IpgMBR@tdC8Pd)DXZr^G8?Pur+jd>SFYIhy>esNzVzE&r7Z^O z((PXO9>454t;621>+9pzbe)c?b*{&T=$y30XCRtQdOtYV88ITgXKtuwN`KhZWOrqo z>A-g`Tq*m0=1zwv!y3Ee8(zgjRwXqxZ->?Rhv#-=@37yEYIYj@&R6+2=iVLXbd=5M z_J0{@>Wg`D$+KR&MSpC!e9z92Tuv+HbGa3Y#EWwqd6Lf<3Pnb~pcGaM#ll))y|5Xq z%82lr(Qs}nhYf6eZ0sBNjR(dj#;3+Z<1^#H_`Hx$1CB8jbOlEfnN&KNRRe>@LrzX( zfQh2b1fO`B=A=pUfiwa+8^}T+=K?t&NHdTNfn1alw8sp9!4ZQG*wP}?b{QxPwi#p@ zW(;c#bB1+>BMe8GI312NoM1S~aEhlfgJlLgjH)d19*bmIafTb1Q^J=pe;@Nhm9aF_ z?y==E1B-#pV1~g*42~I{WwgNPJmZw{IksZ5)dd!*aG)F*2aN;gpmT_Dh;oQ=h;vAA zNOD!-47etjxeh!6JPJGpJPteoJPAAn{6jpKvgOaJd;ph_2GQu)x|KL&A@qN>qiUW< z5x^24EbbXc6cj+JxT%G>RCRInO%#)AT$Er7rKFk`bts`3spitachIa<3mM?M$du}W zsNNb{mgbfZCI@*$|B?`NNcBCr)pa{}X6eUp>8E76Yp(5Hu+bF7lN&qp4 z5(E)Q5QGyDBS^_WoFI~iBtdiz(gdlCkRgbz0vDtWND;(KpcAB*Awp2Z7B|$mGO|xa zyA;varI9^)jOwRp4WUsri;|*yPtE%L`P}F{TQ#wW!wTklVq`yEn79|DTfqXgILQ3`CQ>Le^Fx`!u7T-Ag~jfw zuZB%8A>7d^EEa;rC2VhGFE?osy(yyKi>S#Ow5cXj2=Rv6V5?S>EM*jg8stqyWvM^k zz7=7DnV6*dOs1;T6w`|dcXFC46-t$ssWxR9np%Ci$&zWAfi<+%=et%@y?NE3)fJ_} zwA3GO-wIG>!atDwy&5sSxNv8u{Fx9xC;0_Y7rRbhT2E=op+%gfqSA$G0iBAi`3dn* z65p>;(@P4sF(uB1hy_U$v-6Gh+Ah*2tsC2W4Qf4SH>D0#*KpmauA@@~Wdsb(P2k58 z{y|MOy|jSOPvK?=z98X|Z|q{l`gT_E9wC@j1!r->Igy-uHD-Dl!C9JemP4Es$(a-5 zqc!M>2G4gatEr!FylCj>t1qfWImD|I_>+?p zu^SV_kwkn{o-Rn+ mQ_@a|v@1#Rk^S4bX5E}WDjlFL5ni59K9!XHTEz4s!u=OhuQ|#9 delta 880 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 KF!={75=DCpSIkfV