protected IEnumerable <(int Tick, string Data)> SplitData(BarIndexCalculator c, int lineIndex, int barIndex, string data) { data = Regex.Replace(data, @"\s+", ""); if (data.Length % 2 != 0) { DiagnosticCollector.ReportWarning($"データ定義の文字列長が2の倍数ではありません。終端部分は無視されます。(行: {lineIndex + 1})"); } int headTick = c.GetTickFromBarIndex(barIndex); int barTick = (int)(c.GetBarBeatsFromBarIndex(barIndex) * TicksPerBeat); var list = Enumerable.Range(0, data.Length / 2).Select(p => data.Substring(p * 2, 2)).ToList(); return(list.Select((p, i) => (headTick + (int)((double)barTick * i / list.Count), p))); }
public SusScoreData Parse(TextReader reader) { var shortNotesData = new List <LineData <Match> >(); var longNotesData = new List <LineData <Match> >(); var bpmData = new List <LineData <Match> >(); bool matchAction(Match m, Action <Match> act) { if (m.Success) { act(m); } return(m.Success); } int lineIndex = -1; while (reader.Peek() >= 0) { string line = reader.ReadLine(); lineIndex++; if (matchAction(SusCommandPattern.Match(line), m => ProcessCommand(lineIndex, m.Groups["name"].Value, m.Groups["value"].Value))) { continue; } if (matchAction(BpmDefinitionPattern.Match(line), m => StoreBpmDefinition(lineIndex, m.Groups["key"].Value, double.Parse(m.Groups["value"].Value)))) { continue; } if (matchAction(BpmCommandPattern.Match(line), m => bpmData.Add(new LineData <Match>(lineIndex, CurrentBarIndexOffset, m)))) { continue; } if (matchAction(SusNotePattern.Match(line), m => (m.Groups["longKey"].Success ? longNotesData : shortNotesData).Add(new LineData <Match>(lineIndex, CurrentBarIndexOffset, m)))) { continue; } if (matchAction(TimeSignatureCommandPattern.Match(line), m => StoreTimeSignatureDefinition(lineIndex, int.Parse(m.Groups["barIndex"].Value) + CurrentBarIndexOffset, double.Parse(m.Groups["value"].Value)))) { continue; } } if (!TimeSignatureDefinitions.ContainsKey(0)) { TimeSignatureDefinitions.Add(0, 4.0); DiagnosticCollector.ReportInformation("初期拍子定義が存在しないため、4/4拍子に設定します。"); } var barIndexCalculator = new BarIndexCalculator(TicksPerBeat, TimeSignatureDefinitions); var bpmDic = bpmData.SelectMany(p => SplitData(barIndexCalculator, p.LineIndex, int.Parse(p.Value.Groups["barIndex"].Value) + p.BarIndexOffset, p.Value.Groups["data"].Value).Select(q => new { LineIndex = p.LineIndex, Definition = q })) .Where(p => { if (p.Definition.Data == "00") { return(false); } if (!BpmDefinitions.ContainsKey(p.Definition.Data.ToLower())) { DiagnosticCollector.ReportWarning($"BPM定義番号{p.Definition.Data}は宣言されていません。このBPM変更は無視されます。(行: {p.LineIndex + 1})"); return(false); } return(true); }) .ToDictionary(p => p.Definition.Tick, p => BpmDefinitions[p.Definition.Data.ToLower()]); if (!bpmDic.ContainsKey(0)) { bpmDic.Add(0, 120); DiagnosticCollector.ReportInformation("初期BPM定義が存在しないため、120に設定します。"); } // データ種別と位置 var shortNotes = shortNotesData.GroupBy(p => p.Value.Groups["type"].Value[0]).ToDictionary(p => p.Key, p => p.SelectMany(q => { return(SplitData(barIndexCalculator, q.LineIndex, int.Parse(q.Value.Groups["barIndex"].Value) + q.BarIndexOffset, q.Value.Groups["data"].Value) .Where(r => Regex.IsMatch(r.Data, "[1-9][1-9a-g]", RegexOptions.IgnoreCase)) .Select(r => new NoteDefinition() { LineIndex = q.LineIndex, Type = r.Data[0], Position = new NotePosition() { Tick = r.Tick, LaneIndex = ConvertHex(q.Value.Groups["laneIndex"].Value[0]), Width = ConvertHex(r.Data[1]) } })); }).ToList()); // ロング種別 -> ロングノーツリスト -> 構成点リスト var longNotes = longNotesData.GroupBy(p => p.Value.Groups["type"].Value[0]).ToDictionary(p => p.Key, p => { return(p.GroupBy(q => q.Value.Groups["longKey"].Value.ToUpper()).Select(q => q.SelectMany(r => { return SplitData(barIndexCalculator, r.LineIndex, int.Parse(r.Value.Groups["barIndex"].Value) + r.BarIndexOffset, r.Value.Groups["data"].Value) .Where(s => Regex.IsMatch(s.Data, "[1-5][1-9a-g]", RegexOptions.IgnoreCase)) .Select(s => new NoteDefinition() { LineIndex = r.LineIndex, Type = s.Data[0], Position = new NotePosition() { Tick = s.Tick, LaneIndex = ConvertHex(r.Value.Groups["laneIndex"].Value[0]), Width = ConvertHex(s.Data[1]) } }); })) .SelectMany(q => p.Key == '2' ? q.GroupBy(r => r.Position.LaneIndex).SelectMany(r => FlatSplitLongNotes(r, '1', '2')) : FlatSplitLongNotes(q, '1', '2')) .ToList()); }); foreach (char c in new[] { '1', '5' }) { FillKey(shortNotes, c, new List <NoteDefinition>()); } foreach (char c in new[] { '2', '3', '4', }) { FillKey(longNotes, c, new List <List <NoteDefinition> >()); } return(new SusScoreData() { TicksPerBeat = this.TicksPerBeat, BpmDefinitions = bpmDic, TimeSignatures = TimeSignatureDefinitions.ToDictionary(p => barIndexCalculator.GetTickFromBarIndex(p.Key), p => p.Value), Title = this.Title, ArtistName = this.ArtistName, DesignerName = this.DesignerName, ShortNotes = shortNotes, LongNotes = longNotes }); }