1use crate::command::{base62_to_byte, char_to_base62};
9use std::str::FromStr;
10use thiserror::Error;
11
12use self::mapper::KeyLayoutMapper;
13
14pub mod converter;
15pub mod mapper;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[non_exhaustive]
21pub enum Channel {
22 BgaBase,
24 BgaLayer,
26 BgaPoor,
28 Bgm,
30 BpmChangeU8,
32 BpmChange,
34 Note {
36 channel_id: NoteChannelId,
38 },
39 SectionLen,
41 Stop,
43 Scroll,
45 Speed,
47 Seek,
49 BgaLayer2,
51 BgaBaseOpacity,
53 BgaLayerOpacity,
55 BgaLayer2Opacity,
57 BgaPoorOpacity,
59 BgmVolume,
61 KeyVolume,
63 Text,
65 Judge,
67 BgaBaseArgb,
69 BgaLayerArgb,
71 BgaLayer2Argb,
73 BgaPoorArgb,
75 BgaKeybound,
77 OptionChange,
79}
80
81impl std::fmt::Display for Channel {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "Channel: ")?;
84 match self {
85 Self::BgaBase => write!(f, "BGA"),
86 Self::BgaLayer => write!(f, "BGA_LAYER"),
87 Self::BgaPoor => write!(f, "BGA_POOR"),
88 Self::Bgm => write!(f, "BGM"),
89 Self::BpmChangeU8 => write!(f, "BPM_CHANGE_U8"),
90 Self::BpmChange => write!(f, "BPM_CHANGE"),
91 Self::Note { .. } => write!(f, "NOTE"),
92 Self::SectionLen => write!(f, "SECTION_LEN"),
93 Self::Stop => write!(f, "STOP"),
94 Self::Scroll => write!(f, "SCROLL"),
95 Self::Speed => write!(f, "SPEED"),
96
97 Self::Seek => write!(f, "SEEK"),
98 Self::BgaLayer2 => write!(f, "BGA_LAYER2"),
99
100 Self::BgaBaseOpacity => write!(f, "BGA_BASE_OPACITY"),
101
102 Self::BgaLayerOpacity => write!(f, "BGA_LAYER_OPACITY"),
103
104 Self::BgaLayer2Opacity => write!(f, "BGA_LAYER2_OPACITY"),
105
106 Self::BgaPoorOpacity => write!(f, "BGA_POOR_OPACITY"),
107 Self::BgmVolume => write!(f, "BGM_VOLUME"),
108 Self::KeyVolume => write!(f, "KEY_VOLUME"),
109 Self::Text => write!(f, "TEXT"),
110 Self::Judge => write!(f, "JUDGE"),
111
112 Self::BgaBaseArgb => write!(f, "BGA_BASE_ARGB"),
113
114 Self::BgaLayerArgb => write!(f, "BGA_LAYER_ARGB"),
115
116 Self::BgaLayer2Argb => write!(f, "BGA_LAYER2_ARGB"),
117
118 Self::BgaPoorArgb => write!(f, "BGA_POOR_ARGB"),
119
120 Self::BgaKeybound => write!(f, "BGA_KEYBOUND"),
121
122 Self::OptionChange => write!(f, "CHANGE_OPTION"),
123 }
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130pub enum NoteKind {
131 Visible,
133 Invisible,
135 Long,
137 Landmine,
139}
140
141impl NoteKind {
142 #[must_use]
144 pub const fn is_displayable(self) -> bool {
145 !matches!(self, Self::Invisible)
146 }
147
148 #[must_use]
150 pub const fn is_playable(self) -> bool {
151 matches!(self, Self::Visible | Self::Long)
152 }
153
154 #[must_use]
156 pub const fn is_long(self) -> bool {
157 matches!(self, Self::Long)
158 }
159}
160
161#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub enum PlayerSide {
165 #[default]
167 Player1,
168 Player2,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
174pub enum ChannelIdParseError {
175 #[error("channel id must be exactly 2 ascii characters, got `{0}`")]
177 ExpectedTwoAsciiChars(String),
178 #[error("channel id must be an alpha numeric to parse as base 62, got {0}")]
180 InvalidAsBase62(String),
181}
182
183#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
185#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186pub struct NoteChannelId([u8; 2]);
187
188impl std::fmt::Debug for NoteChannelId {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 f.debug_tuple("ChannelId")
191 .field(&format!("{}{}", self.0[0] as char, self.0[1] as char))
192 .finish()
193 }
194}
195
196impl std::fmt::Display for NoteChannelId {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 write!(f, "{}{}", self.0[0] as char, self.0[1] as char)
199 }
200}
201
202impl TryFrom<[char; 2]> for NoteChannelId {
203 type Error = [char; 2];
204 fn try_from(value: [char; 2]) -> core::result::Result<Self, Self::Error> {
205 Ok(Self([
206 char_to_base62(value[0]).ok_or(value)?,
207 char_to_base62(value[1]).ok_or(value)?,
208 ]))
209 }
210}
211
212impl TryFrom<[u8; 2]> for NoteChannelId {
213 type Error = [u8; 2];
214 fn try_from(value: [u8; 2]) -> core::result::Result<Self, Self::Error> {
215 <Self as TryFrom<[char; 2]>>::try_from([value[0] as char, value[1] as char])
216 .map_err(|_| value)
217 }
218}
219
220impl FromStr for NoteChannelId {
221 type Err = ChannelIdParseError;
222 fn from_str(s: &str) -> Result<Self, Self::Err> {
223 if s.len() != 2 {
224 return Err(ChannelIdParseError::ExpectedTwoAsciiChars(s.to_string()));
225 }
226 let mut chars = s.bytes();
227 let [Some(ch1), Some(ch2), None] = [chars.next(), chars.next(), chars.next()] else {
228 return Err(ChannelIdParseError::ExpectedTwoAsciiChars(s.to_string()));
229 };
230 Self::try_from([ch1, ch2]).map_err(|_| ChannelIdParseError::InvalidAsBase62(s.to_string()))
231 }
232}
233
234impl From<NoteChannelId> for u16 {
235 fn from(value: NoteChannelId) -> Self {
236 base62_to_byte(value.0[0]) as u16 * 62 + base62_to_byte(value.0[1]) as u16
237 }
238}
239
240impl From<NoteChannelId> for u32 {
241 fn from(value: NoteChannelId) -> Self {
242 Into::<u16>::into(value) as u32
243 }
244}
245
246impl From<NoteChannelId> for u64 {
247 fn from(value: NoteChannelId) -> Self {
248 Into::<u16>::into(value) as u64
249 }
250}
251
252impl NoteChannelId {
253 #[must_use]
255 pub fn as_u16(self) -> u16 {
256 self.into()
257 }
258
259 #[must_use]
261 pub const fn bgm() -> Self {
262 Self([b'0', b'1'])
263 }
264
265 #[must_use]
267 pub fn try_into_map<T: KeyLayoutMapper>(self) -> Option<T> {
268 T::from_channel_id(self)
269 }
270}
271
272impl TryFrom<NoteChannelId> for Channel {
273 type Error = NoteChannelId;
274
275 fn try_from(channel_id: NoteChannelId) -> Result<Self, Self::Error> {
276 match channel_id.0 {
277 [b'0', b'1'] => Ok(Channel::Bgm),
278 [b'0', b'2'] => Ok(Channel::SectionLen),
279 [b'0', b'3'] => Ok(Channel::BpmChangeU8),
280 [b'0', b'4'] => Ok(Channel::BgaBase),
281
282 [b'0', b'5'] => Ok(Channel::Seek),
283 [b'0', b'6'] => Ok(Channel::BgaPoor),
284 [b'0', b'7'] => Ok(Channel::BgaLayer),
285 [b'0', b'8'] => Ok(Channel::BpmChange),
286 [b'0', b'9'] => Ok(Channel::Stop),
287 [b'0', b'A'] => Ok(Channel::BgaLayer2),
288
289 [b'0', b'B'] => Ok(Channel::BgaBaseOpacity),
290
291 [b'0', b'C'] => Ok(Channel::BgaLayerOpacity),
292
293 [b'0', b'D'] => Ok(Channel::BgaLayer2Opacity),
294
295 [b'0', b'E'] => Ok(Channel::BgaPoorOpacity),
296 [b'9', b'7'] => Ok(Channel::BgmVolume),
297 [b'9', b'8'] => Ok(Channel::KeyVolume),
298 [b'9', b'9'] => Ok(Channel::Text),
299 [b'A', b'0'] => Ok(Channel::Judge),
300
301 [b'A', b'1'] => Ok(Channel::BgaBaseArgb),
302
303 [b'A', b'2'] => Ok(Channel::BgaLayerArgb),
304
305 [b'A', b'3'] => Ok(Channel::BgaLayer2Argb),
306
307 [b'A', b'4'] => Ok(Channel::BgaPoorArgb),
308
309 [b'A', b'5'] => Ok(Channel::BgaKeybound),
310
311 [b'A', b'6'] => Ok(Channel::OptionChange),
312 [b'S', b'C'] => Ok(Channel::Scroll),
313 [b'S', b'P'] => Ok(Channel::Speed),
314 _ => Err(channel_id),
315 }
316 }
317}
318
319impl From<Channel> for NoteChannelId {
320 fn from(channel: Channel) -> Self {
321 match channel {
322 Channel::Bgm => Self([b'0', b'1']),
323 Channel::SectionLen => Self([b'0', b'2']),
324 Channel::BpmChangeU8 => Self([b'0', b'3']),
325 Channel::BgaBase => Self([b'0', b'4']),
326
327 Channel::Seek => Self([b'0', b'5']),
328 Channel::BgaPoor => Self([b'0', b'6']),
329 Channel::BgaLayer => Self([b'0', b'7']),
330 Channel::BpmChange => Self([b'0', b'8']),
331 Channel::Stop => Self([b'0', b'9']),
332 Channel::BgaLayer2 => Self([b'0', b'A']),
333
334 Channel::BgaBaseOpacity => Self([b'0', b'B']),
335
336 Channel::BgaLayerOpacity => Self([b'0', b'C']),
337
338 Channel::BgaLayer2Opacity => Self([b'0', b'D']),
339
340 Channel::BgaPoorOpacity => Self([b'0', b'E']),
341 Channel::BgmVolume => Self([b'9', b'7']),
342 Channel::KeyVolume => Self([b'9', b'8']),
343 Channel::Text => Self([b'9', b'9']),
344 Channel::Judge => Self([b'A', b'0']),
345
346 Channel::BgaBaseArgb => Self([b'A', b'1']),
347
348 Channel::BgaLayerArgb => Self([b'A', b'2']),
349
350 Channel::BgaLayer2Argb => Self([b'A', b'3']),
351
352 Channel::BgaPoorArgb => Self([b'A', b'4']),
353
354 Channel::BgaKeybound => Self([b'A', b'5']),
355
356 Channel::OptionChange => Self([b'A', b'6']),
357 Channel::Scroll => Self([b'S', b'C']),
358 Channel::Speed => Self([b'S', b'P']),
359 Channel::Note { channel_id } => channel_id,
360 }
361 }
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383#[non_exhaustive]
384pub enum Key {
385 Key(u8),
387 Scratch(u8),
389 FootPedal,
391 FreeZone,
394}
395
396impl Key {
397 #[must_use]
399 pub const fn is_keyxx(&self) -> bool {
400 matches!(self, Self::Key(_))
401 }
402
403 #[must_use]
405 pub const fn key_number(&self) -> Option<u8> {
406 match self {
407 Self::Key(n) => Some(*n),
408 _ => None,
409 }
410 }
411
412 #[must_use]
414 pub const fn scratch_number(&self) -> Option<u8> {
415 match self {
416 Self::Scratch(n) => Some(*n),
417 _ => None,
418 }
419 }
420
421 #[must_use]
423 pub const fn new_key(n: u8) -> Self {
424 Self::Key(n)
425 }
426
427 #[must_use]
429 pub const fn new_scratch(n: u8) -> Self {
430 Self::Scratch(n)
431 }
432}
433
434fn read_channel_general(channel: &str) -> Option<Channel> {
438 let channel_id = channel.parse::<NoteChannelId>().ok()?;
439 Channel::try_from(channel_id).ok()
440}
441
442#[must_use]
444pub fn read_channel(channel: &str) -> Option<Channel> {
445 if let Some(channel) = read_channel_general(channel) {
446 return Some(channel);
447 }
448 let channel_id = channel.parse::<NoteChannelId>().ok()?;
449 Some(Channel::Note { channel_id })
450}