Add EaglerInputStream, fix MCA converter

This commit is contained in:
lax1dude 2024-12-11 00:12:21 -08:00
parent a7875b2a39
commit 46b4bce78f
22 changed files with 1198 additions and 490 deletions

View File

@ -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<ChunkCoordIntPair, byte[]> regions = new HashMap<>();
Map<ChunkCoordIntPair, byte[]> regions1 = new HashMap<>();
Map<ChunkCoordIntPair, byte[]> 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<String, byte[]> 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<String, byte[]> 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<String, byte[]> 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<char[]> 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();
}

View File

@ -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<String, byte[]> convertToMCA(Map<ChunkCoordIntPair, byte[]> regions) {
Map<String, byte[]> 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;
}
}

View File

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

View File

@ -388,6 +388,14 @@ public class VirtualFilesystem {
return list;
}
public List<VFile> listVFiles(String prefix) {
final ArrayList<VFile> 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);
}

View File

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

View File

@ -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<char[]> 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<VFile> 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<String,RegionFile> 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<String,RegionFile> 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();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] uncompress(byte[] input) throws IOException {
return EaglerInputStream.inputStreamToBytes(new InflaterInputStream(new EaglerInputStream(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, StandardCharsets.UTF_8);
}
public static String bytesToString(byte[] bb) {
if (bb == null) return "";
return new String(bb, Charset.forName("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]");
}
}
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]");
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<VFile> listVFiles(String prefix) {
final ArrayList<VFile> 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);
}