use std::collections::BTreeMap; use serde::{de, Deserialize, Deserializer, Serialize}; use serde::ser::{Serializer, SerializeStruct}; use serde_json::Value; use anyhow::Result; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct PatchSelection(pub BTreeMap); #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum PatchSelectionData { Enabled, Number(i8), Hex(Vec) } #[derive(Default)] pub struct PatchFileVec(pub Vec); #[derive(Deserialize, Serialize, Clone, Debug)] pub struct PatchFile(pub Vec); #[derive(Deserialize, Serialize, Clone, Debug)] pub struct PatchList { pub filename: String, pub version: String, pub sha256: String, pub patches: Vec } #[derive(Clone, Debug)] pub struct Patch { pub id: String, pub name: String, pub tooltip: Option, pub data: PatchData } #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(untagged)] pub enum PatchData { Normal(NormalPatch), Number(NumberPatch), } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct NormalPatch { pub patches: Vec } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct NormalPatchField { pub offset: u64, pub off: Vec, pub on: Vec } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct NumberPatch { pub offset: u64, pub default: i32, pub size: i64, pub min: i32, pub max: i32 } impl Serialize for Patch { fn serialize(&self, serializer: S) -> Result where S: Serializer { let mut state = serializer.serialize_struct("Patch", 7)?; state.serialize_field("id", &self.id)?; state.serialize_field("name", &self.name)?; state.serialize_field("tooltip", &self.tooltip)?; match &self.data { PatchData::Normal(patch) => { state.serialize_field("patches", &patch.patches)?; } PatchData::Number(patch) => { state.serialize_field("type", "number")?; state.serialize_field("offset", &patch.offset)?; state.serialize_field("default", &patch.default)?; state.serialize_field("size", &patch.size)?; state.serialize_field("min", &patch.min)?; state.serialize_field("max", &patch.max)?; } } state.end() } } impl<'de> serde::Deserialize<'de> for Patch { fn deserialize>(d: D) -> Result { let value = Value::deserialize(d)?; let data = Ok(match value.get("type").and_then(Value::as_str) { Some("number") => PatchData::Number(NumberPatch { offset: value.get("offset") .and_then(Value::as_u64) .ok_or_else(|| de::Error::missing_field("offset"))?, default: i32::try_from(value.get("default") .and_then(Value::as_i64) .ok_or_else(|| de::Error::missing_field("default"))? ).map_err(|_| de::Error::missing_field("default"))?, size: value.get("size") .and_then(Value::as_i64) .ok_or_else(|| de::Error::missing_field("size"))?, min: i32::try_from(value.get("min") .and_then(Value::as_i64) .ok_or_else(|| de::Error::missing_field("min"))? ).map_err(|_| de::Error::missing_field("min"))?, max: i32::try_from(value.get("max") .and_then(Value::as_i64) .ok_or_else(|| de::Error::missing_field("max"))? ).map_err(|_| de::Error::missing_field("max"))? }), None => { let mut patches = vec![]; for patch in value.get("patches").and_then(Value::as_array).unwrap() { let mut off_list: Vec = Vec::new(); let mut on_list: Vec = Vec::new(); for off in patch.get("off").and_then(Value::as_array).unwrap() { off_list.push(u8::try_from( off.as_u64().ok_or_else(|| de::Error::missing_field("off"))? ).map_err(|_| de::Error::missing_field("off"))?); } for on in patch.get("on").and_then(Value::as_array).unwrap() { on_list.push(u8::try_from( on.as_u64().ok_or_else(|| de::Error::missing_field("on"))? ).map_err(|_| de::Error::missing_field("on"))?); } patches.push(NormalPatchField { offset: patch.get("offset") .and_then(Value::as_u64) .ok_or_else(|| de::Error::missing_field("offset"))?, off: off_list, on: on_list }) } PatchData::Normal(NormalPatch { patches }) }, Some(&_) => return Err(de::Error::custom("unsupported type")) }); Ok(Patch { id: value.get("id") .and_then(Value::as_str) .ok_or_else(|| de::Error::missing_field("id"))? .to_owned(), name: value.get("name") .and_then(Value::as_str) .ok_or_else(|| de::Error::missing_field("name"))? .to_owned(), tooltip: value.get("tooltip") .and_then(Value::as_str) .and_then(|s| Some(s.to_owned())), data: data? }) } }