Skip to content

Commit

Permalink
Start writing C bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
AngheloAlf committed Dec 16, 2023
1 parent 6c7f557 commit c4cf8ed
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Empty file added bindings/c/Makefile
Empty file.
10 changes: 10 additions & 0 deletions bindings/c/include/ipl3checksum.h
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions bindings/c/include/ipl3checksum/checksum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef IPL3CHECKSUM_CHECKSUM_H
#define IPL3CHECKSUM_CHECKSUM_H
#pragma once

#include <stddef.h>
#include <stdint.h>

#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
25 changes: 25 additions & 0 deletions bindings/c/include/ipl3checksum/cickinds.h
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions bindings/c/include/ipl3checksum/detect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef IPL3CHECKSUM_DETECT_H
#define IPL3CHECKSUM_DETECT_H
#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#ifdef __cplusplus
}
#endif

#endif
51 changes: 51 additions & 0 deletions bindings/c/include/ipl3checksum/error.h
Original file line number Diff line number Diff line change
@@ -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
58 changes: 58 additions & 0 deletions src/rs/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
4 changes: 4 additions & 0 deletions src/rs/cickinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/rs/detect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub fn detect_cic_raw(raw_bytes: &[u8]) -> Result<CICKind, Ipl3ChecksumError> {

match CICKind::from_hash_md5(&bytes_hash) {
Some(cic) => Ok(cic),
None => Err(Ipl3ChecksumError::UnableToDetectCIC { hash: bytes_hash }),
None => Err(Ipl3ChecksumError::UnableToDetectCIC),
}
}

Expand Down Expand Up @@ -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
},
}
Expand All @@ -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
},
}
Expand Down
10 changes: 6 additions & 4 deletions src/rs/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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")]
Expand Down
18 changes: 18 additions & 0 deletions src/rs/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>, 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)
}

0 comments on commit c4cf8ed

Please sign in to comment.