detects corrupted and damaged and deleted and whatever savefiles; uiitem will call show() and hide() upon opening and closing

This commit is contained in:
minjaesong
2021-10-01 15:46:25 +09:00
parent 3a98bef509
commit 88c76105d2
12 changed files with 169 additions and 90 deletions

View File

@@ -10,13 +10,15 @@ uniform sampler2D u_texture;
// "steps" of R, G and B. Must be integer && equal or greater than 2
uniform float rcount = 32.0;
uniform float gcount = 64.0;
uniform float bcount = 32.0;
uniform float rcount = 8.0;
uniform float gcount = 8.0;
uniform float bcount = 8.0;
uniform float acount = 1.0;
int bayer[14 * 14] = int[](131,187,8,78,50,18,134,89,155,102,29,95,184,73,22,86,113,171,142,105,34,166,9,60,151,128,40,110,168,137,45,28,64,188,82,54,124,189,80,13,156,56,7,61,186,121,154,6,108,177,24,100,38,176,93,123,83,148,96,17,88,133,44,145,69,161,139,72,30,181,115,27,163,47,178,65,164,14,120,48,5,127,153,52,190,58,126,81,116,21,106,77,173,92,191,63,99,12,76,144,4,185,37,149,192,39,135,23,117,31,170,132,35,172,103,66,129,79,3,97,57,159,70,141,53,94,114,20,49,158,19,146,169,122,183,11,104,180,2,165,152,87,182,118,91,42,67,25,84,147,43,85,125,68,16,136,71,10,193,112,160,138,51,111,162,26,194,46,174,107,41,143,33,74,1,101,195,15,75,140,109,90,32,62,157,98,167,119,179,59,36,130,175,55,0,150);
float bayerSize = 14.0;
//int bayer[14 * 14] = int[](131,187,8,78,50,18,134,89,155,102,29,95,184,73,22,86,113,171,142,105,34,166,9,60,151,128,40,110,168,137,45,28,64,188,82,54,124,189,80,13,156,56,7,61,186,121,154,6,108,177,24,100,38,176,93,123,83,148,96,17,88,133,44,145,69,161,139,72,30,181,115,27,163,47,178,65,164,14,120,48,5,127,153,52,190,58,126,81,116,21,106,77,173,92,191,63,99,12,76,144,4,185,37,149,192,39,135,23,117,31,170,132,35,172,103,66,129,79,3,97,57,159,70,141,53,94,114,20,49,158,19,146,169,122,183,11,104,180,2,165,152,87,182,118,91,42,67,25,84,147,43,85,125,68,16,136,71,10,193,112,160,138,51,111,162,26,194,46,174,107,41,143,33,74,1,101,195,15,75,140,109,90,32,62,157,98,167,119,179,59,36,130,175,55,0,150);
//float bayerSize = 14.0;
int bayer[4 * 4] = int[](0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5);
float bayerSize = 4.0;
float bayerDivider = bayerSize * bayerSize;

View File

