#![deny(clippy::all, clippy::nursery)]
#![deny(nonstandard_style, rust_2018_idioms)]
#![deny(missing_docs, missing_debug_implementations)]
#![forbid(unsafe_code)]
#![no_std]
#[cfg(doctest)]
doc_comment::doctest!("../README.md", readme);
pub mod cb1;
pub mod cb7;
mod rc4;
#[cfg(test)]
mod std_alloc {
#[cfg(feature = "std")]
extern crate std as alloc;
#[cfg(not(feature = "std"))]
extern crate alloc;
pub use alloc::{fmt, vec, vec::Vec};
}
use cb7::{is_beefcode, Cb7};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Scheme {
Raw,
V1,
V7,
}
#[derive(Debug, Clone, Copy)]
pub struct Codebreaker {
scheme: Scheme,
cb7: Cb7,
code_lines: usize,
}
impl Default for Codebreaker {
fn default() -> Self {
Self::new()
}
}
impl Codebreaker {
pub const fn new() -> Self {
Self {
scheme: Scheme::Raw,
cb7: Cb7::new(),
code_lines: 0,
}
}
pub fn new_v7() -> Self {
Self {
scheme: Scheme::V7,
cb7: Cb7::default(),
code_lines: 0,
}
}
pub fn encrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) {
let mut code = (addr, val);
self.encrypt_code_mut(&mut code.0, &mut code.1);
code
}
pub fn encrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) {
let (oldaddr, oldval) = (*addr, *val);
if self.scheme == Scheme::V7 {
self.cb7.encrypt_code_mut(addr, val);
} else {
cb1::encrypt_code_mut(addr, val);
}
if is_beefcode(oldaddr) {
self.cb7.beefcode(oldaddr, oldval);
self.scheme = Scheme::V7;
}
}
pub fn decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) {
let mut code = (addr, val);
self.decrypt_code_mut(&mut code.0, &mut code.1);
code
}
pub fn decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) {
if self.scheme == Scheme::V7 {
self.cb7.decrypt_code_mut(addr, val);
} else {
cb1::decrypt_code_mut(addr, val);
}
if is_beefcode(*addr) {
self.cb7.beefcode(*addr, *val);
self.scheme = Scheme::V7;
}
}
pub fn auto_decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) {
let mut code = (addr, val);
self.auto_decrypt_code_mut(&mut code.0, &mut code.1);
code
}
pub fn auto_decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) {
if self.scheme != Scheme::V7 {
if self.code_lines == 0 {
self.code_lines = num_code_lines(*addr);
if (*addr >> 24) & 0x0e != 0 {
if is_beefcode(*addr) {
self.code_lines -= 1;
return;
}
self.scheme = Scheme::V1;
self.code_lines -= 1;
cb1::decrypt_code_mut(addr, val);
} else {
self.scheme = Scheme::Raw;
self.code_lines -= 1;
}
} else {
self.code_lines -= 1;
if self.scheme == Scheme::Raw {
return;
}
cb1::decrypt_code_mut(addr, val);
}
} else {
self.cb7.decrypt_code_mut(addr, val);
if self.code_lines == 0 {
self.code_lines = num_code_lines(*addr);
if self.code_lines == 1 && *addr == 0xffff_ffff {
self.code_lines = 0;
return;
}
}
self.code_lines -= 1;
}
if is_beefcode(*addr) {
self.cb7.beefcode(*addr, *val);
self.scheme = Scheme::V7;
self.code_lines = 1;
}
}
}
const fn num_code_lines(addr: u32) -> usize {
let cmd = addr >> 28;
if cmd < 3 || cmd > 6 {
1
} else if cmd == 3 {
if addr & 0x0040_0000 != 0 {
2
} else {
1
}
} else {
2
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::code::Code;
use crate::std_alloc::{vec, Vec};
#[cfg(feature = "std")]
use pretty_assertions::assert_eq;
struct Test {
cb: Codebreaker,
decrypted: Vec<Code>,
encrypted: Vec<Code>,
}
fn tests() -> Vec<Test> {
vec![
Test {
cb: Codebreaker::new(),
decrypted: vec![
"2043AFCC 2411FFFF".into(),
"BEEFC0DE 00000000".into(),
"2096F5B8 000000BE".into(),
],
encrypted: vec![
"2AFF014C 2411FFFF".into(),
"B4336FA9 4DFEFB79".into(),
"973E0B2A A7D4AF10".into(),
],
},
Test {
cb: Codebreaker::new_v7(),
decrypted: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"2096F5B8 000000BE".into(),
],
encrypted: vec![
"D08F3A49 00078A53".into(),
"3818DDE5 E72B2B16".into(),
"973E0B2A A7D4AF10".into(),
],
},
Test {
cb: Codebreaker::default(),
decrypted: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"2096F5B8 000000BE".into(),
],
encrypted: vec![
"9A545CC6 188CBCFB".into(),
"2A973DBD 00000000".into(),
"2A03B60A 000000BE".into(),
],
},
]
}
#[test]
fn test_encrypt_code() {
for t in &mut tests() {
for (i, &code) in t.decrypted.iter().enumerate() {
let result: Code = t.cb.encrypt_code(code.0, code.1).into();
assert_eq!(result, t.encrypted[i]);
}
}
}
#[test]
fn test_encrypt_code_mut() {
for t in &mut tests() {
for (i, code) in t.decrypted.iter_mut().enumerate() {
t.cb.encrypt_code_mut(&mut code.0, &mut code.1);
assert_eq!(*code, t.encrypted[i]);
}
}
}
#[test]
fn test_decrypt_code() {
for t in &mut tests() {
for (i, &code) in t.encrypted.iter().enumerate() {
let result: Code = t.cb.decrypt_code(code.0, code.1).into();
assert_eq!(result, t.decrypted[i]);
}
}
}
#[test]
fn test_decrypt_code_mut() {
for t in &mut tests() {
for (i, code) in t.encrypted.iter_mut().enumerate() {
t.cb.decrypt_code_mut(&mut code.0, &mut code.1);
assert_eq!(*code, t.decrypted[i]);
}
}
}
struct AutoTest {
input: Vec<Code>,
output: Vec<Code>,
}
fn auto_tests() -> Vec<AutoTest> {
vec![
AutoTest {
input: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"2096F5B8 000000BE".into(),
],
output: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"2096F5B8 000000BE".into(),
],
},
AutoTest {
input: vec![
"9A545CC6 188CBCFB".into(),
"2A973DBD 00000000".into(),
"2A03B60A 000000BE".into(),
],
output: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"2096F5B8 000000BE".into(),
],
},
AutoTest {
input: vec![
"B4336FA9 4DFEFB79".into(),
"D08F3A49 00078A53".into(),
"3818DDE5 E72B2B16".into(),
"973E0B2A A7D4AF10".into(),
],
output: vec![
"BEEFC0DE 00000000".into(),
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"2096F5B8 000000BE".into(),
],
},
AutoTest {
input: vec![
"9A545CC6 188CBCFB".into(),
"2A973DBD 00000000".into(),
"B4336FA9 4DFEFB79".into(),
"973E0B2A A7D4AF10".into(),
],
output: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"BEEFC0DE 00000000".into(),
"2096F5B8 000000BE".into(),
],
},
AutoTest {
input: vec![
"9029BEAC 0C0A9225".into(),
"2A973DBD 00000000".into(),
"B4336FA9 4DFEFB79".into(),
"973E0B2A A7D4AF10".into(),
],
output: vec![
"9029BEAC 0C0A9225".into(),
"201F6024 00000000".into(),
"BEEFC0DE 00000000".into(),
"2096F5B8 000000BE".into(),
],
},
]
}
#[test]
fn test_auto_decrypt_code() {
for t in &mut auto_tests() {
let mut cb = Codebreaker::new();
for (i, &code) in t.input.iter().enumerate() {
let result: Code = cb.auto_decrypt_code(code.0, code.1).into();
assert_eq!(result, t.output[i]);
}
}
}
#[test]
fn test_auto_decrypt_code_mut() {
for t in &mut auto_tests() {
let mut cb = Codebreaker::new();
for (i, code) in t.input.iter_mut().enumerate() {
cb.auto_decrypt_code_mut(&mut code.0, &mut code.1);
assert_eq!(*code, t.output[i]);
}
}
}
}
#[cfg(test)]
mod code {
use crate::std_alloc::{fmt, Vec};
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Code(pub u32, pub u32);
impl From<(u32, u32)> for Code {
fn from(t: (u32, u32)) -> Self {
Self(t.0, t.1)
}
}
impl From<&str> for Code {
fn from(s: &str) -> Self {
let t: Vec<u32> = s
.splitn(2, ' ')
.map(|v| u32::from_str_radix(v, 16).expect("invalid code format"))
.collect();
Self(t[0], t[1])
}
}
impl fmt::Display for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:08X} {:08X}", self.0, self.1)
}
}
impl fmt::Debug for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
}