From c4cf8edb3c34c1a811279deaae0ef5679b441306 Mon Sep 17 00:00:00 2001 From: angie Date: Sat, 16 Dec 2023 12:03:54 -0300 Subject: [PATCH] Start writing C bindings --- Cargo.toml | 2 +- bindings/c/Makefile | 0 bindings/c/include/ipl3checksum.h | 10 ++++ bindings/c/include/ipl3checksum/checksum.h | 35 +++++++++++++ bindings/c/include/ipl3checksum/cickinds.h | 25 ++++++++++ bindings/c/include/ipl3checksum/detect.h | 14 ++++++ bindings/c/include/ipl3checksum/error.h | 51 +++++++++++++++++++ src/rs/checksum.rs | 58 ++++++++++++++++++++++ src/rs/cickinds.rs | 4 ++ src/rs/detect.rs | 6 +-- src/rs/error.rs | 10 ++-- src/rs/utils.rs | 18 +++++++ 12 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 bindings/c/Makefile create mode 100644 bindings/c/include/ipl3checksum.h create mode 100644 bindings/c/include/ipl3checksum/checksum.h create mode 100644 bindings/c/include/ipl3checksum/cickinds.h create mode 100644 bindings/c/include/ipl3checksum/detect.h create mode 100644 bindings/c/include/ipl3checksum/error.h diff --git a/Cargo.toml b/Cargo.toml index f15b008..51a4f34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ license = "MIT" [lib] name = "ipl3checksum" path = "src/rs/lib.rs" -crate-type = ["lib", "cdylib"] +crate-type = ["lib", "staticlib", "cdylib"] [dependencies] md5 = "0.7.0" diff --git a/bindings/c/Makefile b/bindings/c/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/bindings/c/include/ipl3checksum.h b/bindings/c/include/ipl3checksum.h new file mode 100644 index 0000000..eb72ea5 --- /dev/null +++ b/bindings/c/include/ipl3checksum.h @@ -0,0 +1,10 @@ +#ifndef IPL3CHECKSUM_H +#define IPL3CHECKSUM_H +#pragma once + +#include "ipl3checksum/error.h" +#include "ipl3checksum/cickinds.h" +#include "ipl3checksum/checksum.h" +#include "ipl3checksum/detect.h" + +#endif diff --git a/bindings/c/include/ipl3checksum/checksum.h b/bindings/c/include/ipl3checksum/checksum.h new file mode 100644 index 0000000..c5440c4 --- /dev/null +++ b/bindings/c/include/ipl3checksum/checksum.h @@ -0,0 +1,35 @@ +#ifndef IPL3CHECKSUM_CHECKSUM_H +#define IPL3CHECKSUM_CHECKSUM_H +#pragma once + +#include +#include + +#include "error.h" +#include "cickinds.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +Ipl3Checksum_Error ipl3checksum_calculate_checksum( + uint32_t *dst_checksum0, + uint32_t *dst_checksum1, + size_t rom_bytes_len, + const uint8_t *rom_bytes, + Ipl3Checksum_CICKind kind +); + +Ipl3Checksum_Error ipl3checksum_calculate_checksum_autodetect( + uint32_t *dst_checksum0, + uint32_t *dst_checksum1, + size_t rom_bytes_len, + const uint8_t *rom_bytes +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/c/include/ipl3checksum/cickinds.h b/bindings/c/include/ipl3checksum/cickinds.h new file mode 100644 index 0000000..a102a15 --- /dev/null +++ b/bindings/c/include/ipl3checksum/cickinds.h @@ -0,0 +1,25 @@ +#ifndef IPL3CHECKSUM_CICKINDS_H +#define IPL3CHECKSUM_CICKINDS_H +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* This needs to be synced with the Rust equivalent in `src/rs/cickinds.rs` */ +typedef enum Ipl3Checksum_CICKind { + Ipl3Checksum_CICKind_CIC_6101, + Ipl3Checksum_CICKind_CIC_6102_7101, + Ipl3Checksum_CICKind_CIC_7102, + Ipl3Checksum_CICKind_CIC_X103, // Both 6103 and 7103 + // 6104/7104 does not exist + Ipl3Checksum_CICKind_CIC_X105, // Both 6105 and 7105 + Ipl3Checksum_CICKind_CIC_X106, // Both 6106 and 7106 +} Ipl3Checksum_CICKind; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/c/include/ipl3checksum/detect.h b/bindings/c/include/ipl3checksum/detect.h new file mode 100644 index 0000000..cc30070 --- /dev/null +++ b/bindings/c/include/ipl3checksum/detect.h @@ -0,0 +1,14 @@ +#ifndef IPL3CHECKSUM_DETECT_H +#define IPL3CHECKSUM_DETECT_H +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/c/include/ipl3checksum/error.h b/bindings/c/include/ipl3checksum/error.h new file mode 100644 index 0000000..d680625 --- /dev/null +++ b/bindings/c/include/ipl3checksum/error.h @@ -0,0 +1,51 @@ +#ifndef IPL3CHECKSUM_ERROR_H +#define IPL3CHECKSUM_ERROR_H +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* This needs to be synced with the Rust equivalent in `src/rs/error.rs` */ +typedef enum Ipl3Checksum_Error_Tag { + Ipl3Checksum_Error_Okay, + Ipl3Checksum_Error_UnalignedRead, + Ipl3Checksum_Error_ByteConversion, + Ipl3Checksum_Error_OutOfBounds, + Ipl3Checksum_Error_NullPointer, + Ipl3Checksum_Error_BufferNotBigEnough, + Ipl3Checksum_Error_BufferSizeIsWrong, + Ipl3Checksum_Error_UnableToDetectCIC, +} Ipl3Checksum_Error_Tag; + +typedef struct Ipl3Checksum_Error { + Ipl3Checksum_Error_Tag tag; + union Ipl3Checksum_Error_Payload { + struct Ipl3Checksum_Error_Payload_UnalignedRead { + size_t offset; + } UnalignedRead; + struct Ipl3Checksum_Error_Payload_ByteConversion { + size_t offset; + } ByteConversion; + struct Ipl3Checksum_Error_Payload_OutOfBounds { + size_t offset; + size_t requested_bytes; + size_t buffer_len; + } OutOfBounds; + struct Ipl3Checksum_Error_Payload_BufferNotBigEnough { + size_t buffer_len; + size_t expected_len; + } BufferNotBigEnough; + struct Ipl3Checksum_Error_Payload_BufferSizeIsWrong { + size_t buffer_len; + size_t expected_len; + } BufferSizeIsWrong; + } payload; +} Ipl3Checksum_Error; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/rs/checksum.rs b/src/rs/checksum.rs index f0d2d35..e872637 100644 --- a/src/rs/checksum.rs +++ b/src/rs/checksum.rs @@ -338,3 +338,61 @@ pub(crate) mod python_bindings { } } } + +#[cfg(feature = "c_bindings")] +mod c_bindings { + #[no_mangle] + pub extern "C" fn ipl3checksum_calculate_checksum( + dst_checksum0: *mut u32, + dst_checksum1: *mut u32, + rom_bytes_len: usize, + rom_bytes: *const u8, + kind: super::CICKind, + ) -> super::Ipl3ChecksumError { + if dst_checksum0.is_null() || dst_checksum1.is_null() || rom_bytes.is_null() { + return super::Ipl3ChecksumError::NullPointer; + } + + let bytes = match super::utils::u8_vec_from_pointer_array(rom_bytes_len, rom_bytes) { + Err(e) => return e, + Ok(d) => d, + }; + + let checksum = match super::calculate_checksum(&bytes, &kind) { + Ok(chk) => chk, + Err(e) => return e, + }; + + unsafe {*dst_checksum0 = checksum.0}; + unsafe {*dst_checksum1 = checksum.1}; + + super::Ipl3ChecksumError::Okay + } + + #[no_mangle] + pub extern "C" fn ipl3checksum_calculate_checksum_autodetect( + dst_checksum0: *mut u32, + dst_checksum1: *mut u32, + rom_bytes_len: usize, + rom_bytes: *const u8, + ) -> super::Ipl3ChecksumError { + if dst_checksum0.is_null() || dst_checksum1.is_null() || rom_bytes.is_null() { + return super::Ipl3ChecksumError::NullPointer; + } + + let bytes = match super::utils::u8_vec_from_pointer_array(rom_bytes_len, rom_bytes) { + Err(e) => return e, + Ok(d) => d, + }; + + let checksum = match super::calculate_checksum_autodetect(&bytes) { + Ok(chk) => chk, + Err(e) => return e, + }; + + unsafe {*dst_checksum0 = checksum.0}; + unsafe {*dst_checksum1 = checksum.1}; + + super::Ipl3ChecksumError::Okay + } +} diff --git a/src/rs/cickinds.rs b/src/rs/cickinds.rs index b2f4648..3cb57d8 100644 --- a/src/rs/cickinds.rs +++ b/src/rs/cickinds.rs @@ -4,7 +4,11 @@ #[cfg(feature = "python_bindings")] use pyo3::prelude::*; +/* This needs to be in sync with the C equivalent at `bindings/c/include/ipl3checksum/cickinds.h` */ #[cfg_attr(feature = "python_bindings", pyclass(module = "ipl3checksum"))] +// repr is kinda complex and I may have got it wrong. +// I tried to follow the stuff at https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html +#[cfg_attr(feature = "c_bindings", repr(C))] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[allow(non_camel_case_types)] /// Enum that represents a CIC kind diff --git a/src/rs/detect.rs b/src/rs/detect.rs index ecdf9f7..5a7789e 100644 --- a/src/rs/detect.rs +++ b/src/rs/detect.rs @@ -26,7 +26,7 @@ pub fn detect_cic_raw(raw_bytes: &[u8]) -> Result { match CICKind::from_hash_md5(&bytes_hash) { Some(cic) => Ok(cic), - None => Err(Ipl3ChecksumError::UnableToDetectCIC { hash: bytes_hash }), + None => Err(Ipl3ChecksumError::UnableToDetectCIC), } } @@ -61,7 +61,7 @@ pub(crate) mod python_bindings { buffer_len: _, expected_len: _, } => Ok(None), - super::Ipl3ChecksumError::UnableToDetectCIC { hash: _ } => Ok(None), + super::Ipl3ChecksumError::UnableToDetectCIC => Ok(None), _ => Err(e), // To trigger an exception on Python's side }, } @@ -78,7 +78,7 @@ pub(crate) mod python_bindings { buffer_len: _, expected_len: _, } => Ok(None), - super::Ipl3ChecksumError::UnableToDetectCIC { hash: _ } => Ok(None), + super::Ipl3ChecksumError::UnableToDetectCIC => Ok(None), _ => Err(e), // To trigger an exception on Python's side }, } diff --git a/src/rs/error.rs b/src/rs/error.rs index aa7998f..f6f062c 100644 --- a/src/rs/error.rs +++ b/src/rs/error.rs @@ -6,8 +6,10 @@ use pyo3::exceptions::PyRuntimeError; #[cfg(feature = "python_bindings")] use pyo3::prelude::*; -/* This needs to be in sync with the C equivalent at `crunch64_error.h` */ -#[cfg_attr(feature = "c_bindings", repr(u32))] +/* This needs to be in sync with the C equivalent at `bindings/c/include/ipl3checksum/error.h` */ +// repr is kinda complex and I may have got it wrong. +// I tried to follow the stuff at https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html +#[cfg_attr(feature = "c_bindings", repr(C))] #[derive(Clone, Debug, PartialEq, Eq, Hash, thiserror::Error)] pub enum Ipl3ChecksumError { #[error("Not an error")] @@ -34,8 +36,8 @@ pub enum Ipl3ChecksumError { buffer_len: usize, expected_len: usize, }, - #[error("Unable to detect the CIC variant because the computed hash did not match any of the known variants. Computed hash: {hash}")] - UnableToDetectCIC { hash: String }, + #[error("Unable to detect the CIC variant because the computed hash did not match any of the known variants")] + UnableToDetectCIC, } #[cfg(feature = "python_bindings")] diff --git a/src/rs/utils.rs b/src/rs/utils.rs index 35a67d1..7586b18 100644 --- a/src/rs/utils.rs +++ b/src/rs/utils.rs @@ -39,3 +39,21 @@ pub(crate) fn read_u32_vec( pub(crate) fn get_hash_md5(bytes: &[u8]) -> String { format!("{:x}", md5::compute(bytes)) } + +#[cfg(feature = "c_bindings")] +pub(crate) fn u8_vec_from_pointer_array( + src_len: usize, + src: *const u8, +) -> Result, Ipl3ChecksumError> { + if src.is_null() { + return Err(Ipl3ChecksumError::NullPointer); + } + + let mut bytes = Vec::with_capacity(src_len); + + for i in 0..src_len { + bytes.push(unsafe { *src.add(i) }); + } + + Ok(bytes) +}