work on texture pack redo

This commit is contained in:
ayunami2000 2024-12-09 04:26:55 -05:00
parent 3f648e6692
commit 1e4f407976
20 changed files with 1862 additions and 335 deletions

View File

@ -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));
}
}
}

View 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);
}
}

View 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"));
}
}

View File

@ -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 {

View File

@ -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)));

View File

@ -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");
}
/**

View File

@ -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;

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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));
}
});
}
}
}

View File

@ -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");
}
}