1#![cfg(feature = "minor-command")]
2
3use crate::bms::command::time::ObjTime;
4use std::time::Duration;
5
6use super::{ObjId, graphics::Argb};
7
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct ExWavPan(i64);
14
15impl ExWavPan {
16 #[must_use]
19 pub fn new(value: i64) -> Option<Self> {
20 (-10000..=10000).contains(&value).then_some(Self(value))
21 }
22
23 #[must_use]
25 pub fn value(self) -> i64 {
26 self.0
27 }
28
29 #[must_use]
31 pub const fn default() -> Self {
32 Self(0)
33 }
34}
35
36impl TryFrom<i64> for ExWavPan {
37 type Error = i64;
38
39 fn try_from(value: i64) -> std::result::Result<Self, Self::Error> {
40 Self::new(value).ok_or(value.clamp(-10000, 10000))
41 }
42}
43
44#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct ExWavVolume(i64);
50
51impl ExWavVolume {
52 #[must_use]
55 pub fn new(value: i64) -> Option<Self> {
56 (-10000..=0).contains(&value).then_some(Self(value))
57 }
58
59 #[must_use]
61 pub fn value(self) -> i64 {
62 self.0
63 }
64
65 #[must_use]
67 pub const fn default() -> Self {
68 Self(0)
69 }
70}
71
72impl TryFrom<i64> for ExWavVolume {
73 type Error = i64;
74
75 fn try_from(value: i64) -> std::result::Result<Self, Self::Error> {
76 Self::new(value).ok_or(value.clamp(-10000, 0))
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub struct ExWavFrequency(u64);
85
86impl ExWavFrequency {
87 const MIN_FREQUENCY: u64 = 100;
88 const MAX_FREQUENCY: u64 = 100_000;
89
90 #[must_use]
93 pub fn new(value: u64) -> Option<Self> {
94 (Self::MIN_FREQUENCY..=Self::MAX_FREQUENCY)
95 .contains(&value)
96 .then_some(Self(value))
97 }
98
99 #[must_use]
101 pub fn value(self) -> u64 {
102 self.0
103 }
104}
105
106impl TryFrom<u64> for ExWavFrequency {
107 type Error = u64;
108
109 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
110 Self::new(value).ok_or(value.clamp(Self::MIN_FREQUENCY, Self::MAX_FREQUENCY))
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct StpEvent {
118 pub time: ObjTime,
120 pub duration: Duration,
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132pub struct WavCmdEvent {
133 pub param: WavCmdParam,
135 pub wav_index: ObjId,
137 pub value: u32,
139}
140
141#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152pub struct SwBgaEvent {
153 pub frame_rate: u32,
155 pub total_time: u32,
157 pub line: u8,
159 pub loop_mode: bool,
161 pub argb: Argb,
163 pub pattern: String,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
177#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
178pub struct ExtChrEvent {
179 pub sprite_num: i32,
181 pub bmp_num: i32,
183 pub start_x: i32,
185 pub start_y: i32,
187 pub end_x: i32,
189 pub end_y: i32,
191 pub offset_x: Option<i32>,
193 pub offset_y: Option<i32>,
195 pub abs_x: Option<i32>,
197 pub abs_y: Option<i32>,
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
207#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
208pub enum WavCmdParam {
209 Pitch,
211 Volume,
213 Time,
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_exwav_pan_try_from() {
223 assert!(ExWavPan::try_from(0).is_ok());
225 assert!(ExWavPan::try_from(10000).is_ok());
226 assert!(ExWavPan::try_from(-10000).is_ok());
227 assert!(ExWavPan::try_from(5000).is_ok());
228 assert!(ExWavPan::try_from(-5000).is_ok());
229
230 assert!(ExWavPan::try_from(10001).is_err());
232 assert!(ExWavPan::try_from(-10001).is_err());
233 assert!(ExWavPan::try_from(i64::MAX).is_err());
234 assert!(ExWavPan::try_from(i64::MIN).is_err());
235 }
236
237 #[test]
238 fn test_exwav_volume_try_from() {
239 assert!(ExWavVolume::try_from(0).is_ok());
241 assert!(ExWavVolume::try_from(-10000).is_ok());
242 assert!(ExWavVolume::try_from(-5000).is_ok());
243
244 assert!(ExWavVolume::try_from(1).is_err());
246 assert!(ExWavVolume::try_from(-10001).is_err());
247 assert!(ExWavVolume::try_from(i64::MAX).is_err());
248 assert!(ExWavVolume::try_from(i64::MIN).is_err());
249 }
250
251 #[test]
252 fn test_exwav_frequency_try_from() {
253 assert!(ExWavFrequency::try_from(100).is_ok());
255 assert!(ExWavFrequency::try_from(100000).is_ok());
256 assert!(ExWavFrequency::try_from(50000).is_ok());
257
258 assert!(ExWavFrequency::try_from(99).is_err());
260 assert!(ExWavFrequency::try_from(100001).is_err());
261 assert!(ExWavFrequency::try_from(0).is_err());
262 assert!(ExWavFrequency::try_from(u64::MAX).is_err());
263 }
264
265 #[test]
266 fn test_exwav_values() {
267 let pan = ExWavPan::try_from(5000).unwrap();
269 assert_eq!(pan.value(), 5000);
270
271 let volume = ExWavVolume::try_from(-5000).unwrap();
272 assert_eq!(volume.value(), -5000);
273
274 let frequency = ExWavFrequency::try_from(48000).unwrap();
275 assert_eq!(frequency.value(), 48000);
276 }
277
278 #[test]
279 fn test_exwav_defaults() {
280 assert_eq!(ExWavPan::default().value(), 0);
281 assert_eq!(ExWavVolume::default().value(), 0);
282 }
283}