feat: add support for offline html docs inside the browser applet

This commit is contained in:
Jacobwasbeast 2025-02-16 17:43:58 -06:00
parent f73a7dce4f
commit 7fe5035e88
7 changed files with 289 additions and 25 deletions

View File

@ -522,24 +522,21 @@ namespace Ryujinx.HLE.HOS.Applets
internal RealApplet GetApplicationApplet()
{
RealApplet applet = null;
lock (_lock)
if (_application != null)
{
foreach (var (_, value) in _applets)
return _application;
}
else
{
if (_homeMenu != null)
{
if (value.IsApplication)
{
applet = value;
break;
}
return _homeMenu;
}
else
{
return _overlayDisp;
}
}
if (applet == null)
{
return _foregroundRequestedApplet;
}
return applet;
}
public void RemoveProcess(ulong processHandlePid)

View File

@ -218,6 +218,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
return ResultCode.Success;
}
[CommandCmif(51)]
// ReportVisibleErrorWithErrorContext(nn::err::ErrorCode)
public ResultCode ReportVisibleErrorWithErrorContext(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(60)]
// GetMainAppletApplicationDesiredLanguage() -> nn::os::Language
public ResultCode GetMainAppletApplicationDesiredLanguage(ServiceCtx context)

View File

@ -26,8 +26,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetAppletResourceUserIdOfCallerApplet() -> nn::applet::AppletResourceUserId
public ResultCode GetAppletResourceUserIdOfCallerApplet(ServiceCtx context)
{
ulong appletResourceUserId = _pid;
appletResourceUserId = context.Device.System.WindowSystem.GetByAruId(_pid).CallerApplet.ProcessHandle.TitleId;
ulong appletResourceUserId = 0x0100000000001000;
if (context.Device.System.WindowSystem.GetApplicationApplet() != null)
{
appletResourceUserId = context.Device.System.WindowSystem.GetApplicationApplet().ProcessHandle.TitleId;
}
context.ResponseData.Write(appletResourceUserId);
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { appletResourceUserId });
return ResultCode.Success;

View File

