world portal share code

This commit is contained in:
minjaesong
2023-09-03 17:06:02 +09:00
parent 6a78cf6a41
commit 65b610ce01
13 changed files with 286 additions and 33 deletions

BIN
assets/graphics/fonts/code.tga LFS Normal file

Binary file not shown.

View File

@@ -26,6 +26,7 @@ import net.torvald.terrarum.gamecontroller.KeyToggler;
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent;
import net.torvald.terrarum.gameitems.GameItem;
import net.torvald.terrarum.gameworld.GameWorld;
import net.torvald.terrarum.imagefont.BigAlphNum;
import net.torvald.terrarum.imagefont.TinyAlphNum;
import net.torvald.terrarum.langpack.Lang;
import net.torvald.terrarum.modulebasegame.IngameRenderer;
@@ -198,6 +199,7 @@ public class App implements ApplicationListener {
/** Big interchar */
public static TerrarumSansBitmap fontUITitle;
public static TinyAlphNum fontSmallNumbers;
public static BigAlphNum fontBigNumbers;
/** A gamepad. Multiple gamepads may controll this single virtualised gamepad. */
public static TerrarumController gamepad = null;
@@ -828,6 +830,7 @@ public class App implements ApplicationListener {
fontGame.dispose();
fontGameFBO.dispose();
fontSmallNumbers.dispose();
fontBigNumbers.dispose();
ItemSlotImageFactory.INSTANCE.dispose();
logo.getTexture().dispose();
@@ -1007,6 +1010,7 @@ public class App implements ApplicationListener {
fontSmallNumbers = TinyAlphNum.INSTANCE;
fontBigNumbers = BigAlphNum.INSTANCE;
IME.invoke();
inputStrober = InputStrober.INSTANCE;

View File

@@ -0,0 +1,111 @@
package net.torvald.terrarum.imagefont
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.GlyphLayout
import net.torvald.terrarum.roundToFloat
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2023-09-03.
*/
object BigAlphNum : BitmapFont() {
internal const val W = 12
internal const val H = 16
internal val fontSheet = TextureRegionPack("./assets/graphics/fonts/code.tga", W, H)
private const val interchar = 1
init {
setOwnsTexture(true)
setUseIntegerPositions(true)
}
fun getWidth(str: String): Int {
var l = 0
for (char in str) {
if (!isColourCodeHigh(char) && !isColourCodeLow(char)) {
l += 1
}
}
return (W + interchar) * l
}
lateinit var colMain: Color
lateinit var colShadow: Color
override fun draw(batch: Batch, text: CharSequence, x: Float, y: Float): GlyphLayout? {
val originalColour = batch.color.cpy()
colMain = batch.color.cpy()
colShadow = colMain.cpy().mul(0.5f, 0.5f, 0.5f, 1f)
val x = x.roundToFloat()
val y = y.roundToFloat()
var charsPrinted = 0
text.forEachIndexed { index, c ->
if (isColourCodeHigh(c)) {
val cchigh = c
val cclow = text[index + 1]
val colour = getColour(cchigh, cclow)
colMain = colour
colShadow = colMain.cpy().mul(0.5f, 0.5f, 0.5f, 1f)
}
else if (c in 0.toChar()..255.toChar()) {
val ccode = c.code - 48
if (ccode in 0..47) {
batch.color = colShadow
batch.draw(fontSheet.get(ccode % 16, ccode / 16), x + charsPrinted * (W + interchar) + 1, y)
batch.draw(fontSheet.get(ccode % 16, ccode / 16), x + charsPrinted * (W + interchar), y + 1)
batch.draw(fontSheet.get(ccode % 16, ccode / 16), x + charsPrinted * (W + interchar) + 1, y + 1)
batch.color = colMain
batch.draw(fontSheet.get(ccode % 16, ccode / 16), x + charsPrinted * (W + interchar), y)
}
charsPrinted += 1
}
}
batch.color = originalColour
return null
}
override fun getLineHeight() = H.toFloat()
override fun getCapHeight() = getLineHeight()
override fun getXHeight() = getLineHeight()
private fun isColourCodeHigh(c: Char) = c.code in 0b110110_1111000000..0b110110_1111111111
private fun isColourCodeLow(c: Char) = c.code in 0b110111_0000000000..0b110111_1111111111
private fun getColour(charHigh: Char, charLow: Char): Color { // input: 0x10ARGB, out: RGBA8888
val codePoint = Character.toCodePoint(charHigh, charLow)
if (colourBuffer.containsKey(codePoint))
return colourBuffer[codePoint]!!
val a = codePoint.and(0xF000).ushr(12)
val r = codePoint.and(0x0F00).ushr(8)
val g = codePoint.and(0x00F0).ushr(4)
val b = codePoint.and(0x000F)
val col = Color(r.shl(28) or r.shl(24) or g.shl(20) or g.shl(16) or b.shl(12) or b.shl(8) or a.shl(4) or a)
colourBuffer[codePoint] = col
return col
}
private val colourBuffer = HashMap<Int, Color>()
}

View File

@@ -12,8 +12,8 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
*/
object TinyAlphNum : BitmapFont() {
internal val W = 7
internal val H = 13
internal const val W = 7
internal const val H = 13
internal val fontSheet = TextureRegionPack("./assets/graphics/fonts/7x13_Tamzen7x14b.tga", W, H)

View File

@@ -69,6 +69,7 @@ class UIWorldPortal : UICanvas(
val transitionalListing = UIWorldPortalListing(this)
val transitionalDelete = UIWorldPortalDelete(this)
val transitionalRename = UIWorldPortalRename(this)
val transitionalShare = UIWorldPortalShare(this)
// val transitionalCargo = UIWorldPortalCargo(this)
private val transitionPanel = UIItemHorizontalFadeSlide(
this,
@@ -78,7 +79,7 @@ class UIWorldPortal : UICanvas(
App.scr.height,
0f,
listOf(transitionalListing),
listOf(transitionalSearch, transitionalDelete, transitionalRename),
listOf(transitionalSearch, transitionalDelete, transitionalRename, transitionalShare),
listOf()
)
@@ -87,6 +88,7 @@ class UIWorldPortal : UICanvas(
internal fun queueUpSearchScr() { transitionPanel.setCentreUIto(0) }
internal fun queueUpDeleteScr() { transitionPanel.setCentreUIto(1) }
internal fun queueUpRenameScr() { transitionPanel.setCentreUIto(2) }
internal fun queueUpShareScr() { transitionPanel.setCentreUIto(3) }
internal fun changePanelTo(index: Int) {
transitionPanel.requestTransition(index)

View File

@@ -4,7 +4,6 @@ import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.utils.Disposable
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.gameactors.AVKey
@@ -23,7 +22,6 @@ import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItem
import net.torvald.terrarum.ui.UIItemTextButton
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.unicode.EMDASH
import java.time.Instant
import java.time.format.DateTimeFormatter
@@ -52,9 +50,9 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
private val thumbw = 378
private val thumbw = 400
private val textAreaW = thumbw - 32
private val thumbh = 252
private val thumbh = 268
private val hx = Toolkit.drawWidth.div(2)
private val y = INVENTORY_CELLS_OFFSET_Y() + 1 - 34
@@ -62,14 +60,16 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
private val listHeight = UIItemWorldCellsSimple.height + (listCount - 1) * (UIItemWorldCellsSimple.height + gridGap)
private val memoryGaugeWidth = textAreaW
private val deleteButtonWidth = (thumbw - gridGap) / 2
private val buttonWidth = (UIItemWorldCellsSimple.width + thumbw - 3 * gridGap) / 5 // thumbw + cellw + gridgap = 4*gridgap + 5x
private val buttonsY = y + listHeight + gridGap
private val screencapX = hx - thumbw - gridGap/2
private val buttonSearch = UIItemTextButton(this,
{ Lang["CONTEXT_WORLD_NEW"] },
hx - gridGap/2 - 2*deleteButtonWidth - gridGap,
screencapX,
buttonsY,
deleteButtonWidth,
buttonWidth,
hasBorder = true,
alignment = UIItemTextButton.Companion.Alignment.CENTRE
).also {
@@ -80,9 +80,9 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
}
private val buttonTeleport = UIItemTextButton(this,
{ Lang["GAME_ACTION_TELEPORT"] },
hx - gridGap/2 - deleteButtonWidth,
screencapX + buttonWidth + gridGap,
buttonsY,
deleteButtonWidth,
buttonWidth,
hasBorder = true,
alignment = UIItemTextButton.Companion.Alignment.CENTRE
).also {
@@ -98,9 +98,9 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
}
private val buttonRename = UIItemTextButton(this,
{ Lang["MENU_LABEL_RENAME"] },
hx + gridGap/2,
screencapX + (buttonWidth + gridGap) * 2,
buttonsY,
deleteButtonWidth,
buttonWidth,
hasBorder = true,
alignment = UIItemTextButton.Companion.Alignment.CENTRE
).also {
@@ -111,9 +111,9 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
}
private val buttonDelete = UIItemTextButton(this,
{ Lang["MENU_LABEL_DELETE_WORLD"] },
hx + gridGap/2 + deleteButtonWidth + gridGap,
screencapX + (buttonWidth + gridGap) * 3,
buttonsY,
deleteButtonWidth,
buttonWidth,
hasBorder = true,
alignment = UIItemTextButton.Companion.Alignment.CENTRE,
inactiveCol = Toolkit.Theme.COL_RED, activeCol = Toolkit.Theme.COL_REDD
@@ -123,6 +123,19 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
full.changePanelTo(1)
}
}
private val buttonShare = UIItemTextButton(this,
{ Lang["MENU_LABEL_SHARE"] },
screencapX + (buttonWidth + gridGap) * 4,
buttonsY,
buttonWidth,
hasBorder = true,
alignment = UIItemTextButton.Companion.Alignment.CENTRE,
).also {
it.clickOnceListener = { _,_ ->
full.queueUpShareScr()
full.changePanelTo(1)
}
}
private val navRemoCon = UIItemListNavBarVertical(full, hx + 6 + UIItemWorldCellsSimple.width, y + 7, listHeight + 2, false)
@@ -146,6 +159,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
buttonRename.isEnabled = false
buttonDelete.isEnabled = false
buttonTeleport.isEnabled = false
buttonShare.isEnabled = false
currentWorldSelected = false
}
@@ -157,6 +171,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
buttonRename.isEnabled = true
buttonDelete.isEnabled = info.uuid != INGAME.world.worldIndex
buttonTeleport.isEnabled = info.uuid != INGAME.world.worldIndex
buttonShare.isEnabled = true
currentWorldSelected = info.uuid == INGAME.world.worldIndex
}
}
@@ -171,6 +186,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
addUIitem(buttonRename)
addUIitem(buttonTeleport)
addUIitem(buttonSearch)
addUIitem(buttonShare)
addUIitem(navRemoCon)
}
@@ -344,13 +360,13 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
// draw background //
// screencap panel
batch.color = UIInventoryFull.CELL_COL
Toolkit.fillArea(batch, hx - thumbw - gridGap/2, y, thumbw, thumbh)
Toolkit.fillArea(batch, screencapX, y, thumbw, thumbh)
// draw border //
// screencap panel
batch.color = if (selected?.worldInfo == null) Toolkit.Theme.COL_INVENTORY_CELL_BORDER else Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, hx - thumbw - gridGap/2 - 1, y - 1, thumbw + 2, thumbh + 2)
Toolkit.drawBoxBorder(batch, screencapX - 1, y - 1, thumbw + 2, thumbh + 2)
// memory gauge
@@ -375,9 +391,9 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
if (selected?.worldInfo != null) {
// background for texts panel
batch.color = Toolkit.Theme.COL_CELL_FILL
Toolkit.fillArea(batch, hx - thumbw - gridGap/2, y + thumbh + 3, thumbw, 10 + worldTexts.size * textualListHeight.toInt())
Toolkit.fillArea(batch, screencapX, y + thumbh + 3, thumbw, 10 + worldTexts.size * textualListHeight.toInt())
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, hx - thumbw - gridGap/2 - 1, y + thumbh + 2, thumbw + 2, 10 + worldTexts.size * textualListHeight.toInt() + 2)
Toolkit.drawBoxBorder(batch, screencapX - 1, y + thumbh + 2, thumbw + 2, 10 + worldTexts.size * textualListHeight.toInt() + 2)
// some texts
batch.color = Color.WHITE
@@ -390,7 +406,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
// thumbnail
selected?.worldInfo?.screenshot?.let {
batch.draw(it, (hx - thumbw - gridGap/2).toFloat(), y.toFloat(), thumbw.toFloat(), thumbh.toFloat())
batch.draw(it, (screencapX).toFloat(), y.toFloat(), thumbw.toFloat(), thumbh.toFloat())
}
}
@@ -402,7 +418,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
// control hints
batch.color = Color.WHITE
App.fontGame.draw(batch, full.portalListingControlHelp, hx - thumbw - gridGap/2 + 2, (full.yEnd - 20).toInt())
App.fontGame.draw(batch, full.portalListingControlHelp, screencapX + 2, (full.yEnd - 20).toInt())
}
override fun hide() {
@@ -485,11 +501,11 @@ class UIItemWorldCellsSimple(
var forceMouseDown = false
companion object {
const val width = 378
const val width = 400
const val height = 46
}
override val width: Int = 378
override val width: Int = 400
override val height: Int = 46
private val icons = CommonResourcePool.getAsTextureRegionPack("terrarum-basegame-worldportalicons")

View File

@@ -0,0 +1,94 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.imagefont.BigAlphNum
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.serialise.toBig64
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItemTextButton
import net.torvald.terrarum.utils.PasswordBase32
class UIWorldPortalShare(private val full: UIWorldPortal) : UICanvas() {
override var width = 426
override var height = 400
private val drawX = (Toolkit.drawWidth - width) / 2
private val drawY = (App.scr.height - height) / 2
private val goButtonWidth = 180
private val buttonY = drawY + height - 24
private val backButton = UIItemTextButton(this,
{ Lang["MENU_LABEL_BACK"] }, drawX + (width - goButtonWidth) / 2, buttonY, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true).also {
it.clickOnceListener = { _,_ ->
full.changePanelTo(0)
}
}
init {
addUIitem(backButton)
}
override fun updateUI(delta: Float) {
uiItems.forEach { it.update(delta) }
}
private var shareCode = ""
private var dash = ' '
override fun show() {
shareCode = PasswordBase32.encode(
INGAME.world.worldIndex.mostSignificantBits.toBig64() +
INGAME.world.worldIndex.mostSignificantBits.toBig64()
).let { it.substring(0, it.indexOf('=')) }.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)}"
}
printdbg(this, shareCode)
}
override fun renderUI(batch: SpriteBatch, camera: OrthographicCamera) {
batch.color = Color.WHITE
// share code background
batch.color = Toolkit.Theme.COL_INVENTORY_CELL_BORDER
Toolkit.drawBoxBorder(batch, drawX - 1, drawY + (height / 2) - 6, width + 2, BigAlphNum.H + 12)
batch.color = Toolkit.Theme.COL_CELL_FILL
Toolkit.fillArea(batch, drawX, drawY + (height / 2) - 5, width, BigAlphNum.H + 10)
// share code
batch.color = Toolkit.Theme.COL_MOUSE_UP
Toolkit.drawTextCentered(batch, App.fontBigNumbers, shareCode, width, drawX, drawY + (height / 2))
// ui title
batch.color = Color.WHITE
val titlestr = Lang["MENU_LABEL_SHARE"]
App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontUITitle.getWidth(titlestr)).div(2).toFloat(), UIInventoryFull.INVENTORY_CELLS_OFFSET_Y() - 36f)
// control hints
App.fontGame.draw(batch, full.portalListingControlHelp, (Toolkit.drawWidth - width)/2 + 2, (full.yEnd - 20).toInt())
uiItems.forEach { it.render(batch, camera) }
}
override fun dispose() {
}
override fun doOpening(delta: Float) {
full.selectedButton?.forceMouseDown = true
}
override fun doClosing(delta: Float) {
full.selectedButton?.forceMouseDown = false
}
}

