diff --git a/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java b/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java index 8d48225..7123b1a 100644 --- a/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java +++ b/src/main/java/com/alterdekim/flash/decompiler/FlashDecompiler.java @@ -99,6 +99,10 @@ public class FlashDecompiler { case Push: i = parseActionPush(data, i, tag); break; + case InitArray: + tag.getActions().add(new ActionInitArray()); + i++; + break; case InitObject: tag.getActions().add(new ActionInitObject()); i++; @@ -225,6 +229,7 @@ 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(); diff --git a/src/main/java/com/alterdekim/flash/decompiler/Main.java b/src/main/java/com/alterdekim/flash/decompiler/Main.java index 81e99b8..6afd01b 100644 --- a/src/main/java/com/alterdekim/flash/decompiler/Main.java +++ b/src/main/java/com/alterdekim/flash/decompiler/Main.java @@ -3,6 +3,7 @@ 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.alterdekim.flash.decompiler.translator.Java2Flash; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; @@ -12,7 +13,7 @@ import java.io.File; 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")); // D:\Documents\rtmpSpring\static\swf\cache\rus\base[17].swf + ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\rtmpSpring\\static\\swf\\cache\\rus\\catalogs[18].swf")); // DoAction da = (DoAction) file.getTags() .stream() @@ -23,7 +24,7 @@ public class Main { ObjectMapper objectMapper = new ObjectMapper(); File out = new File("dec.json"); if( out.exists() ) out.delete(); - objectMapper.writeValue(out, new Flash2Java(da).convert()); - + //objectMapper.writeValue(out, new Flash2Java(da).convert()); + new Java2Flash().convert(new Flash2Java(da).convert()); } } \ No newline at end of file diff --git a/src/main/java/com/alterdekim/flash/decompiler/action/ActionInitArray.java b/src/main/java/com/alterdekim/flash/decompiler/action/ActionInitArray.java new file mode 100644 index 0000000..267ac04 --- /dev/null +++ b/src/main/java/com/alterdekim/flash/decompiler/action/ActionInitArray.java @@ -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 ActionInitArray extends ByteCodeAction { + @Override + public ActionField getType() { + return ActionField.InitArray; + } +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/flash/decompiler/item/ActionPushType.java b/src/main/java/com/alterdekim/flash/decompiler/item/ActionPushType.java index 24506fc..82c6cde 100644 --- a/src/main/java/com/alterdekim/flash/decompiler/item/ActionPushType.java +++ b/src/main/java/com/alterdekim/flash/decompiler/item/ActionPushType.java @@ -14,10 +14,11 @@ public enum ActionPushType { // these types are internal (used for decompiler's purposes) // they are not exist - INIT_OBJECT(-1), GET_VARIABLE(-2), - GET_MEMBER(-3); + GET_MEMBER(-3), + INIT_ARRAY(-4), + CONSTANT_POOL(-5); public final int type; diff --git a/src/main/java/com/alterdekim/flash/decompiler/item/internal/ConstantPoolItem.java b/src/main/java/com/alterdekim/flash/decompiler/item/internal/ConstantPoolItem.java new file mode 100644 index 0000000..bdcc66a --- /dev/null +++ b/src/main/java/com/alterdekim/flash/decompiler/item/internal/ConstantPoolItem.java @@ -0,0 +1,25 @@ +package com.alterdekim.flash.decompiler.item.internal; + +import com.alterdekim.flash.decompiler.item.ActionPushType; +import com.alterdekim.flash.decompiler.item.ByteCodeItem; +import lombok.AllArgsConstructor; +import lombok.ToString; + +import java.util.ArrayList; + +@ToString +@AllArgsConstructor +public class ConstantPoolItem extends ByteCodeItem { + + private ArrayList val; + + @Override + public ActionPushType getType() { + return ActionPushType.CONSTANT_POOL; + } + + @Override + public Object getValue() { + return this.val; + } +} diff --git a/src/main/java/com/alterdekim/flash/decompiler/item/internal/InitArrayItem.java b/src/main/java/com/alterdekim/flash/decompiler/item/internal/InitArrayItem.java new file mode 100644 index 0000000..5412def --- /dev/null +++ b/src/main/java/com/alterdekim/flash/decompiler/item/internal/InitArrayItem.java @@ -0,0 +1,24 @@ +package com.alterdekim.flash.decompiler.item.internal; + +import com.alterdekim.flash.decompiler.item.ActionPushType; +import com.alterdekim.flash.decompiler.item.ByteCodeItem; +import lombok.AllArgsConstructor; +import lombok.ToString; + +import java.util.HashMap; + +@ToString +@AllArgsConstructor +public class InitArrayItem extends ByteCodeItem { + private HashMap val; + + @Override + public ActionPushType getType() { + return ActionPushType.INIT_ARRAY; + } + + @Override + public Object getValue() { + return this.val; + } +} 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 63625d9..a53ba61 100644 --- a/src/main/java/com/alterdekim/flash/decompiler/translator/Flash2Java.java +++ b/src/main/java/com/alterdekim/flash/decompiler/translator/Flash2Java.java @@ -5,6 +5,7 @@ import com.alterdekim.flash.decompiler.item.*; import com.alterdekim.flash.decompiler.action.ByteCodeAction; import com.alterdekim.flash.decompiler.item.internal.GetMemberItem; import com.alterdekim.flash.decompiler.item.internal.GetVariableItem; +import com.alterdekim.flash.decompiler.item.internal.InitArrayItem; import com.alterdekim.flash.decompiler.item.internal.InitObjectItem; import com.alterdekim.flash.decompiler.tag.DoAction; import lombok.extern.slf4j.Slf4j; @@ -16,7 +17,7 @@ public class Flash2Java { private final List pool; private final List actions; - private final Stack stack; // ByteCodeAction + private final Stack stack; private final Map vars; public Flash2Java(DoAction doAction) { @@ -27,50 +28,48 @@ public class Flash2Java { } public Map convert() { - // abolish the activeness of variable inside a HashMap. Use stack instead. for (ByteCodeAction action : actions) { - // try { - //log.info("Action type: " + action.getType().toString()); - switch (action.getType()) { - case DefineLocal2: - defineLocalWithoutAssign(); - break; - case DefineLocal: - defineLocal(); - break; - case GetVariable: - getVariable(); - break; - case SetMember: - setMember(); - break; - case Add: - reduceViaAdder(); - break; - case InitObject: - initObject(); - break; - case Push: - this.stack.addAll(((ActionPush) action).getItems()); - break; - case SetVariable: - setVariable(); - break; - case GetMember: - getMember(); - break; - default: - // ignore - } - //} catch (Exception e) { - //log.error(e.getMessage()); - //} + switch (action.getType()) { + case DefineLocal2: + // doesn't need implementation due to HashMap's properties. (variableName) + this.stack.pop(); + break; + case DefineLocal: + defineLocal(); + break; + case GetVariable: + getVariable(); + break; + case SetMember: + setMember(); + break; + case Add: + reduceViaAdder(); + break; + case InitObject: + initObject(); + break; + case InitArray: + initArray(); + break; + case Push: + this.stack.addAll(((ActionPush) action).getItems()); + break; + case SetVariable: + setVariable(); + break; + case GetMember: + getMember(); + break; + default: + // ignore + } } - return vars; + return this.vars; } private void initObject() { - Integer numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way + int numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way HashMap obj = new HashMap<>(); for( int i = 0; i < numberOfEntries; i++ ) { Object val = valConverter( this.stack.pop() ); @@ -80,6 +79,16 @@ public class Flash2Java { this.stack.push(new InitObjectItem(obj)); } + private void initArray() { + int numberOfElements = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way + HashMap obj = new HashMap<>(); + for( int i = 0; i < numberOfElements; i++ ) { + Object val = valConverter( this.stack.pop() ); + obj.put(String.valueOf(i), val); + } + this.stack.push(new InitArrayItem(obj)); + } + private void reduceViaAdder() { /* // get last 2 elements from stack @@ -100,7 +109,6 @@ public class Flash2Java { } private void getMember() { - log.info("GetMember: {}", this.stack); String property = valToString( this.stack.pop() ); ByteCodeItem val = this.stack.pop(); if( val.getType() == ActionPushType.GET_MEMBER ) { @@ -113,56 +121,49 @@ public class Flash2Java { this.stack.push(new GetMemberItem(new ArrayDeque<>(List.of(key, property)))); } - private void setMember() { // make recursive assignment. - log.info("SetMember: {}", this.stack); + private void setMember() { ByteCodeItem val = this.stack.pop(); 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; + } m.put(index, val.getValue()); - this.vars.put(valToString(varOrMember), m); return; } - // GetMemberItem recursive Map m = recursiveGet((ArrayDeque) varOrMember.getValue()); + if( val.getType() == ActionPushType.GET_VARIABLE ) { + m.put(index, this.vars.get(valToString(val))); + return; + } m.put(index, val.getValue()); } private Map recursiveGet(Deque l) { Map m = this.vars; - Iterator i = l.iterator(); - while(i.hasNext()) { - m = (HashMap) m.get(i.next()); + for (String s : l) { + m = (HashMap) m.get(s); } return m; } - private GetMemberItem toGetMember(ByteCodeItem item) { - switch (item.getType()) { - case GET_MEMBER: - return (GetMemberItem) item; - case STRING: - case POOL_INDEX: - default: - return new GetMemberItem(new ArrayDeque<>(List.of(valToString(item)))); - } - } - private String valToString(ByteCodeItem item) { - if ( item.getType() == ActionPushType.POOL_INDEX ) { - return fromPool( (Integer) item.getValue() ); - } else if ( item.getType() == ActionPushType.STRING ) { - return (String) item.getValue(); + switch (item.getType()) { + case POOL_INDEX: + return fromPool( (Integer) item.getValue() ); + case STRING: + return (String) item.getValue(); + default: + return String.valueOf( item.getValue() ); } - return String.valueOf( item.getValue() ); } private Object valConverter(ByteCodeItem item) { - if ( item.getType() == ActionPushType.POOL_INDEX ) { - return fromPool( (Integer) item.getValue() ); - } else if ( item.getType() == ActionPushType.STRING ) { - return item.getValue(); + if( item.getType() == ActionPushType.POOL_INDEX ) { + return fromPool((Integer) item.getValue()); } return item.getValue(); } @@ -170,7 +171,7 @@ public class Flash2Java { private void setVariable() { Object val = this.stack.pop().getValue(); String variableName = valToString( this.stack.pop() ); - vars.put(variableName, val); + this.vars.put(variableName, val); } private void getVariable() { @@ -178,18 +179,17 @@ public class Flash2Java { this.stack.push(new GetVariableItem(variableName)); } - private void defineLocalWithoutAssign() { - String variableName = valToString( this.stack.pop() ); - } - private void defineLocal() { ByteCodeItem val = this.stack.pop(); - String name = valToString( this.stack.pop() ); - // todo: implement assignment ability (from one value to another) - vars.put(name, val.getValue()); + 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, val.getValue()); } private String fromPool(int index) { return pool.get(index); } -} +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/flash/decompiler/translator/FlashVar.java b/src/main/java/com/alterdekim/flash/decompiler/translator/FlashVar.java deleted file mode 100644 index 8a5141a..0000000 --- a/src/main/java/com/alterdekim/flash/decompiler/translator/FlashVar.java +++ /dev/null @@ -1,15 +0,0 @@ -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; -} diff --git a/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java b/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java new file mode 100644 index 0000000..36c4296 --- /dev/null +++ b/src/main/java/com/alterdekim/flash/decompiler/translator/Java2Flash.java @@ -0,0 +1,36 @@ +package com.alterdekim.flash.decompiler.translator; + +import com.alterdekim.flash.decompiler.action.ByteCodeAction; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +@Slf4j +public class Java2Flash { + private Set pool; + + public List convert(Map map) { + this.pool = new HashSet<>(65535); + extractAllStrings(map); + log.info("poolSet: {}", this.pool); + return new ArrayList<>(); + } + + private void extractAllStrings(Map map) { + map.keySet().forEach(k -> { + if(k.matches("[^0-9].+")) { + pool.add(k); + } + }); + map.values().forEach(o -> { + switch( o.getClass().getSimpleName() ) { + case "HashMap": + extractAllStrings((HashMap) o); + break; + case "String": + pool.add((String) o); + break; + } + }); + } +} diff --git a/src/main/java/com/alterdekim/flash/decompiler/util/ActionField.java b/src/main/java/com/alterdekim/flash/decompiler/util/ActionField.java index 6f781bc..fecf478 100644 --- a/src/main/java/com/alterdekim/flash/decompiler/util/ActionField.java +++ b/src/main/java/com/alterdekim/flash/decompiler/util/ActionField.java @@ -4,6 +4,7 @@ public enum ActionField { ConstantPool((byte) 0x88), Push((byte) 0x96), InitObject((byte) 0x43), + InitArray((byte) 0x42), DefineLocal((byte) 0x3C), DefineLocal2((byte) 0x41), SetMember((byte)0x4F), diff --git a/src/main/java/com/alterdekim/flash/decompiler/util/ByteUtil.java b/src/main/java/com/alterdekim/flash/decompiler/util/ByteUtil.java index 76723d8..7d96696 100644 --- a/src/main/java/com/alterdekim/flash/decompiler/util/ByteUtil.java +++ b/src/main/java/com/alterdekim/flash/decompiler/util/ByteUtil.java @@ -1,8 +1,12 @@ package com.alterdekim.flash.decompiler.util; +import lombok.extern.slf4j.Slf4j; + import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.Arrays; +@Slf4j public class ByteUtil { public static int bytesToInt(byte[] bytes) { int value = 0; @@ -21,6 +25,13 @@ public class ByteUtil { } public static double bytesToDouble(byte[] bytes) { - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getDouble(); + byte[] b1 = new byte[8]; + for( int i = bytes.length-1, u = 0; i >= 0; i--, u++ ) { + b1[u] = bytes[i]; + } + byte[] d = new byte[8]; + System.arraycopy(Arrays.copyOfRange(b1, 4, 8), 0, d, 0, 4); + System.arraycopy(Arrays.copyOfRange(b1, 0, 4), 0, d, 4, 4); + return ByteBuffer.wrap(d).order(ByteOrder.BIG_ENDIAN).getDouble(); } }