Compare commits

..

5 Commits

Author SHA1 Message Date
Renovate Bot
737b951ee9 Update avalonia monorepo to 11.3.18 (#148)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Avalonia](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.17` → `11.3.18` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia/11.3.18?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia/11.3.17/11.3.18?slim=true) |
| [Avalonia.Desktop](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.17` → `11.3.18` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia.Desktop/11.3.18?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia.Desktop/11.3.17/11.3.18?slim=true) |
| [Avalonia.Diagnostics](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.17` → `11.3.18` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia.Diagnostics/11.3.18?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia.Diagnostics/11.3.17/11.3.18?slim=true) |
| [Avalonia.Markup.Xaml.Loader](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.17` → `11.3.18` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia.Markup.Xaml.Loader/11.3.18?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia.Markup.Xaml.Loader/11.3.17/11.3.18?slim=true) |

---

### Release Notes

<details>
<summary>AvaloniaUI/Avalonia (Avalonia)</summary>

### [`v11.3.18`](https://github.com/AvaloniaUI/Avalonia/releases/tag/11.3.18)

[Compare Source](https://github.com/AvaloniaUI/Avalonia/compare/11.3.17...11.3.18)

##### What's Changed

##### Enhancements

- XAML – Enhance Roslyn-compiler visible metadata by [@&#8203;maxkatz6](https://github.com/maxkatz6) in [#&#8203;21546](https://github.com/AvaloniaUI/Avalonia/pull/21546)

##### Fixes

- Core – Fix StackOverflow when a `NaN` offset is set on `ScrollViewer` by [@&#8203;NicholasLachapelle](https://github.com/NicholasLachapelle) in [#&#8203;21558](https://github.com/AvaloniaUI/Avalonia/pull/21558)
- macOS – Handle `replacementRange` in `AvnView` by [@&#8203;MrJul](https://github.com/MrJul) in [#&#8203;21608](https://github.com/AvaloniaUI/Avalonia/pull/21608)

**Full Changelog**: <https://github.com/AvaloniaUI/Avalonia/compare/11.3.17...11.3.18>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzguMCIsInVwZGF0ZWRJblZlciI6IjQzLjE3OC4wIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/148
2026-06-26 07:14:22 +00:00
Max
5566e752a4 [HID] Restructure HD Rumble class for future controller support (#109)
- Attempted fixing the strength: so far it hasn't been successful.
- Rumble should skip vibrations if they're not in-line with poll-rate: would like to come back to this. Queuing just does exactly what the hid buffer does, but our timer (poll rate) is not in sync with the rate the controller is reading at, which causes excess drops.
- Refactored the class so that implementing support for HD rumble for other controllers (DS5, Steam Controller) is much easier in the future.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/109
2026-06-26 05:11:19 +00:00
awesomeangotti
a5f72136b2 UI: Add random splashes to loading screen (#128)
This PR introduces splash text messages that change per startup on the loading screen after selecting a game. It also moves the "RYUBING" logo splash in logs to be inside its own class, which also handles loading screen splashes and titlebar splashes. Credits to VewDev, Lotp, Sh0inx, yell0wsuit, and Greemdev for pointers and assistance throughout this PR.

Co-authored-by: Awesomeangotti <awesomeangotti@noreply.git.ryujinx.app>
Co-authored-by: Awesomeangotti <143439211+Awesomeangotti@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/128
2026-06-26 02:42:41 +00:00
awesomeangotti
aa5d32a7b1 Change RPCData embedded resource to PlayReports (#147)
Change RPCData embedded resource to PlayReports

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/147
2026-06-26 00:23:20 +00:00
awesomeangotti
be5881f100 Discord Rich Presence: New Super Mario Bros U Deluxe (#130)
Add RPC support for NSMBUD play reports that generate on main menu and after finishing a course.

Tracked things:

Main menu
Last played course

Examples:

Main menu
![image](/attachments/9f1506bd-fc8c-4eca-930d-64f15a7c650d)

After finishing a course (By dying or by beating it)
![image](/attachments/a4af3f4f-230d-47ac-977a-20281a103cb6)

In the future should I be doing batch PRs for RPC related things? Yes.

Co-authored-by: Awesomeangotti <143439211+Awesomeangotti@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/130
2026-06-25 20:59:56 +00:00
16 changed files with 667 additions and 103 deletions

View File

@@ -3,11 +3,11 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="11.3.17" />
<PackageVersion Include="Avalonia" Version="11.3.18" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.17" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.17" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.17" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.18" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.18" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.18" />
<PackageVersion Include="SharpCompress" Version="0.49.1" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />

View File

@@ -9606,7 +9606,7 @@
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Sends more data to the controller for better rumble.\n\nCurrently only supports first-party Nintendo Switch controllers.\n\nLeave ON if you're using JoyCons or a Pro Controller.",
"en_US": "EXPERIMENTAL.\n\nSends more data to the controller for better rumble.\n\nCurrently only supports first-party Nintendo Switch controllers.\n\nLeave OFF if unsure.",
"es_ES": "",
"fr_FR": "",
"he_IL": "",

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private readonly bool[] _supportedPlayers;
private VibrationValue _neutralVibrationValue = new()
{
AmplitudeLow = 0f,
AmplitudeLow = 0.01f,
FrequencyLow = 160f,
AmplitudeHigh = 0f,
FrequencyHigh = 320f,

View File

@@ -13,84 +13,98 @@ namespace Ryujinx.Input.SDL3
{
private readonly SDL_hid_device* _hidHandle;
private byte[] _buffer;
private static ushort _vendor;
private static ushort _product;
private int _globalCount;
private ulong _lastWriteTicks;
private NpadHdRumble(SDL_hid_device* hidHandle)
{
_hidHandle = hidHandle;
InitializeDevice();
}
public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle)
{
ushort vendor = SDL_GetGamepadVendor(gamepadHandle);
if (vendor != 0x057e)
_vendor = SDL_GetGamepadVendor(gamepadHandle);
if (!Enum.IsDefined(typeof(HDRumbleSupportedVendor), _vendor))
{
return null;
}
ushort product = SDL_GetGamepadProduct(gamepadHandle);
if (!Enum.IsDefined(typeof(HDRumbleSupported), product))
_product = SDL_GetGamepadProduct(gamepadHandle);
if (!Enum.IsDefined(typeof(HDRumbleSupportedProduct), _product))
{
return null;
}
return new NpadHdRumble(SDL_hid_open(vendor, product, 0));
int serialNumber = 0;
string? serial = SDL_GetGamepadSerial(gamepadHandle);
if (serial is not null)
{
int.TryParse(serial, out serialNumber);
}
return new NpadHdRumble(SDL_hid_open(_vendor, _product, serialNumber));
}
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
private bool WriteHdRumble(
int encLeftLowFreq, int encLeftLowAmp,
int encLeftHighFreq, int encLeftHighAmp,
int encRightLowFreq, int encRightLowAmp,
int encRightHighFreq, int encRightHighAmp)
private bool WriteNintendoHdRumble(VibrationValue left, VibrationValue right)
{
byte[] buf = new byte[10];
buf[0] = 0x10;
buf[1] = (byte)((++_globalCount) & 0xF);
buf[2] = (byte)(encLeftHighFreq & 0xFF);
buf[3] = (byte)(encLeftHighAmp + ((encLeftHighFreq >> 8) & 0xFF));
buf[4] = (byte)(encLeftLowFreq + ((encLeftLowAmp >> 8) & 0xFF));
buf[5] = (byte)(encLeftLowAmp & 0xFF);
buf[6] = (byte)(encRightHighFreq & 0xFF);
buf[7] = (byte)(encRightHighAmp + ((encRightHighFreq >> 8) & 0xFF));
buf[8] = (byte)(encRightLowFreq + ((encRightLowAmp >> 8) & 0xFF));
buf[9] = (byte)(encRightLowAmp & 0xFF);
int leftLowAmp = EncodeLowAmp(left.AmplitudeLow);
int leftLowFreq = EncodeLowFreq(left.FrequencyLow) + (leftLowAmp >> 8);
int leftHighFreq = EncodeHighFreq(left.FrequencyHigh);
int leftHighAmp = EncodeHighAmp(left.AmplitudeHigh) + (leftHighFreq >> 8);
int rightLowAmp = EncodeLowAmp(right.AmplitudeLow);
int rightLowFreq = EncodeLowFreq(right.FrequencyLow) + (rightLowAmp >> 8);
int rightHighFreq = EncodeHighFreq(right.FrequencyHigh);
int rightHighAmp = EncodeHighAmp(right.AmplitudeHigh) + (rightHighFreq >> 8);
_buffer[0] = 0x10;
_buffer[1] = (byte)((_globalCount++) & 0xF);
// Left LRA
_buffer[2] = (byte)(leftLowFreq & 0xFF);
_buffer[3] = (byte)(leftHighAmp & 0xFF);
_buffer[4] = (byte)(leftHighFreq & 0xFF);
_buffer[5] = (byte)(leftLowAmp & 0xFF);
// Right LRA
_buffer[6] = (byte)(rightLowFreq & 0xFF);
_buffer[7] = (byte)(rightHighAmp & 0xFF);
_buffer[8] = (byte)(rightHighFreq & 0xFF);
_buffer[9] = (byte)(rightLowAmp & 0xFF);
if (_globalCount > 0xF)
{
_globalCount = 0x0;
}
fixed (byte* ptr = buf)
fixed (byte* ptr = _buffer)
{
if (SendHDRumble(ptr, (nuint)buf.Length) >= 0)
if (SendHdRumble(ptr, (nuint)_buffer.Length) >= 0)
{
return true;
}
if (!String.IsNullOrEmpty(SDL_GetError()))
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
}
return false;
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
}
return false;
}
private static int EncodeLowFreq(float lowFreq)
{
float lf = Math.Clamp(lowFreq, 40.875885f, 626.286133f);
return (int) Math.Round(32 * Math.Log2(lf * 0.1f) - 0x40);
return (int)Math.Clamp(32 * Math.Log2(lowFreq * 0.1f) - 0x40, 81.75177f, 1252.572266f);
}
private static int EncodeHighFreq(float highFreq)
{
float hf = Math.Clamp(highFreq, 81.75177f, 1252.572266f);
return (int) Math.Round((32 * Math.Log2(hf * 0.1f) - 0x60) * 4);
return (int)Math.Clamp(32 * Math.Log2(highFreq * 0.1f) - 0x60, 81.75177f, 1252.572266f);
}
private static int EncodeLowAmp(float rawAmp)
@@ -98,23 +112,20 @@ namespace Ryujinx.Input.SDL3
double encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f)
{
encodedAmp = 1;
}
else if (rawAmp is >= 0.012f and < 0.112f)
{
encodedAmp = 4 * Math.Log2(rawAmp * 110f);
}
else if (rawAmp is >= 0.112f and < 0.225f)
{
encodedAmp = 16 * Math.Log2(rawAmp * 17f);
}
else if (rawAmp is >= 0.225f and <= 1f)
{
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f);
}
return (int)Math.Floor(encodedAmp / 2.0) + 64;
encodedAmp = Math.Round((encodedAmp / 2.0) + 64.0);
encodedAmp = Math.Clamp(encodedAmp, 0.0, 100.2867);
return (int)Math.Round(encodedAmp);
}
private static int EncodeHighAmp(float rawAmp)
@@ -122,82 +133,156 @@ namespace Ryujinx.Input.SDL3
double encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f)
{
encodedAmp = 1;
}
else if (rawAmp is >= 0.012f and < 0.112f)
{
encodedAmp = 4 * Math.Log2(rawAmp * 110f);
}
else if (rawAmp is >= 0.112f and < 0.225f)
{
encodedAmp = 16 * Math.Log2(rawAmp * 17f);
}
else if (rawAmp is >= 0.225f and <= 1f)
{
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f);
}
return (int) Math.Round(encodedAmp * 2);
encodedAmp = Math.Round(encodedAmp / 2.0);
encodedAmp = Math.Clamp(encodedAmp, 0.0, 100.2867);
return (int)encodedAmp;
}
public bool HdRumble(VibrationValue left, VibrationValue right)
{
return WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
EncodeLowAmp(left.AmplitudeLow),
EncodeHighFreq(left.FrequencyHigh),
EncodeHighAmp(left.AmplitudeHigh),
EncodeLowFreq(right.FrequencyLow),
EncodeLowAmp(right.AmplitudeLow),
EncodeHighFreq(right.FrequencyHigh),
EncodeHighAmp(right.AmplitudeHigh));
if(_product is (ushort) HDRumbleSupportedProduct.ProController
or (ushort) HDRumbleSupportedProduct.JoyconLeft
or (ushort) HDRumbleSupportedProduct.JoyconRight
or (ushort) HDRumbleSupportedProduct.JoyconPair
or (ushort) HDRumbleSupportedProduct.JoyconGrip)
{
return WriteNintendoHdRumble(left, right);
}
return false;
}
private int SendHDRumble(byte* data, nuint length)
private int SendHdRumble(byte* data, nuint length)
{
int result = 0;
ulong currentTicks = SDL_GetTicks();
// Ditch rumble if we haven't hit the poll-rate yet.
// TODO: figure out a better way to do this
// While the polling check makes the rumble accurate, it also causes it to miss signals.
if ((currentTicks - _lastWriteTicks) < 8) // https://docs.handheldlegend.com/s/progcc-3/doc/lag-comparison-aAR1mV3JLX
if ((currentTicks - _lastWriteTicks) <= GetPollRate())
{
return result;
}
SDL_LockJoysticks();
result = SDL_hid_write(_hidHandle, data, length);
if (result >= 0)
{
// Fun fact: Mario Kart 8 Deluxe sends rumble packets
// where the amplitude is zero, but the frequency isn't.
result = SDL_hid_write(_hidHandle, data, length);
if (result >= 0)
{
_lastWriteTicks = currentTicks;
}
_lastWriteTicks = currentTicks;
}
SDL_UnlockJoysticks();
return result;
}
private void InitializeDevice()
{
if (_vendor is (ushort)HDRumbleSupportedVendor.Nintendo)
{
_buffer = new byte[10];
byte[] init = new byte[64];
// Pro Controller and Charge Grip
if (_product
is (ushort)HDRumbleSupportedProduct.ProController
or (ushort)HDRumbleSupportedProduct.JoyconGrip)
{
SDL_LockJoysticks();
fixed (byte* ptr = init)
{
init[0] = 0x80;
init[1] = 0x05; // Allow bluetooth timeout TODO: use 0x04 to force USB only (toggle?)
SDL_hid_write(_hidHandle, ptr, 64);
}
SDL_UnlockJoysticks();
return;
}
// Joycons
if (_product
is (ushort)HDRumbleSupportedProduct.JoyconLeft
or (ushort)HDRumbleSupportedProduct.JoyconRight
or (ushort)HDRumbleSupportedProduct.JoyconPair)
{
SDL_LockJoysticks();
fixed (byte* ptr = init)
{
// we could write data to the controller here (see above)
}
SDL_UnlockJoysticks();
return;
}
}
}
private ulong GetPollRate()
{
ulong pollRate = 0;
if (_vendor is (ushort)HDRumbleSupportedVendor.Nintendo)
{
// https://docs.handheldlegend.com/s/progcc-3/doc/lag-comparison-aAR1mV3JLX
pollRate = (ulong) 16.67;
if (_product is (ushort)HDRumbleSupportedProduct.ProController
&& SDL_hid_get_device_info(_hidHandle)->bus_type == SDL_hid_bus_type.SDL_HID_API_BUS_USB)
{
pollRate = (ulong) 8.33;
}
}
return pollRate;
}
public void Dispose()
{
GC.SuppressFinalize(this);
SDL_hid_close(_hidHandle);
}
}
public enum HDRumbleSupported : ushort
public enum HDRumbleSupportedVendor : ushort
{
JoyConLeft = 0x2006,
JoyConRight = 0x2007,
Nintendo = 0x057e,
Valve = 0x28de,
Sony = 0x054c
}
public enum HDRumbleSupportedProduct : ushort
{
// TODO: Currently, HD Rumble only supports the Pro Controller and JoyCons.
// We need to initialize and report to each device differently.
// Nintendo Switch: 0x057e
JoyconLeft = 0x2006,
JoyconRight = 0x2007,
JoyconPair = 0x2008,
ProController = 0x2009,
JoyconGrip = 0x200e,
// Nintendo Switch 2: 0x057e
Joycon2Right = 0x2066,
Joycon2Left = 0x2067,
Joycon2Pair = 0x2068,
Switch2ProController = 0x2069,
GamecubeController = 0x2073
GamecubeController = 0x2073,
// Valve Steam Family: 0x28de
// https://github.com/libsdl-org/SDL/issues/9148
SteamDeck = 0x11ff,
SteamDeckVirtualDevice = 0x1205,
SteamController = 0x1106,
// PlayStation Dualsense: 0x054c
Dualsense = 0x0ce6
}
}

View File

@@ -582,12 +582,12 @@ namespace Ryujinx.Input.HLE
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
// Value=value/multiplier * multiplier (result)
$"L.low.amp={leftVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeLow}), " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeHigh}), " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({leftVibrationValue.AmplitudeHigh}), " +
$"L.low.freq={leftVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyLow}), " +
$"L.high.freq={leftVibrationValue.FrequencyHigh / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyHigh}), " +
$"R.low.amp={rightVibrationValue.AmplitudeLow / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeLow}), " +
$"L.high.freq={leftVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({leftVibrationValue.FrequencyHigh}), " +
$"R.low.amp={rightVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({rightVibrationValue.AmplitudeLow}), " +
$"R.high.amp={rightVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeHigh}), " +
$"R.low.freq={rightVibrationValue.FrequencyLow / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyLow}), " +
$"R.low.freq={rightVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({rightVibrationValue.FrequencyLow}), " +
$"R.high.freq={rightVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyHigh})");
}
}

View File

@@ -140,7 +140,7 @@ namespace Ryujinx.Input
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
UseHDRumble = true,
UseHDRumble = false
},
};
}

View File

@@ -0,0 +1,216 @@
{
"mario": {
"1": {
"1": "Acorn Plains Way",
"2": "Tilted Tunnel",
"21": "Crushing-Cogs Tower",
"3": "Yoshi Hill",
"4": "Mushroom Heights",
"5": "Rise of the Piranha Plants",
"23": "Lemmy's Swingback Castle",
"13": "Blooper's Secret Lair"
},
"2": {
"1": "Stone-Eye Zone",
"2": "Perilous Pokey Cave",
"3": "Fire Snake Cavern",
"21": "Stoneslide Tower",
"4": "Spike's Spouting Sands",
"5": "Dry Desert Mushrooms",
"6": "Blooming Lakitus",
"23": "Morton's Compactor Castle",
"14": "Piranha Plants on Ice"
},
"3": {
"1": "Waterspout Beach",
"2": "Tropical Refresher",
"21": "Giant Skewer Tower",
"20": "Haunted Shipwreck",
"3": "Above the Cheep Cheep Seas",
"4": "Urchin Shoals",
"5": "Dragoneel's Undersea Grotto",
"23": "Larry's Torpedo Castle",
"15": "Skyward Stalk"
},
"4": {
"1": "Spinning-Star Sky",
"2": "Cooligan Fields",
"21": "Freezing-Rain Tower",
"3": "Prickly Goombas!",
"4": "Scaling the Mountainside",
"5": "Icicle Caverns",
"20": "Swaying Ghost House",
"23": "Wendy's Shifting Castle",
"16": "Fliprus Lake"
},
"5": {
"37": "The Mighty Cannonship",
"1": "Jungle of the Giants",
"2": "Bridge over Poisoned Waters",
"3": "Bramball Woods",
"21": "Snake Block Tower",
"20": "Which-Way Labyrinth",
"4": "Painted Swampland",
"5": "Deepsea Ruins",
"6": "Seesaw Bridge",
"7": "Wiggler Stampede",
"23": "Iggy's Volcanic Castle",
"17": "Flight of the Para-Beetles"
},
"6": {
"1": "Fuzzy Clifftop",
"2": "Porcupuffer Falls",
"21": "Grinding-Stone Tower",
"3": "Wadlewing's Nest",
"4": "Light Blocks, Dark Tower",
"5": "Walking Piranha Plants!",
"6": "Thrilling Spine Coaster",
"22": "Screwtop Tower",
"7": "Shifting-Floor Cave",
"23": "Roy's Conveyor Castle"
},
"7": {
"1": "Land of Flying Blocks",
"2": "Seesaw Shrooms",
"3": "Switchback Hill",
"21": "Slide Lift Tower",
"20": "Spinning Spirit House",
"4": "Bouncy Cloud Boomerangs",
"5": "A Quick Dip in the Sky",
"6": "Snaking above Mist Valley",
"23": "Ludwig's Clockwork Castle",
"37": "Boarding the Airship"
},
"8": {
"1": "Meteor Moat",
"2": "Magma-River Cruise",
"3": "Rising Tides of Lava",
"4": "firefall Cliffs",
"42": "Red-Hot Elevator Ride",
"43": "The Final Battle"
},
"9": {
"1": "Spine-Tingling Spine Coaster",
"2": "Run for It",
"3": "Swim for Your Life!",
"4": "Hammerswing Caverns",
"5": "Spinning Platforms of Doom",
"6": "Fire Bar Cliffs",
"7": "Lakitu! Lakitu! Lakitu!",
"8": "Pendulum Castle",
"9": "Follow That Shell!"
},
"11": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"6": "",
"7": "",
"8": ""
}
},
"luigi": {
"1": {
"1": "Waddlewing Warning!",
"2": "Crooked Cavern",
"21": "Flame-Gear Tower",
"3": "Rolling Yoshi Hills",
"4": "Piranha Heights",
"5": "Piranha Gardens",
"23": "Lemmy's Lights-Out Castle",
"13": "Cheep Chomp Chase"
},
"2": {
"1": "Spike's Tumbling Desert",
"2": "Underground Grrrols",
"3": "Piranhas in the Dark",
"21": "Wind-Up Tower",
"4": "The Walls Have Eyes",
"5": "Stone Spike Conveyors",
"6": "Spinning Sandstones",
"23": "Morton's Lava-Block Castle",
"14": "Slippery Rope Ladders"
},
"3": {
"1": "Huckit Beach Resort",
"2": "Urchin Reef Romp",
"21": "Shish-Kebab Tower",
"20": "Haunted Cargo Hold",
"3": "Waterspout Sprint",
"4": "The Great Geysers",
"5": "Dragoneel Depths",
"23": "Larry's Trigger-Happy Castle",
"15": "Beanstalk Jungle"
},
"4": {
"1": "Broozers and Barrels",
"2": "Cooligan Shrooms",
"21": "Icicle Tower",
"3": "Fire and Ice",
"4": "Weighty Waddlewings",
"5": "Ice-Slide Expressway",
"20": "Peek-a-Boo Ghost House",
"23": "Wendy's Thwomp Castle",
"16": "Fliprus Floes"
},
"5": {
"1": "Giant Swing-Along",
"2": "Dancing Blocks, Poison Swamp",
"3": "Heart of Bramball Woods",
"21": "Stone-Snake Tower",
"20": "Boo's Favorite Haunt",
"4": "Painted Pipeworks",
"5": "Deepsea Stone-Eyes",
"6": "Sumo Bro Bridge",
"7": "Wiggler Floodlands",
"23": "Iggy's Swinging-Chains Castle",
"17": "Para-Beetle Parade"
},
"6": {
"1": "Mount Fuzzy",
"2": "Porcupuffer Cavern",
"21": "Smashing-Stone Tower",
"3": "Spike's Seesaws",
"4": "Light-Up-Lift Tower",
"5": "Rising Piranhas",
"6": "Spine Coaster Stowaways",
"22": "Sumo Bro's Spinning Tower",
"7": "Switch-Lift Express",
"23": "Roy's Ironclad Castle"
},
"7": {
"1": "Frozen Fuzzies",
"2": "Wiggler Rodeo",
"3": "Rainbow Skywalk",
"21": "Stonecrush Tower",
"20": "Vanishing Ghost House",
"4": "Above The Bouncy Clouds",
"5": "Flame Chomp Ferris Wheel",
"6": "Three-Headed Snake Block",
"23": "Ludwig's Block-Press Castle",
"37": "Bowser Jr. Showdown"
},
"8": {
"1": "Magma Moat",
"2": "Magmaw River Cruise",
"3": "Hot Cogs",
"4": "Firefall Rising",
"42": "Current Event",
"43": "The Final Battle"
},
"9": {
"1": "Spine Coaster Connections",
"2": "P Switch Peril",
"3": "Star Coin Deep Dive",
"4": "Hammerswing Hideout",
"5": "Under Construction",
"6": "Fire Bar Sprint",
"7": "Cloudy Capers",
"8": "Impossible Pendulums",
"9": "Flying Squirrel Ovation"
}
}
}

View File

@@ -0,0 +1,98 @@
{
"Locales": {
"ar_SA": [],
"de_DE": [],
"el_GR": [],
"en_US": [
"Ryubing is my middle name.",
"Giving it 110 percent!",
"I don't think therefore I don't am!",
"All hail Egg.",
"Insert cringy joke here.",
"ITS RYUBINGING TIME!",
"I hate Mondays...",
"Fantastical!",
"Now with 100% more humor!",
"'Not S&P approved' has been approved by S&P.",
"ARE YOU NOT ENTERTAINED?",
"It's an emulator!",
"Now the real game begins...",
"Cooked fresh since 2018!",
"Must've been the wind...",
"I used to be an adventurer like you before I took an arrow to the knee.",
"Ryubing!",
"May contain nuts!",
"May include occasional pop culture references!",
"100% organically grown!",
"Have a nice day : )",
"Spoats car!",
"Bottom text",
"Im sorry Dave. I'm afraid I can't do that.",
"That's no moon...",
"Sir, finishing this fight.",
"I see how it is...",
"Space! The final frontier!",
"If you could not tell already, I love making bad jokes : )",
"this.",
"Probably contains no baked beans.",
"Y'all ready for this?",
"Removed Herobrine.",
"Right to repair!",
"Programmed in C#!",
"Forgejo has dethroned Gitlab!",
"Any ideas what to put here?",
"Good morning!",
"Good afternoon!",
"Good evening!",
"I hope you are having a great day!",
"Please insert disc two!",
"I... AM RYUBING!",
"Ryubingin' it up",
"bing bing wahoo.",
"egg",
"No, lossless scaling is NOT supported.",
"How do you people do anything?",
"One dollar.",
"Somebody once told me!",
"Its that time of the year again!",
"Brewed from only the finest memes.",
"Async shader compilation would destroy my soul : (",
"Trans rights are human rights!",
":3",
"Patched ':3' splash replication glitch.",
"Please connect a controller!",
"Never gonna give you up!",
"The game was rigged from the start.",
"Ganon is watching you!",
"Now with 100% more JSON in the splash code!",
"Countless hours of fun!",
"Sorry, Link. I can't give credit. Come back when you're a little... mmmmmm... richer!",
"Do a barrel roll!",
"You've met with a terrible fate, haven't you?",
"Yahaha! You found me!",
"I would've been in real trouble if you hadn't shown up when you did, goro.",
"Stay fresh!",
"Yellow, black. Yellow, black. Yellow, black. Yellow, black. Ooh, black and yellow! Let's shake it up a little.",
"Whaaa? You came to see me again? That makes Beedle SO HAPPY!",
"Don't get cooked, stay off the hook!",
"Now with 100% more good vibes in the splash code!",
"It is Wednesday my dudes!"
],
"es_ES": [],
"fr_FR": [],
"he_IL": [],
"it_IT": [],
"ja_JP": [],
"ko_KR": [],
"no_NO": [],
"pl_PL": [],
"pt_BR": [],
"ru_RU": [],
"sv_SE": [],
"th_TH": [],
"tr_TR": [],
"uk_UA": [],
"zh_CN": [],
"zh_TW": []
}
}

View File

@@ -0,0 +1,64 @@
using System.Collections.Generic;
using Ryujinx.Common.Logging;
using Gommon;
using Ryujinx.Ava.Systems.Configuration;
using System;
using System.Text.Json;
namespace Ryujinx.Common
{
public class SplashTextHelper
{
public static void PrintSplash()
{
Logger.Notice.Print(LogClass.Application, " ___ __ _ ");
Logger.Notice.Print(LogClass.Application, @" / _ \ __ __ __ __ / / (_) ___ ___ _");
Logger.Notice.Print(LogClass.Application, @" / , _/ / // // // / / _ \ / / / _ \ / _ `/");
Logger.Notice.Print(LogClass.Application, @"/_/|_| \_, / \_,_/ /_.__//_/ /_//_/ \_, / ");
Logger.Notice.Print(LogClass.Application, " /___/ /___/ ");
Logger.Notice.Print(LogClass.Application, "");
Logger.Notice.Print(LogClass.Application, GetSplash());
Logger.Notice.Print(LogClass.Application, "");
}
private static string s_finalSplash = "";
public static string GetSplash()
{
if (string.IsNullOrEmpty(s_finalSplash))
{
s_finalSplash = GetLangJson();
if (string.IsNullOrEmpty(s_finalSplash))
{
s_finalSplash = "Splash Text";
}
}
return $"{s_finalSplash}";
}
private static SplashLocales s_splashJson;
private static string GetLangJson()
{
try
{
string data;
data = EmbeddedResources.ReadAllText("Ryujinx/Assets/Splashes.json");
s_splashJson = JsonSerializer.Deserialize<SplashLocales>(data);
return s_splashJson.Locales[ConfigurationState.Instance.UI.LanguageCode.Value].GetRandomElement();
}
catch
{
return "";
}
}
private struct SplashLocales
{
public Dictionary<string, List<string>> Locales { get; set; }
}
}
}

