Use StringConverter api consistently; use stringbuilder when possible

Retain a stringbuilder to mutate the string rather than finalizing temporary strings

yields some speed improvements (less gen0 string objects allocated)
This commit is contained in:
Kurt 2021-01-14 22:50:13 -08:00
parent 3816d25f7b
commit 613e6db744
24 changed files with 246 additions and 195 deletions

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PKHeX.Core
{
@ -52,8 +51,8 @@ namespace PKHeX.Core
public string Nickname
{
get => Util.TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x1E, 0x16));
set => Encoding.Unicode.GetBytes(value.PadRight(0xB, (char)0xFFFF)).CopyTo(Data, 0x1E);
get => StringConverter.GetString5(Data, 0x1E, 11 * 2);
set => StringConverter.SetString5(value, 11, 11, (char)0xFFFF).CopyTo(Data, 0x1E);
}
public int Nature { get => (sbyte)Data[0x34]; set => Data[0x34] = (byte)value; }
@ -76,9 +75,11 @@ namespace PKHeX.Core
public int IV_SPA { get => Data[0x47]; set => Data[0x47] = (byte)value; }
public int IV_SPD { get => Data[0x48]; set => Data[0x48] = (byte)value; }
// Unused 0x49
public override string OT_Name {
get => Util.TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x4A, 0x10));
set => Encoding.Unicode.GetBytes(value.PadRight(0x08, (char)0xFFFF)).CopyTo(Data, 0x4A); }
public override string OT_Name
{
get => StringConverter.GetString5(Data, 0x4A, 8 * 2);
set => StringConverter.SetString5(value, 8, 8, (char)0xFFFF).CopyTo(Data, 0x4A);
}
public int OTGender { get => Data[0x5A]; set => Data[0x5A] = (byte)value; }
public override int Level { get => Data[0x5B]; set => Data[0x5C] = (byte)value; }
@ -86,8 +87,8 @@ namespace PKHeX.Core
// Unused 0x5D 0x5E 0x5F
public override string CardTitle
{
get => Util.TrimFromFFFF(Encoding.Unicode.GetString(Data, 0x60, 0x4A));
set => Encoding.Unicode.GetBytes((value + '\uFFFF').PadRight(0x4A / 2, '\0')).CopyTo(Data, 0x60);
get => StringConverter.GetString5(Data, 0x60, 37 * 2);
set => StringConverter.SetString5(value + '\uFFFF', 37, 37, 0).CopyTo(Data, 0x60);
}
// Card Attributes

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PKHeX.Core
{
@ -33,7 +32,7 @@ namespace PKHeX.Core
/// <summary>
/// Name of data source
/// </summary>
public string Origin { get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x01, 0x6E)); set => Encoding.Unicode.GetBytes(value.PadRight(54 + 1, '\0')).CopyTo(Data, 0x01); }
public string Origin { get => StringConverter.GetString6(Data, 0x01, 110); set => StringConverter.SetString6(value, 54, 55).CopyTo(Data, 0x01); }
// Pokemon transfer flags?
public uint Flags_1 { get => BitConverter.ToUInt32(Data, 0x099); set => BitConverter.GetBytes(value).CopyTo(Data, 0x099); }
@ -101,8 +100,8 @@ namespace PKHeX.Core
public string Nickname
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x1E, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(12 + 1, '\0')).CopyTo(Data, 0x1E);
get => StringConverter.GetString6(Data, 0x1E, 0x1A);
set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x1E);
}
public int Nature { get => Data[0x38]; set => Data[0x38] = (byte)value; }
@ -131,8 +130,8 @@ namespace PKHeX.Core
public string OT
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x4E, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, 0x4E);
get => StringConverter.GetString6(Data, 0x4E, 0x1A);
set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x4E);
}
public int Level { get => Data[0x68]; set => Data[0x68] = (byte)value; }

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
@ -42,8 +41,8 @@ namespace PKHeX.Core
public override string CardTitle
{
// Max len 36 char, followed by null terminator
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, CardStart + 2, 72));
set => Encoding.Unicode.GetBytes(value.PadRight(36, '\0')).CopyTo(Data, CardStart + 2);
get => StringConverter.GetString7(Data, CardStart + 2, 0x4A);
set => StringConverter.SetString7b(value, 36, 2, 37).CopyTo(Data, CardStart + 2);
}
private uint RawDate
@ -275,11 +274,11 @@ namespace PKHeX.Core
public bool IsNicknamed => false;
public int Language => 2;
public string GetNickname(int language) => Util.TrimFromZero(Encoding.Unicode.GetString(Data, GetNicknameOffset(language), 0x1A));
public void SetNickname(int language, string value) => Encoding.Unicode.GetBytes(value.PadRight(0x1A / 2, '\0')).CopyTo(Data, GetNicknameOffset(language));
public string GetNickname(int language) => StringConverter.GetString7(Data, GetNicknameOffset(language), 0x1A);
public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 2, 13).CopyTo(Data, GetNicknameOffset(language));
public string GetOT(int language) => Util.TrimFromZero(Encoding.Unicode.GetString(Data, GetOTOffset(language), 0x1A));
public void SetOT(int language, string value) => Encoding.Unicode.GetBytes(value.PadRight(0x1A / 2, '\0')).CopyTo(Data, GetOTOffset(language));
public string GetOT(int language) => StringConverter.GetString7(Data, GetOTOffset(language), 0x1A);
public void SetOT(int language, string value) => StringConverter.SetString7b(value, 12, 2, 13).CopyTo(Data, GetOTOffset(language));
private static int GetNicknameOffset(int language)
{

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
@ -41,8 +40,8 @@ namespace PKHeX.Core
public override string CardTitle
{
// Max len 36 char, followed by null terminator
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 2, 72));
set => Encoding.Unicode.GetBytes(value.PadRight(36, '\0')).CopyTo(Data, 2);
get => StringConverter.GetString6(Data, 2, 0x4A);
set => StringConverter.SetString6(value, 36, 37).CopyTo(Data, 2);
}
internal uint RawDate
@ -142,8 +141,8 @@ namespace PKHeX.Core
public string Nickname
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x86, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(12 + 1, '\0')).CopyTo(Data, 0x86);
get => StringConverter.GetString6(Data, 0x86, 0x1A);
set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x86);
}
public int Nature { get => (sbyte)Data[0xA0]; set => Data[0xA0] = (byte)value; }
@ -171,8 +170,8 @@ namespace PKHeX.Core
public override string OT_Name
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0xB6, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, 0xB6);
get => StringConverter.GetString6(Data, 0xB6, 0x1A);
set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0xB6);
}
public override int Level { get => Data[0xD0]; set => Data[0xD0] = (byte)value; }

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
@ -40,8 +39,8 @@ namespace PKHeX.Core
public override string CardTitle
{
// Max len 36 char, followed by null terminator
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 2, 72));
set => Encoding.Unicode.GetBytes(value.PadRight(36, '\0')).CopyTo(Data, 2);
get => StringConverter.GetString7(Data, 2, 0x4A);
set => StringConverter.SetString7(value, 36, 37).CopyTo(Data, 2);
}
internal uint RawDate
@ -183,8 +182,8 @@ namespace PKHeX.Core
public string Nickname
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x86, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(12 + 1, '\0')).CopyTo(Data, 0x86);
get => StringConverter.GetString7(Data, 0x86, 0x1A);
set => StringConverter.SetString7(value, 12, 13).CopyTo(Data, 0x86);
}
public int Nature { get => (sbyte)Data[0xA0]; set => Data[0xA0] = (byte)value; }
@ -213,8 +212,8 @@ namespace PKHeX.Core
public override string OT_Name
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0xB6, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, 0xB6);
get => StringConverter.GetString7(Data, 0xB6, 0x1A);
set => StringConverter.SetString7(value, 12, 13).CopyTo(Data, 0xB6);
}
public override int Level { get => Data[0xD0]; set => Data[0xD0] = (byte)value; }

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static PKHeX.Core.RibbonIndex;
namespace PKHeX.Core
@ -290,11 +289,11 @@ namespace PKHeX.Core
public bool IsNicknamed => false;
public int Language => 2;
public string GetNickname(int language) => Util.TrimFromZero(Encoding.Unicode.GetString(Data, GetNicknameOffset(language), 0x1A));
public void SetNickname(int language, string value) => Encoding.Unicode.GetBytes(value.PadRight(0x1A / 2, '\0')).CopyTo(Data, GetNicknameOffset(language));
public string GetNickname(int language) => StringConverter.GetString7(Data, GetNicknameOffset(language), 0x1A);
public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetNicknameOffset(language));
public string GetOT(int language) => Util.TrimFromZero(Encoding.Unicode.GetString(Data, GetOTOffset(language), 0x1A));
public void SetOT(int language, string value) => Encoding.Unicode.GetBytes(value.PadRight(0x1A / 2, '\0')).CopyTo(Data, GetOTOffset(language));
public string GetOT(int language) => StringConverter.GetString7(Data, GetOTOffset(language), 0x1A);
public void SetOT(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetOTOffset(language));
private static int GetNicknameOffset(int language)
{

View file

@ -1,5 +1,4 @@
using System;
using System.Text;
namespace PKHeX.Core
{
@ -85,8 +84,8 @@ namespace PKHeX.Core
public override string OT_Name
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x120, 0x1A));
set => Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, 0x120 + 0xB6); // careful with length
get => StringConverter.GetString7(Data, 0x120, 0x1A);
set => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, 0x120 + 0xB6); // careful with length
}
public LanguageID LanguageReceived

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -70,7 +71,11 @@ namespace PKHeX.Core
/// <returns>Decoded string.</returns>
public static string GetString5(byte[] data, int offset, int count)
{
return SanitizeString(Util.TrimFromFFFF(Encoding.Unicode.GetString(data, offset, count)));
var raw = Encoding.Unicode.GetString(data, offset, count);
var sb = new StringBuilder(raw);
Util.TrimFromFFFF(sb);
SanitizeString(sb);
return sb.ToString();
}
/// <summary>Gets the bytes for a Generation 5 string.</summary>
@ -81,12 +86,19 @@ namespace PKHeX.Core
/// <returns>Encoded data.</returns>
public static byte[] SetString5(string value, int maxLength, int padTo = 0, ushort padWith = 0)
{
if (value.Length > maxLength)
value = value.Substring(0, maxLength); // Hard cap
string temp = UnSanitizeString(value, 5)
.PadRight(value.Length + 1, (char)0xFFFF) // Null Terminator
.PadRight(padTo, (char)padWith); // Padding
return Encoding.Unicode.GetBytes(temp);
var sb = new StringBuilder(value, Math.Max(maxLength, padTo));
var delta = sb.Length - maxLength;
if (delta > 0)
sb.Remove(maxLength, delta);
// Replace Special Characters and add Terminator
UnSanitizeString(sb, 5);
sb.Append((char)0xFFFF);
var d2 = padTo - sb.Length;
if (d2 > 0)
sb.Append((char)padWith, d2);
return Encoding.Unicode.GetBytes(sb.ToString());
}
/// <summary>Converts Generation 6 encoded data to decoded string.</summary>
@ -96,7 +108,11 @@ namespace PKHeX.Core
/// <returns>Decoded string.</returns>
public static string GetString6(byte[] data, int offset, int count)
{
return SanitizeString(Util.TrimFromZero(Encoding.Unicode.GetString(data, offset, count)));
var raw = Encoding.Unicode.GetString(data, offset, count);
var sb = new StringBuilder(raw);
Util.TrimFromZero(sb);
SanitizeString(sb);
return sb.ToString();
}
/// <summary>Gets the bytes for a Generation 6 string.</summary>
@ -107,12 +123,19 @@ namespace PKHeX.Core
/// <returns>Encoded data.</returns>
public static byte[] SetString6(string value, int maxLength, int padTo = 0, ushort padWith = 0)
{
if (value.Length > maxLength)
value = value.Substring(0, maxLength); // Hard cap
string temp = UnSanitizeString(value, 6)
.PadRight(value.Length + 1, '\0') // Null Terminator
.PadRight(padTo, (char)padWith);
return Encoding.Unicode.GetBytes(temp);
var sb = new StringBuilder(value);
var delta = sb.Length - maxLength;
if (delta > 0)
sb.Remove(maxLength, delta);
// Replace Special Characters and add Terminator
UnSanitizeString(sb, 6);
sb.Append((char)0);
var d2 = padTo - sb.Length;
if (d2 > 0)
sb.Append((char)padWith, d2);
return Encoding.Unicode.GetBytes(sb.ToString());
}
/// <summary>Converts Generation 7 encoded data to decoded string.</summary>
@ -122,7 +145,12 @@ namespace PKHeX.Core
/// <returns>Decoded string.</returns>
public static string GetString7(byte[] data, int offset, int count)
{
return ConvertBin2StringG7_zh(SanitizeString(Util.TrimFromZero(Encoding.Unicode.GetString(data, offset, count))));
var raw = Encoding.Unicode.GetString(data, offset, count);
var sb = new StringBuilder(raw);
Util.TrimFromZero(sb);
SanitizeString(sb);
RemapChineseGlyphsBin2String(sb);
return sb.ToString();
}
/// <summary>Gets the bytes for a Generation 7 string.</summary>
@ -135,14 +163,21 @@ namespace PKHeX.Core
/// <returns>Encoded data.</returns>
public static byte[] SetString7(string value, int maxLength, int language, int padTo = 0, ushort padWith = 0, bool chinese = false)
{
var sb = new StringBuilder(value);
var delta = sb.Length - maxLength;
if (delta > 0)
sb.Remove(maxLength, delta);
if (chinese)
value = ConvertString2BinG7_zh(value, language);
if (value.Length > maxLength)
value = value.Substring(0, 12); // Hard cap
string temp = UnSanitizeString(value, 7)
.PadRight(value.Length + 1, '\0') // Null Terminator
.PadRight(padTo, (char)padWith);
return Encoding.Unicode.GetBytes(temp);
ConvertString2BinG7_zh(sb, language);
// Replace Special Characters and add Terminator
UnSanitizeString(sb, 7);
sb.Append((char)0);
var d2 = padTo - sb.Length;
if (d2 > 0)
sb.Append((char)padWith, d2);
return Encoding.Unicode.GetBytes(sb.ToString());
}
/// <summary>Gets the bytes for a Generation 7 string.</summary>
@ -155,34 +190,43 @@ namespace PKHeX.Core
/// <returns>Encoded data.</returns>
public static byte[] SetString7b(string value, int maxLength, int language, int padTo = 0, ushort padWith = 0, bool chinese = false)
{
var sb = new StringBuilder(value);
var delta = sb.Length - maxLength;
if (delta > 0)
sb.Remove(maxLength, delta);
if (chinese)
value = ConvertString2BinG7_zh(value, language);
if (value.Length > maxLength)
value = value.Substring(0, 12); // Hard cap
string temp = UnSanitizeString7b(value)
.PadRight(value.Length + 1, '\0') // Null Terminator
.PadRight(padTo, (char)padWith);
return Encoding.Unicode.GetBytes(temp);
ConvertString2BinG7_zh(sb, language);
// Replace Special Characters and add Terminator
UnSanitizeString7b(sb);
sb.Append((char)0);
var d2 = padTo - sb.Length;
if (d2 > 0)
sb.Append((char)padWith, d2);
return Encoding.Unicode.GetBytes(sb.ToString());
}
/// <summary>
/// Converts a Unicode string to Generation 7 in-game Chinese string.
/// </summary>
/// <param name="input">Unicode string.</param>
/// <param name="sb">Unicode string.</param>
/// <param name="lang">Detection of language for Traditional Chinese check</param>
/// <returns>In-game Chinese string.</returns>
private static string ConvertString2BinG7_zh(string input, int lang)
private static void ConvertString2BinG7_zh(StringBuilder sb, int lang)
{
var str = new StringBuilder();
// A string cannot contain a mix of CHS and CHT characters.
var input = sb.ToString();
bool traditional = input.Any(chr => G7_CHT.ContainsKey(chr) && !G7_CHS.ContainsKey(chr))
|| (lang == 10 && !input.Any(chr => G7_CHT.ContainsKey(chr) ^ G7_CHS.ContainsKey(chr))); // CHS and CHT have the same display name
var table = traditional ? G7_CHT : G7_CHS;
foreach (char chr in input)
str.Append(table.TryGetValue(chr, out int index) ? (char)(index + Gen7_ZH_Ofs) : chr);
return str.ToString();
for (int i = 0; i < sb.Length; i++)
{
var chr = sb[i];
if (table.TryGetValue(chr, out var index))
sb[i] = (char) (index + Gen7_ZH_Ofs);
}
}
/// <summary>
@ -190,24 +234,15 @@ namespace PKHeX.Core
/// </summary>
/// <param name="input">In-game Chinese string.</param>
/// <returns>Unicode string.</returns>
private static string ConvertBin2StringG7_zh(string input)
private static void RemapChineseGlyphsBin2String(StringBuilder input)
{
var str = new StringBuilder();
foreach (var val in input)
str.Append((char)GetGen7ChineseChar(val));
return str.ToString();
}
/// <summary>
/// Shifts a character from the Chinese character tables
/// </summary>
/// <param name="val">Input value to shift</param>
/// <returns>Shifted character</returns>
private static ushort GetGen7ChineseChar(ushort val)
{
if (Gen7_ZH_Ofs <= val && val < Gen7_ZH_Ofs + Gen7_ZH.Length)
return Gen7_ZH[val - Gen7_ZH_Ofs];
return val; // regular character
for (int i = 0; i < input.Length; i++)
{
char val = input[i];
if (val < Gen7_ZH_Ofs || val >= Gen7_ZH_Ofs + Gen7_ZH.Length)
continue;
input[i] = Gen7_ZH[val - Gen7_ZH_Ofs];
}
}
#region Gen 7 Chinese Character Tables
@ -231,19 +266,19 @@ namespace PKHeX.Core
/// <summary>
/// Converts full width to single width
/// </summary>
/// <param name="str">Input string to sanitize.</param>
/// <param name="s">Input string to sanitize.</param>
/// <returns></returns>
internal static string SanitizeString(string str)
internal static void SanitizeString(StringBuilder s)
{
if (str.Length == 0)
return str;
var s = str.Replace('', '\''); // Farfetch'd
if (s.Length == 0)
return;
s.Replace('', '\''); // Farfetch'd
// remap custom glyphs to unicode
s = s.Replace('\uE08F', '♀'); // ♀ (gen6+)
s = s.Replace('\uE08E', '♂'); // ♂ (gen6+)
s = s.Replace('\u246E', '♀'); // ♀ (gen5)
return s.Replace('\u246D', '♂'); // ♂ (gen5)
s.Replace('\uE08F', '♀'); // ♀ (gen6+)
s.Replace('\uE08E', '♂'); // ♂ (gen6+)
s.Replace('\u246E', '♀'); // ♀ (gen5)
s.Replace('\u246D', '♂'); // ♂ (gen5)
}
/// <summary>
@ -251,10 +286,10 @@ namespace PKHeX.Core
/// </summary>
/// <param name="str">Input string to set.</param>
/// <returns></returns>
private static string UnSanitizeString7b(string str)
private static void UnSanitizeString7b(StringBuilder str)
{
// gender chars always full width
return str.Replace('\'', ''); // Farfetch'd
str.Replace('\'', ''); // Farfetch'd
}
/// <summary>
@ -263,28 +298,28 @@ namespace PKHeX.Core
/// <param name="str">Input string to set.</param>
/// <param name="generation">Generation specific context</param>
/// <returns></returns>
internal static string UnSanitizeString(string str, int generation)
internal static void UnSanitizeString(StringBuilder str, int generation)
{
var s = str;
if (generation >= 6)
s = str.Replace('\'', ''); // Farfetch'd
str.Replace('\'', ''); // Farfetch'd
if (generation <= 5)
{
s = s.Replace('\u2640', '\u246E'); // ♀
return s.Replace('\u2642', '\u246D'); // ♂
str.Replace('\u2640', '\u246E'); // ♀
str.Replace('\u2642', '\u246D'); // ♂
return;
}
var context = str.Except(FullToHalf);
var context = str.ToString().Except(FullToHalf);
bool fullwidth = context.Select(c => c >> 12) // select the group the char belongs to
.Any(c => c is not (0 or 0xE) /* Latin, Special Symbols */);
if (fullwidth) // jp/ko/zh strings
return s; // keep as full width
return; // keep as full width
// Convert back to half width glyphs
s = s.Replace('\u2640', '\uE08F'); // ♀
return s.Replace('\u2642', '\uE08E'); // ♂
str.Replace('\u2640', '\uE08F'); // ♀
str.Replace('\u2642', '\uE08E'); // ♂
}
private static readonly char[] FullToHalf = {'\u2640', '\u2642'}; // ♀♂

