using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; namespace PKHeX.Core { public static partial class Util { private const string TranslationSplitter = " = "; private static readonly Assembly thisAssembly = typeof(Util).GetTypeInfo().Assembly; private static readonly string[] manifestResourceNames = thisAssembly.GetManifestResourceNames(); private static readonly Dictionary resourceNameMap = new Dictionary(); private static readonly Dictionary stringListCache = new Dictionary(); #region String Lists /// /// Gets a list of all Pokémon species names. /// /// Language of the Pokémon species names to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each Pokémon species name. public static string[] GetSpeciesList(string language) => GetStringList("species", language); /// /// Gets a list of all move names. /// /// Language of the move names to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each move name. public static string[] GetMovesList(string language) => GetStringList("moves", language); /// /// Gets a list of all Pokémon ability names. /// /// Language of the Pokémon ability names to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each Pokémon ability name. public static string[] GetAbilitiesList(string language) => GetStringList("abilities", language); /// /// Gets a list of all Pokémon nature names. /// /// Language of the Pokémon nature names to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each Pokémon nature name. public static string[] GetNaturesList(string language) => GetStringList("natures", language); /// /// Gets a list of all Pokémon form names. /// /// Language of the Pokémon form names to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each Pokémon form name. public static string[] GetFormsList(string language) => GetStringList("forms", language); /// /// Gets a list of all Pokémon type names. /// /// Language of the Pokémon type names to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each Pokémon type name. public static string[] GetTypesList(string language) => GetStringList("types", language); /// /// Gets a list of all Pokémon characteristic. /// /// Language of the Pokémon characteristic to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each Pokémon characteristic. public static string[] GetCharacteristicsList(string language) => GetStringList("character", language); /// /// Gets a list of all items. /// /// Language of the items to select (e.g. "en", "fr", "jp", etc.) /// An array of strings whose indexes correspond to the IDs of each item. public static string[] GetItemsList(string language) => GetStringList("items", language); #endregion public static string[] GetStringList(string f) { if (stringListCache.ContainsKey(f)) return (string[])stringListCache[f].Clone(); var txt = GetStringResource(f); // Fetch File, \n to list. if (txt == null) return Array.Empty(); string[] rawlist = txt.Split('\n'); for (int i = 0; i < rawlist.Length; i++) rawlist[i] = rawlist[i].TrimEnd('\r'); stringListCache.Add(f, rawlist); return (string[])rawlist.Clone(); } public static string[] GetStringList(string f, string l, string type = "text") => GetStringList($"{type}_{f}_{l}"); public static string[] GetNulledStringArray(string[] SimpleStringList) { try { int len = ToInt32(SimpleStringList.Last().Split(',')[0]) + 1; string[] newlist = new string[len]; for (int i = 1; i < SimpleStringList.Length; i++) { var split = SimpleStringList[i].Split(','); newlist[ToInt32(split[0])] = split[1]; } return newlist; } catch { return null; } } public static byte[] GetBinaryResource(string name) { using (var resource = thisAssembly.GetManifestResourceStream( $"PKHeX.Core.Resources.byte.{name}")) { var buffer = new byte[resource.Length]; resource.Read(buffer, 0, (int)resource.Length); return buffer; } } public static string GetStringResource(string name) { if (!resourceNameMap.ContainsKey(name)) { bool Match(string x) => x.StartsWith("PKHeX.Core.Resources.text.") && x.EndsWith($"{name}.txt", StringComparison.OrdinalIgnoreCase); var resname = Array.Find(manifestResourceNames, Match); resourceNameMap.Add(name, resname); } if (resourceNameMap[name] == null) return null; using (var resource = thisAssembly.GetManifestResourceStream(resourceNameMap[name])) using (var reader = new StreamReader(resource)) return reader.ReadToEnd(); } #region Non-Form Translation /// /// Gets the names of the properties defined in the given input /// /// Enumerable of translation definitions in the form "Property = Value". /// private static string[] GetProperties(IEnumerable input) { return input.Select(l => l.Substring(0, l.IndexOf(TranslationSplitter, StringComparison.Ordinal))).ToArray(); } private static IEnumerable DumpStrings(Type t) { var props = ReflectUtil.GetPropertiesStartWithPrefix(t, ""); return props.Select(p => $"{p}{TranslationSplitter}{ReflectUtil.GetValue(t, p)}"); } /// /// Gets the current localization in a static class containing language-specific strings /// /// /// Existing localization lines (if provided) public static string[] GetLocalization(Type t, string[] existingLines = null) { var currentLines = DumpStrings(t).ToArray(); if (existingLines == null) return currentLines; var existing = GetProperties(existingLines); var current = GetProperties(currentLines); var result = new string[currentLines.Length]; for (int i = 0; i < current.Length; i++) { int index = Array.IndexOf(existing, current[i]); result[i] = index < 0 ? currentLines[i] : existingLines[index]; } return result; } /// /// Applies localization to a static class containing language-specific strings. /// /// Type of the static class containing the desired strings. /// Lines containing the localized strings private static void SetLocalization(Type t, IEnumerable lines) { if (lines == null) return; foreach (var line in lines.Where(l => l != null)) { var index = line.IndexOf(TranslationSplitter, StringComparison.Ordinal); if (index < 0) continue; var prop = line.Substring(0, index); var value = line.Substring(index + TranslationSplitter.Length); try { ReflectUtil.SetValue(t, prop, value); } catch { Debug.WriteLine($"Property not present: {prop} || Value written: {value}"); } } } /// /// Applies localization to a static class containing language-specific strings. /// /// Type of the static class containing the desired strings. /// Prefix of the language file to use. Example: if the target is legality_en.txt, should be "legality". /// Culture information private static void SetLocalization(Type t, string languageFilePrefix, string currentCultureCode) { SetLocalization(t, GetStringList($"{languageFilePrefix}_{currentCultureCode}")); } /// /// Applies localization to a static class containing language-specific strings. /// /// Type of the static class containing the desired strings. /// The values used to translate the given static class are retrieved from [TypeName]_[CurrentLangCode2].txt in the resource manager of PKHeX.Core. /// Culture information public static void SetLocalization(Type t, string currentCultureCode) { SetLocalization(t, t.Name, currentCultureCode); } #endregion #region DataSource Providing public static List GetCBList(string textfile, string lang) { // Set up string[] inputCSV = GetStringList(textfile); // Get Language we're fetching for int index = Array.IndexOf(new[] { "ja", "en", "fr", "de", "it", "es", "ko", "zh", }, lang); // Gather our data from the input file return inputCSV.Skip(1) .Select(entry => entry.Split(',')) .Select(data => new ComboItem { Text = data[1 + index], Value = Convert.ToInt32(data[0]) }) .OrderBy(z => z.Text) .ToList(); } public static List GetUnsortedCBList(string textfile) { string[] inputCSV = GetStringList(textfile); return inputCSV.Skip(1) .Select(entry => entry.Split(',')) .Select(data => new ComboItem { Text = data[1], Value = Convert.ToInt32(data[0]) }) .ToList(); } public static List GetCBList(IReadOnlyList inStrings, params int[][] allowed) { if (allowed?[0] == null) allowed = new[] { Enumerable.Range(0, inStrings.Count).ToArray() }; return allowed.SelectMany(list => list .Select(z => new ComboItem { Text = inStrings[z], Value = z }) .OrderBy(z => z.Text)) .ToList(); } public static List GetOffsetCBList(List cbList, IReadOnlyList inStrings, int offset, IEnumerable allowed) { if (allowed == null) allowed = Enumerable.Range(0, inStrings.Count); var list = allowed .Select(z => new ComboItem {Text = inStrings[z - offset], Value = z}) .OrderBy(z => z.Text); cbList.AddRange(list); return cbList; } public static List GetVariedCBListBall(string[] inStrings, int[] stringNum, int[] stringVal) { // First 3 Balls are always first List newlist = new List(); for (int i = 4; i > 1; i--) // add 4,3,2 newlist.Add(new ComboItem { Text = inStrings[i], Value = i }); newlist.AddRange(stringNum .Select((z, i) => new ComboItem { Text = inStrings[z], Value = stringVal[i] }) .OrderBy(z => z.Text)); return newlist; } #endregion } }