Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (107)
games submitted by our members
Games in WIP (536)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2 3
  ignore  |  Print  
  Why does the Basic Java Hex Editor I made freeze on files around 2MB size and up  (Read 6579 times)
0 Members and 1 Guest are viewing this topic.
Offline CyanPrime
« Posted 2010-10-11 07:44:05 »

Alright, so I'm writing a hex editor in Java and when I load in a file around 2MB or bigger it just freezes. No errors show up though, so I'm not sure whats going on. Can anyone help me? This is the code:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
   
    import javax.swing.*;
    import java.util.Vector;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
   
    public class HexEditor extends JFrame{
     JScrollPane hexScroll;
     JScrollPane byteScroll;
     JPanel panel;
     JTextArea hexArea;
     JTextArea byteArea;
     JFileChooser chooser;// = new JFileChooser();
    FileInputStream fin;
     JMenuBar menuBar;
     JMenu file;
      JMenuItem load;
   
     public HexEditor(){
      super("Cypri's java hex editor");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
      chooser = new JFileChooser();
     
      load = new JMenuItem("Load");
       load.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent event) {
         
         try{
         
          openFile();
          fin = new FileInputStream(chooser.getSelectedFile());
         
          int ch;
          StringBuffer strContent = new StringBuffer("");
         
          for(int i = 0; (ch = fin.read()) != -1; i++){
           String s = Integer.toHexString(ch);
           
           if(s.length() < 2)
            s = "0" + Integer.toHexString(ch);
           
           if(i < 10)
            strContent.append(" " + s.toUpperCase());
           
           else{
            strContent.append(" " + s.toUpperCase() + "\n");
            i = 0;
           }
          }
         
          hexArea.setText(strContent.toString());
         
          byte[] b = hexStringToByteArray(strContent.toString().trim());
          char[] chars = new char[b.length];
          String byteText = "";
          int newLine = 0;
          for(int i = 0; i < b.length; i++){
           chars[i] = (char) b[i];
           byteText += chars[i];
           
           newLine++;
           if(newLine > 10){
            byteText += "\n";
            newLine = 0;
           }
          }
         
          hexArea.setText(strContent.toString());
          byteArea.setText(byteText);
          packMe();
         }
         
         catch(Exception e){
          e.printStackTrace();
         }
        }
       });
     
      file = new JMenu("File");
      file.add(load);
     
      menuBar = new JMenuBar();
   
      menuBar.add(file);
     
      hexArea = new JTextArea();
      byteArea = new JTextArea();
     
      hexScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
      byteScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
     
      panel = new JPanel();
     
      panel.add(hexScroll);
      panel.add(byteScroll);
      hexScroll.setViewportView(hexArea);
      byteScroll.setViewportView(byteArea);
     
      hexArea.setPreferredSize(new Dimension(200,200));
      byteArea.setPreferredSize(new Dimension(200,200));
     
      hexScroll.setPreferredSize(new Dimension(200,200));
      byteScroll.setPreferredSize(new Dimension(200,200));
   
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.NORTH, menuBar);
      getContentPane().add(BorderLayout.CENTER, panel);
      pack();
      setVisible(true);
     }
     
     public static byte[] hexStringToByteArray(String s) {
         int len = s.length() -1;
         byte[] data = new byte[(len / 2) + 1];
         for (int i = 0; i < len; i += 2) {
             data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
         }
         return data;
     }
   
     
     public void openFile(){
      chooser.showOpenDialog(null);
     }
     
     public void packMe(){
      pack();
     }
     
     public static void main(String[] args){
         HexEditor app = new HexEditor();
     }
    }
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #1 - Posted 2010-10-11 07:53:40 »

Probably just gets grogged down creating and concatenating Strings. You're using a StringBuffer in there, which is good, but you shouldn't be creating any strings at all if possible (i.e. two append() calls are better than myStr = "0" + xxxx).

Also, run in debug and pause execution when it appears frozen. See what it's doing. Also try printing out every 100th line or something so you can narrow down where it's breaking.

See my work:
OTC Software
Offline dime

Senior Newbie





« Reply #2 - Posted 2010-10-11 09:01:39 »


