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