diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index 034f30aac..bce340f03 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -1237,7 +1237,7 @@ public class ABC { for (int i = 0; i < ins.definition.operands.length; i++) { if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { if (ins.operands[i] >= index) { - ins.operands[i]++; + ins.setOperand(i, ins.operands[i] + 1, b.getCode(), b); } } } @@ -1272,8 +1272,7 @@ public class ABC { for (int i = 0; i < ins.definition.operands.length; i++) { if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { if (ins.operands[i] > index) { - ins.operands[i]--; - b.setModified(); + ins.setOperand(i, ins.operands[i] - 1, b.getCode(), b); } } } @@ -1330,8 +1329,7 @@ public class ABC { for (int i = 0; i < ins.definition.operands.length; i++) { if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { if (ins.operands[i] > index) { - ins.operands[i]--; - b.setModified(); + ins.setOperand(i, ins.operands[i] - 1, b.getCode(), b); } } } @@ -1453,24 +1451,137 @@ public class ABC { getMethodIndexing(); } + /** + * Merges second ABC to this one. + * + * @param secondABC + */ public void mergeABC(ABC secondABC) { - Map namespaceMap = new HashMap<>(); - Map namespaceSetMap = new HashMap<>(); - Map multinameMap = new HashMap<>(); - Map methodInfoMap = new HashMap<>(); + Map mergeStringMap = new HashMap<>(); + Map mergeIntMap = new HashMap<>(); + Map mergeUIntMap = new HashMap<>(); + Map mergeDoubleMap = new HashMap<>(); + Map mergeFloatMap = new HashMap<>(); + Map mergeFloat4Map = new HashMap<>(); + Map mergeDecimalMap = new HashMap<>(); - constants.merge(secondABC.constants, namespaceMap, namespaceSetMap, multinameMap); + Map mergeNamespaceMap = new HashMap<>(); + Map mergeNamespaceSetMap = new HashMap<>(); + Map mergeMultinameMap = new HashMap<>(); + Map mergeMethodInfoMap = new HashMap<>(); + Map mergeMethodBodyMap = new HashMap<>(); + Map mergeClassIndexMap = new HashMap<>(); + Map mergeMetaDataMap = new HashMap<>(); + Map mergeScriptInfoMap = new HashMap<>(); + + mergeABC(secondABC, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeNamespaceSetMap, mergeMultinameMap, mergeMethodInfoMap, mergeMethodBodyMap, mergeClassIndexMap, mergeMetaDataMap, mergeScriptInfoMap); + } + + /** + * Merges second ABC to this one. Gets mapping of indices. + * + * @param secondABC + * @param mergeStringMap + * @param mergeIntMap + * @param mergeUIntMap + * @param mergeDoubleMap + * @param mergeFloatMap + * @param mergeFloat4Map + * @param mergeDecimalMap + * @param mergeNamespaceMap + * @param mergeNamespaceSetMap + * @param mergeMultinameMap + * @param mergeMethodInfoMap + * @param mergeMethodBodyMap + * @param mergeClassIndexMap + * @param mergeMetaDataMap + * @param mergeScriptInfoMap + */ + public void mergeABC(ABC secondABC, + Map mergeStringMap, + Map mergeIntMap, + Map mergeUIntMap, + Map mergeDoubleMap, + Map mergeFloatMap, + Map mergeFloat4Map, + Map mergeDecimalMap, + Map mergeNamespaceMap, + Map mergeNamespaceSetMap, + Map mergeMultinameMap, + Map mergeMethodInfoMap, + Map mergeMethodBodyMap, + Map mergeClassIndexMap, + Map mergeMetaDataMap, + Map mergeScriptInfoMap + ) { + + if (!version.equals(secondABC.version)) { + throw new RuntimeException("ABC versions mismatch"); + } + //Constants + constants.merge(secondABC.constants, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeNamespaceSetMap, mergeMultinameMap); + + //Metadata + for (int m = 0; m < secondABC.metadata_info.size(); m++) { + MetadataInfo secondMetadataInfo = secondABC.metadata_info.get(m); + MetadataInfo newMetadataInfo = new MetadataInfo(); + newMetadataInfo.name_index = mergeStringMap.get(secondMetadataInfo.name_index); + newMetadataInfo.keys = new int[secondMetadataInfo.keys.length]; + newMetadataInfo.values = new int[secondMetadataInfo.values.length]; + for (int i = 0; i < secondMetadataInfo.keys.length; i++) { + newMetadataInfo.keys[i] = mergeStringMap.get(secondMetadataInfo.keys[i]); + newMetadataInfo.values[i] = mergeStringMap.get(secondMetadataInfo.values[i]); + } + int newIndex = metadata_info.size(); + metadata_info.add(newMetadataInfo); + mergeMetaDataMap.put(m, newIndex); + } + + //Method Info for (int i = 0; i < secondABC.method_info.size(); i++) { MethodInfo secondMethodInfo = secondABC.method_info.get(i); int newParamTypes[] = new int[secondMethodInfo.param_types.length]; + for (int t = 0; t < secondMethodInfo.param_types.length; t++) { + newParamTypes[t] = mergeMultinameMap.get(secondMethodInfo.param_types[t]); + } int newParamNames[] = new int[secondMethodInfo.paramNames.length]; - int newRetType = multinameMap.get(secondMethodInfo.ret_type); - int newNameIndex = constants.getStringId(secondABC.constants.getString(secondMethodInfo.name_index), true); + for (int n = 0; n < secondMethodInfo.paramNames.length; n++) { + newParamNames[n] = mergeStringMap.get(secondMethodInfo.paramNames[n]); + } + int newRetType = mergeMultinameMap.get(secondMethodInfo.ret_type); + int newNameIndex = mergeStringMap.get(secondMethodInfo.name_index); ValueKind newOptional[] = new ValueKind[secondMethodInfo.optional.length]; for (int k = 0; k < secondMethodInfo.optional.length; k++) { int vkind = secondMethodInfo.optional[k].value_kind; - int newValueIndex; + Map valueMergeMap = null; switch (vkind) { + case ValueKind.CONSTANT_Utf8: + valueMergeMap = mergeStringMap; + break; + case ValueKind.CONSTANT_Int: + valueMergeMap = mergeIntMap; + break; + case ValueKind.CONSTANT_UInt: + valueMergeMap = mergeUIntMap; + break; + case ValueKind.CONSTANT_Double: + valueMergeMap = mergeDoubleMap; + break; + case ValueKind.CONSTANT_DecimalOrFloat: + //assuming the second ABC has same decimal/float support + if (hasDecimalSupport()) { + valueMergeMap = mergeDecimalMap; + } else if (hasFloatSupport()) { + valueMergeMap = mergeFloatMap; + } else { + //should not happen + } + break; + case ValueKind.CONSTANT_Float4: + if (hasFloatSupport()) { + valueMergeMap = mergeFloat4Map; + } + break; case ValueKind.CONSTANT_ExplicitNamespace: case ValueKind.CONSTANT_Namespace: case ValueKind.CONSTANT_PackageInternalNs: @@ -1478,25 +1589,264 @@ public class ABC { case ValueKind.CONSTANT_ProtectedNamespace: case ValueKind.CONSTANT_PrivateNs: case ValueKind.CONSTANT_StaticProtectedNs: - newValueIndex = namespaceMap.get(secondMethodInfo.optional[k].value_index); + valueMergeMap = mergeNamespaceMap; break; - default: - newValueIndex = secondMethodInfo.optional[k].value_index; } + + int newValueIndex = valueMergeMap != null ? valueMergeMap.get(secondMethodInfo.optional[k].value_index) : secondMethodInfo.optional[k].value_index; newOptional[k] = new ValueKind(newValueIndex, vkind); } MethodInfo newMethodInfo = new MethodInfo(newParamTypes, newRetType, newNameIndex, secondMethodInfo.flags, newOptional, newParamNames); int newIndex = addMethodInfo(newMethodInfo); - methodInfoMap.put(i, newIndex); + mergeMethodInfoMap.put(i, newIndex); } - for (MethodBody secondBody : secondABC.bodies) { - //TODO!!! - //MethodBody newBody = new MethodBody(this, mergeTraits(secondBody.traits), codeBytes, exceptions) + + int classFirstIndex = class_info.size(); + //Class/Instance + for (int c = 0; c < secondABC.class_info.size(); c++) { + ClassInfo secondClassInfo = secondABC.class_info.get(c); + ClassInfo newClassInfo = new ClassInfo(); + int newIndex = class_info.size(); + class_info.add(newClassInfo); + mergeClassIndexMap.put(c, newIndex); + newClassInfo.cinit_index = mergeMethodInfoMap.get(secondClassInfo.cinit_index); + + newClassInfo.lastDispId = secondClassInfo.lastDispId; + InstanceInfo secondInstanceInfo = secondABC.instance_info.get(c); + InstanceInfo newInstanceInfo = new InstanceInfo(); + newInstanceInfo.iinit_index = mergeMethodInfoMap.get(secondInstanceInfo.iinit_index); + newInstanceInfo.super_index = mergeMultinameMap.get(secondInstanceInfo.super_index); + newInstanceInfo.interfaces = new int[secondInstanceInfo.interfaces.length]; + for (int i = 0; i < secondInstanceInfo.interfaces.length; i++) { + newInstanceInfo.interfaces[i] = mergeMultinameMap.get(secondInstanceInfo.interfaces[i]); + } + newInstanceInfo.protectedNS = mergeNamespaceMap.get(secondInstanceInfo.protectedNS); + newInstanceInfo.name_index = mergeMultinameMap.get(secondInstanceInfo.name_index); + newInstanceInfo.flags = secondInstanceInfo.flags; + instance_info.add(newInstanceInfo); + + //do traits in second step as some classes may depend on other classes } + + //Class/Instance traits + for (int c = 0; c < secondABC.class_info.size(); c++) { + ClassInfo secondClassInfo = secondABC.class_info.get(c); + InstanceInfo secondInstanceInfo = secondABC.instance_info.get(c); + ClassInfo newClassInfo = class_info.get(classFirstIndex + c); + InstanceInfo newInstanceInfo = instance_info.get(classFirstIndex + c); + newClassInfo.static_traits = mergeTraits(secondClassInfo.static_traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + newInstanceInfo.instance_traits = mergeTraits(secondInstanceInfo.instance_traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + } + + //Scripts + for (int s = 0; s < secondABC.script_info.size(); s++) { + ScriptInfo secondScriptInfo = secondABC.script_info.get(s); + ScriptInfo newScriptInfo = new ScriptInfo(); + newScriptInfo.init_index = mergeMethodInfoMap.get(secondScriptInfo.init_index); + newScriptInfo.traits = mergeTraits(secondScriptInfo.traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + int newIndex = script_info.size(); + script_info.add(newScriptInfo); + mergeScriptInfoMap.put(s, newIndex); + } + + //Method bodies + for (int b = 0; b < secondABC.bodies.size(); b++) { + MethodBody secondBody = secondABC.bodies.get(b); + MethodBody newBody = secondBody.clone(true); + newBody.method_info = mergeMethodInfoMap.get(secondBody.method_info); + for (int e = 0; e < secondBody.exceptions.length; e++) { + newBody.exceptions[e].name_index = mergeMultinameMap.get(secondBody.exceptions[e].name_index); + newBody.exceptions[e].type_index = mergeMultinameMap.get(secondBody.exceptions[e].type_index); + } + AVM2Code newCode = newBody.getCode(); + for (AVM2Instruction newIns : newCode.code) { + int newOperands[] = newIns.operands == null ? null : newIns.operands.clone(); + boolean modified = false; + if (newIns.operands != null) { + for (int i = 0; i < newIns.definition.operands.length; i++) { + Map mergeMap = null; + switch (newIns.definition.operands[i]) { + case AVM2Code.DAT_CLASS_INDEX: + mergeMap = mergeClassIndexMap; + break; + case AVM2Code.DAT_STRING_INDEX: + mergeMap = mergeStringMap; + break; + case AVM2Code.DAT_INT_INDEX: + mergeMap = mergeIntMap; + break; + case AVM2Code.DAT_UINT_INDEX: + mergeMap = mergeUIntMap; + break; + case AVM2Code.DAT_DOUBLE_INDEX: + mergeMap = mergeDoubleMap; + break; + case AVM2Code.DAT_DECIMAL_INDEX: + mergeMap = mergeDecimalMap; + break; + case AVM2Code.DAT_FLOAT_INDEX: + mergeMap = mergeFloatMap; + break; + case AVM2Code.DAT_FLOAT4_INDEX: + mergeMap = mergeFloat4Map; + break; + case AVM2Code.DAT_NAMESPACE_INDEX: + mergeMap = mergeNamespaceMap; + break; + case AVM2Code.DAT_METHOD_INDEX: + mergeMap = mergeMethodInfoMap; + break; + case AVM2Code.DAT_MULTINAME_INDEX: + mergeMap = mergeMultinameMap; + break; + } + if (mergeMap != null) { + newOperands[i] = mergeMap.get(newIns.operands[i]); + modified = true; + } + } + } + if (modified) { + newIns.setOperands(newOperands, newCode, newBody); + } + newBody.traits = mergeTraits(secondBody.traits, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap, mergeMethodInfoMap, mergeClassIndexMap); + } + newBody.setModified(); + int newIndex = bodies.size(); + bodies.add(newBody); + mergeMethodBodyMap.put(b, newIndex); + } + + //clear caches + abcMethodIndexing = null; + getSwf().clearScriptCache(); + ((Tag) parentTag).setModified(true); } - private Traits mergeTraits(Traits secondTraits) { - return null; //TODO + private Traits mergeTraits(Traits secondTraits, Map mergeMultinameMap, + Map mergeStringMap, + Map mergeIntMap, + Map mergeUIntMap, + Map mergeDoubleMap, + Map mergeFloatMap, + Map mergeFloat4Map, + Map mergeDecimalMap, + Map mergeNamespaceMap, + Map mergeMetaDataMap, + Map mergeMethodInfoMap, + Map mergeClassIndexMap) { + Traits newTraits = new Traits(); + for (Trait t : secondTraits.traits) { + Trait newTrait = null; + if (t instanceof TraitMethodGetterSetter) { + newTrait = mergeTrait((TraitMethodGetterSetter) t, mergeMultinameMap, mergeMethodInfoMap, mergeMetaDataMap); + } else if (t instanceof TraitSlotConst) { + newTrait = mergeTrait((TraitSlotConst) t, mergeMultinameMap, mergeStringMap, mergeIntMap, mergeUIntMap, mergeDoubleMap, mergeFloatMap, mergeFloat4Map, mergeDecimalMap, mergeNamespaceMap, mergeMetaDataMap); + } else if (t instanceof TraitFunction) { + newTrait = mergeTrait((TraitFunction) t, mergeMultinameMap, mergeMethodInfoMap, mergeMetaDataMap); + } else if (t instanceof TraitClass) { + newTrait = mergeTrait((TraitClass) t, mergeClassIndexMap, mergeMultinameMap, mergeMetaDataMap); + } else { + //should not happen + } + + newTraits.addTrait(newTrait); + + } + return newTraits; + } + + private TraitMethodGetterSetter mergeTrait(TraitMethodGetterSetter secondTrait, Map mergeMultinameMap, Map mergeMethodInfoMap, Map mergeMetaDataMap) { + TraitMethodGetterSetter newTrait = secondTrait.clone(); + newTrait.method_info = mergeMethodInfoMap.get(secondTrait.method_info); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + return newTrait; + } + + private TraitSlotConst mergeTrait(TraitSlotConst secondTrait, Map mergeMultinameMap, + Map mergeStringMap, + Map mergeIntMap, + Map mergeUIntMap, + Map mergeDoubleMap, + Map mergeFloatMap, + Map mergeFloat4Map, + Map mergeDecimalMap, + Map mergeNamespaceMap, + Map mergeMetaDataMap + ) { + TraitSlotConst newTrait = secondTrait.clone(); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + newTrait.type_index = mergeMultinameMap.get(secondTrait.type_index); + Map valueMergeMap = null; + + switch (newTrait.value_kind) { + case ValueKind.CONSTANT_Utf8: + valueMergeMap = mergeStringMap; + break; + case ValueKind.CONSTANT_Int: + valueMergeMap = mergeIntMap; + break; + case ValueKind.CONSTANT_UInt: + valueMergeMap = mergeUIntMap; + break; + case ValueKind.CONSTANT_Double: + valueMergeMap = mergeDoubleMap; + break; + case ValueKind.CONSTANT_DecimalOrFloat: + //assuming the second ABC has same decimal/float support + if (hasDecimalSupport()) { + valueMergeMap = mergeDecimalMap; + } else if (hasFloatSupport()) { + valueMergeMap = mergeFloatMap; + } else { + //should not happen + } + break; + case ValueKind.CONSTANT_Float4: + if (hasFloatSupport()) { + valueMergeMap = mergeFloat4Map; + } + break; + case ValueKind.CONSTANT_ExplicitNamespace: + case ValueKind.CONSTANT_Namespace: + case ValueKind.CONSTANT_PackageInternalNs: + case ValueKind.CONSTANT_PackageNamespace: + case ValueKind.CONSTANT_PrivateNs: + case ValueKind.CONSTANT_ProtectedNamespace: + case ValueKind.CONSTANT_StaticProtectedNs: + valueMergeMap = mergeNamespaceMap; + break; + } + if (valueMergeMap != null) { + newTrait.value_index = valueMergeMap.get(secondTrait.value_index); + } + return newTrait; //TODO + } + + private TraitFunction mergeTrait(TraitFunction secondTrait, Map mergeMultinameMap, Map mergeMethodInfoMap, Map mergeMetaDataMap) { + TraitFunction newTrait = secondTrait.clone(); + newTrait.method_info = mergeMethodInfoMap.get(secondTrait.method_info); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + return newTrait; + } + + private TraitClass mergeTrait(TraitClass secondTrait, Map mergeClassMap, Map mergeMultinameMap, Map mergeMetaDataMap) { + TraitClass newTrait = secondTrait.clone(); + newTrait.class_info = mergeClassMap.get(secondTrait.class_info); + newTrait.name_index = mergeMultinameMap.get(secondTrait.name_index); + for (int m = 0; m < secondTrait.metadata.length; m++) { + newTrait.metadata[m] = mergeMetaDataMap.get(secondTrait.metadata[m]); + } + return newTrait; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java index 8671c40b0..0e5f3fb18 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCVersion.java @@ -27,4 +27,27 @@ public class ABCVersion implements Comparable { return "" + major + "." + minor; } + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + this.major; + hash = 53 * hash + this.minor; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ABCVersion other = (ABCVersion) obj; + return true; + } + } 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 c1013919a..ac7f5079a 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 @@ -1100,6 +1100,11 @@ public class AVM2Code implements Cloneable { } } + public void setInstructionOperand(int ip, int operandIndex, int value, MethodBody body) { + int oldVal = code.get(ip).operands[ip]; + code.get(ip).operands[ip] = value; + } + public byte[] getBytes() { return getBytes(null); } @@ -2145,6 +2150,38 @@ public class AVM2Code implements Cloneable { return list; } + public void updateInstructionByteCountByAddr(long instructionAddress, int byteDelta, MethodBody body) { + if (byteDelta != 0) { + updateOffsets(new OffsetUpdater() { + + @Override + public long updateInstructionOffset(long address) { + if (address > instructionAddress) { + return address + byteDelta; + } + return address; + } + + @Override + public int updateOperandOffset(long insAddr, long targetAddress, int offset) { + if (targetAddress > instructionAddress && insAddr <= instructionAddress) { + return offset + byteDelta; + } + if (targetAddress <= instructionAddress && insAddr > instructionAddress) { + return offset - byteDelta; + } + return offset; + } + }, body); + body.setModified(); + } + } + + public void updateInstructionByteCount(int pos, int byteDelta, MethodBody body) { + AVM2Instruction instruction = code.get(pos); + updateInstructionByteCountByAddr(instruction.getAddress(), byteDelta, body); + } + public void updateOffsets(OffsetUpdater updater, MethodBody body) { for (int i = 0; i < code.size(); i++) { AVM2Instruction ins = code.get(i); @@ -2172,6 +2209,7 @@ public class AVM2Code implements Cloneable { } } ins.setAddress(updater.updateInstructionOffset(ins.getAddress())); + //Note: changing operands here does not change instruction byte length as offsets are always S24 (not variable length) } for (ABCException ex : body.exceptions) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java index 50ad462fc..4a51f9ff2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java @@ -772,50 +772,68 @@ public class AVM2ConstantPool implements Cloneable { * Merges second constantpool into this one * * @param secondPool - * @param namespaceMap Output map of merged namespaces. Index in second pool - * to index in this pool. - * @param namespaceSetMap Output map of merged namespace sets. Index in - * second pool to index in this pool. - * @param multinameMap Output map of merged multinames. Index in second pool - * to index in this pool. + * @param stringMap + * @param intMap + * @param uintMap + * @param doubleMap + * @param floatMap + * @param float4Map + * @param decimalMap + * @param namespaceMap + * @param namespaceSetMap + * @param multinameMap */ - public void merge(AVM2ConstantPool secondPool, Map namespaceMap, Map namespaceSetMap, Map multinameMap) { - for (String val : secondPool.constant_string) { - getStringId(val, true); + public void merge(AVM2ConstantPool secondPool, Map stringMap, Map intMap, Map uintMap, Map doubleMap, Map floatMap, Map float4Map, Map decimalMap, Map namespaceMap, Map namespaceSetMap, Map multinameMap) { + stringMap.put(0, 0); + for (int i = 1; i < secondPool.constant_string.size(); i++) { + String val = secondPool.constant_string.get(i); + stringMap.put(i, getStringId(val, true)); } - for (Long val : secondPool.constant_int) { - getIntId(val, true); + intMap.put(0, 0); + for (int i = 1; i < secondPool.constant_int.size(); i++) { + Long val = secondPool.constant_int.get(i); + intMap.put(i, getIntId(val, true)); } - for (Long val : secondPool.constant_uint) { - getUIntId(val, true); + uintMap.put(0, 0); + for (int i = 1; i < secondPool.constant_uint.size(); i++) { + Long val = secondPool.constant_uint.get(i); + uintMap.put(i, getUIntId(val, true)); } - for (Double val : secondPool.constant_double) { - getDoubleId(val, true); + doubleMap.put(0, 0); + for (int i = 1; i < secondPool.constant_double.size(); i++) { + Double val = secondPool.constant_double.get(i); + doubleMap.put(i, getDoubleId(val, true)); } - for (Float val : secondPool.constant_float) { - getFloatId(val, true); + floatMap.put(0, 0); + for (int i = 1; i < secondPool.constant_float.size(); i++) { + Float val = secondPool.constant_float.get(i); + floatMap.put(i, getFloatId(val, true)); } - for (Float4 val : secondPool.constant_float4) { - getFloat4Id(val, true); + float4Map.put(0, 0); + for (int i = 1; i < secondPool.constant_float4.size(); i++) { + Float4 val = secondPool.constant_float4.get(i); + float4Map.put(i, getFloat4Id(val, true)); } - for (Decimal val : secondPool.constant_decimal) { - getDecimalId(val, true); + decimalMap.put(0, 0); + for (int i = 1; i < secondPool.constant_decimal.size(); i++) { + Decimal val = secondPool.constant_decimal.get(i); + decimalMap.put(i, getDecimalId(val, true)); } namespaceMap.put(0, 0); for (int i = 1; i < secondPool.constant_namespace.size(); i++) { - Namespace secondNamespace = secondPool.getNamespace(i); - String secondNsNameStr = secondNamespace.name_index == 0 ? null : secondPool.getString(secondNamespace.name_index); + Namespace secondNamespace = secondPool.constant_namespace.get(i); int mappedId; + int newNameIndex = stringMap.get(secondNamespace.name_index); if (secondNamespace.kind == Namespace.KIND_PRIVATE) {//always add, this does not exists in this ABC. Conflicting private namespaces can have same names. - mappedId = addNamespace(secondNamespace.kind, getStringId(secondNsNameStr, true)); + mappedId = addNamespace(secondNamespace.kind, newNameIndex); } else { - mappedId = getNamespaceId(secondNamespace.kind, secondNsNameStr, 0, true); + mappedId = getNamespaceId(secondNamespace.kind, newNameIndex, 0, true); } namespaceMap.put(i, mappedId); } namespaceSetMap.put(0, 0); for (int i = 1; i < secondPool.constant_namespace_set.size(); i++) { - NamespaceSet secondNamespaceSet = secondPool.getNamespaceSet(i); + NamespaceSet secondNamespaceSet = secondPool.constant_namespace_set.get(i); int mappedsNss[] = new int[secondNamespaceSet.namespaces.length]; for (int n = 0; n < secondNamespaceSet.namespaces.length; n++) { mappedsNss[n] = namespaceMap.get(secondNamespaceSet.namespaces[n]); @@ -825,42 +843,42 @@ public class AVM2ConstantPool implements Cloneable { } multinameMap.put(0, 0); for (int i = 1; i < secondPool.constant_multiname.size(); i++) { - Multiname secondMultiname = secondPool.getMultiname(i); - Multiname importedMultiname = null; - int newNameIndex = secondMultiname.name_index <= 0 ? secondMultiname.name_index : getStringId(secondPool.getString(secondMultiname.name_index), true); + Multiname secondMultiname = secondPool.constant_multiname.get(i); + Multiname newMultiname = null; + int newNameIndex = secondMultiname.name_index <= 0 ? secondMultiname.name_index : stringMap.get(secondMultiname.name_index); int newNsIndex = secondMultiname.namespace_index <= 0 ? secondMultiname.namespace_index : namespaceMap.get(secondMultiname.namespace_index); int newNssIndex = secondMultiname.namespace_set_index <= 0 ? secondMultiname.namespace_set_index : namespaceSetMap.get(secondMultiname.namespace_set_index); switch (secondMultiname.kind) { case Multiname.MULTINAME: - importedMultiname = Multiname.createMultiname(false, newNameIndex, newNssIndex); + newMultiname = Multiname.createMultiname(false, newNameIndex, newNssIndex); break; case Multiname.MULTINAMEA: - importedMultiname = Multiname.createMultiname(true, newNameIndex, newNssIndex); + newMultiname = Multiname.createMultiname(true, newNameIndex, newNssIndex); break; case Multiname.MULTINAMEL: - importedMultiname = Multiname.createMultinameL(false, newNssIndex); + newMultiname = Multiname.createMultinameL(false, newNssIndex); break; case Multiname.MULTINAMELA: - importedMultiname = Multiname.createMultinameL(true, newNssIndex); + newMultiname = Multiname.createMultinameL(true, newNssIndex); break; case Multiname.QNAME: - importedMultiname = Multiname.createQName(false, newNameIndex, newNsIndex); + newMultiname = Multiname.createQName(false, newNameIndex, newNsIndex); break; case Multiname.QNAMEA: - importedMultiname = Multiname.createQName(true, newNameIndex, newNsIndex); + newMultiname = Multiname.createQName(true, newNameIndex, newNsIndex); break; case Multiname.RTQNAME: - importedMultiname = Multiname.createRTQName(false, newNameIndex); + newMultiname = Multiname.createRTQName(false, newNameIndex); break; case Multiname.RTQNAMEA: - importedMultiname = Multiname.createRTQName(true, newNameIndex); + newMultiname = Multiname.createRTQName(true, newNameIndex); break; case Multiname.RTQNAMEL: - importedMultiname = Multiname.createRTQNameL(false); + newMultiname = Multiname.createRTQNameL(false); break; case Multiname.RTQNAMELA: - importedMultiname = Multiname.createRTQNameL(true); + newMultiname = Multiname.createRTQNameL(true); break; case Multiname.TYPENAME: int newQnameIndex = multinameMap.get(secondMultiname.qname_index); @@ -868,11 +886,11 @@ public class AVM2ConstantPool implements Cloneable { for (int p = 0; p < secondMultiname.params.length; p++) { newParams[p] = multinameMap.get(secondMultiname.params[p]); } - importedMultiname = Multiname.createTypeName(newQnameIndex, newParams); + newMultiname = Multiname.createTypeName(newQnameIndex, newParams); break; } - int mappedId = getMultinameId(importedMultiname, true); + int mappedId = getMultinameId(newMultiname, true); multinameMap.put(i, mappedId); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java index f53b17d80..79a1a003a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java @@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; import com.jpexs.decompiler.flash.abc.types.Float4; +import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -488,4 +489,42 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem { public String getFile() { return file; } + + /** + * Set operand value the right way - update offsets neccessarily. Because + * some operand types are variable length (like U30) + * + * @param operandIndex + * @param newValue + * @param code + * @param body + */ + public void setOperand(int operandIndex, int newValue, AVM2Code code, MethodBody body) { + int oldByteCount = getBytesLength(); + operands[operandIndex] = newValue; + int newByteCount = getBytesLength(); + int byteDelta = newByteCount - oldByteCount; + if (byteDelta != 0) { + code.updateInstructionByteCountByAddr(address, byteDelta, body); + } + } + + /** + * Set operand values the right way - update offsets neccessarily. Because + * some operand types are variable length (like U30) + * + * @param operands + * @param code + * @param body + */ + public void setOperands(int operands[], AVM2Code code, MethodBody body) { + int oldByteCount = getBytesLength(); + this.operands = operands; + int newByteCount = getBytesLength(); + int byteDelta = newByteCount - oldByteCount; + if (byteDelta != 0) { + code.updateInstructionByteCountByAddr(address, byteDelta, body); + } + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index dcfde7578..de2f8c1da 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -424,6 +424,10 @@ public final class MethodBody implements Cloneable { @Override public MethodBody clone() { + return clone(false); + } + + public MethodBody clone(boolean deepTraits) { try { MethodBody ret = (MethodBody) super.clone(); if (code != null) { @@ -437,10 +441,9 @@ public final class MethodBody implements Cloneable { } } - // maybe deep clone traits - /*if (traits != null) { - ret.traits = traits.clone(); - }*/ + if (deepTraits && traits != null) { + ret.traits = traits.clone(); + } ret.convertedItems = null; ret.convertException = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java index 98a4cdc45..3e27a2714 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ValueKind.java @@ -25,7 +25,7 @@ import com.jpexs.helpers.Helper; */ public class ValueKind { - public static final int CONSTANT_Decimal = 0x02; //decimal + public static final int CONSTANT_DecimalOrFloat = 0x02; //decimal or float depending on ABC version public static final int CONSTANT_Int = 0x03;// integer @@ -57,6 +57,8 @@ public class ValueKind { public static final int CONSTANT_PrivateNs = 0x05;// namespace + public static final int CONSTANT_Float4 = 0x1E;// float4 + private static final int[] optionalKinds = new int[]{0x03, 0x04, 0x06, 0x02, 0x01, 0x0B, 0x0A, 0x0C, 0x00, 0x08, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x05}; private static final String[] optionalKindNames = new String[]{"Int", "UInt", "Double", "Decimal", "Utf8", "True", "False", "Null", "Undefined", "Namespace", "PackageNamespace", "PackageInternalNs", "ProtectedNamespace", "ExplicitNamespace", "StaticProtectedNs", "PrivateNamespace"}; @@ -137,7 +139,7 @@ public class ValueKind { case CONSTANT_Double: ret = "" + constants.getDouble(value_index); break; - case CONSTANT_Decimal: + case CONSTANT_DecimalOrFloat: ret = "" + constants.getDecimal(value_index); break; case CONSTANT_Utf8: @@ -179,7 +181,7 @@ public class ValueKind { case CONSTANT_Double: ret = "Double(" + constants.getDouble(value_index) + ")"; break; - case CONSTANT_Decimal: + case CONSTANT_DecimalOrFloat: ret = "Decimal(" + constants.getDecimal(value_index) + ")"; break; case CONSTANT_Utf8: diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index ef8c1134b..7c97ca182 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -110,6 +110,7 @@ import com.jpexs.decompiler.flash.importers.SwfXmlImporter; import com.jpexs.decompiler.flash.importers.TextImporter; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; @@ -595,6 +596,13 @@ public class CommandLineArgumentParser { out.println(" ...: Input SWF file"); } + if (filter == null || filter.equals("abcmerge")) { + out.println(" " + (cnt++) + ") -abcmerge "); + out.println(" ...merge all ABC tags in SWF file to one"); + out.println(" ...: Where to save merged file"); + out.println(" ...: Input SWF file"); + } + printCmdLineUsageExamples(out, filter); } @@ -810,7 +818,9 @@ public class CommandLineArgumentParser { command = nextParam.substring(1); } - if (command.equals("swf2swc")) { + if (command.equals("abcmerge")) { + parseAbcMerge(args); + } else if (command.equals("swf2swc")) { parseSwf2Swc(args); } else if (command.equals("linkreport")) { parseLinkReport(selectionClasses, args); @@ -1009,6 +1019,27 @@ public class CommandLineArgumentParser { setConfigurations(args.pop()); } + private static void parseAbcMerge(Stack args) { + if (args.size() < 2) { + badArguments("abcmerge"); + } + final File outFile = new File(args.pop()); + final File swfFile = new File(args.pop()); + processModifySWF(swfFile, outFile, null, (SWF swf, OutputStream stdout) -> { + List abcList = swf.getAbcList(); + if (!abcList.isEmpty() && abcList.size() > 1) { + ABC firstAbc = abcList.get(0).getABC(); + for (int i = 1; i < abcList.size(); i++) { + firstAbc.mergeABC(abcList.get(i).getABC()); + } + for (int i = 1; i < abcList.size(); i++) { + swf.removeTag((Tag) abcList.get(i)); + } + } + }); + + } + private static void parseSwf2Swc(Stack args) { if (args.size() < 2) { badArguments("swf2swc");