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

View File

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

View File

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

View File

@ -5,15 +5,21 @@ using LibHac.Fs;
using LibHac.FsSrv.Impl; using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf; using LibHac.FsSrv.Sf;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Spl; using LibHac.Spl;
using LibHac.Tools.Es; using LibHac.Tools.Es;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using ApplicationId = LibHac.ApplicationId; using ApplicationId = LibHac.ApplicationId;
using ContentType = LibHac.Fs.ContentType;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
@ -73,6 +79,172 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
return ResultCode.Success; 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) public static ResultCode OpenFileSystemFromInternalFile(ServiceCtx context, string fullPath, out IFileSystem openedFileSystem)
{ {
openedFileSystem = null; openedFileSystem = null;

View File

@ -8,6 +8,7 @@ using LibHac.FsSystem;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Sf; using LibHac.Sf;
using LibHac.Spl; using LibHac.Spl;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common; using Ryujinx.Common;
@ -15,6 +16,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy; using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
using Ryujinx.HLE.HOS.Services.Ns.Types; using Ryujinx.HLE.HOS.Services.Ns.Types;
using Ryujinx.HLE.HOS.Services.Pcv; using Ryujinx.HLE.HOS.Services.Pcv;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.IO; 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 // OpenFileSystemWithPatch(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid) -> object<nn::fssrv::sf::IFileSystem> contentFs
public ResultCode OpenFileSystemWithPatch(ServiceCtx context) public ResultCode OpenFileSystemWithPatch(ServiceCtx context)
{ {
// TODO: Find out why the title id read from here is incorrect.
FileSystemType fileSystemType = (FileSystemType)context.RequestData.ReadInt32(); 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; string switchPath = string.Empty;
Logger.Stub?.PrintStub(LogClass.ServiceFs); Logger.Stub?.PrintStub(LogClass.ServiceFs);
foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles) foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles)
@ -70,7 +78,18 @@ namespace Ryujinx.HLE.HOS.Services.Fs
return ResultCode.PathDoesNotExist; return ResultCode.PathDoesNotExist;
} }
string fullPath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(switchPath); string fullPath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(switchPath);
ResultCode result = FileSystemProxyHelper.OpenFileSystemFromInternalFile(context, fullPath, out FileSystemProxy.IFileSystem fileSystem); if (fullPath == null)
{
fullPath = switchPath;
}
FileStream fileStream = new(fullPath, FileMode.Open, FileAccess.Read);
string extension = System.IO.Path.GetExtension(fullPath);
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) if (result == ResultCode.Success)
{ {
@ -79,6 +98,56 @@ namespace Ryujinx.HLE.HOS.Services.Fs
return result; 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)] [CommandCmif(8)]
// OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path) // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path)

View File

@ -16,7 +16,13 @@ namespace Ryujinx.HLE.HOS.Services.Ns
// GetRunningApplicationProgramId() -> u64 // GetRunningApplicationProgramId() -> u64
public ResultCode GetRunningApplicationProgramId(ServiceCtx context) 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; 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 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;
}
} }
} }