flips_sys/
ips.rs

1//! Bindings to `libips.h` to work with IPS patches.
2
3#![allow(bad_style)]
4
5use super::mem;
6
7#[repr(C)]
8#[derive(Clone, Debug, PartialEq)]
9pub enum ipserror {
10    /// Patch applied or created successfully.
11    ips_ok,
12    /// The patch is most likely not inteded for this ROM.
13    ips_notthis,
14    /// You most likely applied the patch on the output ROM.
15    ips_thisout,
16    /// The patch is technically valid, but seems scrambled or malformed.
17    ips_scrambled,
18    /// The patch is invalid.
19    ips_invalid,
20    /// One or both files is bigger than 16MB.
21    ///
22    /// The IPS format doesn't support that. The created patch contains
23    /// only the differences to that point.
24    ips_16MB,
25    /// The input buffers are identical.
26    ips_identical,
27    /// Unused, just kill GCC warning.
28    ips_shut_up_gcc,
29}
30
31#[repr(C)]
32#[derive(Clone, Debug, PartialEq)]
33pub struct ipsstudy {
34    error: ipserror,
35    outlen_min: libc::c_uint,
36    outlen_max: libc::c_uint,
37    outlen_min_mem: libc::c_uint,
38}
39
40impl Default for ipsstudy {
41    fn default() -> Self {
42        Self {
43            error: ipserror::ips_ok,
44            outlen_min: 0,
45            outlen_max: 0,
46            outlen_min_mem: 0,
47        }
48    }
49}
50
51#[link(name="ips")]
52extern "C" {
53    /// Applies the IPS patch in `patch` to `in_` and stores it to `out`.
54    ///
55    /// Send the return value in out to `ips_free` when you're done with it.
56    pub fn ips_apply(patch: mem, in_: mem, out: *mut mem) -> ipserror;
57
58    /// Creates an IPS patch that converts `source` to `target` and stores it in `patch`.
59    pub fn ips_create(source: mem, target: mem, patch: *mut mem) -> ipserror;
60
61    /// Frees the memory returned in the output parameters of the above.
62    ///
63    /// Do not call it twice on the same input, nor on anything you got from
64    /// anywhere else. `ips_free` is guaranteed to be equivalent to calling
65    /// `free` from `<stdlib.h>` on `mem.ptr`.
66    pub fn ips_free(mem: mem);
67
68    /// Detect most patching errors without applying it to a ROM.
69    pub fn ips_study(patch: mem, study: *mut ipsstudy) -> ipserror;
70
71    /// Apply a patch using a previously made study to avoid recreating a study.
72    ///
73    /// Since [`ips_apply`](./fn.ips_apply.html) calls [`ips_study`](./fn.ips_study.html)
74    /// before applying the patch, you should use this function if you have already
75    /// created a study beforehand.
76    pub fn ips_apply_study(patch: mem, study: *mut ipsstudy, in_: mem, out: *mut mem) -> ipserror;
77}
78
79#[cfg(test)]
80mod tests {
81
82    use core::ops::Deref;
83
84    use super::mem;
85    use super::ipserror;
86    use crate::test_utils::ArbitraryBuffer;
87
88    #[quickcheck_macros::quickcheck]
89    fn check_create_and_apply(mut source: ArbitraryBuffer, mut target: ArbitraryBuffer) -> bool {
90        if source == target {
91            return true;
92        }
93
94        unsafe {
95            // create patch
96            let mut mem_patch = mem::default();
97            let result = super::ips_create(source.to_mem(), target.to_mem(), &mut mem_patch as *mut mem);
98            assert_eq!(result, ipserror::ips_ok, "could not create patch");
99
100            // apply patch
101            let mut mem_out = mem::default();
102            let result = super::ips_apply(mem_patch, source.to_mem(), &mut mem_out as *mut mem);
103            assert_eq!(result, ipserror::ips_ok, "could not apply patch");
104
105            // check
106            mem_out.as_ref() == target.deref()
107        }
108    }
109
110    #[quickcheck_macros::quickcheck]
111    fn check_create_identical(mut source: ArbitraryBuffer) -> bool {
112        unsafe {
113            let mut mem_patch = core::mem::MaybeUninit::uninit().assume_init();
114            let result = super::ips_create(source.to_mem(), source.to_mem(), &mut mem_patch as *mut _);
115            result == ipserror::ips_identical
116        }
117    }
118
119    #[quickcheck_macros::quickcheck]
120    fn check_create_equal(mut source: ArbitraryBuffer) -> bool {
121        let mut target = source.clone();
122        unsafe {
123            let mut mem_patch = core::mem::MaybeUninit::uninit().assume_init();
124            let result = super::ips_create(source.to_mem(), target.to_mem(), &mut mem_patch as *mut _);
125            result == ipserror::ips_identical
126        }
127    }
128}