using Harmony; using Il2Cpp; using Il2CppTMPro; using MelonLoader; using MelonLoader.TinyJSON; using Newtonsoft.Json; using PvZ_Fusion_Translator.AssetStore; using PvZ_Fusion_Translator.Patches.GameObjects; using PvZ_Fusion_Translator.Patches.GameObjects.MinorObjects; using PvZ_Fusion_Translator.Patches.Modes.Super_Editor; using System.Diagnostics; using System.Text.RegularExpressions; using Unity.Burst.Intrinsics; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.UI; using static Il2Cpp.AlmanacPlantBank; using static MelonLoader.MelonLogger; using static PvZ_Fusion_Translator.FileLoader; namespace PvZ_Fusion_Translator { public class Utils { internal static bool TryReplaceTexture2D(Texture2D ogTexture) { if (ogTexture != null) { if (TextureStore.textureDict.TryGetValue(ogTexture.name, out string texturePath)) { try { ImageConversion.LoadImage(ogTexture, File.ReadAllBytes(texturePath)); Log.LogDebug("OK! Replaced Texture " + ogTexture.name); ogTexture.name = "replaced_" + ogTexture.name; return true; } catch (Exception ex) { Log.LogError("Failed to replace texture: " + ogTexture.name + " at path: " + texturePath); Log.LogError(ex.ToString()); } } } return false; } internal static Texture2D LoadImage(string path) { if (!File.Exists(path)) { throw new FileNotFoundException($"The image file at path '{path}' does not exist."); } byte[] array = File.ReadAllBytes(path); Texture2D texture2D = new(2, 2, GraphicsFormat.R8G8B8A8_UNorm, TextureCreationFlags.None, 1, IntPtr.Zero, null); ImageConversion.LoadImage(texture2D, array); return texture2D; } public static Color HexToColor(string hex) { hex = hex.Replace("#", ""); float r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber) / 255f; float g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber) / 255f; float b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber) / 255f; return new Color(r, g, b); } public static void OpenSaveDirectory() { string saveDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "LanPiaoPiao", "PlantsVsZombiesRH"); Process.Start("explorer.exe", saveDirectory); } public static void OpenTrello() { string website = "https://trello.com/b/DcdT1kUp"; Process.Start(new ProcessStartInfo(website) { UseShellExecute = true }); } public static string RemoveColorTags(string text) { if (string.IsNullOrEmpty(text)) return text ?? string.Empty; // Remove opening color tags like string withoutOpenTags = Regex.Replace(text, @"]+>", string.Empty, RegexOptions.IgnoreCase); // Remove closing color tags like string withoutCloseTags = Regex.Replace(withoutOpenTags, @"", string.Empty, RegexOptions.IgnoreCase); return withoutCloseTags; } public static string RemoveSizeTags(string text) { if (string.IsNullOrEmpty(text)) return text ?? string.Empty; // Remove opening color tags like string withoutOpenTags = Regex.Replace(text, @"]+>", string.Empty, RegexOptions.IgnoreCase); // Remove closing color tags like string withoutCloseTags = Regex.Replace(withoutOpenTags, @"", string.Empty, RegexOptions.IgnoreCase); return withoutCloseTags; } public static GameObject ConvertToTextMeshProUGUI(GameObject originalText, Transform parent, string name) { GameObject newObj = new GameObject(name); newObj.transform.position = originalText.transform.position; newObj.AddComponent(); newObj.AddComponent(); newObj.AddComponent(); newObj.transform.SetParent(parent); newObj.transform.localScale = Vector3.one; UnityEngine.Object.Destroy(originalText); return newObj; } public static void ConvertButtonText(Transform original, string name) { TMP_FontAsset fontAsset = FontStore.LoadTMPFont(Utils.Language.ToString()); Transform transform = original.FindChild(name); Transform textTransform = transform.FindChild("text"); string text = textTransform.GetComponent().text; Color color = textTransform.GetComponent().color; TextMeshProUGUI newGoBackText = Utils.ConvertToTextMeshProUGUI(textTransform.gameObject, transform, "text2").GetComponent(); newGoBackText.autoSizeTextContainer = true; newGoBackText.text = StringStore.TranslateText(text, false); newGoBackText.font = fontAsset; newGoBackText.color = color; } public static string GetPlantNameFromAlmanac(PlantType thePlantType) { string json; string thePlantName = ""; json = AlmanacPlantMenu_Patch.almanacJson; AlmanacPlantBank.PlantData plantData = JsonUtility.FromJson(json); foreach (AlmanacPlantBank.PlantInfo plantInfo in plantData.plants) { if (plantInfo.seedType == (int)thePlantType) { thePlantName = plantInfo.name; } } return thePlantName; } public static string GetPlantNameFromAlmanac(string theOriginalPlantName, bool log = false) { string originalJson; string translatedJson; string thePlantName = ""; string currentLanguage = Utils.Language.ToString(); string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language); string dumpDir = GetAssetDir(AssetType.Dumps); string originalPath = Path.Combine(dumpDir, "LawnStrings.json"); string path = Path.Combine(almanacDir, "LawnStringsTranslate.json"); if ((!File.Exists(originalPath))) { if(log) { Log.LogError($"LawnStringsTranslate.json file not found at path: {path}"); Log.LogError("Plant name could not be found!"); } thePlantName = ""; } else { bool foundPlantName = false; originalJson = File.ReadAllText(originalPath); translatedJson = AlmanacPlantMenu_Patch.almanacJson; AlmanacPlantBank.PlantData originalPlantData = JsonUtility.FromJson(originalJson); AlmanacPlantBank.PlantData translatedPlantData = JsonUtility.FromJson(translatedJson); for(int i = 0; i < originalPlantData.plants.Count; i++) { AlmanacPlantBank.PlantInfo originalPlantInfo = originalPlantData.plants[i]; KeyValuePair translatedPlantInfo = plantIndices[originalPlantInfo.seedType]; if (originalPlantInfo.name == theOriginalPlantName) { thePlantName = translatedPlantInfo.Value; foundPlantName = true; } } if (!foundPlantName) { if(log) { Log.LogInfo("Couldn't find plant name!"); } thePlantName = ""; } } return thePlantName; } public static string GetZombieNameFromAlmanac(ZombieType theZombieType) { string json; string theZombieName = ""; string currentLanguage = Utils.Language.ToString(); json = AlmanacZombieMenu_Patch.almanacJson; ZombieAlmanacData zombieData = JsonUtility.FromJson(json); foreach (ZombieInfo zombieInfo in zombieData.zombies) { if ((int)zombieInfo.theZombieType == (int)theZombieType) { theZombieName = zombieInfo.name; } } return theZombieName; } public static Dictionary> plantIndices = new Dictionary>(); public static void RegisterPlantIndices() { plantIndices = new Dictionary>(); string originalJson; string translatedJson; string currentLanguage = Utils.Language.ToString(); string almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language); string dumpDir = GetAssetDir(AssetType.Dumps); string originalPath = Path.Combine(dumpDir, "LawnStrings.json"); string path = Path.Combine(almanacDir, "LawnStringsTranslate.json"); if ((!File.Exists(originalPath))) { Log.LogError($"LawnStrings.json file not found at path: {originalPath}"); } else { originalJson = File.ReadAllText(originalPath); translatedJson = AlmanacPlantMenu_Patch.almanacJson; AlmanacPlantBank.PlantData originalPlantData = JsonUtility.FromJson(originalJson); AlmanacPlantBank.PlantData translatedPlantData = JsonUtility.FromJson(translatedJson); for (int i = 0; i < originalPlantData.plants.Count; i++) { PlantInfo originalPlantInfo = originalPlantData.plants[i]; PlantInfo translatedPlantInfo = null; foreach (PlantInfo info in translatedPlantData.plants) { if (info.seedType == originalPlantInfo.seedType) { translatedPlantInfo = info; } } if (translatedPlantInfo != null) { KeyValuePair temp = new KeyValuePair(translatedPlantInfo.seedType, translatedPlantInfo.name); plantIndices.Add(originalPlantInfo.seedType, temp); } } } } /*public static Dictionary> recipeLinks = new Dictionary>(); public static void RegisterRecipeLinks() { recipeLinks.Clear(); Il2CppSystem.Array mixData = MixData.data.Cast(); int rows = mixData.GetLength(0); int columns = mixData.GetLength(1); foreach (PlantType plantType in Enum.GetValues(typeof(PlantType))) { int seedType = (int)plantType; recipeLinks.Add(seedType, new()); if (seedType > 0) { foreach (PlantType checkType in Enum.GetValues(typeof(PlantType))) { if (checkType > 0) { int[] checks = [mixData.GetValue(seedType, (int)checkType).Unbox(), mixData.GetValue((int)checkType, seedType).Unbox()]; if (checks[0] != 0) { recipeLinks[seedType].Add(checks[0]); } if (checks[1] != 0) { recipeLinks[seedType].Add(checks[1]); } } } } if (recipeLinks[seedType].Count > 0) { Log.LogDebug($"Found {recipeLinks[seedType].Count} fusions for {seedType}!"); } } }*/ public static bool CheckForUntranslatedText(string text) { Regex regex = new("\\p{IsCJKUnifiedIdeographs}+"); try { Match match = regex.Match(text); return match.Success; } catch(Exception e) { Log.LogError(e); } return false; } public static bool useLocal = MelonPreferences.GetEntryValue("PvZ_Fusion_Translator", "UseLocal"); public static void SwapLocalData() { Utils.useLocal = (Utils.useLocal) ? false : true; MelonPreferences.SetEntryValue("PvZ_Fusion_Translator", "UseLocal", Utils.useLocal); StringStore.Reload(); } public static async Task GetDataFromWeb(string url, bool isLog = true) { if (isLog) { Log.LogInfo($"Attempting to read {url}"); } var dataRequest = await new HttpClient().GetAsync(url); if(dataRequest.StatusCode == System.Net.HttpStatusCode.OK) { if (isLog) { Log.LogInfo($"Successfully loaded data from {url}!"); } string content = await dataRequest.Content.ReadAsStringAsync(); return content; } else { if (isLog) { Log.LogError($"Failed to load data from {url}! Falling back..."); Log.LogError($"Status code: {dataRequest.StatusCode}"); } new CancellationTokenSource().Cancel(); return null; } } #if MULTI_LANGUAGE internal static void ChangeLanguage(string language) { OldLanguage = Language; if (Enum.TryParse(language, out Utils.LanguageEnum lang)) { Utils.Language = lang; } else { // Handle invalid language string Log.LogError($"Invalid language string: {language}"); } //WarningStore.isWarningMessageLoaded = false; FontStore.Reload(); StringStore.Reload(); TextureStore.Reload(); FileLoader.SaveLanguage(); RegisterPlantIndices(); Zombie_Patch.LoadHPStrings(); } #endif #if MULTI_LANGUAGE public static Utils.LanguageEnum OldLanguage; #endif public static Utils.LanguageEnum Language; public static bool customTextures = !MelonPreferences.GetEntryValue("PvZ_Fusion_Translator", "DefaultTextures"); public static bool customAudio = !MelonPreferences.GetEntryValue("PvZ_Fusion_Translator", "DefaultAudio"); public enum LanguageEnum { // first column English, Chinese_cn, French, //Italian, //German, Spanish, // second column //Portuguese, //Javanese, //Filipino, Vietnamese, //Indonesian, //Russian, //NOTE: legacy language Japanese, // third column Korean, Ukrainian, // Slovak, //Polish, //Turkish, //Arabic, Romanian, LANG_END } public static Dictionary LanguageNames = new Dictionary() { { LanguageEnum.English, "English"}, { LanguageEnum.Chinese_cn, "简体中文"}, { LanguageEnum.French, "Français"}, { LanguageEnum.Spanish, "Español"}, { LanguageEnum.Vietnamese, "Tiếng Việt"}, { LanguageEnum.Japanese, "日本語"}, { LanguageEnum.Korean, "한국어"}, { LanguageEnum.Ukrainian, "українська"}, { LanguageEnum.Romanian, "Română"} }; public static Dictionary ToggleNames = new Dictionary() { { ToggleEnum.Textures, "Textures"}, { ToggleEnum.Audio, "Audio"}, { ToggleEnum.SwapLocal, "Swap Local\nTranslations"} }; public enum ToggleEnum { Textures, Audio, SwapLocal, TOGGLE_END } } }