AMF optimization: Pair type eliminated, ListSet and ListMap introduces (Set/Map which maintains order of keys)

This commit is contained in:
Jindra Petřík
2016-07-24 08:07:49 +02:00
parent 12c7d79046
commit 4750b50b17
16 changed files with 804 additions and 270 deletions

View File

@@ -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<Pair<String, Object>> assocPart = new ArrayList<>();
Map<String, Object> assocPart = new ListMap<>();
List<Object> 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<Pair<String, Object>> serMembers = serializers.get(className).readObject(className, mis);
Map<String, Object> 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<Pair<String, Object>> sealedMembers = new ArrayList<>();
List<Pair<String, Object>> dynamicMembers = new ArrayList<>();
Map<String, Object> sealedMembers = new ListMap<>();
Map<String, Object> 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<Object> 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<String> 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<Pair<Object, Object>> data = new ArrayList<>();
DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data);
Map<Object, Object> 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;
}

View File

@@ -175,9 +175,9 @@ public class Amf3OutputStream extends OutputStream {
objectTable.add(val);
writeU29((val.getDenseValues().size() << 1) | NO_REFERENCE_FLAG);
for (Pair<String, Object> 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<String, Object> v : val.getSealedMembers()) {
writeUtf8Vr(v.getFirst(), stringTable);
for (String key : val.sealedMembersKeySet()) {
writeUtf8Vr(key, stringTable);
}
}
} else {
writeU29((traitsIndex << 2) | NO_REFERENCE_FLAG);
}
for (Pair<String, Object> 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<String, Object> 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<Object, Object> 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);

View File

@@ -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 <K> Key type
* @param <V> Value type
*/
public class ListMap<K, V> implements Map<K, V> {
private final Set<K> orderedKeys = new ListSet<>();
private final Map<K, V> 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<? extends K, ? extends V> 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<? extends K, ? extends V> 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<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
orderedKeys.add(e.getKey());
}
map.putAll(m);
}
@Override
public void clear() {
orderedKeys.clear();
map.clear();
}
@Override
public Set<K> keySet() {
return new ListSet<>(orderedKeys);
}
@Override
public Collection<V> values() {
List<V> vals = new ArrayList<>();
for (K key : orderedKeys) {
vals.add(map.get(key));
}
return vals;
}
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> ret = new ListSet<>();
for (K key : orderedKeys) {
V value = map.get(key);
ret.add(new MyEntry<>(key, value));
}
return ret;
}
public static class MyEntry<K, V> implements Entry<K, V> {
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;
}
}
}

View File

@@ -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 <E>
*/
public class ListSet<E> implements Set<E> {
private final List<E> list = new ArrayList<>();
public ListSet() {
}
public ListSet(Collection<? extends E> 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<E> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <E> 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<? extends E> 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();
}
}

View File

@@ -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<Pair<String, Object>> readObject(String className, InputStream is) throws IOException;
public Map<String, Object> readObject(String className, InputStream is) throws IOException;
public void writeObject(List<Pair<String, Object>> members, OutputStream os) throws IOException;
public void writeObject(Map<String, Object> members, OutputStream os) throws IOException;
}

View File

@@ -1,28 +0,0 @@
package com.jpexs.decompiler.flash.amf.amf3;
public class Pair<T1, T2> {
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;
}
}

View File