View file

@ -84,7 +84,8 @@ namespace PKHeX.Core
break;
s.Append(c);
}
return StringConverter.SanitizeString(s.ToString());
StringConverter.SanitizeString(s);
return s.ToString();
}
/// <summary>

View file

@ -35,7 +35,8 @@ namespace PKHeX.Core
break;
s.Append(c);
}
return StringConverter.SanitizeString(s.ToString());
StringConverter.SanitizeString(s);
return s.ToString();
}
/// <summary>

View file

@ -27,7 +27,8 @@ namespace PKHeX.Core
break;
s.Append(c);
}
return StringConverter.SanitizeString(s.ToString());
StringConverter.SanitizeString(s);
return s.ToString();
}
/// <summary>
@ -76,7 +77,10 @@ namespace PKHeX.Core
/// <returns>Decoded string.</returns>
public static string GetBEString3(byte[] data, int offset, int count)
{
return Util.TrimFromZero(Encoding.BigEndianUnicode.GetString(data, offset, count));
var raw = Encoding.BigEndianUnicode.GetString(data, offset, count);
var sb = new StringBuilder(raw);
Util.TrimFromZero(sb);
return sb.ToString();
}
/// <summary>Gets the bytes for a Big Endian string.</summary>
@ -89,10 +93,14 @@ namespace PKHeX.Core
{
if (value.Length > maxLength)
value = value.Substring(0, maxLength); // Hard cap
var temp = StringConverter.SanitizeString(value)
.PadRight(value.Length + 1, (char)0) // Null Terminator
.PadRight(padTo, (char)padWith);
return Encoding.BigEndianUnicode.GetBytes(temp);
var sb = new StringBuilder(value);
StringConverter.SanitizeString(sb);
sb.Append('\0');
var delta = padTo - value.Length;
if (delta > 0)
sb.Append((char)padWith, delta);
var result = sb.ToString();
return Encoding.BigEndianUnicode.GetBytes(result);
}
/// <summary>

