mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-18 15:08:36 +00:00
480 lines
16 KiB
Java
480 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2010-2014 JPEXS
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.jpexs.decompiler.flash.abc;
|
|
|
|
import com.jpexs.decompiler.flash.abc.types.Decimal;
|
|
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
|
|
import com.jpexs.decompiler.flash.abc.types.MethodInfo;
|
|
import com.jpexs.decompiler.flash.abc.types.Multiname;
|
|
import com.jpexs.decompiler.flash.abc.types.Namespace;
|
|
import com.jpexs.decompiler.flash.abc.types.ValueKind;
|
|
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
|
|
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
|
|
import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction;
|
|
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
|
|
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
|
|
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
|
|
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
|
|
import com.jpexs.helpers.MemoryInputStream;
|
|
import com.jpexs.helpers.utf8.Utf8Helper;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class ABCInputStream {
|
|
|
|
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;
|
|
|
|
public void startBuffer() {
|
|
if (bufferOs == null) {
|
|
bufferOs = new ByteArrayOutputStream();
|
|
} else {
|
|
bufferOs.reset();
|
|
}
|
|
}
|
|
|
|
public byte[] stopBuffer() {
|
|
if (bufferOs == null) {
|
|
return new byte[0];
|
|
}
|
|
byte[] ret = bufferOs.toByteArray();
|
|
bufferOs.reset();
|
|
return ret;
|
|
}
|
|
|
|
public ABCInputStream(MemoryInputStream is) {
|
|
this.is = is;
|
|
}
|
|
|
|
/**
|
|
* Sets position in bytes in the stream
|
|
*
|
|
* @param pos Number of bytes
|
|
* @throws java.io.IOException
|
|
*/
|
|
public void seek(long pos) throws IOException {
|
|
is.seek(pos);
|
|
}
|
|
|
|
public DumpInfo newDumpLevel(String name, String type) {
|
|
if (dumpInfo != null) {
|
|
long startByte = is.getPos();
|
|
DumpInfo di = new DumpInfo(name, type, null, startByte, 0, 0, 0);
|
|
di.parent = dumpInfo;
|
|
dumpInfo.getChildInfos().add(di);
|
|
dumpInfo = di;
|
|
}
|
|
|
|
return dumpInfo;
|
|
}
|
|
|
|
public void endDumpLevel() {
|
|
endDumpLevel(null);
|
|
}
|
|
|
|
public void endDumpLevel(Object value) {
|
|
if (dumpInfo != null) {
|
|
dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte;
|
|
dumpInfo.previewValue = value;
|
|
dumpInfo = dumpInfo.parent;
|
|
}
|
|
}
|
|
|
|
private int readInternal() throws IOException {
|
|
bytesRead++;
|
|
int i = is.read();
|
|
if (DEBUG_READ) {
|
|
System.out.println("Read:0x" + Integer.toHexString(i));
|
|
}
|
|
if (bufferOs != null) {
|
|
if (i != -1) {
|
|
bufferOs.write(i);
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
public int read(String name) throws IOException {
|
|
newDumpLevel(name, "byte");
|
|
int ret = readInternal();
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
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);
|
|
sb.append('/');
|
|
sb.append(b.length);
|
|
sb.append("]: ");
|
|
for (int jj = 0; jj < currBytesRead; jj++) {
|
|
sb.append("0x");
|
|
sb.append(Integer.toHexString(b[jj]));
|
|
sb.append(' ');
|
|
}
|
|
System.out.println(sb.toString());
|
|
}
|
|
if (bufferOs != null) {
|
|
if (currBytesRead > 0) {
|
|
bufferOs.write(b, 0, currBytesRead);
|
|
}
|
|
}
|
|
return currBytesRead;
|
|
}
|
|
|
|
public int readU8(String name) throws IOException {
|
|
newDumpLevel(name, "U8");
|
|
int ret = readInternal();
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
private long readU32Internal() throws IOException {
|
|
int i;
|
|
long ret = 0;
|
|
int bytePos = 0;
|
|
int byteCount = 0;
|
|
boolean nextByte;
|
|
do {
|
|
i = readInternal();
|
|
nextByte = (i >> 7) == 1;
|
|
i &= 0x7f;
|
|
ret += (((long) i) << bytePos);
|
|
byteCount++;
|
|
bytePos += 7;
|
|
} while (nextByte);
|
|
return ret;
|
|
}
|
|
|
|
public long readU32(String name) throws IOException {
|
|
newDumpLevel(name, "U32");
|
|
long ret = readU32Internal();
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
private int readU30Internal() throws IOException {
|
|
return (int) readU32Internal();
|
|
}
|
|
|
|
public int readU30(String name) throws IOException {
|
|
newDumpLevel(name, "U30");
|
|
int ret = readU30Internal();
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
public int readS24(String name) throws IOException {
|
|
newDumpLevel(name, "S24");
|
|
int ret = (readInternal()) + (readInternal() << 8) + (readInternal() << 16);
|
|
|
|
if ((ret >> 23) == 1) {
|
|
ret |= 0xff000000;
|
|
}
|
|
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
public int readU16(String name) throws IOException {
|
|
newDumpLevel(name, "U16");
|
|
int ret = (readInternal()) + (readInternal() << 8);
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
public long readS32(String name) throws IOException {
|
|
int i;
|
|
long ret = 0;
|
|
int bytePos = 0;
|
|
int byteCount = 0;
|
|
boolean nextByte;
|
|
newDumpLevel(name, "S32");
|
|
do {
|
|
i = readInternal();
|
|
nextByte = (i >> 7) == 1;
|
|
i &= 0x7f;
|
|
ret += (i << bytePos);
|
|
byteCount++;
|
|
bytePos += 7;
|
|
if (bytePos == 35) {
|
|
if ((ret >> 31) == 1) {
|
|
ret = -(ret & 0x7fffffff);
|
|
}
|
|
break;
|
|
}
|
|
} while (nextByte);
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
public int available() throws IOException {
|
|
return is.available();
|
|
}
|
|
|
|
private final long readLong() throws IOException {
|
|
byte[] readBuffer = safeRead(8);
|
|
return (((long) readBuffer[7] << 56)
|
|
+ ((long) (readBuffer[6] & 255) << 48)
|
|
+ ((long) (readBuffer[5] & 255) << 40)
|
|
+ ((long) (readBuffer[4] & 255) << 32)
|
|
+ ((long) (readBuffer[3] & 255) << 24)
|
|
+ ((readBuffer[2] & 255) << 16)
|
|
+ ((readBuffer[1] & 255) << 8)
|
|
+ ((readBuffer[0] & 255)));
|
|
}
|
|
|
|
public double readDouble(String name) throws IOException {
|
|
newDumpLevel(name, "Double");
|
|
long el = readLong();
|
|
double ret = Double.longBitsToDouble(el);
|
|
endDumpLevel(ret);
|
|
return ret;
|
|
}
|
|
|
|
private byte[] safeRead(int count) throws IOException {
|
|
byte[] ret = new byte[count];
|
|
for (int i = 0; i < count; i++) {
|
|
ret[i] = (byte) readInternal();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public Namespace readNamespace(String name) throws IOException {
|
|
newDumpLevel(name, "Namespace");
|
|
int kind = read("kind");
|
|
int name_index = 0;
|
|
for (int k = 0; k < Namespace.nameSpaceKinds.length; k++) {
|
|
if (Namespace.nameSpaceKinds[k] == kind) {
|
|
name_index = readU30("name_index");
|
|
break;
|
|
}
|
|
}
|
|
endDumpLevel();
|
|
return new Namespace(kind, name_index);
|
|
}
|
|
|
|
public Multiname readMultiname(String name) throws IOException {
|
|
int kind = readU8("kind");
|
|
int namespace_index = 0;
|
|
int name_index = 0;
|
|
int namespace_set_index = 0;
|
|
int qname_index = 0;
|
|
List<Integer> params = new ArrayList<>();
|
|
|
|
newDumpLevel(name, "Multiname");
|
|
if ((kind == Multiname.QNAME) || (kind == Multiname.QNAMEA)) {
|
|
namespace_index = readU30("namespace_index");
|
|
name_index = readU30("name_index");
|
|
} else if ((kind == Multiname.RTQNAME) || (kind == Multiname.RTQNAMEA)) {
|
|
name_index = readU30("name_index");
|
|
} else if ((kind == Multiname.RTQNAMEL) || (kind == Multiname.RTQNAMELA)) {
|
|
|
|
} else if ((kind == Multiname.MULTINAME) || (kind == Multiname.MULTINAMEA)) {
|
|
name_index = readU30("name_index");
|
|
namespace_set_index = readU30("namespace_set_index");
|
|
} else if ((kind == Multiname.MULTINAMEL) || (kind == Multiname.MULTINAMELA)) {
|
|
namespace_set_index = readU30("namespace_set_index");
|
|
} else if (kind == Multiname.TYPENAME) {
|
|
qname_index = readU30("qname_index"); //Multiname index!!!
|
|
int paramsLength = readU30("paramsLength");
|
|
for (int i = 0; i < paramsLength; i++) {
|
|
params.add(readU30("param")); //multiname indices!
|
|
}
|
|
} else {
|
|
throw new IOException("Unknown kind of Multiname:0x" + Integer.toHexString(kind));
|
|
}
|
|
|
|
endDumpLevel();
|
|
return new Multiname(kind, name_index, namespace_index, namespace_set_index, qname_index, params);
|
|
}
|
|
|
|
public MethodInfo readMethodInfo(String name) throws IOException {
|
|
newDumpLevel(name, "method_info");
|
|
int param_count = readU30("param_count");
|
|
int ret_type = readU30("ret_type");
|
|
int[] param_types = new int[param_count];
|
|
for (int i = 0; i < param_count; i++) {
|
|
param_types[i] = readU30("param_type");
|
|
}
|
|
int name_index = readU30("name_index");
|
|
int flags = readInternal();
|
|
|
|
//// 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional (16=ignore_rest, 32=explicit,) 64=setsdxns, 128=has_paramnames
|
|
ValueKind[] optional = new ValueKind[0];
|
|
if ((flags & 8) == 8) { //if has_optional
|
|
int optional_count = readU30("optional_count");
|
|
optional = new ValueKind[optional_count];
|
|
for (int i = 0; i < optional_count; i++) {
|
|
optional[i] = new ValueKind(readU30("value_index"), read("value_kind"));
|
|
}
|
|
}
|
|
|
|
int[] param_names = new int[param_count];
|
|
if ((flags & 128) == 128) { //if has_paramnames
|
|
for (int i = 0; i < param_count; i++) {
|
|
param_names[i] = readU30("param_name");
|
|
}
|
|
}
|
|
|
|
endDumpLevel();
|
|
return new MethodInfo(param_types, ret_type, name_index, flags, optional, param_names);
|
|
}
|
|
|
|
public Trait readTrait(String name) throws IOException {
|
|
newDumpLevel(name, "Trait");
|
|
long pos = getPosition();
|
|
startBuffer();
|
|
int name_index = readU30("name_index");
|
|
int kind = readInternal();
|
|
int kindType = 0xf & kind;
|
|
int kindFlags = kind >> 4;
|
|
Trait trait;
|
|
|
|
switch (kindType) {
|
|
case 0: //slot
|
|
case 6: //const
|
|
TraitSlotConst t1 = new TraitSlotConst();
|
|
t1.slot_id = readU30("slot_id");
|
|
t1.type_index = readU30("type_index");
|
|
t1.value_index = readU30("value_index");
|
|
if (t1.value_index != 0) {
|
|
t1.value_kind = readInternal();
|
|
}
|
|
trait = t1;
|
|
break;
|
|
case 1: //method
|
|
case 2: //getter
|
|
case 3: //setter
|
|
TraitMethodGetterSetter t2 = new TraitMethodGetterSetter();
|
|
t2.disp_id = readU30("disp_id");
|
|
t2.method_info = readU30("method_info");
|
|
trait = t2;
|
|
break;
|
|
case 4: //class
|
|
TraitClass t3 = new TraitClass();
|
|
t3.slot_id = readU30("slot_id");
|
|
t3.class_info = readU30("class_info");
|
|
trait = t3;
|
|
break;
|
|
case 5: //function
|
|
TraitFunction t4 = new TraitFunction();
|
|
t4.slot_id = readU30("slot_id");
|
|
t4.method_info = readU30("method_info");
|
|
trait = t4;
|
|
break;
|
|
default:
|
|
throw new IOException("Unknown trait kind:" + kind);
|
|
}
|
|
trait.fileOffset = pos;
|
|
trait.kindType = kindType;
|
|
trait.kindFlags = kindFlags;
|
|
trait.name_index = name_index;
|
|
if ((kindFlags & ATTR_METADATA) != 0) {
|
|
int metadata_count = readU30("metadata_count");
|
|
trait.metadata = new int[metadata_count];
|
|
for (int i = 0; i < metadata_count; i++) {
|
|
trait.metadata[i] = readU30("metadata");
|
|
}
|
|
}
|
|
trait.bytes = stopBuffer();
|
|
endDumpLevel();
|
|
return trait;
|
|
}
|
|
|
|
public Traits readTraits(String name) throws IOException {
|
|
newDumpLevel(name, "Traits");
|
|
int count = readU30("count");
|
|
Traits traits = new Traits();
|
|
traits.traits = new ArrayList<>();
|
|
for (int i = 0; i < count; i++) {
|
|
traits.traits.add(readTrait("trait"));
|
|
}
|
|
endDumpLevel();
|
|
return traits;
|
|
}
|
|
|
|
public byte[] readBytesInternal(int count) throws IOException {
|
|
byte[] ret = new byte[count];
|
|
for (int i = 0; i < count; i++) {
|
|
ret[i] = (byte) readInternal();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public byte[] readBytes(int count, String name) throws IOException {
|
|
newDumpLevel(name, "Bytes");
|
|
byte[] ret = readBytesInternal(count);
|
|
endDumpLevel();
|
|
return ret;
|
|
}
|
|
|
|
public Decimal readDecimal(String name) throws IOException {
|
|
newDumpLevel(name, "Decimal");
|
|
byte[] data = readBytesInternal(16);
|
|
endDumpLevel();
|
|
return new Decimal(data);
|
|
}
|
|
|
|
public InstanceInfo readInstanceInfo(String name) throws IOException {
|
|
newDumpLevel(name, "instance_info");
|
|
InstanceInfo ret = new InstanceInfo();
|
|
ret.name_index = readU30("name_index");
|
|
ret.super_index = readU30("super_index");
|
|
ret.flags = readInternal();
|
|
if ((ret.flags & CLASS_PROTECTED_NS) != 0) {
|
|
ret.protectedNS = readU30("protectedNS");
|
|
}
|
|
int interfaces_count = readU30("interfaces_count");
|
|
ret.interfaces = new int[interfaces_count];
|
|
for (int i = 0; i < interfaces_count; i++) {
|
|
ret.interfaces[i] = readU30("interface");
|
|
}
|
|
ret.iinit_index = readU30("iinit_index");
|
|
ret.instance_traits = readTraits("instance_traits");
|
|
endDumpLevel();
|
|
return ret;
|
|
}
|
|
|
|
public String readString(String name) throws IOException {
|
|
newDumpLevel(name, "String");
|
|
int length = readU30Internal();
|
|
byte[] b = safeRead(length);
|
|
String r = new String(b, Utf8Helper.charset);
|
|
endDumpLevel(r);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*public void markStart(){
|
|
bytesRead=0;
|
|
}*/
|
|
public long getPosition() {
|
|
return bytesRead;
|
|
}
|
|
}
|