162 lines
5.7 KiB
Rust
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?
|
|
})
|
|
}
|
|
} |