View file

@ -28,7 +28,8 @@ namespace PKHeX.Core
break;
s.Append((char)chr);
}
return StringConverter.SanitizeString(s.ToString());
StringConverter.SanitizeString(s);
return s.ToString();
}
/// <summary>Gets the bytes for a 4th Generation String</summary>
@ -39,16 +40,22 @@ namespace PKHeX.Core
/// <returns>Encoded data.</returns>
public static byte[] SetString4(string value, int maxLength, int padTo = 0, ushort padWith = 0)
{
if (value.Length > maxLength)
value = value.Substring(0, maxLength); // Hard cap
var temp = StringConverter.UnSanitizeString(value, 4) // Replace Special Characters and add Terminator
.PadRight(value.Length + 1, (char)0xFFFF) // Null Terminator
.PadRight(padTo, (char)padWith); // Padding
var sb = new StringBuilder(value);
var delta = sb.Length - maxLength;
if (delta > 0)
sb.Remove(maxLength, delta);
var data = new byte[temp.Length * 2];
for (int i = 0; i < temp.Length; i++)
// Replace Special Characters and add Terminator
StringConverter.UnSanitizeString(sb, 4);
sb.Append((char)0xFFFF);
var d2 = padTo - sb.Length;
if (d2 > 0)
sb.Append((char)padWith, d2);
var data = new byte[sb.Length * 2];
for (int i = 0; i < sb.Length; i++)
{
var chr = temp[i];
var chr = sb[i];
var val = ConvertChar2ValueG4(chr);
BitConverter.GetBytes(val).CopyTo(data, i * 2);
}
@ -75,7 +82,8 @@ namespace PKHeX.Core
break;
sb.Append((char)chr);
}
return StringConverter.SanitizeString(sb.ToString());
StringConverter.SanitizeString(sb);
return sb.ToString();
}
/// <summary>
@ -88,17 +96,22 @@ namespace PKHeX.Core
/// <returns>Byte array containing encoded character data</returns>
public static byte[] SetBEString4(string value, int maxLength, int padTo = 0, ushort padWith = 0)
{
if (value.Length > maxLength)
value = value.Substring(0, maxLength); // Hard cap
var sb = new StringBuilder(value);
var delta = sb.Length - maxLength;
if (delta > 0)
sb.Remove(maxLength, delta);
var temp = StringConverter.UnSanitizeString(value, 4) // Replace Special Characters and add Terminator
.PadRight(value.Length + 1, (char)0xFFFF) // Null Terminator
.PadRight(padTo, (char)padWith); // Padding
// Replace Special Characters and add Terminator
StringConverter.UnSanitizeString(sb, 4);
sb.Append((char)0xFFFF);
var d2 = padTo - sb.Length;
if (d2 > 0)
sb.Append((char)padWith, d2);
var data = new byte[temp.Length * 2];
for (int i = 0; i < temp.Length; i++)
var data = new byte[sb.Length * 2];
for (int i = 0; i < sb.Length; i++)
{
var chr = temp[i];
var chr = sb[i];
var val = ConvertChar2ValueG4(chr);
BigEndian.GetBytes(val).CopyTo(data, i * 2);
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core

View file

@ -153,8 +153,7 @@ namespace PKHeX.Core
private string GetOTName(int slot)
{
var ofs = 0x390 + (0x6FF00 * slot);
var str = Encoding.BigEndianUnicode.GetString(Data, ofs, 0x10);
return Util.TrimFromZero(str);
return StringConverter4.GetBEString4(Data, ofs, 0x10);
}
private void SetOTName(int slot, string name)
@ -181,8 +180,8 @@ namespace PKHeX.Core
if (BoxName < 0)
return $"BOX {box + 1}";
var str = Encoding.BigEndianUnicode.GetString(Data, BoxName + (box * BoxNameLength), BoxNameLength);
str = Util.TrimFromZero(str);
int ofs = BoxName + (box * BoxNameLength);
var str = GetString(ofs, BoxNameLength);
if (string.IsNullOrWhiteSpace(str))
return $"BOX {box + 1}";
return str;
@ -194,12 +193,11 @@ namespace PKHeX.Core
return;
int ofs = BoxName + (box * BoxNameLength);
var str = Encoding.BigEndianUnicode.GetString(Data, ofs, BoxNameLength);
str = Util.TrimFromZero(str);
var str = GetString(ofs, BoxNameLength);
if (string.IsNullOrWhiteSpace(str))
return;
var data = Encoding.BigEndianUnicode.GetBytes(value.PadLeft(BoxNameLength / 2, '\0'));
var data = SetString(value, BoxNameLength / 2, BoxNameLength / 2);
SetData(data, ofs);
}

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
@ -179,7 +178,7 @@ namespace PKHeX.Core
Data[ofs + 0x1E0] = hasEgg ? 1 : 0;
}
public override string JPEGTitle => HasJPPEGData ? string.Empty : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A));
public override string JPEGTitle => HasJPPEGData ? string.Empty : StringConverter.GetString6(Data, JPEG, 0x1A);
public override byte[] GetJPEGData() => HasJPPEGData ? Array.Empty<byte>() : GetData(JPEG + 0x54, 0xE004);
private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF;

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
@ -117,7 +116,7 @@ namespace PKHeX.Core
Util.GetBytesFromHexString(seed).CopyTo(Data, DaycareOffset + 0x1E8);
}
public override string JPEGTitle => HasJPPEGData ? string.Empty : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A));
public override string JPEGTitle => HasJPPEGData ? string.Empty : StringConverter.GetString6(Data, JPEG, 0x1A);
public override byte[] GetJPEGData() => HasJPPEGData ? Array.Empty<byte>() : GetData(JPEG + 0x54, 0xE004);
private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF;

