Merge branch 'master' into master
This commit is contained in:
commit
e0e778e45d
4
.github/workflows/canary.yml
vendored
4
.github/workflows/canary.yml
vendored
@ -103,8 +103,8 @@ jobs:
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained
|
||||
|
||||
- name: Packing Windows builds
|
||||
if: matrix.platform.os == 'windows-latest'
|
||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -102,8 +102,8 @@ jobs:
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained
|
||||
|
||||
- name: Packing Windows builds
|
||||
if: matrix.platform.os == 'windows-latest'
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -175,3 +175,6 @@ PublishProfiles/
|
||||
|
||||
# Glade backup files
|
||||
*.glade~
|
||||
|
||||
# Ignore MacOS Attribute Files
|
||||
._*
|
||||
|
@ -38,7 +38,7 @@
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||
<PackageVersion Include="Gommon" Version="2.6.5" />
|
||||
<PackageVersion Include="Gommon" Version="2.6.6" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
@ -52,4 +52,4 @@
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,252 +0,0 @@
|
||||
using ARMeilleure.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a table of guest address to a value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||
public unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a level in an <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
public readonly struct Level
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index of the <see cref="Level"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the <see cref="Level"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mask which masks the bits used by the <see cref="Level"/>.
|
||||
/// </summary>
|
||||
public ulong Mask => ((1ul << Length) - 1) << Index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Level"/> structure with the specified
|
||||
/// <paramref name="index"/> and <paramref name="length"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the <see cref="Level"/></param>
|
||||
/// <param name="length">Length of the <see cref="Level"/></param>
|
||||
public Level(int index, int length)
|
||||
{
|
||||
(Index, Length) = (index, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the <see cref="Level"/> from the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Value of the <see cref="Level"/> from the specified guest <paramref name="address"/></returns>
|
||||
public int GetValue(ulong address)
|
||||
{
|
||||
return (int)((address & Mask) >> Index);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
private TEntry** _table;
|
||||
private readonly List<nint> _pages;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
public ulong Mask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
public Level[] Levels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default fill value of newly created leaf pages.
|
||||
/// </summary>
|
||||
public TEntry Fill { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
public nint Base
|
||||
{
|
||||
get
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
return (nint)GetRootPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
||||
/// <see cref="Level"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||
public AddressTable(Level[] levels)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(levels);
|
||||
|
||||
if (levels.Length < 2)
|
||||
{
|
||||
throw new ArgumentException("Table must be at least 2 levels deep.", nameof(levels));
|
||||
}
|
||||
|
||||
_pages = new List<nint>(capacity: 16);
|
||||
|
||||
Levels = levels;
|
||||
Mask = 0;
|
||||
|
||||
foreach (var level in Levels)
|
||||
{
|
||||
Mask |= level.Mask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||
/// <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
||||
public bool IsValid(ulong address)
|
||||
{
|
||||
return (address & ~Mask) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||
public ref TEntry GetValue(ulong address)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!IsValid(address))
|
||||
{
|
||||
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
|
||||
}
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
return ref GetPage(address)[Levels[^1].GetValue(address)];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the leaf page for the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
|
||||
private TEntry* GetPage(ulong address)
|
||||
{
|
||||
TEntry** page = GetRootPage();
|
||||
|
||||
for (int i = 0; i < Levels.Length - 1; i++)
|
||||
{
|
||||
ref Level level = ref Levels[i];
|
||||
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||
|
||||
if (nextPage == null)
|
||||
{
|
||||
ref Level nextLevel = ref Levels[i + 1];
|
||||
|
||||
nextPage = i == Levels.Length - 2 ?
|
||||
(TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true) :
|
||||
(TEntry*)Allocate(1 << nextLevel.Length, nint.Zero, leaf: false);
|
||||
}
|
||||
|
||||
page = (TEntry**)nextPage;
|
||||
}
|
||||
|
||||
return (TEntry*)page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
|
||||
private TEntry** GetRootPage()
|
||||
{
|
||||
if (_table == null)
|
||||
{
|
||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, fill: nint.Zero, leaf: false);
|
||||
}
|
||||
|
||||
return _table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a block of memory of the specified type and length.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of elements</typeparam>
|
||||
/// <param name="length">Number of elements</param>
|
||||
/// <param name="fill">Fill value</param>
|
||||
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||
/// <returns>Allocated block</returns>
|
||||
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||
{
|
||||
var size = sizeof(T) * length;
|
||||
var page = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
||||
var span = new Span<T>((void*)page, length);
|
||||
|
||||
span.Fill(fill);
|
||||
|
||||
_pages.Add(page);
|
||||
|
||||
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
|
||||
/// instance.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
foreach (var page in _pages)
|
||||
{
|
||||
Marshal.FreeHGlobal(page);
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
~AddressTable()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
@ -0,0 +1,44 @@
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a level in an <see cref="IAddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
public readonly struct AddressTableLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index of the <see cref="Level"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the <see cref="AddressTableLevel"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mask which masks the bits used by the <see cref="AddressTableLevel"/>.
|
||||
/// </summary>
|
||||
public ulong Mask => ((1ul << Length) - 1) << Index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddressTableLevel"/> structure with the specified
|
||||
/// <paramref name="index"/> and <paramref name="length"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the <see cref="AddressTableLevel"/></param>
|
||||
/// <param name="length">Length of the <see cref="AddressTableLevel"/></param>
|
||||
public AddressTableLevel(int index, int length)
|
||||
{
|
||||
(Index, Length) = (index, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/></returns>
|
||||
public int GetValue(ulong address)
|
||||
{
|
||||
return (int)((address & Mask) >> Index);
|
||||
}
|
||||
}
|
||||
}
|
75
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
75
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
@ -0,0 +1,75 @@
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
public static class AddressTablePresets
|
||||
{
|
||||
private static readonly AddressTableLevel[] _levels64Bit =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels32Bit =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new( 11, 28),
|
||||
new( 2, 9),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new( 10, 22),
|
||||
new( 1, 9),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new( 38, 1),
|
||||
new( 2, 36),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new( 31, 1),
|
||||
new( 1, 30),
|
||||
};
|
||||
|
||||
//high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization
|
||||
//low power will never run worse than non-sparse, but for most systems it won't be necessary
|
||||
//high power is always used, but I've left low power in here for future reference
|
||||
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
|
||||
{
|
||||
if (sparse)
|
||||
{
|
||||
if (lowPower)
|
||||
{
|
||||
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
||||
}
|
||||
else
|
||||
{
|
||||
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return for64Bits ? _levels64Bit : _levels32Bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
unsafe abstract class Allocator : IDisposable
|
||||
public unsafe abstract class Allocator : IDisposable
|
||||
{
|
||||
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
||||
{
|
||||
|
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the address table's bottom level is sparsely mapped.
|
||||
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
|
||||
/// </summary>
|
||||
bool Sparse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
ulong Mask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="AddressTableLevel"/>s used by the <see cref="IAddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
AddressTableLevel[] Levels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default fill value of newly created leaf pages.
|
||||
/// </summary>
|
||||
TEntry Fill { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
nint Base { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||
/// <see cref="IAddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
||||
bool IsValid(ulong address);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||
ref TEntry GetValue(ulong address);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
unsafe sealed class NativeAllocator : Allocator
|
||||
public unsafe sealed class NativeAllocator : Allocator
|
||||
{
|
||||
public static NativeAllocator Instance { get; } = new();
|
||||
|
||||
|
@ -193,6 +193,8 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Operand hostAddress;
|
||||
|
||||
var table = context.FunctionTable;
|
||||
|
||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||
// onto the dispatch stub.
|
||||
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
||||
@ -203,6 +205,30 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||
}
|
||||
else if (table.Sparse)
|
||||
{
|
||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||
// Deliberately attempts to avoid branches.
|
||||
|
||||
Operand tableBase = !context.HasPtc ?
|
||||
Const(table.Base) :
|
||||
Const(table.Base, Ptc.FunctionTableSymbol);
|
||||
|
||||
hostAddress = tableBase;
|
||||
|
||||
for (int i = 0; i < table.Levels.Length; i++)
|
||||
{
|
||||
var level = table.Levels[i];
|
||||
int clearBits = 64 - (level.Index + level.Length);
|
||||
|
||||
Operand index = context.ShiftLeft(
|
||||
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||
Const(3)
|
||||
);
|
||||
|
||||
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hostAddress = !context.HasPtc ?
|
||||
|
@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
||||
{
|
||||
public static class NativeSignalHandlerGenerator
|
||||
{
|
||||
public const int MaxTrackedRanges = 8;
|
||||
public const int MaxTrackedRanges = 16;
|
||||
|
||||
private const int StructAddressOffset = 0;
|
||||
private const int StructWriteOffset = 4;
|
||||
|
@ -46,7 +46,7 @@ namespace ARMeilleure.Translation
|
||||
public IMemoryManager Memory { get; }
|
||||
|
||||
public EntryTable<uint> CountTable { get; }
|
||||
public AddressTable<ulong> FunctionTable { get; }
|
||||
public IAddressTable<ulong> FunctionTable { get; }
|
||||
public TranslatorStubs Stubs { get; }
|
||||
|
||||
public ulong EntryAddress { get; }
|
||||
@ -62,7 +62,7 @@ namespace ARMeilleure.Translation
|
||||
public ArmEmitterContext(
|
||||
IMemoryManager memory,
|
||||
EntryTable<uint> countTable,
|
||||
AddressTable<ulong> funcTable,
|
||||
IAddressTable<ulong> funcTable,
|
||||
TranslatorStubs stubs,
|
||||
ulong entryAddress,
|
||||
bool highCq,
|
||||
|
@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 6992; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
@ -41,6 +41,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||
public static readonly Symbol FunctionTableSymbol = new(SymbolType.Special, 4);
|
||||
|
||||
private const byte FillingByte = 0x00;
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
@ -101,7 +102,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
Disable();
|
||||
}
|
||||
|
||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
|
||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
||||
{
|
||||
Wait();
|
||||
|
||||
@ -127,6 +128,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||
_memoryMode = memoryMode;
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}");
|
||||
|
||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
||||
|
||||
@ -140,8 +143,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
Directory.CreateDirectory(workPathBackup);
|
||||
}
|
||||
|
||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
|
||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
||||
|
||||
PreLoad();
|
||||
Profiler.PreLoad();
|
||||
@ -706,6 +709,10 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
imm = translator.Stubs.DispatchStub;
|
||||
}
|
||||
else if (symbol == FunctionTableSymbol)
|
||||
{
|
||||
imm = translator.FunctionTable.Base;
|
||||
}
|
||||
|
||||
if (imm == null)
|
||||
{
|
||||
|
@ -22,33 +22,13 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
public class Translator
|
||||
{
|
||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6),
|
||||
};
|
||||
|
||||
private readonly IJitMemoryAllocator _allocator;
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
internal AddressTable<ulong> FunctionTable { get; }
|
||||
internal IAddressTable<ulong> FunctionTable { get; }
|
||||
internal EntryTable<uint> CountTable { get; }
|
||||
internal TranslatorStubs Stubs { get; }
|
||||
internal TranslatorQueue Queue { get; }
|
||||
@ -57,7 +37,7 @@ namespace ARMeilleure.Translation
|
||||
private Thread[] _backgroundTranslationThreads;
|
||||
private volatile int _threadCount;
|
||||
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, IAddressTable<ulong> functionTable)
|
||||
{
|
||||
_allocator = allocator;
|
||||
Memory = memory;
|
||||
@ -72,15 +52,15 @@ namespace ARMeilleure.Translation
|
||||
|
||||
CountTable = new EntryTable<uint>();
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
FunctionTable = functionTable;
|
||||
Stubs = new TranslatorStubs(FunctionTable);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
{
|
||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
||||
return _ptc;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
private readonly IAddressTable<ulong> _functionTable;
|
||||
private readonly Lazy<nint> _dispatchStub;
|
||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||
@ -86,7 +86,7 @@ namespace ARMeilleure.Translation
|
||||
/// </summary>
|
||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(AddressTable<ulong> functionTable)
|
||||
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(functionTable);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public class KeyboardHotkeys
|
||||
{
|
||||
public Key ToggleVsync { get; set; }
|
||||
public Key ToggleVSyncMode { get; set; }
|
||||
public Key Screenshot { get; set; }
|
||||
public Key ShowUI { get; set; }
|
||||
public Key Pause { get; set; }
|
||||
@ -11,5 +11,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
public Key ResScaleDown { get; set; }
|
||||
public Key VolumeUp { get; set; }
|
||||
public Key VolumeDown { get; set; }
|
||||
public Key CustomVSyncIntervalIncrement { get; set; }
|
||||
public Key CustomVSyncIntervalDecrement { get; set; }
|
||||
}
|
||||
}
|
||||
|
9
src/Ryujinx.Common/Configuration/VSyncMode.cs
Normal file
9
src/Ryujinx.Common/Configuration/VSyncMode.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
public enum VSyncMode
|
||||
{
|
||||
Switch,
|
||||
Unbounded,
|
||||
Custom
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ryujinx.Common
|
||||
@ -35,5 +36,13 @@ namespace Ryujinx.Common
|
||||
public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);
|
||||
|
||||
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||
|
||||
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
||||
IsCanaryBuild
|
||||
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
||||
: $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}";
|
||||
|
||||
public static string GetChangelogForVersion(Version version) =>
|
||||
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}";
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
482
src/Ryujinx.Cpu/AddressTable.cs
Normal file
482
src/Ryujinx.Cpu/AddressTable.cs
Normal file
@ -0,0 +1,482 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Cpu.MemoryEhMeilleure;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a table of guest address to a value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a page of the address table.
|
||||
/// </summary>
|
||||
private readonly struct AddressTablePage
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the allocation belongs to a sparse block, false otherwise.
|
||||
/// </summary>
|
||||
public readonly bool IsSparse;
|
||||
|
||||
/// <summary>
|
||||
/// Base address for the page.
|
||||
/// </summary>
|
||||
public readonly IntPtr Address;
|
||||
|
||||
public AddressTablePage(bool isSparse, IntPtr address)
|
||||
{
|
||||
IsSparse = isSparse;
|
||||
Address = address;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
|
||||
/// </summary>
|
||||
private readonly struct TableSparseBlock : IDisposable
|
||||
{
|
||||
public readonly SparseMemoryBlock Block;
|
||||
private readonly TrackingEventDelegate _trackingEvent;
|
||||
|
||||
public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
|
||||
{
|
||||
var block = new SparseMemoryBlock(size, pageInit, null);
|
||||
|
||||
_trackingEvent = (ulong address, ulong size, bool write) =>
|
||||
{
|
||||
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||
ensureMapped((IntPtr)pointer);
|
||||
return pointer;
|
||||
};
|
||||
|
||||
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||
(nuint)block.Block.Pointer,
|
||||
(nuint)(block.Block.Pointer + (IntPtr)block.Block.Size),
|
||||
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||
|
||||
if (!added)
|
||||
{
|
||||
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
|
||||
}
|
||||
|
||||
Block = block;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
|
||||
|
||||
Block.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
private TEntry** _table;
|
||||
private readonly List<AddressTablePage> _pages;
|
||||
private TEntry _fill;
|
||||
|
||||
private readonly MemoryBlock _sparseFill;
|
||||
private readonly SparseMemoryBlock _fillBottomLevel;
|
||||
private readonly TEntry* _fillBottomLevelPtr;
|
||||
|
||||
private readonly List<TableSparseBlock> _sparseReserved;
|
||||
private readonly ReaderWriterLockSlim _sparseLock;
|
||||
|
||||
private ulong _sparseBlockSize;
|
||||
private ulong _sparseReservedOffset;
|
||||
|
||||
public bool Sparse { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ulong Mask { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AddressTableLevel[] Levels { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TEntry Fill
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fill;
|
||||
}
|
||||
set
|
||||
{
|
||||
UpdateFill(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr Base
|
||||
{
|
||||
get
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
return (IntPtr)GetRootPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
||||
/// <see cref="Level"/>.
|
||||
/// </summary>
|
||||
/// <param name="levels">Levels for the address table</param>
|
||||
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||
public AddressTable(AddressTableLevel[] levels, bool sparse)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(levels);
|
||||
|
||||
_pages = new List<AddressTablePage>(capacity: 16);
|
||||
|
||||
Levels = levels;
|
||||
Mask = 0;
|
||||
|
||||
foreach (var level in Levels)
|
||||
{
|
||||
Mask |= level.Mask;
|
||||
}
|
||||
|
||||
Sparse = sparse;
|
||||
|
||||
if (sparse)
|
||||
{
|
||||
// If the address table is sparse, allocate a fill block
|
||||
|
||||
_sparseFill = new MemoryBlock(268435456ul, MemoryAllocationFlags.Mirrorable); //low Power TC uses size: 65536ul
|
||||
|
||||
ulong bottomLevelSize = (1ul << levels.Last().Length) * (ulong)sizeof(TEntry);
|
||||
|
||||
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
||||
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
||||
|
||||
_sparseReserved = new List<TableSparseBlock>();
|
||||
_sparseLock = new ReaderWriterLockSlim();
|
||||
|
||||
_sparseBlockSize = bottomLevelSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an <see cref="AddressTable{TEntry}"/> instance for an ARM function table.
|
||||
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
|
||||
/// </summary>
|
||||
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
|
||||
/// <param name="type">Memory manager type</param>
|
||||
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
|
||||
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type)
|
||||
{
|
||||
// Assume software memory means that we don't want to use any signal handlers.
|
||||
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable;
|
||||
|
||||
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the fill value for the bottom level of the table.
|
||||
/// </summary>
|
||||
/// <param name="fillValue">New fill value</param>
|
||||
private void UpdateFill(TEntry fillValue)
|
||||
{
|
||||
if (_sparseFill != null)
|
||||
{
|
||||
Span<byte> span = _sparseFill.GetSpan(0, (int)_sparseFill.Size);
|
||||
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
|
||||
}
|
||||
|
||||
_fill = fillValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signal that the given code range exists.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="size"></param>
|
||||
public void SignalCodeRange(ulong address, ulong size)
|
||||
{
|
||||
AddressTableLevel bottom = Levels.Last();
|
||||
ulong bottomLevelEntries = 1ul << bottom.Length;
|
||||
|
||||
ulong entryIndex = address >> bottom.Index;
|
||||
ulong entries = size >> bottom.Index;
|
||||
entries += entryIndex - BitUtils.AlignDown(entryIndex, bottomLevelEntries);
|
||||
|
||||
_sparseBlockSize = Math.Max(_sparseBlockSize, BitUtils.AlignUp(entries, bottomLevelEntries) * (ulong)sizeof(TEntry));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsValid(ulong address)
|
||||
{
|
||||
return (address & ~Mask) == 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ref TEntry GetValue(ulong address)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!IsValid(address))
|
||||
{
|
||||
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
|
||||
}
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
TEntry* page = GetPage(address);
|
||||
|
||||
int index = Levels[^1].GetValue(address);
|
||||
|
||||
EnsureMapped((IntPtr)(page + index));
|
||||
|
||||
return ref page[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the leaf page for the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
|
||||
private TEntry* GetPage(ulong address)
|
||||
{
|
||||
TEntry** page = GetRootPage();
|
||||
|
||||
for (int i = 0; i < Levels.Length - 1; i++)
|
||||
{
|
||||
ref AddressTableLevel level = ref Levels[i];
|
||||
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||
|
||||
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
||||
{
|
||||
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
||||
|
||||
if (i == Levels.Length - 2)
|
||||
{
|
||||
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false);
|
||||
}
|
||||
}
|
||||
|
||||
page = (TEntry**)nextPage;
|
||||
}
|
||||
|
||||
return (TEntry*)page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
|
||||
/// </summary>
|
||||
/// <param name="ptr">Pointer to be mapped</param>
|
||||
private void EnsureMapped(IntPtr ptr)
|
||||
{
|
||||
if (Sparse)
|
||||
{
|
||||
// Check sparse allocations to see if the pointer is in any of them.
|
||||
// Ensure the page is committed if there's a match.
|
||||
|
||||
_sparseLock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (TableSparseBlock reserved in _sparseReserved)
|
||||
{
|
||||
SparseMemoryBlock sparse = reserved.Block;
|
||||
|
||||
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||
{
|
||||
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sparseLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the fill value for a non-leaf level of the table.
|
||||
/// </summary>
|
||||
/// <param name="level">Level to get the fill value for</param>
|
||||
/// <returns>The fill value</returns>
|
||||
private IntPtr GetFillValue(int level)
|
||||
{
|
||||
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||
{
|
||||
return (IntPtr)_fillBottomLevelPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
|
||||
private TEntry** GetRootPage()
|
||||
{
|
||||
if (_table == null)
|
||||
{
|
||||
if (Levels.Length == 1)
|
||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true);
|
||||
else
|
||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false);
|
||||
}
|
||||
|
||||
return _table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a leaf page with the fill value.
|
||||
/// </summary>
|
||||
/// <param name="page">Page to initialize</param>
|
||||
private void InitLeafPage(Span<byte> page)
|
||||
{
|
||||
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reserve a new sparse block, and add it to the list.
|
||||
/// </summary>
|
||||
/// <returns>The new sparse block that was added</returns>
|
||||
private TableSparseBlock ReserveNewSparseBlock()
|
||||
{
|
||||
var block = new TableSparseBlock(_sparseBlockSize, EnsureMapped, InitLeafPage);
|
||||
|
||||
_sparseReserved.Add(block);
|
||||
_sparseReservedOffset = 0;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a block of memory of the specified type and length.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of elements</typeparam>
|
||||
/// <param name="length">Number of elements</param>
|
||||
/// <param name="fill">Fill value</param>
|
||||
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||
/// <returns>Allocated block</returns>
|
||||
private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||
{
|
||||
var size = sizeof(T) * length;
|
||||
|
||||
AddressTablePage page;
|
||||
|
||||
if (Sparse && leaf)
|
||||
{
|
||||
_sparseLock.EnterWriteLock();
|
||||
|
||||
SparseMemoryBlock block;
|
||||
|
||||
if (_sparseReserved.Count == 0)
|
||||
{
|
||||
block = ReserveNewSparseBlock().Block;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = _sparseReserved.Last().Block;
|
||||
|
||||
if (_sparseReservedOffset == block.Block.Size)
|
||||
{
|
||||
block = ReserveNewSparseBlock().Block;
|
||||
}
|
||||
}
|
||||
|
||||
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||
|
||||
_sparseReservedOffset += (ulong)size;
|
||||
|
||||
_sparseLock.ExitWriteLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
var address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
|
||||
page = new AddressTablePage(false, address);
|
||||
|
||||
var span = new Span<T>((void*)page.Address, length);
|
||||
span.Fill(fill);
|
||||
}
|
||||
|
||||
_pages.Add(page);
|
||||
|
||||
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||
|
||||
return page.Address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
|
||||
/// instance.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
foreach (var page in _pages)
|
||||
{
|
||||
if (!page.IsSparse)
|
||||
{
|
||||
Marshal.FreeHGlobal(page.Address);
|
||||
}
|
||||
}
|
||||
|
||||
if (Sparse)
|
||||
{
|
||||
foreach (TableSparseBlock block in _sparseReserved)
|
||||
{
|
||||
block.Dispose();
|
||||
}
|
||||
|
||||
_sparseReserved.Clear();
|
||||
|
||||
_fillBottomLevel.Dispose();
|
||||
_sparseFill.Dispose();
|
||||
_sparseLock.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
~AddressTable()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
}
|
||||
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
{
|
||||
return new DummyDiskCacheLoadState();
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Cpu
|
||||
/// <param name="displayVersion">Version of the application</param>
|
||||
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
||||
/// <returns>Disk cache load progress reporter and manager</returns>
|
||||
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled);
|
||||
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
||||
|
@ -1,3 +1,4 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
@ -9,11 +10,13 @@ namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
private readonly Translator _translator;
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
|
||||
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit);
|
||||
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
|
||||
|
||||
if (memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
@ -47,14 +50,15 @@ namespace Ryujinx.Cpu.Jit
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
{
|
||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled));
|
||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
_functionTable.SignalCodeRange(address, size);
|
||||
_translator.PrepareCodeRange(address, size);
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
bool isTail = false)
|
||||
{
|
||||
int tempRegister;
|
||||
int tempGuestAddress = -1;
|
||||
|
||||
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||
funcTable is { Sparse: true };
|
||||
|
||||
if (guestAddress.Kind == OperandKind.Constant)
|
||||
{
|
||||
@ -153,9 +157,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
else
|
||||
{
|
||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||
|
||||
if (inlineLookup && guestAddress.Value == 0)
|
||||
{
|
||||
// X0 will be overwritten. Move the address to a temp register.
|
||||
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
||||
asm.Mov(Register(tempGuestAddress), guestAddress);
|
||||
}
|
||||
}
|
||||
|
||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
||||
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
||||
|
||||
if (!isTail)
|
||||
{
|
||||
@ -176,6 +187,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||
}
|
||||
else if (inlineLookup)
|
||||
{
|
||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||
|
||||
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||
|
||||
if (tempGuestAddress != -1)
|
||||
{
|
||||
guestAddress = Register(tempGuestAddress);
|
||||
}
|
||||
|
||||
ulong tableBase = (ulong)funcTable.Base;
|
||||
|
||||
// Index into the table.
|
||||
asm.Mov(rn, tableBase);
|
||||
|
||||
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||
{
|
||||
var level = funcTable.Levels[i];
|
||||
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||
asm.Lsl(indexReg, indexReg, Const(3));
|
||||
|
||||
// Index into the page.
|
||||
asm.Add(rn, rn, indexReg);
|
||||
|
||||
// Load the page address.
|
||||
asm.LdrRiUn(rn, rn, 0);
|
||||
}
|
||||
|
||||
if (tempGuestAddress != -1)
|
||||
{
|
||||
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Mov(rn, (ulong)funcPtr);
|
||||
@ -252,5 +297,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static Operand Const(long value, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(type, (ulong)value);
|
||||
}
|
||||
|
||||
private static int NextFreeRegister(int start, int avoid)
|
||||
{
|
||||
if (start == avoid)
|
||||
{
|
||||
start++;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,6 +305,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
bool isTail = false)
|
||||
{
|
||||
int tempRegister;
|
||||
int tempGuestAddress = -1;
|
||||
|
||||
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||
funcTable is { Sparse: true };
|
||||
|
||||
if (guestAddress.Kind == OperandKind.Constant)
|
||||
{
|
||||
@ -318,9 +322,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
else
|
||||
{
|
||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||
|
||||
if (inlineLookup && guestAddress.Value == 0)
|
||||
{
|
||||
// X0 will be overwritten. Move the address to a temp register.
|
||||
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
||||
asm.Mov(Register(tempGuestAddress), guestAddress);
|
||||
}
|
||||
}
|
||||
|
||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
||||
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
||||
|
||||
if (!isTail)
|
||||
{
|
||||
@ -341,6 +352,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||
}
|
||||
else if (inlineLookup)
|
||||
{
|
||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||
|
||||
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||
|
||||
if (tempGuestAddress != -1)
|
||||
{
|
||||
guestAddress = Register(tempGuestAddress);
|
||||
}
|
||||
|
||||
ulong tableBase = (ulong)funcTable.Base;
|
||||
|
||||
// Index into the table.
|
||||
asm.Mov(rn, tableBase);
|
||||
|
||||
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||
{
|
||||
var level = funcTable.Levels[i];
|
||||
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||
asm.Lsl(indexReg, indexReg, Const(3));
|
||||
|
||||
// Index into the page.
|
||||
asm.Add(rn, rn, indexReg);
|
||||
|
||||
// Load the page address.
|
||||
asm.LdrRiUn(rn, rn, 0);
|
||||
}
|
||||
|
||||
if (tempGuestAddress != -1)
|
||||
{
|
||||
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Mov(rn, (ulong)funcPtr);
|
||||
@ -613,5 +658,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static Operand Const(long value, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(type, (ulong)value);
|
||||
}
|
||||
|
||||
private static int NextFreeRegister(int start, int avoid)
|
||||
{
|
||||
if (start == avoid)
|
||||
{
|
||||
start++;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.Cpu.LightningJit.State;
|
||||
@ -8,11 +9,16 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
private readonly Translator _translator;
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
|
||||
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_translator = new Translator(memory, for64Bit);
|
||||
|
||||
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||
|
||||
_translator = new Translator(memory, _functionTable);
|
||||
|
||||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
@ -40,7 +46,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||
{
|
||||
return new DummyDiskCacheLoadState();
|
||||
}
|
||||
@ -48,6 +54,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
/// <inheritdoc/>
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
_functionTable.SignalCodeRange(address, size);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -19,25 +19,6 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
// Should be enabled on platforms that enforce W^X.
|
||||
private static bool IsNoWxPlatform => false;
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(23, 9),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6),
|
||||
};
|
||||
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
private readonly NoWxCache _noWxCache;
|
||||
private bool _disposed;
|
||||
@ -47,7 +28,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
internal TranslatorStubs Stubs { get; }
|
||||
internal IMemoryManager Memory { get; }
|
||||
|
||||
public Translator(IMemoryManager memory, bool for64Bits)
|
||||
public Translator(IMemoryManager memory, AddressTable<ulong> functionTable)
|
||||
{
|
||||
Memory = memory;
|
||||
|
||||
@ -63,7 +44,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
}
|
||||
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
FunctionTable = functionTable;
|
||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
@ -23,7 +23,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
private readonly IAddressTable<ulong> _functionTable;
|
||||
private readonly NoWxCache _noWxCache;
|
||||
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
|
||||
private readonly nint _getFunctionAddress;
|
||||
@ -79,7 +79,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||
/// <param name="noWxCache">Cache used on platforms that enforce W^X, otherwise should be null</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(AddressTable<ulong> functionTable, NoWxCache noWxCache)
|
||||
public TranslatorStubs(IAddressTable<ulong> functionTable, NoWxCache noWxCache)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(functionTable);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void SetSize(int width, int height);
|
||||
|
||||
void ChangeVSyncMode(bool vsyncEnabled);
|
||||
void ChangeVSyncMode(VSyncMode vSyncMode);
|
||||
|
||||
void SetAntiAliasing(AntiAliasing antialiasing);
|
||||
void SetScalingFilter(ScalingFilter type);
|
||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_impl.Window.SetSize(width, height);
|
||||
}
|
||||
|
||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
||||
public void ChangeVSyncMode(VSyncMode vSyncMode) { }
|
||||
|
||||
public void SetAntiAliasing(AntiAliasing effect) { }
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
9
src/Ryujinx.Graphics.GAL/VSyncMode.cs
Normal file
9
src/Ryujinx.Graphics.GAL/VSyncMode.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum VSyncMode
|
||||
{
|
||||
Switch,
|
||||
Unbounded,
|
||||
Custom
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
||||
}
|
||||
|
||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
||||
public void ChangeVSyncMode(VSyncMode vSyncMode) { }
|
||||
|
||||
public void SetSize(int width, int height)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -182,6 +182,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Prevent the sum of descriptors from exceeding MaxPushDescriptors
|
||||
int totalDescriptors = 0;
|
||||
foreach (ResourceDescriptor desc in layout.Sets.First().Descriptors)
|
||||
{
|
||||
if (!reserved.Contains(desc.Binding))
|
||||
totalDescriptors += desc.Count;
|
||||
}
|
||||
if (totalDescriptors > gd.Capabilities.MaxPushDescriptors)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private int _width;
|
||||
private int _height;
|
||||
private bool _vsyncEnabled;
|
||||
private VSyncMode _vSyncMode;
|
||||
private bool _swapchainIsDirty;
|
||||
private VkFormat _format;
|
||||
private AntiAliasing _currentAntiAliasing;
|
||||
@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
ImageArrayLayers = 1,
|
||||
PreTransform = capabilities.CurrentTransform,
|
||||
CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
|
||||
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
|
||||
PresentMode = ChooseSwapPresentMode(presentModes, _vSyncMode),
|
||||
Clipped = true,
|
||||
};
|
||||
|
||||
@ -279,9 +279,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
|
||||
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, VSyncMode vSyncMode)
|
||||
{
|
||||
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
|
||||
if (vSyncMode == VSyncMode.Unbounded && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
|
||||
{
|
||||
return PresentModeKHR.ImmediateKhr;
|
||||
}
|
||||
@ -634,9 +634,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_swapchainIsDirty = true;
|
||||
}
|
||||
|
||||
public override void ChangeVSyncMode(bool vsyncEnabled)
|
||||
public override void ChangeVSyncMode(VSyncMode vSyncMode)
|
||||
{
|
||||
_vsyncEnabled = vsyncEnabled;
|
||||
_vSyncMode = vSyncMode;
|
||||
//present mode may change, so mark the swapchain for recreation
|
||||
_swapchainIsDirty = true;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public abstract void Dispose();
|
||||
public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
|
||||
public abstract void SetSize(int width, int height);
|
||||
public abstract void ChangeVSyncMode(bool vsyncEnabled);
|
||||
public abstract void ChangeVSyncMode(VSyncMode vSyncMode);
|
||||
public abstract void SetAntiAliasing(AntiAliasing effect);
|
||||
public abstract void SetScalingFilter(ScalingFilter scalerType);
|
||||
public abstract void SetScalingFilterLevel(float scale);
|
||||
|
@ -6,6 +6,7 @@
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
<IsRoslynComponent>true</IsRoslynComponent>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
@ -84,9 +85,14 @@ namespace Ryujinx.HLE
|
||||
internal readonly RegionCode Region;
|
||||
|
||||
/// <summary>
|
||||
/// Control the initial state of the vertical sync in the SurfaceFlinger service.
|
||||
/// Control the initial state of the present interval in the SurfaceFlinger service (previously Vsync).
|
||||
/// </summary>
|
||||
internal readonly bool EnableVsync;
|
||||
internal readonly VSyncMode VSyncMode;
|
||||
|
||||
/// <summary>
|
||||
/// Control the custom VSync interval, if enabled and active.
|
||||
/// </summary>
|
||||
internal readonly int CustomVSyncInterval;
|
||||
|
||||
/// <summary>
|
||||
/// Control the initial state of the docked mode.
|
||||
@ -195,7 +201,7 @@ namespace Ryujinx.HLE
|
||||
IHostUIHandler hostUIHandler,
|
||||
SystemLanguage systemLanguage,
|
||||
RegionCode region,
|
||||
bool enableVsync,
|
||||
VSyncMode vSyncMode,
|
||||
bool enableDockedMode,
|
||||
bool enablePtc,
|
||||
bool enableInternetAccess,
|
||||
@ -212,7 +218,8 @@ namespace Ryujinx.HLE
|
||||
MultiplayerMode multiplayerMode,
|
||||
bool multiplayerDisableP2p,
|
||||
string multiplayerLdnPassphrase,
|
||||
string multiplayerLdnServer)
|
||||
string multiplayerLdnServer,
|
||||
int customVSyncInterval)
|
||||
{
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
LibHacHorizonManager = libHacHorizonManager;
|
||||
@ -225,7 +232,8 @@ namespace Ryujinx.HLE
|
||||
HostUIHandler = hostUIHandler;
|
||||
SystemLanguage = systemLanguage;
|
||||
Region = region;
|
||||
EnableVsync = enableVsync;
|
||||
VSyncMode = vSyncMode;
|
||||
CustomVSyncInterval = customVSyncInterval;
|
||||
EnableDockedMode = enableDockedMode;
|
||||
EnablePtc = enablePtc;
|
||||
EnableInternetAccess = enableInternetAccess;
|
||||
|
@ -13,7 +13,8 @@ namespace Ryujinx.HLE.HOS
|
||||
string displayVersion,
|
||||
bool diskCacheEnabled,
|
||||
ulong codeAddress,
|
||||
ulong codeSize);
|
||||
ulong codeSize,
|
||||
string cacheSelector);
|
||||
}
|
||||
|
||||
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
||||
@ -67,10 +68,11 @@ namespace Ryujinx.HLE.HOS
|
||||
string displayVersion,
|
||||
bool diskCacheEnabled,
|
||||
ulong codeAddress,
|
||||
ulong codeSize)
|
||||
ulong codeSize,
|
||||
string cacheSelector)
|
||||
{
|
||||
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
|
||||
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled);
|
||||
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled, cacheSelector);
|
||||
}
|
||||
|
||||
public void InvalidateCacheRegion(ulong address, ulong size)
|
||||
|
@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS
|
||||
}
|
||||
}
|
||||
|
||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
|
||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, "default"); //Ready for exefs profiles
|
||||
|
||||
return processContext;
|
||||
}
|
||||
|
@ -10,13 +10,12 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
{
|
||||
class SurfaceFlinger : IConsumerListener, IDisposable
|
||||
{
|
||||
private const int TargetFps = 60;
|
||||
|
||||
private readonly Switch _device;
|
||||
|
||||
private readonly Dictionary<long, Layer> _layers;
|
||||
@ -32,6 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
private readonly long _spinTicks;
|
||||
private readonly long _1msTicks;
|
||||
|
||||
private VSyncMode _vSyncMode;
|
||||
private long _targetVSyncInterval;
|
||||
|
||||
private int _swapInterval;
|
||||
private int _swapIntervalDelay;
|
||||
|
||||
@ -88,7 +90,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
}
|
||||
else
|
||||
{
|
||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||
_ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval;
|
||||
_targetVSyncInterval = _device.TargetVSyncInterval;
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,15 +373,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
|
||||
if (acquireStatus == Status.Success)
|
||||
{
|
||||
// If device vsync is disabled, reflect the change.
|
||||
if (!_device.EnableDeviceVsync)
|
||||
if (_device.VSyncMode == VSyncMode.Unbounded)
|
||||
{
|
||||
if (_swapInterval != 0)
|
||||
{
|
||||
UpdateSwapInterval(0);
|
||||
_vSyncMode = _device.VSyncMode;
|
||||
}
|
||||
}
|
||||
else if (item.SwapInterval != _swapInterval)
|
||||
else if (_device.VSyncMode != _vSyncMode)
|
||||
{
|
||||
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
|
||||
_vSyncMode = _device.VSyncMode;
|
||||
}
|
||||
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
|
||||
{
|
||||
UpdateSwapInterval(item.SwapInterval);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -27,7 +27,11 @@ namespace Ryujinx.HLE
|
||||
public TamperMachine TamperMachine { get; }
|
||||
public IHostUIHandler UIHandler { get; }
|
||||
|
||||
public bool EnableDeviceVsync { get; set; }
|
||||
public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch;
|
||||
public bool CustomVSyncIntervalEnabled { get; set; } = false;
|
||||
public int CustomVSyncInterval { get; set; }
|
||||
|
||||
public long TargetVSyncInterval { get; set; } = 60;
|
||||
|
||||
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
||||
|
||||
@ -59,12 +63,14 @@ namespace Ryujinx.HLE
|
||||
System.State.SetLanguage(Configuration.SystemLanguage);
|
||||
System.State.SetRegion(Configuration.Region);
|
||||
|
||||
EnableDeviceVsync = Configuration.EnableVsync;
|
||||
VSyncMode = Configuration.VSyncMode;
|
||||
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
||||
System.State.DockedMode = Configuration.EnableDockedMode;
|
||||
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||
System.EnablePtc = Configuration.EnablePtc;
|
||||
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
||||
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
||||
UpdateVSyncInterval();
|
||||
#pragma warning restore IDE0055
|
||||
}
|
||||
|
||||
@ -75,6 +81,34 @@ namespace Ryujinx.HLE
|
||||
Gpu.GPFifo.DispatchCalls();
|
||||
}
|
||||
|
||||
public void IncrementCustomVSyncInterval()
|
||||
{
|
||||
CustomVSyncInterval += 1;
|
||||
UpdateVSyncInterval();
|
||||
}
|
||||
|
||||
public void DecrementCustomVSyncInterval()
|
||||
{
|
||||
CustomVSyncInterval -= 1;
|
||||
UpdateVSyncInterval();
|
||||
}
|
||||
|
||||
public void UpdateVSyncInterval()
|
||||
{
|
||||
switch (VSyncMode)
|
||||
{
|
||||
case VSyncMode.Custom:
|
||||
TargetVSyncInterval = CustomVSyncInterval;
|
||||
break;
|
||||
case VSyncMode.Switch:
|
||||
TargetVSyncInterval = 60;
|
||||
break;
|
||||
case VSyncMode.Unbounded:
|
||||
TargetVSyncInterval = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
||||
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
|
||||
public bool LoadNca(string ncaFile) => Processes.LoadNca(ncaFile);
|
||||
|
@ -115,8 +115,11 @@ namespace Ryujinx.Headless.SDL2
|
||||
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
|
||||
public int FsGlobalAccessLogMode { get; set; }
|
||||
|
||||
[Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")]
|
||||
public bool DisableVSync { get; set; }
|
||||
[Option("vsync-mode", Required = false, Default = VSyncMode.Switch, HelpText = "Sets the emulated VSync mode (Switch, Unbounded, or Custom).")]
|
||||
public VSyncMode VSyncMode { get; set; }
|
||||
|
||||
[Option("custom-refresh-rate", Required = false, Default = 90, HelpText = "Sets the custom refresh rate target value (integer).")]
|
||||
public int CustomVSyncInterval { get; set; }
|
||||
|
||||
[Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")]
|
||||
public bool DisableShaderCache { get; set; }
|
||||
|
@ -563,7 +563,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
window,
|
||||
options.SystemLanguage,
|
||||
options.SystemRegion,
|
||||
!options.DisableVSync,
|
||||
options.VSyncMode,
|
||||
!options.DisableDockedMode,
|
||||
!options.DisablePTC,
|
||||
options.EnableInternetAccess,
|
||||
@ -580,7 +580,8 @@ namespace Ryujinx.Headless.SDL2
|
||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
||||
false,
|
||||
"",
|
||||
"");
|
||||
"",
|
||||
options.CustomVSyncInterval);
|
||||
|
||||
return new Switch(configuration);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,7 +3,7 @@ using System;
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
class StatusUpdatedEventArgs(
|
||||
bool vSyncEnabled,
|
||||
string vSyncMode,
|
||||
string dockedMode,
|
||||
string aspectRatio,
|
||||
string gameStatus,
|
||||
@ -11,7 +11,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
string gpuName)
|
||||
: EventArgs
|
||||
{
|
||||
public bool VSyncEnabled = vSyncEnabled;
|
||||
public string VSyncMode = vSyncMode;
|
||||
public string DockedMode = dockedMode;
|
||||
public string AspectRatio = aspectRatio;
|
||||
public string GameStatus = gameStatus;
|
||||
|
@ -314,7 +314,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
|
||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||
Device.EnableDeviceVsync,
|
||||
Device.VSyncMode.ToString(),
|
||||
dockedMode,
|
||||
Device.Configuration.AspectRatio.ToText(),
|
||||
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,8 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
125
src/Ryujinx.Memory/SparseMemoryBlock.cs
Normal file
125
src/Ryujinx.Memory/SparseMemoryBlock.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
public delegate void PageInitDelegate(Span<byte> page);
|
||||
|
||||
public class SparseMemoryBlock : IDisposable
|
||||
{
|
||||
private const ulong MapGranularity = 1UL << 17;
|
||||
|
||||
private readonly PageInitDelegate _pageInit;
|
||||
|
||||
private readonly object _lock = new object();
|
||||
private readonly ulong _pageSize;
|
||||
private readonly MemoryBlock _reservedBlock;
|
||||
private readonly List<MemoryBlock> _mappedBlocks;
|
||||
private ulong _mappedBlockUsage;
|
||||
|
||||
private readonly ulong[] _mappedPageBitmap;
|
||||
|
||||
public MemoryBlock Block => _reservedBlock;
|
||||
|
||||
public SparseMemoryBlock(ulong size, PageInitDelegate pageInit, MemoryBlock fill)
|
||||
{
|
||||
_pageSize = MemoryBlock.GetPageSize();
|
||||
_reservedBlock = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
|
||||
_mappedBlocks = new List<MemoryBlock>();
|
||||
_pageInit = pageInit;
|
||||
|
||||
int pages = (int)BitUtils.DivRoundUp(size, _pageSize);
|
||||
int bitmapEntries = BitUtils.DivRoundUp(pages, 64);
|
||||
_mappedPageBitmap = new ulong[bitmapEntries];
|
||||
|
||||
if (fill != null)
|
||||
{
|
||||
// Fill the block with mappings from the fill block.
|
||||
|
||||
if (fill.Size % _pageSize != 0)
|
||||
{
|
||||
throw new ArgumentException("Fill memory block should be page aligned.", nameof(fill));
|
||||
}
|
||||
|
||||
int repeats = (int)BitUtils.DivRoundUp(size, fill.Size);
|
||||
|
||||
ulong offset = 0;
|
||||
for (int i = 0; i < repeats; i++)
|
||||
{
|
||||
_reservedBlock.MapView(fill, 0, offset, Math.Min(fill.Size, size - offset));
|
||||
offset += fill.Size;
|
||||
}
|
||||
}
|
||||
|
||||
// If a fill block isn't provided, the pages that aren't EnsureMapped are unmapped.
|
||||
// The caller can rely on signal handler to fill empty pages instead.
|
||||
}
|
||||
|
||||
private void MapPage(ulong pageOffset)
|
||||
{
|
||||
// Take a page from the latest mapped block.
|
||||
MemoryBlock block = _mappedBlocks.LastOrDefault();
|
||||
|
||||
if (block == null || _mappedBlockUsage == MapGranularity)
|
||||
{
|
||||
// Need to map some more memory.
|
||||
|
||||
block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable);
|
||||
|
||||
_mappedBlocks.Add(block);
|
||||
|
||||
_mappedBlockUsage = 0;
|
||||
}
|
||||
|
||||
_pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize));
|
||||
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
|
||||
|
||||
_mappedBlockUsage += _pageSize;
|
||||
}
|
||||
|
||||
public void EnsureMapped(ulong offset)
|
||||
{
|
||||
int pageIndex = (int)(offset / _pageSize);
|
||||
int bitmapIndex = pageIndex >> 6;
|
||||
|
||||
ref ulong entry = ref _mappedPageBitmap[bitmapIndex];
|
||||
ulong bit = 1UL << (pageIndex & 63);
|
||||
|
||||
if ((Volatile.Read(ref entry) & bit) == 0)
|
||||
{
|
||||
// Not mapped.
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// Check the bit while locked to make sure that this only happens once.
|
||||
|
||||
ulong lockedEntry = Volatile.Read(ref entry);
|
||||
|
||||
if ((lockedEntry & bit) == 0)
|
||||
{
|
||||
MapPage(offset & ~(_pageSize - 1));
|
||||
|
||||
lockedEntry |= bit;
|
||||
|
||||
Interlocked.Exchange(ref entry, lockedEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_reservedBlock.Dispose();
|
||||
|
||||
foreach (MemoryBlock block in _mappedBlocks)
|
||||
{
|
||||
block.Dispose();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -1,3 +1,4 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
@ -12,7 +13,7 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
public CpuContext(IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
||||
_translator = new Translator(new JitMemoryAllocator(), memory, AddressTable<ulong>.CreateForArm(for64Bit, memory.Type));
|
||||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using NUnit.Framework;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
@ -17,7 +19,10 @@ namespace Ryujinx.Tests.Cpu
|
||||
private static void EnsureTranslator()
|
||||
{
|
||||
// Create a translator, as one is needed to register the signal handler or emit methods.
|
||||
_translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true);
|
||||
_translator ??= new Translator(
|
||||
new JitMemoryAllocator(),
|
||||
new MockMemoryManager(),
|
||||
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
|
@ -1,3 +1,5 @@
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Signal;
|
||||
using ARMeilleure.Translation;
|
||||
using NUnit.Framework;
|
||||
@ -53,7 +55,10 @@ namespace Ryujinx.Tests.Memory
|
||||
private static void EnsureTranslator()
|
||||
{
|
||||
// Create a translator, as one is needed to register the signal handler or emit methods.
|
||||
_translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true);
|
||||
_translator ??= new Translator(
|
||||
new JitMemoryAllocator(),
|
||||
new MockMemoryManager(),
|
||||
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -10,6 +10,7 @@
|
||||
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">linux</TargetOS>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<RunSettingsFilePath>$(MSBuildProjectDirectory)\.runsettings</RunSettingsFilePath>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Multiplayer;
|
||||
@ -16,7 +17,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 56;
|
||||
public const int CurrentVersion = 57;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
@ -191,8 +192,25 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
/// <summary>
|
||||
/// Enables or disables Vertical Sync
|
||||
/// </summary>
|
||||
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
|
||||
/// TODO: Remove this when those older versions aren't in use anymore.
|
||||
public bool EnableVsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current VSync mode; 60 (Switch), unbounded ("Vsync off"), or custom
|
||||
/// </summary>
|
||||
public VSyncMode VSyncMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the custom present interval
|
||||
/// </summary>
|
||||
public bool EnableCustomVSyncInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The custom present interval value
|
||||
/// </summary>
|
||||
public int CustomVSyncInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Shader cache
|
||||
/// </summary>
|
||||
|
@ -82,7 +82,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = Key.F1,
|
||||
ToggleVSyncMode = Key.F1,
|
||||
};
|
||||
|
||||
configurationFileUpdated = true;
|
||||
@ -276,7 +276,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = Key.F1,
|
||||
ToggleVSyncMode = Key.F1,
|
||||
Screenshot = Key.F8,
|
||||
};
|
||||
|
||||
@ -289,7 +289,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = Key.F1,
|
||||
ToggleVSyncMode = Key.F1,
|
||||
Screenshot = Key.F8,
|
||||
ShowUI = Key.F4,
|
||||
};
|
||||
@ -332,7 +332,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
||||
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||
Pause = Key.F5,
|
||||
@ -347,7 +347,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
||||
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||
@ -421,7 +421,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
||||
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||
@ -448,7 +448,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
||||
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||
@ -611,6 +611,33 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
if (configurationFileFormat.Version < 57)
|
||||
{
|
||||
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 57.");
|
||||
|
||||
configurationFileFormat.VSyncMode = VSyncMode.Switch;
|
||||
configurationFileFormat.EnableCustomVSyncInterval = false;
|
||||
|
||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVSyncMode = Key.F1,
|
||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
|
||||
ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
|
||||
ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
|
||||
VolumeUp = configurationFileFormat.Hotkeys.VolumeUp,
|
||||
VolumeDown = configurationFileFormat.Hotkeys.VolumeDown,
|
||||
CustomVSyncIntervalIncrement = Key.Unbound,
|
||||
CustomVSyncIntervalDecrement = Key.Unbound,
|
||||
};
|
||||
|
||||
configurationFileFormat.CustomVSyncInterval = 120;
|
||||
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
|
||||
@ -646,7 +673,9 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
ShowTitleBar.Value = configurationFileFormat.ShowTitleBar;
|
||||
EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
|
||||
HideCursor.Value = configurationFileFormat.HideCursor;
|
||||
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
|
||||
Graphics.VSyncMode.Value = configurationFileFormat.VSyncMode;
|
||||
Graphics.EnableCustomVSyncInterval.Value = configurationFileFormat.EnableCustomVSyncInterval;
|
||||
Graphics.CustomVSyncInterval.Value = configurationFileFormat.CustomVSyncInterval;
|
||||
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
|
||||
Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
|
||||
Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
|
||||
|
@ -1,4 +1,4 @@
|
||||
using ARMeilleure;
|
||||
using ARMeilleure;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
@ -474,9 +474,19 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
public ReactiveObject<string> ShadersDumpPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Vertical Sync
|
||||
/// Toggles the present interval mode. Options are Switch (60Hz), Unbounded (previously Vsync off), and Custom, if enabled.
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableVsync { get; private set; }
|
||||
public ReactiveObject<VSyncMode> VSyncMode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the custom present interval mode.
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableCustomVSyncInterval { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the custom present interval.
|
||||
/// </summary>
|
||||
public ReactiveObject<int> CustomVSyncInterval { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Shader cache
|
||||
@ -536,8 +546,12 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
AspectRatio = new ReactiveObject<AspectRatio>();
|
||||
AspectRatio.LogChangesToValue(nameof(AspectRatio));
|
||||
ShadersDumpPath = new ReactiveObject<string>();
|
||||
EnableVsync = new ReactiveObject<bool>();
|
||||
EnableVsync.LogChangesToValue(nameof(EnableVsync));
|
||||
VSyncMode = new ReactiveObject<VSyncMode>();
|
||||
VSyncMode.LogChangesToValue(nameof(VSyncMode));
|
||||
EnableCustomVSyncInterval = new ReactiveObject<bool>();
|
||||
EnableCustomVSyncInterval.LogChangesToValue(nameof(EnableCustomVSyncInterval));
|
||||
CustomVSyncInterval = new ReactiveObject<int>();
|
||||
CustomVSyncInterval.LogChangesToValue(nameof(CustomVSyncInterval));
|
||||
EnableShaderCache = new ReactiveObject<bool>();
|
||||
EnableShaderCache.LogChangesToValue(nameof(EnableShaderCache));
|
||||
EnableTextureRecompression = new ReactiveObject<bool>();
|
||||
|
@ -64,7 +64,9 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
ShowTitleBar = ShowTitleBar,
|
||||
EnableHardwareAcceleration = EnableHardwareAcceleration,
|
||||
HideCursor = HideCursor,
|
||||
EnableVsync = Graphics.EnableVsync,
|
||||
VSyncMode = Graphics.VSyncMode,
|
||||
EnableCustomVSyncInterval = Graphics.EnableCustomVSyncInterval,
|
||||
CustomVSyncInterval = Graphics.CustomVSyncInterval,
|
||||
EnableShaderCache = Graphics.EnableShaderCache,
|
||||
EnableTextureRecompression = Graphics.EnableTextureRecompression,
|
||||
EnableMacroHLE = Graphics.EnableMacroHLE,
|
||||
@ -179,7 +181,9 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
ShowTitleBar.Value = !OperatingSystem.IsWindows();
|
||||
EnableHardwareAcceleration.Value = true;
|
||||
HideCursor.Value = HideCursorMode.OnIdle;
|
||||
Graphics.EnableVsync.Value = true;
|
||||
Graphics.VSyncMode.Value = VSyncMode.Switch;
|
||||
Graphics.CustomVSyncInterval.Value = 120;
|
||||
Graphics.EnableCustomVSyncInterval.Value = false;
|
||||
Graphics.EnableShaderCache.Value = true;
|
||||
Graphics.EnableTextureRecompression.Value = false;
|
||||
Graphics.EnableMacroHLE.Value = true;
|
||||
@ -240,7 +244,7 @@ namespace Ryujinx.UI.Common.Configuration
|
||||
Hid.EnableMouse.Value = false;
|
||||
Hid.Hotkeys.Value = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = Key.F1,
|
||||
ToggleVSyncMode = Key.F1,
|
||||
ToggleMute = Key.F2,
|
||||
Screenshot = Key.F8,
|
||||
ShowUI = Key.F4,
|
||||
|
@ -247,6 +247,7 @@ namespace Ryujinx.UI.Common
|
||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||
"0100744001588000", // Cars 3: Driven to Win
|
||||
"0100b41013c82000", // Cruis'n Blast
|
||||
"01001b300b9be000", // Diablo III: Eternal Collection
|
||||
"01008c8012920000", // Dying Light Platinum Edition
|
||||
"010073c01af34000", // LEGO Horizon Adventures
|
||||
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
||||
@ -264,6 +265,7 @@ namespace Ryujinx.UI.Common
|
||||
"0100800015926000", // Suika Game
|
||||
"0100e46006708000", // Terraria
|
||||
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
||||
"010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon-
|
||||
"010080b00ad66000", // Undertale
|
||||
];
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -5,6 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -57,6 +57,8 @@ using Key = Ryujinx.Input.Key;
|
||||
using MouseButton = Ryujinx.Input.MouseButton;
|
||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||
using Size = Avalonia.Size;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
{
|
||||
@ -203,6 +205,9 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
|
||||
ConfigurationState.Instance.Graphics.VSyncMode.Event += UpdateVSyncMode;
|
||||
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Event += UpdateCustomVSyncIntervalValue;
|
||||
ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Event += UpdateCustomVSyncIntervalEnabled;
|
||||
|
||||
ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState;
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||
@ -295,6 +300,66 @@ namespace Ryujinx.Ava
|
||||
_renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
|
||||
}
|
||||
|
||||
public void UpdateVSyncMode(object sender, ReactiveEventArgs<VSyncMode> e)
|
||||
{
|
||||
if (Device != null)
|
||||
{
|
||||
Device.VSyncMode = e.NewValue;
|
||||
Device.UpdateVSyncInterval();
|
||||
}
|
||||
_renderer.Window?.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)e.NewValue);
|
||||
|
||||
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
|
||||
}
|
||||
|
||||
public void VSyncModeToggle()
|
||||
{
|
||||
VSyncMode oldVSyncMode = Device.VSyncMode;
|
||||
VSyncMode newVSyncMode = VSyncMode.Switch;
|
||||
bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value;
|
||||
|
||||
switch (oldVSyncMode)
|
||||
{
|
||||
case VSyncMode.Switch:
|
||||
newVSyncMode = VSyncMode.Unbounded;
|
||||
break;
|
||||
case VSyncMode.Unbounded:
|
||||
if (customVSyncIntervalEnabled)
|
||||
{
|
||||
newVSyncMode = VSyncMode.Custom;
|
||||
}
|
||||
else
|
||||
{
|
||||
newVSyncMode = VSyncMode.Switch;
|
||||
}
|
||||
|
||||
break;
|
||||
case VSyncMode.Custom:
|
||||
newVSyncMode = VSyncMode.Switch;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateVSyncMode(this, new ReactiveEventArgs<VSyncMode>(oldVSyncMode, newVSyncMode));
|
||||
}
|
||||
|
||||
private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs<int> e)
|
||||
{
|
||||
if (Device != null)
|
||||
{
|
||||
Device.TargetVSyncInterval = e.NewValue;
|
||||
Device.UpdateVSyncInterval();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCustomVSyncIntervalEnabled(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
if (Device != null)
|
||||
{
|
||||
Device.CustomVSyncIntervalEnabled = e.NewValue;
|
||||
Device.UpdateVSyncInterval();
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowCursor()
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
@ -352,11 +417,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
|
||||
|
||||
string directory = AppDataManager.Mode switch
|
||||
{
|
||||
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
|
||||
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"),
|
||||
};
|
||||
string directory = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
|
||||
|
||||
string path = Path.Combine(directory, filename);
|
||||
|
||||
@ -509,12 +570,6 @@ namespace Ryujinx.Ava
|
||||
Device.Configuration.MultiplayerDisableP2p = e.NewValue;
|
||||
}
|
||||
|
||||
public void ToggleVSync()
|
||||
{
|
||||
Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
|
||||
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isActive = false;
|
||||
@ -868,7 +923,7 @@ namespace Ryujinx.Ava
|
||||
_viewModel.UiHandler,
|
||||
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
||||
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
||||
ConfigurationState.Instance.Graphics.EnableVsync,
|
||||
ConfigurationState.Instance.Graphics.VSyncMode,
|
||||
ConfigurationState.Instance.System.EnableDockedMode,
|
||||
ConfigurationState.Instance.System.EnablePtc,
|
||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||
@ -885,7 +940,8 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.Multiplayer.Mode,
|
||||
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
||||
ConfigurationState.Instance.Multiplayer.LdnServer));
|
||||
ConfigurationState.Instance.Multiplayer.LdnServer,
|
||||
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value));
|
||||
}
|
||||
|
||||
private static IHardwareDeviceDriver InitializeAudio()
|
||||
@ -1006,7 +1062,7 @@ namespace Ryujinx.Ava
|
||||
Device.Gpu.SetGpuThread();
|
||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||
|
||||
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||
_renderer.Window.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)Device.VSyncMode);
|
||||
|
||||
while (_isActive)
|
||||
{
|
||||
@ -1067,6 +1123,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued.
|
||||
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
|
||||
string vSyncMode = Device.VSyncMode.ToString();
|
||||
|
||||
UpdateShaderCount();
|
||||
|
||||
@ -1076,7 +1133,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
|
||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||
Device.EnableDeviceVsync,
|
||||
vSyncMode,
|
||||
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
|
||||
dockedMode,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||
@ -1179,8 +1236,16 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
switch (currentHotkeyState)
|
||||
{
|
||||
case KeyboardHotkeyState.ToggleVSync:
|
||||
ToggleVSync();
|
||||
case KeyboardHotkeyState.ToggleVSyncMode:
|
||||
VSyncModeToggle();
|
||||
break;
|
||||
case KeyboardHotkeyState.CustomVSyncIntervalDecrement:
|
||||
Device.DecrementCustomVSyncInterval();
|
||||
_viewModel.CustomVSyncInterval -= 1;
|
||||
break;
|
||||
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
|
||||
Device.IncrementCustomVSyncInterval();
|
||||
_viewModel.CustomVSyncInterval += 1;
|
||||
break;
|
||||
case KeyboardHotkeyState.Screenshot:
|
||||
ScreenshotRequested = true;
|
||||
@ -1267,9 +1332,9 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
KeyboardHotkeyState state = KeyboardHotkeyState.None;
|
||||
|
||||
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync))
|
||||
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVSyncMode))
|
||||
{
|
||||
state = KeyboardHotkeyState.ToggleVSync;
|
||||
state = KeyboardHotkeyState.ToggleVSyncMode;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot))
|
||||
{
|
||||
@ -1303,6 +1368,14 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
state = KeyboardHotkeyState.VolumeDown;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalIncrement))
|
||||
{
|
||||
state = KeyboardHotkeyState.CustomVSyncIntervalIncrement;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalDecrement))
|
||||
{
|
||||
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "اَلْعَرَبِيَّةُ",
|
||||
"MenuBarFileOpenApplet": "فتح التطبيق المصغر",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "افتح تطبيق تحرير Mii في الوضع المستقل",
|
||||
"SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة",
|
||||
"SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "إدارة أنواع الملفات",
|
||||
"MenuBarToolsInstallFileTypes": "تثبيت أنواع الملفات",
|
||||
"MenuBarToolsUninstallFileTypes": "إزالة أنواع الملفات",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_عرض",
|
||||
"MenuBarViewWindow": "حجم النافذة",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "يفتح المجلد الذي يحتوي على تعديلات(mods) التطبيق",
|
||||
"GameListContextMenuOpenSdModsDirectory": "فتح مجلد تعديلات(mods) أتموسفير",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "يفتح مجلد أتموسفير لبطاقة SD البديلة الذي يحتوي على تعديلات التطبيق. مفيد للتعديلات التي تم تعبئتها للأجهزة الحقيقية.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{0}/{1} لعبة تم تحميلها",
|
||||
"StatusBarSystemVersion": "إصدار النظام: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "الحد الأدنى لتعيينات الذاكرة المكتشفة",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "هل ترغب في زيادة قيمة vm.max_map_count إلى {0}",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيغلق ريوجينكس بمجرد تجاوز هذا الحد.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "حوار الإدخال",
|
||||
"InputDialogOk": "موافق",
|
||||
"InputDialogCancel": "إلغاء",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "اختر اسم الملف الشخصي",
|
||||
"InputDialogAddNewProfileHeader": "الرجاء إدخال اسم الملف الشخصي",
|
||||
"InputDialogAddNewProfileSubtext": "(الطول الأقصى: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "جاري استخراج التحديث...",
|
||||
"DialogUpdaterRenamingMessage": "إعادة تسمية التحديث...",
|
||||
"DialogUpdaterAddingFilesMessage": "إضافة تحديث جديد...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "اكتمل التحديث",
|
||||
"DialogUpdaterRestartMessage": "هل تريد إعادة تشغيل ريوجينكس الآن؟",
|
||||
"DialogUpdaterNoInternetMessage": "أنت غير متصل بالإنترنت.",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "تم إلغاء تثبيت أنواع الملفات بنجاح!",
|
||||
"DialogUninstallFileTypesErrorMessage": "فشل إلغاء تثبيت أنواع الملفات.",
|
||||
"DialogOpenSettingsWindowLabel": "فتح نافذة الإعدادات",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "تطبيق وحدة التحكم المصغر",
|
||||
"DialogMessageDialogErrorExceptionMessage": "خطأ في عرض مربع حوار الرسالة: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "خطأ في عرض لوحة مفاتيح البرامج: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "الإصدار: {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "ريوجينكس - معلومات",
|
||||
"RyujinxConfirm": "ريوجينكس - تأكيد",
|
||||
"FileDialogAllTypes": "كل الأنواع",
|
||||
@ -733,11 +748,37 @@
|
||||
"SelectDlcDialogTitle": "حدد ملفات المحتوي الإضافي",
|
||||
"SelectUpdateDialogTitle": "حدد ملفات التحديث",
|
||||
"SelectModDialogTitle": "حدد مجلد التعديل",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"UserProfileWindowTitle": "مدير الملفات الشخصية للمستخدمين",
|
||||
"CheatWindowTitle": "مدير الغش",
|
||||
"DlcWindowTitle": "إدارة المحتوى القابل للتنزيل لـ {0} ({1})",
|
||||
"ModWindowTitle": "إدارة التعديلات لـ {0} ({1})",
|
||||
"UpdateWindowTitle": "مدير تحديث العنوان",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
@ -753,6 +794,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} تعديل",
|
||||
"UserProfilesEditProfile": "تعديل المحدد",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "إلغاء",
|
||||
"Save": "حفظ",
|
||||
"Discard": "تجاهل",
|
||||
@ -820,5 +862,17 @@
|
||||
"MultiplayerMode": "الوضع:",
|
||||
"MultiplayerModeTooltip": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم LdnMitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.",
|
||||
"MultiplayerModeDisabled": "معطل",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "Deutsch",
|
||||
"MenuBarFileOpenApplet": "Öffne Anwendung",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii-Editor-Applet im Standalone-Modus",
|
||||
"SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff",
|
||||
"SettingsTabSystemMemoryManagerMode": "Speichermanagermodus:",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "Dateitypen verwalten",
|
||||
"MenuBarToolsInstallFileTypes": "Dateitypen installieren",
|
||||
"MenuBarToolsUninstallFileTypes": "Dateitypen deinstallieren",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_Ansicht",
|
||||
"MenuBarViewWindow": "Fenstergröße",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Öffnet das Verzeichnis, welches Mods für die Spiele beinhaltet",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Atmosphere-Mod-Verzeichnis öffnen",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Öffnet das alternative SD-Karten-Atmosphere-Verzeichnis, das die Mods der Anwendung enthält. Dieser Ordner ist nützlich für Mods, die für echte Hardware erstellt worden sind.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{0}/{1} Spiele geladen",
|
||||
"StatusBarSystemVersion": "Systemversion: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Niedriges Limit für Speicherzuordnungen erkannt",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "Möchtest Du den Wert von vm.max_map_count auf {0} erhöhen",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "Einige Spiele könnten versuchen, mehr Speicherzuordnungen zu erstellen, als derzeit erlaubt. Ryujinx wird abstürzen, sobald dieses Limit überschritten wird.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "Eingabe-Dialog",
|
||||
"InputDialogOk": "OK",
|
||||
"InputDialogCancel": "Abbrechen",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "Wähle den Profilnamen",
|
||||
"InputDialogAddNewProfileHeader": "Bitte gebe einen Profilnamen ein",
|
||||
"InputDialogAddNewProfileSubtext": "(Maximale Länge: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "Update wird entpackt...",
|
||||
"DialogUpdaterRenamingMessage": "Update wird umbenannt...",
|
||||
"DialogUpdaterAddingFilesMessage": "Update wird hinzugefügt...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "Update abgeschlossen!",
|
||||
"DialogUpdaterRestartMessage": "Ryujinx jetzt neu starten?",
|
||||
"DialogUpdaterNoInternetMessage": "Es besteht keine Verbindung mit dem Internet!",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "Dateitypen erfolgreich deinstalliert!",
|
||||
"DialogUninstallFileTypesErrorMessage": "Deinstallation der Dateitypen fehlgeschlagen.",
|
||||
"DialogOpenSettingsWindowLabel": "Fenster-Einstellungen öffnen",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "Controller-Applet",
|
||||
"DialogMessageDialogErrorExceptionMessage": "Fehler bei der Anzeige des Meldungs-Dialogs: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Fehler bei der Anzeige der Software-Tastatur: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "Version {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Bestätigung",
|
||||
"FileDialogAllTypes": "Alle Typen",
|
||||
@ -733,11 +748,37 @@
|
||||
"SelectDlcDialogTitle": "DLC-Dateien auswählen",
|
||||
"SelectUpdateDialogTitle": "Update-Datei auswählen",
|
||||
"SelectModDialogTitle": "Mod-Ordner auswählen",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"UserProfileWindowTitle": "Benutzerprofile verwalten",
|
||||
"CheatWindowTitle": "Spiel-Cheats verwalten",
|
||||
"DlcWindowTitle": "Spiel-DLC verwalten",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Spiel-Updates verwalten",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
@ -753,6 +794,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Profil bearbeiten",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "Abbrechen",
|
||||
"Save": "Speichern",
|
||||
"Discard": "Verwerfen",
|
||||
@ -820,5 +862,17 @@
|
||||
"MultiplayerMode": "Modus:",
|
||||
"MultiplayerModeTooltip": "Ändert den LDN-Mehrspielermodus.\n\nLdnMitm ändert die lokale drahtlose/lokale Spielfunktionalität in Spielen so, dass sie wie ein LAN funktioniert und lokale, netzwerkgleiche Verbindungen mit anderen Ryujinx-Instanzen und gehackten Nintendo Switch-Konsolen ermöglicht, auf denen das ldn_mitm-Modul installiert ist.\n\nMultiplayer erfordert, dass alle Spieler die gleiche Spielversion verwenden (d.h. Super Smash Bros. Ultimate v13.0.1 kann sich nicht mit v13.0.0 verbinden).\n\nIm Zweifelsfall auf DISABLED lassen.",
|
||||
"MultiplayerModeDisabled": "Deaktiviert",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "Ελληνικά",
|
||||
"MenuBarFileOpenApplet": "Άνοιγμα Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία",
|
||||
"SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού",
|
||||
"SettingsTabSystemMemoryManagerMode": "Λειτουργία Διαχείρισης Μνήμης:",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "Διαχείριση τύπων αρχείων",
|
||||
"MenuBarToolsInstallFileTypes": "Εγκαταστήσετε τύπους αρχείων.",
|
||||
"MenuBarToolsUninstallFileTypes": "Απεγκαταστήσετε τύπους αρχείων",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_View",
|
||||
"MenuBarViewWindow": "Window Size",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{0}/{1} Φορτωμένα Παιχνίδια",
|
||||
"StatusBarSystemVersion": "Έκδοση Συστήματος: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Εντοπίστηκε χαμηλό όριο για αντιστοιχίσεις μνήμης",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "Θα θέλατε να αυξήσετε την τιμή του vm.max_map_count σε {0}",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "Μερικά παιχνίδια μπορεί να προσπαθήσουν να δημιουργήσουν περισσότερες αντιστοιχίσεις μνήμης από αυτές που επιτρέπονται τώρα. Ο Ryujinx θα καταρρεύσει μόλις ξεπεραστεί αυτό το όριο.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "Διάλογος Εισαγωγής",
|
||||
"InputDialogOk": "ΟΚ",
|
||||
"InputDialogCancel": "Ακύρωση",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "Επιλογή Ονόματος Προφίλ",
|
||||
"InputDialogAddNewProfileHeader": "Εισαγωγή Ονόματος Προφίλ",
|
||||
"InputDialogAddNewProfileSubtext": "(Σύνολο Χαρακτήρων: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "Εξαγωγή Ενημέρωσης...",
|
||||
"DialogUpdaterRenamingMessage": "Μετονομασία Ενημέρωσης...",
|
||||
"DialogUpdaterAddingFilesMessage": "Προσθήκη Νέας Ενημέρωσης...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "Η Ενημέρωση Ολοκληρώθηκε!",
|
||||
"DialogUpdaterRestartMessage": "Θέλετε να επανεκκινήσετε το Ryujinx τώρα;",
|
||||
"DialogUpdaterNoInternetMessage": "Δεν είστε συνδεδεμένοι στο Διαδίκτυο!",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "Επιτυχής απεγκατάσταση τύπων αρχείων!",
|
||||
"DialogUninstallFileTypesErrorMessage": "Αποτυχία απεγκατάστασης τύπων αρχείων.",
|
||||
"DialogOpenSettingsWindowLabel": "Άνοιγμα Παραθύρου Ρυθμίσεων",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "Applet Χειρισμού",
|
||||
"DialogMessageDialogErrorExceptionMessage": "Σφάλμα εμφάνισης του διαλόγου Μηνυμάτων: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Σφάλμα εμφάνισης Λογισμικού Πληκτρολογίου: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "Version {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "Ryujinx - Πληροφορίες",
|
||||
"RyujinxConfirm": "Ryujinx - Επιβεβαίωση",
|
||||
"FileDialogAllTypes": "Όλοι οι τύποι",
|
||||
@ -733,11 +748,37 @@
|
||||
"SelectDlcDialogTitle": "Επιλογή αρχείων DLC",
|
||||
"SelectUpdateDialogTitle": "Επιλογή αρχείων ενημέρωσης",
|
||||
"SelectModDialogTitle": "Select mod directory",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"UserProfileWindowTitle": "Διαχειριστής Προφίλ Χρήστη",
|
||||
"CheatWindowTitle": "Διαχειριστής των Cheats",
|
||||
"DlcWindowTitle": "Downloadable Content Manager",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Διαχειριστής Ενημερώσεων Τίτλου",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
@ -753,6 +794,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Επεξεργασία Επιλεγμένων",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "Ακύρωση",
|
||||
"Save": "Αποθήκευση",
|
||||
"Discard": "Απόρριψη",
|
||||
@ -820,5 +862,17 @@
|
||||
"MultiplayerMode": "Λειτουργία:",
|
||||
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.",
|
||||
"MultiplayerModeDisabled": "Disabled",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "English (US)",
|
||||
"MenuBarFileOpenApplet": "Open Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode",
|
||||
"SettingsTabInputDirectMouseAccess": "Direct Mouse Access",
|
||||
"SettingsTabSystemMemoryManagerMode": "Memory Manager Mode:",
|
||||
@ -145,9 +146,20 @@
|
||||
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin American Spanish",
|
||||
"SettingsTabSystemSystemLanguageSimplifiedChinese": "Simplified Chinese",
|
||||
"SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese",
|
||||
"SettingsTabSystemSystemTimeZone": "System TimeZone:",
|
||||
"SettingsTabSystemSystemTimeZone": "System Time Zone:",
|
||||
"SettingsTabSystemSystemTime": "System Time:",
|
||||
"SettingsTabSystemEnableVsync": "VSync",
|
||||
"SettingsTabSystemVSyncMode": "VSync:",
|
||||
"SettingsTabSystemEnableCustomVSyncInterval": "Enable custom refresh rate (Experimental)",
|
||||
"SettingsTabSystemVSyncModeSwitch": "Switch",
|
||||
"SettingsTabSystemVSyncModeUnbounded": "Unbounded",
|
||||
"SettingsTabSystemVSyncModeCustom": "Custom Refresh Rate",
|
||||
"SettingsTabSystemVSyncModeTooltip": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate.",
|
||||
"SettingsTabSystemVSyncModeTooltipCustom": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate. 'Custom' emulates the specified custom refresh rate.",
|
||||
"SettingsTabSystemEnableCustomVSyncIntervalTooltip": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
|
||||
"SettingsTabSystemCustomVSyncIntervalValueTooltip": "The custom refresh rate target value.",
|
||||
"SettingsTabSystemCustomVSyncIntervalSliderTooltip": "The custom refresh rate, as a percentage of the normal Switch refresh rate.",
|
||||
"SettingsTabSystemCustomVSyncIntervalPercentage": "Custom Refresh Rate %:",
|
||||
"SettingsTabSystemCustomVSyncIntervalValue": "Custom Refresh Rate Value:",
|
||||
"SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)",
|
||||
"SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC cache",
|
||||
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks",
|
||||
@ -156,6 +168,7 @@
|
||||
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
|
||||
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||
"SettingsTabSystemCustomVSyncInterval": "Interval",
|
||||
"SettingsTabSystemHacks": "Hacks",
|
||||
"SettingsTabSystemHacksNote": "May cause instability",
|
||||
"SettingsTabSystemDramSize": "DRAM size:",
|
||||
@ -460,6 +473,7 @@
|
||||
"DialogUpdaterExtractionMessage": "Extracting Update...",
|
||||
"DialogUpdaterRenamingMessage": "Renaming Update...",
|
||||
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "Update Complete!",
|
||||
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||
@ -729,11 +743,13 @@
|
||||
"RyujinxUpdater": "Ryujinx Updater",
|
||||
"SettingsTabHotkeys": "Keyboard Hotkeys",
|
||||
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
|
||||
"SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:",
|
||||
"SettingsTabHotkeysToggleVSyncModeHotkey": "Toggle VSync mode:",
|
||||
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
|
||||
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
|
||||
"SettingsTabHotkeysPauseHotkey": "Pause:",
|
||||
"SettingsTabHotkeysToggleMuteHotkey": "Mute:",
|
||||
"SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey": "Raise custom refresh rate",
|
||||
"SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey": "Lower custom refresh rate",
|
||||
"ControllerMotionTitle": "Motion Control Settings",
|
||||
"ControllerRumbleTitle": "Rumble Settings",
|
||||
"SettingsSelectThemeFileDialogTitle": "Select Theme File",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "Español (ES)",
|
||||
"MenuBarFileOpenApplet": "Abrir applet",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo",
|
||||
"SettingsTabInputDirectMouseAccess": "Acceso directo al ratón",
|
||||
"SettingsTabSystemMemoryManagerMode": "Modo del administrador de memoria:",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "Administrar tipos de archivo",
|
||||
"MenuBarToolsInstallFileTypes": "Instalar tipos de archivo",
|
||||
"MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_View",
|
||||
"MenuBarViewWindow": "Tamaño Ventana",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Abre el directorio que contiene los Mods de la Aplicación.",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Abrir Directorio de Mods de Atmosphere\n\n\n\n\n\n",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Abre el directorio alternativo de la tarjeta SD de Atmosphere que contiene los Mods de la Aplicación. Útil para los mods que están empaquetados para el hardware real.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{0}/{1} juegos cargados",
|
||||
"StatusBarSystemVersion": "Versión del sistema: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Límite inferior para mapeos de memoria detectado",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "¿Quieres aumentar el valor de vm.max_map_count a {0}?",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "Algunos juegos podrían intentar crear más mapeos de memoria de los permitidos. Ryujinx se bloqueará tan pronto como se supere este límite.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "Cuadro de diálogo de entrada",
|
||||
"InputDialogOk": "Aceptar",
|
||||
"InputDialogCancel": "Cancelar",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "Introducir nombre de perfil",
|
||||
"InputDialogAddNewProfileHeader": "Por favor elige un nombre de usuario",
|
||||
"InputDialogAddNewProfileSubtext": "(Máximo de caracteres: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "Extrayendo actualización...",
|
||||
"DialogUpdaterRenamingMessage": "Renombrando actualización...",
|
||||
"DialogUpdaterAddingFilesMessage": "Aplicando actualización...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "¡Actualización completa!",
|
||||
"DialogUpdaterRestartMessage": "¿Quieres reiniciar Ryujinx?",
|
||||
"DialogUpdaterNoInternetMessage": "¡No estás conectado a internet!",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "¡Tipos de archivos desinstalados con éxito!",
|
||||
"DialogUninstallFileTypesErrorMessage": "No se pudo desinstalar los tipos de archivo.",
|
||||
"DialogOpenSettingsWindowLabel": "Abrir ventana de opciones",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "Applet de mandos",
|
||||
"DialogMessageDialogErrorExceptionMessage": "Error al mostrar cuadro de diálogo: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Error al mostrar teclado de software: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "Versión {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Confirmación",
|
||||
"FileDialogAllTypes": "Todos los tipos",
|
||||
@ -733,11 +748,37 @@
|
||||
"SelectDlcDialogTitle": "Selecciona archivo(s) de DLC",
|
||||
"SelectUpdateDialogTitle": "Selecciona archivo(s) de actualización",
|
||||
"SelectModDialogTitle": "Seleccionar un directorio de Mods",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"UserProfileWindowTitle": "Administrar perfiles de usuario",
|
||||
"CheatWindowTitle": "Administrar cheats",
|
||||
"DlcWindowTitle": "Administrar contenido descargable",
|
||||
"ModWindowTitle": "Administrar Mods para {0} ({1})",
|
||||
"UpdateWindowTitle": "Administrar actualizaciones",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} nueva(s) actualización(es) agregada(s)",
|
||||
@ -752,6 +793,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "Se eliminaron {0} actualización(es) faltantes",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Editar selección",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "Cancelar",
|
||||
"Save": "Guardar",
|
||||
"Discard": "Descartar",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "Français",
|
||||
"MenuBarFileOpenApplet": "Ouvrir un programme",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Éditeur de Mii",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'éditeur Mii en mode Standalone",
|
||||
"SettingsTabInputDirectMouseAccess": "Accès direct à la souris",
|
||||
"SettingsTabSystemMemoryManagerMode": "Mode de gestion de la mémoire :",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "Gérer les types de fichiers",
|
||||
"MenuBarToolsInstallFileTypes": "Installer les types de fichiers",
|
||||
"MenuBarToolsUninstallFileTypes": "Désinstaller les types de fichiers",
|
||||
"MenuBarToolsXCITrimmer": "Réduire les fichiers XCI",
|
||||
"MenuBarView": "_Fenêtre",
|
||||
"MenuBarViewWindow": "Taille de la fenêtre",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Ouvre le dossier contenant les mods du jeu",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Ouvrir le dossier des mods Atmosphère",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Ouvre le dossier alternatif de la carte SD Atmosphère qui contient les mods de l'application. Utile pour les mods conçus pour console.",
|
||||
"GameListContextMenuTrimXCI": "Vérifier et réduire les fichiers XCI",
|
||||
"GameListContextMenuTrimXCIToolTip": "Vérifier et réduire les fichiers XCI pour économiser de l'espace",
|
||||
"StatusBarGamesLoaded": "{0}/{1} Jeux chargés",
|
||||
"StatusBarSystemVersion": "Version du Firmware: {0}",
|
||||
"StatusBarXCIFileTrimming": "Réduction du fichier XCI '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Limite basse pour les mappings mémoire détectée",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "Voulez-vous augmenter la valeur de vm.max_map_count à {0}",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "Certains jeux peuvent essayer de créer plus de mappings mémoire que ce qui est actuellement autorisé. Ryujinx plantera dès que cette limite sera dépassée.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "Fenêtre d'entrée de texte",
|
||||
"InputDialogOk": "OK",
|
||||
"InputDialogCancel": "Annuler",
|
||||
"InputDialogCancelling": "Annulation en cours",
|
||||
"InputDialogClose": "Fermer",
|
||||
"InputDialogAddNewProfileTitle": "Choisir un nom de profil",
|
||||
"InputDialogAddNewProfileHeader": "Merci d'entrer un nom de profil",
|
||||
"InputDialogAddNewProfileSubtext": "(Longueur max.: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "Extraction de la mise à jour…",
|
||||
"DialogUpdaterRenamingMessage": "Renommage de la mise à jour...",
|
||||
"DialogUpdaterAddingFilesMessage": "Ajout d'une nouvelle mise à jour...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "Mise à jour terminée !",
|
||||
"DialogUpdaterRestartMessage": "Voulez-vous redémarrer Ryujinx maintenant ?",
|
||||
"DialogUpdaterNoInternetMessage": "Vous n'êtes pas connecté à Internet !",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "Types de fichiers désinstallés avec succès!",
|
||||
"DialogUninstallFileTypesErrorMessage": "Échec de la désinstallation des types de fichiers.",
|
||||
"DialogOpenSettingsWindowLabel": "Ouvrir la fenêtre de configuration",
|
||||
"DialogOpenXCITrimmerWindowLabel": "Fenêtre de réduction de fichiers XCI",
|
||||
"DialogControllerAppletTitle": "Programme Manette",
|
||||
"DialogMessageDialogErrorExceptionMessage": "Erreur lors de l'affichage de la boîte de dialogue : {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Erreur lors de l'affichage du clavier logiciel: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "Version {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Inclus avec le jeu: Version {0}",
|
||||
"TitleBundledDlcLabel": "Inclus avec le jeu :",
|
||||
"TitleXCIStatusPartialLabel": "Partiel",
|
||||
"TitleXCIStatusTrimmableLabel": "Non réduit",
|
||||
"TitleXCIStatusUntrimmableLabel": "Réduit",
|
||||
"TitleXCIStatusFailedLabel": "(Échoué)",
|
||||
"TitleXCICanSaveLabel": "Sauvegarde de {0:n0} Mo",
|
||||
"TitleXCISavingLabel": "Sauvegardé {0:n0} Mo",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Confirmation",
|
||||
"FileDialogAllTypes": "Tous les types",
|
||||
@ -733,11 +748,39 @@
|
||||
"SelectDlcDialogTitle": "Sélectionner les fichiers DLC",
|
||||
"SelectUpdateDialogTitle": "Sélectionner les fichiers de mise à jour",
|
||||
"SelectModDialogTitle": "Sélectionner le répertoire du mod",
|
||||
"TrimXCIFileDialogTitle": "Vérifier et Réduire le fichier XCI",
|
||||
"TrimXCIFileDialogPrimaryText": "Cette fonction va vérifier l'espace vide, puis réduire le fichier XCI pour économiser de l'espace de disque dur.",
|
||||
"TrimXCIFileDialogSecondaryText": "Taille actuelle du fichier: {0:n} MB\nTaille des données de jeux: {1:n} MB\nÉconomie d'espaces sur le disque: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "Fichier XCI n'a pas besoin d'être réduit. Regarder les journaux pour plus de détails",
|
||||
"TrimXCIFileNoUntrimPossible": "Fichier XCI ne peut pas être dé-réduit. Regarder les journaux pour plus de détails",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "Fichier XCI est en Lecture Seule et n'a pas pu être rendu accessible en écriture. Regarder les journaux pour plus de détails",
|
||||
"TrimXCIFileFileSizeChanged": "Fichier XCI a changé en taille depuis qu'il a été scanné. Vérifier que le fichier n'est pas en cours d'écriture et réessayer.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "Fichier XCI a des données dans la zone d'espace libre, ce n'est pas sûr de réduire",
|
||||
"TrimXCIFileInvalidXCIFile": "Fichier XCI contient des données invalides. Regarder les journaux pour plus de détails",
|
||||
"TrimXCIFileFileIOWriteError": "Fichier XCI n'a pas pu été ouvert pour écriture. Regarder les journaux pour plus de détails",
|
||||
"TrimXCIFileFailedPrimaryText": "Réduction du fichier XCI a échoué",
|
||||
"TrimXCIFileCancelled": "L'opération a été annulée",
|
||||
"TrimXCIFileFileUndertermined": "Aucune opération a été faite",
|
||||
"UserProfileWindowTitle": "Gestionnaire de profils utilisateur",
|
||||
"CheatWindowTitle": "Gestionnaire de cheats",
|
||||
"DlcWindowTitle": "Gérer le contenu téléchargeable pour {0} ({1})",
|
||||
"ModWindowTitle": "Gérer les mods pour {0} ({1})",
|
||||
"UpdateWindowTitle": "Gestionnaire de mises à jour",
|
||||
"XCITrimmerWindowTitle": "Rogneur de fichier XCI",
|
||||
"XCITrimmerTitleStatusCount": "{0} sur {1} Fichier(s) Sélectionnés",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} sur {1} Fichier(s) Sélectionnés ({2} affiché(s)",
|
||||
"XCITrimmerTitleStatusTrimming": "Réduction de {0} Fichier(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Dé-Réduction de {0} Fichier(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Échoué",
|
||||
"XCITrimmerPotentialSavings": "Économies potentielles d'espace de disque dur",
|
||||
"XCITrimmerActualSavings": "Économies actualles d'espace de disque dur",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mo",
|
||||
"XCITrimmerSelectDisplayed": "Sélectionner Affiché",
|
||||
"XCITrimmerDeselectDisplayed": "Désélectionner Affiché",
|
||||
"XCITrimmerSortName": "Titre",
|
||||
"XCITrimmerSortSaved": "Économies de disque dur",
|
||||
"XCITrimmerTrim": "Réduire",
|
||||
"XCITrimmerUntrim": "Dé-Réduire",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} nouvelle(s) mise(s) à jour ajoutée(s)",
|
||||
"UpdateWindowBundledContentNotice": "Les mises à jour incluses avec le jeu ne peuvent pas être supprimées mais peuvent être désactivées.",
|
||||
"CheatWindowHeading": "Cheats disponibles pour {0} [{1}]",
|
||||
@ -751,6 +794,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} mises à jour manquantes supprimées",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Éditer la sélection",
|
||||
"Continue": "Continuer",
|
||||
"Cancel": "Annuler",
|
||||
"Save": "Enregistrer",
|
||||
"Discard": "Abandonner",
|
||||
@ -818,5 +862,17 @@
|
||||
"MultiplayerMode": "Mode :",
|
||||
"MultiplayerModeTooltip": "Changer le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.",
|
||||
"MultiplayerModeDisabled": "Désactivé",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Désactiver PàP Hébergement de Réseau (pourrait augmenter la latence)",
|
||||
"MultiplayerDisableP2PTooltip": "Désactiver PàP hébergement de réseau, les postes vont proxy avec le serveur principal au lieu de se connecter directement à vous.",
|
||||
"LdnPassphrase": "Mot de passe Réseau :",
|
||||
"LdnPassphraseTooltip": "Vous pourez seulement voir les jeux hébergé avec le même mot de passe que vous.",
|
||||
"LdnPassphraseInputTooltip": "Entrer un mot de passe dans le format Ryujinx-<8 hex chars>. Vous pourez seulement voir les jeux hébergé avec le même mot de passe que vous.",
|
||||
"LdnPassphraseInputPublic": "(publique)",
|
||||
"GenLdnPass": "Générer Aléatoire",
|
||||
"GenLdnPassTooltip": "Génére un nouveau mot de passe, qui peut être partagé avec les autres.",
|
||||
"ClearLdnPass": "Supprimer",
|
||||
"ClearLdnPassTooltip": "Supprime le mot de passe actuel, ce qui vous remet sur le réseau public.",
|
||||
"InvalidLdnPassphrase": "Mot de passe invalide! Il doit être dans le format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "עִברִית",
|
||||
"MenuBarFileOpenApplet": "פתח יישומון",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "פתח את יישומון עורך ה- Mii במצב עצמאי",
|
||||
"SettingsTabInputDirectMouseAccess": "גישה ישירה לעכבר",
|
||||
"SettingsTabSystemMemoryManagerMode": "מצב מנהל זיכרון:",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "ניהול סוגי קבצים",
|
||||
"MenuBarToolsInstallFileTypes": "סוגי קבצי התקנה",
|
||||
"MenuBarToolsUninstallFileTypes": "סוגי קבצי הסרה",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_View",
|
||||
"MenuBarViewWindow": "Window Size",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "פותח את התיקייה שמכילה מודים של האפליקציה",
|
||||
"GameListContextMenuOpenSdModsDirectory": "פתח תיקיית מודים של Atmosphere",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "פותח את תיקיית כרטיס ה-SD החלופית של Atmosphere המכילה את המודים של האפליקציה. שימושי עבור מודים שארוזים עבור חומרה אמיתית.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{1}/{0} משחקים נטענו",
|
||||
"StatusBarSystemVersion": "גרסת מערכת: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "זוהתה מגבלה נמוכה עבור מיפויי זיכרון",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "האם תרצה להגביר את הערך של vm.max_map_count ל{0}",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "משחקים מסוימים עלולים לייצר עוד מיפויי זיכרון ממה שמתאפשר. Ryujinx יקרוס ברגע שהמגבלה תחרוג.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "דיאלוג קלט",
|
||||
"InputDialogOk": "בסדר",
|
||||
"InputDialogCancel": "ביטול",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "בחרו את שם הפרופיל",
|
||||
"InputDialogAddNewProfileHeader": "אנא הזינו שם לפרופיל",
|
||||
"InputDialogAddNewProfileSubtext": "(אורך מרבי: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "מחלץ עדכון...",
|
||||
"DialogUpdaterRenamingMessage": "משנה את שם העדכון...",
|
||||
"DialogUpdaterAddingFilesMessage": "מוסיף עדכון חדש...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "העדכון הושלם!",
|
||||
"DialogUpdaterRestartMessage": "האם אתם רוצים להפעיל מחדש את ריוג'ינקס עכשיו?",
|
||||
"DialogUpdaterNoInternetMessage": "אתם לא מחוברים לאינטרנט!",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "סוגי קבצים הוסרו בהצלחה!",
|
||||
"DialogUninstallFileTypesErrorMessage": "נכשל בהסרת סוגי קבצים.",
|
||||
"DialogOpenSettingsWindowLabel": "פתח את חלון ההגדרות",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "יישומון בקר",
|
||||
"DialogMessageDialogErrorExceptionMessage": "שגיאה בהצגת דיאלוג ההודעה: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "שגיאה בהצגת תוכנת המקלדת: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "גרסה {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "ריוג'ינקס - מידע",
|
||||
"RyujinxConfirm": "ריוג'ינקס - אישור",
|
||||
"FileDialogAllTypes": "כל הסוגים",
|
||||
@ -733,11 +748,37 @@
|
||||
"SelectDlcDialogTitle": "בחרו קבצי הרחבות משחק",
|
||||
"SelectUpdateDialogTitle": "בחרו קבצי עדכון",
|
||||
"SelectModDialogTitle": "בחר תיקיית מודים",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"UserProfileWindowTitle": "ניהול פרופילי משתמש",
|
||||
"CheatWindowTitle": "נהל צ'יטים למשחק",
|
||||
"DlcWindowTitle": "נהל הרחבות משחק עבור {0} ({1})",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "נהל עדכוני משחקים",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
@ -753,6 +794,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} מוד(ים)",
|
||||
"UserProfilesEditProfile": "ערוך נבחר/ים",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "בטל",
|
||||
"Save": "שמור",
|
||||
"Discard": "השלך",
|
||||
@ -820,5 +862,17 @@
|
||||
"MultiplayerMode": "מצב:",
|
||||
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.",
|
||||
"MultiplayerModeDisabled": "Disabled",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "Italiano",
|
||||
"MenuBarFileOpenApplet": "Apri applet",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone",
|
||||
"SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse",
|
||||
"SettingsTabSystemMemoryManagerMode": "Modalità di gestione della memoria:",
|
||||
@ -460,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "Estrazione dell'aggiornamento...",
|
||||
"DialogUpdaterRenamingMessage": "Rinominazione dell'aggiornamento...",
|
||||
"DialogUpdaterAddingFilesMessage": "Aggiunta del nuovo aggiornamento...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "Aggiornamento completato!",
|
||||
"DialogUpdaterRestartMessage": "Vuoi riavviare Ryujinx adesso?",
|
||||
"DialogUpdaterNoInternetMessage": "Non sei connesso ad Internet!",
|
||||
@ -860,5 +862,17 @@
|
||||
"MultiplayerMode": "Modalità:",
|
||||
"MultiplayerModeTooltip": "Cambia la modalità multigiocatore LDN.\n\nLdnMitm modificherà la funzionalità locale wireless/local play nei giochi per funzionare come se fosse in modalità LAN, consentendo connessioni locali sulla stessa rete con altre istanze di Ryujinx e console Nintendo Switch modificate che hanno il modulo ldn_mitm installato.\n\nLa modalità multigiocatore richiede che tutti i giocatori usino la stessa versione del gioco (es. Super Smash Bros. Ultimate v13.0.1 non può connettersi con la v13.0.0).\n\nNel dubbio, lascia l'opzione su Disabilitato.",
|
||||
"MultiplayerModeDisabled": "Disabilitato",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Language": "日本語",
|
||||
"MenuBarFileOpenApplet": "アプレットを開く",
|
||||
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます",
|
||||
"SettingsTabInputDirectMouseAccess": "マウス直接アクセス",
|
||||
"SettingsTabSystemMemoryManagerMode": "メモリ管理モード:",
|
||||
@ -36,6 +37,7 @@
|
||||
"MenuBarToolsManageFileTypes": "ファイル形式を管理",
|
||||
"MenuBarToolsInstallFileTypes": "ファイル形式をインストール",
|
||||
"MenuBarToolsUninstallFileTypes": "ファイル形式をアンインストール",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_View",
|
||||
"MenuBarViewWindow": "Window Size",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -87,8 +89,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "アプリケーションの Mod データを格納するディレクトリを開きます",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Atmosphere Mods ディレクトリを開く",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "アプリケーションの Mod データを格納する SD カードの Atmosphere ディレクトリを開きます. 実際のハードウェア用に作成された Mod データに有用です.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{0}/{1} ゲーム",
|
||||
"StatusBarSystemVersion": "システムバージョン: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "メモリマッピング上限値が小さすぎます",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "vm.max_map_count の値を {0}に増やしますか?",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "ゲームによっては, 現在許可されているサイズより大きなメモリマッピングを作成しようとすることがあります. この制限を超えると, Ryjinx はすぐにクラッシュします.",
|
||||
@ -403,6 +408,8 @@
|
||||
"InputDialogTitle": "入力ダイアログ",
|
||||
"InputDialogOk": "OK",
|
||||
"InputDialogCancel": "キャンセル",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "プロファイル名を選択",
|
||||
"InputDialogAddNewProfileHeader": "プロファイル名を入力してください",
|
||||
"InputDialogAddNewProfileSubtext": "(最大長: {0})",
|
||||
@ -454,6 +461,7 @@
|
||||
"DialogUpdaterExtractionMessage": "アップデートを展開中...",
|
||||
"DialogUpdaterRenamingMessage": "アップデートをリネーム中...",
|
||||
"DialogUpdaterAddingFilesMessage": "新規アップデートを追加中...",
|
||||
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||
"DialogUpdaterCompleteMessage": "アップデート完了!",
|
||||
"DialogUpdaterRestartMessage": "すぐに Ryujinx を再起動しますか?",
|
||||
"DialogUpdaterNoInternetMessage": "インターネットに接続されていません!",
|
||||
@ -472,6 +480,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "ファイル形式のアンインストールに成功しました!",
|
||||
"DialogUninstallFileTypesErrorMessage": "ファイル形式のアンインストールに失敗しました.",
|
||||
"DialogOpenSettingsWindowLabel": "設定ウインドウを開く",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "コントローラアプレット",
|
||||
"DialogMessageDialogErrorExceptionMessage": "メッセージダイアログ表示エラー: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "ソフトウェアキーボード表示エラー: {0}",
|
||||
@ -681,6 +690,12 @@
|
||||
"TitleUpdateVersionLabel": "バージョン {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "Ryujinx - 情報",
|
||||
"RyujinxConfirm": "Ryujinx - 確認",
|
||||
"FileDialogAllTypes": "すべての種別",
|
||||
@ -733,11 +748,37 @@
|
||||
"SelectDlcDialogTitle": "DLC ファイルを選択",
|
||||
"SelectUpdateDialogTitle": "アップデートファイルを選択",
|
||||
"SelectModDialogTitle": "modディレクトリを選択",
|
||||
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||
"UserProfileWindowTitle": "ユーザプロファイルを管理",
|
||||
"CheatWindowTitle": "チート管理",
|
||||
"DlcWindowTitle": "DLC 管理",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "アップデート管理",
|
||||
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||
"XCITrimmerTitleStatusFailed": "Failed",
|
||||
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||
"XCITrimmerActualSavings": "Actual Savings",
|
||||
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||
"XCITrimmerSortName": "Title",
|
||||
"XCITrimmerSortSaved": "Space Savings",
|
||||
"XCITrimmerTrim": "Trim",
|
||||
"XCITrimmerUntrim": "Untrim",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
@ -752,6 +793,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "編集",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "キャンセル",
|
||||
"Save": "セーブ",
|
||||
"Discard": "破棄",
|
||||
@ -819,5 +861,17 @@
|
||||
"MultiplayerMode": "モード:",
|
||||
"MultiplayerModeTooltip": "LDNマルチプレイヤーモードを変更します.\n\nldn_mitmモジュールがインストールされた, 他のRyujinxインスタンスや,ハックされたNintendo Switchコンソールとのローカル/同一ネットワーク接続を可能にします.\n\nマルチプレイでは, すべてのプレイヤーが同じゲームバージョンである必要があります(例:Super Smash Bros. Ultimate v13.0.1はv13.0.0に接続できません).\n\n不明な場合は「無効」のままにしてください.",
|
||||
"MultiplayerModeDisabled": "無効",
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm"
|
||||
"MultiplayerModeLdnMitm": "ldn_mitm",
|
||||
"MultiplayerModeLdnRyu": "RyuLDN",
|
||||
"MultiplayerDisableP2P": "Disable P2P Network Hosting (may increase latency)",
|
||||
"MultiplayerDisableP2PTooltip": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||
"LdnPassphrase": "Network Passphrase:",
|
||||
"LdnPassphraseTooltip": "You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputTooltip": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||
"LdnPassphraseInputPublic": "(public)",
|
||||
"GenLdnPass": "Generate Random",
|
||||
"GenLdnPassTooltip": "Generates a new passphrase, which can be shared with other players.",
|
||||
"ClearLdnPass": "Clear",
|
||||
"ClearLdnPassTooltip": "Clears the current passphrase, returning to the public network.",
|
||||
"InvalidLdnPassphrase": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\""
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user