Add EaglerInputStream, fix MCA converter
This commit is contained in:
parent
a7875b2a39
commit
46b4bce78f
|
@ -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);
|
||||
if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) {
|
||||
try {
|
||||
sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterEPK.exportWorld(pkt.worldName)));
|
||||
} catch (Throwable 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);
|
||||
}
|
||||
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()));
|
||||
} catch (Throwable t) {
|
||||
throwExceptionToClient("Failed to export world '" + realWorldName + "' as EPK", t);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterMCA.exportWorld(pkt.worldName)));
|
||||
} catch (Throwable t) {
|
||||
throwExceptionToClient("Failed to export file '" + currPath + "'", t);
|
||||
throwExceptionToClient("Failed to export world '" + pkt.worldName+ "' as MCA", 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()));
|
||||
} catch (Throwable 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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
293
sp-server/src/main/java/net/minecraft/src/RegionFile.java
Normal file
293
sp-server/src/main/java/net/minecraft/src/RegionFile.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
167
src/main/java/net/lax1dude/eaglercraft/EaglerInputStream.java
Normal file
167
src/main/java/net/lax1dude/eaglercraft/EaglerInputStream.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +1,27 @@
|
|||
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();
|
||||
return EaglerInputStream.inputStreamToBytes(new InflaterInputStream(new EaglerInputStream(input)));
|
||||
}
|
||||
|
||||
public static String bytesToString(byte[] bb) {
|
||||
if (bb == null) return "";
|
||||
return new String(bb, Charset.forName("UTF-8"));
|
||||
if (bb == null)
|
||||
return "";
|
||||
return new String(bb, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String[] bytesToLines(byte[] bb) {
|
||||
String contents = bytesToString(bb);
|
||||
if(contents.isEmpty()) {
|
||||
if (contents.isEmpty()) {
|
||||
return new String[0];
|
||||
}else {
|
||||
} else {
|
||||
return contents.replace("\r\n", "\n").split("[\r\n]");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
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(EaglerMisc.getBytesFromInputStream(zipInputStream));
|
||||
new VFile(fileLocation, safeName, entry.getName()).setAllBytes(EaglerInputStream.inputStreamToBytesNoClose(zipInputStream));
|
||||
}
|
||||
}
|
||||
zipInputStream.close();
|
||||
} else {
|
||||
EPKDecompiler epkDecompiler = new EPKDecompiler(EaglerAdapter.getFileChooserResult());
|
||||
EPKDecompiler.FileEntry 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user