Compare commits

..

14 Commits

Author SHA1 Message Date
77c4291c34 Avalonia: Update Polish Translation (#3722)
* Add new string

You need the period there otherwise it could be read as "Głoś" -> "Preach"

* Update MainWindow.axaml

Updating to bring it in line with the other languages naming themselves in their respective languages

* Update pl_PL.json

realizing that period isn't necessary considering the string's usage (which to be fair, I should have checked when I added it)

* Update pl_PL.json

* Add Updater Message
2022-10-19 00:10:28 +00:00
6e92b7a378 Dispose Vulkan TextureStorage when views hit 0 instead of immediately (#3738)
Due to the `using` statement being scoped to the `CreateTextureView` method, `TextureStorage` would be disposed as soon as the view was returned.

This was largely fine as the TextureStorage resources were being kept alive by the views holding their own references to them, but it also meant that dispose is only called as soon as the texture is created.

Aliased Storages are TextureStorages created with the same allocation as another TextureStorage, if they have to be aliased as another format. We keep track of a TextureStorage's `_aliasedStorages` as they are created, and dispose them when the TextureStorage is disposed...

...except it is disposed immediately, before any aliased storages are even created. The aliased storages added after this will never be disposed.

This PR attempts to fix this by disposing TextureStorage when its view count reaches 0. The other use of texture storage - the D32S8 blit - still manually disposes the storage, but regular uses created via the GAL are now disposed by the view count.

I think this makes the most sense, as otherwise in the future this behaviour might be forgotton and more things could be added to the Dispose() method that don't work due to it not actually calling at the right time.

This should improve memory leaks in Super Mario Odyssey, most noticeable when resolution scaling. The memory usage of the game is still wildly unpredictable due to how it interacts with the texture cache, but now it shouldn't get considerably longer as you play... I hope. I've seen it typically recover back to the same level occasionally, though it can spike significantly.

Please test a bunch of games on multiple GPUs to make sure this doesn't break anything.
2022-10-18 23:52:08 +00:00
9b852c7481 Fix: Arguments Break when Updating (#3744)
* Wrap Args in quotes

-Wrap args in quotes to allow for spaces in dir paths when restarting Ryujinxs from Update.

* Wrap second instance of GetCommandLineArgs()

* Changed ryuArgs from string to string[]

* Update Ryujinx.Ava/Modules/Updater/Updater.cs

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>

* Update UpdateDialog.cs

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>
2022-10-18 23:41:16 +00:00
c40c3905e2 Avoid allocations in .Parse methods (#3760)
* Avoid allocations in .Parse methods

Use the Span overloads of the Parse methods when possible to avoid string allocations and remove one unnecessarry array allocation

* Avoid another string allocation
2022-10-18 23:31:34 +00:00
a6cd044f0f Vulkan: Fix blit levels/layers parameters being inverted (#3768) 2022-10-18 10:13:44 +02:00
f5a1de6ac5 Fix kernel VA allocation when random allocation fails (#3755)
* Fix kernel VA allocation when random allocation fails

* This was off by one
2022-10-17 22:12:49 +00:00
2aeb5b00e3 Update README.md (#3767)
Update compatibility numbers
2022-10-17 23:58:11 +02:00
60ba7b71f2 remove property changed call in time zone validation (#3752) 2022-10-17 16:48:14 +00:00
7c1d2bbb98 Implement OpenDataStorageWithProgramIndex partially (#3765)
* Implement OpenDataStorageWithProgramIndex partially

* Was not supposed to change this
2022-10-17 13:37:05 +00:00
beacf8c1c8 TamperMachine: Fix input mask check (#3764) 2022-10-16 19:51:52 -03:00
0dbe45ae37 Fix various issues caused by Vertex/Index buffer conversions (#3762)
* Fix various issues caused by #3679

- The arguments for the 0th dummy vertex buffer were incorrect - it was given an offset of 16 rather than a size of 16.
- The wrong size was used when doing `autoBuffer.Get` on a converted vertex buffer.
- The possibility of a vertex buffer being disposed and then rebound can rebindings to find a different buffer where the current range is out of bounds. Avoid binding when out of range to prevent validation errors.
- The above also affects generation of converted buffers, which was a bit more fatal. Conversion functions now attempt to bound input offset/size.

* Fix offset for converted buffer
2022-10-16 19:38:58 -03:00
2b50e52e48 Fix primitive count calculation for topology conversion (#3763)
Luigi's Mansion 3 performs a non-index quads draw with 6 vertices. It's meant to ignore the last two, but the index pattern's primitive count calculation was rounding up.

No idea why the game does this but this should fix random triangles in the map.
2022-10-16 19:25:40 -03:00
49eadbc209 Fix phantom configured Controllers (#3720)
Enable guest controller only when a valid host controller is mapped.
2022-10-16 20:34:42 +02:00
2df16ded9b Improve shader BRX instruction code generation (#3759)
* Improve shader BRX instruction code generation

* Shader cache version bump, add some comments and asserts
2022-10-15 23:20:16 +00:00
30 changed files with 276 additions and 81 deletions

View File

@ -36,7 +36,7 @@
## Compatibility
As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
## Usage

View File

@ -588,5 +588,9 @@
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?"
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?",
"RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?",
"SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:",
"SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:",
"VolumeShort": "Głoś"
}

View File

@ -278,7 +278,7 @@ namespace Ryujinx.Modules
{
string ryuName = Path.GetFileName(Environment.ProcessPath);
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray());
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
if (!OperatingSystem.IsWindows())
{

View File

@ -295,8 +295,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
if (_validTzRegions.Contains(location))
{
TimeZone = location;
OnPropertyChanged(nameof(TimeZone));
}
}

View File

@ -164,7 +164,7 @@
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pl_PL"
Header="Polish" />
Header="Polski" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ru_RU"

View File

@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent();
Load();
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
MultiBinding tzMultiBinding = new() { Converter = converter };
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
tzMultiBinding.Bindings.Add(new Binding("Location"));

View File

@ -129,7 +129,7 @@ namespace Ryujinx.Common
private static (Assembly, string) ResolveManifestPath(string filename)
{
var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries);
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length >= 2)
{

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3732;
private const uint CodeGenVersion = 3759;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@ -377,6 +377,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0))
{
HashSet<ulong> visited = new HashSet<ulong>();
InstBrx opBrx = new InstBrx(lastOp.RawOpCode);
ulong baseOffset = lastOp.GetAbsoluteAddress();
@ -392,9 +394,14 @@ namespace Ryujinx.Graphics.Shader.Decoders
for (int i = 0; i < cbOffsetsCount; i++)
{
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
Block target = getBlock(baseOffset + targetOffset);
target.Predecessors.Add(block);
block.Successors.Add(target);
ulong targetAddress = baseOffset + targetOffset;
if (visited.Add(targetAddress))
{
Block target = getBlock(targetAddress);
target.Predecessors.Add(block);
block.Successors.Add(target);
}
}
}
}

View File

@ -41,24 +41,82 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand address = context.IAdd(Register(op.SrcA, RegisterType.Gpr), Const(offset));
// Sorting the target addresses in descending order improves the code,
// since it will always check the most distant targets first, then the
// near ones. This can be easily transformed into if/else statements.
var sortedTargets = context.CurrBlock.Successors.Skip(startIndex).OrderByDescending(x => x.Address);
var targets = context.CurrBlock.Successors.Skip(startIndex);
Block lastTarget = sortedTargets.LastOrDefault();
bool allTargetsSinglePred = true;
int total = context.CurrBlock.Successors.Count - startIndex;
int count = 0;
foreach (Block possibleTarget in sortedTargets)
foreach (var target in targets.OrderBy(x => x.Address))
{
Operand label = context.GetLabel(possibleTarget.Address);
if (possibleTarget != lastTarget)
if (++count < total && (target.Predecessors.Count > 1 || target.Address <= context.CurrBlock.Address))
{
context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)possibleTarget.Address)));
allTargetsSinglePred = false;
break;
}
else
}
if (allTargetsSinglePred)
{
// Chain blocks, each target block will check if the BRX target address
// matches its own address, if not, it jumps to the next target which will do the same check,
// until it reaches the last possible target, which executed unconditionally.
// We can only do this if the BRX block is the only predecessor of all target blocks.
// Additionally, this is not supported for blocks located before the current block,
// since it will be too late to insert a label, but this is something that can be improved
// in the future if necessary.
var sortedTargets = targets.OrderBy(x => x.Address);
Block currentTarget = null;
ulong firstTargetAddress = 0;
foreach (Block nextTarget in sortedTargets)
{
context.Branch(label);
if (currentTarget != null)
{
if (currentTarget.Address != nextTarget.Address)
{
context.SetBrxTarget(currentTarget.Address, address, (int)currentTarget.Address, nextTarget.Address);
}
}
else
{
firstTargetAddress = nextTarget.Address;
}
currentTarget = nextTarget;
}
context.Branch(context.GetLabel(firstTargetAddress));
}
else
{
// Emit the branches sequentially.
// This generates slightly worse code, but should work for all cases.
var sortedTargets = targets.OrderByDescending(x => x.Address);
ulong lastTargetAddress = ulong.MaxValue;
count = 0;
foreach (Block target in sortedTargets)
{
Operand label = context.GetLabel(target.Address);
if (++count < total)
{
if (target.Address != lastTargetAddress)
{
context.BranchIfTrue(label, context.ICompareEqual(address, Const((int)target.Address)));
}
lastTargetAddress = target.Address;
}
else
{
context.Branch(label);
}
}
}
}

View File

@ -21,8 +21,33 @@ namespace Ryujinx.Graphics.Shader.Translation
public int OperationsCount => _operations.Count;
private struct BrxTarget
{
public readonly Operand Selector;
public readonly int ExpectedValue;
public readonly ulong NextTargetAddress;
public BrxTarget(Operand selector, int expectedValue, ulong nextTargetAddress)
{
Selector = selector;
ExpectedValue = expectedValue;
NextTargetAddress = nextTargetAddress;
}
}
private class BlockLabel
{
public readonly Operand Label;
public BrxTarget BrxTarget;
public BlockLabel(Operand label)
{
Label = label;
}
}
private readonly List<Operation> _operations;
private readonly Dictionary<ulong, Operand> _labels;
private readonly Dictionary<ulong, BlockLabel> _labels;
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
{
@ -30,7 +55,7 @@ namespace Ryujinx.Graphics.Shader.Translation
Config = config;
IsNonMain = isNonMain;
_operations = new List<Operation>();
_labels = new Dictionary<ulong, Operand>();
_labels = new Dictionary<ulong, BlockLabel>();
EmitStart();
}
@ -158,14 +183,40 @@ namespace Ryujinx.Graphics.Shader.Translation
public Operand GetLabel(ulong address)
{
if (!_labels.TryGetValue(address, out Operand label))
{
label = Label();
return EnsureBlockLabel(address).Label;
}
_labels.Add(address, label);
public void SetBrxTarget(ulong address, Operand selector, int targetValue, ulong nextTargetAddress)
{
BlockLabel blockLabel = EnsureBlockLabel(address);
Debug.Assert(blockLabel.BrxTarget.Selector == null);
blockLabel.BrxTarget = new BrxTarget(selector, targetValue, nextTargetAddress);
}
public void EnterBlock(ulong address)
{
BlockLabel blockLabel = EnsureBlockLabel(address);
MarkLabel(blockLabel.Label);
BrxTarget brxTarget = blockLabel.BrxTarget;
if (brxTarget.Selector != null)
{
this.BranchIfFalse(GetLabel(brxTarget.NextTargetAddress), this.ICompareEqual(brxTarget.Selector, Const(brxTarget.ExpectedValue)));
}
}
private BlockLabel EnsureBlockLabel(ulong address)
{
if (!_labels.TryGetValue(address, out BlockLabel blockLabel))
{
blockLabel = new BlockLabel(Label());
_labels.Add(address, blockLabel);
}
return label;
return blockLabel;
}
public void PrepareForVertexReturn()

View File

@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
context.CurrBlock = block;
context.MarkLabel(context.GetLabel(block.Address));
context.EnterBlock(block.Address);
EmitOps(context, block);
}

View File

@ -386,8 +386,25 @@ namespace Ryujinx.Graphics.Vulkan
_waitable.WaitForFences(_gd.Api, _device, offset, size);
}
private bool BoundToRange(int offset, ref int size)
{
if (offset >= Size)
{
return false;
}
size = Math.Min(Size - offset, size);
return true;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
var key = new I8ToI16CacheKey(_gd);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
@ -407,6 +424,11 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
@ -428,6 +450,11 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))

View File

@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
public int GetPrimitiveCount(int vertexCount)
{
return Math.Max(0, ((vertexCount - BaseIndex) + IndexStride - 1) / IndexStride);
return Math.Max(0, (vertexCount - BaseIndex) / IndexStride);
}
public int GetConvertedCount(int indexCount)

View File

@ -49,7 +49,12 @@ namespace Ryujinx.Graphics.Vulkan
}
else
{
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int bufferSize);
if (_offset >= bufferSize)
{
autoBuffer = null;
}
offset = _offset;
size = _size;

View File

@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
emptyVb.SetData(0, new byte[EmptyVbSize]);
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0);
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize, 0);
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
@ -1243,7 +1243,7 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
_vertexBuffersDirty &= ~(1u << i);
_vertexBuffersDirty &= ~(1UL << i);
}
}

View File

@ -480,6 +480,8 @@ namespace Ryujinx.Graphics.Vulkan
if (--_viewsCount == 0)
{
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
Dispose();
}
}

View File

@ -515,10 +515,10 @@ namespace Ryujinx.Graphics.Vulkan
dst.Info,
srcRegion,
dstRegion,
src.FirstLevel,
dst.FirstLevel,
src.FirstLayer,
dst.FirstLayer,
src.FirstLevel,
dst.FirstLevel,
layers,
levels,
linearFilter,

View File

@ -57,37 +57,48 @@ namespace Ryujinx.Graphics.Vulkan
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
{
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
int stride = (_stride + (alignment - 1)) & -alignment;
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (autoBuffer != null)
{
int stride = (_stride + (alignment - 1)) & -alignment;
int newSize = (_size / _stride) * stride;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
0,
(ulong)(_size / _stride) * (ulong)stride,
(ulong)stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
0,
(ulong)newSize,
(ulong)stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
}
_buffer = autoBuffer;
}
_buffer = autoBuffer;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
return;
}
else
{
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
// The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
if (_offset >= size)
{
autoBuffer = null;
}
}
}

View File

@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
internal TextureView CreateTextureView(TextureCreateInfo info, float scale)
{
// This should be disposed when all views are destroyed.
using var storage = CreateTextureStorage(info, scale);
var storage = CreateTextureStorage(info, scale);
return storage.CreateView(info, 0, 0);
}

View File

@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
return -1;
}
return int.Parse(part.Substring(0, numberLength));
return int.Parse(part.AsSpan(0, numberLength));
}
private string ParseNumber(bool isSigned = false)

View File

@ -2540,11 +2540,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int attempt = 0; attempt < 8; attempt++)
{
address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
ulong aslrEndAddr = aslrAddress + totalNeededSize;
ulong endAddr = address + totalNeededSize;
KMemoryInfo info = _blockManager.FindBlock(address).GetInfo();
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
if (info.State != MemoryState.Unmapped)
{
@ -2554,11 +2553,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
ulong currEndAddr = info.Address + info.Size;
if (address >= regionStart &&
address >= currBaseAddr &&
endAddr - 1 <= regionEndAddr - 1 &&
endAddr - 1 <= currEndAddr - 1)
if (aslrAddress >= regionStart &&
aslrAddress >= currBaseAddr &&
aslrEndAddr - 1 <= regionEndAddr - 1 &&
aslrEndAddr - 1 <= currEndAddr - 1)
{
address = aslrAddress;
break;
}
}
@ -2603,7 +2603,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
ulong regionEndAddr = (regionStart + regionPagesCount * PageSize) - 1;
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);

View File

@ -12,6 +12,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
using System;
using System.IO;
using static Ryujinx.HLE.Utilities.StringUtils;
@ -787,6 +788,26 @@ namespace Ryujinx.HLE.HOS.Services.Fs
return ResultCode.Success;
}
[CommandHipc(205)]
// OpenDataStorageWithProgramIndex(u8 program_index) -> object<nn::fssrv::sf::IStorage>
public ResultCode OpenDataStorageWithProgramIndex(ServiceCtx context)
{
byte programIndex = context.RequestData.ReadByte();
if ((context.Device.Application.TitleId & 0xf) != programIndex)
{
throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex}).");
}
var storage = context.Device.FileSystem.RomFs.AsStorage(true);
using var sharedStorage = new SharedRef<LibHac.Fs.IStorage>(storage);
using var sfStorage = new SharedRef<IStorage>(new StorageInterfaceAdapter(ref sharedStorage.Ref()));
MakeObject(context, new FileSystemProxy.IStorage(ref sfStorage.Ref()));
return ResultCode.Success;
}
[CommandHipc(400)]
// OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
public ResultCode OpenDeviceOperator(ServiceCtx context)

View File

@ -816,11 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
Reserved = new Array57<byte>()
};
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber));
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber);
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber);
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber);
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber);
context.Memory.Write(outputPosition, modelInfo);

View File

@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Tamper.Conditions
public bool Evaluate()
{
return (_input.Value & _mask) != 0;
return (_input.Value & _mask) == _mask;
}
}
}

View File

@ -122,9 +122,8 @@ namespace Ryujinx.HLE.HOS.Tamper
for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++)
{
int index = wordIndex * wordSize + nybbleIndex;
string byteData = word.Substring(nybbleIndex, 1);
instruction[index] = byte.Parse(byteData, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
instruction[index] = byte.Parse(word.AsSpan(nybbleIndex, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
}

View File

@ -132,7 +132,7 @@ namespace Ryujinx.HLE.Loaders.Mods
{
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
return int.TryParse(str.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out value);
return int.TryParse(str.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value);
}
else
{

View File

@ -54,7 +54,7 @@ namespace Ryujinx.HLE.Utilities
for (int index = 0; index < bytesInHex; index++)
{
output[index] = byte.Parse(hexString.Substring(index * 2, 2), NumberStyles.HexNumber);
output[index] = byte.Parse(hexString.AsSpan(index * 2, 2), NumberStyles.HexNumber);
}
return output;

View File

@ -51,7 +51,16 @@ namespace Ryujinx.Input.HLE
{
lock (_lock)
{
_device.Hid.RefreshInputConfig(_inputConfig);
List<InputConfig> validInputs = new List<InputConfig>();
foreach (var inputConfigEntry in _inputConfig)
{
if (_controllers[(int)inputConfigEntry.PlayerIndex] != null)
{
validInputs.Add(inputConfigEntry);
}
}
_device.Hid.RefreshInputConfig(validInputs);
}
}
@ -103,6 +112,8 @@ namespace Ryujinx.Input.HLE
_controllers[i] = null;
}
List<InputConfig> validInputs = new List<InputConfig>();
foreach (InputConfig inputConfigEntry in inputConfig)
{
NpadController controller = new NpadController(_cemuHookClient);
@ -116,6 +127,7 @@ namespace Ryujinx.Input.HLE
else
{
_controllers[(int)inputConfigEntry.PlayerIndex] = controller;
validInputs.Add(inputConfigEntry);
}
}
@ -123,7 +135,7 @@ namespace Ryujinx.Input.HLE
_enableKeyboard = enableKeyboard;
_enableMouse = enableMouse;
_device.Hid.RefreshInputConfig(inputConfig);
_device.Hid.RefreshInputConfig(validInputs);
}
}

View File

@ -48,7 +48,7 @@ namespace Ryujinx.Modules
{
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
var ryuArg = Environment.GetCommandLineArgs().AsEnumerable().Skip(1);
Process.Start(ryuExe, ryuArg);