From 057905c3b79099bcfa0c9cb535bdb111d1399004 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 27 Jun 2023 01:21:05 +0900 Subject: [PATCH] thumb generation for player saves --- .../serialise/GameSavingThread.kt | 44 ++++++++++++------ .../serialise/QuickSaveThread.kt | 25 +++++------ .../modulebasegame/serialise/WriteSavegame.kt | 45 +++++++++---------- .../modulebasegame/ui/UILoadSavegame.kt | 3 +- src/net/torvald/terrarum/savegame/VDUtil.kt | 4 +- .../torvald/terrarum/savegame/VirtualDisk.kt | 10 +++-- .../savegame/finder/VirtualDiskCracker.kt | 38 +--------------- work_files/DataFormats/SAVE_FORMAT.md | 1 + 8 files changed, 74 insertions(+), 96 deletions(-) diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt index 16904d516..7507f66dd 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt @@ -14,6 +14,7 @@ import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.toInt import net.torvald.terrarum.savegame.* import net.torvald.terrarum.savegame.VDFileID.LOADORDER +import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT import net.torvald.terrarum.savegame.VDFileID.ROOT import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO import net.torvald.terrarum.savegame.VDFileID.THUMBNAIL @@ -53,7 +54,6 @@ class WorldSavingThread( val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, - val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit, val errorHandler: (Throwable) -> Unit @@ -64,10 +64,8 @@ class WorldSavingThread( disk.saveMode = 2 * isAuto.toInt() // no quick disk.saveKind = VDSaveKind.WORLD_DATA - if (hasThumbnail) { - while (!IngameRenderer.fboRGBexportedLatch) { - Thread.sleep(1L) - } + while (!IngameRenderer.fboRGBexportedLatch) { + Thread.sleep(1L) } val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList() @@ -123,14 +121,14 @@ class WorldSavingThread( - if (hasThumbnail) { - PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) - IngameRenderer.fboRGBexport.dispose() + PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) + IngameRenderer.fboRGBexport.dispose() + + val thumbContent = EntryFile(tgaout.toByteArray64()) + val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent) + addFile(disk, thumb) + - val thumbContent = EntryFile(tgaout.toByteArray64()) - val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent) - addFile(disk, thumb) - } WriteSavegame.saveProgress += 1f @@ -199,7 +197,7 @@ class WorldSavingThread( printdbg(this, "Game saved with size of ${outFile.length()} bytes") - if (hasThumbnail) IngameRenderer.fboRGBexportedLatch = false + IngameRenderer.fboRGBexportedLatch = false WriteSavegame.savingStatus = 255 @@ -217,7 +215,6 @@ class PlayerSavingThread( val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, - val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit, val errorHandler: (Throwable) -> Unit @@ -230,11 +227,30 @@ class PlayerSavingThread( WriteSavegame.saveProgress = 0f + // wait for screencap + while (!IngameRenderer.fboRGBexportedLatch) { + Thread.sleep(1L) + } + + // write screencap + val tgaout = ByteArray64GrowableOutputStream() + val gzout = GZIPOutputStream(tgaout) + PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) + IngameRenderer.fboRGBexport.dispose() + val thumbContent = EntryFile(tgaout.toByteArray64()) + val thumb = DiskEntry(PLAYER_SCREENSHOT, ROOT, ingame.world.creationTime, time_t, thumbContent) + addFile(disk, thumb) + + + printdbg(this, "Writing The Player...") WritePlayer(ingame.actorGamer, disk, ingame, time_t) disk.entries[0]!!.modificationDate = time_t VDUtil.dumpToRealMachine(disk, outFile) + + IngameRenderer.fboRGBexportedLatch = false + callback() } } diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt index 384088566..77a19ae89 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt @@ -24,7 +24,6 @@ class QuickSingleplayerWorldSavingThread( val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, - val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit, val errorHandler: (Throwable) -> Unit @@ -46,14 +45,10 @@ class QuickSingleplayerWorldSavingThread( override fun save() { - printdbg(this, "Quicksaveworld has thumbnail: $hasThumbnail") - val skimmer = DiskSkimmer(outFile) - if (hasThumbnail) { - while (!IngameRenderer.fboRGBexportedLatch) { - Thread.sleep(1L) - } + while (!IngameRenderer.fboRGBexportedLatch) { + Thread.sleep(1L) } val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList() @@ -78,14 +73,14 @@ class QuickSingleplayerWorldSavingThread( val creation_t = ingame.world.creationTime - if (hasThumbnail) { - PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) - IngameRenderer.fboRGBexport.dispose() + PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) + IngameRenderer.fboRGBexport.dispose() + + val thumbContent = EntryFile(tgaout.toByteArray64()) + val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent) + addFile(disk, thumb) + - val thumbContent = EntryFile(tgaout.toByteArray64()) - val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent) - addFile(disk, thumb) - } WriteSavegame.saveProgress += 1f @@ -152,7 +147,7 @@ class QuickSingleplayerWorldSavingThread( printdbg(this, "Game saved with size of ${outFile.length()} bytes") - if (hasThumbnail) IngameRenderer.fboRGBexportedLatch = false + IngameRenderer.fboRGBexportedLatch = false WriteSavegame.savingStatus = 255 ingame.clearModifiedChunks() diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt index 98783b0fe..fa74c4355 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt @@ -36,43 +36,40 @@ object WriteSavegame { @Volatile var saveProgress = 0f @Volatile var saveProgressMax = 1f - private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) = when (mode) { - SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler) - SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler) - SaveMode.QUICK_WORLD -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler) + private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) = when (mode) { + SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler) + SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler) + SaveMode.QUICK_WORLD -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler) else -> throw IllegalArgumentException("$mode") } operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) { savingStatus = 0 - val hasThumbnail = (mode == SaveMode.WORLD || mode == SaveMode.QUICK_WORLD) printdbg(this, "Save queued") - if (hasThumbnail) { - IngameRenderer.screencapExportCallback = { fb -> - printdbg(this, "Generating thumbnail...") + IngameRenderer.screencapExportCallback = { fb -> + printdbg(this, "Generating thumbnail...") - val w = 960 - val h = 640 + val w = 960 + val h = 640 - val cx = /*1-*/(WorldCamera.x % 2) - val cy = /*1-*/(WorldCamera.y % 2) + val cx = /*1-*/(WorldCamera.x % 2) + val cy = /*1-*/(WorldCamera.y % 2) - val x = (fb.width - w) / 2 - cx // force the even-numbered position - val y = (fb.height - h) / 2 - cy // force the even-numbered position + val x = (fb.width - w) / 2 - cx // force the even-numbered position + val y = (fb.height - h) / 2 - cy // force the even-numbered position - val p = Pixmap.createFromFrameBuffer(x, y, w, h) - IngameRenderer.fboRGBexport = p - //PixmapIO2._writeTGA(gzout, p, true, true) - //p.dispose() - IngameRenderer.fboRGBexportedLatch = true + val p = Pixmap.createFromFrameBuffer(x, y, w, h) + IngameRenderer.fboRGBexport = p + //PixmapIO2._writeTGA(gzout, p, true, true) + //p.dispose() + IngameRenderer.fboRGBexportedLatch = true - printdbg(this, "Done thumbnail generation") - } - IngameRenderer.screencapRequested = true + printdbg(this, "Done thumbnail generation") } + IngameRenderer.screencapRequested = true - val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, hasThumbnail, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread") + val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread") savingThread.start() // it is caller's job to keep the game paused or keep a "save in progress" ui up @@ -86,7 +83,7 @@ object WriteSavegame { printdbg(this, "Immediate save fired") - val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, false, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread") + val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread") savingThread.start() // it is caller's job to keep the game paused or keep a "save in progress" ui up diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt index 2c74d1292..04dcba9cc 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt @@ -16,6 +16,7 @@ import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.savegame.ByteArray64InputStream import net.torvald.terrarum.savegame.EntryFile import net.torvald.terrarum.modulebasegame.serialise.LoadSavegame +import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT import net.torvald.terrarum.ui.* import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import java.time.Instant @@ -116,7 +117,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { private val disposablePool = ArrayList() private fun DiskPair.getThumbnail(): TextureRegion { - return this.world.requestFile(-2).let { file -> + return this.player.requestFile(PLAYER_SCREENSHOT).let { file -> CommonResourcePool.getAsTextureRegion("terrarum-defaultsavegamethumb") if (file != null) { diff --git a/src/net/torvald/terrarum/savegame/VDUtil.kt b/src/net/torvald/terrarum/savegame/VDUtil.kt index 8ef4296b4..59305b902 100644 --- a/src/net/torvald/terrarum/savegame/VDUtil.kt +++ b/src/net/torvald/terrarum/savegame/VDUtil.kt @@ -503,8 +503,8 @@ object VDUtil { * Throws an exception if specified size cannot fit into the disk */ fun VirtualDisk.checkCapacity(newSize: Long) { - if (this.usedBytes + newSize > this.capacity) - throw IOException("Not enough space on the disk") +// if (this.usedBytes + newSize > this.capacity) +// throw IOException("Not enough space on the disk") } fun ByteArray64.toIntBig(): Int { if (this.size != 4L) diff --git a/src/net/torvald/terrarum/savegame/VirtualDisk.kt b/src/net/torvald/terrarum/savegame/VirtualDisk.kt index 77e975527..5e50f99e7 100644 --- a/src/net/torvald/terrarum/savegame/VirtualDisk.kt +++ b/src/net/torvald/terrarum/savegame/VirtualDisk.kt @@ -145,9 +145,7 @@ class VirtualDisk( var extraInfoBytes = ByteArray(16) val entries = HashMap() - var isReadOnly: Boolean - set(value) { extraInfoBytes[0] = (extraInfoBytes[0] and 0xFE.toByte()) or value.toBit() } - get() = capacity == 0L || (extraInfoBytes.size > 0 && extraInfoBytes[0].and(1) == 1.toByte()) + val isReadOnly = false var saveMode: Int set(value) { extraInfoBytes[1] = value.toByte() } get() = extraInfoBytes[1].toUint() @@ -260,6 +258,7 @@ object VDFileID { const val SPRITEDEF = -2L const val SPRITEDEF_GLOW = -3L const val LOADORDER = -4L + const val PLAYER_SCREENSHOT = -5L const val BODYPART_TO_ENTRY_MAP = -1025L const val BODYPARTGLOW_TO_ENTRY_MAP = -1026L } @@ -279,6 +278,11 @@ fun diskIDtoReadableFilename(id: EntryID, saveKind: Int?): String = when (id) { "spritedef-glow" else "file #$id" + VDFileID.PLAYER_SCREENSHOT -> + if (saveKind == PLAYER_DATA) + "screenshot.tga.gz" + else + "file #$id" VDFileID.LOADORDER -> "loadOrder.txt" // -16L -> "blockcodex.json.gz" // -17L -> "itemcodex.json.gz" diff --git a/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt b/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt index aecf2c79a..12950f821 100644 --- a/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt +++ b/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt @@ -415,41 +415,6 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() { } } }) - menuEdit.add("Resize Disk…").addMouseListener(object : MouseAdapter() { - override fun mousePressed(e: MouseEvent?) { - if (vdisk != null) { - try { - val dialog = OptionSize() - val confirmed = dialog.showDialog("Input") == JOptionPane.OK_OPTION - if (confirmed) { - vdisk!!.capacity = (dialog.capacity.value as Long).toLong() - updateDiskInfo() - setStat("Disk resized") - } - } - catch (e: Exception) { - e.printStackTrace() - popupError(e.toString()) - } - } - } - }) - menuEdit.addSeparator() - menuEdit.add("Set/Unset Write Protection").addMouseListener(object : MouseAdapter() { - override fun mousePressed(e: MouseEvent?) { - if (vdisk != null) { - try { - vdisk!!.isReadOnly = vdisk!!.isReadOnly.not() - updateDiskInfo() - setStat("Disk write protection ${if (vdisk!!.isReadOnly) "" else "dis"}engaged") - } - catch (e: Exception) { - e.printStackTrace() - popupError(e.toString()) - } - } - } - }) menuBar.add(menuEdit) val menuManage = JMenu("Manage") @@ -638,8 +603,7 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() { } private fun getDiskInfoText(disk: VirtualDisk): String { return """Name: ${String(disk.diskName, sysCharset)} -Capacity: ${disk.capacity} bytes (${disk.usedBytes} bytes used, ${disk.capacity - disk.usedBytes} bytes free) -Write protected: ${disk.isReadOnly.toEnglish()}""" +Capacity: ${disk.capacity} bytes (${disk.usedBytes} bytes used, ${disk.capacity - disk.usedBytes} bytes free)""" } diff --git a/work_files/DataFormats/SAVE_FORMAT.md b/work_files/DataFormats/SAVE_FORMAT.md index c461beb81..de5b0fadc 100644 --- a/work_files/DataFormats/SAVE_FORMAT.md +++ b/work_files/DataFormats/SAVE_FORMAT.md @@ -12,6 +12,7 @@ The main game directory is composed of following directories: [-2] spritedef, [-3] !optional! spritedef-glow, [-4] loadOrder.txt + [-5] screenshot.tga.gz [-1025] !optional! sprite-bodypart-name-to-entry-number-map.properties, [-1026] !optional! spriteglow-bodypart-name-to-entry-number-map.properties, [1+] !optional! bodyparts tga.gz