-
-
Notifications
You must be signed in to change notification settings - Fork 14
commit c8b3117
abduznik edited this page May 23, 2026
·
1 revision
Commit: c8b311735adefea7c6f68cd9b40306a0de36754c
Author: abduznik
Date: 2026-05-23
Why: Fixes a bug or regression in the existing codebase.
lib/core/constants/app_constants.dart | 2 +-
lib/core/romm/rom_constants.dart | 5 +
lib/core/romm/rom_scanner_service.dart | 2 +-
lib/core/romm/romm_service.dart | 2 +-
lib/core/storage/directory_service.dart | 27 +++++-
pubspec.yaml | 2 +-
test/core/emudeck_n3ds_edge_case_test.dart | 151 +++++++++++++++++++++++++++++
7 files changed, 186 insertions(+), 5 deletions(-)
lib/core/constants/app_constants.dartlib/core/romm/rom_constants.dartlib/core/romm/rom_scanner_service.dartlib/core/romm/romm_service.dartlib/core/storage/directory_service.dartpubspec.yamltest/core/emudeck_n3ds_edge_case_test.dart
diff --git a/lib/core/constants/app_constants.dart b/lib/core/constants/app_constants.dart
index f3a6993..4b6b0a5 100644
--- a/lib/core/constants/app_constants.dart
+++ b/lib/core/constants/app_constants.dart
@@ -1,3 +1,3 @@
class AppConstants {
- static const String version = '0.5.0'; // Unified version
+ static const String version = '0.5.1'; // Unified version
}
diff --git a/lib/core/romm/rom_constants.dart b/lib/core/romm/rom_constants.dart
index 755b09f..bc31a97 100644
--- a/lib/core/romm/rom_constants.dart
+++ b/lib/core/romm/rom_constants.dart
@@ -24,6 +24,11 @@ class RomConstants {
'megadrive': ['.md', '.bin', '.gen', '.zip', '.7z'],
'genesis': ['.md', '.bin', '.gen', '.zip', '.7z'],
'3ds': ['.3ds', '.cia', '.app'],
+ 'n3ds': ['.3ds', '.cia', '.app'],
+ 'nintendo-3ds': ['.3ds', '.cia', '.app'],
+ 'nintendo3ds': ['.3ds', '.cia', '.app'],
+ 'new-nintendo-3ds': ['.3ds', '.cia', '.app'],
+ 'new-nintendo-3ds-xl': ['.3ds', '.cia', '.app'],
'wiiu': ['.wua', '.rpx', '.wud', '.wux'],
};
diff --git a/lib/core/romm/rom_scanner_service.dart b/lib/core/romm/rom_scanner_service.dart
index 3935613..2bbb830 100644
--- a/lib/core/romm/rom_scanner_service.dart
+++ b/lib/core/romm/rom_scanner_service.dart
@@ -45,7 +45,7 @@ class RomScannerService {
final storedMTimes = _mappingService.getMTimes();
for (final dir in platformDirs) {
final platformSlug = p.basename(dir.path).toLowerCase();
- final platformId = platformSlugToId[platformSlug];
+ final platformId = platformSlugToId[platformSlug] ?? platformSlugToId[DirectoryService.platformFolderCanonicalMap[platformSlug]];
if (platformId == null) continue;
final stat = await dir.stat();
diff --git a/lib/core/romm/romm_service.dart b/lib/core/romm/romm_service.dart
index 744d379..141189c 100644
--- a/lib/core/romm/romm_service.dart
+++ b/lib/core/romm/romm_service.dart
@@ -18,7 +18,7 @@ class RommService {
RomMConfig get config => _config;
- static const String _ua = 'Freegosy/0.5.0';
+ static const String _ua = 'Freegosy/0.5.1';
void updateConfig(RomMConfig newConfig) {
_config = newConfig;
diff --git a/lib/core/storage/directory_service.dart b/lib/core/storage/directory_service.dart
index 079fc3f..d4372ab 100644
--- a/lib/core/storage/directory_service.dart
+++ b/lib/core/storage/directory_service.dart
@@ -15,6 +15,22 @@ import 'package:freegosy/core/emulator/linux_strategies/emudeck_strategy.dart';
import 'package:freegosy/core/emulator/linux_strategies/retrodeck_strategy.dart';
class DirectoryService {
+ static const Map<String, String> platformFolderCanonicalMap = {
+ 'n3ds': '3ds',
+ 'nintendo-3ds': '3ds',
+ 'nintendo3ds': '3ds',
+ 'new-nintendo-3ds': '3ds',
+ 'new-nintendo-3ds-xl': '3ds',
+ };
+
+ static const Map<String, String> _emudeckFolderAliases = {
+ '3ds': 'n3ds',
+ 'nintendo-3ds': 'n3ds',
+ 'nintendo3ds': 'n3ds',
+ 'new-nintendo-3ds': 'n3ds',
+ 'new-nintendo-3ds-xl': 'n3ds',
+ };
+
static const String _romsRootPathKey = 'romsRootPath';
static const String _emulatorsRootPathKey = 'emulatorsRootPath';
static const String _linuxSyncPresetKey = 'linuxSyncPreset';
@@ -236,9 +252,18 @@ class DirectoryService {
Future<String> getRomsDirectory() async => romsRootPath;
+ String _resolveFolderName(String platformSlug) {
+ final lower = platformSlug.toLowerCase();
+ if (linuxSyncPreset == 'emudeck') {
+ return _emudeckFolderAliases[lower] ?? lower;
+ }
+ return lower;
+ }
+
Future<String> getRomDirectory(Game game) async {
final platformSlug = game.platformSlug ?? 'unknown';
- final dirPath = p.join(romsRootPath, platformSlug);
+ final folderName = _resolveFolderName(platformSlug);
+ final dirPath = p.join(romsRootPath, folderName);
await _ensureDirectoryExists(dirPath);
return dirPath;
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 1afa6da..1773fd0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 0.5.0+1
+version: 0.5.1+1
environment:
diff --git a/test/core/emudeck_n3ds_edge_case_test.dart b/test/core/emudeck_n3ds_edge_case_test.dart
new file mode 100644
index 0000000..c3a731e
--- /dev/null
+++ b/test/core/emudeck_n3ds_edge_case_test.dart
@@ -0,0 +1,151 @@
+import 'dart:io';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:path/path.dart' as p;
+import 'package:freegosy/core/storage/directory_service.dart';
+import 'package:freegosy/core/romm/romm_models.dart';
+import 'package:freegosy/core/romm/rom_constants.dart';
+
+void main() {
+ group('EmuDeck n3ds folder edge case', () {
+ late Directory tempDir;
+
+ setUp(() async {
+ tempDir = await Directory.systemTemp.createTemp('freegosy_n3ds_test_');
+ SharedPreferences.setMockInitialValues({});
+ });
+
+ tearDown(() async {
+ if (await tempDir.exists()) {
+ await tempDir.delete(recursive: true);
+ }
+ });
+
+ group('DirectoryService.getRomDirectory', () {
+ Future<DirectoryService> createService(String preset) async {
+ final prefs = await SharedPreferences.getInstance();
+ final service = DirectoryService(prefs);
+ service.romsRootPath = tempDir.path;
+ service.linuxSyncPreset = preset;
+ return service;
+ }
+
+ test('maps 3ds platformSlug to n3ds folder on EmuDeck preset', () async {
+ final service = await createService('emudeck');
+ await Directory(p.join(tempDir.path, 'n3ds')).create(recursive: true);
+
+ final game = Game(id: '1', name: 'Test', platformSlug: '3ds', fileSize: 0);
+ final romDir = await service.getRomDirectory(game);
+
+ expect(romDir, p.join(tempDir.path, 'n3ds'));
+ });
+
+ test('maps all 3DS slug variants to n3ds folder on EmuDeck', () async {
+ final service = await createService('emudeck');
+ await Directory(p.join(tempDir.path, 'n3ds')).create(recursive: true);
+
+ final slugs = ['3ds', 'n3ds', 'nintendo-3ds', 'nintendo3ds', 'new-nintendo-3ds', 'new-nintendo-3ds-xl'];
+ for (final slug in slugs) {
+ final game = Game(id: 'x', name: 'Test', platformSlug: slug, fileSize: 0);
+ final romDir = await service.getRomDirectory(game);
+ expect(romDir, p.join(tempDir.path, 'n3ds'), reason: 'slug "$slug" should map to n3ds');
+ }
+ });
+
+ test('uses slug directly as folder name on default preset', () async {
+ final service = await createService('default');
+
+ final game = Game(id: '1', name: 'Test', platformSlug: '3ds', fileSize: 0);
+ final romDir = await service.getRomDirectory(game);
+
+ expect(romDir, p.join(tempDir.path, '3ds'));
+ });
+ });
+
+ group('DirectoryService.findExistingRomPath on EmuDeck', () {
+ test('finds 3ds game in n3ds folder on EmuDeck preset', () async {
+ final prefs = await SharedPreferences.getInstance();
+ final service = DirectoryService(prefs);
+ service.romsRootPath = tempDir.path;
+ service.linuxSyncPreset = 'emudeck';
+
+ await Directory(p.join(tempDir.path, 'n3ds')).create(recursive: true);
+ final romFile = File(p.join(tempDir.path, 'n3ds', 'TestGame.3ds'));
+ await romFile.writeAsString('fake rom');
+
+ final game = Game(
+ id: '1',
+ name: 'TestGame',
+ platformSlug: '3ds',
+ fileName: 'TestGame.3ds',
+ fsName: 'TestGame.3ds',
+ fileSize: 100,
+ );
+
+ final foundPath = await service.findExistingRomPath(game);
+
+ expect(foundPath, isNotNull);
+ expect(foundPath, endsWith(p.join('n3ds', 'TestGame.3ds')));
+ });
+
+ test('finds all 3DS slug variant games in n3ds folder on EmuDeck', () async {
+ final prefs = await SharedPreferences.getInstance();
+ final service = DirectoryService(prefs);
+ service.romsRootPath = tempDir.path;
+ service.linuxSyncPreset = 'emudeck';
+
+ await Directory(p.join(tempDir.path, 'n3ds')).create(recursive: true);
+
+ final slugs = ['3ds', 'n3ds', 'nintendo-3ds', 'nintendo3ds', 'new-nintendo-3ds', 'new-nintendo-3ds-xl'];
+ for (int i = 0; i < slugs.length; i++) {
+ final romFile = File(p.join(tempDir.path, 'n3ds', 'Game$i.3ds'));
+ await romFile.writeAsString('fake rom $i');
+
+ final game = Game(
+ id: '$i',
+ name: 'Game$i',
+ platformSlug: slugs[i],
+ fileName: 'Game$i.3ds',
+ fsName: 'Game$i.3ds',
+ fileSize: 100,
+ );
+
+ final foundPath = await service.findExistingRomPath(game);
+ expect(foundPath, isNotNull, reason: 'slug "${slugs[i]}" should find game in n3ds');
+ expect(foundPath, endsWith(p.join('n3ds', 'Game$i.3ds')));
+ }
+ });
+ });
+
+ group('Scanner folder alias map', () {
+ test('exposes platformFolderCanonicalMap for scanner reverse lookup', () {
+ expect(DirectoryService.platformFolderCanonicalMap['n3ds'], equals('3ds'));
+ expect(DirectoryService.platformFolderCanonicalMap['nintendo-3ds'], equals('3ds'));
+ expect(DirectoryService.platformFolderCanonicalMap['nintendo3ds'], equals('3ds'));
+ expect(DirectoryService.platformFolderCanonicalMap['new-nintendo-3ds'], equals('3ds'));
+ expect(DirectoryService.platformFolderCanonicalMap['new-nintendo-3ds-xl'], equals('3ds'));
+ expect(DirectoryService.platformFolderCanonicalMap['3ds'], isNull);
+ });
+ });
+
+ group('RomConstants platformExtensions for n3ds', () {
+ test('includes n3ds as a known platform extension key', () {
+ final extensions = RomConstants.platformExtensions['n3ds'];
+ expect(extensions, isNotNull);
+ expect(extensions!.contains('.3ds'), isTrue);
+ expect(extensions.contains('.cia'), isTrue);
+ expect(extensions.contains('.app'), isTrue);
+ });
+
+ test('includes all 3DS variant extension keys', () {
+ for (final slug in ['n3ds', 'nintendo-3ds', 'nintendo3ds', 'new-nintendo-3ds', 'new-nintendo-3ds-xl']) {
+ final extensions = RomConstants.platformExtensions[slug];
+ expect(extensions, isNotNull, reason: 'slug "$slug" should have extension entries');
+ expect(extensions!.contains('.3ds'), isTrue, reason: 'slug "$slug" missing .3ds');
+ expect(extensions.contains('.cia'), isTrue, reason: 'slug "$slug" missing .cia');
+ expect(extensions.contains('.app'), isTrue, reason: 'slug "$slug" missing .app');
+ }
+ });
+ });
+ });
+}