javascript:%2F*!%20%EC%BB%B5%EC%BC%80%EC%9D%B5%20%EC%9B%B9%EB%A6%AC%EC%8A%A4%20%EB%B0%B1%EC%97%85%20%ED%92%80%EA%B8%B0%20v0.2.0%20%7C%20(c)%202026%20Ruyari%20Cupcake%20%7C%20MIT%20License%20%7C%20https%3A%2F%2Fgithub.com%2Fruyari-cupcake%2Fcupcake-webrisu-backup-unlock%20*%2F%0A(()%20%3D%3E%20%7B%0Aconst%20RisuBackupUnlock%20%3D%20(()%20%3D%3E%20%7B%0A%20%20'use%20strict'%3B%0A%0A%20%20const%20KEY_ENDPOINT%20%3D%20'https%3A%2F%2Fsv.risuai.xyz%2Fcryptokey%3Fkey%3D'%3B%0A%20%20const%20ENCRYPTION_ENTRY%20%3D%20'encryption.risudat'%3B%0A%20%20const%20DATABASE_ENTRY%20%3D%20'database.risudat'%3B%0A%20%20const%20LENGTH_BYTES%20%3D%204%3B%0A%20%20const%20HEADER_WINDOW_BYTES%20%3D%204096%3B%0A%20%20const%20MAX_NAME_BYTES%20%3D%2064%20*%201024%3B%0A%20%20const%20GCM_IV%20%3D%20new%20Uint8Array(12)%3B%0A%20%20const%20SAVE_FORMATS%20%3D%20%5B%0A%20%20%20%20%5B'risusave'%2C%20%5B82%2C%2073%2C%2083%2C%2085%2C%2083%2C%2065%2C%2086%2C%2069%2C%200%5D%5D%2C%0A%20%20%20%20%5B'compressed'%2C%20%5B0%2C%2082%2C%2073%2C%2083%2C%2085%2C%2083%2C%2065%2C%2086%2C%2069%2C%200%2C%208%5D%5D%2C%0A%20%20%20%20%5B'raw'%2C%20%5B0%2C%2082%2C%2073%2C%2083%2C%2085%2C%2083%2C%2065%2C%2086%2C%2069%2C%200%2C%207%5D%5D%2C%0A%20%20%20%20%5B'stream'%2C%20%5B0%2C%2082%2C%2073%2C%2083%2C%2085%2C%2083%2C%2065%2C%2086%2C%2069%2C%200%2C%209%5D%5D%2C%0A%20%20%5D%3B%0A%0A%20%20class%20UnlockError%20extends%20Error%20%7B%0A%20%20%20%20constructor(code%2C%20message)%20%7B%0A%20%20%20%20%20%20super(message)%3B%0A%20%20%20%20%20%20this.name%20%3D%20'UnlockError'%3B%0A%20%20%20%20%20%20this.code%20%3D%20code%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20async%20function%20readBytes(blob%2C%20start%2C%20end)%20%7B%0A%20%20%20%20return%20new%20Uint8Array(await%20blob.slice(start%2C%20end).arrayBuffer())%3B%0A%20%20%7D%0A%0A%20%20function%20readU32(bytes%2C%20offset)%20%7B%0A%20%20%20%20return%20new%20DataView(bytes.buffer%2C%20bytes.byteOffset%2C%20bytes.byteLength).getUint32(offset%2C%20true)%3B%0A%20%20%7D%0A%0A%20%20function%20truncated(offset)%20%7B%0A%20%20%20%20return%20new%20UnlockError('TRUNCATED'%2C%0A%20%20%20%20%20%20%60%EB%B0%B1%EC%97%85%20%ED%8C%8C%EC%9D%BC%EC%9D%B4%20%24%7Boffset%7D%20%EB%B0%94%EC%9D%B4%ED%8A%B8%20%EC%A7%80%EC%A0%90%EC%97%90%EC%84%9C%20%EB%81%8A%EA%B2%A8%20%EC%9E%88%EC%96%B4%EC%9A%94.%20%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EA%B0%80%20%EB%81%9D%EA%B9%8C%EC%A7%80%20%EB%90%90%EB%8A%94%EC%A7%80%20%ED%99%95%EC%9D%B8%ED%95%B4%20%EC%A3%BC%EC%84%B8%EC%9A%94.%60)%3B%0A%20%20%7D%0A%0A%20%20function%20throwIfCancelled(signal)%20%7B%0A%20%20%20%20if%20(signal%20%26%26%20signal.aborted)%20throw%20new%20UnlockError('CANCELLED'%2C%20'%EC%9E%91%EC%97%85%EC%9D%84%20%EC%B7%A8%EC%86%8C%ED%96%88%EC%96%B4%EC%9A%94.')%3B%0A%20%20%7D%0A%0A%20%20async%20function%20scanEntries(blob%2C%20onProgress%2C%20signal)%20%7B%0A%20%20%20%20const%20decoder%20%3D%20new%20TextDecoder()%3B%0A%20%20%20%20const%20entries%20%3D%20%5B%5D%3B%0A%20%20%20%20let%20offset%20%3D%200%3B%0A%20%20%20%20while%20(offset%20%3C%20blob.size)%20%7B%0A%20%20%20%20%20%20throwIfCancelled(signal)%3B%0A%20%20%20%20%20%20if%20(offset%20%2B%20LENGTH_BYTES%20%3E%20blob.size)%20throw%20truncated(offset)%3B%0A%20%20%20%20%20%20let%20header%20%3D%20await%20readBytes(blob%2C%20offset%2C%20Math.min(blob.size%2C%20offset%20%2B%20HEADER_WINDOW_BYTES))%3B%0A%20%20%20%20%20%20const%20nameLength%20%3D%20readU32(header%2C%200)%3B%0A%20%20%20%20%20%20if%20(nameLength%20%3E%20MAX_NAME_BYTES)%20%7B%0A%20%20%20%20%20%20%20%20throw%20new%20UnlockError('CORRUPT'%2C%20%60%24%7Boffset%7D%20%EB%B0%94%EC%9D%B4%ED%8A%B8%20%EC%A7%80%EC%A0%90%EC%9D%98%20%ED%95%AD%EB%AA%A9%20%EC%9D%B4%EB%A6%84%20%EA%B8%B8%EC%9D%B4%EA%B0%80%20%EB%B9%84%EC%A0%95%EC%83%81%EC%9D%B4%EC%97%90%EC%9A%94.%20%EB%A6%AC%EC%8A%A4%20%EB%B0%B1%EC%97%85%20%ED%8C%8C%EC%9D%BC%EC%9D%B4%20%EB%A7%9E%EB%82%98%EC%9A%94%3F%60)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20const%20headerLength%20%3D%20LENGTH_BYTES%20%2B%20nameLength%20%2B%20LENGTH_BYTES%3B%0A%20%20%20%20%20%20if%20(offset%20%2B%20headerLength%20%3E%20blob.size)%20throw%20truncated(offset)%3B%0A%20%20%20%20%20%20if%20(header.length%20%3C%20headerLength)%20header%20%3D%20await%20readBytes(blob%2C%20offset%2C%20offset%20%2B%20headerLength)%3B%0A%20%20%20%20%20%20const%20dataLength%20%3D%20readU32(header%2C%20LENGTH_BYTES%20%2B%20nameLength)%3B%0A%20%20%20%20%20%20const%20dataStart%20%3D%20offset%20%2B%20headerLength%3B%0A%20%20%20%20%20%20const%20end%20%3D%20dataStart%20%2B%20dataLength%3B%0A%20%20%20%20%20%20if%20(end%20%3E%20blob.size)%20throw%20truncated(offset)%3B%0A%20%20%20%20%20%20entries.push(%7B%0A%20%20%20%20%20%20%20%20name%3A%20decoder.decode(header.subarray(LENGTH_BYTES%2C%20LENGTH_BYTES%20%2B%20nameLength))%2C%0A%20%20%20%20%20%20%20%20start%3A%20offset%2C%0A%20%20%20%20%20%20%20%20dataStart%2C%0A%20%20%20%20%20%20%20%20end%2C%0A%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20offset%20%3D%20end%3B%0A%20%20%20%20%20%20if%20(onProgress)%20onProgress(offset%2C%20blob.size%2C%20entries.length)%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20entries%3B%0A%20%20%7D%0A%0A%20%20function%20parseEncryptionMeta(bytes)%20%7B%0A%20%20%20%20let%20meta%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20meta%20%3D%20JSON.parse(new%20TextDecoder().decode(bytes))%3B%0A%20%20%20%20%7D%20catch%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('BAD_META'%2C%20'encryption.risudat%20%EB%82%B4%EC%9A%A9%EC%9D%84%20%EC%9D%BD%EC%9D%84%20%EC%88%98%20%EC%97%86%EC%96%B4%EC%9A%94.')%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(!meta%20%7C%7C%20meta.type%20!%3D%3D%20'account'%20%7C%7C%20!meta.time)%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('BAD_META'%2C%20'encryption.risudat%20%ED%98%95%EC%8B%9D%EC%9D%B4%20%EC%98%88%EC%83%81%EA%B3%BC%20%EB%8B%AC%EB%9D%BC%EC%9A%94%20(type%2Ftime%20%EC%97%86%EC%9D%8C).')%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20meta%3B%0A%20%20%7D%0A%0A%20%20async%20function%20fetchKey(time%2C%20fetchImpl)%20%7B%0A%20%20%20%20let%20response%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20response%20%3D%20await%20fetchImpl(KEY_ENDPOINT%20%2B%20encodeURIComponent(String(time)))%3B%0A%20%20%20%20%7D%20catch%20(error)%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('KEY_NETWORK'%2C%0A%20%20%20%20%20%20%20%20%60%ED%82%A4%20%EC%84%9C%EB%B2%84%EC%97%90%20%EC%97%B0%EA%B2%B0%ED%95%98%EC%A7%80%20%EB%AA%BB%ED%96%88%EC%96%B4%EC%9A%94%20(%24%7Berror%20%26%26%20error.message%7D).%20risuai.xyz%20%ED%8E%98%EC%9D%B4%EC%A7%80%EC%97%90%EC%84%9C%20%EC%8B%A4%ED%96%89%ED%96%88%EB%8A%94%EC%A7%80%2C%20%EC%9D%B8%ED%84%B0%EB%84%B7%EC%9D%B4%20%EB%90%98%EB%8A%94%EC%A7%80%20%ED%99%95%EC%9D%B8%ED%95%B4%20%EC%A3%BC%EC%84%B8%EC%9A%94.%60)%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(response.status%20%3D%3D%3D%20403)%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('KEY_FORBIDDEN'%2C%20'%ED%82%A4%20%EC%84%9C%EB%B2%84%EA%B0%80%20%EA%B1%B0%EC%A0%88%ED%96%88%EC%96%B4%EC%9A%94%20(403).%20%EC%9D%B4%20%EB%8F%84%EA%B5%AC%EB%8A%94%20risuai.xyz%20%ED%8E%98%EC%9D%B4%EC%A7%80%EC%97%90%EC%84%9C%20%EC%8B%A4%ED%96%89%ED%95%B4%EC%95%BC%20%ED%95%B4%EC%9A%94.')%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(!response.ok)%20throw%20new%20UnlockError('KEY_HTTP'%2C%20%60%ED%82%A4%20%EC%84%9C%EB%B2%84%20%EC%9D%91%EB%8B%B5%EC%9D%B4%20%EB%B9%84%EC%A0%95%EC%83%81%EC%9D%B4%EC%97%90%EC%9A%94%20(HTTP%20%24%7Bresponse.status%7D).%60)%3B%0A%20%20%20%20let%20body%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20body%20%3D%20await%20response.json()%3B%0A%20%20%20%20%7D%20catch%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('KEY_BAD'%2C%20'%ED%82%A4%20%EC%84%9C%EB%B2%84%20%EC%9D%91%EB%8B%B5%EC%9D%84%20%EC%9D%BD%EC%9D%84%20%EC%88%98%20%EC%97%86%EC%96%B4%EC%9A%94.')%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(!body%20%7C%7C%20typeof%20body.key%20!%3D%3D%20'string'%20%7C%7C%20!body.key)%20throw%20new%20UnlockError('KEY_BAD'%2C%20'%ED%82%A4%20%EC%84%9C%EB%B2%84%20%EC%9D%91%EB%8B%B5%EC%97%90%20%ED%82%A4%EA%B0%80%20%EC%97%86%EC%96%B4%EC%9A%94.')%3B%0A%20%20%20%20return%20body.key%3B%0A%20%20%7D%0A%0A%20%20async%20function%20decryptDatabase(cipher%2C%20keyString%2C%20subtle)%20%7B%0A%20%20%20%20const%20digest%20%3D%20await%20subtle.digest('SHA-256'%2C%20new%20TextEncoder().encode(keyString))%3B%0A%20%20%20%20const%20key%20%3D%20await%20subtle.importKey('raw'%2C%20digest%2C%20'AES-GCM'%2C%20false%2C%20%5B'decrypt'%5D)%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20return%20new%20Uint8Array(await%20subtle.decrypt(%7B%20name%3A%20'AES-GCM'%2C%20iv%3A%20GCM_IV%20%7D%2C%20key%2C%20cipher))%3B%0A%20%20%20%20%7D%20catch%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('DECRYPT_FAILED'%2C%20'%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%20%ED%95%B4%EB%8F%85%EC%97%90%20%EC%8B%A4%ED%8C%A8%ED%96%88%EC%96%B4%EC%9A%94.%20%ED%8C%8C%EC%9D%BC%EC%9D%B4%20%EC%86%90%EC%83%81%EB%90%90%EA%B1%B0%EB%82%98%20%ED%82%A4%EA%B0%80%20%EB%A7%9E%EC%A7%80%20%EC%95%8A%EC%95%84%EC%9A%94.')%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20function%20detectSaveFormat(bytes)%20%7B%0A%20%20%20%20for%20(const%20%5Bformat%2C%20magic%5D%20of%20SAVE_FORMATS)%20%7B%0A%20%20%20%20%20%20if%20(bytes.length%20%3E%3D%20magic.length%20%26%26%20magic.every((value%2C%20index)%20%3D%3E%20bytes%5Bindex%5D%20%3D%3D%3D%20value))%20return%20format%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20'unknown'%3B%0A%20%20%7D%0A%0A%20%20function%20unlockedFileName(name)%20%7B%0A%20%20%20%20const%20base%20%3D%20String(name%20%7C%7C%20'Binary').replace(%2F%5C.bin%24%2Fi%2C%20'')%3B%0A%20%20%20%20return%20%60%24%7Bbase%7D-unlocked.bin%60%3B%0A%20%20%7D%0A%0A%20%20async%20function%20verifyOutput(output%2C%20expected)%20%7B%0A%20%20%20%20const%20entries%20%3D%20await%20scanEntries(output)%3B%0A%20%20%20%20if%20(entries.length%20!%3D%3D%20expected.length)%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('VERIFY_FAILED'%2C%20%60%EA%B2%B0%EA%B3%BC%20%ED%8C%8C%EC%9D%BC%20%ED%95%AD%EB%AA%A9%20%EC%88%98%EA%B0%80%20%EB%A7%9E%EC%A7%80%20%EC%95%8A%EC%95%84%EC%9A%94%20(%24%7Bentries.length%7D%20%E2%89%A0%20%24%7Bexpected.length%7D).%60)%3B%0A%20%20%20%20%7D%0A%20%20%20%20entries.forEach((entry%2C%20index)%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20want%20%3D%20expected%5Bindex%5D%3B%0A%20%20%20%20%20%20if%20(entry.name%20!%3D%3D%20want.name%20%7C%7C%20entry.end%20-%20entry.dataStart%20!%3D%3D%20want.dataLength)%20%7B%0A%20%20%20%20%20%20%20%20throw%20new%20UnlockError('VERIFY_FAILED'%2C%20%60%EA%B2%B0%EA%B3%BC%20%ED%8C%8C%EC%9D%BC%20%24%7Bindex%7D%EB%B2%88%EC%A7%B8%20%ED%95%AD%EB%AA%A9%EC%9D%B4%20%EC%9B%90%EB%B3%B8%EA%B3%BC%20%EB%8B%AC%EB%9D%BC%EC%9A%94%20(%24%7Bentry.name%7D).%60)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%0A%0A%20%20%2F**%0A%20%20%20*%20Turns%20an%20encrypted%20risuai.xyz%20full%20backup%20into%20a%20plain%20one.%0A%20%20%20*%20%40param%20%7BBlob%7D%20blob%20the%20original%20%60.bin%60%20backup%20(a%20File%20works)%0A%20%20%20*%20%40param%20%7B%7BfetchImpl%3F%3A%20Function%2C%20subtle%3F%3A%20SubtleCrypto%2C%20onScanProgress%3F%3A%20Function%2C%0A%20%20%20*%20%20%20onPhase%3F%3A%20(phase%3A%20'scan'%7C'key'%7C'decrypt'%7C'verify')%20%3D%3E%20void%2C%20signal%3F%3A%20AbortSignal%7D%7D%20%5Boptions%5D%0A%20%20%20*%20%20%20%60signal%60%20stops%20the%20work%20at%20the%20next%20step%20boundary%20(a%20running%20WebCrypto%20call%20cannot%20be%0A%20%20%20*%20%20%20interrupted)%3B%20a%20cancelled%20run%20throws%20UnlockError%20CANCELLED%20and%20never%20returns%20a%20file.%0A%20%20%20*%20%40returns%20%7BPromise%3C%7Bstatus%3A%20'not-encrypted'%2C%20entryCount%3A%20number%7D%20%7C%0A%20%20%20*%20%20%20%7Bstatus%3A%20'unlocked'%2C%20blob%3A%20Blob%2C%20summary%3A%20object%7D%3E%7D%0A%20%20%20*%2F%0A%20%20async%20function%20unlockBackup(blob%2C%20options%20%3D%20%7B%7D)%20%7B%0A%20%20%20%20const%20fetchImpl%20%3D%20options.fetchImpl%20%7C%7C%20globalThis.fetch.bind(globalThis)%3B%0A%20%20%20%20const%20subtle%20%3D%20options.subtle%20%7C%7C%20globalThis.crypto.subtle%3B%0A%20%20%20%20const%20phase%20%3D%20options.onPhase%20%7C%7C%20(()%20%3D%3E%20%7B%7D)%3B%0A%20%20%20%20const%20%7B%20signal%20%7D%20%3D%20options%3B%0A%20%20%20%20phase('scan')%3B%0A%20%20%20%20const%20entries%20%3D%20await%20scanEntries(blob%2C%20options.onScanProgress%2C%20signal)%3B%0A%20%20%20%20const%20encryptionEntries%20%3D%20entries.filter((entry)%20%3D%3E%20entry.name%20%3D%3D%3D%20ENCRYPTION_ENTRY)%3B%0A%20%20%20%20const%20databaseEntries%20%3D%20entries.filter((entry)%20%3D%3E%20entry.name%20%3D%3D%3D%20DATABASE_ENTRY)%3B%0A%20%20%20%20if%20(databaseEntries.length%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('NO_DATABASE'%2C%20'database.risudat%20%ED%95%AD%EB%AA%A9%EC%9D%B4%20%EC%97%86%EC%96%B4%EC%9A%94.%20%EB%A6%AC%EC%8A%A4%20%22%EC%A0%84%EC%B2%B4%20%EB%B0%B1%EC%97%85%22%20.bin%20%ED%8C%8C%EC%9D%BC%EC%9D%B4%20%EB%A7%9E%EB%8A%94%EC%A7%80%20%ED%99%95%EC%9D%B8%ED%95%B4%20%EC%A3%BC%EC%84%B8%EC%9A%94.')%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(databaseEntries.length%20%3E%201%20%7C%7C%20encryptionEntries.length%20%3E%201)%20%7B%0A%20%20%20%20%20%20throw%20new%20UnlockError('UNEXPECTED_LAYOUT'%2C%20'%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%82%98%20%EC%95%94%ED%98%B8%ED%99%94%20%EC%A0%95%EB%B3%B4%EA%B0%80%20%EB%91%90%20%EB%B2%88%20%EC%9D%B4%EC%83%81%20%EB%93%A4%EC%96%B4%20%EC%9E%88%EB%8A%94%20%EB%B0%B1%EC%97%85%EC%9D%B4%EC%97%90%EC%9A%94.%20%EC%95%88%EC%A0%84%EC%9D%84%20%EC%9C%84%ED%95%B4%20%EC%B2%98%EB%A6%AC%ED%95%98%EC%A7%80%20%EC%95%8A%EC%9D%84%EA%B2%8C%EC%9A%94.')%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20(encryptionEntries.length%20%3D%3D%3D%200)%20return%20%7B%20status%3A%20'not-encrypted'%2C%20entryCount%3A%20entries.length%20%7D%3B%0A%0A%20%20%20%20const%20encryption%20%3D%20encryptionEntries%5B0%5D%3B%0A%20%20%20%20const%20database%20%3D%20databaseEntries%5B0%5D%3B%0A%20%20%20%20const%20meta%20%3D%20parseEncryptionMeta(await%20readBytes(blob%2C%20encryption.dataStart%2C%20encryption.end))%3B%0A%20%20%20%20throwIfCancelled(signal)%3B%0A%20%20%20%20phase('key')%3B%0A%20%20%20%20const%20key%20%3D%20await%20fetchKey(meta.time%2C%20fetchImpl)%3B%0A%20%20%20%20throwIfCancelled(signal)%3B%0A%20%20%20%20phase('decrypt')%3B%0A%20%20%20%20const%20plain%20%3D%20await%20decryptDatabase(await%20readBytes(blob%2C%20database.dataStart%2C%20database.end)%2C%20key%2C%20subtle)%3B%0A%20%20%20%20throwIfCancelled(signal)%3B%0A%0A%20%20%20%20const%20databaseHeader%20%3D%20await%20readBytes(blob%2C%20database.start%2C%20database.dataStart)%3B%0A%20%20%20%20new%20DataView(databaseHeader.buffer).setUint32(databaseHeader.length%20-%20LENGTH_BYTES%2C%20plain.byteLength%2C%20true)%3B%0A%0A%20%20%20%20const%20parts%20%3D%20%5B%5D%3B%0A%20%20%20%20const%20expected%20%3D%20%5B%5D%3B%0A%20%20%20%20let%20runStart%20%3D%20-1%3B%0A%20%20%20%20let%20runEnd%20%3D%20-1%3B%0A%20%20%20%20const%20flush%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20%20%20if%20(runStart%20%3E%3D%200)%20parts.push(blob.slice(runStart%2C%20runEnd))%3B%0A%20%20%20%20%20%20runStart%20%3D%20-1%3B%0A%20%20%20%20%7D%3B%0A%20%20%20%20for%20(const%20entry%20of%20entries)%20%7B%0A%20%20%20%20%20%20if%20(entry%20%3D%3D%3D%20encryption)%20%7B%0A%20%20%20%20%20%20%20%20flush()%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20if%20(entry%20%3D%3D%3D%20database)%20%7B%0A%20%20%20%20%20%20%20%20flush()%3B%0A%20%20%20%20%20%20%20%20parts.push(databaseHeader%2C%20plain)%3B%0A%20%20%20%20%20%20%20%20expected.push(%7B%20name%3A%20entry.name%2C%20dataLength%3A%20plain.byteLength%20%7D)%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20if%20(runStart%20%3C%200)%20runStart%20%3D%20entry.start%3B%0A%20%20%20%20%20%20runEnd%20%3D%20entry.end%3B%0A%20%20%20%20%20%20expected.push(%7B%20name%3A%20entry.name%2C%20dataLength%3A%20entry.end%20-%20entry.dataStart%20%7D)%3B%0A%20%20%20%20%7D%0A%20%20%20%20flush()%3B%0A%0A%20%20%20%20const%20output%20%3D%20new%20Blob(parts%2C%20%7B%20type%3A%20'application%2Foctet-stream'%20%7D)%3B%0A%20%20%20%20phase('verify')%3B%0A%20%20%20%20await%20verifyOutput(output%2C%20expected)%3B%0A%20%20%20%20throwIfCancelled(signal)%3B%0A%20%20%20%20return%20%7B%0A%20%20%20%20%20%20status%3A%20'unlocked'%2C%0A%20%20%20%20%20%20blob%3A%20output%2C%0A%20%20%20%20%20%20summary%3A%20%7B%0A%20%20%20%20%20%20%20%20keyTime%3A%20meta.time%2C%0A%20%20%20%20%20%20%20%20entryCount%3A%20expected.length%2C%0A%20%20%20%20%20%20%20%20otherEntryCount%3A%20expected.length%20-%201%2C%0A%20%20%20%20%20%20%20%20databaseBytes%3A%20plain.byteLength%2C%0A%20%20%20%20%20%20%20%20databaseFormat%3A%20detectSaveFormat(plain)%2C%0A%20%20%20%20%20%20%20%20inputBytes%3A%20blob.size%2C%0A%20%20%20%20%20%20%20%20outputBytes%3A%20output.size%2C%0A%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%7D%3B%0A%20%20%7D%0A%0A%20%20return%20%7B%0A%20%20%20%20KEY_ENDPOINT%2C%0A%20%20%20%20ENCRYPTION_ENTRY%2C%0A%20%20%20%20DATABASE_ENTRY%2C%0A%20%20%20%20UnlockError%2C%0A%20%20%20%20scanEntries%2C%0A%20%20%20%20parseEncryptionMeta%2C%0A%20%20%20%20fetchKey%2C%0A%20%20%20%20decryptDatabase%2C%0A%20%20%20%20detectSaveFormat%2C%0A%20%20%20%20unlockedFileName%2C%0A%20%20%20%20unlockBackup%2C%0A%20%20%7D%3B%0A%7D)()%3B%0A%0A%0Aconst%20RBU_VERSION%20%3D%20'0.2.0'%3B%0Aconst%20RBU_PANEL_ID%20%3D%20'cupcake-risu-backup-unlock'%3B%0Aconst%20RBU_LAUNCHER_ID%20%3D%20'cupcake-risu-backup-unlock-launcher'%3B%0Aconst%20RBU_PROGRESS_INTERVAL_MS%20%3D%20200%3B%0Aconst%20RBU_PHASE_TEXT%20%3D%20%7B%0A%20%20scan%3A%20'%EB%B0%B1%EC%97%85%20%ED%8C%8C%EC%9D%BC%20%EA%B5%AC%EC%A1%B0%20%ED%99%95%EC%9D%B8%20%EC%A4%91%E2%80%A6'%2C%0A%20%20key%3A%20'%ED%82%A4%20%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C%20%EC%9D%B4%20%EB%B0%B1%EC%97%85%EC%9D%98%20%ED%82%A4%20%EB%B0%9B%EB%8A%94%20%EC%A4%91%E2%80%A6'%2C%0A%20%20decrypt%3A%20'%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%20%ED%95%B4%EB%8F%85%20%EC%A4%91%E2%80%A6'%2C%0A%20%20verify%3A%20'%EA%B2%B0%EA%B3%BC%20%ED%8C%8C%EC%9D%BC%20%EA%B2%80%EC%A6%9D%20%EC%A4%91%E2%80%A6'%2C%0A%7D%3B%0A%0Aconst%20RBU_STYLE%20%3D%20%60%0A%20%20%3Ahost%20%7B%20all%3A%20initial%3B%20%7D%0A%20%20.panel%20%7B%0A%20%20%20%20position%3A%20fixed%3B%20z-index%3A%202147483647%3B%20top%3A%2016px%3B%20right%3A%2016px%3B%0A%20%20%20%20width%3A%20min(420px%2C%20calc(100vw%20-%2032px))%3B%20max-height%3A%20calc(100vh%20-%2032px)%3B%20overflow%3A%20auto%3B%0A%20%20%20%20box-sizing%3A%20border-box%3B%20padding%3A%2016px%3B%20border-radius%3A%2014px%3B%0A%20%20%20%20background%3A%20%23fffaf3%3B%20color%3A%20%233b2a1f%3B%20border%3A%201px%20solid%20%23e8cfb4%3B%0A%20%20%20%20box-shadow%3A%200%2012px%2032px%20rgba(59%2C%2042%2C%2031%2C%200.28)%3B%0A%20%20%20%20font%3A%2014px%2F1.55%20system-ui%2C%20-apple-system%2C%20%22Apple%20SD%20Gothic%20Neo%22%2C%20%22Malgun%20Gothic%22%2C%20sans-serif%3B%0A%20%20%7D%0A%20%20.head%20%7B%20display%3A%20flex%3B%20align-items%3A%20center%3B%20justify-content%3A%20space-between%3B%20gap%3A%208px%3B%20%7D%0A%20%20h2%20%7B%20margin%3A%200%3B%20font-size%3A%2016px%3B%20%7D%0A%20%20.version%20%7B%20font-size%3A%2011px%3B%20color%3A%20%239a7b63%3B%20font-weight%3A%20normal%3B%20%7D%0A%20%20button%20%7B%0A%20%20%20%20font%3A%20inherit%3B%20cursor%3A%20pointer%3B%20border-radius%3A%2010px%3B%20border%3A%201px%20solid%20%23d9b896%3B%0A%20%20%20%20background%3A%20%23f5e3cf%3B%20color%3A%20%233b2a1f%3B%20padding%3A%208px%2014px%3B%0A%20%20%7D%0A%20%20button%3Adisabled%20%7B%20opacity%3A%200.5%3B%20cursor%3A%20default%3B%20%7D%0A%20%20button.primary%20%7B%20background%3A%20%23c8763b%3B%20border-color%3A%20%23b0652f%3B%20color%3A%20%23fff%3B%20%7D%0A%20%20.close%20%7B%20padding%3A%202px%2010px%3B%20background%3A%20transparent%3B%20border%3A%20none%3B%20font-size%3A%2018px%3B%20%7D%0A%20%20p%20%7B%20margin%3A%208px%200%3B%20%7D%0A%20%20.warn%20%7B%20color%3A%20%23a3402a%3B%20font-weight%3A%20600%3B%20%7D%0A%20%20.status%20%7B%20margin-top%3A%2010px%3B%20white-space%3A%20pre-wrap%3B%20word-break%3A%20break-word%3B%20%7D%0A%20%20.status.error%20%7B%20color%3A%20%23a3402a%3B%20%7D%0A%20%20.status.ok%20%7B%20color%3A%20%232f6b3a%3B%20%7D%0A%20%20.actions%20%7B%20display%3A%20flex%3B%20flex-wrap%3A%20wrap%3B%20gap%3A%208px%3B%20margin-top%3A%2012px%3B%20%7D%0A%20%20.note%20%7B%20font-size%3A%2012px%3B%20color%3A%20%237a5f4b%3B%20margin-top%3A%2012px%3B%20%7D%0A%60%3B%0A%0Afunction%20rbuFormatBytes(bytes)%20%7B%0A%20%20const%20units%20%3D%20%5B'B'%2C%20'KB'%2C%20'MB'%2C%20'GB'%5D%3B%0A%20%20let%20value%20%3D%20bytes%3B%0A%20%20let%20unit%20%3D%200%3B%0A%20%20while%20(value%20%3E%3D%201024%20%26%26%20unit%20%3C%20units.length%20-%201)%20%7B%0A%20%20%20%20value%20%2F%3D%201024%3B%0A%20%20%20%20unit%20%2B%3D%201%3B%0A%20%20%7D%0A%20%20return%20%60%24%7Bvalue.toFixed(unit%20%3D%3D%3D%200%20%3F%200%20%3A%201)%7D%20%24%7Bunits%5Bunit%5D%7D%60%3B%0A%7D%0A%0Afunction%20openUnlockPanel()%20%7B%0A%20%20const%20previous%20%3D%20document.getElementById(RBU_PANEL_ID)%3B%0A%20%20if%20(previous)%20%7B%0A%20%20%20%20if%20(typeof%20previous.rbuDispose%20%3D%3D%3D%20'function')%20previous.rbuDispose()%3B%0A%20%20%20%20else%20previous.remove()%3B%0A%20%20%7D%0A%20%20const%20host%20%3D%20document.createElement('div')%3B%0A%20%20host.id%20%3D%20RBU_PANEL_ID%3B%0A%20%20const%20root%20%3D%20host.attachShadow(%7B%20mode%3A%20'open'%20%7D)%3B%0A%20%20const%20onOfficialSite%20%3D%20%2F(%5E%7C%5C.)risuai%5C.xyz%24%2F.test(location.hostname)%3B%0A%20%20root.innerHTML%20%3D%20%60%0A%20%20%20%20%3Cstyle%3E%24%7BRBU_STYLE%7D%3C%2Fstyle%3E%0A%20%20%20%20%3Cdiv%20class%3D%22panel%22%20role%3D%22dialog%22%20aria-label%3D%22%EC%BB%B5%EC%BC%80%EC%9D%B5%20%EC%9B%B9%EB%A6%AC%EC%8A%A4%20%EB%B0%B1%EC%97%85%20%ED%92%80%EA%B8%B0%22%3E%0A%20%20%20%20%20%20%3Cdiv%20class%3D%22head%22%3E%0A%20%20%20%20%20%20%20%20%3Ch2%3E%F0%9F%A7%81%20%EC%BB%B5%EC%BC%80%EC%9D%B5%20%EC%9B%B9%EB%A6%AC%EC%8A%A4%20%EB%B0%B1%EC%97%85%20%ED%92%80%EA%B8%B0%20%3Cspan%20class%3D%22version%22%3Ev%24%7BRBU_VERSION%7D%3C%2Fspan%3E%3C%2Fh2%3E%0A%20%20%20%20%20%20%20%20%3Cbutton%20class%3D%22close%22%20type%3D%22button%22%20aria-label%3D%22%EB%8B%AB%EA%B8%B0%22%3E%E2%9C%95%3C%2Fbutton%3E%0A%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%20%20%3Cp%3Erisuai.xyz%EC%9D%98%20%3Cb%3E%EC%84%A4%EC%A0%95%20%E2%86%92%20%EA%B3%84%EC%A0%95%20%26amp%3B%20%ED%8C%8C%EC%9D%BC%20%E2%86%92%20%EB%A1%9C%EC%BB%AC%20%EB%B0%B1%EC%97%85%20%EC%A0%80%EC%9E%A5%3C%2Fb%3E%EC%9C%BC%EB%A1%9C%20%EB%B0%9B%EC%9D%80%20%EC%A0%84%EC%B2%B4%20%EB%B0%B1%EC%97%85(.bin)%EC%9D%84%20%EA%B3%A8%EB%9D%BC%20%EC%A3%BC%EC%84%B8%EC%9A%94.%0A%20%20%20%20%20%20%20%20%EC%95%94%ED%98%B8%ED%99%94%EB%A5%BC%20%ED%92%80%EC%96%B4%EC%84%9C%20%EB%A1%9C%EC%BB%AC%EB%A6%AC%EC%8A%A4%C2%B7%EB%85%B8%EB%93%9C%EB%A6%AC%EC%8A%A4%C2%B7%ED%8F%AC%ED%81%AC%20%EB%A6%AC%EC%8A%A4%EC%97%90%EC%84%9C%20%EA%B7%B8%EB%8C%80%EB%A1%9C%20%EB%B6%88%EB%9F%AC%EC%98%AC%20%EC%88%98%20%EC%9E%88%EB%8A%94%20%EB%B0%B1%EC%97%85%EC%9C%BC%EB%A1%9C%20%EB%B0%94%EA%BF%94%20%EB%93%9C%EB%A0%A4%EC%9A%94.%3C%2Fp%3E%0A%20%20%20%20%20%20%24%7BonOfficialSite%20%3F%20''%20%3A%20'%3Cp%20class%3D%22warn%22%3E%E2%9A%A0%20%EC%A7%80%EA%B8%88%20%ED%8E%98%EC%9D%B4%EC%A7%80%EB%8A%94%20risuai.xyz%EA%B0%80%20%EC%95%84%EB%8B%88%EC%97%90%EC%9A%94.%20%ED%82%A4%20%EC%84%9C%EB%B2%84%EA%B0%80%20%EA%B1%B0%EC%A0%88%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%9C%BC%EB%8B%88%20risuai.xyz%EC%97%90%EC%84%9C%20%EC%8B%A4%ED%96%89%ED%95%B4%20%EC%A3%BC%EC%84%B8%EC%9A%94.%3C%2Fp%3E'%7D%0A%20%20%20%20%20%20%3Cinput%20class%3D%22file%22%20type%3D%22file%22%20accept%3D%22.bin%22%20hidden%3E%0A%20%20%20%20%20%20%3Cdiv%20class%3D%22actions%22%3E%0A%20%20%20%20%20%20%20%20%3Cbutton%20class%3D%22pick%20primary%22%20type%3D%22button%22%3E%EB%B0%B1%EC%97%85%20%ED%8C%8C%EC%9D%BC%20%EA%B3%A0%EB%A5%B4%EA%B8%B0%3C%2Fbutton%3E%0A%20%20%20%20%20%20%20%20%3Cbutton%20class%3D%22save%22%20type%3D%22button%22%20hidden%3E%E2%AC%87%20%ED%92%80%EB%A6%B0%20%EB%B0%B1%EC%97%85%20%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0%3C%2Fbutton%3E%0A%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%20%20%3Cdiv%20class%3D%22status%22%20role%3D%22status%22%20aria-live%3D%22polite%22%3E%3C%2Fdiv%3E%0A%20%20%20%20%20%20%3Cp%20class%3D%22note%22%3E%EC%9B%90%EB%B3%B8%20%ED%8C%8C%EC%9D%BC%EC%9D%80%20%EB%B0%94%EB%80%8C%EC%A7%80%20%EC%95%8A%EC%95%84%EC%9A%94.%20%EB%8B%A8%2C%20%EA%B2%B0%EA%B3%BC%20%ED%8C%8C%EC%9D%BC%EC%9D%80%20%EC%9B%90%EB%B3%B8%EC%9D%84%20%EC%9D%B4%EC%96%B4%20%EB%B6%99%EC%97%AC%20%EB%A7%8C%EB%93%A4%EA%B8%B0%20%EB%95%8C%EB%AC%B8%EC%97%90%0A%20%20%20%20%20%20%20%20%EC%A0%80%EC%9E%A5%EC%9D%B4%20%EB%81%9D%EB%82%A0%20%EB%95%8C%EA%B9%8C%EC%A7%80%20%EC%9B%90%EB%B3%B8%EC%9D%84%20%EC%98%AE%EA%B8%B0%EA%B1%B0%EB%82%98%20%EC%A7%80%EC%9A%B0%EC%A7%80%20%EB%A7%88%EC%84%B8%EC%9A%94.%20%ED%8C%8C%EC%9D%BC%20%EB%82%B4%EC%9A%A9%EC%9D%80%20%EC%9D%B4%20%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%20%EB%B0%96%EC%9C%BC%EB%A1%9C%20%EB%82%98%EA%B0%80%EC%A7%80%20%EC%95%8A%EA%B3%A0%2C%0A%20%20%20%20%20%20%20%20%ED%82%A4%20%EC%84%9C%EB%B2%84%EC%97%90%EB%8A%94%20%EB%A6%AC%EC%8A%A4%EC%99%80%20%EB%98%91%EA%B0%99%EC%9D%B4%20%EB%B0%B1%EC%97%85%20%EC%8B%9C%EA%B0%81%EB%A7%8C%20%EB%B3%B4%EB%82%B4%EC%9A%94.%3C%2Fp%3E%0A%20%20%20%20%3C%2Fdiv%3E%60%3B%0A%20%20document.body.appendChild(host)%3B%0A%0A%20%20const%20input%20%3D%20root.querySelector('.file')%3B%0A%20%20const%20pick%20%3D%20root.querySelector('.pick')%3B%0A%20%20const%20save%20%3D%20root.querySelector('.save')%3B%0A%20%20const%20status%20%3D%20root.querySelector('.status')%3B%0A%20%20let%20objectUrl%20%3D%20null%3B%0A%20%20let%20outputName%20%3D%20''%3B%0A%20%20let%20running%20%3D%20null%3B%0A%20%20let%20disposed%20%3D%20false%3B%0A%0A%20%20const%20setStatus%20%3D%20(text%2C%20tone%20%3D%20'')%20%3D%3E%20%7B%0A%20%20%20%20status.textContent%20%3D%20text%3B%0A%20%20%20%20status.className%20%3D%20%60status%20%24%7Btone%7D%60%3B%0A%20%20%7D%3B%0A%20%20const%20releaseOutput%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20if%20(objectUrl)%20URL.revokeObjectURL(objectUrl)%3B%0A%20%20%20%20objectUrl%20%3D%20null%3B%0A%20%20%20%20save.hidden%20%3D%20true%3B%0A%20%20%7D%3B%0A%20%20const%20dispose%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20if%20(disposed)%20return%3B%0A%20%20%20%20disposed%20%3D%20true%3B%0A%20%20%20%20if%20(running)%20running.abort()%3B%0A%20%20%20%20releaseOutput()%3B%0A%20%20%20%20host.remove()%3B%0A%20%20%7D%3B%0A%20%20host.rbuDispose%20%3D%20dispose%3B%0A%0A%20%20root.querySelector('.close').addEventListener('click'%2C%20dispose)%3B%0A%20%20pick.addEventListener('click'%2C%20()%20%3D%3E%20input.click())%3B%0A%20%20save.addEventListener('click'%2C%20()%20%3D%3E%20%7B%0A%20%20%20%20if%20(!objectUrl)%20return%3B%0A%20%20%20%20const%20link%20%3D%20document.createElement('a')%3B%0A%20%20%20%20link.href%20%3D%20objectUrl%3B%0A%20%20%20%20link.download%20%3D%20outputName%3B%0A%20%20%20%20root.appendChild(link)%3B%0A%20%20%20%20link.click()%3B%0A%20%20%20%20link.remove()%3B%0A%20%20%7D)%3B%0A%0A%20%20input.addEventListener('change'%2C%20async%20()%20%3D%3E%20%7B%0A%20%20%20%20const%20file%20%3D%20input.files%20%26%26%20input.files%5B0%5D%3B%0A%20%20%20%20input.value%20%3D%20''%3B%0A%20%20%20%20if%20(!file)%20return%3B%0A%20%20%20%20releaseOutput()%3B%0A%20%20%20%20pick.disabled%20%3D%20true%3B%0A%20%20%20%20const%20run%20%3D%20new%20AbortController()%3B%0A%20%20%20%20running%20%3D%20run%3B%0A%20%20%20%20let%20lastProgressAt%20%3D%200%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20const%20result%20%3D%20await%20RisuBackupUnlock.unlockBackup(file%2C%20%7B%0A%20%20%20%20%20%20%20%20signal%3A%20run.signal%2C%0A%20%20%20%20%20%20%20%20onPhase%3A%20(phase)%20%3D%3E%20setStatus(%60%24%7Bfile.name%7D%20(%24%7BrbuFormatBytes(file.size)%7D)%5Cn%24%7BRBU_PHASE_TEXT%5Bphase%5D%7D%60)%2C%0A%20%20%20%20%20%20%20%20onScanProgress%3A%20(done%2C%20total%2C%20count)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20const%20now%20%3D%20Date.now()%3B%0A%20%20%20%20%20%20%20%20%20%20if%20(now%20-%20lastProgressAt%20%3C%20RBU_PROGRESS_INTERVAL_MS)%20return%3B%0A%20%20%20%20%20%20%20%20%20%20lastProgressAt%20%3D%20now%3B%0A%20%20%20%20%20%20%20%20%20%20setStatus(%60%24%7Bfile.name%7D%5Cn%24%7BRBU_PHASE_TEXT.scan%7D%20%24%7BMath.floor((done%20%2F%20total)%20*%20100)%7D%25%20(%24%7Bcount%7D%EA%B0%9C%20%ED%95%AD%EB%AA%A9)%60)%3B%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20if%20(run.signal.aborted)%20return%3B%0A%20%20%20%20%20%20if%20(result.status%20%3D%3D%3D%20'not-encrypted')%20%7B%0A%20%20%20%20%20%20%20%20setStatus(%60%EC%9D%B4%20%EB%B0%B1%EC%97%85%EC%9D%80%20%EC%95%94%ED%98%B8%ED%99%94%EB%90%98%EC%96%B4%20%EC%9E%88%EC%A7%80%20%EC%95%8A%EC%95%84%EC%9A%94%20(%24%7Bresult.entryCount%7D%EA%B0%9C%20%ED%95%AD%EB%AA%A9).%5Cn%EA%B7%B8%EB%8C%80%EB%A1%9C%20%EB%8B%A4%EB%A5%B8%20%EB%A6%AC%EC%8A%A4%EC%97%90%EC%84%9C%20%EB%B6%88%EB%9F%AC%EC%98%A4%EB%A9%B4%20%EB%8F%BC%EC%9A%94.%60%2C%20'ok')%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20const%20%7B%20summary%20%7D%20%3D%20result%3B%0A%20%20%20%20%20%20objectUrl%20%3D%20URL.createObjectURL(result.blob)%3B%0A%20%20%20%20%20%20outputName%20%3D%20RisuBackupUnlock.unlockedFileName(file.name)%3B%0A%20%20%20%20%20%20save.hidden%20%3D%20false%3B%0A%20%20%20%20%20%20setStatus(%5B%0A%20%20%20%20%20%20%20%20'%E2%9C%85%20%ED%95%B4%EB%8F%85%20%EC%99%84%EB%A3%8C!'%2C%0A%20%20%20%20%20%20%20%20%60%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%20%24%7BrbuFormatBytes(summary.databaseBytes)%7D%20%ED%95%B4%EB%8F%85%2C%20%EC%97%90%EC%85%8B%20%EB%93%B1%20%24%7Bsummary.otherEntryCount%7D%EA%B0%9C%20%ED%95%AD%EB%AA%A9%EC%9D%80%20%EA%B7%B8%EB%8C%80%EB%A1%9C%20%EC%98%AE%EA%B2%BC%EC%96%B4%EC%9A%94.%60%2C%0A%20%20%20%20%20%20%20%20%60%EA%B2%B0%EA%B3%BC%20%ED%8C%8C%EC%9D%BC%3A%20%24%7BoutputName%7D%20(%24%7BrbuFormatBytes(summary.outputBytes)%7D)%60%2C%0A%20%20%20%20%20%20%20%20'%22%ED%92%80%EB%A6%B0%20%EB%B0%B1%EC%97%85%20%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0%22%EB%A5%BC%20%EB%88%8C%EB%9F%AC%20%EC%A0%80%EC%9E%A5%ED%95%9C%20%EB%92%A4%2C%20%EC%98%AE%EA%B8%B8%20%EB%A6%AC%EC%8A%A4%EC%9D%98%20%EC%84%A4%EC%A0%95%20%E2%86%92%20%EA%B3%84%EC%A0%95%20%26%20%ED%8C%8C%EC%9D%BC%20%E2%86%92%20%22%EB%A1%9C%EC%BB%AC%20%EB%B0%B1%EC%97%85%20%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0%22%EB%A1%9C%20%EC%97%B4%EC%96%B4%20%EC%A3%BC%EC%84%B8%EC%9A%94.'%2C%0A%20%20%20%20%20%20%5D.join('%5Cn')%2C%20'ok')%3B%0A%20%20%20%20%7D%20catch%20(error)%20%7B%0A%20%20%20%20%20%20if%20(run.signal.aborted)%20return%3B%0A%20%20%20%20%20%20const%20message%20%3D%20error%20instanceof%20RisuBackupUnlock.UnlockError%0A%20%20%20%20%20%20%20%20%3F%20error.message%0A%20%20%20%20%20%20%20%20%3A%20%60%EC%98%88%EC%83%81%ED%95%98%EC%A7%80%20%EB%AA%BB%ED%95%9C%20%EC%98%A4%EB%A5%98%EA%B0%80%20%EB%82%AC%EC%96%B4%EC%9A%94%3A%20%24%7Berror%20%26%26%20error.message%20%3F%20error.message%20%3A%20error%7D%60%3B%0A%20%20%20%20%20%20setStatus(%60%E2%9D%8C%20%24%7Bmessage%7D%60%2C%20'error')%3B%0A%20%20%20%20%7D%20finally%20%7B%0A%20%20%20%20%20%20if%20(running%20%3D%3D%3D%20run)%20running%20%3D%20null%3B%0A%20%20%20%20%20%20pick.disabled%20%3D%20false%3B%0A%20%20%20%20%7D%0A%20%20%7D)%3B%0A%7D%0A%0Afunction%20mountUnlockLauncher()%20%7B%0A%20%20if%20(document.getElementById(RBU_LAUNCHER_ID))%20return%3B%0A%20%20const%20host%20%3D%20document.createElement('div')%3B%0A%20%20host.id%20%3D%20RBU_LAUNCHER_ID%3B%0A%20%20const%20root%20%3D%20host.attachShadow(%7B%20mode%3A%20'open'%20%7D)%3B%0A%20%20root.innerHTML%20%3D%20%60%0A%20%20%20%20%3Cstyle%3E%0A%20%20%20%20%20%20%3Ahost%20%7B%20all%3A%20initial%3B%20%7D%0A%20%20%20%20%20%20button%20%7B%0A%20%20%20%20%20%20%20%20position%3A%20fixed%3B%20z-index%3A%202147483646%3B%20left%3A%2012px%3B%20bottom%3A%2012px%3B%0A%20%20%20%20%20%20%20%20font%3A%2012px%2F1%20system-ui%2C%20sans-serif%3B%20padding%3A%206px%2010px%3B%20border-radius%3A%20999px%3B%20cursor%3A%20pointer%3B%0A%20%20%20%20%20%20%20%20background%3A%20%23fffaf3%3B%20color%3A%20%233b2a1f%3B%20border%3A%201px%20solid%20%23e8cfb4%3B%20opacity%3A%200.55%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20button%3Ahover%2C%20button%3Afocus-visible%20%7B%20opacity%3A%201%3B%20%7D%0A%20%20%20%20%3C%2Fstyle%3E%0A%20%20%20%20%3Cbutton%20type%3D%22button%22%20title%3D%22%EC%BB%B5%EC%BC%80%EC%9D%B5%20%EC%9B%B9%EB%A6%AC%EC%8A%A4%20%EB%B0%B1%EC%97%85%20%ED%92%80%EA%B8%B0%22%3E%F0%9F%A7%81%20%EB%B0%B1%EC%97%85%20%ED%92%80%EA%B8%B0%3C%2Fbutton%3E%60%3B%0A%20%20root.querySelector('button').addEventListener('click'%2C%20openUnlockPanel)%3B%0A%20%20document.body.appendChild(host)%3B%0A%7D%0A%0AopenUnlockPanel()%3B%0A%7D)()%3B%0A