use std::collections::HashSet; use serde::Deserialize; use strum::{EnumIter, IntoEnumIterator, EnumProperty}; #[derive(Clone, Debug, Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RawStamp { id: usize, seq: usize, name: String, description: Option, stamp_type: String, assetbundle_name: String, balloon_assetbundle_name: String, // ignored by us, used in sekai-viewer UI archive_published_at: usize, // ignored by us, used in sekai-viewer UI archive_display_type: Option, character_id_1: Option, character_id_2: Option, game_character_unit_id: Option } impl RawStamp { pub fn to_stamp(self) -> Stamp { use StampCharacter::*; let RawStamp { id, seq, name, description, stamp_type, assetbundle_name, balloon_assetbundle_name, character_id_1, character_id_2, game_character_unit_id, .. } = self; let character = match (character_id_1, character_id_2, game_character_unit_id) { (None, None, None) => Nobody, (Some(character_id), None, None) => Solo{character_id}, (Some(character_id_1), Some(character_id_2), None) => Duo{character_id_1, character_id_2}, (Some(character_id), None, Some(game_character_unit_id)) => SoloExtra{character_id, game_character_unit_id}, (None, None, Some(game_character_unit_id)) => GCUID{game_character_unit_id}, x => { panic!("They added a new character format: {x:?}"); } }; Stamp { id, seq, name, description, stamp_type, assetbundle_name, balloon_assetbundle_name, character } } } #[derive(Clone, Debug, Default)] pub struct Stamp { pub id: usize, pub seq: usize, pub name: String, pub description: Option, pub stamp_type: String, pub assetbundle_name: String, pub balloon_assetbundle_name: String, pub character: StampCharacter } #[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Hash)] pub enum StampCharacter { #[default] Nobody, Solo{character_id: Character}, Duo{character_id_1: Character, character_id_2: Character}, SoloExtra{character_id: Character, game_character_unit_id: Character}, GCUID{game_character_unit_id: Character}, } impl std::fmt::Display for StampCharacter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { Self::Nobody => write!(f, "text"), Self::Solo{character_id} => write!(f, "{}", character_id.get_str("prefix").unwrap()), Self::Duo{character_id_1, character_id_2} => write!(f, "{}_{}", character_id_1.get_str("prefix").unwrap(), character_id_2.get_str("prefix").unwrap()), Self::SoloExtra{game_character_unit_id, ..} => write!(f, "{}", game_character_unit_id.get_str("prefix").unwrap()), Self::GCUID{game_character_unit_id} => write!(f, "text_{}", game_character_unit_id.get_str("prefix").unwrap()) } } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Hash, EnumIter, EnumProperty)] #[repr(u8)] #[serde(from = "u8")] pub enum Character { // Leo / Need #[strum(props(prefix = "ichika"))] Ichika = 1, #[strum(props(prefix = "saki"))] Saki = 2, #[strum(props(prefix = "honami"))] Honami = 3, #[strum(props(prefix = "shiho"))] Shiho = 4, // MORE MORE JUMP! #[strum(props(prefix = "minori"))] Minori = 5, #[strum(props(prefix = "haruka"))] Haruka = 6, #[strum(props(prefix = "airi"))] Airi = 7, #[strum(props(prefix = "shizuku"))] Shizuku = 8, // Vivid BAD SQUAD #[strum(props(prefix = "kohane"))] Kohane = 9, #[strum(props(prefix = "an"))] An = 10, #[strum(props(prefix = "akito"))] Akito = 11, #[strum(props(prefix = "toya"))] Toya = 12, // Wonderlands x Showtime #[strum(props(prefix = "tsukasa"))] Tsukasa = 13, #[strum(props(prefix = "emu"))] Emu = 14, #[strum(props(prefix = "nene"))] Nene = 15, #[strum(props(prefix = "rui"))] Rui = 16, // Nightcord at 25:00 #[strum(props(prefix = "kanade"))] Kanade = 17, #[strum(props(prefix = "mafuyu"))] Mafuyu = 18, #[strum(props(prefix = "ena"))] Ena = 19, #[strum(props(prefix = "mizuki"))] Mizuki = 20, // VOCALOID #[strum(props(prefix = "miku"))] Miku = 21, #[strum(props(prefix = "rin"))] Rin = 22, #[strum(props(prefix = "len"))] Len = 23, #[strum(props(prefix = "luka"))] Luka = 24, #[strum(props(prefix = "meiko"))] MEIKO = 25, #[strum(props(prefix = "kaito"))] KAITO = 26, // "Game Character Unit ID" // Basically just, character IDs associated to a particular group // - Miku #[strum(props(prefix = "ln_miku"))] LnMiku = 27, #[strum(props(prefix = "mmj_miku"))] MmjMiku = 28, #[strum(props(prefix = "vbs_miku"))] VbsMiku = 29, #[strum(props(prefix = "wxs_miku"))] WxsMiku = 30, #[strum(props(prefix = "n25_miku"))] N25Miku = 31, // - Rin #[strum(props(prefix = "ln_rin"))] LnRin = 32, #[strum(props(prefix = "mmj_rin"))] MmjRin = 33, #[strum(props(prefix = "vbs_rin"))] VbsRin = 34, #[strum(props(prefix = "wxs_rin"))] WxsRin = 35, #[strum(props(prefix = "n25_rin"))] N25Rin = 36, // - Len #[strum(props(prefix = "ln_len"))] LnLen = 37, #[strum(props(prefix = "mmj_len"))] MmjLen = 38, #[strum(props(prefix = "vbs_len"))] VbsLen = 39, #[strum(props(prefix = "wxs_len"))] WxsLen = 40, #[strum(props(prefix = "n25_len"))] N25Len = 41, // - Luka #[strum(props(prefix = "ln_luka"))] LnLuka = 42, #[strum(props(prefix = "mmj_luka"))] MmjLuka = 43, #[strum(props(prefix = "vbs_luka"))] VbsLuka = 44, #[strum(props(prefix = "wxs_luka"))] WxsLuka = 45, #[strum(props(prefix = "n25_luka"))] N25Luka = 46, // - MEIKO #[strum(props(prefix = "ln_meiko"))] LnMEIKO = 47, #[strum(props(prefix = "mmj_meiko"))] MmjMEIKO = 48, #[strum(props(prefix = "vbs_meiko"))] VbsMEIKO = 49, #[strum(props(prefix = "wxs_meiko"))] WxsMEIKO = 50, #[strum(props(prefix = "n25_meiko"))] N25MEIKO = 51, // - KAITO #[strum(props(prefix = "ln_kaito"))] LnKAITO = 52, #[strum(props(prefix = "mmj_kaito"))] MmjKAITO = 53, #[strum(props(prefix = "vbs_kaito"))] VbsKAITO = 54, #[strum(props(prefix = "wxs_kaito"))] WxsKAITO = 55, #[strum(props(prefix = "n25_kaito"))] N25KAITO = 56, } impl From for Character { fn from(val: u8) -> Self { for var in Self::iter() { if var as u8 == val { return var; } } panic!("Unknown Character ID: {val}"); } } impl std::fmt::Display for Character { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!(f, "{self:?}") } } impl Stamp { }