feat: partial support for patches
This commit is contained in:
155
rust/src/model/patch.rs
Normal file
155
rust/src/model/patch.rs
Normal file
@ -0,0 +1,155 @@
|
||||
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)]
|
||||
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 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("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"))?,
|
||||
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("min")
|
||||
.and_then(Value::as_i64)
|
||||
.ok_or_else(|| de::Error::missing_field("min"))?
|
||||
).map_err(|_| de::Error::missing_field("min"))?
|
||||
}),
|
||||
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?
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user