PeachScript.kt
1package com.peachpasswords.vault23/**4 * PeachScript outer codec — maps each payload byte to one Private-Use-Area code5 * point in U+E000..U+E0FF, and back. Byte-for-byte the browser's `peachScript.ts`:6 * byte 0 → U+E000, byte 255 → U+E0FF. The bundled PeachScript font renders those7 * 256 code points; outside Peach the string is unreadable boxes (intended).8 */9object PeachScript {10 private const val BASE = 0xE0001112 /** Encode binary [bytes] to a PeachScript string (one code point per byte). */13 fun encode(bytes: ByteArray): String {14 val sb = StringBuilder(bytes.size)15 for (b in bytes) sb.appendCodePoint(BASE + (b.toInt() and 0xff))16 return sb.toString()17 }1819 /**20 * Decode a PeachScript string back to bytes. Throws on any code point outside21 * U+E000..U+E0FF (edited / truncated / not PeachScript), matching the browser's22 * INVALID_PEACHSCRIPT_CHARACTER.23 */24 fun decode(str: String): ByteArray {25 val cps = str.codePoints().toArray()26 val out = ByteArray(cps.size)27 for (i in cps.indices) {28 val cp = cps[i]29 require(cp in BASE..(BASE + 0xff)) { "INVALID_PEACHSCRIPT_CHARACTER" }30 out[i] = (cp - BASE).toByte()31 }32 return out33 }3435 /** Strip all whitespace before decoding (browser `normalisePeachScript`). */36 fun normalise(str: String): String = str.replace(Regex("\\s+"), "")3738 /**39 * Cheap pre-flight: non-empty and every non-whitespace char is in the PUA40 * range. Used to gate the import button without throwing.41 */42 fun isPeachScript(str: String): Boolean {43 val t = normalise(str)44 if (t.isEmpty()) return false45 var i = 046 while (i < t.length) {47 val cp = t.codePointAt(i)48 if (cp < BASE || cp > BASE + 0xff) return false49 i += Character.charCount(cp)50 }51 return true52 }53}