Add handling for S/V load and data ripping

Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com>
Co-Authored-By: Matt <17801814+sora10pls@users.noreply.github.com>
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
This commit is contained in:
Kurt 2022-11-24 15:30:03 -08:00
parent 11f2e58361
commit 38b784e19b
3451 changed files with 23026 additions and 42267 deletions

View File

@ -13,11 +13,11 @@ public static class Container
public static IFileContainer GetContainer(string path, ContainerType t) => t switch
{
ContainerType.GARC => new GARC(path),
ContainerType.Mini => MiniUtil.GetMini(path),
ContainerType.BinLinker => MiniUtil.GetMini(path),
ContainerType.SARC => new SARC(path),
ContainerType.Folder => new FolderContainer(path),
ContainerType.SingleFile => new SingleFileContainer(path),
ContainerType.GFPack => new GFPack(path),
ContainerType.GameFreakPack => new GFPack(path),
_ => throw new ArgumentOutOfRangeException(nameof(t), t, null),
};

View File

@ -3,9 +3,10 @@ namespace pkNX.Containers;
public enum ContainerType
{
GARC,
Mini,
BinLinker,
SARC,
Folder,
SingleFile,
GFPack,
GameFreakPack,
SingleFileInternal,
}

View File

@ -0,0 +1,7 @@
namespace pkNX.Containers;
public interface IFileInternal
{
byte[] GetPackedFile(string file);
byte[] GetPackedFile(ulong hash);
}

View File

@ -0,0 +1,41 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace pkNX.Containers;
public class InternalFileContainer : IFileContainer
{
public string? FilePath { get; set; }
public bool Modified { get; set; }
public int Count => 1;
public byte[] Data = Array.Empty<byte>();
private byte[] Backup = Array.Empty<byte>();
public InternalFileContainer(byte[] data) => LoadData(data);
private void LoadData(byte[] data) => Backup = (byte[]) (Data = data).Clone();
public void CancelEdits()
{
Modified = false;
Data = (byte[]) Backup.Clone();
}
public byte[] this[int index]
{
get => (byte[])Data.Clone();
set
{
Modified |= !Data.SequenceEqual(value);
Data = value;
}
}
public Task<byte[][]> GetFiles() => Task.FromResult(new[] {this[0]});
public Task<byte[]> GetFile(int file, int subFile = 0) => Task.FromResult(this[0]);
public Task SetFile(int file, byte[] value, int subFile = 0) => Task.FromResult(Data = value);
public Task SaveAs(string path, ContainerHandler handler, CancellationToken token) => new(() => Dump(path, handler), token);
public void Dump(string? path, ContainerHandler handler) => FileMitm.WriteAllBytes(path ?? FilePath!, Data);
}

View File

@ -21,12 +21,10 @@ public class DatTable : Dictionary<ulong, int>
// - u32(?) index
// - u32(?) unk
public static bool IsDatTable(byte[] bytes)
{
// pretty weak, don't call this if you aren't sure!
var count = BitConverter.ToUInt32(bytes, 0);
return 4 + (count * 0x10) == bytes.Length;
}
// pretty weak, don't call this if you aren't sure!
public static bool IsDatTable(ReadOnlySpan<byte> bytes) => IsDatTable(bytes, bytes.Length);
public static bool IsDatTable(ReadOnlySpan<byte> bytes, int len) => IsDatTable(System.Buffers.Binary.BinaryPrimitives.ReadInt32LittleEndian(bytes), len);
public static bool IsDatTable(int count, int len) => 4 + (count * 0x10) == len;
private readonly DatEntry[] Entries;

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Description>Packing &amp; Unpacking</Description>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>

View File

@ -79,7 +79,7 @@ public class DirectCache<T> : DataCache<T> where T : class
}
/// <summary>
/// Data 'from a flatbuffer table
/// Data 'from a FlatBuffer table
/// </summary>
/// <typeparam name="TTable">The type of table</typeparam>
/// <typeparam name="TData">The type of data inside the table</typeparam>

View File

@ -75,13 +75,13 @@ public enum GameFile
Encounters,
/// <summary> Trainer Data related to Trainers of a shared type. </summary>
TrainerClass,
TrainerSpecClass,
/// <summary> Trainer Data for individual Trainers that can be battled. </summary>
TrainerData,
TrainerSpecData,
/// <summary> Trainer PKM template data for regular battles. </summary>
TrainerPoke,
TrainerSpecPoke,
/// <summary> Move data that defines the properties of in-game moves. </summary>
MoveStats,
@ -105,7 +105,7 @@ public enum GameFile
ItemStats,
/// <summary> Static (fixed position/condition) encounter table. </summary>
EncounterStatic,
EncounterTableStatic,
/// <summary> Post-game roulette trainer data with normal difficulty. </summary>
FacilityTrainerNormal,
@ -147,10 +147,10 @@ public enum GameFile
MoveSprites,
/// <summary> Traded Pokémon swap data. </summary>
EncounterTrade,
EncounterTableTrade,
/// <summary> Gift Pokémon data. </summary>
EncounterGift,
EncounterTableGift,
/// <summary> Nest Data </summary>
NestData,
@ -354,4 +354,7 @@ public enum GameFile
MoveObstructionWater,
MoveObstructionWaterEffect,
NushiBattleSettings,
DataTrpfs,
DataTrpfd,
}

View File

@ -4,6 +4,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using static pkNX.Structures.GameVersion;
using static pkNX.Game.GameFile;
using static pkNX.Containers.ContainerType;
namespace pkNX.Game;
@ -21,9 +24,9 @@ public class GameFileMapping
private readonly GameLocation ROM;
public GameFileMapping(GameLocation rom) => FileMap = GetMapping((ROM = rom).Game);
internal IFileContainer GetFile(GameFile file, int language)
internal IFileContainer GetFile(GameFile file, int language, GameManager gameManager)
{
if (file is GameFile.GameText or GameFile.StoryText)
if (file is GameText or StoryText)
file += language + 1; // shift to localized language
if (Cache.TryGetValue(file, out var container))
@ -33,10 +36,18 @@ internal IFileContainer GetFile(GameFile file, int language)
if (info == null)
throw new ArgumentException($"Unknown {nameof(GameFile)} provided.", file.ToString());
var basePath = info.Parent == ContainerParent.ExeFS ? ROM.ExeFS : ROM.RomFS;
if (basePath == null)
throw new ArgumentException($"No {info.Parent} found for {file}.");
container = info.Get(basePath);
if (info.Type is SingleFileInternal && gameManager is IFileInternal irom)
{
var data = irom.GetPackedFile(info.RelativePath);
container = new InternalFileContainer(data);
}
else
{
var basePath = info.Parent == ContainerParent.ExeFS ? ROM.ExeFS : ROM.RomFS;
if (basePath == null)
throw new ArgumentException($"No {info.Parent} found for {file}.");
container = info.Get(basePath);
}
Cache.Add(file, container);
return container;
}
@ -56,13 +67,10 @@ internal void SaveAll()
public static IReadOnlyCollection<GameFileReference> GetMapping(GameVersion game) => game switch
{
GameVersion.GP => GG,
GameVersion.GE => GG,
GameVersion.GG => GG,
GameVersion.SW => SWSH,
GameVersion.SH => SWSH,
GameVersion.SWSH => SWSH,
GameVersion.PLA => PLA,
GP or GE or GG => FilesGG,
SW or SH or SWSH => FilesSWSH,
PLA => FilesPLA,
SL or VL or SV => FilesSV,
_ => throw new ArgumentOutOfRangeException(nameof(game), game, null),
};
@ -71,49 +79,49 @@ internal void SaveAll()
/// <summary>
/// Let's Go Pikachu &amp; Let's Go Eevee
/// </summary>
private static readonly GameFileReference[] GG =
private static readonly GameFileReference[] FilesGG =
{
new(GameFile.TrainerData, "bin", "trainer", "trainer_data"),
new(GameFile.TrainerPoke, "bin", "trainer", "trainer_poke"),
new(GameFile.TrainerClass, "bin", "trainer", "trainer_type"),
new(TrainerSpecData, "bin", "trainer", "trainer_data"),
new(TrainerSpecPoke, "bin", "trainer", "trainer_poke"),
new(TrainerSpecClass, "bin", "trainer", "trainer_type"),
new(GameFile.GameText0, 0, "bin", "message", "JPN", "common"),
new(GameFile.GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
new(GameFile.GameText2, 2, "bin", "message", "English", "common"),
new(GameFile.GameText3, 3, "bin", "message", "French", "common"),
new(GameFile.GameText4, 4, "bin", "message", "Italian", "common"),
new(GameFile.GameText5, 5, "bin", "message", "German", "common"),
new(GameText0, 0, "bin", "message", "JPN", "common"),
new(GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
new(GameText2, 2, "bin", "message", "English", "common"),
new(GameText3, 3, "bin", "message", "French", "common"),
new(GameText4, 4, "bin", "message", "Italian", "common"),
new(GameText5, 5, "bin", "message", "German", "common"),
// 6 unused lang
new(GameFile.GameText6, 7, "bin", "message", "Spanish", "common"),
new(GameFile.GameText7, 8, "bin", "message", "Korean", "common"),
new(GameFile.GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
new(GameFile.GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
new(GameText6, 7, "bin", "message", "Spanish", "common"),
new(GameText7, 8, "bin", "message", "Korean", "common"),
new(GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
new(GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
new(GameFile.StoryText0, 0, "bin", "message", "JPN", "script"),
new(GameFile.StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
new(GameFile.StoryText2, 2, "bin", "message", "English", "script"),
new(GameFile.StoryText3, 3, "bin", "message", "French", "script"),
new(GameFile.StoryText4, 4, "bin", "message", "Italian", "script"),
new(GameFile.StoryText5, 5, "bin", "message", "German", "script"),
new(StoryText0, 0, "bin", "message", "JPN", "script"),
new(StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
new(StoryText2, 2, "bin", "message", "English", "script"),
new(StoryText3, 3, "bin", "message", "French", "script"),
new(StoryText4, 4, "bin", "message", "Italian", "script"),
new(StoryText5, 5, "bin", "message", "German", "script"),
// 6 unused lang
new(GameFile.StoryText6, 7, "bin", "message", "Spanish", "script"),
new(GameFile.StoryText7, 8, "bin", "message", "Korean", "script"),
new(GameFile.StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
new(GameFile.StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
new(StoryText6, 7, "bin", "message", "Spanish", "script"),
new(StoryText7, 8, "bin", "message", "Korean", "script"),
new(StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
new(StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
new(GameFile.ItemStats, "bin", "pokelib", "item"),
new(GameFile.Evolutions, "bin", "pokelib", "evolution"),
new(GameFile.PersonalStats, "bin", "pokelib", "personal"),
new(GameFile.MegaEvolutions, "bin", "pokelib", "mega_evolution"),
new(GameFile.MoveStats, ContainerType.Mini, "bin", "pokelib", "waza", "waza_data.bin"),
new(GameFile.EncounterStatic, ContainerType.SingleFile, "bin", "script_event_data", "event_encount.bin"),
new(GameFile.EncounterTrade, ContainerType.SingleFile, "bin", "script_event_data", "field_trade_data.bin"),
new(GameFile.EncounterGift, ContainerType.SingleFile, "bin", "script_event_data", "add_poke.bin"),
new(GameFile.Learnsets, ContainerType.GFPack, "bin", "archive", "waza_oboe.gfpak"),
new(ItemStats, "bin", "pokelib", "item"),
new(Evolutions, "bin", "pokelib", "evolution"),
new(PersonalStats, "bin", "pokelib", "personal"),
new(MegaEvolutions, "bin", "pokelib", "mega_evolution"),
new(MoveStats, BinLinker, "bin", "pokelib", "waza", "waza_data.bin"),
new(EncounterTableStatic, SingleFile, "bin", "script_event_data", "event_encount.bin"),
new(EncounterTableTrade, SingleFile, "bin", "script_event_data", "field_trade_data.bin"),
new(EncounterTableGift, SingleFile, "bin", "script_event_data", "add_poke.bin"),
new(Learnsets, GameFreakPack, "bin", "archive", "waza_oboe.gfpak"),
new(GameFile.WildData1, ContainerType.SingleFile, "bin", "field", "param", "encount", "encount_data_p.bin"),
new(GameFile.WildData2, ContainerType.SingleFile, "bin", "field", "param", "encount", "encount_data_e.bin"),
new(GameFile.Shops, ContainerType.SingleFile, "bin", "app", "shop", "shop_data.bin"),
new(WildData1, SingleFile, "bin", "field", "param", "encount", "encount_data_p.bin"),
new(WildData2, SingleFile, "bin", "field", "param", "encount", "encount_data_e.bin"),
new(Shops, SingleFile, "bin", "app", "shop", "shop_data.bin"),
// Cutscenes bin\demo
// Models bin\archive\pokemon
@ -124,60 +132,60 @@ internal void SaveAll()
#region Gen 8
/// <summary>
/// Sword
/// Sword &amp; Shield
/// </summary>
private static readonly GameFileReference[] SWSH =
private static readonly GameFileReference[] FilesSWSH =
{
new(GameFile.TrainerData, "bin", "trainer", "trainer_data"),
new(GameFile.TrainerPoke, "bin", "trainer", "trainer_poke"),
new(GameFile.TrainerClass, "bin", "trainer", "trainer_type"),
new(TrainerSpecData, "bin", "trainer", "trainer_data"),
new(TrainerSpecPoke, "bin", "trainer", "trainer_poke"),
new(TrainerSpecClass, "bin", "trainer", "trainer_type"),
new(GameFile.GameText0, 0, "bin", "message", "JPN", "common"),
new(GameFile.GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
new(GameFile.GameText2, 2, "bin", "message", "English", "common"),
new(GameFile.GameText3, 3, "bin", "message", "French", "common"),
new(GameFile.GameText4, 4, "bin", "message", "Italian", "common"),
new(GameFile.GameText5, 5, "bin", "message", "German", "common"),
new(GameText0, 0, "bin", "message", "JPN", "common"),
new(GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
new(GameText2, 2, "bin", "message", "English", "common"),
new(GameText3, 3, "bin", "message", "French", "common"),
new(GameText4, 4, "bin", "message", "Italian", "common"),
new(GameText5, 5, "bin", "message", "German", "common"),
// 6 unused lang
new(GameFile.GameText6, 7, "bin", "message", "Spanish", "common"),
new(GameFile.GameText7, 8, "bin", "message", "Korean", "common"),
new(GameFile.GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
new(GameFile.GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
new(GameText6, 7, "bin", "message", "Spanish", "common"),
new(GameText7, 8, "bin", "message", "Korean", "common"),
new(GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
new(GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
new(GameFile.StoryText0, 0, "bin", "message", "JPN", "script"),
new(GameFile.StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
new(GameFile.StoryText2, 2, "bin", "message", "English", "script"),
new(GameFile.StoryText3, 3, "bin", "message", "French", "script"),
new(GameFile.StoryText4, 4, "bin", "message", "Italian", "script"),
new(GameFile.StoryText5, 5, "bin", "message", "German", "script"),
new(StoryText0, 0, "bin", "message", "JPN", "script"),
new(StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
new(StoryText2, 2, "bin", "message", "English", "script"),
new(StoryText3, 3, "bin", "message", "French", "script"),
new(StoryText4, 4, "bin", "message", "Italian", "script"),
new(StoryText5, 5, "bin", "message", "German", "script"),
// 6 unused lang
new(GameFile.StoryText6, 7, "bin", "message", "Spanish", "script"),
new(GameFile.StoryText7, 8, "bin", "message", "Korean", "script"),
new(GameFile.StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
new(GameFile.StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
new(StoryText6, 7, "bin", "message", "Spanish", "script"),
new(StoryText7, 8, "bin", "message", "Korean", "script"),
new(StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
new(StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
new(GameFile.ItemStats, ContainerType.SingleFile, "bin", "pml", "item", "item.dat"),
new(GameFile.Evolutions, "bin", "pml", "evolution"),
new(ItemStats, SingleFile, "bin", "pml", "item", "item.dat"),
new(Evolutions, "bin", "pml", "evolution"),
new(GameFile.EggMoves, "bin", "pml", "tamagowaza"),
new(GameFile.PersonalStats, "bin", "pml", "personal"),
new(GameFile.MoveStats, "bin", "pml", "waza"),
new(GameFile.EncounterStatic, ContainerType.SingleFile, "bin", "script_event_data", "event_encount_data.bin"),
new(GameFile.EncounterTrade, ContainerType.SingleFile, "bin", "script_event_data", "field_trade.bin"),
new(GameFile.EncounterGift, ContainerType.SingleFile, "bin", "script_event_data", "add_poke.bin"),
new(GameFile.Learnsets, ContainerType.SingleFile, "bin", "pml", "waza_oboe", "wazaoboe_total.bin"),
new(PersonalStats, "bin", "pml", "personal"),
new(MoveStats, "bin", "pml", "waza"),
new(EncounterTableStatic, SingleFile, "bin", "script_event_data", "event_encount_data.bin"),
new(EncounterTableTrade, SingleFile, "bin", "script_event_data", "field_trade.bin"),
new(EncounterTableGift, SingleFile, "bin", "script_event_data", "add_poke.bin"),
new(Learnsets, SingleFile, "bin", "pml", "waza_oboe", "wazaoboe_total.bin"),
new(GameFile.FacilityPokeNormal, ContainerType.SingleFile, "bin", "field", "param", "battle_tower", "battle_tower_poke_table.bin"),
new(GameFile.FacilityTrainerNormal, ContainerType.SingleFile, "bin", "field", "param", "battle_tower", "battle_tower_trainer_table.bin"),
new(FacilityPokeNormal, SingleFile, "bin", "field", "param", "battle_tower", "battle_tower_poke_table.bin"),
new(FacilityTrainerNormal, SingleFile, "bin", "field", "param", "battle_tower", "battle_tower_trainer_table.bin"),
new(GameFile.WildData, ContainerType.SingleFile, "bin", "archive", "field", "resident", "data_table.gfpak"),
new(GameFile.NestData, ContainerType.SingleFile, "bin", "archive", "field", "resident", "data_table.gfpak"),
new(WildData, SingleFile, "bin", "archive", "field", "resident", "data_table.gfpak"),
new(NestData, SingleFile, "bin", "archive", "field", "resident", "data_table.gfpak"),
new(GameFile.DynamaxDens, ContainerType.SingleFile, "bin", "appli", "chika", "data_table", "underground_exploration_poke.bin"),
new(DynamaxDens, SingleFile, "bin", "appli", "chika", "data_table", "underground_exploration_poke.bin"),
new(GameFile.Placement, ContainerType.SingleFile, "bin", "archive", "field", "resident", "placement.gfpak"),
new(GameFile.Shops, ContainerType.SingleFile, "bin", "appli", "shop", "bin", "shop_data.bin"),
new(GameFile.Rentals, ContainerType.SingleFile, "bin", "script_event_data", "rental.bin"),
new(GameFile.SymbolBehave, ContainerType.SingleFile, "bin", "field", "param", "symbol_encount_mons_param", "symbol_encount_mons_param.bin"),
new(Placement, SingleFile, "bin", "archive", "field", "resident", "placement.gfpak"),
new(Shops, SingleFile, "bin", "appli", "shop", "bin", "shop_data.bin"),
new(Rentals, SingleFile, "bin", "script_event_data", "rental.bin"),
new(SymbolBehave, SingleFile, "bin", "field", "param", "symbol_encount_mons_param", "symbol_encount_mons_param.bin"),
// Cutscenes bin\demo
// Models bin\archive\pokemon
@ -185,202 +193,247 @@ internal void SaveAll()
};
/// <summary>
/// Sword
/// Legends: Arceus
/// </summary>
private static readonly GameFileReference[] PLA =
private static readonly GameFileReference[] FilesPLA =
{
new(GameFile.TrainerData, "bin", "trainer"),
new(TrainerSpecData, "bin", "trainer"),
new(GameFile.GameText0, 0, "bin", "message", "JPN", "common"),
new(GameFile.GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
new(GameFile.GameText2, 2, "bin", "message", "English", "common"),
new(GameFile.GameText3, 3, "bin", "message", "French", "common"),
new(GameFile.GameText4, 4, "bin", "message", "Italian", "common"),
new(GameFile.GameText5, 5, "bin", "message", "German", "common"),
new(GameText0, 0, "bin", "message", "JPN", "common"),
new(GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
new(GameText2, 2, "bin", "message", "English", "common"),
new(GameText3, 3, "bin", "message", "French", "common"),
new(GameText4, 4, "bin", "message", "Italian", "common"),
new(GameText5, 5, "bin", "message", "German", "common"),
// 6 unused lang
new(GameFile.GameText6, 7, "bin", "message", "Spanish", "common"),
new(GameFile.GameText7, 8, "bin", "message", "Korean", "common"),
new(GameFile.GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
new(GameFile.GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
new(GameText6, 7, "bin", "message", "Spanish", "common"),
new(GameText7, 8, "bin", "message", "Korean", "common"),
new(GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
new(GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
new(GameFile.StoryText0, 0, "bin", "message", "JPN", "script"),
new(GameFile.StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
new(GameFile.StoryText2, 2, "bin", "message", "English", "script"),
new(GameFile.StoryText3, 3, "bin", "message", "French", "script"),
new(GameFile.StoryText4, 4, "bin", "message", "Italian", "script"),
new(GameFile.StoryText5, 5, "bin", "message", "German", "script"),
new(StoryText0, 0, "bin", "message", "JPN", "script"),
new(StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
new(StoryText2, 2, "bin", "message", "English", "script"),
new(StoryText3, 3, "bin", "message", "French", "script"),
new(StoryText4, 4, "bin", "message", "Italian", "script"),
new(StoryText5, 5, "bin", "message", "German", "script"),
// 6 unused lang
new(GameFile.StoryText6, 7, "bin", "message", "Spanish", "script"),
new(GameFile.StoryText7, 8, "bin", "message", "Korean", "script"),
new(GameFile.StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
new(GameFile.StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
new(StoryText6, 7, "bin", "message", "Spanish", "script"),
new(StoryText7, 8, "bin", "message", "Korean", "script"),
new(StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
new(StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
new(GameFile.DexFormStorage , ContainerType.SingleFile, "bin", "appli", "pokedex", "res_table", "pokedex_form_storage_index_table.bin"),
new(GameFile.DexRank , ContainerType.SingleFile, "bin", "appli", "pokedex", "res_table", "pokedex_rank_table.bin"),
new(GameFile.DexResearch , ContainerType.SingleFile, "bin", "appli", "pokedex", "res_table", "pokedex_research_task_table.bin"),
new(GameFile.PokemonResourceList , ContainerType.SingleFile, "bin", "appli", "res_pokemon", "list", "pokemon_info_list.bin"),
new(GameFile.MoveShop , ContainerType.SingleFile, "bin", "appli", "wazaremember", "bin", "wazashop_table.bin"),
new(GameFile.HaShop , ContainerType.SingleFile, "bin", "appli", "shop", "bin", "ha_shop_data.bin"),
new(DexFormStorage , SingleFile, "bin", "appli", "pokedex", "res_table", "pokedex_form_storage_index_table.bin"),
new(DexRank , SingleFile, "bin", "appli", "pokedex", "res_table", "pokedex_rank_table.bin"),
new(DexResearch , SingleFile, "bin", "appli", "pokedex", "res_table", "pokedex_research_task_table.bin"),
new(PokemonResourceList , SingleFile, "bin", "appli", "res_pokemon", "list", "pokemon_info_list.bin"),
new(MoveShop , SingleFile, "bin", "appli", "wazaremember", "bin", "wazashop_table.bin"),
new(HaShop , SingleFile, "bin", "appli", "shop", "bin", "ha_shop_data.bin"),
new(GameFile.ArchiveFolder , "bin", "archive"),
new(GameFile.Resident , ContainerType.GFPack , "bin", "archive", "field", "resident_release.gfpak"),
new(GameFile.archive_contents , ContainerType.SingleFile, "bin", "archive", "archive_contents.bin"),
new(ArchiveFolder , "bin", "archive"),
new(Resident , GameFreakPack , "bin", "archive", "field", "resident_release.gfpak"),
new(archive_contents , SingleFile, "bin", "archive", "archive_contents.bin"),
new(GameFile.ThrowParam , ContainerType.SingleFile, "bin", "capture", "throw_param_table.bin"),
new(GameFile.ThrowPermissionSet , ContainerType.SingleFile, "bin", "capture", "throw_permissionset_dictionary.bin"),
new(GameFile.ThrowableParam , ContainerType.SingleFile, "bin", "capture", "throwable_param_table.bin"),
new(GameFile.ThrowableResource , ContainerType.SingleFile, "bin", "capture", "throwable_resource_dictionary.bin"),
new(GameFile.ThrowableResourceSet , ContainerType.SingleFile, "bin", "capture", "throwable_resourceset_dictionary.bin"),
new(ThrowParam , SingleFile, "bin", "capture", "throw_param_table.bin"),
new(ThrowPermissionSet , SingleFile, "bin", "capture", "throw_permissionset_dictionary.bin"),
new(ThrowableParam , SingleFile, "bin", "capture", "throwable_param_table.bin"),
new(ThrowableResource , SingleFile, "bin", "capture", "throwable_resource_dictionary.bin"),
new(ThrowableResourceSet , SingleFile, "bin", "capture", "throwable_resourceset_dictionary.bin"),
new(GameFile.Player1DressupTable , ContainerType.SingleFile, "bin", "chara", "table", "dressup_table_p1.bin"),
new(GameFile.Player2DressupTable , ContainerType.SingleFile, "bin", "chara", "table", "dressup_table_p2.bin"),
new(Player1DressupTable , SingleFile, "bin", "chara", "table", "dressup_table_p1.bin"),
new(Player2DressupTable , SingleFile, "bin", "chara", "table", "dressup_table_p2.bin"),
new(GameFile.Outbreak , ContainerType.SingleFile, "bin", "field", "encount", "huge_outbreak.bin"),
new(GameFile.NewHugeGroup , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_group.bin"),
new(GameFile.NewHugeGroupLottery , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_group_lottery.bin"),
new(GameFile.NewHugeLottery , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_lottery.bin"),
new(GameFile.NewHugeTimeLimit , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_time_limit.bin"),
new(Outbreak , SingleFile, "bin", "field", "encount", "huge_outbreak.bin"),
new(NewHugeGroup , SingleFile, "bin", "field", "encount", "new_huge_outbreak_group.bin"),
new(NewHugeGroupLottery , SingleFile, "bin", "field", "encount", "new_huge_outbreak_group_lottery.bin"),
new(NewHugeLottery , SingleFile, "bin", "field", "encount", "new_huge_outbreak_lottery.bin"),
new(NewHugeTimeLimit , SingleFile, "bin", "field", "encount", "new_huge_outbreak_time_limit.bin"),
new(GameFile.AICommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_common_config.bin"),
new(GameFile.AIExcitingConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_exciting_config.bin"),
new(GameFile.AIFieldWazaConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_field_waza_config.bin"),
new(GameFile.AISemiLegendConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_semi_legend_config.bin"),
new(GameFile.AITirednessConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_tiredness_config.bin"),
new(GameFile.AppConfigList , ContainerType.SingleFile, "bin", "misc", "app_config", "app_config_list.bin"),
new(GameFile.AppliHudConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "appli_hud_config.bin"),
new(GameFile.AppliStaffrollConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "appli_staffroll_config.bin"),
new(GameFile.AppliTipsConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "appli_tips_config.bin"),
new(GameFile.BattleCommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_common_config.bin"),
new(GameFile.BattleEndConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_end_config.bin"),
new(GameFile.BattleInConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_in_config.bin"),
new(GameFile.BattleLogicConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_logic_config.bin"),
new(GameFile.BattleStartConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_start_config.bin"),
new(GameFile.BattleViewConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_view_config.bin"),
new(GameFile.BattleVsnsConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_vsns_config.bin"),
new(GameFile.BuddyBattleConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_battle_config.bin"),
new(GameFile.BuddyConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_config.bin"),
new(GameFile.BuddyDirectItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_direct_item_config.bin"),
new(GameFile.BuddyGroupTalkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_group_talk_config.bin"),
new(GameFile.BuddyLandmarkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_landmark_config.bin"),
new(GameFile.BuddyNPCReactionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_npc_reaction_config.bin"),
new(GameFile.BuddyPlayerModeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_player_mode_config.bin"),
new(GameFile.BuddyWarpConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_warp_config.bin"),
new(GameFile.CharacterBipedIkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_biped_ik_config.bin"),
new(GameFile.CharacterBlinkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_blink_config.bin"),
new(GameFile.CharacterControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_controller_config.bin"),
new(GameFile.CharacterLookAtConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_look_at_config.bin"),
new(GameFile.CaptureConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "common_capture_config.bin"),
new(GameFile.CommonGeneralConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "common_general_config.bin"),
new(GameFile.CommonItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "common_item_config.bin"),
new(GameFile.DemoConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "demo_config.bin"),
new(GameFile.EnvPokeVoiceConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "env_poke_voice_config.bin"),
new(GameFile.EventBalloonrunConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_balloonrun_config.bin"),
new(GameFile.EventBalloonthrowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_balloonthrow_config.bin"),
new(GameFile.EventBanditConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_bandit_config.bin"),
new(GameFile.EventCullingConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_culling_config.bin"),
new(GameFile.EventDitherConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_dither_config.bin"),
new(GameFile.EventFarmConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_farm_config.bin"),
new(GameFile.EventGameOverConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_game_over_config.bin"),
new(GameFile.EventItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_item_config.bin"),
new(GameFile.EventMkrgRewardConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_mkrg_reward_config.bin"),
new(GameFile.EventQuestBoardConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_quest_board_config.bin"),
new(GameFile.EventRestrictionBattle , ContainerType.SingleFile, "bin", "misc", "app_config", "event_restriction_battle.bin"),
new(GameFile.EventWork , ContainerType.SingleFile, "bin", "misc", "app_config", "event_work.bin"),
new(GameFile.FieldAnimationFramerateConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_anime_framerate_config.bin"),
new(GameFile.FieldAreaSpeedConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_area_speed_config.bin"),
new(GameFile.FieldCameraConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_camera_config.bin"),
new(GameFile.FieldCaptureDirectorConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_capture_director_config.bin"),
new(GameFile.FieldCharaViewerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_chara_viewer_config.bin"),
new(GameFile.FieldCommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_common_config.bin"),
new(GameFile.FieldDirectItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_direct_item_config.bin"),
new(GameFile.FieldEnvConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_env_config.bin"),
new(GameFile.OutbreakConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_huge_outbreak.bin"),
new(GameFile.FieldItem , ContainerType.SingleFile, "bin", "misc", "app_config", "field_item.bin"),
new(GameFile.FieldItemRespawn , ContainerType.SingleFile, "bin", "misc", "app_config", "field_item_respawn.bin"),
new(GameFile.FieldLandmarkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_landmark_config.bin"),
new(GameFile.FieldLandmarkInciteConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_landmark_incite_config.bin"),
new(GameFile.FieldLockonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_lockon_config.bin"),
new(GameFile.BallThrowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_config.bin"),
new(GameFile.FieldBallMissedConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_hit_none_target_config.bin"),
new(GameFile.FieldObstructionWazaConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_obstruction_waza_config.bin"),
new(GameFile.FieldPokemonSlopeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_pokemon_slope_config.bin"),
new(GameFile.FieldQuestDestinationConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_quest_destination_config.bin"),
new(GameFile.FieldShadowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_shadow_config.bin"),
new(GameFile.FieldSpawnerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_spawner_config.bin"),
new(GameFile.FieldThrowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_throw_config.bin"),
new(GameFile.FieldThrowableAfterHitConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_throwable_after_hit_config.bin"),
new(GameFile.FieldVigilanceBgmConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_vigilance_bgm_config.bin"),
new(GameFile.FieldWeatheringConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_weathering_config.bin"),
new(GameFile.FieldWildPokemonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_wild_pokemon_config.bin"),
new(GameFile.WormholeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_wormhole_config.bin"),
new(GameFile.NPCAIConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_ai_config.bin"),
new(GameFile.NPCControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_controller_config.bin"),
new(GameFile.NPCCreaterConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_creater_config.bin"),
new(GameFile.NPCPokemonAIConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_pokemon_ai_config.bin"),
new(GameFile.NPCPopupConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_popup_config.bin"),
new(GameFile.NPCTalkTableConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_talk_table_config.bin"),
new(GameFile.PlayerCameraShakeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_camera_shake_config.bin"),
new(GameFile.PlayerCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_collision_config.bin"),
new(GameFile.PlayerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_config.bin"),
new(GameFile.PlayerControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_controller_config.bin"),
new(GameFile.PlayerFaceConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_face_config.bin"),
new(GameFile.PokemonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_config.bin"),
new(GameFile.PokemonControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_controller_config.bin"),
new(GameFile.EvolutionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_evolution_config.bin"),
new(GameFile.PokemonFriendshipConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_friendship_config.bin"),
new(GameFile.ShinyRolls , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_rare.bin"),
new(GameFile.SizeScaleConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_size_category_adjust_scale_config.bin"),
new(GameFile.RideBasuraoCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_basurao_collision_config.bin"),
new(GameFile.RideBasuraoConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_basurao_config.bin"),
new(GameFile.RideChangeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_change_config.bin"),
new(GameFile.RideCommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_common_config.bin"),
new(GameFile.RideNyuuraCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_nyuura_collision_config.bin"),
new(GameFile.RideNyuuraConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_nyuura_config.bin"),
new(GameFile.RideNyuuraControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_nyuura_controller_config.bin"),
new(GameFile.RideOdoshishiCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_odoshishi_collision_config.bin"),
new(GameFile.RideOdoshishiConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_odoshishi_config.bin"),
new(GameFile.RideRingumaCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_ringuma_collision_config.bin"),
new(GameFile.RideRingumaConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_ringuma_config.bin"),
new(GameFile.RideRingumaControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_ringuma_controller_config.bin"),
new(GameFile.RideWhooguruCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_whooguru_collision_config.bin"),
new(GameFile.RideWhooguruConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_whooguru_config.bin"),
new(GameFile.RideWhooguruControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_whooguru_controller_config.bin"),
new(GameFile.SoundConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "sound_config.bin"),
new(GameFile.WaterMotion , ContainerType.SingleFile, "bin", "misc", "app_config", "water_motion.bin"),
new(AICommonConfig , SingleFile, "bin", "misc", "app_config", "ai_common_config.bin"),
new(AIExcitingConfig , SingleFile, "bin", "misc", "app_config", "ai_exciting_config.bin"),
new(AIFieldWazaConfig , SingleFile, "bin", "misc", "app_config", "ai_field_waza_config.bin"),
new(AISemiLegendConfig , SingleFile, "bin", "misc", "app_config", "ai_semi_legend_config.bin"),
new(AITirednessConfig , SingleFile, "bin", "misc", "app_config", "ai_tiredness_config.bin"),
new(AppConfigList , SingleFile, "bin", "misc", "app_config", "app_config_list.bin"),
new(AppliHudConfig , SingleFile, "bin", "misc", "app_config", "appli_hud_config.bin"),
new(AppliStaffrollConfig , SingleFile, "bin", "misc", "app_config", "appli_staffroll_config.bin"),
new(AppliTipsConfig , SingleFile, "bin", "misc", "app_config", "appli_tips_config.bin"),
new(BattleCommonConfig , SingleFile, "bin", "misc", "app_config", "battle_common_config.bin"),
new(BattleEndConfig , SingleFile, "bin", "misc", "app_config", "battle_end_config.bin"),
new(BattleInConfig , SingleFile, "bin", "misc", "app_config", "battle_in_config.bin"),
new(BattleLogicConfig , SingleFile, "bin", "misc", "app_config", "battle_logic_config.bin"),
new(BattleStartConfig , SingleFile, "bin", "misc", "app_config", "battle_start_config.bin"),
new(BattleViewConfig , SingleFile, "bin", "misc", "app_config", "battle_view_config.bin"),
new(BattleVsnsConfig , SingleFile, "bin", "misc", "app_config", "battle_vsns_config.bin"),
new(BuddyBattleConfig , SingleFile, "bin", "misc", "app_config", "buddy_battle_config.bin"),
new(BuddyConfig , SingleFile, "bin", "misc", "app_config", "buddy_config.bin"),
new(BuddyDirectItemConfig , SingleFile, "bin", "misc", "app_config", "buddy_direct_item_config.bin"),
new(BuddyGroupTalkConfig , SingleFile, "bin", "misc", "app_config", "buddy_group_talk_config.bin"),
new(BuddyLandmarkConfig , SingleFile, "bin", "misc", "app_config", "buddy_landmark_config.bin"),
new(BuddyNPCReactionConfig , SingleFile, "bin", "misc", "app_config", "buddy_npc_reaction_config.bin"),
new(BuddyPlayerModeConfig , SingleFile, "bin", "misc", "app_config", "buddy_player_mode_config.bin"),
new(BuddyWarpConfig , SingleFile, "bin", "misc", "app_config", "buddy_warp_config.bin"),
new(CharacterBipedIkConfig , SingleFile, "bin", "misc", "app_config", "character_biped_ik_config.bin"),
new(CharacterBlinkConfig , SingleFile, "bin", "misc", "app_config", "character_blink_config.bin"),
new(CharacterControllerConfig , SingleFile, "bin", "misc", "app_config", "character_controller_config.bin"),
new(CharacterLookAtConfig , SingleFile, "bin", "misc", "app_config", "character_look_at_config.bin"),
new(CaptureConfig , SingleFile, "bin", "misc", "app_config", "common_capture_config.bin"),
new(CommonGeneralConfig , SingleFile, "bin", "misc", "app_config", "common_general_config.bin"),
new(CommonItemConfig , SingleFile, "bin", "misc", "app_config", "common_item_config.bin"),
new(DemoConfig , SingleFile, "bin", "misc", "app_config", "demo_config.bin"),
new(EnvPokeVoiceConfig , SingleFile, "bin", "misc", "app_config", "env_poke_voice_config.bin"),
new(EventBalloonrunConfig , SingleFile, "bin", "misc", "app_config", "event_balloonrun_config.bin"),
new(EventBalloonthrowConfig , SingleFile, "bin", "misc", "app_config", "event_balloonthrow_config.bin"),
new(EventBanditConfig , SingleFile, "bin", "misc", "app_config", "event_bandit_config.bin"),
new(EventCullingConfig , SingleFile, "bin", "misc", "app_config", "event_culling_config.bin"),
new(EventDitherConfig , SingleFile, "bin", "misc", "app_config", "event_dither_config.bin"),
new(EventFarmConfig , SingleFile, "bin", "misc", "app_config", "event_farm_config.bin"),
new(EventGameOverConfig , SingleFile, "bin", "misc", "app_config", "event_game_over_config.bin"),
new(EventItemConfig , SingleFile, "bin", "misc", "app_config", "event_item_config.bin"),
new(EventMkrgRewardConfig , SingleFile, "bin", "misc", "app_config", "event_mkrg_reward_config.bin"),
new(EventQuestBoardConfig , SingleFile, "bin", "misc", "app_config", "event_quest_board_config.bin"),
new(EventRestrictionBattle , SingleFile, "bin", "misc", "app_config", "event_restriction_battle.bin"),
new(EventWork , SingleFile, "bin", "misc", "app_config", "event_work.bin"),
new(FieldAnimationFramerateConfig , SingleFile, "bin", "misc", "app_config", "field_anime_framerate_config.bin"),
new(FieldAreaSpeedConfig , SingleFile, "bin", "misc", "app_config", "field_area_speed_config.bin"),
new(FieldCameraConfig , SingleFile, "bin", "misc", "app_config", "field_camera_config.bin"),
new(FieldCaptureDirectorConfig , SingleFile, "bin", "misc", "app_config", "field_capture_director_config.bin"),
new(FieldCharaViewerConfig , SingleFile, "bin", "misc", "app_config", "field_chara_viewer_config.bin"),
new(FieldCommonConfig , SingleFile, "bin", "misc", "app_config", "field_common_config.bin"),
new(FieldDirectItemConfig , SingleFile, "bin", "misc", "app_config", "field_direct_item_config.bin"),
new(FieldEnvConfig , SingleFile, "bin", "misc", "app_config", "field_env_config.bin"),
new(OutbreakConfig , SingleFile, "bin", "misc", "app_config", "field_huge_outbreak.bin"),
new(FieldItem , SingleFile, "bin", "misc", "app_config", "field_item.bin"),
new(FieldItemRespawn , SingleFile, "bin", "misc", "app_config", "field_item_respawn.bin"),
new(FieldLandmarkConfig , SingleFile, "bin", "misc", "app_config", "field_landmark_config.bin"),
new(FieldLandmarkInciteConfig , SingleFile, "bin", "misc", "app_config", "field_landmark_incite_config.bin"),
new(FieldLockonConfig , SingleFile, "bin", "misc", "app_config", "field_lockon_config.bin"),
new(BallThrowConfig , SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_config.bin"),
new(FieldBallMissedConfig , SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_hit_none_target_config.bin"),
new(FieldObstructionWazaConfig , SingleFile, "bin", "misc", "app_config", "field_obstruction_waza_config.bin"),
new(FieldPokemonSlopeConfig , SingleFile, "bin", "misc", "app_config", "field_pokemon_slope_config.bin"),
new(FieldQuestDestinationConfig , SingleFile, "bin", "misc", "app_config", "field_quest_destination_config.bin"),
new(FieldShadowConfig , SingleFile, "bin", "misc", "app_config", "field_shadow_config.bin"),
new(FieldSpawnerConfig , SingleFile, "bin", "misc", "app_config", "field_spawner_config.bin"),
new(FieldThrowConfig , SingleFile, "bin", "misc", "app_config", "field_throw_config.bin"),
new(FieldThrowableAfterHitConfig , SingleFile, "bin", "misc", "app_config", "field_throwable_after_hit_config.bin"),
new(FieldVigilanceBgmConfig , SingleFile, "bin", "misc", "app_config", "field_vigilance_bgm_config.bin"),
new(FieldWeatheringConfig , SingleFile, "bin", "misc", "app_config", "field_weathering_config.bin"),
new(FieldWildPokemonConfig , SingleFile, "bin", "misc", "app_config", "field_wild_pokemon_config.bin"),
new(WormholeConfig , SingleFile, "bin", "misc", "app_config", "field_wormhole_config.bin"),
new(NPCAIConfig , SingleFile, "bin", "misc", "app_config", "npc_ai_config.bin"),
new(NPCControllerConfig , SingleFile, "bin", "misc", "app_config", "npc_controller_config.bin"),
new(NPCCreaterConfig , SingleFile, "bin", "misc", "app_config", "npc_creater_config.bin"),
new(NPCPokemonAIConfig , SingleFile, "bin", "misc", "app_config", "npc_pokemon_ai_config.bin"),
new(NPCPopupConfig , SingleFile, "bin", "misc", "app_config", "npc_popup_config.bin"),
new(NPCTalkTableConfig , SingleFile, "bin", "misc", "app_config", "npc_talk_table_config.bin"),
new(PlayerCameraShakeConfig , SingleFile, "bin", "misc", "app_config", "player_camera_shake_config.bin"),
new(PlayerCollisionConfig , SingleFile, "bin", "misc", "app_config", "player_collision_config.bin"),
new(PlayerConfig , SingleFile, "bin", "misc", "app_config", "player_config.bin"),
new(PlayerControllerConfig , SingleFile, "bin", "misc", "app_config", "player_controller_config.bin"),
new(PlayerFaceConfig , SingleFile, "bin", "misc", "app_config", "player_face_config.bin"),
new(PokemonConfig , SingleFile, "bin", "misc", "app_config", "pokemon_config.bin"),
new(PokemonControllerConfig , SingleFile, "bin", "misc", "app_config", "pokemon_controller_config.bin"),
new(EvolutionConfig , SingleFile, "bin", "misc", "app_config", "pokemon_evolution_config.bin"),
new(PokemonFriendshipConfig , SingleFile, "bin", "misc", "app_config", "pokemon_friendship_config.bin"),
new(ShinyRolls , SingleFile, "bin", "misc", "app_config", "pokemon_rare.bin"),
new(SizeScaleConfig , SingleFile, "bin", "misc", "app_config", "pokemon_size_category_adjust_scale_config.bin"),
new(RideBasuraoCollisionConfig , SingleFile, "bin", "misc", "app_config", "ride_basurao_collision_config.bin"),
new(RideBasuraoConfig , SingleFile, "bin", "misc", "app_config", "ride_basurao_config.bin"),
new(RideChangeConfig , SingleFile, "bin", "misc", "app_config", "ride_change_config.bin"),
new(RideCommonConfig , SingleFile, "bin", "misc", "app_config", "ride_common_config.bin"),
new(RideNyuuraCollisionConfig , SingleFile, "bin", "misc", "app_config", "ride_nyuura_collision_config.bin"),
new(RideNyuuraConfig , SingleFile, "bin", "misc", "app_config", "ride_nyuura_config.bin"),
new(RideNyuuraControllerConfig , SingleFile, "bin", "misc", "app_config", "ride_nyuura_controller_config.bin"),
new(RideOdoshishiCollisionConfig , SingleFile, "bin", "misc", "app_config", "ride_odoshishi_collision_config.bin"),
new(RideOdoshishiConfig , SingleFile, "bin", "misc", "app_config", "ride_odoshishi_config.bin"),
new(RideRingumaCollisionConfig , SingleFile, "bin", "misc", "app_config", "ride_ringuma_collision_config.bin"),
new(RideRingumaConfig , SingleFile, "bin", "misc", "app_config", "ride_ringuma_config.bin"),
new(RideRingumaControllerConfig , SingleFile, "bin", "misc", "app_config", "ride_ringuma_controller_config.bin"),
new(RideWhooguruCollisionConfig , SingleFile, "bin", "misc", "app_config", "ride_whooguru_collision_config.bin"),
new(RideWhooguruConfig , SingleFile, "bin", "misc", "app_config", "ride_whooguru_config.bin"),
new(RideWhooguruControllerConfig , SingleFile, "bin", "misc", "app_config", "ride_whooguru_controller_config.bin"),
new(SoundConfig , SingleFile, "bin", "misc", "app_config", "sound_config.bin"),
new(WaterMotion , SingleFile, "bin", "misc", "app_config", "water_motion.bin"),
new(GameFile.MoveStats , "bin", "pml", "waza"),
new(GameFile.ItemStats , ContainerType.SingleFile, "bin", "pml", "item", "item.dat"),
new(GameFile.Evolutions , ContainerType.SingleFile, "bin", "pml", "evolution", "evolution_data_total.evobin"),
new(GameFile.PersonalStats , ContainerType.SingleFile, "bin", "pml", "personal", "personal_data_total.perbin"),
new(GameFile.Learnsets , ContainerType.SingleFile, "bin", "pml", "waza_oboe", "waza_oboe_total.wazaoboe"),
new(MoveStats , "bin", "pml", "waza"),
new(ItemStats , SingleFile, "bin", "pml", "item", "item.dat"),
new(Evolutions , SingleFile, "bin", "pml", "evolution", "evolution_data_total.evobin"),
new(PersonalStats , SingleFile, "bin", "pml", "personal", "personal_data_total.perbin"),
new(Learnsets , SingleFile, "bin", "pml", "waza_oboe", "waza_oboe_total.wazaoboe"),
new(GameFile.EncounterGift , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_add.bin"),
new(GameFile.SymbolBehave , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_ai.bin"),
new(GameFile.PokeBattleSpawn , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_battle_spawn.bin"),
new(GameFile.PokeBodyParticle , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_body_particle.bin"),
new(GameFile.PokeCaptureCollision , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_capture_collision.bin"),
new(GameFile.PokeDefaultLocator , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_default_locator.trloc"),
new(GameFile.FieldDrops , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_drop_item.bin"),
new(GameFile.BattleDrops , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_drop_item_battle.bin"),
new(GameFile.PokeEatingHabits , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_eating_habits.bin"),
new(GameFile.EncounterRateTable , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_encount.bin"),
new(GameFile.EncounterStatic , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_event_encount.bin"),
new(GameFile.MoveObstructionLegend , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_ns_legend.bin"),
new(GameFile.MoveObstructionLegendEffect , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_ns_legend_effect.bin"),
new(GameFile.MoveObstructionSE , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_se.bin"),
new(GameFile.MoveObstructionWild , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild.bin"),
new(GameFile.MoveObstructionWildEffect , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild_effect.bin"),
new(GameFile.MoveObstructionWater , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild_water.bin"),
new(GameFile.MoveObstructionWaterEffect , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild_water_effect.bin"),
new(GameFile.PokeMisc , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_misc.bin"),
new(GameFile.NushiBattleSettings , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_nushi_battle_setting.bin"),
new(GameFile.PokemonResourceTable , ContainerType.SingleFile, "bin", "pokemon", "table", "poke_resource_table.trpmcatalog"),
new(EncounterTableGift , SingleFile, "bin", "pokemon", "data", "poke_add.bin"),
new(SymbolBehave , SingleFile, "bin", "pokemon", "data", "poke_ai.bin"),
new(PokeBattleSpawn , SingleFile, "bin", "pokemon", "data", "poke_battle_spawn.bin"),
new(PokeBodyParticle , SingleFile, "bin", "pokemon", "data", "poke_body_particle.bin"),
new(PokeCaptureCollision , SingleFile, "bin", "pokemon", "data", "poke_capture_collision.bin"),
new(PokeDefaultLocator , SingleFile, "bin", "pokemon", "data", "poke_default_locator.trloc"),
new(FieldDrops , SingleFile, "bin", "pokemon", "data", "poke_drop_item.bin"),
new(BattleDrops , SingleFile, "bin", "pokemon", "data", "poke_drop_item_battle.bin"),
new(PokeEatingHabits , SingleFile, "bin", "pokemon", "data", "poke_eating_habits.bin"),
new(EncounterRateTable , SingleFile, "bin", "pokemon", "data", "poke_encount.bin"),
new(EncounterTableStatic , SingleFile, "bin", "pokemon", "data", "poke_event_encount.bin"),
new(MoveObstructionLegend , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_ns_legend.bin"),
new(MoveObstructionLegendEffect , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_ns_legend_effect.bin"),
new(MoveObstructionSE , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_se.bin"),
new(MoveObstructionWild , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild.bin"),
new(MoveObstructionWildEffect , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild_effect.bin"),
new(MoveObstructionWater , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild_water.bin"),
new(MoveObstructionWaterEffect , SingleFile, "bin", "pokemon", "data", "poke_field_obstruction_waza_wild_water_effect.bin"),
new(PokeMisc , SingleFile, "bin", "pokemon", "data", "poke_misc.bin"),
new(NushiBattleSettings , SingleFile, "bin", "pokemon", "data", "poke_nushi_battle_setting.bin"),
new(PokemonResourceTable , SingleFile, "bin", "pokemon", "table", "poke_resource_table.trpmcatalog"),
new(GameFile.EncounterTrade , ContainerType.SingleFile, "bin", "script_event_data", "field_trade.bin"), // Incorrect?
new(EncounterTableTrade , SingleFile, "bin", "script_event_data", "field_trade.bin"), // Incorrect?
// Cutscenes bin\demo
// Models bin\archive\pokemon
// pretty much everything is obviously named :)
};
#endregion
#region Gen9
/// <summary>
/// Scarlet &amp; Violet
/// </summary>
private static readonly GameFileReference[] FilesSV =
{
new(DataTrpfd, SingleFile, "arc", "data.trpfd"),
new(DataTrpfs, SingleFile, "arc", "data.trpfs"),
// new(TrainerSpecData, "bin", "trainer"),
//
// new(GameText0, 0, "bin", "message", "JPN", "common"),
// new(GameText1, 1, "bin", "message", "JPN_KANJI", "common"),
// new(GameText2, 2, "bin", "message", "English", "common"),
// new(GameText3, 3, "bin", "message", "French", "common"),
// new(GameText4, 4, "bin", "message", "Italian", "common"),
// new(GameText5, 5, "bin", "message", "German", "common"),
// // 6 unused lang
// new(GameText6, 7, "bin", "message", "Spanish", "common"),
// new(GameText7, 8, "bin", "message", "Korean", "common"),
// new(GameText8, 9, "bin", "message", "Simp_Chinese", "common"),
// new(GameText9, 10, "bin", "message", "Trad_Chinese", "common"),
//
// new(StoryText0, 0, "bin", "message", "JPN", "script"),
// new(StoryText1, 1, "bin", "message", "JPN_KANJI", "script"),
// new(StoryText2, 2, "bin", "message", "English", "script"),
// new(StoryText3, 3, "bin", "message", "French", "script"),
// new(StoryText4, 4, "bin", "message", "Italian", "script"),
// new(StoryText5, 5, "bin", "message", "German", "script"),
// // 6 unused lang
// new(StoryText6, 7, "bin", "message", "Spanish", "script"),
// new(StoryText7, 8, "bin", "message", "Korean", "script"),
// new(StoryText8, 9, "bin", "message", "Simp_Chinese", "script"),
// new(StoryText9, 10, "bin", "message", "Trad_Chinese", "script"),
//
// new(MoveStats , "bin", "pml", "waza"),
// new(ItemStats , SingleFile, "bin", "pml", "item", "item.dat"),
// new(Evolutions , SingleFile, "bin", "pml", "evolution", "evolution_data_total.evobin"),
// new(PersonalStats , SingleFile, "bin", "pml", "personal", "personal_data_total.perbin"),
// new(Learnsets , SingleFile, "bin", "pml", "waza_oboe", "waza_oboe_total.wazaoboe"),
//
// new(EncounterTableGift , SingleFile, "bin", "pokemon", "data", "poke_add.bin"),
// new(EncounterTableTrade , SingleFile, "bin", "script_event_data", "field_trade.bin"), // Incorrect?
};
#endregion
}

View File

@ -0,0 +1,36 @@
using System;
namespace pkNX.Game;
public enum GameLoadResult
{
Success,
ExefsNotFound,
DirectoryNotFound,
RomfsNotFound,
RomfsSelected,
InvalidGameVersion,
UnsupportedGame,
Failed = -1,
}
public static class GameLoadResultExt
{
public static bool IsSuccess(this GameLoadResult result) => result < GameLoadResult.ExefsNotFound;
public static bool IsFailure(this GameLoadResult result) => !result.IsSuccess();
public static string GetErrorMsg(this GameLoadResult result) => result switch
{
GameLoadResult.Success => "Loaded successfully.",
GameLoadResult.ExefsNotFound => "Could not detect exefs folder in the selected directory.",
GameLoadResult.DirectoryNotFound => "The selected directory does not exist!",
GameLoadResult.RomfsNotFound => "Could not detect romfs folder in the selected directory. Please select the folder that contains the romfs folder.",
GameLoadResult.RomfsSelected => "You have selected the romfs folder, however pkNX needs to create a separate folder to keep a clean copy of the original romfs.",
GameLoadResult.InvalidGameVersion => "Couldn't infer game version from romfs file count. Your game dump might have been corrupted.",
GameLoadResult.UnsupportedGame => "This game is (currently) not supported by pkNX!",
GameLoadResult.Failed => "Internal error occurred.",
_ => throw new NotImplementedException(),
};
}

View File

@ -43,8 +43,8 @@ public static (GameLocation?, GameLoadResult) GetGame(string? dir, GameVersion g
return (null, GameLoadResult.DirectoryNotFound);
var dirs = Directory.GetDirectories(dir);
var romfs = Array.Find(dirs, z => Path.GetFileName(z).StartsWith("rom"));
var exefs = Array.Find(dirs, z => Path.GetFileName(z).StartsWith("exe"));
var romfs = Array.Find(dirs, z => Path.GetFileName(z).StartsWith("rom", StringComparison.CurrentCultureIgnoreCase));
var exefs = Array.Find(dirs, z => Path.GetFileName(z).StartsWith("exe", StringComparison.CurrentCultureIgnoreCase));
if (romfs == null)
{
@ -87,6 +87,8 @@ private static GameVersion GetGameFromPath(string romfs, string? exefs)
private const int FILECOUNT_LA = 18_370;
private const int FILECOUNT_LA_101 = 18_371; // Ver. 1.0.1 (Day 1 Patch)
private const int FILECOUNT_LA_110 = 19_095; // Ver. 1.1.0 (Daybreak)
private const int FILECOUNT_SV = 24;
private const int FILECOUNT_SV_101 = 25; // Ver. 1.0.1 (Day 1 Patch)
private static GameVersion GetGameFromCount(int fileCount, string romfs, string? exefs)
{
@ -141,6 +143,21 @@ private static GameVersion GetGameFromCount(int fileCount, string romfs, string?
case FILECOUNT_LA or FILECOUNT_LA_101 or FILECOUNT_LA_110:
return GameVersion.PLA;
case FILECOUNT_SV:
case FILECOUNT_SV_101:
{
if (exefs == null)
return GameVersion.SV;
return GetTitleID() switch
{
// todo sv
"0100ABF008968000" => GameVersion.SL,
"01008DB008C2C000" => GameVersion.VL,
_ => GameVersion.SV, // can't figure out Title ID, default to SWSH so that wild editor prompts for version selection
};
}
default:
return GameVersion.Invalid;
}

View File

@ -1,6 +1,7 @@
using System;
using pkNX.Containers;
using pkNX.Structures;
using static pkNX.Structures.GameVersion;
namespace pkNX.Game;
@ -65,7 +66,7 @@ protected GameManager(GameLocation rom, int language)
/// </summary>
/// <param name="file">File type to fetch</param>
/// <returns>Container that contains the game data requested.</returns>
public IFileContainer GetFile(GameFile file) => FileMap.GetFile(file, Language);
public IFileContainer GetFile(GameFile file) => FileMap.GetFile(file, Language, this);
/// <summary>
/// Fetches strings for the input <see cref="TextName"/>.
@ -106,14 +107,12 @@ public FolderContainer GetFilteredFolder(GameFile type, Func<string, bool>? filt
return c;
}
public static GameManager GetManager(GameLocation loc, int language)
public static GameManager GetManager(GameLocation loc, int language) => loc.Game switch
{
return loc.Game switch
{
GameVersion.GP or GameVersion.GE or GameVersion.GG => new GameManagerGG(loc, language),
GameVersion.SW or GameVersion.SH or GameVersion.SWSH => new GameManagerSWSH(loc, language),
GameVersion.PLA => new GameManagerPLA(loc, language),
_ => throw new ArgumentException(nameof(loc.Game)),
};
}
GP or GE or GG => new GameManagerGG(loc, language),
SW or SH or SWSH => new GameManagerSWSH(loc, language),
PLA => new GameManagerPLA(loc, language),
SL or VL or SV => new GameManagerSV(loc, language),
_ => throw new ArgumentException(nameof(loc.Game)),
};
}

102
pkNX.Game/GameManagerSV.cs Normal file
View File

@ -0,0 +1,102 @@
using System;
using System.IO;
using pkNX.Containers;
using pkNX.Structures.FlatBuffers;
namespace pkNX.Game;
public class GameManagerSV : GameManager, IFileInternal, IDisposable
{
private readonly TrinityFileSystemManager Manager;
private string PathNPDM => Path.Combine(PathExeFS, "main.npdm");
private string TitleID => BitConverter.ToUInt64(File.ReadAllBytes(PathNPDM), 0x290).ToString("X16");
public GameManagerSV(GameLocation rom, int language) : base(rom, language)
{
// TODO: Use GameFileMapping?
// Open the trpfs
var pathTrpfs = Path.Combine(PathRomFS, "arc/data.trpfs");
var pathTrpfd = Path.Combine(PathRomFS, "arc/data.trpfd");
Manager = new TrinityFileSystemManager(pathTrpfs, pathTrpfd);
}
public bool HasFile(string path) => Manager.HasFile(path);
public bool HasFile(ulong hash) => Manager.HasFile(hash);
public byte[] GetPackedFile(string path) => Manager.GetPackedFile(path);
public byte[] GetPackedFile(ulong hash) => Manager.GetPackedFile(hash);
/// <summary>
/// Generally useful game data that can be used by multiple editors.
/// </summary>
public GameData Data { get; protected set; } = null!;
protected override void SetMitm()
{
var basePath = Path.GetDirectoryName(ROM.RomFS);
if (basePath is null)
throw new InvalidDataException("Invalid ROMFS path.");
var tid = ROM.ExeFS != null ? TitleID : "0100A3D008C5C000"; // no way to differentiate without exefs, so default to Scarlet
var redirect = Path.Combine(basePath, tid);
FileMitm.SetRedirect(basePath, redirect);
}
public override void Initialize()
{
base.Initialize();
// initialize gametext
//ResetText();
// initialize common structures
//var personal = GetFilteredFolder(GameFile.PersonalStats, z => Path.GetFileNameWithoutExtension(z) == "personal_total");
//var learn = this[GameFile.Learnsets][0];
//var splitLearn = learn.Split(0x104);
//Learn = new FakeContainer(splitLearn);
//var move = this[GameFile.MoveStats];
//((FolderContainer)move).Initialize();
Data = new GameData
{
//MoveData = new DataCache<IMove>(move)
//{
// Create = FlatBufferConverter.DeserializeFrom<Waza8>,
// Write = z => FlatBufferConverter.SerializeFrom((Waza8)z),
//},
//LevelUpData = new DataCache<Learnset>(Learn)
//{
// Create = z => new Learnset8(z),
// Write = z => z.Write(),
//},
// folders
//PersonalData = new PersonalTable8SWSH(personal[0]),
//EvolutionData = new DataCache<EvolutionSet>(GetFilteredFolder(GameFile.Evolutions))
//{
// Create = data => new EvolutionSet8(data),
// Write = evo => evo.Write(),
//},
};
}
public void ResetMoves() => GetFilteredFolder(GameFile.MoveStats);
public void ResetText()
{
GetFilteredFolder(GameFile.GameText, z => Path.GetExtension(z) == ".dat");
}
protected override void Terminate()
{
// Store Personal Data back in the file. Let the container detect if it is modified.
//var personal = this[GameFile.PersonalStats];
//personal[0] = Data.PersonalData.Table.SelectMany(z => ((IPersonalInfoBin)z).Write()).ToArray();
//var learn = this[GameFile.Learnsets];
//learn[0] = Learn.Files.SelectMany(z => z).ToArray();
}
public void Dispose()
{
Manager.Dispose();
}
}

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using pkNX.Structures;
using static pkNX.Game.TextName;
using static pkNX.Structures.GameVersion;
namespace pkNX.Game;
@ -8,25 +9,19 @@ public static class TextMapping
{
public static IReadOnlyCollection<TextReference> GetMapping(GameVersion game) => game switch
{
GameVersion.XY => XY,
GameVersion.ORASDEMO => AO,
GameVersion.ORAS => AO,
GameVersion.SMDEMO => SMDEMO,
GameVersion.SN => SM,
GameVersion.MN => SM,
GameVersion.US => USUM,
GameVersion.UM => USUM,
GameVersion.GP => GG,
GameVersion.GE => GG,
GameVersion.GG => GG,
GameVersion.SW => SWSH,
GameVersion.SH => SWSH,
GameVersion.SWSH => SWSH,
GameVersion.PLA => PLA,
X or Y or XY => MapXY,
OR or AS or ORAS or ORASDEMO => MapAO,
SMDEMO => MapSMDemo,
SN or MN or SM => MapSM,
US or UM or USUM => MapUSUM,
GP or GE or GG => MapGG,
SW or SH or SWSH => MapSWSH,
PLA => MapPLA,
SL or VL or SV => MapSV,
_ => throw new System.ArgumentOutOfRangeException($"No text mapping for {game}"),
};
private static readonly TextReference[] XY =
private static readonly TextReference[] MapXY =
{
new(005, Forms),
new(013, MoveNames),
@ -46,7 +41,7 @@ public static class TextMapping
new(141, OPowerFlavor),
};
private static readonly TextReference[] AO =
private static readonly TextReference[] MapAO =
{
new(005, Forms),
new(014, MoveNames),
@ -66,7 +61,7 @@ public static class TextMapping
new(165, OPowerFlavor),
};
private static readonly TextReference[] SMDEMO =
private static readonly TextReference[] MapSMDemo =
{
new(020, ItemFlavor),
new(021, ItemNames),
@ -83,7 +78,7 @@ public static class TextMapping
new(055, MoveNames),
};
private static readonly TextReference[] SM =
private static readonly TextReference[] MapSM =
{
new(035, ItemFlavor),
new(036, ItemNames),
@ -105,7 +100,7 @@ public static class TextMapping
new(120, PokedexEntry2),
};
private static readonly TextReference[] USUM =
private static readonly TextReference[] MapUSUM =
{
new(039, ItemFlavor),
new(040, ItemNames),
@ -127,7 +122,7 @@ public static class TextMapping
new(125, PokedexEntry2),
};
private static readonly TextReference[] GG =
private static readonly TextReference[] MapGG =
{
new("iteminfo.dat", ItemFlavor),
new("itemname.dat", ItemNames),
@ -147,7 +142,7 @@ public static class TextMapping
new("zukan_comment_A.dat", PokedexEntry1),
};
private static readonly TextReference[] SWSH =
private static readonly TextReference[] MapSWSH =
{
new("iteminfo.dat", ItemFlavor),
new("itemname.dat", ItemNames),
@ -173,7 +168,33 @@ public static class TextMapping
new("poke_memory_feeling.dat", MemoryFeelings),
};
private static readonly TextReference[] PLA =
private static readonly TextReference[] MapPLA =
{
new("iteminfo.dat", ItemFlavor),
new("itemname.dat", ItemNames),
new("monsname.dat", SpeciesNames),
new("place_name_indirect.dat", metlist_00000),
new("place_name_spe.dat", metlist_30000),
new("place_name_out.dat", metlist_40000),
new("place_name_per.dat", metlist_60000),
new("seikaku.dat", Natures),
new("tokusei.dat", AbilityNames),
new("tokuseiinfo.dat", AbilityFlavor),
new("trname.dat", TrainerNames),
new("trtype.dat", TrainerClasses),
new("trmsg.dat", TrainerText),
new("typename.dat", TypeNames),
new("wazainfo.dat", MoveFlavor),
new("wazaname.dat", MoveNames),
new("zkn_form.dat", Forms),
new("zkn_type.dat", SpeciesClassifications),
new("zukan_comment_A.dat", PokedexEntry1),
new("zukan_comment_B.dat", PokedexEntry2),
new("ribbon.dat", RibbonMark),
new("poke_memory_feeling.dat", MemoryFeelings),
};
private static readonly TextReference[] MapSV =
{
new("iteminfo.dat", ItemFlavor),
new("itemname.dat", ItemNames),

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Description>Game Data Manager</Description>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>

View File

@ -61,7 +61,7 @@ private void Randomize(EvolutionSet evos, int species)
if (evo.Method == 0)
continue;
evo.Species = (ushort)RandSpec.GetRandomSpecies(evo.Species, species);
evo.Form = (byte)RandForm.GetRandomForme(evo.Species, false, false, true, Game.SWSH, Personal.Table);
evo.Form = (byte)RandForm.GetRandomForm(evo.Species, false, false, Game.Generation, Personal.Table);
}
}

View File

@ -13,70 +13,53 @@ public FormRandomizer(IPersonalTable t)
Personal = t;
}
public int GetRandomForme(int species, bool mega, bool fused, bool alola, bool galar, IPersonalInfo[]? stats = null)
public int GetRandomForm(int species, bool mega, bool fuse, int generation, IPersonalInfo[]? stats = null)
{
stats ??= Personal.Table;
if (stats[species].FormCount <= 1)
return 0;
bool IsGen6 = Personal.MaxSpeciesID == 721;
switch ((Species)species)
if (stats.Length == 980 && species is (int)Pikachu or (int)Eevee) // ban LGPE starters, they crash trainer battles
return 0;
if (stats.Length == 1182 && species is (int)Rayquaza) // Rayquaza form count error in SWSH
return 0;
// ban Zen Mode and Galarian Zen Mode
if (species is (int)Darmanitan && generation >= 8)
{
// Rayquaza's Forme Count was unchanged in SWSH despite Megas being removed, so just in case the user allows random Megas, disallow this invalid Forme.
case Rayquaza when Personal.Table.Length == 1192:
return 0;
case Unown:
case Deerling:
case Sawsbuck:
return 31; // Random
case Greninja when !mega:
return 0;
case Scatterbug:
case Spewpa:
case Vivillon:
return 30; // save file specific
case Zygarde when !IsGen6:
return Util.Random.Next(4); // Complete Forme is battle only
case Minior:
return Util.Random.Next(7); // keep the core color a surprise
case Meowth when galar:
return Util.Random.Next(3); // Kanto, Alola, Galar
// only allow Standard, not Zen Mode
case Darmanitan when galar:
{
int form = Util.Random.Next(stats[species].FormCount);
return form & 2;
}
// some species have 1 invalid form among several other valid forms, handle them here
case Pikachu when Personal.Table.Length == 1192:
case Slowbro when galar:
{
int form = Util.Random.Next(stats[species].FormCount - 1);
int banned = GetInvalidForm(species, galar, Personal);
if (form == banned)
form++;
return form;
}
int form = Util.Random.Next(stats[species].FormCount);
return form & 2;
}
if (Personal.Table.Length == 980 && species is (int)Pikachu or (int)Eevee) // gg tableB -- no starters, they crash trainer battles.
return 0;
if (alola && Legal.EvolveToAlolanForms.Contains((ushort)species))
return Util.Random.Next(2);
if (galar && Legal.EvolveToGalarForms.Contains((ushort)species))
return Util.Random.Next(2);
if (!Legal.BattleExclusiveForms.Contains(species) || mega || (fused && Legal.BattleFusions.Contains(species)))
return Util.Random.Next(stats[species].FormCount); // Slot-Random
return 0;
return (Species)species switch
{
Pikachu or Slowbro when generation >= 8 => GetValidForm(species, generation, Personal),
Unown or Deerling or Sawsbuck => 31, // pure random -- todo sv see if this behavior changed
Greninja when !mega => 0, // treat Ash-Greninja as a Mega
Scatterbug or Spewpa or Vivillon => 30, // save file specific -- todo sv see if this behavior changed
Zygarde when generation >= 7 => Util.Random.Next(4), // skip Complete Forme
Minior => Util.Random.Next(7), // skip Core Forms
_ when !mega && Legal.BattleMegas.Contains(species) => 0,
_ when !fuse && Legal.BattleFusions.Contains(species) => 0,
_ when Legal.BattleForms.Contains(species) => 0,
_ => Util.Random.Next(stats[species].FormCount),
};
}
public static int GetInvalidForm(int species, bool galar, IPersonalTable stats) => species switch
private static int GetValidForm(int species, int generation, IPersonalTable stats)
{
(int)Pikachu when stats.Table.Length == 1192 => 8, // LGPE Partner Pikachu
(int)Slowbro when galar => 1, // Mega Slowbro
int form = Util.Random.Next(stats[species].FormCount - 1);
int banned = GetInvalidForm(species, generation);
if (form == banned)
form++;
return form;
}
private static int GetInvalidForm(int species, int generation) => species switch
{
(int)Pikachu when generation >= 8 => 8, // LGPE Partner Pikachu
(int)Slowbro when generation >= 8 => 1, // Mega Slowbro
_ => throw new ArgumentOutOfRangeException(nameof(species)),
};
}

View File

@ -48,6 +48,16 @@ public class SpeciesSettings : RandSettings
[Category(General), Description("Allows Generation 8 species when randomizing.")]
public bool Gen8 { get; set; } = true;
/// <summary>Allows Generation 9 species when randomizing.</summary>
[Category(General), Description("Allows Generation 9 species when randomizing.")]
public bool Gen9 { get; set; } = true;
[Category(Misc), Description("Allows eligible Pokémon to already be in their Mega-Evolved forms when randomizing species.")]
public bool AllowRandomMegaForms { get; set; }
[Category(Misc), Description("Allows Kyurem, Necrozma, and Calyrex to be encountered in their fused states when randomizing species.")]
public bool AllowRandomFusions { get; set; }
/// <summary>Allows Legendary species when randomizing.</summary>
[Category(Misc), Description("Allows Legendary species when randomizing.")]
public bool Legends { get; set; }
@ -97,6 +107,7 @@ public int[] GetSpecies(int maxSpecies, int generation)
if (Gen6) AddGen6Species(list, maxSpecies);
if (Gen7) AddGen7Species(list, maxSpecies);
if (Gen8) AddGen8Species(list, maxSpecies);
if (Gen9) AddGen9Species(list, maxSpecies);
if (generation == 7 && Gen1 && Events && maxSpecies <= Legal.MaxSpeciesID_7_GG)
AddGGEvents(list);
@ -221,6 +232,24 @@ private void AddGen8Species(List<int> list, int maxSpecies)
}
}
private void AddGen9Species(List<int> list, int maxSpecies)
{
if (maxSpecies <= Legal.MaxSpeciesID_8a)
return;
var legendary = Enumerable.Range(994, 6);
list.AddRange(Enumerable.Range(906, 105).Except(legendary)); // Sprigatito - Annihilape
list.Remove(980); // AKETUBAN
list.Remove(987); // BKETUBAN
if (Legends)
{
// list.Add(980); // AKETUBAN
// list.Add(987); // BKETUBAN
list.AddRange(legendary); // Ting-Lu, Chien-Pao, Wo-Chien, Chi-Yu, Koraidon, Miraidon
}
}
private static void AddGGEvents(List<int> list)
{
list.AddRange(Enumerable.Range(808, Legal.MaxSpeciesID_7_GG - Legal.MaxSpeciesID_7_USUM)); // Meltan, Melmetal

View File

@ -48,12 +48,6 @@ public class TrainerRandSettings
[Category(PKM), Description("Randomizes the Species and basic stat details of all Team members.")]
public bool RandomizeTeam { get; set; } = true;
[Category(PKM), Description("Allows random Mega Forms when randomizing species.")]
public bool AllowRandomMegaForms { get; set; }
[Category(PKM), Description("Allows random Fused PKM when randomizing species.")]
public bool AllowRandomFusions { get; set; }
[Category(PKM), Description("Allows random Held Items when randomizing species.")]
public bool AllowRandomHeldItems { get; set; }

View File

@ -163,7 +163,7 @@ private void RandomizeSpecFormItem(IPokeData pk, int Type)
else // every other pkm
{
pk.Species = RandSpec.GetRandomSpeciesType(pk.Species, Type);
pk.Form = RandForm.GetRandomForme(pk.Species, Settings.AllowRandomMegaForms, Settings.AllowRandomFusions, true, true, Personal.Table);
pk.Form = RandForm.GetRandomForm(pk.Species, SpecSettings.AllowRandomMegaForms, SpecSettings.AllowRandomFusions, Info.Generation, Personal.Table);
}
}
@ -181,7 +181,7 @@ private void RandomizeSpecForm(TrainerPoke7b pk, int type)
}
pk.Species = RandSpec.GetRandomSpeciesType(pk.Species, type);
pk.Form = RandForm.GetRandomForme(pk.Species, Settings.AllowRandomMegaForms, Settings.AllowRandomFusions, true, false, Personal.Table);
pk.Form = RandForm.GetRandomForm(pk.Species, SpecSettings.AllowRandomMegaForms, SpecSettings.AllowRandomFusions, Info.Generation, Personal.Table);
}
private void TryForceEvolve(IPokeData pk)
@ -253,7 +253,7 @@ private void UpdatePKMFromSettings(TrainerPoke pk)
return;
c.Species = AllowedGigantamaxes[Util.Random.Next(AllowedGigantamaxes.Length)];
c.Form = c.Species is (int)Species.Pikachu or (int)Species.Meowth ? 0 : RandForm.GetRandomForme(c.Species, false, false, false, false, Personal.Table); // Pikachu & Meowth altforms can't gmax
c.Form = c.Species is (int)Species.Pikachu or (int)Species.Meowth ? 0 : RandForm.GetRandomForm(c.Species, false, false, Info.Generation, Personal.Table); // Pikachu & Meowth altforms can't gmax
}
if (Settings.MaxDynamaxLevel && c.CanDynamax)
c.DynamaxLevel = 10;
@ -348,6 +348,11 @@ private static int[] GetRandomMega(Dictionary<int, int[]> megas, out int species
223, 224, // Sordward and Shielbert
};
// 3 poke max
private static readonly int[] MultiBattle_SV =
{
};
private static Dictionary<int, int> GetFixedCountIndexes(GameVersion game)
{
if (GameVersion.XY.Contains(game))
@ -362,14 +367,19 @@ private static int[] GetRandomMega(Dictionary<int, int[]> megas, out int species
return Legal.ImportantTrainers_GG.ToDictionary(z => z, index => MultiBattle_GG.Contains(index) ? 3 : 6);
if (GameVersion.SWSH.Contains(game))
return Legal.ImportantTrainers_SWSH.ToDictionary(z => z, index => MultiBattle_SWSH.Contains(index) ? 3 : 6);
if (GameVersion.SV.Contains(game))
return Legal.ImportantTrainers_SV.ToDictionary(z => z, index => MultiBattle_SV.Contains(index) ? 3 : 6);
return new Dictionary<int, int>();
}
private static readonly int[] CrashClasses_GG = Legal.BlacklistedClasses_GG;
private static readonly int[] CrashClasses_SWSH = Legal.BlacklistedClasses_SWSH;
private static readonly int[] CrashClasses_SV = Legal.BlacklistedClasses_SV;
private static int[] GetSpecialClasses(GameVersion game)
{
if (GameVersion.SV.Contains(game))
return Legal.SpecialClasses_SV;
if (GameVersion.SWSH.Contains(game))
return Legal.SpecialClasses_SWSH;
if (GameVersion.GG.Contains(game))
@ -387,6 +397,8 @@ private static int[] GetSpecialClasses(GameVersion game)
private static int[] GetCrashClasses(GameVersion game)
{
if (GameVersion.SV.Contains(game))
return CrashClasses_SV;
if (GameVersion.SWSH.Contains(game))
return CrashClasses_SWSH;
if (GameVersion.GG.Contains(game))

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Description>Randomizer Utility</Description>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>

View File

@ -1,63 +0,0 @@
using System.Collections.Generic;
using pkNX.Structures;
using static pkNX.Structures.Species;
namespace pkNX.Sprites;
public static class FormConverter
{
public static bool IsTotemForm(int species, int form, int generation = 7)
{
if (generation != 7)
return false;
if (form == 0)
return false;
if (!Legal.Totem_USUM.Contains((ushort)species))
return false;
if (species == (int)Mimikyu)
return form is 2 or 3;
if (Legal.Totem_Alolan.Contains((ushort)species))
return form == 2;
return form == 1;
}
public static int GetTotemBaseForm(int species, int form)
{
if (species == (int)Mimikyu)
return form - 2;
return form - 1;
}
public static bool IsValidOutOfBoundsForme(int species, int form, int generation)
{
return (Species)species switch
{
Unown => form < (generation == 2 ? 26 : 28), // A-Z : A-Z?!
Mothim => form < 3, // Wormadam base form is kept
Scatterbug or Spewpa => form < 18,
_ => false,
};
}
/// <summary>
/// Checks if the species should have a drop-down selection visible for the form value.
/// </summary>
/// <param name="pi">Game specific personal info</param>
/// <param name="species"> ID</param>
public static bool HasFormSelection(IPersonalFormInfo pi, int species)
{
if (HasFormeValuesNotIndicatedByPersonal.Contains(species))
return true;
int count = pi.FormCount;
return count > 1;
}
private static readonly HashSet<int> HasFormeValuesNotIndicatedByPersonal = new()
{
(int)Unown,
(int)Mothim, // Burmy forme carried over, not cleared
(int)Scatterbug,
(int)Spewpa, // Vivillon pre-evos
};
}

View File

@ -1,260 +0,0 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace pkNX.Sprites;
/// <summary>
/// Image Layering/Blending Utility
/// </summary>
public static class ImageUtil
{
public static Bitmap LayerImage(Image baseLayer, Image overLayer, int x, int y, double transparency)
{
overLayer = ChangeOpacity(overLayer, transparency);
return LayerImage(baseLayer, overLayer, x, y);
}
public static Bitmap LayerImage(Image baseLayer, Image overLayer, int x, int y)
{
Bitmap img = new(baseLayer);
using Graphics gr = Graphics.FromImage(img);
gr.DrawImage(overLayer, x, y, overLayer.Width, overLayer.Height);
return img;
}
public static Bitmap ChangeOpacity(Image img, double trans)
{
if (img.PixelFormat.HasFlag(PixelFormat.Indexed))
return (Bitmap)img;
var bmp = (Bitmap)img.Clone();
GetBitmapData(bmp, out BitmapData bmpData, out IntPtr ptr, out byte[] data);
Marshal.Copy(ptr, data, 0, data.Length);
SetAllTransparencyTo(data, trans);
Marshal.Copy(data, 0, ptr, data.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
public static Bitmap ChangeAllColorTo(Image img, Color c)
{
if (img.PixelFormat.HasFlag(PixelFormat.Indexed))
return (Bitmap)img;
var bmp = (Bitmap)img.Clone();
GetBitmapData(bmp, out BitmapData bmpData, out IntPtr ptr, out byte[] data);
Marshal.Copy(ptr, data, 0, data.Length);
ChangeAllColorTo(data, c);
Marshal.Copy(data, 0, ptr, data.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
public static Bitmap ToGrayscale(Image img)
{
if (img.PixelFormat.HasFlag(PixelFormat.Indexed))
return (Bitmap)img;
var bmp = (Bitmap)img.Clone();
GetBitmapData(bmp, out BitmapData bmpData, out IntPtr ptr, out byte[] data);
Marshal.Copy(ptr, data, 0, data.Length);
SetAllColorToGrayScale(data);
Marshal.Copy(data, 0, ptr, data.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
private static void GetBitmapData(Bitmap bmp, out BitmapData bmpData, out IntPtr ptr, out byte[] data)
{
bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
ptr = bmpData.Scan0;
data = new byte[bmp.Width * bmp.Height * 4];
}
public static Bitmap GetBitmap(byte[] data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb)
{
var bmp = new Bitmap(width, height, format);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, format);
var ptr = bmpData.Scan0;
Marshal.Copy(data, 0, ptr, data.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
public static byte[] GetPixelData(Bitmap bitmap)
{
var argbData = new byte[bitmap.Width * bitmap.Height * 4];
var bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
Marshal.Copy(bd.Scan0, argbData, 0, bitmap.Width * bitmap.Height * 4);
bitmap.UnlockBits(bd);
return argbData;
}
public static void SetAllUsedPixelsOpaque(byte[] data)
{
for (int i = 0; i < data.Length; i += 4)
{
if (data[i + 3] != 0)
data[i + 3] = 0xFF;
}
}
public static void RemovePixels(byte[] pixels, byte[] original)
{
for (int i = 0; i < original.Length; i += 4)
{
if (original[i + 3] == 0)
continue;
pixels[i + 0] = 0;
pixels[i + 1] = 0;
pixels[i + 2] = 0;
pixels[i + 3] = 0;
}
}
private static void SetAllTransparencyTo(byte[] data, double trans)
{
for (int i = 0; i < data.Length; i += 4)
data[i + 3] = (byte)(data[i + 3] * trans);
}
public static void ChangeAllColorTo(byte[] data, Color c)
{
byte R = c.R;
byte G = c.G;
byte B = c.B;
for (int i = 0; i < data.Length; i += 4)
{
if (data[i + 3] == 0)
continue;
data[i + 0] = B;
data[i + 1] = G;
data[i + 2] = R;
}
}
private static void SetAllColorToGrayScale(byte[] data)
{
for (int i = 0; i < data.Length; i += 4)
{
if (data[i + 3] == 0)
continue;
byte greyS = (byte)(((0.3 * data[i + 2]) + (0.59 * data[i + 1]) + (0.11 * data[i + 0])) / 3);
data[i + 0] = greyS;
data[i + 1] = greyS;
data[i + 2] = greyS;
}
}
public static void GlowEdges(byte[] data, byte blue, byte green, byte red, int width, int reach = 3, double amount = 0.0777)
{
PollutePixels(data, width, reach, amount);
CleanPollutedPixels(data, blue, green, red);
}
private static void PollutePixels(byte[] data, int width, int reach, double amount)
{
int stride = width * 4;
int height = data.Length / stride;
for (int i = 0; i < data.Length; i += 4)
{
// only pollute outwards if the current pixel isn't transparent
if (data[i + 3] == 0)
continue;
int x = (i % stride) / 4;
int y = (i / stride);
Pollute(x, y);
}
void Pollute(int x, int y)
{
int left = Math.Max(0, x - reach);
int right = Math.Min(width - 1, x + reach);
int top = Math.Max(0, y - reach);
int bottom = Math.Min(height - 1, y + reach);
for (int i = left; i <= right; i++)
{
for (int j = top; j <= bottom; j++)
{
// update one of the color bits
// it is expected that a transparent pixel RGBA value is 0.
var c = 4 * (i + (j * width));
data[c + 0] += (byte)(amount * (0xFF - data[c + 0]));
}
}
}
}
private static void CleanPollutedPixels(byte[] data, byte blue, byte green, byte red)
{
for (int i = 0; i < data.Length; i += 4)
{
// only clean if the current pixel isn't transparent
if (data[i + 3] != 0)
continue;
// grab the transparency from the donor byte
var transparency = data[i + 0];
if (transparency == 0)
continue;
data[i + 0] = blue;
data[i + 1] = green;
data[i + 2] = red;
data[i + 3] = transparency;
}
}
public static Color ColorBaseStat(int v)
{
const float maxval = 180; // shift the green cap down
float x = 100f * v / maxval;
if (x > 100)
x = 100;
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 Blend(Color color, Color backColor, double amount)
{
byte r = (byte)((color.R * amount) + (backColor.R * (1 - amount)));
byte g = (byte)((color.G * amount) + (backColor.G * (1 - amount)));
byte b = (byte)((color.B * amount) + (backColor.B * (1 - amount)));
return Color.FromArgb(r, g, b);
}
// https://stackoverflow.com/a/24199315
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using var wrapMode = new ImageAttributes();
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
using var graphics = Graphics.FromImage(destImage);
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
return destImage;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,177 +0,0 @@
using System.Drawing;
using pkNX.Structures;
using pkNX.Sprites.Properties;
namespace pkNX.Sprites;
public abstract class SpriteBuilder
{
public static bool ShowEggSpriteAsItem { get; set; } = true;
public abstract int Width { get; }
public abstract int Height { get; }
protected abstract int ItemShiftX { get; }
protected abstract int ItemShiftY { get; }
protected abstract int ItemMaxSize { get; }
protected abstract int EggItemShiftX { get; }
protected abstract int EggItemShiftY { get; }
public abstract Bitmap Hover { get; }
public abstract Bitmap View { get; }
public abstract Bitmap Set { get; }
public abstract Bitmap Delete { get; }
public abstract Bitmap Transparent { get; }
public abstract Bitmap Drag { get; }
public abstract Bitmap UnknownItem { get; }
public abstract Bitmap None { get; }
public abstract Bitmap ItemTM { get; }
public abstract Bitmap ItemTR { get; }
private const double UnknownFormTransparency = 0.5;
private const double ShinyTransparency = 0.7;
private const double EggUnderLayerTransparency = 0.33;
protected abstract string GetSpriteStringSpeciesOnly(int species);
protected abstract string GetSpriteAll(int species, int form, int gender, bool shiny, bool gmax, int generation);
protected abstract string GetItemResourceName(int item);
protected abstract Bitmap Unknown { get; }
protected abstract Bitmap GetEggSprite(int species);
public Image GetSprite(int species, int form, int gender, int heldItem, bool isEgg, bool isShiny, bool isGigantamax, int generation = -1)
{
if (species == 0)
return None;
var baseImage = GetBaseImage(species, form, gender, isShiny, isGigantamax, generation);
return GetSprite(baseImage, species, heldItem, isEgg, isShiny, isGigantamax, generation);
}
public Image GetSprite(Image baseSprite, int species, int heldItem, bool isEgg, bool isShiny, bool isGigantamax, int generation = -1, bool isBoxBGRed = false)
{
if (isEgg)
baseSprite = LayerOverImageEgg(baseSprite, species, heldItem != 0);
if (heldItem > 0)
baseSprite = LayerOverImageItem(baseSprite, heldItem, generation);
if (isShiny)
baseSprite = LayerOverImageShiny(baseSprite);
return baseSprite;
}
private Image GetBaseImage(int species, int form, int gender, bool shiny, bool gmax, int generation)
{
var img = FormConverter.IsTotemForm(species, form)
? GetBaseImageTotem(species, form, gender, shiny, gmax, generation)
: GetBaseImageDefault(species, form, gender, shiny, gmax, generation);
return img ?? GetBaseImageFallback(species, form, gender, shiny, gmax, generation);
}
private Image? GetBaseImageTotem(int species, int form, int gender, bool shiny, bool gmax, int generation)
{
var baseform = FormConverter.GetTotemBaseForm(species, form);
var baseImage = GetBaseImageDefault(species, baseform, gender, shiny, gmax, generation);
if (baseImage == null)
return null;
return ImageUtil.ToGrayscale(baseImage);
}
private Image? GetBaseImageDefault(int species, int form, int gender, bool shiny, bool gmax, int generation)
{
var file = GetSpriteAll(species, form, gender, shiny, gmax, generation);
return (Image?)Resources.ResourceManager.GetObject(file);
}
private Image GetBaseImageFallback(int species, int form, int gender, bool shiny, bool gmax, int generation)
{
if (shiny) // try again without shiny
{
var img = GetBaseImageDefault(species, form, gender, false, gmax, generation);
if (img != null)
return img;
}
// try again without form
var baseImage = (Image?)Resources.ResourceManager.GetObject(GetSpriteStringSpeciesOnly(species));
if (baseImage == null) // failed again
return Unknown;
return ImageUtil.LayerImage(baseImage, Unknown, 0, 0, UnknownFormTransparency);
}
private Image LayerOverImageItem(Image baseImage, int item, int generation)
{
Image itemimg = (Image?)Resources.ResourceManager.GetObject(GetItemResourceName(item)) ?? Resources.bitem_unk;
if (item is >= 328 and <= 419) // gen2/3/4 TM
itemimg = ItemTM;
else if (item is >= 1130 and <= 1229) // Gen8 TR
itemimg = ItemTR;
// Redraw item in bottom right corner; since images are cropped, try to not have them at the edge
int x = ItemShiftX + ((ItemMaxSize - itemimg.Width) / 2);
if (x + itemimg.Width > baseImage.Width)
x = baseImage.Width - itemimg.Width;
int y = ItemShiftY + (ItemMaxSize - itemimg.Height);
return ImageUtil.LayerImage(baseImage, itemimg, x, y);
}
private static Image LayerOverImageShiny(Image baseImage)
{
// Add shiny star to top left of image.
var rare = Resources.rare_icon;
return ImageUtil.LayerImage(baseImage, rare, 0, 0, ShinyTransparency);
}
private Image LayerOverImageEgg(Image baseImage, int species, bool hasItem)
{
if (ShowEggSpriteAsItem && !hasItem)
return LayerOverImageEggAsItem(baseImage, species);
return LayerOverImageEggTransparentSpecies(baseImage, species);
}
private Image LayerOverImageEggTransparentSpecies(Image baseImage, int species)
{
// Partially transparent species.
baseImage = ImageUtil.ChangeOpacity(baseImage, EggUnderLayerTransparency);
// Add the egg layer over-top with full opacity.
var egg = GetEggSprite(species);
return ImageUtil.LayerImage(baseImage, egg, 0, 0);
}
private Image LayerOverImageEggAsItem(Image baseImage, int species)
{
var egg = GetEggSprite(species);
return ImageUtil.LayerImage(baseImage, egg, EggItemShiftX, EggItemShiftY); // similar to held item, since they can't have any
}
}
/// <summary>
/// 56 high, 68 wide sprite builder
/// </summary>
public class SpriteBuilder5668 : SpriteBuilder
{
public override int Height => 56;
public override int Width => 68;
protected override int ItemShiftX => 52;
protected override int ItemShiftY => 24;
protected override int ItemMaxSize => 32;
protected override int EggItemShiftX => 9;
protected override int EggItemShiftY => 2;
protected override string GetSpriteStringSpeciesOnly(int species) => 'b' + $"_{species}";
protected override string GetSpriteAll(int species, int form, int gender, bool shiny, bool gmax, int generation) => 'b' + SpriteName.GetResourceStringSprite(species, form, gender, generation, shiny, gmax);
protected override string GetItemResourceName(int item) => 'b' + $"item_{item}";
protected override Bitmap Unknown => Resources.b_unknown;
protected override Bitmap GetEggSprite(int species) => species == (int)Species.Manaphy ? Resources.b_490_e : Resources.b_egg;
public override Bitmap Hover => Resources.slotHover68;
public override Bitmap View => Resources.slotView68;
public override Bitmap Set => Resources.slotSet68;
public override Bitmap Delete => Resources.slotDel68;
public override Bitmap Transparent => Resources.slotTrans68;
public override Bitmap Drag => Resources.slotDrag68;
public override Bitmap UnknownItem => Resources.bitem_unk;
public override Bitmap None => Resources.b_0;
public override Bitmap ItemTM => Resources.bitem_tm;
public override Bitmap ItemTR => Resources.bitem_tr;
}

View File

@ -1,110 +0,0 @@
using System.Collections.Generic;
using System.Text;
using pkNX.Structures;
namespace pkNX.Sprites;
public static class SpriteName
{
public static bool AllowShinySprite { get; set; } = true;
public static bool AllowGigantamaxSprite { get; set; } = true;
private const char Separator = '_';
private const char Cosplay = 'c';
private const char Shiny = 's';
private const string Gigantamax = "gmax";
private const char GGStarter = 'p';
public static string GetResourceStringBall(int ball) => $"_ball{ball}";
/// <summary>
/// Gets the resource name of the Pokémon sprite.
/// </summary>
public static string GetResourceStringSprite(int species, int form, int gender, int generation = 8, bool shiny = false, bool gmax = false)
{
if (SpeciesDefaultFormSprite.Contains(species) && !gmax) // Species who show their default sprite regardless of Form
form = 0;
if (gmax && species is (int)Species.Toxtricity or (int)Species.Alcremie) // same sprites for all altform gmaxes
form = 0;
switch (form)
{
case 30 when species is >= (int)Species.Scatterbug and <= (int)Species.Vivillon: // save file specific
form = 0;
break;
case 31 when species is (int)Species.Unown or (int)Species.Deerling or (int)Species.Sawsbuck: // Random
form = 0;
break;
}
var sb = new StringBuilder();
sb.Append(Separator).Append(species);
if (form != 0)
{
sb.Append(Separator)
.Append(form);
if (species == (int)Species.Pikachu)
{
if (generation == 6)
{
sb.Append(Cosplay);
gender = 2; // Cosplay Pikachu gift can only be Female, but personal entries are set to be either Gender
}
else if (form == 8)
{
sb.Append(GGStarter);
}
}
else if (species == (int)Species.Eevee)
{
if (form == 1)
sb.Append(GGStarter);
}
}
if (gender == 2 && SpeciesGenderedSprite.Contains(species) && !gmax)
{
sb.Append('f');
}
if (gmax && AllowGigantamaxSprite)
{
sb.Append(Separator);
sb.Append(Gigantamax);
}
if (shiny && AllowShinySprite)
sb.Append(Shiny);
return sb.ToString();
}
/// <summary>
/// Species that show their default Species sprite regardless of current form
/// </summary>
public static readonly HashSet<int> SpeciesDefaultFormSprite = new()
{
(int)Species.Mothim,
(int)Species.Scatterbug,
(int)Species.Spewpa,
(int)Species.Rockruff,
(int)Species.Mimikyu,
(int)Species.Sinistea,
(int)Species.Polteageist,
(int)Species.Urshifu,
};
/// <summary>
/// Species that show a Gender specific Sprite
/// </summary>
public static readonly HashSet<int> SpeciesGenderedSprite = new()
{
(int)Species.Pikachu,
(int)Species.Hippopotas,
(int)Species.Hippowdon,
(int)Species.Unfezant,
(int)Species.Frillish,
(int)Species.Jellicent,
(int)Species.Pyroar,
};
}

View File

@ -1,23 +0,0 @@
using System.Drawing;
using pkNX.Sprites.Properties;
namespace pkNX.Sprites;
public static class SpriteUtil
{
public static readonly SpriteBuilder5668 SB8 = new();
public static SpriteBuilder Spriter { get; set; } = SB8;
public static Image GetBallSprite(int ball)
{
string resource = SpriteName.GetResourceStringBall(ball);
return (Bitmap?)Resources.ResourceManager.GetObject(resource) ?? Resources._ball4; // Poké Ball (default)
}
public static Image GetSprite(int species, int form, int gender, int item, bool isegg, bool shiny, bool gmax, int generation = -1)
{
return Spriter.GetSprite(species, form, gender, item, isegg, shiny, gmax, generation);
}
public static void Initialize() => Spriter = SB8;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

Some files were not shown because too many files have changed in this diff Show More