use std::ffi::{c_char, c_void};
#[cfg(feature = "encode")]
use std::io::Write;
use std::io::{Read, Seek, SeekFrom};
pub struct ReadableStream {
userdata: *mut c_void,
read_fn: extern "C" fn(*mut c_void, *mut u8, usize) -> usize,
seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
}
#[cfg(feature = "encode")]
pub struct WriteableStream {
userdata: *mut c_void,
write_fn: extern "C" fn(*mut c_void, *const u8, usize) -> usize,
flush_fn: extern "C" fn(*mut c_void) -> i32,
seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
}
#[repr(C)]
pub struct Tag {
pub name: *mut c_char,
pub name_length: usize,
pub value: *mut c_char,
pub value_length: usize,
}
#[repr(C)]
pub enum TlgColorType {
Grayscale8,
Bgr24,
Bgra32,
}
#[repr(C)]
pub struct Tlg {
pub version: u32,
pub width: u32,
pub height: u32,
pub color_type: TlgColorType,
pub data: *mut u8,
pub data_length: usize,
pub tags: *mut Tag,
pub tag_count: usize,
}
impl Read for ReadableStream {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let ret = (self.read_fn)(self.userdata, buf.as_mut_ptr(), buf.len());
Ok(ret)
}
}
impl Seek for ReadableStream {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
let (offset, whence) = match pos {
SeekFrom::Start(off) => (off as i64, 0),
SeekFrom::End(off) => (off, 2),
SeekFrom::Current(off) => (off, 1),
};
let ret = (self.seek_fn)(self.userdata, offset, whence);
Ok(ret)
}
}
#[cfg(feature = "encode")]
impl Write for WriteableStream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let ret = (self.write_fn)(self.userdata, buf.as_ptr(), buf.len());
Ok(ret)
}
fn flush(&mut self) -> std::io::Result<()> {
let ret = (self.flush_fn)(self.userdata);
if ret == 0 {
Ok(())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Flush failed",
))
}
}
}
#[cfg(feature = "encode")]
impl Seek for WriteableStream {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
let (offset, whence) = match pos {
SeekFrom::Start(off) => (off as i64, 0),
SeekFrom::End(off) => (off, 2),
SeekFrom::Current(off) => (off, 1),
};
let ret = (self.seek_fn)(self.userdata, offset, whence);
Ok(ret)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn create_readable_stream(
userdata: *mut c_void,
read_fn: extern "C" fn(*mut c_void, *mut u8, usize) -> usize,
seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
) -> *mut ReadableStream {
let stream = ReadableStream {
userdata,
read_fn,
seek_fn,
};
Box::into_raw(Box::new(stream))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn destroy_readable_stream(stream: *mut ReadableStream) {
if !stream.is_null() {
drop(unsafe { Box::from_raw(stream) });
}
}
#[cfg(feature = "encode")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn create_writeable_stream(
userdata: *mut c_void,
write_fn: extern "C" fn(*mut c_void, *const u8, usize) -> usize,
flush_fn: extern "C" fn(*mut c_void) -> i32,
seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
) -> *mut WriteableStream {
let stream = WriteableStream {
userdata,
write_fn,
flush_fn,
seek_fn,
};
Box::into_raw(Box::new(stream))
}
#[cfg(feature = "encode")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn destroy_writeable_stream(stream: *mut WriteableStream) {
if !stream.is_null() {
drop(unsafe { Box::from_raw(stream) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn check_tlg(stream: *mut ReadableStream) -> bool {
if stream.is_null() {
return false;
}
let stream = unsafe { &mut *stream };
match crate::check_tlg(stream) {
Ok(valid) => valid,
Err(_) => false,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn is_valid_tlg(data: *const u8, length: usize) -> bool {
let data = unsafe { std::slice::from_raw_parts(data, length) };
crate::is_valid_tlg(data)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn load_tlg(stream: *mut ReadableStream) -> *mut Tlg {
if stream.is_null() {
return std::ptr::null_mut();
}
let stream = unsafe { &mut *stream };
match crate::load_tlg(stream) {
Ok(mut tlg) => {
let color_type = match tlg.color {
crate::TlgColorType::Grayscale8 => TlgColorType::Grayscale8,
crate::TlgColorType::Bgr24 => TlgColorType::Bgr24,
crate::TlgColorType::Bgra32 => TlgColorType::Bgra32,
};
tlg.data.shrink_to_fit();
let data_ptr = tlg.data.as_mut_ptr();
let data_len = tlg.data.len();
std::mem::forget(tlg.data);
let (tag, tag_count) = if tlg.tags.is_empty() {
(std::ptr::null_mut(), 0)
} else {
let mut tags = Vec::with_capacity(tlg.tags.len());
for (mut name, mut value) in tlg.tags.drain() {
let name_ptr = name.as_mut_ptr();
let name_len = name.len();
name.shrink_to_fit();
std::mem::forget(name);
let value_ptr = value.as_mut_ptr();
let value_len = value.len();
value.shrink_to_fit();
std::mem::forget(value);
tags.push(Tag {
name: name_ptr as *mut c_char,
name_length: name_len,
value: value_ptr as *mut c_char,
value_length: value_len,
});
}
let tag_ptr = tags.as_mut_ptr();
let tag_count = tags.len();
tags.shrink_to_fit();
std::mem::forget(tags);
(tag_ptr, tag_count)
};
let tlg_c = Tlg {
version: tlg.version,
width: tlg.width,
height: tlg.height,
color_type,
data: data_ptr,
data_length: data_len,
tags: tag,
tag_count,
};
Box::into_raw(Box::new(tlg_c))
}
Err(_) => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn destroy_tlg(tlg: *mut Tlg) {
if tlg.is_null() {
return;
}
let tlg = unsafe { Box::from_raw(tlg) };
let _data = unsafe { Vec::from_raw_parts(tlg.data, tlg.data_length, tlg.data_length) };
if !tlg.tags.is_null() {
let tags = unsafe { Vec::from_raw_parts(tlg.tags, tlg.tag_count, tlg.tag_count) };
for tag in tags {
let _name = unsafe {
Vec::from_raw_parts(tag.name as *mut u8, tag.name_length, tag.name_length)
};
let _value = unsafe {
Vec::from_raw_parts(tag.value as *mut u8, tag.value_length, tag.value_length)
};
}
}
}
#[cfg(feature = "encode")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn save_tlg(tlg: Tlg, stream: *mut WriteableStream) -> bool {
if stream.is_null() || tlg.data.is_null() {
return false;
}
let stream = unsafe { &mut *stream };
let color = match tlg.color_type {
TlgColorType::Grayscale8 => crate::TlgColorType::Grayscale8,
TlgColorType::Bgr24 => crate::TlgColorType::Bgr24,
TlgColorType::Bgra32 => crate::TlgColorType::Bgra32,
};
let data = unsafe { std::slice::from_raw_parts(tlg.data, tlg.data_length) }.to_vec();
let mut tags = std::collections::HashMap::new();
if !tlg.tags.is_null() && tlg.tag_count > 0 {
let tag_slice = unsafe { std::slice::from_raw_parts(tlg.tags, tlg.tag_count) };
for tag in tag_slice {
let name =
unsafe { std::slice::from_raw_parts(tag.name as *const u8, tag.name_length) }
.to_vec();
let value =
unsafe { std::slice::from_raw_parts(tag.value as *const u8, tag.value_length) }
.to_vec();
tags.insert(name, value);
}
}
let tlg_rust = crate::Tlg {
version: tlg.version,
width: tlg.width,
height: tlg.height,
color,
data,
tags,
};
match crate::save_tlg(&tlg_rust, stream) {
Ok(_) => true,
Err(_) => false,
}
}