1use crate::string::SwfStr;
8use bitflags::bitflags;
9use enum_map::Enum;
10use std::borrow::Cow;
11use std::fmt::{self, Display, Formatter};
12use std::num::NonZeroU8;
13use std::str::FromStr;
14
15mod bevel_filter;
16mod blur_filter;
17mod color;
18mod color_matrix_filter;
19mod color_transform;
20mod convolution_filter;
21mod drop_shadow_filter;
22mod fixed;
23mod glow_filter;
24mod gradient_filter;
25mod matrix;
26mod point;
27mod rectangle;
28mod twips;
29
30pub use bevel_filter::{BevelFilter, BevelFilterFlags};
31pub use blur_filter::{BlurFilter, BlurFilterFlags};
32pub use color::Color;
33pub use color_matrix_filter::ColorMatrixFilter;
34pub use color_transform::ColorTransform;
35pub use convolution_filter::{ConvolutionFilter, ConvolutionFilterFlags};
36pub use drop_shadow_filter::{DropShadowFilter, DropShadowFilterFlags};
37pub use fixed::{Fixed16, Fixed8};
38pub use glow_filter::{GlowFilter, GlowFilterFlags};
39pub use gradient_filter::{GradientFilter, GradientFilterFlags};
40pub use matrix::Matrix;
41pub use point::{Point, PointDelta};
42pub use rectangle::Rectangle;
43pub use twips::Twips;
44
45#[derive(Debug)]
48pub struct Swf<'a> {
49 pub header: HeaderExt,
50 pub tags: Vec<Tag<'a>>,
51}
52
53pub struct SwfBuf {
56 pub header: HeaderExt,
58
59 pub data: Vec<u8>,
61}
62
63#[derive(Clone, Debug, Eq, PartialEq)]
69pub struct Header {
70 pub compression: Compression,
71 pub version: u8,
72 pub stage_size: Rectangle<Twips>,
73 pub frame_rate: Fixed8,
74 pub num_frames: u16,
75}
76
77impl Header {
78 pub fn default_with_swf_version(version: u8) -> Self {
79 Self {
80 compression: Compression::None,
81 version,
82 stage_size: Default::default(),
83 frame_rate: Fixed8::ONE,
84 num_frames: 0,
85 }
86 }
87}
88
89#[derive(Clone, Debug)]
98pub struct HeaderExt {
99 pub(crate) header: Header,
100 pub(crate) file_attributes: FileAttributes,
101 pub(crate) background_color: Option<SetBackgroundColor>,
102 pub(crate) uncompressed_len: i32,
103}
104
105impl HeaderExt {
106 #[inline]
107 pub fn default_with_swf_version(version: u8) -> Self {
109 Self {
110 header: Header::default_with_swf_version(version),
111 file_attributes: Default::default(),
112 background_color: None,
113 uncompressed_len: 0,
114 }
115 }
116
117 pub fn default_error_header() -> Self {
120 Self {
121 header: Header::default_with_swf_version(0),
122 file_attributes: Default::default(),
123 background_color: None,
124 uncompressed_len: -1,
125 }
126 }
127
128 pub fn default_with_uncompressed_len(length: i32) -> Self {
130 let header = Header {
131 compression: Compression::None,
132 version: 0,
133 stage_size: Default::default(),
134 frame_rate: Fixed8::ONE,
135 num_frames: 1,
136 };
137 Self {
138 header,
139 file_attributes: Default::default(),
140 background_color: None,
141 uncompressed_len: length,
142 }
143 }
144
145 #[inline]
149 pub fn background_color(&self) -> Option<Color> {
150 self.background_color
151 }
152
153 #[inline]
155 pub fn compression(&self) -> Compression {
156 self.header.compression
157 }
158
159 #[inline]
161 pub fn frame_rate(&self) -> Fixed8 {
162 self.header.frame_rate
163 }
164
165 #[inline]
167 pub fn has_metadata(&self) -> bool {
168 self.file_attributes.contains(FileAttributes::HAS_METADATA)
169 }
170
171 #[inline]
173 pub fn swf_header(&self) -> &Header {
174 &self.header
175 }
176
177 #[inline]
179 pub fn is_action_script_3(&self) -> bool {
180 self.file_attributes
181 .contains(FileAttributes::IS_ACTION_SCRIPT_3)
182 }
183
184 #[inline]
186 pub fn num_frames(&self) -> u16 {
187 self.header.num_frames
188 }
189
190 #[inline]
192 pub fn stage_size(&self) -> &Rectangle<Twips> {
193 &self.header.stage_size
194 }
195
196 #[inline]
198 pub fn version(&self) -> u8 {
199 self.header.version
200 }
201
202 #[inline]
204 pub fn uncompressed_len(&self) -> i32 {
205 self.uncompressed_len
206 }
207
208 #[inline]
210 pub fn use_direct_blit(&self) -> bool {
211 self.file_attributes
212 .contains(FileAttributes::USE_DIRECT_BLIT)
213 }
214
215 #[inline]
217 pub fn use_gpu(&self) -> bool {
218 self.file_attributes.contains(FileAttributes::USE_GPU)
219 }
220
221 #[inline]
226 pub fn use_network_sandbox(&self) -> bool {
227 self.file_attributes
228 .contains(FileAttributes::USE_NETWORK_SANDBOX)
229 }
230}
231
232#[derive(Clone, Copy, Debug, Eq, PartialEq)]
237pub enum Compression {
238 None,
239 Zlib,
240 Lzma,
241}
242
243#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
244pub enum Language {
245 Unknown = 0,
246 Latin = 1,
247 Japanese = 2,
248 Korean = 3,
249 SimplifiedChinese = 4,
250 TraditionalChinese = 5,
251}
252
253impl Language {
254 pub fn from_u8(n: u8) -> Option<Self> {
255 num_traits::FromPrimitive::from_u8(n)
256 }
257}
258
259bitflags! {
260 #[derive(Clone, Copy, Debug, PartialEq)]
264 pub struct FileAttributes: u8 {
265 const USE_DIRECT_BLIT = 1 << 6;
267
268 const USE_GPU = 1 << 5;
270
271 const HAS_METADATA = 1 << 4;
273
274 const IS_ACTION_SCRIPT_3 = 1 << 3;
276
277 const USE_NETWORK_SANDBOX = 1 << 0;
282 }
283}
284
285impl Default for FileAttributes {
286 fn default() -> Self {
287 Self::empty()
289 }
290}
291
292#[derive(Debug, Eq, PartialEq)]
293pub struct FrameLabel<'a> {
294 pub label: &'a SwfStr,
295 pub is_anchor: bool,
296}
297
298#[derive(Debug, Eq, PartialEq)]
299pub struct DefineSceneAndFrameLabelData<'a> {
300 pub scenes: Vec<FrameLabelData<'a>>,
301 pub frame_labels: Vec<FrameLabelData<'a>>,
302}
303
304#[derive(Debug, Eq, PartialEq)]
305pub struct FrameLabelData<'a> {
306 pub frame_num: u32,
307 pub label: &'a SwfStr,
308}
309
310pub type Depth = u16;
311pub type CharacterId = u16;
312
313#[derive(Debug, PartialEq)]
314pub struct PlaceObject<'a> {
315 pub version: u8,
316 pub action: PlaceObjectAction,
317 pub depth: Depth,
318 pub matrix: Option<Matrix>,
319 pub color_transform: Option<ColorTransform>,
320 pub ratio: Option<u16>,
321 pub name: Option<&'a SwfStr>,
322 pub clip_depth: Option<Depth>,
323 pub class_name: Option<&'a SwfStr>,
324 pub filters: Option<Vec<Filter>>,
325 pub background_color: Option<Color>,
326 pub blend_mode: Option<BlendMode>,
327 pub clip_actions: Option<Vec<ClipAction<'a>>>,
328 pub has_image: bool,
329 pub is_bitmap_cached: Option<bool>,
330 pub is_visible: Option<bool>,
331 pub amf_data: Option<&'a [u8]>,
332}
333
334bitflags! {
335 pub struct PlaceFlag: u16 {
336 const MOVE = 1 << 0;
337 const HAS_CHARACTER = 1 << 1;
338 const HAS_MATRIX = 1 << 2;
339 const HAS_COLOR_TRANSFORM = 1 << 3;
340 const HAS_RATIO = 1 << 4;
341 const HAS_NAME = 1 << 5;
342 const HAS_CLIP_DEPTH = 1 << 6;
343 const HAS_CLIP_ACTIONS = 1 << 7;
344
345 const HAS_FILTER_LIST = 1 << 8;
347 const HAS_BLEND_MODE = 1 << 9;
348 const HAS_CACHE_AS_BITMAP = 1 << 10;
349 const HAS_CLASS_NAME = 1 << 11;
350 const HAS_IMAGE = 1 << 12;
351 const HAS_VISIBLE = 1 << 13;
352 const OPAQUE_BACKGROUND = 1 << 14;
353 }
354}
355
356#[derive(Clone, Copy, Debug, Eq, PartialEq)]
357pub enum PlaceObjectAction {
358 Place(CharacterId),
359 Modify,
360 Replace(CharacterId),
361}
362
363#[derive(Clone, Debug, PartialEq)]
364pub enum Filter {
365 DropShadowFilter(Box<DropShadowFilter>),
366 BlurFilter(Box<BlurFilter>),
367 GlowFilter(Box<GlowFilter>),
368 BevelFilter(Box<BevelFilter>),
369 GradientGlowFilter(Box<GradientFilter>),
370 ConvolutionFilter(Box<ConvolutionFilter>),
371 ColorMatrixFilter(Box<ColorMatrixFilter>),
372 GradientBevelFilter(Box<GradientFilter>),
373}
374
375#[derive(Default, Clone, Copy, Debug, Eq, FromPrimitive, PartialEq, Enum)]
376pub enum BlendMode {
377 #[default]
378 Normal = 0,
379 Layer = 2,
380 Multiply = 3,
381 Screen = 4,
382 Lighten = 5,
383 Darken = 6,
384 Difference = 7,
385 Add = 8,
386 Subtract = 9,
387 Invert = 10,
388 Alpha = 11,
389 Erase = 12,
390 Overlay = 13,
391 HardLight = 14,
392}
393
394impl BlendMode {
395 pub fn from_u8(n: u8) -> Option<Self> {
396 num_traits::FromPrimitive::from_u8(match n {
397 1 => 0,
398 n => n,
399 })
400 }
401}
402
403impl Display for BlendMode {
404 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
405 let s = match *self {
406 BlendMode::Normal => "normal",
407 BlendMode::Layer => "layer",
408 BlendMode::Multiply => "multiply",
409 BlendMode::Screen => "screen",
410 BlendMode::Lighten => "lighten",
411 BlendMode::Darken => "darken",
412 BlendMode::Difference => "difference",
413 BlendMode::Add => "add",
414 BlendMode::Subtract => "subtract",
415 BlendMode::Invert => "invert",
416 BlendMode::Alpha => "alpha",
417 BlendMode::Erase => "erase",
418 BlendMode::Overlay => "overlay",
419 BlendMode::HardLight => "hardlight",
420 };
421 f.write_str(s)
422 }
423}
424
425impl FromStr for BlendMode {
426 type Err = ();
427
428 fn from_str(s: &str) -> Result<Self, Self::Err> {
429 let mode = match s {
430 "normal" => BlendMode::Normal,
431 "layer" => BlendMode::Layer,
432 "multiply" => BlendMode::Multiply,
433 "screen" => BlendMode::Screen,
434 "lighten" => BlendMode::Lighten,
435 "darken" => BlendMode::Darken,
436 "difference" => BlendMode::Difference,
437 "add" => BlendMode::Add,
438 "subtract" => BlendMode::Subtract,
439 "invert" => BlendMode::Invert,
440 "alpha" => BlendMode::Alpha,
441 "erase" => BlendMode::Erase,
442 "overlay" => BlendMode::Overlay,
443 "hardlight" => BlendMode::HardLight,
444 _ => return Err(()),
445 };
446 Ok(mode)
447 }
448}
449
450#[derive(Clone, Debug, Eq, PartialEq)]
455pub struct ClipAction<'a> {
456 pub events: ClipEventFlag,
457 pub key_code: Option<KeyCode>,
458 pub action_data: &'a [u8],
459}
460
461bitflags! {
462 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
466 pub struct ClipEventFlag: u32 {
467 const LOAD = 1 << 0;
468 const ENTER_FRAME = 1 << 1;
469 const UNLOAD = 1 << 2;
470 const MOUSE_MOVE = 1 << 3;
471 const MOUSE_DOWN = 1 << 4;
472 const MOUSE_UP = 1 << 5;
473 const KEY_DOWN = 1 << 6;
474 const KEY_UP = 1 << 7;
475
476 const DATA = 1 << 8;
478 const INITIALIZE = 1 << 9;
479 const PRESS = 1 << 10;
480 const RELEASE = 1 << 11;
481 const RELEASE_OUTSIDE = 1 << 12;
482 const ROLL_OVER = 1 << 13;
483 const ROLL_OUT = 1 << 14;
484 const DRAG_OVER = 1 << 15;
485 const DRAG_OUT = 1 << 16;
486 const KEY_PRESS = 1 << 17;
487
488 const CONSTRUCT = 1 << 18;
491 }
492}
493
494pub type KeyCode = u8;
496
497#[derive(Debug, PartialEq)]
505pub enum Tag<'a> {
506 ExportAssets(ExportAssets<'a>),
507 ScriptLimits {
508 max_recursion_depth: u16,
509 timeout_in_seconds: u16,
510 },
511 ShowFrame,
512
513 Protect(Option<&'a SwfStr>),
514 CsmTextSettings(CsmTextSettings),
515 DebugId(DebugId),
516 DefineBinaryData(DefineBinaryData<'a>),
517 DefineBits {
518 id: CharacterId,
519 jpeg_data: &'a [u8],
520 },
521 DefineBitsJpeg2 {
522 id: CharacterId,
523 jpeg_data: &'a [u8],
524 },
525 DefineBitsJpeg3(DefineBitsJpeg3<'a>),
526 DefineBitsLossless(DefineBitsLossless<'a>),
527 DefineButton(Box<Button<'a>>),
528 DefineButton2(Box<Button<'a>>),
529 DefineButtonColorTransform(ButtonColorTransform),
530 DefineButtonSound(Box<ButtonSounds>),
531 DefineEditText(Box<EditText<'a>>),
532 DefineFont(Box<FontV1>),
533 DefineFont2(Box<Font<'a>>),
534 DefineFont4(Font4<'a>),
535 DefineFontAlignZones {
536 id: CharacterId,
537 thickness: FontThickness,
538 zones: Vec<FontAlignZone>,
539 },
540 DefineFontInfo(Box<FontInfo<'a>>),
541 DefineFontName {
542 id: CharacterId,
543 name: &'a SwfStr,
544 copyright_info: &'a SwfStr,
545 },
546 DefineMorphShape(Box<DefineMorphShape>),
547 DefineScalingGrid {
548 id: CharacterId,
549 splitter_rect: Rectangle<Twips>,
550 },
551 DefineShape(Shape),
552 DefineSound(Box<Sound<'a>>),
553 DefineSprite(Sprite<'a>),
554 DefineText(Box<Text>),
555 DefineText2(Box<Text>),
556 DefineVideoStream(DefineVideoStream),
557 DoAbc(&'a [u8]),
558 DoAbc2(DoAbc2<'a>),
559 DoAction(DoAction<'a>),
560 DoInitAction {
561 id: CharacterId,
562 action_data: &'a [u8],
563 },
564 EnableDebugger(&'a SwfStr),
565 EnableTelemetry {
566 password_hash: &'a [u8],
567 },
568 End,
569 Metadata(&'a SwfStr),
570 ImportAssets {
571 url: &'a SwfStr,
572 imports: Vec<ExportedAsset<'a>>,
573 },
574 JpegTables(JpegTables<'a>),
575 NameCharacter(NameCharacter<'a>),
576 SetBackgroundColor(SetBackgroundColor),
577 SetTabIndex {
578 depth: Depth,
579 tab_index: u16,
580 },
581 SoundStreamBlock(SoundStreamBlock<'a>),
582 SoundStreamHead(Box<SoundStreamHead>),
583 SoundStreamHead2(Box<SoundStreamHead>),
584 StartSound(StartSound),
585 StartSound2 {
586 class_name: &'a SwfStr,
587 sound_info: Box<SoundInfo>,
588 },
589 SymbolClass(Vec<SymbolClassLink<'a>>),
590 PlaceObject(Box<PlaceObject<'a>>),
591 RemoveObject(RemoveObject),
592 VideoFrame(VideoFrame<'a>),
593 FileAttributes(FileAttributes),
594
595 FrameLabel(FrameLabel<'a>),
596 DefineSceneAndFrameLabelData(DefineSceneAndFrameLabelData<'a>),
597
598 ProductInfo(ProductInfo),
599
600 Unknown {
601 tag_code: u16,
602 data: &'a [u8],
603 },
604}
605
606pub type ExportAssets<'a> = Vec<ExportedAsset<'a>>;
607
608#[derive(Clone, Debug, Eq, PartialEq)]
609pub struct ExportedAsset<'a> {
610 pub id: CharacterId,
611 pub name: &'a SwfStr,
612}
613
614#[derive(Clone, Debug, Eq, PartialEq)]
615pub struct RemoveObject {
616 pub depth: Depth,
617 pub character_id: Option<CharacterId>,
618}
619
620pub type SetBackgroundColor = Color;
621
622#[derive(Clone, Debug, Eq, PartialEq)]
623pub struct SymbolClassLink<'a> {
624 pub id: CharacterId,
625 pub class_name: &'a SwfStr,
626}
627
628#[derive(Clone, Debug, Eq, PartialEq)]
629pub struct ShapeContext {
630 pub swf_version: u8,
631 pub shape_version: u8,
632 pub num_fill_bits: u8,
633 pub num_line_bits: u8,
634}
635
636#[derive(Clone, Debug, Eq, PartialEq)]
637pub struct Shape {
638 pub version: u8,
639 pub id: CharacterId,
640 pub shape_bounds: Rectangle<Twips>,
641 pub edge_bounds: Rectangle<Twips>,
642 pub flags: ShapeFlag,
643 pub styles: ShapeStyles,
644 pub shape: Vec<ShapeRecord>,
645}
646
647bitflags! {
648 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
649 pub struct ShapeFlag: u8 {
650 const HAS_SCALING_STROKES = 1 << 0;
651 const HAS_NON_SCALING_STROKES = 1 << 1;
652 const NON_ZERO_WINDING_RULE = 1 << 2;
653 }
654}
655
656#[derive(Clone, Debug, Eq, PartialEq)]
657pub struct Sound<'a> {
658 pub id: CharacterId,
659 pub format: SoundFormat,
660 pub num_samples: u32,
661 pub data: &'a [u8],
662}
663
664#[derive(Clone, Debug, PartialEq)]
665pub struct SoundInfo {
666 pub event: SoundEvent,
667 pub in_sample: Option<u32>,
668 pub out_sample: Option<u32>,
669 pub num_loops: u16,
670 pub envelope: Option<SoundEnvelope>,
671}
672
673#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
674pub enum SoundEvent {
675 Event = 0,
676 Start = 1,
677 Stop = 2,
678}
679
680impl SoundEvent {
681 pub fn from_u8(n: u8) -> Option<Self> {
682 num_traits::FromPrimitive::from_u8(match n {
683 3 => 2,
684 n => n,
685 })
686 }
687}
688
689pub type SoundEnvelope = Vec<SoundEnvelopePoint>;
690
691#[derive(Clone, Debug, PartialEq)]
692pub struct SoundEnvelopePoint {
693 pub sample: u32,
694 pub left_volume: f32,
695 pub right_volume: f32,
696}
697
698#[derive(Clone, Debug, PartialEq)]
699pub struct StartSound {
700 pub id: CharacterId,
701 pub sound_info: Box<SoundInfo>,
702}
703
704#[derive(Debug, PartialEq)]
705pub struct Sprite<'a> {
706 pub id: CharacterId,
707 pub num_frames: u16,
708 pub tags: Vec<Tag<'a>>,
709}
710
711#[derive(Clone, Debug, Eq, PartialEq)]
712pub struct ShapeStyles {
713 pub fill_styles: Vec<FillStyle>,
714 pub line_styles: Vec<LineStyle>,
715}
716
717#[derive(Clone, Debug, Eq, PartialEq)]
718pub enum ShapeRecord {
719 StyleChange(Box<StyleChangeData>),
720 StraightEdge {
721 delta: PointDelta<Twips>,
722 },
723 CurvedEdge {
724 control_delta: PointDelta<Twips>,
725 anchor_delta: PointDelta<Twips>,
726 },
727}
728
729bitflags! {
730 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
731 pub struct ShapeRecordFlag: u8 {
732 const MOVE_TO = 1 << 0;
733 const FILL_STYLE_0 = 1 << 1;
734 const FILL_STYLE_1 = 1 << 2;
735 const LINE_STYLE = 1 << 3;
736 const NEW_STYLES = 1 << 4;
737 }
738}
739
740#[derive(Clone, Debug, Eq, PartialEq)]
741pub struct StyleChangeData {
742 pub move_to: Option<Point<Twips>>,
743 pub fill_style_0: Option<u32>,
744 pub fill_style_1: Option<u32>,
745 pub line_style: Option<u32>,
746 pub new_styles: Option<ShapeStyles>,
747}
748
749#[derive(Clone, Debug, Eq, PartialEq)]
750pub enum FillStyle {
751 Color(Color),
752 LinearGradient(Gradient),
753 RadialGradient(Gradient),
754 FocalGradient {
755 gradient: Gradient,
756 focal_point: Fixed8,
757 },
758 Bitmap {
759 id: CharacterId,
760 matrix: Matrix,
761 is_smoothed: bool,
762 is_repeating: bool,
763 },
764}
765
766#[derive(Clone, Debug, Eq, PartialEq, Hash)]
767pub struct Gradient {
768 pub matrix: Matrix,
769 pub spread: GradientSpread,
770 pub interpolation: GradientInterpolation,
771 pub records: Vec<GradientRecord>,
772}
773
774#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq, Enum, Hash)]
775pub enum GradientSpread {
776 Pad = 0,
777 Reflect = 1,
778 Repeat = 2,
779}
780
781impl GradientSpread {
782 pub fn from_u8(n: u8) -> Option<Self> {
783 num_traits::FromPrimitive::from_u8(match n {
784 3 => 0,
787 n => n,
788 })
789 }
790}
791
792#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq, Hash)]
793pub enum GradientInterpolation {
794 Rgb = 0,
795 LinearRgb = 1,
796}
797
798impl GradientInterpolation {
799 pub fn from_u8(n: u8) -> Option<Self> {
800 num_traits::FromPrimitive::from_u8(match n {
801 2 | 3 => 0,
804 n => n,
805 })
806 }
807}
808
809#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
810pub struct GradientRecord {
811 pub ratio: u8,
812 pub color: Color,
813}
814
815#[derive(Clone, Debug, Eq, PartialEq)]
816pub struct LineStyle {
817 pub(crate) width: Twips,
818 pub(crate) fill_style: FillStyle,
819 pub(crate) flags: LineStyleFlag,
820 pub(crate) miter_limit: Fixed8,
821}
822
823impl LineStyle {
824 #[inline]
825 pub fn new() -> LineStyle {
826 Default::default()
827 }
828
829 #[inline]
830 pub fn allow_close(&self) -> bool {
831 !self.flags.contains(LineStyleFlag::NO_CLOSE)
832 }
833
834 #[inline]
835 pub fn with_allow_close(mut self, val: bool) -> Self {
836 self.flags.set(LineStyleFlag::NO_CLOSE, !val);
837 self
838 }
839
840 #[inline]
841 pub fn allow_scale_x(&self) -> bool {
842 !self.flags.contains(LineStyleFlag::NO_H_SCALE)
843 }
844
845 #[inline]
846 pub fn with_allow_scale_x(mut self, val: bool) -> Self {
847 self.flags.set(LineStyleFlag::NO_H_SCALE, !val);
848 self
849 }
850
851 #[inline]
852 pub fn allow_scale_y(&self) -> bool {
853 !self.flags.contains(LineStyleFlag::NO_V_SCALE)
854 }
855
856 #[inline]
857 pub fn with_allow_scale_y(mut self, val: bool) -> Self {
858 self.flags.set(LineStyleFlag::NO_V_SCALE, !val);
859 self
860 }
861
862 #[inline]
863 pub fn is_pixel_hinted(&self) -> bool {
864 self.flags.contains(LineStyleFlag::PIXEL_HINTING)
865 }
866
867 #[inline]
868 pub fn with_is_pixel_hinted(mut self, val: bool) -> Self {
869 self.flags.set(LineStyleFlag::PIXEL_HINTING, val);
870 self
871 }
872
873 #[inline]
874 pub fn start_cap(&self) -> LineCapStyle {
875 let cap = (self.flags & LineStyleFlag::START_CAP_STYLE).bits() >> 6;
876 LineCapStyle::from_u8(cap as u8).unwrap()
877 }
878
879 #[inline]
880 pub fn with_start_cap(mut self, val: LineCapStyle) -> Self {
881 self.flags -= LineStyleFlag::START_CAP_STYLE;
882 self.flags |= LineStyleFlag::from_bits_retain((val as u16) << 6);
883 self
884 }
885
886 #[inline]
887 pub fn end_cap(&self) -> LineCapStyle {
888 let cap = (self.flags & LineStyleFlag::END_CAP_STYLE).bits() >> 8;
889 LineCapStyle::from_u8(cap as u8).unwrap()
890 }
891
892 #[inline]
893 pub fn with_end_cap(mut self, val: LineCapStyle) -> Self {
894 self.flags -= LineStyleFlag::END_CAP_STYLE;
895 self.flags |= LineStyleFlag::from_bits_retain((val as u16) << 8);
896 self
897 }
898
899 #[inline]
900 pub fn join_style(&self) -> LineJoinStyle {
901 match self.flags & LineStyleFlag::JOIN_STYLE {
902 LineStyleFlag::ROUND => LineJoinStyle::Round,
903 LineStyleFlag::BEVEL => LineJoinStyle::Bevel,
904 LineStyleFlag::MITER => LineJoinStyle::Miter(self.miter_limit),
905 _ => unreachable!(),
906 }
907 }
908
909 #[inline]
910 pub fn with_join_style(mut self, val: LineJoinStyle) -> Self {
911 self.flags -= LineStyleFlag::JOIN_STYLE;
912 self.flags |= match val {
913 LineJoinStyle::Round => LineStyleFlag::ROUND,
914 LineJoinStyle::Bevel => LineStyleFlag::BEVEL,
915 LineJoinStyle::Miter(miter_limit) => {
916 self.miter_limit = miter_limit;
917 LineStyleFlag::MITER
918 }
919 };
920 self
921 }
922
923 #[inline]
924 pub fn fill_style(&self) -> &FillStyle {
925 &self.fill_style
926 }
927
928 #[inline]
929 pub fn with_fill_style(mut self, val: FillStyle) -> Self {
930 self.flags
931 .set(LineStyleFlag::HAS_FILL, !matches!(val, FillStyle::Color(_)));
932 self.fill_style = val;
933 self
934 }
935
936 #[inline]
937 pub fn with_color(mut self, val: Color) -> Self {
938 self.flags.remove(LineStyleFlag::HAS_FILL);
939 self.fill_style = FillStyle::Color(val);
940 self
941 }
942
943 #[inline]
944 pub fn width(&self) -> Twips {
945 self.width
946 }
947
948 #[inline]
949 pub fn with_width(mut self, val: Twips) -> Self {
950 self.width = val;
951 self
952 }
953}
954
955impl Default for LineStyle {
956 #[inline]
957 fn default() -> Self {
958 Self {
960 width: Twips::ZERO,
961 fill_style: FillStyle::Color(Color::BLACK),
962 flags: Default::default(),
963 miter_limit: Default::default(),
964 }
965 }
966}
967
968bitflags! {
969 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
970 pub struct LineStyleFlag: u16 {
971 const PIXEL_HINTING = 1 << 0;
973 const NO_V_SCALE = 1 << 1;
974 const NO_H_SCALE = 1 << 2;
975 const HAS_FILL = 1 << 3;
976 const JOIN_STYLE = 0b11 << 4;
977 const START_CAP_STYLE = 0b11 << 6;
978
979 const END_CAP_STYLE = 0b11 << 8;
981 const NO_CLOSE = 1 << 10;
982
983 const ROUND = 0b00 << 4;
985 const BEVEL = 0b01 << 4;
986 const MITER = 0b10 << 4;
987 }
988}
989
990impl Default for LineStyleFlag {
991 #[inline]
992 fn default() -> Self {
993 LineStyleFlag::empty()
994 }
995}
996
997#[derive(Default, Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
998pub enum LineCapStyle {
999 #[default]
1000 Round = 0,
1001 None = 1,
1002 Square = 2,
1003}
1004
1005impl LineCapStyle {
1006 #[inline]
1007 pub fn from_u8(n: u8) -> Option<Self> {
1008 num_traits::FromPrimitive::from_u8(n)
1009 }
1010}
1011
1012#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
1013pub enum LineJoinStyle {
1014 #[default]
1015 Round,
1016 Bevel,
1017 Miter(Fixed8),
1018}
1019
1020#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
1021pub enum AudioCompression {
1022 UncompressedUnknownEndian = 0,
1023 Adpcm = 1,
1024 Mp3 = 2,
1025 Uncompressed = 3,
1026 Nellymoser16Khz = 4,
1027 Nellymoser8Khz = 5,
1028 Nellymoser = 6,
1029 Aac = 10,
1030 Speex = 11,
1031}
1032
1033impl AudioCompression {
1034 pub fn from_u8(n: u8) -> Option<Self> {
1035 num_traits::FromPrimitive::from_u8(n)
1036 }
1037}
1038
1039#[derive(Clone, Debug, Eq, PartialEq)]
1040pub struct SoundFormat {
1041 pub compression: AudioCompression,
1042 pub sample_rate: u16,
1043 pub is_stereo: bool,
1044 pub is_16_bit: bool,
1045}
1046
1047#[derive(Clone, Debug, Eq, PartialEq)]
1048pub struct SoundStreamHead {
1049 pub stream_format: SoundFormat,
1050 pub playback_format: SoundFormat,
1051 pub num_samples_per_block: u16,
1052 pub latency_seek: i16,
1053}
1054
1055pub type SoundStreamBlock<'a> = &'a [u8];
1056
1057#[derive(Clone, Debug, PartialEq)]
1058pub struct Button<'a> {
1059 pub id: CharacterId,
1060 pub is_track_as_menu: bool,
1061 pub records: Vec<ButtonRecord>,
1062 pub actions: Vec<ButtonAction<'a>>,
1063}
1064
1065#[derive(Clone, Debug, PartialEq)]
1066pub struct ButtonRecord {
1067 pub states: ButtonState,
1068 pub id: CharacterId,
1069 pub depth: Depth,
1070 pub matrix: Matrix,
1071 pub color_transform: ColorTransform,
1072 pub filters: Vec<Filter>,
1073 pub blend_mode: BlendMode,
1074}
1075
1076bitflags! {
1077 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1078 pub struct ButtonState: u8 {
1079 const UP = 1 << 0;
1080 const OVER = 1 << 1;
1081 const DOWN = 1 << 2;
1082 const HIT_TEST = 1 << 3;
1083 }
1084}
1085
1086#[derive(Clone, Debug, Eq, PartialEq)]
1087pub struct ButtonColorTransform {
1088 pub id: CharacterId,
1089 pub color_transforms: Vec<ColorTransform>,
1090}
1091
1092#[derive(Clone, Debug, PartialEq)]
1093pub struct ButtonSounds {
1094 pub id: CharacterId,
1095 pub over_to_up_sound: Option<ButtonSound>,
1096 pub up_to_over_sound: Option<ButtonSound>,
1097 pub over_to_down_sound: Option<ButtonSound>,
1098 pub down_to_over_sound: Option<ButtonSound>,
1099}
1100
1101pub type ButtonSound = (CharacterId, SoundInfo);
1102
1103#[derive(Clone, Debug, Eq, PartialEq)]
1104pub struct ButtonAction<'a> {
1105 pub conditions: ButtonActionCondition,
1106 pub action_data: &'a [u8],
1107}
1108
1109impl ButtonAction<'_> {
1110 pub fn key_press(&self) -> Option<NonZeroU8> {
1111 NonZeroU8::new(((self.conditions & ButtonActionCondition::KEY_PRESS).bits() >> 9) as u8)
1112 }
1113}
1114
1115bitflags! {
1116 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1117 pub struct ButtonActionCondition: u16 {
1118 const IDLE_TO_OVER_UP = 1 << 0;
1119 const OVER_UP_TO_IDLE = 1 << 1;
1120 const OVER_UP_TO_OVER_DOWN = 1 << 2;
1121 const OVER_DOWN_TO_OVER_UP = 1 << 3;
1122 const OVER_DOWN_TO_OUT_DOWN = 1 << 4;
1123 const OUT_DOWN_TO_OVER_DOWN = 1 << 5;
1124 const OUT_DOWN_TO_IDLE = 1 << 6;
1125 const IDLE_TO_OVER_DOWN = 1 << 7;
1126 const OVER_DOWN_TO_IDLE = 1 << 8;
1127 const KEY_PRESS = 0b1111111 << 9;
1128 }
1129}
1130
1131impl ButtonActionCondition {
1132 #[inline]
1133 pub fn from_key_code(key_code: u8) -> Self {
1134 Self::from_bits_retain((key_code as u16) << 9)
1135 }
1136
1137 #[inline]
1139 pub fn matches(self, test_condition: ButtonActionCondition) -> bool {
1140 let self_key_press = (self & Self::KEY_PRESS).bits();
1141 let test_key_press = (test_condition & Self::KEY_PRESS).bits();
1142 let self_without_key = self & !Self::KEY_PRESS;
1143 let test_without_key = test_condition & !Self::KEY_PRESS;
1144
1145 self_without_key.contains(test_without_key)
1146 && (test_key_press == 0 || test_key_press == self_key_press)
1147 }
1148}
1149
1150#[derive(Clone, Debug, Eq, PartialEq)]
1151pub struct DefineMorphShape {
1152 pub version: u8,
1153 pub id: CharacterId,
1154 pub flags: DefineMorphShapeFlag,
1155 pub start: MorphShape,
1156 pub end: MorphShape,
1157}
1158
1159bitflags! {
1160 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1161 pub struct DefineMorphShapeFlag: u8 {
1162 const HAS_SCALING_STROKES = 1 << 0;
1163 const HAS_NON_SCALING_STROKES = 1 << 1;
1164 }
1165}
1166
1167#[derive(Clone, Debug, Eq, PartialEq)]
1168pub struct MorphShape {
1169 pub shape_bounds: Rectangle<Twips>,
1170 pub edge_bounds: Rectangle<Twips>,
1171 pub fill_styles: Vec<FillStyle>,
1172 pub line_styles: Vec<LineStyle>,
1173 pub shape: Vec<ShapeRecord>,
1174}
1175
1176#[derive(Clone, Debug, Eq, PartialEq)]
1177pub struct FontV1 {
1178 pub id: CharacterId,
1179 pub glyphs: Vec<Vec<ShapeRecord>>,
1180}
1181
1182#[derive(Clone, Debug, Eq, PartialEq)]
1183pub struct Font<'a> {
1184 pub version: u8,
1185 pub id: CharacterId,
1186 pub name: &'a SwfStr,
1187 pub language: Language,
1188 pub layout: Option<FontLayout>,
1189 pub glyphs: Vec<Glyph>,
1190 pub flags: FontFlag,
1191}
1192
1193bitflags! {
1194 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1195 pub struct FontFlag: u8 {
1196 const IS_BOLD = 1 << 0;
1197 const IS_ITALIC = 1 << 1;
1198 const HAS_WIDE_CODES = 1 << 2;
1199 const HAS_WIDE_OFFSETS = 1 << 3;
1200 const IS_ANSI = 1 << 4;
1201 const IS_SMALL_TEXT = 1 << 5;
1202 const IS_SHIFT_JIS = 1 << 6;
1203 const HAS_LAYOUT = 1 << 7;
1204 }
1205}
1206
1207#[derive(Clone, Debug, Eq, PartialEq)]
1208pub struct Font4<'a> {
1209 pub id: CharacterId,
1210 pub is_italic: bool,
1211 pub is_bold: bool,
1212 pub name: &'a SwfStr,
1213 pub data: Option<&'a [u8]>,
1214}
1215
1216#[derive(Clone, Debug, Eq, PartialEq)]
1217pub struct Glyph {
1218 pub shape_records: Vec<ShapeRecord>,
1219 pub code: u16,
1220 pub advance: i16,
1221 pub bounds: Option<Rectangle<Twips>>,
1222}
1223
1224#[derive(Clone, Debug, Eq, PartialEq)]
1225pub struct FontLayout {
1226 pub ascent: u16,
1227 pub descent: u16,
1228 pub leading: i16,
1229 pub kerning: Vec<KerningRecord>,
1230}
1231
1232#[derive(Clone, Debug, Eq, PartialEq)]
1233pub struct KerningRecord {
1234 pub left_code: u16,
1235 pub right_code: u16,
1236 pub adjustment: Twips,
1237}
1238
1239#[derive(Clone, Debug, Eq, PartialEq)]
1240pub struct FontInfo<'a> {
1241 pub id: CharacterId,
1242 pub version: u8,
1243 pub name: &'a SwfStr,
1244 pub flags: FontInfoFlag,
1245 pub language: Language,
1246 pub code_table: Vec<u16>,
1247}
1248
1249bitflags! {
1250 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1251 pub struct FontInfoFlag: u8 {
1252 const HAS_WIDE_CODES = 1 << 0;
1253 const IS_BOLD = 1 << 1;
1254 const IS_ITALIC = 1 << 2;
1255 const IS_SHIFT_JIS = 1 << 3;
1256 const IS_ANSI = 1 << 4;
1257 const IS_SMALL_TEXT = 1 << 5;
1258 }
1259}
1260
1261#[derive(Clone, Debug, Eq, PartialEq)]
1262pub struct DefineBinaryData<'a> {
1263 pub id: CharacterId,
1264 pub data: &'a [u8],
1265}
1266
1267#[derive(Clone, Debug, Eq, PartialEq)]
1268pub struct Text {
1269 pub id: CharacterId,
1270 pub bounds: Rectangle<Twips>,
1271 pub matrix: Matrix,
1272 pub records: Vec<TextRecord>,
1273}
1274
1275#[derive(Clone, Debug, Eq, PartialEq)]
1276pub struct TextRecord {
1277 pub font_id: Option<CharacterId>,
1278 pub color: Option<Color>,
1279 pub x_offset: Option<Twips>,
1280 pub y_offset: Option<Twips>,
1281 pub height: Option<Twips>,
1282 pub glyphs: Vec<GlyphEntry>,
1283}
1284
1285#[derive(Clone, Debug, Eq, PartialEq)]
1286pub struct GlyphEntry {
1287 pub index: u32,
1288 pub advance: i32,
1289}
1290
1291#[derive(Clone, Debug, Eq, PartialEq)]
1292pub struct EditText<'a> {
1293 pub(crate) id: CharacterId,
1294 pub(crate) bounds: Rectangle<Twips>,
1295 pub(crate) font_id: CharacterId,
1296 pub(crate) font_class: &'a SwfStr,
1297 pub(crate) height: Twips,
1298 pub(crate) color: Color,
1299 pub(crate) max_length: u16,
1300 pub(crate) layout: TextLayout,
1301 pub(crate) variable_name: &'a SwfStr,
1302 pub(crate) initial_text: &'a SwfStr,
1303 pub(crate) flags: EditTextFlag,
1304}
1305
1306impl<'a> EditText<'a> {
1307 #[inline]
1308 pub fn new() -> Self {
1309 Default::default()
1310 }
1311
1312 #[inline]
1313 pub fn bounds(&self) -> &Rectangle<Twips> {
1314 &self.bounds
1315 }
1316
1317 #[inline]
1318 pub fn with_bounds(mut self, bounds: Rectangle<Twips>) -> Self {
1319 self.bounds = bounds;
1320 self
1321 }
1322
1323 #[inline]
1324 pub fn id(&self) -> CharacterId {
1325 self.id
1326 }
1327
1328 #[inline]
1329 pub fn with_id(mut self, id: CharacterId) -> Self {
1330 self.id = id;
1331 self
1332 }
1333
1334 #[inline]
1335 pub fn font_id(&self) -> Option<CharacterId> {
1336 self.flags
1337 .contains(EditTextFlag::HAS_FONT)
1338 .then_some(self.font_id)
1339 }
1340
1341 #[inline]
1342 pub fn font_class(&self) -> Option<&'a SwfStr> {
1343 self.flags
1344 .contains(EditTextFlag::HAS_FONT_CLASS)
1345 .then_some(self.font_class)
1346 }
1347
1348 #[inline]
1350 pub fn height(&self) -> Option<Twips> {
1351 self.flags
1352 .intersects(EditTextFlag::HAS_FONT | EditTextFlag::HAS_FONT_CLASS)
1353 .then_some(self.height)
1354 }
1355
1356 #[inline]
1357 pub fn with_default_font(mut self) -> Self {
1358 self.flags -= EditTextFlag::HAS_FONT | EditTextFlag::HAS_FONT_CLASS;
1359 self
1360 }
1361
1362 #[inline]
1363 pub fn with_font_id(mut self, font_id: CharacterId, height: Twips) -> Self {
1364 self.flags |= EditTextFlag::HAS_FONT;
1365 self.flags -= EditTextFlag::HAS_FONT_CLASS;
1366 self.font_id = font_id;
1367 self.height = height;
1368 self
1369 }
1370
1371 #[inline]
1372 pub fn with_font_class(mut self, font_class: &'a SwfStr, height: Twips) -> Self {
1373 self.flags |= EditTextFlag::HAS_FONT_CLASS;
1374 self.flags -= EditTextFlag::HAS_FONT;
1375 self.font_class = font_class;
1376 self.height = height;
1377 self
1378 }
1379
1380 #[inline]
1381 pub fn color(&self) -> Option<&Color> {
1382 self.flags
1383 .contains(EditTextFlag::HAS_TEXT_COLOR)
1384 .then_some(&self.color)
1385 }
1386
1387 #[inline]
1388 pub fn with_color(mut self, color: Option<Color>) -> Self {
1389 if let Some(color) = color {
1390 self.flags |= EditTextFlag::HAS_TEXT_COLOR;
1391 self.color = color;
1392 } else {
1393 self.flags -= EditTextFlag::HAS_TEXT_COLOR;
1394 }
1395 self
1396 }
1397
1398 #[inline]
1399 pub fn max_length(&self) -> Option<u16> {
1400 self.flags
1401 .contains(EditTextFlag::HAS_MAX_LENGTH)
1402 .then_some(self.max_length)
1403 }
1404
1405 #[inline]
1406 pub fn with_max_length(mut self, max_length: Option<u16>) -> Self {
1407 if let Some(max_length) = max_length {
1408 self.flags |= EditTextFlag::HAS_MAX_LENGTH;
1409 self.max_length = max_length;
1410 } else {
1411 self.flags -= EditTextFlag::HAS_MAX_LENGTH;
1412 }
1413 self
1414 }
1415
1416 #[inline]
1417 pub fn layout(&self) -> Option<&TextLayout> {
1418 self.flags
1419 .contains(EditTextFlag::HAS_LAYOUT)
1420 .then_some(&self.layout)
1421 }
1422
1423 #[inline]
1424 pub fn with_layout(mut self, layout: Option<TextLayout>) -> Self {
1425 if let Some(layout) = layout {
1426 self.flags |= EditTextFlag::HAS_LAYOUT;
1427 self.layout = layout;
1428 } else {
1429 self.flags -= EditTextFlag::HAS_LAYOUT;
1430 }
1431 self
1432 }
1433
1434 #[inline]
1435 pub fn variable_name(&self) -> &'a SwfStr {
1436 self.variable_name
1437 }
1438
1439 #[inline]
1440 pub fn with_variable_name(mut self, variable_name: &'a SwfStr) -> Self {
1441 self.variable_name = variable_name;
1442 self
1443 }
1444
1445 #[inline]
1446 pub fn initial_text(&self) -> Option<&'a SwfStr> {
1447 self.flags
1448 .contains(EditTextFlag::HAS_TEXT)
1449 .then_some(self.initial_text)
1450 }
1451
1452 #[inline]
1453 pub fn with_initial_text(mut self, initial_text: Option<&'a SwfStr>) -> Self {
1454 if let Some(initial_text) = initial_text {
1455 self.flags |= EditTextFlag::HAS_TEXT;
1456 self.initial_text = initial_text;
1457 } else {
1458 self.flags -= EditTextFlag::HAS_TEXT;
1459 }
1460 self
1461 }
1462
1463 #[inline]
1464 pub fn flags(&self) -> EditTextFlag {
1465 self.flags
1466 }
1467
1468 #[inline]
1469 pub fn is_auto_size(&self) -> bool {
1470 self.flags.contains(EditTextFlag::AUTO_SIZE)
1471 }
1472
1473 #[inline]
1474 pub fn with_is_auto_size(mut self, value: bool) -> Self {
1475 self.flags.set(EditTextFlag::AUTO_SIZE, value);
1476 self
1477 }
1478
1479 #[inline]
1480 pub fn use_outlines(&self) -> bool {
1481 self.flags.contains(EditTextFlag::USE_OUTLINES)
1482 }
1483
1484 #[inline]
1485 pub fn with_use_outlines(mut self, value: bool) -> Self {
1486 self.flags.set(EditTextFlag::USE_OUTLINES, value);
1487 self
1488 }
1489
1490 #[inline]
1491 pub fn has_border(&self) -> bool {
1492 self.flags.contains(EditTextFlag::BORDER)
1493 }
1494
1495 #[inline]
1496 pub fn with_has_border(mut self, value: bool) -> Self {
1497 self.flags.set(EditTextFlag::BORDER, value);
1498 self
1499 }
1500
1501 #[inline]
1502 pub fn is_html(&self) -> bool {
1503 self.flags.contains(EditTextFlag::HTML)
1504 }
1505
1506 #[inline]
1507 pub fn with_is_html(mut self, value: bool) -> Self {
1508 self.flags.set(EditTextFlag::HTML, value);
1509 self
1510 }
1511
1512 #[inline]
1513 pub fn is_multiline(&self) -> bool {
1514 self.flags.contains(EditTextFlag::MULTILINE)
1515 }
1516
1517 #[inline]
1518 pub fn with_is_multiline(mut self, value: bool) -> Self {
1519 self.flags.set(EditTextFlag::MULTILINE, value);
1520 self
1521 }
1522
1523 #[inline]
1524 pub fn is_password(&self) -> bool {
1525 self.flags.contains(EditTextFlag::PASSWORD)
1526 }
1527
1528 #[inline]
1529 pub fn with_is_password(mut self, value: bool) -> Self {
1530 self.flags.set(EditTextFlag::PASSWORD, value);
1531 self
1532 }
1533
1534 #[inline]
1535 pub fn is_read_only(&self) -> bool {
1536 self.flags.contains(EditTextFlag::READ_ONLY)
1537 }
1538
1539 #[inline]
1540 pub fn with_is_read_only(mut self, value: bool) -> Self {
1541 self.flags.set(EditTextFlag::READ_ONLY, value);
1542 self
1543 }
1544
1545 #[inline]
1546 pub fn is_selectable(&self) -> bool {
1547 !self.flags.contains(EditTextFlag::NO_SELECT)
1548 }
1549
1550 #[inline]
1551 pub fn with_is_selectable(mut self, value: bool) -> Self {
1552 self.flags.set(EditTextFlag::NO_SELECT, !value);
1553 self
1554 }
1555
1556 #[inline]
1557 pub fn was_static(&self) -> bool {
1558 self.flags.contains(EditTextFlag::WAS_STATIC)
1559 }
1560
1561 #[inline]
1562 pub fn with_was_static(mut self, value: bool) -> Self {
1563 self.flags.set(EditTextFlag::WAS_STATIC, value);
1564 self
1565 }
1566
1567 #[inline]
1568 pub fn is_word_wrap(&self) -> bool {
1569 self.flags.contains(EditTextFlag::WORD_WRAP)
1570 }
1571
1572 #[inline]
1573 pub fn with_is_word_wrap(mut self, value: bool) -> Self {
1574 self.flags.set(EditTextFlag::WORD_WRAP, value);
1575 self
1576 }
1577}
1578
1579impl Default for EditText<'_> {
1580 fn default() -> Self {
1581 Self {
1582 id: Default::default(),
1583 bounds: Default::default(),
1584 font_id: Default::default(),
1585 font_class: Default::default(),
1586 height: Twips::ZERO,
1587 color: Color::BLACK,
1588 max_length: 0,
1589 layout: TextLayout {
1590 align: TextAlign::Left,
1591 left_margin: Twips::ZERO,
1592 right_margin: Twips::ZERO,
1593 indent: Twips::ZERO,
1594 leading: Twips::ZERO,
1595 },
1596 variable_name: Default::default(),
1597 initial_text: Default::default(),
1598 flags: EditTextFlag::empty(),
1599 }
1600 }
1601}
1602
1603bitflags! {
1604 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1605 pub struct EditTextFlag: u16 {
1606 const HAS_FONT = 1 << 0;
1607 const HAS_MAX_LENGTH = 1 << 1;
1608 const HAS_TEXT_COLOR = 1 << 2;
1609 const READ_ONLY = 1 << 3;
1610 const PASSWORD = 1 << 4;
1611 const MULTILINE = 1 << 5;
1612 const WORD_WRAP = 1 << 6;
1613 const HAS_TEXT = 1 << 7;
1614
1615 const USE_OUTLINES = 1 << 8;
1616 const HTML = 1 << 9;
1617 const WAS_STATIC = 1 << 10;
1618 const BORDER = 1 << 11;
1619 const NO_SELECT = 1 << 12;
1620 const HAS_LAYOUT = 1 << 13;
1621 const AUTO_SIZE = 1 << 14;
1622 const HAS_FONT_CLASS = 1 << 15;
1623 }
1624}
1625
1626#[derive(Clone, Debug, Default, Eq, PartialEq)]
1627pub struct TextLayout {
1628 pub align: TextAlign,
1629 pub left_margin: Twips,
1630 pub right_margin: Twips,
1631 pub indent: Twips,
1632 pub leading: Twips,
1633}
1634
1635#[derive(Clone, Copy, Debug, Default, Eq, FromPrimitive, PartialEq)]
1636pub enum TextAlign {
1637 #[default]
1638 Left = 0,
1639 Right = 1,
1640 Center = 2,
1641 Justify = 3,
1642}
1643
1644impl TextAlign {
1645 pub fn from_u8(n: u8) -> Option<Self> {
1646 num_traits::FromPrimitive::from_u8(n)
1647 }
1648}
1649
1650#[derive(Clone, Debug, Eq, PartialEq)]
1651pub struct FontAlignZone {
1652 pub left: i16,
1654 pub width: i16,
1655 pub bottom: i16,
1656 pub height: i16,
1657}
1658
1659#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
1660pub enum FontThickness {
1661 Thin = 0,
1662 Medium = 1,
1663 Thick = 2,
1664}
1665
1666impl FontThickness {
1667 pub fn from_u8(n: u8) -> Option<Self> {
1668 num_traits::FromPrimitive::from_u8(n)
1669 }
1670}
1671
1672#[derive(Clone, Debug, PartialEq)]
1673pub struct CsmTextSettings {
1674 pub id: CharacterId,
1675 pub use_advanced_rendering: bool,
1676 pub grid_fit: TextGridFit,
1677 pub thickness: f32, pub sharpness: f32,
1679}
1680
1681#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
1682pub enum TextGridFit {
1683 None = 0,
1684 Pixel = 1,
1685 SubPixel = 2,
1686}
1687
1688impl TextGridFit {
1689 pub fn from_u8(n: u8) -> Option<Self> {
1690 num_traits::FromPrimitive::from_u8(n)
1691 }
1692}
1693
1694#[derive(Clone, Debug, Eq, PartialEq)]
1695pub struct DefineBitsLossless<'a> {
1696 pub version: u8,
1697 pub id: CharacterId,
1698 pub format: BitmapFormat,
1699 pub width: u16,
1700 pub height: u16,
1701 pub data: Cow<'a, [u8]>,
1702}
1703
1704#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1705pub enum BitmapFormat {
1706 ColorMap8 { num_colors: u8 },
1707 Rgb15,
1708 Rgb32,
1709}
1710
1711#[derive(Clone, Debug, Eq, PartialEq)]
1712pub struct DefineVideoStream {
1713 pub id: CharacterId,
1714 pub num_frames: u16,
1715 pub width: u16,
1716 pub height: u16,
1717 pub is_smoothed: bool,
1718 pub deblocking: VideoDeblocking,
1719 pub codec: VideoCodec,
1720}
1721
1722#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
1723pub enum VideoDeblocking {
1724 UseVideoPacketValue = 0,
1725 None = 1,
1726 Level1 = 2,
1727 Level2 = 3,
1728 Level3 = 4,
1729 Level4 = 5,
1730}
1731
1732impl VideoDeblocking {
1733 pub fn from_u8(n: u8) -> Option<Self> {
1734 num_traits::FromPrimitive::from_u8(n)
1735 }
1736}
1737
1738#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
1739pub enum VideoCodec {
1740 None = 0,
1741 H263 = 2,
1742 ScreenVideo = 3,
1743 Vp6 = 4,
1744 Vp6WithAlpha = 5,
1745 ScreenVideoV2 = 6,
1746 H264 = 7,
1747}
1748
1749impl VideoCodec {
1750 pub fn from_u8(n: u8) -> Option<Self> {
1751 num_traits::FromPrimitive::from_u8(n)
1752 }
1753}
1754
1755#[derive(Clone, Debug, Eq, PartialEq)]
1756pub struct VideoFrame<'a> {
1757 pub stream_id: CharacterId,
1758 pub frame_num: u16,
1759 pub data: &'a [u8],
1760}
1761
1762#[derive(Clone, Debug, Eq, PartialEq)]
1763pub struct DefineBitsJpeg3<'a> {
1764 pub id: CharacterId,
1765 pub version: u8,
1766 pub deblocking: Fixed8,
1767 pub data: &'a [u8],
1768 pub alpha_data: &'a [u8],
1769}
1770
1771#[derive(Clone, Debug, Eq, PartialEq)]
1772pub struct DoAbc2<'a> {
1773 pub flags: DoAbc2Flag,
1774 pub name: &'a SwfStr,
1775 pub data: &'a [u8],
1776}
1777
1778bitflags! {
1779 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1780 pub struct DoAbc2Flag: u32 {
1781 const LAZY_INITIALIZE = 1 << 0;
1782 }
1783}
1784
1785pub type DoAction<'a> = &'a [u8];
1786
1787pub type JpegTables<'a> = &'a [u8];
1788
1789#[derive(Clone, Debug, Eq, PartialEq)]
1794pub struct ProductInfo {
1795 pub product_id: u32,
1796 pub edition: u32,
1797 pub major_version: u8,
1798 pub minor_version: u8,
1799 pub build_number: u64,
1800 pub compilation_date: u64,
1801}
1802
1803pub type DebugId = [u8; 16];
1805
1806#[derive(Clone, Debug, Eq, PartialEq)]
1810pub struct NameCharacter<'a> {
1811 pub id: CharacterId,
1812 pub name: &'a SwfStr,
1813}
1814
1815#[cfg(test)]
1816mod tests {
1817 use super::ButtonActionCondition;
1818
1819 #[test]
1820 fn button_conditions_match() {
1821 assert!(ButtonActionCondition::OVER_DOWN_TO_OVER_UP
1822 .matches(ButtonActionCondition::OVER_DOWN_TO_OVER_UP));
1823
1824 assert!(!ButtonActionCondition::OVER_DOWN_TO_OVER_UP
1825 .matches(ButtonActionCondition::IDLE_TO_OVER_UP));
1826
1827 assert!((ButtonActionCondition::OVER_DOWN_TO_OVER_UP
1828 | ButtonActionCondition::IDLE_TO_OVER_UP)
1829 .matches(ButtonActionCondition::IDLE_TO_OVER_UP));
1830
1831 assert!((ButtonActionCondition::OVER_DOWN_TO_OVER_UP
1832 | ButtonActionCondition::from_key_code(3))
1833 .matches(ButtonActionCondition::OVER_DOWN_TO_OVER_UP));
1834
1835 assert!((ButtonActionCondition::OVER_DOWN_TO_OVER_UP
1836 | ButtonActionCondition::from_key_code(3))
1837 .matches(ButtonActionCondition::from_key_code(3)));
1838
1839 assert!(!(ButtonActionCondition::OVER_DOWN_TO_OVER_UP
1840 | ButtonActionCondition::from_key_code(3))
1841 .matches(ButtonActionCondition::from_key_code(1)));
1842 }
1843}