1use std::ffi::{c_char, c_void};
9#[cfg(feature = "encode")]
10use std::io::Write;
11use std::io::{Read, Seek, SeekFrom};
12
13pub struct ReadableStream {
15 userdata: *mut c_void,
17 read_fn: extern "C" fn(*mut c_void, *mut u8, usize) -> usize,
19 seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
21}
22
23#[cfg(feature = "encode")]
24pub struct WriteableStream {
26 userdata: *mut c_void,
28 write_fn: extern "C" fn(*mut c_void, *const u8, usize) -> usize,
30 flush_fn: extern "C" fn(*mut c_void) -> i32,
32 seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
34}
35
36#[repr(C)]
37pub struct Tag {
39 pub name: *mut c_char,
41 pub name_length: usize,
43 pub value: *mut c_char,
45 pub value_length: usize,
47}
48
49#[repr(C)]
50pub enum TlgColorType {
52 Grayscale8,
54 Bgr24,
56 Bgra32,
58}
59
60#[repr(C)]
61pub struct Tlg {
62 pub version: u32,
64 pub width: u32,
66 pub height: u32,
68 pub color_type: TlgColorType,
70 pub data: *mut u8,
72 pub data_length: usize,
74 pub tags: *mut Tag,
76 pub tag_count: usize,
78}
79
80impl Read for ReadableStream {
81 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
82 let ret = (self.read_fn)(self.userdata, buf.as_mut_ptr(), buf.len());
83 Ok(ret)
84 }
85}
86
87impl Seek for ReadableStream {
88 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
89 let (offset, whence) = match pos {
90 SeekFrom::Start(off) => (off as i64, 0),
91 SeekFrom::End(off) => (off, 2),
92 SeekFrom::Current(off) => (off, 1),
93 };
94 let ret = (self.seek_fn)(self.userdata, offset, whence);
95 Ok(ret)
96 }
97}
98
99#[cfg(feature = "encode")]
100impl Write for WriteableStream {
101 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
102 let ret = (self.write_fn)(self.userdata, buf.as_ptr(), buf.len());
103 Ok(ret)
104 }
105
106 fn flush(&mut self) -> std::io::Result<()> {
107 let ret = (self.flush_fn)(self.userdata);
108 if ret == 0 {
109 Ok(())
110 } else {
111 Err(std::io::Error::new(
112 std::io::ErrorKind::Other,
113 "Flush failed",
114 ))
115 }
116 }
117}
118
119#[cfg(feature = "encode")]
120impl Seek for WriteableStream {
121 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
122 let (offset, whence) = match pos {
123 SeekFrom::Start(off) => (off as i64, 0),
124 SeekFrom::End(off) => (off, 2),
125 SeekFrom::Current(off) => (off, 1),
126 };
127 let ret = (self.seek_fn)(self.userdata, offset, whence);
128 Ok(ret)
129 }
130}
131
132#[unsafe(no_mangle)]
134pub unsafe extern "C" fn create_readable_stream(
135 userdata: *mut c_void,
136 read_fn: extern "C" fn(*mut c_void, *mut u8, usize) -> usize,
137 seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
138) -> *mut ReadableStream {
139 let stream = ReadableStream {
140 userdata,
141 read_fn,
142 seek_fn,
143 };
144 Box::into_raw(Box::new(stream))
145}
146
147#[unsafe(no_mangle)]
149pub unsafe extern "C" fn destroy_readable_stream(stream: *mut ReadableStream) {
150 if !stream.is_null() {
151 drop(unsafe { Box::from_raw(stream) });
152 }
153}
154
155#[cfg(feature = "encode")]
157#[unsafe(no_mangle)]
158pub unsafe extern "C" fn create_writeable_stream(
159 userdata: *mut c_void,
160 write_fn: extern "C" fn(*mut c_void, *const u8, usize) -> usize,
161 flush_fn: extern "C" fn(*mut c_void) -> i32,
162 seek_fn: extern "C" fn(*mut c_void, i64, i32) -> u64,
163) -> *mut WriteableStream {
164 let stream = WriteableStream {
165 userdata,
166 write_fn,
167 flush_fn,
168 seek_fn,
169 };
170 Box::into_raw(Box::new(stream))
171}
172
173#[cfg(feature = "encode")]
175#[unsafe(no_mangle)]
176pub unsafe extern "C" fn destroy_writeable_stream(stream: *mut WriteableStream) {
177 if !stream.is_null() {
178 drop(unsafe { Box::from_raw(stream) });
179 }
180}
181
182#[unsafe(no_mangle)]
184pub unsafe extern "C" fn check_tlg(stream: *mut ReadableStream) -> bool {
185 if stream.is_null() {
186 return false;
187 }
188 let stream = unsafe { &mut *stream };
189 match crate::check_tlg(stream) {
190 Ok(valid) => valid,
191 Err(_) => false,
192 }
193}
194
195#[unsafe(no_mangle)]
198pub unsafe extern "C" fn is_valid_tlg(data: *const u8, length: usize) -> bool {
199 let data = unsafe { std::slice::from_raw_parts(data, length) };
200 crate::is_valid_tlg(data)
201}
202
203#[unsafe(no_mangle)]
206pub unsafe extern "C" fn load_tlg(stream: *mut ReadableStream) -> *mut Tlg {
207 if stream.is_null() {
208 return std::ptr::null_mut();
209 }
210 let stream = unsafe { &mut *stream };
211 match crate::load_tlg(stream) {
212 Ok(mut tlg) => {
213 let color_type = match tlg.color {
214 crate::TlgColorType::Grayscale8 => TlgColorType::Grayscale8,
215 crate::TlgColorType::Bgr24 => TlgColorType::Bgr24,
216 crate::TlgColorType::Bgra32 => TlgColorType::Bgra32,
217 };
218 tlg.data.shrink_to_fit();
219 let data_ptr = tlg.data.as_mut_ptr();
220 let data_len = tlg.data.len();
221 std::mem::forget(tlg.data);
222 let (tag, tag_count) = if tlg.tags.is_empty() {
223 (std::ptr::null_mut(), 0)
224 } else {
225 let mut tags = Vec::with_capacity(tlg.tags.len());
226 for (mut name, mut value) in tlg.tags.drain() {
227 let name_ptr = name.as_mut_ptr();
228 let name_len = name.len();
229 name.shrink_to_fit();
230 std::mem::forget(name);
231 let value_ptr = value.as_mut_ptr();
232 let value_len = value.len();
233 value.shrink_to_fit();
234 std::mem::forget(value);
235 tags.push(Tag {
236 name: name_ptr as *mut c_char,
237 name_length: name_len,
238 value: value_ptr as *mut c_char,
239 value_length: value_len,
240 });
241 }
242 let tag_ptr = tags.as_mut_ptr();
243 let tag_count = tags.len();
244 tags.shrink_to_fit();
245 std::mem::forget(tags);
246 (tag_ptr, tag_count)
247 };
248 let tlg_c = Tlg {
249 version: tlg.version,
250 width: tlg.width,
251 height: tlg.height,
252 color_type,
253 data: data_ptr,
254 data_length: data_len,
255 tags: tag,
256 tag_count,
257 };
258 Box::into_raw(Box::new(tlg_c))
259 }
260 Err(_) => std::ptr::null_mut(),
261 }
262}
263
264#[unsafe(no_mangle)]
268pub unsafe extern "C" fn destroy_tlg(tlg: *mut Tlg) {
269 if tlg.is_null() {
270 return;
271 }
272 let tlg = unsafe { Box::from_raw(tlg) };
273 let _data = unsafe { Vec::from_raw_parts(tlg.data, tlg.data_length, tlg.data_length) };
274 if !tlg.tags.is_null() {
275 let tags = unsafe { Vec::from_raw_parts(tlg.tags, tlg.tag_count, tlg.tag_count) };
276 for tag in tags {
277 let _name = unsafe {
278 Vec::from_raw_parts(tag.name as *mut u8, tag.name_length, tag.name_length)
279 };
280 let _value = unsafe {
281 Vec::from_raw_parts(tag.value as *mut u8, tag.value_length, tag.value_length)
282 };
283 }
284 }
285}
286
287#[cfg(feature = "encode")]
290#[unsafe(no_mangle)]
291pub unsafe extern "C" fn save_tlg(tlg: Tlg, stream: *mut WriteableStream) -> bool {
292 if stream.is_null() || tlg.data.is_null() {
293 return false;
294 }
295 let stream = unsafe { &mut *stream };
296 let color = match tlg.color_type {
297 TlgColorType::Grayscale8 => crate::TlgColorType::Grayscale8,
298 TlgColorType::Bgr24 => crate::TlgColorType::Bgr24,
299 TlgColorType::Bgra32 => crate::TlgColorType::Bgra32,
300 };
301 let data = unsafe { std::slice::from_raw_parts(tlg.data, tlg.data_length) }.to_vec();
302 let mut tags = std::collections::HashMap::new();
303 if !tlg.tags.is_null() && tlg.tag_count > 0 {
304 let tag_slice = unsafe { std::slice::from_raw_parts(tlg.tags, tlg.tag_count) };
305 for tag in tag_slice {
306 let name =
307 unsafe { std::slice::from_raw_parts(tag.name as *const u8, tag.name_length) }
308 .to_vec();
309 let value =
310 unsafe { std::slice::from_raw_parts(tag.value as *const u8, tag.value_length) }
311 .to_vec();
312 tags.insert(name, value);
313 }
314 }
315 let tlg_rust = crate::Tlg {
316 version: tlg.version,
317 width: tlg.width,
318 height: tlg.height,
319 color,
320 data,
321 tags,
322 };
323 match crate::save_tlg(&tlg_rust, stream) {
324 Ok(_) => true,
325 Err(_) => false,
326 }
327}