aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/FileSystem/FileSystemProvider.cs30
-rw-r--r--Ryujinx.HLE/FileSystem/IFileSystemProvider.cs3
-rw-r--r--Ryujinx.HLE/FileSystem/PFsProvider.cs5
-rw-r--r--Ryujinx.HLE/FileSystem/RomFsProvider.cs5
-rw-r--r--Ryujinx.HLE/HOS/HomebrewRomFsStream.cs77
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs56
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs126
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs5
-rw-r--r--Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs11
-rw-r--r--Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs32
-rw-r--r--Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs209
-rw-r--r--Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Pm/IShellInterface.cs34
-rw-r--r--Ryujinx.HLE/HOS/Services/ServiceFactory.cs5
-rw-r--r--Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs37
-rw-r--r--Ryujinx.HLE/HOS/Services/Sm/SmErr.cs1
-rw-r--r--Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs3
17 files changed, 632 insertions, 16 deletions
diff --git a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs
index a806c9ee..f5459eec 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 8e2cae64..82cdebd9 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 fdddc9b0..69e7a9b8 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 86bf2348..f64d99c7 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 00000000..f39faf4d
--- /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 8a419af3..b5ce555a 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))
+ FileStream input = new FileStream(filePath, FileMode.Open);
+
+ IExecutable staticObject;
+
+ if (isNro)
{
- IExecutable staticObject = isNro
- ? (IExecutable)new NxRelocatableObject(input)
- : new NxStaticObject(input);
+ 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);
- ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
+ 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 388dcc21..6f8180c5 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<KProcess>(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<KProcess>(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<KProcess>(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 cbcb712f..dd98e8a0 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<SvcHandler, CpuThreadState>[0x80];
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs
new file mode 100644
index 00000000..879fb78c
--- /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 9e294460..bcb9dbaf 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<string>();
@@ -368,6 +368,34 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
return 0;
}
+ // GetFileTimeStampRaw(buffer<bytes<0x301>, 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 72d7787f..88fdb792 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<int, ServiceProcessRequest> Commands => _commands;
- private bool _isInitialized;
-
public IApplicationManagerInterface()
{
_commands = new Dictionary<int, ServiceProcessRequest>
{
-
+ { 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 12f7b69b..89dde1f9 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<int, ServiceProcessRequest>
{
- //...
+ { 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 00000000..8880b334
--- /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<int, ServiceProcessRequest> _commands;
+
+ public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
+
+ public IShellInterface()
+ {
+ _commands = new Dictionary<int, ServiceProcessRequest>
+ {
+ { 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 3853d82e..83a217a5 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 6940bfc8..914862f1 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<int, ServiceProcessRequest>
{
- { 0, Initialize },
- { 1, GetService },
- { 2, RegisterService }
+ { 0, Initialize },
+ { 1, GetService },
+ { 2, RegisterService },
+ { 3, UnregisterService }
};
_registeredServices = new ConcurrentDictionary<string, KPort>();
@@ -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 5b5a66dc..7dd3a205 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 eb3ca94a..e68fe267 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