Files
Terrarum/src/net/torvald/terrarum/serialise/WriteWorldInfo.kt
2019-06-01 03:25:20 +09:00

185 lines
6.4 KiB
Kotlin

package net.torvald.terrarum.serialise
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.GL30
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.utils.ScreenUtils
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.*
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
import net.torvald.terrarum.modulebasegame.weather.WeatherMixer
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64InputStream
import org.apache.commons.codec.digest.DigestUtils
import java.io.ByteArrayOutputStream
import java.util.zip.Deflater
import java.util.zip.DeflaterOutputStream
object WriteWorldInfo {
val META_MAGIC = "TESV".toByteArray(Charsets.UTF_8)
val NULL = 0.toByte()
val VERSION = 1
val HASHED_FILES_COUNT = 3
/**
* TODO currently it'll dump the temporary file (tmp_worldinfo1) onto the disk and will return the temp file.
*
* @return List of ByteArray64, worldinfo0..worldinfo3; `null` on failure
*/
internal operator fun invoke(ingame: IngameInstance): List<ByteArray64>? {
//val path = "${AppLoader.defaultSaveDir}/tmp_worldinfo"
val infileList = arrayOf(
ModMgr.getGdxFilesFromEveryMod("blocks/blocks.csv"),
ModMgr.getGdxFilesFromEveryMod("items/items.csv"),
ModMgr.getGdxFilesFromEveryMod("materials/materials.csv")
)
val outFiles = ArrayList<ByteArray64>() // for worldinfo1-3 only
val worldInfoHash = ArrayList<ByteArray>() // hash of worldinfo1-3
// try to write worldinfo1-3
for (filenum in 1..HASHED_FILES_COUNT) {
val outputStream = ByteArray64GrowableOutputStream()
val infile = infileList[filenum - 1]
infile.forEach {
outputStream.write("## from file: ${it.nameWithoutExtension()} ##############################\n".toByteArray())
val readBytes = it.readBytes()
outputStream.write(readBytes)
outputStream.write("\n".toByteArray())
}
outputStream.flush()
outputStream.close()
outFiles.add(outputStream.toByteArray64())
worldInfoHash.add(DigestUtils.sha256(ByteArray64InputStream(outputStream.toByteArray64())))
}
// compose save meta (actual writing part)
val metaOut = ByteArray64GrowableOutputStream()
metaOut.write(META_MAGIC)
metaOut.write(VERSION)
metaOut.write(HASHED_FILES_COUNT)
// world name
val world = ingame.world
val worldNameBytes = world.worldName.toByteArray(Charsets.UTF_8)
//metaOut.write(worldNameBytes)
worldNameBytes.forEach {
if (it != 0.toByte()) metaOut.write(it.toInt())
}
metaOut.write(NULL.toInt())
// terrain seed
metaOut.write(world.generatorSeed.toLittle())
// randomiser seed
metaOut.write(RoguelikeRandomiser.RNG.state0.toLittle())
metaOut.write(RoguelikeRandomiser.RNG.state1.toLittle())
// weather seed
metaOut.write(WeatherMixer.RNG.state0.toLittle())
metaOut.write(WeatherMixer.RNG.state1.toLittle())
// reference ID of the player
metaOut.write(Terrarum.PLAYER_REF_ID.toLittle())
// ingame time_t
metaOut.write((world as GameWorldExtension).time.TIME_T.toLittle())
// creation time (real world time)
metaOut.write(world.creationTime.toULittle48())
// time at save (real world time)
val timeNow = System.currentTimeMillis() / 1000L
metaOut.write(timeNow.toULittle48())
// get playtime and save it
val timeToAdd = (timeNow - world.loadTime).toInt()
metaOut.write((world.totalPlayTime + timeToAdd).toLittle())
world.lastPlayTime = timeNow
world.totalPlayTime += timeToAdd
// SHA256SUM of worldinfo1-3
worldInfoHash.forEach {
metaOut.write(it)
}
// thumbnail
val texreg = ingame.actorGamer.sprite?.textureRegion
if (texreg != null) {
val batch = SpriteBatch()
val camera = OrthographicCamera(texreg.tileW.toFloat(), texreg.tileH.toFloat())
val fbo = FrameBuffer(Pixmap.Format.RGBA8888, texreg.tileW, texreg.tileH, false)
fbo.inAction(camera, batch) {
batch.inUse {
batch.draw(texreg.get(0, 0), 0f, 0f)
}
}
// bind and unbind the fbo so that I can get the damned Pixmap using ScreenUtils
// NullPointerException if not appconfig.useGL30
Gdx.gl30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, fbo.framebufferHandle)
Gdx.gl30.glReadBuffer(GL30.GL_COLOR_ATTACHMENT0)
val outpixmap = ScreenUtils.getFrameBufferPixmap(0, 0, fbo.width, fbo.height)
Gdx.gl30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0)
Gdx.gl30.glReadBuffer(GL30.GL_BACK)
val tgaSize = PixmapIO2.HEADER_FOOTER_SIZE + outpixmap.width * outpixmap.height * 4
val byteArrayOS = ByteArrayOutputStream(tgaSize)
PixmapIO2._writeTGA(byteArrayOS, outpixmap, true, true)
byteArrayOS.flush()
byteArrayOS.close()
//PixmapIO2.writeTGA(Gdx.files.absolute(AppLoader.defaultDir+"/tmp_writeworldinfo+outpixmap.tga"), outpixmap, true)
outpixmap.dispose()
batch.dispose()
fbo.dispose()
// write uncompressed size
metaOut.write(tgaSize.toULittleShort())
// write compressed tga
val deflater = DeflaterOutputStream(metaOut, Deflater(Deflater.BEST_COMPRESSION, true), false)
deflater.write(byteArrayOS.toByteArray())
deflater.flush(); deflater.finish()
// write footer
metaOut.write(-1); metaOut.write(-2)
}
// more data goes here //
metaOut.flush()
metaOut.close()
return listOf(metaOut.toByteArray64()) + outFiles.toList()
}
}