diff --git a/NBTUtils/NBTUtils.iml b/NBTUtils/NBTUtils.iml new file mode 100644 index 0000000..f3f9362 --- /dev/null +++ b/NBTUtils/NBTUtils.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/NBTUtils/src/main/Utils/BytesUtils.java b/NBTUtils/src/main/Utils/BytesUtils.java new file mode 100644 index 0000000..8ee79ce --- /dev/null +++ b/NBTUtils/src/main/Utils/BytesUtils.java @@ -0,0 +1,240 @@ +package main.Utils; + +import com.sun.jdi.ByteValue; + +import java.util.BitSet; + +public class BytesUtils { + + public static String bytes2fStr(byte[] data) { + String result = ""; + for (int i = 0; i < data.length; i++) { + result += Integer.toHexString((data[i] & 0xFF) | 0x100).toUpperCase().substring(1, 3) + " "; + } + return result; + } + + public static short bytes2short(byte[] res) { + short ret = 0; + for (int i = 0; i < 2; i++) { + if (i < res.length) + ret |= (short) (res[i] & 0xFF) << (i * 8); + } + return ret; + } + + public static int bytes2int(byte[] res) { + int ret = 0; + for (int i = 0; i < 4; i++) { + if (i < res.length) + ret |= (res[i] & 0xFF) << (i * 8); + } + return ret; + } + + public static long bytes2long(byte[] res) { + long ret = 0; + for (int i = 0; i < 8; i++) { + if (i < res.length) + ret |= (long) (res[i] & 0xFF) << (i * 8); + } + return ret; + } + + public static float bytes2float(byte[] res) { + int ret = 0; + for (int i = 0; i < 4; i++) { + ret |= (res[i] & 0xFF) << (i * 8); + } + return Float.intBitsToFloat(ret); + } + + public static double bytes2double(byte[] res) { + long ret = 0; + for (int i = 0; i < 8; i++) { + ret |= (long) (res[i] & 0xFF) << (i * 8); + } + return Double.longBitsToDouble(ret); + } + + public static byte byte2byteA(byte res) { + BitSet ret = new BitSet(8); + BitSet resb = BitSet.valueOf(new byte[]{res}); + for (int i = 0; i < 8; i++) { + ret.set(i, resb.get(7 - i)); + } + if (ret.length() == 0) + return -128; + return ret.toByteArray()[0]; + } + + public static byte[] bytes2bytesA(byte[] res) { + byte[] ret = new byte[res.length]; + for (int i = 0; i < res.length; i++) { + ret[i] = byte2byteA(res[i]); + } + return ret; + } + + public static short bytes2shortA(byte[] res) { + short ret = 0; + for (int i = 0; i < 2; i++) { + ret |= (short) (res[1 - i] & 0xFF) << (i * 8); + } + return ret; + } + + public static byte[] bytesCut(byte[] res, int pos,int destPos, int destLen) { + byte[] ret = new byte[destLen]; + System.arraycopy(res, pos, ret, destPos, destLen); + return ret; + } + + public static byte[] bytesSplicing(byte[] a, byte[] b) { + byte[] ret = new byte[a.length + b.length]; + System.arraycopy(a, 0, ret, 0, a.length); + System.arraycopy(b, 0, ret, a.length, b.length); + return ret; + } + + public static int bytes2intA(byte[] res) { + int ret = 0; + for (int i = 0; i < 4; i++) { + if (3 - i < res.length) + ret |= (res[3 - i] & 0xFF) << (i * 8); + } + return ret; + } + + public static long bytes2longA(byte[] res) { + long ret = 0; + for (int i = 0; i < 8; i++) { + if (7 - i < res.length) + ret |= (long) (res[7 - i] & 0xFF) << (i * 8); + } + return ret; + } + + public static float bytes2floatA(byte[] res) { + int ret = 0; + for (int i = 0; i < 4; i++) { + ret |= (res[3 - i] & 0xFF) << (i * 8); + } + return Float.intBitsToFloat(ret); + } + + public static double bytes2doubleA(byte[] res) { + long ret = 0; + for (int i = 0; i < 8; i++) { + ret |= (long) (res[7 - i] & 0xFF) << (i * 8); + } + return Double.longBitsToDouble(ret); + } + + + public static byte[] longs2bytesA(long[] res) { + byte[] ret = new byte[res.length * 8]; + + for (int i = 0; i < res.length; i++) { + + for (int j = 0; j < 8; j++) {//字节 + ret[j + i * 8] = long2bytes(res[i])[7 - j]; + } + + } + + return ret; + } + + + public static byte[] short2bytes(short res) { + byte[] ret = new byte[2]; + for (int i = 0; i < 2; i++) { + int offset = 16 - (i + 1) * 8; + ret[i] = (byte) ((res >> offset) & 0xff); + } + return ret; + } + + public static byte[] int2bytes(int res) { + byte[] ret = new byte[4]; + for (int i = 0; i < 4; i++) { + int offset = 32 - (i + 1) * 8; + ret[i] = (byte) ((res >> offset) & 0xff); + } + return ret; + } + + public static byte[] long2bytes(long res) { + byte[] ret = new byte[8]; + for (int i = 0; i < 8; i++) { + int offset = 64 - (i + 1) * 8; + ret[i] = (byte) ((res >> offset) & 0xff); + } + return ret; + } + + public static byte[] float2bytes(float res) { + return int2bytes(Float.floatToIntBits(res)); + } + + public static byte[] short2bytesA(short res) { + byte[] buffer = new byte[2]; + for (int i = 0; i < 2; i++) { + int offset = 16 - (1 - i + 1) * 8; + buffer[i] = (byte) ((res >> offset) & 0xff); + } + return buffer; + } + + public static byte[] int2bytesA(int res) { + byte[] buffer = new byte[4]; + for (int i = 0; i < 4; i++) { + int offset = 32 - (3 - i + 1) * 8; + buffer[i] = (byte) ((res >> offset) & 0xff); + } + return buffer; + } + + public static byte[] long2bytesA(long res) { + byte[] buffer = new byte[8]; + for (int i = 0; i < 8; i++) { + int offset = 64 - (7 - i + 1) * 8; + buffer[i] = (byte) ((res >> offset) & 0xff); + } + return buffer; + } + + public static byte[] double2bytes(double res) { + return long2bytes(Double.doubleToLongBits(res)); + } + + public static byte[] float2bytesA(float res) { + return int2bytesA(Float.floatToIntBits(res)); + } + + public static byte[] double2bytesA(double res) { + return long2bytesA(Double.doubleToLongBits(res)); + } + + + public static BitSet bytes2bits(byte[] res) { + BitSet ret = new BitSet(res.length * 8); + for (int i = 0; i < res.length; i++) { + for (int j = 0; j < 8; j++) { + ret.set(i * 8 + j, ((res[i] >> j) & 0x01) == 1); + } + } + return ret; + } + + public static BitSet bytes2bitsA(byte[] res) { + BitSet ret = new BitSet(res.length * 8); + for (int i = 0; i < res.length; i++) { + for (int j = 0; j < 8; j++) { + ret.set(res.length * 8 - i * 8 - j - 1, ((res[i] >> j) & 0x01) == 1); + } + } + return ret; + } +} diff --git a/NBTUtils/src/main/Utils/CompressUtils.java b/NBTUtils/src/main/Utils/CompressUtils.java new file mode 100644 index 0000000..1fec901 --- /dev/null +++ b/NBTUtils/src/main/Utils/CompressUtils.java @@ -0,0 +1,66 @@ +package main.Utils; + +import java.io.*; +import java.util.zip.*; + +public abstract class CompressUtils { + //Zlib压缩 + public static byte[] zlibCompress(byte[] data) throws IOException { + byte[] output = new byte[0]; + + Deflater compresser = new Deflater(); + + compresser.reset(); + compresser.setInput(data); + compresser.finish(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length); + byte[] buf = new byte[1024]; + while (!compresser.finished()) { + int i = compresser.deflate(buf); + bos.write(buf, 0, i); + } + output = bos.toByteArray(); + bos.close(); + compresser.end(); + return output; + } + + //Zlib解压 + public static byte[] zlibDecompress(byte[] data) throws IOException, DataFormatException { + + + Inflater decompresser = new Inflater(); + decompresser.reset(); + decompresser.setInput(data); + + ByteArrayOutputStream o = new ByteArrayOutputStream(data.length); + + byte[] buf = new byte[1024]; + while (!decompresser.finished()) { + int i = decompresser.inflate(buf); + o.write(buf, 0, i); + } + byte[] output = o.toByteArray(); + o.close(); + decompresser.end(); + return output; + } + + //Gzip压缩 + public static byte[] gzipCompress(byte[] data) throws IOException { + ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream =new GZIPOutputStream(byteArrayOutputStream); + gzipOutputStream.write(data); + gzipOutputStream.close(); + return byteArrayOutputStream.toByteArray(); + } + + //Gzip解压 + public static byte[] gzipDecompress(byte[] data) throws IOException { + GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(data)); + byte[] output = gzipInputStream.readAllBytes(); + gzipInputStream.close(); + return output; + } + +} \ No newline at end of file diff --git a/NBTUtils/src/main/exception/NBTException.java b/NBTUtils/src/main/exception/NBTException.java new file mode 100644 index 0000000..55d8357 --- /dev/null +++ b/NBTUtils/src/main/exception/NBTException.java @@ -0,0 +1,16 @@ +package main.exception; + +public class NBTException extends RuntimeException { + public NBTException(String message) { + super(message); + System.out.println(message); + throw new RuntimeException(); + } + + + @Override + public synchronized Throwable fillInStackTrace() { + // fast valid + return null; + } +} diff --git a/NBTUtils/src/main/io/MCUtil.java b/NBTUtils/src/main/io/MCUtil.java new file mode 100644 index 0000000..cebec16 --- /dev/null +++ b/NBTUtils/src/main/io/MCUtil.java @@ -0,0 +1,531 @@ +package main.io; + +import main.Utils.BytesUtils; +import main.Utils.CompressUtils; +import main.exception.NBTException; +import main.mc.MCA; +import main.mc.MCPosInt; +import main.nbt.CompoundTag; +import main.nbt.ListTag; +import main.nbt.TagType; + +import java.io.*; +import java.util.*; +import java.util.zip.DataFormatException; + +public class MCUtil { + /** + * 解析NBT + * + * @param nbt NBT数据 + * @return 复合标签 + * @throws IOException + */ + public static CompoundTag parseNBT(byte[] nbt) throws IOException { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(nbt); + CompoundTag compoundTag = parseCompoundTag(byteArrayInputStream); + byteArrayInputStream.close(); + return compoundTag; + } + + + /** + * 计算方块列表大小所占存储的位大小 + * + * @param blockListSize 方块列表大小 + * @return 存储的位大小 + */ + public static int getMapBitSize(int blockListSize) { + blockListSize--; + int count = 0; + while (blockListSize != 0) { + count++; + blockListSize >>= 1; + } + return (count < 4 ? 4 : count); + } + + /** + * 解析复合标签 + * + * @param in 输入流 + * @return 复合标签 + * @throws IOException + */ + private static CompoundTag parseCompoundTag(InputStream in) throws IOException { + CompoundTag nbt = new CompoundTag(); + + + while (true) { + //读取标签类型 + short tagType = BytesUtils.bytes2short(in.readNBytes(1)); + //判断是否结束 + if (tagType == TagType.TAG_End) { + return nbt; + } + + //读取标签名 + String tagName = new String(in.readNBytes(BytesUtils.bytes2shortA(in.readNBytes(2)))); + + if (tagType == TagType.TAG_Byte) { + nbt.setTag(tagName, in.readNBytes(1)[0]); + } else if (tagType == TagType.TAG_Short) { + nbt.setTag(tagName, BytesUtils.bytes2shortA(in.readNBytes(2))); + } else if (tagType == TagType.TAG_Int) { + nbt.setTag(tagName, BytesUtils.bytes2intA(in.readNBytes(4))); + } else if (tagType == TagType.TAG_Long) { + nbt.setTag(tagName, BytesUtils.bytes2longA(in.readNBytes(8))); + } else if (tagType == TagType.TAG_Float) { + nbt.setTag(tagName, BytesUtils.bytes2floatA(in.readNBytes(4))); + } else if (tagType == TagType.TAG_Double) { + nbt.setTag(tagName, BytesUtils.bytes2doubleA(in.readNBytes(8))); + } else if (tagType == TagType.TAG_Byte_Array) { + nbt.setTag(tagName, in.readNBytes(BytesUtils.bytes2intA(in.readNBytes(4)))); + } else if (tagType == TagType.TAG_String) { + nbt.setTag(tagName, new String(in.readNBytes(BytesUtils.bytes2shortA(in.readNBytes(2))))); + } else if (tagType == TagType.TAG_List) { + nbt.setListTag(tagName, parseListTag(in)); + } else if (tagType == TagType.TAG_Compound) { + nbt.setCompoundTag(tagName, parseCompoundTag(in)); + } else if (tagType == TagType.TAG_Int_Array) { + int arraySize = BytesUtils.bytes2intA(in.readNBytes(4));//读取数组个数 + int[] intdata = new int[arraySize]; + for (int j = 0; j < arraySize; j++) { + intdata[j] = BytesUtils.bytes2intA(in.readNBytes(4)); + } + nbt.setTag(tagName, intdata); + } else if (tagType == TagType.TAG_Long_Array) { + int arraySize = BytesUtils.bytes2intA(in.readNBytes(4));//读取数组个数 + long[] intdata = new long[arraySize]; + for (int j = 0; j < arraySize; j++) { + intdata[j] = BytesUtils.bytes2longA(in.readNBytes(8)); + } + nbt.setTag(tagName, intdata); + } else if (tagType == TagType.TAG_End) { + return nbt; + } + } + } + + /** + * 解析列表标签 + * + * @param in 输入流 + * @return 列表标签 + * @throws IOException + */ + private static ListTag parseListTag(InputStream in) throws IOException { + + //读取列表类型 + short ListTagType = BytesUtils.bytes2short(in.readNBytes(1)); + //创建新列表 + ListTag nbtList = new ListTag(ListTagType); + //获取列表成员数 + int listSize = BytesUtils.bytes2intA(in.readNBytes(4)); + //遍历所有成员 + for (int i = 0; i < listSize; i++) { + if (ListTagType == TagType.TAG_Byte) { + nbtList.addTag(in.readNBytes(1)[0]); + } else if (ListTagType == TagType.TAG_Short) { + nbtList.addTag(BytesUtils.bytes2shortA(in.readNBytes(2))); + } else if (ListTagType == TagType.TAG_Int) { + nbtList.addTag(BytesUtils.bytes2intA(in.readNBytes(4))); + } else if (ListTagType == TagType.TAG_Long) { + nbtList.addTag(BytesUtils.bytes2longA(in.readNBytes(8))); + } else if (ListTagType == TagType.TAG_Float) { + nbtList.addTag(BytesUtils.bytes2floatA(in.readNBytes(4))); + } else if (ListTagType == TagType.TAG_Double) { + nbtList.addTag(BytesUtils.bytes2doubleA(in.readNBytes(8))); + } else if (ListTagType == TagType.TAG_Byte_Array) { + nbtList.addTag(in.readNBytes(BytesUtils.bytes2intA(in.readNBytes(4)))); + } else if (ListTagType == TagType.TAG_String) { + nbtList.addTag(new String(in.readNBytes(BytesUtils.bytes2shortA(in.readNBytes(2))))); + } else if (ListTagType == TagType.TAG_List) { + nbtList.addTag(parseListTag(in)); + } else if (ListTagType == TagType.TAG_Compound) { + nbtList.addTag(parseCompoundTag(in)); + } else if (ListTagType == TagType.TAG_Int_Array) { + int arraySize = BytesUtils.bytes2intA(in.readNBytes(4));//读取数组个数 + int[] intdata = new int[arraySize]; + for (int j = 0; j < arraySize; j++) { + intdata[j] = BytesUtils.bytes2intA(in.readNBytes(4)); + } + nbtList.addTag(intdata); + } else if (ListTagType == TagType.TAG_Long_Array) { + int arraySize = BytesUtils.bytes2intA(in.readNBytes(4));//读取数组个数 + long[] longdata = new long[arraySize]; + for (int j = 0; j < arraySize; j++) { + longdata[j] = BytesUtils.bytes2longA(in.readNBytes(8)); + } + nbtList.addTag(longdata); + } else { + throw new NBTException("ListTag处理异常"); + } + } + + return nbtList; + } + + /** + * 写标签 + * + * @param nbt 标签 + * @param out 输出流 + * @throws IOException + */ + public static void writeNBT(CompoundTag nbt, OutputStream out) throws IOException { + writeCompoundTag(nbt, out); + } + + /** + * 写复合标签 + * + * @param nbt 复合标签 + * @param out 输出流 + * @throws IOException + */ + private static void writeCompoundTag(CompoundTag nbt, OutputStream out) throws IOException { + + + Iterator> integer = nbt.entrySet().iterator(); + while (integer.hasNext()) { + Map.Entry tag = integer.next(); + short tagType = TagType.Object2TagType(tag.getValue()); + //判断标签类型 + if (tagType == -1) { + throw new NBTException("错误的标签类型"); + } + + out.write(tagType);//写标签类型 + out.write(BytesUtils.short2bytes((short) tag.getKey().getBytes().length));//标签名长度 + out.write(tag.getKey().getBytes()); + + + writeTag(tagType, tag.getValue(), out); + + } + + //out.write(TagType.TAG_End);//复合标签尾 + + + } + + /** + * 写出标签 + * + * @param tagType 标签类型 + * @param object 标签对象 + * @param out 输出流 + * @throws IOException + */ + private static void writeTag(short tagType, Object object, OutputStream out) throws IOException { + if (tagType == TagType.TAG_Byte) { + out.write((byte) object); + } else if (tagType == TagType.TAG_Short) { + out.write(BytesUtils.short2bytes((short) object)); + } else if (tagType == TagType.TAG_Int) { + out.write(BytesUtils.int2bytes((int) object)); + } else if (tagType == TagType.TAG_Long) { + out.write(BytesUtils.long2bytes((long) object)); + } else if (tagType == TagType.TAG_Float) { + out.write(BytesUtils.float2bytes((float) object)); + } else if (tagType == TagType.TAG_Double) { + out.write(BytesUtils.double2bytes((double) object)); + } else if (tagType == TagType.TAG_Byte_Array) { + out.write(BytesUtils.int2bytes(((byte[]) object).length)); + out.write((byte[]) object); + } else if (tagType == TagType.TAG_String) { + out.write(BytesUtils.short2bytes((short) ((String) object).getBytes().length)); + out.write(((String) object).getBytes()); + } else if (tagType == TagType.TAG_List) { + writeListTag((ListTag) object, out); + } else if (tagType == TagType.TAG_Compound) { + writeCompoundTag((CompoundTag) object, out); + out.write(0); + } else if (tagType == TagType.TAG_Int_Array) { + out.write(BytesUtils.int2bytes(((int[]) object).length)); + for (int ints : (int[]) object) { + out.write(BytesUtils.int2bytes(ints)); + } + } else if (tagType == TagType.TAG_Long_Array) { + out.write(BytesUtils.int2bytes(((long[]) object).length)); + for (long longs : (long[]) object) { + out.write(BytesUtils.long2bytes(longs)); + } + } + } + + /** + * 写出列表标签 + * + * @param nbt 列表标签 + * @param out 输出流 + * @throws IOException + */ + private static void writeListTag(ListTag nbt, OutputStream out) throws IOException { + short ListTagType = nbt.type; + + /*out.write(TagType.TAG_List);//写标签类型 + out.write(BytesUtils.short2bytes((short) nbt.Name.getBytes().length));//标签名长度 + out.write(nbt.Name.getBytes());*/ + + + out.write(ListTagType);//写列表类型 + out.write(BytesUtils.int2bytes(nbt.size()));//写列表大小 + for (int i = 0; i < nbt.size(); i++) { + Object object = nbt.get(i); + writeTag(ListTagType, object, out); + + } + + + } + + /** + * 读取Dat文件 + * + * @param filePath 文件路径 + * @return 读取的复合标签 + * @throws IOException + */ + public static CompoundTag readDATFile(File filePath) throws IOException { + + FileInputStream fileInputStream = new FileInputStream(filePath); + CompoundTag tag = parseNBT(CompressUtils.gzipDecompress(fileInputStream.readAllBytes())); + fileInputStream.close(); + return tag; + + } + + /** + * 写出Dat文件 + * + * @param filePath 文件路径 + * @param compoundTag 复合标签 + * @throws IOException + */ + public static void writeDATFile(File filePath, CompoundTag compoundTag) throws IOException { + FileOutputStream fileOutputStream = new FileOutputStream(filePath, false); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + writeNBT(compoundTag, byteArrayOutputStream); + fileOutputStream.write(CompressUtils.gzipCompress(byteArrayOutputStream.toByteArray())); + byteArrayOutputStream.close(); + fileOutputStream.close(); + } + + /** + * 读取MCA文件 mca坐标 + * + * @param path mca文件夹路径 + * @param mcaPos mca文件位置 + * @return mca对象 + * @throws IOException + */ + public static MCA readMCAFile(File path, MCPosInt mcaPos) throws IOException { + MCA mca = new MCA(); + mca.Pos = mcaPos.clone(); + File mcaFile = new File(path.getPath() + "\\r." + mcaPos.x + "." + mcaPos.z + ".mca"); + if (!mcaFile.isFile()) { + mcaFile.createNewFile(); + } + + RandomAccessFile randomAccessFile = new RandomAccessFile(mcaFile, "r"); + for (int i = 0; i < 1024; i++) { + //顺序读取区块索引表 + randomAccessFile.seek(i * 4); + //读取区块偏移 + int chunkPosOffset = BytesUtils.bytes2intA(BytesUtils.bytesSplicing(new byte[1], RandomAccessFileReadBytes(randomAccessFile, 3))); + if (chunkPosOffset == 0) { + //区块还未生成 + continue; + } + + //区块数据段大小 + byte chunkSectionSize = randomAccessFile.readByte(); + + + //跳转区块数据区 + randomAccessFile.seek(chunkPosOffset * 4096); + + int chunkSize = randomAccessFile.readInt(); + byte compressType = randomAccessFile.readByte(); + byte[] chunkData = new byte[chunkSize]; + randomAccessFile.read(chunkData); + if (compressType == 1) {//GZip压缩 + mca.chunksNBT[i] = MCUtil.parseNBT(CompressUtils.gzipDecompress(chunkData)); + } else if (compressType == 2) {//ZLib压缩 + try { + mca.chunksNBT[i] = (MCUtil.parseNBT(CompressUtils.zlibDecompress(chunkData))); + } catch (DataFormatException e) { + throw new NBTException("ZLIB解压失败"); + } + } else if (compressType == 3) {//未压缩 + mca.chunksNBT[i] = (MCUtil.parseNBT(chunkData)); + } else { + System.out.println("未知压缩类型:" + String.valueOf(compressType)); + //throw new NBTException("未知压缩类型:" + String.valueOf(compressType)); + } + } + randomAccessFile.close(); + + return mca; + } + + /** + * 写出MCA文件 全部修改 + * + * @param mca mca对象 + * @param path mca文件夹路径 + * @throws IOException + */ + public static void writeMCAFile(MCA mca, File path) throws IOException { + + RandomAccessFile randomAccessFile = new RandomAccessFile( + new File(path.getPath() + "\\r." + mca.Pos.x + "." + mca.Pos.z + ".mca"), "rw"); + + + randomAccessFile.setLength(0);//清除所有 + + + long time = System.currentTimeMillis();//生成新时间戳 + + //数据区写区块 + int chunksCount = 2; + for (int i = 0; i < 1024; i++) { + if (mca.chunksNBT[i] != null) { + //写区块数据 + randomAccessFile.seek(chunksCount * 4096); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + MCUtil.writeNBT(mca.chunksNBT[i], byteArrayOutputStream); + byte[] NBTData = CompressUtils.zlibCompress(byteArrayOutputStream.toByteArray()); + byteArrayOutputStream.close(); + randomAccessFile.writeInt(NBTData.length); + randomAccessFile.write(2); + randomAccessFile.write(NBTData); + + + //写区块时间戳 + randomAccessFile.seek(i * 4 + 4096); + randomAccessFile.writeInt((int) time); + + //写区块位置 + randomAccessFile.seek(i * 4); + randomAccessFile.write(BytesUtils.bytesCut(BytesUtils.int2bytes(chunksCount), 1, 0, 3)); + randomAccessFile.write((int) Math.ceil(NBTData.length / 4096.0)); + + chunksCount += (int) Math.ceil(NBTData.length / 4096.0); + + //mca.chunkLocation.put(chunksCount, (byte) Math.ceil(NBTData.length / 4096.0)); + } + } + + + randomAccessFile.seek(8191 + chunksCount * 4096); + randomAccessFile.write(0); + randomAccessFile.close(); + + } + + /** + * 写出MCA文件 局部修改 + * + * @param mca mca对象 + * @param path mca文件夹路径 + * @throws IOException + */ + public static void writeMCAFile2(MCA mca, File path) throws IOException { + RandomAccessFile randomAccessFile = new RandomAccessFile( + new File(path.getPath() + "\\r." + mca.Pos.x + "." + mca.Pos.z + ".mca"), "rw"); + if (randomAccessFile.length() == 0) + randomAccessFile.setLength(8192); + + long time = System.currentTimeMillis();//生成新时间戳 + + for (int i = 0; i < 1024; i++) { + if (mca.chunksFlag[i]) {//如果被标记 重写新数据 + //读取区块信息表 + randomAccessFile.seek(i * 4); + int chunkPosOffset; + chunkPosOffset = BytesUtils.bytes2intA(BytesUtils.bytesSplicing(new byte[1], RandomAccessFileReadBytes(randomAccessFile, 3))); + //区块数据段大小 + byte chunkSectionSize = randomAccessFile.readByte(); + + /*if (chunkPosOffset == 0) {//未找到区块 + //MCUtil.writeMCAFile(mca,path); + //throw new NBTException("保存时 未找到区块"); + chunkSectionSize = -1; + }*/ + + //生成新区块数据 + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + MCUtil.writeNBT(mca.chunksNBT[i], byteArrayOutputStream); + byte[] newChunkData = CompressUtils.zlibCompress(byteArrayOutputStream.toByteArray()); + byteArrayOutputStream.close(); + + //计算新区块数据所占段数 + byte newChunkSectionSize = (byte) Math.ceil(newChunkData.length / 4096.0); + + //判断大小 + if (chunkSectionSize < newChunkSectionSize) { + //System.out.println("遇到增容"); + //比原先大 查找最大偏移 + int maxChunkOffset = 0; + byte maxChunkSectionSize = 0; + int maxChunkIndex = 0; + for (int j = 0; j < 1024; j++) {//找到最后面位置 + //读取区块信息表 + randomAccessFile.seek(j * 4); + int maxChunkPosOffset = BytesUtils.bytes2intA(BytesUtils.bytesSplicing(new byte[1], RandomAccessFileReadBytes(randomAccessFile, 3))); + if (maxChunkPosOffset == 0) { + continue; + } + if (maxChunkOffset < maxChunkPosOffset) { + maxChunkOffset = maxChunkPosOffset; + maxChunkIndex = j; + //区块数据段大小 + maxChunkSectionSize = randomAccessFile.readByte(); + } + + } + chunkPosOffset = maxChunkOffset + maxChunkSectionSize;//设置最后面的偏移 + } + + if (chunkPosOffset == 0) { + chunkPosOffset = 2; + } + + //写区块数据 + randomAccessFile.seek(chunkPosOffset * 4096); + + randomAccessFile.writeInt(newChunkData.length); + randomAccessFile.write(2); + randomAccessFile.write(newChunkData); + + + //写区块时间戳 + randomAccessFile.seek(i * 4 + 4096); + randomAccessFile.writeInt((int) time); + + //写区块信息 + randomAccessFile.seek(i * 4); + randomAccessFile.write(BytesUtils.bytesCut(BytesUtils.int2bytes(chunkPosOffset), 1, 0, 3)); + randomAccessFile.write(newChunkSectionSize); + } + } + + } + + /** + * RandomAccessFile 读取N个字节 + * + * @param randomAccessFile RandomAccessFile对象 + * @param lenth 欲读取的字节长度 + * @return 读取的字节数组 + * @throws IOException + */ + private static byte[] RandomAccessFileReadBytes(RandomAccessFile randomAccessFile, int lenth) throws IOException { + byte[] data = new byte[lenth]; + randomAccessFile.read(data); + return data; + } +} diff --git a/NBTUtils/src/main/mc/MCA.java b/NBTUtils/src/main/mc/MCA.java new file mode 100644 index 0000000..3129a95 --- /dev/null +++ b/NBTUtils/src/main/mc/MCA.java @@ -0,0 +1,41 @@ +package main.mc; + +import main.exception.NBTException; +import main.nbt.CompoundTag; +import main.nbt.ListTag; +import main.nbt.TagType; + +import java.util.HashMap; + +public class MCA { + public MCPosInt Pos;//当前MCA所在位置 + public long[] chunkTimeStamp = new long[1024];//时间戳 + public CompoundTag[] chunksNBT = new CompoundTag[1024];//区块数据 + public boolean[] chunksFlag = new boolean[1024];//被读取或修改过的标记 + + /** + * 获取相对区块 + * + * @param chunkIndex 区块索引 + * @return 区块对象 + */ + public MCChunk getChunk(int chunkIndex) { + if (chunksNBT[chunkIndex] == null) + throw new NBTException("区块为空:" + chunkIndex); + chunksFlag[chunkIndex] = true; + return new MCChunk(chunksNBT[chunkIndex]); + } + + /** + * 复制区块标签对象 + * + * @param chunkIndex 区块索引 + * @return 新的区块标签对象 + */ + public CompoundTag cloneChunkCompoundTag(int chunkIndex) { + if (chunksNBT[chunkIndex] == null) + throw new NBTException("区块为空:" + chunkIndex); + chunksFlag[chunkIndex] = true; + return chunksNBT[chunkIndex].clone(); + } +} diff --git a/NBTUtils/src/main/mc/MCBlock.java b/NBTUtils/src/main/mc/MCBlock.java new file mode 100644 index 0000000..11f3081 --- /dev/null +++ b/NBTUtils/src/main/mc/MCBlock.java @@ -0,0 +1,69 @@ +package main.mc; + +import main.nbt.CompoundTag; + +public class MCBlock { + public CompoundTag blockState;//方块状态 + public CompoundTag blockEntitie;//方块实体 + + + public static final String FACING_UP = "up"; + public static final String FACING_DOWN = "down"; + public static final String FACING_NORTH = "north"; + public static final String FACING_SOUTH = "south"; + public static final String FACING_EAST = "east"; + public static final String FACING_WEST = "west"; + + /** + * 通过标签创建方块 + * + * @param blockState + * @param blockEntitie + */ + public MCBlock(CompoundTag blockState, CompoundTag blockEntitie) { + this.blockState = blockState; + this.blockEntitie = blockEntitie; + } + + /** + * 通过方块名创建方块 + * + * @param blockName 方块名 如 "minecraft:air" + */ + public MCBlock(String blockName) { + blockState = new CompoundTag(); + blockState.setTag("Name", blockName); + } + + /** + * 获取方块名 + * + * @return 方块名 + */ + public String getBlockName() { + return (String) blockState.getTag("Name"); + } + + + /** + * 设置方块朝向 + * + * @param blockFacing 方块朝向 请使用"FACING_xx" + */ + public void setBlockFacing(String blockFacing) { + blockState.setCompoundTag("Properties").setTag("facing", blockFacing); + } + + /** + * 复制一个新对象 + * + * @return 新方块 + */ + public MCBlock clone() { + if (blockEntitie == null) + return new MCBlock(blockState.clone(), null); + return new MCBlock(blockState.clone(), blockEntitie.clone()); + } + + +} diff --git a/NBTUtils/src/main/mc/MCBlockColors.java b/NBTUtils/src/main/mc/MCBlockColors.java new file mode 100644 index 0000000..cc6fbab --- /dev/null +++ b/NBTUtils/src/main/mc/MCBlockColors.java @@ -0,0 +1,64 @@ +package main.mc; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +public class MCBlockColors { + private ArrayList blocks = new ArrayList<>(); + + public MCBlockColors(File blockImgPath) throws IOException { + ArrayList FileList = new ArrayList<>(); + File[] files = blockImgPath.listFiles(); + for (File f : files) { + if (f.isFile()) { + FileList.add(f); + } + } + + for (File file : FileList) { + BufferedImage img = ImageIO.read(file); + BufferedImage pix = new BufferedImage(1, 1, img.getType()); + pix.createGraphics().drawImage(img, 0, 0, 1, 1, null); + blocks.add(new BlockColor(file.getName().substring(0, file.getName().length() - 4), new Color(pix.getRGB(0, 0)))); + } + + } + + + public static class BlockColor { + public String name; + public Color color; + + public BlockColor(String name, Color color) { + this.name = name; + this.color = color; + } + + public int compare(Color color2) { + return Math.abs(color2.getRed() - color.getRed()) + Math.abs(color2.getGreen() - color.getGreen()) + Math.abs(color2.getBlue() - color.getBlue()); + } + } + + public BlockColor colorFindBlock(Color color) { + if (color.getAlpha() == 0) + return new BlockColor("air", new Color(0, 0, 0, 0)); + int minIndex = 0; + int min = 255 * 255 * 255; + for (int i = 0; i < blocks.size(); i++) { + int c = blocks.get(i).compare(color); + if (min > c) { + min = c; + minIndex = i; + + } + + } + return blocks.get(minIndex); + } + + +} diff --git a/NBTUtils/src/main/mc/MCBlocksCollective.java b/NBTUtils/src/main/mc/MCBlocksCollective.java new file mode 100644 index 0000000..5de83d8 --- /dev/null +++ b/NBTUtils/src/main/mc/MCBlocksCollective.java @@ -0,0 +1,158 @@ +package main.mc; + +import main.exception.NBTException; + +import java.util.ArrayList; + +public class MCBlocksCollective { + public MCPosInt lwh;//集合的宽高 + public ArrayList bloks;//方块集合 + + public MCBlocksCollective(MCPosInt lwh) { + this.lwh = lwh; + bloks = new ArrayList(lwh.x * lwh.y * lwh.z); + for (int i = 0; i < lwh.x * lwh.y * lwh.z; i++) { + bloks.add(null); + } + } + + /** + * 设置方块 + * + * @param pos 要设置的坐标 + * @param block 方块 + */ + public void setBlock(MCPosInt pos, MCBlock block) { + checkPos(pos); + bloks.set(pos.x + pos.z * lwh.x + pos.y * lwh.x * lwh.z, block); + } + + /** + * 获取方块 + * + * @param pos 要获取的坐标 + * @return 方块 + */ + public MCBlock getBlock(MCPosInt pos) { + checkPos(pos); + return bloks.get(pos.x + pos.z * lwh.x + pos.y * lwh.x * lwh.z); + } + + /** + * 检查坐标是否超集合 + * + * @param pos 欲检查坐标 + */ + private void checkPos(MCPosInt pos) { + if ((pos.x < 0 || pos.x >= lwh.x) || (pos.y < 0 || pos.y >= lwh.y) || (pos.z < 0 || pos.z >= lwh.z)) + throw new NBTException("超出范围:" + pos.toStr()); + } + + public MCBlocksCollective clone() { + MCBlocksCollective mcBlocksCollective = new MCBlocksCollective(lwh.clone()); + mcBlocksCollective.bloks = (ArrayList) bloks.clone(); + return mcBlocksCollective; + } + + /** + * 空间翻转 + * + * @param xFlip x翻转 + * @param yFlip y翻转 + * @param zFlip z翻转 + * @return 自身 + */ + public MCBlocksCollective flip(boolean xFlip, boolean yFlip, boolean zFlip) { + MCBlocksCollective BlocksCollective = clone(); + MCPosInt.iteratePos(lwh, p -> { + MCPosInt blockPos = p.clone(); + if (xFlip) + blockPos.x = lwh.x - p.x - 1; + if (yFlip) + blockPos.y = lwh.y - p.y - 1; + if (zFlip) + blockPos.z = lwh.z - p.z - 1; + setBlock(p, BlocksCollective.getBlock(blockPos)); + }); + return this; + } + + /** + * 空间旋转 + * + * @param rotation 旋转的方向 0绕x轴 1绕y轴 2绕z轴 + * @param times 旋转的次数 整数顺时针 负数逆时针 + * @return 自身 + */ + public MCBlocksCollective rotation(int rotation, int times) { + while (times != 0) { + MCBlocksCollective BlocksCollective = clone(); + + { + int temp; + if (rotation == 0) { + temp = lwh.z; + lwh.z = lwh.y; + lwh.y = temp; + } else if (rotation == 1) { + temp = lwh.z; + lwh.z = lwh.x; + lwh.x = temp; + } else if (rotation == 2) { + temp = lwh.y; + lwh.y = lwh.x; + lwh.x = temp; + } + } + + MCPosInt.iteratePos(lwh, p -> { + MCPosInt blockPos = p.clone(); + int temp; + if (rotation == 0) { + temp = blockPos.z; + blockPos.z = blockPos.y; + blockPos.y = temp; + } else if (rotation == 1) { + temp = blockPos.z; + blockPos.z = blockPos.x; + blockPos.x = temp; + } else if (rotation == 2) { + temp = blockPos.y; + blockPos.y = blockPos.x; + blockPos.x = temp; + } + + + setBlock(p, BlocksCollective.getBlock(blockPos)); + }); + + if (rotation == 0) { + flip(false, times > 0, times < 0); + } else if (rotation == 1) { + flip(times < 0, false, times > 0); + } else if (rotation == 2) { + flip(times > 0, times < 0, false); + } + + + if (times > 0) + times--; + else if (times < 0) + times++; + + } + return this; + } + + public MCBlocksCollective replace(replace r, MCBlock replaceBlock) { + MCPosInt.iteratePos(lwh, p -> { + if (r.isReplace(getBlock(p))) + setBlock(p, replaceBlock); + }); + return this; + } + + public interface replace { + boolean isReplace(MCBlock block); + } +} \ No newline at end of file diff --git a/NBTUtils/src/main/mc/MCChunk.java b/NBTUtils/src/main/mc/MCChunk.java new file mode 100644 index 0000000..b61d3ea --- /dev/null +++ b/NBTUtils/src/main/mc/MCChunk.java @@ -0,0 +1,203 @@ +package main.mc; + +import main.Utils.BytesUtils; +import main.exception.NBTException; +import main.io.MCUtil; +import main.nbt.CompoundTag; +import main.nbt.ListTag; +import main.nbt.TagType; + +import java.util.BitSet; + +public class MCChunk { + public CompoundTag chunk;//区块对象 + + /** + * 通过区块标签创建区块对象 + * + * @param chunk + */ + public MCChunk(CompoundTag chunk) { + if (chunk == null) + throw new NBTException("区块为空"); + this.chunk = chunk; + } + + /** + * 复制区块对象 + * + * @return 新的区块对象 + */ + public MCChunk cloneChunk() { + return new MCChunk(chunk.clone()); + } + + /** + * 获取区块坐标 Y可以忽略 + * + * @return 区块坐标 + */ + public MCPosInt getChunkPos() { + return new MCPosInt((int) chunk.getTag("xPos"), (int) chunk.getTag("yPos"), (int) chunk.getTag("zPos")); + } + + /** + * 在区块里寻找子区块 + * + * @param Y 区块Y索引 + * @return 子区块标签对象 + */ + public CompoundTag getSubChunk(int Y) { + ListTag subChunks = chunk.getCompoundTag("").getListTag("sections"); + for (int i = 0; i < subChunks.size(); i++) { + CompoundTag subChunk = subChunks.getCompoundTag(i); + if ((byte) subChunk.getTag("Y") == (byte) Y) { + return subChunk; + } + } + return null; + } + + + /** + * 获取方块实体对象 + * + * @param pos 世界绝对坐标 + * @return 方块实体标签 + */ + public CompoundTag getBlockEntitie(MCPosInt pos) { + ListTag chunkBlockEntities = chunk.getCompoundTag("").getListTag("block_entities"); + if (chunkBlockEntities == null) + return null; + for (int i = 0; i < chunkBlockEntities.size(); i++) { + CompoundTag blockEntitie = chunkBlockEntities.getCompoundTag(i); + if ((int) blockEntitie.getTag("x") == pos.x) + if ((int) blockEntitie.getTag("y") == pos.y) + if ((int) blockEntitie.getTag("z") == pos.z) + return blockEntitie; + } + return null; + } + + /** + * 设置方块实体对象 + * + * @param pos 世界绝对坐标 + * @param blockEntitie 方块实体标签 + */ + public void setBlockEntitie(MCPosInt pos, CompoundTag blockEntitie) { + + ListTag chunkBlockEntities = chunk.getCompoundTag("").getListTag("block_entities"); + if (chunkBlockEntities.type == TagType.TAG_End) { + chunkBlockEntities = new ListTag(TagType.TAG_Compound); + chunk.getCompoundTag("").setListTag("block_entities", chunkBlockEntities); + } + + CompoundTag tag = blockEntitie.clone(); + + tag.setTag("x", pos.x); + tag.setTag("y", pos.y); + tag.setTag("z", pos.z); + for (int i = 0; i < chunkBlockEntities.size(); i++) { + if (chunkBlockEntities.getCompoundTag(i).getTag("x") != null) + if ((int) chunkBlockEntities.getCompoundTag(i).getTag("x") == pos.x) + if ((int) chunkBlockEntities.getCompoundTag(i).getTag("y") == pos.y) + if ((int) chunkBlockEntities.getCompoundTag(i).getTag("z") == pos.z) { + chunkBlockEntities.set(i, tag); + return; + } + + } + chunkBlockEntities.addTag(tag); + return; + } + + /** + * 获取一个方块状态 + * + * @param blockIndex 方块索引 + * @param subChunkY 子区块Y索引 + * @return 方块状态标签 + */ + public CompoundTag getBlockState(int blockIndex, int subChunkY) { + CompoundTag sunChunkBlocks = getSubChunk(subChunkY).getCompoundTag("block_states"); + + //读取区块方块索引 + ListTag blocks = sunChunkBlocks.getListTag("palette"); + + + int mapBit = MCUtil.getMapBitSize(blocks.size()); + int longIndex = blockIndex / (64 / mapBit); + int longBlockIndex = blockIndex % (64 / mapBit); + //System.out.println(sunChunkBlocks); + + if (sunChunkBlocks.getTag("data") == null) + return blocks.getCompoundTag(0); + BitSet blockData = BitSet.valueOf(new long[]{((long[]) sunChunkBlocks.getTag("data"))[longIndex]}); + + //System.out.println(BytesUtils.bytes2int(blockData.get(longBlockIndex * mapBit, (longBlockIndex + 1) * mapBit).toByteArray())); + + return blocks.getCompoundTag(BytesUtils.bytes2int(blockData.get(longBlockIndex * mapBit, (longBlockIndex + 1) * mapBit).toByteArray())); + + } + + /** + * 设置方块状态 + * + * @param blockIndex 方块索引 + * @param subChunkY 子区块Y索引 + * @param blockState 方块状态标签 + */ + public void setBlockState(int blockIndex, int subChunkY, CompoundTag blockState) { + CompoundTag subChunk = getSubChunk(subChunkY); + if (subChunk == null) { + throw new NBTException("Y越界:" + subChunkY); + } + CompoundTag sunChunkBlocks = subChunk.getCompoundTag("block_states"); + //读取区块方块索引 + ListTag blocks = sunChunkBlocks.getListTag("palette"); + int mapBitBefore = MCUtil.getMapBitSize(blocks.size()); + + //防止为空 + if (sunChunkBlocks.getTag("data") == null) + sunChunkBlocks.setTag("data", new long[1]); + + + int foundBlockIndex = -1; + //查找方块列表是否存在要添加的方块 + for (int i = 0; i < blocks.size(); i++) { + if (blocks.getCompoundTag(i).equals(blockState)) { + foundBlockIndex = i; + break; + } + } + + //未找到 添加方块列表 + if (foundBlockIndex == -1) { + blocks.addTag(blockState); + foundBlockIndex = blocks.size() - 1; + } + + int mapBitNow = MCUtil.getMapBitSize(blocks.size()); + MCSubChunk mcSubChunkBefore = new MCSubChunk((long[]) sunChunkBlocks.getTag("data"), (int) Math.ceil(4096.0 / (double) (64 / mapBitBefore)), mapBitBefore); + if (mapBitNow != mapBitBefore) {//存储位数不一样 + MCSubChunk mcSubChunkNow = new MCSubChunk((int) Math.ceil(4096.0 / (double) (64 / mapBitNow)), mapBitNow); + for (int i = 0; i < 4096; i++) { + if (blockIndex == i) {//找到欲替换方块 + mcSubChunkNow.set(i, foundBlockIndex); + } else {//转移数据 + mcSubChunkNow.set(i, mcSubChunkBefore.get(i)); + } + + } + sunChunkBlocks.setTag("data", mcSubChunkNow.getLongArray()); + } else { + mcSubChunkBefore.set(blockIndex, foundBlockIndex); + sunChunkBlocks.setTag("data", mcSubChunkBefore.getLongArray()); + } + + + } + + +} diff --git a/NBTUtils/src/main/mc/MCMap.java b/NBTUtils/src/main/mc/MCMap.java new file mode 100644 index 0000000..22a70b2 --- /dev/null +++ b/NBTUtils/src/main/mc/MCMap.java @@ -0,0 +1,145 @@ +package main.mc; + +import main.exception.NBTException; +import main.io.MCUtil; +import main.nbt.CompoundTag; +import text.lib.MyImageFilter; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class MCMap { + private CompoundTag map; + private File mapFile; + + /** + * 创建地图对象 + * + * @param mapFile 地图文件 + * @throws IOException + */ + public MCMap(File mapFile) throws IOException { + this.mapFile = mapFile; + map = MCUtil.readDATFile(mapFile); + } + + /** + * 保存地图文件 + * + * @throws IOException + */ + public void saveFile() throws IOException { + MCUtil.writeDATFile(mapFile, map); + } + + /** + * 设置地图被制图台锁定 + * + * @param locked 是否锁定 + */ + public void setLocked(boolean locked) { + map.getCompoundTag("").getCompoundTag("data").setTag("locked", (byte) (locked ? 1 : 0)); + } + + /** + * 地图在游戏世界里的位置中心 + * + * @param xz 位置中心坐标 + */ + public void setXZCenter(MCPosInt xz) { + map.getCompoundTag("").getCompoundTag("data").setTag("xCenter", xz.x); + map.getCompoundTag("").getCompoundTag("data").setTag("zCenter", xz.z); + } + + /** + * 设置位置箭头是否被显示 + * + * @param trackingPosition 是否被显示 + */ + public void setTrackingPosition(boolean trackingPosition) { + map.getCompoundTag("").getCompoundTag("data").setTag("trackingPosition", (byte) (trackingPosition ? 1 : 0)); + } + + /** + * 设置玩家位置指示器是否显示 + * + * @param unlimitedTracking 是否被显示 + */ + public void setUnlimitedTracking(boolean unlimitedTracking) { + map.getCompoundTag("").getCompoundTag("data").setTag("unlimitedTracking", (byte) (unlimitedTracking ? 1 : 0)); + } + + /** + * 设置地图缩放等级 + * + * @param scale 缩放等级(最大为4) + */ + public void setScale(int scale) { + if (scale < 0) + scale = 0; + if (scale > 4) + scale = 4; + map.getCompoundTag("").getCompoundTag("data").setTag("scale", (byte) scale); + } + + + /** + * 获取某像素颜色 + * + * @param x 水平像素位置 + * @param y 垂直像素位置 + * @return 返回获取的颜色 + */ + public Color getColor(int x, int y) { + return MCMapColors.byte2color(((byte[]) (map.getCompoundTag("").getCompoundTag("data").getTag("colors")))[x + y * 128]); + } + + /** + * 设置某像素颜色 + * + * @param x 水平像素位置 + * @param y 垂直像素位置 + * @param color 要设置的颜色 + */ + public void setColor(int x, int y, Color color) { + ((byte[]) (map.getCompoundTag("").getCompoundTag("data").getTag("colors")))[x + y * 128] = MCMapColors.color2byte(color); + } + + /** + * 设置图片 (128x128) + * + * @param inputStream 图片输入流 + * @throws IOException + */ + public void setImg(InputStream inputStream) throws IOException { + BufferedImage bufferedImage = ImageIO.read(inputStream); + for (int y = 0; y < 128; y++) { + for (int x = 0; x < 128; x++) { + setColor(x, y, new Color(bufferedImage.getRGB(x, y))); + } + } + + } + + /** + * 输出png图片 (128x128) + * + * @param outputStream + * @throws IOException + */ + public void save2img(OutputStream outputStream) throws IOException { + BufferedImage bufferedImage = new BufferedImage(128, 128, BufferedImage.TYPE_4BYTE_ABGR); + for (int y = 0; y < 128; y++) { + for (int x = 0; x < 128; x++) { + bufferedImage.setRGB(x, y, getColor(x, y).getRGB()); + } + } + ImageIO.write(bufferedImage, "png", outputStream); + } + +} diff --git a/NBTUtils/src/main/mc/MCMapColors.java b/NBTUtils/src/main/mc/MCMapColors.java new file mode 100644 index 0000000..60f2f8d --- /dev/null +++ b/NBTUtils/src/main/mc/MCMapColors.java @@ -0,0 +1,154 @@ +package main.mc; + +import main.Utils.BytesUtils; + +import java.awt.*; + +/* + * 资料: + * https://minecraft.fandom.com/zh/wiki/%E5%9C%B0%E5%9B%BE%E7%89%A9%E5%93%81%E6%A0%BC%E5%BC%8F + * */ +public class MCMapColors { + //MC地图颜色索引 + public static final Color[] mapColors = { + new Color(0, 0, 0, 0), + new Color(127, 178, 56), + new Color(247, 233, 163), + new Color(199, 199, 199), + new Color(255, 0, 0), + new Color(160, 160, 255), + new Color(167, 167, 167), + new Color(0, 124, 0), + new Color(255, 255, 255), + new Color(164, 168, 184), + new Color(151, 109, 77), + new Color(112, 112, 112), + new Color(64, 64, 255), + new Color(143, 119, 72), + new Color(255, 252, 245), + new Color(216, 127, 51), + new Color(178, 76, 216), + new Color(102, 153, 216), + new Color(229, 229, 51), + new Color(127, 204, 25), + new Color(242, 127, 165), + new Color(76, 76, 76), + new Color(153, 153, 153), + new Color(76, 127, 153), + new Color(127, 63, 178), + new Color(51, 76, 178), + new Color(102, 76, 51), + new Color(102, 127, 51), + new Color(153, 51, 51), + new Color(25, 25, 25), + new Color(250, 238, 77), + new Color(92, 219, 213), + new Color(74, 128, 255), + new Color(0, 217, 58), + new Color(129, 86, 49), + new Color(112, 2, 0), + new Color(209, 177, 161), + new Color(159, 82, 36), + new Color(149, 87, 108), + new Color(112, 108, 138), + new Color(186, 133, 36), + new Color(103, 117, 53), + new Color(160, 77, 78), + new Color(57, 41, 35), + new Color(135, 107, 98), + new Color(87, 92, 92), + new Color(122, 73, 88), + new Color(76, 62, 92), + new Color(76, 50, 35), + new Color(76, 82, 42), + new Color(142, 60, 46), + new Color(37, 22, 16), + new Color(189, 48, 49), + new Color(148, 63, 97), + new Color(92, 25, 29), + new Color(22, 126, 134), + new Color(58, 142, 140), + new Color(86, 44, 62), + new Color(20, 180, 133), + new Color(86, 86, 86), + new Color(186, 150, 126) + }; + + //相关联的地图色 乘数 + public static final float[] relatedColor = { + 0.71f, + 0.86f, + 1f, + 0.53f + }; + + /** + * 计算颜色差 + * + * @param color 比对颜色1 + * @param color2 比对颜色2 + * @return 颜色差 + */ + public static int compareColor(Color color, Color color2) { + return Math.abs(color2.getRed() - color.getRed()) + Math.abs(color2.getGreen() - color.getGreen()) + Math.abs(color2.getBlue() - color.getBlue()); + } + +/* HSB计算色差 (效果不咋滴 废弃) + public static int compareColor(Color color, Color color2) { + float[] HSB = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); + float[] HSB2 = Color.RGBtoHSB(color2.getRed(), color2.getGreen(), color2.getBlue(), null); + + HSB[0] = (Math.abs(HSB[0] - HSB2[0])) * 1000f; + HSB[1] = (Math.abs(HSB[1] - HSB2[1])) * 100f; + HSB[2] = (Math.abs(HSB[2] - HSB2[2])) * 100f; + + return (int) (HSB[0] + HSB[1] + HSB[2]); + } +*/ + + /** + * 颜色乘 + * + * @param color 欲被乘的颜色 + * @param n 被乘的小数 + * @return 乘后的颜色 + */ + public static Color multiplyColor(Color color, float n) { + return new Color((int) (color.getRed() * n), (int) (color.getGreen() * n), (int) (color.getBlue() * n)); + } + + /** + * 颜色到字节 + * + * @param color 颜色 + * @return 颜色字节 + */ + public static byte color2byte(Color color) { + byte iMin = 0; + byte jMin = 0; + int colorDeviation = 16581375; + for (byte i = 0; i < 61; i++) {//所有色 + for (byte j = 0; j < 4; j++) {//关联色 + int Deviation = compareColor(color, multiplyColor(mapColors[i], relatedColor[j])); + if (Deviation < colorDeviation) { + colorDeviation = Deviation; + iMin = i; + jMin = j; + } + } + } + return (byte) ((iMin * 4 + jMin)); + } + + /** + * 字节到颜色 + * + * @param color 颜色字节 + * @return 返回颜色 + */ + public static Color byte2color(byte color) { + return multiplyColor(mapColors[(color & 0xFF) / 4], relatedColor[(color & 0xFF) % 4]); + } + + +} diff --git a/NBTUtils/src/main/mc/MCPosInt.java b/NBTUtils/src/main/mc/MCPosInt.java new file mode 100644 index 0000000..324bcf4 --- /dev/null +++ b/NBTUtils/src/main/mc/MCPosInt.java @@ -0,0 +1,222 @@ +package main.mc; + +public class MCPosInt { + public int x, y, z; + + public MCPosInt() { + this.x = 0; + this.y = 0; + this.z = 0; + } + + /** + * 通过xyz创建坐标对象 + * + * @param x + * @param y + * @param z + */ + public MCPosInt(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * 通过xz创建坐标对象 + * + * @param x + * @param z + */ + public MCPosInt(int x, int z) { + this.x = x; + this.y = 0; + this.z = z; + } + + /** + * 坐标到字符串 + * + * @return 格式化后的坐标文本 + */ + public String toStr() { + return "x:" + x + " y:" + y + " z:" + z; + } + + /** + * 世界坐标取区块位置 + * + * @param xyz 世界绝对坐标 + * @return 区块绝对坐标 + */ + public static MCPosInt pos2chunk(MCPosInt xyz) { + return new MCPosInt(xyz.x >> 4, xyz.y >> 4, xyz.z >> 4); + } + + /** + * 世界坐标取方块索引 + * + * @param xyz 世界绝对坐标 + * @return 方块索引值 + */ + public static int pos2blockIndex(MCPosInt xyz) { + return (xyz.x & 0xF) + (xyz.y & 0xF) * 256 + (xyz.z & 0xF) * 16; + } + + /** + * 世界坐标Y到子区块Y索引 + * + * @param y 世界绝对y坐标 + * @return 子区块Y索引 + */ + public static int pos2subChunkY(int y) { + return y >> 4; + } + + /** + * 区块坐标取区块索引 + * + * @param xz 区块绝对坐标 + * @return 区块索引 + */ + public static int chunk2chunkIndex(MCPosInt xz) { + return (xz.x & 0x1F) + (xz.z & 0x1F) * 32; + } + + + /** + * 世界坐标取区块索引 + * + * @param xz 世界绝对坐标 + * @return 区块索引 + */ + public static int pos2chunkIndex(MCPosInt xz) { + MCPosInt rel = pos2chunk(xz); + return chunk2chunkIndex(rel); + } + + + /** + * 将世界坐标转成mca位置 + * + * @param xz 世界绝对坐标 + * @return mca文件位置 + */ + public static MCPosInt pos2regionPos(MCPosInt xz) { + return new MCPosInt(xz.x >> 9, xz.z >> 9); + } + + /** + * 将区块位置转成mca位置 + * + * @param xz 区块位置 + * @return mca文件位置 + */ + public static MCPosInt chunk2regionPos(MCPosInt xz) { + return new MCPosInt(xz.x >> 5, xz.z >> 5); + } + + /** + * 复制个全新的坐标对象 + * + * @return 新的坐标对象 + */ + public MCPosInt clone() { + return new MCPosInt(x, y, z); + } + + /** + * 两坐标取原点坐标 和 区域大小 + * + * @param p1 坐标1 + * @param p2 坐标2 + * @param Origin 原点坐标 + * @param LWH 长宽高 + */ + public static void getOrigin(MCPosInt p1, MCPosInt p2, MCPosInt Origin, MCPosInt LWH) { + LWH.x = Math.abs(p1.x - p2.x); + LWH.y = Math.abs(p1.y - p2.y); + LWH.z = Math.abs(p1.z - p2.z); + Origin.x = Math.min(p1.x, p2.x); + Origin.y = Math.min(p1.y, p2.y); + Origin.z = Math.min(p1.z, p2.z); + } + + /** + * 将两坐标相加 + * + * @param p1 坐标1 + * @param p2 坐标2 + * @return 相加后的坐标 + */ + public static MCPosInt additive(MCPosInt p1, MCPosInt p2) { + return new MCPosInt(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); + } + + + /** + * 和坐标比较 + * + * @param xyz 要比较的坐标 + * @return 是否一样 + */ + public boolean isEquals(MCPosInt xyz) { + return (xyz.x == x && xyz.y == y && xyz.z == z); + } + + public static void iteratePos(MCPosInt lwh, callBack cb) { + for (int y = 0; y < lwh.y; y++) { + for (int z = 0; z < lwh.z; z++) { + for (int x = 0; x < lwh.x; x++) { + cb.iterate(new MCPosInt(x, y, z)); + } + } + } + + } + + + public static float getDistance(MCPosInt p1, MCPosInt p2) { + return (float) Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2) + Math.pow(p2.z - p1.z, 2)); + } + + + /** + * 枚举两坐标直线所有坐标 + * + * @param p1 坐标1 + * @param p2 坐标2 + * @param s 细分 + * @param p 所有坐标 + */ + public static void enumLinePos(MCPosInt p1, MCPosInt p2, float s, callBack p) { + enumLinePos(p1, p2, s, (p_, d) -> { + p.iterate(p_); + }); + } + + + public static void enumLinePos(MCPosInt p1, MCPosInt p2, float s, callBack2 p) { + float k = (MCPosInt.getDistance(p1, p2)) * s; + for (float i = 0; i < k; i++) { + MCPosInt pos = new MCPosInt( + Math.round((p1.x - (p1.x - p2.x) / k * i)), + Math.round((p1.y - (p1.y - p2.y) / k * i)), + Math.round((p1.z - (p1.z - p2.z) / k * i))); + p.iterate(pos, 1f / k * i); + } + if (k == 0) { + p.iterate(p1, 1); + } + } + + + public interface callBack { + void iterate(MCPosInt p); + } + + public interface callBack2 { + void iterate(MCPosInt p, float d); + } + +} diff --git a/NBTUtils/src/main/mc/MCRegion.java b/NBTUtils/src/main/mc/MCRegion.java new file mode 100644 index 0000000..8f6a195 --- /dev/null +++ b/NBTUtils/src/main/mc/MCRegion.java @@ -0,0 +1,309 @@ +package main.mc; + +import main.exception.NBTException; +import main.io.MCUtil; +import main.nbt.CompoundTag; +import main.nbt.ListTag; + +import java.io.File; +import java.io.IOException; + + +public class MCRegion { + public static final short SAVEMODE_RewriteAll = 0;//0全部重写 稳定 需要退出存档 + public static final short SAVEMODE_RewritePart = 1;//1局部重写 速度快 只需区块不被加载就能写 + private int saveMode = SAVEMODE_RewritePart;//保存模式 默认为局部重写 + private File mcaFileDirectory;//MCA文件目录的路径 + private MCA[] mca;//当前打开的MCA + private int mcaCache = 3;//用于缓存mca文件 加快反复横跳速度 + private int mcaCacheNumber = 0;//已存在的缓存数量 + private MCChunk generateChunk = null;//自动生成区块 适合超平坦使用 + + /** + * @param mcaFileDirectory mca文件夹路径 + * @throws IOException + */ + public MCRegion(File mcaFileDirectory) throws IOException { + if (!mcaFileDirectory.isDirectory()) { + throw new NBTException("需要一个文件夹路径"); + } + this.mcaFileDirectory = mcaFileDirectory; + } + + /** + * @param mcaFileDirectory mca文件夹路径 + * @param mcaCache 缓存数量 默认为3 + * @throws IOException + */ + public MCRegion(File mcaFileDirectory, int mcaCache) throws IOException { + if (!mcaFileDirectory.isDirectory()) { + throw new NBTException("需要一个文件夹路径"); + } + this.mcaFileDirectory = mcaFileDirectory; + this.mcaCache = mcaCache; + } + + /** + * 设置保存模式 + * + * @param saveMode 保存模式 默认为局部重写 请使用"SAVEMODE_xx" + */ + public void setSaveMode(int saveMode) { + this.saveMode = saveMode; + } + + /** + * 设置生成区块 遇到空区块时会自动创建区块 为空时开摆 + * + * @param generateChunk 要自动生成的区块 + */ + public void setGenerateChunk(MCChunk generateChunk) { + this.generateChunk = generateChunk; + } + + + /** + * 保存所有MCA文件 + * + * @throws IOException + */ + public void saveMCA() throws IOException { + for (int i = 0; i < mcaCacheNumber; i++) { + saveMCA(i); + } + } + + /** + * 保存MCA文件 + * + * @param index 缓存索引 + * @throws IOException + */ + private void saveMCA(int index) throws IOException { + System.out.println("保存mca:" + mca[index].Pos.toStr()); + if (saveMode == SAVEMODE_RewriteAll) { + MCUtil.writeMCAFile(mca[index], mcaFileDirectory); + } else if (saveMode == SAVEMODE_RewritePart) { + MCUtil.writeMCAFile2(mca[index], mcaFileDirectory); + } + } + + /** + * 区块坐标查mca缓存 如果没有则读取mca + * + * @param xz 区块绝对坐标 + * @return 缓存索引 + * @throws IOException + */ + private int checkMCA(MCPosInt xz) throws IOException { + MCPosInt mcaPos = MCPosInt.chunk2regionPos(xz);//转换坐标 + int chunkIndex = MCPosInt.chunk2chunkIndex(xz); + if (mca == null) {//初始化 + mca = new MCA[mcaCache]; + mca[0] = MCUtil.readMCAFile(mcaFileDirectory, mcaPos); + mcaCacheNumber = 1; + System.out.println("载入mca:" + mcaPos.toStr()); + mca[0].chunkTimeStamp[chunkIndex] = System.currentTimeMillis();//修改时间戳 + mca[0].chunksFlag[chunkIndex] = true; + if (generateChunk != null)//自动生成区块 + if (mca[0].chunksNBT[chunkIndex] == null) { + mca[0].chunksNBT[chunkIndex] = generateChunk.cloneChunk().chunk; + } + return 0; + } + + //查找 + for (int i = 0; i < mcaCacheNumber; i++) { + if (mca[i].Pos.isEquals(mcaPos)) { + mca[i].chunkTimeStamp[chunkIndex] = System.currentTimeMillis();//修改时间戳 + mca[i].chunksFlag[chunkIndex] = true; + if (generateChunk != null)//自动生成区块 + if (mca[i].chunksNBT[chunkIndex] == null) + mca[i].chunksNBT[chunkIndex] = generateChunk.cloneChunk().chunk; + return i; + } + } + + //保存末尾 + if (mcaCacheNumber < mcaCache) { + mcaCacheNumber++; + } else { + saveMCA(mcaCache - 1); + } + + //移动 + System.arraycopy(mca, 0, mca, 1, mcaCache - 1); + + //载入 + mca[0] = MCUtil.readMCAFile(mcaFileDirectory, mcaPos); + System.out.println("载入mca:" + mcaPos.toStr()); + + mca[0].chunkTimeStamp[chunkIndex] = System.currentTimeMillis();//修改时间戳 + mca[0].chunksFlag[chunkIndex] = true; + if (generateChunk != null)//自动生成区块 + if (mca[0].chunksNBT[chunkIndex] == null) + mca[0].chunksNBT[chunkIndex] = generateChunk.cloneChunk().chunk; + return 0; + } + + + /** + * 获取方块状态 + * + * @param xyz 方块绝对坐标 + * @return 新的Tag对象 + * @throws IOException + */ + public CompoundTag getBlockState(MCPosInt xyz) throws IOException { + int index = checkMCA(MCPosInt.pos2chunk(xyz)); + MCChunk chunk = mca[index].getChunk(MCPosInt.pos2chunkIndex(xyz)); + return chunk.getBlockState(MCPosInt.pos2blockIndex(xyz), MCPosInt.pos2subChunkY(xyz.y)).clone(); + } + + /** + * 设置方块状态 + * + * @param xyz 方块绝对坐标 + * @param blockState 方块状态 + * @throws IOException + */ + public void setBlockState(MCPosInt xyz, CompoundTag blockState) throws IOException { + int index = checkMCA(MCPosInt.pos2chunk(xyz)); + MCChunk chunk = mca[index].getChunk(MCPosInt.pos2chunkIndex(xyz)); + chunk.setBlockState(MCPosInt.pos2blockIndex(xyz), MCPosInt.pos2subChunkY(xyz.y), blockState); + } + + /** + * 获取方块实体 + * + * @param xyz 方块绝对坐标 + * @return 方块实体标签 + * @throws IOException + */ + public CompoundTag getBlockEntitie(MCPosInt xyz) throws IOException { + int index = checkMCA(MCPosInt.pos2chunk(xyz)); + MCChunk chunk = mca[index].getChunk(MCPosInt.pos2chunkIndex(xyz)); + return chunk.getBlockEntitie(xyz); + } + + /** + * 设置方块实体 直接覆盖 + * + * @param xyz 方块绝对坐标 + * @param blockEntitie 方块实体标签 + * @throws IOException + */ + public void setBlockEntitie(MCPosInt xyz, CompoundTag blockEntitie) throws IOException { + int index = checkMCA(MCPosInt.pos2chunk(xyz)); + MCChunk chunk = mca[index].getChunk(MCPosInt.pos2chunkIndex(xyz)); + chunk.setBlockEntitie((xyz), blockEntitie); + } + + /** + * 获取一个区块 + * + * @param xz 区块绝对坐标 + * @return 区块对象 + * @throws IOException + */ + public MCChunk getChunk(MCPosInt xz) throws IOException { + int index = checkMCA(xz); + return mca[index].getChunk(MCPosInt.chunk2chunkIndex(xz)).cloneChunk(); + } + + /** + * 设置区块 + * + * @param xz 区块绝对坐标 + * @param chunk 要设置的区块对象 + * @throws IOException + */ + public void setChunk(MCPosInt xz, MCChunk chunk) throws IOException { + int index = checkMCA(xz); + chunk.chunk.getCompoundTag("").setTag("xPos", xz.x).setTag("zPos", xz.z); + ListTag blockEntities = chunk.chunk.getCompoundTag("").getListTag("block_entities"); + if (blockEntities != null) { + for (int i = 0; i < blockEntities.size(); i++) { + CompoundTag tag = blockEntities.getCompoundTag(i); + MCPosInt blockPos = new MCPosInt((int) tag.getTag("x"), (int) tag.getTag("z")); + blockPos.x = blockPos.x % 16 + xz.x * 16; + blockPos.z = blockPos.z % 16 + xz.z * 16; + tag.setTag("x", blockPos.x); + tag.setTag("z", blockPos.z); + } + } + mca[index].chunksNBT[MCPosInt.chunk2chunkIndex(xz)] = chunk.chunk; + } + + /** + * 获取方块 + * + * @param xyz 要获取的坐标 + * @return 获取到的方块 + * @throws IOException + */ + public MCBlock getBlock(MCPosInt xyz) throws IOException { + return new MCBlock(getBlockState(xyz), getBlockEntitie(xyz)); + } + + /** + * 设置方块 + * + * @param xyz 要设置的坐标 + * @param block 要设置的方块 + * @throws IOException + */ + public void setBlock(MCPosInt xyz, MCBlock block) throws IOException { + setBlockState(xyz, block.blockState); + if (block.blockEntitie != null) + setBlockEntitie(xyz, block.blockEntitie); + } + + /** + * 获取方块集 + * + * @param Pos1 坐标1 + * @param Pos2 坐标2 + * @return 方块集 + * @throws IOException + */ + public MCBlocksCollective getBlocksCollective(MCPosInt Pos1, MCPosInt Pos2) throws IOException { + MCPosInt origin = new MCPosInt(); + MCPosInt lwh = new MCPosInt(); + MCPosInt.getOrigin(Pos1, Pos2, origin, lwh); + lwh = MCPosInt.additive(lwh, new MCPosInt(1, 1, 1)); + MCBlocksCollective mcBlocksCollective = new MCBlocksCollective(lwh); + + MCPosInt.iteratePos(lwh, p -> { + MCPosInt blockPos = MCPosInt.additive(origin, p); + MCBlock mcBlock = null; + try { + mcBlock = getBlock(blockPos); + } catch (IOException e) { + throw new RuntimeException(e); + } + mcBlocksCollective.setBlock(p, mcBlock); + }); + + + return mcBlocksCollective; + } + + /** + * 设置方块集 + * + * @param pos 原点坐标 + * @param mcBlocksCollective 方块集 + */ + public void setBlocksCollective(MCPosInt pos, MCBlocksCollective mcBlocksCollective) { + MCPosInt.iteratePos(mcBlocksCollective.lwh, p -> { + MCBlock block = mcBlocksCollective.getBlock(p); + if (block != null) + try { + setBlock(MCPosInt.additive(pos, p), block.clone()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } +} diff --git a/NBTUtils/src/main/mc/MCSubChunk.java b/NBTUtils/src/main/mc/MCSubChunk.java new file mode 100644 index 0000000..9e16b05 --- /dev/null +++ b/NBTUtils/src/main/mc/MCSubChunk.java @@ -0,0 +1,78 @@ +package main.mc; + +import main.exception.NBTException; + +public class MCSubChunk { + private long[] longArray;//地图数据 + private int mapBit;//存储的位大小 + private long max; + + public MCSubChunk(long[] longArray, int longLength, int mapBit) { + this.longArray = new long[longLength]; + System.arraycopy(longArray, 0, this.longArray, 0, longArray.length); + this.mapBit = mapBit; + max = (int) Math.pow(2, mapBit) - 1; + } + + public MCSubChunk(long[] longArray, int mapBit) { + this.longArray = longArray; + this.mapBit = mapBit; + max = (int) Math.pow(2, mapBit) - 1; + } + + public MCSubChunk(int longLength, int mapBit) { + this.longArray = new long[longLength]; + this.mapBit = mapBit; + max = (int) Math.pow(2, mapBit) - 1; + } + + /** + * 获取Long数组 + * + * @return 地图数据 + */ + public long[] getLongArray() { + return longArray; + } + + /** + * 获取地图存储的位大小 + * @return 存储的位大小 + */ + public int getMapBit() { + return mapBit; + } + + /** + * 设置值 + * @param index 索引 + * @param vel 值 + */ + public void set(int index, long vel) { + if (vel < 0 || vel > max) + throw new NBTException("设置的值不在范围:" + vel); + int longIndex = index / (64 / mapBit); + if (longIndex >= longArray.length) + throw new NBTException("索引超出范围:" + index); + int bitIndex = index % (64 / mapBit) * mapBit; + longArray[longIndex] = longArray[longIndex] & ~(max << bitIndex); + longArray[longIndex] = (vel << bitIndex) | longArray[longIndex]; + } + + /** + * 获取值 + * @param index 索引 + * @return 值 + */ + public long get(int index) { + int longIndex = index / (64 / mapBit); + if (longIndex >= longArray.length) + throw new NBTException("索引超出范围:" + index); + int bitIndex = index % (64 / mapBit) * mapBit; + long b = (longArray[longIndex] & (max << bitIndex)) >> bitIndex; + if (b < 0) + return 0; + return b; + } + +} diff --git a/NBTUtils/src/main/nbt/CompoundTag.java b/NBTUtils/src/main/nbt/CompoundTag.java new file mode 100644 index 0000000..188b682 --- /dev/null +++ b/NBTUtils/src/main/nbt/CompoundTag.java @@ -0,0 +1,107 @@ +package main.nbt; + +import java.util.*; + +import static main.nbt.TagType.*; + +public class CompoundTag extends LinkedHashMap { + + + public CompoundTag() { + } + + @Override//克隆新的复合标签对象 + public CompoundTag clone() { + CompoundTag tag = new CompoundTag(); + Iterator> integer = super.entrySet().iterator(); + while (integer.hasNext()) { + Map.Entry entry = integer.next(); + tag.put(entry.getKey(), TagType.cloneTag(entry.getValue())); + } + return tag; + } + + public ListTag getListTag(String name) { + return (ListTag) (Object) super.get(name); + } + + /** + * 获取复合标签 + * + * @param name 标签名 + * @return 复合标签 没有返回null + */ + public CompoundTag getCompoundTag(String name) { + return (CompoundTag) super.get(name); + } + + /** + * 获取标签 + * + * @param name 标签名 + * @return 标签 没有返回null + */ + public Object getTag(String name) { + return super.get(name); + } + + /** + * 获取标签的类型 + * + * @param name 标签名 + * @return 标签类型 没有返回-1 + */ + public short getTagType(String name) { + return TagType.Object2TagType(super.get(name)); + } + + /** + * 设置列表标签 + * + * @param name 标签名 + * @param listTag 要设置的列表标签 + * @return 自身 + */ + public CompoundTag setListTag(String name, ListTag listTag) { + super.put(name, listTag); + return this; + } + + /** + * 设置复合标签 + * + * @param name 标签名 + * @param compoundTag 要设置的复合标签 + * @return 自身 + */ + public CompoundTag setCompoundTag(String name, CompoundTag compoundTag) { + super.put(name, compoundTag); + return this; + } + + /** + * 设置新的复合标签 + * + * @param name 标签名 + * @return 新的复合标签 + */ + public CompoundTag setCompoundTag(String name) { + CompoundTag newCompoundTag = new CompoundTag(); + super.put(name, newCompoundTag); + return newCompoundTag; + } + + /** + * 设置标签 + * + * @param name 标签名 + * @param data 标签对象 + * @return 自身 + */ + public CompoundTag setTag(String name, Object data) { + super.put(name, data); + return this; + } + + +} diff --git a/NBTUtils/src/main/nbt/ListTag.java b/NBTUtils/src/main/nbt/ListTag.java new file mode 100644 index 0000000..6e7f63b --- /dev/null +++ b/NBTUtils/src/main/nbt/ListTag.java @@ -0,0 +1,73 @@ +package main.nbt; + +import main.exception.NBTException; + +import java.util.ArrayList; + +public class ListTag extends ArrayList { + + public short type; + + public ListTag(short type) { + this.type = type; + } + + + @Override//克隆新的列表标签对象 + public ListTag clone() { + ListTag tag = new ListTag(type); + for (int i = 0; i < super.size(); i++) { + tag.addTag(TagType.cloneTag(super.get(i))); + } + return tag; + } + + /** + * 获取列表标签 + * + * @param index 索引 + * @return 列表标签 + */ + public ListTag getListTag(int index) { + return (ListTag) super.get(index); + } + + /** + * 获取复合标签 + * + * @param index 索引 + * @return 复合标签 + */ + public CompoundTag getCompoundTag(int index) { + return (CompoundTag) super.get(index); + } + + /** + * 获取标签对象 + * + * @param index 索引 + * @return 标签对象 + */ + public Object getTag(int index) { + return super.get(index); + } + + /** + * 增加一个标签 + * + * @param o 欲增加的标签对象 + * @return 自身 + * @throws NBTException + */ + public ListTag addTag(Object o) throws NBTException { + if (type == TagType.Object2TagType(o)) { + super.add(o); + return this; + } else { + throw new NBTException("List类型错误 不能为:" + o.getClass().getName()); + } + + } + + +} diff --git a/NBTUtils/src/main/nbt/TagType.java b/NBTUtils/src/main/nbt/TagType.java new file mode 100644 index 0000000..db1a8a0 --- /dev/null +++ b/NBTUtils/src/main/nbt/TagType.java @@ -0,0 +1,100 @@ +package main.nbt; + +import java.util.Objects; + +public final class TagType { + public static final short TAG_End = 0; + public static final short TAG_Byte = 1; + public static final short TAG_Short = 2; + public static final short TAG_Int = 3; + public static final short TAG_Long = 4; + public static final short TAG_Float = 5; + public static final short TAG_Double = 6; + public static final short TAG_Byte_Array = 7; + public static final short TAG_String = 8; + public static final short TAG_List = 9; + public static final short TAG_Compound = 10; + public static final short TAG_Int_Array = 11; + public static final short TAG_Long_Array = 12; + + /** + * 对象到标签类型 + * + * @param o 对象 + * @return 标签类型 没有返回-1 + */ + public static short Object2TagType(Object o) { + if (o == null) + return -1; + Class oc = o.getClass(); + if (oc.equals(Byte.class)) { + return TAG_Byte; + } else if (oc.equals(Short.class)) { + return TAG_Short; + } else if (oc.equals(Integer.class)) { + return TAG_Int; + } else if (oc.equals(Long.class)) { + return TAG_Long; + } else if (oc.equals(Float.class)) { + return TAG_Float; + } else if (oc.equals(Double.class)) { + return TAG_Double; + } else if (oc.equals(byte[].class)) { + return TAG_Byte_Array; + } else if (oc.equals(String.class)) { + return TAG_String; + } else if (oc.equals(ListTag.class)) { + return TAG_List; + } else if (oc.equals(CompoundTag.class)) { + return TAG_Compound; + } else if (oc.equals(int[].class)) { + return TAG_Int_Array; + } else if (oc.equals(long[].class)) { + return TAG_Long_Array; + } else { + return -1; + } + } + + /** + * 标签类型取标签类型名 + * + * @param type 标签类型 + * @return 标签类型名 + */ + public static String TagType2Str(short type) { + return switch (type) { + case TAG_End -> "TAG_End"; + case TAG_Byte -> "TAG_Byte"; + case TAG_Short -> "TAG_Short"; + case TAG_Int -> "TAG_Int"; + case TAG_Long -> "TAG_LONG"; + case TAG_Float -> "TAG_Float"; + case TAG_Double -> "TAG_Double"; + case TAG_Byte_Array -> "TAG_Byte_Array"; + case TAG_String -> "TAG_String"; + case TAG_List -> "TAG_List"; + case TAG_Compound -> "TAG_Compound"; + case TAG_Int_Array -> "TAG_Int_Array"; + case TAG_Long_Array -> "TAG_Long_Array"; + default -> null; + }; + } + + /** + * 复制标签 + * + * @param object 欲复制的标签对象 + * @return 新的标签对象 + */ + public static Object cloneTag(Object object) { + return switch (TagType.Object2TagType(object)) { + case TAG_Byte_Array -> ((byte[]) object).clone(); + case TAG_List -> ((ListTag) object).clone(); + case TAG_Compound -> ((CompoundTag) object).clone(); + case TAG_Int_Array -> ((int[]) object).clone(); + case TAG_Long_Array -> ((long[]) object).clone(); + default -> object; + }; + } +} diff --git a/NBTUtils/src/text/BlocksCollective.java b/NBTUtils/src/text/BlocksCollective.java new file mode 100644 index 0000000..75b8b13 --- /dev/null +++ b/NBTUtils/src/text/BlocksCollective.java @@ -0,0 +1,43 @@ +package text; + +import main.mc.MCBlock; +import main.mc.MCBlocksCollective; +import main.mc.MCPosInt; +import main.mc.MCRegion; + +import java.io.File; +import java.io.IOException; + +public class BlocksCollective { + public static void main(String[] args) { + try { + //创建 + MCRegion mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\测试地图\\region")); + //mcRegion.setSaveMode(MCRegion.SAVEMODE_RewriteAll); + + //获取方块集 + MCBlocksCollective blocks = mcRegion.getBlocksCollective(new MCPosInt(9404, 60, 10171), new MCPosInt(9303, 97, 10098)); + + //替换水为蓝色羊毛 + blocks.replace(b -> { + return "minecraft:water".equals(b.getBlockName()); + }, new MCBlock("minecraft:blue_wool")); + + //替换空气为屏障 + blocks.replace(b -> { + return "minecraft:air".equals(b.getBlockName()); + }, new MCBlock("minecraft:barrier")); + + //设置方块集 绕y轴顺时针旋转1次 + mcRegion.setBlocksCollective(new MCPosInt(9303, 100, 10098), blocks.rotation(1, 1)); + + //设置方块集 y轴翻转 + mcRegion.setBlocksCollective(new MCPosInt(9303, 150, 10098), blocks.flip(false, true, false)); + + //保存 + mcRegion.saveMCA(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/NBTUtils/src/text/Dj Okawari - Flower Dance___WWW.MIDISFREE.COM.mid b/NBTUtils/src/text/Dj Okawari - Flower Dance___WWW.MIDISFREE.COM.mid new file mode 100644 index 0000000..1633576 Binary files /dev/null and b/NBTUtils/src/text/Dj Okawari - Flower Dance___WWW.MIDISFREE.COM.mid differ diff --git a/NBTUtils/src/text/IMG1.png b/NBTUtils/src/text/IMG1.png new file mode 100644 index 0000000..342c7d0 Binary files /dev/null and b/NBTUtils/src/text/IMG1.png differ diff --git a/NBTUtils/src/text/IMG2.png b/NBTUtils/src/text/IMG2.png new file mode 100644 index 0000000..80015a3 Binary files /dev/null and b/NBTUtils/src/text/IMG2.png differ diff --git a/NBTUtils/src/text/IMG3.png b/NBTUtils/src/text/IMG3.png new file mode 100644 index 0000000..f226590 Binary files /dev/null and b/NBTUtils/src/text/IMG3.png differ diff --git a/NBTUtils/src/text/IMG4.png b/NBTUtils/src/text/IMG4.png new file mode 100644 index 0000000..7ef3e9c Binary files /dev/null and b/NBTUtils/src/text/IMG4.png differ diff --git a/NBTUtils/src/text/LyricWulf - His Theme.mid b/NBTUtils/src/text/LyricWulf - His Theme.mid new file mode 100644 index 0000000..829eabd Binary files /dev/null and b/NBTUtils/src/text/LyricWulf - His Theme.mid differ diff --git a/NBTUtils/src/text/LyricWulf - MEGALOVANIA.mid b/NBTUtils/src/text/LyricWulf - MEGALOVANIA.mid new file mode 100644 index 0000000..da277e1 Binary files /dev/null and b/NBTUtils/src/text/LyricWulf - MEGALOVANIA.mid differ diff --git a/NBTUtils/src/text/MCMusic.java b/NBTUtils/src/text/MCMusic.java new file mode 100644 index 0000000..b48b8bf --- /dev/null +++ b/NBTUtils/src/text/MCMusic.java @@ -0,0 +1,172 @@ +package text; + +import main.Utils.BytesUtils; +import main.mc.MCPosInt; +import main.mc.MCRegion; +import main.nbt.CompoundTag; + +import javax.sound.midi.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +public class MCMusic { + static final String[] NOTE_NAMES = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; + static MCRegion mcRegion = null; + static int z = 0; + static int x = 0; + static CompoundTag c1, c2, c3, cs, cb, cb2; + + static boolean[] playkey = new boolean[25]; + + public static void main(String[] args) { + Sequence sequence = null; + + try { + mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\BadApple\\region"), MCRegion.SAVEMODE_RewritePart); + sequence = MidiSystem.getSequence(new File("E:\\工程文件\\java\\MCNBT\\src\\text\\Touhou-Bad-Apple-train-20220115212348-nonstop2k.com.mid")); + mcRegion.setGenerateChunk(mcRegion.getChunk(new MCPosInt(4, 4))); + cb = mcRegion.getBlockState(new MCPosInt(0, -60, 0)); + cb2 = mcRegion.getBlockState(new MCPosInt(1, -60, 0)); + c1 = mcRegion.getBlockEntitie(new MCPosInt(0, -60, 0)); + c2 = mcRegion.getBlockEntitie(new MCPosInt(1, -60, 0)); + c3 = mcRegion.getBlockEntitie(new MCPosInt(2, -60, 0)); + cs = c2.clone(); + } catch (InvalidMidiDataException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + + + try { + + long tickTime = ((sequence.getMicrosecondLength() / sequence.getTickLength())) / 1000;// 1tick 的毫秒时间 + System.out.println("时长:" + sequence.getMicrosecondLength()); + System.out.println("Ticks:" + sequence.getTickLength()); + System.out.println(sequence.getMicrosecondLength() / sequence.getTickLength()); + long Ntick = 0; + int minK = 999; + z = 10000; + + //生成轨道 + for (int i = 0; i < (sequence.getMicrosecondLength() / 1000) / 50; i++) { + mcRegion.setBlockState(new MCPosInt(0, -60, z + i), cb); + mcRegion.setBlockState(new MCPosInt(1, -60, z + i), cb2); + mcRegion.setBlockState(new MCPosInt(2, -60, z + i), cb2); + mcRegion.setBlockEntitie(new MCPosInt(0, -60, z + i), c1); + mcRegion.setBlockEntitie(new MCPosInt(1, -60, z + i), c2); + mcRegion.setBlockEntitie(new MCPosInt(2, -60, z + i), c3); + } + + + for (Track track : sequence.getTracks()) { + + for (int i = 0; i < track.size(); i++) { + + MidiMessage message = track.get(i).getMessage(); + if (message instanceof ShortMessage) { + ShortMessage sm = (ShortMessage) message; + if (sm.getCommand() == ShortMessage.NOTE_ON) { + int key = sm.getData1(); + int octave = (key / 12) - 1; + int note = key % 12; + + if (minK > key) + minK = key; + playsound(key - 24 - 8 , (int) (track.get(i).getTick() * tickTime)); + //play(key - 60); + } else if (sm.getCommand() == ShortMessage.NOTE_OFF) { + int key = sm.getData1(); + int octave = (key / 12) - 1; + int note = key % 12; + + if (minK > key) + minK = key; + + //stop(key - 60); + } + } + //System.out.println(track.get(i).getTick()); + //delay(tickTime * track.get(i).getTick() - Ntick); + //Ntick = tickTime * track.get(i).getTick(); + + } + } + System.out.println("最小音调" + minK); + + delay(100); + + mcRegion.saveMCA(); + + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + static void delay(long ms) throws IOException { + for (long i = 0; i < ms / 40; i++) { + + + mcRegion.setBlockState(new MCPosInt(0, -60, z), cb); + mcRegion.setBlockState(new MCPosInt(1, -60, z), cb2); + mcRegion.setBlockState(new MCPosInt(2, -60, z), cb2); + mcRegion.setBlockEntitie(new MCPosInt(0, -60, z), c1); + mcRegion.setBlockEntitie(new MCPosInt(1, -60, z), c2); + mcRegion.setBlockEntitie(new MCPosInt(2, -60, z), c3); + /*for (int j = 0; j < playkey.length; j++) { + if (playkey[j]) + playsound(j); + }*/ + + z++; + x = 0; + } + + } + + static void playsound(int a, int time) throws IOException { + //0-24 + int posZ = time / 50; + int posX = 3; + + while (!"minecraft:air".equals(mcRegion.getBlockState(new MCPosInt(x + posX, -60, z + posZ)).getTag("Name"))) { + posX++; + } + + mcRegion.setBlockState(new MCPosInt(x + posX, -60, z + posZ), cb2); + String block = ""; + if (a < 25) { + a--; + block = "notepp:block.note_block.bit_-1"; + } else if (a < 49) { + block = "block.note_block.bit"; + } else if (a < 73) { + a++; + block = "notepp:block.note_block.bit_1"; + } + ///playsound notepp:block.note_block.bit_1 ambient @a 2.49 -59.00 -0.63 1 2 + cs.setTag("Command", "playsound " + block + " ambient @a ~ ~ ~ 1000 " + String.valueOf((float) Math.pow(2, ((-12.0 + (a % 25)) / 12.0)))); + + mcRegion.setBlockEntitie(new MCPosInt(x + posX, -60, z + posZ), cs); + } + + static void play(int a) { + if (a < 0) + a = 0; + if (a > 24) + a = 24; + playkey[a] = true; + } + + static void stop(int a) { + if (a < 0) + a = 0; + if (a > 24) + a = 24; + playkey[a] = false; + } +} diff --git a/NBTUtils/src/text/R-C.jpg b/NBTUtils/src/text/R-C.jpg new file mode 100644 index 0000000..3e3efd4 Binary files /dev/null and b/NBTUtils/src/text/R-C.jpg differ diff --git a/NBTUtils/src/text/RGB.png b/NBTUtils/src/text/RGB.png new file mode 100644 index 0000000..a06e264 Binary files /dev/null and b/NBTUtils/src/text/RGB.png differ diff --git a/NBTUtils/src/text/RGBMap.png b/NBTUtils/src/text/RGBMap.png new file mode 100644 index 0000000..73c6a54 Binary files /dev/null and b/NBTUtils/src/text/RGBMap.png differ diff --git a/NBTUtils/src/text/RGBMap2.png b/NBTUtils/src/text/RGBMap2.png new file mode 100644 index 0000000..fcbe4a0 Binary files /dev/null and b/NBTUtils/src/text/RGBMap2.png differ diff --git a/NBTUtils/src/text/Touhou-Bad-Apple-train-20220115212348-nonstop2k.com.mid b/NBTUtils/src/text/Touhou-Bad-Apple-train-20220115212348-nonstop2k.com.mid new file mode 100644 index 0000000..a059122 Binary files /dev/null and b/NBTUtils/src/text/Touhou-Bad-Apple-train-20220115212348-nonstop2k.com.mid differ diff --git a/NBTUtils/src/text/bad apple.mid b/NBTUtils/src/text/bad apple.mid new file mode 100644 index 0000000..79d700b Binary files /dev/null and b/NBTUtils/src/text/bad apple.mid differ diff --git a/NBTUtils/src/text/lib/MyImageFilter.java b/NBTUtils/src/text/lib/MyImageFilter.java new file mode 100644 index 0000000..6c2912a --- /dev/null +++ b/NBTUtils/src/text/lib/MyImageFilter.java @@ -0,0 +1,188 @@ +package text.lib; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.GlyphVector; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +public class MyImageFilter { + public BufferedImage bufferedImage; + + public MyImageFilter(Dimension size) { + bufferedImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_4BYTE_ABGR); + } + + public MyImageFilter(BufferedImage bufferedImage) { + this.bufferedImage = bufferedImage; + + } + + public MyImageFilter(String imgPath) throws IOException { + bufferedImage = ImageIO.read(new File(imgPath)); + } + + public MyImageFilter(Image image) { + bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR); + bufferedImage.createGraphics().drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null); + //this.image = image; + + } + + //缩放图片 + public MyImageFilter Zoom(Dimension size) { + BufferedImage bfimg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D graphics2D = bfimg.createGraphics(); + graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.drawImage(bufferedImage, 0, 0, size.width, size.height, null); + bfimg.getGraphics().dispose(); + + bufferedImage = bfimg; + return this; + } + + //修改画布 + public MyImageFilter ChangeCanvas(Point Pos, Dimension size) { + BufferedImage bfimg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_4BYTE_ABGR); + bfimg.getGraphics().drawImage(bufferedImage, Pos.x, Pos.y, bufferedImage.getWidth(), bufferedImage.getHeight(), null); + bfimg.getGraphics().dispose(); + bufferedImage = bfimg; + return this; + } + + + public MyImageFilter DrawIMG(MyImageFilter img, Point pos,Dimension size) { + Graphics2D g2d = bufferedImage.createGraphics(); + g2d.drawImage(img.bufferedImage, pos.x, pos.y,size.width,size.height, null); + g2d.dispose(); + return this; + } + + //改透明度 + public MyImageFilter ChangeAllAlpha(int Alpha) { + BufferedImage bfimg = new BufferedImage(bufferedImage.getWidth(null), bufferedImage.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR); + bfimg.getGraphics().drawImage(bufferedImage, 0, 0, bfimg.getWidth(), bfimg.getHeight(), null); + // + for (int y = 0; y < bfimg.getHeight(); y++) { + for (int x = 0; x < bfimg.getWidth(); x++) { + Color color = new Color(bfimg.getRGB(x, y), true); + + //System.out.println(color.getAlpha()); + if (color.getAlpha() > Alpha) { + bfimg.setRGB(x, y, new Color(color.getRed(), color.getGreen(), color.getBlue(), Alpha).getRGB()); + //bfimg.setRGB(x, y, new Color(Alpha,0,0,255).getRGB()); + } + + } + } + bfimg.getGraphics().dispose(); + bufferedImage = bfimg; + return this; + } + + + //与另一张图片进行比较 颜色相同Alpha + public MyImageFilter CompareColor(MyImageFilter img) { + BufferedImage bfimg = new BufferedImage(bufferedImage.getWidth(null), bufferedImage.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR); + bfimg.getGraphics().drawImage(bufferedImage, 0, 0, bfimg.getWidth(), bfimg.getHeight(), null); + + for (int y = 0; y < bfimg.getHeight(); y++) { + for (int x = 0; x < bfimg.getWidth(); x++) { + Color color = new Color(bfimg.getRGB(x, y), true); + Color color2 = new Color(img.bufferedImage.getRGB(x, y), true); + int r = 3; + //System.out.println(color.getAlpha()); + if (color.getRed() - color2.getRed() < r) { + if (color.getGreen() - color2.getGreen() < r) + if (color.getBlue() - color2.getBlue() < r) + bfimg.setRGB(x, y, new Color(color.getRed(), color.getGreen(), color.getBlue(), 0).getRGB()); + + } + + } + } + bfimg.getGraphics().dispose(); + bufferedImage = bfimg; + return this; + } + + + //截取图片 + public MyImageFilter Intercept(Rectangle rectangle) { + BufferedImage bfimg = new BufferedImage(rectangle.width, rectangle.height, BufferedImage.TYPE_4BYTE_ABGR); + bfimg.getGraphics().drawImage(bufferedImage, -rectangle.x, -rectangle.y, bufferedImage.getWidth(null), bufferedImage.getHeight(null), null); + bfimg.getGraphics().dispose(); + bufferedImage = bfimg; + return this; + } + + //写文字 + public MyImageFilter WriteString(Point point, String str, Font font, Color color) { + Graphics2D g2d = bufferedImage.createGraphics(); + FontMetrics fm = g2d.getFontMetrics(font); + int widthx = (bufferedImage.getWidth() - fm.stringWidth(str)) / 2; + bufferedImage = WriteString(str, font, color, new Point(widthx, (bufferedImage.getHeight()) / 2 + font.getSize() / 4)).bufferedImage; + g2d.dispose(); + return this; + } + + public MyImageFilter WriteString(String str, Font font, Color color, Point p) { + Graphics2D g2d = bufferedImage.createGraphics(); + g2d.setFont(font); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + GlyphVector v = font.createGlyphVector(g2d.getFontRenderContext(), str); + Shape shape = v.getOutline(); + g2d.translate(p.x, p.y); + g2d.setColor(color); + g2d.fill(shape); + return this; + } + + public MyImageFilter WriteString(String str, Font font, Color color, Point p, Color OutlineColor, float OutlineSize) { + Graphics2D g2d = bufferedImage.createGraphics(); + g2d.setFont(font); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + GlyphVector v = font.createGlyphVector(g2d.getFontRenderContext(), str); + Shape shape = v.getOutline(); + g2d.translate(p.x, p.y); + g2d.setColor(color); + g2d.fill(shape); + + + g2d.setColor(OutlineColor); + g2d.setStroke(new BasicStroke(OutlineSize)); + g2d.draw(shape); + g2d.dispose(); + //g2d.drawString(str, 0,0); + return this; + } + + //抠图 + public MyImageFilter ChangeBlackAlpha(int Alpha) { + BufferedImage bfimg = new BufferedImage(bufferedImage.getWidth(null), bufferedImage.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR); + bfimg.getGraphics().drawImage(bufferedImage, 0, 0, bfimg.getWidth(), bfimg.getHeight(), null); + // + for (int y = 0; y < bfimg.getHeight(); y++) { + for (int x = 0; x < bfimg.getWidth(); x++) { + Color color = new Color(bfimg.getRGB(x, y), true); + if (color.getAlpha() > Alpha) { + bfimg.setRGB(x, y, new Color(255, 255, 255, 255).getRGB()); + //bfimg.setRGB(x, y, new Color(Alpha,0,0,255).getRGB()); + } + + } + } + return this; + } + + public void WriteFile(File file) throws IOException { + ImageIO.write(bufferedImage, "png", file); + } + + public void WriteFile(String file) throws IOException { + WriteFile(new File(file)); + } + +} diff --git a/NBTUtils/src/text/lib/obj-0.3.0.jar b/NBTUtils/src/text/lib/obj-0.3.0.jar new file mode 100644 index 0000000..ce86e15 Binary files /dev/null and b/NBTUtils/src/text/lib/obj-0.3.0.jar differ diff --git a/NBTUtils/src/text/mapPrint.java b/NBTUtils/src/text/mapPrint.java new file mode 100644 index 0000000..8040363 --- /dev/null +++ b/NBTUtils/src/text/mapPrint.java @@ -0,0 +1,120 @@ +package text; + +import main.mc.MCChunk; +import main.mc.MCPosInt; +import main.mc.MCRegion; +import main.nbt.CompoundTag; +import org.w3c.dom.css.RGBColor; +import text.lib.MyImageFilter; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +public class mapPrint { + static ArrayList blocks = new ArrayList<>(); + + static class Block { + String name; + Color color; + + public Block(String name, Color color) { + this.name = name; + this.color = color; + } + + public int compare(Color color2) { + return Math.abs(color2.getRed() - color.getRed()) + Math.abs(color2.getGreen() - color.getGreen()) + Math.abs(color2.getBlue() - color.getBlue()); + } + } + + static Block colorFindBlock(Color color) { + int minIndex = 0; + int min = 255 * 255 * 255; + for (int i = 0; i < blocks.size(); i++) { + int c = blocks.get(i).compare(color); + if (min > c) { + min = c; + minIndex = i; + + } + + } + return blocks.get(minIndex); + } + + public static void main(String[] args) { + ArrayList FileList = new ArrayList<>(); + File[] files = new File("E:\\工程文件\\java\\MCNBT\\src\\text\\mc").listFiles(); + for (File f : files) { + if (f.isFile()) { + FileList.add(f); + } + } + + + try { + for (File file : FileList) { + MyImageFilter img = new MyImageFilter(file.getPath()); + img.Zoom(new Dimension(1, 1)); + blocks.add(new Block(file.getName().substring(0, file.getName().length() - 4), new Color(img.bufferedImage.getRGB(0, 0)))); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + + MyImageFilter img = null; + try { + img = new MyImageFilter("E:\\工程文件\\java\\MCNBT\\src\\text\\白猫RGB.png"); + } catch (IOException e) { + throw new RuntimeException(e); + } + + //img.Zoom(new Dimension(128, 128)); + System.out.println("开始生成"); + try { + MCRegion mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\测试地图\\region")); + + + MCChunk chunk = mcRegion.getChunk(new MCPosInt(0, 0)); + mcRegion.setGenerateChunk(chunk); + //mcRegion.setChunk(new MCPosInt(0, 6400-10), chunk); + //mcRegion.setBlockState(new MCPosInt(114514, 1, 114514), new CompoundTag().setTag("Name", "minecraft:diamond_block")); + //CompoundTag tag = mcRegion.getBlockEntitie(new MCPosInt(114514, 1, 130)); + //tag.getListTag("Items").getCompoundTag(0).getCompoundTag("tag").getListTag("Enchantments").getCompoundTag(0).setTag("lvl", 114514); + //tag.setTag("CustomName","{\"text\":\"我焯!!!\"}"); + //tag.setTag("Command","say 芜湖起飞~"); + //System.out.println(tag); + //tag.getCompoundTag("Properties").setTag("facing", "down"); + //mcRegion.setBlockState(new MCPosInt(1138, 1, 130),tag); + + for (int x = 0; x < img.bufferedImage.getWidth(); x++) { + for (int y = 0; y < img.bufferedImage.getHeight(); y++) { + + CompoundTag block; + //block = new CompoundTag().setTag("Name", "minecraft:air"); + block = new CompoundTag().setTag("Name", "minecraft:" + colorFindBlock(new Color(img.bufferedImage.getRGB(x, y))).name); + mcRegion.setBlockState(new MCPosInt(x + 114514, 0, y + 114514), block); + + } + } + + /*for (int x = 0; x < img.bufferedImage.getWidth(); x++) { + for (int y = 0; y < img.bufferedImage.getHeight(); y++) { + mcRegion.setBlockState(new MCPosInt(x, 80, y + 10240), new CompoundTag().setTag("Name", "minecraft:" + colorFindBlock(new Color(img.bufferedImage.getRGB(x, y))).name)); + + } + }*/ + + mcRegion.saveMCA(); + } catch (IOException e) { + throw new RuntimeException(e); + } + System.out.println("结束生成"); + + } + + +} diff --git a/NBTUtils/src/text/qby.mid b/NBTUtils/src/text/qby.mid new file mode 100644 index 0000000..421d445 Binary files /dev/null and b/NBTUtils/src/text/qby.mid differ diff --git a/NBTUtils/src/text/stay.mid b/NBTUtils/src/text/stay.mid new file mode 100644 index 0000000..1925324 Binary files /dev/null and b/NBTUtils/src/text/stay.mid differ diff --git a/NBTUtils/src/text/test3d.java b/NBTUtils/src/text/test3d.java new file mode 100644 index 0000000..d8b425f --- /dev/null +++ b/NBTUtils/src/text/test3d.java @@ -0,0 +1,198 @@ +package text; + +import de.javagl.obj.*; +import main.mc.*; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +public class test3d { + public static void main(String[] args) { + try { + + MCRegion mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\OBJ\\region"), 10); + mcRegion.setGenerateChunk(mcRegion.getChunk(new MCPosInt(0, 0))); + + File objFile = new File("E:\\桌面\\测试模型\\蒙德\\蒙德城1.1\\蒙德城.obj"); + + + MCBlockColors mcBlockColors = new MCBlockColors(new File("E:\\工程文件\\java\\NBTUtils\\src\\text\\mc")); + + + /*//测试画板 + MyImageFilter myImageFilter = new MyImageFilter(new Dimension(200, 200));*/ + + + Obj originalObj = ObjReader.read(new FileReader(objFile)); + + //MCBlocksCollective blocks = new MCBlocksCollective(new MCPosInt(1024, 370, 1024)); + + //三角化 + Obj obj = ObjUtils.convertToRenderable(originalObj); + //Obj obj = originalObj; + //读取材质文件 + List allMtls = + MtlReader.read(new FileReader( + objFile.getParent() + "\\" + obj.getMtlFileNames().get(0).substring(2) + )); + + //按材质分割obj + Map materialGroups = + ObjSplitting.splitByMaterialGroups(obj); + //System.out.println("getNumGroups:" + obj.getGroup(0).); + for (Map.Entry entry : materialGroups.entrySet()) { + String materialName = entry.getKey(); + Obj materialGroup = entry.getValue(); + System.out.println("材质名:" + materialName); + Mtl material = findMtlForName(allMtls, materialName); + File materialFile = new File(objFile.getParent() + "\\" + material.getMapKd()); + System.out.println("纹理图片:" + materialFile); + System.out.println("面数:" + materialGroup.getNumFaces()); + + BufferedImage materialImg = null; + + if (materialFile.isFile()) { + //读取纹理文件 + materialImg = ImageIO.read(materialFile); + } else { + System.out.println("未找到纹理文件"); + } + //枚举所有面 + for (int i = 0; i < materialGroup.getNumFaces(); i++) { + //System.out.println("面索引:" + i); + ObjFace face = materialGroup.getFace(i);//获取面 + FloatTuple[] faceVertex = new FloatTuple[3]; + FloatTuple[] faceUV = new FloatTuple[3]; + + //枚举所有顶点 + for (int j = 0; j < 3; j++) { + faceVertex[j] = materialGroup.getVertex(face.getVertexIndex(j));//获取顶点坐标 + if (materialImg != null) { + faceUV[j] = materialGroup.getTexCoord(face.getTexCoordIndex(j));//获取uv坐标 + } + } + + + //顶点边 + List side1 = new ArrayList<>();//取边1 + List side2 = new ArrayList<>();//取边2 + MCPosInt.enumLinePos(f2m(faceVertex[0]), f2m(faceVertex[1]), 10f, p -> { + side1.add(p); + }); + MCPosInt.enumLinePos(f2m(faceVertex[0]), f2m(faceVertex[2]), 10f, p -> { + side2.add(p); + }); + + if (side1.size() == 0 || side2.size() == 0) { + side1.add(f2m(faceVertex[0])); + side2.add(f2m(faceVertex[0])); + } + + + for (int j = 0; j < side1.size(); j++) { + + + int finalJ = j; + BufferedImage finalMaterialImg = materialImg; + + MCPosInt.enumLinePos(side1.get(j), + side2.get((int) (((float) side2.size() / side1.size() * j))), + 4f, + (p, d) -> { + + + //myImageFilter.bufferedImage.setRGB(p.x, p.z, color.getRGB()); + Color color; + if (finalMaterialImg != null) { + FloatTuple UV1 = in2Pos((faceUV[0]), (faceUV[1]), 1f / side1.size() * finalJ); + FloatTuple UV2 = in2Pos((faceUV[0]), (faceUV[2]), 1f / side1.size() * finalJ); + + FloatTuple UV3 = in2Pos(UV1, UV2, d); + + + int UVx = Math.round(finalMaterialImg.getWidth() * UV3.getX()); + int UVy = Math.round(finalMaterialImg.getHeight() * (1f - UV3.getY())); + if ((UVx < 0 || UVx >= finalMaterialImg.getWidth()) || (UVy < 0 || UVy >= finalMaterialImg.getHeight())) { + + //color = Color.CYAN; + //System.out.println("纹理过界 原大小>W:" + finalMaterialImg.getWidth() + " H:" + finalMaterialImg.getHeight()); + //System.out.println("UVxy> x:" + UVx + " y:" + UVy); + + if (UVx < 0) + UVx = UVx + finalMaterialImg.getWidth() * (-UVx / finalMaterialImg.getWidth() + 1); + if (UVy < 0) + UVy = UVy + finalMaterialImg.getHeight() * (-UVy / finalMaterialImg.getHeight() + 1); + + UVx = UVx % finalMaterialImg.getWidth(); + UVy = UVy % finalMaterialImg.getHeight(); + //System.out.println("w:" + finalMaterialImg.getWidth() + " h:" + finalMaterialImg.getHeight()); + //System.out.println("UVx:" + UVx + " UVy:" + UVy); + + + color = new Color(finalMaterialImg.getRGB( + UVx, + UVy)); + + + } else { + color = new Color(finalMaterialImg.getRGB( + UVx, + UVy)); + } + + } else { + color = Color.MAGENTA; + } + + MCBlockColors.BlockColor blockColor = mcBlockColors.colorFindBlock(color); + + try { + //System.out.println(MCPosInt.additive(p,new MCPosInt(10240,-50,0)).toStr()); + mcRegion.setBlock(MCPosInt.additive(p, new MCPosInt(10240, -59, 0)), new MCBlock("minecraft:" + blockColor.name)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + } + } + //mcRegion.setBlocksCollective(new MCPosInt(10434, -60, 10434), blocks); + mcRegion.saveMCA(); + + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + //材质名找材质 + private static Mtl findMtlForName(Iterable mtls, String name) { + for (Mtl mtl : mtls) { + if (mtl.getName().equals(name)) { + return mtl; + } + } + return null; + } + + public static FloatTuple in2Pos(FloatTuple p1, FloatTuple p2, float i) { + return FloatTuples.create(((p1.getX() - (p1.getX() - p2.getX()) * i)), + ((p1.getY() - (p1.getY() - p2.getY()) * i)), + ((p1.getZ() - (p1.getZ() - p2.getZ()) * i))); + } + + static MCPosInt f2m(FloatTuple floatTuple) { + return new MCPosInt(Math.round(floatTuple.getX()), Math.round(floatTuple.getY()), Math.round(floatTuple.getZ())); + } + + +} diff --git a/NBTUtils/src/text/text.java b/NBTUtils/src/text/text.java new file mode 100644 index 0000000..cbb8c58 --- /dev/null +++ b/NBTUtils/src/text/text.java @@ -0,0 +1,301 @@ +package text; + + +import main.mc.*; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +public class text { + public static void main(String[] args) { + + + try { + + BufferedImage Image = ImageIO.read(new File("E:\\工程文件\\java\\NBTUtils\\src\\text\\白猫RGB.png")); + + + BufferedImage bufferedImage = new BufferedImage(250, 250, BufferedImage.TYPE_4BYTE_ABGR); + for (int y = 0; y < 250; y++) { + for (int x = 0; x < 250; x++) { + bufferedImage.setRGB(x, y, MCMapColors.byte2color(MCMapColors.color2byte(new Color(Image.getRGB(x*4, y*4)))).getRGB()); + } + } + ImageIO.write(bufferedImage, "png", new File("E:\\工程文件\\java\\NBTUtils\\src\\text\\白猫RGB预览.png")); + + + + + + + + /*{ + MCMap mcMap = new MCMap(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\地图画测试\\data\\map_0.dat")); + mcMap.setImg(new File("E:\\工程文件\\java\\MCNBT\\src\\text\\RGB.png")); + mcMap.saveFile(); + } + { + for (int i = 0; i < 4; i++) { + MCMap mcMap = new MCMap(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\地图画测试\\data\\map_" + (i + 1) + ".dat")); + mcMap.setImg(new File("E:\\工程文件\\java\\MCNBT\\src\\text\\IMG" + (i + 1) + ".png")); + mcMap.saveFile(); + } + }*/ + /* { + MCMap mcMap = new MCMap(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\data\\map_17.dat")); + mcMap.save2img(new File("E:\\工程文件\\java\\MCNBT\\src\\text\\保存的地图.png")); + }*/ + + } catch (IOException e) { + throw new RuntimeException(e); + } + + /*try { + MCRegion mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\BadApple\\region")); + CompoundTag block = mcRegion.getBlockState(new MCPosInt(2 ,-60, -1)); + System.out.println(block); + } catch (IOException e) { + throw new RuntimeException(e); + }*/ + //octave=mid low high {Properties={note=0, octave=low, powered=false, instrument=harp_l}, Name=minecraft:note_block} + + //System.out.println(MCPosInt.pos2subChunkIndex(new MCPosInt(100,-10,100)).toStr()); + //System.out.println(MCPosInt.pos2chunk2(new MCPosInt(10000,100,100)).toStr()); + + + //System.out.println(1024>> 5); + //System.out.println(9999999>> 5); + //System.out.println(MCPosInt.blockToChunk(-114514)); + /*System.out.println(MCPosInt.chunk2relativelyChunk(new MCPosInt(0,0)).toStr()); + + try { + MCRegion mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\测试地图\\region")); + + CompoundTag c = mcRegion.getBlockEntitie(new MCPosInt(4, -60, 0)); + System.out.println(c); + + mcRegion.setBlockEntitie(new MCPosInt(10, -60, -21), c); + + + } catch (IOException e) { + throw new RuntimeException(e); + }*/ + + + //System.out.println(MCUtil.getMapBitSize(33)); + //MCSubChunk mcSubChunk = new MCSubChunk(1, 4); + //mcSubChunk.set(12, 5); + + //System.out.println(mcSubChunk.get(12)); + //mcSubChunk.set(17, 0xf); + //System.out.println(Long.toBinaryString(mcSubChunk.getLongArray()[0])); + //System.out.println(BitsUtils.bits2fStr(BitSet.valueOf(mcSubChunk.getLongArray()))); + /* long a = 55; + a = a << 10; + System.out.println(Long.toBinaryString(a)); + System.out.println(Long.toBinaryString(a & 0xF)); + System.out.println(a & 0xF);*/ + //System.out.println(BitsUtils.bits2fStr(BitSet.valueOf(a.toLongArray()))); + //System.out.println(MCUtil.getMapBitSize(16 * 16)); + /*MCRegion mcRegion = new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\测试地图\\region")); + mcRegion.setBlockState(new MCPosInt(-30, -60, 9), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(-32, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(-512, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(-513, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(32, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(-511, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(-512, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.setBlockState(new MCPosInt(-513, -60, 0), new CompoundTag().setTag("Name", "minecraft:red_wool")); + mcRegion.upDate();*/ + //MCA mca = MCUtil.readMCAFile(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界 (1)\\region\\r.19.19.mca")); + /* MCChunk chunk = mca.getChunk(new MCPosInt(17, 17)); + System.out.println(chunk.getBlockState(new MCPosInt(0, 100, 0))); + + int count = 0; + for (int j = 0; j < 50; j++) { + count++; + for (int i = 0; i < 16; i++) { + chunk.setBlockState(new CompoundTag().setTag("Name", "minecraft:diamond_block"), new MCPosInt(i, 80 + count, 0)); + } + + count++; + for (int i = 0; i < 16; i++) { + chunk.setBlockState(new CompoundTag().setTag("Name", "minecraft:gold_block"), new MCPosInt(i, 80 + count, 15)); + } + count++; + for (int i = 0; i < 16; i++) { + chunk.setBlockState(new CompoundTag().setTag("Name", "minecraft:glass"), new MCPosInt(15, 80 + count, i)); + } + + count++; + for (int i = 0; i < 16; i++) { + chunk.setBlockState(new CompoundTag().setTag("Name", "minecraft:iron_block"), new MCPosInt(0, 80 + count, i)); + } + + } + + + MCUtil.writeMCAFile(mca, new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界aaa\\region\\r.19.19.mca"));*/ + + //MCRegion mcRegion=new MCRegion(new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\region")); + + + +/* + try { + MCPosInt localPos = MCChunkUtils.chunk2local(MCChunkUtils.pos2chunk(new MCPosInt(10000, 10000))); + MCA mca = MCUtil.readMCAFile(new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\region\\r." + localPos.x + "." + localPos.z + ".mca")); + CompoundTag chunk = MCChunkUtils.findChunk(mca.chunksNBT, new MCPosInt(10000 / 16, -4, 10000 / 16)); + + + //CompoundTag az = new CompoundTag(); + //az.setCompoundTag("",new CompoundTag().setListTag("az", mca.chunksNBT)); + + // MCUtil.writeNBT(az, new FileOutputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\aaaa.dat")); + + //System.out.println(mca.chunksNBT); + System.out.println(MCChunkUtils.subChunkFindBlockStates(MCChunkUtils.findSubChunk(chunk, -4), new MCPosInt(0, 6, 0)).getTag("Name")); + System.out.println(MCChunkUtils.subChunkFindBlockStates(MCChunkUtils.findSubChunk(chunk, -4), new MCPosInt(0, 6, 0))); + + System.out.println("修改前---"); + System.out.println(MCChunkUtils.chunkFindBlockEntities(chunk, new MCPosInt(10000, -59, 10000))); + CompoundTag block = MCChunkUtils.chunkFindBlockEntities(chunk, new MCPosInt(10000, -59, 10000)).getListTag("Items").getCompoundTag(0); + block.setTag("id", "minecraft:diamond"); + block.setCompoundTag("tag").setCompoundTag("display").setTag("Name","{\"text\":\"ZEDO\",\"color\":\"green\"}"); + System.out.println("修改后---"); + System.out.println(MCChunkUtils.chunkFindBlockEntities(chunk, new MCPosInt(10000, -59, 10000))); + + MCUtil.writeMCAFile(mca, new File("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\region\\r." + localPos.x + "." + localPos.z + ".mca")); + + } catch (IOException e) { + throw new RuntimeException(e); + + } +*/ + + + /*try { + + System.out.println(MCUtil.readDATFile(new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat"))); + MCUtil.writeNBT(MCUtil.readDATFile(new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat")),new FileOutputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat.dat")); + + } catch (IOException e) { + throw new RuntimeException(e); + }*/ + + //System.out.println(BytesUtils.bytes2longA(new byte[]{ (byte) 0x64,(byte) 0x00,(byte) 0x00 ,(byte) 0xFF,(byte) 0x00 , (byte) 0x00, (byte) 0x00,(byte) 0xAA})); + //System.out.println(BytesUtils.bytes2longA((new byte[]{ (byte) 0xAA,(byte) 0x00,(byte) 0x00 ,(byte) 0x00 ,(byte) 0xFF,(byte) 0x00, (byte) 0x00,(byte) 0x64}))); + //System.out.println(BytesUtils.bytes2fStr(BytesUtils.long2bytes(-6196953082983612316l))); + //System.out.println("bit:" + BitsUtils.bits2fStr(BytesUtils.bytes2bitsA(new byte[]{(byte) 0x23,(byte) 0xa3,(byte) 0x89}))); + //System.out.println("bit:" + BitsUtils.bits2fStr(BytesUtils.bytes2bitsA(BytesUtils.bytes2bytesA(new byte[]{(byte) 0x23,(byte) 0xa3,(byte) 0x89})))); + + + //ByteArrayOutputStream out =new ByteArrayOutputStream(); + //{={fml=}} +/* CompoundTag nbt = new CompoundTag(""); + + CompoundTag nbt2 = new CompoundTag("复合标签"); + nbt2.addTag(new Tag("short", (short) 2233)); + nbt2.addTag(new Tag("int", -114514)); + nbt2.addTag(new Tag("long", 114514L)); + nbt2.addTag(new Tag("float", 2.33f)); + nbt2.addTag(new Tag("double", 223344.5566)); + nbt2.addTag(new Tag("bytes", new byte[]{0, 1, 2, -5})); + nbt2.addTag(new Tag("ints", new int[]{11, 22, 33, 44})); + nbt2.addTag(new Tag("longs", new long[]{11, 22, 33, 44})); + nbt2.addTag(new Tag("string", "字符串测试")); + + + ListTag listTag = new ListTag("longlist", TagType.TAG_Long); + listTag.addTag(8848L); + listTag.addTag(-6666L); + + + ListTag listTag2 = new ListTag("复合list", TagType.TAG_Compound); + listTag2.addTag(new CompoundTag(null).addTag(new Tag("tag1","字符串"))); + listTag2.addTag(new CompoundTag(null).addTag(new Tag("tag2","好耶"))); + + nbt.addCompoundTag(nbt2); + nbt.addCompoundTag(new CompoundTag("套娃测试1").addCompoundTag(new CompoundTag("套娃测试2").addCompoundTag(new CompoundTag("套娃测试3")))); + nbt.addListTag(listTag); + nbt.addListTag(listTag2); + + System.out.println(nbt); + try { + MCUtil.writeNBT(nbt, new FileOutputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\test.dat")); + } catch (IOException e) { + throw new RuntimeException(e); + }*/ + //System.out.println(BytesUtils.bytes2fStr(out.toByteArray())); + + + //ByteArrayInputStream in = new ByteArrayInputStream(); + + + /* try { + inflaterOutputStream.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + }*/ + + /*try { + System.out.println(BytesUtils.bytes2fStr(ZLibUtils.decompress(new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\map.zlib")))); + new FileOutputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\map.zlib.dat").write(ZLibUtils.decompress(new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\map.zlib"))); + } catch (Exception e) { + throw new RuntimeException(e); + }*/ + + + /*int x, z; + x = 1; + z = 1; + System.out.println(4 * ((x & 31) + (z & 31) * 32));*/ + /*try { + FileInputStream fileInputStream = new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat"); + GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream); + + byte[] data = ((InputStream)gzipInputStream).readAllBytes(); + System.out.println(data.length); + + FileOutputStream fileOutputStream =new FileOutputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat.gzip"); + fileOutputStream.write(data); + } catch (Exception e) { + throw new RuntimeException(e); + }*/ + + +/* CompoundTag compoundTag; + try { + compoundTag = MCUtil.readDATFile(new FileInputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat")); + } catch (IOException e) { + throw new RuntimeException(e); + } + //System.out.println(compoundTag.getNBT("a").getNBT("aa")); + System.out.println(compoundTag); + //compoundTag.getCompoundTag("").getCompoundTag("Data").addTag(new Tag("LevelName","芜湖")); + System.out.println("世界名:" + compoundTag.getCompoundTag("").getCompoundTag("Data").getTag("LevelName").getData()); + System.out.println("玩家坐标:" + compoundTag.getCompoundTag("").getCompoundTag("Data").getCompoundTag("Player").getListTag("Pos")); + + try { + MCUtil.writeNBT(compoundTag,new FileOutputStream("E:\\MineCraft文件\\1.18.2\\.minecraft\\saves\\新的世界\\level.dat.dat")); + } catch (IOException e) { + throw new RuntimeException(e); + }*/ + /* + ListTag listTag = compoundTag.getCompoundTag("").getCompoundTag("Data").getCompoundTag("Player").getListTag("Inventory"); + for (int i = 0; i < listTag.size(); i++) { + System.out.println(listTag.getCompoundTag(i).getTagData("Slot")); + }*/ + + //System.out.println(((int[]) compoundTag.getCompoundTag("").getCompoundTag("Data").getCompoundTag("Player").getTag("UUID").getTag())[0]); + //CompoundTag compoundTag = new CompoundTag("az"); + //compoundTag.addCompoundTag(new CompoundTag("ddd")); + + //System.out.println(); + + + } +} diff --git a/NBTUtils/src/text/undertale - his theme.mid b/NBTUtils/src/text/undertale - his theme.mid new file mode 100644 index 0000000..3d5f614 Binary files /dev/null and b/NBTUtils/src/text/undertale - his theme.mid differ diff --git a/NBTUtils/src/text/保存的地图.png b/NBTUtils/src/text/保存的地图.png new file mode 100644 index 0000000..b91517d Binary files /dev/null and b/NBTUtils/src/text/保存的地图.png differ diff --git a/NBTUtils/src/text/爷的头像.png b/NBTUtils/src/text/爷的头像.png new file mode 100644 index 0000000..d7b8c99 Binary files /dev/null and b/NBTUtils/src/text/爷的头像.png differ diff --git a/NBTUtils/src/text/白猫RGB.png b/NBTUtils/src/text/白猫RGB.png new file mode 100644 index 0000000..46a1a57 Binary files /dev/null and b/NBTUtils/src/text/白猫RGB.png differ diff --git a/NBTUtils/src/text/白猫RGB预览.png b/NBTUtils/src/text/白猫RGB预览.png new file mode 100644 index 0000000..5dafb0d Binary files /dev/null and b/NBTUtils/src/text/白猫RGB预览.png differ