I don't use AWT, but looks like your loading the data in the main GUI thread.  This generally is very bad in most GUIs.

In SWT I would throw the load function to a new thread and async output to a progress window/bar.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline gouessej
« Reply #3 - Posted 2010-10-11 10:19:31 »

dime is right, perform your heavy operations on another thread. Why not using a SwingWorker?

Offline erikd

JGO Ninja


Medals: 16
Projects: 4
Exp: 14 years


Maximumisness


« Reply #4 - Posted 2010-10-11 10:24:00 »

Quote
dime is right, perform your heavy operations on another thread. Why not using a SwingWorker?
Perhaps because it's just a quick & dirty hexeditor and he couldn't be bothered with that stuff  Wink This is not really the problem here.

You get an OutOfMemoryError. Just give it more memory, or make it more memory efficient.

Offline CyanPrime
« Reply #5 - Posted 2010-10-11 20:41:00 »

Okay, I've changed my code but I'm still stuck in the loop, and it won't repaint/revalidate despite me telling it to in the loop.

New code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import java.util.Vector;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class HexEditor extends JFrame{
   JScrollPane hexScroll;
   JScrollPane byteScroll;
   JPanel panel;
   JTextArea hexArea;
   JTextArea byteArea;
   JFileChooser chooser;
   FileInputStream fin;
   JMenuBar menuBar;
   JMenu file;
      JMenuItem load;

   public HexEditor(){
      super("Cypri's java hex editor");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
      chooser = new JFileChooser();
     
      load = new JMenuItem("Load");
         load.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent event) {
               
               try{
                 
                  openFile();
                  fin = new FileInputStream(chooser.getSelectedFile());
                 
                  int ch;
                  StringBuffer strContent = new StringBuffer("");
                 
                  System.out.println("Load start.");
               
                  hexArea.setText("");
                 
                  while((ch = fin.read()) != -1){
                     
                     System.out.println("Loop start.");
                     
                     if(Integer.toHexString(ch).length() < 2){
                        hexArea.append(" 0" + Integer.toHexString(ch).toUpperCase());
                     }
                     
                     else{
                        hexArea.append(" " + Integer.toHexString(ch).toUpperCase());
                     }
                     
                     hexArea.revalidate();
                     hexScroll.revalidate();
                     panel.revalidate();
                     revalidateMe();
                     
                     System.out.println("Loop end.");
                  }

                  System.out.println("Out of loop.");
                  byteArea.setText("");
                  byteArea.append(hexStringToByteArray(strContent.toString().trim()).toString());
                  packMe();
               }
               
               catch(Exception e){
                  e.printStackTrace();
               }
            }
         });
     
      file = new JMenu("File");
      file.add(load);
     
      menuBar = new JMenuBar();

      menuBar.add(file);
     
      hexArea = new JTextArea();
      byteArea = new JTextArea();
      hexArea.setLineWrap(true);
      byteArea.setLineWrap(true);
   
      hexScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
      byteScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
     
      panel = new JPanel();
     
      panel.add(hexScroll);
      panel.add(byteScroll);
      hexScroll.setViewportView(hexArea);
      byteScroll.setViewportView(byteArea);
     
      hexScroll.setPreferredSize(new Dimension(440,480));
      byteScroll.setPreferredSize(new Dimension(200,480));

      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.NORTH, menuBar);
      getContentPane().add(BorderLayout.CENTER, panel);
      pack();
      setVisible(true);
   }
   
   public static byte[] hexStringToByteArray(String s) {
       int len = s.length() -1;
       byte[] data = new byte[(len / 2) + 1];
       for (int i = 0; i < len; i += 2) {
           data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
       }
       return data;
   }

   
   public void openFile(){
      chooser.showOpenDialog(null);
   }
   
   public void packMe(){
      pack();
   }
   
   public void revalidateMe(){
      repaint();
   }
   
   public static void main(String[] args){
       HexEditor app = new HexEditor();
   }
}

Offline CyanPrime
« Reply #6 - Posted 2010-10-11 20:52:18 »

