From c74839fd36c9a4e14b29b791936b593635c16ffd Mon Sep 17 00:00:00 2001 From: ClausValcaTD <1.93546948e+08+ClausValcaTD@users.noreply.github.com> Date: Sat, 3 Oct 2026 22:29:19 +0000 Subject: [PATCH 1/5] ci: align formatter and quality gate toolchains --- .github/workflows/auto_fix.yml | 5 ++++- .github/workflows/quality.yml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto_fix.yml b/.github/workflows/auto_fix.yml index bca72e2..b80630e 100644 --- a/.github/workflows/auto_fix.yml +++ b/.github/workflows/auto_fix.yml @@ -16,17 +16,20 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + flutter-version: '3.47.6' + cache: true - name: Auto-Format Dart run: | if [ -d "gui" ]; then cd gui + flutter pub get dart format . cd .. fi if [ -d "core" ]; then cd core + dart pub get dart format . cd .. fi diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 7b49fb6..4fae323 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -29,7 +29,7 @@ jobs: - name: Set up Flutter uses: subosito/flutter-action@v2 with: - channel: stable + flutter-version: '3.47.6' cache: true - name: Install GUI dependencies From 6f4e8b92a3d6b67b4c7662bd241b8ca0e2a12775 Mon Sep 17 00:00:00 2001 From: ClausValcaTD <193546948+ClausValcaTD@users.noreply.github.com> Date: Sat, 3 Oct 2026 22:29:59 +0000 Subject: [PATCH 2/5] style: auto format dart files --- core/bin/openrom.dart | 8 +- core/lib/src/bin_merger.dart | 10 +- core/lib/src/compressor.dart | 4 +- core/lib/src/config.dart | 12 +- core/lib/src/converter.dart | 15 +- core/lib/src/dcp_patcher.dart | 5 +- core/lib/src/gdi_reader.dart | 11 +- core/lib/src/header_remover.dart | 5 +- core/lib/src/m3u_generator.dart | 3 +- core/lib/src/rom_renamer.dart | 11 +- core/test/dcp_builder_test.dart | 16 +- core/test/ssp_builder_test.dart | 13 +- gui/analysis_options.yaml | 6 + gui/lib/l10n/app_localizations.dart | 47 +++-- gui/lib/l10n/app_localizations_ar.dart | 1 - gui/lib/l10n/app_localizations_de.dart | 1 - gui/lib/l10n/app_localizations_en.dart | 1 - gui/lib/l10n/app_localizations_es.dart | 1 - gui/lib/l10n/app_localizations_fr.dart | 1 - gui/lib/l10n/app_localizations_it.dart | 1 - gui/lib/l10n/app_localizations_ja.dart | 1 - gui/lib/l10n/app_localizations_pt.dart | 1 - gui/lib/l10n/app_localizations_ru.dart | 1 - gui/lib/l10n/app_localizations_tr.dart | 1 - gui/lib/l10n/app_localizations_zh.dart | 1 - gui/lib/models/conversion_job.dart | 4 +- gui/lib/models/rom_file.dart | 6 +- gui/lib/patcher/aps_gba_builder.dart | 10 +- gui/lib/patcher/aps_n64_patcher.dart | 9 +- gui/lib/patcher/aps_patcher.dart | 16 +- gui/lib/patcher/dcp_patcher.dart | 4 +- gui/lib/patcher/ups_patcher.dart | 5 +- gui/lib/screens/dreamcast_screen.dart | 24 ++- gui/lib/screens/home_screen.dart | 5 +- gui/lib/screens/patcher_screen.dart | 16 +- gui/lib/screens/settings_screen.dart | 7 +- gui/lib/screens/theme_editor_screen.dart | 5 +- gui/lib/screens/tools_screen.dart | 79 ++++---- gui/lib/services/core_bridge.dart | 188 ++++++++++---------- gui/lib/services/patch_builder_service.dart | 3 +- gui/lib/services/patcher_service.dart | 3 +- gui/lib/services/theme_service.dart | 20 +-- gui/lib/services/tools_service.dart | 122 ++++++------- gui/lib/widgets/rom_card.dart | 8 +- gui/lib/widgets/sidebar.dart | 5 +- gui/lib/widgets/top_bar.dart | 5 +- gui/pubspec.lock | 22 +-- 47 files changed, 341 insertions(+), 402 deletions(-) diff --git a/core/bin/openrom.dart b/core/bin/openrom.dart index 46a8a46..b2162ec 100644 --- a/core/bin/openrom.dart +++ b/core/bin/openrom.dart @@ -355,8 +355,7 @@ int main(List args) { if ((results['m3u'] as List).isNotEmpty) { final discFiles = results['m3u'] as List; - final out = - results['output'] as String? ?? + final out = results['output'] as String? ?? p.dirname(p.canonicalize(discFiles.first)); final rel = results['absolute'] != true; try { @@ -714,9 +713,8 @@ int main(List args) { var fields = readIpbin(file); if (results['set-title'] != null) { final title = results['set-title'] as String; - fields['product_name'] = title.length > 16 - ? title.substring(0, 16) - : title; + fields['product_name'] = + title.length > 16 ? title.substring(0, 16) : title; fields['product_name_2'] = title.length > 16 ? title.substring(16, title.length > 32 ? 32 : title.length) : ''; diff --git a/core/lib/src/bin_merger.dart b/core/lib/src/bin_merger.dart index 2b527f2..ce362e1 100644 --- a/core/lib/src/bin_merger.dart +++ b/core/lib/src/bin_merger.dart @@ -120,9 +120,8 @@ BinMergeResult mergeBins( } final cueDir = p.dirname(p.canonicalize(cuePath)); - final targetDir = (outputDir != null && outputDir.isNotEmpty) - ? outputDir - : cueDir; + final targetDir = + (outputDir != null && outputDir.isNotEmpty) ? outputDir : cueDir; Directory(targetDir).createSync(recursive: true); final tracks = parseCue(cuePath); @@ -182,9 +181,8 @@ BinMergeResult mergeBins( break; } } - final idx01Offset = idx01Entry != null - ? _msfToSectors(idx01Entry.msf) - : 0; + final idx01Offset = + idx01Entry != null ? _msfToSectors(idx01Entry.msf) : 0; computedIndexes = []; for (final idxItem in parsedIndexes) { diff --git a/core/lib/src/compressor.dart b/core/lib/src/compressor.dart index d88134d..b89b085 100644 --- a/core/lib/src/compressor.dart +++ b/core/lib/src/compressor.dart @@ -37,8 +37,8 @@ class CompressionJob { this.status = 'Queued', this.progress = 0.0, this.error, - }) : format = format.toLowerCase(), - level = level.toLowerCase(); + }) : format = format.toLowerCase(), + level = level.toLowerCase(); } class Compressor { diff --git a/core/lib/src/config.dart b/core/lib/src/config.dart index e3872f7..80aa930 100644 --- a/core/lib/src/config.dart +++ b/core/lib/src/config.dart @@ -11,8 +11,7 @@ const String appName = 'OpenROM'; String getDirectoryConfig() { String path; if (Platform.isWindows) { - final base = - Platform.environment['APPDATA'] ?? + final base = Platform.environment['APPDATA'] ?? Platform.environment['USERPROFILE'] ?? ''; path = p.join(base, appName); @@ -64,8 +63,8 @@ String getDefaultBundledPath(String tool) { } final String archFolder; - final archStr = '${Platform.version} ${Platform.operatingSystem}' - .toLowerCase(); + final archStr = + '${Platform.version} ${Platform.operatingSystem}'.toLowerCase(); if (archStr.contains('aarch64') || archStr.contains('arm64')) { archFolder = 'arm64'; } else { @@ -136,9 +135,8 @@ String getDefaultBundledPath(String tool) { return bundled; } - final fallback = Platform.isWindows - ? (winNames[tool] ?? tool) - : (unixNames[tool] ?? tool); + final fallback = + Platform.isWindows ? (winNames[tool] ?? tool) : (unixNames[tool] ?? tool); ensureExecutable(fallback); return fallback; } diff --git a/core/lib/src/converter.dart b/core/lib/src/converter.dart index dca9dc0..811d57d 100644 --- a/core/lib/src/converter.dart +++ b/core/lib/src/converter.dart @@ -59,8 +59,8 @@ class ConversionJob { List? logLines, this.error, List? tempFiles, - }) : logLines = logLines ?? [], - tempFiles = tempFiles ?? []; + }) : logLines = logLines ?? [], + tempFiles = tempFiles ?? []; Map getFileInfo() { if (_fileInfo == null) { @@ -135,8 +135,7 @@ class Converter { final valid = detector.getValidTargets(fmt); if (!valid.contains(tgt) && tgt != 'BIN/CUE' && tgt != 'FILES') { - final err = - 'Cannot convert $fmt → $tgt. ' + final err = 'Cannot convert $fmt → $tgt. ' 'Supported targets for $fmt: ${valid.isNotEmpty ? valid.join(', ') : 'none'}'; _log('[ERROR] $err'); job.error = err; @@ -215,9 +214,8 @@ class Converter { subCmd = 'createcd'; } - final compressionMap = subCmd == 'createcd' - ? chdCdCompression - : chdDvdCompression; + final compressionMap = + subCmd == 'createcd' ? chdCdCompression : chdDvdCompression; final defaultCodec = subCmd == 'createcd' ? 'cdlz' : 'zlib'; final cmd = [ chdman, @@ -396,8 +394,7 @@ class Converter { bool _xboxConvert(ConversionJob job, String src, String fmt) { if (Platform.isMacOS) { - job.error = - 'Xbox format conversion is not supported on macOS. ' + job.error = 'Xbox format conversion is not supported on macOS. ' 'XGDTool does not currently provide a macOS build.'; _log(' ❌ ${job.error}'); return false; diff --git a/core/lib/src/dcp_patcher.dart b/core/lib/src/dcp_patcher.dart index 885fbcb..a2ac9f3 100644 --- a/core/lib/src/dcp_patcher.dart +++ b/core/lib/src/dcp_patcher.dart @@ -184,9 +184,8 @@ class DcpPatcher { final basePath = p.normalize(p.canonicalize(outputDir)); for (final entry in xdeltaEntries) { - final archivePrefix = entry.name.startsWith('xdelta\\') - ? 'xdelta\\' - : 'xdelta/'; + final archivePrefix = + entry.name.startsWith('xdelta\\') ? 'xdelta\\' : 'xdelta/'; final relativePatchName = entry.name.substring(archivePrefix.length); String targetName = relativePatchName.replaceAll('\\', '/'); diff --git a/core/lib/src/gdi_reader.dart b/core/lib/src/gdi_reader.dart index 75df8a8..6c6b59d 100644 --- a/core/lib/src/gdi_reader.dart +++ b/core/lib/src/gdi_reader.dart @@ -44,10 +44,8 @@ List parseGdi(String gdiPath) { final gdiDir = p.dirname(p.canonicalize(gdiPath)); final tracks = []; - final lines = file - .readAsLinesSync() - .where((l) => l.trim().isNotEmpty) - .toList(); + final lines = + file.readAsLinesSync().where((l) => l.trim().isNotEmpty).toList(); if (lines.isEmpty) return tracks; for (final line in lines.skip(1)) { @@ -64,9 +62,8 @@ List parseGdi(String gdiPath) { filename = filename.substring(1, filename.length - 1); } final filepath = p.join(gdiDir, filename); - final filesize = File(filepath).existsSync() - ? File(filepath).lengthSync() - : 0; + final filesize = + File(filepath).existsSync() ? File(filepath).lengthSync() : 0; tracks.add( GdiTrack( diff --git a/core/lib/src/header_remover.dart b/core/lib/src/header_remover.dart index 1b610da..75d7ccf 100644 --- a/core/lib/src/header_remover.dart +++ b/core/lib/src/header_remover.dart @@ -94,9 +94,8 @@ Map? detectHeader(String filepath) { final hasCopierHeaderInternal = _verifySnesInternalHeader(headerData, 0x81C0) || - _verifySnesInternalHeader(headerData, 0x101C0); - final hasCleanInternal = - _verifySnesInternalHeader(headerData, 0x7FC0) || + _verifySnesInternalHeader(headerData, 0x101C0); + final hasCleanInternal = _verifySnesInternalHeader(headerData, 0x7FC0) || _verifySnesInternalHeader(headerData, 0xFFC0); if (hasCopierHeaderInternal) { diff --git a/core/lib/src/m3u_generator.dart b/core/lib/src/m3u_generator.dart index 09f4f9e..7f7fd9e 100644 --- a/core/lib/src/m3u_generator.dart +++ b/core/lib/src/m3u_generator.dart @@ -20,8 +20,7 @@ String generateM3u({ } } - final targetDir = - Directory(outputPath).existsSync() || + final targetDir = Directory(outputPath).existsSync() || !outputPath.toLowerCase().endsWith('.m3u') ? outputPath : p.dirname(outputPath); diff --git a/core/lib/src/rom_renamer.dart b/core/lib/src/rom_renamer.dart index 97fb64f..8088229 100644 --- a/core/lib/src/rom_renamer.dart +++ b/core/lib/src/rom_renamer.dart @@ -164,10 +164,8 @@ String calcCrc32(String filepath, {void Function(double pct)? onProgress}) { raf.closeSync(); } - final hex = (crc & 0xFFFFFFFF) - .toRadixString(16) - .toLowerCase() - .padLeft(8, '0'); + final hex = + (crc & 0xFFFFFFFF).toRadixString(16).toLowerCase().padLeft(8, '0'); return hex; } @@ -216,9 +214,8 @@ Map> loadDatIndex(String datPath) { if (crc.isNotEmpty) { index[crc] = { 'name': currentGameName, - 'description': currentDesc.isNotEmpty - ? currentDesc - : currentGameName, + 'description': + currentDesc.isNotEmpty ? currentDesc : currentGameName, 'rom_name': romName, 'size': size, 'md5': md5, diff --git a/core/test/dcp_builder_test.dart b/core/test/dcp_builder_test.dart index 83081c6..d336c47 100644 --- a/core/test/dcp_builder_test.dart +++ b/core/test/dcp_builder_test.dart @@ -91,14 +91,14 @@ void main() { expect(result['success'], isTrue); - final patchedIpBin = File(p.join(outputDir.path, 'IP.BIN')) - .readAsStringSync(); - final patched1stRead = File(p.join(outputDir.path, '1ST_READ.BIN')) - .readAsStringSync(); - final patchedConfig = File(p.join(outputDir.path, 'DATA', 'CONFIG.TXT')) - .readAsStringSync(); - final patchedNewFile = File(p.join(outputDir.path, 'DATA', 'NEWFILE.BIN')) - .readAsStringSync(); + final patchedIpBin = + File(p.join(outputDir.path, 'IP.BIN')).readAsStringSync(); + final patched1stRead = + File(p.join(outputDir.path, '1ST_READ.BIN')).readAsStringSync(); + final patchedConfig = + File(p.join(outputDir.path, 'DATA', 'CONFIG.TXT')).readAsStringSync(); + final patchedNewFile = + File(p.join(outputDir.path, 'DATA', 'NEWFILE.BIN')).readAsStringSync(); expect(patchedIpBin, equals('MODIFIED_BOOTSECTOR_IPBIN_HEADER_67890')); expect(patched1stRead, equals('MODIFIED_GAME_CODE_XYZ123')); diff --git a/core/test/ssp_builder_test.dart b/core/test/ssp_builder_test.dart index 9ebd178..ac94ee0 100644 --- a/core/test/ssp_builder_test.dart +++ b/core/test/ssp_builder_test.dart @@ -38,8 +38,7 @@ void main() { ecc.recalculateSector(sector); // Verify EDC non-zero - final edcVal = - sector[0x810] | + final edcVal = sector[0x810] | (sector[0x811] << 8) | (sector[0x812] << 16) | (sector[0x813] << 24); @@ -113,10 +112,8 @@ void main() { final changesTxt = utf8.decode(changesFile.content as List); final origIsoContent = Iso9660Reader.readIsoFile(origBin, 18, 39); - final expectedMd5 = md5 - .convert(origIsoContent) - .toString() - .toUpperCase(); + final expectedMd5 = + md5.convert(origIsoContent).toString().toUpperCase(); expect(changesTxt, contains('TEST.BIN: $expectedMd5')); @@ -126,7 +123,9 @@ void main() { }, ); - test('SspBuilder generated SSP can be applied with saturn-patcher CLI binary if available', () async { + test( + 'SspBuilder generated SSP can be applied with saturn-patcher CLI binary if available', + () async { final builder = SspBuilder( originalBin: origBin, modifiedBin: modBin, diff --git a/gui/analysis_options.yaml b/gui/analysis_options.yaml index 0d29021..2b03f93 100644 --- a/gui/analysis_options.yaml +++ b/gui/analysis_options.yaml @@ -7,6 +7,12 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - windows/** + - macos/** + - linux/** include: package:flutter_lints/flutter.yaml linter: diff --git a/gui/lib/l10n/app_localizations.dart b/gui/lib/l10n/app_localizations.dart index ef13b4b..e3e8192 100644 --- a/gui/lib/l10n/app_localizations.dart +++ b/gui/lib/l10n/app_localizations.dart @@ -72,7 +72,7 @@ import 'app_localizations_zh.dart'; /// property. abstract class AppLocalizations { AppLocalizations(String locale) - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -95,11 +95,11 @@ abstract class AppLocalizations { /// of delegates is preferred or required. static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ @@ -113,7 +113,7 @@ abstract class AppLocalizations { Locale('pt'), Locale('ru'), Locale('tr'), - Locale('zh'), + Locale('zh') ]; /// No description provided for @appTitle. @@ -878,18 +878,18 @@ class _AppLocalizationsDelegate @override bool isSupported(Locale locale) => [ - 'ar', - 'de', - 'en', - 'es', - 'fr', - 'it', - 'ja', - 'pt', - 'ru', - 'tr', - 'zh', - ].contains(locale.languageCode); + 'ar', + 'de', + 'en', + 'es', + 'fr', + 'it', + 'ja', + 'pt', + 'ru', + 'tr', + 'zh' + ].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; @@ -923,9 +923,8 @@ AppLocalizations lookupAppLocalizations(Locale locale) { } throw FlutterError( - 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.', - ); + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); } diff --git a/gui/lib/l10n/app_localizations_ar.dart b/gui/lib/l10n/app_localizations_ar.dart index ba03841..c874632 100644 --- a/gui/lib/l10n/app_localizations_ar.dart +++ b/gui/lib/l10n/app_localizations_ar.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_de.dart b/gui/lib/l10n/app_localizations_de.dart index 13a01cb..be78c67 100644 --- a/gui/lib/l10n/app_localizations_de.dart +++ b/gui/lib/l10n/app_localizations_de.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_en.dart b/gui/lib/l10n/app_localizations_en.dart index 5db4036..84c693e 100644 --- a/gui/lib/l10n/app_localizations_en.dart +++ b/gui/lib/l10n/app_localizations_en.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_es.dart b/gui/lib/l10n/app_localizations_es.dart index ecd9158..aca36a3 100644 --- a/gui/lib/l10n/app_localizations_es.dart +++ b/gui/lib/l10n/app_localizations_es.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_fr.dart b/gui/lib/l10n/app_localizations_fr.dart index 003e89b..833ea84 100644 --- a/gui/lib/l10n/app_localizations_fr.dart +++ b/gui/lib/l10n/app_localizations_fr.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_it.dart b/gui/lib/l10n/app_localizations_it.dart index c2ffdf3..9eefc61 100644 --- a/gui/lib/l10n/app_localizations_it.dart +++ b/gui/lib/l10n/app_localizations_it.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_ja.dart b/gui/lib/l10n/app_localizations_ja.dart index 8a23797..fa65621 100644 --- a/gui/lib/l10n/app_localizations_ja.dart +++ b/gui/lib/l10n/app_localizations_ja.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_pt.dart b/gui/lib/l10n/app_localizations_pt.dart index 2da0b95..fbd9495 100644 --- a/gui/lib/l10n/app_localizations_pt.dart +++ b/gui/lib/l10n/app_localizations_pt.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_ru.dart b/gui/lib/l10n/app_localizations_ru.dart index d6e17a8..1520c7b 100644 --- a/gui/lib/l10n/app_localizations_ru.dart +++ b/gui/lib/l10n/app_localizations_ru.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_tr.dart b/gui/lib/l10n/app_localizations_tr.dart index c7fa616..13124a3 100644 --- a/gui/lib/l10n/app_localizations_tr.dart +++ b/gui/lib/l10n/app_localizations_tr.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/l10n/app_localizations_zh.dart b/gui/lib/l10n/app_localizations_zh.dart index ea87b43..8ce7bb8 100644 --- a/gui/lib/l10n/app_localizations_zh.dart +++ b/gui/lib/l10n/app_localizations_zh.dart @@ -1,6 +1,5 @@ // ignore: unused_import import 'package:intl/intl.dart' as intl; - import 'app_localizations.dart'; // ignore_for_file: type=lint diff --git a/gui/lib/models/conversion_job.dart b/gui/lib/models/conversion_job.dart index 698d522..333d77d 100644 --- a/gui/lib/models/conversion_job.dart +++ b/gui/lib/models/conversion_job.dart @@ -47,8 +47,8 @@ class ConversionJob { ratio = compression == 'Max' ? 0.60 : compression == 'Fast' - ? 0.80 - : 0.70; + ? 0.80 + : 0.70; break; case 'RVZ': ratio = compression == 'Max' ? 0.50 : 0.55; diff --git a/gui/lib/models/rom_file.dart b/gui/lib/models/rom_file.dart index 8ff4c8d..6d23f06 100644 --- a/gui/lib/models/rom_file.dart +++ b/gui/lib/models/rom_file.dart @@ -35,15 +35,13 @@ class RomFile { factory RomFile.fromJson(Map json) { return RomFile( filepath: json['filepath'] ?? '', - filename: - json['filename'] ?? + filename: json['filename'] ?? (json['filepath'] != null ? json['filepath'].split('/')['last'] : ''), format: json['format'] ?? 'UNKNOWN', platform: json['platform'] ?? 'ROM File', sizeBytes: json['size_bytes'] ?? 0, sizeStr: json['size_str'] ?? '0 B', - validTargets: - (json['valid_targets'] as List?) + validTargets: (json['valid_targets'] as List?) ?.map((e) => e.toString()) .toList() ?? [], diff --git a/gui/lib/patcher/aps_gba_builder.dart b/gui/lib/patcher/aps_gba_builder.dart index c46979f..9b35d22 100644 --- a/gui/lib/patcher/aps_gba_builder.dart +++ b/gui/lib/patcher/aps_gba_builder.dart @@ -45,16 +45,14 @@ class ApsGbaBuilder { final modChunk = Uint8List(_chunkSize); if (offset < origLen) { - final end = (offset + _chunkSize) < origLen - ? (offset + _chunkSize) - : origLen; + final end = + (offset + _chunkSize) < origLen ? (offset + _chunkSize) : origLen; origChunk.setRange(0, end - offset, origBytes.sublist(offset, end)); } if (offset < modLen) { - final end = (offset + _chunkSize) < modLen - ? (offset + _chunkSize) - : modLen; + final end = + (offset + _chunkSize) < modLen ? (offset + _chunkSize) : modLen; modChunk.setRange(0, end - offset, modBytes.sublist(offset, end)); } diff --git a/gui/lib/patcher/aps_n64_patcher.dart b/gui/lib/patcher/aps_n64_patcher.dart index b32fdb2..9718d68 100644 --- a/gui/lib/patcher/aps_n64_patcher.dart +++ b/gui/lib/patcher/aps_n64_patcher.dart @@ -67,8 +67,7 @@ class ApsN64Patcher extends RomPatcher { if (patchType == _typeN64Patch) { validatesRomHeader = true; final endianness = await patch.readByte(); - final cardId = - ((await patch.readByte() & 0xff) << 8) + + final cardId = ((await patch.readByte() & 0xff) << 8) + (await patch.readByte() & 0xff); final country = await patch.readByte(); final crc = await patch.read(8); @@ -181,12 +180,10 @@ class ApsN64Patcher extends RomPatcher { // Check cartridge ID. await rom.setPosition(0x3c); if (endianness == 1) { - val = - ((await rom.readByte() & 0xff) << 8) + + val = ((await rom.readByte() & 0xff) << 8) + (await rom.readByte() & 0xff); } else { - val = - (await rom.readByte() & 0xff) + + val = (await rom.readByte() & 0xff) + ((await rom.readByte() & 0xff) << 8); } if (cartId != val) return false; diff --git a/gui/lib/patcher/aps_patcher.dart b/gui/lib/patcher/aps_patcher.dart index e328518..7a7f7bd 100644 --- a/gui/lib/patcher/aps_patcher.dart +++ b/gui/lib/patcher/aps_patcher.dart @@ -25,15 +25,15 @@ class ApsPatcher extends RomPatcher { Future apply({bool ignoreChecksum = false}) async { final RomPatcher delegate = switch (await _detectType()) { _ApsType.n64 => ApsN64Patcher( - patchFile: patchFile, - romFile: romFile, - outputFile: outputFile, - ), + patchFile: patchFile, + romFile: romFile, + outputFile: outputFile, + ), _ApsType.gba => ApsGbaPatcher( - patchFile: patchFile, - romFile: romFile, - outputFile: outputFile, - ), + patchFile: patchFile, + romFile: romFile, + outputFile: outputFile, + ), _ApsType.unknown => throw PatchException("Not a valid APS patch."), }; return delegate.apply(ignoreChecksum: ignoreChecksum); diff --git a/gui/lib/patcher/dcp_patcher.dart b/gui/lib/patcher/dcp_patcher.dart index c494248..54389f7 100644 --- a/gui/lib/patcher/dcp_patcher.dart +++ b/gui/lib/patcher/dcp_patcher.dart @@ -40,8 +40,8 @@ class DcpPatcher { int filesPatched = 0; bool ipbinReplaced = false; try { - final lines = LineSplitter.split(result.stdout) - .where((l) => l.trim().isNotEmpty); + final lines = + LineSplitter.split(result.stdout).where((l) => l.trim().isNotEmpty); for (final line in lines.toList().reversed) { final decoded = jsonDecode(line); if (decoded is Map) { diff --git a/gui/lib/patcher/ups_patcher.dart b/gui/lib/patcher/ups_patcher.dart index c492f51..7a3c3d5 100644 --- a/gui/lib/patcher/ups_patcher.dart +++ b/gui/lib/patcher/ups_patcher.dart @@ -103,9 +103,8 @@ class UpsPatcher extends RomPatcher { final copyLen = offset - outPos; if (copyLen > 0) { final available = romLen - romCursor; - final n = copyLen <= available - ? copyLen - : (available > 0 ? available : 0); + final n = + copyLen <= available ? copyLen : (available > 0 ? available : 0); if (n > 0) { out.setRange(outPos, outPos + n, rom, romCursor); romCursor += n; diff --git a/gui/lib/screens/dreamcast_screen.dart b/gui/lib/screens/dreamcast_screen.dart index dda7e24..383e8ab 100644 --- a/gui/lib/screens/dreamcast_screen.dart +++ b/gui/lib/screens/dreamcast_screen.dart @@ -582,10 +582,9 @@ class _IpBinEditorTabState extends State<_IpBinEditorTab> { final regions = (fields['regions'] as List?)?.map((e) => e.toString()).toList() ?? - []; - final peripheralsStr = (fields['peripherals'] ?? '') - .toString() - .trim(); + []; + final peripheralsStr = + (fields['peripherals'] ?? '').toString().trim(); int peripFlags = 0; try { peripFlags = int.parse(peripheralsStr, radix: 16); @@ -593,12 +592,12 @@ class _IpBinEditorTabState extends State<_IpBinEditorTab> { setState(() { _titleController.text = fullTitle; - _productNumberController.text = (fields['product_number'] ?? '') - .toString(); - _versionController.text = (fields['product_version'] ?? '') - .toString(); - _releaseDateController.text = (fields['release_date'] ?? '') - .toString(); + _productNumberController.text = + (fields['product_number'] ?? '').toString(); + _versionController.text = + (fields['product_version'] ?? '').toString(); + _releaseDateController.text = + (fields['release_date'] ?? '').toString(); _japan = regions.contains('Japan'); _usa = regions.contains('USA'); @@ -1190,9 +1189,8 @@ class _GdiInfoTabState extends State<_GdiInfoTab> { vertical: 14, ), ), - onPressed: (_gdiPath.isNotEmpty && !_isLoading) - ? _loadGdi - : null, + onPressed: + (_gdiPath.isNotEmpty && !_isLoading) ? _loadGdi : null, child: _isLoading ? SizedBox( width: 18, diff --git a/gui/lib/screens/home_screen.dart b/gui/lib/screens/home_screen.dart index 2432933..2d175ce 100644 --- a/gui/lib/screens/home_screen.dart +++ b/gui/lib/screens/home_screen.dart @@ -58,9 +58,8 @@ class HomeScreenState extends State { final roms = await FileDetector.detectPaths(paths); for (final rom in roms) { if (!_jobs.any((j) => j.romFile.filepath == rom.filepath)) { - final defaultTarget = rom.validTargets.isNotEmpty - ? rom.validTargets.first - : 'CHD'; + final defaultTarget = + rom.validTargets.isNotEmpty ? rom.validTargets.first : 'CHD'; setState(() { _jobs.add( ConversionJob( diff --git a/gui/lib/screens/patcher_screen.dart b/gui/lib/screens/patcher_screen.dart index f09d704..6552cf2 100644 --- a/gui/lib/screens/patcher_screen.dart +++ b/gui/lib/screens/patcher_screen.dart @@ -266,8 +266,7 @@ class _ApplyPatchTabState extends State<_ApplyPatchTab> { final l10n = AppLocalizations.of(context); final theme = widget.theme; final formatBadge = PatcherFactory.formatName(_patchPath); - final canApply = - !_isPatching && + final canApply = !_isPatching && _romPath.isNotEmpty && _patchPath.isNotEmpty && (_isSspPatch || _outputPath.isNotEmpty); @@ -638,9 +637,8 @@ class _ApplyPatchTabState extends State<_ApplyPatchTab> { child: Text( path.isEmpty ? '...' : path, style: TextStyle( - color: path.isEmpty - ? theme.textSecondary - : theme.textPrimary, + color: + path.isEmpty ? theme.textSecondary : theme.textPrimary, fontSize: 13, ), maxLines: 1, @@ -842,8 +840,7 @@ class _BuildPatchTabState extends State<_BuildPatchTab> { Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final theme = widget.theme; - final canBuild = - !_isBuilding && + final canBuild = !_isBuilding && _originalPath.isNotEmpty && _modifiedPath.isNotEmpty && _outputPath.isNotEmpty; @@ -1084,9 +1081,8 @@ class _BuildPatchTabState extends State<_BuildPatchTab> { child: Text( path.isEmpty ? '...' : path, style: TextStyle( - color: path.isEmpty - ? theme.textSecondary - : theme.textPrimary, + color: + path.isEmpty ? theme.textSecondary : theme.textPrimary, fontSize: 13, ), maxLines: 1, diff --git a/gui/lib/screens/settings_screen.dart b/gui/lib/screens/settings_screen.dart index cfe442e..92eece1 100644 --- a/gui/lib/screens/settings_screen.dart +++ b/gui/lib/screens/settings_screen.dart @@ -20,8 +20,7 @@ class SettingsScreen extends StatefulWidget { bool verify, String outputDir, bool sameFolder, - ) - onSettingsChanged; + ) onSettingsChanged; const SettingsScreen({ super.key, @@ -84,8 +83,8 @@ class _SettingsScreenState extends State { } void _pickOutputDirectory() async { - final String? selectedDirectory = await FilePicker.platform - .getDirectoryPath(); + final String? selectedDirectory = + await FilePicker.platform.getDirectoryPath(); if (selectedDirectory != null) { setState(() { _outputDestination = selectedDirectory; diff --git a/gui/lib/screens/theme_editor_screen.dart b/gui/lib/screens/theme_editor_screen.dart index 570e537..00925ed 100644 --- a/gui/lib/screens/theme_editor_screen.dart +++ b/gui/lib/screens/theme_editor_screen.dart @@ -67,9 +67,8 @@ class _ThemeEditorScreenState extends State { 1, (v) { setInner(() { - temp = HSVColor.fromColor(temp) - .withSaturation(v) - .toColor(); + temp = + HSVColor.fromColor(temp).withSaturation(v).toColor(); }); }, temp, diff --git a/gui/lib/screens/tools_screen.dart b/gui/lib/screens/tools_screen.dart index 76f0821..b1ad926 100644 --- a/gui/lib/screens/tools_screen.dart +++ b/gui/lib/screens/tools_screen.dart @@ -112,7 +112,7 @@ class _ToolsScreenState extends State class _CompressorQueueItem { final String filepath; String - status; // "Queued", "Compressing", "Extracting", "Done", "Failed", "Skipped" + status; // "Queued", "Compressing", "Extracting", "Done", "Failed", "Skipped" double progress; String? error; @@ -394,9 +394,8 @@ class _CompressorTabState extends State<_CompressorTab> { backgroundColor: theme.surface, labelStyle: TextStyle( color: selected ? Colors.black : theme.textPrimary, - fontWeight: selected - ? FontWeight.bold - : FontWeight.normal, + fontWeight: + selected ? FontWeight.bold : FontWeight.normal, ), onSelected: (_) { setState(() { @@ -404,14 +403,12 @@ class _CompressorTabState extends State<_CompressorTab> { for (final item in _queue) { if (item.status == 'Skipped' || item.status == 'Queued') { - final isSkipped = - (_format != 'Extract') && + final isSkipped = (_format != 'Extract') && _isAlreadyCompressed(item.filepath); item.status = isSkipped ? 'Skipped' : 'Queued'; item.progress = isSkipped ? 100.0 : 0.0; - item.error = isSkipped - ? l10n.compressorSkipped - : null; + item.error = + isSkipped ? l10n.compressorSkipped : null; } } }); @@ -434,32 +431,30 @@ class _CompressorTabState extends State<_CompressorTab> { ), const SizedBox(height: 8), Row( - children: - [ - MapEntry('Fast', l10n.compressorLevelFast), - MapEntry('Normal', l10n.compressorLevelNormal), - MapEntry('Ultra', l10n.compressorLevelUltra), - ].map((entry) { - final key = entry.key; - final label = entry.value; - final selected = _level == key; - return Padding( - padding: const EdgeInsets.only(right: 10), - child: ChoiceChip( - label: Text(label), - selected: selected, - selectedColor: theme.accent, - backgroundColor: theme.surface, - labelStyle: TextStyle( - color: selected ? Colors.black : theme.textPrimary, - fontWeight: selected - ? FontWeight.bold - : FontWeight.normal, - ), - onSelected: (_) => setState(() => _level = key), - ), - ); - }).toList(), + children: [ + MapEntry('Fast', l10n.compressorLevelFast), + MapEntry('Normal', l10n.compressorLevelNormal), + MapEntry('Ultra', l10n.compressorLevelUltra), + ].map((entry) { + final key = entry.key; + final label = entry.value; + final selected = _level == key; + return Padding( + padding: const EdgeInsets.only(right: 10), + child: ChoiceChip( + label: Text(label), + selected: selected, + selectedColor: theme.accent, + backgroundColor: theme.surface, + labelStyle: TextStyle( + color: selected ? Colors.black : theme.textPrimary, + fontWeight: + selected ? FontWeight.bold : FontWeight.normal, + ), + onSelected: (_) => setState(() => _level = key), + ), + ); + }).toList(), ), const SizedBox(height: 16), ], @@ -1612,9 +1607,8 @@ class _BinMergerTabState extends State<_BinMergerTab> { width: double.infinity, height: 46, child: ElevatedButton( - onPressed: (_cuePath.isNotEmpty && !_isProcessing) - ? _mergeBins - : null, + onPressed: + (_cuePath.isNotEmpty && !_isProcessing) ? _mergeBins : null, style: ElevatedButton.styleFrom( backgroundColor: theme.accent, foregroundColor: Colors.black, @@ -2133,9 +2127,8 @@ class _CueEditorTabState extends State<_CueEditorTab> { decoration: InputDecoration( contentPadding: const EdgeInsets.all(12), border: InputBorder.none, - hintText: _cuePath == null - ? 'Open a .cue file to edit...' - : null, + hintText: + _cuePath == null ? 'Open a .cue file to edit...' : null, hintStyle: TextStyle(color: widget.theme.textSecondary), ), ), @@ -2373,8 +2366,8 @@ class _RomRenamerTabState extends State<_RomRenamerTab> { ElevatedButton.icon( onPressed: (_isRenaming || _scanResults.isEmpty || _romFolder == null) - ? null - : _rename, + ? null + : _rename, icon: _isRenaming ? const SizedBox( width: 14, diff --git a/gui/lib/services/core_bridge.dart b/gui/lib/services/core_bridge.dart index c96486f..8c02056 100644 --- a/gui/lib/services/core_bridge.dart +++ b/gui/lib/services/core_bridge.dart @@ -33,9 +33,8 @@ class CoreBridge { } final String exeDir = File(Platform.resolvedExecutable).parent.path; - final String binaryName = Platform.isWindows - ? 'openrom-core.exe' - : 'openrom-core'; + final String binaryName = + Platform.isWindows ? 'openrom-core.exe' : 'openrom-core'; final String sameDirBinary = '$exeDir${Platform.pathSeparator}$binaryName'; if (File(sameDirBinary).existsSync()) { @@ -107,8 +106,7 @@ class CoreBridge { ); } } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -183,39 +181,39 @@ class CoreBridge { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isEmpty) return; - try { - final Map event = jsonDecode(line); - final type = event['type']; - if (type == 'progress') { - final double pct = (event['percent'] as num).toDouble(); - onProgress(pct); - } else if (type == 'log') { - final String msg = event['message'] ?? ''; - onLog(msg); - } else if (type == 'done') { - final bool success = event['success'] ?? false; - final String? err = event['error']; - onDone(success, err); - } else if (type == 'error') { - final String msg = event['message'] ?? 'Unknown error'; - onLog('[ERROR] $msg'); - stderrLog.writeln(msg); - } - } catch (_) { - onLog(line); - } - }); + if (line.trim().isEmpty) return; + try { + final Map event = jsonDecode(line); + final type = event['type']; + if (type == 'progress') { + final double pct = (event['percent'] as num).toDouble(); + onProgress(pct); + } else if (type == 'log') { + final String msg = event['message'] ?? ''; + onLog(msg); + } else if (type == 'done') { + final bool success = event['success'] ?? false; + final String? err = event['error']; + onDone(success, err); + } else if (type == 'error') { + final String msg = event['message'] ?? 'Unknown error'; + onLog('[ERROR] $msg'); + stderrLog.writeln(msg); + } + } catch (_) { + onLog(line); + } + }); process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isNotEmpty) { - onLog('[STDERR] $line'); - stderrLog.writeln(line); - } - }); + if (line.trim().isNotEmpty) { + onLog('[STDERR] $line'); + stderrLog.writeln(line); + } + }); final exitCode = await process.exitCode; if (exitCode != 0) { @@ -238,8 +236,7 @@ class CoreBridge { } } } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -303,39 +300,39 @@ class CoreBridge { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isEmpty) return; - try { - final Map event = jsonDecode(line); - final type = event['type']; - if (type == 'progress') { - final double pct = (event['percent'] as num).toDouble(); - onProgress?.call(pct); - } else if (type == 'log') { - final String msg = event['message'] ?? ''; - onLog?.call(msg); - } else if (type == 'done') { - final bool success = event['success'] ?? false; - final String? err = event['error']; - onDone?.call(success, err); - } else if (type == 'error') { - final String msg = event['message'] ?? 'Unknown error'; - onLog?.call('[ERROR] $msg'); - stderrLog.writeln(msg); - } - } catch (_) { - onLog?.call(line); - } - }); + if (line.trim().isEmpty) return; + try { + final Map event = jsonDecode(line); + final type = event['type']; + if (type == 'progress') { + final double pct = (event['percent'] as num).toDouble(); + onProgress?.call(pct); + } else if (type == 'log') { + final String msg = event['message'] ?? ''; + onLog?.call(msg); + } else if (type == 'done') { + final bool success = event['success'] ?? false; + final String? err = event['error']; + onDone?.call(success, err); + } else if (type == 'error') { + final String msg = event['message'] ?? 'Unknown error'; + onLog?.call('[ERROR] $msg'); + stderrLog.writeln(msg); + } + } catch (_) { + onLog?.call(line); + } + }); process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isNotEmpty) { - onLog?.call('[STDERR] $line'); - stderrLog.writeln(line); - } - }); + if (line.trim().isNotEmpty) { + onLog?.call('[STDERR] $line'); + stderrLog.writeln(line); + } + }); final exitCode = await process.exitCode; if (exitCode != 0) { @@ -346,8 +343,7 @@ class CoreBridge { throw OpenROMException(OpenROMError.conversionFailed, details: details); } } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -401,39 +397,39 @@ class CoreBridge { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isEmpty) return; - try { - final Map event = jsonDecode(line); - final type = event['type']; - if (type == 'progress') { - final double pct = (event['percent'] as num).toDouble(); - onProgress?.call(pct); - } else if (type == 'log') { - final String msg = event['message'] ?? ''; - onLog?.call(msg); - } else if (type == 'done') { - final bool success = event['success'] ?? false; - final String? err = event['error']; - onDone?.call(success, err); - } else if (type == 'error') { - final String msg = event['message'] ?? 'Unknown error'; - onLog?.call('[ERROR] $msg'); - stderrLog.writeln(msg); - } - } catch (_) { - onLog?.call(line); - } - }); + if (line.trim().isEmpty) return; + try { + final Map event = jsonDecode(line); + final type = event['type']; + if (type == 'progress') { + final double pct = (event['percent'] as num).toDouble(); + onProgress?.call(pct); + } else if (type == 'log') { + final String msg = event['message'] ?? ''; + onLog?.call(msg); + } else if (type == 'done') { + final bool success = event['success'] ?? false; + final String? err = event['error']; + onDone?.call(success, err); + } else if (type == 'error') { + final String msg = event['message'] ?? 'Unknown error'; + onLog?.call('[ERROR] $msg'); + stderrLog.writeln(msg); + } + } catch (_) { + onLog?.call(line); + } + }); process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isNotEmpty) { - onLog?.call('[STDERR] $line'); - stderrLog.writeln(line); - } - }); + if (line.trim().isNotEmpty) { + onLog?.call('[STDERR] $line'); + stderrLog.writeln(line); + } + }); final exitCode = await process.exitCode; if (exitCode != 0) { @@ -444,8 +440,7 @@ class CoreBridge { throw OpenROMException(OpenROMError.conversionFailed, details: details); } } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -504,8 +499,7 @@ class CoreBridge { details: err.isNotEmpty ? err : 'M3U generation failed', ); } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; diff --git a/gui/lib/services/patch_builder_service.dart b/gui/lib/services/patch_builder_service.dart index b5084c0..109058f 100644 --- a/gui/lib/services/patch_builder_service.dart +++ b/gui/lib/services/patch_builder_service.dart @@ -177,7 +177,8 @@ class PatchBuilderService { required String originalPath, required String modifiedPath, required String outputPath, - required String format, // 'xdelta' | 'ips' | 'ips32' | 'bps' | 'ups' | 'ebp' | 'ppf3' | 'aps (gba)' | 'aps (n64)' | 'dcp' + required String + format, // 'xdelta' | 'ips' | 'ips32' | 'bps' | 'ups' | 'ebp' | 'ppf3' | 'aps (gba)' | 'aps (n64)' | 'dcp' }) async { try { return await compute(_buildPatchIsolate, { diff --git a/gui/lib/services/patcher_service.dart b/gui/lib/services/patcher_service.dart index 819688d..40de517 100644 --- a/gui/lib/services/patcher_service.dart +++ b/gui/lib/services/patcher_service.dart @@ -61,7 +61,8 @@ class PatcherService { if (saturnPatcher == null || saturnPatcher.isEmpty) { throw OpenROMException( OpenROMError.toolFailed, - details: 'saturn-patcher binary not found. Please check your OpenROM installation.', + details: + 'saturn-patcher binary not found. Please check your OpenROM installation.', ); } diff --git a/gui/lib/services/theme_service.dart b/gui/lib/services/theme_service.dart index ad8db06..55694ea 100644 --- a/gui/lib/services/theme_service.dart +++ b/gui/lib/services/theme_service.dart @@ -57,14 +57,12 @@ class ThemeService extends ChangeNotifier { // Load user-saved custom themes from disk try { final dir = await getApplicationSupportDirectory(); - final customFiles = Directory(dir.path) - .listSync() - .whereType() - .where( - (f) => - f.path.endsWith('.json') && - f.uri.pathSegments.last.startsWith('custom_'), - ); + final customFiles = + Directory(dir.path).listSync().whereType().where( + (f) => + f.path.endsWith('.json') && + f.uri.pathSegments.last.startsWith('custom_'), + ); for (final file in customFiles) { try { final content = await file.readAsString(); @@ -95,9 +93,9 @@ class ThemeService extends ChangeNotifier { try { final dir = await getApplicationSupportDirectory(); final safeName = theme.name.toLowerCase().replaceAll( - RegExp(r'[^a-z0-9_]'), - '_', - ); + RegExp(r'[^a-z0-9_]'), + '_', + ); final file = File('${dir.path}/custom_$safeName.json'); await file.writeAsString(jsonEncode(theme.toJson())); diff --git a/gui/lib/services/tools_service.dart b/gui/lib/services/tools_service.dart index 54b1737..cebe213 100644 --- a/gui/lib/services/tools_service.dart +++ b/gui/lib/services/tools_service.dart @@ -102,8 +102,7 @@ class ToolsService { details: err.isNotEmpty ? err : 'CUE generation failed', ); } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -153,33 +152,33 @@ class ToolsService { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isEmpty) return; - try { - final Map event = jsonDecode(line); - final type = event['type']; - if (type == 'progress') { - final double pct = (event['percent'] as num).toDouble(); - onProgress?.call(pct); - } else if (type == 'done' && event['success'] == true) { - mergeResult = BinMergeResult( - mergedBinPath: event['merged_bin'] as String, - mergedCuePath: event['merged_cue'] as String, - tracksCount: (event['tracks_count'] as num?)?.toInt() ?? 0, - ); - } else if (type == 'error') { - stderrLog.writeln(event['message'] ?? ''); - } - } catch (_) {} - }); + if (line.trim().isEmpty) return; + try { + final Map event = jsonDecode(line); + final type = event['type']; + if (type == 'progress') { + final double pct = (event['percent'] as num).toDouble(); + onProgress?.call(pct); + } else if (type == 'done' && event['success'] == true) { + mergeResult = BinMergeResult( + mergedBinPath: event['merged_bin'] as String, + mergedCuePath: event['merged_cue'] as String, + tracksCount: (event['tracks_count'] as num?)?.toInt() ?? 0, + ); + } else if (type == 'error') { + stderrLog.writeln(event['message'] ?? ''); + } + } catch (_) {} + }); process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isNotEmpty) { - stderrLog.writeln(line); - } - }); + if (line.trim().isNotEmpty) { + stderrLog.writeln(line); + } + }); final exitCode = await process.exitCode; if (exitCode == 0 && mergeResult != null) { @@ -191,8 +190,7 @@ class ToolsService { : 'Process exited with code $exitCode'; throw OpenROMException(OpenROMError.conversionFailed, details: details); } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -242,8 +240,7 @@ class ToolsService { } return null; } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -309,8 +306,7 @@ class ToolsService { details: err.isNotEmpty ? err : 'Header removal failed', ); } on FileSystemException catch (e) { - final err = - (e.osError?.errorCode == 13 || + final err = (e.osError?.errorCode == 13 || e.message.toLowerCase().contains('permission')) ? OpenROMError.permissionDenied : OpenROMError.fileNotFound; @@ -426,30 +422,30 @@ class ToolsService { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isEmpty) return; - try { - final Map event = jsonDecode(line); - if (event['type'] == 'result') { - results.add(Map.from(event)); - } else if (event['type'] == 'progress' && onProgress != null) { - onProgress( - event['file'] as String? ?? '', - (event['percent'] as num?)?.toDouble() ?? 0, - ); - } else if (event['type'] == 'error') { - stderrLog.writeln(event['message'] ?? ''); - } - } catch (_) {} - }); + if (line.trim().isEmpty) return; + try { + final Map event = jsonDecode(line); + if (event['type'] == 'result') { + results.add(Map.from(event)); + } else if (event['type'] == 'progress' && onProgress != null) { + onProgress( + event['file'] as String? ?? '', + (event['percent'] as num?)?.toDouble() ?? 0, + ); + } else if (event['type'] == 'error') { + stderrLog.writeln(event['message'] ?? ''); + } + } catch (_) {} + }); process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isNotEmpty) { - stderrLog.writeln(line); - } - }); + if (line.trim().isNotEmpty) { + stderrLog.writeln(line); + } + }); final exitCode = await process.exitCode; if (exitCode != 0 && results.isEmpty) { @@ -487,25 +483,25 @@ class ToolsService { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isEmpty) return; - try { - final Map event = jsonDecode(line); - if (event['type'] == 'rename') { - results.add(Map.from(event)); - } else if (event['type'] == 'error') { - stderrLog.writeln(event['message'] ?? ''); - } - } catch (_) {} - }); + if (line.trim().isEmpty) return; + try { + final Map event = jsonDecode(line); + if (event['type'] == 'rename') { + results.add(Map.from(event)); + } else if (event['type'] == 'error') { + stderrLog.writeln(event['message'] ?? ''); + } + } catch (_) {} + }); process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) { - if (line.trim().isNotEmpty) { - stderrLog.writeln(line); - } - }); + if (line.trim().isNotEmpty) { + stderrLog.writeln(line); + } + }); final exitCode = await process.exitCode; if (exitCode != 0 && results.isEmpty) { diff --git a/gui/lib/widgets/rom_card.dart b/gui/lib/widgets/rom_card.dart index 6ff8143..83fc02c 100644 --- a/gui/lib/widgets/rom_card.dart +++ b/gui/lib/widgets/rom_card.dart @@ -86,8 +86,8 @@ class _RomCardState extends State { final err = widget.job.error ?? OpenROMError.conversionFailed; final details = (widget.job.errorMessage != null && widget.job.errorMessage!.isNotEmpty) - ? widget.job.errorMessage - : widget.job.logs.join('\n'); + ? widget.job.errorMessage + : widget.job.logs.join('\n'); showOpenROMError(context, err, details: details); } @@ -184,8 +184,8 @@ class _RomCardState extends State { color: isFailed ? Colors.redAccent : (widget.job.status == JobStatus.done - ? Colors.greenAccent - : widget.theme.textSecondary), + ? Colors.greenAccent + : widget.theme.textSecondary), fontSize: 13, ), ), diff --git a/gui/lib/widgets/sidebar.dart b/gui/lib/widgets/sidebar.dart index ddea03d..e7c28dc 100644 --- a/gui/lib/widgets/sidebar.dart +++ b/gui/lib/widgets/sidebar.dart @@ -63,9 +63,8 @@ class Sidebar extends StatelessWidget { decoration: BoxDecoration( border: Border( left: BorderSide( - color: isSelected - ? theme.accent - : Colors.transparent, + color: + isSelected ? theme.accent : Colors.transparent, width: 3, ), ), diff --git a/gui/lib/widgets/top_bar.dart b/gui/lib/widgets/top_bar.dart index 75e1a11..a938b72 100644 --- a/gui/lib/widgets/top_bar.dart +++ b/gui/lib/widgets/top_bar.dart @@ -70,9 +70,8 @@ class TopBar extends StatelessWidget { ), const SizedBox(width: 12), ElevatedButton( - onPressed: (fileCount > 0 && !isConverting) - ? onConvertPressed - : null, + onPressed: + (fileCount > 0 && !isConverting) ? onConvertPressed : null, style: ElevatedButton.styleFrom( backgroundColor: theme.accent, foregroundColor: Colors.white, diff --git a/gui/pubspec.lock b/gui/pubspec.lock index 42bd14e..be7c201 100644 --- a/gui/pubspec.lock +++ b/gui/pubspec.lock @@ -169,10 +169,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" jni: dependency: transitive description: @@ -249,10 +249,10 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -265,10 +265,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.19.0" objective_c: dependency: transitive description: @@ -283,7 +283,7 @@ packages: path: "../core" relative: true source: path - version: "3.0.0" + version: "3.7.0" package_config: dependency: transitive description: @@ -533,10 +533,10 @@ packages: dependency: transitive description: name: test_api - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.9" + version: "0.7.12" typed_data: dependency: transitive description: @@ -549,10 +549,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.3" vm_service: dependency: transitive description: From 4b65195ea81de071f11f625f37e0e1a3c31fc19f Mon Sep 17 00:00:00 2001 From: ClausValcaTD <1.93546948e+08+ClausValcaTD@users.noreply.github.com> Date: Sat, 3 Oct 2026 22:32:23 +0000 Subject: [PATCH 3/5] fix: resolve Flutter analyzer findings --- gui/lib/patcher/aps_n64_builder.dart | 2 -- gui/lib/patcher/bps_builder.dart | 1 - gui/lib/patcher/ips32_builder.dart | 2 -- gui/lib/patcher/ppf_builder.dart | 2 -- gui/lib/patcher/ups_builder.dart | 1 - gui/lib/screens/home_screen.dart | 3 +-- gui/lib/screens/tools_screen.dart | 3 +-- 7 files changed, 2 insertions(+), 12 deletions(-) diff --git a/gui/lib/patcher/aps_n64_builder.dart b/gui/lib/patcher/aps_n64_builder.dart index d876cdb..8662617 100644 --- a/gui/lib/patcher/aps_n64_builder.dart +++ b/gui/lib/patcher/aps_n64_builder.dart @@ -2,8 +2,6 @@ import 'dart:io'; import 'dart:typed_data'; -import 'patcher.dart'; - /// Pure Dart APS (N64 variant, magic "APS10") patch creator. class ApsN64Builder { final File originalFile; diff --git a/gui/lib/patcher/bps_builder.dart b/gui/lib/patcher/bps_builder.dart index 022fbab..c9edbc9 100644 --- a/gui/lib/patcher/bps_builder.dart +++ b/gui/lib/patcher/bps_builder.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'dart:typed_data'; import 'checksums.dart'; -import 'patcher.dart'; /// Pure Dart BPS (Binary Patch System) patch creator. class BpsBuilder { diff --git a/gui/lib/patcher/ips32_builder.dart b/gui/lib/patcher/ips32_builder.dart index 6a6b4d6..7fe36ed 100644 --- a/gui/lib/patcher/ips32_builder.dart +++ b/gui/lib/patcher/ips32_builder.dart @@ -2,8 +2,6 @@ import 'dart:io'; import 'dart:typed_data'; -import 'patcher.dart'; - /// Pure Dart IPS32 patch creator. class IPS32Builder { final File originalFile; diff --git a/gui/lib/patcher/ppf_builder.dart b/gui/lib/patcher/ppf_builder.dart index 3fde565..e07c870 100644 --- a/gui/lib/patcher/ppf_builder.dart +++ b/gui/lib/patcher/ppf_builder.dart @@ -2,8 +2,6 @@ import 'dart:io'; import 'dart:typed_data'; -import 'patcher.dart'; - /// Pure Dart PPF3 (Playstation Patch Format v3) patch creator. class PpfBuilder { final File originalFile; diff --git a/gui/lib/patcher/ups_builder.dart b/gui/lib/patcher/ups_builder.dart index abf5a02..52af832 100644 --- a/gui/lib/patcher/ups_builder.dart +++ b/gui/lib/patcher/ups_builder.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'dart:typed_data'; import 'checksums.dart'; -import 'patcher.dart'; /// Pure Dart UPS (Universal Patch System) patch creator. class UpsBuilder { diff --git a/gui/lib/screens/home_screen.dart b/gui/lib/screens/home_screen.dart index 2d175ce..03e6b52 100644 --- a/gui/lib/screens/home_screen.dart +++ b/gui/lib/screens/home_screen.dart @@ -237,9 +237,8 @@ class HomeScreenState extends State { ) : ReorderableListView.builder( itemCount: _jobs.length, - onReorder: (oldIndex, newIndex) { + onReorderItem: (oldIndex, newIndex) { setState(() { - if (newIndex > oldIndex) newIndex -= 1; final job = _jobs.removeAt(oldIndex); _jobs.insert(newIndex, job); }); diff --git a/gui/lib/screens/tools_screen.dart b/gui/lib/screens/tools_screen.dart index b1ad926..44ab998 100644 --- a/gui/lib/screens/tools_screen.dart +++ b/gui/lib/screens/tools_screen.dart @@ -894,9 +894,8 @@ class _M3uTabState extends State<_M3uTab> { ) : ReorderableListView.builder( itemCount: _discFiles.length, - onReorder: (oldIndex, newIndex) { + onReorderItem: (oldIndex, newIndex) { setState(() { - if (newIndex > oldIndex) newIndex -= 1; final item = _discFiles.removeAt(oldIndex); _discFiles.insert(newIndex, item); _updateAutoFilename(); From 05481b9f4731b05fcf26c6167cb5cac4878181b0 Mon Sep 17 00:00:00 2001 From: ClausValcaTD <1.93546948e+08+ClausValcaTD@users.noreply.github.com> Date: Sat, 3 Oct 2026 22:36:05 +0000 Subject: [PATCH 4/5] fix: keep settings tiles on their own material --- gui/lib/screens/settings_screen.dart | 54 +++++++++++++++------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/gui/lib/screens/settings_screen.dart b/gui/lib/screens/settings_screen.dart index 92eece1..7bcb7a5 100644 --- a/gui/lib/screens/settings_screen.dart +++ b/gui/lib/screens/settings_screen.dart @@ -215,37 +215,43 @@ class _SettingsScreenState extends State { // Verification Switch _buildSectionTitle(l10n.postProcessing), - SwitchListTile( - title: Text( - l10n.verifyAfterConversion, - style: TextStyle(color: theme.textPrimary), - ), - subtitle: Text( - 'Runs chdman verify on newly created CHD files', - style: TextStyle(color: theme.textSecondary), + Material( + color: Colors.transparent, + child: SwitchListTile( + title: Text( + l10n.verifyAfterConversion, + style: TextStyle(color: theme.textPrimary), + ), + subtitle: Text( + 'Runs chdman verify on newly created CHD files', + style: TextStyle(color: theme.textSecondary), + ), + value: _verifyAfterConversion, + activeThumbColor: theme.accent, + onChanged: (val) { + setState(() => _verifyAfterConversion = val); + _saveSettings(); + }, ), - value: _verifyAfterConversion, - activeThumbColor: theme.accent, - onChanged: (val) { - setState(() => _verifyAfterConversion = val); - _saveSettings(); - }, ), const SizedBox(height: 24), // Output Folder _buildSectionTitle(l10n.outputDestination), - CheckboxListTile( - title: Text( - 'Same folder as source file', - style: TextStyle(color: theme.textPrimary), + Material( + color: Colors.transparent, + child: CheckboxListTile( + title: Text( + 'Same folder as source file', + style: TextStyle(color: theme.textPrimary), + ), + value: _sameFolderAsSource, + activeColor: theme.accent, + onChanged: (val) { + setState(() => _sameFolderAsSource = val ?? true); + _saveSettings(); + }, ), - value: _sameFolderAsSource, - activeColor: theme.accent, - onChanged: (val) { - setState(() => _sameFolderAsSource = val ?? true); - _saveSettings(); - }, ), if (!_sameFolderAsSource) ...[ const SizedBox(height: 8), From 3e6e0f043e53d6241e76c50c8d89ae50135d3a4c Mon Sep 17 00:00:00 2001 From: ClausValcaTD <1.93546948e+08+ClausValcaTD@users.noreply.github.com> Date: Sat, 3 Oct 2026 22:38:31 +0000 Subject: [PATCH 5/5] fix: isolate patcher checkbox tiles --- gui/lib/screens/patcher_screen.dart | 72 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/gui/lib/screens/patcher_screen.dart b/gui/lib/screens/patcher_screen.dart index 6552cf2..48f0edb 100644 --- a/gui/lib/screens/patcher_screen.dart +++ b/gui/lib/screens/patcher_screen.dart @@ -403,47 +403,53 @@ class _ApplyPatchTabState extends State<_ApplyPatchTab> { const SizedBox(height: 6), // Same folder as ROM checkbox - CheckboxListTile( - value: _sameFolder, - contentPadding: EdgeInsets.zero, - dense: true, - controlAffinity: ListTileControlAffinity.leading, - activeColor: theme.accent, - title: Text( - l10n.patcherSameFolder, - style: TextStyle( - color: theme.textPrimary, - fontSize: 14, + Material( + color: Colors.transparent, + child: CheckboxListTile( + value: _sameFolder, + contentPadding: EdgeInsets.zero, + dense: true, + controlAffinity: ListTileControlAffinity.leading, + activeColor: theme.accent, + title: Text( + l10n.patcherSameFolder, + style: TextStyle( + color: theme.textPrimary, + fontSize: 14, + ), ), + onChanged: (val) { + setState(() { + _sameFolder = val ?? true; + }); + _updateOutputPath(); + }, ), - onChanged: (val) { - setState(() { - _sameFolder = val ?? true; - }); - _updateOutputPath(); - }, ), const SizedBox(height: 4), // Ignore checksum errors checkbox - CheckboxListTile( - value: _ignoreChecksum, - contentPadding: EdgeInsets.zero, - dense: true, - controlAffinity: ListTileControlAffinity.leading, - activeColor: theme.accent, - title: Text( - l10n.patcherIgnoreChecksum, - style: TextStyle( - color: theme.textPrimary, - fontSize: 14, + Material( + color: Colors.transparent, + child: CheckboxListTile( + value: _ignoreChecksum, + contentPadding: EdgeInsets.zero, + dense: true, + controlAffinity: ListTileControlAffinity.leading, + activeColor: theme.accent, + title: Text( + l10n.patcherIgnoreChecksum, + style: TextStyle( + color: theme.textPrimary, + fontSize: 14, + ), ), + onChanged: (val) { + setState(() { + _ignoreChecksum = val ?? false; + }); + }, ), - onChanged: (val) { - setState(() { - _ignoreChecksum = val ?? false; - }); - }, ), const SizedBox(height: 24), ],