25057 Commits
Author SHA1 Message Date
J1coding ce77e85dd6 Android: fix the miscased GL include that breaks every APK build
Every Android build has failed since the screensavers landed in 81d0a3e510.
The workflow still reported success, because the Android job is
continue-on-error, so nothing said so and no APK has been uploaded since.

shockwave.cpp asked for <gl/gl.h>. The vendored header is compat/GL/gl.h,
with an uppercase directory. macOS resolves that spelling and the Linux
runner does not, so the build dies on "fatal error: 'gl/gl.h' file not
found".

It is the only miscased include in the target. All 742 includes under
savers/ were checked against real directory entries rather than by testing
whether files exist, since a case-insensitive filesystem answers yes to
both spellings and would have hidden exactly this.

The line is also redundant. shockwave.cpp is never compiled on its own:
unit_shockwave.cpp wraps it in a namespace and already includes <GL/gl.h>
above it, so the corrected line hits the header guard and expands to
nothing. Its five sibling files carry no GL include at all and rely on the
wrapper, so deleting it would match them. These savers are vendored GPL
sources kept close to upstream, so this changes one character instead.

What this does not establish is whether the build then finishes. The error
has masked everything after it for days, so a second one may be waiting.
2026-08-23 12:03:28 +02:00
J1coding 1b737e25f0 iOS: give CI builds the RetroAchievements client identity
Nightly and MoonStore builds identify themselves to RetroAchievements as
stock PCSX2, so the server allows softcore only. Hardcore has worked on
locally built IPAs and nowhere else.

Nothing was broken. pcsx2/Host.cpp reads the client version from
ra_ua_secret.h behind __has_include and falls back to a stock agent when
the macro is absent. That header is gitignored, so it has never existed on
a runner.

CI now writes it from the IOS_RA_UA_VERSION repository secret, the way the
Android job already writes its keystores from NIGHTLY_RELEASE_KS_B64. A
missing secret warns and continues, because fork pull requests never
receive secrets and a nightly that fails to publish is worse than one
without hardcore. A malformed version fails the build instead:
RetroAchievements refuses a version it cannot order, and a refused agent is
indistinguishable from an unknown one on the client.

Only the nightly and pushes to master embed it. Pull request artifacts keep
the stock agent, so a test build is not one more public copy of an identity
that needs a release to revoke.

A second step reads the finished binary and fails unless the exact version
is in it, trailing space included, since a stale 1.2.3 is a prefix of a
current 1.2.345. Both iOS jobs are continue-on-error, so that failure does
not stop the workflow, but it does skip the upload that follows it, and the
publish step treats a missing IPA as absent rather than fatal. A softcore
build is not shipped.

None of this makes the version private. The compiler bakes the finished
agent into the binary as a plain literal, so anyone holding a build can
read it out. What the gitignored header prevents is a fork inheriting a
live identity straight from source.
nightly-20260823
2026-08-22 16:26:22 +02:00
jpolo1224 9111cd617d GS: remove the texture-replacement diagnostics
All of it was instrumentation for the Persona 3 FES investigation, and that turned
out not to be a texture problem at all -- three named patch groups were failing to
enable, and the texture pack stopped matching because the mod they gate patches the
game's font data.

Removed: the TCPROBE cross-version probe in HashCacheKey::Create, the hit/miss
counters and their geometric reporter, the per-miss detail listing what the pack
holds for a TEX0, the pack filename samples at map load, the duplicate/shadowed
accounting, and the mtime sidecar the palette fallback needed.

Kept the pre-existing "N indexed for '<serial>' (scanned <dir>)" line, which
predates this and is genuinely useful: a zero there with a path that does not match
the user's pack folder is still the fastest read on a pack that does nothing.

