mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-09 18:14:06 +09:00
working invitation code via portal
This commit is contained in:
BIN
assets/graphics/fonts/code.tga
LFS
BIN
assets/graphics/fonts/code.tga
LFS
Binary file not shown.
@@ -7,5 +7,6 @@
|
|||||||
"CONTEXT_WORLD_CODE_SHARE_2": "world when you are away.",
|
"CONTEXT_WORLD_CODE_SHARE_2": "world when you are away.",
|
||||||
"CONTEXT_WORLD_CODE_SHARE_3": "",
|
"CONTEXT_WORLD_CODE_SHARE_3": "",
|
||||||
"CONTEXT_WORLD_CODE_SHARE_4": "Share the code below so the other people can join!",
|
"CONTEXT_WORLD_CODE_SHARE_4": "Share the code below so the other people can join!",
|
||||||
"ERROR_AVATAR_ALREADY_EXISTS": "The Avatar already exists."
|
"ERROR_AVATAR_ALREADY_EXISTS": "The Avatar already exists.",
|
||||||
|
"ERROR_WORLD_NOT_FOUND": "World not found."
|
||||||
}
|
}
|
||||||
@@ -6,5 +6,6 @@
|
|||||||
"CONTEXT_WORLD_CODE_SHARE_2": "플레이할 수 있도록 합니다.",
|
"CONTEXT_WORLD_CODE_SHARE_2": "플레이할 수 있도록 합니다.",
|
||||||
"CONTEXT_WORLD_CODE_SHARE_3": "",
|
"CONTEXT_WORLD_CODE_SHARE_3": "",
|
||||||
"CONTEXT_WORLD_CODE_SHARE_4": "아래의 코드를 공유해 다른 사람을 초대하세요!",
|
"CONTEXT_WORLD_CODE_SHARE_4": "아래의 코드를 공유해 다른 사람을 초대하세요!",
|
||||||
"ERROR_AVATAR_ALREADY_EXISTS": "이미 존재하는 아바타입니다."
|
"ERROR_AVATAR_ALREADY_EXISTS": "이미 존재하는 아바타입니다.",
|
||||||
|
"ERROR_WORLD_NOT_FOUND": "월드를 찾을 수 없습니다."
|
||||||
}
|
}
|
||||||
@@ -120,5 +120,9 @@ class FixtureWorldPortal : Electric {
|
|||||||
internal data class TeleportRequest(
|
internal data class TeleportRequest(
|
||||||
val worldDiskToLoad: DiskSkimmer?, // for loading existing worlds
|
val worldDiskToLoad: DiskSkimmer?, // for loading existing worlds
|
||||||
val worldLoadParam: TerrarumIngame.NewWorldParameters? // for creating new world
|
val worldLoadParam: TerrarumIngame.NewWorldParameters? // for creating new world
|
||||||
)
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "TeleportRequest(disk: ${worldDiskToLoad?.diskFile?.name}, param: $worldLoadParam)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -32,63 +32,47 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
|
|
||||||
private val drawX = (Toolkit.drawWidth - width) / 2
|
private val drawX = (Toolkit.drawWidth - width) / 2
|
||||||
private val drawY = (App.scr.height - height) / 2
|
private val drawY = (App.scr.height - height) / 2
|
||||||
private val cols = 80
|
|
||||||
private val rows = 30
|
|
||||||
private val goButtonWidth = 180
|
private val goButtonWidth = 180
|
||||||
|
|
||||||
private val descStartY = 24 * 4
|
private val descStartY = 24 * 4
|
||||||
private val lh = App.fontGame.lineHeight.toInt()
|
private val lh = App.fontGame.lineHeight.toInt()
|
||||||
|
|
||||||
// private val codeBox = UIItemCodeBox(this, (Toolkit.drawWidth - App.fontSmallNumbers.W * cols) / 2, drawY, cols, rows)
|
|
||||||
|
|
||||||
private val inputWidth = 340
|
private val inputWidth = 340
|
||||||
private val filenameInput = UIItemTextLineInput(this,
|
private val filenameInput = UIItemTextLineInput(this,
|
||||||
(Toolkit.drawWidth - inputWidth) / 2, (App.scr.height - height) / 2 + descStartY + (5) * lh, inputWidth,
|
(Toolkit.drawWidth - inputWidth) / 2, (App.scr.height - height) / 2 + descStartY + (5) * lh, inputWidth,
|
||||||
maxLen = InputLenCap(256, InputLenCap.CharLenUnit.UTF8_BYTES)
|
maxLen = InputLenCap(256, InputLenCap.CharLenUnit.UTF8_BYTES)
|
||||||
)
|
).also {
|
||||||
|
// reset importReturnCode if the text input has changed
|
||||||
/*
|
it.onKeyDown = { _ ->
|
||||||
private val clearButton = UIItemTextButton(this,
|
importReturnCode = 0
|
||||||
{ 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,
|
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)
|
{ Lang["MENU_LABEL_BACK"] }, drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
||||||
private val goButton = UIItemTextButton(this,
|
|
||||||
{ Lang["MENU_IO_IMPORT"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
|
||||||
|
|
||||||
private var importReturnCode = 0
|
it.clickOnceListener = { _,_ ->
|
||||||
|
|
||||||
init {
|
|
||||||
// addUIitem(codeBox)
|
|
||||||
// addUIitem(clearButton)
|
|
||||||
// addUIitem(pasteButton)
|
|
||||||
addUIitem(filenameInput)
|
|
||||||
addUIitem(backButton)
|
|
||||||
addUIitem(goButton)
|
|
||||||
|
|
||||||
/*clearButton.clickOnceListener = { _,_ ->
|
|
||||||
codeBox.clearTextBuffer()
|
|
||||||
}
|
|
||||||
pasteButton.clickOnceListener = { _,_ ->
|
|
||||||
codeBox.pasteFromClipboard()
|
|
||||||
}*/
|
|
||||||
backButton.clickOnceListener = { _,_ ->
|
|
||||||
remoCon.openUI(UILoadSavegame(remoCon))
|
remoCon.openUI(UILoadSavegame(remoCon))
|
||||||
}
|
}
|
||||||
goButton.clickOnceListener = { _,_ ->
|
}
|
||||||
|
private val goButton = UIItemTextButton(this,
|
||||||
|
{ Lang["MENU_IO_IMPORT"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
||||||
|
|
||||||
|
it.clickOnceListener = { _,_ ->
|
||||||
if (filenameInput.getText().isNotBlank()) {
|
if (filenameInput.getText().isNotBlank()) {
|
||||||
importReturnCode = doImport()
|
importReturnCode = doImport()
|
||||||
if (importReturnCode == 0) remoCon.openUI(UILoadSavegame(remoCon))
|
if (importReturnCode == 0) remoCon.openUI(UILoadSavegame(remoCon))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var importReturnCode = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
addUIitem(filenameInput)
|
||||||
|
addUIitem(backButton)
|
||||||
|
addUIitem(goButton)
|
||||||
|
|
||||||
// reset importReturnCode if the text input has changed
|
|
||||||
filenameInput.onKeyDown = { _ ->
|
|
||||||
importReturnCode = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// private var textX = 0
|
// private var textX = 0
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.ui
|
package net.torvald.terrarum.modulebasegame.ui
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Camera
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
import com.badlogic.gdx.graphics.Texture
|
import com.badlogic.gdx.graphics.Texture
|
||||||
@@ -21,8 +20,11 @@ import net.torvald.terrarum.serialise.Common
|
|||||||
import net.torvald.terrarum.modulebasegame.serialise.ReadActor
|
import net.torvald.terrarum.modulebasegame.serialise.ReadActor
|
||||||
import net.torvald.terrarum.savegame.DiskSkimmer
|
import net.torvald.terrarum.savegame.DiskSkimmer
|
||||||
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
||||||
|
import net.torvald.terrarum.serialise.toBigInt64
|
||||||
import net.torvald.terrarum.ui.*
|
import net.torvald.terrarum.ui.*
|
||||||
|
import net.torvald.terrarum.utils.PasswordBase32
|
||||||
import net.torvald.terrarum.utils.RandomWordsName
|
import net.torvald.terrarum.utils.RandomWordsName
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2021-10-25.
|
* Created by minjaesong on 2021-10-25.
|
||||||
@@ -56,7 +58,7 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
private val radioCellWidth = 120
|
private val radioCellWidth = 120
|
||||||
private val inputWidth = 350
|
private val inputWidth = 350
|
||||||
private val radioX = (width - (radioCellWidth * NEW_WORLD_SIZE.size + 9)) / 2
|
private val radioX = (width - (radioCellWidth * NEW_WORLD_SIZE.size + 9)) / 2
|
||||||
private val inputX = width - inputWidth
|
private val inputX = drawX + width - inputWidth + 5
|
||||||
|
|
||||||
private val sizeSelY = 186 + 40
|
private val sizeSelY = 186 + 40
|
||||||
|
|
||||||
@@ -85,74 +87,166 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
|
|
||||||
private val inputLineY1 = 90
|
private val inputLineY1 = 90
|
||||||
private val inputLineY2 = 130
|
private val inputLineY2 = 130
|
||||||
|
private val goButtonWidth = 180
|
||||||
|
private val gridGap = 10
|
||||||
|
private val buttonBaseX = (Toolkit.drawWidth - 3 * goButtonWidth - 2 * gridGap) / 2
|
||||||
|
private val buttonY = drawY + height - 24
|
||||||
|
|
||||||
|
private var mode = 0 // 0: new world, 1: use invitation
|
||||||
|
|
||||||
|
private var uiItemsChangeRequest: (() -> Unit)? = null
|
||||||
|
|
||||||
private val nameInput = UIItemTextLineInput(this,
|
private val nameInput = UIItemTextLineInput(this,
|
||||||
drawX + width - inputWidth + 5, drawY + sizeSelY + inputLineY1, inputWidth,
|
inputX, drawY + sizeSelY + inputLineY1, inputWidth,
|
||||||
{ RandomWordsName(4) }, InputLenCap(VirtualDisk.NAME_LENGTH, InputLenCap.CharLenUnit.UTF8_BYTES))
|
{ RandomWordsName(4) }, InputLenCap(VirtualDisk.NAME_LENGTH, InputLenCap.CharLenUnit.UTF8_BYTES))
|
||||||
|
|
||||||
private val seedInput = UIItemTextLineInput(this,
|
private val seedInput = UIItemTextLineInput(this,
|
||||||
drawX + width - inputWidth + 5, drawY + sizeSelY + inputLineY2, inputWidth,
|
inputX, drawY + sizeSelY + inputLineY2, inputWidth,
|
||||||
{ rng.nextLong().toString() }, InputLenCap(256, InputLenCap.CharLenUnit.CODEPOINTS))
|
{ rng.nextLong().toString() }, InputLenCap(256, InputLenCap.CharLenUnit.CODEPOINTS))
|
||||||
|
|
||||||
private val goButtonWidth = 180
|
private val codeInput = UIItemTextLineInput(this,
|
||||||
|
inputX, drawY + sizeSelY, inputWidth,
|
||||||
|
{ "AAAA BB CCCCC DDDDD EEEEE FFFFF" }, InputLenCap(31, InputLenCap.CharLenUnit.CODEPOINTS)).also {
|
||||||
|
|
||||||
|
// reset importReturnCode if the text input has changed
|
||||||
|
it.onKeyDown = { _ ->
|
||||||
|
importReturnCode = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val backButton = UIItemTextButton(this,
|
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).also {
|
{ Lang["MENU_LABEL_BACK"] }, buttonBaseX, buttonY, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
||||||
|
|
||||||
it.clickOnceListener = { _, _ ->
|
it.clickOnceListener = { _, _ ->
|
||||||
remoCon.openUI(UILoadSavegame(remoCon))
|
remoCon.openUI(UILoadSavegame(remoCon))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private val useInvitationButton = UIItemTextButton(this,
|
||||||
|
{ Lang["MENU_LABEL_USE_CODE"] }, buttonBaseX + goButtonWidth + gridGap, buttonY, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
||||||
|
|
||||||
|
it.clickOnceListener = { _, _ ->
|
||||||
|
if (mode == 0) {
|
||||||
|
it.textfun = { Lang["CONTEXT_WORLD_NEW"] }
|
||||||
|
uiItemsChangeRequest = {
|
||||||
|
uiItems.clear()
|
||||||
|
addUIitem(codeInput)
|
||||||
|
addUIitem(goButton)
|
||||||
|
addUIitem(it)
|
||||||
|
addUIitem(backButton)
|
||||||
|
}
|
||||||
|
mode = 1
|
||||||
|
}
|
||||||
|
else if (mode == 1) {
|
||||||
|
it.textfun = { Lang["MENU_LABEL_USE_CODE"] }
|
||||||
|
uiItemsChangeRequest = {
|
||||||
|
uiItems.clear()
|
||||||
|
addUIitem(sizeSelector)
|
||||||
|
addUIitem(seedInput) // order is important
|
||||||
|
addUIitem(nameInput) // because of the IME candidates overlay
|
||||||
|
addUIitem(goButton)
|
||||||
|
addUIitem(it)
|
||||||
|
addUIitem(backButton)
|
||||||
|
}
|
||||||
|
mode = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private val goButton = UIItemTextButton(this,
|
private val goButton = UIItemTextButton(this,
|
||||||
{ Lang["MENU_LABEL_CONFIRM_BUTTON"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
{ Lang["MENU_LABEL_CONFIRM_BUTTON"] }, buttonBaseX + (goButtonWidth + gridGap) * 2, buttonY, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
||||||
|
|
||||||
it.clickOnceListener = { _, _ ->
|
it.clickOnceListener = { _, _ ->
|
||||||
|
|
||||||
// after the save is complete, proceed to new world generation
|
|
||||||
if (existingPlayer == null) {
|
if (mode == 0) {
|
||||||
newPlayerCreationThread.start()
|
// after the save is complete, proceed to new world generation
|
||||||
newPlayerCreationThread.join()
|
if (existingPlayer == null) {
|
||||||
}
|
newPlayerCreationThread.start()
|
||||||
|
newPlayerCreationThread.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}")
|
printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}")
|
||||||
|
|
||||||
val ingame = TerrarumIngame(App.batch)
|
val ingame = TerrarumIngame(App.batch)
|
||||||
val playerDisk = existingPlayer ?: App.savegamePlayers[UILoadGovernor.playerUUID]!!.loadable()
|
val playerDisk = existingPlayer ?: App.savegamePlayers[UILoadGovernor.playerUUID]!!.loadable()
|
||||||
val player = ReadActor.invoke(playerDisk, ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer
|
val player = ReadActor.invoke(
|
||||||
val seed = try {
|
playerDisk,
|
||||||
seedInput.getTextOrPlaceholder().toLong()
|
ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)
|
||||||
}
|
) as IngamePlayer
|
||||||
catch (e: NumberFormatException) {
|
val seed = try {
|
||||||
XXHash64.hash(seedInput.getTextOrPlaceholder().toByteArray(Charsets.UTF_8), 10000)
|
seedInput.getTextOrPlaceholder().toLong()
|
||||||
}
|
}
|
||||||
val (wx, wy) = TerrarumIngame.NEW_WORLD_SIZE[sizeSelector.selection]
|
catch (e: NumberFormatException) {
|
||||||
val worldParam = TerrarumIngame.NewGameParams(
|
XXHash64.hash(seedInput.getTextOrPlaceholder().toByteArray(Charsets.UTF_8), 10000)
|
||||||
|
}
|
||||||
|
val (wx, wy) = TerrarumIngame.NEW_WORLD_SIZE[sizeSelector.selection]
|
||||||
|
val worldParam = TerrarumIngame.NewGameParams(
|
||||||
player, TerrarumIngame.NewWorldParameters(
|
player, TerrarumIngame.NewWorldParameters(
|
||||||
wx, wy, seed, nameInput.getTextOrPlaceholder()
|
wx, wy, seed, nameInput.getTextOrPlaceholder()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
ingame.gameLoadInfoPayload = worldParam
|
ingame.gameLoadInfoPayload = worldParam
|
||||||
ingame.gameLoadMode = TerrarumIngame.GameLoadMode.CREATE_NEW
|
ingame.gameLoadMode = TerrarumIngame.GameLoadMode.CREATE_NEW
|
||||||
|
|
||||||
Terrarum.setCurrentIngameInstance(ingame)
|
Terrarum.setCurrentIngameInstance(ingame)
|
||||||
val loadScreen = WorldgenLoadScreen(ingame, wx, wy)
|
val loadScreen = WorldgenLoadScreen(ingame, wx, wy)
|
||||||
App.setLoadScreen(loadScreen)
|
App.setLoadScreen(loadScreen)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val code = codeInput.getText().replace(" ", "")
|
||||||
|
val uuid = PasswordBase32.decode(code, 16).let {
|
||||||
|
UUID(it.toBigInt64(0), it.toBigInt64(8))
|
||||||
|
}
|
||||||
|
val world = App.savegameWorlds[uuid]
|
||||||
|
|
||||||
|
printdbg(this, "Decoded UUID=$uuid")
|
||||||
|
|
||||||
|
// world exists?
|
||||||
|
if (world == null) {
|
||||||
|
importReturnCode = 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TODO()
|
||||||
|
|
||||||
|
// after the save is complete, proceed to importing
|
||||||
|
if (existingPlayer == null) {
|
||||||
|
newPlayerCreationThread.start()
|
||||||
|
newPlayerCreationThread.join()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var importReturnCode = 0
|
||||||
|
private val errorMessages = listOf(
|
||||||
|
"", // 0
|
||||||
|
Lang["ERROR_WORLD_NOT_FOUND"], // 1
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addUIitem(sizeSelector)
|
addUIitem(sizeSelector)
|
||||||
addUIitem(seedInput) // order is important
|
addUIitem(seedInput) // order is important
|
||||||
addUIitem(nameInput) // because of the IME candidates overlay
|
addUIitem(nameInput) // because of the IME candidates overlay
|
||||||
addUIitem(goButton)
|
addUIitem(goButton)
|
||||||
|
addUIitem(useInvitationButton)
|
||||||
addUIitem(backButton)
|
addUIitem(backButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
super.show()
|
||||||
|
seedInput.clearText()
|
||||||
|
nameInput.clearText()
|
||||||
|
codeInput.clearText()
|
||||||
|
importReturnCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateUI(delta: Float) {
|
override fun updateUI(delta: Float) {
|
||||||
|
if (uiItemsChangeRequest != null) {
|
||||||
|
uiItemsChangeRequest!!()
|
||||||
|
uiItemsChangeRequest = null
|
||||||
|
}
|
||||||
|
|
||||||
uiItems.forEach { it.update(delta) }
|
uiItems.forEach { it.update(delta) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,23 +256,41 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
// val titlestr = Lang["CONTEXT_WORLD_NEW"]
|
// val titlestr = Lang["CONTEXT_WORLD_NEW"]
|
||||||
// App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontUITitle.getWidth(titlestr)).div(2).toFloat(), titleTextPosY.toFloat())
|
// App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontUITitle.getWidth(titlestr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||||
|
|
||||||
// draw size previews
|
if (mode == 0) {
|
||||||
val texture = tex[sizeSelector.selection.coerceAtMost(tex.lastIndex)]
|
// draw size previews
|
||||||
val tx = drawX + (width - texture.regionWidth) / 2
|
val texture = tex[sizeSelector.selection.coerceAtMost(tex.lastIndex)]
|
||||||
val ty = drawY + (160 - texture.regionHeight) / 2
|
val tx = drawX + (width - texture.regionWidth) / 2
|
||||||
batch.draw(texture, tx.toFloat(), ty.toFloat())
|
val ty = drawY + (160 - texture.regionHeight) / 2
|
||||||
// border
|
batch.draw(texture, tx.toFloat(), ty.toFloat())
|
||||||
batch.color = Toolkit.Theme.COL_INACTIVE
|
// border
|
||||||
Toolkit.drawBoxBorder(batch, tx - 1, ty - 1, texture.regionWidth + 2, texture.regionHeight + 2)
|
batch.color = Toolkit.Theme.COL_INACTIVE
|
||||||
|
Toolkit.drawBoxBorder(batch, tx - 1, ty - 1, texture.regionWidth + 2, texture.regionHeight + 2)
|
||||||
|
|
||||||
batch.color = Color.WHITE
|
batch.color = Color.WHITE
|
||||||
// size selector title
|
// size selector title
|
||||||
val sizestr = Lang["MENU_OPTIONS_SIZE"]
|
val sizestr = Lang["MENU_OPTIONS_SIZE"]
|
||||||
App.fontGame.draw(batch, sizestr, drawX + (width - App.fontGame.getWidth(sizestr)).div(2).toFloat(), drawY + sizeSelY - 40f)
|
App.fontGame.draw(
|
||||||
|
batch,
|
||||||
|
sizestr,
|
||||||
|
drawX + (width - App.fontGame.getWidth(sizestr)).div(2).toFloat(),
|
||||||
|
drawY + sizeSelY - 40f
|
||||||
|
)
|
||||||
|
|
||||||
// name/seed input labels
|
// name/seed input labels
|
||||||
App.fontGame.draw(batch, Lang["MENU_NAME"], drawX - 4, drawY + sizeSelY + inputLineY1)
|
App.fontGame.draw(batch, Lang["MENU_NAME"], drawX - 4, drawY + sizeSelY + inputLineY1)
|
||||||
App.fontGame.draw(batch, Lang["CONTEXT_GENERATOR_SEED"], drawX - 4, drawY + sizeSelY + inputLineY2)
|
App.fontGame.draw(batch, Lang["CONTEXT_GENERATOR_SEED"], drawX - 4, drawY + sizeSelY + inputLineY2)
|
||||||
|
}
|
||||||
|
else if (mode == 1) {
|
||||||
|
// code input labels
|
||||||
|
App.fontGame.draw(batch, Lang["CREDITS_CODE"], drawX - 4, drawY + sizeSelY)
|
||||||
|
|
||||||
|
if (importReturnCode != 0) {
|
||||||
|
batch.color = Toolkit.Theme.COL_RED
|
||||||
|
val tby = codeInput.posY
|
||||||
|
val btny = backButton.posY
|
||||||
|
Toolkit.drawTextCentered(batch, App.fontGame, errorMessages[importReturnCode], Toolkit.drawWidth, 0, (tby + btny) / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uiItems.forEach { it.render(batch, camera) }
|
uiItems.forEach { it.render(batch, camera) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ class UIShare : UICanvas() {
|
|||||||
override fun show() {
|
override fun show() {
|
||||||
shareCode = PasswordBase32.encode(
|
shareCode = PasswordBase32.encode(
|
||||||
INGAME.world.worldIndex.mostSignificantBits.toBig64() +
|
INGAME.world.worldIndex.mostSignificantBits.toBig64() +
|
||||||
INGAME.world.worldIndex.mostSignificantBits.toBig64()
|
INGAME.world.worldIndex.leastSignificantBits.toBig64()
|
||||||
).let { it.substring(0, it.indexOf('=')) }.let {
|
).let {
|
||||||
"${it.substring(0..3)}$dash${it.substring(4..5)}$dash${it.substring(6..10)}$dash${it.substring(11..15)}$dash${it.substring(16..20)}$dash${it.substring(21)}"
|
"${it.substring(0..3)}$dash${it.substring(4..5)}$dash${it.substring(6..10)}$dash${it.substring(11..15)}$dash${it.substring(16..20)}$dash${it.substring(21)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
App.printdbg(this, shareCode)
|
App.printdbg(this, shareCode)
|
||||||
|
App.printdbg(this, INGAME.world.worldIndex)
|
||||||
|
|
||||||
wotKeys = (1..4).map { Lang["CONTEXT_WORLD_CODE_SHARE_$it", false] }
|
wotKeys = (1..4).map { Lang["CONTEXT_WORLD_CODE_SHARE_$it", false] }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class UIWorldPortalShare(private val full: UIWorldPortal) : UICanvas() {
|
|||||||
override fun show() {
|
override fun show() {
|
||||||
shareCode = PasswordBase32.encode(
|
shareCode = PasswordBase32.encode(
|
||||||
INGAME.world.worldIndex.mostSignificantBits.toBig64() +
|
INGAME.world.worldIndex.mostSignificantBits.toBig64() +
|
||||||
INGAME.world.worldIndex.mostSignificantBits.toBig64()
|
INGAME.world.worldIndex.leastSignificantBits.toBig64()
|
||||||
).let { it.substring(0, it.indexOf('=')) }.let {
|
).let {
|
||||||
"${it.substring(0..3)}$dash${it.substring(4..5)}$dash${it.substring(6..10)}$dash${it.substring(11..15)}$dash${it.substring(16..20)}$dash${it.substring(21)}"
|
"${it.substring(0..3)}$dash${it.substring(4..5)}$dash${it.substring(6..10)}$dash${it.substring(11..15)}$dash${it.substring(16..20)}$dash${it.substring(21)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ import com.badlogic.gdx.graphics.Color
|
|||||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
import net.torvald.terrarum.App
|
import net.torvald.terrarum.App
|
||||||
|
import net.torvald.terrarum.INGAME
|
||||||
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
||||||
import net.torvald.terrarum.langpack.Lang
|
import net.torvald.terrarum.langpack.Lang
|
||||||
import net.torvald.terrarum.printStackTrace
|
import net.torvald.terrarum.printStackTrace
|
||||||
import net.torvald.terrarum.savegame.VirtualDisk
|
import net.torvald.terrarum.savegame.VirtualDisk
|
||||||
|
import net.torvald.terrarum.serialise.toBigInt64
|
||||||
import net.torvald.terrarum.ui.*
|
import net.torvald.terrarum.ui.*
|
||||||
|
import net.torvald.terrarum.utils.PasswordBase32
|
||||||
import net.torvald.terrarum.utils.RandomWordsName
|
import net.torvald.terrarum.utils.RandomWordsName
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2023-09-03.
|
* Created by minjaesong on 2023-09-03.
|
||||||
@@ -34,9 +38,14 @@ class UIWorldPortalUseInvitation(val full: UIWorldPortal) : UICanvas() {
|
|||||||
|
|
||||||
|
|
||||||
private val codeInput = UIItemTextLineInput(this,
|
private val codeInput = UIItemTextLineInput(this,
|
||||||
drawX + width - inputWidth + 5, drawY + sizeSelY + inputLineY1, inputWidth,
|
drawX + width - inputWidth + 5, drawY + sizeSelY, inputWidth,
|
||||||
{ "AAAA BB CCCCC DDDDD EEEEE FFFFF" }, InputLenCap(VirtualDisk.NAME_LENGTH, InputLenCap.CharLenUnit.UTF8_BYTES)
|
{ "AAAA BB CCCCC DDDDD EEEEE FFFFF" }, InputLenCap(31, InputLenCap.CharLenUnit.CODEPOINTS)
|
||||||
)
|
).also {
|
||||||
|
// reset importReturnCode if the text input has changed
|
||||||
|
it.onKeyDown = { _ ->
|
||||||
|
importReturnCode = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -59,10 +68,35 @@ class UIWorldPortalUseInvitation(val full: UIWorldPortal) : UICanvas() {
|
|||||||
{ Lang["MENU_LABEL_CONFIRM_BUTTON"] }, buttonBaseX + (goButtonWidth + gridGap) * 2, buttonY, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
{ Lang["MENU_LABEL_CONFIRM_BUTTON"] }, buttonBaseX + (goButtonWidth + gridGap) * 2, buttonY, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
|
||||||
|
|
||||||
it.clickOnceListener = { _, _ ->
|
it.clickOnceListener = { _, _ ->
|
||||||
|
val code = codeInput.getText().replace(" ", "")
|
||||||
|
val uuid = PasswordBase32.decode(code, 16).let {
|
||||||
|
UUID(it.toBigInt64(0), it.toBigInt64(8))
|
||||||
|
}
|
||||||
|
val world = App.savegameWorlds[uuid]
|
||||||
|
|
||||||
|
App.printdbg(this, "Decoded UUID=$uuid")
|
||||||
|
|
||||||
|
// world exists?
|
||||||
|
if (world == null) {
|
||||||
|
importReturnCode = 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// add the world to the player's worldbook
|
||||||
|
// TODO memory cap check? or disable check against shared worlds?
|
||||||
|
full.addWorldToPlayersDict(uuid)
|
||||||
|
full.cleanUpWorldDict()
|
||||||
|
|
||||||
|
full.requestTransition(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var importReturnCode = 0
|
||||||
|
private val errorMessages = listOf(
|
||||||
|
"", // 0
|
||||||
|
Lang["ERROR_WORLD_NOT_FOUND"], // 1
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addUIitem(backButton)
|
addUIitem(backButton)
|
||||||
addUIitem(searchWorldButton)
|
addUIitem(searchWorldButton)
|
||||||
@@ -70,20 +104,32 @@ class UIWorldPortalUseInvitation(val full: UIWorldPortal) : UICanvas() {
|
|||||||
addUIitem(codeInput)
|
addUIitem(codeInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
super.show()
|
||||||
|
codeInput.clearText()
|
||||||
|
importReturnCode = 0
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateUI(delta: Float) {
|
override fun updateUI(delta: Float) {
|
||||||
uiItems.forEach { it.update(delta) }
|
uiItems.forEach { it.update(delta) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun renderUI(batch: SpriteBatch, camera: OrthographicCamera) {
|
override fun renderUI(batch: SpriteBatch, camera: OrthographicCamera) {
|
||||||
|
// error messages
|
||||||
|
if (importReturnCode != 0) {
|
||||||
|
batch.color = Toolkit.Theme.COL_RED
|
||||||
|
val tby = codeInput.posY
|
||||||
|
val btny = backButton.posY
|
||||||
|
Toolkit.drawTextCentered(batch, App.fontGame, errorMessages[importReturnCode], Toolkit.drawWidth, 0, (tby + btny) / 2)
|
||||||
|
}
|
||||||
|
|
||||||
// input labels
|
// input labels
|
||||||
batch.color = Color.WHITE
|
batch.color = Color.WHITE
|
||||||
App.fontGame.draw(batch, Lang["CREDITS_CODE"], drawX - 4, drawY + sizeSelY + inputLineY1)
|
App.fontGame.draw(batch, Lang["CREDITS_CODE"], drawX - 4, drawY + sizeSelY)
|
||||||
|
|
||||||
// control hints
|
// control hints
|
||||||
App.fontGame.draw(batch, full.portalListingControlHelp, 2 + (Toolkit.drawWidth - 560)/2 + 2, (full.yEnd - 20).toInt())
|
App.fontGame.draw(batch, full.portalListingControlHelp, 2 + (Toolkit.drawWidth - 560)/2 + 2, (full.yEnd - 20).toInt())
|
||||||
|
|
||||||
|
|
||||||
uiItems.forEach { it.render(batch, camera) }
|
uiItems.forEach { it.render(batch, camera) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
package net.torvald.terrarum.tests
|
package net.torvald.terrarum.tests
|
||||||
|
|
||||||
|
import net.torvald.terrarum.serialise.toBig64
|
||||||
import net.torvald.terrarum.utils.PasswordBase32
|
import net.torvald.terrarum.utils.PasswordBase32
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
object Base32Test {
|
object Base32Test {
|
||||||
|
|
||||||
operator fun invoke() {
|
operator fun invoke() {
|
||||||
val testStr = "정 참판 양반댁 규수 혼례 치른 날. 123456709".toByteArray()
|
val testStr = UUID.fromString("145efab2-d465-4e1e-abae-db6c809817a9").let {
|
||||||
val pwd = "béchamel".toByteArray()
|
it.mostSignificantBits.toBig64() + it.leastSignificantBits.toBig64()
|
||||||
|
}
|
||||||
|
// val pwd = "béchamel".toByteArray()
|
||||||
|
|
||||||
val enc = PasswordBase32.encode(testStr, pwd).let { it.substring(0, it.indexOf('=')) }
|
val enc = PasswordBase32.encode(testStr)
|
||||||
val dec = PasswordBase32.decode(enc, testStr.size, pwd)
|
val dec = PasswordBase32.decode(enc, testStr.size)
|
||||||
|
|
||||||
println("Encoded text: $enc")
|
println("Encoded text: $enc")
|
||||||
println("Decoded text: ${dec.toString(Charset.defaultCharset())}")
|
println("Encoded bytes: ${testStr.joinToString(" ") { it.toInt().and(255).toString(16).padStart(2, '0') }}")
|
||||||
|
println("Decoded bytes: ${dec.joinToString(" ") { it.toInt().and(255).toString(16).padStart(2, '0') }}")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import net.torvald.terrarum.langpack.Lang
|
|||||||
open class UIItemTextButton(
|
open class UIItemTextButton(
|
||||||
parentUI: UICanvas,
|
parentUI: UICanvas,
|
||||||
/** Stored text (independent to the Langpack) */
|
/** Stored text (independent to the Langpack) */
|
||||||
val textfun: () -> String,
|
var textfun: () -> String,
|
||||||
initialX: Int,
|
initialX: Int,
|
||||||
initialY: Int,
|
initialY: Int,
|
||||||
override val width: Int,
|
override val width: Int,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.torvald.terrarum.utils
|
package net.torvald.terrarum.utils
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base32
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
|
|
||||||
|
|
||||||
@@ -10,91 +11,25 @@ import kotlin.experimental.xor
|
|||||||
*/
|
*/
|
||||||
object PasswordBase32 {
|
object PasswordBase32 {
|
||||||
|
|
||||||
private val stringSet = "YBNDRFG8EJKMCP2XOTLVUIS2A345H769="
|
private val si = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
|
||||||
|
private val so = "YBNDRFG8EJKMCPQXOTLVUIS2A345H769 "
|
||||||
|
|
||||||
private val substituteSet = hashMapOf(
|
private val standardToModified = HashMap<Char, Char>(32)
|
||||||
Pair('0', 'O'),
|
private val modifiedToStandard = HashMap<Char, Char>(32)
|
||||||
Pair('1', 'I'),
|
|
||||||
Pair('Z', '2')
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
init {
|
||||||
0 x
|
(0 until si.length).forEach {
|
||||||
1 6
|
standardToModified[si[it]] = so[it]
|
||||||
2 4
|
modifiedToStandard[so[it]] = si[it]
|
||||||
3 3
|
|
||||||
4 1
|
|
||||||
*/
|
|
||||||
val padLen = arrayOf(0, 6, 4, 3, 1)
|
|
||||||
private val nullPw = byteArrayOf(0.toByte())
|
|
||||||
|
|
||||||
|
|
||||||
private fun encodeToLetters(byteArray: ByteArray, password: ByteArray): IntArray {
|
|
||||||
val out = ArrayList<Int>()
|
|
||||||
|
|
||||||
fun get(i: Int) = byteArray[i] xor (password[i % password.size])
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
5 Bytes -> 8 Letters
|
|
||||||
|
|
||||||
0000 0000 | 1111 1111 | 2222 2222 | 3333 3333 | 4444 4444
|
|
||||||
AAAA ABBB | BBCC CCCD | DDDD EEEE | EFFF FFGG | GGGH HHHH
|
|
||||||
*/
|
|
||||||
|
|
||||||
// non-pads
|
|
||||||
(0..byteArray.lastIndex - 5 step 5).forEach {
|
|
||||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
|
||||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
|
||||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
|
||||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4) or get(it+2).toInt().and(0xF0).ushr(4))
|
|
||||||
/* E */ out.add(get(it+2).toInt().and(0xF).shl(1) or get(it+3).toInt().and(0x80).ushr(7))
|
|
||||||
/* F */ out.add(get(it+3).toInt().and(0x7C).ushr(2))
|
|
||||||
/* G */ out.add(get(it+3).toInt().and(3).shl(3) or get(it+4).toInt().and(0xE0).ushr(5))
|
|
||||||
/* H */ out.add(get(it+4).toInt().and(0x1F))
|
|
||||||
}
|
|
||||||
// pads
|
|
||||||
val residue = byteArray.size % 5
|
|
||||||
if (residue != 0){
|
|
||||||
|
|
||||||
val it = (byteArray.size / 5) * 5 // dark magic of integer division, let's hope the compiler won't "optimise" this...
|
|
||||||
|
|
||||||
when (residue) {
|
|
||||||
1 -> {
|
|
||||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
|
||||||
/* B */ out.add(get(it).toInt().and(7).shl(2))
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
|
||||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
|
||||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
|
||||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4))
|
|
||||||
}
|
|
||||||
3 -> {
|
|
||||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
|
||||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
|
||||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
|
||||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4) or get(it+2).toInt().and(0xF0).ushr(4))
|
|
||||||
/* E */ out.add(get(it+2).toInt().and(0xF).shl(1))
|
|
||||||
}
|
|
||||||
4 -> {
|
|
||||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
|
||||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
|
||||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
|
||||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4) or get(it+2).toInt().and(0xF0).ushr(4))
|
|
||||||
/* E */ out.add(get(it+2).toInt().and(0xF).shl(1) or get(it+3).toInt().and(0x80).ushr(7))
|
|
||||||
/* F */ out.add(get(it+3).toInt().and(0x7C).ushr(2))
|
|
||||||
/* G */ out.add(get(it+3).toInt().and(3).shl(3))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append padding
|
|
||||||
kotlin.repeat(padLen[residue], { out.add(32) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.toIntArray()
|
modifiedToStandard['0'] = modifiedToStandard['O']!!
|
||||||
|
modifiedToStandard['1'] = modifiedToStandard['I']!!
|
||||||
|
modifiedToStandard['Z'] = modifiedToStandard['2']!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val nullPw = byteArrayOf(0.toByte())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param bytes size of multiple of five (5, 10, 15, 20, ...) is highly recommended to prevent
|
* @param bytes size of multiple of five (5, 10, 15, 20, ...) is highly recommended to prevent
|
||||||
@@ -103,11 +38,8 @@ object PasswordBase32 {
|
|||||||
* from doing naughty things. Longer, the better.
|
* from doing naughty things. Longer, the better.
|
||||||
*/
|
*/
|
||||||
fun encode(bytes: ByteArray, password: ByteArray = nullPw): String {
|
fun encode(bytes: ByteArray, password: ByteArray = nullPw): String {
|
||||||
val plaintext = encodeToLetters(bytes, password)
|
val rawstr = Base32().encode(ByteArray(bytes.size) { bytes[it] xor password[it % password.size] }).toString(Charsets.US_ASCII)
|
||||||
val sb = StringBuilder()
|
return rawstr.map { standardToModified[it] }.joinToString("").trim()
|
||||||
plaintext.forEach { sb.append(stringSet[it]) }
|
|
||||||
|
|
||||||
return sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,36 +49,10 @@ object PasswordBase32 {
|
|||||||
* suspect user inputs and sanitise them.
|
* suspect user inputs and sanitise them.
|
||||||
*/
|
*/
|
||||||
fun decode(input: String, outByteLength: Int, password: ByteArray = nullPw): ByteArray {
|
fun decode(input: String, outByteLength: Int, password: ByteArray = nullPw): ByteArray {
|
||||||
val buffer = ByteArray(outByteLength)
|
val decInput = input.trim().map { modifiedToStandard[it] }.joinToString("")
|
||||||
var appendCount = 0
|
return Base32().decode(decInput).let { ba ->
|
||||||
var input = input.toUpperCase()
|
ByteArray(outByteLength) { ba.getOrElse(it) { 0.toByte() } xor password[it % password.size] }
|
||||||
substituteSet.forEach { from, to ->
|
|
||||||
input = input.replace(from, to)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun append(byte: Int) {
|
|
||||||
buffer[appendCount] = byte.toByte() xor (password[appendCount % password.size])
|
|
||||||
appendCount++
|
|
||||||
}
|
|
||||||
fun sbyteOf(i: Int) = stringSet.indexOf(input.getOrElse(i) { '=' }).and(0x1F)
|
|
||||||
|
|
||||||
try {
|
|
||||||
/*
|
|
||||||
8 Letters -> 5 Bytes
|
|
||||||
|
|
||||||
0000 0000 | 1111 1111 | 2222 2222 | 3333 3333 | 4444 4444
|
|
||||||
AAAA ABBB | BBCC CCCD | DDDD EEEE | EFFF FFGG | GGGH HHHH
|
|
||||||
*/
|
|
||||||
(0..input.lastIndex.plus(8) step 8).forEach {
|
|
||||||
/* 0 */ append(sbyteOf(it+0).shl(3) or sbyteOf(it+1).ushr(2))
|
|
||||||
/* 1 */ append(sbyteOf(it+1).shl(6) or sbyteOf(it+2).shl(1) or sbyteOf(it+3).ushr(4))
|
|
||||||
/* 2 */ append(sbyteOf(it+3).shl(4) or sbyteOf(it+4).ushr(1))
|
|
||||||
/* 3 */ append(sbyteOf(it+4).shl(7) or sbyteOf(it+5).shl(2) or sbyteOf(it+6).ushr(3))
|
|
||||||
/* 4 */ append(sbyteOf(it+6).shl(5) or sbyteOf(it+7))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (endOfStream: ArrayIndexOutOfBoundsException) { }
|
|
||||||
|
|
||||||
return buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user