Radicade Public Code

This commit is contained in:
JogmansonReal
2026-10-16 23:33:13 -07:00
commit 55175acdf6
832 changed files with 363261 additions and 0 deletions
@@ -0,0 +1,200 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using TMPro;
using UnityEngine.UI;
internal static class ClientTextRewrites
{
private static readonly (string From, string To)[] Rules =
{
("https://recroom.zendesk.com/home", "https://support.radicade.app/"),
("recroom.zendesk.com", "support.radicade.app"),
("https://www.recroom.com/privacy-policy/", "https://radicade.app/privacy-policy/"),
("https://recroom.com/terms-of-service", "https://radicade.app/terms-of-service/"),
("https://www.recroom.com/community/", "https://radicade.app/community/"),
};
private static readonly (string From, string To)[] ExactRules =
{
("This account has been locked for security reasons. Contact us at recroom.zendesk.com.",
LockedAccountSentence),
("This account has been locked for security reasons. Contact us at support.radicade.app.",
LockedAccountSentence),
};
private const string GenericLoginFailure = "Login failed. Please try again.";
private const string LockedAccountErrorCode = "ACCOUNT_PERMALOCKED";
internal const string LockedAccountSentence =
"This account has been locked for security reasons. Contact us at https://support.radicade.app.";
private static bool _rewriting;
internal static void Apply(Harmony harmony)
{
var mode = AsciiLower((Environment.GetEnvironmentVariable("RADICADE_CLIENT_TEXT") ?? string.Empty).Trim());
if (mode == "0" || mode == "off")
{
return;
}
bool onEnable = mode.Length == 0 || mode == "1" || mode == "all" || mode == "onenable";
bool onSet = mode.Length == 0 || mode == "1" || mode == "all" || mode == "settext";
var seen = new HashSet<MethodBase>();
var names = new List<string>();
if (onEnable)
{
Hook(harmony, seen, names, AccessTools.Method(typeof(TextMeshProUGUI), "OnEnable"), nameof(GraphicEvent), "TextMeshProUGUI.OnEnable");
Hook(harmony, seen, names, AccessTools.Method(typeof(Text), "OnEnable"), nameof(GraphicEvent), "UI.Text.OnEnable");
}
if (onSet)
{
Hook(harmony, seen, names, AccessTools.Method(typeof(TMP_Text), "set_text"), nameof(GraphicEvent), "TMP_Text.set_text");
Hook(harmony, seen, names, AccessTools.Method(typeof(TMP_Text), "SetTextInternal"), nameof(GraphicEvent), "TMP_Text.SetTextInternal");
Hook(harmony, seen, names, AccessTools.Method(typeof(Text), "set_text"), nameof(GraphicEvent), "UI.Text.set_text");
}
if (names.Count == 0)
RadicadeEacBypassPlugin.LogSource.LogWarning("[ClientText] no text hooks could be installed (mode=" + (mode.Length == 0 ? "all" : mode) + "); the watch UI keeps its original text.");
}
private static void Hook(Harmony harmony, HashSet<MethodBase> seen, List<string> names,
MethodBase target, string handler, string label)
{
if (target == null)
{
RadicadeEacBypassPlugin.LogSource.LogWarning("[ClientText] hook target missing: " + label);
return;
}
if (!seen.Add(target)) return;
try
{
var method = new HarmonyMethod(typeof(ClientTextRewrites)
.GetMethod(handler, BindingFlags.Static | BindingFlags.NonPublic));
harmony.Patch(target, postfix: method);
names.Add(label);
}
catch (Exception ex)
{
RadicadeEacBypassPlugin.LogSource.LogWarning("[ClientText] could not hook " + label + ": "
+ ex.GetType().Name + ": " + ex.Message);
}
}
private static void GraphicEvent(object __instance)
{
try
{
if (__instance is not Il2CppObjectBase obj) return;
if (obj.TryCast<TMP_Text>() is { } tmp) { Fix(tmp); return; }
if (obj.TryCast<Text>() is { } txt) { Fix(txt); }
}
catch { }
}
private static void Fix(TMP_Text instance)
{
try
{
if (instance == null || _rewriting) return;
var current = instance.text;
var rewritten = Rewrite(current);
if (ReferenceEquals(current, rewritten) || string.Equals(rewritten, current, StringComparison.Ordinal)) return;
_rewriting = true;
try { instance.text = rewritten; } finally { _rewriting = false; }
}
catch { }
}
private static void Fix(Text instance)
{
try
{
if (instance == null || _rewriting) return;
var current = instance.text;
var rewritten = Rewrite(current);
if (ReferenceEquals(current, rewritten) || string.Equals(rewritten, current, StringComparison.Ordinal)) return;
_rewriting = true;
try { instance.text = rewritten; } finally { _rewriting = false; }
}
catch { }
}
private static string Rewrite(string value)
{
if (string.IsNullOrEmpty(value)) return value;
if (HubSocketAuth.LastLoginRefusalCode == LockedAccountErrorCode &&
IndexOfAsciiIgnoreCase(value, GenericLoginFailure, 0) >= 0)
{
var locked = Replace(value, GenericLoginFailure, LockedAccountSentence);
return locked;
}
if (IndexOfAsciiIgnoreCase(value, "recroom", 0) < 0 &&
IndexOfAsciiIgnoreCase(value, "radicade", 0) < 0) return value;
var result = value;
foreach (var (from, to) in ExactRules)
if (IndexOfAsciiIgnoreCase(result, from, 0) >= 0)
result = Replace(result, from, to);
foreach (var (from, to) in Rules)
if (IndexOfAsciiIgnoreCase(result, from, 0) >= 0)
result = Replace(result, from, to);
return result;
}
private static string Replace(string source, string from, string to)
{
var sb = new System.Text.StringBuilder(source.Length);
var i = 0;
while (i < source.Length)
{
var hit = IndexOfAsciiIgnoreCase(source, from, i);
if (hit < 0) { sb.Append(source, i, source.Length - i); break; }
sb.Append(source, i, hit - i).Append(to);
i = hit + from.Length;
}
return sb.ToString();
}
private static int IndexOfAsciiIgnoreCase(string haystack, string needle, int start)
{
if (haystack == null || needle == null) return -1;
var n = needle.Length;
var h = haystack.Length;
if (n == 0) return start <= h ? start : -1;
if (n > h) return -1;
var last = h - n;
for (var i = (start < 0 ? 0 : start); i <= last; i++)
{
var j = 0;
while (j < n && LowerAscii(haystack[i + j]) == LowerAscii(needle[j])) j++;
if (j == n) return i;
}
return -1;
}
private static char LowerAscii(char c) => c >= 'A' && c <= 'Z' ? (char)(c + 32) : c;
private static string AsciiLower(string s)
{
for (var i = 0; i < s.Length; i++)
if (s[i] >= 'A' && s[i] <= 'Z')
return string.Create(s.Length, s, (span, state) =>
{
for (var k = 0; k < state.Length; k++) span[k] = LowerAscii(state[k]);
});
return s;
}
}