/* * 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 2, 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * http://www.gnu.org/copyleft/gpl.html */ package com.aionemu.packetsamurai.gui; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.net.Inet4Address; import javax.swing.DefaultListSelectionModel; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.StyledDocument; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import org.jdesktop.swingx.JXTreeTable; import com.aionemu.packetsamurai.PacketSamurai; import com.aionemu.packetsamurai.Util; import com.aionemu.packetsamurai.gui.images.IconsTable; import com.aionemu.packetsamurai.parser.PartType; import com.aionemu.packetsamurai.parser.datatree.ValuePart; import com.aionemu.packetsamurai.protocol.PacketId.IdPart; import com.aionemu.packetsamurai.session.DataPacket; import com.aionemu.packetsamurai.session.GameSessionViewer; import com.aionemu.packetsamurai.session.Session; /** * @author Ulysses R. Ribeiro * */ @SuppressWarnings("serial") public class ViewPane extends JPanel { private GridBagLayout _layout = new GridBagLayout(); // Details private SessionDetailPanel _detailsPanel; //Packet Table private PacketTableModel _packetTableModel; private JTable _packetTable; private int _currentSelectedPacket = -1; //Hex Dump private JTextPane _hexDumpArea; private DefaultStyledDocument _hexStyledDoc; //Format Entry private JTextField _packetFormat; //Packet View Table private PacketViewTableModel _packetViewTableModel; private JXTreeTable _packetViewTable; private DataPartNode _currentSelectedPart = null; private GameSessionViewer _gameSessionViewer; private FilterDlg _filterDlg; public ViewPane(GameSessionViewer gsv) { _gameSessionViewer = gsv; this.setLayout(_layout); GridBagConstraints cons = new GridBagConstraints(); cons.insets = new Insets(5,5,5,5); cons.weightx = 0.5; cons.gridy = 0; cons.gridheight = 5; cons.weighty = 0.95; cons.fill = GridBagConstraints.BOTH; _packetTableModel = new PacketTableModel(); _packetTable = new JTable(_packetTableModel); _packetTable.addMouseListener(new PacketTableMouseListener()); _packetTable.setDefaultRenderer(Object.class, new PacketTableRenderer(_packetTableModel)); _packetTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); _packetTable.getSelectionModel().addListSelectionListener(new PacketSelectionListener()); _packetTable.getColumnModel().getColumn(0).setMaxWidth(35); _packetTable.getColumnModel().getColumn(1).setMaxWidth(50); _packetTable.getColumnModel().getColumn(2).setMaxWidth(90); _packetTable.getColumnModel().getColumn(3).setMaxWidth(45); JScrollPane scrollPane = new JScrollPane(_packetTable); this.add(scrollPane,cons); cons.fill = GridBagConstraints.BOTH; //cons.anchor = GridBagConstraints.NORTH; cons.weightx = 0.5; cons.weighty = 0.5; cons.gridx = 1; cons.gridy = 1; cons.gridheight = 1; _hexDumpArea = new JTextPane(); _hexDumpArea.setPreferredSize(new Dimension(100,317)); //_hexDumpArea.setDoubleBuffered(true); _hexStyledDoc = (DefaultStyledDocument) _hexDumpArea.getStyledDocument(); addStylesToHexDump(_hexStyledDoc); //Font font = new Font("Monospaced", Font.PLAIN, 12); //_hexDumpArea.setFont(font); //_hexDumpArea.setRows(18); //_hexDumpArea.setColumns(20); JScrollPane hexDumpScrollPane = new JScrollPane(_hexDumpArea,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); this.add(hexDumpScrollPane,cons); JPanel smallPane = new JPanel(); smallPane.setLayout(_layout); cons.fill = GridBagConstraints.NONE; cons.weightx = 0.05; cons.weighty = 0.1; cons.gridx = 0; cons.gridy = 2; cons.gridheight = 1; JLabel label = new JLabel("Format:"); _packetFormat = new JTextField(); _packetFormat.setEditable(false); cons.anchor = GridBagConstraints.WEST; smallPane.add(label,cons); cons.gridx = 1; cons.weightx = 0.95; cons.fill = GridBagConstraints.HORIZONTAL; smallPane.add(_packetFormat,cons); cons.weightx = 0.5; this.add(smallPane,cons); cons.fill = GridBagConstraints.BOTH; cons.weightx = 0.5; cons.weighty = 0.4; cons.gridx = 1; cons.gridy = 4; cons.gridheight = 1; _packetViewTableModel = new PacketViewTableModel(null); _packetViewTable = new JXTreeTable(_packetViewTableModel); _packetViewTable.setSelectionModel(new DefaultListSelectionModel()); _packetViewTable.getSelectionModel().addListSelectionListener(new PartSelectionListener(_packetViewTable)); _packetViewTable.setDefaultRenderer(Object.class, new IconTableRenderer()); _packetViewTable.addMouseListener(new JTableButtonMouseListener(_packetViewTable)); _packetViewTable.setTreeCellRenderer(new PacketViewTreeRenderer()); JScrollPane packetViewScrollPane = new JScrollPane(_packetViewTable); this.add(packetViewScrollPane,cons); cons.fill = GridBagConstraints.BOTH; cons.anchor = GridBagConstraints.NORTH; cons.gridx = 1; cons.gridy = 0; cons.weighty = 0.1; cons.gridwidth = 1; _detailsPanel = new SessionDetailPanel(_gameSessionViewer.getSession()); this.add(_detailsPanel,cons); this.displaySession(); } public void setSelectedPacket(int startIndex, int endIndex) { this.getPacketTable().setAutoscrolls(true); this.getPacketTable().getSelectionModel().setSelectionInterval(startIndex, endIndex); this.getPacketTable().scrollRectToVisible(this.getPacketTable().getCellRect(startIndex, 0, true)); } private void addStylesToHexDump(StyledDocument doc) { Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE); Style base = doc.addStyle("base", def); StyleConstants.setFontFamily(base, "Monospaced"); Style regular = doc.addStyle("regular", base); //StyleConstants.setUnderline(regular , true); Style s = doc.addStyle("d", regular); StyleConstants.setBackground(s, new Color(72,164,255)); s = doc.addStyle("Q", regular); StyleConstants.setBackground(s, new Color(255,255,128)); s = doc.addStyle("h", regular); StyleConstants.setBackground(s, Color.ORANGE); s = doc.addStyle("s", regular); StyleConstants.setBackground(s, new Color(156,220,156)); s = doc.addStyle("S", regular); StyleConstants.setBackground(s, new Color(156,220,156)); s = doc.addStyle("c", regular); StyleConstants.setBackground(s, Color.PINK); s = doc.addStyle("f", regular); StyleConstants.setBackground(s, Color.LIGHT_GRAY); Color bxColor = new Color(255,234,213); s = doc.addStyle("b", regular); StyleConstants.setBackground(s, bxColor); s = doc.addStyle("x", regular); StyleConstants.setBackground(s, bxColor); s = doc.addStyle("op", regular); StyleConstants.setBackground(s, Color.YELLOW); s = doc.addStyle("selected", regular); StyleConstants.setBackground(s, Color.BLUE); s = doc.addStyle("chk", regular); StyleConstants.setBackground(s, Color.GREEN); } public void addStyledText(String text, String style) { this.addStyledText(text, style, false); } public void addStyledText(String text, String style, boolean index) { Style s = _hexStyledDoc.getStyle(style); if (s == null) { PacketSamurai.getUserInterface().log("WARNING: Missing style for partType: "+style); style = "base"; } try { _hexStyledDoc.insertString(_hexStyledDoc.getLength(), text, _hexStyledDoc.getStyle(style)); } catch (BadLocationException e) { e.printStackTrace(); } } public void highlightSelectedPart(DataPartNode node) { Style style = _hexStyledDoc.getStyle("selected"); try { String text = _hexStyledDoc.getText(node.getOffset(), node.getLength()); //_hexStyledDoc.remove(node.getOffset(), node.getLength()); //_hexStyledDoc.insertString(node.getOffset(), text, style); _hexStyledDoc.replace(node.getOffset(), node.getLength(), text, style); _hexDumpArea.setCaretPosition(node.getOffset()); } catch (BadLocationException e) { e.printStackTrace(); } } public FilterDlg getFilterDialog() { if (_filterDlg == null) _filterDlg = new FilterDlg(((Main) PacketSamurai.getUserInterface()).getMainFrame(), this); return _filterDlg; } public void displaySession() { int size = this.getGameSessionViewer().getAllPackets().size(); DataPacket gp; System.out.println("Size of Packet list: "+size); PacketTableModel packetTableModel = this.getPacketTableModel(); packetTableModel.reinit(size); if(size > 0) { long startTime = this.getGameSessionViewer().getAllPackets().get(0).getTimeStamp(); long teste = System.currentTimeMillis(); for (int i =0; i < size; i++) { gp = this.getGameSessionViewer().getPacket(i); // filtering if (this.getFilterDialog().isFilterEnabledFor(gp)) continue; packetTableModel.addRow(gp, startTime); } System.out.println("done in: "+((System.currentTimeMillis()-teste))+" ms"); } SwingUtilities.invokeLater ( new Runnable() { public void run() { ViewPane.this.getPacketTable().updateUI(); } } ); } public JTable getPacketTable() { //OMG that's hacky! here to update packet count as we usualy do a getPacketTable() to update the ui after adding packets _detailsPanel.setPacketCount(_packetTableModel.getRowCount()); return _packetTable; } public PacketTableModel getPacketTableModel() { return _packetTableModel; } public JTable getPacketViewTable() { return _packetViewTable; } public PacketViewTableModel getPacketViewTableModel() { return _packetViewTableModel; } public GameSessionViewer getGameSessionViewer() { return _gameSessionViewer; } public void updateCurrentPacket() { this.updateCurrentPacket(false); } public void updateCurrentPacket(boolean forced) { if (!forced && _currentSelectedPacket == _packetTable.getSelectedRow()) return; /*try { DataStructure gp = _gameSessionViewer.getPacket(_currentSelectedPacket); if(gp!= null) gp.setViewed(false); } catch (IndexOutOfBoundsException e) { }*/ _currentSelectedPacket = _packetTable.getSelectedRow(); // reset the selected part as we selected another packet _currentSelectedPart = null; DataPacket gp = _gameSessionViewer.getPacket(_currentSelectedPacket); //gp.setViewed(true); //System.out.println("Selected row/packet: "+_table.getSelectedRow()); _hexDumpArea.setText(""); if (gp.getPacketFormat() != null && gp.isValid()) { _packetFormat.setText(gp.getPacketFormat().getPartsStr()); //colorfull hex dump :P //opcode if (gp.getPacketId() != null) { for (IdPart part : gp.getPacketId().getParts()) { int bytes = part.getType().getTypeByteNumber(); for(int i2 = 0; i2 < bytes; i2++) { addStyledText(Util.zeropad(Long.toHexString(part.getValue() << (8*i2)&0xff),2).toUpperCase(),"op"); if(i2 < bytes - 1) addStyledText(" ", "op"); } addStyledText(" ", "base"); } } //chksum if(gp.getProtocol().getChecksumSize() > 0) { int size = gp.getProtocol().getChecksumSize(); for(int i =1; i < size+1; i++) { addStyledText(Util.zeropad(Integer.toHexString(gp.getIdData()[i]&0xff),2).toUpperCase(),"chk"); if(i < size) addStyledText(" ", "chk"); } addStyledText(" ", "base"); } //parsed packet parts for (ValuePart part : gp.getValuePartList()) { addStyledText(Util.rawHexDump(part.getBytes()),part.getType().getName(), true); addStyledText(" ", "base"); } //unparsed data (if any) addStyledText(Util.rawHexDump(gp.getUnparsedData()),"base"); this.addLineBreaksToHexDump(gp.getIdData(), gp.getByteBuffer().array()); int opSize = 0; for (PartType pt : gp.getPacketFormat().getIdParts()) { opSize += pt.getTypeByteNumber()*3; } DataPartNode root = new DataPartNode(gp.getRootNode(), opSize+gp.getProtocol().getChecksumSize()*3); _packetViewTableModel.setRoot(root); _packetViewTable.revalidate(); _packetViewTable.expandAll(); } else { // clear the format _packetFormat.setText(""); // dump everything _hexDumpArea.setText(""); // if we know the opcode but the format was invalid still show the opcode if (gp.getFormat() != null) { for (IdPart part : gp.getPacketId().getParts()) { int bytes = part.getType().getTypeByteNumber(); for(int i2 = 0; i2 < bytes; i2++) { addStyledText(Util.zeropad(Long.toHexString(part.getValue() << (8*i2)&0xff),2).toUpperCase(),"op"); if(i2 < bytes - 1) addStyledText(" ", "op"); } addStyledText(" ", "base"); } } ViewPane.this.addStyledText(Util.rawHexDump(gp.getData()), "base"); this.addLineBreaksToHexDump(gp.getIdData(),gp.getByteBuffer().array()); // no part for u _packetViewTableModel.setRoot(null); } } private void addLineBreaksToHexDump(byte[] id, byte[] data) { //add linefeeds to the dump int lnCount = _hexDumpArea.getText().length()/48; int rest = _hexDumpArea.getText().length()%48; for (int i = 1; i <= lnCount; i++) { int pos = i*67-20; try { int idx = i-1; String ansci = idx==0? (Util.toAnsci(id,0,id.length)+Util.toAnsci(data,0,16-id.length)) : Util.toAnsci(data,idx*16-id.length,idx*16+16-id.length); _hexStyledDoc.replace(pos, 1, " "+ansci+"\n", _hexStyledDoc.getStyle("base")); //_hexStyledDoc.remove(pos,1); //_hexStyledDoc.insertString(pos, "\n", _hexStyledDoc.getStyle("base")); } catch (BadLocationException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } //rest if(rest != 0) { try { int pos = lnCount*67+rest; String space = ""; int spaceCount = 48-rest; while(spaceCount-- > 0) space += " "; String ansci = lnCount==0 ?(Util.toAnsci(id,0,id.length)+Util.toAnsci(data,0,data.length-id.length)) : Util.toAnsci(data,lnCount*16-id.length,data.length-id.length); _hexStyledDoc.insertString(pos, space+" "+ansci, _hexStyledDoc.getStyle("base")); } catch (BadLocationException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } public int getSelectedPacketindex() { return _packetTable.getSelectedRow(); } private class SessionDetailPanel extends JPanel { private JLabel _port; private JLabel _protocol; private JLabel _serverType; private JLabel _packetCount; private JLabel _clientIP; private JLabel _serverIP; public SessionDetailPanel(Session s) { this.setLayout(new GridLayout(3,2)); _port = new JLabel("Port: "+s.getProtocol().getPort()); _protocol = new JLabel("Protocol: "+s.getProtocol().getName()); _serverType = new JLabel("Server Type: "+s.getServerType()); _packetCount = new JLabel("Packets: "+s.getPackets().size()); _clientIP = new JLabel("Client: "+(s.getClientIp() == null ? "N/A" : s.getClientIp().getHostAddress())); _serverIP = new JLabel("Server: "+(s.getServerIp() == null ? "N/A" : s.getServerIp().getHostAddress())); this.add(_port); this.add(_clientIP); this.add(_protocol); this.add(_serverIP); this.add(_serverType); this.add(_packetCount); } public void setPort(int port) { _port.setText("Port: "+port); } public void setProtocol(String protocol) { _protocol.setText("Protocol: "+protocol); } public void setServerType(String type) { _serverType.setText("Server Type: "+type); } public void setPacketCount(int count) { _packetCount.setText("Packets: "+count); } public void setClientIP(Inet4Address addr) { _clientIP.setText("Client: "+addr.toString()); } public void setServerIP(Inet4Address addr) { _serverIP.setText("Server: "+addr.toString()); } } public class PartSelectionListener implements ListSelectionListener { JXTreeTable _table; // It is necessary to keep the table since it is not possible // to determine the table from the event's source PartSelectionListener(JXTreeTable table) { _table = table; } public void valueChanged(ListSelectionEvent e) { if (e.getSource() == _table.getSelectionModel()) { _table.getPathForRow(_table.getSelectedRow()); // is there already a highlighted part? if (_currentSelectedPart != null) { // unhighlight previous selected text // restoring its style try { String text = _hexStyledDoc.getText(_currentSelectedPart.getOffset(), _currentSelectedPart.getLength()); _hexStyledDoc.remove(_currentSelectedPart.getOffset(), _currentSelectedPart.getLength()); _hexStyledDoc.insertString(_currentSelectedPart.getOffset(), text, _hexStyledDoc.getStyle(_currentSelectedPart.getPacketNode().getModelPart().getType().getName())); } catch (BadLocationException e1) { e1.printStackTrace(); } } TreePath tp = _table.getPathForRow(_table.getSelectedRow()); if (tp != null && ((DataPartNode) tp.getLastPathComponent()).isLeaf()) { _currentSelectedPart = (DataPartNode) tp.getLastPathComponent(); ViewPane.this.highlightSelectedPart(_currentSelectedPart); } } } } public class PacketSelectionListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { ViewPane view = ViewPane.this; // If cell selection is enabled, both row and column change events are fired if (e.getSource() == view.getPacketTable().getSelectionModel()) { view.updateCurrentPacket(); } else if (e.getSource() == view.getPacketTable().getColumnModel().getSelectionModel() && view.getPacketTable().getColumnSelectionAllowed() ) { } } } public class PacketViewTreeRenderer extends DefaultTreeCellRenderer { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (value instanceof DataPartNode && leaf) { //System.out.println(((DataPartNode) value).getPacketNode().getModelPart()); this.setText(((DataPartNode) value).getPacketNode().treeString()); this.setIcon(IconsTable.getInstance().getIconForPartType(((DataPartNode) value).getPacketNode().getModelPart().getType())); } return this; } } @Override public void finalize() { System.out.println("Finalized: "+this.getClass().getSimpleName()); } }