private void Window_Close(object sender, DeleteEventArgs args) { if (!_gameLoaded || GtkDialog.CreateExitDialog()) { End(); } else { args.RetVal = true; } }
private void Window_Close(object sender, DeleteEventArgs args) { if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) { End(); } else { args.RetVal = true; } }
private void AddUpdate(string path, bool showErrorDialog = true) { 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(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.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 (InvalidDataException exception) { Logger.Error?.Print(LogClass.Application, $"{exception.Message}. Errored File: {path}"); if (showErrorDialog) { GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing."); } } catch (MissingKeyException exception) { Logger.Error?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {path}"); if (showErrorDialog) { GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", $"Your key set is missing a key with the name: {exception.Name}"); } } } } }
private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct <ApplicationControlProperty> controlHolder, SaveDataFilter filter, out ulong saveDataId) { saveDataId = default; Result result = _virtualFileSystem.FsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); if (ResultFs.TargetNotFound.Includes(result)) { // Savedata was not found. Ask the user if they want to create it using MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null) { Title = "PangoNX Debugger", Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"), Text = $"There is no savedata for {titleName} [{titleId:x16}]", SecondaryText = "Would you like to create savedata for this game?", WindowPosition = WindowPosition.Center }; if (messageDialog.Run() != (int)ResponseType.Yes) { return(false); } ref ApplicationControlProperty control = ref controlHolder.Value; if (LibHac.Util.IsEmpty(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.PrintWarning(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } Uid user = new Uid(1, 0); result = EnsureApplicationSaveData(_virtualFileSystem.FsClient, out _, new TitleId(titleId), ref control, ref user); if (result.IsFailure()) { GtkDialog.CreateErrorDialog($"There was an error creating the specified savedata: {result.ToStringWithName()}"); return(false); } // Try to find the savedata again after creating it result = _virtualFileSystem.FsClient.FindSaveDataWithFilter(out saveDataInfo, SaveDataSpaceId.User, ref filter); }
public void HandleScreenState(KeyboardState keyboard) { bool toggleFullscreen = keyboard.IsKeyDown(OpenTK.Input.Key.F11) || ((keyboard.IsKeyDown(OpenTK.Input.Key.AltLeft) || keyboard.IsKeyDown(OpenTK.Input.Key.AltRight)) && keyboard.IsKeyDown(OpenTK.Input.Key.Enter)) || keyboard.IsKeyDown(OpenTK.Input.Key.Escape); bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen); if (toggleFullscreen != _toggleFullscreen) { if (toggleFullscreen) { if (fullScreenToggled) { ParentWindow.Unfullscreen(); (Toplevel as MainWindow)?.ToggleExtraWidgets(true); } else { if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape)) { if (GtkDialog.CreateExitDialog()) { Exit(); } } else { ParentWindow.Fullscreen(); (Toplevel as MainWindow)?.ToggleExtraWidgets(false); } } } } _toggleFullscreen = toggleFullscreen; bool toggleDockedMode = keyboard.IsKeyDown(OpenTK.Input.Key.F9); if (toggleDockedMode != _toggleDockedMode) { if (toggleDockedMode) { ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; } } _toggleDockedMode = toggleDockedMode; }
private void Update_Pressed(object sender, EventArgs args) { string ryuUpdater = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "RyuUpdater.exe"); try { Process.Start(new ProcessStartInfo(ryuUpdater, "/U") { UseShellExecute = true }); } catch (System.ComponentModel.Win32Exception) { GtkDialog.CreateErrorDialog("Update canceled by user or updater was not found"); } }
private Nca TryCreateNca(IStorage ncaStorage, string containerPath) { try { return(new Nca(_virtualFileSystem.KeySet, ncaStorage)); } catch (InvalidDataException exception) { Logger.Error?.Print(LogClass.Application, $"{exception.Message}. Errored File: {containerPath}"); GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add DLC Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing."); } catch (MissingKeyException exception) { Logger.Error?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {containerPath}"); GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add DLC Failed!", $"Your key set is missing a key with the name: {exception.Name}"); } return(null); }
public void HandleScreenState(KeyboardState keyboard) { bool toggleFullscreen = keyboard.IsKeyDown(OpenTK.Input.Key.F11) || ((keyboard.IsKeyDown(OpenTK.Input.Key.AltLeft) || keyboard.IsKeyDown(OpenTK.Input.Key.AltRight)) && keyboard.IsKeyDown(OpenTK.Input.Key.Enter)) || keyboard.IsKeyDown(OpenTK.Input.Key.Escape); bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen); if (toggleFullscreen != _toggleFullscreen) { if (toggleFullscreen) { if (fullScreenToggled) { ParentWindow.Unfullscreen(); (Toplevel as MainWindow)?.ToggleExtraWidgets(true); } else { if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape)) { if (GtkDialog.CreateChoiceDialog("Ryujinx - Exit", "Are you sure you want to stop emulation?", "All unsaved data will be lost!")) { Exit(); } } else { ParentWindow.Fullscreen(); (Toplevel as MainWindow)?.ToggleExtraWidgets(false); } } } } _toggleFullscreen = toggleFullscreen; }
private void ProfileRemove_Activated(object sender, EventArgs args) { ((ToggleButton)sender).SetStateFlags(0, true); if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) { return; } MessageDialog confirmDialog = GtkDialog.CreateConfirmationDialog("Deleting Profile", "This action is irreversible, are your sure you want to continue?"); if (confirmDialog.Run() == (int)ResponseType.Yes) { string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId); if (File.Exists(path)) { File.Delete(path); } SetProfiles(); } }
public static void LoadApplications(List <string> appDirs, VirtualFileSystem virtualFileSystem, Language desiredTitleLanguage) { int numApplicationsFound = 0; int numApplicationsLoaded = 0; _loadingError = false; _virtualFileSystem = virtualFileSystem; _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.PrintWarning(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"; string saveDataPath = null; 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); // Creates NACP class from the NACP file controlFs.OpenFile(out IFile controlNacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); // 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.PrintWarning(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.PrintWarning(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.PrintWarning(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); Logger.PrintDebug(LogClass.Application, exception.ToString()); 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.PrintWarning(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.PrintWarning(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.PrintWarning(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.PrintWarning(LogClass.Application, exception.Message); numApplicationsFound--; _loadingError = true; continue; } ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId); if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNum)) { SaveDataFilter filter = new SaveDataFilter(); filter.SetUserId(new UserId(1, 0)); filter.SetProgramId(new TitleId(titleIdNum)); Result result = virtualFileSystem.FsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); if (result.IsSuccess()) { saveDataPath = Path.Combine(virtualFileSystem.GetNandPath(), "user", "save", saveDataInfo.SaveDataId.ToString("x16")); } } 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, SaveDataPath = saveDataPath, 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."); }); } }
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); 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 } }); } 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); 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 } }); } if (!_inputDevice.ActiveId.StartsWith("disabled")) { GtkDialog.CreateErrorDialog("Some fields entered where invalid and therefore your config was not saved."); } return(null); }
public void LoadApplication(string path) { if (_gameLoaded) { GtkDialog.CreateInfoDialog("A game has already been loaded", "Please close it first and try again."); } else { PerformanceCheck(); Logger.RestartTime(); InitializeSwitchInstance(); UpdateGraphicsConfig(); SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); bool isDirectory = Directory.Exists(path); if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError)) { if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion)) { if (userError == UserError.NoFirmware) { string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})"; ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run(); if (responseDialog != ResponseType.Yes) { UserErrorDialog.CreateUserErrorDialog(userError); _emulationContext.Dispose(); return; } } if (!SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _)) { UserErrorDialog.CreateUserErrorDialog(userError); _emulationContext.Dispose(); return; } // Tell the user that we installed a firmware for them. if (userError == UserError.NoFirmware) { firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); RefreshFirmwareLabel(); string message = $"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start."; GtkDialog.CreateInfoDialog($"Firmware {firmwareVersion.VersionString} was installed", message); } } else { UserErrorDialog.CreateUserErrorDialog(userError); _emulationContext.Dispose(); return; } } Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); if (Directory.Exists(path)) { string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); if (romFsFiles.Length == 0) { romFsFiles = Directory.GetFiles(path, "*.romfs"); } if (romFsFiles.Length > 0) { Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); _emulationContext.LoadCart(path, romFsFiles[0]); } else { Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); _emulationContext.LoadCart(path); } } else if (File.Exists(path)) { switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) { case ".xci": Logger.Info?.Print(LogClass.Application, "Loading as XCI."); _emulationContext.LoadXci(path); break; case ".nca": Logger.Info?.Print(LogClass.Application, "Loading as NCA."); _emulationContext.LoadNca(path); break; case ".nsp": case ".pfs0": Logger.Info?.Print(LogClass.Application, "Loading as NSP."); _emulationContext.LoadNsp(path); break; default: Logger.Info?.Print(LogClass.Application, "Loading as homebrew."); try { _emulationContext.LoadProgram(path); } catch (ArgumentOutOfRangeException) { Logger.Error?.Print(LogClass.Application, "The file which you have specified is unsupported by Ryujinx."); } break; } } else { Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); _emulationContext.Dispose(); return; } _currentEmulatedGamePath = path; _deviceExitStatus.Reset(); Translator.IsReadyForTranslation.Reset(); #if MACOS_BUILD CreateGameWindow(); #else Thread windowThread = new Thread(() => { CreateGameWindow(); }) { Name = "GUI.WindowThread" }; windowThread.Start(); #endif _gameLoaded = true; _stopEmulation.Sensitive = true; _simulateWakeUpMessage.Sensitive = true; _firmwareInstallFile.Sensitive = false; _firmwareInstallDirectory.Sensitive = false; DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Application.TitleIdText, _emulationContext.Application.TitleName); _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Application.TitleIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); } }
private void HandleInstallerDialog(FileChooserDialog fileChooser) { if (fileChooser.Run() == (int)ResponseType.Accept) { try { string filename = fileChooser.Filename; fileChooser.Dispose(); SystemVersion firmwareVersion = _contentManager.VerifyFirmwarePackage(filename); string dialogTitle = $"Install Firmware {firmwareVersion.VersionString}"; if (firmwareVersion == null) { GtkDialog.CreateErrorDialog($"A valid system firmware was not found in {filename}."); return; } SystemVersion currentVersion = _contentManager.GetCurrentFirmwareVersion(); string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed."; if (currentVersion != null) { dialogMessage += $"\n\nThis will replace the current system version {currentVersion.VersionString}. "; } dialogMessage += "\n\nDo you want to continue?"; ResponseType responseInstallDialog = (ResponseType)GtkDialog.CreateConfirmationDialog(dialogTitle, dialogMessage).Run(); MessageDialog waitingDialog = GtkDialog.CreateWaitingDialog(dialogTitle, "Installing firmware..."); if (responseInstallDialog == ResponseType.Yes) { Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); Thread thread = new Thread(() => { Application.Invoke(delegate { waitingDialog.Run(); }); try { _contentManager.InstallFirmware(filename); Application.Invoke(delegate { waitingDialog.Dispose(); string message = $"System version {firmwareVersion.VersionString} successfully installed."; GtkDialog.CreateInfoDialog(dialogTitle, message); Logger.Info?.Print(LogClass.Application, message); }); } catch (Exception ex) { Application.Invoke(delegate { waitingDialog.Dispose(); GtkDialog.CreateErrorDialog(ex.Message); }); } finally { RefreshFirmwareLabel(); } }); thread.Name = "GUI.FirmwareInstallerThread"; thread.Start(); } } catch (LibHac.MissingKeyException ex) { Logger.Error?.Print(LogClass.Application, ex.ToString()); UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys); } catch (Exception ex) { GtkDialog.CreateErrorDialog(ex.Message); } } else { fileChooser.Dispose(); } }
private void HandleScreenState(KeyboardStateSnapshot keyboard) { bool toggleFullscreen = keyboard.IsPressed(Key.F11) || ((keyboard.IsPressed(Key.AltLeft) || keyboard.IsPressed(Key.AltRight)) && keyboard.IsPressed(Key.Enter)) || keyboard.IsPressed(Key.Escape); bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen); if (toggleFullscreen != _toggleFullscreen) { if (toggleFullscreen) { if (fullScreenToggled) { ParentWindow.Unfullscreen(); (Toplevel as MainWindow)?.ToggleExtraWidgets(true); } else { if (keyboard.IsPressed(Key.Escape)) { if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) { Exit(); } } else { ParentWindow.Fullscreen(); (Toplevel as MainWindow)?.ToggleExtraWidgets(false); } } } } _toggleFullscreen = toggleFullscreen; bool toggleDockedMode = keyboard.IsPressed(Key.F9); if (toggleDockedMode != _toggleDockedMode) { if (toggleDockedMode) { ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; } } _toggleDockedMode = toggleDockedMode; if (_hideCursorOnIdle && !ConfigurationState.Instance.Hid.EnableMouse) { long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; } if (ConfigurationState.Instance.Hid.EnableMouse && _isMouseInClient) { Window.Cursor = _invisibleCursor; } }
private void ExtractSection(NcaSectionType ncaSectionType) { FileChooserDialog fileChooser = new FileChooserDialog("Choose the folder to extract into", null, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Extract", ResponseType.Accept); fileChooser.SetPosition(WindowPosition.Center); int response = fileChooser.Run(); string destination = fileChooser.Filename; fileChooser.Dispose(); if (response == (int)ResponseType.Accept) { Thread extractorThread = new Thread(() => { string sourceFile = _gameTableStore.GetValue(_rowIter, 9).ToString(); Gtk.Application.Invoke(delegate { _dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null) { Title = "Ryujinx - NCA Section Extractor", Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"), SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(sourceFile)}...", WindowPosition = WindowPosition.Center }; int dialogResponse = _dialog.Run(); if (dialogResponse == (int)ResponseType.Cancel || dialogResponse == (int)ResponseType.DeleteEvent) { _cancel = true; _dialog.Dispose(); } }); using (FileStream file = new FileStream(sourceFile, FileMode.Open, FileAccess.Read)) { Nca mainNca = null; Nca patchNca = null; if ((System.IO.Path.GetExtension(sourceFile).ToLower() == ".nsp") || (System.IO.Path.GetExtension(sourceFile).ToLower() == ".pfs0") || (System.IO.Path.GetExtension(sourceFile).ToLower() == ".xci")) { PartitionFileSystem pfs; if (System.IO.Path.GetExtension(sourceFile) == ".xci") { Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); pfs = xci.OpenPartition(XciPartitionType.Secure); } else { pfs = new PartitionFileSystem(file.AsStorage()); } foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) { pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); if (nca.Header.ContentType == NcaContentType.Program) { int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } else { mainNca = nca; } } } } else if (System.IO.Path.GetExtension(sourceFile).ToLower() == ".nca") { mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); } if (mainNca == null) { Logger.PrintError(LogClass.Application, "Extraction failed. The main NCA was not present in the selected file."); Gtk.Application.Invoke(delegate { GtkDialog.CreateErrorDialog("Extraction failed. The main NCA was not present in the selected file."); }); return; } int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); IFileSystem ncaFileSystem = patchNca != null ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); FileSystemClient fsClient = _virtualFileSystem.FsClient; string source = DateTime.Now.ToFileTime().ToString().Substring(10); string output = DateTime.Now.ToFileTime().ToString().Substring(10); fsClient.Register(source.ToU8Span(), ncaFileSystem); fsClient.Register(output.ToU8Span(), new LocalFileSystem(destination)); (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); if (!canceled) { if (resultCode.Value.IsFailure()) { Logger.PrintError(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); Gtk.Application.Invoke(delegate { _dialog?.Dispose(); GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information."); }); } else if (resultCode.Value.IsSuccess()) { Gtk.Application.Invoke(delegate { _dialog?.Dispose(); MessageDialog dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) { Title = "Ryujinx - NCA Section Extractor", Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"), SecondaryText = "Extraction has completed successfully.", WindowPosition = WindowPosition.Center }; dialog.Run(); dialog.Dispose(); }); } } fsClient.Unmount(source.ToU8Span()); fsClient.Unmount(output.ToU8Span()); } }); extractorThread.Name = "GUI.NcaSectionExtractorThread"; extractorThread.IsBackground = true; extractorThread.Start(); } }
internal void LoadApplication(string path) { if (_gameLoaded) { GtkDialog.CreateErrorDialog("A game has already been loaded. Please close the emulator and try again"); } else { Logger.RestartTime(); // TODO: Move this somewhere else + reloadable? GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; if (Directory.Exists(path)) { string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); if (romFsFiles.Length == 0) { romFsFiles = Directory.GetFiles(path, "*.romfs"); } if (romFsFiles.Length > 0) { Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS."); _device.LoadCart(path, romFsFiles[0]); } else { Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS."); _device.LoadCart(path); } } else if (File.Exists(path)) { switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) { case ".xci": Logger.PrintInfo(LogClass.Application, "Loading as XCI."); _device.LoadXci(path); break; case ".nca": Logger.PrintInfo(LogClass.Application, "Loading as NCA."); _device.LoadNca(path); break; case ".nsp": case ".pfs0": Logger.PrintInfo(LogClass.Application, "Loading as NSP."); _device.LoadNsp(path); break; default: Logger.PrintInfo(LogClass.Application, "Loading as homebrew."); try { _device.LoadProgram(path); } catch (ArgumentOutOfRangeException) { Logger.PrintError(LogClass.Application, "The file which you have specified is unsupported by Ryujinx."); } break; } } else { Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); End(); } #if MACOS_BUILD CreateGameWindow(); #else new Thread(CreateGameWindow).Start(); #endif _gameLoaded = true; _stopEmulation.Sensitive = true; DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleId, _device.System.TitleName); string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui"); string metadataFile = System.IO.Path.Combine(metadataFolder, "metadata.json"); IJsonFormatterResolver resolver = CompositeResolver.Create(new[] { StandardResolver.AllowPrivateSnakeCase }); ApplicationMetadata appMetadata; if (!File.Exists(metadataFile)) { Directory.CreateDirectory(metadataFolder); appMetadata = new ApplicationMetadata { Favorite = false, TimePlayed = 0, LastPlayed = "Never" }; byte[] data = JsonSerializer.Serialize(appMetadata, resolver); File.WriteAllText(metadataFile, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson()); } using (Stream stream = File.OpenRead(metadataFile)) { appMetadata = JsonSerializer.Deserialize <ApplicationMetadata>(stream, resolver); } appMetadata.LastPlayed = DateTime.UtcNow.ToString(); byte[] saveData = JsonSerializer.Serialize(appMetadata, resolver); File.WriteAllText(metadataFile, Encoding.UTF8.GetString(saveData, 0, saveData.Length).PrettyPrintJson()); } }
internal void LoadApplication(string path) { if (_gameLoaded) { GtkDialog.CreateDialog("Ryujinx", "A game has already been loaded", "Please close it first and try again."); } else { Logger.RestartTime(); HLE.Switch device = InitializeSwitchInstance(); // TODO: Move this somewhere else + reloadable? Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; if (Directory.Exists(path)) { string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); if (romFsFiles.Length == 0) { romFsFiles = Directory.GetFiles(path, "*.romfs"); } if (romFsFiles.Length > 0) { Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS."); device.LoadCart(path, romFsFiles[0]); } else { Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS."); device.LoadCart(path); } } else if (File.Exists(path)) { switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) { case ".xci": Logger.PrintInfo(LogClass.Application, "Loading as XCI."); device.LoadXci(path); break; case ".nca": Logger.PrintInfo(LogClass.Application, "Loading as NCA."); device.LoadNca(path); break; case ".nsp": case ".pfs0": Logger.PrintInfo(LogClass.Application, "Loading as NSP."); device.LoadNsp(path); break; default: Logger.PrintInfo(LogClass.Application, "Loading as homebrew."); try { device.LoadProgram(path); } catch (ArgumentOutOfRangeException) { Logger.PrintError(LogClass.Application, "The file which you have specified is unsupported by Ryujinx."); } break; } } else { Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); End(device); } _emulationContext = device; _deviceExitStatus.Reset(); #if MACOS_BUILD CreateGameWindow(device); #else var windowThread = new Thread(() => { CreateGameWindow(device); }) { Name = "GUI.WindowThread" }; windowThread.Start(); #endif _gameLoaded = true; _stopEmulation.Sensitive = true; _firmwareInstallFile.Sensitive = false; _firmwareInstallDirectory.Sensitive = false; DiscordIntegrationModule.SwitchToPlayingState(device.System.TitleIdText, device.System.TitleName); ApplicationLibrary.LoadAndSaveMetaData(device.System.TitleIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); } }
private void AddButton_Clicked(object sender, EventArgs args) { FileChooserDialog fileChooser = new FileChooserDialog("Select DLC files", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Add", ResponseType.Accept) { SelectMultiple = true, Filter = new FileFilter() }; fileChooser.SetPosition(WindowPosition.Center); fileChooser.Filter.AddPattern("*.nsp"); 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 a DLC for the selected title!"); } } } } fileChooser.Dispose(); }
internal void LoadApplication(string path) { if (_gameLoaded) { GtkDialog.CreateInfoDialog("Ryujinx", "A game has already been loaded", "Please close it first and try again."); } else { if (ConfigurationState.Instance.Logger.EnableDebug.Value) { MessageDialog debugWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) { Title = "Ryujinx - Warning", Text = "You have debug logging enabled, which is designed to be used by developers only.", SecondaryText = "For optimal performance, it's recommended to disable debug logging. Would you like to disable debug logging now?" }; if (debugWarningDialog.Run() == (int)ResponseType.Yes) { ConfigurationState.Instance.Logger.EnableDebug.Value = false; SaveConfig(); } debugWarningDialog.Dispose(); } if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) { MessageDialog shadersDumpWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) { Title = "Ryujinx - Warning", Text = "You have shader dumping enabled, which is designed to be used by developers only.", SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?" }; if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes) { ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = ""; SaveConfig(); } shadersDumpWarningDialog.Dispose(); } Logger.RestartTime(); HLE.Switch device = InitializeSwitchInstance(); UpdateGraphicsConfig(); Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {_contentManager.GetCurrentFirmwareVersion()?.VersionString}"); if (Directory.Exists(path)) { string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); if (romFsFiles.Length == 0) { romFsFiles = Directory.GetFiles(path, "*.romfs"); } if (romFsFiles.Length > 0) { Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); device.LoadCart(path, romFsFiles[0]); } else { Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); device.LoadCart(path); } } else if (File.Exists(path)) { switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) { case ".xci": Logger.Info?.Print(LogClass.Application, "Loading as XCI."); device.LoadXci(path); break; case ".nca": Logger.Info?.Print(LogClass.Application, "Loading as NCA."); device.LoadNca(path); break; case ".nsp": case ".pfs0": Logger.Info?.Print(LogClass.Application, "Loading as NSP."); device.LoadNsp(path); break; default: Logger.Info?.Print(LogClass.Application, "Loading as homebrew."); try { device.LoadProgram(path); } catch (ArgumentOutOfRangeException) { Logger.Error?.Print(LogClass.Application, "The file which you have specified is unsupported by Ryujinx."); } break; } } else { Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); device.Dispose(); return; } _emulationContext = device; _deviceExitStatus.Reset(); #if MACOS_BUILD CreateGameWindow(device); #else Thread windowThread = new Thread(() => { CreateGameWindow(device); }) { Name = "GUI.WindowThread" }; windowThread.Start(); #endif _gameLoaded = true; _stopEmulation.Sensitive = true; _firmwareInstallFile.Sensitive = false; _firmwareInstallDirectory.Sensitive = false; DiscordIntegrationModule.SwitchToPlayingState(device.Application.TitleIdText, device.Application.TitleName); ApplicationLibrary.LoadAndSaveMetaData(device.Application.TitleIdText, appMetadata => { appMetadata.LastPlayed = DateTime.UtcNow.ToString(); }); } }
internal void LoadApplication(string path) { if (_gameLoaded) { GtkDialog.CreateErrorDialog("A game has already been loaded. Please close the emulator and try again"); } else { Logger.RestartTime(); if (Directory.Exists(path)) { string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); if (romFsFiles.Length == 0) { romFsFiles = Directory.GetFiles(path, "*.romfs"); } if (romFsFiles.Length > 0) { Logger.PrintInfo(LogClass.Application, "Loading as cart with RomFS."); _device.LoadCart(path, romFsFiles[0]); } else { Logger.PrintInfo(LogClass.Application, "Loading as cart WITHOUT RomFS."); _device.LoadCart(path); } } else if (File.Exists(path)) { switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) { case ".xci": Logger.PrintInfo(LogClass.Application, "Loading as XCI."); _device.LoadXci(path); break; case ".nca": Logger.PrintInfo(LogClass.Application, "Loading as NCA."); _device.LoadNca(path); break; case ".nsp": case ".pfs0": Logger.PrintInfo(LogClass.Application, "Loading as NSP."); _device.LoadNsp(path); break; default: Logger.PrintInfo(LogClass.Application, "Loading as homebrew."); try { _device.LoadProgram(path); } catch (ArgumentOutOfRangeException) { Logger.PrintError(LogClass.Application, "The file which you have specified is unsupported by Ryujinx."); } break; } } else { Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); End(); } #if MACOS_BUILD CreateGameWindow(); #else new Thread(CreateGameWindow).Start(); #endif _gameLoaded = true; _stopEmulation.Sensitive = true; if (DiscordIntegrationEnabled) { if (File.ReadAllLines(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(_device.System.TitleId)) { DiscordPresence.Assets.LargeImageKey = _device.System.TitleId; } string state = _device.System.TitleId; if (state == null) { state = "Ryujinx"; } else { state = state.ToUpper(); } string details = "Idling"; if (_device.System.TitleName != null) { details = $"Playing {_device.System.TitleName}"; } DiscordPresence.Details = details; DiscordPresence.State = state; DiscordPresence.Assets.LargeImageText = _device.System.TitleName; DiscordPresence.Assets.SmallImageKey = "ryujinx"; DiscordPresence.Assets.SmallImageText = "Ryujinx is an emulator for the Nintendo Switch"; DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow); DiscordClient.SetPresence(DiscordPresence); } string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui"); string metadataFile = System.IO.Path.Combine(metadataFolder, "metadata.json"); IJsonFormatterResolver resolver = CompositeResolver.Create(new[] { StandardResolver.AllowPrivateSnakeCase }); ApplicationMetadata appMetadata; if (!File.Exists(metadataFile)) { Directory.CreateDirectory(metadataFolder); appMetadata = new ApplicationMetadata { Favorite = false, TimePlayed = 0, LastPlayed = "Never" }; byte[] data = JsonSerializer.Serialize(appMetadata, resolver); File.WriteAllText(metadataFile, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson()); } using (Stream stream = File.OpenRead(metadataFile)) { appMetadata = JsonSerializer.Deserialize <ApplicationMetadata>(stream, resolver); } appMetadata.LastPlayed = DateTime.UtcNow.ToString(); byte[] saveData = JsonSerializer.Serialize(appMetadata, resolver); File.WriteAllText(metadataFile, Encoding.UTF8.GetString(saveData, 0, saveData.Length).PrettyPrintJson()); } }