diff --git a/code/client/clrcore-v2/Client/RedM/UiFeed.cs b/code/client/clrcore-v2/Client/RedM/UiFeed.cs new file mode 100644 index 0000000000..e89f0243dd --- /dev/null +++ b/code/client/clrcore-v2/Client/RedM/UiFeed.cs @@ -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(nativeHash, &dur, ¬ification, 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(nativeHash, &dur, ¬ification, bUnk1); + } + return retVal; + } + + /// + /// Places the first varString onto the kill feed. + /// + /// Returns a feedId that can be used by to remove the feed + public static int FeedTicker(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_FEED_TICKER, feedInfo, feedData); + } + + /// + /// + /// Returns a feedId that can be used by to remove the feed + public static int GameUpdateShard(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_GAME_UPDATE_SHARD, feedInfo, feedData); + } + + /// + /// Places the first varString as a simple notification on the top left of the screen. + /// + /// Returns a feedId that can be used by to remove the feed + public static int HelpText(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_HELP_TEXT, feedInfo, feedData); + } + + /// + /// Displays the first varString as a location in the bottom left of the screen + /// + /// Returns a feedId that can be used by to remove the feed + public static int MissionName(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_MISSION_NAME, feedInfo, feedData); + } + + /// + /// Displays the first varString as a subtitle in the bottom of the screen + /// + /// Returns a feedId that can be used by to remove the feed + public static int Objective(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_OBJECTIVE, feedInfo, feedData); + } + + /// + /// + /// Returns a feedId that can be used by to remove the feed + public static int OneTextShard(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_ONE_TEXT_SHARD, feedInfo, feedData); + } + + /// + /// Displays the as a + /// + /// Returns a feedId that can be used by to remove the feed + public static int RankupToast(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_RANKUP_TOAST, feedInfo, feedData); + } + + /// + /// Displays the first as a message at the center of the screen. + /// + /// Returns a feedId that can be used by to remove the feed + public static int ReticleMessage(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_RETICLE_MESSAGE, feedInfo, feedData); + } + + /// + /// Displays the as a notification in the top left. + /// This notification is similar to but it will not display the color, and + /// varString2 will disappear before the notification finishes. + /// + /// Returns a feedId that can be used by to remove the feed + public static int SampleNotification(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed4Args(Hash._UI_FEED_POST_SAMPLE_NOTIFICATION, feedInfo, feedData); + } + + + /// + /// Displays the as a notification in the top left with the specified icon and color. + /// + /// Returns a feedId that can be used by to remove the feed + public static int SampleToast(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed4Args(Hash._UI_FEED_POST_SAMPLE_TOAST, feedInfo, feedData); + } + + /// + /// Displays the as a simple notification on the right above the kill feed. + /// This will use whatever is in s icon field as the color + /// + /// Returns a feedId that can be used by to remove the feed + public static int SampleToastRight(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_SAMPLE_TOAST_RIGHT, feedInfo, feedData); + } + + /// + /// + /// Returns a feedId that can be used by to remove the feed + public static int ToastWithAppLink(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_SAMPLE_TOAST_WITH_APP_LINK, feedInfo, feedData); + } + + /// + /// + /// Returns a feedId that can be used by to remove the feed + public static int ThreeTextShard(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed4Args(Hash._UI_FEED_POST_THREE_TEXT_SHARD, feedInfo, feedData); + } + + + /// + /// + /// Returns a feedId that can be used by to remove the feed + public static int TwoTextShard(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed4Args(Hash._UI_FEED_POST_TWO_TEXT_SHARD, feedInfo, feedData); + } + + /// + /// Displays the as a microphone notification on the right side of the screen + /// + /// Returns a feedId that can be used by to remove the feed + public static int VoiceChatFeed(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed3Args(Hash._UI_FEED_POST_VOICE_CHAT_FEED, feedInfo, feedData); + } + + + /// + /// Displays the as a microphone notification on the right side of the screen + /// + /// Returns a feedId that can be used by to remove the feed + public static int LocationShard(UiFeedInfo feedInfo, UiFeedData feedData) + { + return GenerateFeed4Args(Hash._UI_FEED_POST_LOCATION_SHARD, feedInfo, feedData, false, true); + } + + /// + /// Removes the specified from the feed. + /// + public static void ClearFeedByFeedId(int feedId) + { + Natives.UiFeedClearHelpTextFeed(feedId, false); + } + } +} diff --git a/code/client/clrcore-v2/Client/RedM/UiFeedStructs.cs b/code/client/clrcore-v2/Client/RedM/UiFeedStructs.cs new file mode 100644 index 0000000000..98e663a657 --- /dev/null +++ b/code/client/clrcore-v2/Client/RedM/UiFeedStructs.cs @@ -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(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; + } + } +}