mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
new import screen
This commit is contained in:
@@ -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"
|
||||
}
|
||||
@@ -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. 아바타 파일 이름을 아래에 입력하고 가져오기를 눌러주세요"
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user