rosu_map/section/events/
decode.rs

1use crate::{
2    decode::{DecodeBeatmap, DecodeState},
3    util::{ParseNumber, ParseNumberError, StrExt},
4    Beatmap,
5};
6
7use super::{BreakPeriod, EventType, ParseEventTypeError};
8
9/// Struct containing all data from a `.osu` file's `[Events]` section.
10#[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    /// All the ways that parsing a `.osu` file into [`Events`] can fail.
28    #[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
39/// The parsing state for [`Events`] in [`DecodeBeatmap`].
40pub 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}