@ -5,15 +5,21 @@ using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Spl;
using LibHac.Tools.Es;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using ApplicationId = LibHac.ApplicationId;
using ContentType = LibHac.Fs.ContentType;
using Path = System.IO.Path;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
@ -72,6 +78,172 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
return ResultCode.Success;
}
public static ResultCode OpenXciHtml(ServiceCtx context, ulong applicationId, Switch device, LibHac.Fs.IStorage ncaStorage, out IFileSystem openedFileSystem)
{
openedFileSystem = null;
try
{
Xci xci = new(context.Device.System.KeySet, ncaStorage);
if (!xci.HasPartition(XciPartitionType.Secure))
{
return ResultCode.PartitionNotFound;
}
var partitionFileSystem = xci.OpenPartition(XciPartitionType.Secure);
Nca nca = null;
try
{
Dictionary<ulong, ContentMetaData> applications = partitionFileSystem.GetContentData(ContentMetaType.Application, device.FileSystem, device.System.FsIntegrityCheckLevel);
if (applicationId == 0)
{
foreach ((ulong _, ContentMetaData content) in applications)
{
nca = content.GetNcaByType(device.FileSystem.KeySet, LibHac.Ncm.ContentType.HtmlDocument, device.Configuration.UserChannelPersistence.Index);
break;
}
}
else if (applications.TryGetValue(applicationId, out ContentMetaData content))
{
nca = content.GetNcaByType(device.FileSystem.KeySet, LibHac.Ncm.ContentType.HtmlDocument, device.Configuration.UserChannelPersistence.Index);
}
ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure();
}
catch (Exception ex)
{
return ResultCode.InvalidInput;
}
LibHac.Fs.Fsa.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
using SharedRef<LibHac.Fs.Fsa.IFileSystem> sharedFs = new(fileSystem);
using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref sharedFs.Ref, true);
openedFileSystem = new IFileSystem(ref adapter.Ref);
return ResultCode.Success;
}
catch (HorizonResultException ex)
{
return (ResultCode)ex.ResultValue.Value;
}
}
public static ResultCode OpenNspHtml(ServiceCtx context, string nspPath, ulong applicationId, Switch device, LibHac.Fs.IStorage ncaStorage, out IFileSystem openedFileSystem)
{
openedFileSystem = null;
try
{
LocalStorage storage = new(nspPath, FileAccess.Read, FileMode.Open);
PartitionFileSystem pfs = new();
using SharedRef<LibHac.Fs.Fsa.IFileSystem> nsp = new(pfs);
pfs.Initialize(storage).ThrowIfFailure();
ImportTitleKeysFromNsp(nsp.Get, context.Device.System.KeySet);
Nca nca = null;
try
{
Dictionary<ulong, ContentMetaData> applications = nsp.Get.GetContentData(ContentMetaType.Application, device.FileSystem, device.System.FsIntegrityCheckLevel);
if (applicationId == 0)
{
foreach ((ulong _, ContentMetaData content) in applications)
{
nca = content.GetNcaByType(device.FileSystem.KeySet, LibHac.Ncm.ContentType.HtmlDocument, device.Configuration.UserChannelPersistence.Index);
break;
}
}
else if (applications.TryGetValue(applicationId, out ContentMetaData content))
{
nca = content.GetNcaByType(device.FileSystem.KeySet, LibHac.Ncm.ContentType.HtmlDocument, device.Configuration.UserChannelPersistence.Index);
}
ProcessLoaderHelper.RegisterProgramMapInfo(device, nsp.Get).ThrowIfFailure();
}
catch (Exception ex)
{
return ResultCode.InvalidInput;
}
LibHac.Fs.Fsa.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
using SharedRef<LibHac.Fs.Fsa.IFileSystem> sharedFs = new(fileSystem);
using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref sharedFs.Ref, true);
openedFileSystem = new IFileSystem(ref adapter.Ref);
return ResultCode.Success;
}
catch (HorizonResultException ex)
{
return (ResultCode)ex.ResultValue.Value;
}
}
public static ResultCode OpenNcaHtml(ServiceCtx context, string nspPath, ulong applicationId, Switch device, LibHac.Fs.IStorage ncaStorage, out IFileSystem openedFileSystem)
{
openedFileSystem = null;
try
{
Nca ncaApp = new(context.Device.System.KeySet, ncaStorage);
if (!ncaApp.SectionExists(NcaSectionType.Data))
{
return ResultCode.PartitionNotFound;
}
LibHac.Fs.Fsa.IFileSystem fileSystemB = ncaApp.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
Nca nca = null;
try
{
Dictionary<ulong, ContentMetaData> applications = fileSystemB.GetContentData(ContentMetaType.Application, device.FileSystem, device.System.FsIntegrityCheckLevel);
if (applicationId == 0)
{
foreach ((ulong _, ContentMetaData content) in applications)
{
nca = content.GetNcaByType(device.FileSystem.KeySet, LibHac.Ncm.ContentType.HtmlDocument, device.Configuration.UserChannelPersistence.Index);
break;
}
}
else if (applications.TryGetValue(applicationId, out ContentMetaData content))
{
nca = content.GetNcaByType(device.FileSystem.KeySet, LibHac.Ncm.ContentType.HtmlDocument, device.Configuration.UserChannelPersistence.Index);
}
ProcessLoaderHelper.RegisterProgramMapInfo(device, fileSystemB).ThrowIfFailure();
}
catch (Exception ex)
{
return ResultCode.InvalidInput;
}
LibHac.Fs.Fsa.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
using SharedRef<LibHac.Fs.Fsa.IFileSystem> sharedFs = new(fileSystem);
using SharedRef<LibHac.FsSrv.Sf.IFileSystem> adapter = FileSystemInterfaceAdapter.CreateShared(ref sharedFs.Ref, true);
openedFileSystem = new IFileSystem(ref adapter.Ref);
return ResultCode.Success;
}
catch (HorizonResultException ex)
{
return (ResultCode)ex.ResultValue.Value;
}
}
public static ResultCode OpenFileSystemFromInternalFile(ServiceCtx context, string fullPath, out IFileSystem openedFileSystem)
{

View File

@ -8,6 +8,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Sf;
using LibHac.Spl;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common;
@ -15,6 +16,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
using Ryujinx.HLE.HOS.Services.Ns.Types;
using Ryujinx.HLE.HOS.Services.Pcv;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.Memory;
using System;
using System.IO;
@ -52,8 +54,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// OpenFileSystemWithPatch(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid) -> object<nn::fssrv::sf::IFileSystem> contentFs
public ResultCode OpenFileSystemWithPatch(ServiceCtx context)
{
// TODO: Find out why the title id read from here is incorrect.
FileSystemType fileSystemType = (FileSystemType)context.RequestData.ReadInt32();
ulong titleId = context.RequestData.ReadUInt64();
ulong titleId = 0x0100000000001000;
if (context.Device.System.WindowSystem.GetApplicationApplet() != null)
{
titleId = context.Device.System.WindowSystem.GetApplicationApplet().ProcessHandle.TitleId;
}
Logger.Info?.Print(LogClass.ServiceAm, $" { titleId} {fileSystemType} ");
string switchPath = string.Empty;
Logger.Stub?.PrintStub(LogClass.ServiceFs);
foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles)
@ -70,14 +78,75 @@ namespace Ryujinx.HLE.HOS.Services.Fs
return ResultCode.PathDoesNotExist;
}
string fullPath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(switchPath);
ResultCode result = FileSystemProxyHelper.OpenFileSystemFromInternalFile(context, fullPath, out FileSystemProxy.IFileSystem fileSystem);
if (result == ResultCode.Success)
if (fullPath == null)
{
MakeObject(context, fileSystem);
fullPath = switchPath;
}
FileStream fileStream = new(fullPath, FileMode.Open, FileAccess.Read);
string extension = System.IO.Path.GetExtension(fullPath);
return result;
if (fileSystemType == FileSystemType.ContentManual)
{
if (extension == ".xci")
{
ResultCode result = FileSystemProxyHelper.OpenXciHtml(context, titleId, context.Device,fileStream.AsStorage(), out FileSystemProxy.IFileSystem fileSystem);
if (result == ResultCode.Success)
{
MakeObject(context, fileSystem);
}
return result;
}
else if (extension == ".nsp")
{
ResultCode result = FileSystemProxyHelper.OpenNspHtml(context, fullPath, titleId, context.Device,fileStream.AsStorage(), out FileSystemProxy.IFileSystem fileSystem);
if (result == ResultCode.Success)
{
MakeObject(context, fileSystem);
}
return result;
}
else
{
ResultCode result = FileSystemProxyHelper.OpenNcaHtml(context, fullPath, titleId, context.Device,fileStream.AsStorage(), out FileSystemProxy.IFileSystem fileSystem);
if (result == ResultCode.Success)
{
MakeObject(context, fileSystem);
}
return result;
}
}
else
{
if (extension == ".nca")
{
ResultCode result = FileSystemProxyHelper.OpenNcaFs(context, fullPath, fileStream.AsStorage(), out FileSystemProxy.IFileSystem fileSystem);
if (result == ResultCode.Success)
{
MakeObject(context, fileSystem);
}
return result;
}
else if (extension == ".nsp")
{
ResultCode result = FileSystemProxyHelper.OpenNsp(context, fullPath, out FileSystemProxy.IFileSystem fileSystem);
if (result == ResultCode.Success)
{
MakeObject(context, fileSystem);
}
return result;
}
}
return ResultCode.InvalidInput;
}
[CommandCmif(8)]

View File

@ -16,7 +16,13 @@ namespace Ryujinx.HLE.HOS.Services.Ns
// GetRunningApplicationProgramId() -> u64
public ResultCode GetRunningApplicationProgramId(ServiceCtx context)
{
context.ResponseData.Write(context.Device.Processes.ActiveApplication.ProgramId);
ulong appletResourceUserId = 0x0100000000001000;
if (context.Device.System.WindowSystem.GetApplicationApplet() != null)
{
appletResourceUserId = context.Device.System.WindowSystem.GetApplicationApplet().ProcessHandle.TitleId;
}
context.ResponseData.Write(appletResourceUserId);
return ResultCode.Success;
}
}

View File

@ -1,7 +1,16 @@
namespace Ryujinx.HLE.HOS.Services.Ns
using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Ns
{
class IReadOnlyApplicationRecordInterface : IpcService
{
[CommandCmif(2)] // 10.0.0+
// IsDataCorruptedResult() -> bool
public ResultCode IsDataCorrupted(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceNs);
context.ResponseData.Write(false);
return ResultCode.Success;
}
}
}