mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-26 04:16:09 +00:00
AMF0 EcmaArray has dense and associative parts
This commit is contained in:
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException;
|
||||
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
|
||||
import com.jpexs.decompiler.flash.ecma.EcmaScript;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf0.Amf0Exporter;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.MemoryInputStream;
|
||||
import java.io.DataInputStream;
|
||||
@@ -37,6 +38,7 @@ import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -322,93 +324,118 @@ public class Amf0InputStream extends InputStream {
|
||||
*/
|
||||
public Object readValue(String name) throws IOException, NoSerializerExistsException {
|
||||
newDumpLevel(name, "value-type");
|
||||
try {
|
||||
int marker = readInternal();
|
||||
switch (marker) {
|
||||
case Marker.NUMBER:
|
||||
return readDouble("DOUBLE");
|
||||
case Marker.BOOLEAN:
|
||||
return readU8("U8") > 0;
|
||||
case Marker.STRING:
|
||||
return readUtf8("UTF-8");
|
||||
case Marker.OBJECT_END:
|
||||
return BasicType.OBJECT_END;
|
||||
case Marker.OBJECT:
|
||||
ObjectType object = new ObjectType();
|
||||
String propName;
|
||||
Object val;
|
||||
Object result = null;
|
||||
int marker = readInternal();
|
||||
System.err.println("marker " + Integer.toHexString(marker));
|
||||
switch (marker) {
|
||||
case Marker.NUMBER:
|
||||
result = readDouble("DOUBLE");
|
||||
break;
|
||||
case Marker.BOOLEAN:
|
||||
result = readU8("U8") > 0;
|
||||
break;
|
||||
case Marker.STRING:
|
||||
result = readUtf8("UTF-8");
|
||||
break;
|
||||
case Marker.OBJECT_END:
|
||||
result = BasicType.OBJECT_END;
|
||||
break;
|
||||
case Marker.OBJECT:
|
||||
ObjectType object = new ObjectType();
|
||||
String propName;
|
||||
Object val;
|
||||
|
||||
while (true) {
|
||||
propName = readUtf8("propertyName");
|
||||
val = readValue("propertyValue");
|
||||
if (propName.equals("")) {
|
||||
break;
|
||||
}
|
||||
object.properties.put(propName, val);
|
||||
}
|
||||
return object;
|
||||
case Marker.MOVIECLIP:
|
||||
throw new IllegalArgumentException("MovieClip not supported in AMF0");
|
||||
case Marker.NULL:
|
||||
return BasicType.NULL;
|
||||
case Marker.UNDEFINED:
|
||||
return BasicType.UNDEFINED;
|
||||
case Marker.REFERENCE:
|
||||
return new ReferenceType(readU16("referenceIndex"));
|
||||
case Marker.ECMA_ARRAY:
|
||||
int associativeCount = (int) readU32("associative-count");
|
||||
EcmaArrayType ea = new EcmaArrayType();
|
||||
for (int a = 0; a < associativeCount; a++) {
|
||||
String eaKey = readUtf8("key");
|
||||
Object eaVal = readValue("value");
|
||||
ea.values.put(eaKey, eaVal);
|
||||
while (true) {
|
||||
propName = readUtf8("propertyName");
|
||||
val = readValue("propertyValue");
|
||||
if (propName.equals("")) {
|
||||
break;
|
||||
}
|
||||
readUtf8("UTF-8-empty");
|
||||
readValue("object-end");
|
||||
|
||||
return ea;
|
||||
case Marker.STRICT_ARRAY:
|
||||
int arrayCount = (int) readU32("array-count");
|
||||
ArrayType at = new ArrayType();
|
||||
for (int a = 0; a < arrayCount; a++) {
|
||||
at.values.add(readValue("value"));
|
||||
object.properties.put(propName, val);
|
||||
}
|
||||
result = object;
|
||||
break;
|
||||
case Marker.MOVIECLIP:
|
||||
throw new IllegalArgumentException("MovieClip not supported in AMF0");
|
||||
case Marker.NULL:
|
||||
result = BasicType.NULL;
|
||||
break;
|
||||
case Marker.UNDEFINED:
|
||||
result = BasicType.UNDEFINED;
|
||||
break;
|
||||
case Marker.REFERENCE:
|
||||
result = new ReferenceType(readU16("referenceIndex"));
|
||||
break;
|
||||
case Marker.ECMA_ARRAY:
|
||||
int associativeCount = (int) readU32("associative-count");
|
||||
System.err.println("associativeCount = " + associativeCount);
|
||||
EcmaArrayType ea = new EcmaArrayType();
|
||||
for (int a = 0; a < associativeCount; a++) {
|
||||
String eaKey = readUtf8("key");
|
||||
Object eaVal = readValue("value");
|
||||
ea.denseValues.put(eaKey, eaVal);
|
||||
}
|
||||
while (true) {
|
||||
String eaKey = readUtf8("key");
|
||||
Object eaVal = readValue("value");
|
||||
if ("".equals(eaKey)) {
|
||||
break;
|
||||
}
|
||||
return at;
|
||||
case Marker.DATE:
|
||||
double dval = readDouble("epoch-millis");
|
||||
int timezone = readS16("time-zone");
|
||||
return new DateType(dval, timezone);
|
||||
case Marker.LONG_STRING:
|
||||
return readUtf8Long("long-string");
|
||||
case Marker.UNSUPPORTED:
|
||||
throw new IllegalArgumentException("Unsupported type");
|
||||
case Marker.RECORDSET:
|
||||
throw new IllegalArgumentException("RecordSet not supported in AMF0");
|
||||
case Marker.XML_DOCUMENT:
|
||||
return new XmlDocumentType(readUtf8Long("xml"));
|
||||
case Marker.TYPED_OBJECT:
|
||||
String className = readUtf8("class-name");
|
||||
TypedObjectType typedObject = new TypedObjectType();
|
||||
typedObject.className = className;
|
||||
ea.associativeValues.put(eaKey, eaVal);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
propName = readUtf8("propertyName");
|
||||
val = readValue("propertyValue");
|
||||
if (propName.equals("")) {
|
||||
break;
|
||||
}
|
||||
typedObject.properties.put(propName, val);
|
||||
result = ea;
|
||||
break;
|
||||
case Marker.STRICT_ARRAY:
|
||||
int arrayCount = (int) readU32("array-count");
|
||||
ArrayType at = new ArrayType();
|
||||
for (int a = 0; a < arrayCount; a++) {
|
||||
at.values.add(readValue("value"));
|
||||
}
|
||||
result = at;
|
||||
break;
|
||||
case Marker.DATE:
|
||||
double dval = readDouble("epoch-millis");
|
||||
int timezone = readS16("time-zone");
|
||||
result = new DateType(dval, timezone);
|
||||
break;
|
||||
case Marker.LONG_STRING:
|
||||
result = readUtf8Long("long-string");
|
||||
break;
|
||||
case Marker.UNSUPPORTED:
|
||||
throw new IllegalArgumentException("Unsupported type");
|
||||
case Marker.RECORDSET:
|
||||
throw new IllegalArgumentException("RecordSet not supported in AMF0");
|
||||
case Marker.XML_DOCUMENT:
|
||||
return new XmlDocumentType(readUtf8Long("xml"));
|
||||
case Marker.TYPED_OBJECT:
|
||||
String className = readUtf8("class-name");
|
||||
TypedObjectType typedObject = new TypedObjectType();
|
||||
typedObject.className = className;
|
||||
|
||||
while (true) {
|
||||
propName = readUtf8("propertyName");
|
||||
val = readValue("propertyValue");
|
||||
if (propName.equals("")) {
|
||||
break;
|
||||
}
|
||||
return typedObject;
|
||||
case Marker.AVMPLUS_OBJECT:
|
||||
Amf3InputStream amf3 = new Amf3InputStream(is);
|
||||
return amf3.readValue("avm-plus-object");
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported type");
|
||||
}
|
||||
} finally {
|
||||
endDumpLevel();
|
||||
typedObject.properties.put(propName, val);
|
||||
}
|
||||
result = typedObject;
|
||||
break;
|
||||
case Marker.AVMPLUS_OBJECT:
|
||||
Amf3InputStream amf3 = new Amf3InputStream(is);
|
||||
result = amf3.readValue("avm-plus-object");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported type");
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
System.err.println("Read: " + Amf0Exporter.amfToString(result, 0, "\r\n", new ArrayList<>(), new HashMap<>(), new HashMap<>()));
|
||||
}
|
||||
endDumpLevel();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void resolveMapReferences(Map<String, Object> map) {
|
||||
@@ -439,8 +466,11 @@ public class Amf0InputStream extends InputStream {
|
||||
}
|
||||
if (value instanceof EcmaArrayType) {
|
||||
EcmaArrayType eat = (EcmaArrayType) value;
|
||||
for (String key : eat.values.keySet()) {
|
||||
eat.values.put(key, resolveReferences(eat.values.get(key), complexObjects));
|
||||
for (String key : eat.denseValues.keySet()) {
|
||||
eat.denseValues.put(key, resolveReferences(eat.denseValues.get(key), complexObjects));
|
||||
}
|
||||
for (String key : eat.associativeValues.keySet()) {
|
||||
eat.associativeValues.put(key, resolveReferences(eat.associativeValues.get(key), complexObjects));
|
||||
}
|
||||
}
|
||||
if (value instanceof ArrayType) {
|
||||
|
||||
@@ -209,9 +209,12 @@ public class Amf0OutputStream extends OutputStream {
|
||||
} else if (value instanceof EcmaArrayType) {
|
||||
write(Marker.ECMA_ARRAY);
|
||||
EcmaArrayType ea = (EcmaArrayType) value;
|
||||
writeU32(ea.values.size());
|
||||
for (String key : ea.values.keySet()) {
|
||||
writeObjectProperty(key, ea.values.get(key), complexObjectsList);
|
||||
writeU32(ea.denseValues.size());
|
||||
for (String key : ea.denseValues.keySet()) {
|
||||
writeObjectProperty(key, ea.denseValues.get(key), complexObjectsList);
|
||||
}
|
||||
for (String key : ea.associativeValues.keySet()) {
|
||||
writeObjectProperty(key, ea.associativeValues.get(key), complexObjectsList);
|
||||
}
|
||||
writeUtf8Empty();
|
||||
write(Marker.OBJECT_END);
|
||||
|
||||
@@ -26,7 +26,8 @@ import java.util.Map;
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class EcmaArrayType implements ComplexObject {
|
||||
public Map<String, Object> values = new LinkedHashMap<>();
|
||||
public Map<String, Object> denseValues = new LinkedHashMap<>();
|
||||
public Map<String, Object> associativeValues = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -35,6 +36,9 @@ public class EcmaArrayType implements ComplexObject {
|
||||
|
||||
@Override
|
||||
public List<Object> getSubValues() {
|
||||
return new ArrayList<>(values.values());
|
||||
List<Object> result = new ArrayList<>();
|
||||
result.addAll(denseValues.values());
|
||||
result.addAll(associativeValues.values());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,8 @@ public class Amf0Exporter {
|
||||
sb.append("{").append(newLine);
|
||||
sb.append(indent(level + 1)).append("\"type\": \"Object\",").append(newLine);
|
||||
sb.append(addId);
|
||||
membersToString(sb, ot.properties, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
membersToString("members", sb, ot.properties, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
sb.append(newLine);
|
||||
sb.append(indent(level)).append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -139,7 +140,10 @@ public class Amf0Exporter {
|
||||
sb.append("{").append(newLine);
|
||||
sb.append(indent(level + 1)).append("\"type\": \"EcmaArray\",").append(newLine);
|
||||
sb.append(addId);
|
||||
membersToString(sb, eat.values, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
membersToString("denseValues", sb, eat.denseValues, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
sb.append(",").append(newLine);
|
||||
membersToString("associativeValues", sb, eat.associativeValues, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
sb.append(newLine);
|
||||
sb.append(indent(level)).append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -172,7 +176,8 @@ public class Amf0Exporter {
|
||||
sb.append(indent(level + 1)).append("\"type\": \"TypedObject\",").append(newLine);
|
||||
sb.append(addId);
|
||||
sb.append(indent(level + 1)).append("\"className\": \"").append(Helper.escapeActionScriptString(tot.className)).append("\",").append(newLine);
|
||||
membersToString(sb, tot.properties, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
membersToString("members", sb, tot.properties, level + 1, newLine, processedObjects, referenceCount, objectAlias);
|
||||
sb.append(newLine);
|
||||
sb.append(indent(level)).append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -209,6 +214,7 @@ public class Amf0Exporter {
|
||||
}
|
||||
|
||||
private static void membersToString(
|
||||
String membersLabel,
|
||||
StringBuilder sb,
|
||||
Map<String, Object> members,
|
||||
int level,
|
||||
@@ -216,7 +222,7 @@ public class Amf0Exporter {
|
||||
List<Object> processedObjects,
|
||||
Map<Object, Integer> referenceCount,
|
||||
Map<Object, String> objectAlias) {
|
||||
sb.append(indent(level)).append("\"members\": {").append(newLine);
|
||||
sb.append(indent(level)).append("\"").append(membersLabel).append("\": {").append(newLine);
|
||||
boolean first = true;
|
||||
for (String key : members.keySet()) {
|
||||
if (!first) {
|
||||
@@ -227,7 +233,7 @@ public class Amf0Exporter {
|
||||
sb.append(amfToString(members.get(key), level + 1, newLine, processedObjects, referenceCount, objectAlias));
|
||||
}
|
||||
sb.append(newLine);
|
||||
sb.append(indent(level)).append("}").append(newLine);
|
||||
sb.append(indent(level)).append("}");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -395,7 +395,8 @@ public class Amf0Importer {
|
||||
case "EcmaArray":
|
||||
EcmaArrayType eat = new EcmaArrayType();
|
||||
typedObject.resolve("members", objectTable, false);
|
||||
eat.values = typedObject.getJsObject("members").getStringMapped();
|
||||
eat.denseValues = typedObject.getJsObject("denseValues").getStringMapped();
|
||||
eat.associativeValues = typedObject.getJsObject("associativeValues").getStringMapped();
|
||||
resultObject = eat;
|
||||
break;
|
||||
case "Array":
|
||||
@@ -510,8 +511,11 @@ public class Amf0Importer {
|
||||
}
|
||||
} else if (object instanceof EcmaArrayType) {
|
||||
EcmaArrayType eat = (EcmaArrayType) object;
|
||||
for (String key : eat.values.keySet()) {
|
||||
eat.values.put(key, replaceReferences(eat.values.get(key), objectsTable));
|
||||
for (String key : eat.denseValues.keySet()) {
|
||||
eat.denseValues.put(key, replaceReferences(eat.denseValues.get(key), objectsTable));
|
||||
}
|
||||
for (String key : eat.associativeValues.keySet()) {
|
||||
eat.associativeValues.put(key, replaceReferences(eat.associativeValues.get(key), objectsTable));
|
||||
}
|
||||
} else if (object instanceof ArrayType) {
|
||||
ArrayType at = (ArrayType) object;
|
||||
|
||||
@@ -81,7 +81,7 @@ public class LsoTag extends Tag {
|
||||
while (ais.available() > 0) {
|
||||
String varName = ais.readUtf8("varName");
|
||||
try {
|
||||
Object varValue = ais.readValue("varValue");
|
||||
Object varValue = ais.readValue("varValue");
|
||||
amfValues.put(varName, varValue);
|
||||
} catch (NoSerializerExistsException ex) {
|
||||
throw new IllegalArgumentException("Serializer for class " + ex.getClassName() + " not found");
|
||||
|
||||
Reference in New Issue
Block a user