Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ccbbaddbcb | ||
|
8bf102d2cd | ||
|
2adf031830 | ||
|
bb4a28b525 | ||
|
a8fbcdae9f | ||
|
4e81ab4229 | ||
|
4117c13377 | ||
|
20a392ad55 |
4
.github/workflows/mako.yml
vendored
4
.github/workflows/mako.yml
vendored
@@ -9,10 +9,6 @@ on:
|
|||||||
types: [created, edited]
|
types: [created, edited]
|
||||||
issues:
|
issues:
|
||||||
types: [opened, edited, reopened, pinned, milestoned, demilestoned, assigned, unassigned, labeled, unlabeled]
|
types: [opened, edited, reopened, pinned, milestoned, demilestoned, assigned, unassigned, labeled, unlabeled]
|
||||||
pull_request_review:
|
|
||||||
types: [submitted, dismissed]
|
|
||||||
pull_request_review_comment:
|
|
||||||
types: [created, edited]
|
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, edited, reopened, synchronize, ready_for_review, assigned, unassigned]
|
types: [opened, edited, reopened, synchronize, ready_for_review, assigned, unassigned]
|
||||||
|
|
||||||
|
@@ -8,8 +8,8 @@
|
|||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.7" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.7" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.7" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.7" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.7" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.7" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.10" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.10" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.13" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
|
@@ -875,6 +875,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
SetVfp("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||||
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
|
SetVfp("<<<<11101x110110xxxx101x01x0xxxx", InstName.Vrintr, InstEmit32.Vrintr_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110111xxxx101x01x0xxxx", InstName.Vrintx, InstEmit32.Vrintx_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
SetVfp("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, OpCode32SimdS.Create, OpCode32SimdS.CreateT32);
|
||||||
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
SetVfp("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, OpCode32SimdSel.Create, OpCode32SimdSel.CreateT32);
|
||||||
@@ -995,6 +996,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32);
|
SetAsimd("1111001x1x000xxxxxxx<<x10x01xxxx", InstName.Vorr, InstEmit32.Vorr_II, OpCode32SimdImm.Create, OpCode32SimdImm.CreateT32);
|
||||||
SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
SetAsimd("111100111x11<<00xxxx0110xxx0xxxx", InstName.Vpadal, InstEmit32.Vpadal, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
SetAsimd("111100111x11<<00xxxx0010xxx0xxxx", InstName.Vpaddl, InstEmit32.Vpaddl, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
|
||||||
SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("1111001x0x<<xxxxxxxx1010x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
SetAsimd("111100110x00xxxxxxxx1111x0x0xxxx", InstName.Vpmax, InstEmit32.Vpmax_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
|
||||||
|
@@ -1115,6 +1115,13 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Vpadal(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
|
||||||
|
EmitVectorPairwiseTernaryLongOpI32(context, (op1, op2, op3) => context.Add(context.Add(op1, op2), op3), op.Opc != 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Vpaddl(ArmEmitterContext context)
|
public static void Vpaddl(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
@@ -578,6 +578,22 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VRINTR (floating-point).
|
||||||
|
public static void Vrintr_S(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintiS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF32(context, (op1) =>
|
||||||
|
{
|
||||||
|
return EmitRoundByRMode(context, op1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VRINTZ (floating-point).
|
// VRINTZ (floating-point).
|
||||||
public static void Vrint_Z(ArmEmitterContext context)
|
public static void Vrint_Z(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
|
@@ -673,6 +673,35 @@ namespace ARMeilleure.Instructions
|
|||||||
context.Copy(GetVecA32(op.Qd), res);
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorPairwiseTernaryLongOpI32(ArmEmitterContext context, Func3I emit, bool signed)
|
||||||
|
{
|
||||||
|
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||||
|
|
||||||
|
int elems = op.GetBytesCount() >> op.Size;
|
||||||
|
int pairs = elems >> 1;
|
||||||
|
|
||||||
|
Operand res = GetVecA32(op.Qd);
|
||||||
|
|
||||||
|
for (int index = 0; index < pairs; index++)
|
||||||
|
{
|
||||||
|
int pairIndex = index * 2;
|
||||||
|
Operand m1 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex, op.Size, signed);
|
||||||
|
Operand m2 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex + 1, op.Size, signed);
|
||||||
|
|
||||||
|
if (op.Size == 2)
|
||||||
|
{
|
||||||
|
m1 = signed ? context.SignExtend32(OperandType.I64, m1) : context.ZeroExtend32(OperandType.I64, m1);
|
||||||
|
m2 = signed ? context.SignExtend32(OperandType.I64, m2) : context.ZeroExtend32(OperandType.I64, m2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand d1 = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size + 1, signed);
|
||||||
|
|
||||||
|
res = EmitVectorInsert(context, res, emit(m1, m2, d1), op.Id + index, op.Size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVecA32(op.Qd), res);
|
||||||
|
}
|
||||||
|
|
||||||
// Narrow
|
// Narrow
|
||||||
|
|
||||||
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit, bool signed = false)
|
||||||
|
@@ -637,6 +637,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Vorn,
|
Vorn,
|
||||||
Vorr,
|
Vorr,
|
||||||
Vpadd,
|
Vpadd,
|
||||||
|
Vpadal,
|
||||||
Vpaddl,
|
Vpaddl,
|
||||||
Vpmax,
|
Vpmax,
|
||||||
Vpmin,
|
Vpmin,
|
||||||
@@ -656,6 +657,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Vrintm,
|
Vrintm,
|
||||||
Vrintn,
|
Vrintn,
|
||||||
Vrintp,
|
Vrintp,
|
||||||
|
Vrintr,
|
||||||
Vrintx,
|
Vrintx,
|
||||||
Vrshr,
|
Vrshr,
|
||||||
Vrshrn,
|
Vrshrn,
|
||||||
|
@@ -72,6 +72,7 @@
|
|||||||
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
||||||
"GameListContextMenuCreateShortcut": "Create Application Shortcut",
|
"GameListContextMenuCreateShortcut": "Create Application Shortcut",
|
||||||
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
|
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
|
||||||
|
"GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application",
|
||||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||||
"StatusBarSystemVersion": "System Version: {0}",
|
"StatusBarSystemVersion": "System Version: {0}",
|
||||||
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||||
|
@@ -9,6 +9,7 @@ using Ryujinx.Ava.UI.Windows;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Applets;
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(path))
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
SvgSource source = new();
|
SvgSource source = new(default(Uri));
|
||||||
|
|
||||||
source.Load(EmbeddedResources.GetStream(path));
|
source.Load(EmbeddedResources.GetStream(path));
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
Click="CreateApplicationShortcut_Click"
|
Click="CreateApplicationShortcut_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCreateShortcut}"
|
Header="{locale:Locale GameListContextMenuCreateShortcut}"
|
||||||
IsEnabled="{Binding CreateShortcutEnabled}"
|
IsEnabled="{Binding CreateShortcutEnabled}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCreateShortcutToolTip}" />
|
ToolTip.Tip="{OnPlatform Default={locale:Locale GameListContextMenuCreateShortcutToolTip}, macOS={locale:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenUserSaveDirectory_Click"
|
Click="OpenUserSaveDirectory_Click"
|
||||||
|
@@ -388,6 +388,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
{
|
{
|
||||||
_contentDialogOverlayWindow.Content = null;
|
_contentDialogOverlayWindow.Content = null;
|
||||||
_contentDialogOverlayWindow.Close();
|
_contentDialogOverlayWindow.Close();
|
||||||
|
_contentDialogOverlayWindow = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@@ -17,14 +17,16 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool InSd { get; }
|
||||||
public string Path { get; }
|
public string Path { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
public ModModel(string path, string name, bool enabled)
|
public ModModel(string path, string name, bool enabled, bool inSd)
|
||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = enabled;
|
Enabled = enabled;
|
||||||
|
InSd = inSd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -180,7 +180,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_controllerImage))
|
if (!string.IsNullOrWhiteSpace(_controllerImage))
|
||||||
{
|
{
|
||||||
SvgSource source = new();
|
SvgSource source = new(default(Uri));
|
||||||
|
|
||||||
source.Load(EmbeddedResources.GetStream(_controllerImage));
|
source.Load(EmbeddedResources.GetStream(_controllerImage));
|
||||||
|
|
||||||
|
@@ -102,13 +102,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
foreach (var path in modsBasePaths)
|
foreach (var path in modsBasePaths)
|
||||||
{
|
{
|
||||||
|
var inSd = path == ModLoader.GetSdModsBasePath();
|
||||||
var modCache = new ModLoader.ModCache();
|
var modCache = new ModLoader.ModCache();
|
||||||
|
|
||||||
ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId);
|
ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId);
|
||||||
|
|
||||||
foreach (var mod in modCache.RomfsDirs)
|
foreach (var mod in modCache.RomfsDirs)
|
||||||
{
|
{
|
||||||
var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled);
|
var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
|
||||||
if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
|
if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
|
||||||
{
|
{
|
||||||
Mods.Add(modModel);
|
Mods.Add(modModel);
|
||||||
@@ -117,12 +118,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
foreach (var mod in modCache.RomfsContainers)
|
foreach (var mod in modCache.RomfsContainers)
|
||||||
{
|
{
|
||||||
Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled));
|
Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled, inSd));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var mod in modCache.ExefsDirs)
|
foreach (var mod in modCache.ExefsDirs)
|
||||||
{
|
{
|
||||||
var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled);
|
var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
|
||||||
if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
|
if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
|
||||||
{
|
{
|
||||||
Mods.Add(modModel);
|
Mods.Add(modModel);
|
||||||
@@ -131,7 +132,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
foreach (var mod in modCache.ExefsContainers)
|
foreach (var mod in modCache.ExefsContainers)
|
||||||
{
|
{
|
||||||
Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled));
|
Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled, inSd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +184,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public void Delete(ModModel model)
|
public void Delete(ModModel model)
|
||||||
{
|
{
|
||||||
var modsDir = ModLoader.GetApplicationDir(ModLoader.GetSdModsBasePath(), _applicationId.ToString("x16"));
|
var isSubdir = true;
|
||||||
|
var pathToDelete = model.Path;
|
||||||
|
var basePath = model.InSd ? ModLoader.GetSdModsBasePath() : ModLoader.GetModsBasePath();
|
||||||
|
var modsDir = ModLoader.GetApplicationDir(basePath, _applicationId.ToString("x16"));
|
||||||
|
|
||||||
|
if (new DirectoryInfo(model.Path).Parent?.FullName == modsDir)
|
||||||
|
{
|
||||||
|
isSubdir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSubdir)
|
||||||
|
{
|
||||||
var parentDir = String.Empty;
|
var parentDir = String.Empty;
|
||||||
|
|
||||||
foreach (var dir in Directory.GetDirectories(modsDir, "*", SearchOption.TopDirectoryOnly))
|
foreach (var dir in Directory.GetDirectories(modsDir, "*", SearchOption.TopDirectoryOnly))
|
||||||
@@ -191,6 +203,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
if (Directory.GetDirectories(dir, "*", SearchOption.AllDirectories).Contains(model.Path))
|
if (Directory.GetDirectories(dir, "*", SearchOption.AllDirectories).Contains(model.Path))
|
||||||
{
|
{
|
||||||
parentDir = dir;
|
parentDir = dir;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,13 +213,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||||
LocaleKeys.DialogModDeleteNoParentMessage,
|
LocaleKeys.DialogModDeleteNoParentMessage,
|
||||||
parentDir));
|
model.Path));
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Application, $"Deleting mod at \"{model.Path}\"");
|
Logger.Info?.Print(LogClass.Application, $"Deleting mod at \"{pathToDelete}\"");
|
||||||
Directory.Delete(parentDir, true);
|
Directory.Delete(pathToDelete, true);
|
||||||
|
|
||||||
Mods.Remove(model);
|
Mods.Remove(model);
|
||||||
OnPropertyChanged(nameof(ModCount));
|
OnPropertyChanged(nameof(ModCount));
|
||||||
|
@@ -263,7 +263,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckLaunchState()
|
private async Task CheckLaunchState()
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
||||||
{
|
{
|
||||||
@@ -271,23 +271,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
if (LinuxHelper.PkExecPath is not null)
|
if (LinuxHelper.PkExecPath is not null)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await Dispatcher.UIThread.InvokeAsync(ShowVmMaxMapCountDialog);
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
await ShowVmMaxMapCountDialog();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await Dispatcher.UIThread.InvokeAsync(ShowVmMaxMapCountWarning);
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
await ShowVmMaxMapCountWarning();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,12 +292,12 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
ShowKeyErrorOnLoad = false;
|
ShowKeyErrorOnLoad = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||||
{
|
{
|
||||||
Updater.BeginParse(this, false).ContinueWith(task =>
|
await Updater.BeginParse(this, false).ContinueWith(task =>
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
|
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
|
||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||||
@@ -404,7 +392,9 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
LoadApplications();
|
LoadApplications();
|
||||||
|
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
CheckLaunchState();
|
CheckLaunchState();
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetMainContent(Control content = null)
|
private void SetMainContent(Control content = null)
|
||||||
|
@@ -330,7 +330,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
HorizonFsClient fsClient = new(this);
|
HorizonFsClient fsClient = new(this);
|
||||||
|
|
||||||
ServiceTable = new ServiceTable();
|
ServiceTable = new ServiceTable();
|
||||||
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient, fsClient));
|
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient, fsClient, AccountManager));
|
||||||
|
|
||||||
foreach (var service in services)
|
foreach (var service in services)
|
||||||
{
|
{
|
||||||
|
@@ -4,6 +4,7 @@ using LibHac.Fs;
|
|||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -11,7 +12,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
{
|
{
|
||||||
public class AccountManager
|
public class AccountManager : IEmulatorAccountManager
|
||||||
{
|
{
|
||||||
public static readonly UserId DefaultUserId = new("00000000000000010000000000000000");
|
public static readonly UserId DefaultUserId = new("00000000000000010000000000000000");
|
||||||
|
|
||||||
@@ -106,6 +107,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
_accountSaveDataManager.Save(_profiles);
|
_accountSaveDataManager.Save(_profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OpenUserOnlinePlay(Uid userId)
|
||||||
|
{
|
||||||
|
OpenUserOnlinePlay(new UserId((long)userId.Low, (long)userId.High));
|
||||||
|
}
|
||||||
|
|
||||||
public void OpenUserOnlinePlay(UserId userId)
|
public void OpenUserOnlinePlay(UserId userId)
|
||||||
{
|
{
|
||||||
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
|
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
|
||||||
@@ -127,6 +133,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
_accountSaveDataManager.Save(_profiles);
|
_accountSaveDataManager.Save(_profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CloseUserOnlinePlay(Uid userId)
|
||||||
|
{
|
||||||
|
CloseUserOnlinePlay(new UserId((long)userId.Low, (long)userId.High));
|
||||||
|
}
|
||||||
|
|
||||||
public void CloseUserOnlinePlay(UserId userId)
|
public void CloseUserOnlinePlay(UserId userId)
|
||||||
{
|
{
|
||||||
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
|
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
|
||||||
|
@@ -1,55 +0,0 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend
|
|
||||||
{
|
|
||||||
[Service("friend:a", FriendServicePermissionLevel.Administrator)]
|
|
||||||
[Service("friend:m", FriendServicePermissionLevel.Manager)]
|
|
||||||
[Service("friend:s", FriendServicePermissionLevel.System)]
|
|
||||||
[Service("friend:u", FriendServicePermissionLevel.User)]
|
|
||||||
[Service("friend:v", FriendServicePermissionLevel.Viewer)]
|
|
||||||
class IServiceCreator : IpcService
|
|
||||||
{
|
|
||||||
private readonly FriendServicePermissionLevel _permissionLevel;
|
|
||||||
|
|
||||||
public IServiceCreator(ServiceCtx context, FriendServicePermissionLevel permissionLevel)
|
|
||||||
{
|
|
||||||
_permissionLevel = permissionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(0)]
|
|
||||||
// CreateFriendService() -> object<nn::friends::detail::ipc::IFriendService>
|
|
||||||
public ResultCode CreateFriendService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MakeObject(context, new IFriendService(_permissionLevel));
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(1)] // 2.0.0+
|
|
||||||
// CreateNotificationService(nn::account::Uid userId) -> object<nn::friends::detail::ipc::INotificationService>
|
|
||||||
public ResultCode CreateNotificationService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
MakeObject(context, new INotificationService(context, userId, _permissionLevel));
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(2)] // 4.0.0+
|
|
||||||
// CreateDaemonSuspendSessionService() -> object<nn::friends::detail::ipc::IDaemonSuspendSessionService>
|
|
||||||
public ResultCode CreateDaemonSuspendSessionService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MakeObject(context, new IDaemonSuspendSessionService(_permissionLevel));
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Friend
|
|
||||||
{
|
|
||||||
enum ResultCode
|
|
||||||
{
|
|
||||||
ModuleId = 121,
|
|
||||||
ErrorCodeShift = 9,
|
|
||||||
|
|
||||||
Success = 0,
|
|
||||||
|
|
||||||
InvalidArgument = (2 << ErrorCodeShift) | ModuleId,
|
|
||||||
InternetRequestDenied = (6 << ErrorCodeShift) | ModuleId,
|
|
||||||
NotificationQueueEmpty = (15 << ErrorCodeShift) | ModuleId,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x200, CharSet = CharSet.Ansi)]
|
|
||||||
struct Friend
|
|
||||||
{
|
|
||||||
public UserId UserId;
|
|
||||||
public long NetworkUserId;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x21)]
|
|
||||||
public string Nickname;
|
|
||||||
|
|
||||||
public UserPresence presence;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsFavourite;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsNew;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x6)]
|
|
||||||
readonly char[] Unknown;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsValid;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,24 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct FriendFilter
|
|
||||||
{
|
|
||||||
public PresenceStatusFilter PresenceStatus;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsFavoriteOnly;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsSameAppPresenceOnly;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsSameAppPlayedOnly;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool IsArbitraryAppPlayedOnly;
|
|
||||||
|
|
||||||
public long PresenceGroupId;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,34 +0,0 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 0x8)]
|
|
||||||
struct UserPresence
|
|
||||||
{
|
|
||||||
public UserId UserId;
|
|
||||||
public long LastTimeOnlineTimestamp;
|
|
||||||
public PresenceStatus Status;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool SamePresenceGroupApplication;
|
|
||||||
|
|
||||||
public Array3<byte> Unknown;
|
|
||||||
private AppKeyValueStorageHolder _appKeyValueStorage;
|
|
||||||
|
|
||||||
public Span<byte> AppKeyValueStorage => MemoryMarshal.Cast<AppKeyValueStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _appKeyValueStorage, AppKeyValueStorageHolder.Size));
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = Size)]
|
|
||||||
private struct AppKeyValueStorageHolder
|
|
||||||
{
|
|
||||||
public const int Size = 0xC0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly override string ToString()
|
|
||||||
{
|
|
||||||
return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status} }}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|
||||||
{
|
|
||||||
class IDaemonSuspendSessionService : IpcService
|
|
||||||
{
|
|
||||||
#pragma warning disable IDE0052 // Remove unread private member
|
|
||||||
private readonly FriendServicePermissionLevel _permissionLevel;
|
|
||||||
#pragma warning restore IDE0052
|
|
||||||
|
|
||||||
public IDaemonSuspendSessionService(FriendServicePermissionLevel permissionLevel)
|
|
||||||
{
|
|
||||||
_permissionLevel = permissionLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,374 +0,0 @@
|
|||||||
using LibHac.Ns;
|
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService;
|
|
||||||
using Ryujinx.Horizon.Common;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|
||||||
{
|
|
||||||
class IFriendService : IpcService
|
|
||||||
{
|
|
||||||
#pragma warning disable IDE0052 // Remove unread private member
|
|
||||||
private readonly FriendServicePermissionLevel _permissionLevel;
|
|
||||||
#pragma warning restore IDE0052
|
|
||||||
private KEvent _completionEvent;
|
|
||||||
|
|
||||||
public IFriendService(FriendServicePermissionLevel permissionLevel)
|
|
||||||
{
|
|
||||||
_permissionLevel = permissionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(0)]
|
|
||||||
// GetCompletionEvent() -> handle<copy>
|
|
||||||
public ResultCode GetCompletionEvent(ServiceCtx context)
|
|
||||||
{
|
|
||||||
_completionEvent ??= new KEvent(context.Device.System.KernelContext);
|
|
||||||
|
|
||||||
if (context.Process.HandleTable.GenerateHandle(_completionEvent.ReadableEvent, out int completionEventHandle) != Result.Success)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Out of handles!");
|
|
||||||
}
|
|
||||||
|
|
||||||
_completionEvent.WritableEvent.Signal();
|
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(1)]
|
|
||||||
// nn::friends::Cancel()
|
|
||||||
public ResultCode Cancel(ServiceCtx context)
|
|
||||||
{
|
|
||||||
// TODO: Original service sets an internal field to 1 here. Determine usage.
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10100)]
|
|
||||||
// nn::friends::GetFriendListIds(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid)
|
|
||||||
// -> int outCount, array<nn::account::NetworkServiceAccountId, 0xa>
|
|
||||||
public ResultCode GetFriendListIds(ServiceCtx context)
|
|
||||||
{
|
|
||||||
int offset = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
// Padding
|
|
||||||
context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
|
|
||||||
|
|
||||||
// Pid placeholder
|
|
||||||
context.RequestData.ReadInt64();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
|
|
||||||
context.ResponseData.Write(0);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new
|
|
||||||
{
|
|
||||||
UserId = userId.ToString(),
|
|
||||||
offset,
|
|
||||||
filter.PresenceStatus,
|
|
||||||
filter.IsFavoriteOnly,
|
|
||||||
filter.IsSameAppPresenceOnly,
|
|
||||||
filter.IsSameAppPlayedOnly,
|
|
||||||
filter.IsArbitraryAppPlayedOnly,
|
|
||||||
filter.PresenceGroupId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10101)]
|
|
||||||
// nn::friends::GetFriendList(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid)
|
|
||||||
// -> int outCount, array<nn::friends::detail::FriendImpl, 0x6>
|
|
||||||
public ResultCode GetFriendList(ServiceCtx context)
|
|
||||||
{
|
|
||||||
int offset = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
// Padding
|
|
||||||
context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
|
|
||||||
|
|
||||||
// Pid placeholder
|
|
||||||
context.RequestData.ReadInt64();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
|
|
||||||
context.ResponseData.Write(0);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new
|
|
||||||
{
|
|
||||||
UserId = userId.ToString(),
|
|
||||||
offset,
|
|
||||||
filter.PresenceStatus,
|
|
||||||
filter.IsFavoriteOnly,
|
|
||||||
filter.IsSameAppPresenceOnly,
|
|
||||||
filter.IsSameAppPlayedOnly,
|
|
||||||
filter.IsArbitraryAppPlayedOnly,
|
|
||||||
filter.PresenceGroupId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10120)] // 10.0.0+
|
|
||||||
// nn::friends::IsFriendListCacheAvailable(nn::account::Uid userId) -> bool
|
|
||||||
public ResultCode IsFriendListCacheAvailable(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Service mount the friends:/ system savedata and try to load friend.cache file, returns true if exists, false otherwise.
|
|
||||||
// NOTE: If no cache is available, guest then calls nn::friends::EnsureFriendListAvailable, we can avoid that by faking the cache check.
|
|
||||||
context.ResponseData.Write(true);
|
|
||||||
|
|
||||||
// TODO: Since we don't support friend features, it's fine to stub it for now.
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10121)] // 10.0.0+
|
|
||||||
// nn::friends::EnsureFriendListAvailable(nn::account::Uid userId)
|
|
||||||
public ResultCode EnsureFriendListAvailable(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Service mount the friends:/ system savedata and create a friend.cache file for the given user id.
|
|
||||||
// Since we don't support friend features, it's fine to stub it for now.
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10400)]
|
|
||||||
// nn::friends::GetBlockedUserListIds(int offset, nn::account::Uid userId) -> (u32, buffer<nn::account::NetworkServiceAccountId, 0xa>)
|
|
||||||
public ResultCode GetBlockedUserListIds(ServiceCtx context)
|
|
||||||
{
|
|
||||||
int offset = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
// Padding
|
|
||||||
context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
// There are no friends blocked, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
|
|
||||||
context.ResponseData.Write(0);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { offset, UserId = userId.ToString() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10420)]
|
|
||||||
// nn::friends::CheckBlockedUserListAvailability(nn::account::Uid userId) -> bool
|
|
||||||
public ResultCode CheckBlockedUserListAvailability(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
// Yes, it is available.
|
|
||||||
context.ResponseData.Write(true);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10600)]
|
|
||||||
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
|
|
||||||
public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Device.System.AccountManager.OpenUserOnlinePlay(userId);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10601)]
|
|
||||||
// nn::friends::DeclareCloseOnlinePlaySession(nn::account::Uid userId)
|
|
||||||
public ResultCode DeclareCloseOnlinePlaySession(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Device.System.AccountManager.CloseUserOnlinePlay(userId);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10610)]
|
|
||||||
// nn::friends::UpdateUserPresence(nn::account::Uid, u64, pid, buffer<nn::friends::detail::UserPresenceImpl, 0x19>)
|
|
||||||
public ResultCode UpdateUserPresence(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId uuid = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
// Pid placeholder
|
|
||||||
context.RequestData.ReadInt64();
|
|
||||||
|
|
||||||
ulong position = context.Request.PtrBuff[0].Position;
|
|
||||||
ulong size = context.Request.PtrBuff[0].Size;
|
|
||||||
|
|
||||||
ReadOnlySpan<UserPresence> userPresenceInputArray = MemoryMarshal.Cast<byte, UserPresence>(context.Memory.GetSpan(position, (int)size));
|
|
||||||
|
|
||||||
if (uuid.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray = userPresenceInputArray.ToArray() });
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10700)]
|
|
||||||
// nn::friends::GetPlayHistoryRegistrationKey(b8 unknown, nn::account::Uid) -> buffer<nn::friends::PlayHistoryRegistrationKey, 0x1a>
|
|
||||||
public ResultCode GetPlayHistoryRegistrationKey(ServiceCtx context)
|
|
||||||
{
|
|
||||||
bool unknownBool = context.RequestData.ReadBoolean();
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL);
|
|
||||||
|
|
||||||
ulong bufferPosition = context.Request.RecvListBuff[0].Position;
|
|
||||||
|
|
||||||
if (userId.IsNull)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Calls nn::friends::detail::service::core::PlayHistoryManager::GetInstance and stores the instance.
|
|
||||||
|
|
||||||
byte[] randomBytes = new byte[8];
|
|
||||||
|
|
||||||
Random.Shared.NextBytes(randomBytes);
|
|
||||||
|
|
||||||
// NOTE: Calls nn::friends::detail::service::core::UuidManager::GetInstance and stores the instance.
|
|
||||||
// Then call nn::friends::detail::service::core::AccountStorageManager::GetInstance and store the instance.
|
|
||||||
// Then it checks if an Uuid is already stored for the UserId, if not it generates a random Uuid.
|
|
||||||
// And store it in the savedata 8000000000000080 in the friends:/uid.bin file.
|
|
||||||
|
|
||||||
Array16<byte> randomGuid = new();
|
|
||||||
|
|
||||||
Guid.NewGuid().ToByteArray().AsSpan().CopyTo(randomGuid.AsSpan());
|
|
||||||
|
|
||||||
PlayHistoryRegistrationKey playHistoryRegistrationKey = new()
|
|
||||||
{
|
|
||||||
Type = 0x101,
|
|
||||||
KeyIndex = (byte)(randomBytes[0] & 7),
|
|
||||||
UserIdBool = 0, // TODO: Find it.
|
|
||||||
UnknownBool = (byte)(unknownBool ? 1 : 0), // TODO: Find it.
|
|
||||||
Reserved = new Array11<byte>(),
|
|
||||||
Uuid = randomGuid,
|
|
||||||
};
|
|
||||||
|
|
||||||
ReadOnlySpan<byte> playHistoryRegistrationKeyBuffer = SpanHelpers.AsByteSpan(ref playHistoryRegistrationKey);
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
NOTE: The service uses the KeyIndex to get a random key from a keys buffer (since the key index is stored in the returned buffer).
|
|
||||||
We currently don't support play history and online services so we can use a blank key for now.
|
|
||||||
Code for reference:
|
|
||||||
|
|
||||||
byte[] hmacKey = new byte[0x20];
|
|
||||||
|
|
||||||
HMACSHA256 hmacSha256 = new HMACSHA256(hmacKey);
|
|
||||||
byte[] hmacHash = hmacSha256.ComputeHash(playHistoryRegistrationKeyBuffer);
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
context.Memory.Write(bufferPosition, playHistoryRegistrationKeyBuffer);
|
|
||||||
context.Memory.Write(bufferPosition + 0x20, new byte[0x20]); // HmacHash
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10702)]
|
|
||||||
// nn::friends::AddPlayHistory(nn::account::Uid, u64, pid, buffer<nn::friends::PlayHistoryRegistrationKey, 0x19>, buffer<nn::friends::InAppScreenName, 0x19>, buffer<nn::friends::InAppScreenName, 0x19>)
|
|
||||||
public ResultCode AddPlayHistory(ServiceCtx context)
|
|
||||||
{
|
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
|
||||||
|
|
||||||
// Pid placeholder
|
|
||||||
context.RequestData.ReadInt64();
|
|
||||||
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
|
||||||
ulong pid = context.Request.HandleDesc.PId;
|
|
||||||
|
|
||||||
ulong playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position;
|
|
||||||
ulong playHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size;
|
|
||||||
|
|
||||||
ulong inAppScreenName1Position = context.Request.PtrBuff[1].Position;
|
|
||||||
#pragma warning restore IDE0059
|
|
||||||
ulong inAppScreenName1Size = context.Request.PtrBuff[1].Size;
|
|
||||||
|
|
||||||
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
|
||||||
ulong inAppScreenName2Position = context.Request.PtrBuff[2].Position;
|
|
||||||
#pragma warning restore IDE0059
|
|
||||||
ulong inAppScreenName2Size = context.Request.PtrBuff[2].Size;
|
|
||||||
|
|
||||||
if (userId.IsNull || inAppScreenName1Size > 0x48 || inAppScreenName2Size > 0x48)
|
|
||||||
{
|
|
||||||
return ResultCode.InvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Call nn::arp::GetApplicationControlProperty here when implemented.
|
|
||||||
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
|
||||||
ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
|
|
||||||
#pragma warning restore IDE0059
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
NOTE: The service calls nn::friends::detail::service::core::PlayHistoryManager to store informations using the registration key computed in GetPlayHistoryRegistrationKey.
|
|
||||||
Then calls nn::friends::detail::service::core::FriendListManager to update informations on the friend list.
|
|
||||||
We currently don't support play history and online services so it's fine to do nothing.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,178 +0,0 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService;
|
|
||||||
using Ryujinx.Horizon.Common;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|
||||||
{
|
|
||||||
class INotificationService : DisposableIpcService
|
|
||||||
{
|
|
||||||
private readonly UserId _userId;
|
|
||||||
private readonly FriendServicePermissionLevel _permissionLevel;
|
|
||||||
|
|
||||||
private readonly object _lock = new();
|
|
||||||
|
|
||||||
private readonly KEvent _notificationEvent;
|
|
||||||
private int _notificationEventHandle = 0;
|
|
||||||
|
|
||||||
private readonly LinkedList<NotificationInfo> _notifications;
|
|
||||||
|
|
||||||
private bool _hasNewFriendRequest;
|
|
||||||
private bool _hasFriendListUpdate;
|
|
||||||
|
|
||||||
public INotificationService(ServiceCtx context, UserId userId, FriendServicePermissionLevel permissionLevel)
|
|
||||||
{
|
|
||||||
_userId = userId;
|
|
||||||
_permissionLevel = permissionLevel;
|
|
||||||
_notifications = new LinkedList<NotificationInfo>();
|
|
||||||
_notificationEvent = new KEvent(context.Device.System.KernelContext);
|
|
||||||
|
|
||||||
_hasNewFriendRequest = false;
|
|
||||||
_hasFriendListUpdate = false;
|
|
||||||
|
|
||||||
NotificationEventHandler.Instance.RegisterNotificationService(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(0)] //2.0.0+
|
|
||||||
// nn::friends::detail::ipc::INotificationService::GetEvent() -> handle<copy>
|
|
||||||
public ResultCode GetEvent(ServiceCtx context)
|
|
||||||
{
|
|
||||||
if (_notificationEventHandle == 0)
|
|
||||||
{
|
|
||||||
if (context.Process.HandleTable.GenerateHandle(_notificationEvent.ReadableEvent, out _notificationEventHandle) != Result.Success)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Out of handles!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_notificationEventHandle);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(1)] //2.0.0+
|
|
||||||
// nn::friends::detail::ipc::INotificationService::Clear()
|
|
||||||
public ResultCode Clear(ServiceCtx context)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
_hasNewFriendRequest = false;
|
|
||||||
_hasFriendListUpdate = false;
|
|
||||||
|
|
||||||
_notifications.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(2)] // 2.0.0+
|
|
||||||
// nn::friends::detail::ipc::INotificationService::Pop() -> nn::friends::detail::ipc::SizedNotificationInfo
|
|
||||||
public ResultCode Pop(ServiceCtx context)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_notifications.Count >= 1)
|
|
||||||
{
|
|
||||||
NotificationInfo notificationInfo = _notifications.First.Value;
|
|
||||||
_notifications.RemoveFirst();
|
|
||||||
|
|
||||||
if (notificationInfo.Type == NotificationEventType.FriendListUpdate)
|
|
||||||
{
|
|
||||||
_hasFriendListUpdate = false;
|
|
||||||
}
|
|
||||||
else if (notificationInfo.Type == NotificationEventType.NewFriendRequest)
|
|
||||||
{
|
|
||||||
_hasNewFriendRequest = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.ResponseData.WriteStruct(notificationInfo);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.NotificationQueueEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignalFriendListUpdate(UserId targetId)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_userId == targetId)
|
|
||||||
{
|
|
||||||
if (!_hasFriendListUpdate)
|
|
||||||
{
|
|
||||||
NotificationInfo friendListNotification = new();
|
|
||||||
|
|
||||||
if (_notifications.Count != 0)
|
|
||||||
{
|
|
||||||
friendListNotification = _notifications.First.Value;
|
|
||||||
_notifications.RemoveFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
friendListNotification.Type = NotificationEventType.FriendListUpdate;
|
|
||||||
_hasFriendListUpdate = true;
|
|
||||||
|
|
||||||
if (_hasNewFriendRequest)
|
|
||||||
{
|
|
||||||
NotificationInfo newFriendRequestNotification = new();
|
|
||||||
|
|
||||||
if (_notifications.Count != 0)
|
|
||||||
{
|
|
||||||
newFriendRequestNotification = _notifications.First.Value;
|
|
||||||
_notifications.RemoveFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
newFriendRequestNotification.Type = NotificationEventType.NewFriendRequest;
|
|
||||||
_notifications.AddFirst(newFriendRequestNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We defer this to make sure we are on top of the queue.
|
|
||||||
_notifications.AddFirst(friendListNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
_notificationEvent.ReadableEvent.Signal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignalNewFriendRequest(UserId targetId)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if ((_permissionLevel & FriendServicePermissionLevel.ViewerMask) != 0 && _userId == targetId)
|
|
||||||
{
|
|
||||||
if (!_hasNewFriendRequest)
|
|
||||||
{
|
|
||||||
if (_notifications.Count == 100)
|
|
||||||
{
|
|
||||||
SignalFriendListUpdate(targetId);
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationInfo newFriendRequestNotification = new()
|
|
||||||
{
|
|
||||||
Type = NotificationEventType.NewFriendRequest,
|
|
||||||
};
|
|
||||||
|
|
||||||
_notifications.AddLast(newFriendRequestNotification);
|
|
||||||
_hasNewFriendRequest = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_notificationEvent.ReadableEvent.Signal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
if (isDisposing)
|
|
||||||
{
|
|
||||||
NotificationEventHandler.Instance.UnregisterNotificationService(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,74 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
|
|
||||||
{
|
|
||||||
public sealed class NotificationEventHandler
|
|
||||||
{
|
|
||||||
private static NotificationEventHandler _instance;
|
|
||||||
private static readonly object _instanceLock = new();
|
|
||||||
|
|
||||||
private readonly INotificationService[] _registry;
|
|
||||||
|
|
||||||
public static NotificationEventHandler Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_instanceLock)
|
|
||||||
{
|
|
||||||
_instance ??= new NotificationEventHandler();
|
|
||||||
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationEventHandler()
|
|
||||||
{
|
|
||||||
_registry = new INotificationService[0x20];
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RegisterNotificationService(INotificationService service)
|
|
||||||
{
|
|
||||||
// NOTE: in case there isn't space anymore in the registry array, Nintendo doesn't return any errors.
|
|
||||||
for (int i = 0; i < _registry.Length; i++)
|
|
||||||
{
|
|
||||||
if (_registry[i] == null)
|
|
||||||
{
|
|
||||||
_registry[i] = service;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UnregisterNotificationService(INotificationService service)
|
|
||||||
{
|
|
||||||
// NOTE: in case there isn't the entry in the registry array, Nintendo doesn't return any errors.
|
|
||||||
for (int i = 0; i < _registry.Length; i++)
|
|
||||||
{
|
|
||||||
if (_registry[i] == service)
|
|
||||||
{
|
|
||||||
_registry[i] = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Use this when we will have enough things to go online.
|
|
||||||
public void SignalFriendListUpdate(UserId targetId)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _registry.Length; i++)
|
|
||||||
{
|
|
||||||
_registry[i]?.SignalFriendListUpdate(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Use this when we will have enough things to go online.
|
|
||||||
public void SignalNewFriendRequest(UserId targetId)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _registry.Length; i++)
|
|
||||||
{
|
|
||||||
_registry[i]?.SignalNewFriendRequest(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
|
||||||
struct NotificationInfo
|
|
||||||
{
|
|
||||||
public NotificationEventType Type;
|
|
||||||
private Array4<byte> _padding;
|
|
||||||
public long NetworkUserIdPlaceholder;
|
|
||||||
}
|
|
||||||
}
|
|
49
src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs
Normal file
49
src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sm;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Friends
|
||||||
|
{
|
||||||
|
class FriendsIpcServer
|
||||||
|
{
|
||||||
|
private const int MaxSessionsCount = 8;
|
||||||
|
private const int TotalMaxSessionsCount = MaxSessionsCount * 5;
|
||||||
|
|
||||||
|
private const int PointerBufferSize = 0xA00;
|
||||||
|
private const int MaxDomains = 64;
|
||||||
|
private const int MaxDomainObjects = 16;
|
||||||
|
private const int MaxPortsCount = 5;
|
||||||
|
|
||||||
|
private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
|
||||||
|
|
||||||
|
private SmApi _sm;
|
||||||
|
private FriendsServerManager _serverManager;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
HeapAllocator allocator = new();
|
||||||
|
|
||||||
|
_sm = new SmApi();
|
||||||
|
_sm.Initialize().AbortOnFailure();
|
||||||
|
|
||||||
|
_serverManager = new FriendsServerManager(allocator, _sm, MaxPortsCount, _managerOptions, TotalMaxSessionsCount);
|
||||||
|
|
||||||
|
#pragma warning disable IDE0055 // Disable formatting
|
||||||
|
_serverManager.RegisterServer((int)FriendsPortIndex.Admin, ServiceName.Encode("friend:a"), MaxSessionsCount);
|
||||||
|
_serverManager.RegisterServer((int)FriendsPortIndex.User, ServiceName.Encode("friend:u"), MaxSessionsCount);
|
||||||
|
_serverManager.RegisterServer((int)FriendsPortIndex.Viewer, ServiceName.Encode("friend:v"), MaxSessionsCount);
|
||||||
|
_serverManager.RegisterServer((int)FriendsPortIndex.Manager, ServiceName.Encode("friend:m"), MaxSessionsCount);
|
||||||
|
_serverManager.RegisterServer((int)FriendsPortIndex.System, ServiceName.Encode("friend:s"), MaxSessionsCount);
|
||||||
|
#pragma warning restore IDE0055
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ServiceRequests()
|
||||||
|
{
|
||||||
|
_serverManager.ServiceRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
_serverManager.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/Ryujinx.Horizon/Friends/FriendsMain.cs
Normal file
17
src/Ryujinx.Horizon/Friends/FriendsMain.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Ryujinx.Horizon.Friends
|
||||||
|
{
|
||||||
|
class FriendsMain : IService
|
||||||
|
{
|
||||||
|
public static void Main(ServiceTable serviceTable)
|
||||||
|
{
|
||||||
|
FriendsIpcServer ipcServer = new();
|
||||||
|
|
||||||
|
ipcServer.Initialize();
|
||||||
|
|
||||||
|
serviceTable.SignalServiceReady();
|
||||||
|
|
||||||
|
ipcServer.ServiceRequests();
|
||||||
|
ipcServer.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/Ryujinx.Horizon/Friends/FriendsPortIndex.cs
Normal file
11
src/Ryujinx.Horizon/Friends/FriendsPortIndex.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Ryujinx.Horizon.Friends
|
||||||
|
{
|
||||||
|
enum FriendsPortIndex
|
||||||
|
{
|
||||||
|
Admin,
|
||||||
|
User,
|
||||||
|
Viewer,
|
||||||
|
Manager,
|
||||||
|
System,
|
||||||
|
}
|
||||||
|
}
|
36
src/Ryujinx.Horizon/Friends/FriendsServerManager.cs
Normal file
36
src/Ryujinx.Horizon/Friends/FriendsServerManager.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using Ryujinx.Horizon.Sdk.Friends.Detail.Ipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sm;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Friends
|
||||||
|
{
|
||||||
|
class FriendsServerManager : ServerManager
|
||||||
|
{
|
||||||
|
private readonly IEmulatorAccountManager _accountManager;
|
||||||
|
private readonly NotificationEventHandler _notificationEventHandler;
|
||||||
|
|
||||||
|
public FriendsServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions)
|
||||||
|
{
|
||||||
|
_accountManager = HorizonStatic.Options.AccountManager;
|
||||||
|
_notificationEventHandler = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result OnNeedsToAccept(int portIndex, Server server)
|
||||||
|
{
|
||||||
|
return (FriendsPortIndex)portIndex switch
|
||||||
|
{
|
||||||
|
#pragma warning disable IDE0055 // Disable formatting
|
||||||
|
FriendsPortIndex.Admin => AcceptImpl(server, new ServiceCreator(_accountManager, _notificationEventHandler, FriendsServicePermissionLevel.Admin)),
|
||||||
|
FriendsPortIndex.User => AcceptImpl(server, new ServiceCreator(_accountManager, _notificationEventHandler, FriendsServicePermissionLevel.User)),
|
||||||
|
FriendsPortIndex.Viewer => AcceptImpl(server, new ServiceCreator(_accountManager, _notificationEventHandler, FriendsServicePermissionLevel.Viewer)),
|
||||||
|
FriendsPortIndex.Manager => AcceptImpl(server, new ServiceCreator(_accountManager, _notificationEventHandler, FriendsServicePermissionLevel.Manager)),
|
||||||
|
FriendsPortIndex.System => AcceptImpl(server, new ServiceCreator(_accountManager, _notificationEventHandler, FriendsServicePermissionLevel.System)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(portIndex)),
|
||||||
|
#pragma warning restore IDE0055
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using LibHac;
|
using LibHac;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
using Ryujinx.Horizon.Sdk.Fs;
|
using Ryujinx.Horizon.Sdk.Fs;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon
|
namespace Ryujinx.Horizon
|
||||||
@@ -10,13 +11,15 @@ namespace Ryujinx.Horizon
|
|||||||
|
|
||||||
public HorizonClient BcatClient { get; }
|
public HorizonClient BcatClient { get; }
|
||||||
public IFsClient FsClient { get; }
|
public IFsClient FsClient { get; }
|
||||||
|
public IEmulatorAccountManager AccountManager { get; }
|
||||||
|
|
||||||
public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient, IFsClient fsClient)
|
public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient, IFsClient fsClient, IEmulatorAccountManager accountManager)
|
||||||
{
|
{
|
||||||
IgnoreMissingServices = ignoreMissingServices;
|
IgnoreMissingServices = ignoreMissingServices;
|
||||||
ThrowOnInvalidCommandIds = true;
|
ThrowOnInvalidCommandIds = true;
|
||||||
BcatClient = bcatClient;
|
BcatClient = bcatClient;
|
||||||
FsClient = fsClient;
|
FsClient = fsClient;
|
||||||
|
AccountManager = accountManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Account
|
||||||
|
{
|
||||||
|
public interface IEmulatorAccountManager
|
||||||
|
{
|
||||||
|
void OpenUserOnlinePlay(Uid userId);
|
||||||
|
void CloseUserOnlinePlay(Uid userId);
|
||||||
|
}
|
||||||
|
}
|
20
src/Ryujinx.Horizon/Sdk/Account/NetworkServiceAccountId.cs
Normal file
20
src/Ryujinx.Horizon/Sdk/Account/NetworkServiceAccountId.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Account
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x8)]
|
||||||
|
readonly record struct NetworkServiceAccountId
|
||||||
|
{
|
||||||
|
public readonly ulong Id;
|
||||||
|
|
||||||
|
public NetworkServiceAccountId(ulong id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly string ToString()
|
||||||
|
{
|
||||||
|
return Id.ToString("x16");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/Ryujinx.Horizon/Sdk/Account/Nickname.cs
Normal file
29
src/Ryujinx.Horizon/Sdk/Account/Nickname.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Account
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x21, Pack = 0x1)]
|
||||||
|
readonly struct Nickname
|
||||||
|
{
|
||||||
|
public readonly Array33<byte> Name;
|
||||||
|
|
||||||
|
public Nickname(in Array33<byte> name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
int length = ((ReadOnlySpan<byte>)Name.AsSpan()).IndexOf((byte)0);
|
||||||
|
if (length < 0)
|
||||||
|
{
|
||||||
|
length = 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(Name.AsSpan()[..length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,16 +6,16 @@ using System.Runtime.InteropServices;
|
|||||||
namespace Ryujinx.Horizon.Sdk.Account
|
namespace Ryujinx.Horizon.Sdk.Account
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
readonly record struct Uid
|
public readonly record struct Uid
|
||||||
{
|
{
|
||||||
public readonly long High;
|
public readonly ulong High;
|
||||||
public readonly long Low;
|
public readonly ulong Low;
|
||||||
|
|
||||||
public bool IsNull => (Low | High) == 0;
|
public bool IsNull => (Low | High) == 0;
|
||||||
|
|
||||||
public static Uid Null => new(0, 0);
|
public static Uid Null => new(0, 0);
|
||||||
|
|
||||||
public Uid(long low, long high)
|
public Uid(ulong low, ulong high)
|
||||||
{
|
{
|
||||||
Low = low;
|
Low = low;
|
||||||
High = high;
|
High = high;
|
||||||
@@ -23,8 +23,8 @@ namespace Ryujinx.Horizon.Sdk.Account
|
|||||||
|
|
||||||
public Uid(byte[] bytes)
|
public Uid(byte[] bytes)
|
||||||
{
|
{
|
||||||
High = BitConverter.ToInt64(bytes, 0);
|
High = BitConverter.ToUInt64(bytes, 0);
|
||||||
Low = BitConverter.ToInt64(bytes, 8);
|
Low = BitConverter.ToUInt64(bytes, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uid(string hex)
|
public Uid(string hex)
|
||||||
@@ -34,8 +34,8 @@ namespace Ryujinx.Horizon.Sdk.Account
|
|||||||
throw new ArgumentException("Invalid Hex value!", nameof(hex));
|
throw new ArgumentException("Invalid Hex value!", nameof(hex));
|
||||||
}
|
}
|
||||||
|
|
||||||
Low = Convert.ToInt64(hex[16..], 16);
|
Low = Convert.ToUInt64(hex[16..], 16);
|
||||||
High = Convert.ToInt64(hex[..16], 16);
|
High = Convert.ToUInt64(hex[..16], 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(BinaryWriter binaryWriter)
|
public void Write(BinaryWriter binaryWriter)
|
||||||
|
12
src/Ryujinx.Horizon/Sdk/Friends/ApplicationInfo.cs
Normal file
12
src/Ryujinx.Horizon/Sdk/Friends/ApplicationInfo.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Ryujinx.Horizon.Sdk.Ncm;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x8)]
|
||||||
|
struct ApplicationInfo
|
||||||
|
{
|
||||||
|
public ApplicationId ApplicationId;
|
||||||
|
public ulong PresenceGroupId;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct BlockedUserImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct FriendCandidateImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x800)]
|
||||||
|
struct FriendDetailedInfoImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
19
src/Ryujinx.Horizon/Sdk/Friends/Detail/FriendImpl.cs
Normal file
19
src/Ryujinx.Horizon/Sdk/Friends/Detail/FriendImpl.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x200, Pack = 0x8)]
|
||||||
|
struct FriendImpl
|
||||||
|
{
|
||||||
|
public Uid UserId;
|
||||||
|
public NetworkServiceAccountId NetworkUserId;
|
||||||
|
public Nickname Nickname;
|
||||||
|
public UserPresenceImpl Presence;
|
||||||
|
public bool IsFavourite;
|
||||||
|
public bool IsNew;
|
||||||
|
public Array6<byte> Unknown;
|
||||||
|
public bool IsValid;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct FriendInvitationForViewerImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x1400)]
|
||||||
|
struct FriendInvitationGroupImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct FriendRequestImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
|
||||||
|
struct FriendSettingImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
partial class DaemonSuspendSessionService : IDaemonSuspendSessionService
|
||||||
|
{
|
||||||
|
// NOTE: This service has no commands.
|
||||||
|
}
|
||||||
|
}
|
1015
src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/FriendService.cs
Normal file
1015
src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/FriendService.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,13 @@
|
|||||||
using System;
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|
||||||
{
|
{
|
||||||
[Flags]
|
enum FriendsServicePermissionLevel
|
||||||
enum FriendServicePermissionLevel
|
|
||||||
{
|
{
|
||||||
UserMask = 1,
|
UserMask = 1,
|
||||||
ViewerMask = 2,
|
ViewerMask = 2,
|
||||||
ManagerMask = 4,
|
ManagerMask = 4,
|
||||||
SystemMask = 8,
|
SystemMask = 8,
|
||||||
|
|
||||||
Administrator = -1,
|
Admin = -1,
|
||||||
User = UserMask,
|
User = UserMask,
|
||||||
Viewer = UserMask | ViewerMask,
|
Viewer = UserMask | ViewerMask,
|
||||||
Manager = UserMask | ViewerMask | ManagerMask,
|
Manager = UserMask | ViewerMask | ManagerMask,
|
@@ -0,0 +1,9 @@
|
|||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
interface IDaemonSuspendSessionService : IServiceObject
|
||||||
|
{
|
||||||
|
// NOTE: This service has no commands.
|
||||||
|
}
|
||||||
|
}
|
97
src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/IFriendService.cs
Normal file
97
src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/IFriendService.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using Ryujinx.Horizon.Sdk.Settings;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
interface IFriendService : IServiceObject
|
||||||
|
{
|
||||||
|
Result GetCompletionEvent(out int completionEventHandle);
|
||||||
|
Result Cancel();
|
||||||
|
Result GetFriendListIds(out int count, Span<NetworkServiceAccountId> friendIds, Uid userId, int offset, SizedFriendFilter filter, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result GetFriendList(out int count, Span<FriendImpl> friendList, Uid userId, int offset, SizedFriendFilter filter, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result UpdateFriendInfo(Span<FriendImpl> info, Uid userId, ReadOnlySpan<NetworkServiceAccountId> friendIds, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result GetFriendProfileImage(out int size, Uid userId, NetworkServiceAccountId friendId, Span<byte> profileImage);
|
||||||
|
Result CheckFriendListAvailability(out bool listAvailable, Uid userId);
|
||||||
|
Result EnsureFriendListAvailable(Uid userId);
|
||||||
|
Result SendFriendRequestForApplication(Uid userId, NetworkServiceAccountId friendId, in InAppScreenName arg2, in InAppScreenName arg3, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result AddFacedFriendRequestForApplication(Uid userId, FacedFriendRequestRegistrationKey key, Nickname nickname, ReadOnlySpan<byte> arg3, in InAppScreenName arg4, in InAppScreenName arg5, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result GetBlockedUserListIds(out int count, Span<NetworkServiceAccountId> blockedIds, Uid userId, int offset);
|
||||||
|
Result CheckBlockedUserListAvailability(out bool listAvailable, Uid userId);
|
||||||
|
Result EnsureBlockedUserListAvailable(Uid userId);
|
||||||
|
Result GetProfileList(Span<ProfileImpl> profileList, Uid userId, ReadOnlySpan<NetworkServiceAccountId> friendIds);
|
||||||
|
Result DeclareOpenOnlinePlaySession(Uid userId);
|
||||||
|
Result DeclareCloseOnlinePlaySession(Uid userId);
|
||||||
|
Result UpdateUserPresence(Uid userId, in UserPresenceImpl userPresence, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result GetPlayHistoryRegistrationKey(out PlayHistoryRegistrationKey registrationKey, Uid userId, bool arg2);
|
||||||
|
Result GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId(out PlayHistoryRegistrationKey registrationKey, NetworkServiceAccountId friendId, bool arg2);
|
||||||
|
Result AddPlayHistory(Uid userId, in PlayHistoryRegistrationKey registrationKey, in InAppScreenName arg2, in InAppScreenName arg3, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result GetProfileImageUrl(out Url imageUrl, Url url, int arg2);
|
||||||
|
Result GetFriendCount(out int count, Uid userId, SizedFriendFilter filter, ulong pidPlaceholder, ulong pid);
|
||||||
|
Result GetNewlyFriendCount(out int count, Uid userId);
|
||||||
|
Result GetFriendDetailedInfo(out FriendDetailedInfoImpl detailedInfo, Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result SyncFriendList(Uid userId);
|
||||||
|
Result RequestSyncFriendList(Uid userId);
|
||||||
|
Result LoadFriendSetting(out FriendSettingImpl friendSetting, Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result GetReceivedFriendRequestCount(out int count, out int count2, Uid userId);
|
||||||
|
Result GetFriendRequestList(out int count, Span<FriendRequestImpl> requestList, Uid userId, int arg3, int arg4);
|
||||||
|
Result GetFriendCandidateList(out int count, Span<FriendCandidateImpl> candidateList, Uid userId, int arg3);
|
||||||
|
Result GetNintendoNetworkIdInfo(out NintendoNetworkIdUserInfo networkIdInfo, out int arg1, Span<NintendoNetworkIdFriendImpl> friendInfo, Uid userId, int arg4);
|
||||||
|
Result GetSnsAccountLinkage(out SnsAccountLinkage accountLinkage, Uid userId);
|
||||||
|
Result GetSnsAccountProfile(out SnsAccountProfile accountProfile, Uid userId, NetworkServiceAccountId friendId, int arg3);
|
||||||
|
Result GetSnsAccountFriendList(out int count, Span<SnsAccountFriendImpl> friendList, Uid userId, int arg3);
|
||||||
|
Result GetBlockedUserList(out int count, Span<BlockedUserImpl> blockedUsers, Uid userId, int arg3);
|
||||||
|
Result SyncBlockedUserList(Uid userId);
|
||||||
|
Result GetProfileExtraList(Span<ProfileExtraImpl> extraList, Uid userId, ReadOnlySpan<NetworkServiceAccountId> friendIds);
|
||||||
|
Result GetRelationship(out Relationship relationship, Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result GetUserPresenceView(out UserPresenceViewImpl userPresenceView, Uid userId);
|
||||||
|
Result GetPlayHistoryList(out int count, Span<PlayHistoryImpl> playHistoryList, Uid userId, int arg3);
|
||||||
|
Result GetPlayHistoryStatistics(out PlayHistoryStatistics statistics, Uid userId);
|
||||||
|
Result LoadUserSetting(out UserSettingImpl userSetting, Uid userId);
|
||||||
|
Result SyncUserSetting(Uid userId);
|
||||||
|
Result RequestListSummaryOverlayNotification();
|
||||||
|
Result GetExternalApplicationCatalog(out ExternalApplicationCatalog catalog, ExternalApplicationCatalogId catalogId, LanguageCode language);
|
||||||
|
Result GetReceivedFriendInvitationList(out int count, Span<FriendInvitationForViewerImpl> invitationList, Uid userId);
|
||||||
|
Result GetReceivedFriendInvitationDetailedInfo(out FriendInvitationGroupImpl invicationGroup, Uid userId, FriendInvitationGroupId groupId);
|
||||||
|
Result GetReceivedFriendInvitationCountCache(out int count, Uid userId);
|
||||||
|
Result DropFriendNewlyFlags(Uid userId);
|
||||||
|
Result DeleteFriend(Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result DropFriendNewlyFlag(Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result ChangeFriendFavoriteFlag(Uid userId, NetworkServiceAccountId friendId, bool favoriteFlag);
|
||||||
|
Result ChangeFriendOnlineNotificationFlag(Uid userId, NetworkServiceAccountId friendId, bool onlineNotificationFlag);
|
||||||
|
Result SendFriendRequest(Uid userId, NetworkServiceAccountId friendId, int arg2);
|
||||||
|
Result SendFriendRequestWithApplicationInfo(Uid userId, NetworkServiceAccountId friendId, int arg2, ApplicationInfo applicationInfo, in InAppScreenName arg4, in InAppScreenName arg5);
|
||||||
|
Result CancelFriendRequest(Uid userId, RequestId requestId);
|
||||||
|
Result AcceptFriendRequest(Uid userId, RequestId requestId);
|
||||||
|
Result RejectFriendRequest(Uid userId, RequestId requestId);
|
||||||
|
Result ReadFriendRequest(Uid userId, RequestId requestId);
|
||||||
|
Result GetFacedFriendRequestRegistrationKey(out FacedFriendRequestRegistrationKey registrationKey, Uid userId);
|
||||||
|
Result AddFacedFriendRequest(Uid userId, FacedFriendRequestRegistrationKey registrationKey, Nickname nickname, ReadOnlySpan<byte> arg3);
|
||||||
|
Result CancelFacedFriendRequest(Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result GetFacedFriendRequestProfileImage(out int size, Uid userId, NetworkServiceAccountId friendId, Span<byte> profileImage);
|
||||||
|
Result GetFacedFriendRequestProfileImageFromPath(out int size, ReadOnlySpan<byte> path, Span<byte> profileImage);
|
||||||
|
Result SendFriendRequestWithExternalApplicationCatalogId(Uid userId, NetworkServiceAccountId friendId, int arg2, ExternalApplicationCatalogId catalogId, in InAppScreenName arg4, in InAppScreenName arg5);
|
||||||
|
Result ResendFacedFriendRequest(Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result SendFriendRequestWithNintendoNetworkIdInfo(Uid userId, NetworkServiceAccountId friendId, int arg2, MiiName arg3, MiiImageUrlParam arg4, MiiName arg5, MiiImageUrlParam arg6);
|
||||||
|
Result GetSnsAccountLinkPageUrl(out WebPageUrl url, Uid userId, int arg2);
|
||||||
|
Result UnlinkSnsAccount(Uid userId, int arg1);
|
||||||
|
Result BlockUser(Uid userId, NetworkServiceAccountId friendId, int arg2);
|
||||||
|
Result BlockUserWithApplicationInfo(Uid userId, NetworkServiceAccountId friendId, int arg2, ApplicationInfo applicationInfo, in InAppScreenName arg4);
|
||||||
|
Result UnblockUser(Uid userId, NetworkServiceAccountId friendId);
|
||||||
|
Result GetProfileExtraFromFriendCode(out ProfileExtraImpl profileExtra, Uid userId, FriendCode friendCode);
|
||||||
|
Result DeletePlayHistory(Uid userId);
|
||||||
|
Result ChangePresencePermission(Uid userId, int permission);
|
||||||
|
Result ChangeFriendRequestReception(Uid userId, bool reception);
|
||||||
|
Result ChangePlayLogPermission(Uid userId, int permission);
|
||||||
|
Result IssueFriendCode(Uid userId);
|
||||||
|
Result ClearPlayLog(Uid userId);
|
||||||
|
Result SendFriendInvitation(Uid userId, ReadOnlySpan<NetworkServiceAccountId> friendIds, in FriendInvitationGameModeDescription description, ApplicationInfo applicationInfo, ReadOnlySpan<byte> arg4, bool arg5);
|
||||||
|
Result ReadFriendInvitation(Uid userId, ReadOnlySpan<FriendInvitationId> invitationIds);
|
||||||
|
Result ReadAllFriendInvitations(Uid userId);
|
||||||
|
Result DeleteFriendListCache(Uid userId);
|
||||||
|
Result DeleteBlockedUserListCache(Uid userId);
|
||||||
|
Result DeleteNetworkServiceAccountCache(Uid userId);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
interface INotificationService : IServiceObject
|
||||||
|
{
|
||||||
|
Result GetEvent(out int eventHandle);
|
||||||
|
Result Clear();
|
||||||
|
Result Pop(out SizedNotificationInfo sizedNotificationInfo);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
interface IServiceCreator : IServiceObject
|
||||||
|
{
|
||||||
|
Result CreateFriendService(out IFriendService friendService);
|
||||||
|
Result CreateNotificationService(out INotificationService notificationService, Uid userId);
|
||||||
|
Result CreateDaemonSuspendSessionService(out IDaemonSuspendSessionService daemonSuspendSessionService);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
sealed class NotificationEventHandler
|
||||||
|
{
|
||||||
|
private readonly NotificationService[] _registry;
|
||||||
|
|
||||||
|
public NotificationEventHandler()
|
||||||
|
{
|
||||||
|
_registry = new NotificationService[0x20];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterNotificationService(NotificationService service)
|
||||||
|
{
|
||||||
|
// NOTE: When there's no enough space in the registry array, Nintendo doesn't return any errors.
|
||||||
|
for (int i = 0; i < _registry.Length; i++)
|
||||||
|
{
|
||||||
|
if (_registry[i] == null)
|
||||||
|
{
|
||||||
|
_registry[i] = service;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterNotificationService(NotificationService service)
|
||||||
|
{
|
||||||
|
// NOTE: When there's no enough space in the registry array, Nintendo doesn't return any errors.
|
||||||
|
for (int i = 0; i < _registry.Length; i++)
|
||||||
|
{
|
||||||
|
if (_registry[i] == service)
|
||||||
|
{
|
||||||
|
_registry[i] = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use this when we have enough things to go online.
|
||||||
|
public void SignalFriendListUpdate(Uid targetId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _registry.Length; i++)
|
||||||
|
{
|
||||||
|
_registry[i]?.SignalFriendListUpdate(targetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use this when we have enough things to go online.
|
||||||
|
public void SignalNewFriendRequest(Uid targetId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _registry.Length; i++)
|
||||||
|
{
|
||||||
|
_registry[i]?.SignalNewFriendRequest(targetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
{
|
{
|
||||||
enum NotificationEventType : uint
|
enum NotificationEventType : uint
|
||||||
{
|
{
|
@@ -0,0 +1,172 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using Ryujinx.Horizon.Sdk.OsTypes;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
partial class NotificationService : INotificationService, IDisposable
|
||||||
|
{
|
||||||
|
private readonly NotificationEventHandler _notificationEventHandler;
|
||||||
|
private readonly Uid _userId;
|
||||||
|
private readonly FriendsServicePermissionLevel _permissionLevel;
|
||||||
|
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
private SystemEventType _notificationEvent;
|
||||||
|
|
||||||
|
private readonly LinkedList<SizedNotificationInfo> _notifications;
|
||||||
|
|
||||||
|
private bool _hasNewFriendRequest;
|
||||||
|
private bool _hasFriendListUpdate;
|
||||||
|
|
||||||
|
public NotificationService(NotificationEventHandler notificationEventHandler, Uid userId, FriendsServicePermissionLevel permissionLevel)
|
||||||
|
{
|
||||||
|
_notificationEventHandler = notificationEventHandler;
|
||||||
|
_userId = userId;
|
||||||
|
_permissionLevel = permissionLevel;
|
||||||
|
_notifications = new LinkedList<SizedNotificationInfo>();
|
||||||
|
Os.CreateSystemEvent(out _notificationEvent, EventClearMode.AutoClear, interProcess: true).AbortOnFailure();
|
||||||
|
|
||||||
|
_hasNewFriendRequest = false;
|
||||||
|
_hasFriendListUpdate = false;
|
||||||
|
|
||||||
|
notificationEventHandler.RegisterNotificationService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(0)]
|
||||||
|
public Result GetEvent([CopyHandle] out int eventHandle)
|
||||||
|
{
|
||||||
|
eventHandle = Os.GetReadableHandleOfSystemEvent(ref _notificationEvent);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(1)]
|
||||||
|
public Result Clear()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_hasNewFriendRequest = false;
|
||||||
|
_hasFriendListUpdate = false;
|
||||||
|
|
||||||
|
_notifications.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(2)]
|
||||||
|
public Result Pop(out SizedNotificationInfo sizedNotificationInfo)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_notifications.Count >= 1)
|
||||||
|
{
|
||||||
|
sizedNotificationInfo = _notifications.First.Value;
|
||||||
|
_notifications.RemoveFirst();
|
||||||
|
|
||||||
|
if (sizedNotificationInfo.Type == NotificationEventType.FriendListUpdate)
|
||||||
|
{
|
||||||
|
_hasFriendListUpdate = false;
|
||||||
|
}
|
||||||
|
else if (sizedNotificationInfo.Type == NotificationEventType.NewFriendRequest)
|
||||||
|
{
|
||||||
|
_hasNewFriendRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sizedNotificationInfo = default;
|
||||||
|
|
||||||
|
return FriendResult.NotificationQueueEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SignalFriendListUpdate(Uid targetId)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_userId == targetId)
|
||||||
|
{
|
||||||
|
if (!_hasFriendListUpdate)
|
||||||
|
{
|
||||||
|
SizedNotificationInfo friendListNotification = new();
|
||||||
|
|
||||||
|
if (_notifications.Count != 0)
|
||||||
|
{
|
||||||
|
friendListNotification = _notifications.First.Value;
|
||||||
|
_notifications.RemoveFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
friendListNotification.Type = NotificationEventType.FriendListUpdate;
|
||||||
|
_hasFriendListUpdate = true;
|
||||||
|
|
||||||
|
if (_hasNewFriendRequest)
|
||||||
|
{
|
||||||
|
SizedNotificationInfo newFriendRequestNotification = new();
|
||||||
|
|
||||||
|
if (_notifications.Count != 0)
|
||||||
|
{
|
||||||
|
newFriendRequestNotification = _notifications.First.Value;
|
||||||
|
_notifications.RemoveFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
newFriendRequestNotification.Type = NotificationEventType.NewFriendRequest;
|
||||||
|
_notifications.AddFirst(newFriendRequestNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We defer this to make sure we are on top of the queue.
|
||||||
|
_notifications.AddFirst(friendListNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
Os.SignalSystemEvent(ref _notificationEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SignalNewFriendRequest(Uid targetId)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_permissionLevel.HasFlag(FriendsServicePermissionLevel.ViewerMask) && _userId == targetId)
|
||||||
|
{
|
||||||
|
if (!_hasNewFriendRequest)
|
||||||
|
{
|
||||||
|
if (_notifications.Count == 100)
|
||||||
|
{
|
||||||
|
SignalFriendListUpdate(targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SizedNotificationInfo newFriendRequestNotification = new()
|
||||||
|
{
|
||||||
|
Type = NotificationEventType.NewFriendRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
_notifications.AddLast(newFriendRequestNotification);
|
||||||
|
_hasNewFriendRequest = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Os.SignalSystemEvent(ref _notificationEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_notificationEventHandler.UnregisterNotificationService(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
{
|
{
|
||||||
enum PresenceStatusFilter : uint
|
enum PresenceStatusFilter : uint
|
||||||
{
|
{
|
51
src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/ServiceCreator.cs
Normal file
51
src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/ServiceCreator.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
partial class ServiceCreator : IServiceCreator
|
||||||
|
{
|
||||||
|
private readonly IEmulatorAccountManager _accountManager;
|
||||||
|
private readonly NotificationEventHandler _notificationEventHandler;
|
||||||
|
private readonly FriendsServicePermissionLevel _permissionLevel;
|
||||||
|
|
||||||
|
public ServiceCreator(IEmulatorAccountManager accountManager, NotificationEventHandler notificationEventHandler, FriendsServicePermissionLevel permissionLevel)
|
||||||
|
{
|
||||||
|
_accountManager = accountManager;
|
||||||
|
_notificationEventHandler = notificationEventHandler;
|
||||||
|
_permissionLevel = permissionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(0)]
|
||||||
|
public Result CreateFriendService(out IFriendService friendService)
|
||||||
|
{
|
||||||
|
friendService = new FriendService(_accountManager, _permissionLevel);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(1)] // 2.0.0+
|
||||||
|
public Result CreateNotificationService(out INotificationService notificationService, Uid userId)
|
||||||
|
{
|
||||||
|
if (userId.IsNull)
|
||||||
|
{
|
||||||
|
notificationService = null;
|
||||||
|
|
||||||
|
return FriendResult.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationService = new NotificationService(_notificationEventHandler, userId, _permissionLevel);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(2)] // 4.0.0+
|
||||||
|
public Result CreateDaemonSuspendSessionService(out IDaemonSuspendSessionService daemonSuspendSessionService)
|
||||||
|
{
|
||||||
|
daemonSuspendSessionService = new DaemonSuspendSessionService();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x8)]
|
||||||
|
struct SizedFriendFilter
|
||||||
|
{
|
||||||
|
public PresenceStatusFilter PresenceStatus;
|
||||||
|
public bool IsFavoriteOnly;
|
||||||
|
public bool IsSameAppPresenceOnly;
|
||||||
|
public bool IsSameAppPlayedOnly;
|
||||||
|
public bool IsArbitraryAppPlayedOnly;
|
||||||
|
public ulong PresenceGroupId;
|
||||||
|
|
||||||
|
public readonly override string ToString()
|
||||||
|
{
|
||||||
|
return $"{{ PresenceStatus: {PresenceStatus}, " +
|
||||||
|
$"IsFavoriteOnly: {IsFavoriteOnly}, " +
|
||||||
|
$"IsSameAppPresenceOnly: {IsSameAppPresenceOnly}, " +
|
||||||
|
$"IsSameAppPlayedOnly: {IsSameAppPlayedOnly}, " +
|
||||||
|
$"IsArbitraryAppPlayedOnly: {IsArbitraryAppPlayedOnly}, " +
|
||||||
|
$"PresenceGroupId: {PresenceGroupId} }}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x8)]
|
||||||
|
struct SizedNotificationInfo
|
||||||
|
{
|
||||||
|
public NotificationEventType Type;
|
||||||
|
public uint Padding;
|
||||||
|
public NetworkServiceAccountId NetworkUserIdPlaceholder;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct NintendoNetworkIdFriendImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct PlayHistoryImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
{
|
{
|
||||||
enum PresenceStatus : uint
|
enum PresenceStatus : uint
|
||||||
{
|
{
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x400)]
|
||||||
|
struct ProfileExtraImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
8
src/Ryujinx.Horizon/Sdk/Friends/Detail/ProfileImpl.cs
Normal file
8
src/Ryujinx.Horizon/Sdk/Friends/Detail/ProfileImpl.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct ProfileImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
struct SnsAccountFriendImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
29
src/Ryujinx.Horizon/Sdk/Friends/Detail/UserPresenceImpl.cs
Normal file
29
src/Ryujinx.Horizon/Sdk/Friends/Detail/UserPresenceImpl.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0xE0)]
|
||||||
|
struct UserPresenceImpl
|
||||||
|
{
|
||||||
|
public Uid UserId;
|
||||||
|
public long LastTimeOnlineTimestamp;
|
||||||
|
public PresenceStatus Status;
|
||||||
|
public bool SamePresenceGroupApplication;
|
||||||
|
public Array3<byte> Unknown;
|
||||||
|
public AppKeyValueStorageHolder AppKeyValueStorage;
|
||||||
|
|
||||||
|
[InlineArray(0xC0)]
|
||||||
|
public struct AppKeyValueStorageHolder
|
||||||
|
{
|
||||||
|
public byte Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly override string ToString()
|
||||||
|
{
|
||||||
|
return $"{{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status} }}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0xE0)]
|
||||||
|
struct UserPresenceViewImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends.Detail
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x800)]
|
||||||
|
struct UserSettingImpl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x4B8)]
|
||||||
|
struct ExternalApplicationCatalog
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x8)]
|
||||||
|
struct ExternalApplicationCatalogId
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x40, Pack = 0x1)]
|
||||||
|
struct FacedFriendRequestRegistrationKey
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/FriendCode.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/FriendCode.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 0x1)]
|
||||||
|
struct FriendCode
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0xC00)]
|
||||||
|
struct FriendInvitationGameModeDescription
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x8)]
|
||||||
|
struct FriendInvitationGroupId
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
8
src/Ryujinx.Horizon/Sdk/Friends/FriendInvitationId.cs
Normal file
8
src/Ryujinx.Horizon/Sdk/Friends/FriendInvitationId.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
struct FriendInvitationId
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
13
src/Ryujinx.Horizon/Sdk/Friends/FriendResult.cs
Normal file
13
src/Ryujinx.Horizon/Sdk/Friends/FriendResult.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
static class FriendResult
|
||||||
|
{
|
||||||
|
private const int ModuleId = 121;
|
||||||
|
|
||||||
|
public static Result InvalidArgument => new(ModuleId, 2);
|
||||||
|
public static Result InternetRequestDenied => new(ModuleId, 6);
|
||||||
|
public static Result NotificationQueueEmpty => new(ModuleId, 15);
|
||||||
|
}
|
||||||
|
}
|
26
src/Ryujinx.Horizon/Sdk/Friends/InAppScreenName.cs
Normal file
26
src/Ryujinx.Horizon/Sdk/Friends/InAppScreenName.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Horizon.Sdk.Settings;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x48)]
|
||||||
|
struct InAppScreenName
|
||||||
|
{
|
||||||
|
public Array64<byte> Name;
|
||||||
|
public LanguageCode LanguageCode;
|
||||||
|
|
||||||
|
public override readonly string ToString()
|
||||||
|
{
|
||||||
|
int length = Name.AsSpan().IndexOf((byte)0);
|
||||||
|
if (length < 0)
|
||||||
|
{
|
||||||
|
length = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(Name.AsSpan()[..length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/MiiImageUrlParam.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/MiiImageUrlParam.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x1)]
|
||||||
|
struct MiiImageUrlParam
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/MiiName.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/MiiName.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 0x1)]
|
||||||
|
struct MiiName
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x38)]
|
||||||
|
struct NintendoNetworkIdUserInfo
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +1,10 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Horizon.Sdk.Account;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
|
||||||
struct PlayHistoryRegistrationKey
|
struct PlayHistoryRegistrationKey
|
||||||
{
|
{
|
||||||
public ushort Type;
|
public ushort Type;
|
||||||
@@ -11,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||||||
public byte UserIdBool;
|
public byte UserIdBool;
|
||||||
public byte UnknownBool;
|
public byte UnknownBool;
|
||||||
public Array11<byte> Reserved;
|
public Array11<byte> Reserved;
|
||||||
public Array16<byte> Uuid;
|
public Uid Uuid;
|
||||||
|
public Array32<byte> HmacHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/PlayHistoryStatistics.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/PlayHistoryStatistics.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x8)]
|
||||||
|
struct PlayHistoryStatistics
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/Relationship.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/Relationship.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x1)]
|
||||||
|
struct Relationship
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/RequestId.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/RequestId.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x8)]
|
||||||
|
struct RequestId
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/SnsAccountLinkage.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/SnsAccountLinkage.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x1)]
|
||||||
|
struct SnsAccountLinkage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/SnsAccountProfile.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/SnsAccountProfile.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x380)]
|
||||||
|
struct SnsAccountProfile
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
30
src/Ryujinx.Horizon/Sdk/Friends/Url.cs
Normal file
30
src/Ryujinx.Horizon/Sdk/Friends/Url.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0xA0, Pack = 0x1)]
|
||||||
|
struct Url
|
||||||
|
{
|
||||||
|
public UrlStorage Path;
|
||||||
|
|
||||||
|
[InlineArray(0xA0)]
|
||||||
|
public struct UrlStorage
|
||||||
|
{
|
||||||
|
public byte Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly string ToString()
|
||||||
|
{
|
||||||
|
int length = ((ReadOnlySpan<byte>)Path).IndexOf((byte)0);
|
||||||
|
if (length < 0)
|
||||||
|
{
|
||||||
|
length = 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(((ReadOnlySpan<byte>)Path)[..length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Friends/WebPageUrl.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Friends/WebPageUrl.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Friends
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x1000)]
|
||||||
|
struct WebPageUrl
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Settings/BatteryLot.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Settings/BatteryLot.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x18, Pack = 0x1)]
|
||||||
|
struct BatteryLot
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x6, Pack = 0x2)]
|
||||||
|
struct AccelerometerOffset
|
||||||
|
{
|
||||||
|
public ushort X;
|
||||||
|
public ushort Y;
|
||||||
|
public ushort Z;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x6, Pack = 0x2)]
|
||||||
|
struct AccelerometerScale
|
||||||
|
{
|
||||||
|
public ushort X;
|
||||||
|
public ushort Y;
|
||||||
|
public ushort Z;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x74, Pack = 0x4)]
|
||||||
|
struct AmiiboEcdsaCertificate
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x24, Pack = 0x4)]
|
||||||
|
struct AmiiboEcqvBlsCertificate
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x48, Pack = 0x4)]
|
||||||
|
struct AmiiboEcqvBlsKey
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x94, Pack = 0x4)]
|
||||||
|
struct AmiiboEcqvBlsRootCertificate
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x18, Pack = 0x4)]
|
||||||
|
struct AmiiboEcqvCertificate
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Settings/Factory/AmiiboKey.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Settings/Factory/AmiiboKey.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x58, Pack = 0x4)]
|
||||||
|
struct AmiiboKey
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x9, Pack = 0x1)]
|
||||||
|
struct AnalogStickFactoryCalibration
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x12, Pack = 0x1)]
|
||||||
|
struct AnalogStickModelParameter
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
9
src/Ryujinx.Horizon/Sdk/Settings/Factory/BdAddress.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Settings/Factory/BdAddress.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Settings.Factory
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x6, Pack = 0x1)]
|
||||||
|
struct BdAddress
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user