diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c08f3dd..6618266 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,7 @@ [toolchain] channel = "1.98.1" +# CI invokes rustfmt/clippy through the rustup shim with no setup action, so +# the pinned toolchain must declare its components here — the runner images +# only preinstall them for their default toolchain. +components = ["rustfmt", "clippy"] targets = ["x86_64-pc-windows-msvc", "wasm32-unknown-unknown"] diff --git a/web/app.js b/web/app.js index e2367b1..d7d2341 100644 --- a/web/app.js +++ b/web/app.js @@ -50,6 +50,22 @@ const KIND_LABEL = { 'native-dll': 'protected native DLL', 'managed-dll': 'protected managed DLL', metadata: 'il2cpp metadata', + 'android-package': + 'Android app package — the web build cannot unpack these yet; use the senbei CLI', + 'android-so': + 'Android AArch64 library — the web build cannot unpack these yet; use the senbei CLI', +}; + +// Android targets are recognized by extension so the row can explain the +// situation instead of reporting a protected file as unrecognized: the +// Android pipeline is filesystem orchestration (senbei-io) with no wasm +// build, so these files need the CLI. The pseudo-kinds are labels only — +// they never reach the worker. +const ANDROID_KIND = { + apk: 'android-package', + apks: 'android-package', + xapk: 'android-package', + so: 'android-so', }; const COMPANION_SVG = @@ -91,8 +107,13 @@ async function stageFiles(list) { // the PE header fields); read a small slice, not the whole file. const head = new Uint8Array(await file.slice(0, 65536).arrayBuffer()); // Companions are ciphertext fragments; detect() only makes sense on the - // base module, so skip it for `._` files. - const kind = file.name.endsWith('._') ? undefined : detect(head); + // base module, so skip it for `._` files. Android targets short-circuit + // detect() as well: an ELF/zip never classifies as a protected PE, and + // the row must carry the android pseudo-kind for its status line. + const ext = file.name.slice(file.name.lastIndexOf('.') + 1).toLowerCase(); + const kind = file.name.endsWith('._') + ? undefined + : (ANDROID_KIND[ext] ?? detect(head)); const old = files.get(file.name); files.set(file.name, { file, @@ -284,7 +305,10 @@ function render() { const unpackable = [...files].some( ([name, e]) => - !name.endsWith('._') && e.kind !== undefined && e.state === 'staged', + !name.endsWith('._') && + e.kind !== undefined && + !e.kind.startsWith('android-') && + e.state === 'staged', ); unpackBtn.disabled = !unpackable; actions.hidden = files.size === 0; @@ -364,6 +388,10 @@ unpackBtn.addEventListener('click', async () => { continue; } + // Android rows are informational only (no wasm pipeline); their staged + // status line already says to use the CLI. + if (entry.kind.startsWith('android-')) continue; + entry.state = 'working'; render(); try { diff --git a/web/index.html b/web/index.html index 59e9c2c..7a7cacc 100644 --- a/web/index.html +++ b/web/index.html @@ -56,7 +56,9 @@

Senbei web

Protected .exe / .dll modules, optional ._ companions, or an il2cpp - global-metadata.dat. + global-metadata.dat. Android packages + (.apk / .apks / .xapk) and + .so libraries are recognized but need the CLI.

diff --git a/web/style.css b/web/style.css index 8a2da81..47cfab0 100644 --- a/web/style.css +++ b/web/style.css @@ -333,6 +333,26 @@ footer { } footer a { color: var(--dim); } +/* --- narrow screens --- */ + +/* On a narrow viewport the status column (flex: 1, right-aligned) is + squeezed by a long file name into a one-word-per-line vertical strip. + Stack the row instead: name + icons on the first line, the status on its + own full-width line below, left-aligned. */ +@media (max-width: 560px) { + main { padding: 1.5rem 0.9rem 2.5rem; } + + #dropzone { padding: 1.75rem 1rem; } + + .file .row { flex-wrap: wrap; } + .file .name { flex: 1; min-width: 0; } + .file .status { + order: 6; /* after the download/remove icons */ + flex-basis: 100%; + text-align: left; + } +} + /* --- animations --- */ @keyframes fadeIn {