Push it to gitea!
This commit is contained in:
commit
132f3c9d16
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