View File

@@ -16,7 +16,8 @@ fun Int.toLittleShort() = byteArrayOf(
this.and(0xFF).toByte(),
this.shr(8).and(0xFF).toByte()
)
fun Long.toLittle() = byteArrayOf(
/** Converts long as 8-byte array */
fun Long.toLittle64() = byteArrayOf(
this.and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte(),
this.ushr(16).and(0xFF).toByte(),
@@ -24,7 +25,7 @@ fun Long.toLittle() = byteArrayOf(
this.ushr(32).and(0xFF).toByte(),
this.ushr(40).and(0xFF).toByte(),
this.ushr(48).and(0xFF).toByte(),
this.ushr(56).and(0xFF).toByte()
this.ushr(56).and(0xFF).toByte(),
)
/** Converts long as 6-byte array, discarding the sign. */
fun Long.toULittle48() = byteArrayOf(
@@ -33,9 +34,21 @@ fun Long.toULittle48() = byteArrayOf(
this.ushr(16).and(0xFF).toByte(),
this.ushr(24).and(0xFF).toByte(),
this.ushr(32).and(0xFF).toByte(),
this.ushr(40).and(0xFF).toByte()
this.ushr(40).and(0xFF).toByte(),
)
fun Double.toLittle() = java.lang.Double.doubleToRawLongBits(this).toLittle()
fun Long.toBig64() = byteArrayOf(
this.ushr(56).and(0xFF).toByte(),
this.ushr(48).and(0xFF).toByte(),
this.ushr(40).and(0xFF).toByte(),
this.ushr(32).and(0xFF).toByte(),
this.ushr(24).and(0xFF).toByte(),
this.ushr(16).and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte(),
this.and(0xFF).toByte(),
)
fun Double.toLittle() = java.lang.Double.doubleToRawLongBits(this).toLittle64()
fun Boolean.toLittle() = byteArrayOf(if (this) 0xFF.toByte() else 0.toByte())
fun ByteArray.toLittleInt32(offset: Int = 0) =

View File

@@ -9,7 +9,7 @@ object Base32Test {
val testStr = "정 참판 양반댁 규수 혼례 치른 날. 123456709".toByteArray()
val pwd = "béchamel".toByteArray()
val enc = PasswordBase32.encode(testStr, pwd)
val enc = PasswordBase32.encode(testStr, pwd).let { it.substring(0, it.indexOf('=')) }
val dec = PasswordBase32.decode(enc, testStr.size, pwd)
println("Encoded text: $enc")

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.tests
import net.torvald.terrarum.savegame.ByteArray64GrowableOutputStream
import net.torvald.terrarum.serialise.toLittle
import net.torvald.terrarum.serialise.toLittle64
import net.torvald.terrarum.serialise.toULittle48
import java.util.zip.Deflater
import java.util.zip.DeflaterOutputStream
@@ -27,7 +28,7 @@ Ooh, black and yellow! Let's shake it up a little.""".trimIndent().toByteArray()
//fun wb(byte: Int) { outputStream.write(byte) }
fun wi32(int: Int) { wb(int.toLittle()) }
fun wi48(long: Long) { wb(long.toULittle48()) }
fun wi64(long: Long) { wb(long.toLittle()) }
fun wi64(long: Long) { wb(long.toLittle64()) }
fun wf32(float: Float) { wi32(float.toRawBits()) }

View File

@@ -2,7 +2,6 @@ package net.torvald.terrarum.tests
import net.torvald.random.HQRNG
import net.torvald.random.XXHash32
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.serialise.toLittle
import java.util.*

View File

@@ -10,6 +10,8 @@ import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath
import net.torvald.random.HQRNG
import net.torvald.terrarum.*
import net.torvald.terrarum.imagefont.BigAlphNum
import net.torvald.terrarum.imagefont.TinyAlphNum
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.roundToInt
@@ -126,6 +128,14 @@ object Toolkit : Disposable {
val tw = font.getWidth(text)
font.draw(batch, text, tbx + (tbw - tw) / 2, tby)
}
fun drawTextCentered(batch: SpriteBatch, font: TinyAlphNum, text: String, tbw: Int, tbx: Int, tby: Int) {
val tw = font.getWidth(text)
font.draw(batch, text, (tbx + (tbw - tw) / 2).toFloat(), tby.toFloat())
}
fun drawTextCentered(batch: SpriteBatch, font: BigAlphNum, text: String, tbw: Int, tbx: Int, tby: Int) {
val tw = font.getWidth(text)
font.draw(batch, text, (tbx + (tbw - tw) / 2).toFloat(), tby.toFloat())
}
fun fillArea(batch: SpriteBatch, x: Int, y: Int, w: Int, h: Int) {
batch.draw(textureWhiteSquare, x.toFloat(), y.toFloat(), w.toFloat(), h.toFloat())

View File

@@ -10,7 +10,7 @@ import kotlin.experimental.xor
*/
object PasswordBase32 {
private val stringSet = "YBNDRFG8EJKMCPQXOTLVWIS2A345H769="
private val stringSet = "YBNDRFG8EJKMCP2XOTLVUIS2A345H769="
private val substituteSet = hashMapOf(
Pair('0', 'O'),
@@ -128,7 +128,7 @@ object PasswordBase32 {
buffer[appendCount] = byte.toByte() xor (password[appendCount % password.size])
appendCount++
}
fun sbyteOf(i: Int) = stringSet.indexOf(input[i]).and(0x1F)
fun sbyteOf(i: Int) = stringSet.indexOf(input.getOrElse(i) { '=' }).and(0x1F)
try {
/*