diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index c13644348..aa7f43504 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -414,9 +414,9 @@ public class Amf3InputStream extends InputStream { renameU29("U29A-value", NO_REFERENCE_BIT_TEXT, "dense count"); int denseCount = (int) (arrayU29 >> 1); LOGGER.log(Level.FINEST, "Array value: denseCount={0}", new Object[]{denseCount}); - List> assocPart = new ArrayList<>(); + Map assocPart = new ListMap<>(); List densePart = new ArrayList<>(); - ArrayType retArray = new ArrayType(densePart, assocPart); + ArrayType retArray = new ArrayType(); objectTable.add(retArray); //add before processing elements which may reference this newDumpLevel("associativeValues", "assoc-value"); while (true) { @@ -427,13 +427,15 @@ public class Amf3InputStream extends InputStream { } else { try { Object val = readValue("value", serializers, objectTable, traitsTable, stringTable); - assocPart.add(new Pair<>(key, val)); + assocPart.put(key, val); } catch (NoSerializerExistsException nse) { - assocPart.add(new Pair<>(key, nse.getIncompleteData())); + assocPart.put(key, nse.getIncompleteData()); + retArray.setAssociativeValues(assocPart); throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); } } } + retArray.setAssociativeValues(assocPart); endDumpLevel(); LOGGER.log(Level.FINEST, "Array value: assocSize={0}", new Object[]{assocPart.size()}); @@ -446,9 +448,12 @@ public class Amf3InputStream extends InputStream { for (int j = i + 1; j < denseCount; j++) { densePart.add(BasicType.UNKNOWN); } + retArray.setDenseValues(densePart); throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); } } + retArray.setDenseValues(densePart); + endDumpLevel(); LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()}); result = retArray; @@ -477,11 +482,11 @@ public class Amf3InputStream extends InputStream { renameU29("U29O-traits-ext", NO_REFERENCE_BIT_TEXT, "not trait reference", "externalized traits", "unused"); String className = readUtf8Vr("className", stringTable); if (!serializers.containsKey(className)) { - throw new NoSerializerExistsException(className, new ObjectType(new Traits(className, false, new ArrayList<>()), (byte[]) null, new ArrayList<>()), null); + throw new NoSerializerExistsException(className, new ObjectType(new Traits(className, false, new ArrayList<>()), (byte[]) null, new HashMap<>()), null); } newDumpLevel("serializedData", "U8[]"); MonitoredInputStream mis = new MonitoredInputStream(is); - List> serMembers = serializers.get(className).readObject(className, mis); + Map serMembers = serializers.get(className).readObject(className, mis); byte serData[] = mis.getReadData(); endDumpLevel(); Traits unserTraits = new Traits(className, false, new ArrayList<>()); @@ -517,10 +522,10 @@ public class Amf3InputStream extends InputStream { traits = traitsTable.get(refIndexTraits); LOGGER.log(Level.FINER, "Traits value: reference({0}) - traitsize={1}", new Object[]{refIndexTraits, traits.getSealedMemberNames().size()}); } - List> sealedMembers = new ArrayList<>(); - List> dynamicMembers = new ArrayList<>(); + Map sealedMembers = new ListMap<>(); + Map dynamicMembers = new ListMap<>(); - Object retObjectType = new ObjectType(traits, sealedMembers, dynamicMembers); + ObjectType retObjectType = new ObjectType(traits); objectTable.add(retObjectType); //add it before any subvalue can reference it List sealedMemberValues = new ArrayList<>(); NoSerializerExistsException error = null; @@ -541,18 +546,22 @@ public class Amf3InputStream extends InputStream { endDumpLevel(); } - for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { - sealedMembers.add(new Pair<>(traits.getSealedMemberNames().get(i), sealedMemberValues.get(i))); + List memberNames = new ArrayList<>(); + memberNames.addAll(traits.getSealedMemberNames()); //Assuming it is ListSet so maintains order + for (int i = 0; i < memberNames.size(); i++) { + sealedMembers.put(memberNames.get(i), sealedMemberValues.get(i)); } + retObjectType.setSealedMembers(sealedMembers); if (traits.isDynamic()) { newDumpLevel("dynamicMembers", "dynamic-member[]"); String dynamicMemberName; while (!(dynamicMemberName = readUtf8Vr("name", stringTable)).isEmpty()) { try { Object dynamicMemberValue = readValue("value", serializers, objectTable, traitsTable, stringTable); - dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); + dynamicMembers.put(dynamicMemberName, dynamicMemberValue); } catch (NoSerializerExistsException nse) { - dynamicMembers.add(new Pair<>(dynamicMemberName, nse.getIncompleteData())); + dynamicMembers.put(dynamicMemberName, nse.getIncompleteData()); + retObjectType.setDynamicMembers(dynamicMembers); throw new NoSerializerExistsException(nse.getClassName(), retObjectType, nse); } finally { //group dumpInfo to one sub "dynamic-member" @@ -570,6 +579,7 @@ public class Amf3InputStream extends InputStream { } } } + retObjectType.setDynamicMembers(dynamicMembers); renameLastDump("UTF-8-empty"); endDumpLevel(); } @@ -767,8 +777,8 @@ public class Amf3InputStream extends InputStream { renameU29("U29Dict-value", NO_REFERENCE_BIT_TEXT, "entries count"); int numEntries = (int) (dictionaryObjectU29 >> 1); int weakKeys = readU8("weak keys"); - List> data = new ArrayList<>(); - DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); + Map data = new ListMap<>(true); + DictionaryType retDictionary = new DictionaryType(weakKeys == 1); objectTable.add(retDictionary); NoSerializerExistsException error = null; newDumpLevel("entries", ""); @@ -789,10 +799,10 @@ public class Amf3InputStream extends InputStream { val = BasicType.UNKNOWN; } - data.add(new Pair<>(key, val)); + retDictionary.put(key, val); if (error != null) { for (int j = i + 1; j < numEntries; j++) { - data.add(new Pair<>(BasicType.UNKNOWN, BasicType.UNKNOWN)); + retDictionary.put(BasicType.UNKNOWN, BasicType.UNKNOWN); } break; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java index 39371a5ae..e9838c76d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java @@ -175,9 +175,9 @@ public class Amf3OutputStream extends OutputStream { objectTable.add(val); writeU29((val.getDenseValues().size() << 1) | NO_REFERENCE_FLAG); - for (Pair p : val.getAssociativeValues()) { - writeUtf8Vr(p.getFirst(), stringTable); - writeValue(p.getSecond(), serializers, stringTable, traitsTable, objectTable); + for (String key : val.associativeKeySet()) { + writeUtf8Vr(key, stringTable); + writeValue(val.getAssociative(key), serializers, stringTable, traitsTable, objectTable); } writeUtf8Vr("", stringTable); for (Object v : val.getDenseValues()) { @@ -207,22 +207,22 @@ public class Amf3OutputStream extends OutputStream { } } else { traitsTable.add(traits); - writeU29((val.getSealedMembers().size() << 4) | NO_REFERENCE_FLAG | NO_TRAIT_REFERENCE_FLAG | (traits.isDynamic() ? DYNAMIC_FLAG : 0)); + writeU29((val.sealedMembersSize() << 4) | NO_REFERENCE_FLAG | NO_TRAIT_REFERENCE_FLAG | (traits.isDynamic() ? DYNAMIC_FLAG : 0)); writeUtf8Vr(val.getClassName(), stringTable); - for (Pair v : val.getSealedMembers()) { - writeUtf8Vr(v.getFirst(), stringTable); + for (String key : val.sealedMembersKeySet()) { + writeUtf8Vr(key, stringTable); } } } else { writeU29((traitsIndex << 2) | NO_REFERENCE_FLAG); } - for (Pair v : val.getSealedMembers()) { - writeValue(v.getSecond(), serializers, stringTable, traitsTable, objectTable); + for (String key : val.sealedMembersKeySet()) { + writeValue(val.getSealedMember(key), serializers, stringTable, traitsTable, objectTable); } if (traits.isDynamic()) { - for (Pair v : val.getDynamicMembers()) { - writeUtf8Vr(v.getFirst(), stringTable); - writeValue(v.getSecond(), serializers, stringTable, traitsTable, objectTable); + for (String key : val.dynamicMembersKeySet()) { + writeUtf8Vr(key, stringTable); + writeValue(val.getDynamicMember(key), serializers, stringTable, traitsTable, objectTable); } writeUtf8Vr("", stringTable); } @@ -349,11 +349,11 @@ public class Amf3OutputStream extends OutputStream { DictionaryType val = (DictionaryType) object; if (dictionaryIndex == -1) { objectTable.add(val); - writeU29((val.getPairs().size() << 1) | NO_REFERENCE_FLAG); + writeU29((val.size() << 1) | NO_REFERENCE_FLAG); writeU8(val.hasWeakKeys() ? 1 : 0); - for (Pair p : val.getPairs()) { - writeValue(p.getFirst(), serializers, stringTable, traitsTable, objectTable); - writeValue(p.getSecond(), serializers, stringTable, traitsTable, objectTable); + for (Object key : val.keySet()) { + writeValue(key, serializers, stringTable, traitsTable, objectTable); + writeValue(val.get(key), serializers, stringTable, traitsTable, objectTable); } } else { writeU29(dictionaryIndex << 1); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ListMap.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ListMap.java new file mode 100644 index 000000000..895a1e55c --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ListMap.java @@ -0,0 +1,196 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Map which maintains order of keys + * + * @param Key type + * @param Value type + */ +public class ListMap implements Map { + + private final Set orderedKeys = new ListSet<>(); + private final Map map; + + /** + * Creates new MaintainKeyOrderMap based on HashMap + */ + public ListMap() { + this(false); + } + + /** + * Creates new MaintainKeyOrderMap + * + * @param useIdentityMap The HashMap will be based on IdentityHashMap + */ + public ListMap(boolean useIdentityMap) { + this(useIdentityMap, new HashMap<>()); + } + + /** + * Creates new MaintainKeyOrderMap based on HashMap + * + * @param m Initial items + */ + public ListMap(Map m) { + this(false, m); + } + + /** + * Creates new MaintainKeyOrderMap + * + * @param useIdentityMap The HashMap will be based on IdentityHashMap + * @param m Initial items + */ + public ListMap(boolean useIdentityMap, Map m) { + if (useIdentityMap) { + map = new IdentityHashMap<>(); + } else { + map = new HashMap<>(); + } + putAll(m); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public V get(Object key) { + return map.get(key); + } + + @Override + public V put(K key, V value) { + orderedKeys.add(key); + return map.put(key, value); + } + + @Override + public V remove(Object key) { + orderedKeys.remove(key); + return map.remove(key); + } + + @Override + public void putAll(Map m) { + for (Map.Entry e : m.entrySet()) { + orderedKeys.add(e.getKey()); + } + map.putAll(m); + } + + @Override + public void clear() { + orderedKeys.clear(); + map.clear(); + } + + @Override + public Set keySet() { + return new ListSet<>(orderedKeys); + } + + @Override + public Collection values() { + List vals = new ArrayList<>(); + for (K key : orderedKeys) { + vals.add(map.get(key)); + } + return vals; + } + + @Override + public Set> entrySet() { + Set> ret = new ListSet<>(); + for (K key : orderedKeys) { + V value = map.get(key); + ret.add(new MyEntry<>(key, value)); + } + return ret; + } + + public static class MyEntry implements Entry { + + private K key; + private V value; + + public MyEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(V value) { + V oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 37 * hash + Objects.hashCode(this.key); + hash = 37 * hash + Objects.hashCode(this.value); + 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 MyEntry other = (MyEntry) obj; + if (!Objects.equals(this.key, other.key)) { + return false; + } + if (!Objects.equals(this.value, other.value)) { + return false; + } + return true; + } + + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ListSet.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ListSet.java new file mode 100644 index 000000000..b351cad62 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ListSet.java @@ -0,0 +1,101 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Set which maintains orders elements by time they were added + * + * @param + */ +public class ListSet implements Set { + + private final List list = new ArrayList<>(); + + public ListSet() { + + } + + public ListSet(Collection c) { + addAll(c); + } + + @Override + public int size() { + return list.size(); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return list.contains(o); + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + + @Override + public Object[] toArray() { + return list.toArray(); + } + + @Override + public E[] toArray(E[] a) { + return (E[]) list.toArray(a); + } + + @Override + public boolean add(E e) { + if (!contains(e)) { + list.add(e); + return true; + } + return false; + } + + @Override + public boolean remove(Object o) { + return list.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return list.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + boolean modified = false; + for (E e : c) { + if (add(e)) { + modified = true; + } + } + return modified; + } + + @Override + public boolean retainAll(Collection c) { + return list.retainAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return list.removeAll(c); + } + + @Override + public void clear() { + list.clear(); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java index 1acbc0b71..dd8c1060d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java @@ -3,11 +3,11 @@ package com.jpexs.decompiler.flash.amf.amf3; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.List; +import java.util.Map; public interface ObjectTypeSerializeHandler { - public List> readObject(String className, InputStream is) throws IOException; + public Map readObject(String className, InputStream is) throws IOException; - public void writeObject(List> members, OutputStream os) throws IOException; + public void writeObject(Map members, OutputStream os) throws IOException; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java deleted file mode 100644 index 81e7dd7b1..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.jpexs.decompiler.flash.amf.amf3; - -public class Pair { - - private T1 first; - private T2 second; - - public Pair(T1 first, T2 second) { - this.first = first; - this.second = second; - } - - public T1 getFirst() { - return first; - } - - public T2 getSecond() { - return second; - } - - public void setSecond(T2 second) { - this.second = second; - } - - public void setFirst(T1 first) { - this.first = first; - } -} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java index 2ebd59327..49ce9743e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java @@ -1,17 +1,18 @@ package com.jpexs.decompiler.flash.amf.amf3; -import java.util.List; +import java.util.Collection; +import java.util.Set; public class Traits { private String className; private boolean dynamic; - private List sealedMemberNames; + private Set sealedMemberNames; - public Traits(String className, boolean dynamic, List sealedMemberNames) { + public Traits(String className, boolean dynamic, Collection sealedMemberNames) { this.className = className; this.dynamic = dynamic; - this.sealedMemberNames = sealedMemberNames; + this.sealedMemberNames = new ListSet<>(sealedMemberNames); } public String getClassName() { @@ -22,8 +23,8 @@ public class Traits { return dynamic; } - public List getSealedMemberNames() { - return sealedMemberNames; + public Set getSealedMemberNames() { + return new ListSet<>(sealedMemberNames); } public void setClassName(String className) { @@ -34,8 +35,8 @@ public class Traits { this.dynamic = dynamic; } - public void setSealedMemberNames(List sealedMemberNames) { - this.sealedMemberNames = sealedMemberNames; + public void setSealedMemberNames(Collection sealedMemberNames) { + this.sealedMemberNames = new ListSet<>(sealedMemberNames); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java index 637219def..428112dc4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -1,28 +1,69 @@ package com.jpexs.decompiler.flash.amf.amf3.types; +import com.jpexs.decompiler.flash.amf.amf3.ListMap; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; -import com.jpexs.decompiler.flash.amf.amf3.Pair; -import com.jpexs.helpers.Helper; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; public class ArrayType implements WithSubValues, Amf3ValueType { private List denseValues; - private List> associativeValues; + private Map associativeValues; - public ArrayType(List denseValues, List> associativeValues) { - this.denseValues = denseValues; - this.associativeValues = associativeValues; + public ArrayType(Map associativeValues) { + this(new ArrayList<>(), associativeValues); + } + + public ArrayType(List denseValues) { + this(denseValues, new HashMap<>()); + } + + public ArrayType() { + this(new ArrayList<>(), new HashMap<>()); + } + + public ArrayType(List denseValues, Map associativeValues) { + this.denseValues = new ArrayList<>(denseValues); + this.associativeValues = new ListMap<>(associativeValues); } public List getDenseValues() { - return denseValues; + return new ArrayList<>(denseValues); } - public List> getAssociativeValues() { - return associativeValues; + public void setDenseValues(List denseValues) { + this.denseValues = new ArrayList<>(denseValues); + } + + public Object setDense(int key, Object value) { + return denseValues.set(key, value); + } + + public Object putAssociative(String key, Object value) { + return associativeValues.put(key, value); + } + + public Map getAssociativeValues() { + return new ListMap<>(associativeValues); + } + + public Object getAssociative(String key) { + return associativeValues.get(key); + } + + public Object getDense(int index) { + if (index >= 0 && index < denseValues.size()) { + return denseValues.get(index); + } + return null; + } + + public Set associativeKeySet() { + return associativeValues.keySet(); } @Override @@ -33,14 +74,14 @@ public class ArrayType implements WithSubValues, Amf3ValueType { @Override public List getSubValues() { List ret = new ArrayList<>(); - for (Pair p : associativeValues) { - ret.add(p.getFirst()); - ret.add(p.getSecond()); - } - for (Object v : denseValues) { - ret.add(v); - } + ret.addAll(associativeValues.keySet()); + ret.addAll(associativeValues.values()); + ret.addAll(denseValues); return ret; } + public void setAssociativeValues(Map associativeValues) { + this.associativeValues = new ListMap<>(associativeValues); + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index 5acd6a9dd..79439f121 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -1,32 +1,31 @@ package com.jpexs.decompiler.flash.amf.amf3.types; +import com.jpexs.decompiler.flash.amf.amf3.ListMap; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; -import com.jpexs.decompiler.flash.amf.amf3.Pair; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; +import java.util.HashMap; +import java.util.Map; -public class DictionaryType implements WithSubValues, Amf3ValueType { +public class DictionaryType extends ListMap implements WithSubValues, Amf3ValueType { - private boolean weakKeys; - private List> pairs; + private final boolean weakKeys; - public DictionaryType(boolean weakKeys, List> pairs) { - this.weakKeys = weakKeys; - this.pairs = pairs; + public DictionaryType(boolean weakKeys) { + this(weakKeys, new HashMap<>()); } - public List> getPairs() { - return pairs; + public DictionaryType(boolean weakKeys, Map entries) { + super(true /*IdentityMap*/, entries); + this.weakKeys = weakKeys; //TODO? Really make the Map weak - something like WeakIdentityMap - but is it neccessary for serialization? } @Override public List getSubValues() { List ret = new ArrayList<>(); - for (Pair p : pairs) { - ret.add(p.getFirst()); - ret.add(p.getSecond()); - } + ret.addAll(keySet()); + ret.addAll(values()); return ret; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index 98cf21353..19d995538 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -1,18 +1,22 @@ package com.jpexs.decompiler.flash.amf.amf3.types; +import com.jpexs.decompiler.flash.amf.amf3.ListSet; +import com.jpexs.decompiler.flash.amf.amf3.ListMap; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; -import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.decompiler.flash.amf.amf3.Traits; -import com.jpexs.helpers.Helper; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; -public class ObjectType implements WithSubValues, Amf3ValueType { +public class ObjectType implements WithSubValues, Amf3ValueType, Map { - private List> sealedMembers; - private List> dynamicMembers; - private List> serializedMembers; + private Map sealedMembers; + private Map dynamicMembers; + private Map serializedMembers; //null = not serialized or unknown private byte[] serializedData = null; private boolean serialized; @@ -26,19 +30,23 @@ public class ObjectType implements WithSubValues, Amf3ValueType { return traits; } - public ObjectType(Traits traits, byte[] serializedData, List> serializedMembers) { + public ObjectType(Traits traits, byte[] serializedData, Map serializedMembers) { this.traits = traits; this.serializedData = serializedData; - this.serializedMembers = serializedMembers; - this.dynamicMembers = new ArrayList<>(); - this.sealedMembers = new ArrayList<>(); + this.serializedMembers = new ListMap(serializedMembers); + this.dynamicMembers = new ListMap<>(); + this.sealedMembers = new ListMap<>(); this.serialized = true; } - public ObjectType(Traits traits, List> sealedMembers, List> dynamicMembers) { - this.sealedMembers = sealedMembers; - this.dynamicMembers = dynamicMembers; - this.serializedMembers = new ArrayList<>(); + public ObjectType(Traits traits) { + this(traits, new HashMap<>(), new HashMap<>()); + } + + public ObjectType(Traits traits, Map sealedMembers, Map dynamicMembers) { + this.sealedMembers = new ListMap<>(sealedMembers); + this.dynamicMembers = new ListMap<>(dynamicMembers); + this.serializedMembers = new ListMap<>(); this.serialized = false; this.traits = traits; } @@ -47,14 +55,6 @@ public class ObjectType implements WithSubValues, Amf3ValueType { return traits.isDynamic(); } - public List> getDynamicMembers() { - return dynamicMembers; - } - - public List> getSealedMembers() { - return sealedMembers; - } - public String getClassName() { return traits.getClassName(); } @@ -62,20 +62,14 @@ public class ObjectType implements WithSubValues, Amf3ValueType { @Override public List getSubValues() { List ret = new ArrayList<>(); - for (Pair p : dynamicMembers) { - ret.add(p.getFirst()); - ret.add(p.getSecond()); - } - for (Pair p : sealedMembers) { - ret.add(p.getFirst()); - ret.add(p.getSecond()); - } + ret.addAll(dynamicMembers.keySet()); + ret.addAll(dynamicMembers.values()); - for (Pair p : serializedMembers) { - ret.add(p.getFirst()); - ret.add(p.getSecond()); - } + ret.addAll(sealedMembers.keySet()); + ret.addAll(sealedMembers.values()); + ret.addAll(serializedMembers.keySet()); + ret.addAll(serializedMembers.values()); return ret; } @@ -92,12 +86,227 @@ public class ObjectType implements WithSubValues, Amf3ValueType { return serializedData; } - public void setSerializedMembers(List> serializedMembers) { - this.serializedMembers = serializedMembers; + public void setSerializedMembers(Map serializedMembers) { + this.serializedMembers = new ListMap<>(serializedMembers); } - public List> getSerializedMembers() { - return serializedMembers; + public Map getSerializedMembers() { + return new ListMap<>(serializedMembers); + } + + public void setSealedMembers(Map sealedMembers) { + this.sealedMembers = new ListMap<>(sealedMembers); + } + + public void setDynamicMembers(Map sealedMembers) { + this.dynamicMembers = new ListMap<>(sealedMembers); + } + + @Override + public int size() { + return keySet().size(); + } + + public int sealedMembersSize() { + return sealedMembers.size(); + } + + public int dynamicMembersSize() { + return dynamicMembers.size(); + } + + public int serializedMembersSize() { + return serializedMembers.size(); + } + + @Override + public boolean isEmpty() { + return dynamicMembers.isEmpty() && sealedMembers.isEmpty() && serializedMembers.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return dynamicMembers.containsKey(key) || sealedMembers.containsKey(key) || serializedMembers.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return dynamicMembers.containsValue(value) || sealedMembers.containsValue(value) || serializedMembers.containsValue(value); + } + + @Override + public Object get(Object key) { + if (!(key instanceof String)) { + return null; + } + String stringKey = (String) key; + + if (dynamicMembers.containsKey(stringKey)) { + return dynamicMembers.get(stringKey); + } + if (sealedMembers.containsKey(stringKey)) { + return sealedMembers.get(stringKey); + } + if (serializedMembers.containsKey(stringKey)) { + return serializedMembers.get(stringKey); + } + return null; + } + + public Object getSealedMember(Object key) { + if (!(key instanceof String)) { + return null; + } + String stringKey = (String) key; + + return sealedMembers.get(stringKey); + } + + public Object getDynamicMember(Object key) { + if (!(key instanceof String)) { + return null; + } + String stringKey = (String) key; + + return dynamicMembers.get(stringKey); + } + + public Object getSerializedMember(Object key) { + if (!(key instanceof String)) { + return null; + } + String stringKey = (String) key; + + return serializedMembers.get(stringKey); + } + + @Override + public Object put(String key, Object value) { + return putDynamicMember(key, value); + } + + public Object putDynamicMember(String key, Object value) { + remove(key); + return dynamicMembers.put(key, value); + } + + public Object putSealedMember(String key, Object value) { + remove(key); + return sealedMembers.put(key, value); + } + + public Object putSerializedMember(String key, Object value) { + remove(key); + return serializedMembers.put(key, value); + } + + @Override + public Object remove(Object key) { + if (!(key instanceof String)) { + return null; + } + String stringKey = (String) key; + if (dynamicMembers.containsKey(stringKey)) { + return dynamicMembers.remove(stringKey); + } + if (sealedMembers.containsKey(stringKey)) { + return sealedMembers.remove(stringKey); + } + if (serializedMembers.containsKey(stringKey)) { + return serializedMembers.remove(stringKey); + } + return null; + } + + @Override + public void putAll(Map m) { + for (Map.Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + public void putAllDynamicMember(Map m) { + for (Map.Entry e : m.entrySet()) { + putDynamicMember(e.getKey(), e.getValue()); + } + } + + public void putAllSealedMember(Map m) { + for (Map.Entry e : m.entrySet()) { + putSealedMember(e.getKey(), e.getValue()); + } + } + + public void putAllSerializedMember(Map m) { + for (Map.Entry e : m.entrySet()) { + putSerializedMember(e.getKey(), e.getValue()); + } + } + + @Override + public void clear() { + clearDynamicMembers(); + clearSealedMembers(); + clearSerializedMembers(); + } + + public void clearDynamicMembers() { + dynamicMembers.clear(); + } + + public void clearSealedMembers() { + sealedMembers.clear(); + } + + public void clearSerializedMembers() { + serializedMembers.clear(); + } + + @Override + public Set keySet() { + Set ret = new ListSet<>(); + ret.addAll(sealedMembers.keySet()); + ret.addAll(dynamicMembers.keySet()); + ret.addAll(serializedMembers.keySet()); + return ret; + } + + public Set sealedMembersKeySet() { + return new ListSet<>(sealedMembers.keySet()); + } + + public Set dynamicMembersKeySet() { + return new ListSet<>(dynamicMembers.keySet()); + } + + public Set serializedMembersKeySet() { + return new ListSet<>(serializedMembers.keySet()); + } + + @Override + public Collection values() { + List values = new ArrayList<>(); + Set keys = keySet(); + for (String key : keys) { + if (dynamicMembers.containsKey(key)) { + values.add(dynamicMembers.get(key)); + } else if (sealedMembers.containsKey(key)) { + values.add(sealedMembers.get(key)); + } else { + values.add(serializedMembers.get(key)); + } + } + return values; + } + + @Override + public Set> entrySet() { + Set keys = keySet(); + Set> ret = new ListSet<>(); + for (String key : keys) { + ret.add(new ListMap.MyEntry<>(key, get(key))); + } + return ret; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java index b88876e27..8395e9c3d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java @@ -1,6 +1,5 @@ package com.jpexs.decompiler.flash.exporters.amf.amf3; -import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; @@ -153,13 +152,17 @@ public class Amf3Exporter { ret.append(indent(level + 1)).append("\"serialized\": \"").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append("\",").append(newLine); if (!ot.getSerializedMembers().isEmpty()) { ret.append(indent(level + 1)).append("\"unserializedMembers\": {").append(newLine); - for (int i = 0; i < ot.getSerializedMembers().size(); i++) { - Pair member = ot.getSerializedMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(indentStr, newLine, processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getSerializedMembers().size() - 1) { - ret.append(",").append(newLine); - } else { - ret.append(newLine); + { + int i = 0; + for (String key : ot.sealedMembersKeySet()) { + Object val = ot.getSealedMember(key); + ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 2, key, referenceCount, objectAlias)).append(":").append(amfToString(indentStr, newLine, processedObjects, level + 1, val, referenceCount, objectAlias)); + if (i < ot.serializedMembersSize() - 1) { + ret.append(",").append(newLine); + } else { + ret.append(newLine); + } + i++; } } ret.append(indent(level + 1)).append("}"); @@ -170,13 +173,17 @@ public class Amf3Exporter { ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",").append(newLine); //if (!ot.getSealedMembers().isEmpty()) { ret.append(indent(level + 1)).append("\"sealedMembers\": {").append(newLine); - for (int i = 0; i < ot.getSealedMembers().size(); i++) { - Pair member = ot.getSealedMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(indentStr, newLine, processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getSealedMembers().size() - 1) { - ret.append(",").append(newLine); - } else { - ret.append(newLine); + { + int i = 0; + for (String key : ot.sealedMembersKeySet()) { + Object val = ot.getSealedMember(key); + ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 2, key, referenceCount, objectAlias)).append(":").append(amfToString(indentStr, newLine, processedObjects, level + 1, val, referenceCount, objectAlias)); + if (i < ot.sealedMembersSize() - 1) { + ret.append(",").append(newLine); + } else { + ret.append(newLine); + } + i++; } } ret.append(indent(level + 1)).append("}"); @@ -187,15 +194,19 @@ public class Amf3Exporter { //} //if (!ot.getDynamicMembers().isEmpty()) { ret.append(indent(level + 1)).append("\"dynamicMembers\": {").append(newLine); - for (int i = 0; i < ot.getDynamicMembers().size(); i++) { - Pair member = ot.getDynamicMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)); - ret.append(":"); - ret.append(amfToString(indentStr, newLine, processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getDynamicMembers().size() - 1) { - ret.append(","); + { + int i = 0; + for (String key : ot.dynamicMembersKeySet()) { + Object val = ot.getDynamicMember(key); + ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 2, key, referenceCount, objectAlias)); + ret.append(":"); + ret.append(amfToString(indentStr, newLine, processedObjects, level + 2, val, referenceCount, objectAlias)); + if (i < ot.dynamicMembersSize() - 1) { + ret.append(","); + } + ret.append(newLine); + i++; } - ret.append(newLine); } ret.append(indent(level + 1)).append("}").append(newLine); //} @@ -219,13 +230,17 @@ public class Amf3Exporter { if (!at.getAssociativeValues().isEmpty()) { ret.append(newLine); } - for (int i = 0; i < at.getAssociativeValues().size(); i++) { - Pair p = at.getAssociativeValues().get(i); - ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 1, p.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(indentStr, newLine, processedObjects, level + 1, p.getSecond(), referenceCount, objectAlias)); - if (i < at.getAssociativeValues().size() - 1) { - ret.append(","); + { + int i = 0; + for (String key : at.associativeKeySet()) { + Object val = at.getAssociative(key); + ret.append(indent(level + 2)).append(amfToString(indentStr, newLine, processedObjects, level + 1, key, referenceCount, objectAlias)).append(" : ").append(amfToString(indentStr, newLine, processedObjects, level + 1, val, referenceCount, objectAlias)); + if (i < at.getAssociativeValues().size() - 1) { + ret.append(","); + } + ret.append(newLine); + i++; } - ret.append(newLine); } if (!at.getAssociativeValues().isEmpty()) { ret.append(indent(level + 1)); @@ -239,13 +254,17 @@ public class Amf3Exporter { ret.append(addId); ret.append(indent(level + 1)).append("\"weakKeys\": ").append(dt.hasWeakKeys()).append(",").append(newLine); ret.append(indent(level + 1)).append("\"entries\": {").append(newLine); - for (int i = 0; i < dt.getPairs().size(); i++) { - Pair pair = dt.getPairs().get(i); - ret.append(indent(level + 1)).append(amfToString(indentStr, newLine, processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(indentStr, newLine, processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias)); - if (i < dt.getPairs().size() - 1) { - ret.append(","); + { + int i = 0; + for (Object key : dt.keySet()) { + Object val = dt.get(key); + ret.append(indent(level + 1)).append(amfToString(indentStr, newLine, processedObjects, level + 1, key, referenceCount, objectAlias)).append(" : ").append(amfToString(indentStr, newLine, processedObjects, level + 1, val, referenceCount, objectAlias)); + if (i < dt.size() - 1) { + ret.append(","); + } + ret.append(newLine); + i++; } - ret.append(newLine); } ret.append(indent(level + 1)).append("}").append(newLine); ret.append(indent(level)).append("}"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java index e21bd342f..23d43bba5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.importers.amf.amf3; -import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.ListMap; import com.jpexs.decompiler.flash.amf.amf3.Traits; import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; public class Amf3Importer { @@ -111,17 +112,29 @@ public class Amf3Importer { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{"); - for (Pair p : values) { - sb.append(p.getFirst()).append(":").append("?").append(",\r\n"); + for (Object key : values.keySet()) { + sb.append(key).append(":").append("?").append(",\r\n"); } sb.append("}"); return sb.toString(); } - private final List> values = new ArrayList<>(); + private final Map values = new ListMap<>(); - public void add(Object key, Object value) { - values.add(new Pair<>(key, value)); + public Object remove(Object key) { + return values.remove(key); + } + + public Set keySet() { + return values.keySet(); + } + + public Object get(Object key) { + return values.get(key); + } + + public void put(Object key, Object value) { + values.put(key, value); } public String getString(Object key) throws Amf3ParseException { @@ -251,64 +264,35 @@ public class Amf3Importer { } public boolean containsKey(Object key) { - for (Pair p : values) { - if (p.getFirst().equals(key)) { - return true; - } - } - return false; - } - - public Object get(Object key) { - for (Pair p : values) { - if (p.getFirst().equals(key)) { - return p.getSecond(); - } - } - return null; + return values.containsKey(key); } public void resolve(Object key, Map objectTable, boolean allowTypedObject) throws Amf3ParseException { - for (Pair p : values) { - if (p.getFirst().equals(key)) { - Object resolved = resolveObjects(p.getSecond(), objectTable, allowTypedObject); - p.setSecond(resolved); - return; - } - } + Object val = values.get(key); + Object resolved = resolveObjects(val, objectTable, allowTypedObject); + values.put(key, resolved); } public List stringKeys() { List ret = new ArrayList<>(); - for (Pair p : values) { - if (p.getFirst() instanceof String) { - ret.add((String) p.getFirst()); + for (Object key : values.keySet()) { + if (key instanceof String) { + ret.add((String) key); } } return ret; } - public List> getAll() { + public Map getAll() { return values; } - public List> getStringMapped() { - List> ret = new ArrayList<>(); - for (Pair p : values) { - if (p.getFirst() instanceof String) { - String keyStr = (String) p.getFirst(); - ret.add(new Pair<>(keyStr, p.getSecond())); - } - } - return ret; - } - - public Map getStringMap() { - Map ret = new HashMap<>(); - for (Pair p : values) { - if (p.getFirst() instanceof String) { - String keyStr = (String) p.getFirst(); - ret.put(keyStr, values); + public Map getStringMapped() { + Map ret = new ListMap<>(); + for (Object key : values.keySet()) { + if (key instanceof String) { + String keyStr = (String) key; + ret.put(keyStr, values.get(key)); } } return ret; @@ -326,7 +310,7 @@ public class Amf3Importer { Object key = value(objectTable); expectedType(SymbolType.COLON); Object value = value(objectTable); - ret.add(key, value); + ret.put(key, value); if ("id".equals(key)) { if (!(value instanceof String)) { throw new Amf3ParseException("id must be string value", lexer.yyline()); @@ -381,9 +365,9 @@ public class Amf3Importer { typedObject.resolve("sealedMembers", objectTable, false); JsObject jsoSealed = typedObject.getJsObject("sealedMembers"); - List> sealedMembers = jsoSealed.getStringMapped(); + Map sealedMembers = jsoSealed.getStringMapped(); typedObject.resolve("dynamicMembers", objectTable, false); - List> dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped(); + Map dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped(); List sealedMemberNames = new ArrayList<>(jsoSealed.stringKeys()); resultObject = new ObjectType(new Traits(className, dynamic, sealedMemberNames), sealedMembers, dynamicMembers); @@ -394,7 +378,7 @@ public class Amf3Importer { List denseValues = typedObject.getJsArray("denseValues").getValues(); typedObject.resolve("associativeValues", objectTable, false); JsObject resolvedArr = typedObject.getJsObject("associativeValues"); - List> associativeValues = resolvedArr.getStringMapped(); + Map associativeValues = resolvedArr.getStringMapped(); resultObject = new ArrayType(denseValues, associativeValues); break; @@ -427,7 +411,7 @@ public class Amf3Importer { case "Dictionary": boolean weakKeys = typedObject.getBoolean("weakKeys"); typedObject.resolve("entries", objectTable, false); - List> entries = typedObject.getJsObject("entries").getAll(); + Map entries = typedObject.getJsObject("entries").getAll(); resultObject = new DictionaryType(weakKeys, entries); break; default: @@ -439,9 +423,12 @@ public class Amf3Importer { } } else { //not allowTypeObject JsObject jsObject = (JsObject) object; - for (Pair p : jsObject.getAll()) { - p.setFirst(resolveObjects(p.getFirst(), objectTable, true)); - p.setSecond(resolveObjects(p.getSecond(), objectTable, true)); + for (Object key : jsObject.keySet()) { + Object val = jsObject.get(key); + Object resKey = resolveObjects(key, objectTable, true); + Object resVal = resolveObjects(val, objectTable, true); + jsObject.remove(key); + jsObject.put(resKey, resVal); } resultObject = jsObject; } @@ -496,19 +483,23 @@ public class Amf3Importer { return objectsTable.get(key); } else if (object instanceof ObjectType) { ObjectType ot = (ObjectType) object; - for (Pair p : ot.getSealedMembers()) { - p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + for (String key : ot.sealedMembersKeySet()) { + ot.putSealedMember(key, replaceReferences(ot.getSealedMember(key), objectsTable)); } - for (Pair p : ot.getDynamicMembers()) { - p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + for (String key : ot.dynamicMembersKeySet()) { + ot.putDynamicMember(key, replaceReferences(ot.getDynamicMember(key), objectsTable)); } + for (String key : ot.serializedMembersKeySet()) { + ot.putSerializedMember(key, replaceReferences(ot.getSerializedMember(key), objectsTable)); + } + } else if (object instanceof ArrayType) { ArrayType at = (ArrayType) object; - for (Pair p : at.getAssociativeValues()) { - p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + for (String key : at.associativeKeySet()) { + at.putAssociative(key, replaceReferences(at.getAssociative(key), objectsTable)); } for (int i = 0; i < at.getDenseValues().size(); i++) { - at.getDenseValues().set(i, replaceReferences(at.getDenseValues().get(i), objectsTable)); + at.setDense(i, replaceReferences(at.getDense(i), objectsTable)); } } else if (object instanceof VectorObjectType) { VectorObjectType vot = (VectorObjectType) object; @@ -517,9 +508,12 @@ public class Amf3Importer { } } else if (object instanceof DictionaryType) { DictionaryType dt = (DictionaryType) object; - for (Pair p : dt.getPairs()) { - p.setFirst(replaceReferences(p.getFirst(), objectsTable)); - p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + for (Object key : dt.keySet()) { + Object val = dt.get(key); + Object newKey = replaceReferences(key, objectsTable); + Object newVal = replaceReferences(val, objectsTable); + dt.remove(key); + dt.put(newKey, newVal); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index dc7a5d623..5aa856b3e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -24,7 +24,6 @@ import com.jpexs.decompiler.flash.SWFCompression; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; -import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.MovieExporter; @@ -1199,9 +1198,8 @@ public class XFLConverter { if (metadataObject.isDynamic()) { writer.writeStartElement("persistentData"); List exportedNames = new ArrayList<>(); - for (Pair dynamicMember : metadataObject.getDynamicMembers()) { - String n = dynamicMember.getFirst(); - Object v = dynamicMember.getSecond(); + for (String n : metadataObject.dynamicMembersKeySet()) { + Object v = metadataObject.getDynamicMember(n); if (v instanceof Long) { exportedNames.add(n); writer.writeStartElement("PD"); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java index dd4bab2e9..246c78569 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java @@ -6,16 +6,10 @@ import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; import com.jpexs.helpers.Helper; import com.jpexs.helpers.MemoryInputStream; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import static org.testng.Assert.*; import org.testng.annotations.AfterTest; @@ -52,15 +46,15 @@ public class Amf3InputStreamTest { Map serializers = new HashMap<>(); serializers.put("CustomClass", new ObjectTypeSerializeHandler() { @Override - public List> readObject(String className, InputStream is) throws IOException { - List> members = new ArrayList<>(); - members.add(new Pair<>("val8", (long) is.read())); - members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + public Map readObject(String className, InputStream is) throws IOException { + Map members = new ListMap<>(); + members.put("val8", (long) is.read()); + members.put("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read()))); return members; } @Override - public void writeObject(List> members, OutputStream os) throws IOException { + public void writeObject(Map members, OutputStream os) throws IOException { throw new UnsupportedOperationException("Not implemented"); } }); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java index 49ecef29f..6075f03a5 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java @@ -24,18 +24,18 @@ public class Amf3OutputStreamTest { Map serializers = new HashMap<>(); serializers.put("CustomClass", new ObjectTypeSerializeHandler() { @Override - public List> readObject(String className, InputStream is) throws IOException { - List> members = new ArrayList<>(); - members.add(new Pair<>("val8", (long) is.read())); - members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + public Map readObject(String className, InputStream is) throws IOException { + Map members = new ListMap<>(); + members.put("val8", (long) is.read()); + members.put("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read()))); return members; } @Override - public void writeObject(List> members, OutputStream os) throws IOException { + public void writeObject(Map members, OutputStream os) throws IOException { Map memberMap = new HashMap<>(); - for (Pair m : members) { - memberMap.put(m.getFirst(), m.getSecond()); + for (String key : members.keySet()) { + memberMap.put(key, members.get(key)); } os.write((int) (long) (Long) memberMap.get("val8")); long val32 = (long) (Long) memberMap.get("val32"); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java index 5ac5fae8b..335dbbcd2 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java @@ -4,9 +4,9 @@ import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream; +import com.jpexs.decompiler.flash.amf.amf3.ListMap; import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.amf.amf3.ObjectTypeSerializeHandler; -import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import com.jpexs.helpers.MemoryInputStream; import java.io.File; @@ -34,18 +34,18 @@ public class Amf3ImporterTest { Map serializers = new HashMap<>(); serializers.put("CustomClass", new ObjectTypeSerializeHandler() { @Override - public List> readObject(String className, InputStream is) throws IOException { - List> members = new ArrayList<>(); - members.add(new Pair<>("val8", (long) is.read())); - members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + public Map readObject(String className, InputStream is) throws IOException { + Map members = new ListMap<>(); + members.put("val8", (long) is.read()); + members.put("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read()))); return members; } @Override - public void writeObject(List> members, OutputStream os) throws IOException { + public void writeObject(Map members, OutputStream os) throws IOException { Map memberMap = new HashMap<>(); - for (Pair m : members) { - memberMap.put(m.getFirst(), m.getSecond()); + for (String key : members.keySet()) { + memberMap.put(key, members.get(key)); } os.write((int) (long) (Long) memberMap.get("val8")); long val32 = (long) (Long) memberMap.get("val32");