diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index 9001886af..17faa8100 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -4,7 +4,7 @@ package net.torvald.terrarum.gameworld import com.badlogic.gdx.graphics.Color import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.blockproperties.BlockCodex -import net.torvald.terrarum.serialise.ReadLayerDataZip +import net.torvald.terrarum.serialise.ReadLayerDataLzma import org.dyn4j.geometry.Vector2 typealias BlockAddress = Long @@ -73,7 +73,7 @@ open class GameWorld { //layerAirPressure = MapLayerHalfFloat(width / 4, height / 8, 13f) // 1013 mBar } - internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData) { + internal constructor(worldIndex: Int, layerData: ReadLayerDataLzma.LayerData) { this.worldIndex = worldIndex layerTerrain = layerData.layerTerrain diff --git a/src/net/torvald/terrarum/modulebasegame/console/ExportLayerData.kt b/src/net/torvald/terrarum/modulebasegame/console/ExportLayerData.kt index 3b8451f6b..15d1937f6 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ExportLayerData.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ExportLayerData.kt @@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame.console import net.torvald.terrarum.console.ConsoleCommand import net.torvald.terrarum.console.Echo import net.torvald.terrarum.console.EchoError +import net.torvald.terrarum.serialise.WriteLayerDataLzma import net.torvald.terrarum.serialise.WriteLayerDataZip /** @@ -11,7 +12,7 @@ import net.torvald.terrarum.serialise.WriteLayerDataZip object ExportLayerData : ConsoleCommand { override fun execute(args: Array) { try { - val outfile = WriteLayerDataZip() + val outfile = WriteLayerDataLzma() Echo("Layer data exported to ${outfile!!.canonicalPath}") } catch (e: Exception) { diff --git a/src/net/torvald/terrarum/modulebasegame/console/ImportLayerData.kt b/src/net/torvald/terrarum/modulebasegame/console/ImportLayerData.kt index 3e1100428..85560c672 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ImportLayerData.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ImportLayerData.kt @@ -3,9 +3,8 @@ package net.torvald.terrarum.modulebasegame.console import net.torvald.terrarum.Terrarum import net.torvald.terrarum.console.ConsoleCommand import net.torvald.terrarum.console.Echo -import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension -import net.torvald.terrarum.serialise.ReadLayerDataZip +import net.torvald.terrarum.serialise.ReadLayerDataLzma import net.torvald.terrarum.worlddrawer.FeaturesDrawer import java.io.File @@ -20,7 +19,7 @@ object ImportLayerData : ConsoleCommand { } val file = File(args[1]) - val layerData = ReadLayerDataZip(file) + val layerData = ReadLayerDataLzma(file) Terrarum.ingame!!.world = GameWorldExtension(1, layerData) diff --git a/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt b/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt index 41651078c..8b9ab4cdb 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt @@ -1,9 +1,7 @@ package net.torvald.terrarum.modulebasegame.gameworld -import com.badlogic.gdx.graphics.Color import net.torvald.terrarum.gameworld.* -import net.torvald.terrarum.serialise.ReadLayerDataZip -import kotlin.properties.Delegates +import net.torvald.terrarum.serialise.ReadLayerDataLzma /** * Created by minjaesong on 2018-07-03. @@ -11,7 +9,7 @@ import kotlin.properties.Delegates class GameWorldExtension: GameWorld { constructor(worldIndex: Int, width: Int, height: Int) : super(worldIndex, width, height) - internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData) : super(worldIndex, layerData) + internal constructor(worldIndex: Int, layerData: ReadLayerDataLzma.LayerData) : super(worldIndex, layerData) val time: WorldTime diff --git a/src/net/torvald/terrarum/serialise/ReadLayerData.kt b/src/net/torvald/terrarum/serialise/ReadLayerData.kt index ea48490f6..67dba41df 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerData.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerData.kt @@ -8,9 +8,12 @@ import java.lang.IllegalArgumentException 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 { @@ -112,4 +115,60 @@ internal object ReadLayerData { return i } -} \ No newline at end of file +} + +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() +) +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() +) +fun Long.toLittle48() = 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.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) \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt b/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt new file mode 100644 index 000000000..343f96d05 --- /dev/null +++ b/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt @@ -0,0 +1,277 @@ +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.BlockDamage +import net.torvald.terrarum.gameworld.MapLayer +import net.torvald.terrarum.gameworld.PairedMapLayer +import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read +import net.torvald.terrarum.toHex +import java.io.* +import java.nio.charset.Charset +import java.util.* +import kotlin.IllegalArgumentException +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): 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 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, "Dimension: ${width}x$height") + + // read payloads + + val pldBuffer4 = ByteArray(4) + val pldBuffer6 = ByteArray(6) + val pldBuffer8 = ByteArray(8) + + val payloads = HashMap() + + + // 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() + + 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() + + // deal with (MSB ++ LSB) + if (t == "TERR" || t == "WALL") { + payloadBytes["${t}_MSB"] = inflatedFile.sliceArray(0 until worldSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB + payloadBytes["${t}_LSB"] = inflatedFile.sliceArray(worldSize.toInt() until u.uncompressedSize.toInt()) // FIXME deflated stream cannot be larger than 2 GB + } + else { + payloadBytes[t] = inflatedFile + } + } + + val spawnPoint = LandUtil.resolveBlockAddr(width, spawnAddress) + + val terrainDamages = HashMap() + val wallDamages = HashMap() + + // 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() + } + + + return LayerData( + MapLayer(width, height, payloadBytes["WALL_MSB"]!!), + MapLayer(width, height, payloadBytes["TERR_MSB"]!!), + MapLayer(width, height, payloadBytes["WIRE"]!!), + PairedMapLayer(width, height, payloadBytes["WALL_LSB"]!!), + PairedMapLayer(width, height, payloadBytes["TERR_LSB"]!!), + + spawnPoint.first, spawnPoint.second, + + wallDamages, terrainDamages + ) + } + + private data class TEMzPayload(val uncompressedSize: Long, val bytes: ByteArray) // FIXME deflated stream cannot be larger than 2 GB + + /** + * Immediately deployable, a part of the gameworld + */ + internal data class LayerData( + val layerWall: MapLayer, + val layerTerrain: MapLayer, + val layerWire: MapLayer, + val layerWallLowBits: PairedMapLayer, + val layerTerrainLowBits: PairedMapLayer, + //val layerThermal: MapLayerHalfFloat, // in Kelvins + //val layerAirPressure: MapLayerHalfFloat, // (milibar - 1000) + + val spawnX: Int, + val spawnY: Int, + val wallDamages: HashMap, + val terrainDamages: HashMap + ) + + private fun ByteArray.shiftLeftBy(size: Int, fill: Byte = 0.toByte()) { + if (size == 0) { + return + } + else if (size < 0) { + throw IllegalArgumentException("This won't shift to right (size = $size)") + } + else if (size >= this.size) { + Arrays.fill(this, 0.toByte()) + } + else { + for (c in size..this.lastIndex) { + this[c - size] = this[c] + } + for (c in (this.size - size)..this.lastIndex) { + this[c] = fill + } + } + } + + + 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 ByteArray.toByteString(): String { + val sb = StringBuilder() + this.forEach { + sb.append(it.toUint().toHex().takeLast(2)) + sb.append(' ') + } + sb.deleteCharAt(sb.lastIndex) + return sb.toString() + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt b/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt index 2cdd84e33..d689733c7 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt @@ -21,7 +21,7 @@ import kotlin.collections.HashMap // internal for everything: prevent malicious module from messing up the savedata internal object ReadLayerDataZip { - // FIXME UNTESTED !! + // FIXME TERRAIN DAMAGE UNTESTED internal operator fun invoke(file: File): LayerData { val inputStream = MarkableFileInputStream(FileInputStream(file)) @@ -49,6 +49,8 @@ internal object ReadLayerDataZip { val height = inputStream.read(4).toLittleInt() val spawnAddress = inputStream.read(6).toLittleInt48() + if (compression != 1) throw IllegalArgumentException("Input file is not compressed as DEFLATE; it's using algorithm $compression") + printdbg(this, "Version number: $versionNumber") printdbg(this, "Layers count: $layerCount") printdbg(this, "Payloads count: $payloadCount") diff --git a/src/net/torvald/terrarum/serialise/WriteLayerData.kt b/src/net/torvald/terrarum/serialise/WriteLayerData.kt index 2a11ea584..21ad557e2 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerData.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerData.kt @@ -15,6 +15,7 @@ import java.util.zip.GZIPOutputStream * Created by minjaesong on 2016-03-18. */ // internal for everything: prevent malicious module from messing up the savedata +@Deprecated("TEMD is deprecated format; use TEMz which does compression") internal object WriteLayerData { val LAYERS_FILENAME = "worldinfo1" @@ -86,43 +87,3 @@ internal object WriteLayerData { } - -/*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() -) -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() -) -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.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.toLittleDouble() = java.lang.Double.longBitsToDouble(this.toLittleLong()) - -fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this) -fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)*/ \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt b/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt new file mode 100644 index 000000000..68e36c990 --- /dev/null +++ b/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt @@ -0,0 +1,175 @@ +package net.torvald.terrarum.serialise + +import com.badlogic.gdx.utils.compression.Lzma +import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.console.EchoError +import net.torvald.terrarum.realestate.LandUtil +import java.io.* +import java.nio.charset.Charset +import java.util.zip.DeflaterOutputStream + +/** + * This object only writes a file named 'worldinfo1'. + * + * The intended operation is as follows: + * 1. This and others write + * + * TODO temporarily dump on the disk THEN pack? Or put all the files (in ByteArray64) in the RAM THEN pack? + * + * Created by minjaesong on 2016-03-18. + */ +// internal for everything: prevent malicious module from messing up the savedata +internal object WriteLayerDataLzma { + + // FIXME TERRAIN DAMAGE UNTESTED + + + // 2400x800 world size: about .. kB + // 8192x2048 world size: about 470 kB but writes much slower than DEFLATE + + + val LAYERS_FILENAME = "world" + + val MAGIC = byteArrayOf(0x54, 0x45, 0x4D, 0x7A) + val VERSION_NUMBER = 3.toByte() + val NUMBER_OF_LAYERS = 3.toByte() + val NUMBER_OF_PAYLOADS = 5.toByte() + val COMPRESSION_ALGORITHM = 2.toByte() + val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64) + val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1) + val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2) + + //val NULL: Byte = 0 + + + /** + * TODO currently it'll dump the temporary file (tmp_worldinfo1) onto the disk and will return the temp file. + * + * @return File on success; `null` on failure + */ + internal operator fun invoke(): File? { + val world = (Terrarum.ingame!!.world) + + val path = "${Terrarum.defaultSaveDir}/tmp_$LAYERS_FILENAME${world.worldIndex}" + + // TODO let's try dump-on-the-disk-then-pack method... + + /*val parentDir = File("${Terrarum.defaultSaveDir}/$saveDirectoryName") + if (!parentDir.exists()) { + parentDir.mkdir() + } + else if (!parentDir.isDirectory) { + EchoError("Savegame directory is not actually a directory, aborting...") + return false + }*/ + + + val outFile = File(path) + if (outFile.exists()) outFile.delete() + outFile.createNewFile() + + val outputStream = BufferedOutputStream(FileOutputStream(outFile), 8192) + var deflater: DeflaterOutputStream // couldn't really use one outputstream for all the files. + + fun wb(byteArray: ByteArray) { outputStream.write(byteArray) } + fun wb(byte: Byte) { outputStream.write(byte.toInt()) } + //fun wb(byte: Int) { outputStream.write(byte) } + fun wi32(int: Int) { wb(int.toLittle()) } + fun wi48(long: Long) { wb(long.toLittle48()) } + fun wi64(long: Long) { wb(long.toLittle()) } + fun wf32(float: Float) { wi32(float.toRawBits()) } + + + //////////////////// + // WRITE BINARIES // + //////////////////// + + + // all the necessary headers + wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM) + + // world width, height, and spawn point + wi32(world.width); wi32(world.height) + wi48(LandUtil.getBlockAddr(world, world.spawnX, world.spawnY)) + + // write payloads // + outputStream.flush() + + // TERR payload + // PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA + // Thus, \0pLd + [10] must be either of these. + + wb(PAYLOAD_HEADER); wb("TERR".toByteArray()) + wi48(world.width * world.height * 3L / 2) + Lzma.compress(ByteArrayInputStream(world.terrainArray), outputStream) + Lzma.compress(ByteArrayInputStream(world.layerTerrainLowBits.data), outputStream) + wb(PAYLOAD_FOOTER) + + // WALL payload + wb(PAYLOAD_HEADER); wb("WALL".toByteArray()) + wi48(world.width * world.height * 3L / 2) + Lzma.compress(ByteArrayInputStream(world.wallArray), outputStream) + Lzma.compress(ByteArrayInputStream(world.layerWallLowBits.data), outputStream) + wb(PAYLOAD_FOOTER) + + // WIRE payload + wb(PAYLOAD_HEADER); wb("WIRE".toByteArray()) + wi48(world.width * world.height.toLong()) + Lzma.compress(ByteArrayInputStream(world.wireArray), outputStream) + wb(PAYLOAD_FOOTER) + + // TdMG payload + wb(PAYLOAD_HEADER); wb("TdMG".toByteArray()) + wi48(world.terrainDamages.size.toLong()) + + + world.terrainDamages.forEach { t, u -> + Lzma.compress(ByteArrayInputStream(t.toLittle48()), outputStream) + Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream) + } + + wb(PAYLOAD_FOOTER) + + // WdMG payload + wb(PAYLOAD_HEADER); wb("WdMG".toByteArray()) + wi48(world.wallDamages.size.toLong()) + + + world.wallDamages.forEach { t, u -> + Lzma.compress(ByteArrayInputStream(t.toLittle48()), outputStream) + Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream) + } + + wb(PAYLOAD_FOOTER) + + // write footer + wb(FILE_FOOTER) + + + ////////////////// + // END OF WRITE // + ////////////////// + + + + // replace savemeta with tempfile + try { + outputStream.flush() + outputStream.close() + + + return outFile + } + catch (e: IOException) { + e.printStackTrace() + } + finally { + outputStream.close() + } + + return null + } + + +} diff --git a/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt b/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt index e6ac6331b..6b2a9de6d 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt @@ -26,7 +26,7 @@ import java.util.zip.GZIPOutputStream // internal for everything: prevent malicious module from messing up the savedata internal object WriteLayerDataZip { - // FIXME output seems legit, but I can't confirm right now !! + // FIXME TERRAIN DAMAGE UNTESTED // 2400x800 world size, default comp level: about 90 kB @@ -189,59 +189,3 @@ internal object WriteLayerDataZip { } - -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() -) -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() -) -fun Long.toLittle48() = 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.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) \ No newline at end of file diff --git a/work_files/DataFormats/Map data format.txt b/work_files/DataFormats/Map data format.txt index aa818a872..6b39b2e71 100644 --- a/work_files/DataFormats/Map data format.txt +++ b/work_files/DataFormats/Map data format.txt @@ -14,7 +14,7 @@ Ord Hex Description 06 05 Number of payloads -07 01 Compression algorithm, 0 for none, 1 for DEFLATE, otherwise undefined (maybe LZMA2 for the future?) +07 01 Compression algorithm, 0 for none, 1 for DEFLATE, 2 for LZMA, otherwise undefined (maybe LZMA2 for the future?) 08 World width 09 World width