Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (540)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Image (The slick class) loading a file from a zip or jar  (Read 1552 times)
0 Members and 1 Guest are viewing this topic.
Offline CyanPrime
« Posted 2010-09-16 02:44:59 »

Not the jar that the game is in, but another jar. how would I go about it?
Offline noblemaster

« JGO Spiffy Duke »


Medals: 20
Projects: 10


Age of Conquest makes your day!


« Reply #1 - Posted 2010-09-16 02:50:57 »

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  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
import java.awt.Image;
import java.awt.Toolkit;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * Provides access to resources in zip files.
 * <p>
 * Usage: Resource resource = ZipResource.getDefault();
 *
 * @author Christoph Aschwanden
 * @since September 14, 2010
 * @access Public
 */

public final class ZipResource extends Resource {
 
  /** The scheme. */
  private static final String SCHEME = "zip:";

  /** The instance of this class. */
  private static ZipResource instance = new ZipResource();

 
  /**
   * The constructor.
   */

  private ZipResource() {
    // not used
  }
 
  /**
   * Returns true if the scheme matches.
   *
   * @param  The scheme. "zip:" for a match.
   * @return  True for matching.
   */

  public static boolean matches(String scheme) {
    return scheme.equalsIgnoreCase(SCHEME);
  }

  /**
   * Returns an instance of this class.
   *
   * @return  The instance of this class.
   */

  public static ZipResource getDefault() {
    return instance;
  }
 
  /**
   * Returns the image for a certain path.
   *
   * @param path  Path and name to the image. "zip:" is optional. Whatever is
   *              listed after the "!" is considered the image file to read.
   *              <p>
   *              Example:
   *              <ul>
   *                <li>zip:data/map/map_1.zip!picture1.png
   *              </ul>
   * @return  The requested image or null if it couldn't be found.
   */

  public Image getImage(String path) {
    // read in the bytes and create image from bytes
    try {
      String[] parts = adapt(path).split("!");
      ZipFile zipFile = new ZipFile(parts[0]);
      ZipEntry entry = zipFile.getEntry(parts[1]);
      if (entry == null) {
        // no entry
        return null;
      }
      else {
        long size = entry.getSize();
        if (size < 0) {
          return null;
        }
        else {
          // read data into byte array and return the byte array
          int bytesRead = 0;
          int bytesToRead = (int)size;
          InputStream inputStream = zipFile.getInputStream(entry);
          byte[] data = new byte[bytesToRead];
          while (true) {
            int result = inputStream.read(data, bytesRead, bytesToRead - bytesRead);
            if ((result == -1) || ((bytesToRead - bytesRead) == result)) {
              // close the stream and return the data
              inputStream.close();
              inputStream = null;
              return Toolkit.getDefaultToolkit().createImage(data);
            }
            else {
              bytesRead += result;
            }
          }
        }
      }
    }
    catch (IOException e) {
      return null;
    }
  }
 
  /**
   * Gets the input stream for a given path.
   *
   * @param path  Path to use. "zip:" is optional. Whatever is
   *              listed after the "!" is considered the file to read.
   *              <p>
   *              Example:
   *              <ul>
   *                <li>zip:data/map/map_1.zip!music_piano.mid
   *              </ul>
   * @return  The input stream.
   * @throws IOException  If the input steam cannot be obtained.
   */

  public InputStream getInputStream(String path) throws IOException {
    String[] parts = adapt(path).split("!");
    ZipFile zipFile = new ZipFile(new File(parts[0]));
    ZipEntry entry = zipFile.getEntry(parts[1]);
    if (entry == null) {
      // no entry
      return null;
    }
    else {
      // return the stream
      return zipFile.getInputStream(entry);
    }
  }
 
  /**
   * Returns a list of all files in the given path.
   *
   * @param path  The path in where to look for files. "zip:" is optional. Whatever is
   *              listed after the "!" is considered the prefix.
   *              <p>
   *              Example:
   *              <ul>
   *                <li>zip:data/map/map_1.zip!pictures/
   *              </ul>
   * @return  A list of path strings. Or null if they couldn't be found.
   */
 
