Commit Graph
82 Commits
Author SHA1 Message Date
phantomptrandClaude Opus 5.5 1934dc8e9d fix(api): appInfoDetails checks ok through assertOk
The require-assert-ok lint rule failed CI on main: the function tested
res.ok by hand. A refusal now throws inside the existing try and still
yields null (no details), so behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:13:06 -07:00
phantomptr 5a28819d25 Release v5.39.0 v5.39.0 2026-09-28 21:45:11 -07:00
phantomptrandClaude Opus 5.5 2cad667901 feat(cheats): game-first Cheats screen with covers, details and version matching; mark PS5 messages read
Cheats: the screen lists the console's games (cover, name, installed
version) grouped as playing / ready / to download / none, using the full
repo index (fetched once) to show availability per game. The detail pane
shows appinfo details (version, size, install and last-opened dates,
content id), the downloaded cheats as switches (disabled with an
explanation while the game isn't running, since the payload refuses a
toggle then), and every repo file ranked with the installed version first
and a prominent Download button. appInfoDetails() wraps /api/ps5/appinfo;
the browser shim now accepts Tauri's camelCase titleId.

Notifications: the payload ring carries no read state, so every entry was
unread forever. Read state now lives client-side per console (watermark +
exceptions) with Mark read / Mark unread / Mark all as read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:45:07 -07:00
phantomptr 05fa3178db Release v5.38.0 v5.38.0 2026-09-28 18:13:57 -07:00
phantomptrandClaude Opus 5.5 15dd1a0c44 feat(install): each queue row carries its install's full status
The running install's phase, bytes, speed, time left and status notes now
live on its queue row instead of a page-level banner. Installers report
structured progress through InstallHooks (phase/current/total/rate) and
status notes through onStatus, which the queue stores on the item.

