diff options
author | Orangerot <purple@orangerot.dev> | 2024-12-28 01:29:12 +0100 |
---|---|---|
committer | Orangerot <purple@orangerot.dev> | 2024-12-28 01:29:12 +0100 |
commit | 9eb8a2938271ad9855f3d72cbbf10eb9ba607cf1 (patch) | |
tree | 376073b982898a1117244ed84c95d0e99cbf399b | |
parent | 97598c741de84b48630293928690d8be2be7e6c6 (diff) |
feat: read and parse Simfiles
-rw-r--r-- | lib/level.dart | 22 | ||||
-rw-r--r-- | lib/simfile.dart | 89 |
2 files changed, 108 insertions, 3 deletions
diff --git a/lib/level.dart b/lib/level.dart index 1391f8a..369a162 100644 --- a/lib/level.dart +++ b/lib/level.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; +import 'package:sense_the_rhythm/simfile.dart'; class Level extends StatefulWidget { const Level({super.key, required this.stepmaniaFolderPath}); @@ -14,6 +15,7 @@ class Level extends StatefulWidget { class _LevelState extends State<Level> { final player = AudioPlayer(); + Simfile? simfile; bool _isPlaying = true; Duration? _duration; Duration? _position; @@ -51,10 +53,7 @@ class _LevelState extends State<Level> { _positionSubscription = player.onPositionChanged.listen( (p) => setState(() => _position = p), ); - } - @override - Widget build(BuildContext context) { player.onDurationChanged.listen((Duration d) { // print('Max duration: $d'); setState(() => _duration = d); @@ -65,12 +64,29 @@ class _LevelState extends State<Level> { setState(() => _position = p); }); + String simfilePath = Directory(widget.stepmaniaFolderPath) + .listSync() + .firstWhere((entity) => entity.path.endsWith('.sm'), + orElse: () => File('')) + .path; + String audioPath = Directory(widget.stepmaniaFolderPath) .listSync() .firstWhere((entity) => entity.path.endsWith('.ogg'), orElse: () => File('')) .path; + + simfile = Simfile(simfilePath); + simfile!.load(); + + print(audioPath); + player.play(DeviceFileSource(audioPath)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( appBar: AppBar( leading: IconButton( diff --git a/lib/simfile.dart b/lib/simfile.dart new file mode 100644 index 0000000..20e79e8 --- /dev/null +++ b/lib/simfile.dart @@ -0,0 +1,89 @@ +import 'dart:io'; + +enum Difficulty { Beginner, Easy, Medium, Hard, Challenge, Edit } + +// These are the standard note values: +// +// 0 – No note +// 1 – Normal note +// 2 – Hold head +// 3 – Hold/Roll tail +// 4 – Roll head +// M – Mine (or other negative note) +// +// Later versions of StepMania accept other note values which may not work in older versions: +// +// K – Automatic keysound +// L – Lift note +// F – Fake note + +RegExp noteTypes = RegExp(r'^([012345MKLF]+)\s*([,;])?'); + +class Chart { + String? chartType; + // Description/author + String? author; + // Difficulty (one of Beginner, Easy, Medium, Hard, Challenge, Edit) + Difficulty? difficulty; + // Numerical meter + int? numericalMeter; + // Groove radar values, generated by the program + String? radarValues; + + List<List<String>>? measures; +} + +class Simfile { + String path; + String? lines; + + // tags of simfile + Map<String, String> tags = {}; + + Chart? chartSimplest; + + Simfile(this.path); + + void load() { + lines = File(path).readAsStringSync(); + + RegExp commentsRegExp = RegExp(r'//.*$'); + lines = lines?.replaceAll(commentsRegExp, ''); + RegExp fieldDataRegExp = RegExp(r'#([^;]+):([^;]*);'); + + for (final fieldData in fieldDataRegExp.allMatches(lines!)) { + List<String> keys = + fieldData[1]!.split(':').map((key) => key.trim()).toList(); + String value = fieldData[2]!; + if (keys[0] != "NOTES") { + tags[keys[0]] = value; + continue; + } + + Chart chart = Chart(); + chart.chartType = keys[1]; + chart.author = keys[2]; + chart.difficulty = Difficulty.values.byName(keys[3]); + chart.numericalMeter = int.parse(keys[4]); + chart.radarValues = keys[5]; + + if (chartSimplest == null || + (chart.difficulty!.index <= chartSimplest!.difficulty!.index && + chart.numericalMeter! <= chartSimplest!.numericalMeter!)) { + List<List<String>> measures = []; + for (final measureRaw in value.split(',')) { + List<String> measure = []; + for (final noteRaw in measureRaw.split('\n')) { + String note = noteRaw.trim(); + if (noteTypes.hasMatch(note)) { + measure.add(note); + } + } + measures.add(measure); + } + chart.measures = measures; + chartSimplest = chart; + } + } + } +} |