Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -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"]
34 changes: 31 additions & 3 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ <h1>Senbei <span class="tag">web</span></h1>
<p class="hint">
Protected <code>.exe</code> / <code>.dll</code> modules, optional
<code>._</code> companions, or an il2cpp
<code>global-metadata.dat</code>.
<code>global-metadata.dat</code>. Android packages
(<code>.apk</code> / <code>.apks</code> / <code>.xapk</code>) and
<code>.so</code> libraries are recognized but need the CLI.
</p>
<input type="file" id="picker" multiple hidden>
</div>
Expand Down
20 changes: 20 additions & 0 deletions web/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading