2016-07-09 22:30:12 +00:00
using System ;
using System.Collections.Generic ;
2017-07-02 02:43:51 +00:00
using System.Diagnostics ;
2017-10-23 18:18:44 +00:00
using System.IO ;
2016-07-09 22:30:12 +00:00
using System.Linq ;
2017-05-12 04:34:18 +00:00
using System.Reflection ;
2016-07-09 22:30:12 +00:00
2017-01-08 07:54:09 +00:00
namespace PKHeX.Core
2016-07-09 22:30:12 +00:00
{
2018-05-12 19:28:48 +00:00
public static partial class Util
2016-07-09 22:30:12 +00:00
{
2017-03-24 17:59:45 +00:00
private const string TranslationSplitter = " = " ;
2017-11-03 03:39:38 +00:00
private static readonly Assembly thisAssembly = typeof ( Util ) . GetTypeInfo ( ) . Assembly ;
private static readonly string [ ] manifestResourceNames = thisAssembly . GetManifestResourceNames ( ) ;
private static readonly Dictionary < string , string > resourceNameMap = new Dictionary < string , string > ( ) ;
private static readonly Dictionary < string , string [ ] > stringListCache = new Dictionary < string , string [ ] > ( ) ;
2017-03-24 17:59:45 +00:00
2019-02-04 04:28:03 +00:00
private static readonly object getStringListLoadLock = new object ( ) ;
2018-11-06 23:25:35 +00:00
2018-05-12 19:28:48 +00:00
#region String Lists
2016-10-06 04:06:24 +00:00
2016-08-08 20:11:02 +00:00
/// <summary>
/// Gets a list of all Pokémon species names.
/// </summary>
/// <param name="language">Language of the Pokémon species names to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each Pokémon species name.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetSpeciesList ( string language ) = > GetStringList ( "species" , language ) ;
2016-08-08 20:11:02 +00:00
/// <summary>
/// Gets a list of all move names.
/// </summary>
/// <param name="language">Language of the move names to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each move name.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetMovesList ( string language ) = > GetStringList ( "moves" , language ) ;
2016-08-08 20:11:02 +00:00
/// <summary>
/// Gets a list of all Pokémon ability names.
/// </summary>
/// <param name="language">Language of the Pokémon ability names to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each Pokémon ability name.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetAbilitiesList ( string language ) = > GetStringList ( "abilities" , language ) ;
2016-08-08 20:11:02 +00:00
2016-10-06 04:06:24 +00:00
/// <summary>
/// Gets a list of all Pokémon nature names.
/// </summary>
/// <param name="language">Language of the Pokémon nature names to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each Pokémon nature name.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetNaturesList ( string language ) = > GetStringList ( "natures" , language ) ;
2016-10-06 04:06:24 +00:00
/// <summary>
/// Gets a list of all Pokémon form names.
/// </summary>
/// <param name="language">Language of the Pokémon form names to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each Pokémon form name.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetFormsList ( string language ) = > GetStringList ( "forms" , language ) ;
2016-10-06 04:06:24 +00:00
/// <summary>
/// Gets a list of all Pokémon type names.
/// </summary>
/// <param name="language">Language of the Pokémon type names to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each Pokémon type name.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetTypesList ( string language ) = > GetStringList ( "types" , language ) ;
2016-10-06 04:06:24 +00:00
/// <summary>
/// Gets a list of all Pokémon characteristic.
/// </summary>
/// <param name="language">Language of the Pokémon characteristic to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each Pokémon characteristic.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetCharacteristicsList ( string language ) = > GetStringList ( "character" , language ) ;
2016-10-06 04:06:24 +00:00
/// <summary>
/// Gets a list of all items.
/// </summary>
/// <param name="language">Language of the items to select (e.g. "en", "fr", "jp", etc.)</param>
/// <returns>An array of strings whose indexes correspond to the IDs of each item.</returns>
2017-10-12 03:00:18 +00:00
public static string [ ] GetItemsList ( string language ) = > GetStringList ( "items" , language ) ;
2016-10-06 04:06:24 +00:00
#endregion
2017-06-18 01:37:19 +00:00
public static string [ ] GetStringList ( string f )
2016-07-09 22:30:12 +00:00
{
2019-03-19 04:34:21 +00:00
lock ( getStringListLoadLock ) // Make sure only one thread can read the cache
{
if ( stringListCache . TryGetValue ( f , out var result ) )
return ( string [ ] ) result . Clone ( ) ;
}
2017-11-02 02:34:01 +00:00
2017-10-23 18:18:44 +00:00
var txt = GetStringResource ( f ) ; // Fetch File, \n to list.
2019-02-15 19:47:35 +00:00
if ( txt = = null )
return Array . Empty < string > ( ) ;
2017-09-30 05:58:25 +00:00
string [ ] rawlist = txt . Split ( '\n' ) ;
2016-07-09 22:30:12 +00:00
for ( int i = 0 ; i < rawlist . Length ; i + + )
2018-05-25 04:16:55 +00:00
rawlist [ i ] = rawlist [ i ] . TrimEnd ( '\r' ) ;
2018-11-06 23:25:35 +00:00
lock ( getStringListLoadLock ) // Make sure only one thread can write to the cache
2019-02-02 07:08:03 +00:00
{
2018-11-06 23:25:35 +00:00
if ( ! stringListCache . ContainsKey ( f ) ) // Check cache again in case of race condition
stringListCache . Add ( f , rawlist ) ;
}
2017-11-08 01:12:04 +00:00
return ( string [ ] ) rawlist . Clone ( ) ;
2016-07-09 22:30:12 +00:00
}
2018-07-29 20:27:48 +00:00
2017-10-12 03:00:18 +00:00
public static string [ ] GetStringList ( string f , string l , string type = "text" ) = > GetStringList ( $"{type}_{f}_{l}" ) ;
2018-07-29 20:27:48 +00:00
2017-06-18 01:37:19 +00:00
public static byte [ ] GetBinaryResource ( string name )
2017-05-12 04:34:18 +00:00
{
2017-11-02 02:34:01 +00:00
using ( var resource = thisAssembly . GetManifestResourceStream (
2017-09-30 05:58:25 +00:00
$"PKHeX.Core.Resources.byte.{name}" ) )
2017-05-12 04:34:18 +00:00
{
var buffer = new byte [ resource . Length ] ;
resource . Read ( buffer , 0 , ( int ) resource . Length ) ;
return buffer ;
2017-10-23 18:18:44 +00:00
}
}
public static string GetStringResource ( string name )
{
2017-11-02 02:47:55 +00:00
if ( ! resourceNameMap . ContainsKey ( name ) )
2018-05-12 19:28:48 +00:00
{
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 ) ;
}
2017-11-02 02:44:35 +00:00
2017-11-03 03:39:38 +00:00
if ( resourceNameMap [ name ] = = null )
2017-11-02 02:44:35 +00:00
return null ;
2017-11-02 02:47:55 +00:00
using ( var resource = thisAssembly . GetManifestResourceStream ( resourceNameMap [ name ] ) )
2017-10-23 18:18:44 +00:00
using ( var reader = new StreamReader ( resource ) )
return reader . ReadToEnd ( ) ;
2017-05-12 04:34:18 +00:00
}
2017-03-25 01:23:39 +00:00
#region Non - Form Translation
2017-03-24 17:59:45 +00:00
/// <summary>
/// Gets the names of the properties defined in the given input
/// </summary>
/// <param name="input">Enumerable of translation definitions in the form "Property = Value".</param>
2017-06-18 01:37:19 +00:00
private static string [ ] GetProperties ( IEnumerable < string > input )
2017-03-24 17:59:45 +00:00
{
return input . Select ( l = > l . Substring ( 0 , l . IndexOf ( TranslationSplitter , StringComparison . Ordinal ) ) ) . ToArray ( ) ;
}
private static IEnumerable < string > DumpStrings ( Type t )
{
2019-02-08 05:40:20 +00:00
var props = ReflectUtil . GetPropertiesStartWithPrefix ( t , string . Empty ) ;
2018-05-12 19:28:48 +00:00
return props . Select ( p = > $"{p}{TranslationSplitter}{ReflectUtil.GetValue(t, p)}" ) ;
2017-03-24 17:59:45 +00:00
}
/// <summary>
/// Gets the current localization in a static class containing language-specific strings
/// </summary>
2018-05-12 19:28:48 +00:00
/// <param name="t"></param>
/// <param name="existingLines">Existing localization lines (if provided)</param>
2017-06-18 01:37:19 +00:00
public static string [ ] GetLocalization ( Type t , string [ ] existingLines = null )
2017-03-24 17:59:45 +00:00
{
var currentLines = DumpStrings ( t ) . ToArray ( ) ;
2018-06-01 05:40:55 +00:00
if ( existingLines = = null )
return currentLines ;
2017-06-18 01:37:19 +00:00
var existing = GetProperties ( existingLines ) ;
var current = GetProperties ( currentLines ) ;
2017-03-24 17:59:45 +00:00
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 ;
}
/// <summary>
/// Applies localization to a static class containing language-specific strings.
/// </summary>
2017-03-25 01:23:39 +00:00
/// <param name="t">Type of the static class containing the desired strings.</param>
2017-03-24 17:59:45 +00:00
/// <param name="lines">Lines containing the localized strings</param>
2017-06-18 01:37:19 +00:00
private static void SetLocalization ( Type t , IEnumerable < string > lines )
2018-05-12 15:13:39 +00:00
{
2017-03-24 17:59:45 +00:00
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
{
2018-04-08 16:19:19 +00:00
ReflectUtil . SetValue ( t , prop , value ) ;
2017-03-24 17:59:45 +00:00
}
2019-02-15 19:47:35 +00:00
catch ( Exception e )
2017-03-24 17:59:45 +00:00
{
2017-07-02 02:43:51 +00:00
Debug . WriteLine ( $"Property not present: {prop} || Value written: {value}" ) ;
2019-02-15 19:47:35 +00:00
Debug . WriteLine ( e . Message ) ;
2017-03-24 17:59:45 +00:00
}
}
}
/// <summary>
/// Applies localization to a static class containing language-specific strings.
/// </summary>
2017-03-25 01:23:39 +00:00
/// <param name="t">Type of the static class containing the desired strings.</param>
2017-03-24 17:59:45 +00:00
/// <param name="languageFilePrefix">Prefix of the language file to use. Example: if the target is legality_en.txt, <paramref name="languageFilePrefix"/> should be "legality".</param>
2017-05-12 16:33:12 +00:00
/// <param name="currentCultureCode">Culture information</param>
2017-06-18 01:37:19 +00:00
private static void SetLocalization ( Type t , string languageFilePrefix , string currentCultureCode )
2017-03-24 17:59:45 +00:00
{
2017-06-18 01:37:19 +00:00
SetLocalization ( t , GetStringList ( $"{languageFilePrefix}_{currentCultureCode}" ) ) ;
2017-03-24 17:59:45 +00:00
}
/// <summary>
/// Applies localization to a static class containing language-specific strings.
/// </summary>
2017-03-25 01:23:39 +00:00
/// <param name="t">Type of the static class containing the desired strings.</param>
2017-03-24 17:59:45 +00:00
/// <remarks>The values used to translate the given static class are retrieved from [TypeName]_[CurrentLangCode2].txt in the resource manager of PKHeX.Core.</remarks>
2017-05-12 16:33:12 +00:00
/// <param name="currentCultureCode">Culture information</param>
2017-06-18 01:37:19 +00:00
public static void SetLocalization ( Type t , string currentCultureCode )
2017-03-24 17:59:45 +00:00
{
2017-06-18 01:37:19 +00:00
SetLocalization ( t , t . Name , currentCultureCode ) ;
2017-03-24 17:59:45 +00:00
}
2017-03-25 01:23:39 +00:00
#endregion
2017-03-24 17:59:45 +00:00
2016-10-06 04:06:24 +00:00
#region DataSource Providing
2019-04-30 04:55:43 +00:00
private static readonly string [ ] CountryRegionLanguages = { "ja" , "en" , "fr" , "de" , "it" , "es" , "ko" , "zh" } ;
2019-02-15 19:47:35 +00:00
public static List < ComboItem > GetCountryRegionList ( string textfile , string lang )
2016-07-09 22:30:12 +00:00
{
2017-06-18 01:37:19 +00:00
string [ ] inputCSV = GetStringList ( textfile ) ;
2019-04-30 04:55:43 +00:00
int index = Array . IndexOf ( CountryRegionLanguages , lang ) ;
return GetCBListCSVSorted ( inputCSV , index ) ;
}
2016-07-09 22:30:12 +00:00
2019-04-30 04:55:43 +00:00
private static List < ComboItem > GetCBListCSVSorted ( string [ ] inputCSV , int index = 0 )
{
var list = GetCBListFromCSV ( inputCSV , index ) ;
list . Sort ( Comparer ) ;
return list ;
2017-09-30 08:07:30 +00:00
}
2018-07-29 20:27:48 +00:00
2019-04-30 04:55:43 +00:00
public static List < ComboItem > GetCSVUnsortedCBList ( string textfile )
2017-09-30 08:07:30 +00:00
{
string [ ] inputCSV = GetStringList ( textfile ) ;
2019-04-30 04:55:43 +00:00
return GetCBListFromCSV ( inputCSV , 0 ) ;
}
private static List < ComboItem > GetCBListFromCSV ( IReadOnlyList < string > inputCSV , int index )
{
var arr = new List < ComboItem > ( inputCSV . Count - 1 ) ; // skip header
index + + ;
for ( int i = 1 ; i < inputCSV . Count ; i + + )
{
var line = inputCSV [ i ] ;
var zeroth = line . IndexOf ( ',' ) ;
var val = line . Substring ( 0 , zeroth ) ;
var text = GetNthEntry ( line , index , zeroth ) ;
var item = new ComboItem { Text = text , Value = Convert . ToInt32 ( val ) } ;
arr . Add ( item ) ;
}
return arr ;
}
private static string GetNthEntry ( string line , int nth , int start )
{
if ( nth ! = 1 )
start = line . IndexOfNth ( ',' , nth - 1 , start + 1 ) ;
var end = line . IndexOfNth ( ',' , 1 , start + 1 ) ;
return end < 0 ? line . Substring ( start + 1 ) : line . Substring ( start + 1 , end - start - 1 ) ;
}
private static int IndexOfNth ( this string s , char t , int n , int start )
{
int count = 0 ;
for ( int i = start ; i < s . Length ; i + + )
{
if ( s [ i ] ! = t )
continue ;
if ( + + count = = n )
return i ;
}
return - 1 ;
2016-07-09 22:30:12 +00:00
}
2018-07-29 20:27:48 +00:00
2019-03-19 04:55:33 +00:00
public static List < ComboItem > GetCBList ( IReadOnlyList < string > inStrings )
2016-07-09 22:30:12 +00:00
{
2019-03-19 04:55:33 +00:00
var list = new List < ComboItem > ( inStrings . Count ) ;
2019-04-30 04:55:43 +00:00
for ( int i = 0 ; i < inStrings . Count ; i + + )
list . Add ( new ComboItem { Text = inStrings [ i ] , Value = i } ) ;
list . Sort ( Comparer ) ;
return list ;
}
public static List < ComboItem > GetCBList ( IReadOnlyList < string > inStrings , int index , int offset = 0 )
{
var list = new List < ComboItem > ( ) ;
AddCBWithOffset ( list , inStrings , offset , index ) ;
2019-03-19 04:55:33 +00:00
return list ;
}
2016-07-09 22:30:12 +00:00
2019-03-19 04:55:33 +00:00
public static List < ComboItem > GetCBList ( IReadOnlyList < string > inStrings , params int [ ] [ ] allowed )
{
var count = allowed . Sum ( z = > z . Length ) ;
var list = new List < ComboItem > ( count ) ;
foreach ( var arr in allowed )
2019-04-30 04:55:43 +00:00
AddCB ( list , inStrings , arr ) ;
2019-03-19 04:55:33 +00:00
return list ;
2016-07-09 22:30:12 +00:00
}
2018-07-29 20:27:48 +00:00
2019-04-30 04:55:43 +00:00
public static void AddCBWithOffset ( List < ComboItem > list , IReadOnlyList < string > inStrings , int offset , int index )
2016-07-09 22:30:12 +00:00
{
2019-04-30 04:55:43 +00:00
var item = new ComboItem { Text = inStrings [ index - offset ] , Value = index } ;
list . Add ( item ) ;
}
2016-07-09 22:30:12 +00:00
2019-04-30 04:55:43 +00:00
public static void AddCBWithOffset ( List < ComboItem > cbList , IReadOnlyList < string > inStrings , int offset , params int [ ] allowed )
{
int beginCount = cbList . Count ;
for ( int i = 0 ; i < allowed . Length ; i + + )
{
int index = allowed [ i ] ;
var item = new ComboItem { Text = inStrings [ index - offset ] , Value = index } ;
cbList . Add ( item ) ;
}
cbList . Sort ( beginCount , allowed . Length , Comparer ) ;
}
public static void AddCB ( List < ComboItem > cbList , IReadOnlyList < string > inStrings , int [ ] allowed )
{
int beginCount = cbList . Count ;
for ( int i = 0 ; i < allowed . Length ; i + + )
{
int index = allowed [ i ] ;
var item = new ComboItem { Text = inStrings [ index ] , Value = index } ;
cbList . Add ( item ) ;
}
cbList . Sort ( beginCount , allowed . Length , Comparer ) ;
2016-07-09 22:30:12 +00:00
}
2018-07-29 20:27:48 +00:00
2017-09-30 08:07:30 +00:00
public static List < ComboItem > GetVariedCBListBall ( string [ ] inStrings , int [ ] stringNum , int [ ] stringVal )
2016-07-09 22:30:12 +00:00
{
2019-04-30 04:55:43 +00:00
const int forcedTop = 3 ; // 3 Balls are preferentially first
var list = new List < ComboItem > ( forcedTop + stringNum . Length )
2018-09-02 02:55:08 +00:00
{
new ComboItem { Text = inStrings [ 4 ] , Value = ( int ) Ball . Poke } ,
new ComboItem { Text = inStrings [ 3 ] , Value = ( int ) Ball . Great } ,
new ComboItem { Text = inStrings [ 2 ] , Value = ( int ) Ball . Ultra } ,
} ;
2016-07-09 22:30:12 +00:00
2019-04-30 04:55:43 +00:00
for ( int i = 0 ; i < stringNum . Length ; i + + )
{
int index = stringNum [ i ] ;
var val = stringVal [ i ] ;
var txt = inStrings [ index ] ;
list . Add ( new ComboItem { Text = txt , Value = val } ) ;
}
list . Sort ( forcedTop , stringNum . Length , Comparer ) ;
return list
;
}
private static readonly FunctorComparer < ComboItem > Comparer =
new FunctorComparer < ComboItem > ( ( a , b ) = > string . Compare ( a . Text , b . Text , StringComparison . Ordinal ) ) ;
private sealed class FunctorComparer < T > : IComparer < T >
{
private readonly Comparison < T > Comparison ;
public FunctorComparer ( Comparison < T > comparison ) = > Comparison = comparison ;
public int Compare ( T x , T y ) = > Comparison ( x , y ) ;
2016-07-09 22:30:12 +00:00
}
2016-10-06 04:06:24 +00:00
#endregion
2016-07-09 22:30:12 +00:00
}
}