static void Main(string[] args) { Console.Title = "Ryujinx Console"; string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; GLib.ExceptionManager.UnhandledException += Glib_UnhandledException; Profile.Initialize(); Application.Init(); string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "RyuFs", "system", "prod.keys"); string userProfilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch", "prod.keys"); if (!File.Exists(appDataPath) && !File.Exists(userProfilePath)) { GtkDialog.CreateErrorDialog($"Key file was not found. Please refer to `KEYS.md` for more info"); } MainWindow mainWindow = new MainWindow(); mainWindow.Show(); if (args.Length == 1) { mainWindow.LoadApplication(args[0]); } Application.Run(); }
static void Main(string[] args) { Console.Title = "Ryujinx Console"; string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); GLib.ExceptionManager.UnhandledException += Glib_UnhandledException; // Initialize the configuration ConfigurationState.Initialize(); // Initialize the logger system LoggerModule.Initialize(); // Initialize Discord integration DiscordIntegrationModule.Initialize(); string configurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); // Now load the configuration as the other subsystems are now registered if (File.Exists(configurationPath)) { ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(configurationPath); ConfigurationState.Instance.Load(configurationFileFormat); } else { // No configuration, we load the default values and save it on disk ConfigurationState.Instance.LoadDefault(); ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath); } Profile.Initialize(); Application.Init(); string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Ryujinx", "system", "prod.keys"); string userProfilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch", "prod.keys"); if (!File.Exists(appDataPath) && !File.Exists(userProfilePath) && !Migration.IsMigrationNeeded()) { GtkDialog.CreateErrorDialog("Key file was not found. Please refer to `KEYS.md` for more info"); } MainWindow mainWindow = new MainWindow(); mainWindow.Show(); if (args.Length == 1) { mainWindow.LoadApplication(args[0]); } Application.Run(); }
private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_titleUpdateWindow").Handle) { _parent = parent; builder.Autoconnect(this); _titleId = titleId; _virtualFileSystem = virtualFileSystem; _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json"); _radioButtonToPathDictionary = new Dictionary <RadioButton, string>(); try { _titleUpdateWindowData = JsonHelper.DeserializeFromFile <TitleUpdateMetadata>(_updateJsonPath); } catch { _titleUpdateWindowData = new TitleUpdateMetadata { Selected = "", Paths = new List <string>() }; } _baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]"; int invalidUpdateCount = 0; foreach (string path in _titleUpdateWindowData.Paths) { if (!File.Exists(path)) { invalidUpdateCount++; continue; } AddUpdate(path); } if (invalidUpdateCount > 0) { Logger.Error?.PrintMsg(LogClass.Application, "Updates have been moved or deleted; skipping " + invalidUpdateCount + " updates"); GtkDialog.CreateErrorDialog(invalidUpdateCount + " updates have been moved or deleted; please re-add your updates."); } if (_titleUpdateWindowData.Selected == "") { _noUpdateRadioButton.Active = true; } else { foreach ((RadioButton update, var _) in _radioButtonToPathDictionary.Where(keyValuePair => keyValuePair.Value == _titleUpdateWindowData.Selected)) { update.Active = true; } } }
private Nca TryCreateNca(IStorage ncaStorage, string containerPath) { try { return(new Nca(_virtualFileSystem.KeySet, ncaStorage)); } catch (Exception exception) { GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {containerPath}"); } return(null); }
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) { ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); bool showDetails = false; Application.Invoke(delegate { try { ErrorAppletDialog msgDialog = new ErrorAppletDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons) { Title = title, Text = message, UseMarkup = true, WindowPosition = WindowPosition.CenterAlways }; msgDialog.SetDefaultSize(400, 0); msgDialog.Response += (object o, ResponseArgs args) => { if (buttons != null) { if (buttons.Length > 1) { if (args.ResponseId != (ResponseType)(buttons.Length - 1)) { showDetails = true; } } } dialogCloseEvent.Set(); msgDialog?.Dispose(); }; msgDialog.Show(); } catch (Exception ex) { GtkDialog.CreateErrorDialog($"Error displaying ErrorApplet Dialog: {ex}"); dialogCloseEvent.Set(); } }); dialogCloseEvent.WaitOne(); return(showDetails); }
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText) { ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); bool okPressed = false; bool error = false; string inputText = args.InitialText ?? ""; Application.Invoke(delegate { try { var swkbdDialog = new SwkbdAppletDialog(_parent) { Title = "Software Keyboard", Text = args.HeaderText, SecondaryText = args.SubtitleText }; swkbdDialog.InputEntry.Text = inputText; swkbdDialog.InputEntry.PlaceholderText = args.GuideText; swkbdDialog.OkButton.Label = args.SubmitText; swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); if (swkbdDialog.Run() == (int)ResponseType.Ok) { inputText = swkbdDialog.InputEntry.Text; okPressed = true; } swkbdDialog.Dispose(); } catch (Exception ex) { error = true; GtkDialog.CreateErrorDialog($"Error displaying Software Keyboard: {ex}"); } finally { dialogCloseEvent.Set(); } }); dialogCloseEvent.WaitOne(); userText = error ? null : inputText; return(error || okPressed); }
public bool DisplayMessageDialog(string title, string message) { ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); bool okPressed = false; Application.Invoke(delegate { MessageDialog msgDialog = null; try { msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) { Title = title, Text = message, UseMarkup = true }; msgDialog.SetDefaultSize(400, 0); msgDialog.Response += (object o, ResponseArgs args) => { if (args.ResponseId == ResponseType.Ok) { okPressed = true; } dialogCloseEvent.Set(); msgDialog?.Dispose(); }; msgDialog.Show(); } catch (Exception ex) { GtkDialog.CreateErrorDialog($"Error displaying Message Dialog: {ex}"); dialogCloseEvent.Set(); } }); dialogCloseEvent.WaitOne(); return(okPressed); }
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 static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) { if (Running) { return; } Running = true; mainWindow.UpdateMenuItem.Sensitive = false; int artifactIndex = -1; // Detect current platform if (OperatingSystem.IsMacOS()) { _platformExt = "osx_x64.zip"; artifactIndex = 1; } else if (OperatingSystem.IsWindows()) { _platformExt = "win_x64.zip"; artifactIndex = 2; } else if (OperatingSystem.IsLinux()) { _platformExt = "linux_x64.tar.gz"; artifactIndex = 0; } if (artifactIndex == -1) { GtkDialog.CreateErrorDialog("Your platform is not supported!"); return; } Version newVersion; Version currentVersion; try { currentVersion = Version.Parse(Program.Version); } catch { GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); return; } // Get latest version number from GitHub API try { using (HttpClient jsonClient = ConstructHttpClient()) { string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest"; // Fetch latest build information string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); JObject jsonRoot = JObject.Parse(fetchedJson); JToken assets = jsonRoot["assets"]; _buildVer = (string)jsonRoot["name"]; foreach (JToken asset in assets) { string assetName = (string)asset["name"]; string assetState = (string)asset["state"]; string downloadURL = (string)asset["browser_download_url"]; if (!assetName.StartsWith("ryujinx-headless-sdl2") && assetName.EndsWith(_platformExt)) { _buildUrl = downloadURL; if (assetState != "uploaded") { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } return; } break; } } if (_buildUrl == null) { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } return; } } } catch (Exception exception) { Logger.Error?.Print(LogClass.Application, exception.Message); GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes."); return; } try { newVersion = Version.Parse(_buildVer); } catch { GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from GitHub Release.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from GitHub Release!"); return; } if (newVersion <= currentVersion) { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } Running = false; mainWindow.UpdateMenuItem.Sensitive = true; return; } // Fetch build size information to learn chunk sizes. using (HttpClient buildSizeClient = ConstructHttpClient()) { try { buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); _buildSize = message.Content.Headers.ContentRange.Length.Value; } catch (Exception ex) { Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); _buildSize = -1; } } // Show a message asking the user if they want to update UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl); updateDialog.Show(); }
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")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.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(); }
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) { if (Running) { return; } Running = true; mainWindow.UpdateMenuItem.Sensitive = false; int artifactIndex = -1; // Detect current platform if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { _platformExt = "osx_x64.zip"; artifactIndex = 1; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _platformExt = "win_x64.zip"; artifactIndex = 2; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { _platformExt = "linux_x64.tar.gz"; artifactIndex = 0; } if (artifactIndex == -1) { GtkDialog.CreateErrorDialog("Your platform is not supported!"); return; } Version newVersion; Version currentVersion; try { currentVersion = Version.Parse(Program.Version); } catch { GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); return; } // Get latest version number from Appveyor try { using (WebClient jsonClient = new WebClient()) { // Fetch latest build information string fetchedJson = await jsonClient.DownloadStringTaskAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master"); JObject jsonRoot = JObject.Parse(fetchedJson); JToken buildToken = jsonRoot["build"]; _jobId = (string)buildToken["jobs"][0]["jobId"]; _buildVer = (string)buildToken["version"]; _buildUrl = $"{AppveyorApiUrl}/buildjobs/{_jobId}/artifacts/ryujinx-{_buildVer}-{_platformExt}"; // If build not done, assume no new update are availaible. if ((string)buildToken["jobs"][0]["status"] != "success") { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } return; } } } catch (Exception exception) { Logger.Error?.Print(LogClass.Application, exception.Message); GtkDialog.CreateErrorDialog("An error has occurred when trying to get release information from AppVeyor."); return; } try { newVersion = Version.Parse(_buildVer); } catch { GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from AppVeyor.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from AppVeyor!"); return; } if (newVersion <= currentVersion) { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } Running = false; mainWindow.UpdateMenuItem.Sensitive = true; return; } // Fetch build size information to learn chunk sizes. using (WebClient buildSizeClient = new WebClient()) { try { buildSizeClient.Headers.Add("Range", "bytes=0-0"); await buildSizeClient.DownloadDataTaskAsync(new Uri(_buildUrl)); string contentRange = buildSizeClient.ResponseHeaders["Content-Range"]; _buildSize = long.Parse(contentRange.Substring(contentRange.IndexOf('/') + 1)); } catch (Exception ex) { Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, will use single-threaded updater"); _buildSize = -1; } } // Show a message asking the user if they want to update UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl); updateDialog.Show(); }
public void LoadApplications(List <string> appDirs, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _loadingError = false; _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") { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.ContentType == NcaContentType.Program && !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; Result result = pfs.OpenFile(out IFile npdmFile, "/main.npdm".ToU8Span(), OpenMode.Read); if (ResultFs.PathNotFound.Includes(result)) { Npdm npdm = new Npdm(npdmFile.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 { controlFs.OpenFile(out IFile icon, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.AsStream().CopyTo(stream); applicationIcon = stream.ToArray(); } } catch (HorizonResultException) { foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) { if (entry.Name == "control.nacp") { continue; } controlFs.OpenFile(out IFile icon, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using (MemoryStream stream = new MemoryStream()) { icon.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--; _loadingError = true; 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.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--; _loadingError = true; 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--; _loadingError = true; 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 }); if (_loadingError) { Gtk.Application.Invoke(delegate { GtkDialog.CreateErrorDialog("One or more files encountered could not be loaded, check logs for more info."); }); } }
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) { if (Running) { return; } Running = true; mainWindow.UpdateMenuItem.Sensitive = false; // Detect current platform if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { _platformExt = "osx_x64.zip"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _platformExt = "win_x64.zip"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { _platformExt = "linux_x64.tar.gz"; } Version newVersion; Version currentVersion; try { currentVersion = Version.Parse(Program.Version); } catch { GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); return; } // Get latest version number from Appveyor try { using (WebClient jsonClient = new WebClient()) { string fetchedJson = await jsonClient.DownloadStringTaskAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master"); JObject jsonRoot = JObject.Parse(fetchedJson); JToken buildToken = jsonRoot["build"]; _jobId = (string)buildToken["jobs"][0]["jobId"]; _buildVer = (string)buildToken["version"]; _buildUrl = $"{AppveyorApiUrl}/buildjobs/{_jobId}/artifacts/ryujinx-{_buildVer}-{_platformExt}"; // If build not done, assume no new update are availaible. if ((string)buildToken["jobs"][0]["status"] != "success") { if (showVersionUpToDate) { GtkDialog.CreateInfoDialog("Ryujinx - Updater", "You are already using the most updated version of Ryujinx!", ""); } return; } } } catch (Exception exception) { Logger.Error?.Print(LogClass.Application, exception.Message); GtkDialog.CreateErrorDialog("An error has occurred when trying to get release information from AppVeyor."); return; } try { newVersion = Version.Parse(_buildVer); } catch { GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from AppVeyor.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from AppVeyor!"); return; } if (newVersion <= currentVersion) { if (showVersionUpToDate) { GtkDialog.CreateInfoDialog("Ryujinx - Updater", "You are already using the most updated version of Ryujinx!", ""); } Running = false; mainWindow.UpdateMenuItem.Sensitive = true; return; } // Show a message asking the user if they want to update UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl); updateDialog.Show(); }
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) { if (Running) { return; } Running = true; mainWindow.UpdateMenuItem.Sensitive = false; int artifactIndex = -1; // Detect current platform if (OperatingSystem.IsMacOS()) { _platformExt = "osx_x64.zip"; artifactIndex = 1; } else if (OperatingSystem.IsWindows()) { _platformExt = "win_x64.zip"; artifactIndex = 2; } else if (OperatingSystem.IsLinux()) { _platformExt = "linux_x64.tar.gz"; artifactIndex = 0; } if (artifactIndex == -1) { GtkDialog.CreateErrorDialog("Your platform is not supported!"); return; } Version newVersion; Version currentVersion; try { currentVersion = Version.Parse(Program.Version); } catch { GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); return; } // Get latest version number from Appveyor try { using (HttpClient jsonClient = new HttpClient()) { // Fetch latest build information string fetchedJson = await jsonClient.GetStringAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master"); JObject jsonRoot = JObject.Parse(fetchedJson); JToken buildToken = jsonRoot["build"]; _jobId = (string)buildToken["jobs"][0]["jobId"]; _buildVer = (string)buildToken["version"]; _buildUrl = $"{AppveyorApiUrl}/buildjobs/{_jobId}/artifacts/ryujinx-{_buildVer}-{_platformExt}"; // If build not done, assume no new update are availaible. if ((string)buildToken["jobs"][0]["status"] != "success") { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } return; } } } catch (Exception exception) { Logger.Error?.Print(LogClass.Application, exception.Message); GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from AppVeyor. This can be caused if a new release is being compiled by AppVeyor. Try again in a few minutes."); return; } try { newVersion = Version.Parse(_buildVer); } catch { GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from AppVeyor.", "Cancelling Update!"); Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from AppVeyor!"); return; } if (newVersion <= currentVersion) { if (showVersionUpToDate) { GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } Running = false; mainWindow.UpdateMenuItem.Sensitive = true; return; } // Fetch build size information to learn chunk sizes. using (HttpClient buildSizeClient = new HttpClient()) { try { buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); _buildSize = message.Content.Headers.ContentRange.Length.Value; } catch (Exception ex) { Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); _buildSize = -1; } } // Show a message asking the user if they want to update UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl); updateDialog.Show(); }
public static void BeginParse() { try { // Detect current platform if (RuntimeInformation.ProcessArchitecture == Architecture.X64) { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { _platformExt = "osx_x64.zip"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _platformExt = "win_x64.zip"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { _platformExt = "linux_x64.tar.gz"; } } else { Logger.PrintError(LogClass.Application, $"You are using an operating system architecture ({RuntimeInformation.ProcessArchitecture.ToString()}) not compatible with Ryujinx."); GtkDialog.CreateErrorDialog($"You are using an operating system architecture ({RuntimeInformation.ProcessArchitecture.ToString()}) not compatible with Ryujinx."); return; } // Begin the Appveyor parsing WebClient jsonClient = new WebClient(); string fetchedJSON = jsonClient.DownloadString(_masterUrl); JObject jsonRoot = JObject.Parse(fetchedJSON); JToken _buildToken = jsonRoot["build"]; _jobId = (string)_buildToken["jobs"][0]["jobId"]; _buildVer = (string)_buildToken["version"]; _buildUrl = "https://ci.appveyor.com/api/buildjobs/" + _jobId + "/artifacts/ryujinx-" + _buildVer + "-" + _platformExt; if (!Directory.Exists(localAppPath)) { Directory.CreateDirectory(localAppPath); } // Get Version from app.config to compare versions Version newVersion = Version.Parse(_buildVer); Version currentVersion = Version.Parse(ConfigurationManager.AppSettings["Version"]); if (newVersion < currentVersion) { GtkDialog.CreateInfoDialog("Update", "Ryujinx - Updater", "You are already using the most updated version of Ryujinx!", ""); return; } // Show a message asking the user if they want to update using (MessageDialog dialog = GtkDialog.CreateAcceptDialog("Update", "Ryujinx - Update", "Would you like to update?", "Version " + _buildVer + " is available.")) { if (dialog.Run() == (int)ResponseType.Yes) { try { // Start Updater.exe string updaterPath = Path.Combine(RyuDir, "Updater.exe"); ProcessStartInfo startInfo = new ProcessStartInfo(updaterPath); startInfo.Arguments = _buildUrl + " " + _buildVer; startInfo.UseShellExecute = true; Process.Start(startInfo); Application.Quit(); } catch (Exception ex) { GtkDialog.CreateErrorDialog(ex.Message); } } } } catch (Exception ex) { Logger.PrintError(LogClass.Application, ex.Message); GtkDialog.CreateErrorDialog("Update failed to grab or parse the information.\nPlease try at a later time, or report the error to our GitHub."); } }
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); int invalidDlcCount = 0; foreach (DlcContainer dlcContainer in _dlcContainerList) { if (!File.Exists(dlcContainer.Path)) { invalidDlcCount++; continue; // skip invalid dlc (next save, no invalid dlc!) } TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", dlcContainer.Path); using FileStream containerFile = File.OpenRead(dlcContainer.Path); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); _virtualFileSystem.ImportTickets(pfs); foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) { pfs.OpenFile(out IFile ncaFile, dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = TryCreateNca(ncaFile.AsStorage(), dlcContainer.Path); if (nca != null) { ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.Path); } } } if (invalidDlcCount > 0) { Logger.Error?.PrintMsg(LogClass.Application, "DLC files have been moved or deleted; skipping " + invalidDlcCount + " DLC files"); GtkDialog.CreateErrorDialog(invalidDlcCount + " DLC files have been moved or deleted; please re-add your DLC."); } }
private InputConfig GetValues() { if (_inputDevice.ActiveId.StartsWith("keyboard")) { Enum.TryParse(_lStickUp.Label, out Key lStickUp); Enum.TryParse(_lStickDown.Label, out Key lStickDown); Enum.TryParse(_lStickLeft.Label, out Key lStickLeft); Enum.TryParse(_lStickRight.Label, out Key lStickRight); Enum.TryParse(_lStickButton.Label, out Key lStickButton); Enum.TryParse(_dpadUp.Label, out Key lDPadUp); Enum.TryParse(_dpadDown.Label, out Key lDPadDown); Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft); Enum.TryParse(_dpadRight.Label, out Key lDPadRight); Enum.TryParse(_minus.Label, out Key lButtonMinus); Enum.TryParse(_l.Label, out Key lButtonL); Enum.TryParse(_zL.Label, out Key lButtonZl); Enum.TryParse(_lSl.Label, out Key lButtonSl); Enum.TryParse(_lSr.Label, out Key lButtonSr); Enum.TryParse(_rStickUp.Label, out Key rStickUp); Enum.TryParse(_rStickDown.Label, out Key rStickDown); Enum.TryParse(_rStickLeft.Label, out Key rStickLeft); Enum.TryParse(_rStickRight.Label, out Key rStickRight); Enum.TryParse(_rStickButton.Label, out Key rStickButton); Enum.TryParse(_a.Label, out Key rButtonA); Enum.TryParse(_b.Label, out Key rButtonB); Enum.TryParse(_x.Label, out Key rButtonX); Enum.TryParse(_y.Label, out Key rButtonY); Enum.TryParse(_plus.Label, out Key rButtonPlus); Enum.TryParse(_r.Label, out Key rButtonR); Enum.TryParse(_zR.Label, out Key rButtonZr); Enum.TryParse(_rSl.Label, out Key rButtonSl); Enum.TryParse(_rSr.Label, out Key rButtonSr); int.TryParse(_dsuServerPort.Buffer.Text, out int port); return(new KeyboardConfig { Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]), ControllerType = Enum.Parse <ControllerType>(_controllerType.ActiveId), PlayerIndex = _playerIndex, LeftJoycon = new NpadKeyboardLeft { StickUp = lStickUp, StickDown = lStickDown, StickLeft = lStickLeft, StickRight = lStickRight, StickButton = lStickButton, DPadUp = lDPadUp, DPadDown = lDPadDown, DPadLeft = lDPadLeft, DPadRight = lDPadRight, ButtonMinus = lButtonMinus, ButtonL = lButtonL, ButtonZl = lButtonZl, ButtonSl = lButtonSl, ButtonSr = lButtonSr }, RightJoycon = new NpadKeyboardRight { StickUp = rStickUp, StickDown = rStickDown, StickLeft = rStickLeft, StickRight = rStickRight, StickButton = rStickButton, ButtonA = rButtonA, ButtonB = rButtonB, ButtonX = rButtonX, ButtonY = rButtonY, ButtonPlus = rButtonPlus, ButtonR = rButtonR, ButtonZr = rButtonZr, ButtonSl = rButtonSl, ButtonSr = rButtonSr }, EnableMotion = _enableMotion.Active, MirrorInput = _mirrorInput.Active, Slot = (int)_slotNumber.Value, AltSlot = (int)_altSlotNumber.Value, Sensitivity = (int)_sensitivity.Value, GyroDeadzone = _gyroDeadzone.Value, DsuServerHost = _dsuServerHost.Buffer.Text, DsuServerPort = port }); } if (_inputDevice.ActiveId.StartsWith("controller")) { Enum.TryParse(_lStickX.Label, out ControllerInputId lStickX); Enum.TryParse(_lStickY.Label, out ControllerInputId lStickY); Enum.TryParse(_lStickButton.Label, out ControllerInputId lStickButton); Enum.TryParse(_minus.Label, out ControllerInputId lButtonMinus); Enum.TryParse(_l.Label, out ControllerInputId lButtonL); Enum.TryParse(_zL.Label, out ControllerInputId lButtonZl); Enum.TryParse(_lSl.Label, out ControllerInputId lButtonSl); Enum.TryParse(_lSr.Label, out ControllerInputId lButtonSr); Enum.TryParse(_dpadUp.Label, out ControllerInputId lDPadUp); Enum.TryParse(_dpadDown.Label, out ControllerInputId lDPadDown); Enum.TryParse(_dpadLeft.Label, out ControllerInputId lDPadLeft); Enum.TryParse(_dpadRight.Label, out ControllerInputId lDPadRight); Enum.TryParse(_rStickX.Label, out ControllerInputId rStickX); Enum.TryParse(_rStickY.Label, out ControllerInputId rStickY); Enum.TryParse(_rStickButton.Label, out ControllerInputId rStickButton); Enum.TryParse(_a.Label, out ControllerInputId rButtonA); Enum.TryParse(_b.Label, out ControllerInputId rButtonB); Enum.TryParse(_x.Label, out ControllerInputId rButtonX); Enum.TryParse(_y.Label, out ControllerInputId rButtonY); Enum.TryParse(_plus.Label, out ControllerInputId rButtonPlus); Enum.TryParse(_r.Label, out ControllerInputId rButtonR); Enum.TryParse(_zR.Label, out ControllerInputId rButtonZr); Enum.TryParse(_rSl.Label, out ControllerInputId rButtonSl); Enum.TryParse(_rSr.Label, out ControllerInputId rButtonSr); int.TryParse(_dsuServerPort.Buffer.Text, out int port); return(new ControllerConfig { Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]), ControllerType = Enum.Parse <ControllerType>(_controllerType.ActiveId), PlayerIndex = _playerIndex, DeadzoneLeft = (float)_controllerDeadzoneLeft.Value, DeadzoneRight = (float)_controllerDeadzoneRight.Value, TriggerThreshold = (float)_controllerTriggerThreshold.Value, LeftJoycon = new NpadControllerLeft { InvertStickX = _invertLStickX.Active, StickX = lStickX, InvertStickY = _invertLStickY.Active, StickY = lStickY, StickButton = lStickButton, ButtonMinus = lButtonMinus, ButtonL = lButtonL, ButtonZl = lButtonZl, ButtonSl = lButtonSl, ButtonSr = lButtonSr, DPadUp = lDPadUp, DPadDown = lDPadDown, DPadLeft = lDPadLeft, DPadRight = lDPadRight }, RightJoycon = new NpadControllerRight { InvertStickX = _invertRStickX.Active, StickX = rStickX, InvertStickY = _invertRStickY.Active, StickY = rStickY, StickButton = rStickButton, ButtonA = rButtonA, ButtonB = rButtonB, ButtonX = rButtonX, ButtonY = rButtonY, ButtonPlus = rButtonPlus, ButtonR = rButtonR, ButtonZr = rButtonZr, ButtonSl = rButtonSl, ButtonSr = rButtonSr }, EnableMotion = _enableMotion.Active, MirrorInput = _mirrorInput.Active, Slot = (int)_slotNumber.Value, AltSlot = (int)_altSlotNumber.Value, Sensitivity = (int)_sensitivity.Value, GyroDeadzone = _gyroDeadzone.Value, DsuServerHost = _dsuServerHost.Buffer.Text, DsuServerPort = port }); } if (!_inputDevice.ActiveId.StartsWith("disabled")) { GtkDialog.CreateErrorDialog("Some fields entered where invalid and therefore your config was not saved."); } return(null); }