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 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}