@@ -33,6 +33,7 @@ import net.torvald.terrarum.modulebasegame.TerrarumIngame;
import net.torvald.terrarum.modulebasegame.ui.ItemSlotImageFactory;
import net.torvald.terrarum.serialise.WriteConfig;
import net.torvald.terrarum.serialise.WriteMeta;
import net.torvald.terrarum.tvda.DiskSkimmer;
import net.torvald.terrarum.tvda.VirtualDisk;
import net.torvald.terrarum.utils.JsonFetcher;
import net.torvald.terrarum.worlddrawer.CreateTileAtlas;
@@ -196,7 +197,7 @@ public class App implements ApplicationListener {
/**
* Sorted by the lastplaytime, in reverse order (index 0 is the most recent game played)
*/
public static ArrayList<Pair<File, WriteMeta.WorldMeta>> savegames = new ArrayList<>();
public static ArrayList<DiskSkimmer> savegames = new ArrayList<>();
public static void updateListOfSavegames() {
AppUpdateListOfSavegames();
@@ -301,7 +302,6 @@ public class App implements ApplicationListener {
createDirs();
initialiseConfig();
readConfigJson();
updateListOfSavegames();
setGamepadButtonLabels();

View File

@@ -22,15 +22,13 @@ import net.torvald.terrarum.blockproperties.WireCodex
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.faction.FactionCodex
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.ReadMeta
import net.torvald.terrarum.tvda.DiskSkimmer
import net.torvald.terrarum.tvda.EntryFile
import net.torvald.terrarum.tvda.VDUtil
import net.torvald.terrarum.tvda.VirtualDisk
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.serialise.*
import net.torvald.terrarum.tvda.*
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.GameFontBase
@@ -520,7 +518,7 @@ val ccK = GameFontBase.toColorCode(0xF888)
val ccE = GameFontBase.toColorCode(0xFBBB)
// Zelda-esque text colour emphasis
val emphStrong = GameFontBase.toColorCode(0xFF88)
val emphRed = GameFontBase.toColorCode(0xFF88)
val emphObj = GameFontBase.toColorCode(0xF0FF)
val emphVerb = GameFontBase.toColorCode(0xFFF6)
@@ -687,39 +685,52 @@ fun AppUpdateListOfSavegames() {
App.savegames.clear()
File(App.defaultSaveDir).listFiles().filter { !it.isDirectory && !it.name.contains('.') }.map { file ->
try {
DiskSkimmer(file, Common.CHARSET) { it.containsKey(-1) }.requestFile(-1)?.let {
file to ReadMeta.fromDiskEntry(it)
}
DiskSkimmer(file, Common.CHARSET, true)
}
catch (e: Throwable) {
System.err.println("Unable to load a savefile ${file.absolutePath}")
e.printStackTrace()
null
}
}.filter { it != null }.sortedByDescending { it!!.second.lastplay_t }.forEach {
}.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() }.forEach {
App.savegames.add(it!!)
}
}
/**
* @param skimmer loaded with the savefile
* @param skimmer loaded with the savefile, rebuilt/updated beforehand
*/
fun checkForSavegameDamage(skimmer: DiskSkimmer): Boolean {
// # check if The Player is there
val player = skimmer.requestFile(PLAYER_REF_ID.toLong().and(0xFFFFFFFFL))?.contents ?: return true
// # check if:
// the world The Player is at actually exists
// all the actors for the world actually exists
val currentWorld = (player as EntryFile).bytes.let {
val maxsize = 1 shl 30
val worldIndexRegex = Regex("""worldIndex: ?([0-9]+)""")
val rawJson = it.sliceArray(0 until minOf(maxsize, if (it.size >= maxsize) maxsize else it.size.toInt())).toString(Common.CHARSET)
try {
// # check for meta
val metaFile = skimmer.requestFile(-1) ?: return true
// # check if The Player is there
val player = skimmer.requestFile(PLAYER_REF_ID.toLong().and(0xFFFFFFFFL))?.contents ?: return true
// # check if:
// the world The Player is at actually exists
// all the actors for the world actually exists
// TODO might want SAX parser for JSON
val currentWorld = (player as EntryFile).bytes.let {
(ReadActor.readActorBare(ByteArray64Reader(it, Common.CHARSET)) as? IngamePlayer
?: return true).worldCurrentlyPlaying
}
if (currentWorld == 0) return true
val worldData = (skimmer.requestFile(currentWorld.toLong())?.contents as? EntryFile)?.bytes ?: return true
val world = Common.jsoner.fromJson(GameWorld::class.java, ByteArray64Reader(worldData, Common.CHARSET))
// todo
var hasMissingActor = false
world.actors.forEach {
if (!skimmer.hasEntry(it.toLong().and(0xFFFFFFFFL))) {
System.err.println("Nonexisting actor $it for savegame ${skimmer.diskFile.absolutePath}")
hasMissingActor = true
}
}; if (hasMissingActor) return true
return false
}
catch (e: Throwable) {
e.printStackTrace()
return true
}
// skimmer.requestFile(367228416) ?: return true
return false
}

View File

@@ -218,11 +218,14 @@ class TitleScreen(batch: SpriteBatch) : IngameInstance(batch) {
worldFBO = FrameBuffer(Pixmap.Format.RGBA8888, App.scr.width, App.scr.height, false)
loadThingsWhileIntroIsVisible()
// load list of savegames
println("[TitleScreen] update list of savegames")
App.updateListOfSavegames()
// App.savegames.forEach { println(it.diskFile.absolutePath) }
loadThingsWhileIntroIsVisible()
printdbg(this, "show() exit")
}

View File

@@ -31,7 +31,7 @@ class UIFakeGradOverlay : UICanvas() {
override fun updateUI(delta: Float) {}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
blendMul(batch)
batch.draw(tex, 0f, 0f, App.scr.halfwf * 1.5f, App.scr.hf)
batch.draw(tex, 0f, 0f, App.scr.wf, App.scr.hf)
blendNormal(batch)
}

View File

@@ -10,6 +10,7 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import net.torvald.getKeycapConsole
import net.torvald.getKeycapPC
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.LoadSavegame
@@ -73,14 +74,6 @@ class UILoadDemoSavefiles : UICanvas() {
else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
// read savegames
init {
App.savegames.forEachIndexed { index, fileMetaPair ->
val x = uiX
val y = titleTopGradEnd + cellInterval * index
addUIitem(UIItemDemoSaveCells(this, x, y, fileMetaPair.first, fileMetaPair.second))
}
}
private var scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
private var listScroll = 0 // only update when animation is finished
@@ -94,6 +87,31 @@ class UILoadDemoSavefiles : UICanvas() {
private var sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, true)
override fun show() {
printdbg(this, "savefiles show()")
// read savegames
var savegamesCount = 0
App.savegames.forEach { skimmer ->
val x = uiX
val y = titleTopGradEnd + cellInterval * savegamesCount
try {
addUIitem(UIItemDemoSaveCells(this, x, y, skimmer))
savegamesCount += 1
}
catch (e: Throwable) {
System.err.println("[UILoadDemoSavefiles] Savefile '${skimmer.diskFile.absolutePath}' cannot be loaded")
e.printStackTrace()
}
}
}
override fun hide() {
uiItems.forEach { it.dispose() }
uiItems.clear()
}
override fun updateUI(delta: Float) {
if (scrollTarget != listScroll) {
@@ -276,24 +294,28 @@ class UIItemDemoSaveCells(
parent: UILoadDemoSavefiles,
initialX: Int,
initialY: Int,
val diskfile: File, val meta: WriteMeta.WorldMeta) : UIItem(parent, initialX, initialY) {
private val skimmer = DiskSkimmer(diskfile, Common.CHARSET)
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
companion object {
const val WIDTH = 480
const val HEIGHT = 120
}
init {
printdbg(this, "Rebuilding skimmer for savefile ${skimmer.diskFile.absolutePath}")
skimmer.rebuild()
}
private var saveDamaged = checkForSavegameDamage(skimmer)
override val width: Int = WIDTH
override val height: Int = HEIGHT
private lateinit var thumbPixmap: Pixmap
private var thumbPixmap: Pixmap? = null
private var thumb: TextureRegion? = null
private val grad = CommonResourcePool.getAsTexture("title_halfgrad")
private var saveDamaged = checkForSavegameDamage(skimmer)
private fun parseDuration(seconds: Long): String {
val s = seconds % 60
val m = (seconds / 60) % 60
@@ -305,18 +327,25 @@ class UIItemDemoSaveCells(
"${d}d${h.toString().padStart(2,'0')}h${m.toString().padStart(2,'0')}m${s.toString().padStart(2,'0')}s"
}
private val metaFile = skimmer.requestFile(-1)
private val saveName = skimmer.getDiskName(Common.CHARSET)
private val saveMode = skimmer.getSaveMode()
private val meta = if (metaFile != null) ReadMeta.fromDiskEntry(metaFile) else null
private val lastPlayedTimestamp = Instant.ofEpochSecond(meta.lastplay_t)
private val colourBad = Color(0xFF8888FF.toInt())
private val lastPlayedTimestamp = if (meta != null)
Instant.ofEpochSecond(meta.lastplay_t)
.atZone(TimeZone.getDefault().toZoneId())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
"/${parseDuration(meta.playtime_t)}"
else "--:--:--/--h--m--s"
init {
try {
// load a thumbnail
val zippedTga = (skimmer.requestFile(-2)!!.contents as EntryFile).bytes
// load thumbnail or use stock if the file is not there
skimmer.requestFile(-2)?.let {
val zippedTga = (it.contents as EntryFile).bytes
val gzin = GZIPInputStream(ByteArray64InputStream(zippedTga))
val tgaFileContents = gzin.readAllBytes(); gzin.close()
@@ -326,15 +355,11 @@ class UIItemDemoSaveCells(
thumb = TextureRegion(thumbTex)
thumb!!.setRegion(0, (thumbTex.height - 2 * height) / 2, width * 2, height * 2)
}
catch (e: NullPointerException) {
// use stock texture
}
}
override var clickOnceListener: ((Int, Int, Int) -> Unit)? = { _: Int, _: Int, _: Int ->
LoadSavegame(VDUtil.readDiskArchive(diskfile, Level.INFO))
LoadSavegame(VDUtil.readDiskArchive(skimmer.diskFile, Level.INFO))
}
override fun render(batch: SpriteBatch, camera: Camera) {
@@ -363,9 +388,9 @@ class UIItemDemoSaveCells(
val tlen = App.fontSmallNumbers.getWidth(lastPlayedTimestamp)
App.fontSmallNumbers.draw(batch, lastPlayedTimestamp, x + (width - tlen) - 3f, y + height - 16f)
// file size
App.fontSmallNumbers.draw(batch, "${diskfile.length().ushr(10)} KiB", x + 3f, y + height - 16f)
App.fontSmallNumbers.draw(batch, "${skimmer.diskFile.length().ushr(10)} KiB", x + 3f, y + height - 16f)
// savegame name
if (saveDamaged) batch.color = Color.RED
if (saveDamaged) batch.color = colourBad
App.fontGame.draw(batch, saveName + "${if (saveMode % 2 == 1) "*" else ""}", x + 3f, y + 1f)
super.render(batch, camera)
@@ -374,7 +399,7 @@ class UIItemDemoSaveCells(
override fun dispose() {
thumb?.texture?.dispose()
thumbPixmap.dispose()
thumbPixmap?.dispose()
}
}

View File

@@ -32,8 +32,8 @@ class UIProxyLoadLatestSave : UICanvas() {
override fun endOpening(delta: Float) {
if (App.savegames.size > 0) {
LoadSavegame(VDUtil.readDiskArchive(App.savegames[0].first, Level.INFO) {
System.err.println("Possibly damaged savefile ${App.savegames[0].first.absolutePath}:\n$it")
LoadSavegame(VDUtil.readDiskArchive(App.savegames[0].diskFile, Level.INFO) {
System.err.println("Possibly damaged savefile ${App.savegames[0].diskFile.absolutePath}:\n$it")
})
}
}

View File

@@ -1,7 +1,9 @@
package net.torvald.terrarum.tvda
import net.torvald.terrarum.serialise.Common
import java.io.*
import java.nio.charset.Charset
import java.nio.file.NoSuchFileException
import java.util.*
import java.util.logging.Level
import kotlin.experimental.and
@@ -18,9 +20,9 @@ import kotlin.experimental.and
* Created by minjaesong on 2017-11-17.
*/
class DiskSkimmer(
private val diskFile: File,
val diskFile: File,
val charset: Charset = Charset.defaultCharset(),
private val skimmingStopFunction: (HashMap<EntryID, Long>) -> Boolean = { false }
noInit: Boolean = false
) {
/*
@@ -45,6 +47,23 @@ removefile:
*/
fun checkFileSanity() {
if (!diskFile.exists()) throw NoSuchFileException(diskFile.absoluteFile)
if (diskFile.length() < 310L) throw RuntimeException("Invalid Virtual Disk file!")
// check magic
val fis = FileInputStream(diskFile)
val magic = ByteArray(4).let { fis.read(it); it }
if (!magic.contentEquals(VirtualDisk.MAGIC)) throw RuntimeException("Invalid Virtual Disk file!")
fis.close()
}
init {
checkFileSanity()
}
/**
* EntryID to Offset.
*
@@ -52,32 +71,30 @@ removefile:
*/
private var entryToOffsetTable = HashMap<EntryID, Long>()
/** temporary storage to store tree edges */
// private var directoryStruct = ArrayList<DirectoryEdge>()
/** root node of the directory tree */
// private var directory = DirectoryNode(0, null, DiskEntry.DIRECTORY, "")
// private data class DirectoryEdge(val nodeParent: EntryID, val node: EntryID, val type: Byte, val name: String)
// private data class DirectoryNode(var nodeThis: EntryID, val nodeParent: EntryID?, var type: Byte, var name: String)
private val dirDelim = Regex("""[\\/]""")
private val DIR = "/"
val fa = RandomAccessFile(diskFile, "rw")
lateinit var fa: RandomAccessFile//RandomAccessFile(diskFile, "rw")
private fun debugPrintln(s: Any) {
if (false) println(s.toString())
}
var initialised = false
private set
init {
if (!noInit) {
rebuild()
}
}
fun rebuild() {
checkFileSanity() // state of the file may have been changed (e.g. file deleted) so we check again
fa = RandomAccessFile(diskFile, "rw")
val fis = FileInputStream(diskFile)
println("[DiskSkimmer] loading the diskfile ${diskFile.canonicalPath}")
var currentPosition = fis.skip(VirtualDisk.HEADER_SIZE) // skip disk header
println("[DiskSkimmer] loading the diskfile ${diskFile.canonicalPath}")
fun skipRead(bytes: Long) {
currentPosition += fis.skip(bytes)
@@ -126,6 +143,7 @@ removefile:
return buffer.toLongBig()
}
val currentLength = diskFile.length()
while (currentPosition < currentLength) {
@@ -154,13 +172,15 @@ removefile:
else {
debugPrintln("[DiskSkimmer] ... discarding entry $entryID at offset $offset (name: ${diskIDtoReadableFilename(entryID)})")
}
if (skimmingStopFunction(entryToOffsetTable)) break
}
fis.close()
initialised = true
}
fun hasEntry(entryID: EntryID) = entryToOffsetTable.containsKey(entryID)
//////////////////////////////////////////////////
// THESE ARE METHODS TO SUPPORT ON-LINE READING //
//////////////////////////////////////////////////

View File

@@ -233,18 +233,36 @@ abstract class UICanvas(
fun setAsAlwaysVisible() {
handler.setAsAlwaysVisible()
show()
}
open fun setAsOpen() {
handler.setAsOpen()
show()
}
open fun setAsClose() {
handler.setAsClose()
hide()
}
open fun toggleOpening() {
handler.toggleOpening()
// handler.toggleOpening()
if (handler.alwaysVisible && !handler.doNotWarnConstant) {
throw RuntimeException("[UIHandler] Tried to 'toggle opening of' constant UI")
}
if (isVisible) {
if (!isClosing) {
setAsClose()
hide()
}
}
else {
if (!isOpening) {
setAsOpen()
show()
}
}
}
inline val isOpened: Boolean

View File

@@ -78,7 +78,7 @@ void main() {
var initialX = posX
var initialY = posY
private var alwaysVisible = false
var alwaysVisible = false; private set
var isOpening = false
var isClosing = false

View File

@@ -167,7 +167,7 @@ class UIItemTextButtonList(
private var clickLatched = false
override fun show() {
printdbg(this, "${this.javaClass.simpleName} show()")
// printdbg(this, "${this.javaClass.simpleName} show()")
clickLatched = true
}

View File

@@ -14,7 +14,7 @@
Each file on the Savegame has following convention:
|Type|Filename|ID|
|--|--|--|
|---|---|---|
|Metadata|savegame|-1|
|Blocks Properties|blocks|-16|
|Items Properties|items|-17|