1use itertools::Itertools;
4use std::{
5 cmp::Reverse,
6 collections::{BTreeMap, HashMap},
7 ops::Bound,
8};
9
10use super::{header::Header, obj::Obj, ParseError, Result};
11use crate::{
12 lex::{
13 command::{self, Channel, Key, NoteKind, ObjId},
14 token::Token,
15 },
16 time::{ObjTime, Track},
17};
18
19#[derive(Debug, Clone, Copy)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct BpmChangeObj {
23 pub time: ObjTime,
25 pub bpm: f64,
27}
28
29impl PartialEq for BpmChangeObj {
30 fn eq(&self, other: &Self) -> bool {
31 self.time == other.time
32 }
33}
34
35impl Eq for BpmChangeObj {}
36
37impl PartialOrd for BpmChangeObj {
38 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
39 Some(self.cmp(other))
40 }
41}
42
43impl Ord for BpmChangeObj {
44 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
45 self.time.cmp(&other.time)
46 }
47}
48
49#[derive(Debug, Clone, Copy)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct SectionLenChangeObj {
53 pub track: Track,
55 pub length: f64,
57}
58
59impl PartialEq for SectionLenChangeObj {
60 fn eq(&self, other: &Self) -> bool {
61 self.track == other.track
62 }
63}
64
65impl Eq for SectionLenChangeObj {}
66
67impl PartialOrd for SectionLenChangeObj {
68 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
69 Some(self.cmp(other))
70 }
71}
72
73impl Ord for SectionLenChangeObj {
74 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
75 self.track.cmp(&other.track)
76 }
77}
78
79#[derive(Debug, Clone, Copy)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct StopObj {
83 pub time: ObjTime,
85 pub duration: u32,
89}
90
91impl PartialEq for StopObj {
92 fn eq(&self, other: &Self) -> bool {
93 self.time == other.time
94 }
95}
96
97impl Eq for StopObj {}
98
99impl PartialOrd for StopObj {
100 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
101 Some(self.cmp(other))
102 }
103}
104
105impl Ord for StopObj {
106 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
107 self.time.cmp(&other.time)
108 }
109}
110
111#[derive(Debug, Clone, Copy)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct BgaObj {
115 pub time: ObjTime,
117 pub id: ObjId,
119 pub layer: BgaLayer,
121}
122
123impl PartialEq for BgaObj {
124 fn eq(&self, other: &Self) -> bool {
125 self.time == other.time
126 }
127}
128
129impl Eq for BgaObj {}
130
131impl PartialOrd for BgaObj {
132 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
133 Some(self.cmp(other))
134 }
135}
136
137impl Ord for BgaObj {
138 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
139 self.time.cmp(&other.time)
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146#[non_exhaustive]
147pub enum BgaLayer {
148 Base,
150 Poor,
152 Overlay,
154}
155
156#[derive(Debug, Clone, Copy)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159pub struct ScrollingFactorObj {
160 pub time: ObjTime,
162 pub factor: f64,
164}
165
166impl PartialEq for ScrollingFactorObj {
167 fn eq(&self, other: &Self) -> bool {
168 self.time == other.time
169 }
170}
171
172impl Eq for ScrollingFactorObj {}
173
174impl PartialOrd for ScrollingFactorObj {
175 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
176 Some(self.cmp(other))
177 }
178}
179
180impl Ord for ScrollingFactorObj {
181 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
182 self.time.cmp(&other.time)
183 }
184}
185
186#[derive(Debug, Clone, Copy)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189pub struct SpacingFactorObj {
190 pub time: ObjTime,
192 pub factor: f64,
194}
195
196impl PartialEq for SpacingFactorObj {
197 fn eq(&self, other: &Self) -> bool {
198 self.time == other.time
199 }
200}
201
202impl Eq for SpacingFactorObj {}
203
204impl PartialOrd for SpacingFactorObj {
205 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
206 Some(self.cmp(other))
207 }
208}
209
210impl Ord for SpacingFactorObj {
211 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
212 self.time.cmp(&other.time)
213 }
214}
215
216#[derive(Debug, Clone)]
218#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
219pub struct ExtendedMessageObj {
220 pub track: Track,
222 pub channel: Channel,
224 pub message: String,
226}
227
228impl PartialEq for ExtendedMessageObj {
229 fn eq(&self, other: &Self) -> bool {
230 self.track == other.track
231 }
232}
233
234impl Eq for ExtendedMessageObj {}
235
236impl PartialOrd for ExtendedMessageObj {
237 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
238 Some(self.cmp(other))
239 }
240}
241
242impl Ord for ExtendedMessageObj {
243 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
244 self.track.cmp(&other.track)
245 }
246}
247
248#[derive(Debug, Default)]
250pub struct Notes {
251 objs: HashMap<ObjId, Vec<Obj>>,
253 bgms: BTreeMap<ObjTime, Vec<ObjId>>,
254 ids_by_key: HashMap<Key, BTreeMap<ObjTime, ObjId>>,
255 bpm_changes: BTreeMap<ObjTime, BpmChangeObj>,
256 section_len_changes: BTreeMap<Track, SectionLenChangeObj>,
257 stops: BTreeMap<ObjTime, StopObj>,
258 bga_changes: BTreeMap<ObjTime, BgaObj>,
259 scrolling_factor_changes: BTreeMap<ObjTime, ScrollingFactorObj>,
260 spacing_factor_changes: BTreeMap<ObjTime, SpacingFactorObj>,
261 extended_messages: Vec<ExtendedMessageObj>,
262}
263
264impl Notes {
265 pub fn new() -> Self {
267 Default::default()
268 }
269
270 pub fn into_all_notes(self) -> Vec<Obj> {
272 self.objs.into_values().flatten().sorted().collect()
273 }
274
275 pub fn all_notes(&self) -> impl Iterator<Item = &Obj> {
277 self.objs.values().flatten().sorted()
278 }
279
280 pub fn bgms(&self) -> &BTreeMap<ObjTime, Vec<ObjId>> {
282 &self.bgms
283 }
284
285 pub fn bpm_changes(&self) -> &BTreeMap<ObjTime, BpmChangeObj> {
287 &self.bpm_changes
288 }
289
290 pub fn section_len_changes(&self) -> &BTreeMap<Track, SectionLenChangeObj> {
292 &self.section_len_changes
293 }
294
295 pub fn stops(&self) -> &BTreeMap<ObjTime, StopObj> {
297 &self.stops
298 }
299
300 pub fn bga_changes(&self) -> &BTreeMap<ObjTime, BgaObj> {
302 &self.bga_changes
303 }
304
305 pub fn next_obj_by_key(&self, key: Key, time: ObjTime) -> Option<&Obj> {
307 self.ids_by_key
308 .get(&key)?
309 .range((Bound::Excluded(time), Bound::Unbounded))
310 .next()
311 .and_then(|(_, id)| {
312 let objs = self.objs.get(id)?;
313 let idx = objs
314 .binary_search_by(|probe| probe.offset.cmp(&time))
315 .unwrap_or_else(|idx| idx);
316 objs.get(idx)
317 })
318 }
319
320 pub fn push_note(&mut self, note: Obj) {
322 self.objs.entry(note.obj).or_default().push(note.clone());
323 self.ids_by_key
324 .entry(note.key)
325 .or_default()
326 .insert(note.offset, note.obj);
327 }
328
329 pub fn remove_latest_note(&mut self, id: ObjId) -> Option<Obj> {
331 self.objs.entry(id).or_default().pop().inspect(|removed| {
332 self.ids_by_key
333 .get_mut(&removed.key)
334 .unwrap()
335 .remove(&removed.offset)
336 .unwrap();
337 })
338 }
339
340 pub fn remove_note(&mut self, id: ObjId) -> Vec<Obj> {
342 self.objs.remove(&id).map_or(vec![], |removed| {
343 for item in &removed {
344 self.ids_by_key
345 .get_mut(&item.key)
346 .unwrap()
347 .remove(&item.offset)
348 .unwrap();
349 }
350 removed
351 })
352 }
353
354 pub fn push_bpm_change(&mut self, bpm_change: BpmChangeObj) {
356 if self
357 .bpm_changes
358 .insert(bpm_change.time, bpm_change)
359 .is_some()
360 {
361 eprintln!(
362 "duplicate bpm change object detected at {:?}",
363 bpm_change.time
364 );
365 }
366 }
367
368 pub fn push_scrolling_factor_change(&mut self, bpm_change: ScrollingFactorObj) {
370 if self
371 .scrolling_factor_changes
372 .insert(bpm_change.time, bpm_change)
373 .is_some()
374 {
375 eprintln!(
376 "duplicate scrolling factor change object detected at {:?}",
377 bpm_change.time
378 );
379 }
380 }
381
382 pub fn push_spacing_factor_change(&mut self, bpm_change: SpacingFactorObj) {
384 if self
385 .spacing_factor_changes
386 .insert(bpm_change.time, bpm_change)
387 .is_some()
388 {
389 eprintln!(
390 "duplicate spacing factor change object detected at {:?}",
391 bpm_change.time
392 );
393 }
394 }
395
396 pub fn push_section_len_change(&mut self, section_len_change: SectionLenChangeObj) {
398 if self
399 .section_len_changes
400 .insert(section_len_change.track, section_len_change)
401 .is_some()
402 {
403 eprintln!(
404 "duplicate section length change object detected at {:?}",
405 section_len_change.track
406 );
407 }
408 }
409
410 pub fn push_stop(&mut self, stop: StopObj) {
412 self.stops
413 .entry(stop.time)
414 .and_modify(|existing| {
415 existing.duration = existing.duration.saturating_add(stop.duration)
416 })
417 .or_insert(stop);
418 }
419
420 pub fn push_bga_change(&mut self, bga: BgaObj) {
422 if self.bga_changes.insert(bga.time, bga).is_some() {
423 eprintln!("duplicate bga change object detected at {:?}", bga.time);
424 }
425 }
426
427 pub fn push_extended_message(&mut self, message: ExtendedMessageObj) {
429 self.extended_messages.push(message);
430 }
431
432 pub(crate) fn parse(&mut self, token: &Token, header: &Header) -> Result<()> {
433 match token {
434 Token::Message {
435 track,
436 channel: Channel::BpmChange,
437 message,
438 } => {
439 for (time, obj) in ids_from_message(*track, message) {
440 let &bpm = header
441 .bpm_changes
442 .get(&obj)
443 .ok_or(ParseError::UndefinedObject(obj))?;
444 self.push_bpm_change(BpmChangeObj { time, bpm });
445 }
446 }
447 Token::Message {
448 track,
449 channel: Channel::BpmChangeU8,
450 message,
451 } => {
452 let denominator = message.len() as u32 / 2;
453 for (i, (c1, c2)) in message.chars().tuples().enumerate() {
454 let bpm = c1.to_digit(16).unwrap() * 16 + c2.to_digit(16).unwrap();
455 if bpm == 0 {
456 continue;
457 }
458 let time = ObjTime::new(track.0, i as u32, denominator);
459 self.push_bpm_change(BpmChangeObj {
460 time,
461 bpm: bpm as f64,
462 });
463 }
464 }
465 Token::Message {
466 track,
467 channel: Channel::Scroll,
468 message,
469 } => {
470 for (time, obj) in ids_from_message(*track, message) {
471 let &factor = header
472 .scrolling_factor_changes
473 .get(&obj)
474 .ok_or(ParseError::UndefinedObject(obj))?;
475 self.push_scrolling_factor_change(ScrollingFactorObj { time, factor });
476 }
477 }
478 Token::Message {
479 track,
480 channel: Channel::Speed,
481 message,
482 } => {
483 for (time, obj) in ids_from_message(*track, message) {
484 let &factor = header
485 .spacing_factor_changes
486 .get(&obj)
487 .ok_or(ParseError::UndefinedObject(obj))?;
488 self.push_spacing_factor_change(SpacingFactorObj { time, factor });
489 }
490 }
491 Token::Message {
492 track,
493 channel: Channel::SectionLen,
494 message,
495 } => {
496 let track = Track(track.0);
497 let length = message.parse().expect("f64 as section length");
498 assert!(0.0 < length, "section length must be greater than zero");
499 self.push_section_len_change(SectionLenChangeObj { track, length });
500 }
501 Token::Message {
502 track,
503 channel: Channel::Stop,
504 message,
505 } => {
506 for (time, obj) in ids_from_message(*track, message) {
507 let &duration = header
508 .stops
509 .get(&obj)
510 .ok_or(ParseError::UndefinedObject(obj))?;
511 self.push_stop(StopObj { time, duration })
512 }
513 }
514 Token::Message {
515 track,
516 channel: channel @ (Channel::BgaBase | Channel::BgaPoor | Channel::BgaLayer),
517 message,
518 } => {
519 for (time, obj) in ids_from_message(*track, message) {
520 if !header.bmp_files.contains_key(&obj) {
521 return Err(ParseError::UndefinedObject(obj));
522 }
523 let layer = match channel {
524 Channel::BgaBase => BgaLayer::Base,
525 Channel::BgaPoor => BgaLayer::Poor,
526 Channel::BgaLayer => BgaLayer::Overlay,
527 _ => unreachable!(),
528 };
529 self.push_bga_change(BgaObj {
530 time,
531 id: obj,
532 layer,
533 });
534 }
535 }
536 Token::Message {
537 track,
538 channel: Channel::Bgm,
539 message,
540 } => {
541 for (time, obj) in ids_from_message(*track, message) {
542 self.bgms.entry(time).or_default().push(obj)
543 }
544 }
545 Token::Message {
546 track,
547 channel:
548 Channel::Note {
549 kind,
550 is_player1,
551 key,
552 },
553 message,
554 } => {
555 for (offset, obj) in ids_from_message(*track, message) {
556 self.push_note(Obj {
557 offset,
558 kind: *kind,
559 is_player1: *is_player1,
560 key: *key,
561 obj,
562 });
563 }
564 }
565 Token::ExtendedMessage {
566 track,
567 channel,
568 message,
569 } => {
570 let track = Track(track.0);
571 self.push_extended_message(ExtendedMessageObj {
572 track,
573 channel: channel.clone(),
574 message: (*message).to_owned(),
575 });
576 }
577 &Token::LnObj(end_id) => {
578 let mut end_note = self
579 .remove_latest_note(end_id)
580 .ok_or(ParseError::UndefinedObject(end_id))?;
581 let Obj { offset, key, .. } = &end_note;
582 let (_, &begin_id) =
583 self.ids_by_key[key].range(..offset).last().ok_or_else(|| {
584 ParseError::SyntaxError(format!(
585 "expected preceding object for #LNOBJ {:?}",
586 end_id
587 ))
588 })?;
589 let mut begin_note = self.remove_latest_note(begin_id).unwrap();
590 begin_note.kind = NoteKind::Long;
591 end_note.kind = NoteKind::Long;
592 self.push_note(begin_note);
593 self.push_note(end_note);
594 }
595 _ => {}
596 }
597 Ok(())
598 }
599
600 pub fn last_visible_time(&self) -> Option<ObjTime> {
602 self.objs
603 .values()
604 .flatten()
605 .filter(|obj| !matches!(obj.kind, NoteKind::Invisible))
606 .map(Reverse)
607 .sorted()
608 .next()
609 .map(|Reverse(obj)| obj.offset)
610 }
611
612 pub fn last_bgm_time(&self) -> Option<ObjTime> {
616 self.bgms.last_key_value().map(|(time, _)| time).cloned()
617 }
618
619 pub fn last_obj_time(&self) -> Option<ObjTime> {
623 let obj_last = self
624 .objs
625 .values()
626 .flatten()
627 .map(Reverse)
628 .sorted()
629 .next()
630 .map(|Reverse(obj)| obj.offset);
631 let bpm_last = self.bpm_changes.last_key_value().map(|(&time, _)| time);
632 let section_len_last =
633 self.section_len_changes
634 .last_key_value()
635 .map(|(&time, _)| ObjTime {
636 track: time,
637 numerator: 0,
638 denominator: 4,
639 });
640 let stop_last = self.stops.last_key_value().map(|(&time, _)| time);
641 let bga_last = self.bga_changes.last_key_value().map(|(&time, _)| time);
642 [obj_last, bpm_last, section_len_last, stop_last, bga_last]
643 .into_iter()
644 .max()
645 .flatten()
646 }
647
648 pub fn resolution_for_pulses(&self) -> u32 {
650 use num::Integer;
651
652 let mut hyp_resolution = 1;
653 for obj in self.objs.values().flatten() {
654 hyp_resolution = hyp_resolution.lcm(&obj.offset.denominator);
655 }
656 for bpm_change in self.bpm_changes.values() {
657 hyp_resolution = hyp_resolution.lcm(&bpm_change.time.denominator);
658 }
659 hyp_resolution
660 }
661}
662
663fn ids_from_message(
664 track: command::Track,
665 message: &'_ str,
666) -> impl Iterator<Item = (ObjTime, ObjId)> + '_ {
667 let denominator = message.len() as u32 / 2;
668 let mut chars = message.chars().tuples().enumerate();
669 std::iter::from_fn(move || {
670 let (i, c1, c2) = loop {
671 let (i, (c1, c2)) = chars.next()?;
672 if !(c1 == '0' && c2 == '0') {
673 break (i, c1, c2);
674 }
675 };
676 let obj = ObjId::from_chars([c1, c2]).expect("invalid object id");
677 let time = ObjTime::new(track.0, i as u32, denominator);
678 Some((time, obj))
679 })
680}
681
682#[cfg(feature = "serde")]
683#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
684pub struct NotesPack {
686 pub objs: Vec<Obj>,
688 pub bpm_changes: Vec<BpmChangeObj>,
690 pub section_len_changes: Vec<SectionLenChangeObj>,
692 pub stops: Vec<StopObj>,
694 pub bga_changes: Vec<BgaObj>,
696 pub scrolling_factor_changes: Vec<ScrollingFactorObj>,
698 pub spacing_factor_changes: Vec<SpacingFactorObj>,
700 pub extended_messages: Vec<ExtendedMessageObj>,
702}
703
704#[cfg(feature = "serde")]
705impl serde::Serialize for Notes {
706 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
707 where
708 S: serde::Serializer,
709 {
710 NotesPack {
711 objs: self.all_notes().cloned().collect(),
712 bpm_changes: self.bpm_changes.values().cloned().collect(),
713 section_len_changes: self.section_len_changes.values().cloned().collect(),
714 stops: self.stops.values().cloned().collect(),
715 bga_changes: self.bga_changes.values().cloned().collect(),
716 scrolling_factor_changes: self.scrolling_factor_changes.values().cloned().collect(),
717 spacing_factor_changes: self.spacing_factor_changes.values().cloned().collect(),
718 extended_messages: self.extended_messages.clone(),
719 }
720 .serialize(serializer)
721 }
722}
723
724#[cfg(feature = "serde")]
725impl<'de> serde::Deserialize<'de> for Notes {
726 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
727 where
728 D: serde::Deserializer<'de>,
729 {
730 let pack = NotesPack::deserialize(deserializer)?;
731 let mut objs = HashMap::<ObjId, Vec<Obj>>::new();
732 let mut bgms: BTreeMap<ObjTime, Vec<ObjId>> = BTreeMap::new();
733 let mut ids_by_key: HashMap<Key, BTreeMap<ObjTime, ObjId>> = HashMap::new();
734 for obj in pack.objs {
735 if matches!(obj.kind, NoteKind::Invisible) {
736 bgms.entry(obj.offset)
737 .and_modify(|ids| ids.push(obj.obj))
738 .or_default();
739 }
740 ids_by_key
741 .entry(obj.key)
742 .and_modify(|id_map| {
743 id_map.insert(obj.offset, obj.obj);
744 })
745 .or_default();
746 objs.entry(obj.obj).or_default().push(obj);
747 }
748 let mut bpm_changes = BTreeMap::new();
749 for bpm_change in pack.bpm_changes {
750 bpm_changes.insert(bpm_change.time, bpm_change);
751 }
752 let mut section_len_changes = BTreeMap::new();
753 for section_len_change in pack.section_len_changes {
754 section_len_changes.insert(section_len_change.track, section_len_change);
755 }
756 let mut stops = BTreeMap::new();
757 for stop in pack.stops {
758 stops.insert(stop.time, stop);
759 }
760 let mut bga_changes = BTreeMap::new();
761 for bga_change in pack.bga_changes {
762 bga_changes.insert(bga_change.time, bga_change);
763 }
764 let mut scrolling_factor_changes = BTreeMap::new();
765 for scrolling_change in pack.scrolling_factor_changes {
766 scrolling_factor_changes.insert(scrolling_change.time, scrolling_change);
767 }
768 let mut spacing_factor_changes = BTreeMap::new();
769 for spacing_change in pack.spacing_factor_changes {
770 spacing_factor_changes.insert(spacing_change.time, spacing_change);
771 }
772 let mut extended_messages = vec![];
773 for extended_message in pack.extended_messages {
774 extended_messages.push(extended_message);
775 }
776 Ok(Notes {
777 objs,
778 bgms,
779 ids_by_key,
780 bpm_changes,
781 section_len_changes,
782 stops,
783 bga_changes,
784 scrolling_factor_changes,
785 spacing_factor_changes,
786 extended_messages,
787 })
788 }
789}