The queue is grouped into Now / Up next (run order) / Finished, with
summary chips, compact waiting rows, and reordering that swaps only with
the neighbour of the same install tier. The Install page drops the
duplicate stream-result card, reorders its sections, folds the FPKG
checklist, and gives link download-first its own notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:13:03 -07:00
PhantomPtrandClaude Opus 5.5 105e4aae96 chore(android): run-android reuses and foregrounds the emulator; software rendering by default (#339)
* chore(android): run-android foregrounds and reuses the emulator; faster emulator

- ensure reuses a running or still-booting emulator instead of booting a
  second; run-android brings its window to the front and reopens a headless
  one with a window; run-android-background leaves it headless.
- A stopped emulator is waited on until it has really exited (forced after
  60s), so a restart no longer mistakes it for one that is booting.
- Windowed emulators render on the host GPU instead of software, with 4 GB
  RAM and up to 6 cores.
- Android dev serves Vite on 1430/1431 (PS5UPLOAD_VITE_PORT), so it can run
  alongside desktop dev on 1420.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(android): software rendering by default; host GPU is opt-in

-gpu host froze the Android 16 image within minutes on an M1 Max (the
emulator warns its guest GL layer is unstable above API 35), with or
without Guest ANGLE. PS5UPLOAD_EMU_GPU=host opts back in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 17:31:42 -07:00
phantomptr 3190458798 Release v5.37.2 v5.37.2 2026-09-28 16:47:58 -07:00
PhantomPtrandClaude Opus 5.5 e6e8601363 Files crash: reload on a missing screen chunk; make keeps node_modules; dev FAQ/changelog (#340)
* fix(app): a screen whose code is gone reloads the page instead of crashing

Screens load on demand; when a screen's file can't be fetched the
import rejects ('Importing a module script failed') and the route showed
the crash screen. That happens to a web UI tab left open across an
update (it asks for file names the new build doesn't have) and to a dev
server whose dependency cache was rebuilt under it. Reproduced by
swapping the served build under an open tab: main showed the crash
screen for Files; now the page reloads once and opens Files from the
new build. A second failure within 30 s shows the error, so an outage
can't reload forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(make): don't reinstall client dependencies that already match the lockfile

setup-client ran npm ci on every make (run-client, run-android, emu-test,
dist-*). npm ci deletes node_modules first — Vite's dependency cache
with it — so a dev server already running lost its cache and its
screens failed ('Importing a module script failed' on Files). It now
reinstalls only when package-lock.json or Node changed since the last
install (a stamp in node_modules). Checked: a make during a running dev
server leaves it working; a stale stamp still reinstalls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(dev): the FAQ and What's New screens load on the dev server

They imported the repo-root FAQ.md and CHANGELOG.md with ?raw, and the
dev server served those (outside the client folder) as raw markdown
instead of modules. A small Vite plugin exposes them as virtual modules,
which go through the normal pipeline in dev and in builds (each is its
own on-demand chunk, locally and in the Docker web UI build). New e2e
test: both screens show their document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): 5.37.2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:47:36 -07:00
phantomptr d8e62af42d Release v5.37.1 v5.37.1 2026-09-28 14:55:47 -07:00
PhantomPtrandClaude Opus 5.5 6f5abad2b5 elfldr that recovers by itself; 75-minute publish builds; 5.37.1 notes (#338)
* feat(elfldr): vendor elfldr with a read deadline, embedded in the engine

elfldr serves one connection at a time and its reads had no deadline,
so a client that went silent without closing (a dropped link, a machine
or console asleep mid-send) left it running but deaf on :9021 — users
had to load elfldr again. Measured on the Phat (FW 5.10): the stock
build answered nothing while a silent connection stayed open; with a
15 s SO_RCVTIMEO it dropped that connection and answered the next
request at once, and a payload sent with 10 s pauses still loaded.

third_party/elfldr is upstream 02cfe91 plus ps5upload.patch (GPLv3,
credited), with the tested ELF checked in so every engine build embeds
it; `make elfldr` rebuilds it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(elfldr): the engine swaps a console's stock elfldr for the patched one

GET /api/ps5/elfldr/health tells healthy / stuck / absent from a request
elfldr answers without running anything. POST /api/ps5/elfldr/ensure
replaces the standard elfldr.elf with the embedded patched build when it
answers and isn't the one this engine installed (a reboot or autoloader
brings the stock one back), leaves any other loader on :9021 alone, and
returns once the new one answers. On the Phat: upgraded (pid 685,
answering at once), then 'current'; the helper was untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci(publish): give each client build 75 minutes

The Intel macOS build ran past the 45-minute limit for v5.37.0 (about
33 minutes for v5.35.0 and v5.36.0), which cancelled it and skipped
create-release, so v5.37.0 was never published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(engine): history tests get their own temp dirs

TmpDir named directories by pid and nanoseconds, but macOS time ticks in
microseconds: two tests starting together shared a directory and one
deleted it under the other (a_legacy_host_port_file_is_still_read read
nothing). A counter makes each name unique. The elfldr embed test reads
the embedded bytes directly instead of clearing an env var mid-run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(elfldr): the app keeps the patched elfldr in place and never sends into a stuck one

Before sending the helper, the app checks the console's loader: a stuck
one (took the connection, never answered) is waited out for 20 s — the
patched build clears in 15 — and one that stays stuck is reported
plainly ('load elfldr again') instead of a socket error. Once the helper
is up, the engine swaps the stock elfldr for the patched build: at once
after a fresh helper send (a reboot or wake brings the stock one back),
else at most every 10 minutes per console. Desktop, browser and Android
share the engine routes; strings in all 20 languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): 5.37.1 — elfldr recovers by itself; 5.37.0 folded in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(elfldr): swap it in when the helper is already up at launch

Only ensurePayloadCurrent asked for the patched elfldr, and a helper
that is already up when the app opens never goes through it — on the
Phat the stock elfldr stayed. The status poll's 'arrives at up' edge
(first sighting included) now asks too, after the same settle as the
log capture and never during a transfer.

Hardware (Phat FW 5.10, web build and Android app): stock elfldr
replaced ~10 s after opening; a silent :9021 connection held while the
helper was shut down — the helper came back through the patched
elfldr; with the stock elfldr wedged, the Android app sent nothing for
150 s (health: stuck), then redeployed and swapped elfldr once released.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(convert): the compress stage pays for reading and writing on one thread

kraken_image::compress runs the encoder on a pool, but one thread reads
the source and writes each compressed block, so the stage takes the
longer of encoding and read+write — not the slower of reading and
encoding, as the estimate assumed. With the Windows Battlefield 6 run's
speeds the model now gives 75 min for a 71-min build.

The estimate also reports the speeds it measured (rates), because the
small read/write samples can be fooled by caches: on a source read an
hour earlier the read sample hit the file cache at 8.6 GB/s and the
estimate came out at 25 min for a 54-min build. Calibrating from past
builds is the follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): build-time estimate, stated as measured

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci(docker): 25 minutes for the PR image checks

