Skip to content

Commit

Permalink
feat(rdr): add UiFeed structs & native usages
Browse files Browse the repository at this point in the history
  • Loading branch information
AvarianKnight committed Jun 11, 2024
1 parent 72755cb commit 314ecfe
Show file tree
Hide file tree
Showing 2 changed files with 350 additions and 0 deletions.
189 changes: 189 additions & 0 deletions code/client/clrcore-v2/Client/RedM/UiFeed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
using System.Security;
using CitizenFX.RedM.Native;

namespace CitizenFX.RedM
{
public static class UiFeed
{
// a lot of feed natives reuse the same general structure with just different argument count so we just define
// these two and reuse them
[SecuritySafeCritical]
private static int GenerateFeed4Args(Hash nativeHash, UiFeedInfo feedInfo, UiFeedData feedData, bool bUnk1 = true, bool bUnk2 = true)
{
int retVal;
unsafe
{
UnsafeUiInfo dur = new UnsafeUiInfo(feedInfo);
UnsafeUiFeedNotification notification = new UnsafeUiFeedNotification(feedData);
retVal = Natives.Call<int>(nativeHash, &dur, &notification, bUnk1, bUnk2);
}
return retVal;
}

[SecuritySafeCritical]
private static int GenerateFeed3Args(Hash nativeHash, UiFeedInfo feedInfo, UiFeedData feedData, bool bUnk1 = true)
{
int retVal;
unsafe
{
UnsafeUiInfo dur = new UnsafeUiInfo(feedInfo);
UnsafeUiFeedNotification notification = new UnsafeUiFeedNotification(feedData);
retVal = Natives.Call<int>(nativeHash, &dur, &notification, bUnk1);
}
return retVal;
}

/// <summary>
/// Places the first <paramref name="feedData" /> varString onto the kill feed.
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int FeedTicker(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_FEED_TICKER, feedInfo, feedData);
}

/// <summary>
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int GameUpdateShard(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_GAME_UPDATE_SHARD, feedInfo, feedData);
}

/// <summary>
/// Places the first <paramref name="feedData" /> varString as a simple notification on the top left of the screen.
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int HelpText(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_HELP_TEXT, feedInfo, feedData);
}

/// <summary>
/// Displays the first <paramref name="feedData" /> varString as a location in the bottom left of the screen
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int MissionName(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_MISSION_NAME, feedInfo, feedData);
}

/// <summary>
/// Displays the first <paramref name="feedData" /> varString as a subtitle in the bottom of the screen
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int Objective(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_OBJECTIVE, feedInfo, feedData);
}

/// <summary>
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int OneTextShard(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_ONE_TEXT_SHARD, feedInfo, feedData);
}

/// <summary>
/// Displays the <paramref name="feedData" /> as a <see cref="UiFeed.SampleToast" />
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int RankupToast(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_RANKUP_TOAST, feedInfo, feedData);
}

/// <summary>
/// Displays the first <paramref name="feedData" /> as a message at the center of the screen.
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int ReticleMessage(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_RETICLE_MESSAGE, feedInfo, feedData);
}

/// <summary>
/// Displays the <paramref name="feedInfo"/> as a notification in the top left.
/// This notification is similar to <see cref="UiFeed.SampleToast" /> but it will not display the color, and
/// varString2 will disappear before the notification finishes.
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int SampleNotification(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed4Args(Hash._UI_FEED_POST_SAMPLE_NOTIFICATION, feedInfo, feedData);
}


/// <summary>
/// Displays the <paramref name="feedInfo" /> as a notification in the top left with the specified icon and color.
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int SampleToast(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed4Args(Hash._UI_FEED_POST_SAMPLE_TOAST, feedInfo, feedData);
}

/// <summary>
/// Displays the <paramref name="feedInfo" /> as a simple notification on the right above the kill feed.
/// This will use whatever is in <see cref="UiFeed" />s icon field as the color
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int SampleToastRight(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_SAMPLE_TOAST_RIGHT, feedInfo, feedData);
}

/// <summary>
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int ToastWithAppLink(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_SAMPLE_TOAST_WITH_APP_LINK, feedInfo, feedData);
}

/// <summary>
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int ThreeTextShard(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed4Args(Hash._UI_FEED_POST_THREE_TEXT_SHARD, feedInfo, feedData);
}


/// <summary>
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int TwoTextShard(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed4Args(Hash._UI_FEED_POST_TWO_TEXT_SHARD, feedInfo, feedData);
}

/// <summary>
/// Displays the <paramref name="feedInfo" /> as a microphone notification on the right side of the screen
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int VoiceChatFeed(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed3Args(Hash._UI_FEED_POST_VOICE_CHAT_FEED, feedInfo, feedData);
}


/// <summary>
/// Displays the <paramref name="feedInfo" /> as a microphone notification on the right side of the screen
/// </summary>
/// <returns>Returns a feedId that can be used by <see cref="UiFeed.ClearFeedByFeedId"/> to remove the feed</returns>
public static int LocationShard(UiFeedInfo feedInfo, UiFeedData feedData)
{
return GenerateFeed4Args(Hash._UI_FEED_POST_LOCATION_SHARD, feedInfo, feedData, false, true);
}

