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 new(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 new_with_stack(source: &str, stack_size: usize) -> RawBms {
190        let source = source.to_string();
191        std::thread::Builder::new()
192            .stack_size(stack_size)
193            .spawn(move || RawBms::new(&source))
194            .unwrap()
195            .join()
196            .unwrap()
197    }
198    pub fn drop_with_stack(self, stack_size: usize) {
199        std::thread::Builder::new()
200            .stack_size(stack_size)
201            .spawn(move || drop(self))
202            .unwrap()
203            .join()
204            .unwrap();
205    }
206    pub fn all_wav_files(&self) -> &HashSet<String> {
207        &self.all_wav_files
208    }
209    pub fn make_bms(&self, mut rng: impl rand::RngCore) -> Bms {
210        use token::Command::*;
211        let mut commands = vec![];
212        self.raw_bms.get_token_vec(&mut commands, &mut rng);
213
214        let base62 = commands.iter().any(|c| matches!(c, Base62));
215
216        let mut bms = Bms {
217            main_data: Vec::with_capacity(1000),
218            ..Default::default()
219        };
220
221        let convert_channel_vec = |ch_vec: &[Channel]| {
222            ch_vec
223                .iter()
224                .map(|ch| ch.to_base_36_or_62(base62))
225                .collect::<Vec<_>>()
226        };
227        for c in commands {
228            match c {
229                MainData(measure, data) => {
230                    if bms.main_data.len() <= *measure {
231                        bms.main_data
232                            .resize_with(measure + 1, Default::default);
233                    }
234                    let measure = &mut bms.main_data[*measure];
235                    use token::MainDataValue::*;
236                    #[rustfmt::skip]
237                    match data {
238                        Bgm(data) =>
239                            measure.bgm.push(convert_channel_vec(data)),
240                        Length(n) =>
241                            measure.length = *n,
242                        Bga(data) =>
243                            measure.bga.push(convert_channel_vec(data)),
244                        Bpm(data) =>
245                            measure.bpm.push(data),
246                        BgaPoor(data) =>
247                            measure.bga_poor.push(convert_channel_vec(data)),
248                        BgaLayer(data) =>
249                            measure.bga_layer.push(convert_channel_vec(data)),
250                        ExBpm(data) =>
251                            measure.ex_bpm.push(convert_channel_vec(data)),
252                        Stop(data) =>
253                            measure.stop.push(convert_channel_vec(data)),
254                        BgaLayer2(data) =>
255                            measure.bga_layer2.push(convert_channel_vec(data)),
256                        ExRank(data) =>
257                            measure.ex_rank.push(convert_channel_vec(data)),
258                        BgaAlpha(data) =>
259                            measure.bga_alpha.push(data),
260                        BgaLayerAlpha(data) =>
261                            measure.bga_layer_alpha.push(data),
262                        BgaLayer2Alpha(data) =>
263                            measure.bga_layer2_alpha.push(data),
264                        BgaPoorAlpha(data) =>
265                            measure.bga_poor_alpha.push(data),
266                        Note(ch, data) =>
267                            measure.note.entry(*ch).or_default().push(convert_channel_vec(data)),
268                        InvisibleNote(ch, data) =>
269                            measure.invisible_note.entry(*ch).or_default().push(convert_channel_vec(data)),
270                        LongNote(ch, data) =>
271                            measure.long_note.entry(*ch).or_default().push(convert_channel_vec(data)),
272                        Text(data) =>
273                            measure.text.push(convert_channel_vec(data)),
274                        BgaArgb(data) =>
275                            measure.bga_argb.push(convert_channel_vec(data)),
276                        BgaLayerArgb(data) =>
277                            measure.bga_layer_argb.push(convert_channel_vec(data)),
278                        BgaLayer2Argb(data) =>
279                            measure.bga_layer2_argb.push(convert_channel_vec(data)),
280                        BgaPoorArgb(data) =>
281                            measure.bga_poor_argb.push(convert_channel_vec(data)),
282                        SwitchBga(data) =>
283                            measure.switch_bga.push(convert_channel_vec(data)),
284                        Option(data) =>
285                            measure.option.push(convert_channel_vec(data)),
286                        Landmine(ch, data) =>
287                            measure.landmine.entry(*ch).or_default().push(data),
288                        Scroll(data) =>
289                            measure.scroll.push(convert_channel_vec(data)),
290                        Speed(data) =>
291                            measure.speed.push(convert_channel_vec(data)),
292                        Other(ch, data) =>
293                            measure.other.push((*ch, data)),
294                    };
295                }
296                Player(n) => {
297                    bms.deprecated.player = Some(match n {
298                        1 => PlayType::SinglePlay,
299                        2 => PlayType::CouplePlay,
300                        3 => PlayType::DoublePlay,
301                        4 => PlayType::BattlePlay,
302                        _ => unreachable!(),
303                    });
304                }
305                Rank(n) => bms.rank = Some(*n),
306                DefExRank(n) => bms.def_ex_rank = Some(*n),
307                ExRank(ch, n) => {
308                    bms.uncommon
309                        .ex_rank
310                        .insert(ch.to_base_36_or_62(base62), *n);
311                }
312                Total(n) => bms.total = Some(*n),
313                VolumeWav(n) => bms.volume_wav = Some(*n),
314                StageFile(s) => bms.stage_file = Some(s),
315                Banner(s) => bms.banner = Some(s),
316                BackBmp(s) => bms.back_bmp = Some(s),
317                CharacterFile(s) => bms.uncommon.character_file = Some(s),
318                PlayLevel(n) => bms.play_level = Some(*n),
319                Difficulty(n) => bms.difficulty = Some(*n),
320                Title(s) => bms.title = Some(s),
321                SubTitle(s) => bms.sub_title.push(s),
322                Artist(s) => bms.artist = Some(s),
323                SubArtist(s) => bms.sub_artist.push(s),
324                Maker(s) => bms.uncommon.maker = Some(s),
325                Genre(s) => bms.genre = Some(s),
326                Comment(s) => bms.uncommon.comment.push(s),
327                Text(ch, s) => {
328                    bms.uncommon.text.insert(ch.to_base_36_or_62(base62), s);
329                }
330                PathWav(s) => bms.uncommon.path_wav = Some(s),
331                Bpm(n) => bms.bpm = Some(*n),
332                ExBpm(ch, n) => {
333                    bms.ex_bpm.insert(ch.to_base_36_or_62(base62), *n);
334                }
335                BaseBpm(n) => bms.deprecated.base_bpm = Some(*n),
336                Stop(ch, n) => {
337                    bms.stop.insert(ch.to_base_36_or_62(base62), *n);
338                }
339                Stp(x, y, z) => bms.uncommon.stp.push((*x, *y, *z)),
340                LnMode(n) => bms.ln_mode = Some(*n),
341                LnType(n) => bms.ln_type = Some(*n),
342                LnObject(ch) => {
343                    bms.ln_object.insert(ch.to_base_36_or_62(base62));
344                }
345                OctFp => bms.uncommon.oct_fp = true,
346                Option(name, opt) => bms.uncommon.option.push((name, opt)),
347                ChangeOption(ch, name, opt) => {
348                    bms.uncommon
349                        .change_option
350                        .insert(ch.to_base_36_or_62(base62), (name, opt));
351                }
352                Wav(ch, s) => {
353                    bms.wav.insert(ch.to_base_36_or_62(base62), s);
354                }
355                WavCommand(opt, ch, v) => {
356                    bms.uncommon.wav_command.push((
357                        *opt,
358                        ch.to_base_36_or_62(base62),
359                        *v,
360                    ));
361                }
362                ExWav(ch, opt, s) => {
363                    bms.uncommon
364                        .ex_wav
365                        .insert(ch.to_base_36_or_62(base62), (opt, s));
366                }
367                Cdda(n) => bms.uncommon.cdda = Some(*n),
368                MidiFile(s) => bms.uncommon.midi_file = Some(s),
369                Bmp(ch, s) => {
370                    bms.bmp.insert(ch.to_base_36_or_62(base62), s);
371                }
372                ExBmp(ch, argb, s) => {
373                    bms.uncommon
374                        .ex_bmp
375                        .insert(ch.to_base_36_or_62(base62), (argb, s));
376                }
377                Bga(ch, bmp, pos) => {
378                    bms.uncommon.bga.insert(
379                        ch.to_base_36_or_62(base62),
380                        (bmp.to_base_36_or_62(base62), pos),
381                    );
382                }
383                AtBga(ch, bmp, pos) => {
384                    bms.uncommon.at_bga.insert(
385                        ch.to_base_36_or_62(base62),
386                        (bmp.to_base_36_or_62(base62), pos),
387                    );
388                }
389                PoorBga(n) => bms.uncommon.poor_bga = Some(*n),
390                SwitchBga(ch, fr, time, line, r#loop, argb, data) => {
391                    bms.deprecated.switch_bga.insert(
392                        ch.to_base_36_or_62(base62),
393                        (*fr, *time, line.to_base_36(), *r#loop, argb, data),
394                    );
395                }
396                Argb(ch, argb) => {
397                    bms.uncommon.argb.insert(ch.to_base_36_or_62(base62), argb);
398                }
399                VideoFile(s) => bms.uncommon.video_file = Some(s),
400                VideoFps(n) => bms.uncommon.video_fps = Some(*n),
401                VideoColors(n) => bms.uncommon.video_colors = Some(*n),
402                VideoDelay(n) => bms.uncommon.video_delay = Some(*n),
403                Movie(s) => bms.uncommon.movie = Some(s),
404                Seek(ch, n) => {
405                    bms.deprecated.seek.insert(ch.to_base_36_or_62(base62), *n);
406                }
407                ExCharacter(sprite_num, bmp, trim_rect, offset, abs_pos) => {
408                    bms.uncommon.ex_character = Some(super::bms::ExCharacter {
409                        sprite_num: *sprite_num,
410                        bmp: *bmp,
411                        trim_rect,
412                        offset: offset.as_ref(),
413                        abs_pos: abs_pos.as_ref(),
414                    });
415                }
416                Url(s) => bms.url = Some(s),
417                Email(s) => bms.email = Some(s),
418                Scroll(ch, n) => {
419                    bms.scroll.insert(ch.to_base_36_or_62(base62), *n);
420                }
421                Speed(ch, n) => {
422                    bms.speed.insert(ch.to_base_36_or_62(base62), *n);
423                }
424                Preview(s) => bms.preview = Some(s),
425                Other(command, value) => {
426                    bms.uncommon.other.push((command, value));
427                }
428                _ => (),
429            }
430        }
431        bms
432    }
433}
434
435/// ランダムを考慮したBMS
436#[derive(Default, Debug, PartialEq)]
437pub struct Bms<'a> {
438    /// メインデータ
439    ///
440    /// mmmcc:chchch...
441    ///
442    /// mmm : 小節数 [0-9]
443    ///
444    /// cc : チャンネル [0-9, A-Z, a-z]
445    ///
446    /// chchch.. : メインのデータで、2文字一組として扱う [0-9, A-Z, a-z]
447    pub main_data: Vec<MainData<'a>>,
448    /// 判定幅
449    ///
450    /// 2を基本とするのが主流だが、その幅は実装依存
451    pub rank: Option<i32>,
452    /// より細かい判定幅
453    ///
454    /// 100をRank2と同じとするのが主流だが、Rankの1に相当する数が実装依存
455    pub def_ex_rank: Option<f64>,
456    /// ゲージ増加の総数
457    ///
458    /// 全ての判定が最良のときのゲージの増加量
459    /// この値からキー1つあたりのゲージ増加量を計算する
460    /// 特にLNの種類によって判定数が変化するので、
461    /// それに対応するため
462    pub total: Option<f64>,
463    /// 譜面全体の音量
464    pub volume_wav: Option<f64>,
465    /// ロード画面に表示する画像
466    pub stage_file: Option<&'a str>,
467    /// 選曲画面やリザルト画面に表示する横長の画像
468    pub banner: Option<&'a str>,
469    /// ステージファイルに重ねる画像
470    ///
471    /// 選曲後カバー等の調整をする画面で、
472    /// ステージファイルにタイトルの代わりに重ねる
473    pub back_bmp: Option<&'a str>,
474    /// レベル
475    pub play_level: Option<i32>,
476    /// 難易度
477    ///
478    /// 主流な名付けは
479    ///
480    /// 1 : EASY, BEGINNER, LIGHT
481    ///
482    /// 2 : NORMAL, STANDARD
483    ///
484    /// 3 : HARD, HYPER
485    ///
486    /// 4 : EX, ANOTHER
487    ///
488    /// 5 : BLACK_ANOTHER, INSANE, 発狂
489    pub difficulty: Option<i32>,
490    /// タイトル
491    pub title: Option<&'a str>,
492    /// サブタイトル
493    pub sub_title: Vec<&'a str>,
494    /// アーティスト
495    pub artist: Option<&'a str>,
496    /// サブアーティスト
497    pub sub_artist: Vec<&'a str>,
498    /// ジャンル名
499    pub genre: Option<&'a str>,
500    /// 基本BPM
501    ///
502    /// 曲選択時の表示や曲の最初のBPMとして使う
503    pub bpm: Option<f64>,
504    /// BPM変化用
505    ///
506    /// BPM変化時に256以上の値を指定したいとき、
507    /// 08チャンネルでidを指定する
508    pub ex_bpm: HashMap<usize, f64>,
509    /// 停止
510    ///
511    /// 譜面を停止するときに09チャンネルでidを指定する
512    pub stop: HashMap<usize, f64>,
513    /// LNの判定方法
514    ///
515    /// 1 : LN, 始点判定が基準で、最後まで押し切らないとPOOR
516    ///
517    /// 2 : CN, 始点判定と終点判定がある
518    ///
519    /// 3 : HCN, CHの判定に、押している間16分ごとに判定を追加したもの
520    pub ln_mode: Option<i32>,
521    /// LNの指定方法
522    ///
523    /// 1 : 主流の指定方法
524    ///
525    /// メインデータの0以外のidをLNの始点終点の繰り返しとして解析
526    ///
527    /// 2 : 非推奨な指定方法
528    ///
529    /// メインデータの0以外のidが続く部分をLNとして解析
530    ///
531    /// 例
532    ///
533    /// 1 : 0011000000002200
534    /// 2 : 0011111111220000
535    pub ln_type: Option<i32>,
536    /// LNの音を鳴らす終点idの指定
537    ///
538    /// LNTYPE 1 で終点として使うと、音が鳴るようになる
539    pub ln_object: HashSet<usize>,
540    /// 音声ファイル
541    ///
542    /// 詳細はメインデータ等に記載
543    pub wav: HashMap<usize, &'a str>,
544    /// 画像ファイル
545    ///
546    /// 詳細はメインデータ等に記載
547    pub bmp: HashMap<usize, &'a str>,
548    /// BMS制作者のホームページ
549    pub url: Option<&'a str>,
550    /// BMS制作者のEメール
551    pub email: Option<&'a str>,
552    /// 譜面速度
553    pub scroll: HashMap<usize, f64>,
554    /// 譜面速度
555    ///
556    /// SCROLLと違って線形補間がされる
557    pub speed: HashMap<usize, f64>,
558    /// 曲選択時に流れる音声
559    ///
560    /// ここで指定されていなければ、previewと名前が付いた音声を流すのが主流
561    pub preview: Option<&'a str>,
562    /// あまり使われないコマンド
563    pub uncommon: UncommonHeader<'a>,
564    /// 非推奨のコマンド
565    pub deprecated: DeprecatedHeader<'a>,
566}
567
568/// 一小節ごとのメインデータ
569#[derive(Debug, PartialEq)]
570pub struct MainData<'a> {
571    pub bgm: Vec<Vec<usize>>,
572    pub length: f64,
573    pub bpm: Vec<&'a [Option<f64>]>,
574    pub bga: Vec<Vec<usize>>,
575    pub bga_poor: Vec<Vec<usize>>,
576    pub bga_layer: Vec<Vec<usize>>,
577    pub ex_bpm: Vec<Vec<usize>>,
578    pub stop: Vec<Vec<usize>>,
579    pub bga_layer2: Vec<Vec<usize>>,
580    pub bga_alpha: Vec<&'a [u8]>,
581    pub bga_layer_alpha: Vec<&'a [u8]>,
582    pub bga_layer2_alpha: Vec<&'a [u8]>,
583    pub bga_poor_alpha: Vec<&'a [u8]>,
584    pub note: HashMap<usize, Vec<Vec<usize>>>,
585    pub invisible_note: HashMap<usize, Vec<Vec<usize>>>,
586    pub long_note: HashMap<usize, Vec<Vec<usize>>>,
587    pub text: Vec<Vec<usize>>,
588    pub ex_rank: Vec<Vec<usize>>,
589    pub bga_argb: Vec<Vec<usize>>,
590    pub bga_layer_argb: Vec<Vec<usize>>,
591    pub bga_layer2_argb: Vec<Vec<usize>>,
592    pub bga_poor_argb: Vec<Vec<usize>>,
593    pub switch_bga: Vec<Vec<usize>>,
594    pub option: Vec<Vec<usize>>,
595    pub landmine: HashMap<usize, Vec<&'a [f64]>>,
596    pub scroll: Vec<Vec<usize>>,
597    pub speed: Vec<Vec<usize>>,
598
599    pub other: Vec<(usize, &'a str)>,
600}
601impl Default for MainData<'_> {
602    fn default() -> Self {
603        MainData {
604            bgm: vec![],
605            length: 1.0,
606            bpm: vec![],
607            bga: vec![],
608            bga_poor: vec![],
609            bga_layer: vec![],
610            ex_bpm: vec![],
611            stop: vec![],
612            bga_layer2: vec![],
613            bga_alpha: vec![],
614            bga_layer_alpha: vec![],
615            bga_layer2_alpha: vec![],
616            bga_poor_alpha: vec![],
617            note: HashMap::new(),
618            invisible_note: HashMap::new(),
619            long_note: HashMap::new(),
620            text: vec![],
621            ex_rank: vec![],
622            bga_argb: vec![],
623            bga_layer_argb: vec![],
624            bga_layer2_argb: vec![],
625            bga_poor_argb: vec![],
626            switch_bga: vec![],
627            option: vec![],
628            landmine: HashMap::new(),
629            scroll: vec![],
630            speed: vec![],
631            other: vec![],
632        }
633    }
634}
635
636/// あまり使われないヘッダー
637#[derive(Default, Debug, PartialEq)]
638pub struct UncommonHeader<'a> {
639    /// メインデータで指定可能な判定幅の設定
640    ///
641    /// 値はDefExRankと同じ扱いをすることが主流だが、DefExRankと同じように判定幅が実装依存
642    pub ex_rank: HashMap<usize, f64>,
643    pub character_file: Option<&'a str>,
644    /// 譜面制作者
645    pub maker: Option<&'a str>,
646    /// 曲選択時に表示するコメント
647    pub comment: Vec<&'a str>,
648    /// プレイ中に表示するテキスト
649    pub text: HashMap<usize, &'a str>,
650    /// 音声ファイルを読み込むときに参照するフォルダ
651    pub path_wav: Option<&'a str>,
652    pub stp: Vec<(usize, u32, f64)>,
653    pub oct_fp: bool,
654    pub option: Vec<(&'a str, &'a str)>,
655    pub change_option: HashMap<usize, (&'a str, &'a str)>,
656    pub wav_command: Vec<(i32, usize, f64)>,
657    pub ex_wav: HashMap<usize, (&'a [Option<f64>; 3], &'a str)>,
658    pub cdda: Option<u32>,
659    pub midi_file: Option<&'a str>,
660    pub ex_bmp: HashMap<usize, (&'a [u8; 4], &'a str)>,
661    pub bga: HashMap<usize, (usize, &'a [[f64; 2]; 3])>,
662    pub at_bga: HashMap<usize, (usize, &'a [[f64; 2]; 3])>,
663    pub poor_bga: Option<i32>,
664    pub argb: HashMap<usize, &'a [u8; 4]>,
665    pub video_file: Option<&'a str>,
666    pub video_fps: Option<f64>,
667    pub video_colors: Option<u32>,
668    pub video_delay: Option<u32>,
669    pub movie: Option<&'a str>,
670    pub ex_character: Option<ExCharacter<'a>>,
671    /// その他のコマンド
672    pub other: Vec<(&'a str, &'a str)>,
673}
674
675/// Extended-Characterファイル
676#[derive(Debug, PartialEq)]
677pub struct ExCharacter<'a> {
678    pub sprite_num: u32,
679    pub bmp: usize,
680    pub trim_rect: &'a [[f64; 2]; 2],
681    pub offset: Option<&'a [f64; 2]>,
682    pub abs_pos: Option<&'a [f64; 2]>,
683}
684
685/// 非推奨のヘッダー
686#[derive(Default, Debug, PartialEq)]
687pub struct DeprecatedHeader<'a> {
688    /// プレイ方法
689    ///
690    /// 主にメインデータから解析するのが主流
691    pub player: Option<PlayType>,
692    /// スクロール速度の基準BPM
693    ///
694    /// オプション側で基準とするBPMを最大、最小、最長、最初のBPMに合わせられるようにするべき
695    pub base_bpm: Option<f64>,
696    /// キーを押したときに表示するBGA
697    ///
698    /// 試験的に追加された
699    pub switch_bga:
700        HashMap<usize, (f64, f64, usize, bool, &'a [u8; 4], &'a [Channel])>,
701    /// ビデオの再生位置を調整
702    ///
703    /// 提案されたプレイヤーで削除済み
704    pub seek: HashMap<usize, f64>,
705}
706
707/// プレイ方式
708#[derive(Debug, PartialEq)]
709pub enum PlayType {
710    SinglePlay,
711    CouplePlay,
712    DoublePlay,
713    BattlePlay,
714}