Both PR gates build the whole image (engine, and the client for the web
UI one) in about 7-10 minutes; the engine gate took 9m55s and the web UI
gate was cancelled at its 10-minute limit on PR #338.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:55:05 -07:00
PhantomPtrandClaude Opus 5.5 832e2afcd2 fix(docker): the web UI image bundles the FAQ and Changelog (#337)
* fix(docker): the web UI image bundles the FAQ and Changelog

Since the FAQ and Changelog screens bundle ../FAQ.md and ../CHANGELOG.md,
the web UI image's frontend stage failed: it built the client at /src
and .dockerignore excluded every *.md. The stage now keeps the repo
layout (/repo/client) and copies the two files in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(scripts): the audit counts a script's node:test file as used

scripts:check runs every *.test.mjs by pattern, so the test beside
stale-client-lib.mjs is named nowhere and the strict audit called it an
orphan (repo scripts failed on all three OSes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:04:23 -07:00
phantomptrandClaude Opus 5.5 0bba2cbab8 fix(scripts): make run-client stops only the engine on the app's port
kill-stale-client did `pkill -f ps5upload-engine`, which SIGTERMed
every engine on the machine — a standalone one on another port died 70
minutes into a package build. It now stops only an engine listening on
19113 (an engine whose app is gone exits by itself). The decision is a
small pure module with node:test cases, which scripts:check now runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:23:54 -07:00
phantomptr ce76e9dec5 Release v5.37.0 v5.37.0 2026-09-28 12:16:16 -07:00
phantomptrandClaude Opus 5.5 e338b29973 test(e2e): the sidebar lists every screen from the start
The browser smoke test still expected the Favorites sidebar (Home only,
"Star screens in More"); links now appear in both the sidebar and
More, so More's are looked up inside the page body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:46:34 -07:00
phantomptrandClaude Opus 5.5 92fd104830 docs(changelog): sidebar, build-time estimate, button sizes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:41:31 -07:00
phantomptrandClaude Opus 5.5 c155c08c88 fix(convert): the time estimate covers the whole build, at the speed the drives allow
A 234 GiB game estimated at 17 min took 71: the estimate timed only
compression, of blocks already in memory, one thread multiplied by the
thread count. It now times the source's sequential reads and the
output drive's writes, runs the sample on every thread at once, and
adds the package's write and verify to compression bounded by the
slower of reading and encoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:41:06 -07:00
phantomptrandClaude Opus 5.5 ca84bb2b61 feat(nav): the sidebar shows every screen; hide what you don't use
The desktop sidebar held Home, your starred screens and About, so a new
user saw nothing else — Convert and File System lived behind the More
icon. It now lists every screen in its sections (each folds away,
remembered); hover a row to hide it, and More's eye switch brings it
back. Hidden screens and folded sections are mirrored to settings.json.
Favorites are retired; nothing is hidden at first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:32:56 -07:00
phantomptrandClaude Opus 5.5 fedaba42e8 fix(css): raw buttons honour their text and font classes
An unlayered `button { font: inherit }` outranked every Tailwind
utility (they live in @layer), so each raw <button> ignored its
text-/font- classes: sidebar headers, tabs, Refresh and the Install
Package header actions rendered at body size. Tailwind's own base layer
already sets it. Checked before/after on 16 screens at desktop and
phone widths; nothing overflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:32:56 -07:00
phantomptrandClaude Opus 5.5 05d6fa3db1 docs(changelog): 5.37.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:44:55 -07:00
phantomptrandClaude Opus 5.5 071487f955 fix(engine): listing a folder that isn't there is a 404, not a 502
Install Package lists every drive's package folder; a drive without one
logged a gateway error. The body is unchanged, which is what the app
reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:31:50 -07:00
phantomptrandClaude Opus 5.5 8b2758290f fix(icons): a suffixed save folder shows its game's icon and name
Saves listed a console folder named PPSA17221.bak and asked the engine
for the icon of "PPSA17221.bak" (400, then a retry). GameIcon now
looks up the leading title id and skips names that aren't one; the
Saves header names the game too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:31:50 -07:00
phantomptrandClaude Opus 5.5 31dc0d9182 fix(engine): a recovery line counts every failure of the outage
It reported only the unlogged repeats, so one logged 502 followed by a
200 read "recovered after 0 failure(s)".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:14:18 -07:00
phantomptrandClaude Opus 5.5 cf7ddbccab fix(engine): exit when the desktop shell dies, even with nowhere to print
The parent-watch thread's first eprintln! panicked on the closed stderr
pipe a dead shell leaves behind, so the engine never shut down and held
its port (reproduced with the v5.36.0 app: killed shell, engine alive
25 s later). Shutdown is now triggered before any message, messages use
a write that cannot panic, and the notify keeps a permit so a parent
that dies during start-up isn't missed (it fell to the 10 s hard exit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:12:53 -07:00
phantomptrandClaude Opus 5.5 d75a5adf4b fix(picker): with folders ticked, Add is the one action
"Use this folder" resolved only the open folder and dropped the ticks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:06:48 -07:00
phantomptrandClaude Opus 5.5 d0bbf5da74 fix(remote): a RAR volume already in the folder is never overwritten
The copy itself got a 409 when its name was taken, but a set's other
volumes were written over, and cleanup would then delete them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:06:24 -07:00
phantomptrandClaude Opus 5.5 f9fbfd5734 chore: untrack a client/node_modules symlink committed by mistake
59440903 committed a worktree's node_modules symlink, pointing at an
absolute path on the author's machine. `node_modules/` only ignores
directories; `node_modules` covers a symlink too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:59:04 -07:00
phantomptrandClaude Opus 5.5 ba68a1910a fix(picker): let the multi-pick hint wrap on a phone
Seen on the Android emulator: "add them toget…" was cut off beside
the Add button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:58:38 -07:00
phantomptrandClaude Opus 5.5 93e02d8969 feat(convert): Game language — declare one PlayGo language in the package
Battlefield 6's eboot imports scePlayGoGetLanguageMask: it asks the
install which language it has, and every converted package answered
"all 31, en-US first". Convert's Options card now takes one language;
the package's playgo-scenario.json then lists it as the default and
only language (the shape Sony's own scenarios take). Carried through a
single run and the Convert queue; reset per game.

Checked on the Phat (FW 5.10): a fr-FR Minecraft build installs and
plays like the all-language control.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:49:40 -07:00
phantomptrandClaude Opus 5.5 8d128ad520 feat(picker): tick several files or folders in the in-app browser
The browser build and Android browse in-app, which picked one path at a
time. pickLocalPaths() opens it with checkboxes kept across folders and
an Add N button; Upload's Choose file/folder feed every pick to the
batch list, pickPaths() uses it off-desktop, and Convert's queue gains
Pick several….

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:11:50 -07:00
phantomptrandClaude Opus 5.5 9b21f5a2e8 feat(remote): a multi-part .rar on a saved server is copied with every volume
UnRAR finds the rest of a set beside its first volume, so copying only
game.part1.rar (or game.rar of an .r00/.r01 set) could never extract. The
server copy now brings the set's other volumes into the same folder and
cleanup removes them too - for Upload, a batch item's turn, and Convert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:01:54 -07:00
phantomptrandClaude Opus 5.5 c1b03a9910 test(engine): hand-run throughput test for the console reader Convert uses
Measured 2026-09-28 on the Phat over this Mac's Wi-Fi: 7.8 MB/s for 2 GiB,
the same as curl on the same file (7.9 MB/s) — the reader adds no overhead;
the link is the limit (117 MB/s wired in the spike).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:56:39 -07:00
phantomptrandClaude Opus 5.5 79d49c08a8 fix(layout): buttons never shrink below their label; the Games grid fills by room, not window width
Buttons in tight rows ellipsized to "Ref…" / "Connect a NAS…"; they keep their
label now (rows wrap). The game cover grid chose columns by window width, so
beside the sidebar at 768 px it squeezed four 99 px cards; it now fits as
many ≥8 rem columns as the space holds (two on a phone, more on wide windows).
Scanned at 390/768/1280: no overflow, no cut labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:14:48 -07:00
phantomptrandClaude Opus 5.5 07c7f1de00 fix(mobile): header actions and the Upload picker row wrap instead of squeezing labels; titles wrap on phones
On a phone, header action rows shrank their buttons to "Ref…" / "New f…" /
"From this d…" (File System, Install Package) and Upload's picker row
crushed three buttons into fragments. Rows wrap now; a long page title wraps
to two lines on a phone instead of truncating.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:02:39 -07:00
phantomptrandClaude Opus 5.5 c40d70ce7c fix(button): an icon passed as a child stays on the label's line
The label span is a block (for truncation) and a block-level svg sat above
the text: Refresh / Add a server / Add point / Reset play time… rendered as an
icon over a word on every screen that passes the icon as a child.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:00:27 -07:00
phantomptrandClaude Opus 5.5 0f296fdfcb style(icons): one icon per screen, the same in the menu and the page header
Audit log, Connection, Game activity and Processes showed a different icon in
their header than in the menu; Cheats shared the Games gamepad, Edit game image
shared the Volumes drive, Processes shared the Console CPU. Now: audit log
shield-check, connection cable, game activity clock, processes layers, cheats
wand, edit game image file-pen, games gamepad.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:58:19 -07:00
phantomptrandClaude Opus 5.5 ea5b7700d7 style: every screen in the same page frame; Convert uses two columns on wide windows
Screens sat in a mix of centered 3xl/4xl/5xl columns with 16 or 24 px padding,
leaving half a wide window empty on some and not others. All screen roots (and
the tabbed shell) now use the shared app-page frame: one max width, 24 px
padding, 16 px on phones. Convert puts the game and its options beside
building and the queue at xl widths. The queue's Start button only shows when
something is waiting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:56:34 -07:00
phantomptrandClaude Opus 5.5 fb1d384154 fix(web): FAQ and Changelog load in the browser build; Keep awake and Capture don't offer what a web page can't do
The browser build showed developer errors ("'faq_load' requires the Tauri desktop
client… Guard the call site…") on FAQ and in Settings → Keep awake, and its
Capture button could only fail. FAQ/CHANGELOG are bundled into the web app as
lazy chunks; Keep awake reports unsupported there; Capture is desktop-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:48:11 -07:00
phantomptrandClaude Opus 5.5 bb75c14224 fix(payload): uninstalling a folder we registered removes its /user/appmeta copy
Registration copies sce_sys into /user/appmeta/<id>; Sony's uninstall doesn't
know about that copy, and the app lists every appmeta folder as installed, so
an uninstalled folder game stayed in Games. Once Sony confirms the uninstall
of our own registration, the flat folder is removed (files only, no recursion).
Verified on the Phat: register → unregister → appmeta gone, not listed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:45:40 -07:00
phantomptrandClaude Opus 5.5 7872b0fc4f feat(convert): a queue - several games converted one after another, installs handed to the console queue
Add this game / Add games from a folder… queue builds on this computer, one at
a time, each shown by the Convert screen's own stage view. After converting:
keep the package, or Stream & install / Upload & install - the package is
handed to the console queue and the next build starts while it installs
(a console dump still swaps inside its build). Optional delete after a
verified install. Reorder, remove, stop after the current build; a build
interrupted by a restart is built again, and the engine now clears an
interrupted build's stale .pkg.partial instead of refusing the rebuild.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:28:22 -07:00
phantomptrandClaude Opus 5.5 9eab028449 feat(upload): several sources at once - review list, multi-select, drops, Add games from a folder
Two or more sources (multi-select pickers, a multi drop, or Add games from a
folder…, which lists a folder's children with games checked) switch Upload to
a review list: each row is inspected in the background (four at a time) and
shows its kind, size and where it lands; an encrypted .rar asks for its
password inline. Before adding: rows landing in the same place block, rows
already queued are skipped, and space per drive blocks only when it clearly
won't fit (warns when it can't tell). Shared destination, resume/overwrite and
only the options the list needs; Add N to queue / Upload N now. One source
still behaves exactly as before; a package dropped on Upload is uploaded and
installed (it no longer jumps to Install Package).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:13:20 -07:00
phantomptrandClaude Opus 5.5 4f28b1d52e feat(upload): shared queue-item builder; a server archive is copied when its turn comes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:01:57 -07:00
phantomptrandClaude Opus 5.5 d0970f0de3 fix(pkg): name the container's own entries (digests, entry_keys, image_key, metas…)
The viewer's Files tab listed them as bare ids (#0001, #0010, …).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:57:43 -07:00
phantomptrandClaude Opus 5.5 cd57a668a7 feat(viewer): open it from anywhere - drops, Convert, the queue, File System, server browser
One app-wide viewer. A package, image or folder dropped on a screen without
its own drop zone opens it with Install… / Convert… (a drop no longer jumps
straight to Install Package); Convert views its game and its package; queue
rows view their package or source; File System and Connections → Browse get
View on packages, images and game folders (read on the console / server).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:54:38 -07:00
phantomptrandClaude Opus 5.5 104b08f9ac test(installer-client): the fake daemons read the whole request line
The client writes a request and its newline separately; a fake that answered
after the first segment closed over unread bytes, and the reset beat the
reply to the client (hello_over_socket flaked under load).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:54:37 -07:00
phantomptrandClaude Opus 5.5 21aee22efa feat(viewer): Files, Images and Related tabs; packages and games on a server or the PS5
The viewer reads remote:// and ps5:// sources through the engine's opener, and
.pkg parsing now works over any reader, so a staged package opens straight
from the console: every Install Package row gets View details, reading the
local original when there is one. New GET /api/game/inspect/files lists a
package's entries or a tree's files (a windowed list for 200k-file dumps);
Images shows and saves every artwork; Related groups the title's other
library and queued packages base → update → DLC, each viewable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:45:36 -07:00
phantomptrandClaude Opus 5.5 bd14603614 feat(convert): Convert & replace swaps a console dump for its package
A dump on the PS5 and its package share a title id, and ShadowMountPlus
re-registers a dump over an install, so a console source is never installed
beside its dump. The swap journals each step to /data/ps5upload/swap first,
parks the dump on its own volume (outside every scan root), waits for SMP to
let go, removes the leftover registration, installs through the queue, and
moves the dump back if anything fails. A running game stops it untouched.
The result offers Delete the old dump / Keep it parked (dropping its
manual.lst line); an interrupted swap is listed on Convert to put back or
finish.

Verified on the Phat (5.10) with a disposable fixture title: a failed install
rolled back (dump restored, SMP re-registered); a good one parked the dump and
left a real app.pkg install; Delete the old dump removed it and the journal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 01:36:17 -07:00
phantomptrandClaude Opus 5.5 3c558ba0ed feat(convert): read a game in place on a saved server or the PS5
A server folder or image is built straight off the server through the
engine's server file system (8 MiB read-ahead, retries) - no copy stage;
only a server archive is still copied, then unpacked. 'On the PS5' browses
the console and reads a dump through ftpsrv (:2121) as ps5://host/path;
installed games under /user/app are refused as encrypted. source::open
resolves remote:// and ps5:// through an opener the engine registers, so
build, estimate, inspect and the viewer all read them. Compression still
copies a server image first (it reads local disk).

Verified on the Phat (5.10): inspect + build of an image on the console
over ps5://, same package size as the local build; /user/app refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 01:08:22 -07:00
phantomptrandClaude Opus 5.5 ebcc541173 feat(convert): build from a .zip, .7z or .rar archive
An archive is unpacked into .ps5upload-extract-<job> in the output folder as
its own Convert stage (progress, Cancel), the game found inside - a folder
however wrapped, or a single .exfat/.ffpkg/.ffpfsc image - is built, and the
unpack is removed when the run ends. Space for the files plus the package is
checked first; unpack folders left by an earlier run are swept. RAR takes a
password (never stored); a wrong one on a content-encrypted RAR now says so
instead of 'File CRC error', for uploads too. zip/7z are built without their
encryption features, so a protected one is refused with that reason. A server
archive is copied, then unpacked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:53:45 -07:00
phantomptrandClaude Opus 5.5 da44b7193f feat(fpkg): convert and inspect a .ffpfsc through the image inside it
PfscReader is Read + Seek over a .ffpfsc's inner file, decoding 64 KiB zlib
blocks on demand with a small cache; the single-file layout parser is shared
with verify and bounds-checked for images users bring. exFAT and UFS2 sources
now open from any reader. A package built from a .ffpfsc is byte-identical
to the one built from its image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:38:32 -07:00
phantomptrandClaude Opus 5.5 67335e2c50 fix(settings): hide the Beta features switch while nothing is in beta; README drops Convert's beta note
Convert graduated; the switch revealed nothing and the README still told users
to enable Convert there. Stream install is Stream & install everywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:30:48 -07:00