[Ryujinx.HLE] Address dotnet-format issues (#5380)

* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Silence dotnet format IDE0060 warnings

* Silence dotnet format IDE0052 warnings

* Address or silence dotnet format IDE1006 warnings

* Address dotnet format CA1816 warnings

* Address or silence dotnet format CA2208 warnings

* Address or silence dotnet format CA1806 and a few CA1854 warnings

* Address dotnet format CA2211 warnings

* Address dotnet format CA1822 warnings

* Address or silence dotnet format CA1069 warnings

* Make dotnet format succeed in style mode

* Address or silence dotnet format CA2211 warnings

* Address review comments

* Address dotnet format CA2208 warnings properly

* Make ProcessResult readonly

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Add previously silenced warnings back

I have no clue how these disappeared

* Revert formatting changes for while and for-loops

* Format if-blocks correctly

* Run dotnet format style after rebase

* Run dotnet format whitespace after rebase

* Run dotnet format style after rebase

* Run dotnet format analyzers after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Disable 'prefer switch expression' rule

* Add comments to disabled warnings

* Fix a few disabled warnings

* Fix naming rule violation, Convert shader properties to auto-property and convert values to const

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Start working on disabled warnings

* Fix and silence a few dotnet-format warnings again

* Run dotnet format after rebase

* Use using declaration instead of block syntax

* Address IDE0251 warnings

* Address a few disabled IDE0060 warnings

* Silence IDE0060 in .editorconfig

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* First dotnet format pass

* Fix naming rule violations

* Fix typo

* Add trailing commas, use targeted new and use array initializer

* Fix build issues

* Fix remaining build issues

* Remove SuppressMessage for CA1069 where possible

* Address dotnet format issues

* Address formatting issues

Co-authored-by: Ac_K <acoustik666@gmail.com>

* Add GetHashCode implementation for RenderingSurfaceInfo

* Explicitly silence CA1822 for every affected method in Syscall

* Address formatting issues in Demangler.cs

* Address review feedback

Co-authored-by: Ac_K <acoustik666@gmail.com>

* Revert marking service methods as static

* Next dotnet format pass

* Address review feedback

---------

Co-authored-by: Ac_K <acoustik666@gmail.com>
This commit is contained in:
TSRBerry 2023-07-16 19:31:14 +02:00 committed by GitHub
parent fec8291c17
commit 326749498b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1015 changed files with 8173 additions and 7615 deletions

View File

@ -114,7 +114,7 @@ namespace Ryujinx.Ava.Common
public static void OpenSaveDir(ulong saveDataId)
{
string saveRootPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
string saveRootPath = Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
if (!Directory.Exists(saveRootPath))
{

View File

@ -2,6 +2,7 @@ using LibHac.Fs;
using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common;
using System;
using System.IO;
@ -81,7 +82,7 @@ namespace Ryujinx.Ava.UI.Models
Task.Run(() =>
{
var saveRoot = Path.Combine(MainWindow.MainWindowViewModel.VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
var saveRoot = Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
long totalSize = GetDirectorySize(saveRoot);

View File

@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data);
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
if (!string.IsNullOrWhiteSpace(avatarPath))
{

View File

@ -1,3 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ryujinx.Tests")]
[assembly: InternalsVisibleTo("Ryujinx.Tests")]

View File

@ -8,4 +8,4 @@ namespace Ryujinx.HLE.Exceptions
public GuestBrokeExecutionException() : base(ExMsg) { }
}
}
}

View File

@ -2,8 +2,8 @@
namespace Ryujinx.HLE.Exceptions
{
class InternalServiceException: Exception
class InternalServiceException : Exception
{
public InternalServiceException(string message) : base(message) { }
}
}
}

View File

@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions
{
public InvalidFirmwarePackageException(string message) : base(message) { }
}
}
}

View File

@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions
{
public InvalidNpdmException(string message) : base(message) { }
}
}
}

View File

@ -3,13 +3,13 @@ using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.Exceptions
{
public class InvalidStructLayoutException<T> : Exception
public class InvalidStructLayoutException<T> : Exception
{
static readonly Type _structType = typeof(T);
public InvalidStructLayoutException(string message) : base(message) { }
public InvalidStructLayoutException(int expectedSize)
: base($"Type {_structType.Name} has the wrong size. Expected: {expectedSize} bytes, got: {Unsafe.SizeOf<T>()} bytes") { }
}
}
}

View File

@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions
{
public InvalidSystemResourceException(string message) : base(message) { }
}
}
}

View File

@ -47,7 +47,7 @@ namespace Ryujinx.HLE.Exceptions
private string BuildMessage()
{
StringBuilder sb = new StringBuilder();
StringBuilder sb = new();
// Print the IPC command details (service name, command ID, and handler)
(Type callingType, MethodBase callingMethod) = WalkStackTrace(new StackTrace(this));
@ -58,9 +58,9 @@ namespace Ryujinx.HLE.Exceptions
var ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommands : Service.CmifCommands;
// Find the handler for the method called
var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod);
var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod);
var ipcCommandId = ipcHandler.Key;
var ipcMethod = ipcHandler.Value;
var ipcMethod = ipcHandler.Value;
if (ipcMethod != null)
{
@ -73,9 +73,9 @@ namespace Ryujinx.HLE.Exceptions
sb.AppendLine(Context.Thread.GetGuestStackTrace());
// Print buffer information
if (Request.PtrBuff.Count > 0 ||
Request.SendBuff.Count > 0 ||
Request.ReceiveBuff.Count > 0 ||
if (Request.PtrBuff.Count > 0 ||
Request.SendBuff.Count > 0 ||
Request.ReceiveBuff.Count > 0 ||
Request.ExchangeBuff.Count > 0 ||
Request.RecvListBuff.Count > 0)
{
@ -149,7 +149,7 @@ namespace Ryujinx.HLE.Exceptions
// Find the IIpcService method that threw this exception
while ((frame = trace.GetFrame(i++)) != null)
{
var method = frame.GetMethod();
var method = frame.GetMethod();
var declType = method.DeclaringType;
if (typeof(IpcService).IsAssignableFrom(declType))
@ -161,4 +161,4 @@ namespace Ryujinx.HLE.Exceptions
return (null, null);
}
}
}
}

View File

@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions
{
public TamperCompilationException(string message) : base(message) { }
}
}
}

View File

@ -6,4 +6,4 @@ namespace Ryujinx.HLE.Exceptions
{
public TamperExecutionException(string message) : base(message) { }
}
}
}

View File

@ -10,4 +10,4 @@ namespace Ryujinx.HLE.Exceptions
public UndefinedInstructionException(ulong address, int opCode) : base(string.Format(ExMsg, address, opCode)) { }
}
}
}

View File

@ -26,17 +26,17 @@ namespace Ryujinx.HLE.FileSystem
public class ContentManager
{
private const ulong SystemVersionTitleId = 0x0100000000000809;
private const ulong SystemUpdateTitleId = 0x0100000000000816;
private const ulong SystemUpdateTitleId = 0x0100000000000816;
private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries;
private Dictionary<string, ulong> _sharedFontTitleDictionary;
private Dictionary<ulong, string> _systemTitlesNameDictionary;
private Dictionary<string, string> _sharedFontFilenameDictionary;
private readonly Dictionary<string, ulong> _sharedFontTitleDictionary;
private readonly Dictionary<ulong, string> _systemTitlesNameDictionary;
private readonly Dictionary<string, string> _sharedFontFilenameDictionary;
private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary;
private struct AocItem
private readonly struct AocItem
{
public readonly string ContainerPath;
public readonly string NcaPath;
@ -48,16 +48,16 @@ namespace Ryujinx.HLE.FileSystem
}
}
private SortedList<ulong, AocItem> _aocData { get; }
private SortedList<ulong, AocItem> AocData { get; }
private VirtualFileSystem _virtualFileSystem;
private readonly VirtualFileSystem _virtualFileSystem;
private readonly object _lock = new();
public ContentManager(VirtualFileSystem virtualFileSystem)
{
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
_sharedFontTitleDictionary = new Dictionary<string, ulong>
{
@ -66,7 +66,7 @@ namespace Ryujinx.HLE.FileSystem
{ "FontExtendedChineseSimplified", 0x0100000000000814 },
{ "FontKorean", 0x0100000000000812 },
{ "FontChineseTraditional", 0x0100000000000813 },
{ "FontNintendoExtended", 0x0100000000000810 }
{ "FontNintendoExtended", 0x0100000000000810 },
};
_systemTitlesNameDictionary = new Dictionary<ulong, string>()
@ -86,12 +86,12 @@ namespace Ryujinx.HLE.FileSystem
{ "FontExtendedChineseSimplified", "nintendo_udsg-r_ext_zh-cn_003.bfttf" },
{ "FontKorean", "nintendo_udsg-r_ko_003.bfttf" },
{ "FontChineseTraditional", "nintendo_udjxh-db_zh-tw_003.bfttf" },
{ "FontNintendoExtended", "nintendo_ext_003.bfttf" }
{ "FontNintendoExtended", "nintendo_ext_003.bfttf" },
};
_virtualFileSystem = virtualFileSystem;
_aocData = new SortedList<ulong, AocItem>();
AocData = new SortedList<ulong, AocItem>();
}
public void LoadEntries(Switch device = null)
@ -99,18 +99,18 @@ namespace Ryujinx.HLE.FileSystem
lock (_lock)
{
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
foreach (StorageId storageId in Enum.GetValues<StorageId>())
{
string contentDirectory = null;
string contentPathString = null;
string contentDirectory = null;
string contentPathString = null;
string registeredDirectory = null;
try
{
contentPathString = ContentPath.GetContentPath(storageId);
contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString);
contentPathString = ContentPath.GetContentPath(storageId);
contentDirectory = ContentPath.GetRealPath(contentPathString);
registeredDirectory = Path.Combine(contentDirectory, "registered");
}
catch (NotSupportedException)
@ -120,7 +120,7 @@ namespace Ryujinx.HLE.FileSystem
Directory.CreateDirectory(registeredDirectory);
LinkedList<LocationEntry> locationList = new LinkedList<LocationEntry>();
LinkedList<LocationEntry> locationList = new();
void AddEntry(LocationEntry entry)
{
@ -133,24 +133,19 @@ namespace Ryujinx.HLE.FileSystem
{
string ncaName = new DirectoryInfo(directoryPath).Name.Replace(".nca", string.Empty);
using (FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0]))
{
Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
using FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0]);
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.AsStorage());
string switchPath = contentPathString + ":/" + ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar);
string switchPath = contentPathString + ":/" + ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar);
// Change path format to switch's
switchPath = switchPath.Replace('\\', '/');
// Change path format to switch's
switchPath = switchPath.Replace('\\', '/');
LocationEntry entry = new LocationEntry(switchPath,
0,
nca.Header.TitleId,
nca.Header.ContentType);
LocationEntry entry = new(switchPath, 0, nca.Header.TitleId, nca.Header.ContentType);
AddEntry(entry);
AddEntry(entry);
_contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName);
}
_contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName);
}
}
@ -160,24 +155,19 @@ namespace Ryujinx.HLE.FileSystem
{
string ncaName = Path.GetFileNameWithoutExtension(filePath);
using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
using FileStream ncaFile = new(filePath, FileMode.Open, FileAccess.Read);
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.AsStorage());
string switchPath = contentPathString + ":/" + filePath.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar);
string switchPath = contentPathString + ":/" + filePath.Replace(contentDirectory, string.Empty).TrimStart(Path.DirectorySeparatorChar);
// Change path format to switch's
switchPath = switchPath.Replace('\\', '/');
// Change path format to switch's
switchPath = switchPath.Replace('\\', '/');
LocationEntry entry = new LocationEntry(switchPath,
0,
nca.Header.TitleId,
nca.Header.ContentType);
LocationEntry entry = new(switchPath, 0, nca.Header.TitleId, nca.Header.ContentType);
AddEntry(entry);
AddEntry(entry);
_contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName);
}
_contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName);
}
}
@ -186,10 +176,7 @@ namespace Ryujinx.HLE.FileSystem
_locationEntries.Remove(storageId);
}
if (!_locationEntries.ContainsKey(storageId))
{
_locationEntries.Add(storageId, locationList);
}
_locationEntries.TryAdd(storageId, locationList);
}
if (device != null)
@ -239,7 +226,7 @@ namespace Ryujinx.HLE.FileSystem
public void AddAocItem(ulong titleId, string containerPath, string ncaPath, bool mergedToContainer = false)
{
// TODO: Check Aoc version.
if (!_aocData.TryAdd(titleId, new AocItem(containerPath, ncaPath)))
if (!AocData.TryAdd(titleId, new AocItem(containerPath, ncaPath)))
{
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}");
}
@ -249,7 +236,7 @@ namespace Ryujinx.HLE.FileSystem
if (!mergedToContainer)
{
using FileStream fileStream = File.OpenRead(containerPath);
using FileStream fileStream = File.OpenRead(containerPath);
using PartitionFileSystem partitionFileSystem = new(fileStream.AsStorage());
_virtualFileSystem.ImportTickets(partitionFileSystem);
@ -257,17 +244,17 @@ namespace Ryujinx.HLE.FileSystem
}
}
public void ClearAocData() => _aocData.Clear();
public void ClearAocData() => AocData.Clear();
public int GetAocCount() => _aocData.Count;
public int GetAocCount() => AocData.Count;
public IList<ulong> GetAocTitleIds() => _aocData.Select(e => e.Key).ToList();
public IList<ulong> GetAocTitleIds() => AocData.Select(e => e.Key).ToList();
public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage, IntegrityCheckLevel integrityCheckLevel)
{
aocStorage = null;
if (_aocData.TryGetValue(aocTitleId, out AocItem aoc))
if (AocData.TryGetValue(aocTitleId, out AocItem aoc))
{
var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read);
using var ncaFile = new UniqueRef<IFile>();
@ -307,7 +294,7 @@ namespace Ryujinx.HLE.FileSystem
{
lock (_lock)
{
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
LinkedListNode<LocationEntry> locationEntry = locationList.First;
while (locationEntry != null)
@ -331,7 +318,7 @@ namespace Ryujinx.HLE.FileSystem
if (_contentDictionary.ContainsValue(ncaId))
{
var content = _contentDictionary.FirstOrDefault(x => x.Value == ncaId);
ulong titleId = content.Key.Item1;
ulong titleId = content.Key.titleId;
NcaContentType contentType = content.Key.type;
StorageId storage = GetInstalledStorage(titleId, contentType, storageId);
@ -403,19 +390,17 @@ namespace Ryujinx.HLE.FileSystem
return false;
}
string installedPath = _virtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath);
string installedPath = VirtualFileSystem.SwitchPathToSystemPath(locationEntry.ContentPath);
if (!string.IsNullOrWhiteSpace(installedPath))
{
if (File.Exists(installedPath))
{
using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read))
{
Nca nca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
bool contentCheck = nca.Header.ContentType == contentType;
using FileStream file = new(installedPath, FileMode.Open, FileAccess.Read);
Nca nca = new(_virtualFileSystem.KeySet, file.AsStorage());
bool contentCheck = nca.Header.ContentType == contentType;
return contentCheck;
}
return contentCheck;
}
}
@ -426,9 +411,9 @@ namespace Ryujinx.HLE.FileSystem
{
LinkedList<LocationEntry> locationList = null;
if (_locationEntries.ContainsKey(storageId))
if (_locationEntries.TryGetValue(storageId, out LinkedList<LocationEntry> locationEntry))
{
locationList = _locationEntries[storageId];
locationList = locationEntry;
}
if (locationList != null)
@ -446,9 +431,9 @@ namespace Ryujinx.HLE.FileSystem
{
LinkedList<LocationEntry> locationList = null;
if (_locationEntries.ContainsKey(storageId))
if (_locationEntries.TryGetValue(storageId, out LinkedList<LocationEntry> locationEntry))
{
locationList = _locationEntries[storageId];
locationList = locationEntry;
}
if (locationList != null)
@ -487,10 +472,10 @@ namespace Ryujinx.HLE.FileSystem
public void InstallFirmware(string firmwareSource)
{
string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem);
string contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString);
string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem);
string contentDirectory = ContentPath.GetRealPath(contentPathString);
string registeredDirectory = Path.Combine(contentDirectory, "registered");
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
if (Directory.Exists(temporaryDirectory))
{
@ -510,28 +495,27 @@ namespace Ryujinx.HLE.FileSystem
throw new FileNotFoundException("Firmware file does not exist.");
}
FileInfo info = new FileInfo(firmwareSource);
FileInfo info = new(firmwareSource);
using (FileStream file = File.OpenRead(firmwareSource))
using FileStream file = File.OpenRead(firmwareSource);
switch (info.Extension)
{
switch (info.Extension)
{
case ".zip":
using (ZipArchive archive = ZipFile.OpenRead(firmwareSource))
{
InstallFromZip(archive, temporaryDirectory);
}
break;
case ".xci":
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
InstallFromCart(xci, temporaryDirectory);
break;
default:
throw new InvalidFirmwarePackageException("Input file is not a valid firmware package");
}
FinishInstallation(temporaryDirectory, registeredDirectory);
case ".zip":
using (ZipArchive archive = ZipFile.OpenRead(firmwareSource))
{
InstallFromZip(archive, temporaryDirectory);
}
break;
case ".xci":
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
InstallFromCart(xci, temporaryDirectory);
break;
default:
throw new InvalidFirmwarePackageException("Input file is not a valid firmware package");
}
FinishInstallation(temporaryDirectory, registeredDirectory);
}
private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
@ -555,7 +539,7 @@ namespace Ryujinx.HLE.FileSystem
{
foreach (var entry in filesystem.EnumerateEntries("/", "*.nca"))
{
Nca nca = new Nca(_virtualFileSystem.KeySet, OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage());
Nca nca = new(_virtualFileSystem.KeySet, OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage());
SaveNca(nca, entry.Name.Remove(entry.Name.IndexOf('.')), temporaryDirectory);
}
@ -575,52 +559,47 @@ namespace Ryujinx.HLE.FileSystem
}
}
private void InstallFromZip(ZipArchive archive, string temporaryDirectory)
private static void InstallFromZip(ZipArchive archive, string temporaryDirectory)
{
using (archive)
foreach (var entry in archive.Entries)
{
foreach (var entry in archive.Entries)
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
{
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
// Clean up the name and get the NcaId
string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/');
string ncaId = pathComponents[^1];
// If this is a fragmented nca, we need to get the previous element.GetZip
if (ncaId.Equals("00"))
{
// Clean up the name and get the NcaId
ncaId = pathComponents[^2];
}
string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/');
if (ncaId.Contains(".nca"))
{
string newPath = Path.Combine(temporaryDirectory, ncaId);
string ncaId = pathComponents[pathComponents.Length - 1];
Directory.CreateDirectory(newPath);
// If this is a fragmented nca, we need to get the previous element.GetZip
if (ncaId.Equals("00"))
{
ncaId = pathComponents[pathComponents.Length - 2];
}
if (ncaId.Contains(".nca"))
{
string newPath = Path.Combine(temporaryDirectory, ncaId);
Directory.CreateDirectory(newPath);
entry.ExtractToFile(Path.Combine(newPath, "00"));
}
entry.ExtractToFile(Path.Combine(newPath, "00"));
}
}
}
}
public void SaveNca(Nca nca, string ncaId, string temporaryDirectory)
public static void SaveNca(Nca nca, string ncaId, string temporaryDirectory)
{
string newPath = Path.Combine(temporaryDirectory, ncaId + ".nca");
Directory.CreateDirectory(newPath);
using (FileStream file = File.Create(Path.Combine(newPath, "00")))
{
nca.BaseStorage.AsStream().CopyTo(file);
}
using FileStream file = File.Create(Path.Combine(newPath, "00"));
nca.BaseStorage.AsStream().CopyTo(file);
}
private IFile OpenPossibleFragmentedFile(IFileSystem filesystem, string path, OpenMode mode)
private static IFile OpenPossibleFragmentedFile(IFileSystem filesystem, string path, OpenMode mode)
{
using var file = new UniqueRef<IFile>();
@ -636,14 +615,12 @@ namespace Ryujinx.HLE.FileSystem
return file.Release();
}
private Stream GetZipStream(ZipArchiveEntry entry)
private static Stream GetZipStream(ZipArchiveEntry entry)
{
MemoryStream dest = MemoryStreamManager.Shared.GetStream();
using (Stream src = entry.Open())
{
src.CopyTo(dest);
}
using Stream src = entry.Open();
src.CopyTo(dest);
return dest;
}
@ -659,7 +636,7 @@ namespace Ryujinx.HLE.FileSystem
throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers.");
}
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new Dictionary<ulong, List<(NcaContentType, string)>>();
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
if (Directory.Exists(firmwarePackage))
{
@ -671,33 +648,32 @@ namespace Ryujinx.HLE.FileSystem
throw new FileNotFoundException("Firmware file does not exist.");
}
FileInfo info = new FileInfo(firmwarePackage);
FileInfo info = new(firmwarePackage);
using (FileStream file = File.OpenRead(firmwarePackage))
using FileStream file = File.OpenRead(firmwarePackage);
switch (info.Extension)
{
switch (info.Extension)
{
case ".zip":
using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage))
{
return VerifyAndGetVersionZip(archive);
}
case ".xci":
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
case ".zip":
using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage))
{
return VerifyAndGetVersionZip(archive);
}
case ".xci":
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
if (xci.HasPartition(XciPartitionType.Update))
{
XciPartition partition = xci.OpenPartition(XciPartitionType.Update);
if (xci.HasPartition(XciPartitionType.Update))
{
XciPartition partition = xci.OpenPartition(XciPartitionType.Update);
return VerifyAndGetVersion(partition);
}
else
{
throw new InvalidFirmwarePackageException("Update not found in xci file.");
}
default:
break;
}
return VerifyAndGetVersion(partition);
}
else
{
throw new InvalidFirmwarePackageException("Update not found in xci file.");
}
default:
break;
}
SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory)
@ -713,21 +689,19 @@ namespace Ryujinx.HLE.FileSystem
{
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
{
using (Stream ncaStream = GetZipStream(entry))
using Stream ncaStream = GetZipStream(entry);
IStorage storage = ncaStream.AsStorage();
Nca nca = new(_virtualFileSystem.KeySet, storage);
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
{
IStorage storage = ncaStream.AsStorage();
Nca nca = new Nca(_virtualFileSystem.KeySet, storage);
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
{
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
}
else
{
updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>());
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
}
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
}
else
{
updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>());
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
}
}
}
@ -742,7 +716,7 @@ namespace Ryujinx.HLE.FileSystem
using (Stream ncaStream = GetZipStream(fileEntry))
{
Nca metaNca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage());
Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
@ -772,18 +746,16 @@ namespace Ryujinx.HLE.FileSystem
{
string versionEntry = updateNcasItem.Find(x => x.type != NcaContentType.Meta).path;
using (Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry)))
using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry));
Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage());
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
}
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
}
}
@ -804,43 +776,39 @@ namespace Ryujinx.HLE.FileSystem
continue;
}
ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath);
ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath);
ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath);
using (Stream metaNcaStream = GetZipStream(metaZipEntry))
using Stream metaNcaStream = GetZipStream(metaZipEntry);
using Stream contentNcaStream = GetZipStream(contentZipEntry);
Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage());
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
using var metaFile = new UniqueRef<IFile>();
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
{
using (Stream contentNcaStream = GetZipStream(contentZipEntry))
var meta = new Cnmt(metaFile.Get.AsStream());
IStorage contentStorage = contentNcaStream.AsStorage();
if (contentStorage.GetSize(out long size).IsSuccess())
{
Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaNcaStream.AsStorage());
byte[] contentData = new byte[size];
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
Span<byte> content = new(contentData);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
contentStorage.Read(0, content);
using var metaFile = new UniqueRef<IFile>();
Span<byte> hash = new(new byte[32]);
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash))
{
var meta = new Cnmt(metaFile.Get.AsStream());
IStorage contentStorage = contentNcaStream.AsStorage();
if (contentStorage.GetSize(out long size).IsSuccess())
{
byte[] contentData = new byte[size];
Span<byte> content = new Span<byte>(contentData);
contentStorage.Read(0, content);
Span<byte> hash = new Span<byte>(new byte[32]);
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash))
{
updateNcas.Remove(metaEntry.TitleId);
}
}
updateNcas.Remove(metaEntry.TitleId);
}
}
}
@ -853,9 +821,9 @@ namespace Ryujinx.HLE.FileSystem
foreach (var entry in updateNcas)
{
foreach (var nca in entry.Value)
foreach (var (type, path) in entry.Value)
{
extraNcas += nca.path + Environment.NewLine;
extraNcas += path + Environment.NewLine;
}
}
@ -880,7 +848,7 @@ namespace Ryujinx.HLE.FileSystem
{
IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage();
Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStorage);
Nca nca = new(_virtualFileSystem.KeySet, ncaStorage);
if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta)
{
@ -936,8 +904,8 @@ namespace Ryujinx.HLE.FileSystem
{
if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry))
{
var metaNcaEntry = ncaEntry.Find(x => x.type == NcaContentType.Meta);
string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path;
string metaNcaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path;
string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path;
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
@ -948,10 +916,10 @@ namespace Ryujinx.HLE.FileSystem
continue;
}
IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaEntry.path, OpenMode.Read).AsStorage();
IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaPath, OpenMode.Read).AsStorage();
IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage();
Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaStorage);
Nca metaNca = new(_virtualFileSystem.KeySet, metaStorage);
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
@ -967,11 +935,11 @@ namespace Ryujinx.HLE.FileSystem
{
byte[] contentData = new byte[size];
Span<byte> content = new Span<byte>(contentData);
Span<byte> content = new(contentData);
contentStorage.Read(0, content);
Span<byte> hash = new Span<byte>(new byte[32]);
Span<byte> hash = new(new byte[32]);
LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash);
@ -1017,24 +985,21 @@ namespace Ryujinx.HLE.FileSystem
{
if (entry.ContentType == NcaContentType.Data)
{
var path = _virtualFileSystem.SwitchPathToSystemPath(entry.ContentPath);
var path = VirtualFileSystem.SwitchPathToSystemPath(entry.ContentPath);
using (FileStream fileStream = File.OpenRead(path))
using FileStream fileStream = File.OpenRead(path);
Nca nca = new(_virtualFileSystem.KeySet, fileStream.AsStorage());
if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
{
Nca nca = new Nca(_virtualFileSystem.KeySet, fileStream.AsStorage());
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
return new SystemVersion(systemVersionFile.Get.AsStream());
}
return new SystemVersion(systemVersionFile.Get.AsStream());
}
}
}
}

