mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
Radicade Public Code
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user