Files
Terrarum/src/net/torvald/terrarum/serialise/ReadLayerData.kt

194 lines
6.6 KiB
Kotlin

package net.torvald.terrarum.serialise
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
import java.io.IOException
import java.io.InputStream
import java.util.*
/**
* Only being used by the title screen and the demoworld. This object may get deleted at any update
*
* Created by minjaesong on 2016-08-24.
*/
// internal for everything: prevent malicious module from messing up the savedata
@Deprecated("TEMD is deprecated format; use TEMz which does compression")
internal object ReadLayerData {
internal operator fun invoke(inputStream: InputStream, inWorld: GameWorldExtension? = null): GameWorldExtension {
val magicBytes = ByteArray(4)
val layerSizeBytes = ByteArray(1)
val layerCountBytes = ByteArray(1)
val worldWidthBytes = ByteArray(4)
val worldHeightBytes = ByteArray(4)
val spawnCoordXBytes = ByteArray(4)
val spawnCoordYBytes = ByteArray(4)
// read header first
inputStream.read(magicBytes)
if (!Arrays.equals(magicBytes, WriteLayerData.MAGIC)) {
throw IllegalArgumentException("File not a Layer Data")
}
inputStream.read(layerSizeBytes)
inputStream.read(layerCountBytes)
inputStream.skip(2) // reserved bytes
inputStream.read(worldWidthBytes)
inputStream.read(worldHeightBytes)
inputStream.read(spawnCoordXBytes)
inputStream.read(spawnCoordYBytes)
val worldWidth = worldWidthBytes.toLittleInt()
val worldHeight = worldHeightBytes.toLittleInt()
val bytesPerTile = layerSizeBytes[0].toUint()
val layerCount = layerCountBytes[0].toUint()
val layerSize = worldWidth * worldHeight * bytesPerTile
val terrainLayerMSB = ByteArray(layerSize)
val wallLayerMSB = ByteArray(layerSize)
val terrainLayerLSB = ByteArray(layerSize / 2)
val wallLayerLSB = ByteArray(layerSize / 2)
var wireLayer: ByteArray? = null
inputStream.read(terrainLayerMSB)
inputStream.read(wallLayerMSB)
inputStream.read(terrainLayerLSB)
inputStream.read(wallLayerLSB)
if (layerCount == 4) {
wireLayer = ByteArray(layerSize)
inputStream.read(wireLayer)
}
// create world out of tiles data
val retWorld = inWorld ?: GameWorldExtension(1, worldWidth, worldHeight, 0, 0, 0) // FIXME null TIME_T for the (partial) test to pass
retWorld.layerTerrain.data = terrainLayerMSB
retWorld.layerWall.data = wallLayerMSB
retWorld.layerTerrainLowBits.data = terrainLayerLSB
retWorld.layerWallLowBits.data = wallLayerLSB
if (wireLayer != null) {
retWorld.layerWire.data = wireLayer
}
retWorld.spawnX = spawnCoordXBytes.toLittleInt()
retWorld.spawnY = spawnCoordYBytes.toLittleInt()
return retWorld
}
internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int {
if (b == null) {
throw NullPointerException()
} else if (off < 0 || len < 0 || len > b.size) {
throw IndexOutOfBoundsException()
} else if (len == 0) {
return 0
}
var c = read()
if (c == -1) {
return -1
}
b[0] = c.toByte()
var i = 1
try {
while (i < len) {
c = read()
if (c == -1) {
break
}
b[i] = c.toByte()
i++
}
} catch (ee: IOException) {
}
return i
}
}
fun Int.toLittle() = byteArrayOf(
this.and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte(),
this.ushr(16).and(0xFF).toByte(),
this.ushr(24).and(0xFF).toByte()
)
/** Converts int as 2-byte array, discarding the sign.*/
fun Int.toULittleShort() = byteArrayOf(
this.and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte()
)
/** Converts int as 2-byte array, preserving the sign. In other words, it converts int to short. */
fun Int.toLittleShort() = byteArrayOf(
this.and(0xFF).toByte(),
this.shr(8).and(0xFF).toByte()
)
fun Long.toLittle() = byteArrayOf(
this.and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte(),
this.ushr(16).and(0xFF).toByte(),
this.ushr(24).and(0xFF).toByte(),
this.ushr(32).and(0xFF).toByte(),
this.ushr(40).and(0xFF).toByte(),
this.ushr(48).and(0xFF).toByte(),
this.ushr(56).and(0xFF).toByte()
)
/** Converts long as 6-byte array, discarding the sign. */
fun Long.toULittle48() = byteArrayOf(
this.and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte(),
this.ushr(16).and(0xFF).toByte(),
this.ushr(24).and(0xFF).toByte(),
this.ushr(32).and(0xFF).toByte(),
this.ushr(40).and(0xFF).toByte()
)
fun Double.toLittle() = java.lang.Double.doubleToRawLongBits(this).toLittle()
fun Boolean.toLittle() = byteArrayOf(if (this) 0xFF.toByte() else 0.toByte())
fun ByteArray.toLittleInt() =
if (this.size != 4) throw Error("Array not in size of 4")
else this[0].toUint() or
this[1].toUint().shl(8) or
this[2].toUint().shl(16) or
this[3].toUint().shl(24)
fun ByteArray.toULittleShort() =
if (this.size != 4) throw Error("Array not in size of 2")
else this[0].toUint() or
this[1].toUint().shl(8)
fun ByteArray.toLittleShort() =
if (this.size != 4) throw Error("Array not in size of 2")
else this[0].toUint() or
this[1].toInt().shl(8)
fun ByteArray.toLittleLong() =
if (this.size != 8) throw Error("Array not in size of 8")
else this[0].toUlong() or
this[1].toUlong().shl(8) or
this[2].toUlong().shl(16) or
this[3].toUlong().shl(24) or
this[4].toUlong().shl(32) or
this[5].toUlong().shl(40) or
this[6].toUlong().shl(48) or
this[7].toUlong().shl(56)
fun ByteArray.toLittleInt48() =
if (this.size != 6) throw Error("Array not in size of 6")
else this[0].toUlong() or
this[1].toUlong().shl(8) or
this[2].toUlong().shl(16) or
this[3].toUlong().shl(24) or
this[4].toUlong().shl(32) or
this[5].toUlong().shl(40)
fun ByteArray.toLittleFloat() = java.lang.Float.intBitsToFloat(this.toLittleInt())
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
const val WORLD_GENERATOR_VERSION = 1