Skip to main content

libtw2_snapshot/
format.rs

1use crate::snap::Error;
2use crate::ReadInt;
3use libtw2_buffer::CapacityError;
4use libtw2_packer::IntUnpacker;
5use libtw2_packer::Packer;
6use libtw2_packer::Unpacker;
7use libtw2_warn::wrap;
8use libtw2_warn::Warn;
9use uuid::Uuid;
10
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct DeltaDifferingSizes;
13
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub enum Warning {
16    Packer(libtw2_packer::Warning),
17    NonZeroPadding,
18    DuplicateDelete,
19    DuplicateUpdate,
20    UnknownDelete,
21    DeleteUpdate,
22    NumUpdatedItems,
23    ExcessSnapData,
24    ExcessUuidItemData,
25}
26
27impl From<libtw2_packer::Warning> for Warning {
28    fn from(w: libtw2_packer::Warning) -> Warning {
29        Warning::Packer(w)
30    }
31}
32
33pub use libtw2_gamenet_common::snap_obj::TypeId;
34
35pub const TYPE_ID_EX: u16 = 0;
36pub const OFFSET_EXTENDED_TYPE_ID: u16 = 0x4000;
37
38pub fn key_to_raw_type_id(key: i32) -> u16 {
39    ((key as u32 >> 16) & 0xffff) as u16
40}
41
42pub fn key_to_id(key: i32) -> u16 {
43    ((key as u32) & 0xffff) as u16
44}
45
46pub fn key(raw_type_id: u16, id: u16) -> i32 {
47    (((raw_type_id as u32) << 16) | (id as u32)) as i32
48}
49
50pub fn uuid_to_item_data(uuid: Uuid) -> [i32; 4] {
51    let mut result = [0; 4];
52    for (int, four_bytes) in result.iter_mut().zip(uuid.as_bytes().chunks(4)) {
53        let four_bytes: [u8; 4] = four_bytes.try_into().unwrap();
54        *int = i32::from_be_bytes(four_bytes);
55    }
56    result
57}
58
59pub fn item_data_to_uuid<W: Warn<Warning>>(warn: &mut W, mut data: &[i32]) -> Option<Uuid> {
60    if data.len() > 4 {
61        data = &data[..4];
62        warn.warn(Warning::ExcessUuidItemData);
63    }
64    let data: &[i32; 4] = data.try_into().ok()?;
65
66    let mut result = [0; 16];
67    for (four_bytes, &int) in result.chunks_mut(4).zip(data) {
68        four_bytes.copy_from_slice(&int.to_be_bytes());
69    }
70    Some(Uuid::from_bytes(result))
71}
72
73#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
74pub struct Item<'a> {
75    pub type_id: TypeId,
76    pub id: u16,
77    pub data: &'a [i32],
78}
79
80#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81pub struct RawItem<'a> {
82    pub raw_type_id: u16,
83    pub id: u16,
84    pub data: &'a [i32],
85}
86
87impl<'a> RawItem<'a> {
88    pub fn from_key(key: i32, data: &'a [i32]) -> RawItem<'a> {
89        RawItem {
90            raw_type_id: key_to_raw_type_id(key),
91            id: key_to_id(key),
92            data: data,
93        }
94    }
95    pub fn key(&self) -> i32 {
96        key(self.raw_type_id, self.id)
97    }
98}
99
100pub struct SnapHeader {
101    pub data_size: i32,
102    pub num_items: i32,
103}
104
105impl SnapHeader {
106    pub fn decode<W: Warn<Warning>>(warn: &mut W, p: &mut Unpacker) -> Result<SnapHeader, Error> {
107        Ok(SnapHeader {
108            data_size: libtw2_packer::positive(p.read_int(wrap(warn))?)?,
109            num_items: libtw2_packer::positive(p.read_int(wrap(warn))?)?,
110        })
111    }
112    pub fn decode_obj(p: &mut IntUnpacker) -> Result<SnapHeader, Error> {
113        Ok(SnapHeader {
114            data_size: libtw2_packer::positive(p.read_int()?)?,
115            num_items: libtw2_packer::positive(p.read_int()?)?,
116        })
117    }
118}
119
120#[derive(Clone, Copy, Debug)]
121pub struct DeltaHeader {
122    pub num_deleted_items: i32,
123    pub num_updated_items: i32,
124}
125
126impl DeltaHeader {
127    pub(crate) fn decode_impl<W: Warn<Warning>, R: ReadInt>(
128        warn: &mut W,
129        reader: &mut R,
130    ) -> Result<DeltaHeader, Error> {
131        let result = DeltaHeader {
132            num_deleted_items: libtw2_packer::positive(reader.read_int(warn)?)?,
133            num_updated_items: libtw2_packer::positive(reader.read_int(warn)?)?,
134        };
135        if reader.read_int(warn)? != 0 {
136            warn.warn(Warning::NonZeroPadding);
137        }
138        Ok(result)
139    }
140    pub fn decode<W: Warn<Warning>>(warn: &mut W, p: &mut Unpacker) -> Result<DeltaHeader, Error> {
141        DeltaHeader::decode_impl(warn, p)
142    }
143    pub fn decode_obj<W: Warn<Warning>>(
144        warn: &mut W,
145        p: &mut IntUnpacker,
146    ) -> Result<DeltaHeader, Error> {
147        DeltaHeader::decode_impl(warn, p)
148    }
149    pub fn encode<'d, 's>(&self, mut p: Packer<'d, 's>) -> Result<&'d [u8], CapacityError> {
150        for int in self.encode_obj() {
151            p.write_int(int)?;
152        }
153        Ok(p.written())
154    }
155    pub fn encode_obj(&self) -> [i32; 3] {
156        [self.num_deleted_items, self.num_updated_items, 0]
157    }
158}
159
160/// Applies an item delta, which is a format internal to a snapshot delta.
161///
162/// `delta` and `out` must be of equal length.
163///
164/// # Errors
165///
166/// Returns [`DeltaDifferingSizes`] if `in_`'s length is different from `out.len()`.
167///
168/// # Panics
169///
170/// Panics if `delta.len() != out.len()`.
171///
172/// # Examples
173///
174/// ```
175/// use libtw2_snapshot::format::apply_item_delta;
176/// let mut out = [0; 4];
177///
178/// apply_item_delta(Some(&[0, 1337, 42, 0x7fff_ffff]), &[0, 1, -21, 1], &mut out);
179/// assert_eq!(out, [0, 1338, 21, -0x8000_0000]);
180///
181/// apply_item_delta(None, &[0, 1, -21, 1], &mut out);
182/// assert_eq!(out, [0, 1, -21, 1]);
183/// ```
184pub fn apply_item_delta(
185    in_: Option<&[i32]>,
186    delta: &[i32],
187    out: &mut [i32],
188) -> Result<(), DeltaDifferingSizes> {
189    assert!(delta.len() == out.len());
190    match in_ {
191        Some(in_) => {
192            if in_.len() != out.len() {
193                return Err(DeltaDifferingSizes);
194            }
195            for i in 0..out.len() {
196                out[i] = in_[i].wrapping_add(delta[i]);
197            }
198        }
199        None => out.copy_from_slice(delta),
200    }
201    Ok(())
202}
203
204/// Creates an item delta, which is a format internal to a snapshot delta.
205///
206/// `delta` and `out` must be of equal length.
207///
208/// # Errors
209///
210/// Returns [`DeltaDifferingSizes`] if `in_`'s length is different from `out.len()`.
211///
212/// # Panics
213///
214/// Panics if `delta.len() != out.len()`.
215///
216/// # Examples
217///
218/// ```
219/// use libtw2_snapshot::format::create_item_delta;
220/// let mut out = [0; 4];
221///
222/// create_item_delta(Some(&[0, 1337, 42, 0x7fff_ffff]), &[0, 1338, 21, -0x8000_0000], &mut out);
223/// assert_eq!(out, [0, 1, -21, 1]);
224///
225/// create_item_delta(None, &[0, 1338, 21, -0x8000_0000], &mut out);
226/// assert_eq!(out, [0, 1338, 21, -0x8000_0000]);
227/// ```
228pub fn create_item_delta(
229    from: Option<&[i32]>,
230    to: &[i32],
231    out: &mut [i32],
232) -> Result<(), DeltaDifferingSizes> {
233    assert!(to.len() == out.len());
234    match from {
235        Some(from) => {
236            if from.len() != to.len() {
237                return Err(DeltaDifferingSizes);
238            }
239            for i in 0..out.len() {
240                out[i] = to[i].wrapping_sub(from[i]);
241            }
242        }
243        None => out.copy_from_slice(to),
244    }
245    Ok(())
246}