/// <summary>
/// Removes the specified <paramref name="feedId" /> from the feed.
/// </summary>
public static void ClearFeedByFeedId(int feedId)
{
Natives.UiFeedClearHelpTextFeed(feedId, false);
}
}
}
161 changes: 161 additions & 0 deletions code/client/clrcore-v2/Client/RedM/UiFeedStructs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
using CitizenFX.Core;
using CitizenFX.Core.Native;
using CitizenFX.RedM.Native;

namespace CitizenFX.RedM
{
public struct UiFeedInfo {
internal Int64 m_duration;
internal IntPtr m_varString1;
internal IntPtr m_varString2;

public UiFeedInfo(Int64 dur) {
m_duration = dur;
m_varString1 = IntPtr.Zero;
m_varString2 = IntPtr.Zero;
}

public UiFeedInfo(Int64 dur, VarString varString1, VarString? varString2)
{
m_duration = dur;
m_varString1 = varString1.m_varStringPtr;
m_varString2 = varString2?.m_varStringPtr ?? IntPtr.Zero;
}
};

[StructLayout(LayoutKind.Explicit, Size = 0x68)]
[SecuritySafeCritical]
internal unsafe struct UnsafeUiInfo
{
[FieldOffset(0x00)] private Int64 m_duration;

[FieldOffset(0x08)] private IntPtr m_unk1;

[FieldOffset(0x10)] private IntPtr m_unk2;

[FieldOffset(0x18)] private Int64 m_unk3;

[FieldOffset(0x20)] private Int64 m_unk4;

[FieldOffset(0x28)] private Int64 m_unk5;

[FieldOffset(0x30)] private IntPtr m_unk6;

[FieldOffset(0x38)] private Int64 m_unk7;

[FieldOffset(0x40)] private Int64 m_unk8;

[FieldOffset(0x48)] private Int64 m_unk9;

[FieldOffset(0x50)] private Int64 m_unk10;

[FieldOffset(0x58)] private Int64 m_unk11;

[FieldOffset(0x60)] private Int64 m_unk12;

public UnsafeUiInfo(UiFeedInfo feedInfo) {
m_duration = feedInfo.m_duration;
m_unk1 = IntPtr.Zero;
m_unk2 = IntPtr.Zero;
m_unk3 = 0;
m_unk4 = 0;
m_unk5 = 0;
m_unk6 = IntPtr.Zero;
m_unk7 = 0;
m_unk8 = 0;
m_unk9 = 0;
m_unk10 = 0;
m_unk11 = 0;
m_unk12 = 0;
}
}

public struct ColorString
{
internal CString m_colorString;
public ColorString(int rgb)
{
m_colorString = Natives.CreateColorString(rgb);
}
}

public struct VarString
{
internal IntPtr m_varStringPtr;
public VarString(params Argument[] arguments)
{
m_varStringPtr = Natives.Call<IntPtr>(Hash.VAR_STRING, arguments);
}
}

public struct UiFeedData
{
internal VarString m_varString1;
internal CString m_string2;
internal VarString? m_varString2;
internal uint m_iconDict;
internal uint m_icon;
internal uint m_color;

public UiFeedData(VarString varStr1, VarString? varStr2, string iconDict = null, string icon = null, string color = null)
{
m_varString1 = varStr1;
m_string2 = null;
m_varString2 = varStr2;
m_iconDict = Game.GenerateHash(iconDict);
m_icon = Game.GenerateHash(icon);
m_color = Game.GenerateHash(color);
}

public UiFeedData(VarString varStr1, CString str, string iconDict = null, string icon = null, string color = null)
{
m_varString1 = varStr1;
m_string2 = str;
m_varString2 = null;
m_iconDict = Game.GenerateHash(iconDict);
m_icon = Game.GenerateHash(icon);
m_color = Game.GenerateHash(color);
}
}


[StructLayout(LayoutKind.Explicit, Size = 0x40)]
[SecuritySafeCritical]
internal unsafe struct UnsafeUiFeedNotification
{
[FieldOffset(0x00)] Int64 unk1;

[FieldOffset(0x08)] char* varStringPtr1;

[FieldOffset(0x10)] char* varStringPtr2;

[FieldOffset(0x18)] Int64 unk2;

[FieldOffset(0x20)] UInt64 iconDictHash;

[FieldOffset(0x28)] UInt64 iconHash;

[FieldOffset(0x30)] UInt64 colorHash;

[FieldOffset(0x38)] Int64 unk3;

public UnsafeUiFeedNotification(UiFeedData fd) {
unk1 = 0;
varStringPtr1 = (char*)fd.m_varString1.m_varStringPtr;
if (fd.m_string2 != null) {
var str = fd.m_string2.GetPinnableReference();
varStringPtr2 = (char*)str;
} else {
varStringPtr2 = (char*)(fd.m_varString2?.m_varStringPtr ?? IntPtr.Zero);
}
unk2 = 0;
iconDictHash = fd.m_iconDict;
iconHash = fd.m_icon;
colorHash = fd.m_color;
unk3 = 0;
}
}
}

0 comments on commit 314ecfe

Please sign in to comment.