-
-
Notifications
You must be signed in to change notification settings - Fork 14
commit 85987ed
abduznik edited this page May 23, 2026
·
1 revision
Commit: 85987ed39e6455886dde20955462842f2b612aa5
Author: abduznik
Date: 2026-05-12
Why: Adds a new feature or capability to the application.
jobs.json | 1 +
.../linux_strategies/emudeck_strategy.dart | 2 +
.../linux_strategies/retrodeck_strategy.dart | 9 ++-
lib/core/storage/directory_service.dart | 48 ++++++-------
lib/ui/screens/library_actions.dart | 5 +-
lib/ui/screens/settings_screen.dart | 84 ++++++++--------------
test/core/emulator/linux_strategies_test.dart | 30 ++++++++
test/unit/firmware_service_test.mocks.dart | 8 +--
test/unit/save_sync_service_test.mocks.dart | 8 +--
test/unit/strategy_registry_test.mocks.dart | 8 +--
test/widgets/library_screen_test.mocks.dart | 8 +--
test/widgets/settings_screen_test.mocks.dart | 8 +--
12 files changed, 117 insertions(+), 102 deletions(-)
jobs.jsonlib/core/emulator/linux_strategies/emudeck_strategy.dartlib/core/emulator/linux_strategies/retrodeck_strategy.dartlib/core/storage/directory_service.dartlib/ui/screens/library_actions.dartlib/ui/screens/settings_screen.darttest/core/emulator/linux_strategies_test.darttest/unit/firmware_service_test.mocks.darttest/unit/save_sync_service_test.mocks.darttest/unit/strategy_registry_test.mocks.darttest/widgets/library_screen_test.mocks.darttest/widgets/settings_screen_test.mocks.dart
diff --git a/jobs.json b/jobs.json
new file mode 100644
index 0000000..ed60395
--- /dev/null
+++ b/jobs.json
@@ -0,0 +1 @@
+{"jobs":[{"completedAt":"2026-05-07T13:14:32Z","conclusion":"success","databaseId":74822265631,"name":"Build Linux","startedAt":"2026-05-07T13:10:17Z","status":"completed","steps":[{"completedAt":"2026-05-07T13:10:19Z","conclusion":"success","name":"Set up job","number":1,"startedAt":"2026-05-07T13:10:18Z","status":"completed"},{"completedAt":"2026-05-07T13:10:20Z","conclusion":"success","name":"Run actions/checkout@v4","number":2,"startedAt":"2026-05-07T13:10:19Z","status":"completed"},{"completedAt":"2026-05-07T13:10:35Z","conclusion":"success","name":"Run subosito/flutter-action@v2","number":3,"startedAt":"2026-05-07T13:10:20Z","status":"completed"},{"completedAt":"2026-05-07T13:13:36Z","conclusion":"success","name":"Install dependencies","number":4,"startedAt":"2026-05-07T13:10:35Z","status":"completed"},{"completedAt":"2026-05-07T13:14:22Z","conclusion":"success","name":"Build Linux","number":5,"startedAt":"2026-05-07T13:13:36Z","status":"completed"},{"completedAt":"2026-05-07T13:14:25Z","conclusion":"success","name":"Package Linux","number":6,"startedAt":"2026-05-07T13:14:22Z","status":"completed"},{"completedAt":"2026-05-07T13:14:28Z","conclusion":"success","name":"Build AppImage","number":7,"startedAt":"2026-05-07T13:14:25Z","status":"completed"},{"completedAt":"2026-05-07T13:14:30Z","conclusion":"success","name":"Upload Linux Artifact","number":8,"startedAt":"2026-05-07T13:14:28Z","status":"completed"},{"completedAt":"2026-05-07T13:14:31Z","conclusion":"success","name":"Post Run subosito/flutter-action@v2","number":15,"startedAt":"2026-05-07T13:14:30Z","status":"completed"},{"completedAt":"2026-05-07T13:14:31Z","conclusion":"success","name":"Post Run actions/checkout@v4","number":16,"startedAt":"2026-05-07T13:14:31Z","status":"completed"},{"completedAt":"2026-05-07T13:14:31Z","conclusion":"success","name":"Complete job","number":17,"startedAt":"2026-05-07T13:14:31Z","status":"completed"}],"url":"https://github.com/abduznik/Freegosy/actions/runs/25497843287/job/74822265631"},{"completedAt":"2026-05-07T13:14:04Z","conclusion":"failure","databaseId":74822265637,"name":"Build Windows Installer","startedAt":"2026-05-07T13:10:16Z","status":"completed","steps":[{"completedAt":"2026-05-07T13:10:19Z","conclusion":"success","name":"Set up job","number":1,"startedAt":"2026-05-07T13:10:17Z","status":"completed"},{"completedAt":"2026-05-07T13:10:24Z","conclusion":"success","name":"Run actions/checkout@v4","number":2,"startedAt":"2026-05-07T13:10:19Z","status":"completed"},{"completedAt":"2026-05-07T13:12:08Z","conclusion":"success","name":"Run subosito/flutter-action@v2","number":3,"startedAt":"2026-05-07T13:10:24Z","status":"completed"},{"completedAt":"2026-05-07T13:12:18Z","conclusion":"success","name":"Install dependencies","number":4,"startedAt":"2026-05-07T13:12:08Z","status":"completed"},{"completedAt":"2026-05-07T13:13:59Z","conclusion":"success","name":"Build Windows Installer","number":5,"startedAt":"2026-05-07T13:12:18Z","status":"completed"},{"completedAt":"2026-05-07T13:14:00Z","conclusion":"failure","name":"Create Zip (Windows)","number":6,"startedAt":"2026-05-07T13:13:59Z","status":"completed"},{"completedAt":"2026-05-07T13:14:00Z","conclusion":"skipped","name":"Upload Windows Artifact","number":7,"startedAt":"2026-05-07T13:14:00Z","status":"completed"},{"completedAt":"2026-05-07T13:14:00Z","conclusion":"success","name":"Post Run subosito/flutter-action@v2","number":13,"startedAt":"2026-05-07T13:14:00Z","status":"completed"},{"completedAt":"2026-05-07T13:14:03Z","conclusion":"success","name":"Post Run actions/checkout@v4","number":14,"startedAt":"2026-05-07T13:14:00Z","status":"completed"},{"completedAt":"2026-05-07T13:14:03Z","conclusion":"success","name":"Complete job","number":15,"startedAt":"2026-05-07T13:14:03Z","status":"completed"}],"url":"https://github.com/abduznik/Freegosy/actions/runs/25497843287/job/74822265637"},{"completedAt":"2026-05-07T13:13:13Z","conclusion":"success","databaseId":74822265675,"name":"Build macOS DMG","startedAt":"2026-05-07T13:10:18Z","status":"completed","steps":[{"completedAt":"2026-05-07T13:10:20Z","conclusion":"success","name":"Set up job","number":1,"startedAt":"2026-05-07T13:10:18Z","status":"completed"},{"completedAt":"2026-05-07T13:10:21Z","conclusion":"success","name":"Run actions/checkout@v4","number":2,"startedAt":"2026-05-07T13:10:20Z","status":"completed"},{"completedAt":"2026-05-07T13:11:10Z","conclusion":"success","name":"Run subosito/flutter-action@v2","number":3,"startedAt":"2026-05-07T13:10:21Z","status":"completed"},{"completedAt":"2026-05-07T13:11:12Z","conclusion":"success","name":"Install dependencies","number":4,"startedAt":"2026-05-07T13:11:10Z","status":"completed"},{"completedAt":"2026-05-07T13:12:41Z","conclusion":"success","name":"Build macOS","number":5,"startedAt":"2026-05-07T13:11:12Z","status":"completed"},{"completedAt":"2026-05-07T13:12:42Z","conclusion":"success","name":"Ad-hoc sign macOS app","number":6,"startedAt":"2026-05-07T13:12:41Z","status":"completed"},{"completedAt":"2026-05-07T13:12:57Z","conclusion":"success","name":"Create DMG","number":7,"startedAt":"2026-05-07T13:12:42Z","status":"completed"},{"completedAt":"2026-05-07T13:13:04Z","conclusion":"success","name":"Create Zip (macOS)","number":8,"startedAt":"2026-05-07T13:12:57Z","status":"completed"},{"completedAt":"2026-05-07T13:13:09Z","conclusion":"success","name":"Upload macOS Artifact","number":9,"startedAt":"2026-05-07T13:13:04Z","status":"completed"},{"completedAt":"2026-05-07T13:13:10Z","conclusion":"success","name":"Post Run subosito/flutter-action@v2","number":17,"startedAt":"2026-05-07T13:13:09Z","status":"completed"},{"completedAt":"2026-05-07T13:13:10Z","conclusion":"success","name":"Post Run actions/checkout@v4","number":18,"startedAt":"2026-05-07T13:13:10Z","status":"completed"},{"completedAt":"2026-05-07T13:13:11Z","conclusion":"success","name":"Complete job","number":19,"startedAt":"2026-05-07T13:13:10Z","status":"completed"}],"url":"https://github.com/abduznik/Freegosy/actions/runs/25497843287/job/74822265675"},{"completedAt":"2026-05-07T13:14:47Z","conclusion":"failure","databaseId":74823055283,"name":"Create Release","startedAt":"2026-05-07T13:14:42Z","status":"completed","steps":[{"completedAt":"2026-05-07T13:14:44Z","conclusion":"success","name":"Set up job","number":1,"startedAt":"2026-05-07T13:14:43Z","status":"completed"},{"completedAt":"2026-05-07T13:14:44Z","conclusion":"success","name":"Create artifacts directory","number":2,"startedAt":"2026-05-07T13:14:44Z","status":"completed"},{"completedAt":"2026-05-07T13:14:45Z","conclusion":"success","name":"Download Linux Artifact","number":3,"startedAt":"2026-05-07T13:14:44Z","status":"completed"},{"completedAt":"2026-05-07T13:14:46Z","conclusion":"failure","name":"Download Windows Artifact","number":4,"startedAt":"2026-05-07T13:14:45Z","status":"completed"},{"completedAt":"2026-05-07T13:14:46Z","conclusion":"skipped","name":"Download macOS Artifact","number":5,"startedAt":"2026-05-07T13:14:46Z","status":"completed"},{"completedAt":"2026-05-07T13:14:46Z","conclusion":"skipped","name":"Create Release","number":6,"startedAt":"2026-05-07T13:14:46Z","status":"completed"},{"completedAt":"2026-05-07T13:14:46Z","conclusion":"success","name":"Complete job","number":7,"startedAt":"2026-05-07T13:14:46Z","status":"completed"}],"url":"https://github.com/abduznik/Freegosy/actions/runs/25497843287/job/74823055283"}]}
diff --git a/lib/core/emulator/linux_strategies/emudeck_strategy.dart b/lib/core/emulator/linux_strategies/emudeck_strategy.dart
index 21a76f1..35bff12 100644
--- a/lib/core/emulator/linux_strategies/emudeck_strategy.dart
+++ b/lib/core/emulator/linux_strategies/emudeck_strategy.dart
@@ -12,6 +12,7 @@ class EmuDeckStrategy extends LinuxEnvironmentStrategy {
@override
String getRomsRoot(String home, String? customPath, String? emudeckRoot) {
+ if (customPath != null) return customPath;
if (emudeckRoot != null) {
// 1. Check if user pointed directly to a 'roms' folder
if (p.basename(emudeckRoot).toLowerCase() == 'roms') return emudeckRoot;
@@ -41,6 +42,7 @@ class EmuDeckStrategy extends LinuxEnvironmentStrategy {
@override
String getEmulatorsRoot(String home, String? customPath, String? emudeckRoot) {
+ if (customPath != null) return customPath;
if (emudeckRoot != null) {
// 1. Check if user pointed directly to 'tools'
if (p.basename(emudeckRoot).toLowerCase() == 'tools') return emudeckRoot;
diff --git a/lib/core/emulator/linux_strategies/retrodeck_strategy.dart b/lib/core/emulator/linux_strategies/retrodeck_strategy.dart
index 0ea2b31..caafaf6 100644
--- a/lib/core/emulator/linux_strategies/retrodeck_strategy.dart
+++ b/lib/core/emulator/linux_strategies/retrodeck_strategy.dart
@@ -12,12 +12,16 @@ class RetroDeckStrategy extends LinuxEnvironmentStrategy {
@override
String getRomsRoot(String home, String? customPath, String? emudeckRoot) {
- return customPath ?? p.join(home, 'retrodeck', 'roms');
+ if (customPath != null) return customPath;
+ if (emudeckRoot != null) return p.join(emudeckRoot, 'roms');
+ return p.join(home, 'retrodeck', 'roms');
}
@override
String getEmulatorsRoot(String home, String? customPath, String? emudeckRoot) {
- return customPath ?? p.join(home, 'retrodeck', 'tools');
+ if (customPath != null) return customPath;
+ if (emudeckRoot != null) return p.join(emudeckRoot, 'tools');
+ return p.join(home, 'retrodeck', 'tools');
}
@override
@@ -45,6 +49,7 @@ class RetroDeckStrategy extends LinuxEnvironmentStrategy {
@override
String getBiosPath(String home, String? emudeckRoot) {
+ if (emudeckRoot != null) return p.join(emudeckRoot, 'bios');
return p.join(home, '.var', 'app', 'net.retrodeck.retrodeck', 'config', 'bios');
}
diff --git a/lib/core/storage/directory_service.dart b/lib/core/storage/directory_service.dart
index 36d4588..ab53601 100644
--- a/lib/core/storage/directory_service.dart
+++ b/lib/core/storage/directory_service.dart
@@ -71,7 +71,7 @@ class DirectoryService {
static const String _romsRootPathKey = 'romsRootPath';
static const String _emulatorsRootPathKey = 'emulatorsRootPath';
static const String _linuxSyncPresetKey = 'linuxSyncPreset';
- static const String _emudeckRootPathKey = 'emudeckRootPath';
+ static const String _linuxPresetRootKey = 'emudeckRootPath'; // Keeping key name for compatibility
// Known extensions per platform slug
static const Map<String, List<String>> platformExtensions = {
@@ -111,7 +111,7 @@ class DirectoryService {
late String romsRootPath;
late String emulatorsRootPath;
String linuxSyncPreset = 'default';
- String? emudeckRootPath;
+ String? linuxPresetRootPath;
final Map<String, String> _emulatorPathOverrides = {};
StorageStatus status = const StorageStatus();
@@ -184,24 +184,24 @@ class DirectoryService {
Future<StorageStatus> initialize() async {
try {
linuxSyncPreset = _prefs.getString(_linuxSyncPresetKey) ?? 'default';
- emudeckRootPath = _prefs.getString(_emudeckRootPathKey);
+ linuxPresetRootPath = _prefs.getString(_linuxPresetRootKey);
// Auto-detection logic for Linux
if (defaultTargetPlatform == TargetPlatform.linux) {
if (linuxSyncPreset == 'auto' || linuxSyncPreset == 'default') {
// If we are in 'auto' or 'default', try to see if EmuDeck or RetroDeck is there.
// BUT: If the user has already explicitly chosen a root, don't overwrite it with auto-detection.
- if (emudeckRootPath == null) {
+ if (linuxPresetRootPath == null) {
final detectedRoot = await detectEmuDeckRoot();
if (detectedRoot != null) {
- emudeckRootPath = detectedRoot;
+ linuxPresetRootPath = detectedRoot;
linuxSyncPreset = 'emudeck';
_linuxStrategy = EmuDeckStrategy();
await _prefs.setString(_linuxSyncPresetKey, 'emudeck');
- await _prefs.setString(_emudeckRootPathKey, emudeckRootPath!);
+ await _prefs.setString(_linuxPresetRootKey, linuxPresetRootPath!);
}
} else if (linuxSyncPreset == 'default') {
- // If they are on 'default' (manual) but have an emudeckRootPath,
+ // If they are on 'default' (manual) but have an linuxPresetRootPath,
// we stay on 'default' unless it's the very first run.
}
@@ -228,13 +228,8 @@ class DirectoryService {
final customRoms = _prefs.getString(_romsRootPathKey);
final customEmus = _prefs.getString(_emulatorsRootPathKey);
- // IMPORTANT: If we are using a preset (EmuDeck/RetroDeck), we IGNORE the manual custom paths
- // to ensure the computed paths from the root always take precedence.
- final String? effectiveCustomRoms = (linuxSyncPreset == 'default') ? customRoms : null;
- final String? effectiveCustomEmus = (linuxSyncPreset == 'default') ? customEmus : null;
-
- romsRootPath = activeLinuxEnvironment.getRomsRoot(home, effectiveCustomRoms, emudeckRootPath);
- emulatorsRootPath = activeLinuxEnvironment.getEmulatorsRoot(home, effectiveCustomEmus, emudeckRootPath);
+ romsRootPath = activeLinuxEnvironment.getRomsRoot(home, customRoms, linuxPresetRootPath);
+ emulatorsRootPath = activeLinuxEnvironment.getEmulatorsRoot(home, customEmus, linuxPresetRootPath);
} else {
romsRootPath = _prefs.getString(_romsRootPathKey) ?? p.join(defaultBase, 'ROMs');
emulatorsRootPath =
@@ -273,7 +268,7 @@ class DirectoryService {
final home = io.Platform.environment['HOME'] ?? '';
if (defaultTargetPlatform == TargetPlatform.linux) {
- romsRootPath = activeLinuxEnvironment.getRomsRoot(home, null, emudeckRootPath);
+ romsRootPath = activeLinuxEnvironment.getRomsRoot(home, null, linuxPresetRootPath);
} else {
romsRootPath = '$base/ROMs';
}
@@ -286,7 +281,7 @@ class DirectoryService {
final home = io.Platform.environment['HOME'] ?? '';
if (defaultTargetPlatform == TargetPlatform.linux) {
- emulatorsRootPath = activeLinuxEnvironment.getEmulatorsRoot(home, null, emudeckRootPath);
+ emulatorsRootPath = activeLinuxEnvironment.getEmulatorsRoot(home, null, linuxPresetRootPath);
} else {
emulatorsRootPath = p.join(base, 'Emulators');
}
@@ -299,13 +294,16 @@ class DirectoryService {
await initialize();
}
- Future<void> setEmudeckRoot(String path) async {
- // If user picked the 'Emulation' folder itself, go up one level
- if (p.basename(path).toLowerCase() == 'emulation') {
- path = p.dirname(path);
+ Future<void> setLinuxPresetRoot(String path) async {
+ // If user picked the 'Emulation' folder or 'retrodeck' folder itself, we might want to handle it
+ // But for now, we just store what they picked and strategies handle it.
+ final name = p.basename(path).toLowerCase();
+ if (name == 'emulation' || name == 'retrodeck') {
+ // If they picked the subfolder instead of the root, we can optionally go up
+ // but let's stay flexible for now.
}
- await _prefs.setString(_emudeckRootPathKey, path);
- emudeckRootPath = path;
+ await _prefs.setString(_linuxPresetRootKey, path);
+ linuxPresetRootPath = path;
// Re-initialize to update paths based on new root
await initialize();
}
@@ -718,7 +716,7 @@ class DirectoryService {
return p.join(appData, emulatorName);
} else if (io.Platform.isLinux) {
final home = io.Platform.environment['HOME'] ?? '';
- return activeLinuxEnvironment.getEmulatorAppSupportDirectory(home, emulatorName, emudeckRootPath, platformSlug: platformSlug);
+ return activeLinuxEnvironment.getEmulatorAppSupportDirectory(home, emulatorName, linuxPresetRootPath, platformSlug: platformSlug);
}
throw UnsupportedError('Platform not supported for save path resolution');
}
@@ -726,7 +724,7 @@ class DirectoryService {
Future<String> getEmulatorBiosDirectory(String emulatorId, {String? platformSlug}) async {
if (io.Platform.isLinux) {
final home = io.Platform.environment['HOME'] ?? '';
- return activeLinuxEnvironment.getBiosPath(home, emudeckRootPath);
+ return activeLinuxEnvironment.getBiosPath(home, linuxPresetRootPath);
}
final emuDir = await getEmulatorDirectory(emulatorId);
final dirPath = p.join(emuDir, 'BIOS');
@@ -776,7 +774,7 @@ class DirectoryService {
}
if (io.Platform.isLinux) {
- final envPath = await activeLinuxEnvironment.findExecutable(emulatorId, executableName, emulatorsRootPath, emudeckRootPath);
+ final envPath = await activeLinuxEnvironment.findExecutable(emulatorId, executableName, emulatorsRootPath, linuxPresetRootPath);
if (envPath != null) return envPath;
}
diff --git a/lib/ui/screens/library_actions.dart b/lib/ui/screens/library_actions.dart
index 754c2d2..d28531e 100644
--- a/lib/ui/screens/library_actions.dart
+++ b/lib/ui/screens/library_actions.dart
@@ -351,9 +351,10 @@ mixin LibraryActionsMixin<T extends ConsumerStatefulWidget> on ConsumerState<T>
ErrorHandler.showInfo(context, 'Syncing', message: 'Auto-syncing saves...');
+ bool ok = false;
if (syncService != null) {
final syncMode = ref.read(retroarchSyncModeProvider);
- final ok = await syncService.pushSaves(game, romPath, syncMode: syncMode);
+ ok = await syncService.pushSaves(game, romPath, syncMode: syncMode);
// --- Safety Sandwich: post-exit backup if save changed ---
try {
@@ -383,7 +384,7 @@ mixin LibraryActionsMixin<T extends ConsumerStatefulWidget> on ConsumerState<T>
if (!context.mounted) return;
if (ok) {
ErrorHandler.showSuccess(context, 'Save Synced', message: 'Saves synced');
- } else {
+ } else if (syncService != null) {
ErrorHandler.showSuccess(context, 'Up to Date', message: 'No files to upload');
}
} catch (e) {
diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart
index a605be0..80377f5 100644
--- a/lib/ui/screens/settings_screen.dart
+++ b/lib/ui/screens/settings_screen.dart
@@ -260,7 +260,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
// --- Storage Section ---
Widget _buildStorageSection(DirectoryService directoryService) {
- final prefs = ref.read(sharedPreferencesProvider);
final preset = directoryService.linuxSyncPreset;
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
@@ -292,67 +291,46 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
if (io.Platform.isLinux && (preset == 'emudeck' || preset == 'retrodeck')) ...[
_buildPathRow(
label: '${preset == 'emudeck' ? 'EmuDeck' : 'RetroDeck'} Installation Root',
- currentPath: preset == 'emudeck'
- ? (directoryService.emudeckRootPath ?? 'Not set')
- : (prefs.getString('retrodeckRootPath') ?? 'Not set'),
+ currentPath: directoryService.linuxPresetRootPath ?? 'Not set',
onChanged: (p) async {
if (p != null) {
- if (preset == 'emudeck') {
- await directoryService.setEmudeckRoot(p);
- } else {
- await prefs.setString('retrodeckRootPath', p);
- await directoryService.initialize();
- }
+ await directoryService.setLinuxPresetRoot(p);
ref.invalidate(directoryServiceProvider);
}
},
),
const SizedBox(height: 16),
- const Text('Computed Paths (Read-only)', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey)),
- const SizedBox(height: 8),
- _buildPathRow(
- label: 'ROMs Directory',
- currentPath: directoryService.romsRootPath,
- onChanged: null,
- ),
- const SizedBox(height: 12),
- _buildPathRow(
- label: 'Emulators Directory',
- currentPath: directoryService.emulatorsRootPath,
- onChanged: null,
- ),
- ] else ...[
- // This handles both non-Linux OSs and Linux 'Manual' mode
- _buildPathRow(
- label: 'ROMs Directory',
- currentPath: directoryService.romsRootPath,
- onChanged: (p) async {
- if (p != null) {
- await directoryService.setRomsRoot(p);
- ref.invalidate(directoryServiceProvider);
- }
- },
- onReset: () async {
- await directoryService.resetRomsRoot();
+ ],
+
+ _buildPathRow(
+ label: 'ROMs Directory',
+ currentPath: directoryService.romsRootPath,
+ onChanged: (p) async {
+ if (p != null) {
+ await directoryService.setRomsRoot(p);
ref.invalidate(directoryServiceProvider);
- },
- ),
- const SizedBox(height: 16),
- _buildPathRow(
- label: 'Emulators Directory',
- currentPath: directoryService.emulatorsRootPath,
- onChanged: (p) async {
- if (p != null) {
- await directoryService.setEmulatorsRoot(p);
- ref.invalidate(directoryServiceProvider);
- }
- },
- onReset: () async {
- await directoryService.resetEmulatorsRoot();
+ }
+ },
+ onReset: () async {
+ await directoryService.resetRomsRoot();
+ ref.invalidate(directoryServiceProvider);
+ },
+ ),
+ const SizedBox(height: 16),
+ _buildPathRow(
+ label: 'Emulators Directory',
+ currentPath: directoryService.emulatorsRootPath,
+ onChanged: (p) async {
+ if (p != null) {
+ await directoryService.setEmulatorsRoot(p);
ref.invalidate(directoryServiceProvider);
- },
- ),
- ],
+ }
+ },
+ onReset: () async {
+ await directoryService.resetEmulatorsRoot();
+ ref.invalidate(directoryServiceProvider);
+ },
+ ),
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: () => SystemUtils.openDirectory(directoryService.romsRootPath),
diff --git a/test/core/emulator/linux_strategies_test.dart b/test/core/emulator/linux_strategies_test.dart
index 3b05502..077a46b 100644
--- a/test/core/emulator/linux_strategies_test.dart
+++ b/test/core/emulator/linux_strategies_test.dart
@@ -81,6 +81,18 @@ void main() {
final result = await emudeckStrategy.findExecutable('cemu', 'Cemu.AppImage', tempHome.path, tempHome.path);
expect(result, p.join(tempHome.path, 'Emulation', 'tools', 'launchers', 'cemu.sh'));
});
+
+ test('Respects custom ROMs path even if EmuDeck root is set', () {
+ final custom = p.join(tempHome.path, 'MyCustomROMs');
+ final result = emudeckStrategy.getRomsRoot(tempHome.path, custom, tempHome.path);
+ expect(result, custom);
+ });
+
+ test('Respects custom Emulators path even if EmuDeck root is set', () {
+ final custom = p.join(tempHome.path, 'MyCustomTools');
+ final result = emudeckStrategy.getEmulatorsRoot(tempHome.path, custom, tempHome.path);
+ expect(result, custom);
+ });
});
group('RetroDeckStrategy', () {
@@ -101,6 +113,24 @@ void main() {
final result = retrodeckStrategy.getEmulatorAppSupportDirectory(tempHome.path, 'pcsx2', null);
expect(result, p.join(retrodeckVarConfig.path, 'PCSX2', 'saves'));
});
+
+ test('Respects custom ROMs path', () {
+ final custom = p.join(tempHome.path, 'MyRetroROMs');
+ final result = retrodeckStrategy.getRomsRoot(tempHome.path, custom, null);
+ expect(result, custom);
+ });
+
+ test('Resolves ROMs relative to installation root (e.g. SD Card)', () {
+ final sdCardRoot = '/run/media/deck/SDCARD/retrodeck';
+ final result = retrodeckStrategy.getRomsRoot(tempHome.path, null, sdCardRoot);
+ expect(result, p.join(sdCardRoot, 'roms'));
+ });
+
+ test('Resolves BIOS relative to installation root (e.g. SD Card)', () {
+ final sdCardRoot = '/run/media/deck/SDCARD/retrodeck';
+ final result = retrodeckStrategy.getBiosPath(tempHome.path, sdCardRoot);
+ expect(result, p.join(sdCardRoot, 'bios'));
+ });
});
});
}
diff --git a/test/unit/firmware_service_test.mocks.dart b/test/unit/firmware_service_test.mocks.dart
index 22ecb61..557a29e 100644
--- a/test/unit/firmware_service_test.mocks.dart
+++ b/test/unit/firmware_service_test.mocks.dart
@@ -511,8 +511,8 @@ class MockDirectoryService extends _i1.Mock implements _i4.DirectoryService {
);
@override
- set emudeckRootPath(String? value) => super.noSuchMethod(
- Invocation.setter(#emudeckRootPath, value),
+ set linuxPresetRootPath(String? value) => super.noSuchMethod(
+ Invocation.setter(#linuxPresetRootPath, value),
returnValueForMissingStub: null,
);
@@ -581,9 +581,9 @@ class MockDirectoryService extends _i1.Mock implements _i4.DirectoryService {
as _i8.Future<void>);
@override
- _i8.Future<void> setEmudeckRoot(String? path) =>
+ _i8.Future<void> setLinuxPresetRoot(String? path) =>
(super.noSuchMethod(
- Invocation.method(#setEmudeckRoot, [path]),
+ Invocation.method(#setLinuxPresetRoot, [path]),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
)
diff --git a/test/unit/save_sync_service_test.mocks.dart b/test/unit/save_sync_service_test.mocks.dart
index 923c94c..376ddbd 100644
--- a/test/unit/save_sync_service_test.mocks.dart
+++ b/test/unit/save_sync_service_test.mocks.dart
@@ -511,8 +511,8 @@ class MockDirectoryService extends _i1.Mock implements _i4.DirectoryService {
);
@override
- set emudeckRootPath(String? value) => super.noSuchMethod(
- Invocation.setter(#emudeckRootPath, value),
+ set linuxPresetRootPath(String? value) => super.noSuchMethod(
+ Invocation.setter(#linuxPresetRootPath, value),
returnValueForMissingStub: null,
);
@@ -581,9 +581,9 @@ class MockDirectoryService extends _i1.Mock implements _i4.DirectoryService {
as _i8.Future<void>);
@override
- _i8.Future<void> setEmudeckRoot(String? path) =>
+ _i8.Future<void> setLinuxPresetRoot(String? path) =>
(super.noSuchMethod(
- Invocation.method(#setEmudeckRoot, [path]),
+ Invocation.method(#setLinuxPresetRoot, [path]),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
)
diff --git a/test/unit/strategy_registry_test.mocks.dart b/test/unit/strategy_registry_test.mocks.dart
index e58387b..7e725a9 100644
--- a/test/unit/strategy_registry_test.mocks.dart
+++ b/test/unit/strategy_registry_test.mocks.dart
@@ -123,8 +123,8 @@ class MockDirectoryService extends _i1.Mock implements _i2.DirectoryService {
);
@override
- set emudeckRootPath(String? value) => super.noSuchMethod(
- Invocation.setter(#emudeckRootPath, value),
+ set linuxPresetRootPath(String? value) => super.noSuchMethod(
+ Invocation.setter(#linuxPresetRootPath, value),
returnValueForMissingStub: null,
);
@@ -193,9 +193,9 @@ class MockDirectoryService extends _i1.Mock implements _i2.DirectoryService {
as _i5.Future<void>);
@override
- _i5.Future<void> setEmudeckRoot(String? path) =>
+ _i5.Future<void> setLinuxPresetRoot(String? path) =>
(super.noSuchMethod(
- Invocation.method(#setEmudeckRoot, [path]),
+ Invocation.method(#setLinuxPresetRoot, [path]),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
)
diff --git a/test/widgets/library_screen_test.mocks.dart b/test/widgets/library_screen_test.mocks.dart
index f56efbf..7e53e1b 100644
--- a/test/widgets/library_screen_test.mocks.dart
+++ b/test/widgets/library_screen_test.mocks.dart
@@ -512,8 +512,8 @@ class MockDirectoryService extends _i1.Mock implements _i4.DirectoryService {
);
@override
- set emudeckRootPath(String? value) => super.noSuchMethod(
- Invocation.setter(#emudeckRootPath, value),
+ set linuxPresetRootPath(String? value) => super.noSuchMethod(
+ Invocation.setter(#linuxPresetRootPath, value),
returnValueForMissingStub: null,
);
@@ -582,9 +582,9 @@ class MockDirectoryService extends _i1.Mock implements _i4.DirectoryService {
as _i8.Future<void>);
@override
- _i8.Future<void> setEmudeckRoot(String? path) =>
+ _i8.Future<void> setLinuxPresetRoot(String? path) =>
(super.noSuchMethod(
- Invocation.method(#setEmudeckRoot, [path]),
+ Invocation.method(#setLinuxPresetRoot, [path]),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
)
diff --git a/test/widgets/settings_screen_test.mocks.dart b/test/widgets/settings_screen_test.mocks.dart
index 85d5435..e112d81 100644
--- a/test/widgets/settings_screen_test.mocks.dart
+++ b/test/widgets/settings_screen_test.mocks.dart
@@ -140,8 +140,8 @@ class MockDirectoryService extends _i1.Mock implements _i2.DirectoryService {
);
@override
- set emudeckRootPath(String? value) => super.noSuchMethod(
- Invocation.setter(#emudeckRootPath, value),
+ set linuxPresetRootPath(String? value) => super.noSuchMethod(
+ Invocation.setter(#linuxPresetRootPath, value),
returnValueForMissingStub: null,
);
@@ -210,9 +210,9 @@ class MockDirectoryService extends _i1.Mock implements _i2.DirectoryService {
as _i7.Future<void>);
@override
- _i7.Future<void> setEmudeckRoot(String? path) =>
+ _i7.Future<void> setLinuxPresetRoot(String? path) =>
(super.noSuchMethod(
- Invocation.method(#setEmudeckRoot, [path]),
+ Invocation.method(#setLinuxPresetRoot, [path]),
returnValue: _i7.Future<void>.value(),
returnValueForMissingStub: _i7.Future<void>.value(),
)