Skip to content

Commit 323a354

Browse files
author
Gengar
committed
Release v0.9.4 Beta
1 parent 41b038b commit 323a354

85 files changed

Lines changed: 2344 additions & 823 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,97 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
66

77
---
88

9+
## [0.9.4] Beta — 2026-02-20
10+
11+
### Added
12+
- Global search activated (Home screen, Y button) — cross-system search with region flags and tag badges
13+
- Local-only mode — consoles without a provider show locally scanned ROM files with a banner hint
14+
- FTP download progress — real-time per-chunk progress reporting instead of staying at 0%
15+
- Download inactivity watchdog — 60-second stall detection with clear error message
16+
- Gamepad key fix — intercepts mismatched logical keys on key-up/repeat for certain gamepad drivers (AYN Thor etc.)
17+
- Provider type badge on version cards (RomM, SMB, FTP, WEB)
18+
- Download overlay sectioned list (Downloading / Queued / Complete headers) with ID-stable focus and auto-scroll
19+
- Download overlay auto-close when queue empties
20+
- SMB domain auth field
21+
- `ProviderConfig.validate()` and `shortLabel` helpers
22+
- `showConsoleNotification()` — themed floating SnackBar used app-wide
23+
- `getUserFriendlyError()` — maps raw exceptions to readable messages
24+
25+
### Improved
26+
- RomM Config screen reworked — focus-aware glow borders, D-pad navigable fields, connection test as toast, per-console sync status with bulk "Update stale" action
27+
- Settings screen — RomM Server item re-enabled, concurrent downloads widget with chevrons, Reset App moved to HUD X-button
28+
- Home view empty-state shows HUD bar with Settings/Exit actions; loading state is a black screen (no flash)
29+
- Game detail loading shows game name + system-colored spinner
30+
- Game list header shows target folder path and local-only banner
31+
- Game grid context-sensitive empty states (search miss, filter miss, local-only empty, connection error)
32+
- Platform icons compressed (~70% smaller file sizes)
33+
- All action shortcuts use `includeRepeats: false` (no more repeated fires on button hold; D-pad retains repeats)
34+
- `OverlayFocusScope` / `DialogFocusScope``_hasClaimed` flag prevents double-release of overlay priority
35+
- RomM cover fallback chain (CDN → small → large → first screenshot)
36+
- `UnifiedGameService` provider calls wrapped in 30s timeout
37+
- `RommApiService` / `WebProvider` — connect 15s / receive 30s timeouts
38+
- README updated with supported systems table and Building from Source section
39+
40+
### Fixed
41+
- **Zip Slip vulnerability** — Android ZIP extraction validates canonical path before writing
42+
- **Path traversal**`_safePath` uses `p.basename()` in both `DownloadService` and `RomManager`
43+
- **Atomic config write**`ConfigStorageService.saveConfig` writes to `.tmp` then renames
44+
- **DB init race condition**`DatabaseService` uses single `static Future<Database>?` guard
45+
- **AudioManager BGM reinit loop**`_hasAttemptedReinit` flag prevents recursive retry
46+
- **Retry timer leak**`DownloadQueueManager` tracks `Timer` instances, cancels on dispose
47+
- **FailedUrlsCache unbounded growth** — replaced `Set<String>` with `Map<String, DateTime>` + 5min TTL
48+
- **Delete dialog defaults to CANCEL** (selection index 1, not 0)
49+
- FTP download cancel now disconnects the FTP session
50+
- `totalBytes` clamping — treats `<= 0` as unknown so progress bar works correctly
51+
- Global search `providerConfig` propagation — results open with correct provider
52+
- `RepoManager` Dio connection leak — `dio.close()` in `finally` block
53+
- `GameDetailController` / `GameListController` — disposed-guard prevents post-dispose `notifyListeners()`
54+
- Global search Escape/B from text field moves focus to results instead of closing
55+
- Search overlay left/right arrows no longer leak to grid while editing
56+
57+
### Internal
58+
- `DownloadItem` fully immutable (all fields `final`), serializes `systemId` + `providerConfig`
59+
- `DatabaseService` schema v3 (adds `provider_config` column with migration)
60+
- `gamepad_key_fix.dart` installed at app startup via `main()`
61+
- Download overlay refactored into `_buildSectionedList` / `_buildCard` / `_buildSectionHeader` helpers
62+
63+
---
64+
65+
## [0.9.3] Beta — 2026-02-19
66+
67+
### Added
68+
- RomM onboarding wizard — guided first-run setup with connection test, platform auto-discovery, and folder assignment
69+
- Game list filter overlay (X button) — filter ROMs by region and language with per-console persistence
70+
- Global search overlay infrastructure (cross-system search, wired but trigger disabled until v0.9.4)
71+
- Android foreground service — downloads continue in background with a persistent notification
72+
- Download queue persistence — queued and errored downloads survive app restarts
73+
- Automatic download retry with exponential backoff (3 attempts, 5s/15s/45s)
74+
- Configurable concurrent download limit (1–3) in Settings
75+
- RomM config screen for editing server URL, API key, and credentials
76+
- Local folder scanner and fuzzy system-ID matcher for onboarding folder assignment
77+
78+
### Improved
79+
- RomM ROM list fetching is now paginated (pages of 500, no more timeouts on large libraries)
80+
- RomM platform API accepts both `items` and `results` response keys (multi-version support)
81+
- Download overlay action button is context-aware (Cancel / Retry / Clear)
82+
- Filter and search are mutually exclusive in the game list
83+
- Onboarding console setup no longer requires a provider (target folder only is valid)
84+
85+
### Fixed
86+
- RomM ROM fetch used a malformed `platform_ids` query parameter
87+
- Download overlay showed "Retry" for completed items
88+
- Game grid passed unfiltered variant lists when filters were active
89+
- Home view carousel index jumped after system list refresh
90+
91+
### Internal
92+
- Added `flutter_foreground_task` dependency
93+
- Android manifest: foreground service, notification, and battery optimization permissions
94+
- `FilterState` / `ActiveFilters` model extracted; `RommSetupState` added to onboarding controller
95+
- `DownloadQueueManager` now takes `StorageService`; `GameDetailController` takes `DownloadQueueManager`
96+
- `local_folder_matcher_test.dart` unit tests
97+
98+
---
99+
9100
## [0.9.2] Beta — 2026-02-18
10101

