mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-09 03:53:11 +00:00
AVM2 instruction reading fix, strongly typed Cache key
This commit is contained in:
@@ -2203,7 +2203,7 @@ public final class SWF implements TreeItem, Timelined {
|
||||
mat.translateX, mat.translateY);
|
||||
}
|
||||
|
||||
private static Cache<SerializableImage> frameCache = Cache.getInstance(false);
|
||||
private static Cache<String, SerializableImage> frameCache = Cache.getInstance(false);
|
||||
|
||||
public static SerializableImage getFromCache(String key) {
|
||||
if (frameCache.contains(key)) {
|
||||
|
||||
@@ -42,7 +42,6 @@ public class ABCInputStream implements AutoCloseable {
|
||||
private static final int CLASS_PROTECTED_NS = 8;
|
||||
private static final int ATTR_METADATA = 4;
|
||||
private final MemoryInputStream is;
|
||||
private long bytesRead = 0;
|
||||
private ByteArrayOutputStream bufferOs = null;
|
||||
public static final boolean DEBUG_READ = false;
|
||||
public DumpInfo dumpInfo;
|
||||
@@ -102,8 +101,15 @@ public class ABCInputStream implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public void endDumpLevelUntil(DumpInfo di) {
|
||||
if (di != null) {
|
||||
while (dumpInfo != null && dumpInfo != di) {
|
||||
endDumpLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int readInternal() throws IOException {
|
||||
bytesRead++;
|
||||
int i = is.read();
|
||||
if (i == -1) {
|
||||
throw new EndOfStreamException();
|
||||
@@ -128,7 +134,6 @@ public class ABCInputStream implements AutoCloseable {
|
||||
|
||||
private int read(byte[] b) throws IOException {
|
||||
int currBytesRead = is.read(b);
|
||||
bytesRead += currBytesRead;
|
||||
if (DEBUG_READ) {
|
||||
StringBuilder sb = new StringBuilder("Read[");
|
||||
sb.append(currBytesRead);
|
||||
@@ -478,7 +483,7 @@ public class ABCInputStream implements AutoCloseable {
|
||||
bytesRead=0;
|
||||
}*/
|
||||
public long getPosition() {
|
||||
return bytesRead;
|
||||
return is.getPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -243,6 +243,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -784,59 +785,77 @@ public class AVM2Code implements Cloneable {
|
||||
}
|
||||
|
||||
public AVM2Code(ABCInputStream ais) throws IOException {
|
||||
try {
|
||||
while (ais.available() > 0) {
|
||||
DumpInfo di = ais.newDumpLevel("instruction", "instruction");
|
||||
long startOffset = ais.getPosition();
|
||||
int instructionCode = ais.read("instructionCode");
|
||||
InstructionDefinition instr = instructionSetByCode[instructionCode];
|
||||
if (di != null) {
|
||||
di.name = instr.instructionName;
|
||||
}
|
||||
if (instr != null) {
|
||||
int[] actualOperands = null;
|
||||
if (instructionCode == 0x1b) { //switch
|
||||
int firstOperand = ais.readS24("default_offset");
|
||||
int case_count = ais.readU30("case_count");
|
||||
actualOperands = new int[case_count + 3];
|
||||
actualOperands[0] = firstOperand;
|
||||
actualOperands[1] = case_count;
|
||||
for (int c = 0; c < case_count + 1; c++) {
|
||||
actualOperands[2 + c] = ais.readS24("actualOperand");
|
||||
}
|
||||
} else {
|
||||
if (instr.operands.length > 0) {
|
||||
actualOperands = new int[instr.operands.length];
|
||||
for (int op = 0; op < instr.operands.length; op++) {
|
||||
switch (instr.operands[op] & 0xff00) {
|
||||
case OPT_U30:
|
||||
actualOperands[op] = ais.readU30("operand");
|
||||
break;
|
||||
case OPT_U8:
|
||||
actualOperands[op] = ais.read("operand");
|
||||
break;
|
||||
case OPT_BYTE:
|
||||
actualOperands[op] = (byte) ais.read("operand");
|
||||
break;
|
||||
case OPT_S24:
|
||||
actualOperands[op] = ais.readS24("operand");
|
||||
break;
|
||||
Map<Long, AVM2Instruction> codeMap = new TreeMap<>();
|
||||
DumpInfo diParent = ais.dumpInfo;
|
||||
List<Long> addresses = new ArrayList<>();
|
||||
addresses.add(ais.getPosition());
|
||||
while (!addresses.isEmpty()) {
|
||||
long address = addresses.remove(0);
|
||||
if (codeMap.containsKey(address)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ais.seek(address);
|
||||
while (ais.available() > 0) {
|
||||
DumpInfo di = ais.newDumpLevel("instruction", "instruction");
|
||||
long startOffset = ais.getPosition();
|
||||
int instructionCode = ais.read("instructionCode");
|
||||
InstructionDefinition instr = instructionSetByCode[instructionCode];
|
||||
if (di != null) {
|
||||
di.name = instr.instructionName;
|
||||
}
|
||||
if (instr != null) {
|
||||
int[] actualOperands = null;
|
||||
if (instructionCode == 0x1b) { //switch
|
||||
int firstOperand = ais.readS24("default_offset");
|
||||
int case_count = ais.readU30("case_count");
|
||||
actualOperands = new int[case_count + 3];
|
||||
actualOperands[0] = firstOperand;
|
||||
actualOperands[1] = case_count;
|
||||
for (int c = 0; c < case_count + 1; c++) {
|
||||
actualOperands[2 + c] = ais.readS24("actualOperand");
|
||||
}
|
||||
} else {
|
||||
if (instr.operands.length > 0) {
|
||||
actualOperands = new int[instr.operands.length];
|
||||
for (int op = 0; op < instr.operands.length; op++) {
|
||||
switch (instr.operands[op] & 0xff00) {
|
||||
case OPT_U30:
|
||||
actualOperands[op] = ais.readU30("operand");
|
||||
break;
|
||||
case OPT_U8:
|
||||
actualOperands[op] = ais.read("operand");
|
||||
break;
|
||||
case OPT_BYTE:
|
||||
actualOperands[op] = (byte) ais.read("operand");
|
||||
break;
|
||||
case OPT_S24:
|
||||
actualOperands[op] = ais.readS24("operand");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code.add(new AVM2Instruction(startOffset, instr, actualOperands));
|
||||
ais.endDumpLevel(instr.instructionCode);
|
||||
} else {
|
||||
ais.endDumpLevel();
|
||||
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
|
||||
//throw new UnknownInstructionCode(instructionCode);
|
||||
if (instr instanceof IfTypeIns) {
|
||||
long target = ais.getPosition() + actualOperands[0];
|
||||
addresses.add(target);
|
||||
}
|
||||
codeMap.put(startOffset, new AVM2Instruction(startOffset, instr, actualOperands));
|
||||
ais.endDumpLevel(instr.instructionCode);
|
||||
} else {
|
||||
ais.endDumpLevel();
|
||||
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
|
||||
//throw new UnknownInstructionCode(instructionCode);
|
||||
}
|
||||
}
|
||||
} catch (EndOfStreamException ex) {
|
||||
// lookupswitch obfuscation, ignore
|
||||
ais.endDumpLevelUntil(diParent);
|
||||
}
|
||||
} catch (EndOfStreamException ex) {
|
||||
// lookupswitch obfuscation, ignore
|
||||
}
|
||||
|
||||
code.addAll(codeMap.values());
|
||||
}
|
||||
|
||||
public void compact() {
|
||||
@@ -1065,7 +1084,6 @@ public class AVM2Code implements Cloneable {
|
||||
}
|
||||
}
|
||||
}
|
||||
long ofs = 0;
|
||||
int ip = 0;
|
||||
int largeLimit = 20000;
|
||||
boolean markOffsets = code.size() <= largeLimit;
|
||||
@@ -1074,6 +1092,7 @@ public class AVM2Code implements Cloneable {
|
||||
Helper.byteArrayToHexWithHeader(writer, getBytes());
|
||||
} else {
|
||||
for (AVM2Instruction ins : code) {
|
||||
long ofs = ins.offset;
|
||||
if (exportMode == ScriptExportMode.PCODE_HEX) {
|
||||
writer.appendNoHilight("; ");
|
||||
writer.appendNoHilight(Helper.bytesToHexString(ins.getBytes()));
|
||||
@@ -1144,7 +1163,6 @@ public class AVM2Code implements Cloneable {
|
||||
outputMap.add(ip);
|
||||
}
|
||||
}
|
||||
ofs += ins.getBytes().length;
|
||||
ip++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.jpexs.decompiler.flash.abc.types.NamespaceSet;
|
||||
import com.jpexs.helpers.utf8.Utf8PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class AVM2ConstantPool {
|
||||
|
||||
@@ -117,35 +119,75 @@ public class AVM2ConstantPool {
|
||||
}
|
||||
|
||||
public long getInt(int index) {
|
||||
return constant_int.get(index);
|
||||
try {
|
||||
return constant_int.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Namespace getNamespace(int index) {
|
||||
return constant_namespace.get(index);
|
||||
try {
|
||||
return constant_namespace.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public NamespaceSet getNamespaceSet(int index) {
|
||||
return constant_namespace_set.get(index);
|
||||
try {
|
||||
return constant_namespace_set.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Multiname getMultiname(int index) {
|
||||
return constant_multiname.get(index);
|
||||
try {
|
||||
return constant_multiname.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getUInt(int index) {
|
||||
return constant_uint.get(index);
|
||||
try {
|
||||
return constant_uint.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double getDouble(int index) {
|
||||
return constant_double.get(index);
|
||||
try {
|
||||
return constant_double.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Decimal getDecimal(int index) {
|
||||
return constant_decimal.get(index);
|
||||
try {
|
||||
return constant_decimal.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getString(int index) {
|
||||
return constant_string.get(index);
|
||||
try {
|
||||
return constant_string.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
Logger.getLogger(AVM2ConstantPool.class.getName()).log(Level.SEVERE, "Multiname not found. Index: " + index, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getIntCount() {
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns;
|
||||
import com.jpexs.decompiler.flash.abc.types.Multiname;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.graph.GraphSource;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
@@ -173,7 +174,12 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem {
|
||||
s.append(" null");
|
||||
} else {
|
||||
s.append(" ");
|
||||
s.append(constants.getMultiname(operands[i]).toString(constants, fullyQualifiedNames));
|
||||
Multiname multiname = constants.getMultiname(operands[i]);
|
||||
if (multiname != null) {
|
||||
s.append(multiname.toString(constants, fullyQualifiedNames));
|
||||
} else {
|
||||
s.append("Multiname not found.");
|
||||
}
|
||||
}
|
||||
/*s.append(" m[");
|
||||
s.append(operands[i]);
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.jpexs.decompiler.flash.abc.RenameType;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
|
||||
import com.jpexs.helpers.Cache;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -254,7 +253,7 @@ public class Deobfuscation {
|
||||
return "\u00A7" + escapeOIdentifier(s) + "\u00A7";
|
||||
}
|
||||
|
||||
private static final Cache<String> nameCache = Cache.getInstance(false);
|
||||
private static final Map<String, String> nameCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Ensures identifier is valid and if not, uses paragraph syntax
|
||||
@@ -268,7 +267,7 @@ public class Deobfuscation {
|
||||
if (s.startsWith("\u00A7") && s.endsWith("\u00A7")) { //Assuming already printed - TODO:detect better
|
||||
return s;
|
||||
}
|
||||
if (nameCache.contains(s)) {
|
||||
if (nameCache.containsKey(s)) {
|
||||
return nameCache.get(s);
|
||||
}
|
||||
if (isValidName(s, validExceptions)) {
|
||||
@@ -281,7 +280,7 @@ public class Deobfuscation {
|
||||
}
|
||||
|
||||
public static String printNamespace(String pkg, String... validNameExceptions) {
|
||||
if (nameCache.contains(pkg)) {
|
||||
if (nameCache.containsKey(pkg)) {
|
||||
return nameCache.get(pkg);
|
||||
}
|
||||
if (pkg.isEmpty()) {
|
||||
|
||||
@@ -195,7 +195,7 @@ public class DefineButton2Tag extends ButtonTag implements Container {
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static final Cache<RECT> rectCache = Cache.getInstance(true);
|
||||
private static final Cache<DefineButton2Tag, RECT> rectCache = Cache.getInstance(true);
|
||||
|
||||
@Override
|
||||
public RECT getRect(Set<BoundedTag> added) {
|
||||
|
||||
@@ -234,7 +234,7 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static final Cache<RECT> rectCache = Cache.getInstance(true);
|
||||
private static final Cache<DefineButtonTag, RECT> rectCache = Cache.getInstance(true);
|
||||
|
||||
@Override
|
||||
public RECT getRect(Set<BoundedTag> added) {
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.tags;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
@@ -125,7 +126,7 @@ public class DefineSpriteTag extends CharacterTag implements Container, Drawable
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private static final Cache<DefineSpriteTag, RECT> rectCache = Cache.getInstance(true);
|
||||
|
||||
@Override
|
||||
public RECT getRect(Set<BoundedTag> added) {
|
||||
|
||||
@@ -34,18 +34,19 @@ import java.util.logging.Logger;
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
* @param <E>
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public class Cache<E> {
|
||||
public class Cache<K, V> {
|
||||
|
||||
private final Map<Object, File> cacheFiles;
|
||||
private final Map<Object, E> cacheMemory;
|
||||
private final Map<K, File> cacheFiles;
|
||||
private final Map<K, V> cacheMemory;
|
||||
private static final List<Cache> instances = new ArrayList<>();
|
||||
public static final int STORAGE_FILES = 1;
|
||||
public static final int STORAGE_MEMORY = 2;
|
||||
|
||||
public static <E> Cache<E> getInstance(boolean weak) {
|
||||
Cache<E> instance = new Cache<>(weak);
|
||||
public static <K, V> Cache<K, V> getInstance(boolean weak) {
|
||||
Cache<K, V> instance = new Cache<>(weak);
|
||||
instances.add(instance);
|
||||
return instance;
|
||||
}
|
||||
@@ -89,7 +90,7 @@ public class Cache<E> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(Object key) {
|
||||
public boolean contains(K key) {
|
||||
if (storageType == STORAGE_FILES) {
|
||||
return cacheFiles.containsKey(key);
|
||||
} else if (storageType == STORAGE_MEMORY) {
|
||||
@@ -106,7 +107,7 @@ public class Cache<E> {
|
||||
cacheFiles.clear();
|
||||
}
|
||||
|
||||
public void remove(Object key) {
|
||||
public void remove(K key) {
|
||||
if (storageType == STORAGE_FILES) {
|
||||
if (cacheFiles.containsKey(key)) {
|
||||
File f = cacheFiles.get(key);
|
||||
@@ -121,7 +122,7 @@ public class Cache<E> {
|
||||
|
||||
}
|
||||
|
||||
public E get(Object key) {
|
||||
public V get(K key) {
|
||||
if (storageType == STORAGE_FILES) {
|
||||
if (!cacheFiles.containsKey(key)) {
|
||||
return null;
|
||||
@@ -130,7 +131,7 @@ public class Cache<E> {
|
||||
try (FileInputStream fis = new FileInputStream(f)) {
|
||||
ObjectInputStream ois = new ObjectInputStream(fis);
|
||||
@SuppressWarnings("unchecked")
|
||||
E item = (E) ois.readObject();
|
||||
V item = (V) ois.readObject();
|
||||
return item;
|
||||
} catch (IOException | ClassNotFoundException ex) {
|
||||
Logger.getLogger(Cache.class.getName()).log(Level.SEVERE, null, ex);
|
||||
@@ -145,7 +146,7 @@ public class Cache<E> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void put(Object key, E value) {
|
||||
public void put(K key, V value) {
|
||||
if (storageType == STORAGE_FILES) {
|
||||
File temp = null;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user