diff --git a/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java
index fb20164b..6cea77fe 100644
--- a/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java
+++ b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java
@@ -21,6 +21,7 @@ import baritone.api.schematic.IStaticSchematic;
import baritone.api.schematic.format.ISchematicFormat;
import baritone.utils.schematic.format.defaults.MCEditSchematic;
import baritone.utils.schematic.format.defaults.SpongeSchematic;
+import baritone.utils.schematic.format.defaults.LitematicaSchematic;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.io.FilenameUtils;
@@ -65,6 +66,16 @@ public enum DefaultSchematicFormats implements ISchematicFormat {
throw new UnsupportedOperationException("Unsupported Version of a Sponge Schematic");
}
}
+ },
+
+ /**
+ * The Litematica schematic specification. Commonly denoted by the ".litematic" file extension.
+ */
+ Litematica("litematic") {
+ @Override
+ public IStaticSchematic parse(InputStream input) throws IOException {
+ return new LitematicaSchematic(CompressedStreamTools.readCompressed(input));
+ }
};
private final String extension;
diff --git a/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java b/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java
new file mode 100644
index 00000000..9c647a0b
--- /dev/null
+++ b/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java
@@ -0,0 +1,217 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Baritone. If not, see .
+ */
+
+package baritone.utils.schematic.format.defaults;
+
+import baritone.utils.schematic.StaticSchematic;
+import net.minecraft.block.*;
+import net.minecraft.block.properties.IProperty;
+import net.minecraft.nbt.*;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.block.state.IBlockState;
+
+import org.apache.commons.lang3.Validate;
+import javax.annotation.Nullable;
+import java.util.*;
+
+/**
+ * @author Emerson
+ * @since 12/27/2020
+ */
+public final class LitematicaSchematic extends StaticSchematic {
+
+ public LitematicaSchematic(NBTTagCompound nbt) {
+ String regionName = (String) nbt.getCompoundTag("Regions").getKeySet().toArray()[0];
+ this.x = Math.abs(nbt.getCompoundTag("Regions").getCompoundTag(regionName).getCompoundTag("Size").getInteger("x"));
+ this.y = Math.abs(nbt.getCompoundTag("Regions").getCompoundTag(regionName).getCompoundTag("Size").getInteger("y"));
+ this.z = Math.abs(nbt.getCompoundTag("Regions").getCompoundTag(regionName).getCompoundTag("Size").getInteger("z"));
+ this.states = new IBlockState[this.x][this.z][this.y];
+
+
+ NBTTagList paletteTag = nbt.getCompoundTag("Regions").getCompoundTag(regionName).getTagList("BlockStatePalette",10);
+ // ListNBT paletteTag = nbt.getCompound("Regions").getCompound(regionName).getList("BlockStatePalette",10);
+
+ // Create the block states array
+ IBlockState[] paletteBlockStates = new IBlockState[paletteTag.tagCount()];
+ // For every part of the array
+ for (int i = 0; i propertiesMap = new HashMap<>();
+ // Create a map for each state
+ for (int j = 0; j property = block.getBlockState().getProperty(keys[j].toString());
+ if (property != null) {
+ blockState = setPropertyValue(blockState, property, propertiesMap.get(keys[j]));
+ }
+ }
+ paletteBlockStates[i] = blockState;
+ }
+
+
+ // BlockData is stored as an NBT long[]
+ int paletteSize = (int) Math.floor(log2(paletteTag.tagCount()))+1;
+ long litematicSize = (long) this.x*this.y*this.z;
+
+ // In 1.12, the long array isn't exposed by the libraries so parsing has to be done manually
+ String rawBlockString = (nbt.getCompoundTag("Regions").getCompoundTag(regionName)).getTag("BlockStates").toString();
+ rawBlockString = rawBlockString.substring(3,rawBlockString.length()-1);
+ String[] rawBlockArrayString = rawBlockString.split(",");
+ long[] rawBlockData = new long[rawBlockArrayString.length];
+ for (int i = 0; i < rawBlockArrayString.length; i++) {
+ rawBlockData[i] = Long.parseLong(rawBlockArrayString[i].substring(0,rawBlockArrayString[i].length()-1));
+ }
+
+
+ LitematicaBitArray bitArray = new LitematicaBitArray(paletteSize, litematicSize, rawBlockData);
+ if (paletteSize > 32) {
+ throw new IllegalStateException("Too many blocks in schematic to handle");
+ }
+
+ int[] serializedBlockStates = new int[(int) litematicSize];
+ for (int i = 0; i> IBlockState setPropertyValue(IBlockState state, IProperty property, String value) {
+ Optional parsed = property.parseValue(value).toJavaUtil();
+ if (parsed.isPresent()) {
+ return state.withProperty(property, parsed.get());
+ } else {
+ throw new IllegalArgumentException("Invalid value for property " + property);
+ }
+ }
+
+ /** LitematicaBitArray class from litematica */
+ private static class LitematicaBitArray
+ {
+ /** The long array that is used to store the data for this BitArray. */
+ private final long[] longArray;
+ /** Number of bits a single entry takes up */
+ private final int bitsPerEntry;
+ /**
+ * The maximum value for a single entry. This also works as a bitmask for a single entry.
+ * For instance, if bitsPerEntry were 5, this value would be 31 (ie, {@code 0b00011111}).
+ */
+ private final long maxEntryValue;
+ /** Number of entries in this array (not the length of the long array that internally backs this array) */
+ private final long arraySize;
+
+ public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn)
+ {
+ Validate.inclusiveBetween(1L, 32L, (long) bitsPerEntryIn);
+ this.arraySize = arraySizeIn;
+ this.bitsPerEntry = bitsPerEntryIn;
+ this.maxEntryValue = (1L << bitsPerEntryIn) - 1L;
+
+ if (longArrayIn != null)
+ {
+ this.longArray = longArrayIn;
+ }
+ else
+ {
+ this.longArray = new long[(int) (roundUp((long) arraySizeIn * (long) bitsPerEntryIn, 64L) / 64L)];
+ }
+ }
+
+ public void setAt(long index, int value)
+ {
+ Validate.inclusiveBetween(0L, this.arraySize - 1L, (long) index);
+ Validate.inclusiveBetween(0L, this.maxEntryValue, (long) value);
+ long startOffset = index * (long) this.bitsPerEntry;
+ int startArrIndex = (int) (startOffset >> 6); // startOffset / 64
+ int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6);
+ int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64
+ this.longArray[startArrIndex] = this.longArray[startArrIndex] & ~(this.maxEntryValue << startBitOffset) | ((long) value & this.maxEntryValue) << startBitOffset;
+
+ if (startArrIndex != endArrIndex)
+ {
+ int endOffset = 64 - startBitOffset;
+ int j1 = this.bitsPerEntry - endOffset;
+ this.longArray[endArrIndex] = this.longArray[endArrIndex] >>> j1 << j1 | ((long) value & this.maxEntryValue) >> endOffset;
+ }
+ }
+
+ public int getAt(long index)
+ {
+ Validate.inclusiveBetween(0L, this.arraySize - 1L, (long) index);
+ long startOffset = index * (long) this.bitsPerEntry;
+ int startArrIndex = (int) (startOffset >> 6); // startOffset / 64
+ int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6);
+ int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64
+
+ if (startArrIndex == endArrIndex)
+ {
+ return (int) (this.longArray[startArrIndex] >>> startBitOffset & this.maxEntryValue);
+ }
+ else
+ {
+ int endOffset = 64 - startBitOffset;
+ return (int) ((this.longArray[startArrIndex] >>> startBitOffset | this.longArray[endArrIndex] << endOffset) & this.maxEntryValue);
+ }
+ }
+
+
+ public long size()
+ {
+ return this.arraySize;
+ }
+
+ public static long roundUp(long number, long interval)
+ {
+ if (interval == 0)
+ {
+ return 0;
+ }
+ else if (number == 0)
+ {
+ return interval;
+ }
+ else
+ {
+ if (number < 0)
+ {
+ interval *= -1;
+ }
+
+ long i = number % interval;
+ return i == 0 ? number : number + interval - i;
+ }
+ }
+ }
+}
\ No newline at end of file