View File

@@ -437,13 +437,9 @@ namespace Ryujinx.Ava
internal static void PrintSystemInfo()
{
Logger.Notice.Print(LogClass.Application, " ___ __ _ ");
Logger.Notice.Print(LogClass.Application, @" / _ \ __ __ __ __ / / (_) ___ ___ _");
Logger.Notice.Print(LogClass.Application, @" / , _/ / // // // / / _ \ / / / _ \ / _ `/");
Logger.Notice.Print(LogClass.Application, @"/_/|_| \_, / \_,_/ /_.__//_/ /_//_/ \_, / ");
Logger.Notice.Print(LogClass.Application, " /___/ /___/ ");
// Print the ryubing logo + joke splash
SplashTextHelper.PrintSplash();
Logger.Notice.Print(LogClass.Application, $"{RyujinxApp.FullAppName} Version: {Version}");
Logger.Notice.Print(LogClass.Application, $".NET Runtime: {RuntimeInformation.FrameworkDescription}");
SystemInfo.Gather().Print();

View File

@@ -175,6 +175,8 @@
<EmbeddedResource Include="Assets\UIImages\Logo_Ryujinx.png" />
<EmbeddedResource Include="Assets\UIImages\Logo_Forgejo.png" />
<EmbeddedResource Include="Assets\UIImages\Logo_Ryujinx_AntiAlias.png" />
<EmbeddedResource Include="Assets\PlayReports\*.json" />
<EmbeddedResource Include="Assets\Splashes.json" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\assets\Locales\*.json" />

View File

@@ -336,7 +336,7 @@ namespace Ryujinx.Ava.Systems.Configuration
EnableRumble = false,
StrongRumble = 1f,
WeakRumble = 1f,
UseHDRumble = true
UseHDRumble = false
};
}
}