View file

@ -86,7 +86,7 @@ namespace PKHeX.Core
SetData(result, 0);
}
public override string GetString(byte[] data, int offset, int length) => Util.TrimFromZero(Encoding.BigEndianUnicode.GetString(data, offset, length));
public override string GetString(byte[] data, int offset, int length) => StringConverter4.GetBEString4(data, offset, length);
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
{

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// ReSharper disable UnusedType.Local
namespace PKHeX.Core
@ -29,14 +28,14 @@ namespace PKHeX.Core
public string Debug1
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x6, 24));
set => Encoding.Unicode.GetBytes(value.PadRight(12, '\0')).CopyTo(Data, 0x6);
get => StringConverter.GetString6(Data, 0x6, 0x1A);
set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x6);
}
public string Debug2
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x50, 24));
set => Encoding.Unicode.GetBytes(value.PadRight(12, '\0')).CopyTo(Data, 0x50);
get => StringConverter.GetString6(Data, 0x50, 0x1A);
set => StringConverter.SetString6(value, 12, 13).CopyTo(Data, 0x50);
}
public ulong RNGConst1 { get => BitConverter.ToUInt64(Data, 0x1A0); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1A0); }
@ -54,9 +53,8 @@ namespace PKHeX.Core
string[] trainers = new string[PlayerCount];
for (int i = 0; i < PlayerCount; i++)
{
trainers[i] = Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0xEC + (0x1A * i), 0x1A));
if (string.IsNullOrWhiteSpace(trainers[i]))
trainers[i] = NPC;
var str = StringConverter.GetString6(Data, 0xEC + (0x1A * i), 0x1A);
trainers[i] = string.IsNullOrWhiteSpace(str) ? NPC : str;
}
return trainers;
}
@ -69,7 +67,7 @@ namespace PKHeX.Core
for (int i = 0; i < PlayerCount; i++)
{
string tr = value[i] == NPC ? string.Empty : value[i];
Encoding.Unicode.GetBytes(tr.PadRight(0x1A / 2)).CopyTo(Data, 0xEC + (0x1A * i));
StringConverter.SetString6(tr, 12, 13).CopyTo(Data, 0xEC + (0x1A * i));
}
}

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PKHeX.Core
{
@ -65,9 +64,8 @@ namespace PKHeX.Core
string[] trainers = new string[PlayerCount];
for (int i = 0; i < PlayerCount; i++)
{
trainers[i] = Util.TrimFromZero(Encoding.Unicode.GetString(Data, 0x12C + (0x1A * i), 0x1A));
if (string.IsNullOrWhiteSpace(trainers[i]))
trainers[i] = NPC;
var str = StringConverter.GetString7(Data, 0x12C + (0x1A * i), 0x1A);
trainers[i] = string.IsNullOrWhiteSpace(trainers[i]) ? NPC : str;
}
return trainers;
}
@ -80,7 +78,7 @@ namespace PKHeX.Core
for (int i = 0; i < PlayerCount; i++)
{
string tr = value[i] == NPC ? string.Empty : value[i];
Encoding.Unicode.GetBytes(tr.PadRight(0x1A / 2)).CopyTo(Data, 0xEC + (0x1A * i));
StringConverter.SetString7(tr, 12, 13).CopyTo(Data, 0xEC + (0x1A * i));
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PKHeX.Core
{
@ -48,8 +47,8 @@ namespace PKHeX.Core
if (pssID == 0)
return false; // no data
string otname = Util.TrimFromZero(Encoding.Unicode.GetString(Data, ofs + 8, 0x1A));
string message = Util.TrimFromZero(Encoding.Unicode.GetString(Data, ofs + 0x22, 0x22));
string otname = StringConverter.GetString6(Data, ofs + 8, 0x1A);
string message = StringConverter.GetString6(Data, ofs + 8 + 0x1A, 0x22);
// Trim terminated

View file

@ -1,5 +1,4 @@
using System;
using System.Text;
namespace PKHeX.Core
{
@ -34,14 +33,8 @@ namespace PKHeX.Core
public string FestivalPlazaName
{
get => Util.TrimFromZero(Encoding.Unicode.GetString(Data, Offset + 0x510, 0x2A));
set
{
const int max = 20;
if (value.Length > max)
value = value.Substring(0, max);
Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, Offset + 0x510);
}
get => StringConverter.GetString7(Data, Offset + 0x510, 0x2A);
set => StringConverter.SetString7(value, 20, 21).CopyTo(Data, Offset + 0x510);
}
public ushort FestaRank { get => BitConverter.ToUInt16(Data, Offset + 0x53A); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x53A); }

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace PKHeX.Core
{
@ -51,8 +52,7 @@ namespace PKHeX.Core
if (!IsNum(c))
continue;
result *= 10;
result += c;
result -= '0';
result += (uint)(c - '0');
}
return result;
}
@ -158,7 +158,7 @@ namespace PKHeX.Core
/// </summary>
/// <param name="input">String to trim.</param>
/// <returns>Trimmed string.</returns>
public static string TrimFromFFFF(string input) => TrimFromFirst(input, (char)0xFFFF);
public static void TrimFromFFFF(StringBuilder input) => TrimFromFirst(input, (char)0xFFFF);
/// <summary>
/// Trims a string at the first instance of a 0x0000 terminator.
@ -167,6 +167,13 @@ namespace PKHeX.Core
/// <returns>Trimmed string.</returns>
public static string TrimFromZero(string input) => TrimFromFirst(input, '\0');
/// <summary>
/// Trims a string at the first instance of a 0x0000 terminator.
/// </summary>
/// <param name="input">String to trim.</param>
/// <returns>Trimmed string.</returns>
public static void TrimFromZero(StringBuilder input) => TrimFromFirst(input, '\0');
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string TrimFromFirst(string input, char c)
{
@ -174,6 +181,16 @@ namespace PKHeX.Core
return index < 0 ? input : input.Substring(0, index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void TrimFromFirst(StringBuilder input, char c)
{
for (int i = 0; i < input.Length; i++)
{
if (input[i] == c)
input.Remove(i, input.Length - i);
}
}
public static Dictionary<string, int>[] GetMultiDictionary(IReadOnlyList<IReadOnlyList<string>> nameArray)
{
var result = new Dictionary<string, int>[nameArray.Count];

View file

@ -152,8 +152,8 @@ namespace PKHeX.WinForms
uint shiny = slgf >> 14 & 0x1;
// uint unkn = slgf >> 15;
string nickname = Util.TrimFromZero(Encoding.Unicode.GetString(data, offset + 0x18, 24));
string OTname = Util.TrimFromZero(Encoding.Unicode.GetString(data, offset + 0x30, 24));
string nickname = StringConverter.GetString6(data, offset + 0x18, 24);
string OTname = StringConverter.GetString6(data, offset + 0x30, 24);
string genderstr = gendersymbols[(int)gender];
string shinystr = shiny == 1 ? "Yes" : "No";
@ -200,8 +200,8 @@ namespace PKHeX.WinForms
TB_TID.Text = BitConverter.ToUInt16(data, offset + 0x10).ToString("00000");
TB_SID.Text = BitConverter.ToUInt16(data, offset + 0x12).ToString("00000");
TB_Nickname.Text = Util.TrimFromZero(Encoding.Unicode.GetString(data, offset + 0x18, 24));
TB_OT.Text = Util.TrimFromZero(Encoding.Unicode.GetString(data, offset + 0x30, 24));
TB_Nickname.Text = StringConverter.GetString6(data, offset + 0x18, 24);
TB_OT.Text = StringConverter.GetString6(data, offset + 0x30, 24);
uint slgf = BitConverter.ToUInt32(data, offset + 0x14);
uint form = slgf & 0x1F;
@ -427,8 +427,7 @@ namespace PKHeX.WinForms
tb.Text = d.FinalString;
d.FinalBytes.CopyTo(data, offset + 0x18);
string nickname = Util.TrimFromZero(Encoding.Unicode.GetString(data, offset + 0x18, 24));
TB_Nickname.Text = nickname;
TB_Nickname.Text = StringConverter.GetString6(data, offset + 0x18, 24);
}
}
}