The cache budget work is untouched.
2.6.6.8 nightly-20260822 2.6.6.8.1
2026-08-21 23:26:12 -04:00
jpolo1224 20a9238b31 Patches: a toggle that changes no patch= lines still has to write the enable list
"I enabled HostFS and it is still off", with the switch sitting on. His game INI
proves it: [Cheats] present but empty, and no [Patches] section at all -- the
enable-list write never ran.

toggleLocalCheat flips the switch optimistically, then bails early when
setBodyEnabled produced an identical body:

    if (newBody == target.body) return   // pushEnableList never reached

setBodyEnabled only comments or uncomments patch= lines, and community pnach files
ship UNCOMMENTED. So for exactly those files enabling is a no-op on the body, the
early return skips the enable list, and Patch.cpp applies a NAMED group only when
its name is in [Patches]/[Cheats] Enable -- unlabelled groups are the only ones
that auto-enable. Switch on, file already correct, patch never applied, nothing
anywhere saying so.

The two writes are independent. Rewrite the body only when it differs; always push
the enable list.

This is what actually blocked the Persona 3 FES mods. The HostFS loader group could
not be enabled at all, so the mod never loaded, and the texture pack keyed to the
modded font stopped matching as a consequence -- which is how it arrived as "my
texture mods broke".
2026-08-21 23:09:00 -04:00
jpolo1224 b506fe2683 Patch: enabling from the library writes to the game INI, not the base layer
"I enabled HostFS and it is still off." The Patch Manager reads the toggle back as
on, and the patch loader never sees it.

ReloadEnabledLists reads through LayeredSettingsInterface, which returns the FIRST
NON-EMPTY layer with LAYER_GAME ahead of LAYER_BASE. So the moment a game's INI
carries any [Patches] Enable entry, the base list is invisible. setEnabledPatches
targeted the game INI only when a VM was running -- opened from the library it had
nowhere to go but base, where a shadowing game layer swallowed it. The UI then
reads the value straight back, which is why it looks like it took.

For this reporter that patch is the HostFS loader their Persona 3 FES mod runs on,
so it stayed off through repeated attempts to switch it on, the mod never loaded,
and the texture pack keyed to the modded font kept missing.

Take the serial from the caller -- PatchManagerViewModel already resolves one via
bestSerial() -- and resolve the game INI by the same glob gameIniBeginWriteForSerial
uses. With no VM and no serial, or no INI for that serial yet, it still falls
through to base, which is correct for a game that has no per-game file at all.

This is the "wants a serial/CRC parameter" the old comment left as the real fix.
2026-08-21 22:32:15 -04:00
jpolo1224 c91ff32d21 Patch: delete the enable-list purge
It was a one-time repair for lists poisoned by patches arming themselves, and it
has done more harm than the thing it repaired. It deleted every [Patches]/[Cheats]
Enable entry, not just the self-armed ones, so anyone who had turned a patch on
deliberately lost it silently on update -- and when the patch in question is the
HostFS loader a mod depends on, the mod stops loading, the texture pack keyed to
the modded data stops matching with it, and the whole thing presents as "my
texture mods broke". That cost eleven builds to chase from the wrong end.

Restricting it to lists that look auto-armed was the first attempt at a fix, and
it was still wrong: the threshold is a guess, and someone with thirty cheats
enabled on one game would have been wiped by it just the same. A narrower
silent-data-loss bug is not a fix.

Removing it outright instead, because its job is finished:

  - It is a migration, and it has already run for everyone who updated in the past
    month. What remains is a shrinking population and a permanent hazard.
  - Nothing re-poisons a list now. 10f4f73fe8 removed the unconditional
    syncAllEnableLists on Patch Manager open, stopped EnumeratePnachFiles falling
    back to the bundled zip over on-disk files, made delete() drop names from the
    enable list, and stopped D-pad Right arming the row under the cursor.
  - Anyone still carrying a poisoned list can clear it themselves, because
    disabling a patch sticks now. That was the original complaint.

