From b126ea48c63a3de8da8f3b817860c0323f2621ef Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Thu, 14 Feb 2019 01:44:39 +0100 Subject: [PATCH] Support HomeBrew Loader (#577) * Make it possibles to load hb-loader and hb-menu One issue remains with hb-menu homebrew icons because of SIMD issues (libjpeg-turbo related) and netloader doesn't work. * Implement GetApplicationControlData * Fix shared fonts for NSO/NRO * Add homebrew NRO romfs support This readd the NRO support by parsing the ASET header * Address comments about HomebrewRomFs * override Dispose in homebrew romfs stream * Use a struct for file timestamp * Simplify positional increments in GetApplicationControlData * Address comments * improve readability of the memory permission check in SetProcessMemoryPermission * Fix previous broken check * Add address space checks in SetProcessMemoryPermission --- Ryujinx.HLE/FileSystem/FileSystemProvider.cs | 30 +++ Ryujinx.HLE/FileSystem/IFileSystemProvider.cs | 3 + Ryujinx.HLE/FileSystem/PFsProvider.cs | 5 + Ryujinx.HLE/FileSystem/RomFsProvider.cs | 5 + Ryujinx.HLE/HOS/HomebrewRomFsStream.cs | 77 +++++++ Ryujinx.HLE/HOS/Horizon.cs | 58 ++++- .../HOS/Kernel/SupervisorCall/SvcMemory.cs | 126 +++++++++++ .../HOS/Kernel/SupervisorCall/SvcTable.cs | 5 +- .../HOS/Services/FspSrv/FileTimestamp.cs | 11 + .../HOS/Services/FspSrv/IFileSystem.cs | 32 ++- .../Ns/IApplicationManagerInterface.cs | 209 +++++++++++++++++- .../Services/Ns/IServiceGetterInterface.cs | 9 +- .../HOS/Services/Pm/IShellInterface.cs | 34 +++ Ryujinx.HLE/HOS/Services/ServiceFactory.cs | 5 + Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 37 +++- Ryujinx.HLE/HOS/Services/Sm/SmErr.cs | 1 + .../Executables/NxRelocatableObject.cs | 3 + 17 files changed, 633 insertions(+), 17 deletions(-) create mode 100644 Ryujinx.HLE/HOS/HomebrewRomFsStream.cs create mode 100644 Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs create mode 100644 Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs diff --git a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs index a806c9eeb..f5459eece 100644 --- a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.FspSrv; +using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -279,5 +280,34 @@ namespace Ryujinx.HLE.FileSystem throw new InvalidOperationException($"Path {path} is not a child directory of {_rootPath}"); } + + public FileTimestamp GetFileTimeStampRaw(string name) + { + CheckIfDescendentOfRootPath(name); + + DateTime creationDateTime = DateTime.UnixEpoch; + DateTime modifiedDateTime = DateTime.UnixEpoch; + DateTime lastAccessDateTime = DateTime.UnixEpoch; + + if (File.Exists(name)) + { + creationDateTime = File.GetCreationTime(name); + modifiedDateTime = File.GetLastWriteTime(name); + lastAccessDateTime = File.GetLastAccessTime(name); + } + else if (Directory.Exists(name)) + { + creationDateTime = Directory.GetCreationTime(name); + modifiedDateTime = Directory.GetLastWriteTime(name); + lastAccessDateTime = Directory.GetLastAccessTime(name); + } + + return new FileTimestamp + { + CreationDateTime = creationDateTime, + ModifiedDateTime = modifiedDateTime, + LastAccessDateTime = lastAccessDateTime + }; + } } } diff --git a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs index 8e2cae643..82cdebd90 100644 --- a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.FspSrv; +using System; namespace Ryujinx.HLE.FileSystem { @@ -36,5 +37,7 @@ namespace Ryujinx.HLE.FileSystem long GetFreeSpace(ServiceCtx context); long GetTotalSpace(ServiceCtx context); + + FileTimestamp GetFileTimeStampRaw(string name); } } diff --git a/Ryujinx.HLE/FileSystem/PFsProvider.cs b/Ryujinx.HLE/FileSystem/PFsProvider.cs index fdddc9b08..69e7a9b83 100644 --- a/Ryujinx.HLE/FileSystem/PFsProvider.cs +++ b/Ryujinx.HLE/FileSystem/PFsProvider.cs @@ -143,5 +143,10 @@ namespace Ryujinx.HLE.FileSystem { throw new NotSupportedException(); } + + public FileTimestamp GetFileTimeStampRaw(string name) + { + throw new NotImplementedException(); + } } } diff --git a/Ryujinx.HLE/FileSystem/RomFsProvider.cs b/Ryujinx.HLE/FileSystem/RomFsProvider.cs index 86bf23485..f64d99c70 100644 --- a/Ryujinx.HLE/FileSystem/RomFsProvider.cs +++ b/Ryujinx.HLE/FileSystem/RomFsProvider.cs @@ -160,5 +160,10 @@ namespace Ryujinx.HLE.FileSystem { throw new NotSupportedException(); } + + public FileTimestamp GetFileTimeStampRaw(string name) + { + throw new NotImplementedException(); + } } } diff --git a/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs b/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs new file mode 100644 index 000000000..f39faf4d4 --- /dev/null +++ b/Ryujinx.HLE/HOS/HomebrewRomFsStream.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; + +namespace Ryujinx.HLE.HOS +{ + class HomebrewRomFsStream : Stream + { + private Stream _baseStream; + private long _positionOffset; + + public HomebrewRomFsStream(Stream baseStream, long positionOffset) + { + _baseStream = baseStream; + _positionOffset = positionOffset; + + _baseStream.Position = _positionOffset; + } + + public override bool CanRead => _baseStream.CanRead; + + public override bool CanSeek => _baseStream.CanSeek; + + public override bool CanWrite => false; + + public override long Length => _baseStream.Length - _positionOffset; + + public override long Position + { + get + { + return _baseStream.Position - _positionOffset; + } + set + { + _baseStream.Position = value + _positionOffset; + } + } + + public override void Flush() + { + _baseStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _baseStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin == SeekOrigin.Begin) + { + offset += _positionOffset; + } + + return _baseStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _baseStream.Dispose(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 8a419af34..b5ce555a1 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -102,6 +102,8 @@ namespace Ryujinx.HLE.HOS public Horizon(Switch device) { + ControlData = new Nacp(); + Device = device; State = new SystemStateMgr(); @@ -549,14 +551,58 @@ namespace Ryujinx.HLE.HOS bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; - using (FileStream input = new FileStream(filePath, FileMode.Open)) - { - IExecutable staticObject = isNro - ? (IExecutable)new NxRelocatableObject(input) - : new NxStaticObject(input); + FileStream input = new FileStream(filePath, FileMode.Open); - ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject }); + IExecutable staticObject; + + if (isNro) + { + NxRelocatableObject obj = new NxRelocatableObject(input); + staticObject = obj; + + // homebrew NRO can actually have some data after the actual NRO + if (input.Length > obj.FileSize) + { + input.Position = obj.FileSize; + + BinaryReader reader = new BinaryReader(input); + + uint asetMagic = reader.ReadUInt32(); + + if (asetMagic == 0x54455341) + { + uint asetVersion = reader.ReadUInt32(); + if (asetVersion == 0) + { + ulong iconOffset = reader.ReadUInt64(); + ulong iconSize = reader.ReadUInt64(); + + ulong nacpOffset = reader.ReadUInt64(); + ulong nacpSize = reader.ReadUInt64(); + + ulong romfsOffset = reader.ReadUInt64(); + ulong romfsSize = reader.ReadUInt64(); + + if (romfsSize != 0) + { + Device.FileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset)); + } + } + else + { + Logger.PrintWarning(LogClass.Loader, $"Unsupported ASET header version found \"{asetVersion}\""); + } + } + } } + else + { + staticObject = new NxStaticObject(input); + } + + ContentManager.LoadEntries(); + + ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject }); } private Npdm GetDefaultNpdm() diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs index 388dcc217..6f8180c50 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs @@ -386,6 +386,132 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _process.MemoryManager.UnmapPhysicalMemory(address, size); } + public KernelResult MapProcessCodeMemory64(int handle, ulong dst, ulong src, ulong size) + { + return MapProcessCodeMemory(handle, dst, src, size); + } + + public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) + { + if (!PageAligned(dst) || !PageAligned(src)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); + + KProcess targetProcess = currentProcess.HandleTable.GetObject(handle); + + if (targetProcess == null) + { + return KernelResult.InvalidHandle; + } + + if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) || + targetProcess.MemoryManager.OutsideAddrSpace(src, size) || + targetProcess.MemoryManager.InsideAliasRegion(dst, size) || + targetProcess.MemoryManager.InsideHeapRegion(dst, size)) + { + return KernelResult.InvalidMemRange; + } + + if (size + dst <= dst || size + src <= src) + { + return KernelResult.InvalidMemState; + } + + return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size); + } + + public KernelResult UnmapProcessCodeMemory64(int handle, ulong dst, ulong src, ulong size) + { + return UnmapProcessCodeMemory(handle, dst, src, size); + } + + public KernelResult UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) + { + if (!PageAligned(dst) || !PageAligned(src)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); + + KProcess targetProcess = currentProcess.HandleTable.GetObject(handle); + + if (targetProcess == null) + { + return KernelResult.InvalidHandle; + } + + if (targetProcess.MemoryManager.OutsideAddrSpace(dst, size) || + targetProcess.MemoryManager.OutsideAddrSpace(src, size) || + targetProcess.MemoryManager.InsideAliasRegion(dst, size) || + targetProcess.MemoryManager.InsideHeapRegion(dst, size)) + { + return KernelResult.InvalidMemRange; + } + + if (size + dst <= dst || size + src <= src) + { + return KernelResult.InvalidMemState; + } + + return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); + } + + public KernelResult SetProcessMemoryPermission64(int handle, ulong src, ulong size, MemoryPermission permission) + { + return SetProcessMemoryPermission(handle, src, size, permission); + } + + public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, MemoryPermission permission) + { + if (!PageAligned(src)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + if (permission != MemoryPermission.None && + permission != MemoryPermission.Read && + permission != MemoryPermission.ReadAndWrite && + permission != MemoryPermission.ReadAndExecute) + { + return KernelResult.InvalidPermission; + } + + KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); + + KProcess targetProcess = currentProcess.HandleTable.GetObject(handle); + + if (targetProcess == null) + { + return KernelResult.InvalidHandle; + } + + if (targetProcess.MemoryManager.OutsideAddrSpace(src, size)) + { + return KernelResult.InvalidMemState; + } + + return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission); + } + private static bool PageAligned(ulong position) { return (position & (KMemoryManager.PageSize - 1)) == 0; diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index cbcb712f7..dd98e8a06 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -71,7 +71,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { 0x6f, nameof(SvcHandler.GetSystemInfo64) }, { 0x70, nameof(SvcHandler.CreatePort64) }, { 0x71, nameof(SvcHandler.ManageNamedPort64) }, - { 0x72, nameof(SvcHandler.ConnectToPort64) } + { 0x72, nameof(SvcHandler.ConnectToPort64) }, + { 0x73, nameof(SvcHandler.SetProcessMemoryPermission64) }, + { 0x77, nameof(SvcHandler.MapProcessCodeMemory64) }, + { 0x78, nameof(SvcHandler.UnmapProcessCodeMemory64) } }; _svcTable64 = new Action[0x80]; diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs new file mode 100644 index 000000000..879fb78cd --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + struct FileTimestamp + { + public DateTime CreationDateTime; + public DateTime ModifiedDateTime; + public DateTime LastAccessDateTime; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index 9e2944601..bcb9dbaf8 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -38,8 +38,8 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { 10, Commit }, { 11, GetFreeSpaceSize }, { 12, GetTotalSpaceSize }, - { 13, CleanDirectoryRecursively } - //{ 14, GetFileTimeStampRaw } + { 13, CleanDirectoryRecursively }, + { 14, GetFileTimeStampRaw } }; _openPaths = new HashSet(); @@ -368,6 +368,34 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + // GetFileTimeStampRaw(buffer, 0x19, 0x301> path) -> bytes<0x20> timestamp + public long GetFileTimeStampRaw(ServiceCtx context) + { + string name = ReadUtf8String(context); + + string path = _provider.GetFullPath(name); + + if (_provider.FileExists(path) || _provider.DirectoryExists(path)) + { + FileTimestamp timestamp = _provider.GetFileTimeStampRaw(path); + + context.ResponseData.Write(new DateTimeOffset(timestamp.CreationDateTime).ToUnixTimeSeconds()); + context.ResponseData.Write(new DateTimeOffset(timestamp.ModifiedDateTime).ToUnixTimeSeconds()); + context.ResponseData.Write(new DateTimeOffset(timestamp.LastAccessDateTime).ToUnixTimeSeconds()); + + byte[] data = new byte[8]; + + // is valid? + data[0] = 1; + + context.ResponseData.Write(data); + + return 0; + } + + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + private bool IsPathAlreadyInUse(string path) { lock (_openPaths) diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index 72d7787f8..88fdb7922 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -1,5 +1,9 @@ -using Ryujinx.HLE.HOS.Ipc; +using LibHac; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using System; using System.Collections.Generic; +using System.Text; namespace Ryujinx.HLE.HOS.Services.Ns { @@ -9,14 +13,211 @@ namespace Ryujinx.HLE.HOS.Services.Ns public override IReadOnlyDictionary Commands => _commands; - private bool _isInitialized; - public IApplicationManagerInterface() { _commands = new Dictionary { - + { 400, GetApplicationControlData } }; } + + public long GetApplicationControlData(ServiceCtx context) + { + long position = context.Request.ReceiveBuff[0].Position; + + Nacp nacp = context.Device.System.ControlData; + + for (int i = 0; i < 0x10; i++) + { + NacpDescription description = nacp.Descriptions[i]; + + byte[] titleData = new byte[0x200]; + byte[] developerData = new byte[0x100]; + + if (description !=null && description.Title != null) + { + byte[] titleDescriptionData = Encoding.ASCII.GetBytes(description.Title); + Buffer.BlockCopy(titleDescriptionData, 0, titleData, 0, titleDescriptionData.Length); + + } + + if (description != null && description.Developer != null) + { + byte[] developerDescriptionData = Encoding.ASCII.GetBytes(description.Developer); + Buffer.BlockCopy(developerDescriptionData, 0, developerData, 0, developerDescriptionData.Length); + } + + context.Memory.WriteBytes(position, titleData); + context.Memory.WriteBytes(position + 0x200, developerData); + + position += i * 0x300; + } + + byte[] isbn = new byte[0x25]; + + if (nacp.Isbn != null) + { + byte[] isbnData = Encoding.ASCII.GetBytes(nacp.Isbn); + Buffer.BlockCopy(isbnData, 0, isbn, 0, isbnData.Length); + } + + context.Memory.WriteBytes(position, isbn); + position += isbn.Length; + + context.Memory.WriteByte(position++, nacp.StartupUserAccount); + context.Memory.WriteByte(position++, nacp.TouchScreenUsageMode); + context.Memory.WriteByte(position++, nacp.AocRegistrationType); + + context.Memory.WriteInt32(position, nacp.AttributeFlag); + position += 4; + + context.Memory.WriteUInt32(position, nacp.SupportedLanguageFlag); + position += 4; + + context.Memory.WriteUInt32(position, nacp.ParentalControlFlag); + position += 4; + + context.Memory.WriteByte(position++, nacp.Screenshot); + context.Memory.WriteByte(position++, nacp.VideoCapture); + context.Memory.WriteByte(position++, nacp.DataLossConfirmation); + context.Memory.WriteByte(position++, nacp.PlayLogPolicy); + + context.Memory.WriteUInt64(position, nacp.PresenceGroupId); + position += 8; + + for (int i = 0; i < nacp.RatingAge.Length; i++) + { + context.Memory.WriteSByte(position++, nacp.RatingAge[i]); + } + + byte[] displayVersion = new byte[0x10]; + + if (nacp.DisplayVersion != null) + { + byte[] displayVersionData = Encoding.ASCII.GetBytes(nacp.DisplayVersion); + Buffer.BlockCopy(displayVersionData, 0, displayVersion, 0, displayVersionData.Length); + } + + context.Memory.WriteBytes(position, displayVersion); + position += displayVersion.Length; + + context.Memory.WriteUInt64(position, nacp.AddOnContentBaseId); + position += 8; + + context.Memory.WriteUInt64(position, nacp.SaveDataOwnerId); + position += 8; + + context.Memory.WriteInt64(position, nacp.UserAccountSaveDataSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.UserAccountSaveDataJournalSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.DeviceSaveDataSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.DeviceSaveDataJournalSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.BcatDeliveryCacheStorageSize); + position += 8; + + byte[] applicationErrorCodeCategory = new byte[0x8]; + + if (nacp.ApplicationErrorCodeCategory != null) + { + byte[] applicationErrorCodeCategoryData = Encoding.ASCII.GetBytes(nacp.ApplicationErrorCodeCategory); + Buffer.BlockCopy(applicationErrorCodeCategoryData, 0, applicationErrorCodeCategoryData, 0, applicationErrorCodeCategoryData.Length); + } + + context.Memory.WriteBytes(position, applicationErrorCodeCategory); + position += applicationErrorCodeCategory.Length; + + for (int i = 0; i < nacp.LocalCommunicationId.Length; i++) + { + context.Memory.WriteUInt64(position, nacp.LocalCommunicationId[i]); + position += 8; + } + + context.Memory.WriteByte(position++, nacp.LogoType); + context.Memory.WriteByte(position++, nacp.LogoHandling); + context.Memory.WriteByte(position++, nacp.RuntimeAddOnContentInstall); + + byte[] reserved000 = new byte[0x3]; + context.Memory.WriteBytes(position, reserved000); + position += reserved000.Length; + + context.Memory.WriteByte(position++, nacp.CrashReport); + context.Memory.WriteByte(position++, nacp.Hdcp); + context.Memory.WriteUInt64(position, nacp.SeedForPseudoDeviceId); + position += 8; + + byte[] bcatPassphrase = new byte[65]; + if (nacp.BcatPassphrase != null) + { + byte[] bcatPassphraseData = Encoding.ASCII.GetBytes(nacp.BcatPassphrase); + Buffer.BlockCopy(bcatPassphraseData, 0, bcatPassphrase, 0, bcatPassphraseData.Length); + } + + context.Memory.WriteBytes(position, bcatPassphrase); + position += bcatPassphrase.Length; + + context.Memory.WriteByte(position++, nacp.Reserved01); + + byte[] reserved02 = new byte[0x6]; + context.Memory.WriteBytes(position, reserved02); + position += reserved02.Length; + + context.Memory.WriteInt64(position, nacp.UserAccountSaveDataSizeMax); + position += 8; + + context.Memory.WriteInt64(position, nacp.UserAccountSaveDataJournalSizeMax); + position += 8; + + context.Memory.WriteInt64(position, nacp.DeviceSaveDataSizeMax); + position += 8; + + context.Memory.WriteInt64(position, nacp.DeviceSaveDataJournalSizeMax); + position += 8; + + context.Memory.WriteInt64(position, nacp.TemporaryStorageSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.CacheStorageSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.CacheStorageJournalSize); + position += 8; + + context.Memory.WriteInt64(position, nacp.CacheStorageDataAndJournalSizeMax); + position += 8; + + context.Memory.WriteInt16(position, nacp.CacheStorageIndex); + position += 2; + + byte[] reserved03 = new byte[0x6]; + context.Memory.WriteBytes(position, reserved03); + position += reserved03.Length; + + + for (int i = 0; i < 16; i++) + { + ulong value = 0; + + if (nacp.PlayLogQueryableApplicationId.Count > i) + { + value = nacp.PlayLogQueryableApplicationId[i]; + } + + context.Memory.WriteUInt64(position, value); + position += 8; + } + + context.Memory.WriteByte(position++, nacp.PlayLogQueryCapability); + context.Memory.WriteByte(position++, nacp.RepairFlag); + context.Memory.WriteByte(position++, nacp.ProgramIndex); + + return 0; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs index 12f7b69b1..89dde1f9b 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs @@ -13,8 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns { _commands = new Dictionary { - //... + { 7996, GetApplicationManagerInterface } }; } + + public long GetApplicationManagerInterface(ServiceCtx context) + { + MakeObject(context, new IApplicationManagerInterface()); + + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs new file mode 100644 index 000000000..8880b3348 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs @@ -0,0 +1,34 @@ +using Ryujinx.HLE.HOS.Ipc; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Pm +{ + class IShellInterface : IpcService + { + private Dictionary _commands; + + public override IReadOnlyDictionary Commands => _commands; + + public IShellInterface() + { + _commands = new Dictionary + { + { 6, GetApplicationPid } + }; + } + + // GetApplicationPid() -> u64 + public long GetApplicationPid(ServiceCtx context) + { + // FIXME: This is wrong but needed to make hb loader works + // TODO: Change this when we will have a way to process via a PM like interface. + long pid = context.Process.Pid; + + context.ResponseData.Write(pid); + + return 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index 3853d82e6..83a217a5d 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -17,6 +17,7 @@ using Ryujinx.HLE.HOS.Services.Ns; using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.Services.Pctl; using Ryujinx.HLE.HOS.Services.Pl; +using Ryujinx.HLE.HOS.Services.Pm; using Ryujinx.HLE.HOS.Services.Prepo; using Ryujinx.HLE.HOS.Services.Psm; using Ryujinx.HLE.HOS.Services.Set; @@ -131,6 +132,7 @@ namespace Ryujinx.HLE.HOS.Services case "ns:am": return new IApplicationManagerInterface(); + case "ns:am2": case "ns:ec": return new IServiceGetterInterface(); @@ -161,6 +163,9 @@ namespace Ryujinx.HLE.HOS.Services case "pl:u": return new ISharedFontManager(); + case "pm:shell": + return new IShellInterface(); + case "prepo:a": return new IPrepoService(); diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 6940bfc8d..914862f1c 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -23,9 +23,10 @@ namespace Ryujinx.HLE.HOS.Services.Sm { _commands = new Dictionary { - { 0, Initialize }, - { 1, GetService }, - { 2, RegisterService } + { 0, Initialize }, + { 1, GetService }, + { 2, RegisterService }, + { 3, UnregisterService } }; _registeredServices = new ConcurrentDictionary(); @@ -128,6 +129,36 @@ namespace Ryujinx.HLE.HOS.Services.Sm return 0; } + public long UnregisterService(ServiceCtx context) + { + if (!_isInitialized) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized); + } + + long namePosition = context.RequestData.BaseStream.Position; + + string name = ReadName(context); + + context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin); + + bool isLight = (context.RequestData.ReadInt32() & 1) != 0; + + int maxSessions = context.RequestData.ReadInt32(); + + if (name == string.Empty) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName); + } + + if (!_registeredServices.TryRemove(name, out _)) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotRegistered); + } + + return 0; + } + private static string ReadName(ServiceCtx context) { string name = string.Empty; diff --git a/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs index 5b5a66dc2..7dd3a2057 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs @@ -5,5 +5,6 @@ namespace Ryujinx.HLE.HOS.Services.Sm public const int NotInitialized = 2; public const int AlreadyRegistered = 4; public const int InvalidName = 6; + public const int NotRegistered = 7; } } \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs index eb3ca94a9..e68fe2670 100644 --- a/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs +++ b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs @@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Loaders.Executables public int RoOffset { get; private set; } public int DataOffset { get; private set; } public int BssSize { get; private set; } + public int FileSize { get; private set; } public int BssOffset => DataOffset + Data.Length; @@ -59,6 +60,8 @@ namespace Ryujinx.HLE.Loaders.Executables Text = Read(textOffset, textSize); Ro = Read(roOffset, roSize); Data = Read(dataOffset, dataSize); + + FileSize = fileSize; } } } \ No newline at end of file