libsmacker/
lib.rs

1use std::{os::raw::c_ulong, slice::from_raw_parts};
2
3#[macro_use]
4extern crate bitflags;
5#[macro_use]
6extern crate enum_primitive;
7use enum_primitive::FromPrimitive;
8use libsmacker_sys as ffi;
9
10enum_from_primitive! {
11#[derive(Debug, Copy, Clone, PartialEq)]
12#[repr(i8)]
13pub enum SeekResult {
14    Done = ffi::SMK_DONE,
15    More = ffi::SMK_MORE,
16    Last = ffi::SMK_LAST,
17}
18}
19
20enum_from_primitive! {
21#[derive(Debug, Copy, Clone, PartialEq)]
22#[repr(u8)]
23pub enum YScaleMode {
24    None = ffi::SMK_FLAG_Y_NONE,
25    Interlace = ffi::SMK_FLAG_Y_INTERLACE,
26    Double = ffi::SMK_FLAG_Y_DOUBLE,
27}
28}
29
30bitflags! {
31    pub struct EnableMask: u8 {
32        const TRACK0 = ffi::SMK_AUDIO_TRACK_0;
33        const TRACK1 = ffi::SMK_AUDIO_TRACK_1;
34        const TRACK2 = ffi::SMK_AUDIO_TRACK_2;
35        const TRACK3 = ffi::SMK_AUDIO_TRACK_3;
36        const TRACK4 = ffi::SMK_AUDIO_TRACK_4;
37        const TRACK5 = ffi::SMK_AUDIO_TRACK_5;
38        const TRACK6 = ffi::SMK_AUDIO_TRACK_6;
39        const VIDEO_TRACK = ffi::SMK_VIDEO_TRACK;
40    }
41}
42
43pub struct Smk {
44    // This pointer must never be allowed to leave the struct
45    ctx: *mut ffi::smk_t,
46    pub stream_info: StreamInfo,
47    pub video_info: VideoInfo,
48    pub audio_info: AudioInfo,
49}
50
51pub struct StreamInfo {
52    pub current_frame: u32,
53    pub frame_count: u32,
54    pub microseconds_per_frame: f64,
55}
56
57pub struct VideoInfo {
58    pub width: u32,
59    pub height: u32,
60    pub y_scale_mode: YScaleMode,
61}
62
63pub struct AudioInfo {
64    pub track_mask: EnableMask,
65    pub channels: [u8; 7],
66    pub bit_depth: [u8; 7],
67    pub sample_rate: [u32; 7],
68}
69
70pub fn stream_info(ctx: *mut ffi::smk_t) -> Option<StreamInfo> {
71    unsafe {
72        let mut current_frame: u32 = 0;
73        let mut frame_count: u32 = 0;
74        let mut microseconds_per_frame: f64 = 0.0;
75        let result = ffi::smk_info_all(
76            ctx,
77            &mut current_frame,
78            &mut frame_count,
79            &mut microseconds_per_frame,
80        );
81        if result == ffi::SMK_ERROR {
82            None
83        } else {
84            Some(StreamInfo {
85                current_frame,
86                frame_count,
87                microseconds_per_frame,
88            })
89        }
90    }
91}
92
93pub fn video_info(ctx: *mut ffi::smk_t) -> Option<VideoInfo> {
94    unsafe {
95        let mut width: u32 = 0;
96        let mut height: u32 = 0;
97        let mut y_scale_mode: u8 = 0;
98        let result = ffi::smk_info_video(ctx, &mut width, &mut height, &mut y_scale_mode);
99        if result == ffi::SMK_ERROR {
100            None
101        } else {
102            Some(VideoInfo {
103                width,
104                height,
105                y_scale_mode: YScaleMode::from_u8(y_scale_mode).unwrap_or(YScaleMode::None),
106            })
107        }
108    }
109}
110
111pub fn audio_info(ctx: *mut ffi::smk_t) -> Option<AudioInfo> {
112    unsafe {
113        let mut track_mask: u8 = 0;
114        let mut channels: [u8; 7] = [0; 7];
115        let mut bit_depth: [u8; 7] = [0; 7];
116        let mut sample_rate: [u32; 7] = [0; 7];
117        let result = ffi::smk_info_audio(
118            ctx,
119            &mut track_mask,
120            channels.as_mut_ptr(),
121            bit_depth.as_mut_ptr(),
122            sample_rate.as_mut_ptr(),
123        );
124        if result == ffi::SMK_ERROR {
125            None
126        } else {
127            Some(AudioInfo {
128                track_mask: EnableMask::from_bits(track_mask).unwrap_or(EnableMask::empty()),
129                channels,
130                bit_depth,
131                sample_rate,
132            })
133        }
134    }
135}
136
137impl Smk {
138    pub fn open_memory(buffer: &[u8]) -> Option<Self> {
139        unsafe {
140            let ctx = ffi::smk_open_memory(buffer.as_ptr(), buffer.len() as c_ulong);
141            if ctx.is_null() {
142                None
143            } else {
144                let stream_info = stream_info(ctx)?;
145                let audio_info = audio_info(ctx)?;
146                let video_info = video_info(ctx)?;
147
148                Some(Self {
149                    ctx,
150                    stream_info,
151                    audio_info,
152                    video_info,
153                })
154            }
155        }
156    }
157
158    pub fn get_palette(&self) -> Option<&[u8]> {
159        unsafe {
160            let ptr = ffi::smk_get_palette(self.ctx);
161            if ptr.is_null() {
162                None
163            } else {
164                Some(from_raw_parts(ptr, 768))
165            }
166        }
167    }
168
169    pub fn get_video(&self) -> Option<&[u8]> {
170        unsafe {
171            let ptr = ffi::smk_get_video(self.ctx);
172            if ptr.is_null() {
173                None
174            } else {
175                Some(from_raw_parts(
176                    ptr,
177                    (self.video_info.width * self.video_info.height) as usize,
178                ))
179            }
180        }
181    }
182
183    pub fn get_audio(&self, track: u8) -> Option<&[u8]> {
184        unsafe {
185            let size = ffi::smk_get_audio_size(self.ctx, track);
186            let ptr = ffi::smk_get_audio(self.ctx, track);
187
188            if ptr.is_null() {
189                None
190            } else {
191                Some(from_raw_parts(ptr, size as usize))
192            }
193        }
194    }
195
196    pub fn first(&self) -> Option<SeekResult> {
197        unsafe { SeekResult::from_i8(ffi::smk_first(self.ctx)) }
198    }
199
200    pub fn next(&self) -> Option<SeekResult> {
201        unsafe { SeekResult::from_i8(ffi::smk_next(self.ctx)) }
202    }
203
204    pub fn seek_keyframe(&self, frame: u32) -> Option<SeekResult> {
205        unsafe { SeekResult::from_i8(ffi::smk_seek_keyframe(self.ctx, frame)) }
206    }
207
208    pub fn enable(&self, mask: EnableMask) {
209        unsafe {
210            ffi::smk_enable_all(self.ctx, mask.bits());
211        }
212    }
213}
214impl Drop for Smk {
215    fn drop(&mut self) {
216        unsafe { ffi::smk_close(self.ctx) }
217    }
218}