beatsabermaprs/types/
schema.rs

1#![allow(nonstandard_style)]
2
3use std::collections::HashMap;
4use serde::{Deserialize, Serialize};
5use serde_repr::{Serialize_repr, Deserialize_repr};
6use crate::error::{Error, Result};
7use crate::types::common::{Axis, BoxFilterOrdering, Direction, DistributionKind, Easing, LightColor, RotationBehaviour, RotationDirection, LimitKind, NoteColor, SliderMidAnchorMode, TransitionKind, Difficulty, read_string_from_file, BoxFilterKind};
8
9#[derive(Serialize, Deserialize, Debug, PartialEq)]
10pub struct Info {
11    pub _version: String,
12    pub _songName: String,
13    pub _songSubName: String,
14    pub _songAuthorName: String,
15    pub _levelAuthorName: String,
16    pub _beatsPerMinute: f64,
17    pub _shuffle: f64,
18    pub _shufflePeriod: f64,
19    pub _previewStartTime: f64,
20    pub _previewDuration: f64,
21    pub _songFilename: String,
22    pub _coverImageFilename: String,
23    pub _environmentName: String,
24    #[serde(skip_serializing_if = "Option::is_none", default)]
25    pub _allDirectionsEnvironmentName : Option<String>,
26    pub _songTimeOffset: f64,
27    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
28    pub _customData: HashMap<String, serde_json::Value>,
29    pub _difficultyBeatmapSets: Vec<DifficultyBeatmapSet>
30}
31
32impl Info {
33    pub fn read_from_str(data: &str) -> Result<Self> {
34        Ok(serde_json::from_str(data)?)
35    }
36
37    pub fn read_from_file(path: &str) -> Result<Self> {
38        Self::read_from_str(&read_string_from_file(path)?)
39    }
40}
41
42#[derive(Serialize, Deserialize, Debug, PartialEq)]
43pub struct DifficultyBeatmapSet {
44    pub _beatmapCharacteristicName: String,
45    pub _difficultyBeatmaps: Vec<DifficultyBeatmap>
46}
47
48#[derive(Serialize, Deserialize, Debug, PartialEq)]
49pub struct DifficultyBeatmap {
50    pub _difficulty: Difficulty,
51    pub _difficultyRank: i32,
52    pub _beatmapFilename: String,
53    pub _noteJumpMovementSpeed: f64,
54    pub _noteJumpStartBeatOffset: f64,
55    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
56    pub _customData: HashMap<String, serde_json::Value>,
57}
58
59#[derive(Debug, PartialEq)]
60pub enum BeatmapFile {
61    Old(OldBeatmapFile),
62    New(NewBeatmapFile)
63}
64
65impl BeatmapFile {
66    pub fn read_from_str(data: &str) -> Result<Self> {
67        let new = serde_json::from_str(data);
68        let old = serde_json::from_str(data);
69        if let Ok(beatmap) = new {
70            Ok(Self::New(beatmap))
71        } else if let Ok(beatmap) = old {
72            Ok(Self::Old(beatmap))
73        } else {
74            Err(Error::BeatmapParsingFailed { err_as_new: new.unwrap_err(), err_as_old: old.unwrap_err() })
75        }
76    }
77
78    pub fn read_from_file(path: &str) -> Result<Self> {
79        Self::read_from_str(&read_string_from_file(path)?)
80    }
81}
82
83#[derive(Serialize, Deserialize, Debug, PartialEq)]
84pub struct NewBeatmapFile {
85    pub version: String,
86    pub bpmEvents: Vec<BpmEvent>,
87    pub rotationEvents: Vec<RotationEvent>,
88    pub colorNotes: Vec<ColorNote>,
89    pub bombNotes: Vec<BombNote>,
90    pub obstacles: Vec<Obstacle>,
91    pub sliders: Vec<Slider>,
92    pub burstSliders: Vec<BurstSlider>,
93    pub waypoints: Vec<serde_json::Value>,
94    pub basicBeatmapEvents: Vec<BasicBeatmapEvent>,
95    pub colorBoostBeatmapEvents: Vec<ColorBoostBeatmapEvent>,
96    pub lightColorEventBoxGroups: Vec<LightColorEventBoxGroup>,
97    pub lightRotationEventBoxGroups: Vec<LightRotationEventBoxGroup>,
98    #[serde(skip_serializing_if = "Vec::is_empty", default)]
99    pub lightTranslationEventBoxGroups: Vec<LightTranslationEventBoxGroup>,
100    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
101    pub basicEventTypesWithKeywords: HashMap<String, serde_json::Value>,
102    pub useNormalEventsAsCompatibleEvents: bool,
103    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
104    pub customData: HashMap<String, serde_json::Value>
105}
106
107#[derive(Serialize, Deserialize, Debug, PartialEq)]
108pub struct BpmEvent {
109    pub b: f64,
110    pub m: f64
111}
112
113fn bool_to_int<S>(value: &bool, serialized: S) -> core::result::Result<S::Ok, S::Error> where S: serde::Serializer {
114    serialized.serialize_i32(*value as i32)
115}
116
117#[derive(Serialize, Deserialize, Debug, PartialEq)]
118pub struct RotationEvent {
119    pub b: f64,
120    #[serde(serialize_with = "bool_to_int")]
121    pub e: bool,
122    pub r: f64
123}
124
125#[derive(Serialize, Deserialize, Debug, PartialEq)]
126pub struct ColorNote {
127    pub b: f64,
128    pub x: i32,
129    pub y: i32,
130    pub c: NoteColor,
131    pub d: Direction,
132    pub a: i32
133}
134
135#[derive(Serialize, Deserialize, Debug, PartialEq)]
136pub struct BombNote {
137    pub b: f64,
138    pub x: i32,
139    pub y: i32
140}
141
142#[derive(Serialize, Deserialize, Debug, PartialEq)]
143pub struct Obstacle {
144    pub b: f64,
145    pub x: i32,
146    pub y: i32,
147    pub d: f64,
148    pub w: f64,
149    pub h: f64
150}
151
152#[derive(Serialize, Deserialize, Debug, PartialEq)]
153pub struct Slider {
154    pub b: f64,
155    pub c: NoteColor,
156    pub x: i32,
157    pub y: i32,
158    pub d: Direction,
159    pub mu: f64,
160    pub tb: f64,
161    pub tx: i32,
162    pub ty: i32,
163    pub tc: Direction,
164    pub tmu: f64,
165    pub m: SliderMidAnchorMode
166}
167
168#[derive(Serialize, Deserialize, Debug, PartialEq)]
169pub struct BurstSlider {
170    pub b: f64,
171    pub c: NoteColor,
172    pub x: i32,
173    pub y: i32,
174    pub d: Direction,
175    pub tb: f64,
176    pub tx: i32,
177    pub ty: i32,
178    pub sc: i32,
179    pub s: f64
180}
181
182#[derive(Serialize, Deserialize, Debug, PartialEq)]
183pub struct BasicBeatmapEvent {
184    pub b: f64,
185    pub et: i32,
186    pub i: i32,
187    #[serde(skip_serializing_if = "Option::is_none", default)]
188    pub f: Option<f64>
189}
190
191#[derive(Serialize, Deserialize, Debug, PartialEq)]
192pub struct ColorBoostBeatmapEvent {
193    pub b: f64,
194    pub o: bool
195}
196
197#[derive(Serialize, Deserialize, Debug, PartialEq)]
198pub struct LightColorEventBoxGroup {
199    pub b: f64,
200    pub g: i32,
201    pub e: Vec<LightColorEventBoxGroupLane>
202}
203
204#[derive(Serialize, Deserialize, Debug, PartialEq)]
205pub struct LightColorEventBoxGroupLane {
206    pub f: FilterObject,
207    pub w: f64,
208    pub d: DistributionKind,
209    pub r: f64,
210    pub t: DistributionKind,
211    #[serde(serialize_with = "bool_to_int")]
212    pub b: bool,
213    #[serde(skip_serializing_if = "Option::is_none", default)]
214    pub i: Option<Easing>,
215    pub e: Vec<LightColorEventData>
216}
217
218#[derive(Serialize, Deserialize, Debug, PartialEq)]
219pub struct LightColorEventData {
220    pub b: f64,
221    pub i: TransitionKind,
222    pub c: LightColor,
223    pub s: f64,
224    pub f: i32
225}
226
227#[derive(Serialize, Deserialize, Debug, PartialEq)]
228pub struct LightRotationEventBoxGroup {
229    pub b: f64,
230    pub g: i32,
231    pub e: Vec<LightRotationEventBoxGroupLane>
232}
233
234#[derive(Serialize, Deserialize, Debug, PartialEq)]
235pub struct LightRotationEventBoxGroupLane {
236    pub f: FilterObject,
237    pub w: f64,
238    pub d: DistributionKind,
239    pub s: f64,
240    pub t: DistributionKind,
241    #[serde(serialize_with = "bool_to_int")]
242    pub b: bool,
243    #[serde(skip_serializing_if = "Option::is_none", default)]
244    pub i: Option<Easing>,
245    pub a: Axis,
246    #[serde(serialize_with = "bool_to_int")]
247    pub r: bool,
248    pub e: Vec<LightRotationEventData>
249}
250
251#[derive(Serialize, Deserialize, Debug, PartialEq)]
252pub struct LightRotationEventData {
253    pub b: f64,
254    pub p: RotationBehaviour,
255    pub l: i32,
256    pub e: Easing,
257    pub r: f64,
258    pub o: RotationDirection
259}
260
261#[derive(Serialize, Deserialize, Debug, PartialEq)]
262pub struct LightTranslationEventBoxGroup {
263    pub b: f64,
264    pub g: i32,
265    pub e: Vec<LightTranslationEventBoxGroupLane>
266}
267
268#[derive(Serialize, Deserialize, Debug, PartialEq)]
269pub struct LightTranslationEventBoxGroupLane {
270    pub f: FilterObject,
271    pub w: f64,
272    pub d: DistributionKind,
273    pub s: f64,
274    pub t: DistributionKind,
275    #[serde(serialize_with = "bool_to_int")]
276    pub b: bool,
277    #[serde(skip_serializing_if = "Option::is_none", default)]
278    pub i: Option<Easing>,
279    pub a: Axis,
280    #[serde(serialize_with = "bool_to_int")]
281    pub r: bool,
282    pub l: Vec<LightTranslationEventData>
283}
284
285#[derive(Serialize, Deserialize, Debug, PartialEq)]
286pub struct LightTranslationEventData {
287    pub b: f64,
288    pub p: RotationBehaviour,
289    pub e: Easing,
290    pub t: f64
291}
292
293#[derive(Serialize, Deserialize, Debug, PartialEq)]
294pub struct FilterObject {
295    pub c: i32,
296    pub f: BoxFilterKind,
297    pub p: i32,
298    pub t: i32,
299    #[serde(serialize_with = "bool_to_int")]
300    pub r: bool,
301    pub n: BoxFilterOrdering,
302    pub s: i32,
303    pub l: f64,
304    pub d: LimitKind
305}
306
307#[derive(Serialize, Deserialize, Debug, PartialEq)]
308pub struct OldBeatmapFile {
309    pub _version: String,
310    pub _notes: Vec<OldNote>,
311    #[serde(skip_serializing_if = "Vec::is_empty", default)]
312    pub _sliders: Vec<OldSlider>,
313    pub _obstacles: Vec<OldObstacle>,
314    pub _events: Vec<OldEvent>,
315    #[serde(skip_serializing_if = "Vec::is_empty", default)]
316    pub _waypoints: Vec<serde_json::Value>,
317    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
318    pub customData: HashMap<String, serde_json::Value>
319}
320
321#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq)]
322#[repr(i8)]
323pub enum OldNoteKind {
324    Red = 0,
325    Blue = 1,
326    Unused = 2,
327    Bomb = 3
328}
329
330#[derive(Serialize, Deserialize, Debug, PartialEq)]
331pub struct OldNote {
332    pub _time: f64,
333    pub _lineIndex: i32,
334    pub _lineLayer: i32,
335    pub _type: OldNoteKind,
336    pub _cutDirection: Direction,
337    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
338    pub _customData: HashMap<String, serde_json::Value>
339}
340
341#[derive(Serialize, Deserialize, Debug, PartialEq)]
342pub struct OldSlider {
343    pub _colorType: NoteColor,
344    pub _headTime: f64,
345    pub _headLineIndex: i32,
346    pub _headLineLayer: i32,
347    pub _headControlPointLengthMultiplier: f64,
348    pub _headCutDirection: Direction,
349    pub _tailTime: f64,
350    pub _tailLineIndex: i32,
351    pub _tailLineLayer: i32,
352    pub _tailControlPointLengthMultiplier: f64,
353    pub _tailCutDirection: Direction,
354    pub _sliderMidAnchorMode: SliderMidAnchorMode,
355    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
356    pub _customData: HashMap<String, serde_json::Value>
357}
358
359#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq)]
360#[repr(i8)]
361pub enum OldObstacleKind {
362    Full = 0,
363    Crouch = 1
364}
365
366#[derive(Serialize, Deserialize, Debug, PartialEq)]
367pub struct OldObstacle {
368    pub _time: f64,
369    pub _lineIndex: i32,
370    pub _type: OldObstacleKind,
371    pub _duration: f64,
372    pub _width: f64,
373    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
374    pub _customData: HashMap<String, serde_json::Value>
375}
376
377#[derive(Serialize, Deserialize, Debug, PartialEq)]
378pub struct OldEvent {
379    pub _time: f64,
380    pub _type: i32,
381    pub _value: i32,
382    #[serde(skip_serializing_if = "Option::is_none", default)]
383    pub _floatValue: Option<f64>,
384    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
385    pub _customData: HashMap<String, serde_json::Value>
386}
387