Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
70d65d3d8e | ||
|
0b58f46266 | ||
|
aa96dcb1be | ||
|
82a638230e | ||
|
d11fe26aa3 | ||
|
dcf10561b9 | ||
|
cdc8fed64f | ||
|
388446c255 | ||
|
29e192f241 | ||
|
5b3662b793 | ||
|
1329c47ea4 | ||
|
6bce46621c | ||
|
e6e5838916 | ||
|
51065d9129 | ||
|
6228331fd1 |
@@ -233,6 +233,29 @@ dotnet_naming_style.IPascalCase.required_suffix =
|
|||||||
dotnet_naming_style.IPascalCase.word_separator =
|
dotnet_naming_style.IPascalCase.word_separator =
|
||||||
dotnet_naming_style.IPascalCase.capitalization = pascal_case
|
dotnet_naming_style.IPascalCase.capitalization = pascal_case
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# .NET 8 migration (new warnings are caused by the NET 8 C# compiler and analyzer)
|
||||||
|
# The following info messages might need to be fixed in the source code instead of hiding the actual message
|
||||||
|
# Without the following lines, dotnet format would fail
|
||||||
|
# Disable "Collection initialization can be simplified"
|
||||||
|
dotnet_diagnostic.IDE0028.severity = none
|
||||||
|
dotnet_diagnostic.IDE0300.severity = none
|
||||||
|
dotnet_diagnostic.IDE0301.severity = none
|
||||||
|
dotnet_diagnostic.IDE0302.severity = none
|
||||||
|
dotnet_diagnostic.IDE0305.severity = none
|
||||||
|
# Disable "'new' expression can be simplified"
|
||||||
|
dotnet_diagnostic.IDE0090.severity = none
|
||||||
|
# Disable "Use primary constructor"
|
||||||
|
dotnet_diagnostic.IDE0290.severity = none
|
||||||
|
# Disable "Member '' does not access instance data and can be marked as static"
|
||||||
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
# Disable "Change type of field '' from '' to '' for improved performance"
|
||||||
|
dotnet_diagnostic.CA1859.severity = none
|
||||||
|
# Disable "Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array"
|
||||||
|
dotnet_diagnostic.CA1861.severity = none
|
||||||
|
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
|
||||||
|
dotnet_diagnostic.CA1862.severity = none
|
||||||
|
|
||||||
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
[src/Ryujinx.HLE/HOS/Services/**.cs]
|
||||||
# Disable "mark members as static" rule for services
|
# Disable "mark members as static" rule for services
|
||||||
dotnet_diagnostic.CA1822.severity = none
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
OS_NAME: Windows x64
|
OS_NAME: Windows x64
|
||||||
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||||
RELEASE_ZIP_OS_NAME: win_x64
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
4
.github/workflows/flatpak.yml
vendored
4
.github/workflows/flatpak.yml
vendored
@@ -49,7 +49,9 @@ jobs:
|
|||||||
run: python -m pip install PyYAML lxml
|
run: python -m pip install PyYAML lxml
|
||||||
|
|
||||||
- name: Restore Nuget packages
|
- name: Restore Nuget packages
|
||||||
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
|
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
|
||||||
|
# So we just publish to grab the dependencies
|
||||||
|
run: dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
||||||
|
|
||||||
- name: Generate nuget_sources.json
|
- name: Generate nuget_sources.json
|
||||||
shell: python
|
shell: python
|
||||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -25,7 +25,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
tag:
|
tag:
|
||||||
name: Create tag
|
name: Create tag
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
OS_NAME: Windows x64
|
OS_NAME: Windows x64
|
||||||
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
DOTNET_RUNTIME_IDENTIFIER: win-x64
|
||||||
RELEASE_ZIP_OS_NAME: win_x64
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@@ -21,16 +21,16 @@
|
|||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="7.0.0" />
|
<PackageVersion Include="NetCoreServer" Version="7.0.0" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
<PackageVersion Include="OpenTK.Core" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.1" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.1" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
@@ -45,10 +45,10 @@
|
|||||||
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageVersion Include="System.Drawing.Common" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.2" />
|
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -68,7 +68,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
|
|||||||
If you wish to build the emulator yourself, follow these steps:
|
If you wish to build the emulator yourself, follow these steps:
|
||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
|
Install the X64 version of [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.games</string>
|
<string>public.app-category.games</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>11.0</string>
|
<string>12.0</string>
|
||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "7.0.200",
|
"version": "8.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -117,12 +117,11 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||||
|
|
||||||
bool result = TryFind(funcOffset, out CacheEntry entry);
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
Debug.Assert(result);
|
{
|
||||||
|
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
Remove(funcOffset);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,22 +180,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Remove(int offset)
|
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||||
{
|
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
_cacheEntries.RemoveAt(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryFind(int offset, out CacheEntry entry)
|
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -210,11 +194,13 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
entry = _cacheEntries[index];
|
entry = _cacheEntries[index];
|
||||||
|
entryIndex = index;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry = default;
|
entry = default;
|
||||||
|
entryIndex = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -95,7 +95,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
int offset = (int)((long)controlPc - context.ToInt64());
|
int offset = (int)((long)controlPc - context.ToInt64());
|
||||||
|
|
||||||
if (!JitCache.TryFind(offset, out CacheEntry funcEntry))
|
if (!JitCache.TryFind(offset, out CacheEntry funcEntry, out _))
|
||||||
{
|
{
|
||||||
return null; // Not found.
|
return null; // Not found.
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.OpenAL" />
|
<PackageReference Include="OpenTK.Audio.OpenAL" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -15,11 +15,11 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dll</TargetPath>
|
<TargetPath>libsoundio.dll</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dylib</TargetPath>
|
<TargetPath>libsoundio.dylib</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win10-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.so</TargetPath>
|
<TargetPath>libsoundio.so</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
|
@@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Utils
|
|||||||
throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null);
|
throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryMarshal.Write(backingMemory.Span[..size], ref data);
|
MemoryMarshal.Write(backingMemory.Span[..size], in data);
|
||||||
|
|
||||||
backingMemory = backingMemory[size..];
|
backingMemory = backingMemory[size..];
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Utils
|
|||||||
throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null);
|
throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryMarshal.Write(backingMemory[..size], ref data);
|
MemoryMarshal.Write(backingMemory[..size], in data);
|
||||||
|
|
||||||
backingMemory = backingMemory[size..];
|
backingMemory = backingMemory[size..];
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -54,6 +54,8 @@ using System.Threading.Tasks;
|
|||||||
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
||||||
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
|
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||||
|
using IRenderer = Ryujinx.Graphics.GAL.IRenderer;
|
||||||
using Key = Ryujinx.Input.Key;
|
using Key = Ryujinx.Input.Key;
|
||||||
using MouseButton = Ryujinx.Input.MouseButton;
|
using MouseButton = Ryujinx.Input.MouseButton;
|
||||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||||
@@ -121,14 +123,12 @@ namespace Ryujinx.Ava
|
|||||||
public int Width { get; private set; }
|
public int Width { get; private set; }
|
||||||
public int Height { get; private set; }
|
public int Height { get; private set; }
|
||||||
public string ApplicationPath { get; private set; }
|
public string ApplicationPath { get; private set; }
|
||||||
public ulong ApplicationId { get; private set; }
|
|
||||||
public bool ScreenshotRequested { get; set; }
|
public bool ScreenshotRequested { get; set; }
|
||||||
|
|
||||||
public AppHost(
|
public AppHost(
|
||||||
RendererHost renderer,
|
RendererHost renderer,
|
||||||
InputManager inputManager,
|
InputManager inputManager,
|
||||||
string applicationPath,
|
string applicationPath,
|
||||||
ulong applicationId,
|
|
||||||
VirtualFileSystem virtualFileSystem,
|
VirtualFileSystem virtualFileSystem,
|
||||||
ContentManager contentManager,
|
ContentManager contentManager,
|
||||||
AccountManager accountManager,
|
AccountManager accountManager,
|
||||||
@@ -152,7 +152,6 @@ namespace Ryujinx.Ava
|
|||||||
NpadManager = _inputManager.CreateNpadManager();
|
NpadManager = _inputManager.CreateNpadManager();
|
||||||
TouchScreenManager = _inputManager.CreateTouchScreenManager();
|
TouchScreenManager = _inputManager.CreateTouchScreenManager();
|
||||||
ApplicationPath = applicationPath;
|
ApplicationPath = applicationPath;
|
||||||
ApplicationId = applicationId;
|
|
||||||
VirtualFileSystem = virtualFileSystem;
|
VirtualFileSystem = virtualFileSystem;
|
||||||
ContentManager = contentManager;
|
ContentManager = contentManager;
|
||||||
|
|
||||||
@@ -642,7 +641,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
|
Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
|
||||||
|
|
||||||
if (!Device.LoadXci(ApplicationPath, ApplicationId))
|
if (!Device.LoadXci(ApplicationPath))
|
||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
@@ -669,7 +668,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
|
Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
|
||||||
|
|
||||||
if (!Device.LoadNsp(ApplicationPath, ApplicationId))
|
if (!Device.LoadNsp(ApplicationPath))
|
||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
|
@@ -539,8 +539,6 @@
|
|||||||
"OpenSetupGuideMessage": "Open the Setup Guide",
|
"OpenSetupGuideMessage": "Open the Setup Guide",
|
||||||
"NoUpdate": "No Update",
|
"NoUpdate": "No Update",
|
||||||
"TitleUpdateVersionLabel": "Version {0}",
|
"TitleUpdateVersionLabel": "Version {0}",
|
||||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
|
||||||
"TitleBundledDlcLabel": "Bundled:",
|
|
||||||
"RyujinxInfo": "Ryujinx - Info",
|
"RyujinxInfo": "Ryujinx - Info",
|
||||||
"RyujinxConfirm": "Ryujinx - Confirmation",
|
"RyujinxConfirm": "Ryujinx - Confirmation",
|
||||||
"FileDialogAllTypes": "All types",
|
"FileDialogAllTypes": "All types",
|
||||||
|
@@ -18,8 +18,7 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
@@ -227,11 +226,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
||||||
? IntegrityCheckLevel.ErrorOnInvalid
|
|
||||||
: IntegrityCheckLevel.None;
|
|
||||||
|
|
||||||
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
|
|
||||||
if (updatePatchNca != null)
|
if (updatePatchNca != null)
|
||||||
{
|
{
|
||||||
patchNca = updatePatchNca;
|
patchNca = updatePatchNca;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Version>1.0.0-dirty</Version>
|
<Version>1.0.0-dirty</Version>
|
||||||
@@ -25,6 +25,16 @@
|
|||||||
<TrimMode>partial</TrimMode>
|
<TrimMode>partial</TrimMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
|
||||||
|
See:
|
||||||
|
https://github.com/amwx/FluentAvalonia/issues/481
|
||||||
|
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
|
||||||
|
-->
|
||||||
|
<PropertyGroup>
|
||||||
|
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" />
|
<PackageReference Include="Avalonia" />
|
||||||
<PackageReference Include="Avalonia.Desktop" />
|
<PackageReference Include="Avalonia.Desktop" />
|
||||||
@@ -40,7 +50,7 @@
|
|||||||
<PackageReference Include="OpenTK.Core" />
|
<PackageReference Include="OpenTK.Core" />
|
||||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" />
|
<PackageReference Include="Silk.NET.Vulkan" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Threading;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
@@ -14,6 +15,7 @@ using Ryujinx.Ui.App.Common;
|
|||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite;
|
viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite;
|
||||||
|
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.IdString, appMetadata =>
|
ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata =>
|
||||||
{
|
{
|
||||||
appMetadata.Favorite = viewModel.SelectedApplication.Favorite;
|
appMetadata.Favorite = viewModel.SelectedApplication.Favorite;
|
||||||
});
|
});
|
||||||
@@ -74,9 +76,19 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
var saveDataFilter = SaveDataFilter.Make(viewModel.SelectedApplication.Id, saveDataType, userId, saveDataId: default, index: default);
|
if (!ulong.TryParse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
||||||
|
});
|
||||||
|
|
||||||
ApplicationHelper.OpenSaveDir(in saveDataFilter, viewModel.SelectedApplication.Id, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.Name);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
|
||||||
|
|
||||||
|
ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +98,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication);
|
await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +108,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication);
|
await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +120,8 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
await new CheatWindow(
|
await new CheatWindow(
|
||||||
viewModel.VirtualFileSystem,
|
viewModel.VirtualFileSystem,
|
||||||
viewModel.SelectedApplication.IdString,
|
viewModel.SelectedApplication.TitleId,
|
||||||
viewModel.SelectedApplication.Name,
|
viewModel.SelectedApplication.TitleName,
|
||||||
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
|
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +133,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
string modsBasePath = ModLoader.GetModsBasePath();
|
string modsBasePath = ModLoader.GetModsBasePath();
|
||||||
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.IdString);
|
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId);
|
||||||
|
|
||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
@@ -134,7 +146,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
string sdModsBasePath = ModLoader.GetSdModsBasePath();
|
string sdModsBasePath = ModLoader.GetSdModsBasePath();
|
||||||
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.IdString);
|
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId);
|
||||||
|
|
||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
@@ -148,15 +160,15 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.Name),
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0"));
|
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "0"));
|
||||||
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1"));
|
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "1"));
|
||||||
|
|
||||||
List<FileInfo> cacheFiles = new();
|
List<FileInfo> cacheFiles = new();
|
||||||
|
|
||||||
@@ -196,14 +208,14 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.Name),
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader"));
|
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"));
|
||||||
|
|
||||||
List<DirectoryInfo> oldCacheDirectories = new();
|
List<DirectoryInfo> oldCacheDirectories = new();
|
||||||
List<FileInfo> newCacheFiles = new();
|
List<FileInfo> newCacheFiles = new();
|
||||||
@@ -251,7 +263,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu");
|
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu");
|
||||||
string mainDir = Path.Combine(ptcDir, "0");
|
string mainDir = Path.Combine(ptcDir, "0");
|
||||||
string backupDir = Path.Combine(ptcDir, "1");
|
string backupDir = Path.Combine(ptcDir, "1");
|
||||||
|
|
||||||
@@ -272,7 +284,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader");
|
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader");
|
||||||
|
|
||||||
if (!Directory.Exists(shaderCacheDir))
|
if (!Directory.Exists(shaderCacheDir))
|
||||||
{
|
{
|
||||||
@@ -293,7 +305,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
viewModel.StorageProvider,
|
viewModel.StorageProvider,
|
||||||
NcaSectionType.Code,
|
NcaSectionType.Code,
|
||||||
viewModel.SelectedApplication.Path,
|
viewModel.SelectedApplication.Path,
|
||||||
viewModel.SelectedApplication.Name);
|
viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +319,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
viewModel.StorageProvider,
|
viewModel.StorageProvider,
|
||||||
NcaSectionType.Data,
|
NcaSectionType.Data,
|
||||||
viewModel.SelectedApplication.Path,
|
viewModel.SelectedApplication.Path,
|
||||||
viewModel.SelectedApplication.Name);
|
viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +333,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
viewModel.StorageProvider,
|
viewModel.StorageProvider,
|
||||||
NcaSectionType.Logo,
|
NcaSectionType.Logo,
|
||||||
viewModel.SelectedApplication.Path,
|
viewModel.SelectedApplication.Path,
|
||||||
viewModel.SelectedApplication.Name);
|
viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +344,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
ApplicationData selectedApplication = viewModel.SelectedApplication;
|
ApplicationData selectedApplication = viewModel.SelectedApplication;
|
||||||
ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.Name, selectedApplication.IdString, selectedApplication.Icon);
|
ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.TitleName, selectedApplication.TitleId, selectedApplication.Icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +354,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
await viewModel.LoadApplication(viewModel.SelectedApplication);
|
await viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -82,7 +82,7 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{Binding Name}"
|
Text="{Binding TitleName}"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
</Panel>
|
</Panel>
|
||||||
|
@@ -85,7 +85,7 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Text="{Binding Name}"
|
Text="{Binding TitleName}"
|
||||||
TextAlignment="Left"
|
TextAlignment="Left"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
Spacing="5">
|
Spacing="5">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Text="{Binding Id, StringFormat=X16}"
|
Text="{Binding TitleId}"
|
||||||
TextAlignment="Left"
|
TextAlignment="Left"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
public static partial IntPtr SetCursor(IntPtr handle);
|
public static partial IntPtr SetCursor(IntPtr handle);
|
||||||
|
|
||||||
[LibraryImport("user32.dll")]
|
[LibraryImport("user32.dll")]
|
||||||
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvAndPlane, byte[] pvXorPlane);
|
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, [In] byte[] pvAndPlane, [In] byte[] pvXorPlane);
|
||||||
|
|
||||||
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
|
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
|
||||||
public static partial ushort RegisterClassEx(ref WndClassEx param);
|
public static partial ushort RegisterClassEx(ref WndClassEx param);
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
namespace Ryujinx.Ava.UI.Models
|
||||||
@@ -25,9 +24,6 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
|
|
||||||
public string FileName => Path.GetFileName(ContainerPath);
|
public string FileName => Path.GetFileName(ContainerPath);
|
||||||
|
|
||||||
public string Label =>
|
|
||||||
Path.GetExtension(FileName)?.ToLower() == ".xci" ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {FileName}" : FileName;
|
|
||||||
|
|
||||||
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
|
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
|
||||||
{
|
{
|
||||||
TitleId = titleId;
|
TitleId = titleId;
|
||||||
|
@@ -46,14 +46,14 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
TitleId = info.ProgramId;
|
TitleId = info.ProgramId;
|
||||||
UserId = info.UserId;
|
UserId = info.UserId;
|
||||||
|
|
||||||
var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.IdString.ToUpper() == TitleIdString);
|
var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.TitleId.ToUpper() == TitleIdString);
|
||||||
|
|
||||||
InGameList = appData != null;
|
InGameList = appData != null;
|
||||||
|
|
||||||
if (InGameList)
|
if (InGameList)
|
||||||
{
|
{
|
||||||
Icon = appData.Icon;
|
Icon = appData.Icon;
|
||||||
Title = appData.Name;
|
Title = appData.TitleName;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -8,10 +8,7 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
public ApplicationControlProperty Control { get; }
|
public ApplicationControlProperty Control { get; }
|
||||||
public string Path { get; }
|
public string Path { get; }
|
||||||
|
|
||||||
public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(
|
public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleUpdateVersionLabel, Control.DisplayVersionString.ToString());
|
||||||
System.IO.Path.GetExtension(Path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
|
|
||||||
Control.DisplayVersionString.ToString()
|
|
||||||
);
|
|
||||||
|
|
||||||
public TitleUpdateModel(ApplicationControlProperty control, string path)
|
public TitleUpdateModel(ApplicationControlProperty control, string path)
|
||||||
{
|
{
|
||||||
|
@@ -17,12 +17,11 @@ using Ryujinx.Common.Configuration;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Application = Avalonia.Application;
|
using Application = Avalonia.Application;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
|
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
|
||||||
|
|
||||||
private string _search;
|
private string _search;
|
||||||
private readonly ApplicationData _applicationData;
|
private readonly ulong _titleId;
|
||||||
|
|
||||||
private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
@@ -93,25 +92,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public IStorageProvider StorageProvider;
|
public IStorageProvider StorageProvider;
|
||||||
|
|
||||||
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
_applicationData = applicationData;
|
_titleId = titleId;
|
||||||
|
|
||||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
StorageProvider = desktop.MainWindow.StorageProvider;
|
StorageProvider = desktop.MainWindow.StorageProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "dlc.json");
|
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
|
||||||
|
|
||||||
if (!File.Exists(_downloadableContentJsonPath))
|
|
||||||
{
|
|
||||||
_downloadableContentContainerList = new List<DownloadableContentContainer>();
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -128,9 +120,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private void LoadDownloadableContents()
|
private void LoadDownloadableContents()
|
||||||
{
|
{
|
||||||
// NOTE: Try to load downloadable contents from PFS first.
|
|
||||||
AddDownloadableContent(_applicationData.Path);
|
|
||||||
|
|
||||||
foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
|
foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
|
||||||
{
|
{
|
||||||
if (File.Exists(downloadableContentContainer.ContainerPath))
|
if (File.Exists(downloadableContentContainer.ContainerPath))
|
||||||
@@ -138,11 +127,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
|
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
|
||||||
|
|
||||||
PartitionFileSystem partitionFileSystem = new();
|
PartitionFileSystem partitionFileSystem = new();
|
||||||
|
partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
|
||||||
if (partitionFileSystem.Initialize(containerFile.AsStorage()).IsFailure())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
||||||
|
|
||||||
@@ -235,34 +220,22 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
foreach (var file in result)
|
foreach (var file in result)
|
||||||
{
|
{
|
||||||
if (!AddDownloadableContent(file.Path.LocalPath))
|
await AddDownloadableContent(file.Path.LocalPath);
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AddDownloadableContent(string path)
|
private async Task AddDownloadableContent(string path)
|
||||||
{
|
{
|
||||||
if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
|
if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
|
||||||
{
|
{
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using FileStream containerFile = File.OpenRead(path);
|
using FileStream containerFile = File.OpenRead(path);
|
||||||
|
|
||||||
IFileSystem partitionFileSystem;
|
PartitionFileSystem partitionFileSystem = new();
|
||||||
|
partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
|
||||||
if (Path.GetExtension(path).ToLower() == ".xci")
|
bool containsDownloadableContent = false;
|
||||||
{
|
|
||||||
partitionFileSystem = new Xci(_virtualFileSystem.KeySet, containerFile.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var pfsTemp = new PartitionFileSystem();
|
|
||||||
pfsTemp.Initialize(containerFile.AsStorage()).ThrowIfFailure();
|
|
||||||
partitionFileSystem = pfsTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
||||||
|
|
||||||
@@ -280,7 +253,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (nca.Header.ContentType == NcaContentType.PublicData)
|
if (nca.Header.ContentType == NcaContentType.PublicData)
|
||||||
{
|
{
|
||||||
if (nca.GetProgramIdBase() != _applicationData.IdBase)
|
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -292,11 +265,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
OnPropertyChanged(nameof(UpdateCount));
|
OnPropertyChanged(nameof(UpdateCount));
|
||||||
Sort();
|
Sort();
|
||||||
|
|
||||||
return true;
|
containsDownloadableContent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (!containsDownloadableContent)
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(DownloadableContentModel model)
|
public void Remove(DownloadableContentModel model)
|
||||||
|
@@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _canUpdate = true;
|
private bool _canUpdate = true;
|
||||||
private Cursor _cursor;
|
private Cursor _cursor;
|
||||||
private string _title;
|
private string _title;
|
||||||
private ApplicationData _currentApplicationData;
|
private string _currentEmulatedGamePath;
|
||||||
private readonly AutoResetEvent _rendererWaitEvent;
|
private readonly AutoResetEvent _rendererWaitEvent;
|
||||||
private WindowState _windowState;
|
private WindowState _windowState;
|
||||||
private double _windowWidth;
|
private double _windowWidth;
|
||||||
@@ -106,6 +106,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public ApplicationData ListSelectedApplication;
|
public ApplicationData ListSelectedApplication;
|
||||||
public ApplicationData GridSelectedApplication;
|
public ApplicationData GridSelectedApplication;
|
||||||
|
|
||||||
|
private string TitleName { get; set; }
|
||||||
internal AppHost AppHost { get; set; }
|
internal AppHost AppHost { get; set; }
|
||||||
|
|
||||||
public MainWindowViewModel()
|
public MainWindowViewModel()
|
||||||
@@ -929,8 +930,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return SortMode switch
|
return SortMode switch
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE0055 // Disable formatting
|
#pragma warning disable IDE0055 // Disable formatting
|
||||||
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Name)
|
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
|
||||||
: SortExpressionComparer<ApplicationData>.Descending(app => app.Name),
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
|
||||||
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
|
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
|
||||||
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
|
||||||
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
|
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
|
||||||
@@ -967,7 +968,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (arg is ApplicationData app)
|
if (arg is ApplicationData app)
|
||||||
{
|
{
|
||||||
return string.IsNullOrWhiteSpace(_searchText) || app.Name.ToLower().Contains(_searchText.ToLower());
|
return string.IsNullOrWhiteSpace(_searchText) || app.TitleName.ToLower().Contains(_searchText.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1096,7 +1097,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
IsLoadingIndeterminate = false;
|
IsLoadingIndeterminate = false;
|
||||||
break;
|
break;
|
||||||
case LoadState.Loaded:
|
case LoadState.Loaded:
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||||
IsLoadingIndeterminate = true;
|
IsLoadingIndeterminate = true;
|
||||||
CacheLoadStatus = "";
|
CacheLoadStatus = "";
|
||||||
break;
|
break;
|
||||||
@@ -1116,7 +1117,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
IsLoadingIndeterminate = false;
|
IsLoadingIndeterminate = false;
|
||||||
break;
|
break;
|
||||||
case ShaderCacheLoadingState.Loaded:
|
case ShaderCacheLoadingState.Loaded:
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||||
IsLoadingIndeterminate = true;
|
IsLoadingIndeterminate = true;
|
||||||
CacheLoadStatus = "";
|
CacheLoadStatus = "";
|
||||||
break;
|
break;
|
||||||
@@ -1167,13 +1168,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
UserChannelPersistence.ShouldRestart = false;
|
UserChannelPersistence.ShouldRestart = false;
|
||||||
|
|
||||||
await LoadApplication(_currentApplicationData);
|
await LoadApplication(_currentEmulatedGamePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Otherwise, clear state.
|
// Otherwise, clear state.
|
||||||
UserChannelPersistence = new UserChannelPersistence();
|
UserChannelPersistence = new UserChannelPersistence();
|
||||||
_currentApplicationData = null;
|
_currentEmulatedGamePath = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1450,12 +1451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
{
|
{
|
||||||
ApplicationData applicationData = new()
|
await LoadApplication(result[0].Path.LocalPath);
|
||||||
{
|
|
||||||
Path = result[0].Path.LocalPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await LoadApplication(applicationData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1469,17 +1465,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
{
|
{
|
||||||
ApplicationData applicationData = new()
|
await LoadApplication(result[0].Path.LocalPath);
|
||||||
{
|
|
||||||
Name = Path.GetFileNameWithoutExtension(result[0].Path.LocalPath),
|
|
||||||
Path = result[0].Path.LocalPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await LoadApplication(applicationData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false)
|
public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||||
{
|
{
|
||||||
if (AppHost != null)
|
if (AppHost != null)
|
||||||
{
|
{
|
||||||
@@ -1499,7 +1489,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
Logger.RestartTime();
|
Logger.RestartTime();
|
||||||
|
|
||||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
|
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
|
||||||
|
|
||||||
PrepareLoadScreen();
|
PrepareLoadScreen();
|
||||||
|
|
||||||
@@ -1508,8 +1498,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AppHost = new AppHost(
|
AppHost = new AppHost(
|
||||||
RendererHostControl,
|
RendererHostControl,
|
||||||
InputManager,
|
InputManager,
|
||||||
application.Path,
|
path,
|
||||||
application.Id,
|
|
||||||
VirtualFileSystem,
|
VirtualFileSystem,
|
||||||
ContentManager,
|
ContentManager,
|
||||||
AccountManager,
|
AccountManager,
|
||||||
@@ -1527,17 +1516,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
CanUpdate = false;
|
CanUpdate = false;
|
||||||
|
|
||||||
LoadHeading = application.Name;
|
LoadHeading = TitleName = titleName;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(application.Name))
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
{
|
{
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||||
application.Name = AppHost.Device.Processes.ActiveApplication.Name;
|
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchToRenderer(startFullscreen);
|
SwitchToRenderer(startFullscreen);
|
||||||
|
|
||||||
_currentApplicationData = application;
|
_currentEmulatedGamePath = path;
|
||||||
|
|
||||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||||
gameThread.Start();
|
gameThread.Start();
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using Avalonia;
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
@@ -7,7 +8,6 @@ using LibHac.Fs;
|
|||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using LibHac.Tools.Fs;
|
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
@@ -17,16 +17,12 @@ using Ryujinx.Common.Configuration;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
|
||||||
using Ryujinx.Ui.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Application = Avalonia.Application;
|
|
||||||
using ContentType = LibHac.Ncm.ContentType;
|
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
@@ -37,7 +33,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public TitleUpdateMetadata TitleUpdateWindowData;
|
public TitleUpdateMetadata TitleUpdateWindowData;
|
||||||
public readonly string TitleUpdateJsonPath;
|
public readonly string TitleUpdateJsonPath;
|
||||||
private VirtualFileSystem VirtualFileSystem { get; }
|
private VirtualFileSystem VirtualFileSystem { get; }
|
||||||
private ApplicationData ApplicationData { get; }
|
private ulong TitleId { get; }
|
||||||
|
|
||||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||||
private AvaloniaList<object> _views = new();
|
private AvaloniaList<object> _views = new();
|
||||||
@@ -77,18 +73,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public IStorageProvider StorageProvider;
|
public IStorageProvider StorageProvider;
|
||||||
|
|
||||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
|
||||||
{
|
{
|
||||||
VirtualFileSystem = virtualFileSystem;
|
VirtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
ApplicationData = applicationData;
|
TitleId = titleId;
|
||||||
|
|
||||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
StorageProvider = desktop.MainWindow.StorageProvider;
|
StorageProvider = desktop.MainWindow.StorageProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdString, "updates.json");
|
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -96,7 +92,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdString} at {TitleUpdateJsonPath}");
|
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}");
|
||||||
|
|
||||||
TitleUpdateWindowData = new TitleUpdateMetadata
|
TitleUpdateWindowData = new TitleUpdateMetadata
|
||||||
{
|
{
|
||||||
@@ -112,9 +108,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private void LoadUpdates()
|
private void LoadUpdates()
|
||||||
{
|
{
|
||||||
// Try to load updates from PFS first
|
|
||||||
AddUpdate(ApplicationData.Path, true);
|
|
||||||
|
|
||||||
foreach (string path in TitleUpdateWindowData.Paths)
|
foreach (string path in TitleUpdateWindowData.Paths)
|
||||||
{
|
{
|
||||||
AddUpdate(path);
|
AddUpdate(path);
|
||||||
@@ -169,41 +162,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddUpdate(string path, bool ignoreNotFound = false)
|
private void AddUpdate(string path)
|
||||||
{
|
{
|
||||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
||||||
{
|
{
|
||||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
|
||||||
? IntegrityCheckLevel.ErrorOnInvalid
|
|
||||||
: IntegrityCheckLevel.None;
|
|
||||||
|
|
||||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
IFileSystem pfs;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(path).ToLower() == ".xci")
|
var pfs = new PartitionFileSystem();
|
||||||
{
|
pfs.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||||
pfs = new Xci(VirtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, pfs, TitleId.ToString("x16"), 0);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var pfsTemp = new PartitionFileSystem();
|
|
||||||
pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
|
|
||||||
pfs = pfsTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<ulong, ContentCollection> updates = pfs.GetUpdateData(VirtualFileSystem, checkLevel);
|
|
||||||
|
|
||||||
Nca patchNca = null;
|
|
||||||
Nca controlNca = null;
|
|
||||||
|
|
||||||
if (updates.TryGetValue(ApplicationData.Id, out ContentCollection content))
|
|
||||||
{
|
|
||||||
patchNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Program);
|
|
||||||
controlNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Control);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
if (controlNca != null && patchNca != null)
|
||||||
{
|
{
|
||||||
@@ -217,13 +186,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (!ignoreNotFound)
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)));
|
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)));
|
||||||
|
@@ -10,7 +10,6 @@ using Ryujinx.Ava.UI.Windows;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
@@ -132,14 +131,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(contentPath))
|
if (!string.IsNullOrEmpty(contentPath))
|
||||||
{
|
{
|
||||||
ApplicationData applicationData = new()
|
await ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
||||||
{
|
|
||||||
Name = "miiEdit",
|
|
||||||
Id = 0x0100000000001009,
|
|
||||||
Path = contentPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -104,7 +104,7 @@
|
|||||||
Content="{locale:Locale GameListHeaderApplication}"
|
Content="{locale:Locale GameListHeaderApplication}"
|
||||||
GroupName="Sort"
|
GroupName="Sort"
|
||||||
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
||||||
Tag="Application" />
|
Tag="Title" />
|
||||||
<RadioButton
|
<RadioButton
|
||||||
Checked="Sort_Checked"
|
Checked="Sort_Checked"
|
||||||
Content="{locale:Locale GameListHeaderDeveloper}"
|
Content="{locale:Locale GameListHeaderDeveloper}"
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using LibHac.Tools.FsSystem;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.Ui.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -36,12 +34,9 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
|
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
|
||||||
{
|
{
|
||||||
LoadedCheats = new AvaloniaList<CheatsList>();
|
LoadedCheats = new AvaloniaList<CheatsList>();
|
||||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
|
||||||
? IntegrityCheckLevel.ErrorOnInvalid
|
|
||||||
: IntegrityCheckLevel.None;
|
|
||||||
|
|
||||||
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
|
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
|
||||||
BuildId = ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath);
|
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
@@ -97,7 +97,7 @@
|
|||||||
MaxLines="2"
|
MaxLines="2"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
TextTrimming="CharacterEllipsis"
|
TextTrimming="CharacterEllipsis"
|
||||||
Text="{Binding Label}" />
|
Text="{Binding FileName}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="10 0"
|
Margin="10 0"
|
||||||
|
@@ -7,9 +7,9 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Button = Avalonia.Controls.Button;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
{
|
{
|
||||||
@@ -24,22 +24,22 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
|
||||||
{
|
{
|
||||||
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, applicationData);
|
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
{
|
{
|
||||||
ContentDialog contentDialog = new()
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
PrimaryButtonText = "",
|
PrimaryButtonText = "",
|
||||||
SecondaryButtonText = "",
|
SecondaryButtonText = "",
|
||||||
CloseButtonText = "",
|
CloseButtonText = "",
|
||||||
Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData),
|
Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId),
|
||||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdString),
|
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
||||||
|
@@ -4,7 +4,6 @@ using Avalonia.Controls.Primitives;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using LibHac.Tools.FsSystem;
|
|
||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
@@ -24,6 +23,7 @@ using Ryujinx.Ui.Common;
|
|||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -139,7 +139,9 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
ViewModel.SelectedIcon = args.Application.Icon;
|
ViewModel.SelectedIcon = args.Application.Icon;
|
||||||
|
|
||||||
ViewModel.LoadApplication(args.Application).Wait();
|
string path = new FileInfo(args.Application.Path).FullName;
|
||||||
|
|
||||||
|
ViewModel.LoadApplication(path).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -188,11 +190,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
LibHacHorizonManager.InitializeBcatServer();
|
LibHacHorizonManager.InitializeBcatServer();
|
||||||
LibHacHorizonManager.InitializeSystemClients();
|
LibHacHorizonManager.InitializeSystemClients();
|
||||||
|
|
||||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem);
|
||||||
? IntegrityCheckLevel.ErrorOnInvalid
|
|
||||||
: IntegrityCheckLevel.None;
|
|
||||||
|
|
||||||
ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem, checkLevel);
|
|
||||||
|
|
||||||
// Save data created before we supported extra data in directory save data will not work properly if
|
// Save data created before we supported extra data in directory save data will not work properly if
|
||||||
// given empty extra data. Luckily some of that extra data can be created using the data from the
|
// given empty extra data. Luckily some of that extra data can be created using the data from the
|
||||||
@@ -299,12 +297,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
_deferLoad = false;
|
_deferLoad = false;
|
||||||
|
|
||||||
ApplicationData applicationData = new()
|
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
|
||||||
{
|
|
||||||
Path = _launchPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
ViewModel.LoadApplication(applicationData, _startFullscreen).Wait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -7,15 +7,15 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Button = Avalonia.Controls.Button;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
{
|
{
|
||||||
public partial class TitleUpdateWindow : UserControl
|
public partial class TitleUpdateWindow : UserControl
|
||||||
{
|
{
|
||||||
public readonly TitleUpdateViewModel ViewModel;
|
public TitleUpdateViewModel ViewModel;
|
||||||
|
|
||||||
public TitleUpdateWindow()
|
public TitleUpdateWindow()
|
||||||
{
|
{
|
||||||
@@ -24,22 +24,22 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
|
||||||
{
|
{
|
||||||
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, applicationData);
|
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
{
|
{
|
||||||
ContentDialog contentDialog = new()
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
PrimaryButtonText = "",
|
PrimaryButtonText = "",
|
||||||
SecondaryButtonText = "",
|
SecondaryButtonText = "",
|
||||||
CloseButtonText = "",
|
CloseButtonText = "",
|
||||||
Content = new TitleUpdateWindow(virtualFileSystem, applicationData),
|
Content = new TitleUpdateWindow(virtualFileSystem, titleId),
|
||||||
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdString),
|
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
||||||
|
@@ -48,7 +48,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
string appDataPath;
|
string appDataPath;
|
||||||
if (OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support");
|
appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
@@ -211,6 +211,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
int xCount = (int)_state.State.LineLengthIn;
|
int xCount = (int)_state.State.LineLengthIn;
|
||||||
int yCount = (int)_state.State.LineCount;
|
int yCount = (int)_state.State.LineCount;
|
||||||
|
|
||||||
|
_channel.TextureManager.RefreshModifiedTextures();
|
||||||
_3dEngine.CreatePendingSyncs();
|
_3dEngine.CreatePendingSyncs();
|
||||||
_3dEngine.FlushUboDirty();
|
_3dEngine.FlushUboDirty();
|
||||||
|
|
||||||
@@ -279,7 +280,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
|
bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
|
||||||
bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
|
bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
|
||||||
|
|
||||||
if (completeSource && completeDest)
|
// Try to set the texture data directly,
|
||||||
|
// but only if we are doing a complete copy,
|
||||||
|
// and not for block linear to linear copies, since those are typically accessed from the CPU.
|
||||||
|
|
||||||
|
if (completeSource && completeDest && !(dstLinear && !srcLinear))
|
||||||
{
|
{
|
||||||
var target = memoryManager.Physical.TextureCache.FindTexture(
|
var target = memoryManager.Physical.TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
|
@@ -651,9 +651,35 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>True if the format is valid, false otherwise</returns>
|
/// <returns>True if the format is valid, false otherwise</returns>
|
||||||
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
|
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
|
||||||
{
|
{
|
||||||
encoded |= (isSrgb ? 1u << 19 : 0u);
|
bool isPacked = (encoded & 0x80000000u) != 0;
|
||||||
|
if (isPacked)
|
||||||
|
{
|
||||||
|
encoded &= ~0x80000000u;
|
||||||
|
}
|
||||||
|
|
||||||
return _textureFormats.TryGetValue((TextureFormat)encoded, out format);
|
encoded |= isSrgb ? 1u << 19 : 0u;
|
||||||
|
|
||||||
|
bool found = _textureFormats.TryGetValue((TextureFormat)encoded, out format);
|
||||||
|
|
||||||
|
if (found && isPacked && !format.Format.IsDepthOrStencil())
|
||||||
|
{
|
||||||
|
// If the packed flag is set, then the components of the pixel are tightly packed into the
|
||||||
|
// GPU registers on the shader.
|
||||||
|
// We can get the same behaviour by aliasing the texture as a format with the same amount of
|
||||||
|
// bytes per pixel, but only a single or the lowest possible number of components.
|
||||||
|
|
||||||
|
format = format.BytesPerPixel switch
|
||||||
|
{
|
||||||
|
1 => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1),
|
||||||
|
2 => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1),
|
||||||
|
4 => new FormatInfo(Format.R32Float, 1, 1, 4, 1),
|
||||||
|
8 => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2),
|
||||||
|
16 => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
|
||||||
|
_ => format,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -101,11 +101,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AlwaysFlushOnOverlap { get; private set; }
|
public bool AlwaysFlushOnOverlap { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates that the texture was fully unmapped since the modified flag was set, and flushes should be ignored until it is modified again.
|
|
||||||
/// </summary>
|
|
||||||
public bool FlushStale { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Increments when the host texture is swapped, or when the texture is removed from all pools.
|
/// Increments when the host texture is swapped, or when the texture is removed from all pools.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1417,7 +1412,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SignalModified()
|
public void SignalModified()
|
||||||
{
|
{
|
||||||
FlushStale = false;
|
|
||||||
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
||||||
|
|
||||||
if (_modifiedStale || Group.HasCopyDependencies)
|
if (_modifiedStale || Group.HasCopyDependencies)
|
||||||
@@ -1438,7 +1432,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
if (bound)
|
if (bound)
|
||||||
{
|
{
|
||||||
FlushStale = false;
|
|
||||||
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1703,12 +1696,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="unmapRange">The range of memory being unmapped</param>
|
/// <param name="unmapRange">The range of memory being unmapped</param>
|
||||||
public void Unmapped(MultiRange unmapRange)
|
public void Unmapped(MultiRange unmapRange)
|
||||||
{
|
{
|
||||||
if (unmapRange.Contains(Range))
|
|
||||||
{
|
|
||||||
// If this is a full unmap, prevent flushes until the texture is mapped again.
|
|
||||||
FlushStale = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangedMapping = true;
|
ChangedMapping = true;
|
||||||
|
|
||||||
if (Group.Storage == this)
|
if (Group.Storage == this)
|
||||||
|
@@ -2,6 +2,8 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@@ -339,7 +341,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
|
if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
|
||||||
{
|
{
|
||||||
alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel;
|
// If the formats are incompatible, but the texture strides match,
|
||||||
|
// we might allow them to be copy compatible depending on the format.
|
||||||
|
// The strides are aligned because the format with higher bytes per pixel
|
||||||
|
// might need a bit of padding at the end due to one width not being a multiple of the other.
|
||||||
|
|
||||||
|
Debug.Assert((1 << BitOperations.Log2((uint)lhs.FormatInfo.BytesPerPixel)) == lhs.FormatInfo.BytesPerPixel);
|
||||||
|
Debug.Assert((1 << BitOperations.Log2((uint)rhs.FormatInfo.BytesPerPixel)) == rhs.FormatInfo.BytesPerPixel);
|
||||||
|
|
||||||
|
int alignment = Math.Max(lhs.FormatInfo.BytesPerPixel, rhs.FormatInfo.BytesPerPixel);
|
||||||
|
|
||||||
|
int lhsStride = BitUtils.AlignUp(lhsSize.Width * lhs.FormatInfo.BytesPerPixel, alignment);
|
||||||
|
int rhsStride = BitUtils.AlignUp(rhsSize.Width * rhs.FormatInfo.BytesPerPixel, alignment);
|
||||||
|
|
||||||
|
alignedWidthMatches = lhsStride == rhsStride;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
@@ -718,7 +733,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
(lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
|
(lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm;
|
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
|
||||||
|
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -992,26 +992,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The action to perform when a memory tracking handle is flipped to dirty.
|
|
||||||
/// This notifies overlapping textures that the memory needs to be synchronized.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="groupHandle">The handle that a dirty flag was set on</param>
|
|
||||||
private void DirtyAction(TextureGroupHandle groupHandle)
|
|
||||||
{
|
|
||||||
// Notify all textures that belong to this handle.
|
|
||||||
|
|
||||||
Storage.SignalGroupDirty();
|
|
||||||
|
|
||||||
lock (groupHandle.Overlaps)
|
|
||||||
{
|
|
||||||
foreach (Texture overlap in groupHandle.Overlaps)
|
|
||||||
{
|
|
||||||
overlap.SignalGroupDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a CpuRegionHandle for a given address and size range in CPU VA.
|
/// Generate a CpuRegionHandle for a given address and size range in CPU VA.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1083,11 +1063,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
views,
|
views,
|
||||||
result.ToArray());
|
result.ToArray());
|
||||||
|
|
||||||
foreach (RegionHandle handle in result)
|
|
||||||
{
|
|
||||||
handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupHandle;
|
return groupHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1359,11 +1334,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
|
var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
|
||||||
|
|
||||||
foreach (RegionHandle handle in cpuRegionHandles)
|
|
||||||
{
|
|
||||||
handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
|
|
||||||
}
|
|
||||||
|
|
||||||
handles = new TextureGroupHandle[] { groupHandle };
|
handles = new TextureGroupHandle[] { groupHandle };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1619,6 +1589,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if ((ignore == null || !handle.HasDependencyTo(ignore)) && handle.Modified)
|
if ((ignore == null || !handle.HasDependencyTo(ignore)) && handle.Modified)
|
||||||
{
|
{
|
||||||
handle.Modified = false;
|
handle.Modified = false;
|
||||||
|
handle.DeferredCopy = null;
|
||||||
Storage.SignalModifiedDirty();
|
Storage.SignalModifiedDirty();
|
||||||
|
|
||||||
lock (handle.Overlaps)
|
lock (handle.Overlaps)
|
||||||
@@ -1660,9 +1631,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If size is zero, we have nothing to flush.
|
// If size is zero, we have nothing to flush.
|
||||||
// If the flush is stale, we should ignore it because the texture was unmapped since the modified
|
if (size == 0)
|
||||||
// flag was set, and flushing it is not safe anymore as the GPU might no longer own the memory.
|
|
||||||
if (size == 0 || Storage.FlushStale)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -152,6 +152,32 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Linear textures are presumed to be used for readback initially.
|
// Linear textures are presumed to be used for readback initially.
|
||||||
_flushBalance = FlushBalanceThreshold + FlushBalanceIncrement;
|
_flushBalance = FlushBalanceThreshold + FlushBalanceIncrement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (RegionHandle handle in handles)
|
||||||
|
{
|
||||||
|
handle.RegisterDirtyEvent(DirtyAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The action to perform when a memory tracking handle is flipped to dirty.
|
||||||
|
/// This notifies overlapping textures that the memory needs to be synchronized.
|
||||||
|
/// </summary>
|
||||||
|
private void DirtyAction()
|
||||||
|
{
|
||||||
|
// Notify all textures that belong to this handle.
|
||||||
|
|
||||||
|
_group.Storage.SignalGroupDirty();
|
||||||
|
|
||||||
|
lock (Overlaps)
|
||||||
|
{
|
||||||
|
foreach (Texture overlap in Overlaps)
|
||||||
|
{
|
||||||
|
overlap.SignalGroupDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeferredCopy = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -449,7 +475,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public void DeferCopy(TextureGroupHandle copyFrom)
|
public void DeferCopy(TextureGroupHandle copyFrom)
|
||||||
{
|
{
|
||||||
Modified = false;
|
Modified = false;
|
||||||
|
|
||||||
DeferredCopy = copyFrom;
|
DeferredCopy = copyFrom;
|
||||||
|
|
||||||
_group.Storage.SignalGroupDirty();
|
_group.Storage.SignalGroupDirty();
|
||||||
@@ -506,7 +531,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
existing.Other.Handle.CreateCopyDependency(this);
|
existing.Other.Handle.CreateCopyDependency(this);
|
||||||
|
|
||||||
if (copyToOther)
|
if (copyToOther && Modified)
|
||||||
{
|
{
|
||||||
existing.Other.Handle.DeferCopy(this);
|
existing.Other.Handle.DeferCopy(this);
|
||||||
}
|
}
|
||||||
@@ -550,10 +575,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (fromHandle != null)
|
if (fromHandle != null)
|
||||||
{
|
{
|
||||||
// Only copy if the copy texture is still modified.
|
// Only copy if the copy texture is still modified.
|
||||||
// It will be set as unmodified if new data is written from CPU, as the data previously in the texture will flush.
|
// DeferredCopy will be set to null if new data is written from CPU (see the DirtyAction method).
|
||||||
// It will also set as unmodified if a copy is deferred to it.
|
// It will also set as unmodified if a copy is deferred to it.
|
||||||
|
|
||||||
shouldCopy = fromHandle.Modified;
|
shouldCopy = true;
|
||||||
|
|
||||||
if (fromHandle._bindCount == 0)
|
if (fromHandle._bindCount == 0)
|
||||||
{
|
{
|
||||||
|
@@ -20,8 +20,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
private readonly Texture[] _rtColors;
|
private readonly Texture[] _rtColors;
|
||||||
private readonly ITexture[] _rtHostColors;
|
private readonly ITexture[] _rtHostColors;
|
||||||
|
private readonly bool[] _rtColorsBound;
|
||||||
private Texture _rtDepthStencil;
|
private Texture _rtDepthStencil;
|
||||||
private ITexture _rtHostDs;
|
private ITexture _rtHostDs;
|
||||||
|
private bool _rtDsBound;
|
||||||
|
|
||||||
public int ClipRegionWidth { get; private set; }
|
public int ClipRegionWidth { get; private set; }
|
||||||
public int ClipRegionHeight { get; private set; }
|
public int ClipRegionHeight { get; private set; }
|
||||||
@@ -51,6 +53,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_rtColors = new Texture[Constants.TotalRenderTargets];
|
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||||
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||||
|
_rtColorsBound = new bool[Constants.TotalRenderTargets];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -153,8 +156,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor);
|
bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor);
|
||||||
|
|
||||||
if (_rtColors[index] != color)
|
if (_rtColors[index] != color)
|
||||||
|
{
|
||||||
|
if (_rtColorsBound[index])
|
||||||
{
|
{
|
||||||
_rtColors[index]?.SignalModifying(false);
|
_rtColors[index]?.SignalModifying(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_rtColorsBound[index] = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (color != null)
|
if (color != null)
|
||||||
{
|
{
|
||||||
@@ -179,8 +189,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor);
|
bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor);
|
||||||
|
|
||||||
if (_rtDepthStencil != depthStencil)
|
if (_rtDepthStencil != depthStencil)
|
||||||
|
{
|
||||||
|
if (_rtDsBound)
|
||||||
{
|
{
|
||||||
_rtDepthStencil?.SignalModifying(false);
|
_rtDepthStencil?.SignalModifying(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_rtDsBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (depthStencil != null)
|
if (depthStencil != null)
|
||||||
{
|
{
|
||||||
@@ -413,21 +430,45 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
bool anyChanged = false;
|
bool anyChanged = false;
|
||||||
|
|
||||||
if (_rtHostDs != _rtDepthStencil?.HostTexture)
|
Texture dsTexture = _rtDepthStencil;
|
||||||
{
|
ITexture hostDsTexture = null;
|
||||||
_rtHostDs = _rtDepthStencil?.HostTexture;
|
|
||||||
|
|
||||||
|
if (dsTexture != null)
|
||||||
|
{
|
||||||
|
hostDsTexture = dsTexture.HostTexture;
|
||||||
|
|
||||||
|
if (!_rtDsBound)
|
||||||
|
{
|
||||||
|
dsTexture.SignalModifying(true);
|
||||||
|
_rtDsBound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rtHostDs != hostDsTexture)
|
||||||
|
{
|
||||||
|
_rtHostDs = hostDsTexture;
|
||||||
anyChanged = true;
|
anyChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < _rtColors.Length; index++)
|
for (int index = 0; index < _rtColors.Length; index++)
|
||||||
{
|
{
|
||||||
ITexture hostTexture = _rtColors[index]?.HostTexture;
|
Texture texture = _rtColors[index];
|
||||||
|
ITexture hostTexture = null;
|
||||||
|
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
hostTexture = texture.HostTexture;
|
||||||
|
|
||||||
|
if (!_rtColorsBound[index])
|
||||||
|
{
|
||||||
|
texture.SignalModifying(true);
|
||||||
|
_rtColorsBound[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_rtHostColors[index] != hostTexture)
|
if (_rtHostColors[index] != hostTexture)
|
||||||
{
|
{
|
||||||
_rtHostColors[index] = hostTexture;
|
_rtHostColors[index] = hostTexture;
|
||||||
|
|
||||||
anyChanged = true;
|
anyChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,6 +493,31 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks all currently bound render target textures as modified, and also makes them be set as modified again on next use.
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshModifiedTextures()
|
||||||
|
{
|
||||||
|
Texture dsTexture = _rtDepthStencil;
|
||||||
|
|
||||||
|
if (dsTexture != null && _rtDsBound)
|
||||||
|
{
|
||||||
|
dsTexture.SignalModifying(false);
|
||||||
|
_rtDsBound = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < _rtColors.Length; index++)
|
||||||
|
{
|
||||||
|
Texture texture = _rtColors[index];
|
||||||
|
|
||||||
|
if (texture != null && _rtColorsBound[index])
|
||||||
|
{
|
||||||
|
texture.SignalModifying(false);
|
||||||
|
_rtColorsBound[index] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forces the texture and sampler pools to be re-loaded from the cache on next use.
|
/// Forces the texture and sampler pools to be re-loaded from the cache on next use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -487,12 +553,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_samplerPoolCache.Dispose();
|
_samplerPoolCache.Dispose();
|
||||||
|
|
||||||
for (int i = 0; i < _rtColors.Length; i++)
|
for (int i = 0; i < _rtColors.Length; i++)
|
||||||
|
{
|
||||||
|
if (_rtColorsBound[i])
|
||||||
{
|
{
|
||||||
_rtColors[i]?.DecrementReferenceCount();
|
_rtColors[i]?.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
_rtColors[i] = null;
|
_rtColors[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_rtDsBound)
|
||||||
|
{
|
||||||
_rtDepthStencil?.DecrementReferenceCount();
|
_rtDepthStencil?.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
_rtDepthStencil = null;
|
_rtDepthStencil = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -430,7 +430,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
||||||
{
|
{
|
||||||
if (gpuVa != 0 && (int)format > 0)
|
if (gpuVa != 0 && format != 0)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
|
Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 5791;
|
private const uint CodeGenVersion = 5957;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -37,10 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
/// <returns>The incremented value of the syncpoint</returns>
|
/// <returns>The incremented value of the syncpoint</returns>
|
||||||
public uint IncrementSyncpoint(uint id)
|
public uint IncrementSyncpoint(uint id)
|
||||||
{
|
{
|
||||||
if (id >= MaxHardwareSyncpoints)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _syncpoints[id].Increment();
|
return _syncpoints[id].Increment();
|
||||||
}
|
}
|
||||||
@@ -53,10 +50,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
/// <returns>The value of the syncpoint</returns>
|
/// <returns>The value of the syncpoint</returns>
|
||||||
public uint GetSyncpointValue(uint id)
|
public uint GetSyncpointValue(uint id)
|
||||||
{
|
{
|
||||||
if (id >= MaxHardwareSyncpoints)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _syncpoints[id].Value;
|
return _syncpoints[id].Value;
|
||||||
}
|
}
|
||||||
@@ -72,10 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
/// <returns>The created SyncpointWaiterHandle object or null if already past threshold</returns>
|
/// <returns>The created SyncpointWaiterHandle object or null if already past threshold</returns>
|
||||||
public SyncpointWaiterHandle RegisterCallbackOnSyncpoint(uint id, uint threshold, Action<SyncpointWaiterHandle> callback)
|
public SyncpointWaiterHandle RegisterCallbackOnSyncpoint(uint id, uint threshold, Action<SyncpointWaiterHandle> callback)
|
||||||
{
|
{
|
||||||
if (id >= MaxHardwareSyncpoints)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _syncpoints[id].RegisterCallback(threshold, callback);
|
return _syncpoints[id].RegisterCallback(threshold, callback);
|
||||||
}
|
}
|
||||||
@@ -88,10 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
|
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when id >= MaxHardwareSyncpoints</exception>
|
||||||
public void UnregisterCallback(uint id, SyncpointWaiterHandle waiterInformation)
|
public void UnregisterCallback(uint id, SyncpointWaiterHandle waiterInformation)
|
||||||
{
|
{
|
||||||
if (id >= MaxHardwareSyncpoints)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
_syncpoints[id].UnregisterCallback(waiterInformation);
|
_syncpoints[id].UnregisterCallback(waiterInformation);
|
||||||
}
|
}
|
||||||
@@ -107,10 +95,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
|||||||
/// <returns>True if timed out</returns>
|
/// <returns>True if timed out</returns>
|
||||||
public bool WaitOnSyncpoint(uint id, uint threshold, TimeSpan timeout)
|
public bool WaitOnSyncpoint(uint id, uint threshold, TimeSpan timeout)
|
||||||
{
|
{
|
||||||
if (id >= MaxHardwareSyncpoints)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)MaxHardwareSyncpoints);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this when GPU channel scheduling will be implemented.
|
// TODO: Remove this when GPU channel scheduling will be implemented.
|
||||||
if (timeout == Timeout.InfiniteTimeSpan)
|
if (timeout == Timeout.InfiniteTimeSpan)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -69,10 +69,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
|
|
||||||
public Operand GetDest(int index)
|
public Operand GetDest(int index)
|
||||||
{
|
{
|
||||||
if (index != 0)
|
ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _dest;
|
return _dest;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bindlessHandle.AsgOp is not Operation handleCombineOp)
|
if (!TryGetOperation(bindlessHandle.AsgOp, out Operation handleCombineOp))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -199,9 +199,64 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryGetOperation(INode asgOp, out Operation outOperation)
|
||||||
|
{
|
||||||
|
if (asgOp is PhiNode phi)
|
||||||
|
{
|
||||||
|
// If we have a phi, let's check if all inputs are effectively the same value.
|
||||||
|
// If so, we can "see through" the phi and pick any of the inputs (since they are all the same).
|
||||||
|
|
||||||
|
Operand firstSrc = phi.GetSource(0);
|
||||||
|
|
||||||
|
for (int index = 1; index < phi.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
if (!IsSameOperand(firstSrc, phi.GetSource(index)))
|
||||||
|
{
|
||||||
|
outOperation = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asgOp = firstSrc.AsgOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asgOp is Operation operation)
|
||||||
|
{
|
||||||
|
outOperation = operation;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
outOperation = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSameOperand(Operand x, Operand y)
|
||||||
|
{
|
||||||
|
if (x.Type == y.Type && x.Type == OperandType.LocalVariable)
|
||||||
|
{
|
||||||
|
return x.AsgOp is Operation xOp &&
|
||||||
|
y.AsgOp is Operation yOp &&
|
||||||
|
xOp.Inst == Instruction.BitwiseOr &&
|
||||||
|
yOp.Inst == Instruction.BitwiseOr &&
|
||||||
|
AreBothEqualConstantBuffers(xOp.GetSource(0), yOp.GetSource(0)) &&
|
||||||
|
AreBothEqualConstantBuffers(xOp.GetSource(1), yOp.GetSource(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AreBothEqualConstantBuffers(Operand x, Operand y)
|
||||||
|
{
|
||||||
|
return x.Type == y.Type && x.Value == y.Value && x.Type == OperandType.ConstantBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
private static Operand GetSourceForMaskedHandle(Operation asgOp, uint mask)
|
private static Operand GetSourceForMaskedHandle(Operation asgOp, uint mask)
|
||||||
{
|
{
|
||||||
// Assume it was already checked that the operation is bitwise AND.
|
// Assume it was already checked that the operation is bitwise AND.
|
||||||
|
|
||||||
Operand src0 = asgOp.GetSource(0);
|
Operand src0 = asgOp.GetSource(0);
|
||||||
Operand src1 = asgOp.GetSource(1);
|
Operand src1 = asgOp.GetSource(1);
|
||||||
|
|
||||||
@@ -210,6 +265,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// We can't check if the mask matches here as both operands are from a constant buffer.
|
// We can't check if the mask matches here as both operands are from a constant buffer.
|
||||||
// Be optimistic and assume it matches. Avoid constant buffer 1 as official drivers
|
// Be optimistic and assume it matches. Avoid constant buffer 1 as official drivers
|
||||||
// uses this one to store compiler constants.
|
// uses this one to store compiler constants.
|
||||||
|
|
||||||
return src0.GetCbufSlot() == 1 ? src1 : src0;
|
return src0.GetCbufSlot() == 1 ? src1 : src0;
|
||||||
}
|
}
|
||||||
else if (src0.Type == OperandType.ConstantBuffer && src1.Type == OperandType.Constant)
|
else if (src0.Type == OperandType.ConstantBuffer && src1.Type == OperandType.Constant)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
@@ -102,11 +103,11 @@ namespace Ryujinx.Graphics.Texture.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static RgbaColor32 operator <<(RgbaColor32 x, int shift)
|
public static RgbaColor32 operator <<(RgbaColor32 x, [ConstantExpected] byte shift)
|
||||||
{
|
{
|
||||||
if (Sse2.IsSupported)
|
if (Sse2.IsSupported)
|
||||||
{
|
{
|
||||||
return new RgbaColor32(Sse2.ShiftLeftLogical(x._color, (byte)shift));
|
return new RgbaColor32(Sse2.ShiftLeftLogical(x._color, shift));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -115,11 +116,11 @@ namespace Ryujinx.Graphics.Texture.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static RgbaColor32 operator >>(RgbaColor32 x, int shift)
|
public static RgbaColor32 operator >>(RgbaColor32 x, [ConstantExpected] byte shift)
|
||||||
{
|
{
|
||||||
if (Sse2.IsSupported)
|
if (Sse2.IsSupported)
|
||||||
{
|
{
|
||||||
return new RgbaColor32(Sse2.ShiftRightLogical(x._color, (byte)shift));
|
return new RgbaColor32(Sse2.ShiftRightLogical(x._color, shift));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -189,7 +189,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PipelineStageFlags.AllCommandsBit,
|
PipelineStageFlags.AllCommandsBit,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
new ReadOnlySpan<MemoryBarrier>(memoryBarrier),
|
new ReadOnlySpan<MemoryBarrier>(in memoryBarrier),
|
||||||
0,
|
0,
|
||||||
ReadOnlySpan<BufferMemoryBarrier>.Empty,
|
ReadOnlySpan<BufferMemoryBarrier>.Empty,
|
||||||
0,
|
0,
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
@@ -33,9 +33,5 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public VulkanException(string message, Exception innerException) : base(message, innerException)
|
public VulkanException(string message, Exception innerException) : base(message, innerException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected VulkanException(SerializationInfo info, StreamingContext context) : base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,8 +35,6 @@ namespace Ryujinx.HLE.Exceptions
|
|||||||
Request = context.Request;
|
Request = context.Request;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ServiceNotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
|
||||||
|
|
||||||
public override string Message
|
public override string Message
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@@ -1,61 +0,0 @@
|
|||||||
using LibHac.Common.Keys;
|
|
||||||
using LibHac.Fs.Fsa;
|
|
||||||
using LibHac.Ncm;
|
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
|
||||||
using LibHac.Tools.Ncm;
|
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Thin wrapper around <see cref="Cnmt"/>
|
|
||||||
/// </summary>
|
|
||||||
public class ContentCollection
|
|
||||||
{
|
|
||||||
private readonly IFileSystem _pfs;
|
|
||||||
private readonly Cnmt _cnmt;
|
|
||||||
|
|
||||||
public ulong Id => _cnmt.TitleId;
|
|
||||||
public TitleVersion Version => _cnmt.TitleVersion;
|
|
||||||
public ContentMetaType Type => _cnmt.Type;
|
|
||||||
public ulong ApplicationId => _cnmt.ApplicationTitleId;
|
|
||||||
public ulong PatchId => _cnmt.PatchTitleId;
|
|
||||||
public TitleVersion RequiredSystemVersion => _cnmt.MinimumSystemVersion;
|
|
||||||
public TitleVersion RequiredApplicationVersion => _cnmt.MinimumApplicationVersion;
|
|
||||||
public byte[] Digest => _cnmt.Hash;
|
|
||||||
|
|
||||||
public ulong ProgramBaseId => Id & ~0x1FFFUL;
|
|
||||||
public bool IsSystemTitle => _cnmt.Type < ContentMetaType.Application;
|
|
||||||
|
|
||||||
public ContentCollection(IFileSystem pfs, Cnmt cnmt)
|
|
||||||
{
|
|
||||||
_pfs = pfs;
|
|
||||||
_cnmt = cnmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Nca GetNcaByType(KeySet keySet, ContentType type, int programIndex = 0)
|
|
||||||
{
|
|
||||||
// TODO: Replace this with a check for IdOffset as soon as LibHac supports it:
|
|
||||||
// && entry.IdOffset == programIndex
|
|
||||||
|
|
||||||
foreach (var entry in _cnmt.ContentEntries)
|
|
||||||
{
|
|
||||||
if (entry.Type != type)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string ncaId = BitConverter.ToString(entry.NcaId).Replace("-", null).ToLower();
|
|
||||||
Nca nca = _pfs.GetNca(keySet, $"/{ncaId}.nca");
|
|
||||||
|
|
||||||
if (nca.GetProgramIndex() == programIndex)
|
|
||||||
{
|
|
||||||
return nca;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -419,11 +419,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (locationList != null)
|
if (locationList != null)
|
||||||
{
|
|
||||||
if (locationList.Contains(entry))
|
|
||||||
{
|
{
|
||||||
locationList.Remove(entry);
|
locationList.Remove(entry);
|
||||||
}
|
|
||||||
|
|
||||||
locationList.AddLast(entry);
|
locationList.AddLast(entry);
|
||||||
}
|
}
|
||||||
|
@@ -85,10 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
|
|||||||
|
|
||||||
public void SetSyncpointMinEqualSyncpointMax(uint id)
|
public void SetSyncpointMinEqualSyncpointMax(uint id)
|
||||||
{
|
{
|
||||||
if (id >= SynchronizationManager.MaxHardwareSyncpoints)
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(id, (uint)SynchronizationManager.MaxHardwareSyncpoints);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
int value = (int)ReadSyncpointValue(id);
|
int value = (int)ReadSyncpointValue(id);
|
||||||
|
|
||||||
|
@@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
|
|||||||
int controlLength = message.Control == null ? 0 : message.Control.Length;
|
int controlLength = message.Control == null ? 0 : message.Control.Length;
|
||||||
BsdSocketFlags flags = message.Flags;
|
BsdSocketFlags flags = message.Flags;
|
||||||
|
|
||||||
if (!MemoryMarshal.TryWrite(rawData, ref msgNameLength))
|
if (!MemoryMarshal.TryWrite(rawData, in msgNameLength))
|
||||||
{
|
{
|
||||||
return LinuxError.EFAULT;
|
return LinuxError.EFAULT;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
|
|||||||
rawData = rawData[msgNameLength..];
|
rawData = rawData[msgNameLength..];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MemoryMarshal.TryWrite(rawData, ref iovCount))
|
if (!MemoryMarshal.TryWrite(rawData, in iovCount))
|
||||||
{
|
{
|
||||||
return LinuxError.EFAULT;
|
return LinuxError.EFAULT;
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
|
|||||||
{
|
{
|
||||||
ulong iovLength = (ulong)message.Iov[index].Length;
|
ulong iovLength = (ulong)message.Iov[index].Length;
|
||||||
|
|
||||||
if (!MemoryMarshal.TryWrite(rawData, ref iovLength))
|
if (!MemoryMarshal.TryWrite(rawData, in iovLength))
|
||||||
{
|
{
|
||||||
return LinuxError.EFAULT;
|
return LinuxError.EFAULT;
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MemoryMarshal.TryWrite(rawData, ref controlLength))
|
if (!MemoryMarshal.TryWrite(rawData, in controlLength))
|
||||||
{
|
{
|
||||||
return LinuxError.EFAULT;
|
return LinuxError.EFAULT;
|
||||||
}
|
}
|
||||||
@@ -96,14 +96,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
|
|||||||
rawData = rawData[controlLength..];
|
rawData = rawData[controlLength..];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MemoryMarshal.TryWrite(rawData, ref flags))
|
if (!MemoryMarshal.TryWrite(rawData, in flags))
|
||||||
{
|
{
|
||||||
return LinuxError.EFAULT;
|
return LinuxError.EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
rawData = rawData[sizeof(BsdSocketFlags)..];
|
rawData = rawData[sizeof(BsdSocketFlags)..];
|
||||||
|
|
||||||
if (!MemoryMarshal.TryWrite(rawData, ref message.Length))
|
if (!MemoryMarshal.TryWrite(rawData, in message.Length))
|
||||||
{
|
{
|
||||||
return LinuxError.EFAULT;
|
return LinuxError.EFAULT;
|
||||||
}
|
}
|
||||||
|
@@ -654,7 +654,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint sentinel = 0;
|
uint sentinel = 0;
|
||||||
MemoryMarshal.Write(data, ref sentinel);
|
MemoryMarshal.Write(data, in sentinel);
|
||||||
data = data[sizeof(uint)..];
|
data = data[sizeof(uint)..];
|
||||||
|
|
||||||
return region.Memory.Span.Length - data.Length;
|
return region.Memory.Span.Length - data.Length;
|
||||||
|
@@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
|
|||||||
|
|
||||||
Header.ToNetworkOrder();
|
Header.ToNetworkOrder();
|
||||||
|
|
||||||
MemoryMarshal.Write(buffer, ref Header);
|
MemoryMarshal.Write(buffer, in Header);
|
||||||
|
|
||||||
buffer = buffer[Unsafe.SizeOf<AddrInfoSerializedHeader>()..];
|
buffer = buffer[Unsafe.SizeOf<AddrInfoSerializedHeader>()..];
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
|
|||||||
AddrInfo4 socketAddress = SocketAddress.Value;
|
AddrInfo4 socketAddress = SocketAddress.Value;
|
||||||
socketAddress.ToNetworkOrder();
|
socketAddress.ToNetworkOrder();
|
||||||
|
|
||||||
MemoryMarshal.Write(buffer, ref socketAddress);
|
MemoryMarshal.Write(buffer, in socketAddress);
|
||||||
|
|
||||||
buffer = buffer[Unsafe.SizeOf<AddrInfo4>()..];
|
buffer = buffer[Unsafe.SizeOf<AddrInfo4>()..];
|
||||||
}
|
}
|
||||||
@@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
|
|||||||
Array4<byte> rawIPv4Address = RawIPv4Address.Value;
|
Array4<byte> rawIPv4Address = RawIPv4Address.Value;
|
||||||
AddrInfo4.RawIpv4AddressNetworkEndianSwap(ref rawIPv4Address);
|
AddrInfo4.RawIpv4AddressNetworkEndianSwap(ref rawIPv4Address);
|
||||||
|
|
||||||
MemoryMarshal.Write(buffer, ref rawIPv4Address);
|
MemoryMarshal.Write(buffer, in rawIPv4Address);
|
||||||
|
|
||||||
buffer = buffer[Unsafe.SizeOf<Array4<byte>>()..];
|
buffer = buffer[Unsafe.SizeOf<Array4<byte>>()..];
|
||||||
}
|
}
|
||||||
|
@@ -161,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw exception;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -206,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw exception;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@@ -2,31 +2,21 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
|
||||||
using LibHac.Loader;
|
using LibHac.Loader;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using LibHac.Tools.Fs;
|
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Tools.Ncm;
|
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
using ApplicationId = LibHac.Ncm.ApplicationId;
|
||||||
using ContentType = LibHac.Ncm.ContentType;
|
|
||||||
using Path = System.IO.Path;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
{
|
{
|
||||||
public static class NcaExtensions
|
static class NcaExtensions
|
||||||
{
|
{
|
||||||
private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
|
||||||
|
|
||||||
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
|
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
|
||||||
{
|
{
|
||||||
// Extract RomFs and ExeFs from NCA.
|
// Extract RomFs and ExeFs from NCA.
|
||||||
@@ -57,7 +47,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
nacpData = controlNca.GetNacp(device);
|
nacpData = controlNca.GetNacp(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update.
|
/* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant update.
|
||||||
|
|
||||||
// Load program 0 control NCA as we are going to need it for display version.
|
// Load program 0 control NCA as we are going to need it for display version.
|
||||||
(_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
|
(_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
|
||||||
@@ -96,11 +86,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
return processResult;
|
return processResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong GetProgramIdBase(this Nca nca)
|
|
||||||
{
|
|
||||||
return nca.Header.TitleId & ~0x1FFFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetProgramIndex(this Nca nca)
|
public static int GetProgramIndex(this Nca nca)
|
||||||
{
|
{
|
||||||
return (int)(nca.Header.TitleId & 0xF);
|
return (int)(nca.Header.TitleId & 0xF);
|
||||||
@@ -111,11 +96,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
return nca.Header.ContentType == NcaContentType.Program;
|
return nca.Header.ContentType == NcaContentType.Program;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsMain(this Nca nca)
|
|
||||||
{
|
|
||||||
return nca.IsProgram() && !nca.IsPatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsPatch(this Nca nca)
|
public static bool IsPatch(this Nca nca)
|
||||||
{
|
{
|
||||||
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
||||||
@@ -128,56 +108,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
return nca.Header.ContentType == NcaContentType.Control;
|
return nca.Header.ContentType == NcaContentType.Control;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (Nca, Nca) GetUpdateData(this Nca mainNca, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel, int programIndex, out string updatePath)
|
|
||||||
{
|
|
||||||
updatePath = "(unknown)";
|
|
||||||
|
|
||||||
// Load Update NCAs.
|
|
||||||
Nca updatePatchNca = null;
|
|
||||||
Nca updateControlNca = null;
|
|
||||||
|
|
||||||
// Clear the program index part.
|
|
||||||
ulong titleIdBase = mainNca.GetProgramIdBase();
|
|
||||||
|
|
||||||
// Load update information if exists.
|
|
||||||
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json");
|
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
|
||||||
{
|
|
||||||
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
|
|
||||||
if (File.Exists(updatePath))
|
|
||||||
{
|
|
||||||
var updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
|
|
||||||
|
|
||||||
IFileSystem updatePartitionFileSystem;
|
|
||||||
|
|
||||||
if (Path.GetExtension(updatePath).ToLower() == ".xci")
|
|
||||||
{
|
|
||||||
updatePartitionFileSystem = new Xci(fileSystem.KeySet, updateFile.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PartitionFileSystem pfsTemp = new();
|
|
||||||
pfsTemp.Initialize(updateFile.AsStorage()).ThrowIfFailure();
|
|
||||||
updatePartitionFileSystem = pfsTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ((ulong updateTitleId, ContentCollection content) in updatePartitionFileSystem.GetUpdateData(fileSystem, checkLevel))
|
|
||||||
{
|
|
||||||
if ((updateTitleId & ~0x1FFFUL) != titleIdBase)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex);
|
|
||||||
updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (updatePatchNca, updateControlNca);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null)
|
public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null)
|
||||||
{
|
{
|
||||||
IFileSystem exeFs = null;
|
IFileSystem exeFs = null;
|
||||||
@@ -242,31 +172,5 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
|
|
||||||
return nacpData;
|
return nacpData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Cnmt GetCnmt(this Nca cnmtNca, IntegrityCheckLevel checkLevel, ContentMetaType metaType)
|
|
||||||
{
|
|
||||||
string path = $"/{metaType}_{cnmtNca.Header.TitleId:x16}.cnmt";
|
|
||||||
using var cnmtFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Result result = cnmtNca.OpenFileSystem(0, checkLevel)
|
|
||||||
.OpenFile(ref cnmtFile.Ref, path.ToU8Span(), OpenMode.Read);
|
|
||||||
|
|
||||||
if (result.IsSuccess())
|
|
||||||
{
|
|
||||||
return new Cnmt(cnmtFile.Release().AsStream());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (HorizonResultException ex)
|
|
||||||
{
|
|
||||||
if (!ResultFs.PathNotFound.Includes(ex.ResultValue))
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed get cnmt for '{cnmtNca.Header.TitleId:x16}' from nca: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,87 +1,26 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Common.Keys;
|
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Ncm;
|
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Tools.Ncm;
|
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ContentType = LibHac.Ncm.ContentType;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
{
|
{
|
||||||
public static class PartitionFileSystemExtensions
|
public static class PartitionFileSystemExtensions
|
||||||
{
|
{
|
||||||
private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public static Dictionary<ulong, ContentCollection> GetApplicationData(this IFileSystem partitionFileSystem,
|
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, out string errorMessage)
|
||||||
VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
|
|
||||||
{
|
|
||||||
fileSystem.ImportTickets(partitionFileSystem);
|
|
||||||
|
|
||||||
var programs = new Dictionary<ulong, ContentCollection>();
|
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
|
|
||||||
{
|
|
||||||
Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, ContentMetaType.Application);
|
|
||||||
|
|
||||||
if (cnmt == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentCollection content = new(partitionFileSystem, cnmt);
|
|
||||||
|
|
||||||
if (content.Type != ContentMetaType.Application)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
programs.TryAdd(content.ApplicationId, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return programs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<ulong, ContentCollection> GetUpdateData(this IFileSystem partitionFileSystem,
|
|
||||||
VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
|
|
||||||
{
|
|
||||||
fileSystem.ImportTickets(partitionFileSystem);
|
|
||||||
|
|
||||||
var programs = new Dictionary<ulong, ContentCollection>();
|
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
|
|
||||||
{
|
|
||||||
Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, ContentMetaType.Patch);
|
|
||||||
|
|
||||||
if (cnmt == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentCollection content = new(partitionFileSystem, cnmt);
|
|
||||||
|
|
||||||
if (content.Type != ContentMetaType.Patch)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
programs.TryAdd(content.ApplicationId, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return programs;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, ulong titleId, out string errorMessage)
|
|
||||||
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
|
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
|
||||||
where TFormat : IPartitionFileSystemFormat
|
where TFormat : IPartitionFileSystemFormat
|
||||||
where THeader : unmanaged, IPartitionFileSystemHeader
|
where THeader : unmanaged, IPartitionFileSystemHeader
|
||||||
@@ -96,21 +35,30 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Dictionary<ulong, ContentCollection> applications = partitionFileSystem.GetApplicationData(device.FileSystem, device.System.FsIntegrityCheckLevel);
|
device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem);
|
||||||
|
|
||||||
if (titleId == 0)
|
// TODO: To support multi-games container, this should use CNMT NCA instead.
|
||||||
|
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
|
||||||
{
|
{
|
||||||
foreach ((ulong _, ContentCollection content) in applications)
|
Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
|
||||||
|
|
||||||
|
if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
|
||||||
{
|
{
|
||||||
mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index);
|
continue;
|
||||||
controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (applications.TryGetValue(titleId, out ContentCollection content))
|
if (nca.IsPatch())
|
||||||
{
|
{
|
||||||
mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index);
|
patchNca = nca;
|
||||||
controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index);
|
}
|
||||||
|
else if (nca.IsProgram())
|
||||||
|
{
|
||||||
|
mainNca = nca;
|
||||||
|
}
|
||||||
|
else if (nca.IsControl())
|
||||||
|
{
|
||||||
|
controlNca = nca;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure();
|
ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure();
|
||||||
@@ -131,7 +79,54 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
return (false, ProcessResult.Failed);
|
return (false, ProcessResult.Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _);
|
// Load Update NCAs.
|
||||||
|
Nca updatePatchNca = null;
|
||||||
|
Nca updateControlNca = null;
|
||||||
|
|
||||||
|
if (ulong.TryParse(mainNca.Header.TitleId.ToString("x16"), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase))
|
||||||
|
{
|
||||||
|
// Clear the program index part.
|
||||||
|
titleIdBase &= ~0xFUL;
|
||||||
|
|
||||||
|
// Load update information if exists.
|
||||||
|
string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
||||||
|
if (File.Exists(titleUpdateMetadataPath))
|
||||||
|
{
|
||||||
|
string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
|
||||||
|
if (File.Exists(updatePath))
|
||||||
|
{
|
||||||
|
PartitionFileSystem updatePartitionFileSystem = new();
|
||||||
|
updatePartitionFileSystem.Initialize(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()).ThrowIfFailure();
|
||||||
|
|
||||||
|
device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem);
|
||||||
|
|
||||||
|
// TODO: This should use CNMT NCA instead.
|
||||||
|
foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca"))
|
||||||
|
{
|
||||||
|
Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath);
|
||||||
|
|
||||||
|
if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16"))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nca.IsProgram())
|
||||||
|
{
|
||||||
|
updatePatchNca = nca;
|
||||||
|
}
|
||||||
|
else if (nca.IsControl())
|
||||||
|
{
|
||||||
|
updateControlNca = nca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (updatePatchNca != null)
|
if (updatePatchNca != null)
|
||||||
{
|
{
|
||||||
@@ -173,18 +168,18 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
return (true, mainNca.Load(device, patchNca, controlNca));
|
return (true, mainNca.Load(device, patchNca, controlNca));
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage = $"Unable to load: Could not find Main NCA for title \"{titleId:X16}\"";
|
errorMessage = "Unable to load: Could not find Main NCA";
|
||||||
|
|
||||||
return (false, ProcessResult.Failed);
|
return (false, ProcessResult.Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Nca GetNca(this IFileSystem fileSystem, KeySet keySet, string path)
|
public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path)
|
||||||
{
|
{
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
return new Nca(keySet, ncaFile.Release().AsStorage());
|
return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
_processesByPid = new ConcurrentDictionary<ulong, ProcessResult>();
|
_processesByPid = new ConcurrentDictionary<ulong, ProcessResult>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadXci(string path, ulong titleId)
|
public bool LoadXci(string path)
|
||||||
{
|
{
|
||||||
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||||
Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
|
Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
|
||||||
@@ -44,7 +44,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, titleId, out string errorMessage);
|
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
@@ -66,13 +66,13 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadNsp(string path, ulong titleId)
|
public bool LoadNsp(string path)
|
||||||
{
|
{
|
||||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
PartitionFileSystem partitionFileSystem = new();
|
PartitionFileSystem partitionFileSystem = new();
|
||||||
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
|
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||||
|
|
||||||
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, titleId, out string errorMessage);
|
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage);
|
||||||
|
|
||||||
if (processResult.ProcessId == 0)
|
if (processResult.ProcessId == 0)
|
||||||
{
|
{
|
||||||
|
@@ -42,14 +42,15 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
|
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
|
||||||
{
|
{
|
||||||
Nca nca = partitionFileSystem.GetNca(device.FileSystem.KeySet, fileEntry.FullPath);
|
Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
|
||||||
|
|
||||||
if (!nca.IsProgram())
|
if (!nca.IsProgram() && nca.IsPatch())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong currentMainProgramId = nca.GetProgramIdBase();
|
ulong currentProgramId = nca.Header.TitleId;
|
||||||
|
ulong currentMainProgramId = currentProgramId & ~0xFFFul;
|
||||||
|
|
||||||
if (applicationId == 0 && currentMainProgramId != 0)
|
if (applicationId == 0 && currentMainProgramId != 0)
|
||||||
{
|
{
|
||||||
@@ -66,7 +67,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasIndex[nca.GetProgramIndex()] = true;
|
hasIndex[(int)(currentProgramId & 0xF)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (programCount == 0)
|
if (programCount == 0)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -72,9 +72,9 @@ namespace Ryujinx.HLE
|
|||||||
return Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
return Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadXci(string xciFile, ulong titleId = 0)
|
public bool LoadXci(string xciFile)
|
||||||
{
|
{
|
||||||
return Processes.LoadXci(xciFile, titleId);
|
return Processes.LoadXci(xciFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadNca(string ncaFile)
|
public bool LoadNca(string ncaFile)
|
||||||
@@ -82,9 +82,9 @@ namespace Ryujinx.HLE
|
|||||||
return Processes.LoadNca(ncaFile);
|
return Processes.LoadNca(ncaFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadNsp(string nspFile, ulong titleId = 0)
|
public bool LoadNsp(string nspFile)
|
||||||
{
|
{
|
||||||
return Processes.LoadNsp(nspFile, titleId);
|
return Processes.LoadNsp(nspFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadProgram(string fileName)
|
public bool LoadProgram(string fileName)
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Version>1.0.0-dirty</Version>
|
<Version>1.0.0-dirty</Version>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" />
|
<PackageReference Include="CommandLineParser" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -52,10 +52,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
if (HasSingleRange)
|
if (HasSingleRange)
|
||||||
{
|
{
|
||||||
if (_singleRange.Size - offset < size)
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(size, _singleRange.Size - offset);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MultiRange(_singleRange.Address + offset, size);
|
return new MultiRange(_singleRange.Address + offset, size);
|
||||||
}
|
}
|
||||||
@@ -108,10 +105,7 @@ namespace Ryujinx.Memory.Range
|
|||||||
{
|
{
|
||||||
if (HasSingleRange)
|
if (HasSingleRange)
|
||||||
{
|
{
|
||||||
if (index != 0)
|
ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _singleRange;
|
return _singleRange;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
@@ -9,11 +9,9 @@ using LibHac.Tools.FsSystem;
|
|||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.App.Common
|
namespace Ryujinx.Ui.App.Common
|
||||||
{
|
{
|
||||||
@@ -21,10 +19,10 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
{
|
{
|
||||||
public bool Favorite { get; set; }
|
public bool Favorite { get; set; }
|
||||||
public byte[] Icon { get; set; }
|
public byte[] Icon { get; set; }
|
||||||
public string Name { get; set; } = "Unknown";
|
public string TitleName { get; set; }
|
||||||
public ulong Id { get; set; }
|
public string TitleId { get; set; }
|
||||||
public string Developer { get; set; } = "Unknown";
|
public string Developer { get; set; }
|
||||||
public string Version { get; set; } = "0";
|
public string Version { get; set; }
|
||||||
public TimeSpan TimePlayed { get; set; }
|
public TimeSpan TimePlayed { get; set; }
|
||||||
public DateTime? LastPlayed { get; set; }
|
public DateTime? LastPlayed { get; set; }
|
||||||
public string FileExtension { get; set; }
|
public string FileExtension { get; set; }
|
||||||
@@ -38,11 +36,7 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
|
|
||||||
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
|
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
|
||||||
|
|
||||||
[JsonIgnore] public string IdString => Id.ToString("x16");
|
public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
|
||||||
|
|
||||||
[JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL;
|
|
||||||
|
|
||||||
public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath)
|
|
||||||
{
|
{
|
||||||
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
@@ -111,7 +105,7 @@ namespace Ryujinx.Ui.App.Common
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
(Nca updatePatchNca, _) = mainNca.GetUpdateData(virtualFileSystem, checkLevel, 0, out string _);
|
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
|
||||||
|
|
||||||
if (updatePatchNca != null)
|
if (updatePatchNca != null)
|
||||||
{
|
{
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Ui.Common.Configuration
|
|
||||||
{
|
|
||||||
public enum ConfigurationLoadResult
|
|
||||||
{
|
|
||||||
Success = 0,
|
|
||||||
NotLoaded = 1,
|
|
||||||
MigratedFromPreVulkan = 1 << 8,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -5,6 +5,7 @@ using Ryujinx.Common.Configuration.Hid.Controller;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.Common.Configuration.Multiplayer;
|
using Ryujinx.Common.Configuration.Multiplayer;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Graphics.Vulkan;
|
||||||
using Ryujinx.Ui.Common.Configuration.System;
|
using Ryujinx.Ui.Common.Configuration.System;
|
||||||
using Ryujinx.Ui.Common.Configuration.Ui;
|
using Ryujinx.Ui.Common.Configuration.Ui;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
@@ -763,7 +764,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
Graphics.ResScaleCustom.Value = 1.0f;
|
Graphics.ResScaleCustom.Value = 1.0f;
|
||||||
Graphics.MaxAnisotropy.Value = -1.0f;
|
Graphics.MaxAnisotropy.Value = -1.0f;
|
||||||
Graphics.AspectRatio.Value = AspectRatio.Fixed16x9;
|
Graphics.AspectRatio.Value = AspectRatio.Fixed16x9;
|
||||||
Graphics.GraphicsBackend.Value = OperatingSystem.IsMacOS() ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl;
|
Graphics.GraphicsBackend.Value = DefaultGraphicsBackend();
|
||||||
Graphics.PreferredGpu.Value = "";
|
Graphics.PreferredGpu.Value = "";
|
||||||
Graphics.ShadersDumpPath.Value = "";
|
Graphics.ShadersDumpPath.Value = "";
|
||||||
Logger.EnableDebug.Value = false;
|
Logger.EnableDebug.Value = false;
|
||||||
@@ -907,7 +908,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationLoadResult Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath)
|
public void Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath)
|
||||||
{
|
{
|
||||||
bool configurationFileUpdated = false;
|
bool configurationFileUpdated = false;
|
||||||
|
|
||||||
@@ -916,12 +917,8 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
|
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
|
||||||
|
|
||||||
LoadDefault();
|
LoadDefault();
|
||||||
|
|
||||||
return ConfigurationLoadResult.NotLoaded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationLoadResult result = ConfigurationLoadResult.Success;
|
|
||||||
|
|
||||||
if (configurationFileFormat.Version < 2)
|
if (configurationFileFormat.Version < 2)
|
||||||
{
|
{
|
||||||
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2.");
|
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2.");
|
||||||
@@ -1336,8 +1333,6 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.GraphicsBackend = GraphicsBackend.OpenGl;
|
configurationFileFormat.GraphicsBackend = GraphicsBackend.OpenGl;
|
||||||
|
|
||||||
result |= ConfigurationLoadResult.MigratedFromPreVulkan;
|
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1535,8 +1530,18 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
|
|
||||||
Ryujinx.Common.Logging.Logger.Notice.Print(LogClass.Application, $"Configuration file updated to version {ConfigurationFileFormat.CurrentVersion}");
|
Ryujinx.Common.Logging.Logger.Notice.Print(LogClass.Application, $"Configuration file updated to version {ConfigurationFileFormat.CurrentVersion}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
private static GraphicsBackend DefaultGraphicsBackend()
|
||||||
|
{
|
||||||
|
// Any system running macOS or returning any amount of valid Vulkan devices should default to Vulkan.
|
||||||
|
// Checks for if the Vulkan version and featureset is compatible should be performed within VulkanRenderer.
|
||||||
|
if (OperatingSystem.IsMacOS() || VulkanRenderer.GetPhysicalDevices().Length > 0)
|
||||||
|
{
|
||||||
|
return GraphicsBackend.Vulkan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GraphicsBackend.OpenGl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LogValueChange<T>(ReactiveEventArgs<T> eventArgs, string valueName)
|
private static void LogValueChange<T>(ReactiveEventArgs<T> eventArgs, string valueName)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -7,7 +7,6 @@ using Ryujinx.Common.SystemInterop;
|
|||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.SDL2.Common;
|
using Ryujinx.SDL2.Common;
|
||||||
using Ryujinx.Ui;
|
using Ryujinx.Ui;
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
@@ -178,8 +177,6 @@ namespace Ryujinx
|
|||||||
? appDataConfigurationPath
|
? appDataConfigurationPath
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
bool showVulkanPrompt = false;
|
|
||||||
|
|
||||||
if (ConfigurationPath == null)
|
if (ConfigurationPath == null)
|
||||||
{
|
{
|
||||||
// No configuration, we load the default values and save it to disk
|
// No configuration, we load the default values and save it to disk
|
||||||
@@ -187,26 +184,17 @@ namespace Ryujinx
|
|||||||
|
|
||||||
ConfigurationState.Instance.LoadDefault();
|
ConfigurationState.Instance.LoadDefault();
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
||||||
|
|
||||||
showVulkanPrompt = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat))
|
if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat))
|
||||||
{
|
{
|
||||||
ConfigurationLoadResult result = ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
||||||
|
|
||||||
if ((result & ConfigurationLoadResult.MigratedFromPreVulkan) != 0)
|
|
||||||
{
|
|
||||||
showVulkanPrompt = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.LoadDefault();
|
ConfigurationState.Instance.LoadDefault();
|
||||||
|
|
||||||
showVulkanPrompt = true;
|
|
||||||
|
|
||||||
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
|
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,12 +205,10 @@ namespace Ryujinx
|
|||||||
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
|
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
|
||||||
showVulkanPrompt = false;
|
|
||||||
}
|
}
|
||||||
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
|
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
|
||||||
showVulkanPrompt = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,12 +319,7 @@ namespace Ryujinx
|
|||||||
|
|
||||||
if (CommandLineState.LaunchPathArg != null)
|
if (CommandLineState.LaunchPathArg != null)
|
||||||
{
|
{
|
||||||
ApplicationData applicationData = new()
|
mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
|
||||||
{
|
|
||||||
Path = CommandLineState.LaunchPathArg,
|
|
||||||
};
|
|
||||||
|
|
||||||
mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||||
@@ -349,35 +330,6 @@ namespace Ryujinx
|
|||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showVulkanPrompt)
|
|
||||||
{
|
|
||||||
var buttonTexts = new Dictionary<int, string>()
|
|
||||||
{
|
|
||||||
{ 0, "Yes (Vulkan)" },
|
|
||||||
{ 1, "No (OpenGL)" },
|
|
||||||
};
|
|
||||||
|
|
||||||
ResponseType response = GtkDialog.CreateCustomDialog(
|
|
||||||
"Ryujinx - Default graphics backend",
|
|
||||||
"Use Vulkan as default graphics backend?",
|
|
||||||
"Ryujinx now supports the Vulkan API. " +
|
|
||||||
"Vulkan greatly improves shader compilation performance, " +
|
|
||||||
"and fixes some graphical glitches; however, since it is a new feature, " +
|
|
||||||
"you may experience some issues that did not occur with OpenGL.\n\n" +
|
|
||||||
"Note that you will also lose any existing shader cache the first time you start a game " +
|
|
||||||
"on version 1.1.200 onwards, because Vulkan required changes to the shader cache that makes it incompatible with previous versions.\n\n" +
|
|
||||||
"Would you like to set Vulkan as the default graphics backend? " +
|
|
||||||
"You can change this at any time on the settings window.",
|
|
||||||
buttonTexts,
|
|
||||||
MessageType.Question);
|
|
||||||
|
|
||||||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = response == 0
|
|
||||||
? GraphicsBackend.Vulkan
|
|
||||||
: GraphicsBackend.OpenGl;
|
|
||||||
|
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
Application.Run();
|
Application.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user