View File

@@ -1,10 +1,12 @@
using Gommon;
using Humanizer;
using MsgPack;
using Ryujinx.Common;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace Ryujinx.Ava.Systems.PlayReport
{
@@ -1116,5 +1118,87 @@ namespace Ryujinx.Ava.Systems.PlayReport
_ => "Wandering"
};
}
private static FormattedValue NsmbudRpc(SparseMultiValue values)
{
if (values.Matched.TryGetValue("WorldNo", out Value world) && values.Matched.TryGetValue("CourseNo", out Value course) | values.Matched.TryGetValue("GameModeType", out Value gamemode))
{
string worldstr = world.ToString();
string coursestr = course.ToString();
int courseint = Int32.Parse(coursestr);
string gamemodestr = gamemode.ToString();
try
{
Dictionary<string, Dictionary<string, Dictionary<string, string>>> output;
string data;
data = EmbeddedResources.ReadAllText("Ryujinx/Assets/PlayReports/nsmbud.json");
output = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, string>>>>(data);
if (SpecialMapNames(courseint) == "Hazard")
{
return $"Last Played: Course {worldstr}-Hazard";
}
string outputloc = output[MarioOrLuigiGamemode(gamemodestr)][worldstr][coursestr];
return $"Last Played: Course {worldstr}-{SpecialMapNames(courseint)} | {outputloc}";
}
catch
{
return FormattedValue.ForceReset;
}
}
if (values.Matched.TryGetValue("RlId", out Value RlId) | values.Matched.TryGetValue("TotalPlayTime", out Value TotalPlayTime))
{
return "At the main menu";
}
static string MarioOrLuigiGamemode(string? gamemode) => gamemode switch
{
"0" => "mario",
"1" => "luigi",
"4" => "mario",
"5" => "mario",
_ => gamemode
};
static string OtherGameMode(string? gamemode) => gamemode switch
{
"2" => "Boost Rush",
"3" => "Challenges",
"4" => "Coin Battle",
"5" => "Coin Battle Editor",
_ => ""
};
static string SpecialMapNames(int? course) => course switch
{
>= 1 and <= 9 => course.ToString(),
13 => "Shortcut",
14 => "Shortcut",
15 => "Shortcut",
16 => "Shortcut",
17 => "Shortcut",
20 => "Ghost",
21 => "Tower",
22 => "Tower",
23 => "Castle",
37 => "Airship",
42 => "Castle",
43 => "Castle",
_ => "Hazard"
};
// For future reference
// Tower course = 21, Castle course = 23,Haunted Mansion/ship = 20
// Tower course 2 (rock candy) = 22
// Peach castle 1 = 42, Peach final battle = 43
// airship = 37, jungle beetles = 17
// Glacier seals = 16, water leaf = 15
// desert ice = 14, acorn squid = 13
// all other course numbers are to be considered a hazard
return "";
}
}
}