Nothing writes these lists except the player, which is how it should have been.
2026-08-21 22:09:53 -04:00
jpolo1224 2bbe909210 Revert the palette fallback and the purge notice
Both existed to work around the wrong diagnosis. The Persona 3 FES mods were not
failing in the texture cache at all -- a PNACH mod was switched off by the
enable-list purge, and because PNACH mods patch game DATA the texture pack keyed
to the patched font stopped matching as a side effect.

The palette fallback substituted a different colour variant of a glyph when the
exact palette was missing. It raised the hit count but could not be right: the
palette IS the colour, so substituted art carries the wrong shade, and it never
addressed why the hashes moved. Gone; the replacement path behaves exactly as it
always did.

The one-time notice goes too, per jpolo1224: the purge no longer takes deliberate
lists, so nothing needs announcing going forward.

Kept: the diagnostics that actually found this -- files scanned versus indexed
versus shadowed, hit and miss counts, and the per-miss detail naming what the pack
holds for that texture. Those are what turned "mods do not apply" into a
measurement, and they cost nothing when nothing is wrong.
2026-08-21 21:24:35 -04:00
jpolo1224 0dcf53af88 Patch: stop the enable-list purge switching off patches people chose
This is what broke Persona 3 FES mods, and it was never the texture cache.

10f4f73fe8 fixed a real bug -- opening the Patch Manager armed every uncommented
group of every on-disk .pnach -- and cleaned up the damage with a one-time purge
of [Patches]/[Cheats] Enable. The purge deleted every entry unconditionally,
including the ones people had turned on deliberately, so updating past 2.6.6
silently switched off everyone's patches and cheats.

It surfaces in the worst possible way. The reporter's mod is a PNACH,
94A82AAA_Modern_Persona_Switch_aem.pnach, and PNACH mods patch game DATA. With it
disabled the font glyphs in GS memory differ, so the texture pack keyed to the
patched font stops matching as well -- and the whole thing reads as "my texture
mods stopped applying". Their logs say it plainly: 18,791 patch lines applied on
2.6.6 against 76 now, same .pnach found in both.

Two changes:

  - The purge now only fires on lists carrying the auto-armed signature.
    syncAllEnableLists persisted every uncommented group across ~4000 bundled
    files, so a poisoned list runs to hundreds of names; a hand-built one is a
    handful. At or under 25 entries it is treated as deliberate and left alone.

  - A one-time notice for the people v1/v2 already hit. Nothing can give them back
    what was deleted, and they have no way to know it happened -- the .pnach is
    still on disk and still found in the log, it is simply no longer enabled.
    Saying so once is the only repair available.

The immediate fix for anyone affected is to re-enable their patches in Patches &
Cheats; the purge is one-shot and will not take them again.
2026-08-21 21:21:13 -04:00
jpolo1224 4d97b6cdf5 GS: fall back to another palette variant when the exact one is absent
A paletted replacement is keyed on TEX0 hash AND palette hash, so a pack only
applies while the game asks for a palette its packer happened to dump. Persona 3
FES lands exactly there: the pack carries several palette variants of each glyph
and the game asks for one that is not among them, so every glyph misses while the
unpaletted art around it replaces fine -- 189 hits against 323 misses, which is
why the scene looked right and the text did not. With this, 441 against 71, and
none of the remainder are palette mismatches.

Lowest palette hash, deliberately. It is arbitrary but STABLE: the same glyph
resolves to the same file on every draw and every run, so text renders in one
consistent colour. Choosing by file mtime was tried and was worse in a way worth
recording -- different glyphs won different variants and the text came out
multicoloured.

The colour can still be wrong, since the replacement image has the packer's
palette baked in and there is no recolouring it. That is the trade, and it is the
right way round: a mod that applies in the wrong shade beats one that does not
apply at all.