11102
### Added

README.md

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A premium console-style game manager for Android — built for handhelds, contro
77
<img src="screenshots/console_list.png" width="600" alt="R-Shop Console Overview" />
88
</p>
99

10-
![Version](https://img.shields.io/badge/version-0.9.3_Beta-blue)
10+
![Version](https://img.shields.io/badge/version-0.9.4_Beta-blue)
1111
![License](https://img.shields.io/badge/license-MIT-green)
1212
![Platform](https://img.shields.io/badge/platform-Android-brightgreen)
1313
![Status](https://img.shields.io/badge/status-Public_Beta-orange)
@@ -20,31 +20,33 @@ R-Shop is a Flutter-based Android app that gives you a **console-like experience
2020

2121
It's built with love for **Android gaming handhelds** (Retroid, Odin, AYN, Anbernic…) but works beautifully on any Android device with touch or controller input.
2222

23-
> **⚠️ Important:** R-Shop is a neutral browser. It does **not** host, distribute, or link to any copyrighted content. Users must provide their own source URLs.
23+
> **Important:** R-Shop is a neutral browser. It does **not** host, distribute, or link to any copyrighted content. Users must provide their own source URLs.
2424
2525
---
2626

27-
## ✨ Features
28-
29-
- 🎮 **Built for Controllers** — Full D-pad/analog navigation with auto-scroll, haptic feedback on every interaction, satisfying click sounds. Feels like a real console UI, not a phone app.
30-
- 🌐 **Multi-Source Providers** — Each console can pull from Web directories, SMB shares, FTP servers, or a RomM instance. Mix and match per system.
31-
- 🔗 **RomM Integration** — Connect to your RomM server and let R-Shop automatically match platforms via IGDB.
32-
- 📥 **Smart Download Queue** — Queue up your entire library. Downloads run with live progress and auto-extraction (ZIP/7z).
33-
-**Installed Indicator** — Already downloaded? A glowing LED strip on each game card tells you at a glance.
34-
- 🖼️ **Automatic Box Art** — Every game gets its cover art fetched and cached automatically via [libretro-thumbnails](https://github.com/libretro-thumbnails).
35-
-**Aggressive Caching** — Optimized for huge libraries (5000+ items). After the first load, the app feels instant even without internet.
36-
- 🔍 **Instant Search** — Find any game across all systems in milliseconds.
37-
- 🗂️ **27 Systems Supported** — Nintendo (NES to 3DS), Sony (PS1–PSP), SEGA (Master System to Dreamcast), and more.
38-
- 🔍 **Global Search** — Find any game across all cached systems instantly from the home screen.
39-
- 🎚️ **Region & Language Filters** — Filter game lists by region or language, with per-system persistence.
40-
- 📡 **Global RomM Connection** — Configure your RomM server once in settings and auto-fill credentials for every console.
41-
- ⚙️ **Configurable Downloads** — Adjust max concurrent downloads (1–3) and queue is persisted across app restarts.
42-
- 📱 **Hybrid Input** — Seamlessly switch between touchscreen and gamepad. Both feel native.
43-
- 💾 **Config Import/Export** — Save your entire setup as JSON and restore it on any device.
27+
## Features
28+
29+
**Controller-first UI** — Full D-pad and analog stick navigation with auto-scroll, haptic feedback, and click sounds. Every interaction feels like a real console interface, not a phone app. Touch and gamepad input work side by side; both feel native.
30+
31+
**Multi-source providers** — Each console can pull from Web directories, SMB shares, FTP servers, or a RomM instance. Mix and match per system, configure once globally or per console.
32+
33+
**RomM integration** — Connect to your RomM server and let R-Shop automatically match platforms via IGDB. Configure the connection once in settings and auto-fill credentials for every console.
34+
35+
**Smart download queue** — Queue up your entire library. Downloads run with live progress, auto-extraction (ZIP/7z), and the queue persists across app restarts. Adjust concurrent downloads (1–3) in settings.
36+
37+
**Automatic box art** — Every game gets its cover art fetched and cached automatically via [libretro-thumbnails](https://github.com/libretro-thumbnails). An installed indicator (glowing LED strip) on each game card tells you at a glance what you've already downloaded.
38+
39+
**Aggressive caching** — Optimized for huge libraries (5000+ items). After the first load, the app feels instant even without internet.
40+
41+
**Global search** — Find any game across all cached systems instantly from the home screen.
42+
43+
**Region and language filters** — Filter game lists by region or language, with per-system persistence.
44+
45+
**Config import/export** — Save your entire setup as JSON and restore it on any device.
4446

4547
---
4648

47-
## 📸 Screenshots
49+
## Screenshots
4850

4951
<p align="center">
5052
<img src="screenshots/console_list.png" width="250" alt="Console Overview" />
@@ -54,13 +56,32 @@ It's built with love for **Android gaming handhelds** (Retroid, Odin, AYN, Anber
5456

5557
---
5658

57-
## 📲 Installation & Updates
59+
## Supported Systems (27)
60+
61+
| Nintendo | Sony | SEGA | Other |
62+
|----------|------|------|-------|
63+
| NES | PlayStation | Master System | Neo Geo Pocket Color |
64+
| Super Nintendo | PlayStation 2 | Mega Drive | Arcade |
65+
| Nintendo 64 | PlayStation 3 | Game Gear | Xbox |
66+
| GameCube | PlayStation 4 | Saturn | Xbox 360 |
67+
| Wii | PSP | Dreamcast | |
68+
| Wii U | PS Vita | | |
69+
| Switch | | | |
70+
| Game Boy | | | |
71+
| Game Boy Color | | | |
72+
| Game Boy Advance | | | |
73+
| Nintendo DS | | | |
74+
| Nintendo 3DS | | | |
75+
76+
---
77+
78+
## Installation & Updates
5879

5980
### Recommended: Obtainium
6081
The best way to install and keep R-Shop updated is via **[Obtainium](https://github.com/ImranR98/Obtainium)**.
6182
1. Install Obtainium.
6283
2. Add this repository URL.
63-
3. Enjoy automatic updates for every new Beta release!
84+
3. Enjoy automatic updates for every new Beta release.
6485

6586
### Manual APK
6687
1. Go to the [**Releases**](../../releases) page.
@@ -69,44 +90,56 @@ The best way to install and keep R-Shop updated is via **[Obtainium](https://git
6990

7091
---
7192

72-
## 🕹️ How to Use
93+
## Getting Started
94+
95+
1. **Launch R-Shop.** On first start, the onboarding wizard walks you through setup.
96+
2. **Configure your consoles.** For each system you want, pick a source type (Web directory, SMB share, FTP server, or RomM) and enter the connection details.
97+
3. **Choose a download folder.** Select where games should be stored per console (e.g., your ROMs folder).
98+
4. **Browse and download.** The app handles the rest — box art, caching, and organization are automatic.
7399

74-
1. **Open the app.**
75-
2. **Onboarding Wizard:** On first launch, the setup wizard walks you through configuring each console. For every system you want, pick a source type (Web directory, SMB share, FTP server, or RomM) and enter the connection details.
76-
3. **Pick a Folder:** Choose where games should be stored per console (e.g., your ROMs folder).
77-
4. **Browse & Download:** The app handles the rest. You can edit your console configuration later in **Settings > Config Editor**.
100+
You can edit your console configuration at any time in **Settings > Config Editor**.
78101

79102
---
80103

81-
## 🐛 Known Issues (Beta)
104+
## Building from Source
82105

83-
* **Initial Cache:** Scrolling through a list of 2000+ games for the very first time might show placeholders briefly while the cache builds up.
106+
```bash
107+
git clone https://github.com/AverageConsumer/R-Shop.git
108+
cd R-Shop
109+
flutter pub get
110+
flutter build apk --release
111+
```
112+
113+
The built APK will be at `build/app/outputs/flutter-apk/app-release.apk`.
84114

85115
---
86116

87-
## 🤝 Contributing
117+
## Known Issues (Beta)
118+
119+
* **Initial cache:** Scrolling through a list of 2000+ games for the very first time might show placeholders briefly while the cache builds up.
120+
121+
---
88122

89-
Contributions are welcome and **greatly appreciated**! This project is maintained by a solo dev who honestly can't even code that well — so if you're a Flutter wizard, your help would be legendary. 🧙
123+
## Contributing
90124

91-
See **[CONTRIBUTING.md](CONTRIBUTING.md)** for details.
125+
Contributions are welcome! See **[CONTRIBUTING.md](CONTRIBUTING.md)** for details.
92126

93127
---
94128

95-
## 📜 License
129+
## License
96130

97131
This project is licensed under the **MIT License** — see the [LICENSE](LICENSE) file for details.
98132

99133
---
100134

101-
## 🙏 Acknowledgments
135+
## Acknowledgments
102136

103-
- **[libretro-thumbnails](https://github.com/libretro-thumbnails)** — For the massive database of game covers.
104-
- **viik4 / iisu** — For the clean platform icons used in the UI.
105-
- **Flutter** — The framework powering this app.
106-
- **The SBCGaming Community** — For the inspiration! 🕹️
137+
- **[libretro-thumbnails](https://github.com/libretro-thumbnails)** — Game cover database
138+
- **viik4 / iisu** — Platform icons
139+
- **The SBCGaming Community** — Inspiration
107140

108141
---
109142

110-
## ⚠️ Disclaimer
143+
## Disclaimer
111144

112145
R-Shop is a tool for managing your personal game library. It does **not** include, distribute, or endorse piracy of any kind. Users are solely responsible for the content they access. Always respect copyright laws in your jurisdiction.

android/app/src/main/kotlin/com/example/r_shop/MainActivity.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class MainActivity : FlutterActivity() {
6262
val zis = ZipInputStream(BufferedInputStream(fis))
6363
var entry = zis.nextEntry
6464
while (entry != null) {
65-
totalBytes += entry.size
65+
if (entry.size > 0) totalBytes += entry.size
6666
entry = zis.nextEntry
6767
}
6868
zis.close()
@@ -73,9 +73,18 @@ class MainActivity : FlutterActivity() {
7373
val zis = ZipInputStream(BufferedInputStream(fis))
7474
var entry = zis.nextEntry
7575

76+
val canonicalTarget = File(targetPath).canonicalPath
77+
7678
while (entry != null) {
7779
val file = File(targetPath, entry.name)
7880

81+
// Zip Slip protection: reject entries that escape the target directory
82+
if (!file.canonicalPath.startsWith(canonicalTarget + File.separator) &&
83+
file.canonicalPath != canonicalTarget) {
84+
entry = zis.nextEntry
85+
continue
86+
}
87+
7988
if (entry.isDirectory) {
8089
file.mkdirs()
8190
} else {

assets/platform_icons/Android.png

-546 KB
Loading

assets/platform_icons/Arcade.png

-597 KB
Loading
-552 KB
Loading

assets/platform_icons/GAMECUBE.png

-622 KB
Loading
-924 KB
Loading

assets/platform_icons/GENESIS.png

-664 KB
Loading

assets/platform_icons/Game_Boy.png

-550 KB
Loading

0 commit comments

Comments
 (0)