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
15pub const MINIMUM_AVIUTL2_VERSION: AviUtl2Version = AviUtl2Version(2004500);
17
18pub fn ensure_minimum_aviutl2_version(version: AviUtl2Version) -> AnyResult<()> {
20 anyhow::ensure!(
21 version >= MINIMUM_AVIUTL2_VERSION,
22 "AviUtl2 version {version} is not supported. {MINIMUM_AVIUTL2_VERSION} or higher is required.",
23 );
24 Ok(())
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41pub struct AviUtl2Version(pub u32);
42impl From<u32> for AviUtl2Version {
43 fn from(value: u32) -> Self {
44 Self(value)
45 }
46}
47impl From<AviUtl2Version> for u32 {
48 fn from(value: AviUtl2Version) -> Self {
49 value.0
50 }
51}
52impl AviUtl2Version {
53 pub fn new(major: u8, minor: u8, patch: u8, build: u8) -> Self {
59 assert!(minor < 100, "minor version must be less than 100");
60 assert!(patch < 100, "patch version must be less than 100");
61 assert!(build < 100, "build version must be less than 100");
62 Self(
63 (major as u32) * 1_000_000
64 + (minor as u32) * 10_000
65 + (patch as u32) * 100
66 + (build as u32),
67 )
68 }
69
70 pub fn major(self) -> u32 {
72 self.0 / 1000000
73 }
74
75 pub fn minor(self) -> u32 {
77 (self.0 / 10000) % 100
78 }
79
80 pub fn patch(self) -> u32 {
82 (self.0 / 100) % 100
83 }
84
85 pub fn build(self) -> u32 {
87 self.0 % 100
88 }
89}
90impl std::fmt::Display for AviUtl2Version {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 let build_str = if self.build() > 0 {
93 ('a'..='z')
94 .nth((self.build() - 1) as usize)
95 .map(|c| c.to_string())
96 .unwrap_or_default()
97 } else {
98 String::new()
99 };
100 write!(
101 f,
102 "{}.{:0>2}beta{}{}",
103 self.major(),
104 self.minor(),
105 self.patch(),
106 build_str
107 )
108 }
109}
110#[cfg(feature = "aviutl2-alias")]
111impl aviutl2_alias::FromTableValue for AviUtl2Version {
112 type Err = std::num::ParseIntError;
113
114 fn from_table_value(value: &str) -> Result<Self, Self::Err> {
115 let v: u32 = value.parse()?;
116 Ok(Self::from(v))
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct FileFilter {
123 pub name: String,
125 pub extensions: Vec<String>,
127}
128
129#[macro_export]
140macro_rules! file_filters {
141 ($($name:expr => [$($ext:expr),* $(,)?] ),* $(,)?) => {
142 vec![
143 $(
144 $crate::FileFilter {
145 name: $name.to_string(),
146 extensions: vec![$($ext.to_string()),*],
147 }
148 ),*
149 ]
150 };
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable)]
158#[repr(C)]
159pub struct Yc48 {
160 pub y: i16,
163 pub cb: i16,
166 pub cr: i16,
169}
170impl Yc48 {
171 pub fn from_yuy2(yuy2: (u8, u8, u8, u8)) -> (Self, Self) {
173 let (y1, u, y2, v) = yuy2;
174 let ny1 = ((y1 as u16 * 1197) >> 6) - 299;
175 let ny2 = ((y2 as u16 * 1197) >> 6) - 299;
176 let ncb = ((u as u16 - 128) * 4681 + 164) >> 8;
177 let ncr = ((v as u16 - 128) * 4681 + 164) >> 8;
178 (
179 Self {
180 y: ny1 as i16,
181 cb: ncb as i16,
182 cr: ncr as i16,
183 },
184 Self {
185 y: ((ny1 + ny2) >> 1) as i16,
186 cb: ncb as i16,
187 cr: ncr as i16,
188 },
189 )
190 }
191
192 pub fn to_yuy2(self, other: Yc48) -> (u8, u8, u8, u8) {
194 let y1 = ((self.y * 219 + 383) >> 12) + 16;
195 let y2 = ((other.y * 219 + 383) >> 12) + 16;
196 let u = (((self.cb + 2048) * 7 + 66) >> 7) + 16;
197 let v = (((self.cr + 2048) * 7 + 66) >> 7) + 16;
198 let y1 = y1.min(255) as u8;
199 let y2 = y2.min(255) as u8;
200 let u = u.min(255) as u8;
201 let v = v.min(255) as u8;
202 (y1, u, y2, v)
203 }
204
205 pub fn from_rgb(self, rgb: (u8, u8, u8)) -> Self {
207 let (r, g, b) = rgb;
208 let r = i16::from(r);
209 let g = i16::from(g);
210 let b = i16::from(b);
211 let y = ((4918 * r + 354) >> 10) + ((9655 * g + 585) >> 10) + ((1875 * b + 523) >> 10);
212 let cb = ((-2775 * r + 240) >> 10) + ((-5449 * g + 515) >> 10) + ((8224 * b + 256) >> 10);
213 let cr = ((8224 * r + 256) >> 10) + ((-6887 * g + 110) >> 10) + ((-1337 * b + 646) >> 10);
214
215 Yc48 { y, cb, cr }
216 }
217
218 pub fn to_rgb(self) -> (u8, u8, u8) {
220 let y = self.y as i32;
221 let cr = self.cr as i32;
222 let cb = self.cr as i32;
223 let r = (255 * y + ((((22881 * cr) >> 16) + 3) << 10)) >> 12;
224 let g = (255 * y + ((((-5616 * cb) >> 16) + ((-11655 * cr) >> 16) + 3) << 10)) >> 12;
225 let b = (255 * y + ((((28919 * cb) >> 16) + 3) << 10)) >> 12;
226
227 let r = r.min(255) as u8;
228 let g = g.min(255) as u8;
229 let b = b.min(255) as u8;
230 (r, g, b)
231 }
232}
233
234pub(crate) fn format_file_filters(file_filters: &[FileFilter]) -> String {
235 let mut file_filter = String::new();
236 for filter in file_filters {
237 let display = format!(
238 "{} ({})",
239 filter.name,
240 if filter.extensions.is_empty() {
241 "*".to_string()
242 } else {
243 filter
244 .extensions
245 .iter()
246 .map(|ext| format!(".{ext}"))
247 .collect::<Vec<_>>()
248 .join(", ")
249 }
250 );
251 file_filter.push_str(&display);
252 file_filter.push('\x00');
253 if filter.extensions.is_empty() {
254 file_filter.push('*');
255 } else {
256 file_filter.push_str(
257 &filter
258 .extensions
259 .iter()
260 .map(|ext| format!("*.{ext}"))
261 .collect::<Vec<_>>()
262 .join(";"),
263 );
264 }
265 file_filter.push('\x00');
266 }
267
268 file_filter
269}
270
271pub(crate) struct KillablePointer<T> {
272 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
273 inner: *mut T,
274}
275unsafe impl<T> Send for KillablePointer<T> {}
276unsafe impl<T> Sync for KillablePointer<T> {}
277impl<T> Drop for KillablePointer<T> {
278 fn drop(&mut self) {
279 self.kill_switch
280 .store(true, std::sync::atomic::Ordering::SeqCst);
281 }
282}
283impl<T> KillablePointer<T> {
284 pub fn new(inner: T) -> Self {
285 Self {
286 kill_switch: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
287 inner: Box::into_raw(Box::new(inner)),
288 }
289 }
290
291 pub fn create_child(&self) -> ChildKillablePointer<T> {
292 ChildKillablePointer {
293 kill_switch: std::sync::Arc::clone(&self.kill_switch),
294 inner: self.inner,
295 }
296 }
297}
298
299pub(crate) struct ChildKillablePointer<T> {
300 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
301 inner: *mut T,
302}
303unsafe impl<T> Send for ChildKillablePointer<T> {}
304unsafe impl<T> Sync for ChildKillablePointer<T> {}
305impl<T> ChildKillablePointer<T> {
306 pub fn is_killed(&self) -> bool {
307 self.kill_switch.load(std::sync::atomic::Ordering::SeqCst)
308 }
309
310 pub unsafe fn as_mut(&mut self) -> &mut T {
311 if self.is_killed() {
312 panic!("parent KillablePointer has been dropped");
313 }
314 unsafe { &mut *self.inner }
315 }
316}
317
318pub(crate) enum LeakType {
319 WideString,
320 ValueVector { len: usize, name: String },
321 Null,
322 Other(String),
323}
324
325pub(crate) struct LeakManager {
326 ptrs: std::sync::Mutex<Vec<(LeakType, usize)>>,
327}
328
329pub(crate) trait IntoLeakedPtr {
330 fn into_leaked_ptr(self) -> (LeakType, usize);
331}
332pub(crate) trait LeakableValue {}
333
334impl LeakManager {
335 pub fn new() -> Self {
336 Self {
337 ptrs: std::sync::Mutex::new(Vec::new()),
338 }
339 }
340
341 pub fn leak<T: IntoLeakedPtr>(&self, value: T) -> *const T {
342 tracing::debug!("Leaking memory for type {}", std::any::type_name::<T>());
343 let mut ptrs = self.ptrs.lock().unwrap();
344 let leaked = value.into_leaked_ptr();
345 let ptr = leaked.1;
346 ptrs.push(leaked);
347 ptr as *const T
348 }
349
350 pub fn leak_as_wide_string(&self, s: &str) -> *const u16 {
351 tracing::debug!("Leaking wide string: {}", s);
352 let mut wide: Vec<u16> = s.encode_utf16().collect();
353 wide.push(0); let boxed = wide.into_boxed_slice();
355 let ptr = Box::into_raw(boxed) as *mut u16 as usize;
356 let mut ptrs = self.ptrs.lock().unwrap();
357 ptrs.push((LeakType::WideString, ptr));
358 ptr as *const u16
359 }
360
361 pub fn leak_value_vec<T: LeakableValue>(&self, vec: Vec<T>) -> *const T {
375 tracing::debug!(
376 "Leaking value vector of type {}",
377 std::any::type_name::<T>()
378 );
379 let len = vec.len();
380 let boxed = vec.into_boxed_slice();
381 let ptr = Box::into_raw(boxed) as *mut T as usize;
382 let mut ptrs = self.ptrs.lock().unwrap();
383 ptrs.push((
384 LeakType::ValueVector {
385 len,
386 name: std::any::type_name::<T>().to_string(),
387 },
388 ptr,
389 ));
390 ptr as *const T
391 }
392
393 pub fn free_leaked_memory(&self) {
394 let mut ptrs = self.ptrs.lock().unwrap();
395 while let Some((ptr_type, ptr)) = ptrs.pop() {
396 match ptr_type {
397 LeakType::WideString => unsafe {
398 let _ = Box::from_raw(ptr as *mut u16);
399 },
400 LeakType::ValueVector { len, name } => {
401 Self::free_leaked_memory_leakable_value(&name, ptr, len);
402 }
403 LeakType::Null => {
404 assert!(ptr == 0);
405 }
406 LeakType::Other(ref type_name) => {
407 Self::free_leaked_memory_other_ptr(type_name, ptr);
408 }
409 }
410 }
411 }
412}
413macro_rules! impl_leak_ptr {
414 ($($t:ty),* $(,)?) => {
415 $(
416 impl IntoLeakedPtr for $t {
417 fn into_leaked_ptr(self) -> (LeakType, usize) {
418 let boxed = Box::new(self);
419 let ptr = Box::into_raw(boxed) as usize;
420 (LeakType::Other(std::any::type_name::<$t>().to_string()), ptr)
421 }
422 }
423 )*
424
425 impl LeakManager {
426 fn free_leaked_memory_other_ptr(ptr_type: &str, ptr: usize) {
427 unsafe {
428 match ptr_type {
429 $(
430 t if t == std::any::type_name::<$t>() => {
431 let _ = Box::from_raw(ptr as *mut $t);
432 },
433 )*
434 _ => {
435 unreachable!("Unknown leaked pointer type: {}", ptr_type);
436 }
437 }
438 }
439 }
440 }
441 };
442}
443macro_rules! impl_leakable_value {
444 ($($t:ty),* $(,)?) => {
445 $(
446 impl LeakableValue for $t {}
447 )*
448 impl LeakManager {
449 fn free_leaked_memory_leakable_value(type_name: &str, ptr: usize, len: usize) {
450 unsafe {
451 match type_name {
452 $(
453 t if t == std::any::type_name::<$t>() => {
454 let _ = Box::from_raw(std::slice::from_raw_parts_mut(ptr as *mut $t, len));
455 },
456 )*
457 _ => {
458 unreachable!("Unknown leaked value vector type: {}", type_name);
459 }
460 }
461 }
462 }
463 }
464 };
465}
466impl_leak_ptr!(
467 aviutl2_sys::input2::WAVEFORMATEX,
468 aviutl2_sys::output2::BITMAPINFOHEADER,
469 aviutl2_sys::filter2::FILTER_ITEM,
470);
471impl_leakable_value!(
472 aviutl2_sys::filter2::FILTER_ITEM_SELECT_ITEM,
473 aviutl2_sys::module2::SCRIPT_MODULE_FUNCTION,
474 usize
475);
476
477impl<T: IntoLeakedPtr> IntoLeakedPtr for Option<T> {
478 fn into_leaked_ptr(self) -> (LeakType, usize) {
479 match self {
480 Some(value) => value.into_leaked_ptr(),
481 None => (LeakType::Null, 0),
482 }
483 }
484}
485
486impl Drop for LeakManager {
487 fn drop(&mut self) {
488 self.free_leaked_memory();
489 }
490}
491
492pub(crate) unsafe fn load_wide_string(ptr: *const u16) -> String {
497 if ptr.is_null() {
498 return String::new();
499 }
500
501 let mut len = 0;
502 while unsafe { *ptr.add(len) } != 0 {
503 len += 1;
504 }
505
506 unsafe { String::from_utf16_lossy(std::slice::from_raw_parts(ptr, len)) }
507}
508
509#[doc(hidden)]
510#[expect(private_bounds)]
511pub fn __output_log_if_error<T: MenuCallbackReturn>(result: T) {
512 if let Some(err_msg) = result.into_optional_error() {
513 let _ = crate::logger::write_error_log(&err_msg);
514 }
515}
516
517#[doc(hidden)]
518#[expect(private_bounds)]
519pub fn __log_and_beep_if_error<T: MenuCallbackReturn>(result: T) {
520 if let Some(err_msg) = result.into_optional_error() {
521 let _ = crate::logger::write_error_log(&err_msg);
522
523 let _ = unsafe {
524 windows::Win32::System::Diagnostics::Debug::MessageBeep(
525 windows::Win32::UI::WindowsAndMessaging::MB_ICONEXCLAMATION,
526 )
527 };
528 }
529}
530
531trait MenuCallbackReturn {
532 fn into_optional_error(self) -> Option<String>;
533}
534impl<E> MenuCallbackReturn for Result<(), E>
535where
536 Box<dyn std::error::Error>: From<E>,
537{
538 fn into_optional_error(self) -> Option<String> {
539 match self {
540 Ok(_) => None,
541 Err(e) => {
542 let boxed: Box<dyn std::error::Error> = e.into();
543 Some(format!("{}", boxed))
544 }
545 }
546 }
547}
548impl MenuCallbackReturn for () {
549 fn into_optional_error(self) -> Option<String> {
550 None
551 }
552}
553
554#[derive(Debug, Clone, PartialEq, Eq)]
556pub(crate) struct CWString(Vec<u16>);
557
558#[derive(thiserror::Error, Debug)]
560#[error("String contains null byte")]
561pub struct NullByteError {
562 position: usize,
563 u16_seq: Vec<u16>,
564}
565impl NullByteError {
566 pub fn nul_position(&self) -> usize {
567 self.position
568 }
569
570 pub fn into_vec(self) -> Vec<u16> {
571 self.u16_seq
572 }
573}
574
575impl CWString {
576 pub fn new(string: &str) -> Result<Self, NullByteError> {
582 let mut wide: Vec<u16> = string.encode_utf16().collect();
583 if let Some((pos, _)) = wide.iter().enumerate().find(|&(_, &c)| c == 0) {
584 return Err(NullByteError {
585 position: pos,
586 u16_seq: wide,
587 });
588 }
589 wide.push(0); Ok(Self(wide))
591 }
592
593 pub fn as_ptr(&self) -> *const u16 {
603 self.0.as_ptr()
604 }
605}
606impl std::fmt::Display for CWString {
607 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
608 let s = String::from_utf16_lossy(&self.0[..self.0.len() - 1]);
609 write!(f, "{}", s)
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 fn test_format_file_filters() {
619 let filters = vec![
620 FileFilter {
621 name: "Image Files".to_string(),
622 extensions: vec!["png".to_string(), "jpg".to_string()],
623 },
624 FileFilter {
625 name: "All Files".to_string(),
626 extensions: vec![],
627 },
628 ];
629 let formatted = format_file_filters(&filters);
630 let expected = "Image Files (.png, .jpg)\x00*.png;*.jpg\x00All Files (*)\x00*\x00";
631 assert_eq!(formatted, expected);
632 }
633
634 #[test]
635 fn test_file_filters_macro() {
636 let filters = file_filters! {
637 "Image Files" => ["png", "jpg"],
638 "All Files" => []
639 };
640 assert_eq!(filters.len(), 2);
641 assert_eq!(filters[0].name, "Image Files");
642 assert_eq!(
643 filters[0].extensions,
644 vec!["png".to_string(), "jpg".to_string()]
645 );
646 assert_eq!(filters[1].name, "All Files");
647 assert_eq!(filters[1].extensions, Vec::<String>::new());
648 }
649
650 #[test]
651 fn test_aviutl2_version() {
652 let version = AviUtl2Version::new(2, 0, 15, 0);
653 assert_eq!(version.0, 2001500);
654 assert_eq!(version.major(), 2);
655 assert_eq!(version.minor(), 0);
656 assert_eq!(version.patch(), 15);
657 assert_eq!(version.build(), 0);
658
659 let version_from_u32: AviUtl2Version = 2001500u32.into();
660 assert_eq!(version_from_u32, version);
661
662 let u32_from_version: u32 = version.into();
663 assert_eq!(u32_from_version, 2001500);
664 }
665
666 #[test]
667 fn test_cwstring_new() {
668 let s = "Hello, world!";
669 let cwstring = CWString::new(s).unwrap();
670 assert_eq!(unsafe { load_wide_string(cwstring.as_ptr()) }, s);
671
672 let s_with_nul = "Hello\0world!";
673 let err = CWString::new(s_with_nul).unwrap_err();
674 assert_eq!(err.nul_position(), 5);
675 }
676}