This does NOT explain why the pack matched on 2.6.6. That is a separate finding
and still open: the same glyph at the same address with the same palette hashes
differently now (2.6.6 asks 85076d2a533c0128, current asks f0576dc2f0bb17d5),
with TBP0, TBW, PSM, TW/TH, region and lod all identical between them. The bytes
in GS local memory differ, which is upstream of texture replacement entirely.
2026-08-21 21:06:50 -04:00
jpolo1224 a4f6fb0bd9 GS/TC: split the base-level hash from the mip chain in the probe
Both builds report identical TBP0, TBW, PSM, TW/TH, region and lod for the same
font glyph, and identical leading bytes -- yet different TEX0 hashes. Since
HashTextureLevel reads straight out of GS local memory, the bytes at that address
differ; the shipped hash folds the base level and every mip into one value, so it
cannot say which of them moved.

Recompute the base level alone for the probed textures and log it beside the
combined hash, plus the actual mip range rather than the bool the first probe
recorded. Base matching with the combined differing means the mip chain moved;
base differing means the glyph data itself did. Those are different bugs.
2026-08-21 21:01:28 -04:00
jpolo1224 6375594405 GS/TC: cross-version probe for the texture-hash divergence (diagnostic)
The two builds ask for disjoint TEX0 hashes for the same on-screen font -- nine
values on 2.6.6, eight now, no overlap, same palette hash, same PSMT4. Every
function feeding the hash is byte-identical between them, so what differs is the
DATA, and HashCacheKey records only the region's width and height: never where it
starts, never which address it came from, never whether mips were folded in.

Print exactly what the key discards -- TBP0, TBW, TW/TH, the full region rect
including its origin, whether lod was present, and the first bytes actually
hashed -- so two logs can be diffed instead of theorised about.
2026-08-21 20:40:17 -04:00
jpolo1224 7573a3e41f GS: drop the palette substitution, keep what it taught us
A paletted glyph's palette IS its colour, so when the game asks for a palette the
pack does not carry there is no "close enough" file to stand in for it.
Substituting produced multicoloured text: the pack holds eight colour variants of
each glyph, all from one extraction seconds apart, and different glyphs won
different variants. Both selection rules were wrong for the same reason -- lowest
hash preferred the base pack, newest was noise, and neither can be right when the
thing being chosen IS the colour.

Keep the diagnostic half, which is what actually advanced this: on a paletted
miss, list every colour of that TEX0 the pack does hold. That distinguishes "the
pack lacks this texture" from "the pack lacks this COLOUR of it", and the second
is the real finding here -- the modder dumped eight colours and the game is asking
for a ninth.

Leaves the replacement path behaving exactly as it did before any of this, with
better logging. The open question is unchanged and now isolated to one value: why
the game asks for a palette that is not among the dumped eight.
2026-08-21 19:50:44 -04:00
jpolo1224 461bcdc52f GS: palette fallback picks the NEWEST variant, and names every candidate
Shadowing was not it: 8386 files, 8339 indexed, only 4 collisions -- and the
remaining misses are 640x384 PSMCT32 with no palette, i.e. FMV frames, which hash
uniquely per frame and can never match. So the layered mods index fine and the
game does ask for them.

What actually loses is the choice BETWEEN palette variants. A mod over a base
pack supplies the same glyph under a different palette, so the two never collide
during indexing -- and the first cut of this fallback took the lowest palette
hash, which is arbitrary, and silently preferred the base pack. The player ends
up seeing the pack they installed FIRST, which is the opposite of what layering a
mod means, and matches the report exactly: base HD text applied, slim font did
not.

Prefer the newest file instead. Modification time is the one signal that
separates "the pack added last" from "the pack it was layered over", and the
directory walk already reports it, so this costs no extra I/O. Ties break on the
lower hash purely to stay reproducible.

Also log every candidate -- palette hash, mtime, full path -- for the first few
fallbacks, so the choice is auditable rather than asserted. If the newest file is
still the wrong one, that log says so immediately instead of costing another
round.

