Files
STARTLINER/rust/src/model/patch.rs
2025-04-13 18:15:41 +00:00

162 lines
5.7 KiB
Rust

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<String, PatchSelectionData>);
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PatchSelectionData {
Enabled,
Number(i8),
Hex(Vec<u8>)
}
#[derive(Default)]
pub struct PatchFileVec(pub Vec<PatchFile>);
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct PatchFile(pub Vec<PatchList>);
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct PatchList {
pub filename: String,
pub version: String,
pub sha256: String,
pub patches: Vec<Patch>
}
#[derive(Clone, Debug)]
pub struct Patch {
pub id: String,
pub name: String,
pub tooltip: Option<String>,
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<NormalPatchField>
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct NormalPatchField {
pub offset: u64,
pub off: Vec<u8>,
pub on: Vec<u8>
}
#[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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
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<u8> = Vec::new();
let mut on_list: Vec<u8> = 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?
})
}
}