diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/screens/level.dart | 6 | ||||
| -rw-r--r-- | lib/screens/level_selection.dart | 28 | ||||
| -rw-r--r-- | lib/utils/simfile.dart | 61 | ||||
| -rw-r--r-- | lib/widgets/arrows.dart | 3 | ||||
| -rw-r--r-- | lib/widgets/level_info_chip.dart | 37 | ||||
| -rw-r--r-- | lib/widgets/level_list_entry.dart | 24 | 
6 files changed, 115 insertions, 44 deletions
| diff --git a/lib/screens/level.dart b/lib/screens/level.dart index e2b2195..9c5823b 100644 --- a/lib/screens/level.dart +++ b/lib/screens/level.dart @@ -244,11 +244,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {                  )),            ),            body: Stack(children: [ -            Arrows( -                notes: notes, -                position: _position != null -                    ? _position!.inMilliseconds.toDouble() -                    : 0.0), +            Arrows(notes: notes),              Positioned(                top: 50,                width: MediaQuery.of(context).size.width, diff --git a/lib/screens/level_selection.dart b/lib/screens/level_selection.dart index 41eb4f7..7241ad7 100644 --- a/lib/screens/level_selection.dart +++ b/lib/screens/level_selection.dart @@ -62,16 +62,24 @@ class _LevelSelectionState extends State<LevelSelection> {      try {        // List all files and folders in the directory        List<Simfile> simfiles = directory -          .listSync() -          .where((entity) => FileSystemEntity.isDirectorySync(entity.path)) -          .map((entity) { -        Simfile simfile = Simfile(entity.path); -        simfile.load(); -        return simfile; -      }).toList(); -      simfiles.sort((a, b) => a.tags['TITLE']!.compareTo(b.tags['TITLE']!)); - -      return simfiles; +          .listSync(recursive: true) +          .where((entity) => entity.path.endsWith('.sm')) +          .map((entity) => Simfile(entity.path)) +          .toList(); + +      List<bool> successfullLoads = +          await Future.wait(simfiles.map((simfile) => simfile.load())); +      List<Simfile> simfilesFiltered = []; +      for (int i = 0; i < simfiles.length; i++) { +        if (successfullLoads[i]) { +          simfilesFiltered.add(simfiles[i]); +        } +      } + +      simfilesFiltered +          .sort((a, b) => a.tags['TITLE']!.compareTo(b.tags['TITLE']!)); + +      return simfilesFiltered;      } catch (e) {        print("Error reading directory: $e");        return []; diff --git a/lib/utils/simfile.dart b/lib/utils/simfile.dart index 0af734f..c528084 100644 --- a/lib/utils/simfile.dart +++ b/lib/utils/simfile.dart @@ -1,5 +1,7 @@  import 'dart:io'; +import 'package:audioplayers/audioplayers.dart'; +  enum Difficulty { Beginner, Easy, Medium, Hard, Challenge, Edit }  // These are the standard note values: @@ -36,12 +38,13 @@ class Chart {  }  class Simfile { -  String directoryPath; -  String? simfilePath; +  String? directoryPath; +  String simfilePath;    String? audioPath;    String? bannerPath;    String? lines; +  Duration? duration;    // tags of simfile    Map<String, String> tags = {}; @@ -50,7 +53,7 @@ class Simfile {    Map<double, double> bpms = {};    double offset = 0; -  Simfile(this.directoryPath); +  Simfile(this.simfilePath);    void _parseChart({required List<String> keys, required String value}) {      Chart chart = Chart(); @@ -119,33 +122,41 @@ class Simfile {      _parseChart(keys: keys, value: value);    } -  void load() { -    simfilePath = Directory(directoryPath) -        .listSync() -        .firstWhere((entity) => entity.path.endsWith('.sm'), -            orElse: () => File('')) -        .path; - -    audioPath = Directory(directoryPath) -        .listSync() -        .firstWhere((entity) => entity.path.endsWith('.ogg'), -            orElse: () => File('')) -        .path; - -    bannerPath = Directory(directoryPath) -        .listSync() -        .firstWhere((file) => file.path.toLowerCase().endsWith('banner.png'), -            orElse: () => File('')) -        .path; - -    lines = File(simfilePath!).readAsStringSync(); - +  Future<bool> load() async { +    directoryPath = File(simfilePath).parent.path; +    lines = File(simfilePath).readAsStringSync(); +          RegExp commentsRegExp = RegExp(r'//.*$');      lines = lines?.replaceAll(commentsRegExp, '');      RegExp fieldDataRegExp = RegExp(r'#([^;]+):([^;]*);');      for (final fieldData in fieldDataRegExp.allMatches(lines!)) { -      _parseTag(fieldData); +      try { +        _parseTag(fieldData); +      } catch (err) { +        return false; +      }      } + +    String? musicFileName = tags["MUSIC"]; +    if (musicFileName == null) return false; +    String? bannerFileName = tags["BANNER"]; +    if (bannerFileName == null) return false; + +    for (FileSystemEntity entity in Directory(directoryPath!).listSync()) { +      if (entity.path.endsWith('.ogg')) { +        audioPath = entity.path; +      } +      if (entity.path.endsWith('anner.png')) { +        bannerPath = entity.path; +      } +    } + +    AudioPlayer audioplayer = AudioPlayer(); +    await audioplayer.setSource(DeviceFileSource(audioPath!)); +    duration = await audioplayer.getDuration(); +    audioplayer.dispose(); + +    return true;    }  } diff --git a/lib/widgets/arrows.dart b/lib/widgets/arrows.dart index cf04e59..162f0f3 100644 --- a/lib/widgets/arrows.dart +++ b/lib/widgets/arrows.dart @@ -4,9 +4,8 @@ import 'package:sense_the_rhythm/widgets/arrow.dart';  class Arrows extends StatelessWidget {    final List<Note> notes; -  final double position; -  const Arrows({super.key, required this.notes, required this.position}); +  const Arrows({super.key, required this.notes});    @override    Widget build(BuildContext context) { diff --git a/lib/widgets/level_info_chip.dart b/lib/widgets/level_info_chip.dart new file mode 100644 index 0000000..8e4146c --- /dev/null +++ b/lib/widgets/level_info_chip.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +class LevelInfoChip extends StatelessWidget { +  final String label; +  final IconData icon; + +  const LevelInfoChip({super.key, required this.label, required this.icon}); + +  @override +  Widget build(BuildContext context) { +    return OutlinedButton( +      style: ButtonStyle( +          shape: WidgetStateProperty.all(RoundedRectangleBorder( +              borderRadius: BorderRadius.all(Radius.circular(5)))), +          minimumSize: WidgetStateProperty.all(Size(10, 10)), +          tapTargetSize: MaterialTapTargetSize.shrinkWrap, +          padding: WidgetStateProperty.all( +              EdgeInsets.symmetric(vertical: 4.0, horizontal: 5.0)) +          ), +      onPressed: () {}, +      child: Row(children: [ +        Icon( +          icon, +          size: 16, +        ), +        SizedBox(width: 4), +        Text( +          label, +          style: TextStyle( +              fontSize: 14, +              fontWeight: +                  FontWeight.w200), // Adjust font size for smaller appearance +        ), +      ]), +    ); +  } +} diff --git a/lib/widgets/level_list_entry.dart b/lib/widgets/level_list_entry.dart index 832186f..abb4784 100644 --- a/lib/widgets/level_list_entry.dart +++ b/lib/widgets/level_list_entry.dart @@ -6,6 +6,7 @@ import 'package:sense_the_rhythm/utils/simfile.dart';  import 'package:sense_the_rhythm/screens/level.dart';  import 'package:sense_the_rhythm/widgets/esense_connect_dialog.dart';  import 'package:sense_the_rhythm/widgets/esense_not_connected_dialog.dart'; +import 'package:sense_the_rhythm/widgets/level_info_chip.dart';  class LevelListEntry extends StatelessWidget {    const LevelListEntry({ @@ -62,8 +63,27 @@ class LevelListEntry extends StatelessWidget {      return ListTile(        leading: Image.file(File(simfile.bannerPath!)),        trailing: Icon(Icons.play_arrow), -      title: Text(simfile.tags["TITLE"]!), -      subtitle: Text('3:45'), +      title: Text( +        simfile.tags["TITLE"]!, +        style: TextStyle(fontWeight: FontWeight.bold), +      ), +      subtitle: Padding( +        padding: const EdgeInsets.only(bottom: 2), +        child: Row( +          spacing: 2, +          children: [ +            LevelInfoChip( +              label: +                  '${simfile.duration!.inMinutes}:${simfile.duration!.inSeconds.remainder(60).toString().padLeft(2, "0")}', +              icon: Icons.timer_outlined, +            ), +            LevelInfoChip( +              label: '${simfile.bpms.entries.first.value.toInt()} BPM', +              icon: Icons.graphic_eq, +            ), +          ], +        ), +      ),        onTap: () {          tapHandler(context);        }, | 
