Skip to content

Commit

Permalink
avm2: implemented flash.utils.unescapeMultiByte
Browse files Browse the repository at this point in the history
  • Loading branch information
dowgird authored and relrelb committed Sep 24, 2022
1 parent c532d70 commit e448f5e
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 1 deletion.
1 change: 1 addition & 0 deletions core/src/avm2/globals/flash/utils.as
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ package flash.utils {
public native function setTimeout(closure:Function, delay:Number, ... arguments):uint;
public native function clearTimeout(id:uint):void;
public native function escapeMultiByte(s:String):String;
public native function unescapeMultiByte(s:String):String;
}
48 changes: 48 additions & 0 deletions core/src/avm2/globals/flash/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,54 @@ pub fn escape_multi_byte<'gc>(
Ok(v.into())
}

fn handle_percent<I>(chars: &mut I) -> Option<u8>
where
I: Iterator<Item = char>,
{
let high = chars.next()?.to_digit(16)? as u8;
let low = chars.next()?.to_digit(16)? as u8;
Some(low | (high << 4))
}

/// Implements `flash.utils.unescapeMultiByte`
pub fn unescape_multi_byte<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let s = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_string(activation)?;
let bs = s.as_wstr();
let mut buf = WString::new();
let chars = bs.chars().map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER));

let mut chars = chars.peekable();
while let Some(c) = chars.next() {
if c == '\0' {
break;
}
if c == '%' {
let mut bytes = Vec::new();
while let Some(b) = handle_percent(&mut chars) {
bytes.push(b);
if !matches!(chars.peek(), Some('%')) {
break;
}
chars.next();
}
buf.push_str(&WString::from_utf8_bytes(bytes));

continue;
}

buf.push_char(c);
}
let v = AvmString::new(activation.context.gc_context, buf);
Ok(v.into())
}

/// Implements `flash.utils.getQualifiedClassName`
pub fn get_qualified_class_name<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
Expand Down
57 changes: 56 additions & 1 deletion tests/tests/swfs/avm2/escape_multi_byte/Test.as
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,59 @@ ba.clear();
for(i=1; i < 0x80; i++) {
ba.writeByte(i);
}
trace(escapeMultiByte(ba));
trace(escapeMultiByte(ba));


trace("// unescapeMultiByte - invalid percent sequences");
for each (var s:String in ["", "%", "%A", "%AG", "%GA", "%%", "%A%", "%G%"] ) {
trace("// " + s);
trace(unescapeMultiByte(s));
}

// This is 0xDC00 utf-encoded:
ba.clear();
ba.writeByte(0xed);
ba.writeByte(0xb0);
ba.writeByte(0x80);
trace("// unescapeMultiByte, unpaired surrogate")
trace(escape(unescapeMultiByte(ba)));


// Zero char
ba.clear();
ba.writeByte(0x50);
ba.writeByte(0x00);
ba.writeByte(0x50);
trace("// unescapeMultiByte, stops on zero char");
trace(escape(unescapeMultiByte(ba)));

// Zero char after percent
ba.clear();
ba.writeByte(0x25); // %
ba.writeByte(0x00);
ba.writeByte(0x48);
trace("// unescapeMultiByte, does not stop on zero char after %");
trace(escape(unescapeMultiByte(ba)));

trace("// unescapeMultiByte - P");
trace(unescapeMultiByte("%50"));

trace("// unescapeMultiByte handles lowercase");
trace(unescapeMultiByte("%4f"));


trace("// invader char");
trace(escape(unescapeMultiByte("%F0%9F%91%BE")));

trace("// percent-escaped unpaired surrogate");
trace(escape(unescapeMultiByte("%ED%B0%80")));

trace("// percent-escaped invalid utf-8");
trace(escape(unescapeMultiByte("%ED%B0")));

trace("// percent-escaped invalid utf-8");
trace(escape(unescapeMultiByte("%F0%9F%91")));

trace("// percent-escaped invalid utf-8 but lowercase");
trace(escape(unescapeMultiByte("%f0%9f%91")));

37 changes: 37 additions & 0 deletions tests/tests/swfs/avm2/escape_multi_byte/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,40 @@ P
%EF%BF%BD
// chars 0x01 - 0x7f
%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E%5F%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E%7F
// unescapeMultiByte - invalid percent sequences
//

// %

// %A

// %AG

// %GA
A
// %%

// %A%

// %G%

// unescapeMultiByte, unpaired surrogate
%uFFFD
// unescapeMultiByte, stops on zero char
P
// unescapeMultiByte, does not stop on zero char after %
H
// unescapeMultiByte - P
P
// unescapeMultiByte handles lowercase
O
// invader char
%uD83D%uDC7E
// percent-escaped unpaired surrogate
%uDC00
// percent-escaped invalid utf-8
%ED%B0
// percent-escaped invalid utf-8
%F0%9F%91
// percent-escaped invalid utf-8 but lowercase
%F0%9F%91
Binary file modified tests/tests/swfs/avm2/escape_multi_byte/test.swf
Binary file not shown.

0 comments on commit e448f5e

Please sign in to comment.