Still not shippable: substituted art carries the packer's palette, so tints can
be wrong, and mtime is a heuristic rather than an expression of intent. A real
fix wants explicit pack precedence.
2026-08-21 19:11:18 -04:00
jpolo1224 367b0b7e14 GS: report replacement files shadowed by an earlier file
The palette fallback moved Persona 3 FES from 189 hits / 323 misses to 441 / 71,
with ZERO of the remaining misses matching a name under another palette -- so the
palette hash is settled, and what is left is textures the map does not hold under
that name at all. The base HD pack now applies; the layered mods still do not.

emplace does not overwrite, and this scan is RECURSIVE. Two files in different
subdirectories that decode to the same texture name collide, and whichever the
directory walk reaches first wins -- silently, with nothing to say which of them
ended up on screen. That is exactly how a mod layered over a base pack loses: not
on intent, on traversal order.

Count the collisions, name the first six with both paths, and report files
scanned alongside textures indexed. "My mod is not applying" and "my mod is being
shadowed by another pack" are indistinguishable to the person reporting it, and
until now they were indistinguishable from the log too.

Nothing about which file wins changes here. Measure first: if the count is zero,
the layered mods are absent rather than shadowed, and that is a different fix.
2026-08-21 18:53:47 -04:00
jpolo1224 d9252de06e GS: palette-relaxed replacement fallback (TEST BUILD, not for release)
A paletted texture is looked up by TEX0 hash AND palette hash, so a pack only
applies while the game asks for a palette its packer happened to dump. The
Persona 3 FES logs land exactly there: the pack carries several palette variants
per glyph (ea34cb7b, df582335, b6eebe9e, 99b15967 ...) and the game asks for
cf4df1c018862d85, which is none of them. Every glyph misses while the unpaletted
art around it replaces fine -- 189 hits alongside 323 misses -- which is why the
scene looks right and the font does not.

Worth recording what this ISN'T, since each cost a round to eliminate: the cache
budget (already present in the working version), the pre-ELF hack strip
(transient, ApplyCoreSettings restores it), paltex (it relaxes the hash cache,
never the replacement lookup -- CreateTextureName always uses the real palette
hash for paletted formats), and name parsing, the hash function and the paltex
defaults, all byte-identical to 2.6.6.

So when the exact palette is missing but the pack holds this TEX0 under others,
take one -- lowest palette hash, so the choice is reproducible rather than
dependent on map iteration order.

NOT correct as shipped, and deliberately labelled so: the replacement image has
the packer's palette baked in, so substituted art can carry the wrong tint. It
exists to answer one question. If the mods appear, the palette hash was the only
obstacle and the real fix is about WHICH palette to prefer, not about loading at
all. If they still do not appear, the pack was never the whole story.
2026-08-21 18:37:38 -04:00
jpolo1224 ddcfd39dab GS: print the pack's own filenames, instead of asking the player for them
The previous diagnostic could say "the pack holds this TEX0 under a different
palette" but not which, and the difference is the entire diagnosis:

  <tex0>-<clut>-<w>x<h>-<bits>.ext   a real, different palette hash -- the palette
                                     contents differ at run time from dump time
  <tex0>-<w>x<h>-<bits>.ext          no palette field, so it indexed with
                                     CLUTHash=0 and can only match under paltex

Answering that meant asking someone to list a directory on their own device. The
people who hit this are players; that is not a reasonable thing to ask, and it is
information the emulator already has in memory.

So print it: up to two matching entries per miss, with their CLUT hash and
filename, plus three sample names at map load so the pack's naming style is on
the record even when nothing misses.

Bounded the same way as the rest of this logging -- at most eight misses reported,
at most two entries each -- because it walks the filename map, and this game's
pack has 8339 entries in it.
2026-08-21 18:25:28 -04:00
jpolo1224 0fe5715336 GS: make the replacement diagnostics able to answer the question
The first version could not. LookupReplacementTexture runs once per NEWLY HASHED
texture, not per draw, so a whole session can be a few hundred calls -- and a
report threshold of 20k therefore printed once, at the very first lookup, and
never again. Two tester logs came back reading "0 hits, 1 misses" after 139 and
195 seconds of play, which cannot distinguish one lookup from twenty thousand.
That ambiguity was the whole answer they were supposed to give.

