1pub use anyhow::Result as AnyResult;
2use zerocopy::{Immutable, IntoBytes};
3
4pub use half::{self, f16};
5pub use num_rational::{self, Rational32};
6pub use raw_window_handle::{self, Win32WindowHandle};
7
8#[derive(Debug, Clone)]
10pub struct AviUtl2Info {
11 pub version: AviUtl2Version,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
29pub struct AviUtl2Version(pub u32);
30impl From<u32> for AviUtl2Version {
31 fn from(value: u32) -> Self {
32 Self(value)
33 }
34}
35impl From<AviUtl2Version> for u32 {
36 fn from(value: AviUtl2Version) -> Self {
37 value.0
38 }
39}
40impl AviUtl2Version {
41 pub fn new(major: u8, minor: u8, patch: u8, build: u8) -> Self {
47 assert!(minor < 100, "minor version must be less than 100");
48 assert!(patch < 100, "patch version must be less than 100");
49 assert!(build < 100, "build version must be less than 100");
50 Self(
51 (major as u32) * 1_000_000
52 + (minor as u32) * 10_000
53 + (patch as u32) * 100
54 + (build as u32),
55 )
56 }
57
58 pub fn major(self) -> u32 {
60 self.0 / 1000000
61 }
62
63 pub fn minor(self) -> u32 {
65 (self.0 / 10000) % 100
66 }
67
68 pub fn patch(self) -> u32 {
70 (self.0 / 100) % 100
71 }
72
73 pub fn build(self) -> u32 {
75 self.0 % 100
76 }
77}
78#[cfg(feature = "aviutl2-alias")]
79impl aviutl2_alias::FromTableValue for AviUtl2Version {
80 type Err = std::num::ParseIntError;
81
82 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
83 let v: u32 = value.parse()?;
84 Ok(Self::from(v))
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct FileFilter {
91 pub name: String,
93 pub extensions: Vec<String>,
95}
96
97#[macro_export]
108macro_rules! file_filters {
109 ($($name:expr => [$($ext:expr),* $(,)?] ),* $(,)?) => {
110 vec![
111 $(
112 $crate::FileFilter {
113 name: $name.to_string(),
114 extensions: vec![$($ext.to_string()),*],
115 }
116 ),*
117 ]
118 };
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable)]
126#[repr(C)]
127pub struct Yc48 {
128 pub y: i16,
131 pub cb: i16,
134 pub cr: i16,
137}
138impl Yc48 {
139 pub fn from_yuy2(yuy2: (u8, u8, u8, u8)) -> (Self, Self) {
141 let (y1, u, y2, v) = yuy2;
142 let ny1 = ((y1 as u16 * 1197) >> 6) - 299;
143 let ny2 = ((y2 as u16 * 1197) >> 6) - 299;
144 let ncb = ((u as u16 - 128) * 4681 + 164) >> 8;
145 let ncr = ((v as u16 - 128) * 4681 + 164) >> 8;
146 (
147 Self {
148 y: ny1 as i16,
149 cb: ncb as i16,
150 cr: ncr as i16,
151 },
152 Self {
153 y: ((ny1 + ny2) >> 1) as i16,
154 cb: ncb as i16,
155 cr: ncr as i16,
156 },
157 )
158 }
159
160 pub fn to_yuy2(self, other: Yc48) -> (u8, u8, u8, u8) {
162 let y1 = ((self.y * 219 + 383) >> 12) + 16;
163 let y2 = ((other.y * 219 + 383) >> 12) + 16;
164 let u = (((self.cb + 2048) * 7 + 66) >> 7) + 16;
165 let v = (((self.cr + 2048) * 7 + 66) >> 7) + 16;
166 let y1 = y1.min(255) as u8;
167 let y2 = y2.min(255) as u8;
168 let u = u.min(255) as u8;
169 let v = v.min(255) as u8;
170 (y1, u, y2, v)
171 }
172
173 pub fn from_rgb(self, rgb: (u8, u8, u8)) -> Self {
175 let (r, g, b) = rgb;
176 let r = i16::from(r);
177 let g = i16::from(g);
178 let b = i16::from(b);
179 let y = ((4918 * r + 354) >> 10) + ((9655 * g + 585) >> 10) + ((1875 * b + 523) >> 10);
180 let cb = ((-2775 * r + 240) >> 10) + ((-5449 * g + 515) >> 10) + ((8224 * b + 256) >> 10);
181 let cr = ((8224 * r + 256) >> 10) + ((-6887 * g + 110) >> 10) + ((-1337 * b + 646) >> 10);
182
183 Yc48 { y, cb, cr }
184 }
185
186 pub fn to_rgb(self) -> (u8, u8, u8) {
188 let y = self.y as i32;
189 let cr = self.cr as i32;
190 let cb = self.cr as i32;
191 let r = (255 * y + ((((22881 * cr) >> 16) + 3) << 10)) >> 12;
192 let g = (255 * y + ((((-5616 * cb) >> 16) + ((-11655 * cr) >> 16) + 3) << 10)) >> 12;
193 let b = (255 * y + ((((28919 * cb) >> 16) + 3) << 10)) >> 12;
194
195 let r = r.min(255) as u8;
196 let g = g.min(255) as u8;
197 let b = b.min(255) as u8;
198 (r, g, b)
199 }
200}
201
202pub(crate) fn format_file_filters(file_filters: &[FileFilter]) -> String {
203 let mut file_filter = String::new();
204 for filter in file_filters {
205 let display = format!(
206 "{} ({})",
207 filter.name,
208 if filter.extensions.is_empty() {
209 "*".to_string()
210 } else {
211 filter
212 .extensions
213 .iter()
214 .map(|ext| format!(".{ext}"))
215 .collect::<Vec<_>>()
216 .join(", ")
217 }
218 );
219 file_filter.push_str(&display);
220 file_filter.push('\x00');
221 if filter.extensions.is_empty() {
222 file_filter.push('*');
223 } else {
224 file_filter.push_str(
225 &filter
226 .extensions
227 .iter()
228 .map(|ext| format!("*.{ext}"))
229 .collect::<Vec<_>>()
230 .join(";"),
231 );
232 }
233 file_filter.push('\x00');
234 }
235
236 file_filter
237}
238
239pub(crate) enum LeakType {
240 WideString,
241 ValueVector { len: usize, name: String },
242 Null,
243 Other(String),
244}
245
246pub(crate) struct LeakManager {
247 ptrs: std::sync::Mutex<Vec<(LeakType, usize)>>,
248}
249
250pub(crate) trait IntoLeakedPtr {
251 fn into_leaked_ptr(self) -> (LeakType, usize);
252}
253pub(crate) trait LeakableValue {}
254
255impl LeakManager {
256 pub fn new() -> Self {
257 Self {
258 ptrs: std::sync::Mutex::new(Vec::new()),
259 }
260 }
261
262 pub fn leak<T: IntoLeakedPtr>(&self, value: T) -> *const T {
263 log::debug!("Leaking memory for type {}", std::any::type_name::<T>());
264 let mut ptrs = self.ptrs.lock().unwrap();
265 let leaked = value.into_leaked_ptr();
266 let ptr = leaked.1;
267 ptrs.push(leaked);
268 ptr as *const T
269 }
270
271 pub fn leak_as_wide_string(&self, s: &str) -> *const u16 {
272 log::debug!("Leaking wide string: {}", s);
273 let mut wide: Vec<u16> = s.encode_utf16().collect();
274 wide.push(0); let boxed = wide.into_boxed_slice();
276 let ptr = Box::into_raw(boxed) as *mut u16 as usize;
277 let mut ptrs = self.ptrs.lock().unwrap();
278 ptrs.push((LeakType::WideString, ptr));
279 ptr as *const u16
280 }
281
282 pub fn leak_value_vec<T: LeakableValue>(&self, vec: Vec<T>) -> *const T {
296 log::debug!(
297 "Leaking value vector of type {}",
298 std::any::type_name::<T>()
299 );
300 let len = vec.len();
301 let boxed = vec.into_boxed_slice();
302 let ptr = Box::into_raw(boxed) as *mut T as usize;
303 let mut ptrs = self.ptrs.lock().unwrap();
304 ptrs.push((
305 LeakType::ValueVector {
306 len,
307 name: std::any::type_name::<T>().to_string(),
308 },
309 ptr,
310 ));
311 ptr as *const T
312 }
313
314 pub fn free_leaked_memory(&self) {
315 let mut ptrs = self.ptrs.lock().unwrap();
316 while let Some((ptr_type, ptr)) = ptrs.pop() {
317 match ptr_type {
318 LeakType::WideString => unsafe {
319 let _ = Box::from_raw(ptr as *mut u16);
320 },
321 LeakType::ValueVector { len, name } => {
322 Self::free_leaked_memory_leakable_value(&name, ptr, len);
323 }
324 LeakType::Null => {
325 assert!(ptr == 0);
326 }
327 LeakType::Other(ref type_name) => {
328 Self::free_leaked_memory_other_ptr(type_name, ptr);
329 }
330 }
331 }
332 }
333}
334macro_rules! impl_leak_ptr {
335 ($($t:ty),* $(,)?) => {
336 $(
337 impl IntoLeakedPtr for $t {
338 fn into_leaked_ptr(self) -> (LeakType, usize) {
339 let boxed = Box::new(self);
340 let ptr = Box::into_raw(boxed) as usize;
341 (LeakType::Other(std::any::type_name::<$t>().to_string()), ptr)
342 }
343 }
344 )*
345
346 impl LeakManager {
347 fn free_leaked_memory_other_ptr(ptr_type: &str, ptr: usize) {
348 unsafe {
349 match ptr_type {
350 $(
351 t if t == std::any::type_name::<$t>() => {
352 let _ = Box::from_raw(ptr as *mut $t);
353 },
354 )*
355 _ => {
356 unreachable!("Unknown leaked pointer type: {}", ptr_type);
357 }
358 }
359 }
360 }
361 }
362 };
363}
364macro_rules! impl_leakable_value {
365 ($($t:ty),* $(,)?) => {
366 $(
367 impl LeakableValue for $t {}
368 )*
369 impl LeakManager {
370 fn free_leaked_memory_leakable_value(type_name: &str, ptr: usize, len: usize) {
371 unsafe {
372 match type_name {
373 $(
374 t if t == std::any::type_name::<$t>() => {
375 let _ = Box::from_raw(std::slice::from_raw_parts_mut(ptr as *mut $t, len));
376 },
377 )*
378 _ => {
379 unreachable!("Unknown leaked value vector type: {}", type_name);
380 }
381 }
382 }
383 }
384 }
385 };
386}
387impl_leak_ptr!(
388 aviutl2_sys::input2::WAVEFORMATEX,
389 aviutl2_sys::output2::BITMAPINFOHEADER,
390 aviutl2_sys::filter2::FILTER_ITEM,
391);
392impl_leakable_value!(
393 aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM,
394 aviutl2_sys::module2::SCRIPT_MODULE_FUNCTION,
395 usize
396);
397
398impl<T: IntoLeakedPtr> IntoLeakedPtr for Option<T> {
399 fn into_leaked_ptr(self) -> (LeakType, usize) {
400 match self {
401 Some(value) => value.into_leaked_ptr(),
402 None => (LeakType::Null, 0),
403 }
404 }
405}
406
407impl Drop for LeakManager {
408 fn drop(&mut self) {
409 self.free_leaked_memory();
410 }
411}
412
413pub(crate) unsafe fn load_wide_string(ptr: *const u16) -> String {
418 if ptr.is_null() {
419 return String::new();
420 }
421
422 let mut len = 0;
423 while unsafe { *ptr.add(len) } != 0 {
424 len += 1;
425 }
426
427 unsafe { String::from_utf16_lossy(std::slice::from_raw_parts(ptr, len)) }
428}
429
430pub(crate) fn alert_error(error: &anyhow::Error) {
431 let _ = native_dialog::DialogBuilder::message()
432 .set_title("エラー")
433 .set_level(native_dialog::MessageLevel::Error)
434 .set_text(format!("エラーが発生しました: {error}"))
435 .alert()
436 .show();
437}
438
439#[derive(Debug, Clone, PartialEq, Eq)]
441pub(crate) struct CWString(Vec<u16>);
442
443#[derive(thiserror::Error, Debug)]
445#[error("String contains null byte")]
446pub struct NullByteError {
447 position: usize,
448 u16_seq: Vec<u16>,
449}
450impl NullByteError {
451 pub fn nul_position(&self) -> usize {
452 self.position
453 }
454
455 pub fn into_vec(self) -> Vec<u16> {
456 self.u16_seq
457 }
458}
459
460impl CWString {
461 pub fn new(string: &str) -> Result<Self, NullByteError> {
467 let mut wide: Vec<u16> = string.encode_utf16().collect();
468 if let Some((pos, _)) = wide.iter().enumerate().find(|&(_, &c)| c == 0) {
469 return Err(NullByteError {
470 position: pos,
471 u16_seq: wide,
472 });
473 }
474 wide.push(0); Ok(Self(wide))
476 }
477
478 pub fn as_ptr(&self) -> *const u16 {
488 self.0.as_ptr()
489 }
490}
491impl std::fmt::Display for CWString {
492 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493 let s = String::from_utf16_lossy(&self.0[..self.0.len() - 1]);
494 write!(f, "{}", s)
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501
502 #[test]
503 fn test_format_file_filters() {
504 let filters = vec![
505 FileFilter {
506 name: "Image Files".to_string(),
507 extensions: vec!["png".to_string(), "jpg".to_string()],
508 },
509 FileFilter {
510 name: "All Files".to_string(),
511 extensions: vec![],
512 },
513 ];
514 let formatted = format_file_filters(&filters);
515 let expected = "Image Files (.png, .jpg)\x00*.png;*.jpg\x00All Files (*)\x00*\x00";
516 assert_eq!(formatted, expected);
517 }
518
519 #[test]
520 fn test_file_filters_macro() {
521 let filters = file_filters! {
522 "Image Files" => ["png", "jpg"],
523 "All Files" => []
524 };
525 assert_eq!(filters.len(), 2);
526 assert_eq!(filters[0].name, "Image Files");
527 assert_eq!(
528 filters[0].extensions,
529 vec!["png".to_string(), "jpg".to_string()]
530 );
531 assert_eq!(filters[1].name, "All Files");
532 assert_eq!(filters[1].extensions, Vec::<String>::new());
533 }
534
535 #[test]
536 fn test_aviutl2_version() {
537 let version = AviUtl2Version::new(2, 0, 15, 0);
538 assert_eq!(version.0, 2001500);
539 assert_eq!(version.major(), 2);
540 assert_eq!(version.minor(), 0);
541 assert_eq!(version.patch(), 15);
542 assert_eq!(version.build(), 0);
543
544 let version_from_u32: AviUtl2Version = 2001500u32.into();
545 assert_eq!(version_from_u32, version);
546
547 let u32_from_version: u32 = version.into();
548 assert_eq!(u32_from_version, 2001500);
549 }
550
551 #[test]
552 fn test_cwstring_new() {
553 let s = "Hello, world!";
554 let cwstring = CWString::new(s).unwrap();
555 assert_eq!(unsafe { load_wide_string(cwstring.as_ptr()) }, s);
556
557 let s_with_nul = "Hello\0world!";
558 let err = CWString::new(s_with_nul).unwrap_err();
559 assert_eq!(err.nul_position(), 5);
560 }
561}