From 1fa9e0e5477341c8e2f3473b1ea1b4ddebc3ae1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Sat, 19 Apr 2014 16:52:01 +0200 Subject: [PATCH] AS3 direct editation --- .../flash/abc/avm2/model/AVM2Item.java | 11 +- .../parser/script/ActionScriptParser.java | 16 +- .../flash/abc/types/traits/Trait.java | 6 +- .../flash/abc/types/traits/TraitClass.java | 11 +- .../flash/configuration/Configuration.java | 50 +++--- .../decompiler/flash/gui/abc/ABCPanel.java | 161 +++++++++++++++++- .../flash/gui/abc/DecompiledEditorPane.java | 2 +- .../flash/gui/action/ActionPanel.java | 1 + .../flash/gui/locales/MainFrame.properties | 4 + .../flash/gui/locales/MainFrame_cs.properties | 6 +- 10 files changed, 238 insertions(+), 30 deletions(-) diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java index f49d79404..c70006f5d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java @@ -46,7 +46,7 @@ public abstract class AVM2Item extends GraphTargetItem { } protected GraphTextWriter formatProperty(GraphTextWriter writer, GraphTargetItem object, GraphTargetItem propertyName, LocalData localData) throws InterruptedException { - boolean empty = false; + boolean empty = object instanceof FindPropertyAVM2Item; if (object instanceof LocalRegAVM2Item) { if (((LocalRegAVM2Item) object).computedValue != null) { if (((LocalRegAVM2Item) object).computedValue.getThroughNotCompilable() instanceof FindPropertyAVM2Item) { @@ -55,6 +55,13 @@ public abstract class AVM2Item extends GraphTargetItem { } } + if(object instanceof FindPropertyAVM2Item){ + FindPropertyAVM2Item fp=(FindPropertyAVM2Item)object; + if(fp.propertyName instanceof FullMultinameAVM2Item){ + propertyName = fp.propertyName; + } + } + if (!empty) { if (object.getPrecedence() > PRECEDENCE_PRIMARY) { writer.append("("); @@ -70,7 +77,7 @@ public abstract class AVM2Item extends GraphTargetItem { } } - if (empty) { + if (empty) { return propertyName.toString(writer, localData); } if (propertyName instanceof FullMultinameAVM2Item) { diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java index 09693876b..8a350e295 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java @@ -2017,6 +2017,7 @@ public class ActionScriptParser { if (s.type != SymbolType.CURLY_OPEN) { expected(s, lexer.yyline(), SymbolType.IDENTIFIER); name = s.value.toString(); + s = lex(); } while (s.type != SymbolType.CURLY_OPEN) { expected(s, lexer.yyline(), SymbolType.DOT); @@ -2097,7 +2098,6 @@ public class ActionScriptParser { } public PackageAVM2Item packageFromString(String str, String fileName) throws ParseException, IOException, CompilationException { - this.constantPool = constantPool; lexer = new ActionScriptLexer(new StringReader(str)); PackageAVM2Item ret = parsePackage(fileName); @@ -2132,6 +2132,20 @@ public class ActionScriptParser { this.otherABCs = otherABCs; } + + public static void compile(String src, ABC abc, boolean documentClass,String fileName) throws ParseException, IOException, InterruptedException, CompilationException { + SWC swc = new SWC(new FileInputStream(Configuration.getPlayerSWC())); + SWF swf = new SWF(swc.getSWF("library.swf"), true); + List playerABCs = new ArrayList<>(); + for (Tag t : swf.tags) { + if (t instanceof ABCContainerTag) { + playerABCs.add(((ABCContainerTag) t).getABC()); + } + } + ActionScriptParser parser = new ActionScriptParser(abc, playerABCs); + parser.addScript(src, documentClass, fileName); + } + public static void compile(String src, String dst) { System.err.println("WARNING: AS3 compiler is not finished yet. This is only used for debuggging!"); try { diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java index 60c344d6f..5cb7f94da 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java @@ -96,7 +96,11 @@ public abstract class Trait implements Serializable { } } if (isStatic) { - ret += " static"; + if((this instanceof TraitSlotConst)&&((TraitSlotConst)this).isNamespace()){ + //static is automatic + }else{ + ret += " static"; + } } if ((kindFlags & ATTR_Final) > 0) { if (!isStatic) { diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index 9131d1fd3..6d5189c83 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -74,11 +74,11 @@ public class TraitClass extends Trait implements TraitWithSlot { name = "*"; } String newimport = ns.getName(abc.constants); - if ((ns.kind != Namespace.KIND_PACKAGE) + /*if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_NAMESPACE) && (ns.kind != Namespace.KIND_STATIC_PROTECTED)) { return false; - } + }*/ /*if (ns.kind == Namespace.KIND_NAMESPACE)*/ { String oldimport = newimport; newimport = null; @@ -118,7 +118,9 @@ public class TraitClass extends Trait implements TraitWithSlot { } if (ns.kind == Namespace.KIND_PACKAGE) { if (!pkg.equals(ignorePackage)) { - imports.add(newimport); + if(!pkg.equals("__AS3__.vec")){ //Automatic import + imports.add(newimport); + } } } if (ns.kind == Namespace.KIND_NAMESPACE) { @@ -159,6 +161,9 @@ public class TraitClass extends Trait implements TraitWithSlot { } if (!imports.contains(newimport)) { String pkg = newimport.substring(0, newimport.lastIndexOf('.')); + if(pkg.equals("__AS3__.vec")){ //special case - is imported always + return; + } if (!pkg.equals(ignorePackage)) { imports.add(newimport); } diff --git a/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java index cb7718611..2020f6654 100644 --- a/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/trunk/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -595,32 +595,42 @@ public class Configuration { return ret; } - public static File getPlayerSWC() { + public static File getFlashLibPath() { try { String home = getFFDecHome(); File libsdir = new File(home + "flashlib"); - if (libsdir.exists()) { - File libs[] = libsdir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - return name.toLowerCase().startsWith("playerglobal"); - } - }); - List libnames = new ArrayList<>(); - for (File f : libs) { - libnames.add(f.getName()); - } - Collections.sort(libnames); - if (!libnames.isEmpty()) { - return new File(libsdir.getAbsolutePath() + File.separator + libnames.get(libnames.size() - 1)); - } else { - return null; - } + if (!libsdir.exists()) { + libsdir.mkdirs(); } + return libsdir; } catch (IOException ex) { - + return null; } + + } + + public static File getPlayerSWC() { + File libsdir = getFlashLibPath(); + if (libsdir!=null && libsdir.exists()) { + File libs[] = libsdir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().startsWith("playerglobal"); + } + }); + List libnames = new ArrayList<>(); + for (File f : libs) { + libnames.add(f.getName()); + } + Collections.sort(libnames); + if (!libnames.isEmpty()) { + return new File(libsdir.getAbsolutePath() + File.separator + libnames.get(libnames.size() - 1)); + } else { + return null; + } + } + return null; } } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index f31445af7..d2539d71a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -26,6 +26,8 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.MethodInfo; @@ -55,7 +57,10 @@ import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; import com.jpexs.decompiler.flash.helpers.Freed; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.treenodes.TreeNode; +import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import java.awt.BorderLayout; @@ -71,6 +76,9 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; @@ -90,6 +98,7 @@ import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; +import javax.swing.border.BevelBorder; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; @@ -123,6 +132,15 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se private NewTraitDialog newTraitDialog; public JLabel scriptNameLabel; + static final String ACTION_SAVE_DECOMPILED = "SAVEDECOMPILED"; + static final String ACTION_EDIT_DECOMPILED = "EDITDECOMPILED"; + static final String ACTION_CANCEL_DECOMPILED = "CANCELDECOMPILED"; + + public JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); + public JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + public JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + public JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + static final String ACTION_ADD_TRAIT = "ADDTRAIT"; public boolean search(String txt, boolean ignoreCase, boolean regexp) { @@ -361,10 +379,34 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se iconDecPanel.add(scriptNameLabel); iconDecPanel.add(iconsPanel); iconDecPanel.add(decompiledScrollPane); + + JPanel decButtonsPan = new JPanel(new FlowLayout()); + decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); + decButtonsPan.add(editDecompiledButton); + decButtonsPan.add(experimentalLabel); + decButtonsPan.add(saveDecompiledButton); + decButtonsPan.add(cancelDecompiledButton); + + editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + + saveDecompiledButton.addActionListener(this); + saveDecompiledButton.setActionCommand(ACTION_SAVE_DECOMPILED); + editDecompiledButton.addActionListener(this); + editDecompiledButton.setActionCommand(ACTION_EDIT_DECOMPILED); + + cancelDecompiledButton.addActionListener(this); + cancelDecompiledButton.setActionCommand(ACTION_CANCEL_DECOMPILED); + saveDecompiledButton.setVisible(false); + cancelDecompiledButton.setVisible(false); + decButtonsPan.setAlignmentX(0); + JPanel decPanel = new JPanel(new BorderLayout()); decPanel.add(searchPanel, BorderLayout.NORTH); decPanel.add(iconDecPanel, BorderLayout.CENTER); + decPanel.add(decButtonsPan, BorderLayout.SOUTH); detailPanel = new DetailPanel(this); JPanel panB = new JPanel(); panB.setLayout(new BorderLayout()); @@ -380,7 +422,9 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se splitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent pce) { - Configuration.guiAvm2SplitPaneDividerLocation.set((int) pce.getNewValue()); + if(!directEditing){ + Configuration.guiAvm2SplitPaneDividerLocation.set((int) pce.getNewValue()); + } } }); decompiledTextArea.setContentType("text/actionscript"); @@ -543,9 +587,124 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se searchPanel.showQuickFindDialog(decompiledTextArea); } + + public String lastDecompiled = null; + public boolean directEditing = false; + private int detWidth = 0; + private int detsp = 0; + + public void setDecompiledEditMode(boolean val) { + if (val) { + lastDecompiled = decompiledTextArea.getText(); + decompiledTextArea.setEditable(true); + saveDecompiledButton.setVisible(true); + editDecompiledButton.setVisible(false); + experimentalLabel.setVisible(false); + cancelDecompiledButton.setVisible(true); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(View.getIcon("editing16")); + directEditing = true; + detWidth = detailPanel.getWidth(); + detsp = splitPane.getDividerLocation(); + detailPanel.setVisible(false); + } else { + decompiledTextArea.setText(lastDecompiled); + decompiledTextArea.setEditable(false); + saveDecompiledButton.setVisible(false); + editDecompiledButton.setVisible(true); + experimentalLabel.setVisible(true); + cancelDecompiledButton.setVisible(false); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(null); + directEditing = false; + detailPanel.setVisible(true); + detailPanel.setSize(detailPanel.getHeight(), detWidth); + splitPane.setDividerLocation(detsp); + } + decompiledTextArea.ignoreCarret = directEditing; + + decompiledTextArea.requestFocusInWindow(); + } + @Override public void actionPerformed(ActionEvent e) { switch (e.getActionCommand()) { + case ACTION_EDIT_DECOMPILED: + File swc = Configuration.getPlayerSWC(); + final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; + if(swc == null){ + if(View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%",adobePage),AppStrings.translate("message.action.playerglobal.title"),JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE)==JOptionPane.OK_OPTION){ + + java.awt.Desktop desktop = null; + if (java.awt.Desktop.isDesktopSupported()) { + desktop = java.awt.Desktop.getDesktop(); + if (desktop.isSupported(java.awt.Desktop.Action.BROWSE)) { + try { + java.net.URI uri = new java.net.URI(adobePage); + desktop.browse(uri); + } catch (URISyntaxException | IOException ex) { + } + } else { + desktop = null; + } + } + + int ret = 0; + do{ + ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()),AppStrings.translate("message.action.playerglobal.title"),JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); + swc = Configuration.getPlayerSWC(); + }while(ret == JOptionPane.OK_OPTION && swc == null); + if(ret == JOptionPane.OK_OPTION){ + setDecompiledEditMode(true); + } + } + }else{ + setDecompiledEditMode(true); + } + break; + case ACTION_CANCEL_DECOMPILED: + setDecompiledEditMode(false); + break; + case ACTION_SAVE_DECOMPILED: + ScriptPack pack = decompiledTextArea.getScriptLeaf(); + String scriptName = pack.getPathScriptName()+".as"; + int oldIndex = pack.scriptIndex; + int newIndex = abc.script_info.size(); + String documentClass = ""; + loopt:for (Tag t : swf.tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t; + for (int i = 0; i < sc.tags.length; i++) { + if (sc.tags[i] == 0) { + documentClass = sc.names[i]; + break loopt; + } + } + } + } + boolean isDocumentClass = documentClass.equals(pack.getPath().toString()); + + try { + ActionScriptParser.compile(decompiledTextArea.getText(), abc,isDocumentClass, scriptName); + //Move newly added script to its position + abc.script_info.set(oldIndex, abc.script_info.get(newIndex)); + abc.script_info.remove(newIndex); + ((Tag)abc.parentTag).setModified(true); + lastDecompiled = decompiledTextArea.getText(); + decompiledTextArea.setClassIndex(-1); + View.showMessageDialog(this, AppStrings.translate("message.action.saved")); + setDecompiledEditMode(false); + reload(); + } catch (ParseException ex) { + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + decompiledTextArea.gotoLine((int)ex.line); + } catch (CompilationException ex) { + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + decompiledTextArea.gotoLine((int)ex.line); + } catch (IOException|InterruptedException ex) { + //ignore + } + break; case ACTION_ADD_TRAIT: int class_index = decompiledTextArea.getClassIndex(); if (class_index < 0) { diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index 12ccf64bd..dead716c7 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -54,7 +54,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL private ABC abc; private ScriptPack script; public int lastTraitIndex = 0; - private boolean ignoreCarret = false; + public boolean ignoreCarret = false; private boolean reset = false; private final ABCPanel abcPanel; private int classIndex = -1; diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index 7d1c4c220..a00b7ae07 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -739,6 +739,7 @@ public class ActionPanel extends JPanel implements ActionListener, SearchListene } catch (IOException ex) { } catch (ParseException ex) { View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + editor.gotoLine((int)ex.line); } break; case ACTION_EDIT_DECOMPILED: diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 9f5bdaad0..ebf835c4e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -464,3 +464,7 @@ error.sound.invalid = Invalid sound. button.prev = Previous button.next = Next + +message.action.playerglobal.title = PlayerGlobal library needed +message.action.playerglobal.needed = For ActionScript 3 direct editation, a library called "PlayerGlobal.swc" needs to be downloaded from Adobe homepage.\r\n%adobehomepage%\r\nPress OK to go to the download page. +message.action.playerglobal.place = Download the library called PlayerGlobal(.swc), and place it to directory\r\n%libpath%\r\n Press OK to continue. \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 7ca259b34..dd5367cd1 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -459,4 +459,8 @@ filter.sounds = Podporovan\u00e9 zvukov\u00e9 form\u00e1ty (*.wav, *.mp3) filter.sounds.wav = Wave form\u00e1t (*.wav) filter.sounds.mp3 = MP3 komprimovan\u00fd form\u00e1t (*.mp3) -error.sound.invalid = Neplatn\u00fd zvuk. \ No newline at end of file +error.sound.invalid = Neplatn\u00fd zvuk. + +message.action.playerglobal.title = Vy\u017eadov\u00e1na knihovna PlayerGlobal +message.action.playerglobal.needed = Pro p\u0159\u00edmou editaci ActionScriptu 3 je pot\u0159eba knihovna "PlayerGlobal.swc", kterou lze stahnout ze str\u00e1nek Adobe\r\n%adobehomepage%\r\nStiskn\u011bte OK pro p\u0159echod na stahovac\u00ed str\u00e1nku. +message.action.playerglobal.place = St\u00e1hn\u011bte knihovnu nazvanou PlayerGlobal(.swc), a um\u00edst\u011bte j\u00ed do adres\u00e1\u0159e\r\n%libpath%\r\n Stiskn\u011bte OK pro pokra\u010dov\u00e1n\u00ed. \ No newline at end of file