From a96b4633dcf24f26be92da02d43a8e7932ef2f4c Mon Sep 17 00:00:00 2001 From: angie Date: Fri, 15 Dec 2023 10:52:37 -0300 Subject: [PATCH] Literal port of python code for checksum --- src/rs/checksum.rs | 200 +++++++++++++++++++++++++++++++++++++++++++++ src/rs/cickinds.rs | 61 +++++++------- src/rs/lib.rs | 3 + src/rs/utils.rs | 31 +++++++ 4 files changed, 265 insertions(+), 30 deletions(-) create mode 100644 src/rs/checksum.rs create mode 100644 src/rs/utils.rs diff --git a/src/rs/checksum.rs b/src/rs/checksum.rs new file mode 100644 index 0000000..bb77d44 --- /dev/null +++ b/src/rs/checksum.rs @@ -0,0 +1,200 @@ +/* SPDX-FileCopyrightText: © 2023 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +#[cfg(feature = "python_bindings")] +use pyo3::prelude::*; + +use crate::cickinds::CICKind; +use crate::utils; + + +fn readWordFromRam(romWords: &[u32], entrypointRam: u32, ramAddr: u32) -> u32 { + //return romWords[utils.u32(ramAddr - entrypointRam + 0x1000) / 4] + romWords[((ramAddr - entrypointRam + 0x1000) / 4) as usize] +} + + +pub fn calculateChecksum(romBytes: &[u8], kind: &CICKind) -> Option<(u32, u32)> { + /* + Calculates the checksum required by an official CIC of a N64 ROM. + + Args: + romBytes (bytes): The bytes of the N64 ROM in big endian format. It must have a minimum size of 0x101000 bytes. + kind (CICKind): The CIC kind variation used to calculate the checksum. + + Returns: + tuple[int, int]|None: If no error happens then the calculated checksum is returned, stored as a tuple + containing two 32-bits words. Otherwise, `None` is returned. Possible errors: + - `romBytes` not being big enough + */ + + if romBytes.len() < 0x101000 { + return None; + } + + let romWords = utils::read_u32_vec(romBytes, 0, 0x101000); + + let seed = kind.get_seed(); + let magic = kind.get_magic(); + + let mut s6 = seed; + + let mut a0 = romWords[8/4]; + if *kind == CICKind::CIC_X103 { + a0 -= 0x100000; + } + if *kind == CICKind::CIC_X106 { + a0 -= 0x200000; + } + let entrypointRam = a0; + + let mut at = magic; + let mut lo = s6 * at; + + if *kind == CICKind::CIC_X105 { + s6 = 0xA0000200; + } + + let mut ra = 0x100000; + + let mut v1 = 0; + let mut t0 = 0; + + let mut t1 = a0; + + let mut t5 = 0x20; + + //let mut v0 = utils.u32(lo); + let mut v0 = lo; + v0 += 1; + + let mut a3 = v0; + let mut t2 = v0; + let mut t3 = v0; + let mut s0 = v0; + let mut a2 = v0; + let mut t4 = v0; + + // poor man's do while + let mut LA40005F0_loop = true; + while LA40005F0_loop { + // v0 = *t1 + v0 = readWordFromRam(&romWords, entrypointRam, t1); + + //v1 = utils.u32(a3 + v0); + v1 = a3 + v0; + + //at = utils.u32(v1) < utils.u32(a3); + at = if v1 < a3 { 1 } else { 0 }; + + let a1 = v1; + // if (at == 0) goto LA4000608; + + if at != 0 { + //t2 = utils.u32(t2 + 0x1) + t2 = t2 + 0x1; + } + + // LA4000608 + v1 = v0 & 0x1F; + //t7 = utils.u32(t5 - v1) + let t7 = t5 - v1; + + + //let t8 = utils.u32(v0 >> t7) + //let t6 = utils.u32(v0 << v1) + let t8 = v0 >> t7; + let t6 = v0 << v1; + + a0 = t6 | t8; + // at = utils.u32(a2) < utils.u32(v0); + at = if a2 < v0 { 1 } else { 0 }; + a3 = a1; + + t3 = t3 ^ v0; + + //s0 = utils.u32(s0 + a0) + s0 = s0 + a0; + // if (at == 0) goto LA400063C; + if at != 0 { + let t9 = a3 ^ v0; + + a2 = t9 ^ a2; + // goto LA4000640; + + // LA400063C: + } else { + a2 = a2 ^ a0; + } + + + // LA4000640: + if *kind == CICKind::CIC_X105 { + // ipl3 6105 copies 0x330 bytes from the ROM's offset 0x000554 (or offset 0x000514 into IPL3) to vram 0xA0000004 + let mut t7 = romWords[((s6 - 0xA0000004 + 0x000554) / 4) as usize]; + + //t0 = utils.u32(t0 + 0x4); + //s6 = utils.u32(s6 + 0x4); + t0 = t0 + 0x4; + s6 = s6 + 0x4; + + t7 = v0 ^ t7; + + // t4 = utils.u32(t7 + t4); + t4 = t7 + t4; + + t7 = 0xA00002FF; + + // t1 = utils.u32(t1 + 0x4); + t1 = t1 + 0x4; + + // s6 = utils.u32(s6 & t7); + s6 = s6 & t7; + } else { + // t0 = utils.u32(t0 + 0x4); + t0 = t0 + 0x4; + + let t7 = v0 ^ s0; + + // t1 = utils.u32(t1 + 0x4); + t1 = t1 + 0x4; + + // t4 = utils.u32(t7 + t4); + t4 = t7 + t4; + } + + + // if (t0 != ra) goto LA40005F0; + if t0 == ra { + LA40005F0_loop = false; + } + } + + if *kind == CICKind::CIC_X103 { + let t6 = a3 ^ t2; + // a3 = utils.u32(t6 + t3); + a3 = t6 + t3; + + let t8 = s0 ^ a2; + // s0 = utils.u32(t8 + t4); + s0 = t8 + t4; + } else if *kind == CICKind::CIC_X106 { + /* + let t6 = utils.u32(a3 * t2); + a3 = utils.u32(t6 + t3); + let t8 = utils.u32(s0 * a2); + s0 = utils.u32(t8 + t4); + */ + let t6 = a3 * t2; + a3 = t6 + t3; + let t8 = s0 * a2; + s0 = t8 + t4; + } else { + let t6 = a3 ^ t2; + a3 = t6 ^ t3; + let t8 = s0 ^ a2; + s0 = t8 ^ t4; + } + + return Some((a3, s0)) +} diff --git a/src/rs/cickinds.rs b/src/rs/cickinds.rs index 37785b8..98979aa 100644 --- a/src/rs/cickinds.rs +++ b/src/rs/cickinds.rs @@ -6,48 +6,49 @@ use pyo3::prelude::*; #[cfg_attr(feature = "python_bindings", pyclass(module = "ipl3checksum"))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[allow(non_camel_case_types)] pub enum CICKind { - Cic6101, - Cic6102_7101, - Cic7102, - CicX103, // Both 6103 and 7103 + CIC_6101, + CIC_6102_7101, + CIC_7102, + CIC_X103, // Both 6103 and 7103 // 6104/7104 does not exist - CicX105, // Both 6105 and 7105 - CicX106, // Both 6106 and 7106 + CIC_X105, // Both 6105 and 7105 + CIC_X106, // Both 6106 and 7106 } #[cfg_attr(feature = "python_bindings", pymethods)] impl CICKind { pub fn get_seed(&self) -> u32 { match self { - CICKind::Cic6101 => 0x3F, - CICKind::Cic6102_7101 => 0x3F, - CICKind::Cic7102 => 0x3F, - CICKind::CicX103 => 0x78, - CICKind::CicX105 => 0x91, - CICKind::CicX106 => 0x85, + CICKind::CIC_6101 => 0x3F, + CICKind::CIC_6102_7101 => 0x3F, + CICKind::CIC_7102 => 0x3F, + CICKind::CIC_X103 => 0x78, + CICKind::CIC_X105 => 0x91, + CICKind::CIC_X106 => 0x85, } } pub fn get_magic(&self) -> u32 { match self { - CICKind::Cic6101 => 0x5D588B65, - CICKind::Cic6102_7101 => 0x5D588B65, - CICKind::Cic7102 => 0x5D588B65, - CICKind::CicX103 => 0x6C078965, - CICKind::CicX105 => 0x5D588B65, - CICKind::CicX106 => 0x6C078965, + CICKind::CIC_6101 => 0x5D588B65, + CICKind::CIC_6102_7101 => 0x5D588B65, + CICKind::CIC_7102 => 0x5D588B65, + CICKind::CIC_X103 => 0x6C078965, + CICKind::CIC_X105 => 0x5D588B65, + CICKind::CIC_X106 => 0x6C078965, } } pub fn get_hash_md5(&self) -> &str { match self { - CICKind::Cic6101 => "900b4a5b68edb71f4c7ed52acd814fc5", - CICKind::Cic6102_7101 => "e24dd796b2fa16511521139d28c8356b", - CICKind::Cic7102 => "955894c2e40a698bf98a67b78a4e28fa", - CICKind::CicX103 => "319038097346e12c26c3c21b56f86f23", - CICKind::CicX105 => "ff22a296e55d34ab0a077dc2ba5f5796", - CICKind::CicX106 => "6460387749ac0bd925aa5430bc7864fe", + CICKind::CIC_6101 => "900b4a5b68edb71f4c7ed52acd814fc5", + CICKind::CIC_6102_7101 => "e24dd796b2fa16511521139d28c8356b", + CICKind::CIC_7102 => "955894c2e40a698bf98a67b78a4e28fa", + CICKind::CIC_X103 => "319038097346e12c26c3c21b56f86f23", + CICKind::CIC_X105 => "ff22a296e55d34ab0a077dc2ba5f5796", + CICKind::CIC_X106 => "6460387749ac0bd925aa5430bc7864fe", } } @@ -66,12 +67,12 @@ impl CICKind { impl CICKind { fn from_value_impl(value: usize) -> Option { match value { - 6101 => Some(CICKind::Cic6101), - 6102 | 7101 => Some(CICKind::Cic6102_7101), - 7102 => Some(CICKind::Cic7102), - 6103 | 7103 => Some(CICKind::CicX103), - 6105 | 7105 => Some(CICKind::CicX105), - 6106 | 7106 => Some(CICKind::CicX106), + 6101 => Some(CICKind::CIC_6101), + 6102 | 7101 => Some(CICKind::CIC_6102_7101), + 7102 => Some(CICKind::CIC_7102), + 6103 | 7103 => Some(CICKind::CIC_X103), + 6105 | 7105 => Some(CICKind::CIC_X105), + 6106 | 7106 => Some(CICKind::CIC_X106), _ => None } } diff --git a/src/rs/lib.rs b/src/rs/lib.rs index dda1a4c..1cbdbab 100644 --- a/src/rs/lib.rs +++ b/src/rs/lib.rs @@ -2,6 +2,9 @@ /* SPDX-License-Identifier: MIT */ pub mod cickinds; +pub mod checksum; + +mod utils; #[cfg(feature = "python_bindings")] use pyo3::prelude::*; diff --git a/src/rs/utils.rs b/src/rs/utils.rs new file mode 100644 index 0000000..ce06214 --- /dev/null +++ b/src/rs/utils.rs @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: © 2023 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +pub(crate) fn read_u32(bytes: &[u8], offset: usize) -> u32 { + if offset % 4 != 0 { + panic!("Unaligned read"); + } + + if offset + 4 > bytes.len() { + panic!("Out of bounds"); + } + + /* + match bytes[offset..offset + 4].try_into() { + Ok(bytes) => u32::from_be_bytes(bytes), + Err(_error) => todo!(), + } + */ + + u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) +} + +pub(crate) fn read_u32_vec(bytes: &[u8], offset: usize, len: usize) -> Vec { + let mut ret = vec![0;len]; + + for i in 0..(len*4) { + ret[i] = read_u32(bytes, offset + i); + } + + ret +}