Three changes:

  - Report on a geometric schedule (1, 2, 4, 8 ... then every 4096) rather than a
    fixed threshold. Bounded whatever the rate, and dense at the start, which is
    where the answer usually is.

  - Print the first eight misses in full: the TEX0 hash, CLUT hash, dimensions
    and PSM actually asked for, plus whether the pack holds that same TEX0 under
    a DIFFERENT CLUT hash. That is the difference between "the pack does not have
    this texture" and "it has it, under another palette hash" -- and the latter is
    the usual answer for paletted UI art, which is what a Persona 3 FES font and
    menu panel are.

  - Log preloading, paltex, async and upscale once at map load. Those decide
    whether a lookup is ever ATTEMPTED rather than whether it matches; a pack that
    indexes thousands of files and is then never consulted looks identical from
    outside to one that misses every time.
2026-08-21 17:58:03 -04:00
jpolo1224 b5b2ed24e1 GS: say whether replacement lookups are hitting, not just how many indexed
"My texture pack does nothing" has two failure stages and the log only covered
the first. The indexed count proves the FILES were found and their names parsed;
it says nothing about whether any draw ever asks for one. A pack that indexes
thousands of textures and misses every lookup is a hash problem -- wrong dump
settings, paltex vs CLUT, wrong upscale -- and from outside it looks exactly
like a pack that never loaded at all. Both read as "the mods are not applying".

Count hits and misses in LookupReplacementTexture and summarise one line per
20k lookups. Summarised rather than logged per lookup because this runs per
draw, and log volume on its own is enough to stall the emulator.

Also counts how many misses WOULD have matched with the CLUT hash zeroed. That
separates "the pack does not contain this texture" from "it does, but the
palette hash differs", which is the usual answer for paletted UI art -- fonts
and menu panels, the exact things a Persona 3 FES mod replaces.

Counters reset in ReloadReplacementMap: carried over from a previous game, a
stale hit count reads as a healthy pack.

Also -Wno-missing-braces on the savers target. Welsh's sources initialise nested
aggregates without inner braces throughout, which -Wall diagnoses once per site
per TU: ~5 million lines and an 842 MB build log, which is how it was noticed.
Upstream code we do not restyle, so the diagnostic has nothing to tell us. Purely
a diagnostic flag; no codegen change.
2026-08-21 17:41:13 -04:00
jpolo1224 81d0a3e510 Library: five more screensaver backgrounds, from ARMSX3
ARMSX2 had Flurry alone. This brings over the Really Slick Screensavers tree
ARMSX3 already runs -- Flux, Plasma, SolarWinds, Hyperspace, Lattice and
Skyrocket -- as one libsavers.so beside libflurry.so.

Terry Welsh's savers, GPL-2.0-OR-LATER, which this tree may take under the "or
later" clause. Nothing here comes from rss-glx: that Linux port is GPL-2.0-only
and cannot go into ARMSX2 at all, which is why Lattice and Skyrocket were
hand-ported from the Windows sources upstream rather than taken from the neutral
versions rss-glx already has. The per-file header is what decides this, not the
repository's LICENSE file; they disagree.

The saver sources build with RS_XSCREENSAVER, selecting their platform-neutral
path, against the GLES2 shim in savers/compat and savers/gl1.c rather than being
rewritten -- so they stay re-pullable and recognisably his code.

