bms_utils/
bmson.rs

1use serde::{Deserialize, Serialize};
2
3impl std::str::FromStr for Bmson {
4    type Err = serde_json::Error;
5    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
6        serde_json::from_str(s)
7    }
8}
9impl Bmson {
10    /// 文字列からBmsonを解析
11    pub fn parse(source: &str) -> serde_json::Result<Bmson> {
12        serde_json::from_str(source)
13    }
14    /// BmsonからJson形式の文字列に変換
15    pub fn to_string(&self) -> serde_json::Result<String> {
16        serde_json::to_string(self)
17    }
18    /// BmsonからJson形式の文字列に変換
19    ///
20    /// 改行やインデントが適切にされている
21    pub fn to_string_pretty(&self) -> serde_json::Result<String> {
22        serde_json::to_string_pretty(self)
23    }
24}
25
26/// Bmson本体
27#[derive(Deserialize, Serialize, Clone, Default, Debug, PartialEq)]
28pub struct Bmson {
29    /// Bmsonのバージョン
30    pub version: String,
31    /// 譜面の情報
32    pub info: BmsonInfo,
33    /// 小節線
34    pub lines: Option<Vec<BarLine>>,
35    /// BPMイベント
36    pub bpm_events: Option<Vec<BpmEvent>>,
37    /// 譜面停止イベント
38    pub stop_events: Option<Vec<StopEvent>>,
39    /// 音声チャンネル
40    pub sound_channels: Option<Vec<SoundChannel>>,
41    /// BGA情報
42    pub bga: Bga,
43    /// スクロール速度イベント(beatoraja拡張)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub scroll_events: Option<Vec<ScrollEvent>>,
46    /// 地雷チャンネル(beatoraja拡張)
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub mine_channels: Option<Vec<MineChannel>>,
49    /// 不可視ノートチャンネル(beatoraja拡張)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub key_channels: Option<Vec<KeyChannel>>,
52}
53
54/// ヘッダー
55#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
56pub struct BmsonInfo {
57    /// タイトル
58    pub title: String,
59    /// サブタイトル
60    #[serde(default)]
61    pub subtitle: String,
62    /// アーティスト
63    pub artist: String,
64    /// サブアーティスト
65    pub subartists: Option<Vec<String>>,
66    /// ジャンル
67    pub genre: String,
68    /// プレイ方法のヒント
69    ///
70    /// beat-7k・popn-5k・generic-nkeysなど
71    #[serde(default = "default_mode_hint")]
72    pub mode_hint: String,
73    /// 難易度
74    ///
75    /// HYPER・FOUR DIMENSIONSなど
76    pub chart_name: String,
77    /// レベル
78    pub level: u32,
79    /// 初期BPM
80    ///
81    /// 曲選択画面の表示などにも使う
82    pub init_bpm: f64,
83    /// 判定幅
84    ///
85    /// 100を初期値とする
86    #[serde(default = "default_judge_rank")]
87    pub judge_rank: f64,
88    /// ゲージ増加の総数
89    ///
90    /// 全て最良判定のときのゲージの増加量
91    ///
92    /// 某ゲームでは、総ノーツ数をnとしたとき、7.605 * n / (0.01 * n + 6.5) となる
93    #[serde(default = "default_total")]
94    pub total: f64,
95    /// 背景画像
96    pub back_image: Option<String>,
97    /// アイキャッチ画像
98    pub eyecatch_image: Option<String>,
99    /// タイトル画像
100    pub title_image: Option<String>,
101    /// バナー画像
102    pub banner_image: Option<String>,
103    /// プレビュー音声
104    pub preview_music: Option<String>,
105    /// 分解能
106    ///
107    /// 四分音符1つに対応するパルス数
108    ///
109    /// 240が初期値
110    #[serde(default = "default_resolution")]
111    pub resolution: u32,
112    /// ロングノートの種類(beatoraja拡張)
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub ln_type: Option<LongNoteType>,
115}
116fn default_mode_hint() -> String {
117    "beat-7k".to_string()
118}
119fn default_judge_rank() -> f64 {
120    100.
121}
122fn default_total() -> f64 {
123    100.
124}
125fn default_resolution() -> u32 {
126    240
127}
128impl Default for BmsonInfo {
129    fn default() -> Self {
130        BmsonInfo {
131            title: String::new(),
132            subtitle: String::new(),
133            artist: String::new(),
134            subartists: None,
135            genre: String::new(),
136            mode_hint: "beat-7k".to_string(),
137            chart_name: String::new(),
138            level: 0,
139            init_bpm: 180.,
140            judge_rank: 100.,
141            total: 100.,
142            back_image: None,
143            eyecatch_image: None,
144            title_image: None,
145            banner_image: None,
146            preview_music: None,
147            resolution: 240,
148            ln_type: None,
149        }
150    }
151}
152
153/// 小節線イベント
154#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
155pub struct BarLine {
156    /// イベント時刻(パルス数)
157    pub y: u32,
158}
159
160/// サウンドチャンネル
161#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
162pub struct SoundChannel {
163    /// ファイル名
164    pub name: String,
165    /// ノーツ
166    pub notes: Vec<Note>,
167}
168
169/// サウンドノート
170#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
171pub struct Note {
172    /// 演奏レーン
173    ///
174    /// 0 or NullでBGM
175    pub x: Option<u32>,
176    /// 演奏時刻(パルス数)
177    pub y: u32,
178    /// 長さ(パルス数)
179    ///
180    /// 0で普通のノート
181    pub l: u32,
182    /// 続行フラグ
183    ///
184    /// trueでそのまま、falseで音声の最初へもどす
185    pub c: bool,
186    /// ロングノートの種類(beatoraja拡張)
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub t: Option<LongNoteType>,
189    /// 終端フラグ(beatoraja拡張)
190    ///
191    /// trueでかつロングノートの終点に配置される場合、終端音として鳴らす
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub up: Option<bool>,
194}
195
196/// BPM変化イベント
197#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
198pub struct BpmEvent {
199    /// イベント時刻(パルス数)
200    pub y: u32,
201    /// BPM
202    pub bpm: f64,
203}
204
205/// 譜面停止イベント
206#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
207pub struct StopEvent {
208    /// イベント時刻(パルス数)
209    pub y: u32,
210    /// 停止時間(パルス数)
211    pub duration: u32,
212}
213
214/// BGAデータ
215#[derive(Deserialize, Serialize, Clone, Default, Debug, PartialEq)]
216pub struct Bga {
217    /// 画像データ
218    pub bga_header: Vec<BgaHeader>,
219    /// BGAイベント
220    pub bga_events: Vec<BgaEvent>,
221    /// レイヤーイベント
222    pub layer_events: Vec<BgaEvent>,
223    /// POORイベント
224    pub poor_events: Vec<BgaEvent>,
225}
226
227/// 画像ファイル
228#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
229pub struct BgaHeader {
230    /// 画像のID
231    pub id: u32,
232    /// 画像ファイル
233    pub name: String,
234}
235
236/// BGAイベント
237#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
238pub struct BgaEvent {
239    /// イベント時刻(パルス数)
240    pub y: u32,
241    /// 画像のID
242    pub id: u32,
243}
244
245/// ロングノートの種類(beatoraja拡張)
246#[derive(
247    serde_repr::Deserialize_repr,
248    serde_repr::Serialize_repr,
249    Clone,
250    Debug,
251    PartialEq,
252)]
253#[repr(u8)]
254pub enum LongNoteType {
255    /// 未設定
256    None = 0,
257    /// ロングノート
258    ///
259    /// 始点と終点の判定の悪い方の判定になる
260    ///
261    /// ただし、終点のLATE側の判定を除く
262    LongNote = 1,
263    /// チャージノート
264    ///
265    /// 始点と終点の両方の判定がある
266    ///
267    /// 始点を押さなかった場合、終点は見逃したことになる
268    ChargeNote = 2,
269    /// ヘルチャージノート
270    ///
271    /// チャージノートに加え、押している間にも16分間隔でロングノート終点と同じ判定が加わる
272    ///
273    /// 16分間隔の判定は、一度離すと再度押すまで始点の判定になる
274    ///
275    /// ヘルチャージノーツの終点は普通の判定と同じ
276    HellChargeNote = 3,
277}
278
279/// スクロール速度設定イベント(beatoraja拡張)
280#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
281pub struct ScrollEvent {
282    /// イベント時刻(パルス数)
283    pub y: f64,
284    /// スクロール速度倍率
285    pub rate: f64,
286}
287
288/// 地雷チャンネル(beatoraja拡張)
289#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
290pub struct MineChannel {
291    /// ファイル名
292    pub name: String,
293    /// ノーツ
294    pub notes: Vec<MineNote>,
295}
296
297/// 地雷ノート(beatoraja拡張)
298#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
299pub struct MineNote {
300    /// 演奏レーン
301    ///
302    /// 0 or NullでBGM
303    pub x: Option<u32>,
304    /// 設置時刻(パルス数)
305    pub y: u32,
306    /// ダメージ
307    ///
308    /// %で指定
309    pub damage: f64,
310}
311
312/// 不可視チャンネル(beatoraja拡張)
313#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
314pub struct KeyChannel {
315    /// ファイル名
316    pub name: String,
317    /// ノーツ
318    pub notes: Vec<KeyNote>,
319}
320
321/// 不可視ノート(beatoraja拡張)
322#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
323pub struct KeyNote {
324    /// 演奏レーン
325    ///
326    /// 0 or NullでBGM
327    pub x: Option<u32>,
328    /// 演奏時刻(パルス数)
329    pub y: u32,
330}