work on texture pack redo
This commit is contained in:
parent
3f648e6692
commit
1e4f407976
|
@ -13,10 +13,9 @@ import com.jcraft.jzlib.InflaterInputStream;
|
|||
import org.json.JSONObject;
|
||||
|
||||
public class AssetRepository {
|
||||
|
||||
private static final HashMap<String,byte[]> filePool = new HashMap<>();
|
||||
private static final HashMap<String,byte[]> filePoolTemp = new HashMap<>();
|
||||
public static final HashMap<String, String> fileNameOverrides = new HashMap<>();
|
||||
|
||||
private static final HashMap<String,byte[]> filePool = new HashMap();
|
||||
public static final HashMap<String, String> fileNameOverrides = new HashMap();
|
||||
|
||||
public static final void loadOverrides(JSONObject json) {
|
||||
JSONObject overrides = json.optJSONObject("assetOverrides", null);
|
||||
|
@ -34,39 +33,13 @@ public class AssetRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private static byte[] def = null;
|
||||
|
||||
public static final void reset() throws IOException {
|
||||
if (def != null) {
|
||||
filePool.clear();
|
||||
install(def);
|
||||
}
|
||||
}
|
||||
|
||||
public static final void installTemp(byte[] pkg) throws IOException {
|
||||
filePoolTemp.clear();
|
||||
filePoolTemp.putAll(filePool);
|
||||
reset();
|
||||
install(pkg);
|
||||
}
|
||||
|
||||
public static final void resetTemp() throws IOException {
|
||||
filePool.clear();
|
||||
filePool.putAll(filePoolTemp);
|
||||
filePoolTemp.clear();
|
||||
}
|
||||
|
||||
public static final void install(byte[] pkg) throws IOException {
|
||||
if (def == null) {
|
||||
def = pkg;
|
||||
}
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(pkg);
|
||||
|
||||
|
||||
byte[] header = new byte[8];
|
||||
in.read(header);
|
||||
String type = readASCII(header);
|
||||
|
||||
|
||||
if("EAGPKG$$".equals(type)) {
|
||||
int l = pkg.length - 16;
|
||||
if(l < 1) {
|
||||
|
@ -86,15 +59,15 @@ public class AssetRepository {
|
|||
throw new IOException("invalid epk file type '" + type + "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final int loadShort(InputStream is) throws IOException {
|
||||
return (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
|
||||
private static final int loadInt(InputStream is) throws IOException {
|
||||
return (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
|
||||
private static final String readASCII(byte[] bytesIn) throws IOException {
|
||||
char[] charIn = new char[bytesIn.length];
|
||||
for(int i = 0; i < bytesIn.length; ++i) {
|
||||
|
@ -102,7 +75,7 @@ public class AssetRepository {
|
|||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
|
||||
private static final String readASCII(InputStream bytesIn) throws IOException {
|
||||
int len = bytesIn.read();
|
||||
char[] charIn = new char[len];
|
||||
|
@ -111,54 +84,54 @@ public class AssetRepository {
|
|||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
|
||||
public static final void loadNew(InputStream is) throws IOException {
|
||||
|
||||
|
||||
String vers = readASCII(is);
|
||||
if(!vers.startsWith("ver2.")) {
|
||||
throw new IOException("Unknown or invalid EPK version: " + vers);
|
||||
}
|
||||
|
||||
|
||||
is.skip(is.read()); // skip filename
|
||||
is.skip(loadShort(is)); // skip comment
|
||||
is.skip(8); // skip millis date
|
||||
|
||||
|
||||
int numFiles = loadInt(is);
|
||||
|
||||
|
||||
char compressionType = (char)is.read();
|
||||
|
||||
|
||||
InputStream zis;
|
||||
switch(compressionType) {
|
||||
case 'G':
|
||||
zis = new GZIPInputStream(is);
|
||||
break;
|
||||
case 'Z':
|
||||
zis = new InflaterInputStream(is);
|
||||
break;
|
||||
case '0':
|
||||
zis = is;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
|
||||
case 'G':
|
||||
zis = new GZIPInputStream(is);
|
||||
break;
|
||||
case 'Z':
|
||||
zis = new InflaterInputStream(is);
|
||||
break;
|
||||
case '0':
|
||||
zis = is;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
|
||||
}
|
||||
|
||||
int blockFile = ('F' << 24) | ('I' << 16) | ('L' << 8) | 'E';
|
||||
int blockEnd = ('E' << 24) | ('N' << 16) | ('D' << 8) | '$';
|
||||
int blockHead = ('H' << 24) | ('E' << 16) | ('A' << 8) | 'D';
|
||||
|
||||
|
||||
CRC32 crc32 = new CRC32();
|
||||
int blockType;
|
||||
for(int i = 0; i < numFiles; ++i) {
|
||||
|
||||
|
||||
blockType = loadInt(zis);
|
||||
|
||||
|
||||
if(blockType == blockEnd) {
|
||||
throw new IOException("Unexpected END when there are still " + (numFiles - i) + " files remaining");
|
||||
}
|
||||
|
||||
|
||||
String name = readASCII(zis);
|
||||
int len = loadInt(zis);
|
||||
|
||||
|
||||
if(i == 0) {
|
||||
if(blockType == blockHead) {
|
||||
byte[] readType = new byte[len];
|
||||
|
@ -174,14 +147,14 @@ public class AssetRepository {
|
|||
throw new IOException("File '" + name + "' did not have a file-type block as the first entry in the file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(blockType == blockFile) {
|
||||
if(len < 5) {
|
||||
throw new IOException("File '" + name + "' is incomplete");
|
||||
}
|
||||
|
||||
|
||||
int expectedCRC = loadInt(zis);
|
||||
|
||||
|
||||
byte[] load = new byte[len - 5];
|
||||
zis.read(load);
|
||||
|
||||
|
@ -192,13 +165,13 @@ public class AssetRepository {
|
|||
throw new IOException("File '" + name + "' has an invalid checksum");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(zis.read() != ':') {
|
||||
throw new IOException("File '" + name + "' is incomplete");
|
||||
}
|
||||
|
||||
|
||||
filePool.put(name, load);
|
||||
|
||||
|
||||
if(name.endsWith("title/eagtek.png")) {
|
||||
try {
|
||||
int off = 27375;
|
||||
|
@ -218,14 +191,14 @@ public class AssetRepository {
|
|||
throw new IOException("Object '" + name + "' is incomplete");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(loadInt(zis) != blockEnd) {
|
||||
throw new IOException("EPK missing END$ object");
|
||||
}
|
||||
|
||||
|
||||
zis.close();
|
||||
}
|
||||
|
||||
|
||||
public static final void loadOld(InputStream is) throws IOException {
|
||||
DataInputStream in = new DataInputStream(is);
|
||||
in.readUTF();
|
||||
|
@ -248,7 +221,7 @@ public class AssetRepository {
|
|||
}
|
||||
if(in.available() > 0 || !" end".equals(s)) throw new IOException("invalid epk file");
|
||||
}
|
||||
|
||||
|
||||
public static final byte[] getResource(String path) {
|
||||
if(path.startsWith("/")) path = path.substring(1);
|
||||
return filePool.get(path);
|
||||
|
@ -258,4 +231,4 @@ public class AssetRepository {
|
|||
filePool.put(path, EaglerAdapter.downloadURL(url));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
217
src/main/java/net/lax1dude/eaglercraft/EPKDecompiler.java
Normal file
217
src/main/java/net/lax1dude/eaglercraft/EPKDecompiler.java
Normal file
|
@ -0,0 +1,217 @@
|
|||
package net.lax1dude.eaglercraft;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.jcraft.jzlib.CRC32;
|
||||
import com.jcraft.jzlib.GZIPInputStream;
|
||||
import com.jcraft.jzlib.InflaterInputStream;
|
||||
|
||||
public class EPKDecompiler {
|
||||
|
||||
public static class FileEntry {
|
||||
public final String type;
|
||||
public final String name;
|
||||
public final byte[] data;
|
||||
protected FileEntry(String type, String name, byte[] data) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArrayInputStream in2;
|
||||
private DataInputStream in;
|
||||
private InputStream zis;
|
||||
private SHA1Digest dg;
|
||||
private CRC32 crc32;
|
||||
private int numFiles;
|
||||
private boolean isFinished = false;
|
||||
private boolean isOldFormat = false;
|
||||
|
||||
public EPKDecompiler(byte[] data) throws IOException {
|
||||
in2 = new ByteArrayInputStream(data);
|
||||
|
||||
byte[] header = new byte[8];
|
||||
in2.read(header);
|
||||
|
||||
if(Arrays.equals(header, new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36})) {
|
||||
byte[] endCode = new byte[] { (byte)':', (byte)':', (byte)':', (byte)'Y',
|
||||
(byte)'E', (byte)'E', (byte)':', (byte)'>' };
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
if(data[data.length - 8 + i] != endCode[i]) {
|
||||
throw new IOException("EPK file is missing EOF code (:::YEE:>)");
|
||||
}
|
||||
}
|
||||
in2 = new ByteArrayInputStream(data, 8, data.length - 16);
|
||||
initNew();
|
||||
}else if(Arrays.equals(header, new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)33,(byte)33})) {
|
||||
initOld();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isOld() {
|
||||
return isOldFormat;
|
||||
}
|
||||
|
||||
public FileEntry readFile() throws IOException {
|
||||
if(!isOldFormat) {
|
||||
return readFileNew();
|
||||
}else {
|
||||
return readFileOld();
|
||||
}
|
||||
}
|
||||
|
||||
private void initNew() throws IOException {
|
||||
InputStream is = in2;
|
||||
|
||||
String vers = readASCII(is);
|
||||
if(!vers.startsWith("ver2.")) {
|
||||
throw new IOException("Unknown or invalid EPK version: " + vers);
|
||||
}
|
||||
|
||||
is.skip(is.read()); // skip filename
|
||||
is.skip(loadShort(is)); // skip comment
|
||||
is.skip(8); // skip millis date
|
||||
|
||||
numFiles = loadInt(is);
|
||||
|
||||
char compressionType = (char)is.read();
|
||||
|
||||
switch(compressionType) {
|
||||
case 'G':
|
||||
zis = new GZIPInputStream(is);
|
||||
break;
|
||||
case 'Z':
|
||||
zis = new InflaterInputStream(is);
|
||||
break;
|
||||
case '0':
|
||||
zis = is;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
|
||||
}
|
||||
|
||||
crc32 = new CRC32();
|
||||
|
||||
}
|
||||
|
||||
private FileEntry readFileNew() throws IOException {
|
||||
if(isFinished) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] typeBytes = new byte[4];
|
||||
zis.read(typeBytes);
|
||||
String type = readASCII(typeBytes);
|
||||
|
||||
if(numFiles == 0) {
|
||||
if(!"END$".equals(type)) {
|
||||
throw new IOException("EPK file is missing END code (END$)");
|
||||
}
|
||||
isFinished = true;
|
||||
return null;
|
||||
}else {
|
||||
if("END$".equals(type)) {
|
||||
throw new IOException("Unexpected END when there are still " + numFiles + " files remaining");
|
||||
}else {
|
||||
String name = readASCII(zis);
|
||||
int len = loadInt(zis);
|
||||
byte[] data;
|
||||
|
||||
if("FILE".equals(type)) {
|
||||
if(len < 5) {
|
||||
throw new IOException("File '" + name + "' is incomplete (no crc)");
|
||||
}
|
||||
|
||||
int loadedCrc = loadInt(zis);
|
||||
|
||||
data = new byte[len - 5];
|
||||
zis.read(data);
|
||||
|
||||
crc32.reset();
|
||||
crc32.update(data, 0, data.length);
|
||||
if((int)crc32.getValue() != loadedCrc) {
|
||||
throw new IOException("File '" + name + "' has an invalid checksum");
|
||||
}
|
||||
|
||||
if(zis.read() != ':') {
|
||||
throw new IOException("File '" + name + "' is incomplete");
|
||||
}
|
||||
}else {
|
||||
data = new byte[len];
|
||||
zis.read(data);
|
||||
}
|
||||
|
||||
if(zis.read() != '>') {
|
||||
throw new IOException("Object '" + name + "' is incomplete");
|
||||
}
|
||||
|
||||
--numFiles;
|
||||
return new FileEntry(type, name, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int loadShort(InputStream is) throws IOException {
|
||||
return (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
private static final int loadInt(InputStream is) throws IOException {
|
||||
return (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
public static final String readASCII(byte[] bytesIn) throws IOException {
|
||||
char[] charIn = new char[bytesIn.length];
|
||||
for(int i = 0; i < bytesIn.length; ++i) {
|
||||
charIn[i] = (char)((int)bytesIn[i] & 0xFF);
|
||||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
private static final String readASCII(InputStream bytesIn) throws IOException {
|
||||
int len = bytesIn.read();
|
||||
char[] charIn = new char[len];
|
||||
for(int i = 0; i < len; ++i) {
|
||||
charIn[i] = (char)(bytesIn.read() & 0xFF);
|
||||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
private void initOld() throws IOException {
|
||||
isOldFormat = true;
|
||||
dg = new SHA1Digest();
|
||||
in = new DataInputStream(in2);
|
||||
in.readUTF();
|
||||
in = new DataInputStream(new InflaterInputStream(in2));
|
||||
}
|
||||
|
||||
private FileEntry readFileOld() throws IOException {
|
||||
if(isFinished) {
|
||||
return null;
|
||||
}
|
||||
String s = in.readUTF();
|
||||
if(s.equals(" end")) {
|
||||
isFinished = true;
|
||||
return null;
|
||||
}else if(!s.equals("<file>")) {
|
||||
throw new IOException("invalid epk file");
|
||||
}
|
||||
String path = in.readUTF();
|
||||
byte[] digest = new byte[20];
|
||||
byte[] digest2 = new byte[20];
|
||||
in.read(digest);
|
||||
int len = in.readInt();
|
||||
byte[] file = new byte[len];
|
||||
in.read(file);
|
||||
dg.update(file, 0, len); dg.doFinal(digest2, 0);
|
||||
if(!Arrays.equals(digest, digest2)) throw new IOException("invalid file hash for "+path);
|
||||
if(!"</file>".equals(in.readUTF())) throw new IOException("invalid epk file");
|
||||
return new FileEntry("FILE", path, file);
|
||||
}
|
||||
|
||||
}
|
|
@ -93,7 +93,7 @@ public class GuiScreenRelay extends GuiScreen {
|
|||
}
|
||||
lastRefresh += 60l;
|
||||
} else if(btn.id == 6) {
|
||||
EaglerAdapter.downloadBytes("EaglerSPRelay.zip", AssetRepository.getResource("relay_download.zip"));
|
||||
EaglerAdapter.downloadBytes("EaglerSPRelay.zip", EaglerAdapter.loadResourceBytes("relay_download.zip"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ public class TextureTerrainMap implements IconRegister {
|
|||
}
|
||||
|
||||
private void loadData() {
|
||||
byte[] data = EaglerAdapter.loadResourceBytes("/" + map.basePath + name + ".png");
|
||||
byte[] data = Minecraft.getMinecraft().texturePackList.getSelectedTexturePack().getResourceAsBytes("/" + map.basePath + name + ".png");
|
||||
if(data == null) {
|
||||
map.replaceTexture(this, map.missingData);
|
||||
}else {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package net.minecraft.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.AssetRepository;
|
||||
import net.lax1dude.eaglercraft.DefaultSkinRenderer;
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.EaglerProfile;
|
||||
|
@ -19,7 +17,6 @@ import net.lax1dude.eaglercraft.IntegratedServer;
|
|||
import net.lax1dude.eaglercraft.IntegratedServerLAN;
|
||||
import net.lax1dude.eaglercraft.Voice;
|
||||
import net.lax1dude.eaglercraft.WorkerNetworkManager;
|
||||
import net.lax1dude.eaglercraft.adapter.SimpleStorage;
|
||||
import net.lax1dude.eaglercraft.adapter.Tessellator;
|
||||
import net.lax1dude.eaglercraft.glemu.FixedFunctionShader;
|
||||
import net.minecraft.src.AchievementList;
|
||||
|
@ -83,7 +80,6 @@ import net.minecraft.src.StatCollector;
|
|||
import net.minecraft.src.StatStringFormatKeyInv;
|
||||
import net.minecraft.src.StringTranslate;
|
||||
import net.minecraft.src.TextureManager;
|
||||
import net.minecraft.src.TexturePackCustom;
|
||||
import net.minecraft.src.TexturePackList;
|
||||
import net.minecraft.src.Timer;
|
||||
import net.minecraft.src.WorldClient;
|
||||
|
@ -339,14 +335,6 @@ public class Minecraft implements Runnable {
|
|||
|
||||
String s = EaglerAdapter.getServerToJoinOnLaunch();
|
||||
GuiScreen scr;
|
||||
|
||||
if (!TexturePackList.defaultTexturePack.getTexturePackFileName().equals(this.gameSettings.skin)) {
|
||||
try {
|
||||
AssetRepository.reset();
|
||||
AssetRepository.install(SimpleStorage.get(this.gameSettings.skin));
|
||||
this.texturePackList.selectedTexturePack = new TexturePackCustom(this.gameSettings.skin, TexturePackList.defaultTexturePack);
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
if(s != null) {
|
||||
scr = new GuiScreenEditProfile(new GuiConnecting(new GuiMainMenu(), this, new ServerData("Eaglercraft Server", s, false)));
|
||||
|
|
|
@ -9,6 +9,7 @@ import net.lax1dude.eaglercraft.EaglerImage;
|
|||
import net.lax1dude.eaglercraft.EaglercraftRandom;
|
||||
import net.lax1dude.eaglercraft.TextureLocation;
|
||||
import net.lax1dude.eaglercraft.adapter.Tessellator;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class FontRenderer {
|
||||
/** Array of width of all the characters in default.png */
|
||||
|
@ -131,7 +132,7 @@ public class FontRenderer {
|
|||
|
||||
private void readFontTexture(String par1Str) {
|
||||
//EaglerImage e = EaglerImage.loadImage(EaglerAdapter.loadResourceBytes(par1Str));
|
||||
EaglerImage e = EaglerAdapter.loadPNG(EaglerAdapter.loadResourceBytes(par1Str));
|
||||
EaglerImage e = EaglerAdapter.loadPNG(Minecraft.getMinecraft().texturePackList.getSelectedTexturePack().getResourceAsBytes(par1Str));
|
||||
int[] var5 = e.data;
|
||||
int var3 = e.w;
|
||||
int var4 = e.h;
|
||||
|
@ -174,7 +175,7 @@ public class FontRenderer {
|
|||
}
|
||||
|
||||
private void readGlyphSizes() {
|
||||
this.glyphWidth = EaglerAdapter.loadResourceBytes("/font/glyph_sizes.bin");
|
||||
this.glyphWidth = Minecraft.getMinecraft().texturePackList.getSelectedTexturePack().getResourceAsBytes("/font/glyph_sizes.bin");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,7 +5,6 @@ import java.util.Calendar;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.AssetRepository;
|
||||
import net.lax1dude.eaglercraft.ConfigConstants;
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.EaglercraftRandom;
|
||||
|
@ -172,7 +171,7 @@ public class GuiMainMenu extends GuiScreen {
|
|||
this.field_92019_w = this.field_92021_u + 12;
|
||||
}
|
||||
|
||||
ConfigConstants.panoramaBlur = AssetRepository.getResource("/title/no-pano-blur.flag") == null;
|
||||
ConfigConstants.panoramaBlur = mc.texturePackList.getSelectedTexturePack().getResourceAsBytes("/title/no-pano-blur.flag") == null;
|
||||
|
||||
if(this.ackLines.isEmpty()) {
|
||||
int width = 315;
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.EPKDecompiler;
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.adapter.SimpleStorage;
|
||||
import net.lax1dude.eaglercraft.EaglerInflater;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.vfs.VFile;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class GuiTexturePacks extends GuiScreen {
|
||||
protected GuiScreen guiScreen;
|
||||
|
@ -33,7 +40,7 @@ public class GuiTexturePacks extends GuiScreen {
|
|||
this.buttonList.add(new GuiSmallButton(5, this.width / 2 - 154, this.height - 48, var1.translateKey("texturePack.openFolder")));
|
||||
this.buttonList.add(new GuiSmallButton(6, this.width / 2 + 4, this.height - 48, var1.translateKey("gui.done")));
|
||||
this.mc.texturePackList.updateAvaliableTexturePacks();
|
||||
//this.fileLocation = (new File("texturepacks")).getAbsolutePath();
|
||||
this.fileLocation = "texturepacks";
|
||||
this.guiTexturePackSlot = new GuiTexturePackSlot(this);
|
||||
this.guiTexturePackSlot.registerScrollButtons(this.buttonList, 7, 8);
|
||||
}
|
||||
|
@ -98,7 +105,26 @@ public class GuiTexturePacks extends GuiScreen {
|
|||
if (isSelectingPack && EaglerAdapter.getFileChooserResultAvailable()) {
|
||||
isSelectingPack = false;
|
||||
String name = EaglerAdapter.getFileChooserResultName();
|
||||
SimpleStorage.set(name.replaceAll("[^A-Za-z0-9_]", "_"), name.toLowerCase().endsWith(".zip") ? TexturePackList.zipToEpk(EaglerAdapter.getFileChooserResult()) : EaglerAdapter.getFileChooserResult());
|
||||
String safeName = name.replaceAll("[^A-Za-z0-9_]", "_");
|
||||
try {
|
||||
if (name.toLowerCase().endsWith(".zip")) {
|
||||
ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(EaglerAdapter.getFileChooserResult()));
|
||||
ZipEntry entry;
|
||||
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||
if (entry.isDirectory()) continue;
|
||||
new VFile(fileLocation, safeName, entry.getName()).setAllBytes(EaglerInflater.getBytesFromInputStream(zipInputStream));
|
||||
}
|
||||
zipInputStream.close();
|
||||
} else {
|
||||
EPKDecompiler epkDecompiler = new EPKDecompiler(EaglerAdapter.getFileChooserResult());
|
||||
EPKDecompiler.FileEntry file;
|
||||
while ((file = epkDecompiler.readFile()) != null) {
|
||||
new VFile(fileLocation, safeName, file.name).setAllBytes(file.data);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
EaglerAdapter.clearFileChooserResult();
|
||||
this.mc.displayGuiScreen(this);
|
||||
}
|
||||
|
@ -111,7 +137,7 @@ public class GuiTexturePacks extends GuiScreen {
|
|||
List var3 = this.mc.texturePackList.availableTexturePacks();
|
||||
|
||||
if (par1) {
|
||||
SimpleStorage.set(((ITexturePack) var3.get(par2)).getTexturePackFileName(), null);
|
||||
new VFile(fileLocation, ((ITexturePack) var3.get(par2)).getTexturePackFileName()).deleteAll();
|
||||
} else {
|
||||
try {
|
||||
this.mc.texturePackList.setTexturePack((ITexturePack) var3.get(par2));
|
||||
|
@ -122,7 +148,7 @@ public class GuiTexturePacks extends GuiScreen {
|
|||
this.mc.texturePackList.setTexturePack((ITexturePack) var3.get(0));
|
||||
this.mc.renderEngine.refreshTextures();
|
||||
this.mc.renderGlobal.loadRenderers();
|
||||
SimpleStorage.set(((ITexturePack) var3.get(par2)).getTexturePackFileName(), null);
|
||||
new VFile(fileLocation, ((ITexturePack) var3.get(par2)).getTexturePackFileName()).deleteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface ITexturePack {
|
||||
public interface ITexturePack
|
||||
{
|
||||
/**
|
||||
* Delete the OpenGL texture id of the pack's thumbnail image, and close the zip
|
||||
* file in case of TexturePackCustom.
|
||||
* Delete the OpenGL texture id of the pack's thumbnail image, and close the zip file in case of TexturePackCustom.
|
||||
*/
|
||||
void deleteTexturePack(RenderEngine var1);
|
||||
|
||||
|
@ -14,12 +15,12 @@ public interface ITexturePack {
|
|||
*/
|
||||
void bindThumbnailTexture(RenderEngine var1);
|
||||
|
||||
InputStream func_98137_a(String var1, boolean var2);
|
||||
InputStream func_98137_a(String var1, boolean var2) throws IOException;
|
||||
|
||||
/**
|
||||
* Gives a texture resource as InputStream.
|
||||
*/
|
||||
InputStream getResourceAsStream(String var1);
|
||||
byte[] getResourceAsBytes(String var1);
|
||||
|
||||
/**
|
||||
* Get the texture pack ID
|
||||
|
@ -27,26 +28,21 @@ public interface ITexturePack {
|
|||
String getTexturePackID();
|
||||
|
||||
/**
|
||||
* Get the file name of the texture pack, or Default if not from a custom
|
||||
* texture pack
|
||||
* Get the file name of the texture pack, or Default if not from a custom texture pack
|
||||
*/
|
||||
String getTexturePackFileName();
|
||||
|
||||
/**
|
||||
* Get the first line of the texture pack description (read from the pack.txt
|
||||
* file)
|
||||
* Get the first line of the texture pack description (read from the pack.txt file)
|
||||
*/
|
||||
String getFirstDescriptionLine();
|
||||
|
||||
/**
|
||||
* Get the second line of the texture pack description (read from the pack.txt
|
||||
* file)
|
||||
* Get the second line of the texture pack description (read from the pack.txt file)
|
||||
*/
|
||||
String getSecondDescriptionLine();
|
||||
|
||||
boolean func_98138_b(String var1, boolean var2);
|
||||
|
||||
boolean isCompatible();
|
||||
|
||||
byte[] getResourceAsBytes(String par1Str);
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.lax1dude.eaglercraft.AssetRepository;
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.adapter.SimpleStorage;
|
||||
|
||||
public class TexturePackCustom extends TexturePackImplementation {
|
||||
public TexturePackCustom(String name, ITexturePack base) {
|
||||
super(name, name, base);
|
||||
try {
|
||||
AssetRepository.installTemp(SimpleStorage.get(name));
|
||||
this.loadThumbnailImage();
|
||||
this.loadDescription();
|
||||
AssetRepository.resetTemp();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
public boolean func_98140_c(String par1Str) {
|
||||
return EaglerAdapter.loadResource(par1Str) != null;
|
||||
}
|
||||
|
||||
public boolean isCompatible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected InputStream func_98139_b(String par1Str) throws IOException {
|
||||
return EaglerAdapter.loadResource(par1Str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getResourceAsBytes(String par1Str) {
|
||||
return EaglerAdapter.loadResourceBytes(par1Str);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import net.lax1dude.eaglercraft.EaglerAdapter;
|
|||
|
||||
public class TexturePackDefault extends TexturePackImplementation {
|
||||
public TexturePackDefault() {
|
||||
super("default", "Default", (ITexturePack) null);
|
||||
super("default", null, "Default", (ITexturePack) null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ public class TexturePackDefault extends TexturePackImplementation {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected InputStream func_98139_b(String par1Str) throws IOException {
|
||||
protected InputStream func_98139_b(String par1Str) {
|
||||
return EaglerAdapter.loadResource(par1Str);
|
||||
}
|
||||
|
||||
|
|
40
src/main/java/net/minecraft/src/TexturePackFolder.java
Normal file
40
src/main/java/net/minecraft/src/TexturePackFolder.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.vfs.VFile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class TexturePackFolder extends TexturePackImplementation
|
||||
{
|
||||
public TexturePackFolder(String par1, VFile par2, ITexturePack par3ITexturePack)
|
||||
{
|
||||
super(par1, par2, par2.getName(), par3ITexturePack);
|
||||
}
|
||||
|
||||
protected InputStream func_98139_b(String par1Str) throws IOException
|
||||
{
|
||||
VFile var2 = new VFile(this.texturePackFile, par1Str.substring(1));
|
||||
|
||||
if (!var2.exists())
|
||||
{
|
||||
throw new IOException(par1Str);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BufferedInputStream(var2.getInputStream());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean func_98140_c(String par1Str)
|
||||
{
|
||||
VFile var2 = new VFile(this.texturePackFile, par1Str);
|
||||
return var2.exists();
|
||||
}
|
||||
|
||||
public boolean isCompatible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -7,21 +7,26 @@ import java.io.InputStreamReader;
|
|||
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.EaglerImage;
|
||||
import net.lax1dude.eaglercraft.TextureLocation;
|
||||
import net.lax1dude.eaglercraft.EaglerInflater;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.vfs.VFile;
|
||||
|
||||
public abstract class TexturePackImplementation implements ITexturePack {
|
||||
public abstract class TexturePackImplementation implements ITexturePack
|
||||
{
|
||||
/**
|
||||
* Texture pack ID as returnd by generateTexturePackID(). Used only internally
|
||||
* and not visible to the user.
|
||||
* Texture pack ID as returnd by generateTexturePackID(). Used only internally and not visible to the user.
|
||||
*/
|
||||
private final String texturePackID;
|
||||
|
||||
/**
|
||||
* The name of the texture pack's zip file/directory or "Default" for the
|
||||
* builtin texture pack. Shown in the GUI.
|
||||
* The name of the texture pack's zip file/directory or "Default" for the builtin texture pack. Shown in the GUI.
|
||||
*/
|
||||
private final String texturePackFileName;
|
||||
|
||||
/**
|
||||
* File object for the texture pack's zip file in TexturePackCustom or the directory in TexturePackFolder.
|
||||
*/
|
||||
protected final VFile texturePackFile;
|
||||
|
||||
/**
|
||||
* First line of texture pack description (from /pack.txt) displayed in the GUI
|
||||
*/
|
||||
|
@ -39,9 +44,11 @@ public abstract class TexturePackImplementation implements ITexturePack {
|
|||
/** The texture id for this pcak's thumbnail image. */
|
||||
private int thumbnailTextureName = -1;
|
||||
|
||||
protected TexturePackImplementation(String par1, String par3Str, ITexturePack par4ITexturePack) {
|
||||
protected TexturePackImplementation(String par1, VFile par2File, String par3Str, ITexturePack par4ITexturePack)
|
||||
{
|
||||
this.texturePackID = par1;
|
||||
this.texturePackFileName = par3Str;
|
||||
this.texturePackFile = par2File;
|
||||
this.field_98141_g = par4ITexturePack;
|
||||
this.loadThumbnailImage();
|
||||
this.loadDescription();
|
||||
|
@ -50,8 +57,10 @@ public abstract class TexturePackImplementation implements ITexturePack {
|
|||
/**
|
||||
* Truncate strings to at most 34 characters. Truncates description lines
|
||||
*/
|
||||
private static String trimStringToGUIWidth(String par0Str) {
|
||||
if (par0Str != null && par0Str.length() > 34) {
|
||||
private static String trimStringToGUIWidth(String par0Str)
|
||||
{
|
||||
if (par0Str != null && par0Str.length() > 34)
|
||||
{
|
||||
par0Str = par0Str.substring(0, 34);
|
||||
}
|
||||
|
||||
|
@ -61,48 +70,90 @@ public abstract class TexturePackImplementation implements ITexturePack {
|
|||
/**
|
||||
* Load and initialize thumbnailImage from the the /pack.png file.
|
||||
*/
|
||||
protected void loadThumbnailImage() {
|
||||
//this.thumbnailImage = EaglerImage.loadImage(EaglerAdapter.loadResourceBytes("/pack.png"));
|
||||
this.thumbnailImage = EaglerAdapter.loadPNG(EaglerAdapter.loadResourceBytes("/pack.png"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load texture pack description from /pack.txt file in the texture pack
|
||||
*/
|
||||
protected void loadDescription() {
|
||||
private void loadThumbnailImage()
|
||||
{
|
||||
InputStream var1 = null;
|
||||
BufferedReader var2 = null;
|
||||
|
||||
try {
|
||||
var1 = this.func_98139_b("/pack.txt");
|
||||
var2 = new BufferedReader(new InputStreamReader(var1));
|
||||
this.firstDescriptionLine = trimStringToGUIWidth(var2.readLine());
|
||||
this.secondDescriptionLine = trimStringToGUIWidth(var2.readLine());
|
||||
} catch (IOException var12) {
|
||||
try
|
||||
{
|
||||
var1 = this.func_98137_a("/pack.png", false);
|
||||
this.thumbnailImage = EaglerImage.loadImage(EaglerInflater.getBytesFromInputStream(var1));
|
||||
}
|
||||
catch (IOException var11)
|
||||
{
|
||||
;
|
||||
} finally {
|
||||
try {
|
||||
if (var2 != null) {
|
||||
var2.close();
|
||||
}
|
||||
|
||||
if (var1 != null) {
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (var1 != null)
|
||||
{
|
||||
var1.close();
|
||||
}
|
||||
} catch (IOException var11) {
|
||||
}
|
||||
catch (IOException var10)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream func_98137_a(String par1Str, boolean par2) {
|
||||
try {
|
||||
/**
|
||||
* Load texture pack description from /pack.txt file in the texture pack
|
||||
*/
|
||||
protected void loadDescription()
|
||||
{
|
||||
InputStream var1 = null;
|
||||
BufferedReader var2 = null;
|
||||
|
||||
try
|
||||
{
|
||||
var1 = this.func_98139_b("/pack.txt");
|
||||
var2 = new BufferedReader(new InputStreamReader(var1));
|
||||
this.firstDescriptionLine = trimStringToGUIWidth(var2.readLine());
|
||||
this.secondDescriptionLine = trimStringToGUIWidth(var2.readLine());
|
||||
}
|
||||
catch (IOException var12)
|
||||
{
|
||||
;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (var2 != null)
|
||||
{
|
||||
var2.close();
|
||||
}
|
||||
|
||||
if (var1 != null)
|
||||
{
|
||||
var1.close();
|
||||
}
|
||||
}
|
||||
catch (IOException var11)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream func_98137_a(String par1Str, boolean par2) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.func_98139_b(par1Str);
|
||||
} catch (IOException var4) {
|
||||
if (this.field_98141_g != null && par2) {
|
||||
}
|
||||
catch (IOException var4)
|
||||
{
|
||||
if (this.field_98141_g != null && par2)
|
||||
{
|
||||
return this.field_98141_g.func_98137_a(par1Str, true);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw var4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,47 +161,53 @@ public abstract class TexturePackImplementation implements ITexturePack {
|
|||
/**
|
||||
* Gives a texture resource as InputStream.
|
||||
*/
|
||||
public InputStream getResourceAsStream(String par1Str) {
|
||||
return this.func_98137_a(par1Str, true);
|
||||
public byte[] getResourceAsBytes(String par1Str)
|
||||
{
|
||||
try {
|
||||
return EaglerInflater.getBytesFromInputStream(this.func_98137_a(par1Str, true));
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract InputStream func_98139_b(String var1) throws IOException;
|
||||
|
||||
/**
|
||||
* Delete the OpenGL texture id of the pack's thumbnail image, and close the zip
|
||||
* file in case of TexturePackCustom.
|
||||
* Delete the OpenGL texture id of the pack's thumbnail image, and close the zip file in case of TexturePackCustom.
|
||||
*/
|
||||
public void deleteTexturePack(RenderEngine par1RenderEngine) {
|
||||
if (this.thumbnailImage != null && this.thumbnailTextureName != -1) {
|
||||
public void deleteTexturePack(RenderEngine par1RenderEngine)
|
||||
{
|
||||
if (this.thumbnailImage != null && this.thumbnailTextureName != -1)
|
||||
{
|
||||
par1RenderEngine.deleteTexture(this.thumbnailTextureName);
|
||||
}
|
||||
}
|
||||
|
||||
private static final TextureLocation tex_unknown_pack = new TextureLocation("/gui/unknown_pack.png");
|
||||
|
||||
|
||||
/**
|
||||
* Bind the texture id of the pack's thumbnail image, loading it if necessary.
|
||||
*/
|
||||
public void bindThumbnailTexture(RenderEngine par1RenderEngine) {
|
||||
if (this.thumbnailImage != null) {
|
||||
if (this.thumbnailTextureName == -1) {
|
||||
public void bindThumbnailTexture(RenderEngine par1RenderEngine)
|
||||
{
|
||||
if (this.thumbnailImage != null)
|
||||
{
|
||||
if (this.thumbnailTextureName == -1)
|
||||
{
|
||||
this.thumbnailTextureName = par1RenderEngine.allocateAndSetupTexture(this.thumbnailImage);
|
||||
}
|
||||
|
||||
EaglerAdapter.glBindTexture(EaglerAdapter.GL_TEXTURE_2D, this.thumbnailTextureName);
|
||||
par1RenderEngine.resetBoundTexture();
|
||||
} else {
|
||||
tex_unknown_pack.bindTexture();
|
||||
}
|
||||
else
|
||||
{
|
||||
par1RenderEngine.bindTexture("/gui/unknown_pack.png");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean func_98138_b(String par1Str, boolean par2) {
|
||||
try {
|
||||
boolean var3 = this.func_98140_c(par1Str);
|
||||
return !var3 && par2 && this.field_98141_g != null ? this.field_98141_g.func_98138_b(par1Str, par2) : var3;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
public boolean func_98138_b(String par1Str, boolean par2)
|
||||
{
|
||||
boolean var3 = this.func_98140_c(par1Str);
|
||||
return !var3 && par2 && this.field_98141_g != null ? this.field_98141_g.func_98138_b(par1Str, par2) : var3;
|
||||
}
|
||||
|
||||
public abstract boolean func_98140_c(String var1);
|
||||
|
@ -158,31 +215,32 @@ public abstract class TexturePackImplementation implements ITexturePack {
|
|||
/**
|
||||
* Get the texture pack ID
|
||||
*/
|
||||
public String getTexturePackID() {
|
||||
public String getTexturePackID()
|
||||
{
|
||||
return this.texturePackID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name of the texture pack, or Default if not from a custom
|
||||
* texture pack
|
||||
* Get the file name of the texture pack, or Default if not from a custom texture pack
|
||||
*/
|
||||
public String getTexturePackFileName() {
|
||||
public String getTexturePackFileName()
|
||||
{
|
||||
return this.texturePackFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first line of the texture pack description (read from the pack.txt
|
||||
* file)
|
||||
* Get the first line of the texture pack description (read from the pack.txt file)
|
||||
*/
|
||||
public String getFirstDescriptionLine() {
|
||||
public String getFirstDescriptionLine()
|
||||
{
|
||||
return this.firstDescriptionLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the second line of the texture pack description (read from the pack.txt
|
||||
* file)
|
||||
* Get the second line of the texture pack description (read from the pack.txt file)
|
||||
*/
|
||||
public String getSecondDescriptionLine() {
|
||||
public String getSecondDescriptionLine()
|
||||
{
|
||||
return this.secondDescriptionLine;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,71 +1,68 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.lax1dude.eaglercraft.AssetRepository;
|
||||
import net.lax1dude.eaglercraft.EPK2Compiler;
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.adapter.SimpleStorage;
|
||||
import net.lax1dude.eaglercraft.EaglerProfile;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.vfs.VFile;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class TexturePackList {
|
||||
public class TexturePackList
|
||||
{
|
||||
/**
|
||||
* An instance of TexturePackDefault for the always available builtin texture
|
||||
* pack.
|
||||
* An instance of TexturePackDefault for the always available builtin texture pack.
|
||||
*/
|
||||
public static final ITexturePack defaultTexturePack = new TexturePackDefault();
|
||||
private static final ITexturePack defaultTexturePack = new TexturePackDefault();
|
||||
|
||||
/** The Minecraft instance. */
|
||||
private final Minecraft mc;
|
||||
|
||||
/** The directory the texture packs will be loaded from. */
|
||||
//private final File texturePackDir;
|
||||
private final VFile texturePackDir;
|
||||
|
||||
/** Folder for the multi-player texturepacks. Returns File. */
|
||||
//private final File mpTexturePackFolder;
|
||||
private final VFile mpTexturePackFolder;
|
||||
|
||||
/** The list of the available texture packs. */
|
||||
private List availableTexturePacks = new ArrayList();
|
||||
|
||||
/**
|
||||
* A mapping of texture IDs to TexturePackBase objects used by
|
||||
* updateAvaliableTexturePacks() to avoid reloading texture packs that haven't
|
||||
* changed on disk.
|
||||
* A mapping of texture IDs to TexturePackBase objects used by updateAvaliableTexturePacks() to avoid reloading
|
||||
* texture packs that haven't changed on disk.
|
||||
*/
|
||||
private Map texturePackCache = new HashMap();
|
||||
|
||||
/** The TexturePack that will be used. */
|
||||
public ITexturePack selectedTexturePack;
|
||||
private ITexturePack selectedTexturePack;
|
||||
|
||||
/** True if a texture pack is downloading in the background. */
|
||||
private boolean isDownloading;
|
||||
|
||||
public TexturePackList(Minecraft par2Minecraft) {
|
||||
public TexturePackList(Minecraft par2Minecraft)
|
||||
{
|
||||
this.mc = par2Minecraft;
|
||||
//this.texturePackDir = new File(par1File, "texturepacks");
|
||||
//this.mpTexturePackFolder = new File(par1File, "texturepacks-mp-cache");
|
||||
this.texturePackDir = new VFile("texturepacks");
|
||||
this.mpTexturePackFolder = new VFile("texturepacks-mp-cache");
|
||||
this.updateAvaliableTexturePacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new TexturePack to be used, returning true if it has actually
|
||||
* changed, false if nothing changed.
|
||||
* Sets the new TexturePack to be used, returning true if it has actually changed, false if nothing changed.
|
||||
*/
|
||||
public boolean setTexturePack(ITexturePack par1ITexturePack) {
|
||||
if (par1ITexturePack == this.selectedTexturePack) {
|
||||
public boolean setTexturePack(ITexturePack par1ITexturePack)
|
||||
{
|
||||
if (par1ITexturePack == this.selectedTexturePack)
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isDownloading = false;
|
||||
this.selectedTexturePack = par1ITexturePack;
|
||||
try {
|
||||
AssetRepository.reset();
|
||||
AssetRepository.install(SimpleStorage.get(this.selectedTexturePack.getTexturePackFileName()));
|
||||
} catch (IOException ignored) {}
|
||||
this.mc.gameSettings.skin = par1ITexturePack.getTexturePackFileName();
|
||||
this.mc.gameSettings.saveOptions();
|
||||
return true;
|
||||
|
@ -73,38 +70,53 @@ public class TexturePackList {
|
|||
}
|
||||
|
||||
/**
|
||||
* filename must end in .zip or .epk
|
||||
* filename must end in .zip
|
||||
*/
|
||||
public void requestDownloadOfTexture(String par1Str) {
|
||||
public void requestDownloadOfTexture(String par1Str)
|
||||
{
|
||||
String var2 = par1Str.substring(par1Str.lastIndexOf("/") + 1);
|
||||
|
||||
if (var2.contains("?")) {
|
||||
if (var2.contains("?"))
|
||||
{
|
||||
var2 = var2.substring(0, var2.indexOf("?"));
|
||||
}
|
||||
|
||||
if (var2.toLowerCase().endsWith(".zip") || var2.toLowerCase().endsWith(".epk")) {
|
||||
this.downloadTexture(par1Str, var2);
|
||||
if (var2.endsWith(".zip"))
|
||||
{
|
||||
VFile var3 = new VFile(this.mpTexturePackFolder, var2);
|
||||
this.downloadTexture(par1Str, var3);
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadTexture(String par1Str, String par2File) {
|
||||
private void downloadTexture(String par1Str, VFile par2File)
|
||||
{
|
||||
HashMap var3 = new HashMap();
|
||||
GuiProgress var4 = new GuiProgress();
|
||||
var3.put("X-Minecraft-Username", EaglerProfile.username);
|
||||
var3.put("X-Minecraft-Version", "1.5.2");
|
||||
var3.put("X-Minecraft-Supported-Resolutions", "16");
|
||||
this.isDownloading = true;
|
||||
SimpleStorage.set(par2File.replaceAll("[^A-Za-z0-9_]", "_"), par2File.toLowerCase().endsWith(".zip") ? zipToEpk(EaglerAdapter.downloadURL(par1Str)) : EaglerAdapter.downloadURL(par1Str));
|
||||
this.onDownloadFinished();
|
||||
this.mc.displayGuiScreen(var4);
|
||||
// todo: extract epk/zip to VFS, activate, and signal success
|
||||
onDownloadFinished(); // temp
|
||||
// SimpleStorage.set(par2File.replaceAll("[^A-Za-z0-9_]", "_"), par2File.toLowerCase().endsWith(".zip") ? zipToEpk(EaglerAdapter.downloadURL(par1Str)) : EaglerAdapter.downloadURL(par1Str));
|
||||
// HttpUtil.downloadTexturePack(par2File, par1Str, new TexturePackDownloadSuccess(this), var3, 10000000, var4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a texture pack is downloading in the background.
|
||||
*/
|
||||
public boolean getIsDownloading() {
|
||||
public boolean getIsDownloading()
|
||||
{
|
||||
return this.isDownloading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from Minecraft.loadWorld() if getIsDownloading() returned true to
|
||||
* prepare the downloaded texture for usage.
|
||||
* Called from Minecraft.loadWorld() if getIsDownloading() returned true to prepare the downloaded texture for
|
||||
* usage.
|
||||
*/
|
||||
public void onDownloadFinished() {
|
||||
public void onDownloadFinished()
|
||||
{
|
||||
this.isDownloading = false;
|
||||
this.updateAvaliableTexturePacks();
|
||||
this.mc.scheduleTexturePackRefresh();
|
||||
|
@ -113,41 +125,43 @@ public class TexturePackList {
|
|||
/**
|
||||
* check the texture packs the client has installed
|
||||
*/
|
||||
public void updateAvaliableTexturePacks() {
|
||||
public void updateAvaliableTexturePacks()
|
||||
{
|
||||
ArrayList var1 = new ArrayList();
|
||||
this.selectedTexturePack = defaultTexturePack;
|
||||
var1.add(defaultTexturePack);
|
||||
Iterator var2 = this.getTexturePackDirContents().iterator();
|
||||
|
||||
while (var2.hasNext()) {
|
||||
String var3 = (String) var2.next();
|
||||
while (var2.hasNext())
|
||||
{
|
||||
VFile var3 = (VFile)var2.next();
|
||||
String var4 = this.generateTexturePackID(var3);
|
||||
|
||||
Object var5 = (ITexturePack) this.texturePackCache.get(var3);
|
||||
if (var4 != null)
|
||||
{
|
||||
Object var5 = (ITexturePack)this.texturePackCache.get(var4);
|
||||
|
||||
if (var5 == null) {
|
||||
try {
|
||||
var5 = new TexturePackCustom(var3, defaultTexturePack);
|
||||
this.texturePackCache.put(var3, var5);
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace(); // bad texture pack
|
||||
if (var5 == null)
|
||||
{
|
||||
var5 = new TexturePackFolder(var4, var3, defaultTexturePack);
|
||||
this.texturePackCache.put(var4, var5);
|
||||
}
|
||||
}
|
||||
|
||||
if (((ITexturePack) var5).getTexturePackFileName().equals(this.mc.gameSettings.skin)) {
|
||||
this.selectedTexturePack = (ITexturePack) var5;
|
||||
try {
|
||||
AssetRepository.reset();
|
||||
AssetRepository.install(SimpleStorage.get(this.selectedTexturePack.getTexturePackFileName()));
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
if (((ITexturePack)var5).getTexturePackFileName().equals(this.mc.gameSettings.skin))
|
||||
{
|
||||
this.selectedTexturePack = (ITexturePack)var5;
|
||||
}
|
||||
|
||||
var1.add(var5);
|
||||
var1.add(var5);
|
||||
}
|
||||
}
|
||||
|
||||
this.availableTexturePacks.removeAll(var1);
|
||||
var2 = this.availableTexturePacks.iterator();
|
||||
|
||||
while (var2.hasNext()) {
|
||||
ITexturePack var6 = (ITexturePack) var2.next();
|
||||
while (var2.hasNext())
|
||||
{
|
||||
ITexturePack var6 = (ITexturePack)var2.next();
|
||||
var6.deleteTexturePack(this.mc.renderEngine);
|
||||
this.texturePackCache.remove(var6.getTexturePackID());
|
||||
}
|
||||
|
@ -156,103 +170,104 @@ public class TexturePackList {
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate an internal texture pack ID from the file/directory name, last
|
||||
* modification time, and file size. Returns null if the file/directory is not a
|
||||
* texture pack.
|
||||
* Generate an internal texture pack ID from the file/directory name, last modification time, and file size. Returns
|
||||
* null if the file/directory is not a texture pack.
|
||||
*/
|
||||
// private String generateTexturePackID(File par1File) {
|
||||
// return par1File.isFile() && par1File.getName().toLowerCase().endsWith(".zip") ? par1File.getName() + ":" + par1File.length() + ":" + par1File.lastModified()
|
||||
// : (par1File.isDirectory() && (new File(par1File, "pack.txt")).exists() ? par1File.getName() + ":folder:" + par1File.lastModified() : null);
|
||||
// }
|
||||
private String generateTexturePackID(VFile par1File)
|
||||
{
|
||||
return (new VFile(par1File, "pack.txt")).exists() ? par1File.getName() + ":folder" : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a List<String> of file/directories in the texture pack directory.
|
||||
* Return a List<File> of file/directories in the texture pack directory.
|
||||
*/
|
||||
private List getTexturePackDirContents() {
|
||||
return SimpleStorage.isAvailable() ? Arrays.asList(SimpleStorage.list()) : Collections.emptyList();
|
||||
private List getTexturePackDirContents()
|
||||
{
|
||||
// TODO: MAKE THIS MORE EFFICIENT!! THIS IS A TEMPORARY FIX BC IM TIRED
|
||||
List<String> strings = this.texturePackDir.list();
|
||||
List<String> strings2 = new ArrayList<>();
|
||||
List<VFile> files = new ArrayList<>();
|
||||
for (String name : strings) {
|
||||
name = name.substring(this.texturePackDir.getPath().length() + 1);
|
||||
name = name.substring(0, name.indexOf('/'));
|
||||
name = this.texturePackDir.getPath() + "/" + name;
|
||||
if (strings2.contains(name)) continue;
|
||||
strings2.add(name);
|
||||
files.add(new VFile(name));
|
||||
}
|
||||
strings2.clear();
|
||||
strings.clear();
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the available texture packs.
|
||||
*/
|
||||
public List availableTexturePacks() {
|
||||
public List availableTexturePacks()
|
||||
{
|
||||
return Collections.unmodifiableList(this.availableTexturePacks);
|
||||
}
|
||||
|
||||
public ITexturePack getSelectedTexturePack() {
|
||||
if (this.selectedTexturePack == null) {
|
||||
this.selectedTexturePack = defaultTexturePack;
|
||||
}
|
||||
public ITexturePack getSelectedTexturePack()
|
||||
{
|
||||
return this.selectedTexturePack;
|
||||
}
|
||||
|
||||
public boolean func_77300_f() {
|
||||
if (!this.mc.gameSettings.serverTextures) {
|
||||
public boolean func_77300_f()
|
||||
{
|
||||
if (!this.mc.gameSettings.serverTextures)
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData var1 = this.mc.getServerData();
|
||||
return var1 == null ? true : var1.func_78840_c();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getAcceptsTextures() {
|
||||
if (!this.mc.gameSettings.serverTextures) {
|
||||
public boolean getAcceptsTextures()
|
||||
{
|
||||
if (!this.mc.gameSettings.serverTextures)
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData var1 = this.mc.getServerData();
|
||||
return var1 == null ? false : var1.getAcceptsTextures();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isDownloading(TexturePackList par0TexturePackList) {
|
||||
static boolean isDownloading(TexturePackList par0TexturePackList)
|
||||
{
|
||||
return par0TexturePackList.isDownloading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selectedTexturePack field (Inner class static accessor method).
|
||||
*/
|
||||
static ITexturePack setSelectedTexturePack(TexturePackList par0TexturePackList, ITexturePack par1ITexturePack) {
|
||||
static ITexturePack setSelectedTexturePack(TexturePackList par0TexturePackList, ITexturePack par1ITexturePack)
|
||||
{
|
||||
return par0TexturePackList.selectedTexturePack = par1ITexturePack;
|
||||
}
|
||||
/*
|
||||
|
||||
/**
|
||||
* Generate an internal texture pack ID from the file/directory name, last
|
||||
* modification time, and file size. Returns null if the file/directory is not a
|
||||
* texture pack. (Inner class static accessor method).
|
||||
|
||||
static String generateTexturePackID(TexturePackList par0TexturePackList, File par1File) {
|
||||
* Generate an internal texture pack ID from the file/directory name, last modification time, and file size. Returns
|
||||
* null if the file/directory is not a texture pack. (Inner class static accessor method).
|
||||
*/
|
||||
static String generateTexturePackID(TexturePackList par0TexturePackList, VFile par1File)
|
||||
{
|
||||
return par0TexturePackList.generateTexturePackID(par1File);
|
||||
}
|
||||
*/
|
||||
static ITexturePack func_98143_h() {
|
||||
|
||||
static ITexturePack func_98143_h()
|
||||
{
|
||||
return defaultTexturePack;
|
||||
}
|
||||
|
||||
static Minecraft getMinecraft(TexturePackList par0TexturePackList) {
|
||||
static Minecraft getMinecraft(TexturePackList par0TexturePackList)
|
||||
{
|
||||
return par0TexturePackList.mc;
|
||||
}
|
||||
|
||||
public static final byte[] zipToEpk(byte[] in) {
|
||||
try {
|
||||
EPK2Compiler epk2Compiler = new EPK2Compiler();
|
||||
try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(in))) {
|
||||
ZipEntry zipEntry;
|
||||
byte[] bb = new byte[16000];
|
||||
while ((zipEntry = zis.getNextEntry()) != null) {
|
||||
if (zipEntry.isDirectory()) continue;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int len;
|
||||
while ((len = zis.read(bb)) != -1) {
|
||||
baos.write(bb, 0, len);
|
||||
}
|
||||
baos.close();
|
||||
epk2Compiler.append(zipEntry.getName(), baos.toByteArray());
|
||||
}
|
||||
}
|
||||
return epk2Compiler.complete();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm.vfs;
|
||||
|
||||
public class BooleanResult {
|
||||
|
||||
public static final BooleanResult TRUE = new BooleanResult(true);
|
||||
public static final BooleanResult FALSE = new BooleanResult(false);
|
||||
|
||||
public final boolean bool;
|
||||
|
||||
private BooleanResult(boolean b) {
|
||||
bool = b;
|
||||
}
|
||||
|
||||
public static BooleanResult _new(boolean b) {
|
||||
return b ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm.vfs;
|
||||
|
||||
public class SYS {
|
||||
|
||||
public static final VirtualFilesystem VFS;
|
||||
|
||||
static {
|
||||
|
||||
VirtualFilesystem.VFSHandle vh = VirtualFilesystem.openVFS("_net_lax1dude_eaglercraft_adapter_teavm_vfs_VirtualFilesystem_1_5_2_eagStorage");
|
||||
|
||||
if(vh.vfs == null) {
|
||||
System.err.println("Could not init filesystem!");
|
||||
throw new RuntimeException("Could not init filesystem: VFSHandle.vfs was null");
|
||||
}
|
||||
|
||||
VFS = vh.vfs;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm.vfs;
|
||||
|
||||
public interface VFSIterator {
|
||||
|
||||
public static class BreakLoop extends RuntimeException {
|
||||
public BreakLoop() {
|
||||
super("iterator loop break request");
|
||||
}
|
||||
}
|
||||
|
||||
public default void end() {
|
||||
throw new BreakLoop();
|
||||
}
|
||||
|
||||
public void next(VIteratorFile entry);
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm.vfs;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class VFile {
|
||||
|
||||
public static final String pathSeperator = "/";
|
||||
public static final String[] altPathSeperator = new String[] { "\\" };
|
||||
|
||||
public static String normalizePath(String p) {
|
||||
for(int i = 0; i < altPathSeperator.length; ++i) {
|
||||
p = p.replace(altPathSeperator[i], pathSeperator);
|
||||
}
|
||||
if(p.startsWith(pathSeperator)) {
|
||||
p = p.substring(1);
|
||||
}
|
||||
if(p.endsWith(pathSeperator)) {
|
||||
p = p.substring(0, p.length() - pathSeperator.length());
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static String[] splitPath(String p) {
|
||||
String[] pth = normalizePath(p).split(pathSeperator);
|
||||
for(int i = 0; i < pth.length; ++i) {
|
||||
pth[i] = pth[i].trim();
|
||||
}
|
||||
return pth;
|
||||
}
|
||||
|
||||
protected String path;
|
||||
|
||||
public static String createPath(Object... p) {
|
||||
ArrayList<String> r = new ArrayList<>();
|
||||
for(int i = 0; i < p.length; ++i) {
|
||||
if(p[i] == null) {
|
||||
continue;
|
||||
}
|
||||
String gg = p[i].toString();
|
||||
if(gg == null) {
|
||||
continue;
|
||||
}
|
||||
String[] parts = splitPath(gg);
|
||||
for(int j = 0; j < parts.length; ++j) {
|
||||
if(parts[j] == null || parts[j].equals(".")) {
|
||||
continue;
|
||||
}else if(parts[j].equals("..") && r.size() > 0) {
|
||||
int k = r.size() - 1;
|
||||
if(!r.get(k).equals("..")) {
|
||||
r.remove(k);
|
||||
}else {
|
||||
r.add("..");
|
||||
}
|
||||
}else {
|
||||
r.add(parts[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(r.size() > 0) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for(int i = 0; i < r.size(); ++i) {
|
||||
if(i > 0) {
|
||||
s.append(pathSeperator);
|
||||
}
|
||||
s.append(r.get(i));
|
||||
}
|
||||
return s.toString();
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public VFile(Object... p) {
|
||||
this.path = createPath(p);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return isRelative() ? null : SYS.VFS.getFile(path).getInputStream();
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return isRelative() ? null : SYS.VFS.getFile(path).getOutputStream();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return path == null || path.contains("..");
|
||||
}
|
||||
|
||||
public boolean canRead() {
|
||||
return !isRelative() && SYS.VFS.fileExists(path);
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path.equals("unnamed") ? null : path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? path : path.substring(i + 1);
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return !isRelative();
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? ".." : path.substring(0, i);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path == null ? 0 : path.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return !isRelative() && SYS.VFS.fileExists(path);
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return !isRelative() && SYS.VFS.deleteFile(path);
|
||||
}
|
||||
|
||||
public boolean renameTo(String p, boolean copy) {
|
||||
if(!isRelative() && SYS.VFS.renameFile(path, p, copy)) {
|
||||
path = p;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return isRelative() ? -1 : SYS.VFS.getFile(path).getSize();
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
if(isRelative()) {
|
||||
throw new ArrayIndexOutOfBoundsException("File is relative");
|
||||
}
|
||||
SYS.VFS.getFile(path).getBytes(fileOffset, array, offset, length);
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
if(isRelative()) {
|
||||
throw new RuntimeException("File is relative");
|
||||
}
|
||||
SYS.VFS.getFile(path).setCacheEnabled();
|
||||
}
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllBytes();
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllChars();
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllLines();
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllBytes(copy);
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllChars(bytes);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllBytes(bytes);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllBytes(bytes, copy);
|
||||
}
|
||||
|
||||
public List<String> list() {
|
||||
if(isRelative()) {
|
||||
return Arrays.asList(path);
|
||||
}
|
||||
return SYS.VFS.listFiles(path);
|
||||
}
|
||||
|
||||
public int deleteAll() {
|
||||
return isRelative() ? 0 : SYS.VFS.deleteFiles(path);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm.vfs;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.Event;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.indexeddb.IDBCursor;
|
||||
import org.teavm.jso.indexeddb.IDBRequest;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
/**
|
||||
* Do not use an instance of this class outside of the VFSIterator.next() method
|
||||
*/
|
||||
public class VIteratorFile extends VFile {
|
||||
|
||||
static final VIteratorFile instance = new VIteratorFile();
|
||||
|
||||
private VIteratorFile() {
|
||||
super("");
|
||||
this.idx = -1;
|
||||
this.cur = null;
|
||||
this.vfs = null;
|
||||
}
|
||||
|
||||
private static class VirtualIteratorOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private final VIteratorFile itr;
|
||||
|
||||
protected VirtualIteratorOutputStream(VIteratorFile itr) {
|
||||
this.itr = itr;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if(!itr.setAllBytes(super.toByteArray(), false)) {
|
||||
throw new IOException("Could not close stream and write to \"" + itr.path + "\" on VFS \"" + itr.vfs.database + "\" (the file was probably deleted)");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int idx;
|
||||
private IDBCursor cur;
|
||||
private VirtualFilesystem vfs;
|
||||
private boolean wasDeleted;
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
|
||||
private static native String readKey(JSObject k);
|
||||
|
||||
static VIteratorFile create(int idx, VirtualFilesystem vfs, IDBCursor cur) {
|
||||
String k = readKey(cur.getKey());
|
||||
if(k == null) {
|
||||
return null;
|
||||
}
|
||||
instance.update(idx, k, vfs, cur);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public VFile makeVFile() {
|
||||
return new VFile(path);
|
||||
}
|
||||
|
||||
private void update(int idx, String path, VirtualFilesystem vfs, IDBCursor cur) {
|
||||
this.idx = idx;
|
||||
this.path = path;
|
||||
this.vfs = vfs;
|
||||
this.cur = cur;
|
||||
this.wasDeleted = false;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return !wasDeleted ? new ByteArrayInputStream(getAllBytes()) : null;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return !wasDeleted ? new VirtualIteratorOutputStream(this) : null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canRead() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? path : path.substring(i + 1);
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? ".." : path.substring(0, i);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path == null ? 0 : path.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return wasDeleted = AsyncHandlers.awaitRequest(cur.delete()).bool;
|
||||
}
|
||||
|
||||
public boolean renameTo(String p) {
|
||||
byte[] data = getAllBytes();
|
||||
String op = path;
|
||||
path = p;
|
||||
if(!setAllBytes(data)) {
|
||||
path = op;
|
||||
return false;
|
||||
}
|
||||
path = op;
|
||||
if(!delete()) {
|
||||
return false;
|
||||
}
|
||||
path = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
throw new RuntimeException("Value of entry is missing");
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
throw new RuntimeException("Value of the fucking value of the entry is missing");
|
||||
}
|
||||
|
||||
return arr.getByteLength();
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
throw new ArrayIndexOutOfBoundsException("Value of entry is missing");
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
throw new ArrayIndexOutOfBoundsException("Value of the fucking value of the entry is missing");
|
||||
}
|
||||
|
||||
Uint8Array a = new Uint8Array(arr);
|
||||
|
||||
if(a.getLength() < fileOffset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + path + "' size was "+a.getLength()+" but user tried to read index "+(fileOffset + length - 1));
|
||||
}
|
||||
|
||||
for(int i = 0; i < length; ++i) {
|
||||
array[i + offset] = (byte)a.get(i + fileOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
// no
|
||||
}
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
|
||||
private static native ArrayBuffer readRow(JSObject obj);
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Uint8Array a = new Uint8Array(arr);
|
||||
int ii = a.getByteLength();
|
||||
|
||||
byte[] array = new byte[ii];
|
||||
for(int i = 0; i < ii; ++i) {
|
||||
array[i] = (byte)a.get(i);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
return VirtualFilesystem.utf8(getAllBytes());
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
return VirtualFilesystem.lines(VirtualFilesystem.utf8(getAllBytes()));
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
return getAllBytes();
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
return setAllBytes(VirtualFilesystem.utf8(bytes));
|
||||
}
|
||||
|
||||
public List<String> list() {
|
||||
throw new RuntimeException("Cannot perform list all in VFS callback");
|
||||
}
|
||||
|
||||
public int deleteAll() {
|
||||
throw new RuntimeException("Cannot perform delete all in VFS callback");
|
||||
}
|
||||
|
||||
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
|
||||
private static native JSObject writeRow(String name, ArrayBuffer data);
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
ArrayBuffer a = new ArrayBuffer(bytes.length);
|
||||
Uint8Array ar = new Uint8Array(a);
|
||||
ar.set(bytes);
|
||||
JSObject obj = writeRow(path, a);
|
||||
BooleanResult r = AsyncHandlers.awaitRequest(cur.update(obj));
|
||||
return r.bool;
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
return setAllBytes(bytes);
|
||||
}
|
||||
|
||||
public static class AsyncHandlers {
|
||||
|
||||
@Async
|
||||
public static native BooleanResult awaitRequest(IDBRequest r);
|
||||
|
||||
private static void awaitRequest(IDBRequest r, final AsyncCallback<BooleanResult> cb) {
|
||||
r.addEventListener("success", new EventListener<Event>() {
|
||||
@Override
|
||||
public void handleEvent(Event evt) {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.addEventListener("error", new EventListener<Event>() {
|
||||
@Override
|
||||
public void handleEvent(Event evt) {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,681 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm.vfs;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.EaglerAdapter;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.TeaVMUtils;
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.indexeddb.EventHandler;
|
||||
import org.teavm.jso.indexeddb.IDBCountRequest;
|
||||
import org.teavm.jso.indexeddb.IDBCursor;
|
||||
import org.teavm.jso.indexeddb.IDBCursorRequest;
|
||||
import org.teavm.jso.indexeddb.IDBDatabase;
|
||||
import org.teavm.jso.indexeddb.IDBFactory;
|
||||
import org.teavm.jso.indexeddb.IDBGetRequest;
|
||||
import org.teavm.jso.indexeddb.IDBObjectStoreParameters;
|
||||
import org.teavm.jso.indexeddb.IDBOpenDBRequest;
|
||||
import org.teavm.jso.indexeddb.IDBRequest;
|
||||
import org.teavm.jso.indexeddb.IDBTransaction;
|
||||
import org.teavm.jso.indexeddb.IDBVersionChangeEvent;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.Int8Array;
|
||||
|
||||
public class VirtualFilesystem {
|
||||
|
||||
protected static class VirtualOutputStream extends ByteArrayOutputStream {
|
||||
private final VFSFile file;
|
||||
|
||||
protected VirtualOutputStream(VFSFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if(!file.setAllBytes(super.toByteArray(), false)) {
|
||||
throw new IOException("Could not close stream and write to \"" + file.filePath + "\" on VFS \"" + file.virtualFilesystem.database + "\" (the file was probably deleted)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class VFSFile {
|
||||
|
||||
public final VirtualFilesystem virtualFilesystem;
|
||||
protected boolean cacheEnabled;
|
||||
protected String filePath;
|
||||
protected int fileSize = -1;
|
||||
protected boolean hasBeenDeleted = false;
|
||||
protected boolean hasBeenAccessed = false;
|
||||
protected boolean exists = false;
|
||||
|
||||
protected byte[] cache = null;
|
||||
protected long cacheHit;
|
||||
|
||||
protected VFSFile(VirtualFilesystem vfs, String filePath, boolean cacheEnabled) {
|
||||
this.virtualFilesystem = vfs;
|
||||
this.filePath = filePath;
|
||||
this.cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
if(cacheEnabled) {
|
||||
setCacheEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof VFSFile) && ((VFSFile)o).filePath.equals(filePath);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return filePath.hashCode();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
if(fileSize < 0) {
|
||||
if(cacheEnabled) {
|
||||
byte[] b = getAllBytes(false);
|
||||
if(b != null) {
|
||||
fileSize = b.length;
|
||||
}
|
||||
}else {
|
||||
ArrayBuffer dat = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
if(dat != null) {
|
||||
fileSize = dat.getByteLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
byte[] dat = getAllBytes(false);
|
||||
if(dat == null) {
|
||||
return null;
|
||||
}
|
||||
return new ByteArrayInputStream(dat);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return new VirtualOutputStream(this);
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
if(hasBeenDeleted) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' has been deleted");
|
||||
}else if(hasBeenAccessed && !exists) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
|
||||
}
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
if(cacheEnabled && cache != null) {
|
||||
System.arraycopy(cache, fileOffset, array, offset, length);
|
||||
}else {
|
||||
ArrayBuffer aa = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
hasBeenAccessed = true;
|
||||
if(aa != null) {
|
||||
exists = true;
|
||||
}else {
|
||||
exists = false;
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
|
||||
}
|
||||
this.fileSize = aa.getByteLength();
|
||||
if(cacheEnabled) {
|
||||
cache = TeaVMUtils.wrapByteArrayBuffer(aa);
|
||||
}
|
||||
if(fileSize < fileOffset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' size was "+fileSize+" but user tried to read index "+(fileOffset + length - 1));
|
||||
}
|
||||
TeaVMUtils.unwrapByteArray(array).set(new Int8Array(aa, fileOffset, length), offset);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
if(!cacheEnabled && !hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
cache = getAllBytes(false);
|
||||
cacheEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
return getAllBytes(false);
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
return utf8(getAllBytes(false));
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
return lines(getAllChars());
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
if(hasBeenDeleted || (hasBeenAccessed && !exists)) {
|
||||
return null;
|
||||
}
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
if(cacheEnabled && cache != null) {
|
||||
byte[] b = cache;
|
||||
if(copy) {
|
||||
b = new byte[cache.length];
|
||||
System.arraycopy(cache, 0, b, 0, cache.length);
|
||||
}
|
||||
return b;
|
||||
}else {
|
||||
hasBeenAccessed = true;
|
||||
ArrayBuffer b = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
if(b != null) {
|
||||
exists = true;
|
||||
}else {
|
||||
exists = false;
|
||||
return null;
|
||||
}
|
||||
this.fileSize = b.getByteLength();
|
||||
if(cacheEnabled) {
|
||||
if(copy) {
|
||||
cache = new byte[fileSize];
|
||||
TeaVMUtils.unwrapByteArray(cache).set(new Int8Array(b));
|
||||
}else {
|
||||
cache = TeaVMUtils.wrapByteArrayBuffer(b);
|
||||
}
|
||||
}
|
||||
return TeaVMUtils.wrapByteArrayBuffer(b);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
return setAllBytes(utf8(bytes), true);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
return setAllBytes(bytes, true);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
if(hasBeenDeleted || bytes == null) {
|
||||
return false;
|
||||
}
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
this.fileSize = bytes.length;
|
||||
if(cacheEnabled) {
|
||||
byte[] copz = bytes;
|
||||
if(copy) {
|
||||
copz = new byte[bytes.length];
|
||||
System.arraycopy(bytes, 0, copz, 0, bytes.length);
|
||||
}
|
||||
cache = copz;
|
||||
return sync();
|
||||
}else {
|
||||
boolean s = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath,
|
||||
TeaVMUtils.unwrapArrayBuffer(bytes)).bool;
|
||||
hasBeenAccessed = true;
|
||||
exists = exists || s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sync() {
|
||||
if(cacheEnabled && cache != null && !hasBeenDeleted) {
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
boolean tryWrite = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath,
|
||||
TeaVMUtils.unwrapArrayBuffer(cache)).bool;
|
||||
hasBeenAccessed = true;
|
||||
exists = exists || tryWrite;
|
||||
return tryWrite;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
if(!AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
|
||||
hasBeenAccessed = true;
|
||||
return false;
|
||||
}
|
||||
virtualFilesystem.fileMap.remove(filePath);
|
||||
hasBeenDeleted = true;
|
||||
hasBeenAccessed = true;
|
||||
exists = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean rename(String newName, boolean copy) {
|
||||
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
ArrayBuffer arr = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
hasBeenAccessed = true;
|
||||
if(arr != null) {
|
||||
exists = true;
|
||||
if(!AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, newName, arr).bool) {
|
||||
return false;
|
||||
}
|
||||
if(!copy && !AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
|
||||
return false;
|
||||
}
|
||||
}else {
|
||||
exists = false;
|
||||
}
|
||||
if(!copy) {
|
||||
virtualFilesystem.fileMap.remove(filePath);
|
||||
filePath = newName;
|
||||
virtualFilesystem.fileMap.put(newName, this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
if(hasBeenDeleted) {
|
||||
return false;
|
||||
}
|
||||
cacheHit = EaglerAdapter.steadyTimeMillis();
|
||||
if(hasBeenAccessed) {
|
||||
return exists;
|
||||
}
|
||||
exists = AsyncHandlers.fileExists(virtualFilesystem.indexeddb, filePath).bool;
|
||||
hasBeenAccessed = true;
|
||||
return exists;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final HashMap<String, VFSFile> fileMap = new HashMap<>();
|
||||
|
||||
public final String database;
|
||||
private final IDBDatabase indexeddb;
|
||||
|
||||
public static class VFSHandle {
|
||||
|
||||
public final boolean failedInit;
|
||||
public final boolean failedLocked;
|
||||
public final String failedError;
|
||||
public final VirtualFilesystem vfs;
|
||||
|
||||
public VFSHandle(boolean init, boolean locked, String error, VirtualFilesystem db) {
|
||||
failedInit = init;
|
||||
failedLocked = locked;
|
||||
failedError = error;
|
||||
vfs = db;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if(failedInit) {
|
||||
return "IDBFactory threw an exception, IndexedDB is most likely not supported in this browser." + (failedError == null ? "" : "\n\n" + failedError);
|
||||
}
|
||||
if(failedLocked) {
|
||||
return "The filesystem requested is already in use on a different tab.";
|
||||
}
|
||||
if(failedError != null) {
|
||||
return "The IDBFactory.open() request failed, reason: " + failedError;
|
||||
}
|
||||
return "Virtual Filesystem Object: " + vfs.database;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static VFSHandle openVFS(String db) {
|
||||
DatabaseOpen evt = AsyncHandlers.openDB(db);
|
||||
if(evt.failedInit) {
|
||||
return new VFSHandle(true, false, evt.failedError, null);
|
||||
}
|
||||
if(evt.failedLocked) {
|
||||
return new VFSHandle(false, true, null, null);
|
||||
}
|
||||
if(evt.failedError != null) {
|
||||
return new VFSHandle(false, false, evt.failedError, null);
|
||||
}
|
||||
return new VFSHandle(false, false, null, new VirtualFilesystem(db, evt.database));
|
||||
}
|
||||
|
||||
private VirtualFilesystem(String db, IDBDatabase idb) {
|
||||
database = db;
|
||||
indexeddb = idb;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
indexeddb.close();
|
||||
}
|
||||
|
||||
public VFSFile getFile(String path) {
|
||||
return getFile(path, false);
|
||||
}
|
||||
|
||||
public VFSFile getFile(String path, boolean cache) {
|
||||
VFSFile f = fileMap.get(path);
|
||||
if(f == null) {
|
||||
fileMap.put(path, f = new VFSFile(this, path, cache));
|
||||
}else {
|
||||
if(cache) {
|
||||
f.setCacheEnabled();
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public boolean renameFile(String oldName, String newName, boolean copy) {
|
||||
return getFile(oldName).rename(newName, copy);
|
||||
}
|
||||
|
||||
public boolean deleteFile(String path) {
|
||||
return getFile(path).delete();
|
||||
}
|
||||
|
||||
public boolean fileExists(String path) {
|
||||
return getFile(path).exists();
|
||||
}
|
||||
|
||||
public List<String> listFiles(String prefix) {
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> {
|
||||
list.add(v.getPath());
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
public int deleteFiles(String prefix) {
|
||||
return AsyncHandlers.deleteFiles(indexeddb, prefix);
|
||||
}
|
||||
|
||||
public int iterateFiles(String prefix, boolean rw, VFSIterator itr) {
|
||||
return AsyncHandlers.iterateFiles(indexeddb, this, prefix, rw, itr);
|
||||
}
|
||||
|
||||
public int renameFiles(String oldPrefix, String newPrefix, boolean copy) {
|
||||
List<String> filesToCopy = listFiles(oldPrefix);
|
||||
int i = 0;
|
||||
for(String str : filesToCopy) {
|
||||
String f = VFile.createPath(newPrefix, str.substring(oldPrefix.length()));
|
||||
if(!renameFile(str, f, copy)) {
|
||||
System.err.println("Could not " + (copy ? "copy" : "rename") + " file \"" + str + "\" to \"" + f + "\" for some reason");
|
||||
}else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public void flushCache(long age) {
|
||||
long curr = EaglerAdapter.steadyTimeMillis();
|
||||
Iterator<VFSFile> files = fileMap.values().iterator();
|
||||
while(files.hasNext()) {
|
||||
if(curr - files.next().cacheHit > age) {
|
||||
files.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DatabaseOpen {
|
||||
|
||||
protected final boolean failedInit;
|
||||
protected final boolean failedLocked;
|
||||
protected final String failedError;
|
||||
|
||||
protected final IDBDatabase database;
|
||||
|
||||
protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) {
|
||||
failedInit = init;
|
||||
failedLocked = locked;
|
||||
failedError = error;
|
||||
database = db;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;")
|
||||
protected static native IDBFactory createIDBFactory();
|
||||
|
||||
protected static class AsyncHandlers {
|
||||
|
||||
@Async
|
||||
protected static native DatabaseOpen openDB(String name);
|
||||
|
||||
private static void openDB(String name, final AsyncCallback<DatabaseOpen> cb) {
|
||||
IDBFactory i = createIDBFactory();
|
||||
if(i == null) {
|
||||
cb.complete(new DatabaseOpen(false, false, "window.indexedDB was null or undefined", null));
|
||||
return;
|
||||
}
|
||||
final IDBOpenDBRequest f = i.open(name, 1);
|
||||
f.setOnBlocked(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, true, null, null));
|
||||
}
|
||||
});
|
||||
f.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, false, null, f.getResult()));
|
||||
}
|
||||
});
|
||||
f.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, false, "open error", null));
|
||||
}
|
||||
});
|
||||
f.setOnUpgradeNeeded(new EventListener<IDBVersionChangeEvent>() {
|
||||
@Override
|
||||
public void handleEvent(IDBVersionChangeEvent evt) {
|
||||
f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult deleteFile(IDBDatabase db, String name);
|
||||
|
||||
private static void deleteFile(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name));
|
||||
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
|
||||
protected static native ArrayBuffer readRow(JSObject obj);
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return [obj];")
|
||||
private static native JSObject makeTheFuckingKeyWork(String k);
|
||||
|
||||
@Async
|
||||
protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name);
|
||||
|
||||
private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback<ArrayBuffer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readonly");
|
||||
final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name));
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(readRow(r.getResult()));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(null);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
|
||||
private static native String readKey(JSObject k);
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"undefined\") ? null : (((typeof k.path) === \"undefined\") ? null : (((typeof k.path) === \"string\") ? k[0] : null));")
|
||||
private static native String readRowKey(JSObject r);
|
||||
|
||||
@Async
|
||||
protected static native Integer iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr);
|
||||
|
||||
private static void iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr, final AsyncCallback<Integer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", rw ? "readwrite" : "readonly");
|
||||
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
|
||||
final int[] res = new int[1];
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
IDBCursor c = r.getResult();
|
||||
if(c == null || c.getKey() == null || c.getValue() == null) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
String k = readKey(c.getKey());
|
||||
if(k != null) {
|
||||
if(k.startsWith(prefix)) {
|
||||
int ci = res[0]++;
|
||||
try {
|
||||
itr.next(VIteratorFile.create(ci, vfs, c));
|
||||
}catch(VFSIterator.BreakLoop ex) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
c.doContinue();
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(res[0] > 0 ? res[0] : -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native Integer deleteFiles(IDBDatabase db, final String prefix);
|
||||
|
||||
private static void deleteFiles(IDBDatabase db, final String prefix, final AsyncCallback<Integer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
|
||||
final int[] res = new int[1];
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
IDBCursor c = r.getResult();
|
||||
if(c == null || c.getKey() == null || c.getValue() == null) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
String k = readKey(c.getKey());
|
||||
if(k != null) {
|
||||
if(k.startsWith(prefix)) {
|
||||
c.delete();
|
||||
++res[0];
|
||||
}
|
||||
}
|
||||
c.doContinue();
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(res[0] > 0 ? res[0] : -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult fileExists(IDBDatabase db, String name);
|
||||
|
||||
private static void fileExists(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readonly");
|
||||
final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name));
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(r.getResult() > 0));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
|
||||
protected static native JSObject writeRow(String name, ArrayBuffer data);
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data);
|
||||
|
||||
private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data));
|
||||
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static byte[] utf8(String str) {
|
||||
if(str == null) return null;
|
||||
return str.getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String utf8(byte[] str) {
|
||||
if(str == null) return null;
|
||||
return new String(str, Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String CRLFtoLF(String str) {
|
||||
if(str == null) return null;
|
||||
str = str.indexOf('\r') != -1 ? str.replace("\r", "") : str;
|
||||
str = str.trim();
|
||||
if(str.endsWith("\n")) {
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if(str.startsWith("\n")) {
|
||||
str = str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String[] lines(String str) {
|
||||
if(str == null) return null;
|
||||
return CRLFtoLF(str).split("\n");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user