Four things the tree carries that are not obvious from the sources:

  - The GL thread takes a 16MB stack. Skyrocket's World constructor declares a
    1024x1024x3 starmap as a local, and a default stack is a SIGSEGV inside
    memset before the first frame draws.
  - Each saver is compiled through a *_unit.cpp that wraps it in a namespace.
    They all declare draw/idleProc/cleanUp/readyToDraw, because each was built as
    its own executable, and two in one .so collide at link. The shared libraries
    must be pre-included OUTSIDE the namespace or the declarations never match
    their definitions -- which compiles clean and fails at link.
  - The lifecycle is serialised by a mutex and a generation token: gl1 keeps its
    state in one global, so a view may only free the run it started. Without it
    the first selection works and every later one is black.
  - gl1_frame_begin() masks alpha off after the first frames. These savers fade
    the previous frame rather than clearing, and that fade writes destination
    alpha, dragging a composited surface transparent -- which reaches the screen
    as full-screen TV static.

Hyperspace runs with dShaders = 0 (its ARB shader path needs objects GLES2 does
not have; upstream exposes this as -shaders 0 and falls back to it itself), and
Skyrocket with dSound = 0 (upstream drives OpenAL and bakes ~7MB of samples into
headers; soundEngine.h is a stub here). Hyperspace and Skyrocket ship no presets
upstream, so neither shows a preset picker -- invented ranges are what made
earlier ports fail to start.

SaverGlView replaces FlurryGlView, which it supersedes: it hosts Flurry and the
Really Slick savers behind one SaverSpec, and keeping both would have declared
FlurryNative twice in one package. Helios is deliberately absent -- it rendered
but animated wrongly and the cause was never found.
2026-08-21 15:26:28 -04:00
jpolo1224 c3f859270f Credit the PR contributors in the code, not only the release notes
Merging preserves authorship in git history, but nobody reads git log to find
out who wrote a file. The tree already had the convention -- "Feature
contributed by misantronic (PR #391)" on exportRecentGamesPublic -- it just was
not applied to the three PRs merged here.

  MemoryCardBackup.kt           bmdhacks     (PR #608)
  PerGameOverrides.{h,cpp}      bmdhacks     (PR #593)
  RecentGamesContentProvider.kt misantronic  (PR #566)

The provider's line also records that the opt-in gate was added on merge, so the
gate is not mistaken for part of the contribution.
2026-08-21 15:14:13 -04:00
jpolo1224 416eb4c668 Library sharing: make the recent-games provider opt-in
RecentGamesContentProvider (PR #566) ships android:exported="true" with no
android:permission, no readPermission, and no caller check in query(). That is
required for the feature to work -- a signature-level permission would only admit
apps we sign, and the companion app it exists for is third-party -- but as merged
it means every app on the device, holding no permissions at all, can read the
recently-played list: titles, serials, last-played times, and the file URIs,
which carry the user's folder layout and frequently their real name. No prompt,
no way to turn it off.

Gate query() on a preference that defaults to OFF, and surface it in App
settings, matching how the second-screen panel and Discord presence are handled:
anything that exposes data outside the app is the user's decision and starts
disabled.

The flag lives in the same "ARMSX2" SharedPreferences file the provider already
reads for the library itself, so the toggle and the gate are one value rather
than two that can drift. Written with commit() and not apply(): the reader is a
different process and can be queried the moment the switch returns, and apply()
only promises the in-memory value.

Returns an empty cursor rather than null when sharing is off -- null is the
failure signal a ContentResolver caller has to special-case, and "the user has
not enabled this" is a legitimate answer rather than an error.
2026-08-21 15:11:31 -04:00
jpolo1224 88d95a83f5 Merge branch 'pr-566' into jit-android-catchup-gv7 2026-08-21 15:10:24 -04:00
jpolo1224 ff2b0c2155 Merge branch 'pr-593' into jit-android-catchup-gv7 2026-08-21 15:08:40 -04:00
jpolo1224 2c36725e11 Merge branch 'pr-608' into jit-android-catchup-gv7 2026-08-21 15:03:02 -04:00
jpolo1224 9262ea0480 Merge remote-tracking branch 'origin/master' into jit-android-catchup-gv7 2026-08-21 14:57:26 -04:00