  public String[] getFiles(String path) {
    try {
      String[] parts = adapt(path).split("!");
      ZipFile zipFile = new ZipFile(new File(parts[0]));
      String search = parts[1];
      Enumeration<? extends ZipEntry> entries = zipFile.entries();
      ArrayList<String> paths = new ArrayList<String>();
      while (entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();
        if (entry.getName().startsWith(search)) {
          if (!entry.isDirectory()) {
            paths.add(entry.getName());
          }
        }
      }
      return paths.toArray(new String[0]);
    }
    catch (Exception e) {
      return null;
    }
  }
 
  /**
   * Returns a list of all directories in the given path.
   *
   * @param path  The path in where to look for sub directories. "zip:" is optional. Whatever is
   *              listed after the "!" is considered the prefix.
   *              <p>
   *              Example:
   *              <ul>
   *                <li>zip:data/map/map_1.zip!pictures/
   *              </ul>
   * @return  A list of path strings. Or null if they couldn't be found.
   */
 
  public String[] getDirectories(String path) {
    try {
      String[] parts = adapt(path).split("!");
      ZipFile zipFile = new ZipFile(new File(parts[0]));
      String search = parts[1];
      Enumeration<? extends ZipEntry> entries = zipFile.entries();
      ArrayList<String> paths = new ArrayList<String>();
      while (entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();
        if (entry.getName().startsWith(search)) {
          if (entry.isDirectory()) {
            paths.add(entry.getName());
          }
        }
      }
      return paths.toArray(new String[0]);
    }
    catch (Exception e) {
      return null;
    }
  }
 
  /**
   * Returns the path so it can be utilized here.
   *
   * @param path  The path. "jar:" is optional.
   * @return  The usable path.
   */

  private String adapt(String path) {
    if (path.indexOf(SCHEME) >= 0) {
      // cut scheme
      return path.substring(SCHEME.length());
    }
    else {
      return path;
    }
  }
}

Offline Nate

« JGO Bitwise Duke »


Medals: 158
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #2 - Posted 2010-09-16 03:51:10 »

Look at Slick's resource loader class. It looks first on the file system, then in the classpath. All JARs for your app make up the "classpath".

You can load the bytes for your image many ways: from a file, from a socket (eg, a URL to a webserver), from the classpath, etc. To load from the classpath you can use one of:

1  
2  
SomeClass.class.getResourceAsStream(path);
getClass().getResourceAsStream(path);


The path you pass is a string. Starting the string with "/" will start the search for your file at the root of the classpath. Eg:

1  
InputStream input = getClass().getResourceAsStream("/stuff/image.png");


Note there are other ways to look up classpath resources relatively to a class' package directory, but I find just always using the above is easier.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CyanPrime
« Reply #3 - Posted 2010-09-16 04:05:26 »

Thank you guys very much. I managed to get what I wanted from this simple source:

1  
2  
3  
4  
5  
6  
7  
8  
9  
try{
         ZipFile zip = new ZipFile("data/graphics/player.zip");
         ZipEntry entry = zip.getEntry("player.png");
         player = new Image(zip.getInputStream(entry), "player.png", false);
      }
     
      catch(Exception e){
         
      }
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #4 - Posted 2010-09-16 06:58:54 »

Look at Slick's resource loader class. It looks first on the file system, then in the classpath.
What's the reason for this? I actually do it the other way around because I assumed Java would be faster at looking at stuff in the classpath. But come to think of it if it has to unzip the JAR each time (although it would be crazy if it did) then obviously navigating around in the classpath is much slower. And also I would rather not load something from the file system first, just in case the user puts some crazy junk in there. But for mods and the like I suppose this has an advantage.

Are the comparisons negligible, or is there a discernible difference?

See my work:
OTC Software
Offline Nate

« JGO Bitwise Duke »


Medals: 158
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #5 - Posted 2010-09-16 09:17:57 »

