diff --git a/pom.xml b/pom.xml
index d7ea83a..b449be9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,12 +6,36 @@
com.alterdekim.game
actionScriptDecompiler
- 1.0-SNAPSHOT
+ 0.0.2
jar
+ SWFDissect
+
+
+ This project is a library for swf editing in Java.
+ DON'T USE THIS IN PRODUCTION. IT WAS CREATED FOR INTERNAL PURPOSES ONLY.
+
+ https://blog.awain.net
+
+
+
+ Michael Wain
+ alterwain@protonmail.com
+ The God
+ https://blog.awain.net
+
+
+
+
+
+ The Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
- 11
- 11
+ 14
+ 14
UTF-8
com.alterdekim.flash.decompiler.Main
@@ -54,15 +78,35 @@
2.17.2
+
- org.apache.maven.plugins
+ maven-clean-plugin
+
+
+ maven-resources-plugin
+
+
maven-compiler-plugin
-
- 14
- 14
-
+
+
+ maven-surefire-plugin
+
+
+ maven-jar-plugin
+
+
+ maven-install-plugin
+
+
+ maven-deploy-plugin
+
+
+ maven-site-plugin
+
+
+ maven-project-info-reports-plugin
diff --git a/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java b/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java
index 7123b1a..af817fa 100644
--- a/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java
+++ b/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java
@@ -5,14 +5,12 @@ 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 com.alterdekim.flash.decompiler.util.*;
import lombok.extern.slf4j.Slf4j;
import org.javatuples.Triplet;
import java.io.*;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
@@ -48,6 +46,21 @@ public class FlashDecompiler {
return processPayload(data);
}
+ public byte[] uncompressSWF(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;
+ }
+ byte[] o = new byte[8 + data.length];
+ byte[] b = Arrays.copyOfRange(swfBytes, 0, 8);
+ System.arraycopy(b, 0, o, 0, 8);
+ System.arraycopy(data, 0, o, 8, data.length);
+ return o;
+ }
+
private ShockwaveFile processPayload(byte[] payload) {
BitSet bitSet = BitSet.valueOf(payload);
int c = (payload.length * 8);
@@ -74,6 +87,7 @@ public class FlashDecompiler {
len = Bits.convert(bitSet.get(i, i+32), 32, true);
i += 32;
}
+ log.info("Len: {}", len);
TagType tagType = TagType.fromId(type);
if( tagType == TagType.DoAction ) {
int g = i / 8;
@@ -131,7 +145,13 @@ public class FlashDecompiler {
i++;
tag.getActions().add(new ActionGetMember());
break;
+ case Add:
+ log.info("Add");
+ i++;
+ tag.getActions().add(new ActionAdd());
+ break;
case Unknown:
+ log.info("Unknown tag has been hit: {} / {}", i, tag.getActions().get(tag.getActions().size()-1));
return tag;
default:
log.error("Unknown action field type: {}", StringUtils.bytesToHex(new byte[] {data[i]}));
@@ -139,20 +159,22 @@ public class FlashDecompiler {
break;
}
}
+ log.info("Out of loop");
return tag;
}
private Triplet findNull(byte[] data, int i, int u) {
int s = i;
- while( data[i] != 0x00 ) {
+ while(data[i] != 0x00 ) {
i++;
u++;
}
- return Triplet.with(Arrays.copyOfRange(data, s, i), i, u);
+ i++;
+ u++;
+ return Triplet.with(Arrays.copyOfRange(data, s, i-1), i, u);
}
private int parseActionPush(byte[] data, int i, DoAction tag) {
- //int start_action_index = i; // for debug
i++;
int len_in_bytes = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
int u = 0;
@@ -166,8 +188,11 @@ public class FlashDecompiler {
// implement other types (float especially)
case STRING:
i++;
+ u++;
+ log.info("S0: {}", i);
var result = findNull(data, i, u);
- push.getItems().add(new StringItem(new String(result.getValue0())));
+ push.getItems().add(new StringItem(new String(result.getValue0(), StandardCharsets.UTF_8)));
+ log.info("STRING: {} {}", new String(result.getValue0(), StandardCharsets.UTF_8), result.getValue1());
i = result.getValue1();
u = result.getValue2();
break;
@@ -215,12 +240,6 @@ public class FlashDecompiler {
}
}
tag.getActions().add(push);
- /*byte[] bv = Arrays.copyOfRange(data, start_action_index, i);
- List s = IntStream.range(0, bv.length)
- .map(v -> bv[v])
- .mapToObj(v -> Integer.toHexString(v))
- .collect(Collectors.toList());
- log.info("Push data block: {}", s);*/
return i;
}
@@ -229,19 +248,18 @@ public class FlashDecompiler {
int bytes_len = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
i += 2;
int count = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
- log.info("ActionPoolSize: {}", count);
i += 2;
int u = 1;
- StringBuilder s = new StringBuilder();
+ List s = new ArrayList<>();
while( u <= count ) {
if( data[i] == 0x00 ) {
- actionPool.add(s.toString());
- s = new StringBuilder();
+ actionPool.add(new String( ByteUtil.toPrimitive(s), StandardCharsets.UTF_8 ));
+ s.clear();
u++;
i++;
continue;
}
- s.append((char) data[i]);
+ s.add(data[i]);
i++;
}
tag.setActionPool(actionPool);
diff --git a/src/main/java/com/alterdekim/flash/decompiler/Main.java b/src/main/java/com/alterdekim/flash/decompiler/Main.java
index 8628317..3085281 100644
--- a/src/main/java/com/alterdekim/flash/decompiler/Main.java
+++ b/src/main/java/com/alterdekim/flash/decompiler/Main.java
@@ -17,7 +17,12 @@ import java.nio.file.StandardOpenOption;
public class Main {
public static void main(String[] args) throws Exception {
FlashDecompiler decompiler = new FlashDecompiler();
- ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\FlashProjects\\Untitled-2.swf")); //
+
+
+ /*byte[] g = decompiler.uncompressSWF(Files.readAllBytes(new File("D:\\Documents\\rtmpSpring\\static\\swf\\cache\\rus\\resources[19].swf").toPath()));
+ Files.write(new File("res.swf").toPath(), g, StandardOpenOption.CREATE);*/
+
+ ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\rtmpSpring\\static\\swf\\cache\\rus\\resources[19].swf"));
DoAction da = (DoAction) file.getTags()
.stream()
diff --git a/src/main/java/com/alterdekim/flash/decompiler/compiler/FlashCompiler.java b/src/main/java/com/alterdekim/flash/decompiler/compiler/FlashCompiler.java
index c8afb3e..44da65b 100644
--- a/src/main/java/com/alterdekim/flash/decompiler/compiler/FlashCompiler.java
+++ b/src/main/java/com/alterdekim/flash/decompiler/compiler/FlashCompiler.java
@@ -9,8 +9,8 @@ import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Queue;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
@RequiredArgsConstructor
@Slf4j
@@ -18,21 +18,19 @@ public class FlashCompiler {
private final Java2Flash pcode;
public byte[] compile() {
- List data = new ArrayList<>();
- data.addAll(List.of((byte) 0x46, (byte) 0x57, (byte) 0x53));
+ List data = new ArrayList<>(List.of((byte) 0x46, (byte) 0x57, (byte) 0x53));
data.add((byte) 0x08); // swf version (8)
List d1 = new ArrayList<>();
d1.addAll(List.of((byte) 0x30, (byte) 0x0A, (byte) 0x00, (byte) 0xA0)); // rect
d1.addAll(List.of((byte) 0x00, (byte) 0x19)); // frameRate
d1.addAll(ByteUtil.intToBytes(1)); // frames Count
- //List tags = List.of(new SetBackgroundColor(new byte[] {0,0,0}), new DoAction(pcode.getPool(), pcode.getActions()), new ShowFrame(), new End());
- //d1.addAll(tags.stream().map(CompileObject::compile).flatMap(Collection::stream).collect(Collectors.toList()));
- d1.addAll(new SetBackgroundColor(new byte[] {0,0,0}).compile());
- d1.addAll(new DoAction(pcode.getPool(), pcode.getActions()).compile());
- d1.addAll(new ShowFrame().compile());
- d1.addAll(new End().compile());
+ d1.addAll(
+ Stream.of(new SetBackgroundColor(new byte[] {0,0,0}), new DoAction(pcode.getPool(), pcode.getActions()), new ShowFrame(), new End())
+ .map(CompileObject::compile)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList())
+ );
// fileSize u32
- log.info("fileSize u32: {}", d1.size());
data.addAll(ByteUtil.intToBytes32(d1.size()+data.size()+4));
data.addAll(d1);
return ByteUtil.toPrimitive(data);
diff --git a/src/main/java/com/alterdekim/flash/decompiler/tag/DoAction.java b/src/main/java/com/alterdekim/flash/decompiler/tag/DoAction.java
index 1ff74be..4fb3f61 100644
--- a/src/main/java/com/alterdekim/flash/decompiler/tag/DoAction.java
+++ b/src/main/java/com/alterdekim/flash/decompiler/tag/DoAction.java
@@ -48,7 +48,7 @@ public class DoAction extends ShockwaveTag implements CompileObject {
case INTEGER:
data.addAll(ByteUtil.intToBytes32((Integer) item.getValue()));
break;
- case POOL_INDEX:
+ case POOL_INDEX: // todo: make distiction between small POOL_INDEX and large POOL_INDEX
Integer index = (Integer) item.getValue();
data.addAll(index < 256 ? ByteUtil.intToBytes8(index) : ByteUtil.intToBytes(index));
break;
@@ -66,9 +66,7 @@ public class DoAction extends ShockwaveTag implements CompileObject {
case ConstantPool:
ActionConstantPool pool = (ActionConstantPool) action;
data.addAll(ByteUtil.intToBytes(pool.getPool().size()));
- pool.getPool().forEach(s -> {
- data.addAll(fromString(s));
- });
+ pool.getPool().forEach(s -> data.addAll(fromString(s)));
bytes.addAll(ByteUtil.intToBytes(data.size()));
bytes.addAll(data);
break;
@@ -82,8 +80,8 @@ public class DoAction extends ShockwaveTag implements CompileObject {
private List fromString(String s) {
List l = new ArrayList<>();
- for( char c : s.toCharArray() ) {
- l.add((byte) c);
+ for( byte c : s.getBytes() ) {
+ l.add(c);
}
l.add((byte) 0x0);
return l;
diff --git a/src/main/java/com/alterdekim/flash/decompiler/translator/Flash2Java.java b/src/main/java/com/alterdekim/flash/decompiler/translator/Flash2Java.java
index dd2cb0c..e379dea 100644
--- a/src/main/java/com/alterdekim/flash/decompiler/translator/Flash2Java.java
+++ b/src/main/java/com/alterdekim/flash/decompiler/translator/Flash2Java.java
@@ -61,15 +61,13 @@ public class Flash2Java {
case GetMember:
getMember();
break;
- default:
- // ignore
}
}
return this.vars;
}
private void initObject() {
- int numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way
+ int numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way todo: rewrite
HashMap obj = new HashMap<>();
for( int i = 0; i < numberOfEntries; i++ ) {
Object val = valConverter( this.stack.pop() );
@@ -80,7 +78,7 @@ public class Flash2Java {
}
private void initArray() {
- int numberOfElements = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way
+ int numberOfElements = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way todo: rewrite
HashMap obj = new HashMap<>();
for( int i = 0; i < numberOfElements; i++ ) {
Object val = valConverter( this.stack.pop() );
@@ -90,22 +88,9 @@ public class Flash2Java {
}
private void reduceViaAdder() {
- /*
- // get last 2 elements from stack
- List si = new ArrayList<>();
- List g = new ArrayList<>();
- for( int i = this.stack.size()-1; i >= 0; i-- ) {
- ByteCodeAction e = this.stack.get(i);
- if( e.getType() != ActionField.Push ) continue;
- ActionPush push = (ActionPush) e;
- if( push.getItems().size() >= 2 ) {
- StringItem from = (StringItem) push.getItems().get(push.getItems().size()-1);
- StringItem to = (StringItem) push.getItems().get(push.getItems().size()-2);
- /*push.getItems().remove()
- ((String) to.getValue()) + ((String) from.getValue())*//*
- }
- }
- */
+ String s1 = valToString( this.stack.pop() );
+ String s2 = valToString( this.stack.pop() );
+ this.stack.push(new StringItem(s2 + s1));
}
private void getMember() {
@@ -117,7 +102,7 @@ public class Flash2Java {
this.stack.push(new GetMemberItem(l));
return;
}
- String key = valToString(val);
+ String key = (String) val.getValue();
this.stack.push(new GetMemberItem(new ArrayDeque<>(List.of(key, property))));
}
@@ -126,11 +111,7 @@ public class Flash2Java {
String index = valToString( this.stack.pop() );
ByteCodeItem varOrMember = this.stack.pop();
if( varOrMember.getType() == ActionPushType.GET_VARIABLE ) {
- Map m = (Map) this.vars.get(valToString( varOrMember ));
- if( val.getType() == ActionPushType.GET_VARIABLE ) {
- m.put(index, this.vars.get(valToString(val)));
- return;
- }
+ Map m = (Map) valConverter(varOrMember);
m.put(index, valConverter(val));
return;
}
@@ -156,16 +137,19 @@ public class Flash2Java {
return fromPool( (Integer) item.getValue() );
case STRING:
return (String) item.getValue();
+ case GET_VARIABLE:
+ return (String) vars.get((String) item.getValue());
default:
return String.valueOf( item.getValue() );
}
}
private Object valConverter(ByteCodeItem item) {
- if( item.getType() == ActionPushType.POOL_INDEX ) {
- return fromPool((Integer) item.getValue());
- }
- return item.getValue();
+ return switch (item.getType()) {
+ case POOL_INDEX -> fromPool((Integer) item.getValue());
+ case GET_VARIABLE -> vars.get((String) item.getValue());
+ default -> item.getValue();
+ };
}
private void setVariable() {
@@ -182,10 +166,6 @@ public class Flash2Java {
private void defineLocal() {
ByteCodeItem val = this.stack.pop();
String name = valToString(this.stack.pop());
- if( val.getType() == ActionPushType.GET_VARIABLE ) {
- this.vars.put(name, this.vars.get(valToString(val)));
- return;
- }
this.vars.put(name, valConverter(val));
}
diff --git a/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java b/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java
index 81d7751..ba04fed 100644
--- a/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java
+++ b/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java
@@ -13,13 +13,14 @@ import java.util.*;
@Getter
public class Java2Flash {
+ private int cnt = 0; // abolish
private List pool;
private List actions;
public Java2Flash convert(Map map) {
this.actions = new ArrayList<>();
this.pool = new ArrayList<>();
- this.pool.addAll(extractAllStrings(new HashSet<>(65535), map)); // maybe make a check if hashset size is bigger than initialCapacity
+ this.pool.addAll(extractAllStrings(new HashSet<>(65535), map));
this.actions.add(new ActionConstantPool(this.pool));
map.keySet().forEach(k -> createVariable(map, k));
map.keySet().stream().filter(k -> map.get(k).getClass().getSimpleName().equals("HashMap")).forEach(k -> setMembers(map, k));
@@ -77,7 +78,6 @@ public class Java2Flash {
private void createVariable(Map map, String key) {
List items = new ArrayList<>();
items.add(fromString(key));
- log.info("Type: {}", map.get(key).getClass().getSimpleName());
items.add(switch (map.get(key).getClass().getSimpleName()) {
case "String" -> fromString((String) map.get(key));
case "Integer" -> new IntegerItem((Integer) map.get(key));
@@ -102,13 +102,21 @@ public class Java2Flash {
try {
Integer.parseInt(k);
} catch (Exception e) {
- pool.add(k);
+ if( pool.size() < 3000 ) {
+ pool.add(k);
+ cnt++;
+ }
}
});
map.values().forEach(o -> {
switch (o.getClass().getSimpleName()) {
case "HashMap" -> extractAllStrings(pool, (HashMap) o);
- case "String" -> pool.add((String) o);
+ case "String" -> {
+ if( pool.size() < 3000 ) {
+ pool.add((String) o);
+ cnt++;
+ }
+ }
}
});
return pool;