mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-21 08:55:38 +00:00
Faster AS3 Debugging - export/import ByteArray variable data
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
import flash.utils.ByteArray;
|
||||
import flash.events.Event;
|
||||
import flash.display.Sprite;
|
||||
|
||||
import flash.utils.getTimer;
|
||||
import flash.events.ProgressEvent;
|
||||
|
||||
public class DebugConnection {
|
||||
|
||||
private static var s:Socket;
|
||||
@@ -12,21 +14,30 @@
|
||||
private static var first:Boolean = true;
|
||||
private static var inited:Boolean = false;
|
||||
private static var name:String;
|
||||
private static var failed:Boolean = false;
|
||||
private static var fillByteArrays = [];
|
||||
private static var lenBytes:Array = [
|
||||
-1, -1, -1, -1
|
||||
];
|
||||
private static var lenBytePos:int = 0;
|
||||
private static var len:int = 0;
|
||||
private static var readBa:ByteArray;
|
||||
|
||||
|
||||
public static const DEBUG_VERSION_MAJOR = 1;
|
||||
public static const DEBUG_VERSION_MINOR = 2;
|
||||
public static const DEBUG_VERSION_MINOR = 3;
|
||||
|
||||
public static const MSG_STRING = 0;
|
||||
public static const MSG_LOADER_URL = 1;
|
||||
public static const MSG_LOADER_BYTES = 2;
|
||||
public static const MSG_LONGSTRING = 3;
|
||||
public static const MSG_LOADER_URL_GETBYTES = 4;
|
||||
public static const MSG_LOADER_BYTES_GETBYTES = 5;
|
||||
|
||||
|
||||
public static const MSG_DUMP_BYTEARRAY = 3;
|
||||
public static const MSG_REQUEST_BYTEARRAY = 4;
|
||||
|
||||
|
||||
private static function sendQueue(){
|
||||
var qo = q;
|
||||
q = [];
|
||||
sendHeader();
|
||||
for each(var m in qo){
|
||||
writeMsg(m.data,m.type);
|
||||
}
|
||||
@@ -68,8 +79,7 @@
|
||||
var a3 = s.readUnsignedByte();
|
||||
var a4 = s.readUnsignedByte();
|
||||
var len = (a1<<24)+(a2<<16)+(a3<<8)+a4;
|
||||
|
||||
s.readBytes(b,0,len);
|
||||
s.readBytes(b,0,len);
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -85,24 +95,65 @@
|
||||
private static function readLongString():String {
|
||||
var b:ByteArray = readBytes();
|
||||
return b.readUTFBytes(b.length);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function initClient(sname){
|
||||
if(inited){
|
||||
return;
|
||||
}
|
||||
name = sname;
|
||||
inited = true;
|
||||
s = new Socket();
|
||||
s.addEventListener(Event.CONNECT, function(){
|
||||
sendQueue();
|
||||
});
|
||||
var port:int = 0;
|
||||
port = 123456;
|
||||
s.connect("localhost",port);
|
||||
inited = true;
|
||||
try {
|
||||
s = new Socket();
|
||||
s.addEventListener(Event.CONNECT, onSocketConnect);
|
||||
s.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
||||
var port:int = 0;
|
||||
port = 123456;
|
||||
s.connect("localhost",port);
|
||||
|
||||
inited = true;
|
||||
} catch (e:SecurityError) {
|
||||
trace("Debugger helper failed to connect to localhost");
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static function onSocketConnect(event:Event):void {
|
||||
sendQueue();
|
||||
}
|
||||
|
||||
|
||||
private static function onSocketData(event:ProgressEvent):void {
|
||||
while (s.bytesAvailable > 0) {
|
||||
if (lenBytePos < 4) {
|
||||
lenBytes[lenBytePos] = s.readUnsignedByte();
|
||||
lenBytePos++;
|
||||
if (lenBytePos == 4) {
|
||||
len = (lenBytes[0]<<24)+(lenBytes[1]<<16)+(lenBytes[2]<<8)+lenBytes[3];
|
||||
readBa = new ByteArray();
|
||||
}
|
||||
} else {
|
||||
var readLen:int = s.bytesAvailable <= len ? s.bytesAvailable : len;
|
||||
s.readBytes(readBa, readBa.length, readLen);
|
||||
len -= readLen;
|
||||
|
||||
if (len == 0) {
|
||||
lenBytePos = 0;
|
||||
var ba:ByteArray = fillByteArrays.pop();
|
||||
var pos = ba.position;
|
||||
ba.position = 0;
|
||||
ba.length = 0;
|
||||
ba.writeBytes(readBa);
|
||||
if (pos > ba.length) {
|
||||
ba.position = ba.length;
|
||||
} else {
|
||||
ba.position = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function writeLoaderURL(url){
|
||||
writeMsg(url,MSG_LOADER_URL);
|
||||
@@ -113,19 +164,59 @@
|
||||
writeMsg(data,MSG_LOADER_BYTES);
|
||||
}
|
||||
|
||||
public static function writeCommaSeparatedToByteArray(s:String, ba:ByteArray) {
|
||||
var bytes:Array = s.split(",");
|
||||
var pos:uint = ba.position;
|
||||
ba.position = 0;
|
||||
ba.length = 0;
|
||||
for (var i:int = 0; i < bytes.length; i++) {
|
||||
ba.writeByte(bytes[i]);
|
||||
}
|
||||
if (pos > ba.length) {
|
||||
ba.position = ba.length;
|
||||
} else {
|
||||
ba.position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
public static function readCommaSeparatedFromByteArray(ba:ByteArray): String {
|
||||
var s:String = "";
|
||||
var splitter = "";
|
||||
for (var i:int = 0; i < ba.length; i++) {
|
||||
s += splitter;
|
||||
s += ba[i];
|
||||
splitter = ",";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private static function sendHeader() {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
if (!s.connected) {
|
||||
return;
|
||||
}
|
||||
writeStringNull("debug.version.major="+DEBUG_VERSION_MAJOR+";debug.version.minor="+DEBUG_VERSION_MINOR);
|
||||
writeString(name);
|
||||
first = false;
|
||||
}
|
||||
|
||||
public static function writeMsg(msg,msgType=0){
|
||||
if(!inited){
|
||||
if (failed) {
|
||||
return;
|
||||
}
|
||||
if(!inited) {
|
||||
initClient("");
|
||||
}
|
||||
if(s.connected){
|
||||
if(first){
|
||||
//s.writeByte(0);
|
||||
writeStringNull("debug.version.major="+DEBUG_VERSION_MAJOR+";debug.version.minor="+DEBUG_VERSION_MINOR);
|
||||
writeString(name);
|
||||
first = false;
|
||||
}
|
||||
if ((msg is ByteArray) && msgType == MSG_DUMP_BYTEARRAY) {
|
||||
var ba2:ByteArray = new ByteArray();
|
||||
ba2.writeBytes(msg);
|
||||
msg = ba2;
|
||||
}
|
||||
|
||||
if(s.connected){
|
||||
sendHeader();
|
||||
s.writeByte(msgType);
|
||||
switch(msgType){
|
||||
case MSG_STRING:
|
||||
@@ -137,11 +228,11 @@
|
||||
case MSG_LOADER_BYTES:
|
||||
writeBytes(msg);
|
||||
break;
|
||||
case MSG_LOADER_URL_GETBYTES:
|
||||
writeString(msg);
|
||||
case MSG_DUMP_BYTEARRAY:
|
||||
writeBytes(msg);
|
||||
break;
|
||||
case MSG_LOADER_BYTES_GETBYTES:
|
||||
writeBytes(msg);
|
||||
case MSG_REQUEST_BYTEARRAY:
|
||||
fillByteArrays.push(msg);
|
||||
break;
|
||||
}
|
||||
s.flush();
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
</timelines>
|
||||
<PrinterSettings/>
|
||||
<publishHistory>
|
||||
<PublishItem publishSize="4021" publishTime="1700417069" publishDebug="true"/>
|
||||
<PublishItem publishSize="3974" publishTime="1700415286" publishDebug="true"/>
|
||||
<PublishItem publishSize="4206" publishTime="1700412219" publishDebug="true"/>
|
||||
<PublishItem publishSize="4201" publishTime="1700412018" publishDebug="true"/>
|
||||
<PublishItem publishSize="4206" publishTime="1700411684" publishDebug="true"/>
|
||||
<PublishItem publishSize="3065" publishTime="1700411624"/>
|
||||
<PublishItem publishSize="568" publishTime="1700411559"/>
|
||||
<PublishItem publishSize="4175" publishTime="1700340276" publishDebug="true"/>
|
||||
<PublishItem publishSize="4162" publishTime="1700339789" publishDebug="true"/>
|
||||
<PublishItem publishSize="4121" publishTime="1700339614" publishDebug="true"/>
|
||||
@@ -27,12 +34,5 @@
|
||||
<PublishItem publishSize="1625" publishTime="1414396836"/>
|
||||
<PublishItem publishSize="1620" publishTime="1414395905"/>
|
||||
<PublishItem publishSize="560" publishTime="1414395873"/>
|
||||
<PublishItem publishSize="559" publishTime="1414395847"/>
|
||||
<PublishItem publishSize="559" publishTime="1414395804"/>
|
||||
<PublishItem publishSize="1587" publishTime="1414395524"/>
|
||||
<PublishItem publishSize="1647" publishTime="1414395264"/>
|
||||
<PublishItem publishSize="1668" publishTime="1414394172"/>
|
||||
<PublishItem publishSize="864" publishTime="1414335994"/>
|
||||
<PublishItem publishSize="533" publishTime="1414335971"/>
|
||||
</publishHistory>
|
||||
</DOMDocument>
|
||||
@@ -5,8 +5,8 @@
|
||||
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
|
||||
<xmp:CreatorTool>Adobe Flash Professional CS6 - build 537</xmp:CreatorTool>
|
||||
<xmp:CreateDate>2014-10-26T15:59:21+01:00</xmp:CreateDate>
|
||||
<xmp:MetadataDate>2023-11-18T12:05:50-08:00</xmp:MetadataDate>
|
||||
<xmp:ModifyDate>2023-11-18T12:05:50-08:00</xmp:ModifyDate>
|
||||
<xmp:MetadataDate>2023-11-19T08:32:38-08:00</xmp:MetadataDate>
|
||||
<xmp:ModifyDate>2023-11-19T08:32:38-08:00</xmp:ModifyDate>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
@@ -15,7 +15,7 @@
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
|
||||
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#">
|
||||
<xmpMM:InstanceID>xmp.iid:CDA31DDF4D86EE11B43B9B2E6D6D7C97</xmpMM:InstanceID>
|
||||
<xmpMM:InstanceID>xmp.iid:D4132A45B886EE11B43B9B2E6D6D7C97</xmpMM:InstanceID>
|
||||
<xmpMM:DocumentID>xmp.did:1476A545885CE411B13FACEEFCD7D43C</xmpMM:DocumentID>
|
||||
<xmpMM:OriginalDocumentID>xmp.did:1476A545885CE411B13FACEEFCD7D43C</xmpMM:OriginalDocumentID>
|
||||
<xmpMM:History>
|
||||
@@ -50,6 +50,12 @@
|
||||
<stEvt:when>2014-10-26T15:59:21+01:00</stEvt:when>
|
||||
<stEvt:softwareAgent>Adobe Flash Professional CS6 - build 481</stEvt:softwareAgent>
|
||||
</rdf:li>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<stEvt:action>created</stEvt:action>
|
||||
<stEvt:instanceID>xmp.iid:D4132A45B886EE11B43B9B2E6D6D7C97</stEvt:instanceID>
|
||||
<stEvt:when>2014-10-26T15:59:21+01:00</stEvt:when>
|
||||
<stEvt:softwareAgent>Adobe Flash Professional CS6 - build 481</stEvt:softwareAgent>
|
||||
</rdf:li>
|
||||
</rdf:Seq>
|
||||
</xmpMM:History>
|
||||
</rdf:Description>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<PackagePaths></PackagePaths>
|
||||
<AS3PackagePaths>.</AS3PackagePaths>
|
||||
<AS3ConfigConst>CONFIG::FLASH_AUTHORING="true";</AS3ConfigConst>
|
||||
<DebuggingPermitted>0</DebuggingPermitted>
|
||||
<DebuggingPermitted>1</DebuggingPermitted>
|
||||
<DebuggingPassword></DebuggingPassword>
|
||||
<CompressMovie>1</CompressMovie>
|
||||
<CompressionType>0</CompressionType>
|
||||
|
||||
Binary file not shown.
@@ -421,6 +421,9 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
|
||||
|
||||
@Internal
|
||||
private ExporterInfo exporterInfo = null;
|
||||
|
||||
@Internal
|
||||
public String debuggerPackage = null;
|
||||
|
||||
private static AbcIndexing playerGlobalAbcIndex;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.gui;
|
||||
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebugAdapter;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebugListener;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.Debugger;
|
||||
import java.awt.BorderLayout;
|
||||
@@ -54,18 +55,13 @@ public class DebugLogDialog extends AppDialog {
|
||||
JScrollPane spane = new FasterScrollPane(logTextArea);
|
||||
spane.setPreferredSize(new Dimension(800, 500));
|
||||
|
||||
debug.addMessageListener(new DebugListener() {
|
||||
debug.addMessageListener(new DebugAdapter() {
|
||||
|
||||
@Override
|
||||
public void onMessage(String clientId, String msg) {
|
||||
log(translate("msg.header").replace("%clientid%", clientId) + msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(String clientId) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoaderURL(String clientId, String url) {
|
||||
log(translate("msg.header").replace("%clientid%", clientId) + " LOADURL:" + url);
|
||||
@@ -75,6 +71,11 @@ public class DebugLogDialog extends AppDialog {
|
||||
public void onLoaderBytes(String clientId, byte[] data) {
|
||||
log(translate("msg.header").replace("%clientid%", clientId) + " LOADBYTES: " + data.length + "B");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDumpByteArray(String clientId, byte[] data) {
|
||||
log(translate("msg.header").replace("%clientid%", clientId) + " DUMPBYTEARRAY: " + data.length + "B");
|
||||
}
|
||||
});
|
||||
Container cnt = getContentPane();
|
||||
cnt.setLayout(new BorderLayout());
|
||||
|
||||
@@ -20,9 +20,14 @@ import com.jpexs.debugger.flash.Variable;
|
||||
import com.jpexs.debugger.flash.messages.in.InBreakAtExt;
|
||||
import com.jpexs.debugger.flash.messages.in.InConstantPool;
|
||||
import com.jpexs.debugger.flash.messages.in.InFrame;
|
||||
import com.jpexs.debugger.flash.messages.in.InGetVariable;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.gui.DebuggerHandler.BreakListener;
|
||||
import com.jpexs.decompiler.flash.gui.abc.ABCPanel;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebugAdapter;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebugListener;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.Debugger;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import de.hameister.treetable.MyTreeTable;
|
||||
import de.hameister.treetable.MyTreeTableModel;
|
||||
@@ -38,6 +43,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -201,19 +207,66 @@ public class DebugPanel extends JPanel {
|
||||
if (isByteArray) {
|
||||
JMenu exportMenu = new JMenu(AppStrings.translate("debug.export").replace("%name%", v.name));
|
||||
JMenuItem exportByteArrayMenuItem = new JMenuItem(AppStrings.translate("debug.export.bytearray"));
|
||||
exportByteArrayMenuItem.addActionListener((ActionEvent e1) -> {
|
||||
exportByteArrayMenuItem.addActionListener((ActionEvent e1) -> {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setCurrentDirectory(new File(Configuration.lastExportDir.get()));
|
||||
if (fc.showSaveDialog(Main.getDefaultMessagesComponent()) == JFileChooser.APPROVE_OPTION) {
|
||||
File file = Helper.fixDialogFile(fc.getSelectedFile());
|
||||
|
||||
//Variant with direct calling readByte - SLOW
|
||||
/*
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
Main.debugExportByteArray(v, fos);
|
||||
fos.write(data);
|
||||
Configuration.lastExportDir.set(file.getParentFile().getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
*/
|
||||
//Asynchronous variant
|
||||
/*DebuggerTools.initDebugger().addMessageListener(new DebugAdapter() {
|
||||
|
||||
@Override
|
||||
public void onDumpByteArray(String clientId, byte[] data) {
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(data);
|
||||
Configuration.lastExportDir.set(file.getParentFile().getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
DebuggerTools.initDebugger().removeMessageListener(this);
|
||||
}
|
||||
});
|
||||
Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent;
|
||||
try {
|
||||
Main.getDebugHandler().callMethod(debugConnectionClass, "writeMsg", Arrays.asList(v, (Double) (double) Debugger.MSG_DUMP_BYTEARRAY));
|
||||
} catch (DebuggerHandler.ActionScriptException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex);
|
||||
}*/
|
||||
|
||||
//Variant using comma separated bytes, pretty fast
|
||||
try {
|
||||
Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent;
|
||||
String dataStr = (String) Main.getDebugHandler().callMethod(debugConnectionClass, "readCommaSeparatedFromByteArray", Arrays.asList(v)).variables.get(0).value;
|
||||
String[] parts = dataStr.split(",");
|
||||
byte[] data = new byte[parts.length];
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
data[i] = (byte) Integer.parseInt(parts[i]);
|
||||
}
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(data);
|
||||
Configuration.lastExportDir.set(file.getParentFile().getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (DebuggerHandler.ActionScriptException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exportMenu.add(exportByteArrayMenuItem);
|
||||
@@ -227,11 +280,57 @@ public class DebugPanel extends JPanel {
|
||||
if (fc.showOpenDialog(Main.getDefaultMessagesComponent()) == JFileChooser.APPROVE_OPTION) {
|
||||
File file = Helper.fixDialogFile(fc.getSelectedFile());
|
||||
Configuration.lastOpenDir.set(file.getParentFile().getAbsolutePath());
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
|
||||
//Variant with asynchronous connection
|
||||
/*DebuggerTools.initDebugger().addMessageListener(new DebugAdapter() {
|
||||
@Override
|
||||
public byte[] onRequestBytes(String clientId) {
|
||||
byte[] data = null;
|
||||
try {
|
||||
data = Helper.readFileEx(file.getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
DebuggerTools.initDebugger().removeMessageListener(this);
|
||||
return data;
|
||||
}
|
||||
});
|
||||
Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent;
|
||||
try {
|
||||
Main.getDebugHandler().callMethod(debugConnectionClass, "writeMsg", Arrays.asList(v, (Double) (double) Debugger.MSG_REQUEST_BYTEARRAY));
|
||||
} catch (DebuggerHandler.ActionScriptException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex);
|
||||
}*/
|
||||
|
||||
//Variant with direct writeByte calls - SLOW
|
||||
/*try (FileInputStream fis = new FileInputStream(file)) {
|
||||
Main.debugImportByteArray(v, fis);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}*/
|
||||
|
||||
//Variant with using comma separated bytes, pretty fast
|
||||
try {
|
||||
byte[] data = Helper.readFileEx(file.getAbsolutePath());
|
||||
String splitter = "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
sb.append(splitter);
|
||||
sb.append(data[i] & 0xff);
|
||||
splitter = ",";
|
||||
}
|
||||
String dataStr = sb.toString();
|
||||
Variable debugConnectionClass = Main.getDebugHandler().getVariable(0, Main.currentDebuggerPackage + "::DebugConnection", false, false).parent;
|
||||
try {
|
||||
Main.getDebugHandler().callMethod(debugConnectionClass, "writeCommaSeparatedToByteArray", Arrays.asList(dataStr, v));
|
||||
} catch (DebuggerHandler.ActionScriptException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, "Error exporting ByteArray", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DebugPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
ViewMessages.showMessageDialog(Main.getDefaultMessagesComponent(), AppStrings.translate("error.file.save") + ": " + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -264,8 +363,8 @@ public class DebugPanel extends JPanel {
|
||||
addWatchMenu.add(watchReadMenuItem);
|
||||
addWatchMenu.add(watchWriteMenuItem);
|
||||
addWatchMenu.add(watchReadWriteMenuItem);
|
||||
pm.add(addWatchMenu);
|
||||
pm.show(e.getComponent(), e.getX(), e.getY());
|
||||
pm.add(addWatchMenu);
|
||||
pm.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.jpexs.decompiler.flash.console.CommandLineArgumentParser;
|
||||
import com.jpexs.decompiler.flash.console.ContextMenuTools;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode;
|
||||
import com.jpexs.decompiler.flash.gfx.GfxConvertor;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebugAdapter;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebugListener;
|
||||
import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools;
|
||||
import com.jpexs.decompiler.flash.gui.pipes.FirstInstance;
|
||||
@@ -217,6 +218,8 @@ public class Main {
|
||||
public static CancellableWorker importWorker = null;
|
||||
public static CancellableWorker deobfuscatePCodeWorker = null;
|
||||
public static CancellableWorker swfPrepareWorker = null;
|
||||
|
||||
public static String currentDebuggerPackage = null;
|
||||
|
||||
public static boolean isSwfAir(Openable openable) {
|
||||
SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(openable.getShortPathTitle());
|
||||
@@ -524,7 +527,13 @@ public class Main {
|
||||
}
|
||||
instrSWF.removeEventListener(prepEventListner);
|
||||
|
||||
instrSWF = super.prepare(instrSWF);
|
||||
//instrSWF = super.prepare(instrSWF);
|
||||
|
||||
if (!DebuggerTools.hasDebugger(instrSWF)) {
|
||||
DebuggerTools.switchDebugger(instrSWF);
|
||||
}
|
||||
DebuggerTools.injectDebugLoader(instrSWF);
|
||||
currentDebuggerPackage = instrSWF.debuggerPackage;
|
||||
|
||||
return instrSWF;
|
||||
}
|
||||
@@ -2399,15 +2408,8 @@ public class Main {
|
||||
watcherWorker.execute();
|
||||
}
|
||||
|
||||
DebuggerTools.initDebugger().addMessageListener(new DebugListener() {
|
||||
@Override
|
||||
public void onMessage(String clientId, String msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderURL(String clientId, String url) {
|
||||
}
|
||||
|
||||
DebuggerTools.initDebugger().addMessageListener(new DebugAdapter() {
|
||||
|
||||
@Override
|
||||
public void onLoaderBytes(String clientId, byte[] data) {
|
||||
String hash = md5(data);
|
||||
@@ -2438,11 +2440,7 @@ public class Main {
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Cannot create tempfile");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(String clientId) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2023 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.gui.debugger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class DebugAdapter implements DebugListener {
|
||||
|
||||
@Override
|
||||
public void onMessage(String clientId, String msg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderURL(String clientId, String url) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderBytes(String clientId, byte[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDumpByteArray(String clientId, byte[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(String clientId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] onRequestBytes(String clientId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,10 @@ public interface DebugListener {
|
||||
public void onLoaderURL(String clientId, String url);
|
||||
|
||||
public void onLoaderBytes(String clientId, byte[] data);
|
||||
|
||||
public void onDumpByteArray(String clientId, byte[] data);
|
||||
|
||||
public void onFinish(String clientId);
|
||||
|
||||
public byte[] onRequestBytes(String clientId);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -37,8 +39,29 @@ import java.util.WeakHashMap;
|
||||
*/
|
||||
public class Debugger {
|
||||
|
||||
public static final int MSG_STRING = 0;
|
||||
|
||||
public static final int MSG_LOADER_URL = 1;
|
||||
|
||||
public static final int MSG_LOADER_BYTES = 2;
|
||||
|
||||
public static final int MSG_DUMP_BYTEARRAY = 3;
|
||||
|
||||
public static final int MSG_REQUEST_BYTEARRAY = 4;
|
||||
|
||||
|
||||
private static final Set<DebugListener> listeners = new HashSet<>();
|
||||
|
||||
private static Logger logger = Logger.getLogger(Debugger.class.getName());
|
||||
|
||||
private static boolean active = false;
|
||||
|
||||
public static boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public synchronized void addMessageListener(DebugListener l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
@@ -61,12 +84,7 @@ public class Debugger {
|
||||
|
||||
private final Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
public static final int MSG_STRING = 0;
|
||||
|
||||
public static final int MSG_LOADER_URL = 1;
|
||||
|
||||
public static final int MSG_LOADER_BYTES = 2;
|
||||
|
||||
|
||||
public String getParameter(String name, String defValue) {
|
||||
if (parameters.containsKey(name)) {
|
||||
return parameters.get(name);
|
||||
@@ -108,6 +126,14 @@ public class Debugger {
|
||||
return type;
|
||||
}
|
||||
|
||||
private void writeBytes(OutputStream os, byte[] data) throws IOException {
|
||||
os.write((data.length >> 24) & 0xff);
|
||||
os.write((data.length >> 16) & 0xff);
|
||||
os.write((data.length >> 8) & 0xff);
|
||||
os.write(data.length & 0xff);
|
||||
os.write(data);
|
||||
}
|
||||
|
||||
private byte[] readBytes(InputStream is) throws IOException {
|
||||
int len = is.read();
|
||||
if (len == -1) {
|
||||
@@ -161,7 +187,8 @@ public class Debugger {
|
||||
@Override
|
||||
public void run() {
|
||||
String clientName = Integer.toString(id);
|
||||
try (InputStream is = s.getInputStream()) {
|
||||
try (InputStream is = s.getInputStream();
|
||||
OutputStream os = s.getOutputStream()) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
int c;
|
||||
@@ -175,11 +202,10 @@ public class Debugger {
|
||||
String ret = baos.toString("UTF-8");
|
||||
|
||||
if (ret.equals("<policy-file-request/>")) {
|
||||
try (OutputStream os = s.getOutputStream()) {
|
||||
os.write(("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"" + serverPort + "\" /></cross-domain-policy>").getBytes("UTF-8"));
|
||||
}
|
||||
os.write(("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" secure=\"false\" /></cross-domain-policy>").getBytes("UTF-8"));
|
||||
} else {
|
||||
if (!ret.isEmpty()) {
|
||||
logger.log(Level.FINER, "Connected. Header: {0}", ret);
|
||||
String[] param = (ret.contains(";") ? ret.split(";") : new String[]{ret});
|
||||
for (String p : param) {
|
||||
if (p.contains("=")) {
|
||||
@@ -190,35 +216,79 @@ public class Debugger {
|
||||
parameters.put(p, "true");
|
||||
}
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
boolean hasType = hasMsgType();
|
||||
logger.log(Level.FINER, "reading name...");
|
||||
String name = readString(is);
|
||||
logger.log(Level.FINER, "name = {0}", name);
|
||||
if (!name.isEmpty()) {
|
||||
clientName = name;
|
||||
}
|
||||
while (true) {
|
||||
int type = 0;
|
||||
logger.finer("reading type...");
|
||||
if (hasType) {
|
||||
type = readType(is);
|
||||
}
|
||||
logger.log(Level.FINE, "received type {0}", type);
|
||||
switch (type) {
|
||||
case MSG_STRING:
|
||||
logger.finer("reading string...");
|
||||
ret = readString(is);
|
||||
logger.finer("informing listeners...");
|
||||
for (DebugListener l : listeners) {
|
||||
l.onMessage(clientName, ret);
|
||||
}
|
||||
logger.finer("listeners informed");
|
||||
break;
|
||||
case MSG_LOADER_URL:
|
||||
logger.finer("reading string...");
|
||||
ret = readString(is);
|
||||
logger.finer("informing listeners...");
|
||||
for (DebugListener l : listeners) {
|
||||
l.onLoaderURL(clientName, ret);
|
||||
}
|
||||
logger.finer("listeners informed");
|
||||
break;
|
||||
case MSG_LOADER_BYTES:
|
||||
logger.finer("reading bytes...");
|
||||
byte[] retB = readBytes(is);
|
||||
logger.finer("informing listeners...");
|
||||
for (DebugListener l : listeners) {
|
||||
l.onLoaderBytes(clientName, retB);
|
||||
}
|
||||
logger.finer("listeners informed");
|
||||
break;
|
||||
case MSG_DUMP_BYTEARRAY:
|
||||
logger.finer("reading bytes...");
|
||||
byte[] retBa = readBytes(is);
|
||||
logger.finer("informing listeners...");
|
||||
for (DebugListener l : listeners) {
|
||||
l.onDumpByteArray(clientName, retBa);
|
||||
}
|
||||
logger.finer("listeners informed");
|
||||
break;
|
||||
case MSG_REQUEST_BYTEARRAY:
|
||||
logger.finer("checking listeners for data...");
|
||||
boolean dataFound = false;
|
||||
for (DebugListener l : listeners) {
|
||||
byte[] data = l.onRequestBytes(clientName);
|
||||
if (data != null) {
|
||||
logger.finer("found listener with data");
|
||||
logger.log(Level.FINER, "writing data.length = {0}", data.length);
|
||||
writeBytes(os, data);
|
||||
logger.finer("data written");
|
||||
dataFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dataFound) {
|
||||
logger.finer("listener not found, writing empty array");
|
||||
writeBytes(os, new byte[0]);
|
||||
}
|
||||
os.flush();
|
||||
logger.finer("listeners checked");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -233,6 +303,7 @@ public class Debugger {
|
||||
//ignore
|
||||
}
|
||||
finished = true;
|
||||
active = false;
|
||||
for (DebugListener l : listeners) {
|
||||
l.onFinish(clientName);
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ public class DebuggerTools {
|
||||
|
||||
public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger";
|
||||
|
||||
private static volatile Debugger debugger;
|
||||
|
||||
private static volatile Debugger debugger;
|
||||
|
||||
private static ScriptPack getDebuggerScriptPack(SWF swf) {
|
||||
List<ABC> allAbcList = new ArrayList<>();
|
||||
for (ABCContainerTag ac : swf.getAbcList()) {
|
||||
@@ -234,6 +234,7 @@ public class DebuggerTools {
|
||||
if (Configuration.randomDebuggerPackage.get()) {
|
||||
newdebuggerpkg += ".pkg" + rhex;
|
||||
}
|
||||
swf.debuggerPackage = newdebuggerpkg;
|
||||
|
||||
//add debug ABC tags to main SWF
|
||||
for (ABCContainerTag ds : debugSWF.getAbcList()) {
|
||||
@@ -261,6 +262,46 @@ public class DebuggerTools {
|
||||
ft.useNetwork = true;
|
||||
ft.setModified(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Add call to DebugConnection.initClient("") to the document class
|
||||
/*String documentClass = swf.getDocumentClass();
|
||||
if (documentClass != null) {
|
||||
List<String> searchClassNames = new ArrayList<>();
|
||||
searchClassNames.add(documentClass);
|
||||
List<ScriptPack> documentClassPacks = swf.getScriptPacksByClassNames(searchClassNames);
|
||||
if (!documentClassPacks.isEmpty()) {
|
||||
ScriptPack documentClassPack = documentClassPacks.get(0);
|
||||
Trait publicTrait = documentClassPack.getPublicTrait();
|
||||
if (publicTrait != null) {
|
||||
if (publicTrait instanceof TraitClass) {
|
||||
TraitClass classTrait = (TraitClass) publicTrait;
|
||||
int classIndex = classTrait.class_info;
|
||||
ABC a = documentClassPack.abc;
|
||||
int cinitMethodInfo = a.class_info.get(classIndex).cinit_index;
|
||||
MethodBody body = a.findBody(cinitMethodInfo);
|
||||
AVM2Code code = body.getCode();
|
||||
int debugConnectionMultiname = a.constants.getMultinameId(
|
||||
Multiname.createQName(false, a.constants.getStringId("DebugConnection", true),
|
||||
a.constants.getNamespaceId(Namespace.KIND_PACKAGE, newdebuggerpkg, 0, true)
|
||||
), true);
|
||||
int initClientMultiname = a.constants.getMultinameId(
|
||||
Multiname.createQName(false, a.constants.getStringId("initClient", true),
|
||||
a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true)
|
||||
), true);
|
||||
code.insertInstruction(0, new AVM2Instruction(0, AVM2Instructions.GetLex, new int[] {debugConnectionMultiname}), true, body);
|
||||
code.insertInstruction(1, new AVM2Instruction(0, AVM2Instructions.PushString, new int[] {a.constants.getStringId("", true)}), true, body);
|
||||
code.insertInstruction(2, new AVM2Instruction(0, AVM2Instructions.CallPropVoid, new int[] {initClientMultiname, 1}), true, body);
|
||||
if (body.max_stack < 2) {
|
||||
body.max_stack = 2;
|
||||
}
|
||||
body.setModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error while attaching debugger", ex);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user