public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage, IntegrityCheckLevel integrityCheckLevel) { aocStorage = null; if (_aocData.TryGetValue(aocTitleId, out AocItem aoc) && aoc.Enabled) { var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read); using var ncaFile = new UniqueRef <IFile>(); PartitionFileSystem pfs; switch (Path.GetExtension(aoc.ContainerPath)) { case ".xci": pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure); pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read); break; case ".nsp": pfs = new PartitionFileSystem(file.AsStorage()); pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read); break; default: return(false); // Print error? } aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()).OpenStorage(NcaSectionType.Data, integrityCheckLevel); return(true); } return(false); }
public void Initialize(Switch device) { lock (_lock) { _certificates = new Dictionary <CaCertificateId, CertStoreEntry>(); _initialized = false; _contentManager = device.System.ContentManager; _virtualFileSystem = device.FileSystem; _fsIntegrityCheckLevel = device.System.FsIntegrityCheckLevel; if (HasCertStoreTitle()) { using LocalStorage ncaFile = new LocalStorage(_virtualFileSystem.SwitchPathToSystemPath(GetCertStoreTitleContentPath()), FileAccess.Read, FileMode.Open); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel); using var trustedCertsFileRef = new UniqueRef <IFile>(); Result result = romfs.OpenFile(ref trustedCertsFileRef.Ref(), "/ssl_TrustedCerts.bdf".ToU8Span(), OpenMode.Read); if (!result.IsSuccess()) { // [1.0.0 - 2.3.0] if (ResultFs.PathNotFound.Includes(result)) { result = romfs.OpenFile(ref trustedCertsFileRef.Ref(), "/ssl_TrustedCerts.tcf".ToU8Span(), OpenMode.Read); } if (result.IsFailure()) { Logger.Error?.Print(LogClass.ServiceSsl, CertStoreTitleMissingErrorMessage); return; } } using IFile trustedCertsFile = trustedCertsFileRef.Release(); trustedCertsFile.GetSize(out long fileSize).ThrowIfFailure(); Span <byte> trustedCertsRaw = new byte[fileSize]; trustedCertsFile.Read(out _, 0, trustedCertsRaw).ThrowIfFailure(); CertStoreFileHeader header = MemoryMarshal.Read <CertStoreFileHeader>(trustedCertsRaw); if (!header.IsValid()) { Logger.Error?.Print(LogClass.ServiceSsl, "Invalid CertStore data found, skipping!"); return; } ReadOnlySpan <byte> trustedCertsData = trustedCertsRaw[Unsafe.SizeOf <CertStoreFileHeader>()..];
private static Result FixExtraDataInSpaceId(HorizonClient hos, SaveDataSpaceId spaceId) { Span <SaveDataInfo> info = stackalloc SaveDataInfo[8]; using var iterator = new UniqueRef <SaveDataIterator>(); Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref(), spaceId); if (rc.IsFailure()) { return(rc); } while (true) { rc = iterator.Get.ReadSaveDataInfo(out long count, info); if (rc.IsFailure()) { return(rc); } if (count == 0) { return(Result.Success); } for (int i = 0; i < count; i++) { rc = FixExtraData(out bool wasFixNeeded, hos, in info[i]);
public void ReadControlData(IFileSystem controlFs, Span <byte> outProperty) { using var controlFile = new UniqueRef <IFile>(); controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); }
private void InitializeLocationNameCache() { if (HasTimeZoneBinaryTitle()) { using (IStorage ncaFileStream = new LocalStorage(_virtualFileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open)) { Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFileStream); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _fsIntegrityCheckLevel); using var binaryListFile = new UniqueRef <IFile>(); romfs.OpenFile(ref binaryListFile.Ref(), "/binaryList.txt".ToU8Span(), OpenMode.Read).ThrowIfFailure(); StreamReader reader = new StreamReader(binaryListFile.Get.AsStream()); List <string> locationNameList = new List <string>(); string locationName; while ((locationName = reader.ReadLine()) != null) { locationNameList.Add(locationName); } LocationNameCache = locationNameList.ToArray(); } } else { LocationNameCache = new string[] { "UTC" }; Logger.Error?.Print(LogClass.ServiceTime, TimeZoneSystemTitleMissingErrorMessage); } }
private void LoadDownloadableContents() { foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList) { if (File.Exists(downloadableContentContainer.ContainerPath)) { using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); VirtualFileSystem.ImportTickets(pfs); foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); if (nca != null) { DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath, downloadableContentNca.Enabled)); } } } } // NOTE: Save the list again to remove leftovers. Save(); }
private void LoadDlcs() { foreach (DlcContainer dlcContainer in _dlcContainerList) { using FileStream containerFile = File.OpenRead(dlcContainer.Path); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); VirtualFileSystem.ImportTickets(pfs); foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.Path); if (nca != null) { Dlcs.Add(new DlcModel(nca.Header.TitleId.ToString("X16"), dlcContainer.Path, dlcNca.Path, dlcNca.Enabled)); } } } }
public byte[] GetFirmwareData(Switch device) { const ulong SystemVersionTitleId = 0x0100000000000809; string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.NandSystem, NcaContentType.Data); if (string.IsNullOrWhiteSpace(contentPath)) { return(null); } string firmwareTitlePath = device.FileSystem.SwitchPathToSystemPath(contentPath); using (IStorage firmwareStorage = new LocalStorage(firmwareTitlePath, FileAccess.Read)) { Nca firmwareContent = new Nca(device.System.KeySet, firmwareStorage); if (!firmwareContent.CanOpenSection(NcaSectionType.Data)) { return(null); } IFileSystem firmwareRomFs = firmwareContent.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); using var firmwareFile = new UniqueRef <IFile>(); Result result = firmwareRomFs.OpenFile(ref firmwareFile.Ref(), "/file".ToU8Span(), OpenMode.Read); if (result.IsFailure()) { return(null); } result = firmwareFile.Get.GetSize(out long fileSize); if (result.IsFailure()) { return(null); } byte[] data = new byte[fileSize]; result = firmwareFile.Get.Read(out _, 0, data); if (result.IsFailure()) { return(null); } return(data); } }
public static void ImportTitleKeysFromNsp(LibHac.Fs.Fsa.IFileSystem nsp, KeySet keySet) { foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { using var ticketFile = new UniqueRef <LibHac.Fs.Fsa.IFile>(); Result result = nsp.OpenFile(ref ticketFile.Ref(), ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.Get.AsStream()); keySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(keySet))); } } }
private async Task AddDownloadableContent(string path) { if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) { return; } using (FileStream containerFile = File.OpenRead(path)) { PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); bool containsDownloadableContent = false; VirtualFileSystem.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 = TryCreateNca(ncaFile.Get.AsStorage(), path); if (nca == null) { continue; } if (nca.Header.ContentType == NcaContentType.PublicData) { if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId) { break; } DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); containsDownloadableContent = true; } } if (!containsDownloadableContent) { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]); } } }
public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem) { if (_avatarDict.Count > 0) { return; } string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(avatarPath)) { using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) { Nca nca = new Nca(virtualFileSystem.KeySet, ncaFileStream); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); foreach (var item in romfs.EnumerateEntries()) { // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) { using var file = new UniqueRef <IFile>(); romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) using (MemoryStream streamPng = new MemoryStream()) { file.Get.AsStream().CopyTo(stream); stream.Position = 0; Image avatarImage = Image.LoadPixelData <Rgba32>(DecompressYaz0(stream), 256, 256); avatarImage.SaveAsPng(streamPng); _avatarDict.Add(item.FullPath, streamPng.ToArray()); } } } } } }
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 ResultCode OpenFileSystemFromInternalFile(ServiceCtx context, string fullPath, out IFileSystem openedFileSystem) { openedFileSystem = null; DirectoryInfo archivePath = new DirectoryInfo(fullPath).Parent; while (string.IsNullOrWhiteSpace(archivePath.Extension)) { archivePath = archivePath.Parent; } if (archivePath.Extension == ".nsp" && File.Exists(archivePath.FullName)) { FileStream pfsFile = new FileStream( archivePath.FullName.TrimEnd(Path.DirectorySeparatorChar), FileMode.Open, FileAccess.Read); try { PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage()); ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet); string filename = fullPath.Replace(archivePath.FullName, string.Empty).TrimStart('\\'); using var ncaFile = new UniqueRef <LibHac.Fs.Fsa.IFile>(); Result result = nsp.OpenFile(ref ncaFile.Ref(), filename.ToU8Span(), OpenMode.Read); if (result.IsFailure()) { return((ResultCode)result.Value); } return(OpenNcaFs(context, fullPath, ncaFile.Release().AsStorage(), out openedFileSystem)); } catch (HorizonResultException ex) { return((ResultCode)ex.ResultValue.Value); } } return(ResultCode.PathDoesNotExist); }
public void ImportTickets(IFileSystem fs) { foreach (DirectoryEntryEx ticketEntry in fs.EnumerateEntries("/", "*.tik")) { using var ticketFile = new UniqueRef <IFile>(); Result result = fs.OpenFile(ref ticketFile.Ref(), ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.Get.AsStream()); if (ticket.TitleKeyType == TitleKeyType.Common) { KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); } } } }
private byte[]? LoadExpectedIcon(SectionItem sectionItem, NacpLanguage nacpLanguage) { var languageName = nacpLanguage.ToString(); var expectedFileName = $"icon_{languageName}.dat"; var iconItem = sectionItem.ChildItems.FirstOrDefault(item => string.Equals(item.Name, expectedFileName, StringComparison.OrdinalIgnoreCase)); if (iconItem == null) { var message = LocalizationManager.Instance.Current.Keys.LoadingError_IconMissing.SafeFormat(expectedFileName); sectionItem.Errors.Add(message); _logger.LogError(message); return(null); } var fileSystem = sectionItem.FileSystem; if (fileSystem == null) { return(null); } try { using var uniqueRefFile = new UniqueRef <IFile>(); fileSystem.OpenFile(ref uniqueRefFile.Ref(), iconItem.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); var file = uniqueRefFile.Release(); file.GetSize(out var fileSize).ThrowIfFailure(); var bytes = new byte[fileSize]; file.AsStream().Read(bytes); return(bytes); } catch (Exception ex) { var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToLoadIcon.SafeFormat(ex.Message); iconItem.Errors.Add(message); _logger.LogError(ex, message); return(null); } }
// fs must contain AOC nca files in its root public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel) { _virtualFileSystem.ImportTickets(fs); foreach (var ncaPath in fs.EnumerateEntries("*.cnmt.nca", SearchOptions.Default)) { using var ncaFile = new UniqueRef <IFile>(); fs.OpenFile(ref ncaFile.Ref(), ncaPath.FullPath.ToU8Span(), OpenMode.Read); var nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); if (nca.Header.ContentType != NcaContentType.Meta) { Logger.Warning?.Print(LogClass.Application, $"{ncaPath} is not a valid metadata file"); continue; } using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel); using var cnmtFile = new UniqueRef <IFile>(); pfs0.OpenFile(ref cnmtFile.Ref(), pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read); var cnmt = new Cnmt(cnmtFile.Get.AsStream()); if (cnmt.Type != ContentMetaType.AddOnContent || (cnmt.TitleId & 0xFFFFFFFFFFFFE000) != aocBaseId) { continue; } string ncaId = BitConverter.ToString(cnmt.ContentEntries[0].NcaId).Replace("-", "").ToLower(); if (!_aocData.TryAdd(cnmt.TitleId, new AocItem(containerPath, $"{ncaId}.nca", true))) { Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {cnmt.TitleId:X16}"); } else { Logger.Info?.Print(LogClass.Application, $"Found AddOnContent with TitleId {cnmt.TitleId:X16}"); } } }
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)
public void EnsureInitialized(ContentManager contentManager) { if (_fontData == null) { _storage.ZeroFill(); uint fontOffset = 0; FontInfo CreateFont(string name) { if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename)) { string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.BuiltInSystem, NcaContentType.Data); string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(fontPath)) { byte[] data; using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open)) { Nca nca = new Nca(_device.System.KeySet, ncaFileStream); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); using var fontFile = new UniqueRef <IFile>(); romfs.OpenFile(ref fontFile.Ref(), ("/" + fontFilename).ToU8Span(), OpenMode.Read).ThrowIfFailure(); data = DecryptFont(fontFile.Get.AsStream()); } FontInfo info = new FontInfo((int)fontOffset, data.Length); WriteMagicAndSize(fontOffset, data.Length); fontOffset += 8; uint start = fontOffset; for (; fontOffset - start < data.Length; fontOffset++) { _storage.GetRef <byte>(fontOffset) = data[fontOffset - start]; } return(info); } else { if (!contentManager.TryGetSystemTitlesName(fontTitle, out string titleName)) { titleName = "Unknown"; } throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); } } else { throw new ArgumentException($"Unknown font \"{name}\"!"); } } _fontData = new Dictionary <SharedFontType, FontInfo> { { SharedFontType.JapanUsEurope, CreateFont("FontStandard") }, { SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") }, { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") }, { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") }, { SharedFontType.Korean, CreateFont("FontKorean") }, { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") } }; if (fontOffset > Horizon.FontSize) { throw new InvalidSystemResourceException("The sum of all fonts size exceed the shared memory size. " + $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. (actual size: {fontOffset} bytes)."); } } }
private void AddButton_Clicked(object sender, EventArgs args) { FileChooserNative fileChooser = new FileChooserNative("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") { SelectMultiple = true }; FileFilter filter = new FileFilter() { Name = "Switch Game DLCs" }; filter.AddPattern("*.nsp"); fileChooser.AddFilter(filter); if (fileChooser.Run() == (int)ResponseType.Accept) { foreach (string containerPath in fileChooser.Filenames) { if (!File.Exists(containerPath)) { return; } using (FileStream containerFile = File.OpenRead(containerPath)) { PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); bool containsDlc = false; _virtualFileSystem.ImportTickets(pfs); TreeIter?parentIter = null; 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 = TryCreateNca(ncaFile.Get.AsStorage(), containerPath); if (nca == null) { continue; } if (nca.Header.ContentType == NcaContentType.PublicData) { if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId) { break; } parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath); ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); containsDlc = true; } } if (!containsDlc) { GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); } } } } fileChooser.Dispose(); }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_dlcWindow").Handle) { builder.Autoconnect(this); _titleId = titleId; _virtualFileSystem = virtualFileSystem; _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json"); _baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]"; try { _dlcContainerList = JsonHelper.DeserializeFromFile <List <DlcContainer> >(_dlcJsonPath); } catch { _dlcContainerList = new List <DlcContainer>(); } _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); CellRendererToggle enableToggle = new CellRendererToggle(); enableToggle.Toggled += (sender, args) => { _dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); bool newValue = !(bool)_dlcTreeView.Model.GetValue(treeIter, 0); _dlcTreeView.Model.SetValue(treeIter, 0, newValue); if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, treeIter)) { do { _dlcTreeView.Model.SetValue(childIter, 0, newValue); }while (_dlcTreeView.Model.IterNext(ref childIter)); } }; _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); foreach (DlcContainer dlcContainer in _dlcContainerList) { if (File.Exists(dlcContainer.Path)) { // The parent tree item has its own "enabled" check box, but it's the actual // nca entries that store the enabled / disabled state. A bit of a UI inconsistency. // Maybe a tri-state check box would be better, but for now we check the parent // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. bool areAllContentPacksEnabled = dlcContainer.DlcNcaList.TrueForAll((nca) => nca.Enabled); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.Path); using FileStream containerFile = File.OpenRead(dlcContainer.Path); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); _virtualFileSystem.ImportTickets(pfs); foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.Path); if (nca != null) { ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.Path); } } } else { // DLC file moved or renamed. Allow the user to remove it without crashing the whole dialog. TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.Path}"); } } }
public byte[] GetApplicationIcon(string applicationPath) { byte[] applicationIcon = null; try { // Look for icon only if applicationPath is not a directory if (!Directory.Exists(applicationPath)) { string extension = Path.GetExtension(applicationPath).ToLower(); using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) { if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") { try { PartitionFileSystem pfs; bool isExeFs = false; if (extension == ".xci") { Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) { if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") { isExeFs = true; } } } if (isExeFs) { applicationIcon = _nspIcon; } else { // Store the ControlFS in variable called controlFs GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _); // Read the icon from the ControlFS and store it as a byte array try { using var icon = new UniqueRef <IFile>(); controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { if (entry.Name == "control.nacp") { continue; } using var icon = new UniqueRef <IFile>(); controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } if (applicationIcon != null) { break; } } if (applicationIcon == null) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } } } } catch (MissingKeyException) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } catch (InvalidDataException) { applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; } catch (Exception exception) { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); } } else if (extension == ".nro") { BinaryReader reader = new(file); byte[] Read(long position, int size) { file.Seek(position, SeekOrigin.Begin); return(reader.ReadBytes(size)); } try { file.Seek(24, SeekOrigin.Begin); int assetOffset = reader.ReadInt32(); if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") { byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); // Reads and stores game icon as byte array applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); } else { applicationIcon = _nroIcon; } } catch { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); } } else if (extension == ".nca") { applicationIcon = _ncaIcon; } // If its an NSO we just set defaults else if (extension == ".nso") { applicationIcon = _nsoIcon; } } } } catch (Exception) { Logger.Warning?.Print(LogClass.Application, $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}"); } return(applicationIcon ?? _ncaIcon); }
private void AddUpdate(string path) { if (File.Exists(path)) { using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) { PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); try { (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); if (controlNca != null && patchNca != null) { ApplicationControlProperty controlData = new ApplicationControlProperty(); using var nacpFile = new UniqueRef <IFile>(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersion.ToString()} - {path}"); radioButton.JoinGroup(_noUpdateRadioButton); _availableUpdatesBox.Add(radioButton); _radioButtonToPathDictionary.Add(radioButton, path); radioButton.Show(); radioButton.Active = true; } else { GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); } } catch (Exception exception) { GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); } } } }
public void LoadApplications(List <string> appDirs, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _desiredTitleLanguage = desiredTitleLanguage; // Builds the applications list with paths to found applications List <string> applications = new List <string>(); foreach (string appDir in appDirs) { if (!Directory.Exists(appDir)) { Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\""); continue; } foreach (string app in GetFilesInDirectory(appDir)) { if ((Path.GetExtension(app).ToLower() == ".nsp") || (Path.GetExtension(app).ToLower() == ".pfs0") || (Path.GetExtension(app).ToLower() == ".xci") || (Path.GetExtension(app).ToLower() == ".nca") || (Path.GetExtension(app).ToLower() == ".nro") || (Path.GetExtension(app).ToLower() == ".nso")) { applications.Add(app); numApplicationsFound++; } } } // Loops through applications list, creating a struct and then firing an event containing the struct for each application foreach (string applicationPath in applications) { double fileSize = new FileInfo(applicationPath).Length * 0.000000000931; string titleName = "Unknown"; string titleId = "0000000000000000"; string developer = "Unknown"; string version = "0"; byte[] applicationIcon = null; BlitStruct <ApplicationControlProperty> controlHolder = new BlitStruct <ApplicationControlProperty>(1); try { using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read)) { if ((Path.GetExtension(applicationPath).ToLower() == ".nsp") || (Path.GetExtension(applicationPath).ToLower() == ".pfs0") || (Path.GetExtension(applicationPath).ToLower() == ".xci")) { try { PartitionFileSystem pfs; bool isExeFs = false; if (Path.GetExtension(applicationPath).ToLower() == ".xci") { Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. bool hasMainNca = false; foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) { if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") { using var ncaFile = new UniqueRef <IFile>(); pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); // Some main NCAs don't have a data partition, so check if the partition exists before opening it if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) { hasMainNca = true; break; } } else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") { isExeFs = true; } } if (!hasMainNca && !isExeFs) { numApplicationsFound--; continue; } } if (isExeFs) { applicationIcon = _nspIcon; using var npdmFile = new UniqueRef <IFile>(); Result result = pfs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read); if (ResultFs.PathNotFound.Includes(result)) { Npdm npdm = new Npdm(npdmFile.Get.AsStream()); titleName = npdm.TitleName; titleId = npdm.Aci0.TitleId.ToString("x16"); } } else { // Store the ControlFS in variable called controlFs GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); ReadControlData(controlFs, controlHolder.ByteSpan); // Get the title name, title ID, developer name and version number from the NACP version = IsUpdateApplied(titleId, out string updateVersion) ? updateVersion : controlHolder.Value.DisplayVersion.ToString(); GetNameIdDeveloper(ref controlHolder.Value, out titleName, out _, out developer); // Read the icon from the ControlFS and store it as a byte array try { using var icon = new UniqueRef <IFile>(); controlFs.OpenFile(ref icon.Ref(), $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { if (entry.Name == "control.nacp") { continue; } using var icon = new UniqueRef <IFile>(); controlFs.OpenFile(ref icon.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.Get.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } if (applicationIcon != null) { break; } } if (applicationIcon == null) { applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; } } } } catch (MissingKeyException exception) { applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); } catch (InvalidDataException) { applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon; Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}"); } catch (Exception exception) { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); numApplicationsFound--; continue; } } else if (Path.GetExtension(applicationPath).ToLower() == ".nro") { BinaryReader reader = new BinaryReader(file); byte[] Read(long position, int size) { file.Seek(position, SeekOrigin.Begin); return(reader.ReadBytes(size)); } try { file.Seek(24, SeekOrigin.Begin); int assetOffset = reader.ReadInt32(); if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") { byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); ulong nacpOffset = reader.ReadUInt64(); ulong nacpSize = reader.ReadUInt64(); // Reads and stores game icon as byte array applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); // Read the NACP data Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); // Get the title name, title ID, developer name and version number from the NACP version = controlHolder.Value.DisplayVersion.ToString(); GetNameIdDeveloper(ref controlHolder.Value, out titleName, out titleId, out developer); } else { applicationIcon = _nroIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } } catch { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); numApplicationsFound--; continue; } } else if (Path.GetExtension(applicationPath).ToLower() == ".nca") { try { Nca nca = new Nca(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) { numApplicationsFound--; continue; } } catch (InvalidDataException) { Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}"); } catch { Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); numApplicationsFound--; continue; } applicationIcon = _ncaIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } // If its an NSO we just set defaults else if (Path.GetExtension(applicationPath).ToLower() == ".nso") { applicationIcon = _nsoIcon; titleName = Path.GetFileNameWithoutExtension(applicationPath); } } } catch (IOException exception) { Logger.Warning?.Print(LogClass.Application, exception.Message); numApplicationsFound--; continue; } ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId); if (appMetadata.LastPlayed != "Never" && !DateTime.TryParse(appMetadata.LastPlayed, out _)) { Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); appMetadata.LastPlayed = "Never"; } ApplicationData data = new ApplicationData { Favorite = appMetadata.Favorite, Icon = applicationIcon, TitleName = titleName, TitleId = titleId, Developer = developer, Version = version, TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed), LastPlayed = appMetadata.LastPlayed, FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + "MB" : fileSize.ToString("0.##") + "GB", Path = applicationPath, ControlHolder = controlHolder }; numApplicationsLoaded++; OnApplicationAdded(new ApplicationAddedEventArgs() { AppData = data }); OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() { NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); } OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() { NumAppsFound = numApplicationsFound, NumAppsLoaded = numApplicationsLoaded }); }
private bool IsUpdateApplied(string titleId, out string version) { string updatePath = "(unknown)"; try { (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); if (patchNca != null && controlNca != null) { ApplicationControlProperty controlData = new ApplicationControlProperty(); using var nacpFile = new UniqueRef <IFile>(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); version = controlData.DisplayVersion.ToString(); return(true); } } catch (InvalidDataException) { Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}"); } catch (MissingKeyException exception) { Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {updatePath}"); } version = ""; return(false); }
private void BuildChildItems(SectionItem parentItem) { try { const string?ROOT_PATH = "/"; var fileSystem = parentItem.FileSystem; if (fileSystem == null) { return; } var directoryEntries = SafeGetDirectoryEntries(fileSystem, ROOT_PATH, parentItem); foreach (var directoryEntry in directoryEntries) { var entryName = directoryEntry.Name; var entryPath = directoryEntry.FullPath; // NACP File if (parentItem.ParentItem.ContentType == NcaContentType.Control && string.Equals(entryName, NacpItem.NacpFileName, StringComparison.OrdinalIgnoreCase) && directoryEntry.Type == DirectoryEntryType.File) { IFile nacpFile; try { using var uniqueRefFile = new UniqueRef <IFile>(); fileSystem.OpenFile(ref uniqueRefFile.Ref(), entryPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile = uniqueRefFile.Release(); } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToOpenNacpFile.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); continue; } ApplicationControlProperty nacp; try { var blitStruct = new BlitStruct <ApplicationControlProperty>(1); nacpFile.Read(out _, 0, blitStruct.ByteSpan).ThrowIfFailure(); nacp = blitStruct.Value; } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToLoadNacpFile.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); continue; } parentItem.ChildItems.Add(new NacpItem(nacp, parentItem, directoryEntry)); } // CNMT File else if (parentItem.ParentItem.ContentType == NcaContentType.Meta && entryName.EndsWith(".cnmt", StringComparison.OrdinalIgnoreCase) && directoryEntry.Type == DirectoryEntryType.File) { IFile cnmtFile; try { using var uniqueRefFile = new UniqueRef <IFile>(); fileSystem.OpenFile(ref uniqueRefFile.Ref(), entryPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); cnmtFile = uniqueRefFile.Release(); } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToOpenCnmtFile.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); continue; } Cnmt cnmt; try { cnmt = new Cnmt(cnmtFile.AsStream()); } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToLoadCnmtFile.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); continue; } parentItem.ChildItems.Add(new CnmtItem(cnmt, parentItem, directoryEntry)); } // MAIN file else if (parentItem.ParentItem.ContentType == NcaContentType.Program && string.Equals(entryName, "main", StringComparison.OrdinalIgnoreCase) && directoryEntry.Type == DirectoryEntryType.File) { IFile nsoFile; try { using var uniqueRefFile = new UniqueRef <IFile>(); fileSystem.OpenFile(ref uniqueRefFile.Ref(), entryPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); nsoFile = uniqueRefFile.Release(); } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToOpenMainFile.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); continue; } NsoHeader?nsoHeader; try { var nsoReader = new NsoReader(); nsoReader.Initialize(nsoFile).ThrowIfFailure(); nsoHeader = nsoReader.Header; } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToLoadMainFile.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); continue; } parentItem.ChildItems.Add(new MainItem(nsoHeader.Value, parentItem, directoryEntry)); } else { var directoryEntryItem = new DirectoryEntryItem(parentItem, directoryEntry); BuildChildItems(directoryEntryItem); parentItem.ChildItems.Add(directoryEntryItem); } } } catch (Exception ex) { OnLoadingException(ex, parentItem); var message = LocalizationManager.Instance.Current.Keys.LoadingError_FailedToLoadSectionContent.SafeFormat(ex.Message); parentItem.Errors.Add(TREE_LOADING_CATEGORY, message); _logger.LogError(ex, message); } }