View file

@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PKHeX.Core;
@ -73,11 +72,11 @@ namespace PKHeX.WinForms
int playeroff = SAV.SecretBase + 0x326;
int favoff = SAV.SecretBase + 0x63A;
string OT = Util.TrimFromZero(Encoding.Unicode.GetString(SAV.Data, playeroff + 0x218, 0x1A));
string OT = StringConverter.GetString6(SAV.Data, playeroff + 0x218, 0x1A);
LB_Favorite.Items.Add($"* {OT}");
for (int i = 0; i < 30; i++)
{
string BaseTrainer = Util.TrimFromZero(Encoding.Unicode.GetString(SAV.Data, favoff + (i * 0x3E0) + 0x218, 0x1A));
string BaseTrainer = StringConverter.GetString6(SAV.Data, favoff + (i * 0x3E0) + 0x218, 0x1A);
if (BaseTrainer.Length < 1 || BaseTrainer[0] == '\0')
BaseTrainer = "Empty";
LB_Favorite.Items.Add($"{i} {BaseTrainer}");
@ -520,7 +519,7 @@ namespace PKHeX.WinForms
int index = LB_Favorite.SelectedIndex - 1;
int favoff = SAV.SecretBase + 0x63A;
string BaseTrainer = Util.TrimFromZero(Encoding.Unicode.GetString(SAV.Data, favoff + (index * 0x3E0) + 0x218, 0x1A));
string BaseTrainer = StringConverter.GetString6(SAV.Data, favoff + (index * 0x3E0) + 0x218, 0x1A);
if (string.IsNullOrEmpty(BaseTrainer))
BaseTrainer = "Empty";