Misc tweaks

Add more xmldoc
Move files/functions to better spot
Less allocation on hover glow
fix honeytree dppt group/slot/shake rand call (ty real.96)
This commit is contained in:
Kurt 2024-02-03 14:11:17 -06:00
parent 3eb6ff2c9e
commit 10c5adadb8
47 changed files with 653 additions and 305 deletions

View file

@ -128,7 +128,7 @@ public sealed class GameStrings : IBasicStrings
Sanitize(); Sanitize();
g4items = (string[])itemlist.Clone(); g4items = [..itemlist];
Get("mail4").CopyTo(g4items, 137); Get("mail4").CopyTo(g4items, 137);
} }

View file

@ -20,8 +20,8 @@ public interface ILocationSet
string GetLocationName(int locationID); string GetLocationName(int locationID);
/// <summary> /// <summary>
/// Gets all groups -- ONLY USE FOR UNIT TESTING. /// Gets all groups -- not really useful besides unit testing.
/// </summary> /// </summary>
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<(int Bank, string[] Names)> GetAll(); IEnumerable<(int Bank, ReadOnlyMemory<string> Names)> GetAll();
} }

View file

@ -18,7 +18,7 @@ public sealed record LocationSet0(string[] Met0) : ILocationSet
return Met0[locationID]; return Met0[locationID];
} }
public IEnumerable<(int Bank, string[] Names)> GetAll() public IEnumerable<(int Bank, ReadOnlyMemory<string> Names)> GetAll()
{ {
yield return (0, Met0); yield return (0, Met0);
} }

View file

@ -27,7 +27,7 @@ public sealed record LocationSet4(string[] Met0, string[] Met2, string[] Met3) :
return names[index]; return names[index];
} }
public IEnumerable<(int Bank, string[] Names)> GetAll() public IEnumerable<(int Bank, ReadOnlyMemory<string> Names)> GetAll()
{ {
yield return (0, Met0); yield return (0, Met0);
yield return (2, Met2); yield return (2, Met2);

View file

@ -29,7 +29,7 @@ public sealed record LocationSet6(string[] Met0, string[] Met3, string[] Met4, s
return names[index]; return names[index];
} }
public IEnumerable<(int Bank, string[] Names)> GetAll() public IEnumerable<(int Bank, ReadOnlyMemory<string> Names)> GetAll()
{ {
yield return (0, Met0); yield return (0, Met0);
yield return (3, Met3); yield return (3, Met3);

View file

@ -96,7 +96,7 @@ public sealed record EncounterStatic7b(GameVersion Version)
return false; return false;
if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount) if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount)
return false; return false;
if (IVs.IsSpecified && !Legal.GetIsFixedIVSequenceValidSkipRand(IVs, pk)) if (IVs.IsSpecified && !Legal.GetIsFixedIVSequenceValidNoRand(IVs, pk))
return false; return false;
return true; return true;
} }

View file

@ -3,6 +3,9 @@ using static PKHeX.Core.GameVersion;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.Gen3"/>.
/// </summary>
public static class EggHatchLocation3 public static class EggHatchLocation3
{ {
private const byte MaskRS = 1 << 0; // 1 private const byte MaskRS = 1 << 0; // 1
@ -10,6 +13,9 @@ public static class EggHatchLocation3
private const byte MaskFRLG = 1 << 2; // 4 private const byte MaskFRLG = 1 << 2; // 4
private const byte MaskAll = MaskRS | MaskE | MaskFRLG; // 7 private const byte MaskAll = MaskRS | MaskE | MaskFRLG; // 7
/// <summary>
/// Returns true if the hatch location is valid for the specified Generation 3 game.
/// </summary>
public static bool IsValidMet3(int location, GameVersion game) => game switch public static bool IsValidMet3(int location, GameVersion game) => game switch
{ {
R or S => IsValidMet3RS(location), R or S => IsValidMet3RS(location),
@ -18,9 +24,24 @@ public static class EggHatchLocation3
_ => false, _ => false,
}; };
/// <summary>
/// Returns true if the hatch location is valid for Ruby and Sapphire.
/// </summary>
public static bool IsValidMet3RS(int location) => HasLocationFlag(LocationPermitted3, MaskRS, location); public static bool IsValidMet3RS(int location) => HasLocationFlag(LocationPermitted3, MaskRS, location);
/// <summary>
/// Returns true if the hatch location is valid for Emerald.
/// </summary>
public static bool IsValidMet3E(int location) => HasLocationFlag(LocationPermitted3, MaskE, location); public static bool IsValidMet3E(int location) => HasLocationFlag(LocationPermitted3, MaskE, location);
/// <summary>
/// Returns true if the hatch location is valid for FireRed and LeafGreen.
/// </summary>
public static bool IsValidMet3FRLG(int location) => HasLocationFlag(LocationPermitted3, MaskFRLG, location); public static bool IsValidMet3FRLG(int location) => HasLocationFlag(LocationPermitted3, MaskFRLG, location);
/// <summary>
/// Returns true if the hatch location is valid for any Generation 3 game.
/// </summary>
public static bool IsValidMet3Any(int location) => HasLocationFlag(LocationPermitted3, MaskAll, location); public static bool IsValidMet3Any(int location) => HasLocationFlag(LocationPermitted3, MaskAll, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location) private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)

View file

@ -3,6 +3,9 @@ using static PKHeX.Core.GameVersion;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.Gen4"/>.
/// </summary>
public static class EggHatchLocation4 public static class EggHatchLocation4
{ {
private const byte MaskDP = 1 << 0; // 1 private const byte MaskDP = 1 << 0; // 1
@ -10,6 +13,9 @@ public static class EggHatchLocation4
private const byte MaskHGSS = 1 << 2; // 4 private const byte MaskHGSS = 1 << 2; // 4
private const byte MaskAll4 = MaskDP | MaskPt | MaskHGSS; // 7 private const byte MaskAll4 = MaskDP | MaskPt | MaskHGSS; // 7
/// <summary>
/// Returns true if the hatch location is valid for the specified Generation 4 game.
/// </summary>
public static bool IsValidMet4(int location, GameVersion game) => game switch public static bool IsValidMet4(int location, GameVersion game) => game switch
{ {
D or P => IsValidMet4DP(location), D or P => IsValidMet4DP(location),
@ -18,9 +24,24 @@ public static class EggHatchLocation4
_ => false, _ => false,
}; };
/// <summary>
/// Returns true if the hatch location is valid for Diamond and Pearl.
/// </summary>
public static bool IsValidMet4DP(int location) => HasLocationFlag(LocationPermitted4, MaskDP, location); public static bool IsValidMet4DP(int location) => HasLocationFlag(LocationPermitted4, MaskDP, location);
/// <summary>
/// Returns true if the hatch location is valid for Platinum.
/// </summary>
public static bool IsValidMet4Pt(int location) => HasLocationFlag(LocationPermitted4, MaskPt, location); public static bool IsValidMet4Pt(int location) => HasLocationFlag(LocationPermitted4, MaskPt, location);
/// <summary>
/// Returns true if the hatch location is valid for HeartGold and SoulSilver.
/// </summary>
public static bool IsValidMet4HGSS(int location) => HasLocationFlag(LocationPermitted4, MaskHGSS, location); public static bool IsValidMet4HGSS(int location) => HasLocationFlag(LocationPermitted4, MaskHGSS, location);
/// <summary>
/// Returns true if the hatch location is valid for any Generation 4 game.
/// </summary>
public static bool IsValidMet4Any(int location) => HasLocationFlag(LocationPermitted4, MaskAll4, location); public static bool IsValidMet4Any(int location) => HasLocationFlag(LocationPermitted4, MaskAll4, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location) private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)

View file

@ -3,6 +3,9 @@ using static PKHeX.Core.GameVersion;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.Gen5"/>.
/// </summary>
public static class EggHatchLocation5 public static class EggHatchLocation5
{ {
private const byte MaskWhite = 1 << 0; // 1 private const byte MaskWhite = 1 << 0; // 1
@ -10,6 +13,9 @@ public static class EggHatchLocation5
private const byte MaskWhite2 = 1 << 2; // 4 private const byte MaskWhite2 = 1 << 2; // 4
private const byte MaskBlack2 = 1 << 3; // 8 private const byte MaskBlack2 = 1 << 3; // 8
/// <summary>
/// Returns true if the location is valid for the specified Generation 5 game.
/// </summary>
public static bool IsValidMet5(int location, GameVersion game) public static bool IsValidMet5(int location, GameVersion game)
{ {
var shift = (uint)(game - W); var shift = (uint)(game - W);
@ -20,9 +26,24 @@ public static class EggHatchLocation5
return HasLocationFlag(LocationPermitted5, mask, location); return HasLocationFlag(LocationPermitted5, mask, location);
} }
/// <summary>
/// Returns true if the hatch location is valid for White.
/// </summary>
public static bool IsValidMet5W(int location) => HasLocationFlag(LocationPermitted5, MaskWhite, location); public static bool IsValidMet5W(int location) => HasLocationFlag(LocationPermitted5, MaskWhite, location);
/// <summary>
/// Returns true if the hatch location is valid for Black.
/// </summary>
public static bool IsValidMet5B(int location) => HasLocationFlag(LocationPermitted5, MaskBlack, location); public static bool IsValidMet5B(int location) => HasLocationFlag(LocationPermitted5, MaskBlack, location);
/// <summary>
/// Returns true if the hatch location is valid for White 2.
/// </summary>
public static bool IsValidMet5W2(int location) => HasLocationFlag(LocationPermitted5, MaskWhite2, location); public static bool IsValidMet5W2(int location) => HasLocationFlag(LocationPermitted5, MaskWhite2, location);
/// <summary>
/// Returns true if the hatch location is valid for Black 2.
/// </summary>
public static bool IsValidMet5B2(int location) => HasLocationFlag(LocationPermitted5, MaskBlack2, location); public static bool IsValidMet5B2(int location) => HasLocationFlag(LocationPermitted5, MaskBlack2, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location) private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)

View file

@ -1,7 +1,13 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.Gen6"/>.
/// </summary>
public static class EggHatchLocation6 public static class EggHatchLocation6
{ {
/// <summary>
/// Returns true if the hatch location is valid for X and Y.
/// </summary>
public static bool IsValidMet6XY(int location) public static bool IsValidMet6XY(int location)
{ {
const int min = 6; const int min = 6;
@ -15,6 +21,9 @@ public static class EggHatchLocation6
return location != 80; // unused return location != 80; // unused
} }
/// <summary>
/// Returns true if the hatch location is valid for Omega Ruby and Alpha Sapphire.
/// </summary>
public static bool IsValidMet6AO(int location) public static bool IsValidMet6AO(int location)
{ {
const int min = 170; const int min = 170;

View file

@ -2,11 +2,17 @@ using System;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.Gen7"/>.
/// </summary>
public static class EggHatchLocation7 public static class EggHatchLocation7
{ {
private const byte MaskSM = 1 << 0; // 1 private const byte MaskSM = 1 << 0; // 1
private const byte MaskUSUM = 1 << 1; // 2 private const byte MaskUSUM = 1 << 1; // 2
/// <summary>
/// Returns true if the hatch location is valid for Sun and Moon.
/// </summary>
public static bool IsValidMet7SM(int location) public static bool IsValidMet7SM(int location)
{ {
if (HasLocationFlag(LocationPermitted7, MaskSM, location)) if (HasLocationFlag(LocationPermitted7, MaskSM, location))
@ -14,6 +20,9 @@ public static class EggHatchLocation7
return location == Locations.Pelago7; // 30016 return location == Locations.Pelago7; // 30016
} }
/// <summary>
/// Returns true if the hatch location is valid for Ultra Sun and Ultra Moon.
/// </summary>
public static bool IsValidMet7USUM(int location) public static bool IsValidMet7USUM(int location)
{ {
if (HasLocationFlag(LocationPermitted7, MaskUSUM, location)) if (HasLocationFlag(LocationPermitted7, MaskUSUM, location))

View file

@ -2,8 +2,14 @@ using System;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.SWSH"/>.
/// </summary>
public static class EggHatchLocation8 public static class EggHatchLocation8
{ {
/// <summary>
/// Returns true if the hatch location is valid for Sword and Shield.
/// </summary>
public static bool IsValidMet8SWSH(int location) public static bool IsValidMet8SWSH(int location)
{ {
if (location % 2 != 0) if (location % 2 != 0)

View file

@ -2,12 +2,22 @@ using System;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.BDSP"/>.
/// </summary>
public static class EggHatchLocation8b public static class EggHatchLocation8b
{ {
private const byte MaskBD = 1 << 0; // 1 private const byte MaskBD = 1 << 0; // 1
private const byte MaskSP = 1 << 1; // 2 private const byte MaskSP = 1 << 1; // 2
/// <summary>
/// Returns true if the hatch location is valid for Brilliant Diamond.
/// </summary>
public static bool IsValidMet8BD(int location) => HasLocationFlag(LocationPermitted8b, MaskBD, location); public static bool IsValidMet8BD(int location) => HasLocationFlag(LocationPermitted8b, MaskBD, location);
/// <summary>
/// Returns true if the hatch location is valid for Shining Pearl.
/// </summary>
public static bool IsValidMet8SP(int location) => HasLocationFlag(LocationPermitted8b, MaskSP, location); public static bool IsValidMet8SP(int location) => HasLocationFlag(LocationPermitted8b, MaskSP, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location) private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)

View file

@ -2,11 +2,22 @@ using System;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Hatch Location validity for <see cref="GameVersion.SV"/>.
/// </summary>
public static class EggHatchLocation9 public static class EggHatchLocation9
{ {
private const byte MaskScarlet = 1 << 0; // 1 private const byte MaskScarlet = 1 << 0; // 1
private const byte MaskViolet = 1 << 1; // 2 private const byte MaskViolet = 1 << 1; // 2
/// <summary>
/// Returns true if the hatch location is valid for Scarlet.
/// </summary>
public static bool IsValidMet9SL(int location) => HasLocationFlag(LocationPermitted9, MaskScarlet, location); public static bool IsValidMet9SL(int location) => HasLocationFlag(LocationPermitted9, MaskScarlet, location);
/// <summary>
/// Returns true if the hatch location is valid for Violet.
/// </summary>
public static bool IsValidMet9VL(int location) => HasLocationFlag(LocationPermitted9, MaskViolet, location); public static bool IsValidMet9VL(int location) => HasLocationFlag(LocationPermitted9, MaskViolet, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location) private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)

View file

@ -87,8 +87,18 @@ public static class Tera9RNG
/// </summary> /// </summary>
private static bool IsMatchType(IPersonalType pi, in byte original) => original == pi.Type1 || original == pi.Type2; private static bool IsMatchType(IPersonalType pi, in byte original) => original == pi.Type1 || original == pi.Type2;
/// <summary>
/// Checks if the original Tera Type matches the Personal Info type for the specified form.
/// </summary>
/// <param name="species">Egg Species</param>
/// <param name="form">Egg Form</param>
/// <param name="original">Original Tera Type from the Entity</param>
/// <returns>True if the Tera Type matches the expected Personal Info type</returns>
/// <remarks>
/// Special consideration is required as some eggs can change forms that have different Personal Info types.
/// </remarks>
public static bool IsMatchTeraTypePersonalEgg(in ushort species, in byte form, in byte original) => public static bool IsMatchTeraTypePersonalEgg(in ushort species, in byte form, in byte original) =>
FormInfo.IsFormChangeEgg(species) FormInfo.IsFormChangeEggTypes(species)
? IsMatchTeraTypePersonalAnyForm(species, original) ? IsMatchTeraTypePersonalAnyForm(species, original)
: IsMatchTeraTypePersonal(species, form, original); : IsMatchTeraTypePersonal(species, form, original);

View file

@ -113,7 +113,12 @@ public static class FormInfo
return false; return false;
} }
public static bool IsFormChangeEgg(ushort species) => FormChangeEgg.Contains(species); /// <summary>
/// Checks if the form can be changed for a given (baby) species into a different-typed form.
/// </summary>
/// <param name="species">Baby species</param>
/// <returns>True if the species can change form to another with a different typing, false if it cannot.</returns>
public static bool IsFormChangeEggTypes(ushort species) => FormChangeEgg.Contains(species);
private static ReadOnlySpan<ushort> FormChangeEgg => private static ReadOnlySpan<ushort> FormChangeEgg =>
[ [
@ -296,6 +301,13 @@ public static class FormInfo
return --form; return --form;
} }
/// <summary>
/// Checks if the <see cref="form"/> for the <see cref="species"/> is a Lord Form from Legends: Arceus.
/// </summary>
/// <param name="species">Entity species</param>
/// <param name="form">Entity form</param>
/// <param name="context">Current context</param>
/// <returns>True if the form is a Lord Form, false if it is not.</returns>
public static bool IsLordForm(ushort species, byte form, EntityContext context) public static bool IsLordForm(ushort species, byte form, EntityContext context)
{ {
if (context != EntityContext.Gen8a) if (context != EntityContext.Gen8a)

View file

@ -11,5 +11,9 @@ public interface IHomeTrack
/// </summary> /// </summary>
ulong Tracker { get; set; } ulong Tracker { get; set; }
/// <summary>
/// Simple check if a <see cref="Tracker"/> is present.
/// </summary>
/// <remarks>Does not ensure that it is a valid tracker, just non-zero.</remarks>
bool HasTracker => Tracker != 0; bool HasTracker => Tracker != 0;
} }

View file

@ -1,17 +1,21 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// <inheritdoc cref="IObedienceLevelReadOnly"/>
/// </summary>
public interface IObedienceLevel : IObedienceLevelReadOnly public interface IObedienceLevel : IObedienceLevelReadOnly
{ {
/// <summary>
/// <inheritdoc cref="IObedienceLevelReadOnly.Obedience_Level"/>
/// </summary>
new byte Obedience_Level { get; set; } new byte Obedience_Level { get; set; }
} }
public interface IObedienceLevelReadOnly
{
byte Obedience_Level { get; } // no setter, use for Encounters
}
public static class ObedienceExtensions public static class ObedienceExtensions
{ {
/// <summary>
/// Suggests the <see cref="IObedienceLevelReadOnly.Obedience_Level"/> for the entity.
/// </summary>
public static byte GetSuggestedObedienceLevel(this IObedienceLevelReadOnly _, PKM entity, int originalMet) public static byte GetSuggestedObedienceLevel(this IObedienceLevelReadOnly _, PKM entity, int originalMet)
{ {
if (entity.Species is (int)Species.Koraidon or (int)Species.Miraidon && entity is PK9 { FormArgument: not 0 }) if (entity.Species is (int)Species.Koraidon or (int)Species.Miraidon && entity is PK9 { FormArgument: not 0 })

View file

@ -0,0 +1,11 @@
/// <summary>
/// Exposes information about the level the Pokémon was obtained.
/// </summary>
public interface IObedienceLevelReadOnly
{
/// <summary>
/// Indicates the level the Pokémon was obtained by the current handler.
/// </summary>
byte Obedience_Level { get; } // no setter, use for Encounters
}

View file

@ -75,7 +75,8 @@ public static class Language
German => "de", German => "de",
Spanish => "es", Spanish => "es",
Korean => "ko", Korean => "ko",
ChineseS or ChineseT => "zh", ChineseS => "zh",
ChineseT => "zh2",
_ => GameLanguage.DefaultLanguage, _ => GameLanguage.DefaultLanguage,
}; };

View file

@ -10,12 +10,60 @@ namespace PKHeX.Core;
/// </summary> /// </summary>
public static class SCBlockUtil public static class SCBlockUtil
{ {
/// <summary>
/// Create a blank block array using the provided <see cref="arr"/> definition.
/// </summary>
/// <param name="arr">Block specification tuples (key, size)</param>
/// <returns>List of blocks</returns>
internal static SCBlock[] GetBlankBlockArray(ReadOnlySpan<uint> arr)
{
var blocks = new SCBlock[arr.Length / 2];
for (int i = 0; i < blocks.Length; i++)
{
int index = i * 2;
var key = arr[index];
var length = (int)arr[index + 1];
var dummy = new byte[length];
blocks[i] = new SCBlock(key, SCTypeCode.None, dummy);
}
return blocks;
}
#if DEBUG
/// <summary>
/// Exports the block keys &amp; sizes in a human-readable format.
/// </summary>
public static IEnumerable<string> RipSizes(IReadOnlyCollection<SCBlock> blocks)
{
int ctr = 0;
foreach (var block in blocks)
{
if (block.Data.Length == 0)
continue;
if (ctr == 4)
{
yield return Environment.NewLine;
ctr = 0;
}
yield return $"0x{block.Key:X8}, 0x{block.Data.Length:X5}, ";
ctr++;
}
}
#endif
/// <summary>
/// Concatenates all blocks into a single file with some metadata sprinkled in.
/// </summary>
/// <param name="blocks">Ascending-key ordered blocks</param>
/// <param name="path">File path to write to</param>
/// <param name="option">Export options</param>
public static void ExportAllBlocksAsSingleFile(IReadOnlyList<SCBlock> blocks, string path, SCBlockExportOption option = SCBlockExportOption.All) public static void ExportAllBlocksAsSingleFile(IReadOnlyList<SCBlock> blocks, string path, SCBlockExportOption option = SCBlockExportOption.All)
{ {
var data = ExportAllBlocks(blocks, option); var data = ExportAllBlocks(blocks, option);
File.WriteAllBytes(path, data); File.WriteAllBytes(path, data);
} }
/// <inheritdoc cref="ExportAllBlocksAsSingleFile(IReadOnlyList{SCBlock}, string, SCBlockExportOption)"/>
public static byte[] ExportAllBlocks(IReadOnlyList<SCBlock> blocks, SCBlockExportOption option = SCBlockExportOption.None) public static byte[] ExportAllBlocks(IReadOnlyList<SCBlock> blocks, SCBlockExportOption option = SCBlockExportOption.None)
{ {
if (option == SCBlockExportOption.None) if (option == SCBlockExportOption.None)
@ -28,6 +76,9 @@ public static class SCBlockUtil
return stream.ToArray(); return stream.ToArray();
} }
/// <summary>
/// Writes the block to the <see cref="bw"/> stream with the specified <see cref="option"/>.
/// </summary>
private static void ExportBlock(SCBlock block, BinaryWriter bw, int blockIndex, SCBlockExportOption option) private static void ExportBlock(SCBlock block, BinaryWriter bw, int blockIndex, SCBlockExportOption option)
{ {
if (option.HasFlag(SCBlockExportOption.DataOnly) && block.Data.Length == 0) if (option.HasFlag(SCBlockExportOption.DataOnly) && block.Data.Length == 0)
@ -48,6 +99,11 @@ public static class SCBlockUtil
bw.Write(block.Data); bw.Write(block.Data);
} }
/// <summary>
/// Get the suggested file name for a <see cref="block"/>, without the file extension.
/// </summary>
/// <param name="block"></param>
/// <returns></returns>
public static string GetBlockFileNameWithoutExtension(SCBlock block) public static string GetBlockFileNameWithoutExtension(SCBlock block)
{ {
var key = block.Key; var key = block.Key;
@ -57,6 +113,9 @@ public static class SCBlockUtil
return name; return name;
} }
/// <summary>
/// Gets a summary of the block similar to it being a record type.
/// </summary>
public static string GetBlockSummary(SCBlock b) public static string GetBlockSummary(SCBlock b)
{ {
var sb = new StringBuilder(64); var sb = new StringBuilder(64);
@ -73,6 +132,12 @@ public static class SCBlockUtil
return sb.ToString(); return sb.ToString();
} }
/// <summary>
/// Import blocks from a folder, using the block key as the file name.
/// </summary>
/// <param name="path">Folder to import from</param>
/// <param name="sav">Save file to import into</param>
/// <returns>List of block keys that failed to import</returns>
public static List<string> ImportBlocksFromFolder(string path, ISCBlockArray sav) public static List<string> ImportBlocksFromFolder(string path, ISCBlockArray sav)
{ {
var failed = new List<string>(); var failed = new List<string>();

View file

@ -23,7 +23,7 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe
public SAV8LA() public SAV8LA()
{ {
AllBlocks = Meta8.GetBlankDataLA(); AllBlocks = BlankBlocks8a.GetBlankBlocks();
Blocks = new SaveBlockAccessor8LA(this); Blocks = new SaveBlockAccessor8LA(this);
SaveRevision = Blocks.DetectRevision(); SaveRevision = Blocks.DetectRevision();
Initialize(); Initialize();

View file

@ -20,7 +20,7 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS
public SAV8SWSH() public SAV8SWSH()
{ {
AllBlocks = Meta8.GetBlankDataSWSH(); AllBlocks = BlankBlocks8.GetBlankBlocks();
Blocks = new SaveBlockAccessor8SWSH(this); Blocks = new SaveBlockAccessor8SWSH(this);
SaveRevision = Zukan.GetRevision(); SaveRevision = Zukan.GetRevision();
Initialize(); Initialize();

View file

@ -24,9 +24,9 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile
public SAV9SV() public SAV9SV()
{ {
AllBlocks = Meta9.GetBlankDataSV(); AllBlocks = BlankBlocks9.GetBlankBlocks();
Blocks = new SaveBlockAccessor9SV(this); Blocks = new SaveBlockAccessor9SV(this);
SaveRevision = Meta9.BlankRevision; SaveRevision = BlankBlocks9.BlankRevision;
Initialize(); Initialize();
ClearBoxes(); ClearBoxes();
} }

View file

@ -81,7 +81,7 @@ public abstract class SAV_STADIUM : SaveFile, ILangDeviantSave
{ {
var result = base.GetFinalData(); var result = base.GetFinalData();
if (IsPairSwapped) if (IsPairSwapped)
ReverseEndianness(result = (byte[])result.Clone()); ReverseEndianness(result = [..result]);
return result; return result;
} }

View file

@ -57,12 +57,11 @@ public static class SlotPointerUtil
public static void UpdateRepointFrom(int newIndex, int oldIndex, Span<int> slotPointers) public static void UpdateRepointFrom(int newIndex, int oldIndex, Span<int> slotPointers)
{ {
for (int s = 0; s < slotPointers.Length; s++) // Don't return on first match; assume multiple pointers can point to the same slot
foreach (ref var ptr in slotPointers)
{ {
if (slotPointers[s] != oldIndex) if (ptr == oldIndex)
continue; ptr = newIndex;
slotPointers[s] = newIndex;
break;
} }
} }

View file

@ -4,37 +4,71 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Battle Video Request Utility for Gen 7
/// </summary>
public static class BVRequestUtil public static class BVRequestUtil
{ {
public static string GetSMBattleVideoURL(string code) private const int Size = 16;
public static string GetBattleVideoURL7(ReadOnlySpan<char> code)
{ {
code = code.Replace("-", string.Empty); var valid = TryGetBattleVideoID(code, out var result);
Debug.Assert(code.Length == 16);
var video_id = StrToU64(code, out bool valid);
if (!valid) if (!valid)
return string.Empty; return string.Empty;
return $"https://ctr-bnda-live.s3.amazonaws.com/10.CTR_BNDA_datastore/ds/1/data/{video_id:D11}-00001"; // Sun datastore return GetBattleVideoURL7(result);
} }
public static ulong StrToU64(ReadOnlySpan<char> input, out bool valid) public static string GetBattleVideoURL7(ulong video_id)
{ {
// Sun datastore
return $"https://ctr-bnda-live.s3.amazonaws.com/10.CTR_BNDA_datastore/ds/1/data/{video_id:D11}-00001";
}
private static bool TryGetBattleVideoID(ReadOnlySpan<char> code, out ulong result)
{
Span<char> noDash = stackalloc char[Size];
var length = SanitizeNoDashes(code, noDash);
if (length != noDash.Length) // didn't fill expected length
{
result = 0;
return false; // invalid length
}
return TryGetID(noDash, out result);
}
private static int SanitizeNoDashes(ReadOnlySpan<char> input, Span<char> result)
{
int ctr = 0;
foreach (var c in input)
{
// Check for both dashes (user entry)
if (c is '-' or 'ー')
continue;
if (ctr == result.Length)
return -1; // fail
result[ctr++] = c;
}
return ctr;
}
public static bool TryGetID(ReadOnlySpan<char> input, out ulong result)
{
Debug.Assert(input.Length == 16);
var chk = Pull(input[..4]) >> 4; // first four chars are checksum bits var chk = Pull(input[..4]) >> 4; // first four chars are checksum bits
var result = Pull(input[4..]); // next 12 chars are the 70 value bits result = Pull(input[4..]); // next 12 chars are the 70 value bits
Span<byte> temp = stackalloc byte[8]; Span<byte> temp = stackalloc byte[8];
WriteUInt64LittleEndian(temp, result); WriteUInt64LittleEndian(temp, result);
var actual = Checksums.CRC16_CCITT(temp); var actual = Checksums.CRC16_CCITT(temp);
valid = chk == actual; return chk == actual;
return result;
static ulong Pull(ReadOnlySpan<char> input) static ulong Pull(ReadOnlySpan<char> input)
{ {
ulong val = 0; ulong val = 0;
foreach (char c in input) foreach (char c in input)
{ {
if (c == '-')
continue;
val <<= 5; val <<= 5;
val |= Get5BitFromChar(c) & 0b11111; val |= Get5BitFromChar(c) & 0b11111;
} }
@ -47,7 +81,7 @@ public static class BVRequestUtil
Span<byte> temp = stackalloc byte[8]; Span<byte> temp = stackalloc byte[8];
WriteUInt64LittleEndian(temp, input); WriteUInt64LittleEndian(temp, input);
uint chk = Checksums.CRC16_CCITT(temp); uint chk = Checksums.CRC16_CCITT(temp);
Span<char> buff = stackalloc char[16]; Span<char> buff = stackalloc char[Size];
int ctr = 15; int ctr = 15;
Push(buff, ref ctr, 12, input); // store value bits Push(buff, ref ctr, 12, input); // store value bits
Push(buff, ref ctr, 04, chk << 4); // store checksum bits Push(buff, ref ctr, 04, chk << 4); // store checksum bits
@ -69,7 +103,7 @@ public static class BVRequestUtil
for (int i = 0, ctr = 0; i < buff.Length; i++) for (int i = 0, ctr = 0; i < buff.Length; i++)
{ {
buff2[ctr++] = buff[i]; buff2[ctr++] = buff[i];
if (i % spacer == 3 && ctr < buff2.Length) if (i % spacer == (spacer - 1) && ctr < buff2.Length)
buff2[ctr++] = '-'; // add dash between every chunk of size {spacer} buff2[ctr++] = '-'; // add dash between every chunk of size {spacer}
} }
return new string(buff2); return new string(buff2);

View file

@ -60,11 +60,12 @@ public static class HoneyTreeUtil
/// </summary> /// </summary>
/// <param name="seed">Current RNG state seed of the game.</param> /// <param name="seed">Current RNG state seed of the game.</param>
/// <param name="isMunchlaxTree">If the tree is a "rare" tree based on <see cref="CalculateMunchlaxTrees"/>.</param>"/> /// <param name="isMunchlaxTree">If the tree is a "rare" tree based on <see cref="CalculateMunchlaxTrees"/>.</param>"/>
/// <remarks><see cref="GameVersion.DPPt"/></remarks>
public static (HoneyTreeSlotGroup Group, int Slot, int Shakes) GetHoneyTreeResult(uint seed, bool isMunchlaxTree) public static (HoneyTreeSlotGroup Group, int Slot, int Shakes) GetHoneyTreeResult(uint seed, bool isMunchlaxTree)
{ {
var randGroup = LCRNG.Next16(ref seed) % 100; var randGroup = LCRNG.Next16(ref seed) / 656;
var randSlot = LCRNG.Next16(ref seed) % 100; var randSlot = LCRNG.Next16(ref seed) / 656;
var randShakes = LCRNG.Next16(ref seed) % 100; var randShakes = LCRNG.Next16(ref seed) / 656;
var group = GetHoneyTreeGroup(randGroup, isMunchlaxTree); var group = GetHoneyTreeGroup(randGroup, isMunchlaxTree);
var slot = GetHoneyTreeSlotIndex(randSlot); var slot = GetHoneyTreeSlotIndex(randSlot);

View file

@ -0,0 +1,143 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// <see cref="SCBlock"/> metadata for <see cref="GameVersion.PLA"/>
/// </summary>
public static class BlankBlocks8a
{
/// <summary>
/// Creates a blank <see cref="SCBlock"/> array for <see cref="GameVersion.PLA"/>
/// </summary>
public static SCBlock[] GetBlankBlocks() => SCBlockUtil.GetBlankBlockArray(DefaultChunkSizes);
private static ReadOnlySpan<uint> DefaultChunkSizes =>
[
0x00EF4BAE, 0x00140, 0x017C3CBB, 0x00001, 0x02168706, 0x1E460, 0x022A2253, 0x00001,
0x024C8CF3, 0x00004, 0x033D60DA, 0x00004, 0x0493AF74, 0x00004, 0x04AE4181, 0x00001,
0x04EABECF, 0x00004, 0x050E9D63, 0x00004, 0x05E7EBEB, 0x02EE0, 0x062AF6C2, 0x00020,
0x069EAD42, 0x00004, 0x070F34AD, 0x00020, 0x0821C372, 0x00004, 0x0A40A680, 0x00004,
0x0A49F0EF, 0x01F40, 0x0B3C5217, 0x00004, 0x0BFDEBA1, 0x00004, 0x0E6246BE, 0x00004,
0x0F3AD63C, 0x00004, 0x10A052BA, 0x00008, 0x10A0546D, 0x00008, 0x10D148DE, 0x00004,
0x111933CD, 0x00004, 0x11B37EC9, 0x000C8, 0x13A0E18B, 0x00004, 0x17833C07, 0x00004,
0x17EB7C4D, 0x00004, 0x180A4E9F, 0x00004, 0x19722C89, 0x00440, 0x1B1E3D8B, 0x00004,
0x1B5E0528, 0x00001, 0x1B65ABFD, 0x00004, 0x1BF70FCB, 0x00004, 0x1D482A63, 0x00004,
0x1E0F1BA3, 0x00190, 0x203A7F34, 0x00008, 0x2085565F, 0x00001, 0x20855812, 0x00001,
0x208559C5, 0x00001, 0x208D511F, 0x00004, 0x2123FE4A, 0x00004, 0x2137FAFF, 0x00004,
0x22540FF0, 0x00004, 0x22DEF108, 0x00004, 0x23AA6AE3, 0x00004, 0x24E0D195, 0x002F2,
0x250F3C75, 0x00004, 0x267DD9DA, 0x00070, 0x2728E588, 0x00004, 0x27931A29, 0x00004,
0x27986623, 0x00004, 0x279EA6CD, 0x00004, 0x27C9C8C2, 0x00100, 0x2846B7DB, 0x00004,
0x2969F5EB, 0x00004, 0x296C9DB8, 0x00004, 0x2985FE5D, 0x008D4, 0x298D9297, 0x00004,
0x29FB8D78, 0x00004, 0x2BBF9423, 0x00001, 0x2BBF9789, 0x00001, 0x2BBF9CA2, 0x00001,
0x2BBF9E55, 0x00001, 0x2C0B9BF3, 0x00004, 0x2C24C5F2, 0x00020, 0x2DBE7204, 0x04B00,
0x2EB1B190, 0x00020, 0x2F85E20D, 0x00004, 0x305FD79A, 0x00008, 0x30967638, 0x00001,
0x3096799E, 0x00001, 0x30967B51, 0x00001, 0x30B884F9, 0x00004, 0x3279D927, 0x00004,
0x35BDC76F, 0x00001, 0x35BDC922, 0x00001, 0x3745DA43, 0x00004, 0x37B18444, 0x00004,
0x385F9860, 0x00004, 0x388E378D, 0x00004, 0x3AAF5E5E, 0x00004, 0x3ADB8A98, 0x000C8,
0x3B4D705E, 0x00001, 0x3B956C1A, 0x00004, 0x3BFC2C3C, 0x000D0, 0x3C4AADD3, 0x00008,
0x3EBEE1A7, 0x00004, 0x3F5225A0, 0x00004, 0x3F7CC8A4, 0x00004, 0x3F8120BA, 0x00002,
0x402FAC1D, 0x00001, 0x4033C7DB, 0x00001, 0x40892A39, 0x00004, 0x40CC5A21, 0x00002,
0x40E13871, 0x00004, 0x41309084, 0x00001, 0x416A4820, 0x00004, 0x416A49D3, 0x00004,
0x416A4D39, 0x00004, 0x416A5252, 0x00004, 0x416A5405, 0x00004, 0x431018F0, 0x00004,
0x43749288, 0x00010, 0x444D8A2C, 0x00001, 0x44552BFF, 0x00004, 0x451E1BAF, 0x00020,
0x457495AE, 0x00010, 0x45851092, 0x00064, 0x4618E7E4, 0x00004, 0x46459F4E, 0x00004,
0x46749741, 0x00010, 0x477498D4, 0x00010, 0x47E1CEAB, 0x54600, 0x481DB963, 0x00001,
0x481DBCC9, 0x00001, 0x481DBE7C, 0x00001, 0x481DC02F, 0x00001, 0x481DCA61, 0x00001,
0x4820CC20, 0x00001, 0x4820CDD3, 0x00001, 0x4820CF86, 0x00001, 0x4820D139, 0x00001,
0x4820D2EC, 0x00001, 0x4820D49F, 0x00001, 0x4820D805, 0x00001, 0x4820E3EA, 0x00001,
0x4820E59D, 0x00001, 0x4823EE28, 0x00001, 0x4823EFDB, 0x00001, 0x4823F18E, 0x00001,
0x4823F341, 0x00001, 0x4823F4F4, 0x00001, 0x4823F6A7, 0x00001, 0x4826224C, 0x00001,
0x48262918, 0x00001, 0x48262ACB, 0x00001, 0x48262C7E, 0x00001, 0x48262E31, 0x00001,
0x48263197, 0x00001, 0x4826334A, 0x00001, 0x482634FD, 0x00001, 0x48749A67, 0x00010,
0x48CE01F7, 0x000FC, 0x48DDB755, 0x00006, 0x4918E303, 0x00001, 0x49749BFA, 0x00010,
0x4A6B888D, 0x00004, 0x4A749D8D, 0x00010, 0x4AA3F543, 0x00004, 0x4AA3F6F6, 0x00004,
0x4AAF7FBE, 0x00004, 0x4BD70B32, 0x041A0, 0x4C5C85AB, 0x00004, 0x4CF1F5D3, 0x00004,
0x4D7EADDD, 0x00004, 0x4DB28157, 0x00004, 0x4EB3ECBB, 0x00004, 0x4EE2B115, 0x00008,
0x4FBDB5FF, 0x00008, 0x500164D0, 0x00004, 0x50016683, 0x00004, 0x500687A1, 0x00004,
0x509A1AC8, 0x00004, 0x509A1C7B, 0x00004, 0x509A1FE1, 0x00004, 0x50FE632A, 0x00004,
0x511622B3, 0x88040, 0x5297D400, 0x00001, 0x5297D766, 0x00001, 0x5297D919, 0x00001,
0x5297DACC, 0x00001, 0x5297DC7F, 0x00001, 0x5297EBCA, 0x00001, 0x5297ED7D, 0x00001,
0x529AE870, 0x00001, 0x529AEA23, 0x00001, 0x529AF608, 0x00001, 0x529AF7BB, 0x00001,
0x529AF96E, 0x00001, 0x529AFB21, 0x00001, 0x529AFCD4, 0x00001, 0x529AFE87, 0x00001,
0x529B003A, 0x00001, 0x529B01ED, 0x00001, 0x52A96788, 0x00001, 0x52A9693B, 0x00001,
0x52A96AEE, 0x00001, 0x52A96CA1, 0x00001, 0x52A96E54, 0x00001, 0x52A97007, 0x00001,
0x52A971BA, 0x00001, 0x52A9736D, 0x00001, 0x52AC71C6, 0x00001, 0x52AC7379, 0x00001,
0x52AC7BF8, 0x00001, 0x52AC7DAB, 0x00001, 0x52AC7F5E, 0x00001, 0x52AC8111, 0x00001,
0x52AC82C4, 0x00001, 0x52AC8477, 0x00001, 0x52AC862A, 0x00001, 0x52AC87DD, 0x00001,
0x52AF8D02, 0x00001, 0x52AF8EB5, 0x00001, 0x52AF9068, 0x00001, 0x52AF921B, 0x00001,
0x52AF93CE, 0x00001, 0x52AF9581, 0x00001, 0x52AF9734, 0x00001, 0x52AF98E7, 0x00001,
0x52AF9C4D, 0x00001, 0x52B27C10, 0x00001, 0x52B27DC3, 0x00001, 0x52B27F76, 0x00001,
0x52B28129, 0x00001, 0x52B282DC, 0x00001, 0x52B2848F, 0x00001, 0x52B28642, 0x00001,
0x52B287F5, 0x00001, 0x52B289A8, 0x00001, 0x52B28B5B, 0x00001, 0x52B4B700, 0x00001,
0x52B4B8B3, 0x00001, 0x52B4BA66, 0x00001, 0x52B4BDCC, 0x00001, 0x52B4BF7F, 0x00001,
0x52B4C132, 0x00001, 0x52B4C2E5, 0x00001, 0x52B4CB64, 0x00001, 0x52B4CD17, 0x00001,
0x52B7CB70, 0x00001, 0x52B7CD23, 0x00001, 0x52B7D089, 0x00001, 0x52B7D23C, 0x00001,
0x52B7D5A2, 0x00001, 0x52B7D755, 0x00001, 0x52BAE346, 0x00001, 0x52BAE4F9, 0x00001,
0x52BAED78, 0x00001, 0x52BAEF2B, 0x00001, 0x52BAF291, 0x00001, 0x52BAF444, 0x00001,
0x52BAF5F7, 0x00001, 0x52BAF7AA, 0x00001, 0x52BAF95D, 0x00001, 0x52BDFB1C, 0x00001,
0x52BDFCCF, 0x00001, 0x52BE039B, 0x00001, 0x52BE054E, 0x00001, 0x52BE0701, 0x00001,
0x52BE08B4, 0x00001, 0x52BE0A67, 0x00001, 0x52BE0C1A, 0x00001, 0x53DB799F, 0x00004,
0x5423DAA0, 0x00004, 0x549B6033, 0x03000, 0x54DAE9C5, 0x00004, 0x567F1330, 0x00001,
0x567F14E3, 0x00001, 0x567F1696, 0x00001, 0x567F1849, 0x00001, 0x567F19FC, 0x00001,
0x56823385, 0x00001, 0x56878ECA, 0x00001, 0x5687907D, 0x00001, 0x57A07D08, 0x00004,
0x57B1D097, 0x00004, 0x5898095A, 0x00004, 0x58AB6233, 0x00064, 0x58DC8855, 0x00004,
0x590CD38E, 0x00004, 0x5979158E, 0x00004, 0x5988DF78, 0x00004, 0x59A4D0C3, 0x00190,
0x5A39A553, 0x00004, 0x5B1F53F3, 0x00004, 0x5C283C72, 0x00008, 0x5C283E25, 0x00008,
0x5C283FD8, 0x00008, 0x5C28418B, 0x00008, 0x5C28433E, 0x00008, 0x5C2844F1, 0x00008,
0x5C2846A4, 0x00008, 0x5C284857, 0x00008, 0x5C284BBD, 0x00008, 0x61A7A35B, 0x00004,
0x62E91A65, 0x00004, 0x62F05895, 0x00004, 0x636A5ABD, 0x000C8, 0x64A1A5B0, 0x00004,
0x64A1A763, 0x00004, 0x64A1AE2F, 0x00004, 0x64A1AFE2, 0x00004, 0x64A1B195, 0x00004,
0x6506EE96, 0x06D60, 0x651D61A2, 0x004B0, 0x67692BB8, 0x00004, 0x67692D6B, 0x00004,
0x67692F1E, 0x00004, 0x676935EA, 0x00004, 0x6769379D, 0x00004, 0x6960C6EF, 0x00004,
0x6AFB0A16, 0x00004, 0x6B35BADB, 0x00060, 0x6B734EFD, 0x00004, 0x6C03D4A8, 0x00014,
0x6C99F9A0, 0x00002, 0x6EB3E8A0, 0x00004, 0x6F36A3AC, 0x00004, 0x717DDAA3, 0x00008,
0x71825204, 0x00001, 0x72391B04, 0x00004, 0x727AE2EE, 0x00004, 0x727AE4A1, 0x00004,
0x727AE654, 0x00004, 0x727AE807, 0x00004, 0x727AE9BA, 0x00004, 0x74026290, 0x00004,
0x744447B4, 0x00004, 0x75931048, 0x00004, 0x75931561, 0x00004, 0x75CE2CF6, 0x00004,
0x7659EC88, 0x00004, 0x76AB1B01, 0x00004, 0x76ABB5CD, 0x00004, 0x77675FA0, 0x00320,
0x7799EB86, 0x03980, 0x77B752BC, 0x00004, 0x77B75622, 0x00004, 0x77B757D5, 0x00004,
0x78848293, 0x00001, 0x78848446, 0x00001, 0x788485F9, 0x00001, 0x78E0935E, 0x00004,
0x79448B5D, 0x00004, 0x79C56A5C, 0x00004, 0x7A8530FD, 0x00004, 0x7ACB8CB5, 0x00004,
0x7B8CCB0B, 0x00180, 0x7CA9D9FA, 0x00004, 0x7D249649, 0x00004, 0x7D87DC83, 0x00004,
0x7E82513F, 0x00004, 0x8048A7DC, 0x00004, 0x8184EFB4, 0x00004, 0x81EC3A78, 0x00004,
0x82AD5F84, 0x00004, 0x82D57F17, 0x000C8, 0x8507839C, 0x00004, 0x85166DE2, 0x00004,
0x877CB98F, 0x009B0, 0x885E5F53, 0x00010, 0x89C1C8DE, 0x00004, 0x8A0E9425, 0x004B0,
0x8B18A566, 0x00004, 0x8B18A719, 0x00004, 0x8B18A8CC, 0x00004, 0x8B18AA7F, 0x00004,
0x8B18AC32, 0x00004, 0x8B18ADE5, 0x00004, 0x8B8FB439, 0x00004, 0x8BDFF0F3, 0x00040,
0x8BEEF106, 0x00004, 0x8C46768E, 0x00004, 0x8C5F59E8, 0x00004, 0x8D781241, 0x00008,
0x8E434F0D, 0x002D0, 0x8F0D8720, 0x00004, 0x8FC0A045, 0x00004, 0x92EB0306, 0x00004,
0x92F697ED, 0x00004, 0x95013114, 0x00008, 0x96993D83, 0x00008, 0x96F6F453, 0x00004,
0x9751BABE, 0x00400, 0x98785EE4, 0x00008, 0x98786097, 0x00008, 0x987863FD, 0x00008,
0x99E1625E, 0x07EB0, 0x9AB5F3D9, 0x00001, 0x9B986D2E, 0x00004, 0x9C41123A, 0x00004,
0x9C808BD3, 0x00004, 0x9D5D1CA5, 0x00004, 0x9E45BE99, 0x00004, 0x9E4635BB, 0x00004,
0x9EC079DA, 0x00002, 0x9FE2790A, 0x00A8C, 0xA00A8ABB, 0x00008, 0xA2AA5B41, 0x00004,
0xA373DA53, 0x01900, 0xA4317061, 0x00004, 0xA69E079B, 0x00004, 0xA94E1F5F, 0x00004,
0xA9F1368B, 0x00004, 0xAB48C136, 0x00004, 0xAD319811, 0x00004, 0xAE7B3CA1, 0x00004,
0xAE89206E, 0x00001, 0xAEE903A2, 0x00008, 0xB027F396, 0x00004, 0xB0B2A5AA, 0x00004,
0xB1EE7CF5, 0x00004, 0xB568265C, 0x00004, 0xB79EF1FE, 0x00004, 0xB7AAB47E, 0x00004,
0xB7DB15CC, 0x00004, 0xB8E961AD, 0x000FB, 0xB9075BC9, 0x00008, 0xB9252862, 0x00110,
0xBA230941, 0x00004, 0xBB0B39CF, 0x00004, 0xBCC66014, 0x00004, 0xBCC72306, 0x00004,
0xBCE74BFD, 0x00004, 0xBDDC386E, 0x00004, 0xC02AC847, 0x00004, 0xC25B0D5A, 0x00004,
0xC2F1C1B9, 0x02580, 0xC4C6417F, 0x000C0, 0xC4FA7C8C, 0x00004, 0xC5277828, 0x00008,
0xC5919BF6, 0x00004, 0xC5D7112B, 0x0DCA8, 0xC69F66B6, 0x00FA0, 0xC74A3FE7, 0x00010,
0xC7652F1C, 0x00004, 0xC7F8E3BC, 0x00008, 0xC7F8E56F, 0x00008, 0xC7F8EA88, 0x00008,
0xC7F8EC3B, 0x00008, 0xC7F8EDEE, 0x00008, 0xC7F8EFA1, 0x00008, 0xC7F8F154, 0x00008,
0xC7F8F4BA, 0x00008, 0xC7F8F66D, 0x00008, 0xC9541EB3, 0x00002, 0xC9A81578, 0x00010,
0xCC022CEC, 0x00008, 0xCC022E9F, 0x00008, 0xCC023052, 0x00008, 0xCD8ADF1D, 0x00004,
0xCFC9AA03, 0x00004, 0xCFEB27B3, 0x009C4, 0xCFED4C69, 0x00004, 0xD0068A74, 0x00004,
0xD03A595A, 0x009B0, 0xD06FD1EA, 0x00004, 0xD11C1F59, 0x00004, 0xD20DF4A0, 0x00004,
0xD2E5D408, 0x00004, 0xD3C06782, 0x00008, 0xD48FDC48, 0x00004, 0xD6546A02, 0x00038,
0xD6C95683, 0x00960, 0xD6F1B724, 0x00004, 0xD8048E00, 0x00004, 0xD94D71D7, 0x00004,
0xDD548BB1, 0x00B58, 0xDED70F11, 0x00004, 0xDFA7E2D8, 0x00004, 0xE0FB1EE7, 0x00008,
0xE1DF0672, 0x005B8, 0xE2798DDE, 0x00001, 0xE36D5700, 0x00064, 0xE450FEF7, 0x0001A,
0xE4E75089, 0x00040, 0xE5169DA1, 0x00004, 0xE668F1A6, 0x00001, 0xE668F359, 0x00001,
0xE668F50C, 0x00001, 0xE668F6BF, 0x00001, 0xE668FA25, 0x00001, 0xE72BDCEC, 0x00004,
0xE733FE4D, 0x00004, 0xE7425CFF, 0x00004, 0xE793EEAE, 0x00004, 0xEA7C87F4, 0x00020,
0xEB550C12, 0x00004, 0xEC13DFD7, 0x00004, 0xEE10F128, 0x00004, 0xEFB533F7, 0x00001,
0xF25C070E, 0x00080, 0xF32218C2, 0x00004, 0xF3CF94FF, 0x00004, 0xF41CFF61, 0x00200,
0xF45C3F59, 0x00004, 0xF4954B60, 0x00004, 0xF5D9F4A5, 0x00118, 0xF626721E, 0x00004,
0xF62D79D3, 0x00004, 0xF8154AC9, 0x00004, 0xFB58B1A7, 0x00064, 0xFB5AE87D, 0x00004,
0xFC374750, 0x00004, 0xFF400328, 0x00004, 0xFFCC05C2, 0x00001,
];
}

View file

@ -1,52 +1,18 @@
using System; using System;
using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
public static class Meta8 /// <summary>
/// <see cref="SCBlock"/> metadata for <see cref="GameVersion.SWSH"/>
/// </summary>
public static class BlankBlocks8
{ {
public static SCBlock[] GetBlankDataSWSH() => GetBlankBlockArray(DefaultChunkSizesSWSH);
public static SCBlock[] GetBlankDataLA() => GetBlankBlockArray(DefaultChunkSizesLA);
/// <summary> /// <summary>
/// Create a blank block array using the provided <see cref="arr"/> definition. /// Creates a blank <see cref="SCBlock"/> array for <see cref="GameVersion.SWSH"/>
/// </summary> /// </summary>
/// <param name="arr">Block specification tuples (key, size)</param> public static SCBlock[] GetBlankBlocks() => SCBlockUtil.GetBlankBlockArray(DefaultChunkSizes);
/// <returns>List of blocks</returns>
private static SCBlock[] GetBlankBlockArray(ReadOnlySpan<uint> arr)
{
var blocks = new SCBlock[arr.Length / 2];
for (int i = 0; i < blocks.Length; i++)
{
int index = i * 2;
var key = arr[index];
var length = (int) arr[index + 1];
var dummy = new byte[length];
blocks[i] = new SCBlock(key, SCTypeCode.None, dummy);
}
return blocks;
}
#if DEBUG private static ReadOnlySpan<uint> DefaultChunkSizes =>
public static IEnumerable<string> RipSizes(IReadOnlyCollection<SCBlock> blocks)
{
int ctr = 0;
foreach (var block in blocks)
{
if (block.Data.Length == 0)
continue;
if (ctr == 4)
{
yield return Environment.NewLine;
ctr = 0;
}
yield return $"0x{block.Key:X8}, 0x{block.Data.Length:X5}, ";
ctr++;
}
}
#endif
private static ReadOnlySpan<uint> DefaultChunkSizesSWSH =>
[ [
0x00A1F55B, 0x00004, 0x0123EA7A, 0x00004, 0x017C3CBB, 0x00001, 0x0192A204, 0x00009, 0x00A1F55B, 0x00004, 0x0123EA7A, 0x00004, 0x017C3CBB, 0x00001, 0x0192A204, 0x00009,
0x01A1F6EE, 0x00004, 0x01BFCAD0, 0x00002, 0x02B647B4, 0x00004, 0x02BFCC63, 0x00002, 0x01A1F6EE, 0x00004, 0x01BFCAD0, 0x00002, 0x02B647B4, 0x00004, 0x02BFCC63, 0x00002,
@ -269,133 +235,4 @@ public static class Meta8
0xFEB13D33, 0x00002, 0xFEB64141, 0x00001, 0xFEE68CE1, 0x00004, 0xFF4498B1, 0x00002, 0xFEB13D33, 0x00002, 0xFEB64141, 0x00001, 0xFEE68CE1, 0x00004, 0xFF4498B1, 0x00002,
0xFFA1F3C8, 0x00004, 0xFFA1F3C8, 0x00004,
]; ];
private static ReadOnlySpan<uint> DefaultChunkSizesLA =>
[
0x00EF4BAE, 0x00140, 0x017C3CBB, 0x00001, 0x02168706, 0x1E460, 0x022A2253, 0x00001,
0x024C8CF3, 0x00004, 0x033D60DA, 0x00004, 0x0493AF74, 0x00004, 0x04AE4181, 0x00001,
0x04EABECF, 0x00004, 0x050E9D63, 0x00004, 0x05E7EBEB, 0x02EE0, 0x062AF6C2, 0x00020,
0x069EAD42, 0x00004, 0x070F34AD, 0x00020, 0x0821C372, 0x00004, 0x0A40A680, 0x00004,
0x0A49F0EF, 0x01F40, 0x0B3C5217, 0x00004, 0x0BFDEBA1, 0x00004, 0x0E6246BE, 0x00004,
0x0F3AD63C, 0x00004, 0x10A052BA, 0x00008, 0x10A0546D, 0x00008, 0x10D148DE, 0x00004,
0x111933CD, 0x00004, 0x11B37EC9, 0x000C8, 0x13A0E18B, 0x00004, 0x17833C07, 0x00004,
0x17EB7C4D, 0x00004, 0x180A4E9F, 0x00004, 0x19722C89, 0x00440, 0x1B1E3D8B, 0x00004,
0x1B5E0528, 0x00001, 0x1B65ABFD, 0x00004, 0x1BF70FCB, 0x00004, 0x1D482A63, 0x00004,
0x1E0F1BA3, 0x00190, 0x203A7F34, 0x00008, 0x2085565F, 0x00001, 0x20855812, 0x00001,
0x208559C5, 0x00001, 0x208D511F, 0x00004, 0x2123FE4A, 0x00004, 0x2137FAFF, 0x00004,
0x22540FF0, 0x00004, 0x22DEF108, 0x00004, 0x23AA6AE3, 0x00004, 0x24E0D195, 0x002F2,
0x250F3C75, 0x00004, 0x267DD9DA, 0x00070, 0x2728E588, 0x00004, 0x27931A29, 0x00004,
0x27986623, 0x00004, 0x279EA6CD, 0x00004, 0x27C9C8C2, 0x00100, 0x2846B7DB, 0x00004,
0x2969F5EB, 0x00004, 0x296C9DB8, 0x00004, 0x2985FE5D, 0x008D4, 0x298D9297, 0x00004,
0x29FB8D78, 0x00004, 0x2BBF9423, 0x00001, 0x2BBF9789, 0x00001, 0x2BBF9CA2, 0x00001,
0x2BBF9E55, 0x00001, 0x2C0B9BF3, 0x00004, 0x2C24C5F2, 0x00020, 0x2DBE7204, 0x04B00,
0x2EB1B190, 0x00020, 0x2F85E20D, 0x00004, 0x305FD79A, 0x00008, 0x30967638, 0x00001,
0x3096799E, 0x00001, 0x30967B51, 0x00001, 0x30B884F9, 0x00004, 0x3279D927, 0x00004,
0x35BDC76F, 0x00001, 0x35BDC922, 0x00001, 0x3745DA43, 0x00004, 0x37B18444, 0x00004,
0x385F9860, 0x00004, 0x388E378D, 0x00004, 0x3AAF5E5E, 0x00004, 0x3ADB8A98, 0x000C8,
0x3B4D705E, 0x00001, 0x3B956C1A, 0x00004, 0x3BFC2C3C, 0x000D0, 0x3C4AADD3, 0x00008,
0x3EBEE1A7, 0x00004, 0x3F5225A0, 0x00004, 0x3F7CC8A4, 0x00004, 0x3F8120BA, 0x00002,
0x402FAC1D, 0x00001, 0x4033C7DB, 0x00001, 0x40892A39, 0x00004, 0x40CC5A21, 0x00002,
0x40E13871, 0x00004, 0x41309084, 0x00001, 0x416A4820, 0x00004, 0x416A49D3, 0x00004,
0x416A4D39, 0x00004, 0x416A5252, 0x00004, 0x416A5405, 0x00004, 0x431018F0, 0x00004,
0x43749288, 0x00010, 0x444D8A2C, 0x00001, 0x44552BFF, 0x00004, 0x451E1BAF, 0x00020,
0x457495AE, 0x00010, 0x45851092, 0x00064, 0x4618E7E4, 0x00004, 0x46459F4E, 0x00004,
0x46749741, 0x00010, 0x477498D4, 0x00010, 0x47E1CEAB, 0x54600, 0x481DB963, 0x00001,
0x481DBCC9, 0x00001, 0x481DBE7C, 0x00001, 0x481DC02F, 0x00001, 0x481DCA61, 0x00001,
0x4820CC20, 0x00001, 0x4820CDD3, 0x00001, 0x4820CF86, 0x00001, 0x4820D139, 0x00001,
0x4820D2EC, 0x00001, 0x4820D49F, 0x00001, 0x4820D805, 0x00001, 0x4820E3EA, 0x00001,
0x4820E59D, 0x00001, 0x4823EE28, 0x00001, 0x4823EFDB, 0x00001, 0x4823F18E, 0x00001,
0x4823F341, 0x00001, 0x4823F4F4, 0x00001, 0x4823F6A7, 0x00001, 0x4826224C, 0x00001,
0x48262918, 0x00001, 0x48262ACB, 0x00001, 0x48262C7E, 0x00001, 0x48262E31, 0x00001,
0x48263197, 0x00001, 0x4826334A, 0x00001, 0x482634FD, 0x00001, 0x48749A67, 0x00010,
0x48CE01F7, 0x000FC, 0x48DDB755, 0x00006, 0x4918E303, 0x00001, 0x49749BFA, 0x00010,
0x4A6B888D, 0x00004, 0x4A749D8D, 0x00010, 0x4AA3F543, 0x00004, 0x4AA3F6F6, 0x00004,
0x4AAF7FBE, 0x00004, 0x4BD70B32, 0x041A0, 0x4C5C85AB, 0x00004, 0x4CF1F5D3, 0x00004,
0x4D7EADDD, 0x00004, 0x4DB28157, 0x00004, 0x4EB3ECBB, 0x00004, 0x4EE2B115, 0x00008,
0x4FBDB5FF, 0x00008, 0x500164D0, 0x00004, 0x50016683, 0x00004, 0x500687A1, 0x00004,
0x509A1AC8, 0x00004, 0x509A1C7B, 0x00004, 0x509A1FE1, 0x00004, 0x50FE632A, 0x00004,
0x511622B3, 0x88040, 0x5297D400, 0x00001, 0x5297D766, 0x00001, 0x5297D919, 0x00001,
0x5297DACC, 0x00001, 0x5297DC7F, 0x00001, 0x5297EBCA, 0x00001, 0x5297ED7D, 0x00001,
0x529AE870, 0x00001, 0x529AEA23, 0x00001, 0x529AF608, 0x00001, 0x529AF7BB, 0x00001,
0x529AF96E, 0x00001, 0x529AFB21, 0x00001, 0x529AFCD4, 0x00001, 0x529AFE87, 0x00001,
0x529B003A, 0x00001, 0x529B01ED, 0x00001, 0x52A96788, 0x00001, 0x52A9693B, 0x00001,
0x52A96AEE, 0x00001, 0x52A96CA1, 0x00001, 0x52A96E54, 0x00001, 0x52A97007, 0x00001,
0x52A971BA, 0x00001, 0x52A9736D, 0x00001, 0x52AC71C6, 0x00001, 0x52AC7379, 0x00001,
0x52AC7BF8, 0x00001, 0x52AC7DAB, 0x00001, 0x52AC7F5E, 0x00001, 0x52AC8111, 0x00001,
0x52AC82C4, 0x00001, 0x52AC8477, 0x00001, 0x52AC862A, 0x00001, 0x52AC87DD, 0x00001,
0x52AF8D02, 0x00001, 0x52AF8EB5, 0x00001, 0x52AF9068, 0x00001, 0x52AF921B, 0x00001,
0x52AF93CE, 0x00001, 0x52AF9581, 0x00001, 0x52AF9734, 0x00001, 0x52AF98E7, 0x00001,
0x52AF9C4D, 0x00001, 0x52B27C10, 0x00001, 0x52B27DC3, 0x00001, 0x52B27F76, 0x00001,
0x52B28129, 0x00001, 0x52B282DC, 0x00001, 0x52B2848F, 0x00001, 0x52B28642, 0x00001,
0x52B287F5, 0x00001, 0x52B289A8, 0x00001, 0x52B28B5B, 0x00001, 0x52B4B700, 0x00001,
0x52B4B8B3, 0x00001, 0x52B4BA66, 0x00001, 0x52B4BDCC, 0x00001, 0x52B4BF7F, 0x00001,
0x52B4C132, 0x00001, 0x52B4C2E5, 0x00001, 0x52B4CB64, 0x00001, 0x52B4CD17, 0x00001,
0x52B7CB70, 0x00001, 0x52B7CD23, 0x00001, 0x52B7D089, 0x00001, 0x52B7D23C, 0x00001,
0x52B7D5A2, 0x00001, 0x52B7D755, 0x00001, 0x52BAE346, 0x00001, 0x52BAE4F9, 0x00001,
0x52BAED78, 0x00001, 0x52BAEF2B, 0x00001, 0x52BAF291, 0x00001, 0x52BAF444, 0x00001,
0x52BAF5F7, 0x00001, 0x52BAF7AA, 0x00001, 0x52BAF95D, 0x00001, 0x52BDFB1C, 0x00001,
0x52BDFCCF, 0x00001, 0x52BE039B, 0x00001, 0x52BE054E, 0x00001, 0x52BE0701, 0x00001,
0x52BE08B4, 0x00001, 0x52BE0A67, 0x00001, 0x52BE0C1A, 0x00001, 0x53DB799F, 0x00004,
0x5423DAA0, 0x00004, 0x549B6033, 0x03000, 0x54DAE9C5, 0x00004, 0x567F1330, 0x00001,
0x567F14E3, 0x00001, 0x567F1696, 0x00001, 0x567F1849, 0x00001, 0x567F19FC, 0x00001,
0x56823385, 0x00001, 0x56878ECA, 0x00001, 0x5687907D, 0x00001, 0x57A07D08, 0x00004,
0x57B1D097, 0x00004, 0x5898095A, 0x00004, 0x58AB6233, 0x00064, 0x58DC8855, 0x00004,
0x590CD38E, 0x00004, 0x5979158E, 0x00004, 0x5988DF78, 0x00004, 0x59A4D0C3, 0x00190,
0x5A39A553, 0x00004, 0x5B1F53F3, 0x00004, 0x5C283C72, 0x00008, 0x5C283E25, 0x00008,
0x5C283FD8, 0x00008, 0x5C28418B, 0x00008, 0x5C28433E, 0x00008, 0x5C2844F1, 0x00008,
0x5C2846A4, 0x00008, 0x5C284857, 0x00008, 0x5C284BBD, 0x00008, 0x61A7A35B, 0x00004,
0x62E91A65, 0x00004, 0x62F05895, 0x00004, 0x636A5ABD, 0x000C8, 0x64A1A5B0, 0x00004,
0x64A1A763, 0x00004, 0x64A1AE2F, 0x00004, 0x64A1AFE2, 0x00004, 0x64A1B195, 0x00004,
0x6506EE96, 0x06D60, 0x651D61A2, 0x004B0, 0x67692BB8, 0x00004, 0x67692D6B, 0x00004,
0x67692F1E, 0x00004, 0x676935EA, 0x00004, 0x6769379D, 0x00004, 0x6960C6EF, 0x00004,
0x6AFB0A16, 0x00004, 0x6B35BADB, 0x00060, 0x6B734EFD, 0x00004, 0x6C03D4A8, 0x00014,
0x6C99F9A0, 0x00002, 0x6EB3E8A0, 0x00004, 0x6F36A3AC, 0x00004, 0x717DDAA3, 0x00008,
0x71825204, 0x00001, 0x72391B04, 0x00004, 0x727AE2EE, 0x00004, 0x727AE4A1, 0x00004,
0x727AE654, 0x00004, 0x727AE807, 0x00004, 0x727AE9BA, 0x00004, 0x74026290, 0x00004,
0x744447B4, 0x00004, 0x75931048, 0x00004, 0x75931561, 0x00004, 0x75CE2CF6, 0x00004,
0x7659EC88, 0x00004, 0x76AB1B01, 0x00004, 0x76ABB5CD, 0x00004, 0x77675FA0, 0x00320,
0x7799EB86, 0x03980, 0x77B752BC, 0x00004, 0x77B75622, 0x00004, 0x77B757D5, 0x00004,
0x78848293, 0x00001, 0x78848446, 0x00001, 0x788485F9, 0x00001, 0x78E0935E, 0x00004,
0x79448B5D, 0x00004, 0x79C56A5C, 0x00004, 0x7A8530FD, 0x00004, 0x7ACB8CB5, 0x00004,
0x7B8CCB0B, 0x00180, 0x7CA9D9FA, 0x00004, 0x7D249649, 0x00004, 0x7D87DC83, 0x00004,
0x7E82513F, 0x00004, 0x8048A7DC, 0x00004, 0x8184EFB4, 0x00004, 0x81EC3A78, 0x00004,
0x82AD5F84, 0x00004, 0x82D57F17, 0x000C8, 0x8507839C, 0x00004, 0x85166DE2, 0x00004,
0x877CB98F, 0x009B0, 0x885E5F53, 0x00010, 0x89C1C8DE, 0x00004, 0x8A0E9425, 0x004B0,
0x8B18A566, 0x00004, 0x8B18A719, 0x00004, 0x8B18A8CC, 0x00004, 0x8B18AA7F, 0x00004,
0x8B18AC32, 0x00004, 0x8B18ADE5, 0x00004, 0x8B8FB439, 0x00004, 0x8BDFF0F3, 0x00040,
0x8BEEF106, 0x00004, 0x8C46768E, 0x00004, 0x8C5F59E8, 0x00004, 0x8D781241, 0x00008,
0x8E434F0D, 0x002D0, 0x8F0D8720, 0x00004, 0x8FC0A045, 0x00004, 0x92EB0306, 0x00004,
0x92F697ED, 0x00004, 0x95013114, 0x00008, 0x96993D83, 0x00008, 0x96F6F453, 0x00004,
0x9751BABE, 0x00400, 0x98785EE4, 0x00008, 0x98786097, 0x00008, 0x987863FD, 0x00008,
0x99E1625E, 0x07EB0, 0x9AB5F3D9, 0x00001, 0x9B986D2E, 0x00004, 0x9C41123A, 0x00004,
0x9C808BD3, 0x00004, 0x9D5D1CA5, 0x00004, 0x9E45BE99, 0x00004, 0x9E4635BB, 0x00004,
0x9EC079DA, 0x00002, 0x9FE2790A, 0x00A8C, 0xA00A8ABB, 0x00008, 0xA2AA5B41, 0x00004,
0xA373DA53, 0x01900, 0xA4317061, 0x00004, 0xA69E079B, 0x00004, 0xA94E1F5F, 0x00004,
0xA9F1368B, 0x00004, 0xAB48C136, 0x00004, 0xAD319811, 0x00004, 0xAE7B3CA1, 0x00004,
0xAE89206E, 0x00001, 0xAEE903A2, 0x00008, 0xB027F396, 0x00004, 0xB0B2A5AA, 0x00004,
0xB1EE7CF5, 0x00004, 0xB568265C, 0x00004, 0xB79EF1FE, 0x00004, 0xB7AAB47E, 0x00004,
0xB7DB15CC, 0x00004, 0xB8E961AD, 0x000FB, 0xB9075BC9, 0x00008, 0xB9252862, 0x00110,
0xBA230941, 0x00004, 0xBB0B39CF, 0x00004, 0xBCC66014, 0x00004, 0xBCC72306, 0x00004,
0xBCE74BFD, 0x00004, 0xBDDC386E, 0x00004, 0xC02AC847, 0x00004, 0xC25B0D5A, 0x00004,
0xC2F1C1B9, 0x02580, 0xC4C6417F, 0x000C0, 0xC4FA7C8C, 0x00004, 0xC5277828, 0x00008,
0xC5919BF6, 0x00004, 0xC5D7112B, 0x0DCA8, 0xC69F66B6, 0x00FA0, 0xC74A3FE7, 0x00010,
0xC7652F1C, 0x00004, 0xC7F8E3BC, 0x00008, 0xC7F8E56F, 0x00008, 0xC7F8EA88, 0x00008,
0xC7F8EC3B, 0x00008, 0xC7F8EDEE, 0x00008, 0xC7F8EFA1, 0x00008, 0xC7F8F154, 0x00008,
0xC7F8F4BA, 0x00008, 0xC7F8F66D, 0x00008, 0xC9541EB3, 0x00002, 0xC9A81578, 0x00010,
0xCC022CEC, 0x00008, 0xCC022E9F, 0x00008, 0xCC023052, 0x00008, 0xCD8ADF1D, 0x00004,
0xCFC9AA03, 0x00004, 0xCFEB27B3, 0x009C4, 0xCFED4C69, 0x00004, 0xD0068A74, 0x00004,
0xD03A595A, 0x009B0, 0xD06FD1EA, 0x00004, 0xD11C1F59, 0x00004, 0xD20DF4A0, 0x00004,
0xD2E5D408, 0x00004, 0xD3C06782, 0x00008, 0xD48FDC48, 0x00004, 0xD6546A02, 0x00038,
0xD6C95683, 0x00960, 0xD6F1B724, 0x00004, 0xD8048E00, 0x00004, 0xD94D71D7, 0x00004,
0xDD548BB1, 0x00B58, 0xDED70F11, 0x00004, 0xDFA7E2D8, 0x00004, 0xE0FB1EE7, 0x00008,
0xE1DF0672, 0x005B8, 0xE2798DDE, 0x00001, 0xE36D5700, 0x00064, 0xE450FEF7, 0x0001A,
0xE4E75089, 0x00040, 0xE5169DA1, 0x00004, 0xE668F1A6, 0x00001, 0xE668F359, 0x00001,
0xE668F50C, 0x00001, 0xE668F6BF, 0x00001, 0xE668FA25, 0x00001, 0xE72BDCEC, 0x00004,
0xE733FE4D, 0x00004, 0xE7425CFF, 0x00004, 0xE793EEAE, 0x00004, 0xEA7C87F4, 0x00020,
0xEB550C12, 0x00004, 0xEC13DFD7, 0x00004, 0xEE10F128, 0x00004, 0xEFB533F7, 0x00001,
0xF25C070E, 0x00080, 0xF32218C2, 0x00004, 0xF3CF94FF, 0x00004, 0xF41CFF61, 0x00200,
0xF45C3F59, 0x00004, 0xF4954B60, 0x00004, 0xF5D9F4A5, 0x00118, 0xF626721E, 0x00004,
0xF62D79D3, 0x00004, 0xF8154AC9, 0x00004, 0xFB58B1A7, 0x00064, 0xFB5AE87D, 0x00004,
0xFC374750, 0x00004, 0xFF400328, 0x00004, 0xFFCC05C2, 0x00001,
];
} }

View file

@ -1,52 +1,20 @@
using System; using System;
using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
public static class Meta9 /// <summary>
/// <see cref="SCBlock"/> metadata for Generation 9 games.
/// </summary>
public static class BlankBlocks9
{ {
public const byte BlankRevision = 2; public const byte BlankRevision = 2; // Latest, Blueberry
public static SCBlock[] GetBlankDataSV() => GetBlankBlockArray(DefaultChunkSizesSV);
/// <summary> /// <summary>
/// Create a blank block array using the provided <see cref="arr"/> definition. /// Creates a blank <see cref="SCBlock"/> array for <see cref="GameVersion.SV"/>
/// </summary> /// </summary>
/// <param name="arr">Block specification tuples (key, size)</param> public static SCBlock[] GetBlankBlocks() => SCBlockUtil.GetBlankBlockArray(DefaultChunkSizes);
/// <returns>List of blocks</returns>
private static SCBlock[] GetBlankBlockArray(ReadOnlySpan<uint> arr)
{
var blocks = new SCBlock[arr.Length / 2];
for (int i = 0; i < blocks.Length; i++)
{
int index = i * 2;
var key = arr[index];
var length = (int) arr[index + 1];
var dummy = new byte[length];
blocks[i] = new SCBlock(key, SCTypeCode.None, dummy);
}
return blocks;
}
#if DEBUG private static ReadOnlySpan<uint> DefaultChunkSizes =>
public static IEnumerable<string> RipSizes(IReadOnlyCollection<SCBlock> blocks)
{
int ctr = 0;
foreach (var block in blocks)
{
if (block.Data.Length == 0)
continue;
if (ctr == 4)
{
yield return Environment.NewLine;
ctr = 0;
}
yield return $"0x{block.Key:X8}, 0x{block.Data.Length:X5}, ";
ctr++;
}
}
#endif
private static ReadOnlySpan<uint> DefaultChunkSizesSV =>
[ [
0x00051292, 0x00008, 0x00182D33, 0x00008, 0x0018E0B9, 0x00008, 0x001FD42F, 0x00008, 0x00051292, 0x00008, 0x00182D33, 0x00008, 0x0018E0B9, 0x00008, 0x001FD42F, 0x00008,
0x0022B5C9, 0x00008, 0x003096FA, 0x00008, 0x0031E2C7, 0x00008, 0x003D333A, 0x00020, 0x0022B5C9, 0x00008, 0x003096FA, 0x00008, 0x0031E2C7, 0x00008, 0x003D333A, 0x00020,

View file

@ -1,12 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public interface IRentalTeam<T> where T : PKM
{
T GetSlot(int slot);
void SetSlot(int slot, T pk);
T[] GetTeam();
void SetTeam(IReadOnlyList<T> team);
}

View file

@ -1,7 +1,17 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Exposes information to differentiate the save file from other patches of the same game.
/// </summary>
public interface ISaveFileRevision public interface ISaveFileRevision
{ {
/// <summary>
/// Incremented magic number to differentiate feature sets available.
/// </summary>
int SaveRevision { get; } int SaveRevision { get; }
/// <summary>
/// Magic string to differentiate feature sets available.
/// </summary>
string SaveRevisionString { get; } string SaveRevisionString { get; }
} }

View file

@ -1,7 +1,18 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Stores the previous timestamp and the current timestamp.
/// </summary>
public interface ISecureValueStorage public interface ISecureValueStorage
{ {
/// <summary>
/// Timestamp that the file was previously saved at.
/// </summary>
/// <remarks>Useful for remembering the timestamp of a backup, if present.</remarks>
ulong TimeStampPrevious { get; set; } ulong TimeStampPrevious { get; set; }
/// <summary>
/// Timestamp that the file was last saved at.
/// </summary>
ulong TimeStampCurrent { get; set; } ulong TimeStampCurrent { get; set; }
} }

View file

@ -0,0 +1,35 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Exposes a rental team interface to fetch team data.
/// </summary>
/// <typeparam name="T">Type of entity stored in the rental team</typeparam>
public interface IRentalTeam<T> where T : PKM
{
/// <summary>
/// Gets the entity at a specific slot.
/// </summary>
T GetSlot(int slot);
/// <summary>
/// Sets the entity at a specific slot.
/// </summary>
void SetSlot(int slot, T pk);
/// <summary>
/// Gets the entire team.
/// </summary>
void GetTeam(Span<T> team);
/// <summary>
/// Sets the entire team.
/// </summary>
void SetTeam(ReadOnlySpan<T> team);
/// <summary>
/// Gets the entire team.
/// </summary>
T[] GetTeam();
}

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using static System.Buffers.Binary.BinaryPrimitives; using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -54,14 +53,19 @@ public sealed class RentalTeam8(byte[] Data) : IRentalTeam<PK8>, IPokeGroup
public PK8[] GetTeam() public PK8[] GetTeam()
{ {
var team = new PK8[COUNT_POKE]; var team = new PK8[COUNT_POKE];
for (int i = 0; i < team.Length; i++) GetTeam(team);
team[i] = GetSlot(i);
return team; return team;
} }
public void SetTeam(IReadOnlyList<PK8> team) public void GetTeam(Span<PK8> team)
{ {
for (int i = 0; i < team.Count; i++) for (int i = 0; i < team.Length; i++)
team[i] = GetSlot(i);
}
public void SetTeam(ReadOnlySpan<PK8> team)
{
for (int i = 0; i < team.Length; i++)
SetSlot(i, team[i]); SetSlot(i, team[i]);
} }
@ -100,7 +104,7 @@ public sealed class RentalTeam8(byte[] Data) : IRentalTeam<PK8>, IPokeGroup
get => Data[0x86D]; get => Data[0x86D];
set => Data[0x86D] = value; set => Data[0x86D] = value;
} }
// 0x12 bytes unused to pad out to full size 0x880 // 0x12 bytes unused to pad out to full size 0x880
public IEnumerable<PKM> Contents => GetTeam(); public IEnumerable<PKM> Contents => GetTeam();
@ -108,7 +112,12 @@ public sealed class RentalTeam8(byte[] Data) : IRentalTeam<PK8>, IPokeGroup
{ {
if (data.Length != SIZE) if (data.Length != SIZE)
return false; return false;
var team = new RentalTeam8(data).GetTeam(); var team = new RentalTeam8(data);
return team.All(x => x.ChecksumValid); for (int i = 0; i < 6; i++)
{
if (!team.GetSlot(i).ChecksumValid)
return false;
}
return true;
} }
} }

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using static System.Buffers.Binary.BinaryPrimitives; using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -87,14 +86,19 @@ public sealed class RentalTeam9(byte[] Data) : IRentalTeam<PK9>, IPokeGroup
public PK9[] GetTeam() public PK9[] GetTeam()
{ {
var team = new PK9[COUNT_POKE]; var team = new PK9[COUNT_POKE];
for (int i = 0; i < team.Length; i++) GetTeam(team);
team[i] = GetSlot(i);
return team; return team;
} }
public void SetTeam(IReadOnlyList<PK9> team) public void GetTeam(Span<PK9> team)
{ {
for (int i = 0; i < team.Count; i++) for (int i = 0; i < team.Length; i++)
team[i] = GetSlot(i);
}
public void SetTeam(ReadOnlySpan<PK9> team)
{
for (int i = 0; i < team.Length; i++)
SetSlot(i, team[i]); SetSlot(i, team[i]);
} }
@ -111,9 +115,19 @@ public sealed class RentalTeam9(byte[] Data) : IRentalTeam<PK9>, IPokeGroup
{ {
if (data.Length != SIZE) if (data.Length != SIZE)
return false; return false;
var team = new RentalTeam9(data).GetTeam(); var team = new RentalTeam9(data);
// Checksum can be invalid for whatever reason. Just sanity check a little. for (int i = 0; i < 6; i++)
return team.All(x => (uint)x.Species <= x.MaxSpeciesID && x.Moves.All(y => (uint)y <= x.MaxMoveID)); {
// Checksum can be invalid for whatever reason. Just sanity check a little.
var pk = team.GetSlot(i);
if (pk.Species == 0)
continue;
if (pk.Species > pk.MaxSpeciesID)
return false;
if (pk.Move1 > pk.MaxMoveID || pk.Move2 > pk.MaxMoveID || pk.Move3 > pk.MaxMoveID || pk.Move4 > pk.MaxMoveID)
return false;
}
return true;
} }
public static RentalTeam9 GetFrom(ReadOnlySpan<byte> data, int index) public static RentalTeam9 GetFrom(ReadOnlySpan<byte> data, int index)
@ -125,5 +139,10 @@ public sealed class RentalTeam9(byte[] Data) : IRentalTeam<PK9>, IPokeGroup
public void WriteTo(Span<byte> data, int index) => Data.CopyTo(data[(index * SIZE)..]); public void WriteTo(Span<byte> data, int index) => Data.CopyTo(data[(index * SIZE)..]);
public ushort EntityChecksum => Checksums.CRC16_CCITT(Data.AsSpan(OFS_1, OFS_END - OFS_1)); private Span<byte> CheckSpan => Data.AsSpan(OFS_1, 6 * LEN_POKE);
/// <summary>
/// Simple checksum to detect team duplication.
/// </summary>
public ushort EntityChecksum => Checksums.CRC16_CCITT(CheckSpan);
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using static PKHeX.Core.StadiumSaveType; using static PKHeX.Core.StadiumSaveType;
using static System.Buffers.Binary.BinaryPrimitives; using static System.Buffers.Binary.BinaryPrimitives;
@ -59,6 +59,10 @@ public static class StadiumUtil
return true; return true;
} }
/// <summary>
/// Checks if the <see cref="magic"/> value is present either with or without byte-swapping.
/// </summary>
/// <remarks>Only checks for a single instance of the magic value.</remarks>
public static StadiumSaveType IsMagicPresentAbsolute(ReadOnlySpan<byte> data, int offset, uint magic) public static StadiumSaveType IsMagicPresentAbsolute(ReadOnlySpan<byte> data, int offset, uint magic)
{ {
var actual = ReadUInt32LittleEndian(data[offset..]); var actual = ReadUInt32LittleEndian(data[offset..]);

View file

@ -18,6 +18,22 @@ public static partial class Util
return new string(result[..ctr]); return new string(result[..ctr]);
} }
/// <inheritdoc cref="CleanFileName(string)"/>
public static string CleanFileName(ReadOnlySpan<char> fileName)
{
Span<char> result = stackalloc char[fileName.Length];
int ctr = GetCleanFileName(fileName, result);
if (ctr == fileName.Length)
return fileName.ToString();
return new string(result[..ctr]);
}
/// <summary>
/// Removes any invalid filename characters from the input string.
/// </summary>
/// <param name="input">String to clean</param>
/// <param name="output">Buffer to write the cleaned string to</param>
/// <returns>Length of the cleaned string</returns>
private static int GetCleanFileName(ReadOnlySpan<char> input, Span<char> output) private static int GetCleanFileName(ReadOnlySpan<char> input, Span<char> output)
{ {
ReadOnlySpan<char> invalid = Path.GetInvalidFileNameChars(); ReadOnlySpan<char> invalid = Path.GetInvalidFileNameChars();

View file

@ -4,6 +4,7 @@ namespace PKHeX.Core;
public static partial class Util public static partial class Util
{ {
/// <inheritdoc cref="Random.Shared"/>
public static Random Rand => Random.Shared; public static Random Rand => Random.Shared;
public static uint Rand32() => Rand32(Rand); public static uint Rand32() => Rand32(Rand);

View file

@ -42,6 +42,7 @@ public static class ReflectUtil
return pi.GetValue(obj, null); return pi.GetValue(obj, null);
return default; return default;
} }
public static bool SetValue(object obj, string name, object value) public static bool SetValue(object obj, string name, object value)
{ {
if (!TryGetPropertyInfo(obj.GetType().GetTypeInfo(), name, out var pi)) if (!TryGetPropertyInfo(obj.GetType().GetTypeInfo(), name, out var pi))

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Buffers;
using System.Drawing; using System.Drawing;
using PKHeX.Core; using PKHeX.Core;
using PKHeX.Drawing.PokeSprite.Properties; using PKHeX.Drawing.PokeSprite.Properties;
@ -234,10 +235,16 @@ public static class SpriteUtil
// If the image has any transparency, any derived background will bleed into it. // If the image has any transparency, any derived background will bleed into it.
// Need to undo any transparency values if any present. // Need to undo any transparency values if any present.
// Remove opaque pixels from original image, leaving only the glow effect pixels. // Remove opaque pixels from original image, leaving only the glow effect pixels.
var original = (byte[])pixels.Clone(); var temp = ArrayPool<byte>.Shared.Rent(pixels.Length);
var original = temp.AsSpan(0, pixels.Length);
pixels.CopyTo(original);
ImageUtil.SetAllUsedPixelsOpaque(pixels); ImageUtil.SetAllUsedPixelsOpaque(pixels);
ImageUtil.GlowEdges(pixels, blue, green, red, baseSprite.Width); ImageUtil.GlowEdges(pixels, blue, green, red, baseSprite.Width);
ImageUtil.RemovePixels(pixels, original); ImageUtil.RemovePixels(pixels, original);
original.Clear();
ArrayPool<byte>.Shared.Return(temp);
} }
public static Bitmap GetLegalIndicator(bool valid) => valid ? Resources.valid : Resources.warn; public static Bitmap GetLegalIndicator(bool valid) => valid ? Resources.valid : Resources.warn;

View file

@ -8,36 +8,74 @@ namespace PKHeX.Drawing;
/// </summary> /// </summary>
public static class ColorUtil public static class ColorUtil
{ {
private const byte MaxStat = 180; // shift the green cap down /// <summary> Clamps absurdly high stats to a reasonable cap. </summary>
private const byte MaxStat = 180;
/// <summary> Ensures the color does not go below this value. </summary>
private const byte MinStat = 0; private const byte MinStat = 0;
/// <summary> Shift the color total down a little as the BST floor isn't 0. </summary>
private const byte ShiftDownBST = 175;
/// <summary> Divide by a fudge factor so that we're roughly within the range of a single stat. </summary>
private const float ShiftDivBST = 3;
/// <summary> /// <summary>
/// Gets the <see cref="Color"/> value for the specified <see cref="stat"/> value. /// Gets the <see cref="Color"/> value for the specified <see cref="stat"/> value.
/// </summary> /// </summary>
/// <param name="stat">Single stat value</param>
public static Color ColorBaseStat(int stat) public static Color ColorBaseStat(int stat)
{ {
float x = (100f * stat) / MaxStat; // Red to Yellow to Green, no Blue component.
if (x > 100) var x = (uint)stat >= MaxStat ? 1f : ((float)stat) / MaxStat;
x = 100; return GetPastelRYG(x);
double red = 255f * (x > 50 ? 1 - (2 * (x - 50) / 100.0) : 1.0);
double green = 255f * (x > 50 ? 1.0 : 2 * x / 100.0);
return Blend(Color.FromArgb((int)red, (int)green, 0), Color.White, 0.4);
} }
public static Color ColorBaseStatTotal(int tot) /// <summary>
/// Gets the <see cref="Color"/> value for the specified <see cref="bst"/> value.
/// </summary>
/// <param name="bst">Base Stat Total</param>
public static Color ColorBaseStatTotal(int bst)
{ {
const byte sumShiftDown = 175; var sumToSingle = Math.Max(MinStat, bst - ShiftDownBST) / ShiftDivBST;
const float sumDivisor = 3f;
var sumToSingle = Math.Max(MinStat, tot - sumShiftDown) / sumDivisor;
return ColorBaseStat((int)sumToSingle); return ColorBaseStat((int)sumToSingle);
} }
/// <summary>
/// Gets a pastel color from Red to Yellow to Green with a 40%/60% blend with White.
/// </summary>
/// <param name="x">Percentage of the way from Red to Green [0,1]</param>
public static Color GetPastelRYG(float x)
{
var r = x > .5f ? 510 * (1 - x) : 255;
var g = x > .5f ? 255 : 510 * x;
// Blend with white to make it lighter rather than a darker shade.
const float white = 0.4f;
const byte b = (byte)(0xFF * (1 - white));
var br = (byte)((r * white) + b);
var bg = (byte)((g * white) + b);
return Color.FromArgb(br, bg, b);
}
/// <summary>
/// Combines two colors together with the specified amount.
/// </summary>
/// <param name="color">New color to layer atop</param>
/// <param name="backColor">Original color to layer beneath</param>
/// <param name="amount">Amount to prefer <see cref="color"/> over <see cref="backColor"/></param>
public static Color Blend(Color color, Color backColor, double amount) public static Color Blend(Color color, Color backColor, double amount)
{ {
byte r = (byte)((color.R * amount) + (backColor.R * (1 - amount))); byte r = MixComponent(color.R, backColor.R, amount);
byte g = (byte)((color.G * amount) + (backColor.G * (1 - amount))); byte g = MixComponent(color.G, backColor.G, amount);
byte b = (byte)((color.B * amount) + (backColor.B * (1 - amount))); byte b = MixComponent(color.B, backColor.B, amount);
return Color.FromArgb(r, g, b); return Color.FromArgb(r, g, b);
} }
/// <summary>
/// Combines two color components with the specified first color percent preference [0,1].
/// </summary>
/// <param name="a">First color's component</param>
/// <param name="b">Second color's component</param>
/// <param name="x">Percent preference of <paramref name="a"/> over <paramref name="b"/></param>
public static byte MixComponent(byte a, byte b, double x) => (byte)((a * x) + (b * (1 - x)));
} }

View file

@ -113,16 +113,21 @@ public static class ImageUtil
data = new byte[bmp.Width * bmp.Height * 4]; data = new byte[bmp.Width * bmp.Height * 4];
} }
public static Bitmap GetBitmap(byte[] data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb) public static Bitmap GetBitmap(byte[] data, int width, int height, int length, PixelFormat format = PixelFormat.Format32bppArgb)
{ {
var bmp = new Bitmap(width, height, format); var bmp = new Bitmap(width, height, format);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, format); var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, format);
var ptr = bmpData.Scan0; var ptr = bmpData.Scan0;
Marshal.Copy(data, 0, ptr, data.Length); Marshal.Copy(data, 0, ptr, length);
bmp.UnlockBits(bmpData); bmp.UnlockBits(bmpData);
return bmp; return bmp;
} }
public static Bitmap GetBitmap(byte[] data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb)
{
return GetBitmap(data, width, height, data.Length, format);
}
public static byte[] GetPixelData(Bitmap bitmap) public static byte[] GetPixelData(Bitmap bitmap)
{ {
var argbData = new byte[bitmap.Width * bitmap.Height * 4]; var argbData = new byte[bitmap.Width * bitmap.Height * 4];

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Buffers;
using System.Drawing; using System.Drawing;
using System.Timers; using System.Timers;
using System.Windows.Forms; using System.Windows.Forms;
@ -98,10 +99,15 @@ public sealed class BitmapAnimator : IDisposable
var frameColor = GetFrameColor(elapsedFraction); var frameColor = GetFrameColor(elapsedFraction);
ArgumentNullException.ThrowIfNull(GlowData); ArgumentNullException.ThrowIfNull(GlowData);
var frameData = (byte[])GlowData.Clone();
ImageUtil.ChangeAllColorTo(frameData, frameColor);
frame = ImageUtil.GetBitmap(frameData, imgWidth, imgHeight); var frameData = ArrayPool<byte>.Shared.Rent(GlowData.Length);
var frameSpan = frameData.AsSpan(0, GlowData.Length);
GlowData.AsSpan().CopyTo(frameSpan);
ImageUtil.ChangeAllColorTo(frameSpan, frameColor);
frame = ImageUtil.GetBitmap(frameData, imgWidth, imgHeight, GlowData.Length);
frameSpan.Clear();
ArrayPool<byte>.Shared.Return(frameData);
if (ExtraLayer != null) if (ExtraLayer != null)
frame = ImageUtil.LayerImage(frame, ExtraLayer, 0, 0); frame = ImageUtil.LayerImage(frame, ExtraLayer, 0, 0);
if (OriginalBackground != null) if (OriginalBackground != null)

View file

@ -95,8 +95,9 @@ public class StringQualityTests
var group = setField.GetValue(strings) as ILocationSet; var group = setField.GetValue(strings) as ILocationSet;
Assert.NotNull(group); Assert.NotNull(group);
foreach (var (bank, arr) in group.GetAll()) foreach (var (bank, mem) in group.GetAll())
{ {
var arr = mem.Span;
var hs = new HashSet<string>(arr.Length); var hs = new HashSet<string>(arr.Length);
bool sm0 = bank == 0 && name == nameof(GameStrings.Gen7); bool sm0 = bank == 0 && name == nameof(GameStrings.Gen7);
for (int index = 0; index < arr.Length; index++) for (int index = 0; index < arr.Length; index++)