Files
Terrarum/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt
2019-06-08 03:00:47 +09:00

220 lines
7.5 KiB
Kotlin

package net.torvald.terrarum.serialise
import com.badlogic.gdx.utils.compression.Lzma
import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.BlockLayer
import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
import net.torvald.terrarum.realestate.LandUtil
import java.io.*
import java.util.*
import kotlin.collections.HashMap
/**
* Created by minjaesong on 2016-08-24.
*/
// internal for everything: prevent malicious module from messing up the savedata
internal object ReadLayerDataLzma {
// FIXME TERRAIN DAMAGE UNTESTED
internal operator fun invoke(file: File): ReadLayerDataZip.LayerData {
val inputStream = MarkableFileInputStream(FileInputStream(file))
val magicBytes = ByteArray(4)
//////////////////
// FILE READING //
//////////////////
// read header first
inputStream.read(magicBytes)
if (!Arrays.equals(magicBytes, WriteLayerDataZip.MAGIC)) {
throw IllegalArgumentException("File not a Layer Data")
}
val versionNumber = inputStream.read(1)[0].toUint()
val layerCount = inputStream.read(1)[0].toUint()
val payloadCount = inputStream.read(1)[0].toUint()
val compression = inputStream.read(1)[0].toUint()
val generatorVer = inputStream.read(2).toULittleShort()
val width = inputStream.read(4).toLittleInt()
val height = inputStream.read(4).toLittleInt()
val spawnAddress = inputStream.read(6).toLittleInt48()
if (compression != 2) throw IllegalArgumentException("Input file is not compressed as LZMA; it's using algorithm $compression")
printdbg(this, "Version number: $versionNumber")
printdbg(this, "Layers count: $layerCount")
printdbg(this, "Payloads count: $payloadCount")
printdbg(this, "Compression: $compression")
printdbg(this, "World generator version: $generatorVer")
printdbg(this, "Dimension: ${width}x$height")
// read payloads
val payloads = PayloadUtil.readAll(inputStream)
/*val pldBuffer4 = ByteArray(4)
val pldBuffer6 = ByteArray(6)
val pldBuffer8 = ByteArray(8)
val payloads = HashMap<String, TEMzPayload>()
// TODO please test the read; write has been fixed up
for (pldCnt in 0 until payloadCount) {
inputStream.read(pldBuffer4)
// check payload header
if (!pldBuffer4.contentEquals(WriteLayerDataZip.PAYLOAD_HEADER))
throw InternalError("Payload $pldCnt not found -- expected ${WriteLayerDataZip.PAYLOAD_HEADER.toByteString()}, got ${pldBuffer4.toByteString()}")
// get payload's name
inputStream.read(pldBuffer4)
val payloadName = pldBuffer4.toString(Charset.forName("US-ASCII"))
printdbg(this, "Payload $pldCnt name: $payloadName") // maybe maybe related with buffer things?
// get uncompressed size
inputStream.read(pldBuffer6)
val uncompressedSize = pldBuffer6.toLittleInt48()
// get deflated size
inputStream.mark(2147483647) // FIXME deflated stream cannot be larger than 2 GB
// creep forward until we hit the PAYLOAD_FOOTER
var deflatedSize: Int = 0 // FIXME deflated stream cannot be larger than 2 GB
// loop init
inputStream.read(pldBuffer8)
// loop main
while (!pldBuffer8.contentEquals(WriteLayerDataZip.PAYLOAD_FOOTER)) {
val aByte = inputStream.read(); deflatedSize += 1
if (aByte == -1) throw InternalError("Unexpected end-of-file at payload $pldCnt")
pldBuffer8.shiftLeftBy(1, aByte.toByte())
}
// at this point, we should have correct size of deflated bytestream
printdbg(this, "Payload $pldCnt compressed size: $deflatedSize")
val deflatedBytes = ByteArray(deflatedSize) // FIXME deflated stream cannot be larger than 2 GB
inputStream.reset() // go back to marked spot
inputStream.read(deflatedBytes)
// PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA
// Thus, \0pLd + [10] must be either of these.
// put constructed payload into a container
payloads.put(payloadName, TEMzPayload(uncompressedSize, deflatedBytes))
// skip over to be aligned with the next payload
inputStream.skip(8)
}
// test for EOF
inputStream.read(pldBuffer8)
if (!pldBuffer8.contentEquals(WriteLayerDataZip.FILE_FOOTER))
throw InternalError("Expected end-of-file, got not-so-end-of-file")*/
//////////////////////
// END OF FILE READ //
//////////////////////
val worldSize = width.toLong() * height
val payloadBytes = HashMap<String, ByteArray>()
payloads.forEach { t, u ->
val inflatedOS = ByteArrayOutputStream(u.uncompressedSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB
try {
Lzma.decompress(ByteArrayInputStream(u.bytes), inflatedOS)
}
catch (e: RuntimeException) {
// keep it empty (zero-sized file was compressed)
}
val inflatedFile = inflatedOS.toByteArray()
payloadBytes[t] = inflatedFile
}
val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress)
val terrainDamages = HashMap<BlockAddress, Float>()
val wallDamages = HashMap<BlockAddress, Float>()
val fluidTypes = HashMap<BlockAddress, FluidType>()
val fluidFills = HashMap<BlockAddress, Float>()
// parse terrain damages
for (c in 0 until payloadBytes["TdMG"]!!.size step 10) {
val bytes = payloadBytes["TdMG"]!!
val tileAddr = bytes.sliceArray(c..c+5)
val value = bytes.sliceArray(c+6..c+9)
terrainDamages[tileAddr.toLittleInt48()] = value.toLittleFloat()
}
// parse wall damages
for (c in 0 until payloadBytes["WdMG"]!!.size step 10) {
val bytes = payloadBytes["WdMG"]!!
val tileAddr = bytes.sliceArray(c..c+5)
val value = bytes.sliceArray(c+6..c+9)
wallDamages[tileAddr.toLittleInt48()] = value.toLittleFloat()
}
// TODO parse fluid(Types|Fills)
return ReadLayerDataZip.LayerData(
BlockLayer(width, height, payloadBytes["WALL"]!!),
BlockLayer(width, height, payloadBytes["TERR"]!!),
spawnPoint.first, spawnPoint.second,
wallDamages, terrainDamages, fluidTypes, fluidFills
)
}
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
}
}