diff --git a/src/net/torvald/terrarum/SavegameCollection.kt b/src/net/torvald/terrarum/SavegameCollection.kt index 87fac2d7a..6947f86f4 100644 --- a/src/net/torvald/terrarum/SavegameCollection.kt +++ b/src/net/torvald/terrarum/SavegameCollection.kt @@ -1,11 +1,19 @@ package net.torvald.terrarum +import com.badlogic.gdx.utils.JsonWriter import net.torvald.terrarum.App.printdbg -import net.torvald.terrarum.savegame.DiskSkimmer +import net.torvald.terrarum.gameactors.AVKey +import net.torvald.terrarum.savegame.* +import net.torvald.terrarum.savegame.VDFileID.ROOT +import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO +import net.torvald.terrarum.serialise.Common +import net.torvald.terrarum.utils.JsonFetcher +import net.torvald.terrarum.utils.forEachSiblings import java.io.File import java.io.IOException import java.nio.file.Files import java.nio.file.StandardCopyOption +import java.util.* import kotlin.io.path.Path /** @@ -57,6 +65,39 @@ class SavegameCollection(files0: List) { fun getBaseFile(): DiskSkimmer { return files.first { it.diskFile.extension.isBlank() } } + + fun getUUID(): UUID { + var uuid: UUID? = null + loadable().getFile(SAVEGAMEINFO)!!.let { + JsonFetcher.readFromJsonString(ByteArray64Reader(it.bytes, Common.CHARSET)).forEachSiblings { name, value -> + if (name == "worldIndex" || name == "uuid") uuid = UUID.fromString(value.asString()) + } + } + return uuid!! + } + + fun renamePlayer(name: String) { + files.forEach { skimmer -> + skimmer.rebuild() + skimmer.getFile(SAVEGAMEINFO)!!.let { file -> + val json = JsonFetcher.readFromJsonString(ByteArray64Reader(file.bytes, Common.CHARSET)) + json.getChild("actorValue").getChild(AVKey.NAME).set(name) + + val jsonBytes = json.prettyPrint(JsonWriter.OutputType.json, 0).encodeToByteArray().toByteArray64() + val newEntry = DiskEntry(SAVEGAMEINFO, ROOT, skimmer.requestFile(SAVEGAMEINFO)!!.creationDate, App.getTIME_T(), EntryFile(jsonBytes)) + + skimmer.appendEntry(newEntry) + + skimmer.setDiskName(name, Common.CHARSET) + } + } + } + + fun renameWorld(name: String) { + files.forEach { skimmer -> + skimmer.setDiskName(name, Common.CHARSET) + } + } } class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollection?) { diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 2bcc0226e..b2eb28548 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -800,17 +800,20 @@ fun AppUpdateListOfSavegames() { } }.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it -> println("${index+1}.\t${it.diskFile.absolutePath}") - it.rebuild() +// it.rebuild() - val jsonFile = it.getFile(SAVEGAMEINFO)!! - var worldUUID: UUID? = null - JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value -> - if (name == "worldIndex") worldUUID = UUID.fromString(value.asString()) - } +// val jsonFile = it.getFile(SAVEGAMEINFO)!! +// var worldUUID: UUID? = null +// JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value -> +// if (name == "worldIndex") worldUUID = UUID.fromString(value.asString()) +// } + + val collection = SavegameCollection.collectFromBaseFilename(File(worldsDir), it.diskFile.name) + val worldUUID = collection.getUUID() // if multiple valid savegames with same UUID exist, only the most recent one is retained if (!App.savegameWorlds.contains(worldUUID)) { - App.savegameWorlds[worldUUID] = SavegameCollection.collectFromBaseFilename(File(worldsDir), it.diskFile.name) + App.savegameWorlds[worldUUID] = collection App.savegameWorldsName[worldUUID] = it.getDiskName(Common.CHARSET) App.sortedSavegameWorlds.add(worldUUID) } @@ -831,18 +834,21 @@ fun AppUpdateListOfSavegames() { null } }.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it -> - println("${index+1}.\t${it.diskFile.absolutePath}") - it.rebuild() +// println("${index+1}.\t${it.diskFile.absolutePath}") +// it.rebuild() - val jsonFile = it.getFile(SAVEGAMEINFO)!! - var playerUUID: UUID? = null - JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value -> - if (name == "uuid") playerUUID = UUID.fromString(value.asString()) - } +// val jsonFile = it.getFile(SAVEGAMEINFO)!! +// var playerUUID: UUID? = null +// JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value -> +// if (name == "uuid") playerUUID = UUID.fromString(value.asString()) +// } + + val collection = SavegameCollection.collectFromBaseFilename(File(playersDir), it.diskFile.name) + val playerUUID = collection.getUUID() // if multiple valid savegames with same UUID exist, only the most recent one is retained if (!App.savegamePlayers.contains(playerUUID)) { - App.savegamePlayers[playerUUID] = SavegameCollection.collectFromBaseFilename(File(playersDir), it.diskFile.name) + App.savegamePlayers[playerUUID] = collection App.savegamePlayersName[playerUUID] = it.getDiskName(Common.CHARSET) App.sortedPlayers.add(playerUUID) } diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureWorldPortal.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureWorldPortal.kt index 6a13f192a..4250c7742 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureWorldPortal.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureWorldPortal.kt @@ -77,11 +77,11 @@ class FixtureWorldPortal : Electric { // load existing val jobAfterSave: () -> Unit if (it.worldDiskToLoad != null) { - UILoadGovernor.worldDisk = it.worldDiskToLoad - UILoadGovernor.playerDisk = App.savegamePlayers[player.uuid]!!.files[0] jobAfterSave = { - UILoadGovernor.playerDisk!!.rebuild() - LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk!!) + LoadSavegame( + App.savegamePlayers[player.uuid]!!.files[0], + it.worldDiskToLoad + ) } } // create new diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadAutosave.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadAutosave.kt index 2c9b1206f..318956a09 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadAutosave.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadAutosave.kt @@ -63,8 +63,8 @@ class UILoadAutosave(val full: UILoadSavegame) : UICanvas() { it.extraDrawOp = getDrawTextualInfoFun(loadables.getManualSave()!!) it.clickOnceListener = { _,_ -> loadables.getManualSave()!!.let { - UILoadGovernor.playerDisk = it.player - UILoadGovernor.worldDisk = it.world +// UILoadGovernor.playerDisk = it.player +// UILoadGovernor.worldDisk = it.world } } } @@ -81,8 +81,8 @@ class UILoadAutosave(val full: UILoadSavegame) : UICanvas() { it.extraDrawOp = getDrawTextualInfoFun(loadables.getAutoSave()!!) it.clickOnceListener = { _,_ -> loadables.getAutoSave()!!.let { - UILoadGovernor.playerDisk = it.player - UILoadGovernor.worldDisk = it.world +// UILoadGovernor.playerDisk = it.player +// UILoadGovernor.worldDisk = it.world } } } diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt index 8811208eb..731df0bad 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt @@ -62,7 +62,7 @@ object UILoadGovernor { var worldUUID: UUID? = null var previousSaveWasLoaded = false // used by the debug save loader - var playerDisk: DiskSkimmer? = null + /*var playerDisk: DiskSkimmer? = null set(value) { printdbg(this, "Player selected: ${value?.diskFile?.name}") field = value @@ -72,12 +72,12 @@ object UILoadGovernor { set(value) { printdbg(this, "World selected: ${value?.diskFile?.name}") field = value - } + }*/ fun reset() { printdbg(this, "Resetting player and world selection") - playerDisk = null - worldDisk = null +// playerDisk = null +// worldDisk = null playerUUID = null worldUUID = null @@ -205,15 +205,14 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() { savegamesCount = 0 App.sortedPlayers.forEach { uuid -> - val skimmer = App.savegamePlayers[uuid]!!.loadable() val x = uiX val y = titleTopGradEnd + cellInterval * savegamesCount try { - playerCells.add(UIItemPlayerCells(this, x, y, skimmer)) + playerCells.add(UIItemPlayerCells(this, x, y, uuid)) savegamesCount += 1 } catch (e: Throwable) { - System.err.println("[UILoadDemoSavefiles] Error while loading Player '${skimmer.diskFile.absolutePath}'") + System.err.println("[UILoadDemoSavefiles] Error while loading Player with UUID $uuid") e.printStackTrace() } } @@ -321,7 +320,10 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() { App.fontGame.draw(batch, txt, (App.scr.width - App.fontGame.getWidth(txt)) / 2f, (App.scr.height - App.fontGame.lineHeight) / 2f) if (loadFired == 2) { - LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk) + LoadSavegame( + App.savegamePlayers[UILoadGovernor.playerUUID]!!.loadable(), + App.savegameWorlds[UILoadGovernor.worldUUID]?.loadable() + ) } } else { @@ -488,13 +490,13 @@ class UIItemPlayerCells( parent: Advanceable, initialX: Int, initialY: Int, - val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) { + val playerUUID: UUID) : UIItem(parent, initialX, initialY) { override val width = SAVE_CELL_WIDTH override val height = SAVE_CELL_HEIGHT override var clickOnceListener: ((Int, Int) -> Unit) = { _: Int, _: Int -> - UILoadGovernor.playerDisk = skimmer +// UILoadGovernor.playerDisk = App. UILoadGovernor.playerUUID = playerUUID UILoadGovernor.worldUUID = worldUUID parent.advanceMode(this) @@ -505,15 +507,14 @@ class UIItemPlayerCells( private var lastPlayTime: String = "????-??-?? --:--:--" private var totalPlayTime: String = "--h--m--s" - lateinit var playerUUID: UUID; private set +// lateinit var playerUUID: UUID; private set lateinit var worldUUID: UUID; private set init { - skimmer.getFile(SAVEGAMEINFO)?.bytes?.let { + App.savegamePlayers[playerUUID]!!.loadable().getFile(SAVEGAMEINFO)?.bytes?.let { var lastPlayTime0 = 0L JsonFetcher.readFromJsonString(ByteArray64Reader(it, Common.CHARSET)).forEachSiblings { name, value -> - if (name == "uuid") playerUUID = UUID.fromString(value.asString()) if (name == "worldCurrentlyPlaying") worldUUID = UUID.fromString(value.asString()) if (name == "totalPlayTime") totalPlayTime = parseDuration(value.asLong()) if (name == "lastPlayTime") lastPlayTime0 = value.asLong() @@ -565,7 +566,8 @@ class UIItemPlayerCells( override fun render(batch: SpriteBatch, camera: Camera) { // try to generate a texture - if (skimmer.initialised && !hasTexture) { + if (!hasTexture) { + val skimmer = App.savegamePlayers[playerUUID]!!.loadable() skimmer.getFile(SAVEGAMEINFO)?.bytes?.let { try { printdbg(this, "Generating portrait for $playerName") @@ -757,7 +759,7 @@ class UIItemWorldCells( private var highlightCol: Color = Toolkit.Theme.COL_LIST_DEFAULT override var clickOnceListener: ((Int, Int) -> Unit) = { _: Int, _: Int -> - UILoadGovernor.worldDisk = skimmer +// UILoadGovernor.worldDisk = skimmer parent.advanceMode(this) } diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadList.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadList.kt index 976b4a9b0..7ec33a369 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadList.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadList.kt @@ -57,36 +57,70 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() { private val mode1Node = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse() + private var showSpinner = false + + + fun advanceMode() { + App.printdbg(this, "Load playerUUID: ${UILoadGovernor.playerUUID}, worldUUID: ${UILoadGovernor.worldUUID}") + full.loadables = SavegameCollectionPair(App.savegamePlayers[UILoadGovernor.playerUUID], App.savegameWorlds[UILoadGovernor.worldUUID]) + + + if (full.loadables.moreRecentAutosaveAvailable()) { + // make choice for load manual or auto, if available + full.hasNewerAutosave = true + + full.queueUpManageScr() + full.bringAutosaveSelectorUp() + } + else if (!full.loadables.saveAvaliable()) { + // show save is damaged and cannot be loaded + full.queueUpDamagedSaveScr() + full.takeAutosaveSelectorDown() + } + else { +// val (p, w) = full.loadables.getLoadableSave()!! +// UILoadGovernor.playerDisk = p; UILoadGovernor.worldDisk = w + + if (full.loadables.newerSaveIsDamaged) { + UILoadGovernor.previousSaveWasLoaded = true + } + + full.queueUpManageScr() + full.takeAutosaveSelectorDown() + } + + full.changePanelTo(1) + } override fun show() { mode1Node.parent = full.remoCon.treeRoot mode1Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame" full.remoCon.setNewRemoConContents(mode1Node) + playerCells.clear() try { full.remoCon.handler.lockToggle() - full.showSpinner = true + showSpinner = true Thread { // read savegames var savegamesCount = 0 App.sortedPlayers.forEach { uuid -> - val skimmer = App.savegamePlayers[uuid]!!.loadable() val x = full.uiX val y = titleTopGradEnd + cellInterval * savegamesCount try { - playerCells.add(UIItemPlayerCells(full, x, y, skimmer)) + playerCells.add(UIItemPlayerCells(full, x, y, uuid)) savegamesCount += 1 } catch (e: Throwable) { - System.err.println("[UILoadSavegame] Error while loading Player '${skimmer.diskFile.absolutePath}'") + System.err.println("[UILoadSavegame] Error while loading Player with UUID $uuid") e.printStackTrace() } } full.remoCon.handler.unlockToggle() - full.showSpinner = false + showSpinner = false }.start() } @@ -180,9 +214,9 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() { full.setCameraPosition(batch, camera, 0f, 0f) val saveTex = TextureRegion(Texture(savePixmap)); saveTex.flip(false, true) batch.inUse { - batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f) + batch.draw(saveTex, posX + (width - uiWidth - 10) / 2f, 0f) // Control help - App.fontGame.draw(batch, controlHelp, full.uiX.toFloat(), controlHelperY.toFloat()) + App.fontGame.draw(batch, controlHelp, posX + full.uiX.toFloat(), controlHelperY.toFloat()) } saveTex.texture.dispose() diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadManage.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadManage.kt index f30fb2a5e..5061168b0 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadManage.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadManage.kt @@ -21,7 +21,6 @@ class UILoadManage(val full: UILoadSavegame) : UICanvas() { private val drawX = (Toolkit.drawWidth - 480) / 2 private val drawY = (App.scr.height - 480) / 2 private val buttonRowY = drawY + 480 - 24 - private val corruptedBackButton = UIItemTextButton(this, "MENU_LABEL_BACK", (Toolkit.drawWidth - goButtonWidth) / 2, buttonRowY, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) private val confirmCancelButton = UIItemTextButton(this, "MENU_LABEL_CANCEL", drawX + (240 - goButtonWidth) / 2, buttonRowY, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) private val confirmDeleteButton = UIItemTextButton(this, "MENU_LABEL_DELETE", drawX + 240 + (240 - goButtonWidth) / 2, buttonRowY, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true, inactiveCol = Toolkit.Theme.COL_RED, activeCol = Toolkit.Theme.COL_REDD) @@ -32,7 +31,6 @@ class UILoadManage(val full: UILoadSavegame) : UICanvas() { private val MODE_RENAME = 32 // show rename dialogue init { - corruptedBackButton.clickOnceListener = { _,_ -> full.remoCon.openUI(UILoadSavegame(full.remoCon)) } confirmCancelButton.clickOnceListener = { _,_ -> full.remoCon.openUI(UILoadSavegame(full.remoCon)) } confirmDeleteButton.clickOnceListener = { _,_ -> val pu = full.buttonSelectedForDeletion!!.playerUUID @@ -45,17 +43,21 @@ class UILoadManage(val full: UILoadSavegame) : UICanvas() { // don't delete the world please full.remoCon.openUI(UILoadSavegame(full.remoCon)) } + + addUIitem(confirmCancelButton) + addUIitem(confirmDeleteButton) } override fun updateUI(delta: Float) { - TODO("Not yet implemented") + confirmCancelButton.update(delta) + confirmDeleteButton.update(delta) } override fun renderUI(batch: SpriteBatch, camera: Camera) { if (mode == MODE_DELETE) { - Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_SAVE_WILL_BE_DELETED"], Toolkit.drawWidth, 0, titleTopGradEnd + cellInterval - 46) - Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_ARE_YOU_SURE"], Toolkit.drawWidth, 0, titleTopGradEnd + cellInterval + SAVE_CELL_HEIGHT + 36) + Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_SAVE_WILL_BE_DELETED"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval - 46) + Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_ARE_YOU_SURE"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval + SAVE_CELL_HEIGHT + 36) full.buttonSelectedForDeletion!!.render(batch, camera) @@ -65,7 +67,20 @@ class UILoadManage(val full: UILoadSavegame) : UICanvas() { } override fun dispose() { - TODO("Not yet implemented") + } + + override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { + confirmCancelButton.touchDown(screenX, screenY, pointer, button) + confirmDeleteButton.touchDown(screenX, screenY, pointer, button) + + return true + } + + override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { + confirmCancelButton.touchUp(screenX, screenY, pointer, button) + confirmDeleteButton.touchUp(screenX, screenY, pointer, button) + + return true } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSaveDamaged.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSaveDamaged.kt new file mode 100644 index 000000000..b8a469c23 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSaveDamaged.kt @@ -0,0 +1,48 @@ +package net.torvald.terrarum.modulebasegame.ui + +import com.badlogic.gdx.graphics.Camera +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import net.torvald.terrarum.App +import net.torvald.terrarum.langpack.Lang +import net.torvald.terrarum.ui.Toolkit +import net.torvald.terrarum.ui.UICanvas +import net.torvald.terrarum.ui.UIItemTextButton + +/** + * Created by minjaesong on 2023-07-07. + */ +class UILoadSaveDamaged(val full: UILoadSavegame) : UICanvas() { + + override var width: Int = Toolkit.drawWidth + override var height: Int = App.scr.height + + private val goButtonWidth = 180 + private val drawX = (Toolkit.drawWidth - 480) / 2 + private val drawY = (App.scr.height - 480) / 2 + private val buttonRowY = drawY + 480 - 24 + private val corruptedBackButton = UIItemTextButton(this, "MENU_LABEL_BACK", (Toolkit.drawWidth - goButtonWidth) / 2, buttonRowY, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) + + init { + corruptedBackButton.clickOnceListener = { _,_ -> + full.changePanelTo(0) + println("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + } + + addUIitem(corruptedBackButton) + } + + override fun updateUI(delta: Float) { + corruptedBackButton.update(delta) + } + + override fun renderUI(batch: SpriteBatch, camera: Camera) { + Toolkit.drawTextCentered(batch, App.fontGame, Lang["ERROR_SAVE_CORRUPTED"], Toolkit.drawWidth, 0, App.scr.height / 2 - 42) + + corruptedBackButton.render(batch, camera) + } + + override fun dispose() { + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt index 127b19dfa..002a96462 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt @@ -1,31 +1,22 @@ package net.torvald.terrarum.modulebasegame.ui import com.badlogic.gdx.Gdx -import com.badlogic.gdx.Input import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.glutils.FrameBuffer -import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.GdxRuntimeException import net.torvald.unicode.getKeycapConsole import net.torvald.unicode.getKeycapPC import net.torvald.terrarum.* import net.torvald.terrarum.App.printdbg 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.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internalWidth -import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT import net.torvald.terrarum.ui.* import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack -import java.time.Instant -import java.time.format.DateTimeFormatter import java.util.* -import java.util.zip.GZIPInputStream -import kotlin.collections.ArrayList import kotlin.math.roundToInt @@ -74,15 +65,6 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { internal val titleTopGradEnd: Int = titleTopGradStart + gradAreaHeight internal val titleBottomGradStart: Int = height - App.scr.tvSafeGraphicsHeight - gradAreaHeight internal val titleBottomGradEnd: Int = titleBottomGradStart + gradAreaHeight - internal val controlHelperY: Int = titleBottomGradStart + gradAreaHeight - textH - - - private val controlHelp: String - get() = if (App.environment == RunningEnvironment.PC) - "${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" + - " ${Lang["MENU_CONTROLS_SCROLL"]}" - else - "${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}" private var scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64 @@ -95,8 +77,6 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { private val scrollAnimLen = 0.1f private var sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, false) - internal var showSpinner = false - internal var buttonSelectedForDeletion: UIItemPlayerCells? = null private val goButtonWidth = 180 @@ -104,7 +84,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { private val drawY = (App.scr.height - 480) / 2 private val buttonRowY = drawY + 480 - 24 - private lateinit var loadables: SavegameCollectionPair + internal lateinit var loadables: SavegameCollectionPair // will be used and modified by subUIs /*private val altSelDrawW = 640 private val altSelHdrawW = altSelDrawW / 2 @@ -115,10 +95,13 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { private val altSelQdrawW = altSelDrawW / 4 private val altSelQQQdrawW = altSelDrawW * 3 / 4*/ + internal var hasNewerAutosave = false + private val transitionalListing = UILoadList(this) private val transitionalAutosave = UILoadAutosave(this) private val transitionalManage = UILoadManage(this) private val transitionalNewCharacter = UINewCharacter(remoCon) + private val transitionalSaveDamaged = UILoadSaveDamaged(this) private val transitionPanel = UIItemHorizontalFadeSlide( this, (width - internalWidth) / 2, @@ -127,118 +110,34 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { App.scr.height, 0f, listOf(transitionalListing), - listOf(transitionalManage, transitionalAutosave, transitionalNewCharacter) + listOf(transitionalManage, transitionalNewCharacter, transitionalSaveDamaged), + listOf(NullUI, transitionalAutosave) ) - init { + internal fun queueUpManageScr() { transitionPanel.setCentreUIto(0) } + internal fun queueUpNewCharScr() { transitionPanel.setCentreUIto(1) } + internal fun queueUpDamagedSaveScr() { transitionPanel.setCentreUIto(2) } - } - - - init { + internal fun bringAutosaveSelectorUp() { transitionPanel.setRightUIto(1) } + internal fun takeAutosaveSelectorDown() { transitionPanel.setRightUIto(0) } + internal fun changePanelTo(index: Int) { + transitionPanel.requestTransition(index) } override fun advanceMode(button: UIItem) { printdbg(this, "advanceMode ${button.javaClass.canonicalName}") - mode += 1 - uiScroll = 0f - scrollFrom = 0 - scrollTarget = 0 - scrollAnimCounter = 0f - loadFired = 0 - - printdbg(this, "savelist mode: $mode") - - // look for recently played world - if (mode == MODE_SELECT_AFTER) { - - // select the most recent loadable save by comparing manual and autosaves, NOT JUST going with loadable() - printdbg(this, "Load playerUUID: ${UILoadGovernor.playerUUID}, worldUUID: ${UILoadGovernor.worldUUID}") - loadables = SavegameCollectionPair(App.savegamePlayers[UILoadGovernor.playerUUID], App.savegameWorlds[UILoadGovernor.worldUUID]) - - mode = if (loadables.moreRecentAutosaveAvailable()) { - // make choice for load manual or auto, if available - - MODE_SAVE_MULTIPLE_CHOICES - } - else if (!loadables.saveAvaliable()) { - // show save is damaged and cannot be loaded - MODE_SAVE_DAMAGED - } - else { - val (p, w) = loadables.getLoadableSave()!! - UILoadGovernor.playerDisk = p; UILoadGovernor.worldDisk = w - - if (loadables.newerSaveIsDamaged) { - UILoadGovernor.previousSaveWasLoaded = true - } - - MODE_LOAD_DA_SHIT_ALREADY - - - // test codes // - - /*val autoThumb = loadables.getManualSave()!!.getThumbnail() - val manualThumb = loadables.getManualSave()!!.getThumbnail() - - loadManualThumbButton = UIItemImageButton(this, manualThumb, - initialX = (Toolkit.drawWidth - altSelDrawW)/2 + altSelQdrawW - imageButtonW/2, - initialY = altSelDrawY + 120, - width = imageButtonW, - height = imageButtonH, - imageDrawWidth = imageButtonW, - imageDrawHeight = imageButtonH, - highlightable = false, - useBorder = true, - ).also { - it.extraDrawOp = getDrawTextualInfoFun(loadables.getManualSave()!!) - it.clickOnceListener = { _,_ -> - loadables.getManualSave()!!.let { - UILoadGovernor.playerDisk = it.player - UILoadGovernor.worldDisk = it.world - } - mode = MODE_LOAD_DA_SHIT_ALREADY - } - } - loadAutoThumbButton = UIItemImageButton(this, autoThumb, - initialX = (Toolkit.drawWidth - altSelDrawW)/2 + altSelQQQdrawW - imageButtonW/2, - initialY = altSelDrawY + 120, - width = imageButtonW, - height = imageButtonH, - imageDrawWidth = imageButtonW, - imageDrawHeight = imageButtonH, - highlightable = false, - useBorder = true, - ).also { - it.extraDrawOp = getDrawTextualInfoFun(loadables.getManualSave()!!) - it.clickOnceListener = { _,_ -> - loadables.getManualSave()!!.let { - UILoadGovernor.playerDisk = it.player - UILoadGovernor.worldDisk = it.world - } - mode = MODE_LOAD_DA_SHIT_ALREADY - } - } - - MODE_SAVE_MULTIPLE_CHOICES*/ - } - - printdbg(this, "mode = $mode") - } - else if (mode == MODE_SAVE_DELETE_CONFIRM) { - // confirm deletion of selected player - buttonSelectedForDeletion = (button as UIItemPlayerCells).also { - deleteCellPosYstart = it.posY.toFloat() - it.forceMouseDown = true - it.update(0.01f) - } + if (button.javaClass.simpleName == "UIItemPlayerCells") { + transitionalListing.advanceMode() } + } override fun show() { + takeAutosaveSelectorDown() transitionPanel.show() + hasNewerAutosave = false /*try { remoCon.handler.lockToggle() showSpinner = true @@ -366,13 +265,13 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { } }*/ - if (mode == MODE_SAVE_DELETE_CONFIRM && deleteCellAnimCounter <= scrollAnimLen) { + /*if (mode == MODE_SAVE_DELETE_CONFIRM && deleteCellAnimCounter <= scrollAnimLen) { // do transitional moving stuff buttonSelectedForDeletion?.posY = Movement.fastPullOut(deleteCellAnimCounter / scrollAnimLen, deleteCellPosYstart, (titleTopGradEnd + cellInterval).toFloat()).roundToInt() deleteCellAnimCounter += delta if (deleteCellAnimCounter > scrollAnimLen) deleteCellAnimCounter = scrollAnimLen - } + }*/ } private var deleteCellAnimCounter = 0f @@ -381,7 +280,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { override fun renderUI(batch: SpriteBatch, camera: Camera) { transitionPanel.render(batch, camera) - if (mode == MODE_LOAD_DA_SHIT_ALREADY) { + /*if (mode == MODE_LOAD_DA_SHIT_ALREADY) { loadFired += 1 // to hide the "flipped skybox" artefact batch.end() @@ -398,167 +297,31 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk) } } - else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { - /*batch.end() - val cells = getCells() - - lateinit var savePixmap: Pixmap - sliderFBO.inAction(camera as OrthographicCamera, batch) { - gdxClearAndEnableBlend(0f, 0f, 0f, 0f) - - setCameraPosition(batch, camera, 0f, 0f) - batch.color = Color.WHITE - batch.inUse { - for (index in 0 until cells.size) { - val it = cells[index] - - if (App.getConfigBoolean("fx_streamerslayout")) - it.posX += uiXdiffChatOverlay - - if (index in listScroll - 2 until listScroll + savesVisible + 2) - it.render(batch, camera) - - if (App.getConfigBoolean("fx_streamerslayout")) - it.posX -= uiXdiffChatOverlay - } - } - savePixmap = Pixmap.createFromFrameBuffer(0, 0, sliderFBO.width, sliderFBO.height) - savePixmap.blending = Pixmap.Blending.None - } - - - // implement "wipe-out" by CPU-rendering (*deep exhale*) - //savePixmap.setColor(1f,1f,1f,0f) - savePixmap.setColor(0f, 0f, 0f, 0f) - savePixmap.fillRectangle(0, savePixmap.height - titleTopGradStart, savePixmap.width, titleTopGradStart) - // top grad - for (y in titleTopGradStart until titleTopGradEnd) { - val alpha = (y - titleTopGradStart).toFloat() / gradAreaHeight - for (x in 0 until savePixmap.width) { - val col = savePixmap.getPixel(x, savePixmap.height - y) - val blendAlpha = (col.and(0xFF) * alpha).roundToInt() - savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha) - } - } - // bottom grad - for (y in titleBottomGradStart until titleBottomGradEnd) { - val alpha = 1f - ((y - titleBottomGradStart).toFloat() / gradAreaHeight) - for (x in 0 until savePixmap.width) { - val col = savePixmap.getPixel(x, savePixmap.height - y) - val blendAlpha = (col.and(0xFF) * alpha).roundToInt() - savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha) - } - } - savePixmap.setColor(0f, 0f, 0f, 0f) - savePixmap.fillRectangle(0, 0, savePixmap.width, height - titleBottomGradEnd + 1) - - - - setCameraPosition(batch, camera, 0f, 0f) - val saveTex = TextureRegion(Texture(savePixmap)); saveTex.flip(false, true) - batch.inUse { - batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f) - // Control help - App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat()) - } - - saveTex.texture.dispose() - savePixmap.dispose() - - batch.begin()*/ - } - else if (mode == MODE_SAVE_MULTIPLE_CHOICES) { - // "The Autosave is more recent than the manual save" - Toolkit.drawTextCentered(batch, App.fontGame, Lang["GAME_MORE_RECENT_AUTOSAVE1"], Toolkit.drawWidth, 0, altSelDrawY) - Toolkit.drawTextCentered(batch, App.fontGame, Lang["GAME_MORE_RECENT_AUTOSAVE2"], Toolkit.drawWidth, 0, altSelDrawY + 24) - // Manual Save Autosave - Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_IO_MANUAL_SAVE"], altSelHdrawW, (Toolkit.drawWidth - altSelDrawW)/2, altSelDrawY + 80) - Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_IO_AUTOSAVE"], altSelHdrawW, Toolkit.drawWidth/2, altSelDrawY + 80) - - - // draw thumbnail-buttons - loadAutoThumbButton.render(batch, camera) - loadManualThumbButton.render(batch, camera) - } - else if (mode == MODE_SAVE_DAMAGED) { - Toolkit.drawTextCentered(batch, App.fontGame, Lang["ERROR_SAVE_CORRUPTED"], Toolkit.drawWidth, 0, App.scr.height / 2 - 42) - - corruptedBackButton.render(batch, camera) - } - - - if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE || mode == MODE_SAVE_DELETE_CONFIRM) { -// deleteCharacterButton.render(batch, camera) - } if (mode == MODE_SAVE_DELETE_CONFIRM) { buttonSelectedForDeletion?.render(batch, camera) confirmCancelButton.render(batch, camera) confirmDeleteButton.render(batch, camera) - } - if (mode == MODE_SAVE_DELETE_CONFIRM) { batch.color = Color.WHITE Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_SAVE_WILL_BE_DELETED"], Toolkit.drawWidth, 0, titleTopGradEnd + cellInterval - 46) Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_ARE_YOU_SURE"], Toolkit.drawWidth, 0, titleTopGradEnd + cellInterval + SAVE_CELL_HEIGHT + 36) - } + }*/ } override fun keyDown(keycode: Int): Boolean { - /*if (this.isVisible && (mode == MODE_SELECT || mode == MODE_SAVE_DELETE)) { - val cells = getCells() - - if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) { - scrollFrom = listScroll - scrollTarget -= 1 - scrollAnimCounter = 0f - } - else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("control_key_down")) && scrollTarget < cells.size - savesVisible) { - scrollFrom = listScroll - scrollTarget += 1 - scrollAnimCounter = 0f - } - }*/ transitionPanel.keyDown(keycode) return true } override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { - printdbg(this, "touchDown mode=$mode") - - if (mode == MODE_SAVE_MULTIPLE_CHOICES) { - if (::loadAutoThumbButton.isInitialized) loadAutoThumbButton.touchDown(screenX, screenY, pointer, button) - if (::loadManualThumbButton.isInitialized) loadManualThumbButton.touchDown(screenX, screenY, pointer, button) - } - else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { -// getCells().forEach { it.touchDown(screenX, screenY, pointer, button) } -// deleteCharacterButton.touchDown(screenX, screenY, pointer, button) - } - else if (mode == MODE_SAVE_DELETE_CONFIRM) { - confirmCancelButton.touchDown(screenX, screenY, pointer, button) - confirmDeleteButton.touchDown(screenX, screenY, pointer, button) - } - else if (mode == MODE_SAVE_DAMAGED) corruptedBackButton.touchDown(screenX, screenY, pointer, button) transitionPanel.touchDown(screenX, screenY, pointer, button) return true } override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { - if (mode == MODE_SAVE_MULTIPLE_CHOICES) { - if (::loadAutoThumbButton.isInitialized) loadAutoThumbButton.touchUp(screenX, screenY, pointer, button) - if (::loadManualThumbButton.isInitialized) loadManualThumbButton.touchUp(screenX, screenY, pointer, button) - } - else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { -// getCells().forEach { it.touchUp(screenX, screenY, pointer, button) } -// deleteCharacterButton.touchUp(screenX, screenY, pointer, button) - } - else if (mode == MODE_SAVE_DELETE_CONFIRM) { - confirmCancelButton.touchUp(screenX, screenY, pointer, button) - confirmDeleteButton.touchUp(screenX, screenY, pointer, button) - } - else if (mode == MODE_SAVE_DAMAGED) corruptedBackButton.touchUp(screenX, screenY, pointer, button) transitionPanel.touchUp(screenX, screenY, pointer, button) return true } @@ -592,9 +355,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { override fun dispose() { try { shapeRenderer.dispose() } catch (e: IllegalArgumentException) {} try { sliderFBO.dispose() } catch (e: IllegalArgumentException) {} - disposablePool.forEach { - try { it.dispose() } catch (e: GdxRuntimeException) {} - } + try { transitionPanel.dispose() } catch (e: IllegalArgumentException) {} } override fun resize(width: Int, height: Int) { diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt b/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt index 96fd42a4a..4d575ca18 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt @@ -78,10 +78,10 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() { // comment below if chargen must send gamers back to the charcters list - UILoadGovernor.playerDisk = DiskSkimmer(outFile) +// UILoadGovernor.playerDisk = DiskSkimmer(outFile) // comment above if chargen must send gamers back to the charcters list - printdbg(this, "playerdisk: ${UILoadGovernor.playerDisk?.diskFile?.path}") +// printdbg(this, "playerdisk: ${UILoadGovernor.playerDisk?.diskFile?.path}") }, "TerrarumBasegameNewCharcterSaveThread") diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt b/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt index fb77e1372..e02e8dcab 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt @@ -100,7 +100,8 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() { printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}") val ingame = TerrarumIngame(App.batch) - val player = ReadActor.invoke(UILoadGovernor.playerDisk!!, ByteArray64Reader(UILoadGovernor.playerDisk!!.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer + val playerDisk = App.savegamePlayers[UILoadGovernor.playerUUID]!!.loadable() + val player = ReadActor.invoke(playerDisk, ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer val seed = try { seedInput.getTextOrPlaceholder().toLong() } diff --git a/src/net/torvald/terrarum/savegame/DiskSkimmer.kt b/src/net/torvald/terrarum/savegame/DiskSkimmer.kt index 134d7d9a0..1e9ba6670 100644 --- a/src/net/torvald/terrarum/savegame/DiskSkimmer.kt +++ b/src/net/torvald/terrarum/savegame/DiskSkimmer.kt @@ -1,5 +1,6 @@ package net.torvald.terrarum.savegame +import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.serialise.toUint import net.torvald.terrarum.serialise.toUlong import java.io.* @@ -72,7 +73,6 @@ removefile: */ private var entryToOffsetTable = HashMap() - val fa: RandomAccessFile = RandomAccessFile(diskFile, "rw") private fun debugPrintln(s: Any) { if (false) println(s.toString()) @@ -87,84 +87,125 @@ removefile: } } + private fun FileInputStream.readBytes(buffer: ByteArray): Int { + val readStatus = this.read(buffer) + return readStatus + } + private fun FileInputStream.readInt16(): Int { + val buffer = ByteArray(2) + val readStatus = readBytes(buffer) + if (readStatus != 2) throw InternalError("Unexpected error -- EOF reached? (expected 2, got $readStatus)") + return buffer.toInt16() + } + private fun FileInputStream.readInt32(): Int { + val buffer = ByteArray(4) + val readStatus = readBytes(buffer) + if (readStatus != 4) throw InternalError("Unexpected error -- EOF reached? (expected 4, got $readStatus)") + return buffer.toInt32() + } + private fun FileInputStream.readInt48(): Long { + val buffer = ByteArray(6) + val readStatus = readBytes(buffer) + if (readStatus != 6) throw InternalError("Unexpected error -- EOF reached? (expected 6, got $readStatus)") + return buffer.toInt48() + } + private fun FileInputStream.readInt24(): Int { + val buffer = ByteArray(3) + val readStatus = readBytes(buffer) + if (readStatus != 3) throw InternalError("Unexpected error -- EOF reached? (expected 3, got $readStatus)") + return buffer.toInt24() + } + private fun FileInputStream.readInt64(): Long { + val buffer = ByteArray(8) + val readStatus = readBytes(buffer) + if (readStatus != 8) throw InternalError("Unexpected error -- EOF reached? (expected 8, got $readStatus)") + return buffer.toInt64() + } + private fun ByteArray.toInt16(offset: Int = 0): Int { + return this[0 + offset].toUint().shl(8) or + this[1 + offset].toUint() + } + private fun ByteArray.toInt24(offset: Int = 0): Int { + return this[0 + offset].toUint().shl(16) or + this[1 + offset].toUint().shl(8) or + this[2 + offset].toUint() + } + private fun ByteArray.toInt32(offset: Int = 0): Int { + return this[0 + offset].toUint().shl(24) or + this[1 + offset].toUint().shl(16) or + this[2 + offset].toUint().shl(8) or + this[3 + offset].toUint() + } + private fun ByteArray.toInt48(offset: Int = 0): Long { + return this[0 + offset].toUlong().shl(40) or + this[1 + offset].toUlong().shl(32) or + this[2 + offset].toUlong().shl(24) or + this[3 + offset].toUlong().shl(16) or + this[4 + offset].toUlong().shl(8) or + this[5 + offset].toUlong() + } + private fun ByteArray.toInt64(offset: Int = 0): Long { + return this[0 + offset].toUlong().shl(56) or + this[1 + offset].toUlong().shl(48) or + this[2 + offset].toUlong().shl(40) or + this[3 + offset].toUlong().shl(32) or + this[4 + offset].toUlong().shl(24) or + this[5 + offset].toUlong().shl(16) or + this[6 + offset].toUlong().shl(8) or + this[7 + offset].toUlong() + } + fun rebuild() { checkFileSanity() // state of the file may have been changed (e.g. file deleted) so we check again + entryToOffsetTable.clear() + // fa = RandomAccessFile(diskFile, "rw") val fis = FileInputStream(diskFile) - var currentPosition = fis.skip(VirtualDisk.HEADER_SIZE) // skip disk header + var currentPosition = VirtualDisk.HEADER_SIZE + fis.skipNBytes(VirtualDisk.HEADER_SIZE) // skip disk header -// println("[DiskSkimmer] loading the diskfile ${diskFile.canonicalPath}") - - fun skipRead(bytes: Long) { - currentPosition += fis.skip(bytes) - } - /** - * Reads a byte and adds up the position var - */ - fun readByte(): Byte { - currentPosition++ - val read = fis.read() - - if (read < 0) throw InternalError("Unexpectedly reached EOF") - return read.toByte() - } - - /** - * Reads specific bytes to the buffer and adds up the position var - */ - fun readBytes(buffer: ByteArray): Int { - val readStatus = fis.read(buffer) - currentPosition += readStatus - return readStatus - } - fun readUshortBig(): Int { - val buffer = ByteArray(2) - val readStatus = readBytes(buffer) - if (readStatus != 2) throw InternalError("Unexpected error -- EOF reached? (expected 2, got $readStatus)") - return buffer.toShortBig() - } - fun readIntBig(): Int { - val buffer = ByteArray(4) - val readStatus = readBytes(buffer) - if (readStatus != 4) throw InternalError("Unexpected error -- EOF reached? (expected 4, got $readStatus)") - return buffer.toIntBig() - } - fun readInt48(): Long { - val buffer = ByteArray(6) - val readStatus = readBytes(buffer) - if (readStatus != 6) throw InternalError("Unexpected error -- EOF reached? (expected 6, got $readStatus)") - return buffer.toInt48() - } - fun readLongBig(): Long { - val buffer = ByteArray(8) - val readStatus = readBytes(buffer) - if (readStatus != 8) throw InternalError("Unexpected error -- EOF reached? (expected 8, got $readStatus)") - return buffer.toLongBig() - } + println("[DiskSkimmer] rebuild ${diskFile.canonicalPath}") val currentLength = diskFile.length() + var ccc = 0 while (currentPosition < currentLength) { - val entryID = readLongBig() // at this point, cursor is 8 bytes past to the entry head + val entryID = fis.readInt64() // at this point, cursor is 8 bytes past to the entry head + currentPosition += 8 - // fill up the offset table + // fill up the offset table/ val offset = currentPosition - skipRead(8) // parent ID - val typeFlag = readByte() - skipRead(3) - skipRead(16) // skip rest of the header +// printdbg(this, "Offset $offset, entryID $entryID") + + fis.readInt64() // parentID + val typeFlag = fis.read().toByte() + fis.readInt24() + fis.read(16) + + currentPosition += 8+4+16 + +// printdbg(this, " $currentPosition") val entrySize = when (typeFlag and 127) { - DiskEntry.NORMAL_FILE -> readInt48() - DiskEntry.DIRECTORY -> readIntBig().toLong() * 8L + DiskEntry.NORMAL_FILE -> { + currentPosition += 6 + fis.readInt48() + } + DiskEntry.DIRECTORY -> { + currentPosition += 4 + fis.readInt32().toLong() * 8L + } else -> 0 } - skipRead(entrySize) // skips rest of the entry's actual contents +// printdbg(this, " type $typeFlag entrySize = $entrySize") + + currentPosition += entrySize // skips rest of the entry's actual contents + fis.skipNBytes(entrySize) if (typeFlag > 0) { if (entryToOffsetTable[entryID] != null) @@ -177,6 +218,12 @@ removefile: else { debugPrintln("[DiskSkimmer] ... discarding entry $entryID at offset:$offset (name: ${diskIDtoReadableFilename(entryID, getSaveKind())})") } + +// printdbg(this, " currentPosition = $currentPosition / $currentLength") + + ccc++ +// if (ccc == 13) System.exit(1) + } fis.close() @@ -199,38 +246,42 @@ removefile: if (!initialised) throw IllegalStateException("File entries not built! Initialise the Skimmer by executing rebuild()") +// rebuild() + entryToOffsetTable[entryID].let { offset -> if (offset == null) { debugPrintln("[DiskSkimmer.requestFile] entry $entryID does not exist on the table") return null } else { - fa.seek(offset) - val parent = fa.read(8).toLongBig() - val fileFlag = fa.read(4)[0] - val creationTime = fa.read(6).toInt48() - val modifyTime = fa.read(6).toInt48() - val skip_crc = fa.read(4) + val fis = FileInputStream(diskFile) + fis.skipNBytes(offset) + val parent = fis.readInt64() + val fileFlag = fis.read().toByte() + fis.readInt24() + val creationTime = fis.readInt48() + val modifyTime = fis.readInt48() + fis.readInt32() + // get entry size // TODO future me, is this kind of comment helpful or redundant? val entrySize = when (fileFlag) { DiskEntry.NORMAL_FILE -> { - fa.read(6).toInt48() + fis.readInt48() } DiskEntry.DIRECTORY -> { - fa.read(4).toIntBig().toLong() + fis.readInt32().toLong() and 0xFFFFFFFFL } DiskEntry.SYMLINK -> 8L - else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag for entryID $entryID at offset $offset") // FIXME no support for compressed file + else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag for entryID $entryID at offset ${offset+8}") // FIXME no support for compressed file } - val entryContent = when (fileFlag) { DiskEntry.NORMAL_FILE -> { val byteArray = ByteArray64(entrySize) // read one byte at a time for (c in 0L until entrySize) { - byteArray[c] = fa.read().toByte() + byteArray[c] = fis.read().toByte() } EntryFile(byteArray) @@ -240,20 +291,21 @@ removefile: // read 8 bytes at a time val bytesBuffer8 = ByteArray(8) for (c in 0L until entrySize) { - fa.read(bytesBuffer8) + fis.read(bytesBuffer8) dirContents.add(bytesBuffer8.toLongBig()) } EntryDirectory(dirContents) } DiskEntry.SYMLINK -> { - val target = fa.read(8).toLongBig() + val target = fis.readInt64() EntrySymlink(target) } - else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag for entryID $entryID at offset $offset") // FIXME no support for compressed file + else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag for entryID $entryID at offset ${offset+8}") // FIXME no support for compressed file } + fis.close() return DiskEntry(entryID, parent, creationTime, modifyTime, entryContent) } } @@ -313,6 +365,7 @@ removefile: }*/ fun invalidateEntry(id: EntryID) { + val fa = RandomAccessFile(diskFile, "rwd") entryToOffsetTable[id]?.let { fa.seek(it + 8) val type = fa.read() @@ -320,12 +373,15 @@ removefile: fa.write(type or 128) entryToOffsetTable.remove(id) } + fa.close() } fun injectDiskCRC(crc: Int) { + val fa = RandomAccessFile(diskFile, "rwd") fa.seek(42L) fa.write(crc.toBigEndian()) + fa.close() } //private val modifiedDirectories = TreeSet() @@ -344,33 +400,41 @@ removefile: }*/ fun setSaveMode(bits: Int) { + val fa = RandomAccessFile(diskFile, "rwd") fa.seek(49L) fa.writeByte(bits) + fa.close() } fun setSaveKind(bits: Int) { + val fa = RandomAccessFile(diskFile, "rwd") fa.seek(50L) fa.writeByte(bits) + fa.close() } fun getSaveMode(): Int { + val fa = RandomAccessFile(diskFile, "rwd") fa.seek(49L) - return fa.read() + return fa.read().also { fa.close() } } fun getSaveKind(): Int { + val fa = RandomAccessFile(diskFile, "rwd") fa.seek(50L) - return fa.read() + return fa.read().also { fa.close() } } override fun getDiskName(charset: Charset): String { + val fa = RandomAccessFile(diskFile, "rwd") val bytes = ByteArray(268) - fa.seek(10L) + fa.seek(10) fa.read(bytes, 0, 32) fa.seek(60L) fa.read(bytes, 32, 236) + fa.close() return bytes.toCanonicalString(charset) } @@ -378,9 +442,11 @@ removefile: * @return creation time of the root directory */ fun getCreationTime(): Long { + val fa = RandomAccessFile(diskFile, "rwd") val bytes = ByteArray(6) fa.seek(320L) fa.read(bytes) + fa.close() return bytes.toInt48() } @@ -388,9 +454,11 @@ removefile: * @return last modified time of the root directory */ fun getLastModifiedTime(): Long { + val fa = RandomAccessFile(diskFile, "rwd") val bytes = ByteArray(6) fa.seek(326L) fa.read(bytes) + fa.close() return bytes.toInt48() } @@ -398,18 +466,22 @@ removefile: * redefines creation time of the root directory */ fun setCreationTime(time_t: Long) { + val fa = RandomAccessFile(diskFile, "rwd") val bytes = ByteArray(6) fa.seek(320L) fa.write(time_t.toInt48()) + fa.close() } /** * redefines last modified time of the root directory */ fun setLastModifiedTime(time_t: Long) { + val fa = RandomAccessFile(diskFile, "rwd") val bytes = ByteArray(6) fa.seek(326L) fa.write(time_t.toInt48()) + fa.close() } /////////////////////////////////////////////////////// @@ -436,6 +508,9 @@ removefile: }*/ fun appendEntry(entry: DiskEntry) { + val fa = RandomAccessFile(diskFile, "rwd") + + // val parentDir = requestFile(entry.parentEntryID)!! val id = entry.entryID // val parent = entry.parentEntryID @@ -456,9 +531,14 @@ removefile: // append modified directory // entryToOffsetTable[parent] = fa.filePointer + 8 // parentDir.serialize().forEach { fa.writeByte(it.toInt()) } + + fa.close() } fun deleteEntry(id: EntryID) { + val fa = RandomAccessFile(diskFile, "rwd") + + val entry = requestFile(id)!! // val parentDir = requestFile(entry.parentEntryID)!! // val parent = entry.parentEntryID @@ -476,6 +556,8 @@ removefile: // append modified directory // entryToOffsetTable[id] = appendAt + 8 // parentDir.serialize().forEach { fa.writeByte(it.toInt()) } + + fa.close() } fun appendEntries(entries: List) = entries.forEach { appendEntry(it) } @@ -523,9 +605,9 @@ removefile: debugPrintln("[DiskSkimmer.getEntryBlockSize] offset for entry $id = $offset") val fis = FileInputStream(diskFile) - fis.skip(offset + 8) + fis.skipNBytes(offset + 8) val type = fis.read().toByte() - fis.skip(272) // skip name, timestamp and CRC + fis.skipNBytes(272) // skip name, timestamp and CRC val ret: Long @@ -582,4 +664,14 @@ removefile: this[6].toUlong().shl(8) or this[7].toUlong() } + + fun setDiskName(name: String, charset: Charset) { + val fa = RandomAccessFile(diskFile, "rwd") + val bytes = name.toEntryName(268, charset) + fa.seek(10L) + fa.write(bytes, 0, 32) + fa.seek(60L) + fa.write(bytes, 32, 236) + fa.close() + } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/savegame/VirtualDisk.kt b/src/net/torvald/terrarum/savegame/VirtualDisk.kt index 5e50f99e7..4b5ffb202 100644 --- a/src/net/torvald/terrarum/savegame/VirtualDisk.kt +++ b/src/net/torvald/terrarum/savegame/VirtualDisk.kt @@ -254,6 +254,7 @@ object VDSaveKind { object VDFileID { const val ROOT = 0L const val SAVEGAMEINFO = -1L + const val PLAYER_JSON = -1L const val THUMBNAIL = -2L const val SPRITEDEF = -2L const val SPRITEDEF_GLOW = -3L diff --git a/src/net/torvald/terrarum/ui/UIItemHorizontalFadeSlide.kt b/src/net/torvald/terrarum/ui/UIItemHorizontalFadeSlide.kt index 88fdb9ff8..f95e66c56 100644 --- a/src/net/torvald/terrarum/ui/UIItemHorizontalFadeSlide.kt +++ b/src/net/torvald/terrarum/ui/UIItemHorizontalFadeSlide.kt @@ -1,6 +1,6 @@ package net.torvald.terrarum.ui -import com.jme3.math.FastMath +import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.INGAME import net.torvald.terrarum.modulebasegame.ui.NullUI import kotlin.math.absoluteValue @@ -36,13 +36,13 @@ class UIItemHorizontalFadeSlide( override val uis: List; get() = listOf(leftUI, centreUI, rightUI) - fun setLeftUI(index: Int) { + fun setLeftUIto(index: Int) { leftUI = uisOnLeft[index] } - fun setCentreUI(index: Int) { + fun setCentreUIto(index: Int) { centreUI = uisOnCentre[index] } - fun setRightUI(index: Int) { + fun setRightUIto(index: Int) { rightUI = uisOnRight[index] } @@ -79,4 +79,31 @@ class UIItemHorizontalFadeSlide( } INGAME.setTooltipMessage(null) } + + override fun dispose() { + uisOnLeft.forEach { try { it.dispose() } catch (e: IllegalArgumentException) {} } + uisOnCentre.forEach { try { it.dispose() } catch (e: IllegalArgumentException) {} } + uisOnRight.forEach { try { it.dispose() } catch (e: IllegalArgumentException) {} } + } + + override fun keyDown(keycode: Int): Boolean { + return super.keyDown(keycode) + } + + override fun keyUp(keycode: Int): Boolean { + return super.keyUp(keycode) + } + + override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { + printdbg(this, "touchDown UIs: ${uis.joinToString { it.javaClass.simpleName }}") + return super.touchDown(screenX, screenY, pointer, button) + } + + override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { + return super.touchUp(screenX, screenY, pointer, button) + } + + override fun scrolled(amountX: Float, amountY: Float): Boolean { + return super.scrolled(amountX, amountY) + } } \ No newline at end of file