/// <summary> /// Create a chunk from a decompressed NBT byte buffer. /// </summary> /// <param name="bytes"></param> public Chunk(byte[] bytes) { int position = 0; Tag = NBTJ.Parse(bytes, ref position)[0]; BlockMap = GetBlockMap(); File.WriteAllText("nbtout.txt", Tag.ToString()); }
/// <summary> /// Parse the block pallete from a pallete NBTTag /// </summary> /// <param name="palleteTag">The tag to parse fom</param> /// <returns>A list of the blocks in the pallete as strings</returns> private string[] ParsePallete(NBTTag palleteTag) { List <string> pallete = new List <string>(); foreach (NBTTag item in (List <NBTTag>)palleteTag.Payload) { NBTTag stringTag = ((List <NBTTag>)item.Payload)[0]; pallete.Add((string)stringTag.Payload); } return(pallete.ToArray()); }
public static NBTTag ProcessPayload(int tagID, byte[] bytes, ref int position, string name = "") { NBTTag tag; switch (tagID) { case 1: // Signed Byte { tag = new NBTTag(name, (sbyte)bytes[position]); position += 1; } break; case 2: // Signed Short { byte[] numBytes = bytes.Skip(position).Take(2).Reverse().ToArray(); tag = new NBTTag(name, BitConverter.ToInt16(numBytes, 0)); position += 2; } break; case 3: // Signed Int { byte[] numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); tag = new NBTTag(name, BitConverter.ToInt32(numBytes, 0)); position += 4; } break; case 4: // Signed Long { byte[] numBytes = bytes.Skip(position).Take(8).Reverse().ToArray(); tag = new NBTTag(name, BitConverter.ToInt64(numBytes, 0)); position += 8; } break; case 5: // Signed Float { byte[] numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); tag = new NBTTag(name, BitConverter.ToSingle(numBytes, 0)); position += 4; } break; case 6: // Signed Double { byte[] numBytes = bytes.Skip(position).Take(8).Reverse().ToArray(); tag = new NBTTag(name, BitConverter.ToDouble(numBytes, 0)); position += 8; } break; case 7: // Array of Signed Bytes { byte[] numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); int arrayLength = BitConverter.ToInt32(numBytes, 0); position += 4; sbyte[] sbyteArray = bytes.Skip(position).Take(arrayLength).Select(i => (sbyte)i).ToArray(); position += arrayLength; tag = new NBTTag(name, sbyteArray); } break; case 8: // String { byte[] numBytes = bytes.Skip(position).Take(2).Reverse().ToArray(); int stringLength = BitConverter.ToInt16(numBytes, 0); position += 2; string str = Encoding.UTF8.GetString(bytes, position, stringLength); position += stringLength; tag = new NBTTag(name, str); } break; case 9: // Tag List -> recursively solve, same way as compound tags { byte childID = bytes[position]; position += 1; byte[] numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); int numberOfTags = BitConverter.ToInt32(numBytes, 0); position += 4; List <NBTTag> children = new List <NBTTag>(); for (int i = 0; i < numberOfTags; i++) { children.Add(ProcessPayload(childID, bytes, ref position)); } tag = new NBTTag(name, children); } break; case 10: // Compund Tag { List <NBTTag> children = Parse(bytes, ref position); tag = new NBTTag(name, children); } break; case 11: // Int array { byte[] numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); int arrayLength = BitConverter.ToInt32(numBytes, 0); position += 4; int[] array = new int[arrayLength]; for (int i = 0; i < arrayLength; i++) { numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); array[i] = BitConverter.ToInt32(numBytes, 0); position += 4; } tag = new NBTTag(name, array); } break; case 12: // Long Array { byte[] numBytes = bytes.Skip(position).Take(4).Reverse().ToArray(); int arrayLength = BitConverter.ToInt32(numBytes, 0); position += 4; if (name == "BlockStates") { byte[] array = bytes.Skip(position).Take(arrayLength * 8).ToArray(); tag = new NBTTag(name, array); position += arrayLength * 8; } else { long[] array = new long[arrayLength]; for (int i = 0; i < arrayLength; i++) { numBytes = bytes.Skip(position).Take(8).Reverse().ToArray(); array[i] = BitConverter.ToInt64(numBytes, 0); position += 8; } tag = new NBTTag(name, array); } } break; default: { tag = new NBTTag(string.Format("{0} - not Recognised", tagID), null); } break; } return(tag); }
/// <summary> /// Generate the blockmap - must be called aftter the Tag is set /// </summary> /// <returns>The generated blockmap</returns> public string[,] GetBlockMap() { if (Tag is null) { throw new NullReferenceException("The Tag of the chunk is not set!"); } // get sections as a stack ordered by Y index Stack <NBTTag> sections = new Stack <NBTTag>(((List <NBTTag>)Tag.Search("Sections").Payload) .OrderBy(element => element.Search("Y").Payload) ); // get the highest non-air section in the chunk // generate queue of all x,z coordinate paris in the chunk List <Tuple <int, int> > unresolved = new List <Tuple <int, int> >(); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { unresolved.Add(new Tuple <int, int>(x, z)); } } string[,] blockMap = new string[16, 16]; // repeat this until all blocks are resolved or run out of sections. do { if (sections.Count <= 0) { break; } // shallow copy unresolved coords to coordinate queue then reset unresolved queue List <Tuple <int, int> > coords = new List <Tuple <int, int> >(unresolved); unresolved = new List <Tuple <int, int> >(); // Get next highest non-empty section of chunk NBTTag topSection = sections.Pop(); while (((List <NBTTag>)topSection.Search("Palette").Payload).Count <= 1) { topSection = sections.Pop(); } Console.WriteLine(topSection); // Parse the pallete string[] pallete = ParsePallete(topSection.Search("Palette")); // get bytes containing block states and calculate bits per block byte[] blockStateBytes = (byte[])topSection.Search("BlockStates").Payload; int bitsPerBlock = NextPowerOfTwo(pallete.Length); //Console.WriteLine("Bits per block: {0}, {1}", bitsPerBlock, pallete.Length); for (int i = 0; i < coords.Count; i++) { // for each x,z pair, find highest block in the section that isn't air int y = 15; string block = ""; do { if (y < 0) //if all block in column are empty, add to unersolved queue { unresolved.Add(coords[i]); break; } block = pallete[NumFromBytes(bitsPerBlock, y * 256 + coords[i].Item2 * 16 + coords[i].Item1, blockStateBytes)]; y--; } while (block == "minecraft:air"); // write block to the 2d array. if empty, air is written to it. blockMap[coords[i].Item1, coords[i].Item2] = block; } Console.WriteLine("Unresolved: {0}", unresolved.Count); }while (unresolved.Count > 0); return(blockMap); }