mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-04 05:18:56 +02:00
Android changes
This commit is contained in:
@@ -205,7 +205,7 @@ namespace Ryujinx.HLE.FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearAocData() => AocData.Clear();
|
||||
public void ClearAocData() => AocData.Clear();
|
||||
|
||||
public int GetAocCount() => AocData.Count;
|
||||
|
||||
@@ -476,6 +476,27 @@ namespace Ryujinx.HLE.FileSystem
|
||||
FinishInstallation(temporaryDirectory, registeredDirectory);
|
||||
}
|
||||
|
||||
public void InstallFirmware(Stream stream, bool isXci)
|
||||
{
|
||||
ContentPath.TryGetContentPath(StorageId.BuiltInSystem, out var contentPathString);
|
||||
ContentPath.TryGetRealPath(contentPathString, out var contentDirectory);
|
||||
string registeredDirectory = Path.Combine(contentDirectory, "registered");
|
||||
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
|
||||
|
||||
if (!isXci)
|
||||
{
|
||||
using ZipArchive archive = new ZipArchive(stream);
|
||||
InstallFromZip(archive, temporaryDirectory);
|
||||
}
|
||||
else
|
||||
{
|
||||
Xci xci = new(_virtualFileSystem.KeySet, stream.AsStorage());
|
||||
InstallFromCart(xci, temporaryDirectory);
|
||||
}
|
||||
|
||||
FinishInstallation(temporaryDirectory, registeredDirectory);
|
||||
}
|
||||
|
||||
public void InstallKeys(string keysSource, string installDirectory)
|
||||
{
|
||||
if (Directory.Exists(keysSource))
|
||||
@@ -662,8 +683,6 @@ namespace Ryujinx.HLE.FileSystem
|
||||
throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers.");
|
||||
}
|
||||
|
||||
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
|
||||
|
||||
if (Directory.Exists(firmwarePackage))
|
||||
{
|
||||
return VerifyAndGetVersionDirectory(firmwarePackage);
|
||||
@@ -702,299 +721,327 @@ namespace Ryujinx.HLE.FileSystem
|
||||
break;
|
||||
}
|
||||
|
||||
SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory)
|
||||
return null;
|
||||
}
|
||||
|
||||
public SystemVersion VerifyFirmwarePackage(Stream file, bool isXci)
|
||||
{
|
||||
if (!isXci)
|
||||
{
|
||||
return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory));
|
||||
using ZipArchive archive = new ZipArchive(file, ZipArchiveMode.Read);
|
||||
return VerifyAndGetVersionZip(archive);
|
||||
}
|
||||
|
||||
SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
|
||||
else
|
||||
{
|
||||
SystemVersion systemVersion = null;
|
||||
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
if (xci.HasPartition(XciPartitionType.Update))
|
||||
{
|
||||
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
|
||||
{
|
||||
using Stream ncaStream = GetZipStream(entry);
|
||||
IStorage storage = ncaStream.AsStorage();
|
||||
XciPartition partition = xci.OpenPartition(XciPartitionType.Update);
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, storage);
|
||||
|
||||
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
|
||||
{
|
||||
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
|
||||
}
|
||||
else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
|
||||
{
|
||||
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry))
|
||||
{
|
||||
string metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||
|
||||
CnmtContentMetaEntry[] metaEntries = null;
|
||||
|
||||
var fileEntry = archive.GetEntry(metaPath);
|
||||
|
||||
using (Stream ncaStream = GetZipStream(fileEntry))
|
||||
{
|
||||
Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
||||
|
||||
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
if (meta.Type == ContentMetaType.SystemUpdate)
|
||||
{
|
||||
metaEntries = meta.MetaEntries;
|
||||
|
||||
updateNcas.Remove(SystemUpdateTitleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (metaEntries == null)
|
||||
{
|
||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||
}
|
||||
|
||||
if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem))
|
||||
{
|
||||
string versionEntry = updateNcasItem.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
||||
|
||||
using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry));
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
||||
|
||||
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
using var systemVersionFile = new UniqueRef<IFile>();
|
||||
|
||||
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CnmtContentMetaEntry metaEntry in metaEntries)
|
||||
{
|
||||
if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry))
|
||||
{
|
||||
metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||
|
||||
string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
||||
|
||||
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
|
||||
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
|
||||
if (contentPath == null)
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath);
|
||||
ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath);
|
||||
|
||||
using Stream metaNcaStream = GetZipStream(metaZipEntry);
|
||||
using Stream contentNcaStream = GetZipStream(contentZipEntry);
|
||||
Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage());
|
||||
|
||||
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
IStorage contentStorage = contentNcaStream.AsStorage();
|
||||
if (contentStorage.GetSize(out long size).IsSuccess())
|
||||
{
|
||||
byte[] contentData = new byte[size];
|
||||
|
||||
Span<byte> content = new(contentData);
|
||||
|
||||
contentStorage.Read(0, content);
|
||||
|
||||
Span<byte> hash = new(new byte[32]);
|
||||
|
||||
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
|
||||
|
||||
if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash))
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.Count > 0)
|
||||
{
|
||||
StringBuilder extraNcas = new();
|
||||
|
||||
foreach (var entry in updateNcas)
|
||||
{
|
||||
foreach (var (type, path) in entry.Value)
|
||||
{
|
||||
extraNcas.AppendLine(path);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}");
|
||||
}
|
||||
return VerifyAndGetVersion(partition);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||
throw new InvalidFirmwarePackageException("Update not found in xci file.");
|
||||
}
|
||||
|
||||
return systemVersion;
|
||||
}
|
||||
|
||||
SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
|
||||
{
|
||||
SystemVersion systemVersion = null;
|
||||
|
||||
CnmtContentMetaEntry[] metaEntries = null;
|
||||
|
||||
foreach (var entry in filesystem.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaStorage);
|
||||
|
||||
if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta)
|
||||
{
|
||||
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
if (meta.Type == ContentMetaType.SystemUpdate)
|
||||
{
|
||||
metaEntries = meta.MetaEntries;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
|
||||
{
|
||||
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
using var systemVersionFile = new UniqueRef<IFile>();
|
||||
|
||||
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
|
||||
{
|
||||
updateNcasItem.Add((nca.Header.ContentType, entry.FullPath));
|
||||
}
|
||||
else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
|
||||
{
|
||||
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullPath));
|
||||
}
|
||||
|
||||
ncaStorage.Dispose();
|
||||
}
|
||||
|
||||
if (metaEntries == null)
|
||||
{
|
||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||
}
|
||||
|
||||
foreach (CnmtContentMetaEntry metaEntry in metaEntries)
|
||||
{
|
||||
if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry))
|
||||
{
|
||||
string metaNcaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||
string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
||||
|
||||
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
|
||||
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
|
||||
if (contentPath == null)
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaPath, OpenMode.Read).AsStorage();
|
||||
IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage();
|
||||
|
||||
Nca metaNca = new(_virtualFileSystem.KeySet, metaStorage);
|
||||
|
||||
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
if (contentStorage.GetSize(out long size).IsSuccess())
|
||||
{
|
||||
byte[] contentData = new byte[size];
|
||||
|
||||
Span<byte> content = new(contentData);
|
||||
|
||||
contentStorage.Read(0, content);
|
||||
|
||||
Span<byte> hash = new(new byte[32]);
|
||||
|
||||
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
|
||||
|
||||
if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash))
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.Count > 0)
|
||||
{
|
||||
StringBuilder extraNcas = new();
|
||||
|
||||
foreach (var entry in updateNcas)
|
||||
{
|
||||
foreach (var (type, path) in entry.Value)
|
||||
{
|
||||
extraNcas.AppendLine(path);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}");
|
||||
}
|
||||
|
||||
return systemVersion;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory)
|
||||
{
|
||||
return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory));
|
||||
}
|
||||
|
||||
public SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
|
||||
{
|
||||
SystemVersion systemVersion = null;
|
||||
|
||||
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
|
||||
{
|
||||
using Stream ncaStream = GetZipStream(entry);
|
||||
IStorage storage = ncaStream.AsStorage();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, storage);
|
||||
|
||||
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
|
||||
{
|
||||
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
|
||||
}
|
||||
else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
|
||||
{
|
||||
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry))
|
||||
{
|
||||
string metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||
|
||||
CnmtContentMetaEntry[] metaEntries = null;
|
||||
|
||||
var fileEntry = archive.GetEntry(metaPath);
|
||||
|
||||
using (Stream ncaStream = GetZipStream(fileEntry))
|
||||
{
|
||||
Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
||||
|
||||
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
if (meta.Type == ContentMetaType.SystemUpdate)
|
||||
{
|
||||
metaEntries = meta.MetaEntries;
|
||||
|
||||
updateNcas.Remove(SystemUpdateTitleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (metaEntries == null)
|
||||
{
|
||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||
}
|
||||
|
||||
if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem))
|
||||
{
|
||||
string versionEntry = updateNcasItem.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
||||
|
||||
using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry));
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
||||
|
||||
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
using var systemVersionFile = new UniqueRef<IFile>();
|
||||
|
||||
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CnmtContentMetaEntry metaEntry in metaEntries)
|
||||
{
|
||||
if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry))
|
||||
{
|
||||
metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||
|
||||
string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
||||
|
||||
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
|
||||
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
|
||||
if (contentPath == null)
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath);
|
||||
ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath);
|
||||
|
||||
using Stream metaNcaStream = GetZipStream(metaZipEntry);
|
||||
using Stream contentNcaStream = GetZipStream(contentZipEntry);
|
||||
Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage());
|
||||
|
||||
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
IStorage contentStorage = contentNcaStream.AsStorage();
|
||||
if (contentStorage.GetSize(out long size).IsSuccess())
|
||||
{
|
||||
byte[] contentData = new byte[size];
|
||||
|
||||
Span<byte> content = new(contentData);
|
||||
|
||||
contentStorage.Read(0, content);
|
||||
|
||||
Span<byte> hash = new(new byte[32]);
|
||||
|
||||
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
|
||||
|
||||
if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash))
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.Count > 0)
|
||||
{
|
||||
StringBuilder extraNcas = new();
|
||||
|
||||
foreach (var entry in updateNcas)
|
||||
{
|
||||
foreach (var (type, path) in entry.Value)
|
||||
{
|
||||
extraNcas.AppendLine(path);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||
}
|
||||
|
||||
return systemVersion;
|
||||
}
|
||||
|
||||
public SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
|
||||
{
|
||||
SystemVersion systemVersion = null;
|
||||
|
||||
CnmtContentMetaEntry[] metaEntries = null;
|
||||
|
||||
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
|
||||
|
||||
foreach (var entry in filesystem.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaStorage);
|
||||
|
||||
if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta)
|
||||
{
|
||||
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
if (meta.Type == ContentMetaType.SystemUpdate)
|
||||
{
|
||||
metaEntries = meta.MetaEntries;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
|
||||
{
|
||||
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
using var systemVersionFile = new UniqueRef<IFile>();
|
||||
|
||||
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
|
||||
{
|
||||
updateNcasItem.Add((nca.Header.ContentType, entry.FullPath));
|
||||
}
|
||||
else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
|
||||
{
|
||||
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullPath));
|
||||
}
|
||||
|
||||
ncaStorage.Dispose();
|
||||
}
|
||||
|
||||
if (metaEntries == null)
|
||||
{
|
||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||
}
|
||||
|
||||
foreach (CnmtContentMetaEntry metaEntry in metaEntries)
|
||||
{
|
||||
if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry))
|
||||
{
|
||||
string metaNcaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||
string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
||||
|
||||
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
|
||||
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
|
||||
if (contentPath == null)
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaPath, OpenMode.Read).AsStorage();
|
||||
IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage();
|
||||
|
||||
Nca metaNca = new(_virtualFileSystem.KeySet, metaStorage);
|
||||
|
||||
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||
|
||||
using var metaFile = new UniqueRef<IFile>();
|
||||
|
||||
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||
{
|
||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||
|
||||
if (contentStorage.GetSize(out long size).IsSuccess())
|
||||
{
|
||||
byte[] contentData = new byte[size];
|
||||
|
||||
Span<byte> content = new(contentData);
|
||||
|
||||
contentStorage.Read(0, content);
|
||||
|
||||
Span<byte> hash = new(new byte[32]);
|
||||
|
||||
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
|
||||
|
||||
if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash))
|
||||
{
|
||||
updateNcas.Remove(metaEntry.TitleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNcas.Count > 0)
|
||||
{
|
||||
StringBuilder extraNcas = new();
|
||||
|
||||
foreach (var entry in updateNcas)
|
||||
{
|
||||
foreach (var (type, path) in entry.Value)
|
||||
{
|
||||
extraNcas.AppendLine(path);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}");
|
||||
}
|
||||
|
||||
return systemVersion;
|
||||
}
|
||||
|
||||
public SystemVersion GetCurrentFirmwareVersion()
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
internal class SoftwareKeyboardApplet : IApplet
|
||||
{
|
||||
private const string DefaultInputText = "Ryujinx";
|
||||
private const string DefaultInputText = "Kenji-NX";
|
||||
|
||||
private const int StandardBufferSize = 0x7D8;
|
||||
private const int InteractiveBufferSize = 0x7D4;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
int ryujinxLogoSize = 32;
|
||||
|
||||
string ryujinxIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Logo_Ryujinx.png";
|
||||
string ryujinxIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Logo_Kenjinx.png";
|
||||
_ryujinxLogo = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, ryujinxIconPath, ryujinxLogoSize, ryujinxLogoSize);
|
||||
|
||||
string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png";
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Ryujinx.HLE.HOS
|
||||
private readonly ICpuContext _cpuContext;
|
||||
private T _memoryManager;
|
||||
|
||||
public ulong ReservedSize { get; }
|
||||
|
||||
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
||||
|
||||
public ulong AddressSpaceSize { get; }
|
||||
@@ -34,7 +36,8 @@ namespace Ryujinx.HLE.HOS
|
||||
GpuContext gpuContext,
|
||||
T memoryManager,
|
||||
ulong addressSpaceSize,
|
||||
bool for64Bit)
|
||||
bool for64Bit,
|
||||
ulong reservedSize = 0UL)
|
||||
{
|
||||
if (memoryManager is IRefCounted rc)
|
||||
{
|
||||
@@ -47,8 +50,8 @@ namespace Ryujinx.HLE.HOS
|
||||
_gpuContext = gpuContext;
|
||||
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit);
|
||||
_memoryManager = memoryManager;
|
||||
|
||||
AddressSpaceSize = addressSpaceSize;
|
||||
ReservedSize = reservedSize;
|
||||
}
|
||||
|
||||
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||
|
||||
@@ -4,6 +4,7 @@ using Ryujinx.Cpu;
|
||||
using Ryujinx.Cpu.AppleHv;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.Cpu.LightningJit;
|
||||
using Ryujinx.Cpu.Nce;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
@@ -46,17 +47,43 @@ namespace Ryujinx.HLE.HOS
|
||||
_codeSize = codeSize;
|
||||
}
|
||||
|
||||
public static NceCpuCodePatch CreateCodePatchForNce(KernelContext context, bool for64Bit, ReadOnlySpan<byte> textSection)
|
||||
{
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && for64Bit && context.Device.Configuration.UseHypervisor && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
return NcePatcher.CreatePatch(textSection);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
||||
{
|
||||
IArmProcessContext processContext;
|
||||
|
||||
bool isArm64Host = RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
||||
|
||||
if (OperatingSystem.IsMacOS() && isArm64Host && for64Bit && context.Device.Configuration.UseHypervisor)
|
||||
if (isArm64Host && for64Bit && context.Device.Configuration.UseHypervisor)
|
||||
{
|
||||
var cpuEngine = new HvEngine(_tickSource);
|
||||
var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
var cpuEngine = new HvEngine(_tickSource);
|
||||
var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AddressSpace.TryCreateWithoutMirror(addressSpaceSize, out var addressSpace))
|
||||
{
|
||||
throw new Exception("Address space creation failed");
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Cpu, $"NCE Base AS Address: 0x{addressSpace.Pointer.ToInt64():X} Size: 0x{addressSpace.Size:X}");
|
||||
|
||||
var cpuEngine = new NceEngine(_tickSource);
|
||||
var memoryManager = new MemoryManagerNative(addressSpace, context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerNative>(pid, cpuEngine, _gpu, memoryManager, addressSpace.Size, for64Bit, memoryManager.ReservedSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -74,11 +101,13 @@ namespace Ryujinx.HLE.HOS
|
||||
: new JitEngine(_tickSource);
|
||||
|
||||
AddressSpace addressSpace = null;
|
||||
MemoryBlock asNoMirror = null;
|
||||
|
||||
// We want to use host tracked mode if the host page size is > 4KB.
|
||||
if ((mode == MemoryManagerMode.HostMapped || mode == MemoryManagerMode.HostMappedUnsafe) && MemoryBlock.GetPageSize() <= 0x1000)
|
||||
{
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace))
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace) &&
|
||||
!AddressSpace.TryCreateWithoutMirror(addressSpaceSize, out asNoMirror))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Cpu, "Address space creation failed, falling back to software page table");
|
||||
|
||||
@@ -89,35 +118,47 @@ namespace Ryujinx.HLE.HOS
|
||||
switch (mode)
|
||||
{
|
||||
case MemoryManagerMode.SoftwarePageTable:
|
||||
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
|
||||
{
|
||||
var mm = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, mm, addressSpaceSize, for64Bit);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryManagerMode.HostMapped:
|
||||
case MemoryManagerMode.HostMappedUnsafe:
|
||||
if (addressSpace == null)
|
||||
if (addressSpace == null && asNoMirror == null)
|
||||
{
|
||||
var memoryManagerHostTracked = new MemoryManagerHostTracked(context.Memory, addressSpaceSize, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostTracked>(pid, cpuEngine, _gpu, memoryManagerHostTracked, addressSpaceSize, for64Bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addressSpaceSize != addressSpace.AddressSpaceSize)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{addressSpace.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})");
|
||||
}
|
||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
||||
|
||||
var memoryManagerHostMapped = new MemoryManagerHostMapped(addressSpace, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, memoryManagerHostMapped, addressSpace.AddressSpaceSize, for64Bit);
|
||||
if (addressSpace != null)
|
||||
{
|
||||
var mm = new MemoryManagerHostMapped(addressSpace, unsafeMode, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, mm, addressSpace.AddressSpaceSize, for64Bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
var mm = new MemoryManagerHostNoMirror(asNoMirror, context.Memory, unsafeMode, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostNoMirror>(pid, cpuEngine, _gpu, mm, asNoMirror.Size, for64Bit);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"{nameof(mode)} contains an invalid value: {mode}");
|
||||
}
|
||||
|
||||
if (addressSpaceSize != processContext.AddressSpaceSize)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{processContext.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})");
|
||||
}
|
||||
}
|
||||
|
||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, _diskCacheSelector ?? "default");
|
||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, _diskCacheSelector);
|
||||
|
||||
return processContext;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
return arrange switch
|
||||
{
|
||||
MemoryArrange.MemoryArrange4GiB => 3455 * MiB,
|
||||
MemoryArrange.MemoryArrange4GiB or
|
||||
MemoryArrange.MemoryArrange4GiBSystemDev or
|
||||
MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB,
|
||||
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
|
||||
|
||||
@@ -36,8 +36,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
protected readonly KernelContext Context;
|
||||
protected virtual bool UsesPrivateAllocations => false;
|
||||
|
||||
public ulong AddrSpaceStart { get; private set; }
|
||||
public ulong AddrSpaceEnd { get; private set; }
|
||||
public ulong AddressSpaceStart { get; private set; }
|
||||
public ulong AddressSpaceEnd { get; private set; }
|
||||
|
||||
public ulong CodeRegionStart { get; private set; }
|
||||
public ulong CodeRegionEnd { get; private set; }
|
||||
@@ -69,6 +69,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
private MemoryRegion _memRegion;
|
||||
|
||||
private bool _allocateFromBack;
|
||||
|
||||
private readonly bool _isKernel;
|
||||
|
||||
private bool _aslrEnabled;
|
||||
@@ -104,6 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryRegion memRegion,
|
||||
ulong address,
|
||||
ulong size,
|
||||
ulong reservedSize,
|
||||
KMemoryBlockSlabManager slabManager)
|
||||
{
|
||||
_contextId = Context.ContextIdManager.GetId();
|
||||
@@ -119,6 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
memRegion,
|
||||
address,
|
||||
size,
|
||||
reservedSize,
|
||||
slabManager);
|
||||
|
||||
if (result != Result.Success)
|
||||
@@ -161,6 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryRegion memRegion,
|
||||
ulong address,
|
||||
ulong size,
|
||||
ulong reservedSize,
|
||||
KMemoryBlockSlabManager slabManager)
|
||||
{
|
||||
ulong endAddr = address + size;
|
||||
@@ -186,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
CodeRegionStart = 0x200000;
|
||||
codeRegionSize = 0x3fe00000;
|
||||
AslrRegionStart = 0x200000;
|
||||
AslrRegionEnd = AslrRegionStart + 0xffe00000;
|
||||
AslrRegionEnd = 0x100000000;
|
||||
stackAndTlsIoStart = 0x200000;
|
||||
stackAndTlsIoEnd = 0x40000000;
|
||||
break;
|
||||
@@ -199,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
CodeRegionStart = 0x8000000;
|
||||
codeRegionSize = 0x78000000;
|
||||
AslrRegionStart = 0x8000000;
|
||||
AslrRegionEnd = AslrRegionStart + 0xff8000000;
|
||||
AslrRegionEnd = 0x1000000000;
|
||||
stackAndTlsIoStart = 0x8000000;
|
||||
stackAndTlsIoEnd = 0x80000000;
|
||||
break;
|
||||
@@ -212,17 +216,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
CodeRegionStart = 0x200000;
|
||||
codeRegionSize = 0x3fe00000;
|
||||
AslrRegionStart = 0x200000;
|
||||
AslrRegionEnd = AslrRegionStart + 0xffe00000;
|
||||
AslrRegionEnd = 0x100000000;
|
||||
stackAndTlsIoStart = 0x200000;
|
||||
stackAndTlsIoEnd = 0x40000000;
|
||||
break;
|
||||
|
||||
case ProcessCreationFlags.AddressSpace64Bit:
|
||||
ulong reservedAddressSpaceSize = _reservedAddressSpaceSize;
|
||||
if (_reservedAddressSpaceSize < addrSpaceEnd)
|
||||
{
|
||||
int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize);
|
||||
int addressSpaceWidth = (int)ulong.Log2(reservedAddressSpaceSize);
|
||||
|
||||
aliasRegion.Size = 1UL << (addressSpaceWidth - 3);
|
||||
aliasRegion.Size = reservedAddressSpaceSize >= 0x1800000000 ? 0x1000000000 : 1UL << (addressSpaceWidth - 3);
|
||||
heapRegion.Size = 0x180000000;
|
||||
stackRegion.Size = 1UL << (addressSpaceWidth - 8);
|
||||
tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3);
|
||||
@@ -230,8 +235,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart;
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
AslrRegionStart = 0x8000000;
|
||||
addrSpaceEnd = 1UL << addressSpaceWidth;
|
||||
AslrRegionStart = Math.Max(reservedSize, 0x8000000);
|
||||
addrSpaceEnd = reservedSize + reservedAddressSpaceSize;
|
||||
AslrRegionEnd = addrSpaceEnd;
|
||||
}
|
||||
else
|
||||
@@ -242,8 +247,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
tlsIoRegion.Size = 0x1000000000;
|
||||
CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment);
|
||||
codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart;
|
||||
AslrRegionStart = 0x8000000;
|
||||
AslrRegionEnd = AslrRegionStart + 0x7ff8000000;
|
||||
AslrRegionStart = Math.Max(reservedSize, 0x8000000);
|
||||
AslrRegionEnd = 0x8000000000;
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
}
|
||||
@@ -285,8 +290,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
_aslrEnabled = aslrEnabled;
|
||||
|
||||
AddrSpaceStart = addrSpaceStart;
|
||||
AddrSpaceEnd = addrSpaceEnd;
|
||||
AddressSpaceStart = addrSpaceStart;
|
||||
AddressSpaceEnd = addrSpaceEnd;
|
||||
|
||||
_slabManager = slabManager;
|
||||
|
||||
@@ -309,10 +314,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
aliasRegion.End = aliasRegion.Start + aliasRegion.Size;
|
||||
heapRegion.Start = mapBaseAddress + heapRegion.AslrOffset;
|
||||
heapRegion.End = heapRegion.Start + heapRegion.Size;
|
||||
stackRegion.Start = mapBaseAddress + stackRegion.AslrOffset;
|
||||
stackRegion.End = stackRegion.Start + stackRegion.Size;
|
||||
tlsIoRegion.Start = mapBaseAddress + tlsIoRegion.AslrOffset;
|
||||
tlsIoRegion.End = tlsIoRegion.Start + tlsIoRegion.Size;
|
||||
|
||||
SortRegion(ref aliasRegion, ref heapRegion, true);
|
||||
|
||||
@@ -439,9 +440,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
ulong endAddr = address + size;
|
||||
|
||||
ulong addrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
|
||||
ulong addrSpacePagesCount = (AddressSpaceEnd - AddressSpaceStart) / PageSize;
|
||||
|
||||
if (AddrSpaceStart > address)
|
||||
if (AddressSpaceStart > address)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
@@ -451,7 +452,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
|
||||
if (endAddr - 1 > AddrSpaceEnd - 1)
|
||||
if (endAddr - 1 > AddressSpaceEnd - 1)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
@@ -984,8 +985,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
public KMemoryInfo QueryMemory(ulong address)
|
||||
{
|
||||
if (address >= AddrSpaceStart &&
|
||||
address < AddrSpaceEnd)
|
||||
if (address >= AddressSpaceStart &&
|
||||
address < AddressSpaceEnd)
|
||||
{
|
||||
lock (_blockManager)
|
||||
{
|
||||
@@ -995,8 +996,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
else
|
||||
{
|
||||
return new KMemoryInfo(
|
||||
AddrSpaceEnd,
|
||||
~AddrSpaceEnd + 1,
|
||||
AddressSpaceEnd,
|
||||
~AddressSpaceEnd + 1,
|
||||
MemoryState.Reserved,
|
||||
KMemoryPermission.None,
|
||||
MemoryAttribute.None,
|
||||
@@ -1558,14 +1559,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryAttribute attributeExpected,
|
||||
bool toServer)
|
||||
{
|
||||
if (AddrSpaceStart > clientAddress)
|
||||
if (AddressSpaceStart > clientAddress)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
|
||||
ulong srcEndAddr = clientAddress + size;
|
||||
|
||||
if (srcEndAddr <= clientAddress || srcEndAddr - 1 > AddrSpaceEnd - 1)
|
||||
if (srcEndAddr <= clientAddress || srcEndAddr - 1 > AddressSpaceEnd - 1)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
@@ -1689,14 +1690,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
blocksNeeded = 0;
|
||||
|
||||
if (AddrSpaceStart > address)
|
||||
if (AddressSpaceStart > address)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
|
||||
ulong endAddr = address + size;
|
||||
|
||||
if (endAddr <= address || endAddr - 1 > AddrSpaceEnd - 1)
|
||||
if (endAddr <= address || endAddr - 1 > AddressSpaceEnd - 1)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
@@ -2029,14 +2030,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
public Result UnmapNoAttributeIfStateEquals(ulong address, ulong size, MemoryState state)
|
||||
{
|
||||
if (AddrSpaceStart > address)
|
||||
if (AddressSpaceStart > address)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
|
||||
ulong endAddr = address + size;
|
||||
|
||||
if (endAddr <= address || endAddr - 1 > AddrSpaceEnd - 1)
|
||||
if (endAddr <= address || endAddr - 1 > AddressSpaceEnd - 1)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
@@ -2810,7 +2811,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
case MemoryState.ProcessMemory:
|
||||
case MemoryState.CodeReadOnly:
|
||||
case MemoryState.CodeWritable:
|
||||
return GetAddrSpaceBaseAddr();
|
||||
return AslrRegionStart;
|
||||
|
||||
case MemoryState.Heap:
|
||||
return HeapRegionStart;
|
||||
@@ -2824,7 +2825,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
return StackRegionStart;
|
||||
|
||||
case MemoryState.KernelStack:
|
||||
return AddrSpaceStart;
|
||||
return AddressSpaceStart;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid state value \"{state}\".");
|
||||
@@ -2849,7 +2850,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
case MemoryState.ProcessMemory:
|
||||
case MemoryState.CodeReadOnly:
|
||||
case MemoryState.CodeWritable:
|
||||
return GetAddrSpaceSize();
|
||||
return AslrRegionEnd - AslrRegionStart;
|
||||
|
||||
case MemoryState.Heap:
|
||||
return HeapRegionEnd - HeapRegionStart;
|
||||
@@ -2863,22 +2864,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
return StackRegionEnd - StackRegionStart;
|
||||
|
||||
case MemoryState.KernelStack:
|
||||
return AddrSpaceEnd - AddrSpaceStart;
|
||||
return AddressSpaceEnd - AddressSpaceStart;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid state value \"{state}\".");
|
||||
}
|
||||
|
||||
public ulong GetAddrSpaceBaseAddr()
|
||||
{
|
||||
return AslrRegionStart;
|
||||
}
|
||||
|
||||
public ulong GetAddrSpaceSize()
|
||||
{
|
||||
return AslrRegionEnd - AslrRegionStart;
|
||||
}
|
||||
|
||||
private static ulong GetDramAddressFromPa(ulong pa)
|
||||
{
|
||||
return pa - DramMemoryMap.DramBase;
|
||||
@@ -2904,12 +2895,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
public bool IsInvalidRegion(ulong address, ulong size)
|
||||
{
|
||||
return address + size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
|
||||
return address + size - 1 > AslrRegionEnd - 1;
|
||||
}
|
||||
|
||||
public bool InsideAddrSpace(ulong address, ulong size)
|
||||
{
|
||||
return AddrSpaceStart <= address && address + size - 1 <= AddrSpaceEnd - 1;
|
||||
return AddressSpaceStart <= address && address + size - 1 <= AddressSpaceEnd - 1;
|
||||
}
|
||||
|
||||
public bool InsideAliasRegion(ulong address, ulong size)
|
||||
@@ -2934,7 +2925,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
public bool OutsideAddrSpace(ulong address, ulong size)
|
||||
{
|
||||
return AddrSpaceStart > address || address + size - 1 > AddrSpaceEnd - 1;
|
||||
return AddressSpaceStart > address || address + size - 1 > AddressSpaceEnd - 1;
|
||||
}
|
||||
|
||||
public bool OutsideStackRegion(ulong address, ulong size)
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
interface IProcessContext : IDisposable
|
||||
{
|
||||
IVirtualMemoryManager AddressSpace { get; }
|
||||
ulong ReservedSize { get; }
|
||||
|
||||
ulong AddressSpaceSize { get; }
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
// TODO: Remove once we no longer need to initialize it externally.
|
||||
HandleTable = new KHandleTable();
|
||||
|
||||
_threads = [];
|
||||
_threads = new LinkedList<KThread>();
|
||||
|
||||
Debugger = new HleProcessDebugger(this);
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
InitializeMemoryManager(creationInfo.Flags);
|
||||
|
||||
ulong codeAddress = creationInfo.CodeAddress;
|
||||
ulong codeAddress = creationInfo.CodeAddress + Context.ReservedSize;
|
||||
|
||||
ulong codeSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
@@ -149,6 +149,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
memRegion,
|
||||
codeAddress,
|
||||
codeSize,
|
||||
Context.ReservedSize,
|
||||
slabManager);
|
||||
|
||||
if (result != Result.Success)
|
||||
@@ -184,7 +185,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
KResourceLimit resourceLimit,
|
||||
MemoryRegion memRegion,
|
||||
IProcessContextFactory contextFactory,
|
||||
ThreadStart customThreadStart = null)
|
||||
ThreadStart customThreadStart = null,
|
||||
ulong entrypointOffset = 0UL)
|
||||
{
|
||||
ResourceLimit = resourceLimit;
|
||||
_memRegion = memRegion;
|
||||
@@ -238,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
InitializeMemoryManager(creationInfo.Flags);
|
||||
|
||||
ulong codeAddress = creationInfo.CodeAddress;
|
||||
ulong codeAddress = creationInfo.CodeAddress + Context.ReservedSize;
|
||||
|
||||
ulong codeSize = codePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
@@ -248,6 +250,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
memRegion,
|
||||
codeAddress,
|
||||
codeSize,
|
||||
Context.ReservedSize,
|
||||
slabManager);
|
||||
|
||||
if (result != Result.Success)
|
||||
@@ -293,6 +296,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
CleanUpForError();
|
||||
}
|
||||
|
||||
_entrypoint += entrypointOffset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -337,10 +342,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
Flags = creationInfo.Flags;
|
||||
TitleId = creationInfo.TitleId;
|
||||
_entrypoint = creationInfo.CodeAddress;
|
||||
_entrypoint = creationInfo.CodeAddress + Context.ReservedSize;
|
||||
_imageSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
switch (Flags & ProcessCreationFlags.AddressSpaceMask)
|
||||
// 19.0.0+ sets all regions to same size
|
||||
_memoryUsageCapacity = MemoryManager.HeapRegionEnd - MemoryManager.HeapRegionStart;
|
||||
/*switch (Flags & ProcessCreationFlags.AddressSpaceMask)
|
||||
{
|
||||
case ProcessCreationFlags.AddressSpace32Bit:
|
||||
case ProcessCreationFlags.AddressSpace64BitDeprecated:
|
||||
@@ -357,7 +364,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid MMU flags value 0x{Flags:x2}.");
|
||||
}
|
||||
}*/
|
||||
|
||||
GenerateRandomEntropy();
|
||||
|
||||
@@ -535,7 +542,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
throw new InvalidOperationException("Trying to start a process with an invalid state!");
|
||||
}
|
||||
|
||||
// TODO: after 19.0.0+ alignment is not needed
|
||||
ulong stackSizeRounded = BitUtils.AlignUp<ulong>(stackSize, KPageTableBase.PageSize);
|
||||
|
||||
ulong neededSize = stackSizeRounded + _imageSize;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
class ProcessContext : IProcessContext
|
||||
{
|
||||
public IVirtualMemoryManager AddressSpace { get; }
|
||||
public ulong ReservedSize => 0UL;
|
||||
|
||||
public ulong AddressSpaceSize { get; }
|
||||
|
||||
|
||||
@@ -1988,10 +1988,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
break;
|
||||
|
||||
case InfoType.AslrRegionAddress:
|
||||
value = process.MemoryManager.GetAddrSpaceBaseAddr();
|
||||
value = process.MemoryManager.AslrRegionStart;
|
||||
break;
|
||||
case InfoType.AslrRegionSize:
|
||||
value = process.MemoryManager.GetAddrSpaceSize();
|
||||
value = process.MemoryManager.AslrRegionEnd - process.MemoryManager.AslrRegionStart;
|
||||
break;
|
||||
|
||||
case InfoType.StackRegionAddress:
|
||||
@@ -2683,7 +2683,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
|
||||
if ((uint)preferredCore > KScheduler.CpuCoresCount - 1)
|
||||
if ((uint)preferredCore > 3)
|
||||
{
|
||||
if ((preferredCore | 2) != -1)
|
||||
{
|
||||
@@ -2813,7 +2813,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
{
|
||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||
|
||||
if (currentProcess.MemoryManager.AddrSpaceStart > handlesPtr)
|
||||
if (currentProcess.MemoryManager.AddressSpaceStart > handlesPtr)
|
||||
{
|
||||
return KernelResult.UserCopyFailed;
|
||||
}
|
||||
@@ -2825,7 +2825,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
return KernelResult.UserCopyFailed;
|
||||
}
|
||||
|
||||
if (handlesPtr + (ulong)handlesSize - 1 > currentProcess.MemoryManager.AddrSpaceEnd - 1)
|
||||
if (handlesPtr + (ulong)handlesSize - 1 > currentProcess.MemoryManager.AddressSpaceEnd - 1)
|
||||
{
|
||||
return KernelResult.UserCopyFailed;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
{
|
||||
byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg");
|
||||
|
||||
AddUser("RyuPlayer", defaultUserImage, DefaultUserId);
|
||||
AddUser("KenPlayer", defaultUserImage, DefaultUserId);
|
||||
|
||||
OpenUser(DefaultUserId);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
|
||||
{
|
||||
try
|
||||
{
|
||||
_portMapping = new Mapping(Protocol.Tcp, PrivatePort, _publicPort, PortLeaseLength, "Ryujinx Local Multiplayer");
|
||||
_portMapping = new Mapping(Protocol.Tcp, PrivatePort, _publicPort, PortLeaseLength, "Kenji-NX Local Multiplayer");
|
||||
|
||||
await device.CreatePortMapAsync(_portMapping);
|
||||
|
||||
@@ -364,7 +364,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
|
||||
|
||||
try
|
||||
{
|
||||
Task delete = _natDevice?.DeletePortMapAsync(new Mapping(Protocol.Tcp, PrivatePort, _publicPort, 60, "Ryujinx Local Multiplayer"));
|
||||
Task delete = _natDevice?.DeletePortMapAsync(new Mapping(Protocol.Tcp, PrivatePort, _publicPort, 60, "Kenji-NX Local Multiplayer"));
|
||||
|
||||
// Just absorb any exceptions.
|
||||
delete?.ContinueWith((task) => { });
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
public static RegisterInfo GetRegisterInfo(ITickSource tickSource, string amiiboId, string userName)
|
||||
{
|
||||
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
|
||||
string nickname = amiiboFile.NickName ?? "Ryujinx";
|
||||
string nickname = amiiboFile.NickName ?? "Kenji-NX";
|
||||
if (NickName != string.Empty)
|
||||
{
|
||||
nickname = NickName;
|
||||
|
||||
@@ -25,7 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request?
|
||||
};
|
||||
|
||||
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
||||
if (!Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
|
||||
}
|
||||
|
||||
GeneralServiceManager.Add(_generalServiceDetail);
|
||||
}
|
||||
@@ -84,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
|
||||
networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties);
|
||||
|
||||
"RyujinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan());
|
||||
"KenjinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan());
|
||||
|
||||
context.Memory.Write(networkProfileDataPosition, networkProfile);
|
||||
|
||||
@@ -196,7 +199,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||
if (!Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||
}
|
||||
|
||||
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Pm.Types;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Pm
|
||||
{
|
||||
[Service("pm:bm")]
|
||||
class IBootModeInterface : IpcService
|
||||
{
|
||||
public IBootModeInterface(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetBootMode() -> u32
|
||||
public ResultCode GetBootMode(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((uint)BootMode.Normal);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Pm.Types
|
||||
{
|
||||
enum BootMode : uint
|
||||
{
|
||||
Normal = 0,
|
||||
Maintenance = 1,
|
||||
SafeMode = 2,
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
||||
|
||||
int retryCount;
|
||||
|
||||
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - size) >> 12;
|
||||
ulong addressSpacePageLimit = ((memMgr.AslrRegionEnd - memMgr.AslrRegionStart) - size) >> 12;
|
||||
|
||||
for (retryCount = 0; retryCount < MaxMapRetries; retryCount++)
|
||||
{
|
||||
@@ -278,7 +278,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
||||
{
|
||||
ulong randomOffset = (ulong)(uint)Random.Shared.Next(0, (int)addressSpacePageLimit) << 12;
|
||||
|
||||
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
|
||||
targetAddress = memMgr.AslrRegionStart + randomOffset;
|
||||
|
||||
if (memMgr.InsideAddrSpace(targetAddress, size) && !memMgr.InsideHeapRegion(targetAddress, size) && !memMgr.InsideAliasRegion(targetAddress, size))
|
||||
{
|
||||
|
||||
@@ -244,6 +244,15 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(68)]
|
||||
// GetSerialNumber() -> buffer<nn::settings::system::SerialNumber, 0x16>
|
||||
public ResultCode GetSerialNumber(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(Encoding.ASCII.GetBytes("RYU00000000000"));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(77)]
|
||||
// GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16>
|
||||
public ResultCode GetDeviceNickName(ServiceCtx context)
|
||||
|
||||
@@ -51,6 +51,14 @@ namespace Ryujinx.HLE.HOS.Services.Spl
|
||||
|
||||
context.ResponseData.Write(configValue);
|
||||
|
||||
if(Ryujinx.Common.PlatformInfo.IsBionic)
|
||||
{
|
||||
if (result == SmcResult.Success)
|
||||
{
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return (ResultCode)((int)result << 9) | ResultCode.ModuleId;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||
{
|
||||
// TODO: Let user specify fields.
|
||||
DesiredKeyboardLayout = (long)KeyboardLayout.Default;
|
||||
DeviceNickName = "Ryujinx's Switch";
|
||||
DeviceNickName = "Kenji-NX's Switch";
|
||||
}
|
||||
|
||||
public void SetLanguage(SystemLanguage language)
|
||||
|
||||
@@ -82,6 +82,13 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
// Apply Nsos patches.
|
||||
device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables);
|
||||
|
||||
// Don't use PTC if ExeFS files have been replaced.
|
||||
bool enablePtc = device.System.EnablePtc && !modLoadResult.Modified;
|
||||
if (!enablePtc)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
|
||||
}
|
||||
|
||||
string programName = "";
|
||||
|
||||
if (!isHomebrew && programId > 0x010000000000FFFF)
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
return programs;
|
||||
}
|
||||
|
||||
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, ulong applicationId, out string errorMessage)
|
||||
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, Stream stream, ulong applicationId, out string errorMessage)
|
||||
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
|
||||
where TFormat : IPartitionFileSystemFormat
|
||||
where THeader : unmanaged, IPartitionFileSystemHeader
|
||||
|
||||
@@ -35,6 +35,12 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
public bool LoadXci(string path, ulong applicationId)
|
||||
{
|
||||
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||
|
||||
return LoadXci(stream, applicationId);
|
||||
}
|
||||
|
||||
public bool LoadXci(Stream stream, ulong applicationId, Stream updateStream = null)
|
||||
{
|
||||
Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
|
||||
|
||||
if (!xci.HasPartition(XciPartitionType.Secure))
|
||||
@@ -44,7 +50,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage);
|
||||
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, stream, applicationId, out string errorMessage);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
@@ -69,10 +75,16 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
public bool LoadNsp(string path, ulong applicationId)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
PartitionFileSystem partitionFileSystem = new();
|
||||
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||
|
||||
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage);
|
||||
return LoadNsp(file, applicationId);
|
||||
}
|
||||
|
||||
public bool LoadNsp(Stream stream, ulong applicationId, Stream updateStream = null)
|
||||
{
|
||||
PartitionFileSystem partitionFileSystem = new();
|
||||
partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure();
|
||||
|
||||
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, applicationId, out string errorMessage);
|
||||
|
||||
if (processResult.ProcessId == 0)
|
||||
{
|
||||
@@ -101,7 +113,13 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
public bool LoadNca(string path)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
return LoadNca(file);
|
||||
}
|
||||
|
||||
public bool LoadNca(Stream ncaStream)
|
||||
{
|
||||
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, ncaStream.AsStorage(false));
|
||||
|
||||
ProcessResult processResult = nca.Load(_device, null, null);
|
||||
|
||||
@@ -243,5 +261,109 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNxo(Stream stream, bool isNro, string name)
|
||||
{
|
||||
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
||||
IFileSystem dummyExeFs = null;
|
||||
Stream romfsStream = null;
|
||||
|
||||
string programName = "";
|
||||
ulong programId = 0000000000000000;
|
||||
|
||||
// Load executable.
|
||||
IExecutable executable;
|
||||
|
||||
if (isNro)
|
||||
{
|
||||
NroExecutable nro = new(stream.AsStorage());
|
||||
|
||||
executable = nro;
|
||||
|
||||
// Open RomFS if exists.
|
||||
IStorage romFsStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.RomFs, false);
|
||||
romFsStorage.GetSize(out long romFsSize).ThrowIfFailure();
|
||||
if (romFsSize != 0)
|
||||
{
|
||||
romfsStream = romFsStorage.AsStream();
|
||||
}
|
||||
|
||||
// Load Nacp if exists.
|
||||
IStorage nacpStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.Nacp, false);
|
||||
nacpStorage.GetSize(out long nacpSize).ThrowIfFailure();
|
||||
if (nacpSize != 0)
|
||||
{
|
||||
nacpStorage.Read(0, nacpData.ByteSpan);
|
||||
|
||||
programName = nacpData.Value.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(programName))
|
||||
{
|
||||
programName = Array.Find(nacpData.Value.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString();
|
||||
}
|
||||
|
||||
if (nacpData.Value.PresenceGroupId != 0)
|
||||
{
|
||||
programId = nacpData.Value.PresenceGroupId;
|
||||
}
|
||||
else if (nacpData.Value.SaveDataOwnerId != 0)
|
||||
{
|
||||
programId = nacpData.Value.SaveDataOwnerId;
|
||||
}
|
||||
else if (nacpData.Value.AddOnContentBaseId != 0)
|
||||
{
|
||||
programId = nacpData.Value.AddOnContentBaseId - 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add icon maybe ?
|
||||
}
|
||||
else
|
||||
{
|
||||
executable = new NsoExecutable(new LocalStorage(name, FileAccess.Read), programName);
|
||||
}
|
||||
|
||||
// Explicitly null TitleId to disable the shader cache.
|
||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
||||
_device.Gpu.HostInitalized.Set();
|
||||
|
||||
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
|
||||
_device.System.KernelContext,
|
||||
dummyExeFs.GetNpdm(),
|
||||
nacpData,
|
||||
diskCacheEnabled: false,
|
||||
diskCacheSelector: null,
|
||||
allowCodeMemoryForJit: true,
|
||||
programName,
|
||||
programId,
|
||||
0,
|
||||
null,
|
||||
executable);
|
||||
|
||||
// Make sure the process id is valid.
|
||||
if (processResult.ProcessId != 0)
|
||||
{
|
||||
// Load RomFS.
|
||||
if (romfsStream != null)
|
||||
{
|
||||
_device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romfsStream);
|
||||
}
|
||||
|
||||
// Start process.
|
||||
if (_processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
if (processResult.Start(_device))
|
||||
{
|
||||
_latestPid = processResult.ProcessId;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using LibHac.Account;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
@@ -11,6 +11,7 @@ using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu.Nce;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
@@ -21,6 +22,7 @@ using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Arp;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
||||
|
||||
@@ -232,7 +234,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
ulong programId,
|
||||
byte programIndex,
|
||||
byte[] arguments = null,
|
||||
params ReadOnlySpan<IExecutable> executables)
|
||||
params IExecutable[] executables)
|
||||
{
|
||||
context.Device.System.ServiceTable.WaitServicesReady();
|
||||
|
||||
@@ -250,20 +252,16 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
ulong argsStart = 0;
|
||||
uint argsSize = 0;
|
||||
ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset;
|
||||
uint codeSize = 0;
|
||||
ulong codeSize = 0;
|
||||
|
||||
var buildIds = new string[executables.Length];
|
||||
|
||||
for (int i = 0; i < executables.Length; i++)
|
||||
var buildIds = executables.Select(e => (e switch
|
||||
{
|
||||
buildIds[i] = (executables[i] switch
|
||||
{
|
||||
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()),
|
||||
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId),
|
||||
_ => "",
|
||||
}).ToUpper();
|
||||
}
|
||||
}).ToUpper());
|
||||
|
||||
NceCpuCodePatch[] nsoPatch = new NceCpuCodePatch[executables.Length];
|
||||
ulong[] nsoBase = new ulong[executables.Length];
|
||||
|
||||
for (int index = 0; index < executables.Length; index++)
|
||||
@@ -288,6 +286,16 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
|
||||
|
||||
bool for64Bit = ((ProcessCreationFlags)meta.Flags).HasFlag(ProcessCreationFlags.Is64Bit);
|
||||
|
||||
NceCpuCodePatch codePatch = ArmProcessContextFactory.CreateCodePatchForNce(context, for64Bit, nso.Text);
|
||||
nsoPatch[index] = codePatch;
|
||||
|
||||
if (codePatch != null)
|
||||
{
|
||||
codeSize += codePatch.Size;
|
||||
}
|
||||
|
||||
nsoBase[index] = codeStart + codeSize;
|
||||
|
||||
codeSize += nsoSize;
|
||||
@@ -389,7 +397,8 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
|
||||
resourceLimit,
|
||||
memoryRegion,
|
||||
processContextFactory);
|
||||
processContextFactory,
|
||||
entrypointOffset: nsoPatch[0]?.Size ?? 0UL);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
@@ -400,9 +409,12 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
for (int index = 0; index < executables.Length; index++)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}...");
|
||||
ulong nsoBaseAddress = process.Context.ReservedSize + nsoBase[index];
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBaseAddress:x16}...");
|
||||
|
||||
result = LoadIntoMemory(process, executables[index], nsoBaseAddress, nsoPatch[index]);
|
||||
|
||||
result = LoadIntoMemory(process, executables[index], nsoBase[index]);
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
|
||||
@@ -460,7 +472,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return processResult;
|
||||
}
|
||||
|
||||
public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)
|
||||
private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress, NceCpuCodePatch codePatch = null)
|
||||
{
|
||||
ulong textStart = baseAddress + image.TextOffset;
|
||||
ulong roStart = baseAddress + image.RoOffset;
|
||||
@@ -480,6 +492,11 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
process.CpuMemory.Fill(bssStart, image.BssSize, 0);
|
||||
|
||||
if (codePatch != null)
|
||||
{
|
||||
codePatch.Write(process.CpuMemory, baseAddress - codePatch.Size, textStart);
|
||||
}
|
||||
|
||||
Result SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (size == 0)
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Homebrew.npdm" />
|
||||
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
|
||||
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Logo_Kenjinx.png" />
|
||||
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" />
|
||||
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnB.png" />
|
||||
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_KeyF6.png" />
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Homebrew.npdm" />
|
||||
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
|
||||
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Logo_Kenjinx.png" />
|
||||
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" />
|
||||
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnB.png" />
|
||||
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_KeyF6.png" />
|
||||
|
||||
@@ -10,6 +10,7 @@ using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
@@ -101,6 +102,26 @@ namespace Ryujinx.HLE
|
||||
return Processes.LoadNxo(fileName);
|
||||
}
|
||||
|
||||
public bool LoadXci(Stream xciStream, ulong applicationId = 0, Stream updateStream = null)
|
||||
{
|
||||
return Processes.LoadXci(xciStream, applicationId, updateStream);
|
||||
}
|
||||
|
||||
public bool LoadNca(Stream ncaStream)
|
||||
{
|
||||
return Processes.LoadNca(ncaStream);
|
||||
}
|
||||
|
||||
public bool LoadNsp(Stream nspStream, ulong applicationId = 0, Stream updateStream = null)
|
||||
{
|
||||
return Processes.LoadNsp(nspStream, applicationId, updateStream);
|
||||
}
|
||||
|
||||
public bool LoadProgram(Stream stream, bool isNro, string name)
|
||||
{
|
||||
return Processes.LoadNxo(stream, isNro, name);
|
||||
}
|
||||
|
||||
public bool WaitFifo()
|
||||
{
|
||||
return Gpu.GPFifo.WaitForCommands();
|
||||
|
||||
@@ -14,16 +14,21 @@ namespace Ryujinx.HLE.Utilities
|
||||
{
|
||||
FileStream file = File.OpenRead(path);
|
||||
|
||||
return OpenApplicationFileSystem(file, Path.GetExtension(path).ToLower() == ".xci", fileSystem, throwOnFailure);
|
||||
}
|
||||
|
||||
public static IFileSystem OpenApplicationFileSystem(Stream stream, bool isXci, VirtualFileSystem fileSystem, bool throwOnFailure = true)
|
||||
{
|
||||
IFileSystem partitionFileSystem;
|
||||
|
||||
if (Path.GetExtension(path).ToLower() == ".xci")
|
||||
if (isXci)
|
||||
{
|
||||
partitionFileSystem = new Xci(fileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
||||
partitionFileSystem = new Xci(fileSystem.KeySet, stream.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
||||
}
|
||||
else
|
||||
{
|
||||
var pfsTemp = new PartitionFileSystem();
|
||||
Result initResult = pfsTemp.Initialize(file.AsStorage());
|
||||
Result initResult = pfsTemp.Initialize(stream.AsStorage());
|
||||
|
||||
if (throwOnFailure)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user