Skip to content

commit 3850059

abduznik edited this page May 23, 2026 · 1 revision

feat: more-emulators v0.1.0 (#5)

Commit: 38500598998820b876088392ccb52630d95e3e38

Author: abduznik

Date: 2026-03-26

Message

  • perf: cache images, fix extraction service, emulator downloads in download tab

  • feat: extraction service, emulator downloads in download tab, image caching, performance optimizations, emulator conflict resolver, melonDS, Flycast, platform guards

  • feat: conflict resolver grouping, Flycast asset fix and extended platform support

  • feat: PPSSPP, mGBA, MAME standalones, RetroArch auto core install, MAME self-extracting exe support

  • docs: update agent.md with new emulators, extraction service, conflict resolver, performance improvements

  • feat: save strategies for DuckStation/melonDS/mGBA/PPSSPP/Cemu, fix syncMode scope to RetroArch only, emulator path overrides, Cemu save as zip

  • feat: save strategies for DuckStation/melonDS/mGBA/PPSSPP/Cemu, emulator path overrides, conflict resolver drives save sync, launch status snackbars, Cemu save zip fix, backupSave chain fix, lint cleanup, agent.md update

  • fix: Cemu save restore path extraction, preserve full zip structure

  • feat: RetroArch auto core download, MissingRetroArchCoreException, downloadCore from buildbot

  • refactor: split settings_screen into display and emulators sections, fix analyze warnings

  • fix: PSP save sync, strategy preferences load before first sync, SAVEDATA zipped as single file

  • fix: PSP saves zip correctly, directory exists check for File vs Directory, preferences await on startup

  • feat: auto-sync saves on game close, fix PSP SAVEDATA restore path, fix cold start null strategy on first launch

  • fix: melonDS launch pipe drain fix, PPSSPP save restore path, auto-sync on game close, PSP empty folder check

  • fix: analyze warnings, BuildContext async gaps, unused imports

  • fix: ZIP magic byte detection for extensionless multi-file ROMs, extraction on background isolate, remove debug prints

Why: Adds a new feature or capability to the application.

Files Changed

README.md                                          |  92 ++--
 agent.md                                           | 285 +++++-----
 lib/core/downloader/download_service.dart          |  39 +-
 lib/core/emulator/emulator_download_service.dart   |  27 +-
 lib/core/emulator/emulator_registry_data.dart      |  78 ++-
 lib/core/emulator/emulator_strategy.dart           |   4 +-
 lib/core/emulator/strategies/azahar_strategy.dart  |   9 +
 lib/core/emulator/strategies/cemu_strategy.dart    |   9 +
 lib/core/emulator/strategies/dolphin_strategy.dart |   7 +
 .../emulator/strategies/duckstation_strategy.dart  |   9 +
 lib/core/emulator/strategies/eden_strategy.dart    |   7 +
 lib/core/emulator/strategies/flycast_strategy.dart |  49 ++
 lib/core/emulator/strategies/mame_strategy.dart    |  69 +++
 lib/core/emulator/strategies/melonds_strategy.dart |  52 ++
 lib/core/emulator/strategies/mgba_strategy.dart    |  49 ++
 lib/core/emulator/strategies/pcsx2_strategy.dart   |  11 +
 lib/core/emulator/strategies/ppsspp_strategy.dart  |  49 ++
 .../emulator/strategies/retroarch_strategy.dart    |  93 +++-
 lib/core/emulator/strategies/rpcs3_strategy.dart   |   9 +
 lib/core/emulator/strategies/xemu_strategy.dart    |   9 +
 lib/core/emulator/strategies/xenia_strategy.dart   |   9 +
 lib/core/emulator/strategy_registry.dart           | 119 ++++-
 lib/core/extraction/extraction_service.dart        |  70 +++
 lib/core/save/save_strategy.dart                   |   2 +-
 lib/core/save/save_sync_service.dart               |  72 ++-
 lib/core/save/strategies/cemu_save_strategy.dart   |  69 +++
 .../save/strategies/duckstation_save_strategy.dart | 122 +++++
 .../save/strategies/melonds_save_strategy.dart     |  68 +++
 lib/core/save/strategies/mgba_save_strategy.dart   |  68 +++
 lib/core/save/strategies/pcsx2_save_strategy.dart  |  42 +-
 lib/core/save/strategies/ppsspp_save_strategy.dart | 108 ++++
 .../save/strategies/retroarch_save_strategy.dart   | 110 ++--
 lib/core/storage/directory_service.dart            |  40 +-
 lib/providers/download_provider.dart               |  31 +-
 lib/providers/library_provider.dart                |   2 -
 lib/providers/romm_provider.dart                   |  11 +-
 lib/ui/screens/library_screen.dart                 | 571 ++++++++++-----------
 lib/ui/screens/library_skeleton.dart               |  91 ++++
 lib/ui/screens/settings_display_section.dart       | 165 ++++++
 lib/ui/screens/settings_emulators_section.dart     | 158 ++++++
 lib/ui/screens/settings_screen.dart                | 344 +++----------
 lib/ui/widgets/game_card.dart                      | 179 ++++---
 macos/Flutter/GeneratedPluginRegistrant.swift      |   2 +
 pubspec.lock                                       | 112 ++++
 pubspec.yaml                                       |   1 +
 45 files changed, 2582 insertions(+), 940 deletions(-)
  • README.md
  • agent.md
  • lib/core/downloader/download_service.dart
  • lib/core/emulator/emulator_download_service.dart
  • lib/core/emulator/emulator_registry_data.dart
  • lib/core/emulator/emulator_strategy.dart
  • lib/core/emulator/strategies/azahar_strategy.dart
  • lib/core/emulator/strategies/cemu_strategy.dart
  • lib/core/emulator/strategies/dolphin_strategy.dart
  • lib/core/emulator/strategies/duckstation_strategy.dart
  • lib/core/emulator/strategies/eden_strategy.dart
  • lib/core/emulator/strategies/flycast_strategy.dart
  • lib/core/emulator/strategies/mame_strategy.dart
  • lib/core/emulator/strategies/melonds_strategy.dart
  • lib/core/emulator/strategies/mgba_strategy.dart
  • lib/core/emulator/strategies/pcsx2_strategy.dart
  • lib/core/emulator/strategies/ppsspp_strategy.dart
  • lib/core/emulator/strategies/retroarch_strategy.dart
  • lib/core/emulator/strategies/rpcs3_strategy.dart
  • lib/core/emulator/strategies/xemu_strategy.dart
  • lib/core/emulator/strategies/xenia_strategy.dart
  • lib/core/emulator/strategy_registry.dart
  • lib/core/extraction/extraction_service.dart
  • lib/core/save/save_strategy.dart
  • lib/core/save/save_sync_service.dart
  • lib/core/save/strategies/cemu_save_strategy.dart
  • lib/core/save/strategies/duckstation_save_strategy.dart
  • lib/core/save/strategies/melonds_save_strategy.dart
  • lib/core/save/strategies/mgba_save_strategy.dart
  • lib/core/save/strategies/pcsx2_save_strategy.dart
  • lib/core/save/strategies/ppsspp_save_strategy.dart
  • lib/core/save/strategies/retroarch_save_strategy.dart
  • lib/core/storage/directory_service.dart
  • lib/providers/download_provider.dart
  • lib/providers/library_provider.dart
  • lib/providers/romm_provider.dart
  • lib/ui/screens/library_screen.dart
  • lib/ui/screens/library_skeleton.dart
  • lib/ui/screens/settings_display_section.dart
  • lib/ui/screens/settings_emulators_section.dart
  • lib/ui/screens/settings_screen.dart
  • lib/ui/widgets/game_card.dart
  • macos/Flutter/GeneratedPluginRegistrant.swift
  • pubspec.lock
  • pubspec.yaml

Diff

diff --git a/README.md b/README.md
index c8a824d..0d041c4 100644
--- a/README.md
+++ b/README.md
@@ -2,32 +2,46 @@
 
 A cross-platform Flutter app for browsing your RomM library, downloading ROMs, and launching games directly in emulators—all from one intuitive interface.
 
-## What's New in 0.0.2
+## What's New in 0.1.0
 
-### Full Windows Native Game Support
-- Download Windows games (.zip and .7z) with automatic extraction
-- Auto-detection of game executables in extracted folders
-- Manual exe and save path configuration via long-press on any Windows game card
-- Crash detection on launch — if a game exits immediately, a clear error message explains likely causes (missing DirectX, Visual C++ redistributables, etc.)
-- Save sync for Windows games via PCGamingWiki automatic save path detection, with manual override support
-- All exe and save path overrides persist across app restarts
+### New Emulator Support
+- **Flycast** (Dreamcast, Naomi, Atomiswave)
+- **melonDS** (Nintendo DS)
+- **PPSSPP** standalone (PlayStation Portable)
+- **mGBA** standalone (Game Boy Advance, GBC, GB)
+- **MAME** (Arcade)
 
 ### Expanded Save Sync
-- **PCSX2 (PS2)**: Syncs memory cards (Mcd001.ps2, Mcd002.ps2) and save states
-- **RPCS3 (PS3)**: Syncs save data folders by title ID
-- **Xenia Canary (Xbox 360)**: Syncs content save folders by title ID
-- **Windows games**: Full save directory packaging into zip for upload, auto-extraction on restore
-- All multi-file save strategies package saves as a single zip for clean cloud storage
-
-### 7-Zip Support
-- Bundled 7zr.exe for .7z extraction — no manual installation required
-- Automatically extracted to AppData on first run
-- Full .7z support for Windows game downloads
-
-### Quality of Life
-- Launch error messages now display for 8 seconds for better readability
-- Stale exe overrides (pointing to moved/deleted files) are automatically discarded and re-detected
-- ROM detection improved for Windows games — returns game folder instead of individual file
+- **DuckStation** (PS1): Memory card save sync
+- **melonDS** (NDS): Save file sync
+- **mGBA** (GBA/GBC/GB): Save file sync
+- **PPSSPP** (PSP): Entire SAVEDATA directory with automatic folder structure restoration
+- **Cemu** (Wii U): Full mlc01/usr/save/ directory packaging and restore
+- All new strategies support both push (upload) and pull (download) operations
+
+### Auto-Sync on Game Close
+- Saves automatically push to cloud when you close the emulator
+- Available for all supported emulators via `launchWithHandle()`
+- Seamless background operation—no manual intervention needed
+- Works alongside pre-launch save push and manual sync
+
+### Smart Emulator Management
+- **Conflict Resolver**: When multiple emulators support the same platform, choose which one to use in Settings
+- **Path Overrides**: Point Freegosy to existing emulator installations via Settings folder icon (no need to re-download)
+- **Preference Persistence**: Your emulator choices and custom paths are saved across app restarts
+
+### RetroArch Core Auto-Download
+- Missing cores are detected at launch time
+- User is offered to auto-download and install the required core
+- Full core list support via RetroArch buildbot
+
+### Performance & UX Enhancements
+- Library grid renders without blocking—0% CPU while idle
+- Cached images (memCache) for fast card rendering
+- Optimized grid with extended cache extent and dual-stem state file matching for RetroArch
+- **Library Display Presets**: Windows, Steam Deck, Cozy, Compact—quick-switch your layout
+- **Launch Status Snackbars**: Real-time feedback showing "Pushing saves / Syncing saves / Launching / Auto-syncing" at each stage
+- **Downloads Tab**: Emulator downloads now show alongside game downloads with progress tracking
 
 ## Currently Working
 
@@ -45,39 +59,47 @@ A cross-platform Flutter app for browsing your RomM library, downloading ROMs, a
   - Cemu (Wii U)
   - Xemu (Xbox)
   - Xenia Canary (Xbox 360)
+  - Flycast (Dreamcast, Naomi, Atomiswave)
+  - melonDS (Nintendo DS)
+  - PPSSPP (PlayStation Portable)
+  - mGBA (Game Boy Advance/Color/Game Boy)
+  - MAME (Arcade)
   - Windows Native (PC games)
 - **Emulator Downloads**: One-tap emulator download and installation from Settings
-- **Save Sync**: Two-way save synchronization with RomM cloud for:
+- **Save Sync**: Bidirectional save synchronization with RomM cloud for:
   - RetroArch (all supported platforms)
   - Dolphin (GameCube/Wii)
   - Eden (Switch)
   - PCSX2 (PS2)
   - RPCS3 (PS3)
+  - DuckStation (PS1)
+  - melonDS (NDS)
+  - mGBA (GBA/GBC/GB)
+  - PPSSPP (PSP)
+  - Cemu (Wii U)
   - Xenia Canary (Xbox 360)
   - Windows native games (via PCGamingWiki auto-detection)
 
 ## Roadmap
 
-### Near Term
+### In Progress
+- **Linux/macOS support** — Platform detection and path resolution already structured; adding platform-specific executable paths and environment variable resolution
 
-- **Cemu save sync** — Wii U saves at mlc01/usr/save/
-- **Azahar save sync** — 3DS saves via SDMC folder
-- **MelonDS support** — better NDS emulation alternative
-- **macOS & Linux support** — platform detection and path resolution already structured for future expansion
+### Near Term
+- **Auto-update emulators** — Keep emulators fresh without manual downloads
+- **Android support** — Deep links to app stores for Play Store/Epic Games/etc.
+- **Recently played / play time tracking** — See your gaming stats at a glance
 
 ### End-Game Features
-
-- Automatic emulator updates
 - Custom ROM platform tagging
 - Mobile companion app for on-the-go library browsing
 
 ### Cross-Platform Vision
-
-Freegosy is designed as a truly cross-platform experience. The codebase is structured to support Windows, macOS, and Linux with platform-specific code isolated behind strategy patterns and service abstractions.
+Freegosy is designed as a truly cross-platform experience. The codebase is structured to support Windows, macOS, Linux, and Android with platform-specific code isolated behind strategy patterns and service abstractions.
 
 ## Status
 
-Actively under development. Release 0.0.2 focuses on Windows game support and expanded save sync coverage.
+Actively under development. Release 0.1.0 brings major emulator expansion, comprehensive save sync, and auto-sync on game close.
 
 ## About RomM
 
@@ -85,4 +107,4 @@ Freegosy is built to complement [RomM](https://github.com/rommapp/romm), a moder
 
 ## Contributing
 
-Check out `agent.md` for the full file map, coding rules, and contracts for adding new emulators, save strategies, or features.
\ No newline at end of file
+Check out `agent.md` for the full file map, coding rules, and contracts for adding new emulators, save strategies, or features.
diff --git a/agent.md b/agent.md
index de0be33..183080a 100644
--- a/agent.md
+++ b/agent.md
@@ -1,135 +1,152 @@
-# Freegosy — Agent Map
-> This file is the source of truth for all LLM agents (Claude, Gemini) working on this codebase.
-> Read this before touching any file. Update this file if you create or split any file.
-
-## Project Overview
-Freegosy is a cross-platform Flutter app for browsing a RomM library, downloading ROMs via HTTP, and launching emulators. Built with Riverpod for state management.
-
-## Rules (MANDATORY)
-- No file exceeds 600 lines. If adding code would exceed this, split the file first and update this map.
-- All RomM API calls go through `romm_service.dart` only. Never call the API directly from UI or providers.
-- All emulator logic goes through the strategy pattern. Never hardcode emulator behavior in UI.
-- New emulator = new file in `core/emulator/strategies/`, register in `strategy_registry.dart` only.
-- New save strategy = new file in `core/save/strategies/`, register in `save_sync_service.dart` only.
-- New screen = new file in `ui/screens/`.
-- New reusable widget = new file in `ui/widgets/`.
-- Providers are thin — they call services, they do not contain business logic.
-- Never use `Platform.environment` or `Platform.isWindows` directly — causes conflicts with flutter/foundation.dart. Use `Process.run('cmd', ['/c', 'echo %APPDATA%'])` for env vars, and `defaultTargetPlatform == TargetPlatform.windows` for platform checks.
-
-## File Map
-
-### Entry Points
-- `lib/main.dart` — App entry point. Initializes Riverpod ProviderScope. Calls app.dart.
-- `lib/app.dart` — MaterialApp setup, theme, initial route, navigation shell.
-
-### Core — RomM
-- `lib/core/romm/romm_service.dart` — All RomM HTTP calls (Dio). Methods: getPlatforms(), getGames(), getSaves(), uploadSave(), getLatestSave(), downloadSave(). Returns typed models.
-- `lib/core/romm/romm_models.dart` — Data models: Game, Platform, SaveFile, RomMConfig.
-
-### Core — Save Sync
-- `lib/core/save/save_strategy.dart` — Abstract base class SaveStrategy. Methods: getSaveDir(), getSaveFiles(), restoreSave(). Helpers: backupSave() (3-version rotation), getRomStem().
-- `lib/core/save/save_sync_service.dart` — SaveSyncService. Methods: pushSaves(), pullSave(), getStrategyForSlug(). Wires all strategies to RommService. Exposes windowsSaveStrategy for external access.
-- `lib/core/save/strategies/retroarch_save_strategy.dart` — RetroArch save strategy (GBA/GBC/GB/SNES/NES/N64/NDS/PSX/PSP/Dreamcast/Megadrive). Reads saves/{coreName}/{stem}.srm and .state.auto.
-- `lib/core/save/strategies/dolphin_save_strategy.dart` — Dolphin save strategy (GC/Wii). Reads User/GC/{region}/Card A/*.gci.
-- `lib/core/save/strategies/eden_save_strategy.dart` — Eden/Switch save strategy. Resolves title ID via filename regex, XCI header parse, or save folder scan. Zips save folder for upload.
-- `lib/core/save/strategies/windows_save_strategy.dart` — Windows native game save strategy. Uses PCGamingWiki API for auto-detection, supports manual override. Zips entire save directory for upload, extracts on restore. Persists overrides via SharedPreferences (prefix `win_save_`).
-- `lib/core/save/strategies/pcsx2_save_strategy.dart` — PCSX2 save strategy (PS2). Memcards: {emulatorDir}/memcards/Mcd001.ps2, Mcd002.ps2. States: {emulatorDir}/sstates/{stem}.000 etc.
-- `lib/core/save/strategies/rpcs3_save_strategy.dart` — RPCS3 save strategy (PS3). Saves at %APPDATA%\rpcs3\dev_hdd0\home\00000001\savedata\{titleId}\. Extracts title ID via regex [A-Z]{4}\d{5}. Zips save folder for upload. Uses Process.run for APPDATA resolution.
-- `lib/core/save/strategies/xenia_save_strategy.dart` — Xenia Canary save strategy (Xbox 360). Saves at {emulatorDir}\content\{titleId}\00000001\. Extracts title ID via 8-char hex regex. Zips save folder for upload.
-
-### Core — Emulator
-- `lib/core/emulator/emulator_strategy.dart` — Abstract base class. Fields: name, emulatorId, supportedSlugs, windowsExecutable, linuxExecutable. Methods: launch(Game, romPath), resolveSavePath(Game), getExecutableForPlatform().
-- `lib/core/emulator/emulator_registry_data.dart` — Static data for emulator definitions.
-- `lib/core/emulator/strategy_registry.dart` — Registry for emulator strategies. Methods: getStrategyForSlug(), getDefinition().
-- `lib/core/emulator/emulator_download_service.dart` — Service for downloading and extracting emulators. Supports direct URL and GitHub release types. Handles .zip and .7z extraction.
-- `lib/core/emulator/github_release_service.dart` — Fetches latest release asset URL from GitHub API with required/excluded name filters.
-- `lib/core/emulator/strategies/retroarch_strategy.dart` — RetroArch strategy. Slugs: gba/gbc/gb/nes/snes/n64/nds/psx/ps1/psp/dc/dreamcast/megadrive/genesis/md etc.
-- `lib/core/emulator/strategies/dolphin_strategy.dart` — Dolphin strategy. Slugs: gc/gamecube/wii/ngc.
-- `lib/core/emulator/strategies/eden_strategy.dart` — Eden strategy. Slugs: switch/nintendo-switch/ns.
-- `lib/core/emulator/strategies/rpcs3_strategy.dart` — RPCS3 strategy. Slugs: ps3/playstation-3/playstation3.
-- `lib/core/emulator/strategies/pcsx2_strategy.dart` — PCSX2 strategy. Slugs: ps2/playstation-2/playstation2.
-- `lib/core/emulator/strategies/azahar_strategy.dart` — Azahar strategy. Slugs: 3ds/n3ds/nintendo-3ds/nintendo3ds/new-nintendo-3ds/new-nintendo-3ds-xl.
-- `lib/core/emulator/strategies/cemu_strategy.dart` — Cemu strategy. Slugs: wiiu/wii-u/nintendo-wii-u/nintendo-wiiu.
-- `lib/core/emulator/strategies/duckstation_strategy.dart` — DuckStation strategy. Slugs: ps1/playstation/psx.
-- `lib/core/emulator/strategies/xemu_strategy.dart` — Xemu strategy. Slugs: xbox.
-- `lib/core/emulator/strategies/xenia_strategy.dart` — Xenia Canary strategy. Slugs: xbox360/xbla.
-- `lib/core/emulator/strategies/windows_strategy.dart` — Windows native game strategy. Auto-detects exe in game folder, validates stored override exists on disk before using. Launches via Process.start. Monitors exit code for 5s — throws if crashed. Persists overrides via SharedPreferences (prefix `win_exe_`).
-
-### Core — Downloader
-- `lib/core/downloader/download_service.dart` — HTTP ROM download via Dio. Stream<DownloadProgress> for UI. Handles .zip extraction via archive package and .7z via bundled 7zr.exe (resolved via DirectoryService.resolveSevenZipPath()). Windows games (.zip/.7z) always extracted regardless of isMultiFile flag.
-
-### Core — Storage
-- `lib/core/storage/directory_service.dart` — Manages ROMs and emulator directories. Persists paths via SharedPreferences. resolveSevenZipPath() extracts bundled 7zr.exe from Flutter assets to %APPDATA%\Freegosy\thirdparty\ on first run. Uses defaultTargetPlatform for Windows check, Process.run for APPDATA resolution.
-
-### Core — Windows
-- `lib/core/windows/windows_game_service.dart` — Finds main exe in game folder (hint match then largest). Skips uninstall/setup/redist/etc. Launches via Process.start detached.
-- `lib/core/windows/pcgamingwiki_service.dart` — Queries PCGamingWiki API for Windows game save locations. Parses MediaWiki markup, expands environment variables (APPDATA, LOCALAPPDATA etc), returns resolved paths.
-
-### Core — Updater
-- `lib/core/updater/updater_service.dart` — Checks GitHub Releases API for new version. Downloads new binary to temp, swaps, relaunches.
-
-### Providers
-- `lib/providers/romm_provider.dart` — Riverpod providers for RomM config, connection state, DirectoryService, StrategyRegistry (loads WindowsStrategy persisted overrides on init), SaveSyncService (loads WindowsSaveStrategy persisted overrides on init).
-- `lib/providers/library_provider.dart` — Riverpod providers for platforms list and games list. Includes search, filtering, card aspect ratio, and RetroArch sync mode persistence.
-- `lib/providers/download_provider.dart` — Riverpod providers for active downloads and progress.
-
-### UI — Screens
-- `lib/ui/screens/library_screen.dart` — Main screen. Game grid with search, platform filter, download, launch, save sync. Windows games support long-press to open config dialog and auto-show config on missing exe. Launch errors show for 8 seconds.
-- `lib/ui/screens/download_screen.dart` — Active downloads list with progress bars.
-- `lib/ui/screens/settings_screen.dart` — RomM server config, card aspect ratio, storage paths, RetroArch sync mode, emulator download/install status.
-
-### UI — Widgets
-- `lib/ui/widgets/game_card.dart` — Single game tile. Shows cover, name, download/launch/sync buttons. Green dot when downloaded.
-- `lib/ui/widgets/download_progress_card.dart` — Single download row with progress bar and cancel button.
-- `lib/ui/widgets/platform_filter_bar.dart` — Horizontal scrollable platform chip row with distinct styling for selected/unselected states.
-- `lib/ui/widgets/windows_game_config_dialog.dart` — Dialog for configuring Windows game exe path and save directory. Browse buttons for both. Returns `Map<String, String>` with keys `exe` and `save`. Only shown for Windows platform games.
-
-## Key Contracts
-
-### EmulatorStrategy (never change these signatures)
-```dart
-abstract class EmulatorStrategy {
-  String get name;
-  String get emulatorId;
-  List<String> get supportedSlugs;
-  String get windowsExecutable;
-  String get linuxExecutable;
-  String getExecutableForPlatform();
-  Future<void> launch(Game game, String romPath);
-  String resolveSavePath(Game game);
-  bool get supportsSaveSync;
-}
-```
-
-### DownloadProgress
-```dart
-class DownloadProgress {
-  final String id; // e.g., game ID or emulator ID
-  final String gameName; // display name (game.name or emulator name)
-  final double percent;
-  final int bytesReceived;
-  final int totalBytes;
-  final bool isComplete;
-  final String? error;
-}
-```
-
-### RomMConfig
-```dart
-class RomMConfig {
-  final String baseUrl;
-  final String username;
-  final String password;
-}
-```
-
-## Dependencies (pubspec.yaml)
-- `flutter_riverpod` — state management
-- `dio` — HTTP client for API calls and downloads
-- `path_provider` — platform-safe file paths
-- `shared_preferences` — persist RomM config, card ratio, sync mode, Windows exe/save overrides
-- `package_info_plus` — read current app version for updater
-- `archive` — zip extraction and creation (ZipDecoder, ZipFileEncoder)
-- `file_picker` — directory and file selection
-- `path` — path manipulation utilities
+# Freegosy — Agent Map
+> This file is the source of truth for all LLM agents (Claude, Gemini) working on this codebase.
+> Read this before touching any file. Update this file if you create or split any file.
+
+## Project Overview
+Freegosy is a cross-platform Flutter app for browsing a RomM library, downloading ROMs via HTTP, and launching emulators. Built with Riverpod for state management.
+
+## Rules (MANDATORY)
+- No file exceeds 600 lines. If adding code would exceed this, split the file first and update this map.
+- All RomM API calls go through `romm_service.dart` only. Never call the API directly from UI or providers.
+- All emulator logic goes through the strategy pattern. Never hardcode emulator behavior in UI.
+- New emulator = new file in `core/emulator/strategies/`, register in `strategy_registry.dart` only.
+- New save strategy = new file in `core/save/strategies/`, register in `save_sync_service.dart` only.
+- New screen = new file in `ui/screens/`.
+- New reusable widget = new file in `ui/widgets/`.
+- Providers are thin — they call services, they do not contain business logic.
+- Never use `Platform.environment` or `Platform.isWindows` directly — causes conflicts with flutter/foundation.dart. Use `Process.run('cmd', ['/c', 'echo %APPDATA%'])` for env vars, and `defaultTargetPlatform == TargetPlatform.windows` for platform checks.
+
+## File Map
+
+### Entry Points
+- `lib/main.dart` — App entry point. Initializes Riverpod ProviderScope. Calls app.dart.
+- `lib/app.dart` — MaterialApp setup, theme, initial route, navigation shell.
+
+### Core — RomM
+- `lib/core/romm/romm_service.dart` — All RomM HTTP calls (Dio). Methods: getPlatforms(), getGames(), getSaves(), uploadSave(), getLatestSave(), downloadSave(). Returns typed models.
+- `lib/core/romm/romm_models.dart` — Data models: Game, Platform, SaveFile, RomMConfig.
+
+### Core — Save Sync
+- `lib/core/save/save_strategy.dart` — Abstract base class SaveStrategy. Methods: getSaveDir(), getSaveFiles(), restoreSave(). Helpers: backupSave() keeps max 3 clean versions (.bak, .bak1, .bak2) — never creates chained .bak.bak files., getRomStem().
+- `lib/core/save/save_sync_service.dart` — SaveSyncService. Methods: pushSaves(), pullSave(), getStrategyForSlug(). getStrategyForSlug() checks StrategyRegistry user preferences first before falling back to platform slug defaults. Accepts StrategyRegistry as constructor parameter. Wires all strategies to RommService. Exposes windowsSaveStrategy for external access.
+- `lib/core/save/strategies/retroarch_save_strategy.dart` — RetroArch save strategy (GBA/GBC/GB/SNES/NES/N64/NDS/PSX/PSP/Dreamcast/Megadrive). Reads saves/{coreName}/{stem}.srm and .state.auto. PSP special case returns entire saves/PPSSPP/PSP directory as zip. All platforms use dual-stem (getRomStem + romPath filename) for state file matching. Directory existence check handles both File and Directory types.
+- `lib/core/save/strategies/dolphin_save_strategy.dart` — Dolphin save strategy (GC/Wii). Reads User/GC/{region}/Card A/*.gci.
+- `lib/core/save/strategies/eden_save_strategy.dart` — Eden/Switch save strategy. Resolves title ID via filename regex, XCI header parse, or save folder scan. Zips save folder for upload.
+- `lib/core/save/strategies/windows_save_strategy.dart` — Windows native game save strategy. Uses PCGamingWiki API for auto-detection, supports manual override. Zips entire save directory for upload, extracts on restore. Persists overrides via SharedPreferences (prefix `win_save_`).
+- `lib/core/save/strategies/pcsx2_save_strategy.dart` — PCSX2 save strategy (PS2). Memcards: {emulatorDir}/memcards/Mcd001.ps2, Mcd002.ps2. States: {emulatorDir}/sstates/{stem}.000 etc.
+- `lib/core/save/strategies/rpcs3_save_strategy.dart` — RPCS3 save strategy (PS3). Saves at %APPDATA%
pcs3\dev_hdd0\home\00000001\savedata\{titleId}\. Extracts title ID via regex [A-Z]{4}\d{5}. Zips save folder for upload. Uses Process.run for APPDATA resolution.
+- `lib/core/save/strategies/xenia_save_strategy.dart` — Xenia Canary save strategy (Xbox 360). Saves at {emulatorDir}\content\{titleId}\00000001\. Extracts title ID via 8-char hex regex. Zips save folder for upload.
+- `lib/core/save/strategies/duckstation_save_strategy.dart` — DuckStation save strategy (PS1). Checks for portable.txt in emulator dir — if present uses {emulatorDir}/memcards/{stem}.mcd, otherwise falls back to %LOCALAPPDATA%\DuckStation\memcards\{stem}.mcd.
+- `lib/core/save/strategies/melonds_save_strategy.dart` — melonDS save strategy (NDS). Saves .sav file next to ROM, derived from actual romPath filename not game name.
+- `lib/core/save/strategies/mgba_save_strategy.dart` — mGBA save strategy (GBA/GBC/GB). Saves .sav file next to ROM, derived from actual romPath filename.
+- `lib/core/save/strategies/ppsspp_save_strategy.dart` — PPSSPP save strategy (PSP). Saves at {emulatorDir}/memstick/PSP/SAVEDATA/, states at PPSSPP_STATE/. Returns entire PSP/SAVEDATA directory as zip. Restore strips top-level SAVEDATA folder to extract directly into memstick/PSP/SAVEDATA/.
+- `lib/core/save/strategies/cemu_save_strategy.dart` — Cemu save strategy (Wii U). Zips {emulatorDir}/mlc01/usr/save/00050000/ for upload. Restore extracts zip directly into mlc01/usr/save/ skipping .bak entries.
+
+### Core — Emulator
+- `lib/core/emulator/emulator_strategy.dart` — Abstract base class. Fields: name, emulatorId, supportedSlugs, windowsExecutable, linuxExecutable. Methods: launch(Game, romPath), resolveSavePath(Game), getExecutableForPlatform(). Optional launchWithHandle(Game, romPath) method returns Process handle for auto-sync tracking. Default returns null.
+- `lib/core/emulator/emulator_registry_data.dart` — Static data for emulator definitions. Includes RetroArch, Dolphin, Eden, RPCS3, PCSX2, Azahar, Cemu, Xemu, Xenia, DuckStation, Flycast, melonDS, PPSSPP, mGBA, MAME.
+- `lib/core/emulator/strategy_registry.dart` — Registry for emulator strategies. Includes conflict detection via detectConflicts() returning Map<String, List<EmulatorStrategy>> grouped by canonical platform name. setPreference(slug, emulatorId) and loadPreferences() persist user preferences under SharedPreferences key prefix 'emulator_pref_'. loadPreferences() is called on startup in romm_provider.dart to ensure conflict preferences are available before first launch.
+- `lib/core/emulator/emulator_download_service.dart` — Service for downloading emulators. Supports direct URL and GitHub release types. Uses ExtractionService for all extraction.
+- `lib/core/emulator/github_release_service.dart` — Fetches latest release asset URL from GitHub API with required/excluded name filters.
+- `lib/core/emulator/strategies/retroarch_strategy.dart` — RetroArch strategy. Slugs: gba/gbc/gb/nes/snes/n64/nds/psx/ps1/psp/dc/dreamcast/megadrive/genesis/md etc. Throws MissingRetroArchCoreException when a core .dll is missing. Has downloadCore() method to fetch from RetroArch buildbot.
+- `lib/core/emulator/strategies/dolphin_strategy.dart` — Dolphin strategy. Slugs: gc/gamecube/wii/ngc.
+- `lib/core/emulator/strategies/eden_strategy.dart` — Eden strategy. Slugs: switch/nintendo-switch/ns.
+- `lib/core/emulator/strategies/rpcs3_strategy.dart` — RPCS3 strategy. Slugs: ps3/playstation-3/playstation3.
+- `lib/core/emulator/strategies/pcsx2_strategy.dart` — PCSX2 strategy. Slugs: ps2/playstation-2/playstation2.
+- `lib/core/emulator/strategies/azahar_strategy.dart` — Azahar strategy. Slugs: 3ds/n3ds/nintendo-3ds/nintendo3ds/new-nintendo-3ds/new-nintendo-3ds-xl.
+- `lib/core/emulator/strategies/cemu_strategy.dart` — Cemu strategy. Slugs: wiiu/wii-u/nintendo-wii-u/nintendo-wiiu.
+- `lib/core/emulator/strategies/duckstation_strategy.dart` — DuckStation strategy. Slugs: ps1/playstation/psx.
+- `lib/core/emulator/strategies/flycast_strategy.dart` — Flycast strategy. Slugs: dc/dreamcast/naomi/naomi2/atomiswave/cave/hikaru.
+- `lib/core/emulator/strategies/melonds_strategy.dart` — melonDS strategy. Slugs: nds/nintendo-ds/ds.
+- `lib/core/emulator/strategies/ppsspp_strategy.dart` — PPSSPP strategy. Slugs: psp/playstation-portable. Implements launchWithHandle() returning Process with ProcessStartMode.normal for auto-sync on game close.
+- `lib/core/emulator/strategies/mgba_strategy.dart` — mGBA strategy. Slugs: gba/gbc/gb/game-boy-advance/game-boy-color/game-boy.
+- `lib/core/emulator/strategies/mame_strategy.dart` — MAME strategy. Slugs: arcade/mame. Handles self-extracting .exe downloads.
+- `lib/core/emulator/strategies/xemu_strategy.dart` — Xemu strategy. Slugs: xbox.
+- `lib/core/emulator/strategies/xenia_strategy.dart` — Xenia Canary strategy. Slugs: xbox360/xbla.
+- `lib/core/emulator/strategies/windows_strategy.dart` — Windows native game strategy. Auto-detects exe in game folder, validates stored override exists on disk before using. Launches via Process.start. Monitors exit code for 5s — throws if crashed. Persists overrides via SharedPreferences (prefix `win_exe_`).
+
+### Core — Extraction
+- `lib/core/extraction/extraction_service.dart` — Unified extraction service. Handles .zip via archive package, .7z via bundled 7zr.exe, and self-extracting .exe files.
+
+### Core — Downloader
+- `lib/core/downloader/download_service.dart` — HTTP ROM download via Dio. Stream<DownloadProgress> for UI. Uses ExtractionService for all extraction.
+
+### Core — Storage
+- `lib/core/storage/directory_service.dart` — Manages ROMs and emulator directories. Persists paths via SharedPreferences. setEmulatorPathOverride()/getEmulatorPathOverride()/loadEmulatorPathOverrides() allow users to point any emulator to a custom installation directory, persisted under SharedPreferences key prefix 'emu_path_'. resolveSevenZipPath() extracts bundled 7zr.exe from Flutter assets.
+
+### Core — Windows
+- `lib/core/windows/windows_game_service.dart` — Finds main exe in game folder. Launches via Process.start detached.
+- `lib/core/windows/pcgamingwiki_service.dart` — Queries PCGamingWiki API for Windows game save locations.
+
+### Core — Updater
+- `lib/core/updater/updater_service.dart` — Checks GitHub Releases API for new version. Downloads and relaunch.
+
+### Providers
+- `lib/providers/romm_provider.dart` — Riverpod providers for RomM config, connection state, DirectoryService, StrategyRegistry, SaveSyncService. strategyRegistryProvider and saveSyncServiceProvider are FutureProviders that must be awaited with .future to ensure preferences are loaded before use.
+- `lib/providers/library_provider.dart` — Riverpod providers for platforms and games. Search, filtering, and display settings persistence. Background refresh silently updates cache.
+- `lib/providers/download_provider.dart` — Riverpod providers for active downloads (games and emulators) and progress.
+
+### UI — Screens
+- `lib/ui/screens/library_screen.dart` — Main screen. Game grid with search, platform filter, preset display system. Launch flow shows status snackbars at each stage. Catches MissingRetroArchCoreException to offer auto-download of missing core then re-launches. Cache invalidation on pull-to-refresh. Auto-syncs saves when game process exits via launchWithHandle(). Awaits strategyRegistryProvider.future and saveSyncServiceProvider.future on launch to ensure preferences are loaded before strategy lookup.
+- `lib/ui/screens/library_skeleton.dart` — Skeleton loading grid and _SkeletonCard widget. Contains top-level functions buildSkeletonGrid() and calculateCardHeight().
+- `lib/ui/screens/download_screen.dart` — Active downloads list.
+- `lib/ui/screens/settings_screen.dart` — Server config, storage paths, RetroArch sync mode.
+- `lib/ui/screens/settings_display_section.dart` — Extracted display settings section. Contains buildDisplaySection() function with preset chips, column count, card shape, spacing, title and hover toggles.
+- `lib/ui/screens/settings_emulators_section.dart` — Extracted emulators section. Contains buildEmulatorsSection() and buildConflictsSection() functions. Handles emulator download, custom path overrides, and conflict resolution UI.
+
+### UI — Widgets
+- `lib/ui/widgets/game_card.dart` — StatefulWidget (not Consumer). Accepts pre-resolved coverUrl. Uses CachedNetworkImage (memCache: 300x400).
+- `lib/ui/widgets/download_progress_card.dart` — Single download row with progress bar.
+- `lib/ui/widgets/platform_filter_bar.dart` — Platform chip row.
+- `lib/ui/widgets/windows_game_config_dialog.dart` — Dialog for configuring Windows game overrides.
+
+## Key Contracts
+
+### EmulatorStrategy (never change these signatures)
+```dart
+abstract class EmulatorStrategy {
+  String get name;
+  String get emulatorId;
+  List<String> get supportedSlugs;
+  String get windowsExecutable;
+  String get linuxExecutable;
+  String getExecutableForPlatform();
+  Future<void> launch(Game game, String romPath);
+  String resolveSavePath(Game game);
+  bool get supportsSaveSync;
+}
+```
+
+### DownloadProgress
+```dart
+class DownloadProgress {
+  final String id; // e.g., game ID or emulator ID
+  final String gameName; // display name (game.name or emulator name)
+  final double percent;
+  final int bytesReceived;
+  final int totalBytes;
+  final bool isComplete;
+  final String? error;
+}
+```
+
+### RomMConfig
+```dart
+class RomMConfig {
+  final String baseUrl;
+  final String username;
+  final String password;
+}
+```
+
+## Dependencies (pubspec.yaml)
+- `flutter_riverpod` — state management
+- `dio` — HTTP client for API calls and downloads
+- `path_provider` — platform-safe file paths
+- `shared_preferences` — persist RomM config, display settings, overrides
+- `package_info_plus` — read current app version
+- `archive` — zip extraction and creation
+- `cached_network_image` — disk and memory cache for network images
+- `file_picker` — directory and file selection
+- `path` — path manipulation utilities
 - `thirdparty/7zr.exe` — bundled 7-Zip console executable for .7z extraction (Flutter asset)
\ No newline at end of file
diff --git a/lib/core/downloader/download_service.dart b/lib/core/downloader/download_service.dart
index e83fab2..ee7cce4 100644
--- a/lib/core/downloader/download_service.dart
+++ b/lib/core/downloader/download_service.dart
@@ -1,8 +1,8 @@
 import 'package:dio/dio.dart';
 import 'dart:async';
 import 'dart:io';
-import 'package:archive/archive_io.dart';
 import 'package:freegosy/core/storage/directory_service.dart';
+import 'package:freegosy/core/extraction/extraction_service.dart';
 import 'package:freegosy/core/romm/romm_models.dart';
 
 class DownloadProgress {
@@ -30,8 +30,13 @@ class DownloadProgress {
 class DownloadService {
   final Dio dio;
   final DirectoryService directoryService;
+  final ExtractionService extractionService;
 
-  DownloadService({required this.dio, required this.directoryService});
+  DownloadService({
+    required this.dio,
+    required this.directoryService,
+    required this.extractionService,
+  });
 
   Stream<DownloadProgress> download(Game game, String downloadUrl, {Map<String, String>? headers}) async* {
     if (await directoryService.isRomDownloaded(game)) {
@@ -67,7 +72,8 @@ class DownloadService {
           final isWindowsGame = ['windows', 'pc', 'win'].contains(game.platformSlug?.toLowerCase() ?? '');
           final isArchive = savePath.toLowerCase().endsWith('.zip') ||
               savePath.toLowerCase().endsWith('.7z');
-          if (game.isMultiFile || (isWindowsGame && isArchive)) {
+          final shouldExtract = game.isMultiFile || (isWindowsGame && isArchive);
+          if (shouldExtract) {
             controller.add(DownloadProgress(
               id: game.id,
               gameName: game.name,
@@ -114,25 +120,16 @@ class DownloadService {
 
     await Directory(extractDir).create(recursive: true);
 
-    if (zipPath.toLowerCase().endsWith('.7z')) {
-      final sevenZipExe = await directoryService.resolveSevenZipPath();
-      if (sevenZipExe == null) {
-        throw Exception('7zr.exe could not be initialized. Try reinstalling Freegosy.');
-      }
-      final result = await Process.run(
-        sevenZipExe, ['x', zipPath, '-o$extractDir', '-y'],
-        runInShell: false,
-      );
-      if (result.exitCode != 0) {
-        throw Exception('7z extraction failed: ${result.stderr}');
+    try {
+      await extractionService.extract(zipPath, extractDir);
+      await File(zipPath).delete();
+    } catch (e) {
+      if (e.toString().contains('Unsupported archive format')) {
+        // File is not an archive - leave it as downloaded
+        await Directory(extractDir).delete();
+        return;
       }
-    } else {
-      final bytes = await File(zipPath).readAsBytes();
-      final archive = ZipDecoder().decodeBytes(bytes);
-      extractArchiveToDisk(archive, extractDir);
+      rethrow;
     }
-
-    // Delete the archive after extraction
-    await File(zipPath).delete();
   }
 }
\ No newline at end of file
diff --git a/lib/core/emulator/emulator_download_service.dart b/lib/core/emulator/emulator_download_service.dart
index 979da86..968a204 100644
--- a/lib/core/emulator/emulator_download_service.dart
+++ b/lib/core/emulator/emulator_download_service.dart
@@ -3,18 +3,19 @@ import 'dart:io';
 import 'package:dio/dio.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:path/path.dart' as p;
-import 'package:archive/archive_io.dart';
 import '../downloader/download_service.dart';
 import '../storage/directory_service.dart';
+import '../extraction/extraction_service.dart';
 import 'emulator_registry_data.dart';
 import 'github_release_service.dart';
 
 class EmulatorDownloadService {
   final Dio _dio;
   final DirectoryService _directoryService;
+  final ExtractionService _extractionService;
   late final GithubReleaseService _githubService;
 
-  EmulatorDownloadService(this._dio, this._directoryService) {
+  EmulatorDownloadService(this._dio, this._directoryService, this._extractionService) {
     _githubService = GithubReleaseService(_dio);
   }
 
@@ -70,7 +71,7 @@ class EmulatorDownloadService {
       yield DownloadProgress(
         id: emulatorId,
         gameName: emulatorName,
-        error: 'No download URL for this platform',
+        error: 'This emulator is not available for your platform',
       );
       return;
     }
@@ -107,7 +108,7 @@ class EmulatorDownloadService {
             percent: 1.0,
             status: 'Extracting...',
           ));
-          await _extractArchive(tempFilePath, emulatorDir);
+          await _extractionService.extract(tempFilePath, emulatorDir);
           controller.add(DownloadProgress(
             id: emulatorId,
             gameName: emulatorName,
@@ -144,22 +145,4 @@ class EmulatorDownloadService {
       );
     }
   }
-
-  Future<void> _extractArchive(String archivePath, String destDir) async {
-    if (archivePath.endsWith('.zip')) {
-      final bytes = await File(archivePath).readAsBytes();
-      final archive = ZipDecoder().decodeBytes(bytes);
-      extractArchiveToDisk(archive, destDir);
-    } else if (archivePath.endsWith('.7z')) {
-      final result = await Process.run(
-        '7z', ['x', archivePath, '-o$destDir', '-y'],
-        runInShell: true,
-      );
-      if (result.exitCode != 0) {
-        throw Exception('7z extraction failed: ${result.stderr}');
-      }
-    } else {
-      throw Exception('Unsupported archive format: $archivePath');
-    }
-  }
 }
\ No newline at end of file
diff --git a/lib/core/emulator/emulator_registry_data.dart b/lib/core/emulator/emulator_registry_data.dart
index 0aefadf..54ba544 100644
--- a/lib/core/emulator/emulator_registry_data.dart
+++ b/lib/core/emulator/emulator_registry_data.dart
@@ -12,6 +12,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
       'gamegear', 'atari2600', 'atari7800', 'lynx', 'neogeo', 'arcade', 'mame',
       'pcengine', 'wonderswan', 'virtualboy', 'msx', 'dos'
     ],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'dolphin',
@@ -21,6 +22,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'Dolphin.exe',
     'linux_executable': 'dolphin-emu',
     'platform_slugs': ['gc', 'gamecube', 'wii', 'ngc'],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'eden',
@@ -30,6 +32,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'eden.exe',
     'linux_executable': 'eden',
     'platform_slugs': ['switch', 'nintendo-switch', 'ns'],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'rpcs3',
@@ -41,6 +44,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'rpcs3.exe',
     'linux_executable': 'rpcs3',
     'platform_slugs': ['ps3', 'playstation-3', 'playstation3'],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'pcsx2',
@@ -52,6 +56,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'pcsx2-qt.exe',
     'linux_executable': 'pcsx2-qt',
     'platform_slugs': ['ps2', 'playstation-2', 'playstation2'],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'azahar',
@@ -63,17 +68,19 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'azahar.exe',
     'linux_executable': 'azahar',
     'platform_slugs': ['3ds', 'n3ds', 'nintendo-3ds', 'nintendo3ds', 'new-nintendo-3ds', 'new-nintendo-3ds-xl'],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'cemu',
     'name': 'Cemu (Wii U)',
     'type': 'github',
     'github_repo': 'cemu-project/Cemu',
-    'github_asset_required': ['windows'],
-    'github_asset_excluded': ['experimental', 'debug'],
+    'github_asset_required': ['windows', 'x64', '.zip'],
+    'github_asset_excluded': ['debug', 'symbols', 'linux', 'macos'],
     'windows_executable': 'Cemu.exe',
     'linux_executable': 'cemu',
     'platform_slugs': ['wiiu', 'wii-u', 'nintendo-wii-u', 'nintendo-wiiu'],
+    'supported_platforms': ['windows', 'linux'],
   },
   {
     'id': 'xemu',
@@ -85,6 +92,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'xemu.exe',
     'linux_executable': 'xemu',
     'platform_slugs': ['xbox'],
+    'supported_platforms': ['windows'],
   },
   {
     'id': 'xenia_canary',
@@ -96,6 +104,7 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'xenia_canary.exe',
     'linux_executable': 'xenia_canary',
     'platform_slugs': ['xbox360', 'xbla'],
+    'supported_platforms': ['windows'],
   },
   {
     'id': 'duckstation',
@@ -107,6 +116,68 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': 'duckstation-qt-x64-ReleaseLTCG.exe',
     'linux_executable': 'duckstation-qt',
     'platform_slugs': ['ps1', 'playstation', 'psx'],
+    'supported_platforms': ['windows', 'linux'],
+  },
+  {
+    'id': 'flycast',
+    'name': 'Flycast (Dreamcast)',
+    'type': 'github',
+    'github_repo': 'flyinghead/flycast',
+    'github_asset_required': ['win64', '.zip'],
+    'github_asset_excluded': ['debug', 'symbols', 'linux', 'macos'],
+    'windows_executable': 'flycast.exe',
+    'linux_executable': 'flycast',
+    'platform_slugs': ['dc', 'dreamcast', 'naomi', 'naomi2', 'atomiswave', 'cave', 'hikaru'],
+    'supported_platforms': ['windows', 'linux'],
+  },
+  {
+    'id': 'melonds',
+    'name': 'melonDS',
+    'type': 'github',
+    'github_repo': 'melonDS-emu/melonDS',
+    'github_asset_required': ['win', '.zip'],
+    'github_asset_excluded': ['source', 'debug'],
+    'windows_executable': 'melonDS.exe',
+    'linux_executable': 'melonDS',
+    'platform_slugs': ['nds', 'nintendo-ds', 'ds'],
+    'supported_platforms': ['windows', 'linux'],
+  },
+  {
+    'id': 'ppsspp',
+    'name': 'PPSSPP (PSP)',
+    'type': 'github',
+    'github_repo': 'hrydgard/ppsspp',
+    'github_asset_required': ['Windows', 'x64', '.zip'],
+    'github_asset_excluded': ['debug', 'symbols', 'VR'],
+    'windows_executable': 'PPSSPPWindows64.exe',
+    'linux_executable': 'PPSSPP',
+    'supported_platforms': ['windows', 'linux'],
+    'platform_slugs': ['psp', 'playstation-portable'],
+  },
+  {
+    'id': 'mgba',
+    'name': 'mGBA (GBA)',
+    'type': 'github',
+    'github_repo': 'mgba-emu/mgba',
+    'github_asset_required': ['win64', '.7z'],
+    'github_asset_excluded': ['debug', 'symbols', 'source'],
+    'windows_executable': 'mGBA.exe',
+    'linux_executable': 'mgba',
+    'supported_platforms': ['windows', 'linux'],
+    'platform_slugs': ['gba', 'gbc', 'gb', 'game-boy-advance', 'game-boy-color', 'game-boy'],
+  },
+  {
+    'id': 'mame',
+    'name': 'MAME',
+    'type': 'github',
+    'github_repo': 'mamedev/mame',
+    'github_asset_required': ['x64.exe'],
+    'github_asset_excluded': ['debug', 'symbols', 'sources', 'tools'],
+    'extraction_type': 'self_extracting',
+    'windows_executable': 'mame.exe',
+    'linux_executable': 'mame',
+    'supported_platforms': ['windows', 'linux'],
+    'platform_slugs': ['arcade', 'mame'],
   },
   {
     'id': 'windows_native',
@@ -115,5 +186,6 @@ const List<Map<String, dynamic>> kEmulatorDefinitions = [
     'windows_executable': '',
     'linux_executable': '',
     'platform_slugs': ['windows', 'pc', 'win'],
+    'supported_platforms': ['windows'],
   },
-];
\ No newline at end of file
+];
diff --git a/lib/core/emulator/emulator_strategy.dart b/lib/core/emulator/emulator_strategy.dart
index 3a28d16..b0e70f9 100644
--- a/lib/core/emulator/emulator_strategy.dart
+++ b/lib/core/emulator/emulator_strategy.dart
@@ -1,4 +1,5 @@
-import 'dart:io' as io;
+import 'dart:io';
+import 'dart:io' as io;
 import 'package:freegosy/core/romm/romm_models.dart';
 
 abstract class EmulatorStrategy {
@@ -16,5 +17,6 @@ abstract class EmulatorStrategy {
   }
 
   Future<void> launch(Game game, String romPath);
+  Future<Process?> launchWithHandle(Game game, String romPath) async => null;
   String resolveSavePath(Game game);
 }
diff --git a/lib/core/emulator/strategies/azahar_strategy.dart b/lib/core/emulator/strategies/azahar_strategy.dart
index 9e7b673..2322d9f 100644
--- a/lib/core/emulator/strategies/azahar_strategy.dart
+++ b/lib/core/emulator/strategies/azahar_strategy.dart
@@ -38,6 +38,15 @@ class AzaharStrategy extends EmulatorStrategy {
     await Process.start(exePath, [romPath], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategies/cemu_strategy.dart b/lib/core/emulator/strategies/cemu_strategy.dart
index 60732ea..326d2e4 100644
--- a/lib/core/emulator/strategies/cemu_strategy.dart
+++ b/lib/core/emulator/strategies/cemu_strategy.dart
@@ -35,6 +35,15 @@ class CemuStrategy extends EmulatorStrategy {
     await Process.start(exePath, ['-g', romPath], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, ['-g', romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategies/dolphin_strategy.dart b/lib/core/emulator/strategies/dolphin_strategy.dart
index 274c301..a1a8b40 100644
--- a/lib/core/emulator/strategies/dolphin_strategy.dart
+++ b/lib/core/emulator/strategies/dolphin_strategy.dart
@@ -33,6 +33,13 @@ class DolphinStrategy extends EmulatorStrategy {
     await Process.run(exePath, ['-e', romPath]);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(emulatorId, getExecutableForPlatform());
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, ['-e', romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) {
     return ''; // Placeholder
diff --git a/lib/core/emulator/strategies/duckstation_strategy.dart b/lib/core/emulator/strategies/duckstation_strategy.dart
index 74f2663..f098b2e 100644
--- a/lib/core/emulator/strategies/duckstation_strategy.dart
+++ b/lib/core/emulator/strategies/duckstation_strategy.dart
@@ -35,6 +35,15 @@ class DuckstationStrategy extends EmulatorStrategy {
     await Process.start(exePath, ['-batch', romPath], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, ['-batch', romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategies/eden_strategy.dart b/lib/core/emulator/strategies/eden_strategy.dart
index 93c16b1..d72d953 100644
--- a/lib/core/emulator/strategies/eden_strategy.dart
+++ b/lib/core/emulator/strategies/eden_strategy.dart
@@ -33,6 +33,13 @@ class EdenStrategy extends EmulatorStrategy {
     await Process.run(exePath, [romPath]);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(emulatorId, getExecutableForPlatform());
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) {
     return ''; // Placeholder
diff --git a/lib/core/emulator/strategies/flycast_strategy.dart b/lib/core/emulator/strategies/flycast_strategy.dart
new file mode 100644
index 0000000..6606830
--- /dev/null
+++ b/lib/core/emulator/strategies/flycast_strategy.dart
@@ -0,0 +1,49 @@
+import 'dart:io';
+import 'package:freegosy/core/emulator/emulator_strategy.dart';
+import 'package:freegosy/core/romm/romm_models.dart';
+import 'package:freegosy/core/storage/directory_service.dart';
+
+class FlycastStrategy extends EmulatorStrategy {
+  final DirectoryService _directoryService;
+
+  FlycastStrategy(this._directoryService);
+
+  @override
+  String get name => 'Flycast';
+
+  @override
+  String get emulatorId => 'flycast';
+
+  @override
+  List<String> get supportedSlugs => ['dc', 'dreamcast', 'naomi', 'atomiswave'];
+
+  @override
+  String get windowsExecutable => 'flycast.exe';
+
+  @override
+  String get linuxExecutable => 'flycast';
+
+  @override
+  bool get supportsSaveSync => false;
+
+  @override
+  Future<void> launch(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    await Process.start(exePath, [romPath], mode: ProcessStartMode.detached);
+  }
+
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
+  @override
+  String resolveSavePath(Game game) => '';
+}
diff --git a/lib/core/emulator/strategies/mame_strategy.dart b/lib/core/emulator/strategies/mame_strategy.dart
new file mode 100644
index 0000000..9847c9c
--- /dev/null
+++ b/lib/core/emulator/strategies/mame_strategy.dart
@@ -0,0 +1,69 @@
+import 'dart:io';
+import 'package:freegosy/core/emulator/emulator_strategy.dart';
+import 'package:freegosy/core/romm/romm_models.dart';
+import 'package:freegosy/core/storage/directory_service.dart';
+
+class MAMEStrategy extends EmulatorStrategy {
+  final DirectoryService _directoryService;
+
+  MAMEStrategy(this._directoryService);
+
+  @override
+  String get name => 'MAME';
+
+  @override
+  String get emulatorId => 'mame';
+
+  @override
+  List<String> get supportedSlugs => ['arcade', 'mame'];
+
+  @override
+  String get windowsExecutable => 'mame.exe';
+
+  @override
+  String get linuxExecutable => 'mame';
+
+  @override
+  bool get supportsSaveSync => false;
+
+  @override
+  Future<void> launch(Game game, String romPath) async {
+    final emulatorDir = await _directoryService.getEmulatorDirectory(emulatorId);
+    final mameExePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, windowsExecutable,
+    );
+
+    if (mameExePath == null) {
+      // Check for self-extracting exe in the emulator directory
+      final dir = Directory(emulatorDir);
+      if (await dir.exists()) {
+        final files = await dir.list().toList();
+        final setupExe = files.firstWhere(
+          (f) => f is File && f.path.toLowerCase().endsWith('.exe') && !f.path.toLowerCase().endsWith('mame.exe'),
+          orElse: () => File(''),
+        );
+
+        if (setupExe.path.isNotEmpty) {
+          // It's a self-extracting exe, run it to extract
+          // We run it with current directory set to emulatorDir so it extracts there
+          await Process.run(setupExe.path, [], workingDirectory: emulatorDir);
+          
+          // Try finding mame.exe again
+          final retryPath = await _directoryService.findEmulatorExecutable(
+            emulatorId, windowsExecutable,
+          );
+          if (retryPath != null) {
+            await Process.start(retryPath, [romPath], mode: ProcessStartMode.detached);
+            return;
+          }
+        }
+      }
+      throw Exception('$name not found. Please download it first.');
+    }
+
+    await Process.start(mameExePath, [romPath], mode: ProcessStartMode.detached);
+  }
+
+  @override
+  String resolveSavePath(Game game) => '';
+}
diff --git a/lib/core/emulator/strategies/melonds_strategy.dart b/lib/core/emulator/strategies/melonds_strategy.dart
new file mode 100644
index 0000000..345745f
--- /dev/null
+++ b/lib/core/emulator/strategies/melonds_strategy.dart
@@ -0,0 +1,52 @@
+import 'dart:io';
+import 'package:freegosy/core/emulator/emulator_strategy.dart';
+import 'package:freegosy/core/romm/romm_models.dart';
+import 'package:freegosy/core/storage/directory_service.dart';
+
+class MelonDSStrategy extends EmulatorStrategy {
+  final DirectoryService _directoryService;
+
+  MelonDSStrategy(this._directoryService);
+
+  @override
+  String get name => 'melonDS';
+
+  @override
+  String get emulatorId => 'melonds';
+
+  @override
+  List<String> get supportedSlugs => ['nds', 'nintendo-ds', 'ds'];
+
+  @override
+  String get windowsExecutable => 'melonDS.exe';
+
+  @override
+  String get linuxExecutable => 'melonDS';
+
+  @override
+  bool get supportsSaveSync => false;
+
+  @override
+  Future<void> launch(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(emulatorId, getExecutableForPlatform());
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    final workingDir = File(exePath).parent.path;
+    await Process.start(exePath, [romPath], workingDirectory: workingDir, mode: ProcessStartMode.detached);
+  }
+
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(emulatorId, getExecutableForPlatform());
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    final workingDir = File(exePath).parent.path;
+    final process = await Process.start(exePath, [romPath], workingDirectory: workingDir, mode: ProcessStartMode.normal);
+    process.stdout.drain();
+    process.stderr.drain();
+    return process;
+  }
+
+  @override
+  String resolveSavePath(Game game) {
+    return ''; // Placeholder
+  }
+}
diff --git a/lib/core/emulator/strategies/mgba_strategy.dart b/lib/core/emulator/strategies/mgba_strategy.dart
new file mode 100644
index 0000000..8aee232
--- /dev/null
+++ b/lib/core/emulator/strategies/mgba_strategy.dart
@@ -0,0 +1,49 @@
+import 'dart:io';
+import 'package:freegosy/core/emulator/emulator_strategy.dart';
+import 'package:freegosy/core/romm/romm_models.dart';
+import 'package:freegosy/core/storage/directory_service.dart';
+
+class MGBAStrategy extends EmulatorStrategy {
+  final DirectoryService _directoryService;
+
+  MGBAStrategy(this._directoryService);
+
+  @override
+  String get name => 'mGBA';
+
+  @override
+  String get emulatorId => 'mgba';
+
+  @override
+  List<String> get supportedSlugs => ['gba', 'gbc', 'gb', 'game-boy-advance', 'game-boy-color', 'game-boy'];
+
+  @override
+  String get windowsExecutable => 'mGBA.exe';
+
+  @override
+  String get linuxExecutable => 'mgba';
+
+  @override
+  bool get supportsSaveSync => false;
+
+  @override
+  Future<void> launch(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    await Process.start(exePath, [romPath], mode: ProcessStartMode.detached);
+  }
+
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
+  @override
+  String resolveSavePath(Game game) => '';
+}
diff --git a/lib/core/emulator/strategies/pcsx2_strategy.dart b/lib/core/emulator/strategies/pcsx2_strategy.dart
index 8612e37..8fd512e 100644
--- a/lib/core/emulator/strategies/pcsx2_strategy.dart
+++ b/lib/core/emulator/strategies/pcsx2_strategy.dart
@@ -37,6 +37,17 @@ class Pcsx2Strategy extends EmulatorStrategy {
     await Process.start(normalizedExe, [normalizedRom], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    final normalizedRom = romPath.replaceAll('/', '\\');
+    final normalizedExe = exePath.replaceAll('/', '\\');
+    return await Process.start(normalizedExe, [normalizedRom], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategies/ppsspp_strategy.dart b/lib/core/emulator/strategies/ppsspp_strategy.dart
new file mode 100644
index 0000000..394f752
--- /dev/null
+++ b/lib/core/emulator/strategies/ppsspp_strategy.dart
@@ -0,0 +1,49 @@
+import 'dart:io';
+import 'package:freegosy/core/emulator/emulator_strategy.dart';
+import 'package:freegosy/core/romm/romm_models.dart';
+import 'package:freegosy/core/storage/directory_service.dart';
+
+class PPSSPPStrategy extends EmulatorStrategy {
+  final DirectoryService _directoryService;
+
+  PPSSPPStrategy(this._directoryService);
+
+  @override
+  String get name => 'PPSSPP';
+
+  @override
+  String get emulatorId => 'ppsspp';
+
+  @override
+  List<String> get supportedSlugs => ['psp', 'playstation-portable'];
+
+  @override
+  String get windowsExecutable => 'PPSSPPWindows64.exe';
+
+  @override
+  String get linuxExecutable => 'PPSSPP';
+
+  @override
+  bool get supportsSaveSync => false;
+
+  @override
+  Future<void> launch(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    await Process.start(exePath, [romPath], mode: ProcessStartMode.detached);
+  }
+
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
+  @override
+  String resolveSavePath(Game game) => '';
+}
diff --git a/lib/core/emulator/strategies/retroarch_strategy.dart b/lib/core/emulator/strategies/retroarch_strategy.dart
index 9950744..dbff3b6 100644
--- a/lib/core/emulator/strategies/retroarch_strategy.dart
+++ b/lib/core/emulator/strategies/retroarch_strategy.dart
@@ -1,8 +1,27 @@
-import 'dart:io';
+import 'dart:io';
+import 'package:dio/dio.dart';
+import 'package:path/path.dart' as p;
+import 'package:path_provider/path_provider.dart';
+import 'package:archive/archive_io.dart';
 import 'package:freegosy/core/emulator/emulator_strategy.dart';
 import 'package:freegosy/core/romm/romm_models.dart';
 import 'package:freegosy/core/storage/directory_service.dart';
 
+class MissingRetroArchCoreException implements Exception {
+  final String coreName;
+  final String corePath;
+  final String exePath;
+
+  MissingRetroArchCoreException({
+    required this.coreName,
+    required this.corePath,
+    required this.exePath,
+  });
+
+  @override
+  String toString() => 'Missing RetroArch Core: $coreName at $corePath';
+}
+
 class RetroArchStrategy extends EmulatorStrategy {
   final DirectoryService _directoryService;
 
@@ -16,13 +35,13 @@ class RetroArchStrategy extends EmulatorStrategy {
 
   @override
   List<String> get supportedSlugs => [
-      'gba', 'gbc', 'gb', 'nes', 'snes', 'n64', 'nds', 
+      'gba', 'gbc', 'gb', 'nes', 'snes', 'n64', 'nds',
       'psx', 'ps1', 'playstation',
       'psp',
-      'segacd', 'saturn', 
+      'segacd', 'saturn',
       'dc', 'dreamcast',
       'megadrive', 'genesis', 'md',
-      'gamegear', 'atari2600', 'atari7800', 'lynx', 'neogeo', 
+      'gamegear', 'atari2600', 'atari7800', 'lynx', 'neogeo',
       'arcade', 'mame', 'pcengine', 'wonderswan', 'virtualboy', 'msx', 'dos'
     ];
 
@@ -77,7 +96,11 @@ class RetroArchStrategy extends EmulatorStrategy {
     final corePath = '$exeDir\\cores\\$coreName';
 
     if (!await File(corePath).exists()) {
-      throw Exception('Core $coreName not found at $corePath. Please download it in RetroArch first.');
+      throw MissingRetroArchCoreException(
+        coreName: coreName,
+        corePath: corePath,
+        exePath: normalizedExe,
+      );
     }
 
     await Process.start(
@@ -87,6 +110,66 @@ class RetroArchStrategy extends EmulatorStrategy {
     );
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+        emulatorId, getExecutableForPlatform());
+    if (exePath == null) {
+      throw Exception('$name not found. Please download it first.');
+    }
+
+    final normalizedExe = exePath.replaceAll('/', r'\');
+    final normalizedRom = romPath.replaceAll('/', r'\');
+    final coreName = _getCoreForSlug(game.platformSlug);
+
+    if (coreName == null) {
+      return await Process.start(
+        normalizedExe,
+        [normalizedRom],
+        mode: ProcessStartMode.normal,
+      );
+    }
+
+    final exeDir = File(normalizedExe).parent.path;
+    final corePath = '$exeDir\\cores\\$coreName';
+
+    if (!await File(corePath).exists()) {
+      throw MissingRetroArchCoreException(
+        coreName: coreName,
+        corePath: corePath,
+        exePath: normalizedExe,
+      );
+    }
+
+    return await Process.start(
+      normalizedExe,
+      ['-L', corePath, normalizedRom],
+      mode: ProcessStartMode.normal,
+    );
+  }
+
+  Future<void> downloadCore(String coreName, String coresDir, Dio dio) async {
+    final url = 'https://buildbot.libretro.com/nightly/windows/x86_64/latest/$coreName.zip';
+    final tempDir = await getTemporaryDirectory();
+    final zipPath = p.join(tempDir.path, '$coreName.zip');
+
+    try {
+      await dio.download(url, zipPath);
+      final bytes = await File(zipPath).readAsBytes();
+      final archive = ZipDecoder().decodeBytes(bytes);
+      for (final entry in archive) {
+        if (entry.isFile && entry.name.endsWith('.dll')) {
+          final outFile = File('$coresDir\\${entry.name}');
+          await outFile.parent.create(recursive: true);
+          await outFile.writeAsBytes(entry.content as List<int>);
+        }
+      }
+    } finally {
+      final f = File(zipPath);
+      if (await f.exists()) await f.delete();
+    }
+  }
+
   @override
   String resolveSavePath(Game game) {
     return '';
diff --git a/lib/core/emulator/strategies/rpcs3_strategy.dart b/lib/core/emulator/strategies/rpcs3_strategy.dart
index 9828c67..1df09ca 100644
--- a/lib/core/emulator/strategies/rpcs3_strategy.dart
+++ b/lib/core/emulator/strategies/rpcs3_strategy.dart
@@ -35,6 +35,15 @@ class Rpcs3Strategy extends EmulatorStrategy {
     await Process.start(exePath, [romPath], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategies/xemu_strategy.dart b/lib/core/emulator/strategies/xemu_strategy.dart
index 8d433e8..133272d 100644
--- a/lib/core/emulator/strategies/xemu_strategy.dart
+++ b/lib/core/emulator/strategies/xemu_strategy.dart
@@ -35,6 +35,15 @@ class XemuStrategy extends EmulatorStrategy {
     await Process.start(exePath, ['-dvd_path', romPath], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, ['-dvd_path', romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategies/xenia_strategy.dart b/lib/core/emulator/strategies/xenia_strategy.dart
index fac9475..7183a07 100644
--- a/lib/core/emulator/strategies/xenia_strategy.dart
+++ b/lib/core/emulator/strategies/xenia_strategy.dart
@@ -35,6 +35,15 @@ class XeniaStrategy extends EmulatorStrategy {
     await Process.start(exePath, [romPath], mode: ProcessStartMode.detached);
   }
 
+  @override
+  Future<Process?> launchWithHandle(Game game, String romPath) async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+      emulatorId, getExecutableForPlatform(),
+    );
+    if (exePath == null) throw Exception('$name not found. Please download it first.');
+    return await Process.start(exePath, [romPath], mode: ProcessStartMode.normal);
+  }
+
   @override
   String resolveSavePath(Game game) => '';
 }
\ No newline at end of file
diff --git a/lib/core/emulator/strategy_registry.dart b/lib/core/emulator/strategy_registry.dart
index 8c46f8f..b60d6cf 100644
--- a/lib/core/emulator/strategy_registry.dart
+++ b/lib/core/emulator/strategy_registry.dart
@@ -1,4 +1,6 @@
+import 'dart:io';
 import 'package:flutter/foundation.dart';
+import 'package:shared_preferences/shared_preferences.dart';
 import 'package:freegosy/core/emulator/emulator_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/retroarch_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/dolphin_strategy.dart';
@@ -8,6 +10,11 @@ import 'package:freegosy/core/emulator/strategies/pcsx2_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/azahar_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/cemu_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/duckstation_strategy.dart';
+import 'package:freegosy/core/emulator/strategies/flycast_strategy.dart';
+import 'package:freegosy/core/emulator/strategies/melonds_strategy.dart';
+import 'package:freegosy/core/emulator/strategies/mgba_strategy.dart';
+import 'package:freegosy/core/emulator/strategies/mame_strategy.dart';
+import 'package:freegosy/core/emulator/strategies/ppsspp_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/xemu_strategy.dart';
 import 'package:freegosy/core/emulator/strategies/xenia_strategy.dart';
 import 'package:freegosy/core/emulator/emulator_registry_data.dart';
@@ -17,9 +24,10 @@ import 'package:freegosy/core/emulator/strategies/windows_strategy.dart';
 class StrategyRegistry {
   final DirectoryService _directoryService;
   late final List<EmulatorStrategy> _strategies;
+  final Map<String, String> _slugPreferences = {};
 
   StrategyRegistry(this._directoryService) {
-    _strategies = [
+    final List<EmulatorStrategy> allPossibleStrategies = [
       RetroArchStrategy(_directoryService),
       DolphinStrategy(_directoryService),
       EdenStrategy(_directoryService),
@@ -28,14 +36,123 @@ class StrategyRegistry {
       AzaharStrategy(_directoryService),
       CemuStrategy(_directoryService),
       DuckstationStrategy(_directoryService),
+      FlycastStrategy(_directoryService),
+      MelonDSStrategy(_directoryService),
+      PPSSPPStrategy(_directoryService),
+      MGBAStrategy(_directoryService),
+      MAMEStrategy(_directoryService),
       XemuStrategy(_directoryService),
       XeniaStrategy(_directoryService),
       WindowsStrategy(_directoryService),
     ];
+
+    _strategies = allPossibleStrategies.where((strategy) {
+      final definition = getDefinition(strategy.emulatorId);
+      if (definition == null) return true; // Default to including if no definition found
+      final supported = List<String>.from(definition['supported_platforms'] ?? []);
+      if (Platform.isWindows && supported.contains('windows')) return true;
+      if (Platform.isLinux && supported.contains('linux')) return true;
+      return false;
+    }).toList();
+  }
+
+  Map<String, List<EmulatorStrategy>> detectConflicts() {
+    final Map<String, List<EmulatorStrategy>> slugToStrategies = {};
+    for (final strategy in _strategies) {
+      for (final slug in strategy.supportedSlugs) {
+        slugToStrategies.putIfAbsent(slug, () => []).add(strategy);
+      }
+    }
+
+    // Identify slugs with conflicts
+    final Map<String, List<EmulatorStrategy>> allConflicts = {};
+    slugToStrategies.forEach((slug, list) {
+      if (list.length > 1) {
+        allConflicts[slug] = list;
+      }
+    });
+
+    if (allConflicts.isEmpty) return {};
+
+    // Group slugs that have the exact same set of strategies
+    final Map<String, List<String>> groups = {}; // key: stringified sorted emulator IDs, value: list of slugs
+    allConflicts.forEach((slug, strategies) {
+      final ids = strategies.map((s) => s.emulatorId).toList()..sort();
+      final key = ids.join('|');
+      groups.putIfAbsent(key, () => []).add(slug);
+    });
+
+    final Map<String, List<EmulatorStrategy>> canonicalConflicts = {};
+    groups.forEach((key, slugs) {
+      // Pick canonical name: longest slug
+      final canonical = slugs.reduce((a, b) => a.length > b.length ? a : b);
+      // Strategies are the same for all slugs in this group
+      canonicalConflicts[canonical] = allConflicts[slugs.first]!;
+    });
+
+    return canonicalConflicts;
+  }
+
+  String? getPreferredEmulatorId(String slug) => _slugPreferences[slug];
+
+  Future<void> loadPreferences() async {
+    final prefs = await SharedPreferences.getInstance();
+    for (final key in prefs.getKeys()) {
+      if (key.startsWith('emulator_pref_')) {
+        final slug = key.replaceFirst('emulator_pref_', '');
+        final emulatorId = prefs.getString(key);
+        if (emulatorId != null) {
+          _slugPreferences[slug] = emulatorId;
+        }
+      }
+    }
+  }
+
+  Future<void> setPreference(String canonicalSlug, String emulatorId) async {
+    final prefs = await SharedPreferences.getInstance();
+    
+    // Find all slugs that belong to the same group as this canonicalSlug
+    final slugToStrategies = <String, List<String>>{};
+    for (final strategy in _strategies) {
+      for (final slug in strategy.supportedSlugs) {
+        slugToStrategies.putIfAbsent(slug, () => []).add(strategy.emulatorId);
+      }
+    }
+    
+    final targetStrategies = slugToStrategies[canonicalSlug];
+    if (targetStrategies == null) {
+      // Fallback: just set for this slug
+      await prefs.setString('emulator_pref_$canonicalSlug', emulatorId);
+      _slugPreferences[canonicalSlug] = emulatorId;
+      return;
+    }
+    
+    targetStrategies.sort();
+    final targetKey = targetStrategies.join('|');
+    
+    // Apply preference to all slugs with the same strategy set
+    for (final entry in slugToStrategies.entries) {
+      final ids = entry.value..sort();
+      if (ids.join('|') == targetKey) {
+        final slug = entry.key;
+        await prefs.setString('emulator_pref_$slug', emulatorId);
+        _slugPreferences[slug] = emulatorId;
+      }
+    }
   }
 
   EmulatorStrategy? getStrategyForSlug(String platformSlug) {
     if (kIsWeb) return null;
+
+    final preferredId = _slugPreferences[platformSlug];
+    if (preferredId != null) {
+      for (final strategy in _strategies) {
+        if (strategy.emulatorId == preferredId) {
+          return strategy;
+        }
+      }
+    }
+
     for (final strategy in _strategies) {
       if (strategy.supportedSlugs.contains(platformSlug)) {
         return strategy;
diff --git a/lib/core/extraction/extraction_service.dart b/lib/core/extraction/extraction_service.dart
new file mode 100644
index 0000000..5880ed9
--- /dev/null
+++ b/lib/core/extraction/extraction_service.dart
@@ -0,0 +1,70 @@
+import 'dart:io';
+import 'package:archive/archive_io.dart';
+import 'package:flutter/foundation.dart';
+import '../storage/directory_service.dart';
+
+Future<void> _extractZipIsolate(List<dynamic> args) async {
+  final bytes = args[0] as Uint8List;
+  final destDir = args[1] as String;
+  final archive = ZipDecoder().decodeBytes(bytes);
+  extractArchiveToDisk(archive, destDir);
+}
+
+class ExtractionService {
+  final DirectoryService directoryService;
+
+  ExtractionService(this.directoryService);
+
+  Future<void> extract(String archivePath, String destDir) async {
+    final pathLower = archivePath.toLowerCase();
+
+    if (pathLower.endsWith('.zip')) {
+      final fileBytes = await File(archivePath).readAsBytes();
+      await compute(_extractZipIsolate, [fileBytes, destDir]);
+    } else if (pathLower.endsWith('.7z')) {
+      final sevenZipExe = await directoryService.resolveSevenZipPath();
+      if (sevenZipExe == null) {
+        throw Exception('7zr.exe could not be initialized. Try reinstalling Freegosy.');
+      }
+      final result = await Process.run(
+        sevenZipExe,
+        ['x', archivePath, '-o$destDir', '-y'],
+        runInShell: false,
+      );
+      if (result.exitCode != 0) {
+        throw Exception('7z extraction failed: ${result.stderr}');
+      }
+    } else if (pathLower.endsWith('.exe')) {
+      // Self-extracting archive
+      var result = await Process.run(
+        archivePath,
+        ['-o$destDir', '-y'],
+        runInShell: false,
+      );
+      if (result.exitCode != 0) {
+        // Try as a plain self-extractor with no arguments
+        result = await Process.run(
+          archivePath,
+          [],
+          workingDirectory: destDir,
+        );
+      }
+    } else {
+      // Try ZIP magic bytes (PK = 0x50 0x4B)
+      bool isZip = false;
+      try {
+        final raf = await File(archivePath).open();
+        final header = await raf.read(4);
+        await raf.close();
+        isZip = header.length >= 2 && header[0] == 0x50 && header[1] == 0x4B;
+      } catch (_) {}
+
+      if (isZip) {
+        final fileBytes = await File(archivePath).readAsBytes();
+        await compute(_extractZipIsolate, [fileBytes, destDir]);
+      } else {
+        throw Exception('Unsupported archive format: $archivePath');
+      }
+    }
+  }
+}
diff --git a/lib/core/save/save_strategy.dart b/lib/core/save/save_strategy.dart
index e854ffd..52e9077 100644
--- a/lib/core/save/save_strategy.dart
+++ b/lib/core/save/save_strategy.dart
@@ -32,7 +32,7 @@ abstract class SaveStrategy {
       if (await bak.exists()) await bak.rename('$path.bak1');
       await file.copy('$path.bak');
     } catch (e) {
-      // Error handled silently
+      // silent
     }
   }
 
diff --git a/lib/core/save/save_sync_service.dart b/lib/core/save/save_sync_service.dart
index ddda8da..4b3139a 100644
--- a/lib/core/save/save_sync_service.dart
+++ b/lib/core/save/save_sync_service.dart
@@ -11,10 +11,17 @@ import 'strategies/windows_save_strategy.dart';
 import 'strategies/pcsx2_save_strategy.dart';
 import 'strategies/rpcs3_save_strategy.dart';
 import 'strategies/xenia_save_strategy.dart';
+import 'strategies/duckstation_save_strategy.dart';
+import 'strategies/melonds_save_strategy.dart';
+import 'strategies/mgba_save_strategy.dart';
+import 'strategies/ppsspp_save_strategy.dart';
+import 'strategies/cemu_save_strategy.dart';
+import '../emulator/strategy_registry.dart';
 
 class SaveSyncService {
   final RommService _rommService;
   final DirectoryService _directoryService;
+  final StrategyRegistry _strategyRegistry;
 
   late final RetroArchSaveStrategy _retroarch;
   late final DolphinSaveStrategy _dolphin;
@@ -23,35 +30,78 @@ class SaveSyncService {
   late final Pcsx2SaveStrategy _pcsx2;
   late final Rpcs3SaveStrategy _rpcs3;
   late final XeniaSaveStrategy _xenia;
+  late final DuckstationSaveStrategy _duckstation;
+  late final MelonDsSaveStrategy _melonds;
+  late final MgbaSaveStrategy _mgba;
+  late final PpssppSaveStrategy _ppsspp;
+  late final CemuSaveStrategy _cemu;
 
-  SaveSyncService(this._rommService, this._directoryService) {
+  SaveSyncService(this._rommService, this._directoryService, this._strategyRegistry) {
     _retroarch = RetroArchSaveStrategy(_directoryService);
     _dolphin = DolphinSaveStrategy(_directoryService);
     _eden = EdenSaveStrategy();
     _windows = WindowsSaveStrategy();
     _pcsx2 = Pcsx2SaveStrategy(_directoryService);
     _rpcs3 = Rpcs3SaveStrategy(_directoryService);
+    _xenia = XeniaSaveStrategy(_directoryService);
+    _duckstation = DuckstationSaveStrategy(_directoryService);
+    _melonds = MelonDsSaveStrategy();
+    _mgba = MgbaSaveStrategy();
+    _ppsspp = PpssppSaveStrategy(_directoryService);
+    _cemu = CemuSaveStrategy(_directoryService);
   }
 
   /// Returns the appropriate save strategy for [platformSlug], or null if unsupported.
   SaveStrategy? getStrategyForSlug(String? platformSlug) {
+    // print('[SaveSync] getStrategyForSlug called with: $platformSlug');
+    if (platformSlug != null) {
+      final preferredId = _strategyRegistry.getPreferredEmulatorId(platformSlug);
+      // print('[SaveSync] preferredId for $platformSlug: $preferredId');
+      if (preferredId != null) {
+        final id = preferredId.toLowerCase();
+        if (id == 'melonds') return _melonds;
+        if (id == 'mgba') return _mgba;
+        if (id == 'duckstation') return _duckstation;
+        if (id == 'retroarch') return _retroarch;
+        if (id == 'ppsspp') return _ppsspp;
+        if (id == 'cemu') return _cemu;
+        if (id == 'pcsx2') return _pcsx2;
+        if (id == 'rpcs3') return _rpcs3;
+        if (id == 'dolphin') return _dolphin;
+        if (id == 'xenia' || id == 'xenia_canary') return _xenia;
+        if (id == 'eden') return _eden;
+        if (id == 'windows') return _windows;
+      }
+    }
+
     switch (platformSlug?.toLowerCase()) {
       case 'gba':
       case 'gbc':
       case 'gb':
+      case 'game-boy-advance':
+      case 'game-boy-color':
+      case 'game-boy':
+        return _mgba;
       case 'snes':
       case 'nes':
       case 'n64':
+      case 'megadrive':
+      case 'genesis':
+      case 'md':
+        return _retroarch;
       case 'nds':
+      case 'nintendo-ds':
+      case 'ds':
+        return _melonds;
       case 'psx':
       case 'ps1':
       case 'playstation':
+        return _duckstation;
       case 'psp':
+      case 'playstation-portable':
+        return _ppsspp;
       case 'dc':
       case 'dreamcast':
-      case 'megadrive':
-      case 'genesis':
-      case 'md':
         return _retroarch;
       case 'gc':
       case 'ngc':
@@ -66,7 +116,6 @@ class SaveSyncService {
       case 'pc':
       case 'win':
         return _windows;
-      // These emulators don't have save sync yet
       case 'ps2':
       case 'playstation-2':
       case 'playstation2':
@@ -78,16 +127,17 @@ class SaveSyncService {
       case 'xbox360':
       case 'xbla':
         return _xenia;
+      case 'wiiu':
+      case 'wii-u':
+      case 'nintendo-wii-u':
+      case 'nintendo-wiiu':
+        return _cemu;
       case '3ds':
       case 'n3ds':
       case 'nintendo-3ds':
       case 'nintendo3ds':
       case 'new-nintendo-3ds':
       case 'new-nintendo-3ds-xl':
-      case 'wiiu':
-      case 'wii-u':
-      case 'nintendo-wii-u':
-      case 'nintendo-wiiu':
       case 'xbox':
         return null;
       default:
@@ -156,6 +206,7 @@ class SaveSyncService {
       if (save == null) {
         return false;
       }
+      // print('[Pull] getLatestSave result: $save');
 
       final downloadUrl = save['download_path'] as String? ?? save['url'] as String?;
       if (downloadUrl == null) {
@@ -166,13 +217,16 @@ class SaveSyncService {
       if (bytes == null) {
         return false;
       }
+      // print('[Pull] downloaded bytes: ${bytes?.length}');
 
       final filename = save['file_name'] as String? ??
           downloadUrl.split('/').last;
 
       final ok = await strategy.restoreSave(game, romPath, bytes, filename);
+      // print('[Pull] restoreSave result: $ok');
       return ok;
     } catch (e) {
+      // print('[Pull] error: $e'); // Removed print statement
       rethrow;
     }
   }
diff --git a/lib/core/save/strategies/cemu_save_strategy.dart b/lib/core/save/strategies/cemu_save_strategy.dart
new file mode 100644
index 0000000..814b2d0
--- /dev/null
+++ b/lib/core/save/strategies/cemu_save_strategy.dart
@@ -0,0 +1,69 @@
+import 'dart:io';
+import 'dart:typed_data';
+import 'package:archive/archive_io.dart';
+import '../../romm/romm_models.dart';
+import '../../storage/directory_service.dart';
+import '../save_strategy.dart';
+
+/// Save strategy for Cemu (Wii U).
+class CemuSaveStrategy extends SaveStrategy {
+  final DirectoryService _directoryService;
+
+  CemuSaveStrategy(this._directoryService);
+
+  @override
+  String get strategyId => 'cemu';
+
+  Future<String?> _getEmulatorDir() async {
+    final dir = await _directoryService.getEmulatorDirectory('cemu');
+    if (!await Directory(dir).exists()) return null;
+    return dir.replaceAll('/', '\\');
+  }
+
+  @override
+  Future<String?> getSaveDir(Game game, String romPath) async {
+    final emuDir = await _getEmulatorDir();
+    if (emuDir == null) return null;
+    return '$emuDir\\mlc01\\usr\\save';
+  }
+
+  @override
+  Future<List<File>> getSaveFiles(Game game, String romPath,
+      {DateTime? sessionStart, String syncMode = 'both'}) async {
+    final emuDir = await _getEmulatorDir();
+    if (emuDir == null) return [];
+    final saveRoot = Directory('$emuDir\\mlc01\\usr\\save\\00050000');
+    if (!await saveRoot.exists()) return [];
+    return [File(saveRoot.path)]; // Return the directory as a single item
+  }
+
+  @override
+  Future<bool> restoreSave(Game game, String destPath, Uint8List data, String filename) async {
+    try {
+      final emuDir = await _getEmulatorDir();
+      if (emuDir == null) return false;
+      final saveRoot = '$emuDir\\mlc01\\usr\\save';
+      await Directory(saveRoot).create(recursive: true);
+      if (filename.toLowerCase().endsWith('.zip')) {
+        final archive = ZipDecoder().decodeBytes(data);
+        for (final entry in archive) {
+          if (entry.name.contains('.bak')) continue;
+          final entryPath = entry.name.replaceAll('/', '\\');
+          if (entryPath.isEmpty || entryPath.endsWith('\\')) continue;
+          final targetPath = '$saveRoot\\$entryPath';          if (entry.isFile) {
+            await backupSave(targetPath);
+            final outFile = File(targetPath);
+            await outFile.parent.create(recursive: true);
+            await outFile.writeAsBytes(entry.content as List<int>);
+          } else {
+            await Directory(targetPath).create(recursive: true);
+          }
+        }
+        return true;
+      }
+      return false;
+    } catch (e) {
+      return false;
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/core/save/strategies/duckstation_save_strategy.dart b/lib/core/save/strategies/duckstation_save_strategy.dart
new file mode 100644
index 0000000..c4ee211
--- /dev/null
+++ b/lib/core/save/strategies/duckstation_save_strategy.dart
@@ -0,0 +1,122 @@
+import 'dart:io';
+import 'dart:typed_data';
+import 'package:archive/archive_io.dart';
+import '../../romm/romm_models.dart';
+import '../../storage/directory_service.dart';
+import '../save_strategy.dart';
+
+/// Save strategy for DuckStation (PlayStation 1).
+class DuckstationSaveStrategy extends SaveStrategy {
+  final DirectoryService _directoryService;
+
+  DuckstationSaveStrategy(this._directoryService);
+
+  @override
+  String get strategyId => 'duckstation';
+
+  Future<String?> _getBaseDir() async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+        'duckstation', 'duckstation-qt-x64-ReleaseLTCG.exe');
+    if (exePath == null) return null;
+    final emulatorDir = File(exePath).parent.path.replaceAll('/', '\\');
+
+    if (await File('$emulatorDir\\portable.txt').exists()) {
+      return emulatorDir;
+    }
+
+    try {
+      final result = await Process.run('cmd', ['/c', 'echo %LOCALAPPDATA%'], runInShell: false);
+      final localAppData = result.stdout.toString().trim();
+      if (localAppData.isNotEmpty && !localAppData.contains('%')) {
+        return '$localAppData\\DuckStation';
+      }
+    } catch (e) {
+      // ignore
+    }
+    return emulatorDir;
+  }
+
+  @override
+  Future<String?> getSaveDir(Game game, String romPath) async {
+    final baseDir = await _getBaseDir();
+    if (baseDir == null) return null;
+    return '$baseDir\\memcards';
+  }
+
+  @override
+  Future<List<File>> getSaveFiles(Game game, String romPath,
+      {DateTime? sessionStart, String syncMode = 'both'}) async {
+    final baseDir = await _getBaseDir();
+    if (baseDir == null) return [];
+
+    final result = <File>[];
+    final stem = getRomStem(game);
+
+    final memcardsDir = Directory('$baseDir\\memcards');
+    if (await memcardsDir.exists()) {
+      await for (final entity in memcardsDir.list()) {
+        if (entity is File &&
+            entity.path.toLowerCase().contains(stem.toLowerCase()) &&
+            entity.path.toLowerCase().endsWith('.mcd')) {
+          if (sessionStart != null) {
+            final stat = await entity.stat();
+            if (stat.modified.isBefore(sessionStart)) continue;
+          }
+          result.add(entity);
+        }
+      }
+    }
+
+    final statesDir = Directory('$baseDir\\savestates');
+    if (await statesDir.exists()) {
+      await for (final entity in statesDir.list()) {
+        if (entity is File && entity.path.toLowerCase().contains(stem.toLowerCase())) {
+          if (sessionStart != null) {
+            final stat = await entity.stat();
+            if (stat.modified.isBefore(sessionStart)) continue;
+          }
+          result.add(entity);
+        }
+      }
+    }
+
+    return result;
+  }
+
+  @override
+  Future<bool> restoreSave(
+      Game game, String destPath, Uint8List data, String filename) async {
+    try {
+      final baseDir = await _getBaseDir();
+      if (baseDir == null) return false;
+
+      if (filename.toLowerCase().endsWith('.zip')) {
+        final archive = ZipDecoder().decodeBytes(data);
+        for (final entry in archive) {
+          if (!entry.isFile) continue;
+          final entryLower = entry.name.toLowerCase();
+          final targetDir = entryLower.endsWith('.mcd')
+              ? '$baseDir\\memcards'
+              : '$baseDir\\savestates';
+
+          final targetPath = '$targetDir\\${entry.name.split('\\').last.split('/').last}';
+          await backupSave(targetPath);
+          final outFile = File(targetPath);
+          await outFile.parent.create(recursive: true);
+          await outFile.writeAsBytes(entry.content as List<int>);
+        }
+        return true;
+      }
+
+      final isState = !filename.toLowerCase().endsWith('.mcd');
+      final targetDir = isState ? '$baseDir\\savestates' : '$baseDir\\memcards';
+      final targetPath = '$targetDir\\$filename';
+      await Directory(targetDir).create(recursive: true);
+      await backupSave(targetPath);
+      await File(targetPath).writeAsBytes(data);
+      return true;
+    } catch (e) {
+      return false;
+    }
+  }
+}
diff --git a/lib/core/save/strategies/melonds_save_strategy.dart b/lib/core/save/strategies/melonds_save_strategy.dart
new file mode 100644
index 0000000..641e397
--- /dev/null
+++ b/lib/core/save/strategies/melonds_save_strategy.dart
@@ -0,0 +1,68 @@
+import 'dart:io';
+import 'dart:typed_data';
+import '../../romm/romm_models.dart';
+import '../save_strategy.dart';
+
+/// Save strategy for melonDS (Nintendo DS).
+class MelonDsSaveStrategy extends SaveStrategy {
+  @override
+  String get strategyId => 'melonds';
+
+  @override
+  Future<String?> getSaveDir(Game game, String romPath) async {
+    return File(romPath).parent.path;
+  }
+
+  @override
+  Future<List<File>> getSaveFiles(Game game, String romPath,
+      {DateTime? sessionStart, String syncMode = 'both'}) async {
+    final romFile = File(romPath);
+    final romStem = romFile.uri.pathSegments.last.replaceAll(RegExp(r'\.[^.]+$'), '');
+    final saveFile = File('${File(romPath).parent.path}/$romStem.sav');
+
+    if (await saveFile.exists()) {
+      if (sessionStart != null) {
+        final stat = await saveFile.stat();
+        if (stat.modified.isBefore(sessionStart)) return [];
+      }
+      return [saveFile];
+    } else {
+      // Fallback to getRomStem(game)
+      final fallbackStem = getRomStem(game);
+      final fallbackSaveFile = File('${File(romPath).parent.path}/$fallbackStem.sav');
+      if (await fallbackSaveFile.exists()) {
+        if (sessionStart != null) {
+          final stat = await fallbackSaveFile.stat();
+          if (stat.modified.isBefore(sessionStart)) return [];
+        }
+        return [fallbackSaveFile];
+      }
+    }
+    return [];
+  }
+
+  @override
+  Future<bool> restoreSave(
+      Game game, String destPath, Uint8List data, String filename) async {
+    try {
+      final romFile = File(destPath);
+      final romStem = romFile.uri.pathSegments.last.replaceAll(RegExp(r'\.[^.]+$'), '');
+      final targetPath = '${File(destPath).parent.path}/$romStem.sav';
+
+      if (await File(targetPath).exists()) {
+        await backupSave(targetPath);
+        await File(targetPath).writeAsBytes(data);
+        return true;
+      } else {
+        // Fallback to getRomStem(game)
+        final fallbackStem = getRomStem(game);
+        final fallbackTargetPath = '${File(destPath).parent.path}/$fallbackStem.sav';
+        await backupSave(fallbackTargetPath);
+        await File(fallbackTargetPath).writeAsBytes(data);
+        return true;
+      }
+    } catch (e) {
+      return false;
+    }
+  }
+}
diff --git a/lib/core/save/strategies/mgba_save_strategy.dart b/lib/core/save/strategies/mgba_save_strategy.dart
new file mode 100644
index 0000000..6cdde1b
--- /dev/null
+++ b/lib/core/save/strategies/mgba_save_strategy.dart
@@ -0,0 +1,68 @@
+import 'dart:io';
+import 'dart:typed_data';
+import '../../romm/romm_models.dart';
+import '../save_strategy.dart';
+
+/// Save strategy for mGBA (GBA/GBC/GB).
+class MgbaSaveStrategy extends SaveStrategy {
+  @override
+  String get strategyId => 'mgba';
+
+  @override
+  Future<String?> getSaveDir(Game game, String romPath) async {
+    return File(romPath).parent.path;
+  }
+
+  @override
+  Future<List<File>> getSaveFiles(Game game, String romPath,
+      {DateTime? sessionStart, String syncMode = 'both'}) async {
+    final romFile = File(romPath);
+    final romStem = romFile.uri.pathSegments.last.replaceAll(RegExp(r'\.[^.]+$'), '');
+    final saveFile = File('${File(romPath).parent.path}/$romStem.sav');
+
+    if (await saveFile.exists()) {
+      if (sessionStart != null) {
+        final stat = await saveFile.stat();
+        if (stat.modified.isBefore(sessionStart)) return [];
+      }
+      return [saveFile];
+    } else {
+      // Fallback to getRomStem(game)
+      final fallbackStem = getRomStem(game);
+      final fallbackSaveFile = File('${File(romPath).parent.path}/$fallbackStem.sav');
+      if (await fallbackSaveFile.exists()) {
+        if (sessionStart != null) {
+          final stat = await fallbackSaveFile.stat();
+          if (stat.modified.isBefore(sessionStart)) return [];
+        }
+        return [fallbackSaveFile];
+      }
+    }
+    return [];
+  }
+
+  @override
+  Future<bool> restoreSave(
+      Game game, String destPath, Uint8List data, String filename) async {
+    try {
+      final romFile = File(destPath);
+      final romStem = romFile.uri.pathSegments.last.replaceAll(RegExp(r'\.[^.]+$'), '');
+      final targetPath = '${File(destPath).parent.path}/$romStem.sav';
+
+      if (await File(targetPath).exists()) {
+        await backupSave(targetPath);
+        await File(targetPath).writeAsBytes(data);
+        return true;
+      } else {
+        // Fallback to getRomStem(game)
+        final fallbackStem = getRomStem(game);
+        final fallbackTargetPath = '${File(destPath).parent.path}/$fallbackStem.sav';
+        await backupSave(fallbackTargetPath);
+        await File(fallbackTargetPath).writeAsBytes(data);
+        return true;
+      }
+    } catch (e) {
+      return false;
+    }
+  }
+}
diff --git a/lib/core/save/strategies/pcsx2_save_strategy.dart b/lib/core/save/strategies/pcsx2_save_strategy.dart
index 7a44a61..abc7446 100644
--- a/lib/core/save/strategies/pcsx2_save_strategy.dart
+++ b/lib/core/save/strategies/pcsx2_save_strategy.dart
@@ -39,35 +39,31 @@ class Pcsx2SaveStrategy extends SaveStrategy {
     final result = <File>[];
 
     // Memory cards — scan all .ps2 files in memcards folder
-    if (syncMode == 'saves' || syncMode == 'both') {
-      final memcardsDir = Directory('$exeDir\\memcards');
-      if (await memcardsDir.exists()) {
-        await for (final entity in memcardsDir.list()) {
-          if (entity is! File) continue;
-          if (!entity.path.toLowerCase().endsWith('.ps2')) continue;
-          if (sessionStart != null) {
-            final stat = await entity.stat();
-            if (stat.modified.isBefore(sessionStart)) continue;
-          }
-          result.add(entity);
+    final memcardsDir = Directory('$exeDir\\memcards');
+    if (await memcardsDir.exists()) {
+      await for (final entity in memcardsDir.list()) {
+        if (entity is! File) continue;
+        if (!entity.path.toLowerCase().endsWith('.ps2')) continue;
+        if (sessionStart != null) {
+          final stat = await entity.stat();
+          if (stat.modified.isBefore(sessionStart)) continue;
         }
+        result.add(entity);
       }
     }
 
     // Save states — named after ROM stem
-    if (syncMode == 'states' || syncMode == 'both') {
-      final stem = getRomStem(game);
-      final statesDir = Directory('$exeDir\\sstates');
-      if (await statesDir.exists()) {
-        await for (final entity in statesDir.list()) {
-          if (entity is! File) continue;
-          if (!entity.path.contains(stem)) continue;
-          if (sessionStart != null) {
-            final stat = await entity.stat();
-            if (stat.modified.isBefore(sessionStart)) continue;
-          }
-          result.add(entity);
+    final stem = getRomStem(game);
+    final statesDir = Directory('$exeDir\\sstates');
+    if (await statesDir.exists()) {
+      await for (final entity in statesDir.list()) {
+        if (entity is! File) continue;
+        if (!entity.path.contains(stem)) continue;
+        if (sessionStart != null) {
+          final stat = await entity.stat();
+          if (stat.modified.isBefore(sessionStart)) continue;
         }
+        result.add(entity);
       }
     }
 
diff --git a/lib/core/save/strategies/ppsspp_save_strategy.dart b/lib/core/save/strategies/ppsspp_save_strategy.dart
new file mode 100644
index 0000000..b62336f
--- /dev/null
+++ b/lib/core/save/strategies/ppsspp_save_strategy.dart
@@ -0,0 +1,108 @@
+import 'dart:io';
+import 'dart:typed_data';
+import 'package:archive/archive_io.dart';
+import '../../romm/romm_models.dart';
+import '../../storage/directory_service.dart';
+import '../save_strategy.dart';
+
+/// Save strategy for PPSSPP (PSP).
+class PpssppSaveStrategy extends SaveStrategy {
+  final DirectoryService _directoryService;
+
+  PpssppSaveStrategy(this._directoryService);
+
+  @override
+  String get strategyId => 'ppsspp';
+
+  Future<String?> _getEmulatorDir() async {
+    final exePath = await _directoryService.findEmulatorExecutable(
+        'ppsspp', 'PPSSPPWindows64.exe');
+    if (exePath == null) return null;
+    return File(exePath).parent.path.replaceAll('/', '\\');
+  }
+
+  @override
+  Future<String?> getSaveDir(Game game, String romPath) async {
+    final emuDir = await _getEmulatorDir();
+    if (emuDir == null) return null;
+    return '$emuDir\\memstick\\PSP\\SAVEDATA';
+  }
+
+  @override
+  Future<List<File>> getSaveFiles(Game game, String romPath,
+      {DateTime? sessionStart, String syncMode = 'both'}) async {
+    final emuDir = await _getEmulatorDir();
+    if (emuDir == null) return [];
+
+    final result = <File>[];
+    final stem = getRomStem(game);
+
+    final saveDataDir = Directory('$emuDir\\memstick\\PSP\\SAVEDATA');
+    if (await saveDataDir.exists()) {
+      bool hasFiles = false;
+      await for (final _ in saveDataDir.list(recursive: true)) {
+        hasFiles = true;
+        break;
+      }
+      if (hasFiles) {
+        result.add(File(saveDataDir.path));
+      }
+    }
+
+    final statesDir = Directory('$emuDir\\memstick\\PSP\\PPSSPP_STATE');
+    if (await statesDir.exists()) {
+      final stateFile = File('${statesDir.path}\\$stem.ppst');
+      if (await stateFile.exists()) {
+        if (sessionStart == null || (await stateFile.stat()).modified.isAfter(sessionStart)) {
+          result.add(stateFile);
+        }
+      }
+    }
+
+    return result;
+  }
+
+  @override
+  Future<bool> restoreSave(
+      Game game, String destPath, Uint8List data, String filename) async {
+    try {
+      final emuDir = await _getEmulatorDir();
+      if (emuDir == null) return false;
+
+      if (filename.toLowerCase().endsWith('.zip')) {
+        final archive = ZipDecoder().decodeBytes(data);
+        final targetBaseDir = '$emuDir\\memstick\\PSP\\SAVEDATA';
+        for (final entry in archive) {
+          if (entry.name.contains('.bak')) continue;
+          final entryPath = entry.name.replaceAll('/', '\\');
+          final segments = entryPath.split('\\');
+          final strippedPath = segments.length > 1 ? segments.skip(1).join('\\') : entryPath;
+          if (strippedPath.isEmpty) continue;
+          final targetPath = '$targetBaseDir\\$strippedPath';
+          if (entry.isFile) {
+            await backupSave(targetPath);
+            final outFile = File(targetPath);
+            await outFile.parent.create(recursive: true);
+            await outFile.writeAsBytes(entry.content as List<int>);
+          } else {
+            await Directory(targetPath).create(recursive: true);
+          }
+        }
+        return true;
+      }
+
+      if (filename.toLowerCase().endsWith('.ppst')) {
+        final targetPath = '$emuDir\\memstick\\PSP\\PPSSPP_STATE\\$filename';
+        await backupSave(targetPath);
+        final outFile = File(targetPath);
+        await outFile.parent.create(recursive: true);
+        await outFile.writeAsBytes(data);
+        return true;
+      }
+
+      return false;
+    } catch (e) {
+      return false;
+    }
+  }
+}
diff --git a/lib/core/save/strategies/retroarch_save_strategy.dart b/lib/core/save/strategies/retroarch_save_strategy.dart
index b9accd9..4767e4e 100644
--- a/lib/core/save/strategies/retroarch_save_strategy.dart
+++ b/lib/core/save/strategies/retroarch_save_strategy.dart
@@ -4,6 +4,7 @@ import 'dart:typed_data';
 import '../../romm/romm_models.dart';
 import '../../storage/directory_service.dart';
 import '../save_strategy.dart';
+import 'package:path/path.dart' as p; // Import path package
 
 /// Save strategy for RetroArch emulator.
 ///
@@ -17,23 +18,24 @@ class RetroArchSaveStrategy extends SaveStrategy {
   @override
   String get strategyId => 'retroarch';
 
+  // _CoreInfo maps platform slugs to RetroArch core info, including save and state directories.
   static const Map<String, _CoreInfo> _coreMap = {
-    'gba':       _CoreInfo('mgba_libretro',            'mGBA',                'mGBA'),
-    'gbc':       _CoreInfo('mgba_libretro',            'mGBA',                'mGBA'),
-    'gb':        _CoreInfo('mgba_libretro',            'mGBA',                'mGBA'),
-    'snes':      _CoreInfo('snes9x_libretro',          'Snes9X',              'Snes9X'),
-    'nes':       _CoreInfo('fceumm_libretro',          'FCEUmm',              'FCEUmm'),
-    'n64':       _CoreInfo('mupen64plus_next_libretro', 'Mupen64Plus-Next',   'Mupen64Plus-Next'),
-    'nds':       _CoreInfo('desmume2015_libretro',     'DeSmuME 2015',        'DeSmuME 2015'),
-    'psx':       _CoreInfo('pcsx_rearmed_libretro',    'PCSX-ReARMed',        'PCSX-ReARMed'),
-    'psp':       _CoreInfo('ppsspp_libretro',          'PPSSPP/PSP/SAVEDATA', 'PPSSPP'),
-    'dreamcast': _CoreInfo('flycast_libretro',         'Flycast',             'Flycast'),
-    'megadrive': _CoreInfo('genesis_plus_gx_libretro', 'Genesis Plus GX',    'Genesis Plus GX'),
-    'dc':        _CoreInfo('flycast_libretro', 'Flycast', 'Flycast'),
-    'ps1':       _CoreInfo('pcsx_rearmed_libretro', 'PCSX-ReARMed', 'PCSX-ReARMed'),
-    'playstation': _CoreInfo('pcsx_rearmed_libretro', 'PCSX-ReARMed', 'PCSX-ReARMed'),
-    'md':        _CoreInfo('genesis_plus_gx_libretro', 'Genesis Plus GX', 'Genesis Plus GX'),
-    'genesis':   _CoreInfo('genesis_plus_gx_libretro', 'Genesis Plus GX', 'Genesis Plus GX'),
+    'gba':       _CoreInfo('mgba_libretro',            'GBA',                'States/GBA'),
+    'gbc':       _CoreInfo('mgba_libretro',            'GBA',                'States/GBA'), // mGBA uses the same save folder for GBA/GBC/GB
+    'gb':        _CoreInfo('mgba_libretro',            'GBA',                'States/GBA'),
+    'snes':      _CoreInfo('snes9x_libretro',          'SNES',               'States/SNES'),
+    'nes':       _CoreInfo('fceumm_libretro',          'NES',                'States/NES'),
+    'n64':       _CoreInfo('mupen64plus_next_libretro', 'N64',                'States/N64'),
+    'nds':       _CoreInfo('desmume2015_libretro',     'NDS',                'States/NDS'),
+    'psx':       _CoreInfo('pcsx_rearmed_libretro',    'PSX',                'States/PSX'),
+    'psp':       _CoreInfo('ppsspp_libretro',          'PPSSPP/PSP/SAVEDATA', 'PPSSPP'), // Note: saveFolder is 'PPSSPP/PSP/SAVEDATA', statesFolder is 'PPSSPP'
+    'playstation': _CoreInfo('pcsx_rearmed_libretro', 'PCSX-ReARMed', 'PCSX-ReARMed'), // Assuming this is also PSX and needs save/state dir logic
+    'playstation-portable': _CoreInfo('ppsspp_libretro', 'PPSSPP/PSP/SAVEDATA', 'PPSSPP'), // Alias for PSP
+    'dreamcast': _CoreInfo('flycast_libretro',         'Dreamcast',          'States/Dreamcast'),
+    'dc':        _CoreInfo('flycast_libretro',         'Dreamcast',          'States/Dreamcast'), // Alias for Dreamcast
+    'megadrive': _CoreInfo('genesis_plus_gx_libretro', 'Mega Drive',         'States/Mega Drive'),
+    'genesis':   _CoreInfo('genesis_plus_gx_libretro', 'Mega Drive',         'States/Mega Drive'), // Alias for Mega Drive
+    'md':        _CoreInfo('genesis_plus_gx_libretro', 'Mega Drive',         'States/Mega Drive'), // Alias for Mega Drive
   };
 
   @override
@@ -46,7 +48,8 @@ class RetroArchSaveStrategy extends SaveStrategy {
     if (exePath == null) return null;
 
     final exeDir = File(exePath).parent.path;
-    return '$exeDir/saves/${coreInfo.saveFolder}';
+    // The saveFolder in _coreMap is relative to the RetroArch installation directory.
+    return p.join(exeDir, 'saves', coreInfo.saveFolder);
   }
 
   @override
@@ -59,32 +62,63 @@ class RetroArchSaveStrategy extends SaveStrategy {
     if (exePath == null) return [];
     final exeDir = File(exePath).parent.path;
 
-    final stem = getRomStem(game);
-    final candidates = <File>[];
-
-    if (syncMode == 'saves' || syncMode == 'both') {
-      final savesDir = '$exeDir/saves/${coreInfo.saveFolder}';
-      candidates.add(File('$savesDir/$stem.srm'));
+    final stem = getRomStem(game); // Assuming getRomStem is available and correct for generating base filename
+
+    final List<File> filesToReturn = [];
+
+    // Special case for PSP saves
+    if (slug == 'psp' || slug == 'playstation-portable') {
+      if (syncMode == 'saves' || syncMode == 'both') {
+        final pspPath = '$exeDir\\saves\\PPSSPP\\PSP'.replaceAll('/', '\\');
+        final pspDir = Directory(pspPath);
+        if (await pspDir.exists()) {
+          bool hasFiles = false;
+          await for (final _ in pspDir.list(recursive: true)) {
+            hasFiles = true;
+            break;
+          }
+          if (hasFiles) {
+            filesToReturn.add(File(pspPath));
+          }
+        }
+      }
+    } else {
+      // Existing logic for non-PSP saves (e.g., SRM files)
+      if (syncMode == 'saves' || syncMode == 'both') {
+        final savesDir = p.join(exeDir, 'saves', coreInfo.saveFolder);
+        filesToReturn.add(File(p.join(savesDir, '$stem.srm')));
+      }
     }
 
+    // Handle States (regardless of platform)
     if (syncMode == 'states' || syncMode == 'both') {
-      final statesDir = '$exeDir/states/${coreInfo.statesFolder}';
-      candidates.add(File('$statesDir/$stem.state.auto'));
-      for (int i = 0; i <= 9; i++) {
-        candidates.add(File('$statesDir/$stem.state$i'));
+      final statesDir = p.join(exeDir, 'states', coreInfo.statesFolder);
+
+      // Derive stem from romPath as fallback
+      final romStem = File(romPath).uri.pathSegments.last.replaceAll(RegExp(r'\.[^.]+$'), '');
+
+      // Check state files for both stems
+      for (final checkStem in [stem, romStem]) {
+        filesToReturn.add(File('$statesDir/$checkStem.state.auto'));
+        for (int i = 0; i <= 9; i++) {
+          filesToReturn.add(File('$statesDir/$checkStem.state$i'));
+        }
       }
     }
 
-    final result = <File>[];
-    for (final f in candidates) {
-      if (!await f.exists()) continue;
-      if (sessionStart != null) {
+    // Filter out non-existent files and apply sessionStart filter
+    final finalResult = <File>[];
+    for (final f in filesToReturn) {
+      final existsAsFile = await f.exists();
+      final existsAsDir = await Directory(f.path).exists();
+      if (!existsAsFile && !existsAsDir) continue;
+      if (sessionStart != null && existsAsFile) {
         final stat = await f.stat();
         if (stat.modified.isBefore(sessionStart)) continue;
       }
-      result.add(f);
+      finalResult.add(f);
     }
-    return result;
+    return finalResult;
   }
 
   @override
@@ -100,14 +134,14 @@ class RetroArchSaveStrategy extends SaveStrategy {
 
       final isState = filename.contains('.state');
       final targetDir = isState
-          ? '$exeDir/states/${coreInfo.statesFolder}'
-          : '$exeDir/saves/${coreInfo.saveFolder}';
+          ? p.join(exeDir, 'states', coreInfo.statesFolder)
+          : p.join(exeDir, 'saves', coreInfo.saveFolder);
 
       final dir = Directory(targetDir);
       if (!await dir.exists()) await dir.create(recursive: true);
 
-      final targetPath = '$targetDir/$filename';
-      await backupSave(targetPath);
+      final targetPath = p.join(targetDir, filename);
+      await backupSave(targetPath); // Backup existing file
       await File(targetPath).writeAsBytes(data);
       return true;
     } catch (e) {
@@ -121,4 +155,4 @@ class _CoreInfo {
   final String saveFolder;
   final String statesFolder;
   const _CoreInfo(this.coreName, this.saveFolder, this.statesFolder);
-}
\ No newline at end of file
+}
diff --git a/lib/core/storage/directory_service.dart b/lib/core/storage/directory_service.dart
index 90f3fa9..e71e873 100644
--- a/lib/core/storage/directory_service.dart
+++ b/lib/core/storage/directory_service.dart
@@ -36,6 +36,7 @@ class DirectoryService {
 
   late String romsRootPath;
   late String emulatorsRootPath;
+  final Map<String, String> _emulatorPathOverrides = {};
 
   DirectoryService();
 
@@ -45,8 +46,30 @@ class DirectoryService {
     emulatorsRootPath = prefs.getString(_emulatorsRootPathKey) ?? _defaultEmulatorsPath;
     await _ensureDirectoryExists(romsRootPath);
     await _ensureDirectoryExists(emulatorsRootPath);
+    await loadEmulatorPathOverrides();
   }
 
+  Future<void> loadEmulatorPathOverrides() async {
+    final prefs = await SharedPreferences.getInstance();
+    for (final key in prefs.getKeys()) {
+      if (key.startsWith('emu_path_')) {
+        final emuId = key.replaceFirst('emu_path_', '');
+        final path = prefs.getString(key);
+        if (path != null) {
+          _emulatorPathOverrides[emuId] = path;
+        }
+      }
+    }
+  }
+
+  Future<void> setEmulatorPathOverride(String emulatorId, String path) async {
+    final prefs = await SharedPreferences.getInstance();
+    await prefs.setString('emu_path_$emulatorId', path);
+    _emulatorPathOverrides[emulatorId] = path;
+  }
+
+  String? getEmulatorPathOverride(String emulatorId) => _emulatorPathOverrides[emulatorId];
+
   Future<void> _ensureDirectoryExists(String path) async {
     final directory = Directory(path);
     if (!await directory.exists()) {
@@ -152,9 +175,19 @@ class DirectoryService {
         return null;
       }
       if (appData.isEmpty || appData.contains('%APPDATA%')) return null;
+    } else if (defaultTargetPlatform == TargetPlatform.linux) {
+      // Linux: Try to find system 7z
+      try {
+        final result = await Process.run('which', ['7z']);
+        if (result.exitCode == 0) {
+          return result.stdout.toString().trim();
+        }
+      } catch (e) {
+        // ignore
+      }
+      return null;
     } else {
-      // macOS/Linux: no 7zr needed, system 7z is installable via package managers
-      // Return null for now — future: resolve via `which 7z`
+      // macOS: no 7zr needed
       return null;
     }
 
@@ -173,6 +206,9 @@ class DirectoryService {
   }
 
   Future<String> getEmulatorDirectory(String emulatorId) async {
+    final override = getEmulatorPathOverride(emulatorId);
+    if (override != null) return override;
+
     final dirPath = '$emulatorsRootPath/$emulatorId';
     await _ensureDirectoryExists(dirPath);
     return dirPath;
diff --git a/lib/providers/download_provider.dart b/lib/providers/download_provider.dart
index db832c1..6c386bd 100644
--- a/lib/providers/download_provider.dart
+++ b/lib/providers/download_provider.dart
@@ -1,13 +1,30 @@
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:dio/dio.dart';
 import '../core/downloader/download_service.dart';
+import '../core/emulator/emulator_download_service.dart';
+import '../core/extraction/extraction_service.dart';
 import '../core/romm/romm_models.dart';
 import 'romm_provider.dart';
 
 final downloadServiceProvider = FutureProvider<DownloadService?>((ref) async {
   final directoryService = await ref.watch(directoryServiceProvider.future);
   if (directoryService == null) return null;
-  return DownloadService(dio: Dio(), directoryService: directoryService);
+  return DownloadService(
+    dio: Dio(),
+    directoryService: directoryService,
+    extractionService: ExtractionService(directoryService),
+  );
+});
+
+final emulatorDownloadServiceProvider =
+    FutureProvider<EmulatorDownloadService?>((ref) async {
+  final directoryService = await ref.watch(directoryServiceProvider.future);
+  if (directoryService == null) return null;
+  return EmulatorDownloadService(
+    Dio(),
+    directoryService,
+    ExtractionService(directoryService),
+  );
 });
 
 final downloadProvider =
@@ -20,7 +37,8 @@ class DownloadNotifier extends StateNotifier<Map<String, DownloadProgress>> {
 
   DownloadNotifier(this._ref) : super({});
 
-  Future<void> startDownload(Game game, String downloadUrl, {Map<String, String>? headers}) async {
+  Future<void> startDownload(Game game, String downloadUrl,
+      {Map<String, String>? headers}) async {
     final service = await _ref.read(downloadServiceProvider.future);
     if (service == null) return;
     service.download(game, downloadUrl, headers: headers).listen((progress) {
@@ -28,6 +46,15 @@ class DownloadNotifier extends StateNotifier<Map<String, DownloadProgress>> {
     });
   }
 
+  Future<void> startEmulatorDownload(
+      String emulatorId, String emulatorName) async {
+    final service = await _ref.read(emulatorDownloadServiceProvider.future);
+    if (service == null) return;
+    service.downloadEmulator(emulatorId).listen((progress) {
+      state = {...state, emulatorId: progress};
+    });
+  }
+
   void removeDownload(String gameId) {
     final newState = Map<String, DownloadProgress>.from(state);
     newState.remove(gameId);
diff --git a/lib/providers/library_provider.dart b/lib/providers/library_provider.dart
index 7432bda..e394015 100644
--- a/lib/providers/library_provider.dart
+++ b/lib/providers/library_provider.dart
@@ -233,7 +233,6 @@ final platformsProvider = FutureProvider<List<Platform>>((ref) async {
         final fresh = await service.getPlatforms();
         if (fresh.isNotEmpty) {
           await _savePlatformsCache(fresh);
-          ref.invalidateSelf();
         }
       } catch (_) {}
     });
@@ -261,7 +260,6 @@ final allGamesProvider = FutureProvider<List<Game>>((ref) async {
         final fresh = await service.getAllGames();
         if (fresh.isNotEmpty) {
           await _saveGamesCache(fresh);
-          ref.invalidateSelf();
         }
       } catch (_) {}
     });
diff --git a/lib/providers/romm_provider.dart b/lib/providers/romm_provider.dart
index ff962b6..3924d3a 100644
--- a/lib/providers/romm_provider.dart
+++ b/lib/providers/romm_provider.dart
@@ -40,11 +40,12 @@ final directoryServiceProvider = FutureProvider<DirectoryService?>((ref) async {
 });
 
 // Provider for StrategyRegistry
-final strategyRegistryProvider = Provider<StrategyRegistry?>((ref) {
+final strategyRegistryProvider = FutureProvider<StrategyRegistry?>((ref) async {
   final directoryService = ref.watch(directoryServiceProvider).value;
   if (directoryService != null) {
     try {
       final registry = StrategyRegistry(directoryService);
+      await registry.loadPreferences(); // Await preferences loading
       // Load persisted Windows exe overrides
       final winStrategy = registry.getStrategyForSlug('windows');
       if (winStrategy is WindowsStrategy) {
@@ -59,12 +60,12 @@ final strategyRegistryProvider = Provider<StrategyRegistry?>((ref) {
 });
 
 // SaveSyncService provider
-final saveSyncServiceProvider = Provider<SaveSyncService?>((ref) {
+final saveSyncServiceProvider = FutureProvider<SaveSyncService?>((ref) async {
   final rommService = ref.watch(rommServiceProvider);
   final directoryService = ref.watch(directoryServiceProvider).asData?.value;
-  if (rommService == null || directoryService == null) return null;
-  final service = SaveSyncService(rommService, directoryService);
-  // Load persisted Windows save path overrides
+  final strategyRegistry = await ref.watch(strategyRegistryProvider.future);
+  if (rommService == null || directoryService == null || strategyRegistry == null) return null;
+  final service = SaveSyncService(rommService, directoryService, strategyRegistry);
   service.windowsSaveStrategy.loadPersistedOverrides();
   return service;
 });
diff --git a/lib/ui/screens/library_screen.dart b/lib/ui/screens/library_screen.dart
index e4379e0..cc7ebe7 100644
--- a/lib/ui/screens/library_screen.dart
+++ b/lib/ui/screens/library_screen.dart
@@ -1,3 +1,7 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:dio/dio.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:shared_preferences/shared_preferences.dart';
@@ -6,11 +10,12 @@ import '../../providers/download_provider.dart';
 import '../../providers/romm_provider.dart';
 import '../../core/storage/directory_service.dart';
 import '../../core/romm/romm_models.dart';
+import '../../core/emulator/strategies/windows_strategy.dart';
+import '../../core/emulator/strategies/retroarch_strategy.dart';
 import '../widgets/game_card.dart';
 import '../widgets/platform_filter_bar.dart';
 import '../widgets/windows_game_config_dialog.dart';
-import '../../core/emulator/strategies/windows_strategy.dart';
-import 'dart:convert';
+import 'library_skeleton.dart';
 
 class LibraryScreen extends ConsumerStatefulWidget {
   const LibraryScreen({super.key});
@@ -26,15 +31,12 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
   Future<void> _loadDownloadStates(
       DirectoryService dirService, List<Game> games) async {
     if (_downloadStatesLoaded) return;
-    
     final results = await Future.wait(
       games.map((game) async {
-        final isDownloaded = 
-          await dirService.isRomDownloaded(game);
+        final isDownloaded = await dirService.isRomDownloaded(game);
         return MapEntry(game.id, isDownloaded);
       }),
     );
-    
     if (mounted) {
       setState(() {
         _downloadedStates = Map.fromEntries(results);
@@ -53,12 +55,40 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
     }
   }
 
-  Future<void> _handleLaunch(BuildContext context, WidgetRef ref, game) async {
-    final registry = ref.read(strategyRegistryProvider);
-    final strategy = registry?.getStrategyForSlug(game.platformSlug ?? '');
+  void _startDownload(BuildContext context, WidgetRef ref, Game game) {
+    final service = ref.read(rommServiceProvider);
+    if (service == null) {
+      ScaffoldMessenger.of(context).showSnackBar(
+        const SnackBar(content: Text('Not connected to RomM')),
+      );
+      return;
+    }
+    final url = service.getDownloadUrl(game);
+    final basicAuth =
+        'Basic ${base64Encode(utf8.encode('${service.config.username}:${service.config.password}'))}';
+    final headers = <String, String>{'Authorization': basicAuth};
+    ref.read(downloadProvider.notifier).startDownload(game, url, headers: headers);
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(content: Text('Downloading ${game.name}...')),
+    );
+    final dirService = ref.read(directoryServiceProvider).asData?.value;
+    if (dirService != null) {
+      Future.delayed(const Duration(seconds: 2), () {
+        _refreshDownloadState(dirService, game);
+      });
+    }
+  }
+
+  Future<void> _handleLaunch(BuildContext context, WidgetRef ref, Game game) async {
+    final messenger = ScaffoldMessenger.of(context);
+
+    // Ensure strategy registry preferences are loaded
+    final registryReady = await ref.read(strategyRegistryProvider.future);
+    if (registryReady == null) return;
+    final strategy = registryReady.getStrategyForSlug(game.platformSlug ?? '');
 
     if (strategy == null) {
-      ScaffoldMessenger.of(context).showSnackBar(
+      messenger.showSnackBar(
         SnackBar(
             content: Text(
                 'No emulator configured for ${game.platformDisplayName ?? game.platformSlug ?? 'this platform'}')),
@@ -66,10 +96,13 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
       return;
     }
 
+    // Wait for save sync service to be ready (FutureProvider chain)
+    final syncService = await ref.read(saveSyncServiceProvider.future);
+
     final dir = await ref.read(directoryServiceProvider.future);
     if (!context.mounted) return;
     if (dir == null) {
-      ScaffoldMessenger.of(context).showSnackBar(
+      messenger.showSnackBar(
         const SnackBar(content: Text('Storage service not available')),
       );
       return;
@@ -118,7 +151,6 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
           ],
         ),
       );
-
       if (!context.mounted) return;
       if (shouldDownload == true) {
         _startDownload(context, ref, game);
@@ -126,18 +158,22 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
       return;
     }
 
-    // Pull latest cloud save before launching
-    final syncService = ref.read(saveSyncServiceProvider);
     if (syncService != null) {
-      // Push local saves first so nothing is lost, then pull cloud save
       final syncMode = ref.read(retroarchSyncModeProvider);
+      messenger.showSnackBar(
+        SnackBar(
+          content: Text('Pushing saves for ${game.name}...'),
+          duration: const Duration(seconds: 30),
+        ),
+      );
       await syncService.pushSaves(game, existingRomPath, syncMode: syncMode);
       if (!context.mounted) return;
+      messenger.clearSnackBars();
       try {
         final pulled = await syncService.pullSave(game, existingRomPath);
         if (!context.mounted) return;
         if (pulled) {
-          ScaffoldMessenger.of(context).showSnackBar(
+          messenger.showSnackBar(
             const SnackBar(
                 content: Text('Cloud save restored'),
                 duration: Duration(seconds: 2)),
@@ -169,9 +205,97 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
     }
 
     try {
-      await strategy.launch(game, existingRomPath);
+      messenger.showSnackBar(
+        SnackBar(
+          content: Text('Launching ${game.name}...'),
+          duration: const Duration(seconds: 3),
+        ),
+      );
+
+      // Try to get process handle for auto-sync when game closes
+      Process? process = await strategy.launchWithHandle(game, existingRomPath);
+      if (process == null) {
+        // Fall back to regular launch if no process handle available
+        await strategy.launch(game, existingRomPath);
+      } else {
+        // Start background Future to handle process exit
+        unawaited(Future.delayed(Duration.zero, () async {
+          try {
+            await process.exitCode;
+            if (!context.mounted) return;
+
+            messenger.showSnackBar(
+              const SnackBar(
+                content: Text('Auto-syncing saves...'),
+                duration: Duration(seconds: 2),
+              ),
+            );
+
+            if (syncService != null) {
+              final syncMode = ref.read(retroarchSyncModeProvider);
+              await syncService.pushSaves(game, existingRomPath, syncMode: syncMode);
+            }
+
+            if (!context.mounted) return;
+            messenger.showSnackBar(
+              const SnackBar(
+                content: Text('Saves synced'),
+                duration: Duration(seconds: 2),
+              ),
+            );
+          } catch (e) {
+            // Silently ignore errors in auto-sync
+          }
+        }));
+      }
     } catch (e) {
       if (!context.mounted) return;
+
+      if (e is MissingRetroArchCoreException) {
+        final shouldDownload = await showDialog<bool>(
+          context: context,
+          builder: (ctx) => AlertDialog(
+            title: const Text('RetroArch Core Missing'),
+            content: Text(
+                'The core ${e.coreName} is required for this game but is not installed. Would you like Freegosy to download and install it automatically?'),
+            actions: [
+              TextButton(
+                onPressed: () => Navigator.of(ctx).pop(false),
+                child: const Text('Cancel'),
+              ),
+              ElevatedButton(
+                onPressed: () => Navigator.of(ctx).pop(true),
+                child: const Text('Install'),
+              ),
+            ],
+          ),
+        );
+        if (shouldDownload == true && context.mounted) {
+          showDialog(
+            context: context,
+            barrierDismissible: false,
+            builder: (ctx) => const Center(child: CircularProgressIndicator()),
+          );
+          try {
+            final raStrategy = strategy as RetroArchStrategy;
+            final coresDir = File(e.corePath).parent.path;
+            await raStrategy.downloadCore(e.coreName, coresDir, Dio());
+            if (context.mounted) {
+              Navigator.of(context).pop();
+              await _handleLaunch(context, ref, game);
+            }
+          } catch (err) {
+            if (context.mounted) {
+              Navigator.of(context).pop();
+              messenger.showSnackBar(
+                SnackBar(content: Text('Failed to download core: $err')),
+              );
+            }
+          }
+        }
+        return;
+      }
+
       final isWindows =
           ['windows', 'pc', 'win'].contains(game.platformSlug?.toLowerCase() ?? '');
       final isMissingExe =
@@ -179,7 +303,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
       if (isWindows && isMissingExe) {
         await _handleWindowsConfig(context, ref, game);
       } else {
-        ScaffoldMessenger.of(context).showSnackBar(
+        messenger.showSnackBar(
           SnackBar(
             content: Text('Launch failed: $e'),
             duration: const Duration(seconds: 8),
@@ -191,13 +315,13 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
 
   Future<void> _handleWindowsConfig(
       BuildContext context, WidgetRef ref, Game game) async {
-    final registry = ref.read(strategyRegistryProvider);
+    final registry = ref.read(strategyRegistryProvider).asData?.value;
     final windowsStrategy =
         registry?.getStrategyForSlug(game.platformSlug ?? '') as WindowsStrategy?;
-    final syncService = ref.read(saveSyncServiceProvider);
-
-    final result = await showDialog<Map<String, String>>(
-      context: context,
+    final syncService = await ref.read(saveSyncServiceProvider.future);
+      if (!context.mounted) return;
+      final result = await showDialog<Map<String, String>>(
+        context: context,
       builder: (ctx) => WindowsGameConfigDialog(
         game: game,
         currentExePath: windowsStrategy?.getExeOverride(game.id),
@@ -205,44 +329,40 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
             syncService?.windowsSaveStrategy.getManualOverride(game.id),
       ),
     );
-    if (result == null) return; // user cancelled
-
+    if (result == null) return;
     final exe = result['exe'] ?? '';
     final save = result['save'] ?? '';
-
     if (exe.isNotEmpty) {
       await windowsStrategy?.setExeOverride(game.id, exe);
     }
     if (save.isNotEmpty) {
       await syncService?.windowsSaveStrategy.setManualOverride(game.id, save);
     }
-
     if (!context.mounted) return;
     await _handleLaunch(context, ref, game);
   }
 
   Future<void> _handleSyncSaves(
       BuildContext context, WidgetRef ref, Game game) async {
-    final syncService = ref.read(saveSyncServiceProvider);
+    final messenger = ScaffoldMessenger.of(context);
+
+    final syncService = await ref.read(saveSyncServiceProvider.future);
     if (syncService == null) {
-      ScaffoldMessenger.of(context).showSnackBar(
+      messenger.showSnackBar(
         const SnackBar(content: Text('Save sync not available')),
       );
       return;
     }
-
     final dir = ref.read(directoryServiceProvider).asData?.value;
     final romPath = dir != null ? await dir.getRomFilePath(game) : '';
-
     if (!context.mounted) return;
-    ScaffoldMessenger.of(context).showSnackBar(
+    messenger.showSnackBar(
       SnackBar(content: Text('Syncing saves for ${game.name}...')),
     );
-
     final syncMode = ref.read(retroarchSyncModeProvider);
     final ok = await syncService.pushSaves(game, romPath, syncMode: syncMode);
     if (!context.mounted) return;
-    ScaffoldMessenger.of(context).showSnackBar(
+    messenger.showSnackBar(
       SnackBar(
         content: Text(ok
             ? 'Saves uploaded for ${game.name}'
@@ -251,63 +371,6 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
     );
   }
 
-  void _startDownload(BuildContext context, WidgetRef ref, game) {
-    final service = ref.read(rommServiceProvider);
-    if (service == null) {
-      ScaffoldMessenger.of(context).showSnackBar(
-        const SnackBar(content: Text('Not connected to RomM')),
-      );
-      return;
-    }
-    final url = service.getDownloadUrl(game);
-    final basicAuth =
-        'Basic ${base64Encode(utf8.encode('${service.config.username}:${service.config.password}'))}';
-    final headers = <String, String>{'Authorization': basicAuth};
-    ref
-        .read(downloadProvider.notifier)
-        .startDownload(game, url, headers: headers);
-    ScaffoldMessenger.of(context).showSnackBar(
-      SnackBar(content: Text('Downloading ${game.name}...')),
-    );
-
-    final dirService = ref.read(directoryServiceProvider).asData?.value;
-    if (dirService != null) {
-      Future.delayed(const Duration(seconds: 2), () {
-        _refreshDownloadState(dirService, game);
-      });
-    }
-  }
-
-  double _calculateCardHeight(int columnCount, double cardSpacing,
-      double cardAspectRatio, BuildContext context) {
-    final screenWidth = MediaQuery.of(context).size.width;
-    const padding = 24.0;
-    final totalSpacing = cardSpacing * (columnCount - 1);
-    final cardWidth = (screenWidth - padding - totalSpacing) / columnCount;
-    final safeRatio = cardAspectRatio <= 0 ? 0.56 : cardAspectRatio;
-    final coverHeight = cardWidth / safeRatio;
-    final totalHeight = coverHeight + 90.0;
-    return totalHeight.clamp(100.0, 900.0);
-  }
-
-  Widget _buildSkeletonGrid(
-      double cardAspectRatio, int columnCount, double cardSpacing) {
-    return GridView.builder(
-      padding: const EdgeInsets.all(12),
-      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
-        crossAxisCount: columnCount,
-        crossAxisSpacing: cardSpacing,
-        mainAxisSpacing: cardSpacing,
-        mainAxisExtent: _calculateCardHeight(
-            columnCount, cardSpacing, cardAspectRatio, context),
-      ),
-      itemCount: 20,
-      itemBuilder: (context, index) {
-        return _SkeletonCard();
-      },
-    );
-  }
-
   @override
   Widget build(BuildContext context) {
     final platformsAsync = ref.watch(platformsProvider);
@@ -323,7 +386,6 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
     final rommConfigAsync = ref.watch(rommConfigProvider);
     final directoryServiceAsync = ref.watch(directoryServiceProvider);
 
-    // Build AppBar title: "Freegosy • hostname • N games"
     final appBarTitle = rommConfigAsync.when(
       data: (config) {
         final uri = Uri.tryParse(config.baseUrl);
@@ -341,220 +403,157 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
       body: ExcludeSemantics(
         child: Column(
           children: [
-          Padding(
-            padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
-            child: TextField(
-              decoration: InputDecoration(
-                hintText: 'Search games...',
-                prefixIcon: const Icon(Icons.search),
-                border: OutlineInputBorder(
-                  borderRadius: BorderRadius.circular(8.0),
+            Padding(
+              padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
+              child: TextField(
+                decoration: InputDecoration(
+                  hintText: 'Search games...',
+                  prefixIcon: const Icon(Icons.search),
+                  border: OutlineInputBorder(
+                    borderRadius: BorderRadius.circular(8.0),
+                  ),
                 ),
+                onChanged: (value) {
+                  ref.read(searchQueryProvider.notifier).state = value;
+                },
               ),
-              onChanged: (value) {
-                ref.read(searchQueryProvider.notifier).state = value;
-              },
             ),
-          ),
-          platformsAsync.when(
-            data: (platforms) => PlatformFilterBar(
-              platforms: platforms,
-              selectedPlatformId: selectedPlatformId,
-              onSelected: (platform) {
-                ref.read(selectedPlatformIdProvider.notifier).state = platform?.id;
-              },
+            platformsAsync.when(
+              data: (platforms) => PlatformFilterBar(
+                platforms: platforms,
+                selectedPlatformId: selectedPlatformId,
+                onSelected: (platform) {
+                  ref.read(selectedPlatformIdProvider.notifier).state = platform?.id;
+                },
+              ),
+              loading: () => const LinearProgressIndicator(),
+              error: (e, s) => Text('Error loading platforms: $e'),
             ),
-            loading: () => const LinearProgressIndicator(),
-            error: (e, s) => Text('Error loading platforms: $e'),
-          ),
-          Expanded(
-            child: gamesAsync.when(
-              loading: () =>
-                  _buildSkeletonGrid(cardAspectRatio, columnCount, cardSpacing),
-              error: (e, s) => Center(
-                child: Padding(
-                  padding: const EdgeInsets.all(16.0),
-                  child: Text(
-                    'Error loading games: $e',
-                    textAlign: TextAlign.center,
-                    style: const TextStyle(color: Colors.red),
+            Expanded(
+              child: gamesAsync.when(
+                loading: () => buildSkeletonGrid(cardAspectRatio, columnCount, cardSpacing, context),
+                error: (e, s) => Center(
+                  child: Padding(
+                    padding: const EdgeInsets.all(16.0),
+                    child: Text(
+                      'Error loading games: $e',
+                      textAlign: TextAlign.center,
+                      style: const TextStyle(color: Colors.red),
+                    ),
                   ),
                 ),
-              ),
-              data: (_) {
-                final gamesCount = filteredGames.length;
-                final countDisplayText =
-                    (selectedPlatformId == null && searchQuery.isEmpty)
-                        ? 'Showing all $gamesCount games'
-                        : 'Showing $gamesCount games';
-
-                final dirService = directoryServiceAsync.asData?.value;
-                if (dirService != null && !_downloadStatesLoaded) {
-                  final games = ref.read(allGamesProvider).asData?.value ?? [];
-                  _loadDownloadStates(dirService, games);
-                }
-
-                return Column(
-                  children: [
-                    Padding(
-                      padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0),
-                      child: Align(
-                        alignment: Alignment.centerRight,
-                        child: Text(
-                          countDisplayText,
-                          style: const TextStyle(fontSize: 12, color: Colors.grey),
+                data: (_) {
+                  final gamesCount = filteredGames.length;
+                  final countDisplayText =
+                      (selectedPlatformId == null && searchQuery.isEmpty)
+                          ? 'Showing all $gamesCount games'
+                          : 'Showing $gamesCount games';
+
+                  final dirService = directoryServiceAsync.asData?.value;
+                  if (dirService != null && !_downloadStatesLoaded) {
+                    final games = ref.read(allGamesProvider).asData?.value ?? [];
+                    _loadDownloadStates(dirService, games);
+                  }
+
+                  return Column(
+                    children: [
+                      Padding(
+                        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 2.0),
+                        child: Align(
+                          alignment: Alignment.centerRight,
+                          child: Text(
+                            countDisplayText,
+                            style: const TextStyle(fontSize: 12, color: Colors.grey),
+                          ),
                         ),
                       ),
-                    ),
-                    Expanded(
-                      child: RefreshIndicator(
-                        onRefresh: () async {
-                          setState(() {
-                            _downloadStatesLoaded = false;
-                            _downloadedStates = {};
-                          });
-                          final prefs = await SharedPreferences.getInstance();
-                          await prefs.remove('cached_games');
-                          await prefs.remove('cached_platforms');
-                          await prefs.remove('cached_games_time');
-                          await prefs.remove('cached_platforms_time');
-                          await prefs.remove('cache_size_exceeded'); // Added this line
-                          ref.invalidate(allGamesProvider);
-                          ref.invalidate(platformsProvider);
-                          await ref.read(allGamesProvider.future);
-                        },
-                        child: filteredGames.isEmpty
-                            ? const CustomScrollView(
-                                slivers: [
-                                  SliverFillRemaining(
-                                    child:
-                                        Center(child: Text('No games found')),
+                      Expanded(
+                        child: RefreshIndicator(
+                          onRefresh: () async {
+                            setState(() {
+                              _downloadStatesLoaded = false;
+                              _downloadedStates = {};
+                            });
+                            final prefs = await SharedPreferences.getInstance();
+                            await prefs.remove('cached_games');
+                            await prefs.remove('cached_platforms');
+                            await prefs.remove('cached_games_time');
+                            await prefs.remove('cached_platforms_time');
+                            await prefs.remove('cache_size_exceeded');
+                            ref.invalidate(allGamesProvider);
+                            ref.invalidate(platformsProvider);
+                            await ref.read(allGamesProvider.future);
+                          },
+                          child: filteredGames.isEmpty
+                              ? const CustomScrollView(
+                                  slivers: [
+                                    SliverFillRemaining(
+                                      child: Center(child: Text('No games found')),
+                                    ),
+                                  ],
+                                )
+                              : GridView.builder(
+                                  padding: const EdgeInsets.all(12),
+                                  cacheExtent: 800,
+                                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+                                    crossAxisCount: columnCount,
+                                    crossAxisSpacing: cardSpacing,
+                                    mainAxisSpacing: cardSpacing,
+                                    mainAxisExtent: calculateCardHeight(
+                                        columnCount, cardSpacing, cardAspectRatio, context),
                                   ),
-                                ],
-                              )
-                            : GridView.builder(
-                                padding: const EdgeInsets.all(12),
-                                gridDelegate:
-                                    SliverGridDelegateWithFixedCrossAxisCount(
-                                  crossAxisCount: columnCount,
-                                  crossAxisSpacing: cardSpacing,
-                                  mainAxisSpacing: cardSpacing,
-                                  mainAxisExtent: _calculateCardHeight(
-                                      columnCount,
-                                      cardSpacing,
-                                      cardAspectRatio,
-                                      context),
-                                ),
-                                itemCount: filteredGames.length,
-                                itemBuilder: (context, index) {
-                                  final game = filteredGames[index];
-                                  final dirService =
-                                      directoryServiceAsync.asData?.value;
-                                  final isWindowsGame = [
-                                    'windows',
-                                    'pc',
-                                    'win'
-                                  ].contains(
-                                      game.platformSlug?.toLowerCase() ?? '');
-                                  if (dirService == null) {
+                                  itemCount: filteredGames.length,
+                                  itemBuilder: (context, index) {
+                                    final game = filteredGames[index];
+                                    final dirService = directoryServiceAsync.asData?.value;
+                                    final isWindowsGame = ['windows', 'pc', 'win']
+                                        .contains(game.platformSlug?.toLowerCase() ?? '');
+                                    final coverUrl = ref.read(rommServiceProvider)?.resolveCoverUrl(game);
+
+                                    if (dirService == null) {
+                                      return GestureDetector(
+                                        onLongPress: isWindowsGame
+                                            ? () => _handleWindowsConfig(context, ref, game)
+                                            : null,
+                                        child: GameCard(
+                                          game: game,
+                                          coverUrl: coverUrl,
+                                          showTitle: showTitle,
+                                          showButtonsOnHover: showButtonsOnHover,
+                                          onDownload: () => _startDownload(context, ref, game),
+                                          onLaunch: () => _handleLaunch(context, ref, game),
+                                          onSyncSaves: () => _handleSyncSaves(context, ref, game),
+                                        ),
+                                      );
+                                    }
+
                                     return GestureDetector(
                                       onLongPress: isWindowsGame
-                                          ? () => _handleWindowsConfig(
-                                              context, ref, game)
+                                          ? () => _handleWindowsConfig(context, ref, game)
                                           : null,
                                       child: GameCard(
                                         game: game,
+                                        coverUrl: coverUrl,
+                                        isDownloaded: _downloadedStates[game.id] ?? false,
                                         showTitle: showTitle,
                                         showButtonsOnHover: showButtonsOnHover,
-                                        onDownload: () =>
-                                            _startDownload(context, ref, game),
-                                        onLaunch: () =>
-                                            _handleLaunch(context, ref, game),
-                                        onSyncSaves: () =>
-                                            _handleSyncSaves(context, ref, game),
+                                        onDownload: () => _startDownload(context, ref, game),
+                                        onLaunch: () => _handleLaunch(context, ref, game),
+                                        onSyncSaves: () => _handleSyncSaves(context, ref, game),
                                       ),
                                     );
-                                  }
-                                  
-                                  return GestureDetector(
-                                    onLongPress: isWindowsGame
-                                        ? () => _handleWindowsConfig(
-                                            context, ref, game)
-                                        : null,
-                                    child: GameCard(
-                                      game: game,
-                                      isDownloaded: _downloadedStates[game.id] ?? false,
-                                      showTitle: showTitle,
-                                      showButtonsOnHover: showButtonsOnHover,
-                                      onDownload: () => _startDownload(
-                                          context, ref, game),
-                                      onLaunch: () =>
-                                          _handleLaunch(context, ref, game),
-                                      onSyncSaves: () => _handleSyncSaves(
-                                          context, ref, game),
-                                    ),
-                                  );
-                                },
-                              ),
+                                  },
+                                ),
+                        ),
                       ),
-                    ),
-                  ],
-                );
-              },
+                    ],
+                  );
+                },
+              ),
             ),
-          ),
-        ],
-      ),
+          ],
+        ),
       ),
     );
   }
-}
-
-class _SkeletonCard extends StatefulWidget {
-  @override
-  State<_SkeletonCard> createState() => _SkeletonCardState();
-}
-
-class _SkeletonCardState extends State<_SkeletonCard>
-    with SingleTickerProviderStateMixin {
-  late AnimationController _controller;
-  late Animation<double> _animation;
-
-  @override
-  void initState() {
-    super.initState();
-    _controller = AnimationController(
-      vsync: this,
-      duration: const Duration(milliseconds: 1200),
-    )..repeat(reverse: true);
-    _animation = CurvedAnimation(
-      parent: _controller,
-      curve: Curves.easeInOut,
-    );
-  }
-
-  @override
-  void dispose() {
-    _controller.dispose();
-    super.dispose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return AnimatedBuilder(
-      animation: _animation,
-      builder: (context, child) {
-        return Container(
-          decoration: BoxDecoration(
-            borderRadius: BorderRadius.circular(8),
-            color: Color.lerp(
-              const Color(0xFF1a1a1a),
-              const Color(0xFF2a2a2a),
-              _animation.value,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
+}
\ No newline at end of file
diff --git a/lib/ui/screens/library_skeleton.dart b/lib/ui/screens/library_skeleton.dart
new file mode 100644
index 0000000..45840c9
--- /dev/null
+++ b/lib/ui/screens/library_skeleton.dart
@@ -0,0 +1,91 @@
+import 'package:flutter/material.dart';
+
+double calculateCardHeight(int columnCount, double cardSpacing,
+    double cardAspectRatio, BuildContext context) {
+  final screenWidth = MediaQuery.of(context).size.width;
+  const padding = 24.0;
+  final totalSpacing = cardSpacing * (columnCount - 1);
+  final cardWidth = (screenWidth - padding - totalSpacing) / columnCount;
+  final safeRatio = cardAspectRatio <= 0 ? 0.56 : cardAspectRatio;
+  final coverHeight = cardWidth / safeRatio;
+  final totalHeight = coverHeight + 90.0;
+  return totalHeight.clamp(100.0, 900.0);
+}
+
+Widget buildSkeletonGrid(
+    double cardAspectRatio, int columnCount, double cardSpacing, BuildContext context) {
+  return GridView.builder(
+    padding: const EdgeInsets.all(12),
+    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+      crossAxisCount: columnCount,
+      crossAxisSpacing: cardSpacing,
+      mainAxisSpacing: cardSpacing,
+      mainAxisExtent: calculateCardHeight(columnCount, cardSpacing, cardAspectRatio, context),
+    ),
+    itemCount: 20,
+    itemBuilder: (context, index) {
+      return const _SkeletonCard();
+    },
+  );
+}
+
+class _SkeletonCard extends StatefulWidget {
+  const _SkeletonCard();
+
+  @override
+  State<_SkeletonCard> createState() => _SkeletonCardState();
+}
+
+class _SkeletonCardState extends State<_SkeletonCard>
+    with SingleTickerProviderStateMixin {
+  late AnimationController _controller;
+  late Animation<double> _animation;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = AnimationController(
+      vsync: this,
+      duration: const Duration(milliseconds: 1200),
+    );
+    _animation = CurvedAnimation(
+      parent: _controller,
+      curve: Curves.easeInOut,
+    );
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    if (TickerMode.valuesOf(context).enabled) {
+      _controller.repeat(reverse: true);
+    } else {
+      _controller.stop();
+    }
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return AnimatedBuilder(
+      animation: _animation,
+      builder: (context, child) {
+        return Container(
+          decoration: BoxDecoration(
+            borderRadius: BorderRadius.circular(8),
+            color: Color.lerp(
+              const Color(0xFF1a1a1a),
+              const Color(0xFF2a2a2a),
+              _animation.value,
+            ),
+          ),
+        );
+      },
+    );
+  }
+}
\ No newline at end of file
diff --git a/lib/ui/screens/settings_display_section.dart b/lib/ui/screens/settings_display_section.dart
new file mode 100644
index 0000000..ffd9574
--- /dev/null
+++ b/lib/ui/screens/settings_display_section.dart
@@ -0,0 +1,165 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+import '../../providers/library_provider.dart'; // Assuming this file contains the providers and kDisplayPresets
+
+// NOTE: kDisplayPresets is assumed to be globally available or defined in library_provider.dart
+// If not, it needs to be imported or passed as an argument.
+
+// Function to build the Library Display section
+Widget buildDisplaySection(
+  BuildContext context,
+  double cardAspectRatio,
+  int columnCount,
+  double cardSpacing,
+  bool showTitle,
+  bool showButtonsOnHover,
+  String activePreset,
+  WidgetRef ref,
+) {
+  return Column(
+    crossAxisAlignment: CrossAxisAlignment.start,
+    children: [
+      const Text('Library Display', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
+      const SizedBox(height: 12),
+      const Text('Presets', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
+      const SizedBox(height: 8),
+      Wrap(
+        spacing: 8,
+        children: [
+          _presetChip('Windows', 'windows_best', activePreset, ref),
+          _presetChip('Steam Deck', 'steamdeck_best', activePreset, ref),
+          _presetChip('Cozy', 'cozy', activePreset, ref),
+          _presetChip('Compact', 'compact', activePreset, ref),
+          _presetChip('Custom', 'custom', activePreset, ref),
+        ],
+      ),
+      const SizedBox(height: 24),
+      Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: [
+          const Text('Columns per row', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
+          Text('$columnCount', style: const TextStyle(fontSize: 16, color: Colors.deepPurple)),
+        ],
+      ),
+      Slider(
+        value: columnCount.toDouble(),
+        min: 2,
+        max: 8,
+        divisions: 6,
+        label: '$columnCount',
+        onChanged: (value) async {
+          ref.read(activePresetProvider.notifier).state = 'custom';
+          ref.read(columnCountProvider.notifier).state = value.toInt();
+          final prefs = await SharedPreferences.getInstance();
+          await prefs.setInt('column_count', value.toInt());
+          await prefs.setString('active_preset', 'custom');
+        },
+      ),
+      const SizedBox(height: 16),
+      const Text('Card Shape', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
+      const SizedBox(height: 8),
+      SegmentedButton<double>(
+        segments: const [
+          ButtonSegment(value: 1.0, label: Text('Square')),
+          ButtonSegment(value: 0.72, label: Text('Portrait')),
+          ButtonSegment(value: 0.58, label: Text('Tall')),
+        ],
+        selected: {
+          [1.0, 0.72, 0.58].reduce((a, b) =>
+              (a - cardAspectRatio).abs() < (b - cardAspectRatio).abs()
+                  ? a
+                  : b)
+        },
+        onSelectionChanged: (selection) async {
+          ref.read(activePresetProvider.notifier).state = 'custom';
+          ref.read(cardAspectRatioProvider.notifier).state = selection.first;
+          final prefs = await SharedPreferences.getInstance();
+          await prefs.setDouble('card_aspect_ratio', selection.first);
+          await prefs.setString('active_preset', 'custom');
+        },
+      ),
+      const SizedBox(height: 16),
+      const Text('Card Spacing', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
+      const SizedBox(height: 8),
+      SegmentedButton<double>(
+        segments: const [
+          ButtonSegment(value: 4.0, label: Text('Tight')),
+          ButtonSegment(value: 8.0, label: Text('Normal')),
+          ButtonSegment(value: 12.0, label: Text('Airy')),
+        ],
+        selected: {
+          [4.0, 8.0, 12.0].reduce((a, b) =>
+              (a - cardSpacing).abs() < (b - cardSpacing).abs() ? a : b)
+        },
+        onSelectionChanged: (selection) async {
+          ref.read(activePresetProvider.notifier).state = 'custom';
+          ref.read(cardSpacingProvider.notifier).state = selection.first;
+          final prefs = await SharedPreferences.getInstance();
+          await prefs.setDouble('card_spacing', selection.first);
+          await prefs.setString('active_preset', 'custom');
+        },
+      ),
+      const SizedBox(height: 16),
+      SwitchListTile(
+        title: const Text('Show game title'),
+        subtitle: const Text('Display title text below cover art'),
+        value: showTitle,
+        contentPadding: EdgeInsets.zero,
+        onChanged: (value) async {
+          ref.read(activePresetProvider.notifier).state = 'custom';
+          ref.read(showTitleProvider.notifier).state = value;
+          final prefs = await SharedPreferences.getInstance();
+          await prefs.setBool('show_title', value);
+          await prefs.setString('active_preset', 'custom');
+        },
+      ),
+      SwitchListTile(
+        title: const Text('Show buttons on hover only'),
+        subtitle: const Text('Buttons appear when hovering over a card'),
+        value: showButtonsOnHover,
+        contentPadding: EdgeInsets.zero,
+        onChanged: (value) async {
+          ref.read(activePresetProvider.notifier).state = 'custom';
+          ref.read(showButtonsOnHoverProvider.notifier).state = value;
+          final prefs = await SharedPreferences.getInstance();
+          await prefs.setBool('show_buttons_on_hover', value);
+          await prefs.setString('active_preset', 'custom');
+        },
+      ),
+    ],
+  );
+}
+
+// Helper widget for preset chips
+Widget _presetChip(String label, String presetKey, String activePreset, WidgetRef ref) {
+  final isSelected = activePreset == presetKey;
+  return FilterChip(
+    label: Text(label),
+    selected: isSelected,
+    onSelected: (selected) async {
+      if (!selected) return;
+      ref.read(activePresetProvider.notifier).state = presetKey;
+      final prefs = await SharedPreferences.getInstance();
+      await prefs.setString('active_preset', presetKey);
+      if (presetKey == 'custom') return;
+      final preset = kDisplayPresets[presetKey]; // kDisplayPresets must be accessible
+      if (preset == null) return;
+      final cols = preset['columnCount'] as int;
+      final ratio = preset['cardAspectRatio'] as double;
+      final spacing = preset['cardSpacing'] as double;
+      final title = preset['showTitle'] as bool;
+      final hover = preset['showButtonsOnHover'] as bool;
+      ref.read(columnCountProvider.notifier).state = cols;
+      ref.read(cardAspectRatioProvider.notifier).state = ratio;
+      ref.read(cardSpacingProvider.notifier).state = spacing;
+      ref.read(showTitleProvider.notifier).state = title;
+      ref.read(showButtonsOnHoverProvider.notifier).state = hover;
+      await prefs.setInt('column_count', cols);
+      await prefs.setDouble('card_aspect_ratio', ratio);
+      await prefs.setDouble('card_spacing', spacing);
+      await prefs.setBool('show_title', title);
+      await prefs.setBool('show_buttons_on_hover', hover);
+    },
+  );
+}
diff --git a/lib/ui/screens/settings_emulators_section.dart b/lib/ui/screens/settings_emulators_section.dart
new file mode 100644
index 0000000..4fc673a
--- /dev/null
+++ b/lib/ui/screens/settings_emulators_section.dart
@@ -0,0 +1,158 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:file_picker/file_picker.dart';
+import '../../core/storage/directory_service.dart';
+import '../../core/emulator/emulator_registry_data.dart';
+import '../../core/emulator/strategy_registry.dart';
+import '../../providers/download_provider.dart';
+
+// Function to build the Emulators section
+Widget buildEmulatorsSection(
+  BuildContext context,
+  DirectoryService directoryService,
+  bool emulatorsLoaded,
+  Map<String, bool> emulatorInstallStates,
+  Function(void Function()) setState,
+  WidgetRef ref,
+) {
+  return Column(
+    crossAxisAlignment: CrossAxisAlignment.start,
+    children: [
+      const Text('Emulators',
+          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
+      const SizedBox(height: 16),
+      if (!emulatorsLoaded)
+        const Center(child: CircularProgressIndicator())
+      else
+        ...kEmulatorDefinitions.map<Widget>((def) {
+          final emulatorId = def['id'] as String;
+          final emulatorName = def['name'] as String;
+          final isInstalled = emulatorInstallStates[emulatorId] ?? false;
+          final overridePath = directoryService.getEmulatorPathOverride(emulatorId);
+
+          return Padding(
+            padding: const EdgeInsets.symmetric(vertical: 8.0),
+            child: Row(
+              children: [
+                Icon(
+                  isInstalled ? Icons.check_circle : Icons.cancel,
+                  color: isInstalled ? Colors.green : Colors.red,
+                ),
+                const SizedBox(width: 8),
+                Expanded(
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      Text(emulatorName),
+                      if (overridePath != null)
+                        Text(
+                          overridePath,
+                          style: const TextStyle(fontSize: 11, color: Colors.grey),
+                          overflow: TextOverflow.ellipsis,
+                        ),
+                    ],
+                  ),
+                ),
+                IconButton(
+                  icon: const Icon(Icons.folder_open, size: 20),
+                  tooltip: 'Set custom directory',
+                  onPressed: () async {
+                    String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
+                    if (selectedDirectory != null) {
+                      await directoryService.setEmulatorPathOverride(emulatorId, selectedDirectory);
+                      setState(() {}); // Trigger parent state update
+                    }
+                  },
+                ),
+                ElevatedButton(
+                  onPressed: isInstalled
+                      ? null
+                      : () async {
+                          // Capture context safely before any async operations.
+                          final messenger = ScaffoldMessenger.of(context);
+
+                          messenger.showSnackBar(SnackBar(
+                            content: Text('Starting download for $emulatorName...'),
+                          ));
+
+                          ref.read(downloadProvider.notifier).startEmulatorDownload(emulatorId, emulatorName);
+
+                          // Listen for download completion and update state
+                          ref.read(downloadProvider.notifier).stream.listen((downloads) {
+                            final progress = downloads[emulatorId];
+                            if (progress != null && progress.isComplete) { // Use safeContext here
+                              // Update the map directly. setState will re-render using the updated map.
+                              emulatorInstallStates[emulatorId] = true;
+                              setState(() {}); // Trigger parent state update
+                              messenger.showSnackBar(SnackBar(
+                                content: Text('$emulatorName downloaded.'),
+                              ));
+                            }
+                          });
+                        },
+                  child: Text(isInstalled ? 'Installed' : 'Download'),
+                ),
+              ],
+            ),
+          );
+        }),
+    ],
+  );
+}
+
+// Function to build the Emulator Conflicts section
+Widget buildConflictsSection(
+  StrategyRegistry registry,
+  Function(void Function()) setState,
+) {
+  final conflicts = registry.detectConflicts();
+  return Column(
+    crossAxisAlignment: CrossAxisAlignment.start,
+    children: [
+      const Text('Emulator Conflicts',
+          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
+      const SizedBox(height: 16),
+      if (conflicts.isEmpty)
+        const Text('No conflicts detected')
+      else
+        ...conflicts.entries.map((entry) {
+          final slug = entry.key;
+          final strategies = entry.value;
+          final currentStrategy = registry.getStrategyForSlug(slug);
+
+          return Padding(
+            padding: const EdgeInsets.symmetric(vertical: 8.0),
+            child: Row(
+              children: [
+                Expanded(
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      Text(slug, style: const TextStyle(fontWeight: FontWeight.w500)),
+                      Text(strategies.map((s) => s.name).join(' vs '),
+                          style: const TextStyle(fontSize: 12, color: Colors.grey)),
+                    ],
+                  ),
+                ),
+                DropdownButton<String>(
+                  value: currentStrategy?.emulatorId,
+                  items: strategies.map((s) {
+                    return DropdownMenuItem(
+                      value: s.emulatorId,
+                      child: Text(s.name),
+                    );
+                  }).toList(),
+                  onChanged: (value) async {
+                    if (value != null) {
+                      await registry.setPreference(slug, value);
+                      setState(() {}); // Trigger parent state update
+                    }
+                  },
+                ),
+              ],
+            ),
+          );
+        }),
+    ],
+  );
+}
diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart
index 55c92dd..74fddff 100644
--- a/lib/ui/screens/settings_screen.dart
+++ b/lib/ui/screens/settings_screen.dart
@@ -1,15 +1,15 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:file_picker/file_picker.dart';
-import 'package:dio/dio.dart';
 import 'package:shared_preferences/shared_preferences.dart';
-
 import '../../core/storage/directory_service.dart';
 import '../../core/emulator/emulator_registry_data.dart';
-import '../../core/emulator/emulator_download_service.dart';
+// import '../../core/extraction/extraction_service.dart'; // Removed unused import
 import '../../providers/romm_provider.dart';
-import '../../providers/library_provider.dart';
+import '../../providers/library_provider.dart'; // Assuming this file contains the display providers and kDisplayPresets
 import '../../core/romm/romm_service.dart';
+import 'settings_emulators_section.dart';
+import 'settings_display_section.dart';
 
 class SettingsScreen extends ConsumerStatefulWidget {
   const SettingsScreen({super.key});
@@ -24,7 +24,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
   late TextEditingController _passwordController;
   bool _isSaving = false;
   Map<String, bool> _emulatorInstallStates = {};
-  bool _emulatorsLoaded = false;
+  bool _emulatorsLoaded = false; // This state is managed here
+  bool _preferencesLoaded = false;
 
   @override
   void initState() {
@@ -32,6 +33,14 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     _baseUrlController = TextEditingController();
     _usernameController = TextEditingController();
     _passwordController = TextEditingController();
+
+    // Mark preferences as loaded (they are now awaited by the provider)
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      final registry = ref.read(strategyRegistryProvider).asData?.value;
+      if (registry != null && !_preferencesLoaded) {
+        if (mounted) setState(() => _preferencesLoaded = true);
+      }
+    });
   }
 
   @override
@@ -42,6 +51,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     super.dispose();
   }
 
+  // This method should remain in the state class to manage its own state.
   Future<void> _loadEmulatorStates(DirectoryService directoryService) async {
     if (_emulatorsLoaded) return;
     final states = <String, bool>{};
@@ -49,9 +59,10 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
       final id = def['id'] as String;
       final exe = def['windows_executable'] as String;
       if (exe.isEmpty) {
-        states[id] = true;
+        states[id] = true; // Assume installed if no executable is defined
         continue;
       }
+      // Check if emulator is installed using the directory service
       states[id] = await directoryService.isEmulatorInstalled(id, exe);
     }
     if (mounted) {
@@ -67,7 +78,9 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     final directoryServiceAsync = ref.watch(directoryServiceProvider);
     final rommService = ref.watch(rommServiceProvider);
     final rommConfigAsync = ref.watch(rommConfigProvider);
+    final strategyRegistry = ref.watch(strategyRegistryProvider).asData?.value;
 
+    // Display section providers
     final cardAspectRatio = ref.watch(cardAspectRatioProvider);
     final columnCount = ref.watch(columnCountProvider);
     final cardSpacing = ref.watch(cardSpacingProvider);
@@ -75,10 +88,16 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     final showButtonsOnHover = ref.watch(showButtonsOnHoverProvider);
     final activePreset = ref.watch(activePresetProvider);
 
+    // Mark preferences as loaded (they are awaited by the provider)
+    if (strategyRegistry != null && !_preferencesLoaded) {
+      if (mounted) setState(() => _preferencesLoaded = true);
+    }
+
     return Scaffold(
       appBar: AppBar(title: const Text('Settings')),
       body: rommConfigAsync.when(
         data: (rommConfig) {
+          // Update controllers with loaded config
           _baseUrlController.text = rommConfig.baseUrl;
           _usernameController.text = rommConfig.username;
           _passwordController.text = rommConfig.password;
@@ -92,6 +111,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
                 return const Center(child: CircularProgressIndicator());
               }
               
+              // Load emulator states if directory service is available
               _loadEmulatorStates(directoryService);
 
               return ExcludeSemantics(
@@ -100,7 +120,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
                   children: [
                     _buildRommServerSection(context, ref, rommService),
                     const SizedBox(height: 24),
-                    _buildDisplaySection(
+                    // Call the extracted display section function
+                    buildDisplaySection(
                       context,
                       cardAspectRatio,
                       columnCount,
@@ -108,13 +129,30 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
                       showTitle,
                       showButtonsOnHover,
                       activePreset,
+                      ref, // Pass ref
                     ),
                     const SizedBox(height: 24),
                     _buildStorageSection(directoryService),
                     const SizedBox(height: 24),
                     _buildRetroArchSyncModeSection(context, ref),
                     const SizedBox(height: 24),
-                    _buildEmulatorsSection(directoryService),
+                    // Call the extracted emulators section function
+                    buildEmulatorsSection(
+                      context, // Pass context
+                      directoryService,
+                      _emulatorsLoaded, // Pass loaded state
+                      _emulatorInstallStates, // Pass the states map
+                      setState, // Pass the setState callback
+                      ref, // Pass ref
+                    ),
+                    if (strategyRegistry != null) ...[
+                      const SizedBox(height: 24),
+                      // Call the extracted conflicts section function
+                      buildConflictsSection(
+                        strategyRegistry,
+                        setState, // Pass the setState callback
+                      ),
+                    ],
                   ],
                 ),
               );
@@ -129,188 +167,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     );
   }
 
-  Widget _buildDisplaySection(
-    BuildContext context,
-    double cardAspectRatio,
-    int columnCount,
-    double cardSpacing,
-    bool showTitle,
-    bool showButtonsOnHover,
-    String activePreset,
-  ) {
-    return Column(
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        const Text('Library Display', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
-        const SizedBox(height: 12),
-        const Text('Presets', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
-        const SizedBox(height: 8),
-        Wrap(
-          spacing: 8,
-          children: [
-            _presetChip('Windows', 'windows_best', activePreset),
-            _presetChip('Steam Deck', 'steamdeck_best', activePreset),
-            _presetChip('Cozy', 'cozy', activePreset),
-            _presetChip('Compact', 'compact', activePreset),
-            _presetChip('Custom', 'custom', activePreset),
-          ],
-        ),
-        const SizedBox(height: 24),
-        Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            const Text('Columns per row', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
-            Text('$columnCount', style: const TextStyle(fontSize: 16, color: Colors.deepPurple)),
-          ],
-        ),
-        Slider(
-          value: columnCount.toDouble(),
-          min: 2,
-          max: 8,
-          divisions: 6,
-          label: '$columnCount',
-          onChanged: (value) async {
-            ref.read(activePresetProvider.notifier).state = 'custom';
-            ref.read(columnCountProvider.notifier).state = value.toInt();
-            final prefs = await SharedPreferences.getInstance();
-            await prefs.setInt('column_count', value.toInt());
-            await prefs.setString('active_preset', 'custom');
-          },
-        ),
-        const SizedBox(height: 16),
-        const Text('Card Shape', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
-        const SizedBox(height: 8),
-        SegmentedButton<double>(
-          segments: const [
-            ButtonSegment(value: 1.0, label: Text('Square')),
-            ButtonSegment(value: 0.72, label: Text('Portrait')),
-            ButtonSegment(value: 0.58, label: Text('Tall')),
-          ],
-          selected: {
-            [1.0, 0.72, 0.58].reduce((a, b) =>
-                (a - cardAspectRatio).abs() < (b - cardAspectRatio).abs()
-                    ? a
-                    : b)
-          },
-          onSelectionChanged: (selection) async {
-            ref.read(activePresetProvider.notifier).state = 'custom';
-            ref.read(cardAspectRatioProvider.notifier).state = selection.first;
-            final prefs = await SharedPreferences.getInstance();
-            await prefs.setDouble('card_aspect_ratio', selection.first);
-            await prefs.setString('active_preset', 'custom');
-          },
-        ),
-        const SizedBox(height: 16),
-        const Text('Card Spacing', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
-        const SizedBox(height: 8),
-        SegmentedButton<double>(
-          segments: const [
-            ButtonSegment(value: 4.0, label: Text('Tight')),
-            ButtonSegment(value: 8.0, label: Text('Normal')),
-            ButtonSegment(value: 12.0, label: Text('Airy')),
-          ],
-          selected: {
-            [4.0, 8.0, 12.0].reduce((a, b) =>
-                (a - cardSpacing).abs() < (b - cardSpacing).abs() ? a : b)
-          },
-          onSelectionChanged: (selection) async {
-            ref.read(activePresetProvider.notifier).state = 'custom';
-            ref.read(cardSpacingProvider.notifier).state = selection.first;
-            final prefs = await SharedPreferences.getInstance();
-            await prefs.setDouble('card_spacing', selection.first);
-            await prefs.setString('active_preset', 'custom');
-          },
-        ),
-        const SizedBox(height: 16),
-        SwitchListTile(
-          title: const Text('Show game title'),
-          subtitle: const Text('Display title text below cover art'),
-          value: showTitle,
-          contentPadding: EdgeInsets.zero,
-          onChanged: (value) async {
-            ref.read(activePresetProvider.notifier).state = 'custom';
-            ref.read(showTitleProvider.notifier).state = value;
-            final prefs = await SharedPreferences.getInstance();
-            await prefs.setBool('show_title', value);
-            await prefs.setString('active_preset', 'custom');
-          },
-        ),
-        SwitchListTile(
-          title: const Text('Show buttons on hover only'),
-          subtitle: const Text('Buttons appear when hovering over a card'),
-          value: showButtonsOnHover,
-          contentPadding: EdgeInsets.zero,
-          onChanged: (value) async {
-            ref.read(activePresetProvider.notifier).state = 'custom';
-            ref.read(showButtonsOnHoverProvider.notifier).state = value;
-            final prefs = await SharedPreferences.getInstance();
-            await prefs.setBool('show_buttons_on_hover', value);
-            await prefs.setString('active_preset', 'custom');
-          },
-        ),
-      ],
-    );
-  }
-
-  Widget _presetChip(String label, String presetKey, String activePreset) {
-    final isSelected = activePreset == presetKey;
-    return FilterChip(
-      label: Text(label),
-      selected: isSelected,
-      onSelected: (selected) async {
-        if (!selected) return;
-        ref.read(activePresetProvider.notifier).state = presetKey;
-        final prefs = await SharedPreferences.getInstance();
-        await prefs.setString('active_preset', presetKey);
-        if (presetKey == 'custom') return;
-        final preset = kDisplayPresets[presetKey];
-        if (preset == null) return;
-        final cols = preset['columnCount'] as int;
-        final ratio = preset['cardAspectRatio'] as double;
-        final spacing = preset['cardSpacing'] as double;
-        final title = preset['showTitle'] as bool;
-        final hover = preset['showButtonsOnHover'] as bool;
-        ref.read(columnCountProvider.notifier).state = cols;
-        ref.read(cardAspectRatioProvider.notifier).state = ratio;
-        ref.read(cardSpacingProvider.notifier).state = spacing;
-        ref.read(showTitleProvider.notifier).state = title;
-        ref.read(showButtonsOnHoverProvider.notifier).state = hover;
-        await prefs.setInt('column_count', cols);
-        await prefs.setDouble('card_aspect_ratio', ratio);
-        await prefs.setDouble('card_spacing', spacing);
-        await prefs.setBool('show_title', title);
-        await prefs.setBool('show_buttons_on_hover', hover);
-      },
-    );
-  }
-
-  Widget _buildRetroArchSyncModeSection(BuildContext context, WidgetRef ref) {
-    final syncMode = ref.watch(retroarchSyncModeProvider);
-    return Column(
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        const Text('RetroArch Save Sync', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
-        const SizedBox(height: 8),
-        const Text('What to sync with RomM cloud', style: TextStyle(color: Colors.grey)),
-        const SizedBox(height: 12),
-        SegmentedButton<String>(
-          segments: const [
-            ButtonSegment(value: 'saves', label: Text('Saves only')),
-            ButtonSegment(value: 'states', label: Text('States only')),
-            ButtonSegment(value: 'both', label: Text('Both')),
-          ],
-          selected: {syncMode},
-          onSelectionChanged: (selection) async {
-            final value = selection.first;
-            ref.read(retroarchSyncModeProvider.notifier).state = value;
-            final prefs = await SharedPreferences.getInstance();
-            await prefs.setString('retroarch_sync_mode', value);
-          },
-        ),
-      ],
-    );
-  }
-
+  // --- RomM Server Section ---
   Widget _buildRommServerSection(BuildContext context, WidgetRef ref, rommService) {
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
@@ -358,6 +215,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
                   return;
                 }
                 try {
+                  // Test connection by fetching platforms
                   final platforms = await rommService.getPlatforms();
                   if (context.mounted) {
                     ScaffoldMessenger.of(context).showSnackBar(
@@ -400,9 +258,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
                       await prefs.setString('rommUsername', username);
                       await prefs.setString('rommPassword', password);
 
-                      // ignore: unused_result
+                      // Invalidate providers to refresh RomM service and config
                       ref.invalidate(rommConfigProvider);
-                      // ignore: unused_result
                       ref.invalidate(rommServiceProvider);
 
                       if (context.mounted) {
@@ -422,6 +279,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     );
   }
 
+  // --- Storage Section ---
   Widget _buildStorageSection(DirectoryService directoryService) {
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
@@ -434,8 +292,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
           onChanged: (newPath) async {
             if (newPath != null) {
               await directoryService.setRomsRoot(newPath);
-              // ignore: unused_result
-              ref.refresh(directoryServiceProvider);
+              // Fix: Replace ref.refresh with ref.invalidate
+              ref.invalidate(directoryServiceProvider);
             }
           },
         ),
@@ -446,8 +304,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
           onChanged: (newPath) async {
             if (newPath != null) {
               await directoryService.setEmulatorsRoot(newPath);
-              // ignore: unused_result
-              ref.refresh(directoryServiceProvider);
+              // Fix: Replace ref.refresh with ref.invalidate
+              ref.invalidate(directoryServiceProvider);
             }
           },
         ),
@@ -484,82 +342,30 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
     );
   }
 
-  Widget _buildEmulatorsSection(DirectoryService directoryService) {
+  // --- RetroArch Sync Mode Section ---
+  Widget _buildRetroArchSyncModeSection(BuildContext context, WidgetRef ref) {
+    final syncMode = ref.watch(retroarchSyncModeProvider);
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
-        const Text('Emulators',
-            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
-        const SizedBox(height: 16),
-        if (!_emulatorsLoaded)
-          const Center(child: CircularProgressIndicator())
-        else
-          ...kEmulatorDefinitions.map<Widget>((def) {
-            final emulatorId = def['id'] as String;
-            final emulatorName = def['name'] as String;
-            final isInstalled = _emulatorInstallStates[emulatorId] ?? false;
-            return Padding(
-              padding: const EdgeInsets.symmetric(vertical: 8.0),
-              child: Row(
-                children: [
-                  Icon(
-                    isInstalled ? Icons.check_circle : Icons.cancel,
-                    color: isInstalled ? Colors.green : Colors.red,
-                  ),
-                  const SizedBox(width: 8),
-                  Expanded(child: Text(emulatorName)),
-                  ElevatedButton(
-                    onPressed: isInstalled
-                        ? null
-                        : () async {
-                            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                              content:
-                                  Text('Starting download for $emulatorName...'),
-                            ));
-                            final emulatorDownloadService =
-                                EmulatorDownloadService(
-                                    Dio(), directoryService);
-                            try {
-                              await for (final progress in emulatorDownloadService
-                                  .downloadEmulator(emulatorId)) {
-                                if (progress.error != null) {
-                                  if (mounted) {
-                                    ScaffoldMessenger.of(context)
-                                        .showSnackBar(SnackBar(
-                                      content: Text('Error: ${progress.error}'),
-                                    ));
-                                  }
-                                  break;
-                                }
-                                if (progress.isComplete) {
-                                  if (mounted) {
-                                    setState(() {
-                                      _emulatorInstallStates[emulatorId] = true;
-                                    });
-                                    ScaffoldMessenger.of(context)
-                                        .showSnackBar(SnackBar(
-                                      content:
-                                          Text('$emulatorName downloaded.'),
-                                    ));
-                                  }
-                                  break;
-                                }
-                              }
-                            } catch (e) {
-                              if (mounted) {
-                                ScaffoldMessenger.of(context)
-                                    .showSnackBar(SnackBar(
-                                  content: Text('Unexpected error: $e'),
-                                ));
-                              }
-                            }
-                          },
-                    child: Text(isInstalled ? 'Installed' : 'Download'),
-                  ),
-                ],
-              ),
-            );
-          }),
+        const Text('RetroArch Save Sync', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
+        const SizedBox(height: 8),
+        const Text('What to sync with RomM cloud', style: TextStyle(color: Colors.grey)),
+        const SizedBox(height: 12),
+        SegmentedButton<String>(
+          segments: const [
+            ButtonSegment(value: 'saves', label: Text('Saves only')),
+            ButtonSegment(value: 'states', label: Text('States only')),
+            ButtonSegment(value: 'both', label: Text('Both')),
+          ],
+          selected: {syncMode},
+          onSelectionChanged: (selection) async {
+            final value = selection.first;
+            ref.read(retroarchSyncModeProvider.notifier).state = value;
+            final prefs = await SharedPreferences.getInstance();
+            await prefs.setString('retroarch_sync_mode', value);
+          },
+        ),
       ],
     );
   }
diff --git a/lib/ui/widgets/game_card.dart b/lib/ui/widgets/game_card.dart
index e41b224..cda7d41 100644
--- a/lib/ui/widgets/game_card.dart
+++ b/lib/ui/widgets/game_card.dart
@@ -1,10 +1,10 @@
 import 'package:flutter/material.dart';
-import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:cached_network_image/cached_network_image.dart';
 import '../../core/romm/romm_models.dart';
-import '../../providers/romm_provider.dart';
 
-class GameCard extends ConsumerStatefulWidget {
+class GameCard extends StatefulWidget {
   final Game game;
+  final String? coverUrl;
   final VoidCallback onDownload;
   final VoidCallback onLaunch;
   final VoidCallback? onSyncSaves;
@@ -15,6 +15,7 @@ class GameCard extends ConsumerStatefulWidget {
   const GameCard({
     super.key,
     required this.game,
+    this.coverUrl,
     required this.onDownload,
     required this.onLaunch,
     this.onSyncSaves,
@@ -24,23 +25,26 @@ class GameCard extends ConsumerStatefulWidget {
   });
 
   @override
-  ConsumerState<GameCard> createState() => _GameCardState();
+  State<GameCard> createState() => _GameCardState();
 }
 
-class _GameCardState extends ConsumerState<GameCard> {
-  bool _isHovering = false;
+class _GameCardState extends State<GameCard> {
+  final ValueNotifier<bool> _hovering = ValueNotifier(false);
 
   @override
-  Widget build(BuildContext context) {
-    final service = ref.watch(rommServiceProvider);
-    final finalCoverUrl = service?.resolveCoverUrl(widget.game);
+  void dispose() {
+    _hovering.dispose();
+    super.dispose();
+  }
 
+  @override
+  Widget build(BuildContext context) {
     return RepaintBoundary(
       child: GestureDetector(
         onLongPress: () => _showContextMenu(context),
         child: MouseRegion(
-          onEnter: (_) => setState(() => _isHovering = true),
-          onExit: (_) => setState(() => _isHovering = false),
+          onEnter: (_) => _hovering.value = true,
+          onExit: (_) => _hovering.value = false,
           child: Card(
             elevation: 2,
             clipBehavior: Clip.antiAlias,
@@ -53,13 +57,21 @@ class _GameCardState extends ConsumerState<GameCard> {
                   child: Stack(
                     fit: StackFit.expand,
                     children: [
-                      (finalCoverUrl == null || finalCoverUrl.isEmpty)
+                      (widget.coverUrl == null || widget.coverUrl!.isEmpty)
                           ? const Center(child: Icon(Icons.sports_esports, size: 48))
-                          : Image.network(
-                              finalCoverUrl,
+                          : CachedNetworkImage(
+                              imageUrl: widget.coverUrl!,
                               fit: BoxFit.cover,
                               alignment: Alignment.topCenter,
-                              errorBuilder: (context, error, stackTrace) => const Center(
+                              memCacheWidth: 300,
+                              memCacheHeight: 400,
+                              placeholder: (context, url) => Container(
+                                color: Colors.grey[300],
+                                child: const Center(
+                                  child: Icon(Icons.image, color: Colors.grey),
+                                ),
+                              ),
+                              errorWidget: (context, url, error) => const Center(
                                 child: Icon(Icons.sports_esports, size: 48),
                               ),
                             ),
@@ -81,74 +93,79 @@ class _GameCardState extends ConsumerState<GameCard> {
                   ),
                 ),
                 // Content - fixed height
-                SizedBox(
-                  height: 90,
-                  child: SingleChildScrollView(
-                    physics: const NeverScrollableScrollPhysics(),
-                    child: Column(
-                      mainAxisSize: MainAxisSize.min,
-                      mainAxisAlignment: MainAxisAlignment.center,
-                      children: [
-                        if (!widget.showButtonsOnHover || !_isHovering)
-                          if (widget.showTitle)
-                            Padding(
-                              padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 1.0),
-                              child: Text(
-                                widget.game.displayName,
-                                maxLines: 2,
-                                textAlign: TextAlign.center,
-                                overflow: TextOverflow.ellipsis,
-                                style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
-                              ),
-                            )
-                          else
-                            const Padding(
-                              padding: EdgeInsets.symmetric(vertical: 1.0),
-                              child: Center(
-                                child: Icon(Icons.more_horiz, size: 16, color: Colors.grey),
-                              ),
-                            ),
-                        
-                        if (!widget.showButtonsOnHover || _isHovering)
-                          Padding(
-                            padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
-                            child: Row(
-                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                              children: [
-                                IconButton(
-                                  visualDensity: VisualDensity.compact,
-                                  iconSize: 22,
-                                  padding: EdgeInsets.zero,
-                                  constraints: const BoxConstraints(),
-                                  icon: const Icon(Icons.download),
-                                  onPressed: widget.onDownload,
-                                  tooltip: 'Download',
+                ValueListenableBuilder<bool>(
+                  valueListenable: _hovering,
+                  builder: (context, isHovering, child) {
+                    return SizedBox(
+                      height: 90,
+                      child: SingleChildScrollView(
+                        physics: const NeverScrollableScrollPhysics(),
+                        child: Column(
+                          mainAxisSize: MainAxisSize.min,
+                          mainAxisAlignment: MainAxisAlignment.center,
+                          children: [
+                            if (!widget.showButtonsOnHover || !isHovering)
+                              if (widget.showTitle)
+                                Padding(
+                                  padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 1.0),
+                                  child: Text(
+                                    widget.game.displayName,
+                                    maxLines: 2,
+                                    textAlign: TextAlign.center,
+                                    overflow: TextOverflow.ellipsis,
+                                    style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
+                                  ),
+                                )
+                              else
+                                const Padding(
+                                  padding: EdgeInsets.symmetric(vertical: 1.0),
+                                  child: Center(
+                                    child: Icon(Icons.more_horiz, size: 16, color: Colors.grey),
+                                  ),
                                 ),
-                                IconButton(
-                                  visualDensity: VisualDensity.compact,
-                                  iconSize: 22,
-                                  padding: EdgeInsets.zero,
-                                  constraints: const BoxConstraints(),
-                                  icon: const Icon(Icons.play_arrow),
-                                  onPressed: widget.onLaunch,
-                                  tooltip: 'Launch',
+                            
+                            if (!widget.showButtonsOnHover || isHovering)
+                              Padding(
+                                padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
+                                child: Row(
+                                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                                  children: [
+                                    IconButton(
+                                      visualDensity: VisualDensity.compact,
+                                      iconSize: 22,
+                                      padding: EdgeInsets.zero,
+                                      constraints: const BoxConstraints(),
+                                      icon: const Icon(Icons.download),
+                                      onPressed: widget.onDownload,
+                                      tooltip: 'Download',
+                                    ),
+                                    IconButton(
+                                      visualDensity: VisualDensity.compact,
+                                      iconSize: 22,
+                                      padding: EdgeInsets.zero,
+                                      constraints: const BoxConstraints(),
+                                      icon: const Icon(Icons.play_arrow),
+                                      onPressed: widget.onLaunch,
+                                      tooltip: 'Launch',
+                                    ),
+                                    if (widget.onSyncSaves != null)
+                                      IconButton(
+                                        visualDensity: VisualDensity.compact,
+                                        iconSize: 18,
+                                        padding: EdgeInsets.zero,
+                                        constraints: const BoxConstraints(),
+                                        icon: const Icon(Icons.cloud_upload),
+                                        onPressed: widget.onSyncSaves,
+                                        tooltip: 'Sync saves',
+                                      ),
+                                  ],
                                 ),
-                                if (widget.onSyncSaves != null)
-                                  IconButton(
-                                    visualDensity: VisualDensity.compact,
-                                    iconSize: 18,
-                                    padding: EdgeInsets.zero,
-                                    constraints: const BoxConstraints(),
-                                    icon: const Icon(Icons.cloud_upload),
-                                    onPressed: widget.onSyncSaves,
-                                    tooltip: 'Sync saves',
-                                  ),
-                              ],
-                            ),
-                          ),
-                      ],
-                    ),
-                  ),
+                              ),
+                          ],
+                        ),
+                      ),
+                    );
+                  }
                 ),
               ],
             ),
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index 55e04aa..42545f9 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -7,8 +7,10 @@ import Foundation
 
 import package_info_plus
 import shared_preferences_foundation
+import sqflite_darwin
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
   FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+  SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
 }
diff --git a/pubspec.lock b/pubspec.lock
index 5018c6d..7a95e6d 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -25,6 +25,30 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.1.2"
+  cached_network_image:
+    dependency: "direct main"
+    description:
+      name: cached_network_image
+      sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.4.0"
+  cached_network_image_platform_interface:
+    dependency: transitive
+    description:
+      name: cached_network_image_platform_interface
+      sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.1.0"
+  cached_network_image_web:
+    dependency: transitive
+    description:
+      name: cached_network_image_web
+      sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.3.0"
   characters:
     dependency: transitive
     description:
@@ -129,11 +153,27 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "8.0.0+1"
+  fixnum:
+    dependency: transitive
+    description:
+      name: fixnum
+      sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.1"
   flutter:
     dependency: "direct main"
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_cache_manager:
+    dependency: transitive
+    description:
+      name: flutter_cache_manager
+      sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.4.0"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -288,6 +328,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "9.3.0"
+  octo_image:
+    dependency: transitive
+    description:
+      name: octo_image
+      sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.0"
   package_info_plus:
     dependency: "direct main"
     description:
@@ -400,6 +448,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.6.1"
+  rxdart:
+    dependency: transitive
+    description:
+      name: rxdart
+      sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.28.0"
   shared_preferences:
     dependency: "direct main"
     description:
@@ -469,6 +525,46 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.10.2"
+  sqflite:
+    dependency: transitive
+    description:
+      name: sqflite
+      sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.2"
+  sqflite_android:
+    dependency: transitive
+    description:
+      name: sqflite_android
+      sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.2+3"
+  sqflite_common:
+    dependency: transitive
+    description:
+      name: sqflite_common
+      sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.5.6"
+  sqflite_darwin:
+    dependency: transitive
+    description:
+      name: sqflite_darwin
+      sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.2"
+  sqflite_platform_interface:
+    dependency: transitive
+    description:
+      name: sqflite_platform_interface
+      sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.0"
   stack_trace:
     dependency: transitive
     description:
@@ -501,6 +597,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.4.1"
+  synchronized:
+    dependency: transitive
+    description:
+      name: synchronized
+      sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.4.0"
   term_glyph:
     dependency: transitive
     description:
@@ -525,6 +629,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.4.0"
+  uuid:
+    dependency: transitive
+    description:
+      name: uuid
+      sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.5.3"
   vector_math:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index ebbfe54..8bc4258 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -35,6 +35,7 @@ dependencies:
   path_provider: ^2.1.2
   shared_preferences: ^2.2.2
   package_info_plus: ^5.0.1
+  cached_network_image: ^3.3.1
 
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.

Clone this wiki locally