mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-08-01 02:30:08 -06:00
Compare commits
4 Commits
Canary-1.2
...
Canary-1.2
Author | SHA1 | Date | |
---|---|---|---|
4ffb8aef12 | |||
290a6ad5de | |||
eda4f4349b | |||
5fbcb1f3a7 |
@ -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; }
|
||||
|
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,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,13 @@ namespace Ryujinx.UI.App.Common
|
||||
|
||||
private bool AddAndAutoSelectUpdate(TitleUpdateModel update)
|
||||
{
|
||||
var currentlySelected = TitleUpdates.Items.FirstOrOptional(it =>
|
||||
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 +1454,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 +1473,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 +1485,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 +1502,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 +1514,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()
|
||||
{
|
||||
|
@ -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)>();
|
||||
|
||||
|
@ -13,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;
|
||||
@ -126,32 +127,22 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
const string name = "miiEdit";
|
||||
const ulong programId = 0x0100000000001009;
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(programId, 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 = name,
|
||||
Id = programId,
|
||||
Path = contentPath,
|
||||
Name = AppletName,
|
||||
Id = AppletProgramId,
|
||||
Path = contentPath
|
||||
};
|
||||
|
||||
string version = "1.0.0";
|
||||
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
||||
|
||||
//version buffer
|
||||
Encoding.ASCII.GetBytes(version).AsSpan().CopyTo(nacpData.ByteSpan.Slice(0x3060));
|
||||
|
||||
//name and distributor buffer
|
||||
//repeat once for each locale (the ApplicationControlProperty has 16 locales)
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
{
|
||||
Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(nacpData.ByteSpan.Slice(i * 0x300));
|
||||
"Ryujinx"u8.ToArray().AsSpan().CopyTo(nacpData.ByteSpan.Slice(i * 0x300 + 0x200));
|
||||
}
|
||||
|
||||
var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion);
|
||||
|
||||
await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
}
|
||||
|
Reference in New Issue
Block a user