Merge branch 'master' into master
This commit is contained in:
commit
52ae6a0b26
@ -1,6 +1,8 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||
{
|
||||
public class JoyconConfigControllerStick<TButton, TStick> where TButton : unmanaged where TStick : unmanaged
|
||||
public class JoyconConfigControllerStick<TButton, TStick>
|
||||
where TButton : unmanaged
|
||||
where TStick : unmanaged
|
||||
{
|
||||
public TStick Joystick { get; set; }
|
||||
public bool InvertStickX { get; set; }
|
||||
|
@ -26,7 +26,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
{
|
||||
private static readonly TitleUpdateMetadataJsonSerializerContext _applicationSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
|
||||
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
// Extract RomFs and ExeFs from NCA.
|
||||
IStorage romFs = nca.GetRomFs(device, patchNca);
|
||||
@ -55,6 +55,10 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
{
|
||||
nacpData = controlNca.GetNacp(device);
|
||||
}
|
||||
else if (customNacpData != null) // if the Application doesn't provide a nacp file but the Application provides an override, use the provided nacp override
|
||||
{
|
||||
nacpData = (BlitStruct<ApplicationControlProperty>)customNacpData;
|
||||
}
|
||||
|
||||
/* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update.
|
||||
|
||||
|
@ -98,12 +98,12 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNca(string path)
|
||||
public bool LoadNca(string path, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
ProcessResult processResult = nca.Load(_device, null, null);
|
||||
ProcessResult processResult = nca.Load(_device, null, null, customNacpData);
|
||||
|
||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
|
@ -84,12 +84,19 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isFirmware = ProgramId is >= 0x0100000000000819 and <= 0x010000000000081C;
|
||||
bool isFirmwareApplication = ProgramId <= 0x0100000000007FFF;
|
||||
|
||||
string name = !isFirmware
|
||||
? (isFirmwareApplication ? "Firmware Application " : "") + (!string.IsNullOrWhiteSpace(Name) ? Name : "<Unknown Name>")
|
||||
: "Firmware";
|
||||
|
||||
// TODO: LibHac npdm currently doesn't support version field.
|
||||
string version = ProgramId > 0x0100000000007FFF
|
||||
? DisplayVersion
|
||||
string version = !isFirmware
|
||||
? (!string.IsNullOrWhiteSpace(DisplayVersion) ? DisplayVersion : "<Unknown Version>")
|
||||
: device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?";
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]");
|
||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
37
src/Ryujinx.HLE/StructHelpers.cs
Normal file
37
src/Ryujinx.HLE/StructHelpers.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
public static class StructHelpers
|
||||
{
|
||||
public static BlitStruct<ApplicationControlProperty> CreateCustomNacpData(string name, string version)
|
||||
{
|
||||
// https://switchbrew.org/wiki/NACP
|
||||
const int OffsetOfDisplayVersion = 0x3060;
|
||||
|
||||
// https://switchbrew.org/wiki/NACP#ApplicationTitle
|
||||
const int TotalApplicationTitles = 0x10;
|
||||
const int SizeOfApplicationTitle = 0x300;
|
||||
const int OffsetOfApplicationPublisherStrings = 0x200;
|
||||
|
||||
|
||||
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
||||
|
||||
// name and publisher buffer
|
||||
// repeat once for each locale (the ApplicationControlProperty has 16 locales)
|
||||
for (int i = 0; i < TotalApplicationTitles; i++)
|
||||
{
|
||||
Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle)..]);
|
||||
"Ryujinx"u8.CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle + OffsetOfApplicationPublisherStrings)..]);
|
||||
}
|
||||
|
||||
// version buffer
|
||||
Encoding.ASCII.GetBytes(version).AsSpan().CopyTo(nacpData.ByteSpan[OffsetOfDisplayVersion..]);
|
||||
|
||||
return nacpData;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Audio.Backends.CompatLayer;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@ -111,7 +113,7 @@ namespace Ryujinx.HLE
|
||||
|
||||
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
||||
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
|
||||
public bool LoadNca(string ncaFile) => Processes.LoadNca(ncaFile);
|
||||
public bool LoadNca(string ncaFile, BlitStruct<ApplicationControlProperty>? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData);
|
||||
public bool LoadNsp(string nspFile, ulong applicationId = 0) => Processes.LoadNsp(nspFile, applicationId);
|
||||
public bool LoadProgram(string fileName) => Processes.LoadNxo(fileName);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using CommandLine;
|
||||
using Gommon;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Common;
|
||||
@ -96,8 +97,13 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
|
||||
Parser.Default.ParseArguments<Options>(args)
|
||||
.WithParsed(Load)
|
||||
.WithNotParsed(errors => errors.Output());
|
||||
.WithParsed(Load)
|
||||
.WithNotParsed(errors =>
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:");
|
||||
|
||||
errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}"));
|
||||
});
|
||||
}
|
||||
|
||||
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
||||
@ -579,8 +585,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
options.MultiplayerLanInterfaceId,
|
||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
||||
false,
|
||||
"",
|
||||
"",
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
options.CustomVSyncInterval);
|
||||
|
||||
return new Switch(configuration);
|
||||
|
@ -1,5 +1,4 @@
|
||||
using DynamicData;
|
||||
using DynamicData.Kernel;
|
||||
using Gommon;
|
||||
using LibHac;
|
||||
using LibHac.Common;
|
||||
@ -37,14 +36,13 @@ using System.Threading.Tasks;
|
||||
using ContentType = LibHac.Ncm.ContentType;
|
||||
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
||||
using Path = System.IO.Path;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
using TimeSpan = System.TimeSpan;
|
||||
|
||||
namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
public class ApplicationLibrary
|
||||
{
|
||||
public static string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||
public Language DesiredLanguage { get; set; }
|
||||
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
||||
public event EventHandler<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
|
||||
@ -191,12 +189,9 @@ namespace Ryujinx.UI.App.Common
|
||||
}
|
||||
}
|
||||
|
||||
if (isExeFs)
|
||||
{
|
||||
return GetApplicationFromExeFs(pfs, filePath);
|
||||
}
|
||||
|
||||
return null;
|
||||
return isExeFs
|
||||
? GetApplicationFromExeFs(pfs, filePath)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
|
||||
@ -512,10 +507,6 @@ namespace Ryujinx.UI.App.Common
|
||||
case ".xci":
|
||||
case ".nsp":
|
||||
{
|
||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem);
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
@ -604,7 +595,7 @@ namespace Ryujinx.UI.App.Common
|
||||
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),
|
||||
nacpFile.Get.Read(out _, 0, LibHac.Common.SpanHelpers.AsByteSpan(ref controlData),
|
||||
ReadOption.None).ThrowIfFailure();
|
||||
|
||||
var displayVersion = controlData.DisplayVersionString.ToString();
|
||||
@ -827,7 +818,7 @@ namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
_downloadableContents.Edit(it =>
|
||||
{
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase, dlcs);
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase, dlcs);
|
||||
|
||||
it.Remove(it.Items.Where(item => item.Dlc.TitleIdBase == application.IdBase));
|
||||
it.AddOrUpdate(dlcs);
|
||||
@ -839,7 +830,7 @@ namespace Ryujinx.UI.App.Common
|
||||
{
|
||||
_titleUpdates.Edit(it =>
|
||||
{
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, updates);
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, updates);
|
||||
|
||||
it.Remove(it.Items.Where(item => item.TitleUpdate.TitleIdBase == application.IdBase));
|
||||
it.AddOrUpdate(updates);
|
||||
@ -1088,14 +1079,15 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
private bool AddAndAutoSelectUpdate(TitleUpdateModel update)
|
||||
{
|
||||
var currentlySelected = TitleUpdates.Items.FirstOrOptional(it =>
|
||||
if (update == null) return false;
|
||||
|
||||
var currentlySelected = TitleUpdates.Items.FindFirst(it =>
|
||||
it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected);
|
||||
|
||||
var shouldSelect = !currentlySelected.HasValue ||
|
||||
currentlySelected.Value.TitleUpdate.Version < update.Version;
|
||||
var shouldSelect = currentlySelected.Check(curr => curr.TitleUpdate?.Version < update.Version);
|
||||
|
||||
_titleUpdates.AddOrUpdate((update, shouldSelect));
|
||||
|
||||
|
||||
if (currentlySelected.HasValue && shouldSelect)
|
||||
{
|
||||
_titleUpdates.AddOrUpdate((currentlySelected.Value.TitleUpdate, false));
|
||||
@ -1464,7 +1456,7 @@ namespace Ryujinx.UI.App.Common
|
||||
if (addedNewDlc)
|
||||
{
|
||||
var gameDlcs = it.Items.Where(dlc => dlc.Dlc.TitleIdBase == application.IdBase).ToList();
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase,
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase,
|
||||
gameDlcs);
|
||||
}
|
||||
}
|
||||
@ -1483,11 +1475,11 @@ namespace Ryujinx.UI.App.Common
|
||||
TitleUpdatesHelper.LoadTitleUpdatesJson(_virtualFileSystem, application.IdBase);
|
||||
it.AddOrUpdate(savedUpdates);
|
||||
|
||||
var selectedUpdate = savedUpdates.FirstOrOptional(update => update.IsSelected);
|
||||
var selectedUpdate = savedUpdates.FindFirst(update => update.IsSelected);
|
||||
|
||||
if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates))
|
||||
{
|
||||
var savedUpdateLookup = savedUpdates.Select(update => update.Item1).ToHashSet();
|
||||
var savedUpdateLookup = savedUpdates.Select(update => update.Update).ToHashSet();
|
||||
bool updatesChanged = false;
|
||||
|
||||
foreach (var update in bundledUpdates.OrderByDescending(bundled => bundled.Version))
|
||||
@ -1495,12 +1487,11 @@ namespace Ryujinx.UI.App.Common
|
||||
if (!savedUpdateLookup.Contains(update))
|
||||
{
|
||||
bool shouldSelect = false;
|
||||
if (!selectedUpdate.HasValue || selectedUpdate.Value.Item1.Version < update.Version)
|
||||
if (selectedUpdate.Check(su => su.Update?.Version < update.Version))
|
||||
{
|
||||
shouldSelect = true;
|
||||
if (selectedUpdate.HasValue)
|
||||
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Item1, false));
|
||||
selectedUpdate = DynamicData.Kernel.Optional<(TitleUpdateModel, bool IsSelected)>.Create((update, true));
|
||||
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Update, false));
|
||||
selectedUpdate = (update, true);
|
||||
}
|
||||
|
||||
modifiedVersion = modifiedVersion || shouldSelect;
|
||||
@ -1513,7 +1504,7 @@ namespace Ryujinx.UI.App.Common
|
||||
if (updatesChanged)
|
||||
{
|
||||
var gameUpdates = it.Items.Where(update => update.TitleUpdate.TitleIdBase == application.IdBase).ToList();
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, gameUpdates);
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, gameUpdates);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1525,14 +1516,14 @@ namespace Ryujinx.UI.App.Common
|
||||
private void SaveDownloadableContentsForGame(ulong titleIdBase)
|
||||
{
|
||||
var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList();
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, titleIdBase, dlcs);
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(titleIdBase, dlcs);
|
||||
}
|
||||
|
||||
// Save the _currently tracked_ update state for the game
|
||||
private void SaveTitleUpdatesForGame(ulong titleIdBase)
|
||||
{
|
||||
var updates = TitleUpdates.Items.Where(update => update.TitleUpdate.TitleIdBase == titleIdBase).ToList();
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, titleIdBase, updates);
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(titleIdBase, updates);
|
||||
}
|
||||
|
||||
// ApplicationData isnt live-updating (e.g. when an update gets applied) and so this is meant to trigger a refresh
|
||||
|
@ -1,16 +1,12 @@
|
||||
using ARMeilleure;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Configuration.Multiplayer;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.UI.Common.Configuration.System;
|
||||
using Ryujinx.UI.Common.Configuration.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.UI.Common.Configuration
|
||||
{
|
||||
@ -21,10 +17,10 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
if (Instance != null)
|
||||
{
|
||||
throw new InvalidOperationException("Configuration is already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
Instance = new ConfigurationState();
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationFileFormat ToFileFormat()
|
||||
{
|
||||
|
@ -7,6 +7,24 @@ namespace Ryujinx.UI.Common.Helper
|
||||
{
|
||||
public static partial class ConsoleHelper
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32")]
|
||||
private static partial nint GetConsoleWindow();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
private static partial nint GetForegroundWindow();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool SetForegroundWindow(nint hWnd);
|
||||
|
||||
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||
|
||||
public static void SetConsoleWindowState(bool show)
|
||||
@ -35,16 +53,11 @@ namespace Ryujinx.UI.Common.Helper
|
||||
return;
|
||||
}
|
||||
|
||||
SetForegroundWindow(hWnd);
|
||||
|
||||
hWnd = GetForegroundWindow();
|
||||
|
||||
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32")]
|
||||
private static partial nint GetConsoleWindow();
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("user32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
|
||||
public static void SaveDownloadableContentsJson(ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
|
||||
{
|
||||
DownloadableContentContainer container = default;
|
||||
List<DownloadableContentContainer> downloadableContentContainerList = new();
|
||||
|
@ -28,7 +28,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
{
|
||||
private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public static List<(TitleUpdateModel, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
public static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
{
|
||||
var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase);
|
||||
|
||||
@ -49,7 +49,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates)
|
||||
public static void SaveTitleUpdatesJson(ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates)
|
||||
{
|
||||
var titleUpdateWindowData = new TitleUpdateMetadata
|
||||
{
|
||||
@ -77,7 +77,7 @@ namespace Ryujinx.UI.Common.Helper
|
||||
JsonHelper.SerializeToFile(titleUpdatesJsonPath, titleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
|
||||
}
|
||||
|
||||
private static List<(TitleUpdateModel, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase)
|
||||
private static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase)
|
||||
{
|
||||
var result = new List<(TitleUpdateModel, bool IsSelected)>();
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
<Application.Styles>
|
||||
<sty:FluentAvaloniaTheme PreferSystemTheme="False" />
|
||||
<sty:FluentAvaloniaTheme PreferUserAccentColor="True" PreferSystemTheme="False" />
|
||||
<StyleInclude Source="/Assets/Styles/Styles.xaml" />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
|
@ -3,6 +3,8 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
@ -670,7 +672,7 @@ namespace Ryujinx.Ava
|
||||
_cursorState = CursorStates.ForceChangeCursor;
|
||||
}
|
||||
|
||||
public async Task<bool> LoadGuestApplication()
|
||||
public async Task<bool> LoadGuestApplication(BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
InitializeSwitchInstance();
|
||||
MainWindow.UpdateGraphicsConfig();
|
||||
@ -740,7 +742,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA).");
|
||||
|
||||
if (!Device.LoadNca(ApplicationPath))
|
||||
if (!Device.LoadNca(ApplicationPath, customNacpData))
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "الغش متوفر لـ {0} [{1}]",
|
||||
"BuildId": "معرف البناء:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "المحتويات القابلة للتنزيل {0}",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "Cheats verfügbar für {0} [{1}]",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowHeading": "DLC verfügbar für {0} [{1}]",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "Διαθέσιμα Cheats για {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -802,7 +802,7 @@
|
||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Language": "Español (ES)",
|
||||
"MenuBarFileOpenApplet": "Abrir applet",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Applet Editor Mii",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo",
|
||||
"SettingsTabInputDirectMouseAccess": "Acceso directo al ratón",
|
||||
"SettingsTabSystemMemoryManagerMode": "Modo del administrador de memoria:",
|
||||
@ -32,12 +32,12 @@
|
||||
"MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware desde un archivo XCI o ZIP",
|
||||
"MenuBarFileToolsInstallFirmwareFromDirectory": "Instalar firmware desde una carpeta",
|
||||
"MenuBarToolsInstallKeys": "Install Keys",
|
||||
"MenuBarFileToolsInstallKeysFromFile": "Install keys from KEYS or ZIP",
|
||||
"MenuBarFileToolsInstallKeysFromFolder": "Install keys from a directory",
|
||||
"MenuBarFileToolsInstallKeysFromFile": "Instalar keys de KEYS o ZIP",
|
||||
"MenuBarFileToolsInstallKeysFromFolder": "Instalar keys de un directorio",
|
||||
"MenuBarToolsManageFileTypes": "Administrar tipos de archivo",
|
||||
"MenuBarToolsInstallFileTypes": "Instalar tipos de archivo",
|
||||
"MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarToolsXCITrimmer": "Recortar archivos XCI",
|
||||
"MenuBarView": "_View",
|
||||
"MenuBarViewWindow": "Tamaño Ventana",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -89,11 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Abre el directorio que contiene los Mods de la Aplicación.",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Abrir Directorio de Mods de Atmosphere\n\n\n\n\n\n",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Abre el directorio alternativo de la tarjeta SD de Atmosphere que contiene los Mods de la Aplicación. Útil para los mods que están empaquetados para el hardware real.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"GameListContextMenuTrimXCI": "Verificar y recortar archivo XCI",
|
||||
"GameListContextMenuTrimXCIToolTip": "Verificar y recortar archivo XCI para ahorrar espacio en disco",
|
||||
"StatusBarGamesLoaded": "{0}/{1} juegos cargados",
|
||||
"StatusBarSystemVersion": "Versión del sistema: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"StatusBarXCIFileTrimming": "Recortando el siguiente archivo XCI: '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Límite inferior para mapeos de memoria detectado",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "¿Quieres aumentar el valor de vm.max_map_count a {0}?",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "Algunos juegos podrían intentar crear más mapeos de memoria de los permitidos. Ryujinx se bloqueará tan pronto como se supere este límite.",
|
||||
@ -480,7 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "¡Tipos de archivos desinstalados con éxito!",
|
||||
"DialogUninstallFileTypesErrorMessage": "No se pudo desinstalar los tipos de archivo.",
|
||||
"DialogOpenSettingsWindowLabel": "Abrir ventana de opciones",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogOpenXCITrimmerWindowLabel": "Ventana recortador XCI",
|
||||
"DialogControllerAppletTitle": "Applet de mandos",
|
||||
"DialogMessageDialogErrorExceptionMessage": "Error al mostrar cuadro de diálogo: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Error al mostrar teclado de software: {0}",
|
||||
@ -509,13 +509,13 @@
|
||||
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n¿Continuar?",
|
||||
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "Instalando firmware...",
|
||||
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Versión de sistema {0} instalada con éxito.",
|
||||
"DialogKeysInstallerKeysNotFoundErrorMessage": "An invalid Keys file was found in {0}",
|
||||
"DialogKeysInstallerKeysInstallTitle": "Install Keys",
|
||||
"DialogKeysInstallerKeysInstallMessage": "New Keys file will be installed.",
|
||||
"DialogKeysInstallerKeysInstallSubMessage": "\n\nThis may replace some of the current installed Keys.",
|
||||
"DialogKeysInstallerKeysInstallConfirmMessage": "\n\nDo you want to continue?",
|
||||
"DialogKeysInstallerKeysInstallWaitMessage": "Installing Keys...",
|
||||
"DialogKeysInstallerKeysInstallSuccessMessage": "New Keys file successfully installed.",
|
||||
"DialogKeysInstallerKeysNotFoundErrorMessage": "Se halló un archivo Keys inválido en {0}",
|
||||
"DialogKeysInstallerKeysInstallTitle": "Instalar Keys",
|
||||
"DialogKeysInstallerKeysInstallMessage": "Un nuevo archivo Keys será instalado.",
|
||||
"DialogKeysInstallerKeysInstallSubMessage": "\n\nEsto puede reemplazar algunas de las Keys actualmente instaladas.",
|
||||
"DialogKeysInstallerKeysInstallConfirmMessage": "\n\nDeseas continuar?",
|
||||
"DialogKeysInstallerKeysInstallWaitMessage": "Instalando Keys...",
|
||||
"DialogKeysInstallerKeysInstallSuccessMessage": "Nuevo archivo Keys instalado con éxito.",
|
||||
"DialogUserProfileDeletionWarningMessage": "Si eliminas el perfil seleccionado no quedará ningún otro perfil",
|
||||
"DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?",
|
||||
"DialogUserProfileUnsavedChangesTitle": "Advertencia - Cambios sin guardar",
|
||||
@ -688,23 +688,23 @@
|
||||
"OpenSetupGuideMessage": "Abrir la guía de instalación",
|
||||
"NoUpdate": "No actualizado",
|
||||
"TitleUpdateVersionLabel": "Versión {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"TitleBundledUpdateVersionLabel": "Incorporado: Versión {0}",
|
||||
"TitleBundledDlcLabel": "Incorporado:",
|
||||
"TitleXCIStatusPartialLabel": "Parcial",
|
||||
"TitleXCIStatusTrimmableLabel": "Sin recortar",
|
||||
"TitleXCIStatusUntrimmableLabel": "Recortado",
|
||||
"TitleXCIStatusFailedLabel": "(Fallido)",
|
||||
"TitleXCICanSaveLabel": "Ahorra {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "{0:n0} Mb ahorrado(s)",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Confirmación",
|
||||
"FileDialogAllTypes": "Todos los tipos",
|
||||
"Never": "Nunca",
|
||||
"SwkbdMinCharacters": "Debe tener al menos {0} caracteres",
|
||||
"SwkbdMinRangeCharacters": "Debe tener {0}-{1} caracteres",
|
||||
"CabinetTitle": "Cabinet Dialog",
|
||||
"CabinetDialog": "Enter your Amiibo's new name",
|
||||
"CabinetScanDialog": "Please scan your Amiibo now.",
|
||||
"CabinetTitle": "Diálogo Gabinete",
|
||||
"CabinetDialog": "Ingresa el nuevo nombre de tu Amiibo",
|
||||
"CabinetScanDialog": "Escanea tu Amiibo ahora.",
|
||||
"SoftwareKeyboard": "Teclado de software",
|
||||
"SoftwareKeyboardModeNumeric": "Debe ser sólo 0-9 o '.'",
|
||||
"SoftwareKeyboardModeAlphabet": "Solo deben ser caracteres no CJK",
|
||||
@ -750,44 +750,44 @@
|
||||
"SelectDlcDialogTitle": "Selecciona archivo(s) de DLC",
|
||||
"SelectUpdateDialogTitle": "Selecciona archivo(s) de actualización",
|
||||
"SelectModDialogTitle": "Seleccionar un directorio de Mods",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"TrimXCIFileDialogTitle": "Verificar y recortar archivo XCI",
|
||||
"TrimXCIFileDialogPrimaryText": "Esta función verificará el espacio vacío y después recortará el archivo XCI para ahorrar espacio en disco",
|
||||
"TrimXCIFileDialogSecondaryText": "Tamaño de archivo actual: {0:n} MB\nTamaño de datos de juego: {1:n} MB\nAhorro de espacio en disco: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "El archivo XCI no necesita ser recortado. Verifica los logs para más detalles.",
|
||||
"TrimXCIFileNoUntrimPossible": "El recorte del archivo XCI no puede ser deshecho. Verifica los registros para más detalles.",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "El archivo XCI es de solo Lectura y no se le puede escribir. Lee el registro para más información.",
|
||||
"TrimXCIFileFileSizeChanged": "El archivo XCI ha cambiado de tamaño desde que fue escaneado. Verifica que no se esté escribiendo al archivo y vuelve a intentarlo.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "El archivo XCI tiene datos en el área de espacio libre, no es seguro recortar.",
|
||||
"TrimXCIFileInvalidXCIFile": "El archivo XCI contiene datos inválidos. Lee el registro para más información.",
|
||||
"TrimXCIFileFileIOWriteError": "El archivo XCI no se puede abrir para escribirlo. Lee el registro para más información.",
|
||||
"TrimXCIFileFailedPrimaryText": "El recorte del archivo XCI falló",
|
||||
"TrimXCIFileCancelled": "La operación fue cancelada",
|
||||
"TrimXCIFileFileUndertermined": "No se realizó ninguna operación",
|
||||
"UserProfileWindowTitle": "Administrar perfiles de usuario",
|
||||
"CheatWindowTitle": "Administrar cheats",
|
||||
"DlcWindowTitle": "Administrar contenido descargable",
|
||||
"ModWindowTitle": "Administrar Mods para {0} ({1})",
|
||||
"UpdateWindowTitle": "Administrar actualizaciones",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerWindowTitle": "Recortador de archivos XCI",
|
||||
"XCITrimmerTitleStatusCount": "{0} de {1} Título(s) seleccionado(s)",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} de {1} Título(s) seleccionado(s) ({2} mostrado(s))",
|
||||
"XCITrimmerTitleStatusTrimming": "Recortando {0} Título(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Deshaciendo recorte de {0} Título(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Fallido",
|
||||
"XCITrimmerPotentialSavings": "Ahorro potencial",
|
||||
"XCITrimmerActualSavings": "Ahorro real",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"XCITrimmerSelectDisplayed": "Seleccionar mostrado(s)",
|
||||
"XCITrimmerDeselectDisplayed": "Deseleccionar mostrado(s)",
|
||||
"XCITrimmerSortName": "Título",
|
||||
"XCITrimmerSortSaved": "Ahorro de espacio",
|
||||
"XCITrimmerTrim": "Recortar",
|
||||
"XCITrimmerUntrim": "Deshacer recorte",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} nueva(s) actualización(es) agregada(s)",
|
||||
"UpdateWindowBundledContentNotice": "Las actualizaciones agrupadas no pueden ser eliminadas, solamente deshabilitadas.",
|
||||
"CheatWindowHeading": "Cheats disponibles para {0} [{1}]",
|
||||
"BuildId": "Id de compilación:",
|
||||
"DlcWindowHeading": "Contenido descargable disponible para {0} [{1}]",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "Se agregaron {0} nuevo(s) contenido(s) descargable(s)",
|
||||
"AutoloadDlcAddedMessage": "Se agregaron {0} nuevo(s) contenido(s) descargable(s)",
|
||||
"AutoloadDlcRemovedMessage": "Se eliminaron {0} contenido(s) descargable(s) faltantes",
|
||||
@ -795,7 +795,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "Se eliminaron {0} actualización(es) faltantes",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Editar selección",
|
||||
"Continue": "Continue",
|
||||
"Continue": "Continuar",
|
||||
"Cancel": "Cancelar",
|
||||
"Save": "Guardar",
|
||||
"Discard": "Descartar",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "Cheats disponibles pour {0} [{1}]",
|
||||
"BuildId": "BuildId :",
|
||||
"DlcWindowBundledContentNotice": "Les DLC inclus avec le jeu ne peuvent pas être supprimés mais peuvent être désactivés.",
|
||||
"DlcWindowHeading": "{0} Contenu(s) téléchargeable(s)",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} nouveau(x) contenu(s) téléchargeable(s) ajouté(s)",
|
||||
"AutoloadDlcAddedMessage": "{0} nouveau(x) contenu(s) téléchargeable(s) ajouté(s)",
|
||||
"AutoloadDlcRemovedMessage": "{0} contenu(s) téléchargeable(s) manquant(s) supprimé(s)",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "Trucchi disponibili per {0} [{1}]",
|
||||
"BuildId": "ID Build",
|
||||
"DlcWindowBundledContentNotice": "i DLC \"impacchettati\" non possono essere rimossi, ma solo disabilitati.",
|
||||
"DlcWindowHeading": "DLC disponibili per {0} [{1}]",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} nuovo/i contenuto/i scaricabile/i aggiunto/i",
|
||||
"AutoloadDlcAddedMessage": "{0} contenuto/i scaricabile/i aggiunto/i",
|
||||
"AutoloadDlcRemovedMessage": "{0} contenuto/i scaricabile/i mancante/i rimosso/i",
|
||||
|
@ -787,7 +787,7 @@
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "利用可能なチート {0} [{1}]",
|
||||
"BuildId": "ビルドID:",
|
||||
"DlcWindowHeading": "利用可能な DLC {0} [{1}]",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "{0} [{1}]에 사용 가능한 치트",
|
||||
"BuildId": "빌드ID:",
|
||||
"DlcWindowBundledContentNotice": "번들 DLC는 제거할 수 없으며 비활성화만 가능합니다.",
|
||||
"DlcWindowHeading": "{1} ({2})에 내려받기 가능한 콘텐츠 {0}개 사용 가능",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0}개의 새로운 내려받기 가능한 콘텐츠가 추가됨",
|
||||
"AutoloadDlcAddedMessage": "{0}개의 새로운 내려받기 가능한 콘텐츠가 추가됨",
|
||||
"AutoloadDlcRemovedMessage": "{0}개의 내려받기 가능한 콘텐츠가 제거됨",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "Kody Dostępne dla {0} [{1}]",
|
||||
"BuildId": "Identyfikator wersji:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Zawartości do Pobrania dostępna dla {1} ({2})",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -787,7 +787,7 @@
|
||||
"CheatWindowHeading": "Cheats disponíveis para {0} [{1}]",
|
||||
"BuildId": "ID da Build:",
|
||||
"DlcWindowBundledContentNotice": "DLCs incorporadas não podem ser removidas, apenas desativadas.",
|
||||
"DlcWindowHeading": "{0} DLCs disponíveis para {1} ({2})",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} novo(s) conteúdo(s) para download adicionado(s)",
|
||||
"AutoloadDlcAddedMessage": "{0} novo(s) conteúdo(s) para download adicionado(s)",
|
||||
"AutoloadDlcRemovedMessage": "{0} conteúdo(s) para download ausente(s) removido(s)",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "Доступные читы для {0} [{1}]",
|
||||
"BuildId": "ID версии:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} DLC",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "สูตรโกงมีให้สำหรับ {0} [{1}]",
|
||||
"BuildId": "รหัสการสร้าง:",
|
||||
"DlcWindowBundledContentNotice": "แพ็ค DLC ไม่สามารถลบทิ้งได้ สามารถปิดใช้งานได้เท่านั้น",
|
||||
"DlcWindowHeading": "{0} DLC ที่สามารถดาวน์โหลดได้",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} DLC ใหม่ที่เพิ่มเข้ามา",
|
||||
"AutoloadDlcAddedMessage": "{0} ใหม่ที่เพิ่มเข้ามา",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "{0} için Hile mevcut [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
|
@ -802,7 +802,7 @@
|
||||
"CheatWindowHeading": "Коди доступні для {0} [{1}]",
|
||||
"BuildId": "ID збірки:",
|
||||
"DlcWindowBundledContentNotice": "Вбудований DLC не може бути видаленим, лише вимкненим.",
|
||||
"DlcWindowHeading": "Вміст для завантаження, доступний для {1} ({2}): {0}",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "{0} новий завантажувальний вміст додано",
|
||||
"AutoloadDlcAddedMessage": "{0} новий завантажувальний вміст додано",
|
||||
"AutoloadDlcRemovedMessage": "{0} відсутній завантажуваний вміст видалено.",
|
||||
|
@ -788,7 +788,7 @@
|
||||
"CheatWindowHeading": "可用於 {0} [{1}] 的密技",
|
||||
"BuildId": "組建識別碼:",
|
||||
"DlcWindowBundledContentNotice": "附帶的 DLC 只能被停用而無法被刪除。",
|
||||
"DlcWindowHeading": "{0} 個可下載內容",
|
||||
"DlcWindowHeading": "{0} DLC(s) available",
|
||||
"DlcWindowDlcAddedMessage": "已加入 {0} 個 DLC",
|
||||
"AutoloadDlcAddedMessage": "已加入 {0} 個 DLC",
|
||||
"AutoloadDlcRemovedMessage": "已刪除 {0} 個遺失的 DLC",
|
||||
|
@ -4,18 +4,6 @@
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
Color="{DynamicResource DataGridSelectionColor}" />
|
||||
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
||||
Color="{DynamicResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="ThemeAccentBrush4"
|
||||
Color="{DynamicResource ThemeAccentColor4}" />
|
||||
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
|
||||
<Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
|
||||
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
|
||||
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
|
||||
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
|
||||
@ -26,6 +14,7 @@
|
||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
|
||||
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||
<Color x:Key="Custom">#6483F5</Color>
|
||||
@ -33,18 +22,6 @@
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
Color="{DynamicResource DataGridSelectionColor}" />
|
||||
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
||||
Color="{DynamicResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="ThemeAccentBrush4"
|
||||
Color="{DynamicResource ThemeAccentColor4}" />
|
||||
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark1">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark2">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark3">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorLight1">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorLight2">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorLight3">#FF00C3E3</Color>
|
||||
<Color x:Key="ThemeAccentColor4">#FFe8e8e8</Color>
|
||||
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
|
||||
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
|
||||
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
|
||||
@ -59,18 +36,7 @@
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||
Color="{DynamicResource DataGridSelectionColor}" />
|
||||
<SolidColorBrush x:Key="ThemeAccentColorBrush"
|
||||
Color="{DynamicResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="ThemeAccentBrush4"
|
||||
Color="{DynamicResource ThemeAccentColor4}" />
|
||||
<Color x:Key="ControlFillColorSecondary">#008AA8</Color>
|
||||
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>
|
||||
<Color x:Key="SystemAccentColorDark1">#FF99b000</Color>
|
||||
<Color x:Key="SystemAccentColorDark2">#FF006d7d</Color>
|
||||
<Color x:Key="SystemAccentColorDark3">#FF00525E</Color>
|
||||
<Color x:Key="SystemAccentColorLight1">#FF00dbff</Color>
|
||||
<Color x:Key="SystemAccentColorLight2">#FF19dfff</Color>
|
||||
<Color x:Key="SystemAccentColorLight3">#FF33e3ff</Color>
|
||||
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
|
||||
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
|
||||
<Color x:Key="ThemeControlBorderColor">#FF505050</Color>
|
||||
|
@ -17,13 +17,13 @@ namespace Ryujinx.Ava.Common.Markup
|
||||
public virtual string Name => "Item";
|
||||
public virtual Action<object, T?>? Setter => null;
|
||||
|
||||
protected abstract T? GetValue();
|
||||
protected abstract T? Value { get; }
|
||||
|
||||
protected virtual void ConfigureBindingExtension(CompiledBindingExtension _) { }
|
||||
|
||||
private ClrPropertyInfo PropertyInfo =>
|
||||
new(Name,
|
||||
_ => GetValue(),
|
||||
_ => Value,
|
||||
Setter as Action<object, object?>,
|
||||
typeof(T));
|
||||
|
||||
|
@ -6,17 +6,17 @@ namespace Ryujinx.Ava.Common.Markup
|
||||
{
|
||||
internal class IconExtension(string iconString) : BasicMarkupExtension<Icon>
|
||||
{
|
||||
protected override Icon GetValue() => new() { Value = iconString };
|
||||
protected override Icon Value => new() { Value = iconString };
|
||||
}
|
||||
|
||||
internal class SpinningIconExtension(string iconString) : BasicMarkupExtension<Icon>
|
||||
{
|
||||
protected override Icon GetValue() => new() { Value = iconString, Animation = IconAnimation.Spin };
|
||||
protected override Icon Value => new() { Value = iconString, Animation = IconAnimation.Spin };
|
||||
}
|
||||
|
||||
internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension<string>
|
||||
{
|
||||
protected override string GetValue() => LocaleManager.Instance[key];
|
||||
protected override string Value => LocaleManager.Instance[key];
|
||||
|
||||
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
|
||||
=> bindingExtension.Source = LocaleManager.Instance;
|
||||
|
@ -91,7 +91,7 @@
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource SystemAccentColor}"
|
||||
Foreground="{DynamicResource FavoriteApplicationIconColor}"
|
||||
IsVisible="{Binding Favorite}"
|
||||
Symbol="StarFilled" />
|
||||
</Grid>
|
||||
|
@ -146,7 +146,7 @@
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource SystemAccentColor}"
|
||||
Foreground="{DynamicResource FavoriteApplicationIconColor}"
|
||||
IsVisible="{Binding Favorite}"
|
||||
Symbol="StarFilled" />
|
||||
</Grid>
|
||||
|
@ -10,6 +10,7 @@ using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Input;
|
||||
@ -1897,7 +1898,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false)
|
||||
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
@ -1935,7 +1936,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
this,
|
||||
TopLevel);
|
||||
|
||||
if (!await AppHost.LoadGuestApplication())
|
||||
if (!await AppHost.LoadGuestApplication(customNacpData))
|
||||
{
|
||||
AppHost.DisposeContext();
|
||||
AppHost = null;
|
||||
|
@ -3,7 +3,9 @@ using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using Gommon;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
@ -11,6 +13,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
@ -19,6 +22,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
@ -123,18 +127,24 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
const string AppletName = "miiEdit";
|
||||
const ulong AppletProgramId = 0x0100000000001009;
|
||||
const string AppletVersion = "1.0.0";
|
||||
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(AppletProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (!string.IsNullOrEmpty(contentPath))
|
||||
{
|
||||
ApplicationData applicationData = new()
|
||||
{
|
||||
Name = "miiEdit",
|
||||
Id = 0x0100000000001009,
|
||||
Path = contentPath,
|
||||
Name = AppletName,
|
||||
Id = AppletProgramId,
|
||||
Path = contentPath
|
||||
};
|
||||
|
||||
var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion);
|
||||
|
||||
await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen);
|
||||
await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,23 +61,17 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
private void RemoveDLC(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button)
|
||||
if (sender is Button { DataContext: DownloadableContentModel dlc })
|
||||
{
|
||||
if (button.DataContext is DownloadableContentModel model)
|
||||
{
|
||||
ViewModel.Remove(model);
|
||||
}
|
||||
ViewModel.Remove(dlc);
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenLocation(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button)
|
||||
if (sender is Button { DataContext: DownloadableContentModel dlc })
|
||||
{
|
||||
if (button.DataContext is DownloadableContentModel model)
|
||||
{
|
||||
OpenHelper.LocateFile(model.ContainerPath);
|
||||
}
|
||||
OpenHelper.LocateFile(dlc.ContainerPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user