| package com.plusminus.craft; | |
| import com.plusminus.craft.WorldInfo; | |
| import java.awt.Color; | |
| import java.awt.Rectangle; | |
| import java.awt.Graphics2D; | |
| import java.awt.AlphaComposite; | |
| import java.awt.RenderingHints; | |
| import java.awt.image.BufferedImage; | |
| import java.awt.image.RescaleOp; | |
| import java.io.File; | |
| import java.io.FileReader; | |
| import java.io.FileInputStream; | |
| import java.io.LineNumberReader; | |
| import java.io.FileNotFoundException; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.zip.ZipFile; | |
| import java.util.zip.ZipException; | |
| import java.util.jar.JarFile; | |
| import java.util.zip.ZipEntry; | |
| import javax.imageio.ImageIO; | |
| /*** | |
| * Utility class which has convenience methods to access the | |
| * files of the current minecraft installation | |
| * @author Vincent Vollers | |
| */ | |
| public class MineCraftEnvironment { | |
| public static enum OS {XP, Vista, MacOS, Linux, NotSupported}; | |
| public static OS os; | |
| public static File baseDir; | |
| public static File xrayBaseDir; | |
| static { | |
| String os = System.getProperty( "os.name" ); | |
| HashMap<String,OS> osData = new HashMap<String,OS>(); | |
| /* Full list of os.name strings | |
| AIX | |
| Digital Unix | |
| FreeBSD | |
| HP UX | |
| Irix | |
| Linux | |
| Mac OS | |
| Mac OS X | |
| MPE/iX | |
| Netware 4.11 | |
| OS/2 | |
| Solaris | |
| Windows 2000 | |
| Windows 7 | |
| Windows 95 | |
| Windows 98 | |
| Windows NT | |
| Windows Vista | |
| Windows XP | |
| */ | |
| osData.put("FreeBSD", OS.Linux); | |
| osData.put("HP UX", OS.Linux); | |
| osData.put("Linux", OS.Linux); | |
| osData.put("Mac OS", OS.MacOS); | |
| osData.put("Mac OS X", OS.MacOS); | |
| osData.put("Windows", OS.XP); | |
| osData.put("Windows 7", OS.Vista); | |
| osData.put("Windows XP", OS.XP); | |
| osData.put("Windows 2003", OS.XP); | |
| osData.put("Windows 2000", OS.XP); | |
| osData.put("Windows Vista", OS.Vista); | |
| if(!osData.containsKey(os)) { | |
| MineCraftEnvironment.os = OS.NotSupported; | |
| } else { | |
| MineCraftEnvironment.os = osData.get(os); | |
| } | |
| switch(MineCraftEnvironment.os) { | |
| case Vista: | |
| MineCraftEnvironment.baseDir = new File(System.getenv("APPDATA"), ".minecraft"); | |
| MineCraftEnvironment.xrayBaseDir = new File(System.getenv("APPDATA"), ".minecraft_xray"); | |
| break; | |
| case Linux: | |
| MineCraftEnvironment.baseDir = new File(System.getProperty("user.home"), ".minecraft"); | |
| MineCraftEnvironment.xrayBaseDir = new File(System.getProperty("user.home"), ".minecraft_xray"); | |
| break; | |
| case XP: | |
| MineCraftEnvironment.baseDir = new File(System.getenv("APPDATA"), ".minecraft"); // untested | |
| MineCraftEnvironment.xrayBaseDir = new File(System.getenv("APPDATA"), ".minecraft_xray"); | |
| break; | |
| case MacOS: | |
| // damn macs ;p | |
| File dotMinecraftEnv = new File(System.getProperty("user.home"), "Library/Application Support/.minecraft"); | |
| if(dotMinecraftEnv.exists()) { | |
| MineCraftEnvironment.baseDir = dotMinecraftEnv; | |
| MineCraftEnvironment.xrayBaseDir = new File(System.getProperty("user.home"), "Library/Application Support/.minecraft_xray"); | |
| } else { | |
| MineCraftEnvironment.baseDir = new File(System.getProperty("user.home"), "Library/Application Support/minecraft"); // untested | |
| MineCraftEnvironment.xrayBaseDir = new File(System.getProperty("user.home"), "Library/Application Support/minecraft_xray"); // untested | |
| } | |
| break; | |
| default: | |
| MineCraftEnvironment.baseDir = null; | |
| MineCraftEnvironment.xrayBaseDir = null; | |
| } | |
| System.out.println(MineCraftEnvironment.baseDir.getAbsolutePath()); | |
| } | |
| /*** | |
| * Returns a list of WorldInfo objects, corresponding to available worlds | |
| * @return | |
| */ | |
| public static ArrayList<WorldInfo> getAvailableWorlds() { | |
| ArrayList<WorldInfo> worlds = new ArrayList<WorldInfo>(); | |
| for(int i=0;i<10;i++) { | |
| File worldDir = getWorldDirectory(i); | |
| if(worldDir.exists() && worldDir.canRead()) { | |
| try | |
| { | |
| // First snatch up the overworld | |
| WorldInfo info = new WorldInfo(worldDir.getCanonicalPath(), i); | |
| worlds.add(info); | |
| // Now see if there's an associated Nether world we can add. | |
| WorldInfo netherinfo = info.getNetherInfo(); | |
| if (netherinfo != null) | |
| { | |
| worlds.add(netherinfo); | |
| } | |
| } | |
| catch (IOException e) | |
| { | |
| // Nothing; guess we'll ignore it. | |
| } | |
| } | |
| } | |
| return worlds; | |
| } | |
| /*** | |
| * Returns a file handle to a chunk file in a world | |
| * @param world | |
| * @param x | |
| * @param z | |
| * @return | |
| */ | |
| public static File getChunkFile(WorldInfo world, int x, int z) { | |
| int xx = x % 64; | |
| if(xx<0) xx = 64+xx; | |
| int zz = z % 64; | |
| if(zz<0) zz = 64+zz; | |
| String firstFolder = Integer.toString(xx, 36); | |
| String secondFolder = Integer.toString(zz, 36); | |
| String filename = "c." + Integer.toString(x, 36) + "." + Integer.toString(z, 36) + ".dat"; | |
| return new File(world.getBasePath(), firstFolder + "/" + secondFolder + "/" + filename); | |
| } | |
| /*** | |
| * Returns a file handle to a base world directory | |
| * @param world | |
| * @return | |
| */ | |
| public static File getWorldDirectory(int world) { | |
| return new File(baseDir, "saves/World" + world); | |
| } | |
| /*** | |
| * Returns a file handle to the base minecraft directory | |
| * @return | |
| */ | |
| public static File getMinecraftDirectory() { | |
| return MineCraftEnvironment.baseDir; | |
| } | |
| /** | |
| * Returns a file handle to our own data directory | |
| * @return | |
| */ | |
| public static File getXrayDirectory() { | |
| return MineCraftEnvironment.xrayBaseDir; | |
| } | |
| /** | |
| * Returns a file handle to our config file; will create the | |
| * directory if needed. | |
| * @return | |
| */ | |
| public static File getXrayConfigFile() { | |
| if (MineCraftEnvironment.xrayBaseDir.exists()) | |
| { | |
| if (!MineCraftEnvironment.xrayBaseDir.isDirectory()) | |
| { | |
| return null; | |
| } | |
| } | |
| else | |
| { | |
| if (!MineCraftEnvironment.xrayBaseDir.mkdir()) | |
| { | |
| return null; | |
| } | |
| } | |
| return new File(MineCraftEnvironment.xrayBaseDir, "xray.properties"); | |
| } | |
| /*** | |
| * Returns a stream to an arbitrary file either from the main jar, or from the user-specified | |
| * texture pack. | |
| * | |
| * @return | |
| */ | |
| public static InputStream getMinecraftTexturepackData(String filename) { | |
| // First check the options.txt file to see if we should be using the defined | |
| // texture pack. | |
| File optionsFile = new File(baseDir, "options.txt"); | |
| String texturepack = null; | |
| if (optionsFile.exists()) | |
| { | |
| LineNumberReader reader = null; | |
| try | |
| { | |
| reader = new LineNumberReader(new FileReader(optionsFile)); | |
| String line = null; | |
| String[] parts; | |
| while ((line = reader.readLine()) != null) | |
| { | |
| parts = line.split(":", 2); | |
| if (parts.length == 2) | |
| { | |
| if (parts[0].equalsIgnoreCase("skin")) | |
| { | |
| if (parts[1].equalsIgnoreCase("Default")) | |
| { | |
| // Default skin, just break and | |
| break; | |
| } | |
| else | |
| { | |
| // Use the specified texture pack | |
| texturepack = parts[1]; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| catch (FileNotFoundException e) | |
| { | |
| // Just ignore it and load the default terrain.png | |
| } | |
| catch (IOException e) | |
| { | |
| // Ditto, just ignore | |
| } | |
| if (reader != null) | |
| { | |
| try | |
| { | |
| reader.close(); | |
| } | |
| catch (IOException e) | |
| { | |
| // do nothing | |
| } | |
| } | |
| } | |
| // Attempt to load in the texture pack | |
| if (texturepack != null) | |
| { | |
| File packFile = new File(baseDir, "texturepacks/" + texturepack); | |
| if (packFile.exists()) | |
| { | |
| ZipFile zf = null; | |
| try | |
| { | |
| zf = new ZipFile(packFile); | |
| ZipEntry entry = zf.getEntry(filename); | |
| if (entry != null) | |
| { | |
| return zf.getInputStream(entry); | |
| } | |
| } | |
| catch (ZipException e) | |
| { | |
| // Do nothing. | |
| } | |
| catch (IOException e) | |
| { | |
| // Do nothing | |
| } | |
| if (zf != null) | |
| { | |
| try | |
| { | |
| zf.close(); | |
| } | |
| catch (IOException e) | |
| { | |
| // do nothing | |
| } | |
| } | |
| } | |
| } | |
| // If we got here, just do what we've always done. | |
| return getMinecraftFile(filename); | |
| } | |
| /*** | |
| * Returns a stream to the texture data (overrides in the directory are handled) | |
| * @return | |
| */ | |
| public static InputStream getMinecraftTextureData() { | |
| return getMinecraftTexturepackData("terrain.png"); | |
| } | |
| /*** | |
| * Returns a stream to the water texture data | |
| * @return | |
| */ | |
| public static InputStream getMinecraftWaterData() { | |
| return getMinecraftTexturepackData("misc/water.png"); | |
| } | |
| /*** | |
| * Returns a stream to the water texture data | |
| * @return | |
| */ | |
| public static InputStream getMinecraftParticleData() { | |
| return getMinecraftTexturepackData("particles.png"); | |
| } | |
| /** | |
| * Returns a stream to the painting data | |
| * @return | |
| */ | |
| public static InputStream getMinecraftPaintingData() { | |
| return getMinecraftTexturepackData("art/kz.png"); | |
| } | |
| /*** | |
| * Returns a stream to the font data (overrides in the directory are handled) | |
| * @return | |
| */ | |
| public static InputStream getMinecraftFontData() { | |
| return getMinecraftFile("default.png"); | |
| } | |
| /*** | |
| * Attempts to create a bufferedImage for a stream | |
| * @param i | |
| * @return | |
| */ | |
| private static BufferedImage buildImageFromInput(InputStream i) { | |
| try { | |
| return ImageIO.read(i); | |
| } catch (IOException e) { | |
| // TODO Auto-generated catch block | |
| e.printStackTrace(); | |
| return null; | |
| } | |
| } | |
| /** | |
| * Attempts to create a bufferedImage containing our painting sheet | |
| * @return | |
| */ | |
| public static BufferedImage getMinecraftPaintings() { | |
| return buildImageFromInput(getMinecraftPaintingData()); | |
| } | |
| /*** | |
| * Attempts to create a bufferedImage containing the texture sprite sheet | |
| * @return | |
| */ | |
| public static BufferedImage getMinecraftTexture() { | |
| BufferedImage bi = buildImageFromInput(getMinecraftTextureData()); | |
| Graphics2D g2d = bi.createGraphics(); | |
| // Figure out our square size, and then check to see if the grass tile is | |
| // grayscale or not; we do this by examining the middle row of pixels. If | |
| // it *is* grayscale, then colorize it. | |
| int square_width = bi.getWidth()/16; | |
| int[] pixels = new int[square_width]; | |
| int i; | |
| int r, g, b; | |
| boolean grayscale = true; | |
| bi.getRGB(0, square_width/2, square_width, 1, pixels, 0, square_width); | |
| for (i=0; i<square_width; i++) | |
| { | |
| //a = (pixels[i] & 0xFF000000) >> 24; | |
| r = (pixels[i] & 0x00FF0000) >> 16; | |
| g = (pixels[i] & 0x0000FF00) >> 8; | |
| b = (pixels[i] & 0x000000FF); | |
| //System.out.println("Pixel " + i + ": " + r + ", " + g + ", " + b + ", " + a); | |
| if (g > r || g > b) | |
| { | |
| grayscale = false; | |
| break; | |
| } | |
| } | |
| // Now do the coloring if we have to. | |
| if (grayscale) | |
| { | |
| AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.3f); | |
| // First greenify grass | |
| Rectangle rect = new Rectangle(0, 0, square_width, square_width); | |
| g2d.setComposite(ac); | |
| g2d.setColor(Color.green); | |
| g2d.fill(rect); | |
| g2d.drawImage(bi, null, 0, 0); | |
| // Now greenify leaves | |
| rect = new Rectangle(4*square_width, 3*square_width, 2*square_width, square_width); | |
| g2d.setComposite(ac); | |
| g2d.setColor(Color.green); | |
| g2d.fill(rect); | |
| g2d.drawImage(bi, null, 0, 0); | |
| } | |
| // Load in the water texture separately and pretend it's a part of the main texture pack. | |
| BufferedImage bi2 = buildImageFromInput(getMinecraftWaterData()); | |
| int water_width = bi2.getWidth(); | |
| g2d.setComposite(AlphaComposite.Src); | |
| if (square_width < water_width) | |
| { | |
| g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
| g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); | |
| } | |
| else | |
| { | |
| g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); | |
| g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); | |
| } | |
| g2d.drawImage(bi2, 15*square_width, 12*square_width, square_width, square_width, null); | |
| // Also create a fake sort of "fire" graphic to use | |
| bi2 = buildImageFromInput(getMinecraftParticleData()); | |
| int particle_width = bi2.getWidth()/16; | |
| int fire_x = 15; | |
| int fire_y = 1; | |
| int flame_x = 0; | |
| int flame_y = 3; | |
| int start_fire_x = fire_x*square_width; | |
| int start_fire_y = fire_y*square_width; | |
| int start_flame_x = flame_x*particle_width; | |
| int start_flame_y = flame_y*particle_width; | |
| g2d.setComposite(AlphaComposite.Src); | |
| g2d.setColor(new Color(0f, 0f, 0f, 0f)); | |
| g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); | |
| g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); | |
| g2d.fillRect(fire_x*square_width, fire_y*square_width, square_width, square_width); | |
| if (square_width < (particle_width*2)) | |
| { | |
| g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
| g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); | |
| } | |
| g2d.drawImage(bi2, | |
| start_fire_x, start_fire_y, start_fire_x+(square_width/2), start_fire_y+(square_width/2), | |
| start_flame_x, start_flame_y, start_flame_x+particle_width, start_flame_y+particle_width, | |
| null); | |
| g2d.drawImage(bi2, | |
| start_fire_x+(square_width/2), start_fire_y, start_fire_x+square_width, start_fire_y+(square_width/2), | |
| start_flame_x, start_flame_y, start_flame_x+particle_width, start_flame_y+particle_width, | |
| null); | |
| g2d.drawImage(bi2, | |
| start_fire_x, start_fire_y+(square_width/2), start_fire_x+(square_width/2), start_fire_y+square_width, | |
| start_flame_x, start_flame_y, start_flame_x+particle_width, start_flame_y+particle_width, | |
| null); | |
| g2d.drawImage(bi2, | |
| start_fire_x+(square_width/2), start_fire_y+(square_width/2), start_fire_x+square_width, start_fire_y+square_width, | |
| start_flame_x, start_flame_y, start_flame_x+particle_width, start_flame_y+particle_width, | |
| null); | |
| return bi; | |
| } | |
| /*** | |
| * Attempts to create a bufferedImage containing the font | |
| * @return | |
| */ | |
| public static BufferedImage getMinecraftFont() { | |
| return buildImageFromInput(getMinecraftFontData()); | |
| } | |
| /*** | |
| * Creates an Inputstream to a file in the bin/ directory in the minecraft directory. | |
| * This handles overrides. If a file exists in the bin/ directory it will load that. | |
| * Otherwise it will look in the .jar file of minecraft | |
| * @param fileName | |
| * @return | |
| */ | |
| public static InputStream getMinecraftFile(String fileName){ | |
| File overrideFile = new File(baseDir, "bin/" + fileName); | |
| if(overrideFile.exists()) { | |
| try { | |
| return new FileInputStream(overrideFile); | |
| } catch (FileNotFoundException e) { | |
| // TODO Auto-generated catch block | |
| e.printStackTrace(); | |
| return null; | |
| } | |
| } | |
| File minecraftDataFile = new File(baseDir, "bin/minecraft.jar"); | |
| if(!minecraftDataFile.exists()) { | |
| return null; | |
| } | |
| try { | |
| JarFile jf = new JarFile(minecraftDataFile); | |
| ZipEntry zipEntry = jf.getEntry(fileName); | |
| if(zipEntry == null) { | |
| return null; | |
| } | |
| return jf.getInputStream(zipEntry); | |
| } catch (IOException e) { | |
| System.out.println(e.toString()); | |
| return null; | |
| } | |
| } | |
| } |