flips_sys/
bps.rs

1//! Bindings to `libbps.h` to work with BPS patches.
2
3#![allow(bad_style)]
4
5use super::mem;
6
7#[repr(C)]
8#[derive(Clone, Debug, PartialEq)]
9pub enum bpserror {
10    /// Patch applied or created successfully.
11    bps_ok,
12    /// You attempted to apply a patch to its output.
13    bps_to_output,
14    /// This is not the intended input file for this patch.
15    bps_not_this,
16    /// This is not a BPS patch, or it's malformed somehow.
17    bps_broken,
18    /// The patch could not be read.
19    bps_io,
20    /// The input files are identical.
21    bps_identical,
22    /// Somehow, you're asking for something a `size_t` can't represent.
23    bps_too_big,
24    /// Memory allocation failure.
25    bps_out_of_mem,
26    /// Patch creation was canceled.
27    bps_canceled,
28    /// Unused, just kill GCC warning.
29    bps_shut_up_gcc,
30}
31
32#[link(name="bps")]
33extern "C" {
34
35    /// Applies the BPS patch to the ROM in `in_` and puts it in `out`.
36    ///
37    /// Metadata, if present and requested, (`metadata` is not NULL), is also
38    /// returned. Send both output to `bps_free` when you're done with them.
39    ///
40    /// If `accept_wrong_input` is true, it may return `bps_to_output` or
41    /// `bps_not_this`, while putting non-NULL in `out`/`metadata`.
42    pub fn bps_apply(
43        patch: mem,
44        in_: mem,
45        out: *mut mem,
46        metadata: *mut mem,
47        accept_wrong_input: bool,
48    ) -> bpserror;
49
50    /// Creates a BPS patch that converts `source` to `target` and stores it to `patch`.
51    ///
52    /// It is safe to give `{NULL, 0}` as `metadata`.
53    pub fn bps_create_linear(
54        source: mem,
55        target: mem,
56        metadata: mem,
57        patch: *mut mem
58    ) -> bpserror;
59
60    pub fn bps_create_delta_inmem(
61        source: mem,
62        target: mem,
63        metadata: mem,
64        patch: *mut mem,
65        progress: *const libc::c_void, // FIXME
66        userdata: *const libc::c_void,
67        moremem: bool,
68    ) -> bpserror;
69}
70
71#[cfg(test)]
72mod tests {
73
74    use core::ops::Deref;
75
76    use super::mem;
77    use super::bpserror;
78    use crate::test_utils::ArbitraryBuffer;
79
80    #[quickcheck_macros::quickcheck]
81    fn check_create_linear_and_apply(mut source: ArbitraryBuffer, mut target: ArbitraryBuffer) -> bool {
82        if source == target {
83            return true;
84        }
85
86        unsafe {
87            // create patch
88            let mut mem_patch = mem::default();
89            let result = super::bps_create_linear(source.to_mem(), target.to_mem(), mem::default(), &mut mem_patch as *mut mem);
90            assert_eq!(result, bpserror::bps_ok, "could not create patch");
91
92            // apply patch
93            let mut mem_out = mem::default();
94            let result = super::bps_apply(mem_patch, source.to_mem(), &mut mem_out as *mut mem, &mut mem::default() as *mut mem, false);
95            assert_eq!(result, bpserror::bps_ok, "could not apply patch");
96
97            // check
98            mem_out.as_ref() == target.deref()
99        }
100    }
101
102    #[quickcheck_macros::quickcheck]
103    fn check_create_delta_and_apply(mut source: ArbitraryBuffer, mut target: ArbitraryBuffer) -> bool {
104        if source == target {
105            return true;
106        }
107
108        unsafe {
109            // create patch
110            let mut mem_patch = mem::default();
111            let result = super::bps_create_delta_inmem(source.to_mem(), target.to_mem(), mem::default(), &mut mem_patch as *mut mem, core::ptr::null(), core::ptr::null(), false);
112            assert_eq!(result, bpserror::bps_ok, "could not create patch");
113
114            // apply patch
115            let mut mem_out = mem::default();
116            let result = super::bps_apply(mem_patch, source.to_mem(), &mut mem_out as *mut mem, &mut mem::default() as *mut mem, false);
117            assert_eq!(result, bpserror::bps_ok, "could not apply patch");
118
119            // check
120            mem_out.as_ref() == target.deref()
121        }
122    }
123
124    #[quickcheck_macros::quickcheck]
125    fn check_create_identical(mut source: ArbitraryBuffer) -> bool {
126        unsafe {
127            let mut mem_patch = core::mem::MaybeUninit::uninit().assume_init();
128            let result = super::bps_create_linear(source.to_mem(), source.to_mem(), mem::default(), &mut mem_patch as *mut _);
129            result == bpserror::bps_identical
130        }
131    }
132
133    #[quickcheck_macros::quickcheck]
134    fn check_create_equal(mut source: ArbitraryBuffer) -> bool {
135        let mut target = source.clone();
136        unsafe {
137            let mut mem_patch = core::mem::MaybeUninit::uninit().assume_init();
138            let result = super::bps_create_linear(source.to_mem(), target.to_mem(), mem::default(), &mut mem_patch as *mut _);
139            result == bpserror::bps_identical
140        }
141    }
142}