diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java index 51c9468..57c2a16 100644 --- a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java +++ b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java @@ -1,28 +1,19 @@ package net.lax1dude.eaglercraft.sp; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; import org.teavm.jso.JSBody; import org.teavm.jso.JSFunctor; import org.teavm.jso.JSObject; import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.Uint8Array; import net.lax1dude.eaglercraft.sp.ipc.*; import net.minecraft.src.AchievementList; import net.minecraft.src.AchievementMap; -import net.minecraft.src.ChunkCoordIntPair; import net.minecraft.src.CompressedStreamTools; import net.minecraft.src.EnumGameType; import net.minecraft.src.ILogAgent; @@ -261,125 +252,25 @@ public class IntegratedServer { break; case IPCPacket05RequestData.ID: { IPCPacket05RequestData pkt = (IPCPacket05RequestData)packet; - if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) {String realWorldName = pkt.worldName; - String worldOwner = "UNKNOWN"; - String splitter = new String(new char[] { (char)253, (char)233, (char)233 }); - if(realWorldName.contains(splitter)) { - int i = realWorldName.lastIndexOf(splitter); - worldOwner = realWorldName.substring(i + 3); - realWorldName = realWorldName.substring(0, i); - } + if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) { try { - - final int[] bytesWritten = new int[1]; - final int[] lastUpdate = new int[1]; - String pfx = "worlds/" + realWorldName + "/"; - EPK2Compiler c = new EPK2Compiler(realWorldName, worldOwner, "epk/world152"); - SYS.VFS.iterateFiles(pfx, false, (i) -> { - byte[] b = i.getAllBytes(); - c.append(i.path.substring(pfx.length()), b); - bytesWritten[0] += b.length; - if (bytesWritten[0] - lastUpdate[0] > 10000) { - lastUpdate[0] = bytesWritten[0]; - updateStatusString("selectWorld.progress.exporting." + pkt.request, bytesWritten[0]); - } - }); - sendIPCPacket(new IPCPacket09RequestResponse(c.complete())); + sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterEPK.exportWorld(pkt.worldName))); } catch (Throwable t) { - throwExceptionToClient("Failed to export world '" + realWorldName + "' as EPK", t); + String realWorldName = pkt.worldName; + int i = realWorldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 })); + if(i != -1) { + realWorldName = realWorldName.substring(0, i); + } + throwExceptionToClient("Failed to export world '" + realWorldName+ "' as EPK", t); sendTaskFailed(); } }else if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_MCA) { try { - final int[] bytesWritten = new int[1]; - final int[] lastUpdate = new int[1]; - String shortpfx = pkt.worldName + "/"; - String pfx = "worlds/" + shortpfx; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ZipOutputStream c = new ZipOutputStream(baos); - c.setComment("contains backup of world '" + pkt.worldName + "'"); - Map regions = new HashMap<>(); - Map regions1 = new HashMap<>(); - Map regionsn1 = new HashMap<>(); - SYS.VFS.iterateFiles(pfx, false, (i) -> { - String currPath = i.path.substring(pfx.length()); - try { - byte[] b = i.getAllBytes(); - if(currPath.equals("level.dat")) { - NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(b); - worldDatNBT.getCompoundTag("Data").setInteger("version", 19133); - b = CompressedStreamTools.compress(worldDatNBT); - } - if (currPath.startsWith("level0/")) { - regions.put(VFSChunkLoader.getChunkCoords(currPath.substring(7, currPath.length() - 4)), b); - } else if (currPath.startsWith("level1/")) { - regions1.put(VFSChunkLoader.getChunkCoords(currPath.substring(7, currPath.length() - 4)), b); - } else if (currPath.startsWith("level-1/")) { - regionsn1.put(VFSChunkLoader.getChunkCoords(currPath.substring(8, currPath.length() - 4)), b); - } else { - ZipEntry zipEntry = new ZipEntry(shortpfx + currPath); - c.putNextEntry(zipEntry); - c.write(b); - c.closeEntry(); - bytesWritten[0] += b.length; - if (bytesWritten[0] - lastUpdate[0] > 10000) { - lastUpdate[0] = bytesWritten[0]; - updateStatusString("selectWorld.progress.exporting." + pkt.request, bytesWritten[0]); - } - } - } catch (Throwable t) { - throwExceptionToClient("Failed to export file '" + currPath + "'", t); - sendTaskFailed(); - } - }); - Map regionsOut = MCAConverter.convertToMCA(regions); - for (String path : regionsOut.keySet()) { - byte[] b = regionsOut.get(path); - ZipEntry zipEntry = new ZipEntry(shortpfx + "region/" + path + ".mca"); - c.putNextEntry(zipEntry); - c.write(b); - c.closeEntry(); - bytesWritten[0] += b.length; - if (bytesWritten[0] - lastUpdate[0] > 10000) { - lastUpdate[0] = bytesWritten[0]; - updateStatusString("selectWorld.progress.exporting." + pkt.request, bytesWritten[0]); - } - } - Map regions1Out = MCAConverter.convertToMCA(regions1); - for (String path : regions1Out.keySet()) { - byte[] b = regions1Out.get(path); - ZipEntry zipEntry = new ZipEntry(shortpfx + "DIM1/region/" + path + ".mca"); - c.putNextEntry(zipEntry); - c.write(b); - c.closeEntry(); - bytesWritten[0] += b.length; - if (bytesWritten[0] - lastUpdate[0] > 10000) { - lastUpdate[0] = bytesWritten[0]; - updateStatusString("selectWorld.progress.exporting." + pkt.request, bytesWritten[0]); - } - } - Map regionsn1Out = MCAConverter.convertToMCA(regionsn1); - for (String path : regionsn1Out.keySet()) { - byte[] b = regionsn1Out.get(path); - ZipEntry zipEntry = new ZipEntry(shortpfx + "DIM-1/region/" + path + ".mca"); - c.putNextEntry(zipEntry); - c.write(b); - c.closeEntry(); - bytesWritten[0] += b.length; - if (bytesWritten[0] - lastUpdate[0] > 10000) { - lastUpdate[0] = bytesWritten[0]; - updateStatusString("selectWorld.progress.exporting." + pkt.request, bytesWritten[0]); - } - } - c.close(); - sendIPCPacket(new IPCPacket09RequestResponse(baos.toByteArray())); + sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterMCA.exportWorld(pkt.worldName))); } catch (Throwable t) { - throwExceptionToClient("Failed to export world '" + pkt.worldName + "' as MCA", t); + throwExceptionToClient("Failed to export world '" + pkt.worldName+ "' as MCA", t); sendTaskFailed(); } - }else { - System.err.println("Unknown IPCPacket05RequestData type '" + pkt.request + "'"); - sendTaskFailed(); } } break; @@ -404,122 +295,20 @@ public class IntegratedServer { IPCPacket07ImportWorld pkt = (IPCPacket07ImportWorld)packet; if(isServerStopped()) { if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_EAG) { - String folder = VFSSaveHandler.worldNameToFolderName(pkt.worldName); try { - VFile dir = new VFile("worlds", folder); - EPKDecompiler dc = new EPKDecompiler(pkt.worldData); - EPKDecompiler.FileEntry f = null; - int lastProgUpdate = 0; - int prog = 0; - boolean hasReadType = dc.isOld(); - while((f = dc.readFile()) != null) { - byte[] b = f.data; - if(!hasReadType) { - if(f.type.equals("HEAD") && f.name.equals("file-type") && EPKDecompiler.readASCII(f.data).equals("epk/world152")) { - hasReadType = true; - continue; - }else { - throw new IOException("file does not contain a singleplayer 1.5.2 world!"); - } - } - if(f.type.equals("FILE")) { - if(f.name.equals("level.dat")) { - NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(b); - worldDatNBT.getCompoundTag("Data").setString("LevelName", pkt.worldName); - worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); - b = CompressedStreamTools.compress(worldDatNBT); - } - VFile ff = new VFile(dir, f.name); - ff.setAllBytes(b); - prog += b.length; - if(prog - lastProgUpdate > 10000) { - lastProgUpdate = prog; - updateStatusString("selectWorld.progress.importing." + pkt.worldFormat, prog); - } - } - } - String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines(); - if(worldsTxt == null || worldsTxt.length <= 0) { - worldsTxt = new String[] { folder }; - }else { - String[] tmp = worldsTxt; - worldsTxt = new String[worldsTxt.length + 1]; - System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length); - worldsTxt[worldsTxt.length - 1] = folder; - } - SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt)); + WorldConverterEPK.importWorld(pkt.worldData, pkt.worldName); sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID)); }catch(Throwable t) { - SYS.VFS.deleteFiles("worlds/" + folder + "/"); + SYS.VFS.deleteFiles("worlds/" + VFSSaveHandler.worldNameToFolderName(pkt.worldName) + "/"); throwExceptionToClient("Failed to import world '" + pkt.worldName + "' as EPK", t); sendTaskFailed(); } }else if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_MCA) { - String folder = VFSSaveHandler.worldNameToFolderName(pkt.worldName); try { - VFile dir = new VFile("worlds", folder); - ZipInputStream folderNames = new ZipInputStream(new ByteArrayInputStream(pkt.worldData)); - ZipEntry folderNameFile = null; - List fileNames = new ArrayList<>(); - while((folderNameFile = folderNames.getNextEntry()) != null) { - if (folderNameFile.getName().contains("__MACOSX/")) continue; - if (folderNameFile.isDirectory()) continue; - String lowerName = folderNameFile.getName().toLowerCase(); - if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue; - fileNames.add(folderNameFile.getName().toCharArray()); - } - final int[] i = new int[] { 0 }; - while(fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) i[0]++; - int folderPrefixOffset = i[0]; - ZipInputStream dc = new ZipInputStream(new ByteArrayInputStream(pkt.worldData)); - ZipEntry f = null; - int lastProgUpdate = 0; - int prog = 0; - byte[] bb = new byte[16000]; - while ((f = dc.getNextEntry()) != null) { - if (f.getName().contains("__MACOSX/")) continue; - if (f.isDirectory()) continue; - String lowerName = f.getName().toLowerCase(); - if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int len; - while ((len = dc.read(bb)) != -1) { - baos.write(bb, 0, len); - } - baos.close(); - byte[] b = baos.toByteArray(); - String fileName = f.getName().substring(folderPrefixOffset); - if (fileName.equals("level.dat")) { - NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(b); - worldDatNBT.getCompoundTag("Data").setString("LevelName", pkt.worldName); - worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); - b = CompressedStreamTools.compress(worldDatNBT); - } - if (fileName.endsWith(".mcr") || fileName.endsWith(".mca")) { - MCAConverter.convertFromMCA(dir, b, fileName); - } else { - VFile ff = new VFile(dir, fileName); - ff.setAllBytes(b); - } - prog += b.length; - if (prog - lastProgUpdate > 10000) { - lastProgUpdate = prog; - updateStatusString("selectWorld.progress.importing." + pkt.worldFormat, prog); - } - } - String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines(); - if(worldsTxt == null || worldsTxt.length <= 0) { - worldsTxt = new String[] { folder }; - }else { - String[] tmp = worldsTxt; - worldsTxt = new String[worldsTxt.length + 1]; - System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length); - worldsTxt[worldsTxt.length - 1] = folder; - } - SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt)); + WorldConverterMCA.importWorld(pkt.worldData, pkt.worldName); sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID)); }catch(Throwable t) { - SYS.VFS.deleteFiles("worlds/" + folder + "/"); + SYS.VFS.deleteFiles("worlds/" + VFSSaveHandler.worldNameToFolderName(pkt.worldName) + "/"); throwExceptionToClient("Failed to import world '" + pkt.worldName + "' as MCA", t); sendTaskFailed(); } diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/MCAConverter.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/MCAConverter.java deleted file mode 100644 index 98680af..0000000 --- a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/MCAConverter.java +++ /dev/null @@ -1,183 +0,0 @@ -package net.lax1dude.eaglercraft.sp; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.zip.InflaterInputStream; - -import com.jcraft.jzlib.DeflaterOutputStream; -import com.jcraft.jzlib.GZIPInputStream; -import com.jcraft.jzlib.GZIPOutputStream; - -import net.minecraft.src.ChunkCoordIntPair; - -public class MCAConverter { - public static void convertFromMCA(VFile dir, byte[] file, String fileName) { - VFile levelDir = new VFile(dir, - "level" + (fileName.startsWith("region/") ? "0" : fileName.substring(3, fileName.indexOf('/')))); - - String[] xz = fileName.substring(fileName.lastIndexOf('r') + 2, fileName.length() - 4).split("\\."); - int gx = Integer.parseInt(xz[0]); - int gz = Integer.parseInt(xz[1]); - - try { - byte[] buffer = new byte[16000]; - for (int x = 0; x < 32; ++x) { - for (int z = 0; z < 32; ++z) { - int i = ((x % 32) + (z % 32) * 32) * 4; - int offset = (((file[i] & 0xff) << 16) | ((file[i + 1] & 0xff) << 8) | (file[i + 2] & 0xff)) * 4096; - if (offset == 0 && file[i + 3] == 0) { - continue; - } - int chunkLen = (((file[offset] & 0xff) << 24) | ((file[offset + 1] & 0xff) << 16) - | ((file[offset + 2] & 0xff) << 8) | (file[offset + 3] & 0xff)); - if (chunkLen == 0) - continue; - byte compression = file[offset + 4]; - byte[] data = new byte[chunkLen - 1]; - System.arraycopy(file, offset + 5, data, 0, chunkLen - 1); - if (compression == 0) { - OutputStream os = new VFile(levelDir, - VFSChunkLoader.getChunkPath(gx * 32 + x, gz * 32 + z) + ".dat").getOutputStream(); - GZIPOutputStream gos = new GZIPOutputStream(os); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - int len; - while ((len = bais.read(buffer)) > 0) { - gos.write(buffer, 0, len); - } - gos.close(); - os.close(); - bais.close(); - } else if (compression == 2) { - OutputStream os = new VFile(levelDir, - VFSChunkLoader.getChunkPath(gx * 32 + x, gz * 32 + z) + ".dat").getOutputStream(); - GZIPOutputStream gos = new GZIPOutputStream(os); - InflaterInputStream iis = new InflaterInputStream(new ByteArrayInputStream(data)); - int len; - while ((len = iis.read(buffer)) > 0) { - gos.write(buffer, 0, len); - } - gos.close(); - os.close(); - iis.close(); - } else if (compression == 1) { - new VFile(levelDir, VFSChunkLoader.getChunkPath(gx * 32 + x, gz * 32 + z) + ".dat") - .setAllBytes(data); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static Map convertToMCA(Map regions) { - Map regionsOut = new HashMap<>(); - - if (regions.size() == 0) - return regionsOut; - - byte[] readBuffer = new byte[16000]; - - try { - int timestamp = (int) (System.currentTimeMillis() / 1000); - - int maxX = Integer.MIN_VALUE; - int maxZ = Integer.MIN_VALUE; - int minX = Integer.MAX_VALUE; - int minZ = Integer.MAX_VALUE; - - for (ChunkCoordIntPair coords : regions.keySet()) { - if (maxX < coords.chunkXPos) - maxX = coords.chunkXPos; - if (maxZ < coords.chunkZPos) - maxZ = coords.chunkZPos; - if (minX > coords.chunkXPos) - minX = coords.chunkXPos; - if (minZ > coords.chunkZPos) - minZ = coords.chunkZPos; - } - - for (int z = minZ - (32 + (minZ % 32)); z <= maxZ + (32 + (maxZ % 32)); z += 32) { - for (int x = minX - (32 + (minX % 32)); x <= maxX + (32 + (maxX % 32)); x += 32) { - ByteArrayOutputStream offsets = new ByteArrayOutputStream(); - DataOutputStream offsetsDos = new DataOutputStream(offsets); - ByteArrayOutputStream timestamps = new ByteArrayOutputStream(); - DataOutputStream timestampsDos = new DataOutputStream(timestamps); - ByteArrayOutputStream chunks = new ByteArrayOutputStream(); - DataOutputStream chunksDos = new DataOutputStream(chunks); - boolean anyChunks = false; - for (int cz = 0; cz < 32; cz++) { - for (int cx = 0; cx < 32; cx++) { - int tx = x + cx; - int tz = z + cz; - - byte[] region = regions.get(new ChunkCoordIntPair(tx, tz)); - if (region == null) { - offsetsDos.writeInt(0); - timestampsDos.writeInt(0); - } else { - anyChunks = true; - - ByteArrayInputStream bais = new ByteArrayInputStream(region); - GZIPInputStream gis = new GZIPInputStream(bais); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DeflaterOutputStream dos = new DeflaterOutputStream(baos); - int len; - while ((len = gis.read(readBuffer)) > 0) { - dos.write(readBuffer, 0, len); - } - dos.close(); - baos.close(); - bais.close(); - gis.close(); - byte[] zlibbed = baos.toByteArray(); - - int offset = 2 + (chunksDos.size() / 4096); - offsetsDos.write((offset >> 16) & 0xff); - offsetsDos.write((offset >> 8) & 0xff); - offsetsDos.write(offset & 0xff); - offsetsDos.write((int) Math.ceil((5 + zlibbed.length) / 4096.0)); - - timestampsDos.writeInt(timestamp); - - chunksDos.writeInt(region.length); - chunksDos.write(2); - chunksDos.write(zlibbed); - - int chunksSizeOff = chunksDos.size() % 4096; - if (chunksSizeOff != 0) - chunksDos.write(new byte[4096 - chunksSizeOff]); - } - } - } - - offsetsDos.close(); - timestampsDos.close(); - chunksDos.close(); - - if (!anyChunks) - continue; - - byte[] offsetsOut = offsets.toByteArray(); - byte[] timestampsOut = timestamps.toByteArray(); - byte[] chunksOut = chunks.toByteArray(); - - byte[] regionFile = new byte[offsetsOut.length + timestampsOut.length + chunksOut.length]; - System.arraycopy(offsetsOut, 0, regionFile, 0, offsetsOut.length); - System.arraycopy(timestampsOut, 0, regionFile, offsetsOut.length, timestampsOut.length); - System.arraycopy(chunksOut, 0, regionFile, offsetsOut.length + timestampsOut.length, - chunksOut.length); - regionsOut.put("r." + (x / 32) + "." + (z / 32), regionFile); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - return regionsOut; - } -} diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/RandomAccessMemoryFile.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/RandomAccessMemoryFile.java new file mode 100644 index 0000000..17f38f3 --- /dev/null +++ b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/RandomAccessMemoryFile.java @@ -0,0 +1,310 @@ +package net.lax1dude.eaglercraft.sp; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.EOFException; +import java.io.IOException; + +/** + * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class RandomAccessMemoryFile implements DataInput, DataOutput { + + private byte[] buffer; + private int length; + private int pos; + + public RandomAccessMemoryFile(byte[] initialBuffer, int initialLength) { + this.buffer = initialBuffer; + this.length = initialLength; + this.pos = 0; + } + + private void grow(int newMaxSize) { + if (length < newMaxSize) { + if (buffer.length < newMaxSize) { + byte[] newBuffer = new byte[newMaxSize | 0x7FFFF]; + System.arraycopy(buffer, 0, newBuffer, 0, length); + buffer = newBuffer; + } + length = newMaxSize; + } + } + + public byte[] getByteArray() { + byte[] b = new byte[length]; + System.arraycopy(buffer, 0, b, 0, length); + return b; + } + + public int read() throws IOException { + return (pos < length) ? (buffer[pos++] & 0xff) : -1; + } + + private int readBytes(byte b[], int off, int len) throws IOException { + if (pos >= length) { + return -1; + } + + int avail = length - pos; + if (len > avail) { + len = avail; + } + if (len <= 0) { + return 0; + } + System.arraycopy(buffer, pos, b, off, len); + pos += len; + return len; + } + + public int read(byte b[], int off, int len) throws IOException { + return readBytes(b, off, len); + } + + public int read(byte b[]) throws IOException { + return readBytes(b, 0, b.length); + } + + public final void readFully(byte b[]) throws IOException { + readFully(b, 0, b.length); + } + + public final void readFully(byte b[], int off, int len) throws IOException { + int n = 0; + do { + int count = this.read(b, off + n, len - n); + if (count < 0) + throw new EOFException(); + n += count; + } while (n < len); + } + + public int skipBytes(int n) throws IOException { + int newpos; + + if (n <= 0) { + return 0; + } + newpos = pos + n; + if (newpos > length) { + newpos = length; + } + seek(newpos); + + return (int) (newpos - pos); + } + + public void write(int b) throws IOException { + grow(pos + 1); + buffer[pos] = (byte) b; + pos += 1; + } + + private void writeBytes(byte b[], int off, int len) throws IOException { + grow(pos + len); + System.arraycopy(b, off, buffer, pos, len); + pos += len; + } + + public void write(byte b[]) throws IOException { + writeBytes(b, 0, b.length); + } + + public void write(byte b[], int off, int len) throws IOException { + writeBytes(b, off, len); + } + + public void seek(int pos) { + this.pos = pos; + } + + public int getLength() { + return length; + } + + public void setLength(int newLength) { + grow(newLength); + } + + public final boolean readBoolean() throws IOException { + int ch = this.read(); + if (ch < 0) + throw new EOFException(); + return (ch != 0); + } + + public final byte readByte() throws IOException { + int ch = this.read(); + if (ch < 0) + throw new EOFException(); + return (byte) (ch); + } + + public final int readUnsignedByte() throws IOException { + int ch = this.read(); + if (ch < 0) + throw new EOFException(); + return ch; + } + + public final short readShort() throws IOException { + int ch1 = this.read(); + int ch2 = this.read(); + if ((ch1 | ch2) < 0) + throw new EOFException(); + return (short) ((ch1 << 8) + (ch2 << 0)); + } + + public final int readUnsignedShort() throws IOException { + int ch1 = this.read(); + int ch2 = this.read(); + if ((ch1 | ch2) < 0) + throw new EOFException(); + return (ch1 << 8) + (ch2 << 0); + } + + public final char readChar() throws IOException { + int ch1 = this.read(); + int ch2 = this.read(); + if ((ch1 | ch2) < 0) + throw new EOFException(); + return (char) ((ch1 << 8) + (ch2 << 0)); + } + + public final int readInt() throws IOException { + int ch1 = this.read(); + int ch2 = this.read(); + int ch3 = this.read(); + int ch4 = this.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) + throw new EOFException(); + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + } + + public final long readLong() throws IOException { + return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL); + } + + public final float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } + + public final double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } + + public final String readLine() throws IOException { + StringBuilder input = new StringBuilder(); + int c = -1; + boolean eol = false; + + while (!eol) { + switch (c = read()) { + case -1: + case '\n': + eol = true; + break; + case '\r': + eol = true; + int cur = pos; + if ((read()) != '\n') { + seek(cur); + } + break; + default: + input.append((char) c); + break; + } + } + + if ((c == -1) && (input.length() == 0)) { + return null; + } + return input.toString(); + } + + public final String readUTF() throws IOException { + throw new IOException("TODO"); + } + + public final void writeBoolean(boolean v) throws IOException { + write(v ? 1 : 0); + } + + public final void writeByte(int v) throws IOException { + write(v); + } + + public final void writeShort(int v) throws IOException { + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + public final void writeChar(int v) throws IOException { + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + public final void writeInt(int v) throws IOException { + write((v >>> 24) & 0xFF); + write((v >>> 16) & 0xFF); + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + public final void writeLong(long v) throws IOException { + write((int) (v >>> 56) & 0xFF); + write((int) (v >>> 48) & 0xFF); + write((int) (v >>> 40) & 0xFF); + write((int) (v >>> 32) & 0xFF); + write((int) (v >>> 24) & 0xFF); + write((int) (v >>> 16) & 0xFF); + write((int) (v >>> 8) & 0xFF); + write((int) (v >>> 0) & 0xFF); + } + + public final void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + public final void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + + public final void writeBytes(String s) throws IOException { + int len = s.length(); + byte[] b = new byte[len]; + s.getBytes(0, len, b, 0); + writeBytes(b, 0, len); + } + + public final void writeChars(String s) throws IOException { + int clen = s.length(); + int blen = 2 * clen; + byte[] b = new byte[blen]; + char[] c = new char[clen]; + s.getChars(0, clen, c, 0); + for (int i = 0, j = 0; i < clen; i++) { + b[j++] = (byte) (c[i] >>> 8); + b[j++] = (byte) (c[i] >>> 0); + } + writeBytes(b, 0, blen); + } + + public final void writeUTF(String str) throws IOException { + throw new IOException("TODO"); + } +} diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/VirtualFilesystem.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/VirtualFilesystem.java index 1ad4c9b..c441824 100644 --- a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/VirtualFilesystem.java +++ b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/VirtualFilesystem.java @@ -388,6 +388,14 @@ public class VirtualFilesystem { return list; } + public List listVFiles(String prefix) { + final ArrayList list = new ArrayList<>(); + AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> { + list.add(new VFile(v.getPath())); + }); + return list; + } + public int deleteFiles(String prefix) { return AsyncHandlers.deleteFiles(indexeddb, prefix); } diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/WorldConverterEPK.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/WorldConverterEPK.java new file mode 100644 index 0000000..549866b --- /dev/null +++ b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/WorldConverterEPK.java @@ -0,0 +1,80 @@ +package net.lax1dude.eaglercraft.sp; + +import java.io.IOException; + +import net.minecraft.src.CompressedStreamTools; +import net.minecraft.src.NBTTagCompound; + +public class WorldConverterEPK { + + public static void importWorld(byte[] archiveContents, String newName) throws IOException { + String folder = VFSSaveHandler.worldNameToFolderName(newName); + VFile dir = new VFile("worlds", folder); + EPKDecompiler dc = new EPKDecompiler(archiveContents); + EPKDecompiler.FileEntry f = null; + int lastProgUpdate = 0; + int prog = 0; + boolean hasReadType = dc.isOld(); + while((f = dc.readFile()) != null) { + byte[] b = f.data; + if(!hasReadType) { + if(f.type.equals("HEAD") && f.name.equals("file-type") && EPKDecompiler.readASCII(f.data).equals("epk/world152")) { + hasReadType = true; + continue; + }else { + throw new IOException("file does not contain a singleplayer 1.5.2 world!"); + } + } + if(f.type.equals("FILE")) { + if(f.name.equals("level.dat")) { + NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(b); + worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); + worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); + b = CompressedStreamTools.compress(worldDatNBT); + } + VFile ff = new VFile(dir, f.name); + ff.setAllBytes(b); + prog += b.length; + if(prog - lastProgUpdate > 10000) { + lastProgUpdate = prog; + IntegratedServer.updateStatusString("selectWorld.progress.importing.1", prog); + } + } + } + String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines(); + if(worldsTxt == null || worldsTxt.length <= 0) { + worldsTxt = new String[] { folder }; + }else { + String[] tmp = worldsTxt; + worldsTxt = new String[worldsTxt.length + 1]; + System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length); + worldsTxt[worldsTxt.length - 1] = folder; + } + SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt)); + } + + public static byte[] exportWorld(String worldName) { + String realWorldName = worldName; + String worldOwner = "UNKNOWN"; + int j = realWorldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 })); + if(j != -1) { + worldOwner = realWorldName.substring(j + 3); + realWorldName = realWorldName.substring(0, j); + } + final int[] bytesWritten = new int[1]; + final int[] lastUpdate = new int[1]; + String pfx = "worlds/" + realWorldName + "/"; + EPK2Compiler c = new EPK2Compiler(realWorldName, worldOwner, "epk/world152"); + SYS.VFS.iterateFiles(pfx, false, (i) -> { + byte[] b = i.getAllBytes(); + c.append(i.path.substring(pfx.length()), b); + bytesWritten[0] += b.length; + if (bytesWritten[0] - lastUpdate[0] > 10000) { + lastUpdate[0] = bytesWritten[0]; + IntegratedServer.updateStatusString("selectWorld.progress.exporting.1", bytesWritten[0]); + } + }); + return c.complete(); + } + +} diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/WorldConverterMCA.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/WorldConverterMCA.java new file mode 100644 index 0000000..0dff851 --- /dev/null +++ b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/WorldConverterMCA.java @@ -0,0 +1,255 @@ +package net.lax1dude.eaglercraft.sp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import net.minecraft.src.CompressedStreamTools; +import net.minecraft.src.NBTTagCompound; +import net.minecraft.src.RegionFile; + +public class WorldConverterMCA { + + public static void importWorld(byte[] archiveContents, String newName) throws IOException { + String folderName = newName.replaceAll("[\\./\"]", "_"); + VFile worldDir = new VFile("worlds", folderName); + while((new VFile(worldDir, "level.dat")).exists() || (new VFile(worldDir, "level.dat_old")).exists()) { + folderName += "_"; + worldDir = new VFile("worlds", folderName); + } + List fileNames = new ArrayList<>(); + try(ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(archiveContents))) { + ZipEntry folderNameFile = null; + while((folderNameFile = zis.getNextEntry()) != null) { + if (folderNameFile.getName().contains("__MACOSX/")) continue; + if (folderNameFile.isDirectory()) continue; + String lowerName = folderNameFile.getName().toLowerCase(); + if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue; + fileNames.add(folderNameFile.getName().toCharArray()); + } + } + final int[] i = new int[] { 0 }; + while(fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) i[0]++; + int folderPrefixOffset = i[0]; + try(ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(archiveContents))) { + ZipEntry f = null; + int lastProgUpdate = 0; + int prog = 0; + while ((f = zis.getNextEntry()) != null) { + if (f.getName().contains("__MACOSX/")) continue; + if (f.isDirectory()) continue; + String lowerName = f.getName().toLowerCase(); + if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) continue; + byte[] b; + int sz = (int)f.getSize(); + if(sz >= 0) { + b = new byte[sz]; + int j = 0, k; + while(j < b.length && (k = zis.read(b, j, b.length - j)) != -1) { + j += k; + } + }else { + b = inputStreamToBytesNoClose(zis); + } + String fileName = f.getName().substring(folderPrefixOffset); + if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) { + NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new ByteArrayInputStream(b)); + worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); + worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + CompressedStreamTools.writeCompressed(worldDatNBT, bo); + b = bo.toByteArray(); + VFile ff = new VFile(worldDir, fileName); + ff.setAllBytes(b); + prog += b.length; + } else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) { + VFile chunkFolder = new VFile(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0")); + RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length)); + for(int j = 0; j < 32; ++j) { + for(int k = 0; k < 32; ++k) { + if(mca.isChunkSaved(j, k)) { + NBTTagCompound chunkNBT; + NBTTagCompound chunkLevel; + try { + chunkNBT = CompressedStreamTools.read(mca.getChunkDataInputStream(j, k)); + if(!chunkNBT.hasKey("Level")) { + throw new IOException("Chunk is missing level data!"); + } + chunkLevel = chunkNBT.getCompoundTag("Level"); + }catch(Throwable t) { + System.err.println("Could not read chunk: " + j + ", " + k); + t.printStackTrace(); + continue; + } + int chunkX = chunkLevel.getInteger("xPos"); + int chunkZ = chunkLevel.getInteger("zPos"); + VFile chunkOut = new VFile(chunkFolder, VFSChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat"); + if(chunkOut.exists()) { + System.err.println("Chunk already exists: " + chunkOut.getPath()); + continue; + } + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + CompressedStreamTools.writeCompressed(chunkNBT, bao); + b = bao.toByteArray(); + chunkOut.setAllBytes(b); + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + IntegratedServer.updateStatusString("selectWorld.progress.importing.2", prog); + } + } + } + } + } else if (fileName.startsWith("data/") || fileName.startsWith("players/")) { + VFile ff = new VFile(worldDir, fileName); + ff.setAllBytes(b); + prog += b.length; + } + } + } + String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines(); + if(worldsTxt == null || worldsTxt.length <= 0 || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) { + worldsTxt = new String[] { folderName }; + }else { + String[] tmp = worldsTxt; + worldsTxt = new String[worldsTxt.length + 1]; + System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length); + worldsTxt[worldsTxt.length - 1] = folderName; + } + SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt)); + } + + public static byte[] exportWorld(String folderName) throws IOException { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + VFile worldFolder; + try(ZipOutputStream zos = new ZipOutputStream(bao)) { + zos.setComment("contains backup of world '" + folderName + "'"); + worldFolder = new VFile("worlds", folderName); + VFile vf = new VFile(worldFolder, "level.dat"); + byte[] b; + int lastProgUpdate = 0; + int prog = 0; + boolean safe = false; + if(vf.exists()) { + zos.putNextEntry(new ZipEntry(folderName + "/level.dat")); + b = vf.getAllBytes(); + zos.write(b); + prog += b.length; + safe = true; + } + vf = new VFile(worldFolder, "level.dat_old"); + if(vf.exists()) { + zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old")); + b = vf.getAllBytes(); + zos.write(b); + prog += b.length; + safe = true; + } + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog); + } + String[] srcFolderNames = new String[] { "level0", "level-1", "level1" }; + String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" }; + List fileList; + for(int i = 0; i < 3; ++i) { + vf = new VFile(worldFolder, srcFolderNames[i]); + fileList = SYS.VFS.listVFiles(vf.getPath()); + String regionFolder = folderName + dstFolderNames[i]; + Map regionFiles = new HashMap<>(); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile chunkFile = fileList.get(k); + NBTTagCompound chunkNBT; + NBTTagCompound chunkLevel; + try { + b = chunkFile.getAllBytes(); + chunkNBT = CompressedStreamTools.readCompressed(new ByteArrayInputStream(b)); + if(!chunkNBT.hasKey("Level")) { + throw new IOException("Chunk is missing level data!"); + } + chunkLevel = chunkNBT.getCompoundTag("Level"); + }catch(IOException t) { + System.err.println("Could not read chunk: " + chunkFile.getPath()); + t.printStackTrace(); + continue; + } + int chunkX = chunkLevel.getInteger("xPos"); + int chunkZ = chunkLevel.getInteger("zPos"); + String regionFileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca"; + RegionFile rf = regionFiles.get(regionFileName); + if(rf == null) { + rf = new RegionFile(new RandomAccessMemoryFile(new byte[65536], 0)); + regionFiles.put(regionFileName, rf); + } + try(DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) { + CompressedStreamTools.write(chunkNBT, dos); + }catch(IOException t) { + System.err.println("Could not write chunk to " + regionFileName + ": " + chunkFile.getPath()); + t.printStackTrace(); + continue; + } + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog); + } + } + if(regionFiles.isEmpty()) { + System.err.println("No region files were generated"); + continue; + } + for(Entry etr : regionFiles.entrySet()) { + String regionPath = regionFolder + etr.getKey(); + zos.putNextEntry(new ZipEntry(regionPath)); + zos.write(etr.getValue().getFile().getByteArray()); + } + } + fileList = SYS.VFS.listVFiles((new VFile(worldFolder, "data")).getPath()); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile dataFile = fileList.get(k); + zos.putNextEntry(new ZipEntry(folderName + "/data/" + dataFile.getName())); + b = dataFile.getAllBytes(); + zos.write(b); + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog); + } + } + fileList = SYS.VFS.listVFiles((new VFile(worldFolder, "players")).getPath()); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile dataFile = fileList.get(k); + zos.putNextEntry(new ZipEntry(folderName + "/players/" + dataFile.getName())); + b = dataFile.getAllBytes(); + zos.write(b); + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog); + } + } + } + return bao.toByteArray(); + } + + private static byte[] inputStreamToBytesNoClose(InputStream is) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(1024); + byte[] buf = new byte[1024]; + int i; + while ((i = is.read(buf)) != -1) { + os.write(buf, 0, i); + } + return os.toByteArray(); + } + +} diff --git a/sp-server/src/main/java/net/minecraft/src/RegionFile.java b/sp-server/src/main/java/net/minecraft/src/RegionFile.java new file mode 100644 index 0000000..4ad6459 --- /dev/null +++ b/sp-server/src/main/java/net/minecraft/src/RegionFile.java @@ -0,0 +1,293 @@ +package net.minecraft.src; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +import net.lax1dude.eaglercraft.sp.RandomAccessMemoryFile; + +public class RegionFile { + private static final byte[] emptySector = new byte[4096]; + private RandomAccessMemoryFile dataFile; + private final int[] offsets = new int[1024]; + private final int[] chunkTimestamps = new int[1024]; + private ArrayList sectorFree; + + /** McRegion sizeDelta */ + private int sizeDelta; + private long lastModified = 0L; + + public RegionFile(RandomAccessMemoryFile dataFile) { + this.sizeDelta = 0; + + try { + this.dataFile = dataFile; + int var2; + + if (this.dataFile.getLength() < 4096L) { + for (var2 = 0; var2 < 1024; ++var2) { + this.dataFile.writeInt(0); + } + + for (var2 = 0; var2 < 1024; ++var2) { + this.dataFile.writeInt(0); + } + + this.sizeDelta += 8192; + } + + if ((this.dataFile.getLength() & 4095L) != 0L) { + for (var2 = 0; (long) var2 < (this.dataFile.getLength() & 4095L); ++var2) { + this.dataFile.write(0); + } + } + + var2 = (int) this.dataFile.getLength() / 4096; + this.sectorFree = new ArrayList(var2); + int var3; + + for (var3 = 0; var3 < var2; ++var3) { + this.sectorFree.add(Boolean.valueOf(true)); + } + + this.sectorFree.set(0, Boolean.valueOf(false)); + this.sectorFree.set(1, Boolean.valueOf(false)); + this.dataFile.seek(0); + int var4; + + for (var3 = 0; var3 < 1024; ++var3) { + var4 = this.dataFile.readInt(); + this.offsets[var3] = var4; + + if (var4 != 0 && (var4 >> 8) + (var4 & 255) <= this.sectorFree.size()) { + for (int var5 = 0; var5 < (var4 & 255); ++var5) { + this.sectorFree.set((var4 >> 8) + var5, Boolean.valueOf(false)); + } + } + } + + for (var3 = 0; var3 < 1024; ++var3) { + var4 = this.dataFile.readInt(); + this.chunkTimestamps[var3] = var4; + } + } catch (IOException var6) { + var6.printStackTrace(); + } + } + + /** + * args: x, y - get uncompressed chunk stream from the region file + */ + public synchronized DataInputStream getChunkDataInputStream(int par1, int par2) { + if (this.outOfBounds(par1, par2)) { + return null; + } else { + try { + int var3 = this.getOffset(par1, par2); + + if (var3 == 0) { + return null; + } else { + int var4 = var3 >> 8; + int var5 = var3 & 255; + + if (var4 + var5 > this.sectorFree.size()) { + return null; + } else { + this.dataFile.seek(var4 * 4096); + int var6 = this.dataFile.readInt(); + + if (var6 > 4096 * var5) { + return null; + } else if (var6 <= 0) { + return null; + } else { + byte var7 = this.dataFile.readByte(); + byte[] var8; + + if (var7 == 1) { + var8 = new byte[var6 - 1]; + this.dataFile.read(var8); + return new DataInputStream( + new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(var8)))); + } else if (var7 == 2) { + var8 = new byte[var6 - 1]; + this.dataFile.read(var8); + return new DataInputStream(new BufferedInputStream( + new InflaterInputStream(new ByteArrayInputStream(var8)))); + } else { + return null; + } + } + } + } + } catch (IOException var9) { + return null; + } + } + } + + /** + * args: x, z - get an output stream used to write chunk data, data is on disk + * when the returned stream is closed + */ + public DataOutputStream getChunkDataOutputStream(int par1, int par2) { + return this.outOfBounds(par1, par2) ? null + : new DataOutputStream(new DeflaterOutputStream(new ChunkBuffer(par1, par2))); + } + + /** + * args: x, z, data, length - write chunk data at (x, z) to disk + */ + protected synchronized void write(int par1, int par2, byte[] par3ArrayOfByte, int par4) { + try { + int var5 = this.getOffset(par1, par2); + int var6 = var5 >> 8; + int var7 = var5 & 255; + int var8 = (par4 + 5) / 4096 + 1; + + if (var8 >= 256) { + return; + } + + if (var6 != 0 && var7 == var8) { + this.write(var6, par3ArrayOfByte, par4); + } else { + int var9; + + for (var9 = 0; var9 < var7; ++var9) { + this.sectorFree.set(var6 + var9, Boolean.valueOf(true)); + } + + var9 = this.sectorFree.indexOf(Boolean.valueOf(true)); + int var10 = 0; + int var11; + + if (var9 != -1) { + for (var11 = var9; var11 < this.sectorFree.size(); ++var11) { + if (var10 != 0) { + if (((Boolean) this.sectorFree.get(var11)).booleanValue()) { + ++var10; + } else { + var10 = 0; + } + } else if (((Boolean) this.sectorFree.get(var11)).booleanValue()) { + var9 = var11; + var10 = 1; + } + + if (var10 >= var8) { + break; + } + } + } + + if (var10 >= var8) { + var6 = var9; + this.setOffset(par1, par2, var9 << 8 | var8); + + for (var11 = 0; var11 < var8; ++var11) { + this.sectorFree.set(var6 + var11, Boolean.valueOf(false)); + } + + this.write(var6, par3ArrayOfByte, par4); + } else { + this.dataFile.seek(this.dataFile.getLength()); + var6 = this.sectorFree.size(); + + for (var11 = 0; var11 < var8; ++var11) { + this.dataFile.write(emptySector); + this.sectorFree.add(Boolean.valueOf(false)); + } + + this.sizeDelta += 4096 * var8; + this.write(var6, par3ArrayOfByte, par4); + this.setOffset(par1, par2, var6 << 8 | var8); + } + } + + this.setChunkTimestamp(par1, par2, (int) (System.currentTimeMillis() / 1000L)); + } catch (IOException var12) { + var12.printStackTrace(); + } + } + + /** + * args: sectorNumber, data, length - write the chunk data to this RegionFile + */ + private void write(int par1, byte[] par2ArrayOfByte, int par3) throws IOException { + this.dataFile.seek(par1 * 4096); + this.dataFile.writeInt(par3 + 1); + this.dataFile.writeByte(2); + this.dataFile.write(par2ArrayOfByte, 0, par3); + } + + /** + * args: x, z - check region bounds + */ + private boolean outOfBounds(int par1, int par2) { + return par1 < 0 || par1 >= 32 || par2 < 0 || par2 >= 32; + } + + /** + * args: x, y - get chunk's offset in region file + */ + private int getOffset(int par1, int par2) { + return this.offsets[par1 + par2 * 32]; + } + + /** + * args: x, z, - true if chunk has been saved / converted + */ + public boolean isChunkSaved(int par1, int par2) { + return this.getOffset(par1, par2) != 0; + } + + /** + * args: x, z, offset - sets the chunk's offset in the region file + */ + private void setOffset(int par1, int par2, int par3) throws IOException { + this.offsets[par1 + par2 * 32] = par3; + this.dataFile.seek((par1 + par2 * 32) * 4); + this.dataFile.writeInt(par3); + } + + /** + * args: x, z, timestamp - sets the chunk's write timestamp + */ + private void setChunkTimestamp(int par1, int par2, int par3) throws IOException { + this.chunkTimestamps[par1 + par2 * 32] = par3; + this.dataFile.seek(4096 + (par1 + par2 * 32) * 4); + this.dataFile.writeInt(par3); + } + + public RandomAccessMemoryFile getFile() { + return dataFile; + } + + class ChunkBuffer extends ByteArrayOutputStream { + private int chunkX; + private int chunkZ; + + public ChunkBuffer(int x, int z) { + super(8096); + this.chunkX = x; + this.chunkZ = z; + } + + /**+ + * close this RegionFile and prevent further writes + */ + public void close() throws IOException { + RegionFile.this.write(this.chunkX, this.chunkZ, this.buf, this.count); + } + } + +} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index dbd5864..9ffea1a 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -14,7 +14,6 @@ import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -72,6 +71,7 @@ import org.lwjgl.util.glu.GLU; import de.cuina.fireandfuel.CodecJLayerMP3; import net.lax1dude.eaglercraft.AssetRepository; import net.lax1dude.eaglercraft.EaglerImage; +import net.lax1dude.eaglercraft.EaglerInputStream; import net.lax1dude.eaglercraft.EarlyLoadScreen; import net.lax1dude.eaglercraft.LANPeerEvent; import net.lax1dude.eaglercraft.PKT; @@ -104,7 +104,7 @@ public class EaglerAdapterImpl2 { public static final InputStream loadResource(String path) { byte[] file = loadResourceBytes(path); if (file != null) { - return new ByteArrayInputStream(file); + return new EaglerInputStream(file); } else { return null; } @@ -638,7 +638,7 @@ public class EaglerAdapterImpl2 { public static final EaglerImage loadPNG(byte[] data) { try { - BufferedImage img = ImageIO.read(new ByteArrayInputStream(data)); + BufferedImage img = ImageIO.read(new EaglerInputStream(data)); int[] pxls = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()); return new EaglerImage(pxls, img.getWidth(), img.getHeight(), true); } catch (IOException e) { diff --git a/src/main/java/net/lax1dude/eaglercraft/AssetRepository.java b/src/main/java/net/lax1dude/eaglercraft/AssetRepository.java index 47a031a..68d3772 100644 --- a/src/main/java/net/lax1dude/eaglercraft/AssetRepository.java +++ b/src/main/java/net/lax1dude/eaglercraft/AssetRepository.java @@ -1,6 +1,5 @@ package net.lax1dude.eaglercraft; -import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; @@ -34,7 +33,7 @@ public class AssetRepository { } public static final void install(byte[] pkg) throws IOException { - ByteArrayInputStream in = new ByteArrayInputStream(pkg); + EaglerInputStream in = new EaglerInputStream(pkg); byte[] header = new byte[8]; in.read(header); @@ -52,7 +51,7 @@ public class AssetRepository { throw new IOException("EPK file is missing EOF code (:::YEE:>)"); } } - loadNew(new ByteArrayInputStream(pkg, 8, pkg.length - 16)); + loadNew(new EaglerInputStream(pkg, 8, pkg.length - 16)); }else if("EAGPKG!!".equals(type)) { loadOld(in); }else { @@ -178,7 +177,7 @@ public class AssetRepository { int len2 = (((int)load[off] & 0xff) << 24) | (((int)load[off + 1] & 0xff) << 16) | (((int)load[off + 2] & 0xff) << 8) | ((int)load[off + 3] & 0xff); if(off + 8 + len2 < load.length) { - loadNew(new ByteArrayInputStream(load, off + 8, len2)); + loadNew(new EaglerInputStream(load, off + 8, len2)); } }catch(Throwable t) { } diff --git a/src/main/java/net/lax1dude/eaglercraft/EPKDecompiler.java b/src/main/java/net/lax1dude/eaglercraft/EPKDecompiler.java index 5810de3..3e63190 100644 --- a/src/main/java/net/lax1dude/eaglercraft/EPKDecompiler.java +++ b/src/main/java/net/lax1dude/eaglercraft/EPKDecompiler.java @@ -1,6 +1,5 @@ package net.lax1dude.eaglercraft; -import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; @@ -23,7 +22,7 @@ public class EPKDecompiler { } } - private ByteArrayInputStream in2; + private EaglerInputStream in2; private DataInputStream in; private InputStream zis; private SHA1Digest dg; @@ -33,7 +32,7 @@ public class EPKDecompiler { private boolean isOldFormat = false; public EPKDecompiler(byte[] data) throws IOException { - in2 = new ByteArrayInputStream(data); + in2 = new EaglerInputStream(data); byte[] header = new byte[8]; in2.read(header); @@ -46,7 +45,7 @@ public class EPKDecompiler { throw new IOException("EPK file is missing EOF code (:::YEE:>)"); } } - in2 = new ByteArrayInputStream(data, 8, data.length - 16); + in2 = new EaglerInputStream(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(); diff --git a/src/main/java/net/lax1dude/eaglercraft/EaglerInputStream.java b/src/main/java/net/lax1dude/eaglercraft/EaglerInputStream.java new file mode 100644 index 0000000..a20a316 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/EaglerInputStream.java @@ -0,0 +1,167 @@ +package net.lax1dude.eaglercraft; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class EaglerInputStream extends InputStream { + + protected byte buf[]; + protected int pos; + protected int mark = 0; + protected int count; + + public EaglerInputStream(byte[] buf) { + this.buf = buf; + this.pos = 0; + this.count = buf.length; + } + + public EaglerInputStream(byte buf[], int offset, int length) { + this.buf = buf; + this.pos = offset; + this.count = Math.min(offset + length, buf.length); + this.mark = offset; + } + + public int read() { + return (pos < count) ? (buf[pos++] & 0xff) : -1; + } + + public int read(byte b[], int off, int len) { + if (pos >= count) { + return -1; + } + + int avail = count - pos; + if (len > avail) { + len = avail; + } + if (len <= 0) { + return 0; + } + System.arraycopy(buf, pos, b, off, len); + pos += len; + return len; + } + + public byte[] readAllBytes() { + byte[] result = Arrays.copyOfRange(buf, pos, count); + pos = count; + return result; + } + + public int readNBytes(byte[] b, int off, int len) { + int n = read(b, off, len); + return n == -1 ? 0 : n; + } + + public long transferTo(OutputStream out) throws IOException { + int len = count - pos; + out.write(buf, pos, len); + pos = count; + return len; + } + + public static byte[] inputStreamToBytesQuiet(InputStream is) { + if (is == null) { + return null; + } + try { + return inputStreamToBytes(is); + } catch (IOException ex) { + return null; + } + } + + public long skip(long n) { + long k = count - pos; + if (n < k) { + k = n < 0 ? 0 : n; + } + + pos += k; + return k; + } + + public int available() { + return count - pos; + } + + public boolean markSupported() { + return true; + } + + public void mark(int readAheadLimit) { + mark = pos; + } + + public void reset() { + pos = mark; + } + + public void close() throws IOException { + } + + public static byte[] inputStreamToBytes(InputStream is) throws IOException { + try { + if (is instanceof EaglerInputStream) { + return ((EaglerInputStream) is).getAsArray(); + } else if (is instanceof ByteArrayInputStream) { + byte[] ret = new byte[is.available()]; + is.read(ret); + return ret; + } else { + ByteArrayOutputStream os = new ByteArrayOutputStream(1024); + byte[] buf = new byte[1024]; + int i; + while ((i = is.read(buf)) != -1) { + os.write(buf, 0, i); + } + return os.toByteArray(); + } + }finally { + is.close(); + } + } + + public static byte[] inputStreamToBytesNoClose(InputStream is) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(1024); + byte[] buf = new byte[1024]; + int i; + while ((i = is.read(buf)) != -1) { + os.write(buf, 0, i); + } + return os.toByteArray(); + } + + public byte[] getAsArray() { + if (pos == 0 && count == buf.length) { + return buf; + } else { + byte[] ret = new byte[count]; + System.arraycopy(buf, pos, ret, 0, count); + return ret; + } + } + + public boolean canUseArrayDirectly() { + return pos == 0 && count == buf.length; + } + + public int getPosition() { + return pos; + } + + public int getMark() { + return mark; + } + + public int getCount() { + return count; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/EaglerMisc.java b/src/main/java/net/lax1dude/eaglercraft/EaglerMisc.java index ba5be90..25cfc6a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/EaglerMisc.java +++ b/src/main/java/net/lax1dude/eaglercraft/EaglerMisc.java @@ -1,39 +1,28 @@ package net.lax1dude.eaglercraft; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import com.jcraft.jzlib.InflaterInputStream; public class EaglerMisc { - - public static byte[] uncompress(byte[] input) throws IOException { - return getBytesFromInputStream(new InflaterInputStream(new ByteArrayInputStream(input))); - } - - public static byte[] getBytesFromInputStream(InputStream is) throws IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - byte[] buffer = new byte[0xFFFF]; - for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { - os.write(buffer, 0, len); - } - return os.toByteArray(); - } - public static String bytesToString(byte[] bb) { - if (bb == null) return ""; - return new String(bb, Charset.forName("UTF-8")); - } + public static byte[] uncompress(byte[] input) throws IOException { + return EaglerInputStream.inputStreamToBytes(new InflaterInputStream(new EaglerInputStream(input))); + } - public static String[] bytesToLines(byte[] bb) { - String contents = bytesToString(bb); - if(contents.isEmpty()) { - return new String[0]; - }else { - return contents.replace("\r\n", "\n").split("[\r\n]"); - } - } + public static String bytesToString(byte[] bb) { + if (bb == null) + return ""; + return new String(bb, StandardCharsets.UTF_8); + } + + public static String[] bytesToLines(byte[] bb) { + String contents = bytesToString(bb); + if (contents.isEmpty()) { + return new String[0]; + } else { + return contents.replace("\r\n", "\n").split("[\r\n]"); + } + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java b/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java index 7915c0d..b943514 100644 --- a/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java +++ b/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java @@ -358,7 +358,7 @@ public class IntegratedServerLAN { if(!dead) { if(state == CONNECTED) { IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(clientId, false)); - EaglerAdapter.disableChannel("NET|" + clientId); + EaglerAdapter.disableChannel(channelId); } state = CLOSED; EaglerAdapter.serverLANDisconnectPeer(clientId); diff --git a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java index f20c033..76a0f52 100644 --- a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java @@ -1,6 +1,5 @@ package net.lax1dude.eaglercraft; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -267,7 +266,7 @@ public class LANClientNetworkManager implements INetworkManager { continue; } - ByteArrayInputStream bai = new ByteArrayInputStream(fullData); + EaglerInputStream bai = new EaglerInputStream(fullData); int pktId = bai.read(); diff --git a/src/main/java/net/lax1dude/eaglercraft/WorkerNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/WorkerNetworkManager.java index 13223e1..8f75837 100644 --- a/src/main/java/net/lax1dude/eaglercraft/WorkerNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/WorkerNetworkManager.java @@ -1,6 +1,5 @@ package net.lax1dude.eaglercraft; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -52,7 +51,7 @@ public class WorkerNetworkManager implements INetworkManager { while((ipcPacket = EaglerAdapter.recieveFromIntegratedServer("NET|" + ipcChannel)) != null) { byte[] bytes = ipcPacket.data; try { - ByteArrayInputStream bai = new ByteArrayInputStream(bytes); + EaglerInputStream bai = new EaglerInputStream(bytes); int pktId = bai.read(); if(pktId == -1) { diff --git a/src/main/java/net/lax1dude/eaglercraft/glemu/HighPolyMesh.java b/src/main/java/net/lax1dude/eaglercraft/glemu/HighPolyMesh.java index f4e5708..b803375 100644 --- a/src/main/java/net/lax1dude/eaglercraft/glemu/HighPolyMesh.java +++ b/src/main/java/net/lax1dude/eaglercraft/glemu/HighPolyMesh.java @@ -2,7 +2,6 @@ package net.lax1dude.eaglercraft.glemu; import static net.lax1dude.eaglercraft.EaglerAdapter.*; -import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; @@ -10,6 +9,7 @@ import java.nio.IntBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import net.lax1dude.eaglercraft.EaglerInputStream; import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.BufferArrayGL; import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.BufferGL; import net.minecraft.src.GLAllocation; @@ -44,7 +44,7 @@ public class HighPolyMesh { static final byte[] headerSequence = "!EAG%mdl".getBytes(StandardCharsets.UTF_8); static HighPolyMesh loadMeshData(byte[] mesh) throws IOException { - DataInputStream mdlIn = new DataInputStream(new ByteArrayInputStream(mesh)); + DataInputStream mdlIn = new DataInputStream(new EaglerInputStream(mesh)); byte[] hd = new byte[headerSequence.length]; mdlIn.read(hd); diff --git a/src/main/java/net/minecraft/src/GuiTexturePacks.java b/src/main/java/net/minecraft/src/GuiTexturePacks.java index 497e149..259ac1a 100644 --- a/src/main/java/net/minecraft/src/GuiTexturePacks.java +++ b/src/main/java/net/minecraft/src/GuiTexturePacks.java @@ -2,6 +2,7 @@ package net.minecraft.src; import net.lax1dude.eaglercraft.EPKDecompiler; import net.lax1dude.eaglercraft.EaglerAdapter; +import net.lax1dude.eaglercraft.EaglerInputStream; import net.lax1dude.eaglercraft.EaglerMisc; import net.lax1dude.eaglercraft.adapter.teavm.vfs.VFile; @@ -108,13 +109,13 @@ public class GuiTexturePacks extends GuiScreen { 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(EaglerMisc.getBytesFromInputStream(zipInputStream)); + try(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(EaglerInputStream.inputStreamToBytesNoClose(zipInputStream)); + } } - zipInputStream.close(); } else { EPKDecompiler epkDecompiler = new EPKDecompiler(EaglerAdapter.getFileChooserResult()); EPKDecompiler.FileEntry file; diff --git a/src/main/java/net/minecraft/src/TexturePackFolder.java b/src/main/java/net/minecraft/src/TexturePackFolder.java index 89309d8..408e597 100644 --- a/src/main/java/net/minecraft/src/TexturePackFolder.java +++ b/src/main/java/net/minecraft/src/TexturePackFolder.java @@ -2,39 +2,30 @@ 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) - { +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 - { + protected InputStream func_98139_b(String par1Str) throws IOException { VFile var2 = new VFile(this.texturePackFile, par1Str.substring(1)); - if (!var2.exists()) - { + if (!var2.exists()) { throw new IOException(par1Str); - } - else - { - return new BufferedInputStream(var2.getInputStream()); + } else { + return var2.getInputStream(); } } - public boolean func_98140_c(String par1Str) - { + public boolean func_98140_c(String par1Str) { VFile var2 = new VFile(this.texturePackFile, par1Str); return var2.exists(); } - public boolean isCompatible() - { + public boolean isCompatible() { return true; } } diff --git a/src/main/java/net/minecraft/src/TexturePackImplementation.java b/src/main/java/net/minecraft/src/TexturePackImplementation.java index 7708cf7..83408ab 100644 --- a/src/main/java/net/minecraft/src/TexturePackImplementation.java +++ b/src/main/java/net/minecraft/src/TexturePackImplementation.java @@ -7,6 +7,7 @@ import java.io.InputStreamReader; import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.EaglerImage; +import net.lax1dude.eaglercraft.EaglerInputStream; import net.lax1dude.eaglercraft.EaglerMisc; import net.lax1dude.eaglercraft.adapter.teavm.vfs.VFile; @@ -78,7 +79,7 @@ public abstract class TexturePackImplementation implements ITexturePack { var1 = this.func_98137_a("/pack.png", false); if (var1 != null) { - this.thumbnailImage = EaglerImage.loadImage(EaglerMisc.getBytesFromInputStream(var1)); + this.thumbnailImage = EaglerImage.loadImage(EaglerInputStream.inputStreamToBytes(var1)); } } catch (IOException var11) @@ -170,7 +171,7 @@ public abstract class TexturePackImplementation implements ITexturePack try { is = this.func_98137_a(par1Str, true); if (is == null) return null; - res = EaglerMisc.getBytesFromInputStream(is); + res = EaglerInputStream.inputStreamToBytes(is); } catch (IOException e) { return null; } finally { diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index 4732261..39e38c3 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -2,7 +2,6 @@ package net.lax1dude.eaglercraft.adapter; import static net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext.*; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -94,6 +93,7 @@ import net.lax1dude.eaglercraft.Base64; import net.lax1dude.eaglercraft.Client; import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.EaglerImage; +import net.lax1dude.eaglercraft.EaglerInputStream; import net.lax1dude.eaglercraft.EaglerProfile; import net.lax1dude.eaglercraft.EarlyLoadScreen; import net.lax1dude.eaglercraft.ExpiringSet; @@ -144,7 +144,7 @@ public class EaglerAdapterImpl2 { public static final InputStream loadResource(String path) { byte[] file = loadResourceBytes(path); if (file != null) { - return new ByteArrayInputStream(file); + return new EaglerInputStream(file); } else { return null; } @@ -2515,7 +2515,7 @@ public class EaglerAdapterImpl2 { public static void handleVoiceSignal(byte[] data) { try { - DataInputStream streamIn = new DataInputStream(new ByteArrayInputStream(data)); + DataInputStream streamIn = new DataInputStream(new EaglerInputStream(data)); int sig = streamIn.read(); switch(sig) { case VOICE_SIGNAL_GLOBAL: @@ -3408,7 +3408,7 @@ public class EaglerAdapterImpl2 { }else { if(open) { try { - IPacket pkt = IPacket.readPacket(new DataInputStream(new ByteArrayInputStream(arr))); + IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); if(pkt instanceof IPacket69Pong) { IPacket69Pong ipkt = (IPacket69Pong)pkt; versError = RelayQuery.VersionMismatch.COMPATIBLE; @@ -3668,7 +3668,7 @@ public class EaglerAdapterImpl2 { }else { if(open) { try { - IPacket pkt = IPacket.readPacket(new DataInputStream(new ByteArrayInputStream(arr))); + IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr))); if(pkt instanceof IPacket07LocalWorlds) { worlds = ((IPacket07LocalWorlds)pkt).worldsList; sock.close(); @@ -3865,7 +3865,7 @@ public class EaglerAdapterImpl2 { hasRecievedAnyData = true; byte[] arr = TeaVMUtils.wrapByteArrayBuffer(evt.getDataAsArray()); try { - packets.add(IPacket.readPacket(new DataInputStream(new ByteArrayInputStream(arr)))); + packets.add(IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)))); } catch (IOException e) { exceptions.add(e); System.err.println("Relay Socket Error: " + e.toString()); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VIteratorFile.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VIteratorFile.java index cc96f9d..e179e70 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VIteratorFile.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VIteratorFile.java @@ -1,6 +1,5 @@ package net.lax1dude.eaglercraft.adapter.teavm.vfs; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -18,6 +17,8 @@ import org.teavm.jso.indexeddb.IDBRequest; import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.Uint8Array; +import net.lax1dude.eaglercraft.EaglerInputStream; + /** * Do not use an instance of this class outside of the VFSIterator.next() method */ @@ -78,7 +79,7 @@ public class VIteratorFile extends VFile { } public InputStream getInputStream() { - return !wasDeleted ? new ByteArrayInputStream(getAllBytes()) : null; + return !wasDeleted ? new EaglerInputStream(getAllBytes()) : null; } public OutputStream getOutputStream() { diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VirtualFilesystem.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VirtualFilesystem.java index 91ebedb..c9bb56b 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VirtualFilesystem.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/vfs/VirtualFilesystem.java @@ -12,7 +12,9 @@ import java.util.Iterator; import java.util.List; import net.lax1dude.eaglercraft.EaglerAdapter; +import net.lax1dude.eaglercraft.EaglerInputStream; import net.lax1dude.eaglercraft.adapter.teavm.TeaVMUtils; + import org.teavm.interop.Async; import org.teavm.interop.AsyncCallback; import org.teavm.jso.JSBody; @@ -102,11 +104,11 @@ public class VirtualFilesystem { } public InputStream getInputStream() { - byte[] dat = getAllBytes(false); + byte[] dat = getAllBytes(true); if(dat == null) { return null; } - return new ByteArrayInputStream(dat); + return new EaglerInputStream(dat); } public OutputStream getOutputStream() { @@ -389,6 +391,14 @@ public class VirtualFilesystem { return list; } + public List listVFiles(String prefix) { + final ArrayList list = new ArrayList<>(); + AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> { + list.add(new VFile(v.getPath())); + }); + return list; + } + public int deleteFiles(String prefix) { return AsyncHandlers.deleteFiles(indexeddb, prefix); }