new import screen

This commit is contained in:
minjaesong
2023-08-25 15:49:35 +09:00
parent eeee1ebdbc
commit 75fcb5be5b
8 changed files with 184 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
{
"CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "This is a world currently playing.",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_1": "Copy the Avatar Code into the clipboard, then hit the Paste button below.",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_2": ""
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_1": "1. Place the Avatar file into the following directory:",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_2": "",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_3": "2. Enter the name of the file below, then press Import"
}

View File

@@ -1,5 +1,5 @@
{
"CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "현재 플레이 중인 월드입니다.",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_1": "아바타 코드를 클립보드에 복사한 다음, 아래의 붙여넣기 버튼을 눌러주세요.",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_2": ""
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_1": "1. 아바타 파일을 다음 폴더에 넣어주세요",
"CONTEXT_IMPORT_AVATAR_INSTRUCTION_3": "2. 아바타 파일 이름을 아래에 입력하고 가져오기를 눌러주세요"
}

View File

@@ -1185,6 +1185,8 @@ public class App implements ApplicationListener {
public static String configDir;
/** defaultDir + "/LoadOrder.txt" */
public static String loadOrderDir;
/** defaultDir + "/Imported" */
public static String importDir;
public static RunningEnvironment environment;
@@ -1224,6 +1226,7 @@ public class App implements ApplicationListener {
loadOrderDir = defaultDir + "/LoadOrder.txt";
recycledPlayersDir = defaultDir + "/Recycled/Players";
recycledWorldsDir = defaultDir + "/Recycled/Worlds";
importDir = defaultDir + "/Imports";
System.out.println(String.format("os.name = %s (with identifier %s)", OSName, operationSystem));
System.out.println(String.format("os.version = %s", OSVersion));
@@ -1239,6 +1242,7 @@ public class App implements ApplicationListener {
new File(worldsDir),
new File(recycledPlayersDir),
new File(recycledWorldsDir),
new File(importDir)
};
for (File it : dirs) {

View File

@@ -8,6 +8,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.utils.OpenFile
import java.awt.Desktop
import java.io.File
@@ -90,7 +91,7 @@ class NoModuleDefaultTitlescreen(batch: FlippingSpriteBatch) : IngameInstance(ba
App.scr.hf - Gdx.input.y in pathButtonY - 12..pathButtonY + pathButtonH + 12)
if (mouseOnLink && Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)) {
Desktop.getDesktop().open(pathFile)
OpenFile(pathFile)
}
fbatch.inUse {

View File

@@ -6,15 +6,22 @@ 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.App.printdbg
import net.torvald.terrarum.AppUpdateListOfSavegames
import net.torvald.terrarum.Second
import net.torvald.terrarum.ceilToInt
import net.torvald.terrarum.gamecontroller.*
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.serialise.Ascii85Codec
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UIItem
import net.torvald.terrarum.ui.UIItemTextButton
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.ui.*
import net.torvald.terrarum.utils.Clipboard
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.utils.OpenFile
import java.awt.Desktop
import java.io.File
/**
* Created by minjaesong on 2023-08-24.
@@ -31,14 +38,23 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
private val rows = 30
private val goButtonWidth = 180
private val descStartY = 24 * 4
private val lh = App.fontGame.lineHeight.toInt()
private val codeBox = UIItemCodeBox(this, (Toolkit.drawWidth - App.fontSmallNumbers.W * cols) / 2, drawY, cols, rows)
// private val codeBox = UIItemCodeBox(this, (Toolkit.drawWidth - App.fontSmallNumbers.W * cols) / 2, drawY, cols, rows)
private val inputWidth = 340
private val filenameInput = UIItemTextLineInput(this,
(Toolkit.drawWidth - inputWidth) / 2, (App.scr.height - height) / 2 + descStartY + (5) * lh, inputWidth,
maxLen = InputLenCap(256, InputLenCap.CharLenUnit.UTF8_BYTES)
)
/*
private val clearButton = UIItemTextButton(this,
{ Lang["MENU_IO_CLEAR"] }, drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24 - 34, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
private val pasteButton = UIItemTextButton(this,
{ Lang["MENU_LABEL_PASTE"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24 - 34, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
*/
private val backButton = UIItemTextButton(this,
{ Lang["MENU_LABEL_BACK"] }, drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
@@ -46,32 +62,63 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
{ Lang["MENU_IO_IMPORT"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
init {
addUIitem(codeBox)
addUIitem(clearButton)
addUIitem(pasteButton)
// addUIitem(codeBox)
// addUIitem(clearButton)
// addUIitem(pasteButton)
addUIitem(filenameInput)
addUIitem(backButton)
addUIitem(goButton)
clearButton.clickOnceListener = { _,_ ->
/*clearButton.clickOnceListener = { _,_ ->
codeBox.clearTextBuffer()
}
pasteButton.clickOnceListener = { _,_ ->
codeBox.pasteFromClipboard()
}
}*/
backButton.clickOnceListener = { _,_ ->
remoCon.openUI(UILoadSavegame(remoCon))
}
goButton.clickOnceListener = { _,_ ->
doImport()
val returnCode = doImport()
if (returnCode == 0) remoCon.openUI(UILoadSavegame(remoCon))
}
}
// private var textX = 0
private var textY = 0
private var mouseOnLink = false
private var pathW = 0
override fun updateUI(delta: Float) {
uiItems.forEach { it.update(delta) }
pathW = App.fontGame.getWidth(App.importDir)
val textX = (Toolkit.drawWidth - pathW) / 2
textY = (App.scr.height - height) / 2 + descStartY + (1) * lh
mouseOnLink = (Gdx.input.x in textX - 48..textX + 48 + pathW &&
Gdx.input.y in textY - 12..textY + lh + 12)
if (mouseOnLink && Gdx.input.isButtonJustPressed(Input.Buttons.LEFT)) {
OpenFile(File(App.importDir))
}
}
private val textboxIndices = (1..3)
override fun renderUI(batch: SpriteBatch, camera: Camera) {
batch.color = Color.WHITE
val textboxWidth = textboxIndices.maxOf { App.fontGame.getWidth(Lang["CONTEXT_IMPORT_AVATAR_INSTRUCTION_$it"]) }
val textX = (Toolkit.drawWidth - textboxWidth) / 2
// draw texts
for (i in textboxIndices) {
App.fontGame.draw(batch, Lang["CONTEXT_IMPORT_AVATAR_INSTRUCTION_$i"], textX, (App.scr.height - height) / 2 + descStartY + (i - 1) * lh)
}
// draw path
batch.color = if (mouseOnLink) Toolkit.Theme.COL_SELECTED else Toolkit.Theme.COL_MOUSE_UP
App.fontGame.draw(batch, App.importDir, (Toolkit.drawWidth - pathW) / 2, textY)
uiItems.forEach { it.render(batch, camera) }
}
@@ -81,13 +128,68 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
override fun advanceMode(button: UIItem) {
}
private fun doImport() {
val rawStr = codeBox.textBuffer.toString()
// sanity check
private fun doImport(): Int {
val file = File("${App.importDir}/${filenameInput.getText()}")
// check file's existence
if (!file.exists()) {
return 1
}
// try to mount the TEVd
try {
val dom = VDUtil.readDiskArchive(file)
val timeNow = App.getTIME_T()
// get the uuid
val oldPlayerInfoFile = dom.getEntry(SAVEGAMEINFO)!!
val playerInfo = JsonFetcher.readFromJsonString(ByteArray64Reader(VDUtil.getAsNormalFile(dom, SAVEGAMEINFO).bytes, Common.CHARSET))
val uuid = playerInfo.getString("uuid")
val newFile = File("${App.playersDir}/$uuid")
printdbg(this, "Avatar uuid: $uuid")
if (newFile.exists()) return 2
// update playerinfo so that:
// totalPlayTime to zero
// lastPlayedTime to now
// playerinfofile's lastModifiedTime to now
// root's lastModifiedTime to now
printdbg(this, "avatar old lastPlayTime: ${playerInfo.getLong("lastPlayTime")}")
printdbg(this, "avatar old totalPlayTime: ${playerInfo.getLong("totalPlayTime")}")
playerInfo.get("lastPlayTime").set(timeNow, null)
playerInfo.get("totalPlayTime").set(0, null)
printdbg(this, "avatar new lastPlayTime: ${playerInfo.getLong("lastPlayTime")}")
printdbg(this, "avatar new totalPlayTime: ${playerInfo.getLong("totalPlayTime")}")
val newJsonBytes = ByteArray64Writer(Common.CHARSET).let {
// println(playerInfo.toString())
it.write(playerInfo.toString())
it.close()
it.toByteArray64()
}
val newPlayerInfo = DiskEntry(SAVEGAMEINFO, ROOT, oldPlayerInfoFile.creationDate, timeNow, EntryFile(newJsonBytes))
VDUtil.addFile(dom, newPlayerInfo)
dom.getEntry(ROOT)!!.modificationDate = timeNow
// mark the file as Imported
dom.saveOrigin = VDSaveOrigin.IMPORTED
// write modified file to the Players dir
VDUtil.dumpToRealMachine(dom, newFile)
val ascii85codec = Ascii85Codec((33..117).map { it.toChar() }.joinToString(""))
val ascii85str = rawStr.substring(2 until rawStr.length - 2).replace("z", "!!!!!")
AppUpdateListOfSavegames()
}
catch (e: Throwable) {
// format error
e.printStackTrace()
return -1
}
return 0
}
}

View File

@@ -447,6 +447,13 @@ removefile:
fa.close()
}
fun setSaveOrigin(bits: Int) {
val fa = RandomAccessFile(diskFile, "rwd")
fa.seek(51L)
fa.writeByte(bits)
fa.close()
}
/**
* @return Save type (0b 0000 00ab)
* b: unset - full save; set - quicksave (only applicable to worlds -- quicksave just means the disk is in dirty state)
@@ -467,6 +474,15 @@ removefile:
return fa.read().also { fa.close() }
}
/**
* @return 16 if the savegame was imported, 0 if the savegame was generated in-game
*/
fun getSaveOrigin(): Int {
val fa = RandomAccessFile(diskFile, "rwd")
fa.seek(51L)
return fa.read().also { fa.close() }
}
override fun getDiskName(charset: Charset): String {

View File

@@ -76,8 +76,11 @@ Version 254 is a customised version of TEVD tailored to be used as a savegame fo
0: Undefined (or very old version of the game)
1: Player Data
2: World Data
Int8[13] Extra info bytes reserved for future usage
/* END extraInfoBytes */
Int8 Savefile Origin Flags
0: Created in-game
16: Imported
Int8[12] Extra info bytes reserved for future usage
-- END extraInfoBytes --
UInt8[236] Rest of the long disk name (268 bytes total)
(Header size: 300 bytes)
@@ -150,6 +153,9 @@ class VirtualDisk(
var saveKind: Int
set(value) { extraInfoBytes[2] = value.toByte() }
get() = extraInfoBytes[2].toUint()
var saveOrigin: Int
set(value) { extraInfoBytes[3] = value.toByte() }
get() = extraInfoBytes[3].toUint()
override fun getDiskName(charset: Charset) = diskName.toCanonicalString(charset)
val root: DiskEntry
get() = entries[0]!!
@@ -249,6 +255,11 @@ object VDSaveKind {
const val WORLD_DATA = 2
}
object VDSaveOrigin {
const val INGAME = 0
const val IMPORTED = 16
}
object VDFileID {
const val ROOT = 0L
const val SAVEGAMEINFO = -1L

View File

@@ -1,24 +1,43 @@
package net.torvald.terrarum.utils
import net.torvald.terrarum.App
import java.awt.Desktop
import java.awt.Toolkit
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.StringSelection
import java.awt.datatransfer.UnsupportedFlavorException
import java.io.File
/**
* Created by minjaesong on 2016-07-31.
*/
object Clipboard {
fun fetch(): String = try {
Toolkit.getDefaultToolkit().systemClipboard.getData(DataFlavor.stringFlavor) as String
}
catch (e: UnsupportedFlavorException) {
""
}
private val IS_MACOS = App.operationSystem == "OSX"
fun fetch(): String =
if (IS_MACOS) "Clipboard is disabled on macOS" else
try {
Toolkit.getDefaultToolkit().systemClipboard.getData(DataFlavor.stringFlavor) as String
}
catch (e: UnsupportedFlavorException) {
""
}
fun copy(s: String) {
if (IS_MACOS) return
val selection = StringSelection(s)
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
clipboard.setContents(selection, selection)
}
}
/**
* Created by minjaesong on 2023-08-25.
*/
object OpenFile {
private val IS_MACOS = App.operationSystem == "OSX"
operator fun invoke(file: File) {
if (IS_MACOS) return // at this point macOS might as well be a bane of existence for "some" devs Apple fanboys think they are not worthy of existence
Desktop.getDesktop().open(file)
}
}