1use std::{
2 fmt::{Binary, Display, Formatter, Result as FmtResult},
3 ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, Sub, SubAssign},
4 str::FromStr,
5};
6
7use crate::{
8 error::GameModsLegacyParseError, iter::GameModsLegacyIter, util, Acronym, GameModsIntermode,
9};
10
11#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[repr(transparent)]
48pub struct GameModsLegacy(u32);
49
50#[allow(non_upper_case_globals)]
51impl GameModsLegacy {
52 pub const NoMod: Self = Self::from_bits_retain(0);
53 pub const NoFail: Self = Self::from_bits_retain(1 << 0);
54 pub const Easy: Self = Self::from_bits_retain(1 << 1);
55 pub const TouchDevice: Self = Self::from_bits_retain(1 << 2);
56 pub const Hidden: Self = Self::from_bits_retain(1 << 3);
57 pub const HardRock: Self = Self::from_bits_retain(1 << 4);
58 pub const SuddenDeath: Self = Self::from_bits_retain(1 << 5);
59 pub const DoubleTime: Self = Self::from_bits_retain(1 << 6);
60 pub const Relax: Self = Self::from_bits_retain(1 << 7);
61 pub const HalfTime: Self = Self::from_bits_retain(1 << 8);
62 pub const Nightcore: Self = Self::from_bits_retain((1 << 9) | Self::DoubleTime.bits());
63 pub const Flashlight: Self = Self::from_bits_retain(1 << 10);
64 pub const Autoplay: Self = Self::from_bits_retain(1 << 11);
65 pub const SpunOut: Self = Self::from_bits_retain(1 << 12);
66 pub const Autopilot: Self = Self::from_bits_retain(1 << 13);
67 pub const Perfect: Self = Self::from_bits_retain((1 << 14) | Self::SuddenDeath.bits());
68 pub const Key4: Self = Self::from_bits_retain(1 << 15);
69 pub const Key5: Self = Self::from_bits_retain(1 << 16);
70 pub const Key6: Self = Self::from_bits_retain(1 << 17);
71 pub const Key7: Self = Self::from_bits_retain(1 << 18);
72 pub const Key8: Self = Self::from_bits_retain(1 << 19);
73 pub const FadeIn: Self = Self::from_bits_retain(1 << 20);
74 pub const Random: Self = Self::from_bits_retain(1 << 21);
75 pub const Cinema: Self = Self::from_bits_retain(1 << 22);
76 pub const Target: Self = Self::from_bits_retain(1 << 23);
77 pub const Key9: Self = Self::from_bits_retain(1 << 24);
78 pub const KeyCoop: Self = Self::from_bits_retain(1 << 25);
79 pub const Key1: Self = Self::from_bits_retain(1 << 26);
80 pub const Key3: Self = Self::from_bits_retain(1 << 27);
81 pub const Key2: Self = Self::from_bits_retain(1 << 28);
82 pub const ScoreV2: Self = Self::from_bits_retain(1 << 29);
83 pub const Mirror: Self = Self::from_bits_retain(1 << 30);
84}
85
86impl GameModsLegacy {
87 pub const fn clock_rate(self) -> f64 {
90 if self.contains(Self::DoubleTime) {
91 1.5
92 } else if self.contains(Self::HalfTime) {
93 0.75
94 } else {
95 1.0
96 }
97 }
98
99 pub const fn len(self) -> usize {
110 self.bits().count_ones() as usize
111 - self.contains(Self::Nightcore) as usize
112 - self.contains(Self::Perfect) as usize
113 }
114
115 pub fn iter(self) -> GameModsLegacyIter {
133 self.into_iter()
134 }
135
136 pub fn to_intermode(self) -> GameModsIntermode {
138 GameModsIntermode::from_bits(self.bits())
139 }
140}
141
142impl GameModsLegacy {
143 const fn all() -> Self {
144 Self::from_bits_retain(u32::MAX >> 2)
145 }
146
147 pub const fn bits(self) -> u32 {
151 self.0
152 }
153
154 pub const fn try_from_bits(bits: u32) -> Option<Self> {
158 if Self::from_bits(bits).bits() == bits {
159 Some(Self::from_bits_retain(bits))
160 } else {
161 None
162 }
163 }
164
165 pub const fn from_bits(bits: u32) -> Self {
167 Self::from_bits_retain(bits & Self::all().bits())
168 }
169
170 pub const fn from_bits_retain(bits: u32) -> Self {
174 Self(bits)
175 }
176
177 pub const fn is_empty(self) -> bool {
179 self.bits() == Self::NoMod.bits()
180 }
181
182 pub const fn intersects(self, other: Self) -> bool {
184 self.bits() & other.bits() != 0
185 }
186
187 pub const fn contains(self, other: Self) -> bool {
189 self.bits() & other.bits() == other.bits()
190 }
191
192 pub const fn insert(&mut self, other: Self) {
194 *self = Self::from_bits_retain(self.bits()).union(other);
195 }
196
197 pub const fn remove(&mut self, other: Self) {
202 *self = Self::from_bits_retain(self.bits()).difference(other);
203 }
204
205 #[must_use]
207 pub const fn intersection(self, other: Self) -> Self {
208 Self::from_bits_retain(self.bits() & other.bits())
209 }
210
211 #[must_use]
213 pub const fn union(self, other: Self) -> Self {
214 Self::from_bits_retain(self.bits() | other.bits())
215 }
216
217 #[must_use]
222 pub const fn difference(self, other: Self) -> Self {
223 Self::from_bits_retain(self.bits() & !other.bits())
224 }
225
226 #[must_use]
228 pub const fn symmetric_difference(self, other: Self) -> Self {
229 Self::from_bits_retain(self.bits() ^ other.bits())
230 }
231}
232
233impl Display for GameModsLegacy {
234 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
235 for m in self.into_iter() {
236 let acronym = match m {
237 Self::NoMod => "NM",
238 Self::NoFail => "NF",
239 Self::Easy => "EZ",
240 Self::TouchDevice => "TD",
241 Self::Hidden => "HD",
242 Self::HardRock => "HR",
243 Self::SuddenDeath => "SD",
244 Self::DoubleTime => "DT",
245 Self::Relax => "RX",
246 Self::HalfTime => "HT",
247 Self::Nightcore => "NC",
248 Self::Flashlight => "FL",
249 Self::SpunOut => "SO",
250 Self::Autopilot => "AP",
251 Self::Perfect => "PF",
252 Self::FadeIn => "FI",
253 Self::Random => "RD",
254 Self::Target => "TP",
255 Self::ScoreV2 => "V2",
256 Self::Mirror => "MR",
257 Self::Key1 => "1K",
258 Self::Key2 => "2K",
259 Self::Key3 => "3K",
260 Self::Key4 => "4K",
261 Self::Key5 => "5K",
262 Self::Key6 => "6K",
263 Self::Key7 => "7K",
264 Self::Key8 => "8K",
265 Self::Key9 => "9K",
266 Self::Autoplay | Self::Cinema | Self::KeyCoop => "",
267 _ => unreachable!(),
268 };
269
270 f.write_str(acronym)?;
271 }
272
273 Ok(())
274 }
275}
276
277impl Binary for GameModsLegacy {
278 fn fmt(&self, f: &mut Formatter) -> FmtResult {
279 Binary::fmt(&self.0, f)
280 }
281}
282
283impl BitOr for GameModsLegacy {
284 type Output = Self;
285
286 fn bitor(self, other: GameModsLegacy) -> Self {
288 self.union(other)
289 }
290}
291
292impl BitOrAssign for GameModsLegacy {
293 fn bitor_assign(&mut self, other: Self) {
295 self.insert(other);
296 }
297}
298
299impl BitXor for GameModsLegacy {
300 type Output = Self;
301
302 fn bitxor(self, other: Self) -> Self {
304 self.symmetric_difference(other)
305 }
306}
307
308impl BitAnd for GameModsLegacy {
309 type Output = Self;
310
311 fn bitand(self, other: Self) -> Self {
313 self.intersection(other)
314 }
315}
316
317impl BitAndAssign for GameModsLegacy {
318 fn bitand_assign(&mut self, other: Self) {
320 *self = Self::from_bits_retain(self.bits()).intersection(other);
321 }
322}
323
324impl Sub for GameModsLegacy {
325 type Output = Self;
326
327 fn sub(self, other: Self) -> Self {
332 self.difference(other)
333 }
334}
335
336impl SubAssign for GameModsLegacy {
337 fn sub_assign(&mut self, other: Self) {
342 self.remove(other);
343 }
344}
345
346impl Extend<GameModsLegacy> for GameModsLegacy {
347 fn extend<T: IntoIterator<Item = Self>>(&mut self, iterator: T) {
349 for item in iterator {
350 self.insert(item);
351 }
352 }
353}
354
355impl FromIterator<GameModsLegacy> for GameModsLegacy {
356 fn from_iter<T: IntoIterator<Item = Self>>(iterator: T) -> Self {
358 let mut mods = Self::NoMod;
359 mods.extend(iterator);
360
361 mods
362 }
363}
364
365impl From<GameModsLegacy> for u32 {
366 fn from(mods: GameModsLegacy) -> Self {
367 mods.bits()
368 }
369}
370
371impl FromStr for GameModsLegacy {
372 type Err = GameModsLegacyParseError;
373
374 fn from_str(s: &str) -> Result<Self, Self::Err> {
375 let mut res = Self::default();
376 let upper = util::to_uppercase(s);
377
378 for m in util::cut(&upper, 2) {
379 let m = match m {
380 "NM" => Self::NoMod,
381 "NF" => Self::NoFail,
382 "EZ" => Self::Easy,
383 "TD" => Self::TouchDevice,
384 "HD" => Self::Hidden,
385 "HR" => Self::HardRock,
386 "SD" => Self::SuddenDeath,
387 "DT" => Self::DoubleTime,
388 "RX" | "RL" => Self::Relax,
389 "HT" => Self::HalfTime,
390 "NC" => Self::Nightcore,
391 "FL" => Self::Flashlight,
392 "SO" => Self::SpunOut,
393 "AP" => Self::Autopilot,
394 "PF" => Self::Perfect,
395 "FI" => Self::FadeIn,
396 "RD" => Self::Random,
397 "TP" => Self::Target,
398 "V2" => Self::ScoreV2,
399 "MR" => Self::Mirror,
400 "1K" | "K1" => Self::Key1,
401 "2K" | "K2" => Self::Key2,
402 "3K" | "K3" => Self::Key3,
403 "4K" | "K4" => Self::Key4,
404 "5K" | "K5" => Self::Key5,
405 "6K" | "K6" => Self::Key6,
406 "7K" | "K7" => Self::Key7,
407 "8K" | "K8" => Self::Key8,
408 "9K" | "K9" => Self::Key9,
409 _ => return Err(GameModsLegacyParseError { mods: Box::from(s) }),
410 };
411
412 res.insert(m);
413 }
414
415 Ok(res)
416 }
417}
418
419impl TryFrom<Acronym> for GameModsLegacy {
420 type Error = GameModsLegacyParseError;
421
422 fn try_from(acronym: Acronym) -> Result<Self, Self::Error> {
423 acronym.as_str().parse()
424 }
425}
426
427impl IntoIterator for GameModsLegacy {
428 type Item = GameModsLegacy;
429 type IntoIter = GameModsLegacyIter;
430
431 fn into_iter(self) -> Self::IntoIter {
432 GameModsLegacyIter::new(self)
433 }
434}
435
436#[cfg(feature = "serde")]
437#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "serde")))]
438const _: () = {
439 use serde::{
440 de::{Deserialize, Deserializer, Error as DeError, SeqAccess, Visitor},
441 ser::{Serialize, Serializer},
442 };
443
444 use crate::serde::{GameModRaw, GameModRawSeed, MaybeOwnedStr, BITFLAGS_U32};
445
446 impl<'de> Deserialize<'de> for GameModsLegacy {
447 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
448 struct GameModsLegacyVisitor;
449
450 impl<'de> Visitor<'de> for GameModsLegacyVisitor {
451 type Value = GameModsLegacy;
452
453 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
454 f.write_str("integer bitflags, mod acronyms, or a sequence of mods")
455 }
456
457 fn visit_i64<E: DeError>(self, v: i64) -> Result<Self::Value, E> {
458 let bits = u32::try_from(v).map_err(|_| DeError::custom(BITFLAGS_U32))?;
459
460 self.visit_u32(bits)
461 }
462
463 fn visit_u32<E: DeError>(self, v: u32) -> Result<Self::Value, E> {
464 Ok(GameModsLegacy::from_bits(v))
465 }
466
467 fn visit_u64<E: DeError>(self, v: u64) -> Result<Self::Value, E> {
468 let bits = u32::try_from(v).map_err(|_| DeError::custom(BITFLAGS_U32))?;
469
470 self.visit_u32(bits)
471 }
472
473 fn visit_str<E: DeError>(self, v: &str) -> Result<Self::Value, E> {
474 v.parse().map_err(DeError::custom)
475 }
476
477 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
478 let mut mods = GameModsLegacy::NoMod;
479
480 let seed = GameModRawSeed {
481 deny_unknown_fields: true,
482 };
483
484 while let Some(raw) = seq.next_element_seed(seed)? {
485 fn try_acronym_to_gamemod<E: DeError>(
486 acronym: &MaybeOwnedStr<'_>,
487 ) -> Result<GameModsLegacy, E> {
488 GameModsLegacy::from_str(acronym.as_str()).map_err(DeError::custom)
489 }
490
491 let gamemod = match raw {
492 GameModRaw::Bits(bits) => GameModsLegacy::from_bits(bits),
493 GameModRaw::Acronym(acronym) => try_acronym_to_gamemod(&acronym)?,
494 GameModRaw::Full { acronym, .. } => try_acronym_to_gamemod(&acronym)?,
495 };
496
497 mods |= gamemod;
498 }
499
500 Ok(mods)
501 }
502 }
503
504 d.deserialize_any(GameModsLegacyVisitor)
505 }
506 }
507
508 impl Serialize for GameModsLegacy {
509 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
510 s.serialize_u32(self.bits())
511 }
512 }
513};
514
515#[cfg(feature = "rkyv")]
516#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "rkyv")))]
517const _: () = {
518 use rkyv::{
519 primitive::ArchivedU32, rancor::Fallible, Archive, Archived, Deserialize, Place, Serialize,
520 };
521
522 impl Archive for GameModsLegacy {
523 type Archived = Archived<u32>;
524 type Resolver = ();
525
526 fn resolve(&self, resolver: (), out: Place<Self::Archived>) {
527 self.bits().resolve(resolver, out);
528 }
529 }
530
531 impl<S: Fallible + ?Sized> Serialize<S> for GameModsLegacy {
532 fn serialize(&self, s: &mut S) -> Result<(), S::Error> {
533 self.bits().serialize(s)
534 }
535 }
536
537 impl<D: Fallible + ?Sized> Deserialize<GameModsLegacy, D> for ArchivedU32 {
538 fn deserialize(&self, _: &mut D) -> Result<GameModsLegacy, D::Error> {
539 Ok(GameModsLegacy::from_bits_retain(self.to_native()))
540 }
541 }
542};
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547
548 #[test]
549 fn try_from_str() {
550 assert_eq!(
551 GameModsLegacy::from_str("Nm").unwrap(),
552 GameModsLegacy::NoMod
553 );
554 assert_eq!(
555 GameModsLegacy::from_str("hD").unwrap(),
556 GameModsLegacy::Hidden
557 );
558
559 let mods = GameModsLegacy::from_bits(24);
560 assert_eq!(GameModsLegacy::from_str("HRhD").unwrap(), mods);
561 assert!(GameModsLegacy::from_str("HHDR").is_err());
562 }
563
564 #[test]
565 fn iter() {
566 let mut iter = GameModsLegacy::default().iter();
567 assert_eq!(iter.next(), Some(GameModsLegacy::NoMod));
568 assert_eq!(iter.next(), None);
569
570 let mut iter = GameModsLegacy::from_bits(584).iter();
571 assert_eq!(iter.next(), Some(GameModsLegacy::Hidden));
572 assert_eq!(iter.next(), Some(GameModsLegacy::Nightcore));
573 assert_eq!(iter.next(), None);
574 }
575
576 #[cfg(feature = "serde")]
577 mod serde {
578 use super::*;
579
580 #[test]
581 fn deser_str() {
582 let json = r#""HDHR""#;
583 let mods = serde_json::from_str::<GameModsLegacy>(json).unwrap();
584 let expected = GameModsLegacy::Hidden | GameModsLegacy::HardRock;
585
586 assert_eq!(mods, expected);
587 }
588
589 #[test]
590 fn deser_bits() {
591 let json = "1096";
592 let mods = serde_json::from_str::<GameModsLegacy>(json).unwrap();
593 let expected =
594 GameModsLegacy::Hidden | GameModsLegacy::DoubleTime | GameModsLegacy::Flashlight;
595
596 assert_eq!(mods, expected);
597 }
598
599 #[test]
600 fn deser_seq() {
601 let json = r#"["NF", 2, { "acronym": "HT", "settings": { "any": true } }]"#;
602 let mods = serde_json::from_str::<GameModsLegacy>(json).unwrap();
603 let expected = GameModsLegacy::NoFail | GameModsLegacy::Easy | GameModsLegacy::HalfTime;
604
605 assert_eq!(mods, expected);
606 }
607 }
608}