View File

@ -2,7 +2,6 @@
using LibHac.Ncm;
using Ryujinx.Common.Configuration;
using System;
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
using Path = System.IO.Path;
@ -10,33 +9,33 @@ namespace Ryujinx.HLE.FileSystem
{
internal static class ContentPath
{
public const string SystemContent = "@SystemContent";
public const string UserContent = "@UserContent";
public const string SdCardContent = "@SdCardContent";
public const string SdCard = "@Sdcard";
public const string CalibFile = "@CalibFile";
public const string Safe = "@Safe";
public const string User = "@User";
public const string System = "@System";
public const string Host = "@Host";
public const string GamecardApp = "@GcApp";
public const string SystemContent = "@SystemContent";
public const string UserContent = "@UserContent";
public const string SdCardContent = "@SdCardContent";
public const string SdCard = "@Sdcard";
public const string CalibFile = "@CalibFile";
public const string Safe = "@Safe";
public const string User = "@User";
public const string System = "@System";
public const string Host = "@Host";
public const string GamecardApp = "@GcApp";
public const string GamecardContents = "@GcS00000001";
public const string GamecardUpdate = "@upp";
public const string GamecardUpdate = "@upp";
public const string RegisteredUpdate = "@RegUpdate";
public const string Nintendo = "Nintendo";
public const string Contents = "Contents";
public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath)
public static string GetRealPath(string switchContentPath)
{
return switchContentPath switch
{
SystemContent => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath, Contents),
UserContent => Path.Combine(AppDataManager.BaseDirPath, UserNandPath, Contents),
SdCardContent => Path.Combine(fileSystem.GetSdCardPath(), Nintendo, Contents),
System => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath),
User => Path.Combine(AppDataManager.BaseDirPath, UserNandPath),
_ => throw new NotSupportedException($"Content Path \"`{switchContentPath}`\" is not supported.")
UserContent => Path.Combine(AppDataManager.BaseDirPath, UserNandPath, Contents),
SdCardContent => Path.Combine(GetSdCardPath(), Nintendo, Contents),
System => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath),
User => Path.Combine(AppDataManager.BaseDirPath, UserNandPath),
_ => throw new NotSupportedException($"Content Path \"`{switchContentPath}`\" is not supported."),
};
}
@ -45,9 +44,9 @@ namespace Ryujinx.HLE.FileSystem
return contentStorageId switch
{
ContentStorageId.System => SystemContent,
ContentStorageId.User => UserContent,
ContentStorageId.User => UserContent,
ContentStorageId.SdCard => SdCardContent,
_ => throw new NotSupportedException($"Content Storage Id \"`{contentStorageId}`\" is not supported.")
_ => throw new NotSupportedException($"Content Storage Id \"`{contentStorageId}`\" is not supported."),
};
}
@ -56,9 +55,9 @@ namespace Ryujinx.HLE.FileSystem
return storageId switch
{
StorageId.BuiltInSystem => SystemContent,
StorageId.BuiltInUser => UserContent,
StorageId.SdCard => SdCardContent,
_ => throw new NotSupportedException($"Storage Id \"`{storageId}`\" is not supported.")
StorageId.BuiltInUser => UserContent,
StorageId.SdCard => SdCardContent,
_ => throw new NotSupportedException($"Storage Id \"`{storageId}`\" is not supported."),
};
}
@ -67,16 +66,16 @@ namespace Ryujinx.HLE.FileSystem
return contentPathString.Split(':')[0] switch
{
SystemContent or
System => StorageId.BuiltInSystem,
System => StorageId.BuiltInSystem,
UserContent or
User => StorageId.BuiltInUser,
SdCardContent => StorageId.SdCard,
Host => StorageId.Host,
User => StorageId.BuiltInUser,
SdCardContent => StorageId.SdCard,
Host => StorageId.Host,
GamecardApp or
GamecardContents or
GamecardUpdate => StorageId.GameCard,
_ => StorageId.None
_ => StorageId.None,
};
}
}
}
}

View File

@ -23,4 +23,4 @@ namespace Ryujinx.HLE.FileSystem
return Result.Success;
}
}
}
}

View File

