rosu_map/section/events/
decode.rs1use crate::{
2 decode::{DecodeBeatmap, DecodeState},
3 util::{ParseNumber, ParseNumberError, StrExt},
4 Beatmap,
5};
6
7use super::{BreakPeriod, EventType, ParseEventTypeError};
8
9#[derive(Clone, Debug, Default, PartialEq)]
11pub struct Events {
12 pub background_file: String,
13 pub breaks: Vec<BreakPeriod>,
14}
15
16impl From<Events> for Beatmap {
17 fn from(events: Events) -> Self {
18 Self {
19 background_file: events.background_file,
20 breaks: events.breaks,
21 ..Self::default()
22 }
23 }
24}
25
26thiserror! {
27 #[derive(Debug)]
29 pub enum ParseEventsError {
30 #[error("failed to parse event type")]
31 EventType(#[from] ParseEventTypeError),
32 #[error("invalid line")]
33 InvalidLine,
34 #[error("failed to parse number")]
35 Number(#[from] ParseNumberError),
36 }
37}
38
39pub type EventsState = Events;
41
42impl DecodeState for EventsState {
43 fn create(_: i32) -> Self {
44 Self::default()
45 }
46}
47
48impl DecodeBeatmap for Events {
49 type Error = ParseEventsError;
50 type State = EventsState;
51
52 fn parse_general(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
53 Ok(())
54 }
55
56 fn parse_editor(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
57 Ok(())
58 }
59
60 fn parse_metadata(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
61 Ok(())
62 }
63
64 fn parse_difficulty(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
65 Ok(())
66 }
67
68 fn parse_events(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
69 let mut split = line.trim_comment().split(',');
70
71 let (Some(event_type), Some(start_time), Some(event_params)) =
72 (split.next(), split.next(), split.next())
73 else {
74 return Err(ParseEventsError::InvalidLine);
75 };
76
77 match event_type.parse()? {
78 EventType::Sprite => {
79 if state.background_file.is_empty() {
80 state.background_file = split
81 .next()
82 .ok_or(ParseEventsError::InvalidLine)?
83 .clean_filename();
84 }
85 }
86 EventType::Video => {
87 const VIDEO_EXTENSIONS: &[[u8; 3]] = &[
88 *b"mp4", *b"mov", *b"avi", *b"flv", *b"mpg", *b"wmv", *b"m4v",
89 ];
90
91 let filename = event_params.clean_filename();
92
93 if let [.., a, b, c] = filename.as_bytes() {
94 let extension = [
95 a.to_ascii_lowercase(),
96 b.to_ascii_lowercase(),
97 c.to_ascii_lowercase(),
98 ];
99
100 if !VIDEO_EXTENSIONS.contains(&extension) {
101 state.background_file = filename;
102 }
103 }
104 }
105 EventType::Background => state.background_file = event_params.clean_filename(),
106 EventType::Break => {
107 let start_time = f64::parse(start_time)?;
108 let end_time = start_time.max(f64::parse(event_params)?);
109
110 state.breaks.push(BreakPeriod {
111 start_time,
112 end_time,
113 });
114 }
115 EventType::Color | EventType::Sample | EventType::Animation => {}
116 }
117
118 Ok(())
119 }
120
121 fn parse_timing_points(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
122 Ok(())
123 }
124
125 fn parse_colors(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
126 Ok(())
127 }
128
129 fn parse_hit_objects(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
130 Ok(())
131 }
132
133 fn parse_variables(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
134 Ok(())
135 }
136
137 fn parse_catch_the_beat(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
138 Ok(())
139 }
140
141 fn parse_mania(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
142 Ok(())
143 }
144}