CyanPrime, chances are good your code is wrong, since you catch Exception and do nothing. If you think it will never fail, good code uses:
1  
2  
3  
} catch (Exception ex) {
   throw new RuntimeException("Error loading playing image.", ex);
}

That does two things: 1) Adds a nice message so you know what the failure is. B) Throws an exception so you will get a stacktrace. You shouldn't get in the habit of writing empty catches, even during the prototype stage, because if you ever leave just one behind, it is a big problem. During prototyping I like to make my main and other methods throw Exception. Then I can come back later, remove the "throws Exception" on main, and the compiler shows me all the places I need to properly deal with exceptions. If you really think it will never happen then you can use:
1  
2  
3  
} catch (Exception ex) {
   throw new RuntimeException(ex);
}

A lot of people use this:
1  
2  
3  
} catch (Exception ex) {
   ex.printStackTrace();
}

This is simply not as good for a "should never happen" exception, because it allows execution to continue, and all bets are off. While I'm here, this is also extremely wrong:
1  
2  
3  
} catch (Exception ex) {
   System.out.println(ex.getMessage());
}

This is just as bad as doing nothing since it both hides the stacktrace and allows execution to continue. Any code that calls getMessage on an exception is probably hiding the stacktrace and bad. Finally, if you really, actually don't care whether the exception is thrown, I like to use this:
1  
2  
} catch (Exception ignored) {
}

This way there is no confusion that the intent is to ignore the exception, and no one (intelligent) is ever going to come back later and call methods or rethrow a variable named "ignored".

What's the reason for this?
Solely convenience. The file system should always be faster though. It is simpler, only one place needs to be checked, and there is no compression.

There's a little known feature for JARs where you can include an index of the files in the JAR to speed up looking for classpath entries.

And also I would rather not load something from the file system first, just in case the user puts some crazy junk in there.
There's an exploit where Windows looks in the current directory for a library before the program directory. So you can put an MP3 and a DLL in a directory, and when a user runs the MP3, Windows Media Player (or whatever) runs with that as the current directory, the DLL happens to be named the same as one WMP wants to load, so Windows uses your DLL instead, and now you own the machine. Smiley

Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 847
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #6 - Posted 2010-09-17 06:08:11 »

There's a little known feature for JARs where you can include an index of the files in the JAR to speed up looking for classpath entries.

That's odd. ZIP contains this index at the end of the file. You can both stream in entries or move to the end, read the index and seek the entries. What would a JAR entry index add to this? Could you provide a link to docs of this feature?

Edit:
You seem to be talking about META-INF/INDEX.LST, which design is rather awful. It must also contain all the packages/classes of the (other!) JARs in the classpath. If you leave them out, those resources won't be found. Ugh.

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

« JGO Bitwise Duke »


Medals: 158
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #7 - Posted 2010-09-17 07:16:06 »

That's odd. ZIP contains this index at the end of the file. You can both stream in entries or move to the end, read the index and seek the entries. What would a JAR entry index add to this? Could you provide a link to docs of this feature?

Edit:
You seem to be talking about META-INF/INDEX.LST, which design is rather awful. It must also contain all the packages/classes of the (other!) JARs in the classpath. If you leave them out, those resources won't be found. Ugh.
Yep, the INDEX.LIST file:
http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JARIndex
The "i" option for the jar tool creates it.

Pages: [1]
  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.

Mr.CodeIt (23 views)
2014-12-23 03:34:11

rwatson462 (53 views)
2014-12-15 09:26:44

Mr.CodeIt (45 views)
2014-12-14 19:50:38

BurntPizza (85 views)
2014-12-09 22:41:13

BurntPizza (110 views)
2014-12-08 04:46:31

JscottyBieshaar (78 views)
2014-12-05 12:39:02

SHC (89 views)
2014-12-03 16:27:13

CopyableCougar4 (96 views)
2014-11-29 21:32:03

toopeicgaming1999 (155 views)
2014-11-26 15:22:04

toopeicgaming1999 (152 views)
2014-11-26 15:20:36
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
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!