diff options
Diffstat (limited to 'Ryujinx.HLE/HOS')
18 files changed, 53 insertions, 1395 deletions
diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs deleted file mode 100644 index 82bd9b31..00000000 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ /dev/null @@ -1,908 +0,0 @@ -using LibHac; -using LibHac.Account; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Fs.Shim; -using LibHac.FsSystem; -using LibHac.Loader; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Executables; -using Ryujinx.Memory; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using static Ryujinx.HLE.HOS.ModLoader; -using ApplicationId = LibHac.Ncm.ApplicationId; -using Path = System.IO.Path; - -namespace Ryujinx.HLE.HOS -{ - using JsonHelper = Common.Utilities.JsonHelper; - - public class ApplicationLoader - { - // Binaries from exefs are loaded into mem in this order. Do not change. - internal static readonly string[] ExeFsPrefixes = - { - "rtld", - "main", - "subsdk0", - "subsdk1", - "subsdk2", - "subsdk3", - "subsdk4", - "subsdk5", - "subsdk6", - "subsdk7", - "subsdk8", - "subsdk9", - "sdk" - }; - - private readonly Switch _device; - private string _titleName; - private string _displayVersion; - private BlitStruct<ApplicationControlProperty> _controlData; - - public BlitStruct<ApplicationControlProperty> ControlData => _controlData; - public string TitleName => _titleName; - public string DisplayVersion => _displayVersion; - - public ulong TitleId { get; private set; } - public bool TitleIs64Bit { get; private set; } - - public string TitleIdText => TitleId.ToString("x16"); - - public IDiskCacheLoadState DiskCacheLoadState { get; private set; } - - public ApplicationLoader(Switch device) - { - _device = device; - _controlData = new BlitStruct<ApplicationControlProperty>(1); - } - - public void LoadCart(string exeFsDir, string romFsFile = null) - { - LocalFileSystem codeFs = new LocalFileSystem(exeFsDir); - - MetaLoader metaData = ReadNpdm(codeFs); - - _device.Configuration.VirtualFileSystem.ModLoader.CollectMods( - new[] { TitleId }, - _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); - - if (TitleId != 0) - { - EnsureSaveData(new ApplicationId(TitleId)); - } - - ulong pid = LoadExeFs(codeFs, string.Empty, metaData); - - if (romFsFile != null) - { - _device.Configuration.VirtualFileSystem.LoadRomFs(pid, romFsFile); - } - } - - public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) - { - Nca mainNca = null; - Nca patchNca = null; - Nca controlNca = null; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); - - if (ncaProgramIndex != programIndex) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } - } - - return (mainNca, patchNca, controlNca); - } - - public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex) - { - Nca patchNca = null; - Nca controlNca = null; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); - - if (ncaProgramIndex != programIndex) - { - continue; - } - - if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) - { - break; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - patchNca = nca; - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } - } - - return (patchNca, controlNca); - } - - public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath) - { - updatePath = null; - - if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) - { - // Clear the program index part. - titleIdBase &= 0xFFFFFFFFFFFFFFF0; - - // Load update informations if existing. - string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); - - if (File.Exists(titleUpdateMetadataPath)) - { - updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected; - - if (File.Exists(updatePath)) - { - FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - - return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex); - } - } - } - - return (null, null); - } - - public void LoadXci(string xciFile) - { - FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read); - Xci xci = new Xci(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage()); - - if (!xci.HasPartition(XciPartitionType.Secure)) - { - Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI secure partition"); - - return; - } - - PartitionFileSystem securePartition = xci.OpenPartition(XciPartitionType.Secure); - - Nca mainNca; - Nca patchNca; - Nca controlNca; - - try - { - (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, securePartition, _device.Configuration.UserChannelPersistence.Index); - - RegisterProgramMapInfo(securePartition).ThrowIfFailure(); - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.Loader, $"Unable to load XCI: {e.Message}"); - - return; - } - - if (mainNca == null) - { - Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find Main NCA"); - - return; - } - - _device.Configuration.ContentManager.LoadEntries(_device); - _device.Configuration.ContentManager.ClearAocData(); - _device.Configuration.ContentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel); - - LoadNca(mainNca, patchNca, controlNca); - } - - public void LoadNsp(string nspFile) - { - FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - - Nca mainNca; - Nca patchNca; - Nca controlNca; - - try - { - (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, nsp, _device.Configuration.UserChannelPersistence.Index); - - RegisterProgramMapInfo(nsp).ThrowIfFailure(); - } - catch (Exception e) - { - Logger.Error?.Print(LogClass.Loader, $"Unable to load NSP: {e.Message}"); - - return; - } - - if (mainNca != null) - { - _device.Configuration.ContentManager.ClearAocData(); - _device.Configuration.ContentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel); - - LoadNca(mainNca, patchNca, controlNca); - - return; - } - - // This is not a normal NSP, it's actually a ExeFS as a NSP - LoadExeFs(nsp, null, isHomebrew: true); - } - - public void LoadNca(string ncaFile) - { - FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); - Nca nca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); - - LoadNca(nca, null, null); - } - - public void LoadServiceNca(string ncaFile) - { - FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); - Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); - - if (mainNca.Header.ContentType != NcaContentType.Program) - { - Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); - - return; - } - - IFileSystem codeFs = null; - - if (mainNca.CanOpenSection(NcaSectionType.Code)) - { - codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel); - } - - if (codeFs == null) - { - Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); - - return; - } - - using var npdmFile = new UniqueRef<IFile>(); - - Result result = codeFs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); - - MetaLoader metaData; - - npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure(); - - var npdmBuffer = new byte[fileSize]; - npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure(); - - metaData = new MetaLoader(); - metaData.Load(npdmBuffer).ThrowIfFailure(); - - NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length]; - - for (int i = 0; i < nsos.Length; i++) - { - string name = ExeFsPrefixes[i]; - - if (!codeFs.FileExists($"/{name}")) - { - continue; // File doesn't exist, skip. - } - - Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); - - using var nsoFile = new UniqueRef<IFile>(); - - codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); - } - - // Collect the nsos, ignoring ones that aren't used. - NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - - string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString; - bool usePtc = _device.System.EnablePtc; - - metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false); - ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - - string titleIdText = npdm.Aci.ProgramId.Value.ToString("x16"); - bool titleIs64Bit = (npdm.Meta.Flags & 1) != 0; - - string programName = Encoding.ASCII.GetString(npdm.Meta.ProgramName).TrimEnd('\0'); - - Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]"); - } - - private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) - { - if (mainNca.Header.ContentType != NcaContentType.Program) - { - Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); - - return; - } - - IStorage dataStorage = null; - IFileSystem codeFs = null; - - (Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), _device.Configuration.UserChannelPersistence.Index, out _); - - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } - - if (updateControlNca != null) - { - controlNca = updateControlNca; - } - - // Load program 0 control NCA as we are going to need it for display version. - (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); - - // Load Aoc - string titleAocMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); - - if (File.Exists(titleAocMetadataPath)) - { - List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath); - - foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) - { - foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) - { - if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled) - { - _device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath); - } - else - { - Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed."); - } - } - } - } - - if (patchNca == null) - { - if (mainNca.CanOpenSection(NcaSectionType.Data)) - { - dataStorage = mainNca.OpenStorage(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); - } - - if (mainNca.CanOpenSection(NcaSectionType.Code)) - { - codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel); - } - } - else - { - if (patchNca.CanOpenSection(NcaSectionType.Data)) - { - dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); - } - - if (patchNca.CanOpenSection(NcaSectionType.Code)) - { - codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, _device.System.FsIntegrityCheckLevel); - } - } - - if (codeFs == null) - { - Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); - - return; - } - - MetaLoader metaData = ReadNpdm(codeFs); - - _device.Configuration.VirtualFileSystem.ModLoader.CollectMods( - _device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), - _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), - _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); - - string displayVersion = string.Empty; - - if (controlNca != null) - { - ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion); - } - else - { - ControlData.ByteSpan.Clear(); - } - - // NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application. - // BODY: As such, to avoid PTC cache confusion, we only trust the the program 0 display version when launching a sub program. - if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0) - { - string dummyTitleName = ""; - BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1); - - ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion); - } - - _displayVersion = displayVersion; - - ulong pid = LoadExeFs(codeFs, displayVersion, metaData); - - if (dataStorage == null) - { - Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); - } - else - { - IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage); - - _device.Configuration.VirtualFileSystem.SetRomFs(pid, newStorage.AsStream(FileAccess.Read)); - } - - // Don't create save data for system programs. - if (TitleId != 0 && (TitleId < SystemProgramId.Start.Value || TitleId > SystemAppletId.End.Value)) - { - // Multi-program applications can technically use any program ID for the main program, but in practice they always use 0 in the low nibble. - // We'll know if this changes in the future because stuff will get errors when trying to mount the correct save. - EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); - } - - Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); - } - - // Sets TitleId, so be sure to call before using it - private MetaLoader ReadNpdm(IFileSystem fs) - { - using var npdmFile = new UniqueRef<IFile>(); - - Result result = fs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); - - MetaLoader metaData; - - if (ResultFs.PathNotFound.Includes(result)) - { - Logger.Warning?.Print(LogClass.Loader, "NPDM file not found, using default values!"); - - metaData = GetDefaultNpdm(); - } - else - { - npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure(); - - var npdmBuffer = new byte[fileSize]; - npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure(); - - metaData = new MetaLoader(); - metaData.Load(npdmBuffer).ThrowIfFailure(); - } - - metaData.GetNpdm(out var npdm).ThrowIfFailure(); - - TitleId = npdm.Aci.ProgramId.Value; - TitleIs64Bit = (npdm.Meta.Flags & 1) != 0; - _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); - - return metaData; - } - - private static void ReadControlData(Switch device, Nca controlNca, ref BlitStruct<ApplicationControlProperty> controlData, ref string titleName, ref string displayVersion) - { - using var controlFile = new UniqueRef<IFile>(); - - IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); - Result result = controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read); - - if (result.IsSuccess()) - { - result = controlFile.Get.Read(out long bytesRead, 0, controlData.ByteSpan, ReadOption.None); - - if (result.IsSuccess() && bytesRead == controlData.ByteSpan.Length) - { - titleName = controlData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString(); - - if (string.IsNullOrWhiteSpace(titleName)) - { - titleName = controlData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); - } - - displayVersion = controlData.Value.DisplayVersionString.ToString(); - } - } - else - { - controlData.ByteSpan.Clear(); - } - } - - private ulong LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) - { - if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) - { - metaData = null; // TODO: Check if we should retain old npdm. - } - - metaData ??= ReadNpdm(codeFs); - - NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length]; - - for (int i = 0; i < nsos.Length; i++) - { - string name = ExeFsPrefixes[i]; - - if (!codeFs.FileExists($"/{name}")) - { - continue; // File doesn't exist, skip. - } - - Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); - - using var nsoFile = new UniqueRef<IFile>(); - - codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); - } - - // ExeFs file replacements. - ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); - - // Collect the nsos, ignoring ones that aren't used. - NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - - // Take the npdm from mods if present. - if (modLoadResult.Npdm != null) - { - metaData = modLoadResult.Npdm; - } - - _device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); - - _device.Configuration.ContentManager.LoadEntries(_device); - - bool usePtc = _device.System.EnablePtc; - - // Don't use PPTC if ExeFs files have been replaced. - usePtc &= !modLoadResult.Modified; - - if (_device.System.EnablePtc && !usePtc) - { - Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled."); - } - - Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText; - _device.Gpu.HostInitalized.Set(); - - MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode; - - if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) - { - memoryManagerMode = MemoryManagerMode.SoftwarePageTable; - } - - // We allow it for nx-hbloader because it can be used to launch homebrew. - bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew; - - metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit); - ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - - DiskCacheLoadState = result.DiskCacheLoadState; - - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); - - return result.ProcessId; - } - - public void LoadProgram(string filePath) - { - MetaLoader metaData = GetDefaultNpdm(); - metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true); - - bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; - - IExecutable executable; - Stream romfsStream = null; - - if (isNro) - { - FileStream input = new FileStream(filePath, FileMode.Open); - NroExecutable obj = new NroExecutable(input.AsStorage()); - - executable = obj; - - // Homebrew NRO can actually have some data after the actual NRO. - if (input.Length > obj.FileSize) - { - input.Position = obj.FileSize; - - BinaryReader reader = new BinaryReader(input); - - uint asetMagic = reader.ReadUInt32(); - if (asetMagic == 0x54455341) - { - uint asetVersion = reader.ReadUInt32(); - if (asetVersion == 0) - { - ulong iconOffset = reader.ReadUInt64(); - ulong iconSize = reader.ReadUInt64(); - - ulong nacpOffset = reader.ReadUInt64(); - ulong nacpSize = reader.ReadUInt64(); - - ulong romfsOffset = reader.ReadUInt64(); - ulong romfsSize = reader.ReadUInt64(); - - if (romfsSize != 0) - { - romfsStream = new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset); - } - - if (nacpSize != 0) - { - input.Seek(obj.FileSize + (long)nacpOffset, SeekOrigin.Begin); - - reader.Read(ControlData.ByteSpan); - - ref ApplicationControlProperty nacp = ref ControlData.Value; - - programInfo.Name = nacp.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString(); - - if (string.IsNullOrWhiteSpace(programInfo.Name)) - { - programInfo.Name = nacp.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString(); - } - - if (nacp.PresenceGroupId != 0) - { - programInfo.ProgramId = nacp.PresenceGroupId; - } - else if (nacp.SaveDataOwnerId != 0) - { - programInfo.ProgramId = nacp.SaveDataOwnerId; - } - else if (nacp.AddOnContentBaseId != 0) - { - programInfo.ProgramId = nacp.AddOnContentBaseId - 0x1000; - } - else - { - programInfo.ProgramId = 0000000000000000; - } - } - } - else - { - Logger.Warning?.Print(LogClass.Loader, $"Unsupported ASET header version found \"{asetVersion}\""); - } - } - } - } - else - { - executable = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read), Path.GetFileNameWithoutExtension(filePath)); - } - - _device.Configuration.ContentManager.LoadEntries(_device); - - _titleName = programInfo.Name; - TitleId = programInfo.ProgramId; - TitleIs64Bit = (npdm.Meta.Flags & 1) != 0; - _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); - - // Explicitly null titleid to disable the shader cache. - Graphics.Gpu.GraphicsConfig.TitleId = null; - _device.Gpu.HostInitalized.Set(); - - ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable); - - if (romfsStream != null) - { - _device.Configuration.VirtualFileSystem.SetRomFs(result.ProcessId, romfsStream); - } - - DiskCacheLoadState = result.DiskCacheLoadState; - - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); - } - - private MetaLoader GetDefaultNpdm() - { - Assembly asm = Assembly.GetCallingAssembly(); - - using (Stream npdmStream = asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm")) - { - var npdmBuffer = new byte[npdmStream.Length]; - npdmStream.Read(npdmBuffer); - - var metaLoader = new MetaLoader(); - metaLoader.Load(npdmBuffer).ThrowIfFailure(); - - return metaLoader; - } - } - - private static (ulong applicationId, int programCount) GetMultiProgramInfo(VirtualFileSystem fileSystem, PartitionFileSystem pfs) - { - ulong mainProgramId = 0; - Span<bool> hasIndex = stackalloc bool[0x10]; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - if (nca.Header.ContentType != NcaContentType.Program) - { - continue; - } - - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - continue; - } - - ulong currentProgramId = nca.Header.TitleId; - ulong currentMainProgramId = currentProgramId & ~0xFFFul; - - if (mainProgramId == 0 && currentMainProgramId != 0) - { - mainProgramId = currentMainProgramId; - } - - if (mainProgramId != currentMainProgramId) - { - // As far as I know there aren't any multi-application game cards containing multi-program applications, - // so because multi-application game cards are the only way we should run into multiple applications - // we'll just return that there's a single program. - return (mainProgramId, 1); - } - - hasIndex[(int)(currentProgramId & 0xF)] = true; - } - - int programCount = 0; - - for (int i = 0; i < hasIndex.Length && hasIndex[i]; i++) - { - programCount++; - } - - return (mainProgramId, programCount); - } - - private Result RegisterProgramMapInfo(PartitionFileSystem pfs) - { - (ulong applicationId, int programCount) = GetMultiProgramInfo(_device.Configuration.VirtualFileSystem, pfs); - - if (programCount <= 0) - return Result.Success; - - Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[0x10]; - - for (int i = 0; i < programCount; i++) - { - mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i); - mapInfo[i].MainProgramId = new ApplicationId(applicationId); - mapInfo[i].ProgramIndex = (byte)i; - } - - return _device.System.LibHacHorizonManager.NsClient.Fs.RegisterProgramIndexMapInfo(mapInfo.Slice(0, programCount)); - } - - private Result EnsureSaveData(ApplicationId applicationId) - { - Logger.Info?.Print(LogClass.Application, "Ensuring required savedata exists."); - - Uid user = _device.System.AccountManager.LastOpenedUser.UserId.ToLibHacUid(); - - ref ApplicationControlProperty control = ref ControlData.Value; - - if (LibHac.Common.Utilities.IsZeros(ControlData.ByteSpan)) - { - // If the current application doesn't have a loaded control property, create a dummy one - // and set the savedata sizes so a user savedata will be created. - control = ref new BlitStruct<ApplicationControlProperty>(1).Value; - - // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; - control.UserAccountSaveDataJournalSize = 0x4000; - control.SaveDataOwnerId = applicationId.Value; - - Logger.Warning?.Print(LogClass.Application, - "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); - } - - HorizonClient hos = _device.System.LibHacHorizonManager.RyujinxClient; - Result resultCode = hos.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control); - - if (resultCode.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {resultCode.ToStringWithName()}"); - - return resultCode; - } - - resultCode = hos.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in user); - - if (resultCode.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {resultCode.ToStringWithName()}"); - } - - return resultCode; - } - } -} diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 2b77a7c2..1639532e 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -35,6 +35,7 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Processes; using Ryujinx.Horizon; using System; using System.Collections.Generic; @@ -358,11 +359,11 @@ namespace Ryujinx.HLE.HOS } } - public void LoadKip(string kipPath) + public bool LoadKip(string kipPath) { using var kipFile = new SharedRef<IStorage>(new LocalStorage(kipPath, FileAccess.Read)); - ProgramLoader.LoadKip(KernelContext, new KipExecutable(in kipFile)); + return ProcessLoaderHelper.LoadKip(KernelContext, new KipExecutable(in kipFile)); } public void ChangeDockedModeState(bool newState) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs index 556703cf..4cf67172 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessTamperInfo.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Kernel.Process { - internal class ProcessTamperInfo + class ProcessTamperInfo { public KProcess Process { get; } public IEnumerable<string> BuildIds { get; } diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs index a6dc9013..16512541 100644 --- a/Ryujinx.HLE/HOS/ModLoader.cs +++ b/Ryujinx.HLE/HOS/ModLoader.cs @@ -10,6 +10,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Mods; +using Ryujinx.HLE.Loaders.Processes; using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -547,7 +548,7 @@ namespace Ryujinx.HLE.HOS return modLoadResult; } - if (nsos.Length != ApplicationLoader.ExeFsPrefixes.Length) + if (nsos.Length != ProcessConst.ExeFsPrefixes.Length) { throw new ArgumentOutOfRangeException("NSO Count is incorrect"); } @@ -556,9 +557,9 @@ namespace Ryujinx.HLE.HOS foreach (var mod in exeMods) { - for (int i = 0; i < ApplicationLoader.ExeFsPrefixes.Length; ++i) + for (int i = 0; i < ProcessConst.ExeFsPrefixes.Length; ++i) { - var nsoName = ApplicationLoader.ExeFsPrefixes[i]; + var nsoName = ProcessConst.ExeFsPrefixes[i]; FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) @@ -596,7 +597,7 @@ namespace Ryujinx.HLE.HOS } } - for (int i = ApplicationLoader.ExeFsPrefixes.Length - 1; i >= 0; --i) + for (int i = ProcessConst.ExeFsPrefixes.Length - 1; i >= 0; --i) { if (modLoadResult.Stubs[1 << i] && !modLoadResult.Replaces[1 << i]) // Prioritizes replacements over stubs { diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs deleted file mode 100644 index 4ebcb7e7..00000000 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ /dev/null @@ -1,423 +0,0 @@ -using LibHac.Loader; -using LibHac.Ncm; -using LibHac.Util; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Memory; -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.HLE.Loaders.Executables; -using Ryujinx.Horizon.Common; -using System; -using System.Linq; -using System.Runtime.InteropServices; -using Npdm = LibHac.Loader.Npdm; - -namespace Ryujinx.HLE.HOS -{ - struct ProgramInfo - { - public string Name; - public ulong ProgramId; - public readonly string TitleIdText; - public readonly string DisplayVersion; - public readonly bool DiskCacheEnabled; - public readonly bool AllowCodeMemoryForJit; - - public ProgramInfo(in Npdm npdm, string displayVersion, bool diskCacheEnabled, bool allowCodeMemoryForJit) - { - ulong programId = npdm.Aci.ProgramId.Value; - - Name = StringUtils.Utf8ZToString(npdm.Meta.ProgramName); - ProgramId = programId; - TitleIdText = programId.ToString("x16"); - DisplayVersion = displayVersion; - DiskCacheEnabled = diskCacheEnabled; - AllowCodeMemoryForJit = allowCodeMemoryForJit; - } - } - - struct ProgramLoadResult - { - public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null, 0); - - public readonly bool Success; - public readonly ProcessTamperInfo TamperInfo; - public readonly IDiskCacheLoadState DiskCacheLoadState; - public readonly ulong ProcessId; - - public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState, ulong pid) - { - Success = success; - TamperInfo = tamperInfo; - DiskCacheLoadState = diskCacheLoadState; - ProcessId = pid; - } - } - - static class ProgramLoader - { - private const bool AslrEnabled = true; - - private const int ArgsHeaderSize = 8; - private const int ArgsDataSize = 0x9000; - private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize; - - public static bool LoadKip(KernelContext context, KipExecutable kip) - { - uint endOffset = kip.DataOffset + (uint)kip.Data.Length; - - if (kip.BssSize != 0) - { - endOffset = kip.BssOffset + kip.BssSize; - } - - uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize); - - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); - - ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL; - - ulong codeAddress = codeBaseAddress + kip.TextOffset; - - ProcessCreationFlags flags = 0; - - if (AslrEnabled) - { - // TODO: Randomization. - - flags |= ProcessCreationFlags.EnableAslr; - } - - if (kip.Is64BitAddressSpace) - { - flags |= ProcessCreationFlags.AddressSpace64Bit; - } - - if (kip.Is64Bit) - { - flags |= ProcessCreationFlags.Is64Bit; - } - - ProcessCreationInfo creationInfo = new ProcessCreationInfo( - kip.Name, - kip.Version, - kip.ProgramId, - codeAddress, - codePagesCount, - flags, - 0, - 0); - - MemoryRegion memoryRegion = kip.UsesSecureMemory - ? MemoryRegion.Service - : MemoryRegion.Application; - - KMemoryRegionManager region = context.MemoryManager.MemoryRegions[(int)memoryRegion]; - - Result result = region.AllocatePages(out KPageList pageList, (ulong)codePagesCount); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - - return false; - } - - KProcess process = new KProcess(context); - - var processContextFactory = new ArmProcessContextFactory( - context.Device.System.TickSource, - context.Device.Gpu, - string.Empty, - string.Empty, - false, - codeAddress, - codeSize); - - result = process.InitializeKip( - creationInfo, - kip.Capabilities, - pageList, - context.ResourceLimit, - memoryRegion, - processContextFactory); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - - return false; - } - - result = LoadIntoMemory(process, kip, codeBaseAddress); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - - return false; - } - - process.DefaultCpuCore = kip.IdealCoreId; - - result = process.Start(kip.Priority, (ulong)kip.StackSize); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); - - return false; - } - - context.Processes.TryAdd(process.Pid, process); - - return true; - } - - public static ProgramLoadResult LoadNsos( - KernelContext context, - MetaLoader metaData, - ProgramInfo programInfo, - byte[] arguments = null, - params IExecutable[] executables) - { - context.Device.System.ServiceTable.WaitServicesReady(); - - LibHac.Result rc = metaData.GetNpdm(out var npdm); - - if (rc.IsFailure()) - { - return ProgramLoadResult.Failed; - } - - ref readonly var meta = ref npdm.Meta; - - ulong argsStart = 0; - uint argsSize = 0; - ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL; - uint codeSize = 0; - - var buildIds = executables.Select(e => (e switch - { - NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()), - NroExecutable nro => BitConverter.ToString(nro.Header.BuildId), - _ => "" - }).Replace("-", "").ToUpper()); - - ulong[] nsoBase = new ulong[executables.Length]; - - for (int index = 0; index < executables.Length; index++) - { - IExecutable nso = executables[index]; - - uint textEnd = nso.TextOffset + (uint)nso.Text.Length; - uint roEnd = nso.RoOffset + (uint)nso.Ro.Length; - uint dataEnd = nso.DataOffset + (uint)nso.Data.Length + nso.BssSize; - - uint nsoSize = textEnd; - - if (nsoSize < roEnd) - { - nsoSize = roEnd; - } - - if (nsoSize < dataEnd) - { - nsoSize = dataEnd; - } - - nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize); - - nsoBase[index] = codeStart + codeSize; - - codeSize += nsoSize; - - if (arguments != null && argsSize == 0) - { - argsStart = codeSize; - - argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize); - - codeSize += argsSize; - } - } - - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); - - int personalMmHeapPagesCount = (int)(meta.SystemResourceSize / KPageTableBase.PageSize); - - ProcessCreationInfo creationInfo = new ProcessCreationInfo( - programInfo.Name, - (int)meta.Version, - programInfo.ProgramId, - codeStart, - codePagesCount, - (ProcessCreationFlags)meta.Flags | ProcessCreationFlags.IsApplication, - 0, - personalMmHeapPagesCount); - - context.Device.System.LibHacHorizonManager.InitializeApplicationClient(new ProgramId(programInfo.ProgramId), in npdm); - - Result result; - - KResourceLimit resourceLimit = new KResourceLimit(context); - - long applicationRgSize = (long)context.MemoryManager.MemoryRegions[(int)MemoryRegion.Application].Size; - - result = resourceLimit.SetLimitValue(LimitableResource.Memory, applicationRgSize); - - if (result.IsSuccess) - { - result = resourceLimit.SetLimitValue(LimitableResource.Thread, 608); - } - - if (result.IsSuccess) - { - result = resourceLimit.SetLimitValue(LimitableResource.Event, 700); - } - - if (result.IsSuccess) - { - result = resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128); - } - - if (result.IsSuccess) - { - result = resourceLimit.SetLimitValue(LimitableResource.Session, 894); - } - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); - - return ProgramLoadResult.Failed; - } - - KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit); - - MemoryRegion memoryRegion = (MemoryRegion)((npdm.Acid.Flags >> 2) & 0xf); - - if (memoryRegion > MemoryRegion.NvServices) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); - - return ProgramLoadResult.Failed; - } - - var processContextFactory = new ArmProcessContextFactory( - context.Device.System.TickSource, - context.Device.Gpu, - programInfo.TitleIdText, - programInfo.DisplayVersion, - programInfo.DiskCacheEnabled, - codeStart, - codeSize); - - result = process.Initialize( - creationInfo, - MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData), - resourceLimit, - memoryRegion, - processContextFactory); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - - return ProgramLoadResult.Failed; - } - - for (int index = 0; index < executables.Length; index++) - { - Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}..."); - - result = LoadIntoMemory(process, executables[index], nsoBase[index]); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - - return ProgramLoadResult.Failed; - } - } - - process.DefaultCpuCore = meta.DefaultCpuId; - - result = process.Start(meta.MainThreadPriority, meta.MainThreadStackSize); - - if (result != Result.Success) - { - Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); - - return ProgramLoadResult.Failed; - } - - context.Processes.TryAdd(process.Pid, process); - - // Keep the build ids because the tamper machine uses them to know which process to associate a - // tamper to and also keep the starting address of each executable inside a process because some - // memory modifications are relative to this address. - ProcessTamperInfo tamperInfo = new ProcessTamperInfo( - process, - buildIds, - nsoBase, - process.MemoryManager.HeapRegionStart, - process.MemoryManager.AliasRegionStart, - process.MemoryManager.CodeRegionStart); - - return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState, process.Pid); - } - - private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) - { - ulong textStart = baseAddress + image.TextOffset; - ulong roStart = baseAddress + image.RoOffset; - ulong dataStart = baseAddress + image.DataOffset; - ulong bssStart = baseAddress + image.BssOffset; - - ulong end = dataStart + (ulong)image.Data.Length; - - if (image.BssSize != 0) - { - end = bssStart + image.BssSize; - } - - process.CpuMemory.Write(textStart, image.Text); - process.CpuMemory.Write(roStart, image.Ro); - process.CpuMemory.Write(dataStart, image.Data); - - process.CpuMemory.Fill(bssStart, image.BssSize, 0); - - Result SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) - { - if (size == 0) - { - return Result.Success; - } - - size = BitUtils.AlignUp<ulong>(size, KPageTableBase.PageSize); - - return process.MemoryManager.SetProcessMemoryPermission(address, size, permission); - } - - Result result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute); - - if (result != Result.Success) - { - return result; - } - - result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, KMemoryPermission.Read); - - if (result != Result.Success) - { - return result; - } - - return SetProcessMemoryPermission(dataStart, end - dataStart, KMemoryPermission.ReadAndWrite); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 1b412d74..413bedce 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // TODO: Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current Pid and store the result (NACP file) internally. // But since we use LibHac and we load one Application at a time, it's not necessary. - context.ResponseData.Write((byte)context.Device.Application.ControlData.Value.UserAccountSwitchLock); + context.ResponseData.Write((byte)context.Device.Processes.ActiveApplication.ApplicationControlProperties.UserAccountSwitchLock); Logger.Stub?.PrintStub(LogClass.ServiceAcc); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs index 00081e1b..72049714 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib public ILibraryAppletSelfAccessor(ServiceCtx context) { - if (context.Device.Application.TitleId == 0x0100000000001009) + if (context.Device.Processes.ActiveApplication.ProgramId == 0x0100000000001009) { // Create MiiEdit data. _appletStandalone = new AppletStandalone() @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib } else { - throw new NotImplementedException($"{context.Device.Application.TitleId} applet is not implemented."); + throw new NotImplementedException($"{context.Device.Processes.ActiveApplication.ProgramId} applet is not implemented."); } } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index f8f88a1c..924f5429 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -115,28 +115,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid(); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new ApplicationId(context.Device.Application.TitleId & ~0xFul); + ApplicationId applicationId = new ApplicationId(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); - BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData; - - ref ApplicationControlProperty control = ref controlHolder.Value; - - if (LibHac.Common.Utilities.IsZeros(controlHolder.ByteSpan)) - { - // If the current application doesn't have a loaded control property, create a dummy one - // and set the savedata sizes so a user savedata will be created. - control = ref new BlitStruct<ApplicationControlProperty>(1).Value; - - // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; - control.UserAccountSaveDataJournalSize = 0x4000; - - Logger.Warning?.Print(LogClass.ServiceAm, - "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); - } + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; LibHac.HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient; - LibHac.Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in control, in userId); + LibHac.Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in nacp, in userId); context.ResponseData.Write(requiredSize); @@ -153,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // TODO: When above calls are implemented, switch to using ns:am long desiredLanguageCode = context.Device.System.State.DesiredLanguageCode; - int supportedLanguages = (int)context.Device.Application.ControlData.Value.SupportedLanguageFlag; + int supportedLanguages = (int)context.Device.Processes.ActiveApplication.ApplicationControlProperties.SupportedLanguageFlag; int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages); if (firstSupported > (int)TitleLanguage.BrazilianPortuguese) @@ -196,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati public ResultCode GetDisplayVersion(ServiceCtx context) { // If an NACP isn't found, the buffer will be all '\0' which seems to be the correct implementation. - context.ResponseData.Write(context.Device.Application.ControlData.Value.DisplayVersion); + context.ResponseData.Write(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion); return ResultCode.Success; } @@ -251,13 +235,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati long journalSize = context.RequestData.ReadInt64(); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new ApplicationId(context.Device.Application.TitleId & ~0xFul); + ApplicationId applicationId = new ApplicationId(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); - BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData; + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; LibHac.Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize, - out CacheStorageTargetMedia storageTarget, applicationId, in controlHolder.Value, index, saveSize, - journalSize); + out CacheStorageTargetMedia storageTarget, applicationId, in nacp, index, saveSize, journalSize); if (result.IsFailure()) return (ResultCode)result.Value; @@ -677,7 +660,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati throw new InvalidSystemResourceException($"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); } - context.Device.Application.LoadServiceNca(filePath); + context.Device.LoadNca(filePath); // FIXME: Most likely not how this should be done? while (!context.Device.System.SmRegistry.IsServiceRegistered("jit:u")) diff --git a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs index c985092b..3e4eca0a 100644 --- a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs +++ b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp return new ApplicationLaunchProperty { - TitleId = context.Device.Application.TitleId, + TitleId = context.Device.Processes.ActiveApplication.ProgramId, Version = 0x00, BaseGameStorageId = (byte)StorageId.BuiltInSystem, UpdateGameStorageId = (byte)StorageId.None diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs index 1789122e..e0c65f44 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index 6d663a4d..c884e880 100644 --- a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal errorReport.AppendLine(); errorReport.AppendLine("ErrorReport log:"); - errorReport.AppendLine($"\tTitleId: {context.Device.Application.TitleId:x16}"); + errorReport.AppendLine($"\tTitleId: {context.Device.Processes.ActiveApplication.ProgramIdText}"); errorReport.AppendLine($"\tPid: {pid}"); errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}"); errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}"); @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal { errorReport.AppendLine("CPU Context:"); - if (context.Device.Application.TitleIs64Bit) + if (context.Device.Processes.ActiveApplication.Is64Bit) { CpuContext64 cpuContext64 = MemoryMarshal.Cast<byte, CpuContext64>(cpuContext)[0]; diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 17a33b79..4317c8f6 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -334,7 +334,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator } // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. - ApplicationControlProperty controlProperty = context.Device.Application.ControlData.Value; + ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; /* diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 37143a5a..1b63f362 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -808,7 +808,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs { byte programIndex = context.RequestData.ReadByte(); - if ((context.Device.Application.TitleId & 0xf) != programIndex) + if ((context.Device.Processes.ActiveApplication.ProgramId & 0xf) != programIndex) { throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex})."); } diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs index 0d552003..b8f9e3b9 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return CountAddOnContentImpl(context, context.Device.Application.TitleId); + return CountAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(3)] @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return ListAddContentImpl(context, context.Device.Application.TitleId); + return ListAddContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(4)] // 1.0.0-6.2.0 @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return GetAddOnContentBaseIdImpl(context, context.Device.Application.TitleId); + return GetAddOnContentBaseIdImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(6)] // 1.0.0-6.2.0 @@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return PrepareAddOnContentImpl(context, context.Device.Application.TitleId); + return PrepareAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); } [CommandHipc(8)] // 4.0.0+ @@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. // TODO: Found where stored value is used. - ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Application.TitleId); + ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId); if (resultCode != ResultCode.Success) { @@ -294,7 +294,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service calls arp:r GetApplicationControlProperty to get AddOnContentBaseId using TitleId, // If the call fails, it returns ResultCode.InvalidPid. - _addOnContentBaseId = context.Device.Application.ControlData.Value.AddOnContentBaseId; + _addOnContentBaseId = context.Device.Processes.ActiveApplication.ApplicationControlProperties.AddOnContentBaseId; if (_addOnContentBaseId == 0) { @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc { uint index = context.RequestData.ReadUInt32(); - ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Application.TitleId); + ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId); if (resultCode != ResultCode.Success) { diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index d3a89178..249343d7 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -1,4 +1,8 @@ -namespace Ryujinx.HLE.HOS.Services.Ns +using LibHac.Ns; +using Ryujinx.Common.Utilities; +using System; + +namespace Ryujinx.HLE.HOS.Services.Ns { [Service("ns:am")] class IApplicationManagerInterface : IpcService @@ -14,9 +18,9 @@ ulong position = context.Request.ReceiveBuff[0].Position; - byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray(); + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; - context.Memory.Write(position, nacpData); + context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index 3b6965d0..8f6acc1c 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -1,4 +1,7 @@ -namespace Ryujinx.HLE.HOS.Services.Ns +using LibHac.Common; +using LibHac.Ns; + +namespace Ryujinx.HLE.HOS.Services.Ns { class IReadOnlyApplicationControlDataInterface : IpcService { @@ -13,9 +16,9 @@ ulong position = context.Request.ReceiveBuff[0].Position; - byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray(); + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; - context.Memory.Write(position, nacpData); + context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs index e0017808..02964749 100644 --- a/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs +++ b/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs @@ -56,8 +56,8 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory _titleId = titleId; // TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields. - _ratingAge = Array.ConvertAll(context.Device.Application.ControlData.Value.RatingAge.ItemsRo.ToArray(), Convert.ToInt32); - _parentalControlFlag = context.Device.Application.ControlData.Value.ParentalControlFlag; + _ratingAge = Array.ConvertAll(context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge.ItemsRo.ToArray(), Convert.ToInt32); + _parentalControlFlag = context.Device.Processes.ActiveApplication.ApplicationControlProperties.ParentalControlFlag; } } diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs index 1d6cc118..52a07d46 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService { @@ -16,8 +15,6 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false) { - ref readonly var controlProperty = ref context.Device.Application.ControlData.Value; - ulong inputPosition = context.Request.SendBuff[0].Position; ulong inputSize = context.Request.SendBuff[0].Size; @@ -34,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService } } - PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)controlProperty.PlayLogQueryCapability; + PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryCapability; List<ulong> titleIds = new List<ulong>(); @@ -48,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService // Check if input title ids are in the whitelist. foreach (ulong titleId in titleIds) { - if (!controlProperty.PlayLogQueryableApplicationId.ItemsRo.Contains(titleId)) + if (!context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryableApplicationId.ItemsRo.Contains(titleId)) { return (ResultCode)Am.ResultCode.ObjectInvalid; } |
