package com.peachpasswords.vault /** * PeachScript outer codec — maps each payload byte to one Private-Use-Area code * point in U+E000..U+E0FF, and back. Byte-for-byte the browser's `peachScript.ts`: * byte 0 → U+E000, byte 255 → U+E0FF. The bundled PeachScript font renders those * 256 code points; outside Peach the string is unreadable boxes (intended). */ object PeachScript { private const val BASE = 0xE000 /** Encode binary [bytes] to a PeachScript string (one code point per byte). */ fun encode(bytes: ByteArray): String { val sb = StringBuilder(bytes.size) for (b in bytes) sb.appendCodePoint(BASE + (b.toInt() and 0xff)) return sb.toString() } /** * Decode a PeachScript string back to bytes. Throws on any code point outside * U+E000..U+E0FF (edited / truncated / not PeachScript), matching the browser's * INVALID_PEACHSCRIPT_CHARACTER. */ fun decode(str: String): ByteArray { val cps = str.codePoints().toArray() val out = ByteArray(cps.size) for (i in cps.indices) { val cp = cps[i] require(cp in BASE..(BASE + 0xff)) { "INVALID_PEACHSCRIPT_CHARACTER" } out[i] = (cp - BASE).toByte() } return out } /** Strip all whitespace before decoding (browser `normalisePeachScript`). */ fun normalise(str: String): String = str.replace(Regex("\\s+"), "") /** * Cheap pre-flight: non-empty and every non-whitespace char is in the PUA * range. Used to gate the import button without throwing. */ fun isPeachScript(str: String): Boolean { val t = normalise(str) if (t.isEmpty()) return false var i = 0 while (i < t.length) { val cp = t.codePointAt(i) if (cp < BASE || cp > BASE + 0xff) return false i += Character.charCount(cp) } return true } }