Letting it run for a while in the loop gave me this error:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
   at javax.swing.text.GapContent.allocateArray(Unknown Source)
   at javax.swing.text.GapVector.resize(Unknown Source)
   at javax.swing.text.GapVector.shiftEnd(Unknown Source)
   at javax.swing.text.GapContent.shiftEnd(Unknown Source)
   at javax.swing.text.GapVector.open(Unknown Source)
   at javax.swing.text.GapVector.replace(Unknown Source)
   at javax.swing.text.GapContent.insertString(Unknown Source)
   at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source)
   at javax.swing.text.AbstractDocument.insertString(Unknown Source)
   at javax.swing.text.PlainDocument.insertString(Unknown Source)
   at javax.swing.JTextArea.append(Unknown Source)
   at HexEditor$1.actionPerformed(HexEditor.java:47)
   at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
   at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
   at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
   at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
   at javax.swing.AbstractButton.doClick(Unknown Source)
   at javax.swing.plaf.basic.BasicMenuItemUI.doClick(Unknown Source)
   at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(Unknown Source)
   at java.awt.Component.processMouseEvent(Unknown Source)
   at javax.swing.JComponent.processMouseEvent(Unknown Source)
   at java.awt.Component.processEvent(Unknown Source)
   at java.awt.Container.processEvent(Unknown Source)
   at java.awt.Component.dispatchEventImpl(Unknown Source)
   at java.awt.Container.dispatchEventImpl(Unknown Source)
   at java.awt.Component.dispatchEvent(Unknown Source)
   at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
   at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
   at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
   at java.awt.Container.dispatchEventImpl(Unknown Source)
   at java.awt.Window.dispatchEventImpl(Unknown Source)
   at java.awt.Component.dispatchEvent(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
   at javax.swing.text.GapContent.getChars(Unknown Source)
   at javax.swing.text.AbstractDocument.getText(Unknown Source)
   at javax.swing.text.WrappedPlainView.loadText(Unknown Source)
   at javax.swing.text.WrappedPlainView.calculateBreakPosition(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.breakLines(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.getLineEnds(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.paint(Unknown Source)
   at javax.swing.text.BoxView.paintChild(Unknown Source)
   at javax.swing.text.BoxView.paint(Unknown Source)
   at javax.swing.text.WrappedPlainView.paint(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI$RootView.paint(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI.paintSafely(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI.paint(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI.update(Unknown Source)
   at javax.swing.JComponent.paintComponent(Unknown Source)
   at javax.swing.JComponent.paint(Unknown Source)
   at javax.swing.JComponent.paintChildren(Unknown Source)
   at javax.swing.JComponent.paint(Unknown Source)
   at javax.swing.JViewport.paint(Unknown Source)
   at javax.swing.JComponent.paintChildren(Unknown Source)
   at javax.swing.JComponent.paint(Unknown Source)
   at javax.swing.JComponent.paintToOffscreen(Unknown Source)
   at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
   at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
   at javax.swing.RepaintManager.paint(Unknown Source)
   at javax.swing.JComponent._paintImmediately(Unknown Source)
   at javax.swing.JComponent.paintImmediately(Unknown Source)
   at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
   at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
   at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
   at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
   at java.awt.event.InvocationEvent.dispatch(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
   at javax.swing.text.GapContent.getChars(Unknown Source)
   at javax.swing.text.AbstractDocument.getText(Unknown Source)
   at javax.swing.text.WrappedPlainView.loadText(Unknown Source)
   at javax.swing.text.WrappedPlainView.calculateBreakPosition(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.breakLines(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.getLineEnds(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.modelToView(Unknown Source)
   at javax.swing.text.CompositeView.modelToView(Unknown Source)
   at javax.swing.text.BoxView.modelToView(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI$RootView.modelToView(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI.modelToView(Unknown Source)
   at javax.swing.text.DefaultCaret.repaintNewCaret(Unknown Source)
   at javax.swing.text.DefaultCaret$1.run(Unknown Source)
   at java.awt.event.InvocationEvent.dispatch(Unknown Source)
   at java.awt.EventQueue.dispatchEvent(Unknown Source)
   at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
   at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
   at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
   at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
   at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
   at java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
   at javax.swing.text.GapContent.getChars(Unknown Source)
   at javax.swing.text.AbstractDocument.getText(Unknown Source)
   at javax.swing.text.WrappedPlainView.loadText(Unknown Source)
   at javax.swing.text.WrappedPlainView.calculateBreakPosition(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.breakLines(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.getLineEnds(Unknown Source)
   at javax.swing.text.WrappedPlainView$WrappedLine.modelToView(Unknown Source)
   at javax.swing.text.CompositeView.modelToView(Unknown Source)
   at javax.swing.text.BoxView.modelToView(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI$RootView.modelToView(Unknown Source)
   at javax.swing.plaf.basic.BasicTextUI.modelToView(Unknown Source)
   at javax.swing.text.DefaultCaret.repaintNewCaret(Unknown Source)
   at javax.swing.text.DefaultCaret$1.run(Unknown Source)
   at java.awt.event.InvocationEvent.dispatch(Unknown Source)
   at java.awt.EventQueue.dispatchEvent(Unknown Source)
   at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
   at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
   at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
   at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
   at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
   at java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #7 - Posted 2010-10-11 21:31:22 »

As erikd just said, your heap is too low. Increase the min/max heap size and try again. Or try to be more efficient about how much is in memory at once time.

See my work:
OTC Software
Offline CyanPrime
« Reply #8 - Posted 2010-10-12 01:49:50 »

Okay, so Java seems to be way too slow to do this. I've gotten it to work more or less, but it's still way too slow. I'm thinking about trying this in C# and .net.
Offline BoBear2681

JGO Coder


Medals: 18



« Reply #9 - Posted 2010-10-12 03:56:22 »

As far as the performance and responsiveness, you shouldn't load the content into the text area on the EDT.  Either launch a separate Thread to read from the file or use a SwingWorker.  Doing file IO on the EDT as you're doing now will obviously cause freezes in the GUI.  Using a separate thread should improve the user experience 100%.

Prior to Java 7, JTextArea.append() was labeled as threadsafe in its Javadoc, but as of Java 7 they've taken that statement away, so that's something to keep in mind - might want to ensure that's called on the EDT.

It's probably also not a bad idea to wrap your FileInputStream in a BufferedInputStream, the benefits will be more noticeable the bigger your file is.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CyanPrime
« Reply #10 - Posted 2010-10-12 18:47:33 »

Alright the thread thing worked. It loads rather fast now. I'm impressed.
Offline erikd

JGO Ninja


Medals: 16
Projects: 4
Exp: 14 years


Maximumisness


« Reply #11 - Posted 2010-10-12 19:16:15 »

That's good because just switching platforms because you have a performance bug would have been a rather shortsighted thing to do  Wink

Offline CyanPrime
« Reply #12 - Posted 2010-10-12 19:16:51 »

This is the output of a normal hex editor's btyes section.


...Okay, I guess I can't copy it? anyway there's a line in files byte code that says "This program can't be ran in dos mode"

this is that line in my hex editor:

Edit: okay, when I click the byte area it freezes so I can't copy that eather, but it's something like "d[]S m[][]d"

Anyway you guys got any idea why it's freezing? Here is the code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import java.util.Vector;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class HexEditor extends JFrame{
   JScrollPane hexScroll;
   JScrollPane byteScroll;
   JPanel panel;
   JTextArea hexArea;
   JTextArea byteArea;
   JFileChooser chooser;
   FileInputStream fin;
   JMenuBar menuBar;
   JMenu file;
      JMenuItem load;

   public HexEditor(){
      super("Cypri's java hex editor");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
      chooser = new JFileChooser();
     
      load = new JMenuItem("Load");
         load.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent event) {
               Thread hflt = new Thread(new HexFileLoader(passMe()));
               hflt.start();        
            }
         });
     
      file = new JMenu("File");
      file.add(load);
     
      menuBar = new JMenuBar();

      menuBar.add(file);
     
      hexArea = new JTextArea();
      byteArea = new JTextArea();
      hexArea.setLineWrap(true);
      hexArea.setWrapStyleWord(true);
      byteArea.setLineWrap(true);
   
      hexScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
      byteScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
     
      panel = new JPanel();
     
      panel.add(hexScroll);
      panel.add(byteScroll);
      hexScroll.setViewportView(hexArea);
      byteScroll.setViewportView(byteArea);
     
      hexScroll.setPreferredSize(new Dimension(440,480));
      byteScroll.setPreferredSize(new Dimension(200,480));

      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.NORTH, menuBar);
      getContentPane().add(BorderLayout.CENTER, panel);
      pack();
      setVisible(true);
   }
   
   public static byte[] hexStringToByteArray(String s) {
       int len = s.length() -1;
       byte[] data = new byte[(len / 2) + 1];
       for (int i = 0; i < len; i += 2) {
           data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
       }
       return data;
   }
   
   public static char[] hexStringToByteCharArray(String s) {
       int len = s.length() -1;
       byte[] data = new byte[(len / 2) + 1];
       char[] chardata = new char[(len / 2) + 1];
       for (int i = 0; i < len; i += 2) {
           data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
           chardata[i / 2] = (char) data[i / 2];
       }
       return chardata;
   }

   
   public void openFile(){
      chooser.showOpenDialog(null);
   }
   
   public void packMe(){
      pack();
   }
   
   public void revalidateMe(){
      repaint();
   }
   
   public HexEditor passMe(){
      return this;
   }
   
   public static void main(String[] args){
       HexEditor app = new HexEditor();
   }
}

class HexFileLoader implements Runnable {
    HexEditor parent;
    HexFileLoader(HexEditor parent) {
        this.parent = parent;
    }

    public void run() {
      try{  
         parent.openFile();
         parent.fin = new FileInputStream(parent.chooser.getSelectedFile());
         
         int ch;
         System.out.println("Load start.");
     
         parent.hexArea.setText("");
         parent.byteArea.setText("");
         
         while((ch = parent.fin.read()) != -1){
            if(Integer.toHexString(ch).length() < 2){
               parent.hexArea.append(" 0" + Integer.toHexString(ch).toUpperCase());
            }
           
            else{
               parent.hexArea.append(" " + Integer.toHexString(ch).toUpperCase());
            }
           
            parent.byteArea.setText(String.valueOf(HexEditor.hexStringToByteCharArray(parent.hexArea.getText().trim())));
         }

         System.out.println("Out of loop.");
         
         parent.packMe();
      }
     
      catch(Exception e){
         e.printStackTrace();
      }
    }
}
Offline CyanPrime
« Reply #13 - Posted 2010-10-12 19:27:47 »

That's good because just switching platforms because you have a performance bug would have been a rather shortsighted thing to do  Wink
Yeah, I guess so.  Cheesy
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 748
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #14 - Posted 2010-10-12 20:01:35 »

I merged your new thread back into this one.



Anyway, to answer your question:

Do not put binary into a JTextArea (your byteArea). It just just a bad idea, it doesn't work for you, and even if it works it's useless, because there are a lot of non-printable characters. You might want to 'escape' the non-printable chars, like converting 0x03 to '\x03'.

I do understand you want to display HEX, but why do you want to see binary in a JTextArea?

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline BoBear2681

JGO Coder


Medals: 18



« Reply #15 - Posted 2010-10-12 20:09:49 »

In your loop, it looks like you can replace this:

1  
parent.byteArea.setText(String.valueOf(HexEditor.hexStringToByteCharArray(parent.hexArea.getText().trim())));


with this:

1  
parent.byteArea.append((char)ch);


My only guess about your inability to select the text in your ascii view is that JTextArea isn't liking unprintable characters in its text.  You could try using filtering them out:

1  
2  
3  
4  
if (ch<0x20 || ch==0x7f) {
   ch = ' ';
}
parent.byteArea.append((char)ch);

Offline CyanPrime
« Reply #16 - Posted 2010-10-12 22:44:12 »

That seemed to work for both problems. Thank you. ^_^
Offline CyanPrime
« Reply #17 - Posted 2010-10-12 23:15:25 »

Okay, file-loading is responsive, but it's still very slow. How would a BufferedInputStream help me exactly?
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 748
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #18 - Posted 2010-10-12 23:23:08 »

Okay, file-loading is responsive, but it's still very slow. How would a BufferedInputStream help me exactly?

The javadoc explains it pretty well

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline CyanPrime
« Reply #19 - Posted 2010-10-13 00:10:15 »

The javadoc explains it pretty well

A BufferedInputStream adds functionality to another input stream-namely, the ability to buffer the input and to support the mark and reset  methods. When the BufferedInputStream  is created, an internal buffer array is created. As bytes from the stream are read or skipped, the internal buffer is refilled as necessary from the contained input stream, many bytes at a time. The mark  operation remembers a point in the input stream and the reset operation causes all the bytes read since the most recent mark operation to be reread before new bytes are taken from the contained input stream.


---

So...it reads into a buffer? This sounds like it'll take longer to read the file.
Offline BoBear2681

JGO Coder


Medals: 18



« Reply #20 - Posted 2010-10-13 01:01:19 »

It does bulk reads from the underlying input stream, and is thus faster in cases where reading from that stream is costly.  In your case, it's faster to read chunks of bytes at a time from a file as opposed to a single byte at a time, as there is less disk IO.
Offline CyanPrime
« Reply #21 - Posted 2010-10-13 01:09:06 »

It does bulk reads from the underlying input stream, and is thus faster in cases where reading from that stream is costly.  In your case, it's faster to read chunks of bytes at a time from a file as opposed to a single byte at a time, as there is less disk IO.
Seems the same to me. Do I need to use something other than read()?
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 748
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #22 - Posted 2010-10-13 01:11:59 »

Seems the same to me. Do I need to use something other than read()?

Are you really going to keep posting about how unlikely you think the solution is?

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline CyanPrime
« Reply #23 - Posted 2010-10-13 01:14:39 »

Are you really going to keep posting about how unlikely you think the solution is?
What do you mean?
Offline CyanPrime
« Reply #24 - Posted 2010-10-13 01:25:17 »

Hmm, am I doing the buffered read wrong? cause it's no faster than a byte by byte read for me even though I'm reading 1000 bytes at a time.

this is my code:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import java.util.Vector;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class HexEditor extends JFrame{
   JScrollPane hexScroll;
   JScrollPane byteScroll;
   JPanel panel;
   JTextArea hexArea;
   JTextArea byteArea;
   JFileChooser chooser;
   FileInputStream fin;
   BufferedInputStream bin;
   JMenuBar menuBar;
   JMenu file;
      JMenuItem load;
     
   JProgressBar progressBar;

   public HexEditor(){
      super("Cypri's java hex editor");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
      progressBar = new JProgressBar();
     
      chooser = new JFileChooser();
     
      load = new JMenuItem("Load");
         load.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent event) {
               Thread hflt = new Thread(new HexFileLoader(passMe()));
               hflt.start();        
            }
         });
     
      file = new JMenu("File");
      file.add(load);
     
      menuBar = new JMenuBar();

      menuBar.add(file);
     
      hexArea = new JTextArea();
      byteArea = new JTextArea();
      hexArea.setLineWrap(true);
      hexArea.setWrapStyleWord(true);
      byteArea.setLineWrap(true);
   
      hexScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
      byteScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
     
      panel = new JPanel();
     
      panel.add(hexScroll);
      panel.add(byteScroll);
      hexScroll.setViewportView(hexArea);
      byteScroll.setViewportView(byteArea);
     
      hexScroll.setPreferredSize(new Dimension(440,480));
      byteScroll.setPreferredSize(new Dimension(200,480));

      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.NORTH, menuBar);
      getContentPane().add(BorderLayout.CENTER, panel);
      getContentPane().add(BorderLayout.SOUTH, progressBar);
      pack();
      setVisible(true);
   }
   
   public static byte[] hexStringToByteArray(String s) {
       int len = s.length() -1;
       byte[] data = new byte[(len / 2) + 1];
       for (int i = 0; i < len; i += 2) {
           data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
       }
       return data;
   }
   
   public static char[] hexStringToByteCharArray(String s) {
       int len = s.length() -1;
       byte[] data = new byte[(len / 2) + 1];
       char[] chardata = new char[(len / 2) + 1];
       for (int i = 0; i < len; i += 2) {
           data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
           chardata[i / 2] = (char) data[i / 2];
       }
       return chardata;
   }

   
   public void openFile(){
      chooser.showOpenDialog(null);
   }
   
   public void packMe(){
      pack();
   }
   
   public void revalidateMe(){
      repaint();
   }
   
   public HexEditor passMe(){
      return this;
   }
   
   public static void main(String[] args){
       HexEditor app = new HexEditor();
       
       
   }
}

class HexFileLoader implements Runnable {
    HexEditor parent;
    HexFileLoader(HexEditor parent) {
        this.parent = parent;
    }

    public void run() {
      try{  
         parent.openFile();
         parent.fin = new FileInputStream(parent.chooser.getSelectedFile());
         parent.bin = new BufferedInputStream(parent.fin);
         
         System.out.println("(int) parent.chooser.getSelectedFile().getTotalSpace(): " + (int) parent.chooser.getSelectedFile().length());
         
         parent.progressBar.setMaximum((int) parent.chooser.getSelectedFile().length());
         parent.progressBar.setValue(0);
         
         int ch;
         System.out.println("Load start.");
     
         parent.hexArea.setText("");
         parent.byteArea.setText("");
         int numOfBytesRead = 0;
         byte[] buf = new byte[1000];
         while((ch = parent.bin.read(buf)) != -1){
            for(int i = 0; i < 1000; i++){
               if(Integer.toHexString(buf[i]).length() < 2){
                  parent.hexArea.append(" 0" + Integer.toHexString(buf[i]).toUpperCase());
               }
               
               else{
                  parent.hexArea.append(" " + Integer.toHexString(buf[i]).toUpperCase());
               }
               
               if (ch<0x20 || ch==0x7f) {
                  ch = ' ';
               }
               
               parent.byteArea.append(String.valueOf((char)buf[i]));
               
               numOfBytesRead++;
   
               parent.progressBar.setValue(numOfBytesRead);
            }
         }

         System.out.println("Out of loop.");
         
         parent.packMe();
      }
     
      catch(Exception e){
         e.printStackTrace();
      }
    }
}
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 748
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #25 - Posted 2010-10-13 01:38:04 »

That's because you have a bunch of bottlenecks, and you just solved one (introducing a brand new bug: always reading multiples of 1000 bytes, even if there are not that many bytes)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline CyanPrime
« Reply #26 - Posted 2010-10-13 01:40:07 »

That's because you have a bunch of bottlenecks, and you just solved one (introducing a brand new bug: always reading multiples of 1000 bytes, even if there are not that many bytes)
Yeah, I figured I had that bug, but I'm more worried about the speed right now. What are some ways I can speed up my code?
Offline CyanPrime
« Reply #27 - Posted 2010-10-13 08:36:52 »

Alright, it's kinda fast now. anyone have any other ideas I can use? See any bugs?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import java.util.Vector;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class HexEditor extends JFrame{
   static final String HEXES = "0123456789ABCDEF";
   
   JScrollPane hexScroll;
   JScrollPane byteScroll;
   JPanel panel;
   JTextArea hexArea;
   JTextArea byteArea;
   JFileChooser chooser;
   FileInputStream fin;
   BufferedInputStream bin;
   JMenuBar menuBar;
   JMenu file;
      JMenuItem load;
     
   JProgressBar progressBar;

   public HexEditor(){
      super("Cypri's java hex editor");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
      progressBar = new JProgressBar();
     
      chooser = new JFileChooser();
     
      load = new JMenuItem("Load");
         load.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent event) {
               Thread hflt = new Thread(new HexFileLoader(passMe()));
               hflt.start();        
            }
         });
     
      file = new JMenu("File");
      file.add(load);
     
      menuBar = new JMenuBar();

      menuBar.add(file);
     
      hexArea = new JTextArea();
      byteArea = new JTextArea();
      hexArea.setLineWrap(true);
      hexArea.setWrapStyleWord(true);
      byteArea.setLineWrap(true);
   
      hexScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
      byteScroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
     
      panel = new JPanel();
     
      panel.add(hexScroll);
      panel.add(byteScroll);
      hexScroll.setViewportView(hexArea);
      byteScroll.setViewportView(byteArea);
     
      hexScroll.setPreferredSize(new Dimension(440,480));
      byteScroll.setPreferredSize(new Dimension(200,480));

      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.NORTH, menuBar);
      getContentPane().add(BorderLayout.CENTER, panel);
      getContentPane().add(BorderLayout.SOUTH, progressBar);
      pack();
      setVisible(true);
   }
   
   public void openFile(){
      chooser.showOpenDialog(null);
   }
   
   public void packMe(){
      pack();
   }
   
   public void revalidateMe(){
      repaint();
   }
   
   public HexEditor passMe(){
      return this;
   }
   
   
     public static String getHex( byte [] raw ) {
       if ( raw == null ) {
         return null;
       }
       
       final StringBuilder hex = new StringBuilder( 2 * raw.length );
       
       for ( final byte b : raw ) {
         hex.append(HEXES.charAt((b & 0xF0) >> 4))
            .append(HEXES.charAt((b & 0x0F)))
            .append(" ");
       }
       
       return hex.toString();
     }

   
   public static void main(String[] args){
       HexEditor app = new HexEditor();
       
       
   }
}

class HexFileLoader implements Runnable {
    HexEditor parent;
    HexFileLoader(HexEditor parent) {
        this.parent = parent;
    }

    public void run() {
      try{  
         parent.openFile();
         parent.fin = new FileInputStream(parent.chooser.getSelectedFile());
         parent.bin = new BufferedInputStream(parent.fin);
         
         System.out.println("(int) parent.chooser.getSelectedFile().getTotalSpace(): " + (int) parent.chooser.getSelectedFile().length());
         
         parent.progressBar.setMaximum((int) parent.chooser.getSelectedFile().length());
         parent.progressBar.setValue(0);
         
         int ch;
         System.out.println("Load start.");
     
         parent.hexArea.setText("");
         parent.byteArea.setText("");
         int numOfBytesRead = 0;
         byte[] buf;
         
         if((int) parent.chooser.getSelectedFile().length() > 10000)
            buf = new byte[10000];
         
         else
            buf = new byte[(int) parent.chooser.getSelectedFile().length()];
         
         while((ch = parent.bin.read(buf)) != -1){
            parent.hexArea.append(HexEditor.getHex(buf));
            parent.byteArea.append(new String(buf));
            numOfBytesRead += buf.length;
            parent.progressBar.setValue(numOfBytesRead);
         }

         System.out.println("Out of loop.");
         
         parent.packMe();
      }
     
      catch(Exception e){
         e.printStackTrace();
      }
    }
}
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #28 - Posted 2010-10-14 05:27:50 »

If you're ever trying to improve speed, the first thing to do is find where the bottleneck is. To do that, you can either use System.nanoTime() and print out time requirements in the code, or you can use a profiler like Shark.

Either way, looking at your code won't help too much now. Instead, figure out what's taking up all the time and cut it out.

See my work:
OTC Software
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 748
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #29 - Posted 2010-10-14 10:02:34 »

Well, the bottleneck is right here:
1  
2  
3  
4  
            parent.hexArea.append(HexEditor.getHex(buf));
            parent.byteArea.append(new String(buf));
            numOfBytesRead += buf.length;
            parent.progressBar.setValue(numOfBytesRead);


3 UI updates for every byte...

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Pages: [1] 2 3
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

CogWheelz (13 views)
2014-07-30 21:08:39

Riven (21 views)
2014-07-29 18:09:19

Riven (14 views)
2014-07-29 18:08:52

Dwinin (12 views)
2014-07-29 10:59:34

E.R. Fleming (32 views)
2014-07-29 03:07:13

E.R. Fleming (12 views)
2014-07-29 03:06:25

pw (42 views)
2014-07-24 01:59:36

Riven (42 views)
2014-07-23 21:16:32

Riven (29 views)
2014-07-23 21:07:15

Riven (30 views)
2014-07-23 20:56:16
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!