aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/HOS/Applets/AppletManager.cs8
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs11
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs105
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs133
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs47
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs14
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs9
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs8
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs13
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs9
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs9
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs62
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs10
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs11
-rw-r--r--Ryujinx.HLE/HOS/Applets/CommonArguments.cs16
15 files changed, 463 insertions, 2 deletions
diff --git a/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/Ryujinx.HLE/HOS/Applets/AppletManager.cs
index 1cba9ec9..5d075882 100644
--- a/Ryujinx.HLE/HOS/Applets/AppletManager.cs
+++ b/Ryujinx.HLE/HOS/Applets/AppletManager.cs
@@ -1,4 +1,5 @@
-using Ryujinx.HLE.HOS.Services.Am.AppletAE;
+using Ryujinx.HLE.HOS.Applets.Browser;
+using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
using System.Collections.Generic;
@@ -14,7 +15,10 @@ namespace Ryujinx.HLE.HOS.Applets
{
{ AppletId.PlayerSelect, typeof(PlayerSelectApplet) },
{ AppletId.Controller, typeof(ControllerApplet) },
- { AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) }
+ { AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) },
+ { AppletId.LibAppletWeb, typeof(BrowserApplet) },
+ { AppletId.LibAppletShop, typeof(BrowserApplet) },
+ { AppletId.LibAppletOff, typeof(BrowserApplet) }
};
}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs b/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs
new file mode 100644
index 00000000..fe6e6040
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BootDisplayKind.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ enum BootDisplayKind
+ {
+ White,
+ Offline,
+ Black,
+ Share,
+ Lobby
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
new file mode 100644
index 00000000..f9693c34
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
@@ -0,0 +1,105 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Am.AppletAE;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ internal class BrowserApplet : IApplet
+ {
+ public event EventHandler AppletStateChanged;
+
+ private AppletSession _normalSession;
+ private AppletSession _interactiveSession;
+
+ private CommonArguments _commonArguments;
+ private List<BrowserArgument> _arguments;
+ private ShimKind _shimKind;
+
+ public BrowserApplet(Horizon system) {}
+
+ public ResultCode GetResult()
+ {
+ return ResultCode.Success;
+ }
+
+ public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
+ {
+ _normalSession = normalSession;
+ _interactiveSession = interactiveSession;
+
+ _commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop());
+
+ Logger.PrintStub(LogClass.ServiceAm, $"WebApplet version: 0x{_commonArguments.AppletVersion:x8}");
+
+ ReadOnlySpan<byte> webArguments = _normalSession.Pop();
+
+ (_shimKind, _arguments) = BrowserArgument.ParseArguments(webArguments);
+
+ Logger.PrintStub(LogClass.ServiceAm, $"Web Arguments: {_arguments.Count}");
+
+ foreach (BrowserArgument argument in _arguments)
+ {
+ Logger.PrintStub(LogClass.ServiceAm, $"{argument.Type}: {argument.GetValue()}");
+ }
+
+ if ((_commonArguments.AppletVersion >= 0x80000 && _shimKind == ShimKind.Web) || (_commonArguments.AppletVersion >= 0x30000 && _shimKind == ShimKind.Share))
+ {
+ List<BrowserOutput> result = new List<BrowserOutput>();
+
+ result.Add(new BrowserOutput(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton));
+
+ _normalSession.Push(BuildResponseNew(result));
+ }
+ else
+ {
+ WebCommonReturnValue result = new WebCommonReturnValue()
+ {
+ ExitReason = WebExitReason.ExitButton,
+ };
+
+ _normalSession.Push(BuildResponseOld(result));
+ }
+
+ AppletStateChanged?.Invoke(this, null);
+
+ return ResultCode.Success;
+ }
+
+ private byte[] BuildResponseOld(WebCommonReturnValue result)
+ {
+ using (MemoryStream stream = new MemoryStream())
+ using (BinaryWriter writer = new BinaryWriter(stream))
+ {
+ writer.WriteStruct(result);
+
+ return stream.ToArray();
+ }
+ }
+ private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
+ {
+ using (MemoryStream stream = new MemoryStream())
+ using (BinaryWriter writer = new BinaryWriter(stream))
+ {
+ writer.WriteStruct(new WebArgHeader
+ {
+ Count = (ushort)outputArguments.Count,
+ ShimKind = _shimKind
+ });
+
+ foreach (BrowserOutput output in outputArguments)
+ {
+ output.Write(writer);
+ }
+
+ writer.Write(new byte[0x2000 - writer.BaseStream.Position]);
+
+ return stream.ToArray();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs
new file mode 100644
index 00000000..17fd4089
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserArgument.cs
@@ -0,0 +1,133 @@
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ class BrowserArgument
+ {
+ public WebArgTLVType Type { get; }
+ public byte[] Value { get; }
+
+ public BrowserArgument(WebArgTLVType type, byte[] value)
+ {
+ Type = type;
+ Value = value;
+ }
+
+ private static readonly Dictionary<WebArgTLVType, Type> _typeRegistry = new Dictionary<WebArgTLVType, Type>
+ {
+ { WebArgTLVType.InitialURL, typeof(string) },
+ { WebArgTLVType.CallbackUrl, typeof(string) },
+ { WebArgTLVType.CallbackableUrl, typeof(string) },
+ { WebArgTLVType.ApplicationId, typeof(ulong) },
+ { WebArgTLVType.DocumentPath, typeof(string) },
+ { WebArgTLVType.DocumentKind, typeof(DocumentKind) },
+ { WebArgTLVType.SystemDataId, typeof(ulong) },
+ { WebArgTLVType.Whitelist, typeof(string) },
+ { WebArgTLVType.NewsFlag, typeof(bool) },
+ { WebArgTLVType.UserID, typeof(UserId) },
+ { WebArgTLVType.ScreenShotEnabled, typeof(bool) },
+ { WebArgTLVType.EcClientCertEnabled, typeof(bool) },
+ { WebArgTLVType.UnknownFlag0x14, typeof(bool) },
+ { WebArgTLVType.UnknownFlag0x15, typeof(bool) },
+ { WebArgTLVType.PlayReportEnabled, typeof(bool) },
+ { WebArgTLVType.BootDisplayKind, typeof(BootDisplayKind) },
+ { WebArgTLVType.FooterEnabled, typeof(bool) },
+ { WebArgTLVType.PointerEnabled, typeof(bool) },
+ { WebArgTLVType.LeftStickMode, typeof(LeftStickMode) },
+ { WebArgTLVType.KeyRepeatFrame1, typeof(int) },
+ { WebArgTLVType.KeyRepeatFrame2, typeof(int) },
+ { WebArgTLVType.BootAsMediaPlayerInverted, typeof(bool) },
+ { WebArgTLVType.DisplayUrlKind, typeof(bool) },
+ { WebArgTLVType.BootAsMediaPlayer, typeof(bool) },
+ { WebArgTLVType.ShopJumpEnabled, typeof(bool) },
+ { WebArgTLVType.MediaAutoPlayEnabled, typeof(bool) },
+ { WebArgTLVType.LobbyParameter, typeof(string) },
+ { WebArgTLVType.JsExtensionEnabled, typeof(bool) },
+ { WebArgTLVType.AdditionalCommentText, typeof(string) },
+ { WebArgTLVType.TouchEnabledOnContents, typeof(bool) },
+ { WebArgTLVType.UserAgentAdditionalString, typeof(string) },
+ { WebArgTLVType.MediaPlayerAutoCloseEnabled, typeof(bool) },
+ { WebArgTLVType.PageCacheEnabled, typeof(bool) },
+ { WebArgTLVType.WebAudioEnabled, typeof(bool) },
+ { WebArgTLVType.PageFadeEnabled, typeof(bool) },
+ { WebArgTLVType.BootLoadingIconEnabled, typeof(bool) },
+ { WebArgTLVType.PageScrollIndicatorEnabled, typeof(bool) },
+ { WebArgTLVType.MediaPlayerSpeedControlEnabled, typeof(bool) },
+ { WebArgTLVType.OverrideWebAudioVolume, typeof(float) },
+ { WebArgTLVType.OverrideMediaAudioVolume, typeof(float) },
+ { WebArgTLVType.MediaPlayerUiEnabled, typeof(bool) },
+ };
+
+ public static (ShimKind, List<BrowserArgument>) ParseArguments(ReadOnlySpan<byte> data)
+ {
+ List<BrowserArgument> browserArguments = new List<BrowserArgument>();
+
+ WebArgHeader header = IApplet.ReadStruct<WebArgHeader>(data.Slice(0, 8));
+
+ ReadOnlySpan<byte> rawTLVs = data.Slice(8);
+
+ for (int i = 0; i < header.Count; i++)
+ {
+ WebArgTLV tlv = IApplet.ReadStruct<WebArgTLV>(rawTLVs);
+ ReadOnlySpan<byte> tlvData = rawTLVs.Slice(Unsafe.SizeOf<WebArgTLV>(), tlv.Size);
+
+ browserArguments.Add(new BrowserArgument((WebArgTLVType)tlv.Type, tlvData.ToArray()));
+
+ rawTLVs = rawTLVs.Slice(Unsafe.SizeOf<WebArgTLV>() + tlv.Size);
+ }
+
+ return (header.ShimKind, browserArguments);
+ }
+
+ public object GetValue()
+ {
+ if (_typeRegistry.TryGetValue(Type, out Type valueType))
+ {
+ if (valueType == typeof(string))
+ {
+ return Encoding.UTF8.GetString(Value);
+ }
+ else if (valueType == typeof(bool))
+ {
+ return Value[0] == 1;
+ }
+ else if (valueType == typeof(uint))
+ {
+ return BitConverter.ToUInt32(Value);
+ }
+ else if (valueType == typeof(int))
+ {
+ return BitConverter.ToInt32(Value);
+ }
+ else if (valueType == typeof(ulong))
+ {
+ return BitConverter.ToUInt64(Value);
+ }
+ else if (valueType == typeof(long))
+ {
+ return BitConverter.ToInt64(Value);
+ }
+ else if (valueType == typeof(float))
+ {
+ return BitConverter.ToSingle(Value);
+ }
+ else if (valueType == typeof(UserId))
+ {
+ return new UserId(Value);
+ }
+ else if (valueType.IsEnum)
+ {
+ return Enum.ToObject(valueType, BitConverter.ToInt32(Value));
+ }
+
+ return $"{valueType.Name} parsing not implemented";
+ }
+
+ return $"Unknown value format (raw length: {Value.Length})";
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs
new file mode 100644
index 00000000..0b368262
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutput.cs
@@ -0,0 +1,47 @@
+using Ryujinx.Common;
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ class BrowserOutput
+ {
+ public BrowserOutputType Type { get; }
+ public byte[] Value { get; }
+
+ public BrowserOutput(BrowserOutputType type, byte[] value)
+ {
+ Type = type;
+ Value = value;
+ }
+
+ public BrowserOutput(BrowserOutputType type, uint value)
+ {
+ Type = type;
+ Value = BitConverter.GetBytes(value);
+ }
+
+ public BrowserOutput(BrowserOutputType type, ulong value)
+ {
+ Type = type;
+ Value = BitConverter.GetBytes(value);
+ }
+
+ public BrowserOutput(BrowserOutputType type, bool value)
+ {
+ Type = type;
+ Value = BitConverter.GetBytes(value);
+ }
+
+ public void Write(BinaryWriter writer)
+ {
+ writer.WriteStruct(new WebArgTLV
+ {
+ Type = (ushort)Type,
+ Size = (ushort)Value.Length
+ });
+
+ writer.Write(Value);
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs
new file mode 100644
index 00000000..209ae8ae
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserOutputType.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ enum BrowserOutputType : ushort
+ {
+ ExitReason = 0x1,
+ LastUrl = 0x2,
+ LastUrlSize = 0x3,
+ SharePostResult = 0x4,
+ PostServiceName = 0x5,
+ PostServiceNameSize = 0x6,
+ PostId = 0x7,
+ MediaPlayerAutoClosedByCompletion = 0x8
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs b/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs
new file mode 100644
index 00000000..385bcdd0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/DocumentKind.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ enum DocumentKind
+ {
+ OfflineHtmlPage = 1,
+ ApplicationLegalInformation,
+ SystemDataPage
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs b/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs
new file mode 100644
index 00000000..917549d2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/LeftStickMode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ enum LeftStickMode
+ {
+ Pointer = 0,
+ Cursor
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs b/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs
new file mode 100644
index 00000000..ca2ef32f
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/ShimKind.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ public enum ShimKind : uint
+ {
+ Shop = 1,
+ Login,
+ Offline,
+ Share,
+ Web,
+ Wifi,
+ Lobby
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs b/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs
new file mode 100644
index 00000000..c5e19f6c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/WebArgHeader.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ public struct WebArgHeader
+ {
+ public ushort Count;
+ public ushort Padding;
+ public ShimKind ShimKind;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs b/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs
new file mode 100644
index 00000000..f6c1e5ae
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLV.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ public struct WebArgTLV
+ {
+ public ushort Type;
+ public ushort Size;
+ public uint Padding;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs b/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs
new file mode 100644
index 00000000..bd303207
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/WebArgTLVType.cs
@@ -0,0 +1,62 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ enum WebArgTLVType : ushort
+ {
+ InitialURL = 0x1,
+ CallbackUrl = 0x3,
+ CallbackableUrl = 0x4,
+ ApplicationId = 0x5,
+ DocumentPath = 0x6,
+ DocumentKind = 0x7,
+ SystemDataId = 0x8,
+ ShareStartPage = 0x9,
+ Whitelist = 0xA,
+ NewsFlag = 0xB,
+ UserID = 0xE,
+ AlbumEntry0 = 0xF,
+ ScreenShotEnabled = 0x10,
+ EcClientCertEnabled = 0x11,
+ PlayReportEnabled = 0x13,
+ UnknownFlag0x14 = 0x14,
+ UnknownFlag0x15 = 0x15,
+ BootDisplayKind = 0x17,
+ BackgroundKind = 0x18,
+ FooterEnabled = 0x19,
+ PointerEnabled = 0x1A,
+ LeftStickMode = 0x1B,
+ KeyRepeatFrame1 = 0x1C,
+ KeyRepeatFrame2 = 0x1D,
+ BootAsMediaPlayerInverted = 0x1E,
+ DisplayUrlKind = 0x1F,
+ BootAsMediaPlayer = 0x21,
+ ShopJumpEnabled = 0x22,
+ MediaAutoPlayEnabled = 0x23,
+ LobbyParameter = 0x24,
+ ApplicationAlbumEntry = 0x26,
+ JsExtensionEnabled = 0x27,
+ AdditionalCommentText = 0x28,
+ TouchEnabledOnContents = 0x29,
+ UserAgentAdditionalString = 0x2A,
+ AdditionalMediaData0 = 0x2B,
+ MediaPlayerAutoCloseEnabled = 0x2C,
+ PageCacheEnabled = 0x2D,
+ WebAudioEnabled = 0x2E,
+ FooterFixedKind = 0x32,
+ PageFadeEnabled = 0x33,
+ MediaCreatorApplicationRatingAge = 0x34,
+ BootLoadingIconEnabled = 0x35,
+ PageScrollIndicatorEnabled = 0x36,
+ MediaPlayerSpeedControlEnabled = 0x37,
+ AlbumEntry1 = 0x38,
+ AlbumEntry2 = 0x39,
+ AlbumEntry3 = 0x3A,
+ AdditionalMediaData1 = 0x3B,
+ AdditionalMediaData2 = 0x3C,
+ AdditionalMediaData3 = 0x3D,
+ BootFooterButton = 0x3E,
+ OverrideWebAudioVolume = 0x3F,
+ OverrideMediaAudioVolume = 0x40,
+ BootMode = 0x41,
+ MediaPlayerUiEnabled = 0x43
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs b/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs
new file mode 100644
index 00000000..aab7a86b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/WebCommonReturnValue.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ public unsafe struct WebCommonReturnValue
+ {
+ public WebExitReason ExitReason;
+ public uint Padding;
+ public fixed byte LastUrl[0x1000];
+ public ulong LastUrlSize;
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs b/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs
new file mode 100644
index 00000000..4e44d34a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Browser/WebExitReason.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Applets.Browser
+{
+ public enum WebExitReason : uint
+ {
+ ExitButton,
+ BackButton,
+ Requested,
+ LastUrl,
+ ErrorDialog = 7
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/CommonArguments.cs b/Ryujinx.HLE/HOS/Applets/CommonArguments.cs
new file mode 100644
index 00000000..5da34db1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/CommonArguments.cs
@@ -0,0 +1,16 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Applets
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 8)]
+ struct CommonArguments
+ {
+ public uint Version;
+ public uint StructureSize;
+ public uint AppletVersion;
+ public uint ThemeColor;
+ [MarshalAs(UnmanagedType.I1)]
+ public bool PlayStartupSound;
+ public ulong SystemTicks;
+ }
+}