swf: SwfStr no longer contains encoding

SwfStr is now an unsized slice analogous to `str` or `bstr`.
The desired encoding must be supplied when converting to String.
This commit is contained in:
Mike Welsh 2021-01-20 12:46:22 -08:00
parent 700c3c1767
commit 66256dd3be
18 changed files with 254 additions and 182 deletions

View File

@ -559,7 +559,9 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Action::Return => self.action_return(), Action::Return => self.action_return(),
Action::SetMember => self.action_set_member(), Action::SetMember => self.action_set_member(),
Action::SetProperty => self.action_set_property(), Action::SetProperty => self.action_set_property(),
Action::SetTarget(target) => self.action_set_target(&target.to_str_lossy()), Action::SetTarget(target) => {
self.action_set_target(&target.to_str_lossy(self.encoding()))
}
Action::SetTarget2 => self.action_set_target2(), Action::SetTarget2 => self.action_set_target2(),
Action::SetVariable => self.action_set_variable(), Action::SetVariable => self.action_set_variable(),
Action::StackSwap => self.action_stack_swap(), Action::StackSwap => self.action_stack_swap(),
@ -889,13 +891,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
fn action_constant_pool( fn action_constant_pool(
&mut self, &mut self,
constant_pool: &[SwfStr<'_>], constant_pool: &[&'_ SwfStr],
) -> Result<FrameControl<'gc>, Error<'gc>> { ) -> Result<FrameControl<'gc>, Error<'gc>> {
self.context.avm1.constant_pool = GcCell::allocate( self.context.avm1.constant_pool = GcCell::allocate(
self.context.gc_context, self.context.gc_context,
constant_pool constant_pool
.iter() .iter()
.map(|s| (*s).to_string_lossy()) .map(|s| (*s).to_string_lossy(self.encoding()))
.collect(), .collect(),
); );
self.set_constant_pool(self.context.avm1.constant_pool); self.set_constant_pool(self.context.avm1.constant_pool);
@ -911,11 +913,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
fn action_define_function( fn action_define_function(
&mut self, &mut self,
name: SwfStr<'_>, name: &'_ SwfStr,
params: &[SwfStr<'_>], params: &[&'_ SwfStr],
actions: SwfSlice, actions: SwfSlice,
) -> Result<FrameControl<'gc>, Error<'gc>> { ) -> Result<FrameControl<'gc>, Error<'gc>> {
let name = name.to_str_lossy(); let name = name.to_str_lossy(self.encoding());
let name = name.as_ref(); let name = name.as_ref();
let swf_version = self.swf_version(); let swf_version = self.swf_version();
let scope = Scope::new_closure_scope(self.scope_cell(), self.context.gc_context); let scope = Scope::new_closure_scope(self.scope_cell(), self.context.gc_context);
@ -982,7 +984,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
if action_func.name.is_empty() { if action_func.name.is_empty() {
self.context.avm1.push(func_obj); self.context.avm1.push(func_obj);
} else { } else {
self.define(&action_func.name.to_str_lossy(), func_obj); self.define(&action_func.name.to_str_lossy(self.encoding()), func_obj);
} }
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
@ -1220,12 +1222,12 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
fn action_get_url( fn action_get_url(
&mut self, &mut self,
url: SwfStr<'_>, url: &'_ SwfStr,
target: SwfStr<'_>, target: &'_ SwfStr,
) -> Result<FrameControl<'gc>, Error<'gc>> { ) -> Result<FrameControl<'gc>, Error<'gc>> {
let target = target.to_str_lossy(); let target = target.to_str_lossy(self.encoding());
let target = target.as_ref(); let target = target.as_ref();
let url = url.to_string_lossy(); let url = url.to_string_lossy(self.encoding());
if target.starts_with("_level") && target.len() > 6 { if target.starts_with("_level") && target.len() > 6 {
match target[6..].parse::<u32>() { match target[6..].parse::<u32>() {
Ok(level_id) => { Ok(level_id) => {
@ -1425,13 +1427,15 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Ok(FrameControl::Continue) Ok(FrameControl::Continue)
} }
fn action_goto_label(&mut self, label: SwfStr<'_>) -> Result<FrameControl<'gc>, Error<'gc>> { fn action_goto_label(&mut self, label: &'_ SwfStr) -> Result<FrameControl<'gc>, Error<'gc>> {
if let Some(clip) = self.target_clip() { if let Some(clip) = self.target_clip() {
if let Some(clip) = clip.as_movie_clip() { if let Some(clip) = clip.as_movie_clip() {
if let Some(frame) = clip.frame_label_to_number(&label.to_str_lossy()) { if let Some(frame) =
clip.frame_label_to_number(&label.to_str_lossy(self.encoding()))
{
clip.goto_frame(&mut self.context, frame, true); clip.goto_frame(&mut self.context, frame, true);
} else { } else {
avm_warn!(self, "GoToLabel: Frame label '{}' not found", label); avm_warn!(self, "GoToLabel: Frame label '{:?}' not found", label);
} }
} else { } else {
avm_warn!(self, "GoToLabel: Target is not a MovieClip"); avm_warn!(self, "GoToLabel: Target is not a MovieClip");
@ -1776,7 +1780,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
SwfValue::Float(v) => f64::from(*v).into(), SwfValue::Float(v) => f64::from(*v).into(),
SwfValue::Double(v) => (*v).into(), SwfValue::Double(v) => (*v).into(),
SwfValue::Str(v) => { SwfValue::Str(v) => {
AvmString::new(self.context.gc_context, v.to_string_lossy()).into() AvmString::new(self.context.gc_context, v.to_string_lossy(self.encoding()))
.into()
} }
SwfValue::Register(v) => self.current_register(*v), SwfValue::Register(v) => self.current_register(*v),
SwfValue::ConstantPool(i) => { SwfValue::ConstantPool(i) => {
@ -2279,9 +2284,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
); );
match catch_vars { match catch_vars {
CatchVar::Var(name) => { CatchVar::Var(name) => activation.set_variable(
activation.set_variable(&name.to_str_lossy(), value.to_owned())? &name.to_str_lossy(activation.encoding()),
} value.to_owned(),
)?,
CatchVar::Register(id) => { CatchVar::Register(id) => {
activation.set_current_register(*id, value.to_owned()) activation.set_current_register(*id, value.to_owned())
} }
@ -2874,6 +2880,14 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
self.scope_cell().read().is_defined(self, name) self.scope_cell().read().is_defined(self, name)
} }
/// Returns the suggested string encoding for actions.
/// For SWF version 6 and higher, this is always UTF-8.
/// For SWF version 5 and lower, this is locale-dependent,
/// and we default to WINDOWS-1252.
pub fn encoding(&self) -> &'static swf::Encoding {
swf::SwfStr::encoding_for_version(self.swf_version)
}
/// Returns the SWF version of the action or function being executed. /// Returns the SWF version of the action or function being executed.
pub fn swf_version(&self) -> u8 { pub fn swf_version(&self) -> u8 {
self.swf_version self.swf_version

View File

@ -94,7 +94,7 @@ impl<'gc> Avm1Function<'gc> {
swf_version: u8, swf_version: u8,
actions: SwfSlice, actions: SwfSlice,
name: &str, name: &str,
params: &[SwfStr<'_>], params: &[&'_ SwfStr],
scope: GcCell<'gc, Scope<'gc>>, scope: GcCell<'gc, Scope<'gc>>,
constant_pool: GcCell<'gc, Vec<String>>, constant_pool: GcCell<'gc, Vec<String>>,
base_clip: DisplayObject<'gc>, base_clip: DisplayObject<'gc>,
@ -121,7 +121,12 @@ impl<'gc> Avm1Function<'gc> {
preload_global: false, preload_global: false,
params: params params: params
.iter() .iter()
.map(|&s| (None, s.to_string_lossy())) .map(|&s| {
(
None,
s.to_string_lossy(SwfStr::encoding_for_version(swf_version)),
)
})
.collect(), .collect(),
scope, scope,
constant_pool, constant_pool,
@ -141,7 +146,11 @@ impl<'gc> Avm1Function<'gc> {
let name = if swf_function.name.is_empty() { let name = if swf_function.name.is_empty() {
None None
} else { } else {
Some(swf_function.name.to_string_lossy()) Some(
swf_function
.name
.to_string_lossy(SwfStr::encoding_for_version(swf_version)),
)
}; };
let mut owned_params = Vec::new(); let mut owned_params = Vec::new();
@ -150,7 +159,10 @@ impl<'gc> Avm1Function<'gc> {
register_index: r, register_index: r,
} in &swf_function.params } in &swf_function.params
{ {
owned_params.push((*r, (*s).to_string_lossy())) owned_params.push((
*r,
(*s).to_string_lossy(SwfStr::encoding_for_version(swf_version)),
))
} }
Avm1Function { Avm1Function {

View File

@ -841,7 +841,9 @@ pub trait TDisplayObject<'gc>:
self.set_color_transform(gc_context, &color_transform.clone().into()); self.set_color_transform(gc_context, &color_transform.clone().into());
} }
if let Some(name) = &place_object.name { if let Some(name) = &place_object.name {
self.set_name(gc_context, &name.to_str_lossy()); let encoding = swf::SwfStr::encoding_for_version(self.swf_version());
let name = name.to_str_lossy(encoding);
self.set_name(gc_context, &name);
} }
if let Some(clip_depth) = place_object.clip_depth { if let Some(clip_depth) = place_object.clip_depth {
self.set_clip_depth(gc_context, clip_depth.into()); self.set_clip_depth(gc_context, clip_depth.into());

View File

@ -152,11 +152,12 @@ impl<'gc> EditText<'gc> {
let document = XMLDocument::new(context.gc_context); let document = XMLDocument::new(context.gc_context);
let text = swf_tag.initial_text.clone().unwrap_or_default(); let text = swf_tag.initial_text.clone().unwrap_or_default();
let default_format = TextFormat::from_swf_tag(swf_tag.clone(), swf_movie.clone(), context); let default_format = TextFormat::from_swf_tag(swf_tag.clone(), swf_movie.clone(), context);
let encoding = swf_movie.encoding();
let mut text_spans = FormatSpans::new(); let mut text_spans = FormatSpans::new();
text_spans.set_default_format(default_format.clone()); text_spans.set_default_format(default_format.clone());
let text = text.to_str_lossy(); let text = text.to_str_lossy(encoding);
if is_html { if is_html {
let _ = document let _ = document
.as_node() .as_node()
@ -193,7 +194,7 @@ impl<'gc> EditText<'gc> {
base.matrix_mut(context.gc_context).ty = bounds.y_min; base.matrix_mut(context.gc_context).ty = bounds.y_min;
let variable = if !swf_tag.variable_name.is_empty() { let variable = if !swf_tag.variable_name.is_empty() {
Some(swf_tag.variable_name.clone()) Some(swf_tag.variable_name)
} else { } else {
None None
}; };
@ -212,13 +213,15 @@ impl<'gc> EditText<'gc> {
id: swf_tag.id, id: swf_tag.id,
bounds: swf_tag.bounds, bounds: swf_tag.bounds,
font_id: swf_tag.font_id, font_id: swf_tag.font_id,
font_class_name: swf_tag.font_class_name.map(|s| s.to_string_lossy()), font_class_name: swf_tag
.font_class_name
.map(|s| s.to_string_lossy(encoding)),
height: swf_tag.height, height: swf_tag.height,
color: swf_tag.color.clone(), color: swf_tag.color.clone(),
max_length: swf_tag.max_length, max_length: swf_tag.max_length,
layout: swf_tag.layout.clone(), layout: swf_tag.layout.clone(),
variable_name: swf_tag.variable_name.to_string_lossy(), variable_name: swf_tag.variable_name.to_string_lossy(encoding),
initial_text: swf_tag.initial_text.map(|s| s.to_string_lossy()), initial_text: swf_tag.initial_text.map(|s| s.to_string_lossy(encoding)),
is_word_wrap: swf_tag.is_word_wrap, is_word_wrap: swf_tag.is_word_wrap,
is_multiline: swf_tag.is_multiline, is_multiline: swf_tag.is_multiline,
is_password: swf_tag.is_password, is_password: swf_tag.is_password,
@ -247,7 +250,7 @@ impl<'gc> EditText<'gc> {
intrinsic_bounds, intrinsic_bounds,
bounds, bounds,
autosize: AutoSizeMode::None, autosize: AutoSizeMode::None,
variable: variable.map(|s| s.to_string_lossy()), variable: variable.map(|s| s.to_string_lossy(encoding)),
bound_stage_object: None, bound_stage_object: None,
firing_variable_binding: false, firing_variable_binding: false,
selection: None, selection: None,
@ -620,7 +623,7 @@ impl<'gc> EditText<'gc> {
.initial_text .initial_text
.clone() .clone()
.unwrap_or_default(); .unwrap_or_default();
let _ = self.set_text(text.to_string(), &mut activation.context); let _ = self.set_text(text, &mut activation.context);
self.0.write(activation.context.gc_context).variable = variable; self.0.write(activation.context.gc_context).variable = variable;
self.try_bind_text_field_variable(activation, true); self.try_bind_text_field_variable(activation, true);

View File

@ -459,7 +459,7 @@ impl<'gc> MovieClip<'gc> {
// giving us a `SwfSlice` for later parsing, so we have to replcate the // giving us a `SwfSlice` for later parsing, so we have to replcate the
// *entire* parsing code here. This sucks. // *entire* parsing code here. This sucks.
let flags = reader.read_u32()?; let flags = reader.read_u32()?;
let name = reader.read_string()?.to_string_lossy(); let name = reader.read_string()?.to_string_lossy(reader.encoding());
let is_lazy_initialize = flags & 1 != 0; let is_lazy_initialize = flags & 1 != 0;
let domain = library.avm2_domain(); let domain = library.avm2_domain();
@ -499,7 +499,7 @@ impl<'gc> MovieClip<'gc> {
for _ in 0..num_symbols { for _ in 0..num_symbols {
let id = reader.read_u16()?; let id = reader.read_u16()?;
let class_name = reader.read_string()?.to_string_lossy(); let class_name = reader.read_string()?.to_string_lossy(reader.encoding());
if let Some(name) = if let Some(name) =
Avm2QName::from_symbol_class(&class_name, activation.context.gc_context) Avm2QName::from_symbol_class(&class_name, activation.context.gc_context)
@ -565,10 +565,11 @@ impl<'gc> MovieClip<'gc> {
.map(|fld| fld.frame_num + 1) .map(|fld| fld.frame_num + 1)
.unwrap_or_else(|| static_data.total_frames as u32 + 1); .unwrap_or_else(|| static_data.total_frames as u32 + 1);
let label = label.to_string_lossy(reader.encoding());
static_data.scene_labels.insert( static_data.scene_labels.insert(
label.to_string(), label.clone(),
Scene { Scene {
name: label.to_string(), name: label,
start, start,
length: end as u16 - start as u16, length: end as u16 - start as u16,
}, },
@ -576,9 +577,10 @@ impl<'gc> MovieClip<'gc> {
} }
for FrameLabelData { frame_num, label } in sfl_data.frame_labels { for FrameLabelData { frame_num, label } in sfl_data.frame_labels {
static_data static_data.frame_labels.insert(
.frame_labels label.to_string_lossy(reader.encoding()),
.insert(label.to_string(), frame_num as u16 + 1); frame_num as u16 + 1,
);
} }
Ok(()) Ok(())
@ -2468,7 +2470,13 @@ impl<'gc, 'a> MovieClipData<'gc> {
is_bold: false, is_bold: false,
is_italic: false, is_italic: false,
}; };
let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); let font_object = Font::from_swf_tag(
context.gc_context,
context.renderer,
&font,
reader.encoding(),
)
.unwrap();
context context
.library .library
.library_for_movie_mut(self.movie()) .library_for_movie_mut(self.movie())
@ -2483,7 +2491,13 @@ impl<'gc, 'a> MovieClipData<'gc> {
reader: &mut SwfStream<'a>, reader: &mut SwfStream<'a>,
) -> DecodeResult { ) -> DecodeResult {
let font = reader.read_define_font_2(2)?; let font = reader.read_define_font_2(2)?;
let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); let font_object = Font::from_swf_tag(
context.gc_context,
context.renderer,
&font,
reader.encoding(),
)
.unwrap();
context context
.library .library
.library_for_movie_mut(self.movie()) .library_for_movie_mut(self.movie())
@ -2498,7 +2512,13 @@ impl<'gc, 'a> MovieClipData<'gc> {
reader: &mut SwfStream<'a>, reader: &mut SwfStream<'a>,
) -> DecodeResult { ) -> DecodeResult {
let font = reader.read_define_font_2(3)?; let font = reader.read_define_font_2(3)?;
let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); let font_object = Font::from_swf_tag(
context.gc_context,
context.renderer,
&font,
reader.encoding(),
)
.unwrap();
context context
.library .library
.library_for_movie_mut(self.movie()) .library_for_movie_mut(self.movie())
@ -2606,15 +2626,16 @@ impl<'gc, 'a> MovieClipData<'gc> {
) -> DecodeResult { ) -> DecodeResult {
let exports = reader.read_export_assets()?; let exports = reader.read_export_assets()?;
for export in exports { for export in exports {
let name = export.name.to_str_lossy(reader.encoding());
let character = context let character = context
.library .library
.library_for_movie_mut(self.movie()) .library_for_movie_mut(self.movie())
.register_export(export.id, &export.name.to_str_lossy()); .register_export(export.id, &name);
// TODO: do other types of Character need to know their exported name? // TODO: do other types of Character need to know their exported name?
if let Some(Character::MovieClip(movie_clip)) = character { if let Some(Character::MovieClip(movie_clip)) = character {
*movie_clip.0.read().static_data.exported_name.borrow_mut() = *movie_clip.0.read().static_data.exported_name.borrow_mut() =
Some(export.name.to_string()); Some(name.to_string());
} }
} }
Ok(()) Ok(())
@ -2631,7 +2652,10 @@ impl<'gc, 'a> MovieClipData<'gc> {
) -> DecodeResult { ) -> DecodeResult {
let frame_label = reader.read_frame_label(tag_len)?; let frame_label = reader.read_frame_label(tag_len)?;
// Frame labels are case insensitive (ASCII). // Frame labels are case insensitive (ASCII).
let label = frame_label.label.to_str_lossy().to_ascii_lowercase(); let label = frame_label
.label
.to_str_lossy(reader.encoding())
.to_ascii_lowercase();
if let std::collections::hash_map::Entry::Vacant(v) = static_data.frame_labels.entry(label) if let std::collections::hash_map::Entry::Vacant(v) = static_data.frame_labels.entry(label)
{ {
v.insert(cur_frame); v.insert(cur_frame);

View File

@ -101,6 +101,7 @@ impl<'gc> Font<'gc> {
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
renderer: &mut dyn RenderBackend, renderer: &mut dyn RenderBackend,
tag: &swf::Font, tag: &swf::Font,
encoding: &'static swf::Encoding,
) -> Result<Font<'gc>, Error> { ) -> Result<Font<'gc>, Error> {
let mut glyphs = vec![]; let mut glyphs = vec![];
let mut code_point_to_glyph = fnv::FnvHashMap::default(); let mut code_point_to_glyph = fnv::FnvHashMap::default();
@ -124,7 +125,7 @@ impl<'gc> Font<'gc> {
fnv::FnvHashMap::default() fnv::FnvHashMap::default()
}; };
let descriptor = FontDescriptor::from_swf_tag(tag); let descriptor = FontDescriptor::from_swf_tag(tag, encoding);
let (ascent, descent, leading) = if let Some(layout) = &tag.layout { let (ascent, descent, leading) = if let Some(layout) = &tag.layout {
(layout.ascent, layout.descent, layout.leading) (layout.ascent, layout.descent, layout.leading)
} else { } else {
@ -392,12 +393,8 @@ pub struct FontDescriptor {
impl FontDescriptor { impl FontDescriptor {
/// Obtain a font descriptor from a SWF font tag. /// Obtain a font descriptor from a SWF font tag.
pub fn from_swf_tag(val: &swf::Font) -> Self { pub fn from_swf_tag(val: &swf::Font, encoding: &'static swf::Encoding) -> Self {
let mut name = val.name.to_string(); let name = val.name.to_string_lossy(encoding);
if let Some(first_null) = name.find('\0') {
name.truncate(first_null);
};
Self { Self {
name, name,

View File

@ -193,12 +193,12 @@ impl TextFormat {
swf_movie: Arc<SwfMovie>, swf_movie: Arc<SwfMovie>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Self { ) -> Self {
let encoding = swf_movie.encoding();
let movie_library = context.library.library_for_movie_mut(swf_movie); let movie_library = context.library.library_for_movie_mut(swf_movie);
let font = et.font_id.and_then(|fid| movie_library.get_font(fid)); let font = et.font_id.and_then(|fid| movie_library.get_font(fid));
let font_class = et let font_class = et
.font_class_name .font_class_name
.map(|s| s.to_string_lossy()) .map(|s| s.to_string_lossy(encoding))
.or_else(|| font.map(|font| font.descriptor().class().to_string())) .or_else(|| font.map(|font| font.descriptor().class().to_string()))
.unwrap_or_else(|| "Times New Roman".to_string()); .unwrap_or_else(|| "Times New Roman".to_string());
let align = et.layout.clone().map(|l| l.align); let align = et.layout.clone().map(|l| l.align);
@ -211,7 +211,7 @@ impl TextFormat {
// Times New Roman non-bold, non-italic. This will need to be revised // Times New Roman non-bold, non-italic. This will need to be revised
// when we start supporting device fonts. // when we start supporting device fonts.
Self { Self {
font: Some(font_class.to_string()), font: Some(font_class),
size: et.height.map(|h| h.to_pixels()), size: et.height.map(|h| h.to_pixels()),
color: et.color, color: et.color,
align, align,

View File

@ -1281,8 +1281,12 @@ impl Player {
renderer: &mut dyn RenderBackend, renderer: &mut dyn RenderBackend,
) -> Result<crate::font::Font<'gc>, Error> { ) -> Result<crate::font::Font<'gc>, Error> {
let mut reader = swf::read::Reader::new(data, 8); let mut reader = swf::read::Reader::new(data, 8);
let device_font = let device_font = crate::font::Font::from_swf_tag(
crate::font::Font::from_swf_tag(gc_context, renderer, &reader.read_define_font_2(3)?)?; gc_context,
renderer,
&reader.read_define_font_2(3)?,
reader.encoding(),
)?;
Ok(device_font) Ok(device_font)
} }

View File

@ -25,6 +25,9 @@ pub struct SwfMovie {
/// Any parameters provided when loading this movie (also known as 'flashvars') /// Any parameters provided when loading this movie (also known as 'flashvars')
parameters: PropertyMap<String>, parameters: PropertyMap<String>,
/// The suggest encoding for this SWF.
encoding: &'static swf::Encoding,
} }
impl SwfMovie { impl SwfMovie {
@ -42,6 +45,7 @@ impl SwfMovie {
data: vec![], data: vec![],
url: None, url: None,
parameters: PropertyMap::new(), parameters: PropertyMap::new(),
encoding: swf::UTF_8,
} }
} }
@ -56,6 +60,7 @@ impl SwfMovie {
data, data,
url: source.url.clone(), url: source.url.clone(),
parameters: source.parameters.clone(), parameters: source.parameters.clone(),
encoding: source.encoding,
} }
} }
@ -74,11 +79,13 @@ impl SwfMovie {
/// Construct a movie based on the contents of the SWF datastream. /// Construct a movie based on the contents of the SWF datastream.
pub fn from_data(swf_data: &[u8], url: Option<String>) -> Result<Self, Error> { pub fn from_data(swf_data: &[u8], url: Option<String>) -> Result<Self, Error> {
let swf_buf = swf::read::decompress_swf(&swf_data[..])?; let swf_buf = swf::read::decompress_swf(&swf_data[..])?;
let encoding = swf::SwfStr::encoding_for_version(swf_buf.header.version);
Ok(Self { Ok(Self {
header: swf_buf.header, header: swf_buf.header,
data: swf_buf.data, data: swf_buf.data,
url, url,
parameters: PropertyMap::new(), parameters: PropertyMap::new(),
encoding,
}) })
} }
@ -95,6 +102,14 @@ impl SwfMovie {
&self.data &self.data
} }
/// Returns the suggested string encoding for the given SWF version.
/// For SWF version 6 and higher, this is always UTF-8.
/// For SWF version 5 and lower, this is locale-dependent,
/// and we default to WINDOWS-1252.
pub fn encoding(&self) -> &'static swf::Encoding {
self.encoding
}
pub fn width(&self) -> u32 { pub fn width(&self) -> u32 {
(self.header.stage_size.x_max - self.header.stage_size.x_min).to_pixels() as u32 (self.header.stage_size.x_max - self.header.stage_size.x_min).to_pixels() as u32
} }

View File

@ -3,7 +3,7 @@
use crate::avm1::{opcode::OpCode, types::*}; use crate::avm1::{opcode::OpCode, types::*};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::read::SwfReadExt; use crate::read::SwfReadExt;
use crate::string::SwfStr; use crate::string::{Encoding, SwfStr, UTF_8, WINDOWS_1252};
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read}; use std::io::{self, Read};
@ -11,27 +11,35 @@ use std::io::{self, Read};
pub struct Reader<'a> { pub struct Reader<'a> {
input: &'a [u8], input: &'a [u8],
version: u8, version: u8,
encoding: &'static encoding_rs::Encoding, encoding: &'static Encoding,
} }
impl<'a> Reader<'a> { impl<'a> Reader<'a> {
#[inline]
pub fn new(input: &'a [u8], version: u8) -> Self { pub fn new(input: &'a [u8], version: u8) -> Self {
Self { Self {
input, input,
version, version,
encoding: if version > 5 { encoding: if version > 5 {
encoding_rs::UTF_8 UTF_8
} else { } else {
// TODO: Allow configurable encoding // TODO: Allow configurable encoding
encoding_rs::WINDOWS_1252 WINDOWS_1252
}, },
} }
} }
#[inline]
pub fn encoding(&self) -> &'static Encoding {
SwfStr::encoding_for_version(self.version)
}
#[inline]
pub fn get_ref(&self) -> &'a [u8] { pub fn get_ref(&self) -> &'a [u8] {
self.input self.input
} }
#[inline]
pub fn get_mut(&mut self) -> &mut &'a [u8] { pub fn get_mut(&mut self) -> &mut &'a [u8] {
&mut self.input &mut self.input
} }
@ -49,7 +57,7 @@ impl<'a> Reader<'a> {
} }
} }
pub fn read_string(&mut self) -> io::Result<SwfStr<'a>> { pub fn read_string(&mut self) -> io::Result<&'a SwfStr> {
let mut pos = 0; let mut pos = 0;
loop { loop {
let byte = *self.input.get(pos).ok_or_else(|| { let byte = *self.input.get(pos).ok_or_else(|| {
@ -63,7 +71,7 @@ impl<'a> Reader<'a> {
let s = unsafe { let s = unsafe {
let slice = self.input.get_unchecked(..pos); let slice = self.input.get_unchecked(..pos);
SwfStr::from_bytes_unchecked(slice, self.encoding) SwfStr::from_bytes(slice)
}; };
self.input = &self.input[pos + 1..]; self.input = &self.input[pos + 1..];
Ok(s) Ok(s)
@ -483,7 +491,6 @@ pub mod tests {
#[test] #[test]
fn read_define_function() { fn read_define_function() {
use encoding_rs::WINDOWS_1252;
// Ensure we read a function properly along with the function data. // Ensure we read a function properly along with the function data.
let action_bytes = vec![ let action_bytes = vec![
0x9b, 0x08, 0x00, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x96, 0x06, 0x00, 0x9b, 0x08, 0x00, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x96, 0x06, 0x00,

View File

@ -18,11 +18,11 @@ pub enum Action<'a> {
CastOp, CastOp,
CharToAscii, CharToAscii,
CloneSprite, CloneSprite,
ConstantPool(Vec<SwfStr<'a>>), ConstantPool(Vec<&'a SwfStr>),
Decrement, Decrement,
DefineFunction { DefineFunction {
name: SwfStr<'a>, name: &'a SwfStr,
params: Vec<SwfStr<'a>>, params: Vec<&'a SwfStr>,
actions: &'a [u8], actions: &'a [u8],
}, },
DefineFunction2(Function<'a>), DefineFunction2(Function<'a>),
@ -41,8 +41,8 @@ pub enum Action<'a> {
GetProperty, GetProperty,
GetTime, GetTime,
GetUrl { GetUrl {
url: SwfStr<'a>, url: &'a SwfStr,
target: SwfStr<'a>, target: &'a SwfStr,
}, },
GetUrl2 { GetUrl2 {
send_vars_method: SendVarsMethod, send_vars_method: SendVarsMethod,
@ -55,7 +55,7 @@ pub enum Action<'a> {
set_playing: bool, set_playing: bool,
scene_offset: u16, scene_offset: u16,
}, },
GotoLabel(SwfStr<'a>), GotoLabel(&'a SwfStr),
Greater, Greater,
If { If {
offset: i16, offset: i16,
@ -91,7 +91,7 @@ pub enum Action<'a> {
Return, Return,
SetMember, SetMember,
SetProperty, SetProperty,
SetTarget(SwfStr<'a>), SetTarget(&'a SwfStr),
SetTarget2, SetTarget2,
SetVariable, SetVariable,
StackSwap, StackSwap,
@ -140,7 +140,7 @@ pub enum Value<'a> {
Int(i32), Int(i32),
Float(f32), Float(f32),
Double(f64), Double(f64),
Str(SwfStr<'a>), Str(&'a SwfStr),
Register(u8), Register(u8),
ConstantPool(u16), ConstantPool(u16),
} }
@ -154,7 +154,7 @@ pub enum SendVarsMethod {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Function<'a> { pub struct Function<'a> {
pub name: SwfStr<'a>, pub name: &'a SwfStr,
pub register_count: u8, pub register_count: u8,
pub params: Vec<FunctionParam<'a>>, pub params: Vec<FunctionParam<'a>>,
pub preload_parent: bool, pub preload_parent: bool,
@ -171,7 +171,7 @@ pub struct Function<'a> {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct FunctionParam<'a> { pub struct FunctionParam<'a> {
pub name: SwfStr<'a>, pub name: &'a SwfStr,
pub register_index: Option<u8>, pub register_index: Option<u8>,
} }
@ -184,6 +184,6 @@ pub struct TryBlock<'a> {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum CatchVar<'a> { pub enum CatchVar<'a> {
Var(SwfStr<'a>), Var(&'a SwfStr),
Register(u8), Register(u8),
} }

View File

@ -60,7 +60,7 @@ impl<W: Write> SwfWriteExt for Writer<W> {
} }
#[inline] #[inline]
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> { fn write_string(&mut self, s: &'_ SwfStr) -> io::Result<()> {
self.output.write_all(s.as_bytes())?; self.output.write_all(s.as_bytes())?;
self.write_u8(0) self.write_u8(0)
} }

View File

@ -56,7 +56,7 @@ impl<W: Write> SwfWriteExt for Writer<W> {
} }
#[inline] #[inline]
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> { fn write_string(&mut self, s: &'_ SwfStr) -> io::Result<()> {
self.output.write_all(s.as_bytes())?; self.output.write_all(s.as_bytes())?;
self.write_u8(0) self.write_u8(0)
} }

View File

@ -8,7 +8,7 @@
use crate::{ use crate::{
error::{Error, Result}, error::{Error, Result},
string::SwfStr, string::{Encoding, SwfStr},
types::*, types::*,
}; };
use bitstream_io::BitRead; use bitstream_io::BitRead;
@ -248,28 +248,30 @@ impl<'a, 'b> BitReader<'a, 'b> {
pub struct Reader<'a> { pub struct Reader<'a> {
input: &'a [u8], input: &'a [u8],
version: u8, version: u8,
encoding: &'static encoding_rs::Encoding,
} }
impl<'a> Reader<'a> { impl<'a> Reader<'a> {
#[inline]
pub fn new(input: &'a [u8], version: u8) -> Reader<'a> { pub fn new(input: &'a [u8], version: u8) -> Reader<'a> {
Reader { Reader { input, version }
input,
version,
encoding: if version > 5 {
encoding_rs::UTF_8
} else {
// TODO: Allow configurable encoding
encoding_rs::WINDOWS_1252
},
}
} }
/// Returns the suggested string encoding for this SWF.
/// For SWF version 6 and higher, this is always UTF-8.
/// For SWF version 5 and lower, this is locale-dependent,
/// and we default to WINDOWS-1252.
#[inline]
pub fn encoding(&self) -> &'static Encoding {
SwfStr::encoding_for_version(self.version)
}
#[inline]
pub fn version(&self) -> u8 { pub fn version(&self) -> u8 {
self.version self.version
} }
/// Returns a reference to the underlying `Reader`. /// Returns a reference to the underlying `Reader`.
#[inline]
pub fn get_ref(&self) -> &'a [u8] { pub fn get_ref(&self) -> &'a [u8] {
self.input self.input
} }
@ -277,6 +279,7 @@ impl<'a> Reader<'a> {
/// Returns a mutable reference to the underlying `Reader`. /// Returns a mutable reference to the underlying `Reader`.
/// ///
/// Reading from this reference is not recommended. /// Reading from this reference is not recommended.
#[inline]
pub fn get_mut(&mut self) -> &mut &'a [u8] { pub fn get_mut(&mut self) -> &mut &'a [u8] {
&mut self.input &mut self.input
} }
@ -317,7 +320,7 @@ impl<'a> Reader<'a> {
slice slice
} }
pub fn read_string(&mut self) -> io::Result<SwfStr<'a>> { pub fn read_string(&mut self) -> io::Result<&'a SwfStr> {
let mut pos = 0; let mut pos = 0;
loop { loop {
let byte = *self.input.get(pos).ok_or_else(|| { let byte = *self.input.get(pos).ok_or_else(|| {
@ -329,16 +332,14 @@ impl<'a> Reader<'a> {
pos += 1; pos += 1;
} }
let s = unsafe { let slice = unsafe { self.input.get_unchecked(..pos) };
let slice = self.input.get_unchecked(..pos); let s = SwfStr::from_bytes(slice);
SwfStr::from_bytes_unchecked(slice, self.encoding)
};
self.input = &self.input[pos + 1..]; self.input = &self.input[pos + 1..];
Ok(s) Ok(s)
} }
fn read_string_with_len(&mut self, len: usize) -> io::Result<SwfStr<'a>> { fn read_string_with_len(&mut self, len: usize) -> io::Result<&'a SwfStr> {
Ok(SwfStr::from_bytes(&self.read_slice(len)?, self.encoding)) Ok(SwfStr::from_bytes_null_terminated(&self.read_slice(len)?))
} }
/// Reads the next SWF tag from the stream. /// Reads the next SWF tag from the stream.

View File

@ -1,58 +1,49 @@
//! String typed used by SWF files. //! String type used by SWF files.
//!
//! Allows for locale-dependent encoding for SWF version <6.
use encoding_rs::{Encoding, UTF_8}; pub use encoding_rs::{Encoding, SHIFT_JIS, UTF_8, WINDOWS_1252};
use std::{borrow::Cow, fmt}; use std::{borrow::Cow, fmt};
/// `SwfStr` is returned by SWF and AVM1 parsing functions. /// `SwfStr` is the string type returned by SWF parsing functions.
/// `SwfStr` is analogous to `&str`, with some additional allowances: /// `SwfStr` is a bstr-like type analogous to `str`:
/// * An encoding is specified along with the string data. /// * The encoding depends on the SWF version (UTF-8 for SWF6 and higher).
/// * The string contains no null bytes. /// Use `Reader::encoding` or `SwfStr::encoding_for_version` to get the
/// proper encoding.
/// * Invalid data for any particular encoding is allowed; /// * Invalid data for any particular encoding is allowed;
/// any conversions to std::String will be lossy for invalid data. /// any conversions to std::String will be lossy for invalid data.
/// This handles the locale dependent encoding of early SWF files and
/// mimics C-style null-terminated string behavior.
/// To convert this to a standard Rust string, use `SwfStr::to_str_lossy`. /// To convert this to a standard Rust string, use `SwfStr::to_str_lossy`.
/// `SwfStr`s are equal if both their encoding and data matches. #[derive(Eq, PartialEq)]
#[derive(Copy, Clone, Eq, PartialEq)] #[repr(transparent)]
pub struct SwfStr<'a> { pub struct SwfStr {
/// The string bytes. /// The string bytes.
string: &'a [u8], string: [u8],
/// The encoding of the string data.
encoding: &'static Encoding,
} }
impl<'a> SwfStr<'a> { impl SwfStr {
/// Create a new `SwfStr` from a byte slice with a given encoding. /// Create a new `SwfStr` from a byte slice with a given encoding.
/// The string will be truncated if a null byte is encountered. /// The string will be truncated if a null byte is encountered.
/// The data is not required to be valid for the given encoding. /// The data is not required to be valid for the given encoding.
#[inline] #[inline]
pub fn from_bytes(string: &'a [u8], encoding: &'static Encoding) -> Self { pub fn from_bytes(string: &[u8]) -> &Self {
let i = string.iter().position(|&c| c == 0).unwrap_or(string.len()); unsafe { &*(string as *const [u8] as *const Self) }
Self {
string: &string[..i],
encoding,
}
} }
/// Create a new `SwfStr` from a byte slice with a given encoding.
/// The data is not required to be valid for the given encoding.
///
/// # Safety
///
/// The string should contain no null bytes.
#[inline] #[inline]
pub unsafe fn from_bytes_unchecked(string: &'a [u8], encoding: &'static Encoding) -> Self { pub fn from_bytes_null_terminated(string: &[u8]) -> &Self {
Self { string, encoding } let i = string.iter().position(|&c| c == 0).unwrap_or(string.len());
Self::from_bytes(&string[..i])
}
/// Create a new UTF-8 `SwfStr` from a Rust `str`.
#[inline]
pub fn from_utf8_str(string: &str) -> &Self {
Self::from_bytes(string.as_bytes())
} }
/// Create a new UTF-8 `SwfStr` from a Rust `str`. /// Create a new UTF-8 `SwfStr` from a Rust `str`.
/// The string will be truncated if a null byte is encountered. /// The string will be truncated if a null byte is encountered.
#[inline] #[inline]
pub fn from_utf8_str(string: &'a str) -> Self { pub fn from_utf8_str_null_terminated(string: &str) -> &Self {
Self::from_bytes(string.as_bytes(), UTF_8) Self::from_bytes_null_terminated(string.as_bytes())
} }
/// Create a new `SwfStr` with the given encoding from a Rust `str`. /// Create a new `SwfStr` with the given encoding from a Rust `str`.
@ -60,24 +51,34 @@ impl<'a> SwfStr<'a> {
/// The string will be truncated if a null byte is encountered. /// The string will be truncated if a null byte is encountered.
/// `None` is returned if the encoding is not lossless. /// `None` is returned if the encoding is not lossless.
/// Intended for tests. /// Intended for tests.
pub fn from_str_with_encoding(string: &'a str, encoding: &'static Encoding) -> Option<Self> { pub fn from_str_with_encoding<'a>(
string: &'a str,
encoding: &'static Encoding,
) -> Option<&'a Self> {
if let (Cow::Borrowed(s), _, false) = encoding.encode(&string) { if let (Cow::Borrowed(s), _, false) = encoding.encode(&string) {
Some(Self::from_bytes(s, encoding)) Some(Self::from_bytes(s))
} else { } else {
None None
} }
} }
/// Returns the byte slice of this string. /// Returns the suggested string encoding for the given SWF version.
/// For SWF version 6 and higher, this is always UTF-8.
/// For SWF version 5 and lower, this is locale-dependent,
/// and we default to WINDOWS-1252.
#[inline] #[inline]
pub fn as_bytes(&self) -> &'a [u8] { pub fn encoding_for_version(swf_version: u8) -> &'static Encoding {
self.string if swf_version >= 6 {
UTF_8
} else {
WINDOWS_1252
}
} }
/// Returns the encoding used by this string. /// Returns the byte slice of this string.
#[inline] #[inline]
pub fn encoding(&self) -> &'static Encoding { pub fn as_bytes(&self) -> &[u8] {
self.encoding &self.string
} }
/// Returns `true` if the string has a length of zero, and `false` otherwise. /// Returns `true` if the string has a length of zero, and `false` otherwise.
@ -95,47 +96,40 @@ impl<'a> SwfStr<'a> {
/// Decodes the string into a Rust UTF-8 `str`. /// Decodes the string into a Rust UTF-8 `str`.
/// The UTF-8 replacement character will be uses for any invalid data. /// The UTF-8 replacement character will be uses for any invalid data.
#[inline] #[inline]
pub fn to_str_lossy(&self) -> Cow<'a, str> { pub fn to_str_lossy(&self, encoding: &'static Encoding) -> Cow<'_, str> {
self.encoding.decode_without_bom_handling(self.string).0 encoding.decode_without_bom_handling(&self.string).0
} }
/// Decodes the string into a Rust UTF-8 `String`. /// Decodes the string into a Rust UTF-8 `String`.
/// The UTF-8 replacement character will be uses for any invalid data. /// The UTF-8 replacement character will be uses for any invalid data.
#[inline] #[inline]
pub fn to_string_lossy(&self) -> String { pub fn to_string_lossy(&self, encoding: &'static Encoding) -> String {
self.to_str_lossy().into_owned() self.to_str_lossy(encoding).into_owned()
} }
} }
impl<'a> Default for SwfStr<'a> { impl<'a> Default for &'a SwfStr {
fn default() -> Self { fn default() -> &'a SwfStr {
Self { SwfStr::from_bytes(&[])
string: &[],
encoding: UTF_8,
}
} }
} }
impl<'a> From<&'a str> for SwfStr<'a> { impl<'a> From<&'a str> for &'a SwfStr {
fn from(s: &'a str) -> Self { fn from(s: &'a str) -> &'a SwfStr {
SwfStr::from_utf8_str(s) SwfStr::from_utf8_str(s)
} }
} }
impl<'a, T: AsRef<str>> PartialEq<T> for SwfStr<'a> { impl<'a, T: ?Sized + AsRef<str>> PartialEq<T> for SwfStr {
fn eq(&self, other: &T) -> bool { fn eq(&self, other: &T) -> bool {
self.string == other.as_ref().as_bytes() &self.string == other.as_ref().as_bytes()
} }
} }
impl<'a> fmt::Display for SwfStr<'a> { impl fmt::Debug for SwfStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_str_lossy()) // Note that this assumes UTF-8 encoding;
} // other encodings like Shift-JIS will output gibberish.
} f.write_str(&self.to_str_lossy(UTF_8))
impl<'a> fmt::Debug for SwfStr<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_str_lossy())
} }
} }

View File

@ -5,11 +5,10 @@ use crate::avm2::read::tests::read_abc_from_file;
use crate::avm2::types::*; use crate::avm2::types::*;
use crate::read::tests::{read_tag_bytes_from_file, read_tag_bytes_from_file_with_index}; use crate::read::tests::{read_tag_bytes_from_file, read_tag_bytes_from_file_with_index};
use crate::read::{decompress_swf, parse_swf}; use crate::read::{decompress_swf, parse_swf};
use crate::string::SwfStr; use crate::string::{SwfStr, WINDOWS_1252};
use crate::tag_code::TagCode; use crate::tag_code::TagCode;
use crate::types::*; use crate::types::*;
use crate::write::write_swf; use crate::write::write_swf;
use encoding_rs::WINDOWS_1252;
use std::fs::File; use std::fs::File;
use std::vec::Vec; use std::vec::Vec;

View File

@ -247,7 +247,7 @@ pub struct FileAttributes {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct FrameLabel<'a> { pub struct FrameLabel<'a> {
pub label: SwfStr<'a>, pub label: &'a SwfStr,
pub is_anchor: bool, pub is_anchor: bool,
} }
@ -260,7 +260,7 @@ pub struct DefineSceneAndFrameLabelData<'a> {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct FrameLabelData<'a> { pub struct FrameLabelData<'a> {
pub frame_num: u32, pub frame_num: u32,
pub label: SwfStr<'a>, pub label: &'a SwfStr,
} }
pub type Depth = u16; pub type Depth = u16;
@ -274,9 +274,9 @@ pub struct PlaceObject<'a> {
pub matrix: Option<Matrix>, pub matrix: Option<Matrix>,
pub color_transform: Option<ColorTransform>, pub color_transform: Option<ColorTransform>,
pub ratio: Option<u16>, pub ratio: Option<u16>,
pub name: Option<SwfStr<'a>>, pub name: Option<&'a SwfStr>,
pub clip_depth: Option<Depth>, pub clip_depth: Option<Depth>,
pub class_name: Option<SwfStr<'a>>, pub class_name: Option<&'a SwfStr>,
pub filters: Option<Vec<Filter>>, pub filters: Option<Vec<Filter>>,
pub background_color: Option<Color>, pub background_color: Option<Color>,
pub blend_mode: Option<BlendMode>, pub blend_mode: Option<BlendMode>,
@ -472,7 +472,7 @@ pub enum Tag<'a> {
}, },
ShowFrame, ShowFrame,
Protect(Option<SwfStr<'a>>), Protect(Option<&'a SwfStr>),
CsmTextSettings(CsmTextSettings), CsmTextSettings(CsmTextSettings),
DebugId(DebugId), DebugId(DebugId),
DefineBinaryData { DefineBinaryData {
@ -505,8 +505,8 @@ pub enum Tag<'a> {
DefineFontInfo(Box<FontInfo<'a>>), DefineFontInfo(Box<FontInfo<'a>>),
DefineFontName { DefineFontName {
id: CharacterId, id: CharacterId,
name: SwfStr<'a>, name: &'a SwfStr,
copyright_info: SwfStr<'a>, copyright_info: &'a SwfStr,
}, },
DefineMorphShape(Box<DefineMorphShape>), DefineMorphShape(Box<DefineMorphShape>),
DefineScalingGrid { DefineScalingGrid {
@ -524,14 +524,14 @@ pub enum Tag<'a> {
id: CharacterId, id: CharacterId,
action_data: &'a [u8], action_data: &'a [u8],
}, },
EnableDebugger(SwfStr<'a>), EnableDebugger(&'a SwfStr),
EnableTelemetry { EnableTelemetry {
password_hash: &'a [u8], password_hash: &'a [u8],
}, },
End, End,
Metadata(SwfStr<'a>), Metadata(&'a SwfStr),
ImportAssets { ImportAssets {
url: SwfStr<'a>, url: &'a SwfStr,
imports: Vec<ExportedAsset<'a>>, imports: Vec<ExportedAsset<'a>>,
}, },
JpegTables(JpegTables<'a>), JpegTables(JpegTables<'a>),
@ -545,7 +545,7 @@ pub enum Tag<'a> {
SoundStreamHead2(Box<SoundStreamHead>), SoundStreamHead2(Box<SoundStreamHead>),
StartSound(StartSound), StartSound(StartSound),
StartSound2 { StartSound2 {
class_name: SwfStr<'a>, class_name: &'a SwfStr,
sound_info: Box<SoundInfo>, sound_info: Box<SoundInfo>,
}, },
SymbolClass(Vec<SymbolClassLink<'a>>), SymbolClass(Vec<SymbolClassLink<'a>>),
@ -570,7 +570,7 @@ pub type ExportAssets<'a> = Vec<ExportedAsset<'a>>;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct ExportedAsset<'a> { pub struct ExportedAsset<'a> {
pub id: CharacterId, pub id: CharacterId,
pub name: SwfStr<'a>, pub name: &'a SwfStr,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -584,7 +584,7 @@ pub type SetBackgroundColor = Color;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct SymbolClassLink<'a> { pub struct SymbolClassLink<'a> {
pub id: CharacterId, pub id: CharacterId,
pub class_name: SwfStr<'a>, pub class_name: &'a SwfStr,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -897,7 +897,7 @@ pub struct FontV1 {
pub struct Font<'a> { pub struct Font<'a> {
pub version: u8, pub version: u8,
pub id: CharacterId, pub id: CharacterId,
pub name: SwfStr<'a>, pub name: &'a SwfStr,
pub language: Language, pub language: Language,
pub layout: Option<FontLayout>, pub layout: Option<FontLayout>,
pub glyphs: Vec<Glyph>, pub glyphs: Vec<Glyph>,
@ -913,7 +913,7 @@ pub struct Font4<'a> {
pub id: CharacterId, pub id: CharacterId,
pub is_italic: bool, pub is_italic: bool,
pub is_bold: bool, pub is_bold: bool,
pub name: SwfStr<'a>, pub name: &'a SwfStr,
pub data: Option<&'a [u8]>, pub data: Option<&'a [u8]>,
} }
@ -944,7 +944,7 @@ pub struct KerningRecord {
pub struct FontInfo<'a> { pub struct FontInfo<'a> {
pub id: CharacterId, pub id: CharacterId,
pub version: u8, pub version: u8,
pub name: SwfStr<'a>, pub name: &'a SwfStr,
pub is_small_text: bool, pub is_small_text: bool,
pub is_shift_jis: bool, pub is_shift_jis: bool,
pub is_ansi: bool, pub is_ansi: bool,
@ -983,13 +983,13 @@ pub struct EditText<'a> {
pub id: CharacterId, pub id: CharacterId,
pub bounds: Rectangle, pub bounds: Rectangle,
pub font_id: Option<CharacterId>, // TODO(Herschel): Combine with height pub font_id: Option<CharacterId>, // TODO(Herschel): Combine with height
pub font_class_name: Option<SwfStr<'a>>, pub font_class_name: Option<&'a SwfStr>,
pub height: Option<Twips>, pub height: Option<Twips>,
pub color: Option<Color>, pub color: Option<Color>,
pub max_length: Option<u16>, pub max_length: Option<u16>,
pub layout: Option<TextLayout>, pub layout: Option<TextLayout>,
pub variable_name: SwfStr<'a>, pub variable_name: &'a SwfStr,
pub initial_text: Option<SwfStr<'a>>, pub initial_text: Option<&'a SwfStr>,
pub is_word_wrap: bool, pub is_word_wrap: bool,
pub is_multiline: bool, pub is_multiline: bool,
pub is_password: bool, pub is_password: bool,
@ -1116,7 +1116,7 @@ pub struct DefineBitsJpeg3<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct DoAbc<'a> { pub struct DoAbc<'a> {
pub name: SwfStr<'a>, pub name: &'a SwfStr,
pub is_lazy_initialize: bool, pub is_lazy_initialize: bool,
pub data: &'a [u8], pub data: &'a [u8],
} }

View File

@ -144,7 +144,7 @@ pub trait SwfWriteExt {
fn write_i32(&mut self, n: i32) -> io::Result<()>; fn write_i32(&mut self, n: i32) -> io::Result<()>;
fn write_f32(&mut self, n: f32) -> io::Result<()>; fn write_f32(&mut self, n: f32) -> io::Result<()>;
fn write_f64(&mut self, n: f64) -> io::Result<()>; fn write_f64(&mut self, n: f64) -> io::Result<()>;
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()>; fn write_string(&mut self, s: &'_ SwfStr) -> io::Result<()>;
} }
pub struct BitWriter<W: Write> { pub struct BitWriter<W: Write> {
@ -257,7 +257,7 @@ impl<W: Write> SwfWriteExt for Writer<W> {
} }
#[inline] #[inline]
fn write_string(&mut self, s: SwfStr<'_>) -> io::Result<()> { fn write_string(&mut self, s: &'_ SwfStr) -> io::Result<()> {
self.output.write_all(s.as_bytes())?; self.output.write_all(s.as_bytes())?;
self.write_u8(0) self.write_u8(0)
} }