thumb generation for player saves

This commit is contained in:
minjaesong
2023-06-27 01:21:05 +09:00
parent 2b50562002
commit 057905c3b7
8 changed files with 74 additions and 96 deletions

View File

@@ -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()
}
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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<Disposable>()
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) {

View File

@@ -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)

View File

@@ -145,9 +145,7 @@ class VirtualDisk(
var extraInfoBytes = ByteArray(16)
val entries = HashMap<EntryID, DiskEntry>()
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"

View File

@@ -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)"""
}

View File

@@ -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