@ -4,16 +4,16 @@ namespace Ryujinx.HLE.FileSystem
{
public struct LocationEntry
{
public string ContentPath { get; private set; }
public int Flag { get; private set; }
public ulong TitleId { get; private set; }
public string ContentPath { get; private set; }
public int Flag { get; private set; }
public ulong TitleId { get; private set; }
public NcaContentType ContentType { get; private set; }
public LocationEntry(string contentPath, int flag, ulong titleId, NcaContentType contentType)
{
ContentPath = contentPath;
Flag = flag;
TitleId = titleId;
Flag = flag;
TitleId = titleId;
ContentType = contentType;
}

View File

@ -5,36 +5,34 @@ namespace Ryujinx.HLE.FileSystem
{
public class SystemVersion
{
public byte Major { get; }
public byte Minor { get; }
public byte Micro { get; }
public byte RevisionMajor { get; }
public byte RevisionMinor { get; }
public byte Major { get; }
public byte Minor { get; }
public byte Micro { get; }
public byte RevisionMajor { get; }
public byte RevisionMinor { get; }
public string PlatformString { get; }
public string Hex { get; }
public string VersionString { get; }
public string VersionTitle { get; }
public string Hex { get; }
public string VersionString { get; }
public string VersionTitle { get; }
public SystemVersion(Stream systemVersionFile)
{
using (BinaryReader reader = new BinaryReader(systemVersionFile))
{
Major = reader.ReadByte();
Minor = reader.ReadByte();
Micro = reader.ReadByte();
using BinaryReader reader = new(systemVersionFile);
Major = reader.ReadByte();
Minor = reader.ReadByte();
Micro = reader.ReadByte();
reader.ReadByte(); // Padding
reader.ReadByte(); // Padding
RevisionMajor = reader.ReadByte();
RevisionMinor = reader.ReadByte();
RevisionMajor = reader.ReadByte();
RevisionMinor = reader.ReadByte();
reader.ReadBytes(2); // Padding
reader.ReadBytes(2); // Padding
PlatformString = StringUtils.ReadInlinedAsciiString(reader, 0x20);
Hex = StringUtils.ReadInlinedAsciiString(reader, 0x40);
VersionString = StringUtils.ReadInlinedAsciiString(reader, 0x18);
VersionTitle = StringUtils.ReadInlinedAsciiString(reader, 0x80);
}
PlatformString = StringUtils.ReadInlinedAsciiString(reader, 0x20);
Hex = StringUtils.ReadInlinedAsciiString(reader, 0x40);
VersionString = StringUtils.ReadInlinedAsciiString(reader, 0x18);
VersionTitle = StringUtils.ReadInlinedAsciiString(reader, 0x80);
}
}
}
}

View File

@ -26,14 +26,14 @@ namespace Ryujinx.HLE.FileSystem
{
public class VirtualFileSystem : IDisposable
{
public static string SafeNandPath = Path.Combine(AppDataManager.DefaultNandDir, "safe");
public static string SystemNandPath = Path.Combine(AppDataManager.DefaultNandDir, "system");
public static string UserNandPath = Path.Combine(AppDataManager.DefaultNandDir, "user");
public static readonly string SafeNandPath = Path.Combine(AppDataManager.DefaultNandDir, "safe");
public static readonly string SystemNandPath = Path.Combine(AppDataManager.DefaultNandDir, "system");
public static readonly string UserNandPath = Path.Combine(AppDataManager.DefaultNandDir, "user");
public KeySet KeySet { get; private set; }
public EmulatedGameCard GameCard { get; private set; }
public EmulatedSdCard SdCard { get; private set; }
public ModLoader ModLoader { get; private set; }
public KeySet KeySet { get; private set; }
public EmulatedGameCard GameCard { get; private set; }
public EmulatedSdCard SdCard { get; private set; }
public ModLoader ModLoader { get; private set; }
private readonly ConcurrentDictionary<ulong, Stream> _romFsByPid;
@ -85,15 +85,15 @@ namespace Ryujinx.HLE.FileSystem
return _romFsByPid[pid];
}
public string GetFullPath(string basePath, string fileName)
public static string GetFullPath(string basePath, string fileName)
{
if (fileName.StartsWith("//"))
{
fileName = fileName.Substring(2);
fileName = fileName[2..];
}
else if (fileName.StartsWith('/'))
{
fileName = fileName.Substring(1);
fileName = fileName[1..];
}
else
{
@ -110,10 +110,10 @@ namespace Ryujinx.HLE.FileSystem
return fullPath;
}
internal string GetSdCardPath() => MakeFullPath(AppDataManager.DefaultSdcardDir);
public string GetNandPath() => MakeFullPath(AppDataManager.DefaultNandDir);
internal static string GetSdCardPath() => MakeFullPath(AppDataManager.DefaultSdcardDir);
public static string GetNandPath() => MakeFullPath(AppDataManager.DefaultNandDir);
public string SwitchPathToSystemPath(string switchPath)
public static string SwitchPathToSystemPath(string switchPath)
{
string[] parts = switchPath.Split(":");
@ -125,7 +125,7 @@ namespace Ryujinx.HLE.FileSystem
return GetFullPath(MakeFullPath(parts[0]), parts[1]);
}
public string SystemPathToSwitchPath(string systemPath)
public static string SystemPathToSwitchPath(string systemPath)
{
string baseSystemPath = AppDataManager.BaseDirPath + Path.DirectorySeparatorChar;
@ -148,7 +148,7 @@ namespace Ryujinx.HLE.FileSystem
return null;
}
private string MakeFullPath(string path, bool isDirectory = true)
private static string MakeFullPath(string path, bool isDirectory = true)
{
// Handles Common Switch Content Paths
switch (path)
@ -185,7 +185,7 @@ namespace Ryujinx.HLE.FileSystem
public void InitializeFsServer(LibHac.Horizon horizon, out HorizonClient fsServerClient)
{
LocalFileSystem serverBaseFs = new LocalFileSystem(AppDataManager.BaseDirPath);
LocalFileSystem serverBaseFs = new(AppDataManager.BaseDirPath);
fsServerClient = horizon.CreatePrivilegedHorizonClient();
var fsServer = new FileSystemServer(fsServerClient);
@ -207,7 +207,7 @@ namespace Ryujinx.HLE.FileSystem
DeviceOperator = fsServerObjects.DeviceOperator,
ExternalKeySet = KeySet.ExternalKeySet,
FsCreators = fsServerObjects.FsCreators,
RandomGenerator = randomGenerator
RandomGenerator = randomGenerator,
};
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
@ -282,16 +282,28 @@ namespace Ryujinx.HLE.FileSystem
public static Result FixExtraData(HorizonClient hos)
{
Result rc = GetSystemSaveList(hos, out List<ulong> systemSaveIds);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
rc = FixUnindexedSystemSaves(hos, systemSaveIds);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
rc = FixExtraDataInSpaceId(hos, SaveDataSpaceId.System);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
rc = FixExtraDataInSpaceId(hos, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
return Result.Success;
}
@ -303,15 +315,23 @@ namespace Ryujinx.HLE.FileSystem
using var iterator = new UniqueRef<SaveDataIterator>();
Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref, spaceId);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
while (true)
{
rc = iterator.Get.ReadSaveDataInfo(out long count, info);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
if (count == 0)
{
return Result.Success;
}
for (int i = 0; i < count; i++)
{
@ -351,7 +371,9 @@ namespace Ryujinx.HLE.FileSystem
private static Result CreateSaveDataDirectory(HorizonClient hos, in SaveDataInfo info)
{
if (info.SpaceId != SaveDataSpaceId.User && info.SpaceId != SaveDataSpaceId.System)
{
return Result.Success;
}
const string MountName = "SaveDir";
var mountNameU8 = MountName.ToU8Span();
@ -360,11 +382,15 @@ namespace Ryujinx.HLE.FileSystem
{
SaveDataSpaceId.System => BisPartitionId.System,
SaveDataSpaceId.User => BisPartitionId.User,
_ => throw new ArgumentOutOfRangeException()
_ => throw new ArgumentOutOfRangeException(nameof(info), info.SpaceId, null),
};
Result rc = hos.Fs.MountBis(mountNameU8, partitionId);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
try
{
var path = $"{MountName}:/save/{info.SaveDataId:x16}".ToU8Span();
@ -391,28 +417,38 @@ namespace Ryujinx.HLE.FileSystem
var mountName = "system".ToU8Span();
DirectoryHandle handle = default;
List<ulong> localList = new List<ulong>();
List<ulong> localList = new();
try
{
Result rc = hos.Fs.MountBis(mountName, BisPartitionId.System);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
rc = hos.Fs.OpenDirectory(out handle, "system:/save".ToU8Span(), OpenDirectoryMode.All);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
DirectoryEntry entry = new DirectoryEntry();
DirectoryEntry entry = new();
while (true)
{
rc = hos.Fs.ReadDirectory(out long readCount, SpanHelpers.AsSpan(ref entry), handle);
if (rc.IsFailure()) return rc;
if (rc.IsFailure())
{
return rc;
}
if (readCount == 0)
{
break;
}
if (Utf8Parser.TryParse(entry.Name, out ulong saveDataId, out int bytesRead, 'x') &&
bytesRead == 16 && (long)saveDataId < 0)
if (Utf8Parser.TryParse(entry.Name, out ulong saveDataId, out int bytesRead, 'x') && bytesRead == 16 && (long)saveDataId < 0)
{
localList.Add(saveDataId);
}
@ -440,7 +476,7 @@ namespace Ryujinx.HLE.FileSystem
// Only save data IDs added to SystemExtraDataFixInfo will be fixed.
private static Result FixUnindexedSystemSaves(HorizonClient hos, List<ulong> existingSaveIds)
{
foreach (var fixInfo in SystemExtraDataFixInfo)
foreach (var fixInfo in _systemExtraDataFixInfo)
{
if (!existingSaveIds.Contains(fixInfo.StaticSaveDataId))
{
@ -472,7 +508,9 @@ namespace Ryujinx.HLE.FileSystem
if (!rc.IsSuccess())
{
if (!ResultFs.TargetNotFound.Includes(rc))
{
return rc;
}
// We'll reach this point only if the save data directory exists but it's not in the save data indexer.
// Creating the save will add it to the indexer while leaving its existing contents intact.
@ -492,7 +530,7 @@ namespace Ryujinx.HLE.FileSystem
OwnerId = info.OwnerId,
Flags = info.Flags,
DataSize = info.DataSize,
JournalSize = info.JournalSize
JournalSize = info.JournalSize,
};
// Make a mask for writing the entire extra data
@ -507,9 +545,11 @@ namespace Ryujinx.HLE.FileSystem
{
wasFixNeeded = true;
Result rc = hos.Fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, info.SpaceId,
info.SaveDataId);
if (rc.IsFailure()) return rc;
Result rc = hos.Fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, info.SpaceId, info.SaveDataId);
if (rc.IsFailure())
{
return rc;
}
// The extra data should have program ID or static save data ID set if it's valid.
// We only try to fix the extra data if the info from the save data indexer has a program ID or static save data ID.
@ -543,7 +583,7 @@ namespace Ryujinx.HLE.FileSystem
else
{
// Try to match the system save with one of the known saves
foreach (ExtraDataFixInfo fixInfo in SystemExtraDataFixInfo)
foreach (ExtraDataFixInfo fixInfo in _systemExtraDataFixInfo)
{
if (extraData.Attribute.StaticSaveDataId == fixInfo.StaticSaveDataId)
{
@ -573,7 +613,7 @@ namespace Ryujinx.HLE.FileSystem
public long JournalSize;
}
private static readonly ExtraDataFixInfo[] SystemExtraDataFixInfo =
private static readonly ExtraDataFixInfo[] _systemExtraDataFixInfo =
{
new ExtraDataFixInfo()
{
@ -581,7 +621,7 @@ namespace Ryujinx.HLE.FileSystem
OwnerId = 0x010000000000001F,
Flags = SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData,
DataSize = 0x10000,
JournalSize = 0x10000
JournalSize = 0x10000,
},
new ExtraDataFixInfo()
{
@ -589,12 +629,13 @@ namespace Ryujinx.HLE.FileSystem
OwnerId = 0x0100000000001009,
Flags = SaveDataFlags.None,
DataSize = 0xC000,
JournalSize = 0xC000
}
JournalSize = 0xC000,
},
};
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}

View File

@ -163,57 +163,57 @@ namespace Ryujinx.HLE
/// </summary>
public Action RefreshInputConfig { internal get; set; }
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
ContentManager contentManager,
AccountManager accountManager,
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
ContentManager contentManager,
AccountManager accountManager,
UserChannelPersistence userChannelPersistence,
IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver,
MemoryConfiguration memoryConfiguration,
IHostUiHandler hostUiHandler,
SystemLanguage systemLanguage,
RegionCode region,
bool enableVsync,
bool enableDockedMode,
bool enablePtc,
bool enableInternetAccess,
IntegrityCheckLevel fsIntegrityCheckLevel,
int fsGlobalAccessLogMode,
long systemTimeOffset,
string timeZone,
MemoryManagerMode memoryManagerMode,
bool ignoreMissingServices,
AspectRatio aspectRatio,
float audioVolume,
bool useHypervisor,
string multiplayerLanInterfaceId)
IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver,
MemoryConfiguration memoryConfiguration,
IHostUiHandler hostUiHandler,
SystemLanguage systemLanguage,
RegionCode region,
bool enableVsync,
bool enableDockedMode,
bool enablePtc,
bool enableInternetAccess,
IntegrityCheckLevel fsIntegrityCheckLevel,
int fsGlobalAccessLogMode,
long systemTimeOffset,
string timeZone,
MemoryManagerMode memoryManagerMode,
bool ignoreMissingServices,
AspectRatio aspectRatio,
float audioVolume,
bool useHypervisor,
string multiplayerLanInterfaceId)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration;
HostUiHandler = hostUiHandler;
SystemLanguage = systemLanguage;
Region = region;
EnableVsync = enableVsync;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
SystemTimeOffset = systemTimeOffset;
TimeZone = timeZone;
MemoryManagerMode = memoryManagerMode;
IgnoreMissingServices = ignoreMissingServices;
AspectRatio = aspectRatio;
AudioVolume = audioVolume;
UseHypervisor = useHypervisor;
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
AccountManager = accountManager;
ContentManager = contentManager;
UserChannelPersistence = userChannelPersistence;
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration;
HostUiHandler = hostUiHandler;
SystemLanguage = systemLanguage;
Region = region;
EnableVsync = enableVsync;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
SystemTimeOffset = systemTimeOffset;
TimeZone = timeZone;
MemoryManagerMode = memoryManagerMode;
IgnoreMissingServices = ignoreMissingServices;
AspectRatio = aspectRatio;
AudioVolume = audioVolume;
UseHypervisor = useHypervisor;
MultiplayerLanInterfaceId = multiplayerLanInterfaceId;
}
}
}
}

View File

@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Applets
{
static class AppletManager
{
private static Dictionary<AppletId, Type> _appletMapping;
private static readonly Dictionary<AppletId, Type> _appletMapping;
static AppletManager()
{
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Applets
{ AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) },
{ AppletId.LibAppletWeb, typeof(BrowserApplet) },
{ AppletId.LibAppletShop, typeof(BrowserApplet) },
{ AppletId.LibAppletOff, typeof(BrowserApplet) }
{ AppletId.LibAppletOff, typeof(BrowserApplet) },
};
}

View File

@ -6,6 +6,6 @@
Offline,
Black,
Share,
Lobby
Lobby,
}
}

View File

@ -13,13 +13,12 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
public event EventHandler AppletStateChanged;
private AppletSession _normalSession;
private AppletSession _interactiveSession;
private CommonArguments _commonArguments;
private List<BrowserArgument> _arguments;
private ShimKind _shimKind;
public BrowserApplet(Horizon system) {}
public BrowserApplet(Horizon system) { }
public ResultCode GetResult()
{
@ -29,7 +28,6 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
{
_normalSession = normalSession;
_interactiveSession = interactiveSession;
_commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop());
@ -48,17 +46,18 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
if ((_commonArguments.AppletVersion >= 0x80000 && _shimKind == ShimKind.Web) || (_commonArguments.AppletVersion >= 0x30000 && _shimKind == ShimKind.Share))
{
List<BrowserOutput> result = new List<BrowserOutput>();
result.Add(new BrowserOutput(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton));
List<BrowserOutput> result = new()
{
new BrowserOutput(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton),
};
_normalSession.Push(BuildResponseNew(result));
}
else
{
WebCommonReturnValue result = new WebCommonReturnValue()
WebCommonReturnValue result = new()
{
ExitReason = WebExitReason.ExitButton,
ExitReason = WebExitReason.ExitButton,
};
_normalSession.Push(BuildResponseOld(result));
@ -69,36 +68,32 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
return ResultCode.Success;
}
private byte[] BuildResponseOld(WebCommonReturnValue result)
private static byte[] BuildResponseOld(WebCommonReturnValue result)
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(result);
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
using BinaryWriter writer = new(stream);
writer.WriteStruct(result);
return stream.ToArray();
}
return stream.ToArray();
}
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
using BinaryWriter writer = new(stream);
writer.WriteStruct(new WebArgHeader
{
writer.WriteStruct(new WebArgHeader
{
Count = (ushort)outputArguments.Count,
ShimKind = _shimKind
});
Count = (ushort)outputArguments.Count,
ShimKind = _shimKind,
});
foreach (BrowserOutput output in outputArguments)
{
output.Write(writer);
}
writer.Write(new byte[0x2000 - writer.BaseStream.Position]);
return stream.ToArray();
foreach (BrowserOutput output in outputArguments)
{
output.Write(writer);
}
writer.Write(new byte[0x2000 - writer.BaseStream.Position]);
return stream.ToArray();
}
}
}

View File

@ -8,16 +8,16 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
{
class BrowserArgument
{
public WebArgTLVType Type { get; }
public byte[] Value { get; }
public WebArgTLVType Type { get; }
public byte[] Value { get; }
public BrowserArgument(WebArgTLVType type, byte[] value)
{
Type = type;
Type = type;
Value = value;
}
private static readonly Dictionary<WebArgTLVType, Type> _typeRegistry = new Dictionary<WebArgTLVType, Type>
private static readonly Dictionary<WebArgTLVType, Type> _typeRegistry = new()
{
{ WebArgTLVType.InitialURL, typeof(string) },
{ WebArgTLVType.CallbackUrl, typeof(string) },
@ -64,11 +64,11 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
public static (ShimKind, List<BrowserArgument>) ParseArguments(ReadOnlySpan<byte> data)
{
List<BrowserArgument> browserArguments = new List<BrowserArgument>();
List<BrowserArgument> browserArguments = new();
WebArgHeader header = IApplet.ReadStruct<WebArgHeader>(data.Slice(0, 8));
WebArgHeader header = IApplet.ReadStruct<WebArgHeader>(data[..8]);
ReadOnlySpan<byte> rawTLVs = data.Slice(8);
ReadOnlySpan<byte> rawTLVs = data[8..];
for (int i = 0; i < header.Count; i++)
{
@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
browserArguments.Add(new BrowserArgument((WebArgTLVType)tlv.Type, tlvData.ToArray()));
rawTLVs = rawTLVs.Slice(Unsafe.SizeOf<WebArgTLV>() + tlv.Size);
rawTLVs = rawTLVs[(Unsafe.SizeOf<WebArgTLV>() + tlv.Size)..];
}
return (header.ShimKind, browserArguments);

View File

@ -11,25 +11,25 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
public BrowserOutput(BrowserOutputType type, byte[] value)
{
Type = type;
Type = type;
Value = value;
}
public BrowserOutput(BrowserOutputType type, uint value)
{
Type = type;
Value = BitConverter.GetBytes(value);
Type = type;
Value = BitConverter.GetBytes(value);
}
public BrowserOutput(BrowserOutputType type, ulong value)
{
Type = type;
Type = type;
Value = BitConverter.GetBytes(value);
}
public BrowserOutput(BrowserOutputType type, bool value)
{
Type = type;
Type = type;
Value = BitConverter.GetBytes(value);
}
@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
writer.WriteStruct(new WebArgTLV
{
Type = (ushort)Type,
Size = (ushort)Value.Length
Size = (ushort)Value.Length,
});
writer.Write(Value);

View File

@ -2,13 +2,13 @@
{
enum BrowserOutputType : ushort
{
ExitReason = 0x1,
LastUrl = 0x2,
LastUrlSize = 0x3,
SharePostResult = 0x4,
PostServiceName = 0x5,
PostServiceNameSize = 0x6,
PostId = 0x7,
MediaPlayerAutoClosedByCompletion = 0x8
ExitReason = 0x1,
LastUrl = 0x2,
LastUrlSize = 0x3,
SharePostResult = 0x4,
PostServiceName = 0x5,
PostServiceNameSize = 0x6,
PostId = 0x7,
MediaPlayerAutoClosedByCompletion = 0x8,
}
}

View File

@ -4,6 +4,6 @@
{
OfflineHtmlPage = 1,
ApplicationLegalInformation,
SystemDataPage
SystemDataPage,
}
}

View File

@ -3,6 +3,6 @@
enum LeftStickMode
{
Pointer = 0,
Cursor
Cursor,
}
}

View File

@ -8,6 +8,6 @@
Share,
Web,
Wifi,
Lobby
Lobby,
}
}

