use std::collections::HashSet; use serde::Deserialize; use strum::{EnumIter, IntoEnumIterator, EnumProperty, EnumIs}; #[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, EnumIs)] 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", name_en = "Ichika Hoshino"))] Ichika = 1, #[strum(props(prefix = "saki", name_en = "Saki Tenma"))] Saki = 2, #[strum(props(prefix = "honami", name_en = "Honami Mochizuki"))] Honami = 3, #[strum(props(prefix = "shiho", name_en = "Shiho Hinomori"))] Shiho = 4, // MORE MORE JUMP! #[strum(props(prefix = "minori", name_en = "Minori Hanasato"))] Minori = 5, #[strum(props(prefix = "haruka", name_en = "Haruka Kiritani"))] Haruka = 6, #[strum(props(prefix = "airi", name_en = "Airi Momoi"))] Airi = 7, #[strum(props(prefix = "shizuku", name_en = "Shizuku Hinomori"))] Shizuku = 8, // Vivid BAD SQUAD #[strum(props(prefix = "kohane", name_en = "Kohane Azusawa"))] Kohane = 9, #[strum(props(prefix = "an", name_en = "An Shiraishi"))] An = 10, #[strum(props(prefix = "akito", name_en = "Akito Shinonome"))] Akito = 11, #[strum(props(prefix = "toya", name_en = "Toya Aoyagi"))] Toya = 12, // Wonderlands x Showtime #[strum(props(prefix = "tsukasa", name_en = "Tsukasa Tenma"))] Tsukasa = 13, #[strum(props(prefix = "emu", name_en = "Emu Otori"))] Emu = 14, #[strum(props(prefix = "nene", name_en = "Nene Kusanagi"))] Nene = 15, #[strum(props(prefix = "rui", name_en = "Rui Kamishiro"))] Rui = 16, // Nightcord at 25:00 #[strum(props(prefix = "kanade", name_en = "Kanade Yoisaki"))] Kanade = 17, #[strum(props(prefix = "mafuyu", name_en = "Mafuyu Asahina"))] Mafuyu = 18, #[strum(props(prefix = "ena", name_en = "Ena Shinonome"))] Ena = 19, #[strum(props(prefix = "mizuki", name_en = "Mizuki Akiyama"))] Mizuki = 20, // VOCALOID #[strum(props(prefix = "miku", name_en = "Hatsune Miku"))] Miku = 21, #[strum(props(prefix = "rin", name_en = "Kagamine Rin"))] Rin = 22, #[strum(props(prefix = "len", name_en = "Kagamine Len"))] Len = 23, #[strum(props(prefix = "luka", name_en = "Megurine Luka"))] Luka = 24, #[strum(props(prefix = "meiko", name_en = "MEIKO"))] MEIKO = 25, #[strum(props(prefix = "kaito", name_en = "KAITO"))] KAITO = 26, // "Game Character Unit ID" // Basically just, character IDs associated to a particular group // - Miku #[strum(props(prefix = "ln_miku", name_en = "Leo / Need Miku"))] LnMiku = 27, #[strum(props(prefix = "mmj_miku", name_en = "MORE MORE JUMP! Miku"))] MmjMiku = 28, #[strum(props(prefix = "vbs_miku", name_en = "Vivid BAD SQUAD Miku"))] VbsMiku = 29, #[strum(props(prefix = "wxs_miku", name_en = "Wonderlands x Showtime Miku"))] WxsMiku = 30, #[strum(props(prefix = "n25_miku", name_en = "Nightcord at 25:00 Miku"))] N25Miku = 31, // - Rin #[strum(props(prefix = "ln_rin", name_en = "Leo / Need Rin"))] LnRin = 32, #[strum(props(prefix = "mmj_rin", name_en = "MORE MORE JUMP! Rin"))] MmjRin = 33, #[strum(props(prefix = "vbs_rin", name_en = "Vivid BAD SQUAD Rin"))] VbsRin = 34, #[strum(props(prefix = "wxs_rin", name_en = "Wonderlands x Showtime Rin"))] WxsRin = 35, #[strum(props(prefix = "n25_rin", name_en = "Nightcord at 25:00 Rin"))] N25Rin = 36, // - Len #[strum(props(prefix = "ln_len", name_en = "Leo / Need Len"))] LnLen = 37, #[strum(props(prefix = "mmj_len", name_en = "MORE MORE JUMP! Len"))] MmjLen = 38, #[strum(props(prefix = "vbs_len", name_en = "Vivid BAD SQUAD Len"))] VbsLen = 39, #[strum(props(prefix = "wxs_len", name_en = "Wonderlands x Showtime Len"))] WxsLen = 40, #[strum(props(prefix = "n25_len", name_en = "Nightcord at 25:00 Len"))] N25Len = 41, // - Luka #[strum(props(prefix = "ln_luka", name_en = "Leo / Need Luka"))] LnLuka = 42, #[strum(props(prefix = "mmj_luka", name_en = "MORE MORE JUMP! Luka"))] MmjLuka = 43, #[strum(props(prefix = "vbs_luka", name_en = "Vivid BAD SQUAD Luka"))] VbsLuka = 44, #[strum(props(prefix = "wxs_luka", name_en = "Wonderlands x Showtime Luka"))] WxsLuka = 45, #[strum(props(prefix = "n25_luka", name_en = "Nightcord at 25:00 Luka"))] N25Luka = 46, // - MEIKO #[strum(props(prefix = "ln_meiko", name_en = "Leo / Need MEIKO"))] LnMEIKO = 47, #[strum(props(prefix = "mmj_meiko", name_en = "MORE MORE JUMP! MEIKO"))] MmjMEIKO = 48, #[strum(props(prefix = "vbs_meiko", name_en = "Vivid BAD SQUAD MEIKO"))] VbsMEIKO = 49, #[strum(props(prefix = "wxs_meiko", name_en = "Wonderlands x Showtime MEIKO"))] WxsMEIKO = 50, #[strum(props(prefix = "n25_meiko", name_en = "Nightcord at 25:00 MEIKO"))] N25MEIKO = 51, // - KAITO #[strum(props(prefix = "ln_kaito", name_en = "Leo / Need KAITO"))] LnKAITO = 52, #[strum(props(prefix = "mmj_kaito", name_en = "MORE MORE JUMP! KAITO"))] MmjKAITO = 53, #[strum(props(prefix = "vbs_kaito", name_en = "Vivid BAD SQUAD KAITO"))] VbsKAITO = 54, #[strum(props(prefix = "wxs_kaito", name_en = "Wonderlands x Showtime KAITO"))] WxsKAITO = 55, #[strum(props(prefix = "n25_kaito", name_en = "Nightcord at 25:00 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 { }