Release/beta#25
Conversation
…ary + ownership (Qt/iOS/Android) Reworks PlayStation Plus Cloud Play end to end so it works in English-only regions (e.g. Hungary), streams owned PS4 and PS5 titles correctly, shows cross-gen editions, and classifies ownership (full game vs trial vs add-on). Applied across the Qt (C++/QML), iOS (Swift) and Android (Kotlin) clients. Catalog / region - Store-locale fallback chain (lang-COUNTRY -> en-COUNTRY -> en-US) so the imagic catalog loads in every region; the validated locale persists. - Accept PS4 (not just PS5) cloud titles in the merge; capture streamingSupported=false subscription titles into the library-stream supplement from every subscription list (these stream via the legacy Kamaji/kratos path even though they are absent from public cloud browse). - Scope the views: Game Catalog = PS Plus subscription lists (plusCatalog tag); Library "all" = full streamable universe + owned; Library "owned" = owned. - Catalog falls back to the imagic catalog when the legacy PS Now /user/stores browse 404s (it does in many regions). - Dedupe per game per platform so cross-gen PS4/PS5 editions both appear. - Broaden the owned-games filter; match owned entitlements by conceptId in addition to product id / stable key. Owned-title streaming (entitlement resolution) - PS5 streams the owned PRODUCT id, not the entitlement id: a cross-gen upgrade (PS4 purchase + free PS5 copy) carries a stale original-SKU entitlement id that Gaikai's cloud catalog has no game for (-> noGameForEntitlementId); product_id is the current streamable SKU. (Fixed Alan Wake Remastered, Death Stranding DC.) - When several SKUs collapse to one edition (base game + bonus/upgrade/avatars), keep the canonical full-game entitlement -- the one whose entitlement id EQUALS its product_id. Package/feature flags don't disambiguate (Death Stranding DC's "Bonus Content" is also PSGD + feature_type 3), so the id==product_id signal is what selects the real game over a DLC product Gaikai can't stream. - PS4 streams the catalog's streamable variant (e.g. God of War's "...N" SKU whose Kamaji container holds the PS-Now license_type=4 SKU), not the owned download SKU; derive the streaming platform from the owned product (cross-gen catalog entries list the other generation). PS4 (CUSA) -> Kamaji/psnow; PS5 (PPSA) -> direct Gaikai (cronos). Datacenter ping no longer hard-fails on a measurement error (Qt). Ownership classification (feature_type) - feature_type 3/5 = full game owned, 1 = trial / free-to-play, 0 = add-on/DLC. - Drop feature_type==0 extras from the owned set (DLC/themes/avatars are never a base game). Keep trials and free-to-play; a trial is kept as its own card so the full version still shows separately as "Add Game" (a trial does not collapse into the full-game catalog entry). Cross-gen owned-library split - Key owned-edition identity on conceptId + PLATFORM (matching the catalog tab) in both the owned cross-reference dedupe and the library merge, so a title owned on PS4 and PS5 (e.g. Days Gone + Days Gone Remastered) shows two separate, independently-streamable cards. Platform labels - Derive PS4/PS5 from the title id (CUSA/PPSA) instead of the hard-coded platform="ps5" -- Android at display time (CloudGameAdapter), iOS in the parser/deserializer (self-correcting the cache); Qt already did. Catalog ownership UX - Cross-reference the catalog against owned entitlements (mark-only): owned -> "Stream", non-owned modern cloud titles -> "Add Game"; OWNED / NOT OWNED badge. Build - Remove the committed machine-specific org.gradle.java.home (an absolute Windows path that broke every non-Windows / CI Gradle build); document selecting the JDK 21 daemon per-machine via JAVA_HOME / ~/.gradle / the IDE Gradle JDK setting. Verified - Qt/macOS (Hungary / PS Plus Premium): catalog loads region-wide; owned PS4 (God of War) and PS5 (Alan Wake Remastered, Death Stranding DC) stream from Library and Catalog; cross-gen Days Gone shows + streams both editions; a trial (Cyberpunk) shows its own Stream card plus an "Add Game" card for the full version; adding the PS5 Remaster lets Spider-Man stream; labels and OWNED/NOT OWNED badges correct. - iOS: swiftc -parse clean. Android: compiles (compileDebugKotlin). Mobile not device-re-tested for the latest streaming/ownership pass. Upstream reconciliation (PR #15) - Sits on top of the merged "PS5 cloud ownership matching" PR (#15) and incorporates its useful additions: bundle-sibling expansion (a bundle entitlement, e.g. RE7 Gold, expands to its component games via componentIdsByProductId) and stable-key matching on the entitlement id. These are grafted onto our cross-reference as additive fallbacks (they only fire when our direct cascade finds no match), keeping our dedupe (conceptId+platform + canonical-entitlement rank), feature_type filtering and field convention where the two approaches differed. Known limitations - Some PS Plus titles are download-only (no cloud-streaming SKU, e.g. Far Cry 5, original Spider-Man PS4): indistinguishable in the catalog from streamable PS4 titles, so they appear but fail at sessions/start with noGameForEntitlementId. - PS5 catalog-only titles must be added to the library externally (PS App) first. - PS3 titles absent from the modern imagic API; PS5 HEVC video-decode freeze is a separate pipeline issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PS Plus Premium streams ~250-330 PS3 Classics that never appear in the
imagic/gameslist catalog the rest of cloud play uses. Source them from the
public pcnow ("Apollo") container API and stream them via the existing
Gaikai konan path.
- Catalog: new fetchPs3Catalog walks the public Apollo PS3 container (no auth),
paginated; surfaced in the Game Catalog and Library "all" views (not "owned").
PS3 cards always show "Stream Game".
- Region-generic: pcnow has two Classics id families -- Americas/SCEA
(store MSF192018, UP/NPUA/BLUS ids, child APOLLOPS3GAMES) and PAL/SCEE
(store MSF192014, EP/NPEA/NPEB/BLES ids, child APOLLOPS3). The account
region group selects the store; everything outside the Americas -> PAL.
- Streaming: for legacy (non-CUSA/PPSA) ids, resolve product->entitlement in
the region-group store, and skip the regional checkout/acquire on a 404
(Premium auto-authorizes at Gaikai; the checkout is unavailable in regions
without a pcnow storefront, e.g. Hungary).
- PS4 (CUSA) / PS5 (PPSA) paths unchanged.
Ported across macOS (Qt), iOS (Swift), Android (Kotlin). macOS + Android
verified streaming on a real PS Plus Premium account; iOS compile-verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Starting a cloud stream locks the activity to landscape via
requestedOrientation. On large tablets, portrait<->landscape also changes
screenLayout/smallestScreenSize, which MainActivity didn't declare in
configChanges -- so Android recreated the activity, detached
CloudPlayFragment, and the in-flight startCloudStreaming coroutine then
crashed on requireActivity() ("Fragment not attached to an activity").
Declare screenLayout|smallestScreenSize so MainActivity handles the rotation
itself instead of being recreated, keeping the fragment attached.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PS Plus disc-upgrade entitlements (feature_type 5, e.g. Horizon Forbidden
West EP9000-PPSA01521) are the SKU the imagic browse catalog binds the
concept to, but Gaikai refuses to cloud-stream them
("disc-upgrade-unsupported"). The owned streamable edition (e.g. the
Complete Edition PPSA17903) is a different title id that is absent from the
catalog and -- like every commerce-API entitlement -- carries no conceptId,
so the owned cross-reference never matches it and only the unstreamable
disc-upgrade SKU survives the dedupe.
Add a disc-upgrade rescue to the owned cross-reference on all platforms
(Qt/iOS/Android): when a concept's surviving owned SKU is a disc upgrade,
adopt the product id of a same-name full-game (feature_type 3) owned SKU so
the card streams the edition Gaikai accepts. Since the only in-data bridge
is the title name, it is guarded to stay safe: same platform only (a PS5
disc upgrade can never resolve to a PS4 CUSA SKU), prefer the canonical base
game (product_id == entitlement id), and bail on genuine ambiguity rather
than guess.
Verified on macOS: Horizon Forbidden West now streams PPSA17903 instead of
the rejected PPSA01521.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cards The "all" library view merges owned entitlements into the browse catalog. For PS5 (PPSA) the override of the catalog card's product id was guarded (if (!existing.product_id)), so it applied only when the catalog card had no id. When the browse row carries a product id -- e.g. Horizon Forbidden West's concept is bound to the disc-upgrade SKU PPSA01521 -- the guard kept that unstreamable id even though the cross-reference had rescued the owned full game (PPSA17903), so Gaikai rejected it with "disc-upgrade-unsupported". Override unconditionally for PS5, matching the iOS and Android merges (which always copy the owned storeProductId). The owned PS5 product IS the streamable entitlement, so it must win over the catalog's fixed per-concept SKU. PS4 (CUSA) is unaffected (the whole block is PS5-only). Fixes Horizon Forbidden West failing to stream on the Steam Deck / Linux build while macOS and Android worked -- the guard only happened to pass on those when the catalog cache had a null product id (data-dependent). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ps5CloudPlatformToken() takes a GAME OBJECT (it reads game.productId / game.id),
but the "all"-view merge passed it the product-id STRING. A string has no
.productId/.id/.device, so it always returned "", the `=== "ps5"` test was never
true, and the block that copies the owned product id onto the matched catalog
card never executed -- for any game.
That left the catalog card's own (often unstreamable) SKU in place. For Horizon
Forbidden West the catalog binds the concept to the disc-upgrade SKU PPSA01521,
so the "all" filter streamed that and Gaikai rejected it
("disc-upgrade-unsupported"), while the "owned" filter worked (it uses the
cross-reference output directly, which already carries the rescued PPSA17903).
Pass the game object so the platform check resolves to "ps5" and the owned
product id wins. Pre-existing bug -- the earlier guard/un-guard edits were both
inside this dead block, which is why neither changed anything. iOS/Android were
unaffected (their merges copy storeProductId with no platform-token check).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports nyakaspeter's PR #16 (fix/cloud-play-catalog-and-ps4-streaming) on top of the post-#17 master (Android streaming/input/UI refactor ad331dd). Conflicts resolved: - CloudGameAdapter.kt: keep our recycled-card focus-stroke reset, adopt the PR's PPSA/CUSA title-id platform-badge derivation. - gradle.properties: take the PR's removal of the machine-specific org.gradle.java.home Windows path. Co-authored-by: Cursor <cursoragent@cursor.com>
…o re-login The PS Plus cloud catalog cross-references the user's owned entitlements to mark games "Stream" (owned) vs "Add Game" (not owned). When that resolution failed (expired npsso / OAuth login_required, or a network error) the failure was silently swallowed: every game was marked "Not Owned" AND the result was cached for 24h, so all owned games showed "Add Game" until the cache expired, with no indication to re-login. - crossReferenceOwnership now propagates the failure instead of swallowing it. - fetchPs5CloudCatalog / fetchPlusCatalog / fetchPsnowCatalog skip caching the ownership-merged result on failure (the raw v3 catalog + PS3 classics still cache), so the next open retries once the session is valid again. - Surface a clear warning: session-expired vs network, distinguished by the OAuth/entitlements error. Reset the warning per fetch. Also fix a stray indentation glitch in CloudPlayFragment.onGameClicked from the ported PR. Verified on-device (Pixel 6, US PS Plus, currently-expired token): the all- "Not Owned" pscloud_catalog.json is no longer written on ownership failure. Co-authored-by: Cursor <cursoragent@cursor.com>
…fixes and Cloud Play UI redesign. Fix cross-buy PS5 streaming by deriving serviceType from entitlement platform_id and using platform-disciplined merge so PS4 licenses cannot corrupt PS5 cards. Add unified catalog assembly with streamability gate and Apollo region fallback on mobile, align Qt/Android/iOS Cloud Play headers and filters, and add iOS CachedAsyncImage for reliable cover art loading. Co-authored-by: Cursor <cursoragent@cursor.com>
…s across Qt, iOS, Android Port the Qt cloud-catalog merge fixes to iOS and Android for parity: - Deterministic owned-entitlement tiebreak (stream rank -> GS package -> sku_id -> product_id -> id) so the catalog is stable regardless of the PSN entitlements response order. - Suppress redundant trial (feature_type 1) cards when the same product is also fully owned (F2P cross-buy wrappers, e.g. Trackmania). - Process pscloud (PS5) owned claims before psnow (PS3/PS4) so a cross-buy PPSA wrapper is dropped cleanly instead of orphaning the browse row. - Drop psnow entitlements that land on a PS5-class card (cross-buy wrapper) rather than appending a bogus duplicate / ghost card. Adds sku_id to the parsed entitlement on iOS/Android for the deterministic tiebreak. Qt also restores serviceType stamping and the QML pscloud routing guard for PS1-classic store wrappers (Worms World Party). Co-authored-by: Cursor <cursoragent@cursor.com>
…parity across Qt, iOS, Android Expired-NPSSO handling (Qt, iOS, Android): - On native PS Now auth failure, surface the "log in again" warning and STOP: do not fall back to the public APOLLOROOT walk (that path is only for region-unsupported accounts) and do not cache the degraded catalog. - iOS/Android no longer let the PS5 (imagic) fetch clobber the session warning; Qt's warning string is aligned to match mobile. Catalog parity (Qt, iOS, Android now emit identical card sets): - Decide PS5-platform browse membership from the authoritative imagic `device` array (or PPSA id), not the CUSA/PPSA productId token, so cross-gen titles (PS4 SKU with PS5 device support) are no longer dropped on mobile. - Skip imagic browse rows already present in the Apollo (PS Now) catalog so a title in both lists is not emitted twice (Crow Country / Grandia / HUMANITY). - categoryFor now resolves the catalog category from serviceType the same way Qt does (psnow and pscloud both short-circuit), independent of the routing-only streamServiceType isOwned gate, so non-owned pscloud PS4 rows are purchaseable. Verified: Qt, iOS, and Android unified caches converge to identical totals and per-card category/serviceType/ownership (4930 / 97 owned / 780 streamable / 4053 purchaseable), zero duplicates, identical productId sets. Co-authored-by: Cursor <cursoragent@cursor.com>
…ame-language picker Move the entire cloud catalog fetch/merge/cache/cross-reference pipeline into libchiaki (new cloudcatalog_* sources + curl_http) so Qt, iOS, and Android consume one display-and-stream-ready contract with zero client-side catalog logic. Region detection and Gaikai bare-language conversion also live in the lib and are exposed to all three UIs (Q_INVOKABLE, Obj-C bridge, JNI). Add a "Cloud Settings" game-language picker (Auto + supported locales, each shown with its locale code) above Game Library/Catalog on iOS and Android. The manual language override is stored separately from the auto-detected catalog/region locale so it is never clobbered by settledLocale/Kamaji writes; streaming prefers the override and falls back to the catalog locale. "Auto" clears the override. Datacenter auto-matching removed; a short region/datacenter caveat is surfaced compactly (popup on iOS, dialog header on Android, caption on Qt). Remove now-dead per-platform catalog/ownership code superseded by the lib. Co-authored-by: Cursor <cursoragent@cursor.com>
…card polish libchiaki: - Apply a cloud-only RTT safety offset (-20ms, clamped to 1ms min) at the single senkusha measurement point so the latency gate, /datacenters/select, /allocate, and the settings display all see the adjusted value. Scoped via service_type so Remote Play is untouched (new CHIAKI_CLOUD_RTT_* constants in common.h). - Add a contributor-guidance block to cloudcatalog_unified.c documenting the shared-merge ground rules (all logic in libchiaki, imagic=owned PS5 / Apollo=PS3-PS4, graceful Apollo region fallback, no title-ID regex matching). Cross-platform (Qt, Android, iOS): - Separate the manual stream-language setting from the auto catalog locale. - Preserve previously-measured datacenter ping RTTs instead of clobbering the picker with a no-RTT list before pinging. Android: - Shorten the inline cloud-language note and show the full caveat in a popup only when a specific language is chosen. iOS: - Redesign the Cloud Play card platform badge as a neon-outlined corner tag (own bottom-right layer) so it no longer competes with the title for space. Tests: - Add cloudcatalog_merge unit test + a desktop fetch harness. Co-authored-by: Cursor <cursoragent@cursor.com>
Add an opt-in on-screen streaming stats overlay (bitrate, packet loss, dropped frames/sec, FPS, live RTT, resolution) across Qt, Android, and iOS. All values are computed in libchiaki and read via a single getter (Android JNI sessionGetMetrics, iOS ChiakiSessionBridge metrics helper), so clients only render — no per-frame instrumentation. FPS/RTT use an EMA in libchiaki to keep the readout stable. The overlay is a single top-centered row toggled from the in-stream menu, with a light translucent background matching across platforms. Android Cloud Play polish: per-platform neon badge (ps5 blue / ps4 indigo / ps3 purple) with glow to match iOS; lighter, correctly oriented bottom gradient behind the title; and a landscape card fix so a single tap launches a game (removed stray focusableInTouchMode that forced a focus-then-activate two-tap; TV still works via the programmatic enableFocusableInTouchModeForTv path). Co-authored-by: Cursor <cursoragent@cursor.com>
Add a "build_apk" checkbox to the manual deploy-android workflow that produces an installable APK artifact and skips the Google Play publish, so testers can sideload a build before it ships. Signed release APK when signing secrets are present, debug APK otherwise. Bump CHIAKI_VERSION to 2.10.22 (single source of truth in CMakeLists.txt; propagates to Android versionName/versionCode, iOS, and macOS/Linux). Co-authored-by: Cursor <cursoragent@cursor.com>
…ed-frame count - deploy-android.yml: pin the APK-only build to arm64-v8a so the sideload artifact is always an installable arm64 split (was picking a stray ABI via sort|tail). Add explicit !inputs.build_apk guards to the Play-publish steps so an APK-only run can never upload to Google Play. - videoreceiver: increment cumulative_frames_lost at each loss site so the stats overlay's running total stays accurate even on loss paths that return before the next successful flush. Overlay-counter accuracy only; no change to decode/FEC/flush behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
The stats overlay being at most one frame behind is immaterial, and the change touched streaming-adjacent code for no user-visible benefit. Keep only the safe CI fixes (APK ABI pin + Play-publish guards) from the prior commit. Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the shared cloud catalog cache whenever the active account or catalog locale changes so one account never sees another's owned games: - Qt/Android/iOS: invalidate the libchiaki-owned catalog cache on NPSSO login/logout/re-entry and on cloud-language change. - Qt: add Settings::NpssoTokenChanged and CloudCatalogBackend::cacheInvalidated; the cloud view re-fetches on invalidation so the visible grid never lingers on the previous account's games. - Qt: fix profile-switch ordering/staleness. CloudCatalogBackend now gets setSettings() and is rebound to the new profile before invalidateCache() runs, so the reload reads the new account's NPSSO instead of the old (deleted) Settings (also removes a latent use-after-free on the next fetch). Co-authored-by: Cursor <cursoragent@cursor.com>
Make log viewing crystal-clear and never-hang across platforms: - macOS (scripts/build-macos.sh) and iOS (ios/build.sh): every launch path captures to one fixed log file and returns immediately; `logs` does a bounded one-shot dump. Detach background watchdog subshells from the terminal so a piped `... | tail`/`grep` no longer blocks on the auto-stop window. iOS drops the obsolete PYLUX_DEV_NO_STREAM toggle and dead foreground-stream helper. - Promote the local Android build script out of tmp/ into android/build-local.sh (mirrors deploy-android.yml; --logs-dump for one-shot, non-hanging logcat). Co-authored-by: Cursor <cursoragent@cursor.com>
…e table Co-authored-by: Cursor <cursoragent@cursor.com>
…ses resolvedStoreCountry Co-authored-by: Cursor <cursoragent@cursor.com>
Phase 2 hardcoded "en" when resolvedStoreCountry was set, regressing non-English native accounts (JP/ja, DE/de, etc.). Keep server-authoritative country from fallbackRegion; take language from cloud_store_locale parse. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Qt SetNpssoToken and Android saveNpssoToken dropped the 24h catalog cache unconditionally on every write. Re-auth paths re-save the same npsso (e.g. token re-exchange after an expired access token), which is not an account change, so the cache was needlessly wiped and the next Cloud Play open paid a full multi-second re-fetch. Guard both on a value change, matching the iOS SecureStore behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rse) Expose the base_url container-locale parser via cloudcatalog_internal.h (lib-internal, not the public API) and add a munit case. Covers the happy path, a non-English native account (FI/fi), and the fail-closed cases (no /container/ segment, empty country, missing language slash, country longer than its buffer) so a malformed base_url can never feed a broken step0_5d container URL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Homebrew migrated the `sdl2` formula to an alias for `sdl2-compat`, an SDL2 API shim that dlopens SDL3 at runtime via @loader_path/libSDL3.dylib. macdeployqt does not copy SDL3 (it's loaded via dlopen, not linked), so the app aborted on launch with "Failed loading SDL3 library" before any of our code ran -- on any build after the Homebrew migration, local and the App Store CI alike. Bundle SDL3 next to libSDL2 under exactly the name sdl2-compat looks for (libSDL3.dylib, NOT libSDL3.0.dylib -- the latter only the bare-name fallback finds, masking the bug on dev machines that have Homebrew SDL3). Added to both scripts/build-macos.sh (sign_app_bundle, covers --iterate + full build) and the deploy-macos.yml App Store workflow (per-arch, lipo-merged + signed). Proven machine-independent via the Homebrew API: sdl2-compat has aliases: ['sdl2']. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
For an owned PS Now title the unified catalog already carries the resolved streaming entitlement, so there is nothing to look up or acquire. When the catalog provides an entitlementId for the launched game, PSKamajiSession skips the entire entitlement path (0.5b anonymous session, 0.5d product->entitlement resolve, 0.5e check/acquire) and goes straight to the authenticated session (step5/6). This is the correctness fix for storefront-less regions where 0.5d/ 0.5e 404 and the acquire always fails even though the entitlement is owned. Safety: if Gaikai rejects the fast-path entitlement (noGameForEntitlementId at session start), CloudStreamingBackend retries exactly once with forceFullEntitlementFlow=true -- the normal resolve/acquire path -- which can't loop because the fast-path is disabled on the retry. Unowned titles are unaffected: no catalog entitlementId -> full flow as before. Also fixes a real bug this surfaced: Gaikai's Step 8 (sessions/start) error dropped the response body, so the noGameForEntitlementId marker never reached the fallback check; include the body in the AllocationError. Validated on live streams: owned (Ghost of Tsushima) fast-paths and streams; unowned (RESOGUN) runs the full $0-acquire flow; a forced bad entitlement (Celeste) rejects -> one-shot fallback -> acquires -> streams. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 (c669fb3) renamed the iOS constant kCloudFallbackRegion to kCloudResolvedStoreCountry / kLegacyCloudFallbackRegion but left a dangling reference in SecureStore.clearAll(), so the iOS target had not compiled since Phase 1 (Phase 1/2 were never actually built or run on iOS until now). Clear all three current cloud-region keys instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the Qt owned-PSNOW fast-path (a90926d) to Android and iOS. For an owned PS Now title the unified catalog already carries the resolved streaming entitlement, so PSKamajiSession skips the entire entitlement path (0.5b anonymous session, 0.5d product->entitlement resolve, 0.5e check/acquire) and goes straight to the authenticated session. This is the correctness fix for storefront-less regions where 0.5d/0.5e 404 and the acquire fails even though the entitlement is owned. Safety: if Gaikai rejects the fast-path entitlement (noGameForEntitlementId at session start), the orchestrator retries exactly once with the full resolve/ acquire flow (one-shot; the fast-path is disabled on the retry, so it can't loop). Unowned titles are unaffected (empty catalog entitlementId -> full flow). Also surfaces the Gaikai step8 response body so the noGameForEntitlementId marker reaches the fallback. CloudPlayFragment.kt / CloudPlayView.swift pass the catalog entitlementId + platform from the launched game. Validated on a real device + simulator: owned (Ghost of Tsushima) fast-paths and allocates; unowned (Gitaroo Man / Bomber Crew) runs the full $0-acquire; a forced bad entitlement (Hollow Knight / Tekken 6) rejects -> one-shot fallback -> resolves + acquires the real entitlement -> allocates. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
step0_5d's /container/{CC}/{lang}/ entitlement lookup 404s on the wrong
language for a non-English native store (NL needs /NL/nl/, rejects
/NL/en/). The imagic catalog can settle its locale to English while the
Kamaji store still serves the native language, so the locale-derived
proxy step0_5d used was unreliable.
Parse the store language from the /user/stores base_url (same source we
already use for the store country) and emit it as a new "resolvedStoreLang"
field. step0_5d now prefers it over the locale proxy, falling back to the
proxy only in fallback/foreign mode where the field is empty. Mirrored
across libchiaki plus Qt, Android and iOS (setting + persist + step0_5d).
Bump schemaVersion 2 -> 3 so existing caches (24h TTL) refetch and pick up
the field immediately rather than after expiry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "PlayStation cloud isn't offered natively in your region" banner fired whenever nativeMode=false -- including when the native probe failed for an auth reason (missing/expired npsso). In that case the region was never actually determined, so the banner was misleading; the login/expired banner is the real message. It also flashed during catalog load because the persisted nativeMode held a stale value mid-fetch. Show the region banner only when nativeMode=false AND there is no auth warning AND the catalog is not loading. Applied to Qt, Android and iOS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ud-games-page Unified cloud games page: cross-platform catalog parity, streaming stats, and Android test-APK CI
assembleRelease with -Pandroid.injected.build.abi produces app-release.apk (no ABI token in the name), so `find -name "*arm64-v8a*.apk"` matched nothing. The pipe `find ... | xargs -0 ls -t` then ran `ls` with no args, listing the cwd and returning the `app` directory — a bogus non-empty value that skipped the *.apk fallback and the "No APK produced" guard, so the final `cp "app" ...` failed with "cp: -r not specified; omitting directory". Add `-r` (--no-run-if-empty) to both xargs calls so an empty find yields an empty APK var, letting the fallback find app-release.apk. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The build_apk step used `-Pandroid.injected.build.abi`, which is Android
Studio's deploy mechanism and marks the APK testOnly=true. Such an APK can
only be installed via `adb install -t` and is rejected by the normal
package installer when sideloaded ("can't install on this device" /
INSTALL_FAILED_TEST_ONLY).
Build a plain assembleRelease/Debug instead — abiFilters already limits the
native libs to the built ABI (arm64-v8a), so the APK stays effectively
arm64 but is now installable from a file manager / Downloads. Also locate
the APK in outputs/ only, so we never grab an intermediate unsigned APK.
Only touches the sideloadable-APK path; the AAB build and Google Play
upload steps are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@nyakaspeter @Chazq2023 @Leeiiiiiii I've incorporated most of your changes into a single unified PR here. One of the main issues we all ran into with the game library was that it was implemented separately on each platform — so the same logic had to be duplicated across Qt, Android, and iOS and tested three times. I've simplified this by pulling the game-loading and matching logic into one shared C library (libchiaki) that every platform now calls. So any change to the game-matching logic can be made in a single place instead of three. I haven't been able to test this end-to-end, especially in your regions, so I can't confirm it's actually fixed. ]I'm hoping you can help with that: please check out this branch, see whether it works in your region, and if not, have your LLM make the fix in the shared location. That should be much easier now that everything lives in one place. The two areas I'm least sure about outside my own region: @nyakaspeter's region fallback — I can't tell whether it holds all the way through to streaming in an unsupported region. @Leeiiiiiii — I dropped the logic that hardcodes a specific language to a datacenter, in favor of offering a language list the user picks from (with a note that a language only applies on a datacenter that serves it). I also ported a unified version of your performance overlay to all three platforms, though there are still a few fields I haven't added yet. These changes also merge the cloud catalog and library into a single view to keep things simpler (most people don't understand the distinction anyway). So overall: I think it's pretty close, but I really can't tell from here — I'd appreciate those of you in other regions testing it and confirming what works and what doesn't. Where possible, I've tried to avoid:
Direct apk download link created via github action if you want to just install the apk and see if it works or not (since I think most of you are on android)without building locally: Download Link |
Summary
Brings the PS Plus Cloud Play catalog and streaming experience to full parity across Qt, Android, and iOS, with all catalog fetch/merge logic centralized in
libchiaki(zero client-side catalog logic). Also adds an in-stream performance stats overlay, an artificial RTT safety offset for cloud sessions, Cloud Play UI polish, a cross-platform game-language picker, and an opt-in Android test-APK CI path.What's included
Cloud catalog (centralized in libchiaki)
resolvedStoreLang): the product→entitlement lookup uses the store language parsed from the Kamajibase_url, not the catalog-settled locale — fixing/container/{CC}/{lang}/404s when a non-English native store (e.g. NL) is handed the wrong language.schemaVersionbumped to 3 so existing caches refetch and pick it up.Streaming
libchiakimetrics with EMA smoothing - toggled from the in-stream overlay, off by default with no per-frame cost.UI polish
CI / tooling
deploy-android.ymlgains an opt-in APK-only checkbox (build_apk) that produces an installable, sideloadable APK artifact and skips the Google Play publish - handy for letting testers try a build before release.CMakeLists.txt; propagates to Android versionName/versionCode, iOS, macOS/Linux).cloudcatalog_mergeunit tests.Credits
This builds on foundational work by nyakaspeter (the PS Plus Cloud Play / cloud streaming groundwork) and Chazq2023 (the cloud catalog and ownership work), and incorporates ideas surfaced by Leeiiiiiii's Android fork - notably the in-stream performance overlay concept and the PSNOW entitlement/SKU resolution investigations. Thanks to all three for the groundwork and inspiration.
Test plan
/container/{CC}/{lang}/with the store's real language; owned + non-owned games resolve and stream.