diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a66dc2..1e064de 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,8 @@ jobs: cache: true cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' # optional, change this to force refresh cache cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' # optional, change this to specify the cache path + - run: chmod +x ./build.sh + - run: ./build.sh - run: flutter config --enable-macos-desktop - run: flutter build -v macos --release - run: ditto -c -k --sequesterRsrc --keepParent retro.app $GITHUB_WORKSPACE/retro.zip @@ -36,6 +38,7 @@ jobs: cache: true cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' # optional, change this to force refresh cache cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' # optional, change this to specify the cache path + - run: ./build.ps1 - run: flutter config --enable-windows-desktop - run: flutter build windows --release - uses: actions/upload-artifact@v3 @@ -54,13 +57,15 @@ jobs: cache: true cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' # optional, change this to force refresh cache cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' # optional, change this to specify the cache path + - run: chmod +x ./build.sh + - run: ./build.sh - run: flutter config --enable-linux-desktop - run: flutter build linux --release - name: Create AppImage run: | wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" chmod a+x appimagetool-x86_64.AppImage - + # Create AppDir cp -r build/linux/x64/release/bundle/ retro.AppDir cp assets/icon-macos.png retro.AppDir/retro.png diff --git a/.gitignore b/.gitignore index 8cac985..396537d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,9 @@ app.*.symbols # Obfuscation related app.*.map.json +# Autogenerated file +# lib/auto/build.dart + # Android Studio will place build artifacts here /android/app/debug /android/app/profile diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..567187d --- /dev/null +++ b/build.ps1 @@ -0,0 +1,10 @@ +Remove-Item -Force lib/auto/build.dart +New-Item -ItemType File -Path lib/auto/build.dart + +$BRANCH = git rev-parse --abbrev-ref HEAD +$COMMIT_HASH = git rev-parse --short HEAD +$COMMIT_DATE = git show -s --format=%ci + +"const String branch = '$BRANCH';" | Out-File -FilePath lib/auto/build.dart -Encoding utf8 +"const String commitHash = '$COMMIT_HASH';" | Out-File -FilePath lib/auto/build.dart -Encoding utf8 -Append +"const String commitDate = '$COMMIT_DATE';" | Out-File -FilePath lib/auto/build.dart -Encoding utf8 -Append diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..0e6c03f --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +rm -f lib/auto/build.dart +touch lib/auto/build.dart +BRANCH=$(git rev-parse --abbrev-ref HEAD) +COMMIT_HASH=$(git rev-parse --short HEAD) +COMMIT_DATE=$(git show -s --format=%ci) + +echo "const String branch = '$BRANCH';" > lib/auto/build.dart +echo "const String commitHash = '$COMMIT_HASH';" >> lib/auto/build.dart +echo "const String commitDate = '$COMMIT_DATE';" >> lib/auto/build.dart \ No newline at end of file diff --git a/lib/arc/arc.dart b/lib/arc/arc.dart index 2f320ec..4cad902 100644 --- a/lib/arc/arc.dart +++ b/lib/arc/arc.dart @@ -73,12 +73,16 @@ class Arc { continue; } - log('File name: $fileName'); - if (fileName != null && fileName != '(signature)' && fileName != '(listfile)' && fileName != '(attributes)') { + if (fileName != '(signature)' && fileName != '(listfile)' && fileName != '(attributes)') { files.add(fileName); if(onFile != null) { final file = mpqArchive.openFileEx(fileName, 0); - await onFile(fileName, file.read(file.size())); + try { + final size = file.size(); + await onFile(fileName, file.read(size)); + } catch (e) { + log('Skipping file $fileName: $e'); + } } } } on StormLibException catch (e) { diff --git a/lib/auto/build.dart b/lib/auto/build.dart new file mode 100644 index 0000000..582fbd4 --- /dev/null +++ b/lib/auto/build.dart @@ -0,0 +1,3 @@ +const String branch = 'v0.2.0'; +const String commitHash = '705c17d'; +const String commitDate = '2024-06-03 23:53:34 -0600'; diff --git a/lib/context.dart b/lib/context.dart new file mode 100644 index 0000000..81e225d --- /dev/null +++ b/lib/context.dart @@ -0,0 +1,7 @@ +import 'package:retro/utils/git.dart'; + +class RetroContext { + static const String versionName = '0.2.0'; + static const int versionCode = 0; + static late GitInfo git; +} diff --git a/lib/features/create/create_custom/create_custom_screen.dart b/lib/features/create/create_custom/create_custom_screen.dart index fff04cb..e0d1b15 100644 --- a/lib/features/create/create_custom/create_custom_screen.dart +++ b/lib/features/create/create_custom/create_custom_screen.dart @@ -26,72 +26,85 @@ class _CreateCustomScreenState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); final textTheme = theme.textTheme; - final viewModel = - Provider.of(context); - final finishViewModel = - Provider.of(context); + final viewModel = Provider.of(context); + final finishViewModel = Provider.of(context); final i18n = AppLocalizations.of(context)!; return CustomScaffold( - title: i18n.createCustomScreen_title, - subtitle: i18n.createCustomScreen_subtitle, - onBackButtonPressed: () { - viewModel.reset(); - Navigator.of(context).pop(); - }, - content: Expanded( - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - children: [ - Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: OutlinedButton( - onPressed: viewModel.onSelectFiles, - style: ElevatedButton.styleFrom( - minimumSize: const Size(200, 50),), - child: - Text(i18n.createCustomScreen_selectButton),), - ), - Text(viewModel.path), - ], - ), - Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Align( - alignment: Alignment.centerLeft, - child: Text( - '${i18n.createCustomScreen_fileToInsert}${viewModel.files.length}', - style: textTheme.titleMedium, - ), + title: i18n.createCustomScreen_title, + subtitle: i18n.createCustomScreen_subtitle, + onBackButtonPressed: () { + viewModel.reset(); + Navigator.of(context).pop(); + }, + content: Expanded( + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: OutlinedButton( + onPressed: viewModel.onSelectFiles, + style: ElevatedButton.styleFrom( + minimumSize: const Size(200, 50), ), + child: Text(i18n.createCustomScreen_selectButton), ), - Expanded( - child: ListView.builder( - itemCount: viewModel.files.length, - itemBuilder: (context, index) { - return Text(p.relative( - viewModel.files[index].path, - from: viewModel.path,),); - },),), - ElevatedButton( - onPressed: viewModel.files.isNotEmpty && - viewModel.path.isNotEmpty - ? () { - finishViewModel.onAddCustomStageEntries( - viewModel.files, viewModel.path,); - viewModel.reset(); - Navigator.of(context).popUntil( - ModalRoute.withName('/create_selection'),); - } - : null, - style: ElevatedButton.styleFrom( - minimumSize: Size( - MediaQuery.of(context).size.width * 0.5, 50,),), - child: Text(i18n.createCustomScreen_stageFiles),), - ], - ),),),); + ), + Text(viewModel.path), + ], + ), + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + '${i18n.createCustomScreen_fileToInsert}${viewModel.files.length}', + style: textTheme.titleMedium, + ), + ), + ), + Expanded( + child: ListView.builder( + itemCount: viewModel.files.length, + itemBuilder: (context, index) { + return Text( + p.relative( + viewModel.files[index].path, + from: viewModel.path, + ), + ); + }, + ), + ), + ElevatedButton( + onPressed: viewModel.files.isNotEmpty && viewModel.path.isNotEmpty + ? () { + finishViewModel.onAddCustomStageEntries( + viewModel.files, + viewModel.path, + ); + viewModel.reset(); + Navigator.of(context).popUntil( + ModalRoute.withName('/create_selection'), + ); + } + : null, + style: ElevatedButton.styleFrom( + minimumSize: Size( + MediaQuery.of(context).size.width * 0.5, + 50, + ), + ), + child: Text(i18n.createCustomScreen_stageFiles), + ), + ], + ), + ), + ), + ); } } diff --git a/lib/features/create/create_finish/create_finish_viewmodel.dart b/lib/features/create/create_finish/create_finish_viewmodel.dart index 4530fbf..4461890 100644 --- a/lib/features/create/create_finish/create_finish_viewmodel.dart +++ b/lib/features/create/create_finish/create_finish_viewmodel.dart @@ -104,13 +104,13 @@ class CreateFinishViewModel with ChangeNotifier { notifyListeners(); } - onAddCustomTextureEntry( + void onAddCustomTextureEntry( HashMap>> replacementMap, ) { for (final entry in replacementMap.entries) { if (entries.containsKey(entry.key) && entries[entry.key] is CustomTexturesEntry) { - (entries[entry.key] as CustomTexturesEntry).pairs.addAll(entry.value); + (entries[entry.key]! as CustomTexturesEntry).pairs.addAll(entry.value); } else if (entries.containsKey(entry.key)) { throw Exception('Cannot add custom texture entry to existing entry'); } else { @@ -124,18 +124,25 @@ class CreateFinishViewModel with ChangeNotifier { notifyListeners(); } + void onAddFile(File file, String path) { + entries[path] = CustomStageEntry([file]); + totalFiles++; + currentState = AppState.changesStaged; + notifyListeners(); + } + void onRemoveFile(File file, String path) { if (entries.containsKey(path) && entries[path] is CustomStageEntry) { - (entries[path] as CustomStageEntry).files.remove(file); + (entries[path]! as CustomStageEntry).files.remove(file); } else if (entries.containsKey(path) && entries[path] is CustomSequencesEntry) { - (entries[path] as CustomSequencesEntry).pairs.removeWhere( + (entries[path]! as CustomSequencesEntry).pairs.removeWhere( (pair) => pair.item1.path == file.path || pair.item2.path == file.path, ); } else if (entries.containsKey(path) && entries[path] is CustomTexturesEntry) { - (entries[path] as CustomTexturesEntry) + (entries[path]! as CustomTexturesEntry) .pairs .removeWhere((pair) => pair.item1.path == file.path); } else { diff --git a/lib/features/create/create_replace_textures/create_replace_textures_viewmodel.dart b/lib/features/create/create_replace_textures/create_replace_textures_viewmodel.dart index 2f9cf34..1eda00d 100644 --- a/lib/features/create/create_replace_textures/create_replace_textures_viewmodel.dart +++ b/lib/features/create/create_replace_textures/create_replace_textures_viewmodel.dart @@ -16,6 +16,7 @@ import 'package:retro/otr/resource.dart'; import 'package:retro/otr/resource_type.dart'; import 'package:retro/otr/types/background.dart'; import 'package:retro/otr/types/texture.dart'; +import 'package:retro/otr/version.dart'; import 'package:retro/utils/log.dart'; import 'package:retro/utils/path.dart' as p; import 'package:tuple/tuple.dart'; @@ -170,8 +171,8 @@ Future?> processFolder( return null; } - String manifestContents = await manifestFile.readAsString(); - Map manifest = json.decode(manifestContents) as Map; + final manifestContents = await manifestFile.readAsString(); + final manifest = json.decode(manifestContents) as Map; // find all images in folder final supportedExtensions = ['.png', '.jpeg', '.jpg']; @@ -187,7 +188,7 @@ Future?> processFolder( p.normalize(texFile.path.split('$folderPath/').last.split('.').first); if (manifest.containsKey(texPathRelativeToFolder)) { final manifestEntry = - TextureManifestEntry.fromJson(manifest[texPathRelativeToFolder]); + TextureManifestEntry.fromJson(manifest[texPathRelativeToFolder] as Map); // if it is, check if the file has changed final texFileBytes = await texFile.readAsBytes(); final texFileHash = sha256.convert(texFileBytes).toString(); @@ -218,38 +219,32 @@ Future?> processFolder( Future?> processOTR( Tuple2, String> params) async { - try { - var fileFound = false; - final processedFiles = HashMap(); + final processedFiles = HashMap(); - // just use the first otr in the list for the directory name - final otrNameForOutputDirectory = - params.item1[0].split(Platform.pathSeparator).last.split('.').first; - - // if folder we'll export to exists, delete it - final dir = Directory('${params.item2}/$otrNameForOutputDirectory'); - if (dir.existsSync()) { - log('Deleting existing folder: ${params.item2}/$otrNameForOutputDirectory'); - await dir.delete(recursive: true); - } + // just use the first otr in the list for the directory name + final otrNameForOutputDirectory = + params.item1[0].split(Platform.pathSeparator).last.split('.').first; - for (final otrPath in params.item1) { - log('Processing OTR: $otrPath'); - final arcFile = Arc(otrPath); + // if folder we'll export to exists, delete it + final dir = Directory('${params.item2}/$otrNameForOutputDirectory'); + if (dir.existsSync()) { + log('Deleting existing folder: ${params.item2}/$otrNameForOutputDirectory'); + await dir.delete(recursive: true); + } - await arcFile.listItems(onFile: (String fileName, Uint8List data) async { - await processFile(fileName, data, '${params.item2}/$otrNameForOutputDirectory/$fileName', (TextureManifestEntry entry) { - processedFiles[fileName] = entry; - }); - },); - arcFile.close(); - } + for (final otrPath in params.item1) { + log('Processing OTR: $otrPath'); + final arcFile = Arc(otrPath); - return processedFiles; - } on StormLibException catch (e) { - log('Failed to find next file: ${e.message}'); - return null; + await arcFile.listItems(onFile: (String fileName, Uint8List data) async { + await processFile(fileName, data, '${params.item2}/$otrNameForOutputDirectory/$fileName', (TextureManifestEntry entry) { + processedFiles[fileName] = entry; + }); + },); + arcFile.close(); } + + return processedFiles.isEmpty ? null : processedFiles; } Future processFile(String fileName, Uint8List data, String outputPath, Function onProcessed) async { @@ -269,6 +264,11 @@ Future processFile(String fileName, Uint8List data, String outputPath, Fun final texture = Texture.empty(); texture.open(data); + // don't try to extract hd textures + if(texture.gameVersion == Version.roy){ + break; + } + final pngBytes = texture.toPNGBytes(); final textureFile = File('$outputPath.png'); await textureFile.create(recursive: true); diff --git a/lib/features/create/create_selection/create_selection_screen.dart b/lib/features/create/create_selection/create_selection_screen.dart index 4e70c83..3a3266d 100644 --- a/lib/features/create/create_selection/create_selection_screen.dart +++ b/lib/features/create/create_selection/create_selection_screen.dart @@ -15,39 +15,79 @@ class _CreateSelectionScreenState extends State { Widget build(BuildContext context) { final i18n = AppLocalizations.of(context)!; return CustomScaffold( - title: i18n.createSelectionScreen_title, - subtitle: i18n.createSelectionScreen_subtitle, - onBackButtonPressed: () { - Navigator.of(context).pop(); - }, - content: Expanded( - child: - Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - OptionCard( + title: i18n.createSelectionScreen_title, + subtitle: i18n.createSelectionScreen_subtitle, + onBackButtonPressed: () { + Navigator.of(context).pop(); + }, + content: Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Wrap( + spacing: 15, + children: [ + OptionCard( text: i18n.createSelectionScreen_nonHdTex, icon: Icons.texture, + overlay: Positioned( + top: 10, + left: 10, + child: Container( + width: 190, + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + color: Colors.yellow[800]!.withOpacity(0.7), + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon( + Icons.warning_amber_rounded, + color: Colors.white, + size: 15, + ), + const SizedBox(width: 2), + Text( + i18n.extractModsWarning_part1, + style: const TextStyle(color: Colors.white, fontSize: 10.5), + ), + ], + ), + Text( + i18n.extractModsWarning_part2, + style: const TextStyle(color: Colors.white, fontSize: 10.5), + ), + ], + ), + ), + ), onTap: () { Navigator.of(context).pushNamed('/create_replace_textures'); - },), - const SizedBox(width: 20), - OptionCard( - text: i18n.createSelectionScreen_customSequences, - icon: Icons.library_music, + }, + ), + OptionCard( + text: i18n.gameSelectionScreen_title, + icon: Icons.videogame_asset_rounded, onTap: () { - Navigator.of(context).pushNamed('/create_custom_sequences'); - },), - const SizedBox(width: 20), - OptionCard( + Navigator.of(context).pushNamed('/game_selection'); + }, + ), + OptionCard( text: i18n.createSelectionScreen_custom, icon: Icons.settings_suggest, onTap: () { Navigator.of(context).pushNamed('/create_custom'); - },), - ], - ) - ],),),); + }, + ), + ], + ) + ], + ), + ), + ); } } diff --git a/lib/features/create/create_selection/games/games_selection_screen.dart b/lib/features/create/create_selection/games/games_selection_screen.dart new file mode 100644 index 0000000..fe3d4b9 --- /dev/null +++ b/lib/features/create/create_selection/games/games_selection_screen.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:retro/ui/components/custom_scaffold.dart'; +import 'package:retro/ui/components/option_card.dart'; + +class GameSelectionScreen extends StatefulWidget { + const GameSelectionScreen({super.key}); + + @override + _GameSelectionScreenState createState() => _GameSelectionScreenState(); +} + +class _GameSelectionScreenState extends State { + @override + Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + return CustomScaffold( + title: i18n.gameSelectionScreen_title, + subtitle: i18n.gameSelectionScreen_subtitle, + onBackButtonPressed: () { + Navigator.of(context).pop(); + }, + content: Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Wrap( + spacing: 15, + alignment: WrapAlignment.center, + children: [ + OptionCard( + text: i18n.gameSelectionScreenSoh_title, + icon: Icons.directions_boat_filled_rounded, + onTap: () { + Navigator.of(context).pushNamed('/game_selection/soh'); + }, + ), + OptionCard( + text: i18n.gameSelectionScreen2Ship_title, + icon: Icons.dark_mode, + onTap: () { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + backgroundColor: const Color.fromARGB(255, 249, 210, 144), + content: Row( + children: [ + const Icon(Icons.favorite, color: Colors.red), + const SizedBox(width: 5), + Text(i18n.gameSelection2ShipComingSoon_text, style: const TextStyle(color: Colors.black, fontSize: 16)), + ], + ), + )); + }, + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/create/create_selection/games/soh/2ship_screen.dart b/lib/features/create/create_selection/games/soh/2ship_screen.dart new file mode 100644 index 0000000..eaa5710 --- /dev/null +++ b/lib/features/create/create_selection/games/soh/2ship_screen.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:retro/ui/components/custom_scaffold.dart'; +import 'package:retro/ui/components/option_card.dart'; + +class Ship2GameScreen extends StatefulWidget { + const Ship2GameScreen({super.key}); + + @override + _Ship2GameScreenState createState() => _Ship2GameScreenState(); +} + +class _Ship2GameScreenState extends State { + @override + Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + return CustomScaffold( + title: i18n.gameSelectionScreen2Ship_title, + subtitle: i18n.gameSelectionScreen2Ship_subtitle, + onBackButtonPressed: () { + Navigator.of(context).pop(); + }, + content: Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Wrap( + spacing: 15, + alignment: WrapAlignment.center, + children: [ + OptionCard( + text: i18n.gameSelectionScreenSoh_title, + icon: FontAwesomeIcons.puzzlePiece, + onTap: () { + Navigator.of(context).pushNamed('/game_selection/soh'); + }, + ), + OptionCard( + text: i18n.gameSelectionScreen2Ship_title, + icon: FontAwesomeIcons.drum, + onTap: () { + Navigator.of(context).pushNamed('/create_replace_textures'); + }, + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/create/create_selection/games/soh/soh_screen.dart b/lib/features/create/create_selection/games/soh/soh_screen.dart new file mode 100644 index 0000000..996b4dc --- /dev/null +++ b/lib/features/create/create_selection/games/soh/soh_screen.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:retro/ui/components/custom_scaffold.dart'; +import 'package:retro/ui/components/option_card.dart'; + +class SOHGameScreen extends StatefulWidget { + const SOHGameScreen({super.key}); + + @override + _SOHGameScreenState createState() => _SOHGameScreenState(); +} + +class _SOHGameScreenState extends State { + @override + Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + return CustomScaffold( + title: i18n.gameSelectionScreenSoh_title, + subtitle: i18n.gameSelectionScreenSoh_subtitle, + onBackButtonPressed: () { + Navigator.of(context).pop(); + }, + content: Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Wrap( + spacing: 15, + alignment: WrapAlignment.center, + children: [ + OptionCard( + text: i18n.createSelectionScreen_customSequences, + icon: Icons.library_music, + onTap: () { + Navigator.of(context).pushNamed('/create_custom_sequences'); + }, + ), + OptionCard( + text: i18n.sohCreateDebugFontScreen_title, + icon: FontAwesomeIcons.font, + onTap: () { + Navigator.of(context).pushNamed('/debug_generate_font'); + }, + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/debug/debug_font_generator/debug_font_generator_screen.dart b/lib/features/debug/debug_font_generator/debug_font_generator_screen.dart index e2ccf02..2d5d468 100644 --- a/lib/features/debug/debug_font_generator/debug_font_generator_screen.dart +++ b/lib/features/debug/debug_font_generator/debug_font_generator_screen.dart @@ -5,6 +5,10 @@ import 'dart:ui' as ui; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart' hide Texture, Image; import 'package:flutter/services.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:retro/features/create/create_finish/create_finish_viewmodel.dart'; import 'package:retro/ui/components/custom_scaffold.dart'; import 'package:retro/ui/components/numpad.dart'; @@ -12,8 +16,7 @@ class DebugGeneratorFontScreen extends StatefulWidget { const DebugGeneratorFontScreen({super.key}); @override - State createState() => - _DebugGeneratorFontScreenState(); + State createState() => _DebugGeneratorFontScreenState(); } class _DebugGeneratorFontScreenState extends State { @@ -23,10 +26,11 @@ class _DebugGeneratorFontScreenState extends State { double textScale = 1; int glyphWidth = 32; int glyphHeight = 32; + int rows = 18; + int columns = 32; void loadFont(File ttf) { - final fontName = - ttf.path.split(Platform.pathSeparator).last.split('.').first; + final fontName = ttf.path.split(Platform.pathSeparator).last.split('.').first; final font = FontLoader(fontName); font.addFont(Future.value(ttf.readAsBytesSync().buffer.asByteData())); font.load(); @@ -37,9 +41,10 @@ class _DebugGeneratorFontScreenState extends State { @override Widget build(BuildContext context) { + final i18n = AppLocalizations.of(context)!; return CustomScaffold( - title: 'Debug Font Generator', - subtitle: 'Convert and generate fonts for the oot debug menu', + title: i18n.sohCreateDebugFontScreen_title, + subtitle: i18n.sohCreateDebugFontScreen_subtitle, onBackButtonPressed: () { Navigator.of(context).pop(); }, @@ -53,13 +58,6 @@ class _DebugGeneratorFontScreenState extends State { direction: Axis.vertical, spacing: 10, children: [ - const Text( - 'Font to OoT Generator', - style: TextStyle( - color: Colors.white, - fontFamily: 'GoogleSans', - fontWeight: FontWeight.bold), - ), TextNumpad( title: 'Text Offset:', step: 0.5, @@ -98,6 +96,16 @@ class _DebugGeneratorFontScreenState extends State { }); }, ), + TextNumpad( + title: 'Rows:', + step: 1, + input: rows, + onInput: (num value) { + setState(() { + rows = value.toInt(); + }); + }, + ), ElevatedButton( child: const Text('Load Font'), onPressed: () async { @@ -111,10 +119,12 @@ class _DebugGeneratorFontScreenState extends State { }, ), if (fontFamily != null) - ElevatedButton( - onPressed: writeFontTable, - child: const Text('Convert Texture'), - ) + ElevatedButton( + onPressed: () async { + await writeFontTable(context); + }, + child: const Text('Convert Texture'), + ) ], ), ), @@ -126,15 +136,18 @@ class _DebugGeneratorFontScreenState extends State { borderRadius: BorderRadius.circular(10), ), child: fontFamily != null - ? CustomPaint( - painter: FontPainter( - fontFamily: fontFamily!, - offset: textOffset, - scale: textScale, - glyphWidth: glyphWidth, - glyphHeight: glyphHeight), - ) - : null, + ? CustomPaint( + painter: FontPainter( + fontFamily: fontFamily!, + offset: textOffset, + scale: textScale, + glyphWidth: glyphWidth, + glyphHeight: glyphHeight, + ht: rows, + vt: columns, + ), + ) + : null, ), ), ], @@ -143,10 +156,11 @@ class _DebugGeneratorFontScreenState extends State { ); } - Future writeFontTable() async { + Future writeFontTable(BuildContext context) async { final recorder = ui.PictureRecorder(); final width = glyphWidth * 8; final height = glyphHeight * 32; + final finishViewModel = Provider.of(context, listen: false); FontPainter( fontFamily: fontFamily!, offset: textOffset, @@ -154,19 +168,17 @@ class _DebugGeneratorFontScreenState extends State { glyphWidth: glyphWidth, glyphHeight: glyphHeight, drawGrid: false, - ).paint(Canvas(recorder), Size(width as double, height as double)); + ht: 8, + ).paint(Canvas(recorder), Size(width.toDouble(), height.toDouble())); final picture = recorder.endRecording(); final img = await picture.toImage(width, height); final pngBytes = await img.toByteData(format: ImageByteFormat.png); + final tmpDir = await getTemporaryDirectory(); + final file = File('${tmpDir.path}/sGfxPrintFontData.png'); - final result = await FilePicker.platform.saveFile( - type: FileType.custom, - allowedExtensions: ['png'], - fileName: 'sGfxPrintFontData.png', - ); - if (result != null) { - await File(result).writeAsBytes(pngBytes!.buffer.asUint8List()); - } + await file.writeAsBytes(pngBytes!.buffer.asUint8List()); + finishViewModel.onAddFile(file, 'textures/font/sGfxPrintFontData'); + Navigator.of(context).popUntil(ModalRoute.withName('/create_selection')); } } @@ -178,45 +190,46 @@ class FontPainter extends CustomPainter { this.glyphWidth = 32, this.glyphHeight = 32, this.drawGrid = true, + this.ht = 18, + this.vt = 32, }); - List> fontTable = [ - ['0', '4', '1', '5', '2', '6', '3', '7'], - ['8', 'C', '9', 'D', 'A', 'E', 'B', 'F'], - ['0', '4', '1', '5', '2', '6', '3', '7'], - ['8', '←', '9', '→', 'A', '↑', 'B', '↓'], - [' ', r'$', '!', '%', '"', '&', '#', "'"], - ['(', ',', ')', '-', '*', '.', '+', '/'], - ['0', '4', '1', '5', '2', '6', '3', '7'], - ['8', '<', '9', '=', ':', '>', ';', '?'], - ['@', 'D', 'A', 'E', 'B', 'F', 'C', 'G'], - ['H', 'L', 'I', 'M', 'J', 'N', 'K', 'O'], - ['P', 'T', 'Q', 'U', 'R', 'V', 'S', 'W'], - ['X', '¥', 'Y', ']', 'Z', '^', '[', '_'], - ["'", 'd', 'a', 'e', 'b', 'f', 'c', 'g'], - ['h', 'l', 'i', 'm', 'j', 'n', 'k', 'o'], - ['p', 't', 'q', 'u', 'r', 'v', 's', 'w'], - ['x', ';', 'y', '}', 'z', '~', '{', '␡'], - [' ', ',', '.', ' ', ' ', ' ', ' ', ' '], - ['い', 'や', 'う', 'や', 'え', 'や', 'え', 'や'], - ['-', 'え', 'あ', 'お', 'い', 'か', 'い', 'か'], - ['く', 'し', 'け', 'チ', 'こ', 'せ', 'こ', 'せ'], - [' ', ',', ' ', '.', ' ', ' ', ' ', ' '], - ['イ', 'ヤ', 'ウ', 'コ', 'ェ', 'ヨ', 'エ', 'ッ'], - ['-', 'エ', 'ア', 'オ', 'イ', 'カ', 'ウ', 'キ'], - ['ワ', 'シ', 'ク', 'ス', 'コ', 'セ', 'サ', 'ン'], - ['タ', 'ト', '子', 'ナ', 'ッ', 'ニ', 'テ', 'ヌ'], - ['ネ', 'つ', 'ノ', 'へ', 'い', 'ホ', 'い', 'マ'], - [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], - ['リ', 'ワ', 'ル', 'ン', 'レ', '"', '口', 'ロ'], - [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], - ['ね', 'ふ', 'の', 'へ', 'は', 'ほ', 'は', 'ま'], - ['み', 'せ', 'せ', 'ゆ', 'の', 'よ', 'も', 'ら'], - [' ', 'わ', 'る', 'ん', 'れ', '"', 'ろ', 'ロ'] + List fontTable = [ + '0', '4', '1', '5', '2', '6', '3', '7', + '8', 'C', '9', 'D', 'A', 'E', 'B', 'F', + '0', '4', '1', '5', '2', '6', '3', '7', + '8', '←', '9', '→', 'A', '↑', 'B', '↓', + ' ', r'$', '!', '%', '"', '&', '#', "'", + '(', ',', ')', '-', '*', '.', '+', '/', + '0', '4', '1', '5', '2', '6', '3', '7', + '8', '<', '9', '=', ':', '>', ';', '?', + '@', 'D', 'A', 'E', 'B', 'F', 'C', 'G', + 'H', 'L', 'I', 'M', 'J', 'N', 'K', 'O', + 'P', 'T', 'Q', 'U', 'R', 'V', 'S', 'W', + 'X', '¥', 'Y', ']', 'Z', '^', '[', '_', + "'", 'd', 'a', 'e', 'b', 'f', 'c', 'g', + 'h', 'l', 'i', 'm', 'j', 'n', 'k', 'o', + 'p', 't', 'q', 'u', 'r', 'v', 's', 'w', + 'x', ';', 'y', '}', 'z', '~', '{', '␡', + ' ', ',', '.', ' ', ' ', ' ', ' ', ' ', + 'い', 'や', 'う', 'や', 'え', 'や', 'え', 'や', + '-', 'え', 'あ', 'お', 'い', 'か', 'い', 'か', + 'く', 'し', 'け', 'チ', 'こ', 'せ', 'こ', 'せ', + ' ', ',', ' ', '.', ' ', ' ', ' ', ' ', + 'イ', 'ヤ', 'ウ', 'コ', 'ェ', 'ヨ', 'エ', 'ッ', + '-', 'エ', 'ア', 'オ', 'イ', 'カ', 'ウ', 'キ', + 'ワ', 'シ', 'ク', 'ス', 'コ', 'セ', 'サ', 'ン', + 'タ', 'ト', '子', 'ナ', 'ッ', 'ニ', 'テ', 'ヌ', + 'ネ', 'つ', 'ノ', 'へ', 'い', 'ホ', 'い', 'マ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 'リ', 'ワ', 'ル', 'ン', 'レ', '"', '口', 'ロ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 'ね', 'ふ', 'の', 'へ', 'は', 'ほ', 'は', 'ま', + 'み', 'せ', 'せ', 'ゆ', 'の', 'よ', 'も', 'ら', + ' ', 'わ', 'る', 'ん', 'れ', '"', 'ろ', 'ロ' ]; - final regExp = RegExp( - r'[\^$*.\[\]{}()?\-"!@#%&/\,><:;_~`+=' // <-- Notice the escaped symbols + final regExp = RegExp(r'[\^$*.\[\]{}()?\-"!@#%&/\,><:;_~`+=' // <-- Notice the escaped symbols "'" // <-- ' is added to the expression ']'); @@ -226,9 +239,10 @@ class FontPainter extends CustomPainter { final int glyphWidth; final int glyphHeight; final bool drawGrid; + final int ht; + final int vt; - double convertFontSize(double size) => - size / WidgetsBinding.instance.window.devicePixelRatio; + double convertFontSize(double size) => size / WidgetsBinding.instance.window.devicePixelRatio; @override void paint(Canvas canvas, Size size) { @@ -237,13 +251,15 @@ class FontPainter extends CustomPainter { ..strokeWidth = 1 ..style = PaintingStyle.stroke; - for (var id = 0; id < 32 * 8; id++) { - final tx = (id % 8).floorToDouble(); - final ty = (id / 8).floorToDouble(); + for (var id = 0; id < vt * ht; id++) { + final tx = (id % ht).floorToDouble(); + final ty = (id / ht).floorToDouble(); final to = Offset(tx * glyphWidth, ty * glyphHeight); - final rect = Rect.fromLTWH( - to.dx, to.dy, glyphWidth.toDouble(), glyphHeight.toDouble()); - final text = fontTable[ty.toInt()][tx.toInt()]; + final rect = Rect.fromLTWH(to.dx, to.dy, glyphWidth.toDouble(), glyphHeight.toDouble()); + if (id >= fontTable.length) { + continue; + } + final text = fontTable[id]; if (drawGrid) { canvas.drawRect(rect, paint); } @@ -254,27 +270,18 @@ class FontPainter extends CustomPainter { text: text == ' ' && ty >= 16 ? '�' : text, style: TextStyle( color: Colors.white, - fontSize: text == ' ' - ? convertFontSize(20) - : convertFontSize(text.contains(regExp) - ? glyphHeight - 5 - : glyphHeight.toDouble()) * - scale, + fontSize: text == ' ' ? convertFontSize(20) : convertFontSize(text.contains(regExp) ? glyphHeight - 5 : glyphHeight.toDouble()) * scale, fontFamily: fontFamily), ), ); textPainter.layout(maxWidth: size.width); textPainter.paint( canvas, - Offset( - tx * glyphWidth + (glyphWidth / 2) - (textPainter.size.width / 2), - (ty * glyphHeight + - (glyphHeight / 2) - - (textPainter.size.height / 2)) - - offset)); + Offset(tx * glyphWidth + (glyphWidth / 2) - (textPainter.size.width / 2), + (ty * glyphHeight + (glyphHeight / 2) - (textPainter.size.height / 2)) - offset)); } } @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; } diff --git a/lib/features/debug/debug_selection/debug_selection_screen.dart b/lib/features/debug/debug_selection/debug_selection_screen.dart index 7e38cad..8dd7bb7 100644 --- a/lib/features/debug/debug_selection/debug_selection_screen.dart +++ b/lib/features/debug/debug_selection/debug_selection_screen.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:retro/ui/components/custom_scaffold.dart'; import 'package:retro/ui/components/option_card.dart'; @@ -13,35 +12,31 @@ class DebugSelectionScreen extends StatefulWidget { class _DebugSelectionScreenState extends State { @override Widget build(BuildContext context) { - final i18n = AppLocalizations.of(context)!; return CustomScaffold( - title: 'Debug Selection', - subtitle: 'This options are on an experimental state and may not work as expected', - onBackButtonPressed: () { - Navigator.of(context).pop(); - }, - content: Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - OptionCard( - text: 'Textures', - icon: Icons.texture, - onTap: () { - Navigator.of(context).pushNamed('/debug_convert_textures'); - },), - const SizedBox(width: 20), - OptionCard( - text: 'Font Generator', - icon: Icons.font_download, - onTap: () { - Navigator.of(context).pushNamed('/debug_generate_font'); - },), - ], - ) - ],),),); + title: 'Debug Selection', + subtitle: 'This options are on an experimental state and may not work as expected', + onBackButtonPressed: () { + Navigator.of(context).pop(); + }, + content: Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + OptionCard( + text: 'Textures', + icon: Icons.texture, + onTap: () { + Navigator.of(context).pushNamed('/debug_convert_textures'); + }, + ), + ], + ) + ], + ), + ), + ); } } diff --git a/lib/features/home/home_screen.dart b/lib/features/home/home_screen.dart index 7158256..f2c3ce3 100644 --- a/lib/features/home/home_screen.dart +++ b/lib/features/home/home_screen.dart @@ -1,10 +1,14 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gradient_animation_text/flutter_gradient_animation_text.dart'; +import 'package:http/http.dart' as http; import 'package:provider/provider.dart'; +import 'package:retro/context.dart'; import 'package:retro/features/home/home_viewmodel.dart'; import 'package:retro/ui/components/option_card.dart'; +import 'package:url_launcher/url_launcher.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -14,6 +18,38 @@ class HomeScreen extends StatefulWidget { } class _HomeScreenState extends State { + int versionCode = -1; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + await getRetroVersion(); + }); + } + + Future getRetroVersion() async { + const url = 'https://raw.githubusercontent.com/HarbourMasters/retro/main/release.json'; + + try { + final response = await http.get( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + } + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + setState(() { + versionCode = data['version'] as int; + }); + } + } catch (e) { + print('Error fetching version code: $e'); + } + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -23,66 +59,113 @@ class _HomeScreenState extends State { final i18n = AppLocalizations.of(context)!; return Scaffold( - body: Column( - mainAxisAlignment: MainAxisAlignment.center, + body: Stack( children: [ - Text(i18n.appTitle, style: textTheme.displaySmall), - Row( + Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('Brought to you by', style: textTheme.titleSmall), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 4), - child: GradientAnimationText( - text: Text('HM64'), - colors: [ - Colors.indigo, - Colors.blue, - Colors.green, - Colors.yellow, - Colors.orange, - Colors.red, - ], - duration: const Duration(seconds: 2), - ), + Text(i18n.appTitle, style: textTheme.displaySmall), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Brought to you by', style: textTheme.titleSmall), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: GradientAnimationText( + text: Text('HM64'), + colors: [ + Colors.indigo, + Colors.blue, + Colors.green, + Colors.yellow, + Colors.orange, + Colors.red, + ], + duration: const Duration(seconds: 2), + ), + ), + const Icon(Icons.favorite, color: Colors.red), + ], + ), + const SizedBox(height: 40), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + OptionCard( + text: i18n.home_createOption, + icon: Icons.add_circle, + onMouseEnter: () => viewModel.onCreateOTRCardFocused(i18n.home_createOptionSubtitle), + onMouseLeave: viewModel.onCardFocusLost, + onTap: () { + Navigator.pushNamed(context, '/create_selection'); + },), + const SizedBox(width: 20), + OptionCard( + text: i18n.home_inspectOption, + icon: Icons.visibility, + onMouseEnter: () => viewModel.onCreateOTRCardFocused(i18n.home_inspectOptionSubtitle), + onMouseLeave: viewModel.onCardFocusLost, + onTap: () { + Navigator.pushNamed(context, '/view_otr'); + },), + const SizedBox(width: 20), + OptionCard( + text: 'Debug', + icon: Icons.warning_rounded, + onTap: () { + Navigator.of(context).pushNamed('/debug_selection'); + },), + ], + ), + const SizedBox(height: 40), + Text( + '${RetroContext.git.commitHash} - ${RetroContext.git.branch}', + style: textTheme.titleSmall?.copyWith(color: colorScheme.onSurface.withOpacity(0.2)), + ), + Text( + RetroContext.git.commitDate, + style: textTheme.titleSmall?.copyWith(color: colorScheme.onSurface.withOpacity(0.2)), ), - const Icon(Icons.favorite, color: Colors.red), ], ), - const SizedBox(height: 40), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - OptionCard( - text: i18n.home_createOption, - icon: Icons.add_circle, - onMouseEnter: () => viewModel.onCreateOTRCardFocused(i18n.home_createOptionSubtitle), - onMouseLeave: viewModel.onCardFocusLost, - onTap: () { - Navigator.pushNamed(context, '/create_selection'); - },), - const SizedBox(width: 20), - OptionCard( - text: i18n.home_inspectOption, - icon: Icons.visibility, - onMouseEnter: () => viewModel.onCreateOTRCardFocused(i18n.home_inspectOptionSubtitle), - onMouseLeave: viewModel.onCardFocusLost, - onTap: () { - Navigator.pushNamed(context, '/view_otr'); - },), - const SizedBox(width: 20), - OptionCard( - text: 'Debug', - icon: Icons.warning_rounded, - onTap: () { - Navigator.of(context).pushNamed('/debug_selection'); - },), - ], + if(versionCode != -1 && RetroContext.versionCode < versionCode) + Positioned( + top: 20, + left: 20, + child: InkWell( + borderRadius: BorderRadius.circular(5), + onTap: () async { + final url = Uri.parse('https://github.com/HarbourMasters/retro/releases/latest'); + if (!await launchUrl(url)) { + throw Exception('Could not launch $url'); + } + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.3), + borderRadius: BorderRadius.circular(5), + ), + child: Row( + children: [ + Icon( + Icons.warning_amber_rounded, + color: Colors.yellow[700], + size: 25, + ), + const SizedBox(width: 5), + Text( + 'New version available', + style: TextStyle( + color: Colors.yellow[700], + fontSize: 15, + ), + ), + ], + ), + ), + ), ), - const SizedBox(height: 40), - Text(viewModel.focusedCardInfo, - style: textTheme.titleSmall - ?.copyWith(color: colorScheme.onSurface.withOpacity(0.2)),), ], ), ); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index cb42d88..3822982 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -48,5 +48,21 @@ "inspectOtrScreen_inspectOtrSubtitle": "Überprüfe die Inhalte einer OTR / O2R", "inspectOtrScreen_noOtrSelected": "Keine OTR / O2R ausgewählt", "inspectOtrScreen_selectButton": "Wähle", - "inspectOtrScreen_search": "Suchen" + "inspectOtrScreen_search": "Suchen", + + "gameSelectionScreen_title": "Spezifische Werkzeuge", + "gameSelectionScreen_subtitle": "Wähle das Spiel aus, für das du eine Auswahl treffen möchtest", + + "gameSelectionScreenSoh_title": "Ship of Harkinian", + "gameSelectionScreenSoh_subtitle": "Wähle das Werkzeug aus, das du verwenden möchtest", + + "sohCreateDebugFontScreen_title": "Debug-Schriftart Generieren", + "sohCreateDebugFontScreen_subtitle": "Generiere Debug-Schriften für den SOH-Karten-Selektor", + + "gameSelectionScreen2Ship_title": "2 Ship 2 Harkinian", + "gameSelectionScreen2Ship_subtitle": "Wähle das Werkzeug aus, das du verwenden möchtest", + "gameSelection2ShipComingSoon_text": "Werkzeuge für 2 Ship 2 Harkinian kommen bald!", + + "extractModsWarning_part1": "Existierende Mods extrahieren", + "extractModsWarning_part2": "Könnte nicht funktionieren." } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 64ebd5b..ed80337 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -48,5 +48,21 @@ "inspectOtrScreen_inspectOtrSubtitle": "Inspect the contents of an OTR / O2R", "inspectOtrScreen_noOtrSelected": "No OTR / O2R Selected", "inspectOtrScreen_selectButton": "Select", - "inspectOtrScreen_search": "Search" + "inspectOtrScreen_search": "Search", + + "gameSelectionScreen_title": "Game Specific Tools", + "gameSelectionScreen_subtitle": "Select the game you want to create a selection for", + + "gameSelectionScreenSoh_title": "Ship of Harkinian", + "gameSelectionScreenSoh_subtitle": "Select the tool you want to use", + + "sohCreateDebugFontScreen_title": "Debug Font Converter", + "sohCreateDebugFontScreen_subtitle": "Convert and generate fonts for the soh map selector", + + "gameSelectionScreen2Ship_title": "2 Ship 2 Harkinian", + "gameSelectionScreen2Ship_subtitle": "Select the tool you want to use", + "gameSelection2ShipComingSoon_text": "2 Ship 2 Harkinian Tools are coming soon!", + + "extractModsWarning_part1": "Extracting existing mods", + "extractModsWarning_part2": "might not work properly." } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index dba29a1..4597614 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -48,5 +48,21 @@ "inspectOtrScreen_inspectOtrSubtitle": "Inspeccione el contenido de un OTR / O2R", "inspectOtrScreen_noOtrSelected": "No se ha seleccionado un OTR / O2R", "inspectOtrScreen_selectButton": "Seleccionar", - "inspectOtrScreen_search": "Buscar" + "inspectOtrScreen_search": "Buscar", + + "gameSelectionScreen_title": "Herramientas Específicas", + "gameSelectionScreen_subtitle": "Selecciona el juego para el cual deseas crear una selección", + + "gameSelectionScreenSoh_title": "Ship of Harkinian", + "gameSelectionScreenSoh_subtitle": "Selecciona la herramienta que deseas utilizar", + + "sohCreateDebugFontScreen_title": "Generar Debug Font", + "sohCreateDebugFontScreen_subtitle": "Genera debug fonts para el selector de mapas de soh", + + "gameSelectionScreen2Ship_title": "2 Ship 2 Harkinian", + "gameSelectionScreen2Ship_subtitle": "Selecciona la herramienta que deseas utilizar", + "gameSelection2ShipComingSoon_text": "Herramientas para 2 Ship 2 Harkinian llegarán pronto!", + + "extractModsWarning_part1": "Extraer mods ya existentes", + "extractModsWarning_part2": "puede no funcionar." } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index bb02277..c3befaf 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -48,5 +48,21 @@ "inspectOtrScreen_inspectOtrSubtitle": "Inspecter le contenu d'un OTR / O2R", "inspectOtrScreen_noOtrSelected": "Aucun OTR / O2R sélectionné", "inspectOtrScreen_selectButton": "Sélectionner", - "inspectOtrScreen_search": "Rechercher" + "inspectOtrScreen_search": "Rechercher", + + "gameSelectionScreen_title": "Outils Spécifiques au Jeu", + "gameSelectionScreen_subtitle": "Sélectionnez le jeu pour lequel vous souhaitez créer une sélection", + + "gameSelectionScreenSoh_title": "Ship of Harkinian", + "gameSelectionScreenSoh_subtitle": "Sélectionnez l'outil que vous souhaitez utiliser", + + "sohCreateDebugFontScreen_title": "Générateur de Police de Débogage", + "sohCreateDebugFontScreen_subtitle": "Convertit et génère des polices pour le sélecteur de cartes du soh", + + "gameSelectionScreen2Ship_title": "2 Ship 2 Harkinian", + "gameSelectionScreen2Ship_subtitle": "Sélectionnez l'outil que vous souhaitez utiliser", + "gameSelection2ShipComingSoon_text": "Les outils 2 Ship 2 Harkinian arrivent bientôt!", + + "extractModsWarning_part1": "Extraire des mods déjà existants", + "extractModsWarning_part2": "Ça pourrait ne pas fonctionner." } \ No newline at end of file diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 709d5f5..ce2f7fa 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -6,7 +6,7 @@ "home_inspectOptionSubtitle": "Inspecteer de inhoud van een OTR / O2R", "createSelectionScreen_title": "Selectie maken", "createSelectionScreen_subtitle": "Selecteer het type selectie dat je wilt maken", - "createSelectionScreen_nonHdTex": "Vervang textures (niet-HD)", + "createSelectionScreen_nonHdTex": "Vervang textures", "createSelectionScreen_customSequences": "Aangepaste composities", "createSelectionScreen_custom": "Aangepast", "createReplaceTexturesScreen_Option": "Vervang textures", @@ -48,5 +48,21 @@ "inspectOtrScreen_inspectOtrSubtitle": "Inspecteer de inhoud van een OTR / O2R", "inspectOtrScreen_noOtrSelected": "Geen OTR / O2R geselecteerd", "inspectOtrScreen_selectButton": "Selecteer", - "inspectOtrScreen_search": "Zoek" + "inspectOtrScreen_search": "Zoek", + + "gameSelectionScreen_title": "Specifieke Tools", + "gameSelectionScreen_subtitle": "Selecteer het spel waarvoor je een selectie wilt maken", + + "gameSelectionScreenSoh_title": "Ship of Harkinian", + "gameSelectionScreenSoh_subtitle": "Selecteer de tool die je wilt gebruiken", + + "sohCreateDebugFontScreen_title": "Debug Lettertype Genereren", + "sohCreateDebugFontScreen_subtitle": "Genereer debug-lettertypen voor de soh map selector", + + "gameSelectionScreen2Ship_title": "2 Ship 2 Harkinian", + "gameSelectionScreen2Ship_subtitle": "Selecteer de tool die je wilt gebruiken", + "gameSelection2ShipComingSoon_text": "Tools voor 2 Ship 2 Harkinian komen binnenkort!", + + "extractModsWarning_part1": "Bestaande mods extraheren", + "extractModsWarning_part2": "Het zou niet kunnen werken." } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c06eacd..4f3f7fe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,11 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:retro/context.dart'; import 'package:retro/features/create/create_custom/create_custom_screen.dart'; import 'package:retro/features/create/create_custom/create_custom_viewmodel.dart'; import 'package:retro/features/create/create_custom_sequences/create_custom_sequences_screen.dart'; @@ -12,6 +14,9 @@ import 'package:retro/features/create/create_finish/create_finish_viewmodel.dart import 'package:retro/features/create/create_replace_textures/create_replace_textures_screen.dart'; import 'package:retro/features/create/create_replace_textures/create_replace_textures_viewmodel.dart'; import 'package:retro/features/create/create_selection/create_selection_screen.dart'; +import 'package:retro/features/create/create_selection/games/games_selection_screen.dart'; +import 'package:retro/features/create/create_selection/games/soh/2ship_screen.dart'; +import 'package:retro/features/create/create_selection/games/soh/soh_screen.dart'; import 'package:retro/features/debug/debug_convert_textures/debug_convert_textures_screen.dart'; import 'package:retro/features/debug/debug_font_generator/debug_font_generator_screen.dart'; import 'package:retro/features/debug/debug_selection/debug_selection_screen.dart'; @@ -21,19 +26,34 @@ import 'package:retro/features/inspect_otr/inspect_otr_screen.dart'; import 'package:retro/features/inspect_otr/inspect_otr_viewmodel.dart'; import 'package:retro/ui/components/ephemeral_bar.dart'; import 'package:retro/ui/theme/theme.dart'; +import 'package:retro/utils/git.dart'; import 'package:window_size/window_size.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); + final errorFile = File('latest.log') + ..createSync(); + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { setWindowTitle('Retro'); - setWindowMinSize(const Size(600, 600)); + setWindowMinSize(const Size(700, 700)); setWindowMaxSize(Size.infinite); } + RetroContext.git = GitLoader.getGitInfo(); + + runZonedGuarded>(bindApp, (error, stack) async { + // Log to file + final errorString = 'Error: $error\nStack: $stack'; + await errorFile.writeAsString(errorString, mode: FileMode.append); + }); +} + +Future bindApp() async { runApp(const Retro()); } + class Retro extends StatelessWidget { const Retro({super.key}); @@ -57,42 +77,43 @@ class Retro extends StatelessWidget { GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], - supportedLocales: const [ - Locale('en', ''), - Locale('de', ''), - Locale('fr', ''), - Locale('es', ''), - Locale('nl', '') - ], + supportedLocales: const [Locale('en', ''), Locale('de', ''), Locale('fr', ''), Locale('es', ''), Locale('nl', '')], darkTheme: darkTheme(), theme: lightTheme(), themeMode: ThemeMode.dark, initialRoute: '/', builder: (context, child) { - return Overlay(initialEntries: [ - OverlayEntry( + return Overlay( + initialEntries: [ + OverlayEntry( builder: (context) => ScaffoldMessenger( child: Stack( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height - 24, - child: child!,), - const Positioned(bottom: 0, child: EphemeralBar()), - ], - ), - ),) - ],); + children: [ + SizedBox( + height: MediaQuery.of(context).size.height - 24, + child: child, + ), + const Positioned(bottom: 0, child: EphemeralBar()), + ], + ), + ), + ) + ], + ); }, routes: { '/': (context) => const HomeScreen(), '/create_selection': (context) => const CreateSelectionScreen(), + '/game_selection': (context) => const GameSelectionScreen(), + '/game_selection/soh': (context) => const SOHGameScreen(), + '/game_selection/2ship': (context) => const Ship2GameScreen(), '/create_custom': (context) => const CreateCustomScreen(), '/view_otr': (context) => const InspectOTRScreen(), '/create_custom_sequences': (context) => const CreateCustomSequencesScreen(), '/create_replace_textures': (context) => const CreateReplaceTexturesScreen(), - '/debug_selection':(context) => const DebugSelectionScreen(), - '/debug_convert_textures':(context) => const DebugGeneratorFontsScreen(), - '/debug_generate_font':(context) => const DebugGeneratorFontScreen(), + '/debug_selection': (context) => const DebugSelectionScreen(), + '/debug_convert_textures': (context) => const DebugGeneratorFontsScreen(), + '/debug_generate_font': (context) => const DebugGeneratorFontScreen(), }, ), ); diff --git a/lib/ui/components/custom_scaffold.dart b/lib/ui/components/custom_scaffold.dart index d041975..0496f07 100644 --- a/lib/ui/components/custom_scaffold.dart +++ b/lib/ui/components/custom_scaffold.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; // ignore: constant_identifier_names const CONTENT_PADDING_VERTICAL = 15.0; @@ -6,7 +7,6 @@ const CONTENT_PADDING_VERTICAL = 15.0; const CONTENT_PADDING_HORIZONTAL = 15.0; class CustomScaffold extends StatelessWidget { - const CustomScaffold({ super.key, required this.title, @@ -27,53 +27,62 @@ class CustomScaffold extends StatelessWidget { final theme = Theme.of(context); final colorScheme = theme.colorScheme; final textTheme = theme.textTheme; + final size = MediaQuery.of(context).size; return Scaffold( - body: Padding( - padding: const EdgeInsets.only( - top: CONTENT_PADDING_VERTICAL, - left: CONTENT_PADDING_HORIZONTAL, - right: CONTENT_PADDING_HORIZONTAL, - ), - child: Column( - children: [ - SizedBox( - height: 60, - child: Row( - children: [ - // back button - if (onBackButtonPressed != null) - IconButton( - icon: const Icon(Icons.chevron_left_outlined), - color: Colors.white, - splashRadius: 30, - onPressed: () => onBackButtonPressed!(), - ), - - // title - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(title, style: textTheme.headlineSmall ), - if (subtitle != null) - Text(subtitle!, style: textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurface.withOpacity(0.5),), + body: SizedBox( + width: size.width, + height: size.height, + child: Padding( + padding: const EdgeInsets.only( + top: CONTENT_PADDING_VERTICAL, + left: CONTENT_PADDING_HORIZONTAL, + right: CONTENT_PADDING_HORIZONTAL, + ), + child: Column( + children: [ + SizedBox( + height: 60, + child: Row( + children: [ + // back button + if (onBackButtonPressed != null) + IconButton( + icon: const Icon(Icons.chevron_left_outlined), + color: Colors.white, + splashRadius: 30, + onPressed: () => onBackButtonPressed!(), ), - ], - ), - // top right widget - if (topRightWidget != null) - Expanded( - child: Align( - alignment: Alignment.centerRight, - child: topRightWidget!,),), - ], + // title + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: textTheme.headlineSmall), + if (subtitle != null) + Text( + subtitle!, + style: textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurface.withOpacity(0.5), + ), + ), + ], + ), + // top right widget + if (topRightWidget != null) + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: topRightWidget, + ), + ), + ], + ), ), - ), - content - ], + content + ], + ), ), ), ); diff --git a/lib/ui/components/ephemeral_bar.dart b/lib/ui/components/ephemeral_bar.dart index 44724a1..324c5e0 100644 --- a/lib/ui/components/ephemeral_bar.dart +++ b/lib/ui/components/ephemeral_bar.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:retro/context.dart'; import 'package:retro/features/create/create_finish/create_finish_screen.dart'; import 'package:retro/features/create/create_finish/create_finish_viewmodel.dart'; import 'package:retro/ui/theme/colors.dart'; @@ -95,7 +96,7 @@ class _EphemeralBarState extends State child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Text('Retro: 0.1.2', style: textTheme.bodyMedium!.copyWith( + Text('Retro: ${RetroContext.versionName}', style: textTheme.bodyMedium!.copyWith( color: colorScheme.onSurface,), ), if (!isExpanded) diff --git a/lib/ui/components/option_card.dart b/lib/ui/components/option_card.dart index 474b445..ea695f6 100644 --- a/lib/ui/components/option_card.dart +++ b/lib/ui/components/option_card.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; class OptionCard extends StatefulWidget { - const OptionCard({ super.key, required this.text, required this.icon, + this.overlay, this.onMouseEnter, this.onMouseLeave, required this.onTap, @@ -14,6 +15,7 @@ class OptionCard extends StatefulWidget { final IconData icon; final Function? onMouseEnter; final Function? onMouseLeave; + final Widget? overlay; final void Function() onTap; @override @@ -29,50 +31,55 @@ class _OptionCardState extends State { final colorScheme = theme.colorScheme; final textTheme = theme.textTheme; - return Column(children: [ - MouseRegion( - onEnter: (event) { - setState(() { isHovered = true; }); + return Stack( + children: [ + Column( + children: [ + MouseRegion( + onEnter: (event) { + setState(() { + isHovered = true; + }); - if (widget.onMouseEnter != null) { - widget.onMouseEnter!(); - } - }, - onExit: (event) { - setState(() { isHovered = false; }); - if (widget.onMouseLeave != null) { - widget.onMouseLeave!(); - } - }, - child: GestureDetector( - onTap: widget.onTap, - child: Container( - height: MediaQuery.of(context).size.height * 0.5, - decoration: BoxDecoration( - color: isHovered ? Colors.white24 : Colors.white12, - borderRadius: BorderRadius.circular(20), + if (widget.onMouseEnter != null) { + widget.onMouseEnter!(); + } + }, + onExit: (event) { + setState(() { + isHovered = false; + }); + if (widget.onMouseLeave != null) { + widget.onMouseLeave!(); + } + }, + child: GestureDetector( + onTap: widget.onTap, + child: DecoratedBox( + decoration: BoxDecoration( + color: isHovered ? Colors.white24 : Colors.white12, + borderRadius: BorderRadius.circular(20), + ), + child: Padding( + padding: const EdgeInsets.all(80), + child: Icon( + widget.icon, + size: 50, + ), + ), + ), + ), + ), + const SizedBox(height: 10), + Text( + widget.text, + style: textTheme.bodyLarge?.copyWith(color: colorScheme.onSurface.withOpacity(0.5)), + textAlign: TextAlign.center, ), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 80, vertical: 100), - child: Icon( - widget.icon, - size: 50, - ),), - ), + ], ), - ), - const SizedBox(height: 10), - SizedBox( - width: 180, - height: 25, - child: Text( - widget.text, - style: textTheme.bodyLarge - ?.copyWith(color: colorScheme.onSurface.withOpacity(0.5)), - textAlign: TextAlign.center, - ),), - ],); + widget.overlay ?? const SizedBox(), + ], + ); } - } diff --git a/lib/utils/git.dart b/lib/utils/git.dart new file mode 100644 index 0000000..1aeede2 --- /dev/null +++ b/lib/utils/git.dart @@ -0,0 +1,15 @@ +import 'package:retro/auto/build.dart'; + +class GitInfo { + GitInfo({required this.branch, required this.commitHash, this.commitDate = ''}); + + final String branch; + final String commitHash; + final String commitDate; +} + +class GitLoader { + static GitInfo getGitInfo() { + return GitInfo(branch: branch, commitHash: commitHash, commitDate: commitDate.split(' -').first); + } +} diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 67e1441..3e70df5 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "retro") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "io.dcvz.retro") +set(APPLICATION_ID "dev.hm64.retro") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 9a9ca54..fda153a 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,12 @@ import FlutterMacOS import Foundation +import path_provider_foundation import url_launcher_macos import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 9a12841..60e5d0d 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -2,6 +2,9 @@ PODS: - flutter_storm (0.0.1): - FlutterMacOS - FlutterMacOS (1.0.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - window_size (0.0.2): @@ -10,6 +13,7 @@ PODS: DEPENDENCIES: - flutter_storm (from `Flutter/ephemeral/.symlinks/plugins/flutter_storm/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) @@ -18,6 +22,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/flutter_storm/macos FlutterMacOS: :path: Flutter/ephemeral + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos window_size: @@ -26,6 +32,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: flutter_storm: 9d6bb8ed36b51e877d7a3aef59328b696766d5d9 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index 4594936..ffd9f89 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = retro // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = io.dcvz.retro +PRODUCT_BUNDLE_IDENTIFIER = dev.hm64.retro // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2022 io.dcvz. All rights reserved. +PRODUCT_COPYRIGHT = MIT brought to you by HM64 diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dddb8a3..08c3ab1 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.network.client + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a..ee95ab7 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.network.client + diff --git a/pubspec.lock b/pubspec.lock index b596a31..8f17d29 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -142,6 +142,30 @@ packages: description: flutter source: sdk version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + url: "https://pub.dev" + source: hosted + version: "10.7.0" + http: + dependency: "direct main" + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" image: dependency: "direct main" description: @@ -230,6 +254,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + url: "https://pub.dev" + source: hosted + version: "2.2.4" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" petitparser: dependency: transitive description: @@ -238,6 +310,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -436,6 +516,14 @@ packages: url: "https://github.com/google/flutter-desktop-embedding.git" source: git version: "0.1.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 24272da..981c091 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: cupertino_icons: ^1.0.2 provider: ^6.0.4 file_picker: ^5.1.0 - url_launcher: ^6.1.7 + url_launcher: ^6.2.6 window_size: git: url: https://github.com/google/flutter-desktop-embedding.git @@ -55,6 +55,9 @@ dependencies: path: ^1.8.2 archive: ^3.6.0 flutter_gradient_animation_text: ^1.0.2 + font_awesome_flutter: ^10.7.0 + path_provider: ^2.1.3 + http: ^1.2.1 flutter_icons: windows: diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index 276844c..d56e3f6 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -89,11 +89,11 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN - VALUE "CompanyName", "io.dcvz" "\0" + VALUE "CompanyName", "dev.hm64.retro" "\0" VALUE "FileDescription", "retro" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "retro" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 io.dcvz. All rights reserved." "\0" + VALUE "LegalCopyright", "MIT License" "\0" VALUE "OriginalFilename", "retro.exe" "\0" VALUE "ProductName", "retro" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0"