PKHeX/PKHeX.Core/PKM/Shared/GBPKML.cs
Kurt 103aa9aa4b
Revise EncounterArea and EncounterType for clarity (#3228)
EncounterArea now stores a more specific type'd array for encounter slots. Better iteration and less casting, as the nonspecific `Slots` fetch is rarely referenced.

EncounterType renamed to GroundTile to reflect how it actually works in Gen4. Was previously an ambiguous field that was clarified a little; we can describe it a little better now. Keep the GUI the same to not scare the end users.

Change Trash Byte properties to get/set a Span. Trash Byte legality checking easier on the garbage collector?
2021-06-29 20:58:06 -07:00

118 lines
4.4 KiB
C#

using System;
namespace PKHeX.Core
{
/// <summary>
/// Mainline format for Generation 1 &amp; 2 <see cref="PKM"/> objects.
/// </summary>
/// <remarks>This format stores <see cref="PKM.Nickname"/> and <see cref="PKM.OT_Name"/> in buffers separate from the rest of the details.</remarks>
public abstract class GBPKML : GBPKM
{
internal const int StringLengthJapanese = 6;
internal const int StringLengthNotJapan = 11;
public sealed override int OTLength => Japanese ? 5 : 7;
public sealed override int NickLength => Japanese ? 5 : 10;
public sealed override bool Japanese => RawOT.Length == StringLengthJapanese;
internal readonly byte[] RawOT;
internal readonly byte[] RawNickname;
// Trash Bytes
public sealed override Span<byte> Nickname_Trash { get => RawNickname; set { if (value.Length == RawNickname.Length) value.CopyTo(RawNickname); } }
public sealed override Span<byte> OT_Trash { get => RawOT; set { if (value.Length == RawOT.Length) value.CopyTo(RawOT); } }
protected GBPKML(int size, bool jp = false) : base(size)
{
int strLen = jp ? StringLengthJapanese : StringLengthNotJapan;
// initialize string buffers
RawOT = new byte[strLen];
RawNickname = new byte[strLen];
for (int i = 0; i < RawOT.Length; i++)
RawOT[i] = RawNickname[i] = StringConverter12.G1TerminatorCode;
}
protected GBPKML(byte[] data, bool jp = false) : base(data)
{
int strLen = jp ? StringLengthJapanese : StringLengthNotJapan;
// initialize string buffers
RawOT = new byte[strLen];
RawNickname = new byte[strLen];
for (int i = 0; i < RawOT.Length; i++)
RawOT[i] = RawNickname[i] = StringConverter12.G1TerminatorCode;
}
public override void SetNotNicknamed(int language) => GetNonNickname(language).CopyTo(RawNickname);
protected override byte[] GetNonNickname(int language)
{
var name = SpeciesName.GetSpeciesNameGeneration(Species, language, Format);
var len = Nickname_Trash.Length;
var data = SetString(name, len, len, 0x50);
if (!Korean)
{
// Decimal point<->period fix
for (int i = 0; i < data.Length; i++)
{
if (data[i] == 0xF2)
data[i] = 0xE8;
}
}
return data;
}
private byte[] SetString(string value, int maxLength, int padTo = 0, byte padWith = 0)
{
if (Korean)
return StringConverter2KOR.SetString2KOR(value, maxLength - 1, padTo, padWith);
return StringConverter12.SetString1(value, maxLength - 1, Japanese, padTo, padWith);
}
public sealed override string Nickname
{
get
{
if (Korean)
return StringConverter2KOR.GetString2KOR(RawNickname, 0, RawNickname.Length);
return StringConverter12.GetString1(RawNickname, 0, RawNickname.Length, Japanese);
}
set
{
if (!IsNicknamed && Nickname == value)
return;
SetStringKeepTerminatorStyle(value, RawNickname);
}
}
public sealed override string OT_Name
{
get
{
if (Korean)
return StringConverter2KOR.GetString2KOR(RawOT, 0, RawOT.Length);
return StringConverter12.GetString1(RawOT, 0, RawOT.Length, Japanese);
}
set
{
if (value == OT_Name)
return;
SetStringKeepTerminatorStyle(value, RawOT);
}
}
private void SetStringKeepTerminatorStyle(string value, byte[] exist)
{
// Reset the destination buffer based on the termination style of the existing string.
bool zeroed = Array.IndexOf(exist, (byte)0) != -1;
byte fill = zeroed ? (byte)0 : StringConverter12.G1TerminatorCode;
for (int i = 0; i < exist.Length; i++)
exist[i] = fill;
int finalLength = Math.Min(value.Length + 1, exist.Length);
byte[] strdata = SetString(value, finalLength);
strdata.CopyTo(exist, 0);
}
}
}