Push it to gitea!
This commit is contained in:
commit
132f3c9d16
.gitignorepom.xml
src/main/java/com/alterdekim/flash/decompiler
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
57
pom.xml
Normal file
57
pom.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.alterdekim.game</groupId>
|
||||
<artifactId>actionScriptDecompiler</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<exec.mainClass>com.alterdekim.flash.decompiler.Main</exec.mainClass>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.jponge</groupId>
|
||||
<artifactId>lzma-java</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.26.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javatuples</groupId>
|
||||
<artifactId>javatuples</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.17.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,210 @@
|
||||
package com.alterdekim.flash.decompiler;
|
||||
|
||||
import com.alterdekim.flash.decompiler.action.*;
|
||||
import com.alterdekim.flash.decompiler.item.*;
|
||||
import com.alterdekim.flash.decompiler.tag.DoAction;
|
||||
import com.alterdekim.flash.decompiler.tag.ShockwaveTag;
|
||||
import com.alterdekim.flash.decompiler.tag.TagType;
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import com.alterdekim.flash.decompiler.util.Bits;
|
||||
import com.alterdekim.flash.decompiler.util.PayloadCompression;
|
||||
import com.alterdekim.flash.decompiler.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.javatuples.Triplet;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alterdekim.flash.decompiler.util.ByteUtil.bytesToInt;
|
||||
import static com.alterdekim.flash.decompiler.util.ByteUtil.bytesToIntRev;
|
||||
import static com.alterdekim.flash.decompiler.util.Compression.decompressDEFLATE;
|
||||
import static com.alterdekim.flash.decompiler.util.Compression.decompressLZMA;
|
||||
|
||||
@Slf4j
|
||||
public class FlashDecompiler {
|
||||
|
||||
private PayloadCompression compression;
|
||||
|
||||
public ShockwaveFile loadFromFile( File flashFile) throws IOException {
|
||||
return processSWF(Files.readAllBytes(flashFile.toPath()));
|
||||
}
|
||||
|
||||
private ShockwaveFile processSWF(byte[] swfBytes) throws IOException {
|
||||
compression = PayloadCompression.getByTag(swfBytes[0]);
|
||||
byte[] data = Arrays.copyOfRange(swfBytes, 8, swfBytes.length);
|
||||
switch (compression) {
|
||||
case DEFLATE:
|
||||
data = decompressDEFLATE(data);
|
||||
break;
|
||||
case LZMA:
|
||||
data = decompressLZMA(data);
|
||||
break;
|
||||
}
|
||||
return processPayload(data);
|
||||
}
|
||||
|
||||
private ShockwaveFile processPayload(byte[] payload) {
|
||||
BitSet bitSet = BitSet.valueOf(payload);
|
||||
int c = (payload.length * 8);
|
||||
int i = 0;
|
||||
int cnt = Bits.convert(bitSet.get(i, i+7), 7, false); // Rect x=[5] [x] [x] [x] [x]
|
||||
i += 7;
|
||||
i += cnt * 4;
|
||||
i = Bits.ceilToByte(i);
|
||||
float frameRate = Bits.convertP8(bitSet.get(i, i+16));
|
||||
i += 16;
|
||||
int frameCount = Bits.convert(bitSet.get(i, i+16), 16, true);
|
||||
i += 16;
|
||||
// 16-bit + 16-bit = 32 bits for each header tag.
|
||||
i = Bits.ceilToByte(i);
|
||||
List<ShockwaveTag> shockwaveTags = new ArrayList<>();
|
||||
while( i < c ) {
|
||||
BitSet tag = bitSet.get(i, i+16);
|
||||
int v = bytesToInt(new byte[] {(byte) Bits.convert(tag.get(8, 16), 8, true),
|
||||
(byte) Bits.convert(tag.get(0, 8), 8, true)});
|
||||
i += 16;
|
||||
int type = (v & 65472) >> 6; // type
|
||||
int len = v & 63; // length in bytes
|
||||
if( len == 63 ) {
|
||||
len = Bits.convert(bitSet.get(i, i+32), 32, true);
|
||||
i += 32;
|
||||
}
|
||||
TagType tagType = TagType.fromId(type);
|
||||
if( tagType == TagType.DoAction ) {
|
||||
int g = i / 8;
|
||||
shockwaveTags.add(processDoActionTag(Arrays.copyOfRange(payload, g, g+len)));
|
||||
} else {
|
||||
log.error("Unknown tag type: {}", type);
|
||||
}
|
||||
i += len * 8;
|
||||
}
|
||||
return new ShockwaveFile(frameRate, frameCount, shockwaveTags, compression);
|
||||
}
|
||||
|
||||
private DoAction processDoActionTag(byte[] data) {
|
||||
int i = 0;
|
||||
DoAction tag = new DoAction();
|
||||
tag.setActions(new ArrayList<>());
|
||||
while( i < data.length ) {
|
||||
switch(ActionField.fromId(data[i])) {
|
||||
case ConstantPool:
|
||||
i++;
|
||||
i = parseActionPool(data, i, tag);
|
||||
break;
|
||||
case Push:
|
||||
i = parseActionPush(data, i, tag);
|
||||
break;
|
||||
case InitObject:
|
||||
tag.getActions().add(new ActionInitObject());
|
||||
i++;
|
||||
break;
|
||||
case DefineLocal:
|
||||
i++;
|
||||
tag.getActions().add(new ActionDefineLocal());
|
||||
break;
|
||||
case SetMember:
|
||||
i++;
|
||||
tag.getActions().add(new ActionSetMember());
|
||||
break;
|
||||
case GetVariable:
|
||||
i++;
|
||||
tag.getActions().add(new ActionGetVariable());
|
||||
break;
|
||||
default:
|
||||
log.error("Unknown action field type: {}", StringUtils.bytesToHex(new byte[] {data[i]}));
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
private Triplet<byte[], Integer, Integer> findNull(byte[] data, int i, int u) {
|
||||
int s = i;
|
||||
while( data[i] != 0x00 ) {
|
||||
i++;
|
||||
u++;
|
||||
}
|
||||
return Triplet.with(Arrays.copyOfRange(data, s, i), i, u);
|
||||
}
|
||||
|
||||
private int parseActionPush(byte[] data, int i, DoAction tag) {
|
||||
i++;
|
||||
int len_in_bytes = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
|
||||
int u = 0;
|
||||
i += 2;
|
||||
int index;
|
||||
ActionPush push = new ActionPush();
|
||||
push.setItems(new ArrayList<>());
|
||||
while( u < len_in_bytes ) {
|
||||
ActionPushType type = ActionPushType.fromType(data[i]);
|
||||
switch(type) {
|
||||
case STRING:
|
||||
i++;
|
||||
var result = findNull(data, i, u);
|
||||
push.getItems().add(new StringItem(new String(result.getValue0())));
|
||||
i = result.getValue1();
|
||||
u = result.getValue2();
|
||||
break;
|
||||
case POOL_INDEX_BIG:
|
||||
i++;
|
||||
index = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
|
||||
push.getItems().add(new PoolIndexItem(index));
|
||||
i += 2;
|
||||
u += 3;
|
||||
continue;
|
||||
case POOL_INDEX:
|
||||
i++;
|
||||
index = bytesToIntRev(new byte[] {data[i]});
|
||||
push.getItems().add(new PoolIndexItem(index));
|
||||
i++;
|
||||
u+=2;
|
||||
continue;
|
||||
case INTEGER:
|
||||
i++;
|
||||
push.getItems().add(new IntegerItem(bytesToIntRev(Arrays.copyOfRange(data, i, i+4))));
|
||||
i += 4;
|
||||
u += 5;
|
||||
continue;
|
||||
case NULL:
|
||||
push.getItems().add(new NullItem());
|
||||
i++;
|
||||
u++;
|
||||
continue;
|
||||
default:
|
||||
i++;
|
||||
u++;
|
||||
log.info("parseActionPush(): Unknown action field: {}", data[i]);
|
||||
}
|
||||
}
|
||||
tag.getActions().add(push);
|
||||
return i;
|
||||
}
|
||||
|
||||
private int parseActionPool(byte[] data, int i, DoAction tag) {
|
||||
List<String> actionPool = new ArrayList<>();
|
||||
int bytes_len = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
|
||||
i += 2;
|
||||
int count = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
|
||||
i += 2;
|
||||
int u = 1;
|
||||
StringBuilder s = new StringBuilder();
|
||||
while( u <= count ) {
|
||||
if( data[i] == 0x00 ) {
|
||||
actionPool.add(s.toString());
|
||||
s = new StringBuilder();
|
||||
u++;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
s.append((char) data[i]);
|
||||
i++;
|
||||
}
|
||||
tag.setActionPool(actionPool);
|
||||
return i;
|
||||
}
|
||||
}
|
31
src/main/java/com/alterdekim/flash/decompiler/Main.java
Normal file
31
src/main/java/com/alterdekim/flash/decompiler/Main.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.alterdekim.flash.decompiler;
|
||||
|
||||
import com.alterdekim.flash.decompiler.tag.DoAction;
|
||||
import com.alterdekim.flash.decompiler.tag.TagType;
|
||||
import com.alterdekim.flash.decompiler.translator.Flash2Java;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Slf4j
|
||||
public class Main {
|
||||
public static void main(String[] args) throws Exception {
|
||||
FlashDecompiler decompiler = new FlashDecompiler();
|
||||
ShockwaveFile file = decompiler.loadFromFile(new File("catalogs.swf"));
|
||||
|
||||
DoAction da = (DoAction) file.getTags()
|
||||
.stream()
|
||||
.filter(p -> p.getType() == TagType.DoAction)
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
Flash2Java converter = new Flash2Java();
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
File out = new File("dec.json");
|
||||
if( out.exists() ) out.delete();
|
||||
objectMapper.writeValue(out, converter.convert(da));
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.alterdekim.flash.decompiler;
|
||||
|
||||
import com.alterdekim.flash.decompiler.tag.ShockwaveTag;
|
||||
import com.alterdekim.flash.decompiler.util.PayloadCompression;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ToString
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ShockwaveFile {
|
||||
private Float frameRate;
|
||||
private Integer frameCount;
|
||||
private List<ShockwaveTag> tags;
|
||||
private PayloadCompression compression;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.alterdekim.flash.decompiler.action;
|
||||
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class ActionDefineLocal extends ByteCodeAction {
|
||||
|
||||
@Override
|
||||
public ActionField getType() {
|
||||
return ActionField.DefineLocal;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
package com.alterdekim.flash.decompiler.action;
|
||||
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class ActionGetVariable extends ByteCodeAction {
|
||||
|
||||
@Override
|
||||
public ActionField getType() {
|
||||
return ActionField.GetVariable;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.alterdekim.flash.decompiler.action;
|
||||
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class ActionInitObject extends ByteCodeAction {
|
||||
|
||||
@Override
|
||||
public ActionField getType() {
|
||||
return ActionField.InitObject;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.alterdekim.flash.decompiler.action;
|
||||
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import com.alterdekim.flash.decompiler.item.ByteCodeItem;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class ActionPush extends ByteCodeAction {
|
||||
|
||||
private List<ByteCodeItem> items;
|
||||
|
||||
@Override
|
||||
public ActionField getType() {
|
||||
return ActionField.Push;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.alterdekim.flash.decompiler.action;
|
||||
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class ActionSetMember extends ByteCodeAction {
|
||||
|
||||
@Override
|
||||
public ActionField getType() {
|
||||
return ActionField.SetMember;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.alterdekim.flash.decompiler.action;
|
||||
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public abstract class ByteCodeAction {
|
||||
public abstract ActionField getType();
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.alterdekim.flash.decompiler.item;
|
||||
|
||||
public enum ActionPushType {
|
||||
STRING(0),
|
||||
FLOAT(1),
|
||||
NULL(2),
|
||||
UNDEFINED(3),
|
||||
REGISTER(4),
|
||||
BOOLEAN(5),
|
||||
DOUBLE(6),
|
||||
INTEGER(7),
|
||||
POOL_INDEX(8),
|
||||
POOL_INDEX_BIG(9);
|
||||
|
||||
public final int type;
|
||||
|
||||
ActionPushType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static ActionPushType fromType(int type) {
|
||||
for( ActionPushType pushType : ActionPushType.values()) {
|
||||
if( pushType.type == type ) return pushType;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.alterdekim.flash.decompiler.item;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public abstract class ByteCodeItem {
|
||||
public abstract ActionPushType getType();
|
||||
public abstract Object getValue();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.alterdekim.flash.decompiler.item;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@AllArgsConstructor
|
||||
public class IntegerItem extends ByteCodeItem {
|
||||
private Integer val;
|
||||
|
||||
@Override
|
||||
public ActionPushType getType() {
|
||||
return ActionPushType.INTEGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return val;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.alterdekim.flash.decompiler.item;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class NullItem extends ByteCodeItem {
|
||||
@Override
|
||||
public ActionPushType getType() {
|
||||
return ActionPushType.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.alterdekim.flash.decompiler.item;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@AllArgsConstructor
|
||||
public class PoolIndexItem extends ByteCodeItem {
|
||||
|
||||
private Integer val;
|
||||
|
||||
@Override
|
||||
public ActionPushType getType() {
|
||||
return ActionPushType.POOL_INDEX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return val;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.alterdekim.flash.decompiler.item;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@AllArgsConstructor
|
||||
public class StringItem extends ByteCodeItem {
|
||||
private String val;
|
||||
|
||||
@Override
|
||||
public ActionPushType getType() {
|
||||
return ActionPushType.STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.alterdekim.flash.decompiler.tag;
|
||||
|
||||
import com.alterdekim.flash.decompiler.action.ByteCodeAction;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Getter
|
||||
@Setter
|
||||
public class DoAction extends ShockwaveTag {
|
||||
|
||||
private List<String> actionPool;
|
||||
private List<ByteCodeAction> actions;
|
||||
|
||||
@Override
|
||||
public TagType getType() {
|
||||
return TagType.DoAction;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.alterdekim.flash.decompiler.tag;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public abstract class ShockwaveTag {
|
||||
public abstract TagType getType();
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.alterdekim.flash.decompiler.tag;
|
||||
|
||||
public enum TagType {
|
||||
SetBackgroundColor(9),
|
||||
DoAction(12),
|
||||
ShowFrame(1),
|
||||
End(0),
|
||||
Unknown(-1);
|
||||
|
||||
private final int id;
|
||||
|
||||
TagType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static TagType fromId(int id) {
|
||||
for( TagType type : TagType.values()) {
|
||||
if( type.id == id ) return type;
|
||||
}
|
||||
return Unknown;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package com.alterdekim.flash.decompiler.translator;
|
||||
|
||||
import com.alterdekim.flash.decompiler.action.ActionPush;
|
||||
import com.alterdekim.flash.decompiler.item.ActionPushType;
|
||||
import com.alterdekim.flash.decompiler.action.ByteCodeAction;
|
||||
import com.alterdekim.flash.decompiler.item.ByteCodeItem;
|
||||
import com.alterdekim.flash.decompiler.item.PoolIndexItem;
|
||||
import com.alterdekim.flash.decompiler.tag.DoAction;
|
||||
import com.alterdekim.flash.decompiler.util.ActionField;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
public class Flash2Java {
|
||||
|
||||
private List<String> pool;
|
||||
|
||||
public Map<String, FlashVar> convert(DoAction doAction) {
|
||||
this.pool = doAction.getActionPool();
|
||||
List<ByteCodeAction> actions = doAction.getActions();
|
||||
|
||||
List<ByteCodeAction> stack = new ArrayList<>();
|
||||
Map<String, FlashVar> vars = new HashMap<>();
|
||||
for (ByteCodeAction action : actions) {
|
||||
try {
|
||||
switch (action.getType()) {
|
||||
case DefineLocal:
|
||||
defineLocal(vars, stack);
|
||||
stack.clear();
|
||||
break;
|
||||
case GetVariable:
|
||||
getVariable(vars, stack);
|
||||
stack.clear();
|
||||
break;
|
||||
case SetMember:
|
||||
setMember(vars, stack);
|
||||
stack.clear();
|
||||
break;
|
||||
default:
|
||||
stack.add(action);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
private void setMember(Map<String, FlashVar> vars, List<ByteCodeAction> stack) {
|
||||
String varKey = vars.keySet().stream().filter(k -> vars.get(k).isActive()).findFirst().get();
|
||||
FlashVar varVal = vars.get(varKey);
|
||||
ActionPush push = (ActionPush) stack.get(0);
|
||||
if( stack.size() == 1 ) {
|
||||
Map<Integer, String> m = varVal.getVal() != null ? ((Map<Integer, String>) varVal.getVal()) : new HashMap<>();
|
||||
m.put((Integer) push.getItems().get(0).getValue(), fromPool((Integer) push.getItems().get(1).getValue()));
|
||||
varVal.setVal(m);
|
||||
vars.put(varKey, varVal);
|
||||
return;
|
||||
}
|
||||
Map<Integer, Map<String, Object>> m = varVal.getVal() != null ? ((Map<Integer, Map<String, Object>>) varVal.getVal()) : new HashMap<>();
|
||||
Map<String, Object> v = new HashMap<>();
|
||||
for( int i = 1; i < push.getItems().size()-1; i+=2 ) {
|
||||
v.put(fromPool((Integer) push.getItems().get(i).getValue()), valConverter(push.getItems().get(i+1)));
|
||||
}
|
||||
m.put((Integer) push.getItems().get(0).getValue(), v);
|
||||
varVal.setVal(m);
|
||||
vars.put(varKey, varVal);
|
||||
}
|
||||
|
||||
private Object valConverter(ByteCodeItem item) {
|
||||
if ( item.getType() == ActionPushType.POOL_INDEX ) {
|
||||
return fromPool( (Integer) item.getValue() );
|
||||
}
|
||||
return item.getValue();
|
||||
}
|
||||
|
||||
private void getVariable(Map<String, FlashVar> vars, List<ByteCodeAction> stack) throws Exception {
|
||||
if (stack.size() != 1 ) { throw new Exception("Bad getVariable format"); }
|
||||
ActionPush push = (ActionPush) stack.get(0);
|
||||
PoolIndexItem pi = (PoolIndexItem) push.getItems().get(0);
|
||||
vars.keySet().forEach(k -> {
|
||||
FlashVar fv = vars.get(k);
|
||||
fv.setActive(false);
|
||||
vars.put(k, fv);
|
||||
});
|
||||
String key = fromPool((int) pi.getValue());
|
||||
FlashVar v = vars.get(key);
|
||||
v.setActive(true);
|
||||
vars.put(key, v);
|
||||
}
|
||||
|
||||
private void defineLocal(Map<String, FlashVar> vars, List<ByteCodeAction> stack) throws Exception {
|
||||
if (stack.size() != 2 || stack.get(1).getType() != ActionField.InitObject) { throw new Exception("Bad defineLocal format"); }
|
||||
ActionPush push = (ActionPush) stack.get(0);
|
||||
PoolIndexItem pi = (PoolIndexItem) push.getItems().get(0);
|
||||
// todo: implement assignment ability
|
||||
vars.put(fromPool((Integer) pi.getValue()), new FlashVar());
|
||||
}
|
||||
|
||||
private String fromPool(int index) {
|
||||
return pool.get(index);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.alterdekim.flash.decompiler.translator;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FlashVar {
|
||||
private boolean isActive = false;
|
||||
private Object val;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.alterdekim.flash.decompiler.util;
|
||||
|
||||
public enum ActionField {
|
||||
ConstantPool((byte) 0x88),
|
||||
Push((byte) 0x96),
|
||||
InitObject((byte) 0x43),
|
||||
DefineLocal((byte) 0x3C),
|
||||
SetMember((byte)0x4F),
|
||||
GetVariable((byte) 0x1C),
|
||||
Unknown((byte) 0x00);
|
||||
|
||||
public final byte id;
|
||||
|
||||
ActionField(byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static ActionField fromId(int id) {
|
||||
for( ActionField type : ActionField.values()) {
|
||||
if( type.id == id ) return type;
|
||||
}
|
||||
return Unknown;
|
||||
}
|
||||
}
|
29
src/main/java/com/alterdekim/flash/decompiler/util/Bits.java
Normal file
29
src/main/java/com/alterdekim/flash/decompiler/util/Bits.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.alterdekim.flash.decompiler.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
@Slf4j
|
||||
public class Bits {
|
||||
|
||||
public static int convert(BitSet bits, int len, boolean swapEndian) {
|
||||
int n = 0;
|
||||
for(int i = 0; i < len; i++ ) {
|
||||
n += bits.get(i) ? 1 << (swapEndian ? i : (len-i-1)) : 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public static float convertP8(BitSet bits) {
|
||||
float n = convert(bits.get(8, 16), 8, true);
|
||||
for(int i = 0; i < 8; i++) {
|
||||
n += bits.get(i) ? 1f / (float) (1 << i) : 0f;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public static int ceilToByte(int n) {
|
||||
return (n % 8 == 0) ? n : n + (8 - (n % 8));
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.alterdekim.flash.decompiler.util;
|
||||
|
||||
public class ByteUtil {
|
||||
public static int bytesToInt(byte[] bytes) {
|
||||
int value = 0;
|
||||
for (byte b : bytes) {
|
||||
value = (value << 8) + (b & 0xFF);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int bytesToIntRev(byte[] bytes) {
|
||||
int value = 0;
|
||||
for(int i = bytes.length-1; i >= 0; i-- ) {
|
||||
value = (value << 8) + (bytes[i] & 0xFF);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.alterdekim.flash.decompiler.util;
|
||||
|
||||
import lzma.sdk.lzma.Decoder;
|
||||
import lzma.streams.LzmaInputStream;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public class Compression {
|
||||
public static byte[] decompressDEFLATE(byte[] data) throws IOException {
|
||||
return IOUtils.toByteArray(new InflaterInputStream(new ByteArrayInputStream(data)));
|
||||
}
|
||||
|
||||
public static byte[] decompressLZMA(byte[] data) throws IOException {
|
||||
try (LzmaInputStream inputStream = new LzmaInputStream(
|
||||
new BufferedInputStream(new ByteArrayInputStream(data)),
|
||||
new Decoder())) {
|
||||
return IOUtils.toByteArray(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.alterdekim.flash.decompiler.util;
|
||||
|
||||
public enum PayloadCompression {
|
||||
NONE((byte) 0x46),
|
||||
DEFLATE((byte) 0x43),
|
||||
LZMA((byte) 0x5a);
|
||||
|
||||
public final byte tag;
|
||||
|
||||
PayloadCompression(byte tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public static PayloadCompression getByTag(byte tag) {
|
||||
for( PayloadCompression compression : PayloadCompression.values() )
|
||||
if( compression.tag == tag ) return compression;
|
||||
return PayloadCompression.NONE;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.alterdekim.flash.decompiler.util;
|
||||
|
||||
public class StringUtils {
|
||||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user