diff --git a/lib/TerranVirtualDisk-src.jar b/lib/TerranVirtualDisk-src.jar index 3e2bf0a76..eb7a1c3f8 100644 --- a/lib/TerranVirtualDisk-src.jar +++ b/lib/TerranVirtualDisk-src.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3da09b3ca817990bdb640d99e0a3d139a5150fb6305e9b4179aee3a215d382a1 -size 93429 +oid sha256:280e6b9e2c9a1013ae40bcb7fcb04e793f81eee84b9831da1d232c98e90084c6 +size 93474 diff --git a/lib/TerranVirtualDisk.jar b/lib/TerranVirtualDisk.jar index 1f31f72e6..77188e2ef 100644 --- a/lib/TerranVirtualDisk.jar +++ b/lib/TerranVirtualDisk.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:482c9c7cef3cd26c28aa66080990d5e64fbe69601025cedcf4cb376ceae5484d -size 5211888 +oid sha256:ad678b0763421e4c1811e359bdd4b6b7ab353eb087fc7bb39e690244116cd425 +size 5212023 diff --git a/src/net/torvald/terrarum/gameworld/ChunkPool.kt b/src/net/torvald/terrarum/gameworld/ChunkPool.kt index d88c6ba43..7a89f0586 100644 --- a/src/net/torvald/terrarum/gameworld/ChunkPool.kt +++ b/src/net/torvald/terrarum/gameworld/ChunkPool.kt @@ -22,7 +22,7 @@ import java.util.TreeMap * Created by minjaesong on 2024-10-07. */ data class ChunkAllocation( - val chunkNumber: Int, + val chunkNumber: Long, var classifier: ChunkAllocClass, var lastAccessTime: Long = System.nanoTime() ) @@ -42,12 +42,12 @@ enum class ChunkAllocClass { open class ChunkPool( // `DiskSkimmer` or `ClusteredFormatDOM` val disk: Any, + val layerIndex: Int, val wordSizeInBytes: Long, val world: GameWorld, - val chunkNumToFileNum: (Int) -> String, val renumberFun: (Int) -> Int, ) { - private val pointers = TreeMap() + private val pointers = TreeMap() private var allocCap = 32 private var allocMap = Array(allocCap) { null } private var allocCounter = 0 @@ -59,12 +59,12 @@ open class ChunkPool( allocMap.fill(null) } - private fun createPointerViewOfChunk(chunkNumber: Int): UnsafePtr { + private fun createPointerViewOfChunk(chunkNumber: Long): UnsafePtr { val baseAddr = pointers[chunkNumber]!! return UnsafePtr(baseAddr, chunkSize) } - private fun createPointerViewOfChunk(chunkNumber: Int, offsetX: Int, offsetY: Int): Pair { + private fun createPointerViewOfChunk(chunkNumber: Long, offsetX: Int, offsetY: Int): Pair { val baseAddr = pointers[chunkNumber]!! return UnsafePtr(baseAddr, chunkSize) to wordSizeInBytes * (offsetY * CHUNK_W + offsetX) } @@ -72,7 +72,7 @@ open class ChunkPool( /** * If the chunk number == playerChunkNum, the result will be `false` */ - private fun Int.isChunkNearPlayer(playerChunkNum: Int): Boolean { + private fun Long.isChunkNearPlayer(playerChunkNum: Long): Boolean { if (this == playerChunkNum) return false val pxy = LandUtil.chunkNumToChunkXY(world, playerChunkNum) @@ -124,7 +124,7 @@ open class ChunkPool( } } - private fun allocate(chunkNumber: Int, allocClass: ChunkAllocClass = ChunkAllocClass.TEMPORARY): UnsafePtr { + private fun allocate(chunkNumber: Long, allocClass: ChunkAllocClass = ChunkAllocClass.TEMPORARY): UnsafePtr { updateAllocMapUsingIngamePlayer() // find the empty spot within the pool @@ -141,7 +141,7 @@ open class ChunkPool( private fun deallocate(allocation: ChunkAllocation) = deallocate(allocation.chunkNumber) - private fun deallocate(chunkNumber: Int): Boolean { + private fun deallocate(chunkNumber: Long): Boolean { val ptr = pointers[chunkNumber] ?: return false storeToDisk(chunkNumber) @@ -168,8 +168,8 @@ open class ChunkPool( } } - private fun fetchFromDisk(chunkNumber: Int) { - val fileName = chunkNumToFileNum(chunkNumber) + private fun fetchFromDisk(chunkNumber: Long) { + val fileName = chunkNumToFileNum(layerIndex, chunkNumber) // read data from the disk if (disk is ClusteredFormatDOM) { @@ -191,8 +191,8 @@ open class ChunkPool( } } - private fun storeToDisk(chunkNumber: Int) { - val fileName = chunkNumToFileNum(chunkNumber) + private fun storeToDisk(chunkNumber: Long) { + val fileName = chunkNumToFileNum(layerIndex, chunkNumber) // write to the disk (the disk must be an autosaving copy of the original) if (disk is ClusteredFormatDOM) { @@ -212,13 +212,13 @@ open class ChunkPool( } } - private fun checkForChunk(chunkNumber: Int) { + private fun checkForChunk(chunkNumber: Long) { if (!pointers.containsKey(chunkNumber)) { fetchFromDisk(chunkNumber) } } - private fun serialise(chunkNumber: Int): ByteArray { + private fun serialise(chunkNumber: Long): ByteArray { val ptr = pointers[chunkNumber]!! val out = ByteArray(chunkSize.toInt()) UnsafeHelper.memcpyFromPtrToArr(ptr, out, 0, chunkSize) @@ -233,7 +233,7 @@ open class ChunkPool( * - word size is 3: Int `00_B2_B1_B0` * - word size is 2: Int `00_00_B1_B0` */ - fun getTileRaw(chunkNumber: Int, offX: Int, offY: Int): Int { + fun getTileRaw(chunkNumber: Long, offX: Int, offY: Int): Int { checkForChunk(chunkNumber) allocMap.find { it?.chunkNumber == chunkNumber }!!.let { it.lastAccessTime = System.nanoTime() } val (ptr, ptrOff) = createPointerViewOfChunk(chunkNumber, offX, offY) @@ -249,7 +249,7 @@ open class ChunkPool( * - First element: Int `00_00_B1_B0` * - Second element: Float16(`B3_B2`).toFloat32 */ - fun getTileI16F16(chunkNumber: Int, offX: Int, offY: Int): Pair { + fun getTileI16F16(chunkNumber: Long, offX: Int, offY: Int): Pair { val raw = getTileRaw(chunkNumber, offX, offY) val ibits = raw.get1SS() val fbits = raw.get2SS().toShort() @@ -261,7 +261,7 @@ open class ChunkPool( * Return format: * - Int `00_00_B1_B0` */ - fun getTileI16(chunkNumber: Int, offX: Int, offY: Int): Int { + fun getTileI16(chunkNumber: Long, offX: Int, offY: Int): Int { val raw = getTileRaw(chunkNumber, offX, offY) val ibits = raw.get1SS() return ibits @@ -273,7 +273,7 @@ open class ChunkPool( * - First element: Int `00_00_B1_B0` * - Second element: Int `00_00_00_B2` */ - fun getTileI16I8(chunkNumber: Int, offX: Int, offY: Int): Pair { + fun getTileI16I8(chunkNumber: Long, offX: Int, offY: Int): Pair { val raw = getTileRaw(chunkNumber, offX, offY) val ibits = raw.get1SS() val jbits = raw.get2SS() and 255 @@ -281,6 +281,11 @@ open class ChunkPool( } companion object { + fun chunkNumToFileNum(layerNum: Int, chunkNum: Long): String { + val entryID = Common.layerAndChunkNumToEntryID(layerNum, chunkNum) + return Common.type254EntryIDtoType17Filename(entryID) + } + private fun Int.get1SS() = this and 65535 private fun Int.get2SS() = (this ushr 16) and 65535 diff --git a/src/net/torvald/terrarum/modulebasegame/SavegameConverter.kt b/src/net/torvald/terrarum/modulebasegame/SavegameConverter.kt new file mode 100644 index 000000000..de7629894 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/SavegameConverter.kt @@ -0,0 +1,75 @@ +package net.torvald.terrarum.modulebasegame + +import net.torvald.reflection.extortField +import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM +import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustfile +import net.torvald.terrarum.savegame.* +import net.torvald.terrarum.serialise.Common +import net.torvald.terrarum.serialise.toBig64 +import java.io.File + +/** + * Created by minjaesong on 2024-10-08. + */ +object SavegameConverter { + + fun type254toType11(infile: File, outFile: File) { + val type254DOM = VDUtil.readDiskArchive(infile) + type254DOMtoType11Disk(type254DOM, outFile) + } + + private val getGenver = Regex("""(?<="genver" ?: ?)[0-9]+""") + + private fun type254DOMtoType11Disk(type254DOM: VirtualDisk, outFile: File) { + val version: Long = when (type254DOM.saveKind) { + VDSaveKind.PLAYER_DATA, VDSaveKind.WORLD_DATA -> { + val savegameInfo = ByteArray64Reader(type254DOM.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET) + CharArray(128).let { + savegameInfo.read(it, 0, 128) + getGenver.find(String(it))?.value?.toLong()!! + } + } + else -> 0L + } + + val newDisk = ClusteredFormatDOM.createNewArchive( + outFile, + Common.CHARSET, + type254DOM.getDiskName(Common.CHARSET), + ClusteredFormatDOM.MAX_CAPA_IN_SECTORS, + byteArrayOf( + -1, // pad + byteArrayOf(15, -1)[type254DOM.saveOrigin.ushr(4).and(1)], // imported/native + byteArrayOf(0x00, 0x50, 0x57)[type254DOM.saveKind], // player/world + byteArrayOf(0x6d, 0x6d, 0x61, 0x61)[type254DOM.saveMode], // manual/auto + type254DOM.extraInfoBytes[4], type254DOM.extraInfoBytes[5], // snapshot info + 0, 0,// reserved + ) + version.toBig64() // VERSION_RAW in big-endian + ) + val DOM = ClusteredFormatDOM(newDisk) + val root = DOM.getRootFile() + + // do filecopy + type254DOM.entries.filter { it.key != 0L }.forEach { entryID, diskEntry -> + val filename = Common.type254EntryIDtoType17Filename(entryID) + if (diskEntry.contents !is EntryFile) throw IllegalStateException("Entry in the savegame is not a file (${diskEntry.contents.javaClass.simpleName})") + + val entry = diskEntry.contents as EntryFile + + val oldBytes = entry.bytes.toByteArray() + + Clustfile(DOM, root, filename).let { file -> + // write bytes + file.createNewFile() + file.writeBytes(oldBytes) + // modify attributes + val FAT = file.extortField("FAT")!! + FAT.creationDate = diskEntry.creationDate + FAT.modificationDate = diskEntry.modificationDate + } + } + + DOM.dispose() + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt new file mode 100644 index 000000000..e28d3b1ce --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/serialise/LoadSavegame.kt @@ -0,0 +1,141 @@ +package net.torvald.terrarum.modulebasegame.serialise + +import net.torvald.terrarum.* +import net.torvald.terrarum.console.Echo +import net.torvald.terrarum.gameworld.BlockLayerI16 +import net.torvald.terrarum.gameworld.BlockLayerI16F16 +import net.torvald.terrarum.gameworld.BlockLayerOresI16I8 +import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.langpack.Lang +import net.torvald.terrarum.modulebasegame.FancyWorldReadLoadScreen +import net.torvald.terrarum.modulebasegame.TerrarumIngame +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer +import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.savegame.* +import net.torvald.terrarum.serialise.Common +import java.io.Reader +import java.util.logging.Level +import kotlin.experimental.or + +/** + * Load and setup the game for the first load. + * + * To load additional actors/worlds, use ReadActor/ReadWorld. + * + * Created by minjaesong on 2021-09-03. + */ +object LoadSavegame { + + fun getWorldName(worldDisk: SimpleFileSystem) = worldDisk.getDiskName(Common.CHARSET) + fun getWorldSavefileName(world: GameWorld) = "${world.worldIndex}" + fun getPlayerSavefileName(player: IngamePlayer) = "${player.uuid}" + + fun getFileBytes(disk: SimpleFileSystem, id: Long): ByteArray64 = disk.getFile(id)!!.bytes + fun getFileReader(disk: SimpleFileSystem, id: Long): Reader = + ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET) + + operator fun invoke(diskPair: DiskPair) = invoke(diskPair.player, diskPair.world) + + private val getGenver = Regex("""(?<="genver" ?: ?)[0-9]+""") + + /** + * @param playerDisk DiskSkimmer representing the Player. + * @param worldDisk0 DiskSkimmer representing the World to be loaded. + * If unset, last played world for the Player will be loaded. + */ + operator fun invoke(playerDisk: DiskSkimmer, worldDisk0: DiskSkimmer? = null) { + val newIngame = TerrarumIngame(App.batch) + playerDisk.rebuild() + + val playerDiskSavegameInfo = + ByteArray64Reader(playerDisk.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET) + + val player = ReadActor.invoke(playerDisk, playerDiskSavegameInfo) as IngamePlayer + + App.printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}") + + val currentWorldId = player.worldCurrentlyPlaying + val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable() + worldDisk.rebuild() + val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET) + val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile) + + + world.layerTerrain = BlockLayerI16(world.width, world.height) + world.layerWall = BlockLayerI16(world.width, world.height) + world.layerOres = BlockLayerOresI16I8(world.width, world.height) + world.layerFluids = BlockLayerI16F16(world.width, world.height) + world.chunkFlags = Array(world.height / LandUtil.CHUNK_H) { ByteArray(world.width / LandUtil.CHUNK_W) } + + newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed + newIngame.worldDisk = VDUtil.readDiskArchive(worldDisk.diskFile, Level.INFO) + newIngame.playerDisk = VDUtil.readDiskArchive(playerDisk.diskFile, Level.INFO) + newIngame.worldName = getWorldName(worldDisk) + newIngame.worldSavefileName = getWorldSavefileName(world) + newIngame.playerSavefileName = getPlayerSavefileName(player) + +// worldDisk.dispose() +// playerDisk.dispose() + + + val loadJob = { it: LoadScreenBase -> + val loadscreen = it as FancyWorldReadLoadScreen + loadscreen.addMessage(Lang["MENU_IO_LOADING"]) + + + val worldGenver = CharArray(128).let { + worldDiskSavegameInfo.read(it, 0, 128) + getGenver.find(String(it))?.value?.toLong()!! + } + val playerGenver = CharArray(128).let { + playerDiskSavegameInfo.read(it, 0, 128) + getGenver.find(String(it))?.value?.toLong()!! + } + + + val actors = world.actors.distinct() + val worldParam = + TerrarumIngame.Codices(newIngame.worldDisk, world, actors, player, worldGenver, playerGenver) {} + + + newIngame.gameLoadInfoPayload = worldParam + newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM + + App.printdbg( + this, + "World dim: ${world.width}x${world.height}, ${world.width / LandUtil.CHUNK_W}x${world.height / LandUtil.CHUNK_H}" + ) + + // load all the world blocklayer chunks + val cw = LandUtil.CHUNK_W + val ch = LandUtil.CHUNK_H + val chunkCount = world.width.toLong() * world.height / (cw * ch) + val worldLayer = intArrayOf(GameWorld.TERRAIN, GameWorld.WALL, GameWorld.ORES, GameWorld.FLUID).map { world.getLayer(it) } + for (chunk in 0L until chunkCount) { + for (layer in worldLayer.indices) { + loadscreen.addMessage(Lang["MENU_IO_LOADING"]) + + newIngame.worldDisk.getFile(Common.layerAndChunkNumToEntryID(layer, chunk))?.let { chunkFile -> + val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk) + + ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy) + world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or GameWorld.CHUNK_LOADED + } + } + loadscreen.progress.getAndAdd(1) + } + + loadscreen.addMessage(Lang["MENU_IO_LOAD_UPDATING_BLOCK_MAPPINGS"]) + world.renumberTilesAfterLoad() + + + Echo("${ccW}World loaded: $ccY${newIngame.worldDisk.getDiskName(Common.CHARSET)}") + App.printdbg(this, "World loaded: ${newIngame.worldDisk.getDiskName(Common.CHARSET)}") + } + + val loadScreen = FancyWorldReadLoadScreen(newIngame, world.width, world.height, loadJob) + Terrarum.setCurrentIngameInstance(newIngame) + App.setLoadScreen(loadScreen) + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt index 9a60bafc2..683b0c440 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt @@ -136,13 +136,13 @@ class WorldSavingThread( for (cx in 0 until cw) { for (cy in 0 until ch) { val chunkFlag = ingame.world.chunkFlags[cy][cx] - val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong() + val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy) if (chunkFlag and 0x7F == CHUNK_LOADED) { // Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}") val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy) - val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber + val entryID = Common.layerAndChunkNumToEntryID(layer, chunkNumber) val entryContent = EntryFile(chunkBytes) val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent) diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt index 05759d891..0f95805cc 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt @@ -1,32 +1,12 @@ package net.torvald.terrarum.modulebasegame.serialise import com.badlogic.gdx.graphics.Pixmap -import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg -import net.torvald.terrarum.console.Echo -import net.torvald.terrarum.gameworld.BlockLayerI16 -import net.torvald.terrarum.gameworld.BlockLayerI16F16 -import net.torvald.terrarum.gameworld.BlockLayerOresI16I8 -import net.torvald.terrarum.gameworld.GameWorld -import net.torvald.terrarum.gameworld.GameWorld.Companion.CHUNK_LOADED -import net.torvald.terrarum.gameworld.GameWorld.Companion.FLUID -import net.torvald.terrarum.gameworld.GameWorld.Companion.ORES -import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN -import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL -import net.torvald.terrarum.langpack.Lang -import net.torvald.terrarum.modulebasegame.FancyWorldReadLoadScreen import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.TerrarumIngame -import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer -import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.savegame.* -import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO -import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.worlddrawer.WorldCamera import java.io.File -import java.io.Reader -import java.util.logging.Level -import kotlin.experimental.or /** * It's your responsibility to create a new VirtualDisk if your save is new, and create a backup for modifying existing save. @@ -106,123 +86,3 @@ object WriteSavegame { } } - - - - -/** - * Load and setup the game for the first load. - * - * To load additional actors/worlds, use ReadActor/ReadWorld. - * - * Created by minjaesong on 2021-09-03. - */ -object LoadSavegame { - - fun getWorldName(worldDisk: SimpleFileSystem) = worldDisk.getDiskName(Common.CHARSET) - fun getWorldSavefileName(world: GameWorld) = "${world.worldIndex}" - fun getPlayerSavefileName(player: IngamePlayer) = "${player.uuid}" - - fun getFileBytes(disk: SimpleFileSystem, id: Long): ByteArray64 = disk.getFile(id)!!.bytes - fun getFileReader(disk: SimpleFileSystem, id: Long): Reader = ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET) - - operator fun invoke(diskPair: DiskPair) = invoke(diskPair.player, diskPair.world) - - private val getGenver = Regex("""(?<="genver" ?: ?)[0-9]+""") - - /** - * @param playerDisk DiskSkimmer representing the Player. - * @param worldDisk0 DiskSkimmer representing the World to be loaded. - * If unset, last played world for the Player will be loaded. - */ - operator fun invoke(playerDisk: DiskSkimmer, worldDisk0: DiskSkimmer? = null) { - val newIngame = TerrarumIngame(App.batch) - playerDisk.rebuild() - - val playerDiskSavegameInfo = ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET) - - val player = ReadActor.invoke(playerDisk, playerDiskSavegameInfo) as IngamePlayer - - printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}") - - val currentWorldId = player.worldCurrentlyPlaying - val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable() - worldDisk.rebuild() - val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET) - val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile) - - - world.layerTerrain = BlockLayerI16(world.width, world.height) - world.layerWall = BlockLayerI16(world.width, world.height) - world.layerOres = BlockLayerOresI16I8(world.width, world.height) - world.layerFluids = BlockLayerI16F16(world.width, world.height) - world.chunkFlags = Array(world.height / LandUtil.CHUNK_H) { ByteArray(world.width / LandUtil.CHUNK_W) } - - newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed - newIngame.worldDisk = VDUtil.readDiskArchive(worldDisk.diskFile, Level.INFO) - newIngame.playerDisk = VDUtil.readDiskArchive(playerDisk.diskFile, Level.INFO) - newIngame.worldName = getWorldName(worldDisk) - newIngame.worldSavefileName = getWorldSavefileName(world) - newIngame.playerSavefileName = getPlayerSavefileName(player) - -// worldDisk.dispose() -// playerDisk.dispose() - - - val loadJob = { it: LoadScreenBase -> - val loadscreen = it as FancyWorldReadLoadScreen - loadscreen.addMessage(Lang["MENU_IO_LOADING"]) - - - val worldGenver = CharArray(128).let { - worldDiskSavegameInfo.read(it, 0, 128) - getGenver.find(String(it))?.value?.toLong()!! - } - val playerGenver = CharArray(128).let { - playerDiskSavegameInfo.read(it, 0, 128) - getGenver.find(String(it))?.value?.toLong()!! - } - - - val actors = world.actors.distinct() - val worldParam = TerrarumIngame.Codices(newIngame.worldDisk, world, actors, player, worldGenver, playerGenver) {} - - - newIngame.gameLoadInfoPayload = worldParam - newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM - - printdbg(this, "World dim: ${world.width}x${world.height}, ${world.width / LandUtil.CHUNK_W}x${world.height / LandUtil.CHUNK_H}") - - // load all the world blocklayer chunks - val cw = LandUtil.CHUNK_W - val ch = LandUtil.CHUNK_H - val chunkCount = world.width * world.height / (cw * ch) - val worldLayer = intArrayOf(TERRAIN, WALL, ORES, FLUID).map { world.getLayer(it) } - for (chunk in 0L until chunkCount) { - for (layer in worldLayer.indices) { - loadscreen.addMessage(Lang["MENU_IO_LOADING"]) - - newIngame.worldDisk.getFile(0x1_0000_0000L or layer.toLong().shl(24) or chunk)?.let { chunkFile -> - val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk.toInt()) - - ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy) - world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or CHUNK_LOADED - } - } - loadscreen.progress.getAndAdd(1) - } - - loadscreen.addMessage(Lang["MENU_IO_LOAD_UPDATING_BLOCK_MAPPINGS"]) - world.renumberTilesAfterLoad() - - - Echo("${ccW}World loaded: $ccY${newIngame.worldDisk.getDiskName(Common.CHARSET)}") - printdbg(this, "World loaded: ${newIngame.worldDisk.getDiskName(Common.CHARSET)}") - } - - val loadScreen = FancyWorldReadLoadScreen(newIngame, world.width, world.height, loadJob) - Terrarum.setCurrentIngameInstance(newIngame) - App.setLoadScreen(loadScreen) - } - -} \ No newline at end of file diff --git a/src/net/torvald/terrarum/realestate/LandUtil.kt b/src/net/torvald/terrarum/realestate/LandUtil.kt index 66de89133..fd0013f81 100644 --- a/src/net/torvald/terrarum/realestate/LandUtil.kt +++ b/src/net/torvald/terrarum/realestate/LandUtil.kt @@ -20,7 +20,7 @@ object LandUtil { const val LAYER_WIRE = 20 const val LAYER_FLUID = 3 - fun toChunkNum(world: GameWorld, x: Int, y: Int): Int { + fun toChunkNum(world: GameWorld, x: Int, y: Int): Long { // coercing and fmod-ing follows ROUNDWORLD rule. See: GameWorld.coerceXY() val (x, y) = world.coerceXY(x, y) return chunkXYtoChunkNum(world, x / CHUNK_W, y / CHUNK_H) @@ -32,13 +32,13 @@ object LandUtil { return Point2i(x / CHUNK_W, y / CHUNK_H) } - fun chunkXYtoChunkNum(world: GameWorld, cx: Int, cy: Int): Int { - val ch = world.height / CHUNK_H + fun chunkXYtoChunkNum(world: GameWorld, cx: Int, cy: Int): Long { + val ch = (world.height / CHUNK_H).toLong() return cx * ch + cy } - fun chunkNumToChunkXY(world: GameWorld, chunkNum: Int): Point2i { + fun chunkNumToChunkXY(world: GameWorld, chunkNum: Long): Point2i { val ch = world.height / CHUNK_H - return Point2i(chunkNum / ch, chunkNum % ch) + return Point2i((chunkNum / ch).toInt(), (chunkNum % ch).toInt()) } fun getBlockAddr(world: GameWorld, x: Int, y: Int): BlockAddress { diff --git a/src/net/torvald/terrarum/savegame/VirtualDisk.kt b/src/net/torvald/terrarum/savegame/VirtualDisk.kt index ae0b44b26..0f747adcc 100644 --- a/src/net/torvald/terrarum/savegame/VirtualDisk.kt +++ b/src/net/torvald/terrarum/savegame/VirtualDisk.kt @@ -161,13 +161,13 @@ class VirtualDisk( var extraInfoBytes = ByteArray(16) val entries = HashMap() val isReadOnly = false - var saveMode: Int + var saveMode: Int // auto? quick? set(value) { extraInfoBytes[1] = value.toByte() } get() = extraInfoBytes[1].toUint() - var saveKind: Int + var saveKind: Int // player? world? set(value) { extraInfoBytes[2] = value.toByte() } get() = extraInfoBytes[2].toUint() - var saveOrigin: Int + var saveOrigin: Int // imported? native? set(value) { extraInfoBytes[3] = value.toByte() } get() = extraInfoBytes[3].toUint() var snapshot: Snapshot? diff --git a/src/net/torvald/terrarum/serialise/Common.kt b/src/net/torvald/terrarum/serialise/Common.kt index 75113c9dc..3ed52e6e5 100644 --- a/src/net/torvald/terrarum/serialise/Common.kt +++ b/src/net/torvald/terrarum/serialise/Common.kt @@ -755,6 +755,14 @@ object Common { return UUID(bytes.toBigInt64(0), bytes.toBigInt64(8)) } + + fun layerAndChunkNumToEntryID(layerNum: Int, chunkNum: Long) = + 0x1_0000_0000L or layerNum.toLong().shl(24) or chunkNum + + + fun type254EntryIDtoType17Filename(entryID: Long): String { + return entryID.toString(16).uppercase().padStart(16,'0') + } } class SaveLoadError(file: File?, cause: Throwable) : RuntimeException("An error occured while loading save file '${file?.absolutePath}'", cause) \ No newline at end of file