PVZFusionTranslation/Source/FileLoader.cs
2026-10-21 19:54:49 +02:00

1141 lines
43 KiB
C#

using Il2CppInterop.Runtime.InteropTypes.Arrays;
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.Odyssey;
using System.Text.Encodings.Web;
using System.Text.Json;
namespace PvZ_Fusion_Translator;
// ReSharper disable AssignNullToNotNullAttribute Unity.UnknownResource
internal static class FileLoader
{
internal enum AssetType
{
Textures, Strings, Dumps, Almanac, Sprites,
}
public static string GetAssetDir(AssetType assetType, Utils.LanguageEnum? language = null)
{
var languagePath = language.HasValue
? "Localization" + Path.DirectorySeparatorChar + language
: string.Empty;
return Path.Combine(Core.ModDirectory, languagePath, assetType.ToString());
}
// private static string GetAssetDir(AssetType assetType) =>
// Path.Combine(Core.Instance.modsDirectory, assetType.ToString());
internal static void LoadStrings() => LoadStrings(Utils.Language);
internal static void LoadStrings(Utils.LanguageEnum language)
{
var stringDir = GetAssetDir(AssetType.Strings, Utils.Language);
if (!Directory.Exists(stringDir))
Directory.CreateDirectory(stringDir);
if (!Core.Instance.ConfigUseLocal.Value)
{
// load translationStrings
var translationStringsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/translation_strings.json"
)
.Result;
if (translationStringsContent != null)
LoadTranslationStrings(translationStringsContent);
else
{
var translationStringsPath = Path.Combine(
GetAssetDir(AssetType.Strings, Utils.Language),
"translation_strings.json"
);
if (File.Exists(translationStringsPath))
LoadTranslationStrings(File.ReadAllText(translationStringsPath));
}
// load translationRegexs
var translationRegexsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/translation_regexs.json"
)
.Result;
if (translationRegexsContent != null)
LoadTranslationRegexs(translationRegexsContent);
else
{
var translationRegexsPath = Path.Combine(
GetAssetDir(AssetType.Strings, Utils.Language),
"translation_regexs.json"
);
if (File.Exists(translationRegexsPath))
LoadTranslationRegexs(File.ReadAllText(translationRegexsPath));
}
var customLevelStringsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/customlevel_strings.json"
)
.Result;
if (customLevelStringsContent != null)
LoadCustomLevelStrings(customLevelStringsContent);
else
{
var customLevelStringsPath = Path.Combine(
GetAssetDir(AssetType.Strings, Utils.Language),
"customlevel_strings.json"
);
if (File.Exists(customLevelStringsPath))
LoadCustomLevelStrings(File.ReadAllText(customLevelStringsPath));
}
// load translationRegexs
var customLevelRegexsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/customlevel_regexs.json"
)
.Result;
if (customLevelRegexsContent != null)
LoadCustomLevelRegexs(customLevelRegexsContent);
else
{
var customLevelRegexsPath = Path.Combine(
GetAssetDir(AssetType.Strings, Utils.Language),
"customlevel_regexs.json"
);
if (File.Exists(customLevelRegexsPath))
LoadCustomLevelRegexs(File.ReadAllText(customLevelRegexsPath));
}
// load changelog
LoadChangelogText();
// load tips
var izTipsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/tips_iz.json"
)
.Result;
if (izTipsContent != null)
LoadIZStrings(izTipsContent);
else
{
var izTipsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "tips_iz.json");
if (File.Exists(izTipsPath))
LoadIZStrings(File.ReadAllText(izTipsPath));
}
var fsTipsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/tips_fs.json"
)
.Result;
if (fsTipsContent != null)
LoadFsStrings(fsTipsContent);
else
{
var fsTipsPath = Path.Combine(GetAssetDir(AssetType.Strings, Utils.Language), "tips_fs.json");
if (File.Exists(fsTipsPath))
LoadFsStrings(File.ReadAllText(fsTipsPath));
}
// load almanacs
var almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
if (!Directory.Exists(almanacDir))
Directory.CreateDirectory(almanacDir);
var plantAlmanacContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Almanac/LawnStringsTranslate.json"
)
.Result;
if (plantAlmanacContent != null)
AlmanacPlantMenu_Patch.LoadPlantAlmanacData(plantAlmanacContent);
else
{
var path = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if (File.Exists(path))
AlmanacPlantMenu_Patch.LoadPlantAlmanacData(File.ReadAllText(path));
}
var zombieAlmanacContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Almanac/ZombieStringsTranslate.json"
)
.Result;
if (zombieAlmanacContent != null)
AlmanacZombieMenu_Patch.AlmanacJson = zombieAlmanacContent;
else
{
var path = Path.Combine(almanacDir, "ZombieStringsTranslate.json");
if (File.Exists(path))
AlmanacZombieMenu_Patch.AlmanacJson = File.ReadAllText(path);
}
var detailStringsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Almanac/DetailStringsTranslate.json"
)
.Result;
if (detailStringsContent != null)
LoadDetailStrings(detailStringsContent);
else
{
var path = Path.Combine(almanacDir, "DetailStringsTranslate.json");
if (File.Exists(path))
LoadDetailStrings(File.ReadAllText(path));
}
// load travel_buffs
LoadTravelBuffs();
// load abyss buffs
//Patches.Modes.Abyss.AbyssBuffMenu_Patch.LoadAbyssBuffData();
SaveStrings();
DumpJson();
}
else
{
if (!Directory.Exists(stringDir))
Directory.CreateDirectory(stringDir);
try
{
foreach (var filepath in Directory.EnumerateFiles(stringDir, "*.json", SearchOption.AllDirectories))
{
var fileName = Path.GetFileNameWithoutExtension(filepath);
StringStore.StringsDict[fileName] = filepath;
if (ModFeatures.IsDebug)
Log.LogInfo("Loading File : " + filepath);
var jsonString = File.ReadAllText(filepath);
if (fileName.EndsWith("translation_strings"))
LoadTranslationStrings(jsonString);
else if (fileName.EndsWith("translation_regexs"))
LoadTranslationRegexs(jsonString);
else if (fileName.EndsWith("customlevel_strings"))
LoadCustomLevelStrings(jsonString);
else if (fileName.EndsWith("customlevel_regexs"))
LoadCustomLevelRegexs(jsonString);
else if (fileName.EndsWith("tips_iz"))
LoadIZStrings(jsonString);
else if (fileName.EndsWith("tips_fs"))
LoadFsStrings(jsonString);
//else if(fileName.EndsWith("abyss_buffs"))
//{
// Patches.Modes.Abyss.AbyssBuffMenu_Patch.LoadAbyssBuffData();
//}
else if (fileName.EndsWith("travel_buffs"))
LoadTravelBuffs();
}
var almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
if (!Directory.Exists(almanacDir))
Directory.CreateDirectory(almanacDir);
var plantAlmanacPath = Path.Combine(almanacDir, "LawnStringsTranslate.json");
if (File.Exists(plantAlmanacPath))
AlmanacPlantMenu_Patch.LoadPlantAlmanacData(File.ReadAllText(plantAlmanacPath));
var zombieAlmanacPath = Path.Combine(almanacDir, "ZombieStringsTranslate.json");
if (File.Exists(zombieAlmanacPath))
AlmanacZombieMenu_Patch.AlmanacJson = File.ReadAllText(zombieAlmanacPath);
var detailStringsPath = Path.Combine(almanacDir, "DetailStringsTranslate.json");
if (File.Exists(detailStringsPath))
LoadDetailStrings(File.ReadAllText(detailStringsPath));
LoadChangelogText();
SaveStrings();
DumpJson();
}
catch (Exception e)
{
Log.LogError("Error loading string.");
Log.LogError(e.GetType() + " " + e.Message);
}
}
Log.LogInfo("Strings loaded successfully.");
}
internal static void LoadTranslationStrings(string content)
{
var dictionary =
System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in dictionary)
StringStore.TranslationString[key] = value;
}
internal static void LoadTranslationRegexs(string content)
{
var dictionary =
System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in dictionary)
StringStore.TranslationStringRegex[key] = value;
Zombie_Patch.LoadHpStrings();
}
internal static void LoadCustomLevelStrings(string content)
{
var dictionary =
System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in dictionary)
StringStore.CustomLevelString[key] = value;
}
internal static void LoadCustomLevelRegexs(string content)
{
var dictionary =
System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var (key, value) in dictionary)
StringStore.CustomLevelStringRegex[key] = value;
}
internal static void LoadIZStrings(string content)
{
var (izLevelData, _) = DumpIZStrings();
var izTranslatedTips = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var level in izLevelData)
{
var data = level.text;
var levelData = JsonUtility.FromJson<Il2CppLevelData>(data);
if (levelData.tips == null)
continue;
if (!izTranslatedTips.ContainsKey(level.name) ||
StringStore.IZTipCollectionString.ContainsKey(levelData.tips))
continue;
StringStore.IZLevelTipDictionary.Add(level.name, izTranslatedTips[level.name]);
StringStore.IZTipCollectionString.Add(levelData.tips, izTranslatedTips[level.name]);
}
}
internal static void LoadFsStrings(string content)
{
var (fusionShowcaseData, _) = DumpFSStrings();
var fsTranslatedTips = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
foreach (var level in fusionShowcaseData)
{
var data = level.text;
var levelData = JsonUtility.FromJson<Il2CppLevelData>(data);
if (levelData.tips == null)
continue;
if (!fsTranslatedTips.ContainsKey(level.name) ||
StringStore.FSTipCollectionString.ContainsKey(levelData.tips))
continue;
StringStore.FSLevelTipDictionary.Add(level.name, fsTranslatedTips[level.name]);
StringStore.FSTipCollectionString.Add(levelData.tips, fsTranslatedTips[level.name]);
}
}
internal static void LoadDetailStrings(string content)
{
var (detailStringsData, _) = DumpDetailStrings();
var translatedDetailStrings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(content);
AlmanacSelectMenu_Patch.DetailStrings = translatedDetailStrings;
foreach (var detailString in detailStringsData.details)
if (translatedDetailStrings.TryGetValue(detailString.title, out var s))
AlmanacSelectMenu_Patch.DetailTranslateStrings[detailString.text] = s;
}
internal static void LoadChangelogText()
{
var stringDir = GetAssetDir(AssetType.Strings, Utils.Language);
var changelogDir = Path.Combine(stringDir, "changelog.txt");
if (!File.Exists(changelogDir))
File.WriteAllText(changelogDir, "");
var changelogText = File.ReadAllText(changelogDir);
if (!Core.Instance.ConfigUseLocal.Value)
{
var changelogContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/changelog.txt"
)
.Result;
if (changelogContent != null)
changelogText = changelogContent;
}
else
changelogText = File.ReadAllText(changelogDir);
NoticePauseMenu_Patch.ChangelogText = changelogText;
}
internal static void LoadTravelBuffs()
{
if (!Core.Instance.ConfigUseLocal.Value)
{
var travelBuffsContent = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Strings/travel_buffs.json"
)
.Result;
if (travelBuffsContent != null)
TravelMgr_Patch.GenerateTranslatedTravelBuffs(travelBuffsContent);
else
{
var travelBuffsPath = Path.Combine(
GetAssetDir(AssetType.Strings, Utils.Language),
"travel_buffs.json"
);
if (File.Exists(travelBuffsPath))
TravelMgr_Patch.GenerateTranslatedTravelBuffs(File.ReadAllText(travelBuffsPath));
}
}
else
{
var stringDir = GetAssetDir(AssetType.Strings, Utils.Language);
foreach (var jsonString in from filepath in
Directory.EnumerateFiles(stringDir, "*.json", SearchOption.AllDirectories)
let fileName = Path.GetFileNameWithoutExtension(filepath)
let jsonString = File.ReadAllText(filepath)
where fileName.EndsWith("travel_buffs")
select jsonString)
TravelMgr_Patch.GenerateTranslatedTravelBuffs(jsonString);
}
}
internal static void LoadTextures()
{
// Default or Custom Texturse -> English Textures -> Localized Textures
try
{
if (!Core.Instance.ConfigDefaultTextures.Value)
LoadCustomTextures();
}
catch (Exception e)
{
Log.LogError("Error loading texture setting.");
Log.LogError($"{e.GetType()} {e.Message}");
}
if (ModFeatures.IsMultiLanguage)
{
LoadLocalizedTextures(Utils.Language);
LoadLocalizedSprites(Utils.Language);
}
else
LoadLocalizedTextures();
Log.LogInfo("Textures loaded successfully.");
}
// ReSharper disable once FunctionComplexityOverflow
internal static void LoadLocalizedTextures(Utils.LanguageEnum? language = null)
{
var textureDir = GetAssetDir(AssetType.Textures, language);
if (!Directory.Exists(textureDir))
Directory.CreateDirectory(textureDir);
try
{
var localTocDir = Path.Combine(textureDir, "Texture_TOC.json");
if (!File.Exists(localTocDir))
File.WriteAllText(localTocDir, "{}");
var localTocContent = File.ReadAllText(localTocDir);
var localToc = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(localTocContent);
var basePath = Core.ModDirectory;
if (!Core.Instance.ConfigUseLocal.Value)
{
var textureTocRequest = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Textures/Texture_TOC.json"
)
.Result;
if (textureTocRequest != null)
{
var textureToc =
JsonConvert.DeserializeObject<Dictionary<string, string>>(textureTocRequest);
List<string> toDownload = new();
foreach (var pair in textureToc)
{
if (!localToc.TryGetValue(pair.Key, out var value))
{
toDownload.Add(pair.Key);
continue;
}
if (pair.Value != value)
toDownload.Add(pair.Key);
if (!File.Exists(Path.Combine(basePath, pair.Key)))
toDownload.Add(pair.Key);
}
var toWriteText = SerializeWithIndentation(textureToc);
File.WriteAllText(localTocDir, toWriteText);
foreach (var path in toDownload)
{
var downloadPath = Path.Combine(basePath, path);
const string BaseUrl =
"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/";
var imageData = Utils.GetByteDataFromWeb($"{BaseUrl}{path}").Result;
if (imageData == null)
continue;
var fileName = Path.GetFileName(downloadPath);
_ = Utils.LoadImage(imageData);
TextureStore.TextureDict[fileName] = imageData;
if (!Directory.Exists(Path.GetDirectoryName(downloadPath)))
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath));
File.WriteAllBytes(downloadPath, imageData);
}
}
}
foreach (var path in Directory.EnumerateFiles(textureDir, "*.png", SearchOption.AllDirectories)
.Where(
p => (!p.Contains("[Custom Textures]", StringComparison.OrdinalIgnoreCase) ||
!Core.Instance.ConfigDefaultTextures.Value) &&
(localToc.ContainsKey(p.Replace(basePath + "\\", "").Replace("\\", "/")) ||
SerializeWithIndentation(localToc) == "{}")
))
{
if (ModFeatures.IsDebug)
Log.LogDebug("Loading File : " + path);
var imageData = File.ReadAllBytes(path);
_ = Utils.LoadImage(imageData);
TextureStore.TextureDict[Path.GetFileNameWithoutExtension(path)] = imageData;
}
}
catch (Exception e)
{
Log.LogError("Error loading Texture.");
Log.LogError(e.GetType() + " " + e.Message);
}
}
// ReSharper disable once FunctionComplexityOverflow
internal static void LoadLocalizedSprites(Utils.LanguageEnum? language = null)
{
var textureDir = GetAssetDir(AssetType.Sprites, language);
if (!Directory.Exists(textureDir))
Directory.CreateDirectory(textureDir);
try
{
var trueTextureDir = GetAssetDir(AssetType.Textures, language);
var localTocDir = Path.Combine(trueTextureDir, "Texture_TOC.json");
if (!File.Exists(localTocDir))
File.WriteAllText(localTocDir, "{}");
var localTocContent = File.ReadAllText(localTocDir);
var localToc = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(localTocContent);
var basePath = Core.ModDirectory;
if (!Core.Instance.ConfigUseLocal.Value)
{
var textureTocRequest = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/Localization/{Utils.Language.ToString()}/Textures/Texture_TOC.json"
)
.Result;
if (textureTocRequest != null)
{
var textureToc =
JsonConvert.DeserializeObject<Dictionary<string, string>>(textureTocRequest);
List<string> toDownload = new();
foreach (var pair in textureToc)
{
if (!localToc.TryGetValue(pair.Key, out var value))
{
toDownload.Add(pair.Key);
continue;
}
if (pair.Value != value)
toDownload.Add(pair.Key);
if (!File.Exists(Path.Combine(basePath, pair.Key)))
toDownload.Add(pair.Key);
}
var toWriteText = SerializeWithIndentation(textureToc);
File.WriteAllText(localTocDir, toWriteText);
foreach (var path in toDownload)
{
var downloadPath = Path.Combine(basePath, path);
const string BaseUrl =
"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/";
var url = $"{BaseUrl}{path}";
var imageData = Utils.GetByteDataFromWeb(url).Result;
if (imageData == null)
continue;
var fileName = Path.GetFileName(downloadPath);
_ = Utils.LoadImage(imageData);
TextureStore.SpriteDict[fileName] = imageData;
if (!Directory.Exists(Path.GetDirectoryName(downloadPath)))
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath));
File.WriteAllBytes(downloadPath, imageData);
}
}
}
foreach (var path in Directory.EnumerateFiles(textureDir, "*.png", SearchOption.AllDirectories)
.Where(
p => (!p.Contains("[Custom Textures]", StringComparison.OrdinalIgnoreCase) ||
!Core.Instance.ConfigDefaultTextures.Value) &&
(localToc.ContainsKey(p.Replace(basePath + "\\", "").Replace("\\", "/")) ||
SerializeWithIndentation(localToc) == "{}")
))
{
if (ModFeatures.IsDebug)
Log.LogDebug("Loading File (Sprite): " + path);
var imageData = File.ReadAllBytes(path);
_ = Utils.LoadImage(imageData);
TextureStore.SpriteDict[Path.GetFileNameWithoutExtension(path)] = imageData;
}
}
catch (Exception e)
{
Log.LogError("Error loading Texture.");
Log.LogError(e.GetType() + " " + e.Message);
}
}
internal static void LoadDefaultTextures()
{
if (!Core.Instance.ConfigDefaultTextures.Value)
return;
var textureDefaultDir = Path.Combine(
Core.ModDirectory,
nameof(AssetType.Dumps),
"Default Textures [Do Not Remove]"
);
if (!Directory.Exists(textureDefaultDir))
Directory.CreateDirectory(textureDefaultDir);
try
{
var trueTextureDir = Path.Combine(Core.ModDirectory, "[Custom Textures]");
var localTocDir = Path.Combine(trueTextureDir, "Default_TOC.json");
if (!File.Exists(localTocDir))
File.WriteAllText(localTocDir, "{}");
var localTocContent = File.ReadAllText(localTocDir);
var localToc = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(localTocContent);
var basePath = Core.ModDirectory;
if (!Core.Instance.ConfigUseLocal.Value)
{
var textureTocRequest = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/{Uri.EscapeDataString("[Custom Textures]")}/Default_TOC.json"
)
.Result;
if (textureTocRequest != null)
{
var textureToc =
JsonConvert.DeserializeObject<Dictionary<string, string>>(textureTocRequest);
List<string> toDownload = new();
foreach (var pair in textureToc)
{
if (!localToc.TryGetValue(pair.Key, out var value))
{
toDownload.Add(pair.Key);
continue;
}
if (pair.Value != value)
toDownload.Add(pair.Key);
if (!File.Exists(Path.Combine(basePath, pair.Key)))
toDownload.Add(pair.Key);
}
var toWriteText = SerializeWithIndentation(textureToc);
File.WriteAllText(localTocDir, toWriteText);
foreach (var path in toDownload)
{
var downloadPath = Path.Combine(basePath, path);
const string BaseUrl = "https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/";
var url = $"{BaseUrl}{path}";
var imageData = Utils.GetByteDataFromWeb(url).Result;
if (imageData == null)
continue;
var fileName = Path.GetFileName(downloadPath);
_ = Utils.LoadImage(imageData);
TextureStore.TextureDict[fileName] = imageData;
if (!Directory.Exists(Path.GetDirectoryName(downloadPath)))
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath));
File.WriteAllBytes(downloadPath, imageData);
}
}
}
foreach (var filepath in Directory.EnumerateFiles(
textureDefaultDir,
"*.png",
SearchOption.AllDirectories
))
{
if (!localToc.ContainsKey(filepath.Replace(basePath + "\\", "").Replace("\\", "/")) &&
SerializeWithIndentation(localToc) != "{}")
continue;
var imageData = File.ReadAllBytes(filepath);
_ = Utils.LoadImage(imageData);
TextureStore.TextureDict[Path.GetFileNameWithoutExtension(filepath)] = imageData;
}
}
catch (Exception e)
{
Log.LogError("Error loading Texture.");
Log.LogError(e.GetType() + " " + e.Message);
}
}
internal static void LoadCustomTextures()
{
var texturePackDir = Path.Combine(Core.ModDirectory, "[Custom Textures]");
if (!Directory.Exists(texturePackDir))
Directory.CreateDirectory(texturePackDir);
try
{
var trueTextureDir = Path.Combine(Core.ModDirectory, "[Custom Textures]");
var localTocDir = Path.Combine(trueTextureDir, "Custom_TOC.json");
if (!File.Exists(localTocDir))
File.WriteAllText(localTocDir, "{}");
var localTocContent = File.ReadAllText(localTocDir);
var localToc = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(localTocContent);
var basePath = Core.ModDirectory;
if (!Core.Instance.ConfigUseLocal.Value)
{
var textureTocRequest = Utils.GetDataFromWeb(
$"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/{Uri.EscapeDataString("[Custom Textures]")}/Custom_TOC.json"
)
.Result;
if (textureTocRequest != null)
{
var textureToc =
JsonConvert.DeserializeObject<Dictionary<string, string>>(textureTocRequest);
List<string> toDownload = new();
foreach (var pair in textureToc)
{
if (!localToc.TryGetValue(pair.Key, out var value) || pair.Value != value)
{
toDownload.Add(pair.Key);
continue;
}
if (!File.Exists(Path.Combine(basePath, pair.Key)))
toDownload.Add(pair.Key);
}
var toWriteText = SerializeWithIndentation(textureToc);
File.WriteAllText(localTocDir, toWriteText);
foreach (var path in toDownload)
{
var downloadPath = Path.Combine(basePath, path);
const string BaseUrl =
"https://raw.githubusercontent.com/Teyliu/PVZF-Translation/refs/heads/main/PvZ_Fusion_Translator/";
var url = $"{BaseUrl}{path}";
var imageData = Utils.GetByteDataFromWeb(url).Result;
if (imageData == null)
continue;
var fileName = Path.GetFileName(downloadPath);
_ = Utils.LoadImage(imageData);
TextureStore.TextureDict[fileName] = imageData;
if (!Directory.Exists(Path.GetDirectoryName(downloadPath)))
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath));
File.WriteAllBytes(downloadPath, imageData);
}
}
}
foreach (var filepath in Directory.EnumerateFiles(texturePackDir, "*.png", SearchOption.AllDirectories))
{
if (!localToc.ContainsKey(filepath.Replace(basePath + "\\", "").Replace("\\", "/")) &&
SerializeWithIndentation(localToc) != "{}")
continue;
var imageData = File.ReadAllBytes(filepath);
_ = Utils.LoadImage(imageData);
TextureStore.TextureDict[Path.GetFileNameWithoutExtension(filepath)] = imageData;
}
}
catch (Exception e)
{
Log.LogError("Error loading Texture.");
Log.LogError(e.GetType() + " " + e.Message);
}
}
internal static void SaveStrings()
{
var stringDir = GetAssetDir(AssetType.Strings, ModFeatures.IsMultiLanguage ? Utils.Language : null);
if (!Directory.Exists(stringDir))
Directory.CreateDirectory(stringDir);
// save regexs
var translationStringRegex = SerializeWithIndentation(StringStore.TranslationStringRegex, 2);
File.WriteAllText(Path.Combine(stringDir, "translation_regexs.json"), translationStringRegex);
// save strings
var translationString = SerializeWithIndentation(StringStore.TranslationString, 2);
File.WriteAllText(Path.Combine(stringDir, "translation_strings.json"), translationString);
// save iz tips
var izTipCollectionString = SerializeWithIndentation(StringStore.IZLevelTipDictionary, 1, ' ');
File.WriteAllText(Path.Combine(stringDir, "tips_iz.json"), izTipCollectionString);
// save fs tips
var fsLevelTipDictionary = SerializeWithIndentation(StringStore.FSLevelTipDictionary);
File.WriteAllText(Path.Combine(stringDir, "tips_fs.json"), fsLevelTipDictionary);
// save changelog
if (NoticePauseMenu_Patch.ChangelogText != "")
File.WriteAllText(Path.Combine(stringDir, "changelog.txt"), NoticePauseMenu_Patch.ChangelogText);
// save abyss buffs
//if(Patches.Modes.Abyss.AbyssBuffMenu_Patch.abyssBuffData != new Dictionary<string, string>())
//{
// string abyssBuffData = SerializeWithIndentation(Patches.Modes.Abyss.AbyssBuffMenu_Patch.abyssBuffData, 2);
// File.WriteAllText(Path.Combine(stringDir, "abyss_buffs.json"), abyssBuffData);
//}
// save almanacs
var almanacDir = GetAssetDir(AssetType.Almanac, Utils.Language);
if (AlmanacPlantMenu_Patch.AlmanacJson != "")
File.WriteAllText(
Path.Combine(almanacDir, "LawnStringsTranslate.json"),
AlmanacPlantMenu_Patch.AlmanacJson
);
if (AlmanacZombieMenu_Patch.AlmanacJson != "")
File.WriteAllText(
Path.Combine(almanacDir, "ZombieStringsTranslate.json"),
AlmanacZombieMenu_Patch.AlmanacJson
);
if (AlmanacSelectMenu_Patch.DetailStrings == new Dictionary<string, string>())
return;
var detailStrings = SerializeWithIndentation(AlmanacSelectMenu_Patch.DetailStrings);
File.WriteAllText(Path.Combine(almanacDir, "DetailStringsTranslate.json"), detailStrings);
}
public static string SerializeWithIndentation<T>(T value, int size = 4, char indentChar = ' ')
{
var stringWriter = new StringWriter();
using (JsonTextWriter writer = new(stringWriter))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = size;
writer.IndentChar = indentChar;
Newtonsoft.Json.JsonSerializer serializer = new();
serializer.Serialize(writer, value, typeof(T));
}
return stringWriter.ToString();
}
public static void DumpJson()
{
var dumpDir = GetAssetDir(AssetType.Dumps);
if (!Directory.Exists(dumpDir))
Directory.CreateDirectory(dumpDir);
var lawnStrings = Resources.Load<TextAsset>("LawnStrings").text;
var zombieStrings = Resources.Load<TextAsset>("ZombieStrings").text;
//string AbyssBuffData = Resources.Load<TextAsset>("AbyssBuffData").text;
File.WriteAllText(Path.Combine(dumpDir, "LawnStrings.json"), lawnStrings);
File.WriteAllText(Path.Combine(dumpDir, "ZombieStrings.json"), zombieStrings);
//File.WriteAllText(Path.Combine(dumpDir, "AbyssBuffData.json"), AbyssBuffData);
// Dump IZ tips.
var (_, izLevelDataDump) = DumpIZStrings();
File.WriteAllText(
Path.Combine(dumpDir, "tips_iz.json"),
System.Text.Json.JsonSerializer.Serialize(
izLevelDataDump,
new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
}
)
);
// Dump fusion showcase tips.
var (_, fusionShowcaseDataDump) = DumpFSStrings();
File.WriteAllText(
Path.Combine(dumpDir, "tips_fs.json"),
System.Text.Json.JsonSerializer.Serialize(
fusionShowcaseDataDump,
new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
}
)
);
// Dump almanac mechanics.
var (_, detailStrings) = DumpDetailStrings();
File.WriteAllText(
Path.Combine(dumpDir, "DetailStrings.json"),
System.Text.Json.JsonSerializer.Serialize(
detailStrings,
new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
}
)
);
}
public static (Il2CppArrayBase<TextAsset>, Dictionary<string, string>) DumpIZStrings()
{
var izLevelData = Resources.LoadAll<TextAsset>("izleveldata");
var izLevelDataDump = new Dictionary<string, string>();
foreach (var level in izLevelData)
{
var data = level.text;
var levelData = JsonUtility.FromJson<Il2CppLevelData>(data);
if (levelData.tips != null)
izLevelDataDump.Add(level.name, levelData.tips);
}
return (izLevelData, izLevelDataDump);
}
public static (Il2CppArrayBase<TextAsset>, Dictionary<string, string>) DumpFSStrings()
{
var fusionShowcaseData = Resources.LoadAll<TextAsset>("leveldata/explore");
var fusionShowcaseDataDump = new Dictionary<string, string>();
foreach (var level in fusionShowcaseData)
{
var data = level.text;
var levelData = JsonUtility.FromJson<Il2CppLevelData>(data);
if (levelData.tips != null)
fusionShowcaseDataDump.Add(level.name, levelData.tips);
}
return (fusionShowcaseData, fusionShowcaseDataDump);
}
public static (Il2CppAlmanacDataClass, Dictionary<string, string>) DumpDetailStrings()
{
var detailStringsData = Resources.Load<TextAsset>("detailstrings").text;
var detailStrings = JsonUtility.FromJson<Il2CppAlmanacDataClass>(detailStringsData);
Dictionary<string, string> detailStringsDump = new();
foreach (var detailString in detailStrings.details)
detailStringsDump[detailString.title] = detailString.text;
return (detailStrings, detailStringsDump);
}
public static void DumpUntranslatedStrings(string text)
{
Debug.Assert(ModFeatures.IsDebug, nameof(ModFeatures.IsDebug));
Log.LogDebug($"Untranslated String: {text}");
var dumpDir = GetAssetDir(AssetType.Dumps);
var jsonFile = Path.Combine(dumpDir, "UntranslatedStrings.json");
if (!Directory.Exists(dumpDir))
Directory.CreateDirectory(dumpDir);
if (!File.Exists(jsonFile))
File.WriteAllText(jsonFile, "{}"); // Initialize empty JSON object
var json = File.ReadAllText(jsonFile);
var untranslatedStrings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json);
if (!untranslatedStrings.TryAdd(text, text))
return;
JsonSerializerOptions options = new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true,
};
File.WriteAllText(jsonFile, System.Text.Json.JsonSerializer.Serialize(untranslatedStrings, options));
}
internal static void LoadLanguage()
{
Debug.Assert(ModFeatures.IsMultiLanguage, nameof(ModFeatures.IsMultiLanguage));
try
{
// Load the language preference as a string and parse it into the enum
var languageName = Core.Instance.ConfigLanguage.Value;
if (Enum.TryParse(languageName, out Utils.LanguageEnum loadedLanguage))
Utils.Language = loadedLanguage;
else
{
Log.LogWarning($"Invalid language '{languageName}' found in preferences. Falling back to English.");
Utils.Language = Utils.LanguageEnum.English; // Default fallback
}
Log.LogWarning($"Loaded language {languageName}");
}
catch (Exception e)
{
Log.LogError("Error loading language setting.");
Log.LogError($"{e.GetType()} {e.Message}");
}
Log.LogInfo($"Language has been loaded: {Utils.Language}");
}
internal static void SaveLanguage()
{
Debug.Assert(ModFeatures.IsMultiLanguage, nameof(ModFeatures.IsMultiLanguage));
try
{
// Save the current language as a string
Core.Instance.ConfigLanguage.Value = Utils.Language.ToString();
// Ensure changes are written to the config file
// MelonPreferences.Save();
}
catch (Exception e)
{
Log.LogError("Error saving language setting.");
Log.LogError($"{e.GetType()} {e.Message}");
}
// Log.LogInfo($"Language has been saved: {Utils.Language}");
}
}