Compare commits

...

77 Commits

Author SHA1 Message Date
dcf10561b9 Fix missing texture flush for draw then DMA copy sequence without render target change (#5933)
* Unbind render targets before DMA copy

* Move DirtyAction to TextureGroupHandle

* Fix lost copy dependency bug

* XML doc
2023-11-15 21:36:25 -03:00
cdc8fed64f chore: Update OpenTK to 4.8.1 (#5912)
OpenTK.OpenAL was renamed to OpenTK.Audio.OpenAL.

Signed-off-by: Mary Guillemard <mary@mary.zone>
2023-11-15 19:08:46 +01:00
388446c255 infra: Workaround Microsoft.NET.ILLink.Tasks restore failure on Flathub
This package seems to be required for triming now but isn't restored by default.
This changes the flatpak pusher to publish so we are sure that the package is in the cache.

Signed-off-by: Mary Guillemard <mary@mary.zone>
2023-11-15 18:12:19 +01:00
29e192f241 Migrate to .NET 8 (#5887)
* Change TargetFramework to net8.0

* Disable info messages

* Fix warings

* Disable additional analyzer messages

* Fix typo

* Add whitespace

* Fix ref vs in warnings

* Use explicit [In] on array parameters

* No need to guard Remove with Contains

* Use 'ArgumentOutOfRangeException.ThrowIf...' instead of explicitly throwing a new exception instance

* Bump .NET SDK version

* Enable JsonSerializerIsReflectionEnabledByDefault

* Use 8.0.100 GA release

* Bump System package versions

---------

Co-authored-by: Zoltan Csizmadia <Zoltan.Csizmadia@vericast.com>
2023-11-15 17:41:31 +01:00
5b3662b793 Disable DMA GPU copy for block linear to linear copies (#5927)
* Disable DMA GPU copy for block linear to linear copies

* Simplify check

* PR feedback
2023-11-14 23:24:42 -03:00
1329c47ea4 Work around issue apparently caused by 5909 (#5926) 2023-11-14 22:24:54 -03:00
6bce46621c Change minimum OS to macOS 12 in Info.plist (#5925)
This should prevent the app from opening on macOS 11 and lower, informing the user that their OS is unsupported.
2023-11-14 21:20:33 +01:00
e6e5838916 Do not set modified flag again if texture was not modified (#5909)
* Do not set modified flag again if texture was not modified

* Formatting

* Fix copy dep regression
2023-11-13 18:07:05 -03:00
51065d9129 Revert "Add support for multi game XCIs (#5638)" (#5914)
This reverts commit 5c3cfb84c0.
2023-11-11 23:35:30 -03:00
6228331fd1 infra: switch back to ubuntu 20.04 LTS for macOS release 2023-11-11 22:38:54 +01:00
98e7c33630 infra: Update to LLVM 15 for macOS release 2023-11-11 22:35:58 +01:00
5c3cfb84c0 Add support for multi game XCIs (#5638)
* Add default values to ApplicationData directly

* Refactor application loading

It should now be possible to load multi game XCIs.
Included updates won't be detected for now.
Opening a game from the command line currently only opens the first one.

* Only include program NCAs where at least one tuple item is not null

* Get application data by title id and add programIndex check back

* Refactor application loading again and remove duplicate code

* Actually use patch ncas for updates

* Fix number of applications found with multi game xcis

* Don't load bundled updates from multi game xcis

* Change ApplicationData.TitleId type to ulong & Add TitleIdString property

* Use cnmt files and ContentCollection to load programs

* Ava: Add updates and DLCs from gamecarts

* Get the cnmt file from its NCA

* Ava: Identify bundled updates in updater window

* Fix the (hopefully) last few bugs

* Add idOffset parameter to GetNcaByType

* Handle missing file for dlc.json

* Ava: Shorten error message for invalid files

* Gtk: Add additional string for bundled updates in TitleUpdateWindow

* Hopefully fix DLC issues

* Apply formatting

* Finally fix DLC issues

* Adjust property names and fileSize field

* Read the correct update file

* Fix wrong casing for application id strings

* Rename TitleId to ApplicationId

* Address review comments

* Fix formatting issues

* Apply suggestions from code review

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Gracefully fail when loading pfs for update and dlc window

* Fix applications with multiple programs

* Fix DLCWindow crash on GTK

* Fix some GUI issues

* Remove IsXci again

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-11-11 21:56:57 +01:00
55557525b1 Create Desktop Shortcut fixes (#5852)
* remove duplicate basePath arg, add --fullscreen arg

* Changing FriendlyName to set "Ryujinx" text

* Fix GetArgsString using the base path

* Change desktop path to the Applications folder when creating shortcut on Mac

Co-authored-by: Nicko Anastassiu <134955950+nickoanastassiu@users.noreply.github.com>

* Move Create Shortcut button to top of context menu

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Nicko Anastassiu <134955950+nickoanastassiu@users.noreply.github.com>
2023-11-11 16:08:42 +01:00
7e6342e44d Add accelerator keys for Options and Help (#5884) 2023-11-11 15:57:15 +01:00
jcm
c3555cb5d6 UI: Change default hide cursor mode to OnIdle (#5906)
Co-authored-by: jcm <butt@butts.com>
2023-11-11 15:27:53 +01:00
815819767c Force all exclusive memory accesses to be ordered on AppleHv (#5898)
* Implement reprotect method on virtual memory manager (currently stubbed)

* Force all exclusive memory accesses to be ordered on AppleHv

* Format whitespace

* Fix test build

* Fix comment copy/paste

* Fix wrong bit for isLoad

* Update src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-11-07 13:24:10 -03:00
623604c391 Overhaul of string formatting/parsing/sorting logic for TimeSpans, DateTimes, and file sizes (#4956)
* Fixed formatting/parsing issues with ApplicationData properties TimePlayed, LastPlayed, and FileSize

Replaced double-based TimePlayed property with TimeSpan?-based one in ApplicationData and ApplicationMetadata
Added a migration for TimePlayed, just like in #4861
Consolidated ApplicationData's FileSize* properties into one FileSize property
Added a formatting/parsing helper class ValueFormatUtils for TimeSpans, DateTimes, and file sizes
Added new value converters for TimeSpans and file sizes for the Avalonia UI
Added TimePlayedSortComparer
Fixed sort order in LastPlayedSortComparer
Fixed sort order for ApplicationData fields TimePlayed, LastPlayed, and FileSize
Fixed crashes caused by SortHelper
Replaced SystemInfo.ToMiBString with ToGiBString backed by ValueFormatUtils
Replaced SaveModel.GetSizeString() with ValueFormatUtils

* Additional ApplicationLibrary changes that got lost in the last commit

* Removed unneeded usings

* Removed converters as they are no longer needed

* Updated comment on FormatDateTime

* Removed base10 parameter from ValueFormatUtils

FormatFileSize now always returns base 2 values with base 10 units
Made ParseFileSize capable of parsing both base 2 and base 10 units

* Removed nullable attribute from TimePlayed property

Centralized TimePlayed update code into ApplicationMetadata

* Changed UpdateTimePlayed() to use TimeSpan logic

* Removed JsonIgnore attributes from ApplicationData

* Implemented requested format changes

* Fixed mistakes in method documentation comments

* Made it so the Last Played value "Never" is localized in the Avalonia UI

* Implemented suggestions

* Remove unused import

* Did a comment refinement pass in ValueFormatUtils.cs

* Reordered ValueFormatUtils methods and sorted them into #regions

* Integrated functionality from #5056

Also removed Logger print from last_played migration code

* Implemented suggestions

* Moved ValueFormatUtils and SystemInfo to namespace Ryujinx.Ui.Common

* common: Respect proper value format convention and use base10 by default

This could be discuss again in another issue/PR, for now revert to the previous behavior.

Signed-off-by: Mary Guillemard <mary@mary.zone>

---------

Signed-off-by: Mary Guillemard <mary@mary.zone>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Mary Guillemard <mary@mary.zone>
2023-11-06 22:47:44 +01:00
617c5700ca Better handle instruction aborts when hitting unmapped memory (#5869)
* Adjust ARMeilleure to better handle instruction aborts when hitting unmapped memory

* Update src/ARMeilleure/Decoders/Decoder.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-11-05 12:32:17 +01:00
7b62f7475e Fix AddSessionObj NRE regression (#5875) 2023-11-01 21:47:40 +01:00
841dd56f4c Implement copy dependency for depth and color textures (#4365)
* Implement copy dependency for depth and color textures

* Revert changes added because R32 <-> D32 copies were illegal

* Restore depth alias matches
2023-10-31 19:00:39 -03:00
a16d582a10 [HLE] Remove ServerBase 1ms polling (#5855)
Added a KEvent for each ServerBase which signals whenever a session is added to the _sessions list, which allows it to rerun the ReplyAndReceive with the new session handle.

This greatly reduces the presence of ServerBase on profiles, especially of games that aren't particularly busy. It should also increase responsiveness when adding session objects, as it doesn't take at most 1ms for them to start working.

It also reduces the load on KTimeManager, which could allow it to spin less often. I have noticed that a bunch of games still do 1ms waits (they actually request a bit less than 1ms), so they still end up spinning to the next millisecond. Maybe for waits like this, it could attempt to nudge the timepoints to snap to each other when they're close enough, and also snap to whole millisecond waits when close enough.
2023-10-30 23:26:31 +01:00
9ef0be477b Skip some invalid texture flushes (#5755) 2023-10-30 23:18:28 +01:00
c14ce4d2a5 Add ldn_mitm as a network client for LDN (#5656)
* Add relevant files from private repo

Hopefully I didn't miss anything.

JsonHelper.cs is a debug only change
I only added line 810-812 in IUserLocalCommunicationService.cs
for the new Spacemeowx2Ldn case.

* Add a small README.md

just for fun

* Add note about NetCoreServer update to 5.1.0

* Fix a few issues

Fix usage of wrong broadcast address
Log warning if empty userstring was received
and don't add them to outNetworkInfo

* Add warning about incompatibility with public LDN version

* Add missing changes from old_master

* Adjust ldn_mitm for Ryujinx/Ryujinx#3805

* ldn: Adapt to changes from #4582

* ldn_mitm: First cleanup iteration

* ldn_mitm: Second cleanup iteration

* Credit spacemeowx2 in README.md

* Address first review comments by AcK

Adhere to Ryujinx coding style
Remove leftover log calls
Change category of a few log calls
Remove leftover debug notes

* Replace return type with void for methods always returning true

* Address first review comments by riperiperi

Purely stylistic changes:
- Adhere to naming style for internal fields
- Improve code formatting

* Throw InvalidOperationException when calling wrong ldn proxy methods

* Add missing newlines in LanDiscovery.Scan()

* Fix Linux not receiving broadcast packets

* Remove ILdnUdpSocket

It's very unlikely that we will ever need a udp client.
Thus we should simplify LanDiscovery initialization
and remove the parameter of InitUdp().

* ldn_mitm: Improve formatting

* fixup! Fix Linux not receiving broadcast packets

By opening the udp server on 'LocalBroadcastAddr'
Linux refused to answer packets going to LocalAddr.
So in order to fix this problem, Linux now opens two LdnProxyUdpServers.

* ldn_mitm: Fix assigning incorrect NodeIds

This just made connecting a lot more reliable! Thanks @riperiperi

* Fix node ids when leaving/joining

* Change NodeId behaviour to work like RyuLdn

* Change timing for accept and network info being reported.

* Wait for connection before sending anything.

* Remove ConnectAsync() from ILdnTcpSocket

* Only broadcast scan responses if we're hosting a network.

* Fix some filters, scan network duplication.

* Fix silly mistake

* Don't die on duplicates, just replace.

* Lock around node updates

These can happen from multiple threads.

* ldn_mitm: Fix namespaces for Types

Improve formatting
Add warning if compression failed

* Add quicker scan, forgetting networks that disappear.

* Always force a network sync when updating AdvertiseData

* Fix TCP frame size being too large for compressed frames

* Allow ldn_mitm to pass -1 id for room localcommunicationids.

* ldn_mitm: Match server socket options

* ldn_mitm: Use correct socket options

* ldn_mitm: Remove TCP broadcast socket options

* config: Rename Spacemeowx2Ldn to LdnMitm

* ldn_mitm: Generate random fake SSID

* ldn_mitm: Adjust logging statements/levels

* ldn_mitm: Add missing Stop() call for udp2

* ldn_mitm: Adjust formatting

* ldn_mitm: Add stub comments and adjust existing ones

* ldn: Add LdnConst class & set tx/rx buffer sizes correctly

* Move LdnConst out of UserServiceCreator

Replace a few values with LdnConsts

* ldn: Adjust namespaces and client names

* ldn_mitm: Adjust formatting

* ldn: Rename RyuLdn to LdnRyu

* Replace LanProtocol.Read() refs with scoped refs

* Add MIT license for ldn_mitm

* Clarify that network interface is also used for LDN

Although it's currently only used by ldn_mitm,
it would probably be more confusing to exclude RyuLdn there.

* Fix giving a station node id 0

* Update Nuget packages

* Remove LdnHelper

* Add update functions for EnableInternetAccess setting

* ldn: Log MultiplayerMode and DisableP2P

* ldn: Adjust namespaces

* Apply formatting

* Conform to Ryujinx code style

* Remove ldn_mitm from THIRDPARTY.md

It shouldn't have been there in the first place.

* Improve formatting

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-10-26 00:32:13 +02:00
jcm
171b46ef49 macOS: Use user-friendly macOS version string (#5838)
* use user-friendly macOS version string rather than kernel version

* add build identifier string

---------

Co-authored-by: jcm <butt@butts.com>
2023-10-25 00:37:13 +02:00
56fe2ff535 Fix loading tickets from a Sha256PartitionFileSystem (#5844) 2023-10-24 13:26:25 -03:00
b1f8f868f6 Fix the AOC manager using incorrect paths (#5840)
* Fix the content manager using incorrect path for some AOC NCAs

* Check Results in a few more places in the content manager
2023-10-23 14:34:31 -03:00
d773d5152e Update to LibHac 0.19.0 (#5831)
* Update to LibHac v0.19.0

- PartitionFileSystem classes now fully match Nintendo's implementation. Current code creating a PartitionFileSystem now need to use the Initialize method.
- Implementing nn::gcsrv and nn::sdmmcsrv now means the FS server now uses that abstraction instead of the old one where we passed in an IDeviceOperator.

* Add GetFileSystemAttribute
2023-10-22 20:30:46 -03:00
33ba170315 Fix NRE on gather operations with depth compare on macOS (#5832) 2023-10-22 20:31:36 +02:00
638be5f296 Revert "Ava UI: Input Menu Refactor (#4998)"
This reverts commit 49b37550ca.

This currently breaks the GTK GUI.
2023-10-21 15:19:21 +02:00
49b37550ca Ava UI: Input Menu Refactor (#4998)
* So much boilerplate

* Slow and steady

* Restructure + Ack suggestions

* Restructure + Ack suggestions

* Restructure

* Clean

* Propogate those fields i forgot about

* It builds

* Progress

* Almost there

* Fix stupid mistake

* Fix more stupid mistakes

* Actually fix fuck ups

* Start localising

* r/therestofthefuckingowl

* Localise ButtonKeyAssigner

* Are you feeling it now mr krabs

* We’re done at last

* Crimes against code

* Try me in the Hague

* Please be quiet

* Crimes are here to stay

* Dispose stuff

* Cleanup a couple things

* Visual fixes and improvements

One weird bug

* Fix rebase errors

* Fixes

* Ack Suggestions

Remaining ack suggestions

Update src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Update src/Ryujinx.Ava/UI/Models/Input/ControllerInputConfig.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Formatting and error

More Ava 11-ness

Whoops

* Code style fixes

* Style fixes

* Analyzer fix

* Remove all ReflectionBindings

* Remove ambigious object

* Remove redundant property

* Old man yells at formatter

* r e a d o n l y

* Fix profiles

* Use new Sliders

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-10-21 13:26:51 +02:00
a42f0bbb87 Add "Create Shortcut" To app context menu (#4734)
* Added basic implementation for shortcut creation

Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop.

* Icons display properly in shortcut

* code cleanup

* Moved shortcut logic to specific file, added Ava UI for shortcuts

* Added linux .desktop shortcut creation

* fixes to .shortcut data

* code issue fixes

* Added basic implementation for shortcut creation

Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop.

* Icons display properly in shortcut

* code cleanup

* Moved shortcut logic to specific file, added Ava UI for shortcuts

* Added linux .desktop shortcut creation

* fixes to .shortcut data

* code issue fixes

* added back shortcut to new contextmenu file

* Replaced COM reference with ComImport for shortcut functionality

* remove specific platform values and regions

* Move ShortcutHelper to Ryujinx.Ui.Common.Helpers

* Adjust styling and structure

* code feedback changes

* Added MacOS support using .app folder

* Added basic implementation for shortcut creation

Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop.

* Icons display properly in shortcut

* code cleanup

* Moved shortcut logic to specific file, added Ava UI for shortcuts

* Added linux .desktop shortcut creation

* fixes to .shortcut data

* code issue fixes

* Added basic implementation for shortcut creation

Currently bitmaps (.bmp) are used as the source file, colours are good (unlike .ico rn) but are scaled poorly on desktop.

* Icons display properly in shortcut

* code cleanup

* Moved shortcut logic to specific file, added Ava UI for shortcuts

* Added linux .desktop shortcut creation

* fixes to .shortcut data

* code issue fixes

* Replaced COM reference with ComImport for shortcut functionality

* remove specific platform values and regions

* Move ShortcutHelper to Ryujinx.Ui.Common.Helpers

* Adjust styling and structure

* code feedback changes

* adjust tooltip message

* added shortcut-template.desktop file

* set shortcut icon location to .local/share/icons

* Linux code feedback changes

* change InteropServices to new securifybv.ShellLink Package

* added ShellLink to readme, updated shortcut comment

* Code feedback changes

* Added MacOS Support (As per Jose Estrada's PR)

* dotnet format

* Small restructuring

* Embed template files into Ryujinx.Ui.Common

* Disable "CreateShortcut" option for flatpak builds

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Jose Estrada <joseestradacobo@gmail.com>
2023-10-20 20:51:15 +02:00
b4bb22ba06 Avalonia: Make slider scrollable with mouse wheel (#5760)
* Add scrollable custom control based on TickFrequency

* Use custom slider to update value when pointer wheel scrolled

* Remove extra xaml file

* Address formatting issues

* Only scroll one element at a time

* Add OnPointerWheelChanged event to VolumeStatus button

Co-authored-by: Ahmad Tantowi <ahmadtantowi@outlook.com>

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-10-20 16:02:12 +02:00
6fdf774845 Ava UI: Update to 11.0.5 (#5815)
* Bump bump bump

* Missed one
2023-10-20 15:41:50 +02:00
76b53e018a GPU: Add fallback when textureGatherOffsets is not supported (#5792)
* GPU: Add fallback when textureGatherOffsets is not supported.

This PR adds a fallback for GPUs or APIs that don't support an equivalent to the method `textureGatherOffsets`, where each of the 4 gathered texels has an individual offset. This is done by reusing the existing code to handle non-const offsets for texture instructions, though it has also been corrected as there were a few implementation issues.

MoltenVK reports support for this capability, and it didn't error when we initially released the MacOS build, but that has since changed. MVK still reports support, but spirv-cross has been fixed in a way that it _attempts_ to use this capability, but the metal compiler errors since it doesn't exist.

Some other fixes:
- textureGatherOffsets emulation has been changed significantly. It now uses 4 texture sample instructions (not gather), calculates a base texel (i=0 j=0) and adds the offsets onto it before converting into a tex coord. The final result is offset into a texel center, so it shouldn't be subject to interpolation, though this isn't perfect and could have some error with floating point formats with linear sampling. It is subject to texture wrap mode as it should be, which is why texelFetch was not used.
  - Maybe gather should be used here with component `w` (i=0, j=0), though this multiplies number of texels fetched by 4... The way it was doing this before _was_ wrong_, but doing it right would avoid issues with texel center precision.
- textureGatherOffset (singular) now performs textureGather with the offset applied to the coords, rather than the slower fallback where each texel is fetched individually.

* Increment shader cache version, remove unused arg

* Use base texture size for gather coord offset.

Implicit LOD for gather is not supported.

* Use 4 texture gathers for offsets emulation

Avoids issues with interpolation at cost of performance

(not sure how bad this is)

* Address Feedback
2023-10-20 15:05:09 +02:00
28dd7d80af Enable copy between MS and non-MS textures with different height (#5801) 2023-10-18 04:47:22 +00:00
1e06b28b22 Horizon: Migrate usb and psc services (#5800)
* Horizon: Migrate Usb and Psc services

* Fix formatting

* Adresses feedback
2023-10-13 23:13:15 -03:00
e768a54f17 Replace ReaderWriterLock with ReaderWriterLockSlim (#5785)
* Replace ReaderWriterLock with ReaderWriterLockSlim

* Resolve Feedback + Correct typo

* Revert some unncessary logic
2023-10-12 18:11:15 +02:00
4e2bb13080 Fix games freezing after initializing LDN 1021 times (#5787)
* Close handle for stateChangeEvent on Finalize

* Properly dispose NetworkClient before setting it to null
2023-10-09 13:47:47 +00:00
ac4f2c1e70 Avalonia: Show aspect ratio popup options in status bar (#5780)
* Show aspect ratio selection popup in status bar

* Add aspect ratio tooltip

* Fix typo
2023-10-08 11:04:41 +02:00
e40470bbe1 Fix return value of Get function when a result does not yet exist for the address. (#5768) 2023-10-07 17:42:10 +02:00
f460ecc182 GPU: Add HLE macros for popular NVN macros (#5761)
* GPU: Add HLE macros for popular NVN macros

* Remove non-vector equality check

The case where it's not hardware accelerated will do the check integer-wise anyways.

* Whitespace 😔

* Address Feedback
2023-10-06 19:55:07 -03:00
086564c3c8 HLE: Fix Mii crc generation and minor issues (#5766)
* HLE: Fix Mii crc generation

Validating CRCs for data and device involves calculating the crc of all data including the crc being checked, which should then be 0.

The crc should be _generated_ on all data _before_ the crc in the struct. It shouldn't include the crcs themselves.

This fixes all generated miis (eg. default) having invalid crcs. This does not affect mii maker, as that generates its own charinfo.

Does not fix MK8D crash.

* Fix other mii issues

* Fully define all fields for Nickname and Ver3StoreData

Fixes an issue where the nickname for a mii would only have the first character on some method calls.

* Add Array96 type
2023-10-06 19:23:39 -03:00
b6ac45d36d Fix SPIR-V call out arguments regression (#5767)
* Fix SPIR-V call out arguments regression

* Shader cache version bump
2023-10-06 00:18:30 -03:00
7afae8c699 nuget: bump Microsoft.CodeAnalysis.CSharp from 4.6.0 to 4.7.0 (#5608)
Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-05 13:40:03 +02:00
7835968214 Strings should not be concatenated using '+' in a loop (#5664)
* Strings should not be concatenated using '+' in a loop

* fix IDE0090

* undo GenerateLoadOrStore

* prefer string interpolation

* Update src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs

Co-authored-by: Mary <thog@protonmail.com>

---------

Co-authored-by: Mary <thog@protonmail.com>
2023-10-05 12:41:00 +02:00
0aceb534cb Fix SPIR-V function calls (#5764)
* Fix SPIR-V function calls

* Shader cache version bump
2023-10-04 21:35:26 -03:00
a0af6e4d07 Use unique temporary variables for function call parameters on SPIR-V (#5757)
* Use unique temporary variables for function call parameters on SPIR-V

* Shader cache version bump
2023-10-04 19:46:11 -03:00
jcm
f61b7818c3 Avalonia: Add macOS check for Color Space Passthrough (#5754)
* add macOS check for color passthrough

* use existing IsMacOS property

---------

Co-authored-by: jcm <butt@butts.com>
2023-10-04 19:15:37 +02:00
a2a97e1b11 Implement textureSamples texture query shader instruction (#5750)
* Implement textureSamples texture query shader instruction

* Shader cache version bump
2023-10-03 22:43:11 +00:00
8b2625b0be Decrement nvmap reference count on surface flinger prealloc (#5753) 2023-10-02 22:13:29 +00:00
651e24fed9 Signal friends completion event and stub CheckBlockedUserListAvailability (#5743) 2023-09-29 13:24:44 +02:00
41b104d0fb Fix audio renderer compressor effect (#5742)
* Delete DecibelToLinearExtended and fix Log10 function

* Fix CopyBuffer and ClearBuffer

* Change effect states from class to struct + formatting

* Formatting

* Make UpdateLowPassFilter readonly

* More compressor fixes
2023-09-29 10:48:49 +00:00
bc44b85b0b nuget: bump FluentAvaloniaUI from 2.0.1 to 2.0.4 (#5729)
* nuget: bump FluentAvaloniaUI from 2.0.1 to 2.0.4

Bumps [FluentAvaloniaUI](https://github.com/amwx/FluentAvalonia) from 2.0.1 to 2.0.4.
- [Commits](https://github.com/amwx/FluentAvalonia/commits)

---
updated-dependencies:
- dependency-name: FluentAvaloniaUI
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update Directory.Packages.props

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-09-28 00:15:45 +02:00
01c2b8097c Implement NGC service (#5681)
* Implement NGC service

* Use raw byte arrays instead of string for _wordSeparators

* Silence IDE0230 for _wordSeparators

* Try to silence warning about _rangeValuesCount not being read on SparseSet

* Make AcType enum private

* Add abstract methods and one TODO that I forgot

* PR feedback

* More PR feedback

* More PR feedback
2023-09-27 19:21:26 +02:00
4bd2ca3f0d nuget: bump System.IdentityModel.Tokens.Jwt from 6.31.0 to 7.0.0 (#5730)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.31.0 to 7.0.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.31.0...7.0.0)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 19:03:41 +02:00
e63157cc33 GPU: Don't create tracking handles for buffer textures (#5727)
* GPU: Don't create tracking handles for buffer textures

Buffer texture memory is handled by the buffer cache - the texture shouldn't create any tracking handles as they aren't used. This change simply makes them create and iterate 0 tracking handles, while keeping the rest of the texture group around.

This prevents a possible issue where many buffer textures are created as views of overlapping buffer ranges, and virtual regions have many dependant textures that don't actually contribute anything to handle state.

Should improve performance in Mortal Kombat 1, possibly certain UE4 games when FIFO raises to 100%.

* Fix interval tree bug

* Don't check view compatibility for buffer textures
2023-09-26 12:37:10 -03:00
7f2fb049f5 Ava: Fix regressions by rewriting CheckLaunchState (#5728) 2023-09-26 07:17:55 +02:00
4744bde0e5 Reduce the amount of descriptor pool allocations on Vulkan (#5673)
* Reduce the amount of descriptor pool allocations on Vulkan

* Formatting

* Slice can be simplified

* Make GetDescriptorPoolSizes static

* Adjust CanFit calculation so that TryAllocateDescriptorSets never fails

* Remove unused field
2023-09-26 02:00:02 +02:00
4a835bb2b9 Make Vulkan memory allocator actually thread safe (#5575)
* Make Vulkan memory allocator actually thread safe

* Make free thread safe too

* PR feedback
2023-09-26 01:50:06 +02:00
ddc9ae2a83 Add VTimer as alternative interrupt method on Apple Hypervisor (#5663)
* Add VTimer as alternative interrupt method on Apple Hypervisor

* Fix naming violations on TimeApi

* Fix timer interval (was 16us rather than 16ms)

* Fix delta ticks calculation

* Missing ThrowOnError call

* Add SupportedOSPlatform attribute on AppleHv classes
2023-09-26 01:18:32 +02:00
d6d3cdd573 Ava UI: Refactor async usage (#5516)
* Remove `async void`

* Async LoadApplications

* Formatting and such

* Remove async from InstallUpdate

* Update src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Cleanup LoadApplications()

* Cleanup

* Formatting

* Revert some stuff

* Cleanup

* Update src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Ack suggestions

* Whitespace

* Fix Peri suggestion

* Add missing trailing commas

* Remove redundant method override

* Remove Dispatcher.UIThread.InvokeAsync/Post where possible

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-09-26 00:04:58 +02:00
53bd4c9f60 Add ldn:u implementation, INetworkClient interface and DisabledLdnClient (#5652)
* Impl first attempt to LDN

* Make this work.

- Endianness swap on all IPs.
- Use local network IP for connections, rather than 127.0.0.1. This is to be changed when tunnelling or whatever.
- Mac addresses are now randomly assigned on the server. (fixes joining lobbies)
- Fixed the "connected" handler for stations to actually find a
- Added info retrieval when connected to a station.
- Users that disconnect are now removed from rooms they were in. (still need to broadcast tho)
- The communication service does a bit better with being closed now.
- Some locking around the game instance dictionary.

* We may just be "initialized". Ignore this for now.

* Lots of WIP

* Add Disconnect packet

* Improve signalling of internal events.

* Fix scan.

* Fix some more stupid things.

* Enable NoDelay on all sockets.

* Add station accept policy, disconnect function.

* Limit max number of games.

* Split out networking stuff from HLE, so it can be swapped.

* Update logging calls.

* Missed a spot.

* Call SignalDisconnect instead of SetState

* Add comment to GetNetworkInfo

* Update configuration + UI

Now has its own tab, more options.

* Refactoring IUserLocalCommunicationService

( Expected new issues :'( )

* some cleanup

* More fix

* Correctly handle errors when connecting.

* Disable *Private call and clean symbols

* Structs cleanup

* Big cleanup

* Fix InvalidHandle (in MK8D and other games)

* Add Reject and Private Network support (v1)

RyuLdn Version bumped to 1.

* Add Initialize Packet

Allows users to keep Mac Addresses assigned by the server.

* Add SetWirelessControllerRestriction and some cleanup

* LDN-2 Initial Rebase

Make this work.

- Endianness swap on all IPs.
- Use local network IP for connections, rather than 127.0.0.1. This is to be changed when tunnelling or whatever.
- Fixed the "connected" handler for stations to actually find a
- The communication service does a bit better with being closed now.
- Some locking around the game instance dictionary.

We may just be "initialized". Ignore this for now.

Lots of WIP

Implement scan filter.

Improve signalling of internal events.

Fix scan.

Fix 0 width data, scan reply end delay removed.

Fix some more stupid things.

Enable NoDelay on all sockets.

Add station accept policy, disconnect function.

Limit max number of games.

Split out networking stuff from HLE, so it can be swapped.

Update logging calls.

Missed a spot.

SetAdvertiseData when open, don't return games that have accept policy 1

Update configuration + UI

Now has its own tab, more options.

Don't Keepalive, it causes problems.

Refactoring IUserLocalCommunicationService

( Expected new issues :'( )

some cleanup

More fix

Correctly handle errors when connecting.

Disable *Private call and clean symbols

Structs cleanup

Big cleanup

Fix InvalidHandle (in MK8D and other games)

Add Reject and Private Network support (v1)

Disable TcpNoDelay option on linux.

Add SetWirelessControllerRestriction and some cleanup

Misc cleanup, implement broadcast flag.

* Misc Changes

* Fix GetNetworkInfo

* Fix some small issues

* Implement GetNetworkInfoLatestUpdate

* Hotfix when LocalCommunicationId = 0xFFFFFFFFFFFFFFFF

* Fix ARMS Scan (and other games using wrong LocalCommunicationId

* Fix latest update when host leaves

* Revert "Fix ARMS Scan (and other games using wrong LocalCommunicationId"

This reverts commit 519c283d3993e2fdfafb8ac6b4e0a98231f6fb75.

* Fix the localCommunicationId = -1

* Don't set Connect flag for nodes already in the room before joining.

* Make IUserLocalCommunicationService disposable

* Don't dispose if there's no client.

* LDN-2-2 Rebase

Make this work.

- Endianness swap on all IPs.
- Use local network IP for connections, rather than 127.0.0.1. This is to be changed when tunnelling or whatever.
- Fixed the "connected" handler for stations to actually find a
- The communication service does a bit better with being closed now.
- Some locking around the game instance dictionary.

We may just be "initialized". Ignore this for now.

Put sockets behind an interface, so that they can be swapped for something proxyable

Lots of WIP

Implement scan filter.

Improve signalling of internal events.

Fix scan.

Fix 0 width data, scan reply end delay removed.

Fix some more stupid things.

Enable NoDelay on all sockets.

Add station accept policy, disconnect function.

Limit max number of games.

Split out networking stuff from HLE, so it can be swapped.

Update logging calls.

Missed a spot.

SetAdvertiseData when open, don't return games that have accept policy 1

Update configuration + UI

Now has its own tab, more options.

Don't Keepalive, it causes problems.

Refactoring IUserLocalCommunicationService

( Expected new issues :'( )

some cleanup

More fix

Correctly handle errors when connecting.

Disable *Private call and clean symbols

Structs cleanup

Big cleanup

Fix InvalidHandle (in MK8D and other games)

Add Reject and Private Network support (v1)

Disable TcpNoDelay option on linux.

Add SetWirelessControllerRestriction and some cleanup

Misc cleanup, implement broadcast flag.

Misc Changes

Fix GetNetworkInfo

Fix some small issues

Disable LAN by default til the config is added.

Fix Splatoon 2

- Stub nfp IUser::StartDetection / IUser::StopDetection.
- Stub ntc IEnsureNetworkClockAvailabilityService and needed calls.

Cleanup previous fixes

Stub IAudioInManager/IAudioIn for Splatoon 2 LAN

Add LAN settings to multiplayer tab

LAN Play > LAN Mode

Implement GetNetworkInfoLatestUpdate

Hotfix when LocalCommunicationId = 0xFFFFFFFFFFFFFFFF

Fix ARMS Scan (and other games using wrong LocalCommunicationId

Fix latest update when host leaves

Revert "Fix ARMS Scan (and other games using wrong LocalCommunicationId"

This reverts commit 519c283d3993e2fdfafb8ac6b4e0a98231f6fb75.

Fix the localCommunicationId = -1

Don't set Connect flag for nodes already in the room before joining.

Make IUserLocalCommunicationService disposable

Fix crash when using LAN mode on linux.

Actually use that call

Don't dispose if there's no client.

Fix the settings window crash

Fix configurationFileUpdated

* Make LDN compatible with Ryujinx/Ryujinx#3805

* Ava: Add Ldn options to SettingsNetworkTab

* Ava: Add update events for multiplayer options

* Apply formatting

* Remove LdnHelper

* ldn: Fix hardcoded /24 subnet mask

* Fix naming rule violations

* Add missing summary doc tag

* Remove NetCoreServer dependency

* Address code style issues and typos

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Call CloseStation/CloseAccessPoint to reduce code duplication

* Fix typo

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix missing trailing commas

* Extract AddressList from AddressEntry

* Use AcceptPolicy as a type for LdnNetworkInfo.StationAcceptPolicy

* Add Flags attribute to ScanFilterFlag

* Rename struct members for LdnNetworkInfo

* Remove extra line

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Extract NetworkErrorMessage from NetworkError

* Fix missing trailing commas

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-09-25 23:50:43 +02:00
eca8808649 Headless: Add support for Scaling Filters, Anti-aliasing and Exclusive Fullscreen (#5412)
* Headless: Added support for fullscreen option

* Headless: cleanup of fullscreen support

* Headless: fullscreen support : implemented proposed changes

* Headless: fullscreen support: cleanup

* Headless: exclusive fullscreen support: wip

* Headless: exclusive fullscreen support: add. windows scale interop

* Headless: exclusive fullscreen support: cleanup

* Headless: exclusive fullscreen support: cleanup

* Headless: fullscreen support: fix for OpenGL scaling

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: add. Vulkan fix

* Headless: fullscreen support: add. macOS fullscreen fix

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: cleanup

* Headless: fullscreen support: cleanup

* Headless: exclusive fullscreen support: add. display selection logic

* Headless: exclusive fullscreen support: add. anti-aliasing + scaling-filter logic

* Headless: exclusive fullscreen support: upd. options to be case-insensitive

* Headless: exclusive fullscreen support: force default values for scaling + anti-aliasing options

* Headless: upd. OpenGL --fullscreen window size logic

* Headless: upd. fullscreen logic

* Headless: cleanup

* Headless: refac. DisplayId option naming

* Headless: refac. scaling + anti-aliasing option handling

* Headless: refac. namespace handling

* Headless: upd. imports ordering

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-09-25 23:40:16 +02:00
f6c3f1cdfd GPU: Discard data when getting texture before full clear (#5719)
* GPU: Discard data when getting texture before full clear

* Fix rules and order of clear checks

* Fix formatting
2023-09-25 23:07:03 +02:00
8026e1c804 nuget: bump Microsoft.NET.Test.Sdk from 17.6.3 to 17.7.2 (#5622)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.3 to 17.7.2.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.6.3...v17.7.2)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 21:29:09 +02:00
d9f9bbfaa6 Vulkan: Fix barriers on macOS (#5700)
* Use old method on macOS

* gdk suggestions

* Update src/Ryujinx.Graphics.Vulkan/TextureStorage.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update src/Ryujinx.Graphics.Vulkan/TextureStorage.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-09-23 19:32:36 -03:00
fe9e19d8cc [INFRA] Addition of basic contributor guides and docs framework. (#5581)
* Addition of basic contributor docs.

- Main contributor guide landing page.
- C# codestyle doc.
- Pull request guide doc.

All files and structure heavily inspired by the dotnet/runtime docs: https://github.com/dotnet/runtime/tree/main/docs

* fix typos and review changes

* Update XML doc requirement & conversation review.
2023-09-22 16:21:11 +02:00
fb55f57da7 Horizon: Migrate wlan and stubs latest services (#5708)
* Horizon: Migrate wlan and stubs latest services

This PR migrate empty wlan services, values are found by RE.
Latest firmwares added some other services which are now stubbed and up-to-date.

* Fix imports ordering
2023-09-20 22:55:27 +02:00
44862dce3e Stub unsupported BSD socket options (#5670)
* Stub unsupported BSD socket options

* Span.Clear
2023-09-19 19:35:56 +02:00
e601419bd4 make cheat list binding public (#5697) 2023-09-19 16:51:56 +00:00
d6bc0de785 use compiled bidning for localizations (#5684) 2023-09-18 22:20:59 +02:00
9f26fd3600 remove some usages of reflection binding (#5686) 2023-09-18 22:09:22 +02:00
88df636c87 Replace ShaderOutputLayer with equivalent ShaderViewportIndexLayerEXT capability (#5683) 2023-09-16 18:49:13 +02:00
7ccff037e8 Fix some Vulkan validation errors (mostly related to barriers) (#5603)
* Replace image barriers inside render pass with more generic memory barrier

* Remove forceStorage since it was creating images with storage bit for formats that are not StorageImage compatible

* Add missing flags on subpass dependency

* Don't call vkCmdSetScissor with a scissor count of 0

* One semaphore per swapchain image

* Remove compute stage from read to write barriers

* Try to improve Pipeline.Barrier nonsense

* Set PipelineStateFlags based on supported stages
2023-09-14 19:58:11 +02:00
a745913329 Fix gl_Layer to geometry shader change not writing gl_Layer (#5682)
* Fix gl_Layer to geometry shader change not writing gl_Layer

* Shader cache version bump
2023-09-14 14:53:53 -03:00
e6700b314f lbl: Migrate service to Horizon (#5628)
* lbl: Migrate service to Horizon

* Fix formatting

* Addresses gdkchan's feedback

* Fix comments
2023-09-14 09:50:19 +02:00
e2cfe6fe44 Fix shader GlobalToStorage pass when base address comes from local or shared memory (#5668)
* Fix shader GlobalToStorage pass when base address comes from local or shared memory

* Shader cache version bump
2023-09-11 01:22:18 +00:00
468 changed files with 13782 additions and 1969 deletions

View File

@ -233,6 +233,29 @@ dotnet_naming_style.IPascalCase.required_suffix =
dotnet_naming_style.IPascalCase.word_separator =
dotnet_naming_style.IPascalCase.capitalization = pascal_case
# TODO:
# .NET 8 migration (new warnings are caused by the NET 8 C# compiler and analyzer)
# The following info messages might need to be fixed in the source code instead of hiding the actual message
# Without the following lines, dotnet format would fail
# Disable "Collection initialization can be simplified"
dotnet_diagnostic.IDE0028.severity = none
dotnet_diagnostic.IDE0300.severity = none
dotnet_diagnostic.IDE0301.severity = none
dotnet_diagnostic.IDE0302.severity = none
dotnet_diagnostic.IDE0305.severity = none
# Disable "'new' expression can be simplified"
dotnet_diagnostic.IDE0090.severity = none
# Disable "Use primary constructor"
dotnet_diagnostic.IDE0290.severity = none
# Disable "Member '' does not access instance data and can be marked as static"
dotnet_diagnostic.CA1822.severity = none
# Disable "Change type of field '' from '' to '' for improved performance"
dotnet_diagnostic.CA1859.severity = none
# Disable "Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array"
dotnet_diagnostic.CA1861.severity = none
# Disable "Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'"
dotnet_diagnostic.CA1862.severity = none
[src/Ryujinx.HLE/HOS/Services/**.cs]
# Disable "mark members as static" rule for services
dotnet_diagnostic.CA1822.severity = none

View File

@ -30,7 +30,7 @@ jobs:
- os: windows-latest
OS_NAME: Windows x64
DOTNET_RUNTIME_IDENTIFIER: win10-x64
DOTNET_RUNTIME_IDENTIFIER: win-x64
RELEASE_ZIP_OS_NAME: win_x64
fail-fast: false
@ -155,4 +155,4 @@ jobs:
with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_headless/*.tar.gz"
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request'

View File

@ -49,7 +49,9 @@ jobs:
run: python -m pip install PyYAML lxml
- name: Restore Nuget packages
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
# So we just publish to grab the dependencies
run: dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
- name: Generate nuget_sources.json
shell: python

View File

@ -25,7 +25,7 @@ env:
jobs:
tag:
name: Create tag
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- name: Get version info
id: version_info
@ -59,7 +59,7 @@ jobs:
- os: windows-latest
OS_NAME: Windows x64
DOTNET_RUNTIME_IDENTIFIER: win10-x64
DOTNET_RUNTIME_IDENTIFIER: win-x64
RELEASE_ZIP_OS_NAME: win_x64
steps:
- uses: actions/checkout@v4
@ -156,11 +156,11 @@ jobs:
with:
global-json-file: global.json
- name: Setup LLVM 14
- name: Setup LLVM 15
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 14
sudo ./llvm.sh 15
- name: Install rcodesign
run: |
@ -215,4 +215,4 @@ jobs:
needs: release
with:
ryujinx_version: "1.1.${{ github.run_number }}"
secrets: inherit
secrets: inherit

147
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,147 @@
# Contribution to Ryujinx
You can contribute to Ryujinx with PRs, testing of PRs and issues. Contributing code and other implementations is greatly appreciated alongside simply filing issues for problems you encounter.
Please read the entire document before continuing as it can potentially save everyone involved a significant amount of time.
# Quick Links
* [Code Style Documentation](docs/coding-guidelines/coding-style.md)
* [Pull Request Guidelines](docs/workflow/pr-guide.md)
## Reporting Issues
We always welcome bug reports, feature proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible.
### Identify Where to Report
The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/Ryujinx). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos:
* [Ryujinx/Ryujinx](https://github.com/Ryujinx/Ryujinx) Ryujinx core project files.
* [Ryujinx/Ryujinx-Games-List](https://github.com/Ryujinx/Ryujinx-Games-List) Ryujinx game compatibility list.
* [Ryujinx/Ryujinx-Website](https://github.com/Ryujinx/Ryujinx-Website) Ryujinx website source code.
* [Ryujinx/Ryujinx-Ldn-Website](https://github.com/Ryujinx/Ryujinx-Ldn-Website) Ryujinx LDN website source code.
### Finding Existing Issues
Before filing a new issue, please search our [open issues](https://github.com/Ryujinx/Ryujinx/issues) to check if it already exists.
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
### Writing a Good Feature Request
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
### Writing a Good Bug Report
Good bug reports make it easier for maintainers to verify and root cause the underlying problem. The better a bug report, the faster the problem will be resolved.
Ideally, a bug report should contain the following information:
* A high-level description of the problem.
* A _minimal reproduction_, i.e. the smallest time commitment/configuration required to reproduce the wrong behavior. This can be in the form of a small homebrew application, or by providing a save file and reproduction steps for a specific game.
* A description of the _expected behavior_, contrasted with the _actual behavior_ observed.
* Information on the environment: OS/distro, CPU, GPU (including driver), RAM etc.
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
## Contributing Changes
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
### DOs and DON'Ts
Please do:
* **DO** follow our [coding style](docs/coding-guidelines/coding-style.md) (C# code-specific).
* **DO** give priority to the current style of the project or file you're changing even if it diverges from the general guidelines.
* **DO** keep the discussions focused. When a new or related topic comes up
it's often better to create new issue than to side track the discussion.
* **DO** clearly state on an issue that you are going to take on implementing it.
* **DO** blog and tweet (or whatever) about your contributions, frequently!
Please do not:
* **DON'T** make PRs for style changes.
* **DON'T** surprise us with big pull requests. Instead, file an issue and talk with us on Discord to start
a discussion so we can agree on a direction before you invest a large amount
of time.
* **DON'T** commit code that you didn't write. If you find code that you think is a good fit to add to Ryujinx, file an issue or talk to us on Discord to start a discussion before proceeding.
* **DON'T** submit PRs that alter licensing related files or headers. If you believe there's a problem with them, file an issue and we'll be happy to discuss it.
### Suggested Workflow
We use and recommend the following workflow:
1. Create or find an issue for your work.
- You can skip this step for trivial changes.
- Get agreement from the team and the community that your proposed change is a good one if it is of significant size or changes core functionality.
- Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
2. Create a personal fork of the repository on GitHub (if you don't already have one).
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
4. Make and commit your changes to your branch.
- [Build Instructions](https://github.com/Ryujinx/Ryujinx#building) explains how to build and test.
- Commit messages should be clear statements of action and intent.
6. Build the repository with your changes.
- Make sure that the builds are clean.
- Make sure that `dotnet format` has been run and any corrections tested and committed.
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
- State in the description what issue or improvement your change is addressing.
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/Ryujinx/Ryujinx/actions) to check for outstanding errors.
8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers)
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
9. When the team members have signed off, and all checks are green, your PR will be merged.
- The next official build will automatically include your change.
- You can delete the branch you used for making the change.
### Good First Issues
The team marks the most straightforward issues as [good first issues](https://github.com/Ryujinx/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
### Commit Messages
Please format commit messages as follows (based on [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)):
```
Summarize change in 50 characters or less
Provide more detail after the first line. Leave one blank line below the
summary and wrap all lines at 72 characters or less.
If the change fixes an issue, leave another blank line after the final
paragraph and indicate which issue is fixed in the specific format
below.
Fix #42
```
Also do your best to factor commits appropriately, not too large with unrelated things in the same commit, and not too small with the same small change applied N times in N different commits.
### PR - CI Process
The [Ryujinx continuous integration](https://github.com/Ryujinx/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
* There is an error within the PR that fails a test or errors the compiler.
* Random failure of the workflow can occasionally result in a CI failure. In this scenario a maintainer will manually restart the job.
### PR Feedback
Ryujinx team and community members will provide feedback on your change. Community feedback is highly valued. You may see the absence of team feedback if the community has already provided good review feedback.
Two Ryujinx team members must review and approve every PR prior to merge. They will often reply with "LGTM, see nit". That means that the PR will be merged once the feedback is resolved. "LGTM" == "looks good to me".
There are lots of thoughts and [approaches](https://github.com/antlr/antlr4-cpp/blob/master/CONTRIBUTING.md#emoji) for how to efficiently discuss changes. It is best to be clear and explicit with your feedback. Please be patient with people who might not understand the finer details about your approach to feedback.
#### Copying Changes from Other Projects
Ryujinx uses some implementations and frameworks from other projects. The following rules must be followed for PRs that include changes from another project:
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
- The license of the file is left in-tact.
- The contribution is correctly attributed in the [3rd party notices](https://github.com/Ryujinx/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.

View File

@ -3,38 +3,40 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="11.0.3" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.3" />
<PackageVersion Include="Avalonia.Desktop" Version="11.0.3" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.3" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.3" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0" />
<PackageVersion Include="Avalonia" Version="11.0.5" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.5" />
<PackageVersion Include="Avalonia.Desktop" Version="11.0.5" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.5" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.5" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.3" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.3" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="7.14.2" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.1" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.4" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
<PackageVersion Include="LibHac" Version="0.18.0" />
<PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NetCoreServer" Version="7.0.0" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
<PackageVersion Include="OpenTK.Core" Version="4.8.1" />
<PackageVersion Include="OpenTK.Graphics" Version="4.8.1" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.1" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.1" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.28.1-build28" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
@ -43,10 +45,10 @@
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.2" />
<PackageVersion Include="System.Drawing.Common" Version="8.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
<PackageVersion Include="System.Management" Version="8.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>
</Project>

View File

@ -68,7 +68,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
If you wish to build the emulator yourself, follow these steps:
### Step 1
Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
Install the X64 version of [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
### Step 2
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
@ -141,3 +141,5 @@ See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
- [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes.
- [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation.

View File

@ -681,4 +681,33 @@
END OF TERMS AND CONDITIONS
```
</details>
</details>
# ShellLink (MIT)
<details>
<summary>See License</summary>
```
MIT License
Copyright (c) 2017 Yorick Koster, Securify B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
</details>

View File

@ -3,8 +3,8 @@ Version=1.0
Name=Ryujinx
Type=Application
Icon=Ryujinx
Exec=env DOTNET_EnableAlternateStackCheck=1 Ryujinx %f
Comment=A Nintendo Switch Emulator
Exec=Ryujinx.sh %f
Comment=Plays Nintendo Switch applications
GenericName=Nintendo Switch Emulator
Terminal=false
Categories=Game;Emulator;

View File

@ -0,0 +1,13 @@
[Desktop Entry]
Version=1.0
Name={0}
Type=Application
Icon={1}
Exec={2} %f
Comment=Nintendo Switch application
GenericName=Nintendo Switch Emulator
Terminal=false
Categories=Game;Emulator;
Keywords=Switch;Nintendo;Emulator;
StartupWMClass=Ryujinx
PrefersNonDefaultGPU=true

View File

@ -43,7 +43,7 @@
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<string>12.0</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
@ -155,4 +155,4 @@
<string>200000</string>
</dict>
</dict>
</plist>
</plist>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>{0}</string>
<key>CFBundleGetInfoString</key>
<string>{1}</string>
<key>CFBundleIconFile</key>
<string>{2}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 - 2023 Ryujinx Team and Contributors.</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>UIPrerenderedIcon</key>
<true/>
<key>LSEnvironment</key>
<dict>
<key>DOTNET_DefaultStackSize</key>
<string>200000</string>
</dict>
</dict>
</plist>

40
docs/README.md Normal file
View File

@ -0,0 +1,40 @@
# Documents Index
This repo includes several documents that explain both high-level and low-level concepts about Ryujinx and its functions. These are very useful for contributors, to get context that can be very difficult to acquire from just reading code.
Intro to Ryujinx
==================
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
* The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions.
* The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
* Audio output is entirely supported via C# wrappers for SDL2, with OpenAL & libsoundio as fallbacks.
Getting Started
===============
- [Installing the .NET SDK](https://dotnet.microsoft.com/download)
- [Official .NET Docs](https://docs.microsoft.com/dotnet/core/)
Contributing (Building, testing, benchmarking, profiling, etc.)
===============
If you want to contribute a code change to this repo, start here.
- [Contributor Guide](../CONTRIBUTING.md)
Coding Guidelines
=================
- [C# coding style](coding-guidelines/coding-style.md)
- [Service Implementation Guidelines - WIP](https://gist.github.com/gdkchan/84ba88cd50efbe58d1babfaa7cd7c455)
Project Docs
=================
To be added. Many project files will contain basic XML docs for key functions and classes in the meantime.
Other Information
=================
- N/A

View File

@ -0,0 +1,116 @@
# C# Coding Style
The general rule we follow is "use Visual Studio defaults".
Using an IDE that supports the `.editorconfig` standard will make this much simpler.
1. We use [Allman style](http://en.wikipedia.org/wiki/Indent_style#Allman_style) braces, where each brace begins on a new line. A single line statement block can go without braces but the block must be properly indented on its own line and must not be nested in other statement blocks that use braces (See rule 18 for more details). One exception is that a `using` statement is permitted to be nested within another `using` statement by starting on the following line at the same indentation level, even if the nested `using` contains a controlled block.
2. We use four spaces of indentation (no tabs).
3. We use `_camelCase` for internal and private fields and use `readonly` where possible. Prefix internal and private instance fields with `_`, static fields with `s_` and thread static fields with `t_`. When used on static fields, `readonly` should come after `static` (e.g. `static readonly` not `readonly static`). Public fields should be used sparingly and should use PascalCasing with no prefix when used.
4. We avoid `this.` unless absolutely necessary.
5. We always specify the visibility, even if it's the default (e.g.
`private string _foo` not `string _foo`). Visibility should be the first modifier (e.g.
`public abstract` not `abstract public`).
6. Namespace imports should be specified at the top of the file, *outside* of `namespace` declarations.
7. Avoid more than one empty line at any time. For example, do not have two
blank lines between members of a type.
8. Avoid spurious free spaces.
For example avoid `if (someVar == 0)...`, where the dots mark the spurious free spaces.
Consider enabling "View White Space (Ctrl+R, Ctrl+W)" or "Edit -> Advanced -> View White Space" if using Visual Studio to aid detection.
9. If a file happens to differ in style from these guidelines (e.g. private members are named `m_member`
rather than `_member`), the existing style in that file takes precedence.
10. We only use `var` when the type is explicitly named on the right-hand side, typically due to either `new` or an explicit cast, e.g. `var stream = new FileStream(...)` not `var stream = OpenStandardInput()`.
- Similarly, target-typed `new()` can only be used when the type is explicitly named on the left-hand side, in a variable definition statement or a field definition statement. e.g. `FileStream stream = new(...);`, but not `stream = new(...);` (where the type was specified on a previous line).
11. We use language keywords instead of BCL types (e.g. `int, string, float` instead of `Int32, String, Single`, etc) for both type references as well as method calls (e.g. `int.Parse` instead of `Int32.Parse`). See issue [#13976](https://github.com/dotnet/runtime/issues/13976) for examples.
12. We use PascalCasing to name all our constant local variables and fields. The only exception is for interop code where the constant value should exactly match the name and value of the code you are calling via interop.
13. We use PascalCasing for all method names, including local functions.
14. We use ```nameof(...)``` instead of ```"..."``` whenever possible and relevant.
15. Fields should be specified at the top within type declarations.
16. When including non-ASCII characters in the source code use Unicode escape sequences (\uXXXX) instead of literal characters. Literal non-ASCII characters occasionally get garbled by a tool or editor.
17. When using labels (for goto), indent the label one less than the current indentation.
18. When using a single-statement if, we follow these conventions:
- Never use single-line form (for example: `if (source == null) throw new ArgumentNullException("source");`)
- Using braces is always accepted, and required if any block of an `if`/`else if`/.../`else` compound statement uses braces or if a single statement body spans multiple lines.
- Braces may be omitted only if the body of *every* block associated with an `if`/`else if`/.../`else` compound statement is placed on a single line.
19. Make all internal and private types static or sealed unless derivation from them is required. As with any implementation detail, they can be changed if/when derivation is required in the future.
20. XML docs should be used when writing interfaces or when a class/method is deemed sufficient in scope or complexity.
21. So-called [Magic Numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) should be defined as named constants before use (for example `for (int i = 56; i < 68; i++)` could read `for (int i = _currentAge; i < _retireAge; i++)`).
This may be ignored for trivial or syntactically common statements.
An [EditorConfig](https://editorconfig.org "EditorConfig homepage") file (`.editorconfig`) has been provided at the root of the runtime repository, enabling C# auto-formatting conforming to the above guidelines.
### Example File:
``ShaderCache.cs:``
```C#
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
/// Memory cache of shader code.
/// </summary>
class ShaderCache : IDisposable
{
/// <summary>
/// Default flags used on the shader translation process.
/// </summary>
public const TranslationFlags DefaultFlags = TranslationFlags.DebugMode;
private readonly struct TranslatedShader
{
public readonly CachedShaderStage Shader;
public readonly ShaderProgram Program;
public TranslatedShader(CachedShaderStage shader, ShaderProgram program)
{
Shader = shader;
Program = program;
}
}
...
/// <summary>
/// Processes the queue of shaders that must save their binaries to the disk cache.
/// </summary>
public void ProcessShaderCacheQueue()
{
// Check to see if the binaries for previously compiled shaders are ready, and save them out.
while (_programsToSaveQueue.TryPeek(out ProgramToSave programToSave))
{
ProgramLinkStatus result = programToSave.HostProgram.CheckProgramLink(false);
if (result != ProgramLinkStatus.Incomplete)
{
if (result == ProgramLinkStatus.Success)
{
_cacheWriter.AddShader(programToSave.CachedProgram, programToSave.BinaryCode ?? programToSave.HostProgram.GetBinary());
}
_programsToSaveQueue.Dequeue();
}
else
{
break;
}
}
}
}
}
```
For other languages, our current best guidance is consistency. When editing files, keep new code and changes consistent with the style in the files. For new files, it should conform to the style for that component. If there is a completely new component, anything that is reasonably broadly accepted is fine.

56
docs/workflow/pr-guide.md Normal file
View File

@ -0,0 +1,56 @@
# Pull Request Guide
## Contributing Rules
All contributions to Ryujinx/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
To merge pull requests, you must have write permissions in the repository.
## Quick Code Review Rules
* Do not mix unrelated changes in one pull request. For example, a code style change should never be mixed with a bug fix.
* All changes should follow the existing code style. You can read more about our code style at [docs/coding-guidelines](../coding-guidelines/coding-style.md).
* Adding external dependencies is to be avoided unless not doing so would introduce _significant_ complexity. Any dependency addition should be justified and discussed before merge.
* Use Draft pull requests for changes you are still working on but want early CI loop feedback. When you think your changes are ready for review, [change the status](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-stage-of-a-pull-request) of your pull request.
* Rebase your changes when required or directly requested. Changes should always be commited on top of the upstream branch, not the other way around.
* If you are asked to make changes during the review process do them as a new commit.
* Only resolve GitHub conversations with reviewers once they have been addressed with a commit, or via a mutual agreement.
## Pull Request Ownership
Every pull request will have automatically have labels and reviewers assigned. The label not only indicates the code segment which the change touches but also the area reviewers to be assigned.
If during the code review process a merge conflict occurs, the PR author is responsible for its resolution. Help will be provided if necessary although GitHub makes this easier by allowing simple conflict resolution using the [conflict-editor](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github).
## Pull Request Builds
When submitting a PR to the `Ryujinx/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
## Review Turnaround Times
Ryujinx is a project that is maintained by volunteers on a completely free-time basis. As such we cannot guarantee any particular timeframe for pull request review and approval. Weeks to months are common for larger (>500 line) PRs but there are some additional best practises to avoid review purgatory.
* Make the reviewers life easier wherever possible. Make use of descriptive commit names, code comments and XML docs where applicable.
* If there is disagreement on feedback then always lean on the side of the development team and community over any personal opinion.
* We're human. We miss things. We forget things. If there has been radio silence on your changes for a substantial period of time then do not hesitate to reach out directly either with something simple like "bump" on GitHub or a directly on Discord.
To re-iterate, make the review as easy for us as possible, respond promptly and be comfortable to interact directly with us for anything else.
## Merging Pull Requests
Anyone with write access can merge a pull request manually when the following conditions have been met:
* The PR has been approved by two reviewers and any other objections are addressed.
* You can request follow up reviews from the original reviewers if they requested changes.
* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of your PR.
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
## Blocking Pull Request Merging
If for whatever reason you would like to move your pull request back to an in-progress status to avoid merging it in the current form, you can turn the PR into a draft PR by selecting the option under the reviewers section. Alternatively, you can do that by adding [WIP] prefix to the pull request title.
## Old Pull Request Policy
From time to time we will review older PRs and check them for relevance. If we find the PR is inactive or no longer applies, we will close it. As the PR owner, you can simply reopen it if you feel your closed PR needs our attention.

View File

@ -1,6 +1,6 @@
{
"sdk": {
"version": "7.0.200",
"version": "8.0.100",
"rollForward": "latestFeature"
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

View File

@ -38,7 +38,9 @@ namespace ARMeilleure.Decoders
{
block = new Block(blkAddress);
if ((dMode != DecoderMode.MultipleBlocks && visited.Count >= 1) || opsCount > instructionLimit || !memory.IsMapped(blkAddress))
if ((dMode != DecoderMode.MultipleBlocks && visited.Count >= 1) ||
opsCount > instructionLimit ||
(visited.Count > 0 && !memory.IsMapped(blkAddress)))
{
block.Exit = true;
block.EndAddress = blkAddress;

View File

@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ARMeilleure.Diagnostics
{
@ -33,7 +34,6 @@ namespace ARMeilleure.Diagnostics
public static string Get(ulong address)
{
if (_symbols.TryGetValue(address, out string result))
{
return result;
@ -48,13 +48,15 @@ namespace ARMeilleure.Diagnostics
ulong diff = address - symbol.Start;
ulong rem = diff % symbol.ElementSize;
result = symbol.Name + "_" + diff / symbol.ElementSize;
StringBuilder resultBuilder = new();
resultBuilder.Append($"{symbol.Name}_{diff / symbol.ElementSize}");
if (rem != 0)
{
result += "+" + rem;
resultBuilder.Append($"+{rem}");
}
result = resultBuilder.ToString();
_symbols.TryAdd(address, result);
return result;

View File

@ -189,7 +189,7 @@ namespace ARMeilleure.Translation
{
if (start.CompareTo(node.End) < 0)
{
if (overlaps.Length >= overlapCount)
if (overlaps.Length <= overlapCount)
{
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
}

View File

@ -7,14 +7,14 @@ namespace ARMeilleure.Translation
internal class TranslatorCache<T>
{
private readonly IntervalTree<ulong, T> _tree;
private readonly ReaderWriterLock _treeLock;
private readonly ReaderWriterLockSlim _treeLock;
public int Count => _tree.Count;
public TranslatorCache()
{
_tree = new IntervalTree<ulong, T>();
_treeLock = new ReaderWriterLock();
_treeLock = new ReaderWriterLockSlim();
}
public bool TryAdd(ulong address, ulong size, T value)
@ -24,70 +24,70 @@ namespace ARMeilleure.Translation
public bool AddOrUpdate(ulong address, ulong size, T value, Func<ulong, T, T> updateFactoryCallback)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
bool result = _tree.AddOrUpdate(address, address + size, value, updateFactoryCallback);
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
return result;
}
public T GetOrAdd(ulong address, ulong size, T value)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
value = _tree.GetOrAdd(address, address + size, value);
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
return value;
}
public bool Remove(ulong address)
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
bool removed = _tree.Remove(address) != 0;
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
return removed;
}
public void Clear()
{
_treeLock.AcquireWriterLock(Timeout.Infinite);
_treeLock.EnterWriteLock();
_tree.Clear();
_treeLock.ReleaseWriterLock();
_treeLock.ExitWriteLock();
}
public bool ContainsKey(ulong address)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
bool result = _tree.ContainsKey(address);
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return result;
}
public bool TryGetValue(ulong address, out T value)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
bool result = _tree.TryGet(address, out value);
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return result;
}
public int GetOverlaps(ulong address, ulong size, ref ulong[] overlaps)
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
int count = _tree.Get(address, address + size, ref overlaps);
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return count;
}
public List<T> AsList()
{
_treeLock.AcquireReaderLock(Timeout.Infinite);
_treeLock.EnterReadLock();
List<T> list = _tree.AsList();
_treeLock.ReleaseReaderLock();
_treeLock.ExitReadLock();
return list;
}

View File

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.OpenAL" />
<PackageReference Include="OpenTK.Audio.OpenAL" />
</ItemGroup>
<ItemGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
@ -15,11 +15,11 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dylib</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win10-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.so</TargetPath>
</ContentWithTargetPath>

View File

@ -31,9 +31,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
public bool IsEffectEnabled { get; }
public AuxiliaryBufferCommand(uint bufferOffset, byte inputBufferOffset, byte outputBufferOffset,
ref AuxiliaryBufferAddresses sendBufferInfo, bool isEnabled, uint countMax,
CpuAddress outputBuffer, CpuAddress inputBuffer, uint updateCount, uint writeOffset, int nodeId)
public AuxiliaryBufferCommand(
uint bufferOffset,
byte inputBufferOffset,
byte outputBufferOffset,
ref AuxiliaryBufferAddresses sendBufferInfo,
bool isEnabled,
uint countMax,
CpuAddress outputBuffer,
CpuAddress inputBuffer,
uint updateCount,
uint writeOffset,
int nodeId)
{
Enabled = true;
NodeId = nodeId;

View File

@ -21,7 +21,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private BiquadFilterParameter _parameter;
public BiquadFilterCommand(int baseIndex, ref BiquadFilterParameter filter, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId)
public BiquadFilterCommand(
int baseIndex,
ref BiquadFilterParameter filter,
Memory<BiquadFilterState> biquadFilterStateMemory,
int inputBufferOffset,
int outputBufferOffset,
bool needInitialization,
int nodeId)
{
_parameter = filter;
BiquadFilterState = biquadFilterStateMemory;

View File

@ -77,7 +77,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ClearBuffer(int index)
{
Unsafe.InitBlock((void*)GetBufferPointer(index), 0, SampleCount);
Unsafe.InitBlock((void*)GetBufferPointer(index), 0, SampleCount * sizeof(float));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -89,7 +89,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyBuffer(int outputBufferIndex, int inputBufferIndex)
{
Unsafe.CopyBlock((void*)GetBufferPointer(outputBufferIndex), (void*)GetBufferPointer(inputBufferIndex), SampleCount);
Unsafe.CopyBlock((void*)GetBufferPointer(outputBufferIndex), (void*)GetBufferPointer(inputBufferIndex), SampleCount * sizeof(float));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -94,18 +94,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
float z = 0.0f;
float z = 1.0f;
bool unknown10OutOfRange = false;
bool unknown10OutOfRange = y >= state.Unknown10;
if (newMean < 1.0e-10f)
{
z = 1.0f;
y = -100.0f;
unknown10OutOfRange = state.Unknown10 < -100.0f;
unknown10OutOfRange = state.Unknown10 <= -100.0f;
}
if (y >= state.Unknown10 || unknown10OutOfRange)
if (unknown10OutOfRange)
{
float tmpGain;
@ -118,7 +118,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
}
z = FloatingPointHelper.DecibelToLinearExtended(tmpGain);
z = FloatingPointHelper.DecibelToLinear(tmpGain);
}
float unknown4New = z;

View File

@ -88,7 +88,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
Matrix2x2 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain,
delayFeedbackCrossGain, delayFeedbackBaseGain);
delayFeedbackCrossGain, delayFeedbackBaseGain);
for (int i = 0; i < sampleCount; i++)
{
@ -125,9 +125,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
Matrix4x4 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain,
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain);
delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain,
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain);
for (int i = 0; i < sampleCount; i++)
@ -172,11 +172,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
Matrix6x6 delayFeedback = new(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
for (int i = 0; i < sampleCount; i++)
{

View File

@ -28,7 +28,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
private LimiterParameter _parameter;
public LimiterCommandVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> resultState, bool isEnabled, ulong workBuffer, int nodeId)
public LimiterCommandVersion2(
uint bufferOffset,
LimiterParameter parameter,
Memory<LimiterState> state,
Memory<EffectResultState> resultState,
bool isEnabled,
ulong workBuffer,
int nodeId)
{
Enabled = true;
NodeId = nodeId;

View File

@ -79,53 +79,57 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessReverbMono(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
{
ProcessReverbGeneric(ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableMono,
_targetEarlyDelayLineIndicesTableMono,
_targetOutputFeedbackIndicesTableMono,
_outputIndicesTableMono);
ProcessReverbGeneric(
ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableMono,
_targetEarlyDelayLineIndicesTableMono,
_targetOutputFeedbackIndicesTableMono,
_outputIndicesTableMono);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessReverbStereo(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
{
ProcessReverbGeneric(ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableStereo,
_targetEarlyDelayLineIndicesTableStereo,
_targetOutputFeedbackIndicesTableStereo,
_outputIndicesTableStereo);
ProcessReverbGeneric(
ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableStereo,
_targetEarlyDelayLineIndicesTableStereo,
_targetOutputFeedbackIndicesTableStereo,
_outputIndicesTableStereo);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessReverbQuadraphonic(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
{
ProcessReverbGeneric(ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableQuadraphonic,
_targetEarlyDelayLineIndicesTableQuadraphonic,
_targetOutputFeedbackIndicesTableQuadraphonic,
_outputIndicesTableQuadraphonic);
ProcessReverbGeneric(
ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableQuadraphonic,
_targetEarlyDelayLineIndicesTableQuadraphonic,
_targetOutputFeedbackIndicesTableQuadraphonic,
_outputIndicesTableQuadraphonic);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessReverbSurround(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
{
ProcessReverbGeneric(ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableSurround,
_targetEarlyDelayLineIndicesTableSurround,
_targetOutputFeedbackIndicesTableSurround,
_outputIndicesTableSurround);
ProcessReverbGeneric(
ref state,
outputBuffers,
inputBuffers,
sampleCount,
_outputEarlyIndicesTableSurround,
_targetEarlyDelayLineIndicesTableSurround,
_targetOutputFeedbackIndicesTableSurround,
_outputIndicesTableSurround);
}
private unsafe void ProcessReverbGeneric(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount, ReadOnlySpan<int> outputEarlyIndicesTable, ReadOnlySpan<int> targetEarlyDelayLineIndicesTable, ReadOnlySpan<int> targetOutputFeedbackIndicesTable, ReadOnlySpan<int> outputIndicesTable)

View File

@ -52,7 +52,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
{
// NOTE: Nintendo uses an approximation of log10, we don't.
// As such, we support the same ranges as Nintendo to avoid unexpected behaviours.
return MathF.Pow(10, MathF.Max(x, 1.0e-10f));
return MathF.Log10(MathF.Max(x, 1.0e-10f));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -62,7 +62,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
foreach (float input in inputs)
{
res += (input * input);
float normInput = input * (1f / 32768f);
res += normInput * normInput;
}
res /= inputs.Length;
@ -81,19 +82,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
return MathF.Pow(10.0f, db / 20.0f);
}
/// <summary>
/// Map decibel to linear in [0, 2] range.
/// </summary>
/// <param name="db">The decibel value to convert</param>
/// <returns>Converted linear value in [0, 2] range</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DecibelToLinearExtended(float db)
{
float tmp = MathF.Log2(DecibelToLinear(db));
return MathF.Truncate(tmp) + MathF.Pow(2.0f, tmp - MathF.Truncate(tmp));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DegreesToRadians(float degrees)
{

View File

@ -3,7 +3,7 @@ using Ryujinx.Audio.Renderer.Parameter.Effect;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
public class CompressorState
public struct CompressorState
{
public ExponentialMovingAverage InputMovingAverage;
public float Unknown4;
@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
CompressorGainReduction = (1.0f - ratio) / Constants.ChannelCountMax;
Unknown10 = threshold - 1.5f;
Unknown14 = threshold + 1.5f;
OutputGain = FloatingPointHelper.DecibelToLinearExtended(parameter.OutputGain + makeupGain);
OutputGain = FloatingPointHelper.DecibelToLinear(parameter.OutputGain + makeupGain);
}
}
}

View File

@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
public class DelayState
public struct DelayState
{
public DelayLine[] DelayLines { get; }
public float[] LowPassZ { get; set; }
@ -53,7 +53,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
LowPassBaseGain = 1.0f - LowPassFeedbackGain;
}
public void UpdateLowPassFilter(ref float tempRawRef, uint channelCount)
public readonly void UpdateLowPassFilter(ref float tempRawRef, uint channelCount)
{
for (int i = 0; i < channelCount; i++)
{

View File

@ -4,7 +4,7 @@ using System;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
public class LimiterState
public struct LimiterState
{
public ExponentialMovingAverage[] DetectorAverage;
public ExponentialMovingAverage[] CompressionGainAverage;

View File

@ -4,7 +4,7 @@ using System;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
public class Reverb3dState
public struct Reverb3dState
{
private readonly float[] _fdnDelayMinTimes = new float[4] { 5.0f, 6.0f, 13.0f, 14.0f };
private readonly float[] _fdnDelayMaxTimes = new float[4] { 45.704f, 82.782f, 149.94f, 271.58f };

View File

@ -5,7 +5,7 @@ using System;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
public class ReverbState
public struct ReverbState
{
private static readonly float[] _fdnDelayTimes = new float[20]
{
@ -54,7 +54,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
// Room
0.70f, 0.68f, 0.70f, 0.68f, 0.70f, 0.68f, 0.70f, 0.68f, 0.68f, 0.68f,
// Chamber
0.70f, 0.68f, 0.70f, 0.68f, 0.70f, 0.68f, 0.68f, 0.68f, 0.68f, 0.68f,
0.70f, 0.68f, 0.70f, 0.68f, 0.70f, 0.68f, 0.68f, 0.68f, 0.68f, 0.68f,
// Hall
0.50f, 0.70f, 0.70f, 0.68f, 0.50f, 0.68f, 0.68f, 0.70f, 0.68f, 0.00f,
// Cathedral

View File

@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Utils
throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null);
}
MemoryMarshal.Write(backingMemory.Span[..size], ref data);
MemoryMarshal.Write(backingMemory.Span[..size], in data);
backingMemory = backingMemory[size..];
}
@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Utils
throw new ArgumentOutOfRangeException(nameof(backingMemory), backingMemory.Length, null);
}
MemoryMarshal.Write(backingMemory[..size], ref data);
MemoryMarshal.Write(backingMemory[..size], in data);
backingMemory = backingMemory[size..];
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

View File

@ -104,7 +104,7 @@ namespace Ryujinx.Ava
{
"Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default
_ => ThemeVariant.Default,
};
if (enableCustomTheme)

View File

@ -3,7 +3,6 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Rendering;
using Avalonia.Threading;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.Dummy;
@ -21,6 +20,7 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Graphics.GAL;
@ -190,7 +190,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState;
_gpuCancellationTokenSource = new CancellationTokenSource();
_gpuDoneEvent = new ManualResetEvent(false);
@ -407,11 +409,21 @@ namespace Ryujinx.Ava
});
}
private void UpdateEnableInternetAccessState(object sender, ReactiveEventArgs<bool> e)
{
Device.Configuration.EnableInternetAccess = e.NewValue;
}
private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs<string> e)
{
Device.Configuration.MultiplayerLanInterfaceId = e.NewValue;
}
private void UpdateMultiplayerModeState(object sender, ReactiveEventArgs<MultiplayerMode> e)
{
Device.Configuration.MultiplayerMode = e.NewValue;
}
public void Stop()
{
_isActive = false;
@ -704,7 +716,7 @@ namespace Ryujinx.Ava
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata =>
{
appMetadata.LastPlayed = DateTime.UtcNow;
appMetadata.UpdatePreGame();
});
return true;
@ -782,7 +794,8 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.AudioVolume,
ConfigurationState.Instance.System.UseHypervisor,
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
ConfigurationState.Instance.Multiplayer.Mode);
Device = new Switch(configuration);
}

View File

@ -14,7 +14,7 @@
"MenuBarFileOpenEmuFolder": "Open Ryujinx Folder",
"MenuBarFileOpenLogsFolder": "Open Logs Folder",
"MenuBarFileExit": "_Exit",
"MenuBarOptions": "Options",
"MenuBarOptions": "_Options",
"MenuBarOptionsToggleFullscreen": "Toggle Fullscreen",
"MenuBarOptionsStartGamesInFullscreen": "Start Games in Fullscreen Mode",
"MenuBarOptionsStopEmulation": "Stop Emulation",
@ -30,7 +30,7 @@
"MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarHelp": "Help",
"MenuBarHelp": "_Help",
"MenuBarHelpCheckForUpdates": "Check for Updates",
"MenuBarHelpAbout": "About",
"MenuSearch": "Search...",
@ -72,6 +72,8 @@
"GameListContextMenuExtractDataRomFSToolTip": "Extract the RomFS section from Application's current config (including updates)",
"GameListContextMenuExtractDataLogo": "Logo",
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
"GameListContextMenuCreateShortcut": "Create Application Shortcut",
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
"StatusBarSystemVersion": "System Version: {0}",
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
@ -648,9 +650,12 @@
"UserEditorTitle": "Edit User",
"UserEditorTitleCreate": "Create User",
"SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN features",
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features",
"NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders",
"AboutChangelogButton": "View Changelog on GitHub",
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser."
}
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
"SettingsTabNetworkMultiplayer": "Multiplayer",
"MultiplayerMode": "Mode:",
"MultiplayerModeTooltip": "Change multiplayer mode"
}

View File

@ -15,7 +15,6 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc;
@ -36,11 +35,9 @@ namespace Ryujinx.Ava.Common
private static HorizonClient _horizonClient;
private static AccountManager _accountManager;
private static VirtualFileSystem _virtualFileSystem;
private static StyleableWindow _owner;
public static void Initialize(VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, StyleableWindow owner)
public static void Initialize(VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient)
{
_owner = owner;
_virtualFileSystem = virtualFileSystem;
_horizonClient = horizonClient;
_accountManager = accountManager;
@ -148,7 +145,7 @@ namespace Ryujinx.Ava.Common
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
AllowMultiple = false
AllowMultiple = false,
});
if (result.Count == 0)
@ -176,7 +173,7 @@ namespace Ryujinx.Ava.Common
string extension = Path.GetExtension(titleFilePath).ToLower();
if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
{
PartitionFileSystem pfs;
IFileSystem pfs;
if (extension == ".xci")
{
@ -184,7 +181,9 @@ namespace Ryujinx.Ava.Common
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
var pfsTemp = new PartitionFileSystem();
pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
pfs = pfsTemp;
}
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))

View File

@ -1,6 +1,7 @@
using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
using System;
namespace Ryujinx.Ava.Common.Locale
@ -18,11 +19,20 @@ namespace Ryujinx.Ava.Common.Locale
{
LocaleKeys keyToUse = Key;
ReflectionBindingExtension binding = new($"[{keyToUse}]")
{
Mode = BindingMode.OneWay,
Source = LocaleManager.Instance,
};
var builder = new CompiledBindingPathBuilder();
builder.SetRawSource(LocaleManager.Instance)
.Property(new ClrPropertyInfo("Item",
obj => (LocaleManager.Instance[keyToUse]),
null,
typeof(string)), (weakRef, iPropInfo) =>
{
return PropertyInfoAccessorFactory.CreateInpcPropertyAccessor(weakRef, iPropInfo);
});
var path = builder.Build();
var binding = new CompiledBindingExtension(path);
return binding.ProvideValue(serviceProvider);
}

View File

@ -1,6 +1,5 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Input;
using System;

View File

@ -82,12 +82,9 @@ namespace Ryujinx.Modules
{
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
});
await ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
_running = false;
@ -114,10 +111,9 @@ namespace Ryujinx.Modules
{
if (showVersionUpToDate)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
});
await ContentDialogHelper.CreateUpdaterInfoDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
"");
}
_running = false;
@ -134,10 +130,9 @@ namespace Ryujinx.Modules
{
if (showVersionUpToDate)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
});
await ContentDialogHelper.CreateUpdaterInfoDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
"");
}
_running = false;
@ -149,10 +144,8 @@ namespace Ryujinx.Modules
{
Logger.Error?.Print(LogClass.Application, exception.Message);
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
});
await ContentDialogHelper.CreateErrorDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
_running = false;
@ -167,12 +160,9 @@ namespace Ryujinx.Modules
{
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
});
await ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
_running = false;
@ -183,10 +173,9 @@ namespace Ryujinx.Modules
{
if (showVersionUpToDate)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
});
await ContentDialogHelper.CreateUpdaterInfoDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
"");
}
_running = false;
@ -212,7 +201,7 @@ namespace Ryujinx.Modules
_buildSize = -1;
}
Dispatcher.UIThread.Post(async () =>
await Dispatcher.UIThread.InvokeAsync(async () =>
{
// Show a message asking the user if they want to update
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
@ -222,7 +211,7 @@ namespace Ryujinx.Modules
if (shouldUpdate)
{
UpdateRyujinx(mainWindow, _buildUrl);
await UpdateRyujinx(mainWindow, _buildUrl);
}
else
{
@ -241,7 +230,7 @@ namespace Ryujinx.Modules
return result;
}
private static async void UpdateRyujinx(Window parent, string downloadUrl)
private static async Task UpdateRyujinx(Window parent, string downloadUrl)
{
_updateSuccessful = false;
@ -579,27 +568,24 @@ namespace Ryujinx.Modules
}
}
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
private static void InstallUpdate(TaskDialog taskDialog, string updateFile)
{
// Extract Update
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
await Task.Run(() =>
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
}
else if (OperatingSystem.IsWindows())
{
ExtractZipFile(taskDialog, updateFile, _updateDir);
}
else
{
throw new NotSupportedException();
}
});
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
}
else if (OperatingSystem.IsWindows())
{
ExtractZipFile(taskDialog, updateFile, _updateDir);
}
else
{
throw new NotSupportedException();
}
// Delete downloaded zip
File.Delete(updateFile);
@ -613,36 +599,33 @@ namespace Ryujinx.Modules
if (!OperatingSystem.IsMacOS())
{
// Replace old files
await Task.Run(() =>
double count = 0;
foreach (string file in allFiles)
{
double count = 0;
foreach (string file in allFiles)
count++;
try
{
count++;
try
{
File.Move(file, file + ".ryuold");
File.Move(file, file + ".ryuold");
Dispatcher.UIThread.Post(() =>
{
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
});
}
catch
Dispatcher.UIThread.InvokeAsync(() =>
{
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
}
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
});
}
Dispatcher.UIThread.Post(() =>
catch
{
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
});
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
}
}
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
Dispatcher.UIThread.InvokeAsync(() =>
{
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
});
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
Directory.Delete(_updateDir, true);
}
@ -658,12 +641,11 @@ namespace Ryujinx.Modules
{
if (showWarnings)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
});
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
);
}
return false;
@ -673,12 +655,11 @@ namespace Ryujinx.Modules
{
if (showWarnings)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
});
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage])
);
}
return false;
@ -688,12 +669,11 @@ namespace Ryujinx.Modules
{
if (showWarnings)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
});
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
);
}
return false;
@ -705,21 +685,19 @@ namespace Ryujinx.Modules
{
if (ReleaseInformation.IsFlatHubBuild())
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
});
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage])
);
}
else
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateWarningDialog(
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
});
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
);
}
}

View File

@ -6,13 +6,13 @@ using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInfo;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
using Ryujinx.Ui.Common.SystemInfo;
using System;
using System.IO;
using System.Runtime.InteropServices;

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>
@ -25,6 +25,16 @@
<TrimMode>partial</TrimMode>
</PropertyGroup>
<!--
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
See:
https://github.com/amwx/FluentAvalonia/issues/481
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
-->
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" />
<PackageReference Include="Avalonia.Desktop" />
@ -40,7 +50,7 @@
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
<PackageReference Include="Silk.NET.Vulkan" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
@ -145,4 +155,4 @@
<ItemGroup>
<AdditionalFiles Include="Assets\Locales\en_US.json" />
</ItemGroup>
</Project>
</Project>

View File

@ -106,7 +106,7 @@ namespace Ryujinx.Ava.UI.Applet
bool error = false;
string inputText = args.InitialText ?? "";
Dispatcher.UIThread.Post(async () =>
Dispatcher.UIThread.InvokeAsync(async () =>
{
try
{
@ -149,7 +149,7 @@ namespace Ryujinx.Ava.UI.Applet
bool showDetails = false;
Dispatcher.UIThread.Post(async () =>
Dispatcher.UIThread.InvokeAsync(async () =>
{
try
{

View File

@ -6,9 +6,11 @@
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{locale:Locale ErrorWindowTitle}"
xmlns:views="using:Ryujinx.Ava.UI.Applet"
Width="450"
Height="340"
CanResize="False"
x:DataType="views:ErrorAppletWindow"
SizeToContent="Height"
mc:Ignorable="d"
Focusable="True">
@ -38,7 +40,7 @@
Grid.Column="1"
Margin="10"
VerticalAlignment="Stretch"
Text="{ReflectionBinding Message}"
Text="{Binding Message}"
TextWrapping="Wrap" />
<StackPanel
Name="ButtonStack"
@ -49,4 +51,4 @@
Orientation="Horizontal"
Spacing="10" />
</Grid>
</Window>
</Window>

View File

@ -4,7 +4,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="using:Ryujinx.Ava.UI.Controls"
Width="400"
x:DataType="views:SwkbdAppletDialog"
mc:Ignorable="d"
Focusable="True">
<Grid
@ -34,13 +36,13 @@
Grid.Row="1"
Grid.Column="1"
Margin="5"
Text="{ReflectionBinding MainText}"
Text="{Binding MainText}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="5"
Text="{ReflectionBinding SecondaryText}"
Text="{Binding SecondaryText}"
TextWrapping="Wrap" />
<TextBox
Name="Input"
@ -50,7 +52,7 @@
VerticalAlignment="Center"
Focusable="True"
KeyUp="Message_KeyUp"
Text="{ReflectionBinding Message}"
Text="{Binding Message}"
TextInput="Message_TextInput"
TextWrapping="Wrap"
UseFloatingWatermark="True" />

View File

@ -12,6 +12,11 @@
Click="ToggleFavorite_Click"
Header="{locale:Locale GameListContextMenuToggleFavorite}"
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
<MenuItem
Click="CreateApplicationShortcut_Click"
Header="{locale:Locale GameListContextMenuCreateShortcut}"
IsEnabled="{Binding CreateShortcutEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuCreateShortcutToolTip}" />
<Separator />
<MenuItem
Click="OpenUserSaveDirectory_Click"
@ -82,4 +87,4 @@
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>
</MenuFlyout>
</MenuFlyout>

View File

@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
{
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
{
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
OpenSaveDirectory(viewModel, SaveDataType.Account, new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
}
}
@ -62,14 +62,14 @@ namespace Ryujinx.Ava.UI.Controls
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
OpenSaveDirectory(viewModel, SaveDataType.Device, userId: default);
OpenSaveDirectory(viewModel, SaveDataType.Device, default);
}
public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default);
OpenSaveDirectory(viewModel, SaveDataType.Bcat, default);
}
private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId)
@ -158,11 +158,12 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{
@ -205,11 +206,12 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{
@ -335,13 +337,24 @@ namespace Ryujinx.Ava.UI.Controls
}
}
public void RunApplication_Click(object sender, RoutedEventArgs args)
public void CreateApplicationShortcut_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
viewModel.LoadApplication(viewModel.SelectedApplication.Path);
ApplicationData selectedApplication = viewModel.SelectedApplication;
ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.TitleName, selectedApplication.TitleId, selectedApplication.Icon);
}
}
public async void RunApplication_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await viewModel.LoadApplication(viewModel.SelectedApplication.Path);
}
}
}

View File

@ -46,7 +46,7 @@
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
<Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.GridItemSelectorSize}" />
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).GridItemSelectorSize}" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
@ -56,10 +56,10 @@
Margin="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}"
Classes.huge="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridHuge}"
Classes.large="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridMedium}"
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
ClipToBounds="True"
CornerRadius="4">
<Grid>
@ -78,7 +78,7 @@
Margin="0,10,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{ReflectionBinding $parent[UserControl].DataContext.ShowNames}">
IsVisible="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
@ -101,4 +101,4 @@
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
</UserControl>

View File

@ -42,7 +42,7 @@
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem:selected /template/ Rectangle#SelectionIndicator">
<Setter Property="MinHeight" Value="{ReflectionBinding $parent[UserControl].DataContext.ListItemSelectorSize}" />
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ListItemSelectorSize}" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
@ -67,10 +67,10 @@
Grid.RowSpan="3"
Grid.Column="0"
Margin="0"
Classes.huge="{ReflectionBinding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{ReflectionBinding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{ReflectionBinding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{ReflectionBinding $parent[UserControl].DataContext.IsGridSmall}"
Classes.huge="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridHuge}"
Classes.large="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridMedium}"
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<Border
Grid.Column="2"
@ -126,17 +126,17 @@
Spacing="5">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding TimePlayed}"
Text="{Binding TimePlayedString}"
TextAlignment="Right"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding LastPlayed, Converter={helpers:NullableDateTimeConverter}}"
Text="{Binding LastPlayedString, Converter={helpers:LocalizedNeverConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FileSize}"
Text="{Binding FileSizeString}"
TextAlignment="Right"
TextWrapping="Wrap" />
</StackPanel>
@ -157,4 +157,4 @@
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
</UserControl>

View File

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Input;
using System;
namespace Ryujinx.Ava.UI.Controls
{
public class SliderScroll : Slider
{
protected override Type StyleKeyOverride => typeof(Slider);
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
var newValue = Value + e.Delta.Y * TickFrequency;
if (newValue < Minimum)
{
Value = Minimum;
}
else if (newValue > Maximum)
{
Value = Maximum;
}
else
{
Value = newValue;
}
e.Handled = true;
}
}
}

View File

@ -0,0 +1,43 @@
using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Globalization;
namespace Ryujinx.Ava.UI.Helpers
{
/// <summary>
/// This <see cref="IValueConverter"/> makes sure that the string "Never" that's returned by <see cref="ValueFormatUtils.FormatDateTime"/> is properly localized in the Avalonia UI.
/// After the Avalonia UI has been made the default and the GTK UI is removed, <see cref="ValueFormatUtils"/> should be updated to directly return a localized string.
/// </summary>
internal class LocalizedNeverConverter : MarkupExtension, IValueConverter
{
private static readonly LocalizedNeverConverter _instance = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not string valStr)
{
return "";
}
if (valStr == "Never")
{
return LocaleManager.Instance[LocaleKeys.Never];
}
return valStr;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance;
}
}
}

View File

@ -1,38 +0,0 @@
using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale;
using System;
using System.Globalization;
namespace Ryujinx.Ava.UI.Helpers
{
internal class NullableDateTimeConverter : MarkupExtension, IValueConverter
{
private static readonly NullableDateTimeConverter _instance = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return LocaleManager.Instance[LocaleKeys.Never];
}
if (value is DateTime dateTime)
{
return dateTime.ToLocalTime().ToString(culture);
}
throw new NotSupportedException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance;
}
}
}

View File

@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Helpers
public static partial IntPtr SetCursor(IntPtr handle);
[LibraryImport("user32.dll")]
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvAndPlane, byte[] pvXorPlane);
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, [In] byte[] pvAndPlane, [In] byte[] pvXorPlane);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
public static partial ushort RegisterClassEx(ref WndClassEx param);

View File

@ -13,20 +13,19 @@ namespace Ryujinx.Ava.UI.Models.Generic
public int Compare(ApplicationData x, ApplicationData y)
{
var aValue = x.LastPlayed;
var bValue = y.LastPlayed;
DateTime aValue = DateTime.UnixEpoch, bValue = DateTime.UnixEpoch;
if (!aValue.HasValue)
if (x?.LastPlayed != null)
{
aValue = DateTime.UnixEpoch;
aValue = x.LastPlayed.Value;
}
if (!bValue.HasValue)
if (y?.LastPlayed != null)
{
bValue = DateTime.UnixEpoch;
bValue = y.LastPlayed.Value;
}
return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value);
return (IsAscending ? 1 : -1) * DateTime.Compare(aValue, bValue);
}
}
}

View File

@ -0,0 +1,31 @@
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
namespace Ryujinx.Ava.UI.Models.Generic
{
internal class TimePlayedSortComparer : IComparer<ApplicationData>
{
public TimePlayedSortComparer() { }
public TimePlayedSortComparer(bool isAscending) { IsAscending = isAscending; }
public bool IsAscending { get; }
public int Compare(ApplicationData x, ApplicationData y)
{
TimeSpan aValue = TimeSpan.Zero, bValue = TimeSpan.Zero;
if (x?.TimePlayed != null)
{
aValue = x.TimePlayed;
}
if (y?.TimePlayed != null)
{
bValue = y.TimePlayed;
}
return (IsAscending ? 1 : -1) * TimeSpan.Compare(aValue, bValue);
}
}
}

View File

@ -4,7 +4,7 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common;
using System;
using Ryujinx.Ui.Common.Helper;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -38,26 +38,7 @@ namespace Ryujinx.Ava.UI.Models
public bool SizeAvailable { get; set; }
public string SizeString => GetSizeString();
private string GetSizeString()
{
const int Scale = 1024;
string[] orders = { "GiB", "MiB", "KiB" };
long max = (long)Math.Pow(Scale, orders.Length);
foreach (string order in orders)
{
if (Size > max)
{
return $"{decimal.Divide(Size, max):##.##} {order}";
}
max /= Scale;
}
return "0 KiB";
}
public string SizeString => ValueFormatUtils.FormatFileSize(Size);
public SaveModel(SaveDataInfo info)
{

View File

@ -327,7 +327,7 @@ namespace Ryujinx.Ava.UI.ViewModels
string imageUrl = _amiiboList.Find(amiibo => amiibo.Equals(selected)).Image;
string usageString = "";
StringBuilder usageStringBuilder = new();
for (int i = 0; i < _amiiboList.Count; i++)
{
@ -341,20 +341,19 @@ namespace Ryujinx.Ava.UI.ViewModels
{
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
{
usageString += Environment.NewLine +
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
usageStringBuilder.Append($"{Environment.NewLine}- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}");
writable = usageItem.Write;
}
}
}
if (usageString.Length == 0)
if (usageStringBuilder.Length == 0)
{
usageString = LocaleManager.Instance[LocaleKeys.Unknown] + ".";
usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
}
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageString}";
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageStringBuilder}";
}
}

View File

@ -126,7 +126,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
PartitionFileSystem partitionFileSystem = new();
partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
_virtualFileSystem.ImportTickets(partitionFileSystem);
@ -212,9 +213,9 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Patterns = new[] { "*.nsp" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
MimeTypes = new[] { "application/x-nx-nsp" }
}
}
MimeTypes = new[] { "application/x-nx-nsp" },
},
},
});
foreach (var file in result)
@ -232,7 +233,8 @@ namespace Ryujinx.Ava.UI.ViewModels
using FileStream containerFile = File.OpenRead(path);
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
PartitionFileSystem partitionFileSystem = new();
partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
bool containsDownloadableContent = false;
_virtualFileSystem.ImportTickets(partitionFileSystem);

View File

@ -26,6 +26,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Ui;
using Ryujinx.Input.HLE;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
@ -39,7 +40,6 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager;
using Key = Ryujinx.Input.Key;
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
@ -356,6 +356,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild();
public string LoadHeading
{
get => _loadHeading;
@ -928,21 +930,20 @@ namespace Ryujinx.Ava.UI.ViewModels
return SortMode switch
{
#pragma warning disable IDE0055 // Disable formatting
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
: SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes)
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes),
ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum)
: SortExpressionComparer<ApplicationData>.Descending(app => app.TimePlayedNum),
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
: SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
ApplicationSort.TotalTimePlayed => new TimePlayedSortComparer(IsAscending),
ApplicationSort.FileType => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension)
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension),
ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSize)
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileSize),
ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite),
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
ApplicationSort.FileType => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension)
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension),
ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
_ => null,
#pragma warning restore IDE0055
};
@ -1068,9 +1069,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Logger.Error?.Print(LogClass.Application, ex.ToString());
static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
Dispatcher.UIThread.Post(Action);
await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
}
}
catch (Exception ex)
@ -1163,16 +1162,13 @@ namespace Ryujinx.Ava.UI.ViewModels
AppHost?.DisposeContext();
}
private void HandleRelaunch()
private async Task HandleRelaunch()
{
if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
{
UserChannelPersistence.ShouldRestart = false;
Dispatcher.UIThread.Post(() =>
{
LoadApplication(_currentEmulatedGamePath);
});
await LoadApplication(_currentEmulatedGamePath);
}
else
{
@ -1191,7 +1187,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Application.Current.Styles.TryGetResource(args.VSyncEnabled
? "VsyncEnabled"
: "VsyncDisabled",
Avalonia.Application.Current.ActualThemeVariant,
Application.Current.ActualThemeVariant,
out object color);
if (color is not null)
@ -1283,7 +1279,12 @@ namespace Ryujinx.Ava.UI.ViewModels
Glyph = Glyph.Grid;
}
public async void InstallFirmwareFromFile()
public void SetAspectRatio(AspectRatio aspectRatio)
{
ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio;
}
public async Task InstallFirmwareFromFile()
{
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
@ -1294,21 +1295,21 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Patterns = new[] { "*.xci", "*.zip" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
MimeTypes = new[] { "application/x-nx-xci", "application/zip" }
MimeTypes = new[] { "application/x-nx-xci", "application/zip" },
},
new("XCI")
{
Patterns = new[] { "*.xci" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
MimeTypes = new[] { "application/x-nx-xci" }
MimeTypes = new[] { "application/x-nx-xci" },
},
new("ZIP")
{
Patterns = new[] { "*.zip" },
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
MimeTypes = new[] { "application/zip" }
MimeTypes = new[] { "application/zip" },
},
}
},
});
if (result.Count > 0)
@ -1317,11 +1318,11 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public async void InstallFirmwareFromFolder()
public async Task InstallFirmwareFromFolder()
{
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
AllowMultiple = false
AllowMultiple = false,
});
if (result.Count > 0)
@ -1352,7 +1353,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public async void ExitCurrentState()
public async Task ExitCurrentState()
{
if (WindowState == WindowState.FullScreen)
{
@ -1377,7 +1378,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public async void ManageProfiles()
public async Task ManageProfiles()
{
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
}
@ -1387,7 +1388,7 @@ namespace Ryujinx.Ava.UI.ViewModels
AppHost.Device.System.SimulateWakeUpMessage();
}
public async void OpenFile()
public async Task OpenFile()
{
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
@ -1404,7 +1405,7 @@ namespace Ryujinx.Ava.UI.ViewModels
"com.ryujinx.xci",
"com.ryujinx.nca",
"com.ryujinx.nro",
"com.ryujinx.nso"
"com.ryujinx.nso",
},
MimeTypes = new[]
{
@ -1412,63 +1413,63 @@ namespace Ryujinx.Ava.UI.ViewModels
"application/x-nx-xci",
"application/x-nx-nca",
"application/x-nx-nro",
"application/x-nx-nso"
}
"application/x-nx-nso",
},
},
new("NSP")
{
Patterns = new[] { "*.nsp" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
MimeTypes = new[] { "application/x-nx-nsp" }
MimeTypes = new[] { "application/x-nx-nsp" },
},
new("XCI")
{
Patterns = new[] { "*.xci" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
MimeTypes = new[] { "application/x-nx-xci" }
MimeTypes = new[] { "application/x-nx-xci" },
},
new("NCA")
{
Patterns = new[] { "*.nca" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
MimeTypes = new[] { "application/x-nx-nca" }
MimeTypes = new[] { "application/x-nx-nca" },
},
new("NRO")
{
Patterns = new[] { "*.nro" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
MimeTypes = new[] { "application/x-nx-nro" }
MimeTypes = new[] { "application/x-nx-nro" },
},
new("NSO")
{
Patterns = new[] { "*.nso" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
MimeTypes = new[] { "application/x-nx-nso" }
MimeTypes = new[] { "application/x-nx-nso" },
},
}
},
});
if (result.Count > 0)
{
LoadApplication(result[0].Path.LocalPath);
await LoadApplication(result[0].Path.LocalPath);
}
}
public async void OpenFolder()
public async Task OpenFolder()
{
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
AllowMultiple = false
AllowMultiple = false,
});
if (result.Count > 0)
{
LoadApplication(result[0].Path.LocalPath);
await LoadApplication(result[0].Path.LocalPath);
}
}
public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "")
public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
{
if (AppHost != null)
{
@ -1488,7 +1489,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.RestartTime();
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path);
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
PrepareLoadScreen();
@ -1505,35 +1506,30 @@ namespace Ryujinx.Ava.UI.ViewModels
this,
TopLevel);
async void Action()
if (!await AppHost.LoadGuestApplication())
{
if (!await AppHost.LoadGuestApplication())
{
AppHost.DisposeContext();
AppHost = null;
AppHost.DisposeContext();
AppHost = null;
return;
}
CanUpdate = false;
LoadHeading = TitleName = titleName;
if (string.IsNullOrWhiteSpace(titleName))
{
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
}
SwitchToRenderer(startFullscreen);
_currentEmulatedGamePath = path;
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
gameThread.Start();
return;
}
Dispatcher.UIThread.Post(Action);
CanUpdate = false;
LoadHeading = TitleName = titleName;
if (string.IsNullOrWhiteSpace(titleName))
{
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
}
SwitchToRenderer(startFullscreen);
_currentEmulatedGamePath = path;
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
gameThread.Start();
}
public void SwitchToRenderer(bool startFullscreen)
@ -1552,13 +1548,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{
if (appMetadata.LastPlayed.HasValue)
{
double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
}
appMetadata.LastPlayed = DateTime.UtcNow;
appMetadata.UpdatePostGame();
});
}
@ -1596,7 +1586,7 @@ namespace Ryujinx.Ava.UI.ViewModels
IsGameRunning = false;
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.InvokeAsync(async () =>
{
ShowMenuAndStatusBar = true;
ShowContent = true;
@ -1609,7 +1599,7 @@ namespace Ryujinx.Ava.UI.ViewModels
AppHost = null;
HandleRelaunch();
await HandleRelaunch();
});
RendererHostControl.WindowCreated -= RendererHost_Created;
@ -1701,7 +1691,6 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
}
#endregion
}
}

View File

@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Vulkan;
@ -54,6 +55,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public event Action CloseWindow;
public event Action SaveSettingsEvent;
private int _networkInterfaceIndex;
private int _multiplayerModeIndex;
public int ResolutionScale
{
@ -76,14 +78,13 @@ namespace Ryujinx.Ava.UI.ViewModels
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
"",
"",
LocaleManager.Instance[LocaleKeys.InputDialogOk],
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
});
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle])
);
}
OnPropertyChanged();
@ -146,6 +147,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableTextureRecompression { get; set; }
public bool EnableMacroHLE { get; set; }
public bool EnableColorSpacePassthrough { get; set; }
public bool ColorSpacePassthroughAvailable => IsMacOS;
public bool EnableFileLog { get; set; }
public bool EnableStub { get; set; }
public bool EnableInfo { get; set; }
@ -251,6 +253,11 @@ namespace Ryujinx.Ava.UI.ViewModels
get => new(_networkInterfaces.Keys);
}
public AvaloniaList<string> MultiplayerModes
{
get => new(Enum.GetNames<MultiplayerMode>());
}
public KeyboardHotkeys KeyboardHotkeys
{
get => _keyboardHotkeys;
@ -272,6 +279,16 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public int MultiplayerModeIndex
{
get => _multiplayerModeIndex;
set
{
_multiplayerModeIndex = value;
ConfigurationState.Instance.Multiplayer.Mode.Value = (MultiplayerMode)_multiplayerModeIndex;
}
}
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
{
_virtualFileSystem = virtualFileSystem;
@ -478,6 +495,8 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value;
}
public void SaveSettings()
@ -579,6 +598,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];
config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);

View File

@ -22,6 +22,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Path = System.IO.Path;
using SpanHelpers = LibHac.Common.SpanHelpers;
@ -169,7 +170,9 @@ namespace Ryujinx.Ava.UI.ViewModels
try
{
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, new PartitionFileSystem(file.AsStorage()), TitleId.ToString("x16"), 0);
var pfs = new PartitionFileSystem();
pfs.Initialize(file.AsStorage()).ThrowIfFailure();
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, pfs, TitleId.ToString("x16"), 0);
if (controlNca != null && patchNca != null)
{
@ -184,18 +187,12 @@ namespace Ryujinx.Ava.UI.ViewModels
}
else
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
});
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
}
}
catch (Exception ex)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
});
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)));
}
}
}
@ -207,7 +204,7 @@ namespace Ryujinx.Ava.UI.ViewModels
SortUpdates();
}
public async void Add()
public async Task Add()
{
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
@ -218,9 +215,9 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Patterns = new[] { "*.nsp" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
MimeTypes = new[] { "application/x-nx-nsp" }
}
}
MimeTypes = new[] { "application/x-nx-nsp" },
},
},
});
foreach (var file in result)

View File

@ -5,6 +5,7 @@
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
@ -460,7 +461,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Slider
<controls:SliderScroll
Width="130"
Maximum="1"
TickFrequency="0.01"
@ -480,7 +481,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Slider
<controls:SliderScroll
Width="130"
Maximum="2"
TickFrequency="0.01"
@ -604,7 +605,7 @@
<StackPanel
HorizontalAlignment="Center"
Orientation="Horizontal">
<Slider
<controls:SliderScroll
Width="130"
Maximum="1"
TickFrequency="0.01"
@ -1083,7 +1084,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Slider
<controls:SliderScroll
Width="130"
Maximum="1"
TickFrequency="0.01"
@ -1105,7 +1106,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Slider
<controls:SliderScroll
Width="130"
Maximum="2"
TickFrequency="0.01"
@ -1125,4 +1126,4 @@
</StackPanel>
</Grid>
</StackPanel>
</UserControl>
</UserControl>

View File

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
@ -23,11 +24,11 @@
Margin="0"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionGyroSensitivity}" />
<Slider
<controls:SliderScroll
Margin="0,-5,0,-5"
Width="150"
MaxWidth="150"
TickFrequency="0.01"
TickFrequency="1"
IsSnapToTickEnabled="True"
SmallChange="0.01"
Maximum="100"
@ -45,11 +46,11 @@
Margin="0"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsMotionGyroDeadzone}" />
<Slider
<controls:SliderScroll
Margin="0,-5,0,-5"
Width="150"
MaxWidth="150"
TickFrequency="0.01"
TickFrequency="1"
IsSnapToTickEnabled="True"
SmallChange="0.01"
Maximum="100"
@ -167,4 +168,4 @@
</Grid>
</Border>
</Grid>
</UserControl>
</UserControl>

View File

@ -1,6 +1,7 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
@ -21,7 +22,7 @@
TextWrapping="WrapWithOverflow"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsRumbleStrongMultiplier}" />
<Slider
<controls:SliderScroll
Margin="0,-5,0,-5"
Width="200"
TickFrequency="0.01"
@ -41,7 +42,7 @@
TextWrapping="WrapWithOverflow"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsRumbleWeakMultiplier}" />
<Slider
<controls:SliderScroll
Margin="0,-5,0,-5"
Width="200"
MaxWidth="200"
@ -58,4 +59,4 @@
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
</UserControl>

View File

@ -17,7 +17,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Main
{
@ -107,20 +106,14 @@ namespace Ryujinx.Ava.UI.Views.Main
await Window.ViewModel.AppHost?.ShowExitPrompt();
}
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
private void PauseEmulation_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
Window.ViewModel.AppHost?.Pause();
});
Window.ViewModel.AppHost?.Pause();
}
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
private void ResumeEmulation_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
Window.ViewModel.AppHost?.Resume();
});
Window.ViewModel.AppHost?.Resume();
}
public async void OpenSettings(object sender, RoutedEventArgs e)
@ -132,13 +125,13 @@ namespace Ryujinx.Ava.UI.Views.Main
ViewModel.LoadConfigurableHotKeys();
}
public void OpenMiiApplet(object sender, RoutedEventArgs e)
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
{
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
if (!string.IsNullOrEmpty(contentPath))
{
ViewModel.LoadApplication(contentPath, false, "Mii Applet");
await ViewModel.LoadApplication(contentPath, false, "Mii Applet");
}
}
@ -196,8 +189,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{
if (FileAssociationHelper.Install())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
@ -209,8 +201,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{
if (FileAssociationHelper.Uninstall())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{

View File

@ -3,9 +3,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:config="clr-namespace:Ryujinx.Common.Configuration;assembly=Ryujinx.Common"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
x:DataType="viewModels:MainWindowViewModel">
@ -112,15 +114,52 @@
Background="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
<SplitButton
Name="AspectRatioStatus"
Margin="5,0,5,0"
Padding="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Background="Transparent"
BorderThickness="0"
CornerRadius="0"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="AspectRatioStatus_PointerReleased"
Text="{Binding AspectRatioStatusText}"
TextAlignment="Left" />
Content="{Binding AspectRatioStatusText}"
Click="AspectRatioStatus_OnClick"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
<SplitButton.Styles>
<Style Selector="Border#SeparatorBorder">
<Setter Property="Opacity" Value="0" />
</Style>
</SplitButton.Styles>
<SplitButton.Flyout>
<MenuFlyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
<MenuItem
Header="{locale:Locale SettingsTabGraphicsAspectRatio4x3}"
Command="{Binding SetAspectRatio}"
CommandParameter="{x:Static config:AspectRatio.Fixed4x3}"/>
<MenuItem
Header="{locale:Locale SettingsTabGraphicsAspectRatio16x9}"
Command="{Binding SetAspectRatio}"
CommandParameter="{x:Static config:AspectRatio.Fixed16x9}"/>
<MenuItem
Header="{locale:Locale SettingsTabGraphicsAspectRatio16x10}"
Command="{Binding SetAspectRatio}"
CommandParameter="{x:Static config:AspectRatio.Fixed16x10}"/>
<MenuItem
Header="{locale:Locale SettingsTabGraphicsAspectRatio21x9}"
Command="{Binding SetAspectRatio}"
CommandParameter="{x:Static config:AspectRatio.Fixed21x9}"/>
<MenuItem
Header="{locale:Locale SettingsTabGraphicsAspectRatio32x9}"
Command="{Binding SetAspectRatio}"
CommandParameter="{x:Static config:AspectRatio.Fixed32x9}"/>
<MenuItem
Header="{locale:Locale SettingsTabGraphicsAspectRatioStretch}"
Command="{Binding SetAspectRatio}"
CommandParameter="{x:Static config:AspectRatio.Stretched}"/>
</MenuFlyout>
</SplitButton.Flyout>
</SplitButton>
<Border
Width="2"
Height="12"
@ -138,6 +177,7 @@
Content="{Binding VolumeStatusText}"
IsChecked="{Binding VolumeMuted}"
IsVisible="{Binding !ShowLoadProgress}"
PointerWheelChanged="VolumeStatus_OnPointerWheelChanged"
Background="Transparent"
BorderThickness="0"
CornerRadius="0">
@ -154,7 +194,7 @@
<ToggleSplitButton.Flyout>
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
<Grid Margin="0">
<Slider
<controls:SliderScroll
MaxHeight="40"
Width="150"
Margin="0"

View File

@ -43,10 +43,9 @@ namespace Ryujinx.Ava.UI.Views.Main
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
}
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e)
{
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
}
@ -54,5 +53,20 @@ namespace Ryujinx.Ava.UI.Views.Main
{
Window.LoadApplications();
}
private void VolumeStatus_OnPointerWheelChanged(object sender, PointerWheelEventArgs e)
{
// Change the volume by 5% at a time
float newValue = Window.ViewModel.Volume + (float)e.Delta.Y * 0.05f;
Window.ViewModel.Volume = newValue switch
{
< 0 => 0,
> 1 => 1,
_ => newValue,
};
e.Handled = true;
}
}
}

View File

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
@ -50,7 +51,7 @@
VerticalAlignment="Center"
Text="{locale:Locale IconSize}"
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
<Slider
<controls:SliderScroll
Width="150"
Height="35"
Margin="5,-10,5,0"
@ -173,4 +174,4 @@
DockPanel.Dock="Right"
Text="{locale:Locale CommonSort}" />
</DockPanel>
</UserControl>
</UserControl>

View File

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
@ -63,13 +64,13 @@
Maximum="100" />
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<Slider Value="{Binding Volume}"
<controls:SliderScroll Value="{Binding Volume}"
Margin="250,0,0,0"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Minimum="0"
Maximum="100"
SmallChange="5"
TickFrequency="5"
SmallChange="1"
TickFrequency="1"
IsSnapToTickEnabled="True"
LargeChange="10"
Width="350" />
@ -77,4 +78,4 @@
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>
</UserControl>

View File

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
@ -73,6 +74,7 @@
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
IsVisible="{Binding ColorSpacePassthroughAvailable}"
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
</CheckBox>
@ -172,7 +174,7 @@
<TextBlock Text="FSR" />
</ComboBoxItem>
</ComboBox>
<Slider Value="{Binding ScalingFilterLevel}"
<controls:SliderScroll Value="{Binding ScalingFilterLevel}"
ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
MinWidth="150"
Margin="10,-3,0,0"
@ -296,4 +298,4 @@
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>
</UserControl>

View File

@ -23,21 +23,34 @@
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkMultiplayer}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale MultiplayerMode}"
ToolTip.Tip="{locale:Locale MultiplayerModeTooltip}"
Width="200" />
<ComboBox SelectedIndex="{Binding MultiplayerModeIndex}"
ToolTip.Tip="{locale:Locale MultiplayerModeTooltip}"
HorizontalContentAlignment="Left"
ItemsSource="{Binding MultiplayerModes}"
Width="250" />
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
</CheckBox>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabNetworkInterface}"
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
Width="200" />
Text="{locale:Locale SettingsTabNetworkInterface}"
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
Width="200" />
<ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}"
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
HorizontalContentAlignment="Left"
ItemsSource="{Binding NetworkInterfaceList}"
Width="250" />
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
HorizontalContentAlignment="Left"
ItemsSource="{Binding NetworkInterfaceList}"
Width="250" />
</StackPanel>
</StackPanel>
</Border>

View File

@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
{
var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
AllowMultiple = false
AllowMultiple = false,
});
if (result.Count > 0)
@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
{
Patterns = new[] { "*.xaml" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
MimeTypes = new[] { "application/xaml+xml" }
}
}
MimeTypes = new[] { "application/xaml+xml" },
},
},
});
if (result.Count > 0)

View File

@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.User
{
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" },
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" }
}
}
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" },
},
},
});
if (result.Count > 0)

View File

@ -11,6 +11,7 @@
Height="500"
MinWidth="500"
MinHeight="500"
x:DataType="window:CheatWindow"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d"
Focusable="True">
@ -40,7 +41,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="18"
Text="{ReflectionBinding Heading}"
Text="{Binding Heading}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
@ -61,7 +62,7 @@
MinWidth="160"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{ReflectionBinding BuildId}"
Text="{Binding BuildId}"
IsReadOnly="True" />
<Border
Grid.Row="3"
@ -77,7 +78,7 @@
MinHeight="300"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{ReflectionBinding LoadedCheats}">
ItemsSource="{Binding LoadedCheats}">
<TreeView.Styles>
<Styles>
<Style Selector="TreeViewItem:empty /template/ ItemsPresenter">
@ -120,18 +121,18 @@
Name="SaveButton"
MinWidth="90"
Margin="5"
Command="{ReflectionBinding Save}"
IsVisible="{ReflectionBinding !NoCheatsFound}">
Command="{Binding Save}"
IsVisible="{Binding !NoCheatsFound}">
<TextBlock Text="{locale:Locale SettingsButtonSave}" />
</Button>
<Button
Name="CancelButton"
MinWidth="90"
Margin="5"
Command="{ReflectionBinding Close}">
Command="{Binding Close}">
<TextBlock Text="{locale:Locale InputDialogCancel}" />
</Button>
</DockPanel>
</DockPanel>
</Grid>
</window:StyleableWindow>
</window:StyleableWindow>

View File

@ -17,7 +17,7 @@ namespace Ryujinx.Ava.UI.Windows
private readonly string _enabledCheatsPath;
public bool NoCheatsFound { get; }
private AvaloniaList<CheatsList> LoadedCheats { get; }
public AvaloniaList<CheatsList> LoadedCheats { get; }
public string Heading { get; }
public string BuildId { get; }

View File

@ -39,14 +39,14 @@
Name="EnableAllButton"
MinWidth="90"
Margin="5"
Command="{ReflectionBinding EnableAll}">
Command="{Binding EnableAll}">
<TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" />
</Button>
<Button
Name="DisableAllButton"
MinWidth="90"
Margin="5"
Command="{ReflectionBinding DisableAll}">
Command="{Binding DisableAll}">
<TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" />
</Button>
</StackPanel>
@ -157,14 +157,14 @@
Name="AddButton"
MinWidth="90"
Margin="5"
Command="{ReflectionBinding Add}">
Command="{Binding Add}">
<TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" />
</Button>
<Button
Name="RemoveAllButton"
MinWidth="90"
Margin="5"
Command="{ReflectionBinding RemoveAll}">
Command="{Binding RemoveAll}">
<TextBlock Text="{locale:Locale DlcManagerRemoveAllButton}" />
</Button>
</StackPanel>
@ -189,4 +189,4 @@
</StackPanel>
</Panel>
</Grid>
</UserControl>
</UserControl>

View File

@ -15,6 +15,7 @@ using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
@ -24,8 +25,8 @@ using Ryujinx.Ui.Common.Helper;
using System;
using System.IO;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using InputManager = Ryujinx.Input.HLE.InputManager;
namespace Ryujinx.Ava.UI.Windows
{
@ -79,35 +80,11 @@ namespace Ryujinx.Ava.UI.Windows
if (Program.PreviewerDetached)
{
Initialize();
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
ViewModel.Initialize(
ContentManager,
StorageProvider,
ApplicationLibrary,
VirtualFileSystem,
AccountManager,
InputManager,
_userChannelPersistence,
LibHacHorizonManager,
UiHandler,
ShowLoading,
SwitchToGameControl,
SetMainContent,
this);
ViewModel.RefreshFirmwareStatus();
LoadGameList();
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
this.ScalingChanged += OnScalingChanged;
}
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
@ -122,36 +99,17 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.IsActive = obj;
}
public void LoadGameList()
{
if (_isLoading)
{
return;
}
_isLoading = true;
LoadApplications();
_isLoading = false;
}
private void OnScalingChanged(object sender, EventArgs e)
{
Program.DesktopScaleFactor = this.RenderScaling;
}
public void AddApplication(ApplicationData applicationData)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.Applications.Add(applicationData);
});
}
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
{
AddApplication(e.AppData);
Dispatcher.UIThread.Post(() =>
{
ViewModel.Applications.Add(e.AppData);
});
}
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
@ -183,7 +141,7 @@ namespace Ryujinx.Ava.UI.Windows
string path = new FileInfo(args.Application.Path).FullName;
ViewModel.LoadApplication(path);
ViewModel.LoadApplication(path).Wait();
}
args.Handled = true;
@ -202,13 +160,10 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.ShowContent = true;
ViewModel.IsLoadingIndeterminate = false;
Dispatcher.UIThread.InvokeAsync(() =>
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
{
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
{
ViewModel.ToggleFullscreen();
}
});
ViewModel.ToggleFullscreen();
}
}
public void ShowLoading(bool startFullscreen = false)
@ -217,13 +172,10 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.ShowLoadProgress = true;
ViewModel.IsLoadingIndeterminate = true;
Dispatcher.UIThread.InvokeAsync(() =>
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
{
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
{
ViewModel.ToggleFullscreen();
}
});
ViewModel.ToggleFullscreen();
}
}
private void Initialize()
@ -251,11 +203,11 @@ namespace Ryujinx.Ava.UI.Windows
VirtualFileSystem.ReloadKeySet();
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this);
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient);
}
[SupportedOSPlatform("linux")]
private static async void ShowVmMaxMapCountWarning()
private static async Task ShowVmMaxMapCountWarning()
{
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
@ -267,7 +219,7 @@ namespace Ryujinx.Ava.UI.Windows
}
[SupportedOSPlatform("linux")]
private static async void ShowVmMaxMapCountDialog()
private static async Task ShowVmMaxMapCountDialog()
{
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
LinuxHelper.RecommendedVmMaxMapCount);
@ -313,33 +265,46 @@ namespace Ryujinx.Ava.UI.Windows
private void CheckLaunchState()
{
if (ShowKeyErrorOnLoad)
{
ShowKeyErrorOnLoad = false;
Dispatcher.UIThread.Post(async () => await
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
}
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
{
Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})");
if (LinuxHelper.PkExecPath is not null)
{
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
Dispatcher.UIThread.Post(async () =>
{
if (OperatingSystem.IsLinux())
{
await ShowVmMaxMapCountDialog();
}
});
}
else
{
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
Dispatcher.UIThread.Post(async () =>
{
if (OperatingSystem.IsLinux())
{
await ShowVmMaxMapCountWarning();
}
});
}
}
if (_deferLoad)
if (!ShowKeyErrorOnLoad)
{
_deferLoad = false;
if (_deferLoad)
{
_deferLoad = false;
ViewModel.LoadApplication(_launchPath, _startFullscreen);
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
}
}
else
{
ShowKeyErrorOnLoad = false;
Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
}
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
@ -372,7 +337,7 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal;
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal;
if (CheckScreenBounds(savedPoint))
{
@ -415,6 +380,30 @@ namespace Ryujinx.Ava.UI.Windows
{
base.OnOpened(e);
Initialize();
ViewModel.Initialize(
ContentManager,
StorageProvider,
ApplicationLibrary,
VirtualFileSystem,
AccountManager,
InputManager,
_userChannelPersistence,
LibHacHorizonManager,
UiHandler,
ShowLoading,
SwitchToGameControl,
SetMainContent,
this);
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
ViewModel.RefreshFirmwareStatus();
LoadApplications();
CheckLaunchState();
}
@ -514,18 +503,15 @@ namespace Ryujinx.Ava.UI.Windows
});
}
public async void LoadApplications()
public void LoadApplications()
{
await Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.Applications.Clear();
ViewModel.Applications.Clear();
StatusBarView.LoadProgressBar.IsVisible = true;
ViewModel.StatusBarProgressMaximum = 0;
ViewModel.StatusBarProgressValue = 0;
StatusBarView.LoadProgressBar.IsVisible = true;
ViewModel.StatusBarProgressMaximum = 0;
ViewModel.StatusBarProgressValue = 0;
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
});
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
ReloadGameList();
}
@ -558,9 +544,17 @@ namespace Ryujinx.Ava.UI.Windows
_isLoading = true;
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
Thread applicationLibraryThread = new(() =>
{
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language);
_isLoading = false;
_isLoading = false;
})
{
Name = "GUI.ApplicationLibraryThread",
IsBackground = true,
};
applicationLibraryThread.Start();
}
}
}

View File

@ -3,7 +3,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.IO;
using System.Reflection;
@ -25,11 +24,6 @@ namespace Ryujinx.Ava.UI.Windows
IconImage = new Bitmap(stream);
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);

View File

@ -192,7 +192,7 @@ namespace Ryujinx.Common.Collections
{
if (start.CompareTo(overlap.End) < 0)
{
if (overlaps.Length >= overlapCount)
if (overlaps.Length <= overlapCount)
{
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
}

View File

@ -0,0 +1,8 @@
namespace Ryujinx.Common.Configuration.Multiplayer
{
public enum MultiplayerMode
{
Disabled,
LdnMitm,
}
}

View File

@ -756,6 +756,18 @@ namespace Ryujinx.Common.Memory
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
public struct Array96<T> : IArray<T> where T : unmanaged
{
T _e0;
Array64<T> _other;
Array31<T> _other2;
public readonly int Length => 96;
public ref T this[int index] => ref AsSpan()[index];
[Pure]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
public struct Array127<T> : IArray<T> where T : unmanaged
{
T _e0;
@ -791,5 +803,34 @@ namespace Ryujinx.Common.Memory
[Pure]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
public struct Array140<T> : IArray<T> where T : unmanaged
{
T _e0;
Array64<T> _other;
Array64<T> _other2;
Array11<T> _other3;
public readonly int Length => 140;
public ref T this[int index] => ref AsSpan()[index];
[Pure]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
public struct Array384<T> : IArray<T> where T : unmanaged
{
T _e0;
Array64<T> _other;
Array64<T> _other2;
Array64<T> _other3;
Array64<T> _other4;
Array64<T> _other5;
Array63<T> _other6;
public readonly int Length => 384;
public ref T this[int index] => ref AsSpan()[index];
[Pure]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);
}
}
#pragma warning restore CS0169, IDE0051

View File

@ -5,7 +5,7 @@ namespace Ryujinx.Common
{
public class ReactiveObject<T>
{
private readonly ReaderWriterLock _readerWriterLock = new();
private readonly ReaderWriterLockSlim _readerWriterLock = new();
private bool _isInitialized;
private T _value;
@ -15,15 +15,15 @@ namespace Ryujinx.Common
{
get
{
_readerWriterLock.AcquireReaderLock(Timeout.Infinite);
_readerWriterLock.EnterReadLock();
T value = _value;
_readerWriterLock.ReleaseReaderLock();
_readerWriterLock.ExitReadLock();
return value;
}
set
{
_readerWriterLock.AcquireWriterLock(Timeout.Infinite);
_readerWriterLock.EnterWriteLock();
T oldValue = _value;
@ -32,7 +32,7 @@ namespace Ryujinx.Common
_isInitialized = true;
_value = value;
_readerWriterLock.ReleaseWriterLock();
_readerWriterLock.ExitWriteLock();
if (!oldIsInitialized || oldValue == null || !oldValue.Equals(_value))
{

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
</PropertyGroup>

View File

@ -1,4 +1,6 @@
using System.Net.NetworkInformation;
using System.Buffers.Binary;
using System.Net;
using System.Net.NetworkInformation;
namespace Ryujinx.Common.Utilities
{
@ -62,5 +64,20 @@ namespace Ryujinx.Common.Utilities
return (targetProperties, targetAddressInfo);
}
public static uint ConvertIpv4Address(IPAddress ipAddress)
{
return BinaryPrimitives.ReadUInt32BigEndian(ipAddress.GetAddressBytes());
}
public static uint ConvertIpv4Address(string ipAddress)
{
return ConvertIpv4Address(IPAddress.Parse(ipAddress));
}
public static IPAddress ConvertUint(uint ipAddress)
{
return new IPAddress(new byte[] { (byte)((ipAddress >> 24) & 0xFF), (byte)((ipAddress >> 16) & 0xFF), (byte)((ipAddress >> 8) & 0xFF), (byte)(ipAddress & 0xFF) });
}
}
}

View File

@ -1,9 +1,11 @@
using Ryujinx.Cpu.AppleHv.Arm;
using Ryujinx.Memory;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
class HvAddressSpace : IDisposable
{
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));

View File

@ -2,10 +2,12 @@ using Ryujinx.Cpu.AppleHv.Arm;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
class HvAddressSpaceRange : IDisposable
{
private const ulong AllocationGranule = 1UL << 14;

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
@ -12,10 +13,18 @@ namespace Ryujinx.Cpu.AppleHv
#pragma warning restore CS0649
}
enum HvExitReason : uint
{
Canceled,
Exception,
VTimerActivated,
Unknown,
}
struct HvVcpuExit
{
#pragma warning disable CS0649 // Field is never assigned to
public uint Reason;
public HvExitReason Reason;
public HvVcpuExitException Exception;
#pragma warning restore CS0649
}
@ -255,6 +264,7 @@ namespace Ryujinx.Cpu.AppleHv
}
}
[SupportedOSPlatform("macos")]
static partial class HvApi
{
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";

View File

@ -0,0 +1,62 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace Ryujinx.Cpu.AppleHv
{
static class HvCodePatcher
{
private const uint XMask = 0x3f808000u;
private const uint XValue = 0x8000000u;
private const uint ZrIndex = 31u;
public static void RewriteUnorderedExclusiveInstructions(Span<byte> code)
{
Span<uint> codeUint = MemoryMarshal.Cast<byte, uint>(code);
Span<Vector128<uint>> codeVector = MemoryMarshal.Cast<byte, Vector128<uint>>(code);
Vector128<uint> mask = Vector128.Create(XMask);
Vector128<uint> value = Vector128.Create(XValue);
for (int index = 0; index < codeVector.Length; index++)
{
Vector128<uint> v = codeVector[index];
if (Vector128.EqualsAny(Vector128.BitwiseAnd(v, mask), value))
{
int baseIndex = index * 4;
for (int instIndex = baseIndex; instIndex < baseIndex + 4; instIndex++)
{
ref uint inst = ref codeUint[instIndex];
if ((inst & XMask) != XValue)
{
continue;
}
bool isPair = (inst & (1u << 21)) != 0;
bool isLoad = (inst & (1u << 22)) != 0;
uint rt2 = (inst >> 10) & 0x1fu;
uint rs = (inst >> 16) & 0x1fu;
if (isLoad && rs != ZrIndex)
{
continue;
}
if (!isPair && rt2 != ZrIndex)
{
continue;
}
// Set the ordered flag.
inst |= 1u << 15;
}
}
}
}
}
}

View File

@ -1,7 +1,9 @@
using ARMeilleure.Memory;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
class HvCpuContext : ICpuContext
{
private readonly ITickSource _tickSource;

View File

@ -1,7 +1,9 @@
using ARMeilleure.Memory;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
public class HvEngine : ICpuEngine
{
private readonly ITickSource _tickSource;

View File

@ -2,9 +2,12 @@ using ARMeilleure.State;
using Ryujinx.Cpu.AppleHv.Arm;
using Ryujinx.Memory.Tracking;
using System;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
class HvExecutionContext : IExecutionContext
{
/// <inheritdoc/>
@ -67,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv
private readonly ExceptionCallbacks _exceptionCallbacks;
private int _interruptRequested;
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
{
_counter = counter;
@ -111,7 +116,15 @@ namespace Ryujinx.Cpu.AppleHv
/// <inheritdoc/>
public void RequestInterrupt()
{
_impl.RequestInterrupt();
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
{
impl.RequestInterrupt();
}
}
private bool GetAndClearInterruptRequested()
{
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
}
/// <inheritdoc/>
@ -131,9 +144,9 @@ namespace Ryujinx.Cpu.AppleHv
{
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
uint reason = vcpu.ExitInfo->Reason;
HvExitReason reason = vcpu.ExitInfo->Reason;
if (reason == 1)
if (reason == HvExitReason.Exception)
{
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
@ -146,14 +159,22 @@ namespace Ryujinx.Cpu.AppleHv
address = SynchronousException(memoryManager, ref vcpu);
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
}
else if (reason == 0)
else if (reason == HvExitReason.Canceled || reason == HvExitReason.VTimerActivated)
{
if (_impl.GetAndClearInterruptRequested())
if (GetAndClearInterruptRequested())
{
ReturnToPool(vcpu);
InterruptHandler();
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
}
if (reason == HvExitReason.VTimerActivated)
{
vcpu.EnableAndUpdateVTimer();
// Unmask VTimer interrupts.
HvApi.hv_vcpu_set_vtimer_mask(vcpu.Handle, false).ThrowOnError();
}
}
else
{

View File

@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
{
_v[index] = value;
}
public void RequestInterrupt()
{
}
public bool GetAndClearInterruptRequested()
{
return false;
}
}
}

View File

@ -2,10 +2,11 @@ using ARMeilleure.State;
using Ryujinx.Memory;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
class HvExecutionContextVcpu : IHvExecutionContext
{
private static readonly MemoryBlock _setSimdFpRegFuncMem;
@ -135,7 +136,6 @@ namespace Ryujinx.Cpu.AppleHv
}
private readonly ulong _vcpu;
private int _interruptRequested;
public HvExecutionContextVcpu(ulong vcpu)
{
@ -181,16 +181,8 @@ namespace Ryujinx.Cpu.AppleHv
public void RequestInterrupt()
{
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
{
ulong vcpu = _vcpu;
HvApi.hv_vcpus_exit(ref vcpu, 1);
}
}
public bool GetAndClearInterruptRequested()
{
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
ulong vcpu = _vcpu;
HvApi.hv_vcpus_exit(ref vcpu, 1);
}
}
}

View File

@ -1,8 +1,10 @@
using Ryujinx.Memory;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
[SupportedOSPlatform("macos")]
readonly struct HvMemoryBlockAllocation : IDisposable
{
private readonly HvMemoryBlockAllocator _owner;

Some files were not shown because too many files have changed in this diff Show More