Commit Graph
195 Commits
Author SHA1 Message Date
Kurt Himebauch 3a4c6a4a7a fix: DNS gateway pick and public fallback slot, drop reference to an unshipped script 2026-11-02 18:56:45 -05:00
Kurt Himebauch 34a1cbbead fix: keep PRoot out of the APK, VPN DNS first, bridge failure exits, HUD polling stays on 2026-11-02 18:17:00 -05:00
Kurt Himebauch f05e0df39e Merge remote-tracking branch 'origin/main' into review/malibridge-1950 2026-11-02 18:14:00 -05:00
Moroni Granja cdde9d3390 fix: a GOG game whose installer cannot run gets its install registry (#2022) 2026-11-02 18:06:08 -05:00
FX1922 3583643a11 feat: Add the Game Jolt store (#1986) 2026-11-02 10:05:30 -05:00
DroidDeck Merge 8171bf20fd Merge latest origin/main and resolve conflicts 2026-11-02 10:44:24 +08:00
DroidDeck Merge 99dce74b09 fix: address maintainer review (restore README, revert version bump) 2026-11-02 10:43:42 +08:00
The412Banner 40dfcd94f9 fix(game-env): DOOM (2016) keeps the swapchain image count it asks for (#1987)
DOOM (2016) asks for exactly two swapchain images and stops with "FATAL
ERROR: getSwapchainImagesKHR failed with error (VK_SUCCESS)" when it gets
more. Every game launch gets GAMESCOPE_WSI_MIN_IMAGE_COUNT=8 and
GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT=1, so gamescope's WSI layer gave it 8
images (3 with only the count removed) and the game crashed on start.

When DOOMx64vk.exe sits beside the launched program (DOOMx64.exe for a GOG
install, or DOOMx64vk.exe), both variables are removed and
vk_x11_strict_image_count=true is set, the option Mesa's driconf gives the
game. Other games keep the defaults. A per-game value in the environment
editor still wins.

Tested on an AYANEO Pocket FIT with DroidDeck 0.6.9 and the same values set
per game: the swapchain gets 2 images and the game reaches its main menu at
60 fps; before, it crashed on every launch.
2026-11-02 02:02:18 +00:00
DroidDeck Merge a2a5f84711 Merge origin/main into feat/malibridge-v25 2026-11-01 19:59:31 +08:00
DroidDeck Merge 55897a47a9 docs: show openMali worklog on repository home 2026-11-01 19:51:53 +08:00
DroidDeck Merge 67197aafec docs: add openMali MaliBridge v25 worklog 2026-11-01 19:47:33 +08:00
Moroni Granja dbf114a0f2 fix: draw the guest cursor into the glasses picture (#1925) 2026-11-01 00:26:14 -04:00
MaxsTechReview 0a07926ef3 docs: DroidDeck 0.6.9 release notes (#1904)
* docs: DroidDeck 0.6.9 release notes

* docs: explain the banking app fix in the 0.6.9 notes

* docs: banking app line in the 0.6.9 summary

* docs: dual-screen line in the 0.6.9 summary

* docs: move the dual-screen line into the 0.6.9 intro

* Update release notes for version 0.6.9

Reformatted the release notes for clarity and improved readability.

* docs: add #1888 and #1896 to the 0.6.9 notes

* docs: add #1903 to the 0.6.9 notes
2026-10-31 13:58:57 -04:00
K11MCH1 583f34fc55 fix: stop importing games as Spacewar (480) and follow a changed Steam app id (#1896) 2026-10-31 13:36:43 -04:00
DroidDeck Merge d4a6d95127 Merge MaliBridge integration (feat/malibridge) into 0.6.7 2026-10-31 19:01:40 +08:00
Kurt Himebauch e3c3471d2f Update README (#1874) 2026-10-30 21:57:32 -04:00
maxjivi05 0a5955d6fa Merge remote-tracking branch 'droiddeck/main' into feat/malibridge
# Conflicts:
#	README.md
#	app/src/main/java/com/droiddeck/launcher/gpu/TurnipDriver.java
#	app/src/main/java/com/droiddeck/launcher/runtime/BwrapSpawner.kt
#	app/src/main/res/values-ar/strings.xml
#	app/src/main/res/values-b+es+419/strings.xml
#	app/src/main/res/values-bg/strings.xml
#	app/src/main/res/values-cs/strings.xml
#	app/src/main/res/values-da/strings.xml
#	app/src/main/res/values-de/strings.xml
#	app/src/main/res/values-el/strings.xml
#	app/src/main/res/values-es/strings.xml
#	app/src/main/res/values-fi/strings.xml
#	app/src/main/res/values-fr/strings.xml
#	app/src/main/res/values-hi/strings.xml
#	app/src/main/res/values-hu/strings.xml
#	app/src/main/res/values-in/strings.xml
#	app/src/main/res/values-it/strings.xml
#	app/src/main/res/values-ja/strings.xml
#	app/src/main/res/values-ko/strings.xml
#	app/src/main/res/values-ms/strings.xml
#	app/src/main/res/values-nb/strings.xml
#	app/src/main/res/values-nl/strings.xml
#	app/src/main/res/values-pl/strings.xml
#	app/src/main/res/values-pt-rPT/strings.xml
#	app/src/main/res/values-pt/strings.xml
#	app/src/main/res/values-ro/strings.xml
#	app/src/main/res/values-ru/strings.xml
#	app/src/main/res/values-sv/strings.xml
#	app/src/main/res/values-th/strings.xml
#	app/src/main/res/values-tr/strings.xml
#	app/src/main/res/values-uk/strings.xml
#	app/src/main/res/values-vi/strings.xml
#	app/src/main/res/values-zh-rCN/strings.xml
#	app/src/main/res/values-zh-rHK/strings.xml
#	app/src/main/res/values-zh-rTW/strings.xml
#	app/src/main/res/values/strings.xml
#	tools/linuxfs/overlay/usr/local/bin/droiddeck-game-env
2026-10-30 11:58:55 -04:00
FX1922 67d391958f feat: apply itch updates as butler patches (#1829) 2026-10-30 07:38:06 -04:00
MaxsTechReview f69e2d0171 fix: Steam's UI draws at an even share of the refresh near 60 fps instead of the panel's full rate (lag and heat since 0.6.7) (#1809) 2026-10-30 02:16:25 -04:00
Kurt Himebauch 001ec02a17 chore: bump version to 0.6.7 (16) (#1783) 2026-10-29 15:20:25 -04:00
Kurt Himebauch a69ae77fc5 docs: DroidDeck 0.6.7 release notes (#1765) 2026-10-29 15:18:55 -04:00
Kurt Himebauch 198f667bf0 feat: add GOG maintenance 2026-10-28 16:10:17 -04:00
Kurt Himebauch 9294a60172 perf(proot): accelerate and cache glibc syscall relays (#1687)
Libc-internal file operations bypass the existing preload wrappers and
still stop in PRoot. Patch recognized ARM64 SVC sites in libc and the
runtime loader, reuse the current translator for 20 path syscall
numbers, and return declined calls to PRoot. Enabled by default after
the tracer-key check; `PROOT_FP_PATCH_SVC=0` remains the troubleshooting
opt-out.

The hardened implementation patches 47 sites on Thor's glibc 2.43.
Relays preserve general/SIMD registers, flags, FP state and errno, and
provide unwind metadata back to the original libc frame. Text is patched
through `/proc/self/mem` after relay pages become executable.
Initialization skips multiple live threads and SVE/SME. Alternate signal
stacks and insufficient remaining stack use PRoot; main-stack growth
respects the current limit. No proroot code or binaries are imported.

Robust-list set/get sites deliberately remain with PRoot/the kernel.
Device tests reproduced stale state when the earlier private-TLS
implementation mixed patched and raw callers. Removing that shortcut
fixes coherence and preserves the host's actual return policy; the
earlier thread-creation gain is retired. SDK forwarding still enables
faccessat2 ENOSYS fallback and clone3 ENOSYS only before Android 15.
Successful clone3 is never executed from the C relay. Existing fsync
handling is unchanged. No in-process RET_TRAP filter is added, so
SIGSYS-mask/handler machinery is not applicable.

Version-2 build-ID/API tables cache relative patch sites in `/tmp`.
Every hit revalidates executable bounds, alignment, instructions and
syscall numbers. Invalid/unsafe files rescan; publication uses a private
temporary file and atomic rename. The PRoot dependency fetch also gains
a mirror matching the existing pinned checksum, resolving upstream
availability failures without changing versions.

Five alternating pairs using the final bundled library on Thor/Android
13:

| operation | wrappers only | hardened relays |
|---|---:|---:|
| libc-internal stat | 49.6 us | 6.0 us |
| libc-internal open/close | 50.4 us | 7.8 us |
| libc faccessat | 49.7 us | 22.0 us |
| libc getcwd | 54.9 us | 5.0 us |
| thread create/join | 136 us | 144 us |
| complete tiny exec/wait | 4.44 ms | 5.60 ms |

An earlier five-pair hardened-library run measured stat 50.1 to 5.9 us
and open/close 51.9 to 7.1 us. The metadata gains survive the safety
checks. Neither run shows a thread-creation gain, and tiny execs retain
roughly 0.9-1.2 ms overhead.

Three full Steam startup pairs yielded Home-ready medians of 47.02 s off
/ 45.25 s on, with overlapping 43-50 s ranges. Steam/CEF-only PSS
medians were 1,223.1 / 1,233.9 MiB; the roughly 11 MiB difference is
smaller than observed variation, but does not establish zero memory
cost. Startup/memory used one unchanged signed bundled-library APK
without overrides; the final stack-gap guard only increases distance
reserved above other mappings. No reliable whole-client startup
improvement is claimed.

Earlier fresh Half-Life SD installs took 350/341 s off and 326/327 s on.
These are historical results from the preceding robust-list revision,
not a post-hardening download retest. Network/shader scheduling varied,
allocation did not reliably improve, and the repeat baseline committed
faster. This PR demonstrates metadata acceleration; it does not
demonstrate a reliable fix for Steam's SD allocation/commit bottleneck.

Validation on `2c3c135b` (production code unchanged since `4998b046`):

- Local release build, runtime/native-library checks, release signing
and four focused Android tests pass. [Final-head
CI](https://github.com/Droid-Deck/DroidDeck/actions/runs/40704837477)
passed: 1,431 Python/native tests (49 skips), 2,648 Android tests (zero
failures/skips), and 162 Rust tests. CodeRabbit is green on the same
head. The installed production APK forwards API 33 and patches 47 sites
with an unset enable flag and no override; Steam Home renders.
- Embedded and shared-PIC matrices pass on glibc 2.43; shared-PIC also
passes on separately loaded glibc 2.39. Coverage includes raw
results/errno/registers/FP state, path/flag/error and protected-pointer
cases, exact readlink boundaries, mixed raw/relay robust state,
concurrent threads, fork, thread churn, alternate-stack signals,
deferred/FIFO cancellation, signal siglongjmp recovery, forced unwind
with caller cleanup, and limited remaining stack.
- Four device test-process filters deny the memory patch file, relay
mapping, executable permission, or patch writes. Every case leaves
original syscall execution working. Host injection also verifies partial
installation after an individual site fails.
- Concurrent atomic symlink replacement passes 3,000 internal
stat/readlink iterations at default/zero TTL. Eight focused host tests
pass, covering instruction/branch limits, stack/limit fallback,
untrusted cache files and NDEBUG checks. API selection gives 45/47/46
sites for unknown/33/simulated-35; unset and explicit 1 agree, and 0
leaves sites untouched. This does not replace Android 15 hardware
testing.
- All bench tools compile with -DNDEBUG. Relative/absolute fixture
workloads agree, and thread/ABI/failure checks pass on Thor with those
builds.
- Earlier internal fts/realpath/NSS/dlopen and normalized read-path
equivalence pass. The write suite retains the existing wrapper SD
Unix-socket mismatch against PRoot.

CodeRabbit disposition: fixed relative fixture paths, prevented
benchmark checks disappearing under NDEBUG, corrected the
limited-fallback description, and narrowed memory-file denial so the
thread check still succeeds. The default-off recommendation was
addressed by fixing/testing the underlying guard and unwind issue while
retaining the requested default-on behavior. The readlink count
suggestion is intentionally not adopted: device boundary comparisons
reproduce current PRoot behavior, so changing that inherited policy
belongs in a separate correctness change.

This is not consequence-free instruction patching: constructor time,
private text copies and stack checks have costs. Android 15+ ARM64 and
16 KiB-page hardware, arbitrary asynchronous cancellation/non-local
exits, user-managed stacks, and attributable tracer-counter measurements
remain gaps. Loader calls before the constructor and arbitrary guest ELF
syscall sites remain outside scope.

Thor was restored to its original APK and bundled library, with app data
preserved and test settings, overrides, temporary files and version-2
test caches cleared.

Detailed evidence and revision boundaries: [proroot
analysis](https://github.com/Droid-Deck/DroidDeck/blob/2c3c135b52c3dd949ef5a99a67d8c12eba24406c/docs/development/proroot-analysis.md).
2026-10-28 10:53:26 -04:00
Kurt Himebauch 2c3c135b52 test(proot): address benchmark review findings 2026-10-28 10:19:29 -04:00
Kurt Himebauch 4998b04636 fix(proot): harden relay state and unwinding 2026-10-28 10:06:44 -04:00
Kurt Himebauch 8f6b2a4123 feat(itch): update detection and safe updates for installed games (#1665)
## Summary

Adds update detection for installed itch.io games. This is PR4 of the
itch.io store rework (browse, filters, game page and collections are
merged; Linux support is #1589). Butler patching comes later as an
add-on pack. Updates here download the full build.

## What changed

- `ItchUpdates` (new): compares each installed itch game with the
uploads itch lists for it (`GET /games/{id}/uploads`), using what the
install already saves: `installVersion` (the upload's `updated_at`) plus
`gameId`, `downloadKeyId` and `uploadId` in `extra`.
- The installed upload is still listed and has a later `updated_at`: an
update.
- The installed upload is gone: the newest Windows upload is offered as
a replaced build.
- An install without an `uploadId` compares the newest Windows upload's
date.
- An unknown installed version, or no answer from itch, never reports an
update.
- The check walks installed games one at a time, one second apart, and
logs `itch.io: checked <n> installed games, <m> with updates`. It runs
when the itch tab opens (at most every 6 hours) and from the new button.
A game itch did not answer for keeps its last result. Results are cached
in the itch preferences until the update is installed or the account
signs out.
- Library: games with an update show an "Update" label on their tile and
row. A Check for updates button in the Library header (itch tab only)
runs the check on demand, shows progress, and ends with a toast:
"Updates available: N" or "Up to date".
- Game page: an Update button appears for installed itch games with an
update. A replaced build asks first and names the new file. A plain
newer build updates directly.
- Safe updates: an update never extracts into the live game. The archive
is unpacked into `.droiddeck-update` inside the game folder, and the new
build must contain an executable before any game file is touched. A
failed or cancelled download or extraction removes the staging folder
and leaves the game as it was. Then `ItchStaging.merge` moves the staged
files into place with renames inside one folder. A staged file replaces
the file with the same path, directories are merged, and anything the
new build doesn't contain (saves, settings, install record, launcher,
art) stays. The install record is marked updating just before the move,
so an interruption there is resumable.
- New strings `stores_itch_updates_found` and
`stores_itch_update_replaced` in all 33 locales. The button label, toast
and dialog buttons reuse existing strings.
- `docs/development/itch-updates.md` describes the feature.

## Notes

- One extra copy of the build's size must be free while an update runs.
- Files the developer removed in the new version stay in the game
folder, because everything the new build doesn't contain is kept.
- The final rename step is not an all-or-nothing swap. A crash or power
loss inside it leaves the game marked updating and resumable.
- The update reruns the normal install over the game's own folder and
storage location.

## Tests

- `ItchUpdatesTest` (Robolectric, like the other itch tests): newer,
same and older stamps, replaced upload falling back to the newest
Windows build, unknown installed version, no answer, an install without
an upload id, and the cache round trip.
- `ItchStagingTest`: new files replace old ones, other files stay, and a
file replacing a directory and the other way round.
- Not run on a device or compiled locally; relying on CI. The controller
focus behaviour of the new buttons is untested.
2026-10-28 09:36:19 -04:00
The412Banner 9d82df6981 fix(game-env): OpenGL games stop waiting for each screen refresh (#1715)
With no frame limit, AIO's Direct3D 7 (DirectDraw) test ran at exactly
144 fps on a 144 Hz panel: every frame waited one refresh (6.94 ms).
Wine asks for a swap interval of 1 on every OpenGL window, and WineD3D's
front-buffer drawing used by DirectDraw and Direct3D 1-7 never lowers
it; it only sets the interval in its present path. Mesa's Zink then
presents in FIFO.

droiddeck-game-env sets vblank_mode=0 for games launched through Proton
before the game's environment edits, so a value set in the launch
options or the editor wins and an editor Remove leaves Mesa's default.
Steam's own runs in compatdata/0 are unchanged. Steam's Frame Limit
still holds a game: gamescope's WSI layer presents every swapchain in
FIFO while its limiter is on.

On the AYANEO Pocket FIT, AIO Direct3D 7 went from 144 to 2,345 and
2,265 fps (1 % low 99 -> 620) with no limit; with the Quick Access
limit at 144 and 72 it held at 143.76 and 71.92.
2026-10-28 13:34:45 +00:00
Kurt Himebauch ddb8ed32cb test(proot): record expanded relay validation 2026-10-28 08:05:50 -04:00
Kurt Himebauch c62b9b5d1b perf(proot): expand and cache syscall relays 2026-10-28 07:17:53 -04:00
FX192 52e9810fbc Pick the newest Windows upload across types for replaced builds and let an update past its commit point finish 2026-10-28 09:00:36 +03:00
Kurt Himebauch 374d4783fc fix: avoid incompatible Turnip selection and explain Steam startup failures (#1380) 2026-10-27 22:29:40 -04:00
Kurt Himebauch eceda6e840 Add opt-in game debug logs and bounded log sharing (#1061) 2026-10-27 22:22:33 -04:00
FX192 e7907d0c74 Check itch updates per game from the game page instead of from the Library header 2026-10-28 01:08:33 +03:00
Kurt Himebauch 785d1bf1da perf(proot): enable syscall patching by default 2026-10-27 14:29:58 -04:00
Kurt Himebauch 4b230cbd0f perf(proot): add opt-in glibc syscall patching 2026-10-27 14:06:52 -04:00
FX192 c90ad997b1 Unpack itch updates into a staging folder before touching the game 2026-10-27 14:00:29 +03:00
FX192 8a16bd3b3b Detect itch.io updates and offer to install them
Installed itch games are compared with the uploads itch lists for them. A game with a newer upload shows an Update label in the Library and an Update button on its page, a replaced build asks first, and a Check for updates button in the itch Library header runs the check on demand.
2026-10-27 13:53:35 +03:00
FX1922andmaxjivi05 d905f03bf7 feat(itch): collections filter in the Library (#1597)
* Add itch.io collections to the Library

A Collections button on the Library's itch tab filters the library to one collection of the signed-in account. Its games that are not installed show in the Not installed section.

* Keep itch collections complete across failures, sign-out and empty libraries

A failed later page no longer publishes a partial collection, selecting a collection refreshes it, pagination runs until an empty page, signing out clears collection state, and the itch chip shows for a signed-in account with no owned games.

* Tie itch collection requests to the account that started them

Requests capture the account generation before reading the key and dedupe per generation, sign-out clears collections after the token is removed, and the collections load follows the itch sign-in state.

* Keep paging itch collections past a page of already seen games

* Run the itch collections tests under Robolectric like the other itch tests

* Let the Installed filter apply inside a collection and keep the grid for an empty itch account

* fix(itch): stop paging a collection when a page adds no new games

* Keep the collections icon on the itch collections tab when it opens

---------

Co-authored-by: maxjivi05 <maxjividen@gmail.com>
2026-10-26 18:15:24 -04:00
maxjivi05 c300342a29 Update MaliBridge to v0.5.4
v0.5.4 backs the exported memory with AHardwareBuffers on phones without a
dma-buf heap (older Mali such as the G68, G77 and G78) and fixes green and
red textures on the Dimensity 9000 series.
2026-10-26 14:08:24 -04:00
maxjivi05 fdc4ff607d Credit MaliBridge by ASEbrahim 2026-10-26 13:55:43 -04:00
maxjivi05 8df90c30dd fix(fex): read the fallback FEX version from droiddeck-fex-update
The pin moved there from droiddeck-steam-x64, so the client settings sheet
and its test read it from the new script. The x86-64 client notes now name
the updater and Components' FEX watcher.
2026-10-26 13:40:36 -04:00
Kurt Himebauch 5d945d7784 refactor: remove duplicate session frame limiter (#1616) 2026-10-26 11:25:31 -04:00
Danbro007 35b85952cf merge: 同步官方 main 并解决多语言资源冲突 2026-10-26 13:11:19 +08:00
Kurt Himebauch 3fd6b1f00d fix(ui): restore consistent focus glide transitions (#1588) 2026-10-25 23:20:57 -04:00
Thomas Lee 4922d309c8 perf: allocate only solved DD-FG flow levels (#1556)
The flow solver starts at flowFinest_, but its images, uniform buffers and descriptor sets were allocated for every pyramid level. Keep the seven-level indices and allocate flow resources only where the solver reads and writes them.

The default preset avoids three unused RGBA16F images, saving 38,707,200 bytes of pixel payload at 2560x1440 without changing the shaders or recorded GPU work. Add native allocation/dispatch regressions and document the resource budgets.
2026-10-25 19:21:40 -04:00
wallentx f4adbeb602 Merge upstream main and close diagnostic publication races 2026-10-25 18:15:44 -05:00
Kurt Himebauch 815b93db5f feat: profile PRoot costs and test fast-path libraries without an APK rebuild (#1528) 2026-10-25 16:49:35 -04:00
ianandKurt Himebauch ee3f32d31e feat: default storage for new installs, with USB/SDCARD/SSD detection (#1520)
* feat: choose default storage for new store installs

Store installs and the Epic Games Launcher can use internal storage, external storage, a folder picked through Choose folder..., or ask every time. The choice is stored in SessionPrefs and used by StoreInstallRoot; Storage > Default storage exposes it.

* fix: allow choosing any folder for default storage

The default storage choice can now hold an arbitrary folder path, not just internal or app-specific external storage. StoreInstallRoot resolves and validates picked folders, keeps a disconnected choice instead of silently falling back, and the folder picker stays available from Storage.

* fix: keep the Epic install folder when external storage is away

* test: use a unique missing folder for the Epic storage check

* fix: detect USB SSDs that the storage manager hides

List storage volumes that have no UUID instead of dropping them, keep volumes that are present but not yet mounted, and fall back to parsing /proc/mounts so USB OTG drives appear even when Android does not expose them as storage volumes or app directories. Prefer the system's volume description for removable drives. (Raw removable drives as move targets, the folder-picker fallback, and the unreadable-drive guidance follow with the move stack.)

* fix: compare mount paths as strings in StorageRootsTest

* fix: never drop a present storage volume from the drive list

Key volumes by description or index when they report no UUID and no directory, keep nofs/unmountable states so a plugged-in drive stays visible with guidance, only attach app directories on strict UUID matches, and accept any fstype under vold-managed locations plus usb-ish mounts directly under /mnt.

* fix: probe folder writes with a fresh temp file and keep Epic's folder unset while external storage is away

---------

Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
2026-10-25 16:38:06 -04:00
ae193e9b8d fix: find GOG Galaxy SDK cloud saves when remote-config lists no locations (#970)
* fix: find GOG Galaxy SDK cloud saves when remote-config lists no locations

cloudStorage.enabled with an empty locations list is Galaxy SDK IStorage
under __default. DroidDeck treated that as no cloud.

Use the SDK folder, strip/prefix the __default namespace, and inflate gzip
bodies GOG stores on download. An empty cached template is fetched again
so devices that already recorded "no cloud" recover.

* fix: keep an empty GOG namespace, expire cached none, close gzip

An explicit empty cloud_namespace is Auto Cloud and must not become
__default just because the path template looks like Galaxy storage.
A cached none is retried after a day so a later enablement is picked up.
GZIPInputStream is closed after a compressed download.

* fix: peel GOG cloud gzip only when the service or PUT compressor says so

GET asks for identity encoding so the body is what GOG stored. A
Content-Encoding of gzip is inflated once. Otherwise a gzip body is
inflated only when it round-trips as GOG's PUT gzip (deflate 6, mtime 0),
so a gzip-format save is not rewritten.

* Reject corrupt GOG cloud downloads

---------

Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
2026-10-25 13:20:29 -04:00
7ef52865d2 fix: place GOG cloud saves at the location root (#1178)
GOG lists every cloud object as <location name>/<path>. The location name
is not part of the local folder, so Silksong's saves/user1.dat belongs at
default/user1.dat, not default/saves/user1.dat. Strip the segment for local
paths and add it back for HEAD/GET/PUT, cache the location name, and migrate
files written by the old layout without touching files the game wrote.

Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
2026-10-25 12:58:54 -04:00