codebreaker/
lib.rs

1//! Encrypt and decrypt cheat codes for all versions of CodeBreaker PS2.
2//!
3//! Uses [cb1](cb1/index.html) and [cb7](cb7/index.html) under the hood to
4//! support both CB v1 and v7 codes.
5//!
6//! # Quickstart
7//!
8//! ```
9//! use codebreaker::Codebreaker;
10//!
11//! let input: Vec<(u32, u32)> = vec![
12//!     (0x2043AFCC, 0x2411FFFF),
13//!     (0x2A973DBD, 0x00000000),
14//!     (0xB4336FA9, 0x4DFEFB79),
15//!     (0x973E0B2A, 0xA7D4AF10),
16//! ];
17//! let output: Vec<(u32, u32)> = vec![
18//!     (0x2043AFCC, 0x2411FFFF),
19//!     (0x201F6024, 0x00000000),
20//!     (0xBEEFC0DE, 0x00000000),
21//!     (0x2096F5B8, 0x000000BE),
22//! ];
23//!
24//! let mut cb = Codebreaker::new();
25//! for (i, code) in input.iter().enumerate() {
26//!     assert_eq!(cb.auto_decrypt_code(code.0, code.1), output[i]);
27//! }
28//! ```
29
30#![deny(clippy::all, clippy::nursery)]
31#![deny(nonstandard_style, rust_2018_idioms)]
32#![deny(missing_docs, missing_debug_implementations)]
33#![forbid(unsafe_code)]
34#![no_std]
35
36#[cfg(doctest)]
37doc_comment::doctest!("../README.md", readme);
38
39pub mod cb1;
40pub mod cb7;
41mod rc4;
42
43#[cfg(test)]
44mod std_alloc {
45    #[cfg(feature = "std")]
46    extern crate std as alloc;
47
48    #[cfg(not(feature = "std"))]
49    extern crate alloc;
50
51    pub use alloc::{fmt, vec, vec::Vec};
52}
53
54use cb7::{is_beefcode, Cb7};
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57enum Scheme {
58    Raw,
59    V1,
60    V7,
61}
62
63/// A processor for CB v1 and v7 codes.
64#[derive(Debug, Clone, Copy)]
65pub struct Codebreaker {
66    scheme: Scheme,
67    cb7: Cb7,
68    code_lines: usize,
69}
70
71/// Does the same as [`new`](#method.new).
72impl Default for Codebreaker {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78impl Codebreaker {
79    /// Returns a new processor for encrypting and decrypting a list of CB v1
80    /// and v7 codes.
81    pub const fn new() -> Self {
82        Self {
83            scheme: Scheme::Raw,
84            cb7: Cb7::new(),
85            code_lines: 0,
86        }
87    }
88
89    /// Returns a new processor for all CB v7 codes published on CMGSCCC.com.
90    ///
91    /// Lets you omit `B4336FA9 4DFEFB79` as the first code in the list.
92    pub fn new_v7() -> Self {
93        Self {
94            scheme: Scheme::V7,
95            cb7: Cb7::default(),
96            code_lines: 0,
97        }
98    }
99
100    /// Encrypts a code and returns the result.
101    ///
102    /// # Example
103    /// ```
104    /// use codebreaker::Codebreaker;
105    ///
106    /// let mut cb = Codebreaker::new();
107    /// let code = cb.encrypt_code(0x2043AFCC, 0x2411FFFF);
108    /// assert_eq!(code, (0x2AFF014C, 0x2411FFFF));
109    /// ```
110    pub fn encrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) {
111        let mut code = (addr, val);
112        self.encrypt_code_mut(&mut code.0, &mut code.1);
113        code
114    }
115
116    /// Encrypts a code directly.
117    ///
118    /// # Example
119    /// ```
120    /// use codebreaker::Codebreaker;
121    ///
122    /// let mut cb = Codebreaker::new();
123    /// let mut code = (0x2043AFCC, 0x2411FFFF);
124    /// cb.encrypt_code_mut(&mut code.0, &mut code.1);
125    /// assert_eq!(code, (0x2AFF014C, 0x2411FFFF));
126    /// ```
127    pub fn encrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) {
128        let (oldaddr, oldval) = (*addr, *val);
129
130        if self.scheme == Scheme::V7 {
131            self.cb7.encrypt_code_mut(addr, val);
132        } else {
133            cb1::encrypt_code_mut(addr, val);
134        }
135
136        if is_beefcode(oldaddr) {
137            self.cb7.beefcode(oldaddr, oldval);
138            self.scheme = Scheme::V7;
139        }
140    }
141
142    /// Decrypts a code and returns the result.
143    ///
144    /// # Example
145    /// ```
146    /// use codebreaker::Codebreaker;
147    ///
148    /// let encrypted: Vec<(u32, u32)> = vec![
149    ///     (0x2AFF014C, 0x2411FFFF),
150    ///     (0xB4336FA9, 0x4DFEFB79),
151    ///     (0x973E0B2A, 0xA7D4AF10),
152    /// ];
153    /// let decrypted: Vec<(u32, u32)> = vec![
154    ///     (0x2043AFCC, 0x2411FFFF),
155    ///     (0xBEEFC0DE, 0x00000000),
156    ///     (0x2096F5B8, 0x000000BE),
157    /// ];
158    ///
159    /// let mut cb = Codebreaker::new();
160    /// for (i, code) in encrypted.iter().enumerate() {
161    ///     let result = cb.decrypt_code(code.0, code.1);
162    ///     assert_eq!(result, decrypted[i]);
163    /// }
164    /// ```
165    pub fn decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) {
166        let mut code = (addr, val);
167        self.decrypt_code_mut(&mut code.0, &mut code.1);
168        code
169    }
170
171    /// Decrypts a code directly.
172    ///
173    /// # Example
174    /// ```
175    /// use codebreaker::Codebreaker;
176    ///
177    /// let mut encrypted: Vec<(u32, u32)> = vec![
178    ///     (0x2AFF014C, 0x2411FFFF),
179    ///     (0xB4336FA9, 0x4DFEFB79),
180    ///     (0x973E0B2A, 0xA7D4AF10),
181    /// ];
182    /// let decrypted: Vec<(u32, u32)> = vec![
183    ///     (0x2043AFCC, 0x2411FFFF),
184    ///     (0xBEEFC0DE, 0x00000000),
185    ///     (0x2096F5B8, 0x000000BE),
186    /// ];
187    ///
188    /// let mut cb = Codebreaker::new();
189    /// for code in encrypted.iter_mut() {
190    ///     cb.decrypt_code_mut(&mut code.0, &mut code.1);
191    /// }
192    /// assert_eq!(encrypted, decrypted);
193    /// ```
194    pub fn decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) {
195        if self.scheme == Scheme::V7 {
196            self.cb7.decrypt_code_mut(addr, val);
197        } else {
198            cb1::decrypt_code_mut(addr, val);
199        }
200
201        if is_beefcode(*addr) {
202            self.cb7.beefcode(*addr, *val);
203            self.scheme = Scheme::V7;
204        }
205    }
206
207    /// Smart version of [`decrypt_code`](#method.decrypt_code) that detects if
208    /// and how a code needs to be decrypted.
209    ///
210    /// # Example
211    /// ```
212    /// use codebreaker::Codebreaker;
213    ///
214    /// let input: Vec<(u32, u32)> = vec![
215    ///     (0x2043AFCC, 0x2411FFFF),
216    ///     (0x2A973DBD, 0x00000000),
217    ///     (0xB4336FA9, 0x4DFEFB79),
218    ///     (0x973E0B2A, 0xA7D4AF10),
219    /// ];
220    /// let output: Vec<(u32, u32)> = vec![
221    ///     (0x2043AFCC, 0x2411FFFF),
222    ///     (0x201F6024, 0x00000000),
223    ///     (0xBEEFC0DE, 0x00000000),
224    ///     (0x2096F5B8, 0x000000BE),
225    /// ];
226    ///
227    /// let mut cb = Codebreaker::new();
228    /// for (i, code) in input.iter().enumerate() {
229    ///     assert_eq!(cb.auto_decrypt_code(code.0, code.1), output[i]);
230    /// }
231    /// ```
232    pub fn auto_decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) {
233        let mut code = (addr, val);
234        self.auto_decrypt_code_mut(&mut code.0, &mut code.1);
235        code
236    }
237
238    /// Smart version of [`decrypt_code_mut`](#method.decrypt_code_mut) that
239    /// detects if and how a code needs to be decrypted.
240    pub fn auto_decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) {
241        if self.scheme != Scheme::V7 {
242            if self.code_lines == 0 {
243                self.code_lines = num_code_lines(*addr);
244                if (*addr >> 24) & 0x0e != 0 {
245                    if is_beefcode(*addr) {
246                        // ignore raw beefcode
247                        self.code_lines -= 1;
248                        return;
249                    }
250                    self.scheme = Scheme::V1;
251                    self.code_lines -= 1;
252                    cb1::decrypt_code_mut(addr, val);
253                } else {
254                    self.scheme = Scheme::Raw;
255                    self.code_lines -= 1;
256                }
257            } else {
258                self.code_lines -= 1;
259                if self.scheme == Scheme::Raw {
260                    return;
261                }
262                cb1::decrypt_code_mut(addr, val);
263            }
264        } else {
265            self.cb7.decrypt_code_mut(addr, val);
266            if self.code_lines == 0 {
267                self.code_lines = num_code_lines(*addr);
268                if self.code_lines == 1 && *addr == 0xffff_ffff {
269                    // XXX: changing encryption via "FFFFFFFF 000xnnnn" is not supported
270                    self.code_lines = 0;
271                    return;
272                }
273            }
274            self.code_lines -= 1;
275        }
276
277        if is_beefcode(*addr) {
278            self.cb7.beefcode(*addr, *val);
279            self.scheme = Scheme::V7;
280            self.code_lines = 1;
281        }
282    }
283}
284
285const fn num_code_lines(addr: u32) -> usize {
286    let cmd = addr >> 28;
287
288    if cmd < 3 || cmd > 6 {
289        1
290    } else if cmd == 3 {
291        if addr & 0x0040_0000 != 0 {
292            2
293        } else {
294            1
295        }
296    } else {
297        2
298    }
299}
300
301#[cfg(test)]
302mod tests {
303    use super::*;
304    use crate::code::Code;
305    use crate::std_alloc::{vec, Vec};
306    #[cfg(feature = "std")]
307    use pretty_assertions::assert_eq;
308
309    struct Test {
310        cb: Codebreaker,
311        decrypted: Vec<Code>,
312        encrypted: Vec<Code>,
313    }
314
315    fn tests() -> Vec<Test> {
316        vec![
317            Test {
318                cb: Codebreaker::new(),
319                decrypted: vec![
320                    "2043AFCC 2411FFFF".into(),
321                    "BEEFC0DE 00000000".into(),
322                    "2096F5B8 000000BE".into(),
323                ],
324                encrypted: vec![
325                    "2AFF014C 2411FFFF".into(),
326                    "B4336FA9 4DFEFB79".into(),
327                    "973E0B2A A7D4AF10".into(),
328                ],
329            },
330            Test {
331                cb: Codebreaker::new_v7(),
332                decrypted: vec![
333                    "9029BEAC 0C0A9225".into(),
334                    "201F6024 00000000".into(),
335                    "2096F5B8 000000BE".into(),
336                ],
337                encrypted: vec![
338                    "D08F3A49 00078A53".into(),
339                    "3818DDE5 E72B2B16".into(),
340                    "973E0B2A A7D4AF10".into(),
341                ],
342            },
343            Test {
344                cb: Codebreaker::default(),
345                decrypted: vec![
346                    "9029BEAC 0C0A9225".into(),
347                    "201F6024 00000000".into(),
348                    "2096F5B8 000000BE".into(),
349                ],
350                encrypted: vec![
351                    "9A545CC6 188CBCFB".into(),
352                    "2A973DBD 00000000".into(),
353                    "2A03B60A 000000BE".into(),
354                ],
355            },
356        ]
357    }
358
359    #[test]
360    fn test_encrypt_code() {
361        for t in &mut tests() {
362            for (i, &code) in t.decrypted.iter().enumerate() {
363                let result: Code = t.cb.encrypt_code(code.0, code.1).into();
364                assert_eq!(result, t.encrypted[i]);
365            }
366        }
367    }
368
369    #[test]
370    fn test_encrypt_code_mut() {
371        for t in &mut tests() {
372            for (i, code) in t.decrypted.iter_mut().enumerate() {
373                t.cb.encrypt_code_mut(&mut code.0, &mut code.1);
374                assert_eq!(*code, t.encrypted[i]);
375            }
376        }
377    }
378
379    #[test]
380    fn test_decrypt_code() {
381        for t in &mut tests() {
382            for (i, &code) in t.encrypted.iter().enumerate() {
383                let result: Code = t.cb.decrypt_code(code.0, code.1).into();
384                assert_eq!(result, t.decrypted[i]);
385            }
386        }
387    }
388
389    #[test]
390    fn test_decrypt_code_mut() {
391        for t in &mut tests() {
392            for (i, code) in t.encrypted.iter_mut().enumerate() {
393                t.cb.decrypt_code_mut(&mut code.0, &mut code.1);
394                assert_eq!(*code, t.decrypted[i]);
395            }
396        }
397    }
398
399    struct AutoTest {
400        input: Vec<Code>,
401        output: Vec<Code>,
402    }
403
404    fn auto_tests() -> Vec<AutoTest> {
405        vec![
406            AutoTest {
407                // raw
408                input: vec![
409                    "9029BEAC 0C0A9225".into(),
410                    "201F6024 00000000".into(),
411                    "2096F5B8 000000BE".into(),
412                ],
413                output: vec![
414                    "9029BEAC 0C0A9225".into(),
415                    "201F6024 00000000".into(),
416                    "2096F5B8 000000BE".into(),
417                ],
418            },
419            AutoTest {
420                // v1 encrypted
421                input: vec![
422                    "9A545CC6 188CBCFB".into(),
423                    "2A973DBD 00000000".into(),
424                    "2A03B60A 000000BE".into(),
425                ],
426                output: vec![
427                    "9029BEAC 0C0A9225".into(),
428                    "201F6024 00000000".into(),
429                    "2096F5B8 000000BE".into(),
430                ],
431            },
432            AutoTest {
433                // v7 encrypted
434                input: vec![
435                    "B4336FA9 4DFEFB79".into(),
436                    "D08F3A49 00078A53".into(),
437                    "3818DDE5 E72B2B16".into(),
438                    "973E0B2A A7D4AF10".into(),
439                ],
440                output: vec![
441                    "BEEFC0DE 00000000".into(),
442                    "9029BEAC 0C0A9225".into(),
443                    "201F6024 00000000".into(),
444                    "2096F5B8 000000BE".into(),
445                ],
446            },
447            AutoTest {
448                // v1 and v7 encrypted
449                input: vec![
450                    "9A545CC6 188CBCFB".into(),
451                    "2A973DBD 00000000".into(),
452                    "B4336FA9 4DFEFB79".into(),
453                    "973E0B2A A7D4AF10".into(),
454                ],
455                output: vec![
456                    "9029BEAC 0C0A9225".into(),
457                    "201F6024 00000000".into(),
458                    "BEEFC0DE 00000000".into(),
459                    "2096F5B8 000000BE".into(),
460                ],
461            },
462            AutoTest {
463                // raw, v1, and v7 encrypted
464                input: vec![
465                    "9029BEAC 0C0A9225".into(),
466                    "2A973DBD 00000000".into(),
467                    "B4336FA9 4DFEFB79".into(),
468                    "973E0B2A A7D4AF10".into(),
469                ],
470                output: vec![
471                    "9029BEAC 0C0A9225".into(),
472                    "201F6024 00000000".into(),
473                    "BEEFC0DE 00000000".into(),
474                    "2096F5B8 000000BE".into(),
475                ],
476            },
477        ]
478    }
479
480    #[test]
481    fn test_auto_decrypt_code() {
482        for t in &mut auto_tests() {
483            let mut cb = Codebreaker::new();
484            for (i, &code) in t.input.iter().enumerate() {
485                let result: Code = cb.auto_decrypt_code(code.0, code.1).into();
486                assert_eq!(result, t.output[i]);
487            }
488        }
489    }
490
491    #[test]
492    fn test_auto_decrypt_code_mut() {
493        for t in &mut auto_tests() {
494            let mut cb = Codebreaker::new();
495            for (i, code) in t.input.iter_mut().enumerate() {
496                cb.auto_decrypt_code_mut(&mut code.0, &mut code.1);
497                assert_eq!(*code, t.output[i]);
498            }
499        }
500    }
501}
502
503#[cfg(test)]
504mod code {
505    use crate::std_alloc::{fmt, Vec};
506
507    #[derive(Copy, Clone, PartialEq, Eq)]
508    pub struct Code(pub u32, pub u32);
509
510    impl From<(u32, u32)> for Code {
511        fn from(t: (u32, u32)) -> Self {
512            Self(t.0, t.1)
513        }
514    }
515
516    impl From<&str> for Code {
517        fn from(s: &str) -> Self {
518            let t: Vec<u32> = s
519                .splitn(2, ' ')
520                .map(|v| u32::from_str_radix(v, 16).expect("invalid code format"))
521                .collect();
522
523            Self(t[0], t[1])
524        }
525    }
526
527    // Implements ToString
528    impl fmt::Display for Code {
529        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530            write!(f, "{:08X} {:08X}", self.0, self.1)
531        }
532    }
533
534    // Used by assert_eq!
535    impl fmt::Debug for Code {
536        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537            write!(f, "{self}")
538        }
539    }
540}