@@ -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<String> sealedMemberNames;
private Set<String> sealedMemberNames;
public Traits(String className, boolean dynamic, List<String> sealedMemberNames) {
public Traits(String className, boolean dynamic, Collection<? extends String> 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<String> getSealedMemberNames() {
return sealedMemberNames;
public Set<String> getSealedMemberNames() {
return new ListSet<>(sealedMemberNames);
}
public void setClassName(String className) {
@@ -34,8 +35,8 @@ public class Traits {
this.dynamic = dynamic;
}
public void setSealedMemberNames(List<String> sealedMemberNames) {
this.sealedMemberNames = sealedMemberNames;
public void setSealedMemberNames(Collection<? extends String> sealedMemberNames) {
this.sealedMemberNames = new ListSet<>(sealedMemberNames);
}
}

View File

@@ -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<Object> denseValues;
private List<Pair<String, Object>> associativeValues;
private Map<String, Object> associativeValues;
public ArrayType(List<Object> denseValues, List<Pair<String, Object>> associativeValues) {
this.denseValues = denseValues;
this.associativeValues = associativeValues;
public ArrayType(Map<? extends String, ? extends Object> associativeValues) {
this(new ArrayList<>(), associativeValues);
}
public ArrayType(List<Object> denseValues) {
this(denseValues, new HashMap<>());
}
public ArrayType() {
this(new ArrayList<>(), new HashMap<>());
}
public ArrayType(List<Object> denseValues, Map<? extends String, ? extends Object> associativeValues) {
this.denseValues = new ArrayList<>(denseValues);
this.associativeValues = new ListMap<>(associativeValues);
}
public List<Object> getDenseValues() {
return denseValues;
return new ArrayList<>(denseValues);
}
public List<Pair<String, Object>> getAssociativeValues() {
return associativeValues;
public void setDenseValues(List<Object> 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<String, Object> 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<String> associativeKeySet() {
return associativeValues.keySet();
}
@Override
@@ -33,14 +74,14 @@ public class ArrayType implements WithSubValues, Amf3ValueType {
@Override
public List<Object> getSubValues() {
List<Object> ret = new ArrayList<>();
for (Pair<String, Object> 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<String, Object> associativeValues) {
this.associativeValues = new ListMap<>(associativeValues);
}
}

View File

@@ -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<Object, Object> implements WithSubValues, Amf3ValueType {
private boolean weakKeys;
private List<Pair<Object, Object>> pairs;
private final boolean weakKeys;
public DictionaryType(boolean weakKeys, List<Pair<Object, Object>> pairs) {
this.weakKeys = weakKeys;
this.pairs = pairs;
public DictionaryType(boolean weakKeys) {
this(weakKeys, new HashMap<>());
}
public List<Pair<Object, Object>> getPairs() {
return pairs;
public DictionaryType(boolean weakKeys, Map<Object, Object> 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<Object> getSubValues() {
List<Object> ret = new ArrayList<>();
for (Pair<Object, Object> p : pairs) {
ret.add(p.getFirst());
ret.add(p.getSecond());
}
ret.addAll(keySet());
ret.addAll(values());
return ret;
}

View File

@@ -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<String, Object> {
private List<Pair<String, Object>> sealedMembers;
private List<Pair<String, Object>> dynamicMembers;
private List<Pair<String, Object>> serializedMembers;
private Map<String, Object> sealedMembers;
private Map<String, Object> dynamicMembers;
private Map<String, Object> 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<Pair<String, Object>> serializedMembers) {
public ObjectType(Traits traits, byte[] serializedData, Map<String, Object> 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<Pair<String, Object>> sealedMembers, List<Pair<String, Object>> 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<String, Object> sealedMembers, Map<String, Object> 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<Pair<String, Object>> getDynamicMembers() {
return dynamicMembers;
}
public List<Pair<String, Object>> getSealedMembers() {
return sealedMembers;
}
public String getClassName() {
return traits.getClassName();
}
@@ -62,20 +62,14 @@ public class ObjectType implements WithSubValues, Amf3ValueType {
@Override
public List<Object> getSubValues() {
List<Object> ret = new ArrayList<>();
for (Pair<String, Object> p : dynamicMembers) {
ret.add(p.getFirst());
ret.add(p.getSecond());
}
for (Pair<String, Object> p : sealedMembers) {
ret.add(p.getFirst());
ret.add(p.getSecond());
}
ret.addAll(dynamicMembers.keySet());
ret.addAll(dynamicMembers.values());
for (Pair<String, Object> 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<Pair<String, Object>> serializedMembers) {
this.serializedMembers = serializedMembers;
public void setSerializedMembers(Map<String, Object> serializedMembers) {
this.serializedMembers = new ListMap<>(serializedMembers);
}
public List<Pair<String, Object>> getSerializedMembers() {
return serializedMembers;
public Map<String, Object> getSerializedMembers() {
return new ListMap<>(serializedMembers);
}
public void setSealedMembers(Map<String, Object> sealedMembers) {
this.sealedMembers = new ListMap<>(sealedMembers);
}
public void setDynamicMembers(Map<String, Object> 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<? extends String, ? extends Object> m) {
for (Map.Entry<? extends String, ? extends Object> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
public void putAllDynamicMember(Map<? extends String, ? extends Object> m) {
for (Map.Entry<? extends String, ? extends Object> e : m.entrySet()) {
putDynamicMember(e.getKey(), e.getValue());
}
}
public void putAllSealedMember(Map<? extends String, ? extends Object> m) {
for (Map.Entry<? extends String, ? extends Object> e : m.entrySet()) {
putSealedMember(e.getKey(), e.getValue());
}
}
public void putAllSerializedMember(Map<? extends String, ? extends Object> m) {
for (Map.Entry<? extends String, ? extends Object> 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<String> keySet() {
Set<String> ret = new ListSet<>();
ret.addAll(sealedMembers.keySet());
ret.addAll(dynamicMembers.keySet());
ret.addAll(serializedMembers.keySet());
return ret;
}
public Set<String> sealedMembersKeySet() {
return new ListSet<>(sealedMembers.keySet());
}
public Set<String> dynamicMembersKeySet() {
return new ListSet<>(dynamicMembers.keySet());
}
public Set<String> serializedMembersKeySet() {
return new ListSet<>(serializedMembers.keySet());
}
@Override
public Collection<Object> values() {
List<Object> values = new ArrayList<>();
Set<String> 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<Entry<String, Object>> entrySet() {
Set<String> keys = keySet();
Set<Entry<String, Object>> ret = new ListSet<>();
for (String key : keys) {
ret.add(new ListMap.MyEntry<>(key, get(key)));
}
return ret;
}
}

View File

@@ -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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<Object, Object> 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("}");

View File

@@ -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<Object, Object> 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<Pair<Object, Object>> values = new ArrayList<>();
private final Map<Object, Object> 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<Object> 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<Object, Object> p : values) {
if (p.getFirst().equals(key)) {
return true;
}
}
return false;
}
public Object get(Object key) {
for (Pair<Object, Object> p : values) {
if (p.getFirst().equals(key)) {
return p.getSecond();
}
}
return null;
return values.containsKey(key);
}
public void resolve(Object key, Map<String, Object> objectTable, boolean allowTypedObject) throws Amf3ParseException {
for (Pair<Object, Object> 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<String> stringKeys() {
List<String> ret = new ArrayList<>();
for (Pair<Object, Object> 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<Pair<Object, Object>> getAll() {
public Map<Object, Object> getAll() {
return values;
}
public List<Pair<String, Object>> getStringMapped() {
List<Pair<String, Object>> ret = new ArrayList<>();
for (Pair<Object, Object> p : values) {
if (p.getFirst() instanceof String) {
String keyStr = (String) p.getFirst();
ret.add(new Pair<>(keyStr, p.getSecond()));
}
}
return ret;
}
public Map<String, Object> getStringMap() {
Map<String, Object> ret = new HashMap<>();
for (Pair<Object, Object> p : values) {
if (p.getFirst() instanceof String) {
String keyStr = (String) p.getFirst();
ret.put(keyStr, values);
public Map<String, Object> getStringMapped() {
Map<String, Object> 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<Pair<String, Object>> sealedMembers = jsoSealed.getStringMapped();
Map<String, Object> sealedMembers = jsoSealed.getStringMapped();
typedObject.resolve("dynamicMembers", objectTable, false);
List<Pair<String, Object>> dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped();
Map<String, Object> dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped();
List<String> sealedMemberNames = new ArrayList<>(jsoSealed.stringKeys());
resultObject = new ObjectType(new Traits(className, dynamic, sealedMemberNames), sealedMembers, dynamicMembers);
@@ -394,7 +378,7 @@ public class Amf3Importer {
List<Object> denseValues = typedObject.getJsArray("denseValues").getValues();
typedObject.resolve("associativeValues", objectTable, false);
JsObject resolvedArr = typedObject.getJsObject("associativeValues");
List<Pair<String, Object>> associativeValues = resolvedArr.getStringMapped();
Map<String, Object> 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<Pair<Object, Object>> entries = typedObject.getJsObject("entries").getAll();
Map<Object, Object> 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<Object, Object> 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<String, Object> p : ot.getSealedMembers()) {
p.setSecond(replaceReferences(p.getSecond(), objectsTable));
for (String key : ot.sealedMembersKeySet()) {
ot.putSealedMember(key, replaceReferences(ot.getSealedMember(key), objectsTable));
}
for (Pair<String, Object> 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<String, Object> 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<Object, Object> 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);
}
}

View File

@@ -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<String> exportedNames = new ArrayList<>();
for (Pair<String, Object> 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");