View File

@ -2,8 +2,8 @@
{
public struct WebArgHeader
{
public ushort Count;
public ushort Padding;
public ushort Count;
public ushort Padding;
public ShimKind ShimKind;
}
}

View File

@ -4,6 +4,6 @@
{
public ushort Type;
public ushort Size;
public uint Padding;
public uint Padding;
}
}

View File

@ -2,61 +2,61 @@
{
enum WebArgTLVType : ushort
{
InitialURL = 0x1,
CallbackUrl = 0x3,
CallbackableUrl = 0x4,
ApplicationId = 0x5,
DocumentPath = 0x6,
DocumentKind = 0x7,
SystemDataId = 0x8,
ShareStartPage = 0x9,
Whitelist = 0xA,
NewsFlag = 0xB,
UserID = 0xE,
AlbumEntry0 = 0xF,
ScreenShotEnabled = 0x10,
EcClientCertEnabled = 0x11,
PlayReportEnabled = 0x13,
UnknownFlag0x14 = 0x14,
UnknownFlag0x15 = 0x15,
BootDisplayKind = 0x17,
BackgroundKind = 0x18,
FooterEnabled = 0x19,
PointerEnabled = 0x1A,
LeftStickMode = 0x1B,
KeyRepeatFrame1 = 0x1C,
KeyRepeatFrame2 = 0x1D,
BootAsMediaPlayerInverted = 0x1E,
DisplayUrlKind = 0x1F,
BootAsMediaPlayer = 0x21,
ShopJumpEnabled = 0x22,
MediaAutoPlayEnabled = 0x23,
LobbyParameter = 0x24,
ApplicationAlbumEntry = 0x26,
JsExtensionEnabled = 0x27,
AdditionalCommentText = 0x28,
TouchEnabledOnContents = 0x29,
UserAgentAdditionalString = 0x2A,
AdditionalMediaData0 = 0x2B,
MediaPlayerAutoCloseEnabled = 0x2C,
PageCacheEnabled = 0x2D,
WebAudioEnabled = 0x2E,
FooterFixedKind = 0x32,
PageFadeEnabled = 0x33,
InitialURL = 0x1,
CallbackUrl = 0x3,
CallbackableUrl = 0x4,
ApplicationId = 0x5,
DocumentPath = 0x6,
DocumentKind = 0x7,
SystemDataId = 0x8,
ShareStartPage = 0x9,
Whitelist = 0xA,
NewsFlag = 0xB,
UserID = 0xE,
AlbumEntry0 = 0xF,
ScreenShotEnabled = 0x10,
EcClientCertEnabled = 0x11,
PlayReportEnabled = 0x13,
UnknownFlag0x14 = 0x14,
UnknownFlag0x15 = 0x15,
BootDisplayKind = 0x17,
BackgroundKind = 0x18,
FooterEnabled = 0x19,
PointerEnabled = 0x1A,
LeftStickMode = 0x1B,
KeyRepeatFrame1 = 0x1C,
KeyRepeatFrame2 = 0x1D,
BootAsMediaPlayerInverted = 0x1E,
DisplayUrlKind = 0x1F,
BootAsMediaPlayer = 0x21,
ShopJumpEnabled = 0x22,
MediaAutoPlayEnabled = 0x23,
LobbyParameter = 0x24,
ApplicationAlbumEntry = 0x26,
JsExtensionEnabled = 0x27,
AdditionalCommentText = 0x28,
TouchEnabledOnContents = 0x29,
UserAgentAdditionalString = 0x2A,
AdditionalMediaData0 = 0x2B,
MediaPlayerAutoCloseEnabled = 0x2C,
PageCacheEnabled = 0x2D,
WebAudioEnabled = 0x2E,
FooterFixedKind = 0x32,
PageFadeEnabled = 0x33,
MediaCreatorApplicationRatingAge = 0x34,
BootLoadingIconEnabled = 0x35,
PageScrollIndicatorEnabled = 0x36,
MediaPlayerSpeedControlEnabled = 0x37,
AlbumEntry1 = 0x38,
AlbumEntry2 = 0x39,
AlbumEntry3 = 0x3A,
AdditionalMediaData1 = 0x3B,
AdditionalMediaData2 = 0x3C,
AdditionalMediaData3 = 0x3D,
BootFooterButton = 0x3E,
OverrideWebAudioVolume = 0x3F,
OverrideMediaAudioVolume = 0x40,
BootMode = 0x41,
MediaPlayerUiEnabled = 0x43
BootLoadingIconEnabled = 0x35,
PageScrollIndicatorEnabled = 0x36,
MediaPlayerSpeedControlEnabled = 0x37,
AlbumEntry1 = 0x38,
AlbumEntry2 = 0x39,
AlbumEntry3 = 0x3A,
AdditionalMediaData1 = 0x3B,
AdditionalMediaData2 = 0x3C,
AdditionalMediaData3 = 0x3D,
BootFooterButton = 0x3E,
OverrideWebAudioVolume = 0x3F,
OverrideMediaAudioVolume = 0x40,
BootMode = 0x41,
MediaPlayerUiEnabled = 0x43,
}
}

View File

@ -5,8 +5,8 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
public struct WebCommonReturnValue
{
public WebExitReason ExitReason;
public uint Padding;
public uint Padding;
public ByteArray4096 LastUrl;
public ulong LastUrlSize;
public ulong LastUrlSize;
}
}

View File

@ -6,6 +6,6 @@
BackButton,
Requested,
LastUrl,
ErrorDialog = 7
ErrorDialog = 7,
}
}

View File

@ -5,12 +5,12 @@ namespace Ryujinx.HLE.HOS.Applets
[StructLayout(LayoutKind.Sequential, Pack = 8)]
struct CommonArguments
{
public uint Version;
public uint StructureSize;
public uint AppletVersion;
public uint ThemeColor;
public uint Version;
public uint StructureSize;
public uint AppletVersion;
public uint ThemeColor;
[MarshalAs(UnmanagedType.I1)]
public bool PlayStartupSound;
public bool PlayStartupSound;
public ulong SystemTicks;
}
}

View File

@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Applets
{
internal class ControllerApplet : IApplet
{
private Horizon _system;
private readonly Horizon _system;
private AppletSession _normalSession;
@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Applets
}
else
{
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Unknown");
Logger.Stub?.PrintStub(LogClass.ServiceHid, "ControllerSupportArg Version Unknown");
argHeader = IApplet.ReadStruct<ControllerSupportArgHeader>(controllerSupportArg); // Read just the header
}
@ -82,17 +82,17 @@ namespace Ryujinx.HLE.HOS.Applets
playerMin = playerMax = 1;
}
int configuredCount = 0;
PlayerIndex primaryIndex = PlayerIndex.Unknown;
int configuredCount;
PlayerIndex primaryIndex;
while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex))
{
ControllerAppletUiArgs uiArgs = new ControllerAppletUiArgs
ControllerAppletUiArgs uiArgs = new()
{
PlayerCountMin = playerMin,
PlayerCountMax = playerMax,
SupportedStyles = (ControllerType)privateArg.NpadStyleSet,
SupportedPlayers = _system.Device.Hid.Npads.GetSupportedPlayers(),
IsDocked = _system.State.DockedMode
IsDocked = _system.State.DockedMode,
};
if (!_system.Device.UiHandler.DisplayMessageDialog(uiArgs))
@ -101,10 +101,10 @@ namespace Ryujinx.HLE.HOS.Applets
}
}
ControllerSupportResultInfo result = new ControllerSupportResultInfo
ControllerSupportResultInfo result = new()
{
PlayerCount = (sbyte)configuredCount,
SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex)
SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex),
};
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ReturnResult {result.PlayerCount} {result.SelectedId}");
@ -122,26 +122,24 @@ namespace Ryujinx.HLE.HOS.Applets
return ResultCode.Success;
}
private byte[] BuildResponse(ControllerSupportResultInfo result)
private static byte[] BuildResponse(ControllerSupportResultInfo result)
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
return stream.ToArray();
}
private byte[] BuildResponse()
private static byte[] BuildResponse()
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)ResultCode.Success);
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
writer.Write((ulong)ResultCode.Success);
return stream.ToArray();
}
}
}

View File

@ -11,4 +11,4 @@ namespace Ryujinx.HLE.HOS.Applets
public IEnumerable<PlayerIndex> SupportedPlayers;
public bool IsDocked;
}
}
}

View File

@ -2,7 +2,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
#pragma warning disable CS0649 // Field is never assigned to
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportArgHeader
{
@ -15,4 +15,4 @@ namespace Ryujinx.HLE.HOS.Applets
public byte EnableIdentificationColor;
}
#pragma warning restore CS0649
}
}

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
#pragma warning disable CS0649 // Field is never assigned to
struct ControllerSupportArgPrivate
{
public uint PrivateSize;
@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Applets
public uint NpadJoyHoldType;
}
#pragma warning restore CS0649
}
}

View File

@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
#pragma warning disable CS0649 // Field is never assigned to
// (8.0.0+ version)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportArgV7
@ -23,4 +23,4 @@ namespace Ryujinx.HLE.HOS.Applets
}
}
#pragma warning restore CS0649
}
}

View File

@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
#pragma warning disable CS0649 // Field is never assigned to
// (1.0.0+ version)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportArgVPre7
@ -23,4 +23,4 @@ namespace Ryujinx.HLE.HOS.Applets
}
}
#pragma warning restore CS0649
}
}

View File

@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Applets
{
ShowControllerSupport = 0,
ShowControllerStrapGuide = 1,
ShowControllerFirmwareUpdate = 2
ShowControllerFirmwareUpdate = 2,
}
}
}

View File

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets
{
#pragma warning disable CS0649
#pragma warning disable CS0649 // Field is never assigned to
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ControllerSupportResultInfo
{
@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Applets
public uint Result;
}
#pragma warning restore CS0649
}
}

View File

@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Applets.Error
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ApplicationErrorArg
{
public uint ErrorNumber;
public ulong LanguageCode;
public uint ErrorNumber;
public ulong LanguageCode;
public ByteArray2048 MessageText;
public ByteArray2048 DetailsText;
}
}
}

View File

@ -22,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Applets.Error
{
private const long ErrorMessageBinaryTitleId = 0x0100000000000801;
private Horizon _horizon;
private AppletSession _normalSession;
private CommonArguments _commonArguments;
private readonly Horizon _horizon;
private AppletSession _normalSession;
private CommonArguments _commonArguments;
private ErrorCommonHeader _errorCommonHeader;
private byte[] _errorStorage;
private byte[] _errorStorage;
public event EventHandler AppletStateChanged;
@ -40,14 +40,14 @@ namespace Ryujinx.HLE.HOS.Applets.Error
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
{
_normalSession = normalSession;
_normalSession = normalSession;
_commonArguments = IApplet.ReadStruct<CommonArguments>(_normalSession.Pop());
Logger.Info?.PrintMsg(LogClass.ServiceAm, $"ErrorApplet version: 0x{_commonArguments.AppletVersion:x8}");
_errorStorage = _normalSession.Pop();
_errorStorage = _normalSession.Pop();
_errorCommonHeader = IApplet.ReadStruct<ErrorCommonHeader>(_errorStorage);
_errorStorage = _errorStorage.Skip(Marshal.SizeOf<ErrorCommonHeader>()).ToArray();
_errorStorage = _errorStorage.Skip(Marshal.SizeOf<ErrorCommonHeader>()).ToArray();
switch (_errorCommonHeader.Type)
{
@ -63,7 +63,8 @@ namespace Ryujinx.HLE.HOS.Applets.Error
break;
}
default: throw new NotImplementedException($"ErrorApplet type {_errorCommonHeader.Type} is not implemented.");
default:
throw new NotImplementedException($"ErrorApplet type {_errorCommonHeader.Type} is not implemented.");
}
AppletStateChanged?.Invoke(this, null);
@ -71,15 +72,16 @@ namespace Ryujinx.HLE.HOS.Applets.Error
return ResultCode.Success;
}
private (uint module, uint description) HexToResultCode(uint resultCode)
private static (uint module, uint description) HexToResultCode(uint resultCode)
{
return ((resultCode & 0x1FF) + 2000, (resultCode >> 9) & 0x3FFF);
}
private string SystemLanguageToLanguageKey(SystemLanguage systemLanguage)
private static string SystemLanguageToLanguageKey(SystemLanguage systemLanguage)
{
return systemLanguage switch
{
#pragma warning disable IDE0055 // Disable formatting
SystemLanguage.Japanese => "ja",
SystemLanguage.AmericanEnglish => "en-US",
SystemLanguage.French => "fr",
@ -98,7 +100,8 @@ namespace Ryujinx.HLE.HOS.Applets.Error
SystemLanguage.SimplifiedChinese => "zh-Hans",
SystemLanguage.TraditionalChinese => "zh-Hant",
SystemLanguage.BrazilianPortuguese => "pt-BR",
_ => "en-US"
_ => "en-US",
#pragma warning restore IDE0055
};
}
@ -111,26 +114,24 @@ namespace Ryujinx.HLE.HOS.Applets.Error
{
string binaryTitleContentPath = _horizon.ContentManager.GetInstalledContentPath(ErrorMessageBinaryTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
using (LibHac.Fs.IStorage ncaFileStream = new LocalStorage(_horizon.Device.FileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open))
using LibHac.Fs.IStorage ncaFileStream = new LocalStorage(FileSystem.VirtualFileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open);
Nca nca = new(_horizon.Device.FileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _horizon.FsIntegrityCheckLevel);
string languageCode = SystemLanguageToLanguageKey(_horizon.State.DesiredSystemLanguage);
string filePath = $"/{module}/{description:0000}/{languageCode}_{key}";
if (romfs.FileExists(filePath))
{
Nca nca = new Nca(_horizon.Device.FileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _horizon.FsIntegrityCheckLevel);
string languageCode = SystemLanguageToLanguageKey(_horizon.State.DesiredSystemLanguage);
string filePath = $"/{module}/{description:0000}/{languageCode}_{key}";
using var binaryFile = new UniqueRef<IFile>();
if (romfs.FileExists(filePath))
{
using var binaryFile = new UniqueRef<IFile>();
romfs.OpenFile(ref binaryFile.Ref, filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
StreamReader reader = new(binaryFile.Get.AsStream(), Encoding.Unicode);
romfs.OpenFile(ref binaryFile.Ref, filePath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
StreamReader reader = new StreamReader(binaryFile.Get.AsStream(), Encoding.Unicode);
return CleanText(reader.ReadToEnd());
}
else
{
return "";
}
return CleanText(reader.ReadToEnd());
}
else
{
return "";
}
}
@ -145,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
{
ErrorCommonArg errorCommonArg = IApplet.ReadStruct<ErrorCommonArg>(_errorStorage);
uint module = errorCommonArg.Module;
uint module = errorCommonArg.Module;
uint description = errorCommonArg.Description;
if (_errorCommonHeader.MessageFlag == 0)
@ -188,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
string messageText = Encoding.ASCII.GetString(messageTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray());
string detailsText = Encoding.ASCII.GetString(detailsTextBuffer.TakeWhile(b => !b.Equals(0)).ToArray());
List<string> buttons = new List<string>();
List<string> buttons = new();
// TODO: Handle the LanguageCode to return the translated "OK" and "Details".
@ -213,4 +214,4 @@ namespace Ryujinx.HLE.HOS.Applets.Error
return ResultCode.Success;
}
}
}
}

View File

@ -9,4 +9,4 @@ namespace Ryujinx.HLE.HOS.Applets.Error
public uint Description;
public uint ResultCode;
}
}
}

View File

