From 683f6cc890dd6e9ec47fef3da7538111c3e25f78 Mon Sep 17 00:00:00 2001 From: EmperorBale Date: Tue, 7 Mar 2023 17:23:16 -0800 Subject: [PATCH] avm2: Implement `unescape` --- core/src/avm2/globals.rs | 1 + core/src/avm2/globals/toplevel.rs | 48 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 834c31180..2fc33266f 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -502,6 +502,7 @@ pub fn load_player_globals<'gc>( toplevel::encode_uri_component, script, )?; + function(activation, "", "unescape", toplevel::unescape, script)?; avm2_system_class!(regexp, activation, regexp::create_class(activation), script); avm2_system_class!(vector, activation, vector::create_class(activation), script); diff --git a/core/src/avm2/globals/toplevel.rs b/core/src/avm2/globals/toplevel.rs index cd5bb1d70..893cf90c2 100644 --- a/core/src/avm2/globals/toplevel.rs +++ b/core/src/avm2/globals/toplevel.rs @@ -8,6 +8,7 @@ use crate::avm2::value::Value; use crate::avm2::Error; use crate::string::{AvmString, WStr, WString}; use crate::stub::Stub; +use ruffle_wstr::Integer; use std::borrow::Cow; use std::fmt::Write; @@ -267,6 +268,53 @@ pub fn escape<'gc>( Ok(AvmString::new(activation.context.gc_context, output).into()) } +pub fn unescape<'gc>( + activation: &mut Activation<'_, 'gc>, + _this: Option>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let value = match args.first() { + None => return Ok("undefined".into()), + Some(Value::Undefined) => return Ok("null".into()), + Some(value) => value.coerce_to_string(activation)?, + }; + + let mut output = WString::new(); + let mut index = 0; + while let Some(byte) = value.get(index) { + index += 1; + if byte != b'%' as u16 { + output.push(byte); + continue; + } + + let prev_index = index; + let len = match value.get(index) { + // 0x75 == 'u' + Some(0x75) => { + // increment one to consume the 'u' + index += 1; + 4 + } + _ => 2, + }; + + if let Some(x) = value + .slice(index..) + .and_then(|v| v.slice(..len)) + .and_then(|v| u32::from_wstr_radix(v, 16).ok()) + { + // NOTE: Yes, unpaired surrogates are allowed + output.push(x as u16); + index += len; + } else { + output.push(b'%' as u16); + index = prev_index; + } + } + Ok(AvmString::new(activation.context.gc_context, output).into()) +} + pub fn encode_uri<'gc>( activation: &mut Activation<'_, 'gc>, _this: Option>,