archive file in image

Discussion in 'Scripts, 3rd Party Apps, and Programming' started by galaxyAbstractor, Oct 2, 2009.

  1. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    Not really, it cancels the whole loop? I thought it only canceled the current run.

    Anyways, it returns false when there is a zip archive in the image
     
  2. ah-blabla

    ah-blabla New Member

    Messages:
    375
    Likes Received:
    7
    Trophy Points:
    0
    An enum would probably be better for readability.
     
    Last edited: Oct 10, 2009
  3. marshian

    marshian New Member

    Messages:
    526
    Likes Received:
    9
    Trophy Points:
    0
    Continue does that. Break cancels the whole loop.

    Code:
    for(int i=0; i<4; i++) {
    	int c = inFile.read();
    	if(c == filesigrar[i]) {
    		valid = "RAR";
    	} else if(c == filesigzip[i]) {
    		valid = "ZIP";
    	} else {
    		//In case any of the 4 bytes fails the check, the file is invalid
    		//and the loop is aborted.
    		valid = "false";
    		break;
    	}
    }
    I think it's a *bit* of an overkill to write an enum to do this... You could use 'f', 'z' and 'r' too, that's readable enough imho :p
     
    • Like Like x 1
  4. ah-blabla

    ah-blabla New Member

    Messages:
    375
    Likes Received:
    7
    Trophy Points:
    0
    I know I'm being pedantic, but: it's better practice (to use enums) in this sort of situation, and prevents you from getting into bad habits. You could use f, z, r, but then you'd have to write comments on it so you remember what it meant when you read the code in half a year (well, you might not, but there is the chance), whereas with an enum it is clear what everything means, e.g:
    Code:
    public enum Status {NONE, ZIP, RAR}
    private Status stat = Status.NONE;
    
    stat = Status.ZIP;
    looks a lot better / is easier to understand.
    The enum is also a lot more future proof, making it easier to extend to extend the program should you wish to in the future, and it's also better if you modify the class to use as part of a larger program.
     
  5. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    Thanks a lot, marshian :D
     
  6. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    oh one more thing, is it possible to make the rows background color green if an archive is found?
     
  7. marshian

    marshian New Member

    Messages:
    526
    Likes Received:
    9
    Trophy Points:
    0
    You're welcome :)

    *looks confused*
    What rows?
     
  8. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    Oh so I had right in my dream that I forgot to say I put the list of files in a jTable xD

    [​IMG]
    Those rows where there were an archive found
     
  9. marshian

    marshian New Member

    Messages:
    526
    Likes Received:
    9
    Trophy Points:
    0
  10. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    Actually it was quite easy, after I found this:
    http://www.java-forums.org/awt-swin...ble-row-having-particular-value.html#post1015 and http://www.javaworld.com/javaworld/javaqa/2001-09/03-qa-0928-jtable.html?page=1

    Code:
    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    
    package hidefiles;
    
    /**
     *
     * @author vigge_sWe
     */
    import java.awt.Component;
    import java.awt.Color;
    import javax.swing.JTable;
    import javax.swing.table.DefaultTableCellRenderer;
    public class CustomTableCellRenderer extends DefaultTableCellRenderer
    {
        @Override
        public Component getTableCellRendererComponent(
    JTable table, Object value, boolean isSelected,
    boolean hasFocus, int row, int col)
    {
         Component comp = super.getTableCellRendererComponent(
                          table,  value, isSelected, hasFocus, row, col);
    
         String s =  table.getModel().getValueAt(row, 2).toString();
    
         if(s.equalsIgnoreCase("RAR") || s.equalsIgnoreCase("ZIP"))
         {
             comp.setBackground(new Color(0x458358));
         }
         else
         {
             comp.setBackground(null);
         }
    
         return( comp );
     }
    }
    Code:
    model.addColumn("Image");
            model.addColumn("Size");
            model.addColumn("Archive");
            jTable1.setModel(model);
            TableCellRenderer renderer = new CustomTableCellRenderer();
    
            jTable1.setDefaultRenderer( jTable1.getColumnClass(2), renderer );
    [​IMG]

    Now to the last question, I promise ;). How would I return the size of the image in the file we added?
     
    Last edited: Oct 11, 2009
  11. marshian

    marshian New Member

    Messages:
    526
    Likes Received:
    9
    Trophy Points:
    0
    The file format is like this:
    <image data><archive data><integer><signature>
    Both integer and signature are 4 byte long, so to read integer skip to the 8th last byte and read an integer through an ObjectInputStream.
     
  12. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    so the size of the image represented there will always be 4 bytes long?
     
  13. ah-blabla

    ah-blabla New Member

    Messages:
    375
    Likes Received:
    7
    Trophy Points:
    0
    If you store the size as an integer then yes, since an integer is 4 bytes long in Java (or rather, it's 32 bits according to specification (see http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html). The only problem is if the file's length is larger than the data that an int can store, since you are storing the image size using the method File.size() which returns the file length in bytes. The largest number an int can store is 2,147,483,647 , so if your image file is longer than 2,147,483,647 bytes (2GB!) then the int won't be able to store the length of the file, but I have never come across images that large.

    And be careful when reading the integer in through the ObjectInputStream, since int is a primitive type you must read it in using the method readInt(). I also just read this
    (from http://java.sun.com/javase/6/docs/api/index.html)
    That means that the int won't be 4 bytes long if you write it using ObjectOutputStream. More research is probably needed, but it might be better to use a normal OutputStream to write the integer and use a normal InputStream to read the int again.


    EDIT://
    I've written a whole program doing this. It's a bit long to post here, so I've posted it here:
    http://www.ahunt.org/programming/image-archive.html
    The code isn't particularly well commented / formatted etc, but it works, and should give you an idea of what to do. I haven't added in filetype signatures yet, but that would be quite simple really. You can then use the class as used in the example main() method.
     
    Last edited: Oct 11, 2009
  14. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    So I should use an ObjectInputStream?

    Code:
    private void jTable1MouseClicked(java.awt.event.MouseEvent evt) {
            if(evt.getClickCount() == 2 &&
                    (jTable1.getValueAt(jTable1.getSelectedRow(), 2).toString().equals("RAR") ||
                     jTable1.getValueAt(jTable1.getSelectedRow(), 2).toString().equals("ZIP")) ){
                zipFileLabel.setText(jTable1.getValueAt(jTable1.getSelectedRow(), 0).toString());
                zipType.setText(jTable1.getValueAt(jTable1.getSelectedRow(), 2).toString());
                
                ObjectInputStream inFile = null;
                File input = new File(findDir + jTable1.getValueAt(jTable1.getSelectedRow(), 0).toString());
              
                try {
                    inFile = new ObjectInputStream((InputStream) new FileInputStream(input));
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
               
                try {
                    //Go to the last 4 bytes
                    inFile.skip(input.length() - 8);
                    int imageSize = inFile.readInt();
                    System.out.println(imageSize);
                    inFile.close();
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
    
                jFrame3.pack();
                jFrame3.setVisible(true);
            }
        }
    (findDir is the path to the folder where the file resides in)

    Where did it go wrong? It worked before I added the ObjectInputStream

    Sorry I'm such a noob xD
     
    Last edited: Oct 11, 2009
  15. ah-blabla

    ah-blabla New Member

    Messages:
    375
    Likes Received:
    7
    Trophy Points:
    0
    Sorry for being a bit confusing earlier on, I'll try and explain a bit more simply (however this isn't such a simple thing...)
    The problem with ObjectOutputStream is that when you write an int to a file, it doesn't simply write the 4 bytes that the int contains, but it first of all writes a header, so all the positions will be different than expected. If you then try to read this int with an ObjectInputStream it expects a header tellling it what the data is, for this to work you need to know where the int+header actually starts, which will not be 8 bytes from the end.
    Instead you should try to write the int using the normal OutputStream.write() method, and then read using InputStream.read().A slight complication is that InputStream / OutputStream read and write in bytes, while an int is 4 bytes. The OutputStream.write(int) method only takes the first 8 bits (first byte) of the int, the rest is ignored, so what you need to do is convert the int to a byte array of length four before writing, since if you pass a byte array to OutputStream.write(byte[]) it prints out all 4 bytes. Similarly when reading you need to create an empty byte array of the length 4, skip to 8 bytes from the end of the file, then use InputStream.read(byte[]) with that byte array, and then read the byte array again.

    I.e. when writing to file, write the length using:
    Code:
    // These two things are probably already setup, so you can remove them.
    int length = WhateverItEquals;
    OutputStream os = whateverYouSetUpEarlier;
    // This here converts length to a byte array and writes it.
    os.write(new byte[] {
                    (byte)(length >>> 24),
                    (byte)(length >>> 16),
                    (byte)(length >>> 8),
                    (byte)length});
    
    To read this you then use the following code:
    Code:
    InputStream is = WhateverYouSetUp;
    // The byte array into which the length will be read.
    byte[] b = new byte[4];
    // Skip to where the int is
    is.skip(File.length() - 8);
    // This then reads in the next four bytes into the array.
    is.read(lengthArray);
    // This converts the byte array back to an int and stores it.
    int length = (b[0] << 24)
                    ((b[1] & 0xFF) << 16)
                    ((b[2] & 0xFF) << 8)
                    (b[3] & 0xFF);
    
    And a slight bit of advice: a good idea would be to create a separate class which loads a file, does all the analysis at once, and from which you can then simply request information about the file, since it seems you are reading the file again and again for each piece of information you want from it.

    Another suggestion is to add another part to the file storing the name of the file you have archive, i.e. the new format of the file would be:
    <image><file_name><file><image_length><file_name_length>
    That would allow you to easily store any file within the image, since only rar and zip is slightly limiting.
     
    Last edited: Oct 12, 2009
  16. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    Well... It works but it puts the wrong length. 358148 bytes on a file that is around 17776 bytes
     
  17. ah-blabla

    ah-blabla New Member

    Messages:
    375
    Likes Received:
    7
    Trophy Points:
    0
    Hmm... Are you sure the length you are using at beginning (ie. when you are making the combined file) is correct? (The same code worked fine for the test program I wrote.) Try posting the full new code, so I can see what exactly is happening.
     
  18. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
  19. ah-blabla

    ah-blabla New Member

    Messages:
    375
    Likes Received:
    7
    Trophy Points:
    0
    Replace the ObjectOutputStream used in the first file, i.e. replace:
    Code:
                    out.flush();
                    out.close();
                    outOOS = new ObjectOutputStream((OutputStream) new FileOutputStream(output, true));
                   
                   
     if(archiveType.equals("rar")){
                        outwrite(0x58); // Identify archive rar
                        out.write(0x59); // Identify archive rar
                        out.write(0x12); // Identify archive rar
                        out.write(0xA2); // Identify archive rar
                    } else if(archiveType.equals("zip")) {
                        out.write(0x58); // Identify archive zip
                        out.write(0x59); // Identify archive zip
                        out.write(0x12); // Identify archive zip
                        out.write(0xA3); // Identify archive zip
                    }
                    outOOS.flush();
    with
    Code:
                    if(archiveType.equals("rar")){
                        // Identify rar
                        out.write({0x58,0x59,0x12,(byte)0xA2)});
                    } else if(archiveType.equals("zip")) {
                        // Identify zip
                        out.write({0x58,0x59,0x12,(byte)0xA3)})
                    }
    out.close();
    
    (Untested, but should work.)
    The code you have there using the ObjectOutputStream (OOS) writes a header for each part of the signature, so the signature is now larger than 4 bytes, which will have been causing the problems. Generally try to avoid using OOS unless you are doing stuff with object serialisation, since that's what it's primarily designed for.
    The code opening the file should now work correctly, since both size and signature should be in the expected places.

    Also, you seem to be wanting to write a 4 byte signature, however 0xA2 and 0xA3 are integer codes: in the code above it is cast to a byte (i.e. cuts out 3/4 of the data), so you should probably replace that with a code representing a byte. Luckily 0xA2 and 0xA3 when truncated to a byte each represent a different number, but it's still better to use bytes.

    And BTW, it isn't necessary to call out.flush() before out.close(), since out.close() will call flush.

    Lets hope this fixes it. :cool:
     
    Last edited: Oct 12, 2009
    • Like Like x 1
  20. galaxyAbstractor

    galaxyAbstractor Community Advocate Community Support

    Messages:
    5,508
    Likes Received:
    35
    Trophy Points:
    48
    "illegal start of expression" and "; expected" when trying that.

    Edit: You do mean
    Code:
    if(archiveType.equals("rar")){
                        // Identify rar
                        out.write(new byte[] {(byte)0x58,(byte)0x59,(byte)0x12,(byte)0xA2});
                    } else if(archiveType.equals("zip")) {
                        // Identify zip
                        out.write(new byte[] {(byte)0x58,(byte)0x59,(byte)0x12,(byte)0xA3});
                    }
    right? Because now it works xD
     
    Last edited: Oct 12, 2009

Share This Page