@ -6,12 +6,12 @@ namespace Ryujinx.HLE.HOS.Applets.Error
struct ErrorCommonHeader
{
public ErrorType Type;
public byte JumpFlag;
public byte ReservedFlag1;
public byte ReservedFlag2;
public byte ReservedFlag3;
public byte ContextFlag;
public byte MessageFlag;
public byte ContextFlag2;
public byte JumpFlag;
public byte ReservedFlag1;
public byte ReservedFlag2;
public byte ReservedFlag3;
public byte ContextFlag;
public byte MessageFlag;
public byte ContextFlag2;
}
}
}

View File

@ -8,6 +8,6 @@
ErrorEulaArg,
ErrorPctlArg,
ErrorRecordArg,
SystemUpdateEulaArg = 8
SystemUpdateEulaArg = 8,
}
}
}

View File

@ -8,10 +8,12 @@ namespace Ryujinx.HLE.HOS.Applets
{
internal class PlayerSelectApplet : IApplet
{
private Horizon _system;
private readonly Horizon _system;
private AppletSession _normalSession;
#pragma warning disable IDE0052 // Remove unread private member
private AppletSession _interactiveSession;
#pragma warning restore IDE0052
public event EventHandler AppletStateChanged;
@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Applets
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
{
_normalSession = normalSession;
_normalSession = normalSession;
_interactiveSession = interactiveSession;
// TODO(jduncanator): Parse PlayerSelectConfig from input data
@ -44,15 +46,14 @@ namespace Ryujinx.HLE.HOS.Applets
{
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)PlayerSelectResult.Success);
using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
using BinaryWriter writer = new(stream);
currentUser.UserId.Write(writer);
writer.Write((ulong)PlayerSelectResult.Success);
return stream.ToArray();
}
currentUser.UserId.Write(writer);
return stream.ToArray();
}
}
}

View File

@ -3,6 +3,6 @@
enum PlayerSelectResult : ulong
{
Success = 0,
Failure = 2
Failure = 2,
}
}

View File

@ -14,4 +14,4 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
[GeneratedRegex("\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
private static partial Regex CJKRegex();
}
}
}

View File

@ -13,6 +13,6 @@
/// <summary>
/// Position the cursor at the end of the text
/// </summary>
End
End,
}
}

View File

@ -43,6 +43,6 @@
/// <summary>
/// [8.0.0+] Request the keyboard applet to use the MovedCursorV2 response when notifying changes in cursor position.
/// </summary>
UseMovedCursorV2 = 0xE
UseMovedCursorV2 = 0xE,
}
}

View File

@ -88,6 +88,6 @@
/// <summary>
/// Same as MovedCursorUtf8, but with additional fields.
/// </summary>
MovedCursorUtf8V2 = 0x10
MovedCursorUtf8V2 = 0x10,
}
}

View File

@ -28,6 +28,6 @@
/// <summary>
/// software keyboard is transitioning to a hidden state because the user pressed either OK or Cancel.
/// </summary>
Disappearing = 0x4
Disappearing = 0x4,
}
}

View File

@ -60,55 +60,51 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
uint resSize = 2 * sizeof(uint) + 0x1;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.FinishedInitialize, writer);
writer.Write((byte)1); // Data (ignored by the program)
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.FinishedInitialize, writer);
writer.Write((byte)1); // Data (ignored by the program)
return stream.ToArray();
}
public static byte[] Default(InlineKeyboardState state)
{
uint resSize = 2 * sizeof(uint);
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.Default, writer);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.Default, writer);
return stream.ToArray();
}
public static byte[] ChangedString(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 6 * sizeof(uint) + MaxStrLenUTF16;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.ChangedString, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.ChangedString, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true);
return stream.ToArray();
}
public static byte[] MovedCursor(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 4 * sizeof(uint) + MaxStrLenUTF16;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.MovedCursor, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.MovedCursor, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false);
return stream.ToArray();
}
public static byte[] MovedTab(string text, uint cursor, InlineKeyboardState state)
@ -117,182 +113,169 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
uint resSize = 4 * sizeof(uint) + MaxStrLenUTF16;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.MovedTab, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.MovedTab, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false);
return stream.ToArray();
}
public static byte[] DecidedEnter(string text, InlineKeyboardState state)
{
uint resSize = 3 * sizeof(uint) + MaxStrLenUTF16;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.DecidedEnter, writer);
WriteString(text, writer, MaxStrLenUTF16, Encoding.Unicode);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.DecidedEnter, writer);
WriteString(text, writer, MaxStrLenUTF16, Encoding.Unicode);
return stream.ToArray();
}
public static byte[] DecidedCancel(InlineKeyboardState state)
{
uint resSize = 2 * sizeof(uint);
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.DecidedCancel, writer);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.DecidedCancel, writer);
return stream.ToArray();
}
public static byte[] ChangedStringUtf8(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 6 * sizeof(uint) + MaxStrLenUTF8;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true);
return stream.ToArray();
}
public static byte[] MovedCursorUtf8(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 4 * sizeof(uint) + MaxStrLenUTF8;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false);
return stream.ToArray();
}
public static byte[] DecidedEnterUtf8(string text, InlineKeyboardState state)
{
uint resSize = 3 * sizeof(uint) + MaxStrLenUTF8;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.DecidedEnterUtf8, writer);
WriteString(text, writer, MaxStrLenUTF8, Encoding.UTF8);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.DecidedEnterUtf8, writer);
WriteString(text, writer, MaxStrLenUTF8, Encoding.UTF8);
return stream.ToArray();
}
public static byte[] UnsetCustomizeDic(InlineKeyboardState state)
{
uint resSize = 2 * sizeof(uint);
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.UnsetCustomizeDic, writer);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.UnsetCustomizeDic, writer);
return stream.ToArray();
}
public static byte[] ReleasedUserWordInfo(InlineKeyboardState state)
{
uint resSize = 2 * sizeof(uint);
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.ReleasedUserWordInfo, writer);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.ReleasedUserWordInfo, writer);
return stream.ToArray();
}
public static byte[] UnsetCustomizedDictionaries(InlineKeyboardState state)
{
uint resSize = 2 * sizeof(uint);
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.UnsetCustomizedDictionaries, writer);
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.UnsetCustomizedDictionaries, writer);
return stream.ToArray();
}
public static byte[] ChangedStringV2(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 6 * sizeof(uint) + MaxStrLenUTF16 + 0x1;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.ChangedStringV2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true);
writer.Write((byte)0); // Flag == 0
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.ChangedStringV2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, true);
writer.Write((byte)0); // Flag == 0
return stream.ToArray();
}
public static byte[] MovedCursorV2(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 4 * sizeof(uint) + MaxStrLenUTF16 + 0x1;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.MovedCursorV2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false);
writer.Write((byte)0); // Flag == 0
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.MovedCursorV2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF16, Encoding.Unicode, false);
writer.Write((byte)0); // Flag == 0
return stream.ToArray();
}
public static byte[] ChangedStringUtf8V2(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 6 * sizeof(uint) + MaxStrLenUTF8 + 0x1;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8V2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true);
writer.Write((byte)0); // Flag == 0
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.ChangedStringUtf8V2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, true);
writer.Write((byte)0); // Flag == 0
return stream.ToArray();
}
public static byte[] MovedCursorUtf8V2(string text, uint cursor, InlineKeyboardState state)
{
uint resSize = 4 * sizeof(uint) + MaxStrLenUTF8 + 0x1;
using (MemoryStream stream = new MemoryStream(new byte[resSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8V2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false);
writer.Write((byte)0); // Flag == 0
using MemoryStream stream = new(new byte[resSize]);
using BinaryWriter writer = new(stream);
return stream.ToArray();
}
BeginResponse(state, InlineKeyboardResponse.MovedCursorUtf8V2, writer);
WriteStringWithCursor(text, cursor, writer, MaxStrLenUTF8, Encoding.UTF8, false);
writer.Write((byte)0); // Flag == 0
return stream.ToArray();
}
}
}

View File

@ -13,6 +13,6 @@
/// <summary>
/// Displays the text entry area as a multi-line field.
/// </summary>
MultiLine
MultiLine,
}
}

View File

@ -8,10 +8,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
[Flags]
enum InvalidButtonFlags : uint
{
None = 0,
None = 0,
AnalogStickL = 1 << 1,
AnalogStickR = 1 << 2,
ZL = 1 << 3,
ZR = 1 << 4,
ZL = 1 << 3,
ZR = 1 << 4,
}
}

View File

@ -51,6 +51,6 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// <summary>
/// Prohibits characters outside of those allowed in Mii Nicknames.
/// </summary>
Username = 1 << 8
Username = 1 << 8,
}
}

View File

@ -8,19 +8,19 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
[Flags]
enum KeyboardCalcFlags : ulong
{
Initialize = 0x1,
SetVolume = 0x2,
Appear = 0x4,
SetInputText = 0x8,
SetCursorPos = 0x10,
SetUtf8Mode = 0x20,
Initialize = 0x1,
SetVolume = 0x2,
Appear = 0x4,
SetInputText = 0x8,
SetCursorPos = 0x10,
SetUtf8Mode = 0x20,
SetKeyboardBackground = 0x100,
SetKeyboardOptions1 = 0x200,
SetKeyboardOptions2 = 0x800,
EnableSeGroup = 0x2000,
DisableSeGroup = 0x4000,
SetBackspaceEnabled = 0x8000,
AppearTrigger = 0x10000,
MustShow = Appear | SetInputText | AppearTrigger
SetKeyboardOptions1 = 0x200,
SetKeyboardOptions2 = 0x800,
EnableSeGroup = 0x2000,
DisableSeGroup = 0x4000,
SetBackspaceEnabled = 0x8000,
AppearTrigger = 0x10000,
MustShow = Appear | SetInputText | AppearTrigger,
}
}

View File

@ -5,8 +5,8 @@
/// </summary>
enum KeyboardMiniaturizationMode : byte
{
None = 0,
Auto = 1,
Forced = 2
None = 0,
Auto = 1,
Forced = 2,
}
}

View File

@ -24,16 +24,16 @@
/// Synonymous with default.
/// </summary>
FullLatin = 3,
/// <summary>
/// All UTF-16 characters except CJK characters allowed.
/// </summary>
Alphabet = 4,
SimplifiedChinese = 5,
SimplifiedChinese = 5,
TraditionalChinese = 6,
Korean = 7,
LanguageSet2 = 8,
LanguageSet2Latin = 9,
Korean = 7,
LanguageSet2 = 8,
LanguageSet2Latin = 9,
}
}

View File

@ -9,4 +9,4 @@
Accept = 1,
Cancel = 2,
}
}
}

View File

@ -13,6 +13,6 @@
/// <summary>
/// Hide input characters.
/// </summary>
Enabled
Enabled,
}
}

View File

@ -77,41 +77,42 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
[MarshalAs(UnmanagedType.I1)]
public bool UseSaveData;
public uint Reserved3;
public uint Reserved3;
public ushort Reserved4;
public byte Reserved5;
public ulong Reserved6;
public ulong Reserved7;
public byte Reserved5;
public ulong Reserved6;
public ulong Reserved7;
public SoftwareKeyboardAppearEx ToExtended()
{
SoftwareKeyboardAppearEx appear = new SoftwareKeyboardAppearEx();
appear.KeyboardMode = KeyboardMode;
appear.OkText = OkText;
appear.LeftOptionalSymbolKey = LeftOptionalSymbolKey;
appear.RightOptionalSymbolKey = RightOptionalSymbolKey;
appear.PredictionEnabled = PredictionEnabled;
appear.CancelButtonDisabled = CancelButtonDisabled;
appear.InvalidChars = InvalidChars;
appear.TextMaxLength = TextMaxLength;
appear.TextMinLength = TextMinLength;
appear.UseNewLine = UseNewLine;
appear.MiniaturizationMode = MiniaturizationMode;
appear.Reserved1 = Reserved1;
appear.Reserved2 = Reserved2;
appear.InvalidButtons = InvalidButtons;
appear.UseSaveData = UseSaveData;
appear.Reserved3 = Reserved3;
appear.Reserved4 = Reserved4;
appear.Reserved5 = Reserved5;
appear.Uid0 = Reserved6;
appear.Uid1 = Reserved7;
appear.SamplingNumber = 0;
appear.Reserved6 = 0;
appear.Reserved7 = 0;
appear.Reserved8 = 0;
appear.Reserved9 = 0;
SoftwareKeyboardAppearEx appear = new()
{
KeyboardMode = KeyboardMode,
OkText = OkText,
LeftOptionalSymbolKey = LeftOptionalSymbolKey,
RightOptionalSymbolKey = RightOptionalSymbolKey,
PredictionEnabled = PredictionEnabled,
CancelButtonDisabled = CancelButtonDisabled,
InvalidChars = InvalidChars,
TextMaxLength = TextMaxLength,
TextMinLength = TextMinLength,
UseNewLine = UseNewLine,
MiniaturizationMode = MiniaturizationMode,
Reserved1 = Reserved1,
Reserved2 = Reserved2,
InvalidButtons = InvalidButtons,
UseSaveData = UseSaveData,
Reserved3 = Reserved3,
Reserved4 = Reserved4,
Reserved5 = Reserved5,
Uid0 = Reserved6,
Uid1 = Reserved7,
SamplingNumber = 0,
Reserved6 = 0,
Reserved7 = 0,
Reserved8 = 0,
Reserved9 = 0,
};
return appear;
}

View File

@ -77,9 +77,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
[MarshalAs(UnmanagedType.I1)]
public bool UseSaveData;
public uint Reserved3;
public uint Reserved3;
public ushort Reserved4;
public byte Reserved5;
public byte Reserved5;
/// <summary>
/// The id of the user associated with the appear request.

View File

@ -21,16 +21,16 @@ namespace Ryujinx.HLE.HOS.Applets
{
private const string DefaultInputText = "Ryujinx";
private const int StandardBufferSize = 0x7D8;
private const int StandardBufferSize = 0x7D8;
private const int InteractiveBufferSize = 0x7D4;
private const int MaxUserWords = 0x1388;
private const int MaxUiTextSize = 100;
private const int MaxUserWords = 0x1388;
private const int MaxUiTextSize = 100;
private const Key CycleInputModesKey = Key.F6;
private readonly Switch _device;
private SoftwareKeyboardState _foregroundState = SoftwareKeyboardState.Uninitialized;
private SoftwareKeyboardState _foregroundState = SoftwareKeyboardState.Uninitialized;
private volatile InlineKeyboardState _backgroundState = InlineKeyboardState.Uninitialized;
private bool _isBackground = false;
@ -42,23 +42,25 @@ namespace Ryujinx.HLE.HOS.Applets
private SoftwareKeyboardConfig _keyboardForegroundConfig;
// Configuration for background (inline) mode.
private SoftwareKeyboardInitialize _keyboardBackgroundInitialize;
#pragma warning disable IDE0052 // Remove unread private member
private SoftwareKeyboardInitialize _keyboardBackgroundInitialize;
private SoftwareKeyboardCustomizeDic _keyboardBackgroundDic;
private SoftwareKeyboardDictSet _keyboardBackgroundDictSet;
private SoftwareKeyboardUserWord[] _keyboardBackgroundUserWords;
private SoftwareKeyboardDictSet _keyboardBackgroundDictSet;
#pragma warning restore IDE0052
private SoftwareKeyboardUserWord[] _keyboardBackgroundUserWords;
private byte[] _transferMemory;
private string _textValue = "";
private int _cursorBegin = 0;
private Encoding _encoding = Encoding.Unicode;
private KeyboardResult _lastResult = KeyboardResult.NotSet;
private string _textValue = "";
private int _cursorBegin = 0;
private Encoding _encoding = Encoding.Unicode;
private KeyboardResult _lastResult = KeyboardResult.NotSet;
private IDynamicTextInputHandler _dynamicTextInputHandler = null;
private SoftwareKeyboardRenderer _keyboardRenderer = null;
private NpadReader _npads = null;
private bool _canAcceptController = false;
private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard;
private SoftwareKeyboardRenderer _keyboardRenderer = null;
private NpadReader _npads = null;
private bool _canAcceptController = false;
private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard;
private readonly object _lock = new();
@ -73,12 +75,12 @@ namespace Ryujinx.HLE.HOS.Applets
{
lock (_lock)
{
_normalSession = normalSession;
_normalSession = normalSession;
_interactiveSession = interactiveSession;
_interactiveSession.DataAvailable += OnInteractiveData;
var launchParams = _normalSession.Pop();
var launchParams = _normalSession.Pop();
var keyboardConfig = _normalSession.Pop();
_isBackground = keyboardConfig.Length == Unsafe.SizeOf<SoftwareKeyboardInitialize>();
@ -88,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Applets
// Initialize the keyboard applet in background mode.
_keyboardBackgroundInitialize = MemoryMarshal.Read<SoftwareKeyboardInitialize>(keyboardConfig);
_backgroundState = InlineKeyboardState.Uninitialized;
_backgroundState = InlineKeyboardState.Uninitialized;
if (_device.UiHandler == null)
{
@ -99,11 +101,11 @@ namespace Ryujinx.HLE.HOS.Applets
// Create a text handler that converts keyboard strokes to strings.
_dynamicTextInputHandler = _device.UiHandler.CreateDynamicTextInputHandler();
_dynamicTextInputHandler.TextChangedEvent += HandleTextChangedEvent;
_dynamicTextInputHandler.KeyPressedEvent += HandleKeyPressedEvent;
_dynamicTextInputHandler.KeyPressedEvent += HandleKeyPressedEvent;
_npads = new NpadReader(_device);
_npads.NpadButtonDownEvent += HandleNpadButtonDownEvent;
_npads.NpadButtonUpEvent += HandleNpadButtonUpEvent;
_npads.NpadButtonUpEvent += HandleNpadButtonUpEvent;
_keyboardRenderer = new SoftwareKeyboardRenderer(_device.UiHandler.HostUiTheme);
}
@ -217,7 +219,7 @@ namespace Ryujinx.HLE.HOS.Applets
_keyboardForegroundConfig.SubmitText : "OK"),
StringLengthMin = _keyboardForegroundConfig.StringLengthMin,
StringLengthMax = _keyboardForegroundConfig.StringLengthMax,
InitialText = initialText
InitialText = initialText,
};
_lastResult = _device.UiHandler.DisplayInputDialog(args, out _textValue) ? KeyboardResult.Accept : KeyboardResult.Cancel;
@ -237,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Applets
// we truncate it.
if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax)
{
_textValue = _textValue.Substring(0, _keyboardForegroundConfig.StringLengthMax);
_textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax];
}
// Does the application want to validate the text itself?
@ -319,178 +321,177 @@ namespace Ryujinx.HLE.HOS.Applets
// request from the game, this is because the inline keyboard is expected to
// keep running in the background sending data by itself.
using (MemoryStream stream = new MemoryStream(data))
using (BinaryReader reader = new BinaryReader(stream))
using MemoryStream stream = new(data);
using BinaryReader reader = new(stream);
var request = (InlineKeyboardRequest)reader.ReadUInt32();
long remaining;
Logger.Debug?.Print(LogClass.ServiceAm, $"Keyboard received command {request} in state {_backgroundState}");
switch (request)
{
var request = (InlineKeyboardRequest)reader.ReadUInt32();
long remaining;
Logger.Debug?.Print(LogClass.ServiceAm, $"Keyboard received command {request} in state {_backgroundState}");
switch (request)
{
case InlineKeyboardRequest.UseChangedStringV2:
Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseChangedStringV2");
break;
case InlineKeyboardRequest.UseMovedCursorV2:
Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseMovedCursorV2");
break;
case InlineKeyboardRequest.SetUserWordInfo:
// Read the user word info data.
case InlineKeyboardRequest.UseChangedStringV2:
Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseChangedStringV2");
break;
case InlineKeyboardRequest.UseMovedCursorV2:
Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseMovedCursorV2");
break;
case InlineKeyboardRequest.SetUserWordInfo:
// Read the user word info data.
remaining = stream.Length - stream.Position;
if (remaining < sizeof(int))
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info of {remaining} bytes");
}
else
{
int wordsCount = reader.ReadInt32();
int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>();
remaining = stream.Length - stream.Position;
if (remaining < sizeof(int))
if (wordsCount > MaxUserWords)
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info of {remaining} bytes");
Logger.Warning?.Print(LogClass.ServiceAm, $"Received {wordsCount} User Words but the maximum is {MaxUserWords}");
}
else if (wordsCount * wordSize != remaining)
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info data of {remaining} bytes for {wordsCount} words");
}
else
{
int wordsCount = reader.ReadInt32();
int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>();
remaining = stream.Length - stream.Position;
_keyboardBackgroundUserWords = new SoftwareKeyboardUserWord[wordsCount];
if (wordsCount > MaxUserWords)
for (int word = 0; word < wordsCount; word++)
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received {wordsCount} User Words but the maximum is {MaxUserWords}");
}
else if (wordsCount * wordSize != remaining)
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info data of {remaining} bytes for {wordsCount} words");
}
else
{
_keyboardBackgroundUserWords = new SoftwareKeyboardUserWord[wordsCount];
for (int word = 0; word < wordsCount; word++)
{
_keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>();
}
_keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>();
}
}
_interactiveSession.Push(InlineResponses.ReleasedUserWordInfo(_backgroundState));
break;
case InlineKeyboardRequest.SetCustomizeDic:
// Read the custom dic data.
remaining = stream.Length - stream.Position;
if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>())
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes");
}
else
{
_keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>();
}
break;
case InlineKeyboardRequest.SetCustomizedDictionaries:
// Read the custom dictionaries data.
remaining = stream.Length - stream.Position;
if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>())
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes");
}
else
{
_keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>();
}
break;
case InlineKeyboardRequest.Calc:
// The Calc request is used to communicate configuration changes and commands to the keyboard.
// Fields in the Calc struct and operations are masked by the Flags field.
}
_interactiveSession.Push(InlineResponses.ReleasedUserWordInfo(_backgroundState));
break;
case InlineKeyboardRequest.SetCustomizeDic:
// Read the custom dic data.
remaining = stream.Length - stream.Position;
if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>())
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes");
}
else
{
_keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>();
}
break;
case InlineKeyboardRequest.SetCustomizedDictionaries:
// Read the custom dictionaries data.
remaining = stream.Length - stream.Position;
if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>())
{
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes");
}
else
{
_keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>();
}
break;
case InlineKeyboardRequest.Calc:
// The Calc request is used to communicate configuration changes and commands to the keyboard.
// Fields in the Calc struct and operations are masked by the Flags field.
// Read the Calc data.
SoftwareKeyboardCalcEx newCalc;
remaining = stream.Length - stream.Position;
if (remaining == Marshal.SizeOf<SoftwareKeyboardCalc>())
{
var keyboardCalcData = reader.ReadBytes((int)remaining);
var keyboardCalc = ReadStruct<SoftwareKeyboardCalc>(keyboardCalcData);
// Read the Calc data.
SoftwareKeyboardCalcEx newCalc;
remaining = stream.Length - stream.Position;
if (remaining == Marshal.SizeOf<SoftwareKeyboardCalc>())
{
var keyboardCalcData = reader.ReadBytes((int)remaining);
var keyboardCalc = ReadStruct<SoftwareKeyboardCalc>(keyboardCalcData);
newCalc = keyboardCalc.ToExtended();
}
else if (remaining == Marshal.SizeOf<SoftwareKeyboardCalcEx>() || remaining == SoftwareKeyboardCalcEx.AlternativeSize)
{
var keyboardCalcData = reader.ReadBytes((int)remaining);
newCalc = keyboardCalc.ToExtended();
}
else if (remaining == Marshal.SizeOf<SoftwareKeyboardCalcEx>() || remaining == SoftwareKeyboardCalcEx.AlternativeSize)
{
var keyboardCalcData = reader.ReadBytes((int)remaining);
newCalc = ReadStruct<SoftwareKeyboardCalcEx>(keyboardCalcData);
}
else
{
Logger.Error?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Calc of {remaining} bytes");
newCalc = ReadStruct<SoftwareKeyboardCalcEx>(keyboardCalcData);
}
else
{
Logger.Error?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Calc of {remaining} bytes");
newCalc = new SoftwareKeyboardCalcEx();
}
newCalc = new SoftwareKeyboardCalcEx();
}
// Process each individual operation specified in the flags.
// Process each individual operation specified in the flags.
bool updateText = false;
bool updateText = false;
if ((newCalc.Flags & KeyboardCalcFlags.Initialize) != 0)
{
_interactiveSession.Push(InlineResponses.FinishedInitialize(_backgroundState));
if ((newCalc.Flags & KeyboardCalcFlags.Initialize) != 0)
{
_interactiveSession.Push(InlineResponses.FinishedInitialize(_backgroundState));
_backgroundState = InlineKeyboardState.Initialized;
}
_backgroundState = InlineKeyboardState.Initialized;
}
if ((newCalc.Flags & KeyboardCalcFlags.SetCursorPos) != 0)
{
_cursorBegin = newCalc.CursorPos;
updateText = true;
if ((newCalc.Flags & KeyboardCalcFlags.SetCursorPos) != 0)
{
_cursorBegin = newCalc.CursorPos;
updateText = true;
Logger.Debug?.Print(LogClass.ServiceAm, $"Cursor position set to {_cursorBegin}");
}
Logger.Debug?.Print(LogClass.ServiceAm, $"Cursor position set to {_cursorBegin}");
}
if ((newCalc.Flags & KeyboardCalcFlags.SetInputText) != 0)
{
_textValue = newCalc.InputText;
updateText = true;
if ((newCalc.Flags & KeyboardCalcFlags.SetInputText) != 0)
{
_textValue = newCalc.InputText;
updateText = true;
Logger.Debug?.Print(LogClass.ServiceAm, $"Input text set to {_textValue}");
}
Logger.Debug?.Print(LogClass.ServiceAm, $"Input text set to {_textValue}");
}
if ((newCalc.Flags & KeyboardCalcFlags.SetUtf8Mode) != 0)
{
_encoding = newCalc.UseUtf8 ? Encoding.UTF8 : Encoding.Default;
if ((newCalc.Flags & KeyboardCalcFlags.SetUtf8Mode) != 0)
{
_encoding = newCalc.UseUtf8 ? Encoding.UTF8 : Encoding.Default;
Logger.Debug?.Print(LogClass.ServiceAm, $"Encoding set to {_encoding}");
}
Logger.Debug?.Print(LogClass.ServiceAm, $"Encoding set to {_encoding}");
}
if (updateText)
{
_dynamicTextInputHandler.SetText(_textValue, _cursorBegin);
_keyboardRenderer.UpdateTextState(_textValue, _cursorBegin, _cursorBegin, null, null);
}
if (updateText)
{
_dynamicTextInputHandler.SetText(_textValue, _cursorBegin);
_keyboardRenderer.UpdateTextState(_textValue, _cursorBegin, _cursorBegin, null, null);
}
if ((newCalc.Flags & KeyboardCalcFlags.MustShow) != 0)
{
ActivateFrontend();
if ((newCalc.Flags & KeyboardCalcFlags.MustShow) != 0)
{
ActivateFrontend();
_backgroundState = InlineKeyboardState.Shown;
_backgroundState = InlineKeyboardState.Shown;
PushChangedString(_textValue, (uint)_cursorBegin, _backgroundState);
}
PushChangedString(_textValue, (uint)_cursorBegin, _backgroundState);
}
// Send the response to the Calc
_interactiveSession.Push(InlineResponses.Default(_backgroundState));
break;
case InlineKeyboardRequest.Finalize:
// Destroy the frontend.
DestroyFrontend();
// The calling application wants to close the keyboard applet and will wait for a state change.
_backgroundState = InlineKeyboardState.Uninitialized;
AppletStateChanged?.Invoke(this, null);
break;
default:
// We shouldn't be able to get here through standard swkbd execution.
Logger.Warning?.Print(LogClass.ServiceAm, $"Invalid Software Keyboard request {request} during state {_backgroundState}");
_interactiveSession.Push(InlineResponses.Default(_backgroundState));
break;
}
// Send the response to the Calc
_interactiveSession.Push(InlineResponses.Default(_backgroundState));
break;
case InlineKeyboardRequest.Finalize:
// Destroy the frontend.
DestroyFrontend();
// The calling application wants to close the keyboard applet and will wait for a state change.
_backgroundState = InlineKeyboardState.Uninitialized;
AppletStateChanged?.Invoke(this, null);
break;
default:
// We shouldn't be able to get here through standard swkbd execution.
Logger.Warning?.Print(LogClass.ServiceAm, $"Invalid Software Keyboard request {request} during state {_backgroundState}");
_interactiveSession.Push(InlineResponses.Default(_backgroundState));
break;
}
}
private void ActivateFrontend()
{
Logger.Debug?.Print(LogClass.ServiceAm, $"Activating software keyboard frontend");
Logger.Debug?.Print(LogClass.ServiceAm, "Activating software keyboard frontend");
_inputMode = KeyboardInputMode.ControllerAndKeyboard;
@ -509,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Applets
private void DeactivateFrontend()
{
Logger.Debug?.Print(LogClass.ServiceAm, $"Deactivating software keyboard frontend");
Logger.Debug?.Print(LogClass.ServiceAm, "Deactivating software keyboard frontend");
_inputMode = KeyboardInputMode.ControllerAndKeyboard;
_inputMode = KeyboardInputMode.ControllerAndKeyboard;
_canAcceptController = false;
_dynamicTextInputHandler.TextProcessingEnabled = false;
@ -520,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Applets
private void DestroyFrontend()
{
Logger.Debug?.Print(LogClass.ServiceAm, $"Destroying software keyboard frontend");
Logger.Debug?.Print(LogClass.ServiceAm, "Destroying software keyboard frontend");
_keyboardRenderer?.Dispose();
_keyboardRenderer = null;
@ -528,7 +529,7 @@ namespace Ryujinx.HLE.HOS.Applets
if (_dynamicTextInputHandler != null)
{
_dynamicTextInputHandler.TextChangedEvent -= HandleTextChangedEvent;
_dynamicTextInputHandler.KeyPressedEvent -= HandleKeyPressedEvent;
_dynamicTextInputHandler.KeyPressedEvent -= HandleKeyPressedEvent;
_dynamicTextInputHandler.Dispose();
_dynamicTextInputHandler = null;
}
@ -536,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Applets
if (_npads != null)
{
_npads.NpadButtonDownEvent -= HandleNpadButtonDownEvent;
_npads.NpadButtonUpEvent -= HandleNpadButtonUpEvent;
_npads.NpadButtonUpEvent -= HandleNpadButtonUpEvent;
_npads = null;
}
}
@ -551,7 +552,7 @@ namespace Ryujinx.HLE.HOS.Applets
{
AdvanceInputMode();
bool typingEnabled = InputModeTypingEnabled();
bool typingEnabled = InputModeTypingEnabled();
bool controllerEnabled = InputModeControllerEnabled();
_dynamicTextInputHandler.TextProcessingEnabled = typingEnabled;
@ -575,14 +576,14 @@ namespace Ryujinx.HLE.HOS.Applets
if (text.Length > MaxUiTextSize)
{
// Limit the text size and change it back.
text = text.Substring(0, MaxUiTextSize);
text = text[..MaxUiTextSize];
cursorBegin = Math.Min(cursorBegin, MaxUiTextSize);
cursorEnd = Math.Min(cursorEnd, MaxUiTextSize);
cursorEnd = Math.Min(cursorEnd, MaxUiTextSize);
_dynamicTextInputHandler.SetText(text, cursorBegin, cursorEnd);
}
_textValue = text;
_textValue = text;
_cursorBegin = cursorBegin;
_keyboardRenderer.UpdateTextState(text, cursorBegin, cursorEnd, overwriteMode, null);
@ -646,7 +647,7 @@ namespace Ryujinx.HLE.HOS.Applets
private void PushUpdatedState(string text, int cursorBegin, KeyboardResult result)
{
_lastResult = result;
_textValue = text;
_textValue = text;
bool cancel = result == KeyboardResult.Cancel;
bool accept = result == KeyboardResult.Accept;
@ -734,33 +735,31 @@ namespace Ryujinx.HLE.HOS.Applets
{
int bufferSize = interactive ? InteractiveBufferSize : StandardBufferSize;
using (MemoryStream stream = new MemoryStream(new byte[bufferSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
using MemoryStream stream = new(new byte[bufferSize]);
using BinaryWriter writer = new(stream);
byte[] output = _encoding.GetBytes(_textValue);
if (!interactive)
{
byte[] output = _encoding.GetBytes(_textValue);
// Result Code.
writer.Write(_lastResult == KeyboardResult.Accept ? 0U : 1U);
}
else
{
// In interactive mode, we write the length of the text as a long, rather than
// a result code. This field is inclusive of the 64-bit size.
writer.Write((long)output.Length + 8);
}
if (!interactive)
{
// Result Code.
writer.Write(_lastResult == KeyboardResult.Accept ? 0U : 1U);
}
else
{
// In interactive mode, we write the length of the text as a long, rather than
// a result code. This field is inclusive of the 64-bit size.
writer.Write((long)output.Length + 8);
}
writer.Write(output);
writer.Write(output);
if (!interactive)
{
_normalSession.Push(stream.ToArray());
}
else
{
_interactiveSession.Push(stream.ToArray());
}
if (!interactive)
{
_normalSession.Push(stream.ToArray());
}
else
{
_interactiveSession.Push(stream.ToArray());
}
}
@ -787,7 +786,7 @@ namespace Ryujinx.HLE.HOS.Applets
return string.Empty;
}
StringBuilder sb = new StringBuilder(capacity: input.Length);
StringBuilder sb = new(capacity: input.Length);
foreach (char c in input)
{
if (!char.IsControl(c))

View File

@ -174,45 +174,46 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
public SoftwareKeyboardCalcEx ToExtended()
{
SoftwareKeyboardCalcEx calc = new SoftwareKeyboardCalcEx();
calc.Unknown = Unknown;
calc.Size = Size;
calc.Unknown1 = Unknown1;
calc.Unknown2 = Unknown2;
calc.Flags = Flags;
calc.Initialize = Initialize;
calc.Volume = Volume;
calc.CursorPos = CursorPos;
calc.Appear = Appear.ToExtended();
calc.InputText = InputText;
calc.UseUtf8 = UseUtf8;
calc.Unknown3 = Unknown3;
calc.BackspaceEnabled = BackspaceEnabled;
calc.Unknown4 = Unknown4;
calc.Unknown5 = Unknown5;
calc.KeytopAsFloating = KeytopAsFloating;
calc.FooterScalable = FooterScalable;
calc.AlphaEnabledInInputMode = AlphaEnabledInInputMode;
calc.InputModeFadeType = InputModeFadeType;
calc.TouchDisabled = TouchDisabled;
calc.HardwareKeyboardDisabled = HardwareKeyboardDisabled;
calc.Unknown6 = Unknown6;
calc.Unknown7 = Unknown7;
calc.KeytopScale0 = KeytopScale0;
calc.KeytopScale1 = KeytopScale1;
calc.KeytopTranslate0 = KeytopTranslate0;
calc.KeytopTranslate1 = KeytopTranslate1;
calc.KeytopBgAlpha = KeytopBgAlpha;
calc.FooterBgAlpha = FooterBgAlpha;
calc.BalloonScale = BalloonScale;
calc.Unknown8 = Unknown8;
calc.Unknown9 = Unknown9;
calc.Unknown10 = Unknown10;
calc.Unknown11 = Unknown11;
calc.SeGroup = SeGroup;
calc.TriggerFlag = TriggerFlag;
calc.Trigger = Trigger;
SoftwareKeyboardCalcEx calc = new()
{
Unknown = Unknown,
Size = Size,
Unknown1 = Unknown1,
Unknown2 = Unknown2,
Flags = Flags,
Initialize = Initialize,
Volume = Volume,
CursorPos = CursorPos,
Appear = Appear.ToExtended(),
InputText = InputText,
UseUtf8 = UseUtf8,
Unknown3 = Unknown3,
BackspaceEnabled = BackspaceEnabled,
Unknown4 = Unknown4,
Unknown5 = Unknown5,
KeytopAsFloating = KeytopAsFloating,
FooterScalable = FooterScalable,
AlphaEnabledInInputMode = AlphaEnabledInInputMode,
InputModeFadeType = InputModeFadeType,
TouchDisabled = TouchDisabled,
HardwareKeyboardDisabled = HardwareKeyboardDisabled,
Unknown6 = Unknown6,
Unknown7 = Unknown7,
KeytopScale0 = KeytopScale0,
KeytopScale1 = KeytopScale1,
KeytopTranslate0 = KeytopTranslate0,
KeytopTranslate1 = KeytopTranslate1,
KeytopBgAlpha = KeytopBgAlpha,
FooterBgAlpha = FooterBgAlpha,
BalloonScale = BalloonScale,
Unknown8 = Unknown8,
Unknown9 = Unknown9,
Unknown10 = Unknown10,
Unknown11 = Unknown11,
SeGroup = SeGroup,
TriggerFlag = TriggerFlag,
Trigger = Trigger,
};
return calc;
}

View File

@ -8,10 +8,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct SoftwareKeyboardConfig
{
private const int SubmitTextLength = 8;
private const int HeaderTextLength = 64;
private const int SubmitTextLength = 8;
private const int HeaderTextLength = 64;
private const int SubtitleTextLength = 128;
private const int GuideTextLength = 256;
private const int GuideTextLength = 256;
/// <summary>
/// Type of keyboard.

View File

@ -10,16 +10,16 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// </summary>
internal class SoftwareKeyboardRenderer : IDisposable
{
private const int TextBoxBlinkSleepMilliseconds = 100;
private const int TextBoxBlinkSleepMilliseconds = 100;
private const int RendererWaitTimeoutMilliseconds = 100;
private readonly object _stateLock = new();
private readonly object _stateLock = new();
private SoftwareKeyboardUiState _state = new SoftwareKeyboardUiState();
private SoftwareKeyboardRendererBase _renderer;
private readonly SoftwareKeyboardUiState _state = new();
private readonly SoftwareKeyboardRendererBase _renderer;
private TimedAction _textBoxBlinkTimedAction = new TimedAction();
private TimedAction _renderAction = new TimedAction();
private readonly TimedAction _textBoxBlinkTimedAction = new();
private readonly TimedAction _renderAction = new();
public SoftwareKeyboardRenderer(IHostUiTheme uiTheme)
{
@ -47,10 +47,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
private static void StartRenderer(TimedAction timedAction, SoftwareKeyboardRendererBase renderer, SoftwareKeyboardUiState state, object stateLock)
{
SoftwareKeyboardUiState internalState = new SoftwareKeyboardUiState();
SoftwareKeyboardUiState internalState = new();
bool canCreateSurface = false;
bool needsUpdate = true;
bool needsUpdate = true;
timedAction.Reset(() =>
{
@ -61,6 +61,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
return;
}
#pragma warning disable IDE0055 // Disable formatting
needsUpdate = UpdateStateField(ref state.InputText, ref internalState.InputText);
needsUpdate |= UpdateStateField(ref state.CursorBegin, ref internalState.CursorBegin);
needsUpdate |= UpdateStateField(ref state.CursorEnd, ref internalState.CursorEnd);
@ -70,6 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
needsUpdate |= UpdateStateField(ref state.TypingEnabled, ref internalState.TypingEnabled);
needsUpdate |= UpdateStateField(ref state.ControllerEnabled, ref internalState.ControllerEnabled);
needsUpdate |= UpdateStateField(ref state.TextBoxBlinkCounter, ref internalState.TextBoxBlinkCounter);
#pragma warning restore IDE0055
canCreateSurface = state.SurfaceInfo != null && internalState.SurfaceInfo == null;
@ -104,16 +106,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
return false;
}
#pragma warning disable CS8632
public void UpdateTextState(string? inputText, int? cursorBegin, int? cursorEnd, bool? overwriteMode, bool? typingEnabled)
#pragma warning restore CS8632
public void UpdateTextState(string inputText, int? cursorBegin, int? cursorEnd, bool? overwriteMode, bool? typingEnabled)
{
lock (_stateLock)
{
// Update the parameters that were provided.
_state.InputText = inputText != null ? inputText : _state.InputText;
_state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin);
_state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd);
_state.InputText = inputText ?? _state.InputText;
_state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin);
_state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd);
_state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode);
_state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled);
@ -130,8 +130,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
lock (_stateLock)
{
// Update the parameters that were provided.
_state.AcceptPressed = acceptPressed.GetValueOrDefault(_state.AcceptPressed);
_state.CancelPressed = cancelPressed.GetValueOrDefault(_state.CancelPressed);
_state.AcceptPressed = acceptPressed.GetValueOrDefault(_state.AcceptPressed);
_state.CancelPressed = cancelPressed.GetValueOrDefault(_state.CancelPressed);
_state.ControllerEnabled = controllerEnabled.GetValueOrDefault(_state.ControllerEnabled);
// Tell the render thread there is something new to render.

View File

@ -21,47 +21,47 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
public const int TextBoxBlinkThreshold = 8;
const string MessageText = "Please use the keyboard to input text";
const string AcceptText = "Accept";
const string CancelText = "Cancel";
const string MessageText = "Please use the keyboard to input text";
const string AcceptText = "Accept";
const string CancelText = "Cancel";
const string ControllerToggleText = "Toggle input";
private readonly object _bufferLock = new();
private RenderingSurfaceInfo _surfaceInfo = null;
private Image<Argb32> _surface = null;
private byte[] _bufferData = null;
private Image<Argb32> _surface = null;
private byte[] _bufferData = null;
private Image _ryujinxLogo = null;
private Image _padAcceptIcon = null;
private Image _padCancelIcon = null;
private Image _keyModeIcon = null;
private readonly Image _ryujinxLogo = null;
private readonly Image _padAcceptIcon = null;
private readonly Image _padCancelIcon = null;
private readonly Image _keyModeIcon = null;
private float _textBoxOutlineWidth;
private float _padPressedPenWidth;
private readonly float _textBoxOutlineWidth;
private readonly float _padPressedPenWidth;
private Color _textNormalColor;
private Color _textSelectedColor;
private Color _textOverCursorColor;
private readonly Color _textNormalColor;
private readonly Color _textSelectedColor;
private readonly Color _textOverCursorColor;
private IBrush _panelBrush;
private IBrush _disabledBrush;
private IBrush _cursorBrush;
private IBrush _selectionBoxBrush;
private readonly IBrush _panelBrush;
private readonly IBrush _disabledBrush;
private readonly IBrush _cursorBrush;
private readonly IBrush _selectionBoxBrush;
private Pen _textBoxOutlinePen;
private Pen _cursorPen;
private Pen _selectionBoxPen;
private Pen _padPressedPen;
private readonly Pen _textBoxOutlinePen;
private readonly Pen _cursorPen;
private readonly Pen _selectionBoxPen;
private readonly Pen _padPressedPen;
private int _inputTextFontSize;
private readonly int _inputTextFontSize;
private Font _messageFont;
private Font _inputTextFont;
private Font _labelsTextFont;
private RectangleF _panelRectangle;
private Point _logoPosition;
private float _messagePositionY;
private Point _logoPosition;
private float _messagePositionY;
public SoftwareKeyboardRendererBase(IHostUiTheme uiTheme)
{
@ -72,35 +72,35 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png";
string padCancelIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnB.png";
string keyModeIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_KeyF6.png";
string keyModeIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_KeyF6.png";
_padAcceptIcon = LoadResource(Assembly.GetExecutingAssembly(), padAcceptIconPath , 0, 0);
_padCancelIcon = LoadResource(Assembly.GetExecutingAssembly(), padCancelIconPath , 0, 0);
_keyModeIcon = LoadResource(Assembly.GetExecutingAssembly(), keyModeIconPath , 0, 0);
_padAcceptIcon = LoadResource(Assembly.GetExecutingAssembly(), padAcceptIconPath, 0, 0);
_padCancelIcon = LoadResource(Assembly.GetExecutingAssembly(), padCancelIconPath, 0, 0);
_keyModeIcon = LoadResource(Assembly.GetExecutingAssembly(), keyModeIconPath, 0, 0);
Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
Color borderColor = ToColor(uiTheme.DefaultBorderColor);
Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
Color borderColor = ToColor(uiTheme.DefaultBorderColor);
Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
_textNormalColor = ToColor(uiTheme.DefaultForegroundColor);
_textSelectedColor = ToColor(uiTheme.SelectionForegroundColor);
_textNormalColor = ToColor(uiTheme.DefaultForegroundColor);
_textSelectedColor = ToColor(uiTheme.SelectionForegroundColor);
_textOverCursorColor = ToColor(uiTheme.DefaultForegroundColor, null, true);
float cursorWidth = 2;
_textBoxOutlineWidth = 2;
_padPressedPenWidth = 2;
_padPressedPenWidth = 2;
_panelBrush = new SolidBrush(panelColor);
_disabledBrush = new SolidBrush(panelTransparentColor);
_cursorBrush = new SolidBrush(_textNormalColor);
_panelBrush = new SolidBrush(panelColor);
_disabledBrush = new SolidBrush(panelTransparentColor);
_cursorBrush = new SolidBrush(_textNormalColor);
_selectionBoxBrush = new SolidBrush(selectionBackgroundColor);
_textBoxOutlinePen = new Pen(borderColor, _textBoxOutlineWidth);
_cursorPen = new Pen(_textNormalColor, cursorWidth);
_selectionBoxPen = new Pen(selectionBackgroundColor, cursorWidth);
_padPressedPen = new Pen(borderColor, _padPressedPenWidth);
_cursorPen = new Pen(_textNormalColor, cursorWidth);
_selectionBoxPen = new Pen(selectionBackgroundColor, cursorWidth);
_padPressedPen = new Pen(borderColor, _padPressedPenWidth);
_inputTextFontSize = 20;
@ -111,22 +111,21 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
// Try a list of fonts in case any of them is not available in the system.
string[] availableFonts = new string[]
{
string[] availableFonts = {
uiThemeFontFamily,
"Liberation Sans",
"FreeSans",
"DejaVu Sans",
"Lucida Grande"
"Lucida Grande",
};
foreach (string fontFamily in availableFonts)
{
try
{
_messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
_inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
_labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
_messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
_inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
_labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
return;
}
@ -138,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
}
private Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
{
var a = (byte)(color.A * 255);
var r = (byte)(color.R * 255);
@ -155,14 +154,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a));
}
private Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
{
Stream resourceStream = assembly.GetManifestResourceStream(resourcePath);
return LoadResource(resourceStream, newWidth, newHeight);
}
private Image LoadResource(Stream resourceStream, int newWidth, int newHeight)
private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight)
{
Debug.Assert(resourceStream != null);
@ -176,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
return image;
}
private void SetGraphicsOptions(IImageProcessingContext context)
private static void SetGraphicsOptions(IImageProcessingContext context)
{
context.GetGraphicsOptions().Antialias = true;
context.GetShapeGraphicsOptions().GraphicsOptions.Antialias = true;
@ -198,9 +197,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
context.DrawImage(_ryujinxLogo, _logoPosition, 1);
float halfWidth = _panelRectangle.Width / 2;
float buttonsY = _panelRectangle.Y + 185;
float buttonsY = _panelRectangle.Y + 185;
PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY);
PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
DrawControllerToggle(context, disableButtonPosition);
});
@ -215,11 +214,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
_surface.Mutate(context =>
{
var messageRectangle = MeasureString(MessageText, _messageFont);
float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
float messagePositionY = _messagePositionY - messageRectangle.Y;
var messagePosition = new PointF(messagePositionX, messagePositionY);
var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
var messageRectangle = MeasureString(MessageText, _messageFont);
float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
float messagePositionY = _messagePositionY - messageRectangle.Y;
var messagePosition = new PointF(messagePositionX, messagePositionY);
var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
SetGraphicsOptions(context);
@ -238,11 +237,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
DrawTextBox(context, state);
float halfWidth = _panelRectangle.Width / 2;
float buttonsY = _panelRectangle.Y + 185;
float buttonsY = _panelRectangle.Y + 185;
PointF acceptButtonPosition = new PointF(halfWidth - 180, buttonsY);
PointF cancelButtonPosition = new PointF(halfWidth , buttonsY);
PointF disableButtonPosition = new PointF(halfWidth + 180, buttonsY);
PointF acceptButtonPosition = new(halfWidth - 180, buttonsY);
PointF cancelButtonPosition = new(halfWidth, buttonsY);
PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
@ -262,7 +261,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Use the whole area of the image to draw, even the alignment, otherwise it may shear the final
// image if the pitch is different.
uint totalWidth = _surfaceInfo.Pitch / 4;
uint totalWidth = _surfaceInfo.Pitch / 4;
uint totalHeight = _surfaceInfo.Size / _surfaceInfo.Pitch;
Debug.Assert(_surfaceInfo.Width <= totalWidth);
@ -277,10 +276,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
private void ComputeConstants()
{
int totalWidth = (int)_surfaceInfo.Width;
int totalWidth = (int)_surfaceInfo.Width;
int totalHeight = (int)_surfaceInfo.Height;
int panelHeight = 240;
int panelHeight = 240;
int panelPositionY = totalHeight - panelHeight;
_panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight);
@ -294,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
}
private static RectangleF MeasureString(string text, Font font)
{
RendererOptions options = new RendererOptions(font);
RendererOptions options = new(font);
if (text == "")
{
@ -310,7 +309,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
private static RectangleF MeasureString(ReadOnlySpan<char> text, Font font)
{
RendererOptions options = new RendererOptions(font);
RendererOptions options = new(font);
if (text == "")
{
@ -327,14 +326,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
var inputTextRectangle = MeasureString(state.InputText, _inputTextFont);
float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
float boxHeight = 32;
float boxY = _panelRectangle.Y + 110;
float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
float boxY = _panelRectangle.Y + 110;
float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
RectangleF boxRectangle = new RectangleF(boxX, boxY, boxWidth, boxHeight);
RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight);
RectangleF boundRectangle = new RectangleF(_panelRectangle.X, boxY - _textBoxOutlineWidth,
RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth,
_panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth);
context.Fill(_panelBrush, boundRectangle);
@ -350,11 +349,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Draw the cursor on top of the text and redraw the text with a different color if necessary.
Color cursorTextColor;
Color cursorTextColor;
IBrush cursorBrush;
Pen cursorPen;
Pen cursorPen;
float cursorPositionYTop = inputTextY + 1;
float cursorPositionYTop = inputTextY + 1;
float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1;
float cursorPositionXLeft;
float cursorPositionXRight;
@ -366,34 +365,34 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
Debug.Assert(state.InputText.Length > 0);
cursorTextColor = _textSelectedColor;
cursorBrush = _selectionBoxBrush;
cursorPen = _selectionBoxPen;
cursorBrush = _selectionBoxBrush;
cursorPen = _selectionBoxPen;
ReadOnlySpan<char> textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin);
ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd);
ReadOnlySpan<char> textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd);
var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont);
var selectionEndRectangle = MeasureString(textUntilEnd , _inputTextFont);
var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont);
cursorVisible = true;
cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
cursorVisible = true;
cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
}
else
{
cursorTextColor = _textOverCursorColor;
cursorBrush = _cursorBrush;
cursorPen = _cursorPen;
cursorBrush = _cursorBrush;
cursorPen = _cursorPen;
if (state.TextBoxBlinkCounter < TextBoxBlinkThreshold)
{
// Show the blinking cursor.
int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin);
ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin);
var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin);
ReadOnlySpan<char> textUntilCursor = state.InputText.AsSpan(0, cursorBegin);
var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
cursorVisible = true;
cursorVisible = true;
cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
if (state.OverwriteMode)
@ -402,8 +401,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
if (state.CursorBegin < state.InputText.Length)
{
textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1);
cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1);
cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
}
else
@ -419,20 +418,19 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
}
else
{
cursorPositionXLeft = inputTextX;
cursorPositionXLeft = inputTextX;
cursorPositionXRight = inputTextX;
}
}
if (state.TypingEnabled && cursorVisible)
{
float cursorWidth = cursorPositionXRight - cursorPositionXLeft;
float cursorWidth = cursorPositionXRight - cursorPositionXLeft;
float cursorHeight = cursorPositionYBottom - cursorPositionYTop;
if (cursorWidth == 0)
{
PointF[] points = new PointF[]
{
PointF[] points = {
new PointF(cursorPositionXLeft, cursorPositionYTop),
new PointF(cursorPositionXLeft, cursorPositionYBottom),
};
@ -443,10 +441,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
context.Draw(cursorPen , cursorRectangle);
context.Draw(cursorPen, cursorRectangle);
context.Fill(cursorBrush, cursorRectangle);
Image<Argb32> textOverCursor = new Image<Argb32>((int)cursorRectangle.Width, (int)cursorRectangle.Height);
Image<Argb32> textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height);
textOverCursor.Mutate(context =>
{
var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y);
@ -470,9 +468,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
// Use relative positions so we can center the the entire drawing later.
float iconX = 0;
float iconY = 0;
float iconWidth = icon.Width;
float iconX = 0;
float iconY = 0;
float iconWidth = icon.Width;
float iconHeight = icon.Height;
var labelRectangle = MeasureString(label, _labelsTextFont);
@ -480,18 +478,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
float labelPositionX = iconWidth + 8 - labelRectangle.X;
float labelPositionY = 3;
float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
float fullHeight = iconHeight;
// Convert all relative positions into absolute.
float originX = (int)(point.X - fullWidth / 2);
float originX = (int)(point.X - fullWidth / 2);
float originY = (int)(point.Y - fullHeight / 2);
iconX += originX;
iconY += originY;
var iconPosition = new Point((int)iconX, (int)iconY);
var iconPosition = new Point((int)iconX, (int)iconY);
var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
@ -526,7 +524,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Use relative positions so we can center the the entire drawing later.
float keyWidth = _keyModeIcon.Width;
float keyWidth = _keyModeIcon.Width;
float keyHeight = _keyModeIcon.Height;
float labelPositionX = keyWidth + 8 - labelRectangle.X;
@ -535,18 +533,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
float keyX = 0;
float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2);
float fullWidth = labelPositionX + labelRectangle.Width;
float fullWidth = labelPositionX + labelRectangle.Width;
float fullHeight = Math.Max(labelPositionY + labelRectangle.Height, keyHeight);
// Convert all relative positions into absolute.
float originX = (int)(point.X - fullWidth / 2);
float originX = (int)(point.X - fullWidth / 2);
float originY = (int)(point.Y - fullHeight / 2);
keyX += originX;
keyY += originY;
var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
var overlayPosition = new Point((int)keyX, (int)keyY);
context.DrawImage(_keyModeIcon, overlayPosition, 1);

View File

@ -23,6 +23,6 @@
/// <summary>
/// swkbd has completed.
/// </summary>
Complete
Complete,
}
}
}

View File

@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Applets
public int StringLengthMin;
public int StringLengthMax;
}
}
}

View File

@ -7,15 +7,15 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// </summary>
internal class SoftwareKeyboardUiState
{
public string InputText = "";
public int CursorBegin = 0;
public int CursorEnd = 0;
public bool AcceptPressed = false;
public bool CancelPressed = false;
public bool OverwriteMode = false;
public bool TypingEnabled = true;
public bool ControllerEnabled = true;
public int TextBoxBlinkCounter = 0;
public string InputText = "";
public int CursorBegin = 0;
public int CursorEnd = 0;
public bool AcceptPressed = false;
public bool CancelPressed = false;
public bool OverwriteMode = false;
public bool TypingEnabled = true;
public bool ControllerEnabled = true;
public int TextBoxBlinkCounter = 0;
public RenderingSurfaceInfo SurfaceInfo = null;
}

View File

@ -19,8 +19,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
public SleepSubstepData(int sleepMilliseconds)
{
SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep);
SleepCount = sleepMilliseconds / SleepMilliseconds;
SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep);
SleepCount = sleepMilliseconds / SleepMilliseconds;
SleepRemainderMilliseconds = sleepMilliseconds - SleepCount * SleepMilliseconds;
}
}

View File

@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS
break;
default:
throw new ArgumentOutOfRangeException();
throw new InvalidOperationException($"{nameof(mode)} contains an invalid value: {mode}");
}
}

View File

@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class ArraySubscriptingExpression : BaseNode
{
private BaseNode _leftNode;
private BaseNode _subscript;
private readonly BaseNode _leftNode;
private readonly BaseNode _subscript;
public ArraySubscriptingExpression(BaseNode leftNode, BaseNode subscript) : base(NodeType.ArraySubscriptingExpression)
{
_leftNode = leftNode;
_leftNode = leftNode;
_subscript = subscript;
}
@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
_leftNode.Print(writer);
writer.Write(")[");
_subscript.Print(writer);
writer.Write("]");
writer.Write("]");
}
}
}
}

View File

@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class ArrayType : BaseNode
{
private BaseNode _base;
private BaseNode _dimensionExpression;
private string _dimensionString;
private readonly BaseNode _base;
private readonly BaseNode _dimensionExpression;
private readonly string _dimensionString;
public ArrayType(BaseNode Base, BaseNode dimensionExpression = null) : base(NodeType.ArrayType)
{
_base = Base;
_base = Base;
_dimensionExpression = dimensionExpression;
}
public ArrayType(BaseNode Base, string dimensionString) : base(NodeType.ArrayType)
{
_base = Base;
_base = Base;
_dimensionString = dimensionString;
}
@ -46,9 +46,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
writer.Write(_dimensionString);
}
else if (_dimensionExpression != null)
else
{
_dimensionExpression.Print(writer);
_dimensionExpression?.Print(writer);
}
writer.Write("]");
@ -56,4 +56,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
_base.PrintRight(writer);
}
}
}
}

View File

@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
ConversionOperatorType,
LocalName,
CtorVtableSpecialName,
ArrayType
ArrayType,
}
public abstract class BaseNode
@ -99,15 +99,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
return null;
}
public virtual void PrintRight(TextWriter writer) {}
public virtual void PrintRight(TextWriter writer) { }
public override string ToString()
{
StringWriter writer = new StringWriter();
StringWriter writer = new();
Print(writer);
return writer.ToString();
}
}
}
}

View File

@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class BinaryExpression : BaseNode
{
private BaseNode _leftPart;
private string _name;
private BaseNode _rightPart;
private readonly BaseNode _leftPart;
private readonly string _name;
private readonly BaseNode _rightPart;
public BinaryExpression(BaseNode leftPart, string name, BaseNode rightPart) : base(NodeType.BinaryExpression)
{
_leftPart = leftPart;
_name = name;
_leftPart = leftPart;
_name = name;
_rightPart = rightPart;
}
@ -38,4 +38,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
}
}
}
}
}

View File

@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class BracedExpression : BaseNode
{
private BaseNode _element;
private BaseNode _expression;
private bool _isArrayExpression;
private readonly BaseNode _element;
private readonly BaseNode _expression;
private readonly bool _isArrayExpression;
public BracedExpression(BaseNode element, BaseNode expression, bool isArrayExpression) : base(NodeType.BracedExpression)
{
_element = element;
_expression = expression;
_element = element;
_expression = expression;
_isArrayExpression = isArrayExpression;
}

View File

@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class BracedRangeExpression : BaseNode
{
private BaseNode _firstNode;
private BaseNode _lastNode;
private BaseNode _expression;
private readonly BaseNode _firstNode;
private readonly BaseNode _lastNode;
private readonly BaseNode _expression;
public BracedRangeExpression(BaseNode firstNode, BaseNode lastNode, BaseNode expression) : base(NodeType.BracedRangeExpression)
{
_firstNode = firstNode;
_lastNode = lastNode;
_firstNode = firstNode;
_lastNode = lastNode;
_expression = expression;
}

View File

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class CallExpression : NodeArray
{
private BaseNode _callee;
private readonly BaseNode _callee;
public CallExpression(BaseNode callee, List<BaseNode> nodes) : base(nodes, NodeType.CallExpression)
{
@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(")");
}
}
}
}

View File

@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class CastExpression : BaseNode
{
private string _kind;
private BaseNode _to;
private BaseNode _from;
private readonly string _kind;
private readonly BaseNode _to;
private readonly BaseNode _from;
public CastExpression(string kind, BaseNode to, BaseNode from) : base(NodeType.CastExpression)
{
_kind = kind;
_to = to;
_to = to;
_from = from;
}
@ -25,4 +25,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(")");
}
}
}
}

View File

@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class ConditionalExpression : BaseNode
{
private BaseNode _thenNode;
private BaseNode _elseNode;
private BaseNode _conditionNode;
private readonly BaseNode _thenNode;
private readonly BaseNode _elseNode;
private readonly BaseNode _conditionNode;
public ConditionalExpression(BaseNode conditionNode, BaseNode thenNode, BaseNode elseNode) : base(NodeType.ConditionalExpression)
{
_thenNode = thenNode;
_thenNode = thenNode;
_conditionNode = conditionNode;
_elseNode = elseNode;
_elseNode = elseNode;
}
public override void PrintLeft(TextWriter writer)
@ -26,4 +26,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(")");
}
}
}
}

View File

@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class ConversionExpression : BaseNode
{
private BaseNode _typeNode;
private BaseNode _expressions;
private readonly BaseNode _typeNode;
private readonly BaseNode _expressions;
public ConversionExpression(BaseNode typeNode, BaseNode expressions) : base(NodeType.ConversionExpression)
{
_typeNode = typeNode;
_typeNode = typeNode;
_expressions = expressions;
}
@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
_expressions.Print(writer);
}
}
}
}

View File

@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
Child.Print(writer);
}
}
}
}

View File

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class CtorDtorNameType : ParentNode
{
private bool _isDestructor;
private readonly bool _isDestructor;
public CtorDtorNameType(BaseNode name, bool isDestructor) : base(NodeType.CtorDtorNameType, name)
{
@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(Child.GetName());
}
}
}
}

View File

@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class CtorVtableSpecialName : BaseNode
{
private BaseNode _firstType;
private BaseNode _secondType;
private readonly BaseNode _firstType;
private readonly BaseNode _secondType;
public CtorVtableSpecialName(BaseNode firstType, BaseNode secondType) : base(NodeType.CtorVtableSpecialName)
{
_firstType = firstType;
_firstType = firstType;
_secondType = secondType;
}
@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
_secondType.Print(writer);
}
}
}
}

View File

@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class DeleteExpression : ParentNode
{
private bool _isGlobal;
private bool _isArrayExpression;
private readonly bool _isGlobal;
private readonly bool _isArrayExpression;
public DeleteExpression(BaseNode child, bool isGlobal, bool isArrayExpression) : base(NodeType.DeleteExpression, child)
{
_isGlobal = isGlobal;
_isGlobal = isGlobal;
_isArrayExpression = isArrayExpression;
}
@ -30,4 +30,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
Child.Print(writer);
}
}
}
}

View File

@ -12,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
Child.PrintLeft(writer);
}
}
}
}

View File

@ -13,4 +13,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(")");
}
}
}
}

View File

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class ElaboratedType : ParentNode
{
private string _elaborated;
private readonly string _elaborated;
public ElaboratedType(string elaborated, BaseNode type) : base(NodeType.ElaboratedType, type)
{
@ -18,4 +18,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
Child.Print(writer);
}
}
}
}

View File

@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class EnclosedExpression : BaseNode
{
private string _prefix;
private BaseNode _expression;
private string _postfix;
private readonly string _prefix;
private readonly BaseNode _expression;
private readonly string _postfix;
public EnclosedExpression(string prefix, BaseNode expression, string postfix) : base(NodeType.EnclosedExpression)
{
_prefix = prefix;
_prefix = prefix;
_expression = expression;
_postfix = postfix;
_postfix = postfix;
}
public override void PrintLeft(TextWriter writer)
@ -22,4 +22,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(_postfix);
}
}
}
}

View File

@ -4,21 +4,21 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class EncodedFunction : BaseNode
{
private BaseNode _name;
private BaseNode _params;
private BaseNode _cv;
private BaseNode _ref;
private BaseNode _attrs;
private BaseNode _ret;
private readonly BaseNode _name;
private readonly BaseNode _params;
private readonly BaseNode _cv;
private readonly BaseNode _ref;
private readonly BaseNode _attrs;
private readonly BaseNode _ret;
public EncodedFunction(BaseNode name, BaseNode Params, BaseNode cv, BaseNode Ref, BaseNode attrs, BaseNode ret) : base(NodeType.NameType)
{
_name = name;
_name = name;
_params = Params;
_cv = cv;
_ref = Ref;
_attrs = attrs;
_ret = ret;
_cv = cv;
_ref = Ref;
_attrs = attrs;
_ret = ret;
}
public override void PrintLeft(TextWriter writer)
@ -45,33 +45,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
public override void PrintRight(TextWriter writer)
{
writer.Write("(");
if (_params != null)
{
_params.Print(writer);
}
_params?.Print(writer);
writer.Write(")");
if (_ret != null)
{
_ret.PrintRight(writer);
}
if (_cv != null)
{
_cv.Print(writer);
}
if (_ref != null)
{
_ref.Print(writer);
}
if (_attrs != null)
{
_attrs.Print(writer);
}
_ret?.PrintRight(writer);
_cv?.Print(writer);
_ref?.Print(writer);
_attrs?.Print(writer);
}
}
}
}

View File

@ -4,17 +4,17 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class FoldExpression : BaseNode
{
private bool _isLeftFold;
private string _operatorName;
private BaseNode _expression;
private BaseNode _initializer;
private readonly bool _isLeftFold;
private readonly string _operatorName;
private readonly BaseNode _expression;
private readonly BaseNode _initializer;
public FoldExpression(bool isLeftFold, string operatorName, BaseNode expression, BaseNode initializer) : base(NodeType.FunctionParameter)
{
_isLeftFold = isLeftFold;
_isLeftFold = isLeftFold;
_operatorName = operatorName;
_expression = expression;
_initializer = initializer;
_expression = expression;
_initializer = initializer;
}
public override void PrintLeft(TextWriter writer)
@ -45,4 +45,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
writer.Write(")");
}
}
}
}

View File

@ -6,7 +6,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
// TODO: Compute inside the Demangler
public BaseNode Reference;
private int _index;
#pragma warning disable IDE0052 // Remove unread private member
private readonly int _index;
#pragma warning restore IDE0052
public ForwardTemplateReference(int index) : base(NodeType.ForwardTemplateReference)
{

View File

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class FunctionParameter : BaseNode
{
private string _number;
private readonly string _number;
public FunctionParameter(string number) : base(NodeType.FunctionParameter)
{
@ -21,4 +21,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
}
}
}
}
}

View File

@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
{
public class FunctionType : BaseNode
{
private BaseNode _returnType;
private BaseNode _params;
private BaseNode _cvQualifier;
private SimpleReferenceType _referenceQualifier;
private BaseNode _exceptionSpec;
private readonly BaseNode _returnType;
private readonly BaseNode _params;
private readonly BaseNode _cvQualifier;
private readonly SimpleReferenceType _referenceQualifier;
private readonly BaseNode _exceptionSpec;
public FunctionType(BaseNode returnType, BaseNode Params, BaseNode cvQualifier, SimpleReferenceType referenceQualifier, BaseNode exceptionSpec) : base(NodeType.FunctionType)
{
_returnType = returnType;
_params = Params;
_cvQualifier = cvQualifier;
_returnType = returnType;
_params = Params;
_cvQualifier = cvQualifier;
_referenceQualifier = referenceQualifier;
_exceptionSpec = exceptionSpec;
_exceptionSpec = exceptionSpec;
}
public override void PrintLeft(TextWriter writer)
@ -58,4 +58,4 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
return true;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More