Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see .
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.mbf
import com.github.luben.zstd.ZstdInputStream
import de.bixilon.mbf.exceptions.UnexpectedStreamEndException
import de.bixilon.mbf.exceptions.UnsupportedMBFVersionException
import java.io.BufferedInputStream
import java.io.InputStream
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.zip.GZIPInputStream
import java.util.zip.InflaterInputStream
class MBFBinaryReader(
private val input: InputStream,
) {
/**
* If the reader should use variable length prefixes
*/
var variableLengthPrefix = false
/**
* Available bytes
*/
val available: Int
get() = input.available()
/**
* Reads one byte from the stream
*/
fun readByte(): Byte {
val read = input.read()
if (read < 0) {
throw UnexpectedStreamEndException("Input stream ended!")
}
return read.toByte()
}
/**
* Reads an unsigned byte from the input stream
*/
fun readUnsignedByte(): Int {
return readByte().toInt() and ((1 shl Byte.SIZE_BITS) - 1)
}
/**
* Reads a byte array from the input stream
* @param length How many bytes to read, defaults to the length prefix
*/
fun readByteArray(length: Int = readLength()): ByteArray {
checkArrayLength(length)
val array = ByteArray(length)
input.read(array)
return array
}
/**
* Reads a byte from the input stream and converts it to a boolean
*/
fun readBoolean(): Boolean {
return readByte() != 0x00.toByte()
}
/**
* Reads a byte boolean array from the input stream
* @param length How many booleans to read, defaults to the length prefix
*/
fun readBooleanArray(length: Int = readLength()): BooleanArray {
val array = BooleanArray(length)
for (index in 0 until length) {
array[index] = readBoolean()
}
return array
}
/**
* Reads a short
*/
fun readShort(): Short {
return (readUnsignedByte() shl Byte.SIZE_BITS or readUnsignedByte()).toShort()
}
/**
* Reads an unsigned short
*/
fun readUnsignedShort(): Int {
return readShort().toInt() and ((1 shl Short.SIZE_BITS) - 1)
}
/**
* Reads a short array
* @param length How many shorts to read, defaults to the length prefix
*/
fun readShortArray(length: Int = readLength()): ShortArray {
checkArrayLength(length * Short.SIZE_BYTES)
val array = ShortArray(length)
for (index in 0 until length) {
array[index] = readShort()
}
return array
}
/**
* Reads an int
*/
fun readInt(): Int {
return readUnsignedShort() shl Short.SIZE_BITS or readUnsignedShort()
}
/**
* Reads an unsigned int
*/
fun readUnsignedInt(): Long {
return readInt().toLong() and ((1L shl Int.SIZE_BITS) - 1)
}
/**
* Reads an int array
* @param length How many ints to read, defaults to the length prefix
*/
fun readIntArray(length: Int = readLength()): IntArray {
checkArrayLength(length * Int.SIZE_BYTES)
val array = IntArray(length)
for (index in 0 until length) {
array[index] = readInt()
}
return array
}
/**
* Reads a long
*/
fun readLong(): Long {
return (readUnsignedInt() shl Int.SIZE_BITS) or readUnsignedInt()
}
/**
* Reads a 128bit unit (uuid in java)
*/
fun readUUID(): UUID {
return UUID(readLong(), readLong())
}
/**
* Reads a long array
* @param length How many longs to read, defaults to the length prefix
*/
fun readLongArray(length: Int = readLength()): LongArray {
checkArrayLength(length * Long.SIZE_BYTES)
val array = LongArray(length)
for (index in 0 until length) {
array[index] = readLong()
}
return array
}
/**
* Reads a float (32bit)
*/
fun readFloat(): Float {
return Float.fromBits(readInt())
}
/**
* Reads a float float
* @param length How many floats to read, defaults to the length prefix
*/
fun readFloatArray(length: Int = readLength()): FloatArray {
checkArrayLength(length * Float.SIZE_BYTES)
val array = FloatArray(length)
for (index in 0 until length) {
array[index] = readFloat()
}
return array
}
/**
* Reads a double (64bit)
*/
fun readDouble(): Double {
return Double.fromBits(readLong())
}
/**
* Reads a double array
* @param length How many doubles to read, defaults to the length prefix
*/
fun readDoubleArray(length: Int = readLength()): DoubleArray {
checkArrayLength(length * Double.SIZE_BYTES)
val array = DoubleArray(length)
for (index in 0 until length) {
array[index] = readDouble()
}
return array
}
/**
* Reads a VarInt
*/
fun readVarInt(): Int {
var byteCount = 0
var result = 0
var read: Int
do {
read = readUnsignedByte()
result = result or (read and 0x7F shl (Byte.SIZE_BITS - 1) * byteCount)
byteCount++
require(byteCount <= Int.SIZE_BYTES + 1) { "VarInt is too big" }
} while (read and 0x80 != 0)
return result
}
/**
* Reads a VarInt array
* @param length How many VarInts to read, defaults to the length prefix
*/
fun readVarIntArray(length: Int = readLength()): IntArray {
checkArrayLength(length)
val array = IntArray(length)
for (index in 0 until length) {
array[index] = readVarInt()
}
return array
}
/**
* Reads a VarLong
*/
fun readVarLong(): Long {
var byteCount = 0
var result = 0L
var read: Long
do {
read = readByte().toLong() and ((1 shl Byte.SIZE_BITS) - 1).toLong()
result = result or (read and 0x7F shl (Byte.SIZE_BITS - 1) * byteCount)
byteCount++
require(byteCount <= Int.SIZE_BYTES + 1) { "VarLong is too big" }
} while (read and 0x80L != 0L)
return result
}
/**
* Reads a VarLong array
* @param length How many VarLongs to read, defaults to the length prefix
*/
fun readVarLongArray(length: Int = readLength()): LongArray {
checkArrayLength(length)
val array = LongArray(length)
for (index in 0 until length) {
array[index] = readVarLong()
}
return array
}
inline fun readArray(length: Int = readLength(), reader: () -> T): Array {
checkArrayLength(length)
val array: Array = arrayOfNulls(length)
for (i in 0 until length) {
array[i] = reader()
}
return array as Array
}
fun readString(length: Int = readLength()): String {
return String(readByteArray(length), StandardCharsets.UTF_8)
}
fun readLength(variable: Boolean = variableLengthPrefix): Int {
if (variable) {
return readVarInt()
}
return readInt()
}
fun readMBFDataType(): MBFDataTypes {
return MBFDataTypes.VALUES.getOrElse(readByte().toInt()) { type -> error("Can not find data type $type") }
}
fun readMBFEntry(): Any? {
return readMBFDataType().type.read(this)
}
/**
* Reads a full mbf object including meta data
*/
fun readMBF(): MBFData {
check(readByte().toInt().toChar() == 'M' && readByte().toInt().toChar() == 'B' && readByte().toInt().toChar() == 'F') { "Data is prefixed with MBF!" }
val version = readByte().toInt()
if (version != 0) {
throw UnsupportedMBFVersionException(version, "Unknown MBF version ($version). Only version 0 is supported!")
}
val flags = readByte().toInt()
val dataInfo = MBFDataInfo(
compression = MBFCompressionTypes.VALUES.getOrElse(flags and 0b11) { error("Can not find compression ($it)!") },
encryption = flags and 0b100 > 0,
variableLengthPrefix = flags and 0b1000 > 0,
preferVariableTypes = flags and 0b10000 > 0,
version = version,
)
var data = this
data.variableLengthPrefix = dataInfo.variableLengthPrefix
if (dataInfo.encryption) {
TODO("Encryption is not implemented yet")
}
if (dataInfo.compression != MBFCompressionTypes.NONE) {
val length = data.readLength()
val compressed = LimitedInputStream(data.input, length)
val stream = when (dataInfo.compression) {
MBFCompressionTypes.ZSTD -> ZstdInputStream(compressed)
MBFCompressionTypes.DEFLATE -> InflaterInputStream(compressed)
MBFCompressionTypes.GZIP -> GZIPInputStream(compressed)
else -> TODO("Compression type is not implemented yet!")
}
data = MBFBinaryReader(BufferedInputStream(stream))
data.variableLengthPrefix = dataInfo.variableLengthPrefix
}
return MBFData(
dataInfo = dataInfo,
data = data.readMBFEntry(),
)
}
companion object {
fun checkArrayLength(length: Int) {
check(length <= MBFUtil.ARRAY_MAX_BYTES) { "Trying to allocate too much memory!" }
}
}
}