diff --git a/.github/assign/audio.yml b/.github/assign/audio.yml new file mode 100644 index 000000000..337007d39 --- /dev/null +++ b/.github/assign/audio.yml @@ -0,0 +1,8 @@ +addReviewers: true + +reviewers: + - marysaka + +filterLabels: + include: + - audio \ No newline at end of file diff --git a/.github/assign/cpu.yml b/.github/assign/cpu.yml new file mode 100644 index 000000000..da824bdc3 --- /dev/null +++ b/.github/assign/cpu.yml @@ -0,0 +1,11 @@ +addReviewers: true + +reviewers: + - gdkchan + - riperiperi + - marysaka + - LDj3SNuD + +filterLabels: + include: + - cpu \ No newline at end of file diff --git a/.github/assign/global.yml b/.github/assign/global.yml new file mode 100644 index 000000000..53a9af429 --- /dev/null +++ b/.github/assign/global.yml @@ -0,0 +1,4 @@ +addReviewers: true + +reviewers: + - Ryujinx/developers \ No newline at end of file diff --git a/.github/assign/gpu.yml b/.github/assign/gpu.yml new file mode 100644 index 000000000..b96d9d87d --- /dev/null +++ b/.github/assign/gpu.yml @@ -0,0 +1,10 @@ +addReviewers: true + +reviewers: + - gdkchan + - riperiperi + - marysaka + +filterLabels: + include: + - gpu \ No newline at end of file diff --git a/.github/assign/gui.yml b/.github/assign/gui.yml new file mode 100644 index 000000000..9731ea5bd --- /dev/null +++ b/.github/assign/gui.yml @@ -0,0 +1,11 @@ +addReviewers: true + +reviewers: + - Ack77 + - emmauss + - TSRBerry + - marysaka + +filterLabels: + include: + - gui \ No newline at end of file diff --git a/.github/assign/horizon.yml b/.github/assign/horizon.yml new file mode 100644 index 000000000..966382b29 --- /dev/null +++ b/.github/assign/horizon.yml @@ -0,0 +1,11 @@ +addReviewers: true + +reviewers: + - gdkchan + - Ack77 + - marysaka + - TSRBerry + +filterLabels: + include: + - horizon \ No newline at end of file diff --git a/.github/assign/infra.yml b/.github/assign/infra.yml new file mode 100644 index 000000000..d319fef19 --- /dev/null +++ b/.github/assign/infra.yml @@ -0,0 +1,9 @@ +addReviewers: true + +reviewers: + - marysaka + - TSRBerry + +filterLabels: + include: + - infra \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..587830be1 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,33 @@ +audio: 'src/Ryujinx.Audio*/**' + +cpu: + - 'src/ARMeilleure/**' + - 'src/Ryujinx.Cpu/**' + - 'src/Ryujinx.Memory/**' + +gpu: + - 'src/Ryujinx.Graphics.*/**' + - 'src/Spv.Generator/**' + - 'src/Ryujinx.ShaderTools/**' + +'graphics-backend:opengl': 'src/Ryujinx.Graphics.OpenGL/**' +'graphics-backend:vulkan': + - 'src/Ryujinx.Graphics.Vulkan/**' + - 'src/Spv.Generator/**' + +gui: + - 'src/Ryujinx/**' + - 'src/Ryujinx.Ui.Common/**' + - 'src/Ryujinx.Ui.LocaleGenerator/**' + - 'src/Ryujinx.Ava/**' + +horizon: + - 'src/Ryujinx.HLE/**' + - 'src/Ryujinx.Horizon*/**' + +kernel: 'src/Ryujinx.HLE/HOS/Kernel/**' + +infra: + - '.github/**' + - 'distribution/**' + - 'Directory.Packages.props' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97db387f0..bf8fd000a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,19 +3,13 @@ name: Build job on: workflow_dispatch: inputs: {} - #push: - # branches: [ master ] - # paths-ignore: - # - '.github/*' - # - '.github/ISSUE_TEMPLATE/**' - # - '*.yml' - # - 'README.md' pull_request: branches: [ master ] paths-ignore: - - '.github/*' - - '.github/ISSUE_TEMPLATE/**' + - '.github/**' - '*.yml' + - '*.json' + - '*.config' - 'README.md' concurrency: diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml new file mode 100644 index 000000000..e32dd27ad --- /dev/null +++ b/.github/workflows/pr_triage.yml @@ -0,0 +1,54 @@ +name: "Pull Request Triage" +on: + pull_request_target: + types: [opened, ready_for_review] + +jobs: + triage: + permissions: + contents: read + pull-requests: write + + runs-on: ubuntu-latest + + steps: + - name: Update labels based on changes + uses: actions/labeler@v4 + with: + sync-labels: true + dot: true + + - name: Auto Assign [Audio] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/audio.yml' + + - name: Auto Assign [CPU] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/cpu.yml' + + - name: Auto Assign [GPU] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/gpu.yml' + + - name: Auto Assign [GUI] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/gui.yml' + + - name: Auto Assign [Horizon] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/horizon.yml' + + - name: Auto Assign [Infra] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/infra.yml' + + - name: Auto Assign [Global] + uses: kentaro-m/auto-assign-action@v1.2.5 + with: + configuration-path: '.github/assign/global.yml' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 98ba34822..63e6d0187 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,9 +6,10 @@ on: push: branches: [ master ] paths-ignore: - - '.github/*' - - '.github/ISSUE_TEMPLATE/**' + - '.github/**' - '*.yml' + - '*.json' + - '*.config' - 'README.md' concurrency: release diff --git a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index 33ae83df6..bec36e2d6 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -165,7 +165,7 @@ namespace ARMeilleure.Instructions { Operand m = GetVecA32(op.Vm >> 1); - Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, doubleSize); + Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, true); Intrinsic inst = (unsigned ? Intrinsic.Arm64FcvtzuGp : Intrinsic.Arm64FcvtzsGp) | Intrinsic.Arm64VDouble; @@ -175,7 +175,7 @@ namespace ARMeilleure.Instructions } else { - InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuS : Intrinsic.Arm64FcvtzsS); + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, unsigned ? Intrinsic.Arm64FcvtzuS : Intrinsic.Arm64FcvtzsS, false); } } else if (!roundWithFpscr && Optimizations.UseSse41) @@ -260,28 +260,64 @@ namespace ARMeilleure.Instructions if (Optimizations.UseAdvSimd) { - if (unsigned) + bool doubleSize = floatSize == OperandType.FP64; + + if (doubleSize) { - inst = rm switch { - 0b00 => Intrinsic.Arm64FcvtauS, - 0b01 => Intrinsic.Arm64FcvtnuS, - 0b10 => Intrinsic.Arm64FcvtpuS, - 0b11 => Intrinsic.Arm64FcvtmuS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) - }; + Operand m = GetVecA32(op.Vm >> 1); + + Operand toConvert = InstEmitSimdHelper32Arm64.EmitExtractScalar(context, m, op.Vm, true); + + if (unsigned) + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtauGp, + 0b01 => Intrinsic.Arm64FcvtnuGp, + 0b10 => Intrinsic.Arm64FcvtpuGp, + 0b11 => Intrinsic.Arm64FcvtmuGp, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + else + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtasGp, + 0b01 => Intrinsic.Arm64FcvtnsGp, + 0b10 => Intrinsic.Arm64FcvtpsGp, + 0b11 => Intrinsic.Arm64FcvtmsGp, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + + Operand asInteger = context.AddIntrinsicInt(inst | Intrinsic.Arm64VDouble, toConvert); + + InsertScalar(context, op.Vd, asInteger); } else { - inst = rm switch { - 0b00 => Intrinsic.Arm64FcvtasS, - 0b01 => Intrinsic.Arm64FcvtnsS, - 0b10 => Intrinsic.Arm64FcvtpsS, - 0b11 => Intrinsic.Arm64FcvtmsS, - _ => throw new ArgumentOutOfRangeException(nameof(rm)) - }; - } + if (unsigned) + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtauS, + 0b01 => Intrinsic.Arm64FcvtnuS, + 0b10 => Intrinsic.Arm64FcvtpuS, + 0b11 => Intrinsic.Arm64FcvtmuS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } + else + { + inst = rm switch { + 0b00 => Intrinsic.Arm64FcvtasS, + 0b01 => Intrinsic.Arm64FcvtnsS, + 0b10 => Intrinsic.Arm64FcvtpsS, + 0b11 => Intrinsic.Arm64FcvtmsS, + _ => throw new ArgumentOutOfRangeException(nameof(rm)) + }; + } - InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); + InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, inst); + } } else if (Optimizations.UseSse41) { diff --git a/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs index 98236be6d..804d915c4 100644 --- a/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs +++ b/src/ARMeilleure/Instructions/InstEmitSimdHelper32Arm64.cs @@ -192,11 +192,10 @@ namespace ARMeilleure.Instructions EmitVectorTernaryOpSimd32(context, (d, n, m) => context.AddIntrinsic(inst, d, n, m)); } - public static void EmitScalarUnaryOpSimd32(ArmEmitterContext context, Func1I scalarFunc) + public static void EmitScalarUnaryOpSimd32(ArmEmitterContext context, Func1I scalarFunc, bool doubleSize) { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - bool doubleSize = (op.Size & 1) != 0; int shift = doubleSize ? 1 : 2; Operand m = GetVecA32(op.Vm >> shift); Operand d = GetVecA32(op.Vd >> shift); @@ -215,8 +214,13 @@ namespace ARMeilleure.Instructions { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; - inst |= ((op.Size & 1) != 0 ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; - EmitScalarUnaryOpSimd32(context, (m) => (inst == 0) ? m : context.AddIntrinsic(inst, m)); + EmitScalarUnaryOpF32(context, inst, (op.Size & 1) != 0); + } + + public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Intrinsic inst, bool doubleSize) + { + inst |= (doubleSize ? Intrinsic.Arm64VDouble : Intrinsic.Arm64VFloat) | Intrinsic.Arm64V128; + EmitScalarUnaryOpSimd32(context, (m) => (inst == 0) ? m : context.AddIntrinsic(inst, m), doubleSize); } public static void EmitScalarBinaryOpSimd32(ArmEmitterContext context, Func2I scalarFunc) diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index daa2eeac2..aa732d0ae 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -2,6 +2,7 @@ using ARMeilleure.CodeGen; using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.Memory; using ARMeilleure.Native; +using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,8 +13,8 @@ namespace ARMeilleure.Translation.Cache { static partial class JitCache { - private const int PageSize = 4 * 1024; - private const int PageMask = PageSize - 1; + private static readonly int PageSize = (int)MemoryBlock.GetPageSize(); + private static readonly int PageMask = PageSize - 1; private const int CodeAlignment = 4; // Bytes. private const int CacheSize = 2047 * 1024 * 1024; diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 366dea6bf..3c697bffe 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -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 = 5281; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 5292; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; diff --git a/src/Ryujinx.Common/Memory/SpanReader.cs b/src/Ryujinx.Common/Memory/SpanReader.cs index 673932d0b..9a1a0a3f0 100644 --- a/src/Ryujinx.Common/Memory/SpanReader.cs +++ b/src/Ryujinx.Common/Memory/SpanReader.cs @@ -24,6 +24,24 @@ namespace Ryujinx.Common.Memory return value; } + public bool TryRead(out T value) where T : unmanaged + { + int valueSize = Unsafe.SizeOf(); + + if (valueSize > _input.Length) + { + value = default; + + return false; + } + + value = MemoryMarshal.Cast(_input)[0]; + + _input = _input.Slice(valueSize); + + return true; + } + public ReadOnlySpan GetSpan(int size) { ReadOnlySpan data = _input.Slice(0, size); diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index ac983cfe5..243510967 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS } private static DirectoryInfo FindTitleDir(DirectoryInfo contentsDir, string titleId) - => contentsDir.EnumerateDirectories($"{titleId}*", DirEnumOptions).FirstOrDefault(); + => contentsDir.EnumerateDirectories(titleId, DirEnumOptions).FirstOrDefault(); private static void AddModsFromDirectory(ModCache mods, DirectoryInfo dir, string titleId) { diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs index bfd1d4dcd..eec5292f5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs @@ -1,8 +1,29 @@ -namespace Ryujinx.HLE.HOS.Services.Hid +using Ryujinx.Common; +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Hid { [Service("hidbus")] class IHidbusServer : IpcService { public IHidbusServer(ServiceCtx context) { } + + [CommandCmif(1)] + // GetBusHandle(nn::hid::NpadIdType, nn::hidbus::BusType, nn::applet::AppletResourceUserId) -> (bool HasHandle, nn::hidbus::BusHandle) + public ResultCode GetBusHandle(ServiceCtx context) + { + NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32(); + context.RequestData.BaseStream.Position += 4; // Padding + BusType busType = (BusType)context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); + + context.ResponseData.Write(false); + context.ResponseData.BaseStream.Position += 7; // Padding + context.ResponseData.WriteStruct(new BusHandle()); + + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { npadIdType, busType, appletResourceUserId }); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs new file mode 100644 index 000000000..936ee68c4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusHandle.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid +{ + [StructLayout(LayoutKind.Sequential)] + struct BusHandle + { + public int AbstractedPadId; + public byte InternalIndex; + public byte PlayerNumber; + public byte BusTypeId; + public byte IsValid; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs new file mode 100644 index 000000000..41852365b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/BusType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum BusType : long + { + LeftJoyRail = 0, + RightJoyRail = 1, + InternalBus = 2 + } +} diff --git a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs index e930bdd75..88dddef5e 100644 --- a/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs +++ b/src/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Horizon.LogManager.Ipc private const int MessageLengthLimit = 5000; private readonly LogService _log; - private readonly ulong _pid; + private readonly ulong _pid; private LogPacket _logPacket; @@ -74,8 +74,12 @@ namespace Ryujinx.Horizon.LogManager.Ipc private bool LogImpl(ReadOnlySpan message) { - SpanReader reader = new(message); - LogPacketHeader header = reader.Read(); + SpanReader reader = new(message); + + if (!reader.TryRead(out LogPacketHeader header)) + { + return true; + } bool isHeadPacket = (header.Flags & LogPacketFlags.IsHead) != 0; bool isTailPacket = (header.Flags & LogPacketFlags.IsTail) != 0; @@ -84,8 +88,10 @@ namespace Ryujinx.Horizon.LogManager.Ipc while (reader.Length > 0) { - int type = ReadUleb128(ref reader); - int size = ReadUleb128(ref reader); + if (!TryReadUleb128(ref reader, out int type) || !TryReadUleb128(ref reader, out int size)) + { + return true; + } LogDataChunkKey key = (LogDataChunkKey)type; @@ -101,15 +107,24 @@ namespace Ryujinx.Horizon.LogManager.Ipc } else if (key == LogDataChunkKey.Line) { - _logPacket.Line = reader.Read(); + if (!reader.TryRead(out _logPacket.Line)) + { + return true; + } } else if (key == LogDataChunkKey.DropCount) { - _logPacket.DropCount = reader.Read(); + if (!reader.TryRead(out _logPacket.DropCount)) + { + return true; + } } else if (key == LogDataChunkKey.Time) { - _logPacket.Time = reader.Read(); + if (!reader.TryRead(out _logPacket.Time)) + { + return true; + } } else if (key == LogDataChunkKey.Message) { @@ -154,23 +169,25 @@ namespace Ryujinx.Horizon.LogManager.Ipc return isTailPacket; } - private static int ReadUleb128(ref SpanReader reader) + private static bool TryReadUleb128(ref SpanReader reader, out int result) { - int result = 0; - int count = 0; - + result = 0; + int count = 0; byte encoded; do { - encoded = reader.Read(); + if (!reader.TryRead(out encoded)) + { + return false; + } result += (encoded & 0x7F) << (7 * count); count++; } while ((encoded & 0x80) != 0); - return result; + return true; } } } \ No newline at end of file diff --git a/src/Ryujinx.Tests.Memory/Tests.cs b/src/Ryujinx.Tests.Memory/Tests.cs index d8a243e3e..5ab01d5a5 100644 --- a/src/Ryujinx.Tests.Memory/Tests.cs +++ b/src/Ryujinx.Tests.Memory/Tests.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Tests.Memory { public class Tests { - private const ulong MemorySize = 0x8000; + private static readonly ulong MemorySize = MemoryBlock.GetPageSize() * 8; private MemoryBlock _memoryBlock; @@ -44,14 +44,17 @@ namespace Ryujinx.Tests.Memory [Platform(Exclude = "MacOsX")] public void Test_Alias() { - using MemoryBlock backing = new MemoryBlock(0x10000, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(0x10000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + ulong pageSize = MemoryBlock.GetPageSize(); + ulong blockSize = MemoryBlock.GetPageSize() * 16; - toAlias.MapView(backing, 0x1000, 0, 0x4000); - toAlias.UnmapView(backing, 0x3000, 0x1000); + using MemoryBlock backing = new MemoryBlock(blockSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new MemoryBlock(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + + toAlias.MapView(backing, pageSize, 0, pageSize * 4); + toAlias.UnmapView(backing, pageSize * 3, pageSize); toAlias.Write(0, 0xbadc0de); - Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, 0x1000), 0xbadc0de); + Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (int)pageSize), 0xbadc0de); } [Test] @@ -59,8 +62,12 @@ namespace Ryujinx.Tests.Memory [Platform(Exclude = "MacOsX")] public void Test_AliasRandom() { - using MemoryBlock backing = new MemoryBlock(0x80000, MemoryAllocationFlags.Mirrorable); - using MemoryBlock toAlias = new MemoryBlock(0x80000, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + ulong pageSize = MemoryBlock.GetPageSize(); + int pageBits = (int)ulong.Log2(pageSize); + ulong blockSize = MemoryBlock.GetPageSize() * 128; + + using MemoryBlock backing = new MemoryBlock(blockSize, MemoryAllocationFlags.Mirrorable); + using MemoryBlock toAlias = new MemoryBlock(blockSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); Random rng = new Random(123); @@ -72,16 +79,16 @@ namespace Ryujinx.Tests.Memory if ((rng.Next() & 1) != 0) { - toAlias.MapView(backing, (ulong)srcPage << 12, (ulong)dstPage << 12, (ulong)pages << 12); + toAlias.MapView(backing, (ulong)srcPage << pageBits, (ulong)dstPage << pageBits, (ulong)pages << pageBits); - int offset = rng.Next(0, 0x1000 - sizeof(int)); + int offset = rng.Next(0, (int)pageSize - sizeof(int)); - toAlias.Write((ulong)((dstPage << 12) + offset), 0xbadc0de); - Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (srcPage << 12) + offset), 0xbadc0de); + toAlias.Write((ulong)((dstPage << pageBits) + offset), 0xbadc0de); + Assert.AreEqual(Marshal.ReadInt32(backing.Pointer, (srcPage << pageBits) + offset), 0xbadc0de); } else { - toAlias.UnmapView(backing, (ulong)dstPage << 12, (ulong)pages << 12); + toAlias.UnmapView(backing, (ulong)dstPage << pageBits, (ulong)pages << pageBits); } } } @@ -91,7 +98,7 @@ namespace Ryujinx.Tests.Memory [Platform(Exclude = "MacOsX")] public void Test_AliasMapLeak() { - ulong pageSize = 4096; + ulong pageSize = MemoryBlock.GetPageSize(); ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that. using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable); diff --git a/src/Ryujinx.Tests/Cpu/CpuTest.cs b/src/Ryujinx.Tests/Cpu/CpuTest.cs index 979b313b0..ad4ba539b 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTest.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Tests.Cpu [TestFixture] public class CpuTest { - protected const ulong Size = 0x1000; - protected const ulong CodeBaseAddress = 0x1000; - protected const ulong DataBaseAddress = CodeBaseAddress + Size; + protected static readonly ulong Size = MemoryBlock.GetPageSize(); + protected static ulong CodeBaseAddress = Size; + protected static ulong DataBaseAddress = CodeBaseAddress + Size; private static bool Ignore_FpcrFz = false; private static bool Ignore_FpcrDn = false; @@ -39,12 +39,24 @@ namespace Ryujinx.Tests.Cpu [SetUp] public void Setup() { - _currAddress = CodeBaseAddress; + int pageBits = (int)ulong.Log2(Size); _ram = new MemoryBlock(Size * 2); - _memory = new MemoryManager(_ram, 1ul << 16); + _memory = new MemoryManager(_ram, 1ul << (pageBits + 4)); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, 0, Size * 2, MemoryMapFlags.Private); + + // Some tests depends on hardcoded address that were computed for 4KiB. + // We change the layout on non 4KiB platforms to keep compat here. + if (Size > 0x1000) + { + DataBaseAddress = 0; + CodeBaseAddress = Size; + } + + _currAddress = CodeBaseAddress; + + _memory.Map(CodeBaseAddress, 0, Size, MemoryMapFlags.Private); + _memory.Map(DataBaseAddress, Size, Size, MemoryMapFlags.Private); _context = CpuContext.CreateExecutionContext(); Translator.IsReadyForTranslation.Set(); diff --git a/src/Ryujinx.Tests/Cpu/CpuTest32.cs b/src/Ryujinx.Tests/Cpu/CpuTest32.cs index 47dc9f8a8..a1f6431cf 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Tests.Cpu [TestFixture] public class CpuTest32 { - protected const uint Size = 0x1000; - protected const uint CodeBaseAddress = 0x1000; - protected const uint DataBaseAddress = CodeBaseAddress + Size; + protected static readonly uint Size = (uint)MemoryBlock.GetPageSize(); + protected static uint CodeBaseAddress = Size; + protected static uint DataBaseAddress = CodeBaseAddress + Size; private uint _currAddress; @@ -33,12 +33,24 @@ namespace Ryujinx.Tests.Cpu [SetUp] public void Setup() { - _currAddress = CodeBaseAddress; + int pageBits = (int)ulong.Log2(Size); _ram = new MemoryBlock(Size * 2); - _memory = new MemoryManager(_ram, 1ul << 16); + _memory = new MemoryManager(_ram, 1ul << (pageBits + 4)); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, 0, Size * 2, MemoryMapFlags.Private); + + // Some tests depends on hardcoded address that were computed for 4KiB. + // We change the layout on non 4KiB platforms to keep compat here. + if (Size > 0x1000) + { + DataBaseAddress = 0; + CodeBaseAddress = Size; + } + + _currAddress = CodeBaseAddress; + + _memory.Map(CodeBaseAddress, 0, Size, MemoryMapFlags.Private); + _memory.Map(DataBaseAddress, Size, Size, MemoryMapFlags.Private); _context = CpuContext.CreateExecutionContext(); _context.IsAarch32 = true; diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs index 2f9504cbf..c88c02c1b 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdMemory32.cs @@ -1,6 +1,7 @@ #define SimdMemory32 using ARMeilleure.State; +using Ryujinx.Memory; using NUnit.Framework; using System; @@ -9,6 +10,7 @@ namespace Ryujinx.Tests.Cpu [Category("SimdMemory32")] public sealed class CpuTestSimdMemory32 : CpuTest32 { + private static readonly uint TestOffset = DataBaseAddress + 0x500; #if SimdMemory32 private uint[] _ldStModes = @@ -42,7 +44,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 3u)] uint n, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4a00000u; // VLD1.8 {D0[0]}, [R0], R0 @@ -58,7 +60,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. - SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -72,7 +74,7 @@ namespace Ryujinx.Tests.Cpu [Values] bool t, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4a00c00u; // VLD1.8 {D0[0]}, [R0], R0 @@ -85,7 +87,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. if (t) opcode |= 1 << 5; - SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -98,7 +100,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 10u)] uint mode, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4200000u; // VLD4.8 {D0, D1, D2, D3}, [R0], R0 @@ -114,7 +116,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -128,7 +130,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 3u)] uint n, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); (V128 vec1, V128 vec2, V128 vec3, V128 vec4) = GenerateTestVectors(); @@ -146,7 +148,7 @@ namespace Ryujinx.Tests.Cpu opcode |= (n & 3) << 8; // ST1 is 0, ST2 is 1 etc. - SingleOpcode(opcode, r0: 0x2500, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); CompareAgainstUnicorn(); } @@ -159,7 +161,7 @@ namespace Ryujinx.Tests.Cpu [Range(0u, 10u)] uint mode, [Values(0x0u)] uint offset) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); (V128 vec1, V128 vec2, V128 vec3, V128 vec4) = GenerateTestVectors(); @@ -177,7 +179,7 @@ namespace Ryujinx.Tests.Cpu opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); - SingleOpcode(opcode, r0: 0x2500, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); CompareAgainstUnicorn(); } @@ -189,7 +191,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x1u, 0x32u)] uint regs, [Values] bool single) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xec100a00u; // VST4.8 {D0, D1, D2, D3}, [R0], R0 @@ -225,7 +227,7 @@ namespace Ryujinx.Tests.Cpu opcode |= regs & 0xff; - SingleOpcode(opcode, r0: 0x2500, sp: 0x2500); + SingleOpcode(opcode, r0: TestOffset, sp: TestOffset); CompareAgainstUnicorn(); } @@ -237,7 +239,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x0u)] uint imm, [Values] bool sub) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xed900a00u; // VLDR.32 S0, [R0, #0] @@ -260,7 +262,7 @@ namespace Ryujinx.Tests.Cpu } opcode |= imm & 0xff; - SingleOpcode(opcode, r0: 0x2500); + SingleOpcode(opcode, r0: TestOffset); CompareAgainstUnicorn(); } @@ -272,7 +274,7 @@ namespace Ryujinx.Tests.Cpu [Values(0x0u)] uint imm, [Values] bool sub) { - var data = GenerateVectorSequence(0x1000); + var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xed800a00u; // VSTR.32 S0, [R0, #0] @@ -297,7 +299,7 @@ namespace Ryujinx.Tests.Cpu (V128 vec1, V128 vec2, _, _) = GenerateTestVectors(); - SingleOpcode(opcode, r0: 0x2500, v0: vec1, v1: vec2); + SingleOpcode(opcode, r0: TestOffset, v0: vec1, v1: vec2); CompareAgainstUnicorn(); } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs index b19137a4b..603e2a559 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs @@ -3,6 +3,7 @@ using ARMeilleure.State; using NUnit.Framework; using System.Collections.Generic; +using System.Runtime.InteropServices; namespace Ryujinx.Tests.Cpu { @@ -703,6 +704,11 @@ namespace Ryujinx.Tests.Cpu [Values] bool q, [Values] bool u) { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + Assert.Ignore("Unicorn on ARM64 crash while executing this test"); + } + uint opcode = 0xf2000400u; // VSHL.S8 D0, D0, D0 if (q) { diff --git a/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs index 2c83b01d8..03d90a1f1 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestT32Flow.cs @@ -109,7 +109,7 @@ namespace Ryujinx.Tests.Cpu ExecuteOpcodes(runUnicorn: false); - Assert.That(GetContext().GetX(0), Is.EqualTo(0x1005)); + Assert.That(GetContext().GetX(0), Is.EqualTo(CodeBaseAddress + 0x5)); } [Test] @@ -133,7 +133,7 @@ namespace Ryujinx.Tests.Cpu ExecuteOpcodes(runUnicorn: false); - Assert.That(GetContext().GetX(0), Is.EqualTo(0x1005)); + Assert.That(GetContext().GetX(0), Is.EqualTo(CodeBaseAddress + 0x5)); Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false)); } @@ -160,7 +160,7 @@ namespace Ryujinx.Tests.Cpu ExecuteOpcodes(runUnicorn: false); - Assert.That(GetContext().GetX(0), Is.EqualTo(0x1007)); + Assert.That(GetContext().GetX(0), Is.EqualTo(CodeBaseAddress + 0x7)); Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false)); } } diff --git a/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs index b740f524a..3d13ff733 100644 --- a/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs +++ b/src/Ryujinx.Tests/Cpu/CpuTestThumb.cs @@ -268,6 +268,12 @@ namespace Ryujinx.Tests.Cpu [Test] public void TestRandomTestCases([ValueSource(nameof(RandomTestCases))] PrecomputedThumbTestCase test) { + if (Size != 0x1000) + { + // TODO: Change it to depend on DataBaseAddress instead. + Assert.Ignore("This test currently only support 4KiB page size"); + } + RunPrecomputedTestCase(test); } diff --git a/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs index 4f4b25245..542db9a7e 100644 --- a/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.Ui.Common/Helper/FileAssociationHelper.cs @@ -11,10 +11,10 @@ namespace Ryujinx.Ui.Common.Helper { public static partial class FileAssociationHelper { - private static string[] _fileExtensions = new string[] { ".nca", ".nro", ".nso", ".nsp", ".xci" }; + private static readonly string[] _fileExtensions = { ".nca", ".nro", ".nso", ".nsp", ".xci" }; [SupportedOSPlatform("linux")] - private static string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); + private static readonly string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); private const int SHCNE_ASSOCCHANGED = 0x8000000; private const int SHCNF_FLUSH = 0x1000; @@ -32,7 +32,7 @@ namespace Ryujinx.Ui.Common.Helper { string installKeyword = uninstall ? "uninstall" : "install"; - if (!AreMimeTypesRegisteredLinux()) + if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux())) { string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml"); string additionalArgs = !uninstall ? "--novendor" : ""; @@ -81,9 +81,9 @@ namespace Ryujinx.Ui.Common.Helper return false; } - key.OpenSubKey(@"shell\open\command"); + var openCmd = key.OpenSubKey(@"shell\open\command"); - string keyValue = (string)key.GetValue(""); + string keyValue = (string)openCmd.GetValue(""); return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName)); } @@ -107,30 +107,31 @@ namespace Ryujinx.Ui.Common.Helper if (uninstall) { + // If the types don't already exist, there's nothing to do and we can call this operation successful. if (!AreMimeTypesRegisteredWindows()) { - return false; + return true; } - + Logger.Debug?.Print(LogClass.Application, $"Removing type association {ext}"); Registry.CurrentUser.DeleteSubKeyTree(keyString); + Logger.Debug?.Print(LogClass.Application, $"Removed type association {ext}"); } else { - RegistryKey key = Registry.CurrentUser.CreateSubKey(keyString); + using var key = Registry.CurrentUser.CreateSubKey(keyString); + if (key is null) { return false; } - key.CreateSubKey(@"shell\open\command"); + Logger.Debug?.Print(LogClass.Application, $"Adding type association {ext}"); + using var openCmd = key.CreateSubKey(@"shell\open\command"); + openCmd.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\""); + Logger.Debug?.Print(LogClass.Application, $"Added type association {ext}"); - key.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\""); - key.Close(); } - // Notify Explorer the file association has been changed. - SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero); - return true; } @@ -141,6 +142,9 @@ namespace Ryujinx.Ui.Common.Helper registered |= RegisterExtension(ext, uninstall); } + // Notify Explorer the file association has been changed. + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero); + return registered; }