View File

@@ -138,6 +138,12 @@ namespace Ryujinx.Ava.Systems.PlayReport
.WithDescription("based on gold count, report info only in the mii selector, and gamestage (progression)")
.AddSparseMultiValueFormatter(["gold", "secret", "stage"], MiitopiaRPC)
)
.AddSpec(
"0100ea80032ea000", // New Super Mario Bros U Deluxe
spec => spec
.WithDescription("based on world map return info.")
.AddSparseMultiValueFormatter(["WorldNo", "CourseNo", "RlId", "TotalPlayTime", "GameModeType"], NsmbudRpc)
)
);
private static string Playing(string game) => $"Playing {game}";

View File

@@ -76,6 +76,8 @@ namespace Ryujinx.Ava.UI.ViewModels
[ObservableProperty] public partial string LoadHeading { get; set; }
[ObservableProperty] public partial string CacheLoadStatus { get; set; }
[ObservableProperty] public partial string Splash { get; set; }
[ObservableProperty] public partial string DockedStatusText { get; set; }
@@ -1256,6 +1258,7 @@ namespace Ryujinx.Ava.UI.ViewModels
break;
case ShaderCacheLoadingState shaderCacheState:
CacheLoadStatus = $"{current} / {total}";
Splash = $"\"{SplashTextHelper.GetSplash()}\"";
switch (shaderCacheState)
{
case ShaderCacheLoadingState.Start:

View File

@@ -135,7 +135,7 @@
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsVisible="{Binding ShowLoadProgress}" RowDefinitions="Auto,Auto,Auto">
IsVisible="{Binding ShowLoadProgress}" RowDefinitions="Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="0"
Margin="10"
@@ -179,6 +179,16 @@
Text="{Binding CacheLoadStatus}"
TextAlignment="Start"
MaxWidth="500" />
<TextBlock
Grid.Row="3"
Margin="10"
FontSize="14"
FontStyle="Oblique"
IsVisible="{Binding ShowLoadProgress}"
Text="{Binding Splash}"
Foreground="LightGray"
TextAlignment="Start"
MaxWidth="500" />
</Grid>
</Grid>
</Grid>