mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-12 00:05:48 +00:00
Compare commits
28 Commits
a3e10a1e5a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8167eb625 | ||
|
|
bab160d650 | ||
|
|
e8cc252d9a | ||
|
|
8065dec744 | ||
|
|
e9c31bea3b | ||
|
|
5511ff5686 | ||
|
|
bf7f978f9d | ||
|
|
1f9bfab923 | ||
|
|
5d8cb3e378 | ||
|
|
708186d8d2 | ||
|
|
ad34237fc6 | ||
|
|
bf083a716c | ||
|
|
2d2661298c | ||
|
|
c4788154fd | ||
|
|
49dd56953c | ||
|
|
722eb93554 | ||
|
|
b0179e6433 | ||
|
|
1d3d4197b7 | ||
|
|
d2b2d65061 | ||
|
|
e1dcaef709 | ||
|
|
b222f671f3 | ||
|
|
4d0cd61b6a | ||
|
|
4e86159bce | ||
|
|
0d66cfa281 | ||
|
|
e656de5fff | ||
|
|
518dd65484 | ||
|
|
88421959a6 | ||
|
|
87ce5162be |
@@ -143,9 +143,7 @@ jobs:
|
|||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: docker
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: ghcr.io/catthehacker/ubuntu:act-latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
@@ -207,7 +205,7 @@ jobs:
|
|||||||
|
|
||||||
post_ci:
|
post_ci:
|
||||||
name: Post-CI Steps
|
name: Post-CI Steps
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- macos_release
|
- macos_release
|
||||||
- release
|
- release
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
name: Comment PR artifacts links
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ['Build PR']
|
|
||||||
types: [completed]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pr_comment:
|
|
||||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@v9
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const {owner, repo} = context.repo;
|
|
||||||
const run_id = ${{github.event.workflow_run.id}};
|
|
||||||
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
|
||||||
|
|
||||||
const issue_number = await (async () => {
|
|
||||||
const pulls = await github.rest.pulls.list({owner, repo});
|
|
||||||
for await (const {data} of github.paginate.iterator(pulls)) {
|
|
||||||
for (const pull of data) {
|
|
||||||
if (pull.head.sha === pull_head_sha) {
|
|
||||||
return pull.number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
if (issue_number) {
|
|
||||||
core.info(`Using pull request ${issue_number}`);
|
|
||||||
} else {
|
|
||||||
return core.error(`No matching pull request found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
|
|
||||||
if (!artifacts.length) {
|
|
||||||
return core.error(`No artifacts found`);
|
|
||||||
}
|
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
|
||||||
for (const art of artifacts) {
|
|
||||||
const url = `https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip`;
|
|
||||||
if (art.name.includes('Debug')) {
|
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
|
|
||||||
} else {
|
|
||||||
body += `\n* [${art.name}](${url})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
|
||||||
body += hidden_debug_artifacts;
|
|
||||||
|
|
||||||
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
|
||||||
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
|
|
||||||
if (existing_comment) {
|
|
||||||
core.info(`Updating comment ${existing_comment.id}`);
|
|
||||||
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
|
|
||||||
} else {
|
|
||||||
core.info(`Creating a comment`);
|
|
||||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.14" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.14" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.14" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.14" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.14" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.14" />
|
||||||
<PackageVersion Include="SharpCompress" Version="0.47.4" />
|
<PackageVersion Include="SharpCompress" Version="0.48.0" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
||||||
@@ -57,7 +57,9 @@
|
|||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="2.88.9" />
|
||||||
|
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" />
|
||||||
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.15" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.15" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" />
|
||||||
|
|||||||
@@ -6100,6 +6100,31 @@
|
|||||||
"zh_TW": "檔案系統全域存取日誌模式:"
|
"zh_TW": "檔案系統全域存取日誌模式:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabLoggingEnableNetLogs",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Enable Net Logs",
|
||||||
|
"es_ES": "Habilitar registros de red.",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsTabLoggingDeveloperOptions",
|
"ID": "SettingsTabLoggingDeveloperOptions",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -17075,6 +17100,31 @@
|
|||||||
"zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3。"
|
"zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "NetLogTooltip",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Prints network log messages in the console.",
|
||||||
|
"es_ES": "Imprimir registros de red en la consola.",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "DeveloperOptionTooltip",
|
"ID": "DeveloperOptionTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -21425,6 +21475,31 @@
|
|||||||
"zh_TW": "需要重新啟動 Ryujinx"
|
"zh_TW": "需要重新啟動 Ryujinx"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsShowConsoleRestartMessage",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "The console will be available the next time Ryujinx starts.",
|
||||||
|
"es_ES": "La consola estará disponible la próxima vez que se inicie Ryujinx.",
|
||||||
|
"fr_FR": "La console sera disponible au prochain démarrage de Ryujinx.",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsGpuBackendRestartMessage",
|
"ID": "SettingsGpuBackendRestartMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
|
|||||||
@@ -12,20 +12,12 @@ namespace Ryujinx.Common.Helper
|
|||||||
private static partial nint GetConsoleWindow();
|
private static partial nint GetConsoleWindow();
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("user32")]
|
[LibraryImport("kernel32", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
private static partial bool FreeConsole();
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[LibraryImport("user32")]
|
|
||||||
private static partial nint GetForegroundWindow();
|
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[LibraryImport("user32")]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static partial bool SetForegroundWindow(nint hWnd);
|
|
||||||
|
|
||||||
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||||
|
public static bool HasConsoleWindow => OperatingSystem.IsWindows() && GetConsoleWindow() != nint.Zero;
|
||||||
|
|
||||||
public static void SetConsoleWindowState(bool show)
|
public static void SetConsoleWindowState(bool show)
|
||||||
{
|
{
|
||||||
@@ -42,22 +34,31 @@ namespace Ryujinx.Common.Helper
|
|||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
private static void SetConsoleWindowStateWindows(bool show)
|
private static void SetConsoleWindowStateWindows(bool show)
|
||||||
{
|
{
|
||||||
const int SW_HIDE = 0;
|
if (show)
|
||||||
const int SW_SHOW = 5;
|
|
||||||
|
|
||||||
nint hWnd = GetConsoleWindow();
|
|
||||||
|
|
||||||
if (hWnd == nint.Zero)
|
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist");
|
if (GetConsoleWindow() != nint.Zero)
|
||||||
|
{
|
||||||
|
Logger.SetConsoleTargetEnabled(true);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetForegroundWindow(hWnd);
|
Logger.SetConsoleTargetEnabled(false);
|
||||||
|
DetachConsole();
|
||||||
|
}
|
||||||
|
|
||||||
hWnd = GetForegroundWindow();
|
[SupportedOSPlatform("windows")]
|
||||||
|
private static void DetachConsole()
|
||||||
|
{
|
||||||
|
if (GetConsoleWindow() == nint.Zero)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
if (!FreeConsole())
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Attempted to detach console window but the operation failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
ServiceNgct,
|
ServiceNgct,
|
||||||
ServiceNifm,
|
ServiceNifm,
|
||||||
ServiceNim,
|
ServiceNim,
|
||||||
|
ServiceNotification,
|
||||||
ServiceNs,
|
ServiceNs,
|
||||||
ServiceNsd,
|
ServiceNsd,
|
||||||
ServiceNtc,
|
ServiceNtc,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
Error,
|
Error,
|
||||||
Guest,
|
Guest,
|
||||||
AccessLog,
|
AccessLog,
|
||||||
|
NetLog,
|
||||||
Notice,
|
Notice,
|
||||||
Trace,
|
Trace,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
public static Log? Error { get; private set; }
|
public static Log? Error { get; private set; }
|
||||||
public static Log? Guest { get; private set; }
|
public static Log? Guest { get; private set; }
|
||||||
public static Log? AccessLog { get; private set; }
|
public static Log? AccessLog { get; private set; }
|
||||||
|
public static Log? NetLog { get; private set; }
|
||||||
public static Log? Stub { get; private set; }
|
public static Log? Stub { get; private set; }
|
||||||
public static Log? Trace { get; private set; }
|
public static Log? Trace { get; private set; }
|
||||||
public static Log Notice { get; } // Always enabled
|
public static Log Notice { get; } // Always enabled
|
||||||
@@ -136,11 +137,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
_time = Stopwatch.StartNew();
|
_time = Stopwatch.StartNew();
|
||||||
|
|
||||||
// Logger should log to console by default
|
SetConsoleTargetEnabled(true);
|
||||||
AddTarget(new AsyncLogTargetWrapper(
|
|
||||||
new ConsoleLogTarget("console"),
|
|
||||||
1000,
|
|
||||||
AsyncLogTargetOverflowAction.Discard));
|
|
||||||
|
|
||||||
Notice = new Log(LogLevel.Notice);
|
Notice = new Log(LogLevel.Notice);
|
||||||
|
|
||||||
@@ -173,6 +170,21 @@ namespace Ryujinx.Common.Logging
|
|||||||
Updated += target.Log;
|
Updated += target.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetConsoleTargetEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
AddTarget(new AsyncLogTargetWrapper(
|
||||||
|
new ConsoleLogTarget("console"),
|
||||||
|
1000,
|
||||||
|
AsyncLogTargetOverflowAction.Discard));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveTarget("console");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void RemoveTarget(string target)
|
public static void RemoveTarget(string target)
|
||||||
{
|
{
|
||||||
ILogTarget logTarget = GetTarget(target);
|
ILogTarget logTarget = GetTarget(target);
|
||||||
@@ -236,6 +248,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break;
|
case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break;
|
||||||
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break;
|
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break;
|
||||||
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break;
|
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break;
|
||||||
|
case LogLevel.NetLog : NetLog = enabled ? new Log(LogLevel.NetLog) : null; break;
|
||||||
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break;
|
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break;
|
||||||
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break;
|
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break;
|
||||||
case LogLevel.Notice : break;
|
case LogLevel.Notice : break;
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
level,
|
level,
|
||||||
x,
|
x,
|
||||||
width,
|
width,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -579,7 +579,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
layer,
|
layer,
|
||||||
width,
|
width,
|
||||||
1,
|
1,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -609,7 +609,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -643,7 +643,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
1,
|
1,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -675,7 +675,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -744,7 +744,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
level,
|
level,
|
||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -773,7 +773,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -807,7 +807,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
depth,
|
depth,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -843,7 +843,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize / 6,
|
mipSize / 6,
|
||||||
data + faceOffset);
|
data + faceOffset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public void Map(BufferHandle handle, int size)
|
public void Map(BufferHandle handle, int size)
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
|
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
|
||||||
nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
|
nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit);
|
||||||
|
|
||||||
_maps[handle] = ptr;
|
_maps[handle] = ptr;
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
|
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
|
||||||
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
|
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
|
||||||
|
|
||||||
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
|
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -925,7 +925,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.CullFace(face.Convert());
|
GL.CullFace((TriangleFace) face.Convert());
|
||||||
|
|
||||||
GL.Enable(EnableCap.CullFace);
|
GL.Enable(EnableCap.CullFace);
|
||||||
}
|
}
|
||||||
@@ -1085,12 +1085,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
if (frontMode == backMode)
|
if (frontMode == backMode)
|
||||||
{
|
{
|
||||||
GL.PolygonMode(MaterialFace.FrontAndBack, frontMode.Convert());
|
GL.PolygonMode((TriangleFace) MaterialFace.FrontAndBack, frontMode.Convert());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GL.PolygonMode(MaterialFace.Front, frontMode.Convert());
|
GL.PolygonMode((TriangleFace) MaterialFace.Front, frontMode.Convert());
|
||||||
GL.PolygonMode(MaterialFace.Back, backMode.Convert());
|
GL.PolygonMode((TriangleFace) MaterialFace.Back, backMode.Convert());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.CompileShader(shaderHandle);
|
GL.CompileShader(shaderHandle);
|
||||||
break;
|
break;
|
||||||
case TargetLanguage.Spirv:
|
case TargetLanguage.Spirv:
|
||||||
GL.ShaderBinary(1, ref shaderHandle, (BinaryFormat)All.ShaderBinaryFormatSpirVArb, shader.BinaryCode, shader.BinaryCode.Length);
|
GL.ShaderBinary(1, ref shaderHandle, ShaderBinaryFormat.ShaderBinaryFormatSpirV, shader.BinaryCode, shader.BinaryCode.Length);
|
||||||
GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null);
|
GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
|||||||
GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
|
GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
_bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit);
|
_bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapWriteBit | MapBufferAccessMask.MapPersistentBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
|
|||||||
@@ -45,6 +45,22 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// PushOutData(object<nn::am::service::IStorage>)
|
||||||
|
public ResultCode PushOutData(ServiceCtx context)
|
||||||
|
{
|
||||||
|
IStorage appletData = GetObject<IStorage>(context, 0);
|
||||||
|
|
||||||
|
if (appletData == null || appletData.Data.Length == 0) // is this necessary?
|
||||||
|
{
|
||||||
|
return ResultCode.NullObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
_appletStandalone.InputData.Enqueue(appletData.Data);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(11)]
|
[CommandCmif(11)]
|
||||||
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
||||||
public ResultCode GetLibraryAppletInfo(ServiceCtx context)
|
public ResultCode GetLibraryAppletInfo(ServiceCtx context)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -9,16 +11,20 @@ using System.Security.Cryptography;
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Caps
|
namespace Ryujinx.HLE.HOS.Services.Caps
|
||||||
{
|
{
|
||||||
class CaptureManager
|
internal class CaptureManager
|
||||||
{
|
{
|
||||||
private readonly string _sdCardPath;
|
public CaptureManager(Switch device)
|
||||||
|
{
|
||||||
|
_ = device;
|
||||||
|
}
|
||||||
|
private readonly string _sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
|
||||||
|
|
||||||
private uint _shimLibraryVersion;
|
private uint _shimLibraryVersion;
|
||||||
|
|
||||||
public CaptureManager(Switch device)
|
private const int ScreenshotWidth = 1280;
|
||||||
{
|
private const int ScreenshotHeight = 720;
|
||||||
_sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
|
private const int ScreenshotBytesPerPixel = 4;
|
||||||
}
|
private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * ScreenshotBytesPerPixel; // 0x384000
|
||||||
|
|
||||||
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
||||||
{
|
{
|
||||||
@@ -53,33 +59,35 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode SaveScreenShot(byte[] screenshotData, ulong appletResourceUserId, ulong titleId, out ApplicationAlbumEntry applicationAlbumEntry)
|
public ResultCode SaveScreenShot(
|
||||||
|
byte[] screenshotData,
|
||||||
|
ulong appletResourceUserId,
|
||||||
|
ulong titleId,
|
||||||
|
out ApplicationAlbumEntry applicationAlbumEntry)
|
||||||
{
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceCaps, new
|
||||||
|
{
|
||||||
|
appletResourceUserId,
|
||||||
|
titleId,
|
||||||
|
screenshotDataLength = screenshotData?.Length ?? 0,
|
||||||
|
});
|
||||||
|
|
||||||
applicationAlbumEntry = default;
|
applicationAlbumEntry = default;
|
||||||
|
|
||||||
if (screenshotData.Length == 0)
|
if (screenshotData == null || screenshotData.Length == 0)
|
||||||
{
|
{
|
||||||
return ResultCode.NullInputBuffer;
|
return ResultCode.NullInputBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (screenshotData.Length < ScreenshotDataSize)
|
||||||
// NOTE: On our current implementation, appletResourceUserId starts at 0, disable it for now.
|
|
||||||
if (appletResourceUserId == 0)
|
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidArgument;
|
Logger.Warning?.PrintMsg(
|
||||||
}
|
LogClass.ServiceCaps,
|
||||||
*/
|
$"Invalid screenshot buffer size 0x{screenshotData.Length:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
/*
|
return ResultCode.NullInputBuffer;
|
||||||
// Doesn't occur in our case.
|
|
||||||
if (applicationAlbumEntry == null)
|
|
||||||
{
|
|
||||||
return ResultCode.NullOutputBuffer;
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if (screenshotData.Length >= 0x384000)
|
|
||||||
{
|
|
||||||
DateTime currentDateTime = DateTime.Now;
|
DateTime currentDateTime = DateTime.Now;
|
||||||
|
|
||||||
applicationAlbumEntry = new ApplicationAlbumEntry()
|
applicationAlbumEntry = new ApplicationAlbumEntry()
|
||||||
@@ -104,24 +112,35 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
|
|
||||||
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
|
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
|
||||||
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
|
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
|
||||||
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
|
|
||||||
|
string folderPath = Path.Combine(
|
||||||
|
_sdCardPath,
|
||||||
|
"Nintendo",
|
||||||
|
"Album",
|
||||||
|
currentDateTime.Year.ToString("0000", CultureInfo.InvariantCulture),
|
||||||
|
currentDateTime.Month.ToString("00", CultureInfo.InvariantCulture),
|
||||||
|
currentDateTime.Day.ToString("00", CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
||||||
|
|
||||||
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
|
_ = Directory.CreateDirectory(folderPath);
|
||||||
Directory.CreateDirectory(folderPath);
|
|
||||||
|
|
||||||
while (File.Exists(filePath))
|
while (File.Exists(filePath))
|
||||||
{
|
{
|
||||||
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
|
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
|
||||||
|
|
||||||
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
|
using SKBitmap bitmap = new(new SKImageInfo(ScreenshotWidth, ScreenshotHeight, SKColorType.Rgba8888));
|
||||||
using SKBitmap bitmap = new(new SKImageInfo(1280, 720, SKColorType.Rgba8888, SKAlphaType.Premul));
|
|
||||||
int dataLen = screenshotData.Length > bitmap.ByteCount ? bitmap.ByteCount : screenshotData.Length;
|
|
||||||
|
|
||||||
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), dataLen);
|
IntPtr pixels = bitmap.GetPixels();
|
||||||
|
|
||||||
|
if (pixels == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal.Copy(screenshotData, 0, pixels, ScreenshotDataSize);
|
||||||
|
|
||||||
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||||
using FileStream file = File.OpenWrite(filePath);
|
using FileStream file = File.OpenWrite(filePath);
|
||||||
@@ -130,9 +149,6 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.NullInputBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash)
|
private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash)
|
||||||
{
|
{
|
||||||
string fileName = $"{currentDateTime:yyyyMMddHHmmss}{applicationAlbumEntry.AlbumFileDateTime.UniqueId:00}-{hash}.jpg";
|
string fileName = $"{currentDateTime:yyyyMMddHHmmss}{applicationAlbumEntry.AlbumFileDateTime.UniqueId:00}-{hash}.jpg";
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Caps
|
namespace Ryujinx.HLE.HOS.Services.Caps
|
||||||
{
|
{
|
||||||
[Service("caps:su")] // 6.0.0+
|
[Service("caps:su")] // 6.0.0+
|
||||||
class IScreenShotApplicationService : IpcService
|
internal class IScreenShotApplicationService : IpcService
|
||||||
{
|
{
|
||||||
public IScreenShotApplicationService(ServiceCtx context) { }
|
private const ulong ScreenshotDataSize = 0x384000;
|
||||||
|
private const ulong ApplicationDataSize = 0x404;
|
||||||
|
|
||||||
|
public IScreenShotApplicationService(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_ = context;
|
||||||
|
}
|
||||||
[CommandCmif(32)] // 7.0.0+
|
[CommandCmif(32)] // 7.0.0+
|
||||||
// SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId)
|
// SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId)
|
||||||
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
||||||
@@ -33,6 +39,15 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
|
ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
|
||||||
ulong screenshotDataSize = context.Request.SendBuff[0].Size;
|
ulong screenshotDataSize = context.Request.SendBuff[0].Size;
|
||||||
|
|
||||||
|
if (screenshotDataSize < ScreenshotDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
|
byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
|
||||||
|
|
||||||
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry);
|
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry);
|
||||||
@@ -60,6 +75,24 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
||||||
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
||||||
|
|
||||||
|
if (applicationDataSize != ApplicationDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid ApplicationData size 0x{applicationDataSize:X}; expected 0x{ApplicationDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshotDataSize < ScreenshotDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
|
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
|
||||||
_ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
|
_ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
|
||||||
|
|
||||||
@@ -88,6 +121,23 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
||||||
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
||||||
|
|
||||||
|
if (userIdListSize != 0x88)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid UserIdList size 0x{userIdListSize:X}; expected 0x88.");
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshotDataSize < ScreenshotDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Parse the UserIdList.
|
// TODO: Parse the UserIdList.
|
||||||
_ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();
|
_ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.Exceptions;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
||||||
@@ -15,7 +14,6 @@ using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
|||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
@@ -68,10 +66,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (localCommunicationId == localCommunicationIdChecked)
|
if (localCommunicationId == localCommunicationIdChecked)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Checked!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Check failed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
context.ResponseData.Write((int)NetworkState.Error);
|
context.ResponseData.Write((int)NetworkState.Error);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: _nifmResultCode = {_nifmResultCode.ToString()}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +113,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfo: _nifmResultCode = {_nifmResultCode.ToString()}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: resultCode = {resultCode.ToString()}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,18 +136,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (_state == NetworkState.StationConnected)
|
if (_state == NetworkState.StationConnected)
|
||||||
{
|
{
|
||||||
networkInfo = _station.NetworkInfo;
|
networkInfo = _station.NetworkInfo;
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _station");
|
||||||
}
|
}
|
||||||
else if (_state == NetworkState.AccessPointCreated)
|
else if (_state == NetworkState.AccessPointCreated)
|
||||||
{
|
{
|
||||||
networkInfo = _accessPoint.NetworkInfo;
|
networkInfo = _accessPoint.NetworkInfo;
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _accessPoint");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkInfo = new NetworkInfo();
|
networkInfo = new NetworkInfo();
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: Invalid state!");
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
|
Logger.NetLog?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
|
||||||
|
|
||||||
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
|
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
|
||||||
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
|
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
|
||||||
@@ -206,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
|
Logger.NetLog?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
|
||||||
|
|
||||||
context.ResponseData.Write(config.ProxyIp);
|
context.ResponseData.Write(config.ProxyIp);
|
||||||
context.ResponseData.Write(config.ProxySubnetMask);
|
context.ResponseData.Write(config.ProxySubnetMask);
|
||||||
@@ -227,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
// NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
|
// NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
|
||||||
|
|
||||||
context.ResponseData.Write((short)_disconnectReason);
|
context.ResponseData.Write((short)_disconnectReason);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetDisconnectReason: {_disconnectReason}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,12 +252,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +271,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
context.ResponseData.WriteStruct(securityParameter);
|
context.ResponseData.WriteStruct(securityParameter);
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: securityParameter = {securityParameter}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,12 +281,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +303,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
context.ResponseData.WriteStruct(networkConfig);
|
context.ResponseData.WriteStruct(networkConfig);
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: networkConfig = {networkConfig}");
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,12 +334,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,6 +415,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (scanFilter.Ssid.Length <= 31)
|
if (scanFilter.Ssid.Length <= 31)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,11 +424,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (scanFilterFlag > ScanFilterFlag.All)
|
if (scanFilterFlag > ScanFilterFlag.All)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state - 3 >= NetworkState.AccessPoint)
|
if (_state - 3 >= NetworkState.AccessPoint)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ScanImpl: Invalid state!");
|
||||||
resultCode = ResultCode.InvalidState;
|
resultCode = ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -438,6 +456,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +481,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanInternal: availableGames = {availableGames}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +522,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}");
|
throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn, $"Protocol value: {protocolValue}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetProtocol: protocolValue = {protocolValue}");
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new { protocolValue});
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,11 +533,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenAccessPoint: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state != NetworkState.Initialized)
|
if (_state != NetworkState.Initialized)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenAccessPoint: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,6 +561,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseAccessPoint: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +571,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseAccessPoint: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,11 +621,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
|
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
|
||||||
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid object!");
|
||||||
return ResultCode.InvalidObject;
|
return ResultCode.InvalidObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,16 +656,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
|
AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
|
||||||
|
|
||||||
_accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
|
_accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created private network! " +
|
||||||
|
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} " +
|
||||||
|
$"| userConfig = {userConfig} | networkConfig = {networkConfig} | addressList = {addressList}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
|
_accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created network! " +
|
||||||
|
$"| securityConfig = {securityConfig} | userConfig = {userConfig} | networkConfig = {networkConfig}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -660,6 +693,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DestroyNetworkImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,9 +710,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
CloseAccessPoint();
|
CloseAccessPoint();
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,14 +731,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state != NetworkState.AccessPointCreated)
|
if (_state != NetworkState.AccessPointCreated)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "RejectImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState; // Must be network host to reject nodes.
|
return ResultCode.InvalidState; // Must be network host to reject nodes.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: disconnectReason = {disconnectReason} | nodeId = {nodeId}");
|
||||||
return NetworkClient.Reject(disconnectReason, nodeId);
|
return NetworkClient.Reject(disconnectReason, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,11 +753,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax)
|
if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,11 +768,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
byte[] advertiseData = new byte[bufferSize];
|
byte[] advertiseData = new byte[bufferSize];
|
||||||
|
|
||||||
context.Memory.Read(bufferPosition, advertiseData);
|
context.Memory.Read(bufferPosition, advertiseData);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: advertiseData = {advertiseData}");
|
||||||
return _accessPoint.SetAdvertiseData(advertiseData);
|
return _accessPoint.SetAdvertiseData(advertiseData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -744,20 +786,24 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acceptPolicy > AcceptPolicy.WhiteList)
|
if (acceptPolicy > AcceptPolicy.WhiteList)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated)
|
if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: acceptPolicy = {acceptPolicy}");
|
||||||
return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
|
return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -768,6 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"AddAcceptFilterEntry: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,6 +829,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ClearAcceptFilter: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,11 +844,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state != NetworkState.Initialized)
|
if (_state != NetworkState.Initialized)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenStation: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +864,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
// NOTE: Calls nifm service and returns related result codes.
|
// NOTE: Calls nifm service and returns related result codes.
|
||||||
// Since we use our own implementation we can return ResultCode.Success.
|
// Since we use our own implementation we can return ResultCode.Success.
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _station = {_station}");
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,6 +875,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseStation: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,11 +885,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetState(NetworkState.Initialized);
|
SetState(NetworkState.Initialized);
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Closed.");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,11 +956,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
|
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
|
||||||
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid object!");
|
||||||
return ResultCode.InvalidObject;
|
return ResultCode.InvalidObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,6 +982,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_state != NetworkState.Station)
|
if (_state != NetworkState.Station)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid state!");
|
||||||
resultCode = ResultCode.InvalidState;
|
resultCode = ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -932,10 +990,16 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (isPrivate)
|
if (isPrivate)
|
||||||
{
|
{
|
||||||
resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
|
resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Private connection established! " +
|
||||||
|
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} | userConfig = {userConfig} " +
|
||||||
|
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
|
resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Connection established! " +
|
||||||
|
$"| securityConfig = {securityConfig} | userConfig = {userConfig} " +
|
||||||
|
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -943,6 +1007,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: resultCode = {resultCode}");
|
||||||
|
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,6 +1023,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,14 +1037,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
_disconnectReason = disconnectReason;
|
_disconnectReason = disconnectReason;
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _disconnectReason = {_disconnectReason}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseStation();
|
CloseStation();
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,6 +1064,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: _disconnectReason = {_disconnectReason}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1010,11 +1081,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
_stateChangeEventHandle = 0;
|
_stateChangeEventHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultCode FinalizeImpl(bool isCausedBySystem)
|
private ResultCode FinalizeImpl(bool isCausedBySystem)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "FinalizeImpl");
|
||||||
DisconnectReason disconnectReason;
|
DisconnectReason disconnectReason;
|
||||||
|
|
||||||
switch (_state)
|
switch (_state)
|
||||||
@@ -1138,7 +1211,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion);
|
NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion);
|
||||||
|
|
||||||
resultCode = ResultCode.Success;
|
resultCode = ResultCode.Success;
|
||||||
|
|
||||||
_nifmResultCode = resultCode;
|
_nifmResultCode = resultCode;
|
||||||
|
|
||||||
SetState(NetworkState.Initialized);
|
SetState(NetworkState.Initialized);
|
||||||
@@ -1152,6 +1224,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"InitializeImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
|
|
||||||
protected override void OnConnected()
|
protected override void OnConnected()
|
||||||
{
|
{
|
||||||
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
|
||||||
|
|
||||||
UpdatePassphraseIfNeeded();
|
UpdatePassphraseIfNeeded();
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
|
|
||||||
protected override void OnDisconnected()
|
protected override void OnDisconnected()
|
||||||
{
|
{
|
||||||
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
|
||||||
|
|
||||||
_passphrase = null;
|
_passphrase = null;
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
|
|
||||||
protected override void OnError(SocketError error)
|
protected override void OnError(SocketError error)
|
||||||
{
|
{
|
||||||
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
|
||||||
|
|
||||||
_error.Set();
|
_error.Set();
|
||||||
}
|
}
|
||||||
@@ -428,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
|
Logger.NetLog?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
|
||||||
_hostedProxy.Start();
|
_hostedProxy.Start();
|
||||||
|
|
||||||
(_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();
|
(_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
||||||
@@ -36,10 +37,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (Connected)
|
if (Connected)
|
||||||
{
|
{
|
||||||
_parent.SetState(NetworkState.StationConnected);
|
_parent.SetState(NetworkState.StationConnected);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"NetworkChanged: {NetworkInfo}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser));
|
_parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser));
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"NetworkChanged: Disconnected (DestroyedByUser)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -81,8 +81,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, IStoredData<T> oldMiiData, SourceFlag flag, IStoredData<T> newMiiData) where T : unmanaged
|
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, T oldMiiData, SourceFlag flag, out T newMiiData) where T : unmanaged, IStoredData<T>
|
||||||
{
|
{
|
||||||
|
newMiiData = default;
|
||||||
|
|
||||||
if (!flag.HasFlag(SourceFlag.Database))
|
if (!flag.HasFlag(SourceFlag.Database))
|
||||||
{
|
{
|
||||||
return ResultCode.NotFound;
|
return ResultCode.NotFound;
|
||||||
@@ -106,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
|
|
||||||
newMiiData.SetFromStoreData(storeData);
|
newMiiData.SetFromStoreData(storeData);
|
||||||
|
|
||||||
if (oldMiiData == newMiiData)
|
if (oldMiiData.Equals(newMiiData))
|
||||||
{
|
{
|
||||||
return ResultCode.NotUpdated;
|
return ResultCode.NotUpdated;
|
||||||
}
|
}
|
||||||
@@ -286,6 +288,18 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResultCode Append(DatabaseSessionMetadata metadata, CharInfo charInfo)
|
||||||
|
{
|
||||||
|
ResultCode result = _miiDatabase.Append(metadata, _utilityImpl, charInfo);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
result = _miiDatabase.SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
|
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
|
||||||
{
|
{
|
||||||
coreData = new CoreData();
|
coreData = new CoreData();
|
||||||
|
|||||||
@@ -449,6 +449,32 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResultCode Append(DatabaseSessionMetadata metadata, UtilityImpl utilityImpl, CharInfo charInfo)
|
||||||
|
{
|
||||||
|
if (!charInfo.IsValid())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidCharInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charInfo.Type == 1)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidOperationOnSpecialMii;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreData coreData = new();
|
||||||
|
coreData.SetFromCharInfo(charInfo);
|
||||||
|
|
||||||
|
StoreData storeData;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
storeData = StoreData.BuildFromCoreData(utilityImpl, coreData);
|
||||||
|
}
|
||||||
|
while (_database.GetIndexByCreatorId(out _, storeData.CreateId));
|
||||||
|
|
||||||
|
return AddOrReplace(metadata, storeData);
|
||||||
|
}
|
||||||
|
|
||||||
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
|
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
|
||||||
{
|
{
|
||||||
if (!_database.GetIndexByCreatorId(out int index, createId))
|
if (!_database.GetIndexByCreatorId(out int index, createId))
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
|
|
||||||
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
||||||
{
|
{
|
||||||
newCharInfo = default;
|
return _database.UpdateLatest(_metadata, oldCharInfo, flag, out newCharInfo);
|
||||||
|
|
||||||
return _database.UpdateLatest(_metadata, oldCharInfo, flag, newCharInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
|
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
|
||||||
@@ -113,14 +111,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
|
|
||||||
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
|
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
|
||||||
{
|
{
|
||||||
newStoreData = default;
|
|
||||||
|
|
||||||
if (!_isSystem)
|
if (!_isSystem)
|
||||||
{
|
{
|
||||||
|
newStoreData = default;
|
||||||
|
|
||||||
return ResultCode.PermissionDenied;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _database.UpdateLatest(_metadata, oldStoreData, flag, newStoreData);
|
return _database.UpdateLatest(_metadata, oldStoreData, flag, out newStoreData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
|
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
|
||||||
@@ -262,5 +260,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
{
|
{
|
||||||
return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
|
return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Append(CharInfo charInfo)
|
||||||
|
{
|
||||||
|
return _database.Append(_metadata, charInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,6 +340,15 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(26)] // 10.2.0+
|
||||||
|
// Append(nn::mii::CharInfo char_info)
|
||||||
|
public ResultCode Append(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CharInfo charInfo = context.RequestData.ReadStruct<CharInfo>();
|
||||||
|
|
||||||
|
return Append(charInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
|
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
|
||||||
{
|
{
|
||||||
byte[] rawData;
|
byte[] rawData;
|
||||||
@@ -421,5 +430,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
|
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
|
||||||
|
|
||||||
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
|
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
|
||||||
|
|
||||||
|
protected abstract ResultCode Append(CharInfo charInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||||
|
{
|
||||||
|
[Service("notif:s")] // 9.0.0+
|
||||||
|
class INotificationServices : IpcService
|
||||||
|
{
|
||||||
|
public INotificationServices(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandCmif(1000)] // 9.0.0+
|
||||||
|
// GetNotificationCount() -> nn::notification::server::INotificationSystemEventAccessor
|
||||||
|
public ResultCode GetNotificationCount(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new INotificationSystemEventAccessor(context));
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1040)] // 9.0.0+
|
||||||
|
// GetNotificationSendingNotifier() -> nn::notification::server::INotificationSystemEventAccessor
|
||||||
|
public ResultCode GetNotificationSendingNotifier(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new INotificationSystemEventAccessor(context));
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,33 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Notification
|
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||||
{
|
{
|
||||||
[Service("notif:a")] // 9.0.0+
|
[Service("notif:a")] // 9.0.0+
|
||||||
class INotificationServicesForApplication : IpcService
|
class INotificationServicesForApplication : IpcService
|
||||||
{
|
{
|
||||||
public INotificationServicesForApplication(ServiceCtx context) { }
|
public INotificationServicesForApplication(ServiceCtx context) { }
|
||||||
|
|
||||||
|
// Leaving this here since I can never find it: https://switchbrew.org/wiki/Glue_services
|
||||||
|
|
||||||
|
[CommandCmif(520)] // 9.0.0+
|
||||||
|
// ListAlarmSettings(nn::arp::ApplicationCertificate) -> s32 AlarmSettingsCount
|
||||||
|
public ResultCode ListAlarmSettings(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// TO-DO: Currently just returns 0. Should read in an ApplicationCertificate.
|
||||||
|
int alarmSettingsCount = 0;
|
||||||
|
context.ResponseData.Write(alarmSettingsCount);
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1000)] // 9.0.0+
|
||||||
|
// Initialize(PID-descriptor, u64 pid_reserved)
|
||||||
|
public ResultCode Intialize(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong pid = context.Request.HandleDesc.PId;
|
||||||
|
context.RequestData.ReadUInt64(); // pid placeholder, zero
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNotification, new { pid });
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Notification
|
|
||||||
{
|
|
||||||
[Service("notif:s")] // 9.0.0+
|
|
||||||
class INotificationServicesForSystem : IpcService
|
|
||||||
{
|
|
||||||
public INotificationServicesForSystem(ServiceCtx context) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||||
|
{
|
||||||
|
class INotificationSystemEventAccessor : IpcService
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly KEvent _getNotificationSendingNotifierEvent;
|
||||||
|
private int _getNotificationSendingNotifierEventHandle;
|
||||||
|
public INotificationSystemEventAccessor(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandCmif(0)] // 9.0.0+
|
||||||
|
// GetNotificationSendingNotifier() -> nn::notification::server::INotificationSystemEventAccessor
|
||||||
|
public ResultCode GetSystemEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (_getNotificationSendingNotifierEventHandle == 0)
|
||||||
|
{
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(_getNotificationSendingNotifierEvent.ReadableEvent, out _getNotificationSendingNotifierEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_getNotificationSendingNotifierEventHandle);
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
private ulong _latestPid;
|
private ulong _latestPid;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
public ProcessResult? ActiveApplication
|
public ProcessResult? ActiveApplication
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -44,6 +45,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
public ProcessLoader(Switch device)
|
public ProcessLoader(Switch device)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
|
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
|
||||||
<PackageReference Include="MsgPack.Cli" />
|
<PackageReference Include="MsgPack.Cli" />
|
||||||
<PackageReference Include="SkiaSharp" />
|
<PackageReference Include="SkiaSharp" />
|
||||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" />
|
<PackageReference Include="SkiaSharp.NativeAssets.Win32" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.macOS" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" />
|
||||||
<PackageReference Include="NetCoreServer" />
|
<PackageReference Include="NetCoreServer" />
|
||||||
<PackageReference Include="Open.NAT.Core" />
|
<PackageReference Include="Open.NAT.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
187
src/Ryujinx.Tests/HLE/CaptureManagerTests.cs
Normal file
187
src/Ryujinx.Tests/HLE/CaptureManagerTests.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Caps;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||||
|
using SkiaSharp;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.HLE
|
||||||
|
{
|
||||||
|
public class CaptureManagerTests
|
||||||
|
{
|
||||||
|
private const int ScreenshotWidth = 1280;
|
||||||
|
private const int ScreenshotHeight = 720;
|
||||||
|
private const int BytesPerPixel = 4;
|
||||||
|
|
||||||
|
private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * BytesPerPixel; // 0x384000
|
||||||
|
private const int PaddedScreenshotDataSize = ScreenshotWidth * 768 * BytesPerPixel; // 0x3C0000
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotRejectsBufferSmallerThan720p()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = new byte[ScreenshotDataSize - 1];
|
||||||
|
|
||||||
|
ResultCode result = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.NullInputBuffer));
|
||||||
|
Assert.That(Directory.Exists(Path.Combine(tempSdCard.Path, "Nintendo", "Album")), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotAcceptsExact720pBuffer()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = CreateTestPattern(ScreenshotDataSize);
|
||||||
|
|
||||||
|
ResultCode result = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out ApplicationAlbumEntry applicationAlbumEntry);
|
||||||
|
|
||||||
|
string filePath = GetSingleAlbumFile(tempSdCard.Path);
|
||||||
|
|
||||||
|
using SKBitmap bitmap = SKBitmap.Decode(filePath);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(bitmap.Width, Is.EqualTo(ScreenshotWidth));
|
||||||
|
Assert.That(bitmap.Height, Is.EqualTo(ScreenshotHeight));
|
||||||
|
Assert.That(applicationAlbumEntry.TitleId, Is.EqualTo(0x0100000000001000));
|
||||||
|
Assert.That(applicationAlbumEntry.AlbumStorage, Is.EqualTo(AlbumStorage.Sd));
|
||||||
|
Assert.That(applicationAlbumEntry.ContentType, Is.EqualTo(ContentType.Screenshot));
|
||||||
|
Assert.That(applicationAlbumEntry.Unknown0x1f, Is.EqualTo(1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotAcceptsBufferLargerThan720p()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = CreateTestPattern(PaddedScreenshotDataSize);
|
||||||
|
|
||||||
|
ResultCode result = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out ApplicationAlbumEntry applicationAlbumEntry);
|
||||||
|
|
||||||
|
string filePath = GetSingleAlbumFile(tempSdCard.Path);
|
||||||
|
|
||||||
|
using SKBitmap bitmap = SKBitmap.Decode(filePath);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(bitmap.Width, Is.EqualTo(ScreenshotWidth));
|
||||||
|
Assert.That(bitmap.Height, Is.EqualTo(ScreenshotHeight));
|
||||||
|
Assert.That(applicationAlbumEntry.TitleId, Is.EqualTo(0x0100000000001000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotCreatesUniqueFileNamesForRepeatedSaves()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = CreateTestPattern(ScreenshotDataSize);
|
||||||
|
|
||||||
|
ResultCode firstResult = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
ResultCode secondResult = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
string[] files = Directory.GetFiles(
|
||||||
|
Path.Combine(tempSdCard.Path, "Nintendo", "Album"),
|
||||||
|
"*.jpg",
|
||||||
|
SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(firstResult, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(secondResult, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(files, Has.Length.EqualTo(2));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CaptureManager CreateCaptureManager(string sdCardPath)
|
||||||
|
{
|
||||||
|
CaptureManager captureManager = (CaptureManager)RuntimeHelpers.GetUninitializedObject(typeof(CaptureManager));
|
||||||
|
|
||||||
|
typeof(CaptureManager)
|
||||||
|
.GetField("_sdCardPath", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.SetValue(captureManager, sdCardPath);
|
||||||
|
|
||||||
|
return captureManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSingleAlbumFile(string sdCardPath)
|
||||||
|
{
|
||||||
|
string albumPath = Path.Combine(sdCardPath, "Nintendo", "Album");
|
||||||
|
|
||||||
|
string[] files = Directory.GetFiles(albumPath, "*.jpg", SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
Assert.That(files, Has.Length.EqualTo(1));
|
||||||
|
|
||||||
|
return files.Single();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] CreateTestPattern(int size)
|
||||||
|
{
|
||||||
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
|
int pixelCount = size / BytesPerPixel;
|
||||||
|
|
||||||
|
for (int i = 0; i < pixelCount; i++)
|
||||||
|
{
|
||||||
|
int x = i % ScreenshotWidth;
|
||||||
|
int y = i / ScreenshotWidth;
|
||||||
|
|
||||||
|
data[(i * 4) + 0] = (byte)(x & 0xff);
|
||||||
|
data[(i * 4) + 1] = (byte)(y & 0xff);
|
||||||
|
data[(i * 4) + 2] = 0x80;
|
||||||
|
data[(i * 4) + 3] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TempSdCard : IDisposable
|
||||||
|
{
|
||||||
|
public string Path { get; } = System.IO.Path.Combine(
|
||||||
|
TestContext.CurrentContext.WorkDirectory,
|
||||||
|
"sdcard-" + Guid.NewGuid());
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(Path))
|
||||||
|
{
|
||||||
|
Directory.Delete(Path, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/Ryujinx.Tests/HLE/MiiDatabaseTests.cs
Normal file
122
src/Ryujinx.Tests/HLE/MiiDatabaseTests.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.StaticService;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.HLE
|
||||||
|
{
|
||||||
|
public class MiiDatabaseTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void UpdateLatestReturnsStoredCharInfo()
|
||||||
|
{
|
||||||
|
DatabaseImpl database = new();
|
||||||
|
StoreData storedData = StoreData.BuildDefault(new UtilityImpl(new TickSource(19200000)), 0);
|
||||||
|
MiiDatabaseManager databaseManager = GetDatabaseManager(database);
|
||||||
|
|
||||||
|
NintendoFigurineDatabase figurineDatabase = new();
|
||||||
|
figurineDatabase.Format();
|
||||||
|
figurineDatabase.Add(storedData);
|
||||||
|
SetFigurineDatabase(databaseManager, figurineDatabase);
|
||||||
|
|
||||||
|
TestDatabaseService service = new(database);
|
||||||
|
|
||||||
|
CharInfo oldCharInfo = new();
|
||||||
|
oldCharInfo.SetFromStoreData(storedData);
|
||||||
|
oldCharInfo.Height--;
|
||||||
|
|
||||||
|
ResultCode result = service.UpdateLatestForTest(oldCharInfo, SourceFlag.Database, out CharInfo newCharInfo);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(newCharInfo.CreateId, Is.EqualTo(oldCharInfo.CreateId));
|
||||||
|
Assert.That(newCharInfo.Height, Is.EqualTo(storedData.CoreData.Height));
|
||||||
|
Assert.That(newCharInfo.IsValid(), Is.True);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AppendAddsRegularCharInfoToDatabase()
|
||||||
|
{
|
||||||
|
DatabaseImpl database = new();
|
||||||
|
UtilityImpl utilityImpl = new(new TickSource(19200000));
|
||||||
|
SetUtilityImpl(database, utilityImpl);
|
||||||
|
MiiDatabaseManager databaseManager = GetDatabaseManager(database);
|
||||||
|
SetFigurineDatabase(databaseManager, CreateFormattedDatabase());
|
||||||
|
|
||||||
|
StoreData defaultStoreData = StoreData.BuildDefault(utilityImpl, 0);
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(defaultStoreData.CoreData.IsValid(), Is.True);
|
||||||
|
Assert.That(defaultStoreData.IsValidDataCrc(), Is.True);
|
||||||
|
Assert.That(defaultStoreData.IsValidDeviceCrc(), Is.True);
|
||||||
|
Assert.That(defaultStoreData.IsValid(), Is.True);
|
||||||
|
});
|
||||||
|
|
||||||
|
CharInfo charInfo = new();
|
||||||
|
charInfo.SetFromStoreData(defaultStoreData);
|
||||||
|
|
||||||
|
DatabaseSessionMetadata metadata = database.CreateSessionMetadata(new SpecialMiiKeyCode());
|
||||||
|
|
||||||
|
ResultCode result = databaseManager.Append(metadata, utilityImpl, charInfo);
|
||||||
|
|
||||||
|
int count = databaseManager.GetCount(metadata);
|
||||||
|
databaseManager.Get(metadata, 0, out StoreData storedData);
|
||||||
|
|
||||||
|
CoreData expectedCoreData = new();
|
||||||
|
expectedCoreData.SetFromCharInfo(charInfo);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(count, Is.EqualTo(1));
|
||||||
|
Assert.That(storedData.IsValid(), Is.True);
|
||||||
|
Assert.That(storedData.CreateId, Is.Not.EqualTo(charInfo.CreateId));
|
||||||
|
Assert.That(storedData.CoreData, Is.EqualTo(expectedCoreData));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TestDatabaseService(DatabaseImpl database) : DatabaseServiceImpl(database, true, new SpecialMiiKeyCode())
|
||||||
|
{
|
||||||
|
public ResultCode UpdateLatestForTest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
||||||
|
{
|
||||||
|
return UpdateLatest(oldCharInfo, flag, out newCharInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MiiDatabaseManager GetDatabaseManager(DatabaseImpl database)
|
||||||
|
{
|
||||||
|
return (MiiDatabaseManager)typeof(DatabaseImpl)
|
||||||
|
.GetField("_miiDatabase", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.GetValue(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetFigurineDatabase(MiiDatabaseManager databaseManager, NintendoFigurineDatabase figurineDatabase)
|
||||||
|
{
|
||||||
|
typeof(MiiDatabaseManager)
|
||||||
|
.GetField("_database", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.SetValue(databaseManager, figurineDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NintendoFigurineDatabase CreateFormattedDatabase()
|
||||||
|
{
|
||||||
|
NintendoFigurineDatabase figurineDatabase = new();
|
||||||
|
figurineDatabase.Format();
|
||||||
|
|
||||||
|
return figurineDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetUtilityImpl(DatabaseImpl database, UtilityImpl utilityImpl)
|
||||||
|
{
|
||||||
|
typeof(DatabaseImpl)
|
||||||
|
.GetField("_utilityImpl", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.SetValue(database, utilityImpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -254,6 +254,7 @@ namespace Ryujinx.Headless
|
|||||||
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
||||||
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
||||||
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||||
|
Logger.SetEnable(LogLevel.NetLog, option.LoggingEnableFsAccessLog);
|
||||||
|
|
||||||
if (!option.DisableFileLog)
|
if (!option.DisableFileLog)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ namespace Ryujinx.Headless
|
|||||||
if (NeedsOverride(nameof(LoggingEnableFsAccessLog)))
|
if (NeedsOverride(nameof(LoggingEnableFsAccessLog)))
|
||||||
LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog;
|
LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog;
|
||||||
|
|
||||||
|
if (NeedsOverride(nameof(LoggingEnableNetLog)))
|
||||||
|
LoggingEnableNetLog = configurationState.Logger.EnableNetLog;
|
||||||
|
|
||||||
if (NeedsOverride(nameof(LoggingGraphicsDebugLevel)))
|
if (NeedsOverride(nameof(LoggingGraphicsDebugLevel)))
|
||||||
LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel;
|
LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel;
|
||||||
|
|
||||||
@@ -371,6 +374,9 @@ namespace Ryujinx.Headless
|
|||||||
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
||||||
public bool LoggingEnableFsAccessLog { get; set; }
|
public bool LoggingEnableFsAccessLog { get; set; }
|
||||||
|
|
||||||
|
[Option("enable-net-logs", Required = false, Default = false, HelpText = "Enables printing net log messages.")]
|
||||||
|
public bool LoggingEnableNetLog { get; set; }
|
||||||
|
|
||||||
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
||||||
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,9 @@
|
|||||||
<PackageReference Include="SharpCompress" />
|
<PackageReference Include="SharpCompress" />
|
||||||
<PackageReference Include="Svg.Controls.Avalonia" />
|
<PackageReference Include="Svg.Controls.Avalonia" />
|
||||||
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
|
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
<PackageReference Include="DynamicData" />
|
<PackageReference Include="DynamicData" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" />
|
<PackageReference Include="FluentAvaloniaUI" />
|
||||||
<PackageReference Include="CommandLineParser" />
|
<PackageReference Include="CommandLineParser" />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 71;
|
public const int CurrentVersion = 72;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
@@ -114,6 +114,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool LoggingEnableFsAccessLog { get; set; }
|
public bool LoggingEnableFsAccessLog { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables printing network log messages
|
||||||
|
/// </summary>
|
||||||
|
public bool LoggingEnableNetLog { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables log messages from Avalonia
|
/// Enables log messages from Avalonia
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Ava.Systems.Configuration.System;
|
using Ryujinx.Ava.Systems.Configuration.System;
|
||||||
using Ryujinx.Ava.Systems.Configuration.UI;
|
using Ryujinx.Ava.Systems.Configuration.UI;
|
||||||
@@ -68,6 +68,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Logger.EnableTrace.Value = cff.LoggingEnableTrace;
|
Logger.EnableTrace.Value = cff.LoggingEnableTrace;
|
||||||
Logger.EnableGuest.Value = cff.LoggingEnableGuest;
|
Logger.EnableGuest.Value = cff.LoggingEnableGuest;
|
||||||
Logger.EnableFsAccessLog.Value = cff.LoggingEnableFsAccessLog;
|
Logger.EnableFsAccessLog.Value = cff.LoggingEnableFsAccessLog;
|
||||||
|
Logger.EnableNetLog.Value = cff.LoggingEnableNetLog;
|
||||||
Logger.FilteredClasses.Value = cff.LoggingFilteredClasses;
|
Logger.FilteredClasses.Value = cff.LoggingFilteredClasses;
|
||||||
Logger.GraphicsDebugLevel.Value = cff.LoggingGraphicsDebugLevel;
|
Logger.GraphicsDebugLevel.Value = cff.LoggingGraphicsDebugLevel;
|
||||||
|
|
||||||
|
|||||||
@@ -258,6 +258,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
|
public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables printing network log messages
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<bool> EnableNetLog { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables log messages from Avalonia
|
/// Enables log messages from Avalonia
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -289,6 +294,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
EnableTrace = new ReactiveObject<bool>();
|
EnableTrace = new ReactiveObject<bool>();
|
||||||
EnableGuest = new ReactiveObject<bool>();
|
EnableGuest = new ReactiveObject<bool>();
|
||||||
EnableFsAccessLog = new ReactiveObject<bool>();
|
EnableFsAccessLog = new ReactiveObject<bool>();
|
||||||
|
EnableNetLog = new ReactiveObject<bool>();
|
||||||
EnableAvaloniaLog = new ReactiveObject<bool>();
|
EnableAvaloniaLog = new ReactiveObject<bool>();
|
||||||
FilteredClasses = new ReactiveObject<LogClass[]>();
|
FilteredClasses = new ReactiveObject<LogClass[]>();
|
||||||
EnableFileLog = new ReactiveObject<bool>();
|
EnableFileLog = new ReactiveObject<bool>();
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
LoggingEnableTrace = Logger.EnableTrace,
|
LoggingEnableTrace = Logger.EnableTrace,
|
||||||
LoggingEnableGuest = Logger.EnableGuest,
|
LoggingEnableGuest = Logger.EnableGuest,
|
||||||
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
|
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
|
||||||
|
LoggingEnableNetLog = Logger.EnableNetLog,
|
||||||
LoggingEnableAvalonia = Logger.EnableAvaloniaLog,
|
LoggingEnableAvalonia = Logger.EnableAvaloniaLog,
|
||||||
LoggingFilteredClasses = Logger.FilteredClasses,
|
LoggingFilteredClasses = Logger.FilteredClasses,
|
||||||
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
|
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
|
||||||
@@ -176,6 +177,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Logger.EnableTrace.Value = false;
|
Logger.EnableTrace.Value = false;
|
||||||
Logger.EnableGuest.Value = true;
|
Logger.EnableGuest.Value = true;
|
||||||
Logger.EnableFsAccessLog.Value = false;
|
Logger.EnableFsAccessLog.Value = false;
|
||||||
|
Logger.EnableNetLog.Value = false;
|
||||||
Logger.EnableAvaloniaLog.Value = false;
|
Logger.EnableAvaloniaLog.Value = false;
|
||||||
Logger.FilteredClasses.Value = [];
|
Logger.FilteredClasses.Value = [];
|
||||||
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
|
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
(_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue);
|
(_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue);
|
||||||
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event +=
|
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event +=
|
||||||
(_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
|
(_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
|
||||||
|
ConfigurationState.Instance.Logger.EnableNetLog.Event +=
|
||||||
|
(_, e) => Logger.SetEnable(LogLevel.NetLog, e.NewValue);
|
||||||
|
|
||||||
ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) =>
|
ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
internal partial class Win32NativeInterop
|
internal partial class Win32NativeInterop
|
||||||
{
|
{
|
||||||
internal const int GWLP_WNDPROC = -4;
|
internal const int GWLP_WNDPROC = -4;
|
||||||
|
internal const int GWL_STYLE = -16;
|
||||||
|
internal const int GWL_EXSTYLE = -20;
|
||||||
|
|
||||||
|
internal const uint WS_OVERLAPPEDWINDOW = 0x00CF0000;
|
||||||
|
internal const uint WS_POPUP = 0x80000000;
|
||||||
|
internal const uint WS_VISIBLE = 0x10000000;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ClassStyles : uint
|
public enum ClassStyles : uint
|
||||||
@@ -107,9 +113,29 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
nint hInstance,
|
nint hInstance,
|
||||||
nint lpParam);
|
nint lpParam);
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW")]
|
||||||
|
public static partial nint GetWindowLongPtrW(nint hWnd, int nIndex);
|
||||||
|
|
||||||
[LibraryImport("user32.dll", SetLastError = true)]
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value);
|
public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value);
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static partial bool SetWindowPos(
|
||||||
|
nint hWnd,
|
||||||
|
nint hWndInsertAfter,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int cx,
|
||||||
|
int cy,
|
||||||
|
uint uFlags);
|
||||||
|
|
||||||
|
internal const uint SWP_NOZORDER = 0x0004;
|
||||||
|
internal const uint SWP_NOACTIVATE = 0x0010;
|
||||||
|
internal const uint SWP_FRAMECHANGED = 0x0020;
|
||||||
|
internal const uint SWP_NOMOVE = 0x0002;
|
||||||
|
internal const uint SWP_NOSIZE = 0x0001;
|
||||||
|
|
||||||
[LibraryImport("user32.dll", SetLastError = true)]
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
public static partial ushort GetAsyncKeyState(int nVirtKey);
|
public static partial ushort GetAsyncKeyState(int nVirtKey);
|
||||||
|
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
if (Devices.Any(controller => controller.Name == name))
|
if (Devices.Any(controller => controller.Name == name))
|
||||||
{
|
{
|
||||||
controllerNumber++;
|
controllerNumber++;
|
||||||
name = GetGamepadName(gamepad, controllerNumber);
|
name = GetUniqueGamepadName(gamepad, ref controllerNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -656,10 +657,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
get => ConfigurationState.Instance.UI.ShowConsole;
|
get => ConfigurationState.Instance.UI.ShowConsole;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
bool restartRequired = value && !ConsoleHelper.HasConsoleWindow;
|
||||||
|
|
||||||
ConfigurationState.Instance.UI.ShowConsole.Value = value;
|
ConfigurationState.Instance.UI.ShowConsole.Value = value;
|
||||||
|
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
|
if (restartRequired)
|
||||||
|
{
|
||||||
|
NotificationHelper.ShowInformation(
|
||||||
|
LocaleManager.Instance[LocaleKeys.SettingsAppRequiredRestartMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.SettingsShowConsoleRestartMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2005,7 +2015,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
LastFullscreenToggle = Environment.TickCount64;
|
LastFullscreenToggle = Environment.TickCount64;
|
||||||
|
|
||||||
if (WindowState is not WindowState.Normal)
|
if (WindowState is WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
WindowState = WindowState.Normal;
|
WindowState = WindowState.Normal;
|
||||||
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
|
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
|
||||||
@@ -2014,21 +2024,74 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
ShowMenuAndStatusBar = true;
|
ShowMenuAndStatusBar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
RestoreWindowFromFullscreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WindowState = WindowState.FullScreen;
|
|
||||||
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
if (IsGameRunning)
|
if (IsGameRunning)
|
||||||
{
|
{
|
||||||
ShowMenuAndStatusBar = false;
|
ShowMenuAndStatusBar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
MakeWindowFullscreen();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WindowState = WindowState.FullScreen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IsFullScreen = WindowState is WindowState.FullScreen;
|
IsFullScreen = WindowState is WindowState.FullScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private nint _savedWindowStyle;
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
private void MakeWindowFullscreen()
|
||||||
|
{
|
||||||
|
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
|
||||||
|
if (hwnd == nint.Zero) return;
|
||||||
|
|
||||||
|
// Save current style and placement
|
||||||
|
_savedWindowStyle = Win32NativeInterop.GetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE);
|
||||||
|
|
||||||
|
// Remove window chrome: WS_OVERLAPPEDWINDOW -> WS_POPUP | WS_VISIBLE
|
||||||
|
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE,
|
||||||
|
unchecked((nint)(Win32NativeInterop.WS_POPUP | Win32NativeInterop.WS_VISIBLE)));
|
||||||
|
|
||||||
|
Avalonia.Platform.Screen? screen = Window.Screens.ScreenFromVisual(Window);
|
||||||
|
int w = screen?.Bounds.Width ?? 0;
|
||||||
|
int h = screen?.Bounds.Height ?? 0;
|
||||||
|
|
||||||
|
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, w, h,
|
||||||
|
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | Win32NativeInterop.SWP_FRAMECHANGED);
|
||||||
|
|
||||||
|
WindowState = WindowState.FullScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
private void RestoreWindowFromFullscreen()
|
||||||
|
{
|
||||||
|
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
|
||||||
|
if (hwnd == nint.Zero) return;
|
||||||
|
|
||||||
|
// Restore original window style
|
||||||
|
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE, _savedWindowStyle);
|
||||||
|
|
||||||
|
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, 0, 0,
|
||||||
|
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE |
|
||||||
|
Win32NativeInterop.SWP_FRAMECHANGED | Win32NativeInterop.SWP_NOMOVE | Win32NativeInterop.SWP_NOSIZE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void SaveConfig()
|
public static void SaveConfig()
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool EnableTrace { get; set; }
|
public bool EnableTrace { get; set; }
|
||||||
public bool EnableGuest { get; set; }
|
public bool EnableGuest { get; set; }
|
||||||
public bool EnableFsAccessLog { get; set; }
|
public bool EnableFsAccessLog { get; set; }
|
||||||
|
public bool EnableNetLog { get; set; }
|
||||||
public bool EnableAvaloniaLog { get; set; }
|
public bool EnableAvaloniaLog { get; set; }
|
||||||
public bool EnableDebug { get; set; }
|
public bool EnableDebug { get; set; }
|
||||||
public bool IsOpenAlEnabled { get; set; }
|
public bool IsOpenAlEnabled { get; set; }
|
||||||
@@ -725,6 +726,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
EnableGuest = config.Logger.EnableGuest;
|
EnableGuest = config.Logger.EnableGuest;
|
||||||
EnableDebug = config.Logger.EnableDebug;
|
EnableDebug = config.Logger.EnableDebug;
|
||||||
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
||||||
|
EnableNetLog = config.Logger.EnableNetLog;
|
||||||
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
|
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
|
||||||
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
||||||
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
||||||
@@ -848,6 +850,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.Logger.EnableGuest.Value = EnableGuest;
|
config.Logger.EnableGuest.Value = EnableGuest;
|
||||||
config.Logger.EnableDebug.Value = EnableDebug;
|
config.Logger.EnableDebug.Value = EnableDebug;
|
||||||
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
|
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
|
||||||
|
config.Logger.EnableNetLog.Value = EnableNetLog;
|
||||||
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
|
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
|
||||||
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
||||||
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
||||||
|
|||||||
@@ -70,6 +70,10 @@
|
|||||||
ToolTip.Tip="{ext:Locale FileAccessLogTooltip}">
|
ToolTip.Tip="{ext:Locale FileAccessLogTooltip}">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableFsAccessLogs}" />
|
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableFsAccessLogs}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding EnableNetLog}"
|
||||||
|
ToolTip.Tip="{ext:Locale NetLogTooltip}">
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableNetLogs}" />
|
||||||
|
</CheckBox>
|
||||||
<CheckBox IsChecked="{Binding EnableDebug}"
|
<CheckBox IsChecked="{Binding EnableDebug}"
|
||||||
ToolTip.Tip="{ext:Locale DebugLogTooltip}">
|
ToolTip.Tip="{ext:Locale DebugLogTooltip}">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" />
|
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" />
|
||||||
|
|||||||
Reference in New Issue
Block a user