osu_file_parser/osu_file/general/
mod.rs1pub mod error;
2pub mod types;
3
4use std::fmt::Debug;
5use std::num::{IntErrorKind, ParseIntError};
6use std::path::PathBuf;
7
8use crate::osu_file::types::Decimal;
9use crate::parsers::comma;
10use nom::bytes::complete::take_till;
11use nom::combinator::map_res;
12use nom::multi::separated_list0;
13use nom::Finish;
14use rust_decimal_macros::dec;
15
16use crate::helper;
17use crate::helper::macros::*;
18
19use crate::osu_file::Integer;
20
21pub use error::*;
22pub use types::*;
23
24versioned_field!(AudioFilename, PathBuf, no_versions, |s| { Ok(PathBuf::from(s)) } -> (), |v| { v.display().to_string() }, PathBuf::from(""));
25versioned_field!(AudioLeadIn, Integer, no_versions, |s| { s.parse() } -> ParseIntError,, 0);
26versioned_field!(AudioHash, String, no_versions, |s| { Ok(s.to_string()) } -> (),
27 |v, version| { if version > 13 { None } else { Some(v.to_string()) } },
28 |version| { if version > 13 { None } else { Some(String::new())}
29});
30versioned_field!(PreviewTime, Integer, no_versions, |s| { s.parse() } -> ParseIntError,, -1);
31versioned_field!(StackLeniency, Decimal, no_versions, |s| { s.parse() } -> (),, Decimal::from(dec!(0.7)));
32versioned_field!(LetterboxInBreaks, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
33versioned_field!(StoryFireInFront, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, true);
34versioned_field!(UseSkinSprites, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
35versioned_field!(AlwaysShowPlayfield, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
36versioned_field!(SkinPreference, String, no_versions, |s| { Ok(s.to_string()) } -> (),, String::new());
37versioned_field!(EpilepsyWarning, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
38versioned_field!(CountdownOffset, Integer, no_versions, |s| { s.parse() } -> ParseIntError,, 0);
39versioned_field!(SpecialStyle, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
40versioned_field!(WidescreenStoryboard, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
41versioned_field!(SamplesMatchPlaybackRate, bool, no_versions, |s| { helper::parse_zero_one_bool(s) } -> helper::ParseZeroOneBoolError, boolean, false);
42versioned_field!(EditorBookmarks, Vec<Integer>, no_versions, |s| {
43 let bookmark = map_res(take_till(|c| c == ','), |s: &str| s.parse::<Integer>());
44 let mut bookmarks = separated_list0(comma::<nom::error::Error<_>>(), bookmark);
45 let input_len = s.len();
46
47 let (s, bookmarks) = bookmarks(s).finish().unwrap();
48
49 if s.is_empty() {
50 Ok(bookmarks)
51 } else {
52 let (_, s) = {
53 let s = if s.len() < input_len {
54 match s.strip_prefix(',') {
55 Some(s) => s,
56 None => s,
57 }
58 } else {
59 s
60 };
61
62 take_till::<_, _, nom::error::Error<_>>(|c| c == ',')(s).unwrap()
63 };
64
65 let err = s.parse::<Integer>().unwrap_err();
67
68 let err = if let IntErrorKind::Empty = err.kind() {
69 ParseError::InvalidCommaList
70 } else {
71 ParseError::ParseIntError(err)
72 };
73
74 Err(err)
75 }
76} -> ParseError,
77|v, version| {
78 if version > 5 {
79 return None;
80 }
81
82 Some(v.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(","))
83},
84);
85versioned_field!(EditorDistanceSpacing, Decimal, no_versions, |s| { s.parse() } -> (),
86|v, version| {
87 if version > 5 {
88 return None;
89 }
90
91 Some(v.to_string())
92}
93,);
94
95general_section!(
96 pub struct General {
98 pub audio_filename: AudioFilename,
100 pub audio_lead_in: AudioLeadIn,
102 pub audio_hash: AudioHash,
104 pub preview_time: PreviewTime,
107 pub countdown: Countdown,
110 pub sample_set: SampleSet,
113 pub stack_leniency: StackLeniency,
116 pub mode: Mode,
119 pub letterbox_in_breaks: LetterboxInBreaks,
122 pub story_fire_in_front: StoryFireInFront,
125 pub use_skin_sprites: UseSkinSprites,
128 pub always_show_playfield: AlwaysShowPlayfield,
131 pub overlay_position: OverlayPosition,
134 pub skin_preference: SkinPreference,
136 pub epilepsy_warning: EpilepsyWarning,
139 pub countdown_offset: CountdownOffset,
142 pub special_style: SpecialStyle,
145 pub widescreen_storyboard: WidescreenStoryboard,
148 pub samples_match_playback_rate: SamplesMatchPlaybackRate,
151 pub editor_bookmarks: EditorBookmarks,
153 pub editor_distance_spacing: EditorDistanceSpacing,
155 },
156 ParseError,
157 " ",
158);