bms_utils/
bms.rs

1#![allow(dead_code)]
2pub(crate) mod lex;
3pub(crate) mod parse;
4pub(crate) mod token;
5pub use token::Channel;
6
7/// ファイルを解析したままのBMS
8///
9/// ランダム要素を確定していない。
10/// [`RawBms::make_bms`]で疑似乱数生成器を指定してBMSを生成する
11#[derive(Debug, Clone, PartialEq, Default)]
12pub struct RawBms {
13    raw_bms: BmsBlock,
14    all_wav_files: HashSet<String>,
15}
16
17#[derive(Debug, Clone, PartialEq, Default)]
18pub(crate) struct BmsBlock(Vec<BmsElement>);
19#[derive(Debug, Clone, PartialEq)]
20pub(crate) enum BmsElement {
21    Command(token::Command),
22    Random(BmsRandomBlock),
23    Switch(BmsSwitchBlock),
24}
25#[derive(Debug, Clone, PartialEq)]
26pub(crate) struct BmsRandomBlock(RandomValue, Vec<BmsRandomElement>);
27#[derive(Debug, Clone, PartialEq)]
28pub(crate) enum BmsRandomElement {
29    Block(BmsBlock),
30    IfBlock(BmsIfBlock),
31}
32#[derive(Debug, Clone, Default, PartialEq)]
33pub(crate) struct BmsIfBlock {
34    pub(crate) r#if: Vec<(u128, BmsBlock)>,
35    pub(crate) r#else: Option<BmsBlock>,
36}
37#[derive(Debug, Clone, PartialEq)]
38pub(crate) struct BmsSwitchBlock(
39    RandomValue,
40    Vec<BmsCaseBlock>,
41    std::collections::HashSet<u128>,
42);
43#[derive(Debug, Clone, PartialEq)]
44pub(crate) struct BmsCaseBlock(SwitchLabel, BmsBlock, bool);
45#[derive(Debug, Clone, PartialEq)]
46pub(crate) enum RandomValue {
47    Max(u128),
48    Set(u128),
49}
50#[derive(Debug, Clone, PartialEq)]
51pub(crate) enum SwitchLabel {
52    Case(u128),
53    Default,
54}
55impl BmsBlock {
56    pub(crate) fn get_token_vec<'a, Rng: rand::RngCore>(
57        &'a self,
58        output: &mut Vec<&'a token::Command>,
59        rng: &mut Rng,
60    ) {
61        for e in &self.0 {
62            e.get_token_vec(output, rng);
63        }
64    }
65}
66impl BmsElement {
67    fn get_token_vec<'a, Rng: rand::RngCore>(
68        &'a self,
69        output: &mut Vec<&'a token::Command>,
70        rng: &mut Rng,
71    ) {
72        match self {
73            BmsElement::Command(c) => {
74                output.push(c);
75            }
76            BmsElement::Random(rb) => {
77                rb.get_token_vec(output, rng);
78            }
79            BmsElement::Switch(sb) => {
80                sb.get_token_vec(output, rng);
81            }
82        }
83    }
84}
85impl BmsRandomBlock {
86    fn get_token_vec<'a, Rng: rand::RngCore>(
87        &'a self,
88        output: &mut Vec<&'a token::Command>,
89        rng: &mut Rng,
90    ) {
91        use rand::Rng;
92        let n = match self.0 {
93            RandomValue::Max(n) => rng.random_range(1..=n),
94            RandomValue::Set(n) => n,
95        };
96        for e in &self.1 {
97            e.get_token_vec(output, rng, n);
98        }
99    }
100}
101impl BmsRandomElement {
102    fn get_token_vec<'a, Rng: rand::RngCore>(
103        &'a self,
104        output: &mut Vec<&'a token::Command>,
105        rng: &mut Rng,
106        n: u128,
107    ) {
108        match self {
109            BmsRandomElement::Block(b) => {
110                b.get_token_vec(output, rng);
111            }
112            BmsRandomElement::IfBlock(ib) => {
113                ib.get_token_vec(output, rng, n);
114            }
115        }
116    }
117}
118impl BmsIfBlock {
119    fn get_token_vec<'a, Rng: rand::RngCore>(
120        &'a self,
121        output: &mut Vec<&'a token::Command>,
122        rng: &mut Rng,
123        n: u128,
124    ) {
125        for (i, b) in &self.r#if {
126            if *i == n {
127                b.get_token_vec(output, rng);
128                return;
129            }
130        }
131        if let Some(b) = &self.r#else {
132            b.get_token_vec(output, rng);
133        }
134    }
135}
136impl BmsSwitchBlock {
137    fn get_token_vec<'a, Rng: rand::RngCore>(
138        &'a self,
139        output: &mut Vec<&'a token::Command>,
140        rng: &mut Rng,
141    ) {
142        use rand::Rng;
143        let n = match self.0 {
144            RandomValue::Max(n) => rng.random_range(1..=n),
145            RandomValue::Set(n) => n,
146        };
147        let mut flag = false;
148        for e in &self.1 {
149            if match &e.0 {
150                SwitchLabel::Case(i) => *i == n,
151                SwitchLabel::Default => !self.2.contains(&n),
152            } {
153                flag = true;
154            }
155            if flag {
156                e.1.get_token_vec(output, rng);
157            }
158            if flag && e.2 {
159                return;
160            }
161        }
162    }
163}
164
165use std::collections::{HashMap, HashSet};
166impl RawBms {
167    pub fn parse(source: &str) -> RawBms {
168        use token::*;
169        use winnow::prelude::*;
170        let token_stream = lex::lex(source);
171        let all_wav_files = token_stream
172            .iter()
173            .filter_map(|t| {
174                if let Token::Command(Command::Wav(_, file)) = t {
175                    Some(file.clone())
176                }
177                else {
178                    None
179                }
180            })
181            .collect::<HashSet<_>>();
182        RawBms {
183            raw_bms: parse::block
184                .parse_next(&mut token_stream.as_slice())
185                .unwrap(),
186            all_wav_files,
187        }
188    }
189    pub fn all_wav_files(&self) -> &HashSet<String> {
190        &self.all_wav_files
191    }
192    pub fn make_bms(&self, mut rng: impl rand::RngCore) -> Bms {
193        use token::Command::*;
194        let mut commands = vec![];
195        self.raw_bms.get_token_vec(&mut commands, &mut rng);
196
197        let base62 = commands.iter().any(|c| matches!(c, Base62));
198
199        let mut bms = Bms {
200            main_data: Vec::with_capacity(1000),
201            ..Default::default()
202        };
203
204        let convert_channel_vec = |ch_vec: &[Channel]| {
205            ch_vec
206                .iter()
207                .map(|ch| ch.to_base_36_or_62(base62))
208                .collect::<Vec<_>>()
209        };
210        for c in commands {
211            match c {
212                MainData(measure, data) => {
213                    if bms.main_data.len() <= *measure {
214                        bms.main_data
215                            .resize_with(measure + 1, Default::default);
216                    }
217                    let measure = &mut bms.main_data[*measure];
218                    use token::MainDataValue::*;
219                    #[rustfmt::skip]
220                    match data {
221                        Bgm(data) =>
222                            measure.bgm.push(convert_channel_vec(data)),
223                        Length(n) =>
224                            measure.length = *n,
225                        Bga(data) =>
226                            measure.bga.push(convert_channel_vec(data)),
227                        Bpm(data) =>
228                            measure.bpm.push(data),
229                        BgaPoor(data) =>
230                            measure.bga_poor.push(convert_channel_vec(data)),
231                        BgaLayer(data) =>
232                            measure.bga_layer.push(convert_channel_vec(data)),
233                        ExBpm(data) =>
234                            measure.ex_bpm.push(convert_channel_vec(data)),
235                        Stop(data) =>
236                            measure.stop.push(convert_channel_vec(data)),
237                        BgaLayer2(data) =>
238                            measure.bga_layer2.push(convert_channel_vec(data)),
239                        ExRank(data) =>
240                            measure.ex_rank.push(convert_channel_vec(data)),
241                        BgaAlpha(data) =>
242                            measure.bga_alpha.push(data),
243                        BgaLayerAlpha(data) =>
244                            measure.bga_layer_alpha.push(data),
245                        BgaLayer2Alpha(data) =>
246                            measure.bga_layer2_alpha.push(data),
247                        BgaPoorAlpha(data) =>
248                            measure.bga_poor_alpha.push(data),
249                        Note(ch, data) =>
250                            measure.notes.entry(*ch).or_default().push(convert_channel_vec(data)),
251                        InvisibleNote(ch, data) =>
252                            measure.invisible_notes.entry(*ch).or_default().push(convert_channel_vec(data)),
253                        LongNote(ch, data) =>
254                            measure.long_notes.entry(*ch).or_default().push(convert_channel_vec(data)),
255                        Text(data) =>
256                            measure.text.push(convert_channel_vec(data)),
257                        BgaArgb(data) =>
258                            measure.bga_argb.push(convert_channel_vec(data)),
259                        BgaLayerArgb(data) =>
260                            measure.bga_layer_argb.push(convert_channel_vec(data)),
261                        BgaLayer2Argb(data) =>
262                            measure.bga_layer2_argb.push(convert_channel_vec(data)),
263                        BgaPoorArgb(data) =>
264                            measure.bga_poor_argb.push(convert_channel_vec(data)),
265                        SwitchBga(data) =>
266                            measure.switch_bga.push(convert_channel_vec(data)),
267                        Option(data) =>
268                            measure.option.push(convert_channel_vec(data)),
269                        Landmine(ch, data) =>
270                            measure.landmine.entry(*ch).or_default().push(data),
271                        Scroll(data) =>
272                            measure.scroll.push(convert_channel_vec(data)),
273                        Speed(data) =>
274                            measure.speed.push(convert_channel_vec(data)),
275                        Other(ch, data) =>
276                            measure.other.push((*ch, data)),
277                    };
278                }
279                Player(n) => {
280                    bms.deprecated.player = Some(match n {
281                        1 => PlayType::SinglePlay,
282                        2 => PlayType::CouplePlay,
283                        3 => PlayType::DoublePlay,
284                        4 => PlayType::BattlePlay,
285                        _ => unreachable!(),
286                    });
287                }
288                Rank(n) => bms.rank = Some(*n),
289                DefExRank(n) => bms.def_ex_rank = Some(*n),
290                ExRank(ch, n) => {
291                    bms.uncommon
292                        .ex_rank
293                        .insert(ch.to_base_36_or_62(base62), *n);
294                }
295                Total(n) => bms.total = Some(*n),
296                VolumeWav(n) => bms.volume_wav = Some(*n),
297                StageFile(s) => bms.stage_file = Some(s),
298                Banner(s) => bms.banner = Some(s),
299                BackBmp(s) => bms.back_bmp = Some(s),
300                CharacterFile(s) => bms.uncommon.character_file = Some(s),
301                PlayLevel(n) => bms.play_level = Some(*n),
302                Difficulty(n) => bms.difficulty = Some(*n),
303                Title(s) => bms.title = Some(s),
304                SubTitle(s) => bms.sub_title.push(s),
305                Artist(s) => bms.artist = Some(s),
306                SubArtist(s) => bms.sub_artist.push(s),
307                Maker(s) => bms.uncommon.maker = Some(s),
308                Genre(s) => bms.genre = Some(s),
309                Comment(s) => bms.uncommon.comment.push(s),
310                Text(ch, s) => {
311                    bms.uncommon.text.insert(ch.to_base_36_or_62(base62), s);
312                }
313                PathWav(s) => bms.uncommon.path_wav = Some(s),
314                Bpm(n) => bms.bpm = Some(*n),
315                ExBpm(ch, n) => {
316                    bms.ex_bpm.insert(ch.to_base_36_or_62(base62), *n);
317                }
318                BaseBpm(n) => bms.deprecated.base_bpm = Some(*n),
319                Stop(ch, n) => {
320                    bms.stop.insert(ch.to_base_36_or_62(base62), *n);
321                }
322                Stp(x, y, z) => bms.uncommon.stp.push((*x, *y, *z)),
323                LnMode(n) => bms.ln_mode = Some(*n),
324                LnType(n) => bms.ln_type = Some(*n),
325                LnObject(ch) => {
326                    bms.ln_object.insert(ch.to_base_36_or_62(base62));
327                }
328                OctFp => bms.uncommon.oct_fp = true,
329                Option(name, opt) => bms.uncommon.option.push((name, opt)),
330                ChangeOption(ch, name, opt) => {
331                    bms.uncommon
332                        .change_option
333                        .insert(ch.to_base_36_or_62(base62), (name, opt));
334                }
335                Wav(ch, s) => {
336                    bms.wav.insert(ch.to_base_36_or_62(base62), s);
337                }
338                WavCommand(opt, ch, v) => {
339                    bms.uncommon.wav_command.push((
340                        *opt,
341                        ch.to_base_36_or_62(base62),
342                        *v,
343                    ));
344                }
345                ExWav(ch, opt, s) => {
346                    bms.uncommon
347                        .ex_wav
348                        .insert(ch.to_base_36_or_62(base62), (opt, s));
349                }
350                Cdda(n) => bms.uncommon.cdda = Some(*n),
351                MidiFile(s) => bms.uncommon.midi_file = Some(s),
352                Bmp(ch, s) => {
353                    bms.bmp.insert(ch.to_base_36_or_62(base62), s);
354                }
355                ExBmp(ch, argb, s) => {
356                    bms.uncommon
357                        .ex_bmp
358                        .insert(ch.to_base_36_or_62(base62), (argb, s));
359                }
360                Bga(ch, bmp, pos) => {
361                    bms.uncommon.bga.insert(
362                        ch.to_base_36_or_62(base62),
363                        (bmp.to_base_36_or_62(base62), pos),
364                    );
365                }
366                AtBga(ch, bmp, pos) => {
367                    bms.uncommon.at_bga.insert(
368                        ch.to_base_36_or_62(base62),
369                        (bmp.to_base_36_or_62(base62), pos),
370                    );
371                }
372                PoorBga(n) => bms.uncommon.poor_bga = Some(*n),
373                SwitchBga(ch, fr, time, line, r#loop, argb, data) => {
374                    bms.deprecated.switch_bga.insert(
375                        ch.to_base_36_or_62(base62),
376                        (*fr, *time, line.to_base_36(), *r#loop, argb, data),
377                    );
378                }
379                Argb(ch, argb) => {
380                    bms.uncommon.argb.insert(ch.to_base_36_or_62(base62), argb);
381                }
382                VideoFile(s) => bms.uncommon.video_file = Some(s),
383                VideoFps(n) => bms.uncommon.video_fps = Some(*n),
384                VideoColors(n) => bms.uncommon.video_colors = Some(*n),
385                VideoDelay(n) => bms.uncommon.video_delay = Some(*n),
386                Movie(s) => bms.uncommon.movie = Some(s),
387                Seek(ch, n) => {
388                    bms.deprecated.seek.insert(ch.to_base_36_or_62(base62), *n);
389                }
390                ExCharacter(sprite_num, bmp, trim_rect, offset, abs_pos) => {
391                    bms.uncommon.ex_character = Some(super::bms::ExCharacter {
392                        sprite_num: *sprite_num,
393                        bmp: *bmp,
394                        trim_rect,
395                        offset: offset.as_ref(),
396                        abs_pos: abs_pos.as_ref(),
397                    });
398                }
399                Url(s) => bms.url = Some(s),
400                Email(s) => bms.email = Some(s),
401                Scroll(ch, n) => {
402                    bms.scroll.insert(ch.to_base_36_or_62(base62), *n);
403                }
404                Speed(ch, n) => {
405                    bms.speed.insert(ch.to_base_36_or_62(base62), *n);
406                }
407                Preview(s) => bms.preview = Some(s),
408                Other(command, value) => {
409                    bms.uncommon.other.push((command, value));
410                }
411                _ => (),
412            }
413        }
414        bms
415    }
416}
417
418/// ランダムを考慮したBMS
419#[derive(Default, Debug, PartialEq)]
420pub struct Bms<'a> {
421    /// メインデータ
422    ///
423    /// mmmcc:chchch...
424    ///
425    /// mmm : 小節数 [0-9]
426    ///
427    /// cc : チャンネル [0-9, A-Z, a-z]
428    ///
429    /// chchch.. : メインのデータで、2文字一組として扱う [0-9, A-Z, a-z]
430    pub main_data: Vec<MainData<'a>>,
431    /// 判定幅
432    ///
433    /// 2を基本とするのが主流だが、その幅は実装依存
434    pub rank: Option<i32>,
435    /// より細かい判定幅
436    ///
437    /// 100をRank2と同じとするのが主流だが、
438    /// Rankの1に相当する数が実装依存
439    pub def_ex_rank: Option<f64>,
440    /// ゲージ増加の総数
441    ///
442    /// 全て最良判定のときのゲージの増加量
443    pub total: Option<f64>,
444    /// 譜面全体の音量
445    pub volume_wav: Option<f64>,
446    /// ロード画面に表示する画像
447    pub stage_file: Option<&'a str>,
448    /// 選曲画面やリザルト画面に表示する横長の画像
449    pub banner: Option<&'a str>,
450    /// ステージファイルに重ねる画像
451    ///
452    /// 選曲後カバー等の調整をする画面で、
453    /// ステージファイルにタイトルの代わりに重ねる
454    pub back_bmp: Option<&'a str>,
455    /// レベル
456    pub play_level: Option<i32>,
457    /// 難易度
458    ///
459    /// 主流な名付けは
460    ///
461    /// 1 : EASY, BEGINNER, LIGHT ...
462    ///
463    /// 2 : NORMAL, STANDARD ...
464    ///
465    /// 3 : HARD, HYPER ...
466    ///
467    /// 4 : EX, ANOTHER ...
468    ///
469    /// 5 : BLACK_ANOTHER, INSANE, 発狂 ...
470    pub difficulty: Option<i32>,
471    /// タイトル
472    pub title: Option<&'a str>,
473    /// サブタイトル
474    pub sub_title: Vec<&'a str>,
475    /// アーティスト
476    pub artist: Option<&'a str>,
477    /// サブアーティスト
478    pub sub_artist: Vec<&'a str>,
479    /// ジャンル名
480    pub genre: Option<&'a str>,
481    /// 基本BPM
482    ///
483    /// 曲選択時の表示や曲の最初のBPMとして使う
484    pub bpm: Option<f64>,
485    /// BPM変化用
486    ///
487    /// BPM変化時に256以上の値を指定したいとき、
488    /// 08チャンネルでidを指定する
489    pub ex_bpm: HashMap<usize, f64>,
490    /// 停止
491    ///
492    /// 譜面を停止するときに09チャンネルでidを指定する
493    pub stop: HashMap<usize, f64>,
494    /// LNの判定方法
495    ///
496    /// 1 : LN, 始点判定が基準で、最後まで押し切らないとPOOR
497    ///
498    /// 2 : CN, 始点判定と終点判定がある
499    ///
500    /// 3 : HCN, CHの判定に、押している間16分ごとに判定を追加したもの
501    pub ln_mode: Option<i32>,
502    /// LNの指定方法
503    ///
504    /// 1 : 主流の指定方法
505    ///
506    /// メインデータの0以外のidをLNの始点終点の繰り返しとして解析
507    ///
508    /// 2 : 非推奨な指定方法
509    ///
510    /// メインデータの0以外のidが続く部分をLNとして解析
511    ///
512    /// 例
513    ///
514    /// 1 : 0011000000002200
515    ///
516    /// 2 : 0011111111220000
517    pub ln_type: Option<i32>,
518    /// LNの音を鳴らす終点idの指定
519    ///
520    /// LNTYPE 1 で終点として使うと、音が鳴るようになる
521    pub ln_object: HashSet<usize>,
522    /// 音声ファイル
523    ///
524    /// 詳細はメインデータ等に記載
525    pub wav: HashMap<usize, &'a str>,
526    /// 画像ファイル
527    ///
528    /// 詳細はメインデータ等に記載
529    pub bmp: HashMap<usize, &'a str>,
530    /// BMS制作者のホームページ
531    pub url: Option<&'a str>,
532    /// BMS制作者のEメール
533    pub email: Option<&'a str>,
534    /// 譜面速度
535    pub scroll: HashMap<usize, f64>,
536    /// 譜面速度
537    ///
538    /// SCROLLと違って線形補間がされる
539    pub speed: HashMap<usize, f64>,
540    /// 曲選択時に流れる音声
541    ///
542    /// ここで指定されていなければ、
543    /// previewと名前が付いた音声ファイルを流すのが主流
544    pub preview: Option<&'a str>,
545    /// あまり使われないコマンド
546    pub uncommon: UncommonHeader<'a>,
547    /// 非推奨のコマンド
548    pub deprecated: DeprecatedHeader<'a>,
549}
550
551/// 一小節ごとのメインデータ
552///
553/// 同じ小節、同じチャンネルが複数行定義された場合に対応
554///
555/// bgmとoptionのみ複数行に対応している場合が多い
556#[derive(Debug, PartialEq)]
557pub struct MainData<'a> {
558    /// BGM
559    pub bgm: Vec<Vec<usize>>,
560    /// 一小節の長さ
561    ///
562    /// 1が基準
563    pub length: f64,
564    /// BPM
565    ///
566    /// 1から255まで
567    pub bpm: Vec<&'a [Option<f64>]>,
568    /// BGA
569    pub bga: Vec<Vec<usize>>,
570    /// POOR BGA
571    pub bga_poor: Vec<Vec<usize>>,
572    /// BGA LAYER
573    pub bga_layer: Vec<Vec<usize>>,
574    /// EXBPM
575    pub ex_bpm: Vec<Vec<usize>>,
576    /// 停止
577    pub stop: Vec<Vec<usize>>,
578    /// BGA LAYER2
579    pub bga_layer2: Vec<Vec<usize>>,
580    /// BGA不透明度
581    pub bga_alpha: Vec<&'a [u8]>,
582    /// BGA LAYER不透明度
583    pub bga_layer_alpha: Vec<&'a [u8]>,
584    /// BGA LAYER2不透明度
585    pub bga_layer2_alpha: Vec<&'a [u8]>,
586    /// POOR BGA不透明度
587    pub bga_poor_alpha: Vec<&'a [u8]>,
588    /// ノーツ
589    ///
590    /// チャンネルを36進数で解釈した値をキーにした`HashMap`
591    pub notes: HashMap<usize, Vec<Vec<usize>>>,
592    /// 不可視ノーツ
593    ///
594    /// チャンネルを36進数で解釈した値をキーにした`HashMap`
595    pub invisible_notes: HashMap<usize, Vec<Vec<usize>>>,
596    /// ロングノーツ
597    ///
598    /// チャンネルを36進数で解釈した値をキーにした`HashMap`
599    pub long_notes: HashMap<usize, Vec<Vec<usize>>>,
600    /// テキスト
601    pub text: Vec<Vec<usize>>,
602    /// EXRANK
603    pub ex_rank: Vec<Vec<usize>>,
604    /// BGA aRGB
605    pub bga_argb: Vec<Vec<usize>>,
606    /// BGA LAYER aRGB
607    pub bga_layer_argb: Vec<Vec<usize>>,
608    /// BGA LAYER2 aRGB
609    pub bga_layer2_argb: Vec<Vec<usize>>,
610    /// POOR BGA aRGB
611    pub bga_poor_argb: Vec<Vec<usize>>,
612    /// SWBGA
613    pub switch_bga: Vec<Vec<usize>>,
614    /// オプション
615    pub option: Vec<Vec<usize>>,
616    /// 地雷
617    pub landmine: HashMap<usize, Vec<&'a [f64]>>,
618    /// スクロール速度
619    pub scroll: Vec<Vec<usize>>,
620    /// ノーツ速度
621    pub speed: Vec<Vec<usize>>,
622
623    /// その他
624    pub other: Vec<(usize, &'a str)>,
625}
626impl Default for MainData<'_> {
627    fn default() -> Self {
628        MainData {
629            bgm: vec![],
630            length: 1.0,
631            bpm: vec![],
632            bga: vec![],
633            bga_poor: vec![],
634            bga_layer: vec![],
635            ex_bpm: vec![],
636            stop: vec![],
637            bga_layer2: vec![],
638            bga_alpha: vec![],
639            bga_layer_alpha: vec![],
640            bga_layer2_alpha: vec![],
641            bga_poor_alpha: vec![],
642            notes: HashMap::new(),
643            invisible_notes: HashMap::new(),
644            long_notes: HashMap::new(),
645            text: vec![],
646            ex_rank: vec![],
647            bga_argb: vec![],
648            bga_layer_argb: vec![],
649            bga_layer2_argb: vec![],
650            bga_poor_argb: vec![],
651            switch_bga: vec![],
652            option: vec![],
653            landmine: HashMap::new(),
654            scroll: vec![],
655            speed: vec![],
656            other: vec![],
657        }
658    }
659}
660
661/// あまり使われないヘッダー
662#[derive(Default, Debug, PartialEq)]
663pub struct UncommonHeader<'a> {
664    /// メインデータで指定可能な判定幅の設定
665    ///
666    /// 値はDefExRankと同じ扱いをすることが主流だが、DefExRankと同じように判定幅が実装依存
667    pub ex_rank: HashMap<usize, f64>,
668    pub character_file: Option<&'a str>,
669    /// 譜面制作者
670    pub maker: Option<&'a str>,
671    /// 曲選択時に表示するコメント
672    pub comment: Vec<&'a str>,
673    /// プレイ中に表示するテキスト
674    pub text: HashMap<usize, &'a str>,
675    /// 音声ファイルを読み込むときに参照するフォルダ
676    pub path_wav: Option<&'a str>,
677    pub stp: Vec<(usize, u32, f64)>,
678    pub oct_fp: bool,
679    pub option: Vec<(&'a str, &'a str)>,
680    pub change_option: HashMap<usize, (&'a str, &'a str)>,
681    pub wav_command: Vec<(i32, usize, f64)>,
682    pub ex_wav: HashMap<usize, (&'a [Option<f64>; 3], &'a str)>,
683    pub cdda: Option<u32>,
684    pub midi_file: Option<&'a str>,
685    pub ex_bmp: HashMap<usize, (&'a [u8; 4], &'a str)>,
686    pub bga: HashMap<usize, (usize, &'a [[f64; 2]; 3])>,
687    pub at_bga: HashMap<usize, (usize, &'a [[f64; 2]; 3])>,
688    pub poor_bga: Option<i32>,
689    pub argb: HashMap<usize, &'a [u8; 4]>,
690    pub video_file: Option<&'a str>,
691    pub video_fps: Option<f64>,
692    pub video_colors: Option<u32>,
693    pub video_delay: Option<u32>,
694    pub movie: Option<&'a str>,
695    pub ex_character: Option<ExCharacter<'a>>,
696    /// その他のコマンド
697    pub other: Vec<(&'a str, &'a str)>,
698}
699
700/// Extended-Characterファイル
701#[derive(Debug, PartialEq)]
702pub struct ExCharacter<'a> {
703    pub sprite_num: u32,
704    pub bmp: usize,
705    pub trim_rect: &'a [[f64; 2]; 2],
706    pub offset: Option<&'a [f64; 2]>,
707    pub abs_pos: Option<&'a [f64; 2]>,
708}
709
710/// 非推奨のヘッダー
711#[derive(Default, Debug, PartialEq)]
712pub struct DeprecatedHeader<'a> {
713    /// プレイ方法
714    ///
715    /// 主にメインデータから解析するのが主流
716    pub player: Option<PlayType>,
717    /// スクロール速度の基準BPM
718    ///
719    /// オプション側で基準とするBPMを最大、最小、最長、最初のBPMに合わせられるようにするべき
720    pub base_bpm: Option<f64>,
721    /// キーを押したときに表示するBGA
722    ///
723    /// 試験的に追加された
724    pub switch_bga:
725        HashMap<usize, (f64, f64, usize, bool, &'a [u8; 4], &'a [Channel])>,
726    /// ビデオの再生位置を調整
727    ///
728    /// 提案されたプレイヤーで削除済み
729    pub seek: HashMap<usize, f64>,
730}
731
732/// プレイ方式
733#[derive(Debug, PartialEq)]
734pub enum PlayType {
735    SinglePlay,
736    CouplePlay